From b43364b4655034c3c0dcef91a71a36b1d07106a9 Mon Sep 17 00:00:00 2001 From: Henrique Joaquim Date: Mon, 29 Apr 2024 09:52:59 +0100 Subject: [PATCH] [Feature] OpenBB Platform CLI (#5302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * rename sdk_token to pat to follow hub convention * black * move OpenBBError class to error model * add OBBject class docstring * pylint * add futures to app docstring * Linting * Fix chart-related mypy errors. Co-Author: ’hjoaquim’ * Fix more mypy warnings * Build static * MyPy improvements * move get_branch request to logging_settings (#5348) * move get_branch request to logging_settings * black * add todo * Revert "add todo" This reverts commit 7e3af83e3d17fda63280a3bf5a68206f722c7ee7. * add todo * Update logging_settings.py * mac/linux install script python output suppressed * rename .settings to .user for consistency * update readme * update readme * Linting * Linting and improvements * Regenerate static files * Snake_case, mypy, typing, improvements. | Co-Author: montezdesousa * Rebuild * Revert Fetcher * better cpi * Snake case * fixes: major_indices_eod for intraday, optional company_overview atrs * Feature/remove unncessary stuff (#5346) * removing the journal service from the command_runner * remove the journal service and fix tests * removing journal from the command_runner * removing journal entry references and adding optional metadata * rebuilding static assets * removing all references to journal entry * removing all references to journal and journal repository * removing journal query references * restructuring the command runner * fix argument mismatch and settings to user mismatch * dropping command runner session * re generation of static assets * ruff n black * removing introduced bug * removing duplicate reference due to merging * 3.8 compatible static assets * changing the way we deal with erros - removing the Error model * removing unncessary filters * fixing tests * regenerating static assets * remving mongodb references from user service * remving mongodb references * updating dependencies with removed pymongo * updating dependencies with removed pymongo * removing useless call * fix typo * ruff * fixing tests * ruff * removing leftovers * making registry map 3.8 compatible * static assets * ruff * Hotfix/squeeze import (#5358) * remove commit_hash and branch references * lazy load stats, scipy, pandas_ta * black & ruff * adding commithash back to log prefix * fix test --------- Co-authored-by: hjoaquim * .user got lost in merge * improve __repr__ for sdk classes * changing posthog project key (#5362) * Linting and improvements * Rebuild QA * fix py38 + metadata ref in docstrings * doc ident * restore qa * update readme: remove journal * doc detail * disable black in App docstring * update readme: command execution * ruff * remove run_in_isolation: cannot pickle nested classes * remove outdated dir openbb_sdk * outdated workflow * update ruff * dependency related changes * using model validator instead * adjustments to the system model * minor adjustments * revert all changes on stocks controller * working historical price * controller_builder file * process class recursivly * adding translators prop * poc with platform as a menu and as command * paths feature * adding subcontrollers * offline access * menu text as obj variable * adjustments * move help to base class; print tables using rich table (for now); clear the choices for better autocompletion * introducing the platform controller factory * remove unnecessary file * revert changes to files * dinamically generate platform menus * generated platform commands * poetry lock * fix for literals and choices * adjustments - recursive iteration only if it's a class * router name should be lower() to match the class processor * deps * better output * handling base models * comment call_news * Feature/v4 deprecate everything (#6113) * comment call_news * remove news * remove keys controller * remove settings controller * remove ta forex * remove qa economy * remove ta stocks * remove qa stocks * remove fa stocks * remove ins stocks * remove gov stocks * remove dps stocks * remove ba stocks * remove bt stocks * remove stocks * remove stocks base controller class * ruff * remove qa forex * remove oanda * remove forex * remove forex * remove forex * remove mention to fx_tickers * remove ta crypto * remove qa crypto * remove dd crypto * remove crypto controller * remove crypto references * remove economy controller * remove economy folder * remoe etf controller * remove reports controller * remove dashboards controller * remove alt controller * rmeove etf files * remove alt files * remove custom reports * remove dashboard files * remove econometrics controller * remove forecast controller * remove reports files * remove forecast files * remove econometrics files * remove portfolio * remove futures * remove fixedincome * remove funds * remove sdk and common * remove all from init * remove reference to ipykernel * remove reference to ta_helpers * remove reference to ta_helpers * cleanup * adjustments to choices * remove old sdk generator * remove mention to dashboards * ruff * cleanup * remove logging infra * remove decorators * remove sdk audit * remove tought of the day * remove tought of the day * fix commands andmenus choices * fixing lints * ruffs * remove old tests * deps trim * using container as condition for recursion * remove unnecessary dependencies * move dep to better section * python versions * poetry lock * better custmo annotations * handling properties * handling edge cases: no func_doc and when signarute doesn't match type hints (self) * reverting treating models as props * provider groups * comment debug stuff * Revert "comment debug stuff" This reverts commit f0a86f04a809d5feef45beb6546443dbb6620eeb. * comment debug stuff * little adjustment for annotated types * injecting the reference to make the custom argument groups available * comments * custom model_dump to drop choices * deprecated * remove update option * remove update * remove translations * remove account controller * command descriptions * menu descriptions * removing interactive tables * limit the number of columns and rows * menu descriptions * show indexes * More cleanups (#6245) * renames * remove .init files * remove unused files * remove session stuff * remove session and sources stuff * remove unused models * remove sdk session * remove charting infra * remove charting stuff but allow console style * updates in models * raise directly * call terminal directly and remove artifacts * remove reference to sources * remove reference to source arg * remove reference to source arg * remove unused stuff * remove screenshot and raise directly * remove unnecessary helper classes * update the config file * remove references to old charting infra * call terminal directly * update deps * make user always local for now * rename to CLI * ruff * export options * export functionality * update rich * update example routine * fix issues with queue * temporary workaround for username * deprecate main menu commands * deprecate commands and remove unused stuff * deprecate commands and remove unused stuff * deprecate commands and remove unused stuff * adjustments and cleanups * routines syncing w hub * small adjustments to point to the right instance * whoami * custom terminal style * adjustments related to preferences * deprecate stuff * rename terminal style * deprecate more things * more deprecation * more adjustments * current settings - deprecating preferences, user and system * terminal settings * revmap custom terminal settings * cli version * ruff * black * linting fixes * happy mypy * ruff * remove old terminal build files * remove devcontainer * update dependencies * trim commands and its descriptions * better warnings * remove commands and menus str and show warnings if dev mode * get command descriptions from the translators instead * Add username in brackets if logged in * fix buggy command descriptions * [Feature] Bug fixing the Platform CLI (#6291) * fix buggy command descriptions * new reference structure * Seggregate data commands from others (settings etc) * fix table html for charting extension * number of rows and columns as a setting * interactive tables * adding menu descriptions --------- Co-authored-by: hjoaquim * fix the way reference was being passed * fix choices parsing for providers * loosen to get instead * obbject registry class * obbject registry class * using the obbject registry to show available options * link obbject results to data processing commands data argument * small improvements to correctly get the standard args * intersecting data processing commands and matching the obbject trough its id * poetry lock * use openbb field * safe json conversion and trimming * better at finding nargs when union * improved obbject registry using a stack * data processing section * Update argparse_translator/argparse_class_processor.py Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com> * fix: remove unused function * fix: terminal.py -> openbb_terminal/cli.py * fix: move local model function to utils & delete module * fix: rename hub_model.py to hub_service.py * fix: delete paths_helper.py, not used * fix: circular import console * fix: move terminal_style.py from plots/ to config, delete plots/ * fix: delele base_helpers.py, function moved to env_handler.py * fix: move custom_prompt_toolkit.py to core/completer * fix: delete requirements.txt, we can export them later if needed * fix: move routines stuff to routines/ * fix: move rich_config.py to core/config * fix: rename parent_classes.py to base_controller.py * Danger: create controllers/ and move stuff * fix: move custom_prompt_toolkit.py to config and delete completer/ * fix: rename custom_prompt_toolkit.py to completer.py * fix: move rouines stuff to folder * ^ oops * Edits * fix: settings import * fix: openbb command * Fix routine record when local user * Lint * Remove quickexit * Fix timezone change * fix data processing commands: not allowing multiple items + need to allow 0 (which evalutes as false) * split comma separated strings into a list so that the cli can consume them * Feature/move terminal files (#6344) * Move terminal files into openbb_terminal * move routines * Create session to handle console, user, settings, etc. * Reset... * Bring back frontend-components * Move frontend-components to openbb_terminal * rename vars * remove doc * rename method * rename method * fix routines * read settings from .env * rename methods * ^ * ^ * better * bug * ^ * move frontend-components back to root level * revert changes * type checking only * docstring * fix: settings autocomplete * comment * use openbb_core singleton * rename src to fix openbb command * fix poetry build * readme --------- Co-authored-by: hjoaquim --------- Co-authored-by: Diogo Sousa Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com> Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com> Co-authored-by: Pratyush Shukla Co-authored-by: tehcoderer Co-authored-by: Theodore Aptekarev --- .devcontainer/Dockerfile | 79 - .devcontainer/devcontainer.json | 33 - .devcontainer/docker-compose.yaml | 11 - .dockerignore | 11 - .../terminal_pull_request_template.md | 6 +- .github/pull_request_template.md | 2 +- .github/workflows/README.md | 10 +- .github/workflows/linting.yml | 2 - .readthedocs.yaml | 20 - MANIFEST.in | 1 - build/conda/cleanup_artifacts.bat | 50 - build/conda/cleanup_artifacts.sh | 36 - build/conda/conda-3-10-env.yaml | 30 - build/conda/conda-3-8-env.yaml | 30 - build/conda/conda-3-9-env.yaml | 30 - build/docker/api.dockerfile | 47 - frontend-components/plotly/src/utils/utils.ts | 2 +- json_validate_exclude.txt | 3 - .../yfinance/openbb_yfinance/__init__.py | 3 - .../CONTRIBUTING.md | 20 +- openbb_terminal/README.md | 3 + openbb_terminal/__init__.py | 13 - openbb_terminal/account/account_controller.py | 601 - openbb_terminal/account/account_view.py | 83 - openbb_terminal/account/show_prompt.py | 24 - openbb_terminal/alternative/alt_controller.py | 99 - .../companieshouse_controller.py | 334 - .../companieshouse/companieshouse_model.py | 480 - .../companieshouse/companieshouse_view.py | 254 - .../alternative/companieshouse/company.py | 14 - .../alternative/companieshouse/company_doc.py | 31 - .../alternative/companieshouse/filing_data.py | 19 - .../alternative/hackernews_model.py | 42 - .../alternative/hackernews_view.py | 40 - .../alternative/oss/github_model.py | 232 - .../alternative/oss/github_view.py | 141 - .../alternative/oss/oss_controller.py | 254 - openbb_terminal/alternative/oss/runa_model.py | 161 - openbb_terminal/alternative/oss/runa_view.py | 133 - .../alternative/realestate/__init__.py | 0 .../realestate/landRegistry_model.py | 267 - .../realestate/landRegistry_view.py | 153 - .../realestate/realestate_controller.py | 263 - openbb_terminal/base_helpers.py | 99 - openbb_terminal/common/__init__.py | 2 - .../common/behavioural_analysis/__init__.py | 0 .../behavioural_analysis/finbrain_model.py | 42 - .../behavioural_analysis/finbrain_view.py | 141 - .../behavioural_analysis/finnhub_model.py | 50 - .../behavioural_analysis/finnhub_view.py | 64 - .../behavioural_analysis/google_model.py | 135 - .../behavioural_analysis/google_view.py | 283 - .../behavioural_analysis/reddit_helpers.py | 174 - .../behavioural_analysis/reddit_model.py | 599 - .../behavioural_analysis/reddit_view.py | 343 - .../behavioural_analysis/stocktwits_model.py | 119 - .../behavioural_analysis/stocktwits_view.py | 102 - openbb_terminal/common/biztoc_model.py | 181 - openbb_terminal/common/biztoc_view.py | 99 - openbb_terminal/common/common_model.py | 122 - openbb_terminal/common/feedparser_model.py | 113 - openbb_terminal/common/feedparser_view.py | 56 - openbb_terminal/common/news_sdk_helper.py | 45 - openbb_terminal/common/newsapi_model.py | 88 - openbb_terminal/common/newsapi_view.py | 54 - .../common/quantitative_analysis/__init__.py | 0 .../common/quantitative_analysis/qa_model.py | 660 - .../common/quantitative_analysis/qa_view.py | 1199 - .../quantitative_analysis/rolling_model.py | 140 - .../quantitative_analysis/rolling_view.py | 464 - .../common/technical_analysis/__init__.py | 0 .../custom_indicators_model.py | 104 - .../custom_indicators_view.py | 166 - .../technical_analysis/momentum_model.py | 277 - .../technical_analysis/momentum_view.py | 524 - .../technical_analysis/overlap_model.py | 163 - .../common/technical_analysis/overlap_view.py | 166 - .../common/technical_analysis/ta_helpers.py | 50 - .../trend_indicators_model.py | 84 - .../trend_indicators_view.py | 116 - .../technical_analysis/volatility_model.py | 694 - .../technical_analysis/volatility_view.py | 349 - .../common/technical_analysis/volume_model.py | 110 - .../common/technical_analysis/volume_view.py | 153 - openbb_terminal/config_terminal.py | 157 - openbb_terminal/core/__init__.py | 0 openbb_terminal/core/config/__init__.py | 0 openbb_terminal/core/config/paths.py | 32 - openbb_terminal/core/config/paths_helper.py | 80 - .../core/integration_tests/README.md | 296 - .../core/integration_tests/controllers.json | 26 - .../integration_controller.py | 754 - .../integration_test_coverage.py | 617 - .../core/integration_tests/utils.py | 141 - openbb_terminal/core/log/__init__.py | 0 .../core/log/collection/__init__.py | 0 .../core/log/collection/log_sender.py | 140 - .../core/log/collection/logging_clock.py | 135 - .../core/log/collection/s3_sender.py | 140 - openbb_terminal/core/log/constants.py | 5 - .../core/log/generation/__init__.py | 0 openbb_terminal/core/log/generation/common.py | 17 - .../core/log/generation/custom_logger.py | 26 - .../core/log/generation/directories.py | 44 - .../core/log/generation/expired_files.py | 30 - .../generation/formatter_with_exceptions.py | 175 - .../generation/path_tracking_file_handler.py | 194 - .../core/log/generation/settings.py | 155 - .../core/log/generation/settings_logger.py | 79 - .../core/log/generation/user_logger.py | 18 - openbb_terminal/core/models/__init__.py | 17 - openbb_terminal/core/models/base_model.py | 40 - .../core/models/credentials_model.py | 29 - .../core/models/preferences_model.py | 91 - openbb_terminal/core/models/profile_model.py | 86 - openbb_terminal/core/models/sources_model.py | 55 - openbb_terminal/core/models/system_model.py | 89 - openbb_terminal/core/models/user_model.py | 17 - openbb_terminal/core/plots/__init__.py | 4 - .../core/plots/assets/Terminal_icon.png | Bin 418780 -> 0 bytes openbb_terminal/core/plots/backend.py | 514 - openbb_terminal/core/plots/config/__init__.py | 0 .../core/plots/config/openbb_styles.py | 271 - openbb_terminal/core/plots/no_import.py | 95 - openbb_terminal/core/plots/plotly.html | 3748 - openbb_terminal/core/plots/plotly_helper.py | 1926 - .../core/plots/plotly_ta/__init__.py | 0 openbb_terminal/core/plots/plotly_ta/base.py | 198 - .../core/plots/plotly_ta/data_classes.py | 347 - .../plugins/custom_indicators_plugin.py | 207 - .../plotly_ta/plugins/momentum_plugin.py | 609 - .../plots/plotly_ta/plugins/overlap_plugin.py | 94 - .../plugins/trend_indicators_plugin.py | 193 - .../plotly_ta/plugins/volatility_plugin.py | 222 - .../plots/plotly_ta/plugins/volume_plugin.py | 117 - .../core/plots/plotly_ta/ta_class.py | 576 - openbb_terminal/core/plots/table.html | 307 - openbb_terminal/core/scripts/__init__.py | 0 openbb_terminal/core/scripts/sdk_audit.py | 233 - openbb_terminal/core/sdk/__init__.py | 0 .../core/sdk/controllers/__init__.py | 9 - .../sdk/controllers/alt_sdk_controller.py | 67 - .../sdk/controllers/crypto_sdk_controller.py | 299 - .../sdk/controllers/etf_sdk_controller.py | 37 - .../controllers/portfolio_sdk_controller.py | 115 - .../sdk/controllers/stocks_sdk_controller.py | 335 - openbb_terminal/core/sdk/models/__init__.py | 19 - .../core/sdk/models/alt_sdk_model.py | 114 - .../core/sdk/models/crypto_sdk_model.py | 585 - .../core/sdk/models/econometrics_sdk_model.py | 79 - .../core/sdk/models/economy_sdk_model.py | 113 - .../core/sdk/models/etf_sdk_model.py | 57 - .../core/sdk/models/fixedincome_sdk_model.py | 63 - .../core/sdk/models/forecast_sdk_model.py | 153 - .../core/sdk/models/forex_sdk_model.py | 27 - .../core/sdk/models/funds_sdk_model.py | 35 - .../core/sdk/models/futures_sdk_model.py | 27 - .../core/sdk/models/keys_sdk_model.py | 69 - .../core/sdk/models/portfolio_sdk_model.py | 206 - .../core/sdk/models/qa_sdk_model.py | 87 - .../core/sdk/models/stocks_sdk_model.py | 577 - .../core/sdk/models/ta_sdk_model.py | 125 - openbb_terminal/core/sdk/sdk_helpers.py | 689 - openbb_terminal/core/sdk/sdk_init.py | 505 - openbb_terminal/core/sdk/trail_map.csv | 530 - .../core/sdk/trail_map_forecasting.csv | 40 - .../core/sdk/trail_map_optimization.csv | 19 - openbb_terminal/core/sdk/trailmap.py | 260 - openbb_terminal/core/session/__init__.py | 0 openbb_terminal/core/session/banner.txt | 18 - openbb_terminal/core/session/constants.py | 55 - .../core/session/current_system.py | 42 - openbb_terminal/core/session/current_user.py | 146 - openbb_terminal/core/session/env_handler.py | 59 - openbb_terminal/core/session/hub_model.py | 846 - openbb_terminal/core/session/local_model.py | 234 - .../core/session/routines_handler.py | 216 - openbb_terminal/core/session/sdk_session.py | 132 - .../core/session/session_controller.py | 198 - openbb_terminal/core/session/session_model.py | 214 - .../core/session/sources_handler.py | 114 - openbb_terminal/core/session/utils.py | 60 - openbb_terminal/core/sources/utils.py | 121 - openbb_terminal/cryptocurrency/README.md | 3 - openbb_terminal/cryptocurrency/__init__.py | 2 - .../cryptocurrency/coinbase_helpers.py | 183 - .../cryptocurrency/coinpaprika_helpers.py | 64 - .../cryptocurrency/crypto_controller.py | 546 - .../cryptocurrency/crypto_models.py | 146 - .../cryptocurrency/crypto_views.py | 58 - .../cryptocurrency/cryptocurrency_helpers.py | 1037 - openbb_terminal/cryptocurrency/data/README.md | 37 - .../data/binance_gecko_map.json | 325 - .../data/coinbase_gecko_map.json | 101 - .../data/coingecko_categories.json | 130 - .../cryptocurrency/data/coingecko_coins.json | 19219 --- .../data/coinpaprika_coins.json | 120449 --------------- .../cryptocurrency/data/defillama_dapps.json | 3229 - .../cryptocurrency/data/erc20_coins.json | 5058 - .../cryptocurrency/data/finbrain_coins.json | 522 - .../cryptocurrency/data/santiment_slugs.json | 1 - .../data/yahoofinance_coins.json | 16797 -- .../cryptocurrency/dataframe_helpers.py | 207 - .../cryptocurrency/defi/__init__.py | 0 .../cryptocurrency/defi/coindix_model.py | 175 - .../cryptocurrency/defi/coindix_view.py | 93 - .../cryptocurrency/defi/cryptosaurio_model.py | 72 - .../cryptocurrency/defi/cryptosaurio_view.py | 73 - .../cryptocurrency/defi/defi_controller.py | 364 - .../cryptocurrency/defi/llama_model.py | 209 - .../cryptocurrency/defi/llama_view.py | 225 - .../cryptocurrency/defi/smartstake_model.py | 72 - .../cryptocurrency/defi/smartstake_view.py | 119 - .../cryptocurrency/defi/substack_model.py | 99 - .../cryptocurrency/defi/substack_view.py | 49 - .../defi/terraengineer_model.py | 77 - .../cryptocurrency/defi/terraengineer_view.py | 98 - .../defi/terramoney_fcd_model.py | 335 - .../defi/terramoney_fcd_view.py | 302 - .../cryptocurrency/discovery/__init__.py | 0 .../discovery/coinmarketcap_model.py | 77 - .../discovery/coinmarketcap_view.py | 64 - .../discovery/coinpaprika_model.py | 80 - .../discovery/coinpaprika_view.py | 74 - .../discovery/cryptostats_model.py | 92 - .../discovery/cryptostats_view.py | 69 - .../discovery/dappradar_model.py | 387 - .../discovery/dappradar_view.py | 335 - .../discovery/discovery_controller.py | 802 - .../discovery/pycoingecko_model.py | 421 - .../discovery/pycoingecko_view.py | 237 - .../cryptocurrency/discovery/sdk_helpers.py | 43 - .../cryptocurrency/due_diligence/__init__.py | 0 .../due_diligence/binance_model.py | 236 - .../due_diligence/binance_view.py | 115 - .../due_diligence/ccxt_model.py | 133 - .../cryptocurrency/due_diligence/ccxt_view.py | 108 - .../due_diligence/coinbase_model.py | 227 - .../due_diligence/coinbase_view.py | 159 - .../due_diligence/coinglass_model.py | 208 - .../due_diligence/coinglass_view.py | 264 - .../due_diligence/coinpaprika_model.py | 491 - .../due_diligence/coinpaprika_view.py | 392 - .../due_diligence/cryptopanic_view.py | 82 - .../due_diligence/dd_controller.py | 1744 - .../due_diligence/due_diligence_api.py | 66 - .../due_diligence/finbrain_crypto_view.py | 139 - .../due_diligence/glassnode_model.py | 612 - .../due_diligence/glassnode_view.py | 392 - .../due_diligence/messari_model.py | 781 - .../due_diligence/messari_view.py | 769 - .../due_diligence/pycoingecko_model.py | 834 - .../due_diligence/pycoingecko_view.py | 384 - .../due_diligence/santiment_model.py | 101 - .../due_diligence/santiment_view.py | 74 - .../due_diligence/sdk_helper.py | 117 - .../cryptocurrency/nft/__init__.py | 0 .../cryptocurrency/nft/nft_controller.py | 169 - .../cryptocurrency/nft/nftpricefloor_model.py | 71 - .../cryptocurrency/nft/nftpricefloor_view.py | 173 - .../cryptocurrency/nft/opensea_model.py | 80 - .../cryptocurrency/nft/opensea_view.py | 45 - .../cryptocurrency/onchain/__init__.py | 0 .../cryptocurrency/onchain/bitquery_model.py | 823 - .../cryptocurrency/onchain/bitquery_view.py | 415 - .../onchain/blockchain_model.py | 133 - .../cryptocurrency/onchain/blockchain_view.py | 181 - .../onchain/ethgasstation_model.py | 63 - .../onchain/ethgasstation_view.py | 50 - .../cryptocurrency/onchain/ethplorer_model.py | 587 - .../cryptocurrency/onchain/ethplorer_view.py | 423 - .../onchain/onchain_controller.py | 1433 - .../cryptocurrency/onchain/topledger_model.py | 310 - .../cryptocurrency/onchain/topledger_view.py | 58 - .../onchain/whale_alert_model.py | 182 - .../onchain/whale_alert_view.py | 85 - .../cryptocurrency/overview/__init__.py | 0 .../overview/blockchaincenter_model.py | 79 - .../overview/blockchaincenter_view.py | 81 - .../cryptocurrency/overview/coinbase_model.py | 64 - .../cryptocurrency/overview/coinbase_view.py | 55 - .../overview/coinpaprika_model.py | 424 - .../overview/coinpaprika_view.py | 419 - .../overview/cryptopanic_model.py | 246 - .../overview/cryptopanic_view.py | 84 - .../overview/glassnode_model.py | 45 - .../cryptocurrency/overview/glassnode_view.py | 160 - .../cryptocurrency/overview/loanscan_model.py | 336 - .../cryptocurrency/overview/loanscan_view.py | 108 - .../overview/overview_controller.py | 1575 - .../overview/pycoingecko_model.py | 535 - .../overview/pycoingecko_view.py | 797 - .../cryptocurrency/overview/rekt_model.py | 200 - .../cryptocurrency/overview/rekt_view.py | 83 - .../cryptocurrency/overview/sdk_helpers.py | 72 - .../overview/tokenterminal_model.py | 302 - .../overview/tokenterminal_view.py | 100 - .../overview/withdrawalfees_model.py | 268 - .../overview/withdrawalfees_view.py | 135 - .../cryptocurrency/pycoingecko_helpers.py | 285 - openbb_terminal/cryptocurrency/pyth_model.py | 108 - openbb_terminal/cryptocurrency/pyth_view.py | 31 - .../quantitative_analysis/__init__.py | 0 .../quantitative_analysis/qa_controller.py | 778 - .../technical_analysis/__init__.py | 0 .../technical_analysis/ta_controller.py | 1548 - .../cryptocurrency/tools/__init__.py | 0 .../cryptocurrency/tools/tools_controller.py | 188 - .../cryptocurrency/tools/tools_helpers.py | 58 - .../cryptocurrency/tools/tools_model.py | 119 - .../cryptocurrency/tools/tools_view.py | 116 - .../custom_reports/lunar_crash/coin_highs.png | Bin 55138 -> 0 bytes .../lunar_crash/luna_crash.html | 346 - .../lunar_crash/luna_supply.png | Bin 350260 -> 0 bytes .../custom_reports/lunar_crash/luna_terra.png | Bin 607642 -> 0 bytes .../custom_reports/lunar_crash/lunatics.ipynb | 285 - .../custom_reports/lunar_crash/tvl.png | Bin 667206 -> 0 bytes .../custom_reports/lunar_crash/ust_terra.png | Bin 465754 -> 0 bytes openbb_terminal/dashboards/__init__.py | 0 .../dashboards/dashboards_controller.py | 268 - .../dashboards/stream/Forecasting.py | 376 - openbb_terminal/dashboards/stream/__init__.py | 0 .../dashboards/stream/common_vars.py | 125 - .../dashboards/stream/pages/Chains.py | 144 - .../dashboards/stream/pages/Correlation.py | 97 - .../dashboards/stream/pages/Futures.py | 248 - .../dashboards/stream/pages/Indicators.py | 519 - .../dashboards/stream/pages/Options.py | 959 - .../dashboards/stream/pages/Short_Data.py | 214 - .../dashboards/stream/pages/Stocks.py | 401 - .../dashboards/stream/streamlit_helpers.py | 167 - openbb_terminal/dashboards/streamlit_run.py | 48 - openbb_terminal/dashboards/voila/chains.ipynb | 181 - .../dashboards/voila/correlation.ipynb | 200 - .../dashboards/voila/forecast.ipynb | 377 - .../dashboards/voila/futures.ipynb | 311 - .../dashboards/voila/shortdata.ipynb | 269 - openbb_terminal/dashboards/voila/stocks.ipynb | 406 - openbb_terminal/decorators.py | 154 - openbb_terminal/econometrics/__init__.py | 0 .../econometrics/econometrics_controller.py | 2203 - .../econometrics/econometrics_helpers.py | 90 - .../econometrics/econometrics_model.py | 535 - .../econometrics/econometrics_view.py | 611 - .../econometrics/regression_model.py | 574 - .../econometrics/regression_view.py | 247 - openbb_terminal/economy/README.md | 3 - openbb_terminal/economy/__init__.py | 0 openbb_terminal/economy/commodity_model.py | 59 - openbb_terminal/economy/commodity_view.py | 61 - .../economy/datasets/NASDAQ_CountryCodes.csv | 59 - openbb_terminal/economy/datasets/cpi.csv | 256 - .../economy/datasets/harmonized_cpi.csv | 137 - openbb_terminal/economy/econdb_model.py | 889 - openbb_terminal/economy/econdb_view.py | 252 - openbb_terminal/economy/economy_controller.py | 2044 - openbb_terminal/economy/economy_helpers.py | 159 - openbb_terminal/economy/fedreserve_model.py | 82 - openbb_terminal/economy/fedreserve_view.py | 115 - openbb_terminal/economy/finviz_model.py | 250 - openbb_terminal/economy/finviz_view.py | 161 - openbb_terminal/economy/fred_model.py | 443 - openbb_terminal/economy/fred_view.py | 412 - openbb_terminal/economy/nasdaq_model.py | 234 - openbb_terminal/economy/nasdaq_view.py | 118 - openbb_terminal/economy/oecd_model.py | 1274 - openbb_terminal/economy/oecd_view.py | 897 - openbb_terminal/economy/plot_view.py | 139 - .../economy/quantitative_analysis/__init__.py | 0 .../quantitative_analysis/qa_controller.py | 811 - openbb_terminal/economy/sdk_helpers.py | 42 - openbb_terminal/economy/wsj_model.py | 282 - openbb_terminal/economy/wsj_view.py | 200 - openbb_terminal/economy/yfinance_model.py | 762 - openbb_terminal/economy/yfinance_view.py | 159 - openbb_terminal/etf/README.md | 3 - openbb_terminal/etf/__init__.py | 0 .../etf/discovery/disc_controller.py | 122 - openbb_terminal/etf/discovery/wsj_model.py | 93 - openbb_terminal/etf/discovery/wsj_view.py | 58 - openbb_terminal/etf/etf_controller.py | 704 - openbb_terminal/etf/etf_helper.py | 25 - openbb_terminal/etf/etfs.csv | 2987 - openbb_terminal/etf/financedatabase_model.py | 103 - openbb_terminal/etf/financedatabase_view.py | 152 - openbb_terminal/etf/fmp_model.py | 208 - openbb_terminal/etf/fmp_view.py | 197 - openbb_terminal/etf/stockanalysis_model.py | 166 - openbb_terminal/etf/stockanalysis_view.py | 166 - .../etf/technical_analysis/ta_controller.py | 1456 - openbb_terminal/featflags_controller.py | 190 - openbb_terminal/fixedincome/__init__.py | 0 .../fixedincome/commercial_paper.csv | 241 - .../fixedincome/corporate_spot_rates.csv | 205 - openbb_terminal/fixedincome/ecb_model.py | 235 - openbb_terminal/fixedincome/ecb_view.py | 180 - .../fixedincome/fixedincome_controller.py | 1546 - openbb_terminal/fixedincome/fred_model.py | 1148 - openbb_terminal/fixedincome/fred_view.py | 1676 - .../fixedincome/ice_bofa_indices.csv | 983 - openbb_terminal/fixedincome/oecd_model.py | 304 - openbb_terminal/fixedincome/oecd_view.py | 113 - openbb_terminal/fixedincome/yfinance_model.py | 78 - openbb_terminal/fixedincome/yfinance_view.py | 68 - openbb_terminal/forecast/__init__.py | 0 openbb_terminal/forecast/anom_model.py | 60 - openbb_terminal/forecast/anom_view.py | 100 - openbb_terminal/forecast/autoarima_model.py | 144 - openbb_terminal/forecast/autoarima_view.py | 123 - openbb_terminal/forecast/autoces_model.py | 144 - openbb_terminal/forecast/autoces_view.py | 123 - openbb_terminal/forecast/autoets_model.py | 148 - openbb_terminal/forecast/autoets_view.py | 123 - openbb_terminal/forecast/autoselect_model.py | 228 - openbb_terminal/forecast/autoselect_view.py | 123 - openbb_terminal/forecast/brnn_model.py | 184 - openbb_terminal/forecast/brnn_view.py | 178 - openbb_terminal/forecast/expo_model.py | 175 - openbb_terminal/forecast/expo_view.py | 146 - openbb_terminal/forecast/forecast.ipynb | 89 - .../forecast/forecast_controller.py | 3572 - openbb_terminal/forecast/forecast_model.py | 553 - openbb_terminal/forecast/forecast_view.py | 341 - openbb_terminal/forecast/helpers.py | 1402 - openbb_terminal/forecast/linregr_model.py | 116 - openbb_terminal/forecast/linregr_view.py | 143 - openbb_terminal/forecast/mstl_model.py | 155 - openbb_terminal/forecast/mstl_view.py | 122 - openbb_terminal/forecast/nbeats_model.py | 182 - openbb_terminal/forecast/nbeats_view.py | 185 - openbb_terminal/forecast/nhits_model.py | 210 - openbb_terminal/forecast/nhits_view.py | 209 - openbb_terminal/forecast/regr_model.py | 103 - openbb_terminal/forecast/regr_view.py | 138 - openbb_terminal/forecast/rnn_model.py | 158 - openbb_terminal/forecast/rnn_view.py | 165 - openbb_terminal/forecast/rwd_model.py | 134 - openbb_terminal/forecast/rwd_view.py | 117 - .../forecast/seasonalnaive_model.py | 146 - .../forecast/seasonalnaive_view.py | 122 - openbb_terminal/forecast/tcn_model.py | 179 - openbb_terminal/forecast/tcn_view.py | 181 - openbb_terminal/forecast/tft_model.py | 217 - openbb_terminal/forecast/tft_view.py | 181 - openbb_terminal/forecast/theta_model.py | 167 - openbb_terminal/forecast/theta_view.py | 137 - openbb_terminal/forecast/timegpt_model.py | 91 - openbb_terminal/forecast/timegpt_view.py | 245 - openbb_terminal/forecast/trans_model.py | 189 - openbb_terminal/forecast/trans_view.py | 192 - openbb_terminal/forecast/whisper_model.py | 342 - openbb_terminal/forecast/whisper_utils.py | 87 - openbb_terminal/forex/README.md | 3 - openbb_terminal/forex/__init__.py | 0 openbb_terminal/forex/av_model.py | 178 - openbb_terminal/forex/av_view.py | 53 - .../forex/data/av_forex_currencies.csv | 160 - .../forex/data/polygon_tickers.csv | 1121 - .../forex/data/yahoofinance_forex.json | 20418 --- openbb_terminal/forex/forex_controller.py | 453 - openbb_terminal/forex/forex_helper.py | 302 - openbb_terminal/forex/fxempire_model.py | 39 - openbb_terminal/forex/fxempire_view.py | 58 - openbb_terminal/forex/polygon_model.py | 86 - .../forex/quantitative_analysis/__init__.py | 0 .../quantitative_analysis/qa_controller.py | 785 - openbb_terminal/forex/sdk_helpers.py | 52 - .../forex/technical_analysis/__init__.py | 0 .../forex/technical_analysis/ta_controller.py | 987 - openbb_terminal/futures/__init__.py | 0 openbb_terminal/futures/databento_view.py | 82 - openbb_terminal/futures/futures_controller.py | 278 - openbb_terminal/futures/futures_helper.py | 16 - openbb_terminal/futures/sdk_helper.py | 57 - openbb_terminal/futures/yfinance_model.py | 216 - openbb_terminal/futures/yfinance_view.py | 281 - openbb_terminal/helper_classes.py | 384 - openbb_terminal/helper_funcs.py | 2215 - openbb_terminal/helpers_denomination.py | 96 - openbb_terminal/intro.json | 155 - openbb_terminal/keys_controller.py | 1131 - openbb_terminal/keys_model.py | 2489 - openbb_terminal/keys_view.py | 51 - openbb_terminal/loggers.py | 385 - openbb_terminal/menu.py | 67 - .../documentation_commands.csv | 481 - .../documentation_commands.json | 845 - .../documentation_commands_sdk.csv | 113 - .../documentation_commands_sdk.json | 301 - .../generate_documentation_commands.py | 350 - .../exports/alternative/covid/.gitkeep | 0 .../common/behavioural_analysis/.gitkeep | 0 .../common/prediction_techniques/.gitkeep | 0 .../common/quantitative_analysis/.gitkeep | 0 .../common/technical_analysis/.gitkeep | 0 .../miscellaneous/exports/custom/.gitkeep | 0 .../exports/econometrics/.gitkeep | 0 .../miscellaneous/exports/economy/.gitkeep | 0 .../miscellaneous/exports/etf/movers/.gitkeep | 0 .../exports/etf/screeners/.gitkeep | 0 .../exports/mutual_funds/.gitkeep | 0 .../miscellaneous/exports/portfolio/.gitkeep | 0 .../exports/portfolio/views/.gitkeep | 0 .../exports/stocks/backtesting/.gitkeep | 0 .../stocks/behavioural_analysis/.gitkeep | 0 .../stocks/comparison_analysis/.gitkeep | 0 .../exports/stocks/dark_pool_shorts/.gitkeep | 0 .../exports/stocks/discovery/.gitkeep | 0 .../exports/stocks/due_diligence/.gitkeep | 0 .../stocks/fundamental_analysis/.gitkeep | 0 .../exports/stocks/government/.gitkeep | 0 .../exports/stocks/insider/.gitkeep | 0 .../exports/stocks/options/.gitkeep | 0 .../stocks/prediction_techniques/.gitkeep | 0 .../stocks/quantitative_analysis/.gitkeep | 0 .../exports/stocks/raw_data/.gitkeep | 0 .../stocks/sector_industry_analysis/.gitkeep | 0 .../stocks/technical_analysis/.gitkeep | 0 .../miscellaneous/futures/futures.csv | 188 - openbb_terminal/miscellaneous/i18n/en.yml | 1145 - .../miscellaneous/i18n/help_translation.ipynb | 195 - .../integration_tests_scripts/.gitkeep | 0 .../integration_tests_scripts/README.MD | 119 - .../account/test_account.openbb | 15 - .../alternative/test_alt.openbb | 11 - .../alternative/test_covid.openbb | 10 - .../alternative/test_oss.openbb | 10 - .../alternative/test_realestate.openbb | 10 - .../cryptocurrency/test_crypto.openbb | 59 - .../cryptocurrency/test_dd.openbb | 81 - .../cryptocurrency/test_defi.openbb | 80 - .../cryptocurrency/test_discovery.openbb | 36 - .../cryptocurrency/test_nft.openbb | 17 - .../cryptocurrency/test_onchain.openbb | 89 - .../cryptocurrency/test_overview.openbb | 114 - .../cryptocurrency/test_qa.openbb | 77 - .../cryptocurrency/test_ta.openbb | 70 - .../cryptocurrency/test_tools.openbb | 12 - .../dashboards/test_dashboards.openbb | 13 - .../econometrics/test_econometrics.openbb | 66 - .../economy/test_economy.openbb | 82 - .../economy/test_qa.openbb | 38 - .../etf/test_ca.openbb | 7 - .../etf/test_disc.openbb | 6 - .../etf/test_etf.openbb | 25 - .../etf/test_ta.openbb | 54 - .../fixedincome/test_fixedincome.openbb | 78 - .../forecast/test_forecast.openbb | 47 - .../forex/test_forex.openbb | 24 - .../forex/test_oanda.openbb | 72 - .../forex/test_qa.openbb | 61 - .../forex/test_ta.openbb | 51 - .../futures/test_futures.openbb | 11 - .../keys/test_keys.openbb | 3 - .../mutual_funds/test_mutual_fund.openbb | 21 - .../portfolio/test_params.openbb | 12 - .../portfolio/test_po.openbb | 99 - .../portfolio/test_portfolio.openbb | 126 - .../reports/test_reports.openbb | 43 - .../stocks/test_ba.openbb | 22 - .../stocks/test_bt.openbb | 105 - .../stocks/test_ca.openbb | 45 - .../stocks/test_disc.openbb | 37 - .../stocks/test_dps.openbb | 20 - .../stocks/test_fa.openbb | 74 - .../stocks/test_forecast.openbb | 3 - .../stocks/test_gov.openbb | 33 - .../stocks/test_hedge.openbb | 16 - .../stocks/test_insider.openbb | 37 - .../stocks/test_options.openbb | 37 - .../stocks/test_qa.openbb | 88 - .../stocks/test_res.openbb | 6 - .../stocks/test_screener.openbb | 17 - .../stocks/test_stocks.openbb | 61 - .../stocks/test_ta.openbb | 82 - .../stocks/test_tradinghours.openbb | 12 - .../terminal/test_news.openbb | 4 - .../miscellaneous/models/all_api_keys.json | 254 - .../miscellaneous/models/hub_credentials.json | 35 - .../models/local_credentials.json | 16 - .../portfolio/allocation_example.xlsx | Bin 17970 -> 0 bytes .../portfolio/holdings_example.xlsx | Bin 29757 -> 0 bytes .../portfolio/parameters_template.xlsx | Bin 33733 -> 0 bytes .../routines/routine_example.openbb | 23 - .../miscellaneous/sources/openbb_default.json | 609 - .../stocks/insider/Agriculture.ini | 96 - .../stocks/insider/Agro-Chemicals.ini | 96 - .../stocks/insider/Air-Transportation.ini | 96 - .../miscellaneous/stocks/insider/Apparel.ini | 96 - .../miscellaneous/stocks/insider/Banks.ini | 96 - .../miscellaneous/stocks/insider/Brokers.ini | 96 - .../stocks/insider/Building-Materials.ini | 96 - .../stocks/insider/Business-Services.ini | 96 - .../stocks/insider/Closed-End-Funds.ini | 96 - .../stocks/insider/Construction.ini | 96 - .../stocks/insider/Credit-Industry.ini | 96 - .../stocks/insider/Durable-Goods.ini | 96 - .../stocks/insider/Electronics.ini | 96 - .../stocks/insider/Furniture.ini | 96 - .../stocks/insider/Gold-Silver.ini | 96 - .../miscellaneous/stocks/insider/Grocery.ini | 96 - .../stocks/insider/Ground-Freight.ini | 96 - .../stocks/insider/Guided-Missiles.ini | 96 - .../miscellaneous/stocks/insider/Health.ini | 96 - .../miscellaneous/stocks/insider/Hotels.ini | 96 - .../stocks/insider/Insurance-Agents.ini | 96 - .../stocks/insider/Insurance-Carriers.ini | 96 - .../stocks/insider/Investment-Offices.ini | 96 - .../miscellaneous/stocks/insider/Legal.ini | 96 - .../miscellaneous/stocks/insider/Lumber.ini | 96 - .../stocks/insider/Manufacturing.ini | 96 - .../miscellaneous/stocks/insider/Mining.ini | 96 - .../stocks/insider/Mortgages.ini | 96 - .../stocks/insider/Nondurable-Goods.ini | 96 - .../stocks/insider/Personal-Credit.ini | 96 - .../stocks/insider/Pharmaceuticals.ini | 96 - .../stocks/insider/Professional-Services.ini | 96 - .../stocks/insider/Railroads.ini | 95 - .../stocks/insider/Real-Estate.ini | 96 - .../stocks/insider/Restaurants.ini | 96 - .../stocks/insider/Retail-Stores.ini | 96 - .../stocks/insider/Retail-Trade.ini | 96 - .../miscellaneous/stocks/insider/Tobacco.ini | 96 - .../stocks/insider/Utility-Communications.ini | 95 - .../stocks/insider/Utility-Gas.ini | 96 - .../stocks/insider/Wholesale-Trade.ini | 96 - .../miscellaneous/stocks/insider/template.ini | 617 - .../miscellaneous/stocks/insider/whales.ini | 617 - .../stocks/screener/5pct_above_low.ini | 18 - .../stocks/screener/all_parameters.txt | 212 - .../stocks/screener/analyst_strong_buy.ini | 16 - .../stocks/screener/break_out_stocks.ini | 21 - .../stocks/screener/buffett_like.ini | 18 - .../stocks/screener/bull_runs_over_10pct.ini | 13 - ...annel_up_and_low_debt_and_sma_50and200.ini | 17 - .../stocks/screener/cheap_bottom_dividend.ini | 13 - .../stocks/screener/cheap_dividend.ini | 14 - .../stocks/screener/cheap_oversold.ini | 12 - .../screener/continued_momentum_scan.ini | 20 - .../stocks/screener/death_cross.ini | 11 - .../stocks/screener/djia_components.ini | 13 - .../stocks/screener/golden_cross.ini | 11 - .../stocks/screener/golden_cross_penny.ini | 16 - .../stocks/screener/growth_stocks.ini | 24 - .../stocks/screener/heavy_inst_ins.ini | 15 - .../stocks/screener/high_vol_and_low_debt.ini | 16 - .../stocks/screener/modified_dreman.ini | 15 - .../stocks/screener/modified_neff.ini | 14 - .../stocks/screener/news_scanner.ini | 17 - .../stocks/screener/oversold.ini | 17 - .../stocks/screener/oversold_under_3dol.ini | 19 - .../stocks/screener/oversold_under_5dol.ini | 19 - .../stocks/screener/potential_reversals.ini | 17 - .../screener/recent_growth_and_support.ini | 18 - .../stocks/screener/rosenwald.ini | 16 - .../stocks/screener/rosenwald_gtfo.ini | 14 - .../stocks/screener/sdk_guide_preset.ini | 16 - .../stocks/screener/sexy_year.ini | 16 - .../stocks/screener/short_squeeze_scan.ini | 18 - ...simplistic_momentum_scanner_under_7dol.ini | 15 - .../screener/sp500_basic_materials_sector.ini | 14 - .../sp500_communication_services_sector.ini | 14 - .../sp500_consumer_cyclical_sector.ini | 14 - .../sp500_consumer_defensive_sector.ini | 14 - .../stocks/screener/sp500_energy_sector.ini | 14 - .../screener/sp500_financial_sector.ini | 14 - .../screener/sp500_healthcare_sector.ini | 14 - .../screener/sp500_industrials_sector.ini | 14 - .../screener/sp500_real_estate_sector.ini | 14 - .../screener/sp500_technology_sector.ini | 14 - .../screener/sp500_utilities_sector.ini | 14 - .../screener/stocks_strong_support_levels.ini | 17 - .../stocks/screener/top_performers_all.ini | 17 - .../screener/top_performers_healthcare.ini | 18 - .../stocks/screener/top_performers_tech.ini | 18 - .../stocks/screener/under_15dol_stocks.ini | 16 - .../stocks/screener/undervalue.ini | 14 - .../stocks/screener/unusual_volume.ini | 17 - .../stocks/screener/value_stocks.ini | 29 - .../weak_support_and_top_performers.ini | 19 - openbb_terminal/mutual_funds/README.md | 3 - openbb_terminal/mutual_funds/__init__.py | 0 .../mutual_funds/avanza_fund_ID.csv | 1320 - openbb_terminal/mutual_funds/avanza_model.py | 40 - openbb_terminal/mutual_funds/avanza_view.py | 115 - openbb_terminal/mutual_funds/mstarpy_model.py | 247 - openbb_terminal/mutual_funds/mstarpy_view.py | 284 - .../mutual_funds/mutual_fund_controller.py | 440 - .../mutual_funds/mutual_funds_utils.py | 250 - openbb_terminal/openbb_terminal/__init__.py | 1 + .../argparse_translator}/__init__.py | 0 .../argparse_class_processor.py | 148 + .../argparse_translator.py | 528 + .../argparse_translator/obbject_registry.py | 76 + .../openbb_terminal/assets/i18n/en.yml | 36 + .../assets/routines/routine_example.openbb | 21 + .../assets}/styles/default/Consolas.ttf | Bin .../assets}/styles/default/dark.mpfstyle.json | 0 .../assets}/styles/default/dark.mplrc.json | 0 .../assets}/styles/default/dark.mplstyle | 0 .../assets}/styles/default/dark.pltstyle.json | 0 .../styles/default/dark.richstyle.json | 0 .../styles/default/light.mpfstyle.json | 0 .../assets}/styles/default/light.mplrc.json | 0 .../assets}/styles/default/light.mplstyle | 0 .../styles/default/light.pltstyle.json | 0 .../styles/default/light.richstyle.json | 0 .../styles/default/tables.pltstyle.json | 0 .../assets}/styles/user/openbb.richstyle.json | 0 openbb_terminal/openbb_terminal/cli.py | 20 + .../openbb_terminal/config/__init__.py | 1 + .../config/completer.py} | 21 +- .../openbb_terminal/config/console.py | 97 + .../openbb_terminal/config/constants.py | 83 + .../openbb_terminal/config/menu_text.py | 262 + .../openbb_terminal/config/setup.py | 84 + .../openbb_terminal/config/style.py | 108 + .../controllers/base_controller.py} | 985 +- .../controllers/base_platform_controller.py | 290 + .../controllers}/choices.py | 82 +- .../controllers/feature_flags_controller.py | 293 + .../controllers/hub_service.py | 105 + .../platform_controller_factory.py | 58 + .../controllers/script_parser.py} | 19 +- .../controllers}/terminal_controller.py | 745 +- .../openbb_terminal/controllers/utils.py | 1056 + .../openbb_terminal/models/settings.py | 86 + openbb_terminal/openbb_terminal/session.py | 79 + openbb_terminal/poetry.lock | 5072 + openbb_terminal/portfolio/__init__.py | 2 - openbb_terminal/portfolio/allocation_model.py | 468 - .../portfolio/attribution_model.py | 398 - openbb_terminal/portfolio/metrics_model.py | 985 - .../portfolio/portfolio_controller.py | 1552 - openbb_terminal/portfolio/portfolio_engine.py | 1006 - openbb_terminal/portfolio/portfolio_helper.py | 339 - openbb_terminal/portfolio/portfolio_model.py | 1915 - .../portfolio_optimization/__init__.py | 0 .../portfolio_optimization/excel_model.py | 180 - .../optimizer_helper.py | 93 - .../portfolio_optimization/optimizer_model.py | 3011 - .../portfolio_optimization/optimizer_view.py | 4081 - .../parameters/Parameter.py | 86 - .../parameters/params_controller.py | 355 - .../parameters/params_helpers.py | 197 - .../parameters/params_statics.py | 221 - .../parameters/params_view.py | 138 - .../portfolio_optimization/po_controller.py | 3679 - .../portfolio_optimization/po_engine.py | 313 - .../portfolio_optimization/po_model.py | 2284 - .../portfolio_optimization/po_view.py | 720 - .../portfolio_optimization/statics.py | 550 - .../yahoo_finance_model.py | 388 - openbb_terminal/portfolio/portfolio_view.py | 2035 - openbb_terminal/portfolio/statics.py | 220 - openbb_terminal/pyproject.toml | 102 + .../.ipynb_checkpoints/test-checkpoint.html | 36444 ----- openbb_terminal/reports/README.md | 65 - openbb_terminal/reports/__init__.py | 0 .../reports/data/crypto_tickers.csv | 100 - openbb_terminal/reports/data/etf_tickers.csv | 101 - .../reports/data/stocks_tickers.csv | 52 - openbb_terminal/reports/reports_controller.py | 378 - openbb_terminal/reports/reports_model.py | 445 - openbb_terminal/reports/stored/.gitkeep | 0 .../stored/20220512_040312_equity_NVDA.html | 38448 ----- .../reports/templates/OpenBB_reports_logo.png | Bin 5214 -> 0 bytes .../reports/templates/crypto.ipynb | 1166 - .../reports/templates/economy.ipynb | 685 - .../reports/templates/equity.ipynb | 1410 - openbb_terminal/reports/templates/etf.ipynb | 499 - .../reports/templates/floppy-disc.png | Bin 2782 -> 0 bytes .../reports/templates/forecast.ipynb | 319 - openbb_terminal/reports/templates/forex.ipynb | 674 - .../reports/templates/portfolio.ipynb | 511 - openbb_terminal/reports/widget_helpers.py | 410 - openbb_terminal/reports/widgets/card.j2 | 8 - openbb_terminal/reports/widgets/report.css | 225 - openbb_terminal/reports/widgets/report.j2 | 19 - openbb_terminal/reports/widgets/row.j2 | 5 - openbb_terminal/reports/widgets/style.css | 54 - openbb_terminal/requirements-full.txt | 444 - openbb_terminal/requirements.txt | 298 - openbb_terminal/rich_config.py | 300 - openbb_terminal/sdk.py | 643 - openbb_terminal/settings_controller.py | 545 - openbb_terminal/sources_controller.py | 193 - openbb_terminal/stocks/README.md | 5 - openbb_terminal/stocks/__init__.py | 0 .../stocks/backtesting/__init__.py | 0 .../stocks/backtesting/bt_controller.py | 321 - .../stocks/backtesting/bt_model.py | 292 - openbb_terminal/stocks/backtesting/bt_view.py | 316 - .../stocks/behavioural_analysis/__init__.py | 0 .../behavioural_analysis/ba_controller.py | 585 - .../behavioural_analysis/finnhub_model.py | 128 - .../behavioural_analysis/finnhub_view.py | 125 - openbb_terminal/stocks/cboe_model.py | 60 - openbb_terminal/stocks/cboe_view.py | 36 - .../stocks/comparison_analysis/__init__.py | 0 .../comparison_analysis/ca_controller.py | 926 - .../comparison_analysis/finbrain_model.py | 146 - .../comparison_analysis/finbrain_view.py | 200 - .../comparison_analysis/finnhub_model.py | 50 - .../finviz_compare_model.py | 81 - .../finviz_compare_view.py | 58 - .../comparison_analysis/marketwatch_model.py | 398 - .../comparison_analysis/marketwatch_view.py | 200 - .../comparison_analysis/polygon_model.py | 57 - .../stocks/comparison_analysis/sdk_helpers.py | 47 - .../yahoo_finance_model.py | 264 - .../comparison_analysis/yahoo_finance_view.py | 341 - .../stocks/dark_pool_shorts/__init__.py | 0 .../stocks/dark_pool_shorts/dps_controller.py | 553 - .../stocks/dark_pool_shorts/finra_model.py | 347 - .../stocks/dark_pool_shorts/finra_view.py | 260 - .../stocks/dark_pool_shorts/ibkr_model.py | 40 - .../stocks/dark_pool_shorts/ibkr_view.py | 49 - .../stocks/dark_pool_shorts/quandl_model.py | 54 - .../stocks/dark_pool_shorts/quandl_view.py | 161 - .../stocks/dark_pool_shorts/sec_model.py | 209 - .../stocks/dark_pool_shorts/sec_view.py | 123 - .../dark_pool_shorts/shortinterest_model.py | 67 - .../dark_pool_shorts/shortinterest_view.py | 52 - .../dark_pool_shorts/stockgrid_model.py | 198 - .../stocks/dark_pool_shorts/stockgrid_view.py | 388 - .../dark_pool_shorts/yahoofinance_model.py | 28 - .../dark_pool_shorts/yahoofinance_view.py | 51 - openbb_terminal/stocks/databento_model.py | 135 - openbb_terminal/stocks/discovery/__init__.py | 0 openbb_terminal/stocks/discovery/ark_model.py | 149 - openbb_terminal/stocks/discovery/ark_view.py | 96 - .../stocks/discovery/disc_controller.py | 885 - .../stocks/discovery/disc_helpers.py | 15 - .../stocks/discovery/fidelity_model.py | 100 - .../stocks/discovery/fidelity_view.py | 104 - .../stocks/discovery/finnhub_model.py | 153 - .../stocks/discovery/finnhub_view.py | 97 - .../stocks/discovery/finviz_model.py | 60 - .../stocks/discovery/finviz_view.py | 116 - openbb_terminal/stocks/discovery/fmp_view.py | 85 - .../stocks/discovery/nasdaq_model.py | 92 - .../stocks/discovery/nasdaq_view.py | 124 - .../stocks/discovery/seeking_alpha_model.py | 215 - .../stocks/discovery/seeking_alpha_view.py | 131 - .../stocks/discovery/shortinterest_model.py | 49 - .../stocks/discovery/shortinterest_view.py | 93 - .../stocks/discovery/yahoofinance_model.py | 171 - .../stocks/discovery/yahoofinance_view.py | 257 - .../stocks/fundamental_analysis/__init__.py | 0 .../stocks/fundamental_analysis/av_model.py | 762 - .../stocks/fundamental_analysis/av_view.py | 555 - .../business_insider_model.py | 253 - .../business_insider_view.py | 290 - .../fundamental_analysis/csimarket_model.py | 82 - .../fundamental_analysis/csimarket_view.py | 89 - .../stocks/fundamental_analysis/dcf_model.py | 611 - .../stocks/fundamental_analysis/dcf_static.py | 181 - .../stocks/fundamental_analysis/dcf_view.py | 1188 - .../fundamental_analysis/eclect_us_model.py | 77 - .../fundamental_analysis/eclect_us_view.py | 46 - .../fundamental_analysis/eodhd_model.py | 75 - .../stocks/fundamental_analysis/eodhd_view.py | 148 - .../fundamental_analysis/fa_controller.py | 2145 - .../stocks/fundamental_analysis/fa_helper.py | 9 - .../fundamental_analysis/finnhub_model.py | 49 - .../fundamental_analysis/finnhub_view.py | 127 - .../fundamental_analysis/finviz_model.py | 140 - .../fundamental_analysis/finviz_view.py | 104 - .../stocks/fundamental_analysis/fmp_model.py | 818 - .../stocks/fundamental_analysis/fmp_view.py | 810 - .../key_metrics_explained.txt | 35 - .../fundamental_analysis/marketwatch_model.py | 351 - .../fundamental_analysis/marketwatch_view.py | 55 - .../fundamental_analysis/nasdaq_model.py | 93 - .../fundamental_analysis/nasdaq_view.py | 67 - .../fundamental_analysis/polygon_model.py | 159 - .../fundamental_analysis/polygon_view.py | 140 - .../fundamental_analysis/sdk_helpers.py | 276 - .../seeking_alpha_model.py | 344 - .../seeking_alpha_view.py | 83 - .../yahoo_finance_model.py | 412 - .../yahoo_finance_view.py | 492 - openbb_terminal/stocks/government/__init__.py | 0 .../stocks/government/gov_controller.py | 546 - .../stocks/government/quiverquant_model.py | 566 - .../stocks/government/quiverquant_view.py | 779 - openbb_terminal/stocks/insider/__init__.py | 0 .../stocks/insider/businessinsider_model.py | 73 - .../stocks/insider/businessinsider_view.py | 173 - .../stocks/insider/finviz_model.py | 50 - openbb_terminal/stocks/insider/finviz_view.py | 51 - .../stocks/insider/insider_controller.py | 957 - .../stocks/insider/openinsider_model.py | 1515 - .../stocks/insider/openinsider_view.py | 272 - openbb_terminal/stocks/insider/sdk_helper.py | 230 - openbb_terminal/stocks/mappings/Mic_Codes.csv | 2125 - openbb_terminal/stocks/options/__init__.py | 0 .../stocks/options/alphaquery_model.py | 64 - .../stocks/options/alphaquery_view.py | 54 - .../stocks/options/barchart_model.py | 49 - .../stocks/options/barchart_view.py | 46 - .../stocks/options/calculator_model.py | 61 - .../stocks/options/calculator_view.py | 95 - openbb_terminal/stocks/options/cboe_model.py | 582 - .../stocks/options/chartexchange_model.py | 84 - .../stocks/options/chartexchange_view.py | 137 - .../stocks/options/fdscanner_model.py | 93 - .../stocks/options/fdscanner_view.py | 67 - .../stocks/options/intrinio_model.py | 754 - .../stocks/options/intrinio_view.py | 240 - .../stocks/options/nasdaq_model.py | 499 - openbb_terminal/stocks/options/op_helpers.py | 706 - .../stocks/options/options_chains_model.py | 1801 - .../stocks/options/options_chains_view.py | 1014 - .../stocks/options/options_controller.py | 1430 - .../stocks/options/options_sdk_helper.py | 1140 - .../stocks/options/options_view.py | 507 - openbb_terminal/stocks/options/tmx_model.py | 595 - .../stocks/options/tradier_model.py | 568 - .../stocks/options/tradier_view.py | 117 - .../stocks/options/yfinance_model.py | 439 - .../stocks/options/yfinance_view.py | 425 - .../stocks/quantitative_analysis/__init__.py | 0 .../quantitative_analysis/beta_model.py | 75 - .../stocks/quantitative_analysis/beta_view.py | 98 - .../quantitative_analysis/factors_model.py | 107 - .../quantitative_analysis/factors_view.py | 26 - .../quantitative_analysis/qa_controller.py | 1167 - .../stocks/quantitative_analysis/qa_model.py | 14 - openbb_terminal/stocks/research/__init__.py | 0 .../stocks/research/res_controller.py | 199 - openbb_terminal/stocks/screener/__init__.py | 0 .../stocks/screener/finviz_model.py | 1974 - .../stocks/screener/finviz_view.py | 247 - .../stocks/screener/screener_controller.py | 662 - .../stocks/screener/screener_helper.py | 35 - .../stocks/screener/screener_view.py | 79 - openbb_terminal/stocks/stock_statics.py | 614 - openbb_terminal/stocks/stocks_controller.py | 820 - openbb_terminal/stocks/stocks_helper.py | 1131 - openbb_terminal/stocks/stocks_model.py | 393 - openbb_terminal/stocks/stocks_view.py | 60 - .../stocks/technical_analysis/__init__.py | 0 .../technical_analysis/finbrain_model.py | 38 - .../technical_analysis/finbrain_view.py | 26 - .../technical_analysis/ta_controller.py | 1798 - .../technical_analysis/tradingview_model.py | 122 - .../technical_analysis/tradingview_view.py | 71 - openbb_terminal/stocks/tradinghours/README.md | 21 - .../stocks/tradinghours/__init__.py | 0 .../stocks/tradinghours/bursa_model.py | 204 - .../stocks/tradinghours/bursa_view.py | 104 - .../stocks/tradinghours/data/__init__.py | 0 .../tradinghours/data/bursa_open_hours.json | 1029 - .../tradinghours/pandas_market_cal_model.py | 58 - .../tradinghours/pandas_market_cal_view.py | 56 - .../tradinghours/tradinghours_controller.py | 315 - .../tradinghours/tradinghours_helper.py | 33 - openbb_terminal/terminal_helper.py | 418 - .../{alternative => tests}/__init__.py | 0 openbb_terminal/thought_of_the_day.py | 142 - poetry.lock | 10198 -- pyproject.toml | 252 - requirements-full.txt | 429 - requirements.txt | 282 - terminal.py | 48 - tests/README.md | 690 - tests/__init__.py | 0 tests/conftest.py | 522 - tests/helpers/__init__.py | 0 tests/helpers/helpers.py | 41 - tests/helpers/tools.py | 65 - tests/test_helpers.py | 9 - tests/website/__init__.py | 0 tests/website/conftest.py | 7 - tests/website/test_generate_docs.py | 109 - website/controller_doc_classes.py | 526 - 978 files changed, 9073 insertions(+), 501502 deletions(-) delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/devcontainer.json delete mode 100644 .devcontainer/docker-compose.yaml delete mode 100644 .dockerignore delete mode 100644 .readthedocs.yaml delete mode 100644 MANIFEST.in delete mode 100644 build/conda/cleanup_artifacts.bat delete mode 100755 build/conda/cleanup_artifacts.sh delete mode 100644 build/conda/conda-3-10-env.yaml delete mode 100644 build/conda/conda-3-8-env.yaml delete mode 100644 build/conda/conda-3-9-env.yaml delete mode 100644 build/docker/api.dockerfile delete mode 100644 json_validate_exclude.txt rename CONTRIBUTING.md => openbb_terminal/CONTRIBUTING.md (99%) create mode 100644 openbb_terminal/README.md delete mode 100644 openbb_terminal/__init__.py delete mode 100644 openbb_terminal/account/account_controller.py delete mode 100644 openbb_terminal/account/account_view.py delete mode 100644 openbb_terminal/account/show_prompt.py delete mode 100644 openbb_terminal/alternative/alt_controller.py delete mode 100644 openbb_terminal/alternative/companieshouse/companieshouse_controller.py delete mode 100644 openbb_terminal/alternative/companieshouse/companieshouse_model.py delete mode 100644 openbb_terminal/alternative/companieshouse/companieshouse_view.py delete mode 100644 openbb_terminal/alternative/companieshouse/company.py delete mode 100644 openbb_terminal/alternative/companieshouse/company_doc.py delete mode 100644 openbb_terminal/alternative/companieshouse/filing_data.py delete mode 100644 openbb_terminal/alternative/hackernews_model.py delete mode 100644 openbb_terminal/alternative/hackernews_view.py delete mode 100644 openbb_terminal/alternative/oss/github_model.py delete mode 100644 openbb_terminal/alternative/oss/github_view.py delete mode 100644 openbb_terminal/alternative/oss/oss_controller.py delete mode 100644 openbb_terminal/alternative/oss/runa_model.py delete mode 100644 openbb_terminal/alternative/oss/runa_view.py delete mode 100644 openbb_terminal/alternative/realestate/__init__.py delete mode 100644 openbb_terminal/alternative/realestate/landRegistry_model.py delete mode 100644 openbb_terminal/alternative/realestate/landRegistry_view.py delete mode 100644 openbb_terminal/alternative/realestate/realestate_controller.py delete mode 100644 openbb_terminal/base_helpers.py delete mode 100644 openbb_terminal/common/__init__.py delete mode 100644 openbb_terminal/common/behavioural_analysis/__init__.py delete mode 100644 openbb_terminal/common/behavioural_analysis/finbrain_model.py delete mode 100644 openbb_terminal/common/behavioural_analysis/finbrain_view.py delete mode 100644 openbb_terminal/common/behavioural_analysis/finnhub_model.py delete mode 100644 openbb_terminal/common/behavioural_analysis/finnhub_view.py delete mode 100644 openbb_terminal/common/behavioural_analysis/google_model.py delete mode 100644 openbb_terminal/common/behavioural_analysis/google_view.py delete mode 100644 openbb_terminal/common/behavioural_analysis/reddit_helpers.py delete mode 100644 openbb_terminal/common/behavioural_analysis/reddit_model.py delete mode 100644 openbb_terminal/common/behavioural_analysis/reddit_view.py delete mode 100644 openbb_terminal/common/behavioural_analysis/stocktwits_model.py delete mode 100644 openbb_terminal/common/behavioural_analysis/stocktwits_view.py delete mode 100644 openbb_terminal/common/biztoc_model.py delete mode 100644 openbb_terminal/common/biztoc_view.py delete mode 100644 openbb_terminal/common/common_model.py delete mode 100644 openbb_terminal/common/feedparser_model.py delete mode 100644 openbb_terminal/common/feedparser_view.py delete mode 100644 openbb_terminal/common/news_sdk_helper.py delete mode 100644 openbb_terminal/common/newsapi_model.py delete mode 100644 openbb_terminal/common/newsapi_view.py delete mode 100644 openbb_terminal/common/quantitative_analysis/__init__.py delete mode 100644 openbb_terminal/common/quantitative_analysis/qa_model.py delete mode 100644 openbb_terminal/common/quantitative_analysis/qa_view.py delete mode 100644 openbb_terminal/common/quantitative_analysis/rolling_model.py delete mode 100644 openbb_terminal/common/quantitative_analysis/rolling_view.py delete mode 100644 openbb_terminal/common/technical_analysis/__init__.py delete mode 100644 openbb_terminal/common/technical_analysis/custom_indicators_model.py delete mode 100644 openbb_terminal/common/technical_analysis/custom_indicators_view.py delete mode 100644 openbb_terminal/common/technical_analysis/momentum_model.py delete mode 100644 openbb_terminal/common/technical_analysis/momentum_view.py delete mode 100644 openbb_terminal/common/technical_analysis/overlap_model.py delete mode 100644 openbb_terminal/common/technical_analysis/overlap_view.py delete mode 100644 openbb_terminal/common/technical_analysis/ta_helpers.py delete mode 100644 openbb_terminal/common/technical_analysis/trend_indicators_model.py delete mode 100644 openbb_terminal/common/technical_analysis/trend_indicators_view.py delete mode 100644 openbb_terminal/common/technical_analysis/volatility_model.py delete mode 100644 openbb_terminal/common/technical_analysis/volatility_view.py delete mode 100644 openbb_terminal/common/technical_analysis/volume_model.py delete mode 100644 openbb_terminal/common/technical_analysis/volume_view.py delete mode 100644 openbb_terminal/config_terminal.py delete mode 100644 openbb_terminal/core/__init__.py delete mode 100644 openbb_terminal/core/config/__init__.py delete mode 100644 openbb_terminal/core/config/paths.py delete mode 100644 openbb_terminal/core/config/paths_helper.py delete mode 100644 openbb_terminal/core/integration_tests/README.md delete mode 100644 openbb_terminal/core/integration_tests/controllers.json delete mode 100644 openbb_terminal/core/integration_tests/integration_controller.py delete mode 100644 openbb_terminal/core/integration_tests/integration_test_coverage.py delete mode 100644 openbb_terminal/core/integration_tests/utils.py delete mode 100644 openbb_terminal/core/log/__init__.py delete mode 100644 openbb_terminal/core/log/collection/__init__.py delete mode 100644 openbb_terminal/core/log/collection/log_sender.py delete mode 100644 openbb_terminal/core/log/collection/logging_clock.py delete mode 100644 openbb_terminal/core/log/collection/s3_sender.py delete mode 100644 openbb_terminal/core/log/constants.py delete mode 100644 openbb_terminal/core/log/generation/__init__.py delete mode 100644 openbb_terminal/core/log/generation/common.py delete mode 100644 openbb_terminal/core/log/generation/custom_logger.py delete mode 100644 openbb_terminal/core/log/generation/directories.py delete mode 100644 openbb_terminal/core/log/generation/expired_files.py delete mode 100644 openbb_terminal/core/log/generation/formatter_with_exceptions.py delete mode 100644 openbb_terminal/core/log/generation/path_tracking_file_handler.py delete mode 100644 openbb_terminal/core/log/generation/settings.py delete mode 100644 openbb_terminal/core/log/generation/settings_logger.py delete mode 100644 openbb_terminal/core/log/generation/user_logger.py delete mode 100644 openbb_terminal/core/models/__init__.py delete mode 100644 openbb_terminal/core/models/base_model.py delete mode 100644 openbb_terminal/core/models/credentials_model.py delete mode 100644 openbb_terminal/core/models/preferences_model.py delete mode 100644 openbb_terminal/core/models/profile_model.py delete mode 100644 openbb_terminal/core/models/sources_model.py delete mode 100644 openbb_terminal/core/models/system_model.py delete mode 100644 openbb_terminal/core/models/user_model.py delete mode 100644 openbb_terminal/core/plots/__init__.py delete mode 100644 openbb_terminal/core/plots/assets/Terminal_icon.png delete mode 100644 openbb_terminal/core/plots/backend.py delete mode 100644 openbb_terminal/core/plots/config/__init__.py delete mode 100644 openbb_terminal/core/plots/config/openbb_styles.py delete mode 100644 openbb_terminal/core/plots/no_import.py delete mode 100644 openbb_terminal/core/plots/plotly.html delete mode 100644 openbb_terminal/core/plots/plotly_helper.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/__init__.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/base.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/data_classes.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/plugins/custom_indicators_plugin.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/plugins/momentum_plugin.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/plugins/overlap_plugin.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/plugins/trend_indicators_plugin.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/plugins/volatility_plugin.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/plugins/volume_plugin.py delete mode 100644 openbb_terminal/core/plots/plotly_ta/ta_class.py delete mode 100644 openbb_terminal/core/plots/table.html delete mode 100644 openbb_terminal/core/scripts/__init__.py delete mode 100644 openbb_terminal/core/scripts/sdk_audit.py delete mode 100644 openbb_terminal/core/sdk/__init__.py delete mode 100644 openbb_terminal/core/sdk/controllers/__init__.py delete mode 100644 openbb_terminal/core/sdk/controllers/alt_sdk_controller.py delete mode 100644 openbb_terminal/core/sdk/controllers/crypto_sdk_controller.py delete mode 100644 openbb_terminal/core/sdk/controllers/etf_sdk_controller.py delete mode 100644 openbb_terminal/core/sdk/controllers/portfolio_sdk_controller.py delete mode 100644 openbb_terminal/core/sdk/controllers/stocks_sdk_controller.py delete mode 100644 openbb_terminal/core/sdk/models/__init__.py delete mode 100644 openbb_terminal/core/sdk/models/alt_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/crypto_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/econometrics_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/economy_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/etf_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/fixedincome_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/forecast_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/forex_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/funds_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/futures_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/keys_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/portfolio_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/qa_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/stocks_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/models/ta_sdk_model.py delete mode 100644 openbb_terminal/core/sdk/sdk_helpers.py delete mode 100644 openbb_terminal/core/sdk/sdk_init.py delete mode 100644 openbb_terminal/core/sdk/trail_map.csv delete mode 100644 openbb_terminal/core/sdk/trail_map_forecasting.csv delete mode 100644 openbb_terminal/core/sdk/trail_map_optimization.csv delete mode 100644 openbb_terminal/core/sdk/trailmap.py delete mode 100644 openbb_terminal/core/session/__init__.py delete mode 100644 openbb_terminal/core/session/banner.txt delete mode 100644 openbb_terminal/core/session/constants.py delete mode 100644 openbb_terminal/core/session/current_system.py delete mode 100644 openbb_terminal/core/session/current_user.py delete mode 100644 openbb_terminal/core/session/env_handler.py delete mode 100644 openbb_terminal/core/session/hub_model.py delete mode 100644 openbb_terminal/core/session/local_model.py delete mode 100644 openbb_terminal/core/session/routines_handler.py delete mode 100644 openbb_terminal/core/session/sdk_session.py delete mode 100644 openbb_terminal/core/session/session_controller.py delete mode 100644 openbb_terminal/core/session/session_model.py delete mode 100644 openbb_terminal/core/session/sources_handler.py delete mode 100644 openbb_terminal/core/session/utils.py delete mode 100644 openbb_terminal/core/sources/utils.py delete mode 100644 openbb_terminal/cryptocurrency/README.md delete mode 100644 openbb_terminal/cryptocurrency/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/coinbase_helpers.py delete mode 100644 openbb_terminal/cryptocurrency/coinpaprika_helpers.py delete mode 100644 openbb_terminal/cryptocurrency/crypto_controller.py delete mode 100644 openbb_terminal/cryptocurrency/crypto_models.py delete mode 100644 openbb_terminal/cryptocurrency/crypto_views.py delete mode 100644 openbb_terminal/cryptocurrency/cryptocurrency_helpers.py delete mode 100644 openbb_terminal/cryptocurrency/data/README.md delete mode 100644 openbb_terminal/cryptocurrency/data/binance_gecko_map.json delete mode 100644 openbb_terminal/cryptocurrency/data/coinbase_gecko_map.json delete mode 100644 openbb_terminal/cryptocurrency/data/coingecko_categories.json delete mode 100644 openbb_terminal/cryptocurrency/data/coingecko_coins.json delete mode 100644 openbb_terminal/cryptocurrency/data/coinpaprika_coins.json delete mode 100644 openbb_terminal/cryptocurrency/data/defillama_dapps.json delete mode 100644 openbb_terminal/cryptocurrency/data/erc20_coins.json delete mode 100644 openbb_terminal/cryptocurrency/data/finbrain_coins.json delete mode 100644 openbb_terminal/cryptocurrency/data/santiment_slugs.json delete mode 100644 openbb_terminal/cryptocurrency/data/yahoofinance_coins.json delete mode 100644 openbb_terminal/cryptocurrency/dataframe_helpers.py delete mode 100644 openbb_terminal/cryptocurrency/defi/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/defi/coindix_model.py delete mode 100644 openbb_terminal/cryptocurrency/defi/coindix_view.py delete mode 100644 openbb_terminal/cryptocurrency/defi/cryptosaurio_model.py delete mode 100644 openbb_terminal/cryptocurrency/defi/cryptosaurio_view.py delete mode 100644 openbb_terminal/cryptocurrency/defi/defi_controller.py delete mode 100644 openbb_terminal/cryptocurrency/defi/llama_model.py delete mode 100644 openbb_terminal/cryptocurrency/defi/llama_view.py delete mode 100644 openbb_terminal/cryptocurrency/defi/smartstake_model.py delete mode 100644 openbb_terminal/cryptocurrency/defi/smartstake_view.py delete mode 100644 openbb_terminal/cryptocurrency/defi/substack_model.py delete mode 100644 openbb_terminal/cryptocurrency/defi/substack_view.py delete mode 100644 openbb_terminal/cryptocurrency/defi/terraengineer_model.py delete mode 100644 openbb_terminal/cryptocurrency/defi/terraengineer_view.py delete mode 100644 openbb_terminal/cryptocurrency/defi/terramoney_fcd_model.py delete mode 100644 openbb_terminal/cryptocurrency/defi/terramoney_fcd_view.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/coinmarketcap_model.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/coinmarketcap_view.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/coinpaprika_model.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/coinpaprika_view.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/cryptostats_model.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/cryptostats_view.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/dappradar_model.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/dappradar_view.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/discovery_controller.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/pycoingecko_model.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/pycoingecko_view.py delete mode 100644 openbb_terminal/cryptocurrency/discovery/sdk_helpers.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/binance_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/binance_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/ccxt_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/ccxt_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/coinbase_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/coinbase_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/coinglass_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/coinglass_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/coinpaprika_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/coinpaprika_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/cryptopanic_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/dd_controller.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/due_diligence_api.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/finbrain_crypto_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/glassnode_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/glassnode_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/messari_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/messari_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/pycoingecko_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/pycoingecko_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/santiment_model.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/santiment_view.py delete mode 100644 openbb_terminal/cryptocurrency/due_diligence/sdk_helper.py delete mode 100644 openbb_terminal/cryptocurrency/nft/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/nft/nft_controller.py delete mode 100644 openbb_terminal/cryptocurrency/nft/nftpricefloor_model.py delete mode 100644 openbb_terminal/cryptocurrency/nft/nftpricefloor_view.py delete mode 100644 openbb_terminal/cryptocurrency/nft/opensea_model.py delete mode 100644 openbb_terminal/cryptocurrency/nft/opensea_view.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/bitquery_model.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/bitquery_view.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/blockchain_model.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/blockchain_view.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/ethgasstation_model.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/ethgasstation_view.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/ethplorer_model.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/ethplorer_view.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/onchain_controller.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/topledger_model.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/topledger_view.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/whale_alert_model.py delete mode 100644 openbb_terminal/cryptocurrency/onchain/whale_alert_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/overview/blockchaincenter_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/blockchaincenter_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/coinbase_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/coinbase_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/coinpaprika_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/coinpaprika_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/cryptopanic_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/cryptopanic_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/glassnode_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/glassnode_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/loanscan_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/loanscan_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/overview_controller.py delete mode 100644 openbb_terminal/cryptocurrency/overview/pycoingecko_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/pycoingecko_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/rekt_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/rekt_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/sdk_helpers.py delete mode 100644 openbb_terminal/cryptocurrency/overview/tokenterminal_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/tokenterminal_view.py delete mode 100644 openbb_terminal/cryptocurrency/overview/withdrawalfees_model.py delete mode 100644 openbb_terminal/cryptocurrency/overview/withdrawalfees_view.py delete mode 100644 openbb_terminal/cryptocurrency/pycoingecko_helpers.py delete mode 100644 openbb_terminal/cryptocurrency/pyth_model.py delete mode 100644 openbb_terminal/cryptocurrency/pyth_view.py delete mode 100644 openbb_terminal/cryptocurrency/quantitative_analysis/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/quantitative_analysis/qa_controller.py delete mode 100644 openbb_terminal/cryptocurrency/technical_analysis/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/technical_analysis/ta_controller.py delete mode 100644 openbb_terminal/cryptocurrency/tools/__init__.py delete mode 100644 openbb_terminal/cryptocurrency/tools/tools_controller.py delete mode 100644 openbb_terminal/cryptocurrency/tools/tools_helpers.py delete mode 100644 openbb_terminal/cryptocurrency/tools/tools_model.py delete mode 100644 openbb_terminal/cryptocurrency/tools/tools_view.py delete mode 100644 openbb_terminal/custom_reports/lunar_crash/coin_highs.png delete mode 100644 openbb_terminal/custom_reports/lunar_crash/luna_crash.html delete mode 100644 openbb_terminal/custom_reports/lunar_crash/luna_supply.png delete mode 100644 openbb_terminal/custom_reports/lunar_crash/luna_terra.png delete mode 100644 openbb_terminal/custom_reports/lunar_crash/lunatics.ipynb delete mode 100644 openbb_terminal/custom_reports/lunar_crash/tvl.png delete mode 100644 openbb_terminal/custom_reports/lunar_crash/ust_terra.png delete mode 100644 openbb_terminal/dashboards/__init__.py delete mode 100644 openbb_terminal/dashboards/dashboards_controller.py delete mode 100644 openbb_terminal/dashboards/stream/Forecasting.py delete mode 100644 openbb_terminal/dashboards/stream/__init__.py delete mode 100644 openbb_terminal/dashboards/stream/common_vars.py delete mode 100644 openbb_terminal/dashboards/stream/pages/Chains.py delete mode 100644 openbb_terminal/dashboards/stream/pages/Correlation.py delete mode 100644 openbb_terminal/dashboards/stream/pages/Futures.py delete mode 100644 openbb_terminal/dashboards/stream/pages/Indicators.py delete mode 100644 openbb_terminal/dashboards/stream/pages/Options.py delete mode 100644 openbb_terminal/dashboards/stream/pages/Short_Data.py delete mode 100644 openbb_terminal/dashboards/stream/pages/Stocks.py delete mode 100644 openbb_terminal/dashboards/stream/streamlit_helpers.py delete mode 100644 openbb_terminal/dashboards/streamlit_run.py delete mode 100644 openbb_terminal/dashboards/voila/chains.ipynb delete mode 100644 openbb_terminal/dashboards/voila/correlation.ipynb delete mode 100644 openbb_terminal/dashboards/voila/forecast.ipynb delete mode 100644 openbb_terminal/dashboards/voila/futures.ipynb delete mode 100644 openbb_terminal/dashboards/voila/shortdata.ipynb delete mode 100644 openbb_terminal/dashboards/voila/stocks.ipynb delete mode 100644 openbb_terminal/decorators.py delete mode 100644 openbb_terminal/econometrics/__init__.py delete mode 100644 openbb_terminal/econometrics/econometrics_controller.py delete mode 100644 openbb_terminal/econometrics/econometrics_helpers.py delete mode 100644 openbb_terminal/econometrics/econometrics_model.py delete mode 100644 openbb_terminal/econometrics/econometrics_view.py delete mode 100644 openbb_terminal/econometrics/regression_model.py delete mode 100644 openbb_terminal/econometrics/regression_view.py delete mode 100644 openbb_terminal/economy/README.md delete mode 100644 openbb_terminal/economy/__init__.py delete mode 100644 openbb_terminal/economy/commodity_model.py delete mode 100644 openbb_terminal/economy/commodity_view.py delete mode 100644 openbb_terminal/economy/datasets/NASDAQ_CountryCodes.csv delete mode 100644 openbb_terminal/economy/datasets/cpi.csv delete mode 100644 openbb_terminal/economy/datasets/harmonized_cpi.csv delete mode 100644 openbb_terminal/economy/econdb_model.py delete mode 100644 openbb_terminal/economy/econdb_view.py delete mode 100644 openbb_terminal/economy/economy_controller.py delete mode 100644 openbb_terminal/economy/economy_helpers.py delete mode 100644 openbb_terminal/economy/fedreserve_model.py delete mode 100644 openbb_terminal/economy/fedreserve_view.py delete mode 100644 openbb_terminal/economy/finviz_model.py delete mode 100644 openbb_terminal/economy/finviz_view.py delete mode 100644 openbb_terminal/economy/fred_model.py delete mode 100644 openbb_terminal/economy/fred_view.py delete mode 100644 openbb_terminal/economy/nasdaq_model.py delete mode 100644 openbb_terminal/economy/nasdaq_view.py delete mode 100644 openbb_terminal/economy/oecd_model.py delete mode 100644 openbb_terminal/economy/oecd_view.py delete mode 100644 openbb_terminal/economy/plot_view.py delete mode 100644 openbb_terminal/economy/quantitative_analysis/__init__.py delete mode 100644 openbb_terminal/economy/quantitative_analysis/qa_controller.py delete mode 100644 openbb_terminal/economy/sdk_helpers.py delete mode 100644 openbb_terminal/economy/wsj_model.py delete mode 100644 openbb_terminal/economy/wsj_view.py delete mode 100644 openbb_terminal/economy/yfinance_model.py delete mode 100644 openbb_terminal/economy/yfinance_view.py delete mode 100644 openbb_terminal/etf/README.md delete mode 100644 openbb_terminal/etf/__init__.py delete mode 100644 openbb_terminal/etf/discovery/disc_controller.py delete mode 100644 openbb_terminal/etf/discovery/wsj_model.py delete mode 100644 openbb_terminal/etf/discovery/wsj_view.py delete mode 100644 openbb_terminal/etf/etf_controller.py delete mode 100644 openbb_terminal/etf/etf_helper.py delete mode 100644 openbb_terminal/etf/etfs.csv delete mode 100644 openbb_terminal/etf/financedatabase_model.py delete mode 100644 openbb_terminal/etf/financedatabase_view.py delete mode 100644 openbb_terminal/etf/fmp_model.py delete mode 100644 openbb_terminal/etf/fmp_view.py delete mode 100644 openbb_terminal/etf/stockanalysis_model.py delete mode 100644 openbb_terminal/etf/stockanalysis_view.py delete mode 100644 openbb_terminal/etf/technical_analysis/ta_controller.py delete mode 100644 openbb_terminal/featflags_controller.py delete mode 100644 openbb_terminal/fixedincome/__init__.py delete mode 100644 openbb_terminal/fixedincome/commercial_paper.csv delete mode 100644 openbb_terminal/fixedincome/corporate_spot_rates.csv delete mode 100644 openbb_terminal/fixedincome/ecb_model.py delete mode 100644 openbb_terminal/fixedincome/ecb_view.py delete mode 100644 openbb_terminal/fixedincome/fixedincome_controller.py delete mode 100644 openbb_terminal/fixedincome/fred_model.py delete mode 100644 openbb_terminal/fixedincome/fred_view.py delete mode 100644 openbb_terminal/fixedincome/ice_bofa_indices.csv delete mode 100644 openbb_terminal/fixedincome/oecd_model.py delete mode 100644 openbb_terminal/fixedincome/oecd_view.py delete mode 100644 openbb_terminal/fixedincome/yfinance_model.py delete mode 100644 openbb_terminal/fixedincome/yfinance_view.py delete mode 100644 openbb_terminal/forecast/__init__.py delete mode 100644 openbb_terminal/forecast/anom_model.py delete mode 100644 openbb_terminal/forecast/anom_view.py delete mode 100644 openbb_terminal/forecast/autoarima_model.py delete mode 100644 openbb_terminal/forecast/autoarima_view.py delete mode 100644 openbb_terminal/forecast/autoces_model.py delete mode 100644 openbb_terminal/forecast/autoces_view.py delete mode 100644 openbb_terminal/forecast/autoets_model.py delete mode 100644 openbb_terminal/forecast/autoets_view.py delete mode 100644 openbb_terminal/forecast/autoselect_model.py delete mode 100644 openbb_terminal/forecast/autoselect_view.py delete mode 100644 openbb_terminal/forecast/brnn_model.py delete mode 100644 openbb_terminal/forecast/brnn_view.py delete mode 100644 openbb_terminal/forecast/expo_model.py delete mode 100644 openbb_terminal/forecast/expo_view.py delete mode 100644 openbb_terminal/forecast/forecast.ipynb delete mode 100644 openbb_terminal/forecast/forecast_controller.py delete mode 100644 openbb_terminal/forecast/forecast_model.py delete mode 100644 openbb_terminal/forecast/forecast_view.py delete mode 100644 openbb_terminal/forecast/helpers.py delete mode 100644 openbb_terminal/forecast/linregr_model.py delete mode 100644 openbb_terminal/forecast/linregr_view.py delete mode 100644 openbb_terminal/forecast/mstl_model.py delete mode 100644 openbb_terminal/forecast/mstl_view.py delete mode 100644 openbb_terminal/forecast/nbeats_model.py delete mode 100644 openbb_terminal/forecast/nbeats_view.py delete mode 100644 openbb_terminal/forecast/nhits_model.py delete mode 100644 openbb_terminal/forecast/nhits_view.py delete mode 100644 openbb_terminal/forecast/regr_model.py delete mode 100644 openbb_terminal/forecast/regr_view.py delete mode 100644 openbb_terminal/forecast/rnn_model.py delete mode 100644 openbb_terminal/forecast/rnn_view.py delete mode 100644 openbb_terminal/forecast/rwd_model.py delete mode 100644 openbb_terminal/forecast/rwd_view.py delete mode 100644 openbb_terminal/forecast/seasonalnaive_model.py delete mode 100644 openbb_terminal/forecast/seasonalnaive_view.py delete mode 100644 openbb_terminal/forecast/tcn_model.py delete mode 100644 openbb_terminal/forecast/tcn_view.py delete mode 100644 openbb_terminal/forecast/tft_model.py delete mode 100644 openbb_terminal/forecast/tft_view.py delete mode 100644 openbb_terminal/forecast/theta_model.py delete mode 100644 openbb_terminal/forecast/theta_view.py delete mode 100644 openbb_terminal/forecast/timegpt_model.py delete mode 100644 openbb_terminal/forecast/timegpt_view.py delete mode 100644 openbb_terminal/forecast/trans_model.py delete mode 100644 openbb_terminal/forecast/trans_view.py delete mode 100644 openbb_terminal/forecast/whisper_model.py delete mode 100644 openbb_terminal/forecast/whisper_utils.py delete mode 100644 openbb_terminal/forex/README.md delete mode 100644 openbb_terminal/forex/__init__.py delete mode 100644 openbb_terminal/forex/av_model.py delete mode 100644 openbb_terminal/forex/av_view.py delete mode 100644 openbb_terminal/forex/data/av_forex_currencies.csv delete mode 100644 openbb_terminal/forex/data/polygon_tickers.csv delete mode 100644 openbb_terminal/forex/data/yahoofinance_forex.json delete mode 100644 openbb_terminal/forex/forex_controller.py delete mode 100644 openbb_terminal/forex/forex_helper.py delete mode 100644 openbb_terminal/forex/fxempire_model.py delete mode 100644 openbb_terminal/forex/fxempire_view.py delete mode 100644 openbb_terminal/forex/polygon_model.py delete mode 100644 openbb_terminal/forex/quantitative_analysis/__init__.py delete mode 100644 openbb_terminal/forex/quantitative_analysis/qa_controller.py delete mode 100644 openbb_terminal/forex/sdk_helpers.py delete mode 100644 openbb_terminal/forex/technical_analysis/__init__.py delete mode 100644 openbb_terminal/forex/technical_analysis/ta_controller.py delete mode 100644 openbb_terminal/futures/__init__.py delete mode 100644 openbb_terminal/futures/databento_view.py delete mode 100644 openbb_terminal/futures/futures_controller.py delete mode 100644 openbb_terminal/futures/futures_helper.py delete mode 100644 openbb_terminal/futures/sdk_helper.py delete mode 100644 openbb_terminal/futures/yfinance_model.py delete mode 100644 openbb_terminal/futures/yfinance_view.py delete mode 100644 openbb_terminal/helper_classes.py delete mode 100644 openbb_terminal/helper_funcs.py delete mode 100644 openbb_terminal/helpers_denomination.py delete mode 100644 openbb_terminal/intro.json delete mode 100644 openbb_terminal/keys_controller.py delete mode 100644 openbb_terminal/keys_model.py delete mode 100644 openbb_terminal/keys_view.py delete mode 100644 openbb_terminal/loggers.py delete mode 100644 openbb_terminal/menu.py delete mode 100644 openbb_terminal/miscellaneous/documentation_scripts/documentation_commands.csv delete mode 100644 openbb_terminal/miscellaneous/documentation_scripts/documentation_commands.json delete mode 100644 openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk.csv delete mode 100644 openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk.json delete mode 100644 openbb_terminal/miscellaneous/documentation_scripts/generate_documentation_commands.py delete mode 100644 openbb_terminal/miscellaneous/exports/alternative/covid/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/common/behavioural_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/common/prediction_techniques/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/common/quantitative_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/common/technical_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/custom/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/econometrics/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/economy/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/etf/movers/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/etf/screeners/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/mutual_funds/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/portfolio/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/portfolio/views/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/backtesting/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/behavioural_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/comparison_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/dark_pool_shorts/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/discovery/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/due_diligence/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/fundamental_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/government/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/insider/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/options/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/prediction_techniques/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/quantitative_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/raw_data/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/sector_industry_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/exports/stocks/technical_analysis/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/futures/futures.csv delete mode 100644 openbb_terminal/miscellaneous/i18n/en.yml delete mode 100644 openbb_terminal/miscellaneous/i18n/help_translation.ipynb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/.gitkeep delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/README.MD delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/account/test_account.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_alt.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_covid.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_oss.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_realestate.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_crypto.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_dd.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_defi.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_discovery.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_nft.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_onchain.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_overview.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_qa.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_ta.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_tools.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/dashboards/test_dashboards.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/econometrics/test_econometrics.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_economy.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_qa.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_ca.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_disc.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_etf.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_ta.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/fixedincome/test_fixedincome.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/forecast/test_forecast.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_forex.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_oanda.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_qa.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_ta.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/futures/test_futures.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/keys/test_keys.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/mutual_funds/test_mutual_fund.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_params.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_po.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_portfolio.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/reports/test_reports.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ba.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_bt.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ca.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_disc.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_dps.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_fa.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_forecast.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_gov.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_hedge.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_insider.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_options.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_qa.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_res.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_screener.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_stocks.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ta.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_tradinghours.openbb delete mode 100644 openbb_terminal/miscellaneous/integration_tests_scripts/terminal/test_news.openbb delete mode 100644 openbb_terminal/miscellaneous/models/all_api_keys.json delete mode 100644 openbb_terminal/miscellaneous/models/hub_credentials.json delete mode 100644 openbb_terminal/miscellaneous/models/local_credentials.json delete mode 100644 openbb_terminal/miscellaneous/portfolio/allocation_example.xlsx delete mode 100644 openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx delete mode 100644 openbb_terminal/miscellaneous/portfolio/parameters_template.xlsx delete mode 100644 openbb_terminal/miscellaneous/routines/routine_example.openbb delete mode 100644 openbb_terminal/miscellaneous/sources/openbb_default.json delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Agriculture.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Agro-Chemicals.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Air-Transportation.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Apparel.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Banks.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Brokers.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Building-Materials.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Business-Services.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Closed-End-Funds.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Construction.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Credit-Industry.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Durable-Goods.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Electronics.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Furniture.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Gold-Silver.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Grocery.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Ground-Freight.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Guided-Missiles.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Health.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Hotels.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Insurance-Agents.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Insurance-Carriers.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Investment-Offices.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Legal.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Lumber.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Manufacturing.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Mining.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Mortgages.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Nondurable-Goods.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Personal-Credit.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Pharmaceuticals.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Professional-Services.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Railroads.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Real-Estate.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Restaurants.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Retail-Stores.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Retail-Trade.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Tobacco.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Utility-Communications.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Utility-Gas.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/Wholesale-Trade.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/template.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/insider/whales.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/5pct_above_low.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/all_parameters.txt delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/analyst_strong_buy.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/break_out_stocks.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/buffett_like.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/bull_runs_over_10pct.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/channel_up_and_low_debt_and_sma_50and200.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/cheap_bottom_dividend.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/cheap_dividend.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/cheap_oversold.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/continued_momentum_scan.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/death_cross.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/djia_components.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/golden_cross.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/golden_cross_penny.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/growth_stocks.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/heavy_inst_ins.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/high_vol_and_low_debt.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/modified_dreman.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/modified_neff.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/news_scanner.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/oversold.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/oversold_under_3dol.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/oversold_under_5dol.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/potential_reversals.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/recent_growth_and_support.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/rosenwald.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/rosenwald_gtfo.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/sdk_guide_preset.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/sexy_year.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/short_squeeze_scan.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/simplistic_momentum_scanner_under_7dol.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_basic_materials_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_communication_services_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_consumer_cyclical_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_consumer_defensive_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_energy_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_financial_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_healthcare_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_industrials_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_real_estate_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_technology_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/sp500_utilities_sector.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/stocks_strong_support_levels.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/top_performers_all.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/top_performers_healthcare.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/top_performers_tech.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/under_15dol_stocks.ini delete mode 100644 openbb_terminal/miscellaneous/stocks/screener/undervalue.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/unusual_volume.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/value_stocks.ini delete mode 100755 openbb_terminal/miscellaneous/stocks/screener/weak_support_and_top_performers.ini delete mode 100644 openbb_terminal/mutual_funds/README.md delete mode 100644 openbb_terminal/mutual_funds/__init__.py delete mode 100644 openbb_terminal/mutual_funds/avanza_fund_ID.csv delete mode 100644 openbb_terminal/mutual_funds/avanza_model.py delete mode 100644 openbb_terminal/mutual_funds/avanza_view.py delete mode 100644 openbb_terminal/mutual_funds/mstarpy_model.py delete mode 100644 openbb_terminal/mutual_funds/mstarpy_view.py delete mode 100644 openbb_terminal/mutual_funds/mutual_fund_controller.py delete mode 100644 openbb_terminal/mutual_funds/mutual_funds_utils.py create mode 100644 openbb_terminal/openbb_terminal/__init__.py rename openbb_terminal/{account => openbb_terminal/argparse_translator}/__init__.py (100%) create mode 100644 openbb_terminal/openbb_terminal/argparse_translator/argparse_class_processor.py create mode 100644 openbb_terminal/openbb_terminal/argparse_translator/argparse_translator.py create mode 100644 openbb_terminal/openbb_terminal/argparse_translator/obbject_registry.py create mode 100644 openbb_terminal/openbb_terminal/assets/i18n/en.yml create mode 100644 openbb_terminal/openbb_terminal/assets/routines/routine_example.openbb rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/Consolas.ttf (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/dark.mpfstyle.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/dark.mplrc.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/dark.mplstyle (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/dark.pltstyle.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/dark.richstyle.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/light.mpfstyle.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/light.mplrc.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/light.mplstyle (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/light.pltstyle.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/light.richstyle.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/default/tables.pltstyle.json (100%) rename openbb_terminal/{miscellaneous => openbb_terminal/assets}/styles/user/openbb.richstyle.json (100%) create mode 100644 openbb_terminal/openbb_terminal/cli.py create mode 100644 openbb_terminal/openbb_terminal/config/__init__.py rename openbb_terminal/{custom_prompt_toolkit.py => openbb_terminal/config/completer.py} (96%) create mode 100644 openbb_terminal/openbb_terminal/config/console.py create mode 100644 openbb_terminal/openbb_terminal/config/constants.py create mode 100644 openbb_terminal/openbb_terminal/config/menu_text.py create mode 100644 openbb_terminal/openbb_terminal/config/setup.py create mode 100644 openbb_terminal/openbb_terminal/config/style.py rename openbb_terminal/{parent_classes.py => openbb_terminal/controllers/base_controller.py} (50%) create mode 100644 openbb_terminal/openbb_terminal/controllers/base_platform_controller.py rename openbb_terminal/{core/completer => openbb_terminal/controllers}/choices.py (82%) create mode 100644 openbb_terminal/openbb_terminal/controllers/feature_flags_controller.py create mode 100644 openbb_terminal/openbb_terminal/controllers/hub_service.py create mode 100644 openbb_terminal/openbb_terminal/controllers/platform_controller_factory.py rename openbb_terminal/{routine_functions.py => openbb_terminal/controllers/script_parser.py} (98%) rename openbb_terminal/{ => openbb_terminal/controllers}/terminal_controller.py (55%) create mode 100644 openbb_terminal/openbb_terminal/controllers/utils.py create mode 100644 openbb_terminal/openbb_terminal/models/settings.py create mode 100644 openbb_terminal/openbb_terminal/session.py create mode 100644 openbb_terminal/poetry.lock delete mode 100644 openbb_terminal/portfolio/__init__.py delete mode 100644 openbb_terminal/portfolio/allocation_model.py delete mode 100644 openbb_terminal/portfolio/attribution_model.py delete mode 100644 openbb_terminal/portfolio/metrics_model.py delete mode 100644 openbb_terminal/portfolio/portfolio_controller.py delete mode 100644 openbb_terminal/portfolio/portfolio_engine.py delete mode 100644 openbb_terminal/portfolio/portfolio_helper.py delete mode 100644 openbb_terminal/portfolio/portfolio_model.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/__init__.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/excel_model.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/optimizer_helper.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/optimizer_model.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/optimizer_view.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/parameters/Parameter.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/parameters/params_controller.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/parameters/params_helpers.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/parameters/params_statics.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/parameters/params_view.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/po_controller.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/po_engine.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/po_model.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/po_view.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/statics.py delete mode 100644 openbb_terminal/portfolio/portfolio_optimization/yahoo_finance_model.py delete mode 100644 openbb_terminal/portfolio/portfolio_view.py delete mode 100644 openbb_terminal/portfolio/statics.py create mode 100644 openbb_terminal/pyproject.toml delete mode 100644 openbb_terminal/reports/.ipynb_checkpoints/test-checkpoint.html delete mode 100644 openbb_terminal/reports/README.md delete mode 100644 openbb_terminal/reports/__init__.py delete mode 100644 openbb_terminal/reports/data/crypto_tickers.csv delete mode 100644 openbb_terminal/reports/data/etf_tickers.csv delete mode 100644 openbb_terminal/reports/data/stocks_tickers.csv delete mode 100644 openbb_terminal/reports/reports_controller.py delete mode 100644 openbb_terminal/reports/reports_model.py delete mode 100644 openbb_terminal/reports/stored/.gitkeep delete mode 100644 openbb_terminal/reports/stored/20220512_040312_equity_NVDA.html delete mode 100644 openbb_terminal/reports/templates/OpenBB_reports_logo.png delete mode 100644 openbb_terminal/reports/templates/crypto.ipynb delete mode 100644 openbb_terminal/reports/templates/economy.ipynb delete mode 100644 openbb_terminal/reports/templates/equity.ipynb delete mode 100644 openbb_terminal/reports/templates/etf.ipynb delete mode 100644 openbb_terminal/reports/templates/floppy-disc.png delete mode 100644 openbb_terminal/reports/templates/forecast.ipynb delete mode 100644 openbb_terminal/reports/templates/forex.ipynb delete mode 100644 openbb_terminal/reports/templates/portfolio.ipynb delete mode 100644 openbb_terminal/reports/widget_helpers.py delete mode 100644 openbb_terminal/reports/widgets/card.j2 delete mode 100644 openbb_terminal/reports/widgets/report.css delete mode 100644 openbb_terminal/reports/widgets/report.j2 delete mode 100644 openbb_terminal/reports/widgets/row.j2 delete mode 100644 openbb_terminal/reports/widgets/style.css delete mode 100644 openbb_terminal/requirements-full.txt delete mode 100644 openbb_terminal/requirements.txt delete mode 100644 openbb_terminal/rich_config.py delete mode 100644 openbb_terminal/sdk.py delete mode 100644 openbb_terminal/settings_controller.py delete mode 100644 openbb_terminal/sources_controller.py delete mode 100644 openbb_terminal/stocks/README.md delete mode 100644 openbb_terminal/stocks/__init__.py delete mode 100644 openbb_terminal/stocks/backtesting/__init__.py delete mode 100644 openbb_terminal/stocks/backtesting/bt_controller.py delete mode 100644 openbb_terminal/stocks/backtesting/bt_model.py delete mode 100644 openbb_terminal/stocks/backtesting/bt_view.py delete mode 100644 openbb_terminal/stocks/behavioural_analysis/__init__.py delete mode 100644 openbb_terminal/stocks/behavioural_analysis/ba_controller.py delete mode 100644 openbb_terminal/stocks/behavioural_analysis/finnhub_model.py delete mode 100644 openbb_terminal/stocks/behavioural_analysis/finnhub_view.py delete mode 100644 openbb_terminal/stocks/cboe_model.py delete mode 100644 openbb_terminal/stocks/cboe_view.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/__init__.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/ca_controller.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/finbrain_model.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/finbrain_view.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/finnhub_model.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/finviz_compare_model.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/finviz_compare_view.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/marketwatch_model.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/marketwatch_view.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/polygon_model.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/sdk_helpers.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/yahoo_finance_model.py delete mode 100644 openbb_terminal/stocks/comparison_analysis/yahoo_finance_view.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/__init__.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/dps_controller.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/finra_model.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/finra_view.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/ibkr_model.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/ibkr_view.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/quandl_model.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/quandl_view.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/sec_model.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/sec_view.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/shortinterest_model.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/shortinterest_view.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/stockgrid_model.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/stockgrid_view.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/yahoofinance_model.py delete mode 100644 openbb_terminal/stocks/dark_pool_shorts/yahoofinance_view.py delete mode 100644 openbb_terminal/stocks/databento_model.py delete mode 100644 openbb_terminal/stocks/discovery/__init__.py delete mode 100644 openbb_terminal/stocks/discovery/ark_model.py delete mode 100644 openbb_terminal/stocks/discovery/ark_view.py delete mode 100644 openbb_terminal/stocks/discovery/disc_controller.py delete mode 100644 openbb_terminal/stocks/discovery/disc_helpers.py delete mode 100644 openbb_terminal/stocks/discovery/fidelity_model.py delete mode 100644 openbb_terminal/stocks/discovery/fidelity_view.py delete mode 100644 openbb_terminal/stocks/discovery/finnhub_model.py delete mode 100644 openbb_terminal/stocks/discovery/finnhub_view.py delete mode 100644 openbb_terminal/stocks/discovery/finviz_model.py delete mode 100644 openbb_terminal/stocks/discovery/finviz_view.py delete mode 100644 openbb_terminal/stocks/discovery/fmp_view.py delete mode 100644 openbb_terminal/stocks/discovery/nasdaq_model.py delete mode 100644 openbb_terminal/stocks/discovery/nasdaq_view.py delete mode 100644 openbb_terminal/stocks/discovery/seeking_alpha_model.py delete mode 100644 openbb_terminal/stocks/discovery/seeking_alpha_view.py delete mode 100644 openbb_terminal/stocks/discovery/shortinterest_model.py delete mode 100644 openbb_terminal/stocks/discovery/shortinterest_view.py delete mode 100644 openbb_terminal/stocks/discovery/yahoofinance_model.py delete mode 100644 openbb_terminal/stocks/discovery/yahoofinance_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/__init__.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/av_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/av_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/business_insider_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/business_insider_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/csimarket_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/csimarket_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/dcf_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/dcf_static.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/dcf_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/eclect_us_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/eclect_us_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/eodhd_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/eodhd_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/fa_controller.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/fa_helper.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/finnhub_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/finnhub_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/finviz_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/finviz_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/fmp_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/fmp_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/key_metrics_explained.txt delete mode 100644 openbb_terminal/stocks/fundamental_analysis/marketwatch_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/marketwatch_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/nasdaq_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/nasdaq_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/polygon_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/polygon_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/sdk_helpers.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/seeking_alpha_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/seeking_alpha_view.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/yahoo_finance_model.py delete mode 100644 openbb_terminal/stocks/fundamental_analysis/yahoo_finance_view.py delete mode 100644 openbb_terminal/stocks/government/__init__.py delete mode 100644 openbb_terminal/stocks/government/gov_controller.py delete mode 100644 openbb_terminal/stocks/government/quiverquant_model.py delete mode 100644 openbb_terminal/stocks/government/quiverquant_view.py delete mode 100644 openbb_terminal/stocks/insider/__init__.py delete mode 100644 openbb_terminal/stocks/insider/businessinsider_model.py delete mode 100644 openbb_terminal/stocks/insider/businessinsider_view.py delete mode 100644 openbb_terminal/stocks/insider/finviz_model.py delete mode 100644 openbb_terminal/stocks/insider/finviz_view.py delete mode 100644 openbb_terminal/stocks/insider/insider_controller.py delete mode 100644 openbb_terminal/stocks/insider/openinsider_model.py delete mode 100644 openbb_terminal/stocks/insider/openinsider_view.py delete mode 100644 openbb_terminal/stocks/insider/sdk_helper.py delete mode 100644 openbb_terminal/stocks/mappings/Mic_Codes.csv delete mode 100644 openbb_terminal/stocks/options/__init__.py delete mode 100644 openbb_terminal/stocks/options/alphaquery_model.py delete mode 100644 openbb_terminal/stocks/options/alphaquery_view.py delete mode 100644 openbb_terminal/stocks/options/barchart_model.py delete mode 100644 openbb_terminal/stocks/options/barchart_view.py delete mode 100644 openbb_terminal/stocks/options/calculator_model.py delete mode 100644 openbb_terminal/stocks/options/calculator_view.py delete mode 100644 openbb_terminal/stocks/options/cboe_model.py delete mode 100644 openbb_terminal/stocks/options/chartexchange_model.py delete mode 100644 openbb_terminal/stocks/options/chartexchange_view.py delete mode 100644 openbb_terminal/stocks/options/fdscanner_model.py delete mode 100644 openbb_terminal/stocks/options/fdscanner_view.py delete mode 100644 openbb_terminal/stocks/options/intrinio_model.py delete mode 100644 openbb_terminal/stocks/options/intrinio_view.py delete mode 100644 openbb_terminal/stocks/options/nasdaq_model.py delete mode 100644 openbb_terminal/stocks/options/op_helpers.py delete mode 100644 openbb_terminal/stocks/options/options_chains_model.py delete mode 100644 openbb_terminal/stocks/options/options_chains_view.py delete mode 100644 openbb_terminal/stocks/options/options_controller.py delete mode 100644 openbb_terminal/stocks/options/options_sdk_helper.py delete mode 100644 openbb_terminal/stocks/options/options_view.py delete mode 100644 openbb_terminal/stocks/options/tmx_model.py delete mode 100644 openbb_terminal/stocks/options/tradier_model.py delete mode 100644 openbb_terminal/stocks/options/tradier_view.py delete mode 100644 openbb_terminal/stocks/options/yfinance_model.py delete mode 100644 openbb_terminal/stocks/options/yfinance_view.py delete mode 100644 openbb_terminal/stocks/quantitative_analysis/__init__.py delete mode 100644 openbb_terminal/stocks/quantitative_analysis/beta_model.py delete mode 100644 openbb_terminal/stocks/quantitative_analysis/beta_view.py delete mode 100644 openbb_terminal/stocks/quantitative_analysis/factors_model.py delete mode 100644 openbb_terminal/stocks/quantitative_analysis/factors_view.py delete mode 100644 openbb_terminal/stocks/quantitative_analysis/qa_controller.py delete mode 100644 openbb_terminal/stocks/quantitative_analysis/qa_model.py delete mode 100644 openbb_terminal/stocks/research/__init__.py delete mode 100644 openbb_terminal/stocks/research/res_controller.py delete mode 100644 openbb_terminal/stocks/screener/__init__.py delete mode 100644 openbb_terminal/stocks/screener/finviz_model.py delete mode 100644 openbb_terminal/stocks/screener/finviz_view.py delete mode 100644 openbb_terminal/stocks/screener/screener_controller.py delete mode 100644 openbb_terminal/stocks/screener/screener_helper.py delete mode 100644 openbb_terminal/stocks/screener/screener_view.py delete mode 100644 openbb_terminal/stocks/stock_statics.py delete mode 100644 openbb_terminal/stocks/stocks_controller.py delete mode 100644 openbb_terminal/stocks/stocks_helper.py delete mode 100644 openbb_terminal/stocks/stocks_model.py delete mode 100644 openbb_terminal/stocks/stocks_view.py delete mode 100644 openbb_terminal/stocks/technical_analysis/__init__.py delete mode 100644 openbb_terminal/stocks/technical_analysis/finbrain_model.py delete mode 100644 openbb_terminal/stocks/technical_analysis/finbrain_view.py delete mode 100644 openbb_terminal/stocks/technical_analysis/ta_controller.py delete mode 100644 openbb_terminal/stocks/technical_analysis/tradingview_model.py delete mode 100644 openbb_terminal/stocks/technical_analysis/tradingview_view.py delete mode 100644 openbb_terminal/stocks/tradinghours/README.md delete mode 100644 openbb_terminal/stocks/tradinghours/__init__.py delete mode 100644 openbb_terminal/stocks/tradinghours/bursa_model.py delete mode 100644 openbb_terminal/stocks/tradinghours/bursa_view.py delete mode 100644 openbb_terminal/stocks/tradinghours/data/__init__.py delete mode 100644 openbb_terminal/stocks/tradinghours/data/bursa_open_hours.json delete mode 100644 openbb_terminal/stocks/tradinghours/pandas_market_cal_model.py delete mode 100644 openbb_terminal/stocks/tradinghours/pandas_market_cal_view.py delete mode 100644 openbb_terminal/stocks/tradinghours/tradinghours_controller.py delete mode 100644 openbb_terminal/stocks/tradinghours/tradinghours_helper.py delete mode 100644 openbb_terminal/terminal_helper.py rename openbb_terminal/{alternative => tests}/__init__.py (100%) delete mode 100644 openbb_terminal/thought_of_the_day.py delete mode 100644 poetry.lock delete mode 100644 pyproject.toml delete mode 100644 requirements-full.txt delete mode 100644 requirements.txt delete mode 100644 terminal.py delete mode 100644 tests/README.md delete mode 100644 tests/__init__.py delete mode 100644 tests/conftest.py delete mode 100644 tests/helpers/__init__.py delete mode 100644 tests/helpers/helpers.py delete mode 100644 tests/helpers/tools.py delete mode 100644 tests/test_helpers.py delete mode 100644 tests/website/__init__.py delete mode 100644 tests/website/conftest.py delete mode 100644 tests/website/test_generate_docs.py delete mode 100644 website/controller_doc_classes.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index d129cb15a14..00000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,79 +0,0 @@ -# SETUP PYTHON IMAGE -FROM --platform=linux/amd64 mcr.microsoft.com/vscode/devcontainers/python:0-3.10-bullseye AS python - -LABEL org.opencontainers.image.source https://github.com/OpenBB-finance/OpenBBTerminal - -# SETUP DEBIAN IMAGE -FROM python as debian - -RUN apt-get update - -RUN apt-get -y install --no-install-recommends \ - gcc \ - g++ \ - make - -RUN apt-get -y install --no-install-recommends \ - git - -RUN apt-get -y install --no-install-recommends \ - curl \ - wget - -RUN apt-get -y install --no-install-recommends \ - libsm6 \ - libxt6 \ - libgl1-mesa-glx \ - libpng16-16 \ - libwebkit2gtk-4.0-dev \ - build-essential \ - libssl-dev \ - librsvg2-dev \ - ffmpeg \ - python3-tk - -RUN apt-get clean - -USER vscode -WORKDIR /home/vscode - -# SETUP POETRY IMAGE -FROM debian as poetry - -ENV PATH="/home/vscode/.local/bin:${PATH}" - -# Define the version of Poetry to install (default is 1.4.2) -# Define the directory of python virtual environment -ARG PYTHON_VIRTUALENV_HOME=/home/vscode/openbb-py-env \ - POETRY_VERSION=1.4.0 - -ENV POETRY_VIRTUALENVS_IN_PROJECT=false \ - POETRY_NO_INTERACTION=true - -# Create a Python virtual environment for Poetry and install it -RUN python3 -m venv ${PYTHON_VIRTUALENV_HOME} && \ - $PYTHON_VIRTUALENV_HOME/bin/pip install --upgrade pip wheel && \ - $PYTHON_VIRTUALENV_HOME/bin/pip install poetry==${POETRY_VERSION} - -ENV PATH="$PYTHON_VIRTUALENV_HOME/bin:$PATH" \ - VIRTUAL_ENV=$PYTHON_VIRTUALENV_HOME - -# Setup for bash -RUN poetry completions bash >> /home/vscode/.bash_completion && \ - echo "export PATH=$PYTHON_VIRTUALENV_HOME/bin:$PATH" >> ~/.bashrc - -# SETUP OPENBB IMAGE -FROM poetry as openbb-dev - -COPY --chown=vscode:vscode pyproject.toml poetry.lock ./ - -RUN mkdir OpenBBUserData -RUN chown vscode:vscode OpenBBUserData - -RUN mkdir .openbb_terminal -RUN chown vscode:vscode .openbb_terminal - -RUN poetry install --no-root --extras optimization --extras forecast - -# Set the working directory for the app -WORKDIR /workspaces/openbb \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 455ca0c18ec..00000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,33 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile -{ - "dockerComposeFile": "./docker-compose.yaml", - "service": "openbb", - "workspaceFolder": "/workspaces/openbb", - "name": "openbb", - "customizations": { - "vscode": { - "extensions": [ - "ms-python.python" - ], - "settings": { - "python.defaultInterpreterPath": "/home/vscode/openbb-py-env/bin/python" - } - } - - }, - - // Features to add to the dev container. More info: https://containers.dev/features. - "features": {}, - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [], - - // Uncomment the next line to run commands after the container is created. - // "postCreateCommand": "cat /etc/os-release", - - // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "devcontainer" - "remoteUser": "vscode", - "overrideCommand": true -} diff --git a/.devcontainer/docker-compose.yaml b/.devcontainer/docker-compose.yaml deleted file mode 100644 index fffffdfb91a..00000000000 --- a/.devcontainer/docker-compose.yaml +++ /dev/null @@ -1,11 +0,0 @@ -version: '3.8' -services: - openbb: - volumes: - - ~/OpenBBUserData:/home/vscode/OpenBBUserData - - ~/.openbb_terminal:/home/vscode/.openbb_terminal - - ../:/workspaces/openbb - platform: linux/amd64 - build: - dockerfile: .devcontainer/Dockerfile - context: ../ diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 6f700b65fbf..00000000000 --- a/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -.git -.* -.dockerignore -.gitignore -docker-compose.yaml -Dockerfile* -.vscode -.idea -__pycache__ -.python-version -logs/ diff --git a/.github/PULL_REQUEST_TEMPLATE/terminal_pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/terminal_pull_request_template.md index 351d7049307..17eb5932dcf 100644 --- a/.github/PULL_REQUEST_TEMPLATE/terminal_pull_request_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/terminal_pull_request_template.md @@ -1,4 +1,4 @@ -# Pull Request the OpenBBTerminal +# Pull Request the OpenBB Platform CLI ## Description @@ -14,8 +14,8 @@ - Please provide instructions so we can reproduce. - Please also list any relevant details for your test configuration. -- [ ] Ensure the affected commands still execute in the OpenBB Terminal. -- [ ] Ensure the Platform (previously named SDK) is working as intended. +- [ ] Ensure the affected commands still execute in the OpenBB Platform CLI. +- [ ] Ensure the Platform is working as intended. - [ ] Check any related reports. ## Checklist diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index fb04004546a..807c4a9212a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,5 +3,5 @@ Please go the the `Preview` tab and select the appropriate PR sub-template: * [OpenBB Platform](?expand=1&template=platform_pull_request_template.md) -* [OpenBB Terminal](?expand=1&template=terminal_pull_request_template.md) +* [OpenBB Platform CLI](?expand=1&template=terminal_pull_request_template.md) * [OpenBB Developers](?expand=1&template=obb_developer_pull_request_template.md) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index f9199285bbf..87caf321275 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -115,7 +115,7 @@ The first job, `code-linting`, runs on an Ubuntu machine and performs several li The second job, `markdown-link-check`, runs on an Ubuntu machine and performs linting of the markdown files in the repository. It uses a Docker container `avtodev/markdown-lint` to perform the linting. ## MacOS Build Workflow -This GitHub Actions workflow is used to build a version of the OpenBB Terminal for M1 MacOS. The build process includes installing necessary dependencies, building the terminal application using PyInstaller, creating a DMG file for distribution, and running integration tests on the built application. +This GitHub Actions workflow is used to build a version of the OpenBB Platform CLI for M1 MacOS. The build process includes installing necessary dependencies, building the terminal application using PyInstaller, creating a DMG file for distribution, and running integration tests on the built application. Jobs ---- @@ -161,13 +161,13 @@ The job includes the following steps: This workflow also uses a concurrency setting that groups the jobs by the workflow and ref, and cancels any in-progress jobs. ## Nightly PyPI Publish Workflow -This workflow is used to publish the latest version of the OpenBB Terminal to PyPI. The workflow is triggered at UTC+0 daily by the GitHub Action schedule event. +This workflow is used to publish the latest version of the OpenBB Platform CLI to PyPI. The workflow is triggered at UTC+0 daily by the GitHub Action schedule event. It does this by first updating the `pyproject.toml` file with a pre-determined version string of the form `.dev`, where `` represents the current day's date as a 8 digit number. Then, the code installs `pypa/build` and uses `python -m build` to create a binary wheel and a source tarball in the `dist/` directory. -Finally, it uses the PyPA specific action `gh-action-pypi-publish` to publish the created files to PyPI. +Finally, it uses the PyPA specific action `gh-action-pypi-publish` to publish the created files to PyPI. ## PYPI publish Workflow The Github Action code `Deploy to PyPI` is used to deploy a Python project to PyPI (Python Package Index) and TestPyPI, which is a separate package index for testing purposes. The code is triggered on two events: @@ -187,7 +187,7 @@ Similarly, the `deploy-pypi` job is triggered only if the pushed branch starts w Note: The code uses the `pypa/build` package for building the binary wheel and source tarball, and the `pypa/gh-action-pypi-publish@release/v1` Github Action for publishing the distributions to PyPI and TestPyPI. ## Unit Tests Workflow -This workflow is used to run unit tests on the OpenBB Terminal. The workflow is triggered on the following events: +This workflow is used to run unit tests on the OpenBB Platform CLI. The workflow is triggered on the following events: The events this workflow will respond to are: 1. Pull requests that are opened, synchronized, edited, or closed. The pull request must be made to the `develop` or `main` branches. @@ -207,7 +207,7 @@ The next job, `full-test`, uses the GitHub Actions `checkout` action to checkout The last job, `tests-conda`, sets up a Miniconda environment using the `setup-miniconda` action. The environment is specified using a YAML file and is activated. Then, the tests are run. ## Windows 10 Build Workflow -This is a GitHub Actions workflow file that automates the build and testing process for the OpenBB Terminal on Windows 10. The workflow consists of two jobs: +This is a GitHub Actions workflow file that automates the build and testing process for the OpenBB Platform CLI on Windows 10. The workflow consists of two jobs: 1. Windows-Build 2. Build-Exe diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 2f068019a35..2abf2e5455f 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -95,5 +95,3 @@ jobs: - name: json-yaml-validate id: json-yaml-validate uses: GrantBirki/json-yaml-validate@v2.0.0 - with: - exclude_file: json_validate_exclude.txt diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index 3dacb80bae9..00000000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# .readthedocs.yaml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details ---- -# Required -version: 2 - -# Build documentation in the docs/ directory with Sphinx -sphinx: - configuration: docs/conf.py - -# Optionally build your docs in additional formats such as PDF -# formats: -# - pdf - -# Optionally set the version of Python and requirements to build your docs -python: - version: 3.7 - install: - - requirements: requirements-full.txt diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index a9fc50aa947..00000000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include openbb_terminal/.env \ No newline at end of file diff --git a/build/conda/cleanup_artifacts.bat b/build/conda/cleanup_artifacts.bat deleted file mode 100644 index 4335f1f8f12..00000000000 --- a/build/conda/cleanup_artifacts.bat +++ /dev/null @@ -1,50 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -:: This script is used to remove artifacts left from the conda build process - -:: Ensure that conda is installed -where conda >nul 2>nul -if errorlevel 1 ( - echo conda could not be found - exit /b -) - - -:: Ensure that the obb conda environment exists -for /f %%A in ('conda env list ^| findstr /c:"obb"') do set env_exists=1 -if not defined env_exists ( - echo The obb conda environment does not exist - echo Create it by running: - echo conda env create -n obb --file build/conda/conda-3-10-env.yaml - exit /b -) - - -:: Ensure that the obb conda environment is activated -set "path=!CONDA_PREFIX!" -set "conda_prefix=%CONDA_DEFAULT_ENV%" -if not defined conda_prefix ( - echo The obb conda environment is not activated - echo Activate it by running 'conda activate obb' - exit /b -) - -if not !conda_prefix! == %CONDA_DEFAULT_ENV% ( - echo The obb conda environment is not activated - echo Activate it by running 'conda activate obb' - exit /b -) - -:: Remove build artifacts -set site_packages_dir=!path!\Lib\site-packages -cd !site_packages_dir! -echo !site_packages_dir! -@REM For /r %%~1f IN (direct_url.json) do Echo "%~1f" - -set "cwd=!site_packages_dir!" -for /f "delims=" %%a in ('dir /b /s /a-d direct_url.json') do (del /f /q %%a) - - -:: Say goodbye -echo Done diff --git a/build/conda/cleanup_artifacts.sh b/build/conda/cleanup_artifacts.sh deleted file mode 100755 index 3811e282a2b..00000000000 --- a/build/conda/cleanup_artifacts.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -e - -# This script is used to remove artifacts left from the conda build process - -# Ensure that conda is installed -if ! command -v conda &> /dev/null -then - echo "conda could not be found" - exit -fi - -# Ensure that the obb conda environment exists -if ! conda env list | grep -q "obb" -then - echo "The obb conda environment does not exist" - echo "Create it by running:" - echo "conda env create -n obb --file build/conda/conda-3-10-env.yaml" - exit -fi - -# Ensure that the obb conda environment is activated -if [[ -z "$CONDA_PREFIX" ]] || [[ "$CONDA_PREFIX" != *"obb" ]] -then - echo "The obb conda environment is not activated" - echo "Activate it by running 'conda activate obb'" - exit -fi - -# Remove build artifacts -find "$(dirname "$(dirname "$(which python)")")"/lib/python*/site-packages \ - -maxdepth 2 -name direct_url.json \ - -exec rm -f {} + - -# Say goodbye -echo "Done" diff --git a/build/conda/conda-3-10-env.yaml b/build/conda/conda-3-10-env.yaml deleted file mode 100644 index a1f04a21f60..00000000000 --- a/build/conda/conda-3-10-env.yaml +++ /dev/null @@ -1,30 +0,0 @@ -channels: - - conda-forge - - defaults -dependencies: - - python =3.10.8 - - git - # Build dependencies - - pip - - pybind11 - - cmake - # System dependencies - - openssl =1.1.1 - - libtiff - # Main dependencies - - numpy ==1.23.4 - # Forecasting dependencies - - ffmpeg - - lightgbm ==3.3.5 - # Optimization dependencies - - cvxpy ==1.2.2 - - pip: - # Build dependencies - - poetry ==1.4.0 - # Main dependencies - - charset-normalizer ==3.1.0 - # Forecasting dependencies - - catboost ==1.1.1 - - xgboost ==1.7.4 - # Optimization dependencies - - qdldl ==0.1.5.post3 diff --git a/build/conda/conda-3-8-env.yaml b/build/conda/conda-3-8-env.yaml deleted file mode 100644 index 63aaadeb295..00000000000 --- a/build/conda/conda-3-8-env.yaml +++ /dev/null @@ -1,30 +0,0 @@ -channels: - - conda-forge - - defaults -dependencies: - - python =3.8.16 - - git - # Build dependencies - - pip - - pybind11 - - cmake - # System dependencies - - openssl =1.1.1 - - libtiff - # Main dependencies - - numpy ==1.23.4 - # Forecasting dependencies - - ffmpeg - - lightgbm ==3.3.5 - # Optimization dependencies - - cvxpy ==1.2.2 - - pip: - # Build dependencies - - poetry ==1.4.0 - # Main dependencies - - charset-normalizer ==3.1.0 - # Forecasting dependencies - - catboost ==1.1.1 - - xgboost ==1.7.4 - # Optimization dependencies - - qdldl ==0.1.5.post3 diff --git a/build/conda/conda-3-9-env.yaml b/build/conda/conda-3-9-env.yaml deleted file mode 100644 index 53b803946d6..00000000000 --- a/build/conda/conda-3-9-env.yaml +++ /dev/null @@ -1,30 +0,0 @@ -channels: - - conda-forge - - defaults -dependencies: - - python =3.9.13 - - git - # Build dependencies - - pip - - pybind11 - - cmake - # System dependencies - - openssl =1.1.1 - - libtiff - # Main dependencies - - numpy ==1.23.4 - # Forecasting dependencies - - ffmpeg - - lightgbm ==3.3.5 - # Optimization dependencies - - cvxpy ==1.2.2 - - pip: - # Build dependencies - - poetry ==1.4.0 - # Main dependencies - - charset-normalizer ==3.1.0 - # Forecasting dependencies - - catboost ==1.1.1 - - xgboost ==1.7.4 - # Optimization dependencies - - qdldl ==0.1.5.post3 diff --git a/build/docker/api.dockerfile b/build/docker/api.dockerfile deleted file mode 100644 index c384555f9f8..00000000000 --- a/build/docker/api.dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -# ---- Base Python ---- -FROM python:3.11-slim-bullseye AS base - -# set work directory -WORKDIR /openbb - -# set environment variables -ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED 1 - -# install dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends build-essential openssh-client curl \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# install toml and poetry -RUN pip install toml poetry - -# ---- Copy Files/Build ---- -FROM base AS builder - -WORKDIR /openbb - -# install Rust -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ - && apt-get update \ - && apt-get install -y --no-install-recommends libwebkit2gtk-4.0-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# add Rust to PATH -ENV PATH="/root/.cargo/bin:${PATH}" - -COPY ./openbb_platform ./openbb_platform - -# Install the SDK -RUN pip install /openbb/openbb_platform[all] -RUN pip install openbb-devtools - -# ---- Copy Files ---- -FROM base - -COPY --from=builder /usr/local /usr/local - -# Specify the command to run -CMD ["uvicorn", "openbb_core.api.rest_api:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] diff --git a/frontend-components/plotly/src/utils/utils.ts b/frontend-components/plotly/src/utils/utils.ts index e292c09980e..4b35559bf9d 100644 --- a/frontend-components/plotly/src/utils/utils.ts +++ b/frontend-components/plotly/src/utils/utils.ts @@ -251,7 +251,7 @@ const openbb_watermark = { xref: "paper", x: 1, y: 0, - text: "OpenBB Terminal", + text: "OpenBB", font_size: 17, font_color: "gray", opacity: 0.5, diff --git a/json_validate_exclude.txt b/json_validate_exclude.txt deleted file mode 100644 index da93f0e08ad..00000000000 --- a/json_validate_exclude.txt +++ /dev/null @@ -1,3 +0,0 @@ -tests/ -openbb_terminal/miscellaneous/gpt_index/index_0.4.json -.devcontainer/devcontainer.json \ No newline at end of file diff --git a/openbb_platform/providers/yfinance/openbb_yfinance/__init__.py b/openbb_platform/providers/yfinance/openbb_yfinance/__init__.py index ad777281871..2e1fc8ce7e8 100644 --- a/openbb_platform/providers/yfinance/openbb_yfinance/__init__.py +++ b/openbb_platform/providers/yfinance/openbb_yfinance/__init__.py @@ -27,9 +27,6 @@ from openbb_yfinance.models.index_historical import ( from openbb_yfinance.models.key_executives import YFinanceKeyExecutivesFetcher from openbb_yfinance.models.key_metrics import YFinanceKeyMetricsFetcher from openbb_yfinance.models.losers import YFLosersFetcher -from openbb_yfinance.models.market_indices import ( - YFinanceMarketIndicesFetcher, -) from openbb_yfinance.models.price_target_consensus import ( YFinancePriceTargetConsensusFetcher, ) diff --git a/CONTRIBUTING.md b/openbb_terminal/CONTRIBUTING.md similarity index 99% rename from CONTRIBUTING.md rename to openbb_terminal/CONTRIBUTING.md index 62f9111ca92..49314e4a8e4 100644 --- a/CONTRIBUTING.md +++ b/openbb_terminal/CONTRIBUTING.md @@ -109,13 +109,13 @@ import logging import pandas as pd -from openbb_terminal.core.session.current_user import get_current_user +from src.current_user import get_current_user from openbb_terminal.decorators import check_api_key, log_start_end from openbb_terminal.helpers import request logger = logging.getLogger(__name__) -@log_start_end(log=logger) + @check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) def get_price_targets(cls, symbol: str) -> pd.DataFrame: """Get price targets for a company [Source: Financial Modeling Prep] @@ -178,7 +178,7 @@ In the example below, you can see that we explicitly handle some of them. It's not always possible to distinguish error types using `status_code`. So depending on the API provider, you can use either error messages or exceptions. ```python -@log_start_end(log=logger) + @check_api_key(["API_NEWS_TOKEN"]) def get_news( query: str, @@ -240,7 +240,7 @@ should be included in the file `OpenBBTerminal/website/content/terminal/usage/da 7. Finally, call `export_data` where the variables are export variable, current filename, command name, and dataframe. ```python -@log_start_end(log=logger) + @check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) def display_price_targets( symbol: str, limit: int = 10, export: str = "", sheet_name: Optional[str] = None @@ -350,7 +350,7 @@ Same with limit, we can pass limit=10 to add the `-l` flag with default=10. Her Our new function will be: ```python - @log_start_end(log=logger) + def call_pt(self, other_args: List[str]): """Process pt command""" parser = argparse.ArgumentParser( @@ -437,7 +437,7 @@ Now from the terminal, this function can be run as desired: When adding a new menu, the code looks like this: ```python -@log_start_end(log=logger) + def call_fa(self, _): """Process fa command""" from openbb_terminal.stocks.fundamental_analysis.fa_controller import ( @@ -1580,11 +1580,11 @@ You can also use the decorator `@log_start_end` to automatically record a messag ```python import logging -from openbb_terminal.decorators import log_start_end + logger = logging.getLogger(__name__) -@log_start_end(log=logger) + def your_function() -> pd.DataFrame: pass ``` @@ -1643,7 +1643,7 @@ Here is an example of **accessing** a setting: ```python -from openbb_terminal.core.session.current_system import get_current_system +from src.current_system import get_current_system system = get_current_system() system = get_current_system() @@ -1657,7 +1657,7 @@ And here is an example of **changing** a setting: ```python -from openbb_terminal.core.session.current_system import get_current_system +from src.current_system import get_current_system set_system_variable("TEST_MODE", True) diff --git a/openbb_terminal/README.md b/openbb_terminal/README.md new file mode 100644 index 00000000000..28bcbff336d --- /dev/null +++ b/openbb_terminal/README.md @@ -0,0 +1,3 @@ +# OpenBB CLI + +Work in progress. diff --git a/openbb_terminal/__init__.py b/openbb_terminal/__init__.py deleted file mode 100644 index 88783ac9207..00000000000 --- a/openbb_terminal/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# flake8: noqa -from . import ( - common, - cryptocurrency, - economy, - etf, - forex, - portfolio, - reports, - stocks, - fixedincome, -) -from .core.plots.plotly_helper import OpenBBFigure, plots_backend, theme, TerminalStyle diff --git a/openbb_terminal/account/account_controller.py b/openbb_terminal/account/account_controller.py deleted file mode 100644 index d16be3a05da..00000000000 --- a/openbb_terminal/account/account_controller.py +++ /dev/null @@ -1,601 +0,0 @@ -import argparse -import logging -from pathlib import Path -from typing import Dict, List, Optional -from uuid import UUID - -from openbb_terminal.account.account_view import ( - display_default_routines, - display_personal_routines, -) -from openbb_terminal.account.show_prompt import set_show_prompt -from openbb_terminal.core.session import hub_model as Hub -from openbb_terminal.core.session.constants import SCRIPT_TAGS -from openbb_terminal.core.session.current_user import ( - get_current_user, - is_local, -) -from openbb_terminal.core.session.routines_handler import ( - get_default_routines_info, - get_personal_routines_info, - read_routine, - save_routine, -) -from openbb_terminal.core.session.session_model import logout -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import check_positive -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.terminal_helper import is_installer, print_guest_block_msg - -logger = logging.getLogger(__name__) - - -class AccountController(BaseController): - """Account Controller Class""" - - CHOICES_COMMANDS = [ - "login", - "logout", - "clear", - "list", - "upload", - "download", - "delete", - "generate", - "show", - "revoke", - ] - - PATH = "/account/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - self.LOCAL_ROUTINES: Dict[str, Path] = {} - self.REMOTE_CHOICES: Dict[str, UUID] = {} - - self.DEFAULT_ROUTINES: List[Dict[str, str]] = self.fetch_default_routines() - self.DEFAULT_CHOICES: Dict[str, None] = { - r["name"]: None for r in self.DEFAULT_ROUTINES if "name" in r - } - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - self.choices: dict = self.choices_default - self.choices["upload"]["--tags"] = {c: None for c in SCRIPT_TAGS} - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def update_runtime_choices(self): - """Update runtime choices""" - self.LOCAL_ROUTINES = self.get_local_routines() - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - self.choices["upload"]["--file"].update( - {c: {} for c in self.LOCAL_ROUTINES} - ) - self.choices["download"]["--name"].update( - { - c: {} - for c in list(self.DEFAULT_CHOICES.keys()) - + list(self.REMOTE_CHOICES.keys()) - } - ) - self.choices["delete"]["--name"].update( - {c: {} for c in self.REMOTE_CHOICES} - ) - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def get_local_routines(self) -> Dict[str, Path]: - """Get local routines - - Returns - ------- - Dict[str, Path] - The local routines - """ - current_user = get_current_user() - return { - filepath.name: filepath - for filepath in current_user.preferences.USER_ROUTINES_DIRECTORY.rglob( - "*.openbb" - ) - } - - def fetch_default_routines(self) -> List[Dict[str, str]]: - """Fetch default routines - - Returns - ------- - List[Dict[str, str]] - The default routines - """ - try: - response = Hub.get_default_routines() - if response and response.status_code == 200: - d = response.json() - return d.get("data", []) - return [] - except Exception: - return [] - - def print_help(self): - """Print help""" - mt = MenuText("account/", 100) - mt.add_info("_info_") - mt.add_cmd("clear") - mt.add_raw("\n") - mt.add_info("_routines_") - mt.add_cmd("list") - mt.add_cmd("upload") - mt.add_cmd("download") - mt.add_cmd("delete") - mt.add_raw("\n") - mt.add_info("_personal_access_token_") - mt.add_cmd("generate") - mt.add_cmd("show") - mt.add_cmd("revoke") - mt.add_raw("\n") - mt.add_info("_authentication_") - mt.add_cmd("login") - mt.add_cmd("logout") - self.update_runtime_choices() - console.print(text=mt.menu_text, menu="Account") - - @log_start_end(log=logger) - def call_login(self, other_args: List[str]) -> None: - """Process login command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="login", - description="Login into new session.", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if not is_local() and "-h" not in other_args and "--help" not in other_args: - console.print("[info]You are already logged in.[/info]") - elif ns_parser: - set_show_prompt(True) - - @log_start_end(log=logger) - def call_logout(self, other_args: List[str]) -> None: - """Process logout command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="logout", - description="Logout from current session.", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - current_user = get_current_user() - logout( - auth_header=current_user.profile.get_auth_header(), - token=current_user.profile.get_token(), - cls=True, - ) - if is_installer(): - set_show_prompt(True) - else: - self.print_help() - - @log_start_end(log=logger) - def call_clear(self, other_args: List[str]): - """Clear data""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="clear", - description="Clear stored configurations from the cloud.", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - i = console.input( - "[bold red]This action is irreversible![/bold red]\n" - "Are you sure you want to permanently delete your keys from OpenBB hub? (y/n): " - ) - console.print("") - if i.lower() in ["y", "yes"]: - Hub.clear_user_configs( - config="features_keys", - auth_header=get_current_user().profile.get_auth_header(), - ) - else: - console.print("[info]Aborted.[/info]") - - @log_start_end(log=logger) - def call_list(self, other_args: List[str]): - """List routines""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="list", - description="List routines available in the cloud.", - ) - parser.add_argument( - "-t", - "--type", - type=str, - dest="type", - default="personal", - choices=["default", "personal"], - help="The type of routines to list.", - ) - parser.add_argument( - "-p", - "--page", - type=check_positive, - dest="page", - default=1, - help="The page number.", - ) - parser.add_argument( - "-s", - "--size", - type=check_positive, - dest="size", - default=10, - help="The number of results per page.", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - if ns_parser.type == "personal": - response = Hub.list_routines( - auth_header=get_current_user().profile.get_auth_header(), - page=ns_parser.page, - size=ns_parser.size, - base_url=Hub.BackendEnvironment.BASE_URL, - ) - df, page, pages = get_personal_routines_info(response) - if not df.empty: - temp_dict = dict(zip(df["name"], df["uuid"])) - self.REMOTE_CHOICES = {**self.REMOTE_CHOICES, **temp_dict} - self.update_runtime_choices() - display_personal_routines(df, page, pages) - else: - console.print("[red]No personal routines found.[/red]") - elif ns_parser.type == "default": - df = get_default_routines_info(self.DEFAULT_ROUTINES) - if not df.empty: - display_default_routines(df) - else: - console.print("[red]No default routines found.[/red]") - - console.print("") - - @log_start_end(log=logger) - def call_upload(self, other_args: List[str]): - """Upload""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="upload", - description="Upload a routine to the cloud.", - ) - parser.add_argument( - "-f", - "--file", - type=str, - dest="file", - required="-h" not in other_args - and "--help" not in other_args - and not is_local(), - help="The file to be loaded", - metavar="FILE", - nargs="+", - ) - parser.add_argument( - "-d", - "--description", - type=str, - dest="description", - help="The description of the routine", - default="", - nargs="+", - ) - parser.add_argument( - "-n", - "--name", - type=str, - dest="name", - help="The name of the routine.", - nargs="+", - ) - parser.add_argument( - "-t", - "--tags", - type=str, - dest="tags", - help="The tags of the routine", - default="", - nargs="+", - ) - parser.add_argument( - "-p", - "--public", - dest="public", - action="store_true", - help="Whether the routine should be public or not", - default=False, - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - routine = read_routine(file_name=" ".join(ns_parser.file)) - if routine is not None: - description = " ".join(ns_parser.description) - - name = ( - " ".join(ns_parser.name) - if ns_parser.name - else " ".join(ns_parser.file).split(sep=".openbb", maxsplit=-1)[0] - ) - - tags = " ".join(ns_parser.tags) if ns_parser.tags else "" - - current_user = get_current_user() - - kwargs = { - "auth_header": current_user.profile.get_auth_header(), - "name": name, - "description": description, - "routine": routine, - "tags": tags, - "public": ns_parser.public, - "base_url": Hub.BackendEnvironment.BASE_URL, - } - response = Hub.upload_routine(**kwargs) # type: ignore - - if response is not None and response.status_code == 409: - i = console.input( - "A routine with the same name already exists, " - "do you want to replace it? (y/n): " - ) - console.print("") - if i.lower() in ["y", "yes"]: - kwargs["override"] = True # type: ignore - response = Hub.upload_routine(**kwargs) # type: ignore - else: - console.print("[info]Aborted.[/info]") - - if response and response.status_code == 200: - the_uuid = response.json()["uuid"] - self.REMOTE_CHOICES[name] = the_uuid - self.update_runtime_choices() - - # store data in list with "personal/default" to identify data's routine type - # and for save_routine - @log_start_end(log=logger) - def call_download(self, other_args: List[str]): - """Download""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="download", - description="Download a routine from the cloud.", - ) - parser.add_argument( - "-n", - "--name", - type=str, - dest="name", - help="The name of the routine.", - required="-h" not in other_args - and "--help" not in other_args - and not is_local(), - nargs="+", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - data = [] - name = " ".join(ns_parser.name) - # Personal routines - if name in self.REMOTE_CHOICES: - response = Hub.download_routine( - auth_header=get_current_user().profile.get_auth_header(), - uuid=self.REMOTE_CHOICES[name], - base_url=Hub.BackendEnvironment.BASE_URL, - ) - if response and response.status_code == 200: - data = [response.json(), "personal"] - # Default routine - elif name in self.DEFAULT_CHOICES: - data = [ - next( - (r for r in self.DEFAULT_ROUTINES if r["name"] == name), - None, - ), - "default", - ] - - # Save routine - if data[0]: - name = data[0].get("name", "") - if name: - console.print(f"[info]Name:[/info] {name}") - - description = data[0].get("description", "") - if description: - console.print(f"[info]Description:[/info] {description}") - - script = [data[0].get("script", ""), data[1]] - if script: - file_name = f"{name}.openbb" - file_path = save_routine( - file_name=file_name, - routine=script, - ) - if file_path == "File already exists": - i = console.input( - "\nA file with the same name already exists, " - "do you want to replace it? (y/n): " - ) - console.print("") - if i.lower() in ["y", "yes"]: - file_path = save_routine( - file_name=file_name, - routine=script, - force=True, - ) - if file_path: - console.print(f"[info]Location:[/info] {file_path}") - else: - console.print("[info]Aborted.[/info]") - elif file_path: - console.print(f"[info]Location:[/info] {file_path}") - - @log_start_end(log=logger) - def call_delete(self, other_args: List[str]): - """Delete""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="delete", - description="Delete a routine on the cloud.", - ) - parser.add_argument( - "-n", - "--name", - type=str, - dest="name", - help="The name of the routine", - required="-h" not in other_args - and "--help" not in other_args - and not is_local(), - nargs="+", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - name = " ".join(ns_parser.name) - - i = console.input( - "[bold red]This action is irreversible![/bold red]\n" - "Are you sure you want to delete this routine? (y/n): " - ) - console.print("") - if i.lower() in ["y", "yes"]: - response = Hub.delete_routine( - auth_header=get_current_user().profile.get_auth_header(), - uuid=self.REMOTE_CHOICES[name], - base_url=Hub.BackendEnvironment.BASE_URL, - ) - if ( - response - and response.status_code == 200 - and name in self.REMOTE_CHOICES - ): - self.REMOTE_CHOICES.pop(name) - self.update_runtime_choices() - else: - console.print("[info]Aborted.[/info]") - - @log_start_end(log=logger) - def call_generate(self, other_args: List[str]) -> None: - """Process generate command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="generate", - description="Generate an OpenBB Personal Access Token.", - ) - parser.add_argument( - "-d", - "--days", - dest="days", - help="Number of days the token will be valid", - type=check_positive, - default=30, - ) - parser.add_argument( - "-s", - "--save", - dest="save", - default=False, - help="Save the token to the keys", - action="store_true", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - i = console.input( - "[bold yellow]This will revoke any token that was previously generated." - "\nThis action is irreversible.[/bold yellow]" - "\nAre you sure you want to generate a new token? (y/n): " - ) - if i.lower() not in ["y", "yes"]: - console.print("\n[info]Aborted.[/info]") - return - - response = Hub.generate_personal_access_token( - auth_header=get_current_user().profile.get_auth_header(), - base_url=Hub.BackendEnvironment.BASE_URL, - days=ns_parser.days, - ) - if response and response.status_code == 200: - token = response.json().get("token", "") - if token: - console.print(f"\n[info]Token:[/info] {token}\n") - - @log_start_end(log=logger) - def call_show(self, other_args: List[str]) -> None: - """Process show command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="show", - description="Show your current OpenBB Personal Access Token.", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - response = Hub.get_personal_access_token( - auth_header=get_current_user().profile.get_auth_header() - ) - if response and response.status_code == 200: - token = response.json().get("token", "") - if token: - console.print(f"[info]Token:[/info] {token}") - - @log_start_end(log=logger) - def call_revoke(self, other_args: List[str]) -> None: - """Process revoke command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="revoke", - description="Revoke your current OpenBB Personal Access Token.", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if is_local() and "-h" not in other_args and "--help" not in other_args: - print_guest_block_msg() - elif ns_parser: - i = console.input( - "[bold red]This action is irreversible![/bold red]\n" - "Are you sure you want to revoke your token? (y/n): " - ) - if i.lower() in ["y", "yes"]: - response = Hub.revoke_personal_access_token( - auth_header=get_current_user().profile.get_auth_header() - ) - if response and response.status_code in [200, 202]: - console.print("[info]Token revoked.[/info]") - else: - console.print("[info]Aborted.[/info]") diff --git a/openbb_terminal/account/account_view.py b/openbb_terminal/account/account_view.py deleted file mode 100644 index fb40d32fbef..00000000000 --- a/openbb_terminal/account/account_view.py +++ /dev/null @@ -1,83 +0,0 @@ -import pandas as pd - -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.rich_config import console - - -def clean_df(df: pd.DataFrame) -> pd.DataFrame: - """ - Cleans the dataframe before displaying it. - - Parameters - ---------- - df : pd.DataFrame - The dataframe to clean. - - Returns - ------- - pd.DataFrame - The cleaned dataframe. - """ - df["updated_date"] = pd.to_datetime(df["updated_date"]) - df["updated_date"] = df["updated_date"].dt.strftime("%Y-%m-%d %H:%M:%S") - df.replace(to_replace=[None], value="-", inplace=True) - to_rename = { - "name": "Name", - "description": "Description", - "version": "Version", - "updated_date": "Last Update", - } - df = df.rename(columns=to_rename) - df = df[["Name", "Description", "Version", "Last Update"]] - return df - - -def display_personal_routines(df: pd.DataFrame, page: int, pages: int): - """Display the routines. - - Parameters - ---------- - df : pd.DataFrame - The routines list. - page : int - The current page. - pages : int - The total number of pages. - """ - try: - title = f"Personal routines - page {page}" - if pages: - title += f" of {pages}" - df = clean_df(df) - - print_rich_table( - df=df, - title=title, - headers=list(df.columns), - show_index=True, - index_name="#", - ) - except Exception: - console.print("Failed to display personal routines.") - - -def display_default_routines(df: pd.DataFrame): - """Display the default routines. - - Parameters - ---------- - df : pd.DataFrame - The default routines list. - """ - try: - df = df.rename(columns={"date_updated": "updated_date"}) - df = clean_df(df) - print_rich_table( - df=df, - title="Default routines", - headers=["Name", "Description", "Version", "Last update"], - show_index=True, - index_name="#", - ) - except Exception: - console.print("Failed to display default routines.") diff --git a/openbb_terminal/account/show_prompt.py b/openbb_terminal/account/show_prompt.py deleted file mode 100644 index 77e555a67ff..00000000000 --- a/openbb_terminal/account/show_prompt.py +++ /dev/null @@ -1,24 +0,0 @@ -__show_prompt = False - - -def get_show_prompt(): - """Get the show_prompt flag. - - Returns - ------- - bool - The show_prompt flag. - """ - return __show_prompt - - -def set_show_prompt(value: bool): - """Set the show_prompt flag. - - Parameters - ---------- - value : bool - The show_prompt flag. - """ - global __show_prompt # pylint: disable=global-statement # noqa: PLW0603 - __show_prompt = value diff --git a/openbb_terminal/alternative/alt_controller.py b/openbb_terminal/alternative/alt_controller.py deleted file mode 100644 index 27fcc4a02fe..00000000000 --- a/openbb_terminal/alternative/alt_controller.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Alternative Data Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -from typing import List, Optional - -from openbb_terminal.alternative import hackernews_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import EXPORT_ONLY_RAW_DATA_ALLOWED -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) -# pylint:disable=import-outside-toplevel - - -class AlternativeDataController(BaseController): - """Alternative Controller class""" - - CHOICES_COMMANDS: List[str] = ["hn"] - CHOICES_MENUS = ["oss", "realestate", "companieshouse"] - PATH = "/alternative/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("alternative/") - mt.add_menu("oss") - mt.add_menu("realestate") - mt.add_menu("companieshouse") - mt.add_raw("\n") - mt.add_cmd("hn") - console.print(text=mt.menu_text, menu="Alternative") - - @log_start_end(log=logger) - def call_oss(self, _): - """Process oss command.""" - from openbb_terminal.alternative.oss.oss_controller import OSSController - - self.queue = self.load_class(OSSController, self.queue) - - @log_start_end(log=logger) - def call_hn(self, other_args: List[str]): - """Process hn command.""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hn", - description="Display Hacker News", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=10, - ) - - if ns_parser: - hackernews_view.display_stories( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_realestate(self, _): - """Process realestate command.""" - from openbb_terminal.alternative.realestate.realestate_controller import ( - RealEstateController, - ) - - self.queue = self.load_class(RealEstateController, self.queue) - - @log_start_end(log=logger) - def call_companieshouse(self, _): - """Process companieshouse command.""" - from openbb_terminal.alternative.companieshouse.companieshouse_controller import ( - CompaniesHouseController, - ) - - self.queue = self.load_class(CompaniesHouseController, self.queue) diff --git a/openbb_terminal/alternative/companieshouse/companieshouse_controller.py b/openbb_terminal/alternative/companieshouse/companieshouse_controller.py deleted file mode 100644 index fb19eaeab47..00000000000 --- a/openbb_terminal/alternative/companieshouse/companieshouse_controller.py +++ /dev/null @@ -1,334 +0,0 @@ -"""Companies House Controller.""" - -__docformat__ = "numpy" - -import argparse -import logging -from typing import List, Optional - -from openbb_terminal.alternative.companieshouse import companieshouse_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class CompaniesHouseController(BaseController): - """Companies House Controller class.""" - - CHOICES_COMMANDS = [ - "search", - "load", - "officers", - "signifcontrol", - "filings", - "filingdocument", - "charges", - ] - PATH = "/alternative/companieshouse/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Construct Data.""" - super().__init__(queue) - - self.companyNo = "" - self.companyName = "" - self.filingCategory = "" - self.filing_total_count = 0 - self.filing_end_index = 0 - self.filing_start_index = 0 - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - company_string = ( - f"{self.companyNo} ({self.companyName})" if self.companyNo else "" - ) - - mt = MenuText("alternative/companieshouse/") - mt.add_param("_company", company_string) - mt.add_raw("\n") - mt.add_cmd("search") - mt.add_cmd("load") - mt.add_cmd("officers") - mt.add_cmd("signifcontrol") - mt.add_cmd("filings") - mt.add_cmd("filingdocument") - mt.add_cmd("charges") - - console.print(text=mt.menu_text, menu="UK Companies House Data") - - @log_start_end(log=logger) - @check_api_key(["API_COMPANIESHOUSE_KEY"]) - def call_search(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="search", - description="Select the company name to search for. [Source: UK Companies House]", - ) - parser.add_argument( - "-n", - "--name", - help="name", - type=str.upper, - required="-h" not in other_args, - dest="name", - metavar="name", - nargs="+", - ) - - parser.add_argument( - "-l", - "--limit", - help="Number of entries to return", - type=check_positive, - required=False, - dest="limit", - metavar="limit", - default=20, - ) - - if ( - other_args - and "-n" not in other_args[0] - and "--name" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-n") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.name: - query = " ".join(ns_parser.name) - companieshouse_view.display_search( - query, ns_parser.limit, export=ns_parser.export - ) - else: - console.print("[red]No entries found for search string[/red]\n") - - @log_start_end(log=logger) - @check_api_key(["API_COMPANIESHOUSE_KEY"]) - def call_load(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Select the company number to get detailed info on. [Source: UK Companies House]", - ) - parser.add_argument( - "-c", - "--companyNo", - help="companyNo", - type=str.upper, - required="-h" not in other_args, - dest="companyNo", - metavar="companyNo", - ) - - if ( - other_args - and "-c" not in other_args[0] - and "--companyNo" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser and ns_parser.companyNo: - self.companyNo = ns_parser.companyNo - company = companieshouse_view.display_company_info(ns_parser.companyNo) - if company.dataAvailable(): - self.companyName = company.name - self.filing_total_count = 0 - self.filing_end_index = 0 - console.print(company.name) - console.print(company.address) - console.print(company.lastAccounts) - else: - console.print( - f"[red]No data found for company number {ns_parser.companyNo}[/red]\n" - ) - - @log_start_end(log=logger) - @check_api_key(["API_COMPANIESHOUSE_KEY"]) - def call_officers(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="officers", - description="Select the company number to retrieve officers for. [Source: UK Companies House]", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if self.companyNo: - if ns_parser: - companieshouse_view.display_officers( - self.companyNo, export=ns_parser.export - ) - else: - console.print("Must load a company prior to using this command") - - @log_start_end(log=logger) - @check_api_key(["API_COMPANIESHOUSE_KEY"]) - def call_signifcontrol(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="signifcontrol", - description="Select the company number to retrieve persons with significant control of company. \ - [Source: UK Companies House]", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if self.companyNo: - if ns_parser: - companieshouse_view.display_persons_with_significant_control( - self.companyNo, export=ns_parser.export - ) - else: - console.print("Must load a company prior to using this command") - - @log_start_end(log=logger) - @check_api_key(["API_COMPANIESHOUSE_KEY"]) - def call_filings(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="filings", - description="Select the company number to retrieve filling history for. [Source: UK Companies House]", - ) - - parser.add_argument( - "-k", - "--category", - help="category", - type=str.lower, - required=False, - dest="category", - metavar="category", - choices=[ - "accounts", - "address", - "capital", - "incorporation", - "officers", - "resolution", - ], - ) - - parser.add_argument( - "-l", - "--limit", - help="Number of entries to return", - type=check_positive, - required=False, - dest="limit", - metavar="limit", - default=100, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if self.companyNo: - if ns_parser: - category = ns_parser.category if ns_parser.category else "" - self.filingCategory = category - filing_data = companieshouse_view.display_filings( - self.companyNo, category, ns_parser.limit, export=ns_parser.export - ) - self.filing_total_count = filing_data.total_count - self.filing_end_index = filing_data.end_index - self.filing_start_index = filing_data.start_index - else: - console.print("Must load a company prior to using this command") - - @log_start_end(log=logger) - @check_api_key(["API_COMPANIESHOUSE_KEY"]) - def call_filingdocument(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="filingdocument", - description="Select the company number and transaction ID to retrieve filling history for. \ - [Source: UK Companies House]", - ) - - parser.add_argument( - "-t", - "--transactionID", - help="transactionID", - action="store", - required=("-h" not in other_args), - dest="transactionID", - metavar="transactionID", - ) - - if ( - other_args - and "-t" not in other_args[0] - and "--transactionID" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if self.companyNo: - if ns_parser: - companieshouse_view.download_filing_document( - self.companyNo, - self.companyName, - ns_parser.transactionID, - ) - else: - console.print("Must load a company prior to using this command") - - @log_start_end(log=logger) - @check_api_key(["API_COMPANIESHOUSE_KEY"]) - def call_charges(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="charges", - description="Select the company number to retrieve officers for. [Source: UK Companies House]", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if self.companyNo: - if ns_parser: - companieshouse_view.display_charges( - self.companyNo, export=ns_parser.export - ) - else: - console.print("Must load a company prior to using this command") diff --git a/openbb_terminal/alternative/companieshouse/companieshouse_model.py b/openbb_terminal/alternative/companieshouse/companieshouse_model.py deleted file mode 100644 index 3a1fe3c3006..00000000000 --- a/openbb_terminal/alternative/companieshouse/companieshouse_model.py +++ /dev/null @@ -1,480 +0,0 @@ -""" UK Companies House Model """ - -__docformat__ = "numpy" - -import logging - -import pandas as pd -import requests - -from openbb_terminal.alternative.companieshouse.company import Company -from openbb_terminal.alternative.companieshouse.company_doc import CompanyDocument -from openbb_terminal.alternative.companieshouse.filing_data import Filing_data -from openbb_terminal.core.session.constants import ( - TIMEOUT, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_COMPANIESHOUSE_KEY"]) -def get_search_results(searchStr: str, limit: int = 20) -> pd.DataFrame: - """All companies with searchStr in their name. - - Parameters - ---------- - searchStr : str - The search string - limit : int - number of rows to return - - Returns - ------- - pd.DataFrame - All comapanies with the search string in their name. - - Example - ------- - >>> from openbb_terminal.sdk import openbb - >>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca") - """ - - df = pd.DataFrame() - - if not searchStr: - return df - - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_COMPANIESHOUSE_KEY, "" - ) - r = requests.get( - "https://api.company-information.service.gov.uk/search/companies?q=" - + searchStr - + f"&items_per_page={limit}", - auth=auth, - timeout=TIMEOUT, - ) - returned_data = r.json() - company_data = [] - for _, item in enumerate(returned_data["items"]): - company_data.append( - { - "Name": item["title"], - "Company Number": item["company_number"], - "Status": item["company_status"], - } - ) - - df = pd.DataFrame(company_data) - return df - - -@log_start_end(log=logger) -@check_api_key(["API_COMPANIESHOUSE_KEY"]) -def get_company_info(company_number: str) -> Company: - """Gets company info by company number - - Parameters - ---------- - company_number : str - The company number. Use get_search_results() to lookup company numbers. - - Returns - ------- - self.address: str - Company address. - self.name: str - Company name. - self.dataAvailable(): bool - True if data is available. - self.lastAccounts: str - Period start and end. - - Example - ------- - >>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca") - >>> company_info = openbb.alt.companieshouse.get_company_info("02723534") - >>> name = company_info.name - """ - - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_COMPANIESHOUSE_KEY, "" - ) - r = requests.get( - f"https://api.company-information.service.gov.uk/company/{company_number}", - auth=auth, - timeout=TIMEOUT, - ) - - last_accounts = {} - returned_data = r.json() - if returned_data.get("company_name"): - company_name = returned_data["company_name"] - if returned_data.get("accounts"): - last_accounts = returned_data["accounts"]["last_accounts"] - address = returned_data["registered_office_address"] - address_lines = [] - if address.get("address_line_1"): - address_lines.append(address.get("address_line_1")) - if address.get("address_line_2"): - address_lines.append(address.get("address_line_2")) - if address.get("locality"): - address_lines.append(address.get("locality")) - if address.get("region"): - address_lines.append(address.get("region")) - if address.get("postal_code"): - address_lines.append(address.get("postal_code")) - pretty_address = ( - ",".join(address_lines) - if len(address_lines) > 0 - else "No address data found" - ) - - if last_accounts: - pretty_accounts = "Period Start On : " + ( - last_accounts.get("period_start_on") or "" - ) + " - " + "Type : " + ( - last_accounts.get("type") or "" - ) + " - " + "Made Up To : " + ( - last_accounts.get("made_up_to") or "" - ) + " - " "Period End On : " + ( - last_accounts.get("period_end_on") or "" - ) - else: - pretty_accounts = "No accounting period data found" - - data = Company(company_name, pretty_address, pretty_accounts) - return data - - return Company() - - -@log_start_end(log=logger) -@check_api_key(["API_COMPANIESHOUSE_KEY"]) -def get_officers(company_number: str) -> pd.DataFrame: - """Gets information on company officers - - Parameters - ---------- - company_number : str - The company number. Use get_search_results() to lookup company numbers. - - Returns - ------- - pd.Dataframe - All officers for given company number - - Example - ------- - >>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca") - >>> officer_info = openbb.alt.companieshouse.get_officers("02723534") - """ - - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_COMPANIESHOUSE_KEY, "" - ) - r = requests.get( - f"https://api.company-information.service.gov.uk/company/{company_number}/officers?items_per_page=100", - auth=auth, - timeout=TIMEOUT, - ) - returned_data = r.json() - - officers = [] - if returned_data.get("items"): - for _, item in enumerate(returned_data["items"]): - officers.append( - { - "Officer Role": (item.get("officer_role") or " - "), - "Appointed On": (item.get("appointed_on") or " - "), - "Resigned On": (item.get("resigned_on") or " - "), - "Name": (item.get("name") or " - "), - } - ) - - df = pd.DataFrame(officers) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_COMPANIESHOUSE_KEY"]) -def get_persons_with_significant_control(company_number: str) -> pd.DataFrame: - """Gets information on persons with significant control over the company - - Parameters - ---------- - company_number : str - The company number. Use get_search_results() to lookup company numbers. - - Returns - ------- - pd.Dataframe - All persons with significant control over given company number - - Example - ------- - >>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca") - >>> signif_control_info = openbb.alt.companieshouse.get_persons_with_significant_control("02723534") - """ - - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_COMPANIESHOUSE_KEY, "" - ) - r = requests.get( - f"https://api.company-information.service.gov.uk/company/{company_number}/persons-with-significant-control?items_per_page=100", - auth=auth, - timeout=TIMEOUT, - ) - returned_data = r.json() - - controllers = [] - if returned_data.get("items"): - for _, item in enumerate(returned_data["items"]): - controllers.append( - { - "Kind": (item.get("kind") or " - "), - "Name": (item.get("name") or " - "), - "Natures of Control": (item.get("natures_of_control") or " - "), - "Notified On": (item.get("notified_on") or " - "), - } - ) - - df = pd.DataFrame(controllers) - - return pd.DataFrame(df) - - -@log_start_end(log=logger) -@check_api_key(["API_COMPANIESHOUSE_KEY"]) -def get_filings(company_number: str, category: str = "", start_index=0) -> Filing_data: - """Gets information on filings for given company, e.g. accounts, etc - - Parameters - ---------- - company_number : str - The company number. Use get_search_results() to lookup company numbers. - - Returns - ------- - pd.Dataframe - All information on all filings for company - - Example - ------- - >>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca") - >>> signif_control_info = openbb.alt.companieshouse.get_filings("02723534") - """ - - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_COMPANIESHOUSE_KEY, "" - ) - url = ( - f"https://api.company-information.service.gov.uk/company/{company_number}/filing-history" - + f"?start_index={start_index}&items_per_page=100" - ) - if category: - url = ( - f"https://api.company-information.service.gov.uk/company/{company_number}/filing-history" - + f"?category={category}&start_index={start_index}&items_per_page=100" - ) - - r = requests.get( - url, - auth=auth, - timeout=TIMEOUT, - ) - returned_data = r.json() - - filings = [] - for _, item in enumerate(returned_data["items"]): - filings.append( - { - "Category": (item.get("category") or " - "), - "Date": (item.get("date") or " - "), - "Description": (item.get("description") or " - "), - "Type": (item.get("type") or " - "), - "Pages": (item.get("pages") or " - "), - "Transaction ID": (item.get("transaction_id") or " - "), - "Download": "https://find-and-update.company-information.service.gov.uk/company/" - f"{company_number}" - "/filing-history/" - f"{item.get('transaction_id')}" - "/document?format=pdf&download=0", - } - ) - start_index = int(returned_data.get("start_index")) - total_count = int(returned_data.get("total_count")) - end_index = start_index + 100 - end_index = min(end_index, total_count) - data = Filing_data(pd.DataFrame(filings), start_index, end_index, total_count) - - return data - - -@log_start_end(log=logger) -@check_api_key(["API_COMPANIESHOUSE_KEY"]) -def get_filing_document(company_number: str, transactionID: str) -> CompanyDocument: - """Download given filing document pdf - - Parameters - ---------- - company_number : str - The company number. Use get_search_results() to lookup company numbers. - transactionID : str - The filing transaction id. Use get_filings() to get id for each document - - Returns - ------- - self.category: str - document category, e.g.confirmation-statement-with-updates, accounts-with-accounts-type-dormant, etc. - self.date: str - date document filed. - self.description: str - description of document files. - self.paper_filed: str - True if documents filed in paper format. - self.pages: str - Number of pages in document. - self.transaction_id: str - Document transaction id. - self.content: bytes - contents of the document. - - Example - ------- - >>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca") - >>> company_doc_info = openbb.alt.companieshouse.get_filing_document("02723534","MzM1NzQ0NzI5NWFkaXF6a2N4") - """ - - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_COMPANIESHOUSE_KEY, "" - ) - r = requests.get( - f"https://api.company-information.service.gov.uk/company/{company_number}/filing-history/{transactionID}", - auth=auth, - timeout=TIMEOUT, - ) - - returned_data = r.json() - - category = date = description = paper_filed = pages = transaction_id = "" - content = b"" - if returned_data.get("links") and returned_data.get("links").get( - "document_metadata" - ): - url = returned_data.get("links").get("document_metadata") + "/content" - response = requests.get( - url, auth=auth, headers={"Accept": "application/pdf"}, timeout=TIMEOUT - ) - - if returned_data.get("category"): - category = returned_data["category"] - if returned_data.get("date"): - date = returned_data["date"] - if returned_data.get("description"): - description = returned_data["description"] - if returned_data.get("paper_filed"): - paper_filed = returned_data["paper_filed"] - if returned_data.get("pages"): - pages = returned_data["pages"] - if returned_data.get("transaction_id"): - transaction_id = returned_data["transaction_id"] - - content = bytes(response.content) - - return CompanyDocument( - category, date, description, paper_filed, pages, transaction_id, content - ) - return CompanyDocument( - category, date, description, paper_filed, pages, transaction_id, content - ) - - -@log_start_end(log=logger) -@check_api_key(["API_COMPANIESHOUSE_KEY"]) -def get_charges(company_number: str) -> pd.DataFrame: - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_COMPANIESHOUSE_KEY, "" - ) - r = requests.get( - f"https://api.company-information.service.gov.uk/company/{company_number}/charges", - auth=auth, - timeout=TIMEOUT, - ) - - returned_data = r.json() - - charges = pd.DataFrame() - for _, item in enumerate(returned_data["items"]): - url = item.get("links").get("self") - id_ = url[url.rfind("/") + 1 :] - charges = pd.concat( - [charges, get_charge(company_number, id_)], ignore_index=True - ) - - return charges - - -@log_start_end(log=logger) -@check_api_key(["API_COMPANIESHOUSE_KEY"]) -def get_charge(company_number: str, charge_id: str) -> pd.DataFrame: - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_COMPANIESHOUSE_KEY, "" - ) - r = requests.get( - f"https://api.company-information.service.gov.uk/company/{company_number}/charges/{charge_id}", - auth=auth, - timeout=TIMEOUT, - ) - - returned_data = r.json() - - charge = {} - if returned_data.get("acquired_on"): - charge.update({"acquired_on": returned_data.get("acquired_on")}) - if returned_data.get("assests_ceased_released"): - charge.update( - {"assests_ceased_released": returned_data.get("assests_ceased_released")} - ) - - if returned_data.get("charge_number"): - charge.update({"charge_number": returned_data.get("charge_number")}) - - if returned_data.get("covering_instrument_date"): - charge.update( - {"covering_instrument_date": returned_data.get("covering_instrument_date")} - ) - - if returned_data.get("created_on"): - charge.update({"created_on": returned_data.get("created_on")}) - - if returned_data.get("delivered_on"): - charge.update({"delivered_on": returned_data.get("delivered_on")}) - - if returned_data.get("id"): - charge.update({"id": returned_data.get("id")}) - - if returned_data.get("status"): - charge.update({"status": returned_data.get("status")}) - - if returned_data.get("particulars"): - part = returned_data.get("particulars") - - if part.get("description"): - charge.update({"description": part.get("description")}) - if part.get("floating_charge_covers_all"): - charge.update( - {"floating_charge_covers_all": part.get("floating_charge_covers_all")} - ) - if part.get("type"): - charge.update({"type": part.get("type")}) - - if returned_data.get("persons_entitled"): - for entitled in returned_data.get("persons_entitled"): - charge.update({"persons_entitled_name": entitled.get("name")}) - - return pd.DataFrame([charge]) diff --git a/openbb_terminal/alternative/companieshouse/companieshouse_view.py b/openbb_terminal/alternative/companieshouse/companieshouse_view.py deleted file mode 100644 index 96e031c7d19..00000000000 --- a/openbb_terminal/alternative/companieshouse/companieshouse_view.py +++ /dev/null @@ -1,254 +0,0 @@ -""" UK Companies House View """ - -__docformat__ = "numpy" - -import logging -import os - -import pandas as pd - -from openbb_terminal.alternative.companieshouse import companieshouse_model -from openbb_terminal.alternative.companieshouse.company import Company -from openbb_terminal.alternative.companieshouse.filing_data import Filing_data -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_search(search_str: str, limit: int, export: str = "") -> None: - """Display company search results. - - Parameters - ---------- - search_str : str - Company name to search for - - limit : int - number of rows to return - - """ - results = companieshouse_model.get_search_results(search_str, limit) - - if results.empty or len(results) == 0: - console.print( - "[red]" + "No data loaded.\n" + "Try different search string." + "[/red]\n" - ) - return - - console.print(f"Retrieved {len(results)} records") - print_rich_table( - results, - show_index=False, - title=f"[bold]{search_str}[/bold]", - export=bool(export), - columns_keep_types=["Company Number"], - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "results", - results, - ) - - -@log_start_end(log=logger) -def display_company_info(company_number: str) -> Company: - """Display company search results. - - Parameters - ---------- - company_number : str - company_number to retrieve info for - - - """ - return companieshouse_model.get_company_info(company_number) - - -@log_start_end(log=logger) -def display_officers(company_number: str, export: str = "") -> None: - """Display company officers results. - - Parameters - ---------- - company_number : str - company_number to retrieve officers for - - - """ - results = companieshouse_model.get_officers(company_number) - - if len(results) > 0: - console.print(f"Retrieved {len(results)} records") - print_rich_table( - results, - show_index=False, - title=f"[bold]{company_number}[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "results", - results, - ) - else: - console.print("[red]No Data Found[/red]") - - -@log_start_end(log=logger) -def display_persons_with_significant_control( - company_number: str, export: str = "" -) -> None: - """Display company officers results. - - Parameters - ---------- - company_number : str - company_number to retrieve officers for - - - """ - results = companieshouse_model.get_persons_with_significant_control(company_number) - - if len(results) > 0: - console.print(f"Retrieved {len(results)} records") - print_rich_table( - results, - show_index=False, - title=f"[bold]{company_number}[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "results", - results, - ) - else: - console.print("[red]No Data Found[/red]") - - -@log_start_end(log=logger) -def display_filings( - company_number: str, - category: str = "", - limit: int = 100, - export: str = "", -) -> Filing_data: - """Display company's filing history. - - Parameters - ---------- - company_number : str - company_number to retrieve filing history for - - """ - start_index = 0 - results = companieshouse_model.get_filings(company_number, category, start_index) - - start = int(results.start_index) - - data = results - total = int(results.total_count) if int(results.total_count) < limit else limit - - while start < total - 100: - results = companieshouse_model.get_filings( - company_number, start_index=start + 100 - ) - data.filings = pd.concat([data.filings, results.filings], ignore_index=True) - start = start + 100 - - data.filings = data.filings.head(limit) - console.print(f"Retrieved {len(data.filings)} filings") - - print_rich_table( - data.filings, - show_index=False, - title=f"[bold]{company_number}[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "results", - data.filings, - ) - - return results - - -def download_filing_document( - company_number: str, company_name: str, transactionID: str -) -> None: - """Download company's filing document. - - Parameters - ---------- - company_number : str - company_number to retrieve filing history for - - company_name : str - company_name to retrieve filing document for, this is used to name the downloaded file for easy access - - transactionID : str - transaction id for filing - - >>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca") - >>> openbb.alt.companieshouse.get_filing_document("02723534","AstraZeneca","MzM1NzQ0NzI5NWFkaXF6a2N4") - """ - - results = companieshouse_model.get_filing_document(company_number, transactionID) - - if results.dataAvailable(): - filename = ( - company_name.replace(" ", "_") - + "_" - + results.category - + "_" - + transactionID - + ".pdf" - ) - with open( - f"{get_current_user().preferences.USER_COMPANIES_HOUSE_DIRECTORY}/{filename}", - "wb", - ) as f: - f.write(results.content) - console.print( - f"File [green] {filename}[/green] downloaded to \ - {get_current_user().preferences.USER_COMPANIES_HOUSE_DIRECTORY}" - ) - else: - console.print("[red]" + "Document not found" + "[/red]\n") - - -@log_start_end(log=logger) -def display_charges(company_number: str, export: str = "") -> None: - results = companieshouse_model.get_charges(company_number) - - if len(results) > 0: - print_rich_table( - results, - show_index=False, - title=f"[bold]{company_number}[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "results", - results, - ) - else: - console.print("[red]" + "No Charges found" + "[/red]\n") - - return results diff --git a/openbb_terminal/alternative/companieshouse/company.py b/openbb_terminal/alternative/companieshouse/company.py deleted file mode 100644 index c4e0ae1f126..00000000000 --- a/openbb_terminal/alternative/companieshouse/company.py +++ /dev/null @@ -1,14 +0,0 @@ -class Company: - """Holder class for company information""" - - name = "" - address = "" - lastAccounts = "" - - def __init__(self, name="", address: str = "", lastAccounts: str = ""): - self.name = name - self.address = address - self.lastAccounts = lastAccounts - - def dataAvailable(self) -> bool: - return len(self.name) > 0 diff --git a/openbb_terminal/alternative/companieshouse/company_doc.py b/openbb_terminal/alternative/companieshouse/company_doc.py deleted file mode 100644 index 2b473989288..00000000000 --- a/openbb_terminal/alternative/companieshouse/company_doc.py +++ /dev/null @@ -1,31 +0,0 @@ -class CompanyDocument: - """Holder class for company document information""" - - category = "" - date = "" - description = "" - paper_filed = "" - pages = "" - transaction_id = "" - content = b"" - - def __init__( - self, - category="", - date: str = "", - description: str = "", - paper_filed: str = "", - pages: str = "", - transaction_id: str = "", - content=b"", - ): - self.category = category - self.date = date - self.description = description - self.paper_filed = paper_filed - self.pages = pages - self.transaction_id = transaction_id - self.content = content - - def dataAvailable(self) -> bool: - return len(self.content) > 0 diff --git a/openbb_terminal/alternative/companieshouse/filing_data.py b/openbb_terminal/alternative/companieshouse/filing_data.py deleted file mode 100644 index d6b7da7f893..00000000000 --- a/openbb_terminal/alternative/companieshouse/filing_data.py +++ /dev/null @@ -1,19 +0,0 @@ -import pandas as pd - - -class Filing_data: - """Holder class for company filings information""" - - filings = pd.DataFrame() - total_count = 0 - start_index = 0 - end_index = 0 - - def __init__(self, filings, start_index, end_index, total_count): - self.filings = filings - self.start_index = start_index - self.end_index = end_index - self.total_count = total_count - - def dataAvailable(self) -> bool: - return len(self.filings) > 0 diff --git a/openbb_terminal/alternative/hackernews_model.py b/openbb_terminal/alternative/hackernews_model.py deleted file mode 100644 index f318029ec63..00000000000 --- a/openbb_terminal/alternative/hackernews_model.py +++ /dev/null @@ -1,42 +0,0 @@ -"""HackerNews Model""" - -__docformat__ = "numpy" - - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_stories(limit: int = 10) -> pd.DataFrame: - """Get top stories from HackerNews. - Parameters - ---------- - limit: int - Number of stories to return - Returns - ------- - pd.DataFrame - DataFrame with stories - """ - - res = request("https://hacker-news.firebaseio.com/v0/topstories.json") - if res.status_code == 200: - top_stories = res.json() - stories = [] - for story_id in top_stories[:limit]: - story = request( - f"https://hacker-news.firebaseio.com/v0/item/{story_id}.json" - ).json() - stories.append(story) - df = pd.DataFrame(stories) - df["time"] = pd.to_datetime(df["time"], unit="s") - df = df[["title", "url", "score", "type", "time"]] - return df - return pd.DataFrame() diff --git a/openbb_terminal/alternative/hackernews_view.py b/openbb_terminal/alternative/hackernews_view.py deleted file mode 100644 index be55b7a6071..00000000000 --- a/openbb_terminal/alternative/hackernews_view.py +++ /dev/null @@ -1,40 +0,0 @@ -"""HackerNews view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.alternative.hackernews_model import get_stories -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_stories( - limit: int = 10, export: str = "", sheet_name: Optional[str] = None -) -> None: - """View top stories from HackerNews. - Parameters - ---------- - limit: int - Number of stories to return - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx file - """ - df = get_stories(limit) - if not df.empty: - df.columns = [col.capitalize() for col in df.columns] - print_rich_table(df, title="HackerNews Top Stories", export=bool(export)) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hn", - df, - sheet_name, - ) diff --git a/openbb_terminal/alternative/oss/github_model.py b/openbb_terminal/alternative/oss/github_model.py deleted file mode 100644 index d76650be93c..00000000000 --- a/openbb_terminal/alternative/oss/github_model.py +++ /dev/null @@ -1,232 +0,0 @@ -"""GitHub Model""" - -__docformat__ = "numpy" -# pylint: disable=C0201,W1401 - -import logging -import math -from datetime import datetime -from typing import Any, Dict, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@check_api_key(["API_GITHUB_KEY"]) -def get_github_data(url: str, **kwargs) -> Optional[Dict[str, Any]]: - """Get repository stats. - - Parameters - ---------- - url: str - github api endpoint - params: dict - params to pass to api endpoint - - Returns - ------- - Dict[str, Any] - Dictionary with data - """ - res = request( - url, - headers={ - "Authorization": f"token {get_current_user().credentials.API_GITHUB_KEY}", - "User-Agent": get_user_agent(), - "Accept": "application/vnd.github.v3.star+json", - }, - **kwargs, - ) - if res.status_code == 200: - return res.json() - if res.status_code in (401, 403): - console.print("[red]Rate limit reached, please provide a GitHub API key.[/red]") - elif res.status_code == 404: - console.print("[red]Repo not found.[/red]") - else: - console.print(f"[red]Error occurred {res.json()}[/red]") - return None - - -def search_repos( - sortby: str = "stars", page: int = 1, categories: str = "" -) -> pd.DataFrame: - """Get repos sorted by stars or forks. Can be filtered by categories. - - Parameters - ---------- - sortby : str - Sort repos by {stars, forks} - categories : str - Check for repo categories. If more than one separate with a comma: e.g., finance,investment. Default: None - page : int - Page number to get repos - - Returns - ------- - pd.DataFrame - Dataframe with repos - """ - params: Dict[str, Any] = {"page": page} - if categories: - params["sort"] = sortby - params["q"] = categories.replace(",", "+") - else: - params["q"] = f"{sortby}:>1" - data = get_github_data("https://api.github.com/search/repositories", params=params) - if data and "items" in data: - return pd.DataFrame(data["items"]) - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_stars_history(repo: str) -> pd.DataFrame: - """Get repository star history. - - Parameters - ---------- - repo : str - Repo to search for Format: org/repo, e.g., openbb-finance/openbbterminal - - Returns - ------- - pd.DataFrame - Dataframe with star history - Columns: Date, Stars - """ - data = get_github_data(f"https://api.github.com/repos/{repo}") - if data and "stargazers_count" in data: - stars_number = data["stargazers_count"] - stars: Dict[str, int] = {} - pages = math.ceil(stars_number / 100) - for page in range(0, pages): - data = get_github_data( - f"https://api.github.com/repos/{repo}/stargazers", - params={"per_page": 100, "page": page}, - ) - if data: - for star in data: - day = star["starred_at"].split("T")[0] - if day in stars: - stars[day] += 1 - else: - stars[day] = 1 - sorted_keys = sorted(stars.keys()) - for i in range(1, len(sorted_keys)): - stars[sorted_keys[i]] += stars[sorted_keys[i - 1]] - df = pd.DataFrame( - { - "Date": [datetime.strptime(date, "%Y-%m-%d").date() for date in stars], - "Stars": stars.values(), - } - ) - df.set_index("Date") - return df - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_top_repos(sortby: str, limit: int = 50, categories: str = "") -> pd.DataFrame: - """Get repos sorted by stars or forks. Can be filtered by categories. - - Parameters - ---------- - sortby : str - Sort repos by {stars, forks} - categories : str - Check for repo categories. If more than one separate with a comma: e.g., finance,investment. Default: None - limit : int - Number of repos to search for - - Returns - ------- - pd.DataFrame - Dataframe with repos - """ - initial_top = limit - df = pd.DataFrame( - columns=[ - "full_name", - "open_issues", - "stargazers_count", - "forks_count", - "language", - "created_at", - "updated_at", - "html_url", - ] - ) - if limit <= 100: - df2 = search_repos(sortby=sortby, page=1, categories=categories) - df = pd.concat([df, df2], ignore_index=True) - else: - p = 2 - while limit > 0: - df2 = search_repos(sortby=sortby, page=p, categories=categories) - df = pd.concat([df, df2], ignore_index=True) - limit -= 100 - p += 1 - return df.head(initial_top) - - -@log_start_end(log=logger) -def get_repo_summary(repo: str) -> pd.DataFrame: - """Get repository summary. - - Parameters - ---------- - repo : str - Repo to search for Format: org/repo, e.g., openbb-finance/openbbterminal - - Returns - ------- - pd.DataFrame - Dataframe with repo summary - Columns: Metric, Value - """ - data = get_github_data(f"https://api.github.com/repos/{repo}") - if not data: - return pd.DataFrame() - release_data = get_github_data(f"https://api.github.com/repos/{repo}/releases") - if not release_data: - return pd.DataFrame() - total_release_downloads: Any = "N/A" - if len(release_data) > 0: - total_release_downloads = 0 - for asset in release_data[0]["assets"]: - total_release_downloads += asset["download_count"] - obj: Dict[str, Any] = { - "Metric": [ - "Name", - "Owner", - "Creation Date", - "Last Update", - "Topics", - "Stars", - "Forks", - "Open Issues", - "Language", - "License", - "Releases", - "Last Release Downloads", - ], - "Value": [ - data["name"], - data["owner"]["login"], - data["created_at"].split("T")[0], - data["updated_at"].split("T")[0], - ", ".join(data["topics"]), - data["stargazers_count"], - data["forks"], - data["open_issues"], - data["language"], - data["license"]["name"], - len(release_data), - total_release_downloads, - ], - } - return pd.DataFrame(obj) diff --git a/openbb_terminal/alternative/oss/github_view.py b/openbb_terminal/alternative/oss/github_view.py deleted file mode 100644 index a46b6f04635..00000000000 --- a/openbb_terminal/alternative/oss/github_view.py +++ /dev/null @@ -1,141 +0,0 @@ -"""GitHub View Module""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.alternative.oss import github_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_star_history( - repo: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots repo summary [Source: https://api.github.com]. - - Parameters - ---------- - repo : str - Repository to display star history. Format: org/repo, e.g., openbb-finance/openbbterminal - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = github_model.get_stars_history(repo) - if not df.empty: - fig = OpenBBFigure(xaxis_title="Date", yaxis_title="Stars") - fig.set_title(f"Star History for {repo}") - - fig.add_scatter( - x=df["Date"], - y=df["Stars"], - mode="lines", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "sh", - df, - sheet_name, - fig, - ) - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -def display_top_repos( - sortby: str, - categories: str = "", - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots repo summary [Source: https://api.github.com]. - - Parameters - ---------- - sortby : str - Sort repos by {stars, forks} - categories : str - Check for repo categories. If more than one separate with a comma: e.g., finance,investment. Default: None - limit : int - Number of repos to look at - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = github_model.get_top_repos(categories=categories, sortby=sortby, limit=limit) - if not df.empty: - fig = OpenBBFigure( - xaxis_title=sortby.capitalize(), yaxis_title="Repository Full Name" - ) - - fig.add_bar( - x=df["stargazers_count" if sortby == "stars" else "forks_count"], - y=df["full_name"], - orientation="h", - ) - - category_substr = "ies" if "," in categories else "y" - category_str = f"categor{category_substr} {categories} " if categories else "" - fig.set_title(f"Top repos {category_str}sorted by {sortby}") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "tr", - df, - sheet_name, - fig, - ) - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -def display_repo_summary( - repo: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing repo summary [Source: https://api.github.com]. - - Parameters - ---------- - repo : str - Repository to display summary. Format: org/repo, e.g., openbb-finance/openbbterminal - export : str - Export dataframe data to csv,json,xlsx file - """ - data = github_model.get_repo_summary(repo) - if not data.empty: - print_rich_table( - data, - headers=list(data.columns), - show_index=False, - title="Repo summary", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rs", - data, - sheet_name, - ) diff --git a/openbb_terminal/alternative/oss/oss_controller.py b/openbb_terminal/alternative/oss/oss_controller.py deleted file mode 100644 index 9eb48c8a408..00000000000 --- a/openbb_terminal/alternative/oss/oss_controller.py +++ /dev/null @@ -1,254 +0,0 @@ -"""OS Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -from typing import List, Optional - -from openbb_terminal.alternative.oss import github_view, runa_model, runa_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - parse_and_split_input, - valid_repo, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class OSSController(BaseController): - """Open Source Controller class""" - - CHOICES_COMMANDS = ["sh", "tr", "rs", "rossidx"] - PATH = "/alternative/oss/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def parse_input(self, an_input: str) -> List: - """Parse controller input - - Overrides the parent class function to handle github org/repo path convention. - See `BaseController.parse_input()` for details. - """ - # Covering the github "org/repo" convention in rs and sh commands - custom_filters = [ - r"(sh .*?(\/[a-zA-Z0-9_\-\/]).*?((?=\/)|(?= )|(?=$)))", - r"(rs .*?(\/[a-zA-Z0-9_\-\/]).*?((?=\/)|(?= )|(?=$)))", - ] - commands = parse_and_split_input( - an_input=an_input, custom_filters=custom_filters - ) - return commands - - def print_help(self): - """Print help""" - mt = MenuText("alternative/oss/", 80) - mt.add_cmd("rossidx") - mt.add_cmd("rs") - mt.add_cmd("sh") - mt.add_cmd("tr") - console.print(text=mt.menu_text, menu="Alternative - Open Source") - - @log_start_end(log=logger) - def call_sh(self, other_args: List[str]): - """Process sh command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sh", - description="Display a repo star history [Source: https://api.github.com]", - ) - parser.add_argument( - "-r", - "--repo", - type=str, - required="-h" not in other_args, - dest="repo", - help="Repository to search for star history. Format: org/repo, e.g., openbb-finance/openbbterminal", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-r") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ) - if ns_parser and valid_repo(ns_parser.repo): - github_view.display_star_history( - repo=ns_parser.repo, export=ns_parser.export - ) - - @log_start_end(log=logger) - def call_rs(self, other_args: List[str]): - """Process rs command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rs", - description="Display a repo summary [Source: https://api.github.com]", - ) - parser.add_argument( - "-r", - "--repo", - type=str, - required="-h" not in other_args, - dest="repo", - help="Repository to search for repo summary. Format: org/repo, e.g., openbb-finance/openbbterminal", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-r") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, raw=True - ) - if ns_parser and valid_repo(ns_parser.repo): - github_view.display_repo_summary( - repo=ns_parser.repo, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rossidx(self, other_args: List[str]): - """Process rossidx command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rossidx", - description=""" - Display list of startups from ross index [Source: https://runacap.com/] - Use --chart to display chart and -t {stars,forks} to set chart type - """, - ) - parser.add_argument( - "-s", - "--sortby", - type=str, - dest="sortby", - nargs="+", - help="Sort startups by column", - default="Stars AGR [%]", - choices=runa_model.SORT_COLUMNS, - metavar="SORTBY", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-c", - "--chart", - action="store_true", - help="Flag to show chart", - dest="show_chart", - default=False, - ) - parser.add_argument( - "-g", - "--growth", - action="store_true", - help="Flag to show growth chart", - dest="show_growth", - default=False, - ) - parser.add_argument( - "-t", - "--chart-type", - type=str, - dest="chart_type", - help="Chart type: {stars, forks}", - default="stars", - choices=["stars", "forks"], - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=10, - ) - - if ns_parser: - runa_view.display_rossindex( - sortby=" ".join(ns_parser.sortby), - ascend=ns_parser.reverse, - limit=ns_parser.limit, - show_chart=ns_parser.show_chart, - show_growth=ns_parser.show_growth, - chart_type=ns_parser.chart_type, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_tr(self, other_args: List[str]): - """Process tr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tr", - description="Display top repositories [Source: https://api.github.com]", - ) - parser.add_argument( - "-s", - "--sortby", - type=str, - dest="sortby", - help="Sort repos by {stars, forks}. Default: stars", - default="stars", - choices=["stars", "forks"], - ) - - parser.add_argument( - "-c", - "--categories", - type=str, - dest="categories", - help="Filter by repo categories. If more than one separate with a comma: e.g., finance,investment", - default="", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10, - ) - if ns_parser: - github_view.display_top_repos( - sortby=ns_parser.sortby, - categories=ns_parser.categories, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/alternative/oss/runa_model.py b/openbb_terminal/alternative/oss/runa_model.py deleted file mode 100644 index b6a38959b16..00000000000 --- a/openbb_terminal/alternative/oss/runa_model.py +++ /dev/null @@ -1,161 +0,0 @@ -"""Runa Model""" - -import logging -from typing import Union - -import pandas as pd -import requests -from bs4 import BeautifulSoup -from requests.adapters import HTTPAdapter, RetryError -from urllib3.util.retry import Retry - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -SORT_COLUMNS = [ - "GitHub", - "Company", - "Country", - "City", - "Founded", - "Raised [$M]", - "Stars", - "Forks", - "Stars AGR [%]", - "Forks AGR [%]", -] - - -@log_start_end(log=logger) -def _retry_session( - url: str, retries: int = 3, backoff_factor: float = 1.0 -) -> requests.Session: - """Helper methods that retries to make request. - - - 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 - - -@log_start_end(log=logger) -def _make_request(url: str) -> Union[BeautifulSoup, None]: - """Helper method to scrap. - - Parameters - ---------- - url : str - url to scrape - - Returns - ------- - Union[BeautifulSoup, None] - BeautifulSoup object or None - """ - headers = {"User-Agent": get_user_agent()} - session = _retry_session("https://runacap.com/") - try: - req = session.get(url, headers=headers) - except Exception as error: - logger.exception(str(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 == 404: - return None - - 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") - - -@log_start_end(log=logger) -def get_startups() -> pd.DataFrame: - """Get startups from ROSS index [Source: https://runacap.com/]. - - Returns - ------- - pd.DataFrame - list of startups - """ - response = request("https://runacap.com/ross-index/annual-2022") - soup = BeautifulSoup(response.content, "html.parser") - startups = [] - if soup: - table = soup.find("table", {"id": "table_1"}) - if table: - rows = table.find_all("tr") - for row in rows: - td = row.find_all("td") - startup = [tr.text for tr in td] - if len(startup) > 0: - startups.append(startup) - df = pd.DataFrame( - startups, - columns=[ - "GitHub", - "Company", - "Country", - "City", - "Founded", - "Raised [$M]", - "REPO", - "Language", - "Stars", - "Forks", - "Stars AGR [%]", - "Forks AGR [%]", - "SG", - "FG", - ], - ) - df.drop("REPO", inplace=True, axis=1) - df["SG"] = pd.to_numeric(df["SG"], errors="coerce") - df["Stars"] = pd.to_numeric( - df["Stars"].str.replace(",", ""), errors="coerce" - ) - df["FG"] = pd.to_numeric(df["FG"], errors="coerce") - df["Forks"] = pd.to_numeric( - df["Forks"].str.replace(",", ""), errors="coerce" - ) - df["Raised [$M]"] = pd.to_numeric(df["Raised [$M]"], errors="coerce") - df["Stars AGR [%]"] = pd.to_numeric( - df["Stars AGR [%]"].str.replace(",", ""), errors="coerce" - ) - df["Forks AGR [%]"] = pd.to_numeric( - df["Forks AGR [%]"].str.replace(",", ""), errors="coerce" - ) - return df - return pd.DataFrame() diff --git a/openbb_terminal/alternative/oss/runa_view.py b/openbb_terminal/alternative/oss/runa_view.py deleted file mode 100644 index e2134e509e7..00000000000 --- a/openbb_terminal/alternative/oss/runa_view.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Rekt view""" - -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.alternative.oss import runa_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, -) -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_rossindex( - limit: int = 10, - sortby: str = "Stars AGR [%]", - ascend: bool = False, - show_chart: bool = False, - show_growth: bool = True, - chart_type: str = "stars", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots list of startups from ross index [Source: https://runacap.com/] - - Parameters - ---------- - limit: int - Number of startups to search - sortby: str - Key by which to sort data. Default: Stars AGR [%] - ascend: bool - Flag to sort data descending - show_chart: bool - Flag to show chart with startups - show_growth: bool - Flag to show growth line chart - chart_type: str - Chart type {stars,forks} - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = runa_model.get_startups() - - if df.empty: - console.print("\nError in runa request\n") - else: - if sortby in runa_model.SORT_COLUMNS: - df = df.sort_values(by=sortby, ascending=ascend) - df = df.head(limit) - if show_chart: - fig = OpenBBFigure.create_subplots( - 1, - 3, - specs=[[{"type": "domain"}, {"type": "bar", "colspan": 2}, None]], - column_widths=[0.1, 0.8, 0.1], - ) - fig.update_layout( - xaxis_title=chart_type.title(), yaxis=dict(title="Company", side="left") - ) - - fig.set_title(f"ROSS Index - Total {chart_type.title()}") - - fig.add_bar( - x=df[chart_type.title()], - y=df["GitHub"], - orientation="h", - name=chart_type.title(), - text=df[chart_type.title()], - textposition="auto", - row=1, - col=2, - ) - - if show_growth: - fig = OpenBBFigure.create_subplots( - 1, - 3, - specs=[[{"type": "domain"}, {"type": "bar", "colspan": 2}, None]], - column_widths=[0.1, 0.8, 0.1], - ) - fig.update_layout( - xaxis_title="Annual Growth [times]", - yaxis=dict(title="Company", side="left"), - ) - fig.set_title(f"ROSS Index - {chart_type.title()} Annual Growth") - fig.add_bar( - x=df["FG" if chart_type == "forks" else "SG"], - y=df["GitHub"], - orientation="h", - name="Annual Growth [times]", - text=df["FG" if chart_type == "forks" else "SG"], - textposition="auto", - row=1, - col=2, - ) - - show_df = df.drop(["SG", "FG"], axis=1) - show_df = show_df.fillna("") - show_df["GitHub"] = show_df["GitHub"].str.wrap(10) - print_rich_table( - show_df, - headers=list(show_df.columns), - floatfmt=".1f", - show_index=False, - title="ROSS Index - the fastest-growing open-source startups", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "runaidx", - df, - sheet_name, - ) - - if show_chart or show_growth: - return fig.show(external=external_axes) - - return None diff --git a/openbb_terminal/alternative/realestate/__init__.py b/openbb_terminal/alternative/realestate/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/alternative/realestate/landRegistry_model.py b/openbb_terminal/alternative/realestate/landRegistry_model.py deleted file mode 100644 index 2c24cd66cf8..00000000000 --- a/openbb_terminal/alternative/realestate/landRegistry_model.py +++ /dev/null @@ -1,267 +0,0 @@ -""" UK Land Registry Model """ - -__docformat__ = "numpy" - -import io -import logging - -import pandas as pd -from SPARQLWrapper.Wrapper import CSV, SPARQLWrapper - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -def pcd_format(postcode: str): - return ( - postcode[0 : len(postcode) - 3].strip() - + " " - + postcode[len(postcode) - 3 : len(postcode)] - ) - - -@log_start_end(log=logger) -def get_estate_sales(postcode: str, limit: int = 25) -> pd.DataFrame: - """All sales for specified postcode. - - Parameters - ---------- - postcode : str - Postcode - - limit : int - number of rows to return - - - Returns - ------- - pd.DataFrame - All sales with that postcode - """ - - if not postcode: - return pd.DataFrame() - - # the upper is done since SDK calls don't appear to go through the controller - pcd_postcode = pcd_format(postcode.upper()) - - query = f""" - PREFIX lrppi: - PREFIX rdf: - PREFIX owl: - PREFIX lrcommon: - PREFIX xsd: - PREFIX ukhpi: - PREFIX skos: - PREFIX rdfs: - PREFIX sr: - - SELECT ?paon ?saon ?street ?town ?county ?postcode ?amount ?date ?id ?category - WHERE - {{ VALUES ?postcode {{ "{pcd_postcode}" }} - ?addr lrcommon:postcode ?postcode . - ?transx lrppi:propertyAddress ?addr ; - lrppi:pricePaid ?amount ; - lrppi:transactionId ?id ; - lrppi:transactionDate ?date . - ?transx lrppi:transactionCategory/skos:prefLabel ?category. - - OPTIONAL {{ ?addr lrcommon:county ?county }} - OPTIONAL {{ ?addr lrcommon:paon ?paon }} - OPTIONAL {{ ?addr lrcommon:saon ?saon }} - OPTIONAL {{ ?addr lrcommon:street ?street }} - OPTIONAL {{ ?addr lrcommon:town ?town }} - }} - ORDER BY DESC(?date) - - LIMIT {limit} - """ - endpoint = "http://landregistry.data.gov.uk/landregistry/query" - - # df = sparql_dataframe.get(endpoint, query) - - df = pd.DataFrame() - sparql = SPARQLWrapper(endpoint) - sparql.setReturnFormat(CSV) - sparql.setQuery(query) - - results = sparql.query().convert() - if isinstance(results, bytes): - _csv = io.StringIO(results.decode("utf-8")) - # print( pd.read_csv(_csv, sep=",")) - df = pd.read_csv(_csv, sep=",") - - return df - - -@log_start_end(log=logger) -def get_towns_sold_prices( - town: str, - start_date: str, - end_date: str, - limit: int = 25, -) -> pd.DataFrame: - """Get towns sold house price data. - - Parameters - ---------- - town : str - town - - start_date : str - startDate - - end_date : str - endDate - - limit : int - number of rows to return - - - Returns - ------- - pd.DataFrame - All sales for that town within the date range specified - """ - - # the upper is done since SDK calls don't appear to go through the controller - town = town.upper() - - query = f""" - prefix rdf: - prefix rdfs: - prefix owl: - prefix xsd: - prefix sr: - prefix ukhpi: - prefix lrppi: - prefix skos: - prefix lrcommon: - - SELECT ?paon ?saon ?street ?county ?postcode ?amount ?date ?category - WHERE - {{ - VALUES ?town {{"{town}"^^xsd:string}} - - ?addr lrcommon:town ?town ; - lrcommon:postcode ?postcode. - - ?transx lrppi:propertyAddress ?addr ; - lrppi:pricePaid ?amount ; - lrppi:transactionDate ?date ; - lrppi:transactionCategory/skos:prefLabel ?category. - - FILTER ( - ?date > "{start_date}"^^xsd:date && - ?date < "{end_date}"^^xsd:date - ). - - OPTIONAL {{?addr lrcommon:county ?county}} - OPTIONAL {{?addr lrcommon:paon ?paon}} - OPTIONAL {{?addr lrcommon:saon ?saon}} - OPTIONAL {{?addr lrcommon:saon ?postcode}} - OPTIONAL {{?addr lrcommon:street ?street}} - }} - ORDER BY (?date && ?postcode) - - LIMIT {limit} - """ - - endpoint = "http://landregistry.data.gov.uk/landregistry/query" - - df = pd.DataFrame() - sparql = SPARQLWrapper(endpoint) - sparql.setReturnFormat(CSV) - sparql.setQuery(query) - sparql.method = "POST" - - results = sparql.query().convert() - if isinstance(results, bytes): - _csv = io.StringIO(results.decode("utf-8")) - df = pd.read_csv(_csv, sep=",") - - return df - - -@log_start_end(log=logger) -def get_region_stats( - region: str, start_date: str = "2010-01-01", end_date: str = "" -) -> pd.DataFrame: - """Get regional house price statistics. - - Parameters - ---------- - region : str - region - - startd_ate : str - startDate - - end_date : str - endDate - - - Returns - ------- - pd.DataFrame - All stats for that region within the date range specified - """ - - region = region.lower() - - query = f""" - prefix rdf: - prefix rdfs: - prefix owl: - prefix xsd: - prefix sr: - prefix ukhpi: - prefix lrppi: - prefix skos: - prefix lrcommon: - - SELECT ?month ?avgPrice ?avgPriceCash ?totalSalesVolume ?avgPriceExistingProperty - ?avgPriceFirstTimeBuyer - ?avgPriceDetached ?hpi ?detachedhpi ?newbuildhpi - {{ - values ?refRegion {{}} - ?region ukhpi:refPeriodStart ?month ; - ukhpi:housePriceIndex ?hpi ; - ukhpi:refRegion ?refRegion ; - ukhpi:averagePrice ?avgPrice ; - ukhpi:averagePriceCash ?avgPriceCash ; - ukhpi:salesVolume ?totalSalesVolume ; - ukhpi:averagePriceDetached ?avgPriceDetached ; - ukhpi:averagePriceExistingProperty ?avgPriceExistingProperty ; - ukhpi:averagePriceFirstTimeBuyer ?avgPriceFirstTimeBuyer ; - ukhpi:housePriceIndexDetached ?detachedhpi ; - ukhpi:housePriceIndexNewBuild ?newbuildhpi . - ?refRegion rdfs:label ?regionLabel . - FILTER(LANG(?regionLabel) = "en"). - FILTER ( - ?month > "{start_date}"^^xsd:date && - ?month < "{end_date}"^^xsd:date - ) - - OPTIONAL {{?region ukhpi:housePriceIndexDetached ?detachedhpi}} - OPTIONAL {{?region ukhpi:housePriceIndexNewBuild ?newbuildhpi }} - OPTIONAL {{?region ukhpi:averagePrice ?averagePrice }} - }} - ORDER BY (?month) - """ - - endpoint = "http://landregistry.data.gov.uk/landregistry/query" - - df = pd.DataFrame() - sparql = SPARQLWrapper(endpoint) - sparql.setReturnFormat(CSV) - sparql.setQuery(query) - sparql.method = "POST" - - results = sparql.query().convert() - if isinstance(results, bytes): - _csv = io.StringIO(results.decode("utf-8")) - df = pd.read_csv(_csv, sep=",") - - return df diff --git a/openbb_terminal/alternative/realestate/landRegistry_view.py b/openbb_terminal/alternative/realestate/landRegistry_view.py deleted file mode 100644 index e9538581165..00000000000 --- a/openbb_terminal/alternative/realestate/landRegistry_view.py +++ /dev/null @@ -1,153 +0,0 @@ -""" UK Land Registry View """ - -__docformat__ = "numpy" - -import logging -import os - -from openbb_terminal.alternative.realestate import landRegistry_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_estate_sales(postcode: str, limit: int, export: str = "") -> None: - """Display real estate sales. - - Parameters - ---------- - postcode : str - Postcode - - limit : int - number of rows to return - - """ - sales = landRegistry_model.get_estate_sales(postcode, limit) - - if sales.empty or len(sales) == 0: - console.print( - "[red]" - + "No data loaded.\n" - + "Make sure you picked a valid postcode." - + "[/red]\n" - ) - return - - print_rich_table( - sales, - show_index=False, - title=f"[bold]{postcode}[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "sales", - sales, - ) - - -@log_start_end(log=logger) -def display_towns_sold_prices( - town: str, start_date: str, end_date: str, limit: int, export: str = "" -): - """Get towns sold house price data. - - Parameters - ---------- - town : str - town - - start_date : str - startDate - - end_date : str - endDate - - limit : int - number of rows to return - - - Returns - ------- - pd.DataFrame - All sales for that town within the date range specified - """ - - sales = landRegistry_model.get_towns_sold_prices(town, start_date, end_date, limit) - - if sales.empty or len(sales) == 0: - console.print( - "[red]" - + "No data loaded.\n" - + "Make sure you picked a valid town and date range." - + "[/red]\n" - ) - return - - print_rich_table( - sales, - show_index=False, - title=f"[bold]{town} : {start_date} - {end_date}[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "towns_sold_prices", - sales, - ) - - -@log_start_end(log=logger) -def display_region_stats(region: str, start_date: str, end_date: str, export: str = ""): - """Get towns sold house price data. - - Parameters - ---------- - region : str - region - - start_date : str - startDate - - end_date : str - endDate - - - Returns - ------- - pd.DataFrame - All stats for that region within the date range specified - """ - - stats = landRegistry_model.get_region_stats(region, start_date, end_date) - - if stats.empty or len(stats) == 0: - console.print( - "[red]" - + "No data loaded.\n" - + "Make sure you picked a valid region and date range." - + "[/red]\n" - ) - return - - print_rich_table( - stats, - show_index=False, - title=f"[bold]{region} : {start_date} - {end_date}[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "region_stats", - stats, - ) diff --git a/openbb_terminal/alternative/realestate/realestate_controller.py b/openbb_terminal/alternative/realestate/realestate_controller.py deleted file mode 100644 index b0a86f379bf..00000000000 --- a/openbb_terminal/alternative/realestate/realestate_controller.py +++ /dev/null @@ -1,263 +0,0 @@ -"""Trading Hours Controller.""" - -__docformat__ = "numpy" - -import argparse -import logging -from datetime import datetime, timedelta -from typing import List, Optional - -from openbb_terminal.alternative.realestate import landRegistry_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import EXPORT_ONLY_RAW_DATA_ALLOWED, check_positive -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -def get_relative_date(months: int): - date = datetime.now() - timedelta(days=365 / 12 * months) - return date.strftime("%Y-%m-%d") - - -def start_date(): - return get_relative_date(13) - - -def end_date(): - return get_relative_date(1) - - -class RealEstateController(BaseController): - """Real Estate Controller class.""" - - CHOICES_COMMANDS = ["sales", "townsales", "regionstats"] - PATH = "/alternative/realestate/" - CHOICES_GENERATION = True - # FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md") - - def __init__(self, queue: Optional[List[str]] = None): - """Construct Data.""" - super().__init__(queue) - - self.enddate = None - self.startdate = None - self.town = None - self.postcode = None - self.region = None - self.limit = 25 - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - mt = MenuText("alternative/realestate/") - mt.add_cmd("sales") - mt.add_cmd("townsales") - mt.add_cmd("regionstats") - - console.print(text=mt.menu_text, menu="UK Real Estate Sales Data") - - @log_start_end(log=logger) - def call_sales(self, other_args: List[str]): - """Process 'postcode' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sales", - description="Select the postcode you want to see sold house price data for. [Source: UK Land Registry]", - ) - parser.add_argument( - "-p", - "--postcode", - help="Postcode", - type=str.upper, - required="-h" not in other_args, - dest="postcode", - metavar="postcode", - ) - - parser.add_argument( - "-l", - "--limit", - help="Number of entries to return", - type=check_positive, - required=False, - dest="limit", - metavar="limit", - default=25, - ) - - if ( - other_args - and "-p" not in other_args[0] - and "--postcode" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.postcode: - landRegistry_view.display_estate_sales( - ns_parser.postcode, ns_parser.limit, export=ns_parser.export - ) - else: - console.print("[red]Select valid postcode[/red]\n") - - @log_start_end(log=logger) - def call_townsales(self, other_args: List[str]): - """Process 'townsales' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="townsales", - description="""Select the town and date range you want to see sold house price data for. - [Source: UK Land Registry]""", - ) - - parser.add_argument( - "-t", - "--town", - help="Town that we want sales information for", - type=str.upper, - required="-h" not in other_args, - dest="town", - metavar="town", - ) - - parser.add_argument( - "-s", - "--startdate", - help="Start date that we want sales information for", - type=str, - required=False, - dest="startdate", - metavar="startdate", - default=start_date(), - ) - - parser.add_argument( - "-e", - "--enddate", - help="End date that we want sales information for", - type=str, - required=False, - dest="enddate", - metavar="enddate", - default=end_date(), - ) - - parser.add_argument( - "-l", - "--limit", - help="Number of entries to return", - type=check_positive, - required=False, - dest="limit", - metavar="limit", - default=25, - ) - - if ( - other_args - and "-t" not in other_args[0] - and "--town" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - - if ns_parser: - if ns_parser.town and ns_parser.startdate and ns_parser.enddate: - landRegistry_view.display_towns_sold_prices( - ns_parser.town, - ns_parser.startdate, - ns_parser.enddate, - ns_parser.limit, - ns_parser.export, - ) - else: - console.print( - "[red]Select the town you want to know about and a valid date range.[/red]\n" - ) - - @log_start_end(log=logger) - def call_regionstats(self, other_args: List[str]): - """Process 'regionstats' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="regionstats", - description="Select the region and date range you want see stats for. [Source: UK Land Registry]", - ) - - parser.add_argument( - "-r", - "--region", - help="Region that we want stats for", - type=str.lower, - required="-h" not in other_args, - dest="region", - metavar="region", - ) - - parser.add_argument( - "-s", - "--startdate", - help="Start date that we want sales information for", - type=str, - required=False, - dest="startdate", - metavar="startdate", - default=start_date(), - ) - - parser.add_argument( - "-e", - "--enddate", - help="End date that we want sales information for", - type=str, - required=False, - dest="enddate", - metavar="enddate", - default=end_date(), - ) - - if ( - other_args - and "-r" not in other_args[0] - and "--region" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-r") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.region and ns_parser.startdate and ns_parser.enddate: - landRegistry_view.display_region_stats( - ns_parser.region, - ns_parser.startdate, - ns_parser.enddate, - ns_parser.export, - ) - else: - console.print( - "[red]Select the region you want to know about and a valid date range.[/red]\n" - ) diff --git a/openbb_terminal/base_helpers.py b/openbb_terminal/base_helpers.py deleted file mode 100644 index e4c4c4136e8..00000000000 --- a/openbb_terminal/base_helpers.py +++ /dev/null @@ -1,99 +0,0 @@ -# This is for helpers that do NOT import any OpenBB Modules -import logging -import os -from typing import Any, List, Literal, Optional - -from dotenv import load_dotenv -from posthog import Posthog -from rich.console import Console - -from openbb_terminal.core.config.paths import ( - PACKAGE_ENV_FILE, - REPOSITORY_ENV_FILE, - SETTINGS_ENV_FILE, -) - -console = Console() - -menus = Literal["", "featflags", "settings"] - -openbb_posthog = Posthog( - "phc_w3vscAiNobDjUM9gYw2ffAFPUc8COciilsvvp3tAPi", - host="https://app.posthog.com", -) - - -def handle_error(name: str, default: Any, menu: menus = ""): - """Handles the error by returning the default value and printing an - informative error message. - - Parameters - ---------- - name: str - The name of the environment variable - default: Any - The value to return if the converter fails - menu: menus - If provided, will tell the user where to fix the setting - - Returns - ---------- - Any - The default value - - """ - base = f"[red]Invalid variable provided for variable '{name}'." - if menu: - base += f" Please change the setting in the `{menu}` menu." - base += "[/red]\n" - console.print(base) - return default - - -def strtobool(val): - """Convert a string representation of truth to true (1) or false (0). - - True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values - are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if - 'val' is anything else. - """ - val = str(val).lower() - if val in ("y", "yes", "t", "true", "on", "1"): - output = True - elif val in ("n", "no", "f", "false", "off", "0"): - output = False - else: - raise ValueError(f"invalid truth value {val}") - - return output - - -def load_env_files(): - """ - Loads the dotenv files in the following order: - 1. Repository .env file - 2. Package .env file - 3. User .env file - - This allows the user to override the package settings with their own - settings, and the package to override the repository settings. - - openbb_terminal modules are reloaded to refresh config files with new env, - otherwise they will use cache with old variables. - """ - load_dotenv(REPOSITORY_ENV_FILE, override=True) - load_dotenv(PACKAGE_ENV_FILE, override=True) - load_dotenv(SETTINGS_ENV_FILE, override=True) - - -def remove_log_handlers(): - """Remove the log handlers - needs to be done before reloading modules.""" - for handler in logging.root.handlers[:]: - logging.root.removeHandler(handler) - - -def clear_openbb_env_vars(exceptions: Optional[List[str]] = None): - """Clear openbb environment variables.""" - for v in os.environ: - if v.startswith("OPENBB") and (not exceptions or v not in exceptions): - os.environ.pop(v) diff --git a/openbb_terminal/common/__init__.py b/openbb_terminal/common/__init__.py deleted file mode 100644 index 92139d2d4a9..00000000000 --- a/openbb_terminal/common/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# flake8: noqa -from . import behavioural_analysis, quantitative_analysis, technical_analysis diff --git a/openbb_terminal/common/behavioural_analysis/__init__.py b/openbb_terminal/common/behavioural_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/common/behavioural_analysis/finbrain_model.py b/openbb_terminal/common/behavioural_analysis/finbrain_model.py deleted file mode 100644 index a90ed8e1bab..00000000000 --- a/openbb_terminal/common/behavioural_analysis/finbrain_model.py +++ /dev/null @@ -1,42 +0,0 @@ -"""FinBrain Model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_sentiment(symbol: str) -> pd.DataFrame: - """Gets Sentiment analysis provided by FinBrain's API [Source: finbrain]. - - Parameters - ---------- - symbol : str - Ticker symbol to get the sentiment analysis from - - Returns - ------- - pd.DataFrame - Empty if there was an issue with data retrieval - """ - result = request(f"https://api.finbrain.tech/v0/sentiments/{symbol}") - sentiment = pd.DataFrame() - if result.status_code == 200: - result_json = result.json() - if "sentimentAnalysis" in result_json: - sentiment = pd.DataFrame.from_dict( - result_json["sentimentAnalysis"], orient="index" - ) - sentiment.index = pd.to_datetime(sentiment.index).to_pydatetime() - sentiment.index.name = "date" - sentiment.columns = ["Sentiment Analysis"] - sentiment.sort_index(ascending=True, inplace=True) - - return sentiment diff --git a/openbb_terminal/common/behavioural_analysis/finbrain_view.py b/openbb_terminal/common/behavioural_analysis/finbrain_view.py deleted file mode 100644 index 7f78967788b..00000000000 --- a/openbb_terminal/common/behavioural_analysis/finbrain_view.py +++ /dev/null @@ -1,141 +0,0 @@ -"""FinBrain View Module""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure, rich_config, theme -from openbb_terminal.common.behavioural_analysis import finbrain_model -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def lambda_sentiment_coloring(val: float, last_val: float) -> str: - if float(val) > last_val: - return f"[green]{val}[/green]" - return f"[red]{val}[/red]" - - -@log_start_end(log=logger) -def display_sentiment_analysis( - symbol: str, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots Sentiment analysis from FinBrain. Prints table if raw is True. [Source: FinBrain] - - Parameters - ---------- - symbol: str - Ticker symbol to get the sentiment analysis from - raw: False - Display raw table data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes: bool, optional - Whether to return the figure object or not, by default False - """ - sentiment = finbrain_model.get_sentiment(symbol) - if sentiment.empty: - return console.print("No sentiment data found.\n") - - if raw: - if ( - rich_config.USE_COLOR - and not get_current_user().preferences.USE_INTERACTIVE_DF - ): - color_df = sentiment["Sentiment Analysis"].apply( - lambda_sentiment_coloring, last_val=0 - ) - color_df = pd.DataFrame( - data=color_df.values, - index=pd.to_datetime(sentiment.index).strftime("%Y-%m-%d"), - ) - print_rich_table( - color_df, - headers=["Sentiment"], - title="FinBrain Ticker Sentiment", - show_index=True, - export=bool(export), - ) - else: - print_rich_table( - pd.DataFrame( - data=sentiment.values, - index=pd.to_datetime(sentiment.index).strftime("%Y-%m-%d"), - ), - headers=["Sentiment"], - title="FinBrain Ticker Sentiment", - show_index=True, - export=bool(export), - ) - - fig = OpenBBFigure( - yaxis=dict(title="Sentiment", range=[-1.1, 1.1]), xaxis_title="Time" - ) - - fig.add_hline(y=0, line_dash="dash") - - start_date = sentiment.index[-1].strftime("%Y/%m/%d") - - fig.set_title( - f"FinBrain's Sentiment Analysis for {symbol.upper()} since {start_date}" - ) - senValues = np.array(pd.to_numeric(sentiment["Sentiment Analysis"].values)) - senNone = np.array(0 * len(sentiment)) - df_sentiment = sentiment["Sentiment Analysis"] - negative_yloc = np.where(senValues < senNone)[0] - positive_yloc = np.where(senValues > senNone)[0] - - fig.add_scatter( - x=df_sentiment.index[positive_yloc], - y=pd.to_numeric(df_sentiment.values)[positive_yloc], - mode="lines+markers", - marker=dict(color=theme.up_color, size=15), - line_width=1, - name=symbol, - ) - fig.add_scatter( - x=[df_sentiment.index[0], df_sentiment.index[-1]], - y=[0, 0], - fillcolor=theme.up_color_transparent.replace("0.5", "0.4"), - line=dict(color="white", dash="dash"), - fill="tonexty", - mode="lines", - name=symbol, - ) - fig.add_scatter( - x=df_sentiment.index[negative_yloc], - y=pd.to_numeric(df_sentiment.values)[negative_yloc], - fill="tonexty", - fillcolor=theme.down_color_transparent.replace("0.5", "0.4"), - line_width=1, - mode="lines+markers", - marker=dict(color=theme.down_color, size=15), - name=symbol, - ) - fig.update_traces(showlegend=False) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "headlines", - sentiment, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/common/behavioural_analysis/finnhub_model.py b/openbb_terminal/common/behavioural_analysis/finnhub_model.py deleted file mode 100644 index 8dc484e7b6e..00000000000 --- a/openbb_terminal/common/behavioural_analysis/finnhub_model.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Finnhub Model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def get_sentiment_stats(ticker: str) -> pd.DataFrame: - """Get sentiment stats [Source: finnhub]. - - Parameters - ---------- - ticker : str - Ticker to get sentiment stats - - Returns - ------- - pd.DataFrame - Get sentiment stats - """ - response = request( - f"https://finnhub.io/api/v1/news-sentiment?symbol={ticker}&token={get_current_user().credentials.API_FINNHUB_KEY}" - ) - - df = pd.DataFrame() - if response.status_code == 200: - df = response.json() - - if df.empty: - console.print(f"No sentiment stats found for {ticker}.\n") - - elif response.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - elif response.status_code == 403: - console.print("[red]API Key not authorized for Premium Feature[/red]\n") - else: - console.print(f"Error in request: {response.json()['error']}", "\n") - - return df diff --git a/openbb_terminal/common/behavioural_analysis/finnhub_view.py b/openbb_terminal/common/behavioural_analysis/finnhub_view.py deleted file mode 100644 index 1c70a33d45b..00000000000 --- a/openbb_terminal/common/behavioural_analysis/finnhub_view.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Finnhub View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal.common.behavioural_analysis import finnhub_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def display_sentiment_stats( - ticker: str, export: str = "", sheet_name: Optional[str] = None -): - """ - Prints Sentiment stats which displays buzz, news score, articles last week, articles weekly average, - bullish vs bearish percentages, sector average bullish percentage, and sector average news score - - Parameters - ---------- - ticker : str - Ticker to get sentiment stats - export : str - Format to export data - """ - d_stats = finnhub_model.get_sentiment_stats(ticker) - - if d_stats.empty: - return - - if d_stats: - console.print( - f""" -Buzz: {round(100*d_stats['buzz']['buzz'],2)} % -News Score: {round(100*d_stats['companyNewsScore'],2)} % - -Articles Last Week: {d_stats['buzz']['articlesInLastWeek']} -Articles Weekly Average: {d_stats['buzz']['weeklyAverage']} - -Bullish: {round(100*d_stats['sentiment']['bullishPercent'],2)} % -Bearish: {round(100*d_stats['sentiment']['bearishPercent'],2)} % - -Sector Average Bullish: {round(100*d_stats['sectorAverageBullishPercent'],2)} -Sector Average News Score: {round(100*d_stats['sectorAverageNewsScore'],2)} %""" - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "stats", - pd.DataFrame(d_stats), - sheet_name, - ) - - else: - console.print("No sentiment stats found.") diff --git a/openbb_terminal/common/behavioural_analysis/google_model.py b/openbb_terminal/common/behavioural_analysis/google_model.py deleted file mode 100644 index 9cd86bf999e..00000000000 --- a/openbb_terminal/common/behavioural_analysis/google_model.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Google Model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd -from pytrends.request import TrendReq - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_mentions(symbol: str) -> pd.DataFrame: - """Get interest over time from google api [Source: google]. - - Parameters - ---------- - symbol: str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - Dataframe of interest over time - """ - try: - pytrend = TrendReq() - pytrend.build_payload(kw_list=[symbol]) - return pytrend.interest_over_time() - - except Exception as e: - if pytrend.google_rl: - console.print(f"[red]Too many requests: {pytrend.google_rl}[/red]\n") - else: - console.print(f"[red]{str(e)}[/red]\n") - - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_regions(symbol: str) -> pd.DataFrame: - """Get interest by region from google api [Source: google]. - - Parameters - ---------- - symbol: str - Ticker symbol to look at - - Returns - ------- - pd.DataFrame - Dataframe of interest by region - """ - - try: - pytrend = TrendReq() - pytrend.build_payload(kw_list=[symbol]) - return pytrend.interest_by_region().sort_values([symbol], ascending=False) - - except Exception as e: - if pytrend.google_rl: - console.print(f"[red]Too many requests: {pytrend.google_rl}[/red]\n") - else: - console.print(f"[red]{str(e)}[/red]\n") - - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_queries(symbol: str, limit: int = 10) -> pd.DataFrame: - """Get related queries from google api [Source: google]. - - Parameters - ---------- - symbol: str - Stock ticker symbol to compare - limit: int - Number of queries to show - - Returns - ------- - pd.DataFrame - Dataframe of related queries - """ - try: - pytrend = TrendReq() - pytrend.build_payload(kw_list=[symbol]) - df = pytrend.related_queries() - df = df[symbol]["top"].head(limit) - df["value"] = df["value"].apply(lambda x: f"{str(x)}%") - return df - - except Exception as e: - if pytrend.google_rl: - console.print(f"[red]Too many requests: {pytrend.google_rl}[/red]\n") - else: - console.print(f"[red]{str(e)}[/red]\n") - - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_rise(symbol: str, limit: int = 10) -> pd.DataFrame: - """Get top rising related queries with this stock's query [Source: google]. - - Parameters - ---------- - symbol: str - Stock ticker symbol - limit: int - Number of queries to show - - Returns - ------- - pd.DataFrame - Dataframe containing rising related queries - """ - try: - pytrend = TrendReq() - pytrend.build_payload(kw_list=[symbol]) - df = pytrend.related_queries() - df = df[symbol]["rising"].head(limit) - return df - - except Exception as e: - if pytrend.google_rl: - console.print(f"[red]Too many requests: {pytrend.google_rl}[/red]\n") - else: - console.print(f"[red]{str(e)}[/red]\n") - - return pd.DataFrame() diff --git a/openbb_terminal/common/behavioural_analysis/google_view.py b/openbb_terminal/common/behavioural_analysis/google_view.py deleted file mode 100644 index c895578901e..00000000000 --- a/openbb_terminal/common/behavioural_analysis/google_view.py +++ /dev/null @@ -1,283 +0,0 @@ -"""Google View.""" - -__docformat__ = "numpy" - -import logging -import os -from typing import List, Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.common.behavioural_analysis import google_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_mentions( - symbol: str, - start_date: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots weekly bars of stock's interest over time. other users watchlist. [Source: Google]. - - Parameters - ---------- - symbol : str - Ticker symbol - start_date : str - Start date as YYYY-MM-DD string - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df_interest = google_model.get_mentions(symbol) - - if df_interest.empty: - return None - - fig = OpenBBFigure( - title=f"Interest over time on {symbol}", - xaxis_title="Date", - yaxis_title="Interest [%]", - ) - if start_date: - df_interest = df_interest[start_date:] # type: ignore - - fig.add_bar( - x=df_interest.index[:-1], - y=df_interest[symbol].values[:-1], - name=symbol, - showlegend=False, - ) - fig.add_bar( - x=[df_interest.index[-1]], - y=[df_interest[symbol].values[-1]], - name=symbol, - showlegend=False, - ) - fig.update_layout(xaxis=dict(type="date")) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mentions", - df_interest, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_correlation_interest( - symbol: str, - data: pd.DataFrame, - words: List[str], - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Plots interest over time of words/sentences versus stock price. [Source: Google]. - - Parameters - ---------- - symbol : str - Ticker symbol to check price - data : pd.DataFrame - Data dataframe - words : List[str] - Words to check for interest for - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure.create_subplots( - rows=2, - cols=1, - shared_xaxes=True, - vertical_spacing=0.08, - subplot_titles=( - f"{symbol.upper()} stock price and interest over time on {','.join(words)}", - "Interest [%]", - ), - ) - fig.add_scatter( - x=data.index, - y=data["Adj Close"].values, - name="Stock Price", - row=1, - col=1, - ) - for word in words: - df_interest = google_model.get_mentions(word) - if not df_interest.empty: - df_interest = df_interest.reindex(data.index, method="nearest") - - fig.add_scatter( - x=df_interest.index, - y=df_interest[word].values, - name=word, - row=2, - col=1, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "interest", - df_interest, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_regions( - symbol: str, - limit: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots bars of regions based on stock's interest. [Source: Google]. - - Parameters - ---------- - symbol : str - Ticker symbol - limit: int - Number of regions to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_interest_region = google_model.get_regions(symbol) - - if df_interest_region.empty: - return None - - df_interest_region = df_interest_region.head(limit) - df = df_interest_region.sort_values([symbol], ascending=True) - - fig = OpenBBFigure( - title=f"Regions with highest interest in {symbol}", - yaxis_title="Region", - xaxis_title="Interest [%]", - ) - fig.add_bar( - x=df[symbol], - y=df.index, - orientation="h", - name=symbol, - showlegend=False, - marker_color=theme.get_colors(reverse=True), - ) - fig.update_layout(yaxis=dict(type="category")) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "regions", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_queries( - symbol: str, limit: int = 5, export: str = "", sheet_name: Optional[str] = None -): - """Prints table showing top related queries with this stock's query. [Source: Google]. - - Parameters - ---------- - symbol : str - Ticker symbol - limit: int - Number of regions to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - {"csv","json","xlsx","png","jpg","pdf","svg"} - """ - # Retrieve a dict with top and rising queries - df = google_model.get_queries(symbol, limit) - - if df.empty: - return - - print_rich_table( - df, - headers=list(df.columns), - title=f"Top {symbol}'s related queries", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "queries", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_rise( - symbol: str, limit: int = 10, export: str = "", sheet_name: Optional[str] = None -): - """Prints top rising related queries with this stock's query. [Source: Google]. - - Parameters - ---------- - symbol : str - Ticker symbol - limit: int - Number of queries to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - df_related_queries = google_model.get_rise(symbol, limit) - - if df_related_queries.empty: - return - - print_rich_table( - df_related_queries, - headers=list(df_related_queries.columns), - title=f"Top rising {symbol}'s related queries", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rise", - df_related_queries, - sheet_name, - ) diff --git a/openbb_terminal/common/behavioural_analysis/reddit_helpers.py b/openbb_terminal/common/behavioural_analysis/reddit_helpers.py deleted file mode 100644 index c4fd0f2c759..00000000000 --- a/openbb_terminal/common/behavioural_analysis/reddit_helpers.py +++ /dev/null @@ -1,174 +0,0 @@ -"""Reddit Helpers.""" - -__docformat__ = "numpy" - -import logging -import re -import urllib.request -from typing import List - -import pandas as pd -import praw -from prawcore.exceptions import ResponseException - -from openbb_terminal.core.models import UserModel -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -reddit_requirements = [ - "API_REDDIT_CLIENT_ID", - "API_REDDIT_CLIENT_SECRET", - "API_REDDIT_USERNAME", - "API_REDDIT_USER_AGENT", - "API_REDDIT_PASSWORD", -] - - -def find_tickers(submission: praw.models.reddit.submission.Submission) -> List[str]: - """Extract potential tickers from reddit submission. - - Parameters - ---------- - submission : praw.models.reddit.submission.Submission - Reddit post to scan - - Returns - ------- - List[str] - List of potential tickers - """ - ls_text = [submission.selftext, submission.title] - submission.comments.replace_more(limit=0) - for comment in submission.comments.list(): - ls_text.append(comment.body) - - l_tickers_found = [] - for s_text in ls_text: - for s_ticker in set(re.findall(r"([A-Z]{3,5})", s_text)): - l_tickers_found.append(s_ticker.strip()) - - return l_tickers_found - - -def get_praw_api(current_user: UserModel) -> praw.Reddit: - """Get praw api. - - Parameters - ---------- - current_user : UserModel - The user model - - Returns - ------- - praw.Reddit - Praw api - """ - # See https://github.com/praw-dev/praw/issues/1016 regarding praw arguments - praw_api = praw.Reddit( - client_id=current_user.credentials.API_REDDIT_CLIENT_ID, - client_secret=current_user.credentials.API_REDDIT_CLIENT_SECRET, - username=current_user.credentials.API_REDDIT_USERNAME, - user_agent=current_user.credentials.API_REDDIT_USER_AGENT, - password=current_user.credentials.API_REDDIT_PASSWORD, - check_for_updates=False, - comment_kind="t1", - message_kind="t4", - redditor_kind="t2", - submission_kind="t3", - subreddit_kind="t5", - trophy_kind="t6", - oauth_url="https://oauth.reddit.com", - reddit_url="https://www.reddit.com", - short_url="https://redd.it", - ratelimit_seconds=5, - timeout=16, - ) - return praw_api - - -def has_author(submission_, authors) -> bool: - """Check if submission has author.""" - return ( - hasattr(submission_, "author") - and hasattr(submission_.author, "name") - and submission_.author.name not in authors - ) - - -def has_content(submission_) -> bool: - """Check if submission has text or title.""" - return hasattr(submission_, "selftext") or hasattr(submission_, "title") - - -class RedditResponses: - tickers: dict = {} - authors: List[str] = [] - - def __init__(self): - pass - - def gather(self, praw_api, responses) -> int: - count = 0 - for submission in responses: - try: - # Get more information about post using PRAW api - submission_ = praw_api.submission(id=submission["id"]) - - # Ensure that the post hasn't been removed by moderator in the meanwhile, - # that there is a description and it's not just an image, that the flair is - # meaningful, and that we aren't re-considering same author's content - - if ( - submission_ is not None - and not submission_.removed_by_category - and has_content(submission_) - and has_author(submission_, self.authors) - ): - l_tickers_found = find_tickers(submission_) - - if l_tickers_found: - count += len(l_tickers_found) - - # Add another author's name to the parsed watchlists - self.authors.append(submission_.author.name) - - # Lookup stock tickers within a watchlist - for key in l_tickers_found: - if key in self.tickers: - # Increment stock ticker found - self.tickers[key] += 1 - else: - # Initialize stock ticker found - self.tickers[key] = 1 - - except ResponseException as e: - logger.exception("Invalid response: %s", str(e)) - - if "received 401 HTTP response" in str(e): - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(f"[red]Invalid response: {str(e)}[/red]\n") - return 0 - return count - - def validate( - self, - url: str = "https://raw.githubusercontent.com/rreichel3/US-Stock-Symbols/main/all/all_tickers.txt", - ) -> None: - """Validate tickers.""" - valids = [] - with urllib.request.urlopen(url) as file: # noqa: S310 - for line in file: - new_item = line.decode("utf-8").replace("\n", "").strip() - valids.append(new_item) - - for key in list(self.tickers.keys()): - if key not in valids: - self.tickers.pop(key) - - def to_df(self) -> pd.DataFrame: - popularity = pd.DataFrame.from_dict(self.tickers, orient="index") - popularity.columns = ["Mentions"] - popularity = popularity.sort_values(by=["Mentions"], ascending=False) - return popularity diff --git a/openbb_terminal/common/behavioural_analysis/reddit_model.py b/openbb_terminal/common/behavioural_analysis/reddit_model.py deleted file mode 100644 index 346be70495b..00000000000 --- a/openbb_terminal/common/behavioural_analysis/reddit_model.py +++ /dev/null @@ -1,599 +0,0 @@ -"""Reddit Model.""" - -__docformat__ = "numpy" -# pylint:disable=C0302 - -import logging -import warnings -from datetime import datetime, timedelta -from typing import List, Tuple - -import pandas as pd -import praw -from pmaw import PushshiftAPI -from prawcore.exceptions import ResponseException -from sklearn.feature_extraction import _stop_words -from tqdm import tqdm -from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer - -from openbb_terminal.common.behavioural_analysis.reddit_helpers import ( - RedditResponses, - find_tickers, - get_praw_api, - reddit_requirements, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -l_sub_reddits = [ - "Superstonk", - "pennystocks", - "RobinHoodPennyStocks", - "Daytrading", - "StockMarket", - "stocks", - "investing", - "wallstreetbets", -] - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def get_popular_tickers( - limit: int = 10, post_limit: int = 50, subreddits: str = "" -) -> pd.DataFrame: - """Get popular tickers from list of subreddits [Source: reddit]. - - Parameters - ---------- - limit : int - Number of top tickers to get - post_limit : int - How many posts to analyze in each subreddit - subreddits : str, optional - String of comma separated subreddits. - - Returns - ------- - pd.DataFrame - DataFrame of top tickers from supplied subreddits - """ - current_user = get_current_user() - sub_reddit_list = ( - (subreddits.split(",") if "," in subreddits else [subreddits]) - if subreddits - else l_sub_reddits - ) - - praw_api = get_praw_api(current_user) - try: - praw_api.user.me() - except (Exception, ResponseException): - console.print("[red]Wrong Reddit API keys[/red]\n") - return pd.DataFrame() - - pmaw_api = PushshiftAPI() - - responses = RedditResponses() - for s_sub_reddit in sub_reddit_list: - console.print( - f"Searching for tickers in latest {post_limit} '{s_sub_reddit}' posts" - ) - warnings.filterwarnings( - "ignore", message=".*Not all PushShift shards are active.*" - ) - submissions = pmaw_api.search_submissions( - subreddit=s_sub_reddit, - limit=post_limit, - filter=["id"], - ) - - count = responses.gather(praw_api, submissions.responses) - responses.validate() - console.print(f" {count} potential tickers found.") - - return responses.to_df().head(limit) - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def get_spac_community( - limit: int = 10, popular: bool = False -) -> Tuple[pd.DataFrame, dict]: - """Get top tickers from r/SPACs [Source: reddit]. - - Parameters - ---------- - limit : int - Number of posts to look at - popular : bool - Search by hot instead of new - - Returns - ------- - Tuple[pd.DataFrame, dict] - Dataframe of reddit submission, - Dictionary of tickers and number of mentions. - """ - current_user = get_current_user() - - praw_api = get_praw_api(current_user) - try: - praw_api.user.me() - except (Exception, ResponseException): - console.print("[red]Wrong Reddit API keys[/red]\n") - return pd.DataFrame(), {} - - d_watchlist_tickers: dict = {} - l_watchlist_author = [] - - if popular: - submissions = praw_api.subreddit("SPACs").hot(limit=limit) - else: - submissions = praw_api.subreddit("SPACs").new(limit=limit) - - columns = [ - "Date", - "Subreddit", - "Flair", - "Title", - "Score", - "# Comments", - "Upvote %", - "Awards", - "Link", - ] - subs = pd.DataFrame(columns=columns) - - try: - for sub in submissions: - if not sub: - break - - # Get more information about post using PRAW api - submission = praw_api.submission(id=sub.id) - - # Ensure that the post hasn't been removed by moderator in the meanwhile, - # that there is a description and it's not just an image, that the flair is - # meaningful, and that we aren't re-considering same author's watchlist - if ( - not submission.removed_by_category - and submission.selftext - and submission.link_flair_text not in ["Yolo", "Meme"] - and submission.author.name not in l_watchlist_author - ): - l_tickers_found = find_tickers(submission) - - if l_tickers_found: - # Add another author's name to the parsed watchlists - l_watchlist_author.append(submission.author.name) - s_datetime = datetime.utcfromtimestamp( - submission.created_utc - ).strftime("%Y-%m-%d %H:%M:%S") - s_link = f"https://old.reddit.com{submission.permalink}" - s_all_awards = "".join( - f"{award['count']} {award['name']}\n" - for award in submission.all_awardings - ) - - s_all_awards = s_all_awards[:-2] - - data = [ - s_datetime, - submission.subreddit, - submission.link_flair_text, - submission.title, - submission.score, - submission.num_comments, - f"{round(100 * submission.upvote_ratio)}%", - s_all_awards, - s_link, - ] - subs.loc[len(subs)] = data - # Lookup stock tickers within a watchlist - for key in l_tickers_found: - if key in d_watchlist_tickers: - # Increment stock ticker found - d_watchlist_tickers[key] += 1 - else: - # Initialize stock ticker found - d_watchlist_tickers[key] = 1 - - except ResponseException as e: - logger.exception("Invalid response: %s", str(e)) - - if "received 401 HTTP response" in str(e): - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(f"[red]Invalid response: {str(e)}[/red]\n") - - return subs, d_watchlist_tickers - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def get_wsb_community(limit: int = 10, new: bool = False) -> pd.DataFrame: - """Get wsb posts [Source: reddit]. - - Parameters - ---------- - limit : int, optional - Number of posts to get, by default 10 - new : bool, optional - Flag to sort by new instead of hot, by default False - - Returns - ------- - pd.DataFrame - Dataframe of reddit submissions - """ - current_user = get_current_user() - - praw_api = get_praw_api(current_user) - try: - praw_api.user.me() - except (Exception, ResponseException): - console.print("[red]Wrong Reddit API keys[/red]\n") - return pd.DataFrame() - - if new: - submissions = praw_api.subreddit("wallstreetbets").new(limit=limit) - else: - submissions = praw_api.subreddit("wallstreetbets").hot(limit=limit) - - columns = [ - "Date", - "Subreddit", - "Flair", - "Title", - "Score", - "# Comments", - "Upvote %", - "Awards", - "Link", - ] - subs = pd.DataFrame(columns=columns) - - try: - for submission in submissions: - submission_ = praw_api.submission(submission.id) - # Ensure that the post hasn't been removed by moderator in the meanwhile, - # that there is a description and it's not just an image, that the flair is - # meaningful, and that we aren't re-considering same author's watchlist - if not submission_.removed_by_category: - s_datetime = datetime.utcfromtimestamp( - submission_.created_utc - ).strftime("%Y-%m-%d %H:%M:%S") - s_link = f"https://old.reddit.com{submission_.permalink}" - s_all_awards = "".join( - f"{award['count']} {award['name']}\n" - for award in submission_.all_awardings - ) - - s_all_awards = s_all_awards[:-2] - - data = [ - s_datetime, - submission_.subreddit, - submission_.link_flair_text, - submission_.title, - submission_.score, - submission_.num_comments, - f"{round(100 * submission_.upvote_ratio)}%", - s_all_awards, - s_link, - ] - subs.loc[len(subs)] = data - except ResponseException as e: - logger.exception("Invalid response: %s", str(e)) - - if "received 401 HTTP response" in str(e): - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(f"[red]Invalid response: {str(e)}[/red]\n") - return subs - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def get_due_dilligence( - limit: int = 5, n_days: int = 3, show_all_flairs: bool = False -) -> pd.DataFrame: - """Get due diligence posts from list of subreddits [Source: reddit]. - - Parameters - ---------- - limit: int - Number of posts to get - n_days: int - Number of days back to get posts - show_all_flairs: bool - Search through all flairs (apart from Yolo and Meme) - - Returns - ------- - pd.DataFrame - Dataframe of submissions - """ - current_user = get_current_user() - - praw_api = get_praw_api(current_user) - try: - praw_api.user.me() - except (Exception, ResponseException): - console.print("[red]Wrong Reddit API keys[/red]\n") - return pd.DataFrame() - - pmaw_api = PushshiftAPI() - - n_ts_after = int((datetime.today() - timedelta(days=n_days)).timestamp()) - l_flair_text = [ - "DD", - "technical analysis", - "Catalyst", - "News", - "Advice", - "Chart", - "Charts and Setups", - "Fundamental Analysis", - "forex", - "Trade Idea", - ] - l_sub_reddits_dd = [ - "pennystocks", - "RobinHoodPennyStocks", - "Daytrading", - "StockMarket", - "stocks", - "investing", - "wallstreetbets", - "forex", - "Forexstrategy", - ] - - submissions = pmaw_api.search_submissions( - after=int(n_ts_after), - subreddit=l_sub_reddits_dd, - filter=["id"], - ) - n_flair_posts_found = 0 - columns = [ - "Date", - "Subreddit", - "Flair", - "Title", - "Score", - "# Comments", - "Upvote %", - "Awards", - "Link", - ] - subs = pd.DataFrame(columns=columns) - - try: - for submission in submissions.responses: - # Get more information about post using PRAW api - submission_ = praw_api.submission(id=submission["id"]) - - # Ensure that the post hasn't been removed in the meanwhile - # Either just filter out Yolo, and Meme flairs, or focus on DD, based on b_DD flag - if ( - not submission_.removed_by_category - and submission_.link_flair_text in l_flair_text, - submission_.link_flair_text not in ["Yolo", "Meme"], - )[show_all_flairs]: - s_datetime = datetime.utcfromtimestamp( - submission_.created_utc - ).strftime("%Y-%m-%d %H:%M:%S") - s_link = f"https://old.reddit.com{submission_.permalink}" - s_all_awards = "".join( - f"{award['count']} {award['name']}\n" - for award in submission_.all_awardings - ) - - s_all_awards = s_all_awards[:-2] - - data = [ - s_datetime, - submission_.subreddit, - submission_.link_flair_text, - submission_.title, - submission_.score, - submission_.num_comments, - f"{round(100 * submission_.upvote_ratio)}%", - s_all_awards, - s_link, - ] - subs.loc[len(subs)] = data - # Increment count of valid posts found - n_flair_posts_found += 1 - - # Check if number of wanted posts found has been reached - if n_flair_posts_found > limit - 1: - break - except ResponseException as e: - logger.exception("Invalid response: %s", str(e)) - - if "received 401 HTTP response" in str(e): - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(f"[red]Invalid response: {str(e)}[/red]\n") - return subs - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def get_posts_about( - symbol: str, - limit: int = 100, - sortby: str = "relevance", - time_frame: str = "week", - full_search: bool = True, - subreddits: str = "all", -) -> Tuple[pd.DataFrame, list, float]: - """Find posts related to a specific search term in Reddit. - - Parameters - ---------- - symbol: str - Ticker symbol to search for - limit: int - Number of posts to get per subreddit - sortby: str - Search type (Possibilities: "relevance", "hot", "top", "new", or "comments") - time_frame: str - Relative time of post (Possibilities: "hour", "day", "week", "month", "year", "all") - full_search: bool - Enable comprehensive search for ticker - subreddits: str - Comma-separated list of subreddits - - Returns - ------- - Tuple[pd.DataFrame, list, float]: - Dataframe of submissions related to the search term, - List of polarity scores, - Average polarity score. - """ - current_user = get_current_user() - - praw_api = get_praw_api(current_user) - try: - praw_api.user.me() - except (Exception, ResponseException): - console.print("[red]Wrong Reddit API keys[/red]\n") - return pd.DataFrame() - - subreddits_l = subreddits.split(",") - posts = [] - post_ids = set() - console.print("Searching through subreddits for posts.") - for sub_str in tqdm(subreddits_l): - try: - subreddit = praw_api.subreddit(sub_str) - except Exception: - console.print("Invalid subreddit name {sub_str}, skipping") - continue - submissions = subreddit.search( - query=symbol, - limit=limit, - sort=sortby, - time_filter=time_frame, - ) - for sub in submissions: - if ( - sub.selftext - and sub.title - and not sub.removed_by_category - and sub.id not in post_ids - ): - post_ids.add(sub.id) - posts.append(sub) - - polarity_scores = [] - post_data = [] - console.print("Analyzing each post...") - for p in tqdm(posts): - texts = [p.title, p.selftext] - if full_search: - top_level_comments = get_comments(p) - texts.extend(top_level_comments) - preprocessed_text = clean_reddit_text(texts) - sentiment = get_sentiment(preprocessed_text) - polarity_scores.append(sentiment) - post_data.append([p.title, sentiment]) - - avg_polarity = sum(polarity_scores) / len(polarity_scores) - - columns = ["Title", "Polarity Score"] - df = pd.DataFrame(post_data, columns=columns) - - return df, polarity_scores, avg_polarity - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def get_comments( - post: praw.models.reddit.submission.Submission, -) -> List[praw.models.reddit.comment.Comment]: - """Recursively gets comments from a post. - - Parameters - ---------- - post: praw.models.reddit.submission.Submission - Post to get comments from - - Returns - ------- - list[praw.models.reddit.comment.Comment] - List of all comments on the post - """ - - def get_more_comments(comments): - sub_top_level_comments = [] - for comment in comments: - if isinstance(comment, praw.models.reddit.comment.Comment): - sub_top_level_comments.append(comment.body) - else: - sub_comments = get_more_comments(comment.comments()) - sub_top_level_comments.extend(sub_comments) - return sub_top_level_comments - - if post.comments: - return get_more_comments(post.comments) - return [] - - -@log_start_end(log=logger) -def clean_reddit_text(docs: List[str]) -> List[str]: - """Tokenize and clean a list of documents for sentiment analysis. - - Parameters - ---------- - docs: list[str] - A list of documents to prepare for sentiment analysis - - Returns - ------- - list[str] - List of cleaned and prepared docs - """ - stop_words = _stop_words.ENGLISH_STOP_WORDS - clean_docs = [] - docs = [doc.lower().strip() for doc in docs] - - for doc in docs: - clean_doc = [] - tokens = doc.split() - for tok in tokens: - clean_tok = [c for c in tok if c.isalpha()] - tok_ = "".join(clean_tok) - if tok_ not in stop_words: - clean_doc.append(tok_) - clean_docs.append(" ".join(clean_doc)) - return clean_docs - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def get_sentiment(post_data: List[str]) -> float: - """Find the sentiment of a post and related comments. - - Parameters - ---------- - post_data: list[str] - A post and its comments in string form - - Returns - ------- - float - A number in the range [-1, 1] representing sentiment - """ - analyzer = SentimentIntensityAnalyzer() - post_data_l = " ".join(post_data) - sentiment = analyzer.polarity_scores(post_data_l) - score = sentiment["pos"] - sentiment["neg"] - return (score - 0.06) * 8 diff --git a/openbb_terminal/common/behavioural_analysis/reddit_view.py b/openbb_terminal/common/behavioural_analysis/reddit_view.py deleted file mode 100644 index cab4c43f6ed..00000000000 --- a/openbb_terminal/common/behavioural_analysis/reddit_view.py +++ /dev/null @@ -1,343 +0,0 @@ -"""Reddit View.""" - -__docformat__ = "numpy" - -import io -import logging -import os -import textwrap -from datetime import datetime -from typing import Dict, Optional, Union - -import pandas as pd -import praw -from finvizfinance.screener.ticker import Ticker - -from openbb_terminal import OpenBBFigure, rich_config -from openbb_terminal.common.behavioural_analysis import reddit_model -from openbb_terminal.common.behavioural_analysis.reddit_helpers import ( - reddit_requirements, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -# pylint: disable=R0913,C0302 -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def print_and_record_reddit_post( - submissions_dict: Dict, submission: praw.models.reddit.submission.Submission -): - """Print reddit submission. - - Parameters - ---------- - submissions_dict : Dict - Dictionary for storing reddit post information - submission : praw.models.reddit.submission.Submission - Submission to show - """ - # Refactor data - s_datetime = datetime.utcfromtimestamp(submission.created_utc).strftime( - "%Y-%m-%d %H:%M:%S" - ) - s_link = f"https://old.reddit.com{submission.permalink}" - s_all_awards = "".join( - f"{award['count']} {award['name']}\n" for award in submission.all_awardings - ) - - s_all_awards = s_all_awards[:-2] - # Create dictionary with data to construct dataframe allows to save data - submissions_dict[submission.id] = { - "created_utc": s_datetime, - "subreddit": submission.subreddit, - "link_flair_text": submission.link_flair_text, - "title": submission.title, - "score": submission.score, - "link": s_link, - "num_comments": submission.num_comments, - "upvote_ratio": submission.upvote_ratio, - "awards": s_all_awards, - } - # Print post data collected so far - console.print(f"[yellow]{s_datetime}[/yellow] - {submission.title}") - console.print(f"[blue]{s_link}[/blue]\n") - columns = ["Subreddit", "Flair", "Score", "# Comments", "Upvote %", "Awards"] - data = [ - submission.subreddit, - submission.link_flair_text, - submission.score, - submission.num_comments, - f"{round(100 * submission.upvote_ratio)}%", - s_all_awards, - ] - df = pd.DataFrame([data], columns=columns) - print_rich_table( - df, headers=list(df.columns), show_index=False, title="Reddit Submission" - ) - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def print_reddit_post(sub: tuple): - """Print reddit submission. - - Parameters - ---------- - sub : tuple - Row from submissions dataframe - """ - sub_list = list(sub[1]) - date = sub_list[0] - title = sub_list[3] - link = sub_list[-1] - - if rich_config.USE_COLOR and not get_current_user().preferences.USE_INTERACTIVE_DF: - console.print(f"[yellow]{date}[/yellow] - {title}") - console.print(f"[blue]{link}[/blue]\n") - - columns = [ - "Subreddit", - "Flair", - "Score", - "# Comments", - "Upvote %", - "Awards", - ] - - print_rich_table( - pd.DataFrame(sub[1][columns]).T, - headers=columns, - show_index=False, - title="Reddit Submission", - ) - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def display_popular_tickers( - limit: int = 10, - post_limit: int = 50, - subreddits: str = "", - export: str = "", - sheet_name: Optional[str] = None, -): - """Print table showing latest popular tickers. [Source: Reddit]. - - Parameters - ---------- - limit : int - Number of top tickers to get - post_limit : int - How many posts to analyze in each subreddit - subreddits : str, optional - String of comma separated subreddits. - export : str - Format to export dataframe - """ - popular_tickers_df = reddit_model.get_popular_tickers(limit, post_limit, subreddits) - if popular_tickers_df.empty: - console.print("No tickers found") - return - print_rich_table( - popular_tickers_df, - headers=list(popular_tickers_df.columns), - show_index=True, - title=f"The following TOP {limit} tickers have been mentioned", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "popular", - popular_tickers_df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def display_spac_community(limit: int = 10, popular: bool = False): - """Print tickers mentioned in r/SPACs [Source: Reddit]. - - Parameters - ---------- - limit: int - Number of posts to look through - popular: bool - Search by popular instead of new - """ - subs, d_watchlist_tickers = reddit_model.get_spac_community(limit, popular) - if not subs.empty: - if d_watchlist_tickers: - lt_watchlist_sorted = sorted( - d_watchlist_tickers.items(), key=lambda item: item[1], reverse=True - ) - s_watchlist_tickers = "" - n_tickers = 0 - tickers = Ticker() - ticker_list = tickers.screener_view() - # validate against a list of all tickers - for t_ticker in lt_watchlist_sorted: - if t_ticker[0] in ticker_list: - if int(t_ticker[1]) > 1: - s_watchlist_tickers += f"{t_ticker[1]} {t_ticker[0]}, " - n_tickers += 1 - if n_tickers: - console.print( - "The following stock tickers have been mentioned more than once across the previous posts on " - "r/spaccs: " - ) - console.print(s_watchlist_tickers[:-2]) - print_rich_table( - pd.DataFrame(subs), - show_index=False, - title="Reddit Submission", - ) - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def display_wsb_community(limit: int = 10, new: bool = False): - """Print WSB posts. - - Parameters - ---------- - limit : int, optional - Number of posts to look at, by default 10 - new : bool, optional - Flag to sort by new instead of hot, by default False - """ - subs = reddit_model.get_wsb_community(limit, new) - # I am not proud of this, but it works to eliminate the max recursion bug - subs = pd.read_csv(io.StringIO(subs.to_csv()), index_col=0).fillna("-") - if not subs.empty: - print_rich_table(subs) - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def display_due_diligence( - limit: int = 10, n_days: int = 3, show_all_flairs: bool = False -): - """Print Reddit due diligence data for a given ticker. - - Parameters - ---------- - limit: int - Number of posts to get - n_days: int - Number of days back to get posts - show_all_flairs: bool - Search through all flairs (apart from Yolo and Meme) - """ - subs = reddit_model.get_due_dilligence(limit, n_days, show_all_flairs) - if not subs.empty: - for sub in subs.iterrows(): - print_reddit_post(sub) - console.print("") - else: - console.print("No DD posts found\n") - - -@log_start_end(log=logger) -@check_api_key(reddit_requirements) -def display_redditsent( - symbol: str, - sortby: str = "relevance", - limit: int = 100, - graphic: bool = False, - time_frame: str = "week", - full_search: bool = True, - subreddits: str = "all", - display: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot Reddit sentiment about a search term. Prints table showing if display is True. - - Parameters - ---------- - symbol: str - The ticker symbol being search for in Reddit - sortby: str - Type of search - limit: str - Number of posts to get at most - graphic: bool - Displays box and whisker plot - time_frame: str - Time frame for search - full_search: bool - Enable comprehensive search for ticker - subreddits: str - Comma-separated list of subreddits - display: bool - Enable printing of raw sentiment values for each post - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure() - - df, polarity_scores, avg_polarity = reddit_model.get_posts_about( - symbol, limit, sortby, time_frame, full_search, subreddits - ) - - if df.empty: - return console.print(f"No posts for {symbol} found") - - if display: - print_rich_table(df=df, export=bool(export)) - - if not fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "polarity_scores", - df, - sheet_name, - ) - - console.print(f"Sentiment Analysis for {symbol} is {avg_polarity}\n") - - if graphic: - df["Title"] = df["Title"].apply( - lambda x: "
".join(textwrap.wrap(str(x), 50)) - ) - fig = OpenBBFigure( - title=f"Sentiment Score of {symbol}", - xaxis_title="Sentiment Score", - ) - fig.add_bar( - x=polarity_scores, - customdata=df["Title"], - hovertemplate="%{customdata}", - ) - fig.update_layout(hovermode="y") - - # remove y ticks of graph object because number based index doesn't make sense here - fig.layout.yaxis.tickvals = [] - - if fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "polarity_scores", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - return None diff --git a/openbb_terminal/common/behavioural_analysis/stocktwits_model.py b/openbb_terminal/common/behavioural_analysis/stocktwits_model.py deleted file mode 100644 index 58163f8d22e..00000000000 --- a/openbb_terminal/common/behavioural_analysis/stocktwits_model.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Stocktwits Model""" - -__docformat__ = "numpy" - -import logging -from typing import Any, Dict, List, Tuple - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_bullbear(symbol: str) -> Tuple[int, int, int, int]: - """Gets bullbear sentiment for ticker [Source: stocktwits]. - - Parameters - ---------- - symbol : str - Ticker symbol to look at - - Returns - ------- - Tuple[int, int, int, int] - Watchlist count, - Number of cases found for ticker, - Number of bullish statements, - Number of bearish statements, - """ - result = request(f"https://api.stocktwits.com/api/2/streams/symbol/{symbol}.json") - if result.status_code == 200: - result_json = result.json() - watchlist_count = result_json["symbol"]["watchlist_count"] - n_cases = 0 - n_bull = 0 - n_bear = 0 - for message in result_json["messages"]: - if message["entities"]["sentiment"]: - n_cases += 1 - n_bull += message["entities"]["sentiment"]["basic"] == "Bullish" - n_bear += message["entities"]["sentiment"]["basic"] == "Bearish" - - return watchlist_count, n_cases, n_bull, n_bear - return 0, 0, 0, 0 - - -@log_start_end(log=logger) -def get_messages(symbol: str, limit: int = 30) -> pd.DataFrame: - """Get last messages for a given ticker [Source: stocktwits]. - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number of messages to get - - Returns - ------- - pd.DataFrame - Dataframe of messages - """ - result = request(f"https://api.stocktwits.com/api/2/streams/symbol/{symbol}.json") - if result.status_code == 200: - return pd.DataFrame( - [message["body"] for message in result.json()["messages"][:limit]] - ) - - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_trending() -> pd.DataFrame: - """Get trending tickers from stocktwits [Source: stocktwits]. - - Returns - ------- - pd.DataFrame - Dataframe of trending tickers and watchlist count - """ - result = request("https://api.stocktwits.com/api/2/trending/symbols.json") - if result.status_code == 200: - l_symbols = [ - [symbol["symbol"], symbol["watchlist_count"], symbol["title"]] - for symbol in result.json()["symbols"] - ] - - df_trending = pd.DataFrame( - l_symbols, columns=["Ticker", "Watchlist Count", "Name"] - ) - return df_trending - - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_stalker(user: str, limit: int = 30) -> List[Dict[str, Any]]: - """Gets messages from given user [Source: stocktwits]. - - Parameters - ---------- - user : str - User to get posts for - limit : int, optional - Number of posts to get, by default 30 - - Returns - ------- - List[Dict[str, Any]] - List of posts - """ - result = request(f"https://api.stocktwits.com/api/2/streams/user/{user}.json") - if result.status_code == 200: - return list(result.json()["messages"][:limit]) - - return [] diff --git a/openbb_terminal/common/behavioural_analysis/stocktwits_view.py b/openbb_terminal/common/behavioural_analysis/stocktwits_view.py deleted file mode 100644 index a1102bc45b4..00000000000 --- a/openbb_terminal/common/behavioural_analysis/stocktwits_view.py +++ /dev/null @@ -1,102 +0,0 @@ -"""Stocktwits View""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.common.behavioural_analysis import stocktwits_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_bullbear(symbol: str): - """ - Print bullbear sentiment based on last 30 messages on the board. - Also prints the watchlist_count. [Source: Stocktwits] - - Parameters - ---------- - symbol: str - Stock ticker symbol - """ - watchlist_count, n_cases, n_bull, n_bear = stocktwits_model.get_bullbear(symbol) - console.print(f"[yellow]Watchlist count[/yellow]: {watchlist_count}") - if n_cases > 0: - console.print(f"\nLast {n_cases} sentiment messages:") - console.print(f"[green]Bullish:[/green] {round(100*n_bull/n_cases, 2)}%") - console.print(f"[red]Bearish:[/red] {round(100*n_bear/n_cases, 2)}%") - else: - console.print("No messages found") - - -@log_start_end(log=logger) -def display_messages(symbol: str, limit: int = 30): - """Prints up to 30 of the last messages on the board. [Source: Stocktwits]. - - Parameters - ---------- - symbol: str - Stock ticker symbol - limit: int - Number of messages to get - """ - messages = stocktwits_model.get_messages(symbol, limit) - - if not messages.empty: - print_rich_table( - messages, - headers=["MESSAGES"], - show_index=False, - title="Last Messages on Board", - ) - else: - console.print("No messages found in Stocktwits stream") - - -@log_start_end(log=logger) -def display_trending(): - """Show trensing stocks on stocktwits.""" - df_trending = stocktwits_model.get_trending() - print_rich_table( - df_trending, - headers=list(df_trending.columns), - show_index=False, - title="Trending Stocks", - ) - - -@log_start_end(log=logger) -def display_stalker(user: str, limit: int = 10): - """Show last posts for given user. - - Parameters - ---------- - user : str - Stocktwits username - limit : int, optional - Number of messages to show, by default 10 - """ - messages = stocktwits_model.get_stalker(user, limit) - - df_messages = pd.DataFrame.from_dict(messages) - - df_messages["created_at"] = pd.to_datetime(df_messages["created_at"]) - - df_messages = pd.DataFrame(df_messages, columns=["created_at", "id", "body", "url"]) - - # We look for a date name in the column to assume its a date on frontend side for filtering etc - df_messages.rename(columns={"created_at": "created_at_date"}, inplace=True) - - df_messages = df_messages.drop(["id", "url"], axis=1) - - print_rich_table( - df_messages, - show_index=False, - limit=limit, - ) diff --git a/openbb_terminal/common/biztoc_model.py b/openbb_terminal/common/biztoc_model.py deleted file mode 100644 index fa37192ee2f..00000000000 --- a/openbb_terminal/common/biztoc_model.py +++ /dev/null @@ -1,181 +0,0 @@ -""" BizToc Model """ - -__docformat__ = "numpy" - -import os - -import certifi -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -# Necessary for installer so that it can locate the correct certificates for -# API calls and https -# https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/73270162#73270162 -os.environ["REQUESTS_CA_BUNDLE"] = certifi.where() -os.environ["SSL_CERT_FILE"] = certifi.where() - - -def get_sources() -> pd.DataFrame: - """Get list of source ids to query for individual news articles via get_news. [Source: BizToc] - - Parameters - ---------- - - Returns - ------- - sources: pd.DataFrame - list of sources - """ - - req_headers = { - "X-RapidAPI-Key": get_current_user().credentials.API_BIZTOC_TOKEN, - "X-RapidAPI-Host": "biztoc.p.rapidapi.com", - } - - req = request( - "https://biztoc.p.rapidapi.com/sources", - headers=req_headers, - ) - - if hasattr(req, "status_code") and req.status_code == 200: - df = pd.DataFrame(req.json(), columns=["id", "title", "web"]) - elif hasattr(req, "status_code") and req.status_code != 200: - # If data request failed - console.print("[red]Status code not 200. Unable to retrieve data\n[/red]") - df = pd.DataFrame() - return df - - df = df.sort_values(by=["title"], ascending=True) - df = df[["id", "title", "web"]] - df.columns = ["id", "Title", "URL"] - return df - - -def get_tags() -> pd.DataFrame: - """Get list of trending tags to query for individual news articles via get_news. [Source: BizToc] - - Parameters - ---------- - - Returns - ------- - tags: pd.DataFrame - list of tags - """ - - req_headers = { - "X-RapidAPI-Key": get_current_user().credentials.API_BIZTOC_TOKEN, - "X-RapidAPI-Host": "biztoc.p.rapidapi.com", - } - - req = request( - "https://biztoc.p.rapidapi.com/tags/main", - headers=req_headers, - ) - - if hasattr(req, "status_code") and req.status_code == 200: - df = pd.DataFrame(req.json(), columns=["tag"]) - elif hasattr(req, "status_code") and req.status_code != 200: - # If data request failed - df = pd.DataFrame() - - return df - - -@check_api_key(["API_BIZTOC_TOKEN"]) -def get_news( - term: str = "", - tag: str = "", - source: str = "", - sort: str = "created", - limit: int = 100, - display_message: bool = True, -) -> pd.DataFrame: - """Get news for a given term and source. [Source: BizToc] - - Parameters - ---------- - term : str - term to search on the news articles - tag : str - display news articles for an individual tag - source: str - source to exclusively show news from - sort: str - the column to sort by - limit : int - number of articles to display - display_message: bool - whether to display a message to the user - - Returns - ------- - articles: pd.DataFrame - term to search on the news articles - """ - - have_data = False - - if display_message: - console.print("[yellow]Fetching news. Please be patient\n[/yellow]") - - req_headers = { - "X-RapidAPI-Key": get_current_user().credentials.API_BIZTOC_TOKEN, - "X-RapidAPI-Host": "biztoc.p.rapidapi.com", - } - df = pd.DataFrame() - while not have_data: - if term: - req = request( - # term=quote(term) - "https://biztoc.p.rapidapi.com/search", - params={"q": term}, - headers=req_headers, - ) - elif tag: - req = request( - f"https://biztoc.p.rapidapi.com/tag/{tag}", - headers=req_headers, - ) - elif source: - req = request( - f"https://biztoc.p.rapidapi.com/news/source/{source}", - headers=req_headers, - ) - else: - req = request( - "https://biztoc.p.rapidapi.com/news/latest/main", headers=req_headers - ) - - if hasattr(req, "status_code") and req.status_code == 200: - have_data = True - elif ( - hasattr(req, "status_code") and req.status_code != 200 - ): # If data request failed - console.print("[red]Status code not 200. Unable to retrieve data\n[/red]") - break - - df = pd.DataFrame(req.json(), columns=["title", "body", "url", "created"]) - if df.empty: - return df - df["created"] = pd.to_datetime(df["created"]) - - df = df.sort_values(by=[sort], ascending=False) - df = df[["created", "title", "body", "url"]] - df.columns = ["Date", "Title", "Body", "URL"] - df = df[:limit] - return df - - -# Define for auto-completion -try: - BIZTOC_TAGS = get_tags().tag.tolist() - - -except Exception: - # If key not defined or there is an issue - BIZTOC_TAGS = [] diff --git a/openbb_terminal/common/biztoc_view.py b/openbb_terminal/common/biztoc_view.py deleted file mode 100644 index f9a1fa02552..00000000000 --- a/openbb_terminal/common/biztoc_view.py +++ /dev/null @@ -1,99 +0,0 @@ -""" BizToc View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.common import biztoc_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@check_api_key(["API_BIZTOC_TOKEN"]) -@log_start_end(log=logger) -def display_news( - term: str = "", - tag: str = "", - source: str = "", - limit: int = 100, - export: str = "", - sheet_name: Optional[str] = None, - sort: str = "created", -): - """Plots news for a given term and source. [Source: Feedparser] - - Parameters - ---------- - term : str - term to search on the news articles - tag : str - display news articles for an individual tag - source : str - source to exclusively show news from - limit : int - number of articles to display - export : str - Export dataframe data to csv,json,xlsx file - sort: str - the column to sort by - """ - df = biztoc_model.get_news(term, tag, source, sort, limit) - - if not df.empty: - print_rich_table(df, title="BizToc News - articles", export=bool(export)) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"news_biztoc_{'_'.join(term)}_{'_'.join(source)}", - df, - sheet_name, - ) - - -@check_api_key(["API_BIZTOC_TOKEN"]) -def display_sources( - export: str = "", - sheet_name: Optional[str] = None, -): - """Plots available sources for news.""" - - df = biztoc_model.get_sources() - - if not df.empty: - print_rich_table(df, title="BizToc News - sources", export=bool(export)) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "news_biztoc_sources", - df, - sheet_name, - ) - - -@check_api_key(["API_BIZTOC_TOKEN"]) -def display_tags( - export: str = "", - sheet_name: Optional[str] = None, -): - """Plots currently trending tags.""" - - df = biztoc_model.get_tags() - - if not df.empty: - print_rich_table( - df, title="BizToc News - list of trending tags", export=bool(export) - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "news_biztoc_tags", - df, - sheet_name, - ) diff --git a/openbb_terminal/common/common_model.py b/openbb_terminal/common/common_model.py deleted file mode 100644 index 770989b0b3a..00000000000 --- a/openbb_terminal/common/common_model.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Common Model""" - -__docformat__ = "numpy" - -import logging -from pathlib import Path -from typing import Any, Dict, Optional - -import pandas as pd -import statsmodels.api as sm -from linearmodels.datasets import wage_panel -from pandas import errors - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -DATA_EXAMPLES: Dict[str, str] = { - "anes96": "American National Election Survey 1996", - "cancer": "Breast Cancer Data", - "ccard": "Bill Greene’s credit scoring data.", - "cancer_china": "Smoking and lung cancer in eight cities in China.", - "co2": "Mauna Loa Weekly Atmospheric CO2 Data", - "committee": "First 100 days of the US House of Representatives 1995", - "copper": "World Copper Market 1951-1975 Dataset", - "cpunish": "US Capital Punishment dataset.", - "danish_data": "Danish Money Demand Data", - "elnino": "El Nino - Sea Surface Temperatures", - "engel": "Engel (1857) food expenditure data", - "fair": "Affairs dataset", - "fertility": "World Bank Fertility Data", - "grunfeld": "Grunfeld (1950) Investment Data", - "heart": "Transplant Survival Data", - "interest_inflation": "(West) German interest and inflation rate 1972-1998", - "longley": "Longley dataset", - "macrodata": "United States Macroeconomic data", - "modechoice": "Travel Mode Choice", - "nile": "Nile River flows at Ashwan 1871-1970", - "randhie": "RAND Health Insurance Experiment Data", - "scotland": "Taxation Powers Vote for the Scottish Parliament 1997", - "spector": "Spector and Mazzeo (1980) - Program Effectiveness Data", - "stackloss": "Stack loss data", - "star98": "Star98 Educational Dataset", - "statecrim": "Statewide Crime Data 2009", - "strikes": "U.S. Strike Duration Data", - "sunspots": "Yearly sunspots data 1700-2008", - "wage_panel": "Veila and M. Verbeek (1998): Whose Wages Do Unions Raise?", -} - -file_types = ["xlsx", "csv"] - - -@log_start_end(log=logger) -def load( - file: str, - sheet_name: Optional[str] = None, - data_files: Optional[Dict[Any, Any]] = None, - data_examples: Optional[Dict[Any, Any]] = None, -) -> pd.DataFrame: - """Load custom file into dataframe. - - Parameters - ---------- - file: str - Path to file - data_files: dict - Contains all available data files within the Export folder - data_examples: dict - Contains all available examples from Statsmodels - - Returns - ------- - pd.DataFrame - Dataframe with custom data - """ - if data_files is None: - data_files = {} - if data_examples is None: - data_examples = DATA_EXAMPLES - if file in data_examples: - if file == "wage_panel": - return wage_panel.load() - return getattr(sm.datasets, file).load_pandas().data - - full_file = data_files[file] if file in data_files else file - - if not Path(full_file).exists(): - console.print(f"[red]Cannot find the file {full_file}[/red]\n") - return pd.DataFrame() - - file_type = Path(full_file).suffix - - try: - if file_type == ".xlsx": - try: - data = ( - pd.read_excel(full_file) - if sheet_name is None - else pd.read_excel(full_file, sheet_name=sheet_name) - ) - except ValueError as err: - console.print(f"[red]{err}[/red]\n") - return pd.DataFrame() - elif file_type == ".csv": - data = pd.read_csv(full_file) - else: - console.print( - f"The file type {file_type} is not supported. Use .xlsx or .csv." - ) - return pd.DataFrame() - except errors.ParserError: - console.print("[red]The given file is not properly formatted.[/red]\b") - return pd.DataFrame() - except errors.EmptyDataError: - console.print("[red]The given file is empty.[/red]\b") - return pd.DataFrame() - - if data is None: - return pd.DataFrame() - if not data.empty: - data.columns = [x.replace("/", "_") for x in data.columns] - return data diff --git a/openbb_terminal/common/feedparser_model.py b/openbb_terminal/common/feedparser_model.py deleted file mode 100644 index 190a2341c8a..00000000000 --- a/openbb_terminal/common/feedparser_model.py +++ /dev/null @@ -1,113 +0,0 @@ -""" Feedparser Model """ - -__docformat__ = "numpy" - -import os -from typing import List -from urllib.parse import quote - -import certifi -import feedparser -import pandas as pd - -from openbb_terminal.rich_config import console - - -def get_news( - term: str = "", - sources: str = "", - sort: str = "published", - limit: int = 10, - display_message: bool = True, -) -> pd.DataFrame: - """Get news for a given term and source. [Source: Feedparser] - - Parameters - ---------- - term : str - term to search on the news articles - sources: str - sources to exclusively show news from (separated by commas) - sort: str - the column to sort by - limit : int - number of articles to display - display_message: bool - whether to display a message to the user - - Returns - ------- - articles: pd.DataFrame - term to search on the news articles - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.news() - """ - # Necessary for installer so that it can locate the correct certificates for - # API calls and https - # https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/73270162#73270162 - os.environ["REQUESTS_CA_BUNDLE"] = certifi.where() - os.environ["SSL_CERT_FILE"] = certifi.where() - - have_data = False - if display_message: - console.print("[yellow]Fetching data. Please be patient\n[/yellow]") - n = 0 - - while not have_data: - if term: - term = quote(term) - data = feedparser.parse( - f"https://news.google.com/rss/search?q={term}&hl=en-US&gl=US&ceid=US:en&when:24h+allinurl" - f':{sources.replace(" ", "%20")}' - ) - else: - data = feedparser.parse( - f'https://news.google.com/rss/search?q=when:24h+allinurl:{sources.replace(" ", "%20")}' - "&hl=en-US&gl=US&ceid=US:en" - ) - - if ( - hasattr(data, "status") and data.status == 200 - ): # Checking if data has status attribute and if data request succeeded - if data.entries: - have_data = True - elif n == 60: # Breaking if 60 successful requests return no data - console.print("[red]Timeout occurred. Please try again\n[/red]") - break - n += 1 - - elif hasattr(data, "status") and data.status != 200: # If data request failed - console.print("[red]Status code not 200. Unable to retrieve data\n[/red]") - break - else: - console.print("[red]Could not retrieve data\n[/red]") - break - - # Filter based on data sources - if sources: - newdata: List = [] - for entry in list(data.entries): - # check if sources specified - if "," in sources: - if entry["source"]["title"].lower().find(sources.lower()) != -1: - newdata.append(entry) - else: - for s in sources.split(","): - if entry["source"]["title"].lower().find(s.lower()) != -1: - newdata.append(entry) - - if newdata: - df = pd.DataFrame(newdata, columns=["title", "link", "published"]) - else: - return pd.DataFrame() - else: - df = pd.DataFrame(data.entries, columns=["title", "link", "published"]) - df["published"] = pd.to_datetime(df["published"]) - df = df.sort_values(by=[sort], ascending=False) - df = df[["published", "title", "link"]] - df.columns = ["Date", "Description", "URL"] - df = df[:limit] - return df diff --git a/openbb_terminal/common/feedparser_view.py b/openbb_terminal/common/feedparser_view.py deleted file mode 100644 index bcd608f6724..00000000000 --- a/openbb_terminal/common/feedparser_view.py +++ /dev/null @@ -1,56 +0,0 @@ -""" News View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal.common import feedparser_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_news( - term: str, - sources: str = "", - limit: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - sort: str = "published", -): - """Plots news for a given term and source. [Source: Feedparser] - - Parameters - ---------- - term : str - term to search on the news articles - sources : str - sources to exclusively show news from - limit : int - number of articles to display - export : str - Export dataframe data to csv,json,xlsx file - sort: str - the column to sort by - """ - df = feedparser_model.get_news(term, sources, sort, limit) - - if not df.empty: - # For table - convert from Timestamp to string - df["Date"] = pd.to_datetime(df["Date"], utc=True) - df["Date"] = df["Date"].dt.strftime("%Y-%m-%d %H:%M:%S") - print_rich_table(df, title="News - articles", export=bool(export)) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"news_{'_'.join(term)}_{'_'.join(sources)}", - df, - sheet_name, - ) diff --git a/openbb_terminal/common/news_sdk_helper.py b/openbb_terminal/common/news_sdk_helper.py deleted file mode 100644 index 8e1346e2029..00000000000 --- a/openbb_terminal/common/news_sdk_helper.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Helper Function for the openbb sdk function openbb.news()""" - -import pandas as pd - -from openbb_terminal.common import biztoc_model, feedparser_model -from openbb_terminal.core.session.current_user import get_current_user - - -def news(term: str = "", sources: str = "", tag="", source="") -> pd.DataFrame: - """Access news from either feedparser or biztoc for a given term or from specified sources - - Parameters - ---------- - term : str, optional - Term to sort for, by default "" - sources : str, optional - News sources to include, by default "" - tag : str, optional - Biztoc only selection for searching by a given tag, by default "" - source : str, optional - Data provider, can be either FeedParser or BizToc. Will default to Biztoc if key is provided - - Returns - ------- - pd.DataFrame - DataFrame of news - """ - if source: - provider = source.lower() - else: - # Use biztoc if token is provided - provider = "feedparser" - biztoc_token = get_current_user().credentials.API_BIZTOC_TOKEN - if biztoc_token != "REPLACE_ME": # noqa: S105 - provider = "biztoc" - - if provider == "feedparser": - return feedparser_model.get_news( - term=term, sources=sources, display_message=False - ) - if provider == "biztoc": - return biztoc_model.get_news( - term=term, tag=tag, source=source, display_message=False - ) - return pd.DataFrame() diff --git a/openbb_terminal/common/newsapi_model.py b/openbb_terminal/common/newsapi_model.py deleted file mode 100644 index 9e85aa06657..00000000000 --- a/openbb_terminal/common/newsapi_model.py +++ /dev/null @@ -1,88 +0,0 @@ -""" News Model """ - -__docformat__ = "numpy" - -import logging -from datetime import datetime, timedelta -from typing import Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_NEWS_TOKEN"]) -def get_news( - query: str, - limit: int = 10, - start_date: Optional[str] = None, - show_newest: bool = True, - sources: str = "", -) -> pd.DataFrame: - """Get news for a given term. [Source: NewsAPI] - - Parameters - ---------- - query : str - term to search on the news articles - start_date: Optional[str] - date to start searching articles from formatted YYYY-MM-DD - show_newest: bool - flag to show newest articles first - sources: str - sources to exclusively show news from (comma separated) - - Returns - ------- - pd.DataFrame - DataFrame with columns Date, Description, URL - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d") - - link = ( - f"https://newsapi.org/v2/everything?q={query}&from={start_date}&sortBy=publishedAt" - "&language=en" - ) - - if sources: - if "," in sources: - sources = ".com,".join(sources.split(",")) - link += f"&domains={sources}.com" - link += f"&apiKey={get_current_user().credentials.API_NEWS_TOKEN}" - response = request(link) - articles = {} - - if response.status_code == 200: - response_json = response.json() - articles = ( - response_json["articles"] - if show_newest - else response_json["articles"][::-1] - ) - elif response.status_code == 426: - console.print(f"Error in request: {response.json()['message']}", "\n") - elif response.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - elif response.status_code == 429: - console.print("[red]Exceeded number of calls per minute[/red]\n") - else: - console.print(f"Error in request: {response.json()['message']}", "\n") - - if articles: - df = pd.DataFrame(articles) - df["publishedAt"] = pd.to_datetime(df["publishedAt"]) - df["publishedAt"].apply(lambda x: x.strftime("%Y-%m-%d %H:%M:%S")) - df = df[["publishedAt", "description", "url"]] - df.columns = ["Date", "Description", "URL"] - df = df[:limit] - return df - - return pd.DataFrame() diff --git a/openbb_terminal/common/newsapi_view.py b/openbb_terminal/common/newsapi_view.py deleted file mode 100644 index a03f34f19a3..00000000000 --- a/openbb_terminal/common/newsapi_view.py +++ /dev/null @@ -1,54 +0,0 @@ -""" News View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.common import newsapi_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_NEWS_TOKEN"]) -def display_news( - query: str, - limit: int = 10, - start_date: Optional[str] = None, - show_newest: bool = True, - sources: str = "", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing news for a given term. [Source: NewsAPI] - - Parameters - ---------- - query : str - term to search on the news articles - start_date: Optional[str] - date to start searching articles from formatted YYYY-MM-DD - limit : int - number of articles to display - show_newest: bool - flag to show newest articles first - sources: str - sources to exclusively show news from - export : str - Export dataframe data to csv,json,xlsx file - """ - df = newsapi_model.get_news(query, limit, start_date, show_newest, sources) - if not df.empty: - print_rich_table(df, title="News - articles", export=bool(export)) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"news_{query}_{'_'.join(sources)}", - df, - sheet_name, - ) diff --git a/openbb_terminal/common/quantitative_analysis/__init__.py b/openbb_terminal/common/quantitative_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/common/quantitative_analysis/qa_model.py b/openbb_terminal/common/quantitative_analysis/qa_model.py deleted file mode 100644 index c02aea728b9..00000000000 --- a/openbb_terminal/common/quantitative_analysis/qa_model.py +++ /dev/null @@ -1,660 +0,0 @@ -"""Quantitative Analysis Model""" - -__docformat__ = "numpy" - -import logging -import warnings -from typing import Tuple, Union - -import numpy as np -import pandas as pd -import statsmodels.api as sm -from scipy import stats -from statsmodels.tools.sm_exceptions import MissingDataError -from statsmodels.tsa.seasonal import DecomposeResult, seasonal_decompose -from statsmodels.tsa.stattools import adfuller, kpss - -from openbb_terminal.decorators import log_start_end - -# TODO : Since these are common/ they should be independent of 'stock' info. -# df_stock should be replaced with a generic df and a column variable - - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_summary(data: pd.DataFrame) -> pd.DataFrame: - """Print summary statistics - - Parameters - ---------- - data : pd.DataFrame - Dataframe to get summary statistics for - - Returns - ------- - summary : pd.DataFrame - Summary statistics - """ - - df_stats = data.describe(percentiles=[0.1, 0.25, 0.5, 0.75, 0.9]) - df_stats.loc["var"] = df_stats.loc["std"] ** 2 - - return df_stats - - -@log_start_end(log=logger) -def get_seasonal_decomposition( - data: pd.DataFrame, multiplicative: bool = False -) -> Tuple[DecomposeResult, pd.DataFrame, pd.DataFrame]: - """Perform seasonal decomposition - - Parameters - ---------- - data : pd.DataFrame - Dataframe of targeted data - multiplicative : bool - Boolean to indicate multiplication instead of addition - - Returns - ------- - Tuple[DecomposeResult, pd.DataFrame, pd.DataFrame] - DecomposeResult class from statsmodels (observed, seasonal, trend, residual, and weights), - Filtered cycle DataFrame, - Filtered trend DataFrame - """ - seasonal_periods = 5 - # Hodrick-Prescott filter - # See Ravn and Uhlig: http://home.uchicago.edu/~huhlig/papers/uhlig.ravn.res.2002.pdf - lamb = 107360000000 - - model = ["additive", "multiplicative"][multiplicative] - - result = seasonal_decompose(data, model=model, period=seasonal_periods) - cycle, trend = sm.tsa.filters.hpfilter( - result.trend[result.trend.notna().values], lamb=lamb - ) - - return result, pd.DataFrame(cycle), pd.DataFrame(trend) - - -@log_start_end(log=logger) -def get_normality(data: pd.DataFrame) -> pd.DataFrame: - """ - Look at the distribution of returns and generate statistics on the relation to the normal curve. - This function calculates skew and kurtosis (the third and fourth moments) and performs both - a Jarque-Bera and Shapiro Wilk test to determine if data is normally distributed. - - Parameters - ---------- - data : pd.DataFrame - Dataframe of targeted data - - Returns - ------- - pd.DataFrame - Dataframe containing statistics of normality - """ - # Kurtosis - # Measures height and sharpness of the central peak relative to that of a standard bell curve - k, kpval = stats.kurtosistest(data) - - # Skewness - # Measure of the asymmetry of the probability distribution of a random variable about its mean - s, spval = stats.skewtest(data) - - # Jarque-Bera goodness of fit test on sample data - # Tests if the sample data has the skewness and kurtosis matching a normal distribution - jb, jbpval = stats.jarque_bera(data) - - # Shapiro - # The Shapiro-Wilk test tests the null hypothesis that the data was drawn from a normal distribution. - sh, shpval = stats.shapiro(data) - - # Kolmogorov-Smirnov - # The one-sample test compares the underlying distribution F(x) of a sample against a given distribution G(x). - # Comparing to normal here. - ks, kspval = stats.kstest(data, "norm") - - l_statistic = [k, s, jb, sh, ks] - l_pvalue = [kpval, spval, jbpval, shpval, kspval] - - return pd.DataFrame( - [l_statistic, l_pvalue], - columns=[ - "Kurtosis", - "Skewness", - "Jarque-Bera", - "Shapiro-Wilk", - "Kolmogorov-Smirnov", - ], - index=["Statistic", "p-value"], - ) - - -@log_start_end(log=logger) -def get_unitroot( - data: pd.DataFrame, fuller_reg: str = "c", kpss_reg: str = "c" -) -> pd.DataFrame: - """Calculate test statistics for unit roots - - Parameters - ---------- - data : pd.DataFrame - DataFrame of target variable - fuller_reg : str - Type of regression of ADF test. Can be ‘c’,’ct’,’ctt’,’nc’ 'c' - Constant and t - trend order - kpss_reg : str - Type of regression for KPSS test. Can be ‘c’,’ct' - - Returns - ------- - pd.DataFrame - Dataframe with results of ADF test and KPSS test - """ - # The Augmented Dickey-Fuller test - # Used to test for a unit root in a univariate process in the presence of serial correlation. - try: - result = adfuller(data, regression=fuller_reg) - except MissingDataError: - data = data.dropna(axis=0) - result = adfuller(data, regression=fuller_reg) - cols = ["Test Statistic", "P-Value", "NLags", "Nobs", "ICBest"] - vals = [result[0], result[1], result[2], result[3], result[5]] - data = pd.DataFrame(data=vals, index=cols, columns=["ADF"]) - - # Kwiatkowski-Phillips-Schmidt-Shin test - # Test for level or trend stationarity - # This test seems to produce an Interpolation Error which says - # The test statistic is outside of the range of p-values available in the - # look-up table. The actual p-value is greater than the p-value returned. - # Wrap this in catch_warnings to prevent - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - try: - res2 = kpss(data, regression=kpss_reg, nlags="auto") - except ValueError: - return pd.DataFrame() - vals2 = [res2[0], res2[1], res2[2], "", ""] - data["KPSS"] = vals2 - return data - - -def calculate_adjusted_var( - kurtosis: float, skew: float, ndp: float, std: float, mean: float -) -> float: - """Calculates VaR, which is adjusted for skew and kurtosis (Cornish-Fischer-Expansion) - - Parameters - ---------- - kurtosis: float - kurtosis of data - skew: float - skew of data - ndp: float - normal distribution percentage number (99% -> -2.326) - std: float - standard deviation of data - mean: float - mean of data - - Returns - ------- - float - Real adjusted VaR - """ - - # Derived from Cornish-Fisher-Expansion - # Formula for quantile from "Finance Compact Plus" by Zimmerman; Part 1, page 130-131 - # More material/resources: - # - "Numerical Methods and Optimization in Finance" by Gilli, Maringer & Schumann; - # - https://www.value-at-risk.net/the-cornish-fisher-expansion/; - # - https://www.diva-portal.org/smash/get/diva2:442078/FULLTEXT01.pdf, Section 2.4.2, p.18; - # - "Risk Management and Financial Institutions" by John C. Hull - - skew_component = skew / 6 * (ndp**2 - 1) ** 2 - skew**2 / 36 * ndp * ( - 2 * ndp**2 - 5 - ) - kurtosis_component = (kurtosis - 3) / 24 * ndp * (ndp**2 - 3) - quantile = ndp + skew_component + kurtosis_component - log_return = mean + quantile * std - real_return = 2.7182818**log_return - 1 - return real_return - - -def get_var( - data: pd.DataFrame, - use_mean: bool = False, - adjusted_var: bool = False, - student_t: bool = False, - percentile: Union[int, float] = 99.9, - portfolio: bool = False, -) -> pd.DataFrame: - """Gets value at risk for specified stock dataframe. - - Parameters - ---------- - data: pd.DataFrame - Data dataframe - use_mean: bool - If one should use the data mean for calculation - adjusted_var: bool - If one should return VaR adjusted for skew and kurtosis - student_t: bool - If one should use the student-t distribution - percentile: Union[int,float] - VaR percentile - portfolio: bool - If the data is a portfolio - - Returns - ------- - pd.DataFrame - DataFrame with Value at Risk per percentile - """ - if not portfolio: - data = data[["adjclose"]].copy() - data.loc[:, "return"] = data.adjclose.pct_change() - data_return = data["return"] - else: - data = data[1:].copy() - data_return = data - - # Distribution percentages - percentile = percentile / 100 - - percentile_90 = -1.282 - percentile_95 = -1.645 - percentile_99 = -2.326 - percentile_custom = stats.norm.ppf(1 - percentile) - - # Mean - mean = data_return.mean() if use_mean else 0 - - # Standard Deviation - std = data_return.std(axis=0) - - if adjusted_var: - # Kurtosis - # Measures height and sharpness of the central peak relative to that of a standard bell curve - k = data_return.kurtosis(axis=0) - - # Skewness - # Measure of the asymmetry of the probability distribution of a random variable about its mean - s = data_return.skew(axis=0) - - # Adjusted VaR - var_90 = calculate_adjusted_var(k, s, percentile_90, std, mean) - var_95 = calculate_adjusted_var(k, s, percentile_95, std, mean) - var_99 = calculate_adjusted_var(k, s, percentile_99, std, mean) - var_custom = calculate_adjusted_var(k, s, percentile_custom, std, mean) - - elif student_t: - # Calculating VaR based on the Student-t distribution - - # Fitting student-t parameters to the data - v, _, _ = stats.t.fit(data_return.fillna(0)) - if not use_mean: - mean = 0 - var_90 = np.sqrt((v - 2) / v) * stats.t.ppf(0.1, v) * std + mean - var_95 = np.sqrt((v - 2) / v) * stats.t.ppf(0.05, v) * std + mean - var_99 = np.sqrt((v - 2) / v) * stats.t.ppf(0.01, v) * std + mean - var_custom = np.sqrt((v - 2) / v) * stats.t.ppf(1 - percentile, v) * std + mean - - else: - # Regular Var - var_90 = mean + percentile_90 * std - var_95 = mean + percentile_95 * std - var_99 = mean + percentile_99 * std - var_custom = mean + percentile_custom * std - - if not portfolio: - data.sort_values("return", inplace=True, ascending=True) - data_return = data["return"] - else: - data.sort_values(inplace=True, ascending=True) - data_return = data - - # Historical VaR - hist_var_90 = data_return.quantile(0.1) - hist_var_95 = data_return.quantile(0.05) - hist_var_99 = data_return.quantile(0.01) - hist_var_custom = data_return.quantile(1 - percentile) - - var_list = [var_90 * 100, var_95 * 100, var_99 * 100, var_custom * 100] - hist_var_list = [ - hist_var_90 * 100, - hist_var_95 * 100, - hist_var_99 * 100, - hist_var_custom * 100, - ] - - str_hist_label = "Historical VaR [%]" - - if adjusted_var: - str_var_label = "Adjusted VaR [%]" - elif student_t: - str_var_label = "Student-t VaR [%]" - else: - str_var_label = "Gaussian VaR [%]" - - data_dictionary = {str_var_label: var_list, str_hist_label: hist_var_list} - df = pd.DataFrame( - data_dictionary, index=["90.0%", "95.0%", "99.0%", f"{percentile*100}%"] - ) - - df.sort_index(inplace=True) - df = df.replace(np.nan, "-") - - return df - - -def get_es( - data: pd.DataFrame, - use_mean: bool = False, - distribution: str = "normal", - percentile: Union[float, int] = 99.9, - portfolio: bool = False, -) -> pd.DataFrame: - """Gets Expected Shortfall for specified stock dataframe. - - Parameters - ---------- - data: pd.DataFrame - Data dataframe - use_mean: bool - If one should use the data mean for calculation - distribution: str - Type of distribution, options: laplace, student_t, normal - percentile: Union[float,int] - VaR percentile - portfolio: bool - If the data is a portfolio - - Returns - ------- - pd.DataFrame - DataFrame with Expected Shortfall per percentile - """ - if not portfolio: - data = data[["adjclose"]].copy() - data.loc[:, "return"] = data.adjclose.pct_change() - data_return = data["return"] - else: - data = data[1:].copy() - data_return = data - - # Distribution percentages - percentile = percentile / 100 - - percentile_90 = -1.282 - percentile_95 = -1.645 - percentile_99 = -2.326 - percentile_custom = stats.norm.ppf(1 - percentile) - - # Mean - mean = data_return.mean() if use_mean else 0 - - # Standard Deviation - std = data_return.std(axis=0) - - if distribution == "laplace": - # Calculating ES based on Laplace distribution - # For formula see: https://en.wikipedia.org/wiki/Expected_shortfall#Laplace_distribution - - # Fitting b (scale parameter) to the variance of the data - # Since variance of the Laplace dist.: var = 2*b**2 - # Thus: - b = np.sqrt(std**2 / 2) - - # Calculation - es_90 = -b * (1 - np.log(2 * 0.1)) + mean - es_95 = -b * (1 - np.log(2 * 0.05)) + mean - es_99 = -b * (1 - np.log(2 * 0.01)) + mean - - es_custom = ( - -b * (1 - np.log(2 * (1 - percentile))) + mean - if 1 - percentile < 0.5 - else 0 - ) - - elif distribution == "student_t": - # Calculating ES based on the Student-t distribution - - # Fitting student-t parameters to the data - v, _, scale = stats.t.fit(data_return.fillna(0)) - if not use_mean: - mean = 0 - - # Student T Distribution percentages - percentile_90 = stats.t.ppf(0.1, v) - percentile_95 = stats.t.ppf(0.05, v) - percentile_99 = stats.t.ppf(0.01, v) - percentile_custom = stats.t.ppf(1 - percentile, v) - - # Calculation - es_90 = ( - -scale - * (v + percentile_90**2) - / (v - 1) - * stats.t.pdf(percentile_90, v) - / 0.1 - + mean - ) - es_95 = ( - -scale - * (v + percentile_95**2) - / (v - 1) - * stats.t.pdf(percentile_95, v) - / 0.05 - + mean - ) - es_99 = ( - -scale - * (v + percentile_99**2) - / (v - 1) - * stats.t.pdf(percentile_99, v) - / 0.01 - + mean - ) - es_custom = ( - -scale - * (v + percentile_custom**2) - / (v - 1) - * stats.t.pdf(percentile_custom, v) - / (1 - percentile) - + mean - ) - - elif distribution == "logistic": - # Logistic distribution - # For formula see: https://en.wikipedia.org/wiki/Expected_shortfall#Logistic_distribution - - # Fitting s (scale parameter) to the variance of the data - # Since variance of the Logistic dist.: var = s**2*pi**2/3 - # Thus: - s = np.sqrt(3 * std**2 / np.pi**2) - - # Calculation - a = 1 - percentile - es_90 = -s * np.log((0.9 ** (1 - 1 / 0.1)) / 0.1) + mean - es_95 = -s * np.log((0.95 ** (1 - 1 / 0.05)) / 0.05) + mean - es_99 = -s * np.log((0.99 ** (1 - 1 / 0.01)) / 0.01) + mean - es_custom = -s * np.log((percentile ** (1 - 1 / a)) / a) + mean - - else: - # Regular Expected Shortfall - es_90 = std * -stats.norm.pdf(percentile_90) / 0.1 + mean - es_95 = std * -stats.norm.pdf(percentile_95) / 0.05 + mean - es_99 = std * -stats.norm.pdf(percentile_99) / 0.01 + mean - es_custom = std * -stats.norm.pdf(percentile_custom) / (1 - percentile) + mean - - # Historical Expected Shortfall - df = get_var(data, use_mean, False, False, percentile, portfolio) - - hist_var_list = list(df["Historical VaR [%]"].values) - - hist_es_90 = data_return[data_return <= hist_var_list[0]].mean() - hist_es_95 = data_return[data_return <= hist_var_list[1]].mean() - hist_es_99 = data_return[data_return <= hist_var_list[2]].mean() - hist_es_custom = data_return[data_return <= hist_var_list[3]].mean() - - es_list = [es_90 * 100, es_95 * 100, es_99 * 100, es_custom * 100] - hist_es_list = [ - hist_es_90 * 100, - hist_es_95 * 100, - hist_es_99 * 100, - hist_es_custom * 100, - ] - - str_hist_label = "Historical ES [%]" - - if distribution == "laplace": - str_es_label = "Laplace ES [%]" - elif distribution == "student_t": - str_es_label = "Student-t ES [%]" - elif distribution == "logistic": - str_es_label = "Logistic ES [%]" - else: - str_es_label = "ES [%]" - - data_dictionary = {str_es_label: es_list, str_hist_label: hist_es_list} - df = pd.DataFrame( - data_dictionary, index=["90.0%", "95.0%", "99.0%", f"{percentile*100}%"] - ) - - df.sort_index(inplace=True) - df = df.replace(np.nan, "-") - - return df - - -def get_sharpe(data: pd.DataFrame, rfr: float = 0, window: float = 252) -> pd.DataFrame: - """Calculates the sharpe ratio - - Parameters - ---------- - data: pd.DataFrame - selected dataframe column - rfr: float - risk free rate - window: float - length of the rolling window - - Returns - ------- - sharpe: pd.DataFrame - sharpe ratio - """ - data_return = data.pct_change().rolling(window).sum() * 100 - std = data.rolling(window).std() / np.sqrt(252) * 100 - - sharpe = (data_return - rfr) / std - - return sharpe - - -def get_sortino( - data: pd.DataFrame, - target_return: float = 0, - window: float = 252, - adjusted: bool = False, -) -> pd.DataFrame: - """Calculates the sortino ratio - - Parameters - ---------- - data: pd.DataFrame - selected dataframe - target_return: float - target return of the asset - window: float - length of the rolling window - adjusted: bool - adjust the sortino ratio - - Returns - ------- - sortino: pd.DataFrame - sortino ratio - """ - data = data * 100 - - # Sortino Ratio - # For method & terminology see: - # http://www.redrockcapital.com/Sortino__A__Sharper__Ratio_Red_Rock_Capital.pdf - - target_downside_deviation = data.rolling(window).apply( - lambda x: (x.values[x.values < 0]).std() / np.sqrt(252) * 100 - ) - data_return = data.rolling(window).sum() - sortino_ratio = (data_return - target_return) / target_downside_deviation - - if adjusted: - # Adjusting the sortino ratio inorder to compare it to sharpe ratio - # Thus if the deviation is neutral then it's equal to the sharpe ratio - sortino_ratio = sortino_ratio / np.sqrt(2) - - return sortino_ratio - - -def get_omega_ratio(data: pd.DataFrame, threshold: float = 0) -> float: - """Calculates the omega ratio - - Parameters - ---------- - data: pd.DataFrame - selected dataframe - threshold: float - target return threshold - - Returns - ------- - omega_ratio: float - omega ratio - """ - # Omega ratio; for more information and explanation see: - # https://en.wikipedia.org/wiki/Omega_ratio - - # Calculating daily threshold from annualised threshold value - daily_threshold = (threshold + 1) ** np.sqrt(1 / 252) - 1 - - # Get excess return - data_excess = data - daily_threshold - - # Values excess return - data_positive_sum = data_excess[data_excess > 0].sum() - data_negative_sum = data_excess[data_excess < 0].sum() - - omega_ratio = data_positive_sum / (-data_negative_sum) - - return omega_ratio - - -def get_omega( - data: pd.DataFrame, threshold_start: float = 0, threshold_end: float = 1.5 -) -> pd.DataFrame: - """Get the omega series - - Parameters - ---------- - data: pd.DataFrame - stock dataframe - threshold_start: float - annualized target return threshold start of plotted threshold range - threshold_end: float - annualized target return threshold end of plotted threshold range - - Returns - ------- - omega: pd.DataFrame - omega series - """ - threshold = np.linspace(threshold_start, threshold_end, 50) - df = pd.DataFrame(threshold, columns=["threshold"]) - - omega_list = [get_omega_ratio(data, i) for i in threshold] - df["omega"] = omega_list - - return df diff --git a/openbb_terminal/common/quantitative_analysis/qa_view.py b/openbb_terminal/common/quantitative_analysis/qa_view.py deleted file mode 100644 index cf24eea51aa..00000000000 --- a/openbb_terminal/common/quantitative_analysis/qa_view.py +++ /dev/null @@ -1,1199 +0,0 @@ -"""Quantitative Analysis View""" - -__docformat__ = "numpy" - -# pylint: disable=C0302 - - -import logging -import os -import warnings -from datetime import datetime -from typing import Any, Dict, List, Optional, Union - -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -from detecta import detect_cusum -from pandas.plotting import register_matplotlib_converters -from scipy import stats -from statsmodels.graphics.gofplots import qqplot - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.common.quantitative_analysis import qa_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -register_matplotlib_converters() - - -def lambda_color_red(val: Any) -> str: - """Adds red to dataframe value""" - if val > 0.05: - return f"[red]{round(val,4)}[/red]" - return round(val, 4) - - -@log_start_end(log=logger) -def display_summary( - data: pd.DataFrame, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing summary statistics - - Parameters - ---------- - data : pd.DataFrame - DataFrame to get statistics of - export : str - Format to export data - """ - summary = qa_model.get_summary(data) - - print_rich_table( - summary, - headers=list(summary.columns), - floatfmt=".3f", - show_index=True, - title="[bold]Summary Statistics[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "summary", - summary, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_hist( - data: pd.DataFrame, - target: str, - symbol: str = "", - bins: int = 15, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots histogram of data - - Parameters - ---------- - data : pd.DataFrame - Dataframe to look at - target : str - Data column to get histogram of the dataframe - symbol : str - Name of dataset - bins : int - Number of bins in histogram - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.qa.hist(data=df, target="Adj Close") - """ - - data = data[target] - - fig = OpenBBFigure.create_subplots(1, 1, shared_yaxes=False) - - if isinstance(data.index[0], datetime): - start = data.index[0] - fig.set_title( - f"Histogram of {symbol} {target} from {start.strftime('%Y-%m-%d')}" - ) - else: - fig.set_title(f"Histogram of {symbol} {target}") - - fig.add_histplot( - data, - name=["Univariate distribution", "Marginal distributions"], - colors=[theme.up_color], - bins=bins, - curve="kde", - ) - - fig.update_layout( - xaxis_title="Value", - margin=dict(r=40), - yaxis=dict(title="Proportion", title_standoff=40), - bargap=0.01, - bargroupgap=0, - ) - fig.update_traces( - selector=dict(type="histogram"), - marker=dict( - color=theme.up_color, - line=dict(color="white", width=2.5), - ), - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_cdf( - data: pd.DataFrame, - target: str, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots Cumulative Distribution Function - - Parameters - ---------- - data : pd.DataFrame - Dataframe to look at - target : str - Data column - symbol : str - Name of dataset - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.qa.cdf(data=df, target="Adj Close") - """ - data = data[target] - start = data.index[0] - cdf = data.value_counts().sort_index().div(len(data)).cumsum() - - minVal = data.values.min() - q25 = np.quantile(data.values, 0.25) - medianVal = np.quantile(data.values, 0.5) - q75 = np.quantile(data.values, 0.75) - labels = [ - (minVal, q25), - (0.25, 0.25), - "Q1", - (q25, q25), - (0, 0.25), - "Q1", - (minVal, medianVal), - (0.5, 0.5), - "Median", - (medianVal, medianVal), - (0, 0.5), - "Median", - (minVal, q75), - (0.75, 0.75), - "Q3", - (q75, q75), - (0, 0.75), - "Q3", - ] - - fig = OpenBBFigure(xaxis_title=target.title(), yaxis_title="Probability") - fig.set_title( - f"Cumulative Distribution Function of {symbol} {target} from {start.strftime('%Y-%m-%d')}" - ) - - fig.add_scatter( - x=cdf.index, - y=cdf.values, - name="Cumulative Distribution Function", - marker_color=theme.up_color, - mode="lines", - ) - - for xt, yt, label in zip(labels[::3], labels[1::3], labels[2::3]): - fig.add_annotation( - x=minVal + (xt[1] - minVal) / 2, - y=yt[1] + 0.04, - text=label, - font=dict(color=theme.down_color), - ) - fig.add_shape( - type="line", - x0=xt[0], - y0=yt[0], - x1=xt[1], - y1=yt[1], - line=dict(color=theme.down_color, width=1.5, dash="dash"), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "cdf", - pd.DataFrame(cdf), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_bw( - data: pd.DataFrame, - target: str, - symbol: str = "", - yearly: bool = True, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots box and whisker plots - - Parameters - ---------- - symbol : str - Name of dataset - data : pd.DataFrame - Dataframe to look at - target : str - Data column to look at - yearly : bool - Flag to indicate yearly accumulation - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.qa.bw(data=df, target="Adj Close") - """ - data = data.copy() - start = data[target].index[0] - - colors = theme.get_colors() - color = colors[0] - pd.options.mode.chained_assignment = None - data["x_data"] = data[target].index.year if yearly else data[target].index.month - pd.options.mode.chained_assignment = "warn" - - l_months = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ] - - fig = OpenBBFigure( - title=( - f"{['Monthly','Yearly'][yearly]} box plot of {symbol.upper()} " - f"{target} from {start.strftime('%Y-%m-%d')}" - ), - yaxis_title=target, - xaxis_title=["Monthly", "Yearly"][yearly], - ) - - for i, group in enumerate(data["x_data"].unique()): - x = group if yearly else l_months[group - 1] - y = data[data["x_data"] == group][target] - - fig.add_box( - y=y, - x=[x] * len(y), - name=str(x), - marker=dict( - color=theme.up_color, - outliercolor=theme.up_color, - ), - fillcolor=colors[i % len(colors)], - line_color=color, - boxmean=True, - whiskerwidth=1, - boxpoints="suspectedoutliers", - hoveron="points", - showlegend=False, - ) - - fig.add_shape( - type="line", - x0=x, - y0=y.min(), - x1=x, - y1=y.max(), - line=dict(color=color, width=1.5, dash="dash"), - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_acf( - data: pd.DataFrame, - target: str, - symbol: str = "", - lags: int = 15, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots Auto and Partial Auto Correlation of returns and change in returns - - Parameters - ---------- - data : pd.DataFrame - Dataframe to look at - target : str - Data column to look at - symbol : str - Name of dataset - lags : int - Max number of lags to look at - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.qa.acf(data=df, target="Adj Close") - """ - data = data[target] - start = data.index[0] - - fig = OpenBBFigure.create_subplots( - rows=2, - cols=2, - shared_xaxes=False, - subplot_titles=[ - f"{symbol} Returns Auto-Correlation", - f"{symbol} Returns Partial Auto-Correlation", - f"Change in {symbol} Returns Auto-Correlation", - f"Change in {symbol} Returns Partial Auto-Correlation", - ], - vertical_spacing=0.1, - horizontal_spacing=0.1, - x_title="Lag", - ).set_title(title=f"ACF differentials starting from {start.strftime('%Y-%m-%d')}") - - kwargs = dict(marker=dict(color=theme.get_colors()[0]), line=dict(color="white")) - np_diff = np.diff(data.values) - - # Diff Auto - correlation function for original time series - fig.add_corr_plot(np_diff, lags, row=1, col=1, **kwargs) # type: ignore - # Diff Partial auto - correlation function for original time series - fig.add_corr_plot(np_diff, lags, row=1, col=2, pacf=True, method="ywm", **kwargs) # type: ignore - # Diff Diff Auto-correlation function for original time series - fig.add_corr_plot(np.diff(np_diff), lags, row=2, col=1, **kwargs) # type: ignore - # Diff Diff Partial auto-correlation function for original time series - fig.add_corr_plot( - np.diff(np_diff), lags, row=2, col=2, pacf=True, method="ywm", **kwargs # type: ignore - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_qqplot( - data: pd.DataFrame, - target: str, - symbol: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots QQ plot for data against normal quantiles - - Parameters - ---------- - data : pd.DataFrame - Dataframe - target : str - Column in data to look at - symbol : str - Stock ticker - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.qa.qqplot(data=df, target="Adj Close") - """ - # Statsmodels has a UserWarning for marker kwarg-- which we don't use - warnings.filterwarnings(category=UserWarning, action="ignore") - data = data[target] - - fig = OpenBBFigure( - xaxis_title="Theoretical quantiles", yaxis_title="Sample quantiles" - ) - fig.set_title(title=f"Q-Q plot for {symbol} {target}") - - qqplot_data = ( - qqplot( - data, - stats.distributions.norm, - fit=True, - line="45", - color=theme.down_color, - ) - .gca() - .lines - ) - - fig.add_scatter( - x=qqplot_data[0].get_xdata(), - y=qqplot_data[0].get_ydata(), - mode="markers", - showlegend=False, - ) - fig.add_scatter( - x=qqplot_data[1].get_xdata(), - y=qqplot_data[1].get_ydata(), - mode="lines", - line_color=theme.up_color, - showlegend=False, - ) - - plt.close() - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_cusum( - data: pd.DataFrame, - target: str, - threshold: float = 5, - drift: float = 2.1, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots Cumulative sum algorithm (CUSUM) to detect abrupt changes in data - - Parameters - ---------- - data : pd.DataFrame - Dataframe - target : str - Column of data to look at - threshold : float - Threshold value - drift : float - Drift parameter - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.qa.cusum(data=df, target="Adj Close") - """ - target_series = data[target].values - - # The code for this plot was adapted from detecta's sources because at the - # time of writing this detect_cusum had a bug related to external axes support. - # see https: // github.com/demotu/detecta/pull/3 - tap, tan = 0, 0 - ta, tai, taf, _ = detect_cusum( - x=target_series, - threshold=threshold, - drift=drift, - ending=True, - show=False, - ) - # Thus some variable names are left unchanged and unreadable... - gp, gn = np.zeros(target_series.size), np.zeros(target_series.size) - for i in range(1, target_series.size): - s = target_series[i] - target_series[i - 1] - gp[i] = gp[i - 1] + s - drift # cumulative sum for + change - gn[i] = gn[i - 1] - s - drift # cumulative sum for - change - if gp[i] < 0: - gp[i], tap = 0, i - if gn[i] < 0: - gn[i], tan = 0, i - if gp[i] > threshold or gn[i] > threshold: # change detected! - ta = np.append(ta, i) # alarm index - tai = np.append(tai, tap if gp[i] > threshold else tan) # start - gp[i], gn[i] = 0, 0 # reset alarm - - fig = OpenBBFigure.create_subplots( - 2, - 1, - x_title="Data points", - subplot_titles=[ - "Time series and detected changes " - + f"(threshold= {threshold:.3g}, drift= {drift:.3g}): N changes = {len(tai)}", - "Time series of the cumulative sums of positive and negative changes", - ], - vertical_spacing=0.1, - ) - target_series_indexes = range(data[target].size) - fig.add_scatter( - x=[*target_series_indexes], y=target_series, showlegend=False, row=1, col=1 - ) - - if len(ta): - fig.add_scatter( - x=tai, - y=target_series[tai], - mode="markers", - marker=dict( - color=theme.up_color, - size=8, - symbol="triangle-right", - ), - name="Start", - row=1, - col=1, - ) - fig.add_scatter( - x=taf, - y=target_series[taf], - mode="markers", - marker=dict( - color=theme.down_color, - size=8, - symbol="triangle-left", - ), - name="Ending", - row=1, - col=1, - ) - fig.add_scatter( - x=ta, - y=target_series[ta], - mode="markers", - marker=dict( - color=theme.get_colors()[-1], - size=5, - symbol="circle", - line=dict( - color=theme.get_colors()[-2], - width=1, - ), - ), - name="Alarm", - row=1, - col=1, - ) - fig.set_yaxis_title("Amplitude", row=1, col=1) - - fig.add_scatter( - x=[*target_series_indexes], - y=gp, - mode="lines", - name="+", - row=2, - col=1, - line=dict(color=theme.get_colors()[0]), - ) - fig.add_scatter( - x=[*target_series_indexes], - y=gn, - mode="lines", - name="-", - row=2, - col=1, - line=dict(color=theme.get_colors()[1]), - ) - fig.add_hline(threshold, row=2, col=1) - fig.update_yaxes( - position=0.0, row=2, col=1, range=[0, max(gp.max(), gn.max(), threshold) * 1.1] - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_seasonal( - symbol: str, - data: pd.DataFrame, - target: str, - multiplicative: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots seasonal decomposition data - - Parameters - ---------- - symbol : str - Name of dataset - data : pd.DataFrame - DataFrame - target : str - Column of data to look at - multiplicative : bool - Boolean to indicate multiplication instead of addition - export : str - Format to export trend and cycle data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = data[target] - result, cycle, trend = qa_model.get_seasonal_decomposition(data, multiplicative) - - plot_data = data.copy() - for rmerge, suffix in zip( - [result.trend, result.seasonal, result.resid, cycle, trend], - ["_result.trend", "_result.seasonal", "_result.resid", "_cycle", "_trend"], - ): - plot_data = pd.merge( - plot_data, - rmerge, - how="outer", - left_index=True, - right_index=True, - suffixes=("", suffix), - ) - - # Multiplicative model - fig = OpenBBFigure.create_subplots(4, 1, vertical_spacing=0.06) - fig.set_title(f"{symbol} (Time-Series) {target} seasonal decomposition") - colors = iter(theme.get_colors()) - - fig.add_scatter( - x=plot_data.index, - y=plot_data[target], - name="Values", - line_color=next(colors), - legend="legend", - row=1, - col=1, - ) - for i, (column, name) in enumerate( - zip( - [("trend", "trend_cycle"), ("trend_trend", "seasonal")], - [ - ("Cyclic-Trend", "Cycle component"), - ("Trend component", "Seasonal effect"), - ], - ) - ): - for j in range(2): - fig.add_scatter( - x=plot_data.index, - y=plot_data[column[j]], - name=name[j], - line_color=next(colors) if i != 0 else theme.down_color, - legend=f"legend{i + 2}", - row=i + 2, - col=1, - ) - - fig.add_scatter( - x=plot_data.index, - y=plot_data["resid"], - name="Residuals", - line_color=next(colors), - legend="legend4", - row=4, - col=1, - ) - - legend_defaults = dict( - xanchor="left", - yanchor="top", - xref="paper", - yref="paper", - font=dict(size=13), - bgcolor="rgba(0, 0, 0, 0.4)", - ) - fig.update_traces(selector=dict(name="Cyclic-Trend"), line_color=theme.down_color) - fig.update_traces( - selector=dict(name="Cycle component"), - line=dict(color=theme.up_color, dash="dash"), - ) - fig.update_yaxes(nticks=5) - fig.update_layout( - legend=dict(**legend_defaults), - legend2=dict(x=0.01, y=0.73, **legend_defaults), - legend3=dict(x=0.01, y=0.46, **legend_defaults), - legend4=dict(x=0.01, y=0.20, **legend_defaults), - ) - - # From #https: // otexts.com/fpp2/seasonal-strength.html - console.print("Time-Series Level is " + str(round(data.mean(), 2))) - - Ft = max(0, 1 - np.var(result.resid)) / np.var(result.trend + result.resid) - console.print(f"Strength of Trend: {Ft:.4f}") - - Fs = max( - 0, - 1 - np.var(result.resid) / np.var(result.seasonal + result.resid), - ) - console.print(f"Strength of Seasonality: {Fs:.4f}\n") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "summary", - cycle.join(trend), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_normality( - data: pd.DataFrame, target: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing normality statistics - - Parameters - ---------- - data : pd.DataFrame - DataFrame - target : str - Column in data to look at - export : str - Format to export data - """ - data = data[target] - normal = qa_model.get_normality(data) - stats1 = normal.copy().T - stats1.iloc[:, 1] = stats1.iloc[:, 1].apply(lambda x: lambda_color_red(x)) - - print_rich_table( - stats1, - show_index=True, - headers=["Statistic", "p-value"], - floatfmt=".4f", - title="[bold]Normality Statistics[/bold]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "normality", - normal, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_unitroot( - data: pd.DataFrame, - target: str, - fuller_reg: str = "c", - kpss_reg: str = "c", - export: str = "", - sheet_name: Optional[str] = None, -): - """Prints table showing unit root test calculations - - Parameters - ---------- - data : pd.DataFrame - DataFrame - target : str - Column of data to look at - fuller_reg : str - Type of regression of ADF test. Can be ‘c’,’ct’,’ctt’,’nc’ 'c' - Constant and t - trend order - kpss_reg : str - Type of regression for KPSS test. Can be ‘c’,’ct' - export : str - Format for exporting data - """ - data = data[target] - data = qa_model.get_unitroot(data, fuller_reg, kpss_reg) - print_rich_table( - data, - show_index=True, - headers=list(data.columns), - title="[bold]Unit Root Calculation[/bold]", - floatfmt=".4f", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "unitroot", - data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_raw( - data: pd.DataFrame, - sortby: str = "", - ascend: bool = False, - limit: int = 20, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing raw stock data - - Parameters - ---------- - data : DataFrame - DataFrame with historical information - sortby : str - The column to sort by - ascend : bool - Whether to sort descending - limit : int - Number of rows to show - export : str - Export data as CSV, JSON, XLSX - """ - - df1 = pd.DataFrame(data) if isinstance(data, pd.Series) else data.copy() - - if sortby: - sortby = sortby.replace("_", " ") - try: - sort_col = [x.lower().replace(" ", "") for x in df1.columns].index( - sortby.lower().replace(" ", "") - ) - except ValueError: - console.print("[red]The provided column is not a valid option[/red]\n") - return - df1 = df1.sort_values(by=data.columns[sort_col], ascending=ascend) - else: - df1 = df1.sort_index(ascending=ascend) - - print_rich_table( - df1, - headers=[x.title() if x != "" else "Date" for x in df1.columns], - title="[bold]Raw Data[/bold]", - show_index=True, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "raw", - data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_line( - data: pd.Series, - title: str = "", - log_y: bool = True, - markers_lines: Optional[List[datetime]] = None, - markers_scatter: Optional[List[datetime]] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display line plot of data - - Parameters - ---------- - data: pd.Series - Data to plot - title: str - Title for plot - log_y: bool - Flag for showing y on log scale - markers_lines: Optional[List[datetime]] - List of dates to highlight using vertical lines - markers_scatter: Optional[List[datetime]] - List of dates to highlight using scatter - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.qa.line(data=df["Adj Close"]) - """ - - fig = OpenBBFigure(yaxis_title=data.name) - - if log_y: - fig.add_scatter( - x=data.index, - y=data.values, - name="", - mode="lines", - showlegend=False, - ) - fig.update_layout(yaxis_type="log") - - else: - fig.add_scatter( - x=data.index, - y=data.values, - name="", - mode="lines", - showlegend=False, - ) - - if markers_lines: - for marker_date in markers_lines: - fig.add_vline(x=marker_date, line=dict(color=theme.up_color, width=2)) - - if markers_scatter: - scatter: Dict[str, list] = dict(x=[], y=[]) - for n, marker_date in enumerate(markers_scatter): - price_location_idx = data.index.get_loc(marker_date, method="nearest") - # algo to improve text placement of highlight event number - if ( - price_location_idx > 0 - and data.iloc[price_location_idx - 1] - > data.iloc[price_location_idx] - ): - text_loc = (0, -20) - else: - text_loc = (0, 10) - - fig.add_annotation( - x=marker_date, - y=data.iloc[price_location_idx], - text=str(n + 1), - xshift=text_loc[0], - yshift=text_loc[1], - ) - scatter["x"].append(marker_date.strftime("%Y-%m-%d")) - scatter["y"].append(data.iloc[price_location_idx]) - - fig.add_scatter( - x=scatter["x"], - y=scatter["y"], - mode="markers", - marker=dict( - size=12, - color=theme.up_color, - line=dict( - width=2, - color=theme.up_color, - ), - ), - ) - - if title: - fig.set_title(title) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "line", - sheet_name=sheet_name, - figure=fig, - ) - - return fig.show(external=external_axes) - - -def display_var( - data: pd.DataFrame, - symbol: str = "", - use_mean: bool = False, - adjusted_var: bool = False, - student_t: bool = False, - percentile: float = 99.9, - data_range: int = 0, - portfolio: bool = False, -) -> None: - """Prints table showing VaR of dataframe. - - Parameters - ---------- - data: pd.Dataframe - Data dataframe - use_mean: bool - if one should use the data mean return - symbol: str - name of the data - adjusted_var: bool - if one should have VaR adjusted for skew and kurtosis (Cornish-Fisher-Expansion) - student_t: bool - If one should use the student-t distribution - percentile: int - var percentile - data_range: int - Number of rows you want to use VaR over - portfolio: bool - If the data is a portfolio - """ - - if data_range > 0: - df = qa_model.get_var( - data[-data_range:], use_mean, adjusted_var, student_t, percentile, portfolio - ) - else: - df = qa_model.get_var( - data, use_mean, adjusted_var, student_t, percentile, portfolio - ) - - if adjusted_var: - str_title = "Adjusted " - elif student_t: - str_title = "Student-t " - else: - str_title = "" - - if symbol != "": - symbol += " " - - print_rich_table( - df, - show_index=True, - headers=list(df.columns), - title=f"[bold]{symbol}{str_title}Value at Risk[/bold]", - floatfmt=".2f", - ) - - -def display_es( - data: pd.DataFrame, - symbol: str = "", - use_mean: bool = False, - distribution: str = "normal", - percentile: float = 99.9, - portfolio: bool = False, -) -> None: - """Prints table showing expected shortfall. - - Parameters - ---------- - data: pd.DataFrame - Data dataframe - use_mean: - if one should use the data mean return - symbol: str - name of the data - distribution: str - choose distribution to use: logistic, laplace, normal - percentile: int - es percentile - portfolio: bool - If the data is a portfolio - """ - df = qa_model.get_es(data, use_mean, distribution, percentile, portfolio) - - if distribution == "laplace": - str_title = "Laplace " - elif distribution == "student_t": - str_title = "Student-t " - elif distribution == "logistic": - str_title = "Logistic " - else: - str_title = "" - - if symbol != "": - symbol += " " - - print_rich_table( - df, - show_index=True, - headers=list(df.columns), - title=f"[bold]{symbol}{str_title}Expected Shortfall[/bold]", - floatfmt=".2f", - ) - - -def display_sharpe( - data: pd.DataFrame, rfr: float = 0, window: float = 252, external_axes: bool = False -) -> Union[OpenBBFigure, None]: - """Plots Calculated the sharpe ratio - - Parameters - ---------- - data: pd.DataFrame - selected dataframe column - rfr: float - risk free rate - window: float - length of the rolling window - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - sharpe_ratio = qa_model.get_sharpe(data, rfr, window) - - data = sharpe_ratio[int(window - 1) :] - - fig = OpenBBFigure(yaxis_title="Sharpe Ratio", xaxis_title="Date") - fig.set_title(f"Sharpe Ratio - over a {window} day window") - - fig.add_scatter(x=data.index, y=data) - - return fig.show(external=external_axes) - - -def display_sortino( - data: pd.DataFrame, - target_return: float, - window: float, - adjusted: bool, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots the sortino ratio - - Parameters - ---------- - data: pd.DataFrame - selected dataframe - target_return: float - target return of the asset - window: float - length of the rolling window - adjusted: bool - adjust the sortino ratio - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - sortino_ratio = qa_model.get_sortino(data, target_return, window, adjusted) - str_adjusted = "Adjusted " if adjusted else "" - - data = sortino_ratio[int(window - 1) :] - - fig = OpenBBFigure(yaxis_title="Sortino Ratio", xaxis_title="Date") - fig.set_title(f"{str_adjusted}Sortino Ratio - over a {window} day window") - - fig.add_scatter(x=data.index, y=data) - - return fig.show(external=external_axes) - - -def display_omega( - data: pd.DataFrame, - threshold_start: float = 0, - threshold_end: float = 1.5, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots the omega ratio - - Parameters - ---------- - data: pd.DataFrame - stock dataframe - threshold_start: float - annualized target return threshold start of plotted threshold range - threshold_end: float - annualized target return threshold end of plotted threshold range - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = qa_model.get_omega(data, threshold_start, threshold_end) - - # Plotting - fig = OpenBBFigure(yaxis_title="Omega Ratio", xaxis_title="Threshold (%)") - fig.set_title(f"Omega Curve - over last {len(data)}'s period") - - fig.add_scatter(x=df["threshold"], y=df["omega"], name="Omega Curve") - - return fig.show(external=external_axes) diff --git a/openbb_terminal/common/quantitative_analysis/rolling_model.py b/openbb_terminal/common/quantitative_analysis/rolling_model.py deleted file mode 100644 index 5619d2f8b39..00000000000 --- a/openbb_terminal/common/quantitative_analysis/rolling_model.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Rolling Statistics""" - -__docformat__ = "numpy" - -import logging -from typing import Tuple - -import pandas as pd -import pandas_ta as ta - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_rolling_avg( - data: pd.DataFrame, window: int = 14 -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Return rolling mean and standard deviation - - Parameters - ---------- - data: pd.DataFrame - Dataframe of target data - window: int - Length of rolling window - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame] - Dataframe of rolling mean, - Dataframe of rolling standard deviation - """ - rolling_mean = data.rolling(window, center=True, min_periods=1).mean() - rolling_std = data.rolling(window, center=True, min_periods=1).std() - - return pd.DataFrame(rolling_mean), pd.DataFrame(rolling_std) - - -@log_start_end(log=logger) -def get_spread( - data: pd.DataFrame, window: int = 14 -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Standard Deviation and Variance - - Parameters - ---------- - data: pd.DataFrame - DataFrame of targeted data - window: int - Length of window - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame] - Dataframe of rolling standard deviation, - Dataframe of rolling variance - """ - df_sd = ta.stdev( - close=data, - length=window, - ).dropna() - df_var = ta.variance( - close=data, - length=window, - ).dropna() - - return pd.DataFrame(df_sd), pd.DataFrame(df_var) - - -@log_start_end(log=logger) -def get_quantile( - data: pd.DataFrame, window: int = 14, quantile_pct: float = 0.5 -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Overlay Median & Quantile - - Parameters - ---------- - data: pd.DataFrame - Dataframe of targeted data - window : int - Length of window - quantile_pct: float - Quantile to display - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame] - Dataframe of rolling median prices over window, - Dataframe of rolling quantile prices over window - """ - df_med = ta.median(close=data, length=window).dropna() - df_quantile = ta.quantile( - data, - length=window, - q=quantile_pct, - ).dropna() - - return pd.DataFrame(df_med), pd.DataFrame(df_quantile) - - -@log_start_end(log=logger) -def get_skew(data: pd.DataFrame, window: int = 14) -> pd.DataFrame: - """Skewness Indicator - - Parameters - ---------- - data: pd.DataFrame - Dataframe of targeted data - window : int - Length of window - - Returns - ------- - data_skew : pd.DataFrame - Dataframe of rolling skew - """ - df_skew = ta.skew(close=data, length=window).dropna() - return df_skew - - -@log_start_end(log=logger) -def get_kurtosis(data: pd.DataFrame, window: int = 14) -> pd.DataFrame: - """Kurtosis Indicator - - Parameters - ---------- - data: pd.DataFrame - Dataframe of targeted data - window: int - Length of window - - Returns - ------- - df_kurt : pd.DataFrame - Dataframe of rolling kurtosis - """ - df_kurt = ta.kurtosis(close=data, length=window).dropna() - return df_kurt diff --git a/openbb_terminal/common/quantitative_analysis/rolling_view.py b/openbb_terminal/common/quantitative_analysis/rolling_view.py deleted file mode 100644 index 90584c97704..00000000000 --- a/openbb_terminal/common/quantitative_analysis/rolling_view.py +++ /dev/null @@ -1,464 +0,0 @@ -"""Rolling Statistics View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.common.quantitative_analysis import rolling_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_mean_std( - data: pd.DataFrame, - target: str, - symbol: str = "", - window: int = 14, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots mean std deviation - - Parameters - ---------- - data: pd.DataFrame - Dataframe - target: str - Column in data to look at - symbol : str - Stock ticker - window : int - Length of window - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = data[target] - rolling_mean, rolling_std = rolling_model.get_rolling_avg(data, window) - plot_data = pd.merge( - data, - rolling_mean, - how="outer", - left_index=True, - right_index=True, - suffixes=("", "_mean"), - ) - plot_data = pd.merge( - plot_data, - rolling_std, - how="outer", - left_index=True, - right_index=True, - suffixes=("", "_std"), - ) - - fig = OpenBBFigure.create_subplots( - 2, - 1, - shared_xaxes=True, - subplot_titles=[ - f"Rolling mean and std (window {str(window)}) of {symbol} {target}" - ], - vertical_spacing=0.1, - ) - fig.add_scatter( - x=plot_data.index, - y=plot_data[target].values, - name="Real Values", - legend="legend", - row=1, - col=1, - ) - fig.add_scatter( - x=plot_data.index, - y=plot_data[target + "_mean"].values, - name="Rolling Mean", - legend="legend", - row=1, - col=1, - ) - fig.add_scatter( - x=plot_data.index, - y=plot_data[target + "_std"].values, - name="Rolling Std", - legend="legend2", - row=2, - col=1, - ) - fig.update_layout( - legend=dict(xref="paper", yref="paper"), - legend2=dict( - xanchor="left", - yanchor="top", - xref="paper", - yref="paper", - x=0.01, - y=0.44, - ), - yaxis=dict(title="Values"), - yaxis2=dict(title=f"{target} Std Deviation"), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "rolling", - rolling_mean.join(rolling_std, lsuffix="_mean", rsuffix="_std"), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_spread( - data: pd.DataFrame, - target: str, - symbol: str = "", - window: int = 14, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots rolling spread - - Parameters - ---------- - data: pd.DataFrame - Dataframe - target: str - Column in data to look at - target: str - Column in data to look at - symbol : str - Stock ticker - window : int - Length of window - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = data[target] - df_sd, df_var = rolling_model.get_spread(data, window) - - plot_data = pd.merge( - data, - df_sd, - how="outer", - left_index=True, - right_index=True, - suffixes=("", "_sd"), - ) - plot_data = pd.merge( - plot_data, - df_var, - how="outer", - left_index=True, - right_index=True, - suffixes=("", "_var"), - ) - - fig = OpenBBFigure.create_subplots( - 3, - 1, - shared_xaxes=True, - subplot_titles=[ - "Real Values", - "Rolling Stdev", - "Rolling Variance", - ], - vertical_spacing=0.1, - ).set_title(f"Spread of {symbol} {target}") - - fig.add_scatter( - x=plot_data.index, - y=plot_data[target].values, - name="Real Values", - row=1, - col=1, - ) - fig.add_scatter( - x=plot_data.index, - y=plot_data[f"STDEV_{window}"].values, - name="Rolling Stdev", - row=2, - col=1, - ) - fig.add_scatter( - x=plot_data.index, - y=plot_data[f"VAR_{window}"].values, - name="Rolling Variance", - row=3, - col=1, - ) - fig.update_layout( - yaxis=dict(title="Values"), - yaxis2=dict(title="Stdev"), - yaxis3=dict(title="Variance"), - ) - fig.update_traces(showlegend=False) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "spread", - df_sd.join(df_var, lsuffix="_sd", rsuffix="_var"), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_quantile( - data: pd.DataFrame, - target: str, - symbol: str = "", - window: int = 14, - quantile: float = 0.5, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots rolling quantile - - Parameters - ---------- - data: pd.DataFrame - Dataframe - target: str - Column in data to look at - symbol : str - Stock ticker - window : int - Length of window - quantile: float - Quantile to get - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes: bool, optional - Whether to return the figure object or not, by default False - """ - data = data[target] - df_med, df_quantile = rolling_model.get_quantile(data, window, quantile) - - plot_data = pd.merge( - data, - df_med, - how="outer", - left_index=True, - right_index=True, - suffixes=("", "_med"), - ) - plot_data = pd.merge( - plot_data, - df_quantile, - how="outer", - left_index=True, - right_index=True, - suffixes=("", "_quantile"), - ) - - fig = OpenBBFigure().set_title(f"{symbol} {target} Median & Quantile") - - fig.add_scatter( - x=plot_data.index, - y=plot_data[target].values, - name=target, - ) - fig.add_scatter( - x=plot_data.index, - y=plot_data[f"MEDIAN_{window}"].values, - name=f"Median w={window}", - ) - fig.add_scatter( - x=plot_data.index, - y=plot_data[f"QTL_{window}_{quantile}"].values, - name=f"Quantile q={quantile}", - line=dict(dash="dash"), - ) - fig.update_layout(yaxis=dict(title=f"{symbol} Value")) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "quantile", - df_med.join(df_quantile), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_skew( - symbol: str, - data: pd.DataFrame, - target: str, - window: int = 14, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots rolling skew - - Parameters - ---------- - symbol: str - Stock ticker - data: pd.DataFrame - Dataframe - target: str - Column in data to look at - window: int - Length of window - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = data[target] - df_skew = rolling_model.get_skew(data, window) - - plot_data = pd.merge( - data, - df_skew, - how="outer", - left_index=True, - right_index=True, - ) - - fig = OpenBBFigure.create_subplots( - rows=2, - cols=1, - vertical_spacing=0.1, - ).set_title(f"{symbol} Skewness Indicator") - - fig.add_scatter( - x=plot_data.index, - y=plot_data[target].values, - name=target, - row=1, - col=1, - ) - - fig.add_scatter( - x=plot_data.index, - y=plot_data[f"SKEW_{window}"].values, - name=f"Skew w={window}", - row=2, - col=1, - ) - - fig.update_layout(yaxis=dict(title=f"{target}"), yaxis2=dict(title="Indicator")) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "skew", - df_skew, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_kurtosis( - symbol: str, - data: pd.DataFrame, - target: str, - window: int = 14, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots rolling kurtosis - - Parameters - ---------- - symbol: str - Ticker - data: pd.DataFrame - Dataframe of stock prices - target: str - Column in data to look at - window: int - Length of window - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = data[target] - df_kurt = rolling_model.get_kurtosis(data, window) - - plot_data = pd.merge( - data, - df_kurt, - how="outer", - left_index=True, - right_index=True, - ) - - fig = OpenBBFigure.create_subplots( - rows=2, - cols=1, - vertical_spacing=0.1, - subplot_titles=[f"{target}", "Kurtosis"], - ).set_title(f"{symbol} {target} Kurtosis Indicator (window {str(window)})") - - fig.add_scatter( - x=plot_data.index, - y=plot_data[target].values, - name=target, - row=1, - col=1, - ) - - fig.add_scatter( - x=plot_data.index, - y=plot_data[f"KURT_{window}"].values, - name=f"Kurtosis w={window}", - row=2, - col=1, - ) - - fig.update_layout(yaxis=dict(title=f"{target}"), yaxis2=dict(title="Indicator")) - fig.update_traces(showlegend=False) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "kurtosis", - df_kurt, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/common/technical_analysis/__init__.py b/openbb_terminal/common/technical_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/common/technical_analysis/custom_indicators_model.py b/openbb_terminal/common/technical_analysis/custom_indicators_model.py deleted file mode 100644 index 619b8b81d2a..00000000000 --- a/openbb_terminal/common/technical_analysis/custom_indicators_model.py +++ /dev/null @@ -1,104 +0,0 @@ -"""Custom Indicator Models""" - -__docformat__ = "numpy" - -import logging -from typing import Any, Optional, Tuple - -import pandas as pd - -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def calculate_fib_levels( - data: pd.DataFrame, - limit: int = 120, - start_date: Optional[Any] = None, - end_date: Optional[Any] = None, -) -> Tuple[pd.DataFrame, pd.Timestamp, pd.Timestamp, float, float, str]: - """Calculate Fibonacci levels - - Parameters - ---------- - data : pd.DataFrame - Dataframe of prices - limit : int - Days to look back for retracement - start_date : Any - Custom start date for retracement - end_date : Any - Custom end date for retracement - - Returns - ------- - df : pd.DataFrame - Dataframe of fib levels - min_date: pd.Timestamp - Date of min point - max_date: pd.Timestamp: - Date of max point - min_pr: float - Price at min point - max_pr: float - Price at max point - """ - close_col = ta_helpers.check_columns(data, high=False, low=False) - if close_col is None: - return pd.DataFrame(), pd.Timestamp(), pd.Timestamp(), 0, 0, "" - if start_date and end_date: - if start_date not in data.index: - date0 = data.index[data.index.get_indexer([end_date], method="nearest")[0]] - console.print(f"Start date not in data. Using nearest: {date0}") - else: - date0 = start_date - if end_date not in data.index: - date1 = data.index[data.index.get_indexer([end_date], method="nearest")[0]] - console.print(f"End date not in data. Using nearest: {date1}") - else: - date1 = end_date - - data0 = data.loc[date0, close_col] - data1 = data.loc[date1, close_col] - - min_pr = min(data0, data1) - max_pr = max(data0, data1) - - if min_pr == data0: - min_date = date0 - max_date = date1 - else: - min_date = date1 - max_date = date0 - else: - data_to_use = data.iloc[-limit:, :][close_col] - - min_pr = data_to_use.min() - min_date = data_to_use.idxmin() - max_pr = data_to_use.max() - max_date = data_to_use.idxmax() - - fib_levels = [0, 0.235, 0.382, 0.5, 0.618, 0.65, 1] - - lvl_text: str = "left" if min_date < max_date else "right" - if min_date > max_date: - min_date, max_date = max_date, min_date - min_pr, max_pr = max_pr, min_pr - - price_dif = max_pr - min_pr - - levels = [ - round(max_pr - price_dif * f_lev, (2 if f_lev > 1 else 4)) - for f_lev in fib_levels - ] - - df = pd.DataFrame() - df["Level"] = fib_levels - df["Level"] = df["Level"].apply(lambda x: str(x * 100) + "%") - df["Price"] = levels - - return df, min_date, max_date, min_pr, max_pr, lvl_text diff --git a/openbb_terminal/common/technical_analysis/custom_indicators_view.py b/openbb_terminal/common/technical_analysis/custom_indicators_view.py deleted file mode 100644 index 87a64aaf844..00000000000 --- a/openbb_terminal/common/technical_analysis/custom_indicators_view.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Custom TA indicators""" - -__docformat__ = "numpy" - -import logging -import os -from typing import List, Optional, Union - -import pandas as pd - -from openbb_terminal.common.technical_analysis import ( - ta_helpers, -) -from openbb_terminal.core.plots.plotly_ta.ta_class import OpenBBFigure, PlotlyTA -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, -) - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def fibonacci_retracement( - data: pd.DataFrame, - limit: int = 120, - start_date: Optional[Union[str, None]] = None, - end_date: Optional[Union[str, None]] = None, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots Calculated fibonacci retracement levels - - Parameters - ---------- - data: pd.DataFrame - OHLC data - limit: int - Days to lookback - start_date: Optional[str, None] - User picked date for starting retracement - end_date: Optional[str, None] - User picked date for ending retracement - symbol: str - Ticker symbol - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load(symbol="aapl") - >>> openbb.ta.fib_chart(data=df) - """ - data = pd.DataFrame(data) - data.index.name = "date" - - if ta_helpers.check_columns(data) is None: - return None - - ta = PlotlyTA() - fig = ta.plot( - data, - dict(fib=dict(limit=limit, start_date=start_date, end_date=end_date)), - f"Fibonacci Support for {symbol.upper()}", - False, - volume=False, - ) - - if not external_axes: - print_rich_table( - ta.df_fib, - headers=["Fib Level", "Price"], - show_index=False, - title="Fibonacci retracement levels", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "fib", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def plot_multiple_indicators( - data: pd.DataFrame, - indicators: List[str], - symbol: str = "", - params: Optional[dict] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots multiple technical analysis indicators - - Parameters - ---------- - data: pd.DataFrame - OHLC data - indicators: list - List of indicators to plot - symbol: str - Ticker symbol - window: List[int] - List of lengths for moving averages - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load(symbol="aapl") - >>> openbb.ta.multi_chart(data=df, indicators=["macd", "rsi"]) - """ - data = pd.DataFrame(data) - data.index.name = "date" - - if ta_helpers.check_columns(data) is None: - return None - - indicators_dict: dict = {indicator: {} for indicator in indicators} - if params: - indicators_dict.update(params) - - ta = PlotlyTA() - - for ma in ta.ma_mode: - if ma in indicators_dict and not indicators_dict[ma].get("length", None): - indicators_dict[ma] = dict(length=[20, 50]) - - fig = ta.plot( - data, - indicators_dict, - f"{symbol.upper()} Technical Analysis", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "indicators", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/common/technical_analysis/momentum_model.py b/openbb_terminal/common/technical_analysis/momentum_model.py deleted file mode 100644 index 98cca9c0953..00000000000 --- a/openbb_terminal/common/technical_analysis/momentum_model.py +++ /dev/null @@ -1,277 +0,0 @@ -"""Momentum Technical Analysis""" - -__docformat__ = "numpy" - -import logging -from typing import Tuple - -import numpy as np -import pandas as pd -import pandas_ta as ta -from sklearn.linear_model import LinearRegression - -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def cci( - data: pd.DataFrame, - window: int = 14, - scalar: float = 0.0015, -) -> pd.DataFrame: - """Commodity channel index - - Parameters - ---------- - high_vals: pd.Series - High values - low_values: pd.Series - Low values - close-values: pd.Series - Close values - window: int - Length of window - scalar: float - Scalar variable - - Returns - ------- - pd.DataFrame - Dataframe of technical indicator - """ - - close_col = ta_helpers.check_columns(data) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.cci( - high=data["High"], - low=data["Low"], - close=data[close_col], - length=window, - scalar=scalar, - ).dropna() - ) - - -@log_start_end(log=logger) -def macd( - data: pd.Series, - n_fast: int = 12, - n_slow: int = 26, - n_signal: int = 9, -) -> pd.DataFrame: - """Moving average convergence divergence - - Parameters - ---------- - data: pd.Series - Values for calculation - n_fast : int - Fast period - n_slow : int - Slow period - n_signal : int - Signal period - Returns - ------- - pd.DataFrame - Dataframe of technical indicator - """ - if isinstance(data, pd.DataFrame): - console.print("[red]Please send a series and not a DataFrame.[/red]\n") - return pd.DataFrame() - return pd.DataFrame( - ta.macd(data, fast=n_fast, slow=n_slow, signal=n_signal).dropna() - ) - - -@log_start_end(log=logger) -def rsi( - data: pd.Series, window: int = 14, scalar: float = 100, drift: int = 1 -) -> pd.DataFrame: - """Relative strength index - - Parameters - ---------- - data: pd.Series - Dataframe of prices - window: int - Length of window - scalar: float - Scalar variable - drift: int - Drift variable - - Returns - ------- - pd.DataFrame - Dataframe of technical indicator - """ - if isinstance(data, pd.DataFrame): - console.print("[red]Please send a series and not a DataFrame.[/red]\n") - return pd.DataFrame() - raw_data = ta.rsi(data, length=window, scalar=scalar, drift=drift) - if raw_data is None: - return pd.DataFrame() - if raw_data.empty: - return pd.DataFrame() - return pd.DataFrame(raw_data.dropna()) - - -@log_start_end(log=logger) -def stoch( - data: pd.DataFrame, - fastkperiod: int = 14, - slowdperiod: int = 3, - slowkperiod: int = 3, -): - """Stochastic oscillator - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices - fastkperiod : int - Fast k period - slowdperiod : int - Slow d period - slowkperiod : int - Slow k period - Returns - ------- - pd.DataFrame - Dataframe of technical indicator - """ - close_col = ta_helpers.check_columns(data) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.stoch( - high=data["High"], - low=data["Low"], - close=data[close_col], - k=fastkperiod, - d=slowdperiod, - smooth_k=slowkperiod, - ).dropna() - ) - - -@log_start_end(log=logger) -def fisher(data: pd.DataFrame, window: int = 14) -> pd.DataFrame: - """Fisher Transform - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices - window: int - Length for indicator window - Returns - ------- - df_ta: pd.DataFrame - Dataframe of technical indicator - """ - # Daily - close_col = ta_helpers.check_columns(data, close=False) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.fisher(high=data["High"], low=data["Low"], length=window).dropna() - ) - - -@log_start_end(log=logger) -def cg(values: pd.Series, window: int) -> pd.DataFrame: - """Center of gravity - - Parameters - ---------- - values: pd.DataFrame - Data to use with close being titled values - window: int - Length for indicator window - Returns - ------- - pd.DataFrame - Dataframe of technical indicator - """ - return pd.DataFrame(ta.cg(close=values, length=window).dropna()) - - -@log_start_end(log=logger) -def clenow_momentum( - values: pd.Series, window: int = 90 -) -> Tuple[float, float, pd.Series]: - """Gets the Clenow Volatility Adjusted Momentum. this is defined as the regression coefficient on log prices - multiplied by the R^2 value of the regression - - Parameters - ---------- - values: pd.Series - Values to perform regression for - window: int - Length of lookback period - - Returns - ------- - float: - R2 of fit to log data - float: - Coefficient of linear regression - pd.Series: - Values for best fit line - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.ta.clenow(df["Close"]) - """ - if len(values) < window: - console.print( - f"[red]Calculation asks for at least last {window} days of data[/red]" - ) - return np.nan, np.nan, pd.Series() - - values = values[-window:] - - y = np.log(values) - X = np.arange(len(y)).reshape(-1, 1) - - lr = LinearRegression() - lr.fit(X, y) - - r2 = lr.score(X, y) - coef = lr.coef_[0] - annualized_coef = (np.exp(coef) ** 252) - 1 - - return r2, annualized_coef, pd.Series(lr.predict(X)) - - -@log_start_end(log=logger) -def demark_seq(values: pd.Series) -> pd.DataFrame: - """Get the integer value for demark sequential indicator - - Parameters - ---------- - values: pd.Series - Series of close values - - Returns - ------- - pd.DataFrame - Dataframe of UP and DOWN sequential indicators - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.ta.demark(df["Close"]) - """ - return ta.td_seq(values, asint=True) diff --git a/openbb_terminal/common/technical_analysis/momentum_view.py b/openbb_terminal/common/technical_analysis/momentum_view.py deleted file mode 100644 index 3fdf15db2e6..00000000000 --- a/openbb_terminal/common/technical_analysis/momentum_view.py +++ /dev/null @@ -1,524 +0,0 @@ -"""Momentum View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.common.technical_analysis import momentum_model, ta_helpers -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_cci( - data: pd.DataFrame, - window: int = 14, - scalar: float = 0.0015, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots CCI Indicator - - Parameters - ---------- - - data : pd.DataFrame - Dataframe of OHLC - window : int - Length of window - scalar : float - Scalar variable - symbol : str - Stock ticker - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(cci=dict(length=window, scalar=scalar)), - f"{symbol.upper()} CCI", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "cci", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_macd( - data: pd.Series, - n_fast: int = 12, - n_slow: int = 26, - n_signal: int = 9, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots MACD signal - - Parameters - ---------- - data : pd.Series - Values to input - n_fast : int - Fast period - n_slow : int - Slow period - n_signal : int - Signal period - symbol : str - Stock ticker - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(macd=dict(fast=n_fast, slow=n_slow, signal=n_signal)), - f"{symbol.upper()} MACD", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "macd", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_rsi( - data: pd.Series, - window: int = 14, - scalar: float = 100.0, - drift: int = 1, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots RSI Indicator - - Parameters - ---------- - data : pd.Series - Values to input - window : int - Length of window - scalar : float - Scalar variable - drift : int - Drift variable - symbol : str - Stock ticker - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if isinstance(data, pd.DataFrame): - return console.print("[red]Please send a series and not a dataframe[/red]\n") - - ta = PlotlyTA() - fig = ta.plot( - data, - dict(rsi=dict(length=window, scalar=scalar, drift=drift)), - f"{symbol.upper()} RSI {window}", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "rsi", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_stoch( - data: pd.DataFrame, - fastkperiod: int = 14, - slowdperiod: int = 3, - slowkperiod: int = 3, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots stochastic oscillator signal - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices - fastkperiod : int - Fast k period - slowdperiod : int - Slow d period - slowkperiod : int - Slow k period - symbol : str - Stock ticker symbol - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - close_col = ta_helpers.check_columns(data) - if close_col is None: - return None - - ta = PlotlyTA() - fig = ta.plot( - data, - dict(stoch=dict(k=fastkperiod, d=slowdperiod, smooth_k=slowkperiod)), - f"Stochastic Relative Strength Index (STOCH RSI) on {symbol}", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "stoch", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_fisher( - data: pd.DataFrame, - window: int = 14, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots Fisher Indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices - window : int - Length of window - symbol : str - Ticker string - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(fisher=dict(length=window)), - f"{symbol.upper()} Fisher Transform", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "fisher", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_cg( - data: pd.Series, - window: int = 14, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots center of gravity Indicator - - Parameters - ---------- - data : pd.Series - Series of values - window : int - Length of window - symbol : str - Stock ticker - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(cg=dict(length=window)), - f"{symbol.upper()} Centre of Gravity", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "cg", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_clenow_momentum( - data: pd.Series, - symbol: str = "", - window: int = 90, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Prints table and plots clenow momentum - - Parameters - ---------- - data : pd.Series - Series of values - symbol : str - Symbol that the data corresponds to - window : int - Length of window - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.ta.clenow_chart(df["Close"]) - """ - fig = PlotlyTA.plot( - data, - dict(clenow=dict(window=window)), - f"Clenow Momentum Exponential Regression on {symbol}", - False, - volume=False, - ) - - r2, coef, _ = momentum_model.clenow_momentum(data, window) - - df_ta = pd.DataFrame.from_dict( - { - "R^2": f"{r2:.5f}", - "Fit Coef": f"{coef:.5f}", - "Factor": f"{coef * r2:.5f}", - }, - orient="index", - ) - - print_rich_table( - df_ta, - show_index=True, - headers=[""], - title=f"Clenow Exponential Regression Factor on {symbol}", - show_header=False, - export=bool(export), - print_to_console=True, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "clenow", - df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -def display_demark( - data: pd.DataFrame, - symbol: str = "", - min_to_show: int = 5, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot demark sequential indicator - - Parameters - ---------- - data : pd.DataFrame - DataFrame of values - symbol : str - Symbol that the data corresponds to - min_to_show: int - Minimum value to show - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.ta.demark_chart(df) - """ - close_col = ta_helpers.check_columns(data, high=False, low=False) - if close_col is None: - return None - - fig = PlotlyTA.plot( - data, - dict(demark=dict(min_val=min_to_show)), - f"{symbol.upper()} Demark Sequential", - volume=False, - ) - - demark_df = momentum_model.demark_seq(data[close_col]) - demark_df.index = data.index - - stock_data = data.copy() - stock_data["up"] = demark_df.TD_SEQ_UPa - stock_data["down"] = demark_df.TD_SEQ_DNa - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "demark", - stock_data, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -# pylint: disable=too-many-arguments,R0913 -@log_start_end(log=logger) -def display_ichimoku( - data: pd.DataFrame, - symbol: str = "", - conversion_period: int = 9, - base_period: int = 26, - lagging_line_period: int = 52, - displacement: int = 26, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots Ichimoku clouds - - Parameters - ---------- - data: pd.DataFrame - OHLC data - conversion_period: int - Conversion line period - base_period: int - Base line period - lagging_line_period: int - Lagging line period - displacement: int - Displacement variable - symbol: str - Ticker symbol - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load(symbol="aapl") - >>> openbb.ta.ichimoku_clouds(data=df) - """ - - data = pd.DataFrame(data) - data.index.name = "date" - - if ta_helpers.check_columns(data) is None: - return None - - ta = PlotlyTA() - fig = ta.plot( - data, - dict( - ichimoku=dict( - conversion_period=conversion_period, - base_period=base_period, - lagging_line_period=lagging_line_period, - displacement=displacement, - ) - ), - f"Ichimoku Clouds for {symbol.upper()}", - True, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "ichimoku", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/common/technical_analysis/overlap_model.py b/openbb_terminal/common/technical_analysis/overlap_model.py deleted file mode 100644 index 3d2348a5d06..00000000000 --- a/openbb_terminal/common/technical_analysis/overlap_model.py +++ /dev/null @@ -1,163 +0,0 @@ -"""Overlap Technical Analysis""" - -___docformat__ = "numpy" - -import logging - -import pandas as pd -import pandas_ta as ta - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -WINDOW_LENGTHS = [20, 50] -WINDOW_LENGTHS2 = [10, 20] - - -@log_start_end(log=logger) -def ema(data: pd.Series, length: int = 50, offset: int = 0) -> pd.DataFrame: - """Gets exponential moving average (EMA) for stock - - Parameters - ---------- - data: pd.Series - Dataframe of dates and prices - length: int - Length of EMA window - offset: int - Length of offset - - Returns - ------- - pd.DataFrame - Dataframe containing prices and EMA - """ - if isinstance(data, pd.DataFrame): - console.print("[red]Please send a series and not a DataFrame.[/red]\n") - return pd.DataFrame() - return pd.DataFrame(ta.ema(data, length=length, offset=offset)).dropna() - - -@log_start_end(log=logger) -def sma(data: pd.Series, length: int = 50, offset: int = 0) -> pd.DataFrame: - """Gets simple moving average (SMA) for stock - - Parameters - ---------- - data: pd.Series - Dataframe of dates and prices - length: int - Length of SMA window - offset: int - Length of offset - - Returns - ------- - pd.DataFrame - Dataframe containing prices and SMA - """ - if isinstance(data, pd.DataFrame): - console.print("[red]Please send a series and not a DataFrame.[/red]\n") - return pd.DataFrame() - return pd.DataFrame(ta.sma(data, length=length, offset=offset)).dropna() - - -@log_start_end(log=logger) -def wma(data: pd.Series, length: int = 50, offset: int = 0) -> pd.DataFrame: - """Gets weighted moving average (WMA) for stock - - Parameters - ---------- - data: pd.Series - Dataframe of dates and prices - length: int - Length of SMA window - offset: int - Length of offset - - Returns - ------- - df_ta: pd.DataFrame - Dataframe containing prices and WMA - """ - if isinstance(data, pd.DataFrame): - console.print("[red]Please send a series and not a DataFrame.[/red]\n") - return pd.DataFrame() - return pd.DataFrame(ta.wma(data, length=length, offset=offset)).dropna() - - -@log_start_end(log=logger) -def hma(data: pd.Series, length: int = 50, offset: int = 0) -> pd.DataFrame: - """Gets hull moving average (HMA) for stock - - Parameters - ---------- - data: pd.Series - Dataframe of dates and prices - length: int - Length of SMA window - offset: int - Length of offset - - Returns - ------- - df_ta: pd.DataFrame - Dataframe containing prices and HMA - """ - if isinstance(data, pd.DataFrame): - console.print("[red]Please send a series and not a DataFrame.[/red]\n") - return pd.DataFrame() - return pd.DataFrame(ta.hma(data, length=length, offset=offset)).dropna() - - -@log_start_end(log=logger) -def zlma(data: pd.Series, length: int = 50, offset: int = 0) -> pd.DataFrame: - """Gets zero-lagged exponential moving average (ZLEMA) for stock - - Parameters - ---------- - data: pd.Series - Dataframe of dates and prices - length: int - Length of EMA window - offset: int - Length of offset - - Returns - ------- - df_ta: pd.DataFrame - Dataframe containing prices and EMA - """ - if isinstance(data, pd.DataFrame): - console.print("[red]Please send a series and not a DataFrame.[/red]\n") - return pd.DataFrame() - return pd.DataFrame(ta.zlma(data, length=length, offset=offset)).dropna() - - -@log_start_end(log=logger) -def vwap(data: pd.DataFrame, offset: int = 0) -> pd.DataFrame: - """Gets volume weighted average price (VWAP) - - Parameters - ---------- - data: pd.DataFrame - Dataframe of dates and prices - offset: int - Length of offset - Returns - ------- - df_vwap: pd.DataFrame - Dataframe with VWAP data - """ - - df_vwap = ta.vwap( - high=data["High"], - low=data["Low"], - close=data["Close"], - volume=data["Volume"], - offset=offset, - ) - - return pd.DataFrame(df_vwap) diff --git a/openbb_terminal/common/technical_analysis/overlap_view.py b/openbb_terminal/common/technical_analysis/overlap_view.py deleted file mode 100644 index 58c3596cec5..00000000000 --- a/openbb_terminal/common/technical_analysis/overlap_view.py +++ /dev/null @@ -1,166 +0,0 @@ -"""TA Overlap View""" - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import List, Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def view_ma( - data: pd.Series, - window: Optional[List[int]] = None, - offset: int = 0, - ma_type: str = "EMA", - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots MA technical indicator - - Parameters - ---------- - data: pd.Series - Series of prices - window: List[int] - Length of EMA window - offset: int - Offset variable - ma_type: str - Type of moving average. Either "EMA" "ZLMA" or "SMA" - symbol: str - Ticker - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.load("AAPL") - >>> openbb.ta.ma_chart(data=df["Adj Close"], symbol="AAPL", ma_type="EMA", window=[20, 50, 100]) - - - >>> from openbb_terminal.sdk import openbb - >>> spuk_index = openbb.economy.index(indices = ["^SPUK"]) - >>> openbb.ta.ma_chart(data = spuk_index["^SPUK"], symbol = "S&P UK Index", ma_type = "EMA", window = [20, 50, 100]) - """ - # Define a dataframe for adding EMA series to it - price_df = pd.DataFrame(data) - price_df.index.name = "date" - - if not window: - window = [50] - - ta = PlotlyTA() - fig = ta.plot( - data, - {f"{ma_type.lower()}": dict(length=window, offset=offset)}, - f"{symbol.upper()} {ma_type.upper()}", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - f"{ma_type.lower()}{'_'.join([str(win) for win in window])}", # type: ignore - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def view_vwap( - data: pd.DataFrame, - symbol: str = "", - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - offset: int = 0, - interval: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots VWMA technical indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices - symbol : str - Ticker - offset : int - Offset variable - start_date: Optional[str] - Initial date, format YYYY-MM-DD - end_date: Optional[str] - Final date, format YYYY-MM-DD - interval : str - Interval of data - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - data.index = data.index.tz_localize(None) - - if start_date is None: - start = data.index[0].date() - console.print(f"No start date specified. Start date: {start}") - else: - start = datetime.date(start_date) - - if end_date is None: - end = data.index[-1].date() - console.print(f"No end date specified. End date: {end}") - else: - end = datetime.date(end_date) - - day_df = data[(start <= data.index.date) & (data.index.date <= end)] - - if len(day_df) == 0: - return console.print( - f"[red]No data found between {start.strftime('%Y-%m-%d')} and {end.strftime('%Y-%m-%d')}[/red]" - ) - - ta = PlotlyTA() - fig = ta.plot( - day_df, - dict(vwap=dict(offset=offset)), - f"{symbol.upper()} {interval} VWAP", - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "VWAP", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/common/technical_analysis/ta_helpers.py b/openbb_terminal/common/technical_analysis/ta_helpers.py deleted file mode 100644 index 4e3026545e3..00000000000 --- a/openbb_terminal/common/technical_analysis/ta_helpers.py +++ /dev/null @@ -1,50 +0,0 @@ -import logging -import re -from typing import Optional - -import pandas as pd - -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def check_columns( - data: pd.DataFrame, high: bool = True, low: bool = True, close: bool = True -) -> Optional[str]: - """Returns the close columns, or None if the dataframe does not have required columns - - Parameters - ---------- - data: pd.DataFrame - The dataframe to check - high: bool - Whether to check for high column - low: bool - Whether to check for low column - close: bool - Whether to check for close column - - Returns - ------- - Optional[str] - The name of the close column, none if df is invalid - - """ - close_regex = r"(Adj\sClose|adj_close|Close)" - # pylint: disable=too-many-boolean-expressions - if ( - (re.findall(r"High", str(data.columns), re.IGNORECASE) is None and high) - or (re.findall(r"Low", str(data.columns), re.IGNORECASE) is None and low) - or (close_col := re.findall(close_regex, str(data.columns), re.IGNORECASE)) - is None - and close - ): - logger.error("Invalid columns. data has columns %s", data.columns) - console.print( - "[red] Please make sure that the columns 'High', 'Low', and 'Close'" - " are in the dataframe.[/red]" - ) - return None - - return [col for col in close_col if col in data.columns][-1] diff --git a/openbb_terminal/common/technical_analysis/trend_indicators_model.py b/openbb_terminal/common/technical_analysis/trend_indicators_model.py deleted file mode 100644 index 405caf4b2c2..00000000000 --- a/openbb_terminal/common/technical_analysis/trend_indicators_model.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Trend Indicators Technical Analysis Model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd -import pandas_ta as ta - -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def adx( - data: pd.DataFrame, - window: int = 14, - scalar: int = 100, - drift: int = 1, -) -> pd.DataFrame: - """ADX technical indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe with OHLC price data - window: int - Length of window - scalar: int - Scalar variable - drift: int - Drift variable - - Returns - ------- - pd.DataFrame - DataFrame with adx indicator - """ - close_col = ta_helpers.check_columns(data) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.adx( - high=data["High"], - low=data["Low"], - close=data[close_col], - length=window, - scalar=scalar, - drift=drift, - ).dropna() - ) - - -@log_start_end(log=logger) -def aroon(data: pd.DataFrame, window: int = 25, scalar: int = 100) -> pd.DataFrame: - """Aroon technical indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe with OHLC price data - window : int - Length of window - scalar : int - Scalar variable - - Returns - ------- - pd.DataFrame - DataFrame with aroon indicator - """ - close_col = ta_helpers.check_columns(data, close=False) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.aroon( - high=data["High"], - low=data["Low"], - length=window, - scalar=scalar, - ).dropna() - ) diff --git a/openbb_terminal/common/technical_analysis/trend_indicators_view.py b/openbb_terminal/common/technical_analysis/trend_indicators_view.py deleted file mode 100644 index bbfea3936fc..00000000000 --- a/openbb_terminal/common/technical_analysis/trend_indicators_view.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Trend Indicators View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_adx( - data: pd.DataFrame, - window: int = 14, - scalar: int = 100, - drift: int = 1, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Plots ADX indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe with OHLC price data - window : int - Length of window - scalar : int - Scalar variable - drift : int - Drift variable - symbol : str - Ticker - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(adx=dict(length=window, scalar=scalar, drift=drift)), - f"Average Directional Movement Index (ADX) on {symbol}", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "adx", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_aroon( - data: pd.DataFrame, - window: int = 25, - scalar: int = 100, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Plots Aroon indicator - - Parameters - ---------- - data: pd.DataFrame - Dataframe with OHLC price data - window: int - Length of window - symbol: str - Ticker - scalar: int - Scalar variable - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(aroon=dict(length=window, scalar=scalar)), - f"Aroon on {symbol}", - False, - volume=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "aroon", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/common/technical_analysis/volatility_model.py b/openbb_terminal/common/technical_analysis/volatility_model.py deleted file mode 100644 index e4c3355f93b..00000000000 --- a/openbb_terminal/common/technical_analysis/volatility_model.py +++ /dev/null @@ -1,694 +0,0 @@ -"""Volatility Technical Indicators""" - -__docformat__ = "numpy" - -import logging -from typing import Optional - -import numpy as np -import pandas as pd -import pandas_ta as ta - -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -MAMODES = ["ema", "sma", "wma", "hma", "zlma"] - -# These are parameters for the volatility models -VOLATILITY_MODELS = [ - "STD", - "Parkinson", - "Garman-Klass", - "Hodges-Tompkins", - "Rogers-Satchell", - "Yang-Zhang", -] - - -@log_start_end(log=logger) -def bbands( - data: pd.DataFrame, window: int = 15, n_std: float = 2, mamode: str = "ema" -) -> pd.DataFrame: - """Calculate Bollinger Bands - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - window : int - Length of window to calculate BB - n_std : float - Number of standard deviations to show - mamode : str - Method of calculating average - - Returns - ------- - df_ta: pd.DataFrame - Dataframe of bollinger band data - """ - close_col = ta_helpers.check_columns(data, high=False, low=False) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.bbands( - close=data[close_col], - length=window, - std=n_std, - mamode=mamode, - ) - ).dropna() - - -@log_start_end(log=logger) -def donchian( - data: pd.DataFrame, - upper_length: int = 20, - lower_length: int = 20, -) -> pd.DataFrame: - """Calculate Donchian Channels - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - upper_length : int - Length of window to calculate upper channel - lower_length : int - Length of window to calculate lower channel - - Returns - ------- - pd.DataFrame - Dataframe of upper and lower channels - """ - close_col = ta_helpers.check_columns(data, close=False) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.donchian( - high=data["High"], - low=data["Low"], - upper_length=upper_length, - lower_length=lower_length, - ).dropna() - ) - - -@log_start_end(log=logger) -def kc( - data: pd.DataFrame, - window: int = 20, - scalar: float = 2, - mamode: str = "ema", - offset: int = 0, -) -> pd.DataFrame: - """Keltner Channels - - Parameters - ---------- - data: pd.DataFrame - Dataframe of ohlc prices - window : int - Length of window - scalar: float - Scalar value - mamode: str - Type of filter - offset : int - Offset value - - Returns - ------- - pd.DataFrame - Dataframe of rolling kc - """ - close_col = ta_helpers.check_columns(data) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.kc( - high=data["High"], - low=data["Low"], - close=data[close_col], - length=window, - scalar=scalar, - mamode=mamode, - offset=offset, - ).dropna() - ) - - -@log_start_end(log=logger) -def atr( - data: pd.DataFrame, - window: int = 14, - mamode: str = "ema", - offset: int = 0, -) -> pd.DataFrame: - """Average True Range - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - window : int - Length of window - mamode: str - Type of filter - offset : int - Offset value - - Returns - ------- - pd.DataFrame - Dataframe of atr - """ - close_col = ta_helpers.check_columns(data) - if close_col is None: - return pd.DataFrame() - return pd.DataFrame( - ta.atr( - high=data["High"], - low=data["Low"], - close=data[close_col], - length=window, - mamode=mamode, - offset=offset, - ).dropna() - ) - - -@log_start_end(log=logger) -def standard_deviation( - data: pd.DataFrame, - window: int = 30, - trading_periods: Optional[int] = None, - is_crypto: bool = False, - clean: bool = True, -) -> pd.DataFrame: - """Standard deviation measures how widely returns are dispersed from the average return. - It is the most common (and biased) estimator of volatility. - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices. - window : int [default: 30] - Length of window to calculate over. - trading_periods : Optional[int] [default: 252] - Number of trading periods in a year. - is_crypto : bool [default: False] - If true, trading_periods is defined as 365. - clean : bool [default: True] - Whether to clean the data or not by dropping NaN values. - - Returns - ------- - pd.DataFrame : results - Dataframe with results. - - Example - ------- - >>> data = openbb.stocks.load('SPY') - >>> df = openbb.ta.standard_deviation(data) - """ - - if window < 2: - console.print("Error: Window must be at least 2, defaulting to 30.") - window = 30 - - if trading_periods and is_crypto: - console.print("is_crypto is overridden by trading_periods.") - - if not trading_periods: - trading_periods = 365 if is_crypto else 252 - - log_return = (data["Close"] / data["Close"].shift(1)).apply(np.log) - - result = log_return.rolling(window=window, center=False).std() * np.sqrt( - trading_periods - ) - - if clean: - return result.dropna() - - return result - - -@log_start_end(log=logger) -def parkinson( - data: pd.DataFrame, - window: int = 30, - trading_periods: Optional[int] = None, - is_crypto: bool = False, - clean=True, -) -> pd.DataFrame: - """Parkinson volatility uses the high and low price of the day rather than just close to close prices. - It is useful for capturing large price movements during the day. - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices. - window : int [default: 30] - Length of window to calculate over. - trading_periods : Optional[int] [default: 252] - Number of trading periods in a year. - is_crypto : bool [default: False] - If true, trading_periods is defined as 365. - clean : bool [default: True] - Whether to clean the data or not by dropping NaN values. - - Returns - ------- - pd.DataFrame : results - Dataframe with results. - - Example - ------- - >>> data = openbb.stocks.load('BTC-USD') - >>> df = openbb.ta.rvol_parkinson(data, is_crypto = True) - """ - - if window < 1: - console.print("Error: Window must be at least 1, defaulting to 30.") - window = 30 - - if trading_periods and is_crypto: - console.print("is_crypto is overridden by trading_periods.") - - if not trading_periods: - trading_periods = 365 if is_crypto else 252 - - rs = (1.0 / (4.0 * np.log(2.0))) * ( - (data["High"] / data["Low"]).apply(np.log) - ) ** 2.0 - - def f(v): - return (trading_periods * v.mean()) ** 0.5 - - result = rs.rolling(window=window, center=False).apply(func=f) - - if clean: - return result.dropna() - - return result - - -@log_start_end(log=logger) -def garman_klass( - data: pd.DataFrame, - window: int = 30, - trading_periods: Optional[int] = None, - is_crypto: bool = False, - clean=True, -) -> pd.DataFrame: - """Garman-Klass volatility extends Parkinson volatility by taking into account the opening and closing price. - As markets are most active during the opening and closing of a trading session. - It makes volatility estimation more accurate. - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices. - window : int [default: 30] - Length of window to calculate overn. - trading_periods : Optional[int] [default: 252] - Number of trading periods in a year. - is_crypto : bool [default: False] - If true, trading_periods is defined as 365. - clean : bool [default: True] - Whether to clean the data or not by dropping NaN values. - - Returns - ------- - pd.DataFrame : results - Dataframe with results. - - Example - ------- - >>> data = openbb.stocks.load('BTC-USD') - >>> df = openbb.ta.rvol_garman_klass(data, is_crypto = True) - """ - - if window < 1: - console.print("Error: Window must be at least 1, defaulting to 30.") - window = 30 - - if trading_periods and is_crypto: - console.print("is_crypto is overridden by trading_periods.") - - if not trading_periods: - trading_periods = 365 if is_crypto else 252 - - log_hl = (data["High"] / data["Low"]).apply(np.log) - log_co = (data["Close"] / data["Open"]).apply(np.log) - - rs = 0.5 * log_hl**2 - (2 * np.log(2) - 1) * log_co**2 - - def f(v): - return (trading_periods * v.mean()) ** 0.5 - - result = rs.rolling(window=window, center=False).apply(func=f) - - if clean: - return result.dropna() - - return result - - -@log_start_end(log=logger) -def hodges_tompkins( - data: pd.DataFrame, - window: int = 30, - trading_periods: Optional[int] = None, - is_crypto: bool = False, - clean=True, -) -> pd.DataFrame: - """Hodges-Tompkins volatility is a bias correction for estimation using an overlapping data sample. - It produces unbiased estimates and a substantial gain in efficiency. - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices. - window : int [default: 30] - Length of window to calculate over. - trading_periods : Optional[int] [default: 252] - Number of trading periods in a year. - is_crypto : bool [default: False] - If true, trading_periods is defined as 365. - clean : bool [default: True] - Whether to clean the data or not by dropping NaN values. - - Returns - ------- - pd.DataFrame : results - Dataframe with results. - - Example - ------- - >>> data = openbb.stocks.load('BTC-USD') - >>> df = openbb.ta.rvol_hodges_tompkins(data, is_crypto = True) - """ - - if window < 2: - console.print("Error: Window must be at least 2, defaulting to 30.") - window = 30 - - if trading_periods and is_crypto: - console.print("is_crypto is overridden by trading_periods.") - - if not trading_periods: - trading_periods = 365 if is_crypto else 252 - - log_return = (data["Close"] / data["Close"].shift(1)).apply(np.log) - - vol = log_return.rolling(window=window, center=False).std() * np.sqrt( - trading_periods - ) - - h = window - n = (log_return.count() - h) + 1 - - adj_factor = 1.0 / (1.0 - (h / n) + ((h**2 - 1) / (3 * n**2))) - - result = vol * adj_factor - - if clean: - return result.dropna() - - return result - - -@log_start_end(log=logger) -def rogers_satchell( - data: pd.DataFrame, - window: int = 30, - trading_periods: Optional[int] = None, - is_crypto: bool = False, - clean=True, -) -> pd.Series: - """Rogers-Satchell is an estimator for measuring the volatility with an average return not equal to zero. - Unlike Parkinson and Garman-Klass estimators, Rogers-Satchell incorporates a drift term, - mean return not equal to zero. - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices. - window : int [default: 30] - Length of window to calculate over. - trading_periods : Optional[int] [default: 252] - Number of trading periods in a year. - is_crypto : bool [default: False] - If true, trading_periods is defined as 365. - clean : bool [default: True] - Whether to clean the data or not by dropping NaN values. - - Returns - ------- - pd.Series : results - Pandas Series with results. - - Example - ------- - >>> data = openbb.stocks.load('BTC-USD') - >>> df = openbb.ta.rvol_rogers_satchell(data, is_crypto = True) - """ - - if window < 1: - console.print("Error: Window must be at least 1, defaulting to 30.") - window = 30 - - if trading_periods and is_crypto: - console.print("is_crypto is overridden by trading_periods.") - - if not trading_periods: - trading_periods = 365 if is_crypto else 252 - - log_ho = (data["High"] / data["Open"]).apply(np.log) - log_lo = (data["Low"] / data["Open"]).apply(np.log) - log_co = (data["Close"] / data["Open"]).apply(np.log) - - rs = log_ho * (log_ho - log_co) + log_lo * (log_lo - log_co) - - def f(v): - return (trading_periods * v.mean()) ** 0.5 - - result = rs.rolling(window=window, center=False).apply(func=f) - - if clean: - return result.dropna() - - return result - - -def yang_zhang( - data: pd.DataFrame, - window: int = 30, - trading_periods: Optional[int] = None, - is_crypto: bool = False, - clean=True, -) -> pd.DataFrame: - """Yang-Zhang volatility is the combination of the overnight (close-to-open volatility). - It is a weighted average of the Rogers-Satchell volatility and the open-to-close volatility. - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices. - window : int [default: 30] - Length of window to calculate standard deviation. - trading_periods : Optional[int] [default: 252] - Number of trading periods in a year. - is_crypto : bool [default: False] - If true, trading_periods is defined as 365. - clean : bool [default: True] - Whether to clean the data or not by dropping NaN values. - - Returns - ------- - pd.DataFrame : results - Dataframe with results. - - Example - ------- - >>> data = openbb.stocks.load('BTC-USD') - >>> df = openbb.ta.rvol_yang_zhang(data, is_crypto = True) - """ - - if window < 2: - console.print("Error: Window must be at least 2, defaulting to 30.") - window = 30 - - if trading_periods and is_crypto: - console.print("is_crypto is overridden by trading_periods.") - - if not trading_periods: - trading_periods = 365 if is_crypto else 252 - - log_ho = (data["High"] / data["Open"]).apply(np.log) - log_lo = (data["Low"] / data["Open"]).apply(np.log) - log_co = (data["Close"] / data["Open"]).apply(np.log) - - log_oc = (data["Open"] / data["Close"].shift(1)).apply(np.log) - log_oc_sq = log_oc**2 - - log_cc = (data["Close"] / data["Close"].shift(1)).apply(np.log) - log_cc_sq = log_cc**2 - - rs = log_ho * (log_ho - log_co) + log_lo * (log_lo - log_co) - - close_vol = log_cc_sq.rolling(window=window, center=False).sum() * ( - 1.0 / (window - 1.0) - ) - open_vol = log_oc_sq.rolling(window=window, center=False).sum() * ( - 1.0 / (window - 1.0) - ) - window_rs = rs.rolling(window=window, center=False).sum() * (1.0 / (window - 1.0)) - - k = 0.34 / (1.34 + (window + 1) / (window - 1)) - result = (open_vol + k * close_vol + (1 - k) * window_rs).apply(np.sqrt) * np.sqrt( - trading_periods - ) - - if clean: - return result.dropna() - - return result - - -@log_start_end(log=logger) -def cones( - data: pd.DataFrame, - lower_q: float = 0.25, - upper_q: float = 0.75, - is_crypto: bool = False, - model: str = "STD", -) -> pd.DataFrame: - """Returns a DataFrame of realized volatility quantiles. - - Parameters - --------- - data: pd.DataFrame - DataFrame of the OHLC data. - lower_q: float (default = 0.25) - The lower quantile to calculate the realized volatility over time for. - upper_q: float (default = 0.75) - The upper quantile to calculate the realized volatility over time for. - is_crypto: bool (default = False) - If true, volatility is calculated for 365 days instead of 252. - model: str (default = "STD") - The model to use for volatility calculation. Choices are: - ["STD", "Parkinson", "Garman-Klass", "Hodges-Tompkins", "Rogers-Satchell", "Yang-Zhang"] - - Standard deviation measures how widely returns are dispersed from the average return. - It is the most common (and biased) estimator of volatility. - - Parkinson volatility uses the high and low price of the day rather than just close to close prices. - It is useful for capturing large price movements during the day. - - Garman-Klass volatility extends Parkinson volatility by taking into account the opening and closing price. - As markets are most active during the opening and closing of a trading session, - it makes volatility estimation more accurate. - - Hodges-Tompkins volatility is a bias correction for estimation using an overlapping data sample. - It produces unbiased estimates and a substantial gain in efficiency. - - Rogers-Satchell is an estimator for measuring the volatility with an average return not equal to zero. - Unlike Parkinson and Garman-Klass estimators, Rogers-Satchell incorporates a drift term, - mean return not equal to zero. - - Yang-Zhang volatility is the combination of the overnight (close-to-open volatility). - It is a weighted average of the Rogers-Satchell volatility and the open-to-close volatility. - - Returns - ------- - pd.DataFrame: cones - DataFrame of realized volatility quantiles. - - Example - ------- - >>> df = openbb.stocks.load("AAPL") - >>> cones_df = openbb.ta.cones(data = df, lower_q = 0.10, upper_q = 0.90) - - >>> cones_df = openbb.ta.cones(df,0.15,0.85,False,"Garman-Klass") - - """ - estimator = pd.DataFrame() - - if lower_q > upper_q: - lower_q, upper_q = upper_q, lower_q - - if (lower_q >= 1) or (upper_q >= 1): - console.print("Error: lower_q and upper_q must be between 0 and 1") - cones_df = pd.DataFrame() - return cones_df - - try: - lower_q_label = str(int(lower_q * 100)) - upper_q_label = str(int(upper_q * 100)) - quantiles = [lower_q, upper_q] - windows = [3, 10, 30, 60, 90, 120, 150, 180, 210, 240, 300, 360] - min_ = [] - max_ = [] - median = [] - top_q = [] - bottom_q = [] - realized = [] - data = data.sort_index(ascending=False) - - for window in windows: - # Looping to build a dataframe with realized volatility over each window. - if model not in VOLATILITY_MODELS: - console.print( - "Model not available. Available models: ", VOLATILITY_MODELS - ) - elif model == "STD": - estimator = standard_deviation( - window=window, data=data, is_crypto=is_crypto - ) - elif model == "Parkinson": - estimator = parkinson(window=window, data=data, is_crypto=is_crypto) - elif model == "Garman-Klass": - estimator = garman_klass(window=window, data=data, is_crypto=is_crypto) - elif model == "Hodges-Tompkins": - estimator = hodges_tompkins( - window=window, data=data, is_crypto=is_crypto - ) - elif model == "Rogers-Satchell": - estimator = rogers_satchell( - window=window, data=data, is_crypto=is_crypto - ) - elif model == "Yang-Zhang": - estimator = yang_zhang(window=window, data=data, is_crypto=is_crypto) - min_.append(estimator.min()) - max_.append(estimator.max()) - median.append(estimator.median()) - top_q.append(estimator.quantile(quantiles[1])) - bottom_q.append(estimator.quantile(quantiles[0])) - realized.append(estimator[-1]) - df_ = [realized, min_, bottom_q, median, top_q, max_] - df_windows = windows - df = pd.DataFrame(df_, columns=df_windows) - df = df.rename( - index={ - 0: "Realized", - 1: "Min", - 2: "Lower " f"{lower_q_label}" "%", - 3: "Median", - 4: "Upper " f"{upper_q_label}" "%", - 5: "Max", - } - ) - cones_df = df.copy() - return cones_df.transpose() - - except Exception: - cones_df = pd.DataFrame() - return cones_df diff --git a/openbb_terminal/common/technical_analysis/volatility_view.py b/openbb_terminal/common/technical_analysis/volatility_view.py deleted file mode 100644 index 783c0058dd6..00000000000 --- a/openbb_terminal/common/technical_analysis/volatility_view.py +++ /dev/null @@ -1,349 +0,0 @@ -"""Volatility Technical Indicators View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.common.technical_analysis import ta_helpers, volatility_model -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, -) - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_bbands( - data: pd.DataFrame, - symbol: str = "", - window: int = 15, - n_std: float = 2, - mamode: str = "sma", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots bollinger bands - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - symbol : str - Ticker symbol - window : int - Length of window to calculate BB - n_std : float - Number of standard deviations to show - mamode : str - Method of calculating average - export : str - Format of export file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(bbands=dict(length=window, std=n_std, mamode=mamode)), - f"{symbol.upper()} Bollinger Bands", - False, - volume=False, - ) - - if ta_helpers.check_columns(data, high=False, low=False) is None: - return None - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "bbands", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_donchian( - data: pd.DataFrame, - symbol: str = "", - upper_length: int = 20, - lower_length: int = 20, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots donchian channels - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - symbol : str - Ticker symbol - upper_length : int - Length of window to calculate upper channel - lower_length : int - Length of window to calculate lower channel - export : str - Format of export file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(donchian=dict(upper_length=upper_length, lower_length=lower_length)), - f"{symbol.upper()} Donchian Channels", - False, - volume=False, - ) - - if ta_helpers.check_columns(data) is None: - return None - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "donchian", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def view_kc( - data: pd.DataFrame, - window: int = 20, - scalar: float = 2, - mamode: str = "ema", - offset: int = 0, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots Keltner Channels Indicator - - Parameters - ---------- - - data: pd.DataFrame - Dataframe of ohlc prices - window: int - Length of window - scalar: float - Scalar value - mamode: str - Type of filter - offset: int - Offset value - symbol: str - Ticker symbol - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(kc=dict(length=window, scalar=scalar, mamode=mamode, offset=offset)), - f"{symbol.upper()} Keltner Channels", - False, - volume=False, - ) - - if ta_helpers.check_columns(data) is None: - return None - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "kc", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_atr( - data: pd.DataFrame, - symbol: str = "", - window: int = 14, - mamode: str = "sma", - offset: int = 0, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots ATR - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - symbol : str - Ticker symbol - window : int - Length of window to calculate upper channel - export : str - Format of export file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(atr=dict(length=window, mamode=mamode, offset=offset)), - f"{symbol.upper()} Average True Range", - False, - volume=False, - ) - - if ta_helpers.check_columns(data) is None: - return None - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "atr", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_cones( - data: pd.DataFrame, - symbol: str = "", - lower_q: float = 0.25, - upper_q: float = 0.75, - model: str = "STD", - is_crypto: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots the realized volatility quantiles for the loaded ticker. - The model used to calculate the volatility is selectable. - - Parameters - ---------- - data: pd.DataFrame - DataFrame of OHLC prices. - symbol: str (default = "") - The ticker symbol. - lower_q: float (default = 0.25) - The lower quantile to calculate for. - upper_q: float (default = 0.75) - The upper quantile to for. - is_crypto: bool (default = False) - If true, volatility is calculated for 365 days instead of 252. - model: str (default = "STD") - The model to use for volatility calculation. Choices are: - ["STD", "Parkinson", "Garman-Klass", "Hodges-Tompkins", "Rogers-Satchell", "Yang-Zhang"] - - Standard deviation measures how widely returns are dispersed from the average return. - It is the most common (and biased) estimator of volatility. - - Parkinson volatility uses the high and low price of the day rather than just close to close prices. - It is useful for capturing large price movements during the day. - - Garman-Klass volatility extends Parkinson volatility by taking into account the opening and closing price. - As markets are most active during the opening and closing of a trading session; - it makes volatility estimation more accurate. - - Hodges-Tompkins volatility is a bias correction for estimation using an overlapping data sample. - It produces unbiased estimates and a substantial gain in efficiency. - - Rogers-Satchell is an estimator for measuring the volatility with an average return not equal to zero. - Unlike Parkinson and Garman-Klass estimators, Rogers-Satchell incorporates a drift term, - mean return not equal to zero. - - Yang-Zhang volatility is the combination of the overnight (close-to-open volatility). - It is a weighted average of the Rogers-Satchell volatility and the open-to-close volatility. - export : str - Format of export file - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - df_ta = openbb.stocks.load('XLY') - openbb.ta.cones_chart(data = df_ta, symbol = 'XLY') - - df_ta = openbb.stocks.load('XLE') - openbb.ta.cones_chart(data = df_ta, symbol = "XLE", lower_q = 0.10, upper_q = 0.90) - - openbb.ta.cones_chart(data = df_ta, symbol = "XLE", model = "Garman-Klass") - """ - - if lower_q > upper_q: - lower_q, upper_q = upper_q, lower_q - - df_ta = volatility_model.cones( - data, lower_q=lower_q, upper_q=upper_q, is_crypto=is_crypto, model=model - ) - lower_q_label = str(int(lower_q * 100)) - upper_q_label = str(int(upper_q * 100)) - - if df_ta.empty: - return None - - fig = OpenBBFigure(xaxis_title="Window of Time (in days)") - fig.set_title(f"{symbol} - Realized Volatility Cones - {model} Model", x=0.5) - - fig.add_scatter(x=df_ta.index, y=df_ta.Min, name="Min") - fig.add_scatter(x=df_ta.index, y=df_ta.Max, name="Max") - fig.add_scatter(x=df_ta.index, y=df_ta.Median, name="Median") - fig.add_scatter( - x=df_ta.index, - y=df_ta["Upper " f"{upper_q_label}" "%"], - name="Upper " f"{upper_q_label}" "%", - ) - fig.add_scatter( - x=df_ta.index, - y=df_ta["Lower " f"{lower_q_label}" "%"], - name="Lower " f"{lower_q_label}" "%", - ) - fig.add_scatter(x=df_ta.index, y=df_ta.Realized, name="Realized") - - fig.update_xaxes(tickmode="array", tickvals=df_ta.index, ticktext=df_ta.index) - fig.horizontal_legend(x=1, y=1, yanchor="top") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "cones", - df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/common/technical_analysis/volume_model.py b/openbb_terminal/common/technical_analysis/volume_model.py deleted file mode 100644 index d1c7ac1e486..00000000000 --- a/openbb_terminal/common/technical_analysis/volume_model.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Volume Technical Analysis""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd -import pandas_ta as ta - -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def ad(data: pd.DataFrame, use_open: bool = False) -> pd.DataFrame: - """Calculate AD technical indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe of prices with OHLC and Volume - use_open : bool - Whether to use open prices - - Returns - ------- - pd.DataFrame - Dataframe with technical indicator - """ - kwargs = {} - if use_open: - kwargs["Open"] = data["Open"] - df_ta = ta.ad( - high=data["High"], - low=data["Low"], - close=data["Close"], - volume=data["Volume"], - **kwargs - ).dropna() - return pd.DataFrame(df_ta) - - -@log_start_end(log=logger) -def adosc( - data: pd.DataFrame, use_open: bool = False, fast: int = 3, slow: int = 10 -) -> pd.DataFrame: - """Calculate AD oscillator technical indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe of OHLC prices - use_open : bool - Whether to use open prices - fast: int - Fast value - slow: int - Slow value - - Returns - ------- - pd.DataFrame - Dataframe with technical indicator - """ - if use_open: - df_ta = ta.adosc( - high=data["High"], - low=data["Low"], - close=data["Close"], - volume=data["Volume"], - open_=data["Open"], - fast=fast, - slow=slow, - ).dropna() - else: - df_ta = ta.adosc( - high=data["High"], - low=data["Low"], - close=data["Close"], - volume=data["Volume"], - fast=fast, - slow=slow, - ).dropna() - return pd.DataFrame(df_ta) - - -@log_start_end(log=logger) -def obv(data: pd.DataFrame) -> pd.DataFrame: - """On Balance Volume - - Parameters - ---------- - data: pd.DataFrame - Dataframe of OHLC prices - - Returns - ------- - pd.DataFrame - Dataframe with technical indicator - """ - close_col = ta_helpers.check_columns(data, high=False, low=False) - if close_col is None: - return pd.DataFrame() - if "Volume" not in data.columns: - console.print("[red]Volume column not found[/red]\n") - return pd.DataFrame() - return pd.DataFrame(ta.obv(close=data[close_col], volume=data["Volume"]).dropna()) diff --git a/openbb_terminal/common/technical_analysis/volume_view.py b/openbb_terminal/common/technical_analysis/volume_view.py deleted file mode 100644 index ff225df919b..00000000000 --- a/openbb_terminal/common/technical_analysis/volume_view.py +++ /dev/null @@ -1,153 +0,0 @@ -"""Volume View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_ad( - data: pd.DataFrame, - use_open: bool = False, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots AD technical indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - use_open : bool - Whether to use open prices in calculation - symbol : str - Ticker symbol - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data as - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot(data, dict(ad=dict(use_open=use_open)), f"{symbol.upper()} AD", False) - - if ta_helpers.check_columns(data) is None: - return None - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "ad", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_adosc( - data: pd.DataFrame, - fast: int = 3, - slow: int = 10, - use_open: bool = False, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots AD Osc Indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - use_open : bool - Whether to use open prices in calculation - fast: int - Length of fast window - slow : int - Length of slow window - symbol : str - Stock ticker - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot( - data, - dict(adosc=dict(fast=fast, slow=slow, use_open=use_open)), - f"{symbol.upper()} AD Oscillator", - False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "adosc", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_obv( - data: pd.DataFrame, - symbol: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots OBV technical indicator - - Parameters - ---------- - data : pd.DataFrame - Dataframe of ohlc prices - symbol : str - Ticker - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data as - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - ta = PlotlyTA() - fig = ta.plot(data, dict(obv=dict()), f"{symbol.upper()} OBV", False) - - close_col = ta_helpers.check_columns(data) - if close_col is None: - return None - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"), - "obv", - ta.df_ta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/config_terminal.py b/openbb_terminal/config_terminal.py deleted file mode 100644 index 9b887724578..00000000000 --- a/openbb_terminal/config_terminal.py +++ /dev/null @@ -1,157 +0,0 @@ -# IMPORTATION STANDARD -import copy -from pathlib import Path - -# IMPORTATION THIRDPARTY -from typing import TYPE_CHECKING, Optional, TypeVar - -import i18n - -# IMPORTATION INTERNAL -from openbb_terminal.base_helpers import load_env_files -from openbb_terminal.core.config.paths import I18N_DICT_LOCATION -from openbb_terminal.core.config.paths_helper import init_userdata -from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.session.current_system import ( - get_current_system, - set_system_variable, -) -from openbb_terminal.core.session.current_user import get_current_user - -from .helper_classes import TerminalStyle as _TerminalStyle - -if TYPE_CHECKING: - from openbb_terminal import OpenBBFigure - - -OpenBBFigureT = TypeVar("OpenBBFigureT", bound="OpenBBFigure") -HOLD: bool = False -COMMAND_ON_CHART: bool = True -current_figure: Optional[OpenBBFigureT] = None # type: ignore -new_axis: bool = True -legends = [] -last_legend = "" - - -# pylint: disable=global-statement -def set_last_legend(leg: str): - global last_legend - last_legend = copy.deepcopy(leg) - - -def get_last_legend() -> str: - return copy.deepcopy(last_legend) - - -def append_legend(append_leg: str): - global legends - legends.append(append_leg) - legends = copy.deepcopy(legends) - - -def reset_legend(): - global legends - legends = [] - - -def get_legends() -> list: - return legends - - -def set_same_axis() -> None: - global new_axis - new_axis = False - - -def set_new_axis() -> None: - global new_axis - new_axis = True - - -def make_new_axis() -> bool: - return new_axis - - -def get_current_figure() -> Optional["OpenBBFigure"]: - return current_figure - - -def set_current_figure(fig: Optional[OpenBBFigureT] = None): - # pylint: disable=global-statement - global current_figure - current_figure = fig - - -def start_plot_backend(): - """Starts the plot backend""" - plots_backend().start(get_current_system().DEBUG_MODE) - - -def setup_i18n(i18n_path: Path = I18N_DICT_LOCATION): - """Select the terminal translation language.""" - - i18n.load_path.append(i18n_path) - i18n.set("locale", get_current_user().preferences.USE_LANGUAGE) - i18n.set("filename_format", "{locale}.{format}") - - -def setup_version(): - """Setup the version of the terminal""" - - def try_get_version_from_dist() -> Optional[str]: - def check_using_git(): - try: - __import__("git") - except ImportError: - return False - return True - - if not check_using_git(): - try: - import pkg_resources # pylint:disable=import-outside-toplevel - - return pkg_resources.get_distribution("OpenBB").version - - except Exception: - pass - - return None - - version = try_get_version_from_dist() - - if version: - set_system_variable("VERSION", version) - - -def setup_logging_app_name(): - """ - Setup the logging app name of the terminal - - - if there's a LOGGING_APP_NAME in the environment => use it - - if "site-packages" in __file__ => use "gst_packaged_pypi" (will override environment variable) - - if none of the above => use "gst" (which is the default value on the SystemModel) - """ - - if "site-packages" in __file__: - set_system_variable("LOGGING_APP_NAME", "gst_packaged_pypi") - - -def setup_config_terminal(is_sdk: bool = False): - """Setup the config terminal""" - load_env_files() - init_userdata() - # To avoid starting plots backend twice - if is_sdk: - start_plot_backend() - setup_i18n() - setup_version() - setup_logging_app_name() - - -# Terminal UX section -current_user = get_current_user() -theme = _TerminalStyle( - current_user.preferences.MPL_STYLE, - current_user.preferences.PMF_STYLE, - current_user.preferences.RICH_STYLE, -) diff --git a/openbb_terminal/core/__init__.py b/openbb_terminal/core/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/config/__init__.py b/openbb_terminal/core/config/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/config/paths.py b/openbb_terminal/core/config/paths.py deleted file mode 100644 index 110ffb9db59..00000000000 --- a/openbb_terminal/core/config/paths.py +++ /dev/null @@ -1,32 +0,0 @@ -# IMPORTATION STANDARD -from pathlib import Path - -# Installation related paths -HOME_DIRECTORY = Path.home() -REPOSITORY_DIRECTORY = Path(__file__).parent.parent.parent.parent -PACKAGE_DIRECTORY = Path(__file__).parent.parent.parent -MISCELLANEOUS_DIRECTORY = PACKAGE_DIRECTORY / "miscellaneous" -REPOSITORY_ENV_FILE = REPOSITORY_DIRECTORY / ".env" -PACKAGE_ENV_FILE = PACKAGE_DIRECTORY / ".env" -PLOTS_CORE_DIRECTORY = PACKAGE_DIRECTORY / "core" / "plots" - -SETTINGS_DIRECTORY = HOME_DIRECTORY / ".openbb_terminal" -SETTINGS_ENV_FILE = SETTINGS_DIRECTORY / ".env" -HIST_FILE_PATH = SETTINGS_DIRECTORY / ".openbb_terminal.his" - -# i18n_dict_location -I18N_DICT_LOCATION = MISCELLANEOUS_DIRECTORY / "i18n" - -# styles -STYLES_DIRECTORY_REPO = MISCELLANEOUS_DIRECTORY / "styles" - -# sources -DATA_SOURCES_DEFAULT_FILE = MISCELLANEOUS_DIRECTORY / "sources" / "openbb_default.json" - -# session -SESSION_FILE_PATH = SETTINGS_DIRECTORY / "session.json" - -# sdk trail map paths -MAP_PATH = PACKAGE_DIRECTORY / "core/sdk" / "trail_map.csv" -MAP_FORECASTING_PATH = PACKAGE_DIRECTORY / "core/sdk" / "trail_map_forecasting.csv" -MAP_OPTIMIZATION_PATH = PACKAGE_DIRECTORY / "core/sdk" / "trail_map_optimization.csv" diff --git a/openbb_terminal/core/config/paths_helper.py b/openbb_terminal/core/config/paths_helper.py deleted file mode 100644 index 0a5a08c18e5..00000000000 --- a/openbb_terminal/core/config/paths_helper.py +++ /dev/null @@ -1,80 +0,0 @@ -# IMPORTATION STANDARD -from pathlib import Path -from typing import List - -from openbb_terminal.core.config.paths import ( - REPOSITORY_ENV_FILE, - SETTINGS_DIRECTORY, - SETTINGS_ENV_FILE, -) -from openbb_terminal.core.session.current_user import get_current_user - -# pylint: disable=W0603 - - -def create_paths(list_dirs: List): - """ - Creates dirs for user data outside the - terminal if they don't exist - """ - for dirs in list_dirs: - if not dirs.exists(): - dirs.mkdir( - parents=True, - ) - - -def create_files(list_files: List): - """ - Creates files outside the terminal if they don't exist - """ - for filename in list_files: - if not filename.is_file(): - with open(str(filename), "w"): - pass - - -current_user = get_current_user() - -dirs_list = [ - SETTINGS_DIRECTORY, - current_user.preferences.USER_DATA_DIRECTORY, - current_user.preferences.USER_DATA_DIRECTORY / "styles", - current_user.preferences.USER_DATA_DIRECTORY / "reports", - current_user.preferences.USER_DATA_DIRECTORY / "reports" / "custom reports", - current_user.preferences.USER_DATA_DIRECTORY / "companies_house", - current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY, - current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY / "econometrics", - current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY / "stocks", - current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY / "dashboards", - current_user.preferences.USER_EXPORTS_DIRECTORY, - current_user.preferences.USER_PORTFOLIO_DATA_DIRECTORY, - current_user.preferences.USER_PORTFOLIO_DATA_DIRECTORY / "views", - current_user.preferences.USER_PORTFOLIO_DATA_DIRECTORY / "holdings", - current_user.preferences.USER_PORTFOLIO_DATA_DIRECTORY / "allocation", - current_user.preferences.USER_PORTFOLIO_DATA_DIRECTORY / "optimization", - current_user.preferences.USER_PRESETS_DIRECTORY, - current_user.preferences.USER_PRESETS_DIRECTORY / "stocks" / "options", - current_user.preferences.USER_PRESETS_DIRECTORY / "stocks" / "screener", - current_user.preferences.USER_PRESETS_DIRECTORY / "stocks" / "insider", - current_user.preferences.USER_PRESETS_DIRECTORY / "etf" / "screener", - current_user.preferences.USER_ROUTINES_DIRECTORY, - current_user.preferences.USER_DATA_DIRECTORY / "sources", -] -dirs_files = [ - SETTINGS_ENV_FILE, - REPOSITORY_ENV_FILE, - Path(current_user.preferences.USER_DATA_SOURCES_FILE), -] -initialized = False - - -def init_userdata(): - """ - Initializes the user data folder - """ - global initialized # noqa - if not initialized: - create_paths(dirs_list) - create_files(dirs_files) - initialized = True diff --git a/openbb_terminal/core/integration_tests/README.md b/openbb_terminal/core/integration_tests/README.md deleted file mode 100644 index 7fd725ec68f..00000000000 --- a/openbb_terminal/core/integration_tests/README.md +++ /dev/null @@ -1,296 +0,0 @@ -# OpenBB Terminal : `Integration Testing` - -This document is part of the `OpenBB Terminal` library documentation. - -It aims to provide necessary information in order to: - -- Build `integration tests` -- Run `integration tests` -- Identify errors noted from `integration tests` - -## 1.1. Why have integration tests ? - -The purpose of integration tests is to provide standard usage examples that can be programmatically run -to make sure that a specific functionality of the terminal and the process to utilize that functionality -is working properly. - -## 2. How to build integration tests ? - -Integration tests themselves are the same as manually running a certain series of steps. As a result, -writing tests is easy because the integration test script is just the total list of steps necessary to -use a command, command with specific argument, or series of commands. - -When you contribute a new feature to the Terminal, it's important that integration tests are added for -this particular feature. It is a part of the Checklist for the PR to be approved. - -All the `integration tests` should be insides the `scripts` folder. The naming convention for scripts -should be `test__.openbb` if you are testing a specific command or `test_.openbb` -if you are testing the entire menu. However, it is encouraged to create as specific of integration tests -as possible to identify errors more precisely. Additionally, all tests must end with the `exit` command. - -These files can be given dynamic output with the following syntax `${key=default}`. Please note that -both key and default can only contain letters and numbers with NO special characters. Each dynamic -argument MUST contain a key and a default value. - -## 3. How integration tests detect bugs ? - -When an unhandled exception is raised during a test session, this is considered a bug. The exception is caught and we save its traceback and other details to debug afterwards. - -### Script examples - -Testing a specific command and it's arguments: - -```zsh -script: test_alt_covid.openbb -- - - - - - - - - - -alternative -covid -country Australia -reset -slopes -country US -ov -country Russia -deaths -country United Kingdom -cases -country Canada -rates -exit -``` - -Testing an entire menu - -```zsh -test_stocks_options.openbb -- - - - - - - - - - -stocks -options -screen -view -view high_IV -set high_IV -scr -q -unu -calc -load aapl -exp 0 -pcr -info -chains -oi -vol -voi -hist 100 -grhist 100 -plot -x ltd -y iv -parity -binom -load spy -vsurf -exit -``` - -## 3. How to run integration tests ? - -### Conda Terminal - -After navigating to the location of the OpenBBTerminal repo, one can run integration tests in a -few different ways using the wildcard expression. Please include a `-t` with `terminal.py` to run -the tests. - -- To see the available tests, use `--list` or `-l`: - - ```zsh - python terminal.py -t -l - ``` - -- Run all integration tests: - - ```zsh - python terminal.py -t - ``` - -- Run some integration tests, by number, name or path: - - ```zsh - python terminal.py -t -p 0 10 22 - ``` - - ```zsh - python terminal.py -t -p stocks cryptocurrency - ``` - - *This specific example runs all of the stocks integration tests. One can use this same format for different tests.* - - ```zsh - python terminal.py -t openbb_terminal/core/integration_tests/scripts/forex - ``` - -- Run one integration test: - - ```zsh - python terminal.py -t -p alternative/test_alt_covid.openbb - ``` - - *Note that the base path is `OpenBBTerminal/openbb_terminal/core/integration_tests/scripts`.* - -- Skip some integration tests: - - ```zsh - python terminal.py -t -p alternative -s alternative/test_alt_covid.openbb - ``` - -- Run integration tests with arguments by adding --key=value: - - ```zsh - python terminal.py -t --ticker=aapl - ``` - -- To see the possible keys, use `--help` or `-h`: - - ```zsh - python terminal.py -t -h - ``` - -- To save time, the tests are run in parallel by default. You can choose the number of subprocesses used with `--subproc`. The default number of subprocesses is the minimum between the number of scripts to run and the number of CPUs of your machine. - - ```zsh - python terminal.py -t forex --subproc 4 - ``` - -- To have a more responsive console output we run the tests in parallel and return the - output of each script as soon as it is ready. This means that test results might not - be displayed in the same order as they were started. To force displaying tests results - in the order they start, use the option `--ordered`: - - ```zsh - python terminal.py -t --ordered - ``` - -- To see terminal outputs being printed during the test session, use `--verbose` or `-v`: - - ```zsh - python terminal.py -t -v - ``` - -- To run the tests sequentially (it will be way slower for a large number of scripts), - use `--subproc 0`: - - ```zsh - python terminal.py -t --subproc 0 - ``` - -- Enabling verbose mode and running scripts in several processes will mix the output - each script in the console. In this case it is advisable to run tests with the option - `--subproc 0`, this will run the tests sequentially. It will be slower, but the outputs - of each test will not be mixed up. - - ```zsh - python terminal.py -t forex --subproc 0 -v - ``` - -### Installer Terminal - -Integration tests can also be used on installers, which is a packaged version of the conda terminal. -More information on how to build an installer can be found [here](/build/README.md). -To run the tests on installers you can use the same syntax as above, just substitute `python terminal.py` by the full path to OpenBBTerminal executable (not the shortcut!). See the examples below for MacOS. - -- Run all integration tests: - - ```zsh - /Full/Path/To/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t - ``` - -- Run some integration tests: - - ```zsh - /Full/Path/To/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t forex stocks - ``` - -- Run one integration test: - - ```zsh - /Full/Path/To/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t alternative/test_alt_covid.openbb - ``` - -- Get integration test coverage: - - ```zsh - /Full/Path/To/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t --coverage - ``` - -## 4. Test report - -The console report has 3 sections: progress, failures and summary. - -- Progress - 1. Shows the tests collected and skipped in the current session - 2. Informs if tests are running in parallel or sequentially - 3. Displays the result of tests already finished - -- Failures - 1. Traceback - 2. Exception type - 3. Detail with exception message - -- Summary - 1. Displays tests failed with the last command called and how long they took to run - -- Example - -```zsh -(obb) % python terminal.py -t forex -============================ integration test session starts ============================= -Collecting scripts from: /Users/username/OpenBBTerminal/openbb_terminal/core/integration_tests/scripts - - -* Collected 7 script(s)... -* Skipping 0 script(s)... -* Running 7 script(s) in 7 parallel subprocess(es)... - -forex/test_forex_av.openbb [ 14%] -forex/test_forex_base.openbb [ 29%] -forex/test_forex_load.openbb [ 43%] -forex/test_forex_oanda.openbb [ 57%] -forex/test_forex_oanda_base.openbb [ 71%] -forex/test_forex_qa.openbb [ 86%] -forex/test_forex_ta.openbb [100%] - -======================================== FAILURES ======================================== -------------------------------- forex/test_forex_qa.openbb ------------------------------- - -Traceback: - File "/Users/username/OpenBBTerminal/openbb_terminal/core/integration_tests/integration_controller.py", line 299, in run_test - run_scripts( - File "/Users/username/OpenBBTerminal/openbb_terminal/core/integration_tests/integration_controller.py", line 272, in run_scripts - terminal(file_cmds, test_mode=True) - File "/Users/username/OpenBBTerminal/openbb_terminal/terminal_controller.py", line 905, in terminal - t_controller.queue = t_controller.switch(an_input) - File "/Users/username/OpenBBTerminal/openbb_terminal/decorators.py", line 64, in wrapper - value = func(*args, **kwargs) - File "/Users/username/OpenBBTerminal/openbb_terminal/parent_classes.py", line 363, in switch - getattr( - File "/Users/username/OpenBBTerminal/openbb_terminal/terminal_controller.py", line 401, in call_forex - self.queue = self.load_class(ForexController, self.queue) - File "/Users/username/OpenBBTerminal/openbb_terminal/parent_classes.py", line 219, in load_class - return class_ins(*args, **kwargs).menu() - File "/Users/username/OpenBBTerminal/openbb_terminal/parent_classes.py", line 934, in menu - self.queue = self.switch(an_input) - File "/Users/username/OpenBBTerminal/openbb_terminal/decorators.py", line 64, in wrapper - value = func(*args, **kwargs) - File "/Users/username/OpenBBTerminal/openbb_terminal/parent_classes.py", line 363, in switch - getattr( - File "/Users/username/OpenBBTerminal/openbb_terminal/decorators.py", line 64, in wrapper - value = func(*args, **kwargs) - File "/Users/username/OpenBBTerminal/openbb_terminal/forex/forex_controller.py", line 434, in call_qa - 1 / 0 -Exception type: ZeroDivisionError -Detail: division by zero -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -================================ integration test summary ================================ -FAILED forex/test_forex_qa.openbb -> command: qa -============================== 1 failed, 6 passed in 8.88s =============================== -``` diff --git a/openbb_terminal/core/integration_tests/controllers.json b/openbb_terminal/core/integration_tests/controllers.json deleted file mode 100644 index 13ff398a1b9..00000000000 --- a/openbb_terminal/core/integration_tests/controllers.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "openbb_terminal.mutual_funds.mutual_fund_controller": "FundController", - "openbb_terminal.alternative.alt_controller": "AlternativeDataController", - "openbb_terminal.alternative.realestate.realestate_controller": "RealEstateController", - "openbb_terminal.alternative.oss.oss_controller": "OSSController", - "openbb_terminal.forex.technical_analysis.ta_controller": "TechnicalAnalysisController", - "openbb_terminal.stocks.research.res_controller": "ResearchController", - "openbb_terminal.stocks.tradinghours.tradinghours_controller": "TradingHoursController", - "openbb_terminal.stocks.backtesting.bt_controller": "BacktestingController", - "openbb_terminal.stocks.behavioural_analysis.ba_controller": "BehaviouralAnalysisController", - "openbb_terminal.stocks.discovery.disc_controller": "DiscoveryController", - "openbb_terminal.etf.discovery.disc_controller": "DiscoveryController", - "openbb_terminal.stocks.fundamental_analysis.fa_controller": "FundamentalAnalysisController", - "openbb_terminal.stocks.technical_analysis.ta_controller": "TechnicalAnalysisController", - "openbb_terminal.stocks.dark_pool_shorts.dps_controller": "DarkPoolShortsController", - "openbb_terminal.stocks.comparison_analysis.ca_controller": "ComparisonAnalysisController", - "openbb_terminal.fixedincome.fixedincome_controller": "FixedIncomeController", - "openbb_terminal.portfolio.portfolio_optimization.po_controller": "PortfolioOptimizationController", - "openbb_terminal.cryptocurrency.nft.nft_controller": "NFTController", - "openbb_terminal.cryptocurrency.technical_analysis.ta_controller": "TechnicalAnalysisController", - "openbb_terminal.cryptocurrency.due_diligence.dd_controller": "DueDiligenceController", - "openbb_terminal.etf.etf_controller": "ETFController", - "openbb_terminal.etf.technical_analysis.ta_controller": "TechnicalAnalysisController", - "openbb_terminal.reports.reports_controller": "ReportController", - "openbb_terminal.portfolio.portfolio_optimization.parameters.params_controller": "ParametersController" -} diff --git a/openbb_terminal/core/integration_tests/integration_controller.py b/openbb_terminal/core/integration_tests/integration_controller.py deleted file mode 100644 index eab820a0520..00000000000 --- a/openbb_terminal/core/integration_tests/integration_controller.py +++ /dev/null @@ -1,754 +0,0 @@ -#!/usr/bin/env python -"""Main Testing Module""" -__docformat__ = "numpy" - -import argparse -import logging -import os -import re -import sys -import time -from functools import partial -from multiprocessing import cpu_count -from multiprocessing.pool import Pool -from pathlib import Path -from traceback import FrameSummary, extract_tb, format_list -from typing import Any, Callable, Dict, List, Optional, Tuple - -from matplotlib import pyplot as plt - -from openbb_terminal.core.config.paths import ( - MISCELLANEOUS_DIRECTORY, - REPOSITORY_DIRECTORY, -) -from openbb_terminal.core.integration_tests.integration_test_coverage import ( - get_coverage_all_controllers, -) -from openbb_terminal.core.integration_tests.utils import ( - SECTION_LENGTH, - to_section_title, -) -from openbb_terminal.core.models import ( - CredentialsModel, - PreferencesModel, - ProfileModel, - SourcesModel, - SystemModel, - UserModel, -) -from openbb_terminal.core.session.current_system import ( - get_current_system, - set_current_system, - set_system_variable, -) -from openbb_terminal.core.session.current_user import get_current_user, set_current_user -from openbb_terminal.core.session.utils import load_dict_to_model -from openbb_terminal.helper_funcs import check_non_negative -from openbb_terminal.rich_config import console -from openbb_terminal.routine_functions import is_reset -from openbb_terminal.terminal_controller import ( - insert_start_slash, - replace_dynamic, - terminal, -) -from openbb_terminal.terminal_helper import suppress_stdout - -logger = logging.getLogger(__name__) -special_arguments_values = [ - "ticker", - "currency", - "crypto", - "country", - "repo", - "crypto_vs", - "crypto_full", - "currency_vs", -] - -SCRIPTS_DIRECTORY = MISCELLANEOUS_DIRECTORY / "integration_tests_scripts" - - -TEST_FILES = sorted(list(SCRIPTS_DIRECTORY.glob("**/*.openbb"))) - - -def get_test_from_index(idx: str) -> Path: - """Get the test from the index. - - Parameters - ---------- - idx: int - The index of the test - - Returns - ------- - Path - The path to the test - """ - try: - return TEST_FILES[int(idx)] - except IndexError: - console.print( - f"[red]Index {idx} not found, must be between 0 and {len(TEST_FILES)-1}.[/red]" - ) - raise - - -def convert_list_to_test_files(path_list: List[str]) -> List[Path]: - """Converts a list of paths to test to a list of Path objects. - - Parameters - ---------- - path_list: List[str] - The list of paths to convert - - Returns - ------- - List[Path] - The list of paths as Path objects - """ - test_files = [] - - for path in path_list: - if path.startswith( - str(Path("openbb_terminal", "core", "integration_tests", "scripts")) - ): - script_path = REPOSITORY_DIRECTORY / path - elif path.isnumeric(): - try: - script_path = get_test_from_index(path) - except IndexError: - continue - else: - script_path = SCRIPTS_DIRECTORY / path - - if script_path.exists(): - chosen_path = script_path - else: - console.print(f"[red]Path not found: {script_path}[/red]\n") - continue - - if chosen_path.is_file() and str(chosen_path).endswith(".openbb"): - test_files.append(str(chosen_path)) - elif chosen_path.is_dir(): - all_files = os.walk(script_path) - for root, _, files in all_files: - for name in files: - if name.endswith(".openbb"): - path_obj = f"{root}/{name}" - test_files.append(path_obj) - - return [Path(x) for x in test_files] - - -def build_test_path_list(path_list: List[str], skip_list: List[str]) -> List[Path]: - """Build the paths to use in test. - - Parameters - ---------- - path_list: List[str] - The list of paths to test - skip_list: List[str] - The list of paths to skip - - Returns - ------- - List[Path] - The list of paths to test - """ - if path_list == "": - console.print("Please send a path when using test mode") - return [] - - valid_test_list = set(convert_list_to_test_files(path_list)) - valid_skip_list = set(convert_list_to_test_files(skip_list)) - final_list = sorted(valid_test_list - valid_skip_list) - # Just display number of skips if the skip path is in valid_test_list - len_skip = len(valid_test_list) - len(final_list) - - console.print(f"\n* Collected {len(valid_test_list)} script(s)...", style="bold") - console.print(f"* Skipping {len_skip} script(s)...", style="bold") - - return final_list - - -def collect_test_files(path_list: List[str], skip_list: List[str]) -> List[Path]: - """Collects the test files from the scripts directory - - Parameters - ---------- - path_list: List[str] - The list of paths to test - skip_list: List[str] - The list of paths to skip - - Returns - ------- - List[Path] - The list of paths to test - """ - console.print(f"Collecting scripts from: {SCRIPTS_DIRECTORY}\n") - - if not path_list: - path_list = [""] - return build_test_path_list(path_list, skip_list) - - -def run_scripts( - path: Path, - verbose: bool = False, - special_arguments: Optional[Dict[str, str]] = None, -): - """Run given .openbb scripts. - - Parameters - ---------- - path : str - The location of the .openbb file - verbose : bool - Whether to run tests in verbose mode - special_arguments: Optional[Dict[str, str]] - Replace `${key=default}` with `value` for every key in the dictionary - """ - if not path.exists(): - console.print(f"Path '{path}' doesn't exist.\n") - - with path.open() as fp: - raw_lines = [x for x in fp if (not is_reset(x)) and ("#" not in x) and x] - raw_lines = [ - raw_line.strip("\n") for raw_line in raw_lines if raw_line.strip("\n") - ] - - # Handle new testing arguments: - if special_arguments: - lines = [] - for line in raw_lines: - new_line = re.sub( - r"\${[^{]+=[^{]+}", - lambda x: replace_dynamic(x, special_arguments), # type: ignore - line, - ) - lines.append(new_line) - - else: - lines = raw_lines - - if "exit" not in lines[-1]: - lines.append("exit") - - export_folder = "" - if "export" in lines[0]: - export_folder = lines[0].split("export ")[1].rstrip() - lines = lines[1:] - - simulate_argv = f"/{'/'.join([line.rstrip() for line in lines])}" - file_cmds = simulate_argv.replace("//", "/home/").split() - file_cmds = insert_start_slash(file_cmds) if file_cmds else file_cmds - file_cmds = ( - [f"export {export_folder}{' '.join(file_cmds)}"] - if export_folder - else [" ".join(file_cmds)] - ) - - if verbose: - terminal(file_cmds, test_mode=True) - else: - with suppress_stdout(): - terminal(file_cmds, test_mode=True) - - -def run_test( - file: Path, - verbose: bool = False, - special_arguments: Optional[Dict[str, str]] = None, - user: Optional[Dict] = None, - system: Optional[Dict] = None, -) -> Tuple[str, Optional[Dict[str, Any]]]: - """Run tests in a single process. - - Parameters - ---------- - file: Path - The path to the file to test - verbose: bool - Whether to run tests in verbose mode - special_arguments: Optional[Dict[str, str]] - Replace `${key=default}` with `value` for every key in the dictionary - - Returns - ------- - Tuple[str, Optional[Dict[str, Any]]] - The name of the file and the exception - """ - - if user: - profile = user.get("profile", {}) - credentials = user.get("credentials", {}) - preferences = user.get("preferences", {}) - sources = user.get("sources", {}) - user_model = UserModel( - profile=load_dict_to_model(profile, ProfileModel), - credentials=load_dict_to_model(credentials, CredentialsModel), # type: ignore - preferences=load_dict_to_model(preferences, PreferencesModel), - sources=load_dict_to_model(sources, SourcesModel), - ) - set_current_user(user_model) - - if system: - set_current_system(load_dict_to_model(system, SystemModel)) - - file_short_name = str(file).replace(str(SCRIPTS_DIRECTORY), "")[1:] - - try: - run_scripts( - file, - verbose=verbose, - special_arguments=special_arguments, - ) - exception = None - except Exception as e: - _, _, exc_traceback = sys.exc_info() - exception = { - "exception": e, - "traceback": extract_tb(exc_traceback), - } - - return file_short_name, exception - - -def display_test_progress( - i: int, n: int, n_failures: int, file_short_name: str, verbose: bool = False -): - """Displays the progress of the tests - - Parameters - ---------- - i: int - The index of the test - n: int - The total number of tests - n_failures: int - The number of failures - file_short_name: str - The name of the file - verbose: bool - Whether to run tests in verbose mode - """ - - style = "red" if n_failures else "green" - if verbose: - console.print("^", style=style) - console.print("^", style=style) - console.print("^", style=style) - percentage = f"{(i + 1)/n:.0%}" - percentage_with_spaces = "[" + (4 - len(percentage)) * " " + percentage + "]" - spaces = SECTION_LENGTH - len(file_short_name) - len(percentage_with_spaces) - console.print( - f"{file_short_name}" + spaces * " " + f"{percentage_with_spaces}", - style=style, - ) - if verbose and i != n - 1: - console.print("- " * int(SECTION_LENGTH / 2), style=style) - - -def run_test_files( - test_files: List[Path], - verbose: bool = False, - special_arguments: Optional[Dict[str, str]] = None, - subprocesses: Optional[int] = None, - ordered: bool = False, -) -> Tuple[int, int, Dict[str, Dict[str, Any]], float]: - """Runs the test scripts and returns the fails dictionary - - Parameters - ---------- - test_files: List[Path] - The list of files to test - verbose: bool - Whether or not to print the output of the scripts - special_arguments: Optional[Dict[str, str]] - The special arguments to use in the scripts - subprocesses: Optional[int] - The number of subprocesses to use to run the tests - ordered: bool - Multiprocessing is not ordered by default. Use this flag to run the tests in order - - Returns - ------- - Tuple[int, int, Dict[str, Dict[str, Any]], float] - """ - n_successes = 0 - n_failures = 0 - fails: Dict[str, Dict[str, Any]] = {} - - if test_files: - n = len(test_files) - - start = time.time() - - if subprocesses == 0: - console.print( - f"* Running {n} script(s) sequentially...\n", - style="bold", - ) - for i, file in enumerate(test_files): - file_short_name, exception = run_test( - file, verbose=verbose, special_arguments=special_arguments - ) - if exception: - n_failures += 1 - fails[file_short_name] = exception - else: - n_successes += 1 - - display_test_progress(i, n, n_failures, file_short_name, verbose) - else: - if not subprocesses: - subprocesses = min(n, cpu_count()) - - console.print( - f"* Running {n} script(s) in {subprocesses} parallel subprocess(es)...\n", - style="bold", - ) - with Pool(processes=subprocesses) as pool: - # Choosing chunksize: line 477 .../lib/python3.9/multiprocessing/pool.py - chunksize, extra = divmod(n, subprocesses * 4) - if extra: - chunksize += 1 - - runner: Callable = pool.imap if ordered else pool.imap_unordered - - for i, result in enumerate( - runner( - partial( - run_test, - verbose=verbose, - special_arguments=special_arguments, - # We inject user and system as dict because pickle cannot - # serialize nested classes and the new process has to be - # aware of the current user and system, otherwise it will - # pick the defaults. - user=get_current_user().to_dict(), - system=get_current_system().to_dict(), - ), - test_files, - chunksize=chunksize, - ) - ): - file_short_name, exception = result - if exception: - n_failures += 1 - fails[file_short_name] = exception - else: - n_successes += 1 - - display_test_progress(i, n, n_failures, file_short_name, verbose) - - end = time.time() - seconds = end - start - else: - console.print("[yellow]* No tests to run.[/yellow]\n", style="bold") - seconds = 0.0 - - return n_successes, n_failures, fails, seconds - - -def display_failures(fails: Dict[str, Dict[str, Any]]): - """Generates the failures section of the test report - - Parameters - ---------- - fails: Dict[str, Dict[str, Any]] - The dictionary with failure information - """ - if fails: - console.print("\n" + to_section_title("FAILURES")) - for file, exception in fails.items(): - title = f"[bold red]{file}[/bold red]" - console.print(to_section_title(title=title, char="-"), style="red") - - console.print("[bold red]\nTraceback:[/bold red]") - formatted_tb = format_list(exception["traceback"]) - style = "" - for i, line in enumerate(formatted_tb): - if "openbb_terminal" not in line: - style = "rgb(128,128,128)" - elif ( - i == len(formatted_tb) - 1 - or "openbb_terminal" not in formatted_tb[i + 1] - ): - style = "yellow" - - console.print(line, end="", style=style) - - console.print( - f"[bold red]Exception type:[/bold red] {exception['exception'].__class__.__name__}" - ) - console.print(f"[bold red]Detail:[/bold red] {exception['exception']}") - console.print("- " * int(SECTION_LENGTH / 2)) - - -def display_summary( - fails: Dict[str, Dict[str, Any]], - n_successes: int, - n_failures: int, - seconds: float, -): - """Generates the summary message - - Parameters - ---------- - fails: Dict[str, Dict[str, Any]] - The dictionary with failure information - n_successes: int - The number of successes - n_failures: int - The number of failures - seconds: float - The number of seconds it took to run the tests - """ - - if fails: - console.print("\n" + to_section_title("Integration Test Summary")) - - for file, exception in fails.items(): - # Assuming the broken command is the last one called in the traceback - broken_cmd = "unknown" - frame: FrameSummary - for _, frame in reversed(list(enumerate(exception["traceback"]))): - if "_controller.py" in frame.filename and "call_" in frame.name: - broken_cmd = frame.name.split("_")[1] - break - - console.print(f"FAILED {file} -> command: {broken_cmd}") - - if n_successes or n_failures: - failures = ( - f"[bold][red]{n_failures} failed, [/red][/bold]" if n_failures > 0 else "" - ) - successes = f"[green]{n_successes} passed [/green]" if n_successes > 0 else "" - - m, s = divmod(seconds, 60) - elapsed_time = "" - if m > 0: - elapsed_time += f"{m:.0f}m:" - elapsed_time += f"{s:.2f}s" - - console.print( - to_section_title(failures + successes + "in " + elapsed_time), - style="green" if not n_failures else "red", - ) - - -def run_test_session( - path_list: List[str], - skip_list: List[str], - special_arguments: Optional[Dict[str, str]] = None, - verbose: bool = False, - subprocesses: Optional[int] = None, - ordered: bool = False, -): - """Run the integration test session - - Workflow: - 1. Collect test scripts - 2. Run test scripts - 3. Display failures traceback and detail, if any - 4. Display test summary - - Parameters - ---------- - path_list: List[str] - The list of paths to test - skip_list: List[str] - The list of paths to skip - special_arguments: Optional[Dict[str, str]] - The special arguments to use in the scripts - verbose: bool - Whether or not to print the output of the scripts - subprocesses - The number of subprocesses to use to run the tests - ordered: bool - Multiprocessing is not ordered by default. Use this flag to run the tests in order. - """ - console.print(to_section_title("integration test session starts"), style="bold") - test_files = collect_test_files(path_list, skip_list) - n_successes, n_failures, fails, seconds = run_test_files( - test_files, verbose, special_arguments, subprocesses, ordered - ) - display_failures(fails) - display_summary(fails, n_successes, n_failures, seconds) - - -def display_available_scripts(path_list: List[str], skip_list: List[str]): - """Display the available scripts - - Parameters - ---------- - path_list: List[str] - The list of paths to test - skip_list: List[str] - The list of paths to skip - """ - test_files = collect_test_files(path_list, skip_list) - console.print("\nAvailable scripts:", style="yellow") - for i, file in enumerate(test_files): - console.print(f"{i}. " + str(file).replace(str(SCRIPTS_DIRECTORY), "")[1:]) - console.print("") - - -def parse_args_and_run(): - """Parse input arguments and run integration tests.""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="testing", - description="Integration tests for the OpenBB Terminal.", - ) - parser.add_argument( - "-p", - "--path", - help=( - "The path to scripts or .openbb file to run." - " Usage examples for this flag:" - " (1) `... -p test_keys_.openbb`," - " (2) `... -p forex/test_forex_load.openbb`," - " (3) `... -p test_keys_.openbb forex`," - " (4) `... -p test_keys_.openbb forex etf`." - " If no path is provided, all scripts will be run." - ), - dest="path", - nargs="+", - default="", - type=str, - ) - parser.add_argument( - "-s", - "--skip", - help=( - "The path to scripts or .openbb file to run." - " Usage examples for this flag:" - " (1) `... -s test_keys_.openbb`," - " (2) `... -s forex/test_forex_load.openbb`," - " (3) `... -s test_keys_.openbb forex`," - " (4) `... -s test_keys_.openbb forex etf`." - " If no path is provided, all scripts will be run." - ), - dest="skip", - nargs="+", - default="", - type=str, - ) - parser.add_argument( - "-v", - "--verbose", - help="Whether or not to print the output of the scripts." - " To use it you must use just 1 subprocess.", - dest="verbose", - action="store_true", - default=False, - ) - parser.add_argument( - "--subproc", - help="The number of subprocesses to use to run the tests." - " Default is the minimum between number of collected scripts and CPUs.", - dest="subprocesses", - type=check_non_negative, - default=None, - ) - parser.add_argument( - "-l", - "--list", - help="List available scripts", - dest="list_", - action="store_true", - default=False, - ) - parser.add_argument( - "-o", - "--ordered", - help="Display results in test starting order.", - dest="ordered", - action="store_true", - default=False, - ) - parser.add_argument( - "-c", - "--coverage", - help="Display integration test coverage.", - dest="coverage", - action="store_true", - default=False, - ) - for arg in special_arguments_values: - parser.add_argument( - f"--{arg}", - help=f"Change the default values for {arg}", - dest=arg, - type=str, - default="", - ) - - ns_parser, unknown_args = parser.parse_known_args() - - if ns_parser.coverage: - return get_coverage_all_controllers() - - # Allow the tester to send a path without the -p flag - if not ns_parser.path and unknown_args: - ns_parser.path = [u for u in unknown_args if u[0] != "-"] - - special_args_dict = {x: getattr(ns_parser, x) for x in special_arguments_values} - - if ns_parser.verbose and ( - ns_parser.subprocesses is None or ns_parser.subprocesses > 1 - ): - console.print( - "WARNING: verbose mode and multiprocessing are not compatible. " - "Several processes running simultaneously will mix the output of the " - "scripts in the screen. Consider running with --subproc 0.\n", - style="yellow", - ) - - if ns_parser.list_: - return display_available_scripts(ns_parser.path, ns_parser.skip) - - return run_test_session( - path_list=ns_parser.path, - skip_list=ns_parser.skip, - special_arguments=special_args_dict, - verbose=ns_parser.verbose, - subprocesses=ns_parser.subprocesses, - ordered=ns_parser.ordered, - ) - - -def main(): - """Run the integration tests.""" - - if "-t" in sys.argv: - sys.argv.remove("-t") - if "--test" in sys.argv: - sys.argv.remove("--test") - - # User - current_user = get_current_user() - current_user.preferences.ENABLE_EXIT_AUTO_HELP = False - current_user.preferences.USE_PROMPT_TOOLKIT = False - current_user.preferences.REMEMBER_CONTEXTS = False - current_user.preferences.PLOT_ENABLE_PYWRY = False - current_user.preferences.USE_INTERACTIVE_DF = False - set_current_user(current_user) - - # System - set_system_variable("HEADLESS", True) - set_system_variable("DEBUG_MODE", True) - set_system_variable("LOG_COLLECT", False) - - # Portfolio optimization - automatically close matplotlib figures - plt.ion() - - # Run integration tests - parse_args_and_run() - - -if __name__ == "__main__": - main() diff --git a/openbb_terminal/core/integration_tests/integration_test_coverage.py b/openbb_terminal/core/integration_tests/integration_test_coverage.py deleted file mode 100644 index 54470741743..00000000000 --- a/openbb_terminal/core/integration_tests/integration_test_coverage.py +++ /dev/null @@ -1,617 +0,0 @@ -"""Obtain the integration test coverage for the OpenBB Terminal.""" - -# IMPORT STANDARD -import contextlib -import importlib -import json -import os -import re -from pathlib import Path - -# IMPORT THIRD-PARTY -import pandas as pd - -from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY - -# IMPORT INTERNAL -from openbb_terminal.core.integration_tests.utils import ( - SECTION_LENGTH, - get_submodule_commands, - map_module_to_calls, - to_section_title, - validate_missing_params, -) -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.rich_config import console - -INTEGRATION_PATH = os.path.join(MISCELLANEOUS_DIRECTORY, "integration_tests_scripts") -COMMAND_FILTERS = [ - "about", - "cls", - "home", - "record", - "resources", - "screenshot", - "stop", - "support", - "whoami", - "wiki", - "h", - "e", - "q", - "r", - "?", -] - - -def find_controllers() -> list: - """Find all controllers in the OpenBB Terminal.""" - controllers = [] - path = os.path.join(".", "openbb_terminal") - for root, _, files in os.walk(path): - for file in files: - if file.endswith("_controller.py") and "sdk" not in root: - controllers.append(os.path.join(root, file)) - - return controllers - - -def find_integration_tests() -> list: - """Find all integration tests in the OpenBB Terminal.""" - test = [] - for root, _, files in os.walk(INTEGRATION_PATH): - for file in files: - if file.endswith(".openbb"): - test.append(os.path.join(root, file)) - - test = [ - re.sub(r"^\.\\openbb_terminal\\miscellaneous\\integration_tests_scripts", "", i) - for i in test - ] - return test - - -def create_matching_dict() -> dict: - """Fill the controller and integration test matches by outlier files.""" - controller_paths = [ - os.path.join( - ".", "openbb_terminal", "mutual_funds", "mutual_fund_controller.py" - ), - os.path.join( - ".", "openbb_terminal", "stocks", "discovery", "disc_controller.py" - ), - os.path.join( - ".", - "openbb_terminal", - "stocks", - "options", - "screen", - "screener_controller.py", - ), - os.path.join(".", "openbb_terminal", "terminal_controller.py"), - os.path.join( - ".", "openbb_terminal", "stocks", "comparison_analysis", "ca_controller.py" - ), - ] - test_paths = [ - os.path.join("mutual_funds", "test_mutual_fund.openbb"), - os.path.join("stocks", "test_disc.openbb"), - os.path.join("stocks", "test_options_screen_ca.openbb"), - os.path.join("terminal", "test_news.openbb"), - os.path.join("stocks", "test_ca.openbb"), - ] - return dict(zip(controller_paths, test_paths)) - - -def match_controller_with_test(controllers: list, tests: list) -> dict: - """Match controllers with integration tests.""" - matched = create_matching_dict() - - for key, value in matched.items(): - controllers.remove(key) - tests = [i for i in tests if value not in i] - - for controller in controllers: - controller_name = os.path.split(Path(controller))[-1].split("_controller.py")[0] - - for test in tests: - test_name = os.path.split(Path(test))[-1][5:-7] - - if controller_name == test_name: - matched[controller] = test - tests.remove(test) - - for i in tests: - # Handle edge case as two controllers share the same integration test - if re.search(r"test_ca.openbb", i): - tests.remove(i) - - if len(tests) > 0: - console.print(f"[red]Unmatched tests: {tests}[/red]") - - return matched - - -def get_module(module_path: str, module_name: str = "") -> object: - """Obtain a module from a given path.""" - module = importlib.import_module(module_path) - if module_name == "": - module_name = re.sub(r".*\.", "", module.__name__) - module_name = "".join([word.capitalize() for word in module_name.split("_")]) - try: - module = getattr(module, module_name) - return module - except AttributeError: - raise ModuleNotFoundError # pylint: disable=raise-missing-from - - -def get_commands_and_params( - module: object, get_commands: bool = True, get_params: bool = True -): - """Get all commands from a given module.""" - params: dict = {} - commands: list = [] - try: - module = module() # type: ignore - except TypeError: - commands = get_submodule_commands(module) - params = map_module_to_calls(module) - if get_commands and get_params: - return commands, params - if get_commands and not get_params: - return commands - if not get_commands and get_params: - return params - - module_data = getattr(module, "choices_default") - if len(commands) == 0: - commands = list(module_data.keys()) - - commands = [command for command in commands if command not in COMMAND_FILTERS] - - for command in commands: - params[command] = [] - with contextlib.suppress(AttributeError): - params[command] = list(module_data[command].keys()) - - if get_commands and get_params: - return commands, params - if get_commands and not get_params: - return commands - if not get_commands and get_params: - return params - - return None - - -def get_tested_commands(test_file: str) -> list: - """Obtain all functions that are called in a given test file.""" - tested_commands = [] - with open(test_file) as f: - for line in f: - try: - function = line.split()[0] - except IndexError: - continue - - if function != "#": - tested_commands.append(function) - - tested_commands = list(dict.fromkeys(tested_commands)) - return list(tested_commands) - - -def get_tested_command_params(test_file: str) -> list: - """Obtain all function parameters that are called in a given test file.""" - tested_commands = [] - with open(test_file) as f: - for line in f: - try: - function = line.split()[0] - except IndexError: - continue - - params = re.findall(r"-\w+", line) - tested_commands.append((function, params)) - return tested_commands - - -def get_missing_params( - tested_command_params: list, controller: str, module_dict: dict -) -> dict: - """Get missing parameters for a given function.""" - missing_params = {} - for function, params in tested_command_params: - if function != controller: - try: - all_params = module_dict[function] - all_params = [param.replace("--", "-") for param in all_params] - except KeyError: - # this catches the (sub)menu - continue - - if set(params).issubset(set(all_params)): - all_params = [param for param in all_params if param not in params] - for param in params: - if param in all_params: - all_params.remove(param) - else: - unpacked_params = [] - used_unabbreviated_params = [] - for param in params: - unpacked_params.append(param) - - for param in unpacked_params: - for unabbreviated_param in all_params: - if unabbreviated_param.startswith(param): - used_unabbreviated_params.append(unabbreviated_param) - - missing_params[function] = [ - param for param in all_params if param not in params - ] - - for param in all_params: - for param2 in used_unabbreviated_params: - if param2.startswith(param): - try: - missing_params[function].remove(param) - missing_params[function].remove(param2) - except ValueError: - pass - - missing_params[function] = [ - param - for param in missing_params[function] - if param not in used_unabbreviated_params - ] - - try: - missing_params[function] = list(dict.fromkeys(missing_params[function])) - except KeyError: - # this catches the (sub)menu - continue - - for key, value in missing_params.items(): - missing_params[key] = list(value) - for param in value: - for param2 in value: - if param != param2 and param2.startswith(param): - with contextlib.suppress(ValueError): - missing_params[key].remove(param) - - missing_params = {k: v for k, v in missing_params.items() if v} - - return missing_params - - -def calculate_parameter_coverage( - tested_function: str, module: object -) -> tuple[dict, dict]: - """Calculate the parameter coverage for controller commands.""" - module_dict = get_commands_and_params(module, get_commands=False) - - tested_command_params = get_tested_command_params(tested_function) - for key, value in dict(tested_command_params).items(): - dict(tested_command_params)[key] = list(set(value)) - tested_command_params = list(dict(tested_command_params).items()) - - try: - controller = str(module).split(".")[3].split("_")[0] - except IndexError: - controller = str(module).split(".")[2].split("_")[0] - - missing_params = get_missing_params(tested_command_params, controller, module_dict) - - coverage_dict = {} - for key, value in missing_params.items(): - try: - coverage_dict[key] = round( - (len(module_dict[key]) - len(value)) / len(module_dict[key]) * 100, 2 - ) - except ZeroDivisionError: - coverage_dict[key] = 100 - - for key, value in module_dict.items(): - if key not in coverage_dict: - coverage_dict[key] = 100 - - return missing_params, coverage_dict - - -def display_command_coverage( - available_commands: list, - tested_commands: list, - untested_commands: list, - coverage: float, - output_table: bool = False, - limit: int = 10, -) -> None: - """Display coverage.""" - if output_table: - df = pd.DataFrame({"function": available_commands}) - df["tested"] = df["function"].apply(lambda x: x in tested_commands) - df = df.sort_values(by=["function"]) - df = df.astype({"tested": "str"}) - df["tested"] = df["tested"].apply( - lambda x: "[red]False[/red]" if x == "False" else "[green]True[/green]" - ) - df = df.sort_values(by=["tested"], ascending=False) - print_rich_table( - df, - headers=[x.title() for x in df.columns], - show_index=False, - title=f"* Integration test command coverage: {coverage}%", - automatic_coloring=True, - columns_to_auto_color=["tested"], - limit=limit, - ) - else: - console.print(f"* Integration test command coverage: {coverage}%\n") - console.print(f"[red]Untested commands: {untested_commands}[/red]") - console.print(f"[green]Tested commands: {tested_commands}[/green]\n") - - -def calculate_command_coverage( - tested_commands: list, available_commands: list -) -> tuple[float, list]: - """Calculate the integration test coverage.""" - untested_commands = [] - for function in available_commands: - if function not in tested_commands: - untested_commands.append(function) - - try: - coverage = round( - (len(available_commands) - len(untested_commands)) - / len(available_commands) - * 100, - 2, - ) - except ZeroDivisionError: - coverage = 100 - - return coverage, untested_commands - - -def get_coverage_all_controllers(output_table: bool = True) -> None: - """Get integration test coverage for all controllers.""" - summary = {} - - parent_dir = os.path.join(Path(__file__).parent) - with open(os.path.join(parent_dir, "controllers.json")) as f: - controller_name_mapping = json.load(f) - - controllers = find_controllers() - tests = find_integration_tests() - matched = match_controller_with_test(controllers, tests) - - for controller, integration_test in matched.items(): - if "/" in controller: - path = controller.replace("/", ".")[2:-3] - elif "\\" in controller: - path = controller.replace("\\", ".")[2:-3] - try: - module = get_module(path) - except ModuleNotFoundError: - module_name = controller_name_mapping.get(path, "") - try: - module = get_module(path, module_name=module_name) - except ModuleNotFoundError: - console.print(f"Module {module_name} not found!") - continue - except OSError as e: - console.print(e) - continue - - try: - available_commands = get_commands_and_params(module, get_params=False) - except TypeError: - continue - - file_path = os.path.join(INTEGRATION_PATH, integration_test) - if not os.path.isfile(file_path): - file_path = os.path.join(INTEGRATION_PATH, integration_test[1:]) - - tested_commands = get_tested_commands(file_path) - command_coverage, untested_commands = calculate_command_coverage( - tested_commands, available_commands - ) - - missing_params, coverage_dict = calculate_parameter_coverage( - tested_function=file_path, - module=module, - ) - missing_params = validate_missing_params(missing_params, test_file=file_path) - - try: - average_parameter_coverage = round( - sum(coverage_dict.values()) / len(coverage_dict), 2 - ) - except ZeroDivisionError: - average_parameter_coverage = 100 - - controller_coverage = calculate_controller_coverage( - command_coverage=command_coverage, - average_parameter_coverage=average_parameter_coverage, - ) - - summary[controller] = { - "Controller coverage": controller_coverage, - "Command coverage": command_coverage, - "Parameter coverage": average_parameter_coverage, - } - - console.print(to_section_title(controller), "\n") - console.print( - f"Controller coverage: {controller_coverage}%\n" - + f"Command coverage: {command_coverage}%\n" - + f"Parameter coverage: {average_parameter_coverage}%" - ) - - display_uncovered_commands( - missing_params, - untested_commands, - coverage_dict, - output_table, - ) - - console.print( - f"* Finished calculating integration test coverage for {controller}\n\n" - ) - - display_coverage_summary(summary) - - -def display_coverage_summary(summary: dict) -> None: - """Display the coverage summary. - - Parameters - ---------- - summary: dict - Summary of the integration test coverage. - """ - - console.print(to_section_title("Integration Coverage Summary")) - summary = dict( - sorted( - summary.items(), key=lambda item: item[1]["Controller coverage"], reverse=False # type: ignore - ) - ) - for controller_name, value in summary.items(): - controller_coverage = value["Controller coverage"] - command_coverage = value["Command coverage"] - parameter_coverage = value["Parameter coverage"] - - len_res = len(str(controller_coverage)) + len("Controller coverage: %") - spaces = SECTION_LENGTH - len(controller_name) - len_res - - console.print( - f"{controller_name}" + spaces * " ", - f"Controller coverage: {controller_coverage}%", - f"\n* Command coverage: {command_coverage}%", - f"\n* Parameter coverage: {parameter_coverage}%", - ) - - -def display_uncovered_commands( - missing_params: dict, - untested_commands: list, - coverage_dict: dict, - output_table: bool, -) -> None: - """Display the uncovered commands.""" - missing_commands = [] - for command in missing_params: - if command not in untested_commands: - missing_commands.append(command) - - for command in untested_commands: - if command not in missing_params: - missing_commands.append(command) - - if output_table: - df = pd.DataFrame() - df["Command"] = missing_commands - df["Missing params"] = [ # pylint: disable=unsupported-assignment-operation - ( - "all params missing" - if command in untested_commands - else missing_params[command] - ) - for command in missing_commands - ] - df["Coverage"] = [ - 0 if command in untested_commands else coverage_dict[command] - for command in missing_commands - ] - df = df.sort_values(by=["Coverage"], ascending=True) - df["Coverage"].astype(str) - df["Coverage"] = df[ # pylint: disable=unsupported-assignment-operation - "Coverage" - ].apply(lambda x: f"[red]{x}[/red]" if x < 80 else f"[green]{x}[/green]") - df = df[["Command", "Coverage", "Missing params"]] - try: - print_rich_table(df, title="Uncovered commands and parameters") - except IndexError: - console.print("\nNo uncovered commands found!") - - -def calculate_controller_coverage( - command_coverage: float, - average_parameter_coverage: float, -) -> float: - """Calculate the global coverage.""" - controller_coverage = (command_coverage * average_parameter_coverage) / 100 - - return round(controller_coverage, 2) - - -def get_coverage_single_controller( - controller: str, - integration_test: str, - module_name: str = "", - output_table: bool = True, -) -> None: - """Get single controller integration test coverage. - - Parameters - ---------- - controller (str): Controller to test. - integration_test (str): Integration test to use. - output_table (bool): Output a table with the detailed results. - - Example: - -------- - test_single_controller( - "openbb_terminal.stocks.stocks_controller", - "/stocks/test_stocks.openbb" - ) - - """ - module = get_module(controller, module_name=module_name) - available_commands = get_commands_and_params(module, get_params=False) - - file_path = os.path.join(INTEGRATION_PATH, integration_test) - if not os.path.isfile(file_path): - file_path = os.path.join(INTEGRATION_PATH, integration_test[1:]) - - tested_commands = get_tested_commands(file_path) - command_coverage, untested_commands = calculate_command_coverage( - tested_commands, available_commands - ) - - missing_params, coverage_dict = calculate_parameter_coverage( - tested_function=file_path, - module=module, - ) - missing_params = validate_missing_params(missing_params, test_file=file_path) - - try: - average_parameter_coverage = round( - sum(coverage_dict.values()) / len(coverage_dict), 2 - ) - except ZeroDivisionError: - average_parameter_coverage = 100 - - controller_coverage = calculate_controller_coverage( - command_coverage=command_coverage, - average_parameter_coverage=average_parameter_coverage, - ) - - console.print(to_section_title(controller), "\n") - console.print( - f"Controller coverage: {controller_coverage}%\n" - + f"Command coverage: {command_coverage}%\n" - + f"Parameter coverage: {round(average_parameter_coverage, 2)}%" - ) - - display_uncovered_commands( - missing_params, - untested_commands, - coverage_dict, - output_table, - ) - - -if __name__ == "__main__": - get_coverage_all_controllers() diff --git a/openbb_terminal/core/integration_tests/utils.py b/openbb_terminal/core/integration_tests/utils.py deleted file mode 100644 index dc0edac5eb3..00000000000 --- a/openbb_terminal/core/integration_tests/utils.py +++ /dev/null @@ -1,141 +0,0 @@ -# IMPORT STANDARD -import inspect -import re - -# IMPORT THIRD PARTY - -# IMPORT INTERNAL - -SECTION_LENGTH = 90 -STYLES = [ - "[bold]", - "[/bold]", - "[red]", - "[/red]", - "[green]", - "[/green]", - "[bold red]", - "[/bold red]", -] - - -def to_section_title(title: str, char: str = "=") -> str: - """Format title for test mode. - - Parameters - ---------- - title: str - The title to format - - Returns - ------- - str - The formatted title - """ - title = " " + title + " " - - len_styles = 0 - for style in STYLES: - if style in title: - len_styles += len(style) - - n = int((SECTION_LENGTH - len(title) + len_styles) / 2) - formatted_title = char * n + title + char * n - formatted_title = formatted_title + char * ( - SECTION_LENGTH - len(formatted_title) + len_styles - ) - - return formatted_title - - -def get_submodule_commands(module: object) -> list: - """Obtain all functions from a given module.""" - filter_func = [ - "about", - "cls", - "home", - "record", - "resources", - "screenshot", - "stop", - "support", - "whoami", - "wiki", - "ad", - ] - functions = [] - for name in dir(module): - if name.startswith("call_"): - functions.append(name[5:]) - - functions = [function for function in functions if function not in filter_func] - return functions - - -def find_all_calls(module) -> list: - """Find all function calls in a module.""" - calls = [] - module_dict = module.__dict__ - for key, _ in module_dict.items(): - if "call_" in key: - calls.append(key) - return calls - - -def parse_args(module, func) -> list: - """Parse the arguments of a given module function.""" - filters = [ - "-ape", - "-yacht", - "-club", - "-h", - "-to", - "-seeking", - "-sloping", - "-neutral", - "-known", - "-yields", - "-level", - "-rating", - "-traded", - "-frens", - "-ad", - "-sheet", - "-flow", - "-weighted", - "-curve", - "-year", - ] - source = inspect.getsource(module.__dict__[func]) - params = re.findall(r"-\w+", source) - params = [param for param in params if not param[1:].isupper()] - params = [param for param in params if not param[1:].isdigit()] - params = [param for param in params if not param[1].isupper()] - params = [param for param in params if param not in filters] - params = list(dict.fromkeys(params)) - return params - - -def map_module_to_calls(module) -> dict: - """Map module to its function calls and parameters.""" - calls = find_all_calls(module) - calls_dict = {} - for call in calls: - calls_dict[call] = parse_args(module, call) - - calls_dict = {k[5:]: v for k, v in calls_dict.items()} - return calls_dict - - -def validate_missing_params(missing_params: dict, test_file: str) -> dict: - """Validate missing parameters.""" - with open(test_file) as f: - lines = f.readlines() - for key, values in missing_params.items(): - for line in lines: - for value in values: - if key in line and value in line: - missing_params[key].remove(value) - - missing_params = {k: v for k, v in missing_params.items() if v} - return missing_params diff --git a/openbb_terminal/core/log/__init__.py b/openbb_terminal/core/log/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/log/collection/__init__.py b/openbb_terminal/core/log/collection/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/log/collection/log_sender.py b/openbb_terminal/core/log/collection/log_sender.py deleted file mode 100644 index 8bcbcd89e8d..00000000000 --- a/openbb_terminal/core/log/collection/log_sender.py +++ /dev/null @@ -1,140 +0,0 @@ -# IMPORTATION STANDARD -from copy import deepcopy -from pathlib import Path -from queue import SimpleQueue -from threading import Thread - -# IMPORTATION THIRDPARTY -# IMPORTATION INTERNAL -from openbb_terminal.core.log.collection.s3_sender import send_to_s3 -from openbb_terminal.core.log.constants import ( - ARCHIVES_FOLDER_NAME, - S3_FOLDER_SUFFIX, - TMP_FOLDER_NAME, -) -from openbb_terminal.core.log.generation.settings import Settings -from openbb_terminal.core.session.current_system import get_current_system - -# DO NOT USE THE FILE LOGGER IN THIS MODULE - - -class QueueItem: - @property - def path(self) -> Path: - return self.__path - - @property - def last(self) -> bool: - return self.__last - - def __init__(self, path: Path, last: bool = False): - self.__path = path - self.__last = last - - def __str__(self): - return "{" + f'"last": {self.last}, "path": {self.path}' + "}" - - -class LogSender(Thread): - MAX_FAILS = 3 - - @staticmethod - def start_required() -> bool: - """Check if it makes sense to start a LogsSender instance .""" - return get_current_system().LOG_COLLECT - - @property - def settings(self) -> Settings: - return deepcopy(self.__settings) - - @property - def fails(self) -> int: - return self.__fails - - @property - def queue(self) -> SimpleQueue: - return self.__queue - - # OVERRIDE - def run(self): - settings = self.__settings - app_settings = settings.app_settings - aws_settings = settings.aws_settings - queue = self.__queue - - app_name = app_settings.name - identifier = app_settings.identifier - - while True: - item: QueueItem = queue.get() # type: ignore - file = item.path - last = item.last - - if self.check_sending_conditions(file=file): - archives_file = file.parent / ARCHIVES_FOLDER_NAME / f"{file.stem}.log" - object_key = ( - f"{app_name}{S3_FOLDER_SUFFIX}/logs/{identifier}/{file.stem}.log" - ) - tmp_file = file.parent / TMP_FOLDER_NAME / f"{file.stem}.log" - - try: - send_to_s3( - archives_file=archives_file, - aws_settings=aws_settings, - file=file, - object_key=object_key, - tmp_file=tmp_file, - last=last, - ) - except Exception: - self.fails_increment() - finally: - self.fails_reset() - - if last: - break - - # OVERRIDE - def __init__( - self, - settings: Settings, - *args, - **kwargs, - ): - super().__init__(*args, **kwargs) - - self.__settings = settings - - self.__fails: int = 0 # type: ignore - self.__queue: SimpleQueue = SimpleQueue() - - def check_sending_conditions(self, file: Path) -> bool: - """Check if the condition are met to send data.""" - - return ( - self.start_required() - and not self.max_fails_reached() - and not self.max_size_exceeded(file=file) - and get_current_system().LOGGING_SEND_TO_S3 - ) - - def fails_increment(self): - self.__fails += 1 - - def fails_reset(self): - self.__fails = 0 - - def max_fails_reached(self) -> bool: - return self.__fails > self.MAX_FAILS - - def max_size_exceeded(self, file: Path) -> bool: - """Check if the log file is bigger than 2MB.""" - if not file.exists(): - return False - return file.stat().st_size > 2 * 1024 * 1024 - - def send_path(self, path: Path, last: bool = False): - """Only closed files should be sent.""" - - queue = self.__queue - queue.put(QueueItem(path=path, last=last)) diff --git a/openbb_terminal/core/log/collection/logging_clock.py b/openbb_terminal/core/log/collection/logging_clock.py deleted file mode 100644 index 91f0ac95e15..00000000000 --- a/openbb_terminal/core/log/collection/logging_clock.py +++ /dev/null @@ -1,135 +0,0 @@ -import logging -import time -from datetime import datetime, timedelta -from enum import Enum -from math import ceil -from threading import Thread -from typing import Callable, Optional - - -class Precision(Enum): - hour = 3600 - minute = 60 - - -class LoggingClock(Thread): - """ - Like a Talking Clock but for logs. - - Usage example : - import logging - from openbb_terminal.log_collection.logging_clock import LoggingClock, Precision - - logging.basicConfig() - logger = logging.getLogger(__name__) - logger.setLevel(level=logging.INFO) - - logging_clock = LoggingClock( - logger=logger, - precision=Precision.minute, - ) - logging_clock.start() - logging_clock.join() - """ - - @classmethod - def calculate_next_sharp( - cls, - current_time: datetime, - precision: Precision, - ) -> datetime: - if precision is Precision.hour: - sharp_time = cls.calculate_next_sharp_hour(current_time=current_time) - elif precision is Precision.minute: - sharp_time = cls.calculate_next_sharp_minute(current_time=current_time) - else: - raise AttributeError(f"Unknown precision {precision}") - - return sharp_time - - @staticmethod - def calculate_next_sharp_hour(current_time: datetime) -> datetime: - current_truncated_time = datetime( - current_time.year, - current_time.month, - current_time.day, - current_time.hour, - ) - next_sharp_hour = current_truncated_time + timedelta(minutes=1) - - return next_sharp_hour - - @staticmethod - def calculate_next_sharp_minute(current_time: datetime) -> datetime: - current_truncated_time = datetime( - current_time.year, - current_time.month, - current_time.day, - current_time.hour, - current_time.minute, - ) - next_sharp_minute = current_truncated_time + timedelta(minutes=1) - - return next_sharp_minute - - @classmethod - def do_action_every_sharp( - cls, - action: Callable, - precision: Precision = Precision.hour, - ): - next_hour = cls.calculate_next_sharp( - current_time=datetime.now(), - precision=precision, - ) - - while True: - current_time = datetime.now() - delta = current_time - next_hour - delta_seconds = delta.total_seconds() - - if delta_seconds > 0: - action() - next_hour = cls.calculate_next_sharp( - current_time=current_time, - precision=precision, - ) - else: - sleep_duration = ceil(abs(delta_seconds)) - time.sleep(sleep_duration) - - # OVERRIDE - def run(self): - action = self.__action - precision = self.__precision - - self.do_action_every_sharp( - action=action, - precision=precision, - ) - - # OVERRIDE - def __init__( - self, - *args, - action: Optional[Callable] = None, - level: int = logging.INFO, - logger: Optional[logging.Logger] = None, - msg: str = "Logging Clock : %s", - precision: Precision = Precision.hour, - **kwargs, - ): - super().__init__(*args, **kwargs) - - self.__action = action or self.default_action - self.__level = level - self.__logger = logger or logging.getLogger(self.__module__) - self.__msg = msg - self.__precision = precision - - def default_action(self): - level = self.__level - logger = self.__logger - msg = self.__msg % datetime.now() - - logger.log(level=level, msg=msg) diff --git a/openbb_terminal/core/log/collection/s3_sender.py b/openbb_terminal/core/log/collection/s3_sender.py deleted file mode 100644 index 4809dfcbd83..00000000000 --- a/openbb_terminal/core/log/collection/s3_sender.py +++ /dev/null @@ -1,140 +0,0 @@ -# IMPORTATION STANDARD -from pathlib import Path -from shutil import copyfile -from typing import Any, Dict - -# IMPORTATION THIRDPARTY -import requests - -try: - import boto3 -except ImportError: - WITH_BOTO3 = False -else: - WITH_BOTO3 = True - -# IMPORTATION INTERNAL -from openbb_terminal.core.log.constants import DEFAULT_API_URL, DEFAULT_BUCKET -from openbb_terminal.core.log.generation.settings import AWSSettings - -# DO NOT USE THE FILE LOGGER IN THIS MODULE - - -def send_to_s3_directly( - aws_access_key_id: str, - aws_secret_access_key: str, - bucket: str, - file: Path, - object_key: str, -): - if not WITH_BOTO3: - raise ModuleNotFoundError("Library `boto3` is required to directly access S3.") - - s3_client = boto3.client( - service_name="s3", - aws_access_key_id=aws_access_key_id, - aws_secret_access_key=aws_secret_access_key, - ) - s3_client.upload_file(str(file), bucket, object_key) - - -def fetch_presigned_url(api_url: str, object_key: str) -> Dict[str, Any]: - raw_response = requests.put( - json={"object_key": object_key}, - timeout=3, - url=api_url, - ) - raw_response.raise_for_status() - - response = raw_response.json() - - return response - - -def send_to_s3_using_presigned_url( - api_url: str, - file: Path, - object_key: str, -): - presigned_info = fetch_presigned_url(api_url=api_url, object_key=object_key) - - with open(file, "rb") as f: - files = {"file": f} - - raw_response = requests.post( - data=presigned_info["fields"], - files=files, # type: ignore - timeout=3, - url=presigned_info["url"], - ) - - raw_response.raise_for_status() - - # SUCCESS IF STATUS_CODE == 204 - - -def send_to_s3( - archives_file: Path, - aws_settings: AWSSettings, - file: Path, - object_key: str, - tmp_file: Path, - last: bool = False, -): - """Send a file into a s3 bucket. - - Args: - archives_file (Path): - Destination Path after processing. - aws_settings (AWSSettings): - AWS settings. - file (Path): - Path of the file to process. - object_key (str): _description_ - File location inside the s3 bucket. - tmp_file (Path): - Temporary Path in which to put the file during processing. - last (bool, optional): - Whether or not this is the last sending before program exit. - Defaults to False. - - Raises: - AttributeError: - If `file` is empty. - """ - - api_url = DEFAULT_API_URL - bucket = DEFAULT_BUCKET - - if file.stat().st_size <= 0: - file.unlink(missing_ok=True) - raise AttributeError(f"File is empty : {file}") - - tmp_file.parent.mkdir(exist_ok=True) - - if last: - copyfile(file, tmp_file) - else: - file.rename(tmp_file) - - if ( - not last - and aws_settings.aws_access_key_id != "REPLACE_ME" # noqa: S105 - and aws_settings.aws_secret_access_key != "REPLACE_ME" # noqa: S105 - ): - send_to_s3_directly( - aws_access_key_id=aws_settings.aws_access_key_id, - aws_secret_access_key=aws_settings.aws_secret_access_key, - bucket=bucket, - file=tmp_file, - object_key=object_key, - ) - else: - send_to_s3_using_presigned_url( - api_url=api_url, - file=tmp_file, - object_key=object_key, - ) - - archives_file.parent.mkdir(exist_ok=True) - tmp_file.rename(archives_file) diff --git a/openbb_terminal/core/log/constants.py b/openbb_terminal/core/log/constants.py deleted file mode 100644 index 1ec5ea31013..00000000000 --- a/openbb_terminal/core/log/constants.py +++ /dev/null @@ -1,5 +0,0 @@ -ARCHIVES_FOLDER_NAME = "archives" -DEFAULT_BUCKET = "gst-logging" -DEFAULT_API_URL = "https://gst.openbb.app/logging" -S3_FOLDER_SUFFIX = "-app" -TMP_FOLDER_NAME = "tmp" diff --git a/openbb_terminal/core/log/generation/__init__.py b/openbb_terminal/core/log/generation/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/log/generation/common.py b/openbb_terminal/core/log/generation/common.py deleted file mode 100644 index cdfde6f8c1b..00000000000 --- a/openbb_terminal/core/log/generation/common.py +++ /dev/null @@ -1,17 +0,0 @@ -# IMPORTATION STANDARD -import contextlib -import logging - -# IMPORTATION THIRDPARTY -# IMPORTATION INTERNAL -from openbb_terminal.core.log.generation.path_tracking_file_handler import ( - PathTrackingFileHandler, -) - - -def do_rollover(): - """RollOver the log file.""" - for handler in logging.getLogger().handlers: - if isinstance(handler, PathTrackingFileHandler): - with contextlib.suppress(PermissionError): - handler.doRollover() diff --git a/openbb_terminal/core/log/generation/custom_logger.py b/openbb_terminal/core/log/generation/custom_logger.py deleted file mode 100644 index 171a3cae7b2..00000000000 --- a/openbb_terminal/core/log/generation/custom_logger.py +++ /dev/null @@ -1,26 +0,0 @@ -# IMPORTATION STANDARD -import logging - -from openbb_terminal.base_helpers import remove_log_handlers -from openbb_terminal.core.log.generation import settings_logger -from openbb_terminal.core.log.generation.common import do_rollover - -# IMPORTATION THIRDPARTY -# IMPORTATION INTERNAL -from openbb_terminal.loggers import setup_logging - -logger = logging.getLogger(__name__) - - -def log_terminal(test_mode: bool): - """Logs for the terminal""" - - do_rollover() - remove_log_handlers() - - if not test_mode: - setup_logging() - - logger.info("START") - settings_logger.log_all_settings(with_rollover=False) - do_rollover() diff --git a/openbb_terminal/core/log/generation/directories.py b/openbb_terminal/core/log/generation/directories.py deleted file mode 100644 index 4135dcae9c5..00000000000 --- a/openbb_terminal/core/log/generation/directories.py +++ /dev/null @@ -1,44 +0,0 @@ -# IMPORTATION STANDARD -import uuid -from pathlib import Path - -from openbb_terminal.core.session.current_user import get_current_user - -# IMPORTATION THIRDPARTY - - -# IMPORTATION INTERNAL - - -def get_log_dir() -> Path: - """Retrieve application's log directory.""" - - log_dir = ( - get_current_user().preferences.USER_DATA_DIRECTORY.joinpath("logs").absolute() - ) - - if not log_dir.is_dir(): - log_dir.mkdir(parents=True, exist_ok=True) - - log_id = (log_dir / ".logid").absolute() - - if not log_id.is_file(): - logging_id = f"{uuid.uuid4()}" - log_id.write_text(logging_id, encoding="utf-8") - else: - logging_id = log_id.read_text(encoding="utf-8").rstrip() - - uuid_log_dir = (log_dir / logging_id).absolute() - - if not uuid_log_dir.is_dir(): - uuid_log_dir.mkdir(parents=True, exist_ok=True) - - return uuid_log_dir - - -def get_log_sub_dir(name: str): - log_dir = get_log_dir() - - sub_dir = log_dir / name - - return sub_dir diff --git a/openbb_terminal/core/log/generation/expired_files.py b/openbb_terminal/core/log/generation/expired_files.py deleted file mode 100644 index 0e4bcb0c523..00000000000 --- a/openbb_terminal/core/log/generation/expired_files.py +++ /dev/null @@ -1,30 +0,0 @@ -# IMPORTATION STANDARD -import contextlib -from datetime import datetime -from pathlib import Path -from typing import List - -# IMPORTATION THIRDPARTY - -# IMPORTATION INTERNAL - - -def get_timestamp_from_x_days(x: int) -> float: - timestamp_from_x_days = datetime.now().timestamp() - x * 86400 - return timestamp_from_x_days - - -def get_expired_file_list(directory: Path, before_timestamp: float) -> List[Path]: - expired_files = list() - if directory.exists and directory.is_dir(): # type: ignore - for file in directory.iterdir(): - if file.is_file() and file.lstat().st_mtime < before_timestamp: - expired_files.append(file) - - return expired_files - - -def remove_file_list(file_list: List[Path]): - for file in file_list: - with contextlib.suppress(PermissionError): - file.unlink(missing_ok=True) diff --git a/openbb_terminal/core/log/generation/formatter_with_exceptions.py b/openbb_terminal/core/log/generation/formatter_with_exceptions.py deleted file mode 100644 index 24ac648ac38..00000000000 --- a/openbb_terminal/core/log/generation/formatter_with_exceptions.py +++ /dev/null @@ -1,175 +0,0 @@ -# IMPORTATION STANDARD -import logging -import re - -# IMPORTATION THIRDPARTY -# IMPORTATION INTERNAL -from openbb_terminal.core.config.paths import HOME_DIRECTORY -from openbb_terminal.core.log.generation.settings import AppSettings -from openbb_terminal.core.log.generation.user_logger import get_user_uuid - - -class FormatterWithExceptions(logging.Formatter): - """Logging Formatter that includes formatting of Exceptions""" - - DATEFORMAT = "%Y-%m-%dT%H:%M:%S%z" - LOGFORMAT = "%(asctime)s|%(name)s|%(funcName)s|%(lineno)s|%(message)s" - LOGPREFIXFORMAT = ( - "%(levelname)s|%(appName)s|%(commitHash)s|%(appId)s|%(sessionId)s|%(userId)s|" - ) - - @staticmethod - def calculate_level_name(record: logging.LogRecord) -> str: - if record.exc_text: - level_name = "X" - elif record.levelname: - level_name = record.levelname[0] - else: - level_name = "U" - - return level_name - - @staticmethod - def extract_log_extra(record: logging.LogRecord): - log_extra = dict() - - if hasattr(record, "func_name_override"): - record.funcName = record.func_name_override # type: ignore - record.lineno = 0 - - if hasattr(record, "session_id"): - log_extra["sessionId"] = record.session_id # type: ignore - - return log_extra - - @staticmethod - def mock_ipv4(text: str) -> str: - pattern = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" - replacement = " FILTERED_IP " - text_mocked = re.sub(pattern, replacement, text) - - return text_mocked - - @staticmethod - def mock_email(text: str) -> str: - pattern = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" - replacement = " FILTERED_EMAIL " - text_mocked = re.sub(pattern, replacement, text) - - return text_mocked - - @staticmethod - def mock_password(text: str) -> str: - pattern = r'("password": ")[^"]+' - replacement = r"\1 FILTERED_PASSWORD " - text_mocked = re.sub(pattern, replacement, text) - return text_mocked - - @staticmethod - def mock_flair(text: str) -> str: - pattern = r'("FLAIR": "\[)(.*?)\]' - replacement = r"\1 FILTERED_FLAIR ]" - text_mocked = re.sub(pattern, replacement, text) - - return text_mocked - - @staticmethod - def mock_home_directory(text: str) -> str: - user_home_directory = str(HOME_DIRECTORY.as_posix()) - text_mocked = text.replace("\\\\", "/").replace( - user_home_directory, "MOCKING_USER_PATH" - ) - - return text_mocked - - @classmethod - def filter_special_tags(cls, text: str) -> str: - text_filtered = text.replace("\n", " MOCKING_BREAKLINE ") - text_filtered = text_filtered.replace("'Traceback", "Traceback") - - return text_filtered - - @classmethod - def filter_piis(cls, text: str) -> str: - text_filtered = cls.mock_ipv4(text=text) - text_filtered = cls.mock_email(text=text_filtered) - text_filtered = cls.mock_password(text=text_filtered) - text_filtered = cls.mock_home_directory(text=text_filtered) - text_filtered = cls.mock_flair(text=text_filtered) - - return text_filtered - - @classmethod - def filter_log_line(cls, text: str): - text_filtered = cls.filter_special_tags(text=text) - text_filtered = cls.filter_piis(text=text_filtered) - - return text_filtered - - # OVERRIDE - def __init__( - self, - app_settings: AppSettings, - style="%", - validate=True, - ) -> None: - super().__init__( - fmt=self.LOGFORMAT, - datefmt=self.DATEFORMAT, - style=style, - validate=validate, - ) - self.__log_settings = app_settings - - # OVERRIDE - def formatException(self, ei) -> str: - """Exception formatting handler - Parameters - ---------- - ei : logging._SysExcInfoType - Exception to be logged - Returns - ---------- - str - Formatted exception - """ - - result = super().formatException(ei) - return repr(result) - - # OVERRIDE - def format(self, record: logging.LogRecord) -> str: - """Log formatter - Parameters - ---------- - record : logging.LogRecord - Logging record - Returns - ---------- - str - Formatted_log message - """ - - app_settings = self.__log_settings - - level_name = self.calculate_level_name(record=record) - log_prefix_content = { - "appName": app_settings.name, - "levelname": level_name, - "appId": app_settings.identifier, - "sessionId": app_settings.session_id, - "commitHash": app_settings.commit_hash, - "userId": get_user_uuid(), - } - - log_extra = self.extract_log_extra(record=record) - log_prefix_content = {**log_prefix_content, **log_extra} - log_prefix = self.LOGPREFIXFORMAT % log_prefix_content - - record.msg = record.msg.replace("|", "-MOCK_PIPE-") - - log_line = super().format(record) - log_line = self.filter_log_line(text=log_line) - log_line_full = log_prefix + log_line - - return log_line_full diff --git a/openbb_terminal/core/log/generation/path_tracking_file_handler.py b/openbb_terminal/core/log/generation/path_tracking_file_handler.py deleted file mode 100644 index 0289441196a..00000000000 --- a/openbb_terminal/core/log/generation/path_tracking_file_handler.py +++ /dev/null @@ -1,194 +0,0 @@ -# IMPORTATION STANDARD -import contextlib -from copy import deepcopy -from logging.handlers import TimedRotatingFileHandler -from pathlib import Path -from typing import Callable - -from openbb_terminal.core.log.collection.log_sender import LogSender -from openbb_terminal.core.log.collection.logging_clock import LoggingClock, Precision - -# IMPORTATION THIRDPARTY -# IMPORTATION INTERNAL -from openbb_terminal.core.log.constants import ARCHIVES_FOLDER_NAME, TMP_FOLDER_NAME -from openbb_terminal.core.log.generation.directories import get_log_dir, get_log_sub_dir -from openbb_terminal.core.log.generation.expired_files import ( - get_expired_file_list, - get_timestamp_from_x_days, - remove_file_list, -) -from openbb_terminal.core.log.generation.settings import Settings - - -class PathTrackingFileHandler(TimedRotatingFileHandler): - @staticmethod - def build_log_file_path(settings: Settings) -> Path: - app_settings = settings.app_settings - log_settings = settings.log_settings - app_name = app_settings.name - directory = log_settings.directory - session_id = app_settings.session_id - - path = directory.absolute().joinpath(f"{app_name}_{session_id}") - return path - - @staticmethod - def build_log_sender(settings: Settings, start: bool) -> LogSender: - log_sender = LogSender(settings=settings, daemon=True) - - if start: - log_sender.start() - - return log_sender - - @staticmethod - def clean_expired_files(before_timestamp: float): - """Deleting old files inside : archives and tmp folders. - - Only files inside the following folders are considered : - - {LOG_FOLDER_PATH}/{ARCHIVES_FOLDER_NAME} - - {LOG_FOLDER_PATH}/{TMP_FOLDER_NAME} - - Args: - before_timestamp (float): Timestamp before which files are considered expired. - """ - - archives_directory = get_log_sub_dir(name=ARCHIVES_FOLDER_NAME) - tmp_directory = get_log_sub_dir(name=TMP_FOLDER_NAME) - expired_archives_file_list = get_expired_file_list( - directory=archives_directory, - before_timestamp=before_timestamp, - ) - expired_tmp_file_list = get_expired_file_list( - directory=tmp_directory, - before_timestamp=before_timestamp, - ) - - remove_file_list(file_list=expired_archives_file_list) - remove_file_list(file_list=expired_tmp_file_list) - - @staticmethod - def build_rolling_clock( - action: Callable, - frequency: str, - start: bool, - ) -> LoggingClock: - frequency = frequency.upper() - - if frequency == "H": - precision = Precision.hour - elif frequency == "M": - precision = Precision.minute - else: - raise AttributeError("Unsupported `logging_clock.Precision`.") - - rolling_clock = LoggingClock( - action=action, - daemon=True, - precision=precision, - ) - - if start: - rolling_clock.start() - - return rolling_clock - - def send_expired_files(self, before_timestamp: float): - """Try to send files older than before_timestamp days. - - Only files inside the following folder are considered : - - {LOG_FOLDER_PATH}/ - - Args: - before_timestamp (float): Timestamp before which files are considered expired. - """ - - log_sender = self.__log_sender - log_directory = get_log_dir() - - expired_log_file_list = get_expired_file_list( - directory=log_directory, - before_timestamp=before_timestamp, - ) - - for file in expired_log_file_list: - log_sender.send_path(last=False, path=file) - - @property - def log_sender(self) -> LogSender: - return self.__log_sender - - @property - def rolling_clock(self) -> LoggingClock: - return self.__rolling_clock - - @property - def settings(self) -> Settings: - return deepcopy(self.__settings) - - # OVERRIDE - def __init__( - self, - settings: Settings, - *args, - **kwargs, - ) -> None: - # SETUP PARENT CLASS - filename = str(self.build_log_file_path(settings=settings)) - frequency = settings.log_settings.frequency - kwargs["when"] = frequency - - super().__init__(filename, *args, **kwargs) - - self.suffix += ".log" - - # SETUP CURRENT CLASS - self.__settings = settings - - self.clean_expired_files(before_timestamp=get_timestamp_from_x_days(x=5)) - - self.__log_sender = self.build_log_sender( - settings=settings, - start=False, - ) - self.__rolling_clock = self.build_rolling_clock( - action=self.doRollover, - frequency=frequency, - start=settings.log_settings.rolling_clock, - ) - - if LogSender.start_required(): - self.__log_sender.start() - self.send_expired_files(before_timestamp=get_timestamp_from_x_days(x=1)) - - # OVERRIDE - def doRollover(self) -> None: - log_sender = self.__log_sender - - if self.lock: - with self.lock: - super().doRollover() - - log_path = Path(self.baseFilename) - - if log_sender.check_sending_conditions(file=log_path): - to_delete_path_list = self.getFilesToDelete() - for path in to_delete_path_list: - log_sender.send_path(path=Path(path)) - - # OVERRIDE - def close(self): - """Do not use the file logger in this function.""" - - log_sender = self.__log_sender - closed_log_path = Path(self.baseFilename) - - if log_sender.check_sending_conditions(file=closed_log_path): - log_sender.send_path(path=closed_log_path, last=True) - with contextlib.suppress(Exception): - log_sender.join(timeout=3) - - super().close() - - if log_sender.check_sending_conditions(file=closed_log_path): - closed_log_path.unlink(missing_ok=True) diff --git a/openbb_terminal/core/log/generation/settings.py b/openbb_terminal/core/log/generation/settings.py deleted file mode 100644 index cec4e0c50df..00000000000 --- a/openbb_terminal/core/log/generation/settings.py +++ /dev/null @@ -1,155 +0,0 @@ -# IMPORTATION STANDARD -from copy import deepcopy -from pathlib import Path - -# IMPORTATION THIRDPARTY -from typing import List - -# IMPORTATION INTERNAL - - -class AWSSettings: - @property - def aws_access_key_id(self) -> str: - return self.__aws_access_key_id - - @property - def aws_secret_access_key(self) -> str: - return self.__aws_secret_access_key - - def __init__( - self, - aws_access_key_id: str, - aws_secret_access_key: str, - ): - self.__aws_access_key_id = aws_access_key_id - self.__aws_secret_access_key = aws_secret_access_key - - -class AppSettings: - @property - def name(self) -> str: - return self.__name - - @property - def commit_hash(self) -> str: - return self.__commit_hash - - @property - def identifier(self) -> str: - return self.__identifier - - @property - def session_id(self) -> str: - return self.__session_id - - @property - def user_id(self) -> str: - return self.__user_id - - @user_id.setter - def user_id(self, value: str): - self.__user_id = value - - def __init__( - self, - name: str, - commit_hash: str, - session_id: str, - identifier: str, - user_id: str, - ): - """ - Args: - name (str): Source of the application. - commit_hash (str): Commit hash of the current running code. - identifier (str): Unique key identifying a particular installation. - session_id (str): Key identifying a particular running session. - user_id (str): Hash identifying a particular user. - """ - - self.__name = name - self.__commit_hash = commit_hash - self.__identifier = identifier - self.__session_id = session_id - self.__user_id = user_id - - -class LogSettings: - @property - def directory(self) -> Path: - return self.__directory - - @property - def frequency(self) -> str: - return self.__frequency - - @property - def handler_list(self) -> List[str]: - return self.__handler_list - - @property - def rolling_clock(self) -> bool: - return self.__rolling_clock - - @property - def verbosity(self) -> int: - return self.__verbosity - - def __init__( - self, - directory: Path, - frequency: str, - handler_list: List[str], - rolling_clock: bool, - verbosity: int, - ): - """ - Args: - directory (Path): Directory used to store log files. - frequency (str): Frequency of the log files rotation. - handler_list (List[str]) : list of handlers : stdout,stderr,noop,file,posthog. - rolling_clock (bool): Whether or not to start a Thread to rotate logs even when inactive. - verbosity (str): Verbosity level as defined in Python `logging` module. - """ - - self.__directory = directory - self.__frequency = frequency - self.__handler_list = handler_list - self.__rolling_clock = rolling_clock - self.__verbosity = verbosity - - -class Settings: - @property - def app_settings(self) -> AppSettings: - return deepcopy(self.__app_settings) - - @property - def log_settings(self) -> LogSettings: - return deepcopy(self.__log_settings) - - @property - def aws_settings(self) -> AWSSettings: - return deepcopy(self.__aws_settings) - - def __init__( - self, - app_settings: AppSettings, - aws_settings: AWSSettings, - log_settings: LogSettings, - ): - """This model regroups all configurations used by these classes instance : - - openbb_terminal.core.log.collection.log_sender.LogSender - - openbb_terminal.core.log.generation.formatter_with_exceptions.FormatterWithExceptions - - openbb_terminal.core.log.generation.path_tracking_file_handler.PathTrackingFileHandler - - Args: - app_name (str): Instance of AppSettings. - log_settings (str): Instance of LogSettings. - aws_settings (str): Instance of AWSSettings. - """ - - self.__app_settings = app_settings - self.__log_settings = log_settings - self.__aws_settings = aws_settings diff --git a/openbb_terminal/core/log/generation/settings_logger.py b/openbb_terminal/core/log/generation/settings_logger.py deleted file mode 100644 index 0dc00527ea7..00000000000 --- a/openbb_terminal/core/log/generation/settings_logger.py +++ /dev/null @@ -1,79 +0,0 @@ -# IMPORTATION STANDARD -import json -import logging - -# IMPORTATION INTERNAL -from openbb_terminal.core.log.generation.common import do_rollover -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.core.session.current_user import get_current_user - -SENSITIVE_WORDS = [ - "API", - "DG_", - "KEY", - "PASSWORD", - "SECRET", - "TOKEN", - "USER", - "USERNAME", - "ACCOUNT", -] - -logger = logging.getLogger(__name__) - - -def log_all_settings(with_rollover: bool = True) -> None: - """Log all settings""" - log_startup() - - if with_rollover: - do_rollover() - - -def get_system() -> dict: - """Log system""" - system_dict = get_current_system().to_dict() - system_dict.pop("LOGGING_AWS_ACCESS_KEY_ID", None) - system_dict.pop("LOGGING_AWS_SECRET_ACCESS_KEY", None) - - return system_dict - - -def get_credentials() -> dict: - """Log credentials""" - - current_user = get_current_user() - - var_list = [v for v in dir(current_user.credentials) if v.startswith("API_")] - - current_keys = {} - - for cfg_var_name in var_list: - cfg_var_value = getattr(current_user.credentials, cfg_var_name) - - if cfg_var_value != "REPLACE_ME": - current_keys[cfg_var_name] = "defined" - else: - current_keys[cfg_var_name] = "not_defined" - - return current_keys - - -def log_preferences() -> None: - """Log preferences""" - logger.info( - "PREFERENCES: %s ", json.dumps(get_current_user().preferences.to_dict()) - ) - - -def log_startup() -> None: - """Combined logging of all settings""" - logger.info("STARTUP: %s ", json.dumps(get_startup())) - - -def get_startup(): - return { - "PREFERENCES": get_current_user().preferences.to_dict(), - "KEYS": get_credentials(), - "SYSTEM": get_system(), - } diff --git a/openbb_terminal/core/log/generation/user_logger.py b/openbb_terminal/core/log/generation/user_logger.py deleted file mode 100644 index e951920f0a7..00000000000 --- a/openbb_terminal/core/log/generation/user_logger.py +++ /dev/null @@ -1,18 +0,0 @@ -# IMPORTATION STANDARD -import logging - -# IMPORTATION THIRDPARTY -# IMPORTATION INTERNAL -from openbb_terminal.core.session.current_user import get_current_user, is_local - -logger = logging.getLogger(__name__) - -NO_USER_PLACEHOLDER = "NA" - - -def get_user_uuid() -> str: - """Get user UUID""" - if not is_local(): - return get_current_user().profile.get_uuid() - - return NO_USER_PLACEHOLDER diff --git a/openbb_terminal/core/models/__init__.py b/openbb_terminal/core/models/__init__.py deleted file mode 100644 index dcebfc389b2..00000000000 --- a/openbb_terminal/core/models/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from openbb_terminal.core.models.base_model import BaseModel -from openbb_terminal.core.models.credentials_model import CredentialsModel -from openbb_terminal.core.models.preferences_model import PreferencesModel -from openbb_terminal.core.models.profile_model import ProfileModel -from openbb_terminal.core.models.sources_model import SourcesModel -from openbb_terminal.core.models.system_model import SystemModel -from openbb_terminal.core.models.user_model import UserModel - -__all__ = [ - "BaseModel", - "CredentialsModel", - "PreferencesModel", - "ProfileModel", - "SourcesModel", - "SystemModel", - "UserModel", -] diff --git a/openbb_terminal/core/models/base_model.py b/openbb_terminal/core/models/base_model.py deleted file mode 100644 index 2c2ca1edd9b..00000000000 --- a/openbb_terminal/core/models/base_model.py +++ /dev/null @@ -1,40 +0,0 @@ -import dataclasses as dc -from typing import Any, Dict, Optional - -from pydantic.dataclasses import dataclass - -# pylint: disable=too-many-instance-attributes, disable=no-member - - -@dataclass -class BaseModel: - def __repr__(self) -> str: - """Return string representation of model.""" - dataclass_repr = "" - for key, value in sorted(self.__dict__.items()): - if key.startswith("_"): - continue - dataclass_repr += f" {key}='{value}', \n" - - return f"{self.__class__.__name__}(\n{dataclass_repr[:-2]}\n)" - - @classmethod - def get_fields(cls) -> Dict[str, Any]: - """Get dict of fields.""" - return cls.__dataclass_fields__ # type: ignore - - def get_value(self, field: str) -> Optional[Any]: - """Get field value.""" - if hasattr(self, field): - return getattr(self, field) - return None - - def to_dict(self) -> Dict[str, Any]: - """Convert model to dict.""" - return dc.asdict(self) # type: ignore - - def get_default(self, field: str) -> Optional[Any]: - """Get default field value.""" - if hasattr(self, field): - return self.get_fields()[field].default - return None diff --git a/openbb_terminal/core/models/credentials_model.py b/openbb_terminal/core/models/credentials_model.py deleted file mode 100644 index a72d1a2ba6c..00000000000 --- a/openbb_terminal/core/models/credentials_model.py +++ /dev/null @@ -1,29 +0,0 @@ -import json -from dataclasses import make_dataclass -from typing import Any, List, Tuple - -from pydantic.dataclasses import dataclass as pydanticdataclass - -from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY -from openbb_terminal.core.models.base_model import BaseModel - -with open(MISCELLANEOUS_DIRECTORY / "models" / "hub_credentials.json") as f: - HUB_CREDENTIALS = json.load(f) - -with open(MISCELLANEOUS_DIRECTORY / "models" / "local_credentials.json") as f: - LOCAL_CREDENTIALS = json.load(f) - - -fields: List[Tuple[str, type, Any]] = [ - (k, str, "REPLACE_ME") for k in list(HUB_CREDENTIALS) + list(LOCAL_CREDENTIALS) -] - -dc = make_dataclass( - cls_name="CredentialsModel", - fields=fields, - bases=(BaseModel,), -) -dc.__repr__ = dc.__base__.__repr__ # type: ignore -CredentialsModel = pydanticdataclass( - dc, config=dict(validate_assignment=True, frozen=True) -) diff --git a/openbb_terminal/core/models/preferences_model.py b/openbb_terminal/core/models/preferences_model.py deleted file mode 100644 index 86c43bd954f..00000000000 --- a/openbb_terminal/core/models/preferences_model.py +++ /dev/null @@ -1,91 +0,0 @@ -import os -from typing import Literal, Optional - -from pydantic import NonNegativeInt, PositiveFloat, PositiveInt -from pydantic.dataclasses import dataclass - -from openbb_terminal.core.config.paths import HOME_DIRECTORY -from openbb_terminal.core.models import BaseModel - -# pylint: disable=too-many-instance-attributes, disable=no-member, useless-parent-delegation - - -@dataclass(config=dict(validate_assignment=True, frozen=True)) -class PreferencesModel(BaseModel): - """Data model for preferences.""" - - # PLOT - # Plot backend - # Examples: - # "tkAgg" - This uses the tkinter library. If unsure, set to this - # "module://backend_interagg" - This is what pycharm defaults to in Scientific Mode - # "MacOSX" - Mac default. Does not work with backtesting - # "Qt5Agg" - This requires the PyQt5 package is installed - # See more: https://matplotlib.org/stable/tutorials/introductory/usage.html#the-builtin-backends - PLOT_BACKEND: Optional[str] = None - PLOT_DPI: PositiveInt = 100 - PLOT_HEIGHT: PositiveInt = 500 - PLOT_WIDTH: PositiveInt = 800 - PLOT_HEIGHT_PERCENTAGE: PositiveFloat = 50.0 - PLOT_WIDTH_PERCENTAGE: PositiveFloat = 70.0 - # Whether to open plot image exports after they are created - PLOT_OPEN_EXPORT: bool = False - # Use interactive window to display plots - PLOT_ENABLE_PYWRY: bool = True - PLOT_PYWRY_WIDTH: PositiveInt = 1400 - PLOT_PYWRY_HEIGHT: PositiveInt = 762 - - # FEATURE FLAGS - FILE_OVERWRITE: bool = False - SHOW_VERSION: bool = True - RETRY_WITH_LOAD: bool = False - USE_TABULATE_DF: bool = True - # Use interactive window to display dataframes with options to sort, filter, etc. - USE_INTERACTIVE_DF: bool = True - USE_CLEAR_AFTER_CMD: bool = False - USE_DATETIME: bool = True - # Enable interactive matplotlib mode: change variable name to be more descriptive and delete comment - USE_PROMPT_TOOLKIT: bool = True - USE_PLOT_AUTOSCALING: bool = False - ENABLE_THOUGHTS_DAY: bool = False - ENABLE_QUICK_EXIT: bool = False - OPEN_REPORT_AS_HTML: bool = True - ENABLE_EXIT_AUTO_HELP: bool = True - REMEMBER_CONTEXTS: bool = True - ENABLE_RICH_PANEL: bool = True - ENABLE_CHECK_API: bool = True - TOOLBAR_HINT: bool = True - - # GENERAL - PREVIOUS_USE: bool = False - TIMEZONE: str = "America/New_York" - FLAIR: str = ":bug" - USE_LANGUAGE: str = "en" - REQUEST_TIMEOUT: PositiveInt = 5 - MONITOR: NonNegativeInt = 0 - - # STYLE - MPL_STYLE: str = "dark" - PMF_STYLE: str = "dark" - RICH_STYLE: str = "dark" - CHART_STYLE: Literal["dark", "light"] = "dark" - TABLE_STYLE: Literal["dark", "light"] = "dark" - - # PATHS - GUESS_EASTER_EGG_FILE: str = os.getcwd() + os.path.sep + "guess_game.json" - USER_DATA_DIRECTORY = HOME_DIRECTORY / "OpenBBUserData" - USER_DATA_SOURCES_FILE: str = str(USER_DATA_DIRECTORY / "sources" / "sources.json") - USER_EXPORTS_DIRECTORY = USER_DATA_DIRECTORY / "exports" - USER_CUSTOM_IMPORTS_DIRECTORY = USER_DATA_DIRECTORY / "custom_imports" - USER_PORTFOLIO_DATA_DIRECTORY = USER_DATA_DIRECTORY / "portfolio" - USER_ROUTINES_DIRECTORY = USER_DATA_DIRECTORY / "routines" - USER_PRESETS_DIRECTORY = USER_DATA_DIRECTORY / "presets" - USER_REPORTS_DIRECTORY = USER_DATA_DIRECTORY / "reports" - USER_CUSTOM_REPORTS_DIRECTORY = USER_DATA_DIRECTORY / "reports" / "custom reports" - USER_FORECAST_MODELS_DIRECTORY = USER_DATA_DIRECTORY / "exports" / "forecast_models" - USER_FORECAST_WHISPER_DIRECTORY = USER_DATA_DIRECTORY / "exports" / "whisper" - USER_STYLES_DIRECTORY = USER_DATA_DIRECTORY / "styles" - USER_COMPANIES_HOUSE_DIRECTORY = USER_DATA_DIRECTORY / "companies_house" - - def __repr__(self) -> str: # pylint: disable=useless-super-delegation - return super().__repr__() diff --git a/openbb_terminal/core/models/profile_model.py b/openbb_terminal/core/models/profile_model.py deleted file mode 100644 index 7be862eb3a7..00000000000 --- a/openbb_terminal/core/models/profile_model.py +++ /dev/null @@ -1,86 +0,0 @@ -from typing import Dict - -from pydantic.dataclasses import dataclass - -from openbb_terminal.core.models import BaseModel - - -@dataclass(config=dict(validate_assignment=True, frozen=True)) -class ProfileModel(BaseModel): - """Data model for profile.""" - - token_type: str = "" - token: str = "" - uuid: str = "" - email: str = "" - username: str = "" - primary_usage: str = "" - remember: bool = True - - def load_user_info(self, session: dict, email: str, remember: bool): - """Load user info from login info. - - Parameters - ---------- - session : dict - The login info. - email : str - The email. - remember : bool - Remember the session. - """ - - self.token_type = session.get("token_type", "") - self.token = session.get("access_token", "") - self.uuid = session.get("uuid", "") - self.email = email - self.username = session.get("username", None) or email.split("@").pop(0) - self.primary_usage = session.get("primary_usage", None) or "personal" - self.remember = remember - - def get_uuid(self) -> str: - """Get uuid. - - Returns - ------- - str - The uuid. - """ - return self.uuid - - def get_token(self) -> str: - """Get token. - - Returns - ------- - str - The token. - """ - return self.token - - def get_session(self) -> Dict[str, str]: - """Get session info. - - Returns - ------- - Dict[str, str] - The session info. - """ - return { - "token_type": self.token_type, - "access_token": self.token, - "uuid": self.uuid, - } - - def get_auth_header(self) -> str: - """Get auth header. - - Returns - ------- - str - The auth header, e.g. "Bearer ". - """ - return f"{self.token_type.title()} {self.token}" - - def __repr__(self) -> str: # pylint: disable=useless-super-delegation - return super().__repr__() diff --git a/openbb_terminal/core/models/sources_model.py b/openbb_terminal/core/models/sources_model.py deleted file mode 100644 index 384aaf4a765..00000000000 --- a/openbb_terminal/core/models/sources_model.py +++ /dev/null @@ -1,55 +0,0 @@ -import json -from typing import Dict, List - -from pydantic.dataclasses import Field, dataclass - -from openbb_terminal.core.config.paths import DATA_SOURCES_DEFAULT_FILE -from openbb_terminal.core.models.base_model import BaseModel -from openbb_terminal.core.sources.utils import flatten - -# pylint: disable=useless-parent-delegation - - -def read_default_sources() -> Dict: - """Read default sources from file. - - Returns - ------- - Dict - Dictionary with sources - """ - try: - with open(DATA_SOURCES_DEFAULT_FILE) as file: - return flatten(json.load(file)) - except Exception as e: - print( # noqa: T201 - f"\nFailed to read data sources file: " - f"{DATA_SOURCES_DEFAULT_FILE}\n{e}\n" - ) - return {} - - -__allowed = read_default_sources() - - -def get_allowed_sources() -> Dict: - """Get allowed sources. - - Returns - ------- - Dict - Dictionary with sources - """ - return __allowed - - -@dataclass(config=dict(validate_assignment=True)) -class SourcesModel(BaseModel): - """Model for sources.""" - - choices: Dict[str, List[str]] = Field( - default_factory=lambda: get_allowed_sources(), - ) - - def __repr__(self): - return super().__repr__() diff --git a/openbb_terminal/core/models/system_model.py b/openbb_terminal/core/models/system_model.py deleted file mode 100644 index 744c1e3143f..00000000000 --- a/openbb_terminal/core/models/system_model.py +++ /dev/null @@ -1,89 +0,0 @@ -import platform -from typing import List, Literal - -from pydantic import Field, root_validator, validator -from pydantic.dataclasses import dataclass - -from openbb_terminal.core.models import BaseModel - -# pylint: disable=too-many-instance-attributes - - -@dataclass(config=dict(validate_assignment=True, frozen=True)) -class SystemModel(BaseModel): - """ - Data model for system variables and configurations. - - Disclaimer: - If you need to have a system related variable that is a credential like - `LOGGING_AWS_ACCESS_KEY_ID` and `LOGGING_AWS_SECRET_ACCESS_KEY`, you need - refer to the following function - `openbb_terminal.core.log.generation.settings_logger.log_system`, - in order to filter it from the logs. - """ - - # System section - OS: str = str(platform.system()) - PYTHON_VERSION: str = str(platform.python_version()) - PLATFORM: str = str(platform.platform()) - - # OpenBB section - VERSION: str = "3.2.5" - - # Logging section - LOGGING_APP_ID: str = "REPLACE_ME" - LOGGING_APP_NAME: str = "gst" - LOGGING_AWS_ACCESS_KEY_ID: str = "REPLACE_ME" - LOGGING_AWS_SECRET_ACCESS_KEY: str = "REPLACE_ME" - LOGGING_COMMIT_HASH: str = "REPLACE_ME" - LOGGING_BRANCH: str = "REPLACE_ME" - LOGGING_FREQUENCY: Literal["D", "H", "M", "S"] = "H" - LOGGING_HANDLERS: List[str] = Field(default_factory=lambda: ["file"]) - LOGGING_ROLLING_CLOCK: bool = False - LOGGING_VERBOSITY: int = 20 - LOGGING_SUB_APP: str = "terminal" - LOGGING_SUPPRESS: bool = False - LOGGING_SEND_TO_S3: bool = True - LOG_COLLECT: bool = True - - # Personalization section - DISABLE_STREAMLIT_WARNING: bool = False - DISABLE_FORECASTING_WARNING: bool = False - DISABLE_OPTIMIZATION_WARNING: bool = False - - # Others - TEST_MODE: bool = False - DEBUG_MODE: bool = False - DEV_BACKEND: bool = False - ENABLE_AUTHENTICATION: bool = True - HEADLESS: bool = False - - def __repr__(self) -> str: # pylint: disable=useless-super-delegation - return super().__repr__() - - @root_validator(allow_reuse=True) - @classmethod - def add_additional_handlers(cls, values): - if ( - not any([values["TEST_MODE"], values["LOGGING_SUPPRESS"]]) - and values["LOG_COLLECT"] - and "posthog" not in values["LOGGING_HANDLERS"] - ): - values["LOGGING_HANDLERS"].append("posthog") - - return values - - @root_validator(allow_reuse=True) - @classmethod - def validate_send_to_s3(cls, values): - if "posthog" in values["LOGGING_HANDLERS"] or values["LOG_COLLECT"] is False: - values["LOGGING_SEND_TO_S3"] = False - return values - - @validator("LOGGING_HANDLERS", allow_reuse=True) - @classmethod - def validate_logging_handlers(cls, v): - for value in v: - if value not in ["stdout", "stderr", "noop", "file", "posthog"]: - raise ValueError("Invalid logging handler") - return v diff --git a/openbb_terminal/core/models/user_model.py b/openbb_terminal/core/models/user_model.py deleted file mode 100644 index d1b160a2178..00000000000 --- a/openbb_terminal/core/models/user_model.py +++ /dev/null @@ -1,17 +0,0 @@ -from pydantic.dataclasses import dataclass - -from openbb_terminal.core.models.base_model import BaseModel -from openbb_terminal.core.models.credentials_model import CredentialsModel -from openbb_terminal.core.models.preferences_model import PreferencesModel -from openbb_terminal.core.models.profile_model import ProfileModel -from openbb_terminal.core.models.sources_model import SourcesModel - - -@dataclass(config=dict(validate_assignment=True, frozen=True)) -class UserModel(BaseModel): - """Data model for user.""" - - profile: ProfileModel - credentials: CredentialsModel - preferences: PreferencesModel - sources: SourcesModel diff --git a/openbb_terminal/core/plots/__init__.py b/openbb_terminal/core/plots/__init__.py deleted file mode 100644 index 4076cd76dec..00000000000 --- a/openbb_terminal/core/plots/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# flake8: noqa -from .plotly_helper import OpenBBFigure -from .plotly_ta.base import Indicator, PltTA, indicator -from .plotly_ta.data_classes import ChartIndicators, TA_Data, TAIndicator, columns_regex diff --git a/openbb_terminal/core/plots/assets/Terminal_icon.png b/openbb_terminal/core/plots/assets/Terminal_icon.png deleted file mode 100644 index da5008ab4305a6cc3781308c66fb2379def15757..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 418780 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%0Dk6^P_ma7N&>Z^m3 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 z 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

|N$7Smp?{K#Epe??OI;By?Ie%mK?qBP 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
- - - diff --git a/openbb_terminal/core/plots/plotly_helper.py b/openbb_terminal/core/plots/plotly_helper.py deleted file mode 100644 index 313b7c3bc24..00000000000 --- a/openbb_terminal/core/plots/plotly_helper.py +++ /dev/null @@ -1,1926 +0,0 @@ -"""Chart and style helpers for Plotly.""" - -# pylint: disable=C0302,R0902,W3301 -import contextlib -import json -import sys -import textwrap -from datetime import datetime, timedelta -from math import floor -from pathlib import Path -from typing import ( - TYPE_CHECKING, - Any, - Dict, - List, - Literal, - Optional, - Tuple, - TypeVar, - Union, -) - -import numpy as np -import pandas as pd -import plotly.graph_objects as go -import plotly.io as pio -import statsmodels.api as sm -from plotly.subplots import make_subplots -from scipy import stats - -from openbb_terminal import config_terminal -from openbb_terminal.base_helpers import console, strtobool -from openbb_terminal.core.config.paths import STYLES_DIRECTORY_REPO -from openbb_terminal.core.plots.backend import PLOTLYJS_PATH, plots_backend -from openbb_terminal.core.plots.config.openbb_styles import ( - PLT_COLORWAY, - PLT_DECREASING_COLORWAY, - PLT_INCREASING_COLORWAY, - PLT_TBL_ROW_COLORS, -) -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.core.session.current_user import get_current_user - -if TYPE_CHECKING: - with contextlib.suppress(ImportError): - from darts import TimeSeries # pylint: disable=W0611 # noqa: F401 - - -TimeSeriesT = TypeVar("TimeSeriesT", bound="TimeSeries") - - -class TerminalStyle: - """The class that helps with handling of style configurations. - - It serves styles for 2 libraries. For `Plotly` this class serves absolute paths - to the .pltstyle files. For `Plotly` and `Rich` this class serves custom - styles as python dictionaries. - """ - - STYLES_REPO = STYLES_DIRECTORY_REPO - USER_STYLES_DIRECTORY: Path = get_current_user().preferences.USER_STYLES_DIRECTORY - - plt_styles_available: Dict[str, Path] = {} - plt_style: str = "dark" - plotly_template: Dict[str, Any] = {} - mapbox_style: str = "dark" - - console_styles_available: Dict[str, Path] = {} - console_style: Dict[str, Any] = {} - - line_color: str = "" - up_color: str = "" - down_color: str = "" - up_colorway: List[str] = [] - down_colorway: List[str] = [] - up_color_transparent: str = "" - down_color_transparent: str = "" - - line_width: float = 1.5 - - def __new__(cls, *args, **kwargs): # pylint: disable=W0613 - """Create a singleton.""" - if not hasattr(cls, "instance"): - cls.instance = super().__new__(cls) # pylint: disable=E1120 - return cls.instance - - def __init__( - self, - plt_style: Optional[str] = "", - console_style: Optional[str] = "", - ): - """Initialize the class. - - Parameters - ---------- - plt_style : `str`, optional - The name of the Plotly style to use, by default "" - console_style : `str`, optional - The name of the Rich style to use, by default "" - """ - self.plt_style = plt_style or self.plt_style - self.load_available_styles() - self.load_style(plt_style) - self.apply_console_style(console_style) - self.apply_style() - - def apply_console_style(self, style: Optional[str] = None) -> None: - """Apply the style to the console.""" - - if style: - if style in self.console_styles_available: - json_path: Optional[Path] = self.console_styles_available[style] - else: - self.load_available_styles() - if style in self.console_styles_available: - json_path = self.console_styles_available[style] - else: - console.print(f"\nInvalid console style '{style}', using default.") - json_path = self.console_styles_available.get("dark", None) - - if json_path: - self.console_style = self.load_json_style(json_path) - else: - console.print("Error loading default.") - - def apply_style(self, style: Optional[str] = "") -> None: - """Apply the style to the libraries.""" - style = style or self.plt_style - - if style != self.plt_style: - self.load_style(style) - - style = style.lower().replace("light", "white") # type: ignore - - if self.plt_style and self.plotly_template: - self.plotly_template.setdefault("layout", {}).setdefault( - "mapbox", {} - ).setdefault("style", "dark") - if "tables" in self.plt_styles_available: - tables = self.load_json_style(self.plt_styles_available["tables"]) - pio.templates["openbb_tables"] = go.layout.Template(tables) - try: - pio.templates["openbb"] = go.layout.Template(self.plotly_template) - except ValueError as err: - if "plotly.graph_objs.Layout: 'legend2'" in str(err): - console.print( - "[red]Warning: Plotly multiple legends are " - "not supported in currently installed version.[/]\n\n" - "[yellow]Please update plotly to version >= 5.15.0[/]\n" - "[green]pip install plotly --upgrade[/]" - ) - sys.exit(1) - - if style in ["dark", "white"]: - pio.templates.default = f"plotly_{style}+openbb" - return - - pio.templates.default = "openbb" - self.mapbox_style = ( - self.plotly_template.setdefault("layout", {}) - .setdefault("mapbox", {}) - .setdefault("style", "dark") - ) - - def load_available_styles_from_folder(self, folder: Path) -> None: - """Load custom styles from folder. - - Parses the styles/default and styles/user folders and loads style files. - To be recognized files need to follow a naming convention: - *.pltstyle - plotly stylesheets - *.richstyle.json - rich stylesheets - - Parameters - ---------- - folder : str - Path to the folder containing the stylesheets - """ - if not folder.exists(): - return - - for attr, ext in zip( - ["plt_styles_available", "console_styles_available"], - [".pltstyle.json", ".richstyle.json"], - ): - for file in folder.rglob(f"*{ext}"): - getattr(self, attr)[file.name.replace(ext, "")] = file - - def load_available_styles(self) -> None: - """Load custom styles from default and user folders.""" - self.load_available_styles_from_folder(self.STYLES_REPO) - self.load_available_styles_from_folder(self.USER_STYLES_DIRECTORY) - - def load_json_style(self, file: Path) -> Dict[str, Any]: - """Load style from json file. - - Parameters - ---------- - file : Path - Path to the file containing the style - - Returns - ------- - Dict[str, Any] - Style as a dictionary - """ - with open(file) as f: - return json.load(f) - - def load_style(self, style: Optional[str] = "") -> None: - """Load style from file. - - Parameters - ---------- - style : str - Name of the style to load - """ - style = style or self.plt_style - - if style not in self.plt_styles_available: - console.print( - f"[red]Plot Style {style} not found. Using default style.[/red]", - ) - style = "dark" - - self.load_plt_style(style) - - def load_plt_style(self, style: str) -> None: - """Load Plotly style from file. - - Parameters - ---------- - style : str - Name of the style to load - """ - self.plt_style = style - self.plotly_template = self.load_json_style(self.plt_styles_available[style]) - line = self.plotly_template.pop("line", {}) - - self.up_color = line.get("up_color", "#00ACFF") - self.down_color = line.get("down_color", "#FF0000") - self.up_color_transparent = line.get( - "up_color_transparent", "rgba(0, 170, 255, 0.50)" - ) - self.down_color_transparent = line.get( - "down_color_transparent", "rgba(230, 0, 57, 0.50)" - ) - self.line_color = line.get("color", "#ffed00") - self.line_width = line.get("width", self.line_width) - self.down_colorway = line.get("down_colorway", PLT_DECREASING_COLORWAY) - self.up_colorway = line.get("up_colorway", PLT_INCREASING_COLORWAY) - - def get_colors(self, reverse: bool = False) -> list: - """Get colors for the plot. - - Parameters - ---------- - reverse : bool, optional - Whether to reverse the colors, by default False - - Returns - ------- - list - List of colors e.g. ["#00ACFF", "#FF0000"] - """ - colors = ( - self.plotly_template.get("layout", {}).get("colorway", PLT_COLORWAY).copy() - ) - if reverse: - colors.reverse() - return colors - - -theme = TerminalStyle( - get_current_user().preferences.CHART_STYLE, - get_current_user().preferences.RICH_STYLE, -) - - -# pylint: disable=R0913 -class OpenBBFigure(go.Figure): - """Custom Figure class for OpenBB Terminal. - - Parameters - ---------- - fig : `go.Figure`, optional - Figure to copy, by default None - has_subplots : `bool`, optional - Whether the figure has subplots, by default False - **kwargs - Keyword arguments to pass to `go.Figure.update_layout` - - Class Methods - ------------- - create_subplots(rows: `int`, cols: `int`, **kwargs) -> `OpenBBFigure` - Creates a subplots figure - to_table(data: `pd.DataFrame`, columnwidth: `list`, print_index: `bool`, ...) - Converts a DataFrame to a table figure - - Methods - ------- - add_hline_legend(y: `float`, name: `str`, line: `dict`, legendrank: `int`, **kwargs) - Adds a horizontal line with a legend label - add_vline_legend(x: `float`, name: `str`, line: `dict`, legendrank: `int`, **kwargs) - Adds a vertical line with a legend label - add_legend_label(trace: `str`, label: `str`, mode: `str`, marker: `dict`, **kwargs) - Adds a legend label - add_histplot(x: `list`, name: `str`, colors: `list`, bins: `int`, show_curve: `str`, ...) - Adds a histogram plot - horizontal_legend(x: `float`, y: `float`, xanchor: `str`, yanchor: `str`, ...) - Moves the legend to a horizontal position - to_subplot(subplot: `OpenBBFigure`, row: `int`, col: `int`, secondary_y: `bool`, ...) - Returns the figure as a subplot of another figure - """ - - plotlyjs_path: Path = PLOTLYJS_PATH - - def __init__(self, fig: Optional[go.Figure] = None, **kwargs) -> None: - super().__init__() - if fig is None and config_terminal.current_figure and config_terminal.HOLD: - fig = config_terminal.current_figure - - if fig: - self.__dict__ = fig.__dict__ - - self._has_secondary_y = kwargs.pop("has_secondary_y", False) - self._subplots_kwargs: Dict[str, Any] = kwargs.pop("subplots_kwargs", {}) - self._multi_rows = kwargs.pop("multi_rows", False) - self._added_logscale = False - self._date_xaxs: dict = {} - self._margin_adjusted = False - self._feature_flags_applied = False - self._exported = False - self._cmd_xshift = 0 - self._bar_width = 0.15 - self._export_image: Optional[Union[Path, str]] = "" - - self._subplot_xdates: Dict[int, Dict[int, List[Any]]] = {} - - fig = config_terminal.get_current_figure() - if fig is None and config_terminal.HOLD: - config_terminal.append_legend(config_terminal.last_legend) - if fig is not None: - traces = len(fig.data) - self.update_layout( - {f"yaxis{traces+1}": dict(title=kwargs.pop("yaxis_title", ""))} - ) - config_terminal.append_legend(config_terminal.last_legend) - - if xaxis := kwargs.pop("xaxis", None): - self.update_xaxes(xaxis) - if yaxis := kwargs.pop("yaxis", None): - self.update_yaxes(yaxis) - - self.update_layout(**kwargs) - - if plots_backend().isatty: - self.update_layout( - margin=dict(l=0, r=0, t=0, b=0, pad=0, autoexpand=True), - height=plots_backend().HEIGHT, - width=plots_backend().WIDTH, - ) - - def set_secondary_axis( - self, title: str, row: Optional[int] = None, col: Optional[int] = None, **kwargs - ) -> "OpenBBFigure": - """Set secondary axis. - - Parameters - ---------- - title : str - Title of the axis - row : int, optional - Row of the axis, by default None - col : int, optional - Column of the axis, by default None - **kwargs - Keyword arguments to pass to go.Figure.update_layout - """ - axis = "yaxis" - title = kwargs.pop("title", "") - if (fig := config_terminal.get_current_figure()) is not None: - total_axes = max(2, len(list(fig.select_yaxes()))) - axis = f"yaxis{total_axes+1}" - if config_terminal.make_new_axis(): - kwargs["side"] = "left" - kwargs.pop("secondary_y", None) - return self.update_layout(**{axis: dict(title=title, **kwargs)}) - - return self.update_yaxes(title=title, row=row, col=col, **kwargs) - - @property - def subplots_kwargs(self): - """Get subplots kwargs property.""" - return self._subplots_kwargs - - @subplots_kwargs.setter - def subplots_kwargs(self, value): - """Get subplots kwargs setter.""" - self._subplots_kwargs = value - - @property - def has_subplots(self): - """Has subplots property.""" - return self._has_subplots() - - @property - def bar_width(self): - """Bar width property.""" - return self._bar_width - - @bar_width.setter - def bar_width(self, value): - """Bar width setter.""" - self._bar_width = value - - @property - def cmd_xshift(self): - """Command line x shift property.""" - return self._cmd_xshift - - @cmd_xshift.setter - def cmd_xshift(self, value): - """Command line x shift setter.""" - self._cmd_xshift = value - - @classmethod - def create_subplots( - cls, - rows: int = 1, - cols: int = 1, - shared_xaxes: bool = True, - vertical_spacing: Optional[float] = None, - horizontal_spacing: Optional[float] = None, - subplot_titles: Optional[Union[List[str], tuple]] = None, - row_width: Optional[List[Union[float, int]]] = None, - specs: Optional[List[List[Optional[Dict[Any, Any]]]]] = None, - **kwargs, - ) -> "OpenBBFigure": - """Create a new Plotly figure with subplots. - - Parameters - ---------- - rows : `int`, optional - Number of rows, by default 1 - cols : `int`, optional - Number of columns, by default 1 - shared_xaxes : `bool`, optional - Whether to share x axes, by default True - vertical_spacing : `float`, optional - Vertical spacing between subplots, by default None - horizontal_spacing : `float`, optional - Horizontal spacing between subplots, by default None - subplot_titles : `Union[List[str], tuple]`, optional - Titles for each subplot, by default None - row_width : `List[Union[float, int]]`, optional - Width of each row, by default [1] - specs : `List[List[dict]]`, optional - Subplot specs, by default `[[{}] * cols] * rows` (all subplots are the same size) - """ - # We save the original kwargs to store them in the figure for later use - subplots_kwargs = dict( - rows=rows, - cols=cols, - shared_xaxes=shared_xaxes, - vertical_spacing=vertical_spacing, - horizontal_spacing=horizontal_spacing, - subplot_titles=subplot_titles, - row_width=row_width or [1] * rows, - specs=specs or [[{}] * cols] * rows, - **kwargs, - ) - - fig = make_subplots(**subplots_kwargs) - - kwargs = { - "multi_rows": rows > 1, - "subplots_kwargs": subplots_kwargs, - } - if specs and any( - spec.get("secondary_y", False) for row in specs for spec in row if spec - ): - kwargs["has_secondary_y"] = True - - return cls(fig, **kwargs) - - def add_trend( - self, - data: pd.DataFrame, - row: int = 1, - col: int = 1, - secondary_y: bool = False, - **kwargs, - ): - """Add a trend line to the figure. - - Parameters - ---------- - data : `pd.DataFrame` - Data to plot - row : `int`, optional - Row number, by default 1 - col : `int`, optional - Column number, by default 1 - secondary_y : `bool`, optional - Whether to plot on secondary y axis, by default None - """ - try: - for column, color in zip( - ["OC_High_trend", "OC_Low_trend"], [theme.up_color, theme.down_color] - ): - if column in data.columns: - name = column.split("_")[1].title() - trend = data.copy().dropna() - self.add_shape( - type="line", - name=f"{name} Trend", - x0=trend.index[0], - y0=trend[column].iloc[0], - x1=trend.index[-1], - y1=trend[column].iloc[-1], - line=dict(color=color, width=2), - row=row, - col=col, - secondary_y=secondary_y, - **kwargs, - ) - - except Exception: - console.print("[red]Error adding trend line[/red]") - - def add_histplot( - self, - dataset: Union[np.ndarray, pd.Series, TimeSeriesT], - name: Optional[Union[str, List[str]]] = None, - colors: Optional[List[str]] = None, - bins: Union[int, str] = 15, - curve: Literal["normal", "kde"] = "normal", - show_curve: bool = True, - show_rug: bool = True, - show_hist: bool = True, - forecast: bool = False, - row: int = 1, - col: int = 1, - ) -> None: - """Add a histogram with a curve and rug plot if desired. - - Parameters - ---------- - dataset : `Union[np.ndarray, pd.Series, TimeSeriesT]` - Data to plot - name : `Optional[Union[str, List[str]]]`, optional - Name of the plot, by default None - colors : `Optional[List[str]]`, optional - Colors of the plot, by default None - bins : `Union[int, str]`, optional - Number of bins, by default 15 - curve : `Literal["normal", "kde"]`, optional - Type of curve to plot, by default "normal" - show_curve : `bool`, optional - Whether to show the curve, by default True - show_rug : `bool`, optional - Whether to show the rug plot, by default True - show_hist : `bool`, optional - Whether to show the histogram, by default True - forecast : `bool`, optional - Whether the data is a darts forecast TimeSeries, by default False - row : `int`, optional - Row of the subplot, by default 1 - col : `int`, optional - Column of the subplot, by default 1 - """ - callback = stats.norm if curve == "normal" else stats.gaussian_kde - - def _validate_x(data: Union[np.ndarray, pd.Series, type[TimeSeriesT]]): - if forecast: - data = data.univariate_values() # type: ignore - if isinstance(data, pd.Series): - data = data.values - if isinstance(data, np.ndarray): - data = data.tolist() - if isinstance(data, list): - data = [data] - - return data - - valid_x = _validate_x(dataset) - - if isinstance(name, str): - name = [name] - - if isinstance(colors, str): - colors = [colors] - if not name: - name = [None] * len(valid_x) # type: ignore - if not colors: - colors = [None] * len(valid_x) # type: ignore - - max_y = 0 - for i0, (x_i, name_i, color_i) in enumerate(zip(valid_x, name, colors)): - if not color_i: - color_i = ( # noqa: PLW2901 - theme.up_color if i0 % 2 == 0 else theme.down_color - ) - - res_mean, res_std = np.mean(x_i), np.std(x_i) - res_min, res_max = min(x_i), max(x_i) - x = np.linspace(res_min, res_max, 100) - if show_hist: - if forecast: - components = list(dataset.components[:4]) # type: ignore - values = ( - dataset[components].all_values(copy=False).flatten(order="F") # type: ignore - ) - n_components = len(components) - n_entries = len(values) // n_components - for i, label in zip(range(n_components), components): - self.add_histogram( - x=values[i * n_entries : (i + 1) * n_entries], # noqa: E203 - name=label, - marker_color=color_i, - nbinsx=bins, - opacity=0.7, - row=row, - col=col, - ) - else: - self.add_histogram( - x=x_i, - name=name_i, - marker_color=color_i, - nbinsx=bins, - histnorm="probability density", - histfunc="sum", - opacity=0.7, - row=row, - col=col, - ) - - if show_rug: - self.add_scatter( - x=x_i, - y=[0.00002] * len(x_i), - name=name_i if len(name) < 2 else name[1], - mode="markers", - marker=dict( - color=theme.down_color, - symbol="line-ns-open", - size=10, - ), - row=row, - col=col, - ) - if show_curve: - # type: ignore - if curve == "kde": - curve_x = [None] * len(valid_x) - curve_y = [None] * len(valid_x) - # pylint: disable=consider-using-enumerate - for index in range(len(valid_x)): - curve_x[index] = [ # type: ignore - res_min + xx * (res_max - res_min) / 500 - for xx in range(500) - ] - curve_y[index] = stats.gaussian_kde(valid_x[index])( - curve_x[index] - ) - for index in range(len(valid_x)): - self.add_scatter( - x=curve_x[index], # type: ignore - y=curve_y[index], # type: ignore - name=name_i, - mode="lines", - showlegend=False, - marker=dict(color=color_i), - row=row, - col=col, - ) - max_y = max(max_y, max(curve_y[index]) * 1.2) # type: ignore - - else: - y = ( - callback(res_mean, res_std).pdf(x) - * len(valid_x[0]) - * (res_max - res_min) - / bins - ) - - self.add_scatter( - x=x, - y=y, - name=name_i, - mode="lines", - marker=dict(color=color_i), - showlegend=False, - row=row, - col=col, - ) - - max_y = max(max_y, *(y * 2)) - - self.update_yaxes( - position=0.0, - range=[0, max_y], - row=row, - col=col, - automargin=False, - autorange=False, - ) - - self.update_layout(barmode="overlay", bargap=0.01, bargroupgap=0) - - def is_image_export(self, export: Optional[str] = "") -> bool: - """Check if the export format is an image format. - - Parameters - ---------- - export : `str` - Export format - - Returns - ------- - `bool` - True if the export format is an image format, False otherwise - """ - if not export: - return False - - return any(ext in export for ext in ["jpg", "pdf", "png", "svg"]) - - def set_title( - self, title: str, wrap: bool = False, wrap_width: int = 80, **kwargs - ) -> "OpenBBFigure": - """Set the main title of the figure. - - Parameters - ---------- - title : `str` - Title of the figure - wrap : `bool` - If True, the title will be wrapped according to the wrap_width parameter - wrap_width : `int` - Width in characters to wrap the title if wrap is True, default is 80 - """ - if wrap: - title = "
".join(textwrap.wrap(title, width=wrap_width)) - - if kwargs.get("row", None) is not None and kwargs.get("col", None) is not None: - self.add_annotation( - text=title, - xref="x domain", - yref="y domain", - x=0.5, - y=1.0, - xanchor="center", - yanchor="bottom", - **kwargs, - ) - return self - - self.update_layout(title=dict(text=title, **kwargs)) - return self - - def set_xaxis_title( - self, - title: str, - row: Optional[int] = None, - col: Optional[int] = None, - **kwargs, - ) -> "OpenBBFigure": - """Set the x axis title of the figure or subplot (if row and col are specified). - - Parameters - ---------- - title : `str` - Title of the x axis - row : `int`, optional - Row number, by default None - col : `int`, optional - Column number, by default None - """ - self.update_xaxes(title=title, row=row, col=col, **kwargs) - return self - - def set_yaxis_title( - self, title: str, row: Optional[int] = None, col: Optional[int] = None, **kwargs - ) -> "OpenBBFigure": - """Set the y axis title of the figure or subplot (if row and col are specified). - - Parameters - ---------- - title : `str` - Title of the x axis - row : `int`, optional - Row number, by default None - col : `int`, optional - Column number, by default None - """ - self.set_secondary_axis(title=title, row=row, col=col, **kwargs) - return self - - def add_hline_legend( - self, - y: float, - name: str, - line: Optional[dict] = None, - legendrank: Optional[int] = None, - **kwargs, - ) -> None: - """Add a horizontal line with a legend label. - - Parameters - ---------- - y : `float` - y value of the line - name : `str` - Name of the to display in the legend - line : `dict` - Line style - legendrank : `int`, optional - Legend rank, by default None (e.g. 1 is above 2) - """ - if line is None: - line = {} - - self.add_hline( - y, - line=line, - ) - self.add_legend_label( - label=name, - mode="lines", - line_dash=line.get("dash", "solid"), - marker=dict(color=line.get("color", theme.line_color)), - legendrank=legendrank, - **kwargs, - ) - - def add_vline_legend( - self, - x: float, - name: str, - line: Optional[dict] = None, - legendrank: Optional[int] = None, - **kwargs, - ) -> None: - """Add a vertical line with a legend label. - - Parameters - ---------- - x : `float` - x value of the line - name : `str` - Name of the to display in the legend - line : `dict` - Line style - legendrank : `int`, optional - Legend rank, by default None (e.g. 1 is above 2) - """ - if line is None: - line = {} - - self.add_vline( - x, - line=line, - ) - self.add_legend_label( - label=name, - mode="lines", - line_dash=line.get("dash", "solid"), - marker=dict(color=line.get("color", theme.line_color)), - legendrank=legendrank, - **kwargs, - ) - - def horizontal_legend( - self, - x: float = 1, - y: float = 1.02, - xanchor: str = "right", - yanchor: str = "bottom", - orientation: str = "h", - **kwargs, - ) -> None: - """Set the legend to be horizontal. - - Parameters - ---------- - x : `float`, optional - The x position of the legend, by default 1 - y : `float`, optional - The y position of the legend, by default 1.02 - xanchor : `str`, optional - The x anchor of the legend, by default "right" - yanchor : `str`, optional - The y anchor of the legend, by default "bottom" - orientation : `str`, optional - The orientation of the legend, by default "h" - """ - self.update_layout( - legend=dict( - x=x, - y=y, - xanchor=xanchor, - yanchor=yanchor, - orientation=orientation, - **kwargs, - ) - ) - - @staticmethod - def chart_volume_scaling( - df_volume: pd.DataFrame, volume_ticks_x: int = 7 - ) -> Dict[str, list]: - """Takes df_volume and returns volume_ticks, tickvals for chart volume scaling - - Parameters - ---------- - df_volume : pd.DataFrame - Dataframe of volume (e.g. df_volume = df["Volume"]) - volume_ticks_x : int, optional - Number to multiply volume, by default 7 - - Returns - ------- - Dict[str, list] - {"range": volume_range, "ticks": tickvals} - """ - df_volume = df_volume.apply(lambda x: f"{x:.1f}") - df_volume = pd.to_numeric(df_volume.astype(float)) - - if isinstance(df_volume, pd.Series): - df_volume = df_volume.to_frame() - - volume_ticks = int(df_volume.max().max()) - round_digits = -3 - first_val = round(volume_ticks * 0.20, round_digits) - - for x, y in zip([2, 5, 6, 7, 8, 9, 10], [1, 4, 5, 6, 7, 8, 9]): - if len(str(volume_ticks)) > x: - round_digits = -y - first_val = round(volume_ticks * 0.20, round_digits) - - tickvals = [ - floor(first_val), - floor(first_val * 2), - floor(first_val * 3), - floor(first_val * 4), - ] - volume_range = [0, floor(volume_ticks * volume_ticks_x)] - - return {"range": volume_range, "ticks": tickvals} - - def add_inchart_volume( - self, - df_stock: pd.DataFrame, - close_col: Optional[str] = "Close", - volume_col: Optional[str] = "Volume", - row: Optional[int] = 1, - col: Optional[int] = 1, - volume_ticks_x: int = 7, - ) -> None: - """Add in-chart volume to a subplot. - - Parameters - ---------- - df_stock : `pd.DataFrame` - Dataframe of the stock - close_col : `str`, optional - Name of the close column, by default "Close" - volume_col : `str`, optional - Name of the volume column, by default "Volume" - row : `int`, optional - Row number, by default 2 - col : `int`, optional - Column number, by default 1 - volume_ticks_x : int, optional - Number to multiply volume, by default 7 - """ - colors = np.where( - df_stock.Open < df_stock[close_col], theme.up_color, theme.down_color - ) - vol_scale = self.chart_volume_scaling(df_stock[volume_col], volume_ticks_x) - self.add_bar( - x=df_stock.index, - y=df_stock[volume_col], - name="Volume", - marker_color=colors, - yaxis="y2", - row=row, - col=col, - opacity=0.7, - secondary_y=True, - ) - ticksize = 13 - (self.subplots_kwargs["rows"] // 2) - self.update_layout( - yaxis2=dict( - fixedrange=True, - side="left", - nticks=10, - range=vol_scale["range"], - tickvals=vol_scale["ticks"], - showgrid=False, - showline=False, - zeroline=False, - tickfont=dict(size=ticksize), - overlaying="y", - ), - yaxis=dict( - autorange=True, - automargin=True, - side="right", - fixedrange=False, - anchor="x", - layer="above traces", - ), - ) - - def add_legend_label( - self, - trace: Optional[str] = None, - label: Optional[str] = None, - mode: Optional[str] = None, - marker: Optional[dict] = None, - line_dash: Optional[str] = None, - legendrank: Optional[int] = None, - **kwargs, - ) -> None: - """Add a legend label. - - Parameters - ---------- - trace : `str`, optional - The name of the trace to use as a template, by default None - label : `str`, optional - The label to use, by default None (uses the trace name if trace is specified) - If trace is not specified, label must be specified - mode : `str`, optional - The mode to use, by default "lines" (uses the trace mode if trace is specified) - marker : `dict`, optional - The marker to use, by default dict() (uses the trace marker if trace is specified) - line_dash : `str`, optional - The line dash to use, by default "solid" (uses the trace line dash if trace is specified) - legendrank : `int`, optional - The legend rank, by default None (e.g. 1 is above 2) - - Raises - ------ - ValueError - If trace is not found - ValueError - If label is not specified and trace is not specified - """ - if trace: - for trace_ in self.data: - if trace_.name == trace: - for arg, default in zip( - [label, mode, marker, line_dash], - [trace, trace_.mode, trace_.marker, trace_.line_dash], - ): - if not arg and default: - arg = default # noqa: PLW2901 - - kwargs.update(dict(yaxis=trace_.yaxis)) - break - else: - raise ValueError(f"Trace '{trace}' not found") - - if not label: - raise ValueError("Label must be specified") - - self.add_scatter( - x=[None], - y=[None], - mode=mode or "lines", - name=label, - marker=marker or dict(), - line_dash=line_dash or "solid", - legendrank=legendrank, - **kwargs, - ) - - def show( - self, - *args, - external: bool = False, - export_image: Optional[Union[Path, str]] = "", - **kwargs, - ) -> Optional["OpenBBFigure"]: - """Show the figure. - - Parameters - ---------- - external : `bool`, optional - Whether to return the figure object instead of showing it, by default False - export_image : `Union[Path, str]`, optional - The path to export the figure image to, by default "" - cmd_xshift : `int`, optional - The x shift of the command source annotation, by default 0 - bar_width : `float`, optional - The width of the bars, by default 0.0001 - date_xaxis : `bool`, optional - Whether to check if the xaxis is a date axis, by default True - """ - self.cmd_xshift = kwargs.pop("cmd_xshift", self.cmd_xshift) - self.bar_width = kwargs.pop("bar_width", self.bar_width) - self._export_image = export_image - - if export_image and not plots_backend().isatty: - if isinstance(export_image, str): - export_image = Path(export_image).resolve() - export_image.touch() - - if kwargs.pop("margin", True): - self._adjust_margins() - - self._apply_feature_flags() - if kwargs.pop("date_xaxis", True): - self.add_rangebreaks() - self._xaxis_tickformatstops() - - self.update_traces(marker_line_width=self.bar_width, selector=dict(type="bar")) - self.update_traces( - selector=dict(type="scatter", hovertemplate=None), - hovertemplate="%{y}", - ) - - # Set modebar style - if plots_backend().isatty: - self.update_layout( # type: ignore - newshape_line_color=( - "gold" if theme.mapbox_style == "dark" else "#0d0887" - ), - modebar=dict( - orientation="v", - bgcolor="#2A2A2A" if theme.mapbox_style == "dark" else "gray", - color="#FFFFFF" if theme.mapbox_style == "dark" else "black", - activecolor="#d1030d" if theme.mapbox_style == "dark" else "blue", - ), - spikedistance=2, - hoverdistance=2, - ) - - if external or self._exported: - return self # type: ignore - - # We check if in headless mode to return the JSON - if strtobool(get_current_system().HEADLESS): - return self.to_json() - - kwargs.update(config=dict(scrollZoom=True, displaylogo=False)) - if plots_backend().isatty: - try: - # We check if we need to export the image - # This is done to avoid opening after exporting - if export_image: - self._exported = True - if config_terminal.HOLD: - # pylint: disable=import-outside-toplevel - from openbb_terminal.helper_funcs import command_location - - for trace in self.select_traces(): - if trace.name and "/" in trace.name: - continue - trace.name = f"{trace.name} {command_location}" - config_terminal.set_current_figure(self) - # We send the figure to the backend to be displayed - return None - return plots_backend().send_figure(self, export_image) - except Exception: - # If the backend fails, we just show the figure normally - # This is a very rare case, but it's better to have a fallback - if get_current_system().DEBUG_MODE: - console.print_exception() - - # We check if any figures were initialized before the backend failed - # If so, we show them with the default plotly backend - queue = plots_backend().get_pending() - for pending in queue: - fig = json.loads(pending).get("plotly", {}) - if not fig: - continue - pio.show(fig, *args, **kwargs) - - self.update_layout( - legend=dict( - orientation=( - "v" - if not self.layout.legend.orientation - else self.layout.legend.orientation - ), - ), - barmode="overlay", - bargap=0, - bargroupgap=0, - ) - - return pio.show(self, *args, **kwargs) - - def _xaxis_tickformatstops(self) -> None: - """Set the datetickformatstops for the xaxis if the x data is datetime.""" - if (dateindex := self.get_dateindex()) is None or list( - self.select_xaxes(lambda x: hasattr(x, "tickformat") and x.tickformat) - ): - return - - tickformatstops = [ - dict(dtickrange=[None, 86_400_000], value="%I:%M%p\n%b,%d"), - dict(dtickrange=[86_400_000, 604_800_000], value="%Y-%m-%d"), - ] - xhoverformat = "%I:%M%p %Y-%m-%d" - - # We check if daily data if the first and second time are the same - # since daily data will have the same time (2021-01-01 00:00:00) - if ( - not hasattr(dateindex[-1], "time") - or dateindex[-1].time() == dateindex[-2].time() - ): - xhoverformat = "%Y-%m-%d" - tickformatstops = [dict(dtickrange=[None, 604_800_000], value="%Y-%m-%d")] - - for entry in self._date_xaxs.values(): - self.update_xaxes( - tickformatstops=[ - *tickformatstops, - dict(dtickrange=[604_800_000, "M1"], value="%Y-%m-%d"), - dict(dtickrange=["M1", None], value="%Y-%m-%d"), - ], - type="date", - row=entry["row"], - col=entry["col"], - tick0=0.5, - ) - self.update_traces( - xhoverformat=xhoverformat, selector=dict(name=entry["name"]) - ) - - def get_subplots_dict(self) -> Dict[str, Dict[str, List[Any]]]: - """Return the subplots dict. - - Returns - ------- - `dict` - The subplots dict - """ - subplots: Dict[str, Dict[str, List[Any]]] = {} - - if not self.has_subplots: - return subplots - - grid_ref = self._validate_get_grid_ref() # pylint: disable=protected-access - for r, plot_row in enumerate(grid_ref): - for c, plot_refs in enumerate(plot_row): - if not plot_refs: - continue - for subplot_ref in plot_refs: - if subplot_ref.subplot_type == "xy": - xaxis, yaxis = subplot_ref.layout_keys - xref = xaxis.replace("axis", "") - yref = yaxis.replace("axis", "") - row = r + 1 - col = c + 1 - subplots.setdefault(xref, {}).setdefault(yref, []).append( - (row, col) - ) - - return subplots - - def get_dateindex(self) -> Optional[List[datetime]]: - """Return the dateindex of the figure. - - Returns - ------- - `list` - The dateindex - """ - output: Optional[List[datetime]] = None - subplots = self.get_subplots_dict() - - try: - false_y = list(self.select_traces(secondary_y=False)) - true_y = list(self.select_traces(secondary_y=True)) - except Exception: - false_y = [] - true_y = [] - - for trace in self.select_traces(): - if not hasattr(trace, "xaxis"): - continue - xref, yref = trace.xaxis, trace.yaxis - row, col = subplots.get(xref, {}).get(yref, [(None, None)])[0] - - if trace.x is not None and len(trace.x) > 5: - for x in trace.x[:2]: - if isinstance(x, (datetime, np.datetime64, pd.DatetimeIndex)): - output = trace.x - name = trace.name if hasattr(trace, "name") else f"{trace}" - - secondary_y: Optional[bool] = trace in true_y - if trace not in (false_y + true_y): - secondary_y = None - - self._date_xaxs[trace.xaxis] = { - "yaxis": trace.yaxis, - "name": name, - "row": row, - "col": col, - "secondary_y": secondary_y, - } - self._subplot_xdates.setdefault(row, {}).setdefault( - col, [] - ).append(trace.x) - - # We convert the dateindex to a list of datetime objects if it's a numpy array - if output is not None and isinstance(output[0], np.datetime64): - output = ( - pd.to_datetime(output).to_pydatetime().astype("datetime64[ms]").tolist() - ) - - return output - - def hide_date_gaps( - self, - df_data: pd.DataFrame, - row: Optional[int] = None, - col: Optional[int] = None, - ) -> None: - """Add rangebreaks to hide datetime gaps on the xaxis. - - Parameters - ---------- - df_data : `pandas.DataFrame` - The dataframe with the data. - row : `int`, optional - The row of the subplot to hide the gaps, by default None - col : `int`, optional - The column of the subplot to hide the gaps, by default None - """ - # We get the min and max dates - dt_start, dt_end = df_data.index.min(), df_data.index.max() - rangebreaks: List[Dict[str, Any]] = [] - - # if weekly or monthly data, we don't need to hide gaps - # this prevents distortions in the plot - check_freq = df_data.index.to_series().diff(-5).dt.days.abs().mode().iloc[0] - if check_freq > 7: - return - - # We get the missing days - is_daily = df_data.index[-1].time() == df_data.index[-2].time() - dt_days = pd.date_range(start=dt_start, end=dt_end, normalize=True) - - # We get the dates that are missing - dt_missing_days = list( - set(dt_days.strftime("%Y-%m-%d")) - set(df_data.index.strftime("%Y-%m-%d")) - ) - dt_missing_days = pd.to_datetime(dt_missing_days) - - if len(dt_missing_days) < 2_000: - rangebreaks = [dict(values=dt_missing_days)] - - df_data = df_data.sort_index() - # We add a rangebreak if the first and second time are not the same - # since daily data will have the same time (00:00) - if not is_daily: - for i in range(len(df_data) - 1): - if df_data.index[i + 1] - df_data.index[i] > timedelta(hours=2): - rangebreaks.insert( - 0, - dict( - bounds=[ - df_data.index[i] - + timedelta(minutes=60 - df_data.index[i].minute), - df_data.index[i + 1], - ] - ), - ) - - self.update_xaxes(rangebreaks=rangebreaks, row=row, col=col) - - def add_rangebreaks(self) -> None: - """Add rangebreaks to hide datetime gaps on the xaxis.""" - if self.get_dateindex() is None: - return - - for row, row_dict in self._subplot_xdates.items(): - for col, values in row_dict.items(): - try: - x_values = ( - pd.to_datetime(np.concatenate(values)) - .to_pydatetime() - .astype("datetime64[ms]") - ) - self.hide_date_gaps( - pd.DataFrame(index=x_values.tolist()), - row=row, - col=col, - ) - except ValueError: - continue - - def to_subplot( - self, - subplot: "OpenBBFigure", - row: int, - col: int, - secondary_y: bool = False, - **kwargs, - ) -> "OpenBBFigure": - """Return the figure as a subplot of another figure. - - Parameters - ---------- - subplot : `plotly.graph_objects.Figure` - The subplot - row : `int` - Row number - col : `int` - Column number - secondary_y : `bool`, optional - Whether to use the secondary y axis, by default False - - Returns - ------- - `plotly.graph_objects.Figure` - The subplot with the figure added - """ - for trace in self.data: - if kwargs: - trace.update(**kwargs) - - subplot.add_trace(trace, row=row, col=col, secondary_y=secondary_y) - subplot.set_xaxis_title(self.layout.xaxis.title.text, row=row, col=col) - subplot.set_yaxis_title(self.layout.yaxis.title.text, row=row, col=col) - - return subplot - - def to_html(self, *args, **kwargs) -> str: - """Return the figure as HTML.""" - self.update_traces(marker_line_width=0.0001, selector=dict(type="bar")) - kwargs.update( - dict( - config={"scrollZoom": True, "displaylogo": False}, - include_plotlyjs=kwargs.pop("include_plotlyjs", False), - full_html=False, - ) - ) - self._apply_feature_flags() - self._xaxis_tickformatstops() - - if not plots_backend().isatty and self.data[0].type != "table": - for key, max_val in zip(["l", "r", "b", "t"], [60, 60, 80, 40]): - if key in self.layout.margin and ( - self.layout.margin[key] is None - or (self.layout.margin[key] > max_val) - ): - self.layout.margin[key] = max_val - - orientation = "v" if self.layout.legend.orientation is None else "h" - - for annotation in self.select_annotations( - selector=dict(x=0, xanchor="right") - ): - annotation.font.size = ( - annotation.font.size - 1.8 if annotation.font.size else 10 - ) - - for trace in self.select_traces( - lambda trace: hasattr(trace, "legend") and trace.legend is not None - ): - if trace.legend in self.layout: - self.layout[trace.legend].font.size = 12 - - self.update_layout( - legend=dict(orientation=orientation, font=dict(size=12)), - font=dict(size=14), - ) - self.update_xaxes(tickfont=dict(size=13)) - self.update_yaxes(tickfont=dict(size=13)) - - return super().to_html(*args, **kwargs) - - @staticmethod - def row_colors(data: pd.DataFrame) -> Optional[List[str]]: - """Return the row colors of the table. - - Parameters - ---------- - data : `pandas.DataFrame` - The dataframe - - Returns - ------- - `list` - The list of colors - """ - row_count = len(data) - # we determine how many rows in `data` and then create a list with alternating - # row colors - row_odd_count = floor(row_count / 2) + row_count % 2 - row_even_count = floor(row_count / 2) - odd_list = [PLT_TBL_ROW_COLORS[0]] * row_odd_count - even_list = [PLT_TBL_ROW_COLORS[1]] * row_even_count - color_list = [x for y in zip(odd_list, even_list) for x in y] - if row_odd_count > row_even_count: - color_list.append(PLT_TBL_ROW_COLORS[0]) - - return color_list - - @staticmethod - def _tbl_values(data: pd.DataFrame, print_index: bool) -> Tuple[List[str], List]: - """Return the values of the table. - - Parameters - ---------- - data : `pandas.DataFrame` - The dataframe to convert - print_index : `bool` - Whether to print the index - - Returns - ------- - `tuple` - The header values and the cell values - """ - if print_index: - header_values = list( - [data.index.name if data.index.name is not None else "", *data.columns] - ) - cell_values = [data.index, *[data[col] for col in data]] - - else: - header_values = data.columns.to_list() - cell_values = [data[col] for col in data] - - header_values = [f"{x}" for x in header_values] - - return header_values, cell_values - - @classmethod - def to_table( - cls, - data: pd.DataFrame, - columnwidth: Optional[List[Union[int, float]]] = None, - print_index: bool = True, - **kwargs, - ) -> "OpenBBFigure": - """Convert a dataframe to a table figure. - - Parameters - ---------- - data : `pandas.DataFrame` - The dataframe to convert - columnwidth : `list`, optional - The width of each column, by default None (auto) - print_index : `bool`, optional - Whether to print the index, by default True - height : `int`, optional - The height of the table, by default len(data.index) * 28 + 25 - width : `int`, optional - The width of the table, by default sum(columnwidth) * 8.7 - - Returns - ------- - `plotly.graph_objects.Figure` - The figure as a table - """ - if not columnwidth: - # we get the length of each column using the max length of the column - # name and the max length of the column values as the column width - columnwidth = [ - max(len(str(data[col].name)), data[col].astype(str).str.len().max()) - for col in data.columns - ] - # we add the length of the index column if we are printing the index - if print_index: - columnwidth.insert( - 0, - max( - len(str(data.index.name)), - data.index.astype(str).str.len().max(), - ), - ) - - # we add a percentage of max to the min column width - columnwidth = [ - int(x + (max(columnwidth) - min(columnwidth)) * 0.2) - for x in columnwidth - ] - - header_values, cell_values = cls._tbl_values(data, print_index) - - if (height := kwargs.get("height", None)) and height < len( - data.index - ) * 28 + 25: - kwargs.pop("height") - if (width := kwargs.get("width", None)) and width < sum(columnwidth) * 8.7: - kwargs.pop("width") - - height = kwargs.pop("height", len(data.index) * 28 + 25) - width = kwargs.pop("width", sum(columnwidth) * 8.7) - - fig: OpenBBFigure = cls() - fig.add_table( - header=dict(values=header_values), - cells=dict( - values=cell_values, - align="left", - height=25, - ), - columnwidth=columnwidth, - **kwargs, - ) - fig.update_layout( - height=height, - width=width, - template="openbb_tables", - margin=dict(l=0, r=0, b=0, t=0, pad=0), - font=dict(size=14), - ) - - return fig - - def _adjust_margins(self) -> None: - """Adjust the margins of the figure.""" - if self._margin_adjusted: - return - - margin_add = ( - dict(l=80, r=60, b=80, t=40, pad=0) - if not self._has_secondary_y or not self.has_subplots - else dict(l=60, r=50, b=85, t=40, pad=0) - ) - - # We adjust margins - if plots_backend().isatty: - for key in ["l", "r", "b", "t", "pad"]: - if key in self.layout.margin and self.layout.margin[key] is not None: - self.layout.margin[key] += margin_add.get(key, 0) - else: - self.layout.margin[key] = margin_add.get(key, 0) - - if not plots_backend().isatty: - org_margin = self.layout.margin - margin = dict(l=40, r=60, b=80, t=50) - for key, max_val in zip(["l", "r", "b", "t"], [60, 50, 80, 50]): - org = org_margin[key] or 0 - if (org + margin[key]) > max_val: - self.layout.margin[key] = max_val - else: - self.layout.margin[key] = org + margin[key] - - self._margin_adjusted = True - - def _set_watermark(self) -> None: - """Set the watermark for OpenBB Terminal.""" - if ( - not plots_backend().isatty - or not get_current_user().preferences.PLOT_ENABLE_PYWRY - or isinstance(self._export_image, Path) - and self._export_image.suffix in [".svg", ".pdf"] - ): - self.add_annotation( - yref="paper", - xref="paper", - x=1, - y=0, - showarrow=False, - text="OpenBB Terminal", - font_size=17, - font_color="gray", - opacity=0.5, - xanchor="right", - yanchor="bottom", - yshift=-80, - xshift=40, - ) - - # pylint: disable=import-outside-toplevel - def _add_cmd_source(self) -> None: - """Set the watermark for OpenBB Terminal.""" - from openbb_terminal.helper_funcs import command_location - - if command_location: - yaxis = self.layout.yaxis - yaxis2 = self.layout.yaxis2 if hasattr(self.layout, "yaxis2") else None - xshift = -70 if yaxis.side == "right" else -80 - - if self.layout.margin["l"] > 100: - xshift -= 50 if self._added_logscale else 40 - - if ( - yaxis2 - and (yaxis.title.text and yaxis2.title.text) - and (yaxis.side == "left" or yaxis2.side == "left") - ): - self.layout.margin["l"] += 20 - - if (yaxis2 and yaxis2.side == "left") or yaxis.side == "left": - title = ( - yaxis.title.text - if not yaxis2 or yaxis2.side != "left" - else yaxis2.title.text - ) - xshift = -110 if not title else -135 - self.layout.margin["l"] += 60 - - self.add_annotation( - x=0, - y=0.5, - yref="paper", - xref="paper", - text=command_location, - textangle=-90, - font_size=24, - font_color="gray" if theme.mapbox_style == "dark" else "black", - opacity=0.5, - yanchor="middle", - xanchor="left", - xshift=xshift + self.cmd_xshift, - ) - - # pylint: disable=import-outside-toplevel - def _apply_feature_flags(self) -> None: - """Apply watermark and command source annotations.""" - if self._feature_flags_applied: - return - - self._add_cmd_source() - self._set_watermark() - - self._feature_flags_applied = True - - def add_logscale_menus(self, yaxis: str = "yaxis") -> None: - """Set the menus for the figure.""" - self._added_logscale = True - bg_color = "#000000" if theme.mapbox_style == "dark" else "#FFFFFF" # type: ignore - font_color = "#FFFFFF" if theme.mapbox_style == "dark" else "#000000" # type: ignore - self.update_layout( - xaxis=dict( - rangeslider=dict(visible=False), - rangeselector=dict( - bgcolor=bg_color, - font=dict(color=font_color), - buttons=list( - [ - dict( - count=1, - label="1M", - step="month", - stepmode="backward", - ), - dict( - count=3, - label="3M", - step="month", - stepmode="backward", - ), - dict(count=1, label="YTD", step="year", stepmode="todate"), - dict( - count=1, - label="1y", - step="year", - stepmode="backward", - ), - dict(step="all"), - ] - ), - ), - ), - bargap=0, - bargroupgap=0, - ) - - self.update_layout( - updatemenus=[ - dict( - bgcolor=bg_color, - font=dict(color=font_color, size=14), - buttons=[ - dict( - label="linear ", - method="relayout", - args=[{f"{yaxis}.type": "linear"}], - ), - dict( - label="log", - method="relayout", - args=[{f"{yaxis}.type": "log"}], - ), - ], - y=1.07, - x=-0.01, - ) - ], - ) - - def add_corr_plot( - self, - series: pd.DataFrame, - max_lag: int = 20, - m: Optional[int] = None, - alpha: Optional[float] = 0.05, - marker: Optional[dict] = None, - row: Optional[int] = None, - col: Optional[int] = None, - pacf: bool = False, - **kwargs, - ) -> None: - """Add a correlation plot to a figure object. - - Parameters - ---------- - fig : OpenBBFigure - Figure object to add plot to - series : pd.DataFrame - Dataframe to look at - max_lag : int, optional - Number of lags to look at, by default 15 - m: Optional[int] - Optionally, a time lag to highlight on the plot. Default is none. - alpha: Optional[float] - Optionally, a significance level to highlight on the plot. Default is 0.05. - row : int, optional - Row to add plot to, by default None - col : int, optional - Column to add plot to, by default None - pacf : bool, optional - Flag to indicate whether to use partial autocorrelation or not, by default False - """ - mode = "markers+lines" if marker else "lines" - line = kwargs.pop("line", None) - - def _prepare_data_corr_plot(x, lags): - zero = True - irregular = False - if lags is None: - # GH 4663 - use a sensible default value - nobs = x.shape[0] - lim = min(int(np.ceil(10 * np.log10(nobs))), nobs - 1) - lags = np.arange(not zero, lim + 1) - elif np.isscalar(lags): - lags = np.arange(not zero, int(lags) + 1) # +1 for zero lag - else: - irregular = True - lags = np.asanyarray(lags).astype(int) - nlags = lags.max(0) - - return lags, nlags, irregular - - lags, nlags, irregular = _prepare_data_corr_plot(series, max_lag) - - callback = sm.tsa.stattools.pacf if pacf else sm.tsa.stattools.acf - if not pacf: - kwargs.update(dict(fft=False)) - - acf_x = callback( - series, - nlags=nlags, - alpha=alpha, - **kwargs, - ) - - acf_x, confint = acf_x[:2] if not pacf else acf_x - - if irregular: - acf_x = acf_x[lags] - - try: - confint = confint[lags] - if lags[0] == 0: - lags = lags[1:] - confint = confint[1:] - acf_x = acf_x[1:] - lags = lags.astype(float) - lags[0] -= 0.5 - lags[-1] += 0.5 - - upp_band = confint[:, 0] - acf_x - low_band = confint[:, 1] - acf_x - - # pylint: disable=C0200 - for x in range(len(acf_x)): - self.add_scatter( - x=(x, x), - y=(0, acf_x[x]), - mode=mode, - marker=marker, - line=line, - line_width=(2 if m is not None and x == m else 1), - row=row, - col=col, - ) - - self.add_scatter( - x=lags, - y=upp_band, - mode="lines", - line_color="rgba(0, 0, 0, 0)", - opacity=0, - row=row, - col=col, - ) - - self.add_scatter( - x=lags, - y=low_band, - mode="lines", - fillcolor="rgba(255, 217, 0, 0.30)", - fill="tonexty", - line_color="rgba(0, 0, 0, 0.0)", - opacity=0, - row=row, - col=col, - ) - self.add_scatter( - x=[0, max_lag + 1], - y=[0, 0], - mode="lines", - line_color="white", - row=row, - col=col, - ) - self.update_traces(showlegend=False) - - except ValueError: - pass diff --git a/openbb_terminal/core/plots/plotly_ta/__init__.py b/openbb_terminal/core/plots/plotly_ta/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/plots/plotly_ta/base.py b/openbb_terminal/core/plots/plotly_ta/base.py deleted file mode 100644 index 0a4019d8377..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/base.py +++ /dev/null @@ -1,198 +0,0 @@ -from typing import Any, Callable, Dict, Iterator, List, Optional, Type - -import pandas as pd - -from .data_classes import ChartIndicators, TAIndicator - - -def columns_regex(df_ta: pd.DataFrame, name: str) -> List[str]: - """Return columns that match regex name""" - column_name = df_ta.filter(regex=rf"{name}(?=[^\d]|$)").columns.tolist() - - return column_name - - -class Indicator: - """Class for technical indicator.""" - - def __init__( - self, - func: Callable, - name: str = "", - **attrs: Any, - ) -> None: - self.func = func - self.name = name - self.attrs = attrs - - def __call__(self, *args: Any, **kwargs: Any) -> Any: - return self.func(*args, **kwargs) - - -class PluginMeta(type): - """Metaclass for all Plotly plugins.""" - - __indicators__: List[Indicator] = [] - __static_methods__: list = [] - __ma_mode__: List[str] = [] - __inchart__: List[str] = [] - __subplots__: List[str] = [] - - def __new__(mcs: Type["PluginMeta"], *args: Any, **kwargs: Any) -> "PluginMeta": - name, bases, attrs = args - indicators: Dict[str, Indicator] = {} - cls_attrs: Dict[str, list] = { - "__ma_mode__": [], - "__inchart__": [], - "__subplots__": [], - } - new_cls = super().__new__(mcs, name, bases, attrs, **kwargs) - for base in reversed(new_cls.__mro__): - for elem, value in base.__dict__.items(): - if elem in indicators: - del indicators[elem] - - is_static_method = isinstance(value, staticmethod) - if is_static_method: - value = value.__func__ # noqa: PLW2901 - if isinstance(value, Indicator): - if is_static_method: - raise TypeError( - f"Indicator {value.name} can't be a static method" - ) - indicators[value.name] = value - elif is_static_method and elem not in new_cls.__static_methods__: - new_cls.__static_methods__.append(elem) - - if elem in ["__ma_mode__", "__inchart__", "__subplots__"]: - cls_attrs[elem].extend(value) - - new_cls.__indicators__ = list(indicators.values()) - new_cls.__static_methods__ = list(set(new_cls.__static_methods__)) - new_cls.__ma_mode__ = list(set(cls_attrs["__ma_mode__"])) - new_cls.__inchart__ = list(set(cls_attrs["__inchart__"])) - new_cls.__subplots__ = list(set(cls_attrs["__subplots__"])) - - return new_cls - - def __iter__(cls: Type["PluginMeta"]) -> Iterator[Indicator]: # type: ignore - return iter(cls.__indicators__) - - # pylint: disable=unused-argument - def __init__(cls, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - -class PltTA(metaclass=PluginMeta): - """The base class that all Plotly plugins must inherit from.""" - - indicators: ChartIndicators - intraday: bool = False - df_stock: pd.DataFrame - df_ta: pd.DataFrame - df_fib: pd.DataFrame - close_column: Optional[str] = "Close" - params: Dict[str, TAIndicator] = {} - inchart_colors: List[str] = [] - show_volume: bool = True - - __static_methods__: list = [] - __indicators__: List[Indicator] = [] - __ma_mode__: List[str] = [] - __inchart__: List[str] = [] - __subplots__: List[str] = [] - - # pylint: disable=unused-argument - def __new__(cls, *args: Any, **kwargs: Any) -> "PltTA": - if cls is PltTA: - raise TypeError("Can't instantiate abstract class Plugin directly") - self = super().__new__(cls) - - static_methods = cls.__static_methods__ - indicators = cls.__indicators__ - - for item in indicators: - # we make sure that the indicator is bound to the instance - if not hasattr(self, item.name): - setattr(self, item.name, item.func.__get__(self, cls)) - - for static_method in static_methods: - if not hasattr(self, static_method): - setattr(self, static_method, staticmethod(getattr(self, static_method))) - - for attr, value in cls.__dict__.items(): - if attr in [ - "__ma_mode__", - "__inchart__", - "__subplots__", - ] and value not in getattr(self, attr): - getattr(self, attr).extend(value) - - return self - - @property - def ma_mode(self) -> List[str]: - return list(set(self.__ma_mode__)) - - @ma_mode.setter - def ma_mode(self, value: List[str]): - self.__ma_mode__ = value - - def add_plugins(self, plugins: List["PltTA"]) -> None: - """Add plugins to current instance""" - for plugin in plugins: - for item in plugin.__indicators__: - # pylint: disable=unnecessary-dunder-call - if not hasattr(self, item.name): - setattr(self, item.name, item.func.__get__(self, type(self))) - self.__indicators__.append(item) - - for static_method in plugin.__static_methods__: - if not hasattr(self, static_method): - setattr( - self, static_method, staticmethod(getattr(self, static_method)) - ) - for attr, value in plugin.__dict__.items(): - if attr in [ - "__ma_mode__", - "__inchart__", - "__subplots__", - ] and value not in getattr(self, attr): - getattr(self, attr).extend(value) - - def remove_plugins(self, plugins: List["PltTA"]) -> None: - """Remove plugins from current instance""" - for plugin in plugins: - for item in plugin.__indicators__: - delattr(self, item.name) - self.__indicators__.remove(item) - - for static_method in plugin.__static_methods__: - delattr(self, static_method) - - def __iter__(self) -> Iterator[Indicator]: - return iter(self.__indicators__) - - def get_float_precision(self) -> str: - """Returns f-string precision format""" - price = self.df_stock[self.close_column].tail(1).values[0] - float_precision = ( - ",.2f" if price > 1.10 else "" if len(str(price)) < 8 else ".6f" - ) - return float_precision - - -def indicator( - name: str = "", - **attrs: Any, -) -> Callable: - """Decorator for adding indicators to a plugin class.""" - attrs["name"] = name - - def decorator(func: Callable) -> Indicator: - if not attrs.pop("name", ""): - name = func.__name__ - - return Indicator(func, name, **attrs) - - return decorator diff --git a/openbb_terminal/core/plots/plotly_ta/data_classes.py b/openbb_terminal/core/plots/plotly_ta/data_classes.py deleted file mode 100644 index 8ec3c9141f9..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/data_classes.py +++ /dev/null @@ -1,347 +0,0 @@ -# pylint: disable=C0302,R0915,R0914,R0913,R0903,R0904 - -import logging -import sys -from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Union - -import pandas as pd -import pandas_ta as ta - -from openbb_terminal.base_helpers import console -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - -# pylint: disable=E1123 -datacls_kwargs = {"slots": True} if sys.version_info >= (3, 10) else {} - - -def columns_regex(df_ta: pd.DataFrame, name: str) -> List[str]: - """Return columns that match regex name""" - column_name = df_ta.filter(regex=rf"{name}(?=[^\d]|$)").columns.tolist() - - return column_name - - -@dataclass(**datacls_kwargs) -class Arguments: - label: str - values: Any - - def __post_init__(self): - if isinstance(self.values, list) and len(self.values) == 1: - self.values = self.values[0] - - -@dataclass(**datacls_kwargs) -class TAIndicator: - name: str - args: List[Arguments] - - def __post_init__(self): - self.args = [Arguments(**arg) for arg in self.args] - - def __iter__(self): - """Return iterator""" - return iter(self.args) - - def get_args(self, label: str) -> Union[Arguments, None]: - """Return arguments by label""" - output = None - for opt in self.args: - if opt.label == label: - output = opt - return output - - def get_argument_values(self, label: str) -> Union[List[Any], Any]: - """Returns arguments values by label""" - output = [] - options = self.get_args(label) - if options is not None: - output = options.values - return output - - -@dataclass(**datacls_kwargs) -class ChartIndicators: - indicators: Optional[List[TAIndicator]] = None - - def __post_init__(self): - self.indicators = ( - [TAIndicator(**indicator) for indicator in self.indicators] - if self.indicators - else [] - ) - - def get_indicator(self, name: str) -> Union[TAIndicator, None]: - """Returns indicator with given name""" - output = None - for indicator in self.indicators: # type: ignore - if indicator.name == name: - output = indicator - return output - - def get_indicator_args(self, name: str, label: str) -> Union[Arguments, None]: - """Returns argument values for given indicator and label""" - output = None - indicator = self.get_indicator(name) - if indicator is not None: - output = indicator.get_args(label) - if output is not None: - output = output.values - return output - - def get_indicators(self) -> Optional[List[TAIndicator]]: - """Return active indicators and their arguments""" - return self.indicators - - def get_params(self) -> Dict[str, TAIndicator]: - """Return dictionary of active indicators and their arguments""" - output = {} - if self.indicators: - output = {indicator.name: indicator for indicator in self.indicators} - return output - - def get_active_ids(self) -> List[str]: - """Returns list of names of active indicators""" - active_ids = [] - if self.indicators: - active_ids = [indicator.name for indicator in self.indicators] - return active_ids - - def get_arg_names(self, name: str) -> List[str]: - """Returns list of argument labels for given indicator""" - output = [] - indicator = self.get_indicator(name) - if indicator is not None: - for opt in indicator.args: - output.append(opt.label) - return output - - def get_options_dict(self, name: str) -> Dict[str, Optional[Arguments]]: - """Returns dictionary of argument labels and values for given indicator""" - output = None - options = self.get_arg_names(name) - if options: - output = {} - for opt in options: - output[opt] = self.get_indicator_args(name, opt) - - return output - - @classmethod - def from_dict(cls, indicators: Dict[str, Dict[str, Any]]) -> "ChartIndicators": - """Return ChartIndicators from dictionary""" - data = [] - for indicator in indicators: - args = [] - for arg in indicators[indicator]: - args.append({"label": arg, "values": indicators[indicator][arg]}) - data.append({"name": indicator, "args": args}) - - return cls(indicators=data) # type: ignore - - def to_dataframe( - self, df_ta: pd.DataFrame, ma_mode: Optional[List[str]] = None - ) -> pd.DataFrame: - """Calculate technical analysis indicators and return dataframe""" - output = df_ta.copy() - if not output.empty and self.indicators: - try: - output = TA_Data(output, self, ma_mode).to_dataframe() - except Exception as err: - logger.error(err) - - return output - - def get_indicator_data(self, df_ta: pd.DataFrame, indicator: TAIndicator, **kwargs): - """Return dataframe with technical analysis indicators""" - output = None - if self.indicators: - try: - output = TA_Data(df_ta, self).get_indicator_data(indicator, **kwargs) - except Exception as err: - logger.error(err) - - return output - - def remove_indicator(self, name: str) -> None: - """Remove indicator from active indicators""" - if self.indicators: - for indicator in self.indicators: - if indicator.name == name: - self.indicators.remove(indicator) - - -class TA_Data: - """Process technical analysis data - - - Parameters - ---------- - df_ta : pd.DataFrame - Dataframe with OHLCV data - indicators : Union[ChartIndicators, Dict[str, Dict[str, Any]]] - ChartIndicators object or dictionary with indicators and arguments - Example: - dict( - sma=dict(length=[20, 50, 100]), - adx=dict(length=14), - macd=dict(fast=12, slow=26, signal=9), - rsi=dict(length=14), - ) - - - Methods - ------- - to_dataframe() - Return dataframe with technical analysis indicators - get_indicator_data(indicator: TAIndicator, **kwargs) - Return dataframe given indicator and arguments - - """ - - @log_start_end(log=logger) - def __init__( - self, - df_ta: Union[pd.DataFrame, pd.Series], - indicators: Union[ChartIndicators, Dict[str, Dict[str, Any]]], - ma_mode: Optional[List[str]] = None, - ): - if isinstance(df_ta, pd.Series): - df_ta = df_ta.to_frame() - - if not isinstance(indicators, ChartIndicators): - indicators = ChartIndicators.from_dict(indicators) - - self.df_ta: pd.DataFrame = df_ta - self.indicators: ChartIndicators = indicators - self.ma_mode: List[str] = ma_mode or ["sma", "ema", "wma", "hma", "zlma", "rma"] - self.close_col = ta_helpers.check_columns(df_ta) - if self.close_col is None: - raise ValueError("No close column found in dataframe") - - self.columns: Dict[str, List[str]] = { - "ad": ["High", "Low", self.close_col, "Volume"], - "adosc": ["High", "Low", self.close_col, "Volume"], - "adx": ["High", "Low", self.close_col], - "aroon": ["High", "Low"], - "atr": ["High", "Low", self.close_col], - "cci": ["High", "Low", self.close_col], - "donchian": ["High", "Low"], - "fisher": ["High", "Low"], - "kc": ["High", "Low", self.close_col], - "obv": [self.close_col, "Volume"], - "stoch": ["High", "Low", self.close_col], - "vwap": ["High", "Low", self.close_col, "Volume"], - } - - self.has_volume = "Volume" in df_ta.columns and bool(df_ta["Volume"].sum() > 0) - - def get_indicator_data(self, indicator: TAIndicator, **args) -> pd.DataFrame: - """Returns dataframe with indicator data - - Parameters - ---------- - indicator : TAIndicator - TAIndicator object - args : dict - Arguments for given indicator - - Returns - ------- - pd.DataFrame - Dataframe with indicator data - """ - output: Optional[pd.DataFrame] = None - if indicator and indicator.name in self.ma_mode: - if isinstance(indicator.get_argument_values("length"), list): - df_ta = pd.DataFrame() - - for length in indicator.get_argument_values("length"): - df_ma = getattr(ta, indicator.name)( - self.df_ta[self.close_col], length=length - ) - if not isinstance(df_ma, pd.Series) or df_ma.empty: - console.print( - f"Error processing {indicator.name.upper()}({length}) not enough data", - style="bold red", - ) - continue - df_ta.insert(0, f"{indicator.name.upper()}_{length}", df_ma) - - output = df_ta - - else: - output = getattr(ta, indicator.name)( - self.df_ta[self.close_col], - length=indicator.get_argument_values("length"), - ) - if indicator.name == "zlma" and output is not None: - output.name = output.name.replace("ZL_EMA", "ZLMA") - - elif indicator.name == "vwap": - ta_columns = self.columns[indicator.name] - ta_columns = [self.df_ta[col] for col in ta_columns] # type: ignore - - output = getattr(ta, indicator.name)( - *ta_columns, - ) - elif indicator.name in self.columns: - ta_columns = self.columns[indicator.name] - ta_columns = [self.df_ta[col] for col in ta_columns] # type: ignore - - if indicator.get_argument_values("use_open") is True: - ta_columns.append(self.df_ta["Open"]) - - output = getattr(ta, indicator.name)(*ta_columns, **args) - else: - output = getattr(ta, indicator.name)(self.df_ta[self.close_col], **args) - - # Drop NaN values from output and return None if empty - if output is not None: - output = output.dropna() - if output.empty: - console.print( - f"{indicator.name.upper()} {args} returned empty dataframe", - style="yellow", - ) - output = None - - return output - - def to_dataframe(self) -> pd.DataFrame: - """Returns dataframe with all indicators""" - active_indicators = self.indicators.get_indicators() - - if not active_indicators: - return None - - output = self.df_ta - for indicator in active_indicators: - if ( - indicator.name in self.columns - and "Volume" in self.columns[indicator.name] - and not self.has_volume - ): - continue - if indicator.name in ["fib", "srlines", "clenow", "demark", "ichimoku"]: - continue - try: - indicator_data = self.get_indicator_data( - indicator, - **self.indicators.get_options_dict(indicator.name) or {}, - ) - except Exception as e: - console.print( - f"Error processing indicator {indicator.name}: {e}", - style="bold red", - ) - indicator_data = None - - if indicator_data is not None: - output = output.join(indicator_data).interpolate("linear") - - return output diff --git a/openbb_terminal/core/plots/plotly_ta/plugins/custom_indicators_plugin.py b/openbb_terminal/core/plots/plotly_ta/plugins/custom_indicators_plugin.py deleted file mode 100644 index 5ea0cbe2615..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/plugins/custom_indicators_plugin.py +++ /dev/null @@ -1,207 +0,0 @@ -from datetime import datetime, timedelta - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.common.technical_analysis.custom_indicators_model import ( - calculate_fib_levels, -) -from openbb_terminal.core.plots.config.openbb_styles import PLT_FIB_COLORWAY -from openbb_terminal.core.plots.plotly_ta.base import PltTA, indicator - - -class Custom(PltTA): - """Volatility technical indicators""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @indicator() - def plot_srlines(self, fig: OpenBBFigure, df_ta: pd.DataFrame): - """Adds support and resistance lines to plotly figure""" - window = self.params["srlines"].get_argument_values("window") - window = window[0] if isinstance(window, list) and len(window) > 0 else 200 - - def is_far_from_level(value, levels, df_stock): - ave = np.mean(df_stock["High"] - df_stock["Low"]) - return np.sum([abs(value - level) < ave for _, level in levels]) == 0 - - def is_support(df, i): - cond1 = df["Low"][i] < df["Low"][i - 1] - cond2 = df["Low"][i] < df["Low"][i + 1] - cond3 = df["Low"][i + 1] < df["Low"][i + 2] - cond4 = df["Low"][i - 1] < df["Low"][i - 2] - return cond1 and cond2 and cond3 and cond4 - - def is_resistance(df, i): - cond1 = df["High"][i] > df["High"][i - 1] - cond2 = df["High"][i] > df["High"][i + 1] - cond3 = df["High"][i + 1] > df["High"][i + 2] - cond4 = df["High"][i - 1] > df["High"][i - 2] - return cond1 and cond2 and cond3 and cond4 - - df_ta2 = df_ta.copy() - today = pd.to_datetime(datetime.now(), unit="ns") - start_date = pd.to_datetime(datetime.now() - timedelta(days=window), unit="ns") - - df_ta2 = df_ta2.loc[(df_ta2.index >= start_date) & (df_ta2.index < today)] - - if df_ta2.index[-2].date() != df_ta2.index[-1].date(): - interval = 1440 - else: - interval = (df_ta2.index[1] - df_ta2.index[0]).seconds / 60 - - if interval <= 15: - cut_days = 1 if interval < 15 else 2 - dt_unique_days = df_ta2.index.normalize().unique() - df_ta2 = df_ta2.loc[ - (df_ta2.index >= pd.to_datetime(dt_unique_days[-cut_days], unit="ns")) - & (df_ta2.index < today) - ].copy() - - levels: list = [] - x_range = df_ta2.index[-1].replace(hour=15, minute=59) - if interval > 15: - x_range = df_ta2.index[-1] + timedelta(days=15) - if x_range.weekday() > 4: - x_range = x_range + timedelta(days=7 - x_range.weekday()) - - elif df_ta2.index[-1] >= today.replace(hour=15, minute=0): - x_range = (df_ta2.index[-1] + timedelta(days=1)).replace(hour=11, minute=0) - if x_range.weekday() > 4: - x_range = x_range + timedelta(days=7 - x_range.weekday()) - - for i in range(2, len(df_ta2) - 2): - if is_support(df_ta2, i): - lv = df_ta2["Low"][i] - if is_far_from_level(lv, levels, df_ta2): - levels.append((i, lv)) - fig.add_scatter( - x=[df_ta.index[0], x_range], - y=[lv, lv], - opacity=1, - mode="lines+text", - text=["", f"{lv:{self.get_float_precision()}}"], - textposition="top center", - textfont=dict( - family="Arial Black", color="rgb(120, 70, 200)", size=10 - ), - line=dict( - width=2, dash="dash", color="rgba(120, 70, 200, 0.70)" - ), - connectgaps=True, - showlegend=False, - row=1, - col=1, - secondary_y=False, - ) - elif is_resistance(df_ta2, i): - lv = df_ta2["High"][i] - if is_far_from_level(lv, levels, df_ta2): - levels.append((i, lv)) - fig.add_scatter( - x=[df_ta.index[0], x_range], - y=[lv, lv], - opacity=1, - mode="lines+text", - text=["", f"{lv:{self.get_float_precision()}}"], - textposition="top center", - textfont=dict( - family="Arial Black", color="rgb(120, 70, 200)", size=10 - ), - line=dict( - width=2, dash="dash", color="rgba(120, 70, 200, 0.70)" - ), - connectgaps=True, - showlegend=False, - row=1, - col=1, - secondary_y=False, - ) - - return fig - - @indicator() - def plot_fib(self, fig: OpenBBFigure, df_ta: pd.DataFrame): - """Adds fibonacci to plotly figure""" - limit = self.params["fib"].get_argument_values("limit") or 120 - start_date = self.params["fib"].get_argument_values("start_date") or None - end_date = self.params["fib"].get_argument_values("end_date") or None - ( - df_fib, - min_date, - max_date, - min_pr, - max_pr, - lvl_text, - ) = calculate_fib_levels(df_ta, limit, start_date, end_date) - levels = df_fib.Price - fibs = [ - "0", - "0.235", - "0.382", - "0.5", - "0.618", - "0.65", - "1", - ] - min_date = pd.to_datetime(min_date).to_pydatetime() - max_date = pd.to_datetime(max_date).to_pydatetime() - self.df_fib = df_fib - - fig.add_scatter( - x=[min_date, max_date], - y=[min_pr, max_pr], - opacity=0.85, - mode="lines", - connectgaps=True, - line=PLT_FIB_COLORWAY[8], - showlegend=False, - row=1, - col=1, - secondary_y=False, - ) - df_ta2 = df_ta.copy() - interval = 1440 - if df_ta2.index[-2].date() == df_ta2.index[-1].date(): - interval = (df_ta2.index[1] - df_ta2.index[0]).seconds / 60 - dt_unique_days = df_ta2.index.normalize().unique() - - if interval not in [15, 30, 60] and len(dt_unique_days) <= 3: - df_ta2 = df_ta2.loc[ - (df_ta2.index >= dt_unique_days[-1]) - & (df_ta2.index < datetime.now()) - ].copy() - df_ta2 = df_ta2.between_time("09:30", "20:00").copy() - - for i in range(7): - idx_int = 4 if lvl_text == "left" else 5 - text_pos = f"bottom {lvl_text}" if i != idx_int else f"top {lvl_text}" - - if fibs[i] == "0": - text_pos = ( - f"top {lvl_text}" if lvl_text != "right" else f"bottom {lvl_text}" - ) - text = ["", f"{fibs[i]} ({levels[i]:{self.get_float_precision()}})"] - if lvl_text == "right": - text = [text[1], text[0]] - - fig.add_scatter( - name=fibs[i], - x=[min_date, df_ta2.index.max()], - y=[levels[i], levels[i]], - opacity=0.9, - mode="lines+text", - text=text, - textposition=text_pos, - textfont=dict(PLT_FIB_COLORWAY[7], color=PLT_FIB_COLORWAY[i]), - line_color=PLT_FIB_COLORWAY[i], - line_width=1.5, - showlegend=False, - row=1, - col=1, - secondary_y=False, - ) - - return fig diff --git a/openbb_terminal/core/plots/plotly_ta/plugins/momentum_plugin.py b/openbb_terminal/core/plots/plotly_ta/plugins/momentum_plugin.py deleted file mode 100644 index 327a2aaaa4d..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/plugins/momentum_plugin.py +++ /dev/null @@ -1,609 +0,0 @@ -import numpy as np -import pandas as pd -import pandas_ta as ta - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.common.technical_analysis.momentum_model import clenow_momentum -from openbb_terminal.core.plots.plotly_ta.base import PltTA, indicator -from openbb_terminal.core.plots.plotly_ta.data_classes import columns_regex - - -class Momentum(PltTA): - """Momentum technical indicators""" - - __subplots__ = ["rsi", "macd", "stoch", "cci", "fisher", "cg"] - __inchart__ = ["clenow", "demark", "ichimoku"] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @indicator() - def plot_cci(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds cci to plotly figure""" - cci_col = columns_regex(df_ta, "CCI")[0] - dmax = df_ta[cci_col].max() - dmin = df_ta[cci_col].min() - fig.add_scatter( - name="CCI", - mode="lines", - line=dict(width=1.5, color="#e0b700"), - x=df_ta.index, - y=df_ta[cci_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_hrect( - y0=100, - y1=dmax, - fillcolor=theme.down_color, - opacity=0.2, - layer="below", - line_width=0, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hrect( - y0=-100, - y1=dmin, - fillcolor=theme.up_color, - opacity=0.2, - layer="below", - line_width=0, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hline( - y=100, - opacity=1, - layer="below", - line=dict(width=2, color=theme.down_color, dash="dash"), - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hline( - y=-100, - opacity=1, - layer="below", - line=dict(width=2, color=theme.up_color, dash="dash"), - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="CCI", - x=0, - xanchor="right", - xshift=-6, - y=0.97, - font_size=14, - font_color="#e0b700", - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(nticks=5, autorange=True) - - return fig, subplot_row + 1 - - @indicator() - def plot_cg(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds cg to plotly figure""" - cg_col = columns_regex(df_ta, "CG")[0] - fig.add_scatter( - name="CG", - mode="lines", - line=dict(width=1.5, color="#ffed00"), - x=df_ta.index, - y=df_ta[cg_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="Signal", - mode="lines", - line=dict(width=1.5, color="#ef7d00"), - x=df_ta.index, - y=df_ta[cg_col].shift(1), - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="CG", - x=0, - xanchor="right", - xshift=-10, - y=0.97, - font_size=14, - font_color="#ffed00", - ) - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="Signal", - x=0, - xanchor="right", - xshift=-6, - y=0.97, - yshift=-20, - font_size=14, - font_color="#ef7d00", - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(nticks=5, autorange=True) - - return fig, subplot_row + 1 - - @indicator() - def plot_clenow(self, fig: OpenBBFigure, df_ta: pd.DataFrame, inchart_index: int): - """Adds current close price to plotly figure""" - window = self.params["clenow"].get_argument_values("window") or 90 - _, _, fit_data = clenow_momentum(df_ta[self.close_column], window=window) - - fig.add_scatter( - x=df_ta.index[-window:], # type: ignore - y=pow(np.e, fit_data), - name="CLenow", - mode="lines", - line=dict(color=self.inchart_colors[inchart_index], width=2), - row=1, - col=1, - secondary_y=False, - ) - fig.add_annotation( - xref="paper", - yref="paper", - text="CLenow", - x=0, - xanchor="right", - xshift=-6, - yshift=-inchart_index * 18, - y=0.98, - font_size=14, - font_color=self.inchart_colors[inchart_index], - opacity=1, - ) - return fig, inchart_index + 1 - - @indicator() - def plot_demark(self, fig: OpenBBFigure, df_ta: pd.DataFrame, inchart_index: int): - """Adds demark to plotly figure""" - min_val = self.params["demark"].get_argument_values("min_val") or 5 - - demark = ta.td_seq(df_ta[self.close_column], asint=True) - demark.set_index(df_ta.index, inplace=True) - - demark["up"] = demark.TD_SEQ_UPa.apply( - lambda x: f"{x}" if x >= min_val else None - ) - demark["down"] = demark.TD_SEQ_DNa.apply( - lambda x: f"{x}" if x >= min_val else None - ) - - # we only keep the values that are not None in up/down columns - high = df_ta["High"][demark["up"].notnull()] - low = df_ta["Low"][demark["down"].notnull()] - demark = demark[demark["up"].notnull() | demark["down"].notnull()] - - fig.add_scatter( - x=low.index, - y=low, - name="Demark Down", - mode="text", - text=demark["down"], - textposition="bottom center", - textfont=dict(color=theme.down_color, size=14.5), - row=1, - col=1, - secondary_y=False, - ) - fig.add_scatter( - x=high.index, - y=high, - name="Demark Up", - mode="text", - text=demark["up"], - textposition="top center", - textfont=dict(color=theme.up_color, size=14.5), - row=1, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref="paper", - yref="paper", - text="Demark", - x=0, - xanchor="right", - xshift=-6, - yshift=-inchart_index * 18, - y=0.98, - font_size=14, - font_color=self.inchart_colors[inchart_index], - opacity=1, - ) - - return fig, inchart_index + 1 - - @indicator() - def plot_fisher(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds fisher to plotly figure""" - fishert_col = columns_regex(df_ta, "FISHERT")[0] - fishers_col = columns_regex(df_ta, "FISHERTs")[0] - fig.add_scatter( - name="Fisher", - mode="lines", - line=dict(width=1.5, color="#e0b700"), - x=df_ta.index, - y=df_ta[fishert_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="Fisher Signal", - mode="lines", - line=dict(width=1.5, color="#9467bd"), - x=df_ta.index, - y=df_ta[fishers_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - dmax = df_ta[fishers_col].max() - dmin = df_ta[fishers_col].min() - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="FISHER", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color="#e0b700", - ) - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="SIGNAL", - x=0, - xanchor="right", - xshift=-6, - y=0.01, - font_size=13, - font_color="#9467bd", - ) - fig.add_hrect( - y0=2, - y1=(dmax + 4), - fillcolor=theme.down_color, - opacity=0.2, - layer="below", - line_width=0, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hrect( - y0=-2, - y1=(dmin - 4), - fillcolor=theme.up_color, - opacity=0.2, - layer="below", - line_width=0, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hline( - y=2, - fillcolor=theme.down_color, - opacity=1, - layer="below", - line=dict(width=2, color=theme.down_color, dash="dash"), - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hline( - y=-2, - fillcolor=theme.up_color, - opacity=1, - layer="below", - line=dict(width=2, color=theme.up_color, dash="dash"), - row=subplot_row, - col=1, - secondary_y=False, - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(nticks=5, autorange=True) - - return fig, subplot_row + 1 - - @indicator() - def plot_macd(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds macd to plotly figure""" - - fig.add_bar( - name="MACD Histogram", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "MACDh")[0]].values, - opacity=0.9, - marker_color="#1a97ea", - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="MACD", - mode="lines", - line=dict(width=1.5, color="#9467bd"), - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "MACD")[0]].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="MACD Signal", - mode="lines", - line=dict(width=1.5, color="rgb(7, 166, 52)"), - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "MACDs")[0]].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="MACD", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color="#9467bd", - ) - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="SIGNAL", - x=0, - xanchor="right", - y=0.70, - font_size=13, - xshift=-3, - font_color="rgb(7, 166, 52)", - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(autorange=True, nticks=5) - - return fig, subplot_row + 1 - - @indicator() - def plot_rsi(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds rsi to plotly figure""" - rsi_col = columns_regex(df_ta, "RSI")[0] - fig.add_scatter( - name="RSI", - mode="lines", - line=dict(width=1.5, color="rgb(0, 122, 204, 1)"), - x=df_ta.index, - y=df_ta[rsi_col].values, - opacity=1, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="RSI", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color="rgb(0, 122, 204, 1)", - ) - fig.add_hrect( - y0=70, - y1=100, - fillcolor=theme.down_color, - opacity=0.2, - layer="below", - line_width=0, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hrect( - y0=0, - y1=30, - fillcolor=theme.up_color, - opacity=0.2, - layer="below", - line_width=0, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hline( - y=70, - fillcolor=theme.down_color, - opacity=1, - layer="below", - line=dict(width=2, color=theme.down_color, dash="dash"), - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_hline( - y=30, - fillcolor=theme.up_color, - opacity=1, - layer="below", - line=dict(width=2, color=theme.up_color, dash="dash"), - row=subplot_row, - col=1, - secondary_y=False, - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(tickvals=[0, 30, 70, 100]) - - return fig, subplot_row + 1 - - @indicator() - def plot_stoch(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds stoch to plotly figure""" - fig.add_scatter( - name="STOCH %K", - mode="lines", - line=dict(width=1.5, color="#e0b700"), - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "STOCHk")[0]].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="STOCH %D", - mode="lines", - line=dict(width=1.5, color="#9467bd", dash="dash"), - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "STOCHd")[0]].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="STOCH", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color="#e0b700", - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(nticks=5, autorange=True) - - return fig, subplot_row + 1 - - @indicator() - def plot_ichimoku(self, fig: OpenBBFigure, df_ta: pd.DataFrame, inchart_index: int): - # Calculate Ichimoku indicator - conversion_period = ( - self.params["ichimoku"].get_argument_values("conversion_period") or 9 - ) - base_period = self.params["ichimoku"].get_argument_values("base_period") or 26 - lagging_line_period = ( - self.params["ichimoku"].get_argument_values("lagging_line_period") or 52 - ) - displacement = self.params["ichimoku"].get_argument_values("displacement") or 26 - - # Tenkan-sen (Conversion Line) - conversion_line = ( - df_ta["High"].rolling(window=conversion_period).max() - + df_ta["Low"].rolling(window=conversion_period).min() - ) / 2 - - # Kijun-sen (Base Line) - base_line = ( - df_ta["High"].rolling(window=base_period).max() - + df_ta["Low"].rolling(window=base_period).min() - ) / 2 - - # Senkou Span A (Leading Span A) - leading_span_a = ((conversion_line + base_line) / 2).shift(displacement) - - # Senkou Span B (Leading Span B) - lagging_line = df_ta[self.close_column].shift(-lagging_line_period) # type: ignore - leading_span_b = ( - ( - lagging_line.rolling(window=base_period).max() - + lagging_line.rolling(window=base_period).min() - ) - / 2 - ).shift(displacement) - - # Plot Tenkan-sen and Kijun-sen - fig.add_scatter( - x=df_ta.index, - y=conversion_line, - line=dict(color="orange", width=1), - name="Tenkan-sen", - secondary_y=False, - showlegend=True, - opacity=1, - ) - fig.add_scatter( - x=df_ta.index, - y=base_line, - line=dict(color="blue", width=1), - name="Kijun-sen", - secondary_y=False, - showlegend=True, - opacity=1, - ) - - # Plot Senkou Span A and Senkou Span B as a filled area - fig.add_scatter( - x=df_ta.index, - y=leading_span_a, - line=dict(color="#009600", width=1), - fill="tonexty", - fillcolor="rgba(0, 150, 0, 0.1)", - name="Senkou Span A", - secondary_y=False, - showlegend=False, - opacity=0.2, - ) - fig.add_scatter( - x=df_ta.index, - y=leading_span_b, - line=dict(color="#c80000", width=1), - fill="tonexty", - fillcolor="rgba(200, 0, 0, 0.1)", - name="Senkou Span B", - showlegend=False, - secondary_y=False, - opacity=0.2, - ) - - fig.add_annotation( - xref="paper", - yref="paper", - text="Ichi" - "moku", - x=0, - xanchor="right", - xshift=-6, - yshift=-inchart_index * 18, - y=0.98, - font_size=14, - opacity=1, - ) - - return fig, inchart_index + 1 diff --git a/openbb_terminal/core/plots/plotly_ta/plugins/overlap_plugin.py b/openbb_terminal/core/plots/plotly_ta/plugins/overlap_plugin.py deleted file mode 100644 index 85ea403ec6d..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/plugins/overlap_plugin.py +++ /dev/null @@ -1,94 +0,0 @@ -import logging - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.core.plots.plotly_ta.base import PltTA, indicator -from openbb_terminal.core.plots.plotly_ta.data_classes import columns_regex - -logger = logging.getLogger(__name__) - - -class Overlap(PltTA): - """Overlap technical indicators""" - - __inchart__ = ["vwap"] - __ma_mode__ = ["sma", "ema", "wma", "hma", "zlma", "rma"] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @indicator() - def plot_ma(self, fig: OpenBBFigure, df_ta: pd.DataFrame, inchart_index: int): - """Adds moving average to plotly figure""" - check_ma = [ma for ma in self.ma_mode if ma in self.indicators.get_active_ids()] - if check_ma: - for ma in check_ma: - column_names = columns_regex(df_ta, ma.upper()) - try: - for column in column_names: - if df_ta[column].empty: - continue - - fig.add_scatter( - name=column.replace("RMA", "MA"), - mode="lines", - line=dict( - width=1.2, color=self.inchart_colors[inchart_index] - ), - x=df_ta.index, - y=df_ta[column].values, - opacity=0.9, - connectgaps=True, - row=1, - col=1, - secondary_y=False, - ) - fig.add_annotation( - xref="paper", - yref="paper", - text=f"{column.replace('_', '').replace('RMA', 'MA')}", - x=0, - xanchor="right", - xshift=-6, - yshift=-inchart_index * 18, - y=0.98, - font_size=14, - font_color=self.inchart_colors[inchart_index], - opacity=1, - ) - inchart_index += 1 - - except Exception as e: - logger.exception("Error adding %s to plot - %s", ma.upper(), e) - - return fig, inchart_index - - @indicator() - def plot_vwap(self, fig: OpenBBFigure, df_ta: pd.DataFrame, inchart_index: int): - """Adds vwap to plotly figure""" - fig.add_scatter( - name=columns_regex(df_ta, "VWAP_")[0], - mode="lines", - line=dict(width=1.5, color=self.inchart_colors[inchart_index]), - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "VWAP_")[0]].values, - opacity=0.8, - row=1, - col=1, - secondary_y=False, - ) - fig.add_annotation( - xref="paper", - yref="paper", - text="VWAP", - x=0, - xanchor="right", - xshift=-6, - yshift=-inchart_index * 18, - y=0.98, - font_size=14, - font_color=self.inchart_colors[inchart_index], - ) - - return fig, inchart_index + 1 diff --git a/openbb_terminal/core/plots/plotly_ta/plugins/trend_indicators_plugin.py b/openbb_terminal/core/plots/plotly_ta/plugins/trend_indicators_plugin.py deleted file mode 100644 index d8022d10bac..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/plugins/trend_indicators_plugin.py +++ /dev/null @@ -1,193 +0,0 @@ -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.core.plots.plotly_ta.base import PltTA, indicator -from openbb_terminal.core.plots.plotly_ta.data_classes import columns_regex - - -class Trend(PltTA): - """Trend technical indicators""" - - __subplots__ = ["adx", "aroon"] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @indicator() - def plot_adx(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds adx to plotly figure""" - fig.add_scatter( - name="ADX", - mode="lines", - line=dict(width=1.5, color="#e0b700"), - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "ADX")[0]].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="+DI", - mode="lines", - line=dict(width=1.5, color=theme.up_color), - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "DMP")[0]].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="-DI", - mode="lines", - line=dict(width=1.5, color=theme.down_color), - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "DMN")[0]].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="ADX", - x=0, - xanchor="right", - xshift=-6, - y=0.97, - font_size=14, - font_color="#e0b700", - ) - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text=( - f"D+
" - f"D-" - ), - x=0, - xanchor="right", - xshift=-14, - y=0.97, - yshift=-20, - font_size=14, - font_color=theme.up_color, - ) - fig.add_hline( - y=25, - fillcolor="white", - opacity=1, - layer="below", - line_width=1.5, - line=dict(color="white", dash="dash"), - row=subplot_row, - col=1, - secondary_y=False, - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(nticks=5, autorange=True) - - return fig, subplot_row + 1 - - @indicator() - def plot_aroon(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds aroon to plotly figure""" - aroon_up_col = columns_regex(df_ta, "AROONU")[0] - aroon_down_col = columns_regex(df_ta, "AROOND")[0] - aroon_osc_col = columns_regex(df_ta, "AROONOSC")[0] - fig.add_scatter( - name="Aroon Up", - mode="lines", - line=dict(width=1.5, color=theme.up_color), - x=df_ta.index, - y=df_ta[aroon_up_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="Aroon Down", - mode="lines", - line=dict(width=1.5, color=theme.down_color), - x=df_ta.index, - y=df_ta[aroon_down_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="Aroon", - x=0, - xanchor="right", - xshift=-6, - y=1, - font_size=14, - font_color="#e0b700", - ) - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text=( - f"↑
" - f"↓" - ), - x=0, - xanchor="right", - xshift=-14, - y=0.75, - font_size=14, - font_color=theme.down_color, - ) - fig.add_hline( - y=50, - fillcolor="white", - opacity=1, - layer="below", - line_width=1.5, - line=dict(color="white", dash="dash"), - row=subplot_row, - col=1, - secondary_y=False, - ) - - subplot_row += 1 - - fig.add_scatter( - name="Aroon Oscillator", - mode="lines", - line=dict(width=1.5, color="#e0b700"), - x=df_ta.index, - y=df_ta[aroon_osc_col].values, - connectgaps=True, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="Aroon
OSC
", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color="#e0b700", - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update( - tickvals=[-100, 0, 100], - ticktext=["-100", "0", "100"], - nticks=5, - autorange=True, - ) - - return fig, subplot_row + 1 diff --git a/openbb_terminal/core/plots/plotly_ta/plugins/volatility_plugin.py b/openbb_terminal/core/plots/plotly_ta/plugins/volatility_plugin.py deleted file mode 100644 index a9a8b58c792..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/plugins/volatility_plugin.py +++ /dev/null @@ -1,222 +0,0 @@ -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.core.plots.plotly_ta.base import PltTA, indicator -from openbb_terminal.core.plots.plotly_ta.data_classes import columns_regex - - -class Volatility(PltTA): - """Volatility technical indicators""" - - __inchart__ = ["bbands", "donchian", "kc"] - __subplots__ = ["atr"] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @indicator() - def plot_atr(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds average true range to plotly figure""" - - fig.add_scatter( - name=f"{columns_regex(df_ta, 'ATR')[0]}", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "ATR")[0]].values, - mode="lines", - line=dict(width=1, color=theme.get_colors()[1]), - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="ATR", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color=theme.get_colors()[1], - ) - fig["layout"][f"yaxis{subplot_row}"].update(nticks=5, autorange=True) - - return fig, subplot_row + 1 - - @indicator() - def plot_bbands(self, fig: OpenBBFigure, df_ta: pd.DataFrame, inchart_index: int): - """Adds bollinger bands to plotly figure""" - - bbands_opacity = 0.8 if theme.plt_style == "light" else 1 - - fig.add_scatter( - name=f"{columns_regex(df_ta, 'BBU')[0]}", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "BBU")[0]].values, - opacity=bbands_opacity, - mode="lines", - line=dict(width=1, color=theme.up_color), - row=1, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name=f"{columns_regex(df_ta, 'BBL')[0]}", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "BBL")[0]].values, - opacity=bbands_opacity, - mode="lines", - line=dict(width=1, color=theme.down_color), - row=1, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name=f"{columns_regex(df_ta, 'BBM')[0]}", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "BBM")[0]].values, - opacity=1, - mode="lines", - line=dict(width=1, color=theme.get_colors()[1], dash="dash"), - row=1, - col=1, - secondary_y=False, - ) - bbands_text = ( - columns_regex(df_ta, "BBL")[0].replace("BBL_", "BB").replace("_", ",") - ) - if float(bbands_text.split(",")[1]) % 1 == 0: - bbands_text = bbands_text.split(".")[0] - fig.add_annotation( - xref="paper", - yref="paper", - text=f"{bbands_text}", - x=0, - xanchor="right", - xshift=-6, - yshift=-inchart_index * 18, - y=0.98, - font_size=14, - font_color=theme.get_colors()[1], - opacity=0.9, - ) - - return fig, inchart_index + 1 - - @indicator() - def plot_donchian(self, fig: OpenBBFigure, df_ta: pd.DataFrame, inchart_index: int): - """Adds donchian channels to plotly figure""" - - if theme.plt_style == "light": - fillcolor = "rgba(239, 103, 137, 0.05)" - donchian_opacity = 0.4 - else: - fillcolor = "rgba(239, 103, 137, 0.05)" - donchian_opacity = 0.4 - - fig.add_scatter( - name=f"{columns_regex(df_ta, 'DCU')[0]}", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "DCU")[0]].values, - opacity=donchian_opacity, - mode="lines", - line=dict(width=0.3, color="#EF6689"), - row=1, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name=f"{columns_regex(df_ta, 'DCL')[0]}", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "DCL")[0]].values, - opacity=donchian_opacity, - mode="lines", - line=dict(width=0.3, color="#EF6689"), - fill="tonexty", - fillcolor=fillcolor, - row=1, - col=1, - secondary_y=False, - ) - - donchian_text = ( - columns_regex(df_ta, "DCL")[0] - .replace("DCL_", "DC") - .replace("_", ",") - .split(".")[0] - ) - - fig.add_annotation( - xref="paper", - yref="paper", - text=f"{donchian_text}", - x=0, - xanchor="right", - xshift=-6, - yshift=-inchart_index * 18, - y=0.98, - font_size=14, - font_color="#B47DA0", - opacity=0.9, - ) - - return fig, inchart_index + 1 - - @indicator() - def plot_kc(self, fig: OpenBBFigure, df_ta: pd.DataFrame, inchart_index: int): - """Adds Keltner channels to plotly figure""" - mamode = (self.params["kc"].get_argument_values("mamode") or "ema").lower() # type: ignore - - if theme.plt_style == "light": - fillcolor = "rgba(239, 103, 137, 0.05)" - kc_opacity = 0.4 - else: - fillcolor = "rgba(239, 103, 137, 0.05)" - kc_opacity = 0.4 - - fig.add_scatter( - name=f"{columns_regex(df_ta, 'KCU')[0]}", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "KCU")[0]].values, - opacity=kc_opacity, - mode="lines", - line=dict(width=0.3, color="#EF6689"), - row=1, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name=f"{columns_regex(df_ta, 'KCL')[0]}", - x=df_ta.index, - y=df_ta[columns_regex(df_ta, "KCL")[0]].values, - opacity=kc_opacity, - mode="lines", - line=dict(width=0.3, color="#EF6689"), - fill="tonexty", - fillcolor=fillcolor, - row=1, - col=1, - secondary_y=False, - ) - kctext = ( - columns_regex(df_ta, "KCL")[0] - .replace(f"KCL{mamode[0]}_", "KC") - .replace("_", ",") - .split(".")[0] - ) - fig.add_annotation( - xref="paper", - yref="paper", - text=f"{kctext}", - x=0, - xanchor="right", - xshift=-6, - yshift=-inchart_index * 18, - y=0.98, - font_size=14, - font_color="#B47DA0", - opacity=0.9, - ) - - return fig, inchart_index + 1 diff --git a/openbb_terminal/core/plots/plotly_ta/plugins/volume_plugin.py b/openbb_terminal/core/plots/plotly_ta/plugins/volume_plugin.py deleted file mode 100644 index 7e4e395d22f..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/plugins/volume_plugin.py +++ /dev/null @@ -1,117 +0,0 @@ -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.core.plots.plotly_ta.base import PltTA, indicator -from openbb_terminal.core.plots.plotly_ta.data_classes import columns_regex - - -class Volume(PltTA): - """Volume technical indicators""" - - __subplots__ = ["ad", "adosc", "obv"] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @indicator() - def plot_ad(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds ad to plotly figure""" - ad_col = columns_regex(df_ta, "AD")[0] - fig.add_scatter( - name="AD", - mode="lines", - line=dict(width=1.5, color=theme.get_colors()[1]), - x=df_ta.index, - y=df_ta[ad_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_hline( - y=0, - fillcolor="white", - opacity=1, - layer="below", - line=dict(color="white", dash="dash", width=2), - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="AD", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color=theme.get_colors()[1], - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(nticks=3, autorange=True) - - return fig, subplot_row + 1 - - @indicator() - def plot_adosc(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds adosc to plotly figure""" - ad_col = columns_regex(df_ta, "ADOSC")[0] - fig.add_scatter( - name="ADOSC", - mode="lines", - line=dict(width=1.5, color=theme.get_colors()[1]), - x=df_ta.index, - y=df_ta[ad_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="ADOSC", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color=theme.get_colors()[1], - ) - - return fig, subplot_row + 1 - - @indicator() - def plot_obv(self, fig: OpenBBFigure, df_ta: pd.DataFrame, subplot_row: int): - """Adds obv to plotly figure""" - obv_col = columns_regex(df_ta, "OBV")[0] - fig.add_scatter( - name="OBV", - mode="lines", - line=dict(width=1.5, color=theme.get_colors()[1]), - x=df_ta.index, - y=df_ta[obv_col].values, - opacity=0.9, - row=subplot_row, - col=1, - secondary_y=False, - ) - - fig.add_annotation( - xref=f"x{subplot_row} domain", - yref=f"y{subplot_row + 1} domain", - text="OBV", - x=0, - xanchor="right", - xshift=-6, - y=0.98, - font_size=14, - font_color=theme.get_colors()[1], - ) - fig["layout"][f"yaxis{subplot_row + 1}"].update(nticks=5, autorange=True) - - return fig, subplot_row + 1 diff --git a/openbb_terminal/core/plots/plotly_ta/ta_class.py b/openbb_terminal/core/plots/plotly_ta/ta_class.py deleted file mode 100644 index 0456cec5fa4..00000000000 --- a/openbb_terminal/core/plots/plotly_ta/ta_class.py +++ /dev/null @@ -1,576 +0,0 @@ -# pylint: disable=R0902 -import importlib -import inspect -import sys -from pathlib import Path -from typing import Any, Dict, List, Optional, Type, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure, config_terminal, theme -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY -from openbb_terminal.core.plots.plotly_ta.base import PltTA -from openbb_terminal.core.plots.plotly_ta.data_classes import ChartIndicators -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.rich_config import console - -PLUGINS_PATH = Path(__file__).parent / "plugins" -PLOTLY_TA: Optional["PlotlyTA"] = None - - -class PlotlyTA(PltTA): - """Plotly Technical Analysis class - - This class is a singleton. It is created and then reused, to assure - the plugins are only loaded once. This is done by overriding the __new__ - method. The __init__ method is overridden to do nothing, except to clear - the internal data structures. - - Attributes - ---------- - inchart_colors (List[str]): - List of colors for inchart indicators - show_volume (bool): - Whether to show the volume subplot - ma_mode (List[str]): - List of available moving average modes - inchart (List[str]): - List of available inchart indicators - subplots (List[str]): - List of available subplots - - StaticMethods - ------------- - plot( - df: pd.DataFrame, - indicators: ChartIndicators, - fig: Optional[OpenBBFigure] = None, - symbol: Optional[str] = "", - candles: bool = True, - volume: bool = True, - ) -> OpenBBFigure: - Plots the chart with the given indicators - - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA - - >>> df = openbb.stocks.load("SPY") - >>> indicators = dict( - >>> sma=dict(length=[20, 50, 100]), - >>> adx=dict(length=14), - >>> macd=dict(fast=12, slow=26, signal=9), - >>> rsi=dict(length=14), - >>> ) - >>> fig = PlotlyTA.plot(df, indicators=indicators) - >>> fig.show() - - If you want to plot the chart with the same indicators, you can - reuse the same instance of the class as follows: - - >>> ta = PlotlyTA() - >>> fig = ta.plot(df, indicators=indicators) - >>> df2 = openbb.stocks.load("AAPL") - >>> fig2 = ta.plot(df2) - >>> fig.show() - >>> fig2.show() - """ - - inchart_colors = theme.get_colors() - plugins: List[Type[PltTA]] = [] - df_ta: pd.DataFrame = None - close_column: Optional[str] = "Close" - has_volume: bool = True - show_volume: bool = True - - def __new__(cls, *args, **kwargs): - """This method is overridden to create a singleton instance of the class.""" - global PLOTLY_TA # pylint: disable=global-statement # noqa - if PLOTLY_TA is None: - # Creates the instance of the class and loads the plugins - # We set the global variable to the instance of the class so that - # the plugins are only loaded once - PLOTLY_TA = super().__new__(cls) - PLOTLY_TA._locate_plugins() - PLOTLY_TA.add_plugins(PLOTLY_TA.plugins) - - cls.inchart_colors = theme.get_colors() - return PLOTLY_TA - - def __init__(self, *args, **kwargs): # pylint: disable=unused-argument - """This method is overridden to do nothing, except to clear the internal data structures.""" - if not args and not kwargs: - self._clear_data() - else: - self.df_fib = None - super().__init__(*args, **kwargs) - - @property - def ma_mode(self) -> List[str]: - return list(set(self.__ma_mode__)) - - @ma_mode.setter - def ma_mode(self, value: List[str]): - self.__ma_mode__ = value - - @property - def inchart(self) -> List[str]: - return list(set(self.__inchart__)) - - @inchart.setter - def inchart(self, value: List[str]): - self.__inchart__ = value - - @property - def subplots(self) -> List[str]: - return list(set(self.__subplots__)) - - @subplots.setter - def subplots(self, value: List[str]): - self.__subplots__ = value - - # pylint: disable=R0913 - def __plot__( - self, - df_stock: Union[pd.DataFrame, pd.Series], - indicators: Optional[Union[ChartIndicators, Dict[str, Dict[str, Any]]]] = None, - symbol: str = "", - candles: bool = True, - volume: bool = True, - fig: Optional[OpenBBFigure] = None, - volume_ticks_x: int = 7, - ) -> OpenBBFigure: - """This method should not be called directly. Use the PlotlyTA.plot() static method instead.""" - - if config_terminal.HOLD: - console.print( - "The previous command is not supported within hold on. Only the last command run" - "will be displayed when hold off is run." - ) - - if isinstance(df_stock, pd.Series): - df_stock = df_stock.to_frame() - - if not isinstance(indicators, ChartIndicators): - indicators = ChartIndicators.from_dict(indicators or {}) - - self.indicators = indicators - self.intraday = df_stock.index[-2].time() != df_stock.index[-1].time() - self.df_stock = df_stock - self.close_column = ta_helpers.check_columns(self.df_stock) - self.params = self.indicators.get_params() - - self.has_volume = "Volume" in self.df_stock.columns and bool( - self.df_stock["Volume"].sum() > 0 - ) - self.show_volume = volume and self.has_volume - - return self.plot_fig( - fig=fig, symbol=symbol, candles=candles, volume_ticks_x=volume_ticks_x - ) - - @staticmethod - def plot( - df_stock: Union[pd.DataFrame, pd.Series], - indicators: Optional[Union[ChartIndicators, Dict[str, Dict[str, Any]]]] = None, - symbol: str = "", - candles: bool = True, - volume: bool = True, - fig: Optional[OpenBBFigure] = None, - volume_ticks_x: int = 7, - ) -> OpenBBFigure: - """Plot a chart with the given indicators. - - Parameters - ---------- - df_stock : pd.DataFrame - Dataframe with stock data - indicators : Union[ChartIndicators, Dict[str, Dict[str, Any]]] - ChartIndicators object or dictionary with indicators and parameters to plot - Example: - dict( - sma=dict(length=[20, 50, 100]), - adx=dict(length=14), - macd=dict(fast=12, slow=26, signal=9), - rsi=dict(length=14), - ) - symbol : str, optional - Symbol to plot, by default uses the dataframe.name attribute if available or "" - candles : bool, optional - Plot a candlestick chart, by default True (if False, plots a line chart) - volume : bool, optional - Plot volume, by default True - fig : OpenBBFigure, optional - Plotly figure to plot on, by default None - volume_ticks_x : int, optional - Number to multiply volume, by default 7 - """ - if indicators is None and PLOTLY_TA is not None: - indicators = PLOTLY_TA.indicators - - return PlotlyTA().__plot__( - df_stock, indicators, symbol, candles, volume, fig, volume_ticks_x - ) - - @staticmethod - def _locate_plugins() -> None: - """Locate all the plugins in the plugins folder""" - path = ( - Path(sys.executable).parent - if hasattr(sys, "frozen") - else REPOSITORY_DIRECTORY - ) - current_system = get_current_system() - - # This is for debugging purposes - if current_system.DEBUG_MODE: - console.print(f"[bold green]Loading plugins from {path}[/]") - console.print("[bold green]Plugins found:[/]") - - for plugin in Path(__file__).parent.glob("plugins/*_plugin.py"): - python_path = plugin.relative_to(path).with_suffix("") - - # This is for debugging purposes - if current_system.DEBUG_MODE: - console.print(f" [bold red]{plugin.name}[/]") - console.print(f" [bold yellow]{python_path}[/]") - console.print(f" [bold bright_cyan]{__package__}[/]") - console.print(f" [bold magenta]{python_path.parts}[/]") - console.print( - f" [bold bright_magenta]{'.'.join(python_path.parts)}[/]" - ) - - module = importlib.import_module( - ".".join(python_path.parts), package=__package__ - ) - for _, obj in inspect.getmembers(module): - if ( - inspect.isclass(obj) - and issubclass(obj, (PltTA)) - and obj != PlotlyTA.__class__ - ) and obj not in PlotlyTA.plugins: - PlotlyTA.plugins.append(obj) - - def _clear_data(self): - """Clear and reset all data to default values""" - self.df_stock = None - self.indicators = {} - self.params = None - self.intraday = False - self.show_volume = True - - def calculate_indicators(self): - """Return dataframe with all indicators""" - return self.indicators.to_dataframe(self.df_stock, self.ma_mode) - - def get_subplot(self, subplot: str) -> bool: - """Return True if subplots will be able to be plotted with current data""" - if subplot == "volume": - return self.show_volume - - if subplot in ["ad", "adosc", "obv", "vwap"] and not self.has_volume: - self.indicators.remove_indicator(subplot) - console.print( - f"[bold red]Warning:[/] [yellow]{subplot.upper()}" - " requires volume data to be plotted but no volume data was found." - " Indicator will not be plotted.[/]" - ) - return False - - output = False - - try: - indicator = self.indicators.get_indicator(subplot) - if indicator is None: - return False - - output = self.indicators.get_indicator_data( - self.df_stock.copy(), - indicator, - **self.indicators.get_options_dict(indicator.name) or {}, - ) - if not isinstance(output, bool): - output.dropna(inplace=True) - - if output is None or output.empty: - output = False - - return True - - except Exception: - output = False - - return output - - def check_subplots(self, subplots: list) -> list: - """Return list of subplots that can be plotted with current data""" - output = [] - for subplot in subplots: - if self.get_subplot(subplot): - output.append(subplot) - - return output - - def get_fig_settings_dict(self): - """Return dictionary with settings for plotly figure""" - row_params = { - "0": dict(rows=1, row_width=[1]), - "1": dict(rows=2, row_width=[0.3, 0.7]), - "2": dict(rows=3, row_width=[0.15, 0.15, 0.7]), - "3": dict(rows=4, row_width=[0.2, 0.2, 0.2, 0.4]), - "4": dict(rows=5, row_width=[0.15, 0.15, 0.15, 0.15, 0.4]), - } - - check_active = self.indicators.get_active_ids() - subplots = [subplot for subplot in self.subplots if subplot in check_active] - - check_rows = min(len(self.check_subplots(subplots)), 4) - check_rows += 1 if "aroon" in subplots and (check_rows + 1) < 5 else 0 - - specs = [[{"secondary_y": True}]] + [[{"secondary_y": False}]] * check_rows - - output = row_params.get(str(check_rows), dict(rows=1, row_width=[1])) - output.update(dict(cols=1, vertical_spacing=0.04, specs=specs)) - - return output - - def init_plot(self, symbol: str = "", candles: bool = True) -> OpenBBFigure: - """Create plotly figure with subplots - - Parameters - ---------- - symbol : str, optional - Symbol to plot, by default uses the dataframe.name attribute if available or "" - candles : bool, optional - Plot a candlestick chart, by default True (if False, plots a line chart) - - Returns - ------- - fig : OpenBBFigure - Plotly figure with candlestick/line chart and volume bar chart (if enabled) - """ - fig = OpenBBFigure.create_subplots( - 1, - 1, - shared_xaxes=True, - vertical_spacing=0.06, - horizontal_spacing=0.01, - row_width=[1], - specs=[[{"secondary_y": True}]], - ) - cc_linewidth = ( - 0.8 if len(self.df_stock.index) > 500 else 0.9 if self.intraday else 1.1 - ) - if candles: - fig.add_candlestick( - x=self.df_stock.index, - open=self.df_stock.Open, - high=self.df_stock.High, - low=self.df_stock.Low, - close=self.df_stock.Close, - decreasing=dict(line=dict(width=cc_linewidth)), - increasing=dict(line=dict(width=cc_linewidth)), - name=f"{symbol} OHLC", - showlegend=False, - row=1, - col=1, - secondary_y=False, - ) - else: - fig.add_scatter( - x=self.df_stock.index, - y=self.df_stock[self.close_column], - name=f"{symbol} Close", - connectgaps=True, - row=1, - col=1, - secondary_y=False, - ) - fig.update_layout(yaxis=dict(nticks=15)) - self.inchart_colors = theme.get_colors()[1:] - - fig.set_title(symbol, x=0.5, y=0.98, xanchor="center", yanchor="top") - return fig - - def plot_fig( - self, - fig: Optional[OpenBBFigure] = None, - symbol: str = "", - candles: bool = True, - volume_ticks_x: int = 7, - ) -> OpenBBFigure: - """Plot indicators on plotly figure - - Parameters - ---------- - fig : OpenBBFigure, optional - Plotly figure to plot indicators on, by default None - symbol : str, optional - Symbol to plot, by default uses the dataframe.name attribute if available or "" - candles : bool, optional - Plot a candlestick chart, by default True (if False, plots a line chart) - volume_ticks_x : int, optional - Number to multiply volume, by default 7 - - Returns - ------- - fig : OpenBBFigure - Plotly figure with candlestick/line chart and volume bar chart (if enabled) - """ - - self.df_ta = self.calculate_indicators() - - symbol = ( - self.df_stock.name - if hasattr(self.df_stock, "name") and not symbol - else symbol - ) - - figure = self.init_plot(symbol, candles) if fig is None else fig - - subplot_row, fig_new = 2, {} - inchart_index, ma_done = 0, False - - figure = self.process_fig(figure, volume_ticks_x) - - # Aroon indicator is always plotted first since it has 2 subplot rows - plot_indicators = sorted( - self.indicators.get_active_ids(), - key=lambda x: 50 if x == "aroon" else 999 if x in self.subplots else 1, - ) - - for indicator in plot_indicators: - try: - if indicator in self.subplots: - figure, subplot_row = getattr(self, f"plot_{indicator}")( - figure, self.df_ta, subplot_row - ) - elif indicator in self.ma_mode or indicator in self.inchart: - if indicator in self.ma_mode: - if ma_done: - continue - indicator, ma_done = "ma", True # noqa - - figure, inchart_index = getattr(self, f"plot_{indicator}")( - figure, self.df_ta, inchart_index - ) - elif indicator in ["fib", "srlines", "demark", "clenow", "ichimoku"]: - figure = getattr(self, f"plot_{indicator}")(figure, self.df_ta) - else: - raise ValueError(f"Unknown indicator: {indicator}") - - fig_new.update(figure.to_plotly_json()) - - remaining_subplots = ( - list( - set(plot_indicators[plot_indicators.index(indicator) + 1 :]) - - set(self.inchart) - ) - if indicator != "ma" - else [] - ) - if subplot_row > 5 and remaining_subplots: - console.print( - f"[bold red]Reached max number of subplots. " - f"Skipping {', '.join(remaining_subplots)}[/]" - ) - break - except Exception as e: - console.print(f"[bold red]Error plotting {indicator}: {e}[/]") - continue - - figure.update(fig_new) - figure.update_yaxes( - row=1, - col=1, - secondary_y=False, - nticks=15 if subplot_row < 3 else 10, - tickfont=dict(size=16), - ) - figure.update_traces( - selector=dict(type="scatter", mode="lines"), connectgaps=True - ) - figure.update_layout(showlegend=False) - - if not self.show_volume: - figure.update_layout(margin=dict(l=20)) - - # We remove xaxis labels from all but bottom subplot, and we make sure - # they all match the bottom one - xbottom = f"y{subplot_row+1}" - xaxes = list(figure.select_xaxes()) - for xa in xaxes: - if xa == xaxes[-1]: - xa.showticklabels = True - if not xa.showticklabels and xa.anchor != xbottom: - xa.showticklabels = False - if xa.anchor != xbottom: - xa.matches = xbottom.replace("y", "x") - - return figure - - def process_fig(self, fig: OpenBBFigure, volume_ticks_x: int = 7) -> OpenBBFigure: - """Process plotly figure before plotting indicators - - Parameters - ---------- - fig : OpenBBFigure - Plotly figure to process - volume_ticks_x : int, optional - Number to multiply volume, by default 7 - - Returns - ------- - fig : OpenBBFigure - Processed plotly figure - """ - - new_subplot = OpenBBFigure.create_subplots( - shared_xaxes=True, **self.get_fig_settings_dict() - ) - - subplots: Dict[str, Dict[str, List[Any]]] = {} - grid_ref = fig._validate_get_grid_ref() # pylint: disable=protected-access - for r, plot_row in enumerate(grid_ref): - for c, plot_refs in enumerate(plot_row): - if not plot_refs: - continue - for subplot_ref in plot_refs: - if subplot_ref.subplot_type == "xy": - xaxis, yaxis = subplot_ref.layout_keys - xref = xaxis.replace("axis", "") - yref = yaxis.replace("axis", "") - row = r + 1 - col = c + 1 - subplots.setdefault(xref, {}).setdefault(yref, []).append( - (row, col) - ) - - for trace in fig.select_traces(): - xref, yref = trace.xaxis, trace.yaxis - row, col = subplots[xref][yref][0] - new_subplot.add_trace(trace, row=row, col=col, secondary_y=False) - - fig_json = fig.to_plotly_json()["layout"] - for layout in fig_json: - if ( - isinstance(fig_json[layout], dict) - and "domain" in fig_json[layout] - and any(x in layout for x in ["xaxis", "yaxis"]) - ): - fig_json[layout]["domain"] = new_subplot.to_plotly_json()["layout"][ - layout - ]["domain"] - - fig.layout.update({layout: fig_json[layout]}) - new_subplot.layout.update({layout: fig.layout[layout]}) - - if self.show_volume: - new_subplot.add_inchart_volume( - self.df_stock, self.close_column, volume_ticks_x=volume_ticks_x - ) - - return new_subplot diff --git a/openbb_terminal/core/plots/table.html b/openbb_terminal/core/plots/table.html deleted file mode 100644 index 93a59444d99..00000000000 --- a/openbb_terminal/core/plots/table.html +++ /dev/null @@ -1,307 +0,0 @@ - - - - - - OpenBB Interactive Tables - - - - - -
- - - diff --git a/openbb_terminal/core/scripts/__init__.py b/openbb_terminal/core/scripts/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/scripts/sdk_audit.py b/openbb_terminal/core/scripts/sdk_audit.py deleted file mode 100644 index b67f427a487..00000000000 --- a/openbb_terminal/core/scripts/sdk_audit.py +++ /dev/null @@ -1,233 +0,0 @@ -import importlib.util -import os -from datetime import datetime -from inspect import getmembers, isfunction -from pathlib import Path -from typing import Callable, List, Tuple - -import pandas as pd - -from openbb_terminal.core.config.paths import MAP_PATH -from openbb_terminal.rich_config import console - -try: - import darts # pylint: disable=W0611 # noqa: F401 - - # If you just import darts this will pass during pip install, this creates - # Failures later on, also importing utils ensures that darts is installed correctly - from darts import utils # pylint: disable=W0611 # noqa: F401 - - FORECASTING = True -except ImportError: - FORECASTING = False - -base_path = Path(__file__).parent.parent.parent.parent / "openbb_terminal" - - -def load_modules(full_path: Path): - """Loads a module from a given Path. - - Parameter: - ---------- - full_path: Path - The path to the module - - Returns: - ---------- - The python module - """ - mod_path = str(full_path).split("OpenBBTerminal" + os.sep)[1].replace(os.sep, ".") - spec = importlib.util.spec_from_file_location(mod_path, full_path) - if not spec: - raise ValueError(f"Could not import path: {full_path}") - mod = importlib.util.module_from_spec(spec) - if not mod: - raise ValueError(f"Could not import path: {full_path}") - if not spec.loader: - raise ValueError(f"Could not import path: {full_path}") - spec.loader.exec_module(mod) - return mod - - -def all_view_models() -> List[Path]: - """Geta all files with 'view' or 'model' in the name. - - Returns: - ---------- - List[Path] - All paths in openbb_terminal with 'view' or 'model' in the name - """ - - file_list = [] - all_files = os.walk(base_path) - for root, _, files in all_files: - for filename in files: - if filename.endswith(".py") and ("view" in filename or "model" in filename): - file_list.append(f"{root}/{filename}") - clean_list = set(file_list) - return [Path(x) for x in clean_list] - - -def get_sdk(file_path: Path = MAP_PATH) -> pd.DataFrame: - """Reads the CSV that generates the sdk and converts it to a dataframe - - Parameters - ---------- - file_path: str - The path to the sdk csv - - Returns: - ---------- - pd.DataFrame - A dataframe with columns for sdk endpoint functiom and trail. Trails will be duplicated - because there is a model and view function - """ - df = pd.read_csv(base_path / file_path) - df_dups = len(df["trail"]) - len(df["trail"].drop_duplicates()) - if df_dups > 0: - console.print(f"Number of duplicate sdk paths: {df_dups}") - console.print( - "This indicates that the same SDK trail is being used multiple times\n" - ) - views = list(df[["view", "trail"]].itertuples(index=False, name=None)) - models = list(df[["model", "trail"]].itertuples(index=False, name=None)) - # Add in whether it is a view or a model in pandas - combined = [x + ("view",) for x in views] + [x + ("model",) for x in models] - final_df = pd.DataFrame() - final_df["name"] = [x[0] for x in combined] - final_df["trail"] = [x[1] for x in combined] - final_df["type"] = [x[2] for x in combined] - final_df = final_df.dropna() - final_df = final_df.set_index("name") - return final_df - - -def format_function(function: Callable) -> Tuple[str, str]: - """Gives a function a pretty name - - Parameters - ---------- - function: Callable - The function to get a pretty string for - - Returns: - ---------- - Tuple[str, str] - The functions pretty name and docstring - """ - mod = str(function.__module__)[:-2].replace("/", ".") - name = function.__name__ - if mod[-1] != ".": - mod = f"{mod}." - return f"{mod}{name}", str(function.__doc__) - - -def functions_df() -> pd.DataFrame: - """Creates a dataframe for all functions in 'models' and 'views'. - - Returns: - ---------- - pd.DataFrame - Information for all view and model functions - """ - modules = all_view_models() - all_formatted = [] - for module in modules: - if not FORECASTING and "forecast" in str(module): - continue - loaded = load_modules(module) - # Gets all of a module's functions, but ignores imported functions - func_list = [ - x[1] - for x in getmembers(loaded, isfunction) - if x[1].__module__ == loaded.__name__ - ] - formatted_list = [format_function(x) for x in func_list] - all_formatted.extend(formatted_list) - func_df = pd.DataFrame() - func_df["name"] = [x[0] for x in all_formatted] - func_df["docstring"] = [x[1] for x in all_formatted] - func_dups = len(func_df["name"]) - len(func_df["name"].drop_duplicates()) - if func_dups > 0: - console.print(f"Number of duplicate functions found: {func_dups}") - console.print( - "This may indicate that functions are defined several times in the terminal.\n" - ) - func_df = func_df.set_index("name") - return func_df - - -def save_df(data: pd.DataFrame) -> None: - timestamp = datetime.now().timestamp() - time_str = (str(timestamp)).replace(".", "") - output_path = f"{time_str}_sdk_audit.csv" - data.to_csv(output_path) - console.print(f"File saved to {output_path}") - - -def get_nonconforming_functions(data: pd.DataFrame) -> pd.DataFrame: - """When we first check all functions we only look in model and view files. This means that any - function in the helpers will be ignored. This function checks any helper funcs in the sdk - treemap, so that we can get a clearer picture of whether or not functions exist - - Parameters - ---------- - data: pd.DataFrame - The dataframe of all values, this will be searched - - Returns - ---------- - pd.DataFrame - The dataframe with any functions that could not be found - """ - df = data[data["docstring"].isnull()] - names = df.index.tolist() - for name in names: - path_list = name.split(".") - file = "/".join(path_list[:-1]) - mod_path = Path(__file__).parent.parent.parent.parent / f"{file}.py" - module = load_modules(mod_path) - command = path_list[-1] - function = getattr(module, command) - if function: - data.loc[name, "docstring"] = function.__doc__ - return data - - -def main(): - console.print( - "This tool checks all functions in a file with a name including 'view' or 'model'against\n" - "all functions in the sdk, which is gathered from 'trail_map.csv'. If the generated csv\n" - "has an entry for 'trail' that means it is in the SDK, and if it has an entry for\n" - "'docstring' it is in a model or view.\n" - ) - funcs_df = functions_df() - sdk_df = get_sdk() - - final_df = funcs_df.merge(sdk_df, how="outer", left_index=True, right_index=True) - final_df = final_df.sort_values("name") - final_df = get_nonconforming_functions(final_df) - # Do this so that the duplicated method also checks the name column - final_df = final_df.reset_index() - # Get further stats on bad data - no_doc_count = len(final_df[final_df["docstring"].isnull()].index) - if no_doc_count > 0: - console.print(f"The number of rows with blank docstrings is: {no_doc_count}") - console.print( - "This indicates a matching function does not exist, is not in a 'model' or 'view'\n" - "file, or that the trailmap does not import it from the place it is defined.\n" - ) - dup_name_count = len(final_df[final_df.duplicated(keep=False)].index) - if dup_name_count > 0: - console.print( - f"The number of duplicate functions after merge is: {dup_name_count}" - ) - console.print( - "This most likely indicates that the same function is being used at " - "different SDK endpoints.\n" - ) - save_df(final_df) - - -if __name__ == "__main__": - main() diff --git a/openbb_terminal/core/sdk/__init__.py b/openbb_terminal/core/sdk/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/sdk/controllers/__init__.py b/openbb_terminal/core/sdk/controllers/__init__.py deleted file mode 100644 index fb33aa31e92..00000000000 --- a/openbb_terminal/core/sdk/controllers/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=unused-import,wrong-import-order - -from .alt_sdk_controller import AltController -from .crypto_sdk_controller import CryptoController -from .etf_sdk_controller import EtfController -from .portfolio_sdk_controller import PortfolioController -from .stocks_sdk_controller import StocksController diff --git a/openbb_terminal/core/sdk/controllers/alt_sdk_controller.py b/openbb_terminal/core/sdk/controllers/alt_sdk_controller.py deleted file mode 100644 index 0f8e192c8da..00000000000 --- a/openbb_terminal/core/sdk/controllers/alt_sdk_controller.py +++ /dev/null @@ -1,67 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.models import alt_sdk_model as model - - -class AltController(model.AltRoot): - """Alternative Module. - - Submodules: - `companieshouse`: Companieshouse Module - `oss`: Oss Module - `realestate`: Realestate Module - - Attributes: - `hn`: Get top stories from HackerNews.\n - `hn_chart`: View top stories from HackerNews.\n - """ - - @property - def companieshouse(self): - """Alternative Companieshouse Submodule - - Attributes: - `download_filing_document`: Download company's filing document.\n - `get_company_info`: Gets company info by company number\n - `get_filing_document`: Download given filing document pdf\n - `get_filings`: Gets information on filings for given company, e.g. accounts, etc\n - `get_officers`: Gets information on company officers\n - `get_persons_with_significant_control`: Gets information on persons with significant control over the company\n - `get_search_results`: All companies with searchStr in their name.\n - """ - - return model.AltCompanieshouse() - - @property - def oss(self): - """Alternative Oss Submodule - - Attributes: - `_make_request`: Helper method to scrap.\n - `_retry_session`: Helper methods that retries to make request.\n - `github_data`: Get repository stats.\n - `history`: Get repository star history.\n - `history_chart`: Plots repo summary [Source: https://api.github.com].\n - `ross`: Get startups from ROSS index [Source: https://runacap.com/].\n - `ross_chart`: Plots list of startups from ross index [Source: https://runacap.com/]\n - `search`: Get repos sorted by stars or forks. Can be filtered by categories.\n - `summary`: Get repository summary.\n - `summary_chart`: Prints table showing repo summary [Source: https://api.github.com].\n - `top`: Get repos sorted by stars or forks. Can be filtered by categories.\n - `top_chart`: Plots repo summary [Source: https://api.github.com].\n - """ - - return model.AltOss() - - @property - def realestate(self): - """Alternative Realestate Submodule - - Attributes: - `get_estate_sales`: All sales for specified postcode.\n - `get_region_stats`: Get regional house price statistics.\n - `get_towns_sold_prices`: Get towns sold house price data.\n - """ - - return model.AltRealestate() diff --git a/openbb_terminal/core/sdk/controllers/crypto_sdk_controller.py b/openbb_terminal/core/sdk/controllers/crypto_sdk_controller.py deleted file mode 100644 index 7785096ff24..00000000000 --- a/openbb_terminal/core/sdk/controllers/crypto_sdk_controller.py +++ /dev/null @@ -1,299 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.models import crypto_sdk_model as model - - -class CryptoController(model.CryptoRoot): - """Cryptocurrency Module. - - Submodules: - `dd`: Due Diligence Module - `defi`: DeFi Module - `disc`: Discovery Module - `nft`: NFT Module - `onchain`: OnChain Module - `ov`: Overview Module - `tools`: Tools Module - - Attributes: - `candle`: Plot candle chart from dataframe. [Source: Binance]\n - `chart`: Load data for Technical Analysis\n - `find`: Find similar coin by coin name,symbol or id.\n - `load`: Load crypto currency to get data for\n - `price`: Displays live price from pyth live feed [Source: https://pyth.network/]\n - """ - - @property - def dd(self): - """Cryptocurrency Due Diligence Submodule - - Attributes: - `all_binance_trading_pairs`: Returns all available pairs on Binance in DataFrame format. DataFrame has 3 columns symbol, baseAsset, quoteAsset\n - `ath`: Get all time high for a coin in a given currency\n - `atl`: Get all time low for a coin in a given currency\n - `balance`: Get account holdings for asset. [Source: Binance]\n - `balance_chart`: Prints table showing account holdings for asset. [Source: Binance]\n - `basic`: Basic coin information [Source: CoinPaprika]\n - `basic_chart`: Prints table showing basic information for coin. Like:\n - `binance_available_quotes_for_each_coin`: Helper methods that for every coin available on Binance add all quote assets. [Source: Binance]\n - `candle`: Get candles for chosen trading pair and time interval. [Source: Coinbase]\n - `check_valid_binance_str`: Check if symbol is in defined binance. [Source: Binance]\n - `coin`: Get coin by id [Source: CoinPaprika]\n - `coin_market_chart`: Get prices for given coin. [Source: CoinGecko]\n - `dev`: Get developer stats for a coin\n - `events`: Get all events related to given coin like conferences, start date of futures trading etc.\n - `events_chart`: Prints table showing all events for given coin id. [Source: CoinPaprika]\n - `ex`: Get all exchanges for given coin id. [Source: CoinPaprika]\n - `ex_chart`: Prints table showing all exchanges for given coin id. [Source: CoinPaprika]\n - `exchanges`: Helper method to get all the exchanges supported by ccxt\n - `fr`: Returns coin fundraising\n - `fr_chart`: Display coin fundraising\n - `get_mt`: Returns available messari timeseries\n - `get_mt_chart`: Prints table showing messari timeseries list\n - `gov`: Returns coin governance\n - `gov_chart`: Prints table showing coin governance\n - `headlines`: Gets Sentiment analysis provided by FinBrain's API [Source: finbrain].\n - `headlines_chart`: Sentiment analysis from FinBrain for Cryptocurrencies\n - `inv`: Returns coin investors\n - `inv_chart`: Prints table showing coin investors\n - `links`: Returns asset's links\n - `links_chart`: Prints table showing coin links\n - `mcapdom`: Returns market dominance of a coin over time\n - `mcapdom_chart`: Plots market dominance of a coin over time\n - `mkt`: All markets for given coin and currency [Source: CoinPaprika]\n - `mkt_chart`: Prints table showing all markets for given coin id. [Source: CoinPaprika]\n - `mt`: Returns messari timeseries\n - `mt_chart`: Plots messari timeseries\n - `news`: Get recent posts from CryptoPanic news aggregator platform. [Source: https://cryptopanic.com/]\n - `news_chart`: Prints table showing recent posts from CryptoPanic news aggregator platform.\n - `ob`: Returns orderbook for a coin in a given exchange\n - `ob_chart`: Plots order book for a coin in a given exchange\n - `pi`: Returns coin product info\n - `pi_chart`: Prints table showing project info\n - `pr`: Fetch data to calculate potential returns of a certain coin. [Source: CoinGecko]\n - `pr_chart`: Prints table showing potential returns of a certain coin. [Source: CoinGecko]\n - `ps`: Get all most important ticker related information for given coin id [Source: CoinPaprika]\n - `ps_chart`: Prints table showing ticker information for single coin [Source: CoinPaprika]\n - `rm`: Returns coin roadmap\n - `rm_chart`: Plots coin roadmap\n - `score`: Get scores for a coin from CoinGecko\n - `show_available_pairs_for_given_symbol`: Return all available quoted assets for given symbol. [Source: Coinbase]\n - `social`: Get social media stats for a coin\n - `stats`: Get 24 hr stats for the product. Volume is in base currency units.\n - `stats_chart`: Prints table showing 24 hr stats for the product. Volume is in base currency units.\n - `team`: Returns coin team\n - `team_chart`: Prints table showing coin team\n - `tk`: Returns coin tokenomics\n - `tk_chart`: Plots coin tokenomics\n - `tokenomics`: Get tokenomics for given coin. [Source: CoinGecko]\n - `trades`: Returns trades for a coin in a given exchange\n - `trades_chart`: Prints table showing trades for a coin in a given exchange\n - `trading_pair_info`: Get information about chosen trading pair. [Source: Coinbase]\n - `trading_pairs`: Helper method that return all trading pairs on binance. Methods ause this data for input for e.g\n - `twitter`: Get twitter timeline for given coin id. Not more than last 50 tweets [Source: CoinPaprika]\n - `twitter_chart`: Prints table showing twitter timeline for given coin id. Not more than last 50 tweets [Source: CoinPaprika]\n - """ - - return model.CryptoDueDiligence() - - @property - def defi(self): - """Cryptocurrency DeFi Submodule - - Attributes: - `dtvl`: Returns information about historical tvl of a defi protocol.\n - `dtvl_chart`: Plots historical TVL of different dApps\n - `gdapps`: Display top dApps (in terms of TVL) grouped by chain.\n - `gdapps_chart`: Plots top dApps (in terms of TVL) grouped by chain.\n - `ldapps`: Returns information about listed DeFi protocols, their current TVL and changes to it in the last hour/day/week.\n - `ldapps_chart`: Prints table showing information about listed DeFi protocols, their current TVL and changes to it in\n - `luna_supply`: Get supply history of the Terra ecosystem\n - `luna_supply_chart`: Plots and prints table showing Luna circulating supply stats\n - `newsletters`: Scrape all substack newsletters from url list.\n - `newsletters_chart`: Prints table showing DeFi related substack newsletters.\n - `stvl`: Returns historical values of the total sum of TVLs from all listed protocols.\n - `stvl_chart`: Plots historical values of the total sum of TVLs from all listed protocols.\n - `vaults`: Get DeFi Vaults Information. DeFi Vaults are pools of funds with an assigned strategy which main goal is to\n - `vaults_chart`: Prints table showing Top DeFi Vaults - pools of funds with an assigned strategy which main goal is to\n - """ - - return model.CryptoDeFi() - - @property - def disc(self): - """Cryptocurrency Discovery Submodule - - Attributes: - `categories_keys`: Get list of categories keys\n - `coin_list`: Get list of coins available on CoinGecko [Source: CoinGecko]\n - `coins`: Get N coins from CoinGecko [Source: CoinGecko]\n - `coins_chart`: Prints table showing top coins [Source: CoinGecko]\n - `coins_for_given_exchange`: Helper method to get all coins available on binance exchange [Source: CoinGecko]\n - `cpsearch`: Search CoinPaprika. [Source: CoinPaprika]\n - `cpsearch_chart`: Prints table showing Search over CoinPaprika. [Source: CoinPaprika]\n - `dapp_categories`: Get dapp categories [Source: https://dappradar.com/]\n - `dapp_categories_chart`: Prints table showing dapp categories [Source: https://dappradar.com/]\n - `dapp_chains`: Get dapp chains [Source: https://dappradar.com/]\n - `dapp_chains_chart`: Prints table showing dapp chains [Source: https://dappradar.com/]\n - `dapp_metrics`: Get dapp metrics [Source: https://dappradar.com/]\n - `dapp_metrics_chart`: Prints table showing dapp metrics [Source: https://dappradar.com/]\n - `dapps`: Get dapps [Source: https://dappradar.com/]\n - `dapps_chart`: Prints table showing dapps [Source: https://dappradar.com/]\n - `defi_chains`: Get defi chains [Source: https://dappradar.com/]\n - `defi_chains_chart`: Prints table showing defi chains [Source: https://dappradar.com/]\n - `fees`: Show cryptos with most fees. [Source: CryptoStats]\n - `fees_chart`: Display crypto with most fees paid [Source: CryptoStats]\n - `gainers`: Shows Largest Gainers - coins which gain the most in given period. [Source: CoinGecko]\n - `gainers_chart`: Prints table showing Largest Gainers - coins which gain the most in given period. [Source: CoinGecko]\n - `losers`: Shows Largest Losers - coins which lose the most in given period. [Source: CoinGecko]\n - `losers_chart`: Prints table showing Largest Losers - coins which lost the most in given period of time. [Source: CoinGecko]\n - `nft_mktp`: Get top nft collections [Source: https://dappradar.com/]\n - `nft_mktp_chart`: Prints table showing nft marketplaces [Source: https://dappradar.com/]\n - `nft_mktp_chains`: Get nft marketplaces chains [Source: https://dappradar.com/]\n - `nft_mktp_chains_chart`: Prints table showing nft marketplaces chains [Source: https://dappradar.com/]\n - `tokens`: Get chains that support tokens [Source: https://dappradar.com/]\n - `tokens_chart`: Prints table showing chains that support tokens [Source: https://dappradar.com/]\n - `top_coins`: Get top cryptp coins.\n - `trending`: Returns trending coins [Source: CoinGecko]\n - `trending_chart`: Prints table showing trending coins [Source: CoinGecko]\n - """ - - return model.CryptoDiscovery() - - @property - def nft(self): - """Cryptocurrency NFT Submodule - - Attributes: - `collections`: Get nft collections [Source: https://nftpricefloor.com/]\n - `collections_chart`: Display NFT collections. [Source: https://nftpricefloor.com/]\n - `fp`: Get nft collections [Source: https://nftpricefloor.com/]\n - `fp_chart`: Display NFT collection floor price over time. [Source: https://nftpricefloor.com/]\n - `stats`: Get stats of a nft collection [Source: opensea.io]\n - `stats_chart`: Prints table showing collection stats. [Source: opensea.io]\n - """ - - return model.CryptoNFT() - - @property - def onchain(self): - """Cryptocurrency OnChain Submodule - - Attributes: - `baas`: Get an average bid and ask prices, average spread for given crypto pair for chosen time period.\n - `baas_chart`: Prints table showing an average bid and ask prices, average spread for given crypto pair for chosen\n - `balance`: Get info about tokens on you ethereum blockchain balance. Eth balance, balance of all tokens which\n - `balance_chart`: Display info about tokens for given ethereum blockchain balance e.g. ETH balance,\n - `btc_supply`: Returns BTC circulating supply [Source: https://api.blockchain.info/]\n - `btc_supply_chart`: Returns BTC circulating supply [Source: https://api.blockchain.info/]\n - `btc_transac`: Returns BTC confirmed transactions [Source: https://api.blockchain.info/]\n - `btc_transac_chart`: Returns BTC confirmed transactions [Source: https://api.blockchain.info/]\n - `btcsingleblock`: Returns BTC block data in json format. [Source: https://blockchain.info/]\n - `btcsingleblock_chart`: Returns BTC block data. [Source: https://api.blockchain.info/]\n - `dex_trades_monthly`: Get list of trades on Decentralized Exchanges monthly aggregated.\n - `dvcp`: Get daily volume for given pair [Source: https://graphql.bitquery.io/]\n - `dvcp_chart`: Prints table showing daily volume for given pair\n - `erc20_tokens`: Helper method that loads ~1500 most traded erc20 token.\n - `hist`: Get information about balance historical transactions. [Source: Ethplorer]\n - `hist_chart`: Display information about balance historical transactions. [Source: Ethplorer]\n - `holders`: Get info about top token holders. [Source: Ethplorer]\n - `holders_chart`: Display info about top ERC20 token holders. [Source: Ethplorer]\n - `info`: Get info about ERC20 token. [Source: Ethplorer]\n - `info_chart`: Display info about ERC20 token. [Source: Ethplorer]\n - `lt`: Get trades on Decentralized Exchanges aggregated by DEX [Source: https://graphql.bitquery.io/]\n - `lt_chart`: Prints table showing Trades on Decentralized Exchanges aggregated by DEX or Month\n - `prices`: Get token historical prices with volume and market cap, and average price. [Source: Ethplorer]\n - `prices_chart`: Display token historical prices with volume and market cap, and average price.\n - `query_graph`: Helper methods for querying graphql api. [Source: https://bitquery.io/]\n - `th`: Get info about token historical transactions. [Source: Ethplorer]\n - `th_chart`: Display info about token history. [Source: Ethplorer]\n - `token_decimals`: Helper methods that gets token decimals number. [Source: Ethplorer]\n - `top`: Get top 50 tokens. [Source: Ethplorer]\n - `top_chart`: Display top ERC20 tokens [Source: Ethplorer]\n - `topledger`: Returns Topledger's Data for the given Organization's Slug[org_slug] based\n - `topledger_chart`: Display on-chain data from Topledger. [Source: Topledger]\n - `ttcp`: Get most traded crypto pairs on given decentralized exchange in chosen time period.\n - `ttcp_chart`: Prints table showing most traded crypto pairs on given decentralized exchange in chosen time period.\n - `tv`: Get token volume on different Decentralized Exchanges. [Source: https://graphql.bitquery.io/]\n - `tv_chart`: Prints table showing token volume on different Decentralized Exchanges.\n - `tx`: Get info about transaction. [Source: Ethplorer]\n - `tx_chart`: Display info about transaction. [Source: Ethplorer]\n - `ueat`: Get number of unique ethereum addresses which made a transaction in given time interval.\n - `ueat_chart`: Prints table showing number of unique ethereum addresses which made a transaction in given time interval\n - `whales`: Whale Alert's API allows you to retrieve live and historical transaction data from major blockchains.\n - `whales_chart`: Display huge value transactions from major blockchains. [Source: https://docs.whale-alert.io/]\n - """ - - return model.CryptoOnChain() - - @property - def ov(self): - """Cryptocurrency Overview Submodule - - Attributes: - `altindex`: Get altcoin index overtime\n - `altindex_chart`: Displays altcoin index overtime\n - `categories`: Returns top crypto categories [Source: CoinGecko]\n - `categories_chart`: Shows top cryptocurrency categories by market capitalization\n - `cbpairs`: Get a list of available currency pairs for trading. [Source: Coinbase]\n - `cbpairs_chart`: Displays a list of available currency pairs for trading. [Source: Coinbase]\n - `coin_list`: Get list of all available coins on CoinPaprika [Source: CoinPaprika]\n - `contracts`: Gets all contract addresses for given platform [Source: CoinPaprika]\n - `contracts_chart`: Gets all contract addresses for given platform. [Source: CoinPaprika]\n - `cr`: Returns crypto {borrow,supply} interest rates for cryptocurrencies across several platforms\n - `cr_chart`: Displays crypto {borrow,supply} interest rates for cryptocurrencies across several platforms\n - `crypto_hack`: Get crypto hack\n - `crypto_hack_slugs`: Get all crypto hack slugs\n - `crypto_hacks`: Get major crypto-related hacks\n - `crypto_hacks_chart`: Display list of major crypto-related hacks. If slug is passed\n - `defi`: Get global statistics about Decentralized Finances [Source: CoinGecko]\n - `defi_chart`: Shows global statistics about Decentralized Finances. [Source: CoinGecko]\n - `derivatives`: Get list of crypto derivatives from CoinGecko API [Source: CoinGecko]\n - `derivatives_chart`: Shows list of crypto derivatives. [Source: CoinGecko]\n - `ewf`: Scrapes exchange withdrawal fees\n - `ewf_chart`: Exchange withdrawal fees\n - `exchanges`: Show top crypto exchanges.\n - `exmarkets`: List markets by exchange ID [Source: CoinPaprika]\n - `exmarkets_chart`: Get all markets for given exchange [Source: CoinPaprika]\n - `exrates`: Get list of crypto, fiats, commodity exchange rates from CoinGecko API [Source: CoinGecko]\n - `exrates_chart`: Shows list of crypto, fiats, commodity exchange rates. [Source: CoinGecko]\n - `globe`: Get global crypto market data.\n - `hm`: Get N coins from CoinGecko [Source: CoinGecko]\n - `hm_chart`: Shows cryptocurrencies heatmap [Source: CoinGecko]\n - `hold`: Returns public companies that holds ethereum or bitcoin [Source: CoinGecko]\n - `hold_chart`: Shows overview of public companies that holds ethereum or bitcoin. [Source: CoinGecko]\n - `indexes`: Get list of crypto indexes from CoinGecko API [Source: CoinGecko]\n - `indexes_chart`: Shows list of crypto indexes. [Source: CoinGecko]\n - `info`: Returns basic coin information for all coins from CoinPaprika API [Source: CoinPaprika]\n - `info_chart`: Displays basic coin information for all coins from CoinPaprika API. [Source: CoinPaprika]\n - `markets`: Returns basic coin information for all coins from CoinPaprika API [Source: CoinPaprika]\n - `markets_chart`: Displays basic market information for all coins from CoinPaprika API. [Source: CoinPaprika]\n - `news`: Get recent posts from CryptoPanic news aggregator platform. [Source: https://cryptopanic.com/]\n - `news_chart`: Display recent posts from CryptoPanic news aggregator platform.\n - `platforms`: List all smart contract platforms like ethereum, solana, cosmos, polkadot, kusama ... [Source: CoinPaprika]\n - `platforms_chart`: List all smart contract platforms like ethereum, solana, cosmos, polkadot, kusama.\n - `products`: Get list of financial products from CoinGecko API\n - `products_chart`: Shows list of financial products. [Source: CoinGecko]\n - `stables`: Returns top stable coins [Source: CoinGecko]\n - `stables_chart`: Shows stablecoins data [Source: CoinGecko]\n - `wf`: Scrapes top coins withdrawal fees\n - `wf_chart`: Top coins withdrawal fees\n - `wfpe`: Scrapes coin withdrawal fees per exchange\n - `wfpe_chart`: Coin withdrawal fees per exchange\n - """ - - return model.CryptoOverview() - - @property - def tools(self): - """Cryptocurrency Tools Submodule - - Attributes: - `apy`: Converts apr into apy\n - `apy_chart`: Displays APY value converted from APR\n - `il`: Calculates Impermanent Loss in a custom liquidity pool\n - `il_chart`: Displays Impermanent Loss in a custom liquidity pool\n - """ - - return model.CryptoTools() diff --git a/openbb_terminal/core/sdk/controllers/etf_sdk_controller.py b/openbb_terminal/core/sdk/controllers/etf_sdk_controller.py deleted file mode 100644 index 771450e1ba5..00000000000 --- a/openbb_terminal/core/sdk/controllers/etf_sdk_controller.py +++ /dev/null @@ -1,37 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.models import etf_sdk_model as model - - -class EtfController(model.EtfRoot): - """Etf Module. - - Submodules: - `disc`: Discovery Module - - Attributes: - `candle`: Show candle plot of loaded ticker.\n - `compare`: Compare selected ETFs\n - `etf_by_category`: Return a selection of ETFs based on category filtered by total assets.\n - `etf_by_name`: Get an ETF symbol and name based on ETF string to search. [Source: StockAnalysis]\n - `holdings`: Get ETF holdings\n - `ld`: Return a selection of ETFs based on description filtered by total assets.\n - `ln`: Return a selection of ETFs based on name filtered by total assets. [Source: Finance Database]\n - `load`: Load a symbol to perform analysis using the string above as a template.\n - `news`: Get news for a given term. [Source: NewsAPI]\n - `news_chart`: Prints table showing news for a given term. [Source: NewsAPI]\n - `overview`: Get overview data for selected etf\n - `symbols`: Gets all etf names and symbols\n - `weights`: Return sector weightings allocation of ETF. [Source: FinancialModelingPrep]\n - """ - - @property - def disc(self): - """Etf Discovery Submodule - - Attributes: - `mover`: Scrape data for top etf movers.\n - """ - - return model.EtfDiscovery() diff --git a/openbb_terminal/core/sdk/controllers/portfolio_sdk_controller.py b/openbb_terminal/core/sdk/controllers/portfolio_sdk_controller.py deleted file mode 100644 index 596d210306b..00000000000 --- a/openbb_terminal/core/sdk/controllers/portfolio_sdk_controller.py +++ /dev/null @@ -1,115 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.models import portfolio_sdk_model as model - - -class PortfolioController(model.PortfolioRoot): - """Portfolio Module. - - Submodules: - `alloc`: Alloc Module - `metric`: Metric Module - `po`: Portfolio Optimization Module - - Attributes: - `bench`: Load benchmark into portfolio\n - `distr`: Display daily returns\n - `distr_chart`: Display daily returns\n - `dret`: Get daily returns\n - `dret_chart`: Display daily returns\n - `es`: Get portfolio expected shortfall\n - `holdp`: Get holdings of assets (in percentage)\n - `holdp_chart`: Display holdings of assets (in percentage)\n - `holdv`: Get holdings of assets (absolute value)\n - `holdv_chart`: Display holdings of assets (absolute value)\n - `load`: Get PortfolioEngine object\n - `maxdd`: Calculate the drawdown (MDD) of historical series. Note that the calculation is done\n - `maxdd_chart`: Display maximum drawdown curve\n - `mret`: Get monthly returns\n - `mret_chart`: Display monthly returns\n - `om`: Get omega ratio\n - `om_chart`: Display omega ratio\n - `perf`: Get portfolio performance vs the benchmark\n - `rbeta`: Get rolling beta using portfolio and benchmark returns\n - `rbeta_chart`: Display rolling beta\n - `rsharpe`: Get rolling sharpe ratio\n - `rsharpe_chart`: Display rolling sharpe\n - `rsort`: Get rolling sortino\n - `rsort_chart`: Display rolling sortino\n - `rvol`: Get rolling volatility\n - `rvol_chart`: Display rolling volatility\n - `show`: Get portfolio transactions\n - `summary`: Get portfolio and benchmark returns summary\n - `var`: Get portfolio VaR\n - `yret`: Get yearly returns\n - `yret_chart`: Display yearly returns\n - """ - - @property - def alloc(self): - """Portfolio Alloc Submodule - - Attributes: - `assets`: Display portfolio asset allocation compared to the benchmark\n - `countries`: Display portfolio country allocation compared to the benchmark\n - `regions`: Display portfolio region allocation compared to the benchmark\n - `sectors`: Display portfolio sector allocation compared to the benchmark\n - """ - - return model.PortfolioAlloc() - - @property - def metric(self): - """Portfolio Metric Submodule - - Attributes: - `calmar`: Get calmar ratio\n - `commonsense`: Get common sense ratio\n - `gaintopain`: Get Pain-to-Gain ratio based on historical data\n - `information`: Get information ratio\n - `jensens`: Get jensen's alpha\n - `kelly`: Get kelly criterion\n - `kurtosis`: Get kurtosis for portfolio and benchmark selected\n - `maxdrawdown`: Get maximum drawdown ratio for portfolio and benchmark selected\n - `payoff`: Get payoff ratio\n - `profitfactor`: Get profit factor\n - `rsquare`: Get R2 Score for portfolio and benchmark selected\n - `sharpe`: Get sharpe ratio for portfolio and benchmark selected\n - `skew`: Get skewness for portfolio and benchmark selected\n - `sortino`: Get sortino ratio for portfolio and benchmark selected\n - `tail`: Get tail ratio\n - `trackerr`: Get tracking error\n - `volatility`: Get volatility for portfolio and benchmark selected\n - """ - - return model.PortfolioMetric() - - @property - def po(self): - """Portfolio Portfolio Optimization Submodule - - Attributes: - `blacklitterman`: Optimize decorrelation weights\n - `ef`: Get Efficient Frontier\n - `ef_chart`: Display efficient frontier\n - `file`: Load portfolio optimization engine from file\n - `herc`: Optimize with Hierarchical Equal Risk Contribution (HERC) method.\n - `hrp`: Optimize with Hierarchical Risk Parity\n - `load`: Load portfolio optimization engine\n - `load_bl_views`: Load a Excel file with views for Black Litterman model.\n - `maxdecorr`: Optimize decorrelation weights\n - `maxdiv`: Optimize diversification weights\n - `maxret`: Optimize maximum return weights\n - `maxsharpe`: Optimize Sharpe ratio weights\n - `maxutil`: Optimize maximum utility weights\n - `minrisk`: Optimize minimum risk weights\n - `nco`: Optimize with Non-Convex Optimization (NCO) model.\n - `plot`: Display efficient frontier\n - `plot_chart`: Display efficient frontier\n - `relriskparity`: Optimize with Relaxed Risk Parity using the least squares approach\n - `riskparity`: Optimize with Risk Parity using the risk budgeting approach\n - `show`: Show portfolio optimization results\n - """ - - return model.PortfolioPortfolioOptimization() diff --git a/openbb_terminal/core/sdk/controllers/stocks_sdk_controller.py b/openbb_terminal/core/sdk/controllers/stocks_sdk_controller.py deleted file mode 100644 index df3475efb89..00000000000 --- a/openbb_terminal/core/sdk/controllers/stocks_sdk_controller.py +++ /dev/null @@ -1,335 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.models import stocks_sdk_model as model - - -class StocksController(model.StocksRoot): - """Stocks Module. - - Submodules: - `ba`: Behavioral Analysis Module - `ca`: Comparison Analysis Module - `disc`: Discovery Module - `dps`: Darkpool Shorts Module - `fa`: Fundamental Analysis Module - `gov`: Government Module - `ins`: Insiders Module - `options`: Options Module - `qa`: Quantitative Analysis Module - `screener`: Screener Module - `ta`: Technical Analysis Module - `th`: Trading Hours Module - - Attributes: - `candle`: Show candle plot of loaded ticker.\n - `load`: Load a symbol to perform analysis using the string above as a template.\n - `process_candle`: Process DataFrame into candle style plot.\n - `quote`: Gets ticker quote from FMP\n - `quote_chart`: Financial Modeling Prep ticker(s) quote.\n - `search`: Search selected query for tickers.\n - `tob`: Get top of book bid and ask for ticker on exchange [CBOE.com]\n - """ - - @property - def ba(self): - """Stocks Behavioral Analysis Submodule - - Attributes: - `bullbear`: Gets bullbear sentiment for ticker [Source: stocktwits].\n - `cnews`: Get news from a company. [Source: Finnhub]\n - `getdd`: Get due diligence posts from list of subreddits [Source: reddit].\n - `headlines`: Gets Sentiment analysis provided by FinBrain's API [Source: finbrain].\n - `headlines_chart`: Plots Sentiment analysis from FinBrain. Prints table if raw is True. [Source: FinBrain]\n - `mentions`: Get interest over time from google api [Source: google].\n - `mentions_chart`: Plots weekly bars of stock's interest over time. other users watchlist. [Source: Google].\n - `messages`: Get last messages for a given ticker [Source: stocktwits].\n - `popular`: Get popular tickers from list of subreddits [Source: reddit].\n - `queries`: Get related queries from google api [Source: google].\n - `redditsent`: Find posts related to a specific search term in Reddit.\n - `regions`: Get interest by region from google api [Source: google].\n - `regions_chart`: Plots bars of regions based on stock's interest. [Source: Google].\n - `rise`: Get top rising related queries with this stock's query [Source: google].\n - `snews`: Get headlines sentiment using VADER model over time. [Source: Finnhub]\n - `snews_chart`: Display stock price and headlines sentiment using VADER model over time. [Source: Finnhub]\n - `stalker`: Gets messages from given user [Source: stocktwits].\n - `text_sent`: Find the sentiment of a post and related comments.\n - `trending`: Get trending tickers from stocktwits [Source: stocktwits].\n - `wsb`: Get wsb posts [Source: reddit].\n - """ - - return model.StocksBehavioralAnalysis() - - @property - def ca(self): - """Stocks Comparison Analysis Submodule - - Attributes: - `balance`: Get balance data. [Source: Marketwatch].\n - `cashflow`: Get cashflow data. [Source: Marketwatch]\n - `hcorr`: Get historical price correlation. [Source: Yahoo Finance]\n - `hcorr_chart`: Correlation heatmap based on historical price comparison\n - `hist`: Get historical prices for all comparison stocks\n - `hist_chart`: Display historical stock prices. [Source: Yahoo Finance]\n - `income`: Get income data. [Source: Marketwatch].\n - `income_chart`: Display income data. [Source: Marketwatch].\n - `scorr`: Get correlation sentiments across similar companies. [Source: FinBrain].\n - `scorr_chart`: Plot correlation sentiments heatmap across similar companies. [Source: FinBrain].\n - `screener`: Screener Overview.\n - `sentiment`: Gets Sentiment analysis from several symbols provided by FinBrain's API.\n - `sentiment_chart`: Display sentiment for all ticker. [Source: FinBrain].\n - `similar`: Find similar tickers to a given symbol.\n - `volume`: Get stock volume. [Source: Yahoo Finance]\n - `volume_chart`: Display stock volume. [Source: Yahoo Finance]\n - """ - - return model.StocksComparisonAnalysis() - - @property - def disc(self): - """Stocks Discovery Submodule - - Attributes: - `active`: Get stocks ordered in descending order by intraday trade volume. [Source: Yahoo Finance]\n - `arkord`: Returns ARK orders in a Dataframe\n - `asc`: Get Yahoo Finance small cap stocks with earnings growth rates better than 25%.\n - `dividends`: Gets dividend calendar for given date. Date represents Ex-Dividend Date\n - `filings`: Get SEC Filings RSS feed, disseminated by FMP\n - `filings_chart`: Display recent forms submitted to the SEC\n - `fipo`: Future IPOs dates. [Source: Finnhub]\n - `gainers`: Get top gainers. [Source: Yahoo Finance]\n - `gtech`: Get technology stocks with revenue and earnings growth in excess of 25%. [Source: Yahoo Finance]\n - `hotpenny`: Returns today hot penny stocks\n - `ipo`: Get IPO calendar\n - `losers`: Get top losers. [Source: Yahoo Finance]\n - `lowfloat`: Returns low float DataFrame\n - `pipo`: Past IPOs dates. [Source: Finnhub]\n - `rtat`: Gets the top 10 retail stocks per day\n - `trending`: Returns a list of trending articles\n - `ugs`: Get stocks with earnings growth rates better than 25% and relatively low PE and PEG ratios.\n - `ulc`: Get Yahoo Finance potentially undervalued large cap stocks.\n - `upcoming`: Returns a DataFrame with upcoming earnings\n - """ - - return model.StocksDiscovery() - - @property - def dps(self): - """Stocks Darkpool Shorts Submodule - - Attributes: - `dpotc`: Get all FINRA data associated with a ticker\n - `dpotc_chart`: Display barchart of dark pool (ATS) and OTC (Non ATS) data. [Source: FINRA]\n - `ftd`: Display fails-to-deliver data for a given ticker. [Source: SEC]\n - `ftd_chart`: Display fails-to-deliver data for a given ticker. [Source: SEC]\n - `hsi`: Returns a high short interest DataFrame\n - `pos`: Get dark pool short positions. [Source: Stockgrid]\n - `prom`: Get all FINRA ATS data, and parse most promising tickers based on linear regression\n - `prom_chart`: Display dark pool (ATS) data of tickers with growing trades activity. [Source: FINRA]\n - `psi_q`: Plots the short interest of a stock. This corresponds to the\n - `psi_q_chart`: Plot the short interest of a stock. This corresponds to the\n - `psi_sg`: Get price vs short interest volume. [Source: Stockgrid]\n - `psi_sg_chart`: Plot price vs short interest volume. [Source: Stockgrid]\n - `shorted`: Get most shorted stock screener [Source: Yahoo Finance]\n - `sidtc`: Get short interest and days to cover. [Source: Stockgrid]\n - `spos`: Get net short position. [Source: Stockgrid]\n - `spos_chart`: Plot net short position. [Source: Stockgrid]\n - """ - - return model.StocksDarkpoolShorts() - - @property - def fa(self): - """Stocks Fundamental Analysis Submodule - - Attributes: - `analysis`: Save time reading SEC filings with the help of machine learning. [Source: https://eclect.us]\n - `analyst`: Get analyst data. [Source: Finviz]\n - `balance`: Get balance sheet.\n - `cal`: Get calendar earnings for ticker symbol\n - `cash`: Get Cash Flow.\n - `customer`: Print customers from ticker provided\n - `dcf`: Get stocks dcf from FMP\n - `dcfc`: Get stocks dcf from FMP\n - `divs`: Get historical dividend for ticker\n - `divs_chart`: Display historical dividends\n - `dupont`: Get dupont ratios\n - `earnings`: Get earnings data.\n - `enterprise`: Financial Modeling Prep ticker enterprise\n - `epsfc`: Takes the ticker, asks for seekingalphaID and gets eps estimates\n - `est`: Get analysts' estimates for a given ticker. [Source: Business Insider]\n - `fama_coe`: Use Fama and French to get the cost of equity for a company\n - `fama_raw`: Get Fama French data\n - `fraud`: Get fraud ratios based on fundamentals\n - `growth`: Get financial statement growth\n - `historical_5`: Get 5 year monthly historical performance for a ticker with dividends filtered\n - `income`: Get income statement.\n - `key`: Get key metrics from overview\n - `metrics`: Get key metrics\n - `mgmt`: Get company managers from Business Insider\n - `mktcap`: Get market cap over time for ticker. [Source: Yahoo Finance]\n - `mktcap_chart`: Display market cap over time. [Source: Yahoo Finance]\n - `news`: Get news from Finviz\n - `overview`: Get overview.\n - `pt`: Get analysts' price targets for a given stock. [Source: Business Insider]\n - `pt_chart`: Display analysts' price targets for a given stock. [Source: Business Insider]\n - `rating`: Get ratings for a given ticker. [Source: Financial Modeling Prep]\n - `ratios`: Get key ratios\n - `revfc`: Takes the ticker, asks for seekingalphaID and gets rev estimates\n - `rot`: Get rating over time data. [Source: Finnhub]\n - `rot_chart`: Rating over time (monthly). [Source: Finnhub]\n - `score`: Gets value score from fmp\n - `sec`: Get SEC filings for a given stock ticker. [Source: Nasdaq]\n - `shrs`: Get shareholders from yahoo\n - `similar_dfs`: Get dataframes for similar companies\n - `splits`: Get splits and reverse splits events. [Source: Yahoo Finance]\n - `splits_chart`: Display splits and reverse splits events. [Source: Yahoo Finance]\n - `supplier`: Get suppliers from ticker provided. [Source: CSIMarket]\n - """ - - return model.StocksFundamentalAnalysis() - - @property - def gov(self): - """Stocks Government Submodule - - Attributes: - `contracts`: Get government contracts for ticker [Source: quiverquant.com]\n - `contracts_chart`: Show government contracts for ticker [Source: quiverquant.com]\n - `government_trading`: Returns the most recent transactions by members of government\n - `gtrades`: Government trading for specific ticker [Source: quiverquant.com]\n - `gtrades_chart`: Government trading for specific ticker [Source: quiverquant.com]\n - `histcont`: Get historical quarterly government contracts [Source: quiverquant.com]\n - `histcont_chart`: Show historical quarterly government contracts [Source: quiverquant.com]\n - `lastcontracts`: Get last government contracts [Source: quiverquant.com]\n - `lastcontracts_chart`: Last government contracts [Source: quiverquant.com]\n - `lasttrades`: Get last government trading [Source: quiverquant.com]\n - `lobbying`: Corporate lobbying details\n - `qtrcontracts`: Analyzes quarterly contracts by ticker\n - `qtrcontracts_chart`: Quarterly contracts [Source: quiverquant.com]\n - `topbuys`: Get top buy government trading [Source: quiverquant.com]\n - `topbuys_chart`: Top buy government trading [Source: quiverquant.com]\n - `toplobbying`: Corporate lobbying details\n - `toplobbying_chart`: Top lobbying tickers based on total spent\n - `topsells`: Get top sell government trading [Source: quiverquant.com]\n - `topsells_chart`: Top sell government trading [Source: quiverquant.com]\n - """ - - return model.StocksGovernment() - - @property - def ins(self): - """Stocks Insiders Submodule - - Attributes: - `act`: Get insider activity. [Source: Business Insider]\n - `act_chart`: Display insider activity. [Source: Business Insider]\n - `blcp`: Get latest CEO/CFO purchases > 25k\n - `blcs`: Get latest CEO/CFO sales > 100k\n - `blip`: Get latest insider purchases > 25k\n - `blis`: Get latest insider sales > 100k\n - `blop`: Get latest officer purchases > 25k\n - `blos`: Get latest officer sales > 100k\n - `filter`: GEt insider trades based on preset filter\n - `lcb`: Get latest cluster buys\n - `lins`: Get last insider activity for a given stock ticker. [Source: Finviz]\n - `lins_chart`: Display insider activity for a given stock ticker. [Source: Finviz]\n - `lip`: Get latest insider purchases\n - `lis`: Get latest insider sales\n - `lit`: Get latest insider trades\n - `lpsb`: Get latest penny stock buys\n - `print_insider_data`: Print insider data\n - `print_insider_data_chart`: Print insider data\n - `stats`: Get OpenInsider stats for ticker\n - """ - - return model.StocksInsiders() - - @property - def options(self): - """Stocks Options Submodule - - Attributes: - `chains`: Get Option Chain For A Stock. No greek data is returned\n - `dte`: Returns a new column containing the DTE as an integer, including 0.\n - `eodchain`: Get full EOD option date across all expirations\n - `expirations`: Get Option Chain Expirations\n - `generate_data`: Gets x values, and y values before and after premiums\n - `get_strategies`: Gets options strategies for all, or a list of, DTE(s).\n - `greeks`: Gets the greeks for a given option\n - `grhist`: Get historical EOD option prices, with Greeks, for a given OCC chain label.\n - `grhist_chart`: Plots historical greeks for a given option.\n - `hist`: Get historical option pricing.\n - `info`: Scrape barchart for options info\n - `info_chart`: Scrapes Barchart.com for the options information\n - `last_price`: Makes api request for last price\n - `load_options_chains`: Loads all options chains from a specific source, fields returned to each attribute will vary.\n - `oi`: Plot open interest\n - `pcr`: Gets put call ratio over last time window [Source: AlphaQuery.com]\n - `pcr_chart`: Display put call ratio [Source: AlphaQuery.com]\n - `price`: Get Option current price for a stock.\n - `unu`: Get unusual option activity from fdscanner.com\n - `unu_chart`: Displays the unusual options table\n - `voi`: Plot volume and open interest\n - `vol`: Plot volume\n - `vsurf`: Gets IV surface for calls and puts for ticker\n - `vsurf_chart`: Display vol surface\n - """ - - return model.StocksOptions() - - @property - def qa(self): - """Stocks Quantitative Analysis Submodule - - Attributes: - `beta`: Calculate beta for a ticker and a reference ticker.\n - `beta_chart`: Display the beta scatterplot + linear regression.\n - `capm`: Provides information that relates to the CAPM model\n - `fama_raw`: Gets base Fama French data to calculate risk\n - `historical_5`: Get 5 year monthly historical performance for a ticker with dividends filtered\n - """ - - return model.StocksQuantitativeAnalysis() - - @property - def screener(self): - """Stocks Screener Submodule - - Attributes: - `screener_data`: Screener Overview\n - `screener_data_chart`: Screener one of the following: overview, valuation, financial, ownership, performance, technical.\n - """ - - return model.StocksScreener() - - @property - def ta(self): - """Stocks Technical Analysis Submodule - - Attributes: - `recom`: Get tradingview recommendation based on technical indicators\n - `recom_chart`: Print tradingview recommendation based on technical indicators\n - `summary`: Get technical summary report provided by FinBrain's API\n - `summary_chart`: Print technical summary report provided by FinBrain's API\n - """ - - return model.StocksTechnicalAnalysis() - - @property - def th(self): - """Stocks Trading Hours Submodule - - Attributes: - `all`: Get all exchanges.\n - `all_chart`: Display all exchanges.\n - `check_if_open`: Check if market open helper function\n - `closed`: Get closed exchanges.\n - `closed_chart`: Display closed exchanges.\n - `exchange`: Get current exchange open hours.\n - `exchange_chart`: Display current exchange trading hours.\n - `open`: Get open exchanges.\n - `open_chart`: Display open exchanges.\n - """ - - return model.StocksTradingHours() diff --git a/openbb_terminal/core/sdk/models/__init__.py b/openbb_terminal/core/sdk/models/__init__.py deleted file mode 100644 index 1731ca7c585..00000000000 --- a/openbb_terminal/core/sdk/models/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=unused-import,wrong-import-order - -from .alt_sdk_model import AltRoot -from .crypto_sdk_model import CryptoRoot -from .econometrics_sdk_model import EconometricsRoot -from .economy_sdk_model import EconomyRoot -from .etf_sdk_model import EtfRoot -from .fixedincome_sdk_model import FixedincomeRoot -from .forecast_sdk_model import ForecastRoot -from .forex_sdk_model import ForexRoot -from .funds_sdk_model import FundsRoot -from .futures_sdk_model import FuturesRoot -from .keys_sdk_model import KeysRoot -from .portfolio_sdk_model import PortfolioRoot -from .qa_sdk_model import QaRoot -from .stocks_sdk_model import StocksRoot -from .ta_sdk_model import TaRoot diff --git a/openbb_terminal/core/sdk/models/alt_sdk_model.py b/openbb_terminal/core/sdk/models/alt_sdk_model.py deleted file mode 100644 index d9659a74a83..00000000000 --- a/openbb_terminal/core/sdk/models/alt_sdk_model.py +++ /dev/null @@ -1,114 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class AltRoot(Category): - """Alternative Module - - Attributes: - `hn`: Get top stories from HackerNews.\n - `hn_chart`: View top stories from HackerNews.\n - """ - - _location_path = "alt" - - def __init__(self): - super().__init__() - self.hn = lib.alt_hackernews_model.get_stories - self.hn_chart = lib.alt_hackernews_view.display_stories - - -class AltCompanieshouse(Category): - """Companieshouse Module. - - Attributes: - `download_filing_document`: Download company's filing document.\n - `get_company_info`: Gets company info by company number\n - `get_filing_document`: Download given filing document pdf\n - `get_filings`: Gets information on filings for given company, e.g. accounts, etc\n - `get_officers`: Gets information on company officers\n - `get_persons_with_significant_control`: Gets information on persons with significant control over the company\n - `get_search_results`: All companies with searchStr in their name.\n - """ - - _location_path = "alt.companieshouse" - - def __init__(self): - super().__init__() - self.download_filing_document = ( - lib.alt_companieshouse_companieshouse_view.download_filing_document - ) - self.get_charges = lib.alt_companieshouse_companieshouse_view.display_charges - self.get_company_info = ( - lib.alt_companieshouse_companieshouse_model.get_company_info - ) - self.get_filing_document = ( - lib.alt_companieshouse_companieshouse_model.get_filing_document - ) - self.get_filings = lib.alt_companieshouse_companieshouse_model.get_filings - self.get_officers = lib.alt_companieshouse_companieshouse_model.get_officers - self.get_persons_with_significant_control = ( - lib.alt_companieshouse_companieshouse_model.get_persons_with_significant_control - ) - self.get_search_results = ( - lib.alt_companieshouse_companieshouse_model.get_search_results - ) - - -class AltOss(Category): - """Oss Module. - - Attributes: - `_make_request`: Helper method to scrap.\n - `_retry_session`: Helper methods that retries to make request.\n - `github_data`: Get repository stats.\n - `history`: Get repository star history.\n - `history_chart`: Plots repo summary [Source: https://api.github.com].\n - `ross`: Get startups from ROSS index [Source: https://runacap.com/].\n - `ross_chart`: Plots list of startups from ross index [Source: https://runacap.com/]\n - `search`: Get repos sorted by stars or forks. Can be filtered by categories.\n - `summary`: Get repository summary.\n - `summary_chart`: Prints table showing repo summary [Source: https://api.github.com].\n - `top`: Get repos sorted by stars or forks. Can be filtered by categories.\n - `top_chart`: Plots repo summary [Source: https://api.github.com].\n - """ - - _location_path = "alt.oss" - - def __init__(self): - super().__init__() - self._make_request = lib.alt_oss_runa_model._make_request - self._retry_session = lib.alt_oss_runa_model._retry_session - self.github_data = lib.alt_oss_github_model.get_github_data - self.history = lib.alt_oss_github_model.get_stars_history - self.history_chart = lib.alt_oss_github_view.display_star_history - self.ross = lib.alt_oss_runa_model.get_startups - self.ross_chart = lib.alt_oss_runa_view.display_rossindex - self.search = lib.alt_oss_github_model.search_repos - self.summary = lib.alt_oss_github_model.get_repo_summary - self.summary_chart = lib.alt_oss_github_view.display_repo_summary - self.top = lib.alt_oss_github_model.get_top_repos - self.top_chart = lib.alt_oss_github_view.display_top_repos - - -class AltRealestate(Category): - """Realestate Module. - - Attributes: - `get_estate_sales`: All sales for specified postcode.\n - `get_region_stats`: Get regional house price statistics.\n - `get_towns_sold_prices`: Get towns sold house price data.\n - """ - - _location_path = "alt.realestate" - - def __init__(self): - super().__init__() - self.get_estate_sales = lib.alt_realestate_landRegistry_model.get_estate_sales - self.get_region_stats = lib.alt_realestate_landRegistry_model.get_region_stats - self.get_towns_sold_prices = ( - lib.alt_realestate_landRegistry_model.get_towns_sold_prices - ) diff --git a/openbb_terminal/core/sdk/models/crypto_sdk_model.py b/openbb_terminal/core/sdk/models/crypto_sdk_model.py deleted file mode 100644 index aad025ddbc0..00000000000 --- a/openbb_terminal/core/sdk/models/crypto_sdk_model.py +++ /dev/null @@ -1,585 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class CryptoRoot(Category): - """Cryptocurrency Module - - Attributes: - `candle`: Plot candle chart from dataframe. [Source: Binance]\n - `chart`: Load data for Technical Analysis\n - `find`: Find similar coin by coin name,symbol or id.\n - `load`: Load crypto currency to get data for\n - `price`: Displays live price from pyth live feed [Source: https://pyth.network/]\n - """ - - _location_path = "crypto" - - def __init__(self): - super().__init__() - self.candle = lib.crypto_helpers.plot_candles - self.chart = lib.crypto_helpers.plot_chart - self.find = lib.crypto_models.find - self.load = lib.crypto_helpers.load - self.price = lib.crypto_pyth_view.display_price - - -class CryptoDueDiligence(Category): - """Due Diligence Module. - - Attributes: - `all_binance_trading_pairs`: Returns all available pairs on Binance in DataFrame format. DataFrame has 3 columns symbol, baseAsset, quoteAsset\n - `ath`: Get all time high for a coin in a given currency\n - `atl`: Get all time low for a coin in a given currency\n - `balance`: Get account holdings for asset. [Source: Binance]\n - `balance_chart`: Prints table showing account holdings for asset. [Source: Binance]\n - `basic`: Basic coin information [Source: CoinPaprika]\n - `basic_chart`: Prints table showing basic information for coin. Like:\n - `binance_available_quotes_for_each_coin`: Helper methods that for every coin available on Binance add all quote assets. [Source: Binance]\n - `candle`: Get candles for chosen trading pair and time interval. [Source: Coinbase]\n - `check_valid_binance_str`: Check if symbol is in defined binance. [Source: Binance]\n - `coin`: Get coin by id [Source: CoinPaprika]\n - `coin_market_chart`: Get prices for given coin. [Source: CoinGecko]\n - `dev`: Get developer stats for a coin\n - `events`: Get all events related to given coin like conferences, start date of futures trading etc.\n - `events_chart`: Prints table showing all events for given coin id. [Source: CoinPaprika]\n - `ex`: Get all exchanges for given coin id. [Source: CoinPaprika]\n - `ex_chart`: Prints table showing all exchanges for given coin id. [Source: CoinPaprika]\n - `exchanges`: Helper method to get all the exchanges supported by ccxt\n - `fr`: Returns coin fundraising\n - `fr_chart`: Display coin fundraising\n - `get_mt`: Returns available messari timeseries\n - `get_mt_chart`: Prints table showing messari timeseries list\n - `gov`: Returns coin governance\n - `gov_chart`: Prints table showing coin governance\n - `headlines`: Gets Sentiment analysis provided by FinBrain's API [Source: finbrain].\n - `headlines_chart`: Sentiment analysis from FinBrain for Cryptocurrencies\n - `inv`: Returns coin investors\n - `inv_chart`: Prints table showing coin investors\n - `links`: Returns asset's links\n - `links_chart`: Prints table showing coin links\n - `mcapdom`: Returns market dominance of a coin over time\n - `mcapdom_chart`: Plots market dominance of a coin over time\n - `mkt`: All markets for given coin and currency [Source: CoinPaprika]\n - `mkt_chart`: Prints table showing all markets for given coin id. [Source: CoinPaprika]\n - `mt`: Returns messari timeseries\n - `mt_chart`: Plots messari timeseries\n - `news`: Get recent posts from CryptoPanic news aggregator platform. [Source: https://cryptopanic.com/]\n - `news_chart`: Prints table showing recent posts from CryptoPanic news aggregator platform.\n - `ob`: Returns orderbook for a coin in a given exchange\n - `ob_chart`: Plots order book for a coin in a given exchange\n - `pi`: Returns coin product info\n - `pi_chart`: Prints table showing project info\n - `pr`: Fetch data to calculate potential returns of a certain coin. [Source: CoinGecko]\n - `pr_chart`: Prints table showing potential returns of a certain coin. [Source: CoinGecko]\n - `ps`: Get all most important ticker related information for given coin id [Source: CoinPaprika]\n - `ps_chart`: Prints table showing ticker information for single coin [Source: CoinPaprika]\n - `rm`: Returns coin roadmap\n - `rm_chart`: Plots coin roadmap\n - `score`: Get scores for a coin from CoinGecko\n - `show_available_pairs_for_given_symbol`: Return all available quoted assets for given symbol. [Source: Coinbase]\n - `social`: Get social media stats for a coin\n - `stats`: Get 24 hr stats for the product. Volume is in base currency units.\n - `stats_chart`: Prints table showing 24 hr stats for the product. Volume is in base currency units.\n - `team`: Returns coin team\n - `team_chart`: Prints table showing coin team\n - `tk`: Returns coin tokenomics\n - `tk_chart`: Plots coin tokenomics\n - `tokenomics`: Get tokenomics for given coin. [Source: CoinGecko]\n - `trades`: Returns trades for a coin in a given exchange\n - `trades_chart`: Prints table showing trades for a coin in a given exchange\n - `trading_pair_info`: Get information about chosen trading pair. [Source: Coinbase]\n - `trading_pairs`: Helper method that return all trading pairs on binance. Methods ause this data for input for e.g\n - `twitter`: Get twitter timeline for given coin id. Not more than last 50 tweets [Source: CoinPaprika]\n - `twitter_chart`: Prints table showing twitter timeline for given coin id. Not more than last 50 tweets [Source: CoinPaprika]\n - """ - - _location_path = "crypto.dd" - - def __init__(self): - super().__init__() - self.all_binance_trading_pairs = ( - lib.crypto_dd_binance_model.get_all_binance_trading_pairs - ) - self.ath = lib.crypto_dd_sdk_helper.ath - self.atl = lib.crypto_dd_sdk_helper.atl - self.balance = lib.crypto_dd_binance_view.get_balance - self.balance_chart = lib.crypto_dd_binance_view.display_balance - self.basic = lib.crypto_dd_coinpaprika_model.basic_coin_info - self.basic_chart = lib.crypto_dd_coinpaprika_view.display_basic - self.binance_available_quotes_for_each_coin = ( - lib.crypto_dd_binance_model.get_binance_available_quotes_for_each_coin - ) - self.candle = lib.crypto_dd_coinbase_model.get_candles - self.check_valid_binance_str = ( - lib.crypto_dd_binance_model.check_valid_binance_str - ) - self.coin = lib.crypto_dd_coinpaprika_model.get_coin - self.coin_market_chart = lib.crypto_dd_pycoingecko_model.get_coin_market_chart - self.dev = lib.crypto_dd_sdk_helper.dev_stats - self.events = lib.crypto_dd_coinpaprika_model.get_coin_events_by_id - self.events_chart = lib.crypto_dd_coinpaprika_view.display_events - self.ex = lib.crypto_dd_coinpaprika_model.get_coin_exchanges_by_id - self.ex_chart = lib.crypto_dd_coinpaprika_view.display_exchanges - self.exchanges = lib.crypto_dd_ccxt_model.get_exchanges - self.fr = lib.crypto_dd_messari_model.get_fundraising - self.fr_chart = lib.crypto_dd_messari_view.display_fundraising - self.get_mt = lib.crypto_dd_messari_model.get_available_timeseries - self.get_mt_chart = lib.crypto_dd_messari_view.display_messari_timeseries_list - self.gov = lib.crypto_dd_messari_model.get_governance - self.gov_chart = lib.crypto_dd_messari_view.display_governance - self.headlines = lib.stocks_ba_finbrain_model.get_sentiment - self.headlines_chart = ( - lib.crypto_dd_finbrain_view.display_crypto_sentiment_analysis - ) - self.inv = lib.crypto_dd_messari_model.get_investors - self.inv_chart = lib.crypto_dd_messari_view.display_investors - self.links = lib.crypto_dd_messari_model.get_links - self.links_chart = lib.crypto_dd_messari_view.display_links - self.mcapdom = lib.crypto_dd_messari_model.get_marketcap_dominance - self.mcapdom_chart = lib.crypto_dd_messari_view.display_marketcap_dominance - self.mkt = lib.crypto_dd_coinpaprika_model.get_coin_markets_by_id - self.mkt_chart = lib.crypto_dd_coinpaprika_view.display_markets - self.mt = lib.crypto_dd_messari_model.get_messari_timeseries - self.mt_chart = lib.crypto_dd_messari_view.display_messari_timeseries - self.news = lib.crypto_ov_cryptopanic_model.get_news - self.news_chart = lib.crypto_dd_cryptopanic_view.display_news - self.ob = lib.crypto_dd_ccxt_model.get_orderbook - self.ob_chart = lib.crypto_dd_ccxt_view.display_order_book - self.pi = lib.crypto_dd_messari_model.get_project_product_info - self.pi_chart = lib.crypto_dd_messari_view.display_project_info - self.pr = lib.crypto_dd_pycoingecko_model.get_coin_potential_returns - self.pr_chart = lib.crypto_dd_pycoingecko_view.display_coin_potential_returns - self.ps = lib.crypto_dd_coinpaprika_model.get_tickers_info_for_coin - self.ps_chart = lib.crypto_dd_coinpaprika_view.display_price_supply - self.rm = lib.crypto_dd_messari_model.get_roadmap - self.rm_chart = lib.crypto_dd_messari_view.display_roadmap - self.score = lib.crypto_dd_sdk_helper.score - self.show_available_pairs_for_given_symbol = ( - lib.crypto_dd_coinbase_model.show_available_pairs_for_given_symbol - ) - self.social = lib.crypto_dd_sdk_helper.social - self.stats = lib.crypto_dd_coinbase_model.get_product_stats - self.stats_chart = lib.crypto_dd_coinbase_view.display_stats - self.team = lib.crypto_dd_messari_model.get_team - self.team_chart = lib.crypto_dd_messari_view.display_team - self.tk = lib.crypto_dd_messari_model.get_tokenomics - self.tk_chart = lib.crypto_dd_messari_view.display_tokenomics - self.tokenomics = lib.crypto_dd_pycoingecko_model.get_coin_tokenomics - self.trades = lib.crypto_dd_ccxt_model.get_trades - self.trades_chart = lib.crypto_dd_ccxt_view.display_trades - self.trading_pair_info = lib.crypto_dd_coinbase_model.get_trading_pair_info - self.trading_pairs = lib.crypto_dd_binance_model._get_trading_pairs - self.twitter = lib.crypto_dd_coinpaprika_model.get_coin_twitter_timeline - self.twitter_chart = lib.crypto_dd_coinpaprika_view.display_twitter - - -class CryptoDeFi(Category): - """DeFi Module. - - Attributes: - `dtvl`: Returns information about historical tvl of a defi protocol.\n - `dtvl_chart`: Plots historical TVL of different dApps\n - `gdapps`: Display top dApps (in terms of TVL) grouped by chain.\n - `gdapps_chart`: Plots top dApps (in terms of TVL) grouped by chain.\n - `ldapps`: Returns information about listed DeFi protocols, their current TVL and changes to it in the last hour/day/week.\n - `ldapps_chart`: Prints table showing information about listed DeFi protocols, their current TVL and changes to it in\n - `luna_supply`: Get supply history of the Terra ecosystem\n - `luna_supply_chart`: Plots and prints table showing Luna circulating supply stats\n - `newsletters`: Scrape all substack newsletters from url list.\n - `newsletters_chart`: Prints table showing DeFi related substack newsletters.\n - `stvl`: Returns historical values of the total sum of TVLs from all listed protocols.\n - `stvl_chart`: Plots historical values of the total sum of TVLs from all listed protocols.\n - `vaults`: Get DeFi Vaults Information. DeFi Vaults are pools of funds with an assigned strategy which main goal is to\n - `vaults_chart`: Prints table showing Top DeFi Vaults - pools of funds with an assigned strategy which main goal is to\n - """ - - _location_path = "crypto.defi" - - def __init__(self): - super().__init__() - self.dtvl = lib.crypto_defi_llama_model.get_defi_protocol - self.dtvl_chart = lib.crypto_defi_llama_view.display_historical_tvl - self.gdapps = lib.crypto_defi_llama_model.get_grouped_defi_protocols - self.gdapps_chart = lib.crypto_defi_llama_view.display_grouped_defi_protocols - self.ldapps = lib.crypto_defi_llama_model.get_defi_protocols - self.ldapps_chart = lib.crypto_defi_llama_view.display_defi_protocols - self.luna_supply = lib.crypto_defi_smartstake_model.get_luna_supply_stats - self.luna_supply_chart = ( - lib.crypto_defi_smartstake_view.display_luna_circ_supply_change - ) - self.newsletters = lib.crypto_defi_substack_model.get_newsletters - self.newsletters_chart = lib.crypto_defi_substack_view.display_newsletters - self.stvl = lib.crypto_defi_llama_model.get_defi_tvl - self.stvl_chart = lib.crypto_defi_llama_view.display_defi_tvl - self.vaults = lib.crypto_defi_coindix_model.get_defi_vaults - self.vaults_chart = lib.crypto_defi_coindix_view.display_defi_vaults - - -class CryptoDiscovery(Category): - """Discovery Module. - - Attributes: - `categories_keys`: Get list of categories keys\n - `coin_list`: Get list of coins available on CoinGecko [Source: CoinGecko]\n - `coins`: Get N coins from CoinGecko [Source: CoinGecko]\n - `coins_chart`: Prints table showing top coins [Source: CoinGecko]\n - `coins_for_given_exchange`: Helper method to get all coins available on binance exchange [Source: CoinGecko]\n - `cpsearch`: Search CoinPaprika. [Source: CoinPaprika]\n - `cpsearch_chart`: Prints table showing Search over CoinPaprika. [Source: CoinPaprika]\n - `dapp_categories`: Get dapp categories [Source: https://dappradar.com/]\n - `dapp_categories_chart`: Prints table showing dapp categories [Source: https://dappradar.com/]\n - `dapp_chains`: Get dapp chains [Source: https://dappradar.com/]\n - `dapp_chains_chart`: Prints table showing dapp chains [Source: https://dappradar.com/]\n - `dapp_metrics`: Get dapp metrics [Source: https://dappradar.com/]\n - `dapp_metrics_chart`: Prints table showing dapp metrics [Source: https://dappradar.com/]\n - `dapps`: Get dapps [Source: https://dappradar.com/]\n - `dapps_chart`: Prints table showing dapps [Source: https://dappradar.com/]\n - `defi_chains`: Get defi chains [Source: https://dappradar.com/]\n - `defi_chains_chart`: Prints table showing defi chains [Source: https://dappradar.com/]\n - `fees`: Show cryptos with most fees. [Source: CryptoStats]\n - `fees_chart`: Display crypto with most fees paid [Source: CryptoStats]\n - `gainers`: Shows Largest Gainers - coins which gain the most in given period. [Source: CoinGecko]\n - `gainers_chart`: Prints table showing Largest Gainers - coins which gain the most in given period. [Source: CoinGecko]\n - `losers`: Shows Largest Losers - coins which lose the most in given period. [Source: CoinGecko]\n - `losers_chart`: Prints table showing Largest Losers - coins which lost the most in given period of time. [Source: CoinGecko]\n - `nft_mktp`: Get top nft collections [Source: https://dappradar.com/]\n - `nft_mktp_chart`: Prints table showing nft marketplaces [Source: https://dappradar.com/]\n - `nft_mktp_chains`: Get nft marketplaces chains [Source: https://dappradar.com/]\n - `nft_mktp_chains_chart`: Prints table showing nft marketplaces chains [Source: https://dappradar.com/]\n - `tokens`: Get chains that support tokens [Source: https://dappradar.com/]\n - `tokens_chart`: Prints table showing chains that support tokens [Source: https://dappradar.com/]\n - `top_coins`: Get top cryptp coins.\n - `trending`: Returns trending coins [Source: CoinGecko]\n - `trending_chart`: Prints table showing trending coins [Source: CoinGecko]\n - """ - - _location_path = "crypto.disc" - - def __init__(self): - super().__init__() - self.categories_keys = lib.crypto_disc_pycoingecko_model.get_categories_keys - self.coin_list = lib.crypto_disc_pycoingecko_model.get_coin_list - self.coins = lib.crypto_disc_pycoingecko_model.get_coins - self.coins_chart = lib.crypto_disc_pycoingecko_view.display_coins - self.coins_for_given_exchange = ( - lib.crypto_disc_pycoingecko_model.get_coins_for_given_exchange - ) - self.cpsearch = lib.crypto_disc_coinpaprika_model.get_search_results - self.cpsearch_chart = lib.crypto_disc_coinpaprika_view.display_search_results - self.dapp_categories = lib.crypto_disc_dappradar_model.get_dapp_categories - self.dapp_categories_chart = ( - lib.crypto_disc_dappradar_view.display_dapp_categories - ) - self.dapp_chains = lib.crypto_disc_dappradar_model.get_dapp_chains - self.dapp_chains_chart = lib.crypto_disc_dappradar_view.display_dapp_chains - self.dapp_metrics = lib.crypto_disc_dappradar_model.get_dapp_metrics - self.dapp_metrics_chart = lib.crypto_disc_dappradar_view.display_dapp_metrics - self.dapps = lib.crypto_disc_dappradar_model.get_dapps - self.dapps_chart = lib.crypto_disc_dappradar_view.display_dapps - self.defi_chains = lib.crypto_disc_dappradar_model.get_defi_chains - self.defi_chains_chart = lib.crypto_disc_dappradar_view.display_defi_chains - self.fees = lib.crypto_disc_cryptostats_model.get_fees - self.fees_chart = lib.crypto_disc_cryptostats_view.display_fees - self.gainers = lib.crypto_disc_pycoingecko_model.get_gainers - self.gainers_chart = lib.crypto_disc_pycoingecko_view.display_gainers - self.losers = lib.crypto_disc_pycoingecko_model.get_losers - self.losers_chart = lib.crypto_disc_pycoingecko_view.display_losers - self.nft_mktp = lib.crypto_disc_dappradar_model.get_nft_marketplaces - self.nft_mktp_chart = lib.crypto_disc_dappradar_view.display_nft_marketplaces - self.nft_mktp_chains = ( - lib.crypto_disc_dappradar_model.get_nft_marketplace_chains - ) - self.nft_mktp_chains_chart = ( - lib.crypto_disc_dappradar_view.display_nft_marketplace_chains - ) - self.tokens = lib.crypto_disc_dappradar_model.get_token_chains - self.tokens_chart = lib.crypto_disc_dappradar_view.display_token_chains - self.top_coins = lib.crypto_disc_sdk_helpers.top_coins - self.trending = lib.crypto_disc_pycoingecko_model.get_trending_coins - self.trending_chart = lib.crypto_disc_pycoingecko_view.display_trending - - -class CryptoNFT(Category): - """NFT Module. - - Attributes: - `collections`: Get nft collections [Source: https://nftpricefloor.com/]\n - `collections_chart`: Display NFT collections. [Source: https://nftpricefloor.com/]\n - `fp`: Get nft collections [Source: https://nftpricefloor.com/]\n - `fp_chart`: Display NFT collection floor price over time. [Source: https://nftpricefloor.com/]\n - `stats`: Get stats of a nft collection [Source: opensea.io]\n - `stats_chart`: Prints table showing collection stats. [Source: opensea.io]\n - """ - - _location_path = "crypto.nft" - - def __init__(self): - super().__init__() - self.collections = lib.crypto_nft_pricefloor_model.get_collections - self.collections_chart = lib.crypto_nft_pricefloor_view.display_collections - self.fp = lib.crypto_nft_pricefloor_model.get_floor_price - self.fp_chart = lib.crypto_nft_pricefloor_view.display_floor_price - self.stats = lib.crypto_nft_opensea_model.get_collection_stats - self.stats_chart = lib.crypto_nft_opensea_view.display_collection_stats - - -class CryptoOnChain(Category): - """OnChain Module. - - Attributes: - `baas`: Get an average bid and ask prices, average spread for given crypto pair for chosen time period.\n - `baas_chart`: Prints table showing an average bid and ask prices, average spread for given crypto pair for chosen\n - `balance`: Get info about tokens on you ethereum blockchain balance. Eth balance, balance of all tokens which\n - `balance_chart`: Display info about tokens for given ethereum blockchain balance e.g. ETH balance,\n - `btc_supply`: Returns BTC circulating supply [Source: https://api.blockchain.info/]\n - `btc_supply_chart`: Returns BTC circulating supply [Source: https://api.blockchain.info/]\n - `btc_transac`: Returns BTC confirmed transactions [Source: https://api.blockchain.info/]\n - `btc_transac_chart`: Returns BTC confirmed transactions [Source: https://api.blockchain.info/]\n - `btcsingleblock`: Returns BTC block data in json format. [Source: https://blockchain.info/]\n - `btcsingleblock_chart`: Returns BTC block data. [Source: https://api.blockchain.info/]\n - `dex_trades_monthly`: Get list of trades on Decentralized Exchanges monthly aggregated.\n - `dvcp`: Get daily volume for given pair [Source: https://graphql.bitquery.io/]\n - `dvcp_chart`: Prints table showing daily volume for given pair\n - `erc20_tokens`: Helper method that loads ~1500 most traded erc20 token.\n - `hist`: Get information about balance historical transactions. [Source: Ethplorer]\n - `hist_chart`: Display information about balance historical transactions. [Source: Ethplorer]\n - `holders`: Get info about top token holders. [Source: Ethplorer]\n - `holders_chart`: Display info about top ERC20 token holders. [Source: Ethplorer]\n - `info`: Get info about ERC20 token. [Source: Ethplorer]\n - `info_chart`: Display info about ERC20 token. [Source: Ethplorer]\n - `lt`: Get trades on Decentralized Exchanges aggregated by DEX [Source: https://graphql.bitquery.io/]\n - `lt_chart`: Prints table showing Trades on Decentralized Exchanges aggregated by DEX or Month\n - `prices`: Get token historical prices with volume and market cap, and average price. [Source: Ethplorer]\n - `prices_chart`: Display token historical prices with volume and market cap, and average price.\n - `query_graph`: Helper methods for querying graphql api. [Source: https://bitquery.io/]\n - `th`: Get info about token historical transactions. [Source: Ethplorer]\n - `th_chart`: Display info about token history. [Source: Ethplorer]\n - `token_decimals`: Helper methods that gets token decimals number. [Source: Ethplorer]\n - `top`: Get top 50 tokens. [Source: Ethplorer]\n - `top_chart`: Display top ERC20 tokens [Source: Ethplorer]\n - `topledger`: Returns Topledger's Data for the given Organization's Slug[org_slug] based\n - `topledger_chart`: Display on-chain data from Topledger. [Source: Topledger]\n - `ttcp`: Get most traded crypto pairs on given decentralized exchange in chosen time period.\n - `ttcp_chart`: Prints table showing most traded crypto pairs on given decentralized exchange in chosen time period.\n - `tv`: Get token volume on different Decentralized Exchanges. [Source: https://graphql.bitquery.io/]\n - `tv_chart`: Prints table showing token volume on different Decentralized Exchanges.\n - `tx`: Get info about transaction. [Source: Ethplorer]\n - `tx_chart`: Display info about transaction. [Source: Ethplorer]\n - `ueat`: Get number of unique ethereum addresses which made a transaction in given time interval.\n - `ueat_chart`: Prints table showing number of unique ethereum addresses which made a transaction in given time interval\n - `whales`: Whale Alert's API allows you to retrieve live and historical transaction data from major blockchains.\n - `whales_chart`: Display huge value transactions from major blockchains. [Source: https://docs.whale-alert.io/]\n - """ - - _location_path = "crypto.onchain" - - def __init__(self): - super().__init__() - self.baas = lib.crypto_onchain_bitquery_model.get_spread_for_crypto_pair - self.baas_chart = ( - lib.crypto_onchain_bitquery_view.display_spread_for_crypto_pair - ) - self.balance = lib.crypto_onchain_ethplorer_model.get_address_info - self.balance_chart = lib.crypto_onchain_ethplorer_view.display_address_info - self.btc_supply = lib.crypto_onchain_blockchain_model.get_btc_circulating_supply - self.btc_supply_chart = ( - lib.crypto_onchain_blockchain_view.display_btc_circulating_supply - ) - self.btc_transac = ( - lib.crypto_onchain_blockchain_model.get_btc_confirmed_transactions - ) - self.btc_transac_chart = ( - lib.crypto_onchain_blockchain_view.display_btc_confirmed_transactions - ) - self.btcsingleblock = lib.crypto_onchain_blockchain_model.get_btc_single_block - self.btcsingleblock_chart = ( - lib.crypto_onchain_blockchain_view.display_btc_single_block - ) - self.dex_trades_monthly = ( - lib.crypto_onchain_bitquery_model.get_dex_trades_monthly - ) - self.dvcp = ( - lib.crypto_onchain_bitquery_model.get_daily_dex_volume_for_given_pair - ) - self.dvcp_chart = ( - lib.crypto_onchain_bitquery_view.display_daily_volume_for_given_pair - ) - self.erc20_tokens = lib.crypto_onchain_bitquery_model.get_erc20_tokens - self.hist = lib.crypto_onchain_ethplorer_model.get_address_history - self.hist_chart = lib.crypto_onchain_ethplorer_view.display_address_history - self.holders = lib.crypto_onchain_ethplorer_model.get_top_token_holders - self.holders_chart = lib.crypto_onchain_ethplorer_view.display_top_token_holders - self.info = lib.crypto_onchain_ethplorer_model.get_token_info - self.info_chart = lib.crypto_onchain_ethplorer_view.display_token_info - self.lt = lib.crypto_onchain_bitquery_model.get_dex_trades_by_exchange - self.lt_chart = lib.crypto_onchain_bitquery_view.display_dex_trades - self.prices = lib.crypto_onchain_ethplorer_model.get_token_historical_price - self.prices_chart = ( - lib.crypto_onchain_ethplorer_view.display_token_historical_prices - ) - self.query_graph = lib.crypto_onchain_bitquery_model.query_graph - self.th = lib.crypto_onchain_ethplorer_model.get_token_history - self.th_chart = lib.crypto_onchain_ethplorer_view.display_token_history - self.token_decimals = lib.crypto_onchain_ethplorer_model.get_token_decimals - self.top = lib.crypto_onchain_ethplorer_model.get_top_tokens - self.top_chart = lib.crypto_onchain_ethplorer_view.display_top_tokens - self.topledger = lib.crypto_onchain_topledger_model.get_topledger_data - self.topledger_chart = lib.crypto_onchain_topledger_view.display_topledger_data - self.ttcp = lib.crypto_onchain_bitquery_model.get_most_traded_pairs - self.ttcp_chart = lib.crypto_onchain_bitquery_view.display_most_traded_pairs - self.tv = lib.crypto_onchain_bitquery_model.get_token_volume_on_dexes - self.tv_chart = lib.crypto_onchain_bitquery_view.display_dex_volume_for_token - self.tx = lib.crypto_onchain_ethplorer_model.get_tx_info - self.tx_chart = lib.crypto_onchain_ethplorer_view.display_tx_info - self.ueat = lib.crypto_onchain_bitquery_model.get_ethereum_unique_senders - self.ueat_chart = ( - lib.crypto_onchain_bitquery_view.display_ethereum_unique_senders - ) - self.whales = lib.crypto_onchain_whale_alert_model.get_whales_transactions - self.whales_chart = ( - lib.crypto_onchain_whale_alert_view.display_whales_transactions - ) - - -class CryptoOverview(Category): - """Overview Module. - - Attributes: - `altindex`: Get altcoin index overtime\n - `altindex_chart`: Displays altcoin index overtime\n - `categories`: Returns top crypto categories [Source: CoinGecko]\n - `categories_chart`: Shows top cryptocurrency categories by market capitalization\n - `cbpairs`: Get a list of available currency pairs for trading. [Source: Coinbase]\n - `cbpairs_chart`: Displays a list of available currency pairs for trading. [Source: Coinbase]\n - `coin_list`: Get list of all available coins on CoinPaprika [Source: CoinPaprika]\n - `contracts`: Gets all contract addresses for given platform [Source: CoinPaprika]\n - `contracts_chart`: Gets all contract addresses for given platform. [Source: CoinPaprika]\n - `cr`: Returns crypto {borrow,supply} interest rates for cryptocurrencies across several platforms\n - `cr_chart`: Displays crypto {borrow,supply} interest rates for cryptocurrencies across several platforms\n - `crypto_hack`: Get crypto hack\n - `crypto_hack_slugs`: Get all crypto hack slugs\n - `crypto_hacks`: Get major crypto-related hacks\n - `crypto_hacks_chart`: Display list of major crypto-related hacks. If slug is passed\n - `defi`: Get global statistics about Decentralized Finances [Source: CoinGecko]\n - `defi_chart`: Shows global statistics about Decentralized Finances. [Source: CoinGecko]\n - `derivatives`: Get list of crypto derivatives from CoinGecko API [Source: CoinGecko]\n - `derivatives_chart`: Shows list of crypto derivatives. [Source: CoinGecko]\n - `ewf`: Scrapes exchange withdrawal fees\n - `ewf_chart`: Exchange withdrawal fees\n - `exchanges`: Show top crypto exchanges.\n - `exmarkets`: List markets by exchange ID [Source: CoinPaprika]\n - `exmarkets_chart`: Get all markets for given exchange [Source: CoinPaprika]\n - `exrates`: Get list of crypto, fiats, commodity exchange rates from CoinGecko API [Source: CoinGecko]\n - `exrates_chart`: Shows list of crypto, fiats, commodity exchange rates. [Source: CoinGecko]\n - `globe`: Get global crypto market data.\n - `hm`: Get N coins from CoinGecko [Source: CoinGecko]\n - `hm_chart`: Shows cryptocurrencies heatmap [Source: CoinGecko]\n - `hold`: Returns public companies that holds ethereum or bitcoin [Source: CoinGecko]\n - `hold_chart`: Shows overview of public companies that holds ethereum or bitcoin. [Source: CoinGecko]\n - `indexes`: Get list of crypto indexes from CoinGecko API [Source: CoinGecko]\n - `indexes_chart`: Shows list of crypto indexes. [Source: CoinGecko]\n - `info`: Returns basic coin information for all coins from CoinPaprika API [Source: CoinPaprika]\n - `info_chart`: Displays basic coin information for all coins from CoinPaprika API. [Source: CoinPaprika]\n - `markets`: Returns basic coin information for all coins from CoinPaprika API [Source: CoinPaprika]\n - `markets_chart`: Displays basic market information for all coins from CoinPaprika API. [Source: CoinPaprika]\n - `news`: Get recent posts from CryptoPanic news aggregator platform. [Source: https://cryptopanic.com/]\n - `news_chart`: Display recent posts from CryptoPanic news aggregator platform.\n - `platforms`: List all smart contract platforms like ethereum, solana, cosmos, polkadot, kusama ... [Source: CoinPaprika]\n - `platforms_chart`: List all smart contract platforms like ethereum, solana, cosmos, polkadot, kusama.\n - `products`: Get list of financial products from CoinGecko API\n - `products_chart`: Shows list of financial products. [Source: CoinGecko]\n - `stables`: Returns top stable coins [Source: CoinGecko]\n - `stables_chart`: Shows stablecoins data [Source: CoinGecko]\n - `wf`: Scrapes top coins withdrawal fees\n - `wf_chart`: Top coins withdrawal fees\n - `wfpe`: Scrapes coin withdrawal fees per exchange\n - `wfpe_chart`: Coin withdrawal fees per exchange\n - """ - - _location_path = "crypto.ov" - - def __init__(self): - super().__init__() - self.altindex = lib.crypto_ov_blockchaincenter_model.get_altcoin_index - self.altindex_chart = lib.crypto_ov_blockchaincenter_view.display_altcoin_index - self.categories = lib.crypto_ov_pycoingecko_model.get_top_crypto_categories - self.categories_chart = lib.crypto_ov_pycoingecko_view.display_categories - self.cbpairs = lib.crypto_ov_coinbase_model.get_trading_pairs - self.cbpairs_chart = lib.crypto_ov_coinbase_view.display_trading_pairs - self.coin_list = lib.crypto_dd_coinpaprika_model.get_coin_list - self.contracts = lib.crypto_ov_coinpaprika_model.get_contract_platform - self.contracts_chart = lib.crypto_ov_coinpaprika_view.display_contracts - self.cr = lib.crypto_ov_loanscan_model.get_rates - self.cr_chart = lib.crypto_ov_loanscan_view.display_crypto_rates - self.crypto_hack = lib.crypto_ov_rekt_model.get_crypto_hack - self.crypto_hack_slugs = lib.crypto_ov_rekt_model.get_crypto_hack_slugs - self.crypto_hacks = lib.crypto_ov_rekt_model.get_crypto_hacks - self.crypto_hacks_chart = lib.crypto_ov_rekt_view.display_crypto_hacks - self.defi = lib.crypto_ov_pycoingecko_model.get_global_defi_info - self.defi_chart = lib.crypto_ov_pycoingecko_view.display_global_defi_info - self.derivatives = lib.crypto_ov_pycoingecko_model.get_derivatives - self.derivatives_chart = lib.crypto_ov_pycoingecko_view.display_derivatives - self.ewf = ( - lib.crypto_ov_withdrawalfees_model.get_overall_exchange_withdrawal_fees - ) - self.ewf_chart = ( - lib.crypto_ov_withdrawalfees_view.display_overall_exchange_withdrawal_fees - ) - self.exchanges = lib.crypto_ov_sdk_helpers.exchanges - self.exmarkets = lib.crypto_ov_coinpaprika_model.get_exchanges_market - self.exmarkets_chart = lib.crypto_ov_coinpaprika_view.display_exchange_markets - self.exrates = lib.crypto_ov_pycoingecko_model.get_exchange_rates - self.exrates_chart = lib.crypto_ov_pycoingecko_view.display_exchange_rates - self.globe = lib.crypto_ov_sdk_helpers.globe - self.hm = lib.crypto_disc_pycoingecko_model.get_coins - self.hm_chart = lib.crypto_ov_pycoingecko_view.display_crypto_heatmap - self.hold = lib.crypto_ov_pycoingecko_model.get_holdings_overview - self.hold_chart = lib.crypto_ov_pycoingecko_view.display_holdings_overview - self.indexes = lib.crypto_ov_pycoingecko_model.get_indexes - self.indexes_chart = lib.crypto_ov_pycoingecko_view.display_indexes - self.info = lib.crypto_ov_coinpaprika_model.get_coins_info - self.info_chart = lib.crypto_ov_coinpaprika_view.display_all_coins_info - self.markets = lib.crypto_ov_coinpaprika_model.get_coins_market_info - self.markets_chart = ( - lib.crypto_ov_coinpaprika_view.display_all_coins_market_info - ) - self.news = lib.crypto_ov_cryptopanic_model.get_news - self.news_chart = lib.crypto_ov_cryptopanic_view.display_news - self.platforms = lib.crypto_ov_coinpaprika_model.get_all_contract_platforms - self.platforms_chart = lib.crypto_ov_coinpaprika_view.display_all_platforms - self.products = lib.crypto_ov_pycoingecko_model.get_finance_products - self.products_chart = lib.crypto_ov_pycoingecko_view.display_products - self.stables = lib.crypto_ov_pycoingecko_model.get_stable_coins - self.stables_chart = lib.crypto_ov_pycoingecko_view.display_stablecoins - self.wf = lib.crypto_ov_withdrawalfees_model.get_overall_withdrawal_fees - self.wf_chart = ( - lib.crypto_ov_withdrawalfees_view.display_overall_withdrawal_fees - ) - self.wfpe = lib.crypto_ov_withdrawalfees_model.get_crypto_withdrawal_fees - self.wfpe_chart = ( - lib.crypto_ov_withdrawalfees_view.display_crypto_withdrawal_fees - ) - - -class CryptoTools(Category): - """Tools Module. - - Attributes: - `apy`: Converts apr into apy\n - `apy_chart`: Displays APY value converted from APR\n - `il`: Calculates Impermanent Loss in a custom liquidity pool\n - `il_chart`: Displays Impermanent Loss in a custom liquidity pool\n - """ - - _location_path = "crypto.tools" - - def __init__(self): - super().__init__() - self.apy = lib.crypto_tools_model.calculate_apy - self.apy_chart = lib.crypto_tools_view.display_apy - self.il = lib.crypto_tools_model.calculate_il - self.il_chart = lib.crypto_tools_view.display_il diff --git a/openbb_terminal/core/sdk/models/econometrics_sdk_model.py b/openbb_terminal/core/sdk/models/econometrics_sdk_model.py deleted file mode 100644 index 7cbb8ecfeb8..00000000000 --- a/openbb_terminal/core/sdk/models/econometrics_sdk_model.py +++ /dev/null @@ -1,79 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class EconometricsRoot(Category): - """Econometrics Module - - Attributes: - `bgod`: Calculate test statistics for autocorrelation\n - `bgod_chart`: Show Breusch-Godfrey autocorrelation test\n - `bols`: The between estimator is an alternative, usually less efficient estimator, can can be used to\n - `bpag`: Calculate test statistics for heteroscedasticity\n - `bpag_chart`: Show Breusch-Pagan heteroscedasticity test\n - `clean`: Clean up NaNs from the dataset\n - `coint`: Calculate cointegration tests between variable number of input series\n - `coint_chart`: Estimates long-run and short-run cointegration relationship for series y and x and apply\n - `comparison`: Compare regression results between Panel Data regressions.\n - `dwat`: Calculate test statistics for Durbin Watson autocorrelation\n - `dwat_chart`: Show Durbin-Watson autocorrelation tests\n - `fdols`: First differencing is an alternative to using fixed effects when there might be correlation.\n - `fe`: When effects are correlated with the regressors the RE and BE estimators are not consistent.\n - `garch`: Calculates volatility forecasts based on GARCH.\n - `garch_chart`: Plots the volatility forecasts based on GARCH\n - `get_regression_data`: This function creates a DataFrame with the required regression data as\n - `granger`: Calculate granger tests\n - `granger_chart`: Show granger tests\n - `load`: Load custom file into dataframe.\n - `norm`: The distribution of returns and generate statistics on the relation to the normal curve.\n - `norm_chart`: Determine the normality of a timeseries.\n - `ols`: Performs an OLS regression on timeseries data. [Source: Statsmodels]\n - `options`: Obtain columns-dataset combinations from loaded in datasets that can be used in other commands\n - `options_chart`: Plot custom data\n - `panel`: Based on the regression type, this function decides what regression to run.\n - `panel_chart`: Based on the regression type, this function decides what regression to run.\n - `pols`: PooledOLS is just plain OLS that understands that various panel data structures.\n - `re`: The random effects model is virtually identical to the pooled OLS model except that is accounts for the\n - `root`: Calculate test statistics for unit roots\n - `root_chart`: Determine the normality of a timeseries.\n - `vif`: Calculates VIF (variance inflation factor), which tests collinearity.\n - """ - - _location_path = "econometrics" - - def __init__(self): - super().__init__() - self.bgod = lib.econometrics_regression_model.get_bgod - self.bgod_chart = lib.econometrics_regression_view.display_bgod - self.bols = lib.econometrics_regression_model.get_bols - self.bpag = lib.econometrics_regression_model.get_bpag - self.bpag_chart = lib.econometrics_regression_view.display_bpag - self.clean = lib.econometrics_model.clean - self.coint = lib.econometrics_model.get_coint_df - self.coint_chart = lib.econometrics_view.display_cointegration_test - self.comparison = lib.econometrics_regression_model.get_comparison - self.dwat = lib.econometrics_regression_model.get_dwat - self.dwat_chart = lib.econometrics_regression_view.display_dwat - self.fdols = lib.econometrics_regression_model.get_fdols - self.fe = lib.econometrics_regression_model.get_fe - self.garch = lib.econometrics_model.get_garch - self.garch_chart = lib.econometrics_view.display_garch - self.get_regression_data = lib.econometrics_regression_model.get_regression_data - self.granger = lib.econometrics_model.get_granger_causality - self.granger_chart = lib.econometrics_view.display_granger - self.load = lib.common_model.load - self.norm = lib.econometrics_model.get_normality - self.norm_chart = lib.econometrics_view.display_norm - self.ols = lib.econometrics_regression_model.get_ols - self.options = lib.econometrics_model.get_options - self.options_chart = lib.econometrics_view.show_options - self.panel = lib.econometrics_regression_model.get_regressions_results - self.panel_chart = lib.econometrics_regression_view.display_panel - self.pols = lib.econometrics_regression_model.get_pols - self.re = lib.econometrics_regression_model.get_re - self.root = lib.econometrics_model.get_root - self.root_chart = lib.econometrics_view.display_root - self.vif = lib.econometrics_model.get_vif diff --git a/openbb_terminal/core/sdk/models/economy_sdk_model.py b/openbb_terminal/core/sdk/models/economy_sdk_model.py deleted file mode 100644 index 10b65a57847..00000000000 --- a/openbb_terminal/core/sdk/models/economy_sdk_model.py +++ /dev/null @@ -1,113 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class EconomyRoot(Category): - """Economy Module - - Attributes: - `available_indices`: Get available indices\n - `balance`: General government deficit is defined as the balance of income and expenditure of government,\n - `balance_chart`: General government balance is defined as the balance of income and expenditure of government,\n - `ccpi`: Inflation measured by consumer price index (CPI) is defined as the change in the prices\n - `ccpi_chart`: Inflation measured by consumer price index (CPI) is defined as the change in the prices\n - `country_codes`: Get available country codes for Bigmac index\n - `cpi`: Obtain CPI data from FRED. [Source: FRED]\n - `cpi_chart`: Inflation measured by consumer price index (CPI) is defined as the change in\n - `currencies`: Scrape data for global currencies\n - `debt`: General government debt-to-GDP ratio measures the gross debt of the general\n - `debt_chart`: General government debt-to-GDP ratio measures the gross debt of the general\n - `events`: Get economic calendar for countries between specified dates\n - `fgdp`: Real gross domestic product (GDP) is GDP given in constant prices and\n - `fgdp_chart`: Real gross domestic product (GDP) is GDP given in constant prices and\n - `fred`: Get Series data. [Source: FRED]\n - `fred_chart`: Display (multiple) series from https://fred.stlouisfed.org. [Source: FRED]\n - `fred_notes`: Get series notes. [Source: FRED]\n - `future`: Get futures data. [Source: Finviz]\n - `futures`: Get futures data.\n - `gdp`: Gross domestic product (GDP) is the standard measure of the value added created\n - `gdp_chart`: Gross domestic product (GDP) is the standard measure of the value added created\n - `get_groups`: Get group available\n - `glbonds`: Scrape data for global bonds\n - `index`: Get data on selected indices over time [Source: Yahoo Finance]\n - `index_chart`: Load (and show) the selected indices over time [Source: Yahoo Finance]\n - `indices`: Get the top US indices\n - `macro`: This functions groups the data queried from the EconDB database [Source: EconDB]\n - `macro_chart`: Show the received macro data about a company [Source: EconDB]\n - `macro_countries`: This function returns the available countries and respective currencies.\n - `macro_parameters`: This function returns the available macro parameters with detail.\n - `overview`: Scrape data for market overview\n - `perfmap`: Opens Finviz map website in a browser. [Source: Finviz]\n - `performance`: Get group (sectors, industry or country) performance data. [Source: Finviz]\n - `revenue`: Governments collect revenues mainly for two purposes: to finance the goods\n - `revenue_chart`: Governments collect revenues mainly for two purposes: to finance the goods\n - `rgdp`: Gross domestic product (GDP) is the standard measure of the value added\n - `rgdp_chart`: Gross domestic product (GDP) is the standard measure of the value added\n - `search_index`: Search indices by keyword. [Source: FinanceDatabase]\n - `spending`: General government spending provides an indication of the size\n - `spending_chart`: General government spending provides an indication of the size\n - `treasury`: Get treasury rates from Federal Reserve\n - `treasury_chart`: Display U.S. Treasury rates [Source: EconDB]\n - `trust`: Trust in government refers to the share of people who report having confidence in\n - `trust_chart`: Trust in government refers to the share of people who report having confidence in\n - `usbonds`: Scrape data for us bonds\n - `usdli`: The USD Liquidity Index is defined as: [WALCL - WLRRAL - WDTGAL]. It is expressed in billions of USD.\n - `usdli_chart`: Display US Dollar Liquidity\n - `valuation`: Get group (sectors, industry or country) valuation data. [Source: Finviz]\n - """ - - _location_path = "economy" - - def __init__(self): - super().__init__() - self.available_indices = lib.economy_yfinance_model.get_available_indices - self.balance = lib.economy_oecd_model.get_balance - self.balance_chart = lib.economy_oecd_view.plot_balance - self.ccpi = lib.economy_oecd_model.get_cpi - self.ccpi_chart = lib.economy_oecd_view.plot_cpi - self.country_codes = lib.economy_nasdaq_model.get_country_codes - self.cpi = lib.economy_fred_model.get_cpi - self.cpi_chart = lib.economy_fred_view.plot_cpi - self.currencies = lib.economy_wsj_model.global_currencies - self.debt = lib.economy_oecd_model.get_debt - self.debt_chart = lib.economy_oecd_view.plot_debt - self.events = lib.economy_nasdaq_model.get_economic_calendar - self.fgdp = lib.economy_oecd_model.get_gdp_forecast - self.fgdp_chart = lib.economy_oecd_view.plot_gdp_forecast - self.fred = lib.economy_fred_model.get_aggregated_series_data - self.fred_chart = lib.economy_fred_view.display_fred_series - self.fred_notes = lib.economy_fred_model.get_series_notes - self.future = lib.economy_finviz_model.get_futures - self.futures = lib.economy_sdk_helpers.futures - self.gdp = lib.economy_oecd_model.get_gdp - self.gdp_chart = lib.economy_oecd_view.plot_gdp - self.get_groups = lib.economy_finviz_model.get_groups - self.glbonds = lib.economy_wsj_model.global_bonds - self.index = lib.economy_yfinance_model.get_indices - self.index_chart = lib.economy_yfinance_view.show_indices - self.indices = lib.economy_wsj_model.us_indices - self.macro = lib.economy_econdb_model.get_aggregated_macro_data - self.macro_chart = lib.economy_econdb_view.show_macro_data - self.macro_countries = lib.economy_econdb_model.get_macro_countries - self.macro_parameters = lib.economy_econdb_model.get_macro_parameters - self.overview = lib.economy_wsj_model.market_overview - self.perfmap = lib.economy_finviz_model.get_performance_map - self.performance = lib.economy_finviz_model.get_performance_data - self.revenue = lib.economy_oecd_model.get_revenue - self.revenue_chart = lib.economy_oecd_view.plot_revenue - self.rgdp = lib.economy_oecd_model.get_real_gdp - self.rgdp_chart = lib.economy_oecd_view.plot_real_gdp - self.search_index = lib.economy_yfinance_model.get_search_indices - self.spending = lib.economy_oecd_model.get_spending - self.spending_chart = lib.economy_oecd_view.plot_spending - self.treasury = lib.economy_fedreserve_model.get_treasury_rates - self.treasury_chart = lib.economy_fedreserve_view.show_treasuries - self.trust = lib.economy_oecd_model.get_trust - self.trust_chart = lib.economy_oecd_view.plot_trust - self.usbonds = lib.economy_wsj_model.us_bonds - self.usdli = lib.economy_fred_model.get_usd_liquidity - self.usdli_chart = lib.economy_fred_view.display_usd_liquidity - self.valuation = lib.economy_finviz_model.get_valuation_data diff --git a/openbb_terminal/core/sdk/models/etf_sdk_model.py b/openbb_terminal/core/sdk/models/etf_sdk_model.py deleted file mode 100644 index 578bf5f2ef7..00000000000 --- a/openbb_terminal/core/sdk/models/etf_sdk_model.py +++ /dev/null @@ -1,57 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class EtfRoot(Category): - """Etf Module - - Attributes: - `candle`: Show candle plot of loaded ticker.\n - `compare`: Compare selected ETFs\n - `etf_by_category`: Return a selection of ETFs based on category filtered by total assets.\n - `etf_by_name`: Get an ETF symbol and name based on ETF string to search. [Source: StockAnalysis]\n - `holdings`: Get ETF holdings\n - `ld`: Return a selection of ETFs based on description filtered by total assets.\n - `ln`: Return a selection of ETFs based on name filtered by total assets. [Source: Finance Database]\n - `load`: Load a symbol to perform analysis using the string above as a template.\n - `news`: Get news for a given term. [Source: NewsAPI]\n - `news_chart`: Prints table showing news for a given term. [Source: NewsAPI]\n - `overview`: Get overview data for selected etf\n - `symbols`: Gets all etf names and symbols\n - `weights`: Return sector weightings allocation of ETF. [Source: FinancialModelingPrep]\n - """ - - _location_path = "etf" - - def __init__(self): - super().__init__() - self.candle = lib.stocks_helper.display_candle - self.compare = lib.etf_stockanalysis_model.compare_etfs - self.etf_by_category = lib.etf_financedatabase_model.get_etfs_by_category - self.etf_by_name = lib.etf_stockanalysis_model.get_etfs_by_name - self.holdings = lib.etf_stockanalysis_model.get_etf_holdings - self.ld = lib.etf_financedatabase_model.get_etfs_by_description - self.ln = lib.etf_financedatabase_model.get_etfs_by_name - self.load = lib.stocks_helper.load - self.news = lib.common_newsapi_model.get_news - self.news_chart = lib.common_newsapi_view.display_news - self.overview = lib.etf_stockanalysis_model.get_etf_overview - self.symbols = lib.etf_stockanalysis_model.get_all_names_symbols - self.weights = lib.etf_fmp_model.get_etf_sector_weightings - - -class EtfDiscovery(Category): - """Discovery Module. - - Attributes: - `mover`: Scrape data for top etf movers.\n - """ - - _location_path = "etf.disc" - - def __init__(self): - super().__init__() - self.mover = lib.etf_disc_wsj_model.etf_movers diff --git a/openbb_terminal/core/sdk/models/fixedincome_sdk_model.py b/openbb_terminal/core/sdk/models/fixedincome_sdk_model.py deleted file mode 100644 index 0670acb71c6..00000000000 --- a/openbb_terminal/core/sdk/models/fixedincome_sdk_model.py +++ /dev/null @@ -1,63 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class FixedincomeRoot(Category): - """Fixedincome Module - - Attributes: - `ameribor`: Obtain data for American Interbank Offered Rate (AMERIBOR)\n - `cp`: Obtain Commercial Paper data\n - `dwpcr`: Obtain data for the Discount Window Primary Credit Rate.\n - `ecb`: Obtain data for ECB interest rates.\n - `ecbycrv`: Gets euro area yield curve data from ECB.\n - `estr`: Obtain data for Euro Short-Term Rate (ESTR)\n - `fed`: Obtain data for Effective Federal Funds Rate.\n - `ffrmc`: Get data for Selected Treasury Constant Maturity Minus Federal Funds Rate\n - `hqm`: The HQM yield curve represents the high quality corporate bond market, i.e.,\n - `icebofa`: Get data for ICE BofA US Corporate Bond Indices.\n - `icespread`: Get data for ICE BofA US Corporate Bond Spreads\n - `iorb`: Obtain data for Interest Rate on Reserve Balances.\n - `moody`: Get data for Moody Corporate Bond Index\n - `projection`: Obtain data for the Federal Reserve's projection of the federal funds rate.\n - `sofr`: Obtain data for Secured Overnight Financing Rate (SOFR)\n - `sonia`: Obtain data for Sterling Overnight Index Average (SONIA)\n - `spot`: The spot rate for any maturity is the yield on a bond that provides\n - `tbffr`: Get data for Selected Treasury Bill Minus Federal Funds Rate.\n - `tmc`: Get data for 10-Year Treasury Constant Maturity Minus Selected Treasury Constant Maturity.\n - `treasury`: Gets interest rates data from selected countries (3 month and 10 year)\n - `usrates`: Plot various treasury rates from the United States\n - `ycrv`: Gets yield curve data from FRED.\n - `ycrv_chart`: Display yield curve based on US Treasury rates for a specified date.\n - """ - - _location_path = "fixedincome" - - def __init__(self): - super().__init__() - self.ameribor = lib.fixedincome_fred_model.get_ameribor - self.cp = lib.fixedincome_fred_model.get_cp - self.dwpcr = lib.fixedincome_fred_model.get_dwpcr - self.ecb = lib.fixedincome_fred_model.get_ecb - self.ecbycrv = lib.fixedincome_ecb_model.get_ecb_yield_curve - self.estr = lib.fixedincome_fred_model.get_estr - self.fed = lib.fixedincome_fred_model.get_fed - self.ffrmc = lib.fixedincome_fred_model.get_ffrmc - self.hqm = lib.fixedincome_fred_model.get_hqm - self.icebofa = lib.fixedincome_fred_model.get_icebofa - self.icespread = lib.fixedincome_fred_model.get_icespread - self.iorb = lib.fixedincome_fred_model.get_iorb - self.moody = lib.fixedincome_fred_model.get_moody - self.projection = lib.fixedincome_fred_model.get_projection - self.sofr = lib.fixedincome_fred_model.get_sofr - self.sonia = lib.fixedincome_fred_model.get_sonia - self.spot = lib.fixedincome_fred_model.get_spot - self.tbffr = lib.fixedincome_fred_model.get_tbffr - self.tmc = lib.fixedincome_fred_model.get_tmc - self.treasury = lib.fixedincome_oecd_model.get_treasury - self.usrates = lib.fixedincome_fred_model.get_usrates - self.ycrv = lib.fixedincome_fred_model.get_yield_curve - self.ycrv_chart = lib.fixedincome_fred_view.display_yield_curve diff --git a/openbb_terminal/core/sdk/models/forecast_sdk_model.py b/openbb_terminal/core/sdk/models/forecast_sdk_model.py deleted file mode 100644 index 42884392e37..00000000000 --- a/openbb_terminal/core/sdk/models/forecast_sdk_model.py +++ /dev/null @@ -1,153 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class ForecastRoot(Category): - """Forecasting Module - - Attributes: - `anom`: Get Quantile Anomaly Detection Data\n - `anom_chart`: Display Quantile Anomaly Detection\n - `atr`: Calculate the Average True Range of a variable based on a a specific stock ticker.\n - `autoarima`: Performs Automatic ARIMA forecasting\n - `autoarima_chart`: Display Automatic ARIMA model.\n - `autoces`: Performs Automatic Complex Exponential Smoothing forecasting\n - `autoces_chart`: Display Automatic Complex Exponential Smoothing Model\n - `autoets`: Performs Automatic ETS forecasting\n - `autoets_chart`: Display Automatic ETS (Error, Trend, Sesonality) Model\n - `autoselect`: Performs Automatic Statistical forecasting\n - `autoselect_chart`: Display Automatic Statistical Forecasting Model\n - `brnn`: Performs Block RNN forecasting\n - `brnn_chart`: Display BRNN forecast\n - `clean`: Clean up NaNs from the dataset\n - `combine`: Adds the given column of df2 to df1\n - `corr`: Returns correlation for a given df\n - `corr_chart`: Plot correlation coefficients for dataset features\n - `delete`: Delete a column from a dataframe\n - `delta`: Calculate the %change of a variable based on a specific column\n - `desc`: Returns statistics for a given df\n - `desc_chart`: Show descriptive statistics for a dataframe\n - `ema`: A moving average provides an indication of the trend of the price movement\n - `expo`: Performs Probabilistic Exponential Smoothing forecasting\n - `expo_chart`: Display Probabilistic Exponential Smoothing forecast\n - `export`: Export a dataframe to a file\n - `linregr`: Perform Linear Regression Forecasting\n - `linregr_chart`: Display Linear Regression Forecasting\n - `load`: Load custom file into dataframe.\n - `mom`: A momentum oscillator, which measures the percentage change between the current\n - `mstl`: Performs MSTL forecasting\n - `mstl_chart`: Display MSTL Model\n - `nbeats`: Perform NBEATS Forecasting\n - `nbeats_chart`: Display NBEATS forecast\n - `nhits`: Performs Nhits forecasting\n - `nhits_chart`: Display Nhits forecast\n - `plot`: Plot data from a dataset\n - `plot_chart`: Plot data from a dataset\n - `regr`: Perform Regression Forecasting\n - `regr_chart`: Display Regression Forecasting\n - `rename`: Rename a column in a dataframe\n - `rnn`: Perform RNN forecasting\n - `rnn_chart`: Display RNN forecast\n - `roc`: A momentum oscillator, which measures the percentage change between the current\n - `rsi`: A momentum indicator that measures the magnitude of recent price changes to evaluate\n - `rwd`: Performs Random Walk with Drift forecasting\n - `rwd_chart`: Display Random Walk with Drift Model\n - `season_chart`: Plot seasonality from a dataset\n - `seasonalnaive`: Performs Seasonal Naive forecasting\n - `seasonalnaive_chart`: Display SeasonalNaive Model\n - `show`: Show a dataframe in a table\n - `signal`: A price signal based on short/long term price.\n - `sto`: Stochastic Oscillator %K and %D : A stochastic oscillator is a momentum indicator comparing a particular closing\n - `tcn`: Perform TCN forecasting\n - `tcn_chart`: Display TCN forecast\n - `tft`: Performs Temporal Fusion Transformer forecasting\n - `tft_chart`: Display Temporal Fusion Transformer forecast\n - `theta`: Performs Theta forecasting\n - `theta_chart`: Display Theta forecast\n - `trans`: Performs Transformer forecasting\n - `trans_chart`: Display Transformer forecast\n - """ - - _location_path = "forecast" - - def __init__(self): - super().__init__() - - if not lib.FORECASTING_TOOLKIT_ENABLED: - - # pylint: disable=C0415 - from openbb_terminal.rich_config import console - - console.print(lib.FORECASTING_TOOLKIT_WARNING) - - if lib.FORECASTING_TOOLKIT_ENABLED: - self.anom = lib.forecast_anom_model.get_anomaly_detection_data - self.anom_chart = lib.forecast_anom_view.display_anomaly_detection - self.atr = lib.forecast_model.add_atr - self.autoarima = lib.forecast_autoarima_model.get_autoarima_data - self.autoarima_chart = ( - lib.forecast_autoarima_view.display_autoarima_forecast - ) - self.autoces = lib.forecast_autoces_model.get_autoces_data - self.autoces_chart = lib.forecast_autoces_view.display_autoces_forecast - self.autoets = lib.forecast_autoets_model.get_autoets_data - self.autoets_chart = lib.forecast_autoets_view.display_autoets_forecast - self.autoselect = lib.forecast_autoselect_model.get_autoselect_data - self.autoselect_chart = ( - lib.forecast_autoselect_view.display_autoselect_forecast - ) - self.brnn = lib.forecast_brnn_model.get_brnn_data - self.brnn_chart = lib.forecast_brnn_view.display_brnn_forecast - self.clean = lib.forecast_model.clean - self.combine = lib.forecast_model.combine_dfs - self.corr = lib.forecast_model.corr_df - self.corr_chart = lib.forecast_view.display_corr - self.delete = lib.forecast_model.delete_column - self.delta = lib.forecast_model.add_delta - self.desc = lib.forecast_model.describe_df - self.desc_chart = lib.forecast_view.describe_df - self.ema = lib.forecast_model.add_ema - self.expo = lib.forecast_expo_model.get_expo_data - self.expo_chart = lib.forecast_expo_view.display_expo_forecast - self.export = lib.forecast_view.export_df - self.linregr = lib.forecast_linregr_model.get_linear_regression_data - self.linregr_chart = lib.forecast_linregr_view.display_linear_regression - self.load = lib.common_model.load - self.mom = lib.forecast_model.add_momentum - self.mstl = lib.forecast_mstl_model.get_mstl_data - self.mstl_chart = lib.forecast_mstl_view.display_mstl_forecast - self.nbeats = lib.forecast_nbeats_model.get_NBEATS_data - self.nbeats_chart = lib.forecast_nbeats_view.display_nbeats_forecast - self.nhits = lib.forecast_nhits_model.get_nhits_data - self.nhits_chart = lib.forecast_nhits_view.display_nhits_forecast - self.plot = lib.forecast_view.display_plot - self.plot_chart = lib.forecast_view.display_plot - self.regr = lib.forecast_regr_model.get_regression_data - self.regr_chart = lib.forecast_regr_view.display_regression - self.rename = lib.forecast_model.rename_column - self.rnn = lib.forecast_rnn_model.get_rnn_data - self.rnn_chart = lib.forecast_rnn_view.display_rnn_forecast - self.roc = lib.forecast_model.add_roc - self.rsi = lib.forecast_model.add_rsi - self.rwd = lib.forecast_rwd_model.get_rwd_data - self.rwd_chart = lib.forecast_rwd_view.display_rwd_forecast - self.season_chart = lib.forecast_view.display_seasonality - self.seasonalnaive = lib.forecast_seasonalnaive_model.get_seasonalnaive_data - self.seasonalnaive_chart = ( - lib.forecast_seasonalnaive_view.display_seasonalnaive_forecast - ) - self.show = lib.forecast_view.show_df - self.signal = lib.forecast_model.add_signal - self.sto = lib.forecast_model.add_sto - self.tcn = lib.forecast_tcn_model.get_tcn_data - self.tcn_chart = lib.forecast_tcn_view.display_tcn_forecast - self.tft = lib.forecast_tft_model.get_tft_data - self.tft_chart = lib.forecast_tft_view.display_tft_forecast - self.theta = lib.forecast_theta_model.get_theta_data - self.theta_chart = lib.forecast_theta_view.display_theta_forecast - self.trans = lib.forecast_trans_model.get_trans_data - self.trans_chart = lib.forecast_trans_view.display_trans_forecast - self.whisper = lib.forecast_whisper_model.transcribe_and_summarize diff --git a/openbb_terminal/core/sdk/models/forex_sdk_model.py b/openbb_terminal/core/sdk/models/forex_sdk_model.py deleted file mode 100644 index 41b8049762d..00000000000 --- a/openbb_terminal/core/sdk/models/forex_sdk_model.py +++ /dev/null @@ -1,27 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class ForexRoot(Category): - """Forex Module - - Attributes: - `candle`: Show candle plot for fx data.\n - `fwd`: Gets forward rates from fxempire\n - `get_currency_list`: Load AV currency codes from a local file.\n - `load`: Load forex for two given symbols.\n - `quote`: Get forex quote.\n - """ - - _location_path = "forex" - - def __init__(self): - super().__init__() - self.candle = lib.forex_helper.display_candle - self.fwd = lib.forex_fxempire_model.get_forward_rates - self.get_currency_list = lib.forex_av_model.get_currency_list - self.load = lib.forex_helper.load - self.quote = lib.forex_sdk_helpers.quote diff --git a/openbb_terminal/core/sdk/models/funds_sdk_model.py b/openbb_terminal/core/sdk/models/funds_sdk_model.py deleted file mode 100644 index 46b392760af..00000000000 --- a/openbb_terminal/core/sdk/models/funds_sdk_model.py +++ /dev/null @@ -1,35 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class FundsRoot(Category): - """Mutual Funds Module - - Attributes: - `carbon`: Search mstarpy for carbon metrics\n - `exclusion`: Search mstarpy exclusion policy in esgData\n - `historical`: Get historical fund, category, index price\n - `historical_chart`: Display historical fund, category, index price\n - `holdings`: Search mstarpy for holdings\n - `load`: Search mstarpy for matching funds\n - `search`: Search mstarpy for matching funds\n - `sector`: Get fund, category, index sector breakdown\n - `sector_chart`: Display fund, category, index sector breakdown\n - """ - - _location_path = "funds" - - def __init__(self): - super().__init__() - self.carbon = lib.funds_mstarpy_model.load_carbon_metrics - self.exclusion = lib.funds_mstarpy_model.load_exclusion_policy - self.historical = lib.funds_mstarpy_model.get_historical - self.historical_chart = lib.funds_mstarpy_view.display_historical - self.holdings = lib.funds_mstarpy_model.load_holdings - self.load = lib.funds_mstarpy_model.load_funds - self.search = lib.funds_mstarpy_model.search_funds - self.sector = lib.funds_mstarpy_model.get_sector - self.sector_chart = lib.funds_mstarpy_view.display_sector diff --git a/openbb_terminal/core/sdk/models/futures_sdk_model.py b/openbb_terminal/core/sdk/models/futures_sdk_model.py deleted file mode 100644 index 9118de52309..00000000000 --- a/openbb_terminal/core/sdk/models/futures_sdk_model.py +++ /dev/null @@ -1,27 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class FuturesRoot(Category): - """Futures Module - - Attributes: - `curve`: Get curve futures [Source: Yahoo Finance]\n - `curve_chart`: Display curve futures [Source: Yahoo Finance]\n - `historical`: Get historical futures data\n - `historical_chart`: Display historical futures [Source: Yahoo Finance]\n - `search`: Get search futures [Source: Yahoo Finance]\n - """ - - _location_path = "futures" - - def __init__(self): - super().__init__() - self.curve = lib.futures_yfinance_model.get_curve_futures - self.curve_chart = lib.futures_yfinance_view.display_curve - self.historical = lib.futures_sdk_model.get_historical - self.historical_chart = lib.futures_yfinance_view.display_historical - self.search = lib.futures_yfinance_model.get_search_futures diff --git a/openbb_terminal/core/sdk/models/keys_sdk_model.py b/openbb_terminal/core/sdk/models/keys_sdk_model.py deleted file mode 100644 index 3a26e2a4eb9..00000000000 --- a/openbb_terminal/core/sdk/models/keys_sdk_model.py +++ /dev/null @@ -1,69 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class KeysRoot(Category): - """Keys Module - - Attributes: - `av`: Set Alpha Vantage key\n - `binance`: Set Binance key\n - `bitquery`: Set Bitquery key\n - `biztoc`: Set BizToc key\n - `cmc`: Set Coinmarketcap key\n - `coinbase`: Set Coinbase key\n - `cpanic`: Set Cpanic key.\n - `databento`: Set DataBento key\n - `eodhd`: Set Eodhd key.\n - `ethplorer`: Set Ethplorer key.\n - `finnhub`: Set Finnhub key\n - `fmp`: Set Financial Modeling Prep key\n - `fred`: Set FRED key\n - `get_keys_info`: Get info on available APIs to use in set_keys.\n - `github`: Set GitHub key.\n - `messari`: Set Messari key.\n - `mykeys`: Get currently set API keys.\n - `news`: Set News key\n - `polygon`: Set Polygon key\n - `quandl`: Set Quandl key\n - `reddit`: Set Reddit key\n - `santiment`: Set Santiment key.\n - `set_keys`: Set API keys in bundle.\n - `smartstake`: Set Smartstake key.\n - `tradier`: Set Tradier key\n - `walert`: Set Walert key\n - """ - - _location_path = "keys" - - def __init__(self): - super().__init__() - self.av = lib.keys_model.set_av_key - self.binance = lib.keys_model.set_binance_key - self.bitquery = lib.keys_model.set_bitquery_key - self.biztoc = lib.keys_model.set_biztoc_key - self.cmc = lib.keys_model.set_cmc_key - self.coinbase = lib.keys_model.set_coinbase_key - self.cpanic = lib.keys_model.set_cpanic_key - self.databento = lib.keys_model.set_databento_key - self.eodhd = lib.keys_model.set_eodhd_key - self.ethplorer = lib.keys_model.set_ethplorer_key - self.finnhub = lib.keys_model.set_finnhub_key - self.fmp = lib.keys_model.set_fmp_key - self.fred = lib.keys_model.set_fred_key - self.get_keys_info = lib.keys_model.get_keys_info - self.github = lib.keys_model.set_github_key - self.messari = lib.keys_model.set_messari_key - self.mykeys = lib.keys_model.get_keys - self.news = lib.keys_model.set_news_key - self.polygon = lib.keys_model.set_polygon_key - self.quandl = lib.keys_model.set_quandl_key - self.reddit = lib.keys_model.set_reddit_key - self.santiment = lib.keys_model.set_santiment_key - self.set_keys = lib.keys_model.set_keys - self.smartstake = lib.keys_model.set_smartstake_key - self.tradier = lib.keys_model.set_tradier_key - self.walert = lib.keys_model.set_walert_key diff --git a/openbb_terminal/core/sdk/models/portfolio_sdk_model.py b/openbb_terminal/core/sdk/models/portfolio_sdk_model.py deleted file mode 100644 index 7b5ebe70a6f..00000000000 --- a/openbb_terminal/core/sdk/models/portfolio_sdk_model.py +++ /dev/null @@ -1,206 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class PortfolioRoot(Category): - """Portfolio Module - - Attributes: - `bench`: Load benchmark into portfolio\n - `distr`: Display daily returns\n - `distr_chart`: Display daily returns\n - `dret`: Get daily returns\n - `dret_chart`: Display daily returns\n - `es`: Get portfolio expected shortfall\n - `holdp`: Get holdings of assets (in percentage)\n - `holdp_chart`: Display holdings of assets (in percentage)\n - `holdv`: Get holdings of assets (absolute value)\n - `holdv_chart`: Display holdings of assets (absolute value)\n - `load`: Get PortfolioEngine object\n - `maxdd`: Calculate the drawdown (MDD) of historical series. Note that the calculation is done\n - `maxdd_chart`: Display maximum drawdown curve\n - `mret`: Get monthly returns\n - `mret_chart`: Display monthly returns\n - `om`: Get omega ratio\n - `om_chart`: Display omega ratio\n - `perf`: Get portfolio performance vs the benchmark\n - `rbeta`: Get rolling beta using portfolio and benchmark returns\n - `rbeta_chart`: Display rolling beta\n - `rsharpe`: Get rolling sharpe ratio\n - `rsharpe_chart`: Display rolling sharpe\n - `rsort`: Get rolling sortino\n - `rsort_chart`: Display rolling sortino\n - `rvol`: Get rolling volatility\n - `rvol_chart`: Display rolling volatility\n - `show`: Get portfolio transactions\n - `summary`: Get portfolio and benchmark returns summary\n - `var`: Get portfolio VaR\n - `yret`: Get yearly returns\n - `yret_chart`: Display yearly returns\n - """ - - _location_path = "portfolio" - - def __init__(self): - super().__init__() - self.bench = lib.portfolio_model.set_benchmark - self.distr = lib.portfolio_model.get_distribution_returns - self.distr_chart = lib.portfolio_view.display_distribution_returns - self.dret = lib.portfolio_model.get_daily_returns - self.dret_chart = lib.portfolio_view.display_daily_returns - self.es = lib.portfolio_model.get_es - self.holdp = lib.portfolio_model.get_holdings_percentage - self.holdp_chart = lib.portfolio_view.display_holdings_percentage - self.holdv = lib.portfolio_model.get_holdings_value - self.holdv_chart = lib.portfolio_view.display_holdings_value - self.load = lib.portfolio_model.generate_portfolio - self.maxdd = lib.portfolio_model.get_maximum_drawdown - self.maxdd_chart = lib.portfolio_view.display_maximum_drawdown - self.mret = lib.portfolio_model.get_monthly_returns - self.mret_chart = lib.portfolio_view.display_monthly_returns - self.om = lib.portfolio_model.get_omega - self.om_chart = lib.portfolio_view.display_omega - self.perf = lib.portfolio_model.get_performance_vs_benchmark - self.rbeta = lib.portfolio_model.get_rolling_beta - self.rbeta_chart = lib.portfolio_view.display_rolling_beta - self.rsharpe = lib.portfolio_model.get_rolling_sharpe - self.rsharpe_chart = lib.portfolio_view.display_rolling_sharpe - self.rsort = lib.portfolio_model.get_rolling_sortino - self.rsort_chart = lib.portfolio_view.display_rolling_sortino - self.rvol = lib.portfolio_model.get_rolling_volatility - self.rvol_chart = lib.portfolio_view.display_rolling_volatility - self.show = lib.portfolio_model.get_transactions - self.summary = lib.portfolio_model.get_summary - self.var = lib.portfolio_model.get_var - self.yret = lib.portfolio_model.get_yearly_returns - self.yret_chart = lib.portfolio_view.display_yearly_returns - - -class PortfolioAlloc(Category): - """Alloc Module. - - Attributes: - `assets`: Display portfolio asset allocation compared to the benchmark\n - `countries`: Display portfolio country allocation compared to the benchmark\n - `regions`: Display portfolio region allocation compared to the benchmark\n - `sectors`: Display portfolio sector allocation compared to the benchmark\n - """ - - _location_path = "portfolio.alloc" - - def __init__(self): - super().__init__() - self.assets = lib.portfolio_model.get_assets_allocation - self.countries = lib.portfolio_model.get_countries_allocation - self.regions = lib.portfolio_model.get_regions_allocation - self.sectors = lib.portfolio_model.get_sectors_allocation - - -class PortfolioMetric(Category): - """Metric Module. - - Attributes: - `calmar`: Get calmar ratio\n - `commonsense`: Get common sense ratio\n - `gaintopain`: Get Pain-to-Gain ratio based on historical data\n - `information`: Get information ratio\n - `jensens`: Get jensen's alpha\n - `kelly`: Get kelly criterion\n - `kurtosis`: Get kurtosis for portfolio and benchmark selected\n - `maxdrawdown`: Get maximum drawdown ratio for portfolio and benchmark selected\n - `payoff`: Get payoff ratio\n - `profitfactor`: Get profit factor\n - `rsquare`: Get R2 Score for portfolio and benchmark selected\n - `sharpe`: Get sharpe ratio for portfolio and benchmark selected\n - `skew`: Get skewness for portfolio and benchmark selected\n - `sortino`: Get sortino ratio for portfolio and benchmark selected\n - `tail`: Get tail ratio\n - `trackerr`: Get tracking error\n - `volatility`: Get volatility for portfolio and benchmark selected\n - """ - - _location_path = "portfolio.metric" - - def __init__(self): - super().__init__() - self.calmar = lib.portfolio_model.get_calmar_ratio - self.commonsense = lib.portfolio_model.get_common_sense_ratio - self.gaintopain = lib.portfolio_model.get_gaintopain_ratio - self.information = lib.portfolio_model.get_information_ratio - self.jensens = lib.portfolio_model.get_jensens_alpha - self.kelly = lib.portfolio_model.get_kelly_criterion - self.kurtosis = lib.portfolio_model.get_kurtosis - self.maxdrawdown = lib.portfolio_model.get_maximum_drawdown_ratio - self.payoff = lib.portfolio_model.get_payoff_ratio - self.profitfactor = lib.portfolio_model.get_profit_factor - self.rsquare = lib.portfolio_model.get_r2_score - self.sharpe = lib.portfolio_model.get_sharpe_ratio - self.skew = lib.portfolio_model.get_skewness - self.sortino = lib.portfolio_model.get_sortino_ratio - self.tail = lib.portfolio_model.get_tail_ratio - self.trackerr = lib.portfolio_model.get_tracking_error - self.volatility = lib.portfolio_model.get_volatility - - -class PortfolioPortfolioOptimization(Category): - """Portfolio Optimization Module. - - Attributes: - `blacklitterman`: Optimize decorrelation weights\n - `ef`: Get Efficient Frontier\n - `ef_chart`: Display efficient frontier\n - `file`: Load portfolio optimization engine from file\n - `herc`: Optimize with Hierarchical Equal Risk Contribution (HERC) method.\n - `hrp`: Optimize with Hierarchical Risk Parity\n - `load`: Load portfolio optimization engine\n - `load_bl_views`: Load a Excel file with views for Black Litterman model.\n - `maxdecorr`: Optimize decorrelation weights\n - `maxdiv`: Optimize diversification weights\n - `maxret`: Optimize maximum return weights\n - `maxsharpe`: Optimize Sharpe ratio weights\n - `maxutil`: Optimize maximum utility weights\n - `minrisk`: Optimize minimum risk weights\n - `nco`: Optimize with Non-Convex Optimization (NCO) model.\n - `plot`: Display efficient frontier\n - `plot_chart`: Display efficient frontier\n - `relriskparity`: Optimize with Relaxed Risk Parity using the least squares approach\n - `riskparity`: Optimize with Risk Parity using the risk budgeting approach\n - `show`: Show portfolio optimization results\n - """ - - _location_path = "portfolio.po" - - def __init__(self): - super().__init__() - - if not lib.OPTIMIZATION_TOOLKIT_ENABLED: - - # pylint: disable=C0415 - from openbb_terminal.rich_config import console - - console.print(lib.OPTIMIZATION_TOOLKIT_WARNING) - - if lib.OPTIMIZATION_TOOLKIT_ENABLED: - self.blacklitterman = lib.portfolio_optimization_po_model.get_blacklitterman - self.ef = lib.portfolio_optimization_po_model.get_ef - self.ef_chart = lib.portfolio_optimization_po_view.display_ef - self.file = lib.portfolio_optimization_po_model.load_parameters_file - self.herc = lib.portfolio_optimization_po_model.get_herc - self.hrp = lib.portfolio_optimization_po_model.get_hrp - self.load = lib.portfolio_optimization_po_model.generate_portfolio - self.load_bl_views = lib.portfolio_optimization_excel_model.load_bl_views - self.maxdecorr = lib.portfolio_optimization_po_model.get_maxdecorr - self.maxdiv = lib.portfolio_optimization_po_model.get_maxdiv - self.maxret = lib.portfolio_optimization_po_model.get_maxret - self.maxsharpe = lib.portfolio_optimization_po_model.get_maxsharpe - self.maxutil = lib.portfolio_optimization_po_model.get_maxutil - self.minrisk = lib.portfolio_optimization_po_model.get_minrisk - self.nco = lib.portfolio_optimization_po_model.get_nco - self.plot = lib.portfolio_optimization_po_view.display_plot - self.plot_chart = lib.portfolio_optimization_po_view.display_plot - self.relriskparity = lib.portfolio_optimization_po_model.get_relriskparity - self.riskparity = lib.portfolio_optimization_po_model.get_riskparity - self.show = lib.portfolio_optimization_po_model.show diff --git a/openbb_terminal/core/sdk/models/qa_sdk_model.py b/openbb_terminal/core/sdk/models/qa_sdk_model.py deleted file mode 100644 index 781e0ab581f..00000000000 --- a/openbb_terminal/core/sdk/models/qa_sdk_model.py +++ /dev/null @@ -1,87 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class QaRoot(Category): - """Quantitative Analysis Module - - Attributes: - `acf`: Plots Auto and Partial Auto Correlation of returns and change in returns\n - `bw`: Plots box and whisker plots\n - `calculate_adjusted_var`: Calculates VaR, which is adjusted for skew and kurtosis (Cornish-Fischer-Expansion)\n - `cdf`: Plots Cumulative Distribution Function\n - `cusum`: Plots Cumulative sum algorithm (CUSUM) to detect abrupt changes in data\n - `decompose`: Perform seasonal decomposition\n - `es`: Gets Expected Shortfall for specified stock dataframe.\n - `es_chart`: Prints table showing expected shortfall.\n - `hist`: Plots histogram of data\n - `kurtosis`: Kurtosis Indicator\n - `kurtosis_chart`: Plots rolling kurtosis\n - `line`: Display line plot of data\n - `normality`: Look at the distribution of returns and generate statistics on the relation to the normal curve.\n - `normality_chart`: Prints table showing normality statistics\n - `omega`: Get the omega series\n - `omega_chart`: Plots the omega ratio\n - `qqplot`: Plots QQ plot for data against normal quantiles\n - `quantile`: Overlay Median & Quantile\n - `quantile_chart`: Plots rolling quantile\n - `rolling`: Return rolling mean and standard deviation\n - `rolling_chart`: Plots mean std deviation\n - `sharpe`: Calculates the sharpe ratio\n - `sharpe_chart`: Plots Calculated the sharpe ratio\n - `skew`: Skewness Indicator\n - `skew_chart`: Plots rolling skew\n - `sortino`: Calculates the sortino ratio\n - `sortino_chart`: Plots the sortino ratio\n - `spread`: Standard Deviation and Variance\n - `spread_chart`: Plots rolling spread\n - `summary`: Print summary statistics\n - `summary_chart`: Prints table showing summary statistics\n - `unitroot`: Calculate test statistics for unit roots\n - `unitroot_chart`: Prints table showing unit root test calculations\n - `var`: Gets value at risk for specified stock dataframe.\n - `var_chart`: Prints table showing VaR of dataframe.\n - """ - - _location_path = "qa" - - def __init__(self): - super().__init__() - self.acf = lib.common_qa_view.display_acf - self.bw = lib.common_qa_view.display_bw - self.calculate_adjusted_var = lib.common_qa_model.calculate_adjusted_var - self.cdf = lib.common_qa_view.display_cdf - self.cusum = lib.common_qa_view.display_cusum - self.decompose = lib.common_qa_model.get_seasonal_decomposition - self.es = lib.common_qa_model.get_es - self.es_chart = lib.common_qa_view.display_es - self.hist = lib.common_qa_view.display_hist - self.kurtosis = lib.common_qa_rolling_model.get_kurtosis - self.kurtosis_chart = lib.common_qa_rolling_view.display_kurtosis - self.line = lib.common_qa_view.display_line - self.normality = lib.common_qa_model.get_normality - self.normality_chart = lib.common_qa_view.display_normality - self.omega = lib.common_qa_model.get_omega - self.omega_chart = lib.common_qa_view.display_omega - self.qqplot = lib.common_qa_view.display_qqplot - self.quantile = lib.common_qa_rolling_model.get_quantile - self.quantile_chart = lib.common_qa_rolling_view.display_quantile - self.rolling = lib.common_qa_rolling_model.get_rolling_avg - self.rolling_chart = lib.common_qa_rolling_view.display_mean_std - self.sharpe = lib.common_qa_model.get_sharpe - self.sharpe_chart = lib.common_qa_view.display_sharpe - self.skew = lib.common_qa_rolling_model.get_skew - self.skew_chart = lib.common_qa_rolling_view.display_skew - self.sortino = lib.common_qa_model.get_sortino - self.sortino_chart = lib.common_qa_view.display_sortino - self.spread = lib.common_qa_rolling_model.get_spread - self.spread_chart = lib.common_qa_rolling_view.display_spread - self.summary = lib.common_qa_model.get_summary - self.summary_chart = lib.common_qa_view.display_summary - self.unitroot = lib.common_qa_model.get_unitroot - self.unitroot_chart = lib.common_qa_view.display_unitroot - self.var = lib.common_qa_model.get_var - self.var_chart = lib.common_qa_view.display_var diff --git a/openbb_terminal/core/sdk/models/stocks_sdk_model.py b/openbb_terminal/core/sdk/models/stocks_sdk_model.py deleted file mode 100644 index f7add81d83a..00000000000 --- a/openbb_terminal/core/sdk/models/stocks_sdk_model.py +++ /dev/null @@ -1,577 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class StocksRoot(Category): - """Stocks Module - - Attributes: - `candle`: Show candle plot of loaded ticker.\n - `load`: Load a symbol to perform analysis using the string above as a template.\n - `process_candle`: Process DataFrame into candle style plot.\n - `quote`: Gets ticker quote from FMP\n - `quote_chart`: Financial Modeling Prep ticker(s) quote.\n - `search`: Search selected query for tickers.\n - `tob`: Get top of book bid and ask for ticker on exchange [CBOE.com]\n - """ - - _location_path = "stocks" - - def __init__(self): - super().__init__() - self.candle = lib.stocks_helper.display_candle - self.load = lib.stocks_helper.load - self.process_candle = lib.stocks_helper.process_candle - self.quote = lib.stocks_model.get_quote - self.quote_chart = lib.stocks_view.display_quote - self.search = lib.stocks_helper.search - self.tob = lib.stocks_cboe_model.get_top_of_book - - -class StocksBehavioralAnalysis(Category): - """Behavioral Analysis Module. - - Attributes: - `bullbear`: Gets bullbear sentiment for ticker [Source: stocktwits].\n - `cnews`: Get news from a company. [Source: Finnhub]\n - `getdd`: Get due diligence posts from list of subreddits [Source: reddit].\n - `headlines`: Gets Sentiment analysis provided by FinBrain's API [Source: finbrain].\n - `headlines_chart`: Plots Sentiment analysis from FinBrain. Prints table if raw is True. [Source: FinBrain]\n - `mentions`: Get interest over time from google api [Source: google].\n - `mentions_chart`: Plots weekly bars of stock's interest over time. other users watchlist. [Source: Google].\n - `messages`: Get last messages for a given ticker [Source: stocktwits].\n - `popular`: Get popular tickers from list of subreddits [Source: reddit].\n - `queries`: Get related queries from google api [Source: google].\n - `redditsent`: Find posts related to a specific search term in Reddit.\n - `regions`: Get interest by region from google api [Source: google].\n - `regions_chart`: Plots bars of regions based on stock's interest. [Source: Google].\n - `rise`: Get top rising related queries with this stock's query [Source: google].\n - `snews`: Get headlines sentiment using VADER model over time. [Source: Finnhub]\n - `snews_chart`: Display stock price and headlines sentiment using VADER model over time. [Source: Finnhub]\n - `stalker`: Gets messages from given user [Source: stocktwits].\n - `text_sent`: Find the sentiment of a post and related comments.\n - `trending`: Get trending tickers from stocktwits [Source: stocktwits].\n - `wsb`: Get wsb posts [Source: reddit].\n - """ - - _location_path = "stocks.ba" - - def __init__(self): - super().__init__() - self.bullbear = lib.stocks_ba_stocktwits_model.get_bullbear - self.cnews = lib.stocks_ba_finnhub_model.get_company_news - self.getdd = lib.stocks_ba_reddit_model.get_due_dilligence - self.headlines = lib.stocks_ba_finbrain_model.get_sentiment - self.headlines_chart = lib.stocks_ba_finbrain_view.display_sentiment_analysis - self.mentions = lib.stocks_ba_google_model.get_mentions - self.mentions_chart = lib.stocks_ba_google_view.display_mentions - self.messages = lib.stocks_ba_stocktwits_model.get_messages - self.popular = lib.stocks_ba_reddit_model.get_popular_tickers - self.queries = lib.stocks_ba_google_model.get_queries - self.redditsent = lib.stocks_ba_reddit_model.get_posts_about - self.regions = lib.stocks_ba_google_model.get_regions - self.regions_chart = lib.stocks_ba_google_view.display_regions - self.rise = lib.stocks_ba_google_model.get_rise - self.snews = lib.stocks_ba_finnhub_model.get_headlines_sentiment - self.snews_chart = ( - lib.stocks_ba_finnhub_view.display_stock_price_headlines_sentiment - ) - self.stalker = lib.stocks_ba_stocktwits_model.get_stalker - self.text_sent = lib.stocks_ba_reddit_model.get_sentiment - self.trending = lib.stocks_ba_stocktwits_model.get_trending - self.wsb = lib.stocks_ba_reddit_model.get_wsb_community - - -class StocksComparisonAnalysis(Category): - """Comparison Analysis Module. - - Attributes: - `balance`: Get balance data. [Source: Marketwatch].\n - `cashflow`: Get cashflow data. [Source: Marketwatch]\n - `hcorr`: Get historical price correlation. [Source: Yahoo Finance]\n - `hcorr_chart`: Correlation heatmap based on historical price comparison\n - `hist`: Get historical prices for all comparison stocks\n - `hist_chart`: Display historical stock prices. [Source: Yahoo Finance]\n - `income`: Get income data. [Source: Marketwatch].\n - `income_chart`: Display income data. [Source: Marketwatch].\n - `scorr`: Get correlation sentiments across similar companies. [Source: FinBrain].\n - `scorr_chart`: Plot correlation sentiments heatmap across similar companies. [Source: FinBrain].\n - `screener`: Screener Overview.\n - `sentiment`: Gets Sentiment analysis from several symbols provided by FinBrain's API.\n - `sentiment_chart`: Display sentiment for all ticker. [Source: FinBrain].\n - `similar`: Find similar tickers to a given symbol.\n - `volume`: Get stock volume. [Source: Yahoo Finance]\n - `volume_chart`: Display stock volume. [Source: Yahoo Finance]\n - """ - - _location_path = "stocks.ca" - - def __init__(self): - super().__init__() - self.balance = lib.stocks_ca_marketwatch_model.get_balance_comparison - self.cashflow = lib.stocks_ca_marketwatch_model.get_cashflow_comparison - self.hcorr = lib.stocks_ca_yahoo_finance_model.get_correlation - self.hcorr_chart = lib.stocks_ca_yahoo_finance_view.display_correlation - self.hist = lib.stocks_ca_yahoo_finance_model.get_historical - self.hist_chart = lib.stocks_ca_yahoo_finance_view.display_historical - self.income = lib.stocks_ca_marketwatch_model.get_income_comparison - self.income_chart = lib.stocks_ca_marketwatch_view.display_income_comparison - self.scorr = lib.stocks_ca_finbrain_model.get_sentiment_correlation - self.scorr_chart = lib.stocks_ca_finbrain_view.display_sentiment_correlation - self.screener = lib.stocks_ca_finviz_compare_model.get_comparison_data - self.sentiment = lib.stocks_ca_finbrain_model.get_sentiments - self.sentiment_chart = lib.stocks_ca_finbrain_view.display_sentiment_compare - self.similar = lib.stocks_ca_sdk_helpers.get_similar - self.volume = lib.stocks_ca_yahoo_finance_model.get_volume - self.volume_chart = lib.stocks_ca_yahoo_finance_view.display_volume - - -class StocksDiscovery(Category): - """Discovery Module. - - Attributes: - `active`: Get stocks ordered in descending order by intraday trade volume. [Source: Yahoo Finance]\n - `arkord`: Returns ARK orders in a Dataframe\n - `asc`: Get Yahoo Finance small cap stocks with earnings growth rates better than 25%.\n - `dividends`: Gets dividend calendar for given date. Date represents Ex-Dividend Date\n - `filings`: Get SEC Filings RSS feed, disseminated by FMP\n - `filings_chart`: Display recent forms submitted to the SEC\n - `fipo`: Future IPOs dates. [Source: Finnhub]\n - `gainers`: Get top gainers. [Source: Yahoo Finance]\n - `gtech`: Get technology stocks with revenue and earnings growth in excess of 25%. [Source: Yahoo Finance]\n - `hotpenny`: Returns today hot penny stocks\n - `ipo`: Get IPO calendar\n - `losers`: Get top losers. [Source: Yahoo Finance]\n - `lowfloat`: Returns low float DataFrame\n - `pipo`: Past IPOs dates. [Source: Finnhub]\n - `rtat`: Gets the top 10 retail stocks per day\n - `trending`: Returns a list of trending articles\n - `ugs`: Get stocks with earnings growth rates better than 25% and relatively low PE and PEG ratios.\n - `ulc`: Get Yahoo Finance potentially undervalued large cap stocks.\n - `upcoming`: Returns a DataFrame with upcoming earnings\n - """ - - _location_path = "stocks.disc" - - def __init__(self): - super().__init__() - self.active = lib.stocks_disc_yahoofinance_model.get_active - self.arkord = lib.stocks_disc_ark_model.get_ark_orders - self.asc = lib.stocks_disc_yahoofinance_model.get_asc - self.dividends = lib.stocks_disc_nasdaq_model.get_dividend_cal - self.filings = lib.stocks_fa_fmp_model.get_filings - self.filings_chart = lib.stocks_disc_fmp_view.display_filings - self.fipo = lib.stocks_disc_finnhub_model.get_future_ipo - self.gainers = lib.stocks_disc_yahoofinance_model.get_gainers - self.gtech = lib.stocks_disc_yahoofinance_model.get_gtech - self.hotpenny = lib.stocks_disc_shortinterest_model.get_today_hot_penny_stocks - self.ipo = lib.stocks_disc_finnhub_model.get_ipo_calendar - self.losers = lib.stocks_disc_yahoofinance_model.get_losers - self.lowfloat = lib.stocks_disc_shortinterest_model.get_low_float - self.pipo = lib.stocks_disc_finnhub_model.get_past_ipo - self.rtat = lib.stocks_disc_nasdaq_model.get_retail_tickers - self.trending = lib.stocks_disc_seeking_alpha_model.get_trending_list - self.ugs = lib.stocks_disc_yahoofinance_model.get_ugs - self.ulc = lib.stocks_disc_yahoofinance_model.get_ulc - self.upcoming = lib.stocks_disc_seeking_alpha_model.get_next_earnings - - -class StocksDarkpoolShorts(Category): - """Darkpool Shorts Module. - - Attributes: - `dpotc`: Get all FINRA data associated with a ticker\n - `dpotc_chart`: Display barchart of dark pool (ATS) and OTC (Non ATS) data. [Source: FINRA]\n - `ftd`: Display fails-to-deliver data for a given ticker. [Source: SEC]\n - `ftd_chart`: Display fails-to-deliver data for a given ticker. [Source: SEC]\n - `hsi`: Returns a high short interest DataFrame\n - `pos`: Get dark pool short positions. [Source: Stockgrid]\n - `prom`: Get all FINRA ATS data, and parse most promising tickers based on linear regression\n - `prom_chart`: Display dark pool (ATS) data of tickers with growing trades activity. [Source: FINRA]\n - `psi_q`: Plots the short interest of a stock. This corresponds to the\n - `psi_q_chart`: Plot the short interest of a stock. This corresponds to the\n - `psi_sg`: Get price vs short interest volume. [Source: Stockgrid]\n - `psi_sg_chart`: Plot price vs short interest volume. [Source: Stockgrid]\n - `shorted`: Get most shorted stock screener [Source: Yahoo Finance]\n - `sidtc`: Get short interest and days to cover. [Source: Stockgrid]\n - `spos`: Get net short position. [Source: Stockgrid]\n - `spos_chart`: Plot net short position. [Source: Stockgrid]\n - """ - - _location_path = "stocks.dps" - - def __init__(self): - super().__init__() - self.dpotc = lib.stocks_dps_finra_model.getTickerFINRAdata - self.dpotc_chart = lib.stocks_dps_finra_view.darkpool_ats_otc - self.ftd = lib.stocks_dps_sec_model.get_fails_to_deliver - self.ftd_chart = lib.stocks_dps_sec_view.fails_to_deliver - self.hsi = lib.stocks_dps_shortinterest_model.get_high_short_interest - self.pos = lib.stocks_dps_stockgrid_model.get_dark_pool_short_positions - self.prom = lib.stocks_dps_finra_model.getATSdata - self.prom_chart = lib.stocks_dps_finra_view.darkpool_otc - self.psi_q = lib.stocks_dps_quandl_model.get_short_interest - self.psi_q_chart = lib.stocks_dps_quandl_view.short_interest - self.psi_sg = lib.stocks_dps_stockgrid_model.get_short_interest_volume - self.psi_sg_chart = lib.stocks_dps_stockgrid_view.short_interest_volume - self.shorted = lib.stocks_dps_yahoofinance_model.get_most_shorted - self.sidtc = lib.stocks_dps_stockgrid_model.get_short_interest_days_to_cover - self.spos = lib.stocks_dps_stockgrid_model.get_net_short_position - self.spos_chart = lib.stocks_dps_stockgrid_view.net_short_position - - -class StocksFundamentalAnalysis(Category): - """Fundamental Analysis Module. - - Attributes: - `analysis`: Save time reading SEC filings with the help of machine learning. [Source: https://eclect.us]\n - `analyst`: Get analyst data. [Source: Finviz]\n - `balance`: Get balance sheet.\n - `cal`: Get calendar earnings for ticker symbol\n - `cash`: Get Cash Flow.\n - `customer`: Print customers from ticker provided\n - `dcf`: Get stocks dcf from FMP\n - `dcfc`: Get stocks dcf from FMP\n - `divs`: Get historical dividend for ticker\n - `divs_chart`: Display historical dividends\n - `dupont`: Get dupont ratios\n - `earnings`: Get earnings data.\n - `enterprise`: Financial Modeling Prep ticker enterprise\n - `epsfc`: Takes the ticker, asks for seekingalphaID and gets eps estimates\n - `est`: Get analysts' estimates for a given ticker. [Source: Business Insider]\n - `fama_coe`: Use Fama and French to get the cost of equity for a company\n - `fama_raw`: Get Fama French data\n - `fraud`: Get fraud ratios based on fundamentals\n - `growth`: Get financial statement growth\n - `historical_5`: Get 5 year monthly historical performance for a ticker with dividends filtered\n - `income`: Get income statement.\n - `key`: Get key metrics from overview\n - `metrics`: Get key metrics\n - `mgmt`: Get company managers from Business Insider\n - `mktcap`: Get market cap over time for ticker. [Source: Yahoo Finance]\n - `mktcap_chart`: Display market cap over time. [Source: Yahoo Finance]\n - `news`: Get news from Finviz\n - `overview`: Get overview.\n - `pt`: Get analysts' price targets for a given stock. [Source: Business Insider]\n - `pt_chart`: Display analysts' price targets for a given stock. [Source: Business Insider]\n - `rating`: Get ratings for a given ticker. [Source: Financial Modeling Prep]\n - `ratios`: Get key ratios\n - `revfc`: Takes the ticker, asks for seekingalphaID and gets rev estimates\n - `rot`: Get rating over time data. [Source: Finnhub]\n - `rot_chart`: Rating over time (monthly). [Source: Finnhub]\n - `score`: Gets value score from fmp\n - `sec`: Get SEC filings for a given stock ticker. [Source: Nasdaq]\n - `shrs`: Get shareholders from yahoo\n - `similar_dfs`: Get dataframes for similar companies\n - `splits`: Get splits and reverse splits events. [Source: Yahoo Finance]\n - `splits_chart`: Display splits and reverse splits events. [Source: Yahoo Finance]\n - `supplier`: Get suppliers from ticker provided. [Source: CSIMarket]\n - """ - - _location_path = "stocks.fa" - - def __init__(self): - super().__init__() - self.analysis = lib.stocks_fa_eclect_us_model.get_filings_analysis - self.analyst = lib.stocks_fa_finviz_model.get_analyst_data - self.balance = lib.stocks_fa_sdk_helpers.get_balance_sheet - self.cal = lib.stocks_fa_yahoo_finance_model.get_calendar_earnings - self.cash = lib.stocks_fa_sdk_helpers.get_cash_flow - self.customer = lib.stocks_fa_csimarket_model.get_customers - self.dcf = lib.stocks_fa_fmp_model.get_dcf - self.dcfc = lib.stocks_fa_fmp_model.get_dcf - self.divs = lib.stocks_fa_yahoo_finance_model.get_dividends - self.divs_chart = lib.stocks_fa_yahoo_finance_view.display_dividends - self.dupont = lib.stocks_fa_av_model.get_dupont - self.earnings = lib.stocks_fa_sdk_helpers.earnings - self.enterprise = lib.stocks_fa_fmp_model.get_enterprise - self.epsfc = lib.stocks_fa_seeking_alpha_model.get_estimates_eps - self.est = lib.stocks_fa_business_insider_model.get_estimates - self.fama_coe = lib.stocks_fa_dcf_model.get_fama_coe - self.fama_raw = lib.stocks_fa_dcf_model.get_fama_raw - self.fraud = lib.stocks_fa_av_model.get_fraud_ratios - self.growth = lib.stocks_fa_fmp_model.get_financial_growth - self.historical_5 = lib.stocks_fa_dcf_model.get_historical_5 - self.income = lib.stocks_fa_sdk_helpers.get_income_statement - self.key = lib.stocks_fa_av_model.get_key_metrics - self.metrics = lib.stocks_fa_fmp_model.get_key_metrics - self.mgmt = lib.stocks_fa_business_insider_model.get_management - self.mktcap = lib.stocks_fa_yahoo_finance_model.get_mktcap - self.mktcap_chart = lib.stocks_fa_yahoo_finance_view.display_mktcap - self.news = lib.stocks_fa_finviz_model.get_news - self.overview = lib.stocks_fa_sdk_helpers.get_overview - self.pt = lib.stocks_fa_business_insider_model.get_price_target_from_analysts - self.pt_chart = ( - lib.stocks_fa_business_insider_view.display_price_target_from_analysts - ) - self.rating = lib.stocks_fa_fmp_model.get_rating - self.ratios = lib.stocks_fa_fmp_model.get_key_ratios - self.revfc = lib.stocks_fa_seeking_alpha_model.get_estimates_rev - self.rot = lib.stocks_fa_finnhub_model.get_rating_over_time - self.rot_chart = lib.stocks_fa_finnhub_view.rating_over_time - self.score = lib.stocks_fa_fmp_model.get_score - self.sec = lib.stocks_fa_nasdaq_model.get_sec_filings - self.shrs = lib.stocks_fa_yahoo_finance_model.get_shareholders - self.similar_dfs = lib.stocks_fa_dcf_model.get_similar_dfs - self.splits = lib.stocks_fa_yahoo_finance_model.get_splits - self.splits_chart = lib.stocks_fa_yahoo_finance_view.display_splits - self.supplier = lib.stocks_fa_csimarket_model.get_suppliers - - -class StocksGovernment(Category): - """Government Module. - - Attributes: - `contracts`: Get government contracts for ticker [Source: quiverquant.com]\n - `contracts_chart`: Show government contracts for ticker [Source: quiverquant.com]\n - `government_trading`: Returns the most recent transactions by members of government\n - `gtrades`: Government trading for specific ticker [Source: quiverquant.com]\n - `gtrades_chart`: Government trading for specific ticker [Source: quiverquant.com]\n - `histcont`: Get historical quarterly government contracts [Source: quiverquant.com]\n - `histcont_chart`: Show historical quarterly government contracts [Source: quiverquant.com]\n - `lastcontracts`: Get last government contracts [Source: quiverquant.com]\n - `lastcontracts_chart`: Last government contracts [Source: quiverquant.com]\n - `lasttrades`: Get last government trading [Source: quiverquant.com]\n - `lobbying`: Corporate lobbying details\n - `qtrcontracts`: Analyzes quarterly contracts by ticker\n - `qtrcontracts_chart`: Quarterly contracts [Source: quiverquant.com]\n - `topbuys`: Get top buy government trading [Source: quiverquant.com]\n - `topbuys_chart`: Top buy government trading [Source: quiverquant.com]\n - `toplobbying`: Corporate lobbying details\n - `toplobbying_chart`: Top lobbying tickers based on total spent\n - `topsells`: Get top sell government trading [Source: quiverquant.com]\n - `topsells_chart`: Top sell government trading [Source: quiverquant.com]\n - """ - - _location_path = "stocks.gov" - - def __init__(self): - super().__init__() - self.contracts = lib.stocks_gov_quiverquant_model.get_contracts - self.contracts_chart = lib.stocks_gov_quiverquant_view.display_contracts - self.government_trading = ( - lib.stocks_gov_quiverquant_model.get_government_trading - ) - self.gtrades = lib.stocks_gov_quiverquant_model.get_cleaned_government_trading - self.gtrades_chart = lib.stocks_gov_quiverquant_view.display_government_trading - self.histcont = lib.stocks_gov_quiverquant_model.get_hist_contracts - self.histcont_chart = lib.stocks_gov_quiverquant_view.display_hist_contracts - self.lastcontracts = lib.stocks_gov_quiverquant_model.get_last_contracts - self.lastcontracts_chart = ( - lib.stocks_gov_quiverquant_view.display_last_contracts - ) - self.lasttrades = lib.stocks_gov_quiverquant_model.get_last_government - self.lobbying = lib.stocks_gov_quiverquant_model.get_lobbying - self.qtrcontracts = lib.stocks_gov_quiverquant_model.get_qtr_contracts - self.qtrcontracts_chart = lib.stocks_gov_quiverquant_view.display_qtr_contracts - self.topbuys = lib.stocks_gov_quiverquant_model.get_government_buys - self.topbuys_chart = lib.stocks_gov_quiverquant_view.display_government_buys - self.toplobbying = lib.stocks_gov_quiverquant_model.get_top_lobbying - self.toplobbying_chart = lib.stocks_gov_quiverquant_view.display_top_lobbying - self.topsells = lib.stocks_gov_quiverquant_model.get_government_sells - self.topsells_chart = lib.stocks_gov_quiverquant_view.display_government_sells - - -class StocksInsiders(Category): - """Insiders Module. - - Attributes: - `act`: Get insider activity. [Source: Business Insider]\n - `act_chart`: Display insider activity. [Source: Business Insider]\n - `blcp`: Get latest CEO/CFO purchases > 25k\n - `blcs`: Get latest CEO/CFO sales > 100k\n - `blip`: Get latest insider purchases > 25k\n - `blis`: Get latest insider sales > 100k\n - `blop`: Get latest officer purchases > 25k\n - `blos`: Get latest officer sales > 100k\n - `filter`: GEt insider trades based on preset filter\n - `lcb`: Get latest cluster buys\n - `lins`: Get last insider activity for a given stock ticker. [Source: Finviz]\n - `lins_chart`: Display insider activity for a given stock ticker. [Source: Finviz]\n - `lip`: Get latest insider purchases\n - `lis`: Get latest insider sales\n - `lit`: Get latest insider trades\n - `lpsb`: Get latest penny stock buys\n - `print_insider_data`: Print insider data\n - `print_insider_data_chart`: Print insider data\n - `stats`: Get OpenInsider stats for ticker\n - """ - - _location_path = "stocks.ins" - - def __init__(self): - super().__init__() - self.act = lib.stocks_insider_businessinsider_model.get_insider_activity - self.act_chart = lib.stocks_insider_businessinsider_view.insider_activity - self.blcp = lib.stocks_insider_sdk_helper.blcp - self.blcs = lib.stocks_insider_sdk_helper.blcs - self.blip = lib.stocks_insider_sdk_helper.blip - self.blis = lib.stocks_insider_sdk_helper.blis - self.blop = lib.stocks_insider_sdk_helper.blop - self.blos = lib.stocks_insider_sdk_helper.blos - self.filter = lib.stocks_insider_sdk_helper.insider_filter - self.lcb = lib.stocks_insider_sdk_helper.lcb - self.lins = lib.stocks_insider_finviz_model.get_last_insider_activity - self.lins_chart = lib.stocks_insider_finviz_view.last_insider_activity - self.lip = lib.stocks_insider_sdk_helper.lip - self.lis = lib.stocks_insider_sdk_helper.lis - self.lit = lib.stocks_insider_sdk_helper.lit - self.lpsb = lib.stocks_insider_sdk_helper.lpsb - self.print_insider_data = ( - lib.stocks_insider_openinsider_model.get_print_insider_data - ) - self.print_insider_data_chart = ( - lib.stocks_insider_openinsider_view.print_insider_data - ) - self.stats = lib.stocks_insider_sdk_helper.stats - - -class StocksOptions(Category): - """Options Module. - - Attributes: - `chains`: Get Option Chain For A Stock. No greek data is returned\n - `dte`: Returns a new column containing the DTE as an integer, including 0.\n - `eodchain`: Get full EOD option date across all expirations\n - `expirations`: Get Option Chain Expirations\n - `generate_data`: Gets x values, and y values before and after premiums\n - `get_strategies`: Gets options strategies for all, or a list of, DTE(s).\n - `greeks`: Gets the greeks for a given option\n - `grhist`: Get historical EOD option prices, with Greeks, for a given OCC chain label.\n - `grhist_chart`: Plots historical greeks for a given option.\n - `hist`: Get historical option pricing.\n - `info`: Scrape barchart for options info\n - `info_chart`: Scrapes Barchart.com for the options information\n - `last_price`: Makes api request for last price\n - `load_options_chains`: Loads all options chains from a specific source, fields returned to each attribute will vary.\n - `oi`: Plot open interest\n - `pcr`: Gets put call ratio over last time window [Source: AlphaQuery.com]\n - `pcr_chart`: Display put call ratio [Source: AlphaQuery.com]\n - `price`: Get Option current price for a stock.\n - `unu`: Get unusual option activity from fdscanner.com\n - `unu_chart`: Displays the unusual options table\n - `voi`: Plot volume and open interest\n - `vol`: Plot volume\n - `vsurf`: Gets IV surface for calls and puts for ticker\n - `vsurf_chart`: Display vol surface\n - """ - - _location_path = "stocks.options" - - def __init__(self): - super().__init__() - self.chains = lib.stocks_options_sdk_helper.get_full_option_chain - self.dte = lib.stocks_options_helpers.get_dte - self.eodchain = lib.stocks_options_intrinio_model.get_full_chain_eod - self.expirations = lib.stocks_options_sdk_helper.get_option_expirations - self.generate_data = lib.stocks_options_yfinance_model.generate_data - self.get_strategies = lib.stocks_options_options_chains_model.get_strategies - self.greeks = lib.stocks_options_sdk_helper.get_greeks - self.grhist = lib.stocks_options_intrinio_model.get_historical_options - self.grhist_chart = lib.stocks_options_intrinio_view.view_historical_greeks - self.hist = lib.stocks_options_sdk_helper.hist - self.info = lib.stocks_options_barchart_model.get_options_info - self.info_chart = lib.stocks_options_barchart_view.print_options_data - self.last_price = lib.stocks_options_tradier_model.get_last_price - self.load_options_chains = lib.stocks_options_sdk_helper.load_options_chains - self.oi = lib.stocks_options_view.plot_oi - self.pcr = lib.stocks_options_alphaquery_model.get_put_call_ratio - self.pcr_chart = lib.stocks_options_alphaquery_view.display_put_call_ratio - self.price = lib.stocks_options_sdk_helper.get_option_current_price - self.unu = lib.stocks_options_fdscanner_model.unusual_options - self.unu_chart = lib.stocks_options_fdscanner_view.display_options - self.voi = lib.stocks_options_view.plot_voi - self.vol = lib.stocks_options_view.plot_vol - self.vsurf = lib.stocks_options_yfinance_model.get_iv_surface - self.vsurf_chart = lib.stocks_options_yfinance_view.display_vol_surface - - -class StocksQuantitativeAnalysis(Category): - """Quantitative Analysis Module. - - Attributes: - `beta`: Calculate beta for a ticker and a reference ticker.\n - `beta_chart`: Display the beta scatterplot + linear regression.\n - `capm`: Provides information that relates to the CAPM model\n - `fama_raw`: Gets base Fama French data to calculate risk\n - `historical_5`: Get 5 year monthly historical performance for a ticker with dividends filtered\n - """ - - _location_path = "stocks.qa" - - def __init__(self): - super().__init__() - self.beta = lib.stocks_qa_beta_model.beta_model - self.beta_chart = lib.stocks_qa_beta_view.beta_view - self.capm = lib.stocks_qa_factors_model.capm_information - self.fama_raw = lib.stocks_qa_factors_model.get_fama_raw - self.historical_5 = lib.stocks_qa_factors_model.get_historical_5 - - -class StocksScreener(Category): - """Screener Module. - - Attributes: - `screener_data`: Screener Overview\n - `screener_data_chart`: Screener one of the following: overview, valuation, financial, ownership, performance, technical.\n - """ - - _location_path = "stocks.screener" - - def __init__(self): - super().__init__() - self.screener_data = lib.stocks_screener_finviz_model.get_screener_data - self.screener_data_chart = lib.stocks_screener_finviz_view.screener - - -class StocksTechnicalAnalysis(Category): - """Technical Analysis Module. - - Attributes: - `recom`: Get tradingview recommendation based on technical indicators\n - `recom_chart`: Print tradingview recommendation based on technical indicators\n - `summary`: Get technical summary report provided by FinBrain's API\n - `summary_chart`: Print technical summary report provided by FinBrain's API\n - """ - - _location_path = "stocks.ta" - - def __init__(self): - super().__init__() - self.recom = lib.stocks_ta_tradingview_model.get_tradingview_recommendation - self.recom_chart = lib.stocks_ta_tradingview_view.print_recommendation - self.summary = lib.stocks_ta_finbrain_model.get_technical_summary_report - self.summary_chart = lib.stocks_ta_finbrain_view.technical_summary_report - - -class StocksTradingHours(Category): - """Trading Hours Module. - - Attributes: - `all`: Get all exchanges.\n - `all_chart`: Display all exchanges.\n - `check_if_open`: Check if market open helper function\n - `closed`: Get closed exchanges.\n - `closed_chart`: Display closed exchanges.\n - `exchange`: Get current exchange open hours.\n - `exchange_chart`: Display current exchange trading hours.\n - `open`: Get open exchanges.\n - `open_chart`: Display open exchanges.\n - """ - - _location_path = "stocks.th" - - def __init__(self): - super().__init__() - self.all = lib.stocks_th_bursa_model.get_all - self.all_chart = lib.stocks_th_bursa_view.display_all - self.check_if_open = lib.stocks_th_bursa_model.check_if_open - self.closed = lib.stocks_th_bursa_model.get_closed - self.closed_chart = lib.stocks_th_bursa_view.display_closed - self.exchange = lib.stocks_th_bursa_model.get_bursa - self.exchange_chart = lib.stocks_th_bursa_view.display_exchange - self.open = lib.stocks_th_bursa_model.get_open - self.open_chart = lib.stocks_th_bursa_view.display_open diff --git a/openbb_terminal/core/sdk/models/ta_sdk_model.py b/openbb_terminal/core/sdk/models/ta_sdk_model.py deleted file mode 100644 index ac57eaff2e3..00000000000 --- a/openbb_terminal/core/sdk/models/ta_sdk_model.py +++ /dev/null @@ -1,125 +0,0 @@ -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # -# flake8: noqa -# pylint: disable=C0301,R0902,R0903 -from openbb_terminal.core.sdk.sdk_helpers import Category -import openbb_terminal.core.sdk.sdk_init as lib - - -class TaRoot(Category): - """Technical Analysis Module - - Attributes: - `ad`: Calculate AD technical indicator\n - `ad_chart`: Plots AD technical indicator\n - `adosc`: Calculate AD oscillator technical indicator\n - `adosc_chart`: Plots AD Osc Indicator\n - `adx`: ADX technical indicator\n - `adx_chart`: Plots ADX indicator\n - `aroon`: Aroon technical indicator\n - `aroon_chart`: Plots Aroon indicator\n - `atr`: Average True Range\n - `atr_chart`: Plots ATR\n - `bbands`: Calculate Bollinger Bands\n - `bbands_chart`: Plots bollinger bands\n - `cci`: Commodity channel index\n - `cci_chart`: Plots CCI Indicator\n - `cg`: Center of gravity\n - `cg_chart`: Plots center of gravity Indicator\n - `clenow`: Gets the Clenow Volatility Adjusted Momentum. this is defined as the regression coefficient on log prices\n - `clenow_chart`: Prints table and plots clenow momentum\n - `cones`: Returns a DataFrame of realized volatility quantiles.\n - `cones_chart`: Plots the realized volatility quantiles for the loaded ticker.\n - `demark`: Get the integer value for demark sequential indicator\n - `demark_chart`: Plot demark sequential indicator\n - `donchian`: Calculate Donchian Channels\n - `donchian_chart`: Plots donchian channels\n - `ema`: Gets exponential moving average (EMA) for stock\n - `fib`: Calculate Fibonacci levels\n - `fib_chart`: Plots Calculated fibonacci retracement levels\n - `fisher`: Fisher Transform\n - `fisher_chart`: Plots Fisher Indicator\n - `hma`: Gets hull moving average (HMA) for stock\n - `kc`: Keltner Channels\n - `kc_chart`: Plots Keltner Channels Indicator\n - `ma`: Plots MA technical indicator\n - `ma_chart`: Plots MA technical indicator\n - `macd`: Moving average convergence divergence\n - `macd_chart`: Plots MACD signal\n - `obv`: On Balance Volume\n - `obv_chart`: Plots OBV technical indicator\n - `rsi`: Relative strength index\n - `rsi_chart`: Plots RSI Indicator\n - `rvol_garman_klass`: Garman-Klass volatility extends Parkinson volatility by taking into account the opening and closing price.\n - `rvol_hodges_tompkins`: Hodges-Tompkins volatility is a bias correction for estimation using an overlapping data sample.\n - `rvol_parkinson`: Parkinson volatility uses the high and low price of the day rather than just close to close prices.\n - `rvol_rogers_satchell`: Rogers-Satchell is an estimator for measuring the volatility with an average return not equal to zero.\n - `rvol_std`: Standard deviation measures how widely returns are dispersed from the average return.\n - `rvol_yang_zhang`: Yang-Zhang volatility is the combination of the overnight (close-to-open volatility).\n - `sma`: Gets simple moving average (SMA) for stock\n - `standard_deviation`: Standard deviation measures how widely returns are dispersed from the average return.\n - `stoch`: Stochastic oscillator\n - `stoch_chart`: Plots stochastic oscillator signal\n - `vwap`: Gets volume weighted average price (VWAP)\n - `vwap_chart`: Plots VWMA technical indicator\n - `wma`: Gets weighted moving average (WMA) for stock\n - `zlma`: Gets zero-lagged exponential moving average (ZLEMA) for stock\n - """ - - _location_path = "ta" - - def __init__(self): - super().__init__() - self.ad = lib.common_ta_volume_model.ad - self.ad_chart = lib.common_ta_volume_view.display_ad - self.adosc = lib.common_ta_volume_model.adosc - self.adosc_chart = lib.common_ta_volume_view.display_adosc - self.adx = lib.common_ta_trend_indicators_model.adx - self.adx_chart = lib.common_ta_trend_indicators_view.display_adx - self.aroon = lib.common_ta_trend_indicators_model.aroon - self.aroon_chart = lib.common_ta_trend_indicators_view.display_aroon - self.atr = lib.common_ta_volatility_model.atr - self.atr_chart = lib.common_ta_volatility_view.display_atr - self.bbands = lib.common_ta_volatility_model.bbands - self.bbands_chart = lib.common_ta_volatility_view.display_bbands - self.cci = lib.common_ta_momentum_model.cci - self.cci_chart = lib.common_ta_momentum_view.display_cci - self.cg = lib.common_ta_momentum_model.cg - self.cg_chart = lib.common_ta_momentum_view.display_cg - self.clenow = lib.common_ta_momentum_model.clenow_momentum - self.clenow_chart = lib.common_ta_momentum_view.display_clenow_momentum - self.cones = lib.common_ta_volatility_model.cones - self.cones_chart = lib.common_ta_volatility_view.display_cones - self.demark = lib.common_ta_momentum_model.demark_seq - self.demark_chart = lib.common_ta_momentum_view.display_demark - self.donchian = lib.common_ta_volatility_model.donchian - self.donchian_chart = lib.common_ta_volatility_view.display_donchian - self.ema = lib.common_ta_overlap_model.ema - self.fib = lib.common_ta_custom_indicators_model.calculate_fib_levels - self.fib_chart = lib.common_ta_custom_indicators_view.fibonacci_retracement - self.fisher = lib.common_ta_momentum_model.fisher - self.fisher_chart = lib.common_ta_momentum_view.display_fisher - self.hma = lib.common_ta_overlap_model.hma - self.kc = lib.common_ta_volatility_model.kc - self.kc_chart = lib.common_ta_volatility_view.view_kc - self.ma = lib.common_ta_overlap_view.view_ma - self.ma_chart = lib.common_ta_overlap_view.view_ma - self.macd = lib.common_ta_momentum_model.macd - self.macd_chart = lib.common_ta_momentum_view.display_macd - self.obv = lib.common_ta_volume_model.obv - self.obv_chart = lib.common_ta_volume_view.display_obv - self.rsi = lib.common_ta_momentum_model.rsi - self.rsi_chart = lib.common_ta_momentum_view.display_rsi - self.rvol_garman_klass = lib.common_ta_volatility_model.garman_klass - self.rvol_hodges_tompkins = lib.common_ta_volatility_model.hodges_tompkins - self.rvol_parkinson = lib.common_ta_volatility_model.parkinson - self.rvol_rogers_satchell = lib.common_ta_volatility_model.rogers_satchell - self.rvol_std = lib.common_ta_volatility_model.standard_deviation - self.rvol_yang_zhang = lib.common_ta_volatility_model.yang_zhang - self.sma = lib.common_ta_overlap_model.sma - self.standard_deviation = lib.common_ta_volatility_model.standard_deviation - self.stoch = lib.common_ta_momentum_model.stoch - self.stoch_chart = lib.common_ta_momentum_view.display_stoch - self.vwap = lib.common_ta_overlap_model.vwap - self.vwap_chart = lib.common_ta_overlap_view.view_vwap - self.wma = lib.common_ta_overlap_model.wma - self.zlma = lib.common_ta_overlap_model.zlma diff --git a/openbb_terminal/core/sdk/sdk_helpers.py b/openbb_terminal/core/sdk/sdk_helpers.py deleted file mode 100644 index bc6ae8b3adb..00000000000 --- a/openbb_terminal/core/sdk/sdk_helpers.py +++ /dev/null @@ -1,689 +0,0 @@ -"""OpenBB Terminal SDK Helpers.""" - -import json -from inspect import signature -from logging import Logger, getLogger -from typing import Any, Callable, Dict, Optional - -from openbb_terminal.core.config.paths import SETTINGS_ENV_FILE -from openbb_terminal.core.sdk.sdk_init import ( - FORECASTING_TOOLKIT_ENABLED, - FORECASTING_TOOLKIT_WARNING, - OPTIMIZATION_TOOLKIT_ENABLED, - OPTIMIZATION_TOOLKIT_WARNING, -) -from openbb_terminal.core.session.current_system import ( - get_current_system, - set_system_variable, -) -from openbb_terminal.rich_config import console - -SETTINGS_ENV_FILE.parent.mkdir(parents=True, exist_ok=True) - - -if ( - not FORECASTING_TOOLKIT_ENABLED - and not get_current_system().DISABLE_FORECASTING_WARNING -): - set_system_variable("DISABLE_FORECASTING_WARNING", True) - - console.print(FORECASTING_TOOLKIT_WARNING) - - -if ( - not OPTIMIZATION_TOOLKIT_ENABLED - and not get_current_system().DISABLE_OPTIMIZATION_WARNING -): - set_system_variable("DISABLE_OPTIMIZATION_WARNING", True) - - console.print(OPTIMIZATION_TOOLKIT_WARNING) - - -def clean_attr_desc(attr: Optional[Any] = None) -> Optional[str]: - """Clean the attribute description.""" - - if attr.__doc__ is None: - return None - - return ( - attr.__doc__.splitlines()[1].lstrip() - if not attr.__doc__.splitlines()[0] - else attr.__doc__.splitlines()[0].lstrip() if attr.__doc__ else "" - ) - - -def class_repr(cls_dict: Dict[str, Any]) -> list: - """Return the representation of the class.""" - - return [ - f" {k}: {clean_attr_desc(v)}\n" - for k, v in cls_dict.items() - if v.__doc__ and not k.startswith("_") - ] - - -class Operation: - def __init__(self, name: str, trail: str, func: Callable) -> None: - self._trail = trail - - self._method = func - - self._name = name - - for attr in [ - "__doc__", - "__name__", - "__annotations__", - "__defaults__", - "__kwdefaults__", - "__module__", - ]: - setattr(self.__class__, attr, getattr(func, attr)) - - setattr(self, attr, getattr(func, attr)) - - self.__signature__ = signature(func) - - self.__class__.__signature__ = signature(func) - - def __call__(self, *args: Any, **kwargs: Any) -> Any: - method = self._method - - # We make a copy of the kwargs to avoid modifying the original - - log_kwargs = kwargs.copy() - - log_kwargs["chart"] = "chart" in self._name - - operation_logger = OperationLogger( - trail=self._trail, method_chosen=method, args=args, kwargs=log_kwargs - ) - - operation_logger.log_before_call() - - method_result = method(*args, **kwargs) - - operation_logger.log_after_call(method_result=method_result) - - return method_result - - def about(self): - # pylint: disable=C0415 - - import webbrowser - - trail = "/".join(self._trail.split(".")).replace("_chart", "") - - url = f"https://docs.openbb.co/sdk/reference/{trail}" - - webbrowser.open(url) - - -class Category: - """The base class that all categories must inherit from.""" - - _location_path: str = "" - - def __init__(self, *args, **kwargs): - """Initialize the class""" - - super().__init__(*args, **kwargs) - - def __repr__(self): - """Return the representation of the class.""" - - repr_docs = [] - - if submodules := class_repr(self.__class__.__dict__): - repr_docs += ["\nSubmodules:\n"] + submodules - - if attributes := class_repr(self.__dict__): - repr_docs += ["\nAttributes:\n"] + attributes - - return f"{self.__class__.__name__}(\n{''.join(repr_docs)}\n)" - - def __getattribute__(self, name: str): - """We override the __getattribute__ method and wrap all callable - - - - attributes with a wrapper that logs the call and the result. - - - - """ - - attr = super().__getattribute__(name) - - if isinstance(attr, Operation) or name.startswith("__"): - return attr - - trail = f"{self.__class__._location_path}.{name}" - - if callable(attr) and not isinstance(attr, Operation): - # We set the attribute to the wrapped function so that we don't - - # have to wrap it when called again. - - setattr(self, name, Operation(name=name, trail=trail, func=attr)) - - return getattr(self, name) - - return attr - - def about(self): - # pylint: disable=C0415 - - import webbrowser - - trail = "/".join(self._location_path.split(".")) - - url = f"https://docs.openbb.co/sdk/reference/{trail}" - - webbrowser.open(url) - - -class OperationLogger: - last_method: Dict[Any, Any] = {} - - def __init__( - self, - trail: str, - method_chosen: Callable, - args: Any, - kwargs: Any, - logger: Optional[Logger] = None, - ) -> None: - self.__trail = trail - - self.__method_chosen = method_chosen - - self.__logger = logger or getLogger(self.__method_chosen.__module__) - - self.__args = args - - self.__kwargs = kwargs - - def log_before_call( - self, - ): - if self.__check_logging_conditions(): - logger = self.__logger - - self.__log_start(logger=logger, method_chosen=self.__method_chosen) - - self.__log_method_info( - logger=logger, - trail=self.__trail, - method_chosen=self.__method_chosen, - args=self.__args, - kwargs=self.__kwargs, - ) - - @staticmethod - def __log_start(logger: Logger, method_chosen: Callable): - logger.info( - "START", - extra={"func_name_override": method_chosen.__name__}, - ) - - def __log_method_info( - self, - logger: Logger, - trail: str, - method_chosen: Callable, - args: Any, - kwargs: Any, - ): - merged_args = self.__merge_function_args(method_chosen, args, kwargs) - - merged_args = self.__remove_key_and_log_state( - method_chosen.__module__, merged_args - ) - - logging_info: Dict[str, Any] = {} - - logging_info["INPUT"] = { - key: str(value)[:100] for key, value in merged_args.items() - } - - logging_info["VIRTUAL_PATH"] = trail - - logging_info["CHART"] = kwargs.get("chart", False) - - logger.info( - f"{json.dumps(logging_info)}", - extra={"func_name_override": method_chosen.__name__}, - ) - - @staticmethod - def __merge_function_args(func: Callable, args: tuple, kwargs: dict) -> dict: - """ - - - - Merge user input args and kwargs with signature defaults into a dictionary. - - - - - - - - Parameters - - - - ---------- - - - - func : Callable - - - - Function to get the args from - - - - args : tuple - - - - Positional args - - - - kwargs : dict - - - - Keyword args - - - - - - - - Returns - - - - ------- - - - - dict - - - - Merged user args and signature defaults - - - - """ - - import inspect # pylint: disable=C0415 - - sig = inspect.signature(func) - - sig_args = { - param.name: param.default - for param in sig.parameters.values() - if param.default is not inspect.Parameter.empty - } - - # merge args with sig_args - - sig_args.update(dict(zip(sig.parameters, args))) - - # add kwargs elements to sig_args - - sig_args.update(kwargs) - - return sig_args - - @staticmethod - def __remove_key_and_log_state(func_module: str, function_args: dict) -> dict: - """ - - - - Remove API key from the function args and log state of keys. - - - - - - - - Parameters - - - - ---------- - - - - func_module : str - - - - Module of the function - - - - function_args : dict - - - - Function args - - - - - - - - Returns - - - - ------- - - - - dict - - - - Function args with API key removed - - - - """ - - if func_module == "openbb_terminal.keys_model": - # pylint: disable=C0415 - - # from openbb_terminal.core.log.generation.settings_logger import ( - - # log_credentials, - - # ) - - # remove key if defined - - function_args.pop("key", None) - - # log_credentials() - - return function_args - - def log_after_call( - self, - method_result: Any, - ): - if self.__check_logging_conditions(): - logger = self.__logger - - self.__log_exception_if_any( - logger=logger, - method_result=method_result, - method_chosen=self.__method_chosen, - ) - - self.__log_end( - logger=logger, - method_chosen=self.__method_chosen, - ) - - OperationLogger.last_method = { - f"{self.__method_chosen.__module__}.{self.__method_chosen.__name__}": { - "args": str(self.__args)[:100], - "kwargs": str(self.__kwargs)[:100], - } - } - - @staticmethod - def __log_exception_if_any( - logger: Logger, - method_chosen: Callable, - method_result: Any, - ): - if isinstance(method_result, Exception): - logger.exception( - f"Exception: {method_result}", - extra={"func_name_override": method_chosen.__name__}, - ) - - @staticmethod - def __log_end(logger: Logger, method_chosen: Callable): - logger.info( - "END", - extra={"func_name_override": method_chosen.__name__}, - ) - - def __check_logging_conditions(self) -> bool: - return ( - not get_current_system().LOGGING_SUPPRESS and not self.__check_last_method() - ) - - def __check_last_method(self) -> bool: - current_method = { - f"{self.__method_chosen.__module__}.{self.__method_chosen.__name__}": { - "args": str(self.__args)[:100], - "kwargs": str(self.__kwargs)[:100], - } - } - - return OperationLogger.last_method == current_method - - -def get_sdk_imports_text() -> str: - """Return the text for the SDK imports.""" - - sdk_imports = """\"\"\"OpenBB Terminal SDK.\"\"\" - - - - - - - -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # - - - - - - - -# flake8: noqa - - - - - - - -# pylint: disable=unused-import,wrong-import-order - - - - - - - -# pylint: disable=C0302,W0611,R0902,R0903,C0412,C0301,not-callable - - - - - - - -import logging - - - - - - - -import openbb_terminal.config_terminal as cfg - - - - - - - -from openbb_terminal import helper_funcs as helper # noqa: F401 - - - - - - - -from openbb_terminal.core.plots.plotly_helper import theme # noqa: F401 - - - - - - - -from openbb_terminal.cryptocurrency.due_diligence.pycoingecko_model import Coin - - - - - - - -from openbb_terminal.dashboards.dashboards_controller import DashboardsController - - - - - - - -from openbb_terminal.helper_classes import TerminalStyle # noqa: F401 - - - - - - - -from openbb_terminal.reports import widget_helpers as widgets # noqa: F401 - - - - - - - -from openbb_terminal.reports.reports_controller import ReportController - - - - - - - -import openbb_terminal.core.sdk.sdk_init as lib - - - - - - - -from openbb_terminal.core.sdk import ( - - - - controllers as ctrl, - - - - - - - - models as model, - - - -) - - - - - - - -from openbb_terminal.core.session.current_system import get_current_system - - - - - - - -from openbb_terminal.core.session.current_user import is_local - - - - - - - -from openbb_terminal.terminal_helper import is_auth_enabled - - - - - - - -cfg.setup_config_terminal(is_sdk=True) - - - - - - - -logger = logging.getLogger(__name__) - - - - - - - -cfg.theme.applyMPLstyle() - - - - - - - -\r\r\r - - - - - - - -""" - - return "\r".join(sdk_imports.splitlines()) diff --git a/openbb_terminal/core/sdk/sdk_init.py b/openbb_terminal/core/sdk/sdk_init.py deleted file mode 100644 index 4d90dcaa5e9..00000000000 --- a/openbb_terminal/core/sdk/sdk_init.py +++ /dev/null @@ -1,505 +0,0 @@ -# flake8: pylint: disable=R0402,C0412,unused-import -# noqa: F401 - -# Alternative -import openbb_terminal.alternative.companieshouse.companieshouse_model as alt_companieshouse_companieshouse_model -import openbb_terminal.alternative.companieshouse.companieshouse_view as alt_companieshouse_companieshouse_view -import openbb_terminal.alternative.hackernews_model as alt_hackernews_model -import openbb_terminal.alternative.hackernews_view as alt_hackernews_view -import openbb_terminal.alternative.oss.github_model as alt_oss_github_model -import openbb_terminal.alternative.oss.github_view as alt_oss_github_view -import openbb_terminal.alternative.oss.runa_model as alt_oss_runa_model -import openbb_terminal.alternative.oss.runa_view as alt_oss_runa_view -import openbb_terminal.alternative.realestate.landRegistry_model as alt_realestate_landRegistry_model - -# Crypto Helpers -import openbb_terminal.cryptocurrency.cryptocurrency_helpers as crypto_helpers - -# ETF -import openbb_terminal.etf.discovery.wsj_model as etf_disc_wsj_model -import openbb_terminal.etf.discovery.wsj_view as etf_disc_wsj_view -import openbb_terminal.stocks.quantitative_analysis.beta_model as stocks_qa_beta_model -import openbb_terminal.stocks.quantitative_analysis.beta_view as stocks_qa_beta_view - -# Stocks - Quantitative Analysis -import openbb_terminal.stocks.quantitative_analysis.factors_model as stocks_qa_factors_model -import openbb_terminal.stocks.quantitative_analysis.factors_view as stocks_qa_factors_view - -# Keys -from openbb_terminal import keys_model - -# Common -from openbb_terminal.common import ( - common_model, - feedparser_model as common_feedparser_model, - feedparser_view as common_feedparser_view, - news_sdk_helper as common_news_sdk_helper, - newsapi_model as common_newsapi_model, - newsapi_view as common_newsapi_view, -) - -# Common Behavioural Analysis -from openbb_terminal.common.behavioural_analysis import ( - finbrain_model as stocks_ba_finbrain_model, - finbrain_view as stocks_ba_finbrain_view, - google_model as stocks_ba_google_model, - google_view as stocks_ba_google_view, - reddit_model as stocks_ba_reddit_model, - reddit_view as stocks_ba_reddit_view, - stocktwits_model as stocks_ba_stocktwits_model, - stocktwits_view as stocks_ba_stocktwits_view, -) - -# Common Quantitative Analysis -from openbb_terminal.common.quantitative_analysis import ( - qa_model as common_qa_model, - qa_view as common_qa_view, - rolling_model as common_qa_rolling_model, - rolling_view as common_qa_rolling_view, -) - -# Common Technical Analysis -from openbb_terminal.common.technical_analysis import ( - custom_indicators_model as common_ta_custom_indicators_model, - custom_indicators_view as common_ta_custom_indicators_view, - momentum_model as common_ta_momentum_model, - momentum_view as common_ta_momentum_view, - overlap_model as common_ta_overlap_model, - overlap_view as common_ta_overlap_view, - trend_indicators_model as common_ta_trend_indicators_model, - trend_indicators_view as common_ta_trend_indicators_view, - volatility_model as common_ta_volatility_model, - volatility_view as common_ta_volatility_view, - volume_model as common_ta_volume_model, - volume_view as common_ta_volume_view, -) -from openbb_terminal.core.session import sdk_session -from openbb_terminal.cryptocurrency import ( - crypto_models, - pyth_view as crypto_pyth_view, -) - -# Cryptocurrency Defi -from openbb_terminal.cryptocurrency.defi import ( - coindix_model as crypto_defi_coindix_model, - coindix_view as crypto_defi_coindix_view, - cryptosaurio_model as crypto_defi_cryptosaurio_model, - cryptosaurio_view as crypto_defi_cryptosaurio_view, - llama_model as crypto_defi_llama_model, - llama_view as crypto_defi_llama_view, - smartstake_model as crypto_defi_smartstake_model, - smartstake_view as crypto_defi_smartstake_view, - substack_model as crypto_defi_substack_model, - substack_view as crypto_defi_substack_view, - terraengineer_model as crypto_defi_terraengineer_model, - terraengineer_view as crypto_defi_terraengineer_view, - terramoney_fcd_model as crypto_defi_terramoney_fcd_model, - terramoney_fcd_view as crypto_defi_terramoney_fcd_view, -) - -# Cryptocurrency Discovery -from openbb_terminal.cryptocurrency.discovery import ( - coinmarketcap_model as crypto_disc_coinmarketcap_model, - coinmarketcap_view as crypto_disc_coinmarketcap_view, - coinpaprika_model as crypto_disc_coinpaprika_model, - coinpaprika_view as crypto_disc_coinpaprika_view, - cryptostats_model as crypto_disc_cryptostats_model, - cryptostats_view as crypto_disc_cryptostats_view, - dappradar_model as crypto_disc_dappradar_model, - dappradar_view as crypto_disc_dappradar_view, - pycoingecko_model as crypto_disc_pycoingecko_model, - pycoingecko_view as crypto_disc_pycoingecko_view, - sdk_helpers as crypto_disc_sdk_helpers, -) - -# Cryptocurrency Due Diligence -from openbb_terminal.cryptocurrency.due_diligence import ( - binance_model as crypto_dd_binance_model, - binance_view as crypto_dd_binance_view, - ccxt_model as crypto_dd_ccxt_model, - ccxt_view as crypto_dd_ccxt_view, - coinbase_model as crypto_dd_coinbase_model, - coinbase_view as crypto_dd_coinbase_view, - coinglass_model as crypto_dd_coinglass_model, - coinglass_view as crypto_dd_coinglass_view, - coinpaprika_model as crypto_dd_coinpaprika_model, - coinpaprika_view as crypto_dd_coinpaprika_view, - cryptopanic_view as crypto_dd_cryptopanic_view, - finbrain_crypto_view as crypto_dd_finbrain_view, - glassnode_model as crypto_dd_glassnode_model, - glassnode_view as crypto_dd_glassnode_view, - messari_model as crypto_dd_messari_model, - messari_view as crypto_dd_messari_view, - pycoingecko_model as crypto_dd_pycoingecko_model, - pycoingecko_view as crypto_dd_pycoingecko_view, - santiment_model as crypto_dd_santiment_model, - santiment_view as crypto_dd_santiment_view, - sdk_helper as crypto_dd_sdk_helper, -) - -# Cryptocurrency NFT -from openbb_terminal.cryptocurrency.nft import ( - nftpricefloor_model as crypto_nft_pricefloor_model, - nftpricefloor_view as crypto_nft_pricefloor_view, - opensea_model as crypto_nft_opensea_model, - opensea_view as crypto_nft_opensea_view, -) - -# Cryptocurrency Onchain -from openbb_terminal.cryptocurrency.onchain import ( - bitquery_model as crypto_onchain_bitquery_model, - bitquery_view as crypto_onchain_bitquery_view, - blockchain_model as crypto_onchain_blockchain_model, - blockchain_view as crypto_onchain_blockchain_view, - ethgasstation_model as crypto_onchain_ethgasstation_model, - ethgasstation_view as crypto_onchain_ethgasstation_view, - ethplorer_model as crypto_onchain_ethplorer_model, - ethplorer_view as crypto_onchain_ethplorer_view, - topledger_model as crypto_onchain_topledger_model, - topledger_view as crypto_onchain_topledger_view, - whale_alert_model as crypto_onchain_whale_alert_model, - whale_alert_view as crypto_onchain_whale_alert_view, -) - -# Cryptocurrency Overview -from openbb_terminal.cryptocurrency.overview import ( - blockchaincenter_model as crypto_ov_blockchaincenter_model, - blockchaincenter_view as crypto_ov_blockchaincenter_view, - coinbase_model as crypto_ov_coinbase_model, - coinbase_view as crypto_ov_coinbase_view, - coinpaprika_model as crypto_ov_coinpaprika_model, - coinpaprika_view as crypto_ov_coinpaprika_view, - cryptopanic_model as crypto_ov_cryptopanic_model, - cryptopanic_view as crypto_ov_cryptopanic_view, - glassnode_model as crypto_ov_glassnode_model, - glassnode_view as crypto_ov_glassnode_view, - loanscan_model as crypto_ov_loanscan_model, - loanscan_view as crypto_ov_loanscan_view, - pycoingecko_model as crypto_ov_pycoingecko_model, - pycoingecko_view as crypto_ov_pycoingecko_view, - rekt_model as crypto_ov_rekt_model, - rekt_view as crypto_ov_rekt_view, - sdk_helpers as crypto_ov_sdk_helpers, - withdrawalfees_model as crypto_ov_withdrawalfees_model, - withdrawalfees_view as crypto_ov_withdrawalfees_view, -) - -# Cryptocurrency Tools -from openbb_terminal.cryptocurrency.tools import ( - tools_model as crypto_tools_model, - tools_view as crypto_tools_view, -) - -# Econometrics -from openbb_terminal.econometrics import ( - econometrics_model, - econometrics_view, - regression_model as econometrics_regression_model, - regression_view as econometrics_regression_view, -) - -# Economy -from openbb_terminal.economy import ( - econdb_model as economy_econdb_model, - econdb_view as economy_econdb_view, - fedreserve_model as economy_fedreserve_model, - fedreserve_view as economy_fedreserve_view, - finviz_model as economy_finviz_model, - finviz_view as economy_finviz_view, - fred_model as economy_fred_model, - fred_view as economy_fred_view, - nasdaq_model as economy_nasdaq_model, - nasdaq_view as economy_nasdaq_view, - oecd_model as economy_oecd_model, - oecd_view as economy_oecd_view, - sdk_helpers as economy_sdk_helpers, - wsj_model as economy_wsj_model, - yfinance_model as economy_yfinance_model, - yfinance_view as economy_yfinance_view, -) - -# ETF's -from openbb_terminal.etf import ( - financedatabase_model as etf_financedatabase_model, - financedatabase_view as etf_financedatabase_view, - fmp_model as etf_fmp_model, - fmp_view as etf_fmp_view, - stockanalysis_model as etf_stockanalysis_model, - stockanalysis_view as etf_stockanalysis_view, -) - -# Fixedincome -from openbb_terminal.fixedincome import ( - ecb_model as fixedincome_ecb_model, - fred_model as fixedincome_fred_model, - fred_view as fixedincome_fred_view, - oecd_model as fixedincome_oecd_model, -) - -# Forex Helpers -# Forex -from openbb_terminal.forex import ( - av_model as forex_av_model, - av_view as forex_av_view, - forex_helper, - fxempire_model as forex_fxempire_model, - fxempire_view as forex_fxempire_view, - sdk_helpers as forex_sdk_helpers, -) - -# Futures -from openbb_terminal.futures import ( - sdk_helper as futures_sdk_model, - yfinance_model as futures_yfinance_model, - yfinance_view as futures_yfinance_view, -) - -# Funds -from openbb_terminal.mutual_funds import ( - mstarpy_model as funds_mstarpy_model, - mstarpy_view as funds_mstarpy_view, -) - -# Stocks Helpers -from openbb_terminal.stocks import ( - cboe_model as stocks_cboe_model, - cboe_view as stocks_cboe_view, - stocks_helper, - stocks_model, - stocks_view, -) - -# Stocks -Behavioral Analysis -from openbb_terminal.stocks.behavioural_analysis import ( - finnhub_model as stocks_ba_finnhub_model, - finnhub_view as stocks_ba_finnhub_view, -) - -# Stocks - Comparison Analysis -from openbb_terminal.stocks.comparison_analysis import ( - finbrain_model as stocks_ca_finbrain_model, - finbrain_view as stocks_ca_finbrain_view, - finnhub_model as stocks_ca_finnhub_model, - finviz_compare_model as stocks_ca_finviz_compare_model, - marketwatch_model as stocks_ca_marketwatch_model, - marketwatch_view as stocks_ca_marketwatch_view, - polygon_model as stocks_ca_polygon_model, - sdk_helpers as stocks_ca_sdk_helpers, - yahoo_finance_model as stocks_ca_yahoo_finance_model, - yahoo_finance_view as stocks_ca_yahoo_finance_view, -) - -# Stocks - Dark Pool Shorts -from openbb_terminal.stocks.dark_pool_shorts import ( - finra_model as stocks_dps_finra_model, - finra_view as stocks_dps_finra_view, - quandl_model as stocks_dps_quandl_model, - quandl_view as stocks_dps_quandl_view, - sec_model as stocks_dps_sec_model, - sec_view as stocks_dps_sec_view, - shortinterest_model as stocks_dps_shortinterest_model, - stockgrid_model as stocks_dps_stockgrid_model, - stockgrid_view as stocks_dps_stockgrid_view, - yahoofinance_model as stocks_dps_yahoofinance_model, -) - -# Stocks - Fundamental Discovery -from openbb_terminal.stocks.discovery import ( - ark_model as stocks_disc_ark_model, - finnhub_model as stocks_disc_finnhub_model, - fmp_view as stocks_disc_fmp_view, - nasdaq_model as stocks_disc_nasdaq_model, - seeking_alpha_model as stocks_disc_seeking_alpha_model, - shortinterest_model as stocks_disc_shortinterest_model, - yahoofinance_model as stocks_disc_yahoofinance_model, -) - -# Stocks - Fundamental Analysis -from openbb_terminal.stocks.fundamental_analysis import ( - av_model as stocks_fa_av_model, - av_view as stocks_fa_av_view, - business_insider_model as stocks_fa_business_insider_model, - business_insider_view as stocks_fa_business_insider_view, - csimarket_model as stocks_fa_csimarket_model, - csimarket_view as stocks_fa_csimarket_view, - dcf_model as stocks_fa_dcf_model, - eclect_us_model as stocks_fa_eclect_us_model, - finnhub_model as stocks_fa_finnhub_model, - finnhub_view as stocks_fa_finnhub_view, - finviz_model as stocks_fa_finviz_model, - fmp_model as stocks_fa_fmp_model, - fmp_view as stocks_fa_fmp_view, - marketwatch_model as stocks_fa_marketwatch_model, - marketwatch_view as stocks_fa_marketwatch_view, - nasdaq_model as stocks_fa_nasdaq_model, - nasdaq_view as stocks_fa_nasdaq_view, - polygon_model as stocks_fa_polygon_model, - polygon_view as stocks_fa_polygon_view, - sdk_helpers as stocks_fa_sdk_helpers, - seeking_alpha_model as stocks_fa_seeking_alpha_model, - seeking_alpha_view as stocks_fa_seeking_alpha_view, - yahoo_finance_model as stocks_fa_yahoo_finance_model, - yahoo_finance_view as stocks_fa_yahoo_finance_view, -) - -# Government -from openbb_terminal.stocks.government import ( - quiverquant_model as stocks_gov_quiverquant_model, - quiverquant_view as stocks_gov_quiverquant_view, -) - -# Stocks - Insider Trading -from openbb_terminal.stocks.insider import ( - businessinsider_model as stocks_insider_businessinsider_model, - businessinsider_view as stocks_insider_businessinsider_view, - finviz_model as stocks_insider_finviz_model, - finviz_view as stocks_insider_finviz_view, - openinsider_model as stocks_insider_openinsider_model, - openinsider_view as stocks_insider_openinsider_view, - sdk_helper as stocks_insider_sdk_helper, -) - -# Stocks - Options -from openbb_terminal.stocks.options import ( - alphaquery_model as stocks_options_alphaquery_model, - alphaquery_view as stocks_options_alphaquery_view, - barchart_model as stocks_options_barchart_model, - barchart_view as stocks_options_barchart_view, - cboe_model as stocks_options_cboe_model, - chartexchange_model as stocks_options_chartexchange_model, - chartexchange_view as stocks_options_chartexchange_view, - fdscanner_model as stocks_options_fdscanner_model, - fdscanner_view as stocks_options_fdscanner_view, - intrinio_model as stocks_options_intrinio_model, - intrinio_view as stocks_options_intrinio_view, - nasdaq_model as stocks_options_nasdaq_model, - op_helpers as stocks_options_helpers, - options_chains_model as stocks_options_options_chains_model, - options_sdk_helper as stocks_options_sdk_helper, - options_view as stocks_options_view, - tradier_model as stocks_options_tradier_model, - tradier_view as stocks_options_tradier_view, - yfinance_model as stocks_options_yfinance_model, - yfinance_view as stocks_options_yfinance_view, -) -from openbb_terminal.stocks.screener import ( - finviz_model as stocks_screener_finviz_model, - finviz_view as stocks_screener_finviz_view, -) - -# Stocks - Technical Analysis -from openbb_terminal.stocks.technical_analysis import ( - finbrain_model as stocks_ta_finbrain_model, - finbrain_view as stocks_ta_finbrain_view, - tradingview_model as stocks_ta_tradingview_model, - tradingview_view as stocks_ta_tradingview_view, -) - -# Stocks - Trading Hours -from openbb_terminal.stocks.tradinghours import ( - bursa_model as stocks_th_bursa_model, - bursa_view as stocks_th_bursa_view, -) - -# Forecast Extras - - -try: - import darts # pyright: reportMissingImports=false - - # If you just import darts this will pass during pip install, this creates - # Failures later on, also importing utils ensures that darts is installed correctly - from darts import utils - - FORECASTING_TOOLKIT_ENABLED = True - - from openbb_terminal.forecast import ( - anom_model as forecast_anom_model, - anom_view as forecast_anom_view, - autoarima_model as forecast_autoarima_model, - autoarima_view as forecast_autoarima_view, - autoces_model as forecast_autoces_model, - autoces_view as forecast_autoces_view, - autoets_model as forecast_autoets_model, - autoets_view as forecast_autoets_view, - autoselect_model as forecast_autoselect_model, - autoselect_view as forecast_autoselect_view, - brnn_model as forecast_brnn_model, - brnn_view as forecast_brnn_view, - expo_model as forecast_expo_model, - expo_view as forecast_expo_view, - forecast_model, - forecast_view, - linregr_model as forecast_linregr_model, - linregr_view as forecast_linregr_view, - mstl_model as forecast_mstl_model, - mstl_view as forecast_mstl_view, - nbeats_model as forecast_nbeats_model, - nbeats_view as forecast_nbeats_view, - nhits_model as forecast_nhits_model, - nhits_view as forecast_nhits_view, - regr_model as forecast_regr_model, - regr_view as forecast_regr_view, - rnn_model as forecast_rnn_model, - rnn_view as forecast_rnn_view, - rwd_model as forecast_rwd_model, - rwd_view as forecast_rwd_view, - seasonalnaive_model as forecast_seasonalnaive_model, - seasonalnaive_view as forecast_seasonalnaive_view, - tcn_model as forecast_tcn_model, - tcn_view as forecast_tcn_view, - tft_model as forecast_tft_model, - tft_view as forecast_tft_view, - theta_model as forecast_theta_model, - theta_view as forecast_theta_view, - trans_model as forecast_trans_model, - trans_view as forecast_trans_view, - whisper_model as forecast_whisper_model, - ) - - -except ImportError: - FORECASTING_TOOLKIT_ENABLED = False - - -# Portfolio - - -from openbb_terminal.portfolio import portfolio_model, portfolio_view - -try: - # pylint: disable=W0611 # noqa: F401 # pyright: reportMissingImports=false - - from openbb_terminal.portfolio.portfolio_optimization import ( - excel_model as portfolio_optimization_excel_model, - optimizer_model as portfolio_optimization_optimizer_model, - optimizer_view as portfolio_optimization_optimizer_view, - po_model as portfolio_optimization_po_model, - po_view as portfolio_optimization_po_view, - ) - - OPTIMIZATION_TOOLKIT_ENABLED = True - - -except ModuleNotFoundError: - OPTIMIZATION_TOOLKIT_ENABLED = False - - -FORECASTING_TOOLKIT_WARNING = ( - "[yellow]" - "Forecasting Toolkit is disabled. " - "To use the Forecasting features please install the toolkit following the " - "instructions here: https://my.openbb.co/app/sdk/installation" - "\n" - "[/yellow]" -) - - -OPTIMIZATION_TOOLKIT_WARNING = ( - "[yellow]" - "Portfolio Optimization Toolkit is disabled. " - "To use the Optimization features please install the toolkit following the " - "instructions here: https://my.openbb.co/app/sdk/installation" - "\n" - "[/yellow]" -) diff --git a/openbb_terminal/core/sdk/trail_map.csv b/openbb_terminal/core/sdk/trail_map.csv deleted file mode 100644 index b280bb990e5..00000000000 --- a/openbb_terminal/core/sdk/trail_map.csv +++ /dev/null @@ -1,530 +0,0 @@ -trail,model,view -alt.hn,alt_hackernews_model.get_stories,alt_hackernews_view.display_stories -alt.oss._make_request,alt_oss_runa_model._make_request, -alt.oss._retry_session,alt_oss_runa_model._retry_session, -alt.oss.github_data,alt_oss_github_model.get_github_data, -alt.oss.history,alt_oss_github_model.get_stars_history,alt_oss_github_view.display_star_history -alt.oss.ross,alt_oss_runa_model.get_startups,alt_oss_runa_view.display_rossindex -alt.oss.search,alt_oss_github_model.search_repos, -alt.oss.summary,alt_oss_github_model.get_repo_summary,alt_oss_github_view.display_repo_summary -alt.oss.top,alt_oss_github_model.get_top_repos,alt_oss_github_view.display_top_repos -alt.realestate.get_estate_sales,alt_realestate_landRegistry_model.get_estate_sales, -alt.realestate.get_region_stats,alt_realestate_landRegistry_model.get_region_stats, -alt.realestate.get_towns_sold_prices,alt_realestate_landRegistry_model.get_towns_sold_prices, -alt.companieshouse.get_search_results,alt_companieshouse_companieshouse_model.get_search_results, -alt.companieshouse.get_company_info,alt_companieshouse_companieshouse_model.get_company_info, -alt.companieshouse.get_officers,alt_companieshouse_companieshouse_model.get_officers, -alt.companieshouse.get_persons_with_significant_control,alt_companieshouse_companieshouse_model.get_persons_with_significant_control, -alt.companieshouse.get_filings,alt_companieshouse_companieshouse_model.get_filings, -alt.companieshouse.get_filing_document,alt_companieshouse_companieshouse_model.get_filing_document, -alt.companieshouse.download_filing_document,alt_companieshouse_companieshouse_view.download_filing_document, -alt.companieshouse.get_charges,alt_companieshouse_companieshouse_view.display_charges -crypto.candle,crypto_helpers.plot_candles, -crypto.chart,crypto_helpers.plot_chart, -crypto.dd.all_binance_trading_pairs,crypto_dd_binance_model.get_all_binance_trading_pairs, -crypto.dd.ath,crypto_dd_sdk_helper.ath, -crypto.dd.atl,crypto_dd_sdk_helper.atl, -crypto.dd.balance,crypto_dd_binance_view.get_balance,crypto_dd_binance_view.display_balance -crypto.dd.basic,crypto_dd_coinpaprika_model.basic_coin_info,crypto_dd_coinpaprika_view.display_basic -crypto.dd.binance_available_quotes_for_each_coin,crypto_dd_binance_model.get_binance_available_quotes_for_each_coin, -crypto.dd.candle,crypto_dd_coinbase_model.get_candles, -crypto.dd.check_valid_binance_str,crypto_dd_binance_model.check_valid_binance_str, -crypto.dd.coin,crypto_dd_coinpaprika_model.get_coin, -crypto.dd.coin_market_chart,crypto_dd_pycoingecko_model.get_coin_market_chart, -crypto.dd.dev,crypto_dd_sdk_helper.dev_stats, -crypto.dd.events,crypto_dd_coinpaprika_model.get_coin_events_by_id,crypto_dd_coinpaprika_view.display_events -crypto.dd.ex,crypto_dd_coinpaprika_model.get_coin_exchanges_by_id,crypto_dd_coinpaprika_view.display_exchanges -crypto.dd.exchanges,crypto_dd_ccxt_model.get_exchanges, -crypto.dd.fr,crypto_dd_messari_model.get_fundraising,crypto_dd_messari_view.display_fundraising -crypto.dd.get_mt,crypto_dd_messari_model.get_available_timeseries,crypto_dd_messari_view.display_messari_timeseries_list -crypto.dd.gov,crypto_dd_messari_model.get_governance,crypto_dd_messari_view.display_governance -crypto.dd.headlines,stocks_ba_finbrain_model.get_sentiment,crypto_dd_finbrain_view.display_crypto_sentiment_analysis -crypto.dd.inv,crypto_dd_messari_model.get_investors,crypto_dd_messari_view.display_investors -crypto.dd.links,crypto_dd_messari_model.get_links,crypto_dd_messari_view.display_links -crypto.dd.mcapdom,crypto_dd_messari_model.get_marketcap_dominance,crypto_dd_messari_view.display_marketcap_dominance -crypto.dd.mkt,crypto_dd_coinpaprika_model.get_coin_markets_by_id,crypto_dd_coinpaprika_view.display_markets -crypto.dd.mt,crypto_dd_messari_model.get_messari_timeseries,crypto_dd_messari_view.display_messari_timeseries -crypto.dd.news,crypto_ov_cryptopanic_model.get_news,crypto_dd_cryptopanic_view.display_news -crypto.dd.ob,crypto_dd_ccxt_model.get_orderbook,crypto_dd_ccxt_view.display_order_book -crypto.dd.pi,crypto_dd_messari_model.get_project_product_info,crypto_dd_messari_view.display_project_info -crypto.dd.pr,crypto_dd_pycoingecko_model.get_coin_potential_returns,crypto_dd_pycoingecko_view.display_coin_potential_returns -crypto.dd.ps,crypto_dd_coinpaprika_model.get_tickers_info_for_coin,crypto_dd_coinpaprika_view.display_price_supply -crypto.dd.rm,crypto_dd_messari_model.get_roadmap,crypto_dd_messari_view.display_roadmap -crypto.dd.score,crypto_dd_sdk_helper.score, -crypto.dd.show_available_pairs_for_given_symbol,crypto_dd_coinbase_model.show_available_pairs_for_given_symbol, -crypto.dd.social,crypto_dd_sdk_helper.social, -crypto.dd.stats,crypto_dd_coinbase_model.get_product_stats,crypto_dd_coinbase_view.display_stats -crypto.dd.team,crypto_dd_messari_model.get_team,crypto_dd_messari_view.display_team -crypto.dd.tk,crypto_dd_messari_model.get_tokenomics,crypto_dd_messari_view.display_tokenomics -crypto.dd.tokenomics,crypto_dd_pycoingecko_model.get_coin_tokenomics, -crypto.dd.trades,crypto_dd_ccxt_model.get_trades,crypto_dd_ccxt_view.display_trades -crypto.dd.trading_pair_info,crypto_dd_coinbase_model.get_trading_pair_info, -crypto.dd.trading_pairs,crypto_dd_binance_model._get_trading_pairs, -crypto.dd.twitter,crypto_dd_coinpaprika_model.get_coin_twitter_timeline,crypto_dd_coinpaprika_view.display_twitter -crypto.defi.dtvl,crypto_defi_llama_model.get_defi_protocol,crypto_defi_llama_view.display_historical_tvl -crypto.defi.gdapps,crypto_defi_llama_model.get_grouped_defi_protocols,crypto_defi_llama_view.display_grouped_defi_protocols -crypto.defi.ldapps,crypto_defi_llama_model.get_defi_protocols,crypto_defi_llama_view.display_defi_protocols -crypto.defi.luna_supply,crypto_defi_smartstake_model.get_luna_supply_stats,crypto_defi_smartstake_view.display_luna_circ_supply_change -crypto.defi.newsletters,crypto_defi_substack_model.get_newsletters,crypto_defi_substack_view.display_newsletters -crypto.defi.stvl,crypto_defi_llama_model.get_defi_tvl,crypto_defi_llama_view.display_defi_tvl -crypto.defi.vaults,crypto_defi_coindix_model.get_defi_vaults,crypto_defi_coindix_view.display_defi_vaults -crypto.disc.categories_keys,crypto_disc_pycoingecko_model.get_categories_keys, -crypto.disc.coin_list,crypto_disc_pycoingecko_model.get_coin_list, -crypto.disc.coins,crypto_disc_pycoingecko_model.get_coins,crypto_disc_pycoingecko_view.display_coins -crypto.disc.coins_for_given_exchange,crypto_disc_pycoingecko_model.get_coins_for_given_exchange, -crypto.disc.cpsearch,crypto_disc_coinpaprika_model.get_search_results,crypto_disc_coinpaprika_view.display_search_results -crypto.disc.dapp_categories,crypto_disc_dappradar_model.get_dapp_categories,crypto_disc_dappradar_view.display_dapp_categories -crypto.disc.dapp_chains,crypto_disc_dappradar_model.get_dapp_chains,crypto_disc_dappradar_view.display_dapp_chains -crypto.disc.dapp_metrics,crypto_disc_dappradar_model.get_dapp_metrics,crypto_disc_dappradar_view.display_dapp_metrics -crypto.disc.dapps,crypto_disc_dappradar_model.get_dapps,crypto_disc_dappradar_view.display_dapps -crypto.disc.defi_chains,crypto_disc_dappradar_model.get_defi_chains,crypto_disc_dappradar_view.display_defi_chains -crypto.disc.fees,crypto_disc_cryptostats_model.get_fees,crypto_disc_cryptostats_view.display_fees -crypto.disc.gainers,crypto_disc_pycoingecko_model.get_gainers,crypto_disc_pycoingecko_view.display_gainers -crypto.disc.losers,crypto_disc_pycoingecko_model.get_losers,crypto_disc_pycoingecko_view.display_losers -crypto.disc.nft_mktp,crypto_disc_dappradar_model.get_nft_marketplaces,crypto_disc_dappradar_view.display_nft_marketplaces -crypto.disc.nft_mktp_chains,crypto_disc_dappradar_model.get_nft_marketplace_chains,crypto_disc_dappradar_view.display_nft_marketplace_chains -crypto.disc.tokens,crypto_disc_dappradar_model.get_token_chains,crypto_disc_dappradar_view.display_token_chains -crypto.disc.top_coins,crypto_disc_sdk_helpers.top_coins, -crypto.disc.trending,crypto_disc_pycoingecko_model.get_trending_coins,crypto_disc_pycoingecko_view.display_trending -crypto.find,crypto_models.find, -crypto.load,crypto_helpers.load, -crypto.nft.collections,crypto_nft_pricefloor_model.get_collections,crypto_nft_pricefloor_view.display_collections -crypto.nft.fp,crypto_nft_pricefloor_model.get_floor_price,crypto_nft_pricefloor_view.display_floor_price -crypto.nft.stats,crypto_nft_opensea_model.get_collection_stats,crypto_nft_opensea_view.display_collection_stats -crypto.onchain.baas,crypto_onchain_bitquery_model.get_spread_for_crypto_pair,crypto_onchain_bitquery_view.display_spread_for_crypto_pair -crypto.onchain.balance,crypto_onchain_ethplorer_model.get_address_info,crypto_onchain_ethplorer_view.display_address_info -crypto.onchain.btc_supply,crypto_onchain_blockchain_model.get_btc_circulating_supply,crypto_onchain_blockchain_view.display_btc_circulating_supply -crypto.onchain.btc_transac,crypto_onchain_blockchain_model.get_btc_confirmed_transactions,crypto_onchain_blockchain_view.display_btc_confirmed_transactions -crypto.onchain.btcsingleblock,crypto_onchain_blockchain_model.get_btc_single_block,crypto_onchain_blockchain_view.display_btc_single_block -crypto.onchain.dex_trades_monthly,crypto_onchain_bitquery_model.get_dex_trades_monthly, -crypto.onchain.dvcp,crypto_onchain_bitquery_model.get_daily_dex_volume_for_given_pair,crypto_onchain_bitquery_view.display_daily_volume_for_given_pair -crypto.onchain.erc20_tokens,crypto_onchain_bitquery_model.get_erc20_tokens, -crypto.onchain.hist,crypto_onchain_ethplorer_model.get_address_history,crypto_onchain_ethplorer_view.display_address_history -crypto.onchain.holders,crypto_onchain_ethplorer_model.get_top_token_holders,crypto_onchain_ethplorer_view.display_top_token_holders -crypto.onchain.info,crypto_onchain_ethplorer_model.get_token_info,crypto_onchain_ethplorer_view.display_token_info -crypto.onchain.lt,crypto_onchain_bitquery_model.get_dex_trades_by_exchange,crypto_onchain_bitquery_view.display_dex_trades -crypto.onchain.prices,crypto_onchain_ethplorer_model.get_token_historical_price,crypto_onchain_ethplorer_view.display_token_historical_prices -crypto.onchain.query_graph,crypto_onchain_bitquery_model.query_graph, -crypto.onchain.th,crypto_onchain_ethplorer_model.get_token_history,crypto_onchain_ethplorer_view.display_token_history -crypto.onchain.token_decimals,crypto_onchain_ethplorer_model.get_token_decimals, -crypto.onchain.top,crypto_onchain_ethplorer_model.get_top_tokens,crypto_onchain_ethplorer_view.display_top_tokens -crypto.onchain.topledger,crypto_onchain_topledger_model.get_topledger_data,crypto_onchain_topledger_view.display_topledger_data -crypto.onchain.ttcp,crypto_onchain_bitquery_model.get_most_traded_pairs,crypto_onchain_bitquery_view.display_most_traded_pairs -crypto.onchain.tv,crypto_onchain_bitquery_model.get_token_volume_on_dexes,crypto_onchain_bitquery_view.display_dex_volume_for_token -crypto.onchain.tx,crypto_onchain_ethplorer_model.get_tx_info,crypto_onchain_ethplorer_view.display_tx_info -crypto.onchain.ueat,crypto_onchain_bitquery_model.get_ethereum_unique_senders,crypto_onchain_bitquery_view.display_ethereum_unique_senders -crypto.onchain.whales,crypto_onchain_whale_alert_model.get_whales_transactions,crypto_onchain_whale_alert_view.display_whales_transactions -crypto.ov.altindex,crypto_ov_blockchaincenter_model.get_altcoin_index,crypto_ov_blockchaincenter_view.display_altcoin_index -crypto.ov.categories,crypto_ov_pycoingecko_model.get_top_crypto_categories,crypto_ov_pycoingecko_view.display_categories -crypto.ov.cbpairs,crypto_ov_coinbase_model.get_trading_pairs,crypto_ov_coinbase_view.display_trading_pairs -crypto.ov.coin_list,crypto_dd_coinpaprika_model.get_coin_list, -crypto.ov.contracts,crypto_ov_coinpaprika_model.get_contract_platform,crypto_ov_coinpaprika_view.display_contracts -crypto.ov.cr,crypto_ov_loanscan_model.get_rates,crypto_ov_loanscan_view.display_crypto_rates -crypto.ov.crypto_hack,crypto_ov_rekt_model.get_crypto_hack, -crypto.ov.crypto_hack_slugs,crypto_ov_rekt_model.get_crypto_hack_slugs, -crypto.ov.crypto_hacks,crypto_ov_rekt_model.get_crypto_hacks,crypto_ov_rekt_view.display_crypto_hacks -crypto.ov.defi,crypto_ov_pycoingecko_model.get_global_defi_info,crypto_ov_pycoingecko_view.display_global_defi_info -crypto.ov.derivatives,crypto_ov_pycoingecko_model.get_derivatives,crypto_ov_pycoingecko_view.display_derivatives -crypto.ov.ewf,crypto_ov_withdrawalfees_model.get_overall_exchange_withdrawal_fees,crypto_ov_withdrawalfees_view.display_overall_exchange_withdrawal_fees -crypto.ov.exchanges,crypto_ov_sdk_helpers.exchanges, -crypto.ov.exmarkets,crypto_ov_coinpaprika_model.get_exchanges_market,crypto_ov_coinpaprika_view.display_exchange_markets -crypto.ov.exrates,crypto_ov_pycoingecko_model.get_exchange_rates,crypto_ov_pycoingecko_view.display_exchange_rates -crypto.ov.globe,crypto_ov_sdk_helpers.globe, -crypto.ov.hm,crypto_disc_pycoingecko_model.get_coins,crypto_ov_pycoingecko_view.display_crypto_heatmap -crypto.ov.hold,crypto_ov_pycoingecko_model.get_holdings_overview,crypto_ov_pycoingecko_view.display_holdings_overview -crypto.ov.indexes,crypto_ov_pycoingecko_model.get_indexes,crypto_ov_pycoingecko_view.display_indexes -crypto.ov.info,crypto_ov_coinpaprika_model.get_coins_info,crypto_ov_coinpaprika_view.display_all_coins_info -crypto.ov.markets,crypto_ov_coinpaprika_model.get_coins_market_info,crypto_ov_coinpaprika_view.display_all_coins_market_info -crypto.ov.news,crypto_ov_cryptopanic_model.get_news,crypto_ov_cryptopanic_view.display_news -crypto.ov.platforms,crypto_ov_coinpaprika_model.get_all_contract_platforms,crypto_ov_coinpaprika_view.display_all_platforms -crypto.ov.products,crypto_ov_pycoingecko_model.get_finance_products,crypto_ov_pycoingecko_view.display_products -crypto.ov.stables,crypto_ov_pycoingecko_model.get_stable_coins,crypto_ov_pycoingecko_view.display_stablecoins -crypto.ov.wf,crypto_ov_withdrawalfees_model.get_overall_withdrawal_fees,crypto_ov_withdrawalfees_view.display_overall_withdrawal_fees -crypto.ov.wfpe,crypto_ov_withdrawalfees_model.get_crypto_withdrawal_fees,crypto_ov_withdrawalfees_view.display_crypto_withdrawal_fees -crypto.price,crypto_pyth_view.display_price, -crypto.tools.apy,crypto_tools_model.calculate_apy,crypto_tools_view.display_apy -crypto.tools.il,crypto_tools_model.calculate_il,crypto_tools_view.display_il -econometrics.bgod,econometrics_regression_model.get_bgod,econometrics_regression_view.display_bgod -econometrics.bols,econometrics_regression_model.get_bols, -econometrics.bpag,econometrics_regression_model.get_bpag,econometrics_regression_view.display_bpag -econometrics.clean,econometrics_model.clean, -econometrics.coint,econometrics_model.get_coint_df,econometrics_view.display_cointegration_test -econometrics.comparison,econometrics_regression_model.get_comparison, -econometrics.dwat,econometrics_regression_model.get_dwat,econometrics_regression_view.display_dwat -econometrics.fdols,econometrics_regression_model.get_fdols, -econometrics.fe,econometrics_regression_model.get_fe, -econometrics.garch,econometrics_model.get_garch,econometrics_view.display_garch -econometrics.get_regression_data,econometrics_regression_model.get_regression_data, -econometrics.granger,econometrics_model.get_granger_causality,econometrics_view.display_granger -econometrics.load,common_model.load, -econometrics.norm,econometrics_model.get_normality,econometrics_view.display_norm -econometrics.ols,econometrics_regression_model.get_ols, -econometrics.options,econometrics_model.get_options,econometrics_view.show_options -econometrics.panel,econometrics_regression_model.get_regressions_results,econometrics_regression_view.display_panel -econometrics.pols,econometrics_regression_model.get_pols, -econometrics.re,econometrics_regression_model.get_re, -econometrics.root,econometrics_model.get_root,econometrics_view.display_root -econometrics.vif,econometrics_model.get_vif, -economy.available_indices,economy_yfinance_model.get_available_indices, -economy.balance,economy_oecd_model.get_balance,economy_oecd_view.plot_balance -economy.ccpi,economy_oecd_model.get_cpi,economy_oecd_view.plot_cpi -economy.country_codes,economy_nasdaq_model.get_country_codes, -economy.cpi,economy_fred_model.get_cpi,economy_fred_view.plot_cpi -economy.currencies,economy_wsj_model.global_currencies, -economy.debt,economy_oecd_model.get_debt,economy_oecd_view.plot_debt -economy.events,economy_nasdaq_model.get_economic_calendar, -economy.fgdp,economy_oecd_model.get_gdp_forecast,economy_oecd_view.plot_gdp_forecast -economy.fred,economy_fred_model.get_aggregated_series_data,economy_fred_view.display_fred_series -economy.fred_notes,economy_fred_model.get_series_notes, -economy.future,economy_finviz_model.get_futures, -economy.futures,economy_sdk_helpers.futures, -economy.gdp,economy_oecd_model.get_gdp,economy_oecd_view.plot_gdp -economy.get_groups,economy_finviz_model.get_groups, -economy.glbonds,economy_wsj_model.global_bonds, -economy.index,economy_yfinance_model.get_indices,economy_yfinance_view.show_indices -economy.indices,economy_wsj_model.us_indices, -economy.macro,economy_econdb_model.get_aggregated_macro_data,economy_econdb_view.show_macro_data -economy.macro_countries,economy_econdb_model.get_macro_countries, -economy.macro_parameters,economy_econdb_model.get_macro_parameters, -economy.overview,economy_wsj_model.market_overview, -economy.perfmap,economy_finviz_model.get_performance_map, -economy.performance,economy_finviz_model.get_performance_data, -economy.revenue,economy_oecd_model.get_revenue,economy_oecd_view.plot_revenue -economy.rgdp,economy_oecd_model.get_real_gdp,economy_oecd_view.plot_real_gdp -economy.search_index,economy_yfinance_model.get_search_indices, -economy.spending,economy_oecd_model.get_spending,economy_oecd_view.plot_spending -economy.treasury,economy_fedreserve_model.get_treasury_rates,economy_fedreserve_view.show_treasuries -economy.trust,economy_oecd_model.get_trust,economy_oecd_view.plot_trust -economy.usbonds,economy_wsj_model.us_bonds, -economy.usdli,economy_fred_model.get_usd_liquidity,economy_fred_view.display_usd_liquidity -economy.valuation,economy_finviz_model.get_valuation_data, -etf.candle,stocks_helper.display_candle, -etf.compare,etf_stockanalysis_model.compare_etfs, -etf.disc.mover,etf_disc_wsj_model.etf_movers, -etf.etf_by_category,etf_financedatabase_model.get_etfs_by_category, -etf.etf_by_name,etf_stockanalysis_model.get_etfs_by_name, -etf.holdings,etf_stockanalysis_model.get_etf_holdings, -etf.ld,etf_financedatabase_model.get_etfs_by_description, -etf.ln,etf_financedatabase_model.get_etfs_by_name, -etf.load,stocks_helper.load, -etf.news,common_newsapi_model.get_news,common_newsapi_view.display_news -etf.overview,etf_stockanalysis_model.get_etf_overview, -etf.symbols,etf_stockanalysis_model.get_all_names_symbols, -etf.weights,etf_fmp_model.get_etf_sector_weightings, -fixedincome.ameribor,fixedincome_fred_model.get_ameribor, -fixedincome.cp,fixedincome_fred_model.get_cp, -fixedincome.dwpcr,fixedincome_fred_model.get_dwpcr, -fixedincome.ecb,fixedincome_fred_model.get_ecb, -fixedincome.ecbycrv,fixedincome_ecb_model.get_ecb_yield_curve, -fixedincome.estr,fixedincome_fred_model.get_estr, -fixedincome.fed,fixedincome_fred_model.get_fed, -fixedincome.ffrmc,fixedincome_fred_model.get_ffrmc, -fixedincome.hqm,fixedincome_fred_model.get_hqm, -fixedincome.icebofa,fixedincome_fred_model.get_icebofa, -fixedincome.icespread,fixedincome_fred_model.get_icespread, -fixedincome.iorb,fixedincome_fred_model.get_iorb, -fixedincome.moody,fixedincome_fred_model.get_moody, -fixedincome.projection,fixedincome_fred_model.get_projection, -fixedincome.sofr,fixedincome_fred_model.get_sofr, -fixedincome.sonia,fixedincome_fred_model.get_sonia, -fixedincome.spot,fixedincome_fred_model.get_spot, -fixedincome.tbffr,fixedincome_fred_model.get_tbffr, -fixedincome.tmc,fixedincome_fred_model.get_tmc, -fixedincome.treasury,fixedincome_oecd_model.get_treasury, -fixedincome.usrates,fixedincome_fred_model.get_usrates, -fixedincome.ycrv,fixedincome_fred_model.get_yield_curve,fixedincome_fred_view.display_yield_curve -forex.candle,forex_helper.display_candle, -forex.fwd,forex_fxempire_model.get_forward_rates, -forex.get_currency_list,forex_av_model.get_currency_list, -forex.load,forex_helper.load, -forex.quote,forex_sdk_helpers.quote, -funds.carbon,funds_mstarpy_model.load_carbon_metrics, -funds.exclusion,funds_mstarpy_model.load_exclusion_policy, -funds.historical,funds_mstarpy_model.get_historical,funds_mstarpy_view.display_historical -funds.holdings,funds_mstarpy_model.load_holdings, -funds.load,funds_mstarpy_model.load_funds, -funds.search,funds_mstarpy_model.search_funds, -funds.sector,funds_mstarpy_model.get_sector,funds_mstarpy_view.display_sector -futures.curve,futures_yfinance_model.get_curve_futures,futures_yfinance_view.display_curve -futures.historical,futures_sdk_model.get_historical,futures_yfinance_view.display_historical -futures.search,futures_yfinance_model.get_search_futures, -keys.av,keys_model.set_av_key, -keys.binance,keys_model.set_binance_key, -keys.bitquery,keys_model.set_bitquery_key, -keys.biztoc,keys_model.set_biztoc_key, -keys.cmc,keys_model.set_cmc_key, -keys.coinbase,keys_model.set_coinbase_key, -keys.cpanic,keys_model.set_cpanic_key, -keys.databento,keys_model.set_databento_key, -keys.eodhd,keys_model.set_eodhd_key, -keys.ethplorer,keys_model.set_ethplorer_key, -keys.finnhub,keys_model.set_finnhub_key, -keys.fmp,keys_model.set_fmp_key, -keys.fred,keys_model.set_fred_key, -keys.get_keys_info,keys_model.get_keys_info, -keys.github,keys_model.set_github_key, -keys.messari,keys_model.set_messari_key, -keys.mykeys,keys_model.get_keys, -keys.news,keys_model.set_news_key, -keys.polygon,keys_model.set_polygon_key, -keys.quandl,keys_model.set_quandl_key, -keys.reddit,keys_model.set_reddit_key, -keys.santiment,keys_model.set_santiment_key, -keys.set_keys,keys_model.set_keys, -keys.smartstake,keys_model.set_smartstake_key, -keys.tradier,keys_model.set_tradier_key, -keys.walert,keys_model.set_walert_key, -login,sdk_session.login, -logout,sdk_session.logout, -news,common_news_sdk_helper.news, -portfolio.alloc.assets,portfolio_model.get_assets_allocation, -portfolio.alloc.countries,portfolio_model.get_countries_allocation, -portfolio.alloc.regions,portfolio_model.get_regions_allocation, -portfolio.alloc.sectors,portfolio_model.get_sectors_allocation, -portfolio.bench,portfolio_model.set_benchmark, -portfolio.distr,portfolio_model.get_distribution_returns,portfolio_view.display_distribution_returns -portfolio.dret,portfolio_model.get_daily_returns,portfolio_view.display_daily_returns -portfolio.es,portfolio_model.get_es, -portfolio.holdp,portfolio_model.get_holdings_percentage,portfolio_view.display_holdings_percentage -portfolio.holdv,portfolio_model.get_holdings_value,portfolio_view.display_holdings_value -portfolio.load,portfolio_model.generate_portfolio, -portfolio.maxdd,portfolio_model.get_maximum_drawdown,portfolio_view.display_maximum_drawdown -portfolio.metric.calmar,portfolio_model.get_calmar_ratio, -portfolio.metric.commonsense,portfolio_model.get_common_sense_ratio, -portfolio.metric.gaintopain,portfolio_model.get_gaintopain_ratio, -portfolio.metric.information,portfolio_model.get_information_ratio, -portfolio.metric.jensens,portfolio_model.get_jensens_alpha, -portfolio.metric.kelly,portfolio_model.get_kelly_criterion, -portfolio.metric.kurtosis,portfolio_model.get_kurtosis, -portfolio.metric.maxdrawdown,portfolio_model.get_maximum_drawdown_ratio, -portfolio.metric.payoff,portfolio_model.get_payoff_ratio, -portfolio.metric.profitfactor,portfolio_model.get_profit_factor, -portfolio.metric.rsquare,portfolio_model.get_r2_score, -portfolio.metric.sharpe,portfolio_model.get_sharpe_ratio, -portfolio.metric.skew,portfolio_model.get_skewness, -portfolio.metric.sortino,portfolio_model.get_sortino_ratio, -portfolio.metric.tail,portfolio_model.get_tail_ratio, -portfolio.metric.trackerr,portfolio_model.get_tracking_error, -portfolio.metric.volatility,portfolio_model.get_volatility, -portfolio.mret,portfolio_model.get_monthly_returns,portfolio_view.display_monthly_returns -portfolio.om,portfolio_model.get_omega,portfolio_view.display_omega -portfolio.perf,portfolio_model.get_performance_vs_benchmark, -portfolio.rbeta,portfolio_model.get_rolling_beta,portfolio_view.display_rolling_beta -portfolio.rsharpe,portfolio_model.get_rolling_sharpe,portfolio_view.display_rolling_sharpe -portfolio.rsort,portfolio_model.get_rolling_sortino,portfolio_view.display_rolling_sortino -portfolio.rvol,portfolio_model.get_rolling_volatility,portfolio_view.display_rolling_volatility -portfolio.show,portfolio_model.get_transactions, -portfolio.summary,portfolio_model.get_summary, -portfolio.var,portfolio_model.get_var, -portfolio.yret,portfolio_model.get_yearly_returns,portfolio_view.display_yearly_returns -qa.acf,common_qa_view.display_acf, -qa.bw,common_qa_view.display_bw, -qa.calculate_adjusted_var,common_qa_model.calculate_adjusted_var, -qa.cdf,common_qa_view.display_cdf, -qa.cusum,common_qa_view.display_cusum, -qa.decompose,common_qa_model.get_seasonal_decomposition, -qa.es,common_qa_model.get_es,common_qa_view.display_es -qa.hist,common_qa_view.display_hist, -qa.kurtosis,common_qa_rolling_model.get_kurtosis,common_qa_rolling_view.display_kurtosis -qa.line,common_qa_view.display_line, -qa.normality,common_qa_model.get_normality,common_qa_view.display_normality -qa.omega,common_qa_model.get_omega,common_qa_view.display_omega -qa.qqplot,common_qa_view.display_qqplot, -qa.quantile,common_qa_rolling_model.get_quantile,common_qa_rolling_view.display_quantile -qa.rolling,common_qa_rolling_model.get_rolling_avg,common_qa_rolling_view.display_mean_std -qa.sharpe,common_qa_model.get_sharpe,common_qa_view.display_sharpe -qa.skew,common_qa_rolling_model.get_skew,common_qa_rolling_view.display_skew -qa.sortino,common_qa_model.get_sortino,common_qa_view.display_sortino -qa.spread,common_qa_rolling_model.get_spread,common_qa_rolling_view.display_spread -qa.summary,common_qa_model.get_summary,common_qa_view.display_summary -qa.unitroot,common_qa_model.get_unitroot,common_qa_view.display_unitroot -qa.var,common_qa_model.get_var,common_qa_view.display_var -stocks.ba.bullbear,stocks_ba_stocktwits_model.get_bullbear, -stocks.ba.cnews,stocks_ba_finnhub_model.get_company_news, -stocks.ba.getdd,stocks_ba_reddit_model.get_due_dilligence, -stocks.ba.headlines,stocks_ba_finbrain_model.get_sentiment,stocks_ba_finbrain_view.display_sentiment_analysis -stocks.ba.mentions,stocks_ba_google_model.get_mentions,stocks_ba_google_view.display_mentions -stocks.ba.messages,stocks_ba_stocktwits_model.get_messages, -stocks.ba.popular,stocks_ba_reddit_model.get_popular_tickers, -stocks.ba.queries,stocks_ba_google_model.get_queries, -stocks.ba.redditsent,stocks_ba_reddit_model.get_posts_about, -stocks.ba.regions,stocks_ba_google_model.get_regions,stocks_ba_google_view.display_regions -stocks.ba.rise,stocks_ba_google_model.get_rise, -stocks.ba.snews,stocks_ba_finnhub_model.get_headlines_sentiment,stocks_ba_finnhub_view.display_stock_price_headlines_sentiment -stocks.ba.stalker,stocks_ba_stocktwits_model.get_stalker, -stocks.ba.text_sent,stocks_ba_reddit_model.get_sentiment, -stocks.ba.trending,stocks_ba_stocktwits_model.get_trending, -stocks.ba.wsb,stocks_ba_reddit_model.get_wsb_community, -stocks.ca.balance,stocks_ca_marketwatch_model.get_balance_comparison, -stocks.ca.cashflow,stocks_ca_marketwatch_model.get_cashflow_comparison, -stocks.ca.hcorr,stocks_ca_yahoo_finance_model.get_correlation,stocks_ca_yahoo_finance_view.display_correlation -stocks.ca.hist,stocks_ca_yahoo_finance_model.get_historical,stocks_ca_yahoo_finance_view.display_historical -stocks.ca.income,stocks_ca_marketwatch_model.get_income_comparison,stocks_ca_marketwatch_view.display_income_comparison -stocks.ca.scorr,stocks_ca_finbrain_model.get_sentiment_correlation,stocks_ca_finbrain_view.display_sentiment_correlation -stocks.ca.screener,stocks_ca_finviz_compare_model.get_comparison_data, -stocks.ca.sentiment,stocks_ca_finbrain_model.get_sentiments,stocks_ca_finbrain_view.display_sentiment_compare -stocks.ca.similar,stocks_ca_sdk_helpers.get_similar, -stocks.ca.volume,stocks_ca_yahoo_finance_model.get_volume,stocks_ca_yahoo_finance_view.display_volume -stocks.candle,stocks_helper.display_candle, -stocks.disc.active,stocks_disc_yahoofinance_model.get_active, -stocks.disc.arkord,stocks_disc_ark_model.get_ark_orders, -stocks.disc.asc,stocks_disc_yahoofinance_model.get_asc, -stocks.disc.dividends,stocks_disc_nasdaq_model.get_dividend_cal, -stocks.disc.filings,stocks_fa_fmp_model.get_filings,stocks_disc_fmp_view.display_filings -stocks.disc.fipo,stocks_disc_finnhub_model.get_future_ipo, -stocks.disc.gainers,stocks_disc_yahoofinance_model.get_gainers, -stocks.disc.gtech,stocks_disc_yahoofinance_model.get_gtech, -stocks.disc.hotpenny,stocks_disc_shortinterest_model.get_today_hot_penny_stocks, -stocks.disc.ipo,stocks_disc_finnhub_model.get_ipo_calendar, -stocks.disc.losers,stocks_disc_yahoofinance_model.get_losers, -stocks.disc.lowfloat,stocks_disc_shortinterest_model.get_low_float, -stocks.disc.pipo,stocks_disc_finnhub_model.get_past_ipo, -stocks.disc.rtat,stocks_disc_nasdaq_model.get_retail_tickers, -stocks.disc.trending,stocks_disc_seeking_alpha_model.get_trending_list, -stocks.disc.ugs,stocks_disc_yahoofinance_model.get_ugs, -stocks.disc.ulc,stocks_disc_yahoofinance_model.get_ulc, -stocks.disc.upcoming,stocks_disc_seeking_alpha_model.get_next_earnings, -stocks.dps.dpotc,stocks_dps_finra_model.getTickerFINRAdata,stocks_dps_finra_view.darkpool_ats_otc -stocks.dps.ftd,stocks_dps_sec_model.get_fails_to_deliver,stocks_dps_sec_view.fails_to_deliver -stocks.dps.hsi,stocks_dps_shortinterest_model.get_high_short_interest, -stocks.dps.pos,stocks_dps_stockgrid_model.get_dark_pool_short_positions, -stocks.dps.prom,stocks_dps_finra_model.getATSdata,stocks_dps_finra_view.darkpool_otc -stocks.dps.psi_q,stocks_dps_quandl_model.get_short_interest,stocks_dps_quandl_view.short_interest -stocks.dps.psi_sg,stocks_dps_stockgrid_model.get_short_interest_volume,stocks_dps_stockgrid_view.short_interest_volume -stocks.dps.shorted,stocks_dps_yahoofinance_model.get_most_shorted, -stocks.dps.sidtc,stocks_dps_stockgrid_model.get_short_interest_days_to_cover, -stocks.dps.spos,stocks_dps_stockgrid_model.get_net_short_position,stocks_dps_stockgrid_view.net_short_position -stocks.fa.analysis,stocks_fa_eclect_us_model.get_filings_analysis, -stocks.fa.analyst,stocks_fa_finviz_model.get_analyst_data, -stocks.fa.balance,stocks_fa_sdk_helpers.get_balance_sheet, -stocks.fa.cal,stocks_fa_yahoo_finance_model.get_calendar_earnings, -stocks.fa.cash,stocks_fa_sdk_helpers.get_cash_flow, -stocks.fa.customer,stocks_fa_csimarket_model.get_customers, -stocks.fa.dcf,stocks_fa_fmp_model.get_dcf, -stocks.fa.dcfc,stocks_fa_fmp_model.get_dcf, -stocks.fa.divs,stocks_fa_yahoo_finance_model.get_dividends,stocks_fa_yahoo_finance_view.display_dividends -stocks.fa.dupont,stocks_fa_av_model.get_dupont, -stocks.fa.earnings,stocks_fa_sdk_helpers.earnings, -stocks.fa.enterprise,stocks_fa_fmp_model.get_enterprise, -stocks.fa.epsfc,stocks_fa_seeking_alpha_model.get_estimates_eps, -stocks.fa.est,stocks_fa_business_insider_model.get_estimates, -stocks.fa.fama_coe,stocks_fa_dcf_model.get_fama_coe, -stocks.fa.fama_raw,stocks_fa_dcf_model.get_fama_raw, -stocks.fa.fraud,stocks_fa_av_model.get_fraud_ratios, -stocks.fa.growth,stocks_fa_fmp_model.get_financial_growth, -stocks.fa.historical_5,stocks_fa_dcf_model.get_historical_5, -stocks.fa.income,stocks_fa_sdk_helpers.get_income_statement, -stocks.fa.key,stocks_fa_av_model.get_key_metrics, -stocks.fa.metrics,stocks_fa_fmp_model.get_key_metrics, -stocks.fa.mgmt,stocks_fa_business_insider_model.get_management, -stocks.fa.mktcap,stocks_fa_yahoo_finance_model.get_mktcap,stocks_fa_yahoo_finance_view.display_mktcap -stocks.fa.news,stocks_fa_finviz_model.get_news, -stocks.fa.overview,stocks_fa_sdk_helpers.get_overview, -stocks.fa.pt,stocks_fa_business_insider_model.get_price_target_from_analysts,stocks_fa_business_insider_view.display_price_target_from_analysts -stocks.fa.rating,stocks_fa_fmp_model.get_rating, -stocks.fa.ratios,stocks_fa_fmp_model.get_key_ratios, -stocks.fa.revfc,stocks_fa_seeking_alpha_model.get_estimates_rev, -stocks.fa.rot,stocks_fa_finnhub_model.get_rating_over_time,stocks_fa_finnhub_view.rating_over_time -stocks.fa.score,stocks_fa_fmp_model.get_score, -stocks.fa.sec,stocks_fa_nasdaq_model.get_sec_filings, -stocks.fa.shrs,stocks_fa_yahoo_finance_model.get_shareholders, -stocks.fa.similar_dfs,stocks_fa_dcf_model.get_similar_dfs, -stocks.fa.splits,stocks_fa_yahoo_finance_model.get_splits,stocks_fa_yahoo_finance_view.display_splits -stocks.fa.supplier,stocks_fa_csimarket_model.get_suppliers, -stocks.gov.contracts,stocks_gov_quiverquant_model.get_contracts,stocks_gov_quiverquant_view.display_contracts -stocks.gov.government_trading,stocks_gov_quiverquant_model.get_government_trading, -stocks.gov.gtrades,stocks_gov_quiverquant_model.get_cleaned_government_trading,stocks_gov_quiverquant_view.display_government_trading -stocks.gov.histcont,stocks_gov_quiverquant_model.get_hist_contracts,stocks_gov_quiverquant_view.display_hist_contracts -stocks.gov.lastcontracts,stocks_gov_quiverquant_model.get_last_contracts,stocks_gov_quiverquant_view.display_last_contracts -stocks.gov.lasttrades,stocks_gov_quiverquant_model.get_last_government, -stocks.gov.lobbying,stocks_gov_quiverquant_model.get_lobbying, -stocks.gov.qtrcontracts,stocks_gov_quiverquant_model.get_qtr_contracts,stocks_gov_quiverquant_view.display_qtr_contracts -stocks.gov.topbuys,stocks_gov_quiverquant_model.get_government_buys,stocks_gov_quiverquant_view.display_government_buys -stocks.gov.toplobbying,stocks_gov_quiverquant_model.get_top_lobbying,stocks_gov_quiverquant_view.display_top_lobbying -stocks.gov.topsells,stocks_gov_quiverquant_model.get_government_sells,stocks_gov_quiverquant_view.display_government_sells -stocks.ins.act,stocks_insider_businessinsider_model.get_insider_activity,stocks_insider_businessinsider_view.insider_activity -stocks.ins.blcp,stocks_insider_sdk_helper.blcp, -stocks.ins.blcs,stocks_insider_sdk_helper.blcs, -stocks.ins.blip,stocks_insider_sdk_helper.blip, -stocks.ins.blis,stocks_insider_sdk_helper.blis, -stocks.ins.blop,stocks_insider_sdk_helper.blop, -stocks.ins.blos,stocks_insider_sdk_helper.blos, -stocks.ins.filter,stocks_insider_sdk_helper.insider_filter, -stocks.ins.lcb,stocks_insider_sdk_helper.lcb, -stocks.ins.lins,stocks_insider_finviz_model.get_last_insider_activity,stocks_insider_finviz_view.last_insider_activity -stocks.ins.lip,stocks_insider_sdk_helper.lip, -stocks.ins.lis,stocks_insider_sdk_helper.lis, -stocks.ins.lit,stocks_insider_sdk_helper.lit, -stocks.ins.lpsb,stocks_insider_sdk_helper.lpsb, -stocks.ins.print_insider_data,stocks_insider_openinsider_model.get_print_insider_data,stocks_insider_openinsider_view.print_insider_data -stocks.ins.stats,stocks_insider_sdk_helper.stats, -stocks.load,stocks_helper.load, -stocks.options.chains,stocks_options_sdk_helper.get_full_option_chain, -stocks.options.dte,stocks_options_helpers.get_dte, -stocks.options.eodchain,stocks_options_intrinio_model.get_full_chain_eod, -stocks.options.expirations,stocks_options_sdk_helper.get_option_expirations, -stocks.options.generate_data,stocks_options_yfinance_model.generate_data, -stocks.options.get_strategies,stocks_options_options_chains_model.get_strategies, -stocks.options.greeks,stocks_options_sdk_helper.get_greeks, -stocks.options.grhist,stocks_options_intrinio_model.get_historical_options,stocks_options_intrinio_view.view_historical_greeks -stocks.options.hist,stocks_options_sdk_helper.hist, -stocks.options.info,stocks_options_barchart_model.get_options_info,stocks_options_barchart_view.print_options_data -stocks.options.last_price,stocks_options_tradier_model.get_last_price, -stocks.options.load_options_chains,stocks_options_sdk_helper.load_options_chains, -stocks.options.oi,stocks_options_view.plot_oi, -stocks.options.pcr,stocks_options_alphaquery_model.get_put_call_ratio,stocks_options_alphaquery_view.display_put_call_ratio -stocks.options.price,stocks_options_sdk_helper.get_option_current_price, -stocks.options.unu,stocks_options_fdscanner_model.unusual_options,stocks_options_fdscanner_view.display_options -stocks.options.voi,stocks_options_view.plot_voi, -stocks.options.vol,stocks_options_view.plot_vol, -stocks.options.vsurf,stocks_options_yfinance_model.get_iv_surface,stocks_options_yfinance_view.display_vol_surface -stocks.process_candle,stocks_helper.process_candle, -stocks.qa.beta,stocks_qa_beta_model.beta_model,stocks_qa_beta_view.beta_view -stocks.qa.capm,stocks_qa_factors_model.capm_information, -stocks.qa.fama_raw,stocks_qa_factors_model.get_fama_raw, -stocks.qa.historical_5,stocks_qa_factors_model.get_historical_5, -stocks.quote,stocks_model.get_quote,stocks_view.display_quote -stocks.screener.screener_data,stocks_screener_finviz_model.get_screener_data,stocks_screener_finviz_view.screener -stocks.search,stocks_helper.search, -stocks.ta.recom,stocks_ta_tradingview_model.get_tradingview_recommendation,stocks_ta_tradingview_view.print_recommendation -stocks.ta.summary,stocks_ta_finbrain_model.get_technical_summary_report,stocks_ta_finbrain_view.technical_summary_report -stocks.th.all,stocks_th_bursa_model.get_all,stocks_th_bursa_view.display_all -stocks.th.check_if_open,stocks_th_bursa_model.check_if_open, -stocks.th.closed,stocks_th_bursa_model.get_closed,stocks_th_bursa_view.display_closed -stocks.th.exchange,stocks_th_bursa_model.get_bursa,stocks_th_bursa_view.display_exchange -stocks.th.open,stocks_th_bursa_model.get_open,stocks_th_bursa_view.display_open -stocks.tob,stocks_cboe_model.get_top_of_book, -ta.ad,common_ta_volume_model.ad,common_ta_volume_view.display_ad -ta.adosc,common_ta_volume_model.adosc,common_ta_volume_view.display_adosc -ta.adx,common_ta_trend_indicators_model.adx,common_ta_trend_indicators_view.display_adx -ta.aroon,common_ta_trend_indicators_model.aroon,common_ta_trend_indicators_view.display_aroon -ta.atr,common_ta_volatility_model.atr,common_ta_volatility_view.display_atr -ta.bbands,common_ta_volatility_model.bbands,common_ta_volatility_view.display_bbands -ta.cci,common_ta_momentum_model.cci,common_ta_momentum_view.display_cci -ta.cg,common_ta_momentum_model.cg,common_ta_momentum_view.display_cg -ta.clenow,common_ta_momentum_model.clenow_momentum,common_ta_momentum_view.display_clenow_momentum -ta.cones,common_ta_volatility_model.cones,common_ta_volatility_view.display_cones -ta.demark,common_ta_momentum_model.demark_seq,common_ta_momentum_view.display_demark -ta.donchian,common_ta_volatility_model.donchian,common_ta_volatility_view.display_donchian -ta.ema,common_ta_overlap_model.ema, -ta.fib,common_ta_custom_indicators_model.calculate_fib_levels,common_ta_custom_indicators_view.fibonacci_retracement -ta.fisher,common_ta_momentum_model.fisher,common_ta_momentum_view.display_fisher -ta.hma,common_ta_overlap_model.hma, -ta.kc,common_ta_volatility_model.kc,common_ta_volatility_view.view_kc -ta.ma,common_ta_overlap_view.view_ma,common_ta_overlap_view.view_ma -ta.macd,common_ta_momentum_model.macd,common_ta_momentum_view.display_macd -ta.obv,common_ta_volume_model.obv,common_ta_volume_view.display_obv -ta.rsi,common_ta_momentum_model.rsi,common_ta_momentum_view.display_rsi -ta.rvol_garman_klass,common_ta_volatility_model.garman_klass, -ta.rvol_hodges_tompkins,common_ta_volatility_model.hodges_tompkins, -ta.rvol_parkinson,common_ta_volatility_model.parkinson, -ta.rvol_rogers_satchell,common_ta_volatility_model.rogers_satchell, -ta.rvol_std,common_ta_volatility_model.standard_deviation, -ta.rvol_yang_zhang,common_ta_volatility_model.yang_zhang, -ta.sma,common_ta_overlap_model.sma, -ta.standard_deviation,common_ta_volatility_model.standard_deviation, -ta.stoch,common_ta_momentum_model.stoch,common_ta_momentum_view.display_stoch -ta.vwap,common_ta_overlap_model.vwap,common_ta_overlap_view.view_vwap -ta.wma,common_ta_overlap_model.wma, -ta.zlma,common_ta_overlap_model.zlma, -whoami,sdk_session.whoami, diff --git a/openbb_terminal/core/sdk/trail_map_forecasting.csv b/openbb_terminal/core/sdk/trail_map_forecasting.csv deleted file mode 100644 index dd2dcdc8d8d..00000000000 --- a/openbb_terminal/core/sdk/trail_map_forecasting.csv +++ /dev/null @@ -1,40 +0,0 @@ -trail,model,view -forecast.anom,forecast_anom_model.get_anomaly_detection_data,forecast_anom_view.display_anomaly_detection -forecast.atr,forecast_model.add_atr, -forecast.autoarima,forecast_autoarima_model.get_autoarima_data,forecast_autoarima_view.display_autoarima_forecast -forecast.autoces,forecast_autoces_model.get_autoces_data,forecast_autoces_view.display_autoces_forecast -forecast.autoets,forecast_autoets_model.get_autoets_data,forecast_autoets_view.display_autoets_forecast -forecast.autoselect,forecast_autoselect_model.get_autoselect_data,forecast_autoselect_view.display_autoselect_forecast -forecast.brnn,forecast_brnn_model.get_brnn_data,forecast_brnn_view.display_brnn_forecast -forecast.clean,forecast_model.clean, -forecast.combine,forecast_model.combine_dfs, -forecast.corr,forecast_model.corr_df,forecast_view.display_corr -forecast.delete,forecast_model.delete_column, -forecast.delta,forecast_model.add_delta, -forecast.desc,forecast_model.describe_df,forecast_view.describe_df -forecast.ema,forecast_model.add_ema, -forecast.expo,forecast_expo_model.get_expo_data,forecast_expo_view.display_expo_forecast -forecast.export,forecast_view.export_df, -forecast.linregr,forecast_linregr_model.get_linear_regression_data,forecast_linregr_view.display_linear_regression -forecast.load,common_model.load, -forecast.mom,forecast_model.add_momentum, -forecast.mstl,forecast_mstl_model.get_mstl_data,forecast_mstl_view.display_mstl_forecast -forecast.nbeats,forecast_nbeats_model.get_NBEATS_data,forecast_nbeats_view.display_nbeats_forecast -forecast.nhits,forecast_nhits_model.get_nhits_data,forecast_nhits_view.display_nhits_forecast -forecast.plot,forecast_view.display_plot,forecast_view.display_plot -forecast.regr,forecast_regr_model.get_regression_data,forecast_regr_view.display_regression -forecast.rename,forecast_model.rename_column, -forecast.rnn,forecast_rnn_model.get_rnn_data,forecast_rnn_view.display_rnn_forecast -forecast.roc,forecast_model.add_roc, -forecast.rsi,forecast_model.add_rsi, -forecast.rwd,forecast_rwd_model.get_rwd_data,forecast_rwd_view.display_rwd_forecast -forecast.season,,forecast_view.display_seasonality -forecast.seasonalnaive,forecast_seasonalnaive_model.get_seasonalnaive_data,forecast_seasonalnaive_view.display_seasonalnaive_forecast -forecast.show,forecast_view.show_df, -forecast.signal,forecast_model.add_signal, -forecast.sto,forecast_model.add_sto, -forecast.tcn,forecast_tcn_model.get_tcn_data,forecast_tcn_view.display_tcn_forecast -forecast.tft,forecast_tft_model.get_tft_data,forecast_tft_view.display_tft_forecast -forecast.theta,forecast_theta_model.get_theta_data,forecast_theta_view.display_theta_forecast -forecast.trans,forecast_trans_model.get_trans_data,forecast_trans_view.display_trans_forecast -forecast.whisper,forecast_whisper_model.transcribe_and_summarize, diff --git a/openbb_terminal/core/sdk/trail_map_optimization.csv b/openbb_terminal/core/sdk/trail_map_optimization.csv deleted file mode 100644 index 577ae0b4e69..00000000000 --- a/openbb_terminal/core/sdk/trail_map_optimization.csv +++ /dev/null @@ -1,19 +0,0 @@ -trail,model,view -portfolio.po.blacklitterman,portfolio_optimization_po_model.get_blacklitterman, -portfolio.po.ef,portfolio_optimization_po_model.get_ef,portfolio_optimization_po_view.display_ef -portfolio.po.file,portfolio_optimization_po_model.load_parameters_file, -portfolio.po.herc,portfolio_optimization_po_model.get_herc, -portfolio.po.hrp,portfolio_optimization_po_model.get_hrp, -portfolio.po.load,portfolio_optimization_po_model.generate_portfolio, -portfolio.po.load_bl_views,portfolio_optimization_excel_model.load_bl_views, -portfolio.po.maxdecorr,portfolio_optimization_po_model.get_maxdecorr, -portfolio.po.maxdiv,portfolio_optimization_po_model.get_maxdiv, -portfolio.po.maxret,portfolio_optimization_po_model.get_maxret, -portfolio.po.maxsharpe,portfolio_optimization_po_model.get_maxsharpe, -portfolio.po.maxutil,portfolio_optimization_po_model.get_maxutil, -portfolio.po.minrisk,portfolio_optimization_po_model.get_minrisk, -portfolio.po.nco,portfolio_optimization_po_model.get_nco, -portfolio.po.plot,portfolio_optimization_po_view.display_plot,portfolio_optimization_po_view.display_plot -portfolio.po.relriskparity,portfolio_optimization_po_model.get_relriskparity, -portfolio.po.riskparity,portfolio_optimization_po_model.get_riskparity, -portfolio.po.show,portfolio_optimization_po_model.show, diff --git a/openbb_terminal/core/sdk/trailmap.py b/openbb_terminal/core/sdk/trailmap.py deleted file mode 100644 index 4da09ca0b1a..00000000000 --- a/openbb_terminal/core/sdk/trailmap.py +++ /dev/null @@ -1,260 +0,0 @@ -import inspect -from pathlib import Path -from types import FunctionType -from typing import Any, Callable, Dict, ForwardRef, List, Optional - -import pandas as pd - -import openbb_terminal.core.sdk.sdk_init as lib -from openbb_terminal.core.config.paths import ( - MAP_FORECASTING_PATH, - MAP_OPTIMIZATION_PATH, - MAP_PATH, -) -from openbb_terminal.core.sdk.sdk_helpers import clean_attr_desc -from openbb_terminal.rich_config import console - - -def get_signature_parameters( - function: Callable[..., Any], globalns: Dict[str, Any] -) -> Dict[str, inspect.Parameter]: - signature = inspect.signature(function) - - params = {} - - cache: dict[str, Any] = {} - - for name, parameter in signature.parameters.items(): - annotation = parameter.annotation - - if annotation is parameter.empty: - params[name] = parameter - - continue - - if annotation is None: - params[name] = parameter.replace(annotation=type(None)) - - continue - - if isinstance(annotation, ForwardRef): - annotation = annotation.__forward_arg__ - - if isinstance(annotation, str): - annotation = eval( # pylint: disable=W0123 # noqa: S307 - annotation, globalns, cache - ) - - params[name] = parameter.replace(annotation=annotation) - - return params - - -class FuncAttr: - def __init__(self) -> None: - self.short_doc: Optional[str] = None - - self.long_doc: Optional[str] = None - - self.func_def: Optional[str] = None - - self.path: Optional[str] = None - - self.lineon: Optional[int] = None - - self.full_path: Optional[str] = None - - self.func_unwrapped: Optional[FunctionType] = None - - self.func_wrapped: Optional[FunctionType] = None - - self.params: Dict[str, inspect.Parameter] = {} - - def get_func_attrs(self, func: str) -> None: - module_path, function_name = func.rsplit(".", 1) - - func_attr = getattr(getattr(lib, module_path), function_name) - - self.func_wrapped = func_attr - - add_lineon = 0 - - if hasattr(func_attr, "__wrapped__"): - while hasattr(func_attr, "__wrapped__"): - func_attr = func_attr.__wrapped__ - - add_lineon += 1 - - self.func_unwrapped = func_attr - - self.lineon = inspect.getsourcelines(func_attr)[1] + add_lineon - - self.long_doc = func_attr.__doc__ - - self.short_doc = clean_attr_desc(func_attr) - - for k, p in get_signature_parameters(func_attr, func_attr.__globals__).items(): - self.params[k] = p - - self.path = inspect.getfile(func_attr) - - full_path = ( - inspect.getfile(func_attr).replace("\\", "/").split("openbb_terminal/")[1] - ) - - self.full_path = f"openbb_terminal/{full_path}" - - def get_definition( - self, location_path: list, class_attr: str, view: bool = False - ) -> str: - """Creates the function definition to be used in SDK docs.""" - - funcspec = self.params - - definition = "" - - for arg, param in funcspec.items(): - annotation = ( - ( - str(param.annotation) - .replace("", "") - .replace("typing.", "") - .replace("pandas.core.frame.", "pd.") - .replace("pandas.core.series.", "pd.") - .replace("openbb_terminal.portfolio.", "") - ) - if param.annotation != inspect.Parameter.empty - else "Any" - ) - - default = "" - - if param.default is not param.empty: - arg_default = ( - param.default - if param.default is not inspect.Parameter.empty - else "None" - ) - - default = ( - f" = {arg_default}" - if not isinstance(arg_default, str) - else f' = "{arg_default}"' - ) - - definition += f"{arg}: {annotation}{default}, " - - definition = definition.rstrip(", ") - - trail = ".".join([t for t in location_path if t != ""]) - - sdk_name = class_attr if not view else f"{class_attr}_chart" - - trail = trail.replace("root.", "") - - sdk_path = f"{f'openbb.{trail}' if trail else 'openbb'}.{sdk_name}" - - return f"{sdk_path}({definition })" - - -# pylint: disable=R0903 - - -class Trailmap: - def __init__(self, trailmap: str, model: str, view: Optional[str] = None): - self.trailmap: str = trailmap - - tmap = trailmap.split(".") - - if len(tmap) == 1: - tmap = ["root", tmap[0]] - - self.class_attr: str = tmap.pop(-1) - - self.location_path = tmap - - self.model = model - - self.view = view if view else None - - self.view_name = "_chart" - - self.model_func: Optional[str] = f"lib.{model}" if model else None - - self.view_func: Optional[str] = f"lib.{view}" if view else None - - self.func_attrs: Dict[str, FuncAttr] = {} - - self.func_attrs["model"] = FuncAttr() - - self.func_attrs["view"] = FuncAttr() - - for k, cls in self.func_attrs.items(): - if getattr(self, f"{k}_func"): - cls.get_func_attrs(getattr(self, f"{k}")) - - cls.func_def = cls.get_definition( - tmap, self.class_attr, view=k == "view" - ) - - -def get_trailmaps(sort: bool = False) -> List[Trailmap]: - trailmaps = [] - - def load_csv(path: Optional[Path] = None) -> Dict[str, Dict[str, str]]: - path = path or MAP_PATH - - df = pd.read_csv(path, keep_default_na=False) - - df = df.set_index("trail") - - if sort: - df = df.sort_index() - - df.to_csv(path, index=True) - - return df.to_dict(orient="index") - - def print_error(error: str) -> None: - console.print( - f"[bold red]{error} is disabled and will not be included in the SDK.[/bold red]" - ) - - console.print( - "[bold red]`poetry install -E all` to install all toolkits.[/bold red]" - ) - - def load(): - map_dict = load_csv(path=MAP_PATH) - - if lib.FORECASTING_TOOLKIT_ENABLED: - map_forecasting_dict = load_csv(path=MAP_FORECASTING_PATH) - - map_dict.update(map_forecasting_dict) - - else: - print_error("Forecasting") - - if lib.OPTIMIZATION_TOOLKIT_ENABLED: - map_optimization_dict = load_csv(path=MAP_OPTIMIZATION_PATH) - - map_dict.update(map_optimization_dict) - - else: - print_error("Optimization") - - map_dict = dict(sorted(map_dict.items(), key=lambda item: item[0])) - - return map_dict - - map_dict = load() - - for trail, attrs in map_dict.items(): - model, view = attrs["model"], attrs["view"] - - trail_map = Trailmap(trail, model, view) - - trailmaps.append(trail_map) - - return trailmaps diff --git a/openbb_terminal/core/session/__init__.py b/openbb_terminal/core/session/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/session/banner.txt b/openbb_terminal/core/session/banner.txt deleted file mode 100644 index f65ba387912..00000000000 --- a/openbb_terminal/core/session/banner.txt +++ /dev/null @@ -1,18 +0,0 @@ - ___ ____ ____ _____ _ _ - / _ \ _ __ ___ _ __ | __ )| __ ) |_ _|__ _ __ _ __ ___ (_)_ __ __ _| | -| | | | '_ \ / _ \ '_ \| _ \| _ \ | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | | -| |_| | |_) | __/ | | | |_) | |_) | | | __/ | | | | | | | | | | | (_| | | - \___/| .__/ \___|_| |_|____/|____/ |_|\___|_| |_| |_| |_|_|_| |_|\__,_|_| - |_| - @@@ - @@@ - @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ - @@@ @@@ @@@ @@@ - @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ - @@@ @@@ - %%%%%%%%%%%%%%%%%@@@ @@@%%%%%%%%%%%%%%%%% - @@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@ - @@@ @@@ @@@ @@@ - @@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@ - - Investment research for everyone, anywhere. diff --git a/openbb_terminal/core/session/constants.py b/openbb_terminal/core/session/constants.py deleted file mode 100644 index 83b9b854aac..00000000000 --- a/openbb_terminal/core/session/constants.py +++ /dev/null @@ -1,55 +0,0 @@ -class HubEnvironment: - # BASE_URL = "http://127.0.0.1:8000/" - BASE_URL = "https://payments.openbb.co/" - HUB_URL = "https://my.openbb.co/" - - def __new__(cls, *args, **kwargs): # pylint: disable=W0613 - """Create a singleton instance of the backend.""" - if not hasattr(cls, "instance"): - cls.instance = super().__new__(cls) # pylint: disable=E1120 - return cls.instance - - -BackendEnvironment = HubEnvironment() - -DEFAULT_ROUTINES_URL = "https://openbb-cms.directus.app/items/Routines" - -TIMEOUT = 30 -CONNECTION_ERROR_MSG = "[red]Connection error.[/red]" -CONNECTION_TIMEOUT_MSG = "[red]Connection timeout.[/red]" - -SCRIPT_TAGS = [ - "stocks", - "crypto", - "etf", - "economy", - "forex", - "fixed income", - "alternative", - "funds", - "bonds", - "macro", - "mutual funds", - "equities", - "options", - "dark pool", - "shorts", - "insider", - "behavioral analysis", - "fundamental analysis", - "technical analysis", - "quantitative analysis", - "forecasting", - "government", - "comparison", - "nft", - "on chain", - "off chain", - "screener", - "report", - "overview", - "rates", - "econometrics", - "portfolio", - "real estate", -] diff --git a/openbb_terminal/core/session/current_system.py b/openbb_terminal/core/session/current_system.py deleted file mode 100644 index f4396455f4f..00000000000 --- a/openbb_terminal/core/session/current_system.py +++ /dev/null @@ -1,42 +0,0 @@ -# IMPORTS STANDARD -import dataclasses -from copy import deepcopy -from typing import Any - -# IMPORTS INTERNAL -from openbb_terminal.core.models import SystemModel -from openbb_terminal.core.session.env_handler import read_env -from openbb_terminal.core.session.utils import load_dict_to_model - - -def get_current_system() -> SystemModel: - """Get current system.""" - return deepcopy(__system) - - -def set_current_system(system: SystemModel): - """Set current system.""" - global __system # pylint: disable=global-statement# noqa: PLW0603 - __system = system - - -def set_system_variable( - name: str, - value: Any, -): - """Set system variable - - Parameters - ---------- - name : str - Variable name - value : Any - Variable value - """ - current_system = get_current_system() - updated_system = dataclasses.replace(current_system, **{name: value}) # type: ignore - set_current_system(updated_system) - - -__env_dict = read_env() -__system = load_dict_to_model(__env_dict, SystemModel) diff --git a/openbb_terminal/core/session/current_user.py b/openbb_terminal/core/session/current_user.py deleted file mode 100644 index fe6cc057e46..00000000000 --- a/openbb_terminal/core/session/current_user.py +++ /dev/null @@ -1,146 +0,0 @@ -# IMPORTS STANDARD -import dataclasses -from copy import deepcopy -from pathlib import Path -from typing import Any, Dict, Optional - -# IMPORTS INTERNAL -from openbb_terminal.core.models import ( - CredentialsModel, - PreferencesModel, - ProfileModel, - SourcesModel, - UserModel, -) -from openbb_terminal.core.session.env_handler import read_env -from openbb_terminal.core.session.sources_handler import load_file_to_model -from openbb_terminal.core.session.utils import load_dict_to_model - -__env_dict = read_env() -__credentials = load_dict_to_model(__env_dict, CredentialsModel) # type: ignore -__preferences = load_dict_to_model(__env_dict, PreferencesModel) -__sources = load_file_to_model(Path(__preferences.USER_DATA_SOURCES_FILE)) - -__profile = ProfileModel() -__local_user = UserModel( # type: ignore - credentials=__credentials, # type: ignore - preferences=__preferences, # type: ignore - profile=__profile, - sources=__sources, -) -__current_user = __local_user - - -def get_current_user() -> UserModel: - """Get current user.""" - return deepcopy(__current_user) - - -def set_current_user(user: UserModel): - """Set current user.""" - global __current_user # pylint: disable=global-statement # noqa - __current_user = user - - -def get_env_dict() -> dict: - """Get env dict.""" - return deepcopy(__env_dict) - - -def is_local() -> bool: - """Check if user is guest. - - Returns - ------- - bool - True if user is guest, False otherwise. - """ - return not bool(__current_user.profile.token) - - -def set_default_user(): - """Set default user.""" - env_dict = read_env() - credentials = load_dict_to_model(env_dict, CredentialsModel) - preferences = load_dict_to_model(env_dict, PreferencesModel) - sources = load_file_to_model(Path(__preferences.USER_DATA_SOURCES_FILE)) - profile = ProfileModel() - default_user = UserModel( # type: ignore - credentials=credentials, - preferences=preferences, - profile=profile, - sources=sources, - ) - set_current_user(default_user) - - -def copy_user( - sources: Optional[SourcesModel] = None, - credentials: Optional[CredentialsModel] = None, # type: ignore - preferences: Optional[PreferencesModel] = None, - profile: Optional[ProfileModel] = None, - user: Optional[UserModel] = None, -): - current_user = user or get_current_user() - sources = sources or current_user.sources - credentials = credentials or current_user.credentials - preferences = preferences or current_user.preferences - profile = profile or current_user.profile - - user_copy = UserModel( # type: ignore - credentials=credentials, - preferences=preferences, - profile=profile, - sources=sources, - ) - - return user_copy - - -def set_preference( - name: str, - value: Any, -): - """Set preference - - Parameters - ---------- - name : str - Preference name - value : Any - Preference value - """ - current_user = get_current_user() - updated_preferences = dataclasses.replace(current_user.preferences, **{name: value}) # type: ignore - updated_user = dataclasses.replace(current_user, preferences=updated_preferences) # type: ignore - set_current_user(updated_user) - - -def set_credential(name: str, value: str): - """Set credential - - Parameters - ---------- - name : str - Credential name - value : str - Credential value - """ - current_user = get_current_user() - updated_credentials = dataclasses.replace(current_user.credentials, **{name: value}) # type: ignore - updated_user = dataclasses.replace(current_user, credentials=updated_credentials) # type: ignore - set_current_user(updated_user) - - -def set_sources(choices: Dict): - """Set sources - - Parameters - ---------- - choices : Dict - Sources dict - """ - current_user = get_current_user() - updated_sources = dataclasses.replace(current_user.sources, choices=choices) # type: ignore - updated_user = dataclasses.replace(current_user, sources=updated_sources) # type: ignore - set_current_user(updated_user) diff --git a/openbb_terminal/core/session/env_handler.py b/openbb_terminal/core/session/env_handler.py deleted file mode 100644 index 66213eca38d..00000000000 --- a/openbb_terminal/core/session/env_handler.py +++ /dev/null @@ -1,59 +0,0 @@ -# IMPORTS STANDARD -import sys -from typing import Any, Dict, Optional - -# IMPORTS THIRDPARTY -from dotenv import dotenv_values, set_key - -# IMPORTS INTERNAL -from openbb_terminal.core.config.paths import ( - PACKAGE_ENV_FILE, - REPOSITORY_ENV_FILE, - SETTINGS_ENV_FILE, -) - -DEFAULT_ORDER = [SETTINGS_ENV_FILE, PACKAGE_ENV_FILE, REPOSITORY_ENV_FILE] - - -def get_reading_order() -> list: - """Get order of .env files. If we are on frozen app, we reverse the order to - read the SETTINGS_ENV_FILE last. - - Returns - ------- - list - List of .env files. - """ - local_order = DEFAULT_ORDER.copy() - if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): - local_order.reverse() - return local_order - - -def read_env() -> Dict[str, Any]: - __env_dict: Dict[str, Optional[str]] = {} - - for env_file in get_reading_order(): - if env_file.exists(): - __env_dict.update(**dotenv_values(env_file)) - - __env_dict_filtered = { - k[len("OPENBBB_") - 1 :]: v - for k, v in __env_dict.items() - if k.startswith("OPENBB_") - } - - return __env_dict_filtered - - -def write_to_dotenv(name: str, value: str) -> None: - """Write to .env file. - - Parameters - ---------- - name : str - Name of the variable. - value : str - Value of the variable. - """ - set_key(str(SETTINGS_ENV_FILE), name, str(value)) diff --git a/openbb_terminal/core/session/hub_model.py b/openbb_terminal/core/session/hub_model.py deleted file mode 100644 index 1103cb18682..00000000000 --- a/openbb_terminal/core/session/hub_model.py +++ /dev/null @@ -1,846 +0,0 @@ -import datetime -import json -from typing import Any, Dict, List, Literal, Optional -from uuid import UUID - -import requests -from jose import jwt - -from openbb_terminal.core.session.constants import ( - CONNECTION_ERROR_MSG, - CONNECTION_TIMEOUT_MSG, - DEFAULT_ROUTINES_URL, - TIMEOUT, - BackendEnvironment, -) -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.rich_config import console - - -def create_session( - email: str, - password: str, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """Create a session. - - Parameters - ---------- - email : str - The email. - password : str - The password. - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - The response from the login request. - """ - try: - data = { - "email": email, - "password": password, - "remember": True, - } - return requests.post(url=base_url + "login", json=data, timeout=timeout) - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("\n[red]Failed to request login info.[/red]") - return None - - -def check_token_expiration(token: str): - """Raises ExpiredSignatureError if the token is expired.""" - header_data = jwt.get_unverified_header(token) - - tok = jwt.decode( - token, - key="", - algorithms=[header_data["alg"]], - options={"verify_signature": False, "verify_exp": True}, - ) - expiration_time = datetime.datetime.fromtimestamp(tok["exp"]) - console.print(f"Token expires at {expiration_time}") - - -def create_session_from_token( - token: str, base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT -): - """Create a session from token. - - Parameters - ---------- - token : str - The token. - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - """ - - try: - check_token_expiration(token) - data = { - "token": token, - } - return requests.post(url=base_url + "sdk/login", json=data, timeout=timeout) - except jwt.ExpiredSignatureError: - console.print( - "\n[red]Token expired. Please regenerate on the OpenBB Hub (my.openbb.co).[/red]" - ) - return None - except requests.exceptions.ConnectionError: - console.print("\n[red]Connection error.[/red]") - return None - except requests.exceptions.Timeout: - console.print("\n[red]Connection timeout.[/red]") - return None - except Exception: - console.print("\n[red]Failed to request login info.[/red]") - return None - - -def delete_session( - auth_header: str, - token: str, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """Delete the session. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - token : str - The token to delete. - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - The response from the logout request. - """ - try: - response = requests.get( - url=base_url + "logout", - headers={"Authorization": auth_header}, - json={"token": token}, - timeout=timeout, - ) - if response.status_code != 200: - console.print("[red]Failed to delete server session.[/red]") - return response - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[bold red]Failed to delete server session.[/bold red]") - return None - - -def process_session_response(response: requests.Response) -> dict: - """Process the response from the login request. - - Parameters - ---------- - response : requests.Response - The response from the login request. - - Returns - ------- - dict - The login info. - """ - if response.status_code == 200: - login = response.json() - return login - if response.status_code == 401: - console.print("\n[red]Wrong credentials.[/red]") - return {} - if response.status_code == 403: - console.print("\n[red]Unverified email.[/red]") - return {} - console.print("\n[red]Failed to login.[/red]") - return {} - - -def get_session(email: str, password: str) -> dict: - """Get the session info. - - Parameters - ---------- - email : str - The email. - password : str - The password. - - Returns - ------- - dict - The session info. - """ - response = create_session(email, password, base_url=BackendEnvironment.BASE_URL) - if response is None: - return {} - return process_session_response(response) - - -def get_session_from_token(token: str) -> dict: - """Get the session info from token. - - Parameters - ---------- - token : str - The token. - - Returns - ------- - dict - The session info. - """ - response = create_session_from_token(token, base_url=BackendEnvironment.BASE_URL) - if response is None: - return {} - return process_session_response(response) - - -def fetch_user_configs( - session: dict, base_url: str = BackendEnvironment.BASE_URL, timeout: int = TIMEOUT -) -> Optional[requests.Response]: - """Fetch user configurations. - - Parameters - ---------- - session : dict - The session info. - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - The response from the get request. - """ - token_type = session.get("token_type", "") - token = session.get("access_token", "") - - try: - response = requests.get( - url=base_url + "terminal/user", - headers={"Authorization": f"{token_type.title()} {token}"}, - timeout=timeout, - ) - if response.status_code not in [200, 401]: # 401: invalid token - console.print("[red]\nFailed to fetch configurations.[/red]") - return response - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]\nFailed to fetch configurations.[/red]") - return None - - -def clear_user_configs( - config: str, - auth_header: str, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """Clear user configurations to the server. - - Parameters - ---------- - config : str - The config to clear. - auth_header : str - The authorization header, e.g. "Bearer ". - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - The response from the put request. - """ - data: Dict[str, dict] = {config: {}} - - try: - response = requests.put( - url=base_url + "user", - headers={"Authorization": auth_header}, - json=data, - timeout=timeout, - ) - if response.status_code == 200: - console.print("[green]Cleared data.[/green]") - else: - console.print("[red]Failed to clear data.[/red]") - return response - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]Failed to clear data.[/red]") - return None - - -def upload_user_field( - key: str, - value: Any, - auth_header: str, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, - silent: bool = False, -) -> Optional[requests.Response]: - """Send user field to the server. - - Parameters - ---------- - key : str - The key to put, e.g. 'features_settings', 'features_keys', 'features_sources'. - value : Any - The value to put. - auth_header : str - The authorization header, e.g. "Bearer ". - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - silent : bool - Whether to silence the console output, by default False - - Returns - ------- - Optional[requests.Response] - The response from the put request. - """ - console_print = console.print if not silent else lambda *args, **kwargs: None - try: - data: Dict[str, dict] = {key: value} - - console_print("Sending to OpenBB hub...") - response = requests.put( - url=base_url + "user", - headers={"Authorization": auth_header}, - json=data, - timeout=timeout, - ) - if response.status_code == 200: - console_print("[green]Saved remotely.[/green]") - else: - console_print("[red]Failed to save remotely.[/red]") - return response - except requests.exceptions.ConnectionError: - console_print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console_print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console_print("[red]Failed to save remotely.[/red]") - return None - - -def upload_config( - key: str, - value: str, - type_: Literal["settings", "terminal_style"], - auth_header: str, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """Patch user configurations to the server. - - Parameters - ---------- - key : str - The key to patch. - value : str - The value to patch. - type_ : Literal["settings", "terminal_style"] - The type of the patch. - auth_header : str - The authorization header, e.g. "Bearer ". - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - The response from the patch request. - """ - if type_ not in ["settings", "terminal_style"]: - console.print("[red]\nInvalid patch type.[/red]") - return None - - data = {"key": f"features_{type_}.{key}", "value": value} - - try: - console.print("Sending to OpenBB hub...") - response = requests.patch( - url=BackendEnvironment.BASE_URL + "terminal/user", - headers={"Authorization": auth_header}, - json=data, - timeout=timeout, - ) - if response.status_code == 200: - console.print("[green]Saved remotely.[/green]") - else: - console.print("[red]Failed to save remotely.[/red]") - return response - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]Failed to save remotely.[/red]") - return None - - -# pylint: disable=too-many-arguments -def upload_routine( - auth_header: str, - name: str = "", - description: str = "", - routine: str = "", - override: bool = False, - tags: str = "", - public: bool = False, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """Send a routine to the server. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - name : str - The name of the routine. - routine : str - The routine. - override : bool - Whether to override the routine if it already exists. - tags : str - The tags of the routine. - public : bool - Whether to make the routine public or not. - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - The response from the post request. - """ - data = { - "name": name, - "description": description, - "script": routine, - "override": override, - "tags": tags, - "version": get_current_system().VERSION, - "public": public, - } - try: - response = requests.post( - headers={"Authorization": auth_header}, - url=base_url + "terminal/script", - json=data, - timeout=timeout, - ) - if response.status_code == 200: - username = get_current_user().profile.username - console.print("[green]Successfully uploaded your routine.[/]") - - run_env = BackendEnvironment.HUB_URL.rstrip("/") - - if public: - console.print( - f"\n[yellow]Share or edit it at {run_env}/u/{username}/routine/{name.replace(' ', '-')}[/]" - ) - else: - console.print(f"\n[yellow]Go to {run_env} to edit this script,[/]") - console.print( - f"[yellow]or even make it public so you can access it at " - f"{run_env}/u/{username}/routine/{name.replace(' ', '-')}[/]" - ) - elif response.status_code != 409: # 409: routine already exists - console.print( - "[red]" + response.json().get("detail", "Unknown error.") + "[/red]" - ) - return response - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]Failed to upload your routine.[/red]") - return None - - -def download_routine( - auth_header: str, - uuid: UUID, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """Download a routine from the server. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - uuid : UUID - The uuid of the routine. - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - The response from the get request. - """ - try: - response = requests.get( - headers={"Authorization": auth_header}, - url=base_url + "terminal/script/" + uuid, # type: ignore - timeout=timeout, - ) - if response.status_code == 404: - console.print("[red]Routine not found.[/red]") - elif response.status_code != 200: - console.print("[red]Failed to download your routine.[/red]") - return response - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]Failed to download your routine.[/red]") - return None - - -def delete_routine( - auth_header: str, - uuid: UUID, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """Delete a routine from the server. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - uuid : UUID - The uuid of the routine. - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - The response from the delete request. - """ - try: - response = requests.delete( - headers={"Authorization": auth_header}, - url=base_url + "terminal/script/" + uuid, # type: ignore - timeout=timeout, - ) - if response.status_code == 200: - console.print("[green]Successfully deleted your routine.[/green]") - elif response.status_code == 404: - console.print("[red]Routine not found.[/red]") - else: - console.print("[red]Failed to delete your routine.[/red]") - return response - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]Failed to delete your routine.[/red]") - return None - - -def list_routines( - auth_header: str, - fields: Optional[List[str]] = None, - page: int = 1, - size: int = 10, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, - silent: bool = False, -) -> Optional[requests.Response]: - """List all routines from the server. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - fields : Optional[List[str]] - The fields to return, by default None - page : int - The page number. - size : int - The number of routines per page. - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - silent : bool - Whether to silence the console output, by default False - - Returns - ------- - Optional[requests.Response] - The response from the get request. - """ - console_print = console.print if not silent else lambda *args, **kwargs: None - try: - if fields is None: - fields = ["name", "description", "version", "updated_date"] - - fields_str = "%2C".join(fields) - response = requests.get( - headers={"Authorization": auth_header}, - url=f"{base_url}terminal/script?fields={fields_str}&page={page}&size={size}", - timeout=timeout, - ) - if response.status_code != 200: - console_print("[red]Failed to list your routines.[/red]") - return response - except requests.exceptions.ConnectionError: - console_print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console_print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console_print("[red]Failed to list your routines.[/red]") - return None - - -def get_default_routines( - url: str = DEFAULT_ROUTINES_URL, timeout: int = TIMEOUT, silent: bool = False -): - """Get the default routines from CMS. - - Parameters - ---------- - timeout : int - The timeout, by default TIMEOUT - silent : bool - Whether to silence the console output, by default False - - Returns - ------- - Optional[requests.Response] - The response from the get request. - """ - console_print = console.print if not silent else lambda *args, **kwargs: None - try: - response = requests.get( - url=url, - timeout=timeout, - ) - if response.status_code != 200: - console_print("[red]Failed to get default routines.[/red]") - return response - except requests.exceptions.ConnectionError: - console_print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console_print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console_print("[red]Failed to get default routines.[/red]") - return None - - -def generate_personal_access_token( - auth_header: str, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, - days: int = 30, -) -> Optional[requests.Response]: - """ - Generate an OpenBB Personal Access Token. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - days : int - The number of days the token should be valid for. - - Returns - ------- - Optional[requests.Response] - """ - - url = f"{base_url}sdk/token" - - payload = json.dumps({"days": days}) - headers = { - "Authorization": auth_header, - "Content-Type": "application/json", - } - - try: - response = requests.put(url=url, headers=headers, data=payload, timeout=timeout) - - if response.status_code != 200: - console.print("[red]Failed to generate personal access token.[/red]") - - return response - - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]Failed to generate personal access token.[/red]") - return None - - -def get_personal_access_token( - auth_header: str, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """ - Show the user's OpenBB Personal Access Token. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - """ - - url = f"{base_url}sdk/token" - - headers = {"Authorization": auth_header} - - try: - response = requests.get(url=url, headers=headers, timeout=timeout) - - if response.status_code == 404: - console.print("[red]No personal access token found.[/red]") - elif response.status_code != 200: - console.print("[red]Failed to get personal access token.[/red]") - - return response - - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]Failed to get personal access token.[/red]") - return None - - -def revoke_personal_access_token( - auth_header: str, - base_url: str = BackendEnvironment.BASE_URL, - timeout: int = TIMEOUT, -) -> Optional[requests.Response]: - """ - Delete the user's OpenBB Personal Access Token. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - base_url : str - The base url, by default BASE_URL - timeout : int - The timeout, by default TIMEOUT - - Returns - ------- - Optional[requests.Response] - """ - - url = f"{base_url}sdk/token" - - headers = {"Authorization": auth_header} - - try: - response = requests.delete(url=url, headers=headers, timeout=timeout) - - if response.status_code not in [200, 202]: - console.print("[red]Failed to revoke personal access token.[/red]") - - return response - - except requests.exceptions.ConnectionError: - console.print(f"\n{CONNECTION_ERROR_MSG}") - return None - except requests.exceptions.Timeout: - console.print(f"\n{CONNECTION_TIMEOUT_MSG}") - return None - except Exception: - console.print("[red]Failed to revoke personal access token.[/red]") - return None diff --git a/openbb_terminal/core/session/local_model.py b/openbb_terminal/core/session/local_model.py deleted file mode 100644 index 8836950649f..00000000000 --- a/openbb_terminal/core/session/local_model.py +++ /dev/null @@ -1,234 +0,0 @@ -import json -import os -import shutil -from pathlib import Path -from typing import List, Optional - -from openbb_terminal.core.config.paths import ( - MISCELLANEOUS_DIRECTORY, - SESSION_FILE_PATH, -) -from openbb_terminal.core.models.sources_model import get_allowed_sources -from openbb_terminal.core.session.current_user import ( - get_env_dict, - set_credential, - set_preference, - set_sources, -) -from openbb_terminal.core.session.sources_handler import merge_sources -from openbb_terminal.rich_config import console - - -def save_session(data: dict, file_path: Path = SESSION_FILE_PATH): - """Save the login info to a file. - - Parameters - ---------- - data : dict - The data to write. - file_path : Path - The file path. - """ - try: - with open(file_path, "w") as file: - file.write(json.dumps(data)) - except Exception: - console.print("[red]Failed to save session info.[/red]") - - -def get_session(file_path: Path = SESSION_FILE_PATH) -> dict: - """Get the session info from the file. - - Parameters - ---------- - file_path : Path - The file path. - - Returns - ------- - dict - The session info. - """ - try: - if os.path.isfile(file_path): - with open(file_path) as file: - return json.load(file) - except Exception: - console.print("\n[red]Failed to get login info.[/red]") - return {} - - -def remove(path: Path) -> bool: - """Remove path. - - Parameters - ---------- - path : Path - The file path. - - Returns - ------- - bool - The status of the removal. - """ - - try: - if os.path.isfile(path): - os.remove(path) - elif os.path.isdir(path): - shutil.rmtree(path) - return True - except Exception: - console.print( - f"\n[bold red]Failed to remove {path}" - "\nPlease delete this manually![/bold red]" - ) - return False - - -def update_flair(username: str): - """Update the flair. - - Parameters - ---------- - username : str - The username. - """ - if "FLAIR" not in get_env_dict(): - MAX_FLAIR_LEN = 20 - flair = "[" + username[:MAX_FLAIR_LEN] + "]" + " 🦋" - set_preference("FLAIR", flair) - - -def apply_configs(configs: dict): - """Apply configurations. - - Parameters - ---------- - configs : dict - The configurations. - """ - # Saving the RICH_STYLE state allows user to change the default from 'hub' style to - # some custom .richstyle.json file - set_credentials_from_hub(configs) - set_preferences_from_hub(configs, fields=["RICH_STYLE"]) - set_rich_style_from_hub(configs) - set_chart_style_from_hub(configs) - set_table_style_from_hub(configs) - set_sources_from_hub(configs) - - -def set_credentials_from_hub(configs: dict): - """Set credentials from hub. - - Parameters - ---------- - configs : dict - The configurations. - """ - if configs: - credentials = configs.get("features_keys", {}) or {} - credentials = {k: v for k, v in credentials.items() if v} - for k, v in credentials.items(): - set_credential(k, v) - - -def set_preferences_from_hub(configs: dict, fields: Optional[List[str]] = None): - """Set preferences from hub. - - Parameters - ---------- - configs : dict - The configurations. - fields : Optional[List[str]] - The fields to set, if None, all fields will be set. - """ - if configs: - preferences = configs.get("features_settings", {}) or {} - for k, v in preferences.items(): - if not fields or k in fields: - set_preference(k, v) - - -def set_rich_style_from_hub(configs: dict): - """Set rich style from hub. - - Parameters - ---------- - configs : dict - The configurations. - """ - if configs: - terminal_style = configs.get("features_terminal_style", {}) or {} - if terminal_style: - rich_style = terminal_style.get("theme", None) - if rich_style: - rich_style = {k: v.replace(" ", "") for k, v in rich_style.items()} - try: - with open( - MISCELLANEOUS_DIRECTORY - / "styles" - / "user" - / "hub.richstyle.json", - "w", - ) as f: - json.dump(rich_style, f) - - # Default to hub theme - preferences = configs.get("features_settings", {}) or {} - if "RICH_STYLE" not in preferences: - set_preference("RICH_STYLE", "hub") - - except Exception: - console.print("[red]Failed to set rich style.[/red]") - - -def set_chart_style_from_hub(configs: dict): - """Set chart style from hub. - - Parameters - ---------- - configs : dict - The configurations. - """ - if configs: - terminal_style = configs.get("features_terminal_style", {}) or {} - if terminal_style: - chart_style = terminal_style.get("chart", None) - if chart_style: - set_preference("CHART_STYLE", chart_style) - # pylint: disable=import-outside-toplevel - from openbb_terminal import theme - - theme.apply_style(chart_style) - - -def set_table_style_from_hub(configs: dict): - """Set table style from hub. - - Parameters - ---------- - configs : dict - The configurations. - """ - if configs: - terminal_style = configs.get("features_terminal_style", {}) or {} - if terminal_style: - table_style = terminal_style.get("table", None) - if table_style: - set_preference("TABLE_STYLE", table_style) - - -def set_sources_from_hub(configs: dict): - """Set sources from hub. - - Parameters - ---------- - configs : dict - The configurations. - """ - if configs: - incoming = configs.get("features_sources", {}) or {} - if incoming: - choices = merge_sources(incoming=incoming, allowed=get_allowed_sources()) - set_sources(choices) diff --git a/openbb_terminal/core/session/routines_handler.py b/openbb_terminal/core/session/routines_handler.py deleted file mode 100644 index 56f185aaf2d..00000000000 --- a/openbb_terminal/core/session/routines_handler.py +++ /dev/null @@ -1,216 +0,0 @@ -import os -from os import walk -from pathlib import Path -from typing import Dict, List, Optional, Tuple, Union - -import numpy as np -import pandas as pd - -import openbb_terminal.core.session.hub_model as Hub -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.rich_config import console - - -# created dictionaries for personal and default routines with the structure -# {"file_name" :["script","personal/default"]} -# and stored dictionaries in list -def download_routines(auth_header: str, silent: bool = False) -> list: - """Download default and personal routines. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - silent : bool - Whether to silence the console output, by default False - - Returns - ------- - Dict[str, str] - The routines. - """ - personal_routines_dict = {} - default_routines_dict = {} - - try: - response = Hub.get_default_routines(silent=silent) - if response and response.status_code == 200: - content = response.json() - data = content.get("data", []) - for routine in data: - name = routine.get("name", "") - if name: - default_routines_dict[name] = [routine.get("script", ""), "default"] - except Exception: - console.print("[red]\nFailed to download default routines.[/red]") - - try: - # Number of routines downloaded is limited to 100 - response = Hub.list_routines( - auth_header=auth_header, - fields=["name", "script"], - page=1, - size=100, - base_url=Hub.BackendEnvironment.BASE_URL, - silent=silent, - ) - if response and response.status_code == 200: - content = response.json() - items = content.get("items", []) - for routine in items: - name = routine.get("name", "") - if name: - personal_routines_dict[name] = [ - routine.get("script", ""), - "personal", - ] - except Exception: - console.print("[red]\nFailed to download personal routines.[/red]") - - return [personal_routines_dict, default_routines_dict] - - -# use os.walk to search subdirectories and then construct file path -def read_routine(file_name: str, folder: Optional[Path] = None) -> Optional[str]: - """Read the routine. - - Parameters - ---------- - file_name : str - The routine. - folder : Optional[Path] - The routines folder. - - Returns - ------- - file_name : str - The routine. - folder : Optional[Path] - The routines folder. - """ - - current_user = get_current_user() - if folder is None: - folder = current_user.preferences.USER_ROUTINES_DIRECTORY - - try: - user_folder = folder / "hub" - for path, _, files in walk(user_folder): - file_path = ( - folder / os.path.relpath(path, folder) / file_name - if file_name in files - else folder / file_name - ) - with open(file_path) as f: - routine = "".join(f.readlines()) - return routine - except Exception: - console.print("[red]Failed to find routine.[/red]") - return None - - -# created new directory structure to account for personal and default routines -def save_routine( - file_name: str, - routine: list, - folder: Optional[Path] = None, - force: bool = False, - silent: bool = False, -) -> Union[Optional[Path], str]: - """Save the routine. - - Parameters - ---------- - file_name : str - The routine. - routine : str - The routine. - folder : Path - The routines folder. - force : bool - Force the save. - silent : bool - Whether to silence the console output, by default False - - Returns - ------- - Optional[Path, str] - The path to the routine or None. - """ - console_print = console.print if not silent else lambda *args, **kwargs: None - - current_user = get_current_user() - if folder is None: - folder = current_user.preferences.USER_ROUTINES_DIRECTORY - - try: - user_folder = folder / "hub" - if routine[1] == "default": - user_folder = folder / "hub" / "default" - elif routine[1] == "personal": - user_folder = folder / "hub" / "personal" - - if not os.path.exists(user_folder): - os.makedirs(user_folder) - - file_path = user_folder / file_name - if os.path.exists(file_path) and not force: - return "File already exists" - with open(file_path, "w") as f: - f.write(routine[0]) - return user_folder / file_name - except Exception: - console_print("[red]\nFailed to save routine.[/red]") - return None - - -def get_default_routines_info(routines: List[Dict[str, str]]) -> pd.DataFrame: - """Get the routines list. - - Parameters - ---------- - response : requests.Response - The response. - - Returns - ------- - Tuple[pd.DataFrame, int, int] - The routines list, the current page and the total number of pages. - """ - df = pd.DataFrame() - if routines: - df = pd.DataFrame(routines) - if all( - c in df.columns for c in ["name", "description", "version", "date_updated"] - ): - df = df[["name", "description", "version", "date_updated"]] - df.index = np.arange(1, len(df) + 1) - return df - - -def get_personal_routines_info(response) -> Tuple[pd.DataFrame, int, int]: - """Get the routines list. - - Parameters - ---------- - response : requests.Response - The response. - - Returns - ------- - Tuple[pd.DataFrame, int, int] - The routines list, the current page and the total number of pages. - """ - df = pd.DataFrame() - page = 1 - pages = 1 - if response and response.status_code == 200: - data = response.json() - page = data.get("page", 1) - pages = data.get("pages", 1) - items = data.get("items", []) - if items: - df = pd.DataFrame(items) - df.index = np.arange(1, len(df) + 1) - - return df, page, pages diff --git a/openbb_terminal/core/session/sdk_session.py b/openbb_terminal/core/session/sdk_session.py deleted file mode 100644 index e531794317e..00000000000 --- a/openbb_terminal/core/session/sdk_session.py +++ /dev/null @@ -1,132 +0,0 @@ -import logging - -from openbb_terminal.core.session import ( - local_model as Local, - session_model, -) -from openbb_terminal.core.session.current_user import ( - get_current_user, - is_local, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console -from openbb_terminal.terminal_helper import print_guest_block_msg - -logger = logging.getLogger(__name__) - -console_print = console.print - - -def set_silent_console_print(silent: bool = False): - global console_print # pylint: disable=global-statement # noqa - console_print = console.print if not silent else lambda *args, **kwargs: None - - -def get_session(email: str, password: str, token: str, save: bool) -> dict: - session = dict() - - if token: - console_print("Creating session from token.") - session = session_model.create_session_from_token(token, save) # type: ignore - - if not session and email: - console_print("Creating session from email and password.") - session = session_model.create_session(email, password, save) # type: ignore - - if not (isinstance(session, dict) and session): - console_print("[red]Failed to create session.[/red]") - - return session - - -@log_start_end(log=logger) -def login( - email: str = "", - password: str = "", - token: str = "", - keep_session: bool = False, - silent: bool = False, -): - """ - Login and load user info. - If there is a saved session it will be used (this can be achieved by `keep_session=True`). - If there's not a local session, - the user can use either email and password or the OpenBB Personal Access Token. - - Parameters - ---------- - email : str - The email. - password : str - The password. - token : str - The OpenBB Personal Access Token. - keep_session : bool - Keep the session, i.e., next time the user logs in, - there is no need to enter the email and password or the token. - silent : bool - If True, the console print will be silent. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.login(email="your_email", password="your_password") - """ - set_silent_console_print(silent=silent) - - session = {} - if not (email or token): - session = Local.get_session() - - if not session: - session = get_session(email, password, token, keep_session) - else: - console_print("Using local session to login.") - - if not session: - console_print("[red]Failed to obtain session.[/red]") - else: - status = session_model.login(session) - if status != session_model.LoginStatus.SUCCESS: - console_print(f"[red]Login failed with status `{status.value}`.[/red]") - else: - console_print("[green]Login successful.[/green]") - - -@log_start_end(log=logger) -def logout(): - """ - Logout and clear session. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.logout() - """ - current_user = get_current_user() - session_model.logout( - auth_header=current_user.profile.get_auth_header(), - token=current_user.profile.token, - ) - - -@log_start_end(log=logger) -def whoami(): - """ - Display user info. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.whoami() - """ - set_silent_console_print(silent=False) - - current_user = get_current_user() - local_user = is_local() - if not local_user: - console_print(f"[info]email:[/info] {current_user.profile.email}") - console_print(f"[info]username:[/info] {current_user.profile.username}") - console_print(f"[info]uuid:[/info] {current_user.profile.uuid}") - else: - print_guest_block_msg() diff --git a/openbb_terminal/core/session/session_controller.py b/openbb_terminal/core/session/session_controller.py deleted file mode 100644 index ecc4118cc09..00000000000 --- a/openbb_terminal/core/session/session_controller.py +++ /dev/null @@ -1,198 +0,0 @@ -from typing import Dict, List, Optional, Tuple - -from prompt_toolkit import PromptSession - -import openbb_terminal.core.session.local_model as Local -from openbb_terminal.core.config.paths import PACKAGE_DIRECTORY -from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.session.constants import BackendEnvironment -from openbb_terminal.core.session.current_system import ( - get_current_system, - set_system_variable, -) -from openbb_terminal.core.session.session_model import ( - LoginStatus, - create_session, - login, -) -from openbb_terminal.rich_config import console -from openbb_terminal.terminal_helper import bootup, is_installer - - -def display_welcome_message(links: bool = True) -> None: - """Display welcome message""" - with open(PACKAGE_DIRECTORY / "core" / "session" / "banner.txt") as f: - console.print(f"[menu]{f.read()}[/menu]\n") - if links: - console.print( - f"Register : [cmds]{BackendEnvironment.HUB_URL + 'register'}[/cmds]" - ) - console.print( - f"Support : [cmds]{BackendEnvironment.HUB_URL + 'app/terminal/support'}[/cmds]" - ) - - -def get_user_input() -> Tuple[str, str, bool]: - """Get user input - - Returns - ------- - Tuple[str, str, bool] - The user email, password and save login option. - """ - - msg = "\nPlease enter your credentials" - - if not is_installer(): - msg += " or press for guest mode:" - else: - msg += ":" - - console.print("[info]" + msg + "[/info]") - - s: PromptSession = PromptSession() - - email = s.prompt( - message="> Email/Username: ", - ) - if not email: - return "", "", False - - password = s.prompt( - message="> Password: ", - is_password=True, - ) - remember = ( - s.prompt(message="> Remember me? (y/n): ", is_password=False).lower() == "y" - ) - - return email, password, remember - - -def pywry_login(welcome: bool = True): - """Login using PyWry window and launch terminal if login is successful. - - Parameters - ---------- - welcome : bool, optional - Display welcome message, by default True - """ - bootup() - - plots_backend().start(get_current_system().DEBUG_MODE) - if plots_backend().isatty: - if welcome: - display_welcome_message(False) - response = plots_backend().call_hub() or None - else: - return prompt_cli(welcome) - - if response is None: - if is_installer(): - return prompt_cli(welcome=False) - return launch_terminal() - - if isinstance(response, dict) and response: - console.print("\n[info]Logging in... Please wait.[/info]\n") - response["token_type"] = "bearer" # noqa: S105 - - for r_key, new_key in zip( - ["status", "accessToken", "primaryUsage"], - ["information_complete", "access_token", "primary_usage"], - ): - response[new_key] = response.pop(r_key, None) - - response["primary_usage"] = response.get("primary_usage", None) or "personal" - - if remember := ( - response.get("remember", False) or response.get("is_oauth", False) - ): - response["remember"] = remember - Local.save_session(response) - - return login_and_launch(response, remember) - - return pywry_login(welcome=False) - - -def prompt_cli(welcome: bool = True): - """Prompt and launch terminal if login is successful. - - Parameters - ---------- - welcome : bool, optional - Display welcome message, by default True - """ - bootup() - - if welcome: - display_welcome_message() - - while True: - email, password, remember = get_user_input() - if not email: - return prompt_cli(welcome=False) if is_installer() else launch_terminal() - - session = create_session(email, password, remember) - if isinstance(session, dict) and session: - return login_and_launch(session, remember) - - -# pylint: disable=inconsistent-return-statements -def launch_terminal( - debug: bool = False, dev: bool = False, queue: Optional[List[str]] = None -): - """Launch terminal""" - # pylint: disable=import-outside-toplevel - from openbb_terminal import terminal_controller - - if queue: - return terminal_controller.main(debug, dev, queue, module="") - - terminal_controller.parse_args_and_run() - - -def login_and_launch(session: dict, remember: bool = False): - """Login and launch terminal. - - Parameters - ---------- - session : dict - The session info. - remember : bool, optional - Remember the session, by default False - """ - status = login(session, remember) - if status in [LoginStatus.SUCCESS, LoginStatus.NO_RESPONSE]: - launch_terminal() - elif status == LoginStatus.FAILED: - pywry_login(welcome=False) - else: - pywry_login(welcome=True) - - -def main( - session: Optional[Dict] = None, - welcome: bool = True, - prompt: bool = True, - queue: Optional[List[str]] = None, - dev: bool = False, -): - """Main function""" - if dev: - set_system_variable("DEV_BACKEND", True) - BackendEnvironment.BASE_URL = "https://payments.openbb.dev/" - BackendEnvironment.HUB_URL = "https://my.openbb.dev/" - - local_session = Local.get_session() if session is None else session - - if not local_session and not prompt: - launch_terminal(queue=queue) - elif local_session: - login_and_launch(session=local_session, remember=True) - else: - pywry_login(welcome) - - -if __name__ == "__main__": - main() diff --git a/openbb_terminal/core/session/session_model.py b/openbb_terminal/core/session/session_model.py deleted file mode 100644 index 5140932fe8e..00000000000 --- a/openbb_terminal/core/session/session_model.py +++ /dev/null @@ -1,214 +0,0 @@ -import json -import sys -from enum import Enum -from typing import Any, Dict, Optional - -import matplotlib.pyplot as plt - -import openbb_terminal.core.session.hub_model as Hub -import openbb_terminal.core.session.local_model as Local -from openbb_terminal.base_helpers import remove_log_handlers -from openbb_terminal.core.config.paths import HIST_FILE_PATH, SESSION_FILE_PATH -from openbb_terminal.core.models.user_model import ProfileModel, SourcesModel, UserModel -from openbb_terminal.core.session.current_user import ( - get_current_user, - set_current_user, - set_default_user, -) -from openbb_terminal.core.session.routines_handler import ( - download_routines, - save_routine, -) -from openbb_terminal.core.session.sources_handler import get_updated_hub_sources -from openbb_terminal.core.session.utils import run_thread -from openbb_terminal.helper_funcs import system_clear -from openbb_terminal.loggers import setup_logging -from openbb_terminal.rich_config import console - -# pylint: disable=consider-using-f-string - - -class LoginStatus(Enum): - SUCCESS = "success" - FAILED = "failed" - NO_RESPONSE = "no_response" - UNAUTHORIZED = "unauthorized" - - -def create_session(email: str, password: str, save: bool) -> Dict[Any, Any]: - """Create a session. - - Parameters - ---------- - email : str - The email. - password : str - The password. - save : bool - Save the session. - """ - - session = Hub.get_session(email, password) - if session and save: - Local.save_session(session) - return session - - -def create_session_from_token(token: str, save: bool) -> Dict[Any, Any]: - """Create a session from token. - - Parameters - ---------- - token : str - The token. - save : bool - Save the session. - """ - - session = Hub.get_session_from_token(token) - if session and save: - Local.save_session(session) - return session - - -def login(session: Dict, remember: bool = False) -> LoginStatus: - """Login and load user info. - - Parameters - ---------- - session : dict - The session info. - remember : bool, optional - Remember the session, by default False - """ - # Create a new user: - # credentials: stored in hub, but we fallback to local (.env) - # profile: stored in hub, so we set default here - # preferences: stored locally, so we use the current user preferences - # sources: stored in hub, so we set default here - - hub_user = UserModel( # type: ignore - credentials=get_current_user().credentials, - profile=ProfileModel(), - preferences=get_current_user().preferences, - sources=SourcesModel(), - ) - response = Hub.fetch_user_configs(session, Hub.BackendEnvironment.BASE_URL) - if response is not None: - if response.status_code == 200: - configs = json.loads(response.content) - email = configs.get("email", "") - hub_user.profile.load_user_info(session, email, remember) - set_current_user(hub_user) - - auth_header = hub_user.profile.get_auth_header() - if sys.stdin.isatty(): - download_and_save_routines(auth_header) - run_thread( - update_backend_sources, - {"auth_header": auth_header, "configs": configs}, - ) - Local.apply_configs(configs) - Local.update_flair(get_current_user().profile.username) - - return LoginStatus.SUCCESS - if response.status_code == 401: - return LoginStatus.UNAUTHORIZED - return LoginStatus.FAILED - return LoginStatus.NO_RESPONSE - - -def download_and_save_routines(auth_header: str): - """Download and save routines. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - """ - routines = download_routines(auth_header=auth_header) - personal_routines_dict = routines[0] - default_routines_dict = routines[1] - try: - for name, content in personal_routines_dict.items(): - save_routine(file_name=f"{name}.openbb", routine=content, force=True) - for name, content in default_routines_dict.items(): - save_routine(file_name=f"{name}.openbb", routine=content, force=True) - except Exception: - console.print("[red]\nFailed to save routines.[/red]") - - -def update_backend_sources(auth_header, configs, silent: bool = True): - """Update backend sources if new source or command path available. - - Parameters - ---------- - auth_header : str - The authorization header, e.g. "Bearer ". - configs : Dict - Dictionary with configs - silent : bool - Whether to silence the console output, by default True - """ - console_print = console.print if not silent else lambda *args, **kwargs: None - - try: - updated_sources = get_updated_hub_sources(configs) - if updated_sources: - Hub.upload_user_field( - key="features_sources", - value=updated_sources, - auth_header=auth_header, - base_url=Hub.BackendEnvironment.BASE_URL, - silent=silent, - ) - except Exception: - console_print("[red]Failed to update backend sources.[/red]") - - -def logout( - auth_header: Optional[str] = None, - token: Optional[str] = None, - cls: bool = False, -): - """Logout and clear session. - - Parameters - ---------- - auth_header : str, optional - The authorization header, e.g. "Bearer ". - token : str, optional - The token to delete. - In the terminal we want to delete the current session, so we use the user own token. - cls : bool - Clear the screen. - """ - if cls: - system_clear() - - if not auth_header or not token: - return - - success = True - - r = Hub.delete_session(auth_header, token, base_url=Hub.BackendEnvironment.BASE_URL) - if not r or r.status_code != 200: - success = False - - if not Local.remove(SESSION_FILE_PATH): - success = False - - if not Local.remove(HIST_FILE_PATH): - success = False - - if not Local.remove(get_current_user().preferences.USER_ROUTINES_DIRECTORY / "hub"): - success = False - - remove_log_handlers() - set_default_user() - setup_logging() - - plt.close("all") - - if success: - console.print("[green]\nLogout successful.[/green]") diff --git a/openbb_terminal/core/session/sources_handler.py b/openbb_terminal/core/session/sources_handler.py deleted file mode 100644 index ac1dd91b4f1..00000000000 --- a/openbb_terminal/core/session/sources_handler.py +++ /dev/null @@ -1,114 +0,0 @@ -import json -import os -from pathlib import Path -from typing import Dict - -from openbb_terminal.core.models.sources_model import SourcesModel, get_allowed_sources -from openbb_terminal.core.sources.utils import extend, flatten - - -def read_sources(path: Path) -> Dict: - """Read sources from file. - - Parameters - ---------- - path : str - Path to file - - Returns - ------- - Dict - Dictionary with sources - """ - try: - if path.exists() and os.stat(path).st_size > 0: - with open(path) as file: - return flatten(json.load(file)) - return {} - except Exception as e: - print(f"\nFailed to read data sources file: {path}\n{e}\n") # noqa: T201 - print("Falling back to OpenBB default sources.") # noqa: T201 - return {} - - -def write_sources(sources: Dict, path: Path): - """Write sources to file. - - Parameters - ---------- - sources : Dict - Dictionary with sources - path : Path - Path to file - """ - try: - with open(path, "w") as f: - json.dump(extend(sources), f, indent=4) - except Exception as e: - print(f"\nFailed to write data sources file: {path}\n{e}\n") # noqa: T201 - - -def merge_sources(incoming: Dict, allowed: Dict): - """Update sources dict if command and sources allowed. - - Parameters - ---------- - incoming : Dict - Dictionary with incoming sources - allowed : Dict - Dictionary with allowed sources - - Returns - ------- - Dict - Updated sources - """ - choices = allowed.copy() - - for cmd_path, incoming_sources in incoming.items(): - if cmd_path in allowed: - allowed_sources = allowed[cmd_path] - filtered_incoming = [s for s in incoming_sources if s in allowed_sources] - remaining = [s for s in allowed_sources if s not in incoming_sources] - choices[cmd_path] = filtered_incoming + remaining - return choices - - -def load_file_to_model(path: Path) -> SourcesModel: - """Load sources model from file. - - Parameters - ---------- - path : Path - Path to file - - Returns - ------- - SourcesModel - Sources model - """ - choices = merge_sources(incoming=read_sources(path), allowed=get_allowed_sources()) - return SourcesModel(choices=choices) # type: ignore - - -def get_updated_hub_sources(configs: Dict) -> Dict: - """Update hub sources if new source or command path available. - - Parameters - ---------- - default : Dict - Dictionary with default sources - hub : Dict - Dictionary with hub sources - - Returns - ------- - Dict - Updated hub sources - """ - if configs: - incoming = configs.get("features_sources", {}) or {} - if incoming: - return merge_sources(incoming=incoming, allowed=get_allowed_sources()) - return {} - return {} diff --git a/openbb_terminal/core/session/utils.py b/openbb_terminal/core/session/utils.py deleted file mode 100644 index 5d20941c541..00000000000 --- a/openbb_terminal/core/session/utils.py +++ /dev/null @@ -1,60 +0,0 @@ -from threading import Thread -from typing import Any, Callable, Dict, Type, TypeVar - -from pydantic import ValidationError - -from openbb_terminal.core.models import BaseModel - -T = TypeVar("T", bound=BaseModel) - - -def load_dict_to_model(dictionary: dict, model: Type[T]) -> T: - """Load variables to model. - - Parameters - ---------- - dictionary : dict - Variables dictionary. - model : Type[T] - Model to load. - - Returns - ------- - T - Model with validated data. - """ - model_name = model.__name__.strip("Model").lower() - try: - return model(**dictionary) # type: ignore - except ValidationError as error: - print(f"Error loading {model_name}:") # noqa: T201 - for err in error.errors(): - loc = err.get("loc", None) - var_name = str(loc[0]) if loc else "" - msg = err.get("msg", "") - var = dictionary.pop(var_name, None) - fields: dict[str, Any] = model.get_fields() - if var and var_name in fields: - default = fields[var_name].default - print( # noqa: T201 - f" {var_name}: {msg}, using default -> {default}" - ) - - return model(**dictionary) # type: ignore - except Exception: - print(f"Error loading {model_name}, using defaults.") # noqa: T201 - return model() # type: ignore - - -def run_thread(target: Callable, kwargs: Dict[str, Any]): - """Run a daemon thread, with the given target and keyword arguments. - - Parameters - ---------- - target : Callable - The target function. - kwargs : Dict[str, Any] - The keyword arguments. - """ - thread = Thread(target=target, kwargs=kwargs, daemon=True) - thread.start() diff --git a/openbb_terminal/core/sources/utils.py b/openbb_terminal/core/sources/utils.py deleted file mode 100644 index 02f69c63674..00000000000 --- a/openbb_terminal/core/sources/utils.py +++ /dev/null @@ -1,121 +0,0 @@ -from collections.abc import MutableMapping -from functools import reduce -from typing import Any, Dict, List, Tuple - - -def nested_dict(test_str, sep, value) -> Dict: - if sep not in test_str: - return {test_str: value} - key, val = test_str.split(sep, 1) - return {key: nested_dict(val, sep, value)} - - -def get_list_of_dicts(d: Dict[str, str], sep: str) -> List[Dict[str, Any]]: - """Get list of dictionaries from a dictionary. - - Parameters - ---------- - d : dict - The dictionary to convert. - - Returns - ------- - List[Dict[str, Any]] - The list of dictionaries. - """ - list_of_dicts = [] - for k, v in d.items(): - list_of_dicts.append(nested_dict(k, sep, v)) - return list_of_dicts - - -def recursive_merge(d1: Dict, d2: Dict) -> Dict: - """Recursively merge dict d2 into dict d1. - - Source: https://stackoverflow.com/questions/7204805/how-to-merge-dictionaries-of-dictionaries - - Parameters - ---------- - d1 : Dict - The dictionary to be merged into. - d2 : Dict - The dictionary to merge into d1. - - Returns - ------- - Dict - The merged dictionary. - """ - for k, v in d1.items(): - if k in d2 and all(isinstance(e, MutableMapping) for e in (v, d2[k])): - d2[k] = recursive_merge(v, d2[k]) - - d3 = d1.copy() - d3.update(d2) - return d3 - - -def merge_dicts(list_of_dicts: List[Dict[str, Any]]) -> Dict[str, Any]: - """Merge a list of dictionaries. - - Parameters - ---------- - list_of_dicts : List[Dict[str, Any]] - The list of dictionaries to merge. - - Returns - ------- - Dict[str, Any] - The merged dictionary. - """ - result: Dict[str, Any] = {} - for d in list_of_dicts: - result = reduce(recursive_merge, (result, d)) - return result - - -def extend(d: Dict[str, str], sep: str = "/") -> Dict[str, Any]: - """Extend a dictionary to nested dictionaries. - - Parameters - ---------- - d : Dict[str, str] - The dictionary to extend. - sep : str, optional - The separator to use, by default "/" - - Returns - ------- - Dict[str, Any] - The extended dictionary. - """ - return merge_dicts(get_list_of_dicts(d, sep)) - - -def flatten(d, parent_key="", sep="/") -> Dict[str, Any]: - """Flatten a dictionary. - - Source: https://stackoverflow.com/questions/6027558/flatten-nested-dictionaries-compressing-keys - - Parameters - ---------- - d : Dict - The dictionary to flatten. - parent_key : str, optional - The parent key, by default "" - sep : str, optional - The separator to use, by default "/" - - Returns - ------- - Dict[str, Any] - The flattened dictionary. - """ - items: List[Tuple[str, List[str]]] = [] - for k, v in d.items(): - new_key = parent_key + sep + k if parent_key else k - if isinstance(v, MutableMapping): - items.extend(flatten(v, new_key, sep=sep).items()) - else: - items.append((new_key, v)) - return dict(items) diff --git a/openbb_terminal/cryptocurrency/README.md b/openbb_terminal/cryptocurrency/README.md deleted file mode 100644 index d34b4661832..00000000000 --- a/openbb_terminal/cryptocurrency/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Learning Materials - -Key trends, people, companies, and projects to watch across the crypto landscape, with predictions for 2022 : diff --git a/openbb_terminal/cryptocurrency/__init__.py b/openbb_terminal/cryptocurrency/__init__.py deleted file mode 100644 index 9653203a6ad..00000000000 --- a/openbb_terminal/cryptocurrency/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# flake8: noqa -from . import defi, discovery, onchain, overview, technical_analysis diff --git a/openbb_terminal/cryptocurrency/coinbase_helpers.py b/openbb_terminal/cryptocurrency/coinbase_helpers.py deleted file mode 100644 index 5fd0ed6e572..00000000000 --- a/openbb_terminal/cryptocurrency/coinbase_helpers.py +++ /dev/null @@ -1,183 +0,0 @@ -"""Coinbase helpers model""" - -__docformat__ = "numpy" - -import argparse -import base64 -import binascii -import hashlib -import hmac -import logging -import time -from typing import Any, Optional, Union - -from requests.auth import AuthBase - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -class CoinbaseProAuth(AuthBase): - """Authorize CoinbasePro requests. Source: https://docs.pro.coinbase.com/?python#signing-a-message""" - - def __init__(self, api_key, secret_key, passphrase): - self.api_key = api_key - self.secret_key = secret_key - self.passphrase = passphrase - - def __call__(self, request_coinbase): - timestamp = str(time.time()) - message = ( - timestamp - + request_coinbase.method - + request_coinbase.path_url - + (request_coinbase.body or "") - ) - message = message.encode("ascii") - - try: - hmac_key = base64.b64decode(self.secret_key) - signature = hmac.new(hmac_key, message, hashlib.sha256) - signature_b64 = base64.b64encode(signature.digest()) - except binascii.Error as e: - logger.exception(str(e)) - signature_b64 = "" - - request_coinbase.headers.update( - { - "CB-ACCESS-SIGN": signature_b64, - "CB-ACCESS-TIMESTAMP": timestamp, - "CB-ACCESS-KEY": self.api_key, - "CB-ACCESS-PASSPHRASE": self.passphrase, - "Content-Type": "application/json", - } - ) - return request_coinbase - - -class CoinbaseRequestException(Exception): - """Coinbase Request Exception object""" - - def __init__(self, message: str): - super().__init__(message) - self.message = message - - def __str__(self) -> str: - return f"CoinbaseRequestException: {self.message}" - - -class CoinbaseApiException(Exception): - """Coinbase API Exception object""" - - def __init__(self, message: str): - super().__init__(message) - self.message = message - - def __str__(self) -> str: - return f"CoinbaseApiException: {self.message}" - - -def check_validity_of_product(product_id: str) -> str: - """Helper method that checks if provided product_id exists. It's a pair of coins in format COIN-COIN. - If product exists it return it, in other case it raise an error. [Source: Coinbase] - - Parameters - ---------- - product_id: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - - Returns - ------- - str - pair of coins in format COIN-COIN - """ - - products = [pair["id"] for pair in make_coinbase_request("/products")] - if product_id.upper() not in products: - raise argparse.ArgumentTypeError( - f"You provided wrong pair of coins {product_id}. " - f"It should be provided as a pair in format COIN-COIN e.g UNI-USD" - ) - return product_id.upper() - - -def make_coinbase_request( - endpoint, params: Optional[dict] = None, auth: Optional[Any] = None -) -> dict: - """Request handler for Coinbase Pro Api. Prepare a request url, params and payload and call endpoint. - [Source: Coinbase] - - Parameters - ---------- - endpoint: str - Endpoint path e.g /products - params: dict - Parameter dedicated for given endpoint - auth: any - Api credentials for purpose of using endpoints that needs authentication - - Returns - ------- - dict - response from Coinbase Pro Api - """ - - url = "https://api.pro.coinbase.com" - response = request(url + endpoint, params=params, auth=auth) - - if not 200 <= response.status_code < 300: - raise CoinbaseApiException(f"Invalid Authentication: {response.text}") - try: - return response.json() - except ValueError as e: - logger.exception(str(e)) - raise CoinbaseRequestException(f"Invalid Response: {response.text}") from e - - -def _get_account_coin_dict() -> dict: - """Helper method that returns dictionary with all symbols and account ids in dictionary format. [Source: Coinbase] - - Returns - ------- - dict: - Your accounts in coinbase - {'1INCH': '0c29b708-d73b-4e1c-a58c-9c261cb4bedb', 'AAVE': '0712af66-c069-45b5-84ae-7b2347c2fd24', ..} - - """ - current_user = get_current_user() - auth = CoinbaseProAuth( - current_user.credentials.API_COINBASE_KEY, - current_user.credentials.API_COINBASE_SECRET, - current_user.credentials.API_COINBASE_PASS_PHRASE, - ) - accounts = make_coinbase_request("/accounts", auth=auth) - return {acc["currency"]: acc["id"] for acc in accounts} - - -def _check_account_validity(account: str) -> Union[str, Any]: - """Helper methods that checks if given account exists. [Source: Coinbase] - - Parameters - ---------- - account: str - coin or account id - - Returns - ------- - Union[str, Any] - Your account id or None - """ - - accounts = _get_account_coin_dict() - - if account in list(accounts.keys()): - return accounts[account] - - if account in list(accounts.values()): - return account - - console.print("Wrong account id or coin symbol") - return None diff --git a/openbb_terminal/cryptocurrency/coinpaprika_helpers.py b/openbb_terminal/cryptocurrency/coinpaprika_helpers.py deleted file mode 100644 index 7a0f6cd4b76..00000000000 --- a/openbb_terminal/cryptocurrency/coinpaprika_helpers.py +++ /dev/null @@ -1,64 +0,0 @@ -"""CoinPaprika helpers""" - -__docformat__ = "numpy" - -from typing import Any, Optional - -import requests -from requests.adapters import HTTPAdapter - - -class PaprikaSession: - PAPRIKA_BASE_URL = "https://api.coinpaprika.com/v1" - - ENDPOINTS = { - "global": "/global", - "coin": "/coins/{}", - "coins": "/coins", - "coin_tweeter": "/coins/{}/twitter", - "coin_events": "/coins/{}/events", - "coin_exchanges": "/coins/{}/exchanges", - "coin_markets": "/coins/{}/markets", - "ohlcv": "/coins/{}/ohlcv/latest", - "ohlcv_hist": "/coins/{}/ohlcv/historical", - "people": "/people/{}", - "tickers": "/tickers", - "ticker_info": "/tickers/{}", - "exchanges": "/exchanges", - "exchange_info": "/exchanges/{}", - "exchange_markets": "/exchanges/{}/markets", - "contract_platforms": "/contracts", - "contract_platform_addresses": "/contracts/{}", - "search": "/search", - } - - def __init__(self, max_retries: int = 5): - self.session = requests.Session() - self.session.mount(self.PAPRIKA_BASE_URL, HTTPAdapter(max_retries=max_retries)) - - def make_request( - self, endpoint: str, payload: Optional[Any] = None, **kwargs: Any - ) -> dict: - """Helper method that handles request for coinpaprika api. - It prepares URL for given endpoint and payload if it's part of requests - - Parameters - ---------- - endpoint: str, - it's an endpoint that we want to query. e.g. to get twitter data for given coin we need to use: - https://api.coinpaprika.com/v1/coins/{}/twitter - payload: dict - the body of your request. Contains the data send to the CoinPaprika API when making an API request - kwargs: - additional parameters that will be added to payload - Returns - ---------- - dict with response data - """ - - url = f"{self.PAPRIKA_BASE_URL}{endpoint}/" - if payload is None: - payload = {} - if kwargs: - payload.update(kwargs) - return self.session.get(url, params=payload).json() diff --git a/openbb_terminal/cryptocurrency/crypto_controller.py b/openbb_terminal/cryptocurrency/crypto_controller.py deleted file mode 100644 index 8d34759869f..00000000000 --- a/openbb_terminal/cryptocurrency/crypto_controller.py +++ /dev/null @@ -1,546 +0,0 @@ -"""Cryptocurrency Context Controller""" - -__docformat__ = "numpy" -# pylint: disable=R0904, C0302, R1710, W0622, C0201, C0301 - -import argparse -import logging -import os -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency import cryptocurrency_helpers, pyth_model, pyth_view -from openbb_terminal.cryptocurrency.crypto_views import find -from openbb_terminal.cryptocurrency.cryptocurrency_helpers import ( - display_all_coins, - plot_chart, -) -from openbb_terminal.cryptocurrency.due_diligence import ( - binance_view, - coinpaprika_view, - finbrain_crypto_view, - pycoingecko_view, -) -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - export_data, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import CryptoBaseController -from openbb_terminal.rich_config import MenuText, console - -# pylint: disable=import-outside-toplevel - - -logger = logging.getLogger(__name__) - -FIND_KEYS = ["id", "symbol", "name"] - -CRYPTO_SOURCES = { - "Binance": "Binance", - "CoingGecko": "CoinGecko", - "CoinPaprika": "CoinPaprika", - "Coinbase": "Coinbase", - "YahooFinance": "YahooFinance", -} - - -class CryptoController(CryptoBaseController): - """Crypto Controller""" - - CHOICES_COMMANDS = [ - "headlines", - "candle", - "load", - "find", - "prt", - "resources", - "price", - ] - CHOICES_MENUS = [ - "ta", - "dd", - "ov", - "disc", - "onchain", - "defi", - "tools", - "nft", - "qa", - "forecast", - ] - - DD_VIEWS_MAPPING = { - "CoingGecko": pycoingecko_view, - "CoinPaprika": coinpaprika_view, - "Binance": binance_view, - } - PATH = "/crypto/" - FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md") - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - choices["load"] = { - "--interval": { - c: {} - for c in [ - "1", - "5", - "15", - "30", - "60", - "240", - "1440", - "10080", - "43200", - ] - }, - "-i": "--interval", - "--exchange": {c: {} for c in self.exchanges}, - "--source": {c: {} for c in ["CCXT", "YahooFinance", "CoinGecko"]}, - "--vs": {c: {} for c in ["usd", "eur"]}, - "--start": None, - "-s": "--start", - "--end": None, - "-e": "--end", - } - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("crypto/") - mt.add_cmd("load") - mt.add_cmd("find") - mt.add_cmd("price", "Pyth") - mt.add_raw("\n") - mt.add_param( - "_symbol", f"{self.symbol.upper()}/{self.vs.upper()}" if self.symbol else "" - ) - if self.source == "CCXT": - mt.add_param( - "_exchange", self.exchange if self.symbol and self.exchange else "" - ) - mt.add_param("_source", self.source if self.symbol and self.source else "") - mt.add_param("_interval", self.current_interval) - mt.add_raw("\n") - mt.add_cmd("headlines") - mt.add_cmd("candle", self.symbol) - mt.add_cmd("prt", self.symbol) - mt.add_raw("\n") - mt.add_menu("disc") - mt.add_menu("ov") - mt.add_menu("onchain") - mt.add_menu("defi") - mt.add_menu("tools") - mt.add_menu("nft") - mt.add_menu("dd", self.symbol) - mt.add_menu("ta", self.symbol) - mt.add_menu("qa", self.symbol) - mt.add_menu("forecast", self.symbol) - console.print(text=mt.menu_text, menu="Cryptocurrency") - - @log_start_end(log=logger) - def call_prt(self, other_args): - """Process prt command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="prt", - description="Potential Returns Tool" - "Tool to check returns if loaded coin reaches provided price or other crypto market cap" - "Uses CoinGecko to grab coin data (price and market cap).", - ) - parser.add_argument( - "--vs", - help="Coin to compare with", - dest="vs", - type=str, - required="-h" not in other_args and "--help" not in other_args, - default=None, - ) - parser.add_argument( - "-p", - "--price", - help="Desired price", - dest="price", - type=int, - default=None, - ) - parser.add_argument( - "-t", - "--top", - help="Compare with top N coins", - dest="top", - type=int, - default=None, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--vs") - - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ): - if self.symbol: - num_args = 0 - for arg in vars(ns_parser): - if getattr(ns_parser, arg): - num_args = num_args + 1 - if num_args > 1: - console.print("[red]Please chose only one flag[/red]\n") - return - current_coin_id = cryptocurrency_helpers.get_coingecko_id(self.symbol) - if ns_parser.vs is not None: - coin_found = cryptocurrency_helpers.get_coingecko_id(ns_parser.vs) - if not coin_found: - console.print( - f"VS Coin '{ns_parser.vs}' not found in CoinGecko\n" - ) - return - else: - coin_found = None - if ( - ns_parser.vs is None - and ns_parser.top is None - and ns_parser.price is None - ): - console.print( - "[red]Please chose a flag: --top, --vs, or --price [/red]\n" - ) - return - pycoingecko_view.display_coin_potential_returns( - current_coin_id, - coin_found, - ns_parser.top, - ns_parser.price, - ) - else: - console.print("[red]Please load a coin first![/red]\n") - - @log_start_end(log=logger) - def call_price(self, other_args): - """Process price command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="price", - description="""Display price and interval of confidence in real-time. [Source: Pyth]""", - ) - parser.add_argument( - "-s", - "--symbol", - required="-h" not in other_args and "--help" not in other_args, - type=str, - dest="symbol", - help="Symbol of coin to load data for, ~100 symbols are available", - choices=pyth_model.ASSETS.keys(), - metavar="SYMBOL", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - - if ns_parser := self.parse_known_args_and_warn(parser, other_args): - upper_symbol = ns_parser.symbol.upper() - if "-USD" not in ns_parser.symbol: - upper_symbol += "-USD" - if upper_symbol in pyth_model.ASSETS: - console.print( - "[param]If it takes too long, you can use 'Ctrl + C' to cancel.\n[/param]" - ) - pyth_view.display_price(upper_symbol) - else: - console.print("[red]The symbol selected does not exist.[/red]\n") - - @log_start_end(log=logger) - def call_candle(self, other_args): - """Process candle command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="candle", - description="""Display chart for loaded coin. You can specify currency vs which you want - to show chart and also number of days to get data for.""", - ) - parser.add_argument( - "--log", - help="Plot with y axis on log scale", - action="store_true", - default=False, - dest="logy", - ) - - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ): - if not self.symbol: - console.print("No coin loaded. First use `load {symbol}`\n") - return - figure = plot_chart( - exchange=self.exchange, - source=self.source, - to_symbol=self.symbol, - from_symbol=self.current_currency, - prices_df=self.current_df, - interval=self.current_interval, - yscale="log" if ns_parser.logy else "linear", - external_axes=ns_parser.is_image, - ) - export_data( - ns_parser.export, - os.path.join(os.path.dirname(os.path.abspath(__file__))), - f"{self.symbol}", - self.current_df, - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None, - figure=figure, - ) - - @log_start_end(log=logger) - def call_ta(self, _): - """Process ta command""" - from openbb_terminal.cryptocurrency.technical_analysis.ta_controller import ( - TechnicalAnalysisController, - ) - - # TODO: Play with this to get correct usage - if self.symbol: - if self.current_currency != "" and not self.current_df.empty: - self.queue = self.load_class( - TechnicalAnalysisController, - stock=self.current_df, - coin=self.symbol, - start=self.current_df.index[0], - interval="", - queue=self.queue, - ) - - else: - console.print("No coin selected. Use 'load' to load a coin.\n") - - @log_start_end(log=logger) - def call_tools(self, _): - """Process tools command""" - from openbb_terminal.cryptocurrency.tools.tools_controller import ( - ToolsController, - ) - - self.queue = self.load_class(ToolsController, self.queue) - - @log_start_end(log=logger) - def call_disc(self, _): - """Process disc command""" - from openbb_terminal.cryptocurrency.discovery.discovery_controller import ( - DiscoveryController, - ) - - self.queue = self.load_class(DiscoveryController, self.queue) - - @log_start_end(log=logger) - def call_ov(self, _): - """Process ov command""" - from openbb_terminal.cryptocurrency.overview.overview_controller import ( - OverviewController, - ) - - self.queue = self.load_class(OverviewController, self.queue) - - @log_start_end(log=logger) - def call_defi(self, _): - """Process defi command""" - from openbb_terminal.cryptocurrency.defi.defi_controller import DefiController - - self.queue = self.load_class(DefiController, self.queue) - - @log_start_end(log=logger) - def call_headlines(self, other_args): - """Process sentiment command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="headlines", - description="""Display sentiment analysis from FinBrain for chosen Cryptocurrencies""", - ) - parser.add_argument( - "-c", - "--coin", - default="BTC", - type=str, - dest="coin", - help="Symbol of coin to load data for, ~100 symbols are available", - choices=finbrain_crypto_view.COINS, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ): - finbrain_crypto_view.display_crypto_sentiment_analysis( - symbol=ns_parser.coin, export=ns_parser.export - ) - - @log_start_end(log=logger) - def call_dd(self, _): - """Process dd command""" - if self.symbol: - from openbb_terminal.cryptocurrency.due_diligence.dd_controller import ( - DueDiligenceController, - ) - - self.queue = self.load_class( - DueDiligenceController, - self.symbol, - self.source, - queue=self.queue, - ) - else: - console.print("No coin selected. Use 'load' to load a coin.\n") - - @log_start_end(log=logger) - def call_qa(self, _): - """Process qa command""" - if self.symbol: - from openbb_terminal.cryptocurrency.quantitative_analysis import ( - qa_controller, - ) - - self.queue = self.load_class( - qa_controller.QaController, - self.symbol, - self.current_df, - self.queue, - ) - - @log_start_end(log=logger) - def call_onchain(self, _): - """Process onchain command""" - from openbb_terminal.cryptocurrency.onchain.onchain_controller import ( - OnchainController, - ) - - self.queue = self.load_class(OnchainController, self.queue) - - @log_start_end(log=logger) - def call_nft(self, _): - """Process nft command""" - from openbb_terminal.cryptocurrency.nft.nft_controller import NFTController - - self.queue = self.load_class(NFTController, self.queue) - - # TODO: merge the two views that this command calls. (find + previously called coins) - @log_start_end(log=logger) - def call_find(self, other_args): - """Process find command""" - parser = argparse.ArgumentParser( - prog="find", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Find similar coin by name, symbol, or id. If you don't remember exact name or id of the Coin at CoinGecko, - Binance, Coinbase or CoinPaprika you can use this command to display coins with similar name, symbol or id - to your search query. - Example of usage: coin name is something like "polka". So I can try: find -c polka -k name -t 25 - It will search for coin that has similar name to polka and display top 25 matches. - -c, --coin stands for coin - you provide here your search query - -k, --key it's a searching key. You can search by symbol, id or name of coin - -l, --limit it displays top N number of records. - coins: Shows list of coins available on CoinGecko, CoinPaprika and Binance.If you provide name of - coin then in result you will see ids of coins with best match for all mentioned services. - If you provide "ALL" in your coin search query, then all coins will be displayed. To move over coins you - can use pagination mechanism with skip, top params. E.g. coins ALL --skip 100 --limit 30 then all coins - from 100 to 130 will be displayed. By default skip = 0, limit = 10. - If you won't provide source of the data everything will be displayed (CoinGecko, CoinPaprika, Binance). - If you want to search only in given source then use --source flag. E.g. if you want to find coin with name - uniswap on CoinPaprika then use: coins uniswap --source cp --limit 10 - """, - ) - parser.add_argument( - "-c", - "--coin", - help="Symbol Name or Id of Coin", - dest="coin", - required="-h" not in other_args and "--help" not in other_args, - type=str, - ) - parser.add_argument( - "-k", - "--key", - dest="key", - help="Specify by which column you would like to search: symbol, name, id", - type=str, - choices=FIND_KEYS, - default="symbol", - ) - parser.add_argument( - "-l", - "--limit", - default=10, - dest="limit", - help="Number of records to display", - type=check_positive, - ) - parser.add_argument( - "-s", - "--skip", - default=0, - dest="skip", - help="Skip n of records", - type=check_positive, - choices=range(1, 300), - metavar="SKIP", - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - # TODO: merge find + display_all_coins - if ns_parser: - if ns_parser.coin == "ALL": - display_all_coins( - symbol=ns_parser.coin, - source=ns_parser.source, - limit=ns_parser.limit, - skip=ns_parser.skip, - show_all=True, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - find( - query=ns_parser.coin, - source=ns_parser.source, - key=ns_parser.key, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_forecast(self, _): - """Process forecast command""" - from openbb_terminal.forecast import forecast_controller - - console.print(self.symbol) - self.queue = self.load_class( - forecast_controller.ForecastController, - self.symbol, - self.current_df, - self.queue, - ) diff --git a/openbb_terminal/cryptocurrency/crypto_models.py b/openbb_terminal/cryptocurrency/crypto_models.py deleted file mode 100644 index 66958794960..00000000000 --- a/openbb_terminal/cryptocurrency/crypto_models.py +++ /dev/null @@ -1,146 +0,0 @@ -import difflib - -import pandas as pd - -from openbb_terminal.rich_config import console - -# TODO: Find better algorithm then difflib.get_close_matches to find most similar coins - - -def find( - query: str, - source: str = "CoinGecko", - key: str = "symbol", - limit: int = 10, -) -> pd.DataFrame: - """Find similar coin by coin name,symbol or id. - - If you don't know exact name or id of the Coin at CoinGecko CoinPaprika, Binance or Coinbase - you use this command to display coins with similar name, symbol or id to your search query. - Example: coin name is something like "polka". So I can try: find -c polka -k name -t 25 - It will search for coin that has similar name to polka and display top 25 matches. - - Parameters - ---------- - query: str - Cryptocurrency - source: str - Data source of coins. CoinGecko or CoinPaprika or Binance or Coinbase - key: str - Searching key (symbol, id, name) - limit: int - Number of records to display - - Returns - ------- - pd.DataFrame - DataFrame with 'limit' similar coins - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.crypto.find("polka", "CoinGecko", "name", 25) - """ - - df = pd.DataFrame() - - if source == "CoinGecko": - df = _find_CoinGecko(key=key, query=query, limit=limit) - - elif source == "CoinPaprika": - df = _find_CoinPaprika(key=key, query=query, limit=limit) - - elif source == "Binance": - df = _find_Binance(key=key, query=query, limit=limit) - - elif source == "Coinbase": - df = _find_Coinbase(key=key, query=query, limit=limit) - - else: - console.print( - "Couldn't execute find methods for CoinPaprika, Binance, Coinbase or CoinGecko\n" - ) - - return df - - -def _find_CoinGecko(key: str, query: str, limit: int) -> pd.DataFrame: - # pylint: disable=C0415 - from openbb_terminal.cryptocurrency.discovery.pycoingecko_model import get_coin_list - - coins_df = get_coin_list() - coins_list = coins_df[key].to_list() - if key in ["symbol", "id"]: - query = query.lower() - - sim = difflib.get_close_matches(query, coins_list, limit) - df = pd.Series(sim).to_frame().reset_index() - df.columns = ["index", key] - coins_df.drop("index", axis=1, inplace=True) - df = df.merge(coins_df, on=key) - - return df - - -def _find_CoinPaprika(key: str, query: str, limit: int) -> pd.DataFrame: - # pylint: disable=C0415 - from openbb_terminal.cryptocurrency.due_diligence.coinpaprika_model import ( - get_coin_list, - ) - - coins_df = get_coin_list() - coins_list = coins_df[key].to_list() - keys = {"name": "title", "symbol": "upper", "id": "lower"} - - func_key = keys[key] - query = getattr(query, str(func_key))() - - sim = difflib.get_close_matches(query, coins_list, limit) - df = pd.Series(sim).to_frame().reset_index() - df.columns = ["index", key] - df = df.merge(coins_df, on=key) - - return df - - -def _find_Binance(key: str, query: str, limit: int) -> pd.DataFrame: - # pylint: disable=C0415 - from openbb_terminal.cryptocurrency.cryptocurrency_helpers import load_binance_map - from openbb_terminal.cryptocurrency.discovery.pycoingecko_model import get_coin_list - - # TODO: Fix it in future. Determine if user looks for symbol like ETH or ethereum - if len(query) > 5: - key = "id" - - coins_df_gecko = get_coin_list() - coins_df_bin = load_binance_map() - coins = pd.merge(coins_df_bin, coins_df_gecko[["id", "name"]], how="left", on="id") - coins_list = coins[key].to_list() - - sim = difflib.get_close_matches(query, coins_list, limit) - df = pd.Series(sim).to_frame().reset_index() - df.columns = ["index", key] - df = df.merge(coins, on=key) - - return df - - -def _find_Coinbase(key: str, query: str, limit: int) -> pd.DataFrame: - # pylint: disable=C0415 - from openbb_terminal.cryptocurrency.cryptocurrency_helpers import load_coinbase_map - from openbb_terminal.cryptocurrency.discovery.pycoingecko_model import get_coin_list - - if len(query) > 5: - key = "id" - - coins_df_gecko = get_coin_list() - coins_df_bin = load_coinbase_map() - coins = pd.merge(coins_df_bin, coins_df_gecko[["id", "name"]], how="left", on="id") - coins_list = coins[key].to_list() - - sim = difflib.get_close_matches(query, coins_list, limit) - df = pd.Series(sim).to_frame().reset_index() - df.columns = ["index", key] - df = df.merge(coins, on=key) - - return df diff --git a/openbb_terminal/cryptocurrency/crypto_views.py b/openbb_terminal/cryptocurrency/crypto_views.py deleted file mode 100644 index caf8d5ce6c4..00000000000 --- a/openbb_terminal/cryptocurrency/crypto_views.py +++ /dev/null @@ -1,58 +0,0 @@ -import os -from typing import Optional - -from openbb_terminal.cryptocurrency import crypto_models -from openbb_terminal.helper_funcs import export_data, print_rich_table - - -def find( - query: str, - source: str = "CoinGecko", - key: str = "symbol", - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Find similar coin by coin name,symbol or id. - - If you don't know exact name or id of the Coin at CoinGecko CoinPaprika, Binance or Coinbase - you use this command to display coins with similar name, symbol or id to your search query. - Example: coin name is something like "polka". So I can try: find -c polka -k name -t 25 - It will search for coin that has similar name to polka and display top 25 matches. - - -c, --coin stands for coin - you provide here your search query - -k, --key it's a searching key. You can search by symbol, id or name of coin - -t, --top it displays top N number of records. - - Parameters - ---------- - query: str - Cryptocurrency - source: str - Data source of coins. CoinGecko or CoinPaprika or Binance or Coinbase - key: str - Searching key (symbol, id, name) - limit: int - Number of records to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = crypto_models.find(query=query, source=source, key=key, limit=limit) - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Similar Coins", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "find", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/cryptocurrency_helpers.py b/openbb_terminal/cryptocurrency/cryptocurrency_helpers.py deleted file mode 100644 index 2b9922602b3..00000000000 --- a/openbb_terminal/cryptocurrency/cryptocurrency_helpers.py +++ /dev/null @@ -1,1037 +0,0 @@ -"""Cryptocurrency helpers""" - -# pylint: disable=C0302,too-many-return-statements - -import difflib -import json -import logging -import os -from datetime import datetime, timedelta -from typing import Optional, Union - -import ccxt -import numpy as np -import pandas as pd -import yfinance as yf -from pycoingecko import CoinGeckoAPI - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.discovery import pycoingecko_model -from openbb_terminal.cryptocurrency.due_diligence import coinpaprika_model -from openbb_terminal.cryptocurrency.due_diligence.pycoingecko_model import ( - get_coin_tokenomics, - get_ohlc, -) -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -__docformat__ = "numpy" - -INTERVALS = ["1H", "3H", "6H", "1D"] - -CCXT_INTERVAL_MAP = { - "1": "1m", - "15": "15m", - "30": "30m", - "60": "1h", - "240": "4h", - "1440": "1d", - "10080": "1w", - "43200": "1M", -} - -SOURCES_INTERVALS = { - "Binance": [ - "1day", - "3day", - "1hour", - "2hour", - "4hour", - "6hour", - "8hour", - "12hour", - "1week", - "1min", - "3min", - "5min", - "15min", - "30min", - "1month", - ], - "Coinbase": [ - "1min", - "5min", - "15min", - "1hour", - "6hour", - "24hour", - "1day", - ], - "YahooFinance": [ - "1min", - "2min", - "5min", - "15min", - "30min", - "60min", - "90min", - "1hour", - "1day", - "5day", - "1week", - "1month", - "3month", - ], -} - - -YF_CURRENCY = [ - "CAD", - "CNY", - "ETH", - "EUR", - "GBP", - "INR", - "JPY", - "KRW", - "RUB", - "USD", - "AUD", - "BTC", -] - - -def check_datetime( - ck_date: Optional[Union[datetime, Union[str, None]]] = None, start: bool = True -) -> datetime: - """Checks if given argument is string and attempts to convert to datetime. - - Parameters - ---------- - ck_date : Union[datetime, Union[str, None]], optional - Date to check, by default None - start : bool, optional - If True and string is invalid, will return 1100 days ago - If False and string is invalid, will return today, by default True - - Returns - ------- - datetime - Datetime object - """ - error_catch = (datetime.now() - timedelta(days=1100)) if start else datetime.now() - try: - if ck_date is None: - return error_catch - if isinstance(ck_date, datetime): - return ck_date - if isinstance(ck_date, str): - return datetime.strptime(ck_date, "%Y-%m-%d") - except Exception: - console.print( - f"Invalid date format (YYYY-MM-DD), " - f"Using {error_catch.strftime('%Y-%m-%d')} for {ck_date}" - ) - return error_catch - - -def _load_coin_map(file_name: str) -> pd.DataFrame: - if file_name.split(".")[1] != "json": - raise TypeError("Please load json file") - - current_dir = os.path.dirname(os.path.abspath(__file__)) - path = os.path.join(current_dir, "data", file_name) - with open(path, encoding="utf8") as f: - coins = json.load(f) - - coins_df = pd.Series(coins).reset_index() - coins_df.columns = ["symbol", "id"] - return coins_df - - -def read_data_file(file_name: str): - if file_name.split(".")[1] != "json": - raise TypeError("Please load json file") - - current_dir = os.path.dirname(os.path.abspath(__file__)) - path = os.path.join(current_dir, "data", file_name) - with open(path, encoding="utf8") as f: - return json.load(f) - - -def load_coins_list(file_name: str, return_raw: bool = False) -> pd.DataFrame: - if file_name.split(".")[1] != "json": - raise TypeError("Please load json file") - - current_dir = os.path.dirname(os.path.abspath(__file__)) - path = os.path.join(current_dir, "data", file_name) - with open(path, encoding="utf8") as f: - coins = json.load(f) - if return_raw: - return coins - return pd.DataFrame(coins) - - -def load_binance_map(): - return _load_coin_map("binance_gecko_map.json") - - -def load_coinbase_map(): - return _load_coin_map("coinbase_gecko_map.json") - - -def prepare_all_coins_df() -> pd.DataFrame: - """Helper method which loads coins from all sources: CoinGecko, CoinPaprika, - Binance, Yahoo Finance and merge those coins on keys: - - CoinGecko - > name < - CoinPaprika - CoinGecko - > id <- Binance - - Returns - ------- - pd.DataFrame - CoinGecko - id for coin in CoinGecko API: uniswap - CoinPaprika - id for coin in CoinPaprika API: uni-uniswap - Binance - symbol (baseAsset) for coin in Binance API: UNI - Coinbase - symbol for coin in Coinbase Pro API e.g UNI - Yahoo Finance - symbol for coin in Yahoo Finance e.g. UNI1-USD - - Symbol: uni - """ - - gecko_coins_df = load_coins_list("coingecko_coins.json") - - paprika_coins_df = load_coins_list("coinpaprika_coins.json") - paprika_coins_df = paprika_coins_df[paprika_coins_df["is_active"]] - paprika_coins_df = paprika_coins_df[["rank", "id", "name", "symbol", "type"]] - yahoofinance_coins_df = load_coins_list("yahoofinance_coins.json") - - # TODO: Think about scheduled job, that once a day will update data - - binance_coins_df = load_binance_map().rename(columns={"symbol": "Binance"}) - coinbase_coins_df = load_coinbase_map().rename(columns={"symbol": "Coinbase"}) - gecko_coins_df.symbol = gecko_coins_df.symbol.str.upper() - - gecko_paprika_coins_df = pd.merge( - gecko_coins_df, paprika_coins_df, on="symbol", how="right" - ) - - df_merged = pd.merge( - left=gecko_paprika_coins_df, - right=binance_coins_df, - left_on="id_x", - right_on="id", - how="left", - ) - df_merged.rename( - columns={ - "id_x": "CoinGecko", - "symbol": "Symbol", - "id_y": "CoinPaprika", - }, - inplace=True, - ) - - df_merged = pd.merge( - left=df_merged, - right=coinbase_coins_df, - left_on="CoinGecko", - right_on="id", - how="left", - ) - - yahoofinance_coins_df.rename( - columns={ - "symbol": "Symbol", - }, - inplace=True, - ) - - yahoofinance_coins_df.Symbol = yahoofinance_coins_df.Symbol.str.upper() - - df_merged = pd.merge( - left=df_merged, - right=yahoofinance_coins_df[["Symbol", "id"]], - on="Symbol", - how="left", - ) - - df_merged.rename( - columns={ - "id": "YahooFinance", - }, - inplace=True, - ) - - return df_merged[ - ["CoinGecko", "CoinPaprika", "Binance", "Coinbase", "YahooFinance", "Symbol"] - ] - - -def _create_closest_match_df( - coin: str, coins: pd.DataFrame, limit: int, cutoff: float -) -> pd.DataFrame: - """Helper method. Creates a DataFrame with best matches for given coin found in given list of coins. - Based on difflib.get_close_matches func. - - Parameters - ---------- - coin: str - coin you search for - coins: list - list of coins in which you want to find similarities - limit: int - limit of matches - cutoff: float - float between <0, 1>. Show only coins matches with score higher then cutoff. - - Returns - ------- - pd.DataFrame - index, id, name, symbol - > depends on source of data. - """ - - coins_list = coins["id"].to_list() - sim = difflib.get_close_matches(coin, coins_list, limit, cutoff) - df = pd.Series(sim).to_frame().reset_index() - df.columns = ["index", "id"] - return df.merge(coins, on="id") - - -def get_coingecko_id(symbol: str): - client = CoinGeckoAPI() - coin_list = client.get_coins_list() - for coin in coin_list: - if coin["symbol"] == symbol.lower(): - return coin["id"] - return None - - -def load_from_ccxt( - symbol: str, - start_date: datetime = (datetime.now() - timedelta(days=1100)), - interval: str = "1440", - exchange: str = "binance", - to_symbol: str = "usdt", - end_date: datetime = datetime.now(), -) -> pd.DataFrame: - """Load crypto currency data [Source: https://github.com/ccxt/ccxt] - - Parameters - ---------- - symbol: str - Coin to get - start_date: datetime - The datetime to start at - interval: str - The interval between data points in minutes. - Choose from: 1, 15, 30, 60, 240, 1440, 10080, 43200 - exchange: str: - The exchange to get data from. - to_symbol: str - Quote Currency (Defaults to usdt) - end_date: datetime - The datetime to stop at - - Returns - ------- - pd.DataFrame - Dataframe consisting of price and volume data - """ - df = pd.DataFrame() - pair = f"{symbol.upper()}/{to_symbol.upper()}" - - try: - if interval not in list(CCXT_INTERVAL_MAP): - console.print( - f"\nInvalid interval chosen for source, " - f"available intervals: {', '.join(list(CCXT_INTERVAL_MAP))}\n" - ) - df = fetch_ccxt_ohlc( - exchange, - 3, - pair, - CCXT_INTERVAL_MAP[interval], - int(datetime.timestamp(start_date)) * 1000, - 1000, - ) - if df.empty: - console.print(f"\nPair {pair} not found in {exchange}\n") - return pd.DataFrame() - i = 0 - for date in df.index: - if date > end_date: - break - i += 1 - first_date = pd.to_datetime(str(df.index.values[0])) - df = df.iloc[0:i] - if df.empty: - console.print( - f"\nThe first data point retrieved was {first_date.strftime('%Y-%m-%d')}, " - f"thus the end date ({end_date.strftime('%Y-%m-%d')}) filtered out all of the data.\n" - ) - return pd.DataFrame() - except Exception: - console.print(f"\nPair {pair} not found on {exchange}\n") - return df - return df - - -def load_from_coingecko( - symbol: str, - start_date: datetime = (datetime.now() - timedelta(days=1100)), - to_symbol: str = "usdt", -) -> pd.DataFrame: - """Load crypto currency data [Source: https://www.coingecko.com/] - - Parameters - ---------- - symbol: str - Coin to get - start_date: datetime - The datetime to start at - to_symbol: str - Quote Currency (Defaults to usdt) - - Returns - ------- - pd.DataFrame - Dataframe consisting of price and volume data - """ - df = pd.DataFrame() - delta = datetime.now() - start_date - days = delta.days - - if days > 365: - console.print("Coingecko free tier only allows a max of 365 days\n") - days = 365 - - coingecko_id = get_coingecko_id(symbol) - if not coingecko_id: - console.print(f"{symbol} not found in Coingecko\n") - return df - - df = get_ohlc(coingecko_id, to_symbol, days) - df_coin = yf.download( - f"{symbol}-{to_symbol}", - end=datetime.now(), - start=start_date, - progress=False, - interval="1d", - ).sort_index(ascending=False) - - if not df_coin.empty: - df = pd.merge(df, df_coin[::-1][["Volume"]], left_index=True, right_index=True) - df.index.name = "date" - return df - - -def load_from_yahoofinance( - symbol: str, - start_date: datetime = (datetime.now() - timedelta(days=1100)), - interval: str = "1440", - to_symbol: str = "usdt", - end_date: datetime = datetime.now(), -) -> pd.DataFrame: - """Load crypto currency data [Source: https://finance.yahoo.com/] - - Parameters - ---------- - symbol: str - Coin to get - start_date: datetime - The datetime to start at - interval: str - The interval between data points in minutes. - Choose from: 1, 15, 30, 60, 240, 1440, 10080, 43200 - to_symbol: str - Quote Currency (Defaults to usdt) - end_date: datetime - The datetime to end at - - Returns - ------- - pd.DataFrame - Dataframe consisting of price and volume data - """ - pair = f"{symbol}-{to_symbol}" - if int(interval) >= 1440: - YF_INTERVAL_MAP = { - "1440": "1d", - "10080": "1wk", - "43200": "1mo", - } - df = yf.download( - pair, - end=end_date, - start=start_date, - progress=False, - interval=YF_INTERVAL_MAP[interval], - ).sort_index(ascending=True) - else: - s_int = str(interval) + "m" - d_granularity = {"1m": 6, "5m": 59, "15m": 59, "30m": 59, "60m": 729} - s_start_dt = datetime.utcnow() - timedelta(days=d_granularity[s_int]) - s_date_start = s_start_dt.strftime("%Y-%m-%d") - df = yf.download( - pair, - start=( - s_date_start - if s_start_dt > start_date - else start_date.strftime("%Y-%m-%d") - ), - progress=False, - interval=s_int, - ) - - open_sum = df["Open"].sum() - if open_sum == 0: - console.print(f"\nPair {pair} has invalid data on Yahoo Finance\n") - return pd.DataFrame() - - if df.empty: - console.print(f"\nPair {pair} not found in Yahoo Finance\n") - return pd.DataFrame() - df.index.name = "date" - return df - - -def load( - symbol: str, - start_date: Optional[Union[datetime, Union[str, None]]] = None, - interval: Union[str, int] = "1440", - exchange: str = "binance", - to_symbol: str = "usd", - end_date: Optional[Union[datetime, Union[str, None]]] = None, - source: str = "YahooFinance", -) -> pd.DataFrame: - """ - Load crypto currency to get data for - Note: - take into consideration that the data might not be found if the data provider - (example) uses `usd` instead of `usdt` as the quote currency, so you might need to adjust - the `to_symbol` parameter. - For `CCXT` a valid pair would be: "BTC/USDT" - For `CoinGecko` a valid pair would be: "BTC-USD" - For `YahooFinance` a valid pair would be: "BTC-USD" - - Parameters - ---------- - symbol: str - Coin to get - start_date: Union[datetime, Union[str, None]], optional - Start date to get data from with. - datetime or string format (YYYY-MM-DD) - interval: Union[str, int] - The interval between data points in minutes. - Choose from: 1, 15, 30, 60, 240, 1440, 10080, 43200 - exchange: str: - The exchange to get data from. - to_symbol: str - Quote Currency (Defaults to usdt) - end_date: Union[datetime, Union[str, None]], optional - End date to get data from with. - datetime or string format (YYYY-MM-DD) - source: str - The source of the data - Choose from: CCXT, CoinGecko, YahooFinance - - Returns - ------- - pd.DataFrame - Dataframe consisting of price and volume data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.crypto.load(symbol="btc",to_symbol="usd",start_date="2019-01-01",source="YahooFinance") - """ - if isinstance(interval, int): - interval = str(interval) - if start_date is None: - start_date = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - start_date = check_datetime(start_date) - end_date = check_datetime(end_date, start=False) - - if source == "CCXT": - return load_from_ccxt( - symbol, start_date, interval, exchange, to_symbol, end_date - ) - if source == "CoinGecko": - return load_from_coingecko(symbol, start_date, to_symbol) - if source == "YahooFinance": - return load_from_yahoofinance(symbol, start_date, interval, to_symbol, end_date) - console.print("[red]Invalid source sent[/red]\n") - return pd.DataFrame() - - -def show_quick_performance( - crypto_df: pd.DataFrame, - symbol: str, - current_currency: str, - source: str, - exchange: str, - interval: str, -): - """Show quick performance stats of crypto prices. Daily prices expected""" - closes = crypto_df["Close"] - volumes = crypto_df["Volume"] if "Volume" in crypto_df else pd.DataFrame() - - perfs = {} - if interval == "1440": - perfs = { - "1D": 100 * closes.pct_change(2)[-1], - "7D": 100 * closes.pct_change(7)[-1], - "1M": 100 * closes.pct_change(30)[-1], - "1Y": 100 * closes.pct_change(365)[-1], - } - first_day_current_year = str(datetime.now().date().replace(month=1, day=1)) - if first_day_current_year in closes.index: - closes_ytd = closes[closes.index > first_day_current_year] - perfs["YTD"] = 100 * (closes_ytd[-1] - closes_ytd[0]) / closes_ytd[0] - else: - perfs["Period"] = 100 * (closes[-1] - closes[0]) / closes[0] - - df = pd.DataFrame.from_dict(perfs, orient="index").dropna().T - df = df.applymap(lambda x: str(round(x, 2)) + " %") - df = df.applymap(lambda x: f"[red]{x}[/red]" if "-" in x else f"[green]{x}[/green]") - if len(closes) > 365: - df["Volatility (1Y)"] = ( - str(round(100 * np.sqrt(365) * closes[-365:].pct_change().std(), 2)) + " %" - ) - else: - df["Volatility (Ann)"] = ( - str(round(100 * np.sqrt(365) * closes.pct_change().std(), 2)) + " %" - ) - if len(volumes) > 7: - df["Volume (7D avg)"] = lambda_long_number_format(np.mean(volumes[-9:-2]), 2) - - df.insert(0, f"\nPrice ({current_currency.upper()})", closes[-1]) - - try: - coingecko_id = get_coingecko_id(symbol) - - coin_data_cg = get_coin_tokenomics(coingecko_id) - if not coin_data_cg.empty: - df.insert( - len(df.columns), - "Circulating Supply", - lambda_long_number_format( - int( - coin_data_cg.loc[ - coin_data_cg["Metric"] == "Circulating Supply" - ]["Value"] - ) - ), - ) - except Exception: # noqa: S110 - pass - - exchange_str = f"in {exchange.capitalize()}" if source == "ccxt" else "" - print_rich_table( - df, - show_index=False, - headers=df.columns, - title=f"{symbol.upper()}/{current_currency.upper()} Performance {exchange_str}", - print_to_console=True, - ) - console.print() - - -def load_yf_data(symbol: str, currency: str, interval: str, days: int): - df_coin = yf.download( - f"{symbol.upper()}-{currency.upper()}", - end=datetime.now(), - start=datetime.now() - timedelta(days=days), - progress=False, - interval=interval, - ignore_tz=True, - ).sort_index(ascending=False) - - df_coin.index.names = ["date"] - if df_coin.empty: - console.print( - f"Could not download data for {symbol}-{currency} from Yahoo Finance" - ) - return pd.DataFrame(), currency - - return df_coin[::-1], currency - - -def display_all_coins( - source: str, - symbol: str, - limit: int, - skip: int, - show_all: bool, - export: str, - sheet_name: str, -) -> None: - """Find similar coin by coin name,symbol or id. - If you don't remember exact name or id of the Coin at CoinGecko, CoinPaprika, Coinbase, Binance - you can use this command to display coins with similar name, symbol or id to your search query. - Example of usage: coin name is something like "polka". So I can try: find -c polka -k name -t 25 - It will search for coin that has similar name to polka and display top 25 matches. - -c, --coin stands for coin - you provide here your search query - -t, --top it displays top N number of records. - - Parameters - ---------- - limit: int - Number of records to display - symbol: str - Cryptocurrency - source: str - Data source of coins. CoinGecko (cg) or CoinPaprika (cp) or Binance (bin), Coinbase (cb) - skip: int - Skip N number of records - show_all: bool - Flag to show all sources of data - export : str - Export dataframe data to csv,json,xlsx file - """ - sources = ["CoinGecko", "CoinPaprika", "Binance", "Coinbase"] - limit, cutoff = 30, 0.75 - coins_func_map = { - "CoinGecko": pycoingecko_model.get_coin_list, - "CoinPaprika": coinpaprika_model.get_coin_list, - "Binance": load_binance_map, - "Coinbase": load_coinbase_map, - } - - if show_all: - coins_func = coins_func_map.get(source) - df = coins_func() if coins_func else prepare_all_coins_df() - - elif not source or source not in sources: - df = prepare_all_coins_df() - cg_coins_list = df["CoinGecko"].to_list() - sim = difflib.get_close_matches(symbol.lower(), cg_coins_list, limit, cutoff) - df_matched = pd.Series(sim).to_frame().reset_index() - df_matched.columns = ["index", "CoinGecko"] - df = df.merge(df_matched, on="CoinGecko") - df.drop("index", axis=1, inplace=True) - - elif source == "CoinGecko": - coins_df = pycoingecko_model.get_coin_list().drop("index", axis=1) - df = _create_closest_match_df(symbol.lower(), coins_df, limit, cutoff) - df = df[["index", "id", "name"]] - - elif source == "CoinPaprika": - coins_df = coinpaprika_model.get_coin_list() - df = _create_closest_match_df(symbol.lower(), coins_df, limit, cutoff) - df = df[["index", "id", "name"]] - - elif source == "Binance": - coins_df_gecko = pycoingecko_model.get_coin_list() - coins_df_bin = load_binance_map() - coins_df_bin.columns = ["symbol", "id"] - coins_df = pd.merge( - coins_df_bin, coins_df_gecko[["id", "name"]], how="left", on="id" - ) - df = _create_closest_match_df(symbol.lower(), coins_df, limit, cutoff) - df = df[["index", "symbol", "name"]] - df.columns = ["index", "id", "name"] - - elif source == "Coinbase": - coins_df_gecko = pycoingecko_model.get_coin_list() - coins_df_cb = load_coinbase_map() - coins_df_cb.columns = ["symbol", "id"] - coins_df = pd.merge( - coins_df_cb, coins_df_gecko[["id", "name"]], how="left", on="id" - ) - df = _create_closest_match_df(symbol.lower(), coins_df, limit, cutoff) - df = df[["index", "symbol", "name"]] - df.columns = ["index", "id", "name"] - - else: - df = pd.DataFrame(columns=["index", "id", "symbol"]) - console.print("Couldn't find any coins") - - try: - df = df[skip : skip + limit] # noqa - except Exception as e: - logger.exception(str(e)) - console.print(e) - - print_rich_table( - df.fillna("N/A"), - headers=list(df.columns), - show_index=False, - title="Similar Coins", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "coins", - df, - sheet_name, - ) - - -def plot_chart( - prices_df: pd.DataFrame, - to_symbol: str = "", - from_symbol: str = "", - source: str = "", - exchange: str = "", - interval: str = "", - external_axes: bool = False, - yscale: str = "linear", -) -> Union[OpenBBFigure, None]: - """Load data for Technical Analysis - - Parameters - ---------- - prices_df: pd.DataFrame - Cryptocurrency - to_symbol: str - Coin (only used for chart title), by default "" - from_symbol: str - Currency (only used for chart title), by default "" - yscale: str - Scale for y axis of plot Either linear or log - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> eth_df = openbb.crypto.load("ETH") - >>> openbb.crypto.chart(prices_df=eth_df, to_symbol="usdt", from_symbol="eth", source="binance") - """ - del interval - - if prices_df.empty: - return console.print("There is not data to plot chart\n") - - exchange_str = f"/{exchange}" if source == "ccxt" else "" - title = ( - f"{source}{exchange_str} - {to_symbol.upper()}/{from_symbol.upper()}" - f" from {prices_df.index[0].strftime('%Y/%m/%d')} " - f"to {prices_df.index[-1].strftime('%Y/%m/%d')}" - ) - - console.print() - - return plot_candles( - symbol=to_symbol, - data=prices_df, - title=title, - volume=True, - external_axes=external_axes, - yscale=yscale, - ) - - -def plot_candles( # pylint: disable=too-many-arguments - symbol: str, - data: Optional[pd.DataFrame] = None, - start_date: Optional[Union[datetime, Union[str, None]]] = None, - end_date: Optional[Union[datetime, Union[str, None]]] = None, - interval: Union[str, int] = "1440", - exchange: str = "binance", - to_symbol: str = "usdt", - source: str = "CCXT", - volume: bool = True, - title: str = "", - external_axes: bool = False, - yscale: str = "linear", - raw: bool = False, -) -> Union[OpenBBFigure, Optional[pd.DataFrame], None]: - """Plot candle chart from dataframe. [Source: Binance] - - Parameters - ---------- - symbol: str - Ticker name - data: pd.DataFrame - Dataframe containing time and OHLCV - start_date: Union[datetime, Union[str, None]] - Start date for data - end_date: Union[datetime, Union[str, None]] - End date for data - interval: Union[str, int] - Interval for data - exchange: str - Exchange to use - to_symbol: str - Currency to use - source: str - Source to use - volume: bool - If volume data shall be plotted, by default True - ylabel: str - Y-label of the graph, by default "" - title: str - Title of graph, by default "" - external_axes : bool, optional - Whether to return the figure object or not, by default False - yscale : str - Scaling for y axis. Either linear or log - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.crypto.candle(symbol="eth") - >>> openbb.crypto.candle(symbol="btc", raw=True) - """ - - if data is None: - data = load( - symbol=symbol, - start_date=start_date, - end_date=end_date, - interval=interval, - exchange=exchange, - to_symbol=to_symbol, - source=source, - ) - - if raw: - return data - - fig = OpenBBFigure.create_subplots( - rows=1, - cols=1, - vertical_spacing=0, - specs=[[{"secondary_y": True}]], - ).set_title(f"\n{symbol if title == '' else title}") - - fig.add_candlestick( - open=data.Open, - high=data.High, - low=data.Low, - close=data.Close, - x=data.index, - name=f"{symbol} OHLC", - row=1, - col=1, - secondary_y=False, - ) - if volume: - fig.add_inchart_volume(data) - - fig.set_yaxis_title("Price", row=1, col=1, secondary_y=False, type=yscale) - fig.update_layout(showlegend=False) - - return fig.show(external=external_axes) - - -def plot_order_book( - bids: np.ndarray, - asks: np.ndarray, - coin: str, -) -> OpenBBFigure: - """ - Plots Bid/Ask. Can be used for Coinbase and Binance - - Parameters - ---------- - bids : np.array - array of bids with columns: price, size, cumulative size - asks : np.array - array of asks with columns: price, size, cumulative size - coin : str - Coin being plotted - """ - - fig = OpenBBFigure(xaxis_title="Price", yaxis_title="Size (Coins)").set_title( - f"Order Book for {coin}" - ) - - fig.add_scatter( - x=bids[:, 0], - y=bids[:, 2], - mode="lines", - name="bids", - line=dict(color=theme.up_color), - fill="tozeroy", - fillcolor=theme.up_color_transparent, - ) - fig.add_scatter( - x=asks[:, 0], - y=asks[:, 2], - mode="lines", - name="asks", - line=dict(color=theme.down_color), - fill="tozeroy", - fillcolor=theme.down_color_transparent, - ) - fig.add_hline(y=0, line=dict(width=0, color="rgba(0, 0, 0, 0)")) - - return fig - - -def check_cg_id(symbol: str): - cg_id = get_coingecko_id(symbol) - if not cg_id: - console.print(f"\n{symbol} not found on CoinGecko") - return "" - return symbol - - -def fetch_ccxt_ohlc(exchange_id, max_retries, symbol, timeframe, since, limit): - exchange = getattr(ccxt, exchange_id)( - { - "enableRateLimit": True, # required by the Manual - } - ) - if isinstance(since, str): - since = exchange.parse8601(since) - ohlcv = get_ohlcv(exchange, max_retries, symbol, timeframe, since, limit) - df = pd.DataFrame(ohlcv, columns=["date", "Open", "High", "Low", "Close", "Volume"]) - df["date"] = pd.to_datetime(df.date, unit="ms") - df.set_index("date", inplace=True) - return df - - -def retry_fetch_ohlcv(exchange, max_retries, symbol, timeframe, since, limit): - num_retries = 0 - try: - num_retries += 1 - ohlcv = exchange.fetch_ohlcv(symbol, timeframe, since, limit) - return ohlcv - except Exception: - if num_retries > max_retries: - raise - return [] - - -def get_ohlcv(exchange, max_retries, symbol, timeframe, since, limit): - timeframe_duration_in_seconds = exchange.parse_timeframe(timeframe) - timeframe_duration_in_ms = timeframe_duration_in_seconds * 1000 - timedelta_ = limit * timeframe_duration_in_ms - now = exchange.milliseconds() - all_ohlcv = [] - fetch_since = since - while fetch_since < now: - ohlcv = retry_fetch_ohlcv( - exchange, max_retries, symbol, timeframe, fetch_since, limit - ) - fetch_since = (ohlcv[-1][0] + 1) if len(ohlcv) else (fetch_since + timedelta_) - all_ohlcv = all_ohlcv + ohlcv - return exchange.filter_by_since_limit(all_ohlcv, since, None, key=0) - - -def get_exchanges_ohlc(): - arr = [] - for exchange in ccxt.exchanges: - exchange_ccxt = getattr(ccxt, exchange)( - { - "enableRateLimit": True, - } - ) - if exchange_ccxt.has["fetchOHLCV"]: - arr.append(exchange) - return arr diff --git a/openbb_terminal/cryptocurrency/data/README.md b/openbb_terminal/cryptocurrency/data/README.md deleted file mode 100644 index 3bcd2484f81..00000000000 --- a/openbb_terminal/cryptocurrency/data/README.md +++ /dev/null @@ -1,37 +0,0 @@ -## Update list of coins - -### Coingecko - -- File: `/data/coingecko_coins.json` -- Endpoint: [https://api.coingecko.com/api/v3/coins/list](https://api.coingecko.com/api/v3/coins/list) - -- File: `/data/coingecko_categories.json` -- Endpoint: [https://api.coingecko.com/api/v3/coins/list](https://api.coingecko.com/api/v3/coins/list) -- Code: - -```python -new_categories = {} -for category in categories: - new_categories[category['category_id']] = category['name'] -``` - -### Coinpaprika - -- File: `/data/coinpaprika_coins.json` -- Endpoint: [https://api.coinpaprika.com/v1/coins](https://api.coinpaprika.com/v1/coins) - -### Defillama - -- File: `/data/defillama_dapps.json` -- Endpoint: [https://api.llama.fi/protocols](https://api.llama.fi/protocols) - -```python -import request -API_URL = "https://api.llama.fi" -url = f"{API_URL}/protocols" -r = request(url) -data = r.json() -protocols = {} -for protocol in data: - protocols[protocol['slug']] = protocol['name'] -``` diff --git a/openbb_terminal/cryptocurrency/data/binance_gecko_map.json b/openbb_terminal/cryptocurrency/data/binance_gecko_map.json deleted file mode 100644 index 5028fce34a2..00000000000 --- a/openbb_terminal/cryptocurrency/data/binance_gecko_map.json +++ /dev/null @@ -1,325 +0,0 @@ -{ - "BUSD": "binance-usd", - "BTC": "bitcoin", - "ETH": "ethereum", - "USDC": "usd-coin", - "XRP": "ripple", - "BETH": "binance-eth", - "ADA": "cardano", - "BNB": "binancecoin", - "DOGE": "dogecoin", - "WBTC": "wrapped-bitcoin", - "MATIC": "matic-network", - "AXS": "axie-infinity", - "DOT": "polkadot", - "FIL": "filecoin", - "LTC": "litecoin", - "LINK": "chainlink", - "TUSD": "true-usd", - "ETC": "ethereum-classic", - "FTT": "ftx-token", - "VET": "vechain", - "EOS": "eos", - "USDT": "tether", - "THETA": "theta-token", - "PAX": "paxos-standard", - "DENT": "dent", - "SOL": "solana", - "ICP": "internet-computer", - "BCH": "bitcoin-cash", - "SHIB": "shiba-inu", - "TRX": "tron", - "UTK": "utrust", - "IOTX": "iotex", - "CHZ": "chiliz", - "NEO": "neo", - "ALGO": "algorand", - "AAVE": "aave", - "QTUM": "qtum", - "UNI": "uniswap", - "SUSHI": "sushi", - "XLM": "stellar", - "TLM": "alien-worlds", - "BTT": "bittorrent-2", - "SLP": "smooth-love-potion", - "ALICE": "my-neighbor-alice", - "WIN": "wink", - "HOT": "holotoken", - "GRT": "the-graph", - "KSM": "kusama", - "DASH": "dash", - "XTZ": "tezos", - "ATOM": "cosmos", - "LUNA": "terra-luna", - "COMP": "compound-governance-token", - "CAKE": "pancakeswap-token", - "ZEC": "zcash", - "RUNE": "thorchain", - "PAXG": "pax-gold", - "EGLD": "elrond-erd-2", - "OMG": "omisego", - "PSG": "paris-saint-germain-fan-token", - "BAKE": "bakerytoken", - "HBAR": "hedera-hashgraph", - "FTM": "fantom", - "SXP": "swipe", - "XMR": "monero", - "ENJ": "enjincoin", - "CRV": "curve-dao-token", - "ZIL": "zilliqa", - "RVN": "ravencoin", - "XVS": "venus", - "YFI": "yearn-finance", - "SAND": "the-sandbox", - "IOTA": "iota", - "ALPHA": "alpha-finance", - "FOR": "force-protocol", - "ONE": "harmony", - "ZRX": "0x", - "GHST": "aavegotchi", - "LINA": "linear", - "GTO": "gifto", - "ONT": "ontology", - "KAVA": "kava", - "SNX": "havven", - "1INCH": "1inch", - "FLOW": "flow", - "MDX": "mdex", - "SC": "siacoin", - "TRU": "truefi", - "BAT": "basic-attention-token", - "YFII": "yfii-finance", - "C98": "coin98", - "CHR": "chromaway", - "MANA": "decentraland", - "SUPER": "superfarm", - "EPS": "ellipsis", - "VTHO": "vethor-token", - "AVAX": "avalanche-2", - "COTI": "coti", - "REEF": "reef-finance", - "NEAR": "near", - "OGN": "origin-protocol", - "CELR": "celer-network", - "ETHUP": "ethup", - "TFUEL": "theta-fuel", - "DODO": "dodo", - "ZEN": "zencash", - "NKN": "nkn", - "MTL": "metal", - "WAVES": "waves", - "WTC": "waltonchain", - "RAMP": "ramp", - "PERP": "perpetual-protocol", - "IOST": "iostoken", - "DEGO": "dego-finance", - "KLAY": "klay-token", - "SRM": "serum", - "MKR": "maker", - "TRB": "tellor", - "OM": "mantra-dao", - "SUN": "sun-token", - "ANKR": "ankr", - "DATA": "streamr-datacoin", - "QUICK": "quick", - "RLC": "iexec-rlc", - "SUSD": "nusd", - "BAND": "band-protocol", - "XEM": "nem", - "REQ": "request-network", - "TKO": "tokocrypto", - "KNC": "kyber-network-crystal", - "AUDIO": "audius", - "UNFI": "unifi-protocol-dao", - "RSR": "reserve-rights-token", - "WRX": "wazirx", - "SKL": "skale", - "LRC": "loopring", - "BADGER": "badger-dao", - "FET": "fetch-ai", - "STMX": "storm", - "PUNDIX": "pundi-x-2", - "LTO": "lto-network", - "FIS": "stafi", - "FUN": "funfair", - "CTSI": "cartesi", - "NANO": "nano", - "DGB": "digibyte", - "MFT": "mainframe", - "ATA": "automata", - "MIR": "mirror-protocol", - "PROM": "prometeus", - "RIF": "rif-token", - "KEEP": "keep-network", - "ATM": "atletico-madrid", - "CTK": "certik", - "QNT": "quant-network", - "ICX": "icon", - "ROSE": "oasis-network", - "DIA": "dia-data", - "BEL": "bella-protocol", - "HNT": "helium", - "BZRX": "bzx-protocol", - "FORTH": "ampleforth-governance-token", - "OCEAN": "ocean-protocol", - "REN": "republic-protocol", - "ARDR": "ardor", - "CLV": "clover-finance", - "ARPA": "arpa-chain", - "IRIS": "iris-network", - "BOND": "barnbridge", - "LIT": "litentry", - "LSK": "lisk", - "MINA": "mina-protocol", - "BQX": "ethos", - "SFP": "safepal", - "FRONT": "frontier-token", - "CVC": "civic", - "NULS": "nuls", - "STPT": "stp-network", - "FLM": "flamingo-finance", - "ADX": "adex", - "BLZ": "bluzelle", - "BAR": "fc-barcelona-fan-token", - "INJ": "injective-protocol", - "UMA": "uma", - "POLS": "polkastarter", - "AION": "aion", - "CKB": "nervos-network", - "COCOS": "cocos-bcx", - "TVK": "terra-virtua-kolect", - "LPT": "livepeer", - "BTCST": "btc-standard-hashrate-token", - "ETHDOWN": "ethdown", - "JST": "just", - "SYS": "syscoin", - "FIO": "fio-protocol", - "POLY": "polymath", - "BAL": "balancer", - "HARD": "hard-protocol", - "STX": "blockstack", - "MITH": "mithril", - "XVG": "verge", - "TOMO": "tomochain", - "GAS": "gas", - "TORN": "tornado-cash", - "DUSK": "dusk-network", - "GTC": "gitcoin", - "PERL": "perlin", - "STRAX": "stratis", - "BTS": "bitshares", - "HIVE": "hive", - "KEY": "selfkey", - "AKRO": "akropolis", - "MBL": "moviebloc", - "HEGIC": "hegic", - "TWT": "trust-wallet-token", - "BURGER": "burger-swap", - "ORN": "orion-protocol", - "VITE": "vite", - "GLM": "golem", - "DEXE": "dexe", - "RAY": "raydium", - "OXT": "orchid-protocol", - "STORJ": "storj", - "ALPACA": "alpaca-finance", - "CELO": "celo", - "BEAM": "beam", - "BCD": "bitcoin-diamond", - "DNT": "district0x", - "ONG": "ong", - "AUTO": "auto", - "GXS": "gxchain", - "REP": "augur", - "CFX": "conflux-token", - "NU": "nucypher", - "MDA": "moeda-loyalty-points", - "POND": "marlin", - "MDT": "measurable-data-token", - "WAN": "wanchain", - "POWR": "power-ledger", - "MLN": "melon", - "FIRO": "zcoin", - "ERN": "ethernity-chain", - "KMD": "komodo", - "CTXC": "cortex", - "JUV": "juventus-fan-token", - "LOOM": "loom-network-new", - "ACM": "ac-milan-fan-token", - "PHB": "red-pulse", - "TROY": "troy", - "DCR": "decred", - "DOCK": "dock", - "COS": "contentos", - "BTG": "bitcoin-gold", - "FARM": "harvest-finance", - "FXS": "frax-share", - "CDT": "blox", - "DREP": "drep-new", - "ANT": "aragon", - "PNT": "pnetwork", - "AST": "airswap", - "BNT": "bancor", - "WING": "wing-finance", - "TCT": "tokenclub", - "ASR": "as-roma-fan-token", - "NMR": "numeraire", - "SNM": "sonm", - "AUCTION": "auction", - "WNXM": "wrapped-nxm", - "MASK": "mask-network", - "VIDT": "v-id-blockchain", - "STEEM": "steem", - "AVA": "concierge-io", - "KP3R": "keep3rv1", - "ELF": "aelf", - "PHA": "pha", - "OG": "og-fan-token", - "SCRT": "secret", - "GO": "gochain", - "NAV": "nav-coin", - "SNT": "status", - "ARK": "ark", - "UFT": "unlend-finance", - "IDEX": "aurora-dao", - "NBS": "new-bitshares", - "QKC": "quark-chain", - "YOYO": "yoyow", - "PPT": "populous", - "WABI": "wabi", - "CVP": "concentrated-voting-power", - "APPC": "appcoins", - "SKY": "skycoin", - "VIA": "viacoin", - "CREAM": "cream-2", - "PIVX": "pivx", - "AR": "arweave", - "IQ": "everipedia", - "GRS": "groestlcoin", - "DF": "dforce-token", - "OAX": "openanx", - "COVER": "cover-protocol", - "AGIX": "singularitynet", - "VIB": "viberate", - "SPARTA": "spartan-protocol-token", - "SWRV": "swerve-dao", - "AMB": "amber", - "QLC": "qlink", - "BIFI": "beefy-finance", - "EZ": "easyfi", - "DLT": "agrello", - "POA": "poa-network", - "NAS": "nebulas", - "QSP": "quantstamp", - "EVX": "everex", - "BRD": "bread", - "SNGLS": "singulardtv", - "RDN": "raiden-network", - "NCASH": "nucleus-vision", - "AERGO": "aergo", - "NXS": "nexus", - "PROS": "prosper", - "CND": "cindicator", - "MTH": "monetha", - "NEBL": "neblio" -} diff --git a/openbb_terminal/cryptocurrency/data/coinbase_gecko_map.json b/openbb_terminal/cryptocurrency/data/coinbase_gecko_map.json deleted file mode 100644 index 33e0be5dc27..00000000000 --- a/openbb_terminal/cryptocurrency/data/coinbase_gecko_map.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "BTC": "bitcoin", - "ETH": "ethereum", - "SOL": "solana", - "ADA": "cardano", - "ALGO": "algorand", - "USDT": "tether", - "WBTC": "wrapped-bitcoin", - "LTC": "litecoin", - "XTZ": "tezos", - "LINK": "chainlink", - "MATIC": "matic-network", - "DAI": "dai", - "DOT": "polkadot", - "ATOM": "cosmos", - "BCH": "bitcoin-cash", - "XLM": "stellar", - "DOGE": "dogecoin", - "FIL": "filecoin", - "USDC": "usd-coin", - "EOS": "eos", - "GRT": "the-graph", - "ETC": "ethereum-classic", - "AXS": "axie-infinity", - "YFI": "yearn-finance", - "UNI": "uniswap", - "ICP": "internet-computer", - "AAVE": "aave", - "OMG": "omisego", - "DASH": "dash", - "QNT": "quant-network", - "UST": "terrausd", - "SUSHI": "sushi", - "ACH": "alchemy-pay", - "MKR": "maker", - "COMP": "compound-governance-token", - "FET": "fetch-ai", - "CGLD": "celo", - "AMP": "amp-token", - "WLUNA": "wrapped-terra", - "CRV": "curve-dao-token", - "ZRX": "0x", - "POLY": "polymath", - "LRC": "loopring", - "SNX": "havven", - "ENJ": "enjincoin", - "ANKR": "ankr", - "XYO": "xyo-network", - "REN": "republic-protocol", - "MANA": "decentraland", - "STORJ": "storj", - "PAX": "paxos-standard", - "CHZ": "chiliz", - "1INCH": "1inch", - "ZEC": "zcash", - "CVC": "civic", - "SKL": "skale", - "RLC": "iexec-rlc", - "UMA": "uma", - "BAND": "band-protocol", - "LPT": "livepeer", - "NKN": "nkn", - "BAT": "basic-attention-token", - "TRB": "tellor", - "IOTX": "iotex", - "MASK": "mask-network", - "KEEP": "keep-network", - "BAL": "balancer", - "RLY": "rally-2", - "NU": "nucypher", - "BOND": "barnbridge", - "CTSI": "cartesi", - "OGN": "origin-protocol", - "OXT": "orchid-protocol", - "COTI": "coti", - "MIR": "mirror-protocol", - "KNC": "kyber-network", - "TRU": "truefi", - "GTC": "gitcoin", - "NMR": "numeraire", - "YFII": "yfii-finance", - "MLN": "melon", - "REQ": "request-network", - "CLV": "clover-finance", - "FARM": "harvest-finance", - "RAD": "radicle", - "FORTH": "ampleforth-governance-token", - "DNT": "district0x", - "RGT": "rari-governance-token", - "ORN": "orion-protocol", - "QUICK": "quick", - "PLA": "playdapp", - "TRIBE": "tribe-2", - "BNT": "bancor", - "LOOM": "loom-network", - "DDX": "derivadao", - "GNT": "golem", - "RAI": "rai", - "REP": "augur", - "SHIB": "shiba-inu" -} diff --git a/openbb_terminal/cryptocurrency/data/coingecko_categories.json b/openbb_terminal/cryptocurrency/data/coingecko_categories.json deleted file mode 100644 index 181ebf91fb1..00000000000 --- a/openbb_terminal/cryptocurrency/data/coingecko_categories.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "aave-tokens": "Aave Tokens", - "analytics": "Analytics", - "arbitrum-ecosystem": "Arbitrum Ecosystem", - "artificial-intelligence": "Artificial Intelligence", - "asset-backed-tokens": "Asset-backed Tokens", - "asset-manager": "Asset Manager", - "augmented-reality": "Augmented Reality", - "automated-market-maker-amm": "Automated Market Maker (AMM)", - "avalanche-ecosystem": "Avalanche Ecosystem", - "axie-infinity": "Axie Infinity", - "big-data": "Big Data", - "binance-launchpool": "Binance Launchpool", - "binance-smart-chain": "Binance Smart Chain Ecosystem", - "business-platform": "Business Platform", - "business-services": "Business Services", - "cardano-ecosystem": "Cardano Ecosystem", - "celo-ecosystem": "Celo Ecosystem", - "centralized-exchange-token-cex": "Centralized Exchange Token (CEX)", - "charity": "Charity", - "cny-stablecoin": "CNY Stablecoin", - "collectibles": "Collectibles", - "communication": "Communication", - "compound-tokens": "Compound Tokens", - "cosmos-ecosystem": "Cosmos Ecosystem", - "cryptocurrency": "Cryptocurrency", - "daomaker-ecosystem": "DaoMaker Ecosystem", - "decentralized-exchange": "Decentralized Exchange Token (DEX)", - "decentralized-finance-defi": "Decentralized Finance (DeFi)", - "defi-index": "DeFi Index", - "decentralized-derivatives": "Derivatives", - "education": "Education", - "energy": "Energy", - "entertainment": "Entertainment", - "etf": "ETF", - "eth-2-0-staking": "Eth 2.0 Staking", - "eur-stablecoin": "EUR Stablecoin", - "exchange-based-tokens": "Exchange-based Tokens", - "fan-token": "Fan Token", - "fantom-ecosystem": "Fantom Ecosystem", - "finance-banking": "Finance / Banking", - "fractionalized-nft": "Fractionalized NFT", - "gambling": "Gambling", - "gaming": "GameFi", - "gbp-stablecoin": "GBP Stablecoin", - "gig-economy": "Gig Economy", - "xdai-ecosystem": "Gnosis Chain Ecosystem", - "governance": "Governance", - "guild-scholarship": "Guild and Scholarship", - "harmony-ecosystem": "Harmony Ecosystem", - "healthcare": "Healthcare", - "heco-chain-ecosystem": "HECO Chain Ecosystem", - "impossible-launchpad": "Impossible Launchpad", - "index-coin": "Index", - "infrastructure": "Infrastructure", - "insurance": "Insurance", - "internet-of-things-iot": "Internet of Things (IOT)", - "investment": "Investment", - "iotex-ecosystem": "IoTeX Ecosystem", - "kardiachain-ecosystem": "KardiaChain Ecosystem", - "krw-stablecoin": "KRW Stablecoin", - "launchpad": "Launchpad", - "layer-1": "Layer 1", - "legal": "Legal", - "lending-borrowing": "Lending/Borrowing", - "leveraged-token": "Leveraged Token", - "lp-tokens": "LP Tokens", - "manufacturing": "Manufacturing", - "marketing": "Marketing", - "masternodes": "Masternodes", - "media": "Media", - "meme-token": "Meme Tokens", - "metaverse": "Metaverse", - "mev-protection": "MEV Protection", - "mirrored-assets": "Mirrored Assets", - "moonriver-ecosystem": "Moonriver Ecosystem", - "music": "Music", - "near-protocol-ecosystem": "Near Protocol Ecosystem", - "nft-index": "NFT Index", - "niftex-shards": "Niftex Shards", - "non-fungible-tokens-nft": "Non-Fungible Tokens (NFT)", - "number": "Number", - "oec-ecosystem": "OEC Ecosystem", - "ohm-fork": "Ohm Fork", - "olympus-pro": "Olympus Pro", - "decentralized-options": "Options", - "oracle": "Oracle", - "decentralized-perpetuals": "Perpetuals", - "play-to-earn": "Play To Earn", - "dot-ecosystem": "Polkadot Ecosystem", - "polygon-ecosystem": "Polygon Ecosystem", - "prediction-markets": "Prediction Markets", - "privacy-coins": "Privacy Coins", - "protocol": "Protocol", - "real-estate": "Real Estate", - "realt-tokens": "RealT Tokens", - "rebase-tokens": "Rebase Tokens", - "reddit-points": "Reddit Points", - "remittance": "Remittance", - "retail": "Retail", - "seigniorage": "Seigniorage", - "smart-contract-platform": "Smart Contract Platform", - "social-money": "Social Money", - "software": "Software", - "solana-ecosystem": "Solana Ecosystem", - "sports": "Sports", - "stablecoins": "Stablecoins", - "storage": "Storage", - "structured-products": "Structured Products", - "synthetic-assets": "Synthetic Issuer", - "synths": "Synths", - "technology-science": "Technology & Science", - "terra-ecosystem": "Terra Ecosystem", - "tokenized-btc": "Tokenized BTC", - "tokenized-gold": "Tokenized Gold", - "tokenized-products": "Tokenized Products", - "tokenized-stock": "Tokenized Stock", - "tokensets": "TokenSets", - "tourism": "Tourism", - "usd-stablecoin": "USD Stablecoin", - "us-election-2020": "US Election 2020", - "utokens": "uTokens", - "virtual-reality": "Virtual Reality", - "wallets": "Wallets", - "wrapped-tokens": "Wrapped-Tokens", - "yearn-yfi-partnerships-mergers": "Yearn Ecosystem", - "yield-aggregator": "Yield Aggregator", - "yield-farming": "Yield Farming", - "zilliqa-ecosystem": "Zilliqa Ecosystem" -} diff --git a/openbb_terminal/cryptocurrency/data/coingecko_coins.json b/openbb_terminal/cryptocurrency/data/coingecko_coins.json deleted file mode 100644 index dc5b2574c89..00000000000 --- a/openbb_terminal/cryptocurrency/data/coingecko_coins.json +++ /dev/null @@ -1,19219 +0,0 @@ -[ - { "id": "01coin", "symbol": "zoc", "name": "01coin" }, - { - "id": "0-5x-long-algorand-token", - "symbol": "algohalf", - "name": "0.5X Long Algorand Token" - }, - { - "id": "0-5x-long-altcoin-index-token", - "symbol": "althalf", - "name": "0.5X Long Altcoin Index Token" - }, - { - "id": "0-5x-long-ascendex-token-token", - "symbol": "asdhalf", - "name": "0.5X Long AscendEx Token Token" - }, - { - "id": "0-5x-long-bitcoin-cash-token", - "symbol": "bchhalf", - "name": "0.5X Long Bitcoin Cash Token" - }, - { - "id": "0-5x-long-bitcoin-token", - "symbol": "half", - "name": "0.5X Long Bitcoin Token" - }, - { - "id": "0-5x-long-cardano-token", - "symbol": "adahalf", - "name": "0.5X Long Cardano Token" - }, - { - "id": "0-5x-long-chainlink-token", - "symbol": "linkhalf", - "name": "0.5X Long Chainlink Token" - }, - { - "id": "0-5x-long-cosmos-token", - "symbol": "atomhalf", - "name": "0.5X Long Cosmos Token" - }, - { - "id": "0-5x-long-dogecoin-token", - "symbol": "dogehalf", - "name": "0.5X Long Dogecoin Token" - }, - { - "id": "0-5x-long-eos-token", - "symbol": "eoshalf", - "name": "0.5X Long EOS Token" - }, - { - "id": "0-5x-long-ethereum-classic-token", - "symbol": "etchalf", - "name": "0.5X Long Ethereum Classic Token" - }, - { - "id": "0-5x-long-ethereum-token", - "symbol": "ethhalf", - "name": "0.5X Long Ethereum Token" - }, - { - "id": "0-5x-long-kyber-network-token", - "symbol": "knchalf", - "name": "0.5X Long Kyber Network Token" - }, - { - "id": "0-5x-long-matic-token", - "symbol": "matichalf", - "name": "0.5X Long Matic Token" - }, - { - "id": "0-5x-long-privacy-index-token", - "symbol": "privhalf", - "name": "0.5X Long Privacy Index Token" - }, - { - "id": "0-5x-long-swipe-token", - "symbol": "sxphalf", - "name": "0.5X Long Swipe Token" - }, - { - "id": "0-5x-long-theta-network-token", - "symbol": "thetahalf", - "name": "0.5X Long Theta Network Token" - }, - { - "id": "0-5x-long-xrp-token", - "symbol": "xrphalf", - "name": "0.5X Long XRP Token" - }, - { "id": "0cash", "symbol": "zch", "name": "0cash" }, - { "id": "0chain", "symbol": "zcn", "name": "0chain" }, - { "id": "0-mee", "symbol": "ome", "name": "O-Mee" }, - { "id": "0x", "symbol": "zrx", "name": "0x" }, - { "id": "0xcert", "symbol": "zxc", "name": "0xcert" }, - { "id": "0xdao", "symbol": "OXD", "name": "0xDAO" }, - { "id": "0xdao-v2", "symbol": "oxd v2", "name": "0xDAO V2" }, - { "id": "0xmonero", "symbol": "0xmr", "name": "0xMonero" }, - { "id": "0xpad", "symbol": "0xpad", "name": "0xPAD" }, - { "id": "0x-wormhole", "symbol": "zrx", "name": "0x (Wormhole)" }, - { - "id": "10024-10028-appoline", - "symbol": "realtoken-s-10024-10028-appoline-st-detroit-mi", - "name": "RealT Token -10024-10028 Appoline St, Detroit, MI 48227" - }, - { - "id": "10084-grayton", - "symbol": "realtoken-s-10084-grayton-st-detroit-mi", - "name": "RealT Token - 10084 Grayton St, Detroit, MI 48224" - }, - { "id": "100x-coin", "symbol": "100x", "name": "100xCoin" }, - { - "id": "10604-somerset", - "symbol": "realtoken-s-10604-somerset-ave-detroit-mi", - "name": "RealT Token - 10604 Somerset Ave, Detroit, MI 48224" - }, - { - "id": "10612-somerset", - "symbol": "realtoken-s-10612-somerset-ave-detroit-mi", - "name": "RealT Token - 10612 Somerset Ave, Detroit, MI 48224" - }, - { - "id": "10616-mckinney", - "symbol": "realtoken-s-10616-mckinney-st-detroit-mi", - "name": "RealT Token - 10616 McKinney St, Detroit, MI 48224" - }, - { - "id": "10617-hathaway", - "symbol": "realtoken-s-10617-hathaway-ave-cleveland-oh", - "name": "RealT Token - 10617 Hathaway Ave, Cleveland, OH 44108" - }, - { - "id": "10629-mckinney", - "symbol": "realtoken-s-10629-mckinney-st-detroit-mi", - "name": "RealT Token - 10629 McKinney St, Detroit, MI 48224" - }, - { - "id": "10639-stratman", - "symbol": "realtoken-s-10639-stratman-st-detroit-mi", - "name": "RealT Token - 10639 Stratman St, Detroit, MI 48224" - }, - { - "id": "10700-whittier", - "symbol": "realtoken-s-10700-whittier-ave-detroit-mi", - "name": "RealT Token - 10700 Whittier Ave, Detroit, MI 48224" - }, - { - "id": "11078-longview", - "symbol": "realtoken-s-11078-longview-st-detroit-mi", - "name": "RealT Token - 11078 Longview St, Detroit, MI 48213" - }, - { - "id": "11078-wayburn", - "symbol": "realtoken-s-11078-wayburn-st-detroit-mi", - "name": "RealT Token - 11078 Wayburn St, Detroit, MI 48224" - }, - { - "id": "11201-college", - "symbol": "realtoken-s-11201-college-st-detroit-mi", - "name": "RealT Token - 11201 College St, Detroit, MI 48205" - }, - { - "id": "11300-roxbury", - "symbol": "realtoken-s-11300-roxbury-st-detroit-mi", - "name": "RealT Token - 11300 Roxbury St, Detroit, MI 48224" - }, - { - "id": "11653-nottingham", - "symbol": "realtoken-s-11653-nottingham-rd-detroit-mi", - "name": "RealT Token - 11653 Nottingham Rd, Detroit, MI 48224" - }, - { - "id": "11957-olga", - "symbol": "realtoken-s-11957-olga-st-detroit-mi", - "name": "RealT Token - 11957 Olga St, Detroit, MI 48213" - }, - { "id": "11-minutes", "symbol": "elvn", "name": "11 Minutes" }, - { - "id": "12334-lansdowne", - "symbol": "realtoken-s-12334-lansdowne-street-detroit-mi", - "name": "RealT Token - 12334 Lansdowne Street, Detroit, MI 48224" - }, - { - "id": "12405-santa-rosa", - "symbol": "realtoken-s-12405-santa-rosa-dr-detroit-mi", - "name": "RealT Token - 12405 Santa Rosa Dr, Detroit, MI 48204" - }, - { - "id": "12409-whitehill", - "symbol": "realtoken-s-12409-whitehill-st-detroit-mi", - "name": "RealT Token - 12409 Whitehill St, Detroit, MI 48224" - }, - { - "id": "1244-s-avers", - "symbol": "realtoken-s-1244-s.avers-st-chicago-il", - "name": "RealT Token - 1244 S. Avers St, Chicago, IL 60623" - }, - { - "id": "12866-lauder", - "symbol": "realtoken-s-12866-lauder-st-detroit-mi", - "name": "RealT Token - 12866 Lauder St, Detroit, MI 48227" - }, - { "id": "12ships", "symbol": "TSHP", "name": "12Ships" }, - { - "id": "13045-wade", - "symbol": "realtoken-s-13045-wade-st-detroit-mi", - "name": "RealT Token - 13045 Wade St, Detroit, MI 48213" - }, - { - "id": "13114-glenfield", - "symbol": "realtoken-s-13114-glenfield-ave-detroit-mi", - "name": "RealT Token - 13114 Glenfield Ave, Detroit, MI 48213" - }, - { - "id": "13116-kilbourne", - "symbol": "realtoken-s-13116-kilbourne-ave-detroit-mi", - "name": "RealT Token - 13116 Kilbourne Ave, Detroit, MI 48213" - }, - { "id": "1337", "symbol": "1337", "name": "Elite" }, - { - "id": "13606-winthrop", - "symbol": "realtoken-s-13606-winthrop-st-detroit-mi", - "name": "RealT Token - 13606 Winthrop St, Detroit, MI 48227" - }, - { - "id": "13991-warwick", - "symbol": "realtoken-s-13991-warwick-st-detroit-mi", - "name": "RealT Token -13991 Warwick St, Detroit, MI, 48223" - }, - { - "id": "14066-santa-rosa", - "symbol": "realtoken-s-14066-santa-rosa-dr-detroit-mi", - "name": "RealT Token - 14066 Santa Rosa Dr, Detroit, MI 48238" - }, - { - "id": "14078-carlisle", - "symbol": "realtoken-s-14078-carlisle-st-detroit-mi", - "name": "RealT Token - 14078 Carlisle St, Detroit, MI 48205" - }, - { - "id": "14229-wilshire", - "symbol": "realtoken-s-14229-wilshire-dr-detroit-mi", - "name": "RealT Token - 14229 Wilshire Dr, Detroit, MI 48213" - }, - { - "id": "14231-strathmoor", - "symbol": "realtoken-s-14231-strathmoor-st-detroit-mi", - "name": "RealT Token - 14231 Strathmoor St, Detroit, MI 48227" - }, - { - "id": "14319-rosemary", - "symbol": "realtoken-s-14319-rosemary-st-detroit-mi", - "name": "RealT Token - 14319 Rosemary St, Detroit, MI 48213" - }, - { - "id": "14494-chelsea", - "symbol": "realtoken-s-14494-chelsea-ave-detroit-mi", - "name": "RealT Token - 14494 Chelsea Ave, Detroit, MI 48213" - }, - { - "id": "14825-wilfred", - "symbol": "realtoken-s-14825-wilfried-st-detroit-mi", - "name": "RealT Token - 14825 Wilfred St, Detroit, MI 48213" - }, - { - "id": "14882-troester", - "symbol": "realtoken-s-14882-troester-st-detroit-mi", - "name": "RealT Token - 14882 Troester St, Detroit, MI 48205" - }, - { - "id": "14918-joy", - "symbol": "realtoken-s-14918-joy-rd-detroit-mi", - "name": "RealT Token - 14918 Joy Rd, Detroit, MI 48228" - }, - { - "id": "15039-ward", - "symbol": "realtoken-s-15039-ward-ave-detroit-mi", - "name": "RealT Token - 15039 Ward Ave, Detroit, MI 48227" - }, - { - "id": "15048-freeland", - "symbol": "realtoken-s-15048-freeland-st-detroit-mi", - "name": "RealT Token - 15048 Freeland St, Detroit, MI, 48227" - }, - { - "id": "15095-hartwell", - "symbol": "realtoken-s-15095-hartwell-st-detroit-mi", - "name": "RealT Token -15095 Hartwell St, Detroit, MI 48227" - }, - { - "id": "15350-greydale", - "symbol": "realtoken-s-15350-greydale-st-detroit-mi", - "name": "RealT Token - 15350 Greydale St, Detroit, MI 48223" - }, - { - "id": "15373-parkside", - "symbol": "realtoken-s-15373-parkside-st-detroit-mi", - "name": "RealT Token - 15373 Parkside St, Detroit, MI 48238" - }, - { - "id": "1542-s-ridgeway", - "symbol": "realtoken-s-1542-s.ridgeway-ave-chicago-il", - "name": "RealT Token - 1542 S Ridgeway Ave, Chicago, IL 60623" - }, - { - "id": "15634-liberal", - "symbol": "realtoken-s-15634-liberal-st-detroit-mi", - "name": "RealT Token - 15634 Liberal St, Detroit, MI 48205" - }, - { - "id": "15753-hartwell", - "symbol": "realtoken-s-15753-hartwell-st-detroit-mi", - "name": "RealT Token - 15753 Hartwell St, Detroit, MI 48227" - }, - { - "id": "15770-prest", - "symbol": "realtoken-s-15770-prest-st-detroit-mi", - "name": "RealT Token - 15770 Prest St, Detroit, MI 48227" - }, - { - "id": "15777-ardmore", - "symbol": "realtoken-s-15777-ardmore-st-detroit-mi", - "name": "RealT Token - 15777 Ardmore St, Detroit, MI 48227" - }, - { - "id": "15778-manor", - "symbol": "realtoken-s-15778-manor-st-detroit-mi", - "name": "RealT Token - 15778 Manor St, Detroit, MI 48238" - }, - { - "id": "15784-monte-vista", - "symbol": "realtoken-s-15784-monte-vista-st-detroit-mi", - "name": "RealT Token - 15784 Monte Vista St, Detroit, MI 48238" - }, - { - "id": "15796-hartwell", - "symbol": "realtoken-s-15796-hartwell-st-detroit-mi", - "name": "RealT Token - 15796 Hartwell St, Detroit, MI 48227" - }, - { - "id": "15860-hartwell", - "symbol": "realtoken-s-15860-hartwell-st-detroit-mi", - "name": "RealT Token - 15860 Hartwell St, Detroit, MI 48227" - }, - { - "id": "1617-s-avers", - "symbol": "realtoken-s-1617-s.avers-ave-chicago-il", - "name": "RealT Token - 1617 S Avers Ave, Chicago, IL 60623" - }, - { - "id": "16200-fullerton", - "symbol": "realtoken-s-16200-fullerton-ave-detroit-mi", - "name": "RealT Token - 16200 Fullerton Ave, Detroit, MI 48227" - }, - { - "id": "17500-evergreen", - "symbol": "realtoken-s-17500-evergreen-rd-detroit-mi", - "name": "RealT Token - 17500 Evergreen Rd, Detroit, MI 48219" - }, - { - "id": "17809-charest", - "symbol": "realtoken-s-17809-charest-st-detroit-mi", - "name": "RealT Token - 17809 Charest St, Detroit, MI 48212" - }, - { - "id": "17813-bradford", - "symbol": "realtoken-s-17813-bradford-st-detroit-mi", - "name": "RealT Token - 17813 Bradford St, Detroit, MI 48205" - }, - { - "id": "1815-s-avers", - "symbol": "realtoken-s-1815-s.avers-ave-chicago-il", - "name": "RealT Token - 1815 S Avers Ave, Chicago, IL 60623" - }, - { - "id": "18273-monte-vista", - "symbol": "realtoken-s-18273-monte-vista-st-detroit-mi", - "name": "RealT Token - 18273 Monte Vista St, Detroit, MI 48221" - }, - { - "id": "18276-appoline", - "symbol": "realtoken-s-18276-appoline-st-detroit-mi", - "name": "RealT Token - 18276 Appoline St, Detroit, MI 48235" - }, - { - "id": "18433-faust", - "symbol": "realtoken-s-18433-faust-ave-detroit-mi", - "name": "RealT Token - 18433 Faust Ave, Detroit, MI, 48219" - }, - { - "id": "18466-fielding", - "symbol": "realtoken-s-18466-fielding-st-detroit-mi", - "name": "RealT Token -18466 Fielding St, Detroit, MI 48219" - }, - { - "id": "18481-westphalia", - "symbol": "realtoken-s-18481-westphalia-st-detroit-mi", - "name": "RealT Token - 18481 Westphalia St, Detroit, MI 48205" - }, - { - "id": "18776-sunderland", - "symbol": "realtoken-s-18776-sunderland-rd-detroit-mi", - "name": "RealT Token - 18776 Sunderland Rd, Detroit, MI 48219" - }, - { - "id": "18900-mansfield", - "symbol": "realtoken-s-18900-mansfield-st-detroit-mi", - "name": "RealT Token - 18900 Mansfield St, Detroit, MI 48235" - }, - { - "id": "18983-alcoy", - "symbol": "realtoken-s-18983-alcoy-ave-detroit-mi", - "name": "RealT Token - 18983 Alcoy Ave, Detroit, MI 48205" - }, - { - "id": "19020-rosemont", - "symbol": "realtoken-s-19020-rosemont-ave-detroit-mi", - "name": "RealT Token - 19020 Rosemont Ave, Detroit, MI 48219" - }, - { - "id": "19136-tracey", - "symbol": "realtoken-s-19136-tracey-st-detroit-mi", - "name": "RealT Token - 19136 Tracey St, Detroit MI 48235" - }, - { - "id": "19163-mitchell", - "symbol": "realtoken-s-19163-mitchell-st-detroit-mi", - "name": "RealT Token - 19163 Mitchell St, Detroit, MI 48234" - }, - { - "id": "19200-strasburg", - "symbol": "realtoken-s-19200-strasburg-st-detroit-mi", - "name": "RealT Token - 19200 Strasburg St, Detroit, MI 48205" - }, - { - "id": "19201-westphalia", - "symbol": "realtoken-s-19201-westphalia-st-detroit-mi", - "name": "RealT Token - 19201 Westphalia St, Detroit, MI 48205" - }, - { - "id": "19218-houghton", - "symbol": "realtoken-s-19218-houghton-st-detroit-mi", - "name": "RealT Token - 19218 Houghton St, Detroit, MI 48219" - }, - { - "id": "19311-keystone", - "symbol": "realtoken-s-19311-keystone-st-detroit-mi", - "name": "RealT Token - 19311 Keystone St, Detroit, MI 48234" - }, - { - "id": "19317-gable", - "symbol": "realtoken-s-19317-gable-st-detroit-mi", - "name": "RealT Token - 19317 Gable St, Detroit, MI 48234" - }, - { - "id": "19333-moenart", - "symbol": "realtoken-s-19333-moenart-st-detroit-mi", - "name": "RealT Token - 19333 Moenart St, Detroit MI 48234" - }, - { - "id": "19596-goulburn", - "symbol": "realtoken-s-19596-goulburn-st-detroit-mi", - "name": "RealT Token - 19596 Goulburn Ave, Detroit, MI 48205" - }, - { - "id": "19996-joann", - "symbol": "realtoken-s-19996-joann-ave-detroit-mi", - "name": "RealT Token - 19996 Joann Ave, Detroit, MI 48205" - }, - { "id": "1art", "symbol": "1art", "name": "OneArt" }, - { "id": "1bch", "symbol": "1bch", "name": "1BCH" }, - { "id": "1beam", "symbol": "1beam", "name": "1Beam" }, - { "id": "1box", "symbol": "1box", "name": "1BOX" }, - { "id": "1doge", "symbol": "1doge", "name": "1Doge" }, - { "id": "1-dollar", "symbol": "oneusd", "name": "1 Dollar" }, - { "id": "1eco", "symbol": "1eco", "name": "1eco" }, - { "id": "1inch", "symbol": "1inch", "name": "1inch" }, - { "id": "1inch-wormhole", "symbol": "1inch", "name": "1INCH (Wormhole)" }, - { "id": "1million-nfts", "symbol": "1mil", "name": "1MillionNFTs" }, - { "id": "1million-token", "symbol": "1mt", "name": "1Million Token" }, - { "id": "1nft", "symbol": "1nft", "name": "1NFT" }, - { "id": "1peco", "symbol": "1peco", "name": "1peco" }, - { "id": "1safu", "symbol": "safu", "name": "1SAFU" }, - { "id": "1sol", "symbol": "1sol", "name": "1Sol" }, - { "id": "1sol-io-wormhole", "symbol": "1sol", "name": "1sol.io (Wormhole)" }, - { "id": "1swap", "symbol": "1swap", "name": "1Swap" }, - { "id": "1tronic", "symbol": "1trc", "name": "1TRONIC" }, - { "id": "1-up", "symbol": "1-up", "name": "1-UP" }, - { "id": "1world", "symbol": "1wo", "name": "1World" }, - { - "id": "1x-long-btc-implied-volatility-token", - "symbol": "bvol", - "name": "Bitcoin Volatility Token" - }, - { - "id": "1x-short-algorand-token", - "symbol": "algohedge", - "name": "1X Short Algorand Token" - }, - { - "id": "1x-short-bitcoin-cash-token", - "symbol": "bchhedge", - "name": "1X Short Bitcoin Cash Token" - }, - { - "id": "1x-short-bitcoin-token", - "symbol": "hedge", - "name": "1X Short Bitcoin Token" - }, - { - "id": "1x-short-bnb-token", - "symbol": "bnbhedge", - "name": "1X Short BNB Token" - }, - { - "id": "1x-short-btc-implied-volatility", - "symbol": "ibvol", - "name": "Inverse Bitcoin Volatility Token" - }, - { - "id": "1x-short-cardano-token", - "symbol": "adahedge", - "name": "1X Short Cardano Token" - }, - { - "id": "1x-short-chainlink-token", - "symbol": "LINKHEDGE", - "name": "1X Short Chainlink Token" - }, - { - "id": "1x-short-compound-token-token", - "symbol": "comphedge", - "name": "1X Short Compound Token Token" - }, - { - "id": "1x-short-cosmos-token", - "symbol": "atomhedge", - "name": "1X Short Cosmos Token" - }, - { - "id": "1x-short-defi-index-token", - "symbol": "defihedge", - "name": "1X Short DeFi Index Token" - }, - { - "id": "1x-short-dogecoin-token", - "symbol": "dogehedge", - "name": "1X Short Dogecoin Token" - }, - { - "id": "1x-short-eos-token", - "symbol": "eoshedge", - "name": "1X Short EOS Token" - }, - { - "id": "1x-short-ethereum-token", - "symbol": "ethhedge", - "name": "1X Short Ethereum Token" - }, - { - "id": "1x-short-exchange-token-index-token", - "symbol": "exchhedge", - "name": "1X Short Exchange Token Index Token" - }, - { - "id": "1x-short-litecoin-token", - "symbol": "ltchedge", - "name": "1X Short Litecoin Token" - }, - { - "id": "1x-short-matic-token", - "symbol": "matichedge", - "name": "1X Short Matic Token" - }, - { - "id": "1x-short-okb-token", - "symbol": "okbhedge", - "name": "1X Short OKB Token" - }, - { - "id": "1x-short-privacy-index-token", - "symbol": "privhedge", - "name": "1X Short Privacy Index Token" - }, - { - "id": "1x-short-shitcoin-index-token", - "symbol": "hedgeshit", - "name": "1X Short Shitcoin Index Token" - }, - { - "id": "1x-short-swipe-token", - "symbol": "sxphedge", - "name": "1X Short Swipe Token" - }, - { - "id": "1x-short-tezos-token", - "symbol": "xtzhedge", - "name": "1X Short Tezos Token" - }, - { - "id": "1x-short-theta-network-token", - "symbol": "thetahedge", - "name": "1X Short Theta Network Token" - }, - { - "id": "1x-short-tomochain-token", - "symbol": "tomohedge", - "name": "1X Short TomoChain Token" - }, - { - "id": "1x-short-trx-token", - "symbol": "trxhedge", - "name": "1X Short TRX Token" - }, - { - "id": "1x-short-vechain-token", - "symbol": "vethedge", - "name": "1X Short VeChain Token" - }, - { - "id": "1x-short-xrp-token", - "symbol": "xrphedge", - "name": "1X Short XRP Token" - }, - { - "id": "20200-lesure", - "symbol": "realtoken-s-20200-lesure-st-detroit-mi", - "name": "RealT Token - 20200 Lesure St, Detroit, MI 48235" - }, - { "id": "2022moon", "symbol": "2022m", "name": "2022MOON" }, - { "id": "2030-floki", "symbol": "2030Floki", "name": "2030 Floki" }, - { - "id": "25097-andover", - "symbol": "realtoken-s-25097-andover-dr-dearborn-heights-mi", - "name": "RealT Token - 25097 Andover Dr, Dearborn Heights, MI 48125" - }, - { - "id": "272-n-e-42nd-court", - "symbol": "realtoken-s-272-n.e.-42nd-court-deerfield-beach-fl", - "name": "RealT Token - 272 N.E. 42nd Court, Deerfield Beach, FL 33064" - }, - { "id": "28vck", "symbol": "vck", "name": "28VCK" }, - { "id": "2acoin", "symbol": "arms", "name": "2ACoin" }, - { "id": "2crazynft", "symbol": "2crz", "name": "2crazyNFT" }, - { "id": "2gather", "symbol": "two", "name": "2gather" }, - { "id": "2gether-2", "symbol": "2gt", "name": "2gether" }, - { "id": "2goshi", "symbol": "2goshi", "name": "2GoShi" }, - { "id": "2key", "symbol": "2key", "name": "2key.network" }, - { "id": "2local-2", "symbol": "2lc", "name": "2local" }, - { "id": "2omb-finance", "symbol": "2omb", "name": "2omb" }, - { "id": "2share", "symbol": "2shares", "name": "2SHARE" }, - { "id": "2x2", "symbol": "2x2", "name": "2X2" }, - { "id": "300fit", "symbol": "fit", "name": "300FIT" }, - { "id": "30mb-token", "symbol": "3omb", "name": "3OMB" }, - { - "id": "3432-harding", - "symbol": "realtoken-s-3432-harding-street-detroit-mi", - "name": "RealT Token - 3432 Harding Street, Detroit, MI, 48214" - }, - { "id": "3shares", "symbol": "3share", "name": "3Share" }, - { - "id": "3x-long-algorand-token", - "symbol": "algobull", - "name": "3X Long Algorand Token" - }, - { - "id": "3x-long-altcoin-index-token", - "symbol": "altbull", - "name": "3X Long Altcoin Index Token" - }, - { - "id": "3x-long-balancer-token", - "symbol": "balbull", - "name": "3X Long Balancer Token" - }, - { - "id": "3x-long-bilira-token", - "symbol": "trybbull", - "name": "3X Long BiLira Token" - }, - { - "id": "3x-long-bitcoin-cash-token", - "symbol": "bchbull", - "name": "3X Long Bitcoin Cash Token" - }, - { - "id": "3x-long-bitcoin-sv-token", - "symbol": "bsvbull", - "name": "3X Long Bitcoin SV Token" - }, - { - "id": "3x-long-bitcoin-token", - "symbol": "bull", - "name": "3X Long Bitcoin Token" - }, - { - "id": "3x-long-bnb-token", - "symbol": "bnbbull", - "name": "3X Long BNB Token" - }, - { - "id": "3x-long-cardano-token", - "symbol": "adabull", - "name": "3X Long Cardano Token" - }, - { - "id": "3x-long-chainlink-token", - "symbol": "linkbull", - "name": "3X Long Chainlink Token" - }, - { - "id": "3x-long-compound-token-token", - "symbol": "compbull", - "name": "3X Long Compound Token Token" - }, - { - "id": "3x-long-compound-usdt-token", - "symbol": "cusdtbull", - "name": "3X Long Compound USDT Token" - }, - { - "id": "3x-long-cosmos-token", - "symbol": "atombull", - "name": "3X Long Cosmos Token" - }, - { - "id": "3x-long-defi-index-token", - "symbol": "defibull", - "name": "3X Long DeFi Index Token" - }, - { - "id": "3x-long-dogecoin-token", - "symbol": "dogebull", - "name": "3X Long Dogecoin Token" - }, - { - "id": "3x-long-dragon-index-token", - "symbol": "drgnbull", - "name": "3X Long Dragon Index Token" - }, - { - "id": "3x-long-eos-token", - "symbol": "eosbull", - "name": "3X Long EOS Token" - }, - { - "id": "3x-long-ethereum-classic-token", - "symbol": "etcbull", - "name": "3X Long Ethereum Classic Token" - }, - { - "id": "3x-long-ethereum-token", - "symbol": "ethbull", - "name": "3X Long Ethereum Token" - }, - { - "id": "3x-long-exchange-token-index-token", - "symbol": "exchbull", - "name": "3X Long Exchange Token Index Token" - }, - { - "id": "3x-long-huobi-token-token", - "symbol": "htbull", - "name": "3X Long Huobi Token Token" - }, - { - "id": "3x-long-kyber-network-token", - "symbol": "kncbull", - "name": "3X Long Kyber Network Token" - }, - { - "id": "3x-long-leo-token", - "symbol": "leobull", - "name": "3X Long LEO Token" - }, - { - "id": "3x-long-litecoin-token", - "symbol": "ltcbull", - "name": "3X Long Litecoin Token" - }, - { - "id": "3x-long-maker-token", - "symbol": "mkrbull", - "name": "3X Long Maker Token" - }, - { - "id": "3x-long-matic-token", - "symbol": "maticbull", - "name": "3X Long Matic Token" - }, - { - "id": "3x-long-midcap-index-token", - "symbol": "midbull", - "name": "3X Long Midcap Index Token" - }, - { - "id": "3x-long-okb-token", - "symbol": "okbbull", - "name": "3X Long OKB Token" - }, - { - "id": "3x-long-pax-gold-token", - "symbol": "paxgbull", - "name": "3X Long PAX Gold Token" - }, - { - "id": "3x-long-privacy-index-token", - "symbol": "privbull", - "name": "3X Long Privacy Index Token" - }, - { - "id": "3x-long-shitcoin-index-token", - "symbol": "bullshit", - "name": "3X Long Shitcoin Index Token" - }, - { - "id": "3x-long-stellar-token", - "symbol": "xlmbull", - "name": "3X Long Stellar Token" - }, - { - "id": "3x-long-sushi-token", - "symbol": "sushibull", - "name": "3X Long Sushi Token" - }, - { - "id": "3x-long-swipe-token", - "symbol": "sxpbull", - "name": "3X Long Swipe Token" - }, - { - "id": "3x-long-tether-gold-token", - "symbol": "xautbull", - "name": "3X Long Tether Gold Token" - }, - { - "id": "3x-long-tether-token", - "symbol": "usdtbull", - "name": "3X Long Tether Token" - }, - { - "id": "3x-long-tezos-token", - "symbol": "xtzbull", - "name": "3X Long Tezos Token" - }, - { - "id": "3x-long-theta-network-token", - "symbol": "thetabull", - "name": "3X Long Theta Network Token" - }, - { - "id": "3x-long-tomochain-token", - "symbol": "tomobull", - "name": "3X Long TomoChain Token" - }, - { - "id": "3x-long-trx-token", - "symbol": "trxbull", - "name": "3X Long TRX Token" - }, - { - "id": "3x-long-vechain-token", - "symbol": "vetbull", - "name": "3X Long VeChain Token" - }, - { - "id": "3x-long-xrp-token", - "symbol": "xrpbull", - "name": "3X Long XRP Token" - }, - { - "id": "3x-long-zcash-token", - "symbol": "zecbull", - "name": "3X Long Zcash Token" - }, - { - "id": "3x-short-altcoin-index-token", - "symbol": "altbear", - "name": "3X Short Altcoin Index Token" - }, - { - "id": "3x-short-balancer-token", - "symbol": "balbear", - "name": "3X Short Balancer Token" - }, - { - "id": "3x-short-bilira-token", - "symbol": "trybbear", - "name": "3X Short BiLira Token" - }, - { - "id": "3x-short-bitcoin-cash-token", - "symbol": "bchbear", - "name": "3X Short Bitcoin Cash Token" - }, - { - "id": "3x-short-bitcoin-sv-token", - "symbol": "bsvbear", - "name": "3X Short Bitcoin SV Token" - }, - { - "id": "3x-short-bitcoin-token", - "symbol": "bear", - "name": "3X Short Bitcoin Token" - }, - { - "id": "3x-short-bnb-token", - "symbol": "bnbbear", - "name": "3X Short BNB Token" - }, - { - "id": "3x-short-cardano-token", - "symbol": "adabear", - "name": "3X Short Cardano Token" - }, - { - "id": "3x-short-chainlink-token", - "symbol": "linkbear", - "name": "3X Short Chainlink Token" - }, - { - "id": "3x-short-compound-token-token", - "symbol": "compbear", - "name": "3X Short Compound Token Token" - }, - { - "id": "3x-short-compound-usdt-token", - "symbol": "cusdtbear", - "name": "3X Short Compound USDT Token" - }, - { - "id": "3x-short-defi-index-token", - "symbol": "defibear", - "name": "3X Short DeFi Index Token" - }, - { - "id": "3x-short-dragon-index-token", - "symbol": "drgnbear", - "name": "3X Short Dragon Index Token" - }, - { - "id": "3x-short-eos-token", - "symbol": "eosbear", - "name": "3X Short EOS Token" - }, - { - "id": "3x-short-ethereum-classic-token", - "symbol": "etcbear", - "name": "3X Short Ethereum Classic Token" - }, - { - "id": "3x-short-ethereum-token", - "symbol": "ethbear", - "name": "3X Short Ethereum Token" - }, - { - "id": "3x-short-exchange-token-index-token", - "symbol": "exchbear", - "name": "3X Short Exchange Token Index Token" - }, - { - "id": "3x-short-huobi-token-token", - "symbol": "htbear", - "name": "3X Short Huobi Token Token" - }, - { - "id": "3x-short-kyber-network-token", - "symbol": "kncbear", - "name": "3X Short Kyber Network Token" - }, - { - "id": "3x-short-leo-token", - "symbol": "leobear", - "name": "3X Short LEO Token" - }, - { - "id": "3x-short-litecoin-token", - "symbol": "ltcbear", - "name": "3X Short Litecoin Token" - }, - { - "id": "3x-short-maker-token", - "symbol": "mkrbear", - "name": "3X Short Maker Token" - }, - { - "id": "3x-short-midcap-index-token", - "symbol": "midbear", - "name": "3X Short Midcap Index Token" - }, - { - "id": "3x-short-okb-token", - "symbol": "okbbear", - "name": "3X Short OKB Token" - }, - { - "id": "3x-short-pax-gold-token", - "symbol": "paxgbear", - "name": "3X Short PAX Gold Token" - }, - { - "id": "3x-short-privacy-index-token", - "symbol": "privbear", - "name": "3X Short Privacy Index Token" - }, - { - "id": "3x-short-shitcoin-index-token", - "symbol": "bearshit", - "name": "3X Short Shitcoin Index Token" - }, - { - "id": "3x-short-stellar-token", - "symbol": "xlmbear", - "name": "3X Short Stellar Token" - }, - { - "id": "3x-short-sushi-token", - "symbol": "sushibear", - "name": "3X Short Sushi Token" - }, - { - "id": "3x-short-swipe-token", - "symbol": "sxpbear", - "name": "3X Short Swipe Token" - }, - { - "id": "3x-short-tether-gold-token", - "symbol": "xautbear", - "name": "3X Short Tether Gold Token" - }, - { - "id": "3x-short-tether-token", - "symbol": "usdtbear", - "name": "3X Short Tether Token" - }, - { - "id": "3x-short-tezos-token", - "symbol": "xtzbear", - "name": "3X Short Tezos Token" - }, - { - "id": "3x-short-trx-token", - "symbol": "trxbear", - "name": "3X Short TRX Token" - }, - { - "id": "3x-short-vechain-token", - "symbol": "vetbear", - "name": "3X Short VeChain Token" - }, - { - "id": "3x-short-xrp-token", - "symbol": "xrpbear", - "name": "3X Short XRP Token" - }, - { - "id": "402-s-kostner", - "symbol": "realtoken-s-402-s.kostner-ave-chicago-il", - "name": "RealT Token - 402 S Kostner Ave, Chicago, IL 60624" - }, - { "id": "404", "symbol": "404", "name": "404" }, - { - "id": "4061-grand", - "symbol": "realtoken-s-4061-grand-st-detroit-mi", - "name": "RealT Token - 4061 Grand St, Detroit, MI 48238" - }, - { "id": "42-coin", "symbol": "42", "name": "42-coin" }, - { - "id": "4340-east-71", - "symbol": "realtoken-s-4340-east-71-cleveland-oh", - "name": "RealT Token - 4340 East 71, Cleveland, OH 44105" - }, - { - "id": "4380-beaconsfield", - "symbol": "realtoken-s-4380-beaconsfield-st-detroit-mi", - "name": "RealT Token - 4380 Beaconsfield St, Detroit, MI 48224" - }, - { - "id": "4680-buckingham", - "symbol": "realtoken-s-4680-buckingham-ave-detroit-mi", - "name": "RealT Token - 4680 Buckingham Ave, Detroit, MI 48224" - }, - { - "id": "4852-4854-w-cortez", - "symbol": "realtoken-s-4852-4854-w.cortez-st-chicago-il", - "name": "RealT Token - 4852-4854 W Cortez St, Chicago, IL 60651" - }, - { "id": "4artechnologies", "symbol": "4art", "name": "4ART Coin" }, - { "id": "4bulls", "symbol": "4b", "name": "4Bulls" }, - { "id": "4int", "symbol": "4int", "name": "4INT" }, - { "id": "4jnet", "symbol": "4jnet", "name": "4JNET" }, - { "id": "4mw", "symbol": "4mw", "name": "4 Meta World" }, - { "id": "4new", "symbol": "kwatt", "name": "4New" }, - { "id": "4play", "symbol": "4play", "name": "4PLAY" }, - { "id": "4-stock", "symbol": "4stc", "name": "4-Stock" }, - { "id": "520", "symbol": "520", "name": "520" }, - { - "id": "5601-s-wood", - "symbol": "realtoken-s-5601-s.wood-st-chicago-il", - "name": "RealT Token - 5601 S Wood St, Chicago, IL 60636" - }, - { - "id": "581-587-jefferson", - "symbol": "realtoken-s-581-587-jefferson-ave-rochester-ny", - "name": "RealT Token - 581-587 Jefferson Ave, Rochester, NY 14611" - }, - { - "id": "5942-audubon", - "symbol": "realtoken-s-5942-audubon-rd-detroit-mi", - "name": "RealT Token - 5942 Audubon Rd, Detroit, MI 48224" - }, - { "id": "5g-cash", "symbol": "vgc", "name": "5G-CASH" }, - { "id": "5table-finance", "symbol": "5table", "name": "5table Finance" }, - { - "id": "6923-greenview", - "symbol": "realtoken-s-6923-greenview-ave-detroit-mi", - "name": "RealT Token - 6923 Greenview Ave, Detroit, MI 48228" - }, - { "id": "7eleven", "symbol": "7e", "name": "7ELEVEN" }, - { "id": "7plus-coin", "symbol": "sv7", "name": "7Plus Coin" }, - { "id": "7share", "symbol": "7share", "name": "7Share" }, - { "id": "7up", "symbol": "7up", "name": "7up" }, - { "id": "808ta-token", "symbol": "808ta", "name": "808TA Token" }, - { - "id": "8181-bliss", - "symbol": "realtoken-s-8181-bliss-st-detroit-mi", - "name": "RealT Token - 8181 Bliss St, Detroit, MI 48234" - }, - { - "id": "8342-schaefer", - "symbol": "realtoken-s-8342-schaefer-highway-detroit-mi", - "name": "RealT Token - 8342 Schaefer Highway, Detroit, MI 48228" - }, - { "id": "867", "symbol": "867", "name": "867" }, - { "id": "888tron", "symbol": "888", "name": "888tron" }, - { "id": "88mph", "symbol": "mph", "name": "88mph" }, - { "id": "8bit-doge", "symbol": "bitd", "name": "8Bit Doge" }, - { "id": "8ight-finance", "symbol": "eight", "name": "8ight Finance" }, - { "id": "8pay", "symbol": "8pay", "name": "8Pay" }, - { "id": "8x8-protocol", "symbol": "exe", "name": "8X8 Protocol" }, - { - "id": "9133-devonshire", - "symbol": "realtoken-s-9133-devonshire-rd-detroit-mi", - "name": "RealT Token - 9133 Devonshire Rd, Detroit, MI 48224" - }, - { - "id": "9165-kensington", - "symbol": "realtoken-s-9165-kensington-ave-detroit-mi", - "name": "RealT Token - 9165 Kensington Ave, Detroit, MI 48224" - }, - { - "id": "9166-devonshire", - "symbol": "realtoken-s-9166-devonshire-rd-detroit-mi", - "name": "RealT Token - 9166 Devonshire Rd, Detroit MI 48224" - }, - { - "id": "9169-boleyn", - "symbol": "realtoken-s-9169-boleyn-st-detroit-mi", - "name": "RealT Token - 9169 Boleyn St, Detroit, MI, 48224" - }, - { - "id": "9309-courville", - "symbol": "realtoken-s-9309-courville-st-detroit-mi", - "name": "RealT Token - 9309 Courville St, Detroit, MI 48224" - }, - { - "id": "9336-patton", - "symbol": "realtoken-s-9336-patton-st-detroit-mi", - "name": "RealT Token - 9336 Patton St, Detroit, MI 48228" - }, - { - "id": "9465-beaconsfield", - "symbol": "realtoken-s-9465-beaconsfield-st-detroit-mi", - "name": "RealT Token - 9465 Beaconsfield St, Detroit, MI 48224" - }, - { - "id": "9481-wayburn", - "symbol": "realtoken-s-9481-wayburn-st-detroit-mi", - "name": "RealT Token - 9481 Wayburn St, Detroit, MI 48224" - }, - { - "id": "9717-everts", - "symbol": "realtoken-s-9717-everts-st-detroit-mi", - "name": "RealT Token - 9717 Everts St, Detroit, MI 48224" - }, - { - "id": "9920-bishop", - "symbol": "realtoken-s-9920-bishop-st-detroit-mi", - "name": "RealT Token - 9920 Bishop St, Detroit, MI 48224" - }, - { "id": "99starz", "symbol": "stz", "name": "99Starz" }, - { "id": "a4-finance", "symbol": "a4", "name": "A4 Finance" }, - { "id": "aag-ventures", "symbol": "aag", "name": "AAG Ventures" }, - { "id": "aananaapegarden", "symbol": "bag", "name": "BananaApeGarden" }, - { "id": "aang-inu", "symbol": "aang", "name": "Aang Inu" }, - { "id": "aaptitude", "symbol": "aapt", "name": "AAptitude" }, - { "id": "aave", "symbol": "aave", "name": "Aave" }, - { "id": "aave-aave", "symbol": "aAAVE", "name": "Aave AAVE" }, - { - "id": "aave-amm-bptbalweth", - "symbol": "aAmmBptBALWETH", - "name": "Aave AMM BptBALWETH" - }, - { - "id": "aave-amm-bptwbtcweth", - "symbol": "aAmmBptWBTCWETH", - "name": "Aave AMM BptWBTCWETH" - }, - { "id": "aave-amm-dai", "symbol": "aAmmDAI", "name": "Aave AMM DAI" }, - { - "id": "aave-amm-uniaaveweth", - "symbol": "aammuniaaveweth", - "name": "Aave AMM UniAAVEWETH" - }, - { - "id": "aave-amm-unibatweth", - "symbol": "aAmmUniBATWETH", - "name": "Aave AMM UniBATWETH" - }, - { - "id": "aave-amm-unicrvweth", - "symbol": "aAmmUniCRVWETH", - "name": "Aave AMM UniCRVWETH" - }, - { - "id": "aave-amm-unidaiusdc", - "symbol": "aAmmUniDAIUSDC", - "name": "Aave AMM UniDAIUSDC" - }, - { - "id": "aave-amm-unidaiweth", - "symbol": "aammunidaiweth", - "name": "Aave AMM UniDAIWETH" - }, - { - "id": "aave-amm-unilinkweth", - "symbol": "aAmmUniLINKWETH", - "name": "Aave AMM UniLINKWETH" - }, - { - "id": "aave-amm-unimkrweth", - "symbol": "aAmmUniMKRWETH", - "name": "Aave AMM UniMKRWETH" - }, - { - "id": "aave-amm-unirenweth", - "symbol": "aAmmUniRENWETH", - "name": "Aave AMM UniRENWETH" - }, - { - "id": "aave-amm-unisnxweth", - "symbol": "aAmmUniSNXWETH", - "name": "Aave AMM UniSNXWETH" - }, - { - "id": "aave-amm-uniuniweth", - "symbol": "aAmmUniUNIWETH", - "name": "Aave AMM UniUNIWETH" - }, - { - "id": "aave-amm-uniusdcweth", - "symbol": "aAmmUniUSDCWETH", - "name": "Aave AMM UniUSDCWETH" - }, - { - "id": "aave-amm-uniwbtcusdc", - "symbol": "aAmmUniWBTCUSDC", - "name": "Aave AMM UniWBTCUSDC" - }, - { - "id": "aave-amm-uniwbtcweth", - "symbol": "aammuniwbtcweth", - "name": "Aave AMM UniWBTCWETH" - }, - { - "id": "aave-amm-uniyfiweth", - "symbol": "aAmmUniYFIWETH", - "name": "Aave AMM UniYFIWETH" - }, - { "id": "aave-amm-usdc", "symbol": "aAmmUSDC", "name": "Aave AMM USDC" }, - { "id": "aave-amm-usdt", "symbol": "aAmmUSDT", "name": "Aave AMM USDT" }, - { "id": "aave-amm-wbtc", "symbol": "aAmmWBTC", "name": "Aave AMM WBTC" }, - { "id": "aave-amm-weth", "symbol": "aAmmWETH", "name": "Aave AMM WETH" }, - { "id": "aave-bal", "symbol": "abal", "name": "Aave BAL" }, - { - "id": "aave-balancer-pool-token", - "symbol": "abpt", - "name": "Aave Balancer Pool Token" - }, - { "id": "aave-bat", "symbol": "abat", "name": "Aave BAT" }, - { "id": "aave-bat-v1", "symbol": "abat", "name": "Aave BAT v1" }, - { "id": "aave-busd", "symbol": "abusd", "name": "Aave BUSD" }, - { "id": "aave-busd-v1", "symbol": "abusd", "name": "Aave BUSD v1" }, - { "id": "aave-crv", "symbol": "aCRV", "name": "Aave CRV" }, - { "id": "aave-dai", "symbol": "adai", "name": "Aave DAI" }, - { "id": "aave-dai-v1", "symbol": "adai", "name": "Aave DAI v1" }, - { "id": "aave-enj", "symbol": "aenj", "name": "Aave ENJ" }, - { "id": "aave-enj-v1", "symbol": "aenj", "name": "Aave ENJ v1" }, - { "id": "aave-eth-v1", "symbol": "aeth", "name": "Aave ETH v1" }, - { "id": "aavegotchi", "symbol": "ghst", "name": "Aavegotchi" }, - { "id": "aavegotchi-alpha", "symbol": "alpha", "name": "Aavegotchi ALPHA" }, - { "id": "aavegotchi-fomo", "symbol": "fomo", "name": "Aavegotchi FOMO" }, - { "id": "aavegotchi-fud", "symbol": "fud", "name": "Aavegotchi FUD" }, - { "id": "aavegotchi-kek", "symbol": "kek", "name": "Aavegotchi KEK" }, - { "id": "aave-gusd", "symbol": "agusd", "name": "Aave GUSD" }, - { - "id": "aave-interest-bearing-ampl", - "symbol": "aampl", - "name": "Aave interest bearing AMPL" - }, - { - "id": "aave-interest-bearing-steth", - "symbol": "asteth", - "name": "Aave Interest Bearing STETH" - }, - { "id": "aave-knc", "symbol": "aknc", "name": "Aave KNC" }, - { "id": "aave-knc-v1", "symbol": "aknc", "name": "Aave KNC v1" }, - { "id": "aave-link", "symbol": "alink", "name": "Aave LINK" }, - { "id": "aave-link-v1", "symbol": "alink", "name": "Aave LINK v1" }, - { "id": "aave-mana", "symbol": "amana", "name": "Aave MANA" }, - { "id": "aave-mana-v1", "symbol": "amana", "name": "Aave MANA v1" }, - { "id": "aave-mkr", "symbol": "amkr", "name": "Aave MKR" }, - { "id": "aave-mkr-v1", "symbol": "amkr", "name": "Aave MKR v1" }, - { - "id": "aave-polygon-aave", - "symbol": "amAAVE", - "name": "Aave Polygon AAVE" - }, - { "id": "aave-polygon-dai", "symbol": "amDAI", "name": "Aave Polygon DAI" }, - { - "id": "aave-polygon-usdc", - "symbol": "amusdc", - "name": "Aave Polygon USDC" - }, - { - "id": "aave-polygon-usdt", - "symbol": "amUSDT", - "name": "Aave Polygon USDT" - }, - { - "id": "aave-polygon-wbtc", - "symbol": "amWBTC", - "name": "Aave Polygon WBTC" - }, - { - "id": "aave-polygon-weth", - "symbol": "amWETH", - "name": "Aave Polygon WETH" - }, - { - "id": "aave-polygon-wmatic", - "symbol": "amWMATIC", - "name": "Aave Polygon WMATIC" - }, - { "id": "aave-rai", "symbol": "arai", "name": "Aave RAI" }, - { "id": "aave-ren", "symbol": "aren", "name": "Aave REN" }, - { "id": "aave-ren-v1", "symbol": "aren", "name": "Aave REN v1" }, - { "id": "aave-snx", "symbol": "asnx", "name": "Aave SNX" }, - { "id": "aave-snx-v1", "symbol": "asnx", "name": "Aave SNX v1" }, - { "id": "aave-susd", "symbol": "asusd", "name": "Aave SUSD" }, - { "id": "aave-susd-v1", "symbol": "asusd", "name": "Aave SUSD v1" }, - { "id": "aave-tusd", "symbol": "atusd", "name": "Aave TUSD" }, - { "id": "aave-tusd-v1", "symbol": "atusd", "name": "Aave TUSD v1" }, - { "id": "aave-uni", "symbol": "auni", "name": "Aave UNI" }, - { "id": "aave-usdc", "symbol": "ausdc", "name": "Aave USDC" }, - { "id": "aave-usdc-v1", "symbol": "ausdc", "name": "Aave USDC v1" }, - { "id": "aave-usdt", "symbol": "ausdt", "name": "Aave USDT" }, - { "id": "aave-usdt-v1", "symbol": "ausdt", "name": "Aave USDT v1" }, - { "id": "aave-wbtc", "symbol": "awbtc", "name": "Aave WBTC" }, - { "id": "aave-wbtc-v1", "symbol": "awbtc", "name": "Aave WBTC v1" }, - { "id": "aave-weth", "symbol": "aweth", "name": "Aave WETH" }, - { "id": "aave-wormhole", "symbol": "aave", "name": "Aave (Wormhole)" }, - { "id": "aave-xsushi", "symbol": "aXSUSHI", "name": "Aave XSUSHI" }, - { "id": "aave-yfi", "symbol": "aYFI", "name": "Aave YFI" }, - { "id": "aave-zrx", "symbol": "azrx", "name": "Aave ZRX" }, - { "id": "aave-zrx-v1", "symbol": "azrx", "name": "Aave ZRX v1" }, - { "id": "aax-token", "symbol": "aab", "name": "AAX Token" }, - { "id": "abachi", "symbol": "abi", "name": "Abachi" }, - { "id": "abacus", "symbol": "abc", "name": "Abacus" }, - { "id": "abby-inu", "symbol": "abby", "name": "Abby Inu" }, - { "id": "abc-chain", "symbol": "abc", "name": "ABC Chain" }, - { "id": "abcc-token", "symbol": "at", "name": "ABCC Token" }, - { "id": "abell-coin", "symbol": "abc", "name": "Abell Coin" }, - { "id": "abey", "symbol": "abey", "name": "Abey" }, - { "id": "abitshadow-token", "symbol": "abst", "name": "Abitshadow Token" }, - { "id": "able-finance", "symbol": "able", "name": "Able Finance" }, - { "id": "absolute", "symbol": "abs", "name": "Absolute" }, - { - "id": "absolute-sync-token", - "symbol": "ast", - "name": "Absolute Sync Token" - }, - { "id": "absorber", "symbol": "abs", "name": "Absorber" }, - { "id": "abulaba", "symbol": "aaa", "name": "Abulaba" }, - { "id": "abura-farm", "symbol": "abu", "name": "Abura Farm" }, - { "id": "acala", "symbol": "aca", "name": "Acala" }, - { "id": "acash-coin", "symbol": "aca", "name": "Acash Coin" }, - { "id": "aca-token", "symbol": "aca", "name": "ACA Token" }, - { "id": "accel-defi", "symbol": "accel", "name": "Accel Defi" }, - { "id": "accesslauncher", "symbol": "acx", "name": "AccessLauncher" }, - { "id": "ace-casino", "symbol": "ace", "name": "Ace Entertainment" }, - { "id": "aced", "symbol": "aced", "name": "Aced [OLD]" }, - { "id": "aced-2", "symbol": "aced", "name": "Aced" }, - { "id": "acent", "symbol": "ace", "name": "Acent" }, - { "id": "acestarter", "symbol": "astar", "name": "AceStarter" }, - { "id": "acetoken", "symbol": "ace", "name": "ACEToken" }, - { "id": "acet-token", "symbol": "act", "name": "Acet" }, - { - "id": "ac-exchange-token", - "symbol": "acxt", - "name": "ACDX Exchange Token" - }, - { "id": "achain", "symbol": "act", "name": "Achain" }, - { "id": "acknoledger", "symbol": "ack", "name": "AcknoLedger" }, - { "id": "ac-milan-fan-token", "symbol": "acm", "name": "AC Milan Fan Token" }, - { "id": "acoconut", "symbol": "ac", "name": "ACoconut" }, - { "id": "acoin", "symbol": "acoin", "name": "Acoin" }, - { "id": "acreage-coin", "symbol": "acr", "name": "Acreage Coin" }, - { "id": "acryptos", "symbol": "acs", "name": "ACryptoS" }, - { "id": "acryptosi", "symbol": "acsi", "name": "ACryptoSI" }, - { "id": "acta-finance", "symbol": "acta", "name": "Acta Finance" }, - { "id": "actinium", "symbol": "acm", "name": "Actinium" }, - { "id": "action-coin", "symbol": "actn", "name": "Action Coin" }, - { "id": "activada", "symbol": "ACADA", "name": "ActivADA" }, - { - "id": "active-world-rewards-token", - "symbol": "awrt", - "name": "Active World Rewards Token" - }, - { "id": "acuity-token", "symbol": "acu", "name": "Acuity" }, - { "id": "acumen", "symbol": "acm", "name": "Acumen" }, - { "id": "acu-platform", "symbol": "acu", "name": "ACU Platform" }, - { "id": "acute-angle-cloud", "symbol": "aac", "name": "Double-A Chain" }, - { "id": "acy-finance", "symbol": "acy", "name": "ACY Finance" }, - { "id": "adaboy", "symbol": "adaboy", "name": "ADABoy" }, - { "id": "adacash", "symbol": "adacash", "name": "ADAcash" }, - { "id": "adadao", "symbol": "adao", "name": "ADADao" }, - { "id": "adadex-tools", "symbol": "adat", "name": "Adadex Tools" }, - { "id": "adaflect", "symbol": "ADAFlect", "name": "ADAFlect" }, - { "id": "adakong", "symbol": "akong", "name": "AdaKong" }, - { "id": "adalend", "symbol": "adal", "name": "Adalend" }, - { "id": "adamant", "symbol": "addy", "name": "Adamant" }, - { "id": "adamant-coin", "symbol": "admc", "name": "Adamant Coin" }, - { "id": "adamant-messenger", "symbol": "adm", "name": "ADAMANT Messenger" }, - { "id": "adam-oracle", "symbol": "adam", "name": "ADAM Oracle" }, - { "id": "adana-demirspor", "symbol": "demir", "name": "Adana Demirspor" }, - { "id": "adapad", "symbol": "adapad", "name": "ADAPad" }, - { "id": "adappter-token", "symbol": "adp", "name": "Adappter Token" }, - { "id": "adaswap", "symbol": "asw", "name": "AdaSwap" }, - { "id": "adax", "symbol": "adax", "name": "ADAX" }, - { "id": "adbank", "symbol": "adb", "name": "adbank" }, - { "id": "addax", "symbol": "adx", "name": "Addax" }, - { "id": "addict-finance", "symbol": "addict", "name": "Addict Finance" }, - { "id": "add-xyz-new", "symbol": "add", "name": "Add.xyz (NEW)" }, - { "id": "adelphoi", "symbol": "adl", "name": "Adelphoi" }, - { "id": "adena-finance", "symbol": "adena", "name": "Adena Finance" }, - { "id": "adene", "symbol": "ADEN", "name": "Adene" }, - { "id": "adex", "symbol": "adx", "name": "Ambire AdEx" }, - { "id": "ad-flex-token", "symbol": "adf", "name": "Ad Flex Token" }, - { "id": "aditus", "symbol": "adi", "name": "Aditus" }, - { "id": "admonkey", "symbol": "admonkey", "name": "AdMonkey" }, - { "id": "adora-token", "symbol": "ara", "name": "Adora Token" }, - { "id": "adroverse", "symbol": "adr", "name": "Adroverse" }, - { "id": "adshares", "symbol": "ads", "name": "Adshares" }, - { "id": "adtoken", "symbol": "adt", "name": "adToken" }, - { - "id": "advanced-internet-block", - "symbol": "aib", - "name": "Advanced Integrated Blocks" - }, - { - "id": "advanced-united-continent", - "symbol": "auc", - "name": "Advanced United Continent" - }, - { "id": "advar-protocol", "symbol": "advar", "name": "Advar Protocol" }, - { "id": "adventure-gold", "symbol": "agld", "name": "Adventure Gold" }, - { "id": "adventure-inu", "symbol": "adinu", "name": "Adventure Inu" }, - { "id": "adventure-token", "symbol": "twa", "name": "Adventure Token" }, - { "id": "advertise-coin", "symbol": "adco", "name": "Advertise Coin" }, - { "id": "aegis", "symbol": "ags", "name": "Aegis" }, - { "id": "aegis-launchpad", "symbol": "agspad", "name": "Aegis Launchpad" }, - { "id": "aelf", "symbol": "elf", "name": "aelf" }, - { "id": "aelin", "symbol": "aelin", "name": "Aelin" }, - { "id": "aelysir", "symbol": "ael", "name": "Aelysir" }, - { "id": "aen-smart-token", "symbol": "aens", "name": "AEN Smart Token" }, - { "id": "aeon", "symbol": "aeon", "name": "Aeon" }, - { "id": "aerarium-fi", "symbol": "aera", "name": "Aerarium Fi" }, - { "id": "aerdrop", "symbol": "aer", "name": "Aerdrop" }, - { "id": "aergo", "symbol": "aergo", "name": "Aergo" }, - { "id": "aerochain", "symbol": "aero", "name": "Aerochain" }, - { "id": "aeron", "symbol": "arnx", "name": "Aeron" }, - { "id": "aerotoken", "symbol": "aet", "name": "AEROTOKEN" }, - { "id": "aerotyne", "symbol": "atyne", "name": "Aerotyne" }, - { "id": "aerovek-aviation", "symbol": "aero", "name": "Aerovek Aviation" }, - { "id": "aeryus", "symbol": "aer", "name": "Aeryus" }, - { "id": "aeternity", "symbol": "ae", "name": "Aeternity" }, - { "id": "aetherius", "symbol": "aeth", "name": "Aetherius" }, - { "id": "aetherv2", "symbol": "ATH", "name": "AetherV2" }, - { "id": "aeur", "symbol": "aeur", "name": "AEUR" }, - { "id": "aevo", "symbol": "aevo", "name": "Always Evolving" }, - { "id": "aevolve-token", "symbol": "avex!", "name": "Aevolve Token" }, - { "id": "aezora", "symbol": "azr", "name": "Azzure" }, - { "id": "afen-blockchain", "symbol": "afen", "name": "AFEN Blockchain" }, - { "id": "affyn", "symbol": "fyn", "name": "Affyn" }, - { "id": "afin-coin", "symbol": "afin", "name": "Asian Fintech" }, - { "id": "afreum", "symbol": "afr", "name": "Afreum" }, - { "id": "afrix", "symbol": "afx", "name": "Afrix" }, - { "id": "afro", "symbol": "afro", "name": "Afro" }, - { "id": "afrodex", "symbol": "afrox", "name": "AfroDex" }, - { "id": "afrostar", "symbol": "afro", "name": "Afrostar" }, - { "id": "aga-carbon-credit", "symbol": "agac", "name": "AGA Carbon Credit" }, - { - "id": "aga-carbon-rewards", - "symbol": "acar", - "name": "AGA Carbon Rewards" - }, - { "id": "again-project", "symbol": "again", "name": "Again Project" }, - { "id": "aga-rewards", "symbol": "edc", "name": "Edcoin" }, - { "id": "aga-rewards-2", "symbol": "agar", "name": "AGA Rewards" }, - { "id": "aga-token", "symbol": "aga", "name": "AGA Token" }, - { "id": "agavecoin", "symbol": "agvc", "name": "AgaveCoin" }, - { "id": "agave-token", "symbol": "agve", "name": "Agave Token" }, - { "id": "agenor", "symbol": "age", "name": "Agenor" }, - { - "id": "agent-shiba-inu", - "symbol": "agentshibainu", - "name": "Agent Shiba I.N.U." - }, - { "id": "ageofgods", "symbol": "aog", "name": "AgeOfGods" }, - { "id": "age-of-knights", "symbol": "gem", "name": "Age Of Knights" }, - { "id": "age-of-tanks", "symbol": "a.o.t", "name": "Age Of Tanks" }, - { "id": "ageur", "symbol": "ageur", "name": "agEUR" }, - { "id": "ageur-wormhole", "symbol": "ageur", "name": "agEUR (Wormhole)" }, - { "id": "aggle-io", "symbol": "aggl", "name": "aggle.io" }, - { "id": "aggregatedfinance", "symbol": "agfi", "name": "AggregatedFinance" }, - { "id": "agile", "symbol": "agl", "name": "Agile" }, - { "id": "agora-defi", "symbol": "agora", "name": "Agora Defi" }, - { - "id": "agoras-currency-of-tau", - "symbol": "agrs", - "name": "Agoras: Currency of Tau" - }, - { "id": "agora-token", "symbol": "ago", "name": "Agora Token" }, - { "id": "agoric", "symbol": "bld", "name": "Agoric" }, - { "id": "agouti", "symbol": "agu", "name": "Agouti" }, - { "id": "agpc", "symbol": "agpc", "name": "AGPC" }, - { "id": "agrello", "symbol": "dlt", "name": "Agrello" }, - { "id": "agricoin", "symbol": "AGN", "name": "Agricoin" }, - { "id": "agrinoble", "symbol": "agn", "name": "Agrinoble" }, - { "id": "agrinovuscoin", "symbol": "agri", "name": "AgriNovusCoin" }, - { "id": "agrocash-x", "symbol": "xagc", "name": "AgroCash X" }, - { "id": "agrofarm", "symbol": "farm", "name": "Agrofarm" }, - { "id": "agro-global", "symbol": "agro", "name": "Agro Global" }, - { "id": "agro-matic", "symbol": "AMT", "name": "Agro-Matic" }, - { "id": "agronomist", "symbol": "agte", "name": "Agronomist" }, - { "id": "ahatoken", "symbol": "aht", "name": "AhaToken" }, - { "id": "aichain", "symbol": "ait", "name": "AICHAIN" }, - { "id": "aicon", "symbol": "aico", "name": "Aicon" }, - { "id": "aidcoin", "symbol": "aid", "name": "AidCoin" }, - { "id": "aidi-finance", "symbol": "aidi", "name": "Aidi Finance" }, - { "id": "aidi-inu", "symbol": "aidi", "name": "Aidi Inu" }, - { "id": "ai-doctor", "symbol": "aidoc", "name": "AI Doctor" }, - { "id": "aidos-kuneen", "symbol": "adk", "name": "Aidos Kuneen" }, - { "id": "ailink-token", "symbol": "ali", "name": "AiLink Token" }, - { "id": "aimedis-2", "symbol": "aimx", "name": "Aimedis" }, - { "id": "ai-network", "symbol": "ain", "name": "AI Network" }, - { "id": "ainu-token", "symbol": "ainu", "name": "Ainu Token" }, - { "id": "aion", "symbol": "aion", "name": "Aion" }, - { "id": "aioz-network", "symbol": "aioz", "name": "AIOZ Network" }, - { "id": "airbloc-protocol", "symbol": "abl", "name": "Airbloc" }, - { - "id": "airbnb-tokenized-stock-zipmex", - "symbol": "abnb", - "name": "Airbnb Tokenized Stock Zipmex" - }, - { "id": "aircoin-2", "symbol": "air", "name": "AirCoin" }, - { "id": "aircoins", "symbol": "airx", "name": "Aircoins" }, - { "id": "airdropper", "symbol": "air", "name": "AirDropper" }, - { "id": "airdrop-world", "symbol": "awt", "name": "Airdrop World" }, - { "id": "airight", "symbol": "airi", "name": "aiRight" }, - { "id": "airnft-token", "symbol": "airt", "name": "AirNFT Token" }, - { "id": "airpay", "symbol": "airpay", "name": "AirPay" }, - { - "id": "airraid-lottery-token", - "symbol": "araid", - "name": "AirRaid Lottery Token" - }, - { "id": "air-shiba", "symbol": "AIRSHIB", "name": "Air Shiba" }, - { "id": "airswap", "symbol": "ast", "name": "AirSwap" }, - { "id": "aisf", "symbol": "agt", "name": "AISF" }, - { "id": "aitra", "symbol": "aitra", "name": "AITRA" }, - { "id": "aiwork", "symbol": "awo", "name": "AiWork" }, - { "id": "ajeverse", "symbol": "aje", "name": "Ajeverse" }, - { "id": "akamaru-inu", "symbol": "akamaru", "name": "Akamaru Inu" }, - { "id": "akash-network", "symbol": "akt", "name": "Akash Network" }, - { "id": "akil-coin", "symbol": "AKL", "name": "Akil Coin" }, - { "id": "akira", "symbol": "akira", "name": "Akira" }, - { "id": "akita-inu", "symbol": "akita", "name": "Akita Inu" }, - { "id": "akitavax", "symbol": "akitax", "name": "Akitavax" }, - { "id": "akoin", "symbol": "akn", "name": "Akoin" }, - { "id": "akroma", "symbol": "aka", "name": "Akroma" }, - { "id": "akropolis", "symbol": "akro", "name": "Akropolis" }, - { "id": "akropolis-delphi", "symbol": "adel", "name": "Delphi" }, - { "id": "akro-wormhole", "symbol": "akro", "name": "Akropolis (Wormhole)" }, - { "id": "aktio", "symbol": "aktio", "name": "Aktio" }, - { "id": "aladdin-dao", "symbol": "ald", "name": "Aladdin DAO" }, - { "id": "aladiex", "symbol": "ala", "name": "Aladiex" }, - { - "id": "alanyaspor-fan-token", - "symbol": "ala", - "name": "Alanyaspor Fan Token" - }, - { "id": "alaya", "symbol": "atp", "name": "Alaya" }, - { "id": "alchemist", "symbol": "mist", "name": "Alchemist" }, - { "id": "alchemix", "symbol": "alcx", "name": "Alchemix" }, - { "id": "alchemix-usd", "symbol": "alusd", "name": "Alchemix USD" }, - { "id": "alchemy-dao", "symbol": "alch", "name": "AlchemyDAO" }, - { "id": "alchemy-pay", "symbol": "ach", "name": "Alchemy Pay" }, - { "id": "aldrin", "symbol": "rin", "name": "Aldrin" }, - { "id": "aleph", "symbol": "aleph", "name": "Aleph.im" }, - { - "id": "aleph-im-wormhole", - "symbol": "aleph", - "name": "Aleph.im (Wormhole)" - }, - { "id": "alephium", "symbol": "alph", "name": "Alephium" }, - { "id": "aleph-zero", "symbol": "azero", "name": "Aleph Zero" }, - { - "id": "alethea-artificial-liquid-intelligence-token", - "symbol": "ali", - "name": "Alethea Artificial Liquid Intelligence Token" - }, - { "id": "alex", "symbol": "alex", "name": "Alex" }, - { - "id": "alfa-romeo-racing-orlen-fan-token", - "symbol": "sauber", - "name": "Alfa Romeo Racing ORLEN Fan Token" - }, - { "id": "alfheim-finance", "symbol": "afm", "name": "Alfheim Finance" }, - { "id": "algebra", "symbol": "algb", "name": "Algebra" }, - { "id": "algoblocks", "symbol": "algoblk", "name": "AlgoBlocks" }, - { "id": "algogems", "symbol": "gems", "name": "AlgoGems" }, - { "id": "algopad", "symbol": "algopad", "name": "AlgoPad" }, - { "id": "algopainter", "symbol": "algop", "name": "AlgoPainter" }, - { "id": "algorand", "symbol": "algo", "name": "Algorand" }, - { "id": "algory", "symbol": "alg", "name": "Algory" }, - { "id": "algovest", "symbol": "avs", "name": "AlgoVest" }, - { "id": "alibabacoin", "symbol": "abbc", "name": "ABBC" }, - { - "id": "alibaba-tokenized-stock-defichain", - "symbol": "dbaba", - "name": "Alibaba Tokenized Stock Defichain" - }, - { - "id": "alibaba-tokenized-stock-zipmex", - "symbol": "baba", - "name": "Alibaba Tokenized Stock Zipmex" - }, - { "id": "alicoin", "symbol": "alicn", "name": "Alicoin" }, - { "id": "alien-inu", "symbol": "alien", "name": "Alien Inu" }, - { "id": "alien-shiba-inu", "symbol": "ashib", "name": "Alien Shiba Inu" }, - { "id": "alien-wars-gold", "symbol": "awg", "name": "Alien Wars Gold" }, - { "id": "alien-worlds", "symbol": "tlm", "name": "Alien Worlds" }, - { "id": "alien-worlds-bsc", "symbol": "tlm", "name": "Alien Worlds (BSC)" }, - { "id": "alightpay", "symbol": "alpy", "name": "Alightpay" }, - { "id": "alinx", "symbol": "alix", "name": "AlinX" }, - { "id": "alis", "symbol": "alis", "name": "ALIS" }, - { "id": "a-list-royale", "symbol": "AList", "name": "A-List Royale" }, - { "id": "alita", "symbol": "ali", "name": "Alita" }, - { "id": "alita-network", "symbol": "alita", "name": "Alita Network" }, - { "id": "alitas", "symbol": "alt", "name": "Alitas" }, - { "id": "alium-finance", "symbol": "alm", "name": "Alium Finance" }, - { - "id": "alkemi-network-dao-token", - "symbol": "alk", - "name": "Alkemi Network DAO Token" - }, - { "id": "alkimi", "symbol": "$ads", "name": "Alkimi" }, - { "id": "all-art", "symbol": "aart", "name": "ALL.ART" }, - { "id": "all-best-ico", "symbol": "allbi", "name": "ALL BEST ICO" }, - { "id": "allbridge", "symbol": "abr", "name": "Allbridge" }, - { - "id": "all-coins-yield-capital", - "symbol": "acyc", - "name": "All Coins Yield Capital" - }, - { "id": "alldex-alliance", "symbol": "axa", "name": "Alldex Alliance" }, - { "id": "allianceblock", "symbol": "albt", "name": "AllianceBlock" }, - { - "id": "alliance-cargo-direct", - "symbol": "acd", - "name": "Alliance Cargo Direct" - }, - { "id": "alliance-fan-token", "symbol": "all", "name": "Alliance Fan Token" }, - { "id": "alliance-x-trading", "symbol": "axt", "name": "Alliance X Trading" }, - { "id": "allium-finance", "symbol": "alm", "name": "Allium Finance" }, - { "id": "allive", "symbol": "alv", "name": "Allive" }, - { "id": "all-me", "symbol": "me", "name": "All.me" }, - { "id": "alloy-project", "symbol": "xao", "name": "Alloy Project" }, - { "id": "allsafe", "symbol": "asafe", "name": "AllSafe" }, - { "id": "all-sports", "symbol": "soc", "name": "All Sports" }, - { "id": "alluo", "symbol": "alluo", "name": "Alluo" }, - { "id": "ally", "symbol": "aly", "name": "Ally" }, - { "id": "ally-direct", "symbol": "drct", "name": "Ally Direct" }, - { "id": "almond", "symbol": "alm", "name": "Almond" }, - { - "id": "alnassr-fc-fan-token", - "symbol": "nassr", - "name": "Alnassr FC Fan Token" - }, - { "id": "aloha", "symbol": "aloha", "name": "Aloha" }, - { "id": "alpaca", "symbol": "alpa", "name": "Alpaca City" }, - { "id": "alpaca-finance", "symbol": "alpaca", "name": "Alpaca Finance" }, - { "id": "alpaca-usd", "symbol": "ausd", "name": "Alpaca USD" }, - { "id": "alpha5", "symbol": "a5t", "name": "Alpha5" }, - { - "id": "alpha-brain-capital", - "symbol": "abc", - "name": "Alpha Brain Capital [OLD]" - }, - { "id": "alpha-brain-capital-2", "symbol": "acap", "name": "Alpha Capital" }, - { "id": "alphacat", "symbol": "acat", "name": "Alphacat" }, - { "id": "alphacoin", "symbol": "alpha", "name": "AlphaCoin" }, - { "id": "alpha-coin", "symbol": "apc", "name": "Alpha Coin" }, - { "id": "alphadex", "symbol": "dex", "name": "AlphaDex" }, - { "id": "alpha-dex", "symbol": "roar", "name": "Alpha DEX" }, - { "id": "alpha-finance", "symbol": "alpha", "name": "Alpha Venture DAO" }, - { "id": "alpha-kombat", "symbol": "alkom", "name": "Alpha Kombat" }, - { "id": "alphalink", "symbol": "ank", "name": "AlphaLink" }, - { "id": "alpha-nodes", "symbol": "$alpha", "name": "Alpha Nodes" }, - { "id": "alpha-pad", "symbol": "apad", "name": "Alpha Pad" }, - { "id": "alpha-platform", "symbol": "a", "name": "Alpha Token" }, - { "id": "alpha-quark-token", "symbol": "aqt", "name": "Alpha Quark Token" }, - { "id": "alpha-rome", "symbol": "aROME", "name": "Alpha Rome" }, - { "id": "alpha-shiba-inu", "symbol": "alphashib", "name": "Alpha Shiba Inu" }, - { "id": "alphatoken", "symbol": ".alpha", "name": ".Alpha" }, - { "id": "alphr", "symbol": "alphr", "name": "Alphr" }, - { - "id": "alpine-f1-team-fan-token", - "symbol": "alpine", - "name": "Alpine F1 Team Fan Token" - }, - { "id": "alta-finance", "symbol": "alta", "name": "Alta Finance" }, - { "id": "altair", "symbol": "air", "name": "Altair" }, - { "id": "altbase", "symbol": "altb", "name": "Altbase" }, - { "id": "alt-coin", "symbol": "ALT", "name": "Alt Coin" }, - { "id": "altcommunity-coin", "symbol": "altom", "name": "ALTOM" }, - { "id": "alter", "symbol": "alter", "name": "Alter" }, - { "id": "altera", "symbol": "aen", "name": "Altera" }, - { "id": "altera-social", "symbol": "altera", "name": "Altera Social" }, - { "id": "altered-protocol", "symbol": "alte", "name": "Altered Protocol" }, - { - "id": "altered-state-token", - "symbol": "asto", - "name": "Altered State Token" - }, - { "id": "alternatemoney", "symbol": "am", "name": "AlternateMoney" }, - { "id": "alt-estate", "symbol": "alt", "name": "AltEstate Token" }, - { "id": "altmarkets-coin", "symbol": "altm", "name": "Altmarkets Coin" }, - { "id": "altrucoin-2", "symbol": "altrucoin", "name": "Altrucoin" }, - { "id": "altswitch", "symbol": "alts", "name": "AltSwitch" }, - { "id": "altura", "symbol": "alu", "name": "Altura" }, - { "id": "aluna", "symbol": "aln", "name": "Aluna" }, - { "id": "alvarenet", "symbol": "alvn", "name": "AlvareNet" }, - { "id": "alyattes", "symbol": "alya", "name": "Alyattes" }, - { "id": "amara-finance", "symbol": "mara", "name": "Amara Finance" }, - { "id": "amasa", "symbol": "amas", "name": "Amasa" }, - { "id": "amaten", "symbol": "ama", "name": "Amaten" }, - { "id": "amateras", "symbol": "amt", "name": "Amateras" }, - { "id": "amazingdoge", "symbol": "adoge", "name": "AmazingDoge" }, - { "id": "amazingteamdao", "symbol": "amazingteam", "name": "AmazingTeamDAO" }, - { "id": "amazonacoin", "symbol": "amz", "name": "AmazonasCoin" }, - { - "id": "amazon-tokenized-stock-defichain", - "symbol": "dAMZN", - "name": "Amazon Tokenized Stock Defichain" - }, - { - "id": "amazon-tokenized-stock-zipmex", - "symbol": "amzn", - "name": "Amazon Tokenized Stock Zipmex" - }, - { "id": "amber", "symbol": "amb", "name": "Ambrosus" }, - { "id": "ambire-wallet", "symbol": "wallet", "name": "Ambire Wallet" }, - { "id": "amc-fight-night", "symbol": "amc", "name": "AMC Fight Night" }, - { "id": "amdg-token", "symbol": "amdg", "name": "AMDG Token" }, - { "id": "amepay", "symbol": "ame", "name": "AME Chain" }, - { "id": "american-shiba", "symbol": "ushiba", "name": "American Shiba" }, - { - "id": "americanstaff-inu", - "symbol": "amstaff", - "name": "AmericanStaff Inu" - }, - { - "id": "amerikan-pitbull-terrier", - "symbol": "terrier", - "name": "Amerikan Pitbull Terrier" - }, - { "id": "ameru-dao", "symbol": "adao", "name": "Ameru DAO" }, - { "id": "amethyst", "symbol": "ames", "name": "Amethyst" }, - { "id": "amis", "symbol": "amis", "name": "AMIS" }, - { "id": "amix", "symbol": "amix", "name": "AMIX" }, - { "id": "ammyi-coin", "symbol": "ami", "name": "AMMYI Coin" }, - { "id": "amnext", "symbol": "amc", "name": "Amnext" }, - { "id": "amo", "symbol": "amo", "name": "AMO Coin" }, - { "id": "amon", "symbol": "amn", "name": "Amon" }, - { "id": "amond", "symbol": "amon", "name": "AmonD" }, - { "id": "amoveo", "symbol": "veo", "name": "Amoveo" }, - { "id": "ampleforth", "symbol": "ampl", "name": "Ampleforth" }, - { - "id": "ampleforth-governance-token", - "symbol": "forth", - "name": "Ampleforth Governance Token" - }, - { - "id": "ampleforth-wormhole", - "symbol": "ampl", - "name": "Ampleforth (Wormhole)" - }, - { "id": "ampleswap", "symbol": "ample", "name": "AmpleSwap" }, - { "id": "amplify", "symbol": "ampt", "name": "Amplify" }, - { "id": "ampnet", "symbol": "aapx", "name": "AMPnet" }, - { "id": "amp-token", "symbol": "amp", "name": "Amp" }, - { "id": "amp-wormhole", "symbol": "amp", "name": "Amp (Wormhole)" }, - { "id": "amun-defi-index", "symbol": "dfi", "name": "Amun DeFi Index" }, - { - "id": "amun-defi-momentum-index", - "symbol": "dmx", - "name": "Amun DeFi Momentum Index" - }, - { "id": "amy-finance", "symbol": "amy", "name": "Amy Finance" }, - { "id": "amz-coin", "symbol": "amz", "name": "AMZ Coin" }, - { "id": "anchor", "symbol": "anct", "name": "Anchor" }, - { "id": "anchor-beth-token", "symbol": "beth", "name": "Anchor bETH Token" }, - { - "id": "anchor-neural-world-token", - "symbol": "anw", - "name": "Anchor Neural World Token" - }, - { "id": "anchor-protocol", "symbol": "anc", "name": "Anchor Protocol" }, - { "id": "anchorswap", "symbol": "anchor", "name": "AnchorSwap" }, - { "id": "anchorust", "symbol": "aust", "name": "AnchorUST" }, - { "id": "ancient-kingdom", "symbol": "dom", "name": "Ancient Kingdom" }, - { "id": "ancient-warrior", "symbol": "ancw", "name": "Ancient Warrior" }, - { "id": "andromeda", "symbol": "m31", "name": "Andromeda" }, - { "id": "andronodes", "symbol": "andro", "name": "AndroNodes" }, - { "id": "anduschain", "symbol": "deb", "name": "Anduschain" }, - { "id": "angelheart-token", "symbol": "AHT", "name": "AngelHeart Token" }, - { "id": "angel-inu", "symbol": "ANGEL", "name": "Angel Inu" }, - { "id": "angel-nodes", "symbol": "angel", "name": "Angel Nodes" }, - { "id": "angel-protocol", "symbol": "halo", "name": "Angel Protocol" }, - { "id": "angelscreed", "symbol": "angel", "name": "AngelsCreed" }, - { "id": "angle-protocol", "symbol": "angle", "name": "ANGLE" }, - { "id": "angryb", "symbol": "anb", "name": "Angryb" }, - { "id": "angrybakery", "symbol": "abake", "name": "AngryBakery" }, - { "id": "angryfloki", "symbol": "$afloki", "name": "AngryFloki" }, - { - "id": "animal-concerts-token", - "symbol": "anml", - "name": "Animal Concerts" - }, - { "id": "animalia", "symbol": "anim", "name": "Animalia" }, - { "id": "animal-token", "symbol": "stray", "name": "Animal Token" }, - { "id": "animal-tycoon", "symbol": "amt", "name": "Animal Tycoon" }, - { "id": "animecoin", "symbol": "ani", "name": "Animecoin" }, - { "id": "animeinu", "symbol": "aime", "name": "AnimeInu" }, - { "id": "anime-token", "symbol": "ani", "name": "Anime Token" }, - { "id": "anime-vault-nftx", "symbol": "anime", "name": "ANIME Vault (NFTX)" }, - { "id": "animverse", "symbol": "anm", "name": "Animverse" }, - { "id": "aniverse", "symbol": "anv", "name": "Aniverse" }, - { "id": "anj", "symbol": "anj", "name": "Aragon Court" }, - { "id": "anji", "symbol": "anji", "name": "Anji" }, - { - "id": "ankaragucu-fan-token", - "symbol": "anka", - "name": "Ankaragücü Fan Tokens" - }, - { "id": "ankr", "symbol": "ankr", "name": "Ankr" }, - { - "id": "ankr-avalanche-reward-earning-bond", - "symbol": "aavaxb", - "name": "Ankr Avalanche Reward Earning Bond" - }, - { - "id": "ankreth", - "symbol": "aethc", - "name": "Ankr Reward-Bearing Staked ETH" - }, - { - "id": "ankr-network-wormhole", - "symbol": "ankr", - "name": "Ankr Network (Wormhole)" - }, - { - "id": "ankr-reward-earning-staked-eth", - "symbol": "aethb", - "name": "Ankr Reward-Earning Staked ETH" - }, - { "id": "annex", "symbol": "ann", "name": "Annex Finance" }, - { "id": "anomus-coin", "symbol": "anom", "name": "Anomus Coin" }, - { "id": "anon", "symbol": "anon", "name": "ANON" }, - { "id": "anonfloki", "symbol": "anonfloki", "name": "AnonFloki" }, - { "id": "anon-inu", "symbol": "ainu", "name": "Anon Inu" }, - { "id": "anons-network", "symbol": "anons", "name": "Anons Network" }, - { "id": "anonymous-bsc", "symbol": "anon", "name": "Anonymous BSC" }, - { "id": "anortis", "symbol": "anortis", "name": "Anortis" }, - { "id": "anpanswap-token", "symbol": "anpan", "name": "AnpanSwap Token" }, - { "id": "anrkey-x", "symbol": "$anrx", "name": "AnRKey X" }, - { "id": "ans-crypto-coin", "symbol": "ans", "name": "ANS Crypto Coin" }, - { "id": "answerly", "symbol": "ansr", "name": "Answerly" }, - { "id": "antalyaspor", "symbol": "akrep", "name": "Antalyaspor" }, - { "id": "antex", "symbol": "antex", "name": "Antex" }, - { "id": "antgold", "symbol": "antg", "name": "AntGold" }, - { "id": "antiample", "symbol": "xamp", "name": "Antiample" }, - { "id": "anti-lockdown", "symbol": "free", "name": "Anti-Lockdown" }, - { "id": "antimatter", "symbol": "matter", "name": "AntiMatter" }, - { "id": "antimony", "symbol": "atmn", "name": "Antimony" }, - { "id": "antis-inu", "symbol": "antis", "name": "Antis Inu" }, - { "id": "antra", "symbol": "antr", "name": "Antra" }, - { "id": "any-blocknet", "symbol": "ablock", "name": "ANY Blocknet" }, - { "id": "anypad", "symbol": "apad", "name": "Anypad" }, - { "id": "anyprinter", "symbol": "anyp", "name": "AnyPrinter" }, - { "id": "anysniper", "symbol": "snipe", "name": "AnySniper" }, - { "id": "anyswap", "symbol": "any", "name": "Anyswap" }, - { "id": "aok", "symbol": "aok", "name": "AOK" }, - { "id": "aonea-coin", "symbol": "a1a", "name": "Aonea Coin" }, - { "id": "aos", "symbol": "aos", "name": "AOS" }, - { "id": "ape", "symbol": "ape", "name": "APE" }, - { "id": "apeboys", "symbol": "ApeBoys", "name": "ApeBoys" }, - { "id": "apebusd", "symbol": "apebusd", "name": "ApeBUSD" }, - { "id": "apecoin", "symbol": "ape", "name": "ApeCoin" }, - { "id": "apecoin-token", "symbol": "ape", "name": "APEcoin Token" }, - { "id": "apefarmer", "symbol": "apef", "name": "ApeFarmer" }, - { "id": "apefund", "symbol": "apefund", "name": "ApeFund" }, - { "id": "ape-fun-token", "symbol": "aft", "name": "Ape Fun Token" }, - { "id": "ape-in", "symbol": "apein", "name": "Ape In" }, - { "id": "ape_in_records", "symbol": "air", "name": "Ape In Records" }, - { "id": "apelab", "symbol": "ape", "name": "ApeLab" }, - { "id": "apenft", "symbol": "nft", "name": "APENFT" }, - { "id": "ape-punk", "symbol": "ape$", "name": "APE Punk" }, - { "id": "apes-of-empires", "symbol": "aoe", "name": "Apes of Empires" }, - { "id": "apes-token", "symbol": "apes", "name": "Apes Token" }, - { "id": "apeswap-finance", "symbol": "banana", "name": "ApeSwap" }, - { "id": "apetoken", "symbol": "APET", "name": "APEToken" }, - { "id": "ape-universe", "symbol": "apeu", "name": "Ape Universe" }, - { "id": "ape-x", "symbol": "ape-x", "name": "Ape-X" }, - { "id": "apex-army", "symbol": "apex", "name": "APEx Army" }, - { "id": "apexit-finance", "symbol": "apex", "name": "ApeXit Finance" }, - { "id": "apex-predator", "symbol": "apex", "name": "Apex Predator" }, - { "id": "apex-protocol", "symbol": "apxp", "name": "APEX Protocol" }, - { "id": "api3", "symbol": "api3", "name": "API3" }, - { "id": "apidai-network", "symbol": "apidai", "name": "Apidai Network" }, - { "id": "apix", "symbol": "apix", "name": "APIX" }, - { "id": "apm-coin", "symbol": "APM", "name": "apM Coin" }, - { "id": "apocalypse", "symbol": "apoc", "name": "Apocalypse" }, - { "id": "apollo", "symbol": "apl", "name": "Apollo" }, - { "id": "apollo-coin", "symbol": "APX", "name": "Apollo Coin" }, - { "id": "apollo-dao", "symbol": "apollo", "name": "Apollo DAO" }, - { "id": "apollo-inu", "symbol": "APOLLO", "name": "Apollo Inu" }, - { - "id": "apollon-limassol", - "symbol": "APL", - "name": "Apollon Limassol Fan Token" - }, - { "id": "apollo-protocol", "symbol": "apollo", "name": "Apollo Protocol" }, - { "id": "apollox-2", "symbol": "apx", "name": "ApolloX" }, - { "id": "apoyield", "symbol": "soul", "name": "APOyield" }, - { "id": "appa-inu", "symbol": "appa", "name": "Appa Inu" }, - { "id": "app-alliance-association", "symbol": "aaa", "name": "AAAchain" }, - { "id": "appcoins", "symbol": "appc", "name": "AppCoins" }, - { "id": "appics", "symbol": "apx", "name": "Appics" }, - { "id": "apple", "symbol": "apple", "name": "Apple" }, - { "id": "apple-finance", "symbol": "aplp", "name": "Apple Finance" }, - { "id": "apple-fruit", "symbol": "apple", "name": "Apple Fruit" }, - { - "id": "apple-protocol-token", - "symbol": "aapl", - "name": "Apple Protocol Token" - }, - { "id": "appleswap", "symbol": "apple", "name": "AppleSwap" }, - { - "id": "apple-tokenized-stock-defichain", - "symbol": "dAAPL", - "name": "Apple Tokenized Stock Defichain" - }, - { "id": "apr-coin", "symbol": "apr", "name": "APR Coin" }, - { "id": "apreum", "symbol": "apu", "name": "Apreum" }, - { "id": "apricot", "symbol": "apt", "name": "Apricot" }, - { "id": "april", "symbol": "april", "name": "April" }, - { "id": "apron", "symbol": "apn", "name": "Apron" }, - { "id": "apwine", "symbol": "apw", "name": "APWine" }, - { "id": "apy-finance", "symbol": "apy", "name": "APY.Finance" }, - { "id": "apyswap", "symbol": "apys", "name": "APYSwap" }, - { "id": "apy-vision", "symbol": "vision", "name": "APY.vision" }, - { "id": "aqar-chain", "symbol": "aqr", "name": "Aqar Chain" }, - { "id": "aquachain", "symbol": "aqua", "name": "Aquachain" }, - { "id": "aquafi", "symbol": "aqua", "name": "AquaFi" }, - { "id": "aqua-goat", "symbol": "aquagoat", "name": "Aqua Goat" }, - { "id": "aquagoat-old", "symbol": "aquagoat", "name": "AquaGoat (Old)" }, - { "id": "aquari", "symbol": "aquari", "name": "Aquari" }, - { "id": "aquarius", "symbol": "aqua", "name": "Aquarius" }, - { "id": "aquariuscoin", "symbol": "arco", "name": "AquariusCoin" }, - { "id": "aquarius-fi", "symbol": "aqu", "name": "Aquarius.Fi" }, - { "id": "arabic", "symbol": "abic", "name": "Arabic" }, - { "id": "arable-protocol", "symbol": "acre", "name": "Arable Protocol" }, - { "id": "arabtycoon", "symbol": "at", "name": "ArabTycoon" }, - { "id": "aradenean-gold", "symbol": "ag", "name": "Aradenean Gold" }, - { "id": "aragon", "symbol": "ant", "name": "Aragon" }, - { "id": "arank", "symbol": "arank", "name": "Arank" }, - { "id": "arata", "symbol": "arata", "name": "Arata" }, - { "id": "ara-token", "symbol": "ara", "name": "Ara Token" }, - { "id": "araw-token", "symbol": "araw", "name": "ARAW Token" }, - { "id": "arbidex", "symbol": "abx", "name": "Arbidex" }, - { "id": "arbidoge", "symbol": "adoge", "name": "ArbiDoge" }, - { "id": "arbifarm", "symbol": "AFARM", "name": "ArbiFarm" }, - { "id": "arbimatter", "symbol": "arbimatter", "name": "ArbiMATTER" }, - { "id": "arbinyan", "symbol": "nyan", "name": "ArbiNYAN" }, - { "id": "arbirise-finance", "symbol": "arf", "name": "ArbiRiseFinance" }, - { "id": "arbis-finance", "symbol": "arbis", "name": "Arbis Finance" }, - { "id": "arbismart-token", "symbol": "rbis", "name": "ArbiSmart Token" }, - { "id": "arbiter", "symbol": "arb", "name": "Arbiter" }, - { "id": "arbix-finance", "symbol": "arbx", "name": "Arbix Finance" }, - { "id": "arbucks", "symbol": "buck", "name": "Arbucks" }, - { "id": "arbys", "symbol": "arbys", "name": "Arbys Token" }, - { "id": "arc", "symbol": "arc", "name": "Arc" }, - { "id": "arcade", "symbol": "arc", "name": "Arcade" }, - { "id": "arcadenetwork", "symbol": "arc", "name": "ArcadeNetwork" }, - { "id": "arcader", "symbol": "ard", "name": "Arcader" }, - { "id": "arcadetoken", "symbol": "arcade", "name": "ArcadeToken" }, - { "id": "arcadium", "symbol": "arcadium", "name": "Arcadium" }, - { "id": "arcana", "symbol": "ARCA", "name": "Arcana" }, - { "id": "arcana-token", "symbol": "XAR", "name": "Arcana Network" }, - { "id": "arcane", "symbol": "ARCANE", "name": "Arcane" }, - { "id": "arcaneleague", "symbol": "arcaneleague", "name": "ArcaneLeague" }, - { "id": "arcane-token", "symbol": "arcane", "name": "Arcane Token" }, - { "id": "arcanineinu", "symbol": "arcanineinu", "name": "ArcanineInu" }, - { "id": "arcblock", "symbol": "abt", "name": "Arcblock" }, - { "id": "arc-governance", "symbol": "arcx", "name": "ARC Governance" }, - { "id": "archangel-token", "symbol": "archa", "name": "ArchAngel" }, - { - "id": "archer-dao-governance-token", - "symbol": "arch", - "name": "Archer DAO Governance Token" - }, - { "id": "archethic", "symbol": "uco", "name": "ArchEthic" }, - { - "id": "archetypal-network", - "symbol": "actp", - "name": "Archetypal Network" - }, - { "id": "arcona", "symbol": "arcona", "name": "Arcona" }, - { "id": "arcs", "symbol": "arx", "name": "ARCS" }, - { "id": "arcticcoin", "symbol": "arc", "name": "Advanced Technology Coin" }, - { "id": "arctic-finance", "symbol": "aurora", "name": "Arctic Finance" }, - { "id": "arc-token", "symbol": "arc", "name": "Arc Token" }, - { "id": "ardana", "symbol": "dana", "name": "Ardana" }, - { "id": "ardcoin", "symbol": "ardx", "name": "ArdCoin" }, - { "id": "ardor", "symbol": "ardr", "name": "Ardor" }, - { "id": "arena-token", "symbol": "arena", "name": "Arena Token" }, - { "id": "arenaverse", "symbol": "averse", "name": "Arenaverse" }, - { "id": "arenum", "symbol": "arn", "name": "Arenum" }, - { "id": "ares-protocol", "symbol": "ares", "name": "Ares Protocol" }, - { - "id": "argentine-football-association-fan-token", - "symbol": "arg", - "name": "Argentine Football Association Fan Token" - }, - { "id": "argo", "symbol": "argo", "name": "ArGoApp" }, - { "id": "argo-2", "symbol": "argo", "name": "Argo" }, - { "id": "argon", "symbol": "argon", "name": "Argon" }, - { "id": "ari10", "symbol": "ari10", "name": "Ari10" }, - { "id": "aria-currency", "symbol": "ria", "name": "aRIA Currency" }, - { "id": "ariadne", "symbol": "ardn", "name": "Ariadne" }, - { "id": "arianee", "symbol": "aria20", "name": "Arianee" }, - { - "id": "aries-financial-token", - "symbol": "afib", - "name": "Aries Financial" - }, - { "id": "arion", "symbol": "arion", "name": "Arion" }, - { "id": "arionum", "symbol": "aro", "name": "Arionum" }, - { "id": "ari-swap", "symbol": "ari", "name": "Ari Swap" }, - { "id": "ariva", "symbol": "arv", "name": "Ariva" }, - { "id": "arix", "symbol": "arix", "name": "Arix" }, - { "id": "ark", "symbol": "ark", "name": "Ark" }, - { "id": "arkadiko-protocol", "symbol": "diko", "name": "Arkadiko" }, - { "id": "arkania-protocol", "symbol": "ania", "name": "Arkania Protocol" }, - { "id": "arkarus", "symbol": "aks", "name": "Arkarus" }, - { "id": "arker-2", "symbol": "arker", "name": "Arker" }, - { - "id": "ark-innovation-etf-defichain", - "symbol": "darkk", - "name": "ARK Innovation ETF Defichain" - }, - { - "id": "ark-of-the-universe", - "symbol": "arks", - "name": "Ark Of The Universe" - }, - { "id": "ark-rivals", "symbol": "arkn", "name": "Ark Rivals" }, - { "id": "armada", "symbol": "ARMD", "name": "Armada" }, - { "id": "armor", "symbol": "armor", "name": "ARMOR" }, - { "id": "armor-nxm", "symbol": "arnxm", "name": "Armor NXM" }, - { "id": "army-node-finance", "symbol": "army", "name": "Army Node Finance" }, - { "id": "armzlegends", "symbol": "prot", "name": "ArmzLegends" }, - { "id": "around-network", "symbol": "art", "name": "Around Network" }, - { "id": "arowana-token", "symbol": "arw", "name": "Arowana Token" }, - { "id": "arpa-chain", "symbol": "arpa", "name": "ARPA Chain" }, - { "id": "arqma", "symbol": "arq", "name": "ArQmA" }, - { "id": "arrb-token", "symbol": "arrb", "name": "Arrb Token" }, - { "id": "arrow", "symbol": "arw", "name": "Arrow" }, - { "id": "arsenal-fan-token", "symbol": "AFC", "name": "Arsenal Fan Token" }, - { "id": "artax", "symbol": "xax", "name": "ARTAX" }, - { - "id": "art-blocks-curated-fidenza-855", - "symbol": "fdnza", - "name": "Art Blocks Curated Fidenza #855" - }, - { - "id": "art-blocks-curated-full-set", - "symbol": "abc123", - "name": "Art Blocks Curated Full Set" - }, - { "id": "artbyte", "symbol": "aby", "name": "ArtByte" }, - { "id": "arte", "symbol": "arte", "name": "ARTE" }, - { "id": "artem", "symbol": "artem", "name": "Artem" }, - { "id": "artemis", "symbol": "mis", "name": "Artemis" }, - { "id": "artemis-vision", "symbol": "arv", "name": "Artemis Vision" }, - { - "id": "arteq-nft-investment-fund", - "symbol": "arteq", - "name": "artèQ NFT Investment Fund" - }, - { "id": "artery", "symbol": "artr", "name": "Artery" }, - { "id": "artex", "symbol": "artex", "name": "Artex" }, - { "id": "artfinity-token", "symbol": "at", "name": "Artfinity Token" }, - { "id": "arth", "symbol": "arth", "name": "ARTH" }, - { "id": "arth-valuecoin", "symbol": "arth", "name": "ARTH Valuecoin" }, - { "id": "artic-foundation", "symbol": "artic", "name": "ARTIC Foundation" }, - { "id": "articuno-inu", "symbol": "articuno", "name": "Articuno Inu" }, - { - "id": "artificial-intelligence", - "symbol": "ai", - "name": "Artificial Intelligence" - }, - { "id": "artii-token", "symbol": "artii", "name": "ARTII Token" }, - { "id": "arti-project", "symbol": "arti", "name": "Arti Project" }, - { "id": "artkit", "symbol": "arti", "name": "ArtKit" }, - { "id": "artl", "symbol": "artl", "name": "ARTL" }, - { "id": "artm", "symbol": "artm", "name": "ARTM" }, - { "id": "artmeta", "symbol": "$mart", "name": "ArtMeta" }, - { "id": "art-of-war", "symbol": "$AOW", "name": "Art of War" }, - { "id": "artonline", "symbol": "art", "name": "ArtOnline" }, - { "id": "artrade", "symbol": "atr", "name": "Artrade" }, - { "id": "art-rino", "symbol": "artrino", "name": "Art Rino" }, - { "id": "artube", "symbol": "att", "name": "Artube" }, - { "id": "artverse-token", "symbol": "avt", "name": "Artverse Token" }, - { "id": "artwork-nft", "symbol": "anft", "name": "Artwork NFT" }, - { "id": "artx", "symbol": "artx", "name": "ARTX" }, - { "id": "arweave", "symbol": "ar", "name": "Arweave" }, - { "id": "aryacoin", "symbol": "aya", "name": "Aryacoin" }, - { "id": "arya-finance", "symbol": "arya", "name": "Arya Finance" }, - { "id": "ascend", "symbol": "ACE", "name": "Ascend" }, - { "id": "ascend-node-club", "symbol": "asnd", "name": "Ascend Node Club" }, - { "id": "ascension", "symbol": "asn", "name": "Ascension" }, - { "id": "ascensionarcade", "symbol": "aat", "name": "AscensionArcade" }, - { - "id": "ascension-protocol", - "symbol": "ascend", - "name": "Ascension Protocol" - }, - { "id": "ascentpad", "symbol": "asp", "name": "AscentPad" }, - { "id": "asch", "symbol": "xas", "name": "Asch" }, - { "id": "asd", "symbol": "asd", "name": "AscendEx Token" }, - { "id": "asec-frontier", "symbol": "asec", "name": "ASEC Frontier" }, - { "id": "asgard-dao", "symbol": "asgard", "name": "Asgard DAO" }, - { "id": "asgard-dao-v2", "symbol": "asgardv2", "name": "Asgard DAO V2" }, - { "id": "asgardian-aereus", "symbol": "volt", "name": "Asgardian Aereus" }, - { "id": "ash", "symbol": "ash", "name": "ASH" }, - { "id": "ashare", "symbol": "ashare", "name": "ASHARE" }, - { "id": "ashera", "symbol": "ash", "name": "Ashera" }, - { "id": "ash-token", "symbol": "ash", "name": "Ash Token" }, - { "id": "asia-coin", "symbol": "ASIA", "name": "Asia Coin" }, - { - "id": "asia-pacific-electronic-coin", - "symbol": "apecoin", - "name": "Asia Pacific Electronic Coin" - }, - { "id": "asimi", "symbol": "asimi", "name": "ASIMI" }, - { "id": "asix", "symbol": "asix", "name": "ASIX" }, - { "id": "ask-me-anything", "symbol": "ama", "name": "Ask Me Anything" }, - { "id": "askobar-network", "symbol": "asko", "name": "Asko" }, - { - "id": "as-monaco-fan-token", - "symbol": "asm", - "name": "AS Monaco Fan Token" - }, - { "id": "aspire", "symbol": "asp", "name": "Aspire" }, - { "id": "aspo-world", "symbol": "aspo", "name": "ASPO World" }, - { "id": "as-roma-fan-token", "symbol": "asr", "name": "AS Roma Fan Token" }, - { "id": "assangedao", "symbol": "justice", "name": "AssangeDAO" }, - { "id": "assemble-protocol", "symbol": "asm", "name": "Assemble Protocol" }, - { "id": "assent-protocol", "symbol": "asnt", "name": "Assent Protocol" }, - { - "id": "asset-backed-protocol", - "symbol": "abp", - "name": "Asset Backed Protocol" - }, - { "id": "asta", "symbol": "asta", "name": "ASTA" }, - { "id": "astar", "symbol": "astr", "name": "Astar" }, - { "id": "aster", "symbol": "atc", "name": "Aster" }, - { "id": "ast-finance", "symbol": "ast", "name": "AST.finance" }, - { - "id": "aston-martin-cognizant-fan-token", - "symbol": "am", - "name": "Aston Martin Cognizant Fan Token" - }, - { - "id": "aston-villa-fan-token", - "symbol": "avl", - "name": "Aston Villa Fan Token" - }, - { - "id": "astra-guild-ventures", - "symbol": "agv", - "name": "Astra Guild Ventures" - }, - { "id": "astral-farm", "symbol": "astral", "name": "Astral Farm" }, - { "id": "astralis", "symbol": "STAR", "name": "Astralis" }, - { "id": "astrapad", "symbol": "astra", "name": "AstraPad" }, - { "id": "astra-protocol-2", "symbol": "$astr", "name": "Astra Protocol" }, - { "id": "astrobirdz", "symbol": "abz", "name": "AstroBirdz" }, - { "id": "astrodonkey", "symbol": "dnky", "name": "AstroDonkey" }, - { "id": "astroelon", "symbol": "elonone", "name": "AstroElon" }, - { "id": "astrokitty", "symbol": "ak", "name": "Astrokitties" }, - { "id": "astronaut", "symbol": "naut", "name": "Astronaut" }, - { "id": "astronodes", "symbol": "astro", "name": "AstroNodes" }, - { "id": "astronos", "symbol": "astro", "name": "Astronos" }, - { "id": "astroport", "symbol": "astro", "name": "Astroport" }, - { "id": "astrosanta", "symbol": "asa", "name": "AstroSanta" }, - { "id": "astrospaces-io", "symbol": "spaces", "name": "AstroSpaces.io" }, - { "id": "astroswap", "symbol": "astro", "name": "AstroSwap" }, - { "id": "astrotools", "symbol": "astro", "name": "AstroTools" }, - { "id": "astro-verse", "symbol": "asv", "name": "Astro Verse" }, - { "id": "astrowar", "symbol": "awar", "name": "Astrowar" }, - { "id": "asuka-inu", "symbol": "asuka", "name": "Asuka Inu" }, - { "id": "asuna-inu", "symbol": "asunainu", "name": "Asuna Inu" }, - { "id": "asva", "symbol": "asva", "name": "Asva Labs" }, - { "id": "asward-coin", "symbol": "asc", "name": "Ashward Coin" }, - { "id": "asyagro", "symbol": "asy", "name": "ASYAGRO" }, - { "id": "atari", "symbol": "atri", "name": "Atari" }, - { "id": "ata-token", "symbol": "ata", "name": "ATA Token" }, - { "id": "atauro", "symbol": "ATR", "name": "Atauro" }, - { "id": "atbcoin", "symbol": "atb", "name": "ATBCoin" }, - { "id": "at-finance", "symbol": "at", "name": "AT Finance" }, - { "id": "atheios", "symbol": "ath", "name": "Atheios" }, - { "id": "athena-money", "symbol": "ath", "name": "Athena Money" }, - { "id": "athena-money-owl", "symbol": "owl", "name": "Athena Money Owl" }, - { "id": "athenas", "symbol": "athenasv2", "name": "Athenas" }, - { "id": "atheneum", "symbol": "aem", "name": "Atheneum" }, - { "id": "athens", "symbol": "ath", "name": "Athens" }, - { "id": "ath-games", "symbol": "ATHD", "name": "ATH Games" }, - { "id": "atlantis", "symbol": "atlas", "name": "Atlantis" }, - { "id": "atlantis-finance", "symbol": "atls", "name": "Atlantis Finance" }, - { "id": "atlantis-loans", "symbol": "atl", "name": "Atlantis Loans" }, - { - "id": "atlantis-loans-polygon", - "symbol": "atlx", - "name": "Atlantis Loans Polygon" - }, - { "id": "atlantis-metaverse", "symbol": "tau", "name": "Atlantis Metaverse" }, - { "id": "atlantis-token", "symbol": "atis", "name": "Atlantis Token" }, - { - "id": "atlantis-universe-money", - "symbol": "aum", - "name": "Atlantis Universe Money" - }, - { "id": "atlas-cloud", "symbol": "atlas", "name": "Atlas Cloud" }, - { "id": "atlas-dex", "symbol": "ats", "name": "Atlas DEX" }, - { "id": "atlas-protocol", "symbol": "ATP", "name": "Atlas Protocol" }, - { "id": "atlas-usv", "symbol": "usv", "name": "Atlas USV" }, - { - "id": "atletico-madrid", - "symbol": "atm", - "name": "Atletico Madrid Fan Token" - }, - { "id": "atlo", "symbol": "atlo", "name": "Atlo" }, - { "id": "atmos", "symbol": "atmos", "name": "Atmos" }, - { "id": "atn", "symbol": "atn", "name": "ATN" }, - { "id": "atomic-token", "symbol": "atmc", "name": "Atomic Token" }, - { "id": "atomic-wallet-coin", "symbol": "awc", "name": "Atomic Wallet Coin" }, - { "id": "atompad", "symbol": "atpad", "name": "AtomPad" }, - { "id": "atrollcity", "symbol": "pine", "name": "Atrollcity" }, - { "id": "atromg8", "symbol": "ag8", "name": "ATROMG8" }, - { "id": "attack-wagon", "symbol": "atk", "name": "Attack Wagon" }, - { "id": "attila", "symbol": "att", "name": "Attila" }, - { "id": "attlas-token", "symbol": "ats", "name": "Attlas Token" }, - { "id": "attn", "symbol": "attn", "name": "ATTN" }, - { "id": "attrace", "symbol": "attr", "name": "Attrace" }, - { "id": "auction", "symbol": "auction", "name": "Bounce" }, - { "id": "auctus", "symbol": "auc", "name": "Auctus" }, - { "id": "audax", "symbol": "audax", "name": "Audax" }, - { "id": "audiocoin", "symbol": "adc", "name": "AudioCoin" }, - { "id": "auditchain", "symbol": "audt", "name": "Auditchain" }, - { "id": "audius", "symbol": "audio", "name": "Audius" }, - { "id": "audius-wormhole", "symbol": "audio", "name": "Audius (Wormhole)" }, - { "id": "augmented-finance", "symbol": "agf", "name": "Augmented Finance" }, - { "id": "augur", "symbol": "rep", "name": "Augur" }, - { "id": "augury-finance", "symbol": "omen", "name": "Augury Finance" }, - { "id": "aunit", "symbol": "aunit", "name": "Aunit" }, - { "id": "aureo", "symbol": "aur", "name": "AUREO" }, - { "id": "aureus-nummus-gold", "symbol": "ang", "name": "Aureus Nummus Gold" }, - { "id": "aureus-token", "symbol": "aureusrh", "name": "Aureus Token" }, - { "id": "auric-network", "symbol": "auscm", "name": "Auric Network" }, - { "id": "aurigami", "symbol": "ply", "name": "Aurigami" }, - { "id": "aurix", "symbol": "aur", "name": "Aurix" }, - { "id": "aurora", "symbol": "aoa", "name": "Aurora Chain" }, - { "id": "auroracoin", "symbol": "aur", "name": "Auroracoin" }, - { "id": "aurora-dao", "symbol": "idex", "name": "IDEX" }, - { "id": "aurora-near", "symbol": "aurora", "name": "Aurora" }, - { "id": "aurorashibamoon", "symbol": "ashibam", "name": "AuroraShibaMoon" }, - { "id": "auroratoken", "symbol": "aurora", "name": "AuroraToken" }, - { "id": "aurora-token", "symbol": "$adtx", "name": "Aurora Dimension" }, - { "id": "aurory", "symbol": "aury", "name": "Aurory" }, - { "id": "aurory-wormhole", "symbol": "aury", "name": "Aurory (Wormhole)" }, - { "id": "auroshiba", "symbol": "ashiba", "name": "AuroShiba" }, - { "id": "aurum", "symbol": "aur", "name": "Aurum" }, - { "id": "auruscoin", "symbol": "awx", "name": "AurusDeFi" }, - { "id": "aurusgold", "symbol": "awg", "name": "AurusGOLD" }, - { "id": "aurus-silver", "symbol": "aws", "name": "AurusSILVER" }, - { "id": "ausd", "symbol": "ausd", "name": "aUSD" }, - { - "id": "australian-crypto-coin-green", - "symbol": "accg", - "name": "Australian Crypto Coin Green" - }, - { - "id": "australian-kelpie", - "symbol": "knockers", - "name": "Australian Kelpie" - }, - { - "id": "australian-safe-shepherd", - "symbol": "ass", - "name": "Australian Safe Shepherd" - }, - { "id": "auto", "symbol": "auto", "name": "Auto" }, - { "id": "autobahn", "symbol": "atb", "name": "Autobahn" }, - { "id": "autobahn-network", "symbol": "txl", "name": "Autobahn Network" }, - { "id": "autobitco-token", "symbol": "abco", "name": "AutoBitco Token" }, - { "id": "autobusd", "symbol": "abs", "name": "Autobusd" }, - { "id": "autocrypto", "symbol": "au", "name": "AutoCrypto" }, - { "id": "automata", "symbol": "ata", "name": "Automata" }, - { "id": "automatic-network", "symbol": "aumi", "name": "AutoMatic Network" }, - { "id": "automaticup", "symbol": "atmup", "name": "AutoMaticUp" }, - { "id": "autonio", "symbol": "niox", "name": "Autonio" }, - { "id": "autoshark", "symbol": "jaws", "name": "AutoShark" }, - { "id": "autz-token", "symbol": "autz", "name": "AUTZ Token" }, - { "id": "auxilium", "symbol": "aux", "name": "Auxilium" }, - { "id": "avakus", "symbol": "avak", "name": "Avakus" }, - { "id": "avalanche-2", "symbol": "avax", "name": "Avalanche" }, - { - "id": "avalanche-honeybee", - "symbol": "a.bee", - "name": "Avalanche HoneyBee" - }, - { "id": "avalanchevoid", "symbol": "void", "name": "AvalancheVoid" }, - { - "id": "avalanche-wormhole", - "symbol": "avax", - "name": "Avalanche (Wormhole)" - }, - { "id": "avalaunch", "symbol": "xava", "name": "Avalaunch" }, - { "id": "avaluse", "symbol": "aval", "name": "Avaluse" }, - { "id": "ava-mim", "symbol": "avamim", "name": "Ava MIM" }, - { "id": "avania", "symbol": "nova", "name": "Avania" }, - { "id": "avanyan", "symbol": "anyan", "name": "AvaNyan" }, - { "id": "avaocado-dao", "symbol": "avg", "name": "Avocado DAO" }, - { "id": "avaone-finance", "symbol": "avao", "name": "AvaOne Finance" }, - { "id": "avapay", "symbol": "avapay", "name": "AvaPay" }, - { "id": "avara", "symbol": "avr", "name": "AVARA" }, - { - "id": "avastr-vault-nftx", - "symbol": "avastr", - "name": "AVASTR Vault (NFTX)" - }, - { "id": "avata-network", "symbol": "avat", "name": "AVATA Network" }, - { "id": "avaterra", "symbol": "terra", "name": "Avaterra" }, - { "id": "ava-time", "symbol": "avtime", "name": "Ava Time" }, - { "id": "avaware", "symbol": "ave", "name": "Avaware" }, - { "id": "avaware-usd", "symbol": "ausd", "name": "Avaware USD" }, - { "id": "avax-1x", "symbol": "avax1x", "name": "AVAX 1x" }, - { "id": "avax-capital-node", "symbol": "acn", "name": "AVAX Capital Node" }, - { "id": "avaxdao", "symbol": "avaxd", "name": "AvaxDAO" }, - { "id": "avaxlauncher", "symbol": "avxl", "name": "Avaxlauncher" }, - { "id": "avaxtars", "symbol": "avxt", "name": "Avaxtars" }, - { "id": "avaxup", "symbol": "avaxup", "name": "AvaxUp" }, - { "id": "aventus", "symbol": "avt", "name": "Aventus" }, - { "id": "avianite", "symbol": "avian", "name": "Avianite" }, - { "id": "avian-network", "symbol": "avn", "name": "AVIAN" }, - { "id": "avinoc", "symbol": "avinoc", "name": "AVINOC" }, - { "id": "avme", "symbol": "avme", "name": "AVME" }, - { "id": "avnrich", "symbol": "avn", "name": "AVNRich" }, - { "id": "avocadocoin", "symbol": "avdo", "name": "AvocadoCoin" }, - { "id": "axe", "symbol": "axe", "name": "Axe" }, - { "id": "axel", "symbol": "axel", "name": "AXEL" }, - { "id": "axelar-network", "symbol": "axl", "name": "Axelar Network" }, - { "id": "axentro", "symbol": "axnt", "name": "Axentro" }, - { "id": "axia", "symbol": "axiav3", "name": "Axia" }, - { "id": "axia-coin", "symbol": "axc", "name": "AXIA Coin" }, - { "id": "axial", "symbol": "axl", "name": "AXiaL" }, - { "id": "axial-token", "symbol": "axial", "name": "Axial Token" }, - { "id": "axie-infinity", "symbol": "axs", "name": "Axie Infinity" }, - { - "id": "axie-infinity-shard-wormhole", - "symbol": "axset", - "name": "Axie Infinity Shard (Wormhole)" - }, - { "id": "axioms", "symbol": "axi", "name": "Axioms" }, - { "id": "axion", "symbol": "axn", "name": "Axion" }, - { "id": "axis-defi", "symbol": "axis", "name": "Axis DeFi" }, - { "id": "axis-token", "symbol": "axis", "name": "AXIS Token" }, - { "id": "axl-inu", "symbol": "axl", "name": "AXL INU" }, - { "id": "axpire", "symbol": "axpr", "name": "aXpire" }, - { "id": "axus-coin", "symbol": "axus", "name": "Axus Coin" }, - { "id": "azbit", "symbol": "az", "name": "Azbit" }, - { "id": "azeusx", "symbol": "azx", "name": "AzeusX" }, - { "id": "az-fundchain", "symbol": "azt", "name": "AZ Fundchain" }, - { "id": "azit", "symbol": "azit", "name": "Azit" }, - { "id": "aztec-nodes-sun", "symbol": "sun", "name": "Aztec Nodes SUN" }, - { "id": "azuki", "symbol": "azuki", "name": "Azuki" }, - { "id": "azuma-coin", "symbol": "azum", "name": "Azuma Coin" }, - { "id": "azuras", "symbol": "uzz", "name": "UZURAS" }, - { "id": "azus", "symbol": "azu", "name": "Azus" }, - { "id": "b20", "symbol": "b20", "name": "B20" }, - { "id": "b21", "symbol": "b21", "name": "B21" }, - { "id": "b2bcoin-2", "symbol": "b2b", "name": "B2Bcoin" }, - { "id": "b2u-coin", "symbol": "b2u", "name": "B2U Coin" }, - { "id": "baanx", "symbol": "bxx", "name": "Baanx" }, - { "id": "baasid", "symbol": "baas", "name": "BaaSid" }, - { "id": "babb", "symbol": "bax", "name": "BABB" }, - { "id": "babelfish", "symbol": "babel", "name": "BabelFish" }, - { "id": "baboon-financial", "symbol": "BOON", "name": "Baboon Financial" }, - { "id": "baby-ada", "symbol": "babyada", "name": "Baby ADA" }, - { "id": "babyaeth", "symbol": "baeth", "name": "BabyAETH" }, - { "id": "baby-aetherius", "symbol": "babyaeth", "name": "Baby Aetherius" }, - { "id": "baby-alucard", "symbol": "alucard", "name": "Baby Alucard" }, - { "id": "babyarmy", "symbol": "barmy", "name": "BabyArmy" }, - { "id": "baby-bali", "symbol": "BB", "name": "Baby Bali" }, - { "id": "babybanana", "symbol": "bbnana", "name": "BabyBanana" }, - { "id": "babybitcoin", "symbol": "babybitc", "name": "BabyBitcoin" }, - { "id": "baby-bitcoin", "symbol": "bbtc", "name": "Baby Bitcoin" }, - { "id": "babybnb", "symbol": "babybnb", "name": "BabyBNB" }, - { "id": "babyboo", "symbol": "babyboo", "name": "BabyBoo" }, - { "id": "baby-boxer", "symbol": "bboxer", "name": "Baby Boxer" }, - { "id": "babybusd", "symbol": "babybusd", "name": "BabyBUSD" }, - { "id": "baby-cake", "symbol": "babycake", "name": "Baby Cake" }, - { "id": "babycare", "symbol": "babycare", "name": "BabyCare" }, - { "id": "babycasper", "symbol": "babycasper", "name": "Baby Casper" }, - { "id": "baby-catcoin", "symbol": "babycats", "name": "Baby Catcoin" }, - { "id": "babycatgirl", "symbol": "babycatgirl", "name": "BabyCatGirl" }, - { "id": "baby-cat-girl", "symbol": "bbycat", "name": "Baby Cat Girl" }, - { "id": "baby-chedda", "symbol": "babychedda", "name": "Baby Chedda" }, - { "id": "baby-cheems-inu", "symbol": "bci", "name": "Baby Cheems Inu" }, - { "id": "baby-defido", "symbol": "babydefido", "name": "Baby DeFido" }, - { - "id": "baby-doge-billionaire", - "symbol": "babydb", - "name": "Baby Doge Billionaire" - }, - { "id": "babydogecake", "symbol": "bdc", "name": "BabyDogeCake" }, - { - "id": "baby-doge-cash", - "symbol": "babydogecash", - "name": "Baby Doge Cash" - }, - { "id": "baby-doge-coin", "symbol": "babydoge", "name": "Baby Doge Coin" }, - { "id": "babydoge-coin-eth", "symbol": "babydoge", "name": "BabyDoge ETH" }, - { "id": "baby-doge-inu", "symbol": "$babydogeinu", "name": "Baby Doge Inu" }, - { "id": "babydogeking", "symbol": "babydogeking", "name": "BabyDogeKing" }, - { - "id": "baby-doge-money-maker", - "symbol": "BABYDOGEMM", - "name": "Baby Doge Money Maker" - }, - { "id": "babydogex", "symbol": "BDOGEX", "name": "BabyDogeX" }, - { "id": "babydogezilla", "symbol": "babydogezilla", "name": "BabyDogeZilla" }, - { "id": "baby-dogo-coin", "symbol": "babydogo", "name": "Baby Dogo Coin" }, - { "id": "babydot", "symbol": "bdot", "name": "BabyDot" }, - { "id": "baby-doug", "symbol": "babydoug", "name": "Baby Doug" }, - { "id": "babyelon", "symbol": "babyelon", "name": "BabyElon" }, - { "id": "babyeth", "symbol": "babyeth", "name": "BabyEth" }, - { "id": "babyethereum", "symbol": "bbeth", "name": "BabyEthereum" }, - { "id": "babyeth-v2", "symbol": "babyethv2", "name": "BabyETH V2" }, - { "id": "baby-everdoge", "symbol": "baby everdoge", "name": "Baby EverDoge" }, - { "id": "babyfeg", "symbol": "bbfeg", "name": "Babyfeg" }, - { "id": "babyfloki", "symbol": "babyfloki", "name": "BabyFloki" }, - { "id": "baby-floki", "symbol": "BabyFloki", "name": "Baby Floki" }, - { - "id": "baby-floki-billionaire", - "symbol": "BabyFB", - "name": "Baby Floki Billionaire" - }, - { - "id": "baby-floki-coin", - "symbol": "BABYFLOKICOIN", - "name": "Baby Floki Coin" - }, - { "id": "baby-floki-doge", "symbol": "babyfd", "name": "Baby Floki Doge" }, - { "id": "baby-floki-inu", "symbol": "bfloki", "name": "Baby Floki Inu" }, - { "id": "baby-floki-up", "symbol": "bfu", "name": "Baby Floki Up" }, - { - "id": "babyflokizilla", - "symbol": "babyflokizilla", - "name": "BabyFlokiZilla" - }, - { "id": "babyfrog", "symbol": "babyfrog", "name": "BabyFrog" }, - { "id": "babyharmony", "symbol": "babyharmony", "name": "BabyHarmony" }, - { "id": "baby-kishu", "symbol": "babykishu", "name": "Baby Kishu" }, - { "id": "babykitty", "symbol": "BabyKitty", "name": "BabyKitty" }, - { "id": "babykraken", "symbol": "krakbaby", "name": "BabyKraken" }, - { "id": "baby-kshark", "symbol": "bks", "name": "Baby KShark" }, - { "id": "baby-lambo-inu", "symbol": "blinu", "name": "Baby Lambo Inu" }, - { "id": "babylink", "symbol": "bblink", "name": "BabyLink" }, - { "id": "baby-lion", "symbol": "bln", "name": "Baby Lion" }, - { "id": "babylondao", "symbol": "bby", "name": "BabylonDAO" }, - { "id": "babylon-finance", "symbol": "babl", "name": "Babylon Finance" }, - { "id": "babylons", "symbol": "babi", "name": "Babylons" }, - { "id": "baby-lovely-inu", "symbol": "blovely", "name": "Baby Lovely Inu" }, - { "id": "baby-meta", "symbol": "babymeta", "name": "Baby Meta" }, - { "id": "baby-mind", "symbol": "bmnd", "name": "Baby Mind" }, - { "id": "baby-moon-floki", "symbol": "floki", "name": "Baby Moon Floki" }, - { "id": "baby-moon-wolf", "symbol": "babywolf", "name": "Baby Moon Wolf" }, - { "id": "baby-o-baby", "symbol": "bob", "name": "Baby o Baby" }, - { "id": "babyokx", "symbol": "babyokx", "name": "BABYOKX" }, - { "id": "baby-panda", "symbol": "bpanda", "name": "Baby Panda" }, - { "id": "baby-pig-token", "symbol": "babypig", "name": "Baby Pig Token" }, - { "id": "baby-pokemoon", "symbol": "bpm", "name": "Baby Pokemoon" }, - { "id": "baby-poocoin", "symbol": "babypoo", "name": "Baby Poocoin" }, - { - "id": "babypumpkin-finance", - "symbol": "bump", - "name": "BabyPumpkin Finance" - }, - { "id": "babyquick", "symbol": "babyquick", "name": "BabyQuick" }, - { "id": "baby-ripple", "symbol": "BabyXRP", "name": "Baby Ripple" }, - { "id": "baby-ryukyu-inu", "symbol": "brki", "name": "Baby Ryukyu Inu" }, - { "id": "babysafemoon", "symbol": "bsfm", "name": "BabySafeMoon" }, - { "id": "baby-saitama", "symbol": "babysaitama", "name": "Baby Saitama" }, - { "id": "baby-samo-coin", "symbol": "baby", "name": "Baby Samo Coin" }, - { "id": "baby-satoshi", "symbol": "sats", "name": "Baby Satoshi" }, - { - "id": "baby-schrodinger-coin", - "symbol": "babydinger", - "name": "Baby Schrodinger Coin" - }, - { "id": "baby-shark", "symbol": "shark", "name": "Baby Shark" }, - { "id": "baby-shark-finance", "symbol": "bbs", "name": "Baby-Shark.Finance" }, - { "id": "baby-shark-inu", "symbol": "dododo", "name": "Baby Shark Inu" }, - { "id": "baby-shark-tank", "symbol": "bashtank", "name": "Baby Shark Tank" }, - { "id": "baby-shiba", "symbol": "bhiba", "name": "Baby Shiba" }, - { "id": "baby-shiba-coin", "symbol": "babyshiba", "name": "Baby Shiba Coin" }, - { "id": "baby-shiba-dot", "symbol": "bsd", "name": "Baby Shiba Dot" }, - { - "id": "baby-shiba-inu", - "symbol": "babyshibainu", - "name": "Baby Shiba Inu" - }, - { - "id": "baby-shiba-rocket", - "symbol": "bshibr", - "name": "Baby Shiba Rocket" - }, - { "id": "babyshibby-inu", "symbol": "babyshib", "name": "BabyShibby Inu" }, - { "id": "baby-shibnobi", "symbol": "babyshinja", "name": "Baby Shibnobi" }, - { "id": "baby-soulja-boy", "symbol": "DRACO", "name": "Baby Soulja Boy" }, - { "id": "baby-squid-games", "symbol": "squids", "name": "Baby Squid Games" }, - { - "id": "baby-starlink-doge", - "symbol": "starlinkdoge", - "name": "Baby Starlink Doge" - }, - { "id": "babyswap", "symbol": "baby", "name": "BabySwap" }, - { "id": "baby-tether", "symbol": "BABYTETHER", "name": "BABY TETHER" }, - { "id": "babytigergold", "symbol": "babytiger", "name": "BabyTigerGold" }, - { "id": "baby-tiger-king", "symbol": "babytk", "name": "Baby Tiger King" }, - { "id": "baby-token", "symbol": "baby", "name": "Baby Token" }, - { "id": "babytraderjoe", "symbol": "bjoe", "name": "BabyTraderJoe" }, - { "id": "baby-trump", "symbol": "babytrump", "name": "Baby Trump" }, - { "id": "babyusdt", "symbol": "babyusdt", "name": "BabyUSDT" }, - { "id": "babywakandainu", "symbol": "babywkd", "name": "Babywakandainu" }, - { "id": "babywhale", "symbol": "bbw", "name": "BabyWhale" }, - { - "id": "baby-white-hamster", - "symbol": "$bwh", - "name": "Baby White Hamster" - }, - { "id": "babywhitetiger", "symbol": "bwt", "name": "BabyWhiteTiger" }, - { "id": "baby-woj", "symbol": "bwj", "name": "Baby WOJ" }, - { "id": "babyxape", "symbol": "babyx", "name": "BabyXape" }, - { "id": "babyxrp", "symbol": "bbyxrp", "name": "BabyXrp" }, - { "id": "backed-protocol", "symbol": "bakt", "name": "Backed Protocol" }, - { "id": "backpacker-coin", "symbol": "bpc", "name": "BackPacker Coin" }, - { "id": "bacon", "symbol": "bacon", "name": "BACON" }, - { "id": "bacondao", "symbol": "bacon", "name": "BaconDAO" }, - { "id": "baconswap", "symbol": "bacon", "name": "BaconSwap" }, - { - "id": "baddest-alpha-ape-bundle", - "symbol": "aped", - "name": "Baddest Alpha Ape Bundle" - }, - { "id": "badger-dao", "symbol": "badger", "name": "Badger DAO" }, - { - "id": "badger-sett-badger", - "symbol": "bbadger", - "name": "Badger Sett Badger" - }, - { "id": "badger-sett-digg", "symbol": "bdigg", "name": "Badger Sett Digg" }, - { "id": "bae", "symbol": "bae", "name": "Bae" }, - { "id": "baepay", "symbol": "baepay", "name": "BAEPAY" }, - { "id": "bafe-io", "symbol": "bafe", "name": "Bafe.io" }, - { - "id": "bafi-finance-token", - "symbol": "bafi", - "name": "Bafi Finance Token" - }, - { "id": "bagel", "symbol": "bagel", "name": "Bagels Finance" }, - { "id": "bagswap", "symbol": "bswap", "name": "BagSwap" }, - { "id": "baguette", "symbol": "bag", "name": "Baguette" }, - { "id": "bagus-wallet", "symbol": "bg", "name": "Bagus Wallet" }, - { "id": "bahtcoin", "symbol": "bht", "name": "Bahtcoin" }, - { "id": "baitcoin", "symbol": "bait", "name": "Baitcoin" }, - { "id": "bakc-vault-nftx", "symbol": "bakc", "name": "BAKC Vault (NFTX)" }, - { "id": "bake-coin", "symbol": "bakecoin", "name": "Bake Coin" }, - { "id": "bakedcake", "symbol": "bakedcake", "name": "BakedCake Finance" }, - { "id": "baked-token", "symbol": "baked", "name": "Baked Token" }, - { "id": "baker-guild", "symbol": "baker", "name": "Baker Guild" }, - { "id": "bakerytoken", "symbol": "bake", "name": "BakerySwap" }, - { "id": "bakerytools", "symbol": "tbake", "name": "BakeryTools" }, - { "id": "bake-up", "symbol": "$bakeup", "name": "Bake Up" }, - { "id": "baklava", "symbol": "bava", "name": "Baklava" }, - { "id": "baksdao", "symbol": "baks", "name": "BaksDAO" }, - { "id": "balanced-dollars", "symbol": "bnUSD", "name": "Balanced Dollars" }, - { "id": "balancer", "symbol": "bal", "name": "Balancer" }, - { "id": "balance-tokens", "symbol": "baln", "name": "Balance Tokens" }, - { "id": "balicoin", "symbol": "bali", "name": "Bali Coin" }, - { - "id": "balikesirspor-token", - "symbol": "blks", - "name": "Balıkesirspor Token" - }, - { "id": "balisari", "symbol": "bst", "name": "Balisari" }, - { - "id": "bali-social-integrated", - "symbol": "bsi", - "name": "Bali Social Integrated" - }, - { "id": "bali-token", "symbol": "bli", "name": "Bali Token" }, - { "id": "balkari-token", "symbol": "bkr", "name": "Balkari Token" }, - { "id": "ball-coin", "symbol": "ball", "name": "BALL Coin" }, - { "id": "balloon-coin", "symbol": "balo", "name": "Balloon Coin" }, - { "id": "balloonsville-air", "symbol": "air", "name": "Balloonsville AIR" }, - { "id": "ballotbox", "symbol": "bbx", "name": "Ballotbox" }, - { "id": "ballswap", "symbol": "bsp", "name": "BallSwap" }, - { - "id": "ballswapper-accelerator-reflection-token", - "symbol": "bart", - "name": "Ballswapper Accelerator Reflection Token" - }, - { "id": "balpha", "symbol": "balpha", "name": "bAlpha" }, - { "id": "bamboo-coin", "symbol": "bmbo", "name": "Bamboo Coin" }, - { "id": "bamboo-defi", "symbol": "bamboo", "name": "BambooDeFi" }, - { "id": "bamboo-token-2", "symbol": "bamboo", "name": "Bamboo Token" }, - { "id": "bami", "symbol": "bami", "name": "Bami" }, - { "id": "banana", "symbol": "banana", "name": "Banana" }, - { "id": "banana-bucks", "symbol": "bab", "name": "Banana Bucks" }, - { "id": "banana-finance", "symbol": "banana", "name": "Banana Finance" }, - { - "id": "banana-task-force-ape", - "symbol": "btfa", - "name": "Banana Task Force Ape" - }, - { "id": "bananatok", "symbol": "bna", "name": "BananaTok" }, - { "id": "banana-token", "symbol": "bnana", "name": "Chimpion" }, - { "id": "bananaz-club", "symbol": "BANANAZ", "name": "BANANAZ CLUB" }, - { "id": "banano", "symbol": "ban", "name": "Banano" }, - { "id": "banca", "symbol": "banca", "name": "Banca" }, - { "id": "bancor", "symbol": "bnt", "name": "Bancor Network Token" }, - { - "id": "bancor-governance-token", - "symbol": "vbnt", - "name": "Bancor Governance Token" - }, - { "id": "bancor-wormhole", "symbol": "bnt", "name": "Bancor (Wormhole)" }, - { "id": "band-protocol", "symbol": "band", "name": "Band Protocol" }, - { - "id": "bang-decentralized", - "symbol": "bang", - "name": "BANG Decentralized" - }, - { "id": "bankcoincash", "symbol": "bcash", "name": "BankCoin BCash" }, - { "id": "bankera", "symbol": "bnk", "name": "Bankera" }, - { "id": "bankerdoge", "symbol": "banker", "name": "BankerDoge" }, - { "id": "bankers-dream", "symbol": "bank$", "name": "Bankers Dream" }, - { "id": "banketh", "symbol": "banketh", "name": "BankEth" }, - { "id": "bankless-bed-index", "symbol": "bed", "name": "Bankless BED Index" }, - { "id": "bankless-dao", "symbol": "bank", "name": "Bankless DAO" }, - { - "id": "bankless-defi-innovation-index", - "symbol": "gmi", - "name": "Bankless DeFi Innovation Index" - }, - { "id": "banklife", "symbol": "lib", "name": "Banklife" }, - { - "id": "bankroll-extended-token", - "symbol": "bnkrx", - "name": "Bankroll Extended Token" - }, - { "id": "bankroll-network", "symbol": "bnkr", "name": "Bankroll Network" }, - { "id": "bankroll-vault", "symbol": "vlt", "name": "Bankroll Vault" }, - { "id": "banksea", "symbol": "kse", "name": "Banksea" }, - { "id": "banksocial", "symbol": "bsl", "name": "BankSocial" }, - { "id": "bankwupt", "symbol": "bankwupt", "name": "Bankwupt" }, - { "id": "banque-universal", "symbol": "cbu", "name": "Banque Universal" }, - { "id": "bantu", "symbol": "xbn", "name": "Bantu" }, - { "id": "bao-finance", "symbol": "bao", "name": "Bao Finance" }, - { "id": "barbecueswap", "symbol": "bbq", "name": "BarbecueSwap" }, - { "id": "bare", "symbol": "bare", "name": "BARE" }, - { "id": "barnbridge", "symbol": "bond", "name": "BarnBridge" }, - { "id": "barrel", "symbol": "barrel", "name": "Barrel" }, - { "id": "barter", "symbol": "brtr", "name": "Barter" }, - { "id": "bartertrade", "symbol": "bart", "name": "BarterTrade" }, - { "id": "based-finance", "symbol": "based", "name": "Based Finance" }, - { "id": "based-gold", "symbol": "bgld", "name": "Based Gold" }, - { "id": "based-money", "symbol": "$based", "name": "Based Money" }, - { "id": "based-shares", "symbol": "bshare", "name": "BASED Shares" }, - { "id": "based-token", "symbol": "bdc", "name": "Based Token" }, - { "id": "base-protocol", "symbol": "base", "name": "Base Protocol" }, - { "id": "base-reward-token", "symbol": "brw", "name": "Base Reward Token" }, - { "id": "basic", "symbol": "basic", "name": "BASIC" }, - { - "id": "basic-attention-token", - "symbol": "bat", - "name": "Basic Attention Token" - }, - { - "id": "basic-attention-token-wormhole", - "symbol": "bat", - "name": "Basic Attention Token (Wormhole)" - }, - { "id": "basid-coin", "symbol": "basid", "name": "Basid Coin" }, - { "id": "basis-bond", "symbol": "bab", "name": "Basis Bond" }, - { "id": "basis-cash", "symbol": "bac", "name": "Basis Cash" }, - { "id": "basis-coin-cash", "symbol": "bcc", "name": "Basis Coin Cash" }, - { "id": "basiscoin-share", "symbol": "bcs", "name": "Basis Coin Share" }, - { "id": "basis-dollar", "symbol": "bsd", "name": "Basis Dollar" }, - { - "id": "basis-dollar-share", - "symbol": "bsds", - "name": "Basis Dollar Share" - }, - { - "id": "basis-gold-share-heco", - "symbol": "bags", - "name": "Basis Gold Share (Heco)" - }, - { "id": "basis-markets", "symbol": "basis", "name": "basis.markets" }, - { "id": "basis-share", "symbol": "bas", "name": "Basis Share" }, - { "id": "basketball-legends", "symbol": "BBL", "name": "Basketball Legends" }, - { "id": "basketballverse", "symbol": "bvr", "name": "Basketballverse" }, - { "id": "basketcoin", "symbol": "bskt", "name": "BasketCoin" }, - { "id": "basketdao", "symbol": "bask", "name": "BasketDAO" }, - { - "id": "basketdao-defi-index", - "symbol": "bdi", - "name": "BasketDAO DeFi Index" - }, - { - "id": "bastille-de-la-bouje", - "symbol": "bastille", - "name": "Bastille De La Bouje" - }, - { "id": "bastion-protocol", "symbol": "bstn", "name": "Bastion Protocol" }, - { "id": "bata", "symbol": "bta", "name": "Bata" }, - { "id": "batman", "symbol": "batman", "name": "Batman" }, - { "id": "batorrent", "symbol": "ba", "name": "BaTorrent" }, - { - "id": "battle-esports-coin", - "symbol": "bes", - "name": "battle esports coin" - }, - { "id": "battle-for-life", "symbol": "bfl", "name": "Battle for Life" }, - { "id": "battle-hero", "symbol": "bath", "name": "Battle Hero" }, - { "id": "battle-inu", "symbol": "BINU", "name": "Battle Inu" }, - { "id": "battlemechs", "symbol": "gemz", "name": "BattleMechs" }, - { - "id": "battle-of-guardians-share", - "symbol": "bgs", - "name": "Battle of Guardians Share" - }, - { "id": "battle-pets", "symbol": "pet", "name": "Battle Pets" }, - { "id": "battleroyaletoken", "symbol": "brtk", "name": "BattleRoyaleToken" }, - { "id": "battle-saga", "symbol": "btl", "name": "Battle Saga" }, - { "id": "battleverse", "symbol": "bvc", "name": "BattleVerse" }, - { - "id": "bayc-history", - "symbol": "sweep", - "name": "BAYC History -The Great Floor Sweep" - }, - { "id": "bayc-vault-nftx", "symbol": "bayc", "name": "BAYC Vault (NFTX)" }, - { "id": "bb-gaming", "symbol": "bb", "name": "BB Gaming" }, - { "id": "bbscoin", "symbol": "bbs", "name": "BBSCoin" }, - { "id": "bbs-network", "symbol": "bbs", "name": "BBS Network" }, - { "id": "bchpad", "symbol": "bpad", "name": "BCHPad" }, - { "id": "bcpay-fintech", "symbol": "bcpay", "name": "BCPAY FinTech" }, - { "id": "b-cube-ai", "symbol": "bcube", "name": "B-cube.ai" }, - { "id": "bcv", "symbol": "bcv", "name": "BitCapitalVendor" }, - { "id": "bdollar", "symbol": "bdo", "name": "bDollar" }, - { "id": "bdollar-share", "symbol": "sbdo", "name": "bDollar Share" }, - { "id": "beachboyz", "symbol": "boyz", "name": "BeachBoyz" }, - { "id": "beach-token", "symbol": "beach", "name": "Beach Token" }, - { "id": "beach-token-bsc", "symbol": "beach", "name": "Beach Token BSC" }, - { "id": "beacon", "symbol": "becn", "name": "Beacon" }, - { "id": "beaglecake", "symbol": "beaglecake", "name": "BeagleCake" }, - { "id": "beam", "symbol": "beam", "name": "BEAM" }, - { "id": "beamswap", "symbol": "glint", "name": "BeamSwap" }, - { "id": "bean", "symbol": "bean", "name": "Bean" }, - { "id": "bean-cash", "symbol": "bitb", "name": "Bean Cash" }, - { "id": "bearn-fi", "symbol": "bfi", "name": "Bearn.fi" }, - { "id": "beast-masters", "symbol": "master", "name": "Beast Masters" }, - { "id": "beast-nft", "symbol": "bnft", "name": "Beast NFT" }, - { "id": "beast-token", "symbol": "beast", "name": "Beast Token" }, - { "id": "beatbind", "symbol": "bbnd", "name": "BeatBind" }, - { "id": "beatzcoin", "symbol": "btzc", "name": "BeatzCoin" }, - { - "id": "beavis-and-butthead", - "symbol": "bbh", - "name": "Beavis and Butthead" - }, - { "id": "bebop-inu", "symbol": "bebop-inu", "name": "Bebop Inu" }, - { "id": "becoswap-token", "symbol": "beco", "name": "BecoSwap Token" }, - { "id": "bedpage-coin", "symbol": "bed", "name": "Bedpage Coin" }, - { "id": "bedrock", "symbol": "rock", "name": "Bedrock" }, - { "id": "beechat", "symbol": "chat", "name": "BeeChat" }, - { - "id": "beefy-escrowed-fantom", - "symbol": "beftm", - "name": "Beefy Escrowed Fantom" - }, - { "id": "beefy-finance", "symbol": "bifi", "name": "Beefy.Finance" }, - { "id": "bee-inu", "symbol": "beeinu", "name": "Bee Inu" }, - { "id": "beekan", "symbol": "bkbt", "name": "BeeKan / Beenews" }, - { "id": "beenode", "symbol": "bnode", "name": "Beenode" }, - { "id": "beer-money", "symbol": "beer", "name": "Beer Money" }, - { "id": "bee-safe", "symbol": "BSAFE", "name": "Bee Safe" }, - { "id": "beethoven-x", "symbol": "beets", "name": "Beethoven X" }, - { "id": "beetle-coin", "symbol": "beet", "name": "Beetle Coin" }, - { "id": "beglobal-finance", "symbol": "glb", "name": "BeGlobal Finance" }, - { "id": "beholder", "symbol": "eye", "name": "Behodler" }, - { "id": "bela", "symbol": "aqua", "name": "Bela Aqua" }, - { "id": "beldex", "symbol": "bdx", "name": "Beldex" }, - { "id": "believer", "symbol": "blvr", "name": "BELIEVER" }, - { "id": "belifex", "symbol": "befx", "name": "Belifex" }, - { "id": "belka", "symbol": "BLK", "name": "Belka" }, - { "id": "bella-protocol", "symbol": "bel", "name": "Bella Protocol" }, - { "id": "bellcoin", "symbol": "bell", "name": "Bellcoin" }, - { "id": "belon-dao", "symbol": "BE", "name": "Belon DAO" }, - { "id": "belrium", "symbol": "bel", "name": "Belrium" }, - { "id": "belt", "symbol": "belt", "name": "Belt" }, - { "id": "beluga-fi", "symbol": "beluga", "name": "Beluga.fi" }, - { "id": "bem", "symbol": "bemt", "name": "BEM" }, - { "id": "bemil-coin", "symbol": "bem", "name": "Bemil Coin" }, - { "id": "benative", "symbol": "bnv", "name": "BeNative" }, - { - "id": "benchmark-protocol", - "symbol": "mark", - "name": "Benchmark Protocol" - }, - { - "id": "benchmark-protocol-governance-token", - "symbol": "bend", - "name": "Benchmark Protocol Governance Token" - }, - { "id": "benddao", "symbol": "bend", "name": "BendDao" }, - { "id": "benepit", "symbol": "bnp", "name": "BenePit" }, - { "id": "bengals", "symbol": "pbengals", "name": "Bengals - Super Bowl" }, - { "id": "benqi", "symbol": "qi", "name": "BENQI" }, - { - "id": "benqi-liquid-staked-avax", - "symbol": "savax", - "name": "BENQI Liquid Staked AVAX" - }, - { "id": "bent-finance", "symbol": "bent", "name": "Bent Finance" }, - { "id": "benzene", "symbol": "bzn", "name": "Benzene" }, - { "id": "bep20-leo", "symbol": "bLEO", "name": "BEP20 LEO" }, - { "id": "bepay", "symbol": "becoin", "name": "bePAY Finance" }, - { "id": "bepis", "symbol": "BEPIS", "name": "BEPIS" }, - { "id": "bepro-network", "symbol": "bepro", "name": "BEPRO Network" }, - { "id": "bernard", "symbol": "bern", "name": "Bernard" }, - { "id": "berry", "symbol": "Berry", "name": "Berry" }, - { "id": "berry-data", "symbol": "bry", "name": "Berry Data" }, - { "id": "berryswap", "symbol": "berry", "name": "BerrySwap" }, - { "id": "berserk-inu", "symbol": "berserk", "name": "Berserk Inu" }, - { "id": "berylbit", "symbol": "brb", "name": "BerylBit" }, - { "id": "beshare-token", "symbol": "bst", "name": "Beshare Token" }, - { "id": "beskar", "symbol": "bsk-baa025", "name": "Beskar" }, - { "id": "bestay", "symbol": "bsy", "name": "Bestay" }, - { "id": "beta-finance", "symbol": "beta", "name": "Beta Finance" }, - { "id": "betdao", "symbol": "bdao", "name": "BetDao" }, - { "id": "betdice", "symbol": "dice", "name": "BetDice" }, - { "id": "betherchip", "symbol": "bec", "name": "Betherchip" }, - { "id": "bethereum", "symbol": "bether", "name": "Bethereum" }, - { "id": "betify", "symbol": "betify", "name": "Betify" }, - { "id": "betller-coin", "symbol": "btllr", "name": "Betller Coin" }, - { "id": "betswamp", "symbol": "bets", "name": "Betswamp" }, - { "id": "betswap-gg", "symbol": "bsgg", "name": "Betswap.gg" }, - { "id": "better-money", "symbol": "better", "name": "Better Money" }, - { "id": "betu", "symbol": "betu", "name": "Betu" }, - { "id": "betxoin", "symbol": "betxc", "name": "Betxoin" }, - { "id": "beverage", "symbol": "beverage", "name": "BEVERAGE" }, - { "id": "beverage-token", "symbol": "drink", "name": "Beverage Token" }, - { "id": "beyondcoin", "symbol": "bynd", "name": "Beyondcoin" }, - { "id": "beyond-finance", "symbol": "byn", "name": "BeyondFi" }, - { "id": "beyond-protocol", "symbol": "bp", "name": "Beyond Protocol" }, - { "id": "bezant", "symbol": "bznt", "name": "Bezant" }, - { "id": "bezoge-earth", "symbol": "bezoge", "name": "Bezoge Earth" }, - { "id": "bezosdoge", "symbol": "$bedoge", "name": "BezosDoge" }, - { "id": "bfg-token", "symbol": "bfg", "name": "BFG Token" }, - { "id": "bficoin", "symbol": "bfic", "name": "Bficoin" }, - { "id": "bfk-warzone", "symbol": "bfk", "name": "BFK WARZONE" }, - { "id": "bgan-vault-nftx", "symbol": "bgan", "name": "BGAN Vault (NFTX)" }, - { "id": "bgl-token", "symbol": "bgl", "name": "BGL Token" }, - { "id": "bgogo", "symbol": "bgg", "name": "Bgogo Token" }, - { "id": "bgov", "symbol": "bgov", "name": "BGOV" }, - { "id": "bholdus-token", "symbol": "bho", "name": "Bholdus Token" }, - { "id": "biblepay", "symbol": "bbp", "name": "BiblePay" }, - { "id": "bibox-token", "symbol": "bix", "name": "Bibox Token" }, - { "id": "biconomy", "symbol": "bico", "name": "Biconomy" }, - { - "id": "biconomy-exchange-token", - "symbol": "bit", - "name": "Biconomy Exchange Token" - }, - { "id": "bidao", "symbol": "bid", "name": "Bidao" }, - { "id": "biden", "symbol": "biden", "name": "BIDEN Token" }, - { "id": "bidipass", "symbol": "bdp", "name": "BidiPass" }, - { "id": "bifi", "symbol": "bifi", "name": "BiFi" }, - { "id": "bifrost", "symbol": "bfc", "name": "Bifrost" }, - { - "id": "bifrost-native-coin", - "symbol": "bnc", - "name": "Bifrost Native Coin" - }, - { "id": "bigbang-core", "symbol": "bbc", "name": "BigBang Core" }, - { "id": "bigbang-game", "symbol": "bbgc", "name": "BigBang Game" }, - { "id": "bigbom-eco", "symbol": "bbo", "name": "Bigbom" }, - { - "id": "big-brain-capital-dao", - "symbol": "bbc dao", - "name": "Big Brain Capital DAO" - }, - { "id": "bigdata-cash", "symbol": "bdcash", "name": "BigdataCash" }, - { "id": "big-data-protocol", "symbol": "bdp", "name": "Big Data Protocol" }, - { "id": "big-defi-energy", "symbol": "bde", "name": "Big Defi Energy" }, - { "id": "big-digital-shares", "symbol": "bds", "name": "Big Digital Shares" }, - { "id": "big-dog-coin", "symbol": "bdog", "name": "Big Dog Coin" }, - { "id": "big-eth", "symbol": "bigeth", "name": "BIG ETH" }, - { "id": "bigfoot", "symbol": "foot", "name": "BIGFOOT" }, - { - "id": "big-fund-capital-dao", - "symbol": "bfc", - "name": "Big Fund Capital DAO" - }, - { "id": "biggerminds", "symbol": "mind", "name": "BiggerMINDS" }, - { "id": "big-league", "symbol": "bglg", "name": "Big League" }, - { "id": "big-mouth-monster", "symbol": "bmm", "name": "Big Mouth Monster" }, - { "id": "bigo-token", "symbol": "bigo", "name": "BIGOCOIN" }, - { "id": "bigshortbets", "symbol": "bigsb", "name": "BigShortBets" }, - { "id": "bigwinner", "symbol": "big", "name": "Bigwinner" }, - { "id": "biitgatti", "symbol": "bitgatti", "name": "Bitgatti" }, - { "id": "bikini-finance", "symbol": "BIKINI", "name": "Bikini Finance" }, - { "id": "bilaxy-token", "symbol": "bia", "name": "Bilaxy Token" }, - { "id": "bilira", "symbol": "tryb", "name": "BiLira" }, - { "id": "billionaire-plus", "symbol": "bplus", "name": "Billionaire Plus" }, - { "id": "billionhappiness", "symbol": "bhc", "name": "BillionHappiness" }, - { "id": "bill-token", "symbol": "bill", "name": "Bill Token" }, - { "id": "bimp-finance", "symbol": "bimp", "name": "Bimp Finance" }, - { "id": "binahero", "symbol": "hero", "name": "BinaHero" }, - { "id": "binahunter", "symbol": "bhunt", "name": "BinaHunter" }, - { "id": "binamars", "symbol": "bmars", "name": "Binamars" }, - { "id": "binamars-game", "symbol": "bgame", "name": "Binamars Game" }, - { "id": "binamon", "symbol": "bmon", "name": "Binamon" }, - { "id": "binance8", "symbol": "b8", "name": "Binance8" }, - { "id": "binance-bitcoin", "symbol": "btcb", "name": "Binance Bitcoin" }, - { "id": "binancecoin", "symbol": "bnb", "name": "BNB" }, - { - "id": "binance-coin-wormhole", - "symbol": "bnb", - "name": "Binance Coin (Wormhole)" - }, - { "id": "binancedog", "symbol": "bidog", "name": "Binancedog" }, - { "id": "binance-eth", "symbol": "beth", "name": "Binance ETH staking" }, - { "id": "binancehodl", "symbol": "bihodl", "name": "BinanceHODL" }, - { "id": "binanceidr", "symbol": "bidr", "name": "BIDR" }, - { - "id": "binance-multi-chain-capital", - "symbol": "bmcc", - "name": "Binance Multi-Chain Capital" - }, - { - "id": "binance-peg-avalanche", - "symbol": "avax", - "name": "Binance-Peg Avalanche" - }, - { - "id": "binance-peg-bitcoin-cash", - "symbol": "bch", - "name": "Binance-Peg Bitcoin Cash" - }, - { - "id": "binance-peg-cardano", - "symbol": "ada", - "name": "Binance-Peg Cardano" - }, - { - "id": "binance-peg-dogecoin", - "symbol": "doge", - "name": "Binance-Peg Dogecoin" - }, - { "id": "binance-peg-eos", "symbol": "eos", "name": "Binance-Peg EOS" }, - { - "id": "binance-peg-filecoin", - "symbol": "fil", - "name": "Binance-Peg Filecoin" - }, - { "id": "binance-peg-firo", "symbol": "firo", "name": "Binance-Peg Firo" }, - { "id": "binance-peg-iotex", "symbol": "iotx", "name": "Binance-Peg IoTeX" }, - { - "id": "binance-peg-litecoin", - "symbol": "ltc", - "name": "Binance-Peg Litecoin" - }, - { - "id": "binance-peg-ontology", - "symbol": "ont", - "name": "Binance-Peg Ontology" - }, - { - "id": "binance-peg-polkadot", - "symbol": "dot", - "name": "Binance-Peg Polkadot" - }, - { "id": "binance-peg-xrp", "symbol": "xrp", "name": "Binance-Peg XRP" }, - { - "id": "binance-smart-chain-girl", - "symbol": "bscgirl", - "name": "Binance Smart Chain Girl" - }, - { "id": "binance-usd", "symbol": "busd", "name": "Binance USD" }, - { - "id": "binance-usd-wormhole", - "symbol": "busdet", - "name": "Binance USD (Wormhole)" - }, - { - "id": "binance-usd-wormhole-from-bsc", - "symbol": "busdbs", - "name": "Binance USD (Wormhole from BSC)" - }, - { - "id": "binance-wrapped-btc", - "symbol": "bbtc", - "name": "Binance Wrapped BTC" - }, - { - "id": "binance-wrapped-dot", - "symbol": "bdot", - "name": "Binance Wrapped DOT" - }, - { "id": "binapet", "symbol": "bpet", "name": "Binapet" }, - { "id": "binarium", "symbol": "bin", "name": "Binarium" }, - { "id": "binary-cat", "symbol": "kitty", "name": "Binary Cat" }, - { "id": "binarydao", "symbol": "byte", "name": "BinaryDAO" }, - { "id": "binaryx", "symbol": "bnx", "name": "BinaryX" }, - { "id": "bincentive", "symbol": "bcnt", "name": "Bincentive" }, - { "id": "binemon", "symbol": "bin", "name": "Binemon" }, - { "id": "bingdwendwen", "symbol": "bingdwendwen", "name": "BingDwenDwen" }, - { "id": "bingo-cash", "symbol": "bgo", "name": "Bingo Cash" }, - { "id": "bingo-doge", "symbol": "bdoge", "name": "Bingo Doge" }, - { "id": "bingo-game", "symbol": "bingo", "name": "Bingo Game" }, - { "id": "bingo-share", "symbol": "sbgo", "name": "Bingo Share" }, - { "id": "bingus-network", "symbol": "bingus", "name": "Bingus Network" }, - { "id": "binjit-coin", "symbol": "bnj", "name": "Binjit Coin" }, - { "id": "binom-protocol", "symbol": "binom", "name": "Binom Protocol" }, - { "id": "binopoly", "symbol": "bino", "name": "Binopoly" }, - { "id": "binosaurs", "symbol": "binosaurs", "name": "Binosaurs" }, - { "id": "binspirit", "symbol": "binspirit", "name": "binSPIRIT" }, - { "id": "binstarter", "symbol": "bsr", "name": "BinStarter" }, - { "id": "bintex-futures", "symbol": "bntx", "name": "Bintex Futures" }, - { "id": "biokkoin", "symbol": "bkkg", "name": "Biokkoin" }, - { "id": "biopassport", "symbol": "biot", "name": "Bio Passport" }, - { "id": "bios", "symbol": "bios", "name": "0x_nodes" }, - { "id": "biotron", "symbol": "btrn", "name": "Biotron" }, - { "id": "bip", "symbol": "bip", "name": "Minter" }, - { "id": "birake", "symbol": "bir", "name": "Birake" }, - { "id": "birb", "symbol": "birb", "name": "Birb" }, - { "id": "birdchain", "symbol": "bird", "name": "Birdchain" }, - { "id": "bird-money", "symbol": "bird", "name": "Bird.Money" }, - { "id": "birdy-finance", "symbol": "bird", "name": "Birdy Finance" }, - { "id": "bishares", "symbol": "bison", "name": "BiShares" }, - { "id": "bishu-finance", "symbol": "bishufi", "name": "Bishu Finance" }, - { "id": "bismuth", "symbol": "bis", "name": "Bismuth" }, - { "id": "bistroo", "symbol": "bist", "name": "Bistroo" }, - { "id": "biswap", "symbol": "bsw", "name": "Biswap" }, - { "id": "biswap-token", "symbol": "biswap", "name": "Biswap Token" }, - { "id": "bit2me", "symbol": "b2m", "name": "Bit2Me" }, - { "id": "bitacium", "symbol": "xbond", "name": "Bitacium" }, - { "id": "bitalgo", "symbol": "alg", "name": "Bitalgo" }, - { "id": "bitant", "symbol": "bitant", "name": "BitANT" }, - { "id": "bitastir", "symbol": "bita", "name": "BitAstir" }, - { "id": "bitau", "symbol": "bau", "name": "BitAU" }, - { "id": "bitazza", "symbol": "btz", "name": "Bitazza" }, - { "id": "bitball", "symbol": "btb", "name": "Bitball" }, - { "id": "bitball-treasure", "symbol": "btrs", "name": "Bitball Treasure" }, - { "id": "bitbar", "symbol": "btb", "name": "Bitbar" }, - { "id": "bitbase-token", "symbol": "btbs", "name": "BitBase Token" }, - { "id": "bitberry-token", "symbol": "bbr", "name": "Bitberry Token" }, - { "id": "bitblocks-finance", "symbol": "bbkfi", "name": "BitBlocks Finance" }, - { "id": "bitblocks-fire", "symbol": "bfire", "name": "Bitblocks Fire" }, - { "id": "bitblocks-project", "symbol": "bbk", "name": "BitBlocks" }, - { "id": "bitbook-token", "symbol": "bbt", "name": "BitBook" }, - { "id": "bitboost", "symbol": "bbt", "name": "BitBoost" }, - { "id": "bitbucks", "symbol": "bitbucks", "name": "BitBucks" }, - { "id": "bitcanna", "symbol": "bcna", "name": "BitCanna" }, - { "id": "bitcarecoin", "symbol": "bcare", "name": "BitCareCoin" }, - { "id": "bitcash", "symbol": "bitc", "name": "BitCash" }, - { "id": "bitcashpay-old", "symbol": "bcp", "name": "BitcashPay (Old)" }, - { "id": "bitceo", "symbol": "bceo", "name": "bitCEO" }, - { "id": "bitcherry", "symbol": "bchc", "name": "BitCherry" }, - { "id": "bitcicoin", "symbol": "bitci", "name": "Bitcicoin" }, - { "id": "bitclave", "symbol": "cat", "name": "BitClave" }, - { "id": "bitcloud", "symbol": "btdx", "name": "Bitcloud" }, - { "id": "bitclout", "symbol": "deso", "name": "Decentralized Social" }, - { "id": "bitcny", "symbol": "bitcny", "name": "bitCNY" }, - { "id": "bitcoffeen", "symbol": "bff", "name": "Bitcoffeen" }, - { "id": "bitcoin", "symbol": "btc", "name": "Bitcoin" }, - { "id": "bitcoin-2", "symbol": "btc2", "name": "Bitcoin 2" }, - { "id": "bitcoin-adult", "symbol": "btad", "name": "Bitcoin Adult" }, - { - "id": "bitcoin-and-ethereum-standard-token", - "symbol": "best", - "name": "Bitcoin and Ethereum Standard Token" - }, - { "id": "bitcoin-anonymous", "symbol": "btca", "name": "Bitcoin Anonymous" }, - { "id": "bitcoin-asia", "symbol": "btca", "name": "Bitcoin Asia" }, - { "id": "bitcoin-asset", "symbol": "bta", "name": "Bitcoin Asset (Old)" }, - { "id": "bitcoin-asset-2", "symbol": "bta", "name": "Bitcoin Asset" }, - { "id": "bitcoin-atom", "symbol": "bca", "name": "Bitcoin Atom" }, - { "id": "bitcoinbam", "symbol": "btcbam", "name": "BitcoinBam" }, - { "id": "bitcoin-bep2", "symbol": "btcb", "name": "Bitcoin BEP2" }, - { "id": "bitcoin-br", "symbol": "BTCBR", "name": "Bitcoin BR" }, - { "id": "bitcoinbrand", "symbol": "btcb", "name": "BitcoinBrand" }, - { "id": "bitcoin-cash", "symbol": "bch", "name": "Bitcoin Cash" }, - { "id": "bitcoin-cash-sv", "symbol": "bsv", "name": "Bitcoin SV" }, - { "id": "bitcoin-city-coin", "symbol": "bcity", "name": "Bitcoin City Coin" }, - { - "id": "bitcoin-confidential", - "symbol": "bc", - "name": "Bitcoin Confidential" - }, - { "id": "bitcoin-cure", "symbol": "byron", "name": "Byron" }, - { "id": "bitcoin-diamond", "symbol": "bcd", "name": "Bitcoin Diamond" }, - { "id": "bitcoin-fast", "symbol": "bcf", "name": "Bitcoin Fast" }, - { "id": "bitcoin-final", "symbol": "btcf", "name": "Bitcoin Final" }, - { "id": "bitcoin-god", "symbol": "god", "name": "Bitcoin God" }, - { "id": "bitcoin-gold", "symbol": "btg", "name": "Bitcoin Gold" }, - { "id": "bitcoin-green", "symbol": "bitg", "name": "Bitcoin Green" }, - { "id": "bitcoin-hd", "symbol": "bhd", "name": "Bitcoin HD" }, - { "id": "bitcoin-hot", "symbol": "bth", "name": "Bitcoin Hot" }, - { "id": "bitcoin-instant", "symbol": "bti", "name": "Bitcoin Instant" }, - { - "id": "bitcoin-international", - "symbol": "btci", - "name": "Bitcoin International" - }, - { "id": "bitcoin-latinum", "symbol": "ltnm", "name": "Bitcoin Latinum" }, - { "id": "bitcoinlegend", "symbol": "bcl", "name": "BitcoinLegend" }, - { "id": "bitcoin-lightning", "symbol": "bltg", "name": "Block-Logic" }, - { - "id": "bitcoin-metaverse", - "symbol": "bitmeta", - "name": "Bitcoin Metaverse" - }, - { "id": "bitcoinmoney", "symbol": "bcm", "name": "BitcoinMoney" }, - { "id": "bitcoinmono", "symbol": "btcmz", "name": "BitcoinMono" }, - { "id": "bitcoin-networks", "symbol": "btcn", "name": "Bitcoin Networks" }, - { "id": "bitcoin-one", "symbol": "btcone", "name": "BitCoin One" }, - { "id": "bitcoin-pay", "symbol": "btp", "name": "Bitcoin Pay" }, - { "id": "bitcoin-plus", "symbol": "xbc", "name": "Bitcoin Plus" }, - { "id": "bitcoinprint", "symbol": "btcp", "name": "BitcoinPrint" }, - { "id": "bitcoin-private", "symbol": "btcp", "name": "Bitcoin Private" }, - { "id": "bitcoin-pro", "symbol": "btcp", "name": "Bitcoin Pro" }, - { "id": "bitcoin-red", "symbol": "btcred", "name": "Bitcoin Red" }, - { "id": "bitcoinregular", "symbol": "btrl", "name": "BitcoinRegular" }, - { "id": "bitcoin-rhodium", "symbol": "xrc", "name": "xRhodium" }, - { "id": "bitcoin-sb", "symbol": "bsb", "name": "Bitcoin SB" }, - { "id": "bitcoin-scrypt", "symbol": "btcs", "name": "Bitcoin Scrypt" }, - { "id": "bitcoinsov", "symbol": "bsov", "name": "BitcoinSoV" }, - { "id": "bitcoinstaking", "symbol": "bsk", "name": "BitcoinStaking" }, - { "id": "bitcoin-stash", "symbol": "bsh", "name": "Bitcoin Stash" }, - { "id": "bitcoin-subsidium", "symbol": "xbtx", "name": "Bitcoin Subsidium" }, - { "id": "bitcoin-token", "symbol": "btct", "name": "BitcoinToken" }, - { "id": "bitcoin-trust", "symbol": "bct", "name": "Bitcoin Trust" }, - { "id": "bitcoin-ultra", "symbol": "btcu", "name": "BitcoinUltra" }, - { "id": "bitcoinv", "symbol": "btcv", "name": "BitcoinV" }, - { "id": "bitcoin-vault", "symbol": "btcv", "name": "Bitcoin Vault" }, - { "id": "bitcoinvend", "symbol": "bcvt", "name": "BitcoinVend" }, - { "id": "bitcoinx", "symbol": "bcx", "name": "BitcoinX" }, - { "id": "bitcoinx-2", "symbol": "btcx", "name": "BitcoinXGames" }, - { "id": "bitcoinz", "symbol": "btcz", "name": "BitcoinZ" }, - { "id": "bitcoiva", "symbol": "bca", "name": "Bitcoiva" }, - { "id": "bitcolojix", "symbol": "btcix", "name": "BITCOLOJIX" }, - { "id": "bitcomine", "symbol": "bme", "name": "BitcoMine" }, - { "id": "bitcomo", "symbol": "bm", "name": "Bitcomo" }, - { "id": "bitconnect", "symbol": "bcc", "name": "Bitconnect" }, - { "id": "bitconnectx-genesis", "symbol": "bccx", "name": "BCCXGenesis" }, - { "id": "bitcore", "symbol": "btx", "name": "BitCore" }, - { "id": "bitcorn", "symbol": "corn", "name": "BITCORN" }, - { "id": "bitcrex-coin", "symbol": "bic", "name": "Bitcrex Coin" }, - { "id": "bitcrush", "symbol": "crush", "name": "Bitcrush" }, - { "id": "bitcurate", "symbol": "btcr", "name": "Bitcurate" }, - { "id": "bitdao", "symbol": "bit", "name": "BitDAO" }, - { "id": "bitdegree", "symbol": "bdg", "name": "BitDegree" }, - { "id": "bitecoin", "symbol": "$BITC", "name": "Bitecoin" }, - { - "id": "bit-ecological-digital", - "symbol": "bed", - "name": "Bit Ecological Digital" - }, - { "id": "bitenium-token", "symbol": "bt", "name": "Bitenium Token" }, - { "id": "bit_financial", "symbol": "bfc", "name": "Bit Financial" }, - { "id": "bitflowers", "symbol": "petal", "name": "bitFlowers" }, - { "id": "bitforex", "symbol": "bf", "name": "Bitforex Token" }, - { "id": "bitfresh", "symbol": "bft", "name": "Bitfresh" }, - { "id": "bitgear", "symbol": "gear", "name": "Bitgear" }, - { "id": "bitgem", "symbol": "xbtg", "name": "Bitgem" }, - { "id": "bitgesell", "symbol": "bgl", "name": "Bitgesell" }, - { "id": "bitget-token", "symbol": "bgb", "name": "Bitget Token" }, - { "id": "bitguild", "symbol": "plat", "name": "BitGuild PLAT" }, - { "id": "bithachi", "symbol": "bith", "name": "Bithachi" }, - { "id": "bithashex", "symbol": "bhax", "name": "Bithashex" }, - { "id": "bithash-token", "symbol": "bt", "name": "BitHash Token" }, - { "id": "bithostcoin", "symbol": "bih", "name": "BitHostCoin" }, - { "id": "bit-hotel", "symbol": "bth", "name": "Bit Hotel" }, - { "id": "bitica-coin", "symbol": "bdcc", "name": "BITICA COIN" }, - { "id": "bitjob", "symbol": "stu", "name": "bitJob" }, - { "id": "bitkub-coin", "symbol": "kub", "name": "Bitkub Coin" }, - { "id": "bitliberte", "symbol": "liberte", "name": "Bitliberte" }, - { "id": "bitlocus", "symbol": "btl", "name": "Bitlocus" }, - { "id": "bitlorrent", "symbol": "blt", "name": "Bitlorrent" }, - { "id": "bitmark", "symbol": "marks", "name": "Bitmark" }, - { "id": "bitmart-token", "symbol": "bmx", "name": "BitMart Token" }, - { "id": "bitmax-coin", "symbol": "bmx", "name": "Bitmax Coin" }, - { "id": "bitmex-token", "symbol": "bmex", "name": "BitMEX Token" }, - { "id": "bitmic", "symbol": "bmic", "name": "Bitmic" }, - { "id": "bitmoney", "symbol": "bit", "name": "BitMoney" }, - { "id": "bitnautic", "symbol": "btnt", "name": "BitNautic" }, - { "id": "bitnorm", "symbol": "bn", "name": "Bitnorm" }, - { "id": "bito-coin", "symbol": "bito", "name": "BITO Coin" }, - { "id": "bitone", "symbol": "bio", "name": "BITONE" }, - { "id": "bitonyx-token", "symbol": "btnyx", "name": "BitOnyx Token" }, - { "id": "bitorbit", "symbol": "bitorb", "name": "BitOrbit" }, - { "id": "bitpaid-token", "symbol": "btp", "name": "Bitpaid Token" }, - { - "id": "bitpanda-ecosystem-token", - "symbol": "best", - "name": "Bitpanda Ecosystem Token" - }, - { "id": "bitpower", "symbol": "bpp", "name": "Bitpower" }, - { - "id": "bit-public-talent-network", - "symbol": "bptn", - "name": "Bit Public Talent Network" - }, - { "id": "bitradio", "symbol": "bro", "name": "Bitradio" }, - { "id": "bitrent", "symbol": "rntb", "name": "BitRent" }, - { "id": "bit-rides", "symbol": "rides", "name": "Bit Rides" }, - { "id": "bitrise-token", "symbol": "brise", "name": "Bitgert" }, - { "id": "bitriver", "symbol": "btr", "name": "Bitriver" }, - { "id": "bitroncoin", "symbol": "btrn", "name": "BitronCoin" }, - { "id": "bitrue-token", "symbol": "btr", "name": "Bitrue Coin" }, - { "id": "bitscreener", "symbol": "bitx", "name": "BitScreener" }, - { "id": "bitshares", "symbol": "bts", "name": "BitShares" }, - { "id": "bitshark", "symbol": "btshk", "name": "Bitshark" }, - { "id": "bitshiba", "symbol": "shiba", "name": "BitShiba" }, - { "id": "bitsol-finance", "symbol": "btsl", "name": "Bitsol Finance" }, - { "id": "bitsong", "symbol": "btsg", "name": "BitSong" }, - { "id": "bitsonic-token", "symbol": "bsc", "name": "Bitsonic Token" }, - { "id": "bitspawn", "symbol": "spwn", "name": "Bitspawn" }, - { "id": "bitstake", "symbol": "xbs", "name": "BitStake" }, - { "id": "bitsten-token", "symbol": "bst", "name": "Bitsten Token" }, - { "id": "bit-store-coin", "symbol": "store", "name": "Bit Store" }, - { "id": "bitsum", "symbol": "mat", "name": "Matka" }, - { "id": "bitswift", "symbol": "bits", "name": "Bitswift" }, - { "id": "bitto-exchange", "symbol": "BITTO", "name": "BITTO" }, - { "id": "bittoken", "symbol": "bitt", "name": "BITToken" }, - { "id": "bittokens", "symbol": "bxt", "name": "BitTokens" }, - { "id": "bittorrent", "symbol": "btt", "name": "BitTorrent" }, - { "id": "bittorrent-old", "symbol": "bttold", "name": "BitTorrent [OLD]" }, - { "id": "bittracksystems", "symbol": "bttr", "name": "BittrackSystems" }, - { "id": "bittube", "symbol": "tube", "name": "BitTube" }, - { "id": "bittwatt", "symbol": "bwt", "name": "Bittwatt" }, - { "id": "bitup", "symbol": "bitup", "name": "BitUp" }, - { "id": "bitup-token", "symbol": "but", "name": "BitUP Token" }, - { "id": "bitvalley", "symbol": "bitv", "name": "BitValley" }, - { "id": "bitvalve-2", "symbol": "btv", "name": "BitValve" }, - { "id": "bitvote", "symbol": "btv", "name": "Bitvote" }, - { "id": "bitweb", "symbol": "bte", "name": "Bitweb" }, - { "id": "bitwell-token", "symbol": "well", "name": "Bitwell Token" }, - { "id": "bitwhite", "symbol": "btw", "name": "BitWhite" }, - { "id": "bitwin24", "symbol": "bwi", "name": "Bitwin24" }, - { "id": "bitz", "symbol": "bitz", "name": "bitz" }, - { "id": "bitzeny", "symbol": "zny", "name": "BitZeny" }, - { "id": "bixb-coin", "symbol": "bixb", "name": "BixB Coin" }, - { "id": "bixcpro", "symbol": "bixcpro", "name": "BIXCPRO" }, - { "id": "bizzcoin", "symbol": "bizz", "name": "BIZZCOIN" }, - { "id": "bkex-token", "symbol": "bkk", "name": "BKEX Chain" }, - { "id": "bking-finance", "symbol": "bkf", "name": "BKING Finance" }, - { "id": "blackcoin", "symbol": "blk", "name": "BlackCoin" }, - { "id": "black-diamond", "symbol": "diamonds", "name": "Black Diamond" }, - { "id": "blackdragon-token", "symbol": "BDT", "name": "BlackDragon Token" }, - { "id": "black-eye-galaxy", "symbol": "byg", "name": "Black Eye Galaxy" }, - { "id": "blackhat-coin", "symbol": "blkc", "name": "BlackHat Coin" }, - { "id": "blackholedao", "symbol": "bho", "name": "BlackHoleDAO" }, - { - "id": "blackhole-protocol", - "symbol": "black", - "name": "BlackHole Protocol" - }, - { - "id": "blackholeswap-compound-dai-usdc", - "symbol": "bhsc", - "name": "BlackHoleSwap-Compound DAI/USDC" - }, - { "id": "black-kishu-inu", "symbol": "bishu", "name": "Black Kishu Inu" }, - { "id": "black-label", "symbol": "klb", "name": "Black Label" }, - { "id": "black-lemon", "symbol": "bom", "name": "Black Lemon" }, - { "id": "blackmoon-crypto", "symbol": "bmc", "name": "Blackmoon Crypto" }, - { "id": "blacknet", "symbol": "bln", "name": "Blacknet" }, - { "id": "blackpearl-chain", "symbol": "bplc", "name": "BlackPearl Token" }, - { "id": "black-phoenix", "symbol": "bpx", "name": "Black Phoenix" }, - { "id": "blackpoker", "symbol": "bpkr", "name": "BlackPoker" }, - { "id": "blackpool-token", "symbol": "bpt", "name": "BlackPool Token" }, - { "id": "black-shiba", "symbol": "bshib", "name": "Black Shiba" }, - { "id": "black-whale", "symbol": "blk", "name": "Black Whale" }, - { "id": "blade", "symbol": "blade", "name": "BladeWarrior" }, - { "id": "blanc", "symbol": "blanc", "name": "Blanc" }, - { "id": "blank", "symbol": "blank", "name": "BlockWallet" }, - { "id": "blast", "symbol": "blast", "name": "BLAST" }, - { "id": "blastoise-inu", "symbol": "blast", "name": "Blastoise Inu" }, - { "id": "blaze-network", "symbol": "BLZN", "name": "Blaze Network" }, - { "id": "blaze-the-cat", "symbol": "$blaze", "name": "Blaze the cat" }, - { "id": "blind-boxes", "symbol": "bles", "name": "Blind Boxes" }, - { "id": "blink", "symbol": "blk", "name": "BLink" }, - { "id": "blin-metaverse", "symbol": "blin", "name": "Blin Metaverse" }, - { "id": "blitzpredict", "symbol": "xbp", "name": "BlitzPick" }, - { "id": "blizzard", "symbol": "xblzd", "name": "Blizzard" }, - { "id": "blizzard-network", "symbol": "blizz", "name": "Blizzard Network" }, - { "id": "blizz-finance", "symbol": "blzz", "name": "Blizz Finance" }, - { "id": "blocery", "symbol": "bly", "name": "Blocery" }, - { "id": "block-18", "symbol": "18c", "name": "Block 18" }, - { "id": "block2play", "symbol": "B2P", "name": "Block2Play" }, - { "id": "block-ape-scissors", "symbol": "bas", "name": "Block Ape Scissors" }, - { "id": "blockasset", "symbol": "block", "name": "Blockasset" }, - { "id": "blockbank", "symbol": "bbank", "name": "BlockBank" }, - { "id": "blockbase", "symbol": "bbt", "name": "BlockBase" }, - { "id": "blockblend", "symbol": "bbl", "name": "BlockBlend" }, - { "id": "blockburn", "symbol": "burn", "name": "BlockBurn" }, - { "id": "blockcdn", "symbol": "bcdn", "name": "BlockCDN" }, - { - "id": "blockchain-adventurers-guild", - "symbol": "bag", - "name": "Blockchain Adventurers Guild" - }, - { - "id": "blockchain-based-distributed-super-computing-platform", - "symbol": "mbcc", - "name": "Blockchain-Based Distributed Super Computing Platform" - }, - { - "id": "blockchain-brawlers", - "symbol": "brwl", - "name": "Blockchain Brawlers" - }, - { - "id": "blockchain-certified-data-token", - "symbol": "bcdt", - "name": "EvidenZ" - }, - { - "id": "blockchain-cuties-universe", - "symbol": "cute", - "name": "Blockchain Cuties Universe" - }, - { - "id": "blockchain-cuties-universe-governance", - "symbol": "bcug", - "name": "Blockchain Cuties Universe Governance" - }, - { - "id": "blockchain-euro-project", - "symbol": "bepr", - "name": "Blockchain Euro Project" - }, - { - "id": "blockchain-exchange-alliance", - "symbol": "bxa", - "name": "Blockchain Exchange Alliance" - }, - { - "id": "blockchain-monster-hunt", - "symbol": "bcmc", - "name": "Blockchain Monster Hunt" - }, - { - "id": "blockchain-of-hash-power", - "symbol": "bhp", - "name": "Blockchain of Hash Power" - }, - { "id": "blockchainpoland", "symbol": "bcp", "name": "BlockchainPoland" }, - { - "id": "blockchain-property", - "symbol": "bpf", - "name": "Blockchain Property" - }, - { - "id": "blockchain-protocol", - "symbol": "protocol", - "name": "Blockchain Protocol" - }, - { - "id": "blockchain-quotations-index-token", - "symbol": "bqt", - "name": "Blockchain Quotations Index Token" - }, - { "id": "blockchainspace", "symbol": "guild", "name": "BlockchainSpace" }, - { - "id": "block-commerce-protocol", - "symbol": "bcp", - "name": "Block Commerce Protocol" - }, - { "id": "block-creatures", "symbol": "moolah", "name": "Block Creatures" }, - { "id": "block-duelers", "symbol": "bdt", "name": "Block Duelers" }, - { "id": "blockidcoin", "symbol": "bid", "name": "Blockidcoin" }, - { - "id": "blockmason-credit-protocol", - "symbol": "bcpt", - "name": "Blockmason Credit Protocol" - }, - { "id": "blockmason-link", "symbol": "blink", "name": "BlockMason Link" }, - { "id": "blockmax", "symbol": "ocb", "name": "BLOCKMAX" }, - { "id": "blockmesh-2", "symbol": "bmh", "name": "BlockMesh" }, - { "id": "blockmine", "symbol": "gold nugget", "name": "Blockmine" }, - { "id": "block-monsters", "symbol": "mnstrs", "name": "Block Monsters" }, - { "id": "blocknet", "symbol": "block", "name": "Blocknet" }, - { "id": "blocknotex", "symbol": "bnox", "name": "BlockNoteX" }, - { "id": "blockpad", "symbol": "blos", "name": "Blockius" }, - { "id": "blockpass", "symbol": "pass", "name": "Blockpass" }, - { "id": "blockport", "symbol": "bux", "name": "BUX Token" }, - { "id": "blocks", "symbol": "blocks", "name": "BLOCKS" }, - { "id": "blockspace-token", "symbol": "bls", "name": "Blocks Space" }, - { "id": "blocksport", "symbol": "bspt", "name": "Blocksport" }, - { "id": "blocksquare", "symbol": "bst", "name": "Blocksquare" }, - { "id": "blockstack", "symbol": "stx", "name": "Stacks" }, - { "id": "blockstamp", "symbol": "bst", "name": "BlockStamp" }, - { "id": "blockster", "symbol": "bxr", "name": "Blockster" }, - { - "id": "blockstream-mining-notes", - "symbol": "bmn", - "name": "BlockStream Mining Notes" - }, - { "id": "blockswap-network", "symbol": "cbsn", "name": "BlockSwap Network" }, - { "id": "blocktanium", "symbol": "bkt", "name": "Blocktanium" }, - { "id": "blocktix", "symbol": "tix", "name": "Blocktix" }, - { "id": "blocktyme", "symbol": "btym", "name": "Blocktyme" }, - { "id": "blockv", "symbol": "vee", "name": "BLOCKv" }, - { "id": "blockwarrior", "symbol": "blwa", "name": "BlockWarrior" }, - { "id": "blockwrk", "symbol": "wrk", "name": "BlockWRK" }, - { "id": "bloc-money", "symbol": "bloc", "name": "Bloc.Money" }, - { "id": "blocsport-one", "symbol": "bls", "name": "blocsport.one" }, - { "id": "blocto-token", "symbol": "blt", "name": "Blocto Token" }, - { "id": "bloggercoin", "symbol": "blogger", "name": "Bloggercoin" }, - { "id": "blokpad", "symbol": "bpad", "name": "BlokPad" }, - { "id": "bloktopia", "symbol": "blok", "name": "Bloktopia" }, - { "id": "blood-token", "symbol": "blood", "name": "Blood Token" }, - { "id": "bloody-shiba", "symbol": "BLOODYSHIBA", "name": "Bloody shiba" }, - { "id": "bloom", "symbol": "blt", "name": "Bloom" }, - { "id": "bloom-token", "symbol": "bloom", "name": "Bloom Token" }, - { - "id": "bloomzed-token", - "symbol": "blct", - "name": "Bloomzed Loyalty Club Ticket" - }, - { "id": "blossomcoin", "symbol": "blosm", "name": "BlossomCoin" }, - { "id": "blowup", "symbol": "$blow", "name": "BlowUP" }, - { "id": "blox", "symbol": "cdt", "name": "Blox" }, - { "id": "bloxmove-erc20", "symbol": "blxm", "name": "bloXmove Token" }, - { "id": "blox-token", "symbol": "blox", "name": "Blox Token" }, - { "id": "blubber-coin", "symbol": "blub", "name": "Blubber Coin" }, - { "id": "bluca", "symbol": "bluc", "name": "Bluca" }, - { "id": "blue", "symbol": "blue", "name": "Blue Protocol" }, - { "id": "blueark", "symbol": "brk", "name": "BlueArk" }, - { "id": "blue-baikal", "symbol": "bbc", "name": "Blue Baikal" }, - { "id": "bluebit", "symbol": "bbt", "name": "BlueBit" }, - { - "id": "bluechip-capital-token", - "symbol": "bcc", - "name": "BlueChip Capital Token" - }, - { "id": "bluechips-token", "symbol": "bchip", "name": "BLUECHIPS Token" }, - { "id": "bluecoin", "symbol": "blu", "name": "Bluecoin" }, - { "id": "bluefi", "symbol": "bleu", "name": "BleuFi" }, - { "id": "bluefincore", "symbol": "bfc", "name": "Bluefincore" }, - { "id": "blue-gold", "symbol": "blg", "name": "Blue Gold" }, - { "id": "blue-horizon", "symbol": "blh", "name": "Blue Horizon" }, - { "id": "blueshift", "symbol": "blues", "name": "Blueshift" }, - { - "id": "bluesparrow-token", - "symbol": "bluesparrow", - "name": "BlueSparrow Token" - }, - { "id": "blue-whale", "symbol": "bwx", "name": "Blue Whale" }, - { "id": "bluewizard", "symbol": "wiz", "name": "BlueWizard" }, - { "id": "blurt", "symbol": "blurt", "name": "Blurt" }, - { "id": "bluzelle", "symbol": "blz", "name": "Bluzelle" }, - { "id": "bmchain-token", "symbol": "bmt", "name": "BMCHAIN token" }, - { "id": "bnb48-club-token", "symbol": "koge", "name": "BNB48 Club Token" }, - { "id": "bnbback", "symbol": "bnbback", "name": "BNBBack" }, - { "id": "bnb-bank", "symbol": "bbk", "name": "BNB Bank" }, - { - "id": "bnb-chain-all-best-ico", - "symbol": "bnballbi", - "name": "BNB CHAIN ALL BEST ICO" - }, - { "id": "bnbdefi", "symbol": "$defi", "name": "BNBDeFi" }, - { "id": "bnb-diamond", "symbol": "bnbd", "name": "BNB Diamond" }, - { "id": "bnbeer", "symbol": "bnbeer", "name": "BNBeer" }, - { "id": "bnbglobal-v2", "symbol": "bnbg", "name": "BNBGlobal V2" }, - { "id": "bnbheroes-token", "symbol": "bnbh", "name": "BnbHeroes Token" }, - { "id": "bnbhunter", "symbol": "bht", "name": "BNBHunter" }, - { "id": "bnb-hyper-rise", "symbol": "hyperrise", "name": "BNB Hyper Rise" }, - { "id": "bnbrise", "symbol": "brise", "name": "BNBRise" }, - { "id": "bnb-shinobi", "symbol": "chakra", "name": "BNB Shinobi" }, - { "id": "bnb-superheroes", "symbol": "bsh", "name": "BNB Superheroes" }, - { "id": "bnbtiger", "symbol": "bnbtiger", "name": "BNBTiger" }, - { "id": "bnbx-finance", "symbol": "bnbx", "name": "BNBX Finance" }, - { "id": "bnftx-token", "symbol": "bnftt", "name": "BNFTX Token" }, - { "id": "bnktothefuture", "symbol": "bft", "name": "BnkToTheFuture" }, - { "id": "bnoincoin", "symbol": "bnc", "name": "Bnoincoin" }, - { - "id": "b-non-fungible-yearn", - "symbol": "bnfy", - "name": "B Non-Fungible Yearn" - }, - { "id": "bnpl-pay", "symbol": "bnpl", "name": "BNPL Pay" }, - { "id": "bnsd-finance", "symbol": "bnsd", "name": "BNSD Finance" }, - { "id": "bns-token", "symbol": "bns", "name": "BNS Token" }, - { "id": "bnx-finex", "symbol": "bnx", "name": "Bnx Finex" }, - { "id": "boba-brewery", "symbol": "bre", "name": "Boba Brewery" }, - { "id": "bobadoge", "symbol": "bdoge", "name": "BobaDoge" }, - { "id": "boba-network", "symbol": "boba", "name": "Boba Network" }, - { "id": "bobatama", "symbol": "boba", "name": "Bobatama" }, - { "id": "bobcoin", "symbol": "bobc", "name": "Bobcoin" }, - { "id": "bobo-cash", "symbol": "bobo", "name": "Bobo Cash" }, - { "id": "bobs_repair", "symbol": "bob", "name": "Bob's Repair" }, - { "id": "boda-token", "symbol": "bodav2", "name": "BODA Token" }, - { "id": "bodhi-network", "symbol": "nbot", "name": "Bodhi Network" }, - { "id": "bofb", "symbol": "bofb", "name": "bofb" }, - { "id": "bogged-finance", "symbol": "bog", "name": "Bogged Finance" }, - { - "id": "bogged-finance-[old]", - "symbol": "bog", - "name": "Bogged Finance [OLD]" - }, - { "id": "bohr", "symbol": "BR", "name": "BoHr" }, - { "id": "boid", "symbol": "boid", "name": "Boid" }, - { "id": "boku", "symbol": "boku", "name": "Boryoku Dragonz" }, - { "id": "bold-point", "symbol": "bpt", "name": "Bold Point" }, - { "id": "boletoken", "symbol": "bole", "name": "BoleToken" }, - { "id": "bole-token", "symbol": "bole", "name": "Bole Token" }, - { "id": "boliecoin", "symbol": "bolc", "name": "Boliecoin" }, - { "id": "bolivarcoin", "symbol": "boli", "name": "Bolivarcoin" }, - { "id": "bollycoin", "symbol": "BOLLY", "name": "BollyCoin" }, - { - "id": "bologna-fc-fan-token", - "symbol": "bfc", - "name": "Bologna FC Fan Token" - }, - { "id": "bolt", "symbol": "bolt", "name": "Bolt" }, - { "id": "boltt-coin", "symbol": "boltt", "name": "BolttCoin" }, - { "id": "bolt-true-dollar", "symbol": "btd", "name": "Bolt Dollar" }, - { "id": "bolt-true-share", "symbol": "bts", "name": "Bolt Share" }, - { "id": "bomb", "symbol": "bomb", "name": "BOMB" }, - { "id": "bomber-coin", "symbol": "bcoin", "name": "Bomber Coin" }, - { "id": "bomberman", "symbol": "$BOMB", "name": "Bomberman" }, - { "id": "bomb-money", "symbol": "bomb", "name": "Bomb Money" }, - { - "id": "bomb-money-bshare", - "symbol": "bshare", - "name": "Bomb Money BShare" - }, - { "id": "bondappetite-usd", "symbol": "usdap", "name": "BondAppetite USD" }, - { - "id": "bondappetit-gov-token", - "symbol": "bag", - "name": "BondAppetit Governance Token" - }, - { "id": "bonded-luna", "symbol": "bluna", "name": "Bonded Luna" }, - { "id": "bondly", "symbol": "bondly", "name": "Bondly" }, - { "id": "bondtoearn", "symbol": "bte", "name": "BondToEarn" }, - { "id": "bone", "symbol": "BONE", "name": "Bone [OLD]" }, - { "id": "bone-2", "symbol": "bone", "name": "Bone" }, - { "id": "b-one-payment", "symbol": "b1p", "name": "B ONE PAYMENT" }, - { "id": "bone-shibaswap", "symbol": "bone", "name": "Bone ShibaSwap" }, - { "id": "bonezyard", "symbol": "bnz", "name": "BonezYard" }, - { "id": "bonfi", "symbol": "bnf", "name": "BonFi" }, - { "id": "bonfida", "symbol": "fida", "name": "Bonfida" }, - { "id": "bonfire", "symbol": "bonfire", "name": "Bonfire" }, - { "id": "bongweedcoin", "symbol": "bwc", "name": "BongWeedCoin" }, - { "id": "bonorum-coin", "symbol": "bono", "name": "Bonorum" }, - { "id": "bontecoin", "symbol": "bonte", "name": "Bontecoin" }, - { "id": "bonus-cake", "symbol": "BonusCake", "name": "Bonus Cake" }, - { "id": "bonuz", "symbol": "bonuz", "name": "Bonuz" }, - { "id": "boobank", "symbol": "boob", "name": "BooBank" }, - { "id": "booby-trap", "symbol": "bbt", "name": "Booby Trap" }, - { "id": "boocake", "symbol": "boocake", "name": "BooCake" }, - { "id": "boo-finance", "symbol": "boofi", "name": "Boo Finance" }, - { "id": "boolberry", "symbol": "bbr", "name": "BoolBerry" }, - { "id": "boolean", "symbol": "bool", "name": "Boolean" }, - { "id": "boo-mirrorworld", "symbol": "xboo", "name": "Boo MirrorWorld" }, - { "id": "boom-shiba", "symbol": "boomshiba", "name": "Boom Shiba" }, - { "id": "boomspace", "symbol": "space", "name": "BoomSpace" }, - { "id": "boom-token", "symbol": "boom", "name": "Boom Token" }, - { "id": "boorio", "symbol": "orio", "name": "Boorio" }, - { "id": "boost", "symbol": "boost", "name": "Boost" }, - { "id": "boosted-finance", "symbol": "boost", "name": "Boosted Finance" }, - { "id": "booster", "symbol": "boo", "name": "Booster" }, - { "id": "booster-bsc", "symbol": "boost", "name": "Booster BSC" }, - { "id": "boosto", "symbol": "bst", "name": "BOOSTO" }, - { "id": "boozedoge", "symbol": "BODO", "name": "BoozeDoge" }, - { "id": "bora", "symbol": "bora", "name": "BORA" }, - { "id": "borealis", "symbol": "brl", "name": "Borealis" }, - { - "id": "bored-ape-social-club", - "symbol": "bape", - "name": "Bored Ape Social Club" - }, - { "id": "bored-battle-apes", "symbol": "bape", "name": "Bored Battle Apes" }, - { - "id": "bored-floki-yacht-club", - "symbol": "bfyc", - "name": "Bored Floki Yacht Club" - }, - { "id": "bored-museum", "symbol": "bored", "name": "Bored Museum" }, - { "id": "boringdao", "symbol": "boring", "name": "BoringDAO" }, - { "id": "boringdao-btc", "symbol": "obtc", "name": "BoringDAO BTC" }, - { "id": "boringdao-[old]", "symbol": "bor", "name": "BoringDAO [OLD]" }, - { "id": "boring-protocol", "symbol": "bop", "name": "Boring Protocol" }, - { "id": "bork", "symbol": "bork", "name": "Bork" }, - { "id": "bork-inu", "symbol": "bork", "name": "Bork Inu" }, - { "id": "born-to-race", "symbol": "brt", "name": "Born To Race" }, - { "id": "boruto-inu", "symbol": "boruto", "name": "Boruto Inu" }, - { - "id": "boryoku-genesis-dragonz-index", - "symbol": "drgnz", - "name": "Boryoku Genesis Dragonz Index" - }, - { "id": "bosagora", "symbol": "boa", "name": "BOSAGORA" }, - { "id": "boson-protocol", "symbol": "boson", "name": "Boson Protocol" }, - { "id": "boss", "symbol": "boss", "name": "Boss" }, - { "id": "bossswap", "symbol": "boss", "name": "Boss Swap" }, - { "id": "bostrom", "symbol": "boot", "name": "Bostrom" }, - { "id": "botopiafinance", "symbol": "BTOP", "name": "BotopiaFinance" }, - { "id": "bot-planet", "symbol": "bot", "name": "Bot Planet" }, - { "id": "botto", "symbol": "botto", "name": "Botto" }, - { "id": "bottos", "symbol": "bto", "name": "Bottos" }, - { "id": "botxcoin", "symbol": "botx", "name": "BOTXCOIN" }, - { "id": "bouje-token", "symbol": "bouje", "name": "Bouje Token" }, - { "id": "bountie-hunter", "symbol": "bountie", "name": "Bountie Hunter" }, - { "id": "bounty", "symbol": "bnty", "name": "Bounty" }, - { "id": "bounty0x", "symbol": "bnty", "name": "Bounty0x" }, - { "id": "bountymarketcap", "symbol": "bmc", "name": "BountyMarketCap" }, - { "id": "boutspro", "symbol": "bouts", "name": "BoutsPro" }, - { "id": "bovineverse", "symbol": "bvg", "name": "BovineVerse" }, - { "id": "bowscoin", "symbol": "bsc", "name": "BowsCoin" }, - { "id": "boxaxis", "symbol": "baxs", "name": "BoxAxis" }, - { "id": "boxerdoge", "symbol": "boxerdoge", "name": "BoxerDOGE" }, - { "id": "boxer-inu", "symbol": "boxer", "name": "Boxer Inu" }, - { "id": "box-token", "symbol": "box", "name": "BOX Token" }, - { "id": "bozkurt-token", "symbol": "bt", "name": "Bozkurt Token" }, - { "id": "b-protocol", "symbol": "bpro", "name": "B.Protocol" }, - { "id": "br34p", "symbol": "br34p", "name": "BR34P" }, - { "id": "brainiac", "symbol": "brains", "name": "Brainiac" }, - { "id": "braintrust", "symbol": "btrst", "name": "Braintrust" }, - { "id": "brandpad-finance", "symbol": "brand", "name": "BrandPad Finance" }, - { "id": "brank", "symbol": "brank", "name": "Brank" }, - { "id": "bravo-coin", "symbol": "bravo", "name": "BravoCoin" }, - { "id": "brazil-fan-token", "symbol": "bft", "name": "Brazil Fan Token" }, - { "id": "brcp-token", "symbol": "brcp", "name": "BRCP Token" }, - { "id": "bread", "symbol": "brd", "name": "Bread" }, - { "id": "breezecoin", "symbol": "brze", "name": "Breezecoin" }, - { "id": "brewlabs", "symbol": "brewlabs", "name": "Brewlabs" }, - { "id": "bribe", "symbol": "BRIBE", "name": "BRIBE" }, - { "id": "bribe-token-2", "symbol": "bribe", "name": "BRIBE Token" }, - { "id": "brick", "symbol": "brick", "name": "r/FortNiteBR Bricks" }, - { - "id": "brickchain-finance", - "symbol": "brick", - "name": "Brickchain Finance" - }, - { "id": "brick-token", "symbol": "brick", "name": "Brick Token" }, - { "id": "bridge", "symbol": "brg.x", "name": "Bridge$" }, - { "id": "bridge-mutual", "symbol": "bmi", "name": "Bridge Mutual" }, - { "id": "bridge-oracle", "symbol": "brg", "name": "Bridge Oracle" }, - { "id": "brig-finance", "symbol": "brig", "name": "Brig Finance" }, - { "id": "bright-risk-index", "symbol": "bri", "name": "Bright Risk Index" }, - { "id": "bright-token", "symbol": "bright", "name": "Bright Token" }, - { "id": "bright-union", "symbol": "bright", "name": "Bright Union" }, - { "id": "bring", "symbol": "nor", "name": "Noir" }, - { "id": "bring-finance", "symbol": "brng", "name": "bRing.Finance" }, - { "id": "britto", "symbol": "brt", "name": "Britto" }, - { "id": "brmv-token", "symbol": "brmv", "name": "BRMV Token" }, - { "id": "brokkr", "symbol": "bro", "name": "Brokkr" }, - { "id": "brokoli", "symbol": "brkl", "name": "Brokoli" }, - { "id": "broovs-projects", "symbol": "brs", "name": "Broovs Projects" }, - { - "id": "brother-music-platform", - "symbol": "bmp", - "name": "Brother Music Platform" - }, - { "id": "bro-token", "symbol": "bro", "name": "Bro Token" }, - { "id": "browniesswap", "symbol": "brown", "name": "BrowniesSwap" }, - { "id": "brz", "symbol": "brz", "name": "Brazilian Digital Token" }, - { "id": "bsc33dao", "symbol": "bsc33", "name": "BSC33DAO" }, - { "id": "bscarmy", "symbol": "barmy", "name": "BscArmy" }, - { "id": "bscbay", "symbol": "bscb", "name": "BSCBAY" }, - { "id": "bscbond", "symbol": "bscb", "name": "BscBond" }, - { "id": "bsccrop", "symbol": "bsccrop", "name": "BSCCrop" }, - { "id": "bscex", "symbol": "bscx", "name": "BSCEX" }, - { "id": "bscgold", "symbol": "bscgold", "name": "BSCGold" }, - { "id": "bsclaunch", "symbol": "bsl", "name": "BSClaunch" }, - { "id": "bsc-memepad", "symbol": "bscm", "name": "BSC MemePad" }, - { "id": "bscpad", "symbol": "bscpad", "name": "BSCPAD" }, - { "id": "bscshiba", "symbol": "bshiba", "name": "bscShiba" }, - { "id": "bscstarter", "symbol": "start", "name": "Starter.xyz" }, - { "id": "bsc-station", "symbol": "bscs", "name": "BSC Station" }, - { "id": "bscview", "symbol": "bscv", "name": "Bscview" }, - { "id": "bscwin-bulls", "symbol": "bscwin", "name": "BSCWIN Bulls" }, - { "id": "bsdium", "symbol": "bscd", "name": "BSDium" }, - { "id": "bsha3", "symbol": "bsha3", "name": "BSHA3" }, - { "id": "bsocial", "symbol": "bins", "name": "BSocial" }, - { "id": "bsys", "symbol": "bsys", "name": "BSYS" }, - { - "id": "btc-2x-flexible-leverage-index", - "symbol": "BTC2x-FLI", - "name": "BTC 2x Flexible Leverage Index" - }, - { - "id": "btc-2x-flexible-leverage-index-polygon", - "symbol": "btc2x-fli-p", - "name": "BTC 2x Flexible Leverage Index (Polygon)" - }, - { "id": "btc-lite", "symbol": "btcl", "name": "BTC Lite" }, - { "id": "btcmoon", "symbol": "btcm", "name": "BTCMoon" }, - { - "id": "btc-network-demand-set-ii", - "symbol": "byte", - "name": "BTC Network Demand Set II" - }, - { "id": "btc-proxy", "symbol": "btcpx", "name": "BTC Proxy" }, - { - "id": "btc-standard-hashrate-token", - "symbol": "btcst", - "name": "BTC Standard Hashrate Token" - }, - { "id": "btecoin", "symbol": "bte", "name": "BTEcoin" }, - { "id": "btf", "symbol": "btf", "name": "Bitcoin Faith" }, - { "id": "bt-finance", "symbol": "bt", "name": "BT.Finance" }, - { "id": "btmiracles", "symbol": "bm", "name": "BtMiracles" }, - { "id": "btour-chain", "symbol": "msot", "name": "BTour Chain" }, - { "id": "btrips", "symbol": "btr", "name": "BTRIPS" }, - { "id": "bts-chain", "symbol": "btsc", "name": "BTS Chain" }, - { "id": "btse-token", "symbol": "btse", "name": "BTSE Token" }, - { "id": "btu-protocol", "symbol": "btu", "name": "BTU Protocol" }, - { "id": "bubblefong", "symbol": "bbf", "name": "Bubblefong" }, - { "id": "bubble-network", "symbol": "bbl", "name": "Bubble Network" }, - { "id": "buccaneer-2", "symbol": "buc", "name": "Buccaneer" }, - { "id": "buckhath-coin", "symbol": "bhig", "name": "BuckHath Coin" }, - { "id": "buckswap", "symbol": "bucks", "name": "BuckSwap" }, - { "id": "budacoin", "symbol": "buda", "name": "BudaCoin" }, - { "id": "budbo", "symbol": "bubo", "name": "Budbo" }, - { "id": "buddy", "symbol": "bud", "name": "Buddy" }, - { "id": "buffalo-swap", "symbol": "buff", "name": "Buffalo Swap" }, - { "id": "buffaloswap-horn", "symbol": "horn", "name": "BuffaloSwap HORN" }, - { "id": "buff-doge-coin", "symbol": "dogecoin", "name": "Buff Doge Coin" }, - { "id": "buffed-kishu", "symbol": "bkishu", "name": "Buffed Kishu" }, - { "id": "buffedshiba", "symbol": "bshib", "name": "BuffedShiba" }, - { "id": "buff-floki", "symbol": "BUFlOKI", "name": "Buff Floki" }, - { "id": "buff-samo", "symbol": "bsamo", "name": "Buff Samo" }, - { "id": "buff-shiba-inu", "symbol": "buffshiba", "name": "Buff Shiba Inu" }, - { "id": "buffswap", "symbol": "buffs", "name": "BuffSwap" }, - { "id": "bugg-finance", "symbol": "bugg", "name": "BUGG Finance" }, - { "id": "bugg-inu", "symbol": "bugg", "name": "Bugg Inu" }, - { "id": "buildercoin", "symbol": "bldr", "name": "BuilderCoin" }, - { "id": "builder-coin", "symbol": "bldr", "name": "BUilDER Coin" }, - { "id": "build-finance", "symbol": "build", "name": "BUILD Finance" }, - { "id": "buildup", "symbol": "bup", "name": "BuildUp" }, - { "id": "bukh", "symbol": "bukh", "name": "bUKHI" }, - { "id": "bul-finance", "symbol": "bul", "name": "Bul.Finance" }, - { "id": "bulk-network", "symbol": "bulk", "name": "Bulk Network" }, - { "id": "bullchain", "symbol": "BULL", "name": "Bullchain" }, - { "id": "bull-coin", "symbol": "bull", "name": "Bull Coin" }, - { - "id": "bulldog-billionaires", - "symbol": "bone", - "name": "Bulldog Billionaires" - }, - { "id": "bulldog-coin", "symbol": "bulldog", "name": "BullDog Coin" }, - { "id": "bulldoge-inu", "symbol": "bulld", "name": "Bulldoge Inu" }, - { "id": "bulldogswap", "symbol": "budg", "name": "BullDogSwap" }, - { "id": "bulldog-token", "symbol": "bdog", "name": "Bulldog Token" }, - { "id": "bulleon", "symbol": "bul", "name": "Bulleon" }, - { "id": "bulleth", "symbol": "bulleth", "name": "Bulleth" }, - { "id": "bullex", "symbol": "blx", "name": "BulleX" }, - { "id": "bullieverse", "symbol": "bull", "name": "Bullieverse" }, - { "id": "bull-inu", "symbol": "BULL", "name": "BULL Inu" }, - { "id": "bullion", "symbol": "cbx", "name": "Bullion" }, - { "id": "bullionfx", "symbol": "bull", "name": "BullionFX" }, - { "id": "bullish-af", "symbol": "bullaf", "name": "Bullish AF" }, - { "id": "bullishapes", "symbol": "bullish", "name": "BullishApes" }, - { "id": "bullit", "symbol": "bult", "name": "Bullit" }, - { "id": "bullperks", "symbol": "blp", "name": "BullPerks" }, - { "id": "bullrise", "symbol": "bull", "name": "BullRise" }, - { "id": "bull-run-token", "symbol": "br", "name": "Bull Run Finance" }, - { "id": "bullswap-protocol", "symbol": "bvl", "name": "Bullswap Protocol" }, - { "id": "bully-inu", "symbol": "binu", "name": "Bully Inu" }, - { "id": "bumoon", "symbol": "bumn", "name": "BUMooN" }, - { "id": "bumper", "symbol": "bump", "name": "Bumper" }, - { "id": "bundle-dao", "symbol": "bdl", "name": "Bundle Dao" }, - { "id": "bundles", "symbol": "bund", "name": "Bundles" }, - { "id": "bunicorn", "symbol": "buni", "name": "Bunicorn" }, - { "id": "bunnycoin", "symbol": "bun", "name": "Bunnycoin" }, - { "id": "bunny-farm", "symbol": "BNF", "name": "Bunny Farm" }, - { "id": "bunnygirl", "symbol": "bunnygirl", "name": "BunnyGirl" }, - { "id": "bunnypark", "symbol": "bp", "name": "BunnyPark" }, - { "id": "bunnypark-game", "symbol": "bg", "name": "BunnyPark Game" }, - { "id": "bunnyrocket", "symbol": "bunnyrocket", "name": "BunnyRocket" }, - { "id": "bunnytoken", "symbol": "bunny", "name": "BunnyToken" }, - { - "id": "bunny-token-polygon", - "symbol": "polybunny", - "name": "Pancake Bunny Polygon" - }, - { "id": "bunnyverse", "symbol": "bnv", "name": "BunnyVerse" }, - { "id": "bunny-zilla", "symbol": "bunnyzilla", "name": "Bunny Zilla" }, - { "id": "bunscake", "symbol": "bscake", "name": "Bunscake" }, - { - "id": "bured-fortis-oeconomia-token", - "symbol": "bfot", - "name": "Burned Fortis Oeconomia Token" - }, - { "id": "burency", "symbol": "buy", "name": "Burency" }, - { "id": "burgerburn", "symbol": "brgb", "name": "BurgerBurn" }, - { "id": "burger-swap", "symbol": "burger", "name": "Burger Swap" }, - { "id": "buried-bones", "symbol": "bbt", "name": "Buried Bones" }, - { "id": "burn1coin", "symbol": "burn1coin", "name": "Burn1Coin" }, - { "id": "burndoge", "symbol": "burndoge", "name": "BurnDoge" }, - { "id": "burnfloki", "symbol": "bfloki", "name": "BurnFloki" }, - { "id": "burningmoon", "symbol": "bm", "name": "BurningMoon" }, - { "id": "burnrocket", "symbol": "burnrocket", "name": "BurnRocket" }, - { "id": "burnt-cake", "symbol": "bcake", "name": "Burnt Cake" }, - { "id": "burnx20", "symbol": "burnx20", "name": "BurnX 2.0" }, - { "id": "burp", "symbol": "burp", "name": "Burp" }, - { "id": "burrow", "symbol": "brrr", "name": "Burrow" }, - { "id": "busdx", "symbol": "busdx", "name": "BUSDX" }, - { - "id": "business-age-of-empires", - "symbol": "baoe", - "name": "Business Age of Empires" - }, - { - "id": "business-credit-substitute", - "symbol": "bcs", - "name": "Business Credit Substitute" - }, - { "id": "busta", "symbol": "bust", "name": "BUSTA" }, - { "id": "busy-dao", "symbol": "busy", "name": "Busy DAO" }, - { "id": "buttcoin-2", "symbol": "butt", "name": "Button" }, - { "id": "butterflydao", "symbol": "btrfly", "name": "Redacted Cartel" }, - { - "id": "butterfly-protocol-2", - "symbol": "bfly", - "name": "Butterfly Protocol" - }, - { "id": "butterswap", "symbol": "butter", "name": "ButterSwap" }, - { "id": "buu-inu", "symbol": "buu", "name": "Buu Inu" }, - { "id": "buxcoin", "symbol": "bux", "name": "BUXCOIN" }, - { "id": "buying", "symbol": "buy", "name": "Buying.com" }, - { "id": "buymainstreet", "symbol": "$mainst", "name": "BuyMainStreet" }, - { "id": "buysell", "symbol": "bull", "name": "BuySell" }, - { "id": "buyucoin-token", "symbol": "buc", "name": "BuyUCoin Token" }, - { "id": "buzzshow", "symbol": "gldy", "name": "Buzzshow" }, - { "id": "bxbtc", "symbol": "bxbtc", "name": "bxBTC" }, - { "id": "bxh", "symbol": "bxh", "name": "BXH Token" }, - { "id": "bxiot", "symbol": "bxiot", "name": "bXIOT" }, - { "id": "bxmi-token", "symbol": "bxmi", "name": "Bxmi Token" }, - { "id": "bxtb-foundation", "symbol": "bxtb", "name": "BXTB Foundation" }, - { "id": "byakko", "symbol": "BYK", "name": "Byakko" }, - { "id": "byteball", "symbol": "gbyte", "name": "Obyte" }, - { "id": "bytecoin", "symbol": "bcn", "name": "Bytecoin" }, - { "id": "bytedex", "symbol": "bext", "name": "ByteDex" }, - { "id": "byteex", "symbol": "bx", "name": "ByteEx" }, - { "id": "bytenext", "symbol": "bnu", "name": "ByteNext" }, - { "id": "bytom", "symbol": "btm", "name": "Bytom" }, - { "id": "bytz", "symbol": "bytz", "name": "BYTZ" }, - { "id": "bzedge", "symbol": "bze", "name": "BZEdge" }, - { "id": "bzx-protocol", "symbol": "bzrx", "name": "bZx Protocol" }, - { "id": "bzzone", "symbol": "bzzone", "name": "Bzzone" }, - { "id": "c2x-2", "symbol": "ctx", "name": "C2X" }, - { "id": "c3", "symbol": "c3", "name": "C3" }, - { "id": "caash", "symbol": "cash", "name": "Caash" }, - { "id": "caashcow", "symbol": "COW", "name": "CaashCow" }, - { "id": "caave", "symbol": "caave", "name": "cAAVE" }, - { "id": "cabana-token", "symbol": "cba", "name": "Cabana Token" }, - { "id": "cabin", "symbol": "cabin", "name": "Cabin" }, - { "id": "cache-gold", "symbol": "cgt", "name": "CACHE Gold" }, - { "id": "cacti-club", "symbol": "cacti", "name": "Cacti Club" }, - { "id": "cad-coin", "symbol": "cadc", "name": "CAD Coin" }, - { "id": "caesar-finance", "symbol": "$caesar", "name": "Caesar Finance" }, - { "id": "cafeswap-token", "symbol": "brew", "name": "CafeSwap Token" }, - { "id": "cage", "symbol": "c4g3", "name": "Cage" }, - { "id": "cage-governance", "symbol": "cgt", "name": "Cage Governance" }, - { "id": "caica-coin", "symbol": "cicc", "name": "CAICA Coin" }, - { "id": "caietf-finance", "symbol": "cai", "name": "CAIETF.Finance" }, - { "id": "cake", "symbol": "cake", "name": "CakeDAO" }, - { "id": "cakebaker", "symbol": "cakebaker", "name": "CakeBaker" }, - { "id": "cake-bank", "symbol": "cakebank", "name": "Cake Bank" }, - { "id": "cake-girl", "symbol": "cakegirl", "name": "Cake Girl" }, - { "id": "cake-monster", "symbol": "monsta", "name": "Cake Monster" }, - { "id": "cakepunks", "symbol": "cakepunks", "name": "CAKEPUNKS" }, - { "id": "cakeswap", "symbol": "cakeswap", "name": "CakeSwap" }, - { "id": "caketools", "symbol": "ckt", "name": "Caketools" }, - { "id": "cakezilla", "symbol": "cakezilla", "name": "CakeZilla" }, - { "id": "calamari-network", "symbol": "kma", "name": "Calamari Network" }, - { "id": "calcifer", "symbol": "CALCIFER", "name": "Calcifer" }, - { "id": "calicoin", "symbol": "cali", "name": "CaliCoin" }, - { "id": "californium", "symbol": "cf", "name": "Californium" }, - { "id": "callisto", "symbol": "clo", "name": "Callisto Network" }, - { "id": "calluna-wallet", "symbol": "can", "name": "Calluna Wallet" }, - { "id": "calo-app", "symbol": "calo", "name": "Calo" }, - { "id": "caluracoin", "symbol": "clc", "name": "CaluraCoin" }, - { "id": "calvex", "symbol": "clvx", "name": "Calvex" }, - { "id": "calypso", "symbol": "cyo", "name": "Calypso" }, - { "id": "camelcoin", "symbol": "cml", "name": "Camelcoin" }, - { "id": "cameltoken", "symbol": "CMLT", "name": "CamelToken" }, - { "id": "camp", "symbol": "camp", "name": "Camp" }, - { "id": "canary", "symbol": "cnr", "name": "Canary" }, - { "id": "canary-dollar", "symbol": "cand", "name": "Canary Dollar" }, - { "id": "canaryx", "symbol": "cnyx", "name": "CanaryX" }, - { "id": "candela-coin", "symbol": "cla", "name": "Candela Coin" }, - { - "id": "can-devs-do-something-please", - "symbol": "cddsp", - "name": "Can Devs Do Something Please" - }, - { "id": "candle", "symbol": "cndl", "name": "Candle" }, - { "id": "candylad", "symbol": "candylad", "name": "Candylad" }, - { "id": "cannabiscoin", "symbol": "cann", "name": "CannabisCoin" }, - { - "id": "cannabis-seed-token", - "symbol": "cana", - "name": "Cannabis Seed Token" - }, - { "id": "cannumo", "symbol": "canu", "name": "Cannumo" }, - { "id": "canon-crypto", "symbol": "cato", "name": "Canon Crypto" }, - { "id": "canopus", "symbol": "opus", "name": "Canopus" }, - { "id": "canyacoin", "symbol": "can", "name": "CanYaCoin" }, - { "id": "cap", "symbol": "cap", "name": "Cap" }, - { - "id": "capital-aggregator-token", - "symbol": "cat+", - "name": "Capital Aggregator Token" - }, - { - "id": "capital-dao-starter-token", - "symbol": "cds", - "name": "Capital DAO Starter Token" - }, - { "id": "capital-system", "symbol": "capsys", "name": "Capital System" }, - { "id": "capital-x-cell", "symbol": "cxc", "name": "CAPITAL X CELL" }, - { "id": "capitol", "symbol": "cptl", "name": "Capitol" }, - { "id": "cappasity", "symbol": "capp", "name": "Cappasity" }, - { "id": "captain", "symbol": "capt", "name": "Captain" }, - { "id": "captain-inu", "symbol": "cptinu", "name": "Captain Inu" }, - { - "id": "captain-shibarrow", - "symbol": "shibarrow", - "name": "Captain Shibarrow" - }, - { "id": "capybara", "symbol": "capy", "name": "Capybara" }, - { "id": "caramelswap", "symbol": "mel", "name": "CaramelSwap" }, - { "id": "carat", "symbol": "carat", "name": "CARAT" }, - { "id": "carbon", "symbol": "crbn", "name": "Carbon" }, - { "id": "carboncoin", "symbol": "carbon", "name": "Carboncoin" }, - { "id": "carbon-credit", "symbol": "cct", "name": "Carbon Credit" }, - { "id": "carbondefi", "symbol": "carbo", "name": "CarbonDEFI" }, - { "id": "carboneum", "symbol": "c8", "name": "Carboneum" }, - { "id": "carbon-finance", "symbol": "carbon", "name": "Carbon Finance" }, - { "id": "carbon-gems", "symbol": "gems", "name": "Carbon GEMS" }, - { "id": "carbon-labs", "symbol": "carb", "name": "Carbon Labs" }, - { "id": "car-coin", "symbol": "ccm", "name": "Car Coin" }, - { "id": "cardano", "symbol": "ada", "name": "Cardano" }, - { "id": "cardanoevo", "symbol": "cevo", "name": "CardanoEvo" }, - { "id": "cardanomics", "symbol": "adx", "name": "Cardanomics" }, - { "id": "cardashift", "symbol": "clap", "name": "Cardashift" }, - { "id": "cardax", "symbol": "cdx", "name": "Cardax" }, - { "id": "cardence", "symbol": "$crdn", "name": "Cardence" }, - { "id": "cardstack", "symbol": "card", "name": "Cardstack" }, - { "id": "cardstarter", "symbol": "cards", "name": "Cardstarter" }, - { "id": "cardswap", "symbol": "cswap", "name": "CardSwap" }, - { "id": "cardwallet", "symbol": "cw", "name": "CardWallet" }, - { "id": "care-coin", "symbol": "crc", "name": "Care Coin" }, - { "id": "cargolink", "symbol": "clx", "name": "CargoLink" }, - { "id": "cargox", "symbol": "cxo", "name": "CargoX" }, - { "id": "caribmarsx", "symbol": "CMX", "name": "CaribMarsX" }, - { "id": "carma-coin", "symbol": "carma", "name": "Carma Coin" }, - { "id": "carnomaly", "symbol": "carr", "name": "Carnomaly" }, - { "id": "carr-finance", "symbol": "crt", "name": "Carrot Finance" }, - { - "id": "carrot-stable-coin", - "symbol": "carrot", - "name": "Carrot Stable Coin" - }, - { "id": "carrot-token", "symbol": "carrot", "name": "Carrot Token" }, - { "id": "carry", "symbol": "cre", "name": "Carry" }, - { "id": "carsautofinance", "symbol": "caf", "name": "CarsAutoFinance" }, - { "id": "cartesi", "symbol": "ctsi", "name": "Cartesi" }, - { "id": "carvertical", "symbol": "cv", "name": "carVertical" }, - { "id": "case-closed", "symbol": "$caseclosed", "name": "Case Closed" }, - { "id": "cash", "symbol": "cash", "name": "Cash" }, - { "id": "cash2", "symbol": "cash2", "name": "Cash2" }, - { "id": "cashaa", "symbol": "cas", "name": "Cashaa" }, - { "id": "cashbackpro", "symbol": "cbp", "name": "CashBackPro" }, - { "id": "cashbet-coin", "symbol": "cbc", "name": "CBC.network" }, - { "id": "cashcats", "symbol": "$cats", "name": "CashCats" }, - { "id": "cashcow", "symbol": "cow", "name": "CashCow" }, - { "id": "cashcow-finance", "symbol": "ccf", "name": "CashCow.Finance" }, - { "id": "cashdog", "symbol": "cashdog", "name": "CashDog" }, - { "id": "cashhand", "symbol": "chnd", "name": "Cashhand" }, - { "id": "cashio-dollar", "symbol": "cash", "name": "Cashio Dollar" }, - { "id": "cashio-token", "symbol": "cashio", "name": "Cashio Token" }, - { "id": "cashpay", "symbol": "cpz", "name": "CashPay" }, - { "id": "cash-tech", "symbol": "cate", "name": "Cash Tech" }, - { "id": "casinocoin", "symbol": "csc", "name": "Casinocoin" }, - { "id": "casinoxmetaverse", "symbol": "$casio", "name": "CasinoXMetaverse" }, - { "id": "casper-defi", "symbol": "casper", "name": "Casper DeFi" }, - { "id": "casper-network", "symbol": "cspr", "name": "Casper Network" }, - { "id": "casperpad", "symbol": "cspd", "name": "CasperPad" }, - { "id": "caspian", "symbol": "csp", "name": "Caspian" }, - { "id": "castweet", "symbol": "ctt", "name": "Castweet" }, - { "id": "cat", "symbol": "cat", "name": "CAT" }, - { "id": "catapult", "symbol": "atd", "name": "Catapult" }, - { "id": "catbonk", "symbol": "cabo", "name": "Catbonk" }, - { "id": "catboy", "symbol": "ctb", "name": "CatBoy [OLD]" }, - { "id": "catboy-2", "symbol": "catboy", "name": "CatBoy" }, - { "id": "catbread", "symbol": "catbread", "name": "CatBread" }, - { "id": "catchy", "symbol": "catchy", "name": "Catchy" }, - { "id": "catcoin-2", "symbol": "cat", "name": "CATCOIN" }, - { "id": "catcoin-token", "symbol": "cats", "name": "CatCoin Token" }, - { "id": "catecoin", "symbol": "cate", "name": "CateCoin" }, - { "id": "catemoon-inu", "symbol": "cate", "name": "Catemoon Inu" }, - { "id": "catena-x", "symbol": "cex", "name": "Catena X" }, - { "id": "catex-token", "symbol": "catt", "name": "Catex Token" }, - { "id": "catge-coin", "symbol": "catge", "name": "Catge Coin" }, - { "id": "catgirl", "symbol": "catgirl", "name": "Catgirl" }, - { "id": "catnip", "symbol": "nip", "name": "CatNIP" }, - { "id": "cato", "symbol": "cato", "name": "CATO" }, - { "id": "catocoin", "symbol": "cato", "name": "CatoCoin" }, - { "id": "catpay", "symbol": "catpay", "name": "CATpay" }, - { "id": "cats-claw", "symbol": "claw", "name": "Cats Claw" }, - { "id": "catscoin", "symbol": "cats", "name": "Catscoin" }, - { "id": "cat-token", "symbol": "cat", "name": "Cat Token" }, - { "id": "catwoman", "symbol": "cw", "name": "CatWoman" }, - { "id": "catzcoin", "symbol": "catz", "name": "CatzCoin" }, - { "id": "cavapoo", "symbol": "cava", "name": "Cavapoo" }, - { "id": "cave", "symbol": "cave", "name": "Crypto Cavemen" }, - { "id": "cbbn-token", "symbol": "CBBN", "name": "CBBN Token" }, - { "id": "cbdao", "symbol": "bree", "name": "CBDAO" }, - { "id": "cbd-coin", "symbol": "cbd", "name": "CBD Coin" }, - { "id": "cberry", "symbol": "cby", "name": "CBerry" }, - { "id": "cbet-token", "symbol": "cbet", "name": "CBET Token" }, - { "id": "cc", "symbol": "cc", "name": "CC" }, - { "id": "ccomp", "symbol": "ccomp", "name": "cCOMP" }, - { "id": "ccore", "symbol": "cco", "name": "Ccore" }, - { "id": "ccrv", "symbol": "ccrv", "name": "cCRV" }, - { "id": "ccswap", "symbol": "cc", "name": "CCSwap" }, - { "id": "ccuniverse", "symbol": "uvu", "name": "CCUniverse" }, - { "id": "cdai", "symbol": "cdai", "name": "cDAI" }, - { "id": "cdzexchange", "symbol": "cdz", "name": "CDzExchange" }, - { "id": "cedars", "symbol": "ceds", "name": "CEDARS" }, - { "id": "ceek", "symbol": "ceek", "name": "CEEK Smart VR Token" }, - { "id": "ceezee-safu", "symbol": "safu", "name": "CEEZEE SAFU" }, - { "id": "celcoin", "symbol": "celc", "name": "CelCoin" }, - { "id": "celeb", "symbol": "celeb", "name": "Celeb" }, - { "id": "celer-network", "symbol": "celr", "name": "Celer Network" }, - { "id": "celery", "symbol": "cly", "name": "Celery" }, - { "id": "celestial", "symbol": "celt", "name": "Celestial" }, - { "id": "celestial-unity", "symbol": "cu", "name": "Celestial Unity" }, - { "id": "celeum", "symbol": "clx", "name": "Celeum" }, - { "id": "celletf", "symbol": "ecell", "name": "Consensus Cell Network" }, - { "id": "cellframe", "symbol": "cell", "name": "Cellframe" }, - { "id": "celo", "symbol": "celo", "name": "Celo" }, - { "id": "celo-dollar", "symbol": "cusd", "name": "Celo Dollar" }, - { "id": "celo-euro", "symbol": "ceur", "name": "Celo Euro" }, - { "id": "celolaunch", "symbol": "cla", "name": "CeloLaunch" }, - { "id": "celostarter", "symbol": "cstar", "name": "CeloStarter" }, - { "id": "celsius-degree-token", "symbol": "cel", "name": "Celsius Network" }, - { "id": "celsius-wormhole", "symbol": "cel", "name": "Celsius (Wormhole)" }, - { - "id": "celsiusx-wrapped-ada", - "symbol": "cxada", - "name": "CelsiusX Wrapped ADA" - }, - { - "id": "celsiusx-wrapped-doge", - "symbol": "cxdoge", - "name": "CelsiusX Wrapped DOGE" - }, - { - "id": "celsiusx-wrapped-eth", - "symbol": "cxeth", - "name": "CelsiusX Wrapped ETH" - }, - { "id": "cens-world", "symbol": "cens", "name": "Cens World" }, - { "id": "centaur", "symbol": "cntr", "name": "Centaur" }, - { "id": "centaurify", "symbol": "cent", "name": "Centaurify" }, - { "id": "centcex", "symbol": "cenx", "name": "Centcex" }, - { "id": "centerprime", "symbol": "cpx", "name": "CenterPrime" }, - { "id": "centralex", "symbol": "cenx", "name": "Centralex" }, - { "id": "centrality", "symbol": "cennz", "name": "Centrality" }, - { "id": "centric-cash", "symbol": "cns", "name": "Centric Swap" }, - { "id": "centrifuge", "symbol": "cfg", "name": "Centrifuge" }, - { "id": "centurion", "symbol": "cnt", "name": "Centurion" }, - { "id": "centurion-inu", "symbol": "cent", "name": "Centurion Inu" }, - { "id": "cerberus", "symbol": "ccf", "name": "Cerberus Finance" }, - { "id": "cerberus-2", "symbol": "crbrus", "name": "Cerberus" }, - { "id": "cerberusdao", "symbol": "3dog", "name": "CerberusDAO" }, - { "id": "cerby-token", "symbol": "cerby", "name": "Cerby Token" }, - { "id": "cerealia", "symbol": "cer", "name": "Cerealia" }, - { "id": "cere-network", "symbol": "cere", "name": "Cere Network" }, - { "id": "ceres", "symbol": "ceres", "name": "Ceres" }, - { "id": "cerium", "symbol": "xce", "name": "Cerium" }, - { "id": "certik", "symbol": "ctk", "name": "CertiK" }, - { "id": "certrise", "symbol": "cert", "name": "CertRise" }, - { "id": "certurium", "symbol": "crt", "name": "Certurium" }, - { "id": "cerveza", "symbol": "cvza", "name": "Cerveza" }, - { "id": "cetf", "symbol": "cetf", "name": "Cell ETF" }, - { "id": "cexlt", "symbol": "clt", "name": "Cexlt" }, - { "id": "cfl365-finance", "symbol": "cfl365", "name": "CFL365 Finance" }, - { "id": "cforforum-token", "symbol": "cfo", "name": "Cfoforum Token" }, - { "id": "cfx-quantum", "symbol": "cfxq", "name": "CFX Quantum" }, - { "id": "chadfi", "symbol": "chad", "name": "ChadFi" }, - { "id": "chads-vc", "symbol": "chads", "name": "CHADS VC" }, - { "id": "chain", "symbol": "chn", "name": "Chain [OLD]" }, - { "id": "chain-2", "symbol": "xcn", "name": "Chain" }, - { "id": "chainbing", "symbol": "cbg", "name": "Chainbing" }, - { "id": "chaincade", "symbol": "chaincade", "name": "ChainCade" }, - { "id": "chaincoin", "symbol": "chc", "name": "Chaincoin" }, - { "id": "chain-colosseum", "symbol": "colos", "name": "Chain Colosseum" }, - { "id": "chaincorn", "symbol": "cornx", "name": "Chaincorn" }, - { "id": "chain-estate-dao", "symbol": "ches", "name": "Chain Estate DAO" }, - { "id": "chainflix", "symbol": "cfxt", "name": "Chainflix" }, - { "id": "chain-flowers", "symbol": "cflo", "name": "Chain Flowers" }, - { "id": "chain-games", "symbol": "chain", "name": "Chain Games" }, - { "id": "chainge-finance", "symbol": "chng", "name": "Chainge Finance" }, - { "id": "chain-guardians", "symbol": "cgg", "name": "Chain Guardians" }, - { "id": "chainium", "symbol": "chx", "name": "WeOwn" }, - { "id": "chainlink", "symbol": "link", "name": "Chainlink" }, - { - "id": "chainlink-wormhole", - "symbol": "link", - "name": "Chainlink (Wormhole)" - }, - { "id": "chainlist", "symbol": "clist", "name": "Chainlist" }, - { "id": "chainpay", "symbol": "cpay", "name": "Chainpay" }, - { "id": "chain-pet", "symbol": "cpet", "name": "Chain Pet" }, - { "id": "chainport", "symbol": "portx", "name": "ChainPort" }, - { "id": "chainsquare", "symbol": "chs", "name": "Chainsquare" }, - { "id": "chainswap", "symbol": "asap", "name": "Chainswap" }, - { "id": "chain-wars-essence", "symbol": "cwe", "name": "Chain Wars" }, - { "id": "chainx", "symbol": "pcx", "name": "ChainX" }, - { "id": "challengedac", "symbol": "chl", "name": "ChallengeDAC" }, - { "id": "champinu", "symbol": "champinu", "name": "ChampInu" }, - { "id": "change", "symbol": "cag", "name": "Change" }, - { "id": "changenow", "symbol": "now", "name": "ChangeNOW" }, - { "id": "change-our-world", "symbol": "chg", "name": "Change Our World" }, - { "id": "changer", "symbol": "cng", "name": "Changer" }, - { "id": "channels", "symbol": "can", "name": "Channels" }, - { "id": "charactbit", "symbol": "chb", "name": "Charactbit" }, - { "id": "charg-coin", "symbol": "chg", "name": "Charg Coin" }, - { - "id": "chargedefi-charge", - "symbol": "charge", - "name": "ChargeDeFi Charge" - }, - { - "id": "chargedefi-static", - "symbol": "static", - "name": "ChargeDeFi Static" - }, - { "id": "charged-particles", "symbol": "ionx", "name": "Charged Particles" }, - { "id": "charitas", "symbol": "char", "name": "Charitas" }, - { "id": "charity-alfa", "symbol": "mich", "name": "Charity Alfa" }, - { "id": "charix-token", "symbol": "charix", "name": "Charix Token" }, - { "id": "charizard-inu", "symbol": "charizard", "name": "Charizard Inu" }, - { "id": "charli3", "symbol": "c3", "name": "Charli3" }, - { "id": "charlie-finance", "symbol": "cht", "name": "Charlie Finance" }, - { "id": "charm", "symbol": "charm", "name": "Charm" }, - { "id": "chartex", "symbol": "chart", "name": "ChartEx" }, - { "id": "checkdot", "symbol": "cdt", "name": "CheckDot" }, - { "id": "checoin", "symbol": "checoin", "name": "CheCoin" }, - { "id": "chedda", "symbol": "chedda", "name": "Chedda" }, - { "id": "chee", "symbol": "chee", "name": "Chee Finance" }, - { "id": "cheebs", "symbol": "chee", "name": "CHEEBS" }, - { "id": "cheecoin", "symbol": "chee", "name": "Cheecoin" }, - { "id": "cheems", "symbol": "cheems", "name": "Cheems" }, - { "id": "cheems-inu", "symbol": "$CINU", "name": "CHEEMS INU" }, - { "id": "cheersland", "symbol": "cheers", "name": "CheersLand" }, - { "id": "cheesecakeswap", "symbol": "ccake", "name": "CheesecakeSwap" }, - { "id": "cheesedao", "symbol": "cheez", "name": "CheeseDAO" }, - { "id": "cheesefry", "symbol": "cheese", "name": "CheeseFry" }, - { "id": "cheesesoda-token", "symbol": "soda", "name": "CheeseSoda Token" }, - { "id": "cheeseswap", "symbol": "chs", "name": "CheeseSwap" }, - { "id": "cheese-swap", "symbol": "cheese", "name": "Cheese Swap" }, - { "id": "cheese-token", "symbol": "cheese", "name": "Cheese Token" }, - { "id": "cheesus", "symbol": "cheesus", "name": "Cheesus" }, - { "id": "chefcake", "symbol": "chefcake", "name": "ChefCake" }, - { "id": "chellitcoin", "symbol": "chlt", "name": "Chellitcoin" }, - { - "id": "chemix-ecology-governance-token", - "symbol": "kun", - "name": "Chemix Ecology Governance Token" - }, - { "id": "cheqd-network", "symbol": "cheq", "name": "CHEQD Network" }, - { "id": "cherish", "symbol": "chc", "name": "Cherish" }, - { "id": "cherry-network", "symbol": "cher", "name": "Cherry Network" }, - { "id": "cherrypick", "symbol": "cherry", "name": "CherryPick" }, - { "id": "cherryswap", "symbol": "che", "name": "CherrySwap" }, - { "id": "cherry-token", "symbol": "yt", "name": "Cherry Token" }, - { "id": "chesscoin-0-32", "symbol": "chess", "name": "ChessCoin 0.32%" }, - { "id": "chessnft", "symbol": "cnft", "name": "ChessNFT" }, - { "id": "chex-token", "symbol": "chex", "name": "CHEX Token" }, - { "id": "chhipscoin", "symbol": "chh", "name": "CHHIPSCOIN" }, - { "id": "chi", "symbol": "chi", "name": "Chi" }, - { "id": "chia", "symbol": "xch", "name": "Chia" }, - { "id": "chiba-inu", "symbol": "chiba", "name": "Chiba Inu" }, - { "id": "chicken", "symbol": "kfc", "name": "Chicken" }, - { "id": "chickenlegs", "symbol": "corn", "name": "ChickenLegs" }, - { - "id": "chicken-nugget-coin", - "symbol": "nugget", - "name": "Chicken Nugget Coin" - }, - { "id": "chicken-zilla", "symbol": "chkn", "name": "Chicken Zilla" }, - { "id": "chi-gastoken", "symbol": "chi", "name": "Chi Gastoken" }, - { "id": "chi-gastoken-bsc", "symbol": "chi", "name": "Chi Gastoken (BSC)" }, - { "id": "chihiro-inu", "symbol": "chiro", "name": "Chihiro Inu" }, - { "id": "chihuahua", "symbol": "hua", "name": "Chihuahua" }, - { "id": "chihuahuasol", "symbol": "chih", "name": "ChihuahuaSol" }, - { "id": "chihuahua-token", "symbol": "huahua", "name": "Chihuahua Chain" }, - { "id": "chihuahuax", "symbol": "chihua", "name": "Chihuahuax" }, - { "id": "chihua-token", "symbol": "chihua", "name": "Chihua Token" }, - { "id": "chikincoin", "symbol": "ckc", "name": "ChikinCoin" }, - { "id": "chikn-egg", "symbol": "egg", "name": "Chikn Egg" }, - { "id": "chikn-feed", "symbol": "feed", "name": "chikn feed" }, - { "id": "chiliz", "symbol": "chz", "name": "Chiliz" }, - { "id": "chiliz-wormhole", "symbol": "chz", "name": "Chiliz (Wormhole)" }, - { "id": "chilliswap", "symbol": "chli", "name": "ChilliSwap" }, - { "id": "chimaera", "symbol": "chi", "name": "XAYA" }, - { "id": "chimeras", "symbol": "chim", "name": "Chimeras" }, - { "id": "chimp-fight", "symbol": "nana", "name": "Nana" }, - { "id": "chinaom", "symbol": "com", "name": "ChinaOM" }, - { "id": "chinu", "symbol": "$chinu", "name": "Chinu" }, - { "id": "chip", "symbol": "chip", "name": "Chip" }, - { "id": "chipstars", "symbol": "chips", "name": "Chipstars" }, - { "id": "chipz", "symbol": "chpz", "name": "Chipz" }, - { "id": "chiva-token", "symbol": "chiv", "name": "Chiva Token" }, - { "id": "chives-coin", "symbol": "xcc", "name": "Chives Coin" }, - { "id": "chiwawa", "symbol": "chiwa", "name": "Chiwawa" }, - { "id": "choice-coin", "symbol": "choice", "name": "Choice Coin" }, - { "id": "chonk", "symbol": "chonk", "name": "Chonk" }, - { "id": "chopper-inu", "symbol": "CHOPPER", "name": "Chopper Inu" }, - { "id": "chord-protocol", "symbol": "chord", "name": "Chord Protocol" }, - { "id": "chorusx", "symbol": "cx1", "name": "ChorusX" }, - { "id": "chow-chow-finance", "symbol": "chow", "name": "Chow Chow Finance" }, - { "id": "chowdao", "symbol": "chow", "name": "ChowDAO" }, - { "id": "chow-inu", "symbol": "chow", "name": "Chow Inu" }, - { "id": "christmas-bnb", "symbol": "xmasbnb", "name": "Christmas BNB" }, - { "id": "chromaway", "symbol": "chr", "name": "Chromia" }, - { "id": "chronicle", "symbol": "xnl", "name": "Chronicle" }, - { "id": "chronobank", "symbol": "time", "name": "chrono.tech" }, - { "id": "chronobase", "symbol": "tik", "name": "ChronoBase" }, - { "id": "chronologic", "symbol": "day", "name": "Chronologic" }, - { "id": "chubbies", "symbol": "chubbies20", "name": "Chubbies" }, - { "id": "chubbyakita", "symbol": "cakita", "name": "ChubbyAkita" }, - { "id": "chug-token", "symbol": "chug", "name": "CHUG Token" }, - { "id": "chumbai-valley", "symbol": "chmb", "name": "Chumbi Valley" }, - { "id": "chum-coin", "symbol": "chum", "name": "Chum Coin" }, - { "id": "cia", "symbol": "CIA", "name": "CIA" }, - { "id": "cifculation", "symbol": "clc", "name": "Cifculation" }, - { "id": "cigarette-token", "symbol": "cig", "name": "Cigarette" }, - { "id": "cindicator", "symbol": "cnd", "name": "Cindicator" }, - { "id": "cindrum", "symbol": "cind", "name": "Cindrum" }, - { - "id": "cine-media-celebrity-coin", - "symbol": "cmccoin", - "name": "CINE MEDIA CELEBRITY COIN" - }, - { "id": "cino-games", "symbol": "cino", "name": "Cino Games" }, - { "id": "cipher-2", "symbol": "cpr", "name": "CIPHER" }, - { "id": "circleex", "symbol": "cx", "name": "CircleEx" }, - { "id": "circlepod", "symbol": "cpx", "name": "Circlepod" }, - { "id": "circleswap", "symbol": "cir", "name": "CircleSwap" }, - { "id": "circuits-of-value", "symbol": "coval", "name": "Circuits of Value" }, - { "id": "ciredo", "symbol": "cir", "name": "Ciredo" }, - { "id": "cirrus", "symbol": "crs", "name": "Cirrus" }, - { "id": "cirus", "symbol": "cirus", "name": "Cirus" }, - { "id": "citadao", "symbol": "knight", "name": "CitaDAO" }, - { "id": "citadel", "symbol": "ctl", "name": "Citadel" }, - { "id": "citadel-one", "symbol": "xct", "name": "Citadel.one" }, - { - "id": "cities-vault-nftx", - "symbol": "cities", - "name": "CITIES Vault (NFTX)" - }, - { "id": "citizen-finance", "symbol": "cifi", "name": "Citizen Finance" }, - { - "id": "citizen-finance-old", - "symbol": "cifi", - "name": "Citizen Finance (OLD)" - }, - { "id": "citrus", "symbol": "cts", "name": "Citrus" }, - { "id": "city-coin", "symbol": "city", "name": "City Coin" }, - { - "id": "citystates-medieval", - "symbol": "csm", - "name": "CityStates Medieval" - }, - { "id": "civic", "symbol": "cvc", "name": "Civic" }, - { "id": "civic-power", "symbol": "power", "name": "Civic Power" }, - { "id": "civilization", "symbol": "civ", "name": "Civilization" }, - { "id": "claimswap", "symbol": "cla", "name": "ClaimSwap" }, - { "id": "clams", "symbol": "clam", "name": "Clams" }, - { "id": "clash-of-cars", "symbol": "clash", "name": "Clash Of Cars" }, - { "id": "clash-token", "symbol": "sct", "name": "Clash Token" }, - { "id": "class-coin", "symbol": "class", "name": "Class Coin" }, - { "id": "classicbitcoin", "symbol": "cbtc", "name": "ClassicBitcoin" }, - { "id": "classicdoge", "symbol": "xdoge", "name": "ClassicDoge" }, - { "id": "classzz", "symbol": "czz", "name": "ClassZZ" }, - { "id": "clavis", "symbol": "clavis", "name": "Clavis" }, - { "id": "cleanocean", "symbol": "clean", "name": "CleanOcean" }, - { "id": "clear-coin", "symbol": "clr", "name": "Clear Coin" }, - { "id": "cleardao", "symbol": "clh", "name": "ClearDAO" }, - { "id": "clearpoll", "symbol": "poll", "name": "ClearPoll" }, - { "id": "clearpool", "symbol": "cpool", "name": "Clearpool" }, - { "id": "clear-water", "symbol": "$clear", "name": "Clear Water" }, - { "id": "clexchain", "symbol": "clex", "name": "Clexchain" }, - { "id": "clifford-inu", "symbol": "cliff", "name": "Clifford Inu" }, - { - "id": "climb-token-finance", - "symbol": "climb", - "name": "Climb Token Finance" - }, - { "id": "clintex-cti", "symbol": "cti", "name": "ClinTex CTi" }, - { "id": "cliq", "symbol": "ct", "name": "CLIQ" }, - { "id": "cloakcoin", "symbol": "cloak", "name": "Cloakcoin" }, - { "id": "clock-24", "symbol": "c24", "name": "Clock 24" }, - { - "id": "cloud9bsc-finance", - "symbol": "cloud9", - "name": "Cloud9bsc.finance" - }, - { "id": "cloudbric", "symbol": "clbk", "name": "Cloudbric" }, - { "id": "cloudname", "symbol": "cname", "name": "Cloudname" }, - { "id": "cloud-protocol", "symbol": "cpro", "name": "Cloud Protocol" }, - { "id": "cloutcontracts", "symbol": "ccs", "name": "CloutContracts" }, - { "id": "clover", "symbol": "clv", "name": "Clover" }, - { "id": "clover-finance", "symbol": "clv", "name": "Clover Finance" }, - { "id": "clqdr", "symbol": "clqdr", "name": "cLQDR" }, - { - "id": "club-atletico-independiente", - "symbol": "cai", - "name": "Club Atletico Independiente Fan Token" - }, - { "id": "club-donkey", "symbol": "cdonk", "name": "Club Donkey" }, - { - "id": "clube-atletico-mineiro-fan-token", - "symbol": "galo", - "name": "Clube Atlético Mineiro Fan Token" - }, - { "id": "clucoin", "symbol": "clu", "name": "CluCoin" }, - { "id": "clytie", "symbol": "cly", "name": "Clytie" }, - { "id": "cmc-coin", "symbol": "cmcc", "name": "CMC Coin" }, - { "id": "cmeta", "symbol": "CMETA", "name": "cMETA" }, - { "id": "cmitcoin", "symbol": "cmit", "name": "CMITCOIN" }, - { "id": "cneta", "symbol": "cneta", "name": "cNETA" }, - { "id": "cng-casino", "symbol": "cng", "name": "CNG Casino" }, - { "id": "cnn", "symbol": "cnn", "name": "Content Neutrality Network" }, - { "id": "cnns", "symbol": "cnns", "name": "CNNS" }, - { "id": "coalculus", "symbol": "coal", "name": "Coalculus" }, - { "id": "cobak-token", "symbol": "cbk", "name": "Cobak Token" }, - { "id": "coban", "symbol": "coban", "name": "COBAN" }, - { "id": "cobragoose", "symbol": "CBG", "name": "CobraGoose" }, - { "id": "cockapoo", "symbol": "cpoo", "name": "Cockapoo" }, - { "id": "cocktail", "symbol": "cocktail", "name": "Cocktail" }, - { "id": "cocktailbar", "symbol": "coc", "name": "cocktailbar.finance" }, - { "id": "cocos-bcx", "symbol": "cocos", "name": "COCOS BCX" }, - { "id": "coco-swap", "symbol": "coco", "name": "Coco Swap" }, - { "id": "code4rena", "symbol": "arena", "name": "Code4rena" }, - { "id": "code-7", "symbol": "code7", "name": "Code 7" }, - { "id": "codeo-token", "symbol": "codeo", "name": "CODEO TOKEN" }, - { "id": "codex", "symbol": "cdex", "name": "Codex" }, - { "id": "codex-finance", "symbol": "codex", "name": "CODEX Finance" }, - { "id": "codi-finance", "symbol": "codi", "name": "Codi Finance" }, - { "id": "codyfight", "symbol": "ctok", "name": "Codyfight" }, - { "id": "coffe-1-st-round", "symbol": "cff", "name": "Coffe" }, - { "id": "coffeecoin", "symbol": "cof", "name": "CoffeeCoin" }, - { "id": "coffin-dollar", "symbol": "cousd", "name": "Coffin Dollar" }, - { "id": "coffin-finance", "symbol": "coffin", "name": "Coffin Finance" }, - { "id": "cofinex", "symbol": "cnx", "name": "Cofinex" }, - { "id": "cofix", "symbol": "cofi", "name": "CoFiX" }, - { "id": "cogecoin", "symbol": "coge", "name": "Cogecoin" }, - { "id": "cogiverse", "symbol": "cogi", "name": "9D NFT" }, - { "id": "coic", "symbol": "coic", "name": "COIC Token" }, - { "id": "coin", "symbol": "coin", "name": "Coin" }, - { "id": "coin98", "symbol": "c98", "name": "Coin98" }, - { "id": "coinage-finance", "symbol": "cage", "name": "Coinage Finance" }, - { "id": "coinalpha", "symbol": "alp", "name": "CoinAlpha" }, - { "id": "coin-artist", "symbol": "coin", "name": "Coin Artist" }, - { "id": "coinary-token", "symbol": "cyt", "name": "Coinary Token" }, - { - "id": "coinbase-stock", - "symbol": "coin", - "name": "Coinbase Tokenized Stock on FTX" - }, - { - "id": "coinbase-stock-bittrex", - "symbol": "coin", - "name": "Coinbase Tokenized Stock on Bittrex" - }, - { - "id": "coinbase-tokenized-stock-defichain", - "symbol": "dcoin", - "name": "Coinbase Tokenized Stock Defichain" - }, - { "id": "coin-capsule", "symbol": "caps", "name": "Ternoa" }, - { "id": "coinclaim", "symbol": "clm", "name": "CoinClaim" }, - { "id": "coindeal-token", "symbol": "cdl", "name": "CoinDeal Token" }, - { "id": "coin-discovery", "symbol": "codi", "name": "Coin Discovery" }, - { "id": "coindom", "symbol": "scc", "name": "Stem Cell Coin" }, - { "id": "coinerr", "symbol": "err", "name": "Coinerr" }, - { "id": "coinex-token", "symbol": "cet", "name": "CoinEx Token" }, - { "id": "coin-fast-alert", "symbol": "cfa", "name": "Coin Fast Alert [OLD]" }, - { "id": "coin-fast-alert-v2", "symbol": "cfa", "name": "Coin Fast Alert V2" }, - { "id": "coinfi", "symbol": "cofi", "name": "CoinFi" }, - { "id": "coinfield-coin", "symbol": "cfc", "name": "CoinField Coin" }, - { "id": "coinfirm-amlt", "symbol": "amlt", "name": "AMLT Network" }, - { "id": "coinflect", "symbol": "cflt", "name": "Coinflect" }, - { "id": "coinfresh", "symbol": "cfresh", "name": "Coinfresh" }, - { "id": "coingress", "symbol": "cgress", "name": "Coingress" }, - { "id": "coinhunters", "symbol": "chtrv2", "name": "Coinhunters" }, - { "id": "coinlancer", "symbol": "cl", "name": "Coinlancer" }, - { "id": "coinlion", "symbol": "lion", "name": "CoinLion" }, - { "id": "coinloan", "symbol": "clt", "name": "CoinLoan" }, - { "id": "coinlogin", "symbol": "clg", "name": "CoinLogin" }, - { "id": "coinmeet", "symbol": "meet", "name": "CoinMeet" }, - { "id": "coinmerge", "symbol": "cmerge", "name": "CoinMerge (ERC20)" }, - { "id": "coinmerge-bsc", "symbol": "cmerge", "name": "CoinMerge (BEP20)" }, - { "id": "coinmetro", "symbol": "xcm", "name": "Coinmetro" }, - { "id": "coinmooner", "symbol": "mooner", "name": "CoinMooner" }, - { "id": "coinnec", "symbol": "coi", "name": "Coinnec" }, - { "id": "coin-of-nature", "symbol": "con", "name": "Coin of Nature" }, - { - "id": "coin-of-the-champions", - "symbol": "coc", - "name": "Coin of the champions" - }, - { "id": "coinone-token", "symbol": "cone", "name": "CoinOne Token" }, - { - "id": "coin-on-file-token", - "symbol": "coft", - "name": "Coin on File Token" - }, - { "id": "coinpad", "symbol": "CP", "name": "Coinpad" }, - { "id": "coinpoker", "symbol": "chp", "name": "CoinPoker" }, - { "id": "coinracer", "symbol": "crace", "name": "Coinracer" }, - { "id": "coinradr", "symbol": "radr", "name": "CoinRadr" }, - { "id": "coin-sack", "symbol": "cs", "name": "Coin Sack" }, - { "id": "coinsale", "symbol": "csf", "name": "Coinsale" }, - { "id": "coinsbit-token", "symbol": "cnb", "name": "Coinsbit Token" }, - { "id": "coinscan", "symbol": "scan", "name": "CoinScan" }, - { "id": "coinscope", "symbol": "coinscope", "name": "Coinscope" }, - { "id": "coinspaid", "symbol": "cpd", "name": "CoinsPaid" }, - { "id": "coinstox", "symbol": "csx", "name": "Coinstox" }, - { "id": "coinswap", "symbol": "coins", "name": "CoinSwap" }, - { "id": "coinswap-space", "symbol": "css", "name": "CoinSwap Space" }, - { "id": "cointorox", "symbol": "orox", "name": "Cointorox" }, - { "id": "cointribe", "symbol": "ctribe", "name": "CoinTribe" }, - { "id": "cointribute", "symbol": "ctb", "name": "Cointribute" }, - { "id": "coinversation", "symbol": "cto", "name": "Coinversation" }, - { "id": "coinviewcap", "symbol": "cvc", "name": "CoinViewCap" }, - { "id": "coinwealth", "symbol": "cnw", "name": "CoinWealth" }, - { "id": "coinweb", "symbol": "cweb", "name": "Coinweb" }, - { "id": "coinwind", "symbol": "cow", "name": "CoinWind" }, - { "id": "coinxpad", "symbol": "cxpad", "name": "CoinxPad" }, - { "id": "coinzoom-token", "symbol": "zoom", "name": "CoinZoom Token" }, - { "id": "cola-token", "symbol": "cola", "name": "Cola Token" }, - { "id": "coldbank", "symbol": "cold", "name": "Coldbank" }, - { "id": "cold-finance", "symbol": "cold", "name": "Cold Finance" }, - { "id": "coldstack", "symbol": "cls", "name": "Coldstack" }, - { "id": "colizeum", "symbol": "zeum", "name": "Colizeum" }, - { "id": "collateral-pay", "symbol": "coll", "name": "Collateral Pay" }, - { - "id": "collateral-pay-governance", - "symbol": "collg", - "name": "Collateral Pay Governance" - }, - { "id": "collectcoin", "symbol": "clct", "name": "CollectCoin" }, - { "id": "collective", "symbol": "co2", "name": "Collective" }, - { "id": "collector-coin", "symbol": "ags", "name": "Collector Coin" }, - { "id": "collegecoinnetwork", "symbol": "ccn", "name": "CollegeCoinNetwork" }, - { "id": "collegicoin", "symbol": "clg", "name": "Collegicoin" }, - { "id": "colligo", "symbol": "cotk", "name": "Colligo" }, - { "id": "colony", "symbol": "cly", "name": "Colony" }, - { - "id": "colony-network-token", - "symbol": "clny", - "name": "Colony Network Token" - }, - { "id": "colossuscoinxt", "symbol": "colx", "name": "ColossusXT" }, - { "id": "columbus", "symbol": "cbs", "name": "Columbus" }, - { "id": "comb-finance", "symbol": "comb", "name": "Comb Finance" }, - { "id": "combo-2", "symbol": "comb", "name": "Combo" }, - { "id": "comdex", "symbol": "cmdx", "name": "Comdex" }, - { "id": "comet-nodes", "symbol": "comet", "name": "Comet Nodes" }, - { "id": "comfy", "symbol": "comfy", "name": "Comfy" }, - { "id": "comfy-share", "symbol": "cshare", "name": "Comfy Share" }, - { "id": "comfy-v2", "symbol": "comfy", "name": "Comfy V2" }, - { "id": "commons-earth", "symbol": "com", "name": "Commons Earth" }, - { "id": "communifty", "symbol": "cnft", "name": "Communifty" }, - { - "id": "community-business-token", - "symbol": "cbt", - "name": "Community Business Token" - }, - { "id": "community-coin-2", "symbol": "ctc", "name": "Community Coin Token" }, - { "id": "community-coin-token", "symbol": "cmc", "name": "Community Coin" }, - { - "id": "community-doge-coin", - "symbol": "ccdoge", - "name": "Community Doge Coin" - }, - { "id": "community-metaverse", "symbol": "comt", "name": "Community Token" }, - { "id": "communitytoken", "symbol": "ct", "name": "Cojam" }, - { - "id": "community-vote-power", - "symbol": "cvp", - "name": "Community Vote Power" - }, - { "id": "comodo-coin", "symbol": "cmd", "name": "Comodo Coin" }, - { "id": "compendium-fi", "symbol": "cmfi", "name": "Compendium.Fi" }, - { "id": "complifi", "symbol": "comfi", "name": "CompliFi" }, - { "id": "component", "symbol": "cmp", "name": "Component" }, - { "id": "compound-0x", "symbol": "czrx", "name": "c0x" }, - { "id": "compound-basic-attention-token", "symbol": "cbat", "name": "cBAT" }, - { "id": "compound-chainlink-token", "symbol": "clink", "name": "cLINK" }, - { "id": "compound-coin", "symbol": "comp", "name": "Compound Coin" }, - { "id": "compound-ether", "symbol": "ceth", "name": "cETH" }, - { "id": "compound-governance-token", "symbol": "comp", "name": "Compound" }, - { "id": "compound-maker", "symbol": "cmkr", "name": "cMKR" }, - { "id": "compound-sushi", "symbol": "csushi", "name": "cSUSHI" }, - { "id": "compound-uniswap", "symbol": "cuni", "name": "cUNI" }, - { "id": "compound-usd-coin", "symbol": "cusdc", "name": "cUSDC" }, - { "id": "compound-usdt", "symbol": "cusdt", "name": "cUSDT" }, - { - "id": "compound-wormhole", - "symbol": "comp", - "name": "Compound (Wormhole)" - }, - { "id": "compound-wrapped-btc", "symbol": "cwbtc", "name": "cWBTC" }, - { - "id": "compound-wrapped-btc-legacy", - "symbol": "cwbtc", - "name": "cWBTC (Legacy)" - }, - { "id": "compound-yearn-finance", "symbol": "cyfi", "name": "cYFI" }, - { "id": "comsa", "symbol": "cms", "name": "COMSA" }, - { "id": "concave", "symbol": "cnv", "name": "Concave" }, - { "id": "conceal", "symbol": "ccx", "name": "Conceal" }, - { - "id": "concentrated-voting-power", - "symbol": "cvp", - "name": "PowerPool Concentrated Voting Power" - }, - { "id": "concertvr", "symbol": "cvt", "name": "concertVR" }, - { "id": "concierge-io", "symbol": "ava", "name": "Travala.com" }, - { "id": "concordium", "symbol": "ccd", "name": "Concordium" }, - { "id": "concrete-codes", "symbol": "conc", "name": "Concrete Codes" }, - { "id": "conegame", "symbol": "conegame", "name": "ConeGame" }, - { "id": "confetti", "symbol": "cfti", "name": "Confetti" }, - { "id": "conflux-token", "symbol": "cfx", "name": "Conflux" }, - { - "id": "congruent-dao-token", - "symbol": "gaas", - "name": "Congruent DAO Token" - }, - { "id": "conic-finance", "symbol": "cnc", "name": "Conic" }, - { "id": "conjee", "symbol": "CONJ", "name": "Conjee" }, - { "id": "connect-coin", "symbol": "xcon", "name": "Connect Coin" }, - { "id": "connect-financial", "symbol": "cnfi", "name": "Connect Financial" }, - { "id": "connectico", "symbol": "con", "name": "Connectico" }, - { "id": "connectjob", "symbol": "cjt", "name": "ConnectJob" }, - { "id": "connectome", "symbol": "cntm", "name": "Connectome" }, - { "id": "connector-coin", "symbol": "ctco", "name": "Connector Coin" }, - { "id": "connect-token", "symbol": "cnt", "name": "Connect Stela" }, - { "id": "conqueror", "symbol": "conq", "name": "Conqueror" }, - { "id": "consentium", "symbol": "csm", "name": "Consentium" }, - { "id": "constellation-labs", "symbol": "dag", "name": "Constellation" }, - { "id": "constitutiondao", "symbol": "people", "name": "ConstitutionDAO" }, - { - "id": "constitutiondao-wormhole", - "symbol": "people", - "name": "ConstitutionDAO (Wormhole)" - }, - { "id": "contentbox", "symbol": "box", "name": "ContentBox" }, - { "id": "contentos", "symbol": "cos", "name": "Contentos" }, - { - "id": "content-value-network", - "symbol": "cvnt", - "name": "Conscious Value Network" - }, - { "id": "continuum-finance", "symbol": "ctn", "name": "Continuum Finance" }, - { "id": "continuum-world", "symbol": "um", "name": "Continuum World" }, - { "id": "contracoin", "symbol": "ctcn", "name": "Contracoin" }, - { "id": "contracto", "symbol": "lock", "name": "Contracto" }, - { "id": "contribute", "symbol": "trib", "name": "Contribute" }, - { "id": "conun", "symbol": "con", "name": "CONUN" }, - { "id": "convergence", "symbol": "conv", "name": "Convergence" }, - { "id": "converter-finance", "symbol": "con", "name": "Converter Finance" }, - { "id": "convex-crv", "symbol": "cvxcrv", "name": "Convex CRV" }, - { "id": "convex-finance", "symbol": "cvx", "name": "Convex Finance" }, - { - "id": "convex-finance-wormhole", - "symbol": "cvx", - "name": "Convex Finance (Wormhole)" - }, - { "id": "cook", "symbol": "cook", "name": "Cook" }, - { "id": "cool-cats", "symbol": "cool20", "name": "Cool Cats" }, - { "id": "coolmining", "symbol": "cooha", "name": "CoolMining" }, - { "id": "cool-monke-banana", "symbol": "cmb", "name": "Cool Monke Banana" }, - { "id": "cool-vault-nftx", "symbol": "cool", "name": "COOL Vault (NFTX)" }, - { "id": "cope", "symbol": "cope", "name": "Cope" }, - { "id": "copiosa", "symbol": "cop", "name": "Copiosa" }, - { "id": "cops-finance", "symbol": "cops", "name": "Cops Finance" }, - { "id": "copuppy", "symbol": "cp", "name": "CoPuppy" }, - { "id": "copycat-finance", "symbol": "copycat", "name": "Copycat Finance" }, - { "id": "coral-swap", "symbol": "coral", "name": "Coral Swap" }, - { "id": "cord-finance", "symbol": "cord", "name": "CORD.Finance" }, - { "id": "core", "symbol": "cmcx", "name": "CORE MultiChain" }, - { "id": "coredao", "symbol": "coredao", "name": "coreDAO" }, - { "id": "corestarter", "symbol": "cstr", "name": "CoreStarter" }, - { "id": "coreto", "symbol": "cor", "name": "Coreto" }, - { "id": "coreum", "symbol": "core", "name": "Coreum" }, - { "id": "corgicoin", "symbol": "corgi", "name": "CorgiCoin" }, - { "id": "corgidoge", "symbol": "corgi", "name": "Corgidoge" }, - { "id": "corgi-inu", "symbol": "corgi", "name": "Corgi Inu" }, - { "id": "corginftgame", "symbol": "cor", "name": "CorgiNFTGame" }, - { "id": "corgiswap", "symbol": "coris", "name": "CorgiSwap" }, - { "id": "corionx", "symbol": "corx", "name": "CorionX" }, - { "id": "corn", "symbol": "corn", "name": "CORN" }, - { "id": "cornatto", "symbol": "cnc", "name": "Cornatto" }, - { "id": "cornichon", "symbol": "corn", "name": "Cornichon" }, - { "id": "cornucopias", "symbol": "copi", "name": "Cornucopias" }, - { "id": "corra-finance", "symbol": "cora", "name": "Corra.Finance" }, - { "id": "corsac-v2", "symbol": "csct", "name": "Corsac v2" }, - { "id": "cortex", "symbol": "ctxc", "name": "Cortex" }, - { "id": "coshi-inu", "symbol": "coshi", "name": "CoShi Inu" }, - { "id": "cosmic-ape-coin", "symbol": "CAC", "name": "Cosmic Ape Coin" }, - { "id": "cosmic-coin", "symbol": "cosmic", "name": "Cosmic Coin" }, - { "id": "cosmic-music", "symbol": "csmc", "name": "Cosmic Music" }, - { "id": "cosmicswap", "symbol": "cosmic", "name": "CosmicSwap" }, - { - "id": "cosmic-universe-magic-token", - "symbol": "magic", - "name": "Cosmic Universe Magic Token" - }, - { "id": "cosmo-coin", "symbol": "cosm", "name": "Cosmo Coin" }, - { "id": "cosmos", "symbol": "atom", "name": "Cosmos Hub" }, - { "id": "cosmostarter", "symbol": "csms", "name": "Cosmostarter" }, - { "id": "cosplay-token", "symbol": "cot", "name": "Cosplay Token (OLD)" }, - { "id": "cosplay-token-2", "symbol": "cot", "name": "Cosplay Token" }, - { "id": "cost-coin", "symbol": "akm", "name": "COST COIN+" }, - { "id": "coti", "symbol": "coti", "name": "COTI" }, - { "id": "cotrader", "symbol": "cot", "name": "CoTrader" }, - { "id": "couchain", "symbol": "cou", "name": "Couchain" }, - { "id": "cougar-token", "symbol": "cgs", "name": "Cougar Token" }, - { "id": "council-of-apes", "symbol": "coape", "name": "Council of Apes" }, - { "id": "counos-coin", "symbol": "cca", "name": "Counos Coin" }, - { "id": "counosx", "symbol": "ccxx", "name": "CounosX" }, - { "id": "counterparty", "symbol": "xcp", "name": "Counterparty" }, - { "id": "county-metaverse", "symbol": "county", "name": "County Metaverse" }, - { "id": "couponbay", "symbol": "cup", "name": "CouponBay" }, - { "id": "cousin-doge", "symbol": "cousindoge", "name": "Cousin Doge" }, - { "id": "covalent", "symbol": "cqt", "name": "Covalent" }, - { "id": "covalent-cova", "symbol": "cova", "name": "Cova Unity" }, - { "id": "covenant-child", "symbol": "covn", "name": "Covenant" }, - { "id": "covercompared", "symbol": "cvr", "name": "CoverCompared" }, - { "id": "cover-protocol", "symbol": "cover", "name": "Cover Protocol" }, - { "id": "covesting", "symbol": "cov", "name": "Covesting" }, - { "id": "covicoin", "symbol": "cvc", "name": "CoviCoin" }, - { - "id": "covid-19-recovery-token", - "symbol": "covdr", - "name": "COVID-19 Recovery Token" - }, - { "id": "covid-doge", "symbol": "covid doge", "name": "Covid Doge" }, - { "id": "covid-slice", "symbol": "covid19", "name": "Covid Slice" }, - { "id": "covid-token", "symbol": "covidtoken", "name": "Covid Token" }, - { "id": "cowboy-snake", "symbol": "cows", "name": "Cowboy Snake" }, - { "id": "cowcoin", "symbol": "cc", "name": "CowCoin" }, - { "id": "cow-inu", "symbol": "ci", "name": "Cow Inu" }, - { "id": "cow-protocol", "symbol": "cow", "name": "CoW Protocol" }, - { "id": "cowry", "symbol": "cow", "name": "COWRY" }, - { "id": "coxswap", "symbol": "cox", "name": "Coxswap" }, - { "id": "cpchain", "symbol": "cpc", "name": "CPChain" }, - { "id": "cplay-network", "symbol": "cplay", "name": "CPLAY Network" }, - { - "id": "cpos-cloud-payment", - "symbol": "cpos", - "name": "CPOS Cloud Payment" - }, - { "id": "cprop", "symbol": "cprop", "name": "CPROP" }, - { "id": "cpuchain", "symbol": "cpu", "name": "CPUchain" }, - { "id": "cpucoin", "symbol": "cpu", "name": "CPUcoin" }, - { "id": "crabada", "symbol": "cra", "name": "Crabada" }, - { "id": "crabada-amulet", "symbol": "cram", "name": "Crabada Amulet" }, - { "id": "crafting-finance", "symbol": "crf", "name": "Crafting Finance" }, - { "id": "craft-network", "symbol": "cft", "name": "Craft network" }, - { "id": "cranx-chain", "symbol": "granx", "name": "GranX Chain" }, - { "id": "cratos", "symbol": "crts", "name": "Cratos" }, - { "id": "crave", "symbol": "crave", "name": "Crave" }, - { - "id": "crazy-bunny-equity-token", - "symbol": "cbunny", - "name": "Crazy Bunny Equity Token" - }, - { "id": "crazyminer", "symbol": "pwr", "name": "CrazyMiner" }, - { "id": "crazy-rich-coin", "symbol": "crc", "name": "Crazy Rich Coin" }, - { "id": "crazytime", "symbol": "crazytime", "name": "CrazyTime" }, - { "id": "crb-coin", "symbol": "crb", "name": "CRB Coin" }, - { "id": "crd-network", "symbol": "crd", "name": "CRD Network" }, - { "id": "crdt", "symbol": "CRDT", "name": "CRDT" }, - { "id": "cre8r-dao", "symbol": "cre8r", "name": "CRE8R DAO" }, - { "id": "cream", "symbol": "crm", "name": "Creamcoin" }, - { "id": "cream-2", "symbol": "cream", "name": "Cream" }, - { "id": "cream-eth2", "symbol": "creth2", "name": "Cream ETH 2" }, - { "id": "creampye", "symbol": "pye", "name": "Creampye [OLD]" }, - { "id": "cream-shares", "symbol": "cshare", "name": "IceCream Shares" }, - { "id": "cream-wormhole", "symbol": "cream", "name": "Cream (Wormhole)" }, - { "id": "creaticles", "symbol": "cre8", "name": "Creaticles" }, - { "id": "creativecoin", "symbol": "crea", "name": "CREA" }, - { "id": "creator-platform", "symbol": "ctr", "name": "Creator Platform" }, - { "id": "creator-protocol", "symbol": "cre", "name": "Creator Protocol" }, - { "id": "creature_hunters", "symbol": "chts", "name": "Creature Hunters" }, - { "id": "creda", "symbol": "creda", "name": "CreDA" }, - { "id": "credefi", "symbol": "credi", "name": "Credefi" }, - { "id": "credit", "symbol": "credit", "name": "TerraCredit" }, - { "id": "credit-2", "symbol": "CREDIT", "name": "PROXI DeFi" }, - { "id": "creditcoin-2", "symbol": "ctc", "name": "Creditcoin" }, - { "id": "credit-printer", "symbol": "creditp", "name": "Credit Printer" }, - { "id": "credits", "symbol": "cs", "name": "CREDITS" }, - { "id": "creditum", "symbol": "credit", "name": "Creditum" }, - { "id": "credmark", "symbol": "cmk", "name": "Credmark" }, - { "id": "creds", "symbol": "creds", "name": "Creds" }, - { "id": "crescent-network", "symbol": "cre", "name": "Crescent Network" }, - { "id": "cresio", "symbol": "xcre", "name": "Cresio" }, - { "id": "crespo", "symbol": "cso", "name": "Crespo" }, - { "id": "crevacoin", "symbol": "creva", "name": "Crevacoin" }, - { "id": "crex-token", "symbol": "crex", "name": "Crex Token" }, - { "id": "cribnb", "symbol": "crb", "name": "Cribnb" }, - { - "id": "cricket-foundation", - "symbol": "cric", - "name": "Cricket Foundation" - }, - { "id": "crimecash", "symbol": "ccash", "name": "CrimeCash" }, - { "id": "crimegold", "symbol": "cgold", "name": "CrimeGold" }, - { "id": "crinet", "symbol": "cnt", "name": "Crinet" }, - { "id": "crir-msh", "symbol": "msh", "name": "CRIR MSH" }, - { "id": "croat", "symbol": "croat", "name": "CROAT" }, - { "id": "crobank", "symbol": "bank", "name": "CroBank" }, - { "id": "croblanc", "symbol": "croblanc", "name": "Croblanc" }, - { "id": "crocash", "symbol": "crocash", "name": "Crocash" }, - { "id": "crodex", "symbol": "crx", "name": "Crodex" }, - { "id": "croeth", "symbol": "croeth", "name": "CROETH" }, - { "id": "crogecoin", "symbol": "croge", "name": "Crogecoin" }, - { "id": "croissant-games", "symbol": "croissant", "name": "Croissant Games" }, - { "id": "croking", "symbol": "crk", "name": "Croking" }, - { "id": "crolambos", "symbol": "crolambos", "name": "CROLambos" }, - { "id": "cromoon", "symbol": "moon", "name": "CroMoon" }, - { "id": "cronaswap", "symbol": "crona", "name": "CronaSwap" }, - { "id": "cronodes", "symbol": "crn", "name": "CroNodes" }, - { "id": "cronofi-finance", "symbol": "crono", "name": "Cronofi Finance" }, - { "id": "cronosphere", "symbol": "sphere", "name": "Cronosphere" }, - { - "id": "cronos-stable-swap", - "symbol": "cross", - "name": "Cronos Stable Swap" - }, - { "id": "cronos-wormhole", "symbol": "cro", "name": "Cronos (Wormhole)" }, - { "id": "cronus-finance", "symbol": "crn", "name": "Cronus Finance" }, - { "id": "cropbytes", "symbol": "cbx", "name": "CropBytes" }, - { "id": "cropperfinance", "symbol": "crp", "name": "CropperFinance" }, - { "id": "cro-predict", "symbol": "CRP", "name": "CRO Predict" }, - { "id": "cross-chain-bch", "symbol": "ccbch", "name": "Cross-Chain BCH" }, - { - "id": "cross-chain-bridge", - "symbol": "bridge", - "name": "Cross-Chain Bridge" - }, - { - "id": "cross-chain-capital", - "symbol": "ccc", - "name": "Cross Chain Capital" - }, - { - "id": "cross-chain-farming", - "symbol": "CCF", - "name": "Cross Chain Farming" - }, - { "id": "crosschain-iotx", "symbol": "ciotx", "name": "Crosschain IOTX" }, - { "id": "crossfi", "symbol": "crfi", "name": "CrossFi" }, - { - "id": "crossing-the-yellow-blocks", - "symbol": "cbk", - "name": "Crossing the Yellow Blocks" - }, - { "id": "crosspad", "symbol": "cross", "name": "CrossPad" }, - { "id": "crossswap", "symbol": "cswap", "name": "CrossSwap" }, - { "id": "crosswallet", "symbol": "cwt", "name": "CrossWallet" }, - { "id": "crossy-animals", "symbol": "ca", "name": "Crossy Animals" }, - { "id": "crotama", "symbol": "crotama", "name": "Crotama" }, - { "id": "crowd", "symbol": "cwd", "name": "CROWD" }, - { "id": "crowdclassic", "symbol": "crcl", "name": "CRowdCLassic" }, - { "id": "crowdhero", "symbol": "crwd", "name": "CrowdHero" }, - { "id": "crowdswap", "symbol": "crowd", "name": "CrowdSwap" }, - { "id": "crown", "symbol": "crw", "name": "Crown" }, - { "id": "crowns", "symbol": "cws", "name": "Seascape Crowns" }, - { "id": "crown-sovereign", "symbol": "csov", "name": "Crown Sovereign" }, - { "id": "crown-token", "symbol": "cwt", "name": "Crown Token" }, - { "id": "crowny-token", "symbol": "crwny", "name": "Crowny Token" }, - { "id": "crow-token", "symbol": "crow", "name": "Crow" }, - { "id": "croxswap", "symbol": "crox", "name": "CroxSwap" }, - { "id": "crude-token", "symbol": "crude", "name": "Crude Token" }, - { "id": "crunch", "symbol": "crunch", "name": "Crunchy" }, - { "id": "crunchy-dao", "symbol": "crdao", "name": "Crunchy DAO" }, - { "id": "crunchy-network", "symbol": "crunch", "name": "Crunchy Network" }, - { - "id": "crusaders-of-crypto", - "symbol": "crusader", - "name": "Crusaders of Crypto" - }, - { "id": "crust-network", "symbol": "cru", "name": "Crust Network" }, - { "id": "crust-storage-market", "symbol": "csm", "name": "Crust Shadow" }, - { "id": "cryb", "symbol": "cryb", "name": "Cryb" }, - { "id": "crybet", "symbol": "cbt", "name": "CryBet" }, - { "id": "crycash", "symbol": "crc", "name": "CRYCASH" }, - { "id": "cry-coin", "symbol": "CRYY", "name": "Cry Coin" }, - { "id": "cryn", "symbol": "cryn", "name": "CRYN" }, - { "id": "cryowar-token", "symbol": "cwar", "name": "Cryowar" }, - { "id": "crypcade-shares", "symbol": "cade", "name": "CrypCade Shares" }, - { "id": "cryptalk", "symbol": "cryp", "name": "Cryptalk" }, - { "id": "cryptaur", "symbol": "cpt", "name": "Cryptaur" }, - { "id": "crypterium", "symbol": "crpt", "name": "Crypterium" }, - { "id": "cryptertoken", "symbol": "crypt", "name": "CrypterToken" }, - { "id": "cryptex", "symbol": "crx", "name": "CryptEx" }, - { "id": "cryptex-finance", "symbol": "ctx", "name": "Cryptex Finance" }, - { "id": "cryptia", "symbol": "crypt", "name": "Cryptia" }, - { "id": "cryptid", "symbol": "cid", "name": "Cryptid" }, - { "id": "cryption-network", "symbol": "cnt", "name": "Cryption Network" }, - { "id": "crypto20", "symbol": "c20", "name": "CRYPTO20" }, - { "id": "crypto-accept", "symbol": "acpt", "name": "Crypto Accept" }, - { - "id": "crypto-application-token", - "symbol": "capp", - "name": "Crypto Application Token" - }, - { - "id": "crypto-arcade-punk", - "symbol": "c-arcade", - "name": "Crypto Arcade Punk" - }, - { "id": "cryptoart-ai", "symbol": "cart", "name": "CryptoArt.Ai" }, - { "id": "cryptoball", "symbol": "CRYPTOBALL", "name": "CryptoBall" }, - { "id": "crypto-bank", "symbol": "cbank", "name": "Crypto Bank" }, - { "id": "crypto-bank-token", "symbol": "cbt", "name": "Crypto Bank Token" }, - { "id": "cryptobattles", "symbol": "cbt", "name": "CryptoBattles" }, - { "id": "cryptobay", "symbol": "bay", "name": "CryptoBay" }, - { "id": "crypto-bay", "symbol": "pearl", "name": "Crypto Bay" }, - { "id": "cryptobike", "symbol": "cb", "name": "CryptoBike" }, - { - "id": "crypto-bike-riders", - "symbol": "riders", - "name": "Crypto Bike Riders" - }, - { "id": "crypto-birds", "symbol": "xcb", "name": "Crypto Birds" }, - { "id": "cryptoblades", "symbol": "skill", "name": "CryptoBlades" }, - { - "id": "cryptoblades-kingdoms", - "symbol": "king", - "name": "CryptoBlades Kingdoms" - }, - { "id": "cryptoblast", "symbol": "cbt", "name": "CryptoBlast" }, - { "id": "cryptobonusmiles", "symbol": "cbm", "name": "CryptoBonusMiles" }, - { "id": "cryptobosscoin", "symbol": "cbc", "name": "CryptoBossCoin" }, - { "id": "cryptobucks", "symbol": "CBUCKS", "name": "CRYPTOBUCKS" }, - { "id": "cryptobuyer-token", "symbol": "xpt", "name": "Cryptobuyer Token" }, - { "id": "crypto-candy", "symbol": "candy", "name": "Crypto Candy" }, - { - "id": "crypto-carbon-energy", - "symbol": "cyce", - "name": "Crypto Carbon Energy" - }, - { "id": "cryptocars", "symbol": "ccar", "name": "CryptoCars" }, - { "id": "crypto-cars-world", "symbol": "cars", "name": "Crypto Cars World" }, - { "id": "cryptocart", "symbol": "ccv2", "name": "CryptoCart V2" }, - { "id": "cryptocat", "symbol": "ccat", "name": "CryptoCat" }, - { "id": "cryptocean", "symbol": "cron", "name": "Cryptocean" }, - { "id": "cryptochrome", "symbol": "chm", "name": "Cryptochrome" }, - { "id": "cryptocitizen", "symbol": "ccash", "name": "CryptoCitizen" }, - { "id": "cryptoclans", "symbol": "coc", "name": "CryptoClans" }, - { "id": "crypto-classic", "symbol": "crc", "name": "Crypto Classic" }, - { "id": "cryptocoinpay", "symbol": "ccp", "name": "CryptoCoinPay" }, - { "id": "crypto-com-chain", "symbol": "cro", "name": "Cronos" }, - { - "id": "crypto-crash-gaming", - "symbol": "ccg", - "name": "Crypto Crash Gaming" - }, - { "id": "cryptocricketclub", "symbol": "3cs", "name": "CryptoCricketClub" }, - { - "id": "cryptocurrency-market-index", - "symbol": "cmi", - "name": "Cryptocurrency Market Index" - }, - { - "id": "cryptocurrency-top-10-tokens-index", - "symbol": "cc10", - "name": "Cryptocurrency Top 10 Index" - }, - { - "id": "crypto-development-services", - "symbol": "CDS", - "name": "Crypto Development Services" - }, - { "id": "cryptodicehero", "symbol": "hro", "name": "CryptoDiceHero" }, - { "id": "cryptodogs", "symbol": "crd", "name": "CryptoDogs" }, - { "id": "cryptodrop", "symbol": "cdrop", "name": "CryptoDrop" }, - { "id": "cryptoenergy", "symbol": "cnrg", "name": "CryptoEnergy" }, - { - "id": "crypto-energy-token", - "symbol": "CET", - "name": "Crypto Energy Token" - }, - { "id": "crypto-excellence", "symbol": "ce", "name": "Crypto Excellence" }, - { "id": "cryptoexpress", "symbol": "xpress", "name": "CryptoXpress" }, - { - "id": "crypto-fantasy-coin", - "symbol": "CFC", - "name": "Crypto Fantasy Coin" - }, - { - "id": "crypto-fantasy-league", - "symbol": "cfl", - "name": "Crypto Fantasy League" - }, - { "id": "cryptofarming", "symbol": "$CFAR", "name": "CryptoFarming" }, - { "id": "cryptofifa", "symbol": "ffa", "name": "FFA.Games" }, - { "id": "crypto-fight-club", "symbol": "fight", "name": "Crypto Fight Club" }, - { "id": "cryptoflow", "symbol": "cfl", "name": "Cryptoflow" }, - { "id": "cryptofootball", "symbol": "$ballz", "name": "CryptoFootball" }, - { "id": "cryptoforspeed", "symbol": "cfs", "name": "CryptoForSpeed" }, - { "id": "cryptofranc", "symbol": "xchf", "name": "CryptoFranc" }, - { "id": "cryptogamez", "symbol": "cgaz", "name": "CryptoGamez" }, - { - "id": "crypto-gaming-united", - "symbol": "cgu", - "name": "Crypto Gaming United" - }, - { "id": "cryptogangsters", "symbol": "cgang", "name": "CryptoGangsters" }, - { "id": "cryptogcoin", "symbol": "crg", "name": "Cryptogcoin" }, - { - "id": "crypto-gladiator-shards", - "symbol": "cgs", - "name": "Crypto Gladiator Shards" - }, - { "id": "cryptogodz", "symbol": "godz", "name": "Cryptogodz" }, - { "id": "cryptogram", "symbol": "cryptogram", "name": "Cryptogram" }, - { "id": "cryptoguards", "symbol": "cgar", "name": "CryptoGuards" }, - { "id": "cryptoheadz", "symbol": "headz", "name": "Cryptoheadz" }, - { - "id": "crypto-holding-frank-token", - "symbol": "chft", - "name": "Crypto Holding Frank Token" - }, - { "id": "cryptohorse", "symbol": "chorse", "name": "CryptoHorse" }, - { - "id": "cryptoids-admin-coin", - "symbol": "cac", - "name": "Cryptoids Admin Coin" - }, - { - "id": "cryptoids-game-coin", - "symbol": "cgc", - "name": "Cryptoids Game Coin" - }, - { "id": "cryptoindex-io", "symbol": "cix100", "name": "Cryptoindex.com 100" }, - { - "id": "crypto-international", - "symbol": "cri", - "name": "Crypto International" - }, - { "id": "crypto-inu", "symbol": "abcd", "name": "Crypto Inu" }, - { "id": "crypto-island", "symbol": "cisla", "name": "Crypto Island" }, - { "id": "crypto-jackpot", "symbol": "cjp", "name": "Crypto Jackpot" }, - { "id": "cryptojetski", "symbol": "cjet", "name": "CryptoJetski" }, - { "id": "cryptokek", "symbol": "kek", "name": "Cryptokek" }, - { "id": "cryptokenz", "symbol": "cyt", "name": "Cryptokenz" }, - { "id": "crypto-klash", "symbol": "klh", "name": "Crypto Klash" }, - { "id": "crypto-kombat", "symbol": "kombat", "name": "Crypto Kombat" }, - { "id": "cryptolion", "symbol": "CLION", "name": "CryptoLion" }, - { "id": "cryptomafia", "symbol": "$CMF", "name": "CryptoMafia" }, - { - "id": "crypto-makers-foundation", - "symbol": "cmf", - "name": "Crypto Makers Foundation" - }, - { "id": "cryptomeda", "symbol": "tech", "name": "Cryptomeda" }, - { - "id": "crypto-media-network", - "symbol": "cmn", - "name": "Crypto Media Network" - }, - { - "id": "cryptomines-eternal", - "symbol": "ETERNAL", - "name": "CryptoMines Eternal" - }, - { - "id": "cryptomines-reborn", - "symbol": "crux", - "name": "CryptoMines Reborn" - }, - { "id": "cryptomoonshots", "symbol": "cms", "name": "CryptoMoonShots" }, - { "id": "cryptomotorcycle", "symbol": "cmc", "name": "CryptoMotorcycle" }, - { "id": "crypto-mushroomz", "symbol": "shroomz", "name": "Crypto Mushroomz" }, - { - "id": "cryptoneur-network-foundation", - "symbol": "CNF", - "name": "CryptoNeur Network foundation" - }, - { "id": "cryptonex", "symbol": "cnx", "name": "Cryptonex" }, - { "id": "cryptonia-poker", "symbol": "cnp", "name": "Cryptonia Poker" }, - { "id": "cryptonite", "symbol": "CRT", "name": "Cryptonite" }, - { "id": "cryptonite-token", "symbol": "ctnt", "name": "Cryptonite Token" }, - { "id": "cryptonits", "symbol": "crt", "name": "Cryptonits" }, - { "id": "cryptonovae", "symbol": "yae", "name": "Cryptonovae" }, - { "id": "cryptopay", "symbol": "cpay", "name": "Cryptopay" }, - { - "id": "cryptoperformance-coin", - "symbol": "cpc", - "name": "CryptoPerformance Coin" - }, - { "id": "crypto-perx", "symbol": "cprx", "name": "Crypto Perx" }, - { "id": "crypto-phoenix", "symbol": "cphx", "name": "Crypto Phoenix" }, - { - "id": "cryptopia-land-dollar", - "symbol": "cld", - "name": "Cryptopia Land Dollar" - }, - { "id": "crypto-piece", "symbol": "belly", "name": "Crypto Piece" }, - { "id": "cryptoplanes", "symbol": "cpan", "name": "CryptoPlanes" }, - { "id": "cryptoplants", "symbol": "cpc", "name": "CryptoPlants Club" }, - { "id": "cryptopolis", "symbol": "cpo", "name": "Cryptopolis" }, - { "id": "crypto-pote-token", "symbol": "pope", "name": "Crypto Pote Token" }, - { "id": "crypto-price-index", "symbol": "cpi", "name": "Crypto Price Index" }, - { "id": "cryptoprofile", "symbol": "cp", "name": "CryptoProfile" }, - { "id": "crypto-puffs", "symbol": "puffs", "name": "Crypto Puffs" }, - { - "id": "cryptopunk-7171-hoodie", - "symbol": "hoodie", - "name": "CryptoPunk #7171" - }, - { "id": "cryptopunt", "symbol": "pun", "name": "CryptoPunt" }, - { "id": "crypto-raiders", "symbol": "raider", "name": "Crypto Raiders" }, - { "id": "crypto-realms-war", "symbol": "yny", "name": "Crypto Realms War" }, - { "id": "cryptorewards", "symbol": "crs", "name": "Crypto Rewards Studio" }, - { "id": "cryptorg-token", "symbol": "ctg", "name": "Cryptorg Token" }, - { - "id": "crypto-rocket-launch", - "symbol": "crl", - "name": "Crypto Rocket Launch" - }, - { "id": "cryptorockets", "symbol": "crocket", "name": "CryptoRockets" }, - { "id": "crypto-royale", "symbol": "roy", "name": "Crypto Royale" }, - { "id": "cryptorpg", "symbol": "rpg", "name": "CryptoRPG" }, - { "id": "cryptorun-network", "symbol": "crn", "name": "Cryptorun Network" }, - { "id": "cryptosaga", "symbol": "saga", "name": "CryptoSaga" }, - { "id": "cryptoshares", "symbol": "shares", "name": "Cryptoshares" }, - { "id": "crypto-shield", "symbol": "shield", "name": "Crypto Shield" }, - { "id": "cryptoships", "symbol": "cship", "name": "CryptoShips" }, - { "id": "cryptoskates", "symbol": "cst", "name": "CryptoSkates" }, - { "id": "crypto-snack", "symbol": "snack", "name": "Crypto Snack" }, - { "id": "crypto-soccer", "symbol": "csc", "name": "Crypto Soccer" }, - { "id": "cryptosoul", "symbol": "soul", "name": "CryptoSoul" }, - { "id": "crypto-sports", "symbol": "cspn", "name": "Crypto Sports" }, - { "id": "crypto-stake-token", "symbol": "cst", "name": "Crypto Stake Token" }, - { "id": "cryptosword", "symbol": "swd", "name": "CryptoSword" }, - { "id": "cryptotanks", "symbol": "tank", "name": "CryptoTanks" }, - { "id": "cryptotask-2", "symbol": "ctask", "name": "CryptoTask" }, - { "id": "cryptotaxis-token", "symbol": "ctax", "name": "Cryptotaxis Token" }, - { "id": "cryptotem", "symbol": "totem", "name": "Cryptotem" }, - { "id": "cryptotrains", "symbol": "ctrain", "name": "CryptoTrains" }, - { "id": "crypto-twitter", "symbol": "ct", "name": "Crypto Twitter" }, - { "id": "cryptotycoon", "symbol": "ctt", "name": "CryptoTycoon" }, - { "id": "cryptounit", "symbol": "cru", "name": "Cryptounit" }, - { - "id": "cryptoverificationcoin", - "symbol": "cvcc", - "name": "CryptoVerificationCoin" - }, - { "id": "cryptoverse", "symbol": "", "name": "Cryptoverse" }, - { - "id": "crypto-village-accelerator", - "symbol": "cva", - "name": "Crypto Village Accelerator" - }, - { - "id": "crypto-village-accelerator-cvag", - "symbol": "cvag", - "name": "Crypto Village Accelerator CVAG" - }, - { - "id": "crypto-volatility-token", - "symbol": "cvol", - "name": "Crypto Volatility Token" - }, - { "id": "cryptovszombie", "symbol": "cvz", "name": "CryptoVsZombie" }, - { "id": "cryptowar-xblade", "symbol": "open", "name": "OpenWorld" }, - { "id": "cryptowolf", "symbol": "cwolf", "name": "CryptoWolf" }, - { "id": "cryptoworld-vip", "symbol": "cwv", "name": "CryptoWorld.VIP" }, - { "id": "cryptozerofi", "symbol": "zeri", "name": "CryptoZerofi" }, - { "id": "cryptozoo", "symbol": "zoo", "name": "CryptoZoo" }, - { "id": "cryptozoon", "symbol": "zoon", "name": "CryptoZoon" }, - { "id": "cryptrust", "symbol": "ctrt", "name": "Cryptrust" }, - { "id": "cryptyk", "symbol": "ctk", "name": "Cryptyk" }, - { "id": "crystal", "symbol": "crystal", "name": "Crystal" }, - { "id": "crystal-clear", "symbol": "cct", "name": "Crystal Clear" }, - { "id": "crystal-kingdoms", "symbol": "ckg", "name": "Crystal Kingdoms" }, - { "id": "crystal-of-dragon", "symbol": "cod", "name": "Crystal Of Dragon" }, - { "id": "crystal-powder", "symbol": "cp", "name": "Crystal Powder" }, - { "id": "crystal-token", "symbol": "cyl", "name": "Crystal Token" }, - { "id": "crystal-wallet", "symbol": "crt", "name": "Crystal Wallet" }, - { "id": "crystl-finance", "symbol": "crystl", "name": "Crystl Finance" }, - { "id": "cspc", "symbol": "cspc", "name": "CSPC" }, - { "id": "csp-dao-network", "symbol": "nebo", "name": "CSP DAO Network" }, - { "id": "cspro-chain", "symbol": "cspro", "name": "CSPro Chain" }, - { "id": "ctc", "symbol": "c2c", "name": "C2C System" }, - { "id": "ctc-2", "symbol": "ctc", "name": "CTC" }, - { "id": "c-token-2", "symbol": "c", "name": "Carbon Coin" }, - { "id": "ctomorrow-platform", "symbol": "ctp", "name": "Ctomorrow Platform" }, - { - "id": "cts-coin", - "symbol": "ctsc", - "name": "Crypto Trading Solutions Coin" - }, - { "id": "cube", "symbol": "itamcube", "name": "CUBE" }, - { "id": "cube-intelligence", "symbol": "auto", "name": "Cube Intelligence" }, - { "id": "cube-token", "symbol": "cube", "name": "Cube Token" }, - { "id": "cub-finance", "symbol": "cub", "name": "Cub Finance" }, - { "id": "cubiex", "symbol": "cbix", "name": "Cubiex" }, - { "id": "cubiex-power", "symbol": "cbix-p", "name": "Cubiex Power" }, - { "id": "cubo", "symbol": "cubo", "name": "Cubo" }, - { "id": "cudl-finance", "symbol": "cudl", "name": "Cudl Finance" }, - { "id": "cudos", "symbol": "cudos", "name": "Cudos" }, - { "id": "cuex", "symbol": "CCAP", "name": "CUEX Capital" }, - { "id": "cuffies", "symbol": "$cuffies", "name": "Cuffies" }, - { "id": "cujo-inu", "symbol": "cujo", "name": "Cujo Inu" }, - { "id": "cult-dao", "symbol": "cult", "name": "Cult DAO" }, - { "id": "cultiplan", "symbol": "ctpl", "name": "Cultiplan" }, - { - "id": "culture-ticket-chain", - "symbol": "ctc", - "name": "Culture Ticket Chain" - }, - { "id": "cuminu", "symbol": "cuminu", "name": "Cuminu" }, - { "id": "cumrocket", "symbol": "cummies", "name": "CumRocket" }, - { "id": "cupid-farm", "symbol": "arrows", "name": "Cupid Farm" }, - { "id": "curate", "symbol": "xcur", "name": "Curate" }, - { "id": "curecoin", "symbol": "cure", "name": "Curecoin" }, - { "id": "cure-token-v2", "symbol": "CURE", "name": "CURE Token V2" }, - { "id": "curio-governance", "symbol": "cgt", "name": "Curio Governance" }, - { "id": "curio-stable-coin", "symbol": "csc", "name": "Curio Stable Coin" }, - { "id": "curryswap", "symbol": "curry", "name": "CurrySwap" }, - { "id": "curve-dao-token", "symbol": "crv", "name": "Curve DAO Token" }, - { - "id": "curve-dao-token-wormhole", - "symbol": "crv", - "name": "Curve DAO Token (Wormhole)" - }, - { - "id": "curve-fi-gdai-gusdc-gusdt", - "symbol": "g3CRV", - "name": "Curve.fi gDAI/gUSDC/gUSDT" - }, - { - "id": "curve-fi-ydai-yusdc-yusdt-ytusd", - "symbol": "yCurve", - "name": "LP-yCurve" - }, - { "id": "curvehash", "symbol": "curve", "name": "CURVEHASH" }, - { - "id": "custom-contract-network", - "symbol": "ccn", - "name": "Custom contract network" - }, - { "id": "cutcoin", "symbol": "cut", "name": "CUTcoin" }, - { "id": "cvault-finance", "symbol": "core", "name": "cVault.finance" }, - { "id": "cvd19", "symbol": "cvd19", "name": "CVD19" }, - { "id": "cvip", "symbol": "cvip", "name": "CVIP" }, - { "id": "cvnx", "symbol": "cvnx", "name": "CVNX" }, - { "id": "cxcoin", "symbol": "cx", "name": "CxCoin" }, - { "id": "cxrbn-token", "symbol": "cxrbn", "name": "CXRBN Token" }, - { "id": "cyberclassic", "symbol": "class", "name": "Cyberclassic" }, - { "id": "cyber-crystal", "symbol": "crystal", "name": "Cyber Crystal" }, - { "id": "cyberdao", "symbol": "cyber", "name": "CyberDAO" }, - { "id": "cyberdoge", "symbol": "cybrrrdoge", "name": "CyberDoge" }, - { "id": "cyberdragon-gold", "symbol": "gold", "name": "CyberDragon Gold" }, - { "id": "cyberfi", "symbol": "cfi", "name": "CyberFi" }, - { "id": "cyberfm", "symbol": "cyfm", "name": "CyberFM" }, - { "id": "cyberkongz", "symbol": "kongz20", "name": "CYBERKONGZ" }, - { "id": "cybermiles", "symbol": "cmt", "name": "CyberMiles" }, - { "id": "cyber-movie-chain", "symbol": "cmct", "name": "Cyber Movie Chain" }, - { "id": "cybermusic", "symbol": "cymt", "name": "CyberMusic" }, - { "id": "cybertime-finance", "symbol": "ctf", "name": "CyberTime Finance" }, - { "id": "cybertronchain", "symbol": "ctc", "name": "CyberTronchain" }, - { "id": "cybervein", "symbol": "cvt", "name": "CyberVeinToken" }, - { "id": "cybex", "symbol": "cyb", "name": "Cybex" }, - { - "id": "cybloc-battery-token", - "symbol": "cbt", - "name": "CyBloc Battery Token" - }, - { "id": "cyborg-apes", "symbol": "borg", "name": "Cyborg Apes" }, - { "id": "cycan-network", "symbol": "cyn", "name": "Cycan Network" }, - { "id": "cycle-punk", "symbol": "bike", "name": "Cycle Punk" }, - { "id": "cycle-token", "symbol": "cycle", "name": "Cycle Token" }, - { "id": "cyclone-protocol", "symbol": "cyc", "name": "Cyclone Protocol" }, - { "id": "cyclops-treasure", "symbol": "cytr", "name": "Cyclops Treasure" }, - { "id": "cyclos", "symbol": "cys", "name": "Cykura" }, - { "id": "cylum-finance", "symbol": "cym", "name": "Cylum Finance" }, - { "id": "cyn-c", "symbol": "cync", "name": "Cyn-C" }, - { "id": "cyop-protocol", "symbol": "cyop", "name": "CyOp Protocol" }, - { "id": "cypherium", "symbol": "cph", "name": "Cypherium" }, - { "id": "czdiamond", "symbol": "czdiamond", "name": "CZDiamond" }, - { "id": "czfarm", "symbol": "czf", "name": "CZFarm" }, - { "id": "czusd", "symbol": "czusd", "name": "CZUSD" }, - { "id": "d3", "symbol": "defi", "name": "D3" }, - { "id": "dabb-doge", "symbol": "DDoge", "name": "Dabb Doge" }, - { "id": "dacc", "symbol": "dacc", "name": "DACC" }, - { "id": "dacsee", "symbol": "dacs", "name": "Dacsee" }, - { "id": "dacxi", "symbol": "dacxi", "name": "Dacxi" }, - { "id": "daddybezos", "symbol": "djbz", "name": "DaddyBezos" }, - { "id": "daddycake", "symbol": "daddycake", "name": "DaddyCake" }, - { "id": "daddy-doge", "symbol": "daddydoge", "name": "Daddy Doge" }, - { "id": "daddy-dogeback", "symbol": "daddydb", "name": "Daddy Dogeback" }, - { "id": "daddyeth", "symbol": "DADDYETH", "name": "DaddyETH" }, - { "id": "daddy-ethereum", "symbol": "DDETH", "name": "Daddy Ethereum" }, - { "id": "daddyshiba", "symbol": "daddyshiba", "name": "DaddyShiba" }, - { "id": "daddyusdt", "symbol": "DaddyUSDT", "name": "DaddyUSDT" }, - { "id": "daddyyorkie", "symbol": "DDY", "name": "DaddyYorkie" }, - { "id": "daex", "symbol": "dax", "name": "DAEX" }, - { "id": "dafin", "symbol": "daf", "name": "DaFIN" }, - { "id": "dafi-protocol", "symbol": "dafi", "name": "Dafi Protocol" }, - { "id": "daftcoin", "symbol": "daft", "name": "DaftCoin" }, - { "id": "dagger", "symbol": "xdag", "name": "Dagger" }, - { "id": "dai", "symbol": "dai", "name": "Dai" }, - { "id": "daikicoin", "symbol": "dic", "name": "Daikicoin" }, - { "id": "daikokuten-sama", "symbol": "DKKS", "name": "Daikokuten Sama" }, - { "id": "daily-cop", "symbol": "dlycop", "name": "Daily COP" }, - { "id": "dain-token", "symbol": "dain", "name": "Dain Token" }, - { "id": "dairy-money-milk", "symbol": "milk", "name": "Dairy.Money MILK" }, - { - "id": "dai-stablecoin-wormhole", - "symbol": "daipo", - "name": "Dai Stablecoin (Wormhole)" - }, - { "id": "daisy", "symbol": "daisy", "name": "Daisy Protocol" }, - { "id": "dai-wormhole", "symbol": "dai", "name": "DAI (Wormhole)" }, - { "id": "dalecoin", "symbol": "dalc", "name": "Dalecoin" }, - { "id": "dali", "symbol": "dali", "name": "DALI" }, - { "id": "dama", "symbol": "dama", "name": "DAMA" }, - { "id": "damn-token", "symbol": "damn", "name": "DAMN Token" }, - { "id": "danat-coin", "symbol": "dnc", "name": "Danat Coin" }, - { "id": "dandy", "symbol": "dandy", "name": "Dandy Dego" }, - { "id": "dangermoon", "symbol": "DANGERMOON", "name": "DangerMoon" }, - { "id": "dao1", "symbol": "dao1", "name": "DAO1" }, - { "id": "daobao", "symbol": "bao", "name": "DAOBAO" }, - { "id": "dao-farmer-daof", "symbol": "daof", "name": "DAO Farmer DAOF" }, - { "id": "dao-farmer-dfg", "symbol": "dfg", "name": "DAO Farmer DFG" }, - { "id": "dao-farmer-dfm", "symbol": "dfm", "name": "DAO Farmer DFM" }, - { "id": "dao-farmer-dfw", "symbol": "dfw", "name": "DAO Farmer DFW" }, - { "id": "daohaus", "symbol": "haus", "name": "DAOhaus" }, - { "id": "dao-invest", "symbol": "vest", "name": "DAO Invest" }, - { "id": "daoland", "symbol": "dld", "name": "Daoland" }, - { "id": "daolaunch", "symbol": "dal", "name": "DAOLaunch" }, - { "id": "dao-maker", "symbol": "dao", "name": "DAO Maker" }, - { "id": "daopolis-token", "symbol": "daos", "name": "Daopolis Token" }, - { "id": "daosquare", "symbol": "rice", "name": "DAOSquare" }, - { "id": "daostack", "symbol": "gen", "name": "DAOstack" }, - { "id": "daovc", "symbol": "daovc", "name": "DAOvc" }, - { "id": "daoventures", "symbol": "dvd", "name": "DAOventures" }, - { "id": "dapp", "symbol": "dapp", "name": "LiquidApps" }, - { "id": "dappatoz", "symbol": "dcash", "name": "Dappatoz" }, - { "id": "dapp-com", "symbol": "dappt", "name": "Dapp.com" }, - { "id": "dappercoin", "symbol": "dapp", "name": "DapperCoin" }, - { "id": "dappnode", "symbol": "node", "name": "DAppNode" }, - { "id": "dappradar", "symbol": "radar", "name": "DappRadar" }, - { "id": "dappstore", "symbol": "dappx", "name": "dAppstore" }, - { "id": "dappsy", "symbol": "app", "name": "Dappsy" }, - { "id": "daps-token", "symbol": "daps", "name": "DAPS Coin" }, - { "id": "darcmatter-coin", "symbol": "darc", "name": "Konstellation" }, - { "id": "dar-dex-token", "symbol": "ddt", "name": "Dar Dex Token" }, - { "id": "darenft", "symbol": "dnft", "name": "DareNFT" }, - { "id": "dareplay", "symbol": "dpl", "name": "DarePlay" }, - { "id": "darkcrypto", "symbol": "dark", "name": "DarkCrypto" }, - { "id": "darkcrypto-share", "symbol": "sky", "name": "DarkCrypto Share" }, - { - "id": "dark-energy-crystals", - "symbol": "dec", - "name": "Dark Energy Crystals" - }, - { "id": "dark-frontiers", "symbol": "dark", "name": "Dark Frontiers" }, - { "id": "darkgang-finance", "symbol": "darkg", "name": "DarkGang Finance" }, - { "id": "darkknight", "symbol": "dknight", "name": "Dark Knight" }, - { "id": "dark-land-survival", "symbol": "big", "name": "Dark Land Survival" }, - { "id": "dark-magic", "symbol": "dmagic", "name": "Dark Magic" }, - { "id": "darkmagick", "symbol": "dmgk", "name": "DarkMAGICK" }, - { "id": "darkmatter", "symbol": "dmt", "name": "DarkMatter" }, - { "id": "dark-matter", "symbol": "dmt", "name": "Dark Matter" }, - { "id": "dark-matter-defi", "symbol": "dmd", "name": "Dark Matter Defi" }, - { - "id": "dark-matter-token", - "symbol": "dkmt", - "name": "Vibranium Dark Matter" - }, - { "id": "darkness-dollar", "symbol": "dusd", "name": "Darkness Dollar" }, - { "id": "darkness-share", "symbol": "ness", "name": "Darkness Share" }, - { "id": "darkpaycoin", "symbol": "d4rk", "name": "DARK" }, - { "id": "dark-planet", "symbol": "dp", "name": "Dark Planet" }, - { "id": "darkshield", "symbol": "dks", "name": "DarkShield" }, - { "id": "darleygo-essence", "symbol": "dge", "name": "DarleyGo Essence" }, - { "id": "darma-cash", "symbol": "dmch", "name": "Darma Cash" }, - { "id": "dart-insurance", "symbol": "dart", "name": "dART Insurance" }, - { "id": "dart-inu", "symbol": "dart", "name": "Dart Inu" }, - { - "id": "darwinia-commitment-token", - "symbol": "kton", - "name": "Darwinia Commitment Token" - }, - { - "id": "darwinia-crab-network", - "symbol": "crab", - "name": "Darwinia Crab Network Native Token" - }, - { - "id": "darwinia-network-native-token", - "symbol": "ring", - "name": "Darwinia Network Native Token" - }, - { "id": "dascoin", "symbol": "grn", "name": "GreenPower" }, - { "id": "dash", "symbol": "dash", "name": "Dash" }, - { "id": "dash-diamond", "symbol": "dashd", "name": "Dash Diamond" }, - { "id": "dashsports", "symbol": "dass", "name": "DashSports" }, - { "id": "data", "symbol": "dta", "name": "DATA" }, - { "id": "databroker-dao", "symbol": "dtx", "name": "DaTa eXchange Token" }, - { - "id": "datachain-foundation", - "symbol": "dc", - "name": "DATACHAIN FOUNDATION" - }, - { "id": "datacoin", "symbol": "dtc", "name": "Datacoin" }, - { - "id": "data-economy-index", - "symbol": "data", - "name": "Data Economy Index" - }, - { - "id": "data-enterprise-chain", - "symbol": "dec", - "name": "Data Enterprise Chain" - }, - { "id": "datahighway", "symbol": "dhx", "name": "DataHighway" }, - { "id": "datakyc", "symbol": "dkyc", "name": "DataKYC" }, - { "id": "datamine", "symbol": "dam", "name": "Datamine" }, - { - "id": "data-trade-on-demand-platform", - "symbol": "dtop", - "name": "DTOP Token" - }, - { "id": "datx", "symbol": "datx", "name": "DATx" }, - { "id": "dav", "symbol": "dav", "name": "DAV Network" }, - { "id": "davidcoin", "symbol": "dc", "name": "DavidCoin" }, - { "id": "davinci-coin", "symbol": "dac", "name": "Davinci Coin" }, - { "id": "davinci-token", "symbol": "vinci", "name": "DaVinci Token" }, - { - "id": "davis-cup-fan-token", - "symbol": "DAVIS", - "name": "Davis Cup Fan Token" - }, - { "id": "dawg", "symbol": "dawg", "name": "DAWG" }, - { "id": "dawn-of-gods", "symbol": "dago", "name": "Dawn Of Gods" }, - { "id": "dawn-protocol", "symbol": "dawn", "name": "Dawn Protocol" }, - { "id": "dawn-wars", "symbol": "dw", "name": "Dawn Wars" }, - { "id": "day-by-day", "symbol": "dbd", "name": "Day By Day" }, - { "id": "day-of-defeat", "symbol": "dod", "name": "Day Of Defeat" }, - { "id": "dbquest", "symbol": "dbq", "name": "DBQuest" }, - { "id": "dbx-2", "symbol": "dbx", "name": "DBX" }, - { "id": "dcd-ecosystem", "symbol": "dcd", "name": "DCD Ecosystem" }, - { "id": "dchess-king", "symbol": "king", "name": "DChess King" }, - { "id": "dcoin-token", "symbol": "dt", "name": "Dcoin Token" }, - { "id": "d-community", "symbol": "dili", "name": "D Community" }, - { "id": "dctdao", "symbol": "dctd", "name": "DCTDAO" }, - { "id": "dddx-protocol", "symbol": "dddx", "name": "DDDX Protocol" }, - { "id": "ddmcoin", "symbol": "ddm", "name": "DDMCoin" }, - { "id": "dds-store", "symbol": "dds", "name": "DDS.Store" }, - { "id": "dead-knight", "symbol": "dkm", "name": "Dead Knight" }, - { "id": "deapcoin", "symbol": "dep", "name": "DEAPCOIN" }, - { "id": "deathroad", "symbol": "drace", "name": "DeathRoad" }, - { "id": "death-token", "symbol": "death", "name": "Death Token" }, - { "id": "debio-network", "symbol": "dbio", "name": "DeBio Network" }, - { "id": "debitum-network", "symbol": "deb", "name": "Debitum Network" }, - { "id": "deblox", "symbol": "dgs", "name": "Deblox" }, - { "id": "decentbet", "symbol": "dbet", "name": "DecentBet" }, - { "id": "decent-database", "symbol": "decent", "name": "DECENT Database" }, - { "id": "decentr", "symbol": "dec", "name": "Decentr" }, - { "id": "decentrahub-coin", "symbol": "dcntr", "name": "Decentrahub Coin" }, - { "id": "decentraland", "symbol": "mana", "name": "Decentraland" }, - { - "id": "decentraland-wormhole", - "symbol": "mana", - "name": "Decentraland (Wormhole)" - }, - { "id": "decentral-games", "symbol": "dg", "name": "Decentral Games" }, - { - "id": "decentral-games-governance", - "symbol": "xdg", - "name": "Decentral Games Governance" - }, - { - "id": "decentral-games-ice", - "symbol": "ice", - "name": "Decentral Games ICE" - }, - { - "id": "decentral-games-old", - "symbol": "dg", - "name": "Decentral Games (Old)" - }, - { "id": "decentralized-advertising", "symbol": "dad", "name": "DAD" }, - { - "id": "decentralized-asset-trading-platform", - "symbol": "datp", - "name": "Decentralized Asset Trading Platform" - }, - { - "id": "decentralized-autonomous-organization", - "symbol": "DAO", - "name": "Decentralized Autonomous Organization" - }, - { - "id": "decentralized-community-investment-protocol", - "symbol": "dcip", - "name": "Decentralized Community Investment Protocol" - }, - { - "id": "decentralized-currency-assets", - "symbol": "dca", - "name": "Decentralize Currency" - }, - { "id": "decentralized-etf", "symbol": "detf", "name": "Decentralized ETF" }, - { - "id": "decentralized-machine-learning", - "symbol": "dml", - "name": "Decentralized Machine Learning Protocol" - }, - { - "id": "decentralized-mining-exchange", - "symbol": "dmc", - "name": "Decentralized Mining Exchange" - }, - { - "id": "decentralized-nations", - "symbol": "dena", - "name": "Decentralized Nations" - }, - { - "id": "decentralized-universal-basic-income", - "symbol": "dubi", - "name": "Decentralized Universal Basic Income" - }, - { "id": "decentralized-usd", "symbol": "DUSD", "name": "Decentralized USD" }, - { - "id": "decentralized-vulnerability-platform", - "symbol": "dvp", - "name": "Decentralized Vulnerability Platform" - }, - { "id": "decentra-lotto", "symbol": "delo", "name": "Decentra-Lotto" }, - { "id": "decentralway", "symbol": "dcw", "name": "Decentralway" }, - { "id": "decentraweb", "symbol": "dweb", "name": "DecentraWeb" }, - { "id": "decentsol", "symbol": "dsol", "name": "DecentSol" }, - { "id": "decenturion", "symbol": "dcnt", "name": "Decenturion" }, - { "id": "dechart", "symbol": "dch", "name": "DeChart" }, - { "id": "decimal", "symbol": "del", "name": "Decimal" }, - { "id": "decimated", "symbol": "dio", "name": "Decimated" }, - { "id": "decoin", "symbol": "dtep", "name": "Decoin" }, - { "id": "decred", "symbol": "dcr", "name": "Decred" }, - { "id": "decredit", "symbol": "cdtc", "name": "DeCredit" }, - { "id": "decubate", "symbol": "dcb", "name": "Decubate" }, - { "id": "decurian", "symbol": "ecu", "name": "Decurian" }, - { "id": "decus", "symbol": "sats", "name": "DeCus Satoshi" }, - { "id": "deepbrain-chain", "symbol": "dbc", "name": "DeepBrain Chain" }, - { "id": "deepcloud-ai", "symbol": "deep", "name": "DeepCloud AI" }, - { "id": "deeper-network", "symbol": "dpr", "name": "Deeper Network" }, - { "id": "deeponion", "symbol": "onion", "name": "DeepOnion" }, - { "id": "deepspace", "symbol": "dps", "name": "DEEPSPACE" }, - { "id": "deepspace-token", "symbol": "dxo", "name": "DeepSpace Token" }, - { "id": "deesse", "symbol": "love", "name": "Deesse" }, - { "id": "deez-nuts", "symbol": "deeznuts", "name": "Deez Nuts" }, - { "id": "defactor", "symbol": "factr", "name": "Defactor" }, - { "id": "defender-of-doge", "symbol": "dod", "name": "Defender of Doge" }, - { "id": "defhold", "symbol": "defo", "name": "DefHold" }, - { "id": "defi11", "symbol": "d11", "name": "DeFi11" }, - { "id": "defiai", "symbol": "dfai", "name": "DeFiAI" }, - { "id": "defiant", "symbol": "defi", "name": "DeFiant" }, - { "id": "defiat", "symbol": "dft", "name": "DeFiat" }, - { "id": "defiato", "symbol": "dfiat", "name": "DeFiato" }, - { "id": "defibank-money", "symbol": "dfm", "name": "DefiBank.Money" }, - { - "id": "defi-bank-tycoon", - "symbol": "dbtycoon", - "name": "DeFi Bank Tycoon" - }, - { "id": "defibay", "symbol": "DBay", "name": "DefiBay" }, - { "id": "defibet", "symbol": "$dbet", "name": "DefiBet" }, - { "id": "defi-bids", "symbol": "bid", "name": "DeFi Bids" }, - { "id": "defibox", "symbol": "box", "name": "DefiBox" }, - { "id": "defi-ch", "symbol": "dfch", "name": "DeFi.ch" }, - { "id": "defichain", "symbol": "dfi", "name": "DeFiChain" }, - { "id": "defi-city", "symbol": "dfc", "name": "DeFi City" }, - { "id": "deficliq", "symbol": "cliq", "name": "DefiCliq" }, - { "id": "defi-coin", "symbol": "defc", "name": "DeFi Coin" }, - { "id": "deficonnect", "symbol": "dfc", "name": "DefiConnect" }, - { "id": "defi-degen-land", "symbol": "ddl", "name": "DeFi Degen Land" }, - { "id": "defido", "symbol": "defido", "name": "DeFido" }, - { "id": "defidollar", "symbol": "dusd", "name": "DefiDollar" }, - { "id": "defidollar-dao", "symbol": "dfd", "name": "DefiDollar DAO" }, - { "id": "defi-empire-gold", "symbol": "deg", "name": "Defi Empire Gold" }, - { "id": "defi-for-you", "symbol": "dfy", "name": "Defi For You" }, - { "id": "defi-gold", "symbol": "dfgl", "name": "DeFi Gold" }, - { - "id": "defihelper-governance-token", - "symbol": "dfh", - "name": "DeFiHelper Governance Token" - }, - { "id": "defi-holdings", "symbol": "dhold", "name": "DeFi Holdings" }, - { "id": "defihorse", "symbol": "dfh", "name": "DeFiHorse" }, - { "id": "defi-hunters-dao", "symbol": "ddao", "name": "DeFi Hunters DAO" }, - { "id": "defiking", "symbol": "dfk", "name": "DefiKing" }, - { "id": "defi-kingdoms", "symbol": "jewel", "name": "DeFi Kingdoms" }, - { - "id": "defi-kingdoms-crystal", - "symbol": "crystal", - "name": "DeFi Kingdoms Crystal" - }, - { "id": "defil", "symbol": "dfl", "name": "DeFIL" }, - { "id": "defi-land", "symbol": "dfl", "name": "DeFi Land" }, - { "id": "defi-launch", "symbol": "dlaunch", "name": "DeFi Launch" }, - { "id": "defily", "symbol": "dfl", "name": "Defily" }, - { "id": "defina-finance", "symbol": "fina", "name": "Defina Finance" }, - { "id": "define", "symbol": "dfa", "name": "DeFine" }, - { "id": "definer", "symbol": "fin", "name": "DeFiner" }, - { "id": "definitex", "symbol": "dfx", "name": "Definitex" }, - { "id": "definity", "symbol": "defx", "name": "DeFinity" }, - { "id": "definix", "symbol": "finix", "name": "Definix" }, - { "id": "defi-on-mcw", "symbol": "dfm", "name": "DeFi on MCW" }, - { "id": "defipie", "symbol": "PIE", "name": "DeFiPie" }, - { "id": "defiplaza", "symbol": "dfp2", "name": "DefiPlaza" }, - { "id": "defipulse-index", "symbol": "dpi", "name": "DeFi Pulse Index" }, - { "id": "defire", "symbol": "cwap", "name": "DeFIRE" }, - { "id": "defis", "symbol": "xgm", "name": "Defis" }, - { "id": "defiscale", "symbol": "dfc", "name": "DeFiScale" }, - { - "id": "defi-shopping-stake", - "symbol": "dss", - "name": "Defi Shopping Stake" - }, - { "id": "defis-network", "symbol": "dfs", "name": "Defis Network" }, - { "id": "defisportscoin", "symbol": "dsc", "name": "DefiSportsCoin" }, - { "id": "defistarter", "symbol": "dfi", "name": "DfiStarter" }, - { "id": "defi-stoa", "symbol": "sta", "name": "Defi STOA" }, - { "id": "defit", "symbol": "defit", "name": "Digital Fitness" }, - { - "id": "defi-top-5-tokens-index", - "symbol": "defi5", - "name": "DEFI Top 5 Index" - }, - { "id": "defiville-island", "symbol": "isla", "name": "DefiVille Island" }, - { "id": "defi-warrior", "symbol": "fiwa", "name": "Defi Warrior" }, - { - "id": "defi-yield-protocol", - "symbol": "dyp", - "name": "DeFi Yield Protocol" - }, - { "id": "deflect", "symbol": "deflct", "name": "Deflect" }, - { "id": "defrost-finance", "symbol": "melt", "name": "Defrost Finance" }, - { "id": "defycoinv2", "symbol": "defy", "name": "DefyCoinV2" }, - { "id": "defydefi", "symbol": "defy", "name": "DefyDefi" }, - { "id": "defyswap-finance", "symbol": "dfy", "name": "DefySwap Finance" }, - { "id": "degate", "symbol": "dg", "name": "DeGate" }, - { "id": "degem", "symbol": "dgm", "name": "DegemV2" }, - { "id": "degen", "symbol": "degn", "name": "Degen" }, - { "id": "degen-ape-club", "symbol": "dac", "name": "Degen Ape Club" }, - { "id": "degen-arts", "symbol": "dac", "name": "Degen Arts" }, - { "id": "degenerate-money", "symbol": "degenr", "name": "Degenerate Money" }, - { "id": "degenerator", "symbol": "meme", "name": "Meme" }, - { "id": "degen-finance", "symbol": "degen", "name": "Degen Finance" }, - { "id": "degen-index", "symbol": "degen", "name": "DEGEN Index" }, - { - "id": "degen-protocol-token", - "symbol": "sh33p", - "name": "Degen Protocol Token" - }, - { "id": "degenvc", "symbol": "dgvc", "name": "DegenVC" }, - { "id": "degis", "symbol": "deg", "name": "Degis" }, - { "id": "dego-finance", "symbol": "dego", "name": "Dego Finance" }, - { - "id": "degree-crypto-token", - "symbol": "dct", - "name": "Degree Crypto Token" - }, - { "id": "dehealth", "symbol": "dhlt", "name": "DeHealth" }, - { - "id": "dehero-community-token", - "symbol": "heroes", - "name": "Dehero Community Token" - }, - { "id": "dehive", "symbol": "dhv", "name": "DeHive" }, - { "id": "dehorizon", "symbol": "devt", "name": "DeHorizon" }, - { "id": "dehr-network", "symbol": "dhr", "name": "DeHR Network" }, - { "id": "dehub", "symbol": "dehub", "name": "DeHub" }, - { "id": "deimos-token", "symbol": "dio", "name": "Deimos Token" }, - { "id": "deipool", "symbol": "dip", "name": "Deipool" }, - { "id": "dei-token", "symbol": "dei", "name": "DEI Token" }, - { "id": "dekbox", "symbol": "dek", "name": "DekBox" }, - { "id": "deku-inu", "symbol": "DEKU", "name": "Deku Inu" }, - { "id": "deligence", "symbol": "ira", "name": "Diligence" }, - { "id": "deli-of-thrones", "symbol": "dotx", "name": "DeFi of Thrones" }, - { "id": "delion", "symbol": "dln", "name": "Delion" }, - { "id": "deliq", "symbol": "dlq", "name": "Deliq" }, - { "id": "delos-defi", "symbol": "delos", "name": "Delos Defi" }, - { "id": "delphy", "symbol": "dpy", "name": "Delphy" }, - { "id": "deltachain", "symbol": "delta", "name": "DeltaChain" }, - { - "id": "delta-exchange-token", - "symbol": "deto", - "name": "Delta Exchange Token" - }, - { "id": "deltafi", "symbol": "delfi", "name": "DeltaFi" }, - { "id": "delta-financial", "symbol": "delta", "name": "Delta Financial" }, - { "id": "deltaflip", "symbol": "DeltaF", "name": "DeltaFlip" }, - { "id": "deltahub-community", "symbol": "DHC", "name": "DeltaHub Community" }, - { "id": "delta-theta", "symbol": "dlta", "name": "delta.theta" }, - { "id": "demeter-usd", "symbol": "dusd", "name": "Demeter USD" }, - { "id": "demodyfi", "symbol": "dmod", "name": "Demodyfi" }, - { "id": "demole", "symbol": "dmlg", "name": "Demole" }, - { "id": "demonic-doge", "symbol": "dmd", "name": "Demonic Doge" }, - { "id": "denarius", "symbol": "d", "name": "Denarius" }, - { "id": "dendomains", "symbol": "ddn", "name": "Den Domains" }, - { - "id": "denizlispor-fan-token", - "symbol": "dnz", - "name": "Denizlispor Fan Token" - }, - { "id": "dent", "symbol": "dent", "name": "Dent" }, - { "id": "dentacoin", "symbol": "dcn", "name": "Dentacoin" }, - { "id": "dentrocoin", "symbol": "dentro", "name": "Dentrocoin" }, - { "id": "deonex-token", "symbol": "don", "name": "DEONEX Token" }, - { "id": "depay", "symbol": "depay", "name": "DePay" }, - { "id": "depo", "symbol": "depo", "name": "Depo" }, - { "id": "depocket", "symbol": "depo", "name": "DePocket" }, - { "id": "depth-token", "symbol": "dep", "name": "Depth Token" }, - { "id": "dequant", "symbol": "deq", "name": "Dequant" }, - { "id": "derace", "symbol": "derc", "name": "DeRace" }, - { "id": "deracoin", "symbol": "drc", "name": "Deracoin" }, - { "id": "derify-protocol", "symbol": "drf", "name": "Derify Protocol" }, - { "id": "deri-protocol", "symbol": "deri", "name": "Deri Protocol" }, - { "id": "derivadao", "symbol": "ddx", "name": "DerivaDAO" }, - { "id": "derived", "symbol": "dvdx", "name": "Derived" }, - { "id": "derivex", "symbol": "dvx", "name": "Derivex" }, - { "id": "dero", "symbol": "dero", "name": "Dero" }, - { "id": "desirenft", "symbol": "desire", "name": "DesireNFT" }, - { "id": "desmos", "symbol": "dsm", "name": "Desmos" }, - { "id": "despace", "symbol": "dse", "name": "Despace" }, - { "id": "despace-protocol", "symbol": "des", "name": "DeSpace Protocol" }, - { "id": "destorage", "symbol": "ds", "name": "DeStorage" }, - { "id": "deswap", "symbol": "daw", "name": "Deswap" }, - { "id": "dether", "symbol": "DTH", "name": "Dether" }, - { "id": "detik", "symbol": "dtk", "name": "DETIK" }, - { "id": "deus-finance-2", "symbol": "deus", "name": "DEUS Finance" }, - { "id": "deutsche-emark", "symbol": "dem", "name": "Deutsche eMark" }, - { "id": "deuxpad", "symbol": "deux", "name": "DeuxPad" }, - { "id": "deva-token", "symbol": "deva", "name": "DEVA Token" }, - { "id": "devault", "symbol": "dvt", "name": "DeVault" }, - { "id": "devery", "symbol": "eve", "name": "Devery" }, - { "id": "devia8", "symbol": "devia8", "name": "Devia8" }, - { "id": "deviantcoin", "symbol": "dev", "name": "Deviant Coin" }, - { "id": "devikins", "symbol": "dvk", "name": "Devikins" }, - { "id": "devil-finance", "symbol": "devil", "name": "Devil Finance" }, - { "id": "devil-token", "symbol": "DEVL", "name": "Devil Token" }, - { - "id": "devious-licks-gold", - "symbol": "dgold", - "name": "Devious Licks Gold" - }, - { "id": "devita-global", "symbol": "life", "name": "DEVITA" }, - { "id": "devium", "symbol": "devi", "name": "Devium" }, - { "id": "devolution", "symbol": "devo", "name": "DeVolution" }, - { "id": "devour", "symbol": "restaurants", "name": "Devour" }, - { "id": "dev-protocol", "symbol": "dev", "name": "Dev Protocol" }, - { "id": "dexa-coin", "symbol": "dexa", "name": "DEXA COIN" }, - { "id": "dexalot", "symbol": "alot", "name": "Dexalot" }, - { "id": "dexbrowser", "symbol": "bro", "name": "DexBrowser" }, - { "id": "dexchain", "symbol": "dxc", "name": "Dexchain" }, - { "id": "dexe", "symbol": "dexe", "name": "DeXe" }, - { "id": "dexfin", "symbol": "dxf", "name": "Dexfin" }, - { "id": "dexfolio", "symbol": "dexf", "name": "Dexfolio" }, - { "id": "dex-game", "symbol": "dxgm", "name": "DexGame" }, - { "id": "dexigas", "symbol": "dxg", "name": "Dexigas" }, - { "id": "dexioprotocol", "symbol": "Dexi", "name": "Dexioprotocol" }, - { "id": "dexira", "symbol": "dex", "name": "dexIRA" }, - { "id": "dexit-finance", "symbol": "dxt", "name": "Dexit Network" }, - { "id": "dexkit", "symbol": "kit", "name": "DexKit" }, - { "id": "dexlab", "symbol": "dxl", "name": "Dexlab" }, - { "id": "dexmex", "symbol": "dexm", "name": "Dexmex" }, - { "id": "dexpad", "symbol": "dxp", "name": "DexPad" }, - { "id": "dexpools", "symbol": "dxp", "name": "Dexpools" }, - { "id": "dexscrow", "symbol": "dxw", "name": "Dexscrow" }, - { "id": "dexsport", "symbol": "desu", "name": "Dexsport" }, - { "id": "dextf", "symbol": "dextf", "name": "Domani Protocol" }, - { - "id": "dextoken-governance", - "symbol": "dexg", - "name": "Dextoken Governance" - }, - { "id": "dextools", "symbol": "dext", "name": "DexTools" }, - { "id": "dex-trade-coin", "symbol": "dxc", "name": "Dex-Trade Coin" }, - { "id": "dextro", "symbol": "dxo", "name": "Dextro" }, - { "id": "dfe-finance", "symbol": "dfe", "name": "DFE.Finance" }, - { "id": "dfinance", "symbol": "xfi", "name": "Dfinance" }, - { "id": "dfohub", "symbol": "buidl", "name": "dfohub" }, - { "id": "dforce-token", "symbol": "df", "name": "dForce Token" }, - { "id": "dfs-mafia", "symbol": "dfsm", "name": "DFS MAFIA" }, - { "id": "dfsocial", "symbol": "dfsocial", "name": "DFSocial (Old)" }, - { "id": "dfsocial-gaming-2", "symbol": "dfsg", "name": "DFSocial Gaming" }, - { "id": "dfund", "symbol": "dfnd", "name": "dFund" }, - { "id": "dfuture", "symbol": "dft", "name": "dfuture" }, - { "id": "dfx-finance", "symbol": "dfx", "name": "DFX Finance" }, - { "id": "dfyn-network", "symbol": "dfyn", "name": "Dfyn Network" }, - { "id": "dgld", "symbol": "dgld", "name": "DGLD" }, - { "id": "dgpayment", "symbol": "dgp", "name": "DGPayment" }, - { "id": "dhabicoin", "symbol": "dbc", "name": "Dhabicoin" }, - { "id": "dhealth", "symbol": "dhp", "name": "dHealth" }, - { "id": "dhedge-dao", "symbol": "dht", "name": "dHEDGE DAO" }, - { - "id": "dhedge-stablecoin-yield", - "symbol": "dusd", - "name": "dHEDGE Stablecoin Yield" - }, - { "id": "dhedge-top-index", "symbol": "dtop", "name": "dHEDGE Top Index" }, - { "id": "dia-data", "symbol": "DIA", "name": "DIA" }, - { "id": "diamond", "symbol": "dmd", "name": "Diamond" }, - { "id": "diamond-boyz-coin", "symbol": "dbz", "name": "Diamond Boyz Coin" }, - { "id": "diamond-coin", "symbol": "diamond", "name": "Diamond Coin" }, - { "id": "diamonddao", "symbol": "dmnd", "name": "DiamondDAO" }, - { "id": "diamond-dnd", "symbol": "dnd", "name": "Diamond DND" }, - { - "id": "diamond-hands-token", - "symbol": "dhc", - "name": "Diamond Hands Token" - }, - { "id": "diamondhold", "symbol": "dhold", "name": "DiamondHold" }, - { "id": "diamond-love", "symbol": "love", "name": "Diamond Love" }, - { - "id": "diamond-platform-token", - "symbol": "dpt", - "name": "Diamond Platform Token" - }, - { "id": "diamondq", "symbol": "DIQ", "name": "DiamondQ" }, - { "id": "diamondshiba", "symbol": "ds$", "name": "DiamondShiba" }, - { "id": "diamond-whitex", "symbol": "DWHX", "name": "Diamond Whitex" }, - { "id": "diamond-xrpl", "symbol": "diamond", "name": "Diamond XRPL" }, - { "id": "diarrheacoin", "symbol": "DIAH", "name": "DiarrheaCoin" }, - { "id": "dibs-money", "symbol": "dibs", "name": "Dibs Money" }, - { "id": "dibs-share", "symbol": "dshare", "name": "Dibs Share" }, - { "id": "dice-inu", "symbol": "dice", "name": "Dice Inu" }, - { "id": "didcoin", "symbol": "did", "name": "Didcoin" }, - { "id": "diemlibre", "symbol": "dlb", "name": "DiemLibre" }, - { "id": "difo-network", "symbol": "dfn", "name": "Difo Network" }, - { "id": "dify-finance", "symbol": "yfiii", "name": "Dify.Finance" }, - { "id": "dig-chain", "symbol": "dig", "name": "Dig Chain" }, - { "id": "digg", "symbol": "digg", "name": "DIGG" }, - { "id": "digible", "symbol": "digi", "name": "Digible" }, - { "id": "digibyte", "symbol": "dgb", "name": "DigiByte" }, - { "id": "digichain", "symbol": "digichain", "name": "Digichain Coin" }, - { "id": "digicol-token", "symbol": "dgcl", "name": "DigiCol Token" }, - { "id": "digi-dinar", "symbol": "ddr", "name": "Digi Dinar" }, - { - "id": "digidinar-stabletoken", - "symbol": "ddrst", - "name": "DigiDinar StableToken" - }, - { "id": "digidinar-token", "symbol": "ddrt", "name": "DigiDinar Token" }, - { "id": "digifinextoken", "symbol": "dft", "name": "DigiFinexToken" }, - { "id": "digifit", "symbol": "DGI", "name": "DIGIFIT" }, - { "id": "digimetaverse", "symbol": "dgmv", "name": "DigiMetaverse" }, - { "id": "digimoney", "symbol": "dgm", "name": "DigiMoney" }, - { "id": "diginu", "symbol": "diginu", "name": "DIGINU" }, - { "id": "digipharm", "symbol": "dph", "name": "Digipharm" }, - { - "id": "digital-bank-of-africa", - "symbol": "dba", - "name": "Digital Bank of Africa" - }, - { "id": "digitalbits", "symbol": "xdb", "name": "DigitalBits" }, - { "id": "digitalcoin", "symbol": "dgc", "name": "Digitalcoin" }, - { "id": "digitaldollar", "symbol": "dusd", "name": "DigitalDollar" }, - { "id": "digital-fantasy-sports", "symbol": "dfs", "name": "Fantasy Sports" }, - { - "id": "digital-financial-exchange", - "symbol": "difx", - "name": "Digital Financial Exchange" - }, - { "id": "digital-money-bits", "symbol": "dmb", "name": "Digital Money Bits" }, - { "id": "digitalnote", "symbol": "xdn", "name": "DigitalNote" }, - { "id": "digitalprice", "symbol": "dp", "name": "DigitalPrice" }, - { "id": "digital-rand", "symbol": "dzar", "name": "Digital Rand" }, - { - "id": "digital-reserve-currency", - "symbol": "drc", - "name": "Digital Reserve Currency" - }, - { - "id": "digital-swis-franc", - "symbol": "dsfr", - "name": "Digital Swiss Franc" - }, - { "id": "digital-ticks", "symbol": "dtx", "name": "Digital Ticks" }, - { - "id": "digitex-futures-exchange", - "symbol": "dgtx", - "name": "Digitex Token" - }, - { "id": "digits-dao", "symbol": "digits", "name": "Digits DAO" }, - { "id": "digiwill", "symbol": "dgw", "name": "Digiwill" }, - { "id": "digixdao", "symbol": "dgd", "name": "DigixDAO" }, - { "id": "digix-gold", "symbol": "dgx", "name": "Digix Gold" }, - { "id": "dike", "symbol": "dike", "name": "Dike" }, - { "id": "dimecoin", "symbol": "dime", "name": "Dimecoin" }, - { "id": "diminutive-coin", "symbol": "dimi", "name": "Diminutive Coin" }, - { "id": "dimitra", "symbol": "dmtr", "name": "Dimitra" }, - { "id": "dimsum", "symbol": "dms", "name": "DimSum" }, - { "id": "dina", "symbol": "dina", "name": "Dina" }, - { - "id": "dinamo-zagreb-fan-token", - "symbol": "dzg", - "name": "Dinamo Zagreb Fan Token" - }, - { "id": "dinastycoin", "symbol": "dcy", "name": "Dinastycoin" }, - { "id": "dinero", "symbol": "din", "name": "Dinero" }, - { "id": "dinger-token", "symbol": "dinger", "name": "Dinger Token" }, - { "id": "dingocoin", "symbol": "dingo", "name": "Dingocoin" }, - { "id": "dingo-token", "symbol": "dingo", "name": "Dingo Token" }, - { "id": "dink-donk", "symbol": "dink", "name": "Dink Doink" }, - { "id": "dino", "symbol": "dino", "name": "Dino" }, - { "id": "dinoegg", "symbol": "dinoegg", "name": "DINOEGG" }, - { "id": "dino-exchange", "symbol": "dino", "name": "Dino Exchange" }, - { "id": "dinoland", "symbol": "dnl", "name": "Dinoland" }, - { "id": "dinopark", "symbol": "dinop", "name": "DinoPark" }, - { "id": "dinosaureggs", "symbol": "dsg", "name": "Dinosaur Eggs Token" }, - { "id": "dinoswap", "symbol": "dino", "name": "DinoSwap" }, - { "id": "dinox", "symbol": "dnxc", "name": "DinoX" }, - { "id": "dint-token", "symbol": "dint", "name": "DINT Token" }, - { "id": "dionpay", "symbol": "dion", "name": "Dionpay" }, - { "id": "dios-finance", "symbol": "dios", "name": "Dios Finance" }, - { "id": "dipper", "symbol": "dip", "name": "Dipper" }, - { "id": "dipper-network", "symbol": "dip", "name": "Dipper Network" }, - { "id": "dirham", "symbol": "dah", "name": "Dirham" }, - { "id": "dirham-crypto", "symbol": "dhs", "name": "Dirham Crypto" }, - { "id": "dirty-finance", "symbol": "dirty", "name": "Dirty Finance" }, - { "id": "disbalancer", "symbol": "ddos", "name": "disBalancer" }, - { "id": "disco-burn-token", "symbol": "dbt", "name": "Disco Burn Token" }, - { "id": "district0x", "symbol": "dnt", "name": "district0x" }, - { "id": "distx", "symbol": "distx", "name": "DistX" }, - { "id": "ditto", "symbol": "ditto", "name": "Ditto" }, - { "id": "dittoinu", "symbol": "dittoinu", "name": "DittoInu" }, - { - "id": "divergence-protocol", - "symbol": "diver", - "name": "Divergence Protocol" - }, - { "id": "diversifi", "symbol": "dfi", "name": "DiversiFi" }, - { "id": "divi", "symbol": "divi", "name": "Divi" }, - { "id": "divine-dao", "symbol": "divine", "name": "Divine DAO" }, - { "id": "diviner", "symbol": "div", "name": "Diviner" }, - { "id": "diviner-protocol", "symbol": "dpt", "name": "Diviner Protocol" }, - { "id": "divs", "symbol": "divs", "name": "Divs" }, - { "id": "dixt-finance", "symbol": "dixt", "name": "Dixt Finance" }, - { "id": "diyarbekirspor", "symbol": "diyar", "name": "Diyarbekirspor" }, - { "id": "dkargo", "symbol": "dka", "name": "dKargo" }, - { "id": "dkey-bank", "symbol": "dkey", "name": "DKEY Bank" }, - { "id": "dlp-duck-token", "symbol": "duck", "name": "DLP Duck Token" }, - { "id": "dmarket", "symbol": "dmt", "name": "DMarket" }, - { "id": "dmd", "symbol": "dmd", "name": "DMD" }, - { "id": "dmme-app", "symbol": "dmme", "name": "DMme" }, - { "id": "dmm-governance", "symbol": "dmg", "name": "DMM: Governance" }, - { "id": "dmt-token", "symbol": "dmt", "name": "DMT Token" }, - { "id": "dmz-token", "symbol": "dmz", "name": "DMZ Token" }, - { "id": "dna-dollar", "symbol": "dna", "name": "DNA Dollar" }, - { "id": "dna-genesys", "symbol": "dna", "name": "DNA Genesys" }, - { "id": "dna-share", "symbol": "dshare", "name": "DNA Share" }, - { "id": "dnaxcat", "symbol": "dxct", "name": "DNAxCAT" }, - { "id": "dnd-metaverse", "symbol": "dndb", "name": "DnD Metaverse" }, - { "id": "dnft-protocol", "symbol": "dnf", "name": "DNFT Protocol" }, - { "id": "doaibu", "symbol": "doa", "name": "Doaibu" }, - { "id": "dobermann", "symbol": "dobe", "name": "Dobermann" }, - { "id": "dock", "symbol": "dock", "name": "Dock" }, - { "id": "docuchain", "symbol": "dcct", "name": "DocuChain" }, - { "id": "documentchain", "symbol": "dms", "name": "Documentchain" }, - { "id": "dodo", "symbol": "dodo", "name": "DODO" }, - { "id": "dodreamchain", "symbol": "drm", "name": "DoDreamChain" }, - { "id": "doex", "symbol": "doex", "name": "DOEX" }, - { "id": "dofi", "symbol": "doo", "name": "Dofi" }, - { "id": "dog", "symbol": "dog", "name": "Dog [OLD]" }, - { "id": "dog-2", "symbol": "dog", "name": "Dog" }, - { "id": "dogami", "symbol": "doga", "name": "Dogami" }, - { "id": "dog-boss", "symbol": "dogboss", "name": "Dog Boss" }, - { "id": "dog-club-token", "symbol": "dclub", "name": "Dog Club Token" }, - { "id": "dog-collar", "symbol": "COLLAR", "name": "Dog Collar" }, - { "id": "dogdeficoin", "symbol": "dogdefi", "name": "DogDeFiCoin" }, - { - "id": "doge-1-mission-to-the-moon", - "symbol": "doge-1", - "name": "Doge-1 Mission to the moon" - }, - { "id": "doge-alliance", "symbol": "dogeally", "name": "Doge Alliance" }, - { "id": "doge-army-token", "symbol": "dgat", "name": "Doge Army Token" }, - { "id": "dogebnb-org", "symbol": "dogebnb", "name": "DogeBNB.org" }, - { "id": "dogebonk", "symbol": "dobo", "name": "DogeBonk" }, - { "id": "dogecash", "symbol": "dogec", "name": "DogeCash" }, - { "id": "doge-cat", "symbol": "dogecat", "name": "DOGE-CAT" }, - { "id": "dogecoin", "symbol": "doge", "name": "Dogecoin" }, - { "id": "dogecoin-2", "symbol": "doge2", "name": "Dogecoin 2.0" }, - { "id": "dogecola", "symbol": "dogecola", "name": "DogeCola" }, - { "id": "dogecolony", "symbol": "dogeco", "name": "Dogecolony" }, - { "id": "dogecube", "symbol": "DogeCube", "name": "DogeCube" }, - { "id": "dogedao", "symbol": "dogedao", "name": "DogeDao" }, - { "id": "doge-dash", "symbol": "DogeDash", "name": "Doge Dash" }, - { "id": "dogedead", "symbol": "dogedead", "name": "DogeDead" }, - { "id": "dogedealer", "symbol": "dogedealer", "name": "DogeDealer" }, - { "id": "dogedi", "symbol": "dogedi", "name": "DOGEDI" }, - { "id": "dogefather", "symbol": "dogefather", "name": "Dogefather" }, - { "id": "dogefather-token", "symbol": "father", "name": "DogeFather Token" }, - { "id": "dogefi", "symbol": "dogefi", "name": "DogeFi" }, - { "id": "doge-floki-coin", "symbol": "dofi", "name": "Doge Floki Coin" }, - { "id": "dogefood", "symbol": "dogefood", "name": "DogeFood" }, - { "id": "dogegamer", "symbol": "dga", "name": "Doge Gamer" }, - { "id": "dogegayson", "symbol": "GOGE", "name": "DogeGaySon" }, - { "id": "dogegf", "symbol": "dogegf", "name": "DogeGF" }, - { "id": "doge-in-shiba", "symbol": "dgshib", "name": "Doge in Shiba" }, - { "id": "dogeking", "symbol": "dogeking", "name": "DogeKing" }, - { "id": "doge-king", "symbol": "dogek", "name": "Doge King" }, - { "id": "dogekongzilla", "symbol": "dogekongzilla", "name": "DogeKongZilla" }, - { "id": "dogekwon-terra", "symbol": "dkwon", "name": "DogeKwon Terra" }, - { "id": "dogelana", "symbol": "dgln", "name": "Dogelana" }, - { "id": "dogelon-mars", "symbol": "elon", "name": "Dogelon Mars" }, - { - "id": "dogelon-mars-wormhole", - "symbol": "elon", - "name": "Dogelon Mars (Wormhole)" - }, - { "id": "doge-lumens", "symbol": "dxlm", "name": "Doge Lumens" }, - { "id": "dogeman", "symbol": "dgman", "name": "DogeMan" }, - { "id": "dogemania", "symbol": "dogemania", "name": "DogeMania" }, - { "id": "dogematic", "symbol": "dm", "name": "Dogematic" }, - { "id": "dogemetaverse", "symbol": "dogemeta", "name": "Dogemetaverse" }, - { "id": "dogemon-go", "symbol": "dogo", "name": "DogemonGo" }, - { "id": "dogemoon", "symbol": "dogemoon", "name": "Dogemoon" }, - { - "id": "doge-ninja-samurai", - "symbol": "dinja", - "name": "DOGE NINJA SAMURAI" - }, - { "id": "dogeon", "symbol": "don", "name": "Dogeon" }, - { "id": "dogepepsi", "symbol": "DOGEPEPSI", "name": "DogePepsi" }, - { "id": "doge-protocol", "symbol": "dogep", "name": "Doge Protocol" }, - { "id": "doge-raca", "symbol": "doca", "name": "Doge Raca" }, - { "id": "doge-rise-up", "symbol": "DOGERISEUP", "name": "Doge Rise Up" }, - { "id": "dogerocket", "symbol": "dogerkt", "name": "DogeRocket" }, - { "id": "doge-run", "symbol": "drun", "name": "Doge Run" }, - { "id": "doge-spacex", "symbol": "dogesx", "name": "Doge SpaceX" }, - { - "id": "doges-spirited-howling-castle-game", - "symbol": "miyazaki", - "name": "Doges Spirited Howling Castle Game" - }, - { "id": "doge-strap", "symbol": "drap", "name": "Doge Strap" }, - { "id": "dogestribute", "symbol": "dgstb", "name": "Dogestribute" }, - { "id": "dogeswap", "symbol": "doges", "name": "Dogeswap" }, - { "id": "dogetama", "symbol": "dogetama", "name": "Dogetama" }, - { "id": "dogethefloki", "symbol": "dtf", "name": "DogeTheFloki" }, - { "id": "doge-token", "symbol": "doget", "name": "Doge Token" }, - { "id": "doge-universe", "symbol": "SPACExDOGE", "name": "Doge Universe" }, - { "id": "dogevillage", "symbol": "dogev", "name": "DogeVillage" }, - { "id": "dogeville", "symbol": "dville", "name": "DogeVille" }, - { "id": "dogewarrior", "symbol": "dwr", "name": "DogeWarrior" }, - { "id": "dogewhale", "symbol": "dogewhale", "name": "Dogewhale" }, - { "id": "dogewhisky", "symbol": "DogeWhisky", "name": "Dogewhisky" }, - { "id": "doge-x", "symbol": "DOGEX", "name": "Doge X" }, - { "id": "doge-yellow-coin", "symbol": "dogey", "name": "Doge Yellow Coin" }, - { "id": "dogeyield", "symbol": "dogy", "name": "DogeYield" }, - { "id": "dogey-inu", "symbol": "dinu", "name": "Dogey-Inu" }, - { "id": "dogezero", "symbol": "doge0", "name": "DogeZero" }, - { "id": "dogezilla", "symbol": "dogezilla", "name": "DogeZilla" }, - { "id": "doge-zilla", "symbol": "DOGEZ", "name": "DogeZilla Token" }, - { "id": "doggy", "symbol": "doggy", "name": "Doggy" }, - { "id": "doggystyle-coin", "symbol": "dsc", "name": "DoggyStyle Coin" }, - { "id": "doggy-swap", "symbol": "dogs", "name": "Doggy Swap" }, - { "id": "dogira", "symbol": "dogira", "name": "Dogira" }, - { "id": "dogkage", "symbol": "dake", "name": "DogKage" }, - { "id": "dogs", "symbol": "dogs", "name": "DOGS" }, - { "id": "dogs-kombat", "symbol": "dk", "name": "Dogs Kombat" }, - { "id": "dogsofelon", "symbol": "doe", "name": "Dogs Of Elon" }, - { "id": "dogswap-token", "symbol": "dog", "name": "Dogeswap Token (HECO)" }, - { "id": "dogus", "symbol": "dogus", "name": "Dogus" }, - { "id": "dogyrace", "symbol": "dor", "name": "DogyRace" }, - { "id": "dogz", "symbol": "dogz", "name": "Dogz" }, - { "id": "dogzverse-token", "symbol": "DGZV", "name": "DogZVerse Token" }, - { "id": "dohrnii", "symbol": "dhn", "name": "Dohrnii" }, - { "id": "dojo", "symbol": "dojo", "name": "DOJO" }, - { "id": "doken", "symbol": "doken", "name": "DoKEN" }, - { "id": "doki-doki-finance", "symbol": "doki", "name": "Doki Doki" }, - { "id": "dok-token", "symbol": "DOK", "name": "DOK Token" }, - { "id": "dola-usd", "symbol": "dola", "name": "Dola" }, - { "id": "dollardoge", "symbol": "dollardoge", "name": "DollarDoge" }, - { "id": "dollar-online", "symbol": "dollar", "name": "Dollar INTERNATIONAL" }, - { "id": "dollars", "symbol": "usdx", "name": "Dollars" }, - { "id": "dollypad-finance", "symbol": "dolly", "name": "Dollypad Finance" }, - { "id": "domain-coin", "symbol": "dmn", "name": "Domain Coin" }, - { "id": "domi", "symbol": "domi", "name": "Domi" }, - { "id": "dominium-2", "symbol": "dom", "name": "Dominium" }, - { "id": "domraider", "symbol": "drt", "name": "DomRaider" }, - { "id": "dona", "symbol": "dona", "name": "DONA" }, - { "id": "donkey", "symbol": "donk", "name": "Donkey" }, - { "id": "don-key", "symbol": "don", "name": "Don-key" }, - { "id": "donkey-token", "symbol": "don", "name": "Donkey Token" }, - { "id": "donnie-finance", "symbol": "don", "name": "Donnie Finance" }, - { "id": "dont-kyc", "symbol": "dkyc", "name": "Don't KYC" }, - { "id": "donut", "symbol": "donut", "name": "Donut" }, - { "id": "doogee", "symbol": "doogee", "name": "Doogee" }, - { "id": "doom-hero-dao", "symbol": "dhd", "name": "Doom Hero Dao" }, - { "id": "doom-hero-game", "symbol": "dhg", "name": "Doom Hero Game" }, - { "id": "doont-buy", "symbol": "dbuy", "name": "Doont Buy" }, - { "id": "door", "symbol": "door", "name": "DOOR" }, - { "id": "dooropen", "symbol": "doc", "name": "Dooropen" }, - { "id": "doos-token", "symbol": "doos", "name": "DOOS TOKEN" }, - { "id": "dope-wars-paper", "symbol": "paper", "name": "Dope Wars Paper" }, - { "id": "dopewarz", "symbol": "drug", "name": "DopeWarz" }, - { "id": "dopex", "symbol": "dpx", "name": "Dopex" }, - { - "id": "dopex-rebate-token", - "symbol": "rdpx", - "name": "Dopex Rebate Token" - }, - { "id": "dopple-finance", "symbol": "dop", "name": "Dopple Finance" }, - { "id": "doraemoninu", "symbol": "doraemoninu", "name": "DoraemonInu" }, - { "id": "doraemoon", "symbol": "dora", "name": "Doraemoon" }, - { "id": "dora-factory", "symbol": "dora", "name": "Dora Factory" }, - { "id": "doragonland", "symbol": "dor", "name": "DoragonLand" }, - { "id": "doren", "symbol": "dre", "name": "DoRen" }, - { "id": "dose-token", "symbol": "dose", "name": "DOSE" }, - { "id": "dos-network", "symbol": "dos", "name": "DOS Network" }, - { "id": "dotarcade", "symbol": "adt", "name": "DotArcade" }, - { "id": "dotc-pro-token", "symbol": "dotc", "name": "dotc.pro token" }, - { "id": "dot-finance", "symbol": "pink", "name": "Dot Finance" }, - { "id": "dotmoovs", "symbol": "moov", "name": "dotmoovs" }, - { "id": "do-token", "symbol": "do", "name": "Do Token" }, - { "id": "dotoracle", "symbol": "dto", "name": "DotOracle" }, - { "id": "double-ace", "symbol": "daa", "name": "Double Ace" }, - { "id": "double-bubble", "symbol": "dbubble", "name": "Double Bubble" }, - { "id": "doubledice-token", "symbol": "dodi", "name": "DoubleDice" }, - { "id": "doubloon", "symbol": "dbl", "name": "Doubloon" }, - { "id": "douge", "symbol": "douge", "name": "Douge" }, - { "id": "dough", "symbol": "dough", "name": "Dough" }, - { "id": "dovu", "symbol": "dov", "name": "Dovu" }, - { "id": "dowcoin", "symbol": "dow", "name": "Dowcoin" }, - { "id": "down-bad", "symbol": "db", "name": "Down Bad" }, - { "id": "down-below", "symbol": "$abyss", "name": "Down Below" }, - { "id": "doxed", "symbol": "dox", "name": "Doxed" }, - { "id": "doxxed", "symbol": "dox", "name": "Doxxed" }, - { "id": "doxxed-santa", "symbol": "DxSanta", "name": "Doxxed Santa" }, - { "id": "dpad-finance", "symbol": "dpad", "name": "Dpad Finance" }, - { "id": "dpk", "symbol": "dpk token", "name": "DPK" }, - { "id": "dprating", "symbol": "rating", "name": "DPRating" }, - { "id": "dp-token", "symbol": "DPT", "name": "DP Token" }, - { "id": "dracarys", "symbol": "drac", "name": "DRACARYS" }, - { "id": "drachen-lord", "symbol": "drag", "name": "Drachen Lord" }, - { "id": "drachma", "symbol": "dra", "name": "Drachma" }, - { "id": "draco-finance", "symbol": "draco", "name": "Draco.Finance" }, - { "id": "draco-force", "symbol": "drc", "name": "Draco Force" }, - { "id": "dracoo-point", "symbol": "dra", "name": "Dracoo Point" }, - { "id": "dracula", "symbol": "drc", "name": "Dracula" }, - { "id": "dragon-battles", "symbol": "drb", "name": "Dragon Battles" }, - { "id": "dragonbit", "symbol": "drgb", "name": "Dragonbit" }, - { "id": "dragonbite", "symbol": "bite", "name": "DragonBite" }, - { "id": "dragonchain", "symbol": "drgn", "name": "Dragonchain" }, - { "id": "dragon-coin", "symbol": "drg", "name": "Dragon Coin" }, - { - "id": "dragon-crypto-aurum", - "symbol": "dcau", - "name": "Dragon Crypto Aurum" - }, - { "id": "dragon-egg", "symbol": "dregg", "name": "Dragon Egg" }, - { "id": "dragonereum-gold", "symbol": "gold", "name": "Dragonereum GOLD" }, - { - "id": "dragon-fortune", - "symbol": "dragonfortune", - "name": "Dragon Fortune" - }, - { "id": "dragon-infinity", "symbol": "$di", "name": "Dragon Infinity" }, - { "id": "dragon-kart-token", "symbol": "kart", "name": "Dragon Kart Token" }, - { "id": "dragonknight", "symbol": "dk", "name": "Dragon Knight" }, - { - "id": "dragon-mainland-shards", - "symbol": "dms", - "name": "Dragon Mainland Shards" - }, - { "id": "dragonmaster", "symbol": "totem", "name": "DragonMaster" }, - { "id": "dragonmoon", "symbol": "dmoon", "name": "DragonMoon" }, - { "id": "dragonmusk", "symbol": "dmusk", "name": "Dragonmusk" }, - { "id": "dragon-pool", "symbol": "dp", "name": "Dragon Pool" }, - { "id": "dragonsb", "symbol": "sb", "name": "DragonSB" }, - { "id": "dragonsea", "symbol": "dge", "name": "DragonSea" }, - { "id": "dragons-gamefi", "symbol": "$dragons", "name": "Dragons gameFi" }, - { "id": "dragon-slayer", "symbol": "drs", "name": "Dragon Slayer" }, - { "id": "dragons-quick", "symbol": "dquick", "name": "Dragon's Quick" }, - { "id": "dragonvein", "symbol": "dvc", "name": "DragonVein" }, - { "id": "dragon-verse", "symbol": "drv", "name": "Dragon Verse" }, - { "id": "dragon-war", "symbol": "draw", "name": "Dragon War" }, - { "id": "dragon-warrior", "symbol": "gon+", "name": "Dragon Warrior" }, - { "id": "dragon-x", "symbol": "dax", "name": "Dragon X" }, - { "id": "drakeball-super", "symbol": "dbs", "name": "Drakeball Super" }, - { - "id": "drakeball-token", - "symbol": "dball", - "name": "DrakeBall Token [OLD]" - }, - { "id": "draken", "symbol": "drk", "name": "Draken" }, - { "id": "drakoin", "symbol": "drk", "name": "Drakoin" }, - { "id": "drax", "symbol": "drax", "name": "Drax" }, - { "id": "drc-mobility", "symbol": "drc", "name": "DRC Mobility" }, - { "id": "drdoge", "symbol": "drdoge", "name": "DrDoge" }, - { "id": "dreamdao", "symbol": "dream", "name": "DreamDAO" }, - { - "id": "dreamr-platform-token", - "symbol": "dmr", - "name": "Dreamr Platform Token" - }, - { "id": "dreamscoin", "symbol": "dream", "name": "DreamsCoin" }, - { "id": "dreams-quest", "symbol": "dreams", "name": "Dreams Quest" }, - { "id": "dream-swap", "symbol": "dream", "name": "Dream Swap" }, - { "id": "dream-token", "symbol": "dream", "name": "Dream" }, - { "id": "dreamverse", "symbol": "dv", "name": "Dreamverse" }, - { - "id": "dreamy-undersea-world", - "symbol": "duw", - "name": "Dreamy Undersea World" - }, - { "id": "d-reit", "symbol": "drt", "name": "D-ReiT" }, - { "id": "drep-new", "symbol": "drep", "name": "Drep [new]" }, - { "id": "drife", "symbol": "drf", "name": "Drife" }, - { "id": "drip-network", "symbol": "DRIP", "name": "Drip Network" }, - { "id": "drivenx", "symbol": "dvx", "name": "DRIVENx" }, - { "id": "dronefly", "symbol": "kdc", "name": "DroneFly" }, - { - "id": "drops-ownership-power", - "symbol": "dop", - "name": "Drops Ownership Power" - }, - { "id": "dr-skull", "symbol": "drsl", "name": "Dr. Skull" }, - { "id": "drunkdoge", "symbol": "drunk", "name": "DrunkDoge" }, - { "id": "drunk-robots", "symbol": "metal", "name": "Drunk Robots" }, - { - "id": "dry-doge-metaverse", - "symbol": "drydoge", - "name": "Dry Doge Metaverse" - }, - { "id": "dshares", "symbol": "dshare", "name": "DShares" }, - { "id": "d-skyscraper", "symbol": "dsg", "name": "D-Skyscraper" }, - { "id": "dsys", "symbol": "dsys", "name": "DSYS" }, - { "id": "dt-dragon-coin", "symbol": "dt", "name": "DT Dragon Coin" }, - { "id": "dtng", "symbol": "dtng", "name": "DTNG" }, - { "id": "dtravel", "symbol": "trvl", "name": "TRVL" }, - { - "id": "dtsla", - "symbol": "dTSLA", - "name": "Tesla Tokenized Stock Defichain" - }, - { "id": "dtube-coin", "symbol": "dtube", "name": "Dtube Coin" }, - { - "id": "ducato-protocol-token", - "symbol": "ducato", - "name": "Ducato Protocol Token" - }, - { "id": "duckdaodime", "symbol": "ddim", "name": "DuckDaoDime" }, - { "id": "duckduck-token", "symbol": "duck", "name": "DuckDuck Token" }, - { - "id": "duckie-land-multi-metaverse", - "symbol": "mmeta", - "name": "Duckie Land Multi Metaverse" - }, - { "id": "duckrocket", "symbol": "duck", "name": "DuckRocket" }, - { "id": "duck-vault-nftx", "symbol": "duck", "name": "DUCK Vault (NFTX)" }, - { "id": "duckydefi", "symbol": "degg", "name": "DuckyDefi" }, - { "id": "duelist-king", "symbol": "dkt", "name": "Duelist King" }, - { "id": "duel-network", "symbol": "duel", "name": "Duel Network" }, - { "id": "duet-protocol", "symbol": "duet", "name": "Duet Protocol" }, - { "id": "dukascoin", "symbol": "duk+", "name": "Dukascoin" }, - { "id": "dukecoin", "symbol": "dkc", "name": "Dukecoin" }, - { "id": "duke-inu-token", "symbol": "duke", "name": "Duke Inu Token" }, - { "id": "dulcet", "symbol": "dlc", "name": "Dulcet" }, - { "id": "dumpbuster", "symbol": "gtfo", "name": "DumpBuster" }, - { "id": "dune", "symbol": "dun", "name": "Dune" }, - { "id": "dunes", "symbol": "dunes", "name": "Dunes" }, - { "id": "dune-token", "symbol": "dune", "name": "Dune Token" }, - { "id": "dungeonswap", "symbol": "dnd", "name": "DungeonSwap" }, - { "id": "duo", "symbol": "duo", "name": "DUO Network" }, - { "id": "duo-2", "symbol": "duo", "name": "Duo" }, - { "id": "durham-inu", "symbol": "rbi", "name": "Durham Inu" }, - { "id": "dusd-network", "symbol": "dusd", "name": "DUSD Network" }, - { "id": "dusk-network", "symbol": "dusk", "name": "DUSK Network" }, - { "id": "dust", "symbol": "dust", "name": "Dust" }, - { "id": "dust-protocol", "symbol": "dust", "name": "DUST Protocol" }, - { "id": "dust-token", "symbol": "dust", "name": "DUST Token" }, - { "id": "dvf", "symbol": "dvf", "name": "DeversiFi" }, - { "id": "dvision-network", "symbol": "dvi", "name": "Dvision Network" }, - { "id": "dxbpay", "symbol": "dxb", "name": "DXBPay" }, - { "id": "dxchain", "symbol": "dx", "name": "DxChain Token" }, - { "id": "dxdao", "symbol": "dxd", "name": "DXdao" }, - { "id": "dxsale-network", "symbol": "sale", "name": "DxSale Network" }, - { "id": "dx-spot", "symbol": "dxs", "name": "Dx Spot" }, - { "id": "dyakon", "symbol": "dyn", "name": "DYAKON" }, - { "id": "dydx", "symbol": "dydx", "name": "dYdX" }, - { "id": "dydx-wormhole", "symbol": "dydx", "name": "dYdX (Wormhole)" }, - { "id": "dymmax", "symbol": "dmx", "name": "Dymmax" }, - { "id": "dynamic", "symbol": "dyn", "name": "Dynamic" }, - { "id": "dynamic-set-dollar", "symbol": "dsd", "name": "Dynamic Set Dollar" }, - { "id": "dynamite", "symbol": "dyt", "name": "DoYourTip" }, - { "id": "dynamite-token", "symbol": "dynmt", "name": "Dynamite Token" }, - { "id": "dynamix", "symbol": "dyna", "name": "Dynamix" }, - { "id": "dynamo-coin", "symbol": "dynamo", "name": "Dynamo Coin" }, - { - "id": "dynasty-global-investments-ag", - "symbol": "dyn", - "name": "Dynasty Global Investments AG" - }, - { "id": "dyor", "symbol": "DYOR", "name": "DYOR" }, - { "id": "dyor-token", "symbol": "dyor", "name": "DYOR [OLD]" }, - { "id": "e1337", "symbol": "1337", "name": "1337" }, - { "id": "eaglecoin-2", "symbol": "elc", "name": "EagleCoin" }, - { "id": "eagle-vision", "symbol": "evi", "name": "Eagle Vision" }, - { "id": "eagonswap-token", "symbol": "eagon", "name": "EagonSwap Token" }, - { "id": "early-bird", "symbol": "ebird", "name": "Early Bird" }, - { "id": "earlybsc", "symbol": "ebsc", "name": "EarlyBSC" }, - { "id": "earnablefi", "symbol": "efi", "name": "EarnableFi" }, - { "id": "earnable-v2", "symbol": "earn", "name": "Earnable v2" }, - { "id": "earnbusd", "symbol": "ebusd", "name": "EarnBUSD" }, - { "id": "earncraft", "symbol": "plot", "name": "Earncraft" }, - { "id": "earndefi", "symbol": "edc", "name": "EarnDeFi" }, - { "id": "earnfinex", "symbol": "EFX", "name": "Earnfinex" }, - { "id": "earnguild", "symbol": "earn", "name": "EarnGuild" }, - { "id": "earnhub", "symbol": "ehb", "name": "EarnHub" }, - { "id": "earn-network", "symbol": "earn$", "name": "Earn Network" }, - { "id": "earnpay", "symbol": "earnpay", "name": "EarnPay" }, - { "id": "earnscoin", "symbol": "ern", "name": "Earnscoin" }, - { "id": "earnx-v2", "symbol": "earnx", "name": "EarnX V2" }, - { "id": "earnzcoin", "symbol": "erz", "name": "EarnzCoin" }, - { "id": "earthbyt", "symbol": "ebyt", "name": "EarthByt" }, - { "id": "earthchain", "symbol": "earth", "name": "EarthChain" }, - { "id": "earthcoin", "symbol": "eac", "name": "Earthcoin" }, - { "id": "earthfund", "symbol": "1earth", "name": "EarthFund" }, - { "id": "easter-floki", "symbol": "EFLOKI", "name": "Easter Floki" }, - { "id": "easticoin", "symbol": "esti", "name": "Easticoin" }, - { "id": "easyfi", "symbol": "ez", "name": "EasyFi V2" }, - { "id": "easy-finance-token", "symbol": "eft", "name": "Easy Finance Token" }, - { "id": "easymine", "symbol": "emt", "name": "easyMine" }, - { "id": "eautocoin", "symbol": "ato", "name": "EAutocoin" }, - { "id": "eazypayza", "symbol": "ezpay", "name": "EazyPayZa" }, - { "id": "ebisu-network", "symbol": "ebs", "name": "Ebisu Network" }, - { "id": "eblockstock", "symbol": "ebso", "name": "eBlockStock" }, - { "id": "eboost", "symbol": "ebst", "name": "eBoost" }, - { "id": "ebox", "symbol": "ebox", "name": "Ebox" }, - { "id": "ebsp-token", "symbol": "ebsp", "name": "EBSP Token" }, - { "id": "ecash", "symbol": "xec", "name": "eCash" }, - { "id": "ecc", "symbol": "ecc", "name": "ECC" }, - { "id": "ecchi-coin", "symbol": "ecchi", "name": "Ecchi Coin" }, - { "id": "e-chat", "symbol": "echt", "name": "e-Chat" }, - { "id": "echidna", "symbol": "ecd", "name": "Echidna" }, - { "id": "echoin", "symbol": "ec", "name": "Echoin" }, - { "id": "echolink", "symbol": "eko", "name": "EchoLink" }, - { "id": "echosoracoin", "symbol": "esrc", "name": "EchoSoraCoin" }, - { "id": "echo-tech-coin", "symbol": "ecot", "name": "Echo Tech Coin" }, - { "id": "ecio-space", "symbol": "ecio", "name": "ECIO Space" }, - { "id": "eclipse-2", "symbol": "ecp", "name": "Eclipse" }, - { "id": "ecobit", "symbol": "ecob", "name": "Ecobit" }, - { "id": "ecochain", "symbol": "ecoc", "name": "Ecochain" }, - { "id": "ecochain-token", "symbol": "ect", "name": "Ecochain Token" }, - { "id": "ecocredit", "symbol": "ECO", "name": "EcoCREDIT" }, - { "id": "eco-defi", "symbol": "ecop", "name": "Eco DeFi" }, - { "id": "ecodollar", "symbol": "ecos", "name": "EcoDollar" }, - { "id": "ecofi", "symbol": "eco", "name": "EcoFi" }, - { "id": "ecog9coin", "symbol": "egc", "name": "EcoG9coin" }, - { "id": "ecoin-2", "symbol": "ecoin", "name": "Ecoin" }, - { "id": "ecoin-finance", "symbol": "ecoin", "name": "Ecoin Finance" }, - { "id": "ecomi", "symbol": "omi", "name": "ECOMI" }, - { "id": "ecomverse-finance", "symbol": "ecov", "name": "EcomVerse Finance" }, - { "id": "ecoreal-estate", "symbol": "ecoreal", "name": "Ecoreal Estate" }, - { "id": "ecoscu", "symbol": "ecu", "name": "ECOSC" }, - { - "id": "ecosystem-coin-network", - "symbol": "ecn", - "name": "Ecosystem Coin Network" - }, - { "id": "eco-value-coin", "symbol": "evc", "name": "Eco Value Coin" }, - { "id": "ecowatt", "symbol": "ewt", "name": "Ecowatt" }, - { "id": "ecp-technology", "symbol": "ecp", "name": "ECP+ Technology" }, - { "id": "edac", "symbol": "edac", "name": "EDAC" }, - { "id": "edain", "symbol": "eai", "name": "Edain" }, - { "id": "edc-blockchain", "symbol": "edc", "name": "EDC Blockchain" }, - { "id": "eddaswap", "symbol": "edda", "name": "EDDASwap" }, - { "id": "eden", "symbol": "eden", "name": "EDEN" }, - { "id": "edenchain", "symbol": "edn", "name": "Edenchain" }, - { "id": "edenloop", "symbol": "elt", "name": "EdenLoop" }, - { "id": "edge", "symbol": "edge", "name": "Edge" }, - { "id": "edgecoin-2", "symbol": "edgt", "name": "Edgecoin" }, - { "id": "edgeless", "symbol": "edg", "name": "Edgeless" }, - { "id": "edgeswap", "symbol": "egs", "name": "EdgeSwap" }, - { "id": "edgeware", "symbol": "edg", "name": "Edgeware" }, - { "id": "educare", "symbol": "ekt", "name": "EDUCare" }, - { - "id": "education-ecosystem", - "symbol": "ledu", - "name": "Education Ecosystem" - }, - { "id": "educoin", "symbol": "edu", "name": "Educoin" }, - { "id": "edufex", "symbol": "edux", "name": "Edufex" }, - { "id": "eeat", "symbol": "EEAT", "name": "eEat" }, - { "id": "effect-network", "symbol": "efx", "name": "Effect Network" }, - { "id": "efil", "symbol": "efil", "name": "eFIL" }, - { "id": "efin", "symbol": "efin", "name": "eFIN" }, - { - "id": "efin-decentralized", - "symbol": "wefin", - "name": "eFin Decentralized" - }, - { "id": "efinity", "symbol": "efi", "name": "Efinity Token" }, - { "id": "eft", "symbol": "eft", "name": "EFT" }, - { "id": "efun", "symbol": "efun", "name": "EFUN" }, - { "id": "egame", "symbol": "egi", "name": "eGame" }, - { "id": "eggplant", "symbol": "eggplant", "name": "Eggplant" }, - { "id": "eggplant-finance", "symbol": "eggp", "name": "Eggplant Finance" }, - { "id": "egg-protocol", "symbol": "egg", "name": "EGG Protocol" }, - { "id": "eggzilla", "symbol": "EGG", "name": "eggzilla" }, - { "id": "egoh-finance", "symbol": "egoh", "name": "EGOH Finance" }, - { "id": "egold", "symbol": "egold", "name": "eGold" }, - { "id": "egoras", "symbol": "egr", "name": "Egoras Rights" }, - { "id": "egoras-credit", "symbol": "egc", "name": "Egoras Credit" }, - { "id": "egretia", "symbol": "egt", "name": "Egretia" }, - { "id": "egyptian-mau", "symbol": "mau", "name": "Egyptian Mau" }, - { "id": "ehash", "symbol": "ehash", "name": "EHash" }, - { "id": "eidos", "symbol": "eidos", "name": "EIDOS" }, - { "id": "eifi-finance", "symbol": "eifi", "name": "EIFI Finance" }, - { "id": "eight-hours", "symbol": "ehrt", "name": "Eight Hours" }, - { "id": "eiichiro-oda-inu", "symbol": "oda", "name": "Eiichiro Oda Inu" }, - { "id": "einsteinium", "symbol": "emc2", "name": "Einsteinium" }, - { "id": "eject", "symbol": "eject", "name": "Eject" }, - { "id": "ekta-2", "symbol": "ekta", "name": "Ekta" }, - { "id": "elamachain", "symbol": "elama", "name": "Elamachain" }, - { "id": "elasticswap", "symbol": "tic", "name": "ElasticSwap" }, - { "id": "elastos", "symbol": "ela", "name": "Elastos" }, - { "id": "electra", "symbol": "eca", "name": "Electra" }, - { "id": "electra-protocol", "symbol": "xep", "name": "Electra Protocol" }, - { "id": "electric-arena", "symbol": "earena", "name": "Electric Arena" }, - { "id": "electric-cash", "symbol": "elcash", "name": "Electric Cash" }, - { - "id": "electric-vehicle-direct-currency", - "symbol": "evdc", - "name": "Electric Vehicle Direct Currency" - }, - { - "id": "electric-vehicle-zone", - "symbol": "evz", - "name": "Electric Vehicle Zone" - }, - { "id": "electrify-asia", "symbol": "elec", "name": "Electrify.Asia" }, - { "id": "electronero", "symbol": "etnx", "name": "Electronero" }, - { "id": "electronero-pulse", "symbol": "etnxp", "name": "Electronero Pulse" }, - { "id": "electroneum", "symbol": "etn", "name": "Electroneum" }, - { "id": "electronicgulden", "symbol": "efl", "name": "Electronic Gulden" }, - { - "id": "electronic-pk-chain", - "symbol": "epc", - "name": "Electronic PK Chain" - }, - { "id": "electrum-dark", "symbol": "eld", "name": "Electrum Dark" }, - { "id": "elefworld", "symbol": "elef", "name": "ELEF World" }, - { "id": "element-finance", "symbol": "elfi", "name": "Element Finance" }, - { "id": "elementrem", "symbol": "ele", "name": "Elementrem" }, - { "id": "elements-2", "symbol": "elm", "name": "Elements" }, - { "id": "elemon", "symbol": "elmon", "name": "Elemon" }, - { "id": "elena-protocol", "symbol": "elena", "name": "Elena Protocol" }, - { "id": "elephant-money", "symbol": "elephant", "name": "Elephant Money" }, - { "id": "elevate", "symbol": "ele", "name": "Elevate" }, - { "id": "e-leven", "symbol": "elv", "name": "E-leven" }, - { "id": "eleven-finance", "symbol": "ele", "name": "Eleven Finance" }, - { "id": "eleventoken", "symbol": "elvn", "name": "ElevenToken" }, - { "id": "elexir", "symbol": "elxr", "name": "Elexir" }, - { "id": "elf-token", "symbol": "elf", "name": "Elf Token" }, - { "id": "eligma", "symbol": "goc", "name": "GoCrypto" }, - { "id": "elis", "symbol": "xls", "name": "ELIS" }, - { "id": "elite-swap", "symbol": "elt", "name": "Elite Swap" }, - { "id": "elitium", "symbol": "eum", "name": "Elitium" }, - { "id": "elk-finance", "symbol": "elk", "name": "Elk Finance" }, - { "id": "ellaism", "symbol": "ella", "name": "Ellaism" }, - { "id": "ellipsis", "symbol": "eps", "name": "Ellipsis" }, - { "id": "eloin", "symbol": "eloin", "name": "Eloin" }, - { "id": "elo-inu", "symbol": "Elo Inu", "name": "Elo Inu" }, - { "id": "elonballs", "symbol": "elonballs", "name": "ELONBALLS" }, - { "id": "elonbank", "symbol": "elonbank", "name": "ElonBank" }, - { "id": "elondoge-dao", "symbol": "edao", "name": "ElonDoge DAO" }, - { "id": "elon-doge-token", "symbol": "edoge", "name": "ElonDoge Token" }, - { "id": "elonflokiinu", "symbol": "EFloki", "name": "ElonFlokiInu" }, - { "id": "elongate", "symbol": "elongate", "name": "ElonGate" }, - { "id": "elongate-duluxe", "symbol": "elongd", "name": "Elongate Deluxe" }, - { "id": "elon-goat", "symbol": "egt", "name": "Elon GOAT" }, - { "id": "elongrab", "symbol": "ELONGRAB", "name": "Elongrab" }, - { "id": "eloniumcoin", "symbol": "elnc", "name": "EloniumCoin" }, - { "id": "elonjet", "symbol": "elonjet", "name": "ElonJet" }, - { "id": "elonomics", "symbol": "$elonom", "name": "Elonomics" }, - { "id": "elon-peg", "symbol": "elonpeg", "name": "Elon Peg" }, - { "id": "elons-marvin", "symbol": "marvin", "name": "Elon's Marvin" }, - { "id": "elonspets", "symbol": "elp", "name": "ElonsPets" }, - { "id": "elons-rabbit", "symbol": "erabbit", "name": "ELONs RABBIT" }, - { "id": "elontech", "symbol": "etch", "name": "ElonTech" }, - { "id": "elpis-battle", "symbol": "EBA", "name": "Elpis Battle" }, - { "id": "elrond-erd-2", "symbol": "egld", "name": "Elrond" }, - { "id": "eltcoin", "symbol": "eltcoin", "name": "Eltcoin" }, - { "id": "elumia", "symbol": "elu", "name": "Elumia" }, - { "id": "elvantis", "symbol": "elv", "name": "Elvantis" }, - { "id": "elves-century", "symbol": "elves", "name": "Elves Century" }, - { "id": "elves-continent", "symbol": "ek", "name": "Elves Continent" }, - { "id": "elya", "symbol": "elya", "name": "Elya" }, - { "id": "elyfi", "symbol": "elfi", "name": "ELYFI" }, - { "id": "elynet-token", "symbol": "elyx", "name": "Elynet Token" }, - { "id": "elysia", "symbol": "el", "name": "ELYSIA" }, - { "id": "elysian", "symbol": "ely", "name": "Elysian" }, - { "id": "elysiant-token", "symbol": "els", "name": "Elysian Token" }, - { "id": "elysium", "symbol": "lys", "name": "Elysium" }, - { "id": "emanate", "symbol": "EMT", "name": "Emanate" }, - { "id": "embr", "symbol": "embr", "name": "Embr" }, - { "id": "emerald-crypto", "symbol": "emd", "name": "Emerald Crypto" }, - { "id": "emercoin", "symbol": "emc", "name": "EmerCoin" }, - { "id": "eminer", "symbol": "em", "name": "Eminer" }, - { "id": "emirex-token", "symbol": "emrx", "name": "Emirex Token" }, - { "id": "emiswap", "symbol": "esw", "name": "EmiSwap" }, - { "id": "emocoin", "symbol": "EMO", "name": "Emocoin" }, - { "id": "emogi-network", "symbol": "lol", "name": "EMOGI Network" }, - { "id": "e-money", "symbol": "ngm", "name": "e-Money" }, - { "id": "e-money-eur", "symbol": "eeur", "name": "e-Money EUR" }, - { - "id": "empire-capital-token", - "symbol": "ecc", - "name": "Empire Capital Token" - }, - { "id": "empire-token", "symbol": "empire", "name": "Empire Token" }, - { "id": "emp-money", "symbol": "emp", "name": "Emp Money" }, - { "id": "emporiumx", "symbol": "epx", "name": "EmporiumX" }, - { "id": "empow", "symbol": "em", "name": "Empow" }, - { "id": "empowa", "symbol": "emp", "name": "Empowa" }, - { "id": "emp-shares", "symbol": "eshare", "name": "EMP Shares" }, - { "id": "empty-set-dollar", "symbol": "esd", "name": "Empty Set Dollar" }, - { "id": "empyrean", "symbol": "empyr", "name": "Empyrean" }, - { "id": "emrals", "symbol": "emrals", "name": "Emrals" }, - { "id": "encocoinplus", "symbol": "epg", "name": "Encocoinplus" }, - { "id": "encrypgen", "symbol": "dna", "name": "EncrypGen" }, - { "id": "endgame-token", "symbol": "end", "name": "Endgame Token" }, - { - "id": "endless-battlefield", - "symbol": "eb", - "name": "Endless Battlefield" - }, - { - "id": "endless-battlefield-coin", - "symbol": "ebc", - "name": "Endless Battlefield Coin" - }, - { "id": "endor", "symbol": "edr", "name": "Endor Protocol Token" }, - { - "id": "endpoint-cex-fan-token", - "symbol": "endcex", - "name": "Endpoint Cex Fan Token" - }, - { "id": "enegra", "symbol": "egx", "name": "Enegra" }, - { "id": "energi", "symbol": "nrg", "name": "Energi" }, - { "id": "energo", "symbol": "tsl", "name": "Tesla Token" }, - { "id": "energoncoin", "symbol": "tfg1", "name": "Energoncoin" }, - { "id": "energy", "symbol": "nrgy", "name": "ENERGY" }, - { "id": "energy8", "symbol": "e8", "name": "Energy8" }, - { - "id": "energy-efficient-mortgage-tokenized-stock-defichain", - "symbol": "dEEM", - "name": "iShares MSCI Emerging Markets ETF Defichain" - }, - { "id": "energyfi", "symbol": "EFT", "name": "Energyfi" }, - { "id": "energy-ledger", "symbol": "elx", "name": "Energy Ledger" }, - { "id": "energy-pay", "symbol": "ENY", "name": "Energy Pay" }, - { "id": "energy-web-token", "symbol": "ewt", "name": "Energy Web Token" }, - { "id": "enex", "symbol": "enx", "name": "ENEX" }, - { "id": "engine", "symbol": "egcc", "name": "Engine" }, - { "id": "engine-token", "symbol": "engn", "name": "Engine Token" }, - { - "id": "enhanced-liquidity-konstrukt", - "symbol": "elk", - "name": "Enhanced Liquidity Konstrukt" - }, - { "id": "enhance-token", "symbol": "enhance", "name": "Enhance Token" }, - { "id": "enhance-v2", "symbol": "enh", "name": "Enhance V2" }, - { "id": "enigma", "symbol": "eng", "name": "Enigma" }, - { "id": "enigma-dao", "symbol": "gma", "name": "Enigma DAO" }, - { "id": "enjincoin", "symbol": "enj", "name": "Enjin Coin" }, - { "id": "enjinstarter", "symbol": "ejs", "name": "Enjinstarter" }, - { "id": "enjin-wormhole", "symbol": "enj", "name": "Enjin (Wormhole)" }, - { "id": "enkix", "symbol": "ekx", "name": "EnkiX" }, - { "id": "enno-cash", "symbol": "enno", "name": "ENNO Cash" }, - { "id": "enq-enecuum", "symbol": "enq", "name": "Enecuum" }, - { "id": "enreachdao", "symbol": "nrch", "name": "EnreachDAO" }, - { "id": "enrex", "symbol": "enrx", "name": "Enrex" }, - { "id": "en-tan-mo", "symbol": "etm", "name": "En-Tan-Mo" }, - { "id": "enterbutton", "symbol": "entc", "name": "EnterButton" }, - { "id": "entercoin", "symbol": "entrc", "name": "EnterCoin" }, - { "id": "enterdao", "symbol": "entr", "name": "EnterDAO" }, - { "id": "entireswap", "symbol": "entire", "name": "EntireSwap" }, - { "id": "entropyfi", "symbol": "erp", "name": "Entropyfi" }, - { "id": "enumivo", "symbol": "enu", "name": "Enumivo" }, - { "id": "env-finance", "symbol": "env", "name": "ENV Finance" }, - { "id": "envida", "symbol": "edat", "name": "EnviDa" }, - { "id": "envion", "symbol": "evn", "name": "Envion" }, - { "id": "enviro", "symbol": "enviro", "name": "Enviro" }, - { "id": "envision", "symbol": "vis", "name": "Envision" }, - { "id": "envoy-defi", "symbol": "voy", "name": "enVoy DeFi" }, - { "id": "envoy-network", "symbol": "env", "name": "Envoy" }, - { "id": "eos", "symbol": "eos", "name": "EOS" }, - { "id": "eosbet", "symbol": "bet", "name": "EarnBet" }, - { "id": "eosblack", "symbol": "black", "name": "eosBLACK" }, - { "id": "eosdac", "symbol": "eosdac", "name": "eosDAC" }, - { "id": "eosforce", "symbol": "eosc", "name": "EOSForce" }, - { "id": "eos-pow-coin", "symbol": "pow", "name": "EOS PoW Coin" }, - { "id": "eox", "symbol": "eox", "name": "EOX" }, - { "id": "e-p", "symbol": "e$p", "name": "E$P" }, - { "id": "epanus", "symbol": "eps", "name": "Epanus" }, - { "id": "epic-cash", "symbol": "epic", "name": "Epic Cash" }, - { "id": "epichero", "symbol": "epichero", "name": "EpicHero" }, - { "id": "epic-war", "symbol": "ewar", "name": "Epic War" }, - { "id": "epik-prime", "symbol": "epik", "name": "Epik Prime" }, - { "id": "epik-protocol", "symbol": "epk", "name": "EpiK Protocol" }, - { "id": "eqifi", "symbol": "eqx", "name": "EQIFi" }, - { "id": "equal", "symbol": "eql", "name": "Equal" }, - { "id": "equalizer", "symbol": "eqz", "name": "Equalizer" }, - { "id": "equilibrium", "symbol": "eq", "name": "Equilibrium Games" }, - { "id": "equilibrium-eosdt", "symbol": "eosdt", "name": "Equilibrium EOSDT" }, - { "id": "equitrader", "symbol": "eqt", "name": "EquiTrader" }, - { "id": "equos-origin", "symbol": "eqo", "name": "EQO" }, - { "id": "era7", "symbol": "era", "name": "Era7" }, - { "id": "e-radix", "symbol": "exrd", "name": "e-Radix" }, - { "id": "era-swap-token", "symbol": "es", "name": "Era Swap Token" }, - { "id": "erc20", "symbol": "erc20", "name": "ERC20" }, - { - "id": "erenyeagerinu", - "symbol": "erenyeagerinu", - "name": "Eren Yeager Inu" - }, - { "id": "ergo", "symbol": "erg", "name": "Ergo" }, - { "id": "ergopad", "symbol": "ergopad", "name": "Ergopad" }, - { "id": "erica-social-token", "symbol": "est", "name": "Erica Social Token" }, - { "id": "eristica", "symbol": "ert", "name": "Eristica token" }, - { "id": "eron", "symbol": "eron", "name": "ERON" }, - { "id": "eros", "symbol": "ers", "name": "Eros" }, - { "id": "eroverse", "symbol": "ero", "name": "Eroverse" }, - { "id": "ertha", "symbol": "ertha", "name": "Ertha" }, - { "id": "erth-token", "symbol": "erth", "name": "ERTH Token" }, - { "id": "erugo-world-coin", "symbol": "ewc", "name": "Erugo World Coin" }, - { "id": "erzurumspor-token", "symbol": "erz", "name": "Erzurumspor Token" }, - { "id": "escoin-token", "symbol": "elg", "name": "Escoin Token" }, - { "id": "escudonavacense", "symbol": "esn", "name": "EscudoNavacense" }, - { "id": "esg", "symbol": "esg", "name": "ESG" }, - { "id": "esg-chain", "symbol": "esgc", "name": "ESG Chain" }, - { "id": "eshark-token", "symbol": "eshk", "name": "eShark Token" }, - { "id": "eska", "symbol": "esk", "name": "Eska" }, - { - "id": "eskisehir-fan-token", - "symbol": "eses", - "name": "Eskişehir Fan Tokens" - }, - { "id": "espers", "symbol": "esp", "name": "Espers" }, - { "id": "esplash", "symbol": "espl", "name": "ESplash" }, - { "id": "esportgame", "symbol": "esm", "name": "EsportGame" }, - { "id": "esports", "symbol": "ert", "name": "Esports.com" }, - { "id": "esportspro", "symbol": "espro", "name": "EsportsPro" }, - { "id": "esportsref", "symbol": "esr", "name": "EsportsRef" }, - { - "id": "essence-of-creation", - "symbol": "eoc", - "name": "Essence of Creation" - }, - { "id": "essentia", "symbol": "ess", "name": "Essentia" }, - { "id": "ester-finance", "symbol": "est", "name": "Ester Finance" }, - { "id": "eswapping-v2", "symbol": "eswapv2", "name": "eSwapping v2" }, - { "id": "eterland", "symbol": "eter", "name": "Eterland" }, - { "id": "etermon", "symbol": "etm", "name": "Etermon" }, - { - "id": "eterna-hybrid-exchange", - "symbol": "ehx", - "name": "Eterna Hybrid Exchange" - }, - { "id": "eternal-dragons", "symbol": "", "name": "Eternal Dragons" }, - { "id": "eternalflow", "symbol": "EFT", "name": "EternalFlow" }, - { "id": "eternalgirl", "symbol": "etgl", "name": "EternalGirl" }, - { "id": "eternal-oasis", "symbol": "etos", "name": "Eternal Oasis" }, - { "id": "eternal-spire-v2", "symbol": "ensp", "name": "Eternal Spire V2" }, - { "id": "etg-finance", "symbol": "etgf", "name": "ETG Finance" }, - { - "id": "eth_20_day_ma_crossover_set", - "symbol": "eth20smaco", - "name": "ETH 20 Day MA Crossover Set" - }, - { - "id": "eth2-staking-by-poolx", - "symbol": "eth2", - "name": "Eth 2.0 Staking by Pool-X" - }, - { - "id": "eth-2x-flexible-leverage-index", - "symbol": "ETH2x-FLI", - "name": "Index Coop - ETH 2x Flexible Leverage Index" - }, - { "id": "eth3s", "symbol": "eth3s", "name": "ETH3S" }, - { "id": "etha-lend", "symbol": "etha", "name": "ETHA Lend" }, - { "id": "ethart", "symbol": "arte", "name": "Items" }, - { "id": "ethbnt", "symbol": "ethbnt", "name": "ETHBNT Relay" }, - { "id": "ethbtc-1x-short", "symbol": "ratiodoom", "name": "ETHBTC 1x Short" }, - { - "id": "ethbtc-2x-long-polygon", - "symbol": "mratiomoon", - "name": "ETHBTC 2x Long (Polygon)" - }, - { - "id": "ethbtc-2x-long-token", - "symbol": "ethbtcmoon", - "name": "ETHBTC 2x Long Token" - }, - { - "id": "eth-btc-ema-ratio-trading-set", - "symbol": "ethbtcemaco", - "name": "ETH/BTC EMA Ratio Trading Set" - }, - { "id": "ethdown", "symbol": "ethdown", "name": "ETHDOWN" }, - { "id": "etheal", "symbol": "heal", "name": "Etheal" }, - { "id": "etheking", "symbol": "ethe", "name": "ETHEKing" }, - { "id": "ether-1", "symbol": "ETHO", "name": "Etho Protocol" }, - { "id": "ethera-2", "symbol": "eta", "name": "Ethera" }, - { "id": "ethera-black", "symbol": "etb", "name": "Ethera Black" }, - { "id": "etherback", "symbol": "ethback", "name": "EtherBack" }, - { "id": "etherconnect", "symbol": "ecc", "name": "Etherconnect" }, - { "id": "ethereans", "symbol": "os", "name": "Ethereans" }, - { "id": "etherean-socks", "symbol": "eth2socks", "name": "Etherean Socks" }, - { "id": "ethereum", "symbol": "eth", "name": "Ethereum" }, - { "id": "ethereum-apex", "symbol": "eapex", "name": "Ethereum Apex" }, - { "id": "ethereum-cash", "symbol": "ecash", "name": "Ethereum Cash" }, - { - "id": "ethereum-cash-token", - "symbol": "ecash", - "name": "Ethereum Cash Token" - }, - { "id": "ethereum-classic", "symbol": "etc", "name": "Ethereum Classic" }, - { - "id": "ethereum-eagle-project", - "symbol": "egl", - "name": "Ethereum Gas Limit" - }, - { "id": "ethereum-gold", "symbol": "etg", "name": "Ethereum Gold" }, - { - "id": "ethereum-gold-project", - "symbol": "etgp", - "name": "Ethereum Gold Project" - }, - { "id": "ethereum-lite", "symbol": "elite", "name": "Ethereum Lite" }, - { "id": "ethereummax", "symbol": "emax", "name": "EthereumMax" }, - { "id": "ethereum-meta", "symbol": "ethm", "name": "Ethereum Meta" }, - { "id": "ethereum-money", "symbol": "ethmny", "name": "Ethereum Money" }, - { - "id": "ethereum-name-service", - "symbol": "ens", - "name": "Ethereum Name Service" - }, - { - "id": "ethereum-name-service-wormhole", - "symbol": "ens", - "name": "Ethereum Name Service (Wormhole)" - }, - { - "id": "ethereum-push-notification-service", - "symbol": "push", - "name": "Ethereum Push Notification Service - EPNS" - }, - { - "id": "ethereum-shillings", - "symbol": "eshill", - "name": "Ethereum Shillings" - }, - { "id": "ethereum-stake", "symbol": "ethys", "name": "Ethereum Stake" }, - { - "id": "ethereum-volatility-index-token", - "symbol": "ethv", - "name": "Ethereum Volatility Index Token" - }, - { "id": "ethereum-wormhole", "symbol": "eth", "name": "Ethereum (Wormhole)" }, - { - "id": "ethereum-wrapped-filecoin", - "symbol": "efil", - "name": "Ethereum Wrapped Filecoin" - }, - { "id": "ethereumx", "symbol": "etx", "name": "EthereumX" }, - { "id": "ethereum-yield", "symbol": "ethy", "name": "Ethereum Yield" }, - { "id": "ethergem", "symbol": "egem", "name": "EtherGem" }, - { "id": "etherinc", "symbol": "eti", "name": "EtherInc" }, - { "id": "etherisc", "symbol": "dip", "name": "Etherisc DIP Token" }, - { "id": "etherland", "symbol": "eland", "name": "Etherland" }, - { "id": "ether-legends", "symbol": "elet", "name": "Elementeum" }, - { "id": "etherlite-2", "symbol": "etl", "name": "EtherLite" }, - { "id": "ethermon", "symbol": "emon", "name": "Ethermon" }, - { "id": "ethernaal", "symbol": "naal", "name": "Ethernaal" }, - { "id": "ethernal-finance", "symbol": "ethfin", "name": "Ethernal Finance" }, - { "id": "ethernity-chain", "symbol": "ern", "name": "Ethernity Chain" }, - { "id": "ethernity-cloud", "symbol": "etny", "name": "Ethernity CLOUD" }, - { "id": "etheroll", "symbol": "dice", "name": "Etheroll" }, - { "id": "etherparty", "symbol": "fuel", "name": "Etherparty" }, - { "id": "etherpay", "symbol": "ethpy", "name": "Etherpay" }, - { "id": "etherprint", "symbol": "ethp", "name": "Etherprint" }, - { "id": "etherrock-72", "symbol": "pebble", "name": "Etherrock #72" }, - { "id": "ethersmart", "symbol": "etm", "name": "EtherSmart" }, - { "id": "ethersniper", "symbol": "ets", "name": "Ethersniper" }, - { "id": "etherstones", "symbol": "eths", "name": "Etherstones" }, - { "id": "ether-terrestrial", "symbol": "et", "name": "Ether Terrestrial" }, - { "id": "etherzero", "symbol": "etz", "name": "Ether Zero" }, - { "id": "ethfan-burn", "symbol": "$efb", "name": "ETHFan Burn" }, - { "id": "eth-fan-token", "symbol": "eft", "name": "ETH Fan Token Ecosystem" }, - { "id": "ethichub", "symbol": "ethix", "name": "EthicHub" }, - { "id": "ethlend", "symbol": "lend", "name": "Aave [OLD]" }, - { - "id": "eth-max-yield-index", - "symbol": "ethmaxy", - "name": "ETH Max Yield Index" - }, - { "id": "eth-note", "symbol": "ethnote", "name": "Eth Note" }, - { "id": "ethorse", "symbol": "horse", "name": "Ethorse" }, - { "id": "ethos", "symbol": "vgx", "name": "Voyager Token" }, - { "id": "ethos-project", "symbol": "ethos", "name": "Ethos Project" }, - { "id": "ethpad", "symbol": "ethpad", "name": "ETHPad" }, - { "id": "ethplode", "symbol": "ethplo", "name": "ETHplode" }, - { "id": "ethplus", "symbol": "ethp", "name": "ETHPlus" }, - { - "id": "eth-rsi-60-40-yield-set-ii", - "symbol": "ethrsiapy", - "name": "ETH RSI 60/40 Yield Set II" - }, - { "id": "eth-shiba", "symbol": "ethshib", "name": "Eth Shiba" }, - { - "id": "ethst-governance-token", - "symbol": "et", - "name": "ETHST Governance Token" - }, - { "id": "ethtez", "symbol": "ethtz", "name": "ETHtez" }, - { "id": "ethup", "symbol": "ethup", "name": "ETHUP" }, - { - "id": "eth-variable-long", - "symbol": "methmoon", - "name": "ETH Variable Leverage Long" - }, - { "id": "ethverse", "symbol": "ethv", "name": "Ethverse" }, - { "id": "ethzilla", "symbol": "ethzilla", "name": "EthZilla" }, - { "id": "etitanium", "symbol": "etit", "name": "eTitanium" }, - { "id": "etna-metabolism", "symbol": "mtb", "name": "ETNA Metabolism" }, - { "id": "etna-network", "symbol": "etna", "name": "ETNA Network" }, - { "id": "etor", "symbol": "etor", "name": "etor" }, - { "id": "etrade", "symbol": "ett", "name": "Etrade" }, - { "id": "eub-chain", "symbol": "eubc", "name": "EUB Chain" }, - { "id": "euler-tools", "symbol": "euler", "name": "Euler Tools" }, - { "id": "euno", "symbol": "euno", "name": "EUNO" }, - { "id": "eunomia", "symbol": "ents", "name": "EUNOMIA" }, - { "id": "euphoria-2", "symbol": "wagmi", "name": "Euphoria" }, - { "id": "eureka-coin", "symbol": "erk", "name": "Eureka Coin" }, - { "id": "eurocoinpay", "symbol": "ecte", "name": "EurocoinToken" }, - { "id": "euronin", "symbol": "euronin", "name": "Euronin" }, - { "id": "europecoin", "symbol": "erc", "name": "EuropeCoin" }, - { "id": "euro-stable-token", "symbol": "eurst", "name": "Euro Stable Token" }, - { "id": "eutaria", "symbol": "eut", "name": "Eutaria" }, - { "id": "evagrow-coin", "symbol": "eva", "name": "Evagrow Coin" }, - { "id": "evanesco-network", "symbol": "eva", "name": "Evanesco Network" }, - { "id": "evedo", "symbol": "eved", "name": "Evedo" }, - { "id": "eve-exchange", "symbol": "eve", "name": "EVE Token" }, - { "id": "evencoin", "symbol": "evn", "name": "EvenCoin" }, - { "id": "everape", "symbol": "everape", "name": "EverApe" }, - { "id": "everbnb", "symbol": "everbnb", "name": "EverBNB" }, - { "id": "everdome", "symbol": "dome", "name": "Everdome" }, - { "id": "everdot", "symbol": "everdot", "name": "EverDot" }, - { "id": "everearn", "symbol": "earn", "name": "EverEarn" }, - { "id": "everestcoin", "symbol": "evcoin", "name": "EverestCoin" }, - { "id": "everest-token", "symbol": "evrt", "name": "Everest Token" }, - { "id": "evereth", "symbol": "EverETH", "name": "EverETH" }, - { "id": "everex", "symbol": "evx", "name": "Everex" }, - { "id": "evergain", "symbol": "evergain", "name": "EverGain" }, - { "id": "evergreen-token", "symbol": "egt", "name": "Evergreen Token" }, - { "id": "evergrowcoin", "symbol": "EGC", "name": "EverGrowCoin" }, - { "id": "everid", "symbol": "id", "name": "Everest" }, - { "id": "everipedia", "symbol": "iq", "name": "Everipedia" }, - { "id": "everlens", "symbol": "elen", "name": "Everlens" }, - { "id": "evermars", "symbol": "evm", "name": "EverMars" }, - { "id": "everprinter", "symbol": "eprint", "name": "EverPrinter" }, - { "id": "everreflect", "symbol": "evrf", "name": "EverReflect" }, - { "id": "everrise", "symbol": "rise", "name": "EverRise" }, - { "id": "eversafu", "symbol": "eversafu", "name": "EverSAFU" }, - { "id": "everscale", "symbol": "ever", "name": "Everscale" }, - { "id": "eversol", "symbol": "esol", "name": "EVERSOL" }, - { - "id": "eversol-staked-sol", - "symbol": "esol", - "name": "EverSOL Staked SOL" - }, - { "id": "everswap", "symbol": "ever", "name": "EverSwap" }, - { "id": "everton-fan-token", "symbol": "efc", "name": "Everton Fan Token" }, - { "id": "everus", "symbol": "evr", "name": "Everus" }, - { "id": "everyape-bsc", "symbol": "evape", "name": "EveryApe BSC" }, - { "id": "everycoin", "symbol": "evy", "name": "EveryCoin" }, - { "id": "every-game", "symbol": "egame", "name": "Every Game" }, - { "id": "everyonescrypto", "symbol": "eoc", "name": "EveryonesCrypto" }, - { "id": "every-original", "symbol": "eveo", "name": "EVERY ORIGINAL" }, - { - "id": "evident-proof-transaction-token", - "symbol": "eptt", - "name": "Evident Proof Transaction Token" - }, - { "id": "evil-coin", "symbol": "evil", "name": "Evil Coin" }, - { "id": "evil-shiba-inu", "symbol": "esi", "name": "Evil Shiba Inu" }, - { "id": "evilsquidgame", "symbol": "evilsquid", "name": "EvilSquidGame" }, - { "id": "evmos", "symbol": "evmos", "name": "Evmos" }, - { "id": "evny-token", "symbol": "evny", "name": "EVNY Token" }, - { "id": "evocardano", "symbol": "evoc", "name": "EvoCardano" }, - { "id": "evolution", "symbol": "gen", "name": "Evolution" }, - { "id": "evolution-finance", "symbol": "evn", "name": "Evolution Finance" }, - { "id": "evolution-network", "symbol": "evox", "name": "Evolution Network" }, - { "id": "evolution-token", "symbol": "evo", "name": "Evolution Token" }, - { "id": "evoverse-power", "symbol": "epw", "name": "Evoverse Power" }, - { "id": "evoverses", "symbol": "evo", "name": "EvoVerses" }, - { "id": "evrice", "symbol": "evc", "name": "Evrice" }, - { "id": "evrynet", "symbol": "evry", "name": "Evrynet" }, - { "id": "evulus", "symbol": "evu", "name": "Evulus" }, - { "id": "excalibur", "symbol": "exc", "name": "Excalibur" }, - { "id": "excavo-finance", "symbol": "cavo", "name": "Excavo Finance" }, - { "id": "excelon", "symbol": "xlon", "name": "Excelon" }, - { "id": "exchangecoin", "symbol": "excc", "name": "ExchangeCoin" }, - { - "id": "exchange-genesis-ethlas-medium", - "symbol": "xgem", - "name": "Exchange Genesis Ethlas Medium" - }, - { "id": "exchange-union", "symbol": "xuc", "name": "Exchange Union" }, - { - "id": "exciting-japan-coin", - "symbol": "xjp", - "name": "eXciting Japan Coin" - }, - { "id": "exclusivecoin", "symbol": "excl", "name": "ExclusiveCoin" }, - { "id": "exeedme", "symbol": "xed", "name": "Exeedme" }, - { "id": "exenpay-token", "symbol": "exenp", "name": "ExenPay Token" }, - { "id": "exentoken", "symbol": "exen", "name": "ExenToken" }, - { "id": "exgold", "symbol": "exg", "name": "Exgold" }, - { "id": "exip", "symbol": "EXIP", "name": "EXIP" }, - { "id": "exist", "symbol": "exist", "name": "Exist" }, - { "id": "exmoc", "symbol": "exmoc", "name": "EXMOC" }, - { "id": "exmo-coin", "symbol": "exm", "name": "EXMO Coin" }, - { "id": "exmr-monero", "symbol": "exmr", "name": "EXMR FDN" }, - { "id": "exnetwork-token", "symbol": "exnt", "name": "ExNetwork Token" }, - { "id": "exodia", "symbol": "exod", "name": "Exodia" }, - { "id": "exodia-inu", "symbol": "exodia", "name": "Exodia Inu" }, - { "id": "exohood", "symbol": "exo", "name": "Exohood" }, - { "id": "exor", "symbol": "exor", "name": "EXOR" }, - { "id": "exordium-limited", "symbol": "exo", "name": "Exordium Limited" }, - { "id": "exp", "symbol": "exp", "name": "Exp" }, - { "id": "expanse", "symbol": "exp", "name": "Expanse" }, - { "id": "experience-chain", "symbol": "xpc", "name": "eXPerience Chain" }, - { "id": "experiencecoin", "symbol": "epc", "name": "ExperienceCoin" }, - { "id": "experiencer", "symbol": "expr", "name": "Experiencer" }, - { - "id": "experty-wisdom-token", - "symbol": "wis", - "name": "Experty Wisdom Token" - }, - { - "id": "exponential-capital", - "symbol": "expo", - "name": "Exponential Capital" - }, - { - "id": "export-mortos-platform", - "symbol": "emp", - "name": "Export Mortos Platform" - }, - { "id": "exrnchain", "symbol": "exrn", "name": "EXRNchain" }, - { "id": "exrt-network", "symbol": "exrt", "name": "EXRT Network" }, - { "id": "extradna", "symbol": "xdna", "name": "extraDNA" }, - { "id": "exzocoin", "symbol": "exzo", "name": "ExzoCoin 2.0" }, - { "id": "eyes-protocol", "symbol": "eyes", "name": "EYES Protocol" }, - { "id": "ezchain", "symbol": "ezc", "name": "EZChain" }, - { "id": "ezdex", "symbol": "ezx", "name": "EZDex" }, - { "id": "ezystayz", "symbol": "ezy", "name": "Ezystayz" }, - { "id": "fabric", "symbol": "fab", "name": "Fabric" }, - { "id": "fabwelt", "symbol": "welt", "name": "Fabwelt" }, - { "id": "face", "symbol": "face", "name": "Faceter" }, - { - "id": "facebook-tokenized-stock-defichain", - "symbol": "dfb", - "name": "Facebook Tokenized Stock Defichain" - }, - { - "id": "facebook-tokenized-stock-zipmex", - "symbol": "fb", - "name": "Facebook Tokenized Stock Zipmex" - }, - { "id": "facedao", "symbol": "face", "name": "FaceDAO" }, - { "id": "face-vault-nftx", "symbol": "face", "name": "FACE Vault (NFTX)" }, - { "id": "factom", "symbol": "fct", "name": "Factom" }, - { "id": "facts", "symbol": "bkc", "name": "FACTS" }, - { "id": "fado-go", "symbol": "fado", "name": "FADO Go" }, - { "id": "faircoin", "symbol": "fair", "name": "Faircoin" }, - { "id": "fairface", "symbol": "faf", "name": "Fairface" }, - { "id": "fairgame", "symbol": "fair", "name": "FairGame" }, - { "id": "fairlife", "symbol": "fairlife", "name": "FairLife" }, - { "id": "fairum", "symbol": "fai", "name": "Fairum" }, - { "id": "fairy-forest", "symbol": "ffn", "name": "Fairy Forest" }, - { "id": "faithcoin", "symbol": "faith", "name": "FaithCoin" }, - { "id": "faith-tribe", "symbol": "ftrb", "name": "Faith Tribe" }, - { "id": "falafel", "symbol": "falafel", "name": "Falafel" }, - { "id": "falcon-nine", "symbol": "f9", "name": "Falcon Nine" }, - { "id": "falcon-swaps", "symbol": "falcons", "name": "Falcon Swaps" }, - { "id": "falcon-token", "symbol": "fnt", "name": "Falcon Project" }, - { "id": "falconx", "symbol": "falcx", "name": "FalconX" }, - { "id": "family", "symbol": "fam", "name": "Family" }, - { "id": "familyparty", "symbol": "fpc", "name": "FamilyParty" }, - { "id": "famous-coin", "symbol": "famous", "name": "Famous Coin" }, - { "id": "famous-five", "symbol": "fafi", "name": "Famous Five" }, - { "id": "fam-token", "symbol": "fam", "name": "FAM Token" }, - { "id": "fan8", "symbol": "fan8", "name": "Fan8" }, - { "id": "fanadise", "symbol": "fan", "name": "Fanadise" }, - { "id": "fananywhere", "symbol": "faw", "name": "FanAnywhere" }, - { "id": "fanaticos-cash", "symbol": "fch", "name": "Fanáticos Cash" }, - { "id": "fanbi-token", "symbol": "fbt", "name": "FANBI TOKEN" }, - { "id": "fancy-games", "symbol": "fnc", "name": "Fancy Games" }, - { "id": "fandom-chain", "symbol": "kdc", "name": "Fandom Chain" }, - { "id": "fango", "symbol": "xfg", "name": "Fango" }, - { "id": "fangs", "symbol": "dragonland", "name": "Dragon Land Metaverse" }, - { "id": "fang-token", "symbol": "fang", "name": "FANG Token" }, - { "id": "fanitrade", "symbol": "fani", "name": "FaniTrade" }, - { "id": "fanscoin", "symbol": "fc", "name": "FansCoin" }, - { "id": "fans-of-doge", "symbol": "DogeFans", "name": "Fans of Doge" }, - { "id": "fanspel", "symbol": "fan", "name": "Fanspel" }, - { "id": "fans-squid", "symbol": "fst", "name": "Fans Squid" }, - { "id": "fanstime", "symbol": "fti", "name": "FansTime" }, - { "id": "fantasm-fsm", "symbol": "fsm", "name": "Fantasm FSM" }, - { "id": "fantasm-xftm", "symbol": "xftm", "name": "Fantasm XFTM" }, - { - "id": "fantastic-protocol-fxm-token", - "symbol": "fxm", - "name": "Fantastic Protocol FXM Token" - }, - { - "id": "fantastic-protocol-peg-avax-favax-token", - "symbol": "favax", - "name": "Fantastic Protocol Peg-AVAX (FAVAX) Token" - }, - { - "id": "fantastic-protocol-peg-ftm", - "symbol": "ftmx", - "name": "Fantastic Protocol Peg-FTM" - }, - { - "id": "fantastic-protocol-snowy-token", - "symbol": "snowy", - "name": "Fantastic Protocol SNOWY Token" - }, - { "id": "fantasy", "symbol": "ftsy", "name": "Fantasy" }, - { "id": "fantasy-doge", "symbol": "ftd", "name": "Fantasy Doge" }, - { "id": "fantasy-gold", "symbol": "fgc", "name": "Fantasy Gold" }, - { "id": "fantasy-monster", "symbol": "fts", "name": "Fantasy Monster" }, - { "id": "fantasy-world-gold", "symbol": "fwg", "name": "Fantasy World Gold" }, - { "id": "fanterra", "symbol": "fterra", "name": "FanTerra" }, - { "id": "fantohm", "symbol": "fhm", "name": "Fantohm" }, - { "id": "fantom", "symbol": "ftm", "name": "Fantom" }, - { "id": "fantom-cake", "symbol": "fatcake", "name": "Fantom Cake" }, - { "id": "fantom-doge", "symbol": "rip", "name": "Fantom Doge" }, - { "id": "fantom-ethprinter", "symbol": "fethp", "name": "FANTOM ETHPrinter" }, - { "id": "fantom-frens", "symbol": "ffs", "name": "Fantom Frens" }, - { "id": "fantomgo", "symbol": "ftg", "name": "fantomGO" }, - { - "id": "fantom-libero-financial", - "symbol": "flibero", - "name": "Fantom Libero Financial" - }, - { "id": "fantom-maker", "symbol": "fame", "name": "Fantom Maker" }, - { - "id": "fantom-moon-finance", - "symbol": "fmf", - "name": "Fantom-moon.finance" - }, - { "id": "fantom-oasis", "symbol": "ftmo", "name": "Fantom Oasis" }, - { - "id": "fantom-of-the-opera-apes", - "symbol": "fantomapes", - "name": "Fantom of the Opera Apes" - }, - { "id": "fantomstarter", "symbol": "fs", "name": "FantomStarter" }, - { - "id": "fantom-token-wormhole", - "symbol": "ftmet", - "name": "Fantom Token (Wormhole)" - }, - { "id": "fantom-usd", "symbol": "fusd", "name": "Fantom USD" }, - { - "id": "fantums-of-opera-token", - "symbol": "foo", - "name": "Fantums of Opera Token" - }, - { "id": "fanverse", "symbol": "fanv", "name": "FanVerse" }, - { "id": "fanzy", "symbol": "fx1", "name": "FANZY" }, - { "id": "faraland", "symbol": "fara", "name": "FaraLand" }, - { "id": "farmageddon", "symbol": "fg", "name": "Farmageddon" }, - { "id": "farmbit", "symbol": "fmb", "name": "Farmbit" }, - { "id": "farmerdoge", "symbol": "crop", "name": "FarmerDoge V3" }, - { "id": "farmers-only", "symbol": "fox", "name": "FoxSwap" }, - { "id": "farmersonly-onion", "symbol": "onion", "name": "FarmersOnly Onion" }, - { "id": "farmers-world-wood", "symbol": "fww", "name": "Farmers World Wood" }, - { "id": "farm-finance", "symbol": "fft", "name": "Farm Finance" }, - { "id": "farmland-protocol", "symbol": "far", "name": "Farmland Protocol" }, - { "id": "farm-planet", "symbol": "fpl", "name": "Farm Planet" }, - { "id": "farmpoly", "symbol": "poly", "name": "FarmPoly" }, - { "id": "farm-skylines", "symbol": "fsk", "name": "Farm Skylines" }, - { "id": "farmyield", "symbol": "famy", "name": "FarmYield" }, - { "id": "fashion-coin", "symbol": "fshn", "name": "Fashion Coin" }, - { - "id": "fast-food-wolf-game", - "symbol": "ffwool", - "name": "Fast Food Wolf Game" - }, - { "id": "fastmoon", "symbol": "fastmoon", "name": "FastMoon" }, - { "id": "fastswap", "symbol": "fast", "name": "FastSwap" }, - { "id": "fastswap-bsc", "symbol": "fast", "name": "Fastswap (BSC)" }, - { "id": "fatcake", "symbol": "fatcake", "name": "FatCake" }, - { "id": "fatcoin", "symbol": "fat", "name": "Fatcoin" }, - { "id": "fat-doge", "symbol": "foge", "name": "Fat Doge" }, - { "id": "fate-token", "symbol": "fate", "name": "Fate Token" }, - { "id": "fat-satoshi", "symbol": "fatoshi", "name": "Fat Satoshi" }, - { "id": "favecoin", "symbol": "fave", "name": "Favecoin" }, - { "id": "fawkes-mask", "symbol": "fmk", "name": "Fawkes Mask" }, - { "id": "fayre", "symbol": "fayre", "name": "Fayre" }, - { "id": "fbomb", "symbol": "bomb", "name": "fBOMB" }, - { - "id": "fc-barcelona-fan-token", - "symbol": "bar", - "name": "FC Barcelona Fan Token" - }, - { "id": "fc-porto", "symbol": "porto", "name": "FC Porto" }, - { "id": "fdoge-finance", "symbol": "fdoge", "name": "FDoge Finance" }, - { "id": "fds", "symbol": "fds", "name": "Fair Dollars" }, - { "id": "fear", "symbol": "fear", "name": "Fear" }, - { "id": "fear-token", "symbol": "fear", "name": "Fear Token" }, - { - "id": "fear-uncertainty-doubt", - "symbol": "fud", - "name": "Fear Uncertainty Doubt" - }, - { "id": "feathercoin", "symbol": "ftc", "name": "Feathercoin" }, - { "id": "fedoracoin", "symbol": "tips", "name": "Fedoracoin" }, - { "id": "fedora-gold", "symbol": "fed", "name": "Fedora Gold" }, - { "id": "feeder-finance", "symbol": "feed", "name": "Feeder Finance" }, - { "id": "feedeveryshiba", "symbol": "fes", "name": "FeedEveryShiba" }, - { "id": "feed-pups", "symbol": "fups", "name": "Feed Pups" }, - { "id": "feed-system", "symbol": "feedtk", "name": "Feed System" }, - { "id": "feed-token", "symbol": "feed", "name": "Feed Token" }, - { "id": "feelcoin", "symbol": "feel", "name": "FeelCoin" }, - { "id": "feg-token", "symbol": "feg", "name": "FEG Token" }, - { "id": "feg-token-bsc", "symbol": "feg", "name": "FEG Token BSC" }, - { "id": "feichang-niu", "symbol": "fcn", "name": "Feichang Niu" }, - { "id": "feisty-doge-nft", "symbol": "nfd", "name": "Feisty Doge NFT" }, - { "id": "fei-usd", "symbol": "fei", "name": "Fei USD" }, - { "id": "felix", "symbol": "FLX", "name": "Felix" }, - { "id": "fenerbahce-token", "symbol": "fb", "name": "Fenerbahçe Token" }, - { "id": "fenix-danjon", "symbol": "DJN", "name": "Fenix Danjon" }, - { "id": "fenix-finance", "symbol": "fenix", "name": "Fenix Finance" }, - { "id": "fenomy", "symbol": "fenomy", "name": "Fenomy" }, - { "id": "fera", "symbol": "fera", "name": "Fera" }, - { "id": "ferret", "symbol": "frt", "name": "Ferret" }, - { "id": "ferrum-network", "symbol": "frm", "name": "Ferrum Network" }, - { "id": "fertilizer", "symbol": "frt", "name": "Fertilizer" }, - { "id": "fesbnb", "symbol": "fesbnb", "name": "FesBNB" }, - { "id": "fess-chain", "symbol": "fess", "name": "Fesschain" }, - { "id": "fetch-ai", "symbol": "fet", "name": "Fetch.ai" }, - { "id": "fetch-inu", "symbol": "FINU", "name": "Fetch Inu" }, - { "id": "fetish-coin", "symbol": "fetish", "name": "Fetish Coin" }, - { "id": "feudalz-goldz", "symbol": "goldz", "name": "Feudalz Goldz" }, - { "id": "fewmans-gold", "symbol": "fewgo", "name": "Fewmans Gold" }, - { "id": "few-understand", "symbol": "few", "name": "Few Understand" }, - { "id": "feyorra", "symbol": "fey", "name": "Feyorra" }, - { "id": "fia-protocol", "symbol": "fia", "name": "FIA Protocol" }, - { "id": "fiat-dao-token", "symbol": "fdt", "name": "FIAT DAO Token" }, - { "id": "fibitpro-exchange", "symbol": "fbt", "name": "Fibitpro Exchange" }, - { "id": "fibodex", "symbol": "fibo", "name": "FiboDex" }, - { "id": "fibos", "symbol": "fo", "name": "FIBOS" }, - { "id": "fibo-token", "symbol": "fibo", "name": "FibSwap DEX" }, - { "id": "fidelis", "symbol": "fdls", "name": "FIDELIS" }, - { "id": "fidex-exchange", "symbol": "fex", "name": "FIDEX Exchange" }, - { "id": "fidira", "symbol": "fid", "name": "Fidira" }, - { "id": "fief", "symbol": "fief", "name": "Fief" }, - { "id": "fiestacoin", "symbol": "fiesta", "name": "FiestaCoin" }, - { "id": "fiftyonefifty", "symbol": "fifty", "name": "FIFTYONEFIFTY" }, - { "id": "filda", "symbol": "filda", "name": "Filda" }, - { "id": "filecash", "symbol": "fic", "name": "Filecash" }, - { "id": "filecoin", "symbol": "fil", "name": "Filecoin" }, - { - "id": "filecoin-standard-full-hashrate", - "symbol": "sfil", - "name": "Filecoin Standard Full Hashrate" - }, - { - "id": "filecoin-standard-hashrate-token", - "symbol": "filst", - "name": "Filecoin Standard Hashrate Token" - }, - { "id": "filenet", "symbol": "fn", "name": "Filenet" }, - { "id": "filestar", "symbol": "star", "name": "FileStar" }, - { "id": "filipcoin", "symbol": "fcp", "name": "Filipcoin" }, - { "id": "film-coin", "symbol": "fliks", "name": "Film Coin" }, - { "id": "fimi-market-inc", "symbol": "fimi", "name": "Fimi Market Inc." }, - { "id": "final-frontier", "symbol": "frnt", "name": "Final Frontier" }, - { "id": "finance-blocks", "symbol": "fbx", "name": "Finance Blocks" }, - { "id": "finance-vote", "symbol": "fvt", "name": "Finance Vote" }, - { - "id": "financial-intelligence-games", - "symbol": "fig", - "name": "Financial Intelligence Games" - }, - { - "id": "financial-intelligence-group-token", - "symbol": "atbfig", - "name": "Financial Intelligence Group Token" - }, - { - "id": "financial-investment-token", - "symbol": "fit", - "name": "FINANCIAL INVESTMENT TOKEN" - }, - { "id": "financio", "symbol": "fin", "name": "Financio" }, - { "id": "findora", "symbol": "fra", "name": "Findora" }, - { "id": "findshibby", "symbol": "fshibby", "name": "Findshibby" }, - { "id": "finexbox-token", "symbol": "fnb", "name": "FinexboxToken" }, - { "id": "fingerprints", "symbol": "prints", "name": "FingerprintsDAO" }, - { "id": "finminity", "symbol": "fmt", "name": "Finminity" }, - { "id": "fino-dao", "symbol": "fino", "name": "FINO DAO" }, - { "id": "fins-token", "symbol": "fins", "name": "Fins Token" }, - { "id": "finswap", "symbol": "fnsp", "name": "Finswap" }, - { "id": "fintab", "symbol": "fntb", "name": "FinTab" }, - { "id": "fintropy", "symbol": "fint", "name": "Fintropy" }, - { "id": "fintrux", "symbol": "ftx", "name": "FintruX" }, - { "id": "finxflo", "symbol": "fxf", "name": "FINXFLO" }, - { "id": "fio-protocol", "symbol": "fio", "name": "FIO Protocol" }, - { "id": "fira", "symbol": "fira", "name": "FIRA" }, - { "id": "firdaos", "symbol": "fdo", "name": "Firdaos" }, - { "id": "fireants", "symbol": "ants", "name": "FireAnts" }, - { "id": "fireball-2", "symbol": "fire", "name": "FireBall" }, - { "id": "firebird-finance", "symbol": "hope", "name": "Firebird.Finance" }, - { "id": "firebot", "symbol": "fbx", "name": "FireBot" }, - { "id": "fireflame-inu", "symbol": "fire", "name": "FireFlame Inu" }, - { "id": "fire-lotto", "symbol": "flot", "name": "Fire Lotto" }, - { "id": "fire-protocol", "symbol": "fire", "name": "Fire Protocol" }, - { "id": "firerocket", "symbol": "firerocket", "name": "FireRocket" }, - { "id": "firestarter", "symbol": "flame", "name": "FireStarter" }, - { "id": "firetoken", "symbol": "fire", "name": "FireToken" }, - { "id": "fire-token-2", "symbol": "fire", "name": "Fire Token" }, - { "id": "firezard", "symbol": "zard", "name": "FireZard" }, - { "id": "firmachain", "symbol": "fct", "name": "Firmachain" }, - { "id": "firstdoge", "symbol": "firstdoge", "name": "FirstDoge" }, - { - "id": "first-doge-finance", - "symbol": "fdoge", - "name": "First Doge Finance" - }, - { "id": "first-eleven", "symbol": "f11", "name": "First Eleven" }, - { "id": "first-ever-nft", "symbol": "fen", "name": "First Ever NFT" }, - { "id": "firsthare", "symbol": "firsthare", "name": "FirstHare" }, - { "id": "first-inu", "symbol": "finu", "name": "First Inu" }, - { "id": "firulais", "symbol": "firu", "name": "Firulais" }, - { - "id": "firulais-wallet-token", - "symbol": "FIWT", - "name": "Firulais Wallet Token" - }, - { "id": "fisco", "symbol": "fscc", "name": "FISCO Coin" }, - { "id": "fish-crypto", "symbol": "fico", "name": "Fish Crypto" }, - { "id": "fishervspirate", "symbol": "fvp", "name": "FisherVsPirate" }, - { "id": "fishing-town", "symbol": "fhtn", "name": "Fishing Town" }, - { - "id": "fishingtowngiltoken", - "symbol": "gil", - "name": "FishingTownGilToken" - }, - { "id": "fishy-tank-token", "symbol": "fte", "name": "Fishy Tank Token" }, - { "id": "fistbump", "symbol": "fist", "name": "Fistbump" }, - { "id": "fit-beat", "symbol": "ftb", "name": "Fit\u0026Beat" }, - { "id": "fitmin", "symbol": "ftm", "name": "Fitmin" }, - { "id": "fit-token", "symbol": "fit", "name": "FIT Token" }, - { "id": "five-balance", "symbol": "fbn", "name": "Fivebalance Coin" }, - { "id": "fixed-trade-coin", "symbol": "fxtc", "name": "Fixed Trade Coin" }, - { "id": "fk-coin", "symbol": "fk", "name": "FK Coin" }, - { "id": "flag-network", "symbol": "flag", "name": "Flag Network" }, - { "id": "flame", "symbol": "flame", "name": "FLAME" }, - { "id": "flamedefi", "symbol": "fdao", "name": "FlameDefi" }, - { - "id": "flamengo-fan-token", - "symbol": "mengo", - "name": "Flamengo Fan Token" - }, - { "id": "flamingo-finance", "symbol": "flm", "name": "Flamingo Finance" }, - { "id": "flappydoge", "symbol": "FLPD", "name": "FlappyDoge" }, - { "id": "flappy-floki", "symbol": "flaflo", "name": "Flappy Floki" }, - { "id": "flappy-shiba-inu", "symbol": "fsinu", "name": "Flappy Shiba Inu" }, - { "id": "flare-finance", "symbol": "exfi", "name": "Flare Finance" }, - { "id": "flare-token", "symbol": "1flr", "name": "Flare Token" }, - { "id": "flash-inu", "symbol": "flash", "name": "Flash Inu" }, - { "id": "flash-token", "symbol": "flash", "name": "Flash Loans" }, - { "id": "flash-token-2", "symbol": "FLASH", "name": "Flash Token" }, - { "id": "flashx-ultra", "symbol": "fsxu", "name": "FlashX Ultra" }, - { "id": "flavors-bsc", "symbol": "flvr", "name": "Flavors BSC" }, - { "id": "flesh-token", "symbol": "flesh", "name": "Flesh Token" }, - { "id": "fleta", "symbol": "fleta", "name": "FLETA" }, - { "id": "flex-coin", "symbol": "flex", "name": "FLEX Coin" }, - { "id": "flexq", "symbol": "flq", "name": "FlexQ" }, - { "id": "flex-usd", "symbol": "flexusd", "name": "flexUSD" }, - { "id": "flinch", "symbol": "fln", "name": "Flinch" }, - { "id": "flipper-token", "symbol": "flip", "name": "Flipper Token" }, - { "id": "flipstar", "symbol": "flip", "name": "FlipStar" }, - { "id": "flits", "symbol": "fls", "name": "Flits" }, - { "id": "flix", "symbol": "flix", "name": "Flix" }, - { "id": "flixxo", "symbol": "flixx", "name": "Flixxo" }, - { "id": "float-protocol", "symbol": "bank", "name": "Float Protocol" }, - { - "id": "float-protocol-float", - "symbol": "float", - "name": "Float Protocol: Float" - }, - { "id": "flock", "symbol": "flock", "name": "Flock" }, - { "id": "flokachu-token", "symbol": "flokachu", "name": "Flokachu Token" }, - { "id": "floki-adventure", "symbol": "fiat", "name": "Floki Adventure" }, - { "id": "flokibonk", "symbol": "flobo", "name": "FlokiBonk" }, - { "id": "flokibro", "symbol": "fbro", "name": "FlokiBro" }, - { "id": "flokicoke", "symbol": "FLOKICOKE", "name": "FlokiCoke" }, - { "id": "flokidoge", "symbol": "$floge", "name": "Flokidoge" }, - { - "id": "flokifrunkpuppy", - "symbol": "FLOKIFRUNKPUPPY", - "name": "FlokiFrunkPuppy" - }, - { "id": "flokigainz", "symbol": "gainz", "name": "FlokiGainz" }, - { "id": "floki-gold", "symbol": "flokigold", "name": "Floki Gold" }, - { "id": "flokigravity", "symbol": "flokig", "name": "FlokiGravity" }, - { "id": "floki-inu", "symbol": "floki", "name": "Floki Inu" }, - { "id": "flokikong", "symbol": "kong", "name": "FlokiKong" }, - { "id": "flokiloki", "symbol": "flokiloki", "name": "FlokiLoki" }, - { "id": "flokimars", "symbol": "flom", "name": "Flokimars" }, - { "id": "floki-meta", "symbol": "mfloki", "name": "Floki Meta" }, - { "id": "floki-millions", "symbol": "millions", "name": "Floki Millions" }, - { "id": "flokimon", "symbol": "fmon", "name": "FlokiMON" }, - { "id": "floki-monk", "symbol": "flokimonk", "name": "Floki Monk" }, - { "id": "flokimooni", "symbol": "flokim", "name": "Flokimooni" }, - { "id": "floki-musk", "symbol": "floki", "name": "Floki Musk" }, - { "id": "floki-one", "symbol": "floki", "name": "Floki One" }, - { "id": "flokipad", "symbol": "flokipad", "name": "FlokiPad" }, - { "id": "flokipetworld", "symbol": "FPET", "name": "Flokipetworld" }, - { "id": "floki-pup", "symbol": "flokipup", "name": "Floki Pup" }, - { "id": "flokirai", "symbol": "FLOKIRAI", "name": "Flokirai" }, - { "id": "flokirocket", "symbol": "rkf", "name": "FlokiRocket" }, - { "id": "floki-rocket", "symbol": "rloki", "name": "Floki Rocket" }, - { "id": "floki-shiba", "symbol": "fshib", "name": "Floki Shiba" }, - { "id": "flokishu", "symbol": "flishu", "name": "FloKishu" }, - { "id": "flokiswap", "symbol": "flokis", "name": "FlokiSwap" }, - { "id": "flokiverse", "symbol": "flokiv", "name": "Flokiverse" }, - { "id": "flokizap", "symbol": "flokiz", "name": "FlokiZap" }, - { "id": "floof", "symbol": "floof", "name": "FLOOF" }, - { "id": "floordao", "symbol": "floor", "name": "FloorDAO" }, - { "id": "florida-man", "symbol": "fman", "name": "Florida Man" }, - { "id": "florin", "symbol": "xfl", "name": "Florin" }, - { "id": "floshin", "symbol": "floshin", "name": "Floshin" }, - { "id": "flourish-coin", "symbol": "flrs", "name": "Flourish Coin" }, - { "id": "flourishing-ai-token", "symbol": "ai", "name": "Flourishing AI" }, - { "id": "flow", "symbol": "flow", "name": "Flow" }, - { "id": "flowchaincoin", "symbol": "flc", "name": "Flowchain" }, - { "id": "flowcom", "symbol": "fig", "name": "FlowCom" }, - { "id": "flower", "symbol": "flow", "name": "Flower" }, - { "id": "flower-solana", "symbol": "FLOW", "name": "Flower Solana" }, - { "id": "floyx", "symbol": "floyx", "name": "Floyx" }, - { "id": "fluffy-coin", "symbol": "fluf", "name": "Fluffy Coin" }, - { "id": "fluffy-inu", "symbol": "fluffy", "name": "Fluffy Inu" }, - { "id": "fluidfi", "symbol": "fluid", "name": "FluidFi" }, - { "id": "flurry", "symbol": "flurry", "name": "Flurry Finance" }, - { "id": "flux", "symbol": "flux", "name": "Datamine FLUX" }, - { "id": "fluxchain", "symbol": "flx", "name": "FLUXchain" }, - { "id": "flux-protocol", "symbol": "flux", "name": "Flux Protocol" }, - { "id": "flux-token", "symbol": "flx", "name": "Flux Token" }, - { "id": "flyapy", "symbol": "fly", "name": "FlyApy" }, - { "id": "flying-colours", "symbol": "ours", "name": "Flying Colours" }, - { "id": "flypaper", "symbol": "sticky", "name": "FlyPaper" }, - { "id": "flypme", "symbol": "fyp", "name": "FlypMe" }, - { "id": "fme", "symbol": "fme", "name": "FME" }, - { "id": "fm-gallery", "symbol": "fmg", "name": "FM Gallery" }, - { "id": "fnb-protocol", "symbol": "fnb", "name": "FNB Protocol" }, - { "id": "fndz-token", "symbol": "fndz", "name": "FNDZ Token" }, - { "id": "fnkcom", "symbol": "fnk", "name": "Fnk.com" }, - { "id": "foam-protocol", "symbol": "foam", "name": "FOAM" }, - { "id": "fodl-finance", "symbol": "fodl", "name": "Fodl Finance" }, - { "id": "foho-coin", "symbol": "foho", "name": "Foho Coin" }, - { "id": "foincoin", "symbol": "foin", "name": "Foin" }, - { "id": "folder-protocol", "symbol": "fol", "name": "Folder Protocol" }, - { "id": "follow-friday", "symbol": "ff", "name": "Follow Friday" }, - { "id": "follow-token", "symbol": "folo", "name": "Alpha Impact" }, - { "id": "fomo-baby", "symbol": "fomobaby", "name": "FOMO BABY" }, - { - "id": "fomo-chronicles-manga", - "symbol": "otaku", - "name": "FOMO Chronicles Manga" - }, - { "id": "fomoeth", "symbol": "fomoeth", "name": "FomoETH" }, - { "id": "fomo-labs", "symbol": "fomo", "name": "Fomo Labs" }, - { "id": "fomo-pot", "symbol": "fomo", "name": "FOMO Pot" }, - { "id": "fompound", "symbol": "fomp", "name": "Fompound" }, - { "id": "fone", "symbol": "fone", "name": "Fone" }, - { "id": "font", "symbol": "font", "name": "Font" }, - { "id": "food-bank", "symbol": "food", "name": "Food Bank" }, - { "id": "foodchain-global", "symbol": "food", "name": "FoodChain Global" }, - { - "id": "food-farmer-finance", - "symbol": "fff", - "name": "Food Farmer Finance" - }, - { "id": "football-coin", "symbol": "xfc", "name": "Football Coin" }, - { - "id": "football-fantasy-pro", - "symbol": "fanta", - "name": "Football Fantasy Pro" - }, - { "id": "footballgo", "symbol": "fgsport", "name": "FootBallGo" }, - { "id": "footballstars", "symbol": "fts", "name": "FootballStars" }, - { "id": "footie-plus", "symbol": "footie", "name": "Footie Plus" }, - { "id": "forcecowboy", "symbol": "fcb", "name": "ForceCowBoy" }, - { "id": "force-for-fast", "symbol": "fff", "name": "Force For Fast" }, - { "id": "force-protocol", "symbol": "for", "name": "ForTube" }, - { "id": "forefront", "symbol": "ff", "name": "Forefront" }, - { "id": "foresight", "symbol": "fors", "name": "Foresight" }, - { "id": "forest-knight", "symbol": "knight", "name": "Forest Knight" }, - { "id": "foreverblast", "symbol": "feb", "name": "ForeverBlast" }, - { "id": "foreverbnb", "symbol": "fbnb", "name": "ForeverBNB" }, - { "id": "foreverfomo", "symbol": "ForeverFOMO", "name": "ForeverFOMO" }, - { "id": "forever-pump", "symbol": "ForeverPump", "name": "Forever Pump" }, - { "id": "forexcoin", "symbol": "forex", "name": "FOREXCOIN" }, - { "id": "forge-finance", "symbol": "forge", "name": "Forge Finance" }, - { "id": "forint", "symbol": "forint", "name": "Forint" }, - { - "id": "for-loot-and-glory", - "symbol": "FLAG", - "name": "For Loot And Glory" - }, - { "id": "formation-fi", "symbol": "form", "name": "Formation FI" }, - { "id": "formula", "symbol": "fml", "name": "FormulA" }, - { "id": "forrest-pump", "symbol": "fpump", "name": "Forrest Pump" }, - { "id": "forta", "symbol": "forta", "name": "Forta" }, - { "id": "forthbox", "symbol": "fbx", "name": "ForthBox" }, - { "id": "fortknox", "symbol": "knox", "name": "FortKnox" }, - { "id": "fortknoxter", "symbol": "fkx", "name": "FortKnoxster" }, - { "id": "fortress", "symbol": "fts", "name": "Fortress Loans" }, - { "id": "fortressdao", "symbol": "fort", "name": "Fortress" }, - { "id": "fortuna", "symbol": "fota", "name": "Fortuna" }, - { - "id": "fortuna-sittard-fan-token", - "symbol": "FOR", - "name": "Fortuna Sittard Fan Token" - }, - { "id": "fortune", "symbol": "fortune", "name": "Fortune" }, - { "id": "fortune-finance", "symbol": "frtn", "name": "Fortune Finance" }, - { "id": "forus", "symbol": "fors", "name": "Forus" }, - { "id": "forward", "symbol": "forward", "name": "Forward" }, - { "id": "fossil", "symbol": "fossil", "name": "Fossil" }, - { "id": "foundation-eth", "symbol": "feth", "name": "Foundation ETH" }, - { - "id": "foundrydao-logistics", - "symbol": "fry", - "name": "FoundryDAO Logistics" - }, - { "id": "fountain-protocol", "symbol": "FTP", "name": "Fountain Protocol" }, - { "id": "fox", "symbol": "fox", "name": "Fox" }, - { "id": "foxboy", "symbol": "fbb", "name": "Foxboy" }, - { "id": "foxdcoin", "symbol": "foxd", "name": "Foxdcoin" }, - { "id": "fox-finance", "symbol": "fox", "name": "Fox Finance" }, - { "id": "foxgirl", "symbol": "foxgirl", "name": "FoxGirl" }, - { "id": "fox-trading-token", "symbol": "foxt", "name": "Fox Trading Token" }, - { "id": "foxy-equilibrium", "symbol": "foxy", "name": "Foxy Equilibrium" }, - { "id": "fqswap", "symbol": "fqs", "name": "FqSwap" }, - { "id": "fractal", "symbol": "fcl", "name": "Fractal" }, - { "id": "fraction", "symbol": "fraction", "name": "Fraction" }, - { - "id": "fractionalized-smb-2367", - "symbol": "daojones", - "name": "Fractionalized SMB-2367" - }, - { - "id": "fractionalized-wave-999", - "symbol": "wav", - "name": "Fractionalized WAVE-999" - }, - { "id": "fragments-of-arker", "symbol": "foa", "name": "Fragments of Arker" }, - { "id": "fraktal", "symbol": "frak", "name": "Fraktal" }, - { - "id": "fraktionalized-thug-2856", - "symbol": "thug", - "name": "Fraktionalized THUG 2856" - }, - { "id": "frakt-token", "symbol": "frkt", "name": "FRAKT Token" }, - { "id": "france-rev-finance", "symbol": "FRF", "name": "France REV Finance" }, - { - "id": "frankenstein-finance", - "symbol": "frank", - "name": "Frankenstein Finance" - }, - { "id": "frank-inu", "symbol": "frank", "name": "Frank Inu" }, - { "id": "franklin", "symbol": "fly", "name": "Franklin" }, - { "id": "frat", "symbol": "frat", "name": "Frat" }, - { "id": "frax", "symbol": "frax", "name": "Frax" }, - { "id": "frax-price-index", "symbol": "fpi", "name": "Frax Price Index" }, - { - "id": "frax-price-index-share", - "symbol": "fpis", - "name": "Frax Price Index Share" - }, - { "id": "frax-share", "symbol": "fxs", "name": "Frax Share" }, - { - "id": "frax-share-wormhole", - "symbol": "fxs", - "name": "Frax Share (Wormhole)" - }, - { "id": "frax-wormhole", "symbol": "frax", "name": "Frax (Wormhole)" }, - { "id": "fredenergy", "symbol": "fred", "name": "FRED Energy" }, - { "id": "freecash", "symbol": "fch", "name": "Freecash" }, - { "id": "freedom", "symbol": "fdm", "name": "Freedom" }, - { "id": "freedom-22-dao", "symbol": "free", "name": "Freedom 22 DAO" }, - { "id": "freedomcoin", "symbol": "FREED", "name": "Freedomcoin" }, - { "id": "freedom-coin", "symbol": "free", "name": "FREEdom coin" }, - { "id": "freedom-god-dao", "symbol": "fgd", "name": "Freedom God DAO" }, - { - "id": "freedom-jobs-business", - "symbol": "$fjb", - "name": "Freedom. Jobs. Business" - }, - { "id": "freedom-reserve", "symbol": "fr", "name": "Freedom Reserve" }, - { "id": "freela", "symbol": "frel", "name": "Freela" }, - { "id": "freeliquid", "symbol": "fl", "name": "Freeliquid" }, - { "id": "freemoon", "symbol": "freemoon", "name": "Freemoon" }, - { "id": "freemoon-token", "symbol": "freemoon", "name": "Freemoon Token" }, - { "id": "free-novak", "symbol": "FREEDOM", "name": "Free Novak" }, - { "id": "freerossdao", "symbol": "free", "name": "FreeRossDAO" }, - { "id": "freeway-token", "symbol": "fwt", "name": "Freeway Token" }, - { "id": "freicoin", "symbol": "frc", "name": "Freicoin" }, - { - "id": "freight-trust-network", - "symbol": "edi", - "name": "Freight Trust Network" - }, - { "id": "fren", "symbol": "fren", "name": "FREN" }, - { - "id": "french-connection-finance", - "symbol": "fcf", - "name": "French Connection Finance" - }, - { - "id": "french-digital-reserve", - "symbol": "fdr", - "name": "French Digital Reserve" - }, - { "id": "frenchie", "symbol": "fren", "name": "Frenchie" }, - { "id": "frencoin", "symbol": "fren", "name": "FrenCoin" }, - { "id": "frens", "symbol": "frens", "name": "Frens" }, - { "id": "frens-token", "symbol": "frens", "name": "Frens Token" }, - { "id": "frenzy", "symbol": "fzy", "name": "Frenzy" }, - { "id": "freshcut-diamond", "symbol": "fcd", "name": "FreshCut Diamond" }, - { "id": "freyala", "symbol": "xya", "name": "GameFi Crossing" }, - { - "id": "friends-with-benefits-pro", - "symbol": "fwb", - "name": "Friends With Benefits Pro" - }, - { "id": "friendz", "symbol": "fdz", "name": "Friendz" }, - { "id": "friesdao", "symbol": "fries", "name": "friesDAO" }, - { "id": "frieza-inu", "symbol": "frinu", "name": "Frieza Inu" }, - { "id": "fringe-finance", "symbol": "frin", "name": "Fringe Finance" }, - { "id": "frmx-token", "symbol": "frmx", "name": "FRMx Token" }, - { "id": "frog", "symbol": "frog", "name": "Frog" }, - { "id": "frogdao-dime", "symbol": "fdd", "name": "FrogDAO Dime" }, - { "id": "froge-finance", "symbol": "frogex", "name": "FrogeX" }, - { "id": "froggies-token", "symbol": "FROGGIES", "name": "Froggies" }, - { "id": "frog-nation-farm", "symbol": "frog", "name": "Frog Nation Farm" }, - { "id": "frogswap", "symbol": "frog", "name": "FrogSwap" }, - { "id": "frogzilla", "symbol": "FZL", "name": "FrogZilla" }, - { "id": "frontier-token", "symbol": "front", "name": "Frontier" }, - { - "id": "frontier-token-wormhole", - "symbol": "front", - "name": "Frontier Token (Wormhole)" - }, - { "id": "front-row", "symbol": "frr", "name": "Frontrow" }, - { "id": "frost", "symbol": "frost", "name": "Frost" }, - { "id": "frosted-cake", "symbol": "FROSTEDCAKE", "name": "Frosted Cake" }, - { "id": "frosty-floki", "symbol": "FrostyFloki", "name": "Frosty Floki" }, - { - "id": "frosty-floki-v2", - "symbol": "frostyfloki", - "name": "Frosty Floki V2" - }, - { "id": "froyo-games", "symbol": "froyo", "name": "Froyo Games" }, - { "id": "frozencake", "symbol": "frozen", "name": "FrozenCake" }, - { "id": "frozentomb", "symbol": "ftomb", "name": "Frozentomb" }, - { "id": "frozentomb-lot", "symbol": "flot", "name": "Frozentomb LOT" }, - { "id": "fruit", "symbol": "fruit", "name": "Fruit" }, - { "id": "fruit-fighters", "symbol": "foofight", "name": "Fruit Fighters" }, - { "id": "fruits", "symbol": "frts", "name": "Fruits" }, - { "id": "frz-solar-system", "symbol": "frzss", "name": "Frz Solar System" }, - { "id": "fsd-coin", "symbol": "fsdcoin", "name": "FSD Coin" }, - { "id": "fsn", "symbol": "fsn", "name": "FUSION" }, - { "id": "fsw-token", "symbol": "fsw", "name": "Falconswap" }, - { "id": "ftm-guru", "symbol": "elite", "name": "ftm.guru" }, - { "id": "ftmlaunch", "symbol": "ftml", "name": "FTMlaunch" }, - { "id": "ftmpay", "symbol": "ftmp", "name": "FTMPay" }, - { "id": "ftm-pup-token", "symbol": "fpup", "name": "FTM PUP Token" }, - { "id": "ftribe-fighters", "symbol": "f2c", "name": "Ftribe Fighters" }, - { "id": "ftx-token", "symbol": "ftt", "name": "FTX Token" }, - { "id": "ftx-wormhole", "symbol": "ftt", "name": "FTX (Wormhole)" }, - { "id": "fubuki", "symbol": "fubuki", "name": "Fubuki" }, - { "id": "fudcoin-official", "symbol": "fud", "name": "FUDcoin Official" }, - { "id": "fuel-token", "symbol": "fuel", "name": "Jetfuel Finance" }, - { "id": "fufu", "symbol": "fufu", "name": "Fufu" }, - { "id": "fuji", "symbol": "fuji", "name": "Fuji" }, - { "id": "fullmetal-inu", "symbol": "fma", "name": "FullMetal Inu" }, - { "id": "fuma-finance", "symbol": "fuma", "name": "Fuma Finance" }, - { "id": "fumoney", "symbol": "fum", "name": "FUMoney" }, - { "id": "functionland", "symbol": "fula", "name": "Functionland" }, - { "id": "fundamenta", "symbol": "fmta", "name": "Fundamenta" }, - { "id": "funder-one", "symbol": "fundx", "name": "Funder One" }, - { "id": "fund-of-yours", "symbol": "foy", "name": "Fund Of Yours" }, - { "id": "fundum", "symbol": "fnd", "name": "Fundum" }, - { "id": "funfair", "symbol": "fun", "name": "FUNToken" }, - { "id": "fungie-dao", "symbol": "fng", "name": "Fungie DAO" }, - { "id": "furucombo", "symbol": "combo", "name": "Furucombo" }, - { "id": "furukuru", "symbol": "fuku", "name": "Furukuru" }, - { "id": "fuse-doge", "symbol": "fDoge", "name": "Fuse Doge" }, - { "id": "fuse-dollar", "symbol": "fusd", "name": "Fuse Dollar" }, - { "id": "fusefi", "symbol": "volt", "name": "Voltage Finance" }, - { "id": "fuse-network-token", "symbol": "fuse", "name": "Fuse" }, - { "id": "fusible", "symbol": "fusii", "name": "Fusible" }, - { "id": "futura-finance", "symbol": "fft", "name": "Futura Finance" }, - { "id": "futurax", "symbol": "ftxt", "name": "FUTURAX" }, - { "id": "future", "symbol": "ftr", "name": "Future" }, - { "id": "future1coin", "symbol": "f1c", "name": "Future1Coin" }, - { "id": "futurecoin", "symbol": "future", "name": "FutureCoin" }, - { "id": "futurefi", "symbol": "fufi", "name": "FutureFi" }, - { - "id": "future-real-estate-token", - "symbol": "fret", - "name": "Future Real Estate Token" - }, - { "id": "futurescash", "symbol": "fct", "name": "FuturesCash" }, - { "id": "futurescoin", "symbol": "fc", "name": "FuturesCoin" }, - { "id": "future-star", "symbol": "fstar", "name": "Future Star" }, - { "id": "futureswap", "symbol": "fst", "name": "Futureswap" }, - { "id": "futurocoin", "symbol": "fto", "name": "FuturoCoin" }, - { "id": "fuze-token", "symbol": "fuze", "name": "FUZE Token" }, - { "id": "fuzex", "symbol": "fxt", "name": "FuzeX" }, - { "id": "fuzzballs", "symbol": "fuzz", "name": "FuzzBalls" }, - { "id": "fuzz-finance", "symbol": "fuzz", "name": "Fuzz Finance" }, - { "id": "fwar-finance", "symbol": "fwt", "name": "Fwar Finance" }, - { "id": "fx-coin", "symbol": "fx", "name": "Function X" }, - { "id": "fxpay", "symbol": "fxp", "name": "FXPay" }, - { "id": "fxt-token", "symbol": "fxt", "name": "FXT Token" }, - { "id": "fxwallet", "symbol": "fxl", "name": "FXWallet" }, - { "id": "fydcoin", "symbol": "fyd", "name": "FYDcoin" }, - { "id": "fyooz", "symbol": "fyz", "name": "Fyooz" }, - { - "id": "g2-crypto-gaming-lottery", - "symbol": "g2", - "name": "G2 Crypto Gaming \u0026 Lottery" - }, - { "id": "g999", "symbol": "g999", "name": "G999" }, - { "id": "gabecoin", "symbol": "gabecoin", "name": "Gabecoin" }, - { "id": "gacube", "symbol": "gac", "name": "GACUBE" }, - { "id": "gadget-war", "symbol": "gwar", "name": "Gadget War" }, - { "id": "gafa", "symbol": "gafa", "name": "Gafa" }, - { "id": "gaia-dao", "symbol": "gaia", "name": "Gaia DAO" }, - { "id": "gaia-everworld", "symbol": "gaia", "name": "Gaia Everworld" }, - { "id": "gaias-tears", "symbol": "dfktears", "name": "Gaia's Tears" }, - { "id": "gain", "symbol": "gain", "name": "GainPool" }, - { "id": "gain-protocol", "symbol": "gain", "name": "Gain Protocol" }, - { "id": "gains", "symbol": "gains", "name": "Gains" }, - { "id": "gains-farm", "symbol": "gfarm2", "name": "Gains Farm" }, - { "id": "gains-network", "symbol": "gns", "name": "Gains Network" }, - { "id": "gaj", "symbol": "gaj", "name": "Gaj Finance" }, - { "id": "gala", "symbol": "gala", "name": "Gala" }, - { - "id": "galactic-arena-the-nftverse", - "symbol": "gan", - "name": "Galactic Arena: The NFTverse" - }, - { "id": "galactrum", "symbol": "ore", "name": "Galactrum" }, - { - "id": "galatasaray-fan-token", - "symbol": "gal", - "name": "Galatasaray Fan Token" - }, - { - "id": "galatic-kitty-fighters", - "symbol": "gkf", - "name": "Galatic Kitty Fighters" - }, - { "id": "gala-wormhole", "symbol": "gala", "name": "Gala (Wormhole)" }, - { "id": "galaxium", "symbol": "glxm", "name": "Galaxium" }, - { "id": "galaxy-adventure", "symbol": "gla", "name": "Galaxy Adventure" }, - { "id": "galaxy-blitz", "symbol": "mit", "name": "Galaxy Blitz" }, - { "id": "galaxybusd", "symbol": "galaxy", "name": "GalaxyBUSD" }, - { "id": "galaxycoin", "symbol": "Galaxy", "name": "GalaxyCoin" }, - { "id": "galaxy-coin", "symbol": "glxc", "name": "Galaxy Coin" }, - { "id": "galaxy-doge", "symbol": "galaxydoge", "name": "Galaxy Doge" }, - { "id": "galaxy-fight-club", "symbol": "gcoin", "name": "Galaxy Fight Club" }, - { "id": "galaxy-force-x", "symbol": "gfx", "name": "Galaxy Force X" }, - { "id": "galaxygoggle", "symbol": "gg", "name": "GalaxyGoggle" }, - { "id": "galaxy-heroes-coin", "symbol": "ghc", "name": "Galaxy Heroes Coin" }, - { "id": "galaxy-network", "symbol": "gnc", "name": "Galaxy Network" }, - { "id": "galaxy-surge", "symbol": "GALS", "name": "Galaxy Surge" }, - { "id": "galaxy-war", "symbol": "gwt", "name": "Galaxy War" }, - { "id": "galeon", "symbol": "galeon", "name": "Galeon" }, - { "id": "gallant", "symbol": "gal", "name": "Gallant" }, - { "id": "gamb", "symbol": "gmb", "name": "GAMB" }, - { "id": "gambit", "symbol": "gmt", "name": "Gambit" }, - { "id": "gambler-shiba", "symbol": "gshiba", "name": "Gambler Shiba" }, - { "id": "game", "symbol": "gtc", "name": "Game" }, - { "id": "game-ace-token", "symbol": "gat", "name": "Game Ace Token" }, - { "id": "gamebox", "symbol": "gamebox", "name": "Gamebox" }, - { "id": "game-city", "symbol": "gmci", "name": "Game City" }, - { "id": "game-coin", "symbol": "gmex", "name": "Game Coin" }, - { "id": "gamecredits", "symbol": "game", "name": "GameCredits" }, - { "id": "gamecrypt", "symbol": "gamecrypt", "name": "GameCrypt" }, - { "id": "gamedao", "symbol": "dao", "name": "GameDAO" }, - { "id": "gamee", "symbol": "gmee", "name": "GAMEE" }, - { "id": "game-fantasy-token", "symbol": "gft", "name": "Game Fantasy Token" }, - { "id": "game-fanz", "symbol": "gfn", "name": "Game Fanz" }, - { "id": "gamefi", "symbol": "gafi", "name": "GameFi" }, - { "id": "gamefi-collection", "symbol": "gfc", "name": "GameFi Collection" }, - { "id": "gamefi-token", "symbol": "gfi", "name": "GameFi Token" }, - { "id": "gameflip", "symbol": "flp", "name": "Gameflip" }, - { "id": "game-frag", "symbol": "frag", "name": "Game-Frag" }, - { "id": "gamenft", "symbol": "gnft", "name": "GameNFT" }, - { "id": "gameology", "symbol": "gmy", "name": "Gameology" }, - { "id": "game-on-players", "symbol": "gopx", "name": "Game On Players" }, - { "id": "gamepad", "symbol": "gpad", "name": "Gamepad" }, - { "id": "gamer", "symbol": "gmr", "name": "GAMER" }, - { "id": "gamercoin", "symbol": "ghx", "name": "GamerCoin" }, - { "id": "gamerse", "symbol": "lfg", "name": "Gamerse" }, - { "id": "gamers-yield", "symbol": "gy", "name": "Gamers Yield" }, - { "id": "gamesafe", "symbol": "gamesafe", "name": "Gamesafe" }, - { "id": "gamespad", "symbol": "gmpd", "name": "GamesPad" }, - { "id": "gamesta", "symbol": "gsg", "name": "Gamesta" }, - { "id": "gamestar-exchange", "symbol": "gms", "name": "GameStar" }, - { "id": "game-stars", "symbol": "gst", "name": "Game Stars" }, - { "id": "gamestarter", "symbol": "game", "name": "Gamestarter" }, - { "id": "gamestation", "symbol": "gamer", "name": "GameStation" }, - { "id": "gamestop-finance", "symbol": "gme", "name": "GameStop Finance" }, - { - "id": "gamestop-tokenized-stock-defichain", - "symbol": "dgme", - "name": "GameStop Tokenized Stock Defichain" - }, - { "id": "gameswap-org", "symbol": "gswap", "name": "Gameswap" }, - { "id": "game-tree", "symbol": "gtcoin", "name": "Game Tree" }, - { "id": "gameverse", "symbol": "gmv", "name": "GameVerse" }, - { "id": "gamex", "symbol": "gmx", "name": "GameX" }, - { - "id": "game-x-change-potion", - "symbol": "gxp", - "name": "Game X Change Potion" - }, - { "id": "gameyoo", "symbol": "gyc", "name": "GameYoo" }, - { "id": "gamezone", "symbol": "gzone", "name": "GameZone" }, - { "id": "gami", "symbol": "gami", "name": "Gami" }, - { "id": "gamifi", "symbol": "gmi", "name": "GamiFi" }, - { "id": "gaming-doge", "symbol": "gamingdoge", "name": "Gaming Doge" }, - { "id": "gamingshiba", "symbol": "gamingshiba", "name": "GamingShiba" }, - { "id": "gamium", "symbol": "gmm", "name": "Gamium" }, - { "id": "gami-world", "symbol": "gami", "name": "GAMI World" }, - { "id": "gamma", "symbol": "gamma", "name": "Gamma" }, - { "id": "gamma-strategies", "symbol": "gamma", "name": "Gamma Strategies" }, - { "id": "gamma-token", "symbol": "gam", "name": "Gamma Token" }, - { "id": "gamyfi-token", "symbol": "gfx", "name": "GamyFi Token" }, - { "id": "ganesha-token", "symbol": "gnsh", "name": "Ganesha Token" }, - { "id": "gangstabet", "symbol": "gbet", "name": "GangstaBet" }, - { "id": "gan-punks", "symbol": "gpunks20", "name": "Gan Punks" }, - { "id": "ganymede", "symbol": "gany", "name": "Ganymede" }, - { "id": "gapcoin", "symbol": "gap", "name": "Gapcoin" }, - { - "id": "gard-governance-token", - "symbol": "ggt", - "name": "GARD Governance Token" - }, - { "id": "garfield-token", "symbol": "garfield", "name": "Garfield Token" }, - { "id": "gari-network", "symbol": "gari", "name": "Gari Network" }, - { "id": "garlic", "symbol": "GRLC", "name": "Garlic" }, - { "id": "garlicoin", "symbol": "grlc", "name": "Garlicoin" }, - { "id": "gas", "symbol": "gas", "name": "Gas" }, - { "id": "gas-dao", "symbol": "gas", "name": "Gas DAO" }, - { "id": "gasp", "symbol": "gasp", "name": "gAsp" }, - { "id": "gastoken", "symbol": "gst2", "name": "GasToken" }, - { "id": "gastream", "symbol": "gstrm", "name": "GaStream" }, - { "id": "gastrocoin", "symbol": "gtc", "name": "GastroCoin" }, - { "id": "gatechain-token", "symbol": "gt", "name": "GateToken" }, - { - "id": "gatechain-wormhole", - "symbol": "gt", - "name": "GateChain (Wormhole)" - }, - { "id": "gatenet", "symbol": "gate", "name": "GATENet" }, - { "id": "gates-of-ethernity", "symbol": "goe", "name": "Gates of Ethernity" }, - { "id": "gateway-protocol", "symbol": "gwp", "name": "Gateway Protocol" }, - { "id": "gather", "symbol": "gth", "name": "Gather" }, - { "id": "gatorswap", "symbol": "gator", "name": "GatorSwap" }, - { "id": "gatsby-inu", "symbol": "gatsbyinu", "name": "Gatsby Inu" }, - { "id": "gauntlet", "symbol": "gau", "name": "Gauntlet" }, - { "id": "gaur-money", "symbol": "gaur", "name": "Gaur Money" }, - { "id": "gaur-shares", "symbol": "gshare", "name": "Gaur Shares" }, - { "id": "gazetv", "symbol": "gaze", "name": "GazeTV" }, - { "id": "gbox", "symbol": "gbox", "name": "GBOX" }, - { "id": "gbrick", "symbol": "gbx", "name": "Gbrick" }, - { "id": "gcn-coin", "symbol": "gcn", "name": "GCN Coin" }, - { - "id": "gdao-governance-vault", - "symbol": "xgdao", - "name": "GDAO Governance Vault" - }, - { "id": "gdoge-finance", "symbol": "gdoge", "name": "GDOGE Finance" }, - { "id": "gear", "symbol": "gear", "name": "Gear" }, - { "id": "gearbox", "symbol": "gear", "name": "Gearbox" }, - { "id": "geeq", "symbol": "GEEQ", "name": "GEEQ" }, - { "id": "gegem", "symbol": "geg", "name": "GeGem" }, - { "id": "geist-dai", "symbol": "gdai", "name": "Geist Dai" }, - { "id": "geist-eth", "symbol": "geth", "name": "Geist ETH" }, - { "id": "geist-finance", "symbol": "geist", "name": "Geist Finance" }, - { "id": "geist-ftm", "symbol": "gftm", "name": "Geist FTM" }, - { "id": "geist-fusdt", "symbol": "gfusdt", "name": "Geist fUSDT" }, - { "id": "geist-usdc", "symbol": "gusdc", "name": "Geist USDC" }, - { "id": "geist-wbtc", "symbol": "gwbtc", "name": "Geist WBTC" }, - { "id": "gelato", "symbol": "gel", "name": "Gelato" }, - { "id": "geld-finance", "symbol": "geldf", "name": "GELD Finance" }, - { "id": "gembites", "symbol": "gbts", "name": "GemBites" }, - { "id": "gemguardian", "symbol": "gemg", "name": "GemGuardian" }, - { "id": "gemini-dollar", "symbol": "gusd", "name": "Gemini Dollar" }, - { "id": "gemit-app", "symbol": "gemit", "name": "GEMIT.app" }, - { "id": "gemlink", "symbol": "glink", "name": "GemLink" }, - { - "id": "gemma-extending-tech", - "symbol": "gxt", - "name": "Gemma Extending Tech" - }, - { "id": "gemmine", "symbol": "gemmine", "name": "GemMine" }, - { "id": "gempay", "symbol": "gpay", "name": "GemPay" }, - { "id": "gems-2", "symbol": "gem", "name": "Gems" }, - { "id": "gemsgg", "symbol": "gems", "name": "GEMSgg" }, - { "id": "gem-token", "symbol": "gem", "name": "Gem Token" }, - { "id": "gemuni", "symbol": "geni", "name": "GemUni" }, - { "id": "genaro-network", "symbol": "gnx", "name": "Genaro Network" }, - { "id": "genart", "symbol": "genart", "name": "GENART" }, - { "id": "gencoin-capital", "symbol": "gencap", "name": "GenCoin Capital" }, - { "id": "gene", "symbol": "gene", "name": "Gene" }, - { "id": "genebank-token", "symbol": "gnbt", "name": "Genebank Token" }, - { - "id": "general-attention-currency", - "symbol": "xac", - "name": "General Attention Currency" - }, - { - "id": "generational-wealth-society", - "symbol": "gws", - "name": "Generational Wealth Society" - }, - { - "id": "generation-of-yield", - "symbol": "ygy", - "name": "Generation of Yield" - }, - { "id": "genes-chain", "symbol": "genes", "name": "GENES Chain" }, - { "id": "genesis-mana", "symbol": "mana", "name": "Genesis Mana" }, - { "id": "genesis-nft", "symbol": "gnss", "name": "Genesis NFT" }, - { "id": "genesis-pool", "symbol": "gpool", "name": "Genesis Pool" }, - { "id": "genesis-shards", "symbol": "gs", "name": "Genesis Shards" }, - { "id": "genesis-token", "symbol": "gent", "name": "Genesis Token" }, - { "id": "genesis-vision", "symbol": "gvt", "name": "Genesis Vision" }, - { "id": "genesis-worlds", "symbol": "genesis", "name": "Genesis Worlds" }, - { "id": "genesisx", "symbol": "xgs", "name": "GenesisX" }, - { "id": "genesysgo-shadow", "symbol": "shdw", "name": "GenesysGo Shadow" }, - { "id": "genexi", "symbol": "gxi", "name": "Genexi" }, - { "id": "gengar-token", "symbol": "gengar", "name": "Gengar Token" }, - { "id": "genie-protocol", "symbol": "gnp", "name": "Genie Protocol" }, - { "id": "genius-coin", "symbol": "genius", "name": "Genius Coin" }, - { "id": "genius-yield", "symbol": "gens", "name": "Genius Yield" }, - { "id": "genix", "symbol": "genix", "name": "Genix" }, - { "id": "gennix", "symbol": "gnnx", "name": "Gennix" }, - { "id": "genomesdao", "symbol": "$gene", "name": "GenomesDAO" }, - { "id": "genopets", "symbol": "gene", "name": "Genopets" }, - { "id": "genre", "symbol": "genre", "name": "GENRE" }, - { "id": "genshinflokiinu", "symbol": "gfloki", "name": "GenshinFlokiInu" }, - { "id": "genshin-nft", "symbol": "genshin", "name": "Genshin NFT" }, - { "id": "genshinshibinu", "symbol": "gshib", "name": "GenshinShibInu" }, - { "id": "genshiro", "symbol": "gens", "name": "Genshiro" }, - { - "id": "gensokishis-metaverse", - "symbol": "mv", - "name": "GensoKishi Metaverse" - }, - { "id": "gentarium", "symbol": "gtm", "name": "Gentarium" }, - { "id": "genx", "symbol": "genx", "name": "GENX" }, - { "id": "geocoin", "symbol": "geo", "name": "Geocoin" }, - { "id": "geodb", "symbol": "geo", "name": "GeoDB" }, - { "id": "geojam", "symbol": "jam", "name": "Geojam" }, - { "id": "geopoly", "symbol": "geo$", "name": "Geopoly" }, - { "id": "gera-coin", "symbol": "gera", "name": "Gera Coin" }, - { "id": "gerowallet", "symbol": "gero", "name": "GeroWallet" }, - { "id": "get-token", "symbol": "get", "name": "GET Protocol" }, - { "id": "geyser", "symbol": "gysr", "name": "Geyser" }, - { "id": "geysercoin", "symbol": "gsr", "name": "GeyserCoin" }, - { "id": "gforce", "symbol": "gfce", "name": "GFORCE" }, - { "id": "gg-coin", "symbol": "ggc", "name": "Global Game Coin" }, - { "id": "gg-token", "symbol": "ggtk", "name": "GG Token" }, - { "id": "ghibli-inu", "symbol": "ghibli", "name": "Ghibli Inu" }, - { "id": "ghospers-game", "symbol": "ghsp", "name": "Ghospers Game" }, - { "id": "ghostblade-inu", "symbol": "ghostblade", "name": "GhostBlade Inu" }, - { "id": "ghost-block", "symbol": "ecto", "name": "Ghost Block" }, - { "id": "ghost-by-mcafee", "symbol": "ghost", "name": "Ghost" }, - { "id": "ghostface", "symbol": "ghostface", "name": "Ghostface" }, - { "id": "ghostface-shiba", "symbol": "gfshib", "name": "Ghostface Shiba" }, - { - "id": "ghost-farmer-capital", - "symbol": "gfc", - "name": "Ghost Farmer Capital" - }, - { "id": "ghost-inu", "symbol": "ghost", "name": "Ghost Inu" }, - { "id": "ghostmarket", "symbol": "gm", "name": "GhostMarket" }, - { "id": "ghost-trader", "symbol": "GTR", "name": "Ghost Trader" }, - { "id": "ghoul-token", "symbol": "ghoul", "name": "Ghoul Token" }, - { "id": "gibx-swap", "symbol": "x", "name": "GIBX Swap" }, - { "id": "gictrade", "symbol": "gict", "name": "GICTrade" }, - { "id": "giddy", "symbol": "gddy", "name": "Giddy" }, - { "id": "gif-dao", "symbol": "gif", "name": "GIF DAO" }, - { "id": "giftbag", "symbol": "gbag", "name": "Giftbag" }, - { "id": "gift-coin", "symbol": "gift", "name": "Gift Coin" }, - { "id": "giftedhands", "symbol": "ghd", "name": "Giftedhands" }, - { "id": "gifto", "symbol": "gto", "name": "Gifto" }, - { "id": "gigachad", "symbol": "chad", "name": "GigaChad" }, - { "id": "giga-watt-token", "symbol": "wtt", "name": "Giga Watt Token" }, - { "id": "gigco", "symbol": "gig", "name": "GIGCO" }, - { "id": "gigecoin", "symbol": "gig", "name": "GigEcoin" }, - { "id": "giletjaunecoin", "symbol": "gjco", "name": "GiletJauneCoin" }, - { "id": "gilgamesh-eth", "symbol": "gil", "name": "Gilgamesh ETH" }, - { "id": "gimmer", "symbol": "gmr", "name": "Gimmer" }, - { "id": "ginga-finance", "symbol": "gin", "name": "Ginga Finance" }, - { "id": "ginoa", "symbol": "ginoa", "name": "Ginoa" }, - { "id": "ginspirit", "symbol": "ginspirit", "name": "GinSpirit" }, - { "id": "gin-token", "symbol": "gin", "name": "Gin Token" }, - { - "id": "ginza-eternity-reward", - "symbol": "ger", - "name": "Ginza Eternity Reward" - }, - { "id": "ginza-network", "symbol": "ginza", "name": "Ginza Network" }, - { "id": "giresunspor-token", "symbol": "grs", "name": "Giresunspor Token" }, - { "id": "girl-story", "symbol": "metagirl", "name": "Girl Story" }, - { "id": "gitcoin", "symbol": "gtc", "name": "Gitcoin" }, - { "id": "give-global", "symbol": "give", "name": "Give Global" }, - { "id": "giveth", "symbol": "giv", "name": "Giveth" }, - { "id": "givetime-io", "symbol": "gtm", "name": "Givetime.io" }, - { - "id": "givingtoservices-svs", - "symbol": "svs", - "name": "GivingToServices SVS" - }, - { "id": "givly-coin", "symbol": "giv", "name": "GIV Token" }, - { "id": "gizadao", "symbol": "giza", "name": "GizaDao" }, - { "id": "glacierlaunch", "symbol": "GLAC", "name": "GlacierLaunch" }, - { "id": "glass-chain", "symbol": "gls", "name": "Glass Chain" }, - { - "id": "gld-tokenized-stock-defichain", - "symbol": "dGLD", - "name": "SPDR Gold Shares Defichain" - }, - { "id": "gleec-coin", "symbol": "gleec", "name": "Gleec Coin" }, - { "id": "glex", "symbol": "glex", "name": "GLEX" }, - { "id": "glide-finance", "symbol": "glide", "name": "Glide Finance" }, - { "id": "glimpse", "symbol": "glms", "name": "Glimpse" }, - { "id": "glitch-protocol", "symbol": "glch", "name": "Glitch Protocol" }, - { "id": "glitchy", "symbol": "gly", "name": "Glitchy" }, - { "id": "glitter-finance", "symbol": "xgli", "name": "Glitter Finance" }, - { "id": "glitzkoin", "symbol": "gtn", "name": "GlitzKoin" }, - { "id": "global-aex-token", "symbol": "gat", "name": "Global AEX Token" }, - { "id": "globalboost", "symbol": "bsty", "name": "GlobalBoost-Y" }, - { "id": "globalchainz", "symbol": "gcz", "name": "GlobalChainZ" }, - { "id": "global-china-cash", "symbol": "cnc", "name": "Global China Cash" }, - { "id": "globalcoin", "symbol": "glc", "name": "GlobalCoin" }, - { - "id": "global-coin-research", - "symbol": "gcr", - "name": "Global Coin Research" - }, - { - "id": "global-crypto-alliance", - "symbol": "call", - "name": "Global Crypto Alliance" - }, - { - "id": "global-digital-content", - "symbol": "gdc", - "name": "Global Digital Content" - }, - { "id": "global-gaming", "symbol": "gmng", "name": "Global Gaming" }, - { "id": "globalgive", "symbol": "ggive", "name": "GlobalGive" }, - { "id": "global-human-trust", "symbol": "ght", "name": "Global Human Trust" }, - { - "id": "global-reserve-system", - "symbol": "glob", - "name": "Global Reserve System" - }, - { "id": "global-smart-asset", "symbol": "gsa", "name": "Global Smart Asset" }, - { - "id": "global-social-chain", - "symbol": "gsc", - "name": "Global Social Chain" - }, - { "id": "global-trust-coin", "symbol": "gtc", "name": "Global Trust Coin" }, - { - "id": "globaltrustfund-token", - "symbol": "gtf", - "name": "GLOBALTRUSTFUND TOKEN" - }, - { - "id": "globalvillage-ecosystem", - "symbol": "gve", - "name": "Globalvillage Ecosystem" - }, - { - "id": "globe-derivative-exchange", - "symbol": "gdt", - "name": "Globe Derivative Exchange" - }, - { - "id": "globiance-exchange", - "symbol": "gbex", - "name": "Globiance Exchange" - }, - { "id": "glorydoge", "symbol": "gloryd", "name": "GloryDoge" }, - { "id": "glosfer-token", "symbol": "glo", "name": "Glosfer Token" }, - { "id": "glouki", "symbol": "glk", "name": "Glouki" }, - { "id": "glow", "symbol": "GLOW", "name": "Glow" }, - { "id": "glow-token", "symbol": "glow", "name": "Glow Token" }, - { "id": "glowv2", "symbol": "glowv2", "name": "GlowV2" }, - { "id": "glox-finance", "symbol": "glox", "name": "Glox Finance" }, - { "id": "glyph-vault-nftx", "symbol": "glyph", "name": "GLYPH Vault (NFTX)" }, - { "id": "gm", "symbol": "gm", "name": "GM" }, - { "id": "gma", "symbol": "gma", "name": "GMA" }, - { "id": "gmcoin", "symbol": "gm", "name": "GM Holding" }, - { "id": "gm-coin", "symbol": "gm", "name": "Gm Coin" }, - { "id": "gmcoin-2", "symbol": "gmcoin", "name": "GMCoin" }, - { "id": "gm-floki", "symbol": "gmfloki", "name": "GM Floki" }, - { "id": "gm-inu", "symbol": "gminu", "name": "GM Inu" }, - { "id": "gmr-finance", "symbol": "gmr", "name": "GMR Finance (OLD)" }, - { "id": "gmsol", "symbol": "GMSOL", "name": "GMSOL" }, - { "id": "gmt-token", "symbol": "gmt", "name": "GMT Token" }, - { "id": "gmx", "symbol": "gmx", "name": "GMX" }, - { "id": "gn", "symbol": "gn", "name": "GN" }, - { "id": "gnar-token", "symbol": "GNAR", "name": "GNAR TOKEN" }, - { "id": "gnft", "symbol": "gnft", "name": "GNFT" }, - { "id": "gnome", "symbol": "$gnome", "name": "GNOME" }, - { "id": "gnometoken", "symbol": "gnome", "name": "GnomeToken" }, - { "id": "gnosis", "symbol": "gno", "name": "Gnosis" }, - { "id": "gny", "symbol": "gny", "name": "GNY" }, - { "id": "goal", "symbol": "goal", "name": "CronosFC" }, - { "id": "goaltime-n", "symbol": "gtx", "name": "GoalTime N" }, - { "id": "goal-token", "symbol": "goal", "name": "Goal Token" }, - { "id": "goatcoin", "symbol": "goat", "name": "Goatcoin" }, - { "id": "goat-coin", "symbol": "goat", "name": "Goat Coin" }, - { "id": "goats", "symbol": "goats", "name": "GOATS" }, - { "id": "g-o-a-t-token", "symbol": "G.O.A.T", "name": "G.O.A.T Token" }, - { "id": "gobble-token", "symbol": "gobble", "name": "Gobble Token" }, - { "id": "goblin", "symbol": "goblin", "name": "Goblin" }, - { "id": "gobyte", "symbol": "gbx", "name": "GoByte" }, - { "id": "gochain", "symbol": "go", "name": "GoChain" }, - { "id": "gocryptome", "symbol": "gcme", "name": "GoCryptoMe" }, - { "id": "god-candle", "symbol": "god", "name": "God Candle" }, - { "id": "god-dao", "symbol": "god", "name": "God DAO" }, - { "id": "gods-and-legends", "symbol": "gnlr", "name": "Gods and Legends" }, - { "id": "god-shiba-token", "symbol": "gshib", "name": "God Shiba Token" }, - { "id": "gods-unchained", "symbol": "gods", "name": "Gods Unchained" }, - { "id": "godzilla", "symbol": "godz", "name": "Godzilla" }, - { "id": "goforit", "symbol": "goi", "name": "GoForIt Walk\u0026Win" }, - { "id": "gogeta-inu", "symbol": "gogeta", "name": "Gogeta Inu" }, - { "id": "gogocoin", "symbol": "gogo", "name": "GOGOcoin" }, - { "id": "gogo-finance", "symbol": "gogo", "name": "GOGO Finance" }, - { "id": "gogolcoin", "symbol": "gol", "name": "GogolCoin" }, - { "id": "goin", "symbol": "GOIN", "name": "GOIN" }, - { "id": "goku", "symbol": "goku", "name": "Goku" }, - { "id": "gokumarket-credit", "symbol": "gmc", "name": "GokuMarket Credit" }, - { "id": "gold", "symbol": "gold", "name": "GOLD" }, - { "id": "goldario", "symbol": "gld", "name": "Goldario" }, - { "id": "gold-bcr", "symbol": "gbcr", "name": "Gold BCR" }, - { "id": "goldblocks", "symbol": "gb", "name": "GoldBlocks" }, - { "id": "gold-cash", "symbol": "gold", "name": "Gold Cash" }, - { "id": "goldcoin", "symbol": "glc", "name": "Goldcoin" }, - { "id": "gold-coin-reserve", "symbol": "gcr", "name": "Gold Coin Reserve" }, - { "id": "goldefy", "symbol": "god", "name": "GoldeFy" }, - { "id": "golden-age", "symbol": "ga", "name": "Golden Age" }, - { "id": "golden-ball", "symbol": "glb", "name": "Golden Ball" }, - { "id": "golden-banana", "symbol": "gba", "name": "Golden Banana" }, - { "id": "goldendiamond9", "symbol": "g9", "name": "GoldenDiamond9" }, - { "id": "golden-doge", "symbol": "gdoge", "name": "Golden Doge" }, - { "id": "golden-goose", "symbol": "gold", "name": "Golden Goose" }, - { - "id": "golden-kitty-cake", - "symbol": "gkcake", - "name": "Golden Kitty Cake" - }, - { "id": "golden-ratio-coin", "symbol": "goldr", "name": "Golden Ratio Coin" }, - { "id": "golden-ratio-token", "symbol": "grt", "name": "Golden Ratio Token" }, - { "id": "golden-roots", "symbol": "gdr", "name": "Golden Roots" }, - { "id": "golden-token", "symbol": "gold", "name": "Golden Token" }, - { "id": "goldenugget", "symbol": "gnto", "name": "GoldeNugget" }, - { "id": "goldex-token", "symbol": "gldx", "name": "Goldex Token" }, - { "id": "goldfarm", "symbol": "gold", "name": "GoldFarm" }, - { - "id": "gold-fever-native-gold", - "symbol": "ngl", - "name": "Gold Fever Native Gold" - }, - { "id": "goldfinch", "symbol": "gfi", "name": "Goldfinch" }, - { "id": "goldfinx", "symbol": "gix", "name": "GoldFinX" }, - { "id": "goldfund-ico", "symbol": "gfun", "name": "GoldFund" }, - { "id": "gol-d-inu", "symbol": "ginu", "name": "Gol D Inu" }, - { "id": "goldkash", "symbol": "xgk", "name": "GoldKash" }, - { "id": "goldminer", "symbol": "gm", "name": "GoldMiner" }, - { - "id": "gold-mining-members", - "symbol": "gmm", - "name": "Gold Mining Members" - }, - { "id": "goldmint", "symbol": "mntp", "name": "Goldmint" }, - { "id": "goldmoney", "symbol": "gdm", "name": "Goldmoney" }, - { "id": "goldnugget", "symbol": "ngt", "name": "Gold Nugget" }, - { "id": "gold-pegas", "symbol": "gdp", "name": "Gold Pegas" }, - { "id": "goldpesa-option", "symbol": "gpo", "name": "GoldPesa Option" }, - { "id": "gold-poker", "symbol": "gpkr", "name": "Gold Poker" }, - { - "id": "gold-secured-currency", - "symbol": "gsx", - "name": "Gold Secured Currency" - }, - { "id": "gold-sonic", "symbol": "gsonic", "name": "Gold Sonic" }, - { "id": "golem", "symbol": "glm", "name": "Golem" }, - { "id": "golff", "symbol": "gof", "name": "Golff" }, - { "id": "golfinance", "symbol": "GOL", "name": "GolFinance" }, - { "id": "golfrochain", "symbol": "golf", "name": "Golfrochain" }, - { "id": "goma-finance", "symbol": "goma", "name": "GOMA Finance [OLD]" }, - { "id": "goma-finance-2", "symbol": "goma", "name": "Goma Finance" }, - { "id": "gomax", "symbol": "gomax", "name": "GOMAx" }, - { "id": "gomb", "symbol": "gomb", "name": "GOMB" }, - { "id": "gombshare", "symbol": "gshare", "name": "GOMBSHARE" }, - { "id": "gomics", "symbol": "gom", "name": "Gomics" }, - { "id": "gomoney2", "symbol": "gom2", "name": "GoMoney2" }, - { "id": "gondola-finance", "symbol": "gdl", "name": "Gondola Finance" }, - { "id": "gonetwork", "symbol": "got", "name": "GoNetwork" }, - { "id": "good-bridging", "symbol": "gb", "name": "Good Bridging" }, - { "id": "good-fire", "symbol": "gf", "name": "Good Fire" }, - { "id": "good-game", "symbol": "gg", "name": "Good Game" }, - { "id": "good-games-guild", "symbol": "ggg", "name": "Good Games Guild" }, - { "id": "goofydoge", "symbol": "GoofyDoge", "name": "GoofyDoge" }, - { - "id": "google-tokenized-stock-defichain", - "symbol": "dgoogl", - "name": "Google Tokenized Stock Defichain" - }, - { - "id": "google-tokenized-stock-zipmex", - "symbol": "googl", - "name": "Google Tokenized Stock Zipmex" - }, - { "id": "gooreo", "symbol": "gooreo", "name": "Gooreo" }, - { "id": "goose-finance", "symbol": "egg", "name": "Goose Finance" }, - { "id": "goosefx", "symbol": "gofx", "name": "GooseFX" }, - { "id": "gorgeous", "symbol": "gorgeous", "name": "Gorgeous" }, - { "id": "gorilla-diamond", "symbol": "gdt", "name": "Gorilla Diamond" }, - { "id": "gorilla-inu", "symbol": "gorilla inu", "name": "Gorilla Inu" }, - { "id": "gorilla-nodes", "symbol": "banana", "name": "Gorilla Nodes" }, - { "id": "gosh-realm", "symbol": "gosh", "name": "GOSH Realm" }, - { "id": "gotem", "symbol": "gotem", "name": "gotEM" }, - { "id": "gotham", "symbol": "gotham", "name": "Gotham" }, - { "id": "gourmetgalaxy", "symbol": "gum", "name": "Gourmet Galaxy" }, - { "id": "governance-ohm", "symbol": "gohm", "name": "Governance OHM" }, - { - "id": "governance-ohm-wormhole", - "symbol": "gohm", - "name": "Governance OHM (Wormhole)" - }, - { "id": "governance-zil", "symbol": "gzil", "name": "governance ZIL" }, - { "id": "governor-dao", "symbol": "gdao", "name": "Governor DAO" }, - { "id": "govi", "symbol": "govi", "name": "CVI" }, - { "id": "govworld", "symbol": "gov", "name": "GovWorld" }, - { "id": "gowithmi", "symbol": "gmat", "name": "GoWithMi" }, - { - "id": "goztepe-s-k-fan-token", - "symbol": "goz", - "name": "Göztepe S.K. Fan Token" - }, - { "id": "gpex", "symbol": "gpx", "name": "GPEX" }, - { "id": "gps-ecosystem", "symbol": "gps", "name": "GPS Ecosystem" }, - { "id": "grafenocoin-2", "symbol": "gfnc", "name": "GrafenoCoin" }, - { "id": "grafsound", "symbol": "gsmt", "name": "Grafsound" }, - { "id": "graft-blockchain", "symbol": "grft", "name": "Graft Blockchain" }, - { "id": "gram", "symbol": "gram", "name": "OpenGram" }, - { "id": "grand-exchange", "symbol": "gp", "name": "Grand Exchange" }, - { "id": "grandpa-doge", "symbol": "grandpadoge", "name": "Grandpa Doge" }, - { "id": "granny-shiba", "symbol": "gshiba", "name": "Granny Shiba" }, - { "id": "grape-2", "symbol": "grape", "name": "Grape Protocol" }, - { "id": "grape-finance", "symbol": "grape", "name": "Grape Finance" }, - { "id": "grapefruit-coin", "symbol": "grpft", "name": "Grapefruit Coin" }, - { "id": "grape-token", "symbol": "grape", "name": "Grape" }, - { "id": "grap-finance", "symbol": "grap", "name": "Grap Finance" }, - { "id": "graphene", "symbol": "gfn", "name": "Graphene" }, - { "id": "graphlinq-protocol", "symbol": "glq", "name": "GraphLinq Protocol" }, - { "id": "grave", "symbol": "grve", "name": "Grave" }, - { "id": "graviocoin", "symbol": "gio", "name": "Graviocoin" }, - { "id": "gravitoken", "symbol": "grv", "name": "Gravitoken" }, - { "id": "graviton", "symbol": "grav", "name": "Graviton" }, - { "id": "graviton-zero", "symbol": "grav", "name": "Graviton Zero" }, - { "id": "gravitx", "symbol": "GRX", "name": "GravitX" }, - { "id": "gravity", "symbol": "gzro", "name": "Gravity" }, - { "id": "grearn", "symbol": "gst", "name": "GrEarn" }, - { "id": "great-ape", "symbol": "greatape", "name": "Great Ape" }, - { - "id": "great-bounty-dealer", - "symbol": "gbd", - "name": "Great Bounty Dealer" - }, - { "id": "greed", "symbol": "$greed", "name": "Greed" }, - { "id": "greekmythology", "symbol": "GMT", "name": "GreekMythology" }, - { "id": "greenair", "symbol": "green", "name": "GreenAir" }, - { "id": "green-beli", "symbol": "grbe", "name": "Green Beli" }, - { "id": "green-ben", "symbol": "EBEN", "name": "Green Ben" }, - { "id": "green-chart", "symbol": "green", "name": "Green Chart" }, - { - "id": "green-climate-world", - "symbol": "wgc", - "name": "Green Climate World" - }, - { "id": "greencoin", "symbol": "gre", "name": "Greencoin" }, - { - "id": "green-dildo-finance", - "symbol": "gdildo", - "name": "Green Dildo Finance" - }, - { "id": "green-energy-coin", "symbol": "gec", "name": "Green Energy Coin" }, - { "id": "greeneum-network", "symbol": "green", "name": "Greeneum Network" }, - { "id": "greenex", "symbol": "gnx", "name": "Greenex" }, - { - "id": "green-eyed-monsters", - "symbol": "GEM", - "name": "Green Eyed Monsters" - }, - { "id": "green-floki", "symbol": "greenfloki", "name": "Green Floki" }, - { "id": "green-flow", "symbol": "grc", "name": "GreenCoin.AI" }, - { "id": "greenfuel", "symbol": "greenfuel", "name": "Greenfuel" }, - { "id": "greenheart-cbd", "symbol": "cbd", "name": "Greenheart CBD" }, - { "id": "greenhouse", "symbol": "green", "name": "Greenhouse" }, - { "id": "green-life-energy", "symbol": "gnl", "name": "Green Life Energy" }, - { "id": "green-light", "symbol": "gl", "name": "Green Light" }, - { "id": "green-meta", "symbol": "gmeta", "name": "Green Meta" }, - { "id": "greenmoon", "symbol": "grm", "name": "GreenMoon" }, - { "id": "greenmoonzilla", "symbol": "grmzilla", "name": "GreenMoonZilla" }, - { "id": "greenpay-coin", "symbol": "gpc", "name": "GreenPay Coin" }, - { "id": "green-planet", "symbol": "gamma", "name": "Green Planet" }, - { - "id": "green-satoshi-token", - "symbol": "gst", - "name": "Green Satoshi Token" - }, - { "id": "green-shiba-inu", "symbol": "ginux", "name": "Green Shiba Inu" }, - { "id": "greentek", "symbol": "gte", "name": "GreenTek" }, - { "id": "greentrust", "symbol": "gnt", "name": "GreenTrust" }, - { "id": "green-world", "symbol": "grew", "name": "Green World" }, - { "id": "greenzonex", "symbol": "gzx", "name": "GreenZoneX" }, - { "id": "gremlins-finance", "symbol": "grem", "name": "Gremlins Finance" }, - { "id": "greyhound", "symbol": "greyhound", "name": "Greyhound" }, - { "id": "gric", "symbol": "gc", "name": "Gric Coin" }, - { "id": "grid", "symbol": "grid", "name": "GridPlus [OLD]" }, - { "id": "gridcoin-research", "symbol": "grc", "name": "Gridcoin" }, - { "id": "gridzone", "symbol": "zone", "name": "GridZone.io" }, - { "id": "griffin-art", "symbol": "gart", "name": "Griffin Art" }, - { "id": "grimace-coin", "symbol": "grimace", "name": "Grimace Coin" }, - { "id": "grim-evo", "symbol": "grim evo", "name": "Grim EVO" }, - { "id": "grimm", "symbol": "grimm", "name": "Grimm" }, - { "id": "grimtoken", "symbol": "grim", "name": "GrimToken" }, - { "id": "grin", "symbol": "grin", "name": "Grin" }, - { "id": "gro-dao-token", "symbol": "gro", "name": "Gro DAO Token" }, - { "id": "groestlcoin", "symbol": "grs", "name": "Groestlcoin" }, - { "id": "grok", "symbol": "grok", "name": "Grok" }, - { "id": "grom", "symbol": "gr", "name": "GROM" }, - { "id": "groupdao", "symbol": "gdo", "name": "GroupDao" }, - { "id": "gro-vault-token", "symbol": "gvt", "name": "Gro Vault Token" }, - { "id": "growing-fi", "symbol": "grow", "name": "Growing.fi" }, - { "id": "growthcoin", "symbol": "grw", "name": "GrowthCoin" }, - { "id": "growth-defi", "symbol": "gro", "name": "GROWTH DeFi" }, - { "id": "grow-token-2", "symbol": "grow", "name": "Grow Token" }, - { "id": "gscarab", "symbol": "gscarab", "name": "GScarab" }, - { "id": "gsenetwork", "symbol": "gse", "name": "GSENetwork" }, - { "id": "gsmcoin", "symbol": "gsm", "name": "GSMcoin" }, - { "id": "gspi", "symbol": "gspi", "name": "Shopping.io Governance" }, - { "id": "gstcoin", "symbol": "gst", "name": "GSTCOIN" }, - { "id": "gton-capital", "symbol": "gton", "name": "GTON CAPITAL" }, - { "id": "gt-protocol", "symbol": "gtp", "name": "GT-Protocol" }, - { "id": "gu", "symbol": "gu", "name": "Kugle GU" }, - { "id": "guapcoin", "symbol": "guap", "name": "Guapcoin" }, - { "id": "guarded-ether", "symbol": "geth", "name": "Guarded Ether" }, - { "id": "guardian-token", "symbol": "guard", "name": "Guardian Token" }, - { "id": "guccinuv2", "symbol": "GucciV2", "name": "GuccinuV2" }, - { "id": "guider", "symbol": "gdr", "name": "Guider" }, - { "id": "guildfi", "symbol": "gf", "name": "GuildFi" }, - { "id": "guild-of-guardians", "symbol": "gog", "name": "Guild of Guardians" }, - { "id": "guitarswap", "symbol": "gut", "name": "GuitarSwap" }, - { "id": "gulden", "symbol": "nlg", "name": "Gulden" }, - { "id": "gummy-beans", "symbol": "GUMMIE", "name": "Gummy Beans" }, - { "id": "gummy-bull-token", "symbol": "GUMMY", "name": "Gummy Bull Token" }, - { "id": "guncoin", "symbol": "gun", "name": "Guncoin" }, - { "id": "gunstar-metaverse", "symbol": "gst", "name": "Gunstar Metaverse" }, - { - "id": "gunstar-metaverse-currency", - "symbol": "gsc", - "name": "Gunstar Metaverse Currency" - }, - { "id": "gunthy", "symbol": "gunthy", "name": "GUNTHY" }, - { - "id": "gusd-token", - "symbol": "gusdt", - "name": "Global Utility Smart Digital Token" - }, - { "id": "guss-one", "symbol": "guss", "name": "GUSS.ONE" }, - { "id": "gutter-cat-gang", "symbol": "gcg", "name": "Gutter Cat Gang" }, - { "id": "guzzler", "symbol": "gzlr", "name": "Guzzler" }, - { "id": "gw", "symbol": "gw", "name": "GW" }, - { "id": "gxchain", "symbol": "gxc", "name": "GXChain" }, - { "id": "gyen", "symbol": "gyen", "name": "GYEN" }, - { "id": "gym-network", "symbol": "gymnet", "name": "Gym Network" }, - { "id": "gym-token", "symbol": "gym", "name": "GYM Token" }, - { "id": "gyro", "symbol": "gyro", "name": "Gyro" }, - { "id": "gyroscope", "symbol": "gyfi", "name": "Gyroscope" }, - { "id": "h2finance", "symbol": "yfih2", "name": "H2Finance" }, - { "id": "h2o", "symbol": "h2o", "name": "H2O" }, - { "id": "h2o-dao", "symbol": "h2o", "name": "H2O Dao" }, - { "id": "h3ro3s", "symbol": "h3ro3s", "name": "H3RO3S" }, - { "id": "habitat", "symbol": "hbt", "name": "Habitat" }, - { "id": "habits", "symbol": "hbx", "name": "Habits" }, - { "id": "hacash", "symbol": "hac", "name": "Hacash" }, - { "id": "hachiko", "symbol": "Hachiko", "name": "Hachiko" }, - { "id": "hachiko-charity", "symbol": "hkc", "name": "Hachiko Charity" }, - { "id": "hachikoinu", "symbol": "inu", "name": "Hachiko Inu Token" }, - { "id": "hackenai", "symbol": "hai", "name": "Hacken Token" }, - { "id": "hackerlabs-dao", "symbol": "hld", "name": "Hackerlabs DAO" }, - { "id": "hackspace-capital", "symbol": "hac", "name": "Hackspace Capital" }, - { "id": "hades-money", "symbol": "hades", "name": "Hades Money" }, - { "id": "hado", "symbol": "hdo", "name": "HADO" }, - { - "id": "hai-governence-token", - "symbol": "damo", - "name": "Hai Governance Token" - }, - { "id": "haino", "symbol": "HE", "name": "Haino [OLD]" }, - { "id": "haino-2", "symbol": "he", "name": "Haino" }, - { "id": "hakka-finance", "symbol": "hakka", "name": "Hakka Finance" }, - { "id": "hakuna-matata", "symbol": "matata", "name": "Hakuna Matata" }, - { "id": "hakunamatata-new", "symbol": "hkun", "name": "HakunaMatata (new)" }, - { "id": "hakuna-metata", "symbol": "tata", "name": "HakunaMatata (old)" }, - { "id": "hakurai", "symbol": "$rai", "name": "Hakurai" }, - { "id": "hakuryu", "symbol": "$ryu", "name": "Hakuryu" }, - { "id": "hakuswap", "symbol": "haku", "name": "HakuSwap" }, - { "id": "halcyon", "symbol": "hal", "name": "Halcyon" }, - { "id": "halfpizza", "symbol": "piza", "name": "Half Pizza" }, - { "id": "halo-network", "symbol": "ho", "name": "HALO Network" }, - { "id": "halo-platform", "symbol": "halo", "name": "Halo Platform" }, - { "id": "halving-coin", "symbol": "halv", "name": "Halving" }, - { "id": "hamaru", "symbol": "maru", "name": "Hamaru" }, - { "id": "hamdan-coin", "symbol": "hmc", "name": "Hamdan Coin" }, - { "id": "hampton-metaverse", "symbol": "hmeta", "name": "Hampton Metaverse" }, - { "id": "hamster", "symbol": "ham", "name": "Hamster" }, - { "id": "hamster-money", "symbol": "ham", "name": "Hamster Money" }, - { "id": "hamster-share", "symbol": "hshare", "name": "Hamster Share" }, - { "id": "hanacoin", "symbol": "hana", "name": "Hanacoin" }, - { "id": "hanagold-token", "symbol": "hng", "name": "HanaGold Token" }, - { "id": "handle-fi", "symbol": "forex", "name": "handle.fi" }, - { "id": "handshake", "symbol": "hns", "name": "Handshake" }, - { "id": "handy", "symbol": "handy", "name": "Handy" }, - { "id": "hangrybirds", "symbol": "hangry", "name": "HangryBirds" }, - { "id": "hanu-yokia", "symbol": "hanu", "name": "Hanu Yokia" }, - { "id": "hanzo-inu", "symbol": "hanzo", "name": "Hanzo" }, - { "id": "hapi", "symbol": "hapi", "name": "HAPI" }, - { "id": "happiness-token", "symbol": "hps", "name": "Happiness Token" }, - { - "id": "happy-birthday-coin", - "symbol": "hbdc", - "name": "Happy Birthday Coin" - }, - { "id": "happycoin", "symbol": "happy", "name": "HappyCoin" }, - { "id": "happyfans", "symbol": "happy", "name": "HappyFans" }, - { "id": "happyland", "symbol": "hpl", "name": "HappyLand" }, - { - "id": "happyland-reward-token", - "symbol": "hpw", - "name": "HappyLand Reward Token" - }, - { "id": "harambe", "symbol": "harambe", "name": "Harambe" }, - { "id": "harambe-protocol", "symbol": "riph", "name": "Harambe Protocol" }, - { "id": "hara-token", "symbol": "hart", "name": "Hara Token" }, - { "id": "hare", "symbol": "hare", "name": "Hare" }, - { "id": "hare-plus", "symbol": "HARE PLUS", "name": "Hare Plus" }, - { "id": "hare-token", "symbol": "hare", "name": "Hare Token [OLD]" }, - { "id": "harmes-shares", "symbol": "hshares", "name": "Hermes Shares" }, - { "id": "harmomized-app", "symbol": "hmz", "name": "Harmonized App" }, - { "id": "harmon-ape", "symbol": "ape", "name": "Harmon Ape" }, - { "id": "harmony", "symbol": "one", "name": "Harmony" }, - { "id": "harmonycoin", "symbol": "hmc", "name": "HarmonyCoin" }, - { "id": "harmonylauncher", "symbol": "harl", "name": "HarmonyLauncher" }, - { - "id": "harmony-parrot-egg", - "symbol": "1pegg", - "name": "Harmony Parrot Egg" - }, - { "id": "harmony-play", "symbol": "hplay", "name": "Harmony Play" }, - { "id": "harmonyville", "symbol": "hville", "name": "Harmonyville" }, - { "id": "haroldcoin", "symbol": "hrld", "name": "Haroldcoin" }, - { "id": "harpy-finance", "symbol": "harpy", "name": "Harpy Finance" }, - { "id": "harrison-first", "symbol": "FIRST", "name": "Harrison First" }, - { - "id": "harrypotterobamasonic10inu", - "symbol": "BITCOIN", - "name": "HarryPotterObamaSonic10Inu" - }, - { "id": "harvest-finance", "symbol": "farm", "name": "Harvest Finance" }, - { "id": "hashbit", "symbol": "hbit", "name": "HashBit" }, - { "id": "hash-bridge-oracle", "symbol": "hbo", "name": "Hash Bridge Oracle" }, - { "id": "hashbx", "symbol": "hbx", "name": "HashBX" }, - { "id": "hashcoin", "symbol": "hsc", "name": "HashCoin" }, - { "id": "hashgard", "symbol": "gard", "name": "Hashgard" }, - { "id": "hashkings-buds", "symbol": "buds", "name": "Hashkings Buds" }, - { "id": "hashland-coin", "symbol": "hc", "name": "HashLand Coin" }, - { "id": "hashmasks", "symbol": "mask20", "name": "Hashmasks" }, - { "id": "hashnet-biteco", "symbol": "hnb", "name": "HashNet BitEco" }, - { "id": "hashpanda", "symbol": "panda", "name": "HashPanda" }, - { "id": "hashshare", "symbol": "hss", "name": "Hashshare" }, - { "id": "hashtagger", "symbol": "MOOO", "name": "Hashtagger" }, - { "id": "hash-token", "symbol": "hash", "name": "Hash Token" }, - { "id": "hatayspor-token", "symbol": "hatay", "name": "Hatayspor Token" }, - { "id": "hatch-dao", "symbol": "hatch", "name": "Hatch DAO" }, - { "id": "hathor", "symbol": "htr", "name": "Hathor" }, - { "id": "hatoken", "symbol": "hatok", "name": "Hatoken [OLD]" }, - { "id": "hatoken-2", "symbol": "hatok", "name": "Hatoken" }, - { "id": "hat-swap-city", "symbol": "htc", "name": "Hat Swap City" }, - { "id": "hatter", "symbol": "hatter", "name": "Hatter" }, - { "id": "have-fun", "symbol": "hf", "name": "Have Fun" }, - { - "id": "have-fun-staying-poor", - "symbol": "hfsp", - "name": "Have Fun Staying Poor" - }, - { "id": "haven", "symbol": "xhv", "name": "Haven" }, - { "id": "havens-nook", "symbol": "hxn", "name": "Havens Nook" }, - { "id": "haven-token", "symbol": "haven", "name": "Haven Token" }, - { "id": "havven", "symbol": "snx", "name": "Synthetix Network Token" }, - { "id": "havy-2", "symbol": "havy", "name": "Havy" }, - { "id": "hawaii-coin", "symbol": "hwi", "name": "Hawaii Coin" }, - { "id": "hawksight", "symbol": "$hawk", "name": "Hawksight" }, - { "id": "hayate-inu", "symbol": "hinu", "name": "Hayate Inu" }, - { "id": "hayfever", "symbol": "hay", "name": "Hayfever" }, - { "id": "hazza", "symbol": "haz", "name": "Hazza" }, - { "id": "hbarpad", "symbol": "hbarp", "name": "HbarPad" }, - { "id": "hbtc-token", "symbol": "hbc", "name": "HBTC Captain Token" }, - { "id": "hdac", "symbol": "hdac", "name": "HDAC" }, - { "id": "hddcoin", "symbol": "hdd", "name": "HDDcoin" }, - { - "id": "hdpunk-vault-nftx", - "symbol": "hdpunk", - "name": "HDPUNK Vault (NFTX)" - }, - { - "id": "headbangers-club", - "symbol": "headbangers", - "name": "Headbangers Club" - }, - { "id": "head-football", "symbol": "head", "name": "Head Football" }, - { "id": "healing-potion", "symbol": "hppot", "name": "Healing Potion" }, - { "id": "healthchainus", "symbol": "hcut", "name": "HealthChainUS" }, - { "id": "heal-the-world", "symbol": "heal", "name": "Heal The World" }, - { "id": "healthify", "symbol": "htf", "name": "Healthify" }, - { "id": "health-potion", "symbol": "hep", "name": "Health Potion" }, - { "id": "hearn-fi", "symbol": "hearn", "name": "Hearn.fi" }, - { "id": "heartbout", "symbol": "hb", "name": "HeartBout" }, - { "id": "heartbout-pay", "symbol": "hp", "name": "HeartBout Pay" }, - { "id": "heartnumber", "symbol": "htn", "name": "Heart Number" }, - { "id": "hearts", "symbol": "heart", "name": "Hearts" }, - { "id": "hebeblock", "symbol": "hebe", "name": "Hebeblock" }, - { "id": "hecate-capital", "symbol": "HECATE", "name": "Hecate Capital" }, - { "id": "hecofi", "symbol": "hfi", "name": "HecoFi" }, - { "id": "heco-origin-token", "symbol": "hogt", "name": "Heco Origin Token" }, - { "id": "heco-peg-bnb", "symbol": "BNB", "name": "Heco-Peg Binance Coin" }, - { "id": "heco-peg-xrp", "symbol": "xrp", "name": "Heco-Peg XRP" }, - { "id": "hector-dao", "symbol": "hec", "name": "Hector Finance" }, - { "id": "hedera-hashgraph", "symbol": "hbar", "name": "Hedera" }, - { "id": "hedge-finance", "symbol": "hedge", "name": "Hedge Finance" }, - { "id": "hedget", "symbol": "hget", "name": "Hedget" }, - { "id": "hedgetrade", "symbol": "hedg", "name": "HedgeTrade" }, - { "id": "hedget-wormhole", "symbol": "hget", "name": "Hedget (Wormhole)" }, - { "id": "hedpay", "symbol": "hdp.ф", "name": "HEdpAY" }, - { "id": "hedron", "symbol": "hdrn", "name": "Hedron" }, - { "id": "hegic", "symbol": "hegic", "name": "Hegic" }, - { - "id": "helicopter-finance", - "symbol": "copter", - "name": "Helicopter Finance" - }, - { "id": "helio", "symbol": "hlo", "name": "Helio" }, - { "id": "helium", "symbol": "hnt", "name": "Helium" }, - { "id": "heliumx", "symbol": "hex", "name": "Heliumx" }, - { "id": "helix", "symbol": "hlix", "name": "Helix" }, - { "id": "helkin", "symbol": "hk", "name": "Helkin" }, - { "id": "helleniccoin", "symbol": "hnc", "name": "HNC Coin" }, - { "id": "hell-hounds", "symbol": "SOUL", "name": "HELL HOUNDS" }, - { "id": "hellmoon", "symbol": "hmoon", "name": "HELLMOON" }, - { "id": "hellogold", "symbol": "hgt", "name": "HelloGold" }, - { "id": "hellshare", "symbol": "hellshare", "name": "HELLSHARE" }, - { "id": "hellsing-inu", "symbol": "hellsing", "name": "Hellsing Inu" }, - { "id": "helmet-insure", "symbol": "helmet", "name": "Helmet Insure" }, - { "id": "help-coin", "symbol": "hlp", "name": "HLP Token" }, - { "id": "help-coins", "symbol": "hcs", "name": "Help Coins" }, - { - "id": "helper-search-token", - "symbol": "hsn", - "name": "Helper Search Token" - }, - { "id": "helpico", "symbol": "help", "name": "Helpico" }, - { "id": "helpseed", "symbol": "helps", "name": "HelpSeed" }, - { - "id": "help-the-homeless-coin", - "symbol": "hth", - "name": "Help The Homeless Coin" - }, - { "id": "help-token", "symbol": "help", "name": "GoHelpFund" }, - { "id": "hempcoin-thc", "symbol": "thc", "name": "Hempcoin" }, - { "id": "hepa-finance", "symbol": "hepa", "name": "Hepa Finance" }, - { "id": "heptafranc", "symbol": "hptf", "name": "HEPTAFRANC" }, - { "id": "hera-finance", "symbol": "hera", "name": "Hera Finance" }, - { "id": "herbalist-token", "symbol": "herb", "name": "Herbalist Token" }, - { "id": "heres", "symbol": "hrs", "name": "Heres" }, - { "id": "herity-network", "symbol": "her", "name": "Herity Network" }, - { "id": "hermes", "symbol": "hermes", "name": "HERMES" }, - { "id": "hermes-protocol", "symbol": "hermes", "name": "Hermes Protocol" }, - { "id": "hermez-network-token", "symbol": "hez", "name": "Hermez Network" }, - { "id": "hero", "symbol": "hero", "name": "HERO" }, - { "id": "hero-arena", "symbol": "hera", "name": "Hero Arena" }, - { "id": "herobattle", "symbol": "hrb", "name": "Herobattle" }, - { "id": "herobook", "symbol": "hbg", "name": "HeroBook" }, - { "id": "hero-cat-key", "symbol": "hck", "name": "Hero Cat Key" }, - { "id": "hero-cat-token", "symbol": "hct", "name": "Hero Cat Token" }, - { "id": "herocoin", "symbol": "play", "name": "HEROcoin" }, - { "id": "heroeschained", "symbol": "hec", "name": "HeroesChained" }, - { "id": "heroes-empires", "symbol": "he", "name": "Heroes \u0026 Empires" }, - { "id": "heroes-of-nft", "symbol": "hon", "name": "Heroes of NFT" }, - { "id": "hero-essence", "symbol": "hes", "name": "Hero Essence" }, - { "id": "heroes-td", "symbol": "htd", "name": "Heroes TD" }, - { "id": "heroestd-cgc", "symbol": "cgc", "name": "HeroesTD CGC" }, - { "id": "herofi", "symbol": "heroegg", "name": "HeroFi" }, - { "id": "herofi-token", "symbol": "rofi", "name": "HeroFi Token (OLD)" }, - { "id": "herofi-token-2", "symbol": "rofi", "name": "HeroFi Token" }, - { "id": "hero-inu", "symbol": "heros", "name": "Heros Token" }, - { "id": "heropark", "symbol": "hp", "name": "HeroPark" }, - { "id": "herosofcrypton", "symbol": "heroes", "name": "Heroes of Crypton" }, - { "id": "hero-token", "symbol": "raise", "name": "Raise Token" }, - { "id": "heroverse", "symbol": "her", "name": "HeroVerse" }, - { "id": "hertz-network", "symbol": "htz", "name": "Hertz Network" }, - { "id": "herum", "symbol": "ram", "name": "Herum" }, - { "id": "hesh-fi", "symbol": "hesh", "name": "Hesh.Fi" }, - { "id": "hex", "symbol": "hex", "name": "HEX" }, - { "id": "hex-money", "symbol": "hxy", "name": "HXY Money" }, - { "id": "hey-shiba", "symbol": "$heyshib", "name": "Hey Shiba" }, - { "id": "hfuel", "symbol": "hfuel", "name": "HFuel" }, - { "id": "hibiki-finance", "symbol": "hibiki", "name": "Hibiki Finance" }, - { "id": "hiblocks", "symbol": "hibs", "name": "Hiblocks" }, - { "id": "hic-et-nunc-dao", "symbol": "hdao", "name": "Hic et nunc DAO" }, - { "id": "hicoin", "symbol": "xhi", "name": "HiCoin" }, - { "id": "hidden-coin", "symbol": "hdn", "name": "Hidden Coin" }, - { "id": "hideous-coin", "symbol": "hideous", "name": "Hideous Finance" }, - { "id": "hi-dollar", "symbol": "hi", "name": "hi Dollar" }, - { "id": "hierocoin", "symbol": "bar", "name": "Hierocoin" }, - { - "id": "hifi-gaming-society", - "symbol": "hifi", - "name": "HiFi Gaming Society" - }, - { "id": "higgs", "symbol": "higgs", "name": "Higgs" }, - { - "id": "highest-yield-savings-service", - "symbol": "hyss", - "name": "Highest Yield Savings Service" - }, - { - "id": "high-performance-blockchain", - "symbol": "hpb", - "name": "High Performance Blockchain" - }, - { - "id": "high-roller-hippo-clique", - "symbol": "roll", - "name": "High Roller Hippo Clique" - }, - { "id": "highstreet", "symbol": "high", "name": "Highstreet" }, - { "id": "hillstone", "symbol": "hsf", "name": "Hillstone Finance" }, - { - "id": "himalayan-cat-coin", - "symbol": "hima", - "name": "Himalayan Cat Coin" - }, - { "id": "himo-world", "symbol": "himo", "name": "Himo World" }, - { "id": "hina-inu", "symbol": "hina", "name": "Hina Inu" }, - { "id": "hintchain", "symbol": "hint", "name": "Hintchain" }, - { "id": "hippie-inu", "symbol": "HIPPIE", "name": "Hippie Inu" }, - { "id": "hippo-coin", "symbol": "$HIPPO", "name": "Hippo Coin" }, - { "id": "hippo-token", "symbol": "hip", "name": "Hippo Token" }, - { "id": "hiram-coin", "symbol": "hiram", "name": "Hiram Coin" }, - { "id": "hirevibes", "symbol": "hvt", "name": "HireVibes" }, - { "id": "historia", "symbol": "hta", "name": "Historia" }, - { "id": "hitbtc-token", "symbol": "hit", "name": "HitBTC Token" }, - { "id": "hitchain", "symbol": "hit", "name": "HitChain" }, - { "id": "hitcoin", "symbol": "htc", "name": "Hitcoin" }, - { "id": "hithotx", "symbol": "hitx", "name": "Hithotx" }, - { "id": "hive", "symbol": "hive", "name": "Hive" }, - { "id": "hive_dollar", "symbol": "HBD", "name": "Hive Dollar" }, - { - "id": "hive-investments-honey", - "symbol": "hny", - "name": "Hive.Investments HONEY" - }, - { "id": "hiveterminal", "symbol": "hvn", "name": "Hiveterminal token" }, - { "id": "hiz-finance", "symbol": "hiz", "name": "Hiz Finance" }, - { "id": "hk-coin", "symbol": "hkc", "name": "HK Coin" }, - { "id": "hkd-com-dao", "symbol": "hdao", "name": "Hkd.com Dao" }, - { "id": "hland-token", "symbol": "hland", "name": "HLand Token" }, - { "id": "hlth-token", "symbol": "hlth", "name": "HLTH Token" }, - { - "id": "hnk-orijent-1919-token", - "symbol": "ori", - "name": "HNK Orijent 1919 Token" - }, - { "id": "hobbs-networking", "symbol": "hnw", "name": "Hobbs Networking" }, - { "id": "hobonickels", "symbol": "hbn", "name": "Hobonickels" }, - { "id": "hodlada", "symbol": "HADA", "name": "HodlADA" }, - { "id": "hodlassets", "symbol": "hodl", "name": "HodlAssets" }, - { "id": "hodlbusd", "symbol": "hbusd", "name": "HodlBUSD" }, - { "id": "hodlcoin", "symbol": "hodl", "name": "HOdlcoin" }, - { "id": "hodler-heroes-nft", "symbol": "hhnft", "name": "Hodler Heroes NFT" }, - { "id": "hodl-eth", "symbol": "HETH", "name": "Hodl ETH" }, - { "id": "hodl-finance", "symbol": "hft", "name": "Hodl Finance" }, - { "id": "hodl-token", "symbol": "hodl", "name": "HODL" }, - { "id": "hodltree", "symbol": "htre", "name": "HodlTree" }, - { "id": "hodl-vault", "symbol": "hvlt", "name": "HODL Vault" }, - { "id": "hodooi-com", "symbol": "hod", "name": "HoDooi.com" }, - { "id": "hoff-coin", "symbol": "hoff", "name": "Hoff Coin" }, - { "id": "hoge-finance", "symbol": "hoge", "name": "Hoge Finance" }, - { "id": "hogl-finance", "symbol": "hogl", "name": "HOGL Finance" }, - { "id": "hokage-inu", "symbol": "HOKAGE", "name": "Hokage Inu" }, - { "id": "hokkaidu-inu", "symbol": "hokk", "name": "Hokkaido Inu" }, - { "id": "hold2earn", "symbol": "h2e", "name": "Hold2Earn" }, - { "id": "holdenomics", "symbol": "holdenomics", "name": "Holdenomics" }, - { "id": "holdermoon", "symbol": "hlm", "name": "HolderMoon" }, - { "id": "holderswap", "symbol": "hfs", "name": "HolderSwap" }, - { "id": "holdex-finance", "symbol": "holdex", "name": "Holdex Finance" }, - { "id": "holiday-token", "symbol": "HOL", "name": "Holiday Token" }, - { "id": "hollaex-token", "symbol": "xht", "name": "HollaEx Token" }, - { "id": "hollygold", "symbol": "hgold", "name": "HollyGold" }, - { "id": "hololoot", "symbol": "hol", "name": "Hololoot" }, - { "id": "holoride", "symbol": "ride", "name": "holoride" }, - { "id": "holotoken", "symbol": "hot", "name": "Holo" }, - { "id": "holydoge", "symbol": "hdoge", "name": "HolyDoge" }, - { "id": "holygrail", "symbol": "hly", "name": "HolyGrail" }, - { "id": "holyheld-2", "symbol": "move", "name": "Mover" }, - { "id": "holy-knight", "symbol": "holy", "name": "Holy Knight" }, - { "id": "holy-trinity", "symbol": "holy", "name": "Holy Trinity" }, - { "id": "home-coin", "symbol": "home", "name": "Home Coin" }, - { "id": "homeros", "symbol": "hmr", "name": "Homeros" }, - { "id": "homerun", "symbol": "hmrn", "name": "Homerun" }, - { "id": "hom-token", "symbol": "homt", "name": "HOM Token" }, - { "id": "hondaiscoin", "symbol": "hndc", "name": "HondaisCoin" }, - { "id": "honest-mining", "symbol": "hnst", "name": "Honest" }, - { "id": "honey", "symbol": "hny", "name": "Honey" }, - { "id": "honey-badger", "symbol": "honeybadger", "name": "Honey Badger" }, - { "id": "honeybear", "symbol": "bear", "name": "HoneyBear" }, - { "id": "honeybee", "symbol": "bee", "name": "HoneyBee" }, - { "id": "honeycomb-2", "symbol": "honey", "name": "Honeycomb" }, - { "id": "honey-deluxe", "symbol": "honeyd", "name": "Honey Deluxe" }, - { - "id": "honey-fancybearsmetaverse", - "symbol": "$honey", - "name": "Honey (Fancy Bears Metaverse)" - }, - { "id": "honeyfarm-finance", "symbol": "honey", "name": "HoneyFarm Finance" }, - { "id": "honey-finance", "symbol": "honey", "name": "Honey Finance" }, - { "id": "honeymoon-token", "symbol": "moon", "name": "HoneyMOON Token" }, - { "id": "honeypad", "symbol": "honey", "name": "HONEYPAD" }, - { - "id": "honey-pot-beekeepers", - "symbol": "honey", - "name": "Honey Pot BeeKeepers" - }, - { "id": "honey-token", "symbol": "sweet", "name": "Honey Token" }, - { "id": "honk-honk", "symbol": "honk", "name": "Honk Honk" }, - { "id": "hono", "symbol": "Hono", "name": "Hono" }, - { "id": "hoodler", "symbol": "hood", "name": "Hoodler" }, - { "id": "hoodrat-finance", "symbol": "hoodrat", "name": "Hoodrat Finance" }, - { "id": "hoopoe", "symbol": "hoop", "name": "Hoopoe" }, - { "id": "hoo-token", "symbol": "hoo", "name": "Hoo Token" }, - { "id": "hope-galaxy", "symbol": "hope", "name": "HOPE Galaxy" }, - { "id": "hope-token", "symbol": "hope", "name": "Hope Token" }, - { "id": "hoppers-game", "symbol": "fly", "name": "Hoppers Game" }, - { "id": "hoppy", "symbol": "hop", "name": "HOPPY" }, - { "id": "hopr", "symbol": "hopr", "name": "HOPR" }, - { "id": "hoqu", "symbol": "hqx", "name": "HOQU" }, - { "id": "hord", "symbol": "hord", "name": "Hord" }, - { "id": "horde", "symbol": "hor", "name": "Horde" }, - { "id": "horde-token", "symbol": "$horde", "name": "Horde Token" }, - { "id": "horgi", "symbol": "horgi", "name": "Horgi" }, - { "id": "horizondollar", "symbol": "hzd", "name": "Horizon Dollar" }, - { "id": "horizonland", "symbol": "hrz", "name": "Horizonland" }, - { "id": "horizon-protocol", "symbol": "hzn", "name": "Horizon Protocol" }, - { "id": "horny-doge", "symbol": "horny", "name": "Horny Doge" }, - { "id": "horuspay", "symbol": "horus", "name": "HorusPay" }, - { "id": "hosky", "symbol": "hosky", "name": "Hosky" }, - { "id": "hospital-coin", "symbol": "hosp", "name": "Hospital Coin" }, - { "id": "hotbit-token", "symbol": "htb", "name": "Hotbit Token" }, - { "id": "hot-cross", "symbol": "hotcross", "name": "Hot Cross" }, - { "id": "hotdoge", "symbol": "hotdoge", "name": "HotDoge" }, - { "id": "hot-doge", "symbol": "hotdoge", "name": "HotDoge [OLD]" }, - { "id": "hotdollars-token", "symbol": "hds", "name": "HotDollars Token" }, - { "id": "hotelium", "symbol": "htl", "name": "Hotelium" }, - { "id": "hotnow", "symbol": "hot", "name": "HotNow" }, - { "id": "hotzilla", "symbol": "hotzilla", "name": "HotZilla" }, - { "id": "housedao", "symbol": "house", "name": "HouseDAO" }, - { "id": "howdoo", "symbol": "udoo", "name": "Hyprr" }, - { "id": "howl-city", "symbol": "hwl", "name": "Howl City" }, - { "id": "howlx", "symbol": "hwxt", "name": "Howlx" }, - { "id": "hrdcoin", "symbol": "hrd", "name": "HRDcoin" }, - { "id": "hrdgcoin", "symbol": "hrdg", "name": "HRDGCOIN" }, - { "id": "hshare", "symbol": "hc", "name": "HyperCash" }, - { "id": "h-space-metaverse", "symbol": "hksm", "name": "H-Space Metaverse" }, - { "id": "htm", "symbol": "htm", "name": "HTM" }, - { "id": "htmlcoin", "symbol": "html", "name": "HTMLCOIN" }, - { "id": "htmoon", "symbol": "htmoon", "name": "HTMoon" }, - { "id": "htmoon-fomo", "symbol": "HTMoon", "name": "HTMoon FOMO" }, - { "id": "hubble", "symbol": "hbb", "name": "Hubble" }, - { "id": "hub-token", "symbol": "hub", "name": "Hub Token" }, - { "id": "huckleberry", "symbol": "finn", "name": "Huckleberry" }, - { "id": "hudi", "symbol": "hudi", "name": "Hudi" }, - { "id": "hughug-coin", "symbol": "hghg", "name": "HUGHUG" }, - { "id": "hugo-finance", "symbol": "hugo", "name": "Hugo Game" }, - { "id": "huh", "symbol": "huh", "name": "HUH Token" }, - { "id": "humancoin-2", "symbol": "hmnc", "name": "HumanCoin" }, - { "id": "humandao", "symbol": "hdao", "name": "humanDAO" }, - { "id": "humaniq", "symbol": "hmq", "name": "Humaniq" }, - { "id": "human-protocol", "symbol": "hmt", "name": "HUMAN Protocol" }, - { "id": "humans-ai", "symbol": "heart", "name": "Humans.ai" }, - { "id": "humanscape", "symbol": "hum", "name": "Humanscape" }, - { "id": "human-world", "symbol": "ssap", "name": "Human World" }, - { - "id": "hummingbird-egg-token", - "symbol": "hegg", - "name": "Hummingbird Egg Token" - }, - { - "id": "hummingbird-finance", - "symbol": "hmng", - "name": "Hummingbird Finance" - }, - { "id": "hummingbot", "symbol": "hbot", "name": "Hummingbot" }, - { "id": "hummus", "symbol": "hum", "name": "Hummus" }, - { "id": "humpback", "symbol": "hump", "name": "Whale Loans" }, - { "id": "hundred-finance", "symbol": "hnd", "name": "Hundred Finance" }, - { - "id": "hungarian-vizsla-inu", - "symbol": "hvi", - "name": "Hungarian Vizsla Inu" - }, - { "id": "hunger-doge", "symbol": "hungrydoge", "name": "Hunger Doge" }, - { "id": "hunger-token", "symbol": "hunger", "name": "Hunger Token" }, - { "id": "hungrybear", "symbol": "hungry", "name": "HungryBear" }, - { "id": "hungry-bees", "symbol": "hbee", "name": "Hungry Bees" }, - { "id": "hunny-love-token", "symbol": "love", "name": "HunnyDAO" }, - { "id": "hunter-crypto-coin", "symbol": "hcc", "name": "Hunter Crypto Coin" }, - { "id": "hunterdoge", "symbol": "$hd", "name": "HunterDoge" }, - { "id": "hunt-token", "symbol": "hunt", "name": "HUNT" }, - { - "id": "huobi-bitcoin-cash", - "symbol": "hbch", - "name": "Huobi Bitcoin Cash" - }, - { "id": "huobi-btc", "symbol": "hbtc", "name": "Huobi BTC" }, - { - "id": "huobi-btc-wormhole", - "symbol": "hbtc", - "name": "Huobi BTC (Wormhole)" - }, - { "id": "huobi-ethereum", "symbol": "heth", "name": "Huobi Ethereum" }, - { "id": "huobi-fil", "symbol": "hfil", "name": "Huobi Fil" }, - { "id": "huobi-litecoin", "symbol": "hltc", "name": "Huobi Litecoin" }, - { "id": "huobi-polkadot", "symbol": "hdot", "name": "Huobi Polkadot" }, - { "id": "huobi-pool-token", "symbol": "hpt", "name": "Huobi Pool Token" }, - { "id": "huobi-token", "symbol": "ht", "name": "Huobi Token" }, - { "id": "hupayx", "symbol": "hpx", "name": "HUPAYX" }, - { "id": "huplife", "symbol": "hup", "name": "HUP.LIFE" }, - { "id": "hurify", "symbol": "hur", "name": "Hurify" }, - { "id": "hurrian-network", "symbol": "mld", "name": "Hurrian Network" }, - { "id": "hurricane-nft", "symbol": "nhct", "name": "Hurricane NFT" }, - { - "id": "hurricaneswap-token", - "symbol": "hct", - "name": "HurricaneSwap Token" - }, - { "id": "husd", "symbol": "husd", "name": "HUSD" }, - { - "id": "husd-stablecoin-wormhole", - "symbol": "husd", - "name": "HUSD Stablecoin (Wormhole)" - }, - { "id": "hush", "symbol": "hush", "name": "Hush" }, - { "id": "husky", "symbol": "husky", "name": "Husky" }, - { "id": "husky-avax", "symbol": "husky", "name": "Husky AVAX" }, - { "id": "husky-inu", "symbol": "hdog", "name": "Husky Inu" }, - { "id": "huskyshiba", "symbol": "hshiba", "name": "HuskyShiba" }, - { "id": "huskyx", "symbol": "huskyx", "name": "HuskyX" }, - { "id": "hut34-entropy", "symbol": "entrp", "name": "Hut34 Entropy" }, - { "id": "hxro", "symbol": "hxro", "name": "Hxro" }, - { "id": "hxro-wormhole", "symbol": "hxro", "name": "Hxro (Wormhole)" }, - { "id": "hybrid", "symbol": "$hybrid", "name": "Hybrid" }, - { "id": "hybrid-bank-cash", "symbol": "hbc", "name": "Hybrid Bank Cash" }, - { "id": "hybrix", "symbol": "hy", "name": "Hybrix" }, - { "id": "hycon", "symbol": "hyc", "name": "Hycon" }, - { "id": "hydra", "symbol": "hydra", "name": "Hydra" }, - { "id": "hydra-token", "symbol": "hyd", "name": "Hydraledger" }, - { "id": "hydraverse", "symbol": "hdv", "name": "Hydraverse" }, - { "id": "hydro", "symbol": "hydro", "name": "Hydro" }, - { "id": "hydrogen-token", "symbol": "hgt", "name": "Hydrogen Token" }, - { "id": "hydro-h2o", "symbol": "h2o", "name": "Hydro H2O" }, - { "id": "hydrolink", "symbol": "hlink", "name": "HydroLink" }, - { "id": "hydro-protocol", "symbol": "hot", "name": "Hydro Protocol" }, - { - "id": "hydro-protocol-token", - "symbol": "hydro", - "name": "Hydro Protocol Token" - }, - { "id": "hyfi-token", "symbol": "hyfi", "name": "HyFi Token" }, - { "id": "hygenercoin", "symbol": "hg", "name": "Hygenercoin" }, - { "id": "hymnode", "symbol": "hnt", "name": "Hymnode" }, - { "id": "hyperalloy", "symbol": "alloy", "name": "HyperAlloy" }, - { "id": "hyperboost", "symbol": "hyperboost", "name": "HyperBoost" }, - { "id": "hyperchain-x", "symbol": "hyper", "name": "HyperChain X" }, - { - "id": "hyper-credit-network", - "symbol": "hpay", - "name": "Hyper Credit Network" - }, - { "id": "hyperdao", "symbol": "hdao", "name": "HyperDAO" }, - { "id": "hyper-deflate", "symbol": "hdfl", "name": "Hyper Deflate" }, - { "id": "hyperexchange", "symbol": "hx", "name": "HyperExchange" }, - { "id": "hyper-finance", "symbol": "hyfi", "name": "Hyper Finance" }, - { "id": "hyperion", "symbol": "hyn", "name": "Hyperion" }, - { "id": "hyperjump", "symbol": "jump", "name": "HyperJump" }, - { "id": "hyper-pay", "symbol": "hpy", "name": "Hyper Pay" }, - { "id": "hyperquant", "symbol": "hqt", "name": "HyperQuant" }, - { - "id": "hypersign-identity-token", - "symbol": "hid", - "name": "Hypersign Identity Token" - }, - { - "id": "hypersonic-finance", - "symbol": "hypersonic", - "name": "Hypersonic Finance" - }, - { "id": "hyperstake", "symbol": "hyp", "name": "Element" }, - { "id": "hyper-trust", "symbol": "hptt", "name": "Hyper Trust" }, - { "id": "hyperverse", "symbol": "hvt", "name": "HyperVerse" }, - { "id": "hyruleswap", "symbol": "rupee", "name": "HyruleSwap" }, - { "id": "hyve", "symbol": "hyve", "name": "Hyve" }, - { "id": "hzm-coin", "symbol": "hzm", "name": "HZM Coin" }, - { "id": "i0coin", "symbol": "i0c", "name": "I0Coin" }, - { "id": "i9-coin", "symbol": "i9c", "name": "i9 Coin" }, - { "id": "i9x-coin", "symbol": "i9x", "name": "i9X Coin" }, - { "id": "iab", "symbol": "iab", "name": "IAB" }, - { "id": "iagon", "symbol": "iag", "name": "Iagon" }, - { "id": "i-am-vaccinated", "symbol": "iamvax", "name": "I Am Vaccinated" }, - { "id": "ibank", "symbol": "ibank", "name": "iBank" }, - { "id": "ibax-network", "symbol": "ibxc", "name": "IBAX Network" }, - { "id": "ibetyou", "symbol": "iby", "name": "iBetYou" }, - { "id": "ibex", "symbol": "ibex", "name": "IBEX" }, - { "id": "ibg-eth", "symbol": "ibg", "name": "iBG (ETH)" }, - { "id": "ibg-token", "symbol": "ibg", "name": "iBG Token" }, - { "id": "ibithub", "symbol": "ibh", "name": "iBitHub" }, - { "id": "ibiza-token", "symbol": "ibz", "name": "Ibiza Token" }, - { "id": "ibnb-2", "symbol": "ibnb", "name": "iBNB" }, - { "id": "ibuffer-token", "symbol": "ibfr", "name": "iBuffer Token" }, - { "id": "icarus-finance", "symbol": "ica", "name": "Icarus Finance" }, - { "id": "icarus-network", "symbol": "ica", "name": "Icarus Network" }, - { "id": "ic-defi", "symbol": "icd", "name": "IC DeFi" }, - { "id": "iceberg", "symbol": "ICEBERG", "name": "ICEBERG" }, - { "id": "icebreak-r", "symbol": "icebrk", "name": "IceBreak-R" }, - { "id": "icecream-finance", "symbol": "cream", "name": "IceCream Finance" }, - { "id": "icecubes-finance", "symbol": "icube", "name": "IceCubes Finance" }, - { "id": "ice-dao", "symbol": "icy", "name": "Ice Dao" }, - { "id": "iceflake-finance", "symbol": "flake", "name": "IceFlake Finance" }, - { "id": "iceslush-finance", "symbol": "slush", "name": "IceSlush Finance" }, - { "id": "ice-token", "symbol": "ice", "name": "Popsicle Finance" }, - { "id": "ice-wormhole", "symbol": "ice", "name": "Ice (Wormhole)" }, - { "id": "ichello", "symbol": "ello", "name": "Ichello" }, - { "id": "ichi-farm", "symbol": "ichi", "name": "ICHI" }, - { "id": "ichigo-inu", "symbol": "ichigo", "name": "Ichigo Inu" }, - { "id": "i-coin", "symbol": "icn", "name": "I-Coin V2" }, - { "id": "icolcoin", "symbol": "icol", "name": "Icolcoin" }, - { "id": "icomex", "symbol": "icmx", "name": "iCOMEX" }, - { "id": "icommunity", "symbol": "icom", "name": "iCommunity" }, - { "id": "icon", "symbol": "icx", "name": "ICON" }, - { "id": "iconiq-lab-token", "symbol": "icnq", "name": "Iconic Token" }, - { "id": "icon-usdc", "symbol": "iusdc", "name": "Icon USDC" }, - { "id": "icrypto-world", "symbol": "icw", "name": "iCrypto World" }, - { "id": "icy-money", "symbol": "ICY", "name": "ICY.MONEY" }, - { "id": "idavoll-network", "symbol": "idv", "name": "Idavoll DAO" }, - { "id": "ideachain", "symbol": "ich", "name": "IdeaChain" }, - { "id": "ideamarket", "symbol": "imo", "name": "Ideamarket" }, - { "id": "ideanet-token", "symbol": "inet", "name": "Ideanet Token" }, - { "id": "ideaology", "symbol": "idea", "name": "Ideaology" }, - { "id": "ideas", "symbol": "IDS", "name": "IDEAS" }, - { - "id": "idefiyieldprotocol", - "symbol": "idyp", - "name": "iDeFiYieldProtocol" - }, - { "id": "idena", "symbol": "iDNA", "name": "Idena" }, - { "id": "identity", "symbol": "idtt", "name": "Identity" }, - { "id": "idexo-token", "symbol": "ido", "name": "Idexo Token" }, - { "id": "idia", "symbol": "idia", "name": "Impossible Finance Launchpad" }, - { "id": "idk", "symbol": "idk", "name": "IDK" }, - { "id": "idle", "symbol": "idle", "name": "IDLE" }, - { "id": "idle-cyber", "symbol": "afk", "name": "Idle Cyber" }, - { - "id": "idle-dai-risk-adjusted", - "symbol": "idleDAISafe", - "name": "IdleDAI (Risk Adjusted)" - }, - { - "id": "idle-dai-yield", - "symbol": "idleDAIYield", - "name": "IdleDAI (Best Yield)" - }, - { "id": "idle-mystic", "symbol": "mst", "name": "Idle Mystic" }, - { "id": "idle-mystic-token", "symbol": "imt", "name": "Idle Mystic Token" }, - { - "id": "idle-susd-yield", - "symbol": "idleSUSDYield", - "name": "IdleSUSD (Yield)" - }, - { "id": "idle-token", "symbol": "idle", "name": "IDLE Token" }, - { - "id": "idle-tusd-yield", - "symbol": "idleTUSDYield", - "name": "IdleTUSD (Best Yield)" - }, - { - "id": "idle-usdc-risk-adjusted", - "symbol": "idleUSDCSafe", - "name": "IdleUSDC (Risk Adjusted)" - }, - { - "id": "idle-usdc-yield", - "symbol": "idleUSDCYield", - "name": "IdleUSDC (Yield)" - }, - { - "id": "idle-usdt-risk-adjusted", - "symbol": "IdleUSDTSafe", - "name": "IdleUSDT (Risk Adjusted)" - }, - { - "id": "idle-usdt-yield", - "symbol": "idleUSDTYield", - "name": "IdleUSDT (Yield)" - }, - { - "id": "idle-wbtc-yield", - "symbol": "idleWBTCYield", - "name": "IdleWBTC (Best Yield)" - }, - { "id": "idm-token", "symbol": "idm", "name": "IDM Token" }, - { "id": "idol", "symbol": "$IDOL", "name": "IDOL" }, - { "id": "idoscan", "symbol": "idoscan", "name": "Idoscan" }, - { "id": "iethereum", "symbol": "ieth", "name": "iEthereum" }, - { "id": "iexec-rlc", "symbol": "rlc", "name": "iExec RLC" }, - { "id": "ifarm", "symbol": "ifarm", "name": "iFARM" }, - { "id": "ifoswap-token", "symbol": "h2o", "name": "IFOSwap Token" }, - { "id": "iftoken", "symbol": "ift", "name": "IFToken" }, - { "id": "ifx24", "symbol": "ifx24", "name": "IFX24" }, - { "id": "ig-gold", "symbol": "igg", "name": "IG Gold" }, - { "id": "ignis", "symbol": "ignis", "name": "Ignis" }, - { "id": "ignition", "symbol": "ic", "name": "Ignition" }, - { "id": "igtoken", "symbol": "ig", "name": "IGToken" }, - { - "id": "iht-real-estate-protocol", - "symbol": "iht", - "name": "IHT Real Estate Protocol" - }, - { "id": "iinjaz", "symbol": "ijz", "name": "iinjaz" }, - { "id": "ijascoin", "symbol": "ijc", "name": "IjasCoin" }, - { "id": "ik-coin", "symbol": "ikc", "name": "iK Coin" }, - { "id": "ikura-token", "symbol": "ikura", "name": "Ikura Token" }, - { "id": "ilayer", "symbol": "ilayer", "name": "iLayer" }, - { "id": "ilcoin", "symbol": "ilc", "name": "ILCOIN" }, - { "id": "illiquiddao", "symbol": "jpegs", "name": "IlliquidDAO" }, - { "id": "illuvium", "symbol": "ilv", "name": "Illuvium" }, - { "id": "illuvium-wormhole", "symbol": "ilv", "name": "Illuvium (Wormhole)" }, - { "id": "i-love-you", "symbol": "ily", "name": "I Love You" }, - { "id": "ilus-coin", "symbol": "ilus", "name": "ILUS Coin" }, - { "id": "imagecash", "symbol": "imgc", "name": "ImageCash" }, - { "id": "imagecoin", "symbol": "img", "name": "ImageCoin" }, - { "id": "imagictoken", "symbol": "imagic", "name": "iMagicToken" }, - { "id": "ime-lab", "symbol": "lime", "name": "iMe Lab" }, - { "id": "immortaldao", "symbol": "immo", "name": "ImmortalDAO" }, - { "id": "immortl", "symbol": "imrtl", "name": "Immortl" }, - { "id": "immutable", "symbol": "dara", "name": "Immutable" }, - { "id": "immutable-x", "symbol": "imx", "name": "Immutable X" }, - { "id": "imo", "symbol": "imo", "name": "IMO" }, - { "id": "i-money-crypto", "symbol": "imc", "name": "i Money Crypto" }, - { "id": "impactmarket", "symbol": "pact", "name": "impactMarket" }, - { "id": "impactx", "symbol": "impactx", "name": "ImpactX" }, - { "id": "impactxp", "symbol": "impactxp", "name": "ImpactXP" }, - { "id": "imperial-obelisk", "symbol": "imp", "name": "Imperial Obelisk" }, - { "id": "imperium-empires", "symbol": "ime", "name": "Imperium Empires" }, - { "id": "impermax", "symbol": "imx", "name": "Impermax" }, - { "id": "impleum", "symbol": "impl", "name": "Impleum" }, - { "id": "impossible-finance", "symbol": "if", "name": "Impossible Finance" }, - { "id": "improved-bitcoin", "symbol": "iBTC", "name": "Improved Bitcoin" }, - { "id": "impulse-by-fdr", "symbol": "impulse", "name": "Impulse By FDR" }, - { "id": "impulseven", "symbol": "i7", "name": "ImpulseVen" }, - { "id": "inari", "symbol": "inari", "name": "Inari" }, - { "id": "incakoin", "symbol": "nka", "name": "IncaKoin" }, - { "id": "incinerate", "symbol": "FIRE", "name": "Incinerate" }, - { "id": "inci-token", "symbol": "INCI", "name": "Inci Token" }, - { "id": "incognito-2", "symbol": "prv", "name": "Incognito" }, - { "id": "incoin", "symbol": "in", "name": "InCoin" }, - { "id": "income", "symbol": "income", "name": "Income" }, - { "id": "income-island", "symbol": "income", "name": "Income Island" }, - { "id": "incooom-genesis", "symbol": "cooom", "name": "Incooom Genesis" }, - { - "id": "incooom-genesis-gold", - "symbol": "gcooom", - "name": "Incooom Genesis Gold" - }, - { - "id": "incooom-genesis-psychedelic", - "symbol": "pcooom", - "name": "Incooom Genesis Psychedelic" - }, - { "id": "indahash", "symbol": "idh", "name": "indaHash" }, - { "id": "index-chain", "symbol": "IDX", "name": "Index Chain" }, - { "id": "index-cooperative", "symbol": "index", "name": "Index Cooperative" }, - { - "id": "index-coop-eth-2x-flexible-leverage-index", - "symbol": "eth2x-fli-p", - "name": "Index Coop - ETH 2x Flexible Leverage Index (Polygon)" - }, - { - "id": "index-coop-inverse-eth-flexible-leverage-index", - "symbol": "ieth-fli-p", - "name": "Index Coop - Inverse ETH Flexible Leverage Index" - }, - { - "id": "index-coop-inverse-matic-flexible-leverage-index", - "symbol": "imatic-fli-p", - "name": "Index Coop - Inverse MATIC Flexible Leverage Index" - }, - { - "id": "index-coop-matic-2x-flexible-leverage-index", - "symbol": "MATIC2x-FLI-P", - "name": "Index Coop - MATIC 2x Flexible Leverage Index" - }, - { "id": "indexed-finance", "symbol": "ndx", "name": "Indexed Finance" }, - { "id": "indian-shiba-inu", "symbol": "indshib", "name": "Indian Shiba Inu" }, - { "id": "indigg", "symbol": "indi", "name": "IndiGG" }, - { "id": "indodex", "symbol": "iddx", "name": "IndoDEX" }, - { "id": "indorse", "symbol": "ind", "name": "Indorse" }, - { "id": "infam", "symbol": "inf", "name": "Infam" }, - { "id": "infbundle", "symbol": "inf", "name": "INFBUNDLE" }, - { "id": "infchain", "symbol": "inf", "name": "InfChain" }, - { "id": "infinitecoin", "symbol": "ifc", "name": "Infinitecoin" }, - { "id": "infinitee", "symbol": "inftee", "name": "Infinitee" }, - { - "id": "infinite-ecosystem", - "symbol": "infinity", - "name": "Infinite Ecosystem" - }, - { "id": "infinite-fleet", "symbol": "inf", "name": "Infinite Fleet" }, - { "id": "infinite-launch", "symbol": "ila", "name": "Infinite Launch" }, - { "id": "infinite-ricks", "symbol": "rick", "name": "Infinite Ricks" }, - { "id": "infinito", "symbol": "inft", "name": "Infinito" }, - { "id": "infinitup", "symbol": "iup", "name": "InfinitUp" }, - { "id": "infinitx", "symbol": "inx", "name": "InfinitX" }, - { "id": "infinity-arena", "symbol": "inaz", "name": "Infinity Arena" }, - { "id": "infinitycake", "symbol": "incake", "name": "InfinityCake" }, - { "id": "infinitycash", "symbol": "ifc", "name": "InfinityCash" }, - { "id": "infinity-economics", "symbol": "xin", "name": "Infinity Economics" }, - { "id": "infinity-esaham", "symbol": "infs", "name": "Infinity Esaham" }, - { "id": "infinity-eth", "symbol": "ieth", "name": "Infinity ETH" }, - { "id": "infinity-finance", "symbol": "8fi", "name": "Infinity Finance" }, - { "id": "infinity-game-nft", "symbol": "ign", "name": "Infinity Game NFT" }, - { "id": "infinitygaming", "symbol": "play", "name": "InfinityGaming" }, - { "id": "infinitypad", "symbol": "infp", "name": "InfinityPad" }, - { "id": "infinity-pad", "symbol": "ipad", "name": "Infinity Pad" }, - { - "id": "infinity-protocol-bsc", - "symbol": "infinity", - "name": "Infinity Protocol BSC" - }, - { - "id": "infinity-rocket-token", - "symbol": "irt", - "name": "Infinity Rocket Token" - }, - { "id": "infinity-skies", "symbol": "isky", "name": "Infinity Skies" }, - { "id": "infinium", "symbol": "inf", "name": "Infinium" }, - { "id": "inflationcoin", "symbol": "iflt", "name": "InflationCoin" }, - { - "id": "inflation-hedging-coin", - "symbol": "ihc", - "name": "Inflation Hedging Coin" - }, - { "id": "inflex-finance", "symbol": "inflex", "name": "Inflex Finance" }, - { "id": "infliv", "symbol": "ifv", "name": "INFLIV" }, - { "id": "influencer", "symbol": "imi", "name": "Influencer" }, - { "id": "influencer-doge", "symbol": "idoge", "name": "Influencer Doge" }, - { - "id": "influencer-finance", - "symbol": "influence", - "name": "Influencer Finance" - }, - { "id": "influencer-token", "symbol": "inf", "name": "INFLUENCER TOKEN" }, - { "id": "influxcoin", "symbol": "infx", "name": "Influxcoin" }, - { "id": "infomatix", "symbol": "info", "name": "Infomatix" }, - { "id": "info-token", "symbol": "info", "name": "Kardia Info" }, - { "id": "inft-platform", "symbol": "inft", "name": "iNFT Token" }, - { "id": "inftspace", "symbol": "ins", "name": "iNFTspace" }, - { "id": "inheritance-art", "symbol": "iai", "name": "inheritance Art" }, - { "id": "injective-protocol", "symbol": "inj", "name": "Injective" }, - { "id": "injeolmi", "symbol": "wijm", "name": "Wrapped Injeolmi" }, - { "id": "ink", "symbol": "ink", "name": "Ink" }, - { "id": "ink-fantom", "symbol": "ink", "name": "Ink Fantom" }, - { "id": "ink-protocol", "symbol": "xnk", "name": "Ink Protocol" }, - { "id": "inkz", "symbol": "inkz", "name": "INKz" }, - { "id": "inlock-token", "symbol": "ilk", "name": "INLOCK" }, - { "id": "inmediate", "symbol": "dit", "name": "Direct Insurance Token" }, - { "id": "innova", "symbol": "inn", "name": "Innova" }, - { "id": "innovaminex", "symbol": "minx", "name": "InnovaMinex" }, - { - "id": "innovation-blockchain-payment", - "symbol": "IBP", - "name": "Innovation Blockchain Payment" - }, - { - "id": "innovative-bioresearch", - "symbol": "innbc", - "name": "Innovative Bioresearch Coin" - }, - { - "id": "innovativebioresearchclassic", - "symbol": "innbcl", - "name": "InnovativeBioresearchClassic" - }, - { "id": "ino-coin", "symbol": "ino", "name": "Ino Coin" }, - { "id": "inoovi", "symbol": "ivi", "name": "Inoovi" }, - { "id": "inpoker", "symbol": "inp", "name": "InPoker" }, - { "id": "inpulse-x", "symbol": "ipx", "name": "InpulseX" }, - { "id": "ins3", "symbol": "ins3", "name": "INS3" }, - { "id": "insanecoin", "symbol": "insn", "name": "INSaNe" }, - { "id": "insight-protocol", "symbol": "inx", "name": "Insight Protocol" }, - { "id": "insights-network", "symbol": "instar", "name": "INSTAR" }, - { "id": "instadapp", "symbol": "inst", "name": "Instadapp" }, - { "id": "instantxrp", "symbol": "instantxrp", "name": "InstantXRP" }, - { "id": "instaraise", "symbol": "insta", "name": "Instaraise" }, - { "id": "instinct", "symbol": "instinct", "name": "Instinct" }, - { - "id": "instrumental-finance", - "symbol": "strm", - "name": "Instrumental Finance" - }, - { "id": "insula", "symbol": "isla", "name": "Insula" }, - { "id": "insurace", "symbol": "insur", "name": "InsurAce" }, - { "id": "insure", "symbol": "sure", "name": "inSure DeFi" }, - { "id": "insuredao", "symbol": "insure", "name": "InsureDAO" }, - { "id": "insured-finance", "symbol": "infi", "name": "Insured Finance" }, - { "id": "insurepal", "symbol": "ipl", "name": "InsurePal" }, - { "id": "insureum", "symbol": "isr", "name": "Insureum" }, - { "id": "insurex", "symbol": "ixt", "name": "iXledger" }, - { "id": "integral", "symbol": "itgr", "name": "Integral" }, - { "id": "integritee", "symbol": "teer", "name": "Integritee" }, - { "id": "integrity", "symbol": "grit", "name": "Integrity" }, - { - "id": "intelligent-investment-chain", - "symbol": "iic", - "name": "Intelligent Investment Chain" - }, - { "id": "intelligent-mining", "symbol": "im", "name": "Intelligent Mining" }, - { "id": "intercoin", "symbol": "itr", "name": "Intercoin" }, - { "id": "intercrone", "symbol": "icr", "name": "InterCrone" }, - { - "id": "interest-bearing-bitcoin", - "symbol": "ibbtc", - "name": "Interest Bearing Bitcoin" - }, - { - "id": "interest-bearing-bitcoin-wormhole", - "symbol": "ibbtc", - "name": "Interest Bearing Bitcoin (Wormhole)" - }, - { - "id": "interest-bearing-eth", - "symbol": "ibETH", - "name": "Interest Bearing ETH" - }, - { - "id": "interest-compounding-eth-index", - "symbol": "iceth", - "name": "Interest Compounding ETH Index" - }, - { "id": "interfinex-bills", "symbol": "ifex", "name": "Interfinex Bills" }, - { - "id": "intergalactic-cockroach", - "symbol": "icc", - "name": "Intergalactic Cockroach" - }, - { "id": "interlude", "symbol": "ish", "name": "Interlude" }, - { - "id": "inter-milan-fan-token", - "symbol": "inter", - "name": "Inter Milan Fan Token" - }, - { - "id": "international-cryptox", - "symbol": "incx", - "name": "International CryptoX" - }, - { "id": "internet-computer", "symbol": "icp", "name": "Internet Computer" }, - { "id": "internet-node-token", "symbol": "int", "name": "INTchain" }, - { - "id": "internet-of-energy-network", - "symbol": "ioen", - "name": "Internet of Energy Network" - }, - { "id": "internxt", "symbol": "inxt", "name": "Internxt" }, - { "id": "intersola", "symbol": "isola", "name": "Intersola" }, - { - "id": "interstellar-domain-order", - "symbol": "ido", - "name": "Interstellar Domain Order" - }, - { "id": "intervalue", "symbol": "inve", "name": "InterValue" }, - { "id": "intexcoin", "symbol": "intx", "name": "INTEXCOIN" }, - { "id": "intucoin", "symbol": "intu", "name": "INTUCoin" }, - { "id": "inubis", "symbol": "inubis", "name": "Inubis" }, - { "id": "inugami", "symbol": "inugami", "name": "Inugami" }, - { - "id": "inu-jump-and-the-temple-of-shiba", - "symbol": "inujump", - "name": "Inu Jump and the Temple of Shiba" - }, - { "id": "inu-token", "symbol": "inu", "name": "INU Token" }, - { "id": "inu-wars", "symbol": "IWR", "name": "Inu Wars" }, - { "id": "inuyasha", "symbol": "inuyasha", "name": "Inuyasha" }, - { "id": "inventoryclub", "symbol": "vnt", "name": "InventoryClub" }, - { - "id": "inverse-btc-flexible-leverage-index", - "symbol": "iBTC-FLI-P", - "name": "Inverse BTC Flexible Leverage Index" - }, - { - "id": "inverse-ethereum-volatility-index-token", - "symbol": "iethv", - "name": "Inverse Ethereum Volatility Index Token" - }, - { "id": "inverse-finance", "symbol": "inv", "name": "Inverse Finance" }, - { "id": "investdex", "symbol": "invest", "name": "InvestDex" }, - { "id": "investdigital", "symbol": "idt", "name": "InvestDigital" }, - { "id": "investel", "symbol": "investel", "name": "Investel" }, - { "id": "investin", "symbol": "ivn", "name": "Investin" }, - { - "id": "invest-like-stakeborg-index", - "symbol": "ilsi", - "name": "Invest Like Stakeborg Index" - }, - { "id": "invictus", "symbol": "in", "name": "Invictus" }, - { - "id": "invictus-capital-token", - "symbol": "icap", - "name": "Invictus Capital Token" - }, - { - "id": "invictus-hyprion-fund", - "symbol": "ihf", - "name": "Invictus Hyperion Fund" - }, - { "id": "invi-token", "symbol": "invi", "name": "INVI Token" }, - { "id": "invoice-coin", "symbol": "ivc", "name": "Invoice Coin" }, - { "id": "invoke", "symbol": "iv", "name": "Invoker" }, - { "id": "iobusd", "symbol": "iobusd", "name": "ioBUSD" }, - { "id": "iocoin", "symbol": "ioc", "name": "I/O Coin" }, - { "id": "ioeth", "symbol": "ioeth", "name": "ioETH" }, - { "id": "ioex", "symbol": "ioex", "name": "ioeX" }, - { "id": "ioi-token", "symbol": "ioi", "name": "IOI Token" }, - { "id": "ion", "symbol": "ion", "name": "Ion" }, - { "id": "ionchain-token", "symbol": "ionc", "name": "IONChain" }, - { "id": "ionomy", "symbol": "ion", "name": "Ionomy" }, - { "id": "iostoken", "symbol": "iost", "name": "IOST" }, - { "id": "iota", "symbol": "miota", "name": "IOTA" }, - { "id": "iot-chain", "symbol": "itc", "name": "IoT Chain" }, - { "id": "ioten", "symbol": "iotn", "name": "IOTEN" }, - { "id": "iotex", "symbol": "iotx", "name": "IoTeX" }, - { "id": "iotexchart", "symbol": "iotexchart", "name": "Iotexchart" }, - { "id": "iotexdoge", "symbol": "iodoge", "name": "IotexDoge" }, - { "id": "iotex-monster-go", "symbol": "mtgo", "name": "Iotex Monster Go" }, - { "id": "iotexpad", "symbol": "tex", "name": "IoTeXPad" }, - { "id": "iotexshiba", "symbol": "ioshib", "name": "IoTexShiba" }, - { "id": "iouni", "symbol": "iouni", "name": "ioUNI" }, - { "id": "iousdc", "symbol": "iousdc", "name": "ioUSDC" }, - { "id": "iousdt", "symbol": "iousdt", "name": "ioUSDT" }, - { "id": "iowbtc", "symbol": "iowbtc", "name": "ioWBTC" }, - { "id": "iown", "symbol": "iown", "name": "iOWN Token" }, - { "id": "ipay", "symbol": "ipay", "name": "iPay" }, - { "id": "ipse", "symbol": "post", "name": "IPSE" }, - { "id": "ipulse", "symbol": "pls", "name": "iPulse" }, - { "id": "ipx-token", "symbol": "ipx", "name": "Tachyon Protocol" }, - { "id": "iq-cash", "symbol": "iq", "name": "IQ.cash" }, - { "id": "iqcoin", "symbol": "iqcoin", "name": "IQCoin" }, - { "id": "iq-coin", "symbol": "IQG", "name": "IQ Coin" }, - { "id": "iqeon", "symbol": "iqn", "name": "IQeon" }, - { "id": "iqoniq", "symbol": "iqq", "name": "Iqoniq" }, - { "id": "iq-protocol", "symbol": "iqt", "name": "IQ Protocol" }, - { - "id": "irena-green-energy", - "symbol": "irena", - "name": "IRENA Green Energy" - }, - { "id": "iridium", "symbol": "ird", "name": "Iridium" }, - { "id": "iris-network", "symbol": "iris", "name": "IRISnet" }, - { "id": "iris-token-2", "symbol": "iris", "name": "Iris Token" }, - { "id": "iron-bank", "symbol": "ib", "name": "Iron Bank" }, - { "id": "iron-bank-euro", "symbol": "ibeur", "name": "Iron Bank EURO" }, - { "id": "iron-bsc", "symbol": "iron", "name": "Iron BSC" }, - { "id": "iron-finance", "symbol": "ice", "name": "Iron Finance" }, - { "id": "ironman", "symbol": "ironman", "name": "Ironman" }, - { "id": "iron-stablecoin", "symbol": "iron", "name": "Iron" }, - { - "id": "iron-titanium-token", - "symbol": "titan", - "name": "IRON Titanium Token" - }, - { "id": "isalcoin", "symbol": "isal", "name": "Isalcoin" }, - { - "id": "isengard-nft-marketplace", - "symbol": "iset-84e55e", - "name": "Isengard NFT Marketplace" - }, - { - "id": "ishares-msci-world-etf-tokenized-stock-defichain", - "symbol": "dURTH", - "name": "iShares MSCI World ETF Tokenized Stock Defichain" - }, - { "id": "isiklar-coin", "symbol": "isikc", "name": "Isiklar Coin" }, - { "id": "islamicoin", "symbol": "islami", "name": "ISLAMICOIN" }, - { "id": "island-boyz", "symbol": "$islbyz", "name": "Island Boyz" }, - { "id": "island-doges", "symbol": "island", "name": "Island Doges" }, - { "id": "islander", "symbol": "isa", "name": "Islander" }, - { "id": "island-inu", "symbol": "islainu", "name": "Island Inu" }, - { "id": "islandswap", "symbol": "isl", "name": "Islandswap" }, - { "id": "isotopec", "symbol": "iso", "name": "IsotopeC" }, - { "id": "ispolink", "symbol": "isp", "name": "Ispolink" }, - { - "id": "istanbul-basaksehir-fan-token", - "symbol": "ibfk", - "name": "İstanbul Başakşehir Fan Token" - }, - { "id": "istardust", "symbol": "isdt", "name": "Istardust" }, - { "id": "italian-lira", "symbol": "itl", "name": "Italian Lira" }, - { "id": "italo", "symbol": "xta", "name": "Italo" }, - { "id": "itam-games", "symbol": "itam", "name": "ITAM Games" }, - { "id": "itc", "symbol": "itc", "name": "ITC" }, - { "id": "iten", "symbol": "iten", "name": "ITEN" }, - { - "id": "iteration-syndicate", - "symbol": "ITS", - "name": "Iteration Syndicate" - }, - { "id": "iticoin", "symbol": "iti", "name": "iTicoin" }, - { - "id": "itrust-governance-token", - "symbol": "itg", - "name": "iTrust Governance Token" - }, - { "id": "itsmyne", "symbol": "myne", "name": "ITSMYNE" }, - { "id": "its-not-art", "symbol": "notart", "name": "Its Not Art" }, - { "id": "iungo", "symbol": "ing", "name": "Iungo" }, - { "id": "iupixel", "symbol": "PIXEL", "name": "iUPixel" }, - { "id": "ivogel", "symbol": "ivg", "name": "IVOGEL" }, - { "id": "ivory", "symbol": "ivory", "name": "Ivory" }, - { "id": "ixcoin", "symbol": "ixc", "name": "Ixcoin" }, - { "id": "ixicash", "symbol": "ixi", "name": "IxiCash" }, - { "id": "ixinium", "symbol": "xxa", "name": "Ixinium" }, - { "id": "ixirswap", "symbol": "ixir", "name": "Ixirswap" }, - { "id": "ixo", "symbol": "ixo", "name": "IXO" }, - { "id": "ix-swap", "symbol": "ixs", "name": "IX Swap" }, - { "id": "ix-token", "symbol": "ixt", "name": "IX Token" }, - { "id": "izanagi", "symbol": "nagi", "name": "Izanagi" }, - { "id": "ize", "symbol": "ize", "name": "IZE" }, - { "id": "izumi-finance", "symbol": "izi", "name": "Izumi Finance" }, - { "id": "jackpool-finance", "symbol": "jfi", "name": "JackPool.finance" }, - { "id": "jackpot", "symbol": "777", "name": "Jackpot" }, - { "id": "jackpot-army", "symbol": "jackpot", "name": "Jackpot Army" }, - { "id": "jackpot-token", "symbol": "jpt", "name": "Jackpot Token" }, - { "id": "jack-token", "symbol": "jack", "name": "Jack Token" }, - { "id": "jacy", "symbol": "jacy", "name": "JACY" }, - { "id": "jade-currency", "symbol": "jade", "name": "Jade Currency" }, - { "id": "jade-protocol", "symbol": "jade", "name": "Jade Protocol" }, - { "id": "jaguarswap", "symbol": "jaguar", "name": "JaguarSwap" }, - { "id": "jaiho-crypto", "symbol": "jaiho", "name": "Jaiho Crypto" }, - { "id": "jarvis", "symbol": "jar", "name": "Jarvis+" }, - { - "id": "jarvis-reward-token", - "symbol": "jrt", - "name": "Jarvis Reward Token" - }, - { - "id": "jarvis-synthetic-australian-dollar", - "symbol": "jaud", - "name": "Jarvis Synthetic Australian Dollar" - }, - { - "id": "jarvis-synthetic-british-pound", - "symbol": "jgbp", - "name": "Jarvis Synthetic British Pound" - }, - { - "id": "jarvis-synthetic-canadian-dollar", - "symbol": "jcad", - "name": "Jarvis Synthetic Canadian Dollar" - }, - { - "id": "jarvis-synthetic-chinese-yuan", - "symbol": "jcny", - "name": "Jarvis Synthetic Chinese Yuan" - }, - { - "id": "jarvis-synthetic-euro", - "symbol": "jeur", - "name": "Jarvis Synthetic Euro" - }, - { - "id": "jarvis-synthetic-japanese-yen", - "symbol": "jjpy", - "name": "Jarvis Synthetic Japanese Yen" - }, - { - "id": "jarvis-synthetic-singapore-dollar", - "symbol": "jsgd", - "name": "Jarvis Synthetic Singapore Dollar" - }, - { - "id": "jarvis-synthetic-swedish-krona", - "symbol": "jsek", - "name": "Jarvis Synthetic Swedish Krona" - }, - { - "id": "jarvis-synthetic-swiss-franc", - "symbol": "jchf", - "name": "Jarvis Synthetic Swiss Franc" - }, - { "id": "jasmycoin", "symbol": "jasmy", "name": "JasmyCoin" }, - { "id": "javascript-token", "symbol": "js", "name": "JavaScript Token" }, - { "id": "jax-network", "symbol": "wjxn", "name": "Jax.Network" }, - { "id": "jd-coin", "symbol": "jdc", "name": "JD Coin" }, - { "id": "jdi-token", "symbol": "jdi", "name": "JDI Token" }, - { "id": "jedstar", "symbol": "jed", "name": "JEDSTAR" }, - { "id": "jeet", "symbol": "jeet", "name": "JEET" }, - { "id": "jejudoge", "symbol": "jejudoge", "name": "Jejudoge" }, - { "id": "jejudoge-bsc", "symbol": "jejudoge", "name": "Jejudoge BSC" }, - { "id": "jem", "symbol": "jem", "name": "Jem" }, - { - "id": "jenny-metaverse-dao-token", - "symbol": "uJENNY", - "name": "Jenny Metaverse DAO Token" - }, - { "id": "jeritex", "symbol": "jrit", "name": "JERITEX" }, - { "id": "jet", "symbol": "jet", "name": "JET" }, - { "id": "jetcoin", "symbol": "jet", "name": "Jetcoin" }, - { "id": "jetoken", "symbol": "jets", "name": "JeToken" }, - { "id": "jetswap-token", "symbol": "wings", "name": "JetSwap Token" }, - { "id": "jewel", "symbol": "jwl", "name": "Jewel" }, - { "id": "jfin-coin", "symbol": "jfin", "name": "JFIN Coin" }, - { "id": "jigen", "symbol": "jig", "name": "Jigen" }, - { "id": "jigsaw", "symbol": "jigsaw", "name": "Jigsaw" }, - { "id": "jigstack", "symbol": "stak", "name": "Jigstack" }, - { "id": "jindoge", "symbol": "jindoge", "name": "Jindoge" }, - { "id": "jindo-inu", "symbol": "jind", "name": "Jindo Inu" }, - { "id": "jk-coin", "symbol": "jk", "name": "JK Coin" }, - { "id": "jmtime", "symbol": "jmt", "name": "JMTIME" }, - { "id": "jobcash", "symbol": "jch", "name": "JobCash" }, - { "id": "jobchain", "symbol": "job", "name": "Jobchain" }, - { "id": "joe", "symbol": "joe", "name": "JOE" }, - { "id": "joe-hat-token", "symbol": "hat", "name": "Joe Hat Token" }, - { "id": "joe-wormhole", "symbol": "joe", "name": "JOE (Wormhole)" }, - { "id": "joincoin", "symbol": "join", "name": "JoinCoin" }, - { "id": "joint", "symbol": "joint", "name": "Joint Ventures" }, - { "id": "jointer", "symbol": "jntr", "name": "Jointer" }, - { "id": "jojo", "symbol": "jojo", "name": "JOJO" }, - { "id": "jojo-inu", "symbol": "jojo", "name": "JoJo Inu" }, - { "id": "joke-community", "symbol": "$joke", "name": "Joke Community" }, - { - "id": "jokermanor-metaverse", - "symbol": "jkt", - "name": "JokerManor Metaverse" - }, - { "id": "joker-token", "symbol": "joker", "name": "Joker Token" }, - { "id": "jokes-meme", "symbol": "joke", "name": "Jokes Meme" }, - { "id": "jomon-shiba", "symbol": "jshiba", "name": "Jomon Shiba" }, - { "id": "jones-dao", "symbol": "jones", "name": "Jones DAO" }, - { "id": "joorschain", "symbol": "jic", "name": "JoorsChain" }, - { "id": "joos-protocol", "symbol": "joos", "name": "JOOS Protocol" }, - { "id": "joulecoin", "symbol": "xjo", "name": "Joulecoin" }, - { "id": "joys", "symbol": "joys", "name": "JOYS" }, - { "id": "joystick-2", "symbol": "joy", "name": "Joystick" }, - { "id": "joystream", "symbol": "joy", "name": "Joystream" }, - { "id": "jpaw-inu", "symbol": "jpaw", "name": "Jpaw Inu" }, - { "id": "jpeg-d", "symbol": "jpeg", "name": "JPEG'd" }, - { "id": "jpegvaultdao", "symbol": "jpeg", "name": "JPEGvaultDAO [OLD]" }, - { "id": "jpegvaultdao-2", "symbol": "jpeg", "name": "JPEGvaultDAO" }, - { "id": "jpg-nft-index", "symbol": "jpg", "name": "JPG NFT Index" }, - { "id": "jpool", "symbol": "jsol", "name": "JPool" }, - { "id": "jpyc", "symbol": "jpyc", "name": "JPY Coin" }, - { "id": "jsb-foundation", "symbol": "jsb", "name": "JSB Foundation" }, - { "id": "jswap-finance", "symbol": "jf", "name": "Jswap.Finance" }, - { "id": "jubi-token", "symbol": "jt", "name": "Jubi Token" }, - { "id": "juggernaut", "symbol": "jgn", "name": "Juggernaut" }, - { "id": "juicebox", "symbol": "jbx", "name": "Juicebox" }, - { "id": "juice-coin", "symbol": "juice", "name": "Juice Coin" }, - { "id": "julien", "symbol": "julien", "name": "JULIEN" }, - { "id": "julswap", "symbol": "juld", "name": "JulSwap" }, - { "id": "jumbo", "symbol": "jub", "name": "Jumbo" }, - { "id": "jumptoken", "symbol": "JMPT", "name": "JumpToken" }, - { "id": "junca-cash", "symbol": "jcc", "name": "Junca cash" }, - { "id": "jungle", "symbol": "Jungle", "name": "Jungle" }, - { "id": "jungle-defi", "symbol": "jfi", "name": "Jungle DeFi" }, - { "id": "jungle-road", "symbol": "jgrd", "name": "Jungle Road" }, - { "id": "jungle-token", "symbol": "jungle", "name": "Jungle Token" }, - { "id": "junko-inu", "symbol": "junkoinu", "name": "Junko Inu" }, - { "id": "juno-network", "symbol": "juno", "name": "JUNO" }, - { "id": "junsonmingchancoin", "symbol": "jmc", "name": "Junsonmingchancoin" }, - { "id": "jupiter", "symbol": "jup", "name": "Jupiter" }, - { "id": "jur", "symbol": "jur", "name": "Jur" }, - { "id": "jurasaur", "symbol": "jrex", "name": "Jurasaur" }, - { "id": "jurassic_nodes", "symbol": "dino", "name": "Jurassic Nodes" }, - { "id": "jurassic-token", "symbol": "jrsc", "name": "Jurassic Token" }, - { "id": "just", "symbol": "jst", "name": "JUST" }, - { "id": "justfarm", "symbol": "jfm", "name": "JustFarm" }, - { "id": "justmoney", "symbol": "jm", "name": "JustMoney" }, - { "id": "just-stablecoin", "symbol": "usdj", "name": "JUST Stablecoin" }, - { "id": "justyours", "symbol": "just", "name": "JustYours" }, - { "id": "juventus-fan-token", "symbol": "juv", "name": "Juventus Fan Token" }, - { "id": "k21", "symbol": "k21", "name": "K21" }, - { "id": "k-9-inu", "symbol": "k9", "name": "K-9 Inu" }, - { "id": "kaafila", "symbol": "kfl", "name": "Kaafila" }, - { "id": "kabosu", "symbol": "kabosu", "name": "Kabosu" }, - { "id": "kaby-arena", "symbol": "kaby", "name": "Kaby Arena" }, - { "id": "kaby-gaming-token", "symbol": "kgt", "name": "Kaby Gaming Token" }, - { "id": "kaco-finance", "symbol": "kac", "name": "KACO Finance" }, - { "id": "kadena", "symbol": "kda", "name": "Kadena" }, - { "id": "kaiba-defi", "symbol": "kaiba", "name": "Kaiba Defi" }, - { "id": "kaidht", "symbol": "kaidht", "name": "Dog House Token" }, - { "id": "kai-inu", "symbol": "KAIINU", "name": "Kai Inu" }, - { "id": "kaiju-worlds", "symbol": "kaiju", "name": "Kaiju Worlds" }, - { "id": "kaikeninu", "symbol": "kaieco", "name": "KaikenInu" }, - { "id": "kaiken-shiba", "symbol": "kshib", "name": "Kaiken Shiba" }, - { "id": "kainet", "symbol": "kainet", "name": "KAINET" }, - { "id": "kaizen", "symbol": "kzen", "name": "Kaizen" }, - { "id": "kaizilla", "symbol": "kaizilla", "name": "Kaizilla" }, - { "id": "kaka-nft-world", "symbol": "kaka", "name": "KAKA NFT World" }, - { "id": "kakashiinuv2", "symbol": "kki", "name": "KakashiInuV2" }, - { "id": "kala", "symbol": "kala", "name": "Kala" }, - { "id": "kala-finance", "symbol": "kala", "name": "Kala Finance" }, - { "id": "kalamint", "symbol": "kalam", "name": "Kalamint" }, - { "id": "kala-network", "symbol": "klnw", "name": "KALA Network" }, - { "id": "kalao", "symbol": "klo", "name": "Kalao" }, - { "id": "kalata", "symbol": "kala", "name": "Kalata Protocol" }, - { "id": "kalera-nft", "symbol": "kln", "name": "Kalera NFT" }, - { "id": "kalissa", "symbol": "kali", "name": "Kalissa" }, - { "id": "kalkicoin", "symbol": "klc", "name": "Kalkicoin" }, - { "id": "kalmar", "symbol": "kalm", "name": "Kalmar" }, - { "id": "kalori", "symbol": "klr", "name": "Kalori" }, - { "id": "kambria", "symbol": "kat", "name": "Kambria" }, - { "id": "kampay", "symbol": "kampay", "name": "Kampay" }, - { "id": "kan", "symbol": "kan", "name": "BitKan" }, - { "id": "kanaloa-network", "symbol": "kana", "name": "Kanaloa Network" }, - { "id": "kandy", "symbol": "kandy", "name": "KANDY" }, - { "id": "kang3n", "symbol": "kang3n", "name": "kang3n" }, - { "id": "kanga-exchange", "symbol": "kng", "name": "Kanga Exchange" }, - { "id": "kangal", "symbol": "kangal", "name": "Kangal" }, - { "id": "kangaroo", "symbol": "gar", "name": "Kangaroo" }, - { "id": "kangaroocake", "symbol": "kcake", "name": "KangarooCake" }, - { "id": "kanpeki", "symbol": "kae", "name": "Kanpeki" }, - { "id": "kappa", "symbol": "kappa", "name": "Kappa" }, - { "id": "karaganda-token", "symbol": "krg", "name": "Karaganda Token" }, - { "id": "karastar-kara", "symbol": "kara", "name": "KaraStar" }, - { "id": "karastar-umy", "symbol": "umy", "name": "KaraStar UMY" }, - { "id": "karbo", "symbol": "krb", "name": "Karbo" }, - { "id": "kardiachain", "symbol": "kai", "name": "KardiaChain" }, - { "id": "karencoin", "symbol": "karen", "name": "KarenCoin" }, - { "id": "karmadao", "symbol": "kdao", "name": "KarmaDao" }, - { "id": "karma-dao", "symbol": "karma", "name": "Karma DAO" }, - { "id": "karmaverse", "symbol": "knot", "name": "Karmaverse" }, - { - "id": "karsiyaka-taraftar-token", - "symbol": "ksk", - "name": "Karsiyaka Taraftar Token" - }, - { "id": "kart-racing-league", "symbol": "krl", "name": "Kart Racing League" }, - { "id": "karura", "symbol": "kar", "name": "Karura" }, - { "id": "kashhcoin", "symbol": "kashh", "name": "Kashhcoin" }, - { "id": "kassandra", "symbol": "kacy", "name": "Kassandra" }, - { "id": "kasta", "symbol": "kasta", "name": "Kasta" }, - { "id": "katalyo", "symbol": "ktlyo", "name": "Katalyo" }, - { "id": "katana-finance", "symbol": "katana", "name": "Katana Finance" }, - { "id": "katana-inu", "symbol": "KATA", "name": "Katana Inu" }, - { "id": "katsumi", "symbol": "katsumi", "name": "Katsumi" }, - { "id": "kattana", "symbol": "ktn", "name": "Kattana" }, - { "id": "kauri", "symbol": "kau", "name": "Kauri" }, - { "id": "kava", "symbol": "kava", "name": "Kava" }, - { "id": "kava-lend", "symbol": "HARD", "name": "Kava Lend" }, - { "id": "kava-swap", "symbol": "swp", "name": "Kava Swap" }, - { "id": "kawaii-islands", "symbol": "kwt", "name": "Kawaii Islands" }, - { "id": "kawaiinu", "symbol": "kawaii", "name": "kawaiINU" }, - { "id": "kawai-shiba", "symbol": "kshiba", "name": "Kawai Shiba" }, - { "id": "kawakami", "symbol": "kawa", "name": "Kawakami" }, - { "id": "kayserispor", "symbol": "kysr", "name": "Kayserispor" }, - { "id": "kazama-senshi", "symbol": "kazama", "name": "Kazama Senshi" }, - { "id": "kbn", "symbol": "kbn", "name": "Koban" }, - { "id": "kcash", "symbol": "kcash", "name": "Kcash" }, - { "id": "kcc-memepad", "symbol": "kccm", "name": "KCC MemePad" }, - { "id": "kccpad", "symbol": "kccpad", "name": "KCCPad" }, - { "id": "kdag", "symbol": "kdag", "name": "King DAG" }, - { "id": "kebab-token", "symbol": "kebab", "name": "Kebab Token" }, - { "id": "keep3rv1", "symbol": "kp3r", "name": "Keep3rV1" }, - { - "id": "keep3rv1-wormhole", - "symbol": "kp3r", - "name": "Keep3rV1 (Wormhole)" - }, - { "id": "keep4r", "symbol": "kp4r", "name": "Keep4r" }, - { "id": "keep-network", "symbol": "keep", "name": "Keep Network" }, - { - "id": "keep-network-wormhole", - "symbol": "keep", - "name": "Keep Network (Wormhole)" - }, - { "id": "keeps-coin", "symbol": "kpc", "name": "KEEPs Coin" }, - { "id": "keeshond", "symbol": "$ksh", "name": "Keeshond" }, - { "id": "keiko", "symbol": "keiko", "name": "Keiko" }, - { "id": "keisuke-inu", "symbol": "$kei", "name": "Keisuke Inu" }, - { "id": "kekwcoin", "symbol": "kekw", "name": "Kekwcoin" }, - { "id": "kelpie-inu", "symbol": "kelpie", "name": "Kelpie Inu" }, - { "id": "kelvpn", "symbol": "kel", "name": "KelVPN" }, - { "id": "kemacoin", "symbol": "kema", "name": "KemaCoin" }, - { "id": "kenshi", "symbol": "kenshi", "name": "Kenshi" }, - { - "id": "kentucky-farm-capital", - "symbol": "KFC", - "name": "Kentucky Farm Capital" - }, - { "id": "kephi-gallery", "symbol": "kphi", "name": "Kephi Gallery" }, - { "id": "kepler452b", "symbol": "452b", "name": "Kepler452b" }, - { "id": "kepler-network", "symbol": "kmw", "name": "Kepler Network" }, - { "id": "kerman", "symbol": "kerman", "name": "KERMAN" }, - { "id": "kermit", "symbol": "kermit", "name": "Kermit Finance" }, - { "id": "kevacoin", "symbol": "kva", "name": "Kevacoin" }, - { "id": "key", "symbol": "key", "name": "Key" }, - { "id": "keyco", "symbol": "kec", "name": "Keyco" }, - { "id": "keyfi", "symbol": "keyfi", "name": "KeyFi" }, - { "id": "keysians-network", "symbol": "ken", "name": "Keysians Network" }, - { "id": "keys-token", "symbol": "keys", "name": "Keys Token" }, - { "id": "keytango", "symbol": "tango", "name": "keyTango" }, - { "id": "kfan-token", "symbol": "kfan", "name": "KFan Token" }, - { "id": "khalifa-finance", "symbol": "khalifa", "name": "Khalifa Finance" }, - { "id": "khaos-finance", "symbol": "khaos", "name": "Khaos Finance" }, - { "id": "ki", "symbol": "xki", "name": "KI" }, - { "id": "kiba-inu", "symbol": "kiba", "name": "Kiba Inu" }, - { "id": "kibastablecapital", "symbol": "KSC", "name": "KibaStableCapital" }, - { "id": "kichicoin", "symbol": "kich", "name": "KichiCoin" }, - { "id": "kick", "symbol": "kick", "name": "Kick" }, - { "id": "kick-io", "symbol": "kick", "name": "KICK.IO" }, - { "id": "kickpad", "symbol": "kpad", "name": "KickPad" }, - { "id": "kickstarter", "symbol": "KSR", "name": "Kickstarter" }, - { "id": "kiki", "symbol": "kiki", "name": "Kiki" }, - { "id": "kiki-finance", "symbol": "kiki", "name": "Kiki Finance" }, - { "id": "kikswap", "symbol": "kik", "name": "Kikswap" }, - { "id": "kiku-inu", "symbol": "kinu", "name": "Kiku Inu" }, - { "id": "kilimanjaro", "symbol": "kili", "name": "Kilimanjaro" }, - { "id": "killswitch", "symbol": "ksw", "name": "KillSwitch" }, - { "id": "killthezero", "symbol": "KTZ", "name": "KILLTHEZERO" }, - { "id": "killua-inu", "symbol": "killua", "name": "Killua Inu" }, - { "id": "kill-zill", "symbol": "kz", "name": "KiLL ZiLL" }, - { "id": "kiloample", "symbol": "kmpl", "name": "KiloAmple" }, - { "id": "kilo-shiba-inu", "symbol": "kshib", "name": "Kilo Shiba Inu" }, - { "id": "kilt-protocol", "symbol": "kilt", "name": "KILT Protocol" }, - { "id": "kimchi-finance", "symbol": "kimchi", "name": "KIMCHI.finance" }, - { "id": "kimetsu-inu", "symbol": "kimetsu", "name": "Kimetsu Inu" }, - { "id": "kimex", "symbol": "kmx", "name": "KIMEX" }, - { "id": "kimjongmoon", "symbol": "kimj", "name": "KimJongMoon" }, - { "id": "kin", "symbol": "kin", "name": "Kin" }, - { "id": "kind-ads-token", "symbol": "kind", "name": "Kind Ads Token" }, - { "id": "kindcow-finance", "symbol": "kind", "name": "Kindcow Finance" }, - { "id": "kindness-for-soul", "symbol": "kfs g", "name": "Kindness For Soul" }, - { "id": "kineko", "symbol": "kko", "name": "Kineko" }, - { "id": "kine-protocol", "symbol": "kine", "name": "Kine Protocol" }, - { "id": "kinetic-money", "symbol": "kntc", "name": "Kinetic Money" }, - { "id": "king-arthur", "symbol": "bking", "name": "King Arthur" }, - { "id": "king-cardano", "symbol": "kada", "name": "King Cardano" }, - { "id": "kingdoge", "symbol": "kdoge", "name": "KingDoge" }, - { "id": "kingdoge-token", "symbol": "kingdoge", "name": "KingDoge Token" }, - { "id": "king-dog-inu", "symbol": "kingdog", "name": "King Dog Inu" }, - { "id": "kingdom-game-4-0", "symbol": "kdg", "name": "Kingdom Game 4.0" }, - { "id": "kingdom-karnage", "symbol": "kkt", "name": "Kingdom Karnage" }, - { "id": "kingdom-quest", "symbol": "kgc", "name": "Kingdom Quest" }, - { "id": "kingdomswap", "symbol": "KS", "name": "Kingdomswap" }, - { "id": "kingdomx", "symbol": "kt", "name": "KingdomX" }, - { "id": "king-finance", "symbol": "king", "name": "King Finance" }, - { "id": "king-floki", "symbol": "king", "name": "King Floki V2" }, - { "id": "king-forever", "symbol": "KFR", "name": "KING FOREVER" }, - { "id": "kingfund-finance", "symbol": "king", "name": "KingFund Finance" }, - { "id": "king-money", "symbol": "kim", "name": "King Money" }, - { "id": "king-of-shiba", "symbol": "kingshiba", "name": "King of Shiba" }, - { "id": "kingpad", "symbol": "crown", "name": "KingPad" }, - { "id": "king-rooster", "symbol": "krc", "name": "King Rooster" }, - { "id": "king-samo", "symbol": "ksamo", "name": "King Samo" }, - { "id": "king-share", "symbol": "kshare", "name": "KING Share" }, - { "id": "king-shiba", "symbol": "kingshib", "name": "King Shiba" }, - { "id": "kingspeed", "symbol": "ksc", "name": "KingSpeed" }, - { "id": "king-swap", "symbol": "$king", "name": "King Swap" }, - { "id": "kingtama", "symbol": "kingtama", "name": "Kingtama" }, - { "id": "kingxchain", "symbol": "kxc", "name": "KingXChain" }, - { "id": "kino", "symbol": "kino", "name": "Kino" }, - { "id": "kintaman", "symbol": "kinta", "name": "Kintaman" }, - { "id": "kintsugi", "symbol": "kint", "name": "Kintsugi" }, - { "id": "kiradoge-coin", "symbol": "KIRADOGE", "name": "Kiradoge" }, - { "id": "kira-network", "symbol": "kex", "name": "KIRA Network" }, - { "id": "kirobo", "symbol": "kiro", "name": "Kirobo" }, - { "id": "kishimoto-inu", "symbol": "kishimoto", "name": "Kishimoto Inu" }, - { "id": "kishu-baby", "symbol": "kishubaby", "name": "Kishu Baby" }, - { "id": "kishuelon", "symbol": "KELON", "name": "Kishuelon" }, - { "id": "kishu-inu", "symbol": "kishu", "name": "Kishu Inu" }, - { "id": "kishutama", "symbol": "kishutama", "name": "Kishutama" }, - { "id": "kissmymoon", "symbol": "kissmymoon", "name": "KissMyMoon" }, - { "id": "kitcoin", "symbol": "ktc", "name": "Kitcoin" }, - { "id": "kite-sync", "symbol": "kite", "name": "Kite Sync" }, - { "id": "kitsumon", "symbol": "$kmc", "name": "Kitsumon" }, - { "id": "kitten-coin", "symbol": "kittens", "name": "Kitten Coin" }, - { "id": "kittenfinance", "symbol": "kif", "name": "KittenFinance" }, - { - "id": "kittens-puppies", - "symbol": "kap", - "name": "Kittens \u0026 Puppies" - }, - { "id": "kitty", "symbol": "kit", "name": "Kitty" }, - { "id": "kittycake", "symbol": "kcake", "name": "KittyCake" }, - { "id": "kittycoin", "symbol": "kitty", "name": "Kitty Coin" }, - { "id": "kitty-coin-solana", "symbol": "kitty", "name": "Kitty Coin Solana" }, - { "id": "kitty-finance", "symbol": "kitty", "name": "Kitty Finance" }, - { "id": "kitty-inu", "symbol": "kitty", "name": "Kitty Inu" }, - { "id": "kitty-ninja", "symbol": "KINJA", "name": "Kitty Ninja" }, - { "id": "kitty-shiba", "symbol": "kshiba", "name": "Kitty Shiba" }, - { "id": "kitty-solana", "symbol": "kitty", "name": "Kitty Solana" }, - { "id": "kiwigo", "symbol": "kgo", "name": "Kiwigo" }, - { "id": "kiwi-token", "symbol": "kiwi", "name": "KIWI Token" }, - { "id": "klaycity-lay007", "symbol": "lay", "name": "KlayCity Lay007" }, - { "id": "klaycity-orb", "symbol": "orb", "name": "KlayCity ORB" }, - { "id": "klayfi-finance", "symbol": "kfi", "name": "KlayFi Finance" }, - { "id": "klaygames", "symbol": "klayg", "name": "KlayGames" }, - { "id": "klaymeta", "symbol": "meta", "name": "Klaymeta" }, - { - "id": "klaymore-stakehouse", - "symbol": "house", - "name": "Klaymore Stakehouse" - }, - { "id": "klayswap-protocol", "symbol": "ksp", "name": "KlaySwap Protocol" }, - { "id": "klay-token", "symbol": "klay", "name": "Klaytn" }, - { "id": "klear-finance", "symbol": "klear", "name": "Klear Finance" }, - { "id": "kleekai", "symbol": "klee", "name": "KleeKai" }, - { "id": "klend", "symbol": "klt", "name": "KLend" }, - { "id": "kleros", "symbol": "pnk", "name": "Kleros" }, - { "id": "klever", "symbol": "klv", "name": "Klever" }, - { "id": "klever-finance", "symbol": "kfi", "name": "Klever Finance" }, - { "id": "klima-dao", "symbol": "klima", "name": "Klima DAO" }, - { "id": "klimatas", "symbol": "kts", "name": "Klimatas" }, - { "id": "kling", "symbol": "kling", "name": "Kling" }, - { "id": "klist-protocol", "symbol": "list", "name": "KList Protocol" }, - { "id": "kmushicoin", "symbol": "ktv", "name": "Kmushicoin" }, - { "id": "knekted", "symbol": "knt", "name": "Knekted" }, - { - "id": "knights-of-fantom", - "symbol": "knights", - "name": "Knights of Fantom" - }, - { - "id": "knights-peasants", - "symbol": "knight", - "name": "Knights \u0026 Peasants" - }, - { "id": "knightswap", "symbol": "knight", "name": "KnightSwap" }, - { "id": "knight-war-spirits", "symbol": "kws", "name": "Knight War Spirits" }, - { "id": "knit-finance", "symbol": "kft", "name": "Knit Finance" }, - { - "id": "know-your-developer", - "symbol": "kydc", - "name": "Know Your Developer" - }, - { "id": "knoxedge", "symbol": "knx", "name": "KnoxEdge" }, - { "id": "knoxfs", "symbol": "kfx", "name": "KnoxFS" }, - { "id": "koakombat", "symbol": "koacombat", "name": "KoaKombat" }, - { "id": "koala-token", "symbol": "mkoala", "name": "Koala Token" }, - { "id": "kobocoin", "symbol": "kobo", "name": "Kobocoin" }, - { "id": "koda-finance", "symbol": "koda", "name": "Koda Cryptocurrency" }, - { "id": "kodi", "symbol": "kodi", "name": "KODI" }, - { "id": "koel-coin", "symbol": "koel", "name": "Koel Coin" }, - { "id": "kogecoin", "symbol": "kogecoin", "name": "KogeCoin" }, - { "id": "koinon", "symbol": "koin", "name": "Koinon" }, - { "id": "koinos", "symbol": "koin", "name": "Koinos" }, - { "id": "koji", "symbol": "koji", "name": "Koji" }, - { "id": "koka-inu", "symbol": "INU", "name": "KOKA INU" }, - { "id": "kok-coin", "symbol": "kok", "name": "KOK Coin" }, - { "id": "kokoswap", "symbol": "koko", "name": "KokoSwap" }, - { "id": "kolibri-dao", "symbol": "kdao", "name": "Kolibri DAO" }, - { "id": "kolibri-usd", "symbol": "kusd", "name": "Kolibri USD" }, - { "id": "kollect", "symbol": "kol", "name": "Kollect" }, - { "id": "kollector", "symbol": "kltr", "name": "Kollector" }, - { - "id": "kols-offering-token", - "symbol": "kot", - "name": "Kols Offering Token" - }, - { "id": "kombai-inu", "symbol": "kombai", "name": "Kombai Inu" }, - { "id": "komet", "symbol": "komet", "name": "Komet" }, - { "id": "kommunitas", "symbol": "kom", "name": "Kommunitas" }, - { "id": "komodo", "symbol": "kmd", "name": "Komodo" }, - { "id": "kompass", "symbol": "komp", "name": "Kompass" }, - { "id": "kompete", "symbol": "kompete", "name": "Kompete" }, - { "id": "kondux", "symbol": "kndx", "name": "Kondux" }, - { - "id": "kong-land-alpha-citizenship", - "symbol": "citizen", - "name": "KONG Land Alpha Citizenship" - }, - { "id": "kongtama", "symbol": "kongtama", "name": "Kongtama" }, - { "id": "konjungate", "symbol": "konj", "name": "KONJUNGATE" }, - { "id": "konomi-network", "symbol": "kono", "name": "Konomi Network" }, - { "id": "konstrukt", "symbol": "kst", "name": "Konstrukt" }, - { "id": "koreadoge", "symbol": "kdoge", "name": "Koreadoge" }, - { "id": "kori-inu", "symbol": "kori", "name": "Kori Inu" }, - { "id": "koromaru", "symbol": "koromaru", "name": "KOROMARU" }, - { - "id": "korss-chain-launchpad", - "symbol": "kclp", - "name": "Kross Chain Launchpad" - }, - { "id": "koto", "symbol": "koto", "name": "Koto" }, - { "id": "kounotori", "symbol": "kto", "name": "Kounotori" }, - { "id": "kpop-coin", "symbol": "kpop", "name": "KPOP Coin" }, - { "id": "kranz-token", "symbol": "krz", "name": "Kranz Token" }, - { "id": "krause", "symbol": "$KRAUSE", "name": "KRAUSE" }, - { "id": "kred", "symbol": "kred", "name": "KRED" }, - { "id": "krida-fans", "symbol": "krida", "name": "Krida Fans" }, - { "id": "krigger", "symbol": "krigger", "name": "Krigger" }, - { "id": "krill", "symbol": "krill", "name": "Krill" }, - { "id": "krios", "symbol": "GIG", "name": "Krios" }, - { - "id": "kripto-galaxy-battle", - "symbol": "kaba", - "name": "Kripto Galaxy Battle" - }, - { "id": "kromatika", "symbol": "krom", "name": "Kromatika" }, - { "id": "kronn", "symbol": "krex", "name": "Kronn" }, - { "id": "kronobit", "symbol": "knb", "name": "Kronobit" }, - { "id": "kronos-dao", "symbol": "krno", "name": "Kronos Dao" }, - { "id": "krook-coin", "symbol": "KROOK", "name": "Krook Coin" }, - { "id": "k-root-wallet", "symbol": "kroot", "name": "K-Root Wallet" }, - { "id": "krown", "symbol": "krw", "name": "KROWN" }, - { "id": "kryll", "symbol": "krl", "name": "KRYLL" }, - { "id": "kryptobellion", "symbol": "krypto", "name": "Kryptobellion" }, - { "id": "krypto-kitty", "symbol": "kty", "name": "Krypto Kitty" }, - { "id": "kryptomon", "symbol": "kmon", "name": "Kryptomon" }, - { "id": "krypton-token", "symbol": "kgc", "name": "Krypton Galaxy Coin" }, - { "id": "kryptopets", "symbol": "kpets", "name": "KryptoPets" }, - { "id": "kryxivia-game", "symbol": "kxa", "name": "Kryxivia Game" }, - { "id": "kryza-exchange", "symbol": "krx", "name": "KRYZA Exchange" }, - { "id": "kryza-network", "symbol": "krn", "name": "KRYZA Network" }, - { "id": "ksm-starter", "symbol": "kst", "name": "Karus Starter" }, - { "id": "kstarcoin", "symbol": "ksc", "name": "KStarCoin" }, - { "id": "k-tune", "symbol": "ktt", "name": "K-Tune" }, - { "id": "kuaitoken", "symbol": "kt", "name": "Kuai Token" }, - { "id": "kuber-finance", "symbol": "kft", "name": "Kuber Finance" }, - { "id": "kubic", "symbol": "kubic", "name": "Kubic" }, - { "id": "kucoin-shares", "symbol": "kcs", "name": "KuCoin Token" }, - { "id": "kudoge", "symbol": "kudo", "name": "KuDoge" }, - { "id": "kuende", "symbol": "kue", "name": "Kuende" }, - { "id": "kujira", "symbol": "kuji", "name": "Kujira" }, - { "id": "kukachu", "symbol": "kuka", "name": "Kukachu" }, - { "id": "kuky-star", "symbol": "kuky", "name": "Kuky Star" }, - { "id": "kult-of-kek", "symbol": "kok", "name": "Kult of Kek" }, - { "id": "kulupu", "symbol": "klp", "name": "Kulupu" }, - { "id": "kuma-inu", "symbol": "kuma", "name": "Kuma Inu" }, - { "id": "kunaiinu", "symbol": "kunai", "name": "KunaiInu" }, - { "id": "kunci-coin", "symbol": "kunci", "name": "Kunci Coin" }, - { "id": "kunoichix", "symbol": "kuno", "name": "KunoichiX" }, - { "id": "kurai-inu", "symbol": "kurai", "name": "Kurai Inu" }, - { "id": "kurai-metaverse", "symbol": "kurai", "name": "Kurai MetaVerse" }, - { "id": "kuramainu", "symbol": "kunu", "name": "KuramaInu" }, - { "id": "kurobi", "symbol": "kuro", "name": "Kurobi" }, - { "id": "kuro-shiba", "symbol": "kuro", "name": "Kuro Shiba" }, - { "id": "kurrent", "symbol": "kurt", "name": "Kurrent" }, - { "id": "kusama", "symbol": "ksm", "name": "Kusama" }, - { "id": "kusd-t", "symbol": "kusd-t", "name": "KUSD-T" }, - { "id": "kush-finance", "symbol": "kseed", "name": "Kush Finance" }, - { - "id": "kusunoki-samurai", - "symbol": "kusunoki", - "name": "Kusunoki Samurai" - }, - { "id": "kuswap", "symbol": "kus", "name": "KuSwap" }, - { "id": "kuverit", "symbol": "kuv", "name": "Kuverit" }, - { "id": "kwikswap-protocol", "symbol": "kwik", "name": "KwikSwap Protocol" }, - { "id": "kwiktrust", "symbol": "ktx", "name": "KwikTrust" }, - { "id": "kyanite", "symbol": "kyan", "name": "Kyanite" }, - { "id": "kyberdyne", "symbol": "kbd", "name": "Kyberdyne" }, - { - "id": "kyber-network", - "symbol": "kncl", - "name": "Kyber Network Crystal Legacy" - }, - { - "id": "kyber-network-crystal", - "symbol": "knc", - "name": "Kyber Network Crystal" - }, - { "id": "kyccoin", "symbol": "kycc", "name": "KYCCOIN" }, - { "id": "kylin-network", "symbol": "kyl", "name": "Kylin Network" }, - { "id": "kyoko", "symbol": "kyoko", "name": "Kyoko" }, - { "id": "kyrrex", "symbol": "krrx", "name": "Kyrrex" }, - { "id": "kzcash", "symbol": "kzc", "name": "Kzcash" }, - { "id": "l2pad", "symbol": "l2pad", "name": "L2PAD" }, - { "id": "label-foundation", "symbol": "lbl", "name": "LABEL Foundation" }, - { "id": "labracoin", "symbol": "labra", "name": "LabraCoin" }, - { "id": "labs-group", "symbol": "labs", "name": "LABS Group" }, - { "id": "la-casa-de-papel", "symbol": "lcdp", "name": "La Casa De Papel" }, - { - "id": "laconic-network-token", - "symbol": "lnt", - "name": "Laconic Network Token" - }, - { "id": "lacucina", "symbol": "lac", "name": "LaCucina" }, - { "id": "laddercaster", "symbol": "lada", "name": "LadderCaster" }, - { "id": "la-doge-de-papel", "symbol": "lddp", "name": "La Doge de Papel" }, - { "id": "ladyminers", "symbol": "yttrium", "name": "Ladyminers" }, - { "id": "lady-uni", "symbol": "luni", "name": "Lady Uni" }, - { "id": "ladz", "symbol": "ladz", "name": "LADZ" }, - { "id": "laikacoin", "symbol": "laika", "name": "LaikaCoin" }, - { "id": "laika-protocol", "symbol": "laika", "name": "Laika Protocol" }, - { "id": "lambda", "symbol": "lamb", "name": "Lambda" }, - { "id": "lamden", "symbol": "tau", "name": "Lamden" }, - { "id": "lanacoin", "symbol": "lana", "name": "LanaCoin" }, - { "id": "lanceria", "symbol": "lanc", "name": "Lanceria" }, - { "id": "landi-token", "symbol": "landi", "name": "LANDI Token" }, - { "id": "land-of-realm", "symbol": "lor", "name": "Land of Realm" }, - { "id": "landorc", "symbol": "lorc", "name": "LandOrc" }, - { "id": "landshare", "symbol": "land", "name": "Landshare" }, - { "id": "la-peseta", "symbol": "pta", "name": "La Peseta" }, - { "id": "laqira-protocol", "symbol": "lqr", "name": "Laqira Protocol" }, - { "id": "largo-coin", "symbol": "lrg", "name": "Largo Coin" }, - { "id": "larix", "symbol": "larix", "name": "Larix" }, - { "id": "lasereyes", "symbol": "lsr", "name": "LaserEyes" }, - { "id": "laser-eyes", "symbol": "lasereyes", "name": "Laser Eyes" }, - { "id": "lassocoin", "symbol": "lasso", "name": "LassoCoin" }, - { "id": "last-survivor", "symbol": "lsc", "name": "Last Survivor" }, - { "id": "latamcash", "symbol": "lmch", "name": "Latamcash" }, - { "id": "latiumx", "symbol": "latx", "name": "LatiumX" }, - { "id": "latoken", "symbol": "la", "name": "LATOKEN" }, - { "id": "latteswap", "symbol": "latte", "name": "LatteSwap" }, - { "id": "lattice-token", "symbol": "ltx", "name": "Lattice Token" }, - { "id": "launchblock", "symbol": "lbp", "name": "LaunchBlock" }, - { "id": "launchmynft", "symbol": "mynft", "name": "LaunchMyNFT" }, - { "id": "launchpool", "symbol": "lpool", "name": "Launchpool" }, - { "id": "launchwall", "symbol": "wall", "name": "LaunchWall" }, - { "id": "launchzone", "symbol": "lz", "name": "LaunchZone" }, - { "id": "laurus-coin", "symbol": "lscn", "name": "Laurus Coin" }, - { "id": "lavacake-finance", "symbol": "lava", "name": "LavaCake Finance" }, - { "id": "lavaswap", "symbol": "lava", "name": "Lavaswap" }, - { "id": "lavax-labs", "symbol": "lavax", "name": "LavaX Labs" }, - { "id": "law", "symbol": "law", "name": "LAW" }, - { "id": "law-token", "symbol": "law", "name": "Law Token" }, - { "id": "layer2dao", "symbol": "l2dao", "name": "Layer2DAO" }, - { "id": "lazio-fan-token", "symbol": "lazio", "name": "Lazio Fan Token" }, - { "id": "lazydoge", "symbol": "lazydoge", "name": "LazyDoge" }, - { - "id": "lazy-horse-race-club", - "symbol": "LHRC", - "name": "Lazy Horse Race Club" - }, - { "id": "lazymint", "symbol": "lazy", "name": "LazyMint" }, - { "id": "lazy-shiba", "symbol": "lazyshiba", "name": "Lazy Shiba" }, - { "id": "lbk", "symbol": "lbk", "name": "LBK" }, - { "id": "lbry-credits", "symbol": "lbc", "name": "LBRY Credits" }, - { "id": "lcg", "symbol": "lcg", "name": "LCG" }, - { "id": "lcms", "symbol": "lcms", "name": "LCMS" }, - { "id": "lcx", "symbol": "lcx", "name": "LCX" }, - { "id": "lead-token", "symbol": "lead", "name": "Lead Token" }, - { "id": "leafcoin", "symbol": "leaf", "name": "Leafcoin" }, - { "id": "leafty", "symbol": "leafty", "name": "Leafty" }, - { - "id": "leaguedao-governance-token", - "symbol": "leag", - "name": "LeagueDAO Governance Token" - }, - { "id": "league-of-ancients", "symbol": "loa", "name": "League of Ancients" }, - { "id": "league-of-empires", "symbol": "loe", "name": "League of Empires" }, - { - "id": "league-of-kingdoms", - "symbol": "loka", - "name": "League of Kingdoms" - }, - { "id": "league-of-zodiacs", "symbol": "loz", "name": "League of Zodiacs" }, - { "id": "lean", "symbol": "lean", "name": "Lean" }, - { - "id": "lean-management-token", - "symbol": "lean", - "name": "Lean Management Token" - }, - { "id": "leapableio", "symbol": "lea", "name": "LeapableIO" }, - { "id": "leasehold", "symbol": "lsh", "name": "Leasehold" }, - { "id": "leash", "symbol": "leash", "name": "Doge Killer" }, - { "id": "le-caliente", "symbol": "lecliente", "name": "LE CALIENTE" }, - { "id": "ledgerscore", "symbol": "led", "name": "LedgerScore" }, - { "id": "ledgis", "symbol": "led", "name": "Ledgis" }, - { "id": "ledgity", "symbol": "lty", "name": "Ledgity" }, - { - "id": "leeds-united-fan-token", - "symbol": "lufc", - "name": "Leeds United Fan Token" - }, - { "id": "leek-token", "symbol": "leek", "name": "Leek Token" }, - { "id": "legal-block", "symbol": "lbk", "name": "Legal Block" }, - { "id": "legendaryum", "symbol": "lgnd", "name": "Legendaryum" }, - { - "id": "legend-of-fantasy-war", - "symbol": "lfw", - "name": "Legend of Fantasy War" - }, - { "id": "legends-of-mitra", "symbol": "mita", "name": "Legends of Mitra" }, - { "id": "legends-room", "symbol": "more", "name": "More Coin" }, - { - "id": "legia-warsaw-fan-token", - "symbol": "LEG", - "name": "Legia Warsaw Fan Token" - }, - { "id": "legion-network", "symbol": "lgx", "name": "Legion Network" }, - { "id": "legolas-exchange", "symbol": "lgo", "name": "LGO Token" }, - { "id": "leisurepay", "symbol": "lpy", "name": "LeisurePay" }, - { "id": "lemochain", "symbol": "lemo", "name": "LemoChain" }, - { "id": "lemon", "symbol": "lmn", "name": "Lemon" }, - { "id": "lemon-bet", "symbol": "lbet", "name": "Lemon Bet" }, - { "id": "lemonchain", "symbol": "lemc", "name": "LemonChain" }, - { "id": "lemond", "symbol": "lemd", "name": "Lemond" }, - { "id": "lemonn-token", "symbol": "lmn", "name": "Lemonn" }, - { "id": "lemon-token", "symbol": "lemn", "name": "Lemon Token" }, - { "id": "lenda", "symbol": "lenda", "name": "Lenda" }, - { "id": "lendefi", "symbol": "ldfi", "name": "Lendefi" }, - { - "id": "lend-flare-dao-token", - "symbol": "lft", - "name": "Lend Flare Dao Token" - }, - { "id": "lendhub", "symbol": "lhb", "name": "Lendhub" }, - { "id": "lendingblock", "symbol": "lnd", "name": "Lendingblock" }, - { - "id": "lendroid-support-token", - "symbol": "lst", - "name": "Lendroid Support Token" - }, - { "id": "leo", "symbol": "leo", "name": "Leo" }, - { "id": "leonicorn-swap", "symbol": "leos", "name": "Leonicorn Swap" }, - { "id": "leonidas", "symbol": "leo", "name": "Leonidas" }, - { "id": "leonidas-token", "symbol": "leonidas", "name": "Leonidas Token" }, - { "id": "leon-token", "symbol": "leon", "name": "Leon Token" }, - { "id": "leopard", "symbol": "leopard", "name": "Leopard" }, - { "id": "leo-token", "symbol": "leo", "name": "LEO Token" }, - { "id": "lepasa", "symbol": "lepa", "name": "Lepasa" }, - { "id": "leprechaun-finance", "symbol": "lep", "name": "Leprechaun Finance" }, - { "id": "lepricon", "symbol": "l3p", "name": "Lepricon" }, - { "id": "leslar-metaverse", "symbol": "leslar", "name": "LESLAR Metaverse" }, - { "id": "less-network", "symbol": "less", "name": "Less Network" }, - { "id": "lethean", "symbol": "lthn", "name": "Lethean" }, - { "id": "letitride", "symbol": "lir", "name": "LetItRide" }, - { "id": "lets-go-brandon", "symbol": "letsgo", "name": "Lets Go Brandon" }, - { "id": "lets-go-farming", "symbol": "LGF", "name": "Let's Go Farming" }, - { - "id": "levante-ud-fan-token", - "symbol": "lev", - "name": "Levante U.D. Fan Token" - }, - { "id": "levelapp", "symbol": "lvl", "name": "LevelApp" }, - { "id": "levelg", "symbol": "levelg", "name": "LEVELG" }, - { "id": "levelup-gaming", "symbol": "lvlup", "name": "LevelUp Gaming" }, - { "id": "leven", "symbol": "leven", "name": "Leven Token" }, - { "id": "leverj-gluon", "symbol": "l2", "name": "Leverj Gluon" }, - { "id": "lever-network", "symbol": "lev", "name": "Lever Network" }, - { "id": "levin", "symbol": "levin", "name": "Levin" }, - { "id": "levolution", "symbol": "levl", "name": "Levolution" }, - { "id": "lexit-2", "symbol": "lexi", "name": "LEXIT" }, - { "id": "lgcy-network", "symbol": "lgcy", "name": "LGCY Network" }, - { "id": "lhcoin", "symbol": "lhcoin", "name": "LHCoin" }, - { "id": "libartysharetoken", "symbol": "lst", "name": "Libartysharetoken" }, - { "id": "libcoin", "symbol": "lib", "name": "Libcoin" }, - { "id": "libera", "symbol": "lib", "name": "Libera" }, - { "id": "liber-coin", "symbol": "lbr", "name": "LIBER COIN" }, - { "id": "libero-financial", "symbol": "libero", "name": "Libero Financial" }, - { "id": "libertas-token", "symbol": "libertas", "name": "LIBERTAS" }, - { "id": "libfx", "symbol": "libfx", "name": "Libfx" }, - { "id": "libonomy", "symbol": "lby", "name": "Libonomy" }, - { "id": "libra-2", "symbol": "lc", "name": "Libra" }, - { "id": "libra-credit", "symbol": "lba", "name": "LibraToken" }, - { "id": "libre-defi", "symbol": "libre", "name": "Libre DeFi" }, - { "id": "librefreelencer", "symbol": "libref", "name": "LibreFreelencer" }, - { "id": "librium-tech", "symbol": "lib", "name": "Librium Tech" }, - { "id": "lichang", "symbol": "lc", "name": "Lichang" }, - { "id": "lido-dao", "symbol": "ldo", "name": "Lido DAO" }, - { "id": "lido-dao-wormhole", "symbol": "ldo", "name": "Lido DAO (Wormhole)" }, - { - "id": "lido-staked-ether-wormhole", - "symbol": "steth", - "name": "Lido Staked Ether (Wormhole)" - }, - { - "id": "lido-staked-matic", - "symbol": "stmatic", - "name": "Lido Staked Matic" - }, - { "id": "lido-staked-sol", "symbol": "stsol", "name": "Lido Staked SOL" }, - { "id": "lien", "symbol": "lien", "name": "Lien" }, - { "id": "life-coin", "symbol": "lfc", "name": "Life Coin" }, - { "id": "life-crypto", "symbol": "life", "name": "Life Crypto" }, - { "id": "life-dao", "symbol": "lf", "name": "Life DAO" }, - { "id": "lifetime", "symbol": "lft", "name": "Lifetime" }, - { "id": "life-token-v2", "symbol": "ltnv2", "name": "Life Token v2" }, - { - "id": "lightbeam-courier-coin", - "symbol": "lbcc", - "name": "Lightbeam Courier Coin" - }, - { "id": "lightcoin", "symbol": "lhc", "name": "Lightcoin" }, - { "id": "light-defi", "symbol": "light", "name": "Light Defi" }, - { "id": "lightening-cash", "symbol": "lic", "name": "Lightening Cash" }, - { "id": "lightforge", "symbol": "ltfg", "name": "Lightforge" }, - { "id": "lightning-bitcoin", "symbol": "lbtc", "name": "Lightning Bitcoin" }, - { "id": "lightningcoin", "symbol": "lc", "name": "LightningCoin" }, - { - "id": "lightning-protocol", - "symbol": "light", - "name": "Lightning Protocol" - }, - { "id": "lightstreams", "symbol": "pht", "name": "Lightstreams Photon" }, - { "id": "light-year", "symbol": "lc", "name": "Light Year" }, - { "id": "lightyears", "symbol": "year", "name": "Lightyears" }, - { "id": "likecoin", "symbol": "like", "name": "LikeCoin" }, - { "id": "lildoge", "symbol": "lildoge", "name": "LilDoge" }, - { - "id": "lil-doge-floki-token", - "symbol": "ldf", - "name": "Lil Doge Floki Token" - }, - { "id": "lil-floki", "symbol": "lilfloki", "name": "Lil Floki" }, - { "id": "lilflokiceo", "symbol": "lilflokiceo", "name": "LilFlokiCeo" }, - { "id": "lilith-swap", "symbol": "LLTH", "name": "Lilith Swap" }, - { "id": "lillion", "symbol": "lil", "name": "Lillion" }, - { "id": "lilly-finance", "symbol": "ly", "name": "Lilly Finance" }, - { "id": "limbo-token", "symbol": "lnfs", "name": "Limbo Token" }, - { "id": "limcore", "symbol": "limc", "name": "LimCore" }, - { "id": "limestone-network", "symbol": "limex", "name": "Limestone Network" }, - { "id": "limited-euro", "symbol": "leur", "name": "Limited Euro" }, - { "id": "limited-usd", "symbol": "lusd", "name": "Limited USD" }, - { "id": "limitswap", "symbol": "limit", "name": "LimitSwap" }, - { "id": "limocoin-swap", "symbol": "lmcswap", "name": "Limocoin Swap" }, - { "id": "limon-group", "symbol": "limon", "name": "LimonV2" }, - { "id": "lina", "symbol": "lina", "name": "LINA" }, - { "id": "linda", "symbol": "mrx", "name": "Metrix Coin" }, - { "id": "linear", "symbol": "lina", "name": "Linear" }, - { "id": "linear-bsc", "symbol": "lina", "name": "Linear (BSC)" }, - { "id": "linfinity", "symbol": "lfc", "name": "Linfinity" }, - { "id": "linix", "symbol": "lnx", "name": "LNX Protocol" }, - { "id": "link", "symbol": "ln", "name": "LINK" }, - { "id": "linka", "symbol": "linka", "name": "LINKA" }, - { "id": "linkart", "symbol": "lar", "name": "LinkArt" }, - { "id": "linkcoin-token", "symbol": "lkn", "name": "LinkCoin Token" }, - { "id": "linker-coin", "symbol": "lnc", "name": "Linker Coin" }, - { - "id": "linketh-2x-token", - "symbol": "linkethmoon", - "name": "LINKETH 2x Token" - }, - { "id": "linkeye", "symbol": "let", "name": "Linkeye" }, - { "id": "linkka", "symbol": "ika", "name": "Linkka" }, - { - "id": "link-machine-learning", - "symbol": "lml", - "name": "Link Machine Learning" - }, - { "id": "linkpool", "symbol": "lpl", "name": "LinkPool" }, - { "id": "links", "symbol": "links", "name": "Links" }, - { "id": "linksync", "symbol": "sync", "name": "LinkSync" }, - { "id": "linktoken", "symbol": "ltk", "name": "LinkToken" }, - { "id": "linspirit", "symbol": "linspirit", "name": "linSpirit" }, - { "id": "l-inu", "symbol": "l", "name": "L inu" }, - { "id": "lionisland-inu", "symbol": "lionisland", "name": "LionIsland Inu" }, - { - "id": "lion-scrub-finance", - "symbol": "lion", - "name": "Lion Scrub Finance" - }, - { "id": "lion-token", "symbol": "lion", "name": "Lion Token" }, - { "id": "liq-protocol", "symbol": "liq", "name": "LIQ Protocol" }, - { "id": "liquidator-dao", "symbol": "liquidator", "name": "Liquidator DAO" }, - { "id": "liquidchain", "symbol": "xlc", "name": "Liquidchain" }, - { - "id": "liquid-collectibles", - "symbol": "lico", - "name": "Liquid Collectibles" - }, - { "id": "liquid-defi", "symbol": "liq", "name": "Liquid DeFi" }, - { "id": "liquiddriver", "symbol": "lqdr", "name": "LiquidDriver" }, - { "id": "liquid-icp", "symbol": "licp", "name": "Liquid ICP" }, - { "id": "liquidifty", "symbol": "lqt", "name": "Liquidifty" }, - { "id": "liquidify", "symbol": "lat", "name": "Liquidify" }, - { - "id": "liquidity-dividends-protocol", - "symbol": "LID", - "name": "Liquidity Dividends Protocol" - }, - { "id": "liquidity-network", "symbol": "lqd", "name": "Liquidity Network" }, - { "id": "liquidswap", "symbol": "lqd", "name": "LiquidSwap" }, - { "id": "liquidus", "symbol": "liq", "name": "Liquidus" }, - { "id": "liquity", "symbol": "lqty", "name": "Liquity" }, - { "id": "liquity-usd", "symbol": "lusd", "name": "Liquity USD" }, - { "id": "liqwid-finance", "symbol": "lq", "name": "Liqwid Finance" }, - { "id": "lisk", "symbol": "lsk", "name": "Lisk" }, - { "id": "lit", "symbol": "LIT", "name": "LIT" }, - { "id": "litebar", "symbol": "ltb", "name": "LiteBar" }, - { "id": "litebitcoin", "symbol": "lbtc", "name": "LiteBitcoin" }, - { "id": "litecash", "symbol": "cash", "name": "Litecash" }, - { "id": "litecoin", "symbol": "ltc", "name": "Litecoin" }, - { "id": "litecoin-bep2", "symbol": "ltcb", "name": "Litecoin BEP2" }, - { "id": "litecoin-cash", "symbol": "lcc", "name": "Litecoin Cash" }, - { "id": "litecoin-finance", "symbol": "ltfn", "name": "Litecoin Finance" }, - { "id": "litecoin-plus", "symbol": "lcp", "name": "Litecoin Plus" }, - { "id": "litecoin-sv", "symbol": "lsv", "name": "Litecoin SV" }, - { "id": "litecoin-token", "symbol": "ltk", "name": "Litecoin Token" }, - { "id": "litecoin-ultra", "symbol": "ltcu", "name": "LiteCoin Ultra" }, - { "id": "litecoinz", "symbol": "ltz", "name": "LitecoinZ" }, - { "id": "litedex", "symbol": "ldx", "name": "Litedex" }, - { "id": "litedoge", "symbol": "ldoge", "name": "LiteDoge" }, - { "id": "litegold", "symbol": "ltg", "name": "LiteGold" }, - { "id": "litentry", "symbol": "lit", "name": "Litentry" }, - { "id": "litex", "symbol": "lxt", "name": "LITEX" }, - { "id": "litherium", "symbol": "lith", "name": "Litherium" }, - { "id": "lithium-2", "symbol": "lithium", "name": "Lithium" }, - { "id": "lithium-finance", "symbol": "lith", "name": "Lithium Finance" }, - { "id": "lith-token", "symbol": "lith", "name": "Lith Token" }, - { "id": "lition", "symbol": "lit", "name": "Lition" }, - { - "id": "little-angry-bunny-v2", - "symbol": "lab-v2", - "name": "Little Angry Bunny v2" - }, - { "id": "littlebabydoge", "symbol": "lbd", "name": "LittleBabyDoge" }, - { - "id": "little-bunny-rocket", - "symbol": "lbr", - "name": "Little Bunny Rocket" - }, - { - "id": "littleghosts-ectoplasm", - "symbol": "ecto", - "name": "LittleGhosts Ectoplasm" - }, - { "id": "littleinu", "symbol": "linu", "name": "LittleInu" }, - { "id": "little-rabbit", "symbol": "ltrbt", "name": "Little Rabbit" }, - { "id": "littlesesame", "symbol": "lsc", "name": "Littlesesame" }, - { "id": "little-tsuki-inu", "symbol": "liltk", "name": "Little Tsuki Inu" }, - { "id": "little-ugly-duck", "symbol": "lud", "name": "Little Ugly Duck" }, - { "id": "lituni", "symbol": "lito", "name": "Lituni" }, - { "id": "live-nft", "symbol": "livenft", "name": "Live NFT" }, - { "id": "livenodes", "symbol": "lno", "name": "Livenodes" }, - { "id": "livenpay", "symbol": "lvn", "name": "LivenPay" }, - { "id": "livepeer", "symbol": "lpt", "name": "Livepeer" }, - { "id": "live-swap-coin", "symbol": "lsc", "name": "Live Swap Coin" }, - { "id": "livetrade-token", "symbol": "ltd", "name": "LiveTrade Token" }, - { "id": "lixir-protocol", "symbol": "lix", "name": "Lixir Finance" }, - { "id": "lizard-token", "symbol": "lizard", "name": "Lizard Token" }, - { - "id": "lizardtoken-finance", - "symbol": "liz", - "name": "LizardToken.Finance" - }, - { "id": "llamaswap", "symbol": "lama", "name": "LlamaSwap" }, - { "id": "llamaverse", "symbol": "spit", "name": "Llamaverse" }, - { "id": "lnko-token", "symbol": "lnko", "name": "LNKO Token" }, - { "id": "loa-protocol", "symbol": "loa", "name": "LOA Protocol" }, - { "id": "lobby", "symbol": "lby", "name": "Lobby" }, - { "id": "lobis", "symbol": "lobi", "name": "Lobis" }, - { "id": "lobstex-coin", "symbol": "lobs", "name": "Lobstex" }, - { "id": "localcoinswap", "symbol": "lcs", "name": "LocalCoinSwap" }, - { "id": "local-terra", "symbol": "local", "name": "Local Terra" }, - { "id": "localtrade", "symbol": "ltt", "name": "LocalTrade" }, - { "id": "locgame", "symbol": "locg", "name": "LOCGame" }, - { "id": "lockchain", "symbol": "loc", "name": "LockTrip" }, - { "id": "locklet", "symbol": "lkt", "name": "Locklet" }, - { "id": "lockness", "symbol": "lkn", "name": "Lockness" }, - { "id": "locus-chain", "symbol": "locus", "name": "Locus Chain" }, - { "id": "lofi-defi", "symbol": "lofi", "name": "Lofi Defi" }, - { "id": "logitron", "symbol": "ltr", "name": "LogiTron" }, - { "id": "logix-coin", "symbol": "lgx", "name": "Logix Coin" }, - { "id": "logosunaux", "symbol": "logos", "name": "Logosunaux" }, - { "id": "logs", "symbol": "logs", "name": "LOGS" }, - { "id": "loki-network", "symbol": "oxen", "name": "Oxen" }, - { "id": "lokr", "symbol": "lkr", "name": "Lokr" }, - { "id": "lol", "symbol": "lol", "name": "LOL" }, - { "id": "loltoken", "symbol": "lol", "name": "LOLTOKEN" }, - { "id": "londefy", "symbol": "ldf", "name": "Londefy" }, - { "id": "londex-2", "symbol": "ldx", "name": "Londex" }, - { "id": "londoncoingold", "symbol": "ldxg", "name": "LondonCoinGold" }, - { - "id": "london-vault-nftx", - "symbol": "london", - "name": "LONDON Vault (NFTX)" - }, - { "id": "lonelyfans", "symbol": "lof", "name": "LonelyFans" }, - { "id": "longdrink-finance", "symbol": "long", "name": "Longdrink Finance" }, - { "id": "lookscoin", "symbol": "look", "name": "LooksCoin" }, - { "id": "looksrare", "symbol": "looks", "name": "LooksRare" }, - { "id": "looks-rare", "symbol": "lr", "name": "Looks Rare" }, - { "id": "loomi", "symbol": "loomi", "name": "Loomi" }, - { "id": "loom-network", "symbol": "loomold", "name": "Loom Network (OLD)" }, - { "id": "loom-network-new", "symbol": "loom", "name": "Loom Network (NEW)" }, - { "id": "loon-network", "symbol": "loon", "name": "Loon Network" }, - { "id": "loopnetwork", "symbol": "loop", "name": "LoopNetwork" }, - { "id": "loopring", "symbol": "lrc", "name": "Loopring" }, - { - "id": "loopringcoin-v2-wormhole", - "symbol": "lrc", - "name": "LoopringCoin V2 (Wormhole)" - }, - { "id": "loopstarter", "symbol": "loops", "name": "LOOPStarter" }, - { "id": "loopswap", "symbol": "lswap", "name": "LoopSwap" }, - { "id": "loop-token", "symbol": "loop", "name": "LOOP Finance" }, - { "id": "loot", "symbol": "loot", "name": "Lootex" }, - { "id": "loot-network", "symbol": "LOOT", "name": "Loot Network" }, - { "id": "loot-token", "symbol": "loot", "name": "Loot Token" }, - { "id": "lord-arena", "symbol": "lorda", "name": "Lord Arena" }, - { "id": "lorde-edge", "symbol": "edgelon", "name": "Lorde Edge" }, - { "id": "lords", "symbol": "lords", "name": "LORDS" }, - { "id": "lordtoken", "symbol": "ltt", "name": "LordToken" }, - { "id": "loserchick-egg", "symbol": "egg", "name": "LoserChick EGG" }, - { "id": "loser-coin", "symbol": "lowb", "name": "Loser Coin" }, - { "id": "lossless", "symbol": "lss", "name": "Lossless" }, - { "id": "lost-world", "symbol": "lost", "name": "Lost World" }, - { "id": "lotdog", "symbol": "lotdog", "name": "Lotdog" }, - { "id": "loterra", "symbol": "lota", "name": "LoTerra" }, - { "id": "lotoblock", "symbol": "loto", "name": "Lotoblock" }, - { "id": "lotto", "symbol": "lotto", "name": "Lotto" }, - { "id": "lot-trade", "symbol": "lott", "name": "LOT.TRADE" }, - { "id": "lotus-capital", "symbol": "lc", "name": "Lotus Capital" }, - { "id": "loud-market", "symbol": "loud", "name": "Loud Market" }, - { "id": "louverture", "symbol": "lvt", "name": "Louverture V2" }, - { "id": "louvre-finance", "symbol": "louvre", "name": "Louvre Finance" }, - { "id": "lovechain", "symbol": "lov", "name": "LoveChain" }, - { "id": "love-earth-coin", "symbol": "lec", "name": "Love Earth Coin" }, - { "id": "lovelace-world", "symbol": "lace", "name": "Lovelace World" }, - { - "id": "lovely-inu-finance", - "symbol": "lovely", - "name": "Lovely Inu finance" - }, - { "id": "lovepot-token", "symbol": "love", "name": "LovePot Token" }, - { "id": "love-protocol", "symbol": "love", "name": "Love Protocol" }, - { "id": "lox-network", "symbol": "lox", "name": "Lox Network" }, - { "id": "lp-3pool-curve", "symbol": "3crv", "name": "LP 3pool Curve" }, - { "id": "l-pesa", "symbol": "lpk", "name": "Kripton" }, - { "id": "lpi-dao", "symbol": "lpi", "name": "LPI DAO" }, - { - "id": "lp-renbtc-curve", - "symbol": "renbtcCurve", - "name": "LP renBTC Curve" - }, - { "id": "lp-scurve", "symbol": "sCurve", "name": "LP-sCurve" }, - { "id": "lto-network", "symbol": "lto", "name": "LTO Network" }, - { "id": "ltradex", "symbol": "ltex", "name": "Ltradex" }, - { "id": "luart", "symbol": "luart", "name": "Luart" }, - { "id": "lua-token", "symbol": "lua", "name": "LuaSwap" }, - { "id": "luatoken-wormhole", "symbol": "lua", "name": "LuaToken (Wormhole)" }, - { "id": "luca", "symbol": "luca", "name": "LUCA" }, - { "id": "lucent", "symbol": "lcnt", "name": "Lucent" }, - { "id": "lucha", "symbol": "lucha", "name": "Lucha" }, - { "id": "lucidao", "symbol": "lcd", "name": "Lucidao" }, - { "id": "lucid-lands", "symbol": "llg", "name": "Lucid Lands" }, - { "id": "luckchain", "symbol": "bash", "name": "LuckChain" }, - { "id": "luckstar", "symbol": "lst", "name": "Luckstar" }, - { "id": "lucky-2", "symbol": "lucky", "name": "LUCKY" }, - { "id": "lucky-block", "symbol": "lblock", "name": "Lucky Block" }, - { "id": "lucky-cat", "symbol": "lucky", "name": "Lucky Cat" }, - { "id": "lucky-lion", "symbol": "lucky", "name": "Lucky Lion" }, - { "id": "luckypig", "symbol": "luckypig", "name": "LuckyPig" }, - { - "id": "lucky-property-development-invest", - "symbol": "lpdi", - "name": "Lucky Property Development Invest" - }, - { "id": "luckyseventoken", "symbol": "lst", "name": "LuckySevenToken" }, - { "id": "lucky-shinu", "symbol": "lushi", "name": "Lucky Shinu" }, - { "id": "luckytoken", "symbol": "lkt", "name": "LuckyToken" }, - { "id": "lucky-token", "symbol": "lucky", "name": "Lucky Token" }, - { - "id": "lucky-unicorn-token", - "symbol": "l99", - "name": "Lucky Unicorn Token" - }, - { "id": "lucretius", "symbol": "luc", "name": "Lucretius" }, - { "id": "lucrosus-capital", "symbol": "$LUCA", "name": "Lucrosus Capital" }, - { "id": "lucy", "symbol": "lucy", "name": "LUCY" }, - { "id": "lucy-inu", "symbol": "lucy", "name": "Lucy Inu" }, - { "id": "ludena-protocol", "symbol": "ldn", "name": "Ludena Protocol" }, - { "id": "ludos", "symbol": "lud", "name": "Ludos Protocol" }, - { "id": "luffy-inu", "symbol": "luffy", "name": "Luffy" }, - { "id": "lukso-token", "symbol": "lyxe", "name": "LUKSO Token" }, - { "id": "lulz", "symbol": "lulz", "name": "LULZ" }, - { "id": "lumenswap", "symbol": "lsp", "name": "Lumenswap" }, - { "id": "lumerin", "symbol": "lmr", "name": "Lumerin" }, - { "id": "lumi-credits", "symbol": "lumi", "name": "LUMI Credits" }, - { - "id": "luminos-mining-protocol", - "symbol": "lumi", - "name": "Luminos Mining Protocol" - }, - { "id": "lum-network", "symbol": "lum", "name": "Lum Network" }, - { "id": "lumos-metaverse", "symbol": "lumosx", "name": "Lumos Metaverse" }, - { "id": "lum-rune", "symbol": "lum", "name": "Lum Rune" }, - { "id": "luna-1x", "symbol": "luna1x", "name": "LUNA 1x" }, - { "id": "lunachow", "symbol": "luchow", "name": "LunaChow" }, - { "id": "lunadoge", "symbol": "loge", "name": "LunaDoge" }, - { "id": "lunafi", "symbol": "lfi", "name": "Lunafi" }, - { "id": "lunafox", "symbol": "lufx", "name": "LunaFox" }, - { "id": "lunaland", "symbol": "lln", "name": "LunaLand" }, - { "id": "luna-pad", "symbol": "lunapad", "name": "Luna-Pad" }, - { "id": "lunar", "symbol": "lnr", "name": "Lunar" }, - { "id": "lunarbrain", "symbol": "lun", "name": "LunarBrain" }, - { "id": "lunarium", "symbol": "xln", "name": "Lunarium" }, - { "id": "lunarswap", "symbol": "lunar", "name": "LunarSwap" }, - { "id": "lunar-token", "symbol": "lunar", "name": "Lunar Token" }, - { "id": "luna-rush", "symbol": "lus", "name": "Luna Rush" }, - { "id": "lunaverse", "symbol": "luv", "name": "Lunaverse" }, - { "id": "luna-wormhole", "symbol": "luna", "name": "LUNA (Wormhole)" }, - { "id": "lunch-money", "symbol": "lmy", "name": "Lunch Money" }, - { "id": "lunes", "symbol": "lunes", "name": "Lunes" }, - { "id": "lung-protocol", "symbol": "l2p", "name": "Lung Protocol" }, - { "id": "luni", "symbol": "luni", "name": "LUNI" }, - { "id": "lunr-token", "symbol": "lunr", "name": "Lunr Token" }, - { "id": "lunyr", "symbol": "lun", "name": "Lunyr" }, - { "id": "luto-cash", "symbol": "luto", "name": "Luto Cash" }, - { - "id": "lux-bio-exchange-coin", - "symbol": "lbxc", - "name": "LUX BIO EXCHANGE COIN" - }, - { "id": "luxcoin", "symbol": "lux", "name": "LUXCoin" }, - { "id": "luxeracing", "symbol": "luxe", "name": "LuxeRacing" }, - { "id": "luxfi", "symbol": "lxf", "name": "LuxFi" }, - { "id": "luxor", "symbol": "lux", "name": "Luxor" }, - { - "id": "luxurious-pro-network-token", - "symbol": "lpnt", - "name": "Luxurious Pro Network Token" - }, - { "id": "luxury-club", "symbol": "lux", "name": "Luxury Club" }, - { "id": "luxy", "symbol": "luxy", "name": "Luxy" }, - { "id": "luyuka", "symbol": "lyk", "name": "Luyuka" }, - { "id": "lyca-island", "symbol": "lyca", "name": "Lyca Nation" }, - { "id": "lydia-finance", "symbol": "lyd", "name": "Lydia Finance" }, - { "id": "lyfe", "symbol": "lyfe", "name": "Lyfe" }, - { "id": "lyfe-gold", "symbol": "lgold", "name": "Lyfe Gold" }, - { "id": "lyfe-land", "symbol": "lland", "name": "Lyfe Land" }, - { "id": "lyfe-silver", "symbol": "lsilver", "name": "Lyfe Silver" }, - { "id": "lympo", "symbol": "lym", "name": "Lympo" }, - { "id": "lympo-market-token", "symbol": "lmt", "name": "Lympo Market Token" }, - { "id": "lynkey", "symbol": "lynk", "name": "LynKey" }, - { "id": "lynx", "symbol": "lynx", "name": "Lynx" }, - { "id": "lyocredit", "symbol": "lyo", "name": "Lyocredit" }, - { "id": "lyptus-token", "symbol": "lyptus", "name": "Lyptus Token" }, - { "id": "lyra", "symbol": "lyr", "name": "Lyra" }, - { "id": "lyra-finance", "symbol": "lyra", "name": "Lyra Finance" }, - { "id": "lys-capital", "symbol": "lys", "name": "LYS Capital" }, - { "id": "lzp", "symbol": "LZP", "name": "LZP" }, - { "id": "m2", "symbol": "m2", "name": "M2" }, - { "id": "macaronswap", "symbol": "mcrn", "name": "MacaronSwap" }, - { "id": "machinecoin", "symbol": "mac", "name": "Machinecoin" }, - { "id": "machix", "symbol": "mcx", "name": "Machi X" }, - { "id": "macoin", "symbol": "beck", "name": "Macoin" }, - { "id": "madagascar-token", "symbol": "$time", "name": "Madagascar Token" }, - { "id": "mad-bucks", "symbol": "mad", "name": "MAD Bucks" }, - { "id": "madcredits", "symbol": "MWD", "name": "MadCredits" }, - { "id": "mad-meerkat-etf", "symbol": "metf", "name": "Mad Meerkat ETF" }, - { - "id": "mad-meerkat-optimizer", - "symbol": "mmo", - "name": "Mad Meerkat Optimizer" - }, - { "id": "mad-network", "symbol": "mad", "name": "MADNetwork" }, - { "id": "mad-rabbit", "symbol": "madr", "name": "Mad Rabbit" }, - { "id": "mad-viking-games", "symbol": "mvg", "name": "Mad Viking Games" }, - { "id": "madworld", "symbol": "umad", "name": "MADworld" }, - { "id": "maecenas", "symbol": "art", "name": "Maecenas" }, - { "id": "mafacoin", "symbol": "mafa", "name": "MafaCoin" }, - { "id": "maga-coin", "symbol": "maga", "name": "MAGA coin" }, - { "id": "maggot", "symbol": "maggot", "name": "Maggot" }, - { "id": "magic", "symbol": "magic", "name": "Magic" }, - { "id": "magic-beasties", "symbol": "bsts", "name": "Magic Beasties" }, - { "id": "magic-birds-token", "symbol": "mbt", "name": "Magic Birds Token" }, - { "id": "magic-bnb", "symbol": "mbnb", "name": "MAGIC BNB" }, - { "id": "magicbox", "symbol": "mbt", "name": "MagicBox" }, - { "id": "magic-cake", "symbol": "magiccake", "name": "Magic Cake" }, - { "id": "magiccraft", "symbol": "mcrt", "name": "MagicCraft" }, - { "id": "magic-cube", "symbol": "mcc", "name": "Magic Cube Coin" }, - { - "id": "magic-cube-finance", - "symbol": "mast", - "name": "Magic Cube Finance" - }, - { "id": "magicdoge", "symbol": "magicdoge", "name": "MagicDOGE" }, - { "id": "magic-eggs", "symbol": "maga", "name": "Magic Eggs" }, - { "id": "magic-elpis-gem", "symbol": "meg", "name": "Magic Elpis Gem" }, - { - "id": "magic-ethereum-money", - "symbol": "mem", - "name": "Magic Ethereum Money" - }, - { "id": "magic-forest", "symbol": "magf", "name": "Magic Forest" }, - { - "id": "magic-internet-money", - "symbol": "mim", - "name": "Magic Internet Money" - }, - { - "id": "magic-internet-money-wormhole", - "symbol": "mimet", - "name": "Magic Internet Money (Wormhole)" - }, - { "id": "magic-inu", "symbol": "magic", "name": "Magic Inu" }, - { "id": "magick-dao", "symbol": "magick", "name": "Magick DAO" }, - { "id": "magic-metaverse", "symbol": "mac", "name": "Magic Metaverse" }, - { "id": "magic-of-universe", "symbol": "mgc", "name": "Magic of Universe" }, - { "id": "magic-power", "symbol": "mgp", "name": "Magic Power" }, - { "id": "magic-token", "symbol": "magic", "name": "Magic Token" }, - { - "id": "magic-trading-token", - "symbol": "mtk", - "name": "Magic Trading Token" - }, - { "id": "magik", "symbol": "magik", "name": "Magik" }, - { "id": "magnachain", "symbol": "mgc", "name": "Magnachain" }, - { "id": "magnet-dao", "symbol": "mag", "name": "Magnet DAO" }, - { "id": "magnetgold", "symbol": "mtg", "name": "MagnetGold" }, - { "id": "magpiecoin", "symbol": "mgpc", "name": "MagpieCoin" }, - { "id": "mahadao", "symbol": "maha", "name": "MahaDAO" }, - { "id": "maia", "symbol": "maia", "name": "Maia" }, - { "id": "maiar-dex", "symbol": "mex", "name": "Maiar DEX" }, - { "id": "maidcoin", "symbol": "$maid", "name": "MaidCoin" }, - { "id": "maidsafecoin", "symbol": "maid", "name": "MaidSafeCoin" }, - { "id": "maincoin", "symbol": "mnc", "name": "MainCoin" }, - { "id": "mainframe", "symbol": "mft", "name": "Hifi Finance" }, - { - "id": "mainframe-protocol", - "symbol": "vmain", - "name": "MainFrame Protocol" - }, - { - "id": "mainstream-for-the-underground", - "symbol": "mftu", - "name": "Mainstream For The Underground" - }, - { "id": "maison-capital", "symbol": "MSN", "name": "Maison Capital" }, - { "id": "major-protocol", "symbol": "mjr", "name": "Major Protocol" }, - { - "id": "make-a-difference-token", - "symbol": "mad", - "name": "Make A Difference Token" - }, - { "id": "make-more-money", "symbol": "M3C", "name": "Make More Money" }, - { "id": "maker", "symbol": "mkr", "name": "Maker" }, - { "id": "makes", "symbol": "mks", "name": "Makes" }, - { "id": "makiswap", "symbol": "maki", "name": "MakiSwap" }, - { "id": "makk", "symbol": "makk", "name": "Makk" }, - { "id": "malinka", "symbol": "MLNK", "name": "Malinka" }, - { "id": "mama-dao", "symbol": "mama", "name": "Mama DAO" }, - { "id": "mamadoge", "symbol": "mamadoge", "name": "MamaDoge" }, - { "id": "mamaverse", "symbol": "coinmama", "name": "MamaVerse" }, - { "id": "manager-pro", "symbol": "MPRO", "name": "Manager PRO" }, - { "id": "manarium", "symbol": "ari", "name": "Manarium" }, - { "id": "manateecoin", "symbol": "mtc", "name": "ManateeCoin" }, - { - "id": "manchester-city-fan-token", - "symbol": "city", - "name": "Manchester City Fan Token" - }, - { "id": "mancium", "symbol": "manc", "name": "Mancium" }, - { "id": "mandala-2", "symbol": "mndl", "name": "Mandala" }, - { - "id": "mandala-exchange-token", - "symbol": "mdx", - "name": "Mandala Exchange Token" - }, - { "id": "mandi-token", "symbol": "mandi", "name": "Mandi Token" }, - { "id": "mandox-2", "symbol": "mandox", "name": "MandoX" }, - { "id": "maneki-neko", "symbol": "NEKI", "name": "Maneki-neko" }, - { "id": "manga-token", "symbol": "$manga", "name": "Manga Token" }, - { "id": "mangochain", "symbol": "mgp", "name": "MangoChain" }, - { "id": "mango-markets", "symbol": "mngo", "name": "Mango" }, - { "id": "manifest", "symbol": "mnfst", "name": "Manifest" }, - { "id": "manifold-finance", "symbol": "fold", "name": "Manifold Finance" }, - { "id": "manna", "symbol": "manna", "name": "Manna" }, - { "id": "mantis-network", "symbol": "mntis", "name": "Mantis Network" }, - { "id": "mantra-dao", "symbol": "om", "name": "MANTRA DAO" }, - { "id": "manufactory-2", "symbol": "mnft", "name": "ManuFactory" }, - { "id": "manyswap", "symbol": "many", "name": "Manyswap" }, - { "id": "many-worlds", "symbol": "many", "name": "Many Worlds" }, - { "id": "mao-zedong", "symbol": "mao", "name": "Mao Zedong" }, - { "id": "mapcoin", "symbol": "mapc", "name": "MapCoin" }, - { "id": "maple", "symbol": "mpl", "name": "Maple" }, - { "id": "maps", "symbol": "maps", "name": "MAPS" }, - { "id": "marble", "symbol": "$MARBLE", "name": "Marble Dao" }, - { "id": "marblecoin", "symbol": "mbc", "name": "Marblecoin" }, - { "id": "marblex", "symbol": "mbx", "name": "Marblex" }, - { "id": "marcopolo", "symbol": "map", "name": "MAP Protocol" }, - { "id": "margarita", "symbol": "margarita", "name": "Margarita" }, - { "id": "margin-call", "symbol": "mc", "name": "Margin Call" }, - { "id": "marginswap", "symbol": "mfi", "name": "Marginswap" }, - { "id": "marhabadefi", "symbol": "mrhb", "name": "MarhabaDeFi" }, - { "id": "marinade", "symbol": "mnde", "name": "Marinade" }, - { - "id": "marinade-staked-sol-wormhole", - "symbol": "msol", - "name": "Marinade staked SOL (Wormhole)" - }, - { "id": "mariofloki", "symbol": "mrfloki", "name": "MarioFloki" }, - { "id": "markaccy", "symbol": "MKCY", "name": "Markaccy" }, - { "id": "market-ledger", "symbol": "ml", "name": "Market Ledger" }, - { "id": "market-making-pro", "symbol": "mmpro", "name": "Market Making Pro" }, - { "id": "marketmove", "symbol": "move", "name": "MarketMove" }, - { "id": "marketpeak", "symbol": "peak", "name": "PEAKDEFI" }, - { "id": "markhor-meta", "symbol": "mm", "name": "Markhor Meta" }, - { "id": "markmeta", "symbol": "mmt", "name": "MarkMeta" }, - { "id": "marlin", "symbol": "pond", "name": "Marlin" }, - { "id": "marmaj", "symbol": "marmaj", "name": "Marmaj" }, - { "id": "mar-network", "symbol": "mars", "name": "Mars Network" }, - { "id": "marnotaur", "symbol": "taur", "name": "Marnotaur" }, - { "id": "mars", "symbol": "mars", "name": "Mars" }, - { "id": "mars4", "symbol": "mars4", "name": "MARS4" }, - { "id": "marscoin", "symbol": "mars", "name": "Marscoin" }, - { "id": "marscolony", "symbol": "clny", "name": "MarsColony" }, - { "id": "marsdao", "symbol": "mdao", "name": "MarsDAO" }, - { "id": "mars-doge", "symbol": "MarsDoge", "name": "Mars Doge" }, - { "id": "mars-dogecoin", "symbol": "mdc", "name": "Mars DogeCoin" }, - { - "id": "mars-ecosystem-token", - "symbol": "xms", - "name": "Mars Ecosystem Token" - }, - { "id": "mars-floki-inu", "symbol": "floki", "name": "Mars Floki Inu" }, - { - "id": "marshal-lion-group-coin", - "symbol": "mlgc", - "name": "Marshal Lion Group Coin" - }, - { "id": "marshall-rogan-inu", "symbol": "mri", "name": "Marshall Inu" }, - { "id": "marshmellowdefi", "symbol": "mash", "name": "MarshmallowDeFi" }, - { "id": "mars-inu", "symbol": "marsinu", "name": "Mars Inu" }, - { "id": "mars-panda-world", "symbol": "mpt", "name": "Mars Panda World" }, - { "id": "mars-protocol", "symbol": "mars", "name": "Mars Protocol" }, - { "id": "marsrise", "symbol": "marsrise", "name": "MarsRise" }, - { "id": "mars-space-x", "symbol": "MPX", "name": "Mars Space X" }, - { "id": "mars-token", "symbol": "omt", "name": "Mars Token" }, - { "id": "marsupilamii", "symbol": "mars", "name": "Marsupilamii" }, - { "id": "marsx", "symbol": "mx", "name": "MarsX" }, - { "id": "martexcoin", "symbol": "mxt", "name": "MarteXcoin" }, - { "id": "martiandoge", "symbol": "martiandoge", "name": "MartianDoge" }, - { "id": "martkist", "symbol": "martk", "name": "Martkist" }, - { "id": "marvelous-nfts", "symbol": "mnft", "name": "Marvelous NFTs" }, - { "id": "marvininu", "symbol": "marvin", "name": "MarvinInu" }, - { "id": "marvin-inu", "symbol": "marvin", "name": "Marvin Inu" }, - { "id": "marx", "symbol": "marx", "name": "MarX" }, - { "id": "marxcoin", "symbol": "marx", "name": "MarxCoin" }, - { "id": "masari", "symbol": "msr", "name": "Masari" }, - { "id": "mashima-inu", "symbol": "mashima", "name": "Mashima Inu" }, - { "id": "maskdoge", "symbol": "MaskDoge", "name": "MaskDoge" }, - { "id": "mask-network", "symbol": "mask", "name": "Mask Network" }, - { "id": "mask-vault-nftx", "symbol": "mask", "name": "MASK Vault (NFTX)" }, - { "id": "mason-token", "symbol": "mason", "name": "Mason Token" }, - { "id": "masq", "symbol": "masq", "name": "MASQ" }, - { "id": "mass", "symbol": "mass", "name": "MASS" }, - { "id": "mass-vehicle-ledger", "symbol": "mvl", "name": "MVL" }, - { "id": "masterchef2", "symbol": "masterchef2", "name": "MASTERCHEF2" }, - { - "id": "master-contract-token", - "symbol": "mct", - "name": "Master Contract Token" - }, - { "id": "master-floki", "symbol": "MASTER", "name": "Master Floki" }, - { "id": "masternodesme", "symbol": "mnme", "name": "MasterNodesME" }, - { "id": "masterwin", "symbol": "mw", "name": "MasterWin" }, - { "id": "mastiff-inu", "symbol": "minu", "name": "Mastiff Inu" }, - { "id": "mata", "symbol": "mata", "name": "Mata" }, - { "id": "matador-token", "symbol": "mtdr", "name": "Matador Token" }, - { "id": "matchpool", "symbol": "gup", "name": "Guppy" }, - { "id": "mate", "symbol": "mate", "name": "Mate" }, - { "id": "material", "symbol": "mtrl", "name": "Material" }, - { "id": "materium", "symbol": "mtrm", "name": "Materium" }, - { "id": "math", "symbol": "math", "name": "MATH" }, - { - "id": "math-token-wormhole", - "symbol": "math", - "name": "MATH Token (Wormhole)" - }, - { - "id": "matic-aave-aave", - "symbol": "maaave", - "name": "Matic Aave Interest Bearing AAVE" - }, - { - "id": "matic-aave-usdc", - "symbol": "mausdc", - "name": "Matic Aave Interest Bearing USDC" - }, - { - "id": "matic-aave-weth", - "symbol": "maweth", - "name": "Matic Aave Interest Bearing WETH" - }, - { - "id": "matic-dai-stablecoin", - "symbol": "dai-matic", - "name": "Matic DAI Stablecoin" - }, - { "id": "maticlaunch", "symbol": "mtcl", "name": "MaticLaunch" }, - { "id": "matic-launchpad", "symbol": "maticpad", "name": "Matic Launchpad" }, - { "id": "matic-network", "symbol": "matic", "name": "Polygon" }, - { "id": "maticpad", "symbol": "matpad", "name": "MaticPad" }, - { "id": "maticverse", "symbol": "mverse", "name": "MaticVerse" }, - { "id": "matic-wormhole", "symbol": "maticpo", "name": "MATIC (Wormhole)" }, - { - "id": "matic-wormhole-from-ethereum", - "symbol": "maticet", - "name": "Matic (Wormhole from Ethereum)" - }, - { "id": "matrexcoin", "symbol": "mac", "name": "Matrexcoin" }, - { "id": "matrix-2", "symbol": "mtix", "name": "Matrix" }, - { "id": "matrix-ai-network", "symbol": "man", "name": "Matrix AI Network" }, - { "id": "matrixcoin", "symbol": "mc", "name": "MatrixCoin" }, - { "id": "matrixetf", "symbol": "mdf", "name": "MatrixETF" }, - { "id": "matrix-protocol", "symbol": "mtx", "name": "Matrix Protocol" }, - { "id": "matrix-samurai", "symbol": "mxs", "name": "Marketing Samurai" }, - { - "id": "matrix-solana-index", - "symbol": "msi", - "name": "Matrix Solana Index" - }, - { "id": "matrixswap", "symbol": "matrix", "name": "Matrix Labs" }, - { "id": "matryx", "symbol": "mtx", "name": "MATRYX" }, - { "id": "mavaverse-token", "symbol": "mvv", "name": "Mavaverse Token" }, - { "id": "maxcoin", "symbol": "max", "name": "Maxcoin" }, - { "id": "maxgoat", "symbol": "maxgoat", "name": "MaxGoat" }, - { "id": "maximizer", "symbol": "maxi", "name": "Maximizer" }, - { "id": "maximus", "symbol": "maxi", "name": "Maximus" }, - { "id": "maximus-token", "symbol": "maxi", "name": "Maximus Token" }, - { "id": "maxonrow", "symbol": "mxw", "name": "Maxonrow" }, - { "id": "max-property-group", "symbol": "mcf", "name": "Max Crowdfund" }, - { "id": "max-revive", "symbol": "maxr", "name": "Max Revive" }, - { "id": "max-token", "symbol": "max", "name": "MAX Token" }, - { "id": "maya-coin", "symbol": "maya", "name": "Maya Coin" }, - { "id": "maya-preferred-223", "symbol": "mayp", "name": "Maya Preferred" }, - { "id": "maza", "symbol": "mzc", "name": "Maza" }, - { "id": "mbitbooks", "symbol": "mbit", "name": "MBitBooks" }, - { "id": "mcat", "symbol": "mcat", "name": "mCat" }, - { "id": "mcdex", "symbol": "mcb", "name": "MCDEX" }, - { "id": "mcdoge", "symbol": "mcdoge", "name": "McDoge" }, - { "id": "mcelo", "symbol": "mcelo", "name": "mCELO" }, - { "id": "mceur", "symbol": "mceur", "name": "mcEUR" }, - { "id": "mcfinance", "symbol": "mcf", "name": "MCFinance" }, - { "id": "mchain", "symbol": "mar", "name": "Mchain" }, - { "id": "mch-coin", "symbol": "mchc", "name": "MCH Coin" }, - { "id": "mci-coin", "symbol": "cyclub", "name": "Cyclub" }, - { "id": "mcn-ventures", "symbol": "mcn", "name": "MCN Ventures" }, - { "id": "mcobit", "symbol": "mct", "name": "Mcobit" }, - { "id": "mcoin1", "symbol": "mcoin", "name": "mCoin" }, - { "id": "mcontent", "symbol": "mcontent", "name": "MContent" }, - { "id": "mcpepe", "symbol": "MCPEPE", "name": "MCPEPE" }, - { "id": "mcs-token", "symbol": "mcs", "name": "MCS Token" }, - { "id": "mdex", "symbol": "mdx", "name": "Mdex" }, - { "id": "mdsquare", "symbol": "tmed", "name": "MDsquare" }, - { "id": "mdtoken", "symbol": "mdtk", "name": "MDtoken" }, - { "id": "mdu", "symbol": "mdu", "name": "MDUKEY" }, - { "id": "mead", "symbol": "mead", "name": "Mead" }, - { "id": "meals", "symbol": "meals", "name": "MEALS" }, - { "id": "meanfi", "symbol": "mean", "name": "Meanfi" }, - { "id": "mean-tamato", "symbol": "meantamato", "name": "Mean Tamato" }, - { - "id": "measurable-data-token", - "symbol": "mdt", - "name": "Measurable Data Token" - }, - { "id": "meblox-protocol", "symbol": "meb", "name": "Meblox Protocol" }, - { "id": "mechachain", "symbol": "$mecha", "name": "MechaChain" }, - { "id": "mecha-morphing", "symbol": "mape", "name": "Mecha Morphing" }, - { "id": "mechashiba", "symbol": "mec", "name": "MechaShiba" }, - { "id": "mecha-tracker", "symbol": "mecha", "name": "Mecha Tracker" }, - { "id": "mech-master", "symbol": "mech", "name": "Mech Master" }, - { "id": "meconcash", "symbol": "mch", "name": "Meconcash" }, - { "id": "medacoin", "symbol": "meda", "name": "Medacoin" }, - { "id": "medamon", "symbol": "mon", "name": "Medamon" }, - { "id": "medcarecoin", "symbol": "mcc", "name": "MedCareCoin" }, - { "id": "media-eye", "symbol": "eye", "name": "MeDIA eYe" }, - { - "id": "media-licensing-token", - "symbol": "mlt", - "name": "Media Licensing Token" - }, - { "id": "media-network", "symbol": "media", "name": "Media Network" }, - { "id": "medibit", "symbol": "medibit", "name": "MEDIBIT" }, - { "id": "medibloc", "symbol": "med", "name": "Medibloc" }, - { "id": "medicalchain", "symbol": "mtn", "name": "Medicalchain" }, - { "id": "medicalveda", "symbol": "mveda", "name": "MedicalVeda" }, - { "id": "medican-coin", "symbol": "mcan", "name": "Medican Coin" }, - { "id": "medic-coin", "symbol": "medic", "name": "Medic Coin" }, - { "id": "mediconnect", "symbol": "medi", "name": "MediConnect" }, - { "id": "medikhanax", "symbol": "mkx", "name": "MedikhanaX" }, - { "id": "medishares", "symbol": "mds", "name": "MediShares" }, - { "id": "medi-token", "symbol": "medi", "name": "Medi Token" }, - { "id": "medium", "symbol": "mdm", "name": "MEDIUM" }, - { "id": "medooza-ecosystem", "symbol": "mdza", "name": "Medooza Ecosystem" }, - { "id": "medping", "symbol": "mpg", "name": "Medping" }, - { "id": "medusa", "symbol": "dusa", "name": "Medusa" }, - { "id": "meebitsdao-pool", "symbol": "mbbt", "name": "MeebitsDAO Pool" }, - { "id": "meeb-master", "symbol": "meeb", "name": "Meeb Master" }, - { "id": "meeb-vault-nftx", "symbol": "meeb", "name": "MEEB Vault (NFTX)" }, - { "id": "meeds-dao", "symbol": "meed", "name": "Meeds DAO" }, - { "id": "meerkat-shares", "symbol": "mshare", "name": "Meerkat Shares" }, - { "id": "meetone", "symbol": "meetone", "name": "MEET.ONE" }, - { "id": "meetple", "symbol": "mpt", "name": "Meetple" }, - { "id": "megabonk", "symbol": "mbonk", "name": "megaBonk" }, - { "id": "megacoin", "symbol": "mec", "name": "Megacoin" }, - { "id": "megacosm", "symbol": "megacosm", "name": "Megacosm" }, - { "id": "megacryptopolis", "symbol": "mega", "name": "MegaCryptoPolis" }, - { - "id": "mega-lottery-services-global", - "symbol": "mlr", - "name": "Mega Lottery Services Global" - }, - { "id": "megamoon", "symbol": "mgmoon", "name": "MegaMoon" }, - { "id": "megashibazilla", "symbol": "msz", "name": "MegaShibaZilla" }, - { "id": "megatech", "symbol": "mgt", "name": "Megatech" }, - { "id": "megatoken", "symbol": "mega", "name": "MegaToken" }, - { "id": "megaweapon", "symbol": "$weapon", "name": "Megaweapon" }, - { "id": "megla-doge", "symbol": "MGD", "name": "Megla Doge" }, - { "id": "meishu", "symbol": "meishu", "name": "Meishu" }, - { "id": "meka", "symbol": "meka", "name": "MekaMiners" }, - { "id": "mekkafroggo", "symbol": "mekka", "name": "MekkaFroggo" }, - { "id": "mekka-froggo", "symbol": "lfgo", "name": "Mekka Froggo" }, - { "id": "melalie", "symbol": "mel", "name": "MELX" }, - { "id": "meland-ai", "symbol": "meld", "name": "Meland.ai" }, - { "id": "meld", "symbol": "meld", "name": "MELD" }, - { "id": "meld-gold", "symbol": "mcau", "name": "Meld Gold" }, - { "id": "meli-games", "symbol": "meli", "name": "Meli Games" }, - { "id": "meliodas", "symbol": "meliodas", "name": "Meliodas" }, - { "id": "meliora", "symbol": "mora", "name": "Meliora" }, - { "id": "mello-token", "symbol": "mello", "name": "Mello Token" }, - { "id": "melodity", "symbol": "meld", "name": "Melodity" }, - { "id": "melody", "symbol": "melody", "name": "Melody" }, - { "id": "melon", "symbol": "mln", "name": "Enzyme" }, - { - "id": "melonheadsprotocol", - "symbol": "mhsp", - "name": "MelonHeadSProtocol" - }, - { "id": "melonx", "symbol": "$mlnx", "name": "MELONx" }, - { "id": "melos-studio", "symbol": "melos", "name": "Melos Studio" }, - { "id": "melo-token", "symbol": "melo", "name": "Melo Token" }, - { "id": "membrana-platform", "symbol": "mbn", "name": "Membrana" }, - { - "id": "meme-chain-capital", - "symbol": "memes", - "name": "Meme Chain Capital" - }, - { "id": "memecoin", "symbol": "mem", "name": "Memecoin" }, - { "id": "memecoin-factory", "symbol": "factory", "name": "Memecoin Factory" }, - { "id": "memecoinuniverse", "symbol": "mcu", "name": "MemeCoinUniverse" }, - { "id": "meme-doge-coin", "symbol": "memedoge", "name": "Meme Doge Coin" }, - { "id": "memeflate", "symbol": "mflate", "name": "Memeflate" }, - { "id": "meme-inu", "symbol": "meme", "name": "Meme Inu" }, - { "id": "memekiller", "symbol": "kill", "name": "MemeKiller" }, - { "id": "meme-kong", "symbol": "mkong", "name": "Meme Kong" }, - { "id": "meme-lordz", "symbol": "$lordz", "name": "Meme Lordz" }, - { "id": "meme-ltd", "symbol": "meme20", "name": "MEME LTD" }, - { "id": "meme-machine", "symbol": "mema", "name": "Meme Machine" }, - { "id": "memenft", "symbol": "mnft", "name": "MemeNFT" }, - { "id": "memenopoly-money", "symbol": "mnop", "name": "Memenopoly Money" }, - { "id": "memepad", "symbol": "mepad", "name": "MemePad" }, - { "id": "memes-token", "symbol": "memes", "name": "Memes Token" }, - { "id": "memetic", "symbol": "meme", "name": "Memetic" }, - { "id": "memeverse", "symbol": "meme", "name": "Memeverse" }, - { "id": "memewars", "symbol": "mwar", "name": "MemeWars" }, - { "id": "memex", "symbol": "memex", "name": "MEMEX" }, - { "id": "menapay", "symbol": "mpay", "name": "Menapay" }, - { "id": "menlo-one", "symbol": "one", "name": "Menlo One" }, - { "id": "mensa", "symbol": "msa", "name": "Mensa" }, - { "id": "mensa-protocol", "symbol": "mensa", "name": "Mensa Protocol" }, - { "id": "meong-token", "symbol": "meong", "name": "Meong Token" }, - { "id": "meo-tools", "symbol": "meo", "name": "Meo.tools" }, - { "id": "meownaut", "symbol": "mnt", "name": "Meownaut" }, - { "id": "meowswap", "symbol": "meow", "name": "MeowSwap" }, - { "id": "meowswap-token", "symbol": "meow", "name": "MeowSwap Token" }, - { "id": "mercenary", "symbol": "mgold", "name": "Mercenary" }, - { "id": "merchant-token", "symbol": "mto", "name": "Merchant Token" }, - { "id": "merchdao", "symbol": "mrch", "name": "MerchDAO" }, - { "id": "mercor-finance", "symbol": "mrcr", "name": "Mercor Finance" }, - { "id": "merculet", "symbol": "mvp", "name": "Merculet" }, - { "id": "mercurial", "symbol": "mer", "name": "Mercurial" }, - { "id": "mercury", "symbol": "mer", "name": "Mercury" }, - { "id": "merebel", "symbol": "meri", "name": "Merebel" }, - { "id": "merge", "symbol": "merge", "name": "Merge" }, - { "id": "mergecoin", "symbol": "mgc", "name": "MergeCoin" }, - { "id": "meridian-network", "symbol": "lock", "name": "Meridian Network" }, - { "id": "merit-circle", "symbol": "mc", "name": "Merit Circle" }, - { "id": "merkle-network", "symbol": "merkle", "name": "Merkle Network" }, - { "id": "merlin", "symbol": "merl", "name": "Merlin" }, - { "id": "mermaid", "symbol": "merd", "name": "Mermaid" }, - { "id": "meschain", "symbol": "mes", "name": "MesChain" }, - { "id": "mesefa", "symbol": "sefa", "name": "Mesefa" }, - { "id": "meshbox", "symbol": "mesh", "name": "MeshBox" }, - { "id": "meta", "symbol": "mta", "name": "mStable Governance Token: Meta" }, - { "id": "metaair", "symbol": "mair", "name": "MetaAir" }, - { "id": "metaaltpad", "symbol": "MAP", "name": "MetaAltPad" }, - { "id": "meta-apes", "symbol": "MAPES", "name": "Meta Apes" }, - { "id": "metaaudio", "symbol": "audiom", "name": "MetaAudio" }, - { "id": "metaaxis", "symbol": "mta", "name": "MetaAxis" }, - { "id": "metabean", "symbol": "metabean", "name": "MetaBean" }, - { "id": "metabet", "symbol": "mbet", "name": "MetaBET" }, - { "id": "metabike", "symbol": "MBIKE", "name": "Metabike" }, - { - "id": "meta-billionaires-club", - "symbol": "metabc", - "name": "Meta Billionaires Club" - }, - { "id": "metablackout", "symbol": "mbt", "name": "MetaBlackout" }, - { "id": "metabolic", "symbol": "mtbc", "name": "Metabolic" }, - { "id": "metabots", "symbol": "mtb", "name": "MetaBots" }, - { "id": "metabrands", "symbol": "mage", "name": "MetaBrands" }, - { "id": "meta-brawl", "symbol": "brawl", "name": "Meta Brawl" }, - { "id": "meta-bsc", "symbol": "meta", "name": "Meta BSC" }, - { "id": "metabullrage", "symbol": "berage", "name": "Metabullrage" }, - { "id": "metabullrun", "symbol": "mbr", "name": "MetaBullRun" }, - { "id": "metabusdcoin", "symbol": "mbc", "name": "MetaBUSDCoin" }, - { "id": "meta-capital", "symbol": "mcap", "name": "Meta Capital" }, - { "id": "meta-car", "symbol": "meta car", "name": "Meta Car" }, - { "id": "metacash", "symbol": "meta", "name": "MetaCash" }, - { "id": "metacat", "symbol": "metacat", "name": "MetaCat" }, - { "id": "meta-cat", "symbol": "mcat", "name": "Meta Cat" }, - { "id": "metacelo", "symbol": "cmeta", "name": "MetaCelo" }, - { "id": "metachess", "symbol": "shah", "name": "MetaChess" }, - { "id": "metaclash", "symbol": "pcore", "name": "MetaClash" }, - { "id": "meta-cloth", "symbol": "meta cloth", "name": "Meta Cloth" }, - { "id": "meta-club", "symbol": "club", "name": "Meta Club" }, - { "id": "metacoin", "symbol": "mtc", "name": "Metacoin" }, - { "id": "metacontinental", "symbol": "con", "name": "MetaContinental" }, - { "id": "metacraft", "symbol": "mct", "name": "Metacraft" }, - { "id": "metacz", "symbol": "metacz", "name": "MetaCZ" }, - { "id": "meta-decentraland", "symbol": "mdl", "name": "Meta Decentraland" }, - { "id": "metadium", "symbol": "meta", "name": "Metadium" }, - { "id": "metadoctor", "symbol": "medoc", "name": "MetaDoctor" }, - { "id": "meta-doge", "symbol": "metadoge", "name": "Meta Doge" }, - { "id": "metadogeswap", "symbol": "mds", "name": "MetaDogeSwap" }, - { "id": "metadoge-v2", "symbol": "metadogev2", "name": "MetaDoge V2" }, - { "id": "metadog-racing", "symbol": "dog$", "name": "MetaDog Racing" }, - { "id": "metadrace", "symbol": "drace", "name": "MetaDrace" }, - { "id": "meta-dragon-city", "symbol": "dragon", "name": "Meta Dragon City" }, - { "id": "metadress", "symbol": "mtd", "name": "MetaDress" }, - { "id": "metadubai", "symbol": "mdb", "name": "MetaDubai" }, - { "id": "metafabric", "symbol": "fabric", "name": "MetaFabric" }, - { "id": "metaface", "symbol": "MFT", "name": "MetaFace" }, - { "id": "metafarm-dao", "symbol": "metafarm", "name": "MetaFarm DAO" }, - { - "id": "meta-farmer-finance", - "symbol": "mff", - "name": "Meta Farmer Finance" - }, - { "id": "metafashioners", "symbol": "mfs", "name": "MetaFashioners" }, - { "id": "metaficial-world", "symbol": "mw", "name": "Metaficial World" }, - { "id": "metafighter", "symbol": "mf", "name": "MetaFighter" }, - { "id": "metafinance", "symbol": "mfi", "name": "MetaFinance" }, - { "id": "meta_finance", "symbol": "mf1", "name": "Meta Finance" }, - { "id": "metafish", "symbol": "fish", "name": "Metafish" }, - { "id": "metaflip", "symbol": "metaflip", "name": "MetaFlip" }, - { "id": "meta-floki", "symbol": "mfloki", "name": "Meta Floki" }, - { "id": "meta-floki-inu", "symbol": "metaflokinu", "name": "Meta Floki Inu" }, - { - "id": "meta-flokimon-go", - "symbol": "metaflokimg", - "name": "Meta FlokiMon Go" - }, - { "id": "metafluence", "symbol": "meto", "name": "Metafluence" }, - { "id": "metafocus", "symbol": "metafocus", "name": "MetaFocus" }, - { "id": "metafootball", "symbol": "mtf", "name": "MetaFootball" }, - { "id": "metagalaxy", "symbol": "mgxy", "name": "Metagalaxy" }, - { "id": "metagalaxy-land", "symbol": "megaland", "name": "Metagalaxy Land" }, - { "id": "metagame", "symbol": "seed", "name": "MetaGame" }, - { "id": "metagame-arena", "symbol": "mga", "name": "Metagame Arena" }, - { "id": "metagamehub-dao", "symbol": "mgh", "name": "MetaGameHub DAO" }, - { "id": "metagamespace", "symbol": "metags", "name": "MetaGameSpace" }, - { "id": "metagaming", "symbol": "mtgm", "name": "MetaGaming" }, - { "id": "metagaming-guild", "symbol": "mgg", "name": "MetaGaming Guild" }, - { "id": "metagamz", "symbol": "metag", "name": "MetagamZ" }, - { "id": "metagear", "symbol": "gear", "name": "MetaGear" }, - { "id": "metagochi", "symbol": "mgchi", "name": "Metagochi" }, - { "id": "metagods", "symbol": "mgod", "name": "MetaGods" }, - { "id": "metagon", "symbol": "metagon", "name": "Metagon" }, - { "id": "metagox", "symbol": "mtx", "name": "Metagox" }, - { "id": "meta-grow", "symbol": "META", "name": "META GROW" }, - { "id": "meta-hangry-games", "symbol": "mhg", "name": "Meta Hangry Games" }, - { "id": "metahash", "symbol": "mhc", "name": "#MetaHash" }, - { "id": "metahero", "symbol": "hero", "name": "Metahero" }, - { "id": "meta-house", "symbol": "meta house", "name": "Meta House" }, - { "id": "meta-inu", "symbol": "metainu", "name": "Meta Inu" }, - { "id": "meta-inu-token", "symbol": "meta", "name": "Meta Inu Token" }, - { "id": "meta-islands", "symbol": "igo", "name": "Meta Islands" }, - { "id": "metakings", "symbol": "mtk", "name": "Metakings" }, - { "id": "meta-knight", "symbol": "MetaKnight", "name": "Meta Knight" }, - { "id": "metakombat", "symbol": "kombat", "name": "MetaKombat" }, - { "id": "meta-kongz", "symbol": "mkc", "name": "Meta Kongz" }, - { "id": "metal", "symbol": "mtl", "name": "Metal" }, - { "id": "metalama", "symbol": "LAMA", "name": "MetaLama" }, - { "id": "metaland-dao", "symbol": "meta", "name": "Metaland DAO" }, - { "id": "metaland-gameverse", "symbol": "mst", "name": "Monster" }, - { "id": "metaland-shards", "symbol": "mls", "name": "Metaland Shards" }, - { "id": "meta-land-yield", "symbol": "mly", "name": "Meta Land Yield" }, - { "id": "metalandz", "symbol": "$metaz", "name": "MetaLandz" }, - { - "id": "metal-backed-money", - "symbol": "mbmx", - "name": "Metal Backed Money" - }, - { "id": "meta-legends", "symbol": "metal", "name": "Meta Legends" }, - { "id": "metalk", "symbol": "meta", "name": "Metalk" }, - { "id": "metal-music-coin", "symbol": "mtlmc3", "name": "Metal Music Coin" }, - { "id": "metaloop-tech", "symbol": "mlt", "name": "Metaloop Tech" }, - { - "id": "metal-packaging-token", - "symbol": "mpt", - "name": "Metal Packaging Token" - }, - { "id": "metalswap", "symbol": "xmt", "name": "MetalSwap" }, - { "id": "metaluna", "symbol": "metaluna", "name": "MetaLuna" }, - { "id": "metamall", "symbol": "mall", "name": "MetaMall" }, - { "id": "metamaps", "symbol": "mema", "name": "MetaMaps" }, - { "id": "metamars", "symbol": "metam", "name": "MetaMars" }, - { "id": "metamate", "symbol": "mtm", "name": "MetaMate" }, - { "id": "metamatrix", "symbol": "mtx", "name": "MetaMatrix" }, - { "id": "meta-monstas", "symbol": "MONSTA", "name": "Meta Monstas" }, - { "id": "metamoon", "symbol": "MetaMoon", "name": "MetaMoon" }, - { "id": "metamorph", "symbol": "metm", "name": "MetaMorph" }, - { "id": "metamounts", "symbol": "mount", "name": "MetaMounts" }, - { "id": "metamui", "symbol": "mmui", "name": "MetaMUI" }, - { "id": "meta-musk", "symbol": "META", "name": "Meta Musk" }, - { "id": "meta-mvrs", "symbol": "mvrs", "name": "Meta MVRS" }, - { "id": "metanations", "symbol": "mena", "name": "MetaNations" }, - { "id": "metan-evolutions", "symbol": "METAN", "name": "Metan Evolutions" }, - { "id": "metanft", "symbol": "mnft", "name": "MetaNFT" }, - { "id": "metanftgaming", "symbol": "mng", "name": "MetaNFTGaming" }, - { "id": "metaniagames", "symbol": "metania", "name": "MetaniaGames" }, - { "id": "metanyx", "symbol": "metx", "name": "Metanyx" }, - { "id": "metapad", "symbol": "mpd", "name": "Metapad" }, - { "id": "metapay", "symbol": "mpay", "name": "Metapay" }, - { "id": "metapets", "symbol": "metapets", "name": "MetaPets" }, - { "id": "meta-pets", "symbol": "mp", "name": "Meta Pets" }, - { "id": "metapixel", "symbol": "mepix", "name": "MetaPixel" }, - { "id": "metaplace", "symbol": "mpc", "name": "Metaplace" }, - { "id": "metaplanet", "symbol": "mpl", "name": "MetaPlanet" }, - { "id": "metaplay", "symbol": "mplay", "name": "MetaPlay" }, - { "id": "metaplayers-gg", "symbol": "fps", "name": "MetaPlayers.gg" }, - { "id": "metapool", "symbol": "mpool", "name": "Metapool" }, - { "id": "meta-pool", "symbol": "meta", "name": "Meta Pool" }, - { "id": "metaportal", "symbol": "metaportal", "name": "MetaPortal" }, - { - "id": "metaportal-gaming-index", - "symbol": "game", - "name": "MetaPortal Gaming Index" - }, - { "id": "metapplay", "symbol": "metap", "name": "MetapPlay" }, - { "id": "metaprints", "symbol": "", "name": "Metaprints" }, - { "id": "metaq", "symbol": "metaq", "name": "MetaQ" }, - { "id": "metaraca", "symbol": "metar", "name": "MetaRaca" }, - { "id": "metaracers", "symbol": "mrs", "name": "MetaRacers" }, - { "id": "metarare", "symbol": "mtra", "name": "MetaRare" }, - { "id": "metarcade", "symbol": "meta", "name": "Metarcade" }, - { "id": "metarealm", "symbol": "mrlm", "name": "MetaRealm" }, - { "id": "metareserve", "symbol": "power", "name": "MetaReserve" }, - { "id": "metarim", "symbol": "rim", "name": "MetaRim" }, - { "id": "metarobox", "symbol": "rbx", "name": "Metarobox" }, - { "id": "metaroid-nft", "symbol": "metaroid", "name": "Metaroid NFT" }, - { "id": "metaroyale", "symbol": "mrvr", "name": "MetaRoyale" }, - { "id": "meta-ruffy", "symbol": "mr", "name": "Meta Ruffy" }, - { "id": "metarun", "symbol": "mrun", "name": "Metarun" }, - { "id": "metasafemoon", "symbol": "metasfm", "name": "MetaSafeMoon" }, - { "id": "metaseer", "symbol": "metas", "name": "Metaseer" }, - { "id": "metasens", "symbol": "msu", "name": "Metasens" }, - { "id": "meta-shark", "symbol": "mts", "name": "Meta Shark" }, - { "id": "metashells", "symbol": "shell", "name": "MetaShells" }, - { "id": "metashiba", "symbol": "METASHIB", "name": "MetaShiba" }, - { "id": "meta-shiba", "symbol": "mshiba", "name": "Meta Shiba" }, - { "id": "meta-shiba-bsc", "symbol": "meshi", "name": "Meta Shiba BSC" }, - { "id": "metashib-token", "symbol": "metashib", "name": "MetaShib Token" }, - { "id": "meta-shield", "symbol": "shield", "name": "Meta Shield" }, - { "id": "metashooter", "symbol": "mhunt", "name": "MetaShooter" }, - { - "id": "meta-skinny-hope", - "symbol": "metash", - "name": "Meta Skinny\u0026Hope" - }, - { "id": "metasnooker", "symbol": "msr", "name": "MetaSnooker" }, - { "id": "metasoccer", "symbol": "msu", "name": "MetaSoccer" }, - { "id": "metaspace", "symbol": "mspace", "name": "Metaspace" }, - { "id": "metaspace-reit", "symbol": "mreit", "name": "MetaSpace REIT" }, - { "id": "meta-spatial", "symbol": "spat", "name": "Meta Spatial" }, - { "id": "metaspets", "symbol": "msp", "name": "MetaSpets" }, - { "id": "metasphere", "symbol": "mtsp", "name": "Metasphere" }, - { "id": "metastar", "symbol": "metastar", "name": "Metastar" }, - { "id": "metastrike", "symbol": "mts", "name": "Metastrike" }, - { "id": "metasurvivor", "symbol": "msg", "name": "MetaSurvivor" }, - { "id": "metaswap", "symbol": "msc", "name": "MetaSwap" }, - { "id": "metatariff", "symbol": "riff", "name": "MetaTariff" }, - { "id": "metatoll", "symbol": "tax", "name": "MetaToll" }, - { "id": "metatown", "symbol": "mtown", "name": "MetaTown" }, - { "id": "metatrone", "symbol": "met", "name": "Metatrone" }, - { "id": "metaufo", "symbol": "metaufo", "name": "MetaUFO" }, - { "id": "metauniverse", "symbol": "metauniverse", "name": "MetaUniverse" }, - { "id": "meta-uranus", "symbol": "meur", "name": "Meta Uranus" }, - { "id": "meta-utopia", "symbol": "land", "name": "Meta Utopia" }, - { "id": "metavault", "symbol": "mvd", "name": "Metavault" }, - { "id": "metavegas", "symbol": "metavegas", "name": "MetaVegas" }, - { "id": "metaverseair", "symbol": "mvrs", "name": "MetaverseAir" }, - { - "id": "metaverse-all-best-ico", - "symbol": "metaallbi", - "name": "Metaverse ALL BEST ICO" - }, - { "id": "metaverse-capital", "symbol": "MVC", "name": "Metaverse Capital" }, - { "id": "metaverse-dao", "symbol": "mdao", "name": "Metaverse DAO" }, - { "id": "metaverse-dog", "symbol": "mvdg", "name": "MetaVerse Dog" }, - { "id": "metaverse-doge", "symbol": "$MVDOGE", "name": "Metaverse Doge" }, - { - "id": "metaverse-dualchain-network-architecture", - "symbol": "dna", - "name": "Metaverse DNA" - }, - { "id": "metaverse-etp", "symbol": "etp", "name": "Metaverse ETP" }, - { - "id": "metaverse-exchange", - "symbol": "metacex", - "name": "Metaverse Exchange" - }, - { "id": "metaverse-face", "symbol": "mefa", "name": "Metaverse Face" }, - { "id": "metaverse-index", "symbol": "mvi", "name": "Metaverse Index" }, - { - "id": "metaverse-index-token", - "symbol": "metai", - "name": "Metaverse Index Token" - }, - { "id": "metaverse-lab", "symbol": "mvp", "name": "Metaverse Lab" }, - { "id": "metaversemgl", "symbol": "mglc", "name": "MetaverseMGL" }, - { "id": "metaverse-miner", "symbol": "META", "name": "Metaverse Miner" }, - { - "id": "metaverse-nft-index", - "symbol": "PLAY", - "name": "Metaverse NFT Index" - }, - { "id": "metaversepro", "symbol": "meta", "name": "MetaversePRO" }, - { "id": "metaverse-vr", "symbol": "mevr", "name": "Metaverse VR" }, - { "id": "metaversex", "symbol": "metax", "name": "MetaverseX" }, - { "id": "metaversus", "symbol": "metavs", "name": "MetaVersus" }, - { "id": "metavisa", "symbol": "mesa", "name": "MetaVisa" }, - { "id": "metavpad", "symbol": "metav", "name": "MetaVPad" }, - { "id": "metawar", "symbol": "metawar", "name": "Metawar" }, - { "id": "metawars", "symbol": "wars", "name": "MetaWars" }, - { "id": "metawear", "symbol": "wear", "name": "MetaWear" }, - { "id": "metawhale-gold", "symbol": "MWG", "name": "Metawhale Gold" }, - { "id": "metaworld", "symbol": "mw", "name": "MetaWorld" }, - { "id": "meta-world", "symbol": "metaworld", "name": "Meta World" }, - { "id": "meta-world-game", "symbol": "MTW", "name": "Meta World Game" }, - { "id": "metaxhunter", "symbol": "xhunter", "name": "MetaXHunter" }, - { "id": "metaxiz", "symbol": "mexi", "name": "Metaxiz" }, - { "id": "metaxy", "symbol": "mxy", "name": "Metaxy" }, - { "id": "metayoka-spice", "symbol": "spice", "name": "Metayoka Spice" }, - { "id": "metazilla", "symbol": "mz", "name": "MetaZilla" }, - { "id": "metazoon-token", "symbol": "mez", "name": "MetaZoon Token" }, - { "id": "metazuckzilla", "symbol": "meta", "name": "MetaZuckZilla" }, - { - "id": "meteorite-network", - "symbol": "meteor", - "name": "Meteorite Network" - }, - { - "id": "meteor-remnants-essence", - "symbol": "mre", - "name": "Meteor Remnants Essence" - }, - { "id": "meter", "symbol": "mtrg", "name": "Meter Governance" }, - { - "id": "meter-governance-mapped-by-meter-io", - "symbol": "eMTRG", - "name": "Meter Governance mapped by Meter.io" - }, - { "id": "meter-stable", "symbol": "mtr", "name": "Meter Stable" }, - { "id": "method-fi", "symbol": "mthd", "name": "Method Finance" }, - { "id": "metis", "symbol": "mts", "name": "Metis" }, - { "id": "metisrise", "symbol": "mrise", "name": "MetisRise" }, - { "id": "metis-token", "symbol": "metis", "name": "Metis Token" }, - { "id": "metoshi", "symbol": "meto", "name": "Metoshi" }, - { "id": "metra", "symbol": "metra", "name": "Metra" }, - { "id": "metria", "symbol": "metr", "name": "Metria Network" }, - { "id": "metric-exchange", "symbol": "metric", "name": "MetricExchange" }, - { "id": "metronome", "symbol": "met", "name": "Metronome" }, - { "id": "metroverse", "symbol": "met", "name": "Metroverse" }, - { "id": "mettalex", "symbol": "mtlx", "name": "Mettalex" }, - { "id": "metti-inu", "symbol": "metti", "name": "Metti Inu" }, - { "id": "meverse", "symbol": "mev", "name": "MEVerse" }, - { "id": "mew-inu", "symbol": "MEW", "name": "Mew Inu" }, - { "id": "mewn-inu", "symbol": "mewn", "name": "Mewn Inu" }, - { "id": "mewtwo-inu", "symbol": "mewtwo", "name": "Mewtwo Inu" }, - { "id": "mex", "symbol": "mex", "name": "MEX" }, - { "id": "mexc-token", "symbol": "mexc", "name": "MEXC Token" }, - { "id": "mgc-token", "symbol": "mgc", "name": "MGC Token" }, - { "id": "mgoat", "symbol": "mgoat", "name": "MetaGoat" }, - { "id": "mg-social", "symbol": "mgs", "name": "MG.Social" }, - { "id": "miami", "symbol": "miami", "name": "MIAMI" }, - { "id": "miamicoin", "symbol": "mia", "name": "MiamiCoin" }, - { "id": "miami-land", "symbol": "miami", "name": "Miami Land" }, - { "id": "miaw-token", "symbol": "miaw", "name": "Miaw" }, - { "id": "mib-coin", "symbol": "mib", "name": "MIB Coin" }, - { "id": "microbitcoin", "symbol": "mbc", "name": "MicroBitcoin" }, - { - "id": "micro-bitcoin-finance", - "symbol": "mbtc", - "name": "Micro Bitcoin Finance" - }, - { "id": "microcoin", "symbol": "mcc", "name": "MicroCoin" }, - { "id": "microcosm", "symbol": "mic", "name": "Microcosm" }, - { "id": "microcredittoken", "symbol": "1mct", "name": "MicroCreditToken" }, - { "id": "microdexwallet", "symbol": "micro", "name": "MicroDexWallet" }, - { - "id": "micro-gaming-protocol", - "symbol": "mgp", - "name": "Micro Gaming Protocol" - }, - { "id": "micromines", "symbol": "micro", "name": "Micromines" }, - { "id": "micromoney", "symbol": "amm", "name": "MicroMoney" }, - { "id": "micropets", "symbol": "pets", "name": "MicroPets" }, - { - "id": "micro-santa-coin", - "symbol": "MicroSanta", - "name": "Micro Santa Coin" - }, - { "id": "microshiba", "symbol": "microshib", "name": "MicroSHIBA" }, - { - "id": "microsoft-tokenized-stock-defichain", - "symbol": "dmsft", - "name": "Microsoft Tokenized Stock Defichain" - }, - { "id": "microtick", "symbol": "TICK", "name": "Microtick" }, - { "id": "microtuber", "symbol": "mct", "name": "MicroTuber" }, - { "id": "microzillas", "symbol": "mizl", "name": "MicroZillas" }, - { "id": "midas", "symbol": "midas", "name": "Midas" }, - { "id": "midasdao", "symbol": "crown", "name": "MidasDAO" }, - { "id": "midas-dollar", "symbol": "mdo", "name": "Midas Dollar" }, - { "id": "midas-gold", "symbol": "mdg", "name": "Midas Gold" }, - { "id": "midas-protocol", "symbol": "mas", "name": "Midas Protocol" }, - { "id": "migranet", "symbol": "mig", "name": "Migranet" }, - { "id": "miks-coin", "symbol": "miks", "name": "MIKS COIN" }, - { "id": "milestonebased", "symbol": "mile", "name": "milestoneBased" }, - { "id": "mileverse", "symbol": "mvc", "name": "MileVerse" }, - { "id": "milf-finance", "symbol": "milf", "name": "MILF Finance" }, - { "id": "military-finance", "symbol": "mil", "name": "Military Finance" }, - { "id": "militia-games", "symbol": "milit", "name": "Militia Games" }, - { "id": "milk", "symbol": "milk", "name": "Cool Cats Milk" }, - { "id": "milk2", "symbol": "milk", "name": "CashCow Protocol Milk" }, - { "id": "milk-alliance", "symbol": "mlk", "name": "MiL.k Alliance" }, - { "id": "milk-and-butter", "symbol": "mb", "name": "Milk and Butter" }, - { "id": "milkshake", "symbol": "mshake", "name": "Milkshake" }, - { "id": "milkshakeswap", "symbol": "milk", "name": "Milkshake Swap" }, - { "id": "milkshiba", "symbol": "mshib", "name": "MilkShiba" }, - { "id": "milkyswap", "symbol": "milky", "name": "MilkySwap" }, - { "id": "milky-token", "symbol": "milky", "name": "Milky Token" }, - { "id": "milkywayex", "symbol": "milky", "name": "MilkyWayEx" }, - { "id": "millenniumclub", "symbol": "mclb", "name": "MillenniumClub Coin" }, - { - "id": "millennium-sapphire", - "symbol": "msto", - "name": "Millennium Sapphire" - }, - { "id": "millimeter", "symbol": "mm", "name": "Millimeter" }, - { "id": "million", "symbol": "mm", "name": "Million" }, - { - "id": "millionaire-maker", - "symbol": "million", - "name": "Millionaire Maker" - }, - { "id": "million-monke", "symbol": "MIMO", "name": "Million Monke" }, - { "id": "millionsy", "symbol": "milli", "name": "MILLIONSY" }, - { - "id": "millonarios-fc-fan-token", - "symbol": "mfc", - "name": "Millonarios FC Fan Token" - }, - { "id": "milo-inu", "symbol": "milo", "name": "Milo Inu" }, - { "id": "mim", "symbol": "swarm", "name": "MIM" }, - { "id": "mimas", "symbol": "mimas", "name": "Mimas" }, - { "id": "mimatic", "symbol": "mimatic", "name": "MAI" }, - { "id": "mimblewimblecoin", "symbol": "mwc", "name": "MimbleWimbleCoin" }, - { "id": "mimir-token", "symbol": "mimir", "name": "Mimir Token" }, - { - "id": "mimo-parallel-governance-token", - "symbol": "mimo", - "name": "Mimo Governance Token" - }, - { "id": "mimosa", "symbol": "mimo", "name": "Mimosa" }, - { "id": "mina-protocol", "symbol": "mina", "name": "Mina Protocol" }, - { "id": "minato", "symbol": "mnto", "name": "Minato" }, - { "id": "mincoin", "symbol": "mnc", "name": "Mincoin" }, - { "id": "mindcell", "symbol": "mdc", "name": "MindCell" }, - { "id": "mindcoin", "symbol": "mnd", "name": "MindCoin" }, - { "id": "mindfolk-wood", "symbol": "$wood", "name": "Mindfolk Wood" }, - { "id": "mind-music", "symbol": "mnd", "name": "Mind Music" }, - { "id": "mindol", "symbol": "min", "name": "MINDOL" }, - { "id": "minds", "symbol": "minds", "name": "Minds" }, - { "id": "mindsync", "symbol": "mai", "name": "Mindsync" }, - { "id": "minebee", "symbol": "mb", "name": "MineBee" }, - { "id": "minecrypto", "symbol": "mcr", "name": "MineCrypto" }, - { "id": "mine-network", "symbol": "mnet", "name": "MINE Network" }, - { "id": "mineral", "symbol": "mnr", "name": "Mineral" }, - { "id": "minerblox", "symbol": "mblox", "name": "MinerBlox" }, - { "id": "minereum", "symbol": "mne", "name": "Minereum" }, - { "id": "minergate-token", "symbol": "mg", "name": "MinerGate Token" }, - { "id": "minerjoe", "symbol": "gold", "name": "MinerJoe" }, - { "id": "minersdefi", "symbol": "miners", "name": "MinersDefi" }, - { "id": "minerva-wallet", "symbol": "miva", "name": "Minerva Wallet" }, - { "id": "mines-of-dalarnia", "symbol": "dar", "name": "Mines of Dalarnia" }, - { "id": "mineum", "symbol": "mnm", "name": "Mineum" }, - { "id": "mini", "symbol": "mini", "name": "Mini" }, - { - "id": "mini-baby-doge", - "symbol": "minibabydoge", - "name": "Mini Baby Doge" - }, - { "id": "minibitcoin", "symbol": "mbtc", "name": "MiniBitcoin" }, - { "id": "minibnb", "symbol": "minibnb", "name": "MiniBNB" }, - { "id": "minibtc", "symbol": "minibtc", "name": "MiniBTC" }, - { "id": "mini-cardano", "symbol": "mada", "name": "Mini Cardano" }, - { "id": "minidoge", "symbol": "minidoge", "name": "MiniDOGE" }, - { "id": "mini-doge-pro", "symbol": "minidogepro", "name": "Mini Doge Pro" }, - { "id": "mini-floki", "symbol": "MiniFloki", "name": "Mini Floki" }, - { "id": "miniflokiada", "symbol": "mflokiada", "name": "MiniFlokiADA" }, - { "id": "minifootball", "symbol": "minifootball", "name": "Minifootball" }, - { "id": "minihokk", "symbol": "MHokk", "name": "MiniHokk" }, - { - "id": "minikishimoto-inu", - "symbol": "minikishimoto", - "name": "miniKishimoto Inu" - }, - { "id": "minikishu", "symbol": "MINIKISHU", "name": "MINIKISHU" }, - { "id": "minimals", "symbol": "mms", "name": "Minimals" }, - { "id": "mini-metis", "symbol": "minime", "name": "Mini Metis" }, - { "id": "mini-mongoose", "symbol": "miniMONGOOSE", "name": "mini Mongoose" }, - { "id": "minipanther", "symbol": "mp", "name": "MiniPanther" }, - { "id": "mini-saitama", "symbol": "minisaitama", "name": "Mini Saitama" }, - { "id": "mini-shib", "symbol": "mshib", "name": "mini SHIB" }, - { "id": "minishib-eth", "symbol": "minishib", "name": "miniSHIB ETH" }, - { "id": "minishib-token", "symbol": "minishib", "name": "miniSHIB Token" }, - { "id": "minisportzilla", "symbol": "minisportz", "name": "MiniSportZilla" }, - { "id": "minithunder", "symbol": "Thunder", "name": "MiniThunder" }, - { "id": "mini-tiger", "symbol": "minitiger", "name": "Mini Tiger" }, - { "id": "miniusdc", "symbol": "miniusdc", "name": "MiniUSDC" }, - { - "id": "miniverse-dollar", - "symbol": "mvdollar", - "name": "MiniVerse Dollar" - }, - { "id": "miniverse-share", "symbol": "mshare", "name": "Miniverse Share" }, - { "id": "minix", "symbol": "MNX", "name": "MiniX" }, - { "id": "miniyak", "symbol": "myak", "name": "MiniYAK" }, - { "id": "minmax", "symbol": "max", "name": "MinMax" }, - { "id": "minotaur", "symbol": "MINO", "name": "Minotaur" }, - { "id": "mint-club", "symbol": "mint", "name": "Mint Club" }, - { "id": "mintcoin", "symbol": "mint", "name": "Mintcoin" }, - { "id": "mintdao", "symbol": "mint", "name": "MintDAO" }, - { "id": "mintea", "symbol": "mint", "name": "Mintea" }, - { "id": "minter-hub", "symbol": "hub", "name": "Minter Hub" }, - { "id": "minto", "symbol": "btcmt", "name": "Minto" }, - { "id": "mintstarter", "symbol": "mint", "name": "MintStarter" }, - { "id": "mintswap", "symbol": "mint", "name": "MintSwap" }, - { "id": "mintyswap", "symbol": "mintys", "name": "MintySwap" }, - { "id": "mirai-token", "symbol": "mirai", "name": "Mirai Token" }, - { "id": "miraqle", "symbol": "mql", "name": "MiraQle" }, - { "id": "mirl", "symbol": "mirl", "name": "MIRL" }, - { "id": "mirocana", "symbol": "miro", "name": "Mirocana" }, - { "id": "mirrored-airbnb", "symbol": "mabnb", "name": "Mirrored Airbnb" }, - { "id": "mirrored-alibaba", "symbol": "mbaba", "name": "Mirrored Alibaba" }, - { "id": "mirrored-amazon", "symbol": "mamzn", "name": "Mirrored Amazon" }, - { - "id": "mirrored-amc-entertainment", - "symbol": "mamc", - "name": "Mirrored AMC Entertainment" - }, - { "id": "mirrored-apple", "symbol": "maapl", "name": "Mirrored Apple" }, - { "id": "mirrored-bitcoin", "symbol": "mbtc", "name": "Mirrored Bitcoin" }, - { "id": "mirrored-coca-cola", "symbol": "mko", "name": "Mirrored Coca-Cola" }, - { "id": "mirrored-coinbase", "symbol": "mcoin", "name": "Mirrored Coinbase" }, - { "id": "mirrored-ether", "symbol": "meth", "name": "Mirrored Ether" }, - { "id": "mirrored-facebook", "symbol": "mfb", "name": "Mirrored Facebook" }, - { - "id": "mirrored-galaxy-digital-holdings-ltd", - "symbol": "mglxy", - "name": "Mirrored Galaxy Digital Holdings Ltd" - }, - { "id": "mirrored-gamestop", "symbol": "mgme", "name": "Mirrored GameStop" }, - { - "id": "mirrored-goldman-sachs", - "symbol": "mgs", - "name": "Mirrored Goldman Sachs" - }, - { "id": "mirrored-google", "symbol": "mgoogl", "name": "Mirrored Google" }, - { - "id": "mirrored-invesco-qqq-trust", - "symbol": "mqqq", - "name": "Mirrored Invesco QQQ Trust" - }, - { - "id": "mirrored-ishares-gold-trust", - "symbol": "miau", - "name": "Mirrored iShares Gold Trust" - }, - { - "id": "mirrored-ishares-silver-trust", - "symbol": "mslv", - "name": "Mirrored iShares Silver Trust" - }, - { - "id": "mirrored-johnson-johnson", - "symbol": "mjnj", - "name": "Mirrored Johnson \u0026 Johnson" - }, - { - "id": "mirrored-microsoft", - "symbol": "mmsft", - "name": "Mirrored Microsoft" - }, - { "id": "mirrored-netflix", "symbol": "mnflx", "name": "Mirrored Netflix" }, - { "id": "mirrored-nio", "symbol": "mnio", "name": "Mirrored Nio" }, - { "id": "mirrored-nvidia", "symbol": "mnvda", "name": "Mirrored Nvidia" }, - { "id": "mirrored-paypal", "symbol": "mpypl", "name": "Mirrored Paypal" }, - { - "id": "mirrored-robinhood-markets", - "symbol": "mhood", - "name": "Mirrored Robinhood Markets" - }, - { - "id": "mirrored-spdr-s-p-500", - "symbol": "mspy", - "name": "Mirrored SPDR S\u0026P 500" - }, - { "id": "mirrored-square", "symbol": "msq", "name": "Mirrored Square" }, - { - "id": "mirrored-starbucks", - "symbol": "msbux", - "name": "Mirrored Starbucks" - }, - { "id": "mirrored-tesla", "symbol": "mtsla", "name": "Mirrored Tesla" }, - { "id": "mirrored-twitter", "symbol": "mtwtr", "name": "Mirrored Twitter" }, - { - "id": "mirrored-united-states-oil-fund", - "symbol": "muso", - "name": "Mirrored United States Oil Fund" - }, - { "id": "mirror-mamd-token", "symbol": "mamd", "name": "Mirror mAMD Token" }, - { - "id": "mirror-markk-token", - "symbol": "markk", - "name": "Mirror mARKK Token" - }, - { "id": "mirror-mdot-token", "symbol": "mdot", "name": "Mirror mDOT Token" }, - { "id": "mirror-protocol", "symbol": "mir", "name": "Mirror Protocol" }, - { "id": "mirror-world-token", "symbol": "mw", "name": "Mirror World Token" }, - { "id": "misbloc", "symbol": "msb", "name": "Misbloc" }, - { "id": "mishka", "symbol": "msk", "name": "Mishka" }, - { "id": "missedeverything", "symbol": "ME", "name": "MissedEverything" }, - { "id": "mission-helios", "symbol": "helios", "name": "Mission Helios" }, - { "id": "mist", "symbol": "mist", "name": "Mist" }, - { "id": "mistel-finance", "symbol": "mistel", "name": "Mistel Finance" }, - { "id": "mistswap", "symbol": "mist", "name": "MistSwap" }, - { "id": "misty-inu", "symbol": "misty", "name": "Misty Inu" }, - { "id": "mithril", "symbol": "mith", "name": "Mithril" }, - { "id": "mithril-share", "symbol": "mis", "name": "Mithril Share" }, - { "id": "mithrilverse", "symbol": "mithril", "name": "Mithrilverse" }, - { "id": "mittens", "symbol": "mitten", "name": "Mittens" }, - { "id": "mixin", "symbol": "xin", "name": "Mixin" }, - { "id": "mixmarvel", "symbol": "mix", "name": "MixMarvel" }, - { "id": "mixsome", "symbol": "some", "name": "Mixsome" }, - { "id": "mixtrust", "symbol": "mxt", "name": "MixTrust" }, - { "id": "mixty-finance", "symbol": "mxf", "name": "Mixty Finance" }, - { "id": "miyazaki-inu", "symbol": "miyazaki", "name": "Miyazaki Inu" }, - { "id": "mizar", "symbol": "mzr", "name": "Mizar" }, - { "id": "mjackswap", "symbol": "mjack", "name": "MjackSwap" }, - { "id": "mjewel", "symbol": "mjewel", "name": "MJewel" }, - { "id": "mkitty", "symbol": "mkitty", "name": "mKitty" }, - { - "id": "mkreth-1x-short", - "symbol": "mkrethdoom", - "name": "MKRETH 1x Short" - }, - { "id": "mktcash", "symbol": "mch", "name": "Mktcash" }, - { "id": "mktcoin", "symbol": "mlm", "name": "MktCoin" }, - { "id": "mloky", "symbol": "mloky", "name": "MLOKY" }, - { "id": "mmacoin", "symbol": "MMA", "name": "MMACoin" }, - { "id": "mma-gaming", "symbol": "mma", "name": "MMA Gaming" }, - { "id": "mmaon", "symbol": "mmaon", "name": "MMAON" }, - { "id": "mmfinance", "symbol": "mmf", "name": "MMFinance" }, - { "id": "mmmluckup7", "symbol": "mmm7", "name": "MMMLUCKUP7" }, - { "id": "mmocoin", "symbol": "mmo", "name": "MMOCoin" }, - { "id": "mms-cash", "symbol": "mcash", "name": "MMS Cash" }, - { "id": "mms-coin", "symbol": "mmsc", "name": "MMS Coin" }, - { "id": "mm-token", "symbol": "mm", "name": "MM Token" }, - { "id": "mnmcoin", "symbol": "mnmc", "name": "MNMCoin" }, - { "id": "mnpostree", "symbol": "mptc", "name": "MNPoSTree" }, - { "id": "mo", "symbol": "mo", "name": "MO" }, - { "id": "moar", "symbol": "moar", "name": "Moar Finance" }, - { "id": "mobiecoin", "symbol": "mbx", "name": "MobieCoin" }, - { "id": "mobifi", "symbol": "mofi", "name": "MobiFi" }, - { "id": "mobilecoin", "symbol": "mob", "name": "MobileCoin" }, - { - "id": "mobile-crypto-pay-coin", - "symbol": "mcpc", - "name": "Mobile Crypto Pay Coin" - }, - { "id": "mobilego", "symbol": "mgo", "name": "MobileGo" }, - { "id": "mobilian-coin", "symbol": "mbn", "name": "Mobilian Coin" }, - { "id": "mobilink-coin", "symbol": "molk", "name": "MobilinkToken" }, - { "id": "mobility-coin", "symbol": "mobic", "name": "Mobility Coin" }, - { "id": "mob-inu", "symbol": "mob", "name": "Mob Inu" }, - { "id": "mobist", "symbol": "mitx", "name": "Mobist" }, - { "id": "mobit-global", "symbol": "mbgl", "name": "Mobit Global" }, - { "id": "mobius", "symbol": "mobi", "name": "Mobius" }, - { "id": "mobius-finance", "symbol": "mot", "name": "Mobius Finance" }, - { "id": "mobius-money", "symbol": "mobi", "name": "Mobius Money" }, - { "id": "mobox", "symbol": "mbox", "name": "Mobox" }, - { "id": "mobula", "symbol": "mobl", "name": "Mobula" }, - { "id": "moby-dick", "symbol": "wot", "name": "Moby Dick [OLD]" }, - { "id": "moby-dick-2", "symbol": "moby", "name": "Moby Dick" }, - { "id": "mochi-inu", "symbol": "mochi", "name": "Mochi Inu" }, - { "id": "mochi-market", "symbol": "moma", "name": "Mochi Market" }, - { "id": "mochimo", "symbol": "mcm", "name": "Mochimo" }, - { "id": "mocktailswap", "symbol": "mok", "name": "MocktailSwap" }, - { "id": "moda-dao", "symbol": "moda", "name": "MODA DAO" }, - { "id": "modefi", "symbol": "mod", "name": "Modefi" }, - { "id": "model-x-coin", "symbol": "modx", "name": "MODEL-X-coin" }, - { "id": "modex", "symbol": "modex", "name": "Modex" }, - { "id": "modihost", "symbol": "aim", "name": "ModiHost" }, - { "id": "moebius", "symbol": "mobi", "name": "Moebius" }, - { - "id": "moeda-loyalty-points", - "symbol": "mda", - "name": "Moeda Loyalty Points" - }, - { "id": "mogu", "symbol": "mogx", "name": "Mogu" }, - { "id": "mogul-productions", "symbol": "stars", "name": "Mogul Productions" }, - { "id": "mojitoswap", "symbol": "mjt", "name": "MojitoSwap" }, - { "id": "mojocoin", "symbol": "mojo", "name": "Mojocoin" }, - { "id": "mojo-v2", "symbol": "MOJOV2", "name": "Mojo V2" }, - { "id": "molecular-future", "symbol": "mof", "name": "Molecular Future" }, - { "id": "mollector", "symbol": "mol", "name": "Mollector" }, - { "id": "moma-protocol", "symbol": "momat", "name": "Moma Protocol" }, - { "id": "momento", "symbol": "momento", "name": "Momento" }, - { "id": "moments", "symbol": "mmt", "name": "Moments Market" }, - { "id": "momentum", "symbol": "XMM", "name": "Momentum" }, - { "id": "momentum-token", "symbol": "mtm", "name": "Momentum Token" }, - { "id": "mommon", "symbol": "mmon", "name": "Mammon" }, - { "id": "mommy-doge", "symbol": "mommydoge", "name": "Mommy Doge" }, - { "id": "mommyusdt", "symbol": "mommyusdt", "name": "MommyUSDT" }, - { "id": "momo-key", "symbol": "key", "name": "MoMo Key" }, - { "id": "momo-protocol", "symbol": "momo", "name": "Momo Protocol" }, - { "id": "momoshiba", "symbol": "momo", "name": "MomoShiba" }, - { "id": "mona", "symbol": "mona", "name": "Monaco Planet" }, - { "id": "monaco", "symbol": "mco", "name": "MCO" }, - { "id": "monacoin", "symbol": "mona", "name": "MonaCoin" }, - { "id": "monastery-finance", "symbol": "monk", "name": "Monastery Finance" }, - { "id": "monavale", "symbol": "mona", "name": "Monavale" }, - { "id": "mondayclub", "symbol": "monday", "name": "MondayClub" }, - { - "id": "mondo-community-coin", - "symbol": "mndcc", - "name": "Mondo Community Coin" - }, - { - "id": "monerium-eur-money", - "symbol": "eure", - "name": "Monerium EUR emoney" - }, - { "id": "monero", "symbol": "xmr", "name": "Monero" }, - { "id": "monero-classic-xmc", "symbol": "xmc", "name": "Monero-Classic" }, - { "id": "monerov", "symbol": "xmv", "name": "MoneroV" }, - { "id": "moneta", "symbol": "moneta", "name": "Moneta" }, - { "id": "monetaryunit", "symbol": "mue", "name": "MonetaryUnit" }, - { "id": "monetas", "symbol": "mntg", "name": "Monetas" }, - { "id": "monetha", "symbol": "mth", "name": "Monetha" }, - { "id": "moneybrain-bips", "symbol": "bips", "name": "Moneybrain BiPS" }, - { "id": "moneybyte", "symbol": "mon", "name": "MoneyByte" }, - { "id": "moneydefiswap", "symbol": "msd", "name": "MoneydefiSwap" }, - { "id": "moneyhero", "symbol": "myh", "name": "Moneyhero" }, - { - "id": "moneyrain-finance", - "symbol": "moneyrain", - "name": "Moneyrain Finance" - }, - { "id": "moneyswap", "symbol": "mswap", "name": "MoneySwap" }, - { "id": "moneytoken", "symbol": "imt", "name": "MoneyToken" }, - { "id": "moneytree", "symbol": "money", "name": "MoneyTree" }, - { "id": "monfter", "symbol": "mon", "name": "Monfter" }, - { "id": "mongo-coin", "symbol": "mongocm", "name": "MONGO Coin" }, - { "id": "mongol-nft", "symbol": "mnft", "name": "Mongol NFT" }, - { "id": "mongoose", "symbol": "mongoose", "name": "Mongoose" }, - { "id": "mongoosecoin", "symbol": "mongoose", "name": "MongooseCoin" }, - { "id": "moniwar", "symbol": "mowa", "name": "Moniwar" }, - { "id": "monk", "symbol": "monk", "name": "Monk" }, - { "id": "monkeyball", "symbol": "mbs", "name": "MonkeyLeague" }, - { "id": "monkey-claus-game", "symbol": "MCG", "name": "Monkey Claus Game" }, - { "id": "monnfts", "symbol": "mon", "name": "MONNFTS" }, - { "id": "monnos", "symbol": "mns", "name": "Monnos" }, - { "id": "monomoney", "symbol": "mono", "name": "MonoMoney" }, - { "id": "mononoke-inu", "symbol": "Mononoke-Inu", "name": "Mononoke Inu" }, - { - "id": "monopoly-millionaire-game", - "symbol": "MMG", - "name": "Monopoly Millionaire Game" - }, - { "id": "monox", "symbol": "mono", "name": "MonoX" }, - { "id": "monsoon-finance", "symbol": "mcash", "name": "Monsoon Finance" }, - { "id": "monspac", "symbol": "mspc", "name": "Monspac" }, - { "id": "monsta-infinite", "symbol": "moni", "name": "Monsta Infinite" }, - { "id": "monstaverse", "symbol": "monstr", "name": "MonstaVerse" }, - { - "id": "monster-adventure-token", - "symbol": "mat", - "name": "Monster Adventure Token" - }, - { "id": "monster-battle", "symbol": "mbs", "name": "Monster Battle" }, - { - "id": "monster-cash-share", - "symbol": "mss", - "name": "Monster Slayer Share" - }, - { "id": "monster-galaxy", "symbol": "ggm", "name": "Monster Galaxy" }, - { - "id": "monster-grand-prix-token", - "symbol": "mgpx", - "name": "Monster Grand Prix Token" - }, - { "id": "monster-of-god", "symbol": "monx", "name": "Monster of God" }, - { "id": "monsterquest", "symbol": "mqst", "name": "MonsterQuest" }, - { "id": "monsterra", "symbol": "MSTR", "name": "Monsterra" }, - { "id": "monster-saga", "symbol": "mts", "name": "Monster Saga" }, - { "id": "monsters-clan", "symbol": "mons", "name": "Monsters Clan" }, - { "id": "monster-slayer", "symbol": "ms", "name": "Monster Slayer" }, - { "id": "monster-valley", "symbol": "monster", "name": "Monster Valley" }, - { "id": "moochii", "symbol": "moochii", "name": "Moochii" }, - { "id": "moola-celo-atoken", "symbol": "mcelo", "name": "Moola CELO AToken" }, - { - "id": "moola-celo-dollars", - "symbol": "mcusd", - "name": "Moola Celo Dollars" - }, - { "id": "moola-market", "symbol": "moo", "name": "Moola Market" }, - { "id": "moomonster", "symbol": "moo", "name": "MooMonster" }, - { "id": "moon", "symbol": "moon", "name": "r/CryptoCurrency Moons" }, - { "id": "moonai", "symbol": "mooi", "name": "Moonaï" }, - { "id": "moonarch", "symbol": "moonarch", "name": "Moonarch" }, - { "id": "moonbeam", "symbol": "glmr", "name": "Moonbeam" }, - { "id": "moonbeans", "symbol": "beans", "name": "MoonBeans" }, - { "id": "moonbear-finance", "symbol": "mbf", "name": "MoonBear.Finance" }, - { "id": "moonbeers", "symbol": "beers", "name": "MoonBeers" }, - { "id": "moonbet", "symbol": "mbet", "name": "MoonBet" }, - { "id": "moonbird", "symbol": "mbird", "name": "Moonbird" }, - { "id": "moonboobs", "symbol": "boobs", "name": "MoonBoobs" }, - { "id": "mooncake", "symbol": "moon", "name": "MoonCake" }, - { - "id": "mooncat-vault-nftx", - "symbol": "mooncat", - "name": "MOONCAT Vault (NFTX)" - }, - { "id": "mooncoin", "symbol": "moon", "name": "Mooncoin" }, - { "id": "moondao-2", "symbol": "md", "name": "MoonDao" }, - { "id": "moonedge", "symbol": "mooned", "name": "MoonEdge" }, - { "id": "mooner", "symbol": "mnr", "name": "Mooner" }, - { "id": "moonery", "symbol": "mnry", "name": "Moonery" }, - { "id": "mooney", "symbol": "mooney", "name": "Moon DAO" }, - { "id": "moon-eye", "symbol": "me", "name": "Moon Eye" }, - { "id": "moonfarmer", "symbol": "mfm", "name": "MoonFarmer" }, - { "id": "moonfarm-finance", "symbol": "mfo", "name": "MoonFarm Finance" }, - { "id": "moonflower-farm", "symbol": "mff", "name": "Moonflower Farm" }, - { "id": "moonforce", "symbol": "force", "name": "MoonForce" }, - { "id": "moongame", "symbol": "mgt", "name": "Moongame" }, - { "id": "mooni", "symbol": "mooni", "name": "Mooni" }, - { "id": "moonienft", "symbol": "mny", "name": "MoonieNFT" }, - { "id": "moonka", "symbol": "mka", "name": "Moonka" }, - { "id": "moonkat-finance", "symbol": "mkat", "name": "MoonKat Finance" }, - { "id": "moonlana", "symbol": "mola", "name": "MoonLana" }, - { "id": "moonlift", "symbol": "mltpx", "name": "Moonlift Capital" }, - { "id": "moon-light-night", "symbol": "mlnt", "name": "Moon Light Night" }, - { "id": "moonlight-token", "symbol": "moonlight", "name": "Moonlight Token" }, - { - "id": "moon-maker-protocol", - "symbol": "mmp", - "name": "Moon Maker Protocol" - }, - { "id": "moonminer", "symbol": "moonminer", "name": "MoonMiner" }, - { "id": "moon-nation-game", "symbol": "mng", "name": "Moon Nation Game" }, - { "id": "moonpaw", "symbol": "moonpaw", "name": "MoonPaw" }, - { "id": "moonpoly", "symbol": "cmp", "name": "Moonpoly" }, - { "id": "moonpot", "symbol": "pots", "name": "Moonpot" }, - { "id": "moonpump", "symbol": "pump", "name": "MoonPump" }, - { "id": "moon-rabbit", "symbol": "aaa", "name": "Moon Rabbit" }, - { "id": "moonradar-finance", "symbol": "mrf", "name": "Moonradar Finance" }, - { "id": "moonraise", "symbol": "mrt", "name": "MoonRaise" }, - { "id": "moonretriever", "symbol": "FETCH", "name": "MoonRetriever" }, - { "id": "moonrise", "symbol": "moonrise", "name": "MoonRise" }, - { "id": "moonriver", "symbol": "movr", "name": "Moonriver" }, - { "id": "moonrock", "symbol": "rock", "name": "MoonRock" }, - { "id": "moon-rocket-coin", "symbol": "mrc", "name": "Moon Rocket Coin" }, - { "id": "moon-sack", "symbol": "sack", "name": "Moon Sack" }, - { "id": "moonscape", "symbol": "mscp", "name": "Moonscape" }, - { "id": "moonsdust", "symbol": "moond", "name": "MoonsDust" }, - { "id": "moonseeds", "symbol": "seeds", "name": "MoonSeeds" }, - { "id": "moonseer", "symbol": "moon", "name": "Moonseer" }, - { "id": "moonshot", "symbol": "moonshot", "name": "Moonshot" }, - { "id": "moonshot-max", "symbol": "msm", "name": "MoonShot Max" }, - { "id": "moonshots-farm", "symbol": "BONES", "name": "Moonshots Farm" }, - { "id": "moonshot-token", "symbol": "mshot", "name": "Moonshot Token" }, - { "id": "moonstar", "symbol": "moonstar", "name": "MoonStar" }, - { - "id": "moonstarevenge-token", - "symbol": "MTR", - "name": "MoonstaRevenge Token" - }, - { "id": "moonstarter", "symbol": "mnst", "name": "MoonStarter" }, - { "id": "moon-stop", "symbol": "mnstp", "name": "Moon Stop" }, - { "id": "moonswap", "symbol": "moon", "name": "MoonSwap" }, - { "id": "moontimer", "symbol": "mtg", "name": "MoonTimer" }, - { "id": "moontoken", "symbol": "moontoken", "name": "MoonToken" }, - { "id": "moon-token", "symbol": "moon", "name": "Moon" }, - { "id": "moontools", "symbol": "moons", "name": "MoonTools" }, - { "id": "moontrust", "symbol": "mntt", "name": "MoonTrust" }, - { "id": "moontrustbsc", "symbol": "mnttbsc", "name": "MoonTrustBSC" }, - { "id": "moon-warriors", "symbol": "mwar", "name": "Moon Warriors" }, - { "id": "moonway", "symbol": "moonway", "name": "MoonWay" }, - { "id": "moonwell", "symbol": "mfam", "name": "Moonwell Apollo" }, - { "id": "moonwilly", "symbol": "moonwilly", "name": "MoonWilly" }, - { "id": "moonwolf-io", "symbol": "wolf", "name": "moonwolf.io" }, - { "id": "morcilla-war", "symbol": "mor", "name": "Morcilla War" }, - { "id": "moremoney-usd", "symbol": "money", "name": "Moremoney USD" }, - { "id": "more-token", "symbol": "more", "name": "More Token" }, - { "id": "mork", "symbol": "mork", "name": "MORK" }, - { "id": "morpher", "symbol": "mph", "name": "Morpher" }, - { "id": "morpheus-labs", "symbol": "mitx", "name": "Morpheus Labs" }, - { "id": "morpheus-network", "symbol": "mnw", "name": "Morpheus Network" }, - { "id": "morpheus-token", "symbol": "pills", "name": "Morpheus Swap" }, - { "id": "morphie", "symbol": "mrfi", "name": "Morphie" }, - { "id": "morph-vault-nftx", "symbol": "morph", "name": "MORPH Vault (NFTX)" }, - { "id": "mor-stablecoin", "symbol": "mor", "name": "Mor Stablecoin" }, - { "id": "mortal-wars", "symbol": "mwt", "name": "Mortal Wars" }, - { "id": "moshiheads", "symbol": "moshi", "name": "Moshiheads" }, - { - "id": "moss-carbon-credit", - "symbol": "mco2", - "name": "Moss Carbon Credit" - }, - { "id": "mossland", "symbol": "moc", "name": "Mossland" }, - { "id": "mosterisland", "symbol": "mi", "name": "MosterIsland" }, - { "id": "mota", "symbol": "mgot", "name": "MoTA" }, - { "id": "motacoin", "symbol": "mota", "name": "MotaCoin" }, - { "id": "motel-crypto", "symbol": "motel", "name": "Motel Crypto" }, - { "id": "mother-earth", "symbol": "mot", "name": "Mother Earth" }, - { "id": "mother-of-memes", "symbol": "mom", "name": "Mother of Memes" }, - { "id": "motionwreck-games", "symbol": "mwg", "name": "MotionWreck Games" }, - { "id": "motiv-protocol", "symbol": "mov", "name": "MOTIV Protocol" }, - { "id": "motocoin", "symbol": "moto", "name": "Motocoin" }, - { "id": "mound-token", "symbol": "mnd", "name": "Mound Token" }, - { "id": "mouse", "symbol": "mouse", "name": "MouseMN" }, - { "id": "mousecoin", "symbol": "mic3", "name": "MOUSECOIN" }, - { "id": "mouse-haunt", "symbol": "mht", "name": "Mouse Haunt" }, - { "id": "mousepad", "symbol": "mpad", "name": "MousePad" }, - { "id": "movei", "symbol": "move", "name": "Movei" }, - { "id": "move-network", "symbol": "movd", "name": "MOVE Network" }, - { "id": "moviebloc", "symbol": "mbl", "name": "MovieBloc" }, - { "id": "movie-magic", "symbol": "mvm", "name": "Movie Magic" }, - { "id": "mozik", "symbol": "moz", "name": "Mozik" }, - { "id": "mozox", "symbol": "mozox", "name": "MozoX" }, - { "id": "mozza", "symbol": "mozza", "name": "Mozza" }, - { "id": "mp3", "symbol": "mp3", "name": "MP3" }, - { "id": "mp4", "symbol": "mp4", "name": "MP4" }, - { "id": "mr-fox-token", "symbol": "mrfox", "name": "Mr.FOX Token" }, - { "id": "mrv", "symbol": "mrv", "name": "MRV" }, - { "id": "mrweb-finance", "symbol": "ama", "name": "MrWeb Finance" }, - { "id": "msgsender", "symbol": "msg", "name": "MsgSender" }, - { "id": "mshare", "symbol": "mshare", "name": "MShare" }, - { "id": "msn", "symbol": "msn", "name": "MSN" }, - { "id": "msol", "symbol": "msol", "name": "Marinade staked SOL" }, - { "id": "mstation", "symbol": "mst", "name": "MStation" }, - { "id": "mti-finance", "symbol": "mti", "name": "MTI Finance" }, - { "id": "mt-pelerin-shares", "symbol": "mps", "name": "Mt Pelerin Shares" }, - { "id": "mttcoin", "symbol": "mttcoin", "name": "MTTCoin" }, - { "id": "mtvx", "symbol": "MTVX", "name": "MTVX" }, - { "id": "mu-coin", "symbol": "mu", "name": "Mu Coin" }, - { "id": "mu-continent", "symbol": "mu", "name": "Mu Continent" }, - { "id": "mu-dank", "symbol": "dank", "name": "MU DANK" }, - { "id": "mud-guild-game", "symbol": "mgg", "name": "MUD Guild Game" }, - { "id": "muesliswap-milk", "symbol": "milk", "name": "MuesliSwap MILK" }, - { - "id": "muesliswap-yield-token", - "symbol": "myield", - "name": "MuesliSwap Yield Token" - }, - { "id": "mu-gold", "symbol": "mug", "name": "Mu Gold" }, - { "id": "mulierum", "symbol": "diva", "name": "Mulierum" }, - { "id": "multichain", "symbol": "multi", "name": "Multichain" }, - { - "id": "multi-chain-capital", - "symbol": "mcc", - "name": "Multi-Chain Capital [OLD]" - }, - { - "id": "multi-chain-capital-2", - "symbol": "mcc", - "name": "Multi-Chain Capital" - }, - { "id": "multi-farm-capital", "symbol": "mfc", "name": "Multi-Farm Capital" }, - { "id": "multigame", "symbol": "multi", "name": "Multigame" }, - { "id": "multigencapital", "symbol": "mgc", "name": "MultiGenCapital" }, - { "id": "multipad", "symbol": "mpad", "name": "MultiPad" }, - { - "id": "multiplanetary-inus", - "symbol": "inus", - "name": "MultiPlanetary Inus" - }, - { "id": "multiplier", "symbol": "mxx", "name": "Multiplier" }, - { "id": "multiplier-bsc", "symbol": "bmxx", "name": "Multiplier (BSC)" }, - { - "id": "multi-stake-capital", - "symbol": "msc", - "name": "Multi-Stake Capital" - }, - { "id": "multistarter", "symbol": "mstart", "name": "MultiStarter" }, - { - "id": "multi-strategies-capital", - "symbol": "msc", - "name": "Multi Strategies Capital" - }, - { "id": "multivac", "symbol": "mtv", "name": "MultiVAC" }, - { "id": "multiverse", "symbol": "ai", "name": "Multiverse" }, - { "id": "multiverse-capital", "symbol": "mvc", "name": "Multiverse Capital" }, - { "id": "multiversepad", "symbol": "mtvp", "name": "MultiversePad" }, - { "id": "muna", "symbol": "muna", "name": "Muna" }, - { "id": "munch-token", "symbol": "munch", "name": "Munch Token" }, - { "id": "mundo-token", "symbol": "$mundo", "name": "MUNDO Token" }, - { "id": "muon-network", "symbol": "", "name": "Muon Network" }, - { "id": "murphycat", "symbol": "murphy", "name": "MURPHYCAT" }, - { "id": "mus", "symbol": "mus", "name": "Musashi Finance" }, - { "id": "musd", "symbol": "musd", "name": "mStable USD" }, - { "id": "muse-2", "symbol": "muse", "name": "Muse DAO" }, - { "id": "museo", "symbol": "mse", "name": "Museo" }, - { - "id": "museum-of-crypto-art", - "symbol": "moca", - "name": "Museum of Crypto Art" - }, - { "id": "mushroom-coin", "symbol": "coin", "name": "Mushroom Coin" }, - { "id": "mushu-finance", "symbol": "mushu", "name": "Mushu Finance" }, - { "id": "musk", "symbol": "musk", "name": "Musk" }, - { "id": "musk-doge", "symbol": "mkd", "name": "Musk Doge" }, - { "id": "musk-gold", "symbol": "musk", "name": "MUSK Gold" }, - { "id": "musk-ito", "symbol": "mito", "name": "Musk Ito" }, - { "id": "musk-melon", "symbol": "melon", "name": "Musk Melon" }, - { "id": "musk-metaverse", "symbol": "metamusk", "name": "Musk Metaverse" }, - { "id": "muskswap", "symbol": "musk", "name": "MuskSwap" }, - { "id": "muso-finance-2", "symbol": "muso", "name": "MUSO Finance" }, - { "id": "must", "symbol": "must", "name": "Must" }, - { "id": "mute", "symbol": "mute", "name": "Mute" }, - { "id": "muzible", "symbol": "muzz", "name": "Muzible" }, - { "id": "muzika-network", "symbol": "mzk", "name": "Muzika Network" }, - { "id": "mvp", "symbol": "mvp", "name": "MVP" }, - { "id": "mvpad", "symbol": "mvd", "name": "MvPad" }, - { "id": "mvp-coin", "symbol": "mvp", "name": "MVP Coin" }, - { "id": "mvs-multiverse", "symbol": "mvs", "name": "MVS Multiverse" }, - { "id": "mxc", "symbol": "mxc", "name": "MXC" }, - { "id": "mx-token", "symbol": "mx", "name": "MX Token" }, - { "id": "mybit-token", "symbol": "myb", "name": "MyBit Token" }, - { "id": "mybricks", "symbol": "bricks", "name": "MyBricks" }, - { "id": "myce", "symbol": "yce", "name": "MYCE" }, - { - "id": "my-ceremonial-event", - "symbol": "myce", - "name": "MY Ceremonial Event" - }, - { "id": "mycro-ico", "symbol": "myo", "name": "Mycro" }, - { "id": "my-defi-legends", "symbol": "dlegends", "name": "My DeFi Legends" }, - { "id": "my-defi-pet", "symbol": "dpet", "name": "My DeFi Pet" }, - { "id": "my-farm-pet", "symbol": "myfarmpet", "name": "My Farm Pet" }, - { "id": "my-identity-coin", "symbol": "myid", "name": "My Identity Coin" }, - { "id": "mykey", "symbol": "key", "name": "MYKEY" }, - { "id": "mykingdom", "symbol": "myk", "name": "MyKingdom" }, - { "id": "mylivn-coin", "symbol": "mlvc", "name": "Mylivn Coin" }, - { "id": "my-lotto-coin", "symbol": "myl", "name": "My Lotto Coin" }, - { "id": "my-master-war", "symbol": "mat", "name": "My Master War" }, - { "id": "mymessage", "symbol": "mesa", "name": "myMessage" }, - { "id": "mymine", "symbol": "mymine", "name": "MyMine" }, - { "id": "mymn", "symbol": "mymn", "name": "MyMN" }, - { "id": "my-neighbor-alice", "symbol": "alice", "name": "My Neighbor Alice" }, - { - "id": "my-neighbor-alice-wormhole", - "symbol": "alice", - "name": "My Neighbor Alice (Wormhole)" - }, - { "id": "myobu", "symbol": "myobu", "name": "Myōbu" }, - { "id": "my-pandaverse", "symbol": "PANDAVS", "name": "My Pandaverse" }, - { "id": "myra-ai", "symbol": "myra", "name": "Myra AI" }, - { "id": "myriadcoin", "symbol": "xmy", "name": "Myriad" }, - { "id": "myriad-social", "symbol": "myria", "name": "Myriad Social" }, - { "id": "my-shiba-academia", "symbol": "msa", "name": "My Shiba Academia" }, - { "id": "mysterium", "symbol": "myst", "name": "Mysterium" }, - { "id": "mystery", "symbol": "myst", "name": "MYSTERY" }, - { "id": "mystic-bets", "symbol": "mbt", "name": "MysticBets" }, - { "id": "mystic-warrior", "symbol": "mystic", "name": "Mystic Warrior" }, - { "id": "myswap-token", "symbol": "mst", "name": "MySwap" }, - { "id": "myteamcoin", "symbol": "myc", "name": "Myteamcoin" }, - { "id": "myteamfinance", "symbol": "myf", "name": "MyteamFinance" }, - { "id": "mytheria", "symbol": "myra", "name": "Mytheria" }, - { "id": "myth-token", "symbol": "myth", "name": "Myth Token" }, - { "id": "mytoken", "symbol": "mt", "name": "MyToken" }, - { "id": "mytrade", "symbol": "myt", "name": "Mytrade" }, - { "id": "mytvchain", "symbol": "mytv", "name": "MyTVchain" }, - { "id": "mywish", "symbol": "wish", "name": "MyWish" }, - { "id": "n1ce", "symbol": "n1ce", "name": "N1CE" }, - { "id": "nabox", "symbol": "nabox", "name": "Nabox" }, - { "id": "nacho-finance", "symbol": "nacho", "name": "Nacho Finance" }, - { "id": "nadeshiko", "symbol": "ndsk", "name": "Nadeshiko" }, - { "id": "naffiti", "symbol": "naff", "name": "Naffiti" }, - { "id": "nafter", "symbol": "naft", "name": "Nafter" }, - { "id": "nafty", "symbol": "nafty", "name": "Nafty" }, - { "id": "naga", "symbol": "ngc", "name": "NAGA" }, - { "id": "naga-kingdom", "symbol": "naga", "name": "Naga Kingdom" }, - { "id": "nagaswap", "symbol": "bnw", "name": "NagaSwap" }, - { "id": "nahmii", "symbol": "nii", "name": "Nahmii" }, - { - "id": "naiad-water-investment-coin", - "symbol": "naiad", - "name": "Naiad Water Investment Coin" - }, - { "id": "naka-bodhi-token", "symbol": "nbot", "name": "Naka Bodhi Token" }, - { "id": "nakamoto-games", "symbol": "naka", "name": "Nakamoto Games" }, - { "id": "name-changing-token", "symbol": "nct", "name": "Name Change Token" }, - { "id": "namecoin", "symbol": "nmc", "name": "Namecoin" }, - { - "id": "nami-corporation-token", - "symbol": "nami", - "name": "Nami Corporation Token" - }, - { "id": "nami-inu", "symbol": "nami", "name": "Nami Inu" }, - { "id": "nanjcoin", "symbol": "nanj", "name": "NANJCOIN" }, - { "id": "nano", "symbol": "xno", "name": "Nano" }, - { - "id": "nano-bitcoin-token", - "symbol": "nbtc", - "name": "Nano Bitcoin Token" - }, - { "id": "nanobyte", "symbol": "nbt", "name": "NanoByte" }, - { "id": "nano-doge", "symbol": "nanodoge", "name": "Nano Doge Token" }, - { "id": "nano-dogecoin", "symbol": "indc", "name": "Nano Dogecoin" }, - { "id": "nanometer-bitcoin", "symbol": "nmbtc", "name": "NanoMeter Bitcoin" }, - { "id": "nantrade", "symbol": "nan", "name": "NanTrade" }, - { "id": "naos-busd", "symbol": "nbusd", "name": "NAOS BUSD" }, - { "id": "naos-finance", "symbol": "naos", "name": "NAOS Finance" }, - { "id": "naos-usd", "symbol": "nusd", "name": "NAOS USD" }, - { "id": "napoleon-x", "symbol": "npx", "name": "Napoleon X" }, - { "id": "napoli-fan-token", "symbol": "NAP", "name": "Napoli Fan Token" }, - { "id": "naraka-token", "symbol": "nt", "name": "Naraka" }, - { "id": "naruto-inu", "symbol": "NARUTO", "name": "Naruto Inu" }, - { "id": "nasa-doge", "symbol": "nasadoge", "name": "Nasa Doge" }, - { "id": "nasdacoin", "symbol": "nsd", "name": "Nasdacoin" }, - { "id": "nasdex-token", "symbol": "nsdx", "name": "NASDEX Token" }, - { "id": "nation3", "symbol": "nation", "name": "Nation3" }, - { - "id": "native-utility-token", - "symbol": "nut", - "name": "Native Utility Token" - }, - { - "id": "natural-farm-union-protocol", - "symbol": "nfup", - "name": "Natural Farm Union Protocol" - }, - { - "id": "natus-vincere-fan-token", - "symbol": "navi", - "name": "Natus Vincere Fan Token" - }, - { "id": "nausicaal-inu", "symbol": "nausicaa", "name": "Nausicaa-Inu" }, - { "id": "nav-coin", "symbol": "nav", "name": "Navcoin" }, - { "id": "navibration", "symbol": "navi", "name": "Navibration" }, - { "id": "navigator", "symbol": "nttc", "name": "Navigator" }, - { "id": "naxar", "symbol": "naxar", "name": "Naxar" }, - { "id": "nayuta-coin", "symbol": "nc", "name": "Nayuta Coin" }, - { "id": "ndau", "symbol": "ndau", "name": "Ndau" }, - { "id": "ndex", "symbol": "ndx", "name": "nDEX" }, - { "id": "ndn-link", "symbol": "ndn", "name": "NDN Link" }, - { "id": "near", "symbol": "near", "name": "NEAR Protocol" }, - { "id": "nearpad", "symbol": "pad", "name": "NearPad" }, - { "id": "neblio", "symbol": "nebl", "name": "Neblio" }, - { "id": "nebulas", "symbol": "nas", "name": "Nebulas" }, - { "id": "nebulatoken", "symbol": "nebula", "name": "NebulaToken" }, - { "id": "nectar-token", "symbol": "nec", "name": "Nectar" }, - { "id": "neetcoin", "symbol": "neet", "name": "Neetcoin" }, - { "id": "neet-finance", "symbol": "neet", "name": "NEET Finance" }, - { "id": "neeva-defi", "symbol": "nva", "name": "Neeva Defi" }, - { "id": "neftipedia", "symbol": "nft", "name": "NEFTiPEDiA" }, - { "id": "neighbourhoods", "symbol": "nht", "name": "Neighbourhoods" }, - { "id": "nekocoin", "symbol": "nekos", "name": "Nekocoin" }, - { "id": "neko-network", "symbol": "neko", "name": "Neko Network" }, - { "id": "nekonium", "symbol": "nuko", "name": "Nekonium" }, - { "id": "nelo-metaverse", "symbol": "nelo", "name": "NELO Metaverse" }, - { "id": "nem", "symbol": "xem", "name": "NEM" }, - { "id": "nemesis", "symbol": "NMS", "name": "Nemesis" }, - { "id": "nemesis-dao", "symbol": "nms", "name": "Nemesis DAO" }, - { - "id": "nemesis-wealth-projects-bsc", - "symbol": "nms", - "name": "Nemesis Wealth Projects BSC" - }, - { "id": "neo", "symbol": "neo", "name": "NEO" }, - { "id": "neofi", "symbol": "neofi", "name": "NeoFi" }, - { "id": "neo-holistic-coin", "symbol": "nhc", "name": "Neo Holistic Coin" }, - { "id": "neon-exchange", "symbol": "nex", "name": "Nash" }, - { "id": "neonomad-finance", "symbol": "nni", "name": "Neonomad Finance" }, - { "id": "neopin", "symbol": "npt", "name": "Neopin" }, - { "id": "neos-credits", "symbol": "ncr", "name": "Neos Credits" }, - { "id": "neoworld-cash", "symbol": "nash", "name": "NeoWorld Cash" }, - { "id": "neptune", "symbol": "NPTUN", "name": "Neptune" }, - { "id": "nerdy-inu", "symbol": "nerdy", "name": "Nerdy Inu" }, - { "id": "nerian-network", "symbol": "nerian", "name": "Nerian Network" }, - { "id": "nerva", "symbol": "xnv", "name": "Nerva" }, - { "id": "nerve-finance", "symbol": "nrv", "name": "Nerve Finance" }, - { "id": "nerveflux", "symbol": "nerve", "name": "NerveFlux" }, - { "id": "nervenetwork", "symbol": "nvt", "name": "NerveNetwork" }, - { "id": "nervos-network", "symbol": "ckb", "name": "Nervos Network" }, - { "id": "nerv-protocol", "symbol": "nrv", "name": "Nerv Protocol" }, - { "id": "nest", "symbol": "nest", "name": "Nest Protocol" }, - { "id": "nest-arcade", "symbol": "nesta", "name": "Nest Arcade" }, - { "id": "nest-egg", "symbol": "negg", "name": "Nest Egg" }, - { "id": "nestegg-coin", "symbol": "egg", "name": "NestEgg Coin" }, - { "id": "nesten", "symbol": "nit", "name": "Nesten" }, - { "id": "nestree", "symbol": "egg", "name": "Nestree" }, - { "id": "neta", "symbol": "neta", "name": "NETA" }, - { "id": "netbox-coin", "symbol": "nbx", "name": "Netbox Coin" }, - { "id": "netcoin", "symbol": "net", "name": "Netcoin" }, - { "id": "netcoincapital", "symbol": "ncc", "name": "Netcoincapital" }, - { - "id": "netflix-tokenized-stock-defichain", - "symbol": "dNFLX", - "name": "Netflix Tokenized Stock Defichain" - }, - { - "id": "netflix-tokenized-stock-zipmex", - "symbol": "nflx", - "name": "Netflix Tokenized Stock Zipmex" - }, - { "id": "nether", "symbol": "ntr", "name": "Nether" }, - { "id": "netkoin", "symbol": "ntk", "name": "Netkoin" }, - { "id": "netm", "symbol": "ntm", "name": "Netm" }, - { "id": "netswap", "symbol": "nett", "name": "Netswap" }, - { "id": "netvrk", "symbol": "ntvrk", "name": "Netvrk" }, - { "id": "neumark", "symbol": "neu", "name": "Neumark" }, - { "id": "neural-protocol", "symbol": "nrp", "name": "Neural Protocol" }, - { "id": "neurochain", "symbol": "ncc", "name": "NeuroChain" }, - { "id": "neuromorphic-io", "symbol": "nmp", "name": "Neuromorphic.io" }, - { "id": "neurotoken", "symbol": "ntk", "name": "Neurotoken" }, - { "id": "neutrino", "symbol": "usdn", "name": "Neutrino USD" }, - { - "id": "neutrino-system-base-token", - "symbol": "nsbt", - "name": "Neutrino System Base Token" - }, - { "id": "nevada", "symbol": "nevada", "name": "Nevada" }, - { "id": "neverdrop", "symbol": "nd", "name": "NeverDrop" }, - { "id": "newb-farm", "symbol": "newb", "name": "NewB.Farm" }, - { "id": "new-bitshares", "symbol": "nbs", "name": "New BitShares" }, - { "id": "newdex-token", "symbol": "dex", "name": "Newdex Token" }, - { - "id": "new-earth-order-money", - "symbol": "neom", - "name": "New Earth Order Money" - }, - { "id": "new-era", "symbol": "nec", "name": "New Era" }, - { - "id": "new-frontier-presents", - "symbol": "nfp", - "name": "New Frontier Presents" - }, - { "id": "newinu", "symbol": "newinu", "name": "Newinu" }, - { "id": "new-landbox", "symbol": "land", "name": "LandBox" }, - { "id": "newo-coin", "symbol": "newo", "name": "NEWO Coin" }, - { "id": "new-order", "symbol": "newo", "name": "New Order" }, - { "id": "new-origin", "symbol": "noc", "name": "New Origin" }, - { - "id": "new-paradigm-assets-solution", - "symbol": "npas", - "name": "New Paradigm Assets Solution" - }, - { "id": "new-power-coin", "symbol": "npw", "name": "New Power Coin" }, - { "id": "newscrypto-coin", "symbol": "nwc", "name": "Newscrypto Coin" }, - { "id": "newsolution", "symbol": "nst", "name": "Newsolution" }, - { "id": "newsolution-2-0", "symbol": "nste", "name": "NewSolution 2.0" }, - { - "id": "newton-coin-project", - "symbol": "ncp", - "name": "Newton Coin Project" - }, - { "id": "newton-project", "symbol": "new", "name": "Newton Project" }, - { - "id": "new-year-resolution", - "symbol": "nyr", - "name": "New Year Resolution" - }, - { "id": "new-year-token", "symbol": "NYT", "name": "New Year Token" }, - { "id": "newyorkcoin", "symbol": "nyc", "name": "NewYorkCoin" }, - { "id": "newyork-exchange", "symbol": "nye", "name": "NewYork Exchange" }, - { "id": "nexalt", "symbol": "xlt", "name": "Nexalt" }, - { "id": "nexdax", "symbol": "nt", "name": "NexDAX" }, - { "id": "nexo", "symbol": "nexo", "name": "NEXO" }, - { "id": "nextdao", "symbol": "nax", "name": "NextDAO" }, - { - "id": "next-defi-protocol", - "symbol": "nxdf", - "name": "NeXt-DeFi Protocol" - }, - { "id": "next-earth", "symbol": "nxtt", "name": "Next Earth" }, - { "id": "nextech-network", "symbol": "nx", "name": "Nxtech Network" }, - { "id": "nextexchange", "symbol": "next", "name": "NEXT" }, - { "id": "next-level", "symbol": "nxl", "name": "Next Level" }, - { "id": "next-token", "symbol": "NXT", "name": "Next Token" }, - { "id": "nextype-finance", "symbol": "nt", "name": "NEXTYPE Finance" }, - { "id": "nexum", "symbol": "nexm", "name": "Nexum" }, - { "id": "nexus", "symbol": "nxs", "name": "Nexus" }, - { - "id": "nexus-beth-token-share-representation", - "symbol": "nETH", - "name": "Nexus bETH token share representation" - }, - { - "id": "nexus-bluna-token-share-representation", - "symbol": "nLuna", - "name": "Nexus bLuna token share representation" - }, - { "id": "nexus-dubai", "symbol": "nxd", "name": "Nexus Dubai" }, - { "id": "nexus-governance-token", "symbol": "psi", "name": "Nexus Protocol" }, - { - "id": "nexus-mutual-wormhole", - "symbol": "nxm", - "name": "Nexus Mutual (Wormhole)" - }, - { "id": "nexus-token", "symbol": "nexus", "name": "Nexus Token" }, - { "id": "nezuko-inu", "symbol": "nezuko", "name": "Nezuko Inu" }, - { "id": "nfans", "symbol": "nfs", "name": "Nfans" }, - { "id": "nfmonsters", "symbol": "nfmon", "name": "NFMonsters" }, - { "id": "nfraction", "symbol": "nfta", "name": "NFracTion" }, - { "id": "nft11", "symbol": "nft11", "name": "NFT11" }, - { "id": "nfta", "symbol": "nfta", "name": "NFTA" }, - { - "id": "nft-all-best-ico", - "symbol": "nftallbi", - "name": "NFT All Best ICO" - }, - { "id": "nft-alley", "symbol": "alley", "name": "NFT Alley" }, - { "id": "nft-art-finance", "symbol": "nftart", "name": "NFT Art Finance" }, - { "id": "nftascii", "symbol": "nftascii", "name": "NFTASCII" }, - { "id": "nftb", "symbol": "nftb", "name": "NFTb" }, - { "id": "nftblackmarket", "symbol": "nbm", "name": "NFTBlackmarket" }, - { "id": "nftbomb", "symbol": "nbp", "name": "NFTBomb" }, - { "id": "nftbooks", "symbol": "nftbs", "name": "NFTBooks" }, - { "id": "nft-champions", "symbol": "champ", "name": "NFT Champions" }, - { "id": "nftcircle", "symbol": "nftc", "name": "NFTCircle" }, - { "id": "nftdao", "symbol": "NAO", "name": "NFTDAO" }, - { "id": "nfteez", "symbol": "nfteez", "name": "NFTeez" }, - { "id": "nfteyez", "symbol": "eye", "name": "NftEyez" }, - { "id": "nftfundart", "symbol": "nfa", "name": "NFTFundArt" }, - { "id": "nftfy", "symbol": "nftfy", "name": "Nftfy" }, - { "id": "nftgamingstars", "symbol": "gs1", "name": "NFTGamingStars" }, - { - "id": "nft-global-platform", - "symbol": "nftg", - "name": "NFT Global Platform" - }, - { "id": "nftify", "symbol": "n1", "name": "NFTify" }, - { "id": "nftime", "symbol": "nftm", "name": "Nftime" }, - { "id": "nftinder", "symbol": "nftndr", "name": "NFTinder" }, - { "id": "nft-index", "symbol": "nfti", "name": "NFT Index" }, - { "id": "nftlaunch", "symbol": "nftl", "name": "NFTLaunch" }, - { "id": "nftlegends", "symbol": "nfl", "name": "NFTLegends" }, - { "id": "nftlootbox", "symbol": "loot", "name": "LootBox.io" }, - { "id": "nftl-token", "symbol": "nftl", "name": "NFTL Token" }, - { "id": "nftmake", "symbol": "make", "name": "NFTMAKE" }, - { "id": "nftmall", "symbol": "gem", "name": "NFTmall" }, - { "id": "nftmania", "symbol": "mania", "name": "NFTMania" }, - { "id": "nftmart-token", "symbol": "nmt", "name": "NFTMart Token" }, - { "id": "nft-maze", "symbol": "MAZE", "name": "NFT MAZE" }, - { "id": "nftmusic", "symbol": "music", "name": "NFTMusic.ai" }, - { "id": "nftmusic-stream", "symbol": "streamer", "name": "NFTMUSIC.STREAM" }, - { "id": "nftopia", "symbol": "nftopia", "name": "NFTOPIA" }, - { "id": "nftpad", "symbol": "nftpad", "name": "NFTPad" }, - { - "id": "nft-platform-index", - "symbol": "NFTP", - "name": "NFT Platform Index" - }, - { "id": "nft-protocol", "symbol": "nft", "name": "NFT Protocol" }, - { "id": "nftpunk", "symbol": "nftpunk2.0", "name": "NFTPunk" }, - { "id": "nftpunk-finance", "symbol": "nftpunk", "name": "NFTPunk.Finance" }, - { "id": "nftrade", "symbol": "nftd", "name": "NFTrade" }, - { "id": "nft-royal-token", "symbol": "nrt", "name": "NFT Royal Token" }, - { "id": "nft-soccer-games", "symbol": "nfsg", "name": "NFT Soccer Games" }, - { "id": "nftsocial", "symbol": "nsc", "name": "NFTSocial" }, - { "id": "nft-solpad", "symbol": "nftsol", "name": "NFT SolPad" }, - { "id": "nft-stars", "symbol": "nfts", "name": "NFT Stars" }, - { "id": "nft-starter", "symbol": "nst", "name": "NFT Starter" }, - { "id": "nftstyle", "symbol": "nftstyle", "name": "NFTStyle" }, - { "id": "nftswaps", "symbol": "swaps", "name": "NFTSwaps" }, - { "id": "nft-tech", "symbol": "nftt", "name": "NFT Tech" }, - { "id": "nft-tone", "symbol": "tone", "name": "NFT Tone" }, - { "id": "nft-wars", "symbol": "war", "name": "NFT Wars" }, - { "id": "nftwiki", "symbol": "nftk", "name": "NFTWiki" }, - { "id": "nft-worlds", "symbol": "wrld", "name": "NFT Worlds" }, - { "id": "nftx", "symbol": "nftx", "name": "NFTX" }, - { "id": "nfty-token", "symbol": "nfty", "name": "NFTY Token" }, - { "id": "nfx-coin", "symbol": "nfxc", "name": "NFX Coin" }, - { "id": "ngin", "symbol": "ng", "name": "Ngin" }, - { "id": "nhbtc", "symbol": "nhbtc", "name": "nHBTC" }, - { "id": "nice", "symbol": "nice", "name": "Nice" }, - { "id": "nicheman", "symbol": "nicheman", "name": "Nicheman" }, - { "id": "nickel-token", "symbol": "nickel", "name": "Nickel Token" }, - { "id": "nidhi-dao", "symbol": "guru", "name": "Nidhi Dao" }, - { "id": "nido-invest-dao", "symbol": "nido", "name": "NIDO Invest Dao" }, - { "id": "niftify", "symbol": "nift", "name": "Niftify" }, - { "id": "niftsy", "symbol": "niftsy", "name": "Envelop (Niftsy)" }, - { "id": "nifty-league", "symbol": "nftl", "name": "Nifty League" }, - { "id": "niftypays", "symbol": "nifty", "name": "NiftyPays" }, - { "id": "nifty-token", "symbol": "nfty", "name": "NFTY DeFi Protocol" }, - { "id": "night-life-crypto", "symbol": "nlife", "name": "Night Life Crypto" }, - { "id": "niifi", "symbol": "niifi", "name": "NiiFi" }, - { "id": "nil-coin", "symbol": "nil", "name": "NIL" }, - { "id": "nil-dao", "symbol": "nil", "name": "Nil DAO" }, - { "id": "nilu", "symbol": "nilu", "name": "Nilu" }, - { "id": "nimbus", "symbol": "nbu", "name": "Nimbus" }, - { - "id": "nimbus-governance-token", - "symbol": "gnbu", - "name": "Nimbus Governance Token" - }, - { "id": "nimiq-2", "symbol": "nim", "name": "Nimiq" }, - { "id": "ninja-doge", "symbol": "$ninjadoge", "name": "Ninja Doge" }, - { - "id": "ninja-fantasy-token", - "symbol": "nfs", - "name": "Ninja Fantasy Token" - }, - { "id": "ninja-panda-inu", "symbol": "npi", "name": "Ninja Panda Inu" }, - { "id": "ninja-protocol", "symbol": "ninja", "name": "Ninja Protocol" }, - { "id": "ninja-squad", "symbol": "nst", "name": "Ninja Squad" }, - { "id": "ninky", "symbol": "ninky", "name": "Idle Ninja Online" }, - { "id": "ninneko", "symbol": "nino", "name": "Ninneko" }, - { "id": "nintia-estate", "symbol": "ninti", "name": "Nintia Estate" }, - { "id": "niob", "symbol": "niob", "name": "NIOB" }, - { "id": "niobio-cash", "symbol": "nbr", "name": "Niobio" }, - { "id": "niobium-coin", "symbol": "nbc", "name": "Niobium Coin" }, - { "id": "niros", "symbol": "niros", "name": "Niros" }, - { "id": "nirvana", "symbol": "vana", "name": "Nirvana" }, - { "id": "nirvana-ana", "symbol": "ana", "name": "Nirvana ANA" }, - { "id": "nirvanameta", "symbol": "mnu", "name": "NirvanaMeta" }, - { "id": "nirvana-nirv", "symbol": "nirv", "name": "Nirvana NIRV" }, - { "id": "nitroex", "symbol": "ntx", "name": "NitroEX" }, - { "id": "nitro-league", "symbol": "nitro", "name": "Nitro League" }, - { "id": "nix-bridge-token", "symbol": "voice", "name": "Voice Token" }, - { "id": "nkcl-classic", "symbol": "nkclc", "name": "NKCL Classic" }, - { "id": "nkn", "symbol": "nkn", "name": "NKN" }, - { "id": "nnb-token", "symbol": "nnb", "name": "NNB Token" }, - { "id": "noah", "symbol": "noah", "name": "Noah" }, - { "id": "noahark", "symbol": "nrk", "name": "NoahArk" }, - { - "id": "noah-coin", - "symbol": "noahp", - "name": "Noah Decentralized State Coin" - }, - { "id": "noa-play", "symbol": "noa", "name": "NOA PLAY" }, - { "id": "nobelium", "symbol": "nobel", "name": "Nobelium" }, - { "id": "nobility", "symbol": "nbl", "name": "Nobility" }, - { "id": "noblecoin", "symbol": "nobl", "name": "NobleCoin" }, - { "id": "nobo-finance", "symbol": "nobf", "name": "Nobo Finance" }, - { "id": "no-bull", "symbol": "NB", "name": "No Bull" }, - { "id": "nobunaga", "symbol": "nbng", "name": "Nobunaga" }, - { "id": "nocapcoin", "symbol": "ncc", "name": "NoCapCoin" }, - { - "id": "node-aggregator-capital", - "symbol": "$NODAC", - "name": "Node Aggregator Capital" - }, - { "id": "nodebunch", "symbol": "noch", "name": "NodeBunch" }, - { "id": "node-compiler-avax", "symbol": "nodec", "name": "Piler" }, - { "id": "node-cubed", "symbol": "n3", "name": "Node Cubed" }, - { "id": "nodeify", "symbol": "ndfi", "name": "Nodeify" }, - { "id": "noderunners", "symbol": "ndr", "name": "Node Runners" }, - { "id": "nodeseeds", "symbol": "nds", "name": "Nodeseeds" }, - { "id": "node-squared", "symbol": "n2", "name": "Node Squared" }, - { "id": "nodestats", "symbol": "ns", "name": "Nodestats" }, - { "id": "nodetrade", "symbol": "mnx", "name": "Nodetrade" }, - { "id": "noel-capital", "symbol": "noel", "name": "Noel Capital" }, - { "id": "no-face-inu", "symbol": "NOFACE", "name": "No Face Inu" }, - { "id": "noften", "symbol": "nof", "name": "nOFTEN" }, - { "id": "nogoaltoken", "symbol": "ino", "name": "NoGoalToken" }, - { "id": "noia-network", "symbol": "noia", "name": "Syntropy" }, - { "id": "nokencoin", "symbol": "nokn", "name": "Nokencoin" }, - { "id": "noku", "symbol": "noku", "name": "Noku" }, - { "id": "nole-npc", "symbol": "npc", "name": "NPC DAO" }, - { "id": "nolewater", "symbol": "amsk", "name": "NoleWater" }, - { "id": "nolian-credits", "symbol": "nlcr", "name": "Nolian Credits" }, - { "id": "no-limit-ape", "symbol": "nla", "name": "No Limit Ape" }, - { "id": "nolimitcoin", "symbol": "nlc2", "name": "NoLimitCoin" }, - { "id": "nomad-exiles", "symbol": "pride", "name": "Nomad Exiles" }, - { "id": "nominex", "symbol": "nmx", "name": "Nominex" }, - { "id": "nomy", "symbol": "nomy", "name": "NOMY" }, - { "id": "non-fungible-toke", "symbol": "toke", "name": "Non-Fungible TOKE" }, - { "id": "non-fungible-yearn", "symbol": "nfy", "name": "Non-Fungible Yearn" }, - { "id": "nooft", "symbol": "nooft", "name": "Nooft" }, - { "id": "no-one", "symbol": "noone", "name": "No One" }, - { "id": "nora-token", "symbol": "nra", "name": "Nora Token" }, - { "id": "nord-finance", "symbol": "nord", "name": "Nord Finance" }, - { "id": "north", "symbol": "north", "name": "North (North Pole)" }, - { "id": "nosana", "symbol": "nos", "name": "Nosana" }, - { "id": "noshit", "symbol": "nsh", "name": "NoShit" }, - { "id": "nosta", "symbol": "nosta", "name": "Nosta" }, - { "id": "nosturis", "symbol": "ntrs", "name": "Nosturis" }, - { "id": "nota", "symbol": "usnota", "name": "NOTA" }, - { "id": "notable", "symbol": "nbl", "name": "Notable" }, - { - "id": "not-another-shit-altcoin", - "symbol": "nasa", - "name": "Not Another Shit Altcoin" - }, - { "id": "note-blockchain", "symbol": "ntbc", "name": "Note Blockchain" }, - { "id": "nothing", "symbol": "nada", "name": "Nothing" }, - { "id": "notional-finance", "symbol": "note", "name": "Notional Finance" }, - { "id": "not-much", "symbol": "nm", "name": "Not Much" }, - { "id": "notsafemoon", "symbol": "notsafemoon", "name": "NotSafeMoon" }, - { "id": "nova", "symbol": "nova", "name": "NOVA" }, - { "id": "novacoin", "symbol": "nvc", "name": "Novacoin" }, - { "id": "nova-finance", "symbol": "nova", "name": "Nova Finance" }, - { "id": "nova-network", "symbol": "nbk", "name": "Nova Token" }, - { - "id": "novara-calcio-fan-token", - "symbol": "nov", - "name": "Novara Calcio Fan Token" - }, - { "id": "novaxcrystal", "symbol": "xcrs", "name": "NovaXCrystal" }, - { "id": "novaxmetal", "symbol": "xmtl", "name": "NovaXMetal" }, - { "id": "novaxsolar", "symbol": "xslr", "name": "NovaXSolar" }, - { "id": "novem-gold", "symbol": "nnn", "name": "Novem Gold" }, - { "id": "novem-pro", "symbol": "nvm", "name": "Novem Pro" }, - { "id": "novo-token", "symbol": "novo", "name": "Novo Token" }, - { "id": "nowlage-coin", "symbol": "nac", "name": "Nowlage Coin" }, - { "id": "npccoin", "symbol": "npc", "name": "NPCcoin" }, - { "id": "npo-coin", "symbol": "npo", "name": "NPO Coin" }, - { "id": "nrgy-defi", "symbol": "nrgy", "name": "NRGY Defi" }, - { "id": "nshare", "symbol": "nshare", "name": "NSHARE" }, - { "id": "nsights", "symbol": "nsi", "name": "nSights" }, - { "id": "nss-coin", "symbol": "nss", "name": "NSS Coin" }, - { "id": "nsur-coin", "symbol": "nsur", "name": "NSUR Coin" }, - { "id": "nsure-network", "symbol": "nsure", "name": "Nsure Network" }, - { "id": "ntoken0031", "symbol": "n0031", "name": "nYFI" }, - { "id": "nubits", "symbol": "usnbt", "name": "NuBits" }, - { "id": "nucleus-vision", "symbol": "ncash", "name": "Nucleus Vision" }, - { "id": "nuco-cloud", "symbol": "ncdt", "name": "Nuco.Cloud" }, - { "id": "nucoin", "symbol": "nuc", "name": "NuCoin" }, - { "id": "nucypher", "symbol": "nu", "name": "NuCypher" }, - { "id": "nudes", "symbol": "nudes", "name": "NUDES" }, - { "id": "nuke-token", "symbol": "nuke", "name": "Nuke Token" }, - { "id": "nuls", "symbol": "nuls", "name": "Nuls" }, - { "id": "num-ars", "symbol": "nuars", "name": "Num ARS" }, - { "id": "number-1-token", "symbol": "nr1", "name": "Number 1 Token" }, - { "id": "numbers-protocol", "symbol": "num", "name": "Numbers Protocol" }, - { "id": "numeraire", "symbol": "nmr", "name": "Numeraire" }, - { "id": "numisme", "symbol": "nume", "name": "NumisMe" }, - { "id": "nuna", "symbol": "nuna", "name": "Nuna" }, - { "id": "nunet", "symbol": "ntx", "name": "NuNet" }, - { "id": "nunu-spirits", "symbol": "nnt", "name": "Nunu Spirits" }, - { "id": "nurifootball", "symbol": "nrfb", "name": "NuriFootBall" }, - { "id": "nusd", "symbol": "susd", "name": "sUSD" }, - { "id": "nusd-hotbit", "symbol": "nusd", "name": "nUSD (HotBit)" }, - { "id": "nushares", "symbol": "nsr", "name": "NuShares" }, - { "id": "nuts-gaming", "symbol": "nutsg", "name": "NUTS Gaming" }, - { "id": "nuvo-cash", "symbol": "nuvo", "name": "Nuvo Cash" }, - { - "id": "nvidia-tokenized-stock-defichain", - "symbol": "dnvda", - "name": "Nvidia Tokenized Stock Defichain" - }, - { "id": "nvirworld", "symbol": "nvir", "name": "NvirWorld" }, - { "id": "nvl-project", "symbol": "nvl", "name": "NVL Project" }, - { "id": "nxm", "symbol": "nxm", "name": "Nexus Mutual" }, - { "id": "nxt", "symbol": "nxt", "name": "NXT" }, - { "id": "nxusd", "symbol": "nxusd", "name": "NXUSD" }, - { "id": "nyan-cat", "symbol": "ncat", "name": "NCAT Token" }, - { - "id": "nyantereum", - "symbol": "nyante", - "name": "Nyantereum International" - }, - { "id": "nyan-v2", "symbol": "nyan-2", "name": "Nyan V2" }, - { "id": "nycccoin", "symbol": "nyc", "name": "NewYorkCityCoin" }, - { "id": "nyerium", "symbol": "nyex", "name": "Nyerium" }, - { "id": "nym", "symbol": "nym", "name": "Nym" }, - { "id": "nynja", "symbol": "nyn", "name": "NYNJACoin" }, - { "id": "nyx-token", "symbol": "nyxt", "name": "Nyx Token" }, - { "id": "nyzo", "symbol": "nyzo", "name": "Nyzo" }, - { "id": "nzd-stablecoin", "symbol": "nzds", "name": "NZD Stablecoin" }, - { "id": "o3-swap", "symbol": "o3", "name": "O3 Swap" }, - { "id": "oasis-2", "symbol": "xos", "name": "OASIS" }, - { "id": "oasis-city", "symbol": "osc", "name": "Oasis City" }, - { "id": "oasisdao", "symbol": "oas", "name": "OasisDAO" }, - { "id": "oasis-gold", "symbol": "og", "name": "Oasis Gold" }, - { "id": "oasis-network", "symbol": "rose", "name": "Oasis Network" }, - { "id": "oath", "symbol": "oath", "name": "Oath" }, - { "id": "obic", "symbol": "obic", "name": "OBIC" }, - { "id": "obortech", "symbol": "obot", "name": "Obortech" }, - { "id": "obrok", "symbol": "obrok", "name": "OBRok" }, - { "id": "observer-coin", "symbol": "obsr", "name": "OBSERVER Coin" }, - { "id": "obsidium", "symbol": "obs", "name": "Obsidium" }, - { "id": "obtoken", "symbol": "obt", "name": "OBToken" }, - { "id": "occamfi", "symbol": "occ", "name": "OccamFi" }, - { "id": "oceanex-token", "symbol": "oce", "name": "OceanEX Token" }, - { "id": "oceanland", "symbol": "oland", "name": "OceanLand" }, - { "id": "ocean-protocol", "symbol": "ocean", "name": "Ocean Protocol" }, - { "id": "oceidon-blox", "symbol": "oblox", "name": "Oceidon Blox" }, - { "id": "oc-protocol", "symbol": "ocp", "name": "OC Protocol" }, - { "id": "ocra", "symbol": "ocra", "name": "OCRA" }, - { "id": "octafarm", "symbol": "octf", "name": "Octafarm" }, - { - "id": "octane-protocol-token", - "symbol": "octane", - "name": "Octane Protocol Token" - }, - { "id": "octapay", "symbol": "octa", "name": "OctaPay" }, - { "id": "octaplex-network", "symbol": "plx", "name": "Octaplex Network" }, - { "id": "octaverse-games", "symbol": "ovg", "name": "Octaverse Games" }, - { "id": "octax", "symbol": "octax", "name": "OctaX" }, - { "id": "oction", "symbol": "octi", "name": "Oction" }, - { "id": "octocoin", "symbol": "888", "name": "Octocoin" }, - { "id": "octofi", "symbol": "octo", "name": "OctoFi" }, - { "id": "octopus-network", "symbol": "oct", "name": "Octopus Network" }, - { "id": "octopus-protocol", "symbol": "ops", "name": "Octopus Protocol" }, - { "id": "octree", "symbol": "oct", "name": "Octree" }, - { "id": "octree-finance", "symbol": "oak", "name": "Octree Finance" }, - { "id": "oculus-vision", "symbol": "OCV", "name": "Oculus Vision" }, - { "id": "oddz", "symbol": "oddz", "name": "Oddz" }, - { "id": "odem", "symbol": "ode", "name": "ODEM" }, - { "id": "odindao", "symbol": "odin", "name": "OdinDAO" }, - { "id": "odin-platform", "symbol": "odn", "name": "Odin Platform" }, - { "id": "odin-protocol", "symbol": "odin", "name": "Odin Protocol" }, - { "id": "odop", "symbol": "odop", "name": "oDOP" }, - { "id": "oduwa-coin", "symbol": "owc", "name": "Oduwa Coin" }, - { "id": "odyssey", "symbol": "ocn", "name": "Odyssey" }, - { "id": "oec-auction", "symbol": "auctionk", "name": "OEC AUCTION" }, - { "id": "oec-bch", "symbol": "bchk", "name": "OEC BCH" }, - { "id": "oec-binance-coin", "symbol": "BNB", "name": "OEC Binance Coin" }, - { "id": "oec-btc", "symbol": "btck", "name": "OEC BTC" }, - { "id": "oec-chainlink", "symbol": "linkk", "name": "OEC Chainlink" }, - { "id": "oec-dai", "symbol": "daik", "name": "OEC DAI" }, - { "id": "oec-dot", "symbol": "dotk", "name": "OEC DOT" }, - { "id": "oec-etc", "symbol": "etck", "name": "OEC ETC" }, - { "id": "oec-eth", "symbol": "ethk", "name": "OEC ETH" }, - { "id": "oec-fil", "symbol": "filk", "name": "OEC FIL" }, - { "id": "oec-kine", "symbol": "kinek", "name": "OEC KINE" }, - { "id": "oec-ltc", "symbol": "ltck", "name": "OEC LTC" }, - { "id": "oec-sfg", "symbol": "sfgk", "name": "OEC SFG" }, - { "id": "oec-shib", "symbol": "shibk", "name": "OEC SHIB" }, - { "id": "oec-sushi", "symbol": "sushik", "name": "OEC SUSHI" }, - { "id": "oec-token", "symbol": "okt", "name": "OEC Token" }, - { "id": "oec-tron", "symbol": "trxk", "name": "OEC Tron" }, - { "id": "oec-uni", "symbol": "unik", "name": "OEC UNI" }, - { "id": "oec-xrp", "symbol": "xrpk", "name": "OEC XRP" }, - { - "id": "official-crypto-cowboy-token", - "symbol": "occt", - "name": "Official Crypto Cowboy Token" - }, - { "id": "offline", "symbol": "off", "name": "Offline" }, - { "id": "offshift", "symbol": "xft", "name": "Offshift" }, - { "id": "ofi-cash", "symbol": "ofi", "name": "OFI.cash" }, - { "id": "ogcnode", "symbol": "org", "name": "Ogcnode" }, - { "id": "og-fan-token", "symbol": "og", "name": "OG Fan Token" }, - { "id": "oh-finance", "symbol": "oh", "name": "Oh! Finance" }, - { "id": "ohm-coin", "symbol": "ohmc", "name": "Ohmcoin" }, - { "id": "ohm-inu-dao", "symbol": "AVohminu", "name": "Ohm Inu DAO" }, - { "id": "oikos", "symbol": "oks", "name": "Oikos" }, - { "id": "oilage", "symbol": "oil", "name": "OILage" }, - { "id": "oiler", "symbol": "oil", "name": "Oiler" }, - { "id": "oin-finance", "symbol": "oin", "name": "OIN Finance" }, - { "id": "oink-token", "symbol": "oink", "name": "Oink Token" }, - { "id": "oiocoin", "symbol": "oioc", "name": "OIOCoin" }, - { "id": "ojamu", "symbol": "oja", "name": "Ojamu" }, - { "id": "oje-token", "symbol": "oje", "name": "Oje Token" }, - { "id": "okb", "symbol": "okb", "name": "OKB" }, - { "id": "okboomer", "symbol": "okboomer", "name": "OKBoomer" }, - { "id": "okcash", "symbol": "ok", "name": "OKCash" }, - { "id": "okex-fly", "symbol": "okfly", "name": "Okex Fly" }, - { "id": "ok-lets-go", "symbol": "oklg", "name": "ok.lets.go." }, - { "id": "okletsplay", "symbol": "oklp", "name": "OkLetsPlay" }, - { "id": "okratech-token", "symbol": "ort", "name": "Okratech Token" }, - { "id": "oktplay", "symbol": "oktp", "name": "OKTPlay" }, - { "id": "ola-city", "symbol": "ola", "name": "Ola City" }, - { "id": "olcf", "symbol": "olcf", "name": "OLCF" }, - { "id": "olea", "symbol": "OLE", "name": "Olea" }, - { "id": "olecoin", "symbol": "ole", "name": "OleCoin" }, - { "id": "olivecash", "symbol": "olive", "name": "Olive Cash" }, - { "id": "olympia", "symbol": "soo", "name": "Olympia" }, - { "id": "olympic-doge", "symbol": "olympic doge", "name": "Olympic Doge" }, - { "id": "olympus", "symbol": "ohm", "name": "Olympus" }, - { "id": "olympus-inu-dao", "symbol": "ohminu", "name": "Olympus Inu Dao" }, - { "id": "olympus-token", "symbol": "olympus", "name": "OLYMPUS token" }, - { "id": "olympus-v1", "symbol": "ohm", "name": "Olympus v1" }, - { "id": "olympus-wormhole", "symbol": "ohm", "name": "Olympus (Wormhole)" }, - { "id": "olyseum", "symbol": "oly", "name": "Olyseum" }, - { "id": "oly-sport", "symbol": "oly", "name": "Oly Sport" }, - { "id": "omax-token", "symbol": "omax", "name": "Omax Token" }, - { "id": "ombre", "symbol": "omb", "name": "Ombre" }, - { "id": "omega", "symbol": "omega", "name": "OMEGA" }, - { - "id": "omega-protocol-money", - "symbol": "opm", - "name": "Omega Protocol Money" - }, - { "id": "omicron", "symbol": "omic", "name": "Omicron" }, - { "id": "omisego", "symbol": "omg", "name": "OMG Network" }, - { "id": "omlira", "symbol": "oml", "name": "Omlira" }, - { "id": "omm-tokens", "symbol": "omm", "name": "Omm Tokens" }, - { "id": "omni", "symbol": "omni", "name": "Omni" }, - { "id": "omnia", "symbol": "omnia", "name": "Omnia" }, - { "id": "omni-cash", "symbol": "oca$h", "name": "Omni Cash" }, - { - "id": "omni-consumer-protocol", - "symbol": "ocp", - "name": "Omni Consumer Protocol" - }, - { "id": "omnidex", "symbol": "charm", "name": "OmniDex" }, - { "id": "omniflix-network", "symbol": "flix", "name": "OmniFlix Network" }, - { - "id": "omni-people-driven", - "symbol": "oai", - "name": "OMNI - People Driven" - }, - { - "id": "omni-real-estate-token", - "symbol": "ort", - "name": "Omni Real Estate Token" - }, - { "id": "omnis", "symbol": "omnis", "name": "OMNIS" }, - { "id": "omnitude", "symbol": "ecom", "name": "Omnitude" }, - { "id": "omniwhirl", "symbol": "whirl", "name": "OmniWhirl" }, - { "id": "omotenashicoin", "symbol": "mtns", "name": "OmotenashiCoin" }, - { "id": "onbuff", "symbol": "onit", "name": "ONBUFF" }, - { "id": "on-chain-capital", "symbol": "OCC", "name": "On Chain Capital" }, - { "id": "one", "symbol": "one", "name": "One" }, - { "id": "one-basis-cash", "symbol": "obs", "name": "One Basis Cash" }, - { "id": "onebit", "symbol": "1bit", "name": "OneBit" }, - { "id": "onebtc", "symbol": "onebtc", "name": "Legacy oneBTC" }, - { "id": "one-cash", "symbol": "onc", "name": "One Cash" }, - { "id": "onedao-finance", "symbol": "odao", "name": "OneDao Finance" }, - { "id": "one-dex", "symbol": "odex", "name": "One DEX" }, - { "id": "onefortune", "symbol": "bold", "name": "OneFortune" }, - { "id": "onefuse", "symbol": "onefuse", "name": "oneFUSE" }, - { "id": "onegetcoin", "symbol": "ogc", "name": "Onegetcoin" }, - { "id": "one-hundred-coin-2", "symbol": "one", "name": "One Hundred Coin" }, - { "id": "one-ledger", "symbol": "olt", "name": "OneLedger" }, - { "id": "onemoon", "symbol": "onemoon", "name": "OneMoon" }, - { "id": "oneperl", "symbol": "oneperl", "name": "onePERL" }, - { "id": "one-piece", "symbol": "onepiece", "name": "ONE PIECE" }, - { "id": "onerare", "symbol": "orare", "name": "OneRare" }, - { "id": "onering", "symbol": "ring", "name": "OneRing" }, - { "id": "oneroot-network", "symbol": "rnt", "name": "OneRoot Network" }, - { "id": "one-share", "symbol": "ons", "name": "One Share" }, - { "id": "oneswap-dao-token", "symbol": "ones", "name": "OneSwap DAO Token" }, - { "id": "oneto11", "symbol": "1to11", "name": "OneTo11" }, - { "id": "one-world-coin", "symbol": "owo", "name": "One World Coin" }, - { "id": "ong", "symbol": "ong", "name": "Ontology Gas" }, - { "id": "oni-exchange", "symbol": "oni", "name": "ONI Exchange" }, - { "id": "onigiri", "symbol": "onigiri", "name": "Onigiri" }, - { "id": "onion-mixer", "symbol": "omt", "name": "Onion Mixer" }, - { "id": "oni-token", "symbol": "oni", "name": "ONINO" }, - { "id": "onlexpa-token", "symbol": "onlexpa", "name": "onLEXpa Token" }, - { "id": "online-cold-wallet", "symbol": "ocw", "name": "Online Cold Wallet" }, - { "id": "online-expo", "symbol": "expo", "name": "Expo Token" }, - { "id": "only1", "symbol": "like", "name": "Only1" }, - { "id": "only-1-token", "symbol": "o1t", "name": "Only 1 Token" }, - { "id": "onomy-protocol", "symbol": "nom", "name": "Onomy Protocol" }, - { "id": "onooks", "symbol": "ooks", "name": "Onooks" }, - { "id": "onston", "symbol": "onston", "name": "Onston" }, - { "id": "ontology", "symbol": "ont", "name": "Ontology" }, - { "id": "onus", "symbol": "onus", "name": "ONUS" }, - { "id": "onx-finance", "symbol": "onx", "name": "OnX Finance" }, - { "id": "onyx", "symbol": "onyx", "name": "Onyx" }, - { "id": "oobit", "symbol": "obt", "name": "Oobit" }, - { "id": "o-ocean-mar22", "symbol": "o-ocean-mar22", "name": "O-OCEAN-MAR22" }, - { "id": "oogear", "symbol": "og", "name": "Oogear" }, - { "id": "oogi", "symbol": "oogi", "name": "OOGI" }, - { "id": "ooki", "symbol": "ooki", "name": "Ooki" }, - { "id": "oolongswap", "symbol": "olo", "name": "OolongSwap" }, - { "id": "oooor-finance", "symbol": "oooor", "name": "OOOOR Finance" }, - { "id": "opacity", "symbol": "opct", "name": "Opacity" }, - { "id": "opalcoin", "symbol": "auop", "name": "Opalcoin" }, - { "id": "op-coin", "symbol": "opc", "name": "OP Coin" }, - { "id": "openalexa-protocol", "symbol": "oap", "name": "OpenAlexa Protocol" }, - { "id": "openanx", "symbol": "oax", "name": "OAX" }, - { "id": "opendao", "symbol": "sos", "name": "OpenDAO" }, - { "id": "opendao-wormhole", "symbol": "sos", "name": "OpenDAO (Wormhole)" }, - { - "id": "open-data-protocol", - "symbol": "open", - "name": "Open Data Protocol" - }, - { - "id": "open-governance-token", - "symbol": "open", - "name": "OPEN Governance Token" - }, - { "id": "openlive-nft", "symbol": "opv", "name": "OpenLive NFT" }, - { "id": "opennity", "symbol": "opnn", "name": "Opennity" }, - { "id": "openocean", "symbol": "ooe", "name": "OpenOcean" }, - { "id": "open-platform", "symbol": "open", "name": "Open Platform" }, - { "id": "openstream-world", "symbol": "osw", "name": "OpenStream World" }, - { "id": "openswap", "symbol": "oswap", "name": "OpenSwap" }, - { "id": "openswap-token", "symbol": "openx", "name": "OpenSwap Token" }, - { "id": "operon-origins", "symbol": "oro", "name": "Operon Origins" }, - { "id": "opes-wrapped-pe", "symbol": "wpe", "name": "OPES (Wrapped PE)" }, - { "id": "opium", "symbol": "opium", "name": "Opium" }, - { "id": "oppa", "symbol": "oppa", "name": "OPPA" }, - { "id": "oppa-token", "symbol": "oppa", "name": "OPPA Token" }, - { "id": "opsya-insurance", "symbol": "opsy", "name": "Opsya Insurance" }, - { "id": "optimus", "symbol": "optcm", "name": "Optimus" }, - { "id": "optimuscat", "symbol": "opcat", "name": "OptimusCat" }, - { "id": "optimusrise", "symbol": "ore", "name": "OptimusRise" }, - { - "id": "option-panda-platform", - "symbol": "opa", - "name": "Option Panda Platform" - }, - { "id": "option-room", "symbol": "room", "name": "OptionRoom" }, - { - "id": "optionroom-governance-token", - "symbol": "court", - "name": "OptionRoom Governance Token" - }, - { "id": "options-market", "symbol": "osm", "name": "Options Market" }, - { "id": "optitoken", "symbol": "opti", "name": "Optitoken" }, - { "id": "opulous", "symbol": "opul", "name": "Opulous" }, - { "id": "opus", "symbol": "opt", "name": "Opus" }, - { "id": "opyn-squeeth", "symbol": "osqth", "name": "Opyn Squeeth" }, - { "id": "oraclechain", "symbol": "oct", "name": "OracleChain" }, - { "id": "oracle-system", "symbol": "orc", "name": "Oracle System" }, - { "id": "oracle-top-5", "symbol": "orcl5", "name": "Oracle Top 5 Index" }, - { "id": "oracolxor", "symbol": "xor", "name": "Oracolxor" }, - { "id": "oracula", "symbol": "oracula", "name": "Oracula" }, - { "id": "oragonx", "symbol": "orgn", "name": "OragonX" }, - { "id": "oraichain-token", "symbol": "orai", "name": "Oraichain Token" }, - { "id": "orakler", "symbol": "orkl", "name": "Orakler" }, - { "id": "orakuru", "symbol": "ork", "name": "Orakuru" }, - { "id": "orao-network", "symbol": "orao", "name": "ORAO Network" }, - { "id": "orbitau-taureum", "symbol": "taum", "name": "Orbitau Taureum" }, - { "id": "orbit-chain", "symbol": "orc", "name": "Orbit Chain" }, - { "id": "orbitcoin", "symbol": "orb", "name": "Orbitcoin" }, - { "id": "orbit-token", "symbol": "orbit", "name": "Orbit Token" }, - { "id": "orbler", "symbol": "orbr", "name": "Orbler" }, - { "id": "orbs", "symbol": "orbs", "name": "Orbs" }, - { "id": "orbyt-token", "symbol": "orbyt", "name": "ORBYT Token" }, - { "id": "orca", "symbol": "orca", "name": "Orca" }, - { "id": "orca-avai", "symbol": "avai", "name": "Orca AVAI" }, - { "id": "orcadao", "symbol": "orca", "name": "Orca DAO" }, - { "id": "orchid-protocol", "symbol": "oxt", "name": "Orchid Protocol" }, - { "id": "orclands-metaverse", "symbol": "orc", "name": "Orclands Metaverse" }, - { - "id": "order-of-the-apeverse", - "symbol": "OAV", - "name": "Order of the Apeverse" - }, - { "id": "orenda-protocol", "symbol": "orex", "name": "Orenda Protocol" }, - { "id": "oren-game", "symbol": "OREN", "name": "OREN Game" }, - { "id": "ore-token", "symbol": "ore", "name": "ORE Token" }, - { "id": "organix", "symbol": "ogx", "name": "Organix" }, - { "id": "orica", "symbol": "ori", "name": "Orica" }, - { "id": "orient-walt", "symbol": "htdf", "name": "Orient Walt" }, - { - "id": "original-crypto-coin", - "symbol": "tusc", - "name": "The Universal Settlement Coin" - }, - { - "id": "original-gangsta-shiba", - "symbol": "ogshib", - "name": "Original Gangsta Shiba" - }, - { "id": "origin-dollar", "symbol": "ousd", "name": "Origin Dollar" }, - { "id": "origin-protocol", "symbol": "ogn", "name": "Origin Protocol" }, - { "id": "origin-sport", "symbol": "ors", "name": "Origin Sport" }, - { "id": "origintrail", "symbol": "trac", "name": "OriginTrail" }, - { "id": "origo", "symbol": "ogo", "name": "Origo" }, - { "id": "orion-money", "symbol": "orion", "name": "Orion Money" }, - { "id": "orion-protocol", "symbol": "orn", "name": "Orion Protocol" }, - { "id": "orkan", "symbol": "ork", "name": "Orkan" }, - { "id": "orlycoin", "symbol": "orly", "name": "Orlycoin" }, - { "id": "ormeus-cash", "symbol": "omc", "name": "Ormeus Cash" }, - { "id": "ormeuscoin", "symbol": "orme", "name": "Ormeus Coin" }, - { "id": "ormeus-ecosystem", "symbol": "eco", "name": "Ormeus Ecosystem" }, - { "id": "orne", "symbol": "orne", "name": "Orne" }, - { "id": "oro", "symbol": "oro", "name": "ORO" }, - { "id": "oros-finance", "symbol": "oros", "name": "OROS.finance" }, - { "id": "orsgroup-io", "symbol": "ors", "name": "ORS Group" }, - { "id": "oshare", "symbol": "oshare", "name": "OShare" }, - { "id": "osk", "symbol": "osk", "name": "OSK" }, - { "id": "osmiumcoin", "symbol": "os76", "name": "OsmiumCoin" }, - { "id": "osmoscoin", "symbol": "os", "name": "OsmosCoin" }, - { "id": "osmosis", "symbol": "osmo", "name": "Osmosis" }, - { "id": "otcbtc-token", "symbol": "otb", "name": "OTCBTC Token" }, - { - "id": "ot-compound-dai", - "symbol": "ot-cdai-29dec2022", - "name": "OT Compound Dai" - }, - { - "id": "ot-eth-usdc", - "symbol": "ot-ethusdc-29dec2022", - "name": "OT-ETH/USDC" - }, - { "id": "otfinancial", "symbol": "ot", "name": "OTFINANCIAL" }, - { "id": "otherlife", "symbol": "OTL", "name": "OtherLife" }, - { - "id": "otium-technologies", - "symbol": "otium", - "name": "Otium Technologies" - }, - { "id": "otocash", "symbol": "oto", "name": "OTOCASH" }, - { "id": "oto-protocol", "symbol": "oto", "name": "OTO Protocol" }, - { - "id": "ot-pendle-eth", - "symbol": "ot-pe-29dec2022", - "name": "OT-PENDLE/ETH" - }, - { "id": "otterclam", "symbol": "clam", "name": "OtterClam" }, - { "id": "otter-finance", "symbol": "otr", "name": "Otter Finance" }, - { "id": "oud", "symbol": "oud", "name": "OUD" }, - { "id": "ouranos", "symbol": "our", "name": "Ouranos" }, - { "id": "ourglass", "symbol": "glass", "name": "OurGlass" }, - { "id": "ouroboros", "symbol": "ouro", "name": "Ouroboros" }, - { - "id": "ouro-governance-share", - "symbol": "ogs", - "name": "Ouro Governance Share" - }, - { "id": "ouro-stablecoin", "symbol": "ouro", "name": "Ouro Stablecoin" }, - { "id": "our-pay", "symbol": "our", "name": "Our Pay" }, - { "id": "ouse", "symbol": "ouse", "name": "Ouse" }, - { "id": "outer-ring", "symbol": "gq", "name": "Outer Ring" }, - { "id": "outrace", "symbol": "ore", "name": "Outrace" }, - { "id": "ovato", "symbol": "ovo", "name": "Ovato" }, - { "id": "overline-emblem", "symbol": "emb", "name": "Overline Emblem" }, - { "id": "overload-game", "symbol": "ovl", "name": "Overlord Game" }, - { "id": "overlord", "symbol": "lord", "name": "Overlord" }, - { "id": "ovr", "symbol": "ovr", "name": "Ovr" }, - { "id": "owgaming", "symbol": "ow", "name": "OWGaming" }, - { "id": "owldao", "symbol": "owl", "name": "OwlDAO" }, - { "id": "owl-share", "symbol": "oshare", "name": "Owl Share" }, - { "id": "owndata", "symbol": "own", "name": "OWNDATA" }, - { "id": "ownix", "symbol": "onx", "name": "Ownix" }, - { "id": "ownly", "symbol": "own", "name": "Ownly" }, - { "id": "own-token", "symbol": "own", "name": "OWN Token" }, - { "id": "oxbitcoin", "symbol": "0xbtc", "name": "0xBitcoin" }, - { "id": "oxbull-solana", "symbol": "oxs", "name": "Oxbull Solana" }, - { "id": "oxbull-tech", "symbol": "oxb", "name": "Oxbull Tech" }, - { "id": "oxo-farm", "symbol": "oxo", "name": "OXO.Farm" }, - { "id": "oxsolid", "symbol": "oxsolid", "name": "oxSOLID" }, - { "id": "oxy-dev", "symbol": "oyt", "name": "OxyDev" }, - { "id": "oxygen", "symbol": "oxy", "name": "Oxygen" }, - { "id": "ozagold", "symbol": "ozg", "name": "Ozagold" }, - { "id": "p2p", "symbol": "p2p", "name": "P2P" }, - { - "id": "p2p-solutions-foundation", - "symbol": "p2ps", - "name": "P2P solutions foundation" - }, - { "id": "paccoin", "symbol": "pac", "name": "PAC Protocol" }, - { "id": "pacific", "symbol": "paf", "name": "Pacific" }, - { "id": "pacific-defi", "symbol": "pacific", "name": "Pacific DeFi" }, - { "id": "packageportal", "symbol": "port", "name": "PackagePortal" }, - { "id": "packswap", "symbol": "pact", "name": "PactSwap" }, - { "id": "pacoca", "symbol": "pacoca", "name": "Pacoca" }, - { - "id": "pagan-gods-fur-token", - "symbol": "fur", - "name": "Pagan Gods Fur token" - }, - { "id": "page", "symbol": "page", "name": "Page" }, - { "id": "paid-network", "symbol": "paid", "name": "PAID Network" }, - { "id": "paint", "symbol": "paint", "name": "MurAll" }, - { "id": "paint-swap", "symbol": "brush", "name": "Paint Swap" }, - { "id": "pakcoin", "symbol": "pak", "name": "Pakcoin" }, - { "id": "pakkun-inu", "symbol": "pakk", "name": "Pakkun Inu" }, - { "id": "pala", "symbol": "pala", "name": "Pala" }, - { "id": "palace", "symbol": "paa", "name": "Palace" }, - { "id": "paladin", "symbol": "pal", "name": "Paladin" }, - { "id": "paladin-dao", "symbol": "pal", "name": "Paladin DAO" }, - { - "id": "paladin-stkaave", - "symbol": "palstkaave", - "name": "Paladin stkAAVE" - }, - { - "id": "palantir-tokenized-stock-defichain", - "symbol": "dpltr", - "name": "Palantir Tokenized Stock Defichain" - }, - { "id": "palestine-finance", "symbol": "pal", "name": "Palestine Finance" }, - { "id": "palette", "symbol": "plt", "name": "Palette" }, - { "id": "palgold", "symbol": "palg", "name": "PalGold" }, - { "id": "pallapay", "symbol": "palla", "name": "Pallapay" }, - { "id": "pallas-finance", "symbol": "pallas", "name": "Pallas Finance" }, - { "id": "palletone", "symbol": "ptn", "name": "PalletOneToken" }, - { "id": "pamp-network", "symbol": "pamp", "name": "Pamp Network" }, - { "id": "pampther", "symbol": "pampther", "name": "Pampther" }, - { "id": "pancake-bunny", "symbol": "bunny", "name": "Pancake Bunny" }, - { "id": "pancake-games", "symbol": "gcake", "name": "Pancake Games" }, - { "id": "pancake-hunny", "symbol": "hunny", "name": "Hunny Finance" }, - { "id": "pancakelock", "symbol": "plock", "name": "PancakeLock" }, - { "id": "pancakepoll", "symbol": "ppoll", "name": "PancakePoll" }, - { "id": "pancakeswap-token", "symbol": "cake", "name": "PancakeSwap" }, - { - "id": "pancakeswap-wormhole", - "symbol": "cake", - "name": "PancakeSwap (Wormhole)" - }, - { "id": "pancaketools", "symbol": "tcake", "name": "PancakeTools" }, - { "id": "pandacoin", "symbol": "pnd", "name": "Pandacoin" }, - { "id": "panda-coin", "symbol": "panda", "name": "Panda Coin" }, - { "id": "pandadao", "symbol": "panda", "name": "PandaDAO" }, - { "id": "panda-dao", "symbol": "pdao", "name": "Panda Dao" }, - { "id": "panda-girl", "symbol": "pgirl", "name": "Panda Girl" }, - { "id": "panda-multiverse", "symbol": "pndmlv", "name": "Panda Multiverse" }, - { "id": "pandaswap-2", "symbol": "panda", "name": "PandaSwap" }, - { "id": "pando", "symbol": "pando", "name": "Pando" }, - { "id": "pandora-protocol", "symbol": "pndr", "name": "Pandora Protocol" }, - { "id": "pando-token", "symbol": "ptx", "name": "Pando Token" }, - { "id": "pangolin", "symbol": "png", "name": "Pangolin" }, - { "id": "pangolinswap", "symbol": "pangolin", "name": "Pangolinswap" }, - { "id": "panicswap", "symbol": "panic", "name": "PanicSwap" }, - { "id": "pantheon-x", "symbol": "xpn", "name": "PANTHEON X" }, - { "id": "panther", "symbol": "zkp", "name": "Panther Protocol" }, - { "id": "pantherswap", "symbol": "panther", "name": "PantherSwap" }, - { "id": "pantos", "symbol": "pan", "name": "Pantos" }, - { "id": "panvala-pan", "symbol": "pan", "name": "Panvala Pan" }, - { "id": "papa-dao", "symbol": "papa", "name": "PAPA DAO" }, - { "id": "papa-doge", "symbol": "papadoge", "name": "Papa Doge" }, - { "id": "paparazzi", "symbol": "pazzi", "name": "Paparazzi" }, - { "id": "papel", "symbol": "papel", "name": "Papel Token" }, - { "id": "paper-fantom", "symbol": "paper", "name": "Paper" }, - { "id": "pappay", "symbol": "pappay", "name": "Pappay" }, - { - "id": "paprprintr-finance", - "symbol": "papr", - "name": "Paprprintr Finance" - }, - { "id": "parabolic", "symbol": "PARA", "name": "Parabolic" }, - { "id": "parachute", "symbol": "par", "name": "Parachute" }, - { "id": "paradisefi", "symbol": "eden", "name": "ParadiseFi" }, - { "id": "paradisehotel-nft", "symbol": "pht", "name": "ParadiseHotel NFT" }, - { "id": "paradox-nft", "symbol": "pxbsc", "name": "Paradox NFT" }, - { "id": "paragen", "symbol": "rgen", "name": "Paragen" }, - { "id": "paragon-capital", "symbol": "PARAGON", "name": "Paragon Capital" }, - { "id": "paragonsdao", "symbol": "pdt", "name": "ParagonsDAO" }, - { "id": "paralink-network", "symbol": "para", "name": "Paralink Network" }, - { "id": "parallelchain", "symbol": "xpll", "name": "ParallelChain" }, - { "id": "parallelcoin", "symbol": "duo", "name": "ParallelCoin" }, - { "id": "paras", "symbol": "paras", "name": "Paras" }, - { "id": "parasol-finance", "symbol": "psol", "name": "Parasol Finance" }, - { "id": "parasset", "symbol": "aset", "name": "Parasset" }, - { "id": "parastate", "symbol": "state", "name": "ParaState" }, - { "id": "paraswap", "symbol": "psp", "name": "ParaSwap" }, - { "id": "paratoken", "symbol": "para", "name": "ParaToken [OLD]" }, - { "id": "paratoken-2", "symbol": "para", "name": "ParaToken" }, - { "id": "parex", "symbol": "prx", "name": "Parex" }, - { "id": "paribu-net", "symbol": "prb", "name": "Paribu Net" }, - { "id": "paribus", "symbol": "pbx", "name": "Paribus" }, - { - "id": "paris-saint-germain-fan-token", - "symbol": "psg", - "name": "Paris Saint-Germain Fan Token" - }, - { "id": "parkgene", "symbol": "gene", "name": "Parkgene" }, - { - "id": "parking-infinity-token", - "symbol": "park", - "name": "Parking Infinity Token" - }, - { "id": "parkingo", "symbol": "got", "name": "ParkinGo" }, - { "id": "parma", "symbol": "parma", "name": "PARMA" }, - { "id": "parrotdao", "symbol": "parr", "name": "ParrotDao" }, - { "id": "parrot-egg", "symbol": "ipegg", "name": "Parrot Egg" }, - { - "id": "parrot-egg-polygon", - "symbol": "ppegg", - "name": "Polygon Parrot Egg" - }, - { "id": "parrot-protocol", "symbol": "prt", "name": "Parrot Protocol" }, - { "id": "parrot-usd", "symbol": "pai", "name": "Parrot USD" }, - { "id": "parsiq", "symbol": "prq", "name": "PARSIQ" }, - { "id": "parsiq-boost", "symbol": "prqboost", "name": "Parsiq Boost" }, - { "id": "parsl", "symbol": "seed", "name": "Parsl" }, - { "id": "par-stablecoin", "symbol": "par", "name": "Parallel" }, - { "id": "partial", "symbol": "partial", "name": "Partial" }, - { "id": "partial-share", "symbol": "pshare", "name": "Partial Share" }, - { "id": "particl", "symbol": "part", "name": "Particl" }, - { "id": "particle-2", "symbol": "prtcle", "name": "Particle" }, - { - "id": "particle-technology", - "symbol": "part", - "name": "Particle Technology" - }, - { - "id": "partisia-blockchain", - "symbol": "mpc", - "name": "Partisia Blockchain" - }, - { "id": "partneroid", "symbol": "ptr", "name": "Partner Coin" }, - { "id": "parts-of-four-coin", "symbol": "p4c", "name": "Parts of Four Coin" }, - { "id": "partyboard", "symbol": "pab", "name": "PartyBoard" }, - { "id": "party-dog", "symbol": "PDog", "name": "Party Dog" }, - { "id": "partyswap", "symbol": "party", "name": "PartySwap" }, - { "id": "party-v2", "symbol": "party", "name": "Party" }, - { "id": "pascalcoin", "symbol": "pasc", "name": "Pascal" }, - { "id": "passive-income", "symbol": "psi", "name": "Passive Income" }, - { "id": "passive-token", "symbol": "Passive", "name": "Passive Token" }, - { "id": "passport-finance", "symbol": "pass", "name": "Passport Finance" }, - { "id": "pasta-finance", "symbol": "pasta", "name": "Pasta Finance" }, - { "id": "pastel", "symbol": "psl", "name": "Pastel" }, - { "id": "pastrypunks", "symbol": "PastryPunks", "name": "PastryPunks" }, - { "id": "pasv", "symbol": "pasv", "name": "PASV" }, - { "id": "pathdao", "symbol": "path", "name": "PathDAO" }, - { "id": "pathfundv2", "symbol": "path", "name": "PathFundV2" }, - { "id": "patientory", "symbol": "ptoy", "name": "Patientory" }, - { "id": "patron", "symbol": "pat", "name": "Patron" }, - { "id": "paul-token", "symbol": "paul", "name": "PAUL Token" }, - { "id": "pavecoin", "symbol": "pvn", "name": "Pavecoin" }, - { "id": "pavia", "symbol": "pavia", "name": "Pavia" }, - { "id": "paw", "symbol": "paw", "name": "Paw" }, - { "id": "pawn", "symbol": "pawn", "name": "Pawn" }, - { "id": "pawn-my-nft", "symbol": "pnft", "name": "Pawn My NFT" }, - { "id": "paws-funds", "symbol": "paws", "name": "Paws Funds" }, - { "id": "pawthereum", "symbol": "pawth", "name": "Pawthereum" }, - { "id": "pawtocol", "symbol": "upi", "name": "Pawtocol" }, - { "id": "paw-v2", "symbol": "paw", "name": "Paw V2" }, - { "id": "pax-gold", "symbol": "paxg", "name": "PAX Gold" }, - { - "id": "paxos-gold-wormhole", - "symbol": "paxg", - "name": "Paxos Gold (Wormhole)" - }, - { "id": "paxos-standard", "symbol": "usdp", "name": "Pax Dollar" }, - { "id": "payaccept", "symbol": "payt", "name": "PayAccept" }, - { "id": "paybandcoin", "symbol": "PYBC", "name": "PaybandCoin" }, - { "id": "paybolt", "symbol": "pay", "name": "PayBolt" }, - { "id": "paybswap", "symbol": "payb", "name": "Paybswap" }, - { "id": "paycent", "symbol": "pyn", "name": "Paycent" }, - { "id": "paycer-protocol", "symbol": "pcr", "name": "Paycer Protocol" }, - { "id": "paycheck-defi", "symbol": "check", "name": "Paycheck" }, - { "id": "pay-coin", "symbol": "pci", "name": "Paycoin" }, - { "id": "paycon-token", "symbol": "con", "name": "Paycon Token" }, - { "id": "paygo", "symbol": "paygo", "name": "PayGo" }, - { "id": "pay-it-now", "symbol": "pin", "name": "Pay It Now" }, - { "id": "paymastercoin", "symbol": "pmc", "name": "PayMasterCoin" }, - { "id": "payment-coin", "symbol": "pod", "name": "Payment Coin" }, - { "id": "paynet-coin", "symbol": "payn", "name": "PayNet Coin" }, - { - "id": "paypal-tokenized-stock-zipmex", - "symbol": "pypl", - "name": "Paypal Tokenized Stock Zipmex" - }, - { "id": "payperex", "symbol": "pax", "name": "PayperEx" }, - { "id": "paypie", "symbol": "ppp", "name": "PayPie" }, - { "id": "paypolitan-token", "symbol": "epan", "name": "Paypolitan Token" }, - { "id": "payrue", "symbol": "propel", "name": "Propel" }, - { "id": "paytomat", "symbol": "pti", "name": "Paytomat" }, - { "id": "payturn", "symbol": "ptr", "name": "Payturn" }, - { "id": "payyoda", "symbol": "yot", "name": "PayYoda" }, - { "id": "payz-payments", "symbol": "payz", "name": "Payz Payments" }, - { "id": "pazzy", "symbol": "pazzy", "name": "Pazzy" }, - { "id": "pbtc35a", "symbol": "pbtc35a", "name": "pBTC35A" }, - { "id": "pcatv3", "symbol": "pcatv3", "name": "pCATv3" }, - { "id": "pchain", "symbol": "pi", "name": "Plian" }, - { "id": "pdbc-defichain", "symbol": "dpdbc", "name": "PDBC Defichain" }, - { "id": "pddollar", "symbol": "pdd", "name": "PDDOLLAR" }, - { "id": "pdollar", "symbol": "pdo", "name": "pDollar" }, - { "id": "pdollar-share", "symbol": "spdo", "name": "pDollar Share" }, - { "id": "pdshare", "symbol": "pdshare", "name": "PDSHARE" }, - { "id": "pdx-coin", "symbol": "pdx", "name": "PDX Coin" }, - { "id": "peace-dao", "symbol": "peace", "name": "Peace DAO" }, - { "id": "peace-doge", "symbol": "peacedoge", "name": "Peace Doge" }, - { "id": "peace-token", "symbol": "pet", "name": "Peace Token" }, - { "id": "peach-finance", "symbol": "peech", "name": "Peach Finance" }, - { "id": "peachfolio", "symbol": "pchf", "name": "Peachfolio" }, - { "id": "peacockcoin", "symbol": "pekc", "name": "Peacockcoin" }, - { "id": "peacockcoin-eth", "symbol": "pekc", "name": "Peacockcoin (ETH)" }, - { "id": "pea-farm", "symbol": "pea", "name": "Pea Farm" }, - { "id": "peak-avalanche", "symbol": "peakavax", "name": "Peak Avalanche" }, - { "id": "peak-finance", "symbol": "peak", "name": "Peak Finance" }, - { "id": "peakmines-peak", "symbol": "peak", "name": "Peakmines PEAK" }, - { "id": "peanut", "symbol": "nux", "name": "Peanut" }, - { "id": "peanuts", "symbol": "peanuts", "name": "Peanuts" }, - { "id": "peaq", "symbol": "peaq", "name": "peaq" }, - { "id": "pear", "symbol": "pear", "name": "Pear" }, - { "id": "peardao", "symbol": "pex", "name": "PearDAO" }, - { "id": "pearl-finance", "symbol": "pearl", "name": "Pearl Finance" }, - { "id": "peaswap", "symbol": "pea", "name": "PeaSwap" }, - { "id": "peculium-2", "symbol": "pcl", "name": "Peculium" }, - { "id": "peecoin-charts", "symbol": "peecoin", "name": "PeeCoin Charts" }, - { "id": "peepcoin", "symbol": "pcn", "name": "Peepcoin" }, - { "id": "peercoin", "symbol": "ppc", "name": "Peercoin" }, - { "id": "peerex-network", "symbol": "PERX", "name": "PeerEx Network" }, - { "id": "peerguess", "symbol": "guess", "name": "PeerGuess" }, - { "id": "peet-defi", "symbol": "pte", "name": "Peet DeFi" }, - { "id": "pegascoin", "symbol": "pgc", "name": "Pegascoin" }, - { "id": "pegasusdao", "symbol": "sus", "name": "PegasusDAO" }, - { "id": "pegaxy-stone", "symbol": "pgx", "name": "Pegaxy Stone" }, - { "id": "pegazus-finance", "symbol": "peg", "name": "Pegazus Finance" }, - { "id": "pegnet", "symbol": "peg", "name": "PegNet" }, - { "id": "pele-network", "symbol": "pele", "name": "PELE Network" }, - { "id": "pellet-food", "symbol": "PELLET", "name": "Pellet Food" }, - { "id": "pendle", "symbol": "pendle", "name": "Pendle" }, - { "id": "pengolincoin", "symbol": "pgo", "name": "PengolinCoin" }, - { "id": "penguin-finance", "symbol": "pefi", "name": "Penguin Finance" }, - { "id": "penguin-karts", "symbol": "pgk", "name": "Penguin Karts" }, - { - "id": "penguin-party-fish", - "symbol": "fish", - "name": "Penguin Party Fish" - }, - { "id": "penky", "symbol": "penky", "name": "Penky" }, - { "id": "penny-for-bit", "symbol": "pfb", "name": "Penny For Bit" }, - { "id": "pension-plan", "symbol": "pp", "name": "Pension Plan" }, - { "id": "pentagon-finance", "symbol": "pent", "name": "Pentagon Finance" }, - { "id": "peony-coin", "symbol": "pny", "name": "Peony Coin" }, - { "id": "peoples-punk", "symbol": "dddd", "name": "People's Punk" }, - { "id": "peoplez", "symbol": "lez", "name": "Peoplez" }, - { "id": "peos", "symbol": "peos", "name": "pEOS" }, - { "id": "pepedex", "symbol": "ppdex", "name": "Pepedex" }, - { "id": "pepegold", "symbol": "peps", "name": "PEPS Coin" }, - { "id": "pepemon-pepeballs", "symbol": "ppblz", "name": "Pepemon Pepeballs" }, - { "id": "pepemoon", "symbol": "pepe", "name": "Pepemoon" }, - { "id": "pepeverse", "symbol": "pepevr", "name": "PepeVerse" }, - { "id": "pera-finance", "symbol": "pera", "name": "Pera Finance" }, - { "id": "percent", "symbol": "pct", "name": "Percent" }, - { "id": "perfect-world", "symbol": "pfw", "name": "Perfect World" }, - { "id": "peri-finance", "symbol": "peri", "name": "PERI Finance" }, - { "id": "perion", "symbol": "perc", "name": "Perion" }, - { "id": "perkle", "symbol": "prkl", "name": "Perkle" }, - { "id": "perlin", "symbol": "perl", "name": "PERL.eco" }, - { "id": "permission-coin", "symbol": "ask", "name": "Permission Coin" }, - { - "id": "perpetual-protocol", - "symbol": "perp", - "name": "Perpetual Protocol" - }, - { - "id": "perpetual-wormhole", - "symbol": "perp", - "name": "Perpetual (Wormhole)" - }, - { "id": "perpetuum-coin", "symbol": "prp", "name": "Perpetuum Coin" }, - { "id": "per-project", "symbol": "per", "name": "PER Project" }, - { "id": "persia", "symbol": "persia", "name": "Persia" }, - { "id": "persistence", "symbol": "xprt", "name": "Persistence" }, - { - "id": "persistence-staked-xprt", - "symbol": "stkxprt", - "name": "pSTAKE Staked XPRT" - }, - { - "id": "perth-mint-gold-token", - "symbol": "pmgt", - "name": "Perth Mint Gold Token" - }, - { "id": "peseta-digital", "symbol": "ptd", "name": "Peseta Digital" }, - { "id": "pet-games", "symbol": "petg", "name": "Pet Games" }, - { "id": "peth18c", "symbol": "peth18c", "name": "pETH18C" }, - { "id": "petkingdom", "symbol": "pkd", "name": "PetKingdom" }, - { "id": "petrachor", "symbol": "pta", "name": "Petrachor" }, - { "id": "petrodollar", "symbol": "xpd", "name": "PetroDollar" }, - { "id": "petshelp", "symbol": "peth", "name": "PetsHelp" }, - { "id": "petworld", "symbol": "pw", "name": "PetWorld" }, - { "id": "pexcoin", "symbol": "pex", "name": "Pexcoin" }, - { "id": "pftm", "symbol": "pftm", "name": "pFTM" }, - { "id": "pha", "symbol": "pha", "name": "Phala Network" }, - { "id": "phaeton", "symbol": "phae", "name": "Phaeton" }, - { "id": "phant", "symbol": "pnt", "name": "Phant" }, - { "id": "phantamdao", "symbol": "phm", "name": "PhantomDAO" }, - { "id": "phantasia", "symbol": "fant", "name": "Phantasia" }, - { "id": "phantasma", "symbol": "soul", "name": "Phantasma" }, - { "id": "phantasma-energy", "symbol": "kcal", "name": "Phantasma Energy" }, - { "id": "phantom-protocol", "symbol": "phm", "name": "Phantom Protocol" }, - { "id": "pharma-pay-coin", "symbol": "prp", "name": "Pharma Pay Coin" }, - { "id": "phenix-finance", "symbol": "phnx", "name": "Phenix Finance" }, - { "id": "phifi-finance", "symbol": "phifiv2", "name": "PhiFi Finance" }, - { "id": "philcoin", "symbol": "phl", "name": "Philcoin" }, - { - "id": "philetairus-socius", - "symbol": "socius", - "name": "Philetairus Socius" - }, - { "id": "philips-pay-coin", "symbol": "ppc", "name": "PHILLIPS PAY COIN" }, - { "id": "phillionex", "symbol": "phn", "name": "Phillionex" }, - { "id": "philscurrency", "symbol": "wage", "name": "Digiwage" }, - { "id": "phoenix", "symbol": "phx", "name": "Phoenix" }, - { "id": "phoenixchain", "symbol": "pcn", "name": "PhoenixChain" }, - { "id": "phoenixcoin", "symbol": "pxc", "name": "Phoenixcoin" }, - { "id": "phoenixdao", "symbol": "phnx", "name": "PhoenixDAO" }, - { - "id": "phoenix-defi-finance", - "symbol": "pnixs", - "name": "Phoenix Defi Finance" - }, - { - "id": "phoenixdefi-finance", - "symbol": "pnix", - "name": "PhoenixDefi.Finance" - }, - { "id": "phoenix-global", "symbol": "phb", "name": "Phoenix Global" }, - { "id": "phoenix-token", "symbol": "phx", "name": "Phoenix Token" }, - { "id": "phoenix-unity", "symbol": "pxu", "name": "Phoenix Unity" }, - { "id": "phoneum", "symbol": "pht", "name": "Phoneum" }, - { "id": "phonon-dao", "symbol": "phonon", "name": "Phonon DAO" }, - { "id": "phore", "symbol": "phr", "name": "Phore" }, - { "id": "photochromic", "symbol": "phcr", "name": "PhotoChromic" }, - { "id": "photon", "symbol": "pho", "name": "Photon" }, - { "id": "photonswap", "symbol": "photon", "name": "PhotonSwap" }, - { - "id": "phuket-holiday-coin", - "symbol": "phc", - "name": "Phuket Holiday Coin" - }, - { "id": "phunk-vault-nftx", "symbol": "phunk", "name": "PHUNK Vault (NFTX)" }, - { "id": "phuntoken", "symbol": "phtk", "name": "Phuntoken" }, - { "id": "phu-quoc-dog", "symbol": "pqd", "name": "Phu Quoc Dog" }, - { "id": "phuture", "symbol": "phtr", "name": "Phuture" }, - { "id": "physis", "symbol": "phy", "name": "Physis" }, - { "id": "piasa", "symbol": "piasa", "name": "PIASA" }, - { "id": "pibble", "symbol": "pib", "name": "Pibble" }, - { "id": "picartnft", "symbol": "panft", "name": "PicArtNFT" }, - { "id": "picasso-network", "symbol": "pica", "name": "Picasso Network" }, - { "id": "piccolo-inu", "symbol": "pinu", "name": "Piccolo Inu" }, - { "id": "picipo", "symbol": "picipo", "name": "Picipo" }, - { "id": "pick", "symbol": "pick", "name": "PICK" }, - { "id": "pickle-finance", "symbol": "pickle", "name": "Pickle Finance" }, - { "id": "picogo", "symbol": "pico", "name": "PicoGo" }, - { "id": "piction-network", "symbol": "pxl", "name": "Piction Network" }, - { "id": "pidao", "symbol": "pid", "name": "PIDAO" }, - { - "id": "piedao-balanced-crypto-pie", - "symbol": "bcp", - "name": "PieDAO Balanced Crypto Pie" - }, - { "id": "piedao-btc", "symbol": "btc++", "name": "PieDAO BTC++" }, - { "id": "piedao-defi", "symbol": "defi++", "name": "PieDAO DEFI++" }, - { - "id": "piedao-defi-large-cap", - "symbol": "defi+l", - "name": "PieDAO DEFI Large Cap" - }, - { - "id": "piedao-defi-small-cap", - "symbol": "DEFI+S", - "name": "PieDAO DEFI Small Cap" - }, - { "id": "piedao-dough-v2", "symbol": "dough", "name": "PieDAO DOUGH v2" }, - { - "id": "piedao-yearn-ecosystem-pie", - "symbol": "ypie", - "name": "PieDAO Yearn Ecosystem Pie" - }, - { "id": "pieme", "symbol": "pie", "name": "Pieme" }, - { "id": "pie-share", "symbol": "pie", "name": "Pie Share" }, - { "id": "pigeoncoin", "symbol": "pgn", "name": "Pigeoncoin" }, - { "id": "pigeon-sol", "symbol": "pgnt", "name": "Pigeon Sol" }, - { "id": "pig-finance", "symbol": "pig", "name": "Pig Finance" }, - { "id": "piggy", "symbol": "piggy", "name": "Piggy" }, - { "id": "piggy-bank", "symbol": "pb", "name": "Piggy Bank" }, - { "id": "piggy-bank-token", "symbol": "piggy", "name": "Piggy Bank Token" }, - { "id": "piggy-finance", "symbol": "piggy", "name": "Piggy Finance" }, - { "id": "piggy-planet", "symbol": "pigi", "name": "Piggy Planet" }, - { "id": "piggy-share", "symbol": "pshare", "name": "Piggy Share" }, - { "id": "pigs", "symbol": "pigs", "name": "PIGS" }, - { "id": "pikachu", "symbol": "pika", "name": "Pika" }, - { "id": "pikachu-inu", "symbol": "pikachu", "name": "Pikachu Inu" }, - { "id": "pillar", "symbol": "plr", "name": "Pillar" }, - { "id": "pilot", "symbol": "ptd", "name": "Pilot" }, - { "id": "pineapple-swap", "symbol": "PNS", "name": "Pineapple swap" }, - { "id": "pinecone-finance", "symbol": "pct", "name": "Pinecone Finance" }, - { - "id": "pi-network-defi", - "symbol": "PiNetworkDeFi", - "name": "Pi Network DeFi" - }, - { "id": "pinkcoin", "symbol": "pink", "name": "Pinkcoin" }, - { "id": "pinkelon", "symbol": "pinke", "name": "PinkElon" }, - { "id": "pinkmoon", "symbol": "pinkm", "name": "PinkMoon" }, - { "id": "pinknode", "symbol": "pnode", "name": "Pinknode" }, - { "id": "pink-panda", "symbol": "pinkpanda", "name": "Pink Panda" }, - { "id": "pink-panther", "symbol": "PINK", "name": "PINK PANTHER" }, - { "id": "pinksale", "symbol": "pinksale", "name": "PinkSale" }, - { "id": "pinkslip-finance", "symbol": "pslip", "name": "Pinkslip Finance" }, - { "id": "pinkswap-token", "symbol": "pinks", "name": "PinkSwap Token" }, - { "id": "pintu-token", "symbol": "PTU", "name": "Pintu Token" }, - { "id": "pip", "symbol": "pip", "name": "PIP" }, - { "id": "piplcoin", "symbol": "pipl", "name": "PiplCoin" }, - { "id": "pippi-finance", "symbol": "pipi", "name": "Pippi Finance" }, - { "id": "pirate-boy", "symbol": "pirateboy", "name": "Pirate Boy" }, - { "id": "piratecash", "symbol": "pirate", "name": "PirateCash" }, - { "id": "pirate-chain", "symbol": "arrr", "name": "Pirate Chain" }, - { "id": "piratecoin", "symbol": "piratecoin☠", "name": "PirateCoin" }, - { "id": "piratedao", "symbol": "jolly", "name": "PirateDAO" }, - { "id": "pirate-dice", "symbol": "booty", "name": "Pirate Dice" }, - { "id": "pirate-inu", "symbol": "pinu", "name": "Pirate Inu" }, - { "id": "piratep", "symbol": "piratep", "name": "PirateP" }, - { "id": "piratera", "symbol": "pira", "name": "Piratera" }, - { "id": "piratesking", "symbol": "PKT", "name": "PiratesKing" }, - { "id": "pirate-x-pirate", "symbol": "pxp", "name": "Pirate x Pirate" }, - { "id": "piston", "symbol": "pstn", "name": "Piston" }, - { "id": "pist-trust", "symbol": "pist", "name": "Pist Trust" }, - { "id": "pitbull", "symbol": "pit", "name": "Pitbull" }, - { "id": "pitch", "symbol": "pitch", "name": "Pitch" }, - { "id": "pitquidity", "symbol": "pitqd", "name": "Pitquidity" }, - { "id": "pitquidity-bsc", "symbol": "pitqd", "name": "Pitquidity-BSC" }, - { "id": "pittys", "symbol": "PITTYS", "name": "Pittys" }, - { "id": "pivot-token", "symbol": "pvt", "name": "Pivot Token" }, - { "id": "pivx", "symbol": "pivx", "name": "PIVX" }, - { "id": "pixelgas", "symbol": "pixelgas", "name": "PixelGas" }, - { "id": "pixelpotus", "symbol": "pxl", "name": "PixelPotus" }, - { "id": "pixelsquid", "symbol": "pixelsquid", "name": "PixelSQUID" }, - { "id": "pixels-so", "symbol": "pixl", "name": "Pixels.so" }, - { "id": "pixelverse", "symbol": "pixel", "name": "PixelVerse" }, - { "id": "pixeos", "symbol": "pixeos", "name": "PixEOS" }, - { "id": "pixiu-finance", "symbol": "pixiu", "name": "Pixiu Finance" }, - { "id": "pixl-coin-2", "symbol": "pxlc", "name": "Pixl Coin" }, - { "id": "pixls-vault-nftx", "symbol": "pixls", "name": "PIXLS Vault (NFTX)" }, - { "id": "pizza-game", "symbol": "pizza", "name": "Pizza Game" }, - { "id": "pizza-nft", "symbol": "$PIZZA", "name": "Pizza NFT" }, - { "id": "pizza-pug-coin", "symbol": "ppug", "name": "Pizza Pug Coin" }, - { "id": "pizzaswap", "symbol": "pizza", "name": "PizzaSwap" }, - { "id": "pizza-usde", "symbol": "pizza", "name": "PIZZA" }, - { "id": "pkg-token", "symbol": "pkg", "name": "PKG Token" }, - { "id": "pkt", "symbol": "pkt", "name": "PKT" }, - { "id": "placeh", "symbol": "phl", "name": "Placeholders" }, - { "id": "place-war", "symbol": "place", "name": "PlaceWar Governance Token" }, - { "id": "plair", "symbol": "pla", "name": "Plair" }, - { "id": "planet", "symbol": "pla", "name": "PLANET" }, - { "id": "planet-finance", "symbol": "aqua", "name": "Planet Finance" }, - { "id": "planet-inu", "symbol": "PLANETINU", "name": "Planet Inu" }, - { "id": "planet-of-apes", "symbol": "poa", "name": "Planet of Apes" }, - { "id": "planet-sandbox", "symbol": "psb", "name": "Planet Sandbox" }, - { "id": "planetverse", "symbol": "planetverse", "name": "PlanetVerse" }, - { "id": "planetwatch", "symbol": "planets", "name": "PlanetWatch" }, - { "id": "plant2earn", "symbol": "p2e", "name": "Plant2Earn" }, - { "id": "plant-exodus", "symbol": "pexo", "name": "Plant Exodus" }, - { - "id": "plant-vs-undead-token", - "symbol": "pvu", - "name": "Plant vs Undead Token" - }, - { "id": "plasma-finance", "symbol": "ppay", "name": "Plasma Finance" }, - { "id": "plastiks", "symbol": "plastik", "name": "Plastiks" }, - { "id": "plata-network", "symbol": "plata", "name": "Plata Network" }, - { "id": "plateau-finance", "symbol": "plt", "name": "Plateau Finance" }, - { - "id": "platform-for-underpaid-female-athletes", - "symbol": "pufa", - "name": "Platform for Underpaid Female Athletes" - }, - { "id": "platincoin", "symbol": "plc", "name": "PlatinCoin" }, - { "id": "platinum-finance", "symbol": "plat", "name": "Platinum Finance" }, - { "id": "platinx", "symbol": "ptx", "name": "PlatinX" }, - { "id": "plato-farm", "symbol": "mark", "name": "Plato Farm" }, - { "id": "plato-game", "symbol": "plato", "name": "Plato Game" }, - { "id": "platoncoin", "symbol": "pltc", "name": "PlatonCoin" }, - { "id": "platon-network", "symbol": "lat", "name": "PlatON Network" }, - { "id": "platypus-finance", "symbol": "ptp", "name": "Platypus Finance" }, - { "id": "play2live", "symbol": "luc", "name": "Level-Up Coin" }, - { "id": "playa3ull-games", "symbol": "3ull", "name": "Playa3ull Games" }, - { "id": "playandlike", "symbol": "pal", "name": "PlayAndLike" }, - { "id": "playandlike-old", "symbol": "pal", "name": "PlayAndLike [OLD]" }, - { "id": "playcent", "symbol": "pcnt", "name": "Playcent" }, - { "id": "playchip", "symbol": "pla", "name": "PlayChip" }, - { "id": "playcoin-qrc20", "symbol": "ply", "name": "PlayCoin (QRC-20)" }, - { "id": "playdapp", "symbol": "pla", "name": "PlayDapp" }, - { "id": "playermon", "symbol": "pym", "name": "Playermon" }, - { "id": "playersonly", "symbol": "po", "name": "PlayersOnly" }, - { "id": "playervsplayercoin", "symbol": "pvp", "name": "PlayerVsPlayerCoin" }, - { "id": "playfuel", "symbol": "plf", "name": "PlayFuel" }, - { "id": "playgame", "symbol": "pxg", "name": "PlayGame" }, - { "id": "playground", "symbol": "playa", "name": "Playground" }, - { - "id": "playground-waves-floor-index", - "symbol": "waves", - "name": "Playground Waves Floor Index" - }, - { "id": "playgroundz", "symbol": "iog", "name": "Playgroundz" }, - { - "id": "play-it-forward-dao", - "symbol": "pif", - "name": "Play It Forward DAO" - }, - { "id": "playkey", "symbol": "pkt", "name": "PlayKey" }, - { "id": "playmarket", "symbol": "pmt", "name": "DAO PlayMarket 2.0" }, - { "id": "playnity", "symbol": "ply", "name": "PlayNity" }, - { "id": "playpad", "symbol": "ppad", "name": "PlayPad" }, - { "id": "playposeidon-nft", "symbol": "ppp", "name": "PlayPoseidon NFT" }, - { "id": "play-to-maximize", "symbol": "pmx", "name": "Play To Maximize" }, - { "id": "playtreks", "symbol": "treks", "name": "PlayTreks" }, - { "id": "playzap", "symbol": "playzap", "name": "PlayZap" }, - { "id": "plaza-finance", "symbol": "plaza", "name": "Plaza Finance" }, - { "id": "plc-ultima", "symbol": "plcu", "name": "PLC Ultima" }, - { "id": "plearn", "symbol": "pln", "name": "PLEARN" }, - { "id": "pleasure-coin", "symbol": "nsfw", "name": "Pleasure Coin" }, - { "id": "plebe-gaming", "symbol": "pleb", "name": "Plebe Gaming" }, - { "id": "pledge", "symbol": "plgr", "name": "Pledge" }, - { "id": "pledgecamp", "symbol": "plg", "name": "Pledgecamp" }, - { "id": "pledge-finance", "symbol": "mplgr", "name": "Pledge Finance" }, - { "id": "plentycoin", "symbol": "plentycoin", "name": "PlentyCoin" }, - { "id": "plenty-dao", "symbol": "plenty", "name": "Plenty DeFi" }, - { "id": "plethori", "symbol": "ple", "name": "Plethori" }, - { "id": "plex", "symbol": "plex", "name": "PLEX" }, - { "id": "plgnet", "symbol": "plug", "name": "PL^Gnet" }, - { "id": "plotx", "symbol": "plot", "name": "PlotX" }, - { "id": "plug", "symbol": "plg", "name": "Plug" }, - { "id": "plug-chain", "symbol": "plugcn", "name": "Plug Chain" }, - { "id": "plugin", "symbol": "pli", "name": "Plugin" }, - { "id": "plumcake-finance", "symbol": "plum", "name": "PlumCake Finance" }, - { "id": "plunge", "symbol": "plg", "name": "Plunge" }, - { "id": "pluracoin", "symbol": "plura", "name": "PluraCoin" }, - { "id": "plus-coin", "symbol": "nplc", "name": "Plus Coin" }, - { "id": "plusonecoin", "symbol": "plus1", "name": "PlusOneCoin" }, - { "id": "pluspad", "symbol": "plus", "name": "PlusPad" }, - { "id": "pluton", "symbol": "plu", "name": "Pluton" }, - { "id": "pluton-chain", "symbol": "plc", "name": "Pluton Chain" }, - { "id": "pluto-network", "symbol": "ptn", "name": "Pluto Network" }, - { "id": "plutonian-dao", "symbol": "pld", "name": "Plutonian DAO" }, - { "id": "plutonium", "symbol": "pln", "name": "Plutonium" }, - { "id": "plutopepe", "symbol": "pluto", "name": "PlutoPepe" }, - { "id": "plutos-network", "symbol": "plut", "name": "Plutos Network" }, - { "id": "pmail", "symbol": "pml", "name": "Pmail" }, - { "id": "pnetwork", "symbol": "pnt", "name": "pNetwork" }, - { "id": "pn-token", "symbol": "pn", "name": "PN Token" }, - { "id": "poa-network", "symbol": "poa", "name": "POA Network" }, - { "id": "poc-blockchain", "symbol": "poc", "name": "POC Blockchain" }, - { "id": "poc-chain", "symbol": "pocc", "name": "POC Chain" }, - { "id": "pochi-inu", "symbol": "pochi", "name": "Pochi Inu" }, - { "id": "pocket-arena", "symbol": "poc", "name": "Pocket Arena" }, - { "id": "pocketcoin", "symbol": "pkoin", "name": "Pocketcoin" }, - { "id": "pocket-doge", "symbol": "pckt", "name": "Pocket" }, - { "id": "pocket-network", "symbol": "pokt", "name": "Pocket Network" }, - { "id": "pocket-node", "symbol": "node", "name": "Pocket Node" }, - { "id": "pocmon-2", "symbol": "mon", "name": "PocMon" }, - { "id": "pocoland", "symbol": "poco", "name": "Pocoland" }, - { "id": "podo-point", "symbol": "pod", "name": "Podo Point" }, - { "id": "poet", "symbol": "poe", "name": "Po.et" }, - { "id": "pofi", "symbol": "pofi", "name": "PoFi" }, - { "id": "pogcoin", "symbol": "pog", "name": "PogCoin" }, - { "id": "pog-coin", "symbol": "pog", "name": "PolygonumOnline" }, - { "id": "pogged-finance", "symbol": "pog", "name": "Pogged Finance" }, - { "id": "pointpay", "symbol": "pxp", "name": "PointPay" }, - { "id": "pokeball", "symbol": "poke", "name": "Pokeball" }, - { "id": "pokedx", "symbol": "pdx", "name": "PokeDX" }, - { "id": "pokelon", "symbol": "pokelon", "name": "POKELON" }, - { "id": "pokemonspace", "symbol": "POS", "name": "PokemonSpace" }, - { "id": "poken", "symbol": "pkn", "name": "Poken" }, - { "id": "pokerain", "symbol": "mmda", "name": "Pokerain" }, - { "id": "pokerfi", "symbol": "pokerfi", "name": "PokerFi" }, - { "id": "pokmonsters", "symbol": "pok", "name": "Pokmonsters" }, - { "id": "polar", "symbol": "polar", "name": "POLAR" }, - { "id": "polaris", "symbol": "polar", "name": "Polarisdefi" }, - { "id": "polaris-2", "symbol": "polaris", "name": "Polaris" }, - { "id": "polarisdao", "symbol": "aris", "name": "PolarisDAO" }, - { "id": "polaris-share", "symbol": "pola", "name": "Polaris Share" }, - { "id": "polaris-token", "symbol": "plrs", "name": "Polaris Token" }, - { "id": "polars", "symbol": "pol", "name": "Polars" }, - { "id": "polar-shares", "symbol": "spolar", "name": "Polar Shares" }, - { "id": "polar-sync", "symbol": "polar", "name": "Polar Sync" }, - { "id": "polar-token", "symbol": "polar", "name": "Polar Token" }, - { "id": "pole", "symbol": "pole", "name": "Pole (North Pole)" }, - { - "id": "police-and-thief-game", - "symbol": "loot", - "name": "Police and Thief Game" - }, - { "id": "policedoge", "symbol": "policedoge", "name": "PoliceDOGE" }, - { "id": "polinate", "symbol": "poli", "name": "Polinate" }, - { "id": "polis", "symbol": "polis", "name": "Polis" }, - { "id": "polkabridge", "symbol": "pbr", "name": "PolkaBridge" }, - { "id": "polkacipher", "symbol": "cphr", "name": "PolkaCipher" }, - { "id": "polka-city", "symbol": "polc", "name": "Polkacity" }, - { "id": "polka-classic", "symbol": "dotc", "name": "Polka Classic" }, - { "id": "polkadex", "symbol": "pdex", "name": "Polkadex" }, - { "id": "polkadog-v2-0", "symbol": "einstein", "name": "Polkadog V2.0" }, - { "id": "polkadomain", "symbol": "name", "name": "PolkaDomain" }, - { "id": "polkadot", "symbol": "dot", "name": "Polkadot" }, - { "id": "polkaex", "symbol": "pkex", "name": "PolkaEx" }, - { "id": "polkafantasy", "symbol": "xp", "name": "PolkaFantasy" }, - { "id": "polkafoundry", "symbol": "pkf", "name": "PolkaFoundry" }, - { "id": "polkago", "symbol": "$PLKG", "name": "Polkago" }, - { - "id": "polkainsure-finance", - "symbol": "pis", - "name": "Polkainsure Finance" - }, - { "id": "polkainu", "symbol": "pinu", "name": "PolkaInu" }, - { "id": "polkally", "symbol": "kally", "name": "Kally" }, - { "id": "polkamarkets", "symbol": "polk", "name": "Polkamarkets" }, - { "id": "polkamonster", "symbol": "pkmon", "name": "PolkaMonster" }, - { "id": "polkaparty", "symbol": "polp", "name": "PolkaParty" }, - { "id": "polkapet-world", "symbol": "pets", "name": "PolkaPet World" }, - { "id": "polkaplay", "symbol": "polo", "name": "NftyPlay" }, - { "id": "polkarare", "symbol": "prare", "name": "Polkarare" }, - { - "id": "polkasocial-network", - "symbol": "psn", - "name": "Polkasocial Network" - }, - { "id": "polkastarter", "symbol": "pols", "name": "Polkastarter" }, - { "id": "polkaswap", "symbol": "pswap", "name": "Polkaswap" }, - { "id": "polkatrail", "symbol": "trail", "name": "Polkatrail" }, - { "id": "polkatrain", "symbol": "polt", "name": "Polkatrain" }, - { "id": "polkawar", "symbol": "pwar", "name": "PolkaWar" }, - { "id": "polker", "symbol": "pkr", "name": "Polker" }, - { "id": "pollchain", "symbol": "poll", "name": "Pollchain" }, - { "id": "pollen", "symbol": "pln", "name": "Pollen" }, - { "id": "pollux-coin", "symbol": "pox", "name": "Pollux Coin" }, - { "id": "polly", "symbol": "polly", "name": "Polly Finance" }, - { "id": "polly-defi-nest", "symbol": "ndefi", "name": "Polly DeFi Nest" }, - { "id": "polyalpha-finance", "symbol": "alpha", "name": "PolyAlpha Finance" }, - { "id": "polybius", "symbol": "plbt", "name": "Polybius" }, - { "id": "polycat-finance", "symbol": "fish", "name": "Polycat Finance" }, - { - "id": "polychain-monsters", - "symbol": "pmon", - "name": "Polychain Monsters" - }, - { "id": "polycub", "symbol": "polycub", "name": "PolyCub" }, - { "id": "polydoge", "symbol": "polydoge", "name": "PolyDoge" }, - { "id": "polyfarm-egg", "symbol": "egg", "name": "PolyFarm EGG" }, - { "id": "polyfloki", "symbol": "POKI", "name": "PolyFLOKI" }, - { "id": "polygamma", "symbol": "gamma", "name": "PolyGamma Finance" }, - { "id": "polygen", "symbol": "pgen", "name": "Polygen" }, - { "id": "polygod", "symbol": "gull", "name": "PolyGod" }, - { - "id": "polygon-babydoge", - "symbol": "polybabydoge", - "name": "Polygon BabyDoge" - }, - { - "id": "polygon-ecosystem-index", - "symbol": "peco", - "name": "Amun Polygon Ecosystem Index" - }, - { - "id": "polygonfarm-finance", - "symbol": "spade", - "name": "PolygonFarm Finance" - }, - { - "id": "polyient-games-governance-token", - "symbol": "pgt", - "name": "Polyient Games Governance Token" - }, - { - "id": "polyient-games-unity", - "symbol": "pgu", - "name": "Polyient Games Unity" - }, - { "id": "polylastic", "symbol": "polx", "name": "Polylastic" }, - { "id": "polylauncher", "symbol": "angel", "name": "Polylauncher" }, - { "id": "polylion", "symbol": "lion", "name": "PolyLion" }, - { "id": "polymath", "symbol": "poly", "name": "Polymath" }, - { "id": "polymesh", "symbol": "polyx", "name": "Polymesh" }, - { "id": "polymoon", "symbol": "polymoon", "name": "PolyMoon" }, - { "id": "polypad", "symbol": "polypad", "name": "PolyPad" }, - { "id": "poly-peg-mdex", "symbol": "hmdx", "name": "Poly-Peg Mdex" }, - { "id": "polypug", "symbol": "polypug", "name": "PolyPug" }, - { "id": "polypup", "symbol": "pup", "name": "PolyPup" }, - { - "id": "polypup-collar-token", - "symbol": "collar", - "name": "PolyPup Collar Token" - }, - { "id": "polyquity", "symbol": "pyq", "name": "PolyQuity" }, - { "id": "polyquity-dollar", "symbol": "pyd", "name": "PolyQuity Dollar" }, - { "id": "polyroll", "symbol": "roll", "name": "Polyroll" }, - { "id": "polysafu", "symbol": "safu", "name": "polySAFU" }, - { "id": "polyshark-finance", "symbol": "shark", "name": "PolyShark Finance" }, - { "id": "polyshiba", "symbol": "polyshiba", "name": "PolyShiba" }, - { "id": "polyshield", "symbol": "shi3ld", "name": "PolyShield" }, - { "id": "polysports", "symbol": "ps1", "name": "POLYSPORTS" }, - { "id": "polystar", "symbol": "polystar", "name": "PolyStar" }, - { "id": "polyswarm", "symbol": "nct", "name": "PolySwarm" }, - { "id": "polytrade", "symbol": "trade", "name": "Polytrade" }, - { "id": "polyunity-finance", "symbol": "unity", "name": "PolyUnity Finance" }, - { "id": "polyvertex", "symbol": "vert", "name": "PolyVertex" }, - { "id": "polywave", "symbol": "wave", "name": "polyWAVE" }, - { "id": "polywhale", "symbol": "krill", "name": "Polywhale" }, - { "id": "polyx", "symbol": "pxt", "name": "POLYX" }, - { "id": "polyyeld-token", "symbol": "yeld", "name": "PolyYeld Token" }, - { "id": "polyyield-token", "symbol": "yield", "name": "PolyYield Token" }, - { "id": "polyzap", "symbol": "pzap", "name": "PolyZap" }, - { "id": "pomeranian", "symbol": "pom", "name": "Pomeranian" }, - { "id": "pomerocket", "symbol": "pome", "name": "PomeRocket" }, - { "id": "pomi", "symbol": "Pomi", "name": "Pomi" }, - { "id": "pompom", "symbol": "pom", "name": "PomPom" }, - { "id": "pomskey", "symbol": "pm", "name": "Pomskey" }, - { "id": "pontoon", "symbol": "toon", "name": "Pontoon" }, - { "id": "ponyo-inu", "symbol": "ponyo", "name": "Ponyo Impact" }, - { "id": "ponzicoin", "symbol": "ponzi", "name": "PonziCoin" }, - { "id": "pooch", "symbol": "pooch", "name": "Pooch" }, - { "id": "poocoin", "symbol": "poocoin", "name": "PooCoin" }, - { "id": "poodle", "symbol": "poodl", "name": "Poodl Token" }, - { "id": "poofcash", "symbol": "poof", "name": "PoofCash" }, - { "id": "poolcoin", "symbol": "pool", "name": "POOLCOIN" }, - { "id": "poollotto-finance", "symbol": "plt", "name": "Poollotto.finance" }, - { "id": "pool-party", "symbol": "pp", "name": "Pool Party" }, - { "id": "pooltogether", "symbol": "pool", "name": "PoolTogether" }, - { "id": "pool-token", "symbol": "pool", "name": "Pool Token" }, - { "id": "poolz-finance", "symbol": "poolz", "name": "Poolz Finance" }, - { "id": "poomoon", "symbol": "poo", "name": "POOMOON" }, - { "id": "poopay", "symbol": "poo", "name": "PooPay" }, - { "id": "poopsicle", "symbol": "poop", "name": "Poopsicle" }, - { "id": "poordoge", "symbol": "poordoge", "name": "PoorDoge" }, - { "id": "poor-quack", "symbol": "poor", "name": "Poor Quack" }, - { "id": "pop", "symbol": "pop!", "name": "POP" }, - { "id": "pop-chest-token", "symbol": "pop", "name": "POP Network" }, - { "id": "popcorn", "symbol": "pop", "name": "Popcorn" }, - { "id": "popkon", "symbol": "popk", "name": "POPKON" }, - { "id": "popop-world", "symbol": "pop", "name": "Popop World" }, - { "id": "populous", "symbol": "ppt", "name": "Populous" }, - { - "id": "populous-xbrl-token", - "symbol": "pxt", - "name": "Populous XBRL Token" - }, - { "id": "poriot", "symbol": "zk", "name": "Poriot" }, - { "id": "porkswap", "symbol": "pswap", "name": "PorkSwap" }, - { "id": "pornrocket", "symbol": "pornrocket", "name": "PornRocket" }, - { "id": "porta", "symbol": "kian", "name": "Porta" }, - { "id": "portal", "symbol": "portal", "name": "Portal" }, - { - "id": "portals-ivory-index", - "symbol": "ivry", - "name": "Portals Ivory Index" - }, - { "id": "porte-token", "symbol": "porte", "name": "Porte Token" }, - { "id": "port-finance", "symbol": "port", "name": "Port Finance" }, - { "id": "portify", "symbol": "pfy", "name": "Portify" }, - { "id": "portion", "symbol": "prt", "name": "Portion" }, - { - "id": "portugal-national-team-fan-token", - "symbol": "por", - "name": "Portugal National Team Fan Token" - }, - { "id": "portuma", "symbol": "por", "name": "Portuma" }, - { "id": "pos-coin", "symbol": "pos", "name": "POS Coin" }, - { "id": "poseidon-finance", "symbol": "psdn", "name": "Poseidon Finance" }, - { "id": "poseidon-token", "symbol": "pos", "name": "Poseidon Token" }, - { "id": "position-token", "symbol": "posi", "name": "Position Token" }, - { "id": "positron-token", "symbol": "pot", "name": "Positron Token" }, - { "id": "postcoin", "symbol": "post", "name": "PostCoin" }, - { "id": "potato", "symbol": "potato", "name": "Potato" }, - { "id": "potcoin", "symbol": "pot", "name": "Potcoin" }, - { "id": "potent-coin", "symbol": "ptt", "name": "Potent Coin" }, - { "id": "potentiam", "symbol": "ptm", "name": "Potentiam" }, - { - "id": "potion-brew-finance", - "symbol": "brew", - "name": "Potion Brew Finance" - }, - { "id": "potter-inu", "symbol": "potterinu", "name": "Potter Inu" }, - { "id": "povo-finance", "symbol": "povo", "name": "Povo Finance" }, - { "id": "powerada", "symbol": "pow", "name": "PowerADA" }, - { "id": "powerbalt", "symbol": "pwrb", "name": "PowerBalt" }, - { "id": "power-block", "symbol": "pow", "name": "Power Block" }, - { "id": "power-cash", "symbol": "prch", "name": "Power Cash" }, - { - "id": "power-index-pool-token", - "symbol": "pipt", - "name": "Power Index Pool Token" - }, - { "id": "powerinu", "symbol": "PowerInu", "name": "PowerInu" }, - { "id": "power-ledger", "symbol": "powr", "name": "Power Ledger" }, - { "id": "power-nodes", "symbol": "power", "name": "Power Nodes" }, - { "id": "powertrade-fuel", "symbol": "ptf", "name": "PowerTrade Fuel" }, - { "id": "powerzilla", "symbol": "powerzilla", "name": "PowerZilla" }, - { "id": "pqbert", "symbol": "pqbert", "name": "pQBERT" }, - { "id": "pragma", "symbol": "pragma", "name": "Pragma" }, - { "id": "prcy-coin", "symbol": "prcy", "name": "PRCY Coin" }, - { "id": "precharge", "symbol": "pcpi", "name": "Precharge" }, - { "id": "precium", "symbol": "pcm", "name": "Precium" }, - { "id": "predict", "symbol": "pt", "name": "Predict" }, - { "id": "predictcoin", "symbol": "pred", "name": "Predictcoin" }, - { "id": "prediqt", "symbol": "pqt", "name": "Prediqt" }, - { "id": "predix-network", "symbol": "prdx", "name": "Predix Network" }, - { "id": "prelax", "symbol": "peax", "name": "Prelax" }, - { "id": "premia", "symbol": "premia", "name": "Premia" }, - { "id": "premio", "symbol": "premio", "name": "Premio" }, - { "id": "presaledao", "symbol": "af-presaledao", "name": "PresaleDAO" }, - { "id": "presearch", "symbol": "pre", "name": "Presearch" }, - { - "id": "president-doge", - "symbol": "presidentdoge", - "name": "President Doge" - }, - { "id": "primas", "symbol": "pst", "name": "Primas" }, - { "id": "prime", "symbol": "d2d", "name": "Prime" }, - { "id": "primecoin", "symbol": "xpm", "name": "Primecoin" }, - { "id": "prime-dai", "symbol": "pdai", "name": "Prime DAI" }, - { "id": "prime-numbers", "symbol": "prnt", "name": "Prime Numbers" }, - { "id": "prime-xi", "symbol": "pxi", "name": "Prime-XI" }, - { "id": "prism", "symbol": "prism", "name": "Prism" }, - { "id": "prism-cluna", "symbol": "cluna", "name": "Prism cLUNA" }, - { - "id": "prism-governance-token", - "symbol": "xprism", - "name": "Prism Governance Token" - }, - { "id": "prism-network", "symbol": "prism", "name": "Prism Network" }, - { "id": "prism-pluna", "symbol": "pluna", "name": "Prism pLUNA" }, - { "id": "prism-protocol", "symbol": "prism", "name": "Prism Protocol" }, - { "id": "prism-yluna", "symbol": "yluna", "name": "Prism yLUNA" }, - { "id": "privacyswap", "symbol": "prv", "name": "PrivacySwap" }, - { "id": "privapp-network", "symbol": "bpriva", "name": "Privapp Network" }, - { "id": "privateum", "symbol": "pvm", "name": "Privateum" }, - { "id": "privatix", "symbol": "prix", "name": "Privatix" }, - { "id": "privcy", "symbol": "priv", "name": "PRiVCY" }, - { "id": "privi-pix", "symbol": "pix", "name": "Privi Pix" }, - { "id": "privi-trax", "symbol": "trax", "name": "Privi Trax" }, - { "id": "prize-coin", "symbol": "prz", "name": "Prize Coin" }, - { "id": "prizm", "symbol": "pzm", "name": "Prizm" }, - { "id": "prntr", "symbol": "prntr", "name": "PRNTR" }, - { "id": "probably-nothing", "symbol": "pn", "name": "Probably Nothing" }, - { "id": "probinex", "symbol": "pbx", "name": "Probinex" }, - { "id": "probit-exchange", "symbol": "prob", "name": "Probit Token" }, - { "id": "prodax", "symbol": "dax", "name": "Prodax" }, - { - "id": "professional-fighters-league-fan-token", - "symbol": "pfl", - "name": "Professional Fighters League Fan Token" - }, - { "id": "profit-bank", "symbol": "pbk", "name": "Profit Bank" }, - { "id": "profit-bls", "symbol": "profit", "name": "Profit Bls" }, - { "id": "projectfeenixv2", "symbol": "feenixv2", "name": "ProjectFeenixv2" }, - { "id": "project-galaxy", "symbol": "gal", "name": "Project Galaxy" }, - { "id": "project-inverse", "symbol": "xiv", "name": "Project Inverse" }, - { "id": "projectmars", "symbol": "mars", "name": "ProjectMars" }, - { "id": "project-oasis", "symbol": "oasis", "name": "ProjectOasis" }, - { "id": "project-one-whale", "symbol": "pow", "name": "Project: One Whale" }, - { "id": "project-pai", "symbol": "pai", "name": "Project Pai" }, - { "id": "project-quantum", "symbol": "qbit", "name": "Project Quantum" }, - { "id": "project-with", "symbol": "wiken", "name": "Project WITH" }, - { "id": "projectx", "symbol": "xil", "name": "ProjectX" }, - { "id": "project-x", "symbol": "nanox", "name": "Project-X" }, - { "id": "project-x-nodes", "symbol": "pxt2", "name": "Project X Nodes" }, - { "id": "projekt-gold", "symbol": "gold", "name": "Projekt Gold" }, - { "id": "prometeus", "symbol": "prom", "name": "Prometeus" }, - { "id": "prometheus", "symbol": "phi", "name": "Prometheus" }, - { "id": "prometheus-token", "symbol": "pro", "name": "Prometheus Token" }, - { "id": "promise-token", "symbol": "promise", "name": "Promise Token" }, - { "id": "promodio", "symbol": "pmd", "name": "Promodio" }, - { "id": "promo-swipe-coin", "symbol": "psc", "name": "Promo Swipe Coin" }, - { "id": "promotionchain", "symbol": "pc", "name": "PromotionChain" }, - { "id": "proof-of-liquidity", "symbol": "pol", "name": "Proof Of Liquidity" }, - { "id": "propel-token", "symbol": "pel", "name": "Propel Token" }, - { "id": "propersix", "symbol": "psix", "name": "ProperSix" }, - { - "id": "property-blockchain-trade", - "symbol": "pbt", - "name": "PROPERTY BLOCKCHAIN TRADE" - }, - { "id": "prophecy", "symbol": "pry", "name": "Prophecy" }, - { "id": "props", "symbol": "props", "name": "Props Token" }, - { "id": "propy", "symbol": "pro", "name": "Propy" }, - { - "id": "proshares-bitcoin-strategy-etf", - "symbol": "bito", - "name": "ProShares Bitcoin Strategy ETF" - }, - { "id": "prosper", "symbol": "pros", "name": "Prosper" }, - { "id": "prostarter-token", "symbol": "prot", "name": "ProStarter" }, - { "id": "proswap", "symbol": "pros", "name": "ProSwap" }, - { "id": "protector-roge", "symbol": "proge", "name": "Protector Roge" }, - { "id": "proteus-finance", "symbol": "proteus", "name": "Proteus Finance" }, - { "id": "protocol-finance", "symbol": "pfi", "name": "Protocol Finance" }, - { "id": "protofi", "symbol": "proto", "name": "Protofi" }, - { "id": "proton", "symbol": "xpr", "name": "Proton" }, - { "id": "proton-loan", "symbol": "loan", "name": "Proton Loan" }, - { "id": "proton-token", "symbol": "ptt", "name": "Proton Token" }, - { "id": "proud-money", "symbol": "proud", "name": "Proud Money" }, - { "id": "provoco", "symbol": "voco", "name": "Provoco" }, - { "id": "proxeus", "symbol": "xes", "name": "Proxeus" }, - { "id": "proximax", "symbol": "xpx", "name": "ProximaX" }, - { "id": "proxy", "symbol": "prxy", "name": "Proxy" }, - { "id": "proxynode", "symbol": "prx", "name": "ProxyNode" }, - { "id": "pryz", "symbol": "pryz", "name": "Pryz" }, - { "id": "pshare", "symbol": "pshare", "name": "pSHARE" }, - { "id": "pspace", "symbol": "pspace", "name": "pSPACE" }, - { "id": "pstake-finance", "symbol": "pstake", "name": "pSTAKE Finance" }, - { - "id": "pstake-staked-atom", - "symbol": "stkatom", - "name": "pSTAKE Staked ATOM" - }, - { "id": "psule", "symbol": "psule", "name": "Sule" }, - { "id": "psyche", "symbol": "usd1", "name": "Psyche" }, - { "id": "psycho-doge", "symbol": "psychodoge", "name": "Psycho Doge" }, - { "id": "psy-coin", "symbol": "psy", "name": "PSY Coin" }, - { "id": "psyoptions", "symbol": "psy", "name": "PsyOptions" }, - { "id": "ptokens-btc", "symbol": "pbtc", "name": "pTokens BTC" }, - { "id": "ptokens-ore", "symbol": "ore", "name": "ORE Network" }, - { "id": "pube-finance", "symbol": "pube", "name": "Pube Finance" }, - { "id": "pub-finance", "symbol": "pint", "name": "Pub Finance" }, - { - "id": "public-index-network", - "symbol": "pin", - "name": "Public Index Network" - }, - { "id": "public-mint", "symbol": "mint", "name": "Public Mint" }, - { "id": "publish", "symbol": "news", "name": "PUBLISH" }, - { "id": "puddingswap", "symbol": "pud", "name": "PuddingSwap" }, - { "id": "pudgy-pups-club", "symbol": "pups", "name": "Pudgy Pups Club" }, - { "id": "pudgy-vault-nftx", "symbol": "pudgy", "name": "PUDGY Vault (NFTX)" }, - { "id": "puff", "symbol": "puff", "name": "PUFF" }, - { "id": "puff-santa", "symbol": "puffsanta", "name": "Puff Santa" }, - { "id": "puglife", "symbol": "pugl", "name": "PugLife" }, - { "id": "puli-inu", "symbol": "puli", "name": "Puli" }, - { "id": "pulsar-token", "symbol": "$pulsar", "name": "Pulsar Token" }, - { "id": "pulsedoge", "symbol": "pulsedoge", "name": "PulseDoge" }, - { "id": "pulsemoon", "symbol": "pulsemoon", "name": "PulseMoon" }, - { "id": "pulsepad", "symbol": "plspad", "name": "PulsePad" }, - { "id": "pulse-token", "symbol": "pulse", "name": "Pulse Token" }, - { "id": "pumapay", "symbol": "pma", "name": "PumaPay" }, - { - "id": "puml-better-health", - "symbol": "puml", - "name": "PUML Better Health" - }, - { "id": "pump-coin", "symbol": "pump", "name": "Pump Coin" }, - { "id": "pumpkin-inu", "symbol": "pumpkin", "name": "Pumpkin Inu" }, - { "id": "pumpkin-punks", "symbol": "ppunks", "name": "Pumpkin Punks" }, - { "id": "pumpshibax", "symbol": "pshibax", "name": "PumpShibaX" }, - { "id": "pumptopia", "symbol": "ptpa", "name": "Pumptopia" }, - { "id": "pumpy-farm", "symbol": "pmp", "name": "Pumpy Farm" }, - { "id": "punch", "symbol": "punch", "name": "Punch" }, - { "id": "pundi-x", "symbol": "npxs", "name": "Pundi X [OLD]" }, - { "id": "pundi-x-2", "symbol": "pundix", "name": "Pundi X" }, - { "id": "pundi-x-nem", "symbol": "npxsxem", "name": "Pundi X NEM" }, - { "id": "pundi-x-purse", "symbol": "purse", "name": "Pundi X PURSE" }, - { "id": "punk-floor", "symbol": "floor", "name": "PUNK Floor" }, - { "id": "punk-network", "symbol": "punk", "name": "Punk.Network" }, - { "id": "punks-comic", "symbol": "punks", "name": "PUNKS Comic" }, - { "id": "punks-comic-pow", "symbol": "pow", "name": "Metahero Universe" }, - { "id": "punk-shiba", "symbol": "PUNKS", "name": "Punk Shiba" }, - { "id": "punk-vault-nftx", "symbol": "punk", "name": "Punk Vault (NFTX)" }, - { "id": "pup-doge", "symbol": "pupdoge", "name": "Pup Doge" }, - { "id": "pupper", "symbol": "pup", "name": "Pupper" }, - { "id": "puppy-doge", "symbol": "puppy", "name": "Puppy Doge" }, - { "id": "puppy-token", "symbol": "puppy", "name": "Puppy Token" }, - { "id": "pups-milk", "symbol": "milk", "name": "Pups Milk" }, - { "id": "purefi", "symbol": "ufi", "name": "PureFi" }, - { "id": "puregold-token", "symbol": "pgpay", "name": "PGPay" }, - { "id": "purfect-network", "symbol": "pur", "name": "Purfect Network" }, - { "id": "puriever", "symbol": "pure", "name": "Puriever" }, - { - "id": "purple-butterfly-trading", - "symbol": "pbtt", - "name": "Purple Butterfly Trading" - }, - { - "id": "purple-floki-inu", - "symbol": "purplefloki", - "name": "Purple Floki Inu" - }, - { "id": "purpose", "symbol": "prps", "name": "Purpose" }, - { "id": "purr-vault-nftx", "symbol": "purr", "name": "PURR Vault (NFTX)" }, - { "id": "pusd", "symbol": "pusd", "name": "PUSD" }, - { "id": "pussy-financial", "symbol": "pussy", "name": "Pussy Financial" }, - { "id": "putincoin", "symbol": "put", "name": "PUTinCoin" }, - { - "id": "puzzle-and-dragons-x", - "symbol": "padx", - "name": "Puzzle And Dragons X" - }, - { "id": "pw-gold", "symbol": "pwg", "name": "PW-GOLD" }, - { "id": "pwrd-stablecoin", "symbol": "pwrd", "name": "PWRD Stablecoin" }, - { "id": "pye", "symbol": "pye", "name": "PYE" }, - { "id": "pylon-eco-token", "symbol": "petn", "name": "Pylon Eco Token" }, - { "id": "pylon-finance", "symbol": "pylon", "name": "Pylon Finance" }, - { "id": "pylon-network", "symbol": "pylnt", "name": "Pylon Network" }, - { "id": "pylon-protocol", "symbol": "mine", "name": "Pylon Protocol" }, - { "id": "pyram-token", "symbol": "pyram", "name": "Pyram Token" }, - { "id": "pyrexcoin", "symbol": "gpyx", "name": "GoldenPyrex" }, - { "id": "pyrk", "symbol": "pyrk", "name": "Pyrk" }, - { "id": "pyromaniac", "symbol": "pyro", "name": "Pyroworld" }, - { "id": "pyro-network", "symbol": "pyro", "name": "PYRO Network" }, - { "id": "pyrrho-defi", "symbol": "PYO", "name": "Pyrrho" }, - { "id": "q8e20-token", "symbol": "q8e20", "name": "Q8E20 Token" }, - { "id": "qanplatform", "symbol": "qanx", "name": "QANplatform" }, - { "id": "qao", "symbol": "\u0026#127760;", "name": "QAO" }, - { "id": "qash", "symbol": "qash", "name": "QASH" }, - { "id": "qatar-2022", "symbol": "fwc", "name": "Qatar 2022" }, - { "id": "qawalla", "symbol": "qwla", "name": "Qawalla" }, - { "id": "qbao", "symbol": "qbt", "name": "Qbao" }, - { "id": "qcash", "symbol": "qc", "name": "Qcash" }, - { "id": "qchi", "symbol": "qch", "name": "QChi" }, - { "id": "qchi-chain", "symbol": "qhc", "name": "QChi Chain" }, - { - "id": "q-dao-governance-token-v1-0", - "symbol": "qdao", - "name": "Q DAO Governance token v1.0" - }, - { "id": "qfinance", "symbol": "qfi", "name": "QFinance" }, - { - "id": "qian-second-generation-dollar", - "symbol": "qsd", - "name": "QIAN Second Generation Dollar" - }, - { "id": "qi-dao", "symbol": "qi", "name": "Qi Dao" }, - { "id": "qiibee", "symbol": "qbx", "name": "qiibee" }, - { "id": "qiswap", "symbol": "qi", "name": "QiSwap" }, - { "id": "qitchain-network", "symbol": "qtc", "name": "Qitcoin" }, - { "id": "qitmeer-network", "symbol": "meer", "name": "Qitmeer Network" }, - { "id": "qlink", "symbol": "qlc", "name": "QLC Chain" }, - { "id": "qmall", "symbol": "qmall", "name": "Qmall" }, - { "id": "qmcoin", "symbol": "qmc", "name": "QMCoin" }, - { "id": "qobit", "symbol": "qob", "name": "Qobit" }, - { "id": "qoiniq", "symbol": "qiq", "name": "QoinIQ" }, - { "id": "qowatt", "symbol": "qwt", "name": "QoWatt" }, - { "id": "qqq-token", "symbol": "qqq", "name": "Poseidon Network" }, - { - "id": "qqq-tokenized-stock-defichain", - "symbol": "dQQQ", - "name": "Invesco QQQ Trust Defichain" - }, - { "id": "qredit", "symbol": "xqr", "name": "Qredit" }, - { "id": "qredo", "symbol": "qrdo", "name": "Qredo" }, - { "id": "qrkita-token", "symbol": "qrt", "name": "Qrkita Token" }, - { "id": "qshare", "symbol": "qshare", "name": "QShare" }, - { "id": "qtum", "symbol": "qtum", "name": "Qtum" }, - { "id": "quadency", "symbol": "quad", "name": "Quadency" }, - { "id": "quadrant-protocol", "symbol": "equad", "name": "Quadrant Protocol" }, - { "id": "quai-dao", "symbol": "quai", "name": "Quai Dao" }, - { "id": "quam-network", "symbol": "quam", "name": "Quam Network" }, - { "id": "quannabu", "symbol": "qbu", "name": "Quannabu" }, - { "id": "quantfury", "symbol": "qtf", "name": "Quantfury" }, - { "id": "quantic", "symbol": "quantic", "name": "Quantic" }, - { "id": "quantis", "symbol": "quan", "name": "Quantis" }, - { "id": "quantland", "symbol": "qlt", "name": "Quantland" }, - { "id": "quant-network", "symbol": "qnt", "name": "Quant" }, - { "id": "quantstamp", "symbol": "qsp", "name": "Quantstamp" }, - { "id": "quantum-assets", "symbol": "qa", "name": "Quantum Assets" }, - { - "id": "quantum-resistant-ledger", - "symbol": "qrl", - "name": "Quantum Resistant Ledger" - }, - { "id": "quantum-tech", "symbol": "qua", "name": "Quantum Tech" }, - { "id": "quark", "symbol": "qrk", "name": "Quark" }, - { "id": "quark-chain", "symbol": "qkc", "name": "QuarkChain" }, - { "id": "quartz", "symbol": "qtz", "name": "Quartz" }, - { "id": "quartz-defi", "symbol": "quartz", "name": "Quartz.defi" }, - { - "id": "quartz-defi-ashare", - "symbol": "ashare", - "name": "quartz.defi AShare" - }, - { "id": "quasacoin", "symbol": "qua", "name": "Quasacoin" }, - { "id": "quasarcoin", "symbol": "qac", "name": "Quasarcoin" }, - { "id": "quattro-tech", "symbol": "qtech", "name": "Quattro Tech" }, - { "id": "qube-2", "symbol": "qube", "name": "Qube" }, - { "id": "qubism", "symbol": "qub", "name": "Qubism" }, - { "id": "qubit", "symbol": "qbt", "name": "Qubit" }, - { "id": "quebecoin", "symbol": "qbc", "name": "Quebecoin" }, - { "id": "queenbee", "symbol": "qbz", "name": "QUEENBEE" }, - { "id": "quick", "symbol": "quick", "name": "Quickswap" }, - { "id": "quickchart", "symbol": "quickchart", "name": "QuickChart" }, - { "id": "quick-mining", "symbol": "qm", "name": "Quick Mining" }, - { - "id": "quickswap-wormhole", - "symbol": "quick", - "name": "Quickswap (Wormhole)" - }, - { "id": "quickx-protocol", "symbol": "qcx", "name": "QuickX Protocol" }, - { "id": "quidax", "symbol": "qdx", "name": "Quidax" }, - { "id": "quidd", "symbol": "quidd", "name": "Quidd" }, - { "id": "quid-ika", "symbol": "QUID", "name": "Quid Ika" }, - { "id": "quid-token", "symbol": "quid", "name": "Quid Token" }, - { "id": "quik", "symbol": "quik", "name": "Quik" }, - { "id": "quinads", "symbol": "quin", "name": "QUINADS" }, - { "id": "quincoin", "symbol": "qin", "name": "QUINCOIN" }, - { - "id": "quipuswap-governance-token", - "symbol": "quipu", - "name": "QuipuSwap Governance Token" - }, - { "id": "quiverx", "symbol": "qrx", "name": "QuiverX" }, - { "id": "quix-defi-index", "symbol": "qdi", "name": "Quix Defi Index" }, - { "id": "quizdrop", "symbol": "qdrop", "name": "QuizDrop" }, - { "id": "quiztok", "symbol": "qtcon", "name": "Quiztok" }, - { "id": "quota", "symbol": "qta", "name": "QUOTA" }, - { "id": "quoth", "symbol": "quoth", "name": "Quoth" }, - { "id": "quotient", "symbol": "xqn", "name": "Quotient" }, - { "id": "quras-token", "symbol": "xqc", "name": "Quras Token" }, - { "id": "qwertycoin", "symbol": "qwc", "name": "Qwertycoin" }, - { "id": "r34p", "symbol": "r34p", "name": "R34P" }, - { "id": "rabbit-finance", "symbol": "rabbit", "name": "Rabbit Finance" }, - { "id": "rabet", "symbol": "rbt", "name": "Rabet" }, - { "id": "rac", "symbol": "rac", "name": "RAC" }, - { "id": "racefi", "symbol": "racefi", "name": "RaceFi" }, - { "id": "racex", "symbol": "racex", "name": "RaceX" }, - { - "id": "racing-club-fan-token", - "symbol": "racing", - "name": "Racing Club Fan Token" - }, - { "id": "racoon", "symbol": "rac", "name": "Racøøn" }, - { "id": "rad", "symbol": "rad", "name": "RAD" }, - { "id": "radar", "symbol": "radar", "name": "Radar" }, - { - "id": "radditarium-network", - "symbol": "raddit", - "name": "Radditarium Network" - }, - { "id": "radi", "symbol": "radi", "name": "RADI" }, - { "id": "radial-finance", "symbol": "rdl", "name": "Radial Finance" }, - { "id": "radicle", "symbol": "rad", "name": "Radicle" }, - { "id": "radio-caca", "symbol": "raca", "name": "Radio Caca" }, - { "id": "radio-hero", "symbol": "RAHO", "name": "Radio Hero" }, - { "id": "radium", "symbol": "val", "name": "Validity" }, - { "id": "radix", "symbol": "xrd", "name": "Radix" }, - { "id": "radom", "symbol": "dom", "name": "Radom" }, - { "id": "rae-token", "symbol": "rae", "name": "Receive Access Ecosystem" }, - { "id": "rage-fan", "symbol": "rage", "name": "Rage.Fan" }, - { "id": "ragnarokdao", "symbol": "rgk", "name": "RagnarokDAO" }, - { "id": "rai", "symbol": "rai", "name": "Rai Reflex Index" }, - { "id": "raicoin", "symbol": "rai", "name": "Raicoin" }, - { "id": "raiden-network", "symbol": "rdn", "name": "Raiden Network Token" }, - { "id": "raider-aurum", "symbol": "aurum", "name": "Raider Aurum" }, - { "id": "raid-token", "symbol": "raid", "name": "Raid Token" }, - { "id": "rai-finance", "symbol": "sofi", "name": "RAI Finance" }, - { "id": "railgun", "symbol": "rail", "name": "Railgun" }, - { "id": "railnode", "symbol": "TRAIN", "name": "RailNode" }, - { "id": "rainbowtoken", "symbol": "rainbowtoken", "name": "RainbowToken" }, - { "id": "rainbow-token", "symbol": "rnbw", "name": "HaloDAO" }, - { "id": "rainbow-token-2", "symbol": "rbw", "name": "Rainbow Token" }, - { "id": "rainicorn", "symbol": "raini", "name": "Rainicorn" }, - { "id": "rainmaker-games", "symbol": "rain", "name": "Rainmaker Games" }, - { "id": "rain-network", "symbol": "rain", "name": "RAIN Network" }, - { - "id": "raised-in-oblivion-x", - "symbol": "riox", - "name": "Raised in Oblivion X" - }, - { "id": "rai-token", "symbol": "rai", "name": "Rai Token" }, - { "id": "raja-inu", "symbol": "rajainu", "name": "Raja Inu" }, - { "id": "rake-finance", "symbol": "rak", "name": "Rake Finance" }, - { "id": "rakon", "symbol": "rkn", "name": "RAKON" }, - { "id": "raku-coin", "symbol": "rakuc", "name": "Raku Coin" }, - { "id": "rally-2", "symbol": "rly", "name": "Rally" }, - { "id": "rally-solana", "symbol": "srly", "name": "Rally (Solana)" }, - { "id": "ramenswap", "symbol": "ramen", "name": "RamenSwap" }, - { "id": "ramifi", "symbol": "ram", "name": "Ramifi Protocol" }, - { "id": "rammus", "symbol": "rammus", "name": "Rammus" }, - { "id": "ramp", "symbol": "ramp", "name": "RAMP" }, - { "id": "rams", "symbol": "prams", "name": "Rams - Super Bowl" }, - { "id": "random", "symbol": "rndm", "name": "Random" }, - { "id": "rangers-fan-token", "symbol": "rft", "name": "Rangers Fan Token" }, - { "id": "rangers-protocol-gas", "symbol": "rpg", "name": "Rangers Protocol" }, - { "id": "rango-exchange", "symbol": "rango", "name": "Rango Exchange" }, - { "id": "rankerdao", "symbol": "ranker", "name": "RankerDao" }, - { "id": "rank-token", "symbol": "rank", "name": "Rank Token" }, - { - "id": "ran-online-crypto-world", - "symbol": "rcw", - "name": "Ran Online Crypto World" - }, - { "id": "ran-x-crypto", "symbol": "rxc", "name": "Ran x Crypto" }, - { "id": "rapids", "symbol": "rpd", "name": "Rapids" }, - { "id": "rapidz", "symbol": "rpzx", "name": "Rapidz" }, - { "id": "raptoreum", "symbol": "rtm", "name": "Raptoreum" }, - { "id": "raptor-finance", "symbol": "raptr", "name": "Raptor Finance" }, - { "id": "rare", "symbol": "rare", "name": "Rare" }, - { "id": "rarest", "symbol": "rarest", "name": "RAREST" }, - { "id": "rarible", "symbol": "rari", "name": "Rarible" }, - { - "id": "rari-governance-token", - "symbol": "rgt", - "name": "Rari Governance Token" - }, - { - "id": "rari-governance-token-wormhole", - "symbol": "rgt", - "name": "Rari Governance Token (Wormhole)" - }, - { "id": "rari-token", "symbol": "rari", "name": "Rari Token" }, - { "id": "rarx", "symbol": "rarx", "name": "Rarx" }, - { "id": "rasko", "symbol": "rasko", "name": "rASKO" }, - { "id": "rasta-finance", "symbol": "rasta", "name": "Rasta Finance" }, - { "id": "rate3", "symbol": "rte", "name": "Rate3" }, - { "id": "ratecoin", "symbol": "xra", "name": "Ratecoin" }, - { "id": "ratio-finance", "symbol": "ratio", "name": "Ratio Protocol" }, - { "id": "ratoken", "symbol": "ratoken", "name": "Ratoken" }, - { "id": "ratrace", "symbol": "ratrace", "name": "RatRace" }, - { "id": "ratscoin", "symbol": "rats", "name": "Ratscoin" }, - { "id": "raven", "symbol": "raven", "name": "Raven" }, - { "id": "ravencoin", "symbol": "rvn", "name": "Ravencoin" }, - { "id": "ravencoin-classic", "symbol": "rvc", "name": "Ravencoin Classic" }, - { "id": "raven-dark", "symbol": "xrd", "name": "Raven Dark" }, - { "id": "ravendex", "symbol": "rave", "name": "Ravendex" }, - { "id": "raven-protocol", "symbol": "raven", "name": "Raven Protocol" }, - { "id": "raya-crypto", "symbol": "raya", "name": "RAYA Crypto" }, - { "id": "raydium", "symbol": "ray", "name": "Raydium" }, - { "id": "raydium-wormhole", "symbol": "ray", "name": "Raydium (Wormhole)" }, - { "id": "rayons-energy", "symbol": "rayons", "name": "Rayons Energy" }, - { "id": "raze-network", "symbol": "raze", "name": "Raze Network" }, - { "id": "razor-network", "symbol": "razor", "name": "Razor Network" }, - { "id": "rbch", "symbol": "rbch", "name": "rBCH" }, - { "id": "rbxsamurai", "symbol": "rbxs", "name": "RBXSamurai" }, - { "id": "rbx-token", "symbol": "rbx", "name": "RBX Token" }, - { "id": "rccc", "symbol": "rccc", "name": "RCCC" }, - { "id": "rchain", "symbol": "REV", "name": "RChain" }, - { "id": "rdctoken", "symbol": "rdct", "name": "RDCToken" }, - { "id": "realbox", "symbol": "reb", "name": "Realbox" }, - { "id": "realfevr", "symbol": "fevr", "name": "RealFevr" }, - { - "id": "realfinance-network", - "symbol": "refi", - "name": "Realfinance Network" - }, - { "id": "realio-network", "symbol": "rio", "name": "Realio Network" }, - { - "id": "realital-metaverse", - "symbol": "reta", - "name": "Realital Metaverse" - }, - { "id": "reallink", "symbol": "real", "name": "RealLink" }, - { "id": "realliq", "symbol": "rlq", "name": "Realliq" }, - { "id": "realm", "symbol": "realm", "name": "Realm" }, - { - "id": "realms-of-ethernity", - "symbol": "reth", - "name": "Realms of Ethernity" - }, - { "id": "real-realm", "symbol": "real", "name": "Real Realm" }, - { - "id": "realtoken-s-13895-saratoga-st-detroit-mi", - "symbol": "realtoken-s-13895-saratoga-st-detroit-mi", - "name": "RealT Token - 13895 Saratoga St, Detroit, MI 48205" - }, - { "id": "realtract", "symbol": "ret", "name": "RealTract" }, - { "id": "realy-metaverse", "symbol": "real", "name": "Realy Metaverse" }, - { "id": "reapchain", "symbol": "reap", "name": "ReapChain" }, - { "id": "reaper-token", "symbol": "reaper", "name": "Reaper Token" }, - { - "id": "rebase-aggregator-capital", - "symbol": "$react", - "name": "Rebase Aggregator Capital" - }, - { - "id": "rebasing-liquidity", - "symbol": "delta rlp", - "name": "Rebasing Liquidity" - }, - { "id": "rebel-bots", "symbol": "rbls", "name": "Rebel Bots" }, - { "id": "rebellion-dao", "symbol": "reb", "name": "Rebellion DAO" }, - { - "id": "rebellion-protocol", - "symbol": "rebl", - "name": "Rebellion Protocol" - }, - { "id": "rebeltradertoken", "symbol": "rtt", "name": "RebelTraderToken" }, - { "id": "rebit", "symbol": "keyt", "name": "Rebit" }, - { "id": "recast1", "symbol": "R1", "name": "Recast1" }, - { "id": "recharge", "symbol": "rcg", "name": "Recharge" }, - { "id": "recharge-finance", "symbol": "r3fi", "name": "Recharge Finance" }, - { "id": "recklessape", "symbol": "reap", "name": "RecklessApe" }, - { - "id": "recovery-right-token", - "symbol": "rrt", - "name": "Recovery Right Token" - }, - { "id": "rec-token", "symbol": "rec", "name": "Rec Token" }, - { "id": "red", "symbol": "red", "name": "Red" }, - { "id": "redbux", "symbol": "redbux", "name": "RedBUX" }, - { "id": "redchillies", "symbol": "redc", "name": "RedChillies" }, - { "id": "reddcoin", "symbol": "rdd", "name": "Reddcoin" }, - { "id": "reddoge", "symbol": "reddoge", "name": "RedDoge" }, - { "id": "redfeg", "symbol": "redfeg", "name": "RedFeg" }, - { "id": "red-floki", "symbol": "redfloki", "name": "Red Floki" }, - { "id": "redfox-labs-2", "symbol": "rfox", "name": "RedFOX Labs" }, - { "id": "redi", "symbol": "redi", "name": "REDi" }, - { "id": "red-kishu", "symbol": "redkishu", "name": "Red Kishu" }, - { - "id": "redlight-node-district", - "symbol": "playmates", - "name": "Redlight Node District" - }, - { "id": "redmars", "symbol": "rmars", "name": "REDMARS" }, - { "id": "redpanda-earth", "symbol": "redpanda", "name": "RedPanda Earth" }, - { "id": "red-pill", "symbol": "rpp", "name": "Red Pill" }, - { "id": "red-pulse", "symbol": "phb", "name": "Phoenix Global [OLD]" }, - { "id": "redshiba", "symbol": "redshiba", "name": "RedShiba" }, - { "id": "red-shiba-token", "symbol": "rst", "name": "Red Shiba Token" }, - { "id": "redzilla", "symbol": "redzilla", "name": "RedZilla" }, - { "id": "reecoin", "symbol": "ree", "name": "ReeCoin" }, - { "id": "reefer-token", "symbol": "reefer", "name": "Reefer Token" }, - { "id": "reef-finance", "symbol": "reef", "name": "Reef Finance" }, - { "id": "reesykle", "symbol": "sycle", "name": "Reesykle" }, - { "id": "refereum", "symbol": "rfr", "name": "Refereum" }, - { "id": "ref-finance", "symbol": "ref", "name": "Ref Finance" }, - { "id": "refi", "symbol": "refi", "name": "Reimagined Finance" }, - { "id": "refinable", "symbol": "fine", "name": "Refinable" }, - { "id": "reflect-finance", "symbol": "rfi", "name": "reflect.finance" }, - { "id": "reflecto", "symbol": "reflecto", "name": "Reflecto" }, - { "id": "reflex", "symbol": "rfx", "name": "Reflex" }, - { - "id": "reflexer-ungovernance-token", - "symbol": "flx", - "name": "Reflexer Ungovernance Token" - }, - { - "id": "reflex-finance-v2", - "symbol": "reflex", - "name": "Reflex Finance V2" - }, - { - "id": "reforestation-mahogany", - "symbol": "RMOG", - "name": "Reforestation Mahogany" - }, - { "id": "refork", "symbol": "efk", "name": "ReFork" }, - { "id": "refugees-token", "symbol": "$RFG", "name": "Refugees" }, - { "id": "regen", "symbol": "regen", "name": "Regen" }, - { "id": "regiment-finance", "symbol": "rtf", "name": "Regiment Finance" }, - { "id": "regularpresale", "symbol": "REGU", "name": "RegularPresale" }, - { "id": "rekt-2", "symbol": "rkt", "name": "Rekt" }, - { "id": "relay-token", "symbol": "relay", "name": "Relay Token" }, - { "id": "relbit", "symbol": "rlb", "name": "Relbit" }, - { "id": "release-ico-project", "symbol": "rel", "name": "RELEASE" }, - { "id": "relevant", "symbol": "rel", "name": "Relevant" }, - { "id": "relite-finance", "symbol": "reli", "name": "Relite Finance" }, - { "id": "reltime", "symbol": "rtc", "name": "Reltime" }, - { "id": "rematicegc", "symbol": "rmtx", "name": "RematicEGC" }, - { "id": "remex", "symbol": "rmx", "name": "RemeX" }, - { "id": "remita-coin", "symbol": "remit", "name": "Remita Coin" }, - { "id": "remme", "symbol": "rem", "name": "Remme" }, - { "id": "remnant-labs", "symbol": "remn", "name": "Remnant Labs" }, - { "id": "rena-finance", "symbol": "rena", "name": "RENA Finance" }, - { "id": "renbtc", "symbol": "renbtc", "name": "renBTC" }, - { "id": "render-token", "symbol": "rndr", "name": "Render Token" }, - { "id": "rendoge", "symbol": "rendoge", "name": "renDOGE" }, - { - "id": "renewableelectronicenergycoin", - "symbol": "reec", - "name": "Renewable Electronic Energy Coin" - }, - { "id": "renfil", "symbol": "renfil", "name": "renFIL" }, - { "id": "renrenbit", "symbol": "rrb", "name": "Renrenbit" }, - { "id": "rentberry", "symbol": "berry", "name": "Rentberry" }, - { "id": "rentible", "symbol": "rnb", "name": "Rentible" }, - { "id": "renzec", "symbol": "renzec", "name": "renZEC" }, - { "id": "repo", "symbol": "repo", "name": "Repo Coin" }, - { "id": "represent", "symbol": "rpt", "name": "Represent" }, - { "id": "reptilian", "symbol": "rptc", "name": "Reptilian" }, - { "id": "republic-credits", "symbol": "rpc", "name": "Republic Credits" }, - { "id": "republic-of-dogs", "symbol": "rod", "name": "Republic of Dogs" }, - { "id": "republic-protocol", "symbol": "ren", "name": "REN" }, - { "id": "request-network", "symbol": "req", "name": "Request" }, - { "id": "reserve", "symbol": "rsv", "name": "Reserve" }, - { - "id": "reserve-rights-token", - "symbol": "rsr", - "name": "Reserve Rights Token" - }, - { - "id": "reserve-rights-wormhole", - "symbol": "rsr", - "name": "Reserve Rights (Wormhole)" - }, - { "id": "resfinex-token", "symbol": "res", "name": "Resfinex Token" }, - { - "id": "resource-protocol", - "symbol": "source", - "name": "ReSource Protocol" - }, - { - "id": "restore-truth-token", - "symbol": "rtt", - "name": "Restore Truth Token" - }, - { "id": "reth", "symbol": "reth", "name": "rETH" }, - { "id": "reth2", "symbol": "reth2", "name": "rETH2" }, - { "id": "retire-token", "symbol": "retire", "name": "Retire Token" }, - { "id": "retrocade", "symbol": "rc", "name": "RetroCade" }, - { "id": "retro-defi", "symbol": "rcube", "name": "Retro DEFI" }, - { "id": "retromoon", "symbol": "retro", "name": "Retromoon" }, - { "id": "return-of-the-king", "symbol": "rok", "name": "Return of The King" }, - { "id": "reucoin", "symbol": "reu", "name": "REUCOIN" }, - { "id": "revain", "symbol": "rev", "name": "Revain" }, - { "id": "revault-network", "symbol": "reva", "name": "Revault Network" }, - { "id": "revenue-coin", "symbol": "rvc", "name": "Revenue Coin" }, - { "id": "reverse", "symbol": "rvrs", "name": "Reverse" }, - { "id": "reverse-protocol", "symbol": "rvrs", "name": "Reverse Protocol" }, - { "id": "revest-finance", "symbol": "rvst", "name": "Revest Finance" }, - { "id": "review-capital", "symbol": "recap", "name": "Review Capital" }, - { "id": "revival", "symbol": "RVL", "name": "REVIVAL" }, - { "id": "revive", "symbol": "rve", "name": "Revive" }, - { "id": "revolotto", "symbol": "rvl", "name": "Revolotto" }, - { "id": "revolt", "symbol": "revt", "name": "Revolt" }, - { "id": "revolution", "symbol": "rev", "name": "Revolution" }, - { "id": "revolutiongames", "symbol": "rvlng", "name": "RevolutionGames" }, - { "id": "revolution-populi", "symbol": "rvp", "name": "Revolution Populi" }, - { "id": "revoluzion", "symbol": "rvz", "name": "Revoluzion" }, - { "id": "revolve-games", "symbol": "rpg", "name": "Revolve Games" }, - { "id": "revolver-token", "symbol": "$rvlvr", "name": "Revolver Token" }, - { "id": "revomon", "symbol": "revo", "name": "Revomon" }, - { "id": "revonetwork", "symbol": "revo", "name": "RevoNetwork" }, - { "id": "revuto", "symbol": "revu", "name": "Revuto" }, - { "id": "revv", "symbol": "revv", "name": "REVV" }, - { "id": "reward-cycle", "symbol": "rc", "name": "Reward Cycle" }, - { "id": "reward-cycle-2", "symbol": "RC2", "name": "Reward Cycle 2" }, - { "id": "rewardeum", "symbol": "reum", "name": "Rewardeum" }, - { - "id": "reward-hunters-token", - "symbol": "rht", - "name": "Reward Hunters Token" - }, - { "id": "rewardiqa", "symbol": "rew", "name": "Rewardiqa" }, - { "id": "rewards", "symbol": "rwd", "name": "Rewards" }, - { "id": "rewards-bunny", "symbol": "rbunny", "name": "Rewards Bunny" }, - { "id": "rewardscoin", "symbol": "rwsc", "name": "RewardsCoin" }, - { "id": "rewards-token", "symbol": "rewards", "name": "Rewards Token" }, - { "id": "rewardsx", "symbol": "rex", "name": "RewardsX" }, - { "id": "rex", "symbol": "rex", "name": "Imbrex" }, - { "id": "rfust", "symbol": "rfust", "name": "rfUST" }, - { "id": "rhea-dao", "symbol": "🏔", "name": "Rhea DAO" }, - { "id": "rhegic2", "symbol": "rhegic2", "name": "rHEGIC2" }, - { "id": "rhinos-finance", "symbol": "rho", "name": "Rhinos Finance" }, - { "id": "rhinos-game", "symbol": "rhinos", "name": "rhinos.game" }, - { "id": "rhobusd", "symbol": "rhobusd", "name": "rhoBUSD" }, - { "id": "rhousdc", "symbol": "rhousdc", "name": "rhoUSDC" }, - { "id": "rhousdt", "symbol": "rhousdt", "name": "rhoUSDT" }, - { "id": "rhythm", "symbol": "rhythm", "name": "Rhythm" }, - { "id": "ribbon-finance", "symbol": "rbn", "name": "Ribbon Finance" }, - { "id": "rice", "symbol": "rice", "name": "Rice" }, - { "id": "ricefarm", "symbol": "rice", "name": "RiceFarm" }, - { "id": "riceswap", "symbol": "rice", "name": "RiceSwap" }, - { "id": "rice-wallet", "symbol": "rice", "name": "Rice Wallet" }, - { "id": "rich", "symbol": "rch", "name": "Rich" }, - { "id": "richcity", "symbol": "rich", "name": "RichCity" }, - { "id": "rich-dao", "symbol": "rich", "name": "Rich DAO" }, - { "id": "rich-doge-coin", "symbol": "richdoge 💲", "name": "Rich Doge Coin" }, - { "id": "richie", "symbol": "rich", "name": "Richie" }, - { "id": "richierich-coin", "symbol": "rich", "name": "RichieRich Coin" }, - { "id": "rich-maker", "symbol": "rich", "name": "Rich Maker" }, - { "id": "richochet", "symbol": "ric", "name": "Ricochet" }, - { "id": "richquack", "symbol": "quack", "name": "Rich Quack" }, - { "id": "richway-finance", "symbol": "rich", "name": "Richway.Finance" }, - { "id": "rick-and-morty", "symbol": "RICKMORTY", "name": "Rick And Morty" }, - { "id": "rickmortydoxx", "symbol": "rickmortydoxx", "name": "RickMortyDoxx" }, - { "id": "ricnatum", "symbol": "rcnt", "name": "Ricnatum" }, - { "id": "ride_finance", "symbol": "rides", "name": "Rides Finance" }, - { "id": "ride-my-car", "symbol": "ride", "name": "Ride My Car" }, - { "id": "ridge", "symbol": "ridge", "name": "Ridge" }, - { "id": "ridotto", "symbol": "rdt", "name": "Ridotto" }, - { "id": "riecoin", "symbol": "ric", "name": "Riecoin" }, - { "id": "rifi-united", "symbol": "ru", "name": "RIFI United" }, - { - "id": "rif-token", - "symbol": "rif", - "name": "RSK Infrastructure Framework" - }, - { "id": "rigel-finance", "symbol": "rigel", "name": "Rigel Finance" }, - { "id": "rigel-protocol", "symbol": "rgp", "name": "Rigel Protocol" }, - { "id": "rigoblock", "symbol": "grg", "name": "RigoBlock" }, - { "id": "rijent-coin", "symbol": "rtc", "name": "Rijent Coin" }, - { "id": "rikkei-finance", "symbol": "rifi", "name": "Rikkei Finance" }, - { "id": "rilcoin", "symbol": "ril", "name": "Rilcoin" }, - { "id": "ring", "symbol": "ring", "name": "Ring" }, - { "id": "ring-financial", "symbol": "ring", "name": "RING Financial" }, - { "id": "ring-x-platform", "symbol": "ringx", "name": "RING X PLATFORM" }, - { "id": "rio-defi", "symbol": "rfuel", "name": "RioDeFi" }, - { "id": "riot-racers", "symbol": "riot", "name": "Riot Racers" }, - { "id": "ripae", "symbol": "pae", "name": "Ripae" }, - { "id": "ripio-coin", "symbol": "rpc", "name": "Ripio Coin" }, - { - "id": "ripio-credit-network", - "symbol": "rcn", - "name": "Ripio Credit Network" - }, - { "id": "ripple", "symbol": "xrp", "name": "XRP" }, - { "id": "ripple-alpha", "symbol": "xla", "name": "Ripple Alpha" }, - { "id": "rise", "symbol": "rise", "name": "Rise" }, - { "id": "rise2protocol", "symbol": "ripr", "name": "RISE2PROTOCOL" }, - { "id": "risecity", "symbol": "rsc", "name": "RiseCity" }, - { "id": "risecointoken", "symbol": "rsct", "name": "RiseCoin Token" }, - { "id": "risehero", "symbol": "rise", "name": "RiseHero" }, - { "id": "rise-of-defenders", "symbol": "rdr", "name": "Rise of Defenders" }, - { "id": "rise-of-nebula", "symbol": "ron", "name": "Rise Of Nebula" }, - { "id": "riseupv2", "symbol": "riv2", "name": "RiseUpV2" }, - { "id": "risingsun", "symbol": "rsun", "name": "RisingSun" }, - { "id": "riskmoon", "symbol": "riskmoon", "name": "Riskmoon" }, - { "id": "ritestream", "symbol": "rite", "name": "ritestream" }, - { "id": "rito", "symbol": "rito", "name": "Rito" }, - { "id": "ri-token", "symbol": "ri", "name": "RI Token" }, - { "id": "riverboat", "symbol": "rib", "name": "RiverBoat" }, - { "id": "rivetz", "symbol": "rvt", "name": "Rivetz" }, - { "id": "rivex-erc20", "symbol": "rvx", "name": "Rivex" }, - { "id": "rivrdoge", "symbol": "rivrdoge", "name": "RivrDoge" }, - { "id": "rivrfloki", "symbol": "rivrfloki", "name": "RivrFloki" }, - { "id": "rivrkitty", "symbol": "rkitty", "name": "RivrKitty" }, - { "id": "rivrshiba", "symbol": "rivrshib", "name": "RivrShiba" }, - { "id": "rizen-coin", "symbol": "rzn", "name": "Rizen Coin (Old)" }, - { "id": "rizen-coin-2", "symbol": "rzn", "name": "Rizen Coin" }, - { "id": "rizespor-token", "symbol": "rize", "name": "Rizespor Token" }, - { "id": "rizon", "symbol": "atolo", "name": "RIZON" }, - { "id": "rmrk", "symbol": "rmrk", "name": "RMRK" }, - { "id": "rna-cash", "symbol": "rna", "name": "RNA Cash" }, - { "id": "rna-share", "symbol": "rshare", "name": "RNA Share" }, - { "id": "road", "symbol": "road", "name": "ROAD" }, - { "id": "roaring-twenties", "symbol": "roar", "name": "Roaring Twenties" }, - { "id": "roar-token", "symbol": "roar", "name": "Roar Token" }, - { "id": "roarts", "symbol": "roar", "name": "RoArts" }, - { "id": "robbocoach", "symbol": "rbc", "name": "RobboCoach" }, - { "id": "robet-coin", "symbol": "robet", "name": "RoBet Coin" }, - { "id": "robinhoodprotocol", "symbol": "xrhp", "name": "RobinHoodProtocol" }, - { "id": "robinhoodswap", "symbol": "rbh", "name": "RobinHoodSwap" }, - { "id": "robiniaswap-token", "symbol": "rbs", "name": "RobiniaSwap Token" }, - { "id": "robin-inu", "symbol": "robin", "name": "Robin Inu" }, - { "id": "robinos", "symbol": "rbn", "name": "Robinos" }, - { "id": "robocalls", "symbol": "rc20", "name": "RoboCalls" }, - { "id": "robodoge-coin", "symbol": "robodoge", "name": "RoboDoge Coin" }, - { "id": "robofi-token", "symbol": "vics", "name": "RoboFi Token" }, - { "id": "robohero", "symbol": "robo", "name": "RoboHero" }, - { "id": "robo-inu-finance", "symbol": "rbif", "name": "Robo Inu Finance" }, - { "id": "robonomics-network", "symbol": "xrt", "name": "Robonomics Network" }, - { - "id": "robonomics-web-services", - "symbol": "rws", - "name": "Robonomics Web Services" - }, - { "id": "roboots", "symbol": "rbo", "name": "Roboots" }, - { "id": "roboshib", "symbol": "roboshib", "name": "RoboShib" }, - { "id": "robot", "symbol": "robot", "name": "Robot" }, - { "id": "robotina", "symbol": "rox", "name": "Robotina" }, - { "id": "robo-token", "symbol": "robo", "name": "Robo Token" }, - { "id": "robot-shib", "symbol": "RSHIB", "name": "ROBOT SHIB" }, - { "id": "robot-warriors", "symbol": "metabot", "name": "Robot Warriors" }, - { "id": "robot-wars", "symbol": "war", "name": "Robot Wars" }, - { "id": "robust-token", "symbol": "rbt", "name": "Robust Token" }, - { "id": "rocket", "symbol": "rokt", "name": "Rocket" }, - { "id": "rocket-bunny", "symbol": "bunny", "name": "Rocket Bunny" }, - { "id": "rocketbusd", "symbol": "rocketbusd", "name": "RocketBUSD" }, - { "id": "rocketcoin-2", "symbol": "rocket", "name": "RocketCoin" }, - { "id": "rocketdoge", "symbol": "rd", "name": "RocketDoge" }, - { "id": "rocket-finance", "symbol": "rocket", "name": "Rocket Finance" }, - { "id": "rocket-fund", "symbol": "rkt", "name": "Rocket Fund" }, - { "id": "rocket-launchpad", "symbol": "rckt", "name": "Rocket Launchpad" }, - { "id": "rocket-pool", "symbol": "rpl", "name": "Rocket Pool" }, - { "id": "rocket-pool-eth", "symbol": "rETH", "name": "Rocket Pool ETH" }, - { - "id": "rocket-pool-wormhole", - "symbol": "rpl", - "name": "Rocket Pool (Wormhole)" - }, - { "id": "rocket-raccoon", "symbol": "roc", "name": "Rocket Raccoon" }, - { "id": "rocket-share", "symbol": "srocket", "name": "Rocket Share" }, - { "id": "rocket-shib", "symbol": "rocketshib", "name": "Rocket Shib" }, - { "id": "rockettoken", "symbol": "rktn", "name": "RocketToken [OLD]" }, - { "id": "rockettoken-2", "symbol": "rktn", "name": "RocketToken" }, - { "id": "rocket-vault-rocketx", "symbol": "rvf", "name": "RocketX exchange" }, - { "id": "rocket-zilla", "symbol": "ROZ", "name": "Rocket Zilla" }, - { "id": "rocki", "symbol": "rocki", "name": "Rocki" }, - { "id": "rock-n-rain-coin", "symbol": "rnrc", "name": "Rock N Rain Coin" }, - { - "id": "rock-paper-scissors-token", - "symbol": "rpst", - "name": "Rock Paper Scissors Token" - }, - { "id": "rocks-idle-game", "symbol": "rocks", "name": "Rocks Idle Game" }, - { "id": "rockstar-doge", "symbol": "rockstar", "name": "Rockstar Doge" }, - { "id": "rocky-inu", "symbol": "rocky", "name": "Rocky Inu" }, - { "id": "roco-finance", "symbol": "roco", "name": "Roco Finance" }, - { "id": "rogan", "symbol": "rogan", "name": "Rogan" }, - { "id": "roge", "symbol": "roge", "name": "Rogue Doge" }, - { "id": "rogue-west", "symbol": "rogue", "name": "Rogue West" }, - { "id": "roi", "symbol": "roi", "name": "5ROI" }, - { "id": "roiyal-coin", "symbol": "roco", "name": "ROIyal Coin" }, - { "id": "rokkit-fuel", "symbol": "$rokk", "name": "Rokkit Fuel" }, - { "id": "rollbit-coin", "symbol": "rlb", "name": "Rollbit Coin" }, - { "id": "rollium", "symbol": "rlm", "name": "Rollium" }, - { "id": "rome", "symbol": "rome", "name": "Rome" }, - { "id": "romeo-doge", "symbol": "romeodoge", "name": "Romeo Doge" }, - { "id": "ronin", "symbol": "ron", "name": "Ronin" }, - { "id": "ronin-gamez", "symbol": "roningmz", "name": "Ronin Gamez" }, - { "id": "ronpaulcoin", "symbol": "rpc", "name": "RonPaulCoin" }, - { "id": "roobee", "symbol": "roobee", "name": "Roobee" }, - { "id": "rook", "symbol": "rook", "name": "KeeperDAO" }, - { "id": "rook-token", "symbol": "r0ok", "name": "r0ok Token" }, - { "id": "roonex", "symbol": "rnx", "name": "ROONEX" }, - { "id": "rooster-battle", "symbol": "rice", "name": "Rooster Battle" }, - { "id": "rootkit", "symbol": "root", "name": "Rootkit" }, - { "id": "rootstock", "symbol": "rbtc", "name": "Rootstock RSK" }, - { "id": "rope", "symbol": "$rope", "name": "Rope" }, - { "id": "rope-token", "symbol": "rope", "name": "Rope Token" }, - { "id": "ror-universe", "symbol": "ror", "name": "ROR Universe" }, - { "id": "rose", "symbol": "rose", "name": "Rose" }, - { "id": "roseon-finance", "symbol": "rosn", "name": "Roseon Finance" }, - { "id": "rose-wormhole", "symbol": "rose", "name": "ROSE (Wormhole)" }, - { "id": "ro-slayers", "symbol": "slyr", "name": "RO Slayers" }, - { "id": "rotharium", "symbol": "rth", "name": "Rotharium" }, - { "id": "rotten", "symbol": "rot", "name": "Rotten" }, - { "id": "rotten-floki", "symbol": "rotten", "name": "Rotten Floki" }, - { "id": "rottweiler-inu", "symbol": "rottt", "name": "Rottweiler Inu" }, - { "id": "rougecoin", "symbol": "xrge", "name": "RougeCoin" }, - { "id": "round-dollar", "symbol": "rd", "name": "Round Dollar" }, - { - "id": "roundrobin-protocol-token", - "symbol": "rrt", - "name": "RoundRobin Protocol Token" - }, - { - "id": "roush-fenway-racing-fan-token", - "symbol": "roush", - "name": "Roush Fenway Racing Fan Token" - }, - { "id": "route", "symbol": "route", "name": "Router Protocol" }, - { "id": "rover-inu", "symbol": "rover", "name": "Rover Inu" }, - { "id": "rowan-coin", "symbol": "rwn", "name": "Rowan Coin" }, - { "id": "roxe", "symbol": "roc", "name": "Roxe" }, - { "id": "royalada", "symbol": "royalada", "name": "RoyalADA" }, - { "id": "royal-bet", "symbol": "rbet", "name": "Royal BET" }, - { "id": "royal-bnb", "symbol": "rb", "name": "Royal BNB" }, - { "id": "royal-doge", "symbol": "rdoge", "name": "Royal Doge" }, - { "id": "royale", "symbol": "roya", "name": "Royale" }, - { "id": "royal-flush-coin", "symbol": "rfc", "name": "Royal Flush Coin" }, - { "id": "royal-gold", "symbol": "RGOLD", "name": "Royal Gold" }, - { "id": "royal-protocol", "symbol": "roy", "name": "Royal Protocol" }, - { "id": "rps-league", "symbol": "rps", "name": "Rps League" }, - { "id": "rss3", "symbol": "RSS3", "name": "RSS3" }, - { "id": "rubic", "symbol": "rbc", "name": "Rubic" }, - { "id": "rubix", "symbol": "rbt", "name": "Rubix" }, - { "id": "ruby-currency", "symbol": "rbc", "name": "Ruby Currency" }, - { "id": "ruby-play-network", "symbol": "ruby", "name": "Ruby Play Network" }, - { "id": "rudolph-coin", "symbol": "RUDOLPH", "name": "Rudolph Coin" }, - { "id": "ruff", "symbol": "ruff", "name": "Ruff" }, - { "id": "rug-busters", "symbol": "rugbust", "name": "Rug Busters" }, - { "id": "r-u-generous", "symbol": "rug", "name": "R U Generous" }, - { "id": "rug-proof", "symbol": "rpt", "name": "Rug Proof" }, - { - "id": "rugpull-prevention", - "symbol": "rugpull", - "name": "Rugpull Prevention" - }, - { "id": "rugseekers", "symbol": "SEEK", "name": "RugSeekers" }, - { "id": "rugzombie", "symbol": "zmbe", "name": "RugZombie" }, - { "id": "ruler-protocol", "symbol": "ruler", "name": "Ruler Protocol" }, - { "id": "ruletka", "symbol": "rtk", "name": "Ruletka" }, - { "id": "rumble-gaming", "symbol": "rmbl", "name": "Rumble Gaming" }, - { "id": "rumito", "symbol": "rutc", "name": "Rumito" }, - { "id": "run", "symbol": "run", "name": "Run" }, - { "id": "rune", "symbol": "rune", "name": "Rune" }, - { "id": "rune-shards", "symbol": "rxs", "name": "Rune Shards" }, - { "id": "rupaya", "symbol": "rupx", "name": "Rupaya" }, - { "id": "rupee", "symbol": "rup", "name": "Rupee" }, - { "id": "rupiah-token", "symbol": "idrt", "name": "Rupiah Token" }, - { "id": "rusd", "symbol": "rusd", "name": "rUSD" }, - { "id": "rush", "symbol": "ruc", "name": "Rush" }, - { "id": "rushcoin", "symbol": "rush", "name": "RushCoin" }, - { "id": "rush-defi", "symbol": "rush", "name": "Rush DeFi" }, - { "id": "rushmoon", "symbol": "rush", "name": "RushMoon" }, - { "id": "rush-technology", "symbol": "rush", "name": "Rush Technology" }, - { "id": "russell-coin", "symbol": "rc", "name": "RussellCoin" }, - { "id": "rutheneum", "symbol": "rth", "name": "Rutheneum" }, - { "id": "ruufcoin", "symbol": "ruuf", "name": "RuufCoin" }, - { "id": "ruyi", "symbol": "ruyi", "name": "Ruyi" }, - { "id": "rxcdnatoken", "symbol": "dna", "name": "RxcDnaToken" }, - { "id": "rxcgames", "symbol": "rxcg", "name": "RXCGames" }, - { "id": "rxseed-coin", "symbol": "wsow", "name": "RxSeed Coin" }, - { "id": "ryi-unity", "symbol": "ryiu", "name": "RYI Unity" }, - { "id": "ryo", "symbol": "ryo", "name": "Ryo Currency" }, - { "id": "ryoshimoto", "symbol": "ryoshimoto", "name": "Ryoshimoto" }, - { "id": "ryoshis-vision", "symbol": "ryoshi", "name": "Ryoshis Vision" }, - { "id": "ryoshi-token", "symbol": "ryoshi", "name": "Ryoshi Token" }, - { "id": "ryze-inu", "symbol": "$ryzeinu", "name": "Ryze Inu" }, - { "id": "s4fe", "symbol": "s4f", "name": "S4FE" }, - { "id": "saave", "symbol": "saave", "name": "sAAVE" }, - { "id": "sabac-warrior", "symbol": "sw", "name": "Sabac Warrior" }, - { "id": "sabaka-inu", "symbol": "SABAKA INU", "name": "Sabaka Inu" }, - { "id": "saber", "symbol": "sbr", "name": "Saber" }, - { "id": "saber-wormhole", "symbol": "sbr", "name": "Saber (Wormhole)" }, - { - "id": "saber-wrapped-usd-coin", - "symbol": "susdc-9", - "name": "Saber Wrapped USD Coin" - }, - { "id": "sacks", "symbol": "sacks", "name": "Sacks" }, - { "id": "sacred-tails", "symbol": "st", "name": "Sacred Tails" }, - { "id": "sada", "symbol": "sada", "name": "sADA" }, - { "id": "sadbaby", "symbol": "sdby", "name": "Sadbaby" }, - { "id": "saddle-finance", "symbol": "sdl", "name": "Saddle Finance" }, - { "id": "safcoin", "symbol": "saf", "name": "SafCoin" }, - { "id": "safeaffinity", "symbol": "affinity", "name": "Affinity" }, - { "id": "safeape", "symbol": "usda", "name": "SafeApe" }, - { "id": "safe-baby-shiba", "symbol": "sbsh", "name": "Safe Baby Shiba" }, - { "id": "safebank", "symbol": "safebank", "name": "SafeBank" }, - { "id": "safebank-token", "symbol": "sbank", "name": "SafeBank BSC" }, - { "id": "safebitcoin", "symbol": "safebtc", "name": "SafeBitcoin" }, - { "id": "safeblast", "symbol": "blast", "name": "SafeBlast" }, - { "id": "safebull", "symbol": "safebull", "name": "SafeBull" }, - { "id": "safebuy", "symbol": "sbf", "name": "SAfebuy" }, - { "id": "safecapital", "symbol": "scap", "name": "SafeCapital" }, - { "id": "safecap-token", "symbol": "sfc", "name": "SafeCap Token" }, - { "id": "safechaintoken", "symbol": "sct", "name": "Safechaintoken" }, - { "id": "safe-coin", "symbol": "safe", "name": "Safe" }, - { "id": "safe-coin-2", "symbol": "safe", "name": "SafeCoin" }, - { "id": "safecookie", "symbol": "safecookie", "name": "SafeCookie" }, - { "id": "safecrypto", "symbol": "sco", "name": "SafeCrypto" }, - { "id": "safe-deal", "symbol": "sfd", "name": "SafeDeal" }, - { "id": "safedoge", "symbol": "safedoge", "name": "SafeDoge" }, - { "id": "safedogecoin", "symbol": "SafeDoge", "name": "SafeDogecoin" }, - { "id": "safedog-protocol", "symbol": "safedog", "name": "Safedog Protocol" }, - { "id": "safedollar", "symbol": "sdo", "name": "SafeDollar" }, - { "id": "safe-drive", "symbol": "drive", "name": "Safe Drive" }, - { "id": "safe-earn", "symbol": "safearn", "name": "Safe Earn" }, - { "id": "safeearth", "symbol": "safeearth", "name": "SafeEarth" }, - { "id": "safe-energy", "symbol": "energyx", "name": "Safe Energy" }, - { "id": "safeeth", "symbol": "safeeth", "name": "SafeETH" }, - { "id": "safegem", "symbol": "gems", "name": "SafeGem" }, - { "id": "safehamsters", "symbol": "safehamsters", "name": "SafeHamsters" }, - { "id": "safe-haven", "symbol": "sha", "name": "Safe Haven" }, - { "id": "safehold", "symbol": "safehold", "name": "SafeHold" }, - { "id": "safeinsure", "symbol": "sins", "name": "SafeInsure" }, - { "id": "safelaunch", "symbol": "sfex", "name": "SafeLaunch" }, - { "id": "safelcarus", "symbol": "safeicarus", "name": "SafeIcarus" }, - { "id": "safelight", "symbol": "safelight", "name": "SafeLight" }, - { "id": "safemars", "symbol": "safemars", "name": "Safemars" }, - { "id": "safemars-protocol", "symbol": "smars", "name": "Safemars Protocol" }, - { "id": "safememe", "symbol": "sme", "name": "SafeMeme" }, - { "id": "safemoneybsc", "symbol": "safemoney", "name": "SafeMoneyBSC" }, - { "id": "safemoon", "symbol": "safemoon", "name": "SafeMoon [OLD]" }, - { "id": "safemoon-2", "symbol": "sfm", "name": "SafeMoon" }, - { "id": "safemoonavax", "symbol": "safemoona", "name": "SafemoonAvax" }, - { "id": "safemooncash", "symbol": "safemooncash", "name": "SafeMoonCash" }, - { "id": "safemoon-inu", "symbol": "smi", "name": "SafeMoon Inu" }, - { "id": "safemoon-swap", "symbol": "sfms", "name": "SafeMoon Swap" }, - { "id": "safemoon-zilla", "symbol": "sfz", "name": "Safemoon Zilla" }, - { "id": "safemusk", "symbol": "safemusk", "name": "SafeMusk" }, - { "id": "safenami", "symbol": "safenami", "name": "SafeNami" }, - { "id": "safe-nebula", "symbol": "snb", "name": "Safe Nebula" }, - { "id": "safenotmoon", "symbol": "$SNM", "name": "SafeNotMoon" }, - { "id": "safe-ohm", "symbol": "sohm", "name": "SafeOHM" }, - { "id": "safepal", "symbol": "sfp", "name": "SafePal" }, - { "id": "safepe", "symbol": "loox", "name": "SafePe" }, - { "id": "safeplus", "symbol": "$SPLUSv2", "name": "SafePlus V2" }, - { "id": "safepluto", "symbol": "safepluto", "name": "SafePluto" }, - { "id": "safermoon", "symbol": "safermoon", "name": "SAFERMOON" }, - { "id": "safe-seafood-coin", "symbol": "ssf", "name": "Safe SeaFood Coin" }, - { "id": "safeshiba", "symbol": "safeshib", "name": "SafeShiba" }, - { "id": "safe-shield", "symbol": "sfshld", "name": "Safe Shield" }, - { "id": "safespace", "symbol": "safespace", "name": "SafeSpace" }, - { "id": "safestar", "symbol": "safestar", "name": "Safestar" }, - { "id": "safesun", "symbol": "SAFESUN", "name": "SafeSun" }, - { "id": "safeswap", "symbol": "ssgt", "name": "Safeswap" }, - { "id": "safeswap-online", "symbol": "swap", "name": "SafeSwap Online" }, - { "id": "safeswap-token", "symbol": "ssgtx", "name": "Safeswap Token" }, - { "id": "safetesla", "symbol": "safetesla", "name": "SafeTesla" }, - { "id": "safe-token", "symbol": "SAFE", "name": "SAFE TOKEN" }, - { "id": "safetrees", "symbol": "trees", "name": "Safetrees" }, - { "id": "safe-trip-finance", "symbol": "stf", "name": "Safe Trip Finance" }, - { "id": "safety", "symbol": "sft", "name": "Safety" }, - { "id": "safewages", "symbol": "safew", "name": "SafeWages" }, - { "id": "safewhale", "symbol": "swhal", "name": "SafeWhale" }, - { "id": "safewin", "symbol": "safewin", "name": "SafeWin" }, - { "id": "safewolf", "symbol": "sw", "name": "SafeWolf" }, - { "id": "safexi", "symbol": "sxi", "name": "SafeXI" }, - { "id": "safezone", "symbol": "safezone", "name": "SafeZone" }, - { "id": "saffron-finance", "symbol": "sfi", "name": "saffron.finance" }, - { "id": "safle", "symbol": "safle", "name": "Safle" }, - { "id": "safufide", "symbol": "safest", "name": "SafuFide" }, - { "id": "safutitano", "symbol": "safti", "name": "SafuTitano" }, - { "id": "safuu", "symbol": "safuu", "name": "SAFUU" }, - { - "id": "safuyield-protocol", - "symbol": "safuyield", - "name": "SafuYield Protocol" - }, - { "id": "sai", "symbol": "sai", "name": "Sai" }, - { "id": "sail", "symbol": "sail", "name": "SAIL" }, - { "id": "saint-inu", "symbol": "saint", "name": "Saint Inu" }, - { "id": "saint-token", "symbol": "saint", "name": "Saint Token" }, - { "id": "saitama-inu", "symbol": "saitama", "name": "Saitama Inu" }, - { "id": "saitama-kitty", "symbol": "saikitty", "name": "Saitama Kitty" }, - { - "id": "saitama-samurai", - "symbol": "saitamurai", - "name": "Saitama Samurai" - }, - { "id": "saitamax", "symbol": "saitax", "name": "SaitamaX" }, - { "id": "saitanobi", "symbol": "saitanobi", "name": "Saitanobi" }, - { "id": "saito", "symbol": "saito", "name": "Saito" }, - { "id": "saitoki-inu", "symbol": "saitoki", "name": "Saitoki Inu" }, - { "id": "saiyan-inu", "symbol": "sayan", "name": "Saiyan Inu" }, - { "id": "saja", "symbol": "saja", "name": "Saja" }, - { "id": "sak3", "symbol": "sak3", "name": "SAKE" }, - { "id": "sakaryaspor", "symbol": "skry", "name": "Sakaryaspor" }, - { "id": "sakata-inu", "symbol": "sakata", "name": "Sakata Inu" }, - { "id": "sake-token", "symbol": "sake", "name": "SakeToken" }, - { "id": "sakhalin-husky", "symbol": "SAHU", "name": "Sakhalin Husky" }, - { "id": "sakura", "symbol": "sku", "name": "Sakura" }, - { "id": "sakura-bloom", "symbol": "skb", "name": "Sakura Bloom" }, - { "id": "sakura-inu", "symbol": "sakura", "name": "Sakura Inu" }, - { "id": "sakura-neko", "symbol": "neko", "name": "Sakura Neko" }, - { "id": "salary", "symbol": "slr", "name": "Salary" }, - { "id": "salmon", "symbol": "slm", "name": "Salmon" }, - { "id": "salmonation", "symbol": "sui", "name": "Salmonation" }, - { "id": "salt", "symbol": "salt", "name": "SALT" }, - { "id": "salus", "symbol": "sls", "name": "SaluS" }, - { "id": "samecoin", "symbol": "same", "name": "Samecoin" }, - { "id": "samo-inu", "symbol": "SINU", "name": "Samo INU" }, - { "id": "samoyedcoin", "symbol": "samo", "name": "Samoyedcoin" }, - { - "id": "samsunspor-fan-token", - "symbol": "sam", - "name": "Samsunspor Fan Token" - }, - { "id": "samurai-legends", "symbol": "smg", "name": "Samurai Legends" }, - { "id": "samusky-token", "symbol": "samu", "name": "Samusky Token" }, - { "id": "sandclock", "symbol": "quartz", "name": "Sandclock" }, - { "id": "sandego", "symbol": "sdgo", "name": "SanDeGo" }, - { "id": "san-diego-coin", "symbol": "sand", "name": "San Diego Coin" }, - { - "id": "sandwich-network", - "symbol": "$sandwich", - "name": "Sandwich Network" - }, - { - "id": "sanliurfaspor-token", - "symbol": "urfa", - "name": "Sanliurfaspor Token" - }, - { "id": "sanshu-inu", "symbol": "sanshu", "name": "Sanshu Inu" }, - { "id": "sans-token", "symbol": "sans", "name": "Sans Token" }, - { "id": "santa-capital", "symbol": "SC", "name": "Santa Capital" }, - { "id": "santaclaus", "symbol": "santa", "name": "Santaclaus" }, - { "id": "santa-coin-2", "symbol": "santa", "name": "Santa Coin" }, - { "id": "santa-dash", "symbol": "santadash", "name": "Santa Dash" }, - { "id": "santadoge", "symbol": "santa", "name": "SantaDoge" }, - { "id": "santa-floki", "symbol": "HoHoHo", "name": "Santa Floki" }, - { "id": "santa-floki-v2", "symbol": "hohoho", "name": "Santa Floki v2.0" }, - { "id": "santa-inu", "symbol": "saninu", "name": "Santa Inu" }, - { "id": "santa-shiba", "symbol": "santashib", "name": "Santa Shiba" }, - { - "id": "santas-war-nft-epic", - "symbol": "SANTAWAR", - "name": "Santas War NFT Epic" - }, - { - "id": "santiment-network-token", - "symbol": "san", - "name": "Santiment Network Token" - }, - { - "id": "santos-fc-fan-token", - "symbol": "santos", - "name": "Santos FC Fan Token" - }, - { "id": "santrast", "symbol": "snrw", "name": "Santrast" }, - { - "id": "sao-paulo-fc-fan-token", - "symbol": "spfc", - "name": "Sao Paulo FC Fan Token" - }, - { "id": "sapchain", "symbol": "sap", "name": "Sapchain" }, - { "id": "sapien", "symbol": "spn", "name": "Sapien" }, - { "id": "sappchat", "symbol": "app", "name": "SappChat" }, - { "id": "sapphire", "symbol": "sapp", "name": "Sapphire" }, - { "id": "sarcophagus", "symbol": "sarco", "name": "Sarcophagus" }, - { "id": "sashimi", "symbol": "sashimi", "name": "Sashimi" }, - { "id": "sata-exchange", "symbol": "satax", "name": "Sata Exchange" }, - { "id": "satcoin", "symbol": "SAT", "name": "Satcoin" }, - { "id": "satisfinance", "symbol": "sat", "name": "SatisFinance" }, - { - "id": "satoexchange-token", - "symbol": "satx", - "name": "SatoExchange Token" - }, - { "id": "satopay", "symbol": "stop", "name": "SatoPay" }, - { - "id": "satopay-yield-token", - "symbol": "spy", - "name": "Satopay Yield Token" - }, - { "id": "sator", "symbol": "sao", "name": "Sator" }, - { "id": "satoru-inu", "symbol": "sato", "name": "Satoru Inu" }, - { "id": "satoshicity", "symbol": "city", "name": "SatoshiCity" }, - { "id": "satoshi-monsters", "symbol": "ssm", "name": "Satoshi Monsters" }, - { "id": "satoshistreetbets", "symbol": "ssb", "name": "SatoshiStreetBets" }, - { "id": "satoshiswap", "symbol": "swap", "name": "SatoshiSwap" }, - { "id": "satozhi", "symbol": "satoz", "name": "Satozhi" }, - { "id": "satt", "symbol": "satt", "name": "SaTT" }, - { "id": "saturna", "symbol": "sat", "name": "Saturna" }, - { "id": "saud", "symbol": "saud", "name": "sAUD" }, - { - "id": "saunafinance-token", - "symbol": "sauna", - "name": "SaunaFinance Token" - }, - { "id": "sav3", "symbol": "sav3", "name": "SAV3" }, - { "id": "savage", "symbol": "savg", "name": "SAVAGE" }, - { "id": "savanna", "symbol": "svn", "name": "Savanna" }, - { "id": "saveanimal", "symbol": "saveanimal", "name": "SaveAnimal" }, - { "id": "save-baby-doge", "symbol": "babydoge", "name": "Save Baby Doge" }, - { - "id": "savebee-farm-honeycomb", - "symbol": "hc", - "name": "SAVEBEE FARM HONEYCOMB" - }, - { "id": "savebritney", "symbol": "sbrt", "name": "SaveBritney" }, - { "id": "saveplanetearth", "symbol": "spe", "name": "SavePlanetEarth" }, - { "id": "save-ralph", "symbol": "ralph", "name": "Save Ralph" }, - { "id": "savetheworld", "symbol": "save", "name": "SaveTheWorld" }, - { "id": "save-token-us", "symbol": "save", "name": "SaveToken" }, - { "id": "savix", "symbol": "svx", "name": "Savix" }, - { "id": "saylor-moon", "symbol": "smoon", "name": "SaylorMoon" }, - { "id": "sbank", "symbol": "sts", "name": "SBank" }, - { "id": "sbet", "symbol": "sbet", "name": "SBET" }, - { "id": "sbf-coin", "symbol": "sbfc", "name": "SBF Coin" }, - { - "id": "sbland-vault-nftx", - "symbol": "sbland", - "name": "SBLAND Vault (NFTX)" - }, - { "id": "sbtc", "symbol": "sbtc", "name": "sBTC" }, - { "id": "scalara-nft-index", "symbol": "NFTI", "name": "Scalara NFT Index" }, - { "id": "scaleswap-token", "symbol": "sca", "name": "Scaleswap Token" }, - { "id": "scallop", "symbol": "sclp", "name": "Scallop" }, - { "id": "scan-defi", "symbol": "scan", "name": "Scan DeFi" }, - { "id": "scanetchain", "symbol": "swc", "name": "Scanetchain" }, - { "id": "scar", "symbol": "scar", "name": "Scar" }, - { "id": "scarab-finance", "symbol": "scarab", "name": "Scarab Finance" }, - { "id": "scarcity", "symbol": "scx", "name": "Scarcity" }, - { "id": "scardust", "symbol": "scard", "name": "SCARDust" }, - { "id": "scarface-finance", "symbol": "scar", "name": "Scarface Finance" }, - { - "id": "scary-chain-capital", - "symbol": "scc", - "name": "Scary Chain Capital" - }, - { "id": "scary-games", "symbol": "scy", "name": "Scary Games" }, - { "id": "scaryswap", "symbol": "scary", "name": "Scaryswap" }, - { "id": "scash", "symbol": "scash", "name": "sCASH" }, - { - "id": "s-c-corinthians-fan-token", - "symbol": "sccp", - "name": "S.C. Corinthians Fan Token" - }, - { "id": "schain-wallet", "symbol": "scha", "name": "Schain Wallet" }, - { "id": "schillingcoin", "symbol": "sch", "name": "Schilling-Coin" }, - { "id": "schnoodle", "symbol": "snood", "name": "Schnoodle" }, - { "id": "scholarship-coin", "symbol": "scho", "name": "Scholarship Coin" }, - { "id": "schrodinger", "symbol": "kitty dinger", "name": "Schrodinger" }, - { "id": "science_chain", "symbol": "scc", "name": "Science Chain" }, - { "id": "scientia", "symbol": "scie", "name": "Scientia" }, - { "id": "scientix", "symbol": "scix", "name": "Scientix" }, - { "id": "scientix-usd", "symbol": "scusd", "name": "Scientix USD" }, - { "id": "scifi-index", "symbol": "scifi", "name": "SCIFI Index" }, - { "id": "scolcoin", "symbol": "scol", "name": "Scolcoin" }, - { "id": "sconex", "symbol": "sconex", "name": "SCOneX" }, - { "id": "scoobi-doge", "symbol": "scoobi", "name": "Scoobi Doge" }, - { "id": "scootercoin", "symbol": "scoot", "name": "ScooterCoin" }, - { "id": "scope", "symbol": "SCP", "name": "Scope" }, - { "id": "scopecoin", "symbol": "xscp", "name": "ScopeCoin" }, - { "id": "scopuly-token", "symbol": "scop", "name": "Scopuly Token" }, - { "id": "score-token", "symbol": "sco", "name": "Score Token" }, - { - "id": "scorpion-finance", - "symbol": "scorpfin", - "name": "Scorpion Finance" - }, - { "id": "scorum", "symbol": "scr", "name": "Scorum" }, - { "id": "scotty-beam", "symbol": "scotty", "name": "Scotty Beam" }, - { "id": "scrap", "symbol": "scrap", "name": "Scrap" }, - { "id": "scream", "symbol": "scream", "name": "Scream" }, - { "id": "scribe", "symbol": "scribe", "name": "Scribe" }, - { "id": "script-network", "symbol": "scpt", "name": "Script Network" }, - { "id": "scriv", "symbol": "scriv", "name": "SCRIV" }, - { "id": "scroll-token", "symbol": "xd", "name": "Data Transaction Token" }, - { "id": "scrooge", "symbol": "scrooge", "name": "Scrooge" }, - { "id": "scry-info", "symbol": "ddd", "name": "Scry.info" }, - { "id": "scrypta", "symbol": "lyra", "name": "Scrypta" }, - { "id": "sdefi", "symbol": "sdefi", "name": "sDEFI" }, - { "id": "sdot", "symbol": "sdot", "name": "sDOT" }, - { "id": "sdraco", "symbol": "sdraco", "name": "SDRACO" }, - { "id": "sea", "symbol": "sea", "name": "Sea" }, - { "id": "seachain", "symbol": "seachain", "name": "SeaChain" }, - { "id": "seadex", "symbol": "sead", "name": "SEADEX" }, - { "id": "seadog-metaverse", "symbol": "seadog", "name": "Seadog Metaverse" }, - { "id": "seamlessswap-token", "symbol": "seamless", "name": "SeamlessSwap" }, - { "id": "seancecircle", "symbol": "seance", "name": "SeanceCircle" }, - { "id": "sea-oceanus", "symbol": "sea", "name": "Sea Oceanus" }, - { "id": "seba", "symbol": "seba", "name": "Seba" }, - { "id": "sechain", "symbol": "snn", "name": "SeChain" }, - { "id": "secret", "symbol": "scrt", "name": "Secret" }, - { "id": "secret-erc20", "symbol": "wscrt", "name": "Secret (ERC20)" }, - { "id": "secret-finance", "symbol": "sefi", "name": "Secret Finance" }, - { "id": "secretsky-finance", "symbol": "ssf", "name": "SecretSky Finance" }, - { "id": "secretworld", "symbol": "ssd", "name": "SecretWorld" }, - { - "id": "securabyte-protocol", - "symbol": "sbyte", - "name": "Securabyte Protocol" - }, - { "id": "secure", "symbol": "scr", "name": "Secure" }, - { "id": "secure-cash", "symbol": "scsx", "name": "Secure Cash" }, - { - "id": "secured-moonrat-token", - "symbol": "smrat", - "name": "Secured MoonRat Token" - }, - { "id": "secured-ship", "symbol": "ship", "name": "Secured Ship" }, - { "id": "securus", "symbol": "xscr", "name": "Securus" }, - { "id": "seda", "symbol": "seda", "name": "SEDA" }, - { "id": "sedo-pow-token", "symbol": "sedo", "name": "SEDO POW TOKEN" }, - { "id": "seed2need", "symbol": "silo", "name": "Seed2Need" }, - { "id": "seeded-network", "symbol": "seeded", "name": "Seeded Network" }, - { "id": "seeder-finance", "symbol": "leaf", "name": "Seeder Finance" }, - { "id": "seedify-fund", "symbol": "sfund", "name": "Seedify.fund" }, - { "id": "seedling", "symbol": "sdln", "name": "Seedling" }, - { "id": "seedon", "symbol": "seon", "name": "Seedon" }, - { "id": "seeds", "symbol": "seeds", "name": "Seeds" }, - { "id": "seedswap", "symbol": "snft", "name": "SeedSwap" }, - { "id": "seed-token", "symbol": "seed", "name": "Seed Token" }, - { "id": "seed-venture", "symbol": "seed", "name": "Seed Venture" }, - { "id": "seek-tiger", "symbol": "sti", "name": "Seek Tiger" }, - { "id": "seele", "symbol": "seele", "name": "Seele" }, - { "id": "seen", "symbol": "seen", "name": "SEEN" }, - { "id": "seesaw", "symbol": "ssw", "name": "Seesaw" }, - { - "id": "seigniorage-shares", - "symbol": "share", - "name": "Seigniorage Shares" - }, - { "id": "seismic", "symbol": "scb", "name": "Seismic" }, - { "id": "sekuritance", "symbol": "skrt", "name": "Sekuritance" }, - { "id": "sekuya", "symbol": "skuy", "name": "Sekuya" }, - { "id": "selenium", "symbol": "slc", "name": "Selenium" }, - { "id": "selfbar", "symbol": "sbar", "name": "Selfbar" }, - { "id": "selfkey", "symbol": "key", "name": "SelfKey" }, - { "id": "semitoken", "symbol": "semi", "name": "Semitoken" }, - { "id": "semux", "symbol": "sem", "name": "Semux" }, - { "id": "senate", "symbol": "senate", "name": "SENATE" }, - { "id": "senator-karen", "symbol": "karen", "name": "Senator Karen" }, - { "id": "sense", "symbol": "sense", "name": "Sense" }, - { "id": "sensei", "symbol": "sensei", "name": "Sensei" }, - { "id": "sensei-shib", "symbol": "$sensei", "name": "Sensei Shib" }, - { "id": "sensible-finance", "symbol": "sensi", "name": "Sensible Finance" }, - { "id": "sensitrust", "symbol": "sets", "name": "Sensitrust" }, - { "id": "senso", "symbol": "senso", "name": "SENSO" }, - { "id": "senspark", "symbol": "sen", "name": "Senspark" }, - { "id": "sentiment-token", "symbol": "sent", "name": "Sentiment Token" }, - { "id": "sentinel", "symbol": "dvpn", "name": "Sentinel" }, - { "id": "sentinel-chain", "symbol": "senc", "name": "Sentinel Chain" }, - { "id": "sentinel-group", "symbol": "dvpn", "name": "Sentinel [OLD]" }, - { "id": "sentinel-protocol", "symbol": "upp", "name": "Sentinel Protocol" }, - { "id": "sentivate", "symbol": "sntvt", "name": "Sentivate" }, - { "id": "sentre", "symbol": "sntr", "name": "Sentre" }, - { "id": "seor-network", "symbol": "seor", "name": "SEOR Network" }, - { "id": "sequence", "symbol": "seq", "name": "Sequence" }, - { "id": "ser", "symbol": "ser", "name": "SER" }, - { "id": "serenity", "symbol": "seren", "name": "Serenity" }, - { - "id": "serenity-financial", - "symbol": "srnt", - "name": "Serenity Financial" - }, - { "id": "serey-coin", "symbol": "sry", "name": "Serey Coin" }, - { "id": "sergey-save-link", "symbol": "ssl", "name": "SERGS Governance" }, - { "id": "sergs", "symbol": "sergs", "name": "SERGS" }, - { "id": "serum", "symbol": "srm", "name": "Serum" }, - { - "id": "serum-ecosystem-token", - "symbol": "seco", - "name": "Serum Ecosystem Token" - }, - { "id": "serum-wormhole", "symbol": "srmso", "name": "Serum (Wormhole)" }, - { - "id": "serum-wormhole-from-ethereum", - "symbol": "srmet", - "name": "Serum (Wormhole from Ethereum)" - }, - { "id": "sessia", "symbol": "kicks", "name": "SESSIA" }, - { "id": "seth", "symbol": "seth", "name": "sETH" }, - { "id": "seth2", "symbol": "seth2", "name": "sETH2" }, - { "id": "sether", "symbol": "seth", "name": "Sether" }, - { "id": "seur", "symbol": "seur", "name": "sEUR" }, - { "id": "severe-rise-games", "symbol": "srgt", "name": "Severe Rise Games" }, - { - "id": "sewer-rat-social-club-chiz-token", - "symbol": "chiz", - "name": "Sewer Rat Social Club CHIZ Token" - }, - { "id": "sexcoin", "symbol": "sxc", "name": "Sexcoin" }, - { "id": "sf-capital", "symbol": "sfcp", "name": "SF Capital" }, - { "id": "s-finance", "symbol": "sfg", "name": "S.Finance" }, - { "id": "sg20", "symbol": "sg20", "name": "SG20" }, - { "id": "sgt-shib", "symbol": "sgts", "name": "Sgt.SHIB" }, - { "id": "shabu-shabu", "symbol": "kobe", "name": "Shabu Shabu" }, - { "id": "shade-cash", "symbol": "shade", "name": "Shade Cash" }, - { "id": "shade-protocol", "symbol": "shd", "name": "Shade Protocol" }, - { "id": "shadows", "symbol": "dows", "name": "Shadows" }, - { "id": "shadow-token", "symbol": "shdw", "name": "Shadow Token" }, - { "id": "shakita-inu", "symbol": "shak", "name": "Shakita Inu" }, - { "id": "shaman-king-inu", "symbol": "shaman", "name": "Shaman King Inu" }, - { "id": "shambala", "symbol": "bala", "name": "Shambala" }, - { "id": "shapepay", "symbol": "spp", "name": "ShapePay" }, - { - "id": "shapeshift-fox-token", - "symbol": "fox", - "name": "ShapeShift FOX Token" - }, - { "id": "shard", "symbol": "shard", "name": "Shard Coin" }, - { "id": "shard-2", "symbol": "shard", "name": "Shard" }, - { "id": "sharder-protocol", "symbol": "ss", "name": "Sharder protocol" }, - { "id": "shardingdao", "symbol": "shd", "name": "ShardingDAO" }, - { "id": "shardus", "symbol": "ult", "name": "Shardus" }, - { "id": "shareat", "symbol": "xat", "name": "ShareAt" }, - { "id": "sharering", "symbol": "shr", "name": "ShareToken" }, - { "id": "sharity", "symbol": "$shari", "name": "Sharity" }, - { "id": "sharkcoin", "symbol": "skn", "name": "Sharkcoin" }, - { "id": "shark-girl", "symbol": "sgirl", "name": "Shark Girl" }, - { "id": "sharkrace", "symbol": "shrk", "name": "SharkRace" }, - { "id": "shar-pei", "symbol": "sharpei", "name": "Shar Pei" }, - { "id": "shd-cash", "symbol": "shdc", "name": "SHD Cash" }, - { - "id": "shebolleth-commerce", - "symbol": "sbecom", - "name": "SheBollETH Commerce" - }, - { "id": "shed-coin", "symbol": "shed", "name": "Shed Coin" }, - { "id": "sheeba", "symbol": "sheeba", "name": "SHEEBA" }, - { "id": "sheep-game", "symbol": "awool", "name": "Sheep Game" }, - { "id": "sheeptoken", "symbol": "SHEEP", "name": "SheepToken" }, - { "id": "sheesh", "symbol": "sheesh", "name": "Sheesh" }, - { - "id": "sheesha-finance", - "symbol": "sheesha", - "name": "Sheesha Finance (BEP20)" - }, - { - "id": "sheesha-finance-erc20", - "symbol": "sheesha", - "name": "Sheesha Finance (ERC20)" - }, - { - "id": "sheesha-finance-polygon", - "symbol": "msheesha", - "name": "Sheesha Finance Polygon" - }, - { "id": "sheeshmoon", "symbol": "smoo", "name": "SheeshMoon" }, - { "id": "sheikh-coin", "symbol": "sheikh", "name": "Sheikh Coin" }, - { "id": "shelby-token", "symbol": "sby", "name": "Shelby Token" }, - { "id": "shelling", "symbol": "shl", "name": "Shelling" }, - { "id": "shell-token", "symbol": "shell", "name": "Shell Token" }, - { "id": "sheng", "symbol": "sheng", "name": "SHENG" }, - { "id": "shengweihu", "symbol": "shengweihu", "name": "Shengweihu" }, - { "id": "shera", "symbol": "shr", "name": "shera" }, - { "id": "sherlock365", "symbol": "lock", "name": "Sherlock365" }, - { "id": "sherlock-wallet", "symbol": "sher", "name": "Sherlock Wallet" }, - { "id": "sherpa", "symbol": "sherpa", "name": "Sherpa" }, - { "id": "shiba-bank", "symbol": "shibabank", "name": "Shiba Bank" }, - { "id": "shibabnb-org", "symbol": "shibabnb", "name": "ShibaBNB.org" }, - { "id": "shiba-bsc", "symbol": "shibsc", "name": "SHIBA BSC" }, - { "id": "shibacash", "symbol": "shibacash", "name": "ShibaCash" }, - { "id": "shibachu", "symbol": "shibchu", "name": "Shibachu" }, - { "id": "shiback", "symbol": "shiback", "name": "SHIBACK" }, - { "id": "shibacock", "symbol": "cock", "name": "Shibacock" }, - { "id": "shibacorgi", "symbol": "shico", "name": "ShibaCorgi" }, - { "id": "shibadoge", "symbol": "shibdoge", "name": "ShibaDoge" }, - { "id": "shiba-dollars", "symbol": "shibadollars", "name": "Shiba Dollars" }, - { "id": "shiba-dragon", "symbol": "shibad", "name": "Shiba Dragon" }, - { "id": "shibaduff", "symbol": "shibaduff", "name": "ShibaDuff" }, - { "id": "shiba-elon", "symbol": "eshib", "name": "Shiba Elon" }, - { "id": "shibaelonverse", "symbol": "shibev", "name": "ShibaElonVerse" }, - { "id": "shiba-fantom", "symbol": "shiba", "name": "Shiba Fantom" }, - { "id": "shibafi", "symbol": "ShibaFi", "name": "Shiba Finance" }, - { "id": "shiba-floki", "symbol": "floki", "name": "Shiba Floki Inu" }, - { "id": "shibagalaxy", "symbol": "shibgx", "name": "ShibaGalaxy" }, - { "id": "shiba-games", "symbol": "shibagames", "name": "Shiba Games" }, - { "id": "shiba-hunter", "symbol": "shunt", "name": "Shiba Hunter" }, - { "id": "shiba-inu", "symbol": "shib", "name": "Shiba Inu" }, - { - "id": "shiba-inu-billionaire", - "symbol": "shibib", - "name": "Shiba Inu Billionaire" - }, - { - "id": "shiba-inu-classic", - "symbol": "shibic", - "name": "Shiba Inu Classic" - }, - { "id": "shiba-inu-empire", "symbol": "shibemp", "name": "Shiba Inu Empire" }, - { "id": "shiba-inu-mother", "symbol": "shibm", "name": "Shiba Inu Mother" }, - { - "id": "shiba-inu-wormhole", - "symbol": "shib", - "name": "Shiba Inu (Wormhole)" - }, - { - "id": "shibaken-finance", - "symbol": "shibaken", - "name": "Shibaken Finance" - }, - { "id": "shibalana", "symbol": "shiba", "name": "Shibalana" }, - { "id": "shiba-light", "symbol": "shibt", "name": "Shiba Light" }, - { "id": "shibalite", "symbol": "shiblite", "name": "ShibaLite" }, - { "id": "shiba-maki", "symbol": "shibamaki", "name": "Shiba Maki" }, - { "id": "shibametaverse", "symbol": "shibameta", "name": "ShibaMetaverse" }, - { "id": "shiba-metaverse", "symbol": "shibmeta", "name": "Shiba Metaverse" }, - { "id": "shibamon", "symbol": "shibamon", "name": "Shibamon" }, - { "id": "shiba-mongoose", "symbol": "shibmong", "name": "Shiba Mongoose" }, - { "id": "shiba-monk", "symbol": "shibamonk", "name": "Shiba Monk" }, - { "id": "shiba-moon", "symbol": "shibm", "name": "Shiba Moon" }, - { "id": "shibana", "symbol": "bana", "name": "Shibana" }, - { "id": "shibanaut", "symbol": "snaut", "name": "Shibanaut" }, - { "id": "shibanaut-token", "symbol": "shibanaut", "name": "Shibanaut Token" }, - { "id": "shibanft", "symbol": "shibanft", "name": "ShibaNFT" }, - { "id": "shibanomi", "symbol": "shio", "name": "Shibanomi" }, - { "id": "shibanomics", "symbol": "shibin", "name": "Shibanomics" }, - { "id": "shibanova", "symbol": "nova", "name": "ShibaNova" }, - { "id": "shibapad", "symbol": "sbp", "name": "ShibaPad" }, - { "id": "shiba-predator", "symbol": "qom", "name": "Shiba Predator" }, - { "id": "shibapup", "symbol": "shibapup", "name": "ShibaPup" }, - { "id": "shiba-ramen", "symbol": "shibaramen", "name": "Shiba Ramen" }, - { "id": "shib-army", "symbol": "shibarmy", "name": "Shib Army" }, - { "id": "shibarocket", "symbol": "shibarocket", "name": "ShibaRocket" }, - { "id": "shiba-shogun", "symbol": "shibagun", "name": "Shiba Shogun" }, - { "id": "shiba-toby", "symbol": "shbt", "name": "Shiba Toby" }, - { "id": "shibavax", "symbol": "shibx", "name": "Shibavax" }, - { "id": "shibaverse", "symbol": "VERSE", "name": "Shibaverse" }, - { "id": "shibaverse-token", "symbol": "shiver", "name": "Shibaverse Token" }, - { "id": "shiba-viking", "symbol": "shibking", "name": "Shiba Viking" }, - { "id": "shibawallet", "symbol": "shwa", "name": "ShibaWallet" }, - { "id": "shiba-watch", "symbol": "shibaw", "name": "Shiba Watch" }, - { "id": "shiba-x", "symbol": "shibax", "name": "Shiba X" }, - { "id": "shibazilla", "symbol": "shibazilla", "name": "ShibaZilla" }, - { "id": "shibboo-inu", "symbol": "shibboo", "name": "Shibboo Inu" }, - { "id": "shib-cake", "symbol": "shibcake", "name": "SHIB CAKE" }, - { "id": "shibelon", "symbol": "shibelon", "name": "ShibElon" }, - { "id": "shibelon-mars", "symbol": "shibelon", "name": "Shibelon Mars" }, - { "id": "shibfuel", "symbol": "shibfuel", "name": "ShibFueL" }, - { "id": "shibgeki", "symbol": "shibgeki", "name": "Shibgeki" }, - { "id": "shib-generating", "symbol": "shg", "name": "Shib Generating" }, - { "id": "shibgf", "symbol": "shibgf", "name": "SHIBGF" }, - { "id": "shibgotchi", "symbol": "shibgotchi", "name": "SHiBGOTCHi" }, - { "id": "shibkiller", "symbol": "shibkiller", "name": "ShibKiller" }, - { "id": "shibmerican", "symbol": "shibmerican", "name": "Shibmerican" }, - { "id": "shibnaki", "symbol": "SHAKI", "name": "SHIBNAKI" }, - { "id": "shibnobi", "symbol": "shinja", "name": "Shibnobi" }, - { "id": "shiboki", "symbol": "shiboki [old[", "name": "Shiboki [OLD]" }, - { "id": "shiboki-2", "symbol": "shiboki", "name": "Shiboki" }, - { "id": "shibonics", "symbol": "snis", "name": "Shibonics" }, - { "id": "shibonk", "symbol": "shibo", "name": "Shibonk" }, - { "id": "shibooster", "symbol": "shiboost", "name": "Shibooster" }, - { "id": "shiborg-inu", "symbol": "shiborg", "name": "SHIBORG INU" }, - { "id": "shibosu", "symbol": "shibosu", "name": "Shibosu" }, - { "id": "shibrobi", "symbol": "shiborg", "name": "ShibRobi" }, - { "id": "shibrwd", "symbol": "SRWD", "name": "ShibRWD" }, - { "id": "shibtama", "symbol": "shibtama", "name": "Shibtama" }, - { "id": "shibtaro", "symbol": "shibtaro", "name": "ShibTaro" }, - { "id": "shibui-dao", "symbol": "shibui", "name": "Shibui DAO" }, - { "id": "shibu-life", "symbol": "shibu", "name": "Shibu Life" }, - { "id": "shibvinci", "symbol": "shiv", "name": "ShibVinci" }, - { "id": "shiden", "symbol": "sdn", "name": "Shiden Network" }, - { "id": "shield", "symbol": "xsh", "name": "SHIELD" }, - { "id": "shield-dao", "symbol": "sld", "name": "ShieldEX" }, - { "id": "shield-finance", "symbol": "coli", "name": "Coliquidity" }, - { "id": "shield-network", "symbol": "shieldnet", "name": "Shield Network" }, - { - "id": "shield-protocol", - "symbol": "shield", - "name": "Shield Protocol [OLD]" - }, - { "id": "shield-protocol-2", "symbol": "shield", "name": "Shield Protocol" }, - { - "id": "shield-protocol-token", - "symbol": "shield", - "name": "Shield Protocol Token" - }, - { "id": "shiftal-coin", "symbol": "sfl", "name": "Shiftal Coin" }, - { "id": "shih-tzu", "symbol": "shih", "name": "Shih Tzu" }, - { "id": "shikage", "symbol": "shkg", "name": "Shikage" }, - { "id": "shikoku-inu", "symbol": "shiko", "name": "Shikoku Inu" }, - { "id": "shill", "symbol": "posh", "name": "Shill" }, - { "id": "shilling", "symbol": "sh", "name": "Shilling" }, - { "id": "shillit-app", "symbol": "shill", "name": "Shillit App" }, - { "id": "shill-token", "symbol": "shill", "name": "SHILL Token" }, - { "id": "shilly-bar", "symbol": "shbar", "name": "Shilly Bar" }, - { "id": "shimmer-network", "symbol": "smr", "name": "Shimmer Network" }, - { "id": "shinchan-token", "symbol": "shinnosuke", "name": "ShinChan Token" }, - { "id": "shinechain", "symbol": "she", "name": "ShineChain" }, - { "id": "shinedao", "symbol": "shn", "name": "ShineDAO" }, - { "id": "shinemine", "symbol": "shine", "name": "ShineMine" }, - { "id": "shinfloki", "symbol": "shinfloki", "name": "ShinFloki" }, - { - "id": "shining-crystal-shard", - "symbol": "scs", - "name": "Shining Crystal Shard" - }, - { "id": "shinji-inu", "symbol": "shinji", "name": "Shinji Inu" }, - { "id": "shinjirai", "symbol": "$shinji", "name": "ShinjiRai" }, - { "id": "shinjiro", "symbol": "shinjiro", "name": "Shinjiro" }, - { - "id": "shinji-the-zombie-slayer", - "symbol": "shinjiz", - "name": "Shinji the Zombie Slayer" - }, - { "id": "shinjurai", "symbol": "shinjurai", "name": "Shinjurai" }, - { "id": "shinjutsu", "symbol": "shinjutsu", "name": "Shinjutsu" }, - { "id": "shinobi", "symbol": "snb", "name": "Shinobi" }, - { "id": "shinobi-inu", "symbol": "shin", "name": "Shinobi Inu" }, - { "id": "shinomics", "symbol": "shin", "name": "Shinomics" }, - { "id": "shintama", "symbol": "shintama", "name": "Shintama" }, - { "id": "shiny", "symbol": "shiny", "name": "Shiny" }, - { "id": "shipchain", "symbol": "ship", "name": "ShipChain" }, - { "id": "shipitpro", "symbol": "shpp", "name": "ShipItPro" }, - { "id": "shiplay", "symbol": "sply", "name": "ShiPlay" }, - { "id": "shirainu", "symbol": "shr", "name": "ShiraINU" }, - { "id": "shirtum", "symbol": "shi", "name": "Shirtum" }, - { "id": "shiryo-inu", "symbol": "shiryo-inu", "name": "Shiryo" }, - { "id": "shisha", "symbol": "shish", "name": "Shisha" }, - { "id": "shitcoin", "symbol": "shit", "name": "ShitCoin" }, - { "id": "shitzu", "symbol": "shitzu", "name": "Shitzu" }, - { "id": "shitzu-inu", "symbol": "shitzuinu", "name": "Shitzu Inu" }, - { - "id": "shiwbawitch-token", - "symbol": "shibawitch", - "name": "SHIWBAWITCH TOKEN" - }, - { "id": "shkooby-inu", "symbol": "shkooby", "name": "SHKOOBY INU" }, - { "id": "shockwave-finance", "symbol": "wave", "name": "Shockwave Finance" }, - { "id": "shoebill-coin", "symbol": "shbl", "name": "Shoebill Coin" }, - { "id": "shoefy", "symbol": "shoe", "name": "ShoeFy" }, - { "id": "shokky", "symbol": "shokky", "name": "SHOKKY" }, - { "id": "shontoken", "symbol": "shon", "name": "ShonToken" }, - { "id": "shoot", "symbol": "shoo", "name": "SHOOT" }, - { "id": "shopnext", "symbol": "next", "name": "ShopNEXT" }, - { "id": "shopperoo", "symbol": "sro", "name": "Shopperoo" }, - { "id": "shoppi-coin", "symbol": "shop", "name": "Shoppi Coin" }, - { "id": "shopping-io", "symbol": "spi", "name": "Shopping.io" }, - { "id": "shorty", "symbol": "shorty", "name": "Shorty" }, - { "id": "showcase-token", "symbol": "sho", "name": "Showcase Token" }, - { "id": "showhand", "symbol": "hand", "name": "ShowHand" }, - { "id": "shping", "symbol": "shping", "name": "Shping" }, - { "id": "shroom-finance", "symbol": "shroom", "name": "Niftyx Protocol" }, - { "id": "shrooms", "symbol": "shrm", "name": "Shrooms" }, - { "id": "shuey-rhon-inu", "symbol": "shuey", "name": "Shuey Rhon Inu" }, - { "id": "shumo", "symbol": "shumo", "name": "Shumo" }, - { "id": "shuna-inuverse", "symbol": "shunav2", "name": "Shuna Inuverse" }, - { "id": "shyft-network-2", "symbol": "shft", "name": "Shyft Network" }, - { "id": "si14bet", "symbol": "si14", "name": "Si14Bet" }, - { "id": "siacashcoin", "symbol": "scc", "name": "SiaCashCoin" }, - { "id": "siaclassic", "symbol": "scc", "name": "SiaClassic" }, - { "id": "siacoin", "symbol": "sc", "name": "Siacoin" }, - { "id": "siambitcoin", "symbol": "sbtc", "name": "SiamBitcoin" }, - { "id": "siamese-neko", "symbol": "siam", "name": "Siamese Neko" }, - { "id": "siaprime-coin", "symbol": "scp", "name": "ScPrime" }, - { "id": "sibcoin", "symbol": "sib", "name": "SIBCoin" }, - { "id": "siberian-husky", "symbol": "shusky", "name": "Siberian Husky" }, - { "id": "sicash", "symbol": "sic", "name": "SICash" }, - { "id": "siddcoin", "symbol": "sidd", "name": "Siddcoin" }, - { "id": "sidekick-token", "symbol": "sk", "name": "SideKick Token" }, - { "id": "sideshift-token", "symbol": "xai", "name": "SideShift Token" }, - { "id": "sidus", "symbol": "sidus", "name": "Sidus" }, - { "id": "sienna", "symbol": "sienna", "name": "Sienna" }, - { "id": "sienna-erc20", "symbol": "wsienna", "name": "Sienna [ERC-20]" }, - { "id": "sierra", "symbol": "sra", "name": "Sierra" }, - { "id": "sif", "symbol": "sif", "name": "Sif" }, - { "id": "sifchain", "symbol": "erowan", "name": "Sifchain" }, - { "id": "sifu-vision", "symbol": "sifu", "name": "Sifu Vision" }, - { "id": "signal-token", "symbol": "sig", "name": "Signal Token" }, - { "id": "signata", "symbol": "sata", "name": "Signata" }, - { "id": "signaturechain", "symbol": "sign", "name": "SignatureChain" }, - { "id": "signed", "symbol": "sign", "name": "Signed" }, - { "id": "signum", "symbol": "signa", "name": "Signum" }, - { "id": "silent-notary", "symbol": "ubsn", "name": "Silent Notary" }, - { "id": "sil-finance", "symbol": "sil", "name": "SIL Finance Token V2" }, - { "id": "silkchain", "symbol": "silk", "name": "SilkChain" }, - { "id": "silo-finance", "symbol": "silo", "name": "Silo Finance" }, - { "id": "silva-token", "symbol": "silva", "name": "Silva" }, - { "id": "silvercashs", "symbol": "svc", "name": "Silvercashs" }, - { "id": "silvercoin", "symbol": "sc", "name": "SilverCoin" }, - { "id": "silver-gateway", "symbol": "svs", "name": "Silver Gateway" }, - { "id": "silverstonks", "symbol": "sstx", "name": "Silver Stonks" }, - { "id": "silvertoken", "symbol": "slvt", "name": "SilverToken" }, - { "id": "silver-token", "symbol": "silver", "name": "Silver Token" }, - { - "id": "silver-tokenized-stock-defichain", - "symbol": "dSLV", - "name": "iShares Silver Trust Defichain" - }, - { "id": "silverway", "symbol": "slv", "name": "Silverway" }, - { "id": "simba-empire", "symbol": "sim", "name": "Simba Empire" }, - { "id": "simba-inu", "symbol": "simbainu", "name": "Simba Inu" }, - { - "id": "simba-storage-token", - "symbol": "sst", - "name": "SIMBA Storage Token" - }, - { "id": "simba-token", "symbol": "simba", "name": "Simba Token" }, - { "id": "simbcoin-swap", "symbol": "smbswap", "name": "SimbCoin Swap" }, - { "id": "simian-finance", "symbol": "sifi", "name": "Simian Finance" }, - { - "id": "simple-cool-automatic-money", - "symbol": "scam", - "name": "Simple Cool Automatic Money" - }, - { - "id": "simple-masternode-coin", - "symbol": "smnc", - "name": "Simple Masternode Coin" - }, - { "id": "simple-token", "symbol": "ost", "name": "OST" }, - { "id": "simplexchain", "symbol": "sxc", "name": "SimplexChain" }, - { "id": "simplicity-coin", "symbol": "spl", "name": "Simplicity Coin" }, - { "id": "simpli-finance", "symbol": "simpli", "name": "Simpli Finance" }, - { "id": "simply", "symbol": "simply", "name": "Simply" }, - { "id": "simp-token", "symbol": "simp", "name": "Simp" }, - { "id": "simracer-coin", "symbol": "src", "name": "Simracer Coin" }, - { "id": "sincere-doge", "symbol": "sdoge", "name": "Sincere Doge" }, - { "id": "sin-city", "symbol": "sin", "name": "Sinverse" }, - { "id": "sinerium", "symbol": "xnr", "name": "Sinerium" }, - { - "id": "singer-community-coin", - "symbol": "SINGER", - "name": "Singer Community Coin" - }, - { "id": "singh", "symbol": "singh", "name": "Singh" }, - { "id": "single-finance", "symbol": "single", "name": "Single Finance" }, - { "id": "sing-token", "symbol": "sing", "name": "Sing Token" }, - { - "id": "sing-token-avalanche", - "symbol": "sing", - "name": "Sing Token (Avalanche)" - }, - { "id": "sing-token-bsc", "symbol": "sing", "name": "Sing Token (BSC)" }, - { "id": "singulardtv", "symbol": "sngls", "name": "SingularDTV" }, - { "id": "singularity", "symbol": "sgly", "name": "Singularity" }, - { "id": "singularitydao", "symbol": "sdao", "name": "SingularityDAO" }, - { "id": "singularitynet", "symbol": "agix", "name": "SingularityNET" }, - { "id": "sins-finance", "symbol": "sin", "name": "Sins Finance" }, - { - "id": "sint-truidense-voetbalvereniging-fan-token", - "symbol": "stv", - "name": "Sint-Truidense Voetbalvereniging Fan Token" - }, - { "id": "sipher", "symbol": "sipher", "name": "Sipher" }, - { "id": "siren", "symbol": "si", "name": "Siren" }, - { "id": "sirin-labs-token", "symbol": "srn", "name": "Sirin Labs Token" }, - { "id": "sirio", "symbol": "SIR", "name": "Sirio" }, - { "id": "sirius-finance", "symbol": "srs", "name": "Sirius Finance" }, - { "id": "sishi-finance", "symbol": "sishi", "name": "Sishi Finance" }, - { "id": "sivasspor", "symbol": "siv", "name": "Sivasspor" }, - { "id": "sixeleven", "symbol": "611", "name": "SixEleven" }, - { "id": "six-network", "symbol": "six", "name": "SIX Network" }, - { "id": "sjwcoin", "symbol": "sjw", "name": "SJWCoin" }, - { "id": "skale", "symbol": "skl", "name": "SKALE" }, - { "id": "skey-network", "symbol": "skey", "name": "Skey Network" }, - { "id": "skillchain", "symbol": "ski", "name": "Skillchain" }, - { "id": "skill-guilds", "symbol": "sgk", "name": "Skill Guilds" }, - { "id": "skinchain", "symbol": "skc", "name": "SKINCHAIN" }, - { "id": "skincoin", "symbol": "skin", "name": "SkinCoin" }, - { "id": "sklay", "symbol": "sklay", "name": "sKLAY" }, - { "id": "skraps", "symbol": "skrp", "name": "Skraps" }, - { "id": "skrumble-network", "symbol": "skm", "name": "Skrumble Network" }, - { "id": "skull", "symbol": "skull", "name": "Skull" }, - { "id": "skybridger", "symbol": "skbr", "name": "SkyBridger" }, - { "id": "skycoin", "symbol": "sky", "name": "Skycoin" }, - { "id": "skylands", "symbol": "skylands", "name": "SkyLands" }, - { "id": "skylight", "symbol": "sltn", "name": "Skylight" }, - { "id": "skyrim-finance", "symbol": "skyrim", "name": "Skyrim Finance" }, - { "id": "skyrocketing", "symbol": "skyrocketing", "name": "Skyrocketing" }, - { "id": "sky-shiba", "symbol": "sky", "name": "Sky Shiba" }, - { "id": "skywalker", "symbol": "sky", "name": "Skywalker" }, - { "id": "skyward-finance", "symbol": "skyward", "name": "Skyward Finance" }, - { "id": "skyx-token", "symbol": "skyx", "name": "SkyX Token" }, - { "id": "sl3-token", "symbol": "sl3", "name": "Mammoth" }, - { "id": "slam-token", "symbol": "slam", "name": "Slam Token" }, - { "id": "slavi-coin", "symbol": "slv", "name": "Slavi Coin" }, - { "id": "sleepearn-finance", "symbol": "sen", "name": "SleepEarn Finance" }, - { "id": "sleepy-shib", "symbol": "sleepy-shib", "name": "Sleepy-Shib" }, - { "id": "sleepy-sloth", "symbol": "sleepy", "name": "Sleepy Sloth Finance" }, - { "id": "slimcoin", "symbol": "slm", "name": "Slimcoin" }, - { "id": "slink", "symbol": "slink", "name": "sLINK" }, - { "id": "slittle-rabbit", "symbol": "sltrbt", "name": "sLittle Rabbit" }, - { "id": "slnv2", "symbol": "slnv2", "name": "SLNV2" }, - { "id": "slothcoin", "symbol": "sloth", "name": "SlothCoin" }, - { "id": "slt", "symbol": "slt", "name": "SLT" }, - { "id": "slushie-capital", "symbol": "slush", "name": "Slushie Capital" }, - { "id": "small-doge", "symbol": "sdog", "name": "Small Doge" }, - { "id": "small-fish-cookie", "symbol": "sfc", "name": "Small Fish Cookie" }, - { - "id": "smart-block-chain-city", - "symbol": "sbcc", - "name": "Smart Block Chain City" - }, - { "id": "smart-busd", "symbol": "sbusd", "name": "Smart BUSD" }, - { "id": "smartcash", "symbol": "smart", "name": "SmartCash" }, - { "id": "smartchem", "symbol": "smac", "name": "Smartchem" }, - { "id": "smartcoin-2", "symbol": "smrt", "name": "SmartCoin" }, - { "id": "smart-coin-smrtr", "symbol": "smrtr", "name": "SmarterCoin" }, - { - "id": "smartcredit-token", - "symbol": "smartcredit", - "name": "SmartCredit Token" - }, - { - "id": "smart-donation-coin", - "symbol": "sdc", - "name": "Smart Donation Coin" - }, - { "id": "smartlands", "symbol": "slt", "name": "Smartlands Network" }, - { "id": "smartlink", "symbol": "smak", "name": "Smartlink" }, - { "id": "smartlox", "symbol": "smartlox", "name": "SmartLOX" }, - { - "id": "smart-marketing-token", - "symbol": "smt", - "name": "Smart Marketing Token" - }, - { "id": "smart-medical-coin", "symbol": "smc", "name": "Smart Medical Coin" }, - { "id": "smartmesh", "symbol": "smt", "name": "SmartMesh" }, - { "id": "smart-mfg", "symbol": "mfg", "name": "Smart MFG" }, - { "id": "smartnft", "symbol": "smartnft", "name": "SmartNFT" }, - { - "id": "smartnodes-finance", - "symbol": "smrt", - "name": "SmartNodes Finance" - }, - { "id": "smartofgiving", "symbol": "aog", "name": "smARTOFGIVING" }, - { "id": "smartpad", "symbol": "pad", "name": "SmartPad" }, - { "id": "smart-payment", "symbol": "spay-v2", "name": "Smart Payment V2" }, - { "id": "smart-reward-token", "symbol": "srt", "name": "Smart Reward Token" }, - { "id": "smartshare", "symbol": "ssp", "name": "Smartshare" }, - { "id": "smart-shiba", "symbol": "smartshib", "name": "Smart Shiba" }, - { "id": "smart-token", "symbol": "smart", "name": "Smart Token" }, - { "id": "smart-valor", "symbol": "valor", "name": "Smart Valor" }, - { "id": "smart-wallet-token", "symbol": "swt", "name": "Smart Wallet Token" }, - { "id": "smartway-finance", "symbol": "smart", "name": "Smartway.Finance" }, - { "id": "smartworth", "symbol": "smartworth", "name": "Smartworth" }, - { "id": "smartx", "symbol": "sat", "name": "SmartX" }, - { "id": "smarty-pay", "symbol": "spy", "name": "Smarty Pay" }, - { "id": "smash-cash", "symbol": "smash", "name": "Smash Cash" }, - { "id": "smash-token", "symbol": "sas", "name": "Smashchain" }, - { "id": "smaugs-nft", "symbol": "smg", "name": "Smaugs NFT" }, - { "id": "smd-coin", "symbol": "smd", "name": "SMD Coin" }, - { "id": "smegmars", "symbol": "SMGM", "name": "SMEGMARS" }, - { "id": "smg", "symbol": "smg", "name": "SMG" }, - { "id": "smilecoin", "symbol": "sec", "name": "Smilecoin" }, - { "id": "smile-coin", "symbol": "smile", "name": "Smile Coin" }, - { "id": "smile-token", "symbol": "smile", "name": "Smile Token" }, - { "id": "smileycoin", "symbol": "smly", "name": "Smileycoin" }, - { "id": "smoke", "symbol": "smoke", "name": "Smoke" }, - { "id": "smoke-high", "symbol": "smoke", "name": "Smoke High" }, - { "id": "smooth-love-potion", "symbol": "slp", "name": "Smooth Love Potion" }, - { - "id": "smooth-love-potion-wormhole", - "symbol": "slp", - "name": "Smooth Love Potion (Wormhole)" - }, - { "id": "smoothy", "symbol": "smty", "name": "Smoothy" }, - { "id": "smscodes", "symbol": "smsct", "name": "SMSCodes" }, - { "id": "smugdoge", "symbol": "smug", "name": "SmugDoge" }, - { "id": "snake", "symbol": "GSK", "name": "SNAKE" }, - { "id": "snake-rinium", "symbol": "rim", "name": "Snake Rinium" }, - { - "id": "snakes-on-a-nft-game", - "symbol": "snakes", - "name": "Snakes On A NFT Game" - }, - { "id": "snake-token", "symbol": "snk", "name": "Snake Token" }, - { "id": "snapex", "symbol": "snap", "name": "SnapEx" }, - { "id": "snapparazzi", "symbol": "rno", "name": "Earneo" }, - { "id": "snetwork", "symbol": "snet", "name": "Snetwork" }, - { - "id": "snglsdao-governance-token", - "symbol": "sgt", - "name": "snglsDAO Governance Token" - }, - { "id": "snook", "symbol": "snk", "name": "Snook" }, - { "id": "snoopdao", "symbol": "snoop", "name": "SnoopDAO" }, - { "id": "snoopdoge", "symbol": "snoop", "name": "SnoopDoge" }, - { "id": "snoopflokidog", "symbol": "sfd", "name": "Snoop Floki Dog" }, - { "id": "snoshares", "symbol": "snoshare", "name": "Snoshares" }, - { "id": "snovio", "symbol": "snov", "name": "Snovian.Space" }, - { "id": "snowballtoken", "symbol": "snowball", "name": "Snowball Token" }, - { "id": "snowball-token", "symbol": "snob", "name": "Snowball" }, - { "id": "snowballxyz", "symbol": "sno", "name": "Snowball-Snowbank" }, - { "id": "snowbank", "symbol": "sb", "name": "Snowbank" }, - { "id": "snowbear", "symbol": "sbr", "name": "Snowbear" }, - { "id": "snowblossom", "symbol": "snow", "name": "SnowBlossom" }, - { "id": "snow-burn", "symbol": "burn", "name": "Snow Burn" }, - { "id": "snowcrash-token", "symbol": "nora", "name": "SnowCrash Token" }, - { "id": "snowdog", "symbol": "sdog", "name": "Snowdog" }, - { "id": "snowgem", "symbol": "tent", "name": "TENT" }, - { "id": "snowswap", "symbol": "SNOW", "name": "Snowswap" }, - { "id": "snowtomb", "symbol": "stomb", "name": "Snowtomb" }, - { "id": "snowtomb-lot", "symbol": "slot", "name": "Snowtomb LOT" }, - { "id": "snowy-owl", "symbol": "sno", "name": "Snowy Owl" }, - { "id": "snx-debt-mirror", "symbol": "dsnx", "name": "SNX Debt Mirror" }, - { "id": "soakmont", "symbol": "soak", "name": "Soakmont" }, - { "id": "soar-2", "symbol": "soar", "name": "Soar" }, - { "id": "soba-token", "symbol": "soba", "name": "SOBA Token" }, - { "id": "socaverse", "symbol": "soca", "name": "Socaverse" }, - { "id": "soccerhub", "symbol": "sch", "name": "SoccerHub" }, - { "id": "soccer-infinity", "symbol": "SOCIN", "name": "Soccer Infinity" }, - { - "id": "socean-staked-sol", - "symbol": "scnsol", - "name": "Socean Staked Sol" - }, - { "id": "socialblox", "symbol": "sblx", "name": "SocialBlox" }, - { "id": "social-capitalism", "symbol": "socap", "name": "Social Capitalism" }, - { "id": "social-finance", "symbol": "sofi", "name": "Social Finance" }, - { "id": "social-good-project", "symbol": "sg", "name": "SocialGood" }, - { "id": "sociall", "symbol": "scl", "name": "Sociall" }, - { "id": "social-rocket", "symbol": "rocks", "name": "Social Rocket" }, - { "id": "social-send", "symbol": "send", "name": "Social Send" }, - { "id": "socialx-2", "symbol": "sosx", "name": "SocialX" }, - { - "id": "society-of-galactic-exploration", - "symbol": "sge", - "name": "Society of Galactic Exploration" - }, - { "id": "socilink", "symbol": "soc", "name": "SociLink" }, - { "id": "soda-coin", "symbol": "soc", "name": "SODA Coin" }, - { "id": "soft-bitcoin", "symbol": "sbtc", "name": "Soft Bitcoin" }, - { "id": "softchain", "symbol": "scc", "name": "SoftChain" }, - { "id": "soga-project", "symbol": "soga", "name": "SOGA Project" }, - { "id": "sokuswap", "symbol": "soku", "name": "SokuSwap" }, - { "id": "sol-1x", "symbol": "sol1x", "name": "SOL 1x" }, - { "id": "solabrador", "symbol": "SOLAB", "name": "Solabrador" }, - { "id": "solace", "symbol": "solace", "name": "SOLACE" }, - { "id": "solace-coin", "symbol": "solace", "name": "Solace Coin" }, - { "id": "solalambo", "symbol": "sob", "name": "SolaLambo" }, - { "id": "solana", "symbol": "sol", "name": "Solana" }, - { - "id": "solana-ecosystem-index", - "symbol": "soli", - "name": "Solana Ecosystem Index" - }, - { "id": "solana-inu", "symbol": "inu", "name": "Solana Inu" }, - { "id": "solana-nut", "symbol": "SOLNUT", "name": "Solana Nut" }, - { "id": "solana-paws", "symbol": "paws", "name": "Solana Paws" }, - { - "id": "solanasail-governance-token", - "symbol": "gsail", - "name": "SolanaSail Governance Token" - }, - { "id": "solanax", "symbol": "sold", "name": "Solanax" }, - { "id": "sola-ninja", "symbol": "snj", "name": "Sola Ninja" }, - { "id": "solanium", "symbol": "slim", "name": "Solanium" }, - { "id": "solanyx", "symbol": "SYX", "name": "Solanyx" }, - { "id": "solape-token", "symbol": "solape", "name": "SOLAPE Token" }, - { "id": "solapoly", "symbol": "slpy", "name": "Solapoly" }, - { "id": "solar", "symbol": "solar", "name": "Solar" }, - { "id": "solarbeam", "symbol": "solar", "name": "Solarbeam" }, - { "id": "solar-bear", "symbol": "solbear", "name": "Solar Bear" }, - { "id": "solar-energy", "symbol": "seg", "name": "Solar Energy" }, - { "id": "solareum", "symbol": "slrm", "name": "Solareum" }, - { "id": "solareum-wallet", "symbol": "xsb", "name": "Solareum Wallet" }, - { "id": "solarfare", "symbol": "slf", "name": "Solarfare" }, - { "id": "solarflare", "symbol": "flare", "name": "Solarflare" }, - { "id": "solar-full-cycle", "symbol": "SFC", "name": "Solar Full Cycle" }, - { "id": "solaris", "symbol": "xlr", "name": "Solaris" }, - { "id": "solarminex", "symbol": "smx", "name": "SolarMineX" }, - { "id": "solarmoon", "symbol": "solar", "name": "Solarmoon" }, - { "id": "sola-token", "symbol": "sola", "name": "SOLA Token" }, - { "id": "solaunch", "symbol": "slh", "name": "SoLaunch" }, - { "id": "sol-baby-doge", "symbol": "SBABYDOGE", "name": "SOL Baby Doge" }, - { "id": "solbank-token", "symbol": "sbnk", "name": "Solbank Token" }, - { "id": "solberg", "symbol": "slb", "name": "Solberg" }, - { "id": "solberry", "symbol": "solberry", "name": "SolBerry" }, - { "id": "solbit", "symbol": "sbt", "name": "SOLBIT" }, - { "id": "solcash", "symbol": "solcash", "name": "SOLCash" }, - { "id": "solcats", "symbol": "meow", "name": "Solcats" }, - { "id": "solchicks-shards", "symbol": "shards", "name": "SolChicks Shards" }, - { "id": "solchicks-token", "symbol": "chicks", "name": "SolChicks Token" }, - { "id": "solcial", "symbol": "slcl", "name": "Solcial" }, - { "id": "solclout", "symbol": "sct", "name": "SolClout" }, - { "id": "solcondoms", "symbol": "condoms", "name": "SolCondoms" }, - { "id": "solcubator", "symbol": "solc", "name": "Solcubator" }, - { "id": "soldait", "symbol": "sit", "name": "Soldait" }, - { "id": "soldate-token", "symbol": "date", "name": "SolDate Token" }, - { "id": "soldex", "symbol": "solx", "name": "Soldex" }, - { "id": "soldiernodes", "symbol": "sld", "name": "SoldierNodes" }, - { "id": "soldiersland", "symbol": "sld", "name": "SoldiersLand" }, - { "id": "soldo", "symbol": "sld", "name": "Soldo" }, - { "id": "soldoge", "symbol": "sdoge", "name": "SolDoge" }, - { "id": "solend", "symbol": "slnd", "name": "Solend" }, - { "id": "solex-finance", "symbol": "slx", "name": "Solex Finance" }, - { "id": "solfarm", "symbol": "tulip", "name": "Tulip Protocol" }, - { "id": "solfina", "symbol": "solfi", "name": "Solfina" }, - { "id": "solfire-finance", "symbol": "fire", "name": "Solfire Finance" }, - { "id": "sol-flowers", "symbol": "flwr", "name": "SOL Flowers" }, - { "id": "sol-fox", "symbol": "sfox", "name": "Sol Fox" }, - { "id": "solhero", "symbol": "hero", "name": "SolHero" }, - { "id": "solice", "symbol": "slc", "name": "Solice" }, - { "id": "solideth", "symbol": "solideth", "name": "SolidETH" }, - { "id": "solidex", "symbol": "sex", "name": "Solidex" }, - { "id": "soliditylabs", "symbol": "soliditylabs", "name": "SolidityLabs" }, - { "id": "solidly", "symbol": "solid", "name": "Solidly" }, - { "id": "solid-protocol", "symbol": "solid", "name": "Solid Protocol" }, - { "id": "solidray-finance", "symbol": "SRT", "name": "Solidray Finance" }, - { - "id": "solidsex-tokenized-vesolid", - "symbol": "SOLIDsex", - "name": "SOLIDsex: Tokenized veSOLID" - }, - { "id": "solidus-aitech", "symbol": "aitech", "name": "Solidus Ai Tech" }, - { "id": "solit", "symbol": "slt", "name": "Solit" }, - { "id": "solium-rise", "symbol": "solar", "name": "Solium Rise" }, - { "id": "solminter", "symbol": "smrt", "name": "Solminter" }, - { "id": "solmoon", "symbol": "solmo", "name": "SolMoon" }, - { "id": "solo-coin", "symbol": "solo", "name": "Sologenic" }, - { "id": "solomon-defi", "symbol": "slm", "name": "Solomon Defi" }, - { "id": "solootbox-dao", "symbol": "box", "name": "Solootbox DAO" }, - { "id": "solo-vault-nftx", "symbol": "solo", "name": "SOLO Vault (NFTX)" }, - { "id": "solpad-finance", "symbol": "solpad", "name": "Solpad Finance" }, - { "id": "solpay-finance", "symbol": "solpay", "name": "SolPay Finance" }, - { "id": "solrazr", "symbol": "solr", "name": "SolRazr" }, - { "id": "solrider", "symbol": "srd", "name": "SolRider" }, - { "id": "solrise-finance", "symbol": "slrs", "name": "Solrise Finance" }, - { "id": "solster", "symbol": "str", "name": "Solster" }, - { "id": "soltato-fries", "symbol": "fries", "name": "Soltato FRIES" }, - { "id": "solum", "symbol": "solum", "name": "Solum" }, - { "id": "soluna", "symbol": "slna", "name": "Soluna" }, - { "id": "solunavax-index", "symbol": "solunavax", "name": "SOLUNAVAX Index" }, - { "id": "solve-care", "symbol": "solve", "name": "SOLVE" }, - { "id": "solvent", "symbol": "svt", "name": "Solvent" }, - { "id": "solview", "symbol": "solv", "name": "Solview" }, - { "id": "sol-wormhole", "symbol": "sol", "name": "SOL (Wormhole)" }, - { "id": "solx-gaming-guild", "symbol": "sgg", "name": "SolX Gaming Guild" }, - { "id": "solyard-finance", "symbol": "yard", "name": "Solyard Finance" }, - { "id": "somax", "symbol": "smx", "name": "Somax" }, - { "id": "sombe", "symbol": "sbe", "name": "Sombe" }, - { "id": "sombra-network", "symbol": "smbr", "name": "Sombra" }, - { "id": "somdej", "symbol": "sdc", "name": "Somdej" }, - { - "id": "somee-advertising-token", - "symbol": "sat", - "name": "SoMee Advertising Token" - }, - { "id": "somee-social", "symbol": "somee", "name": "SoMee.Social" }, - { "id": "somee-social-old", "symbol": "ong", "name": "SoMee.Social [OLD]" }, - { "id": "somesing", "symbol": "ssx", "name": "SOMESING" }, - { "id": "sommelier", "symbol": "somm", "name": "Sommelier" }, - { "id": "somnium", "symbol": "som", "name": "Somnium" }, - { - "id": "somnium-space-cubes", - "symbol": "cube", - "name": "Somnium Space CUBEs" - }, - { "id": "sona-network", "symbol": "sona", "name": "Sona Network" }, - { "id": "sonar", "symbol": "ping", "name": "Sonar" }, - { "id": "sonarwatch", "symbol": "sonar", "name": "SonarWatch" }, - { "id": "sone-finance", "symbol": "sone", "name": "S-ONE Finance" }, - { "id": "songbird", "symbol": "sgb", "name": "Songbird" }, - { "id": "songcoin", "symbol": "song", "name": "SongCoin" }, - { "id": "sonic", "symbol": "sonic", "name": "Sonic" }, - { "id": "sonic-token", "symbol": "sonic", "name": "Sonic Token" }, - { "id": "sonm", "symbol": "snm", "name": "SONM" }, - { "id": "sono", "symbol": "sono", "name": "SONO" }, - { "id": "sonocoin", "symbol": "sono", "name": "SonoCoin" }, - { "id": "son-of-doge-v2", "symbol": "SODV2", "name": "Son of Doge V2" }, - { "id": "son-of-elon", "symbol": "soe", "name": "Son of Elon" }, - { "id": "sonofsaitama", "symbol": "sos", "name": "SonOfSaitama" }, - { "id": "sonofshib", "symbol": "son", "name": "SONofSHIB" }, - { "id": "sopay", "symbol": "sop", "name": "SoPay" }, - { "id": "sophiatx", "symbol": "sphtx", "name": "SophiaTX" }, - { "id": "sora", "symbol": "xor", "name": "Sora" }, - { "id": "sorachancoin", "symbol": "sora", "name": "SorachanCoin" }, - { - "id": "sora-synthetic-usd", - "symbol": "xstusd", - "name": "SORA Synthetic USD" - }, - { - "id": "sora-validator-token", - "symbol": "val", - "name": "Sora Validator Token" - }, - { "id": "sos-amazonia", "symbol": "sosamz", "name": "SOS AMAZÔNIA" }, - { "id": "sos-foundation", "symbol": "sos", "name": "SOS Foundation" }, - { "id": "sota-finance", "symbol": "sota", "name": "SOTA Finance" }, - { - "id": "soul-dog-city-bones", - "symbol": "bones", - "name": "Soul Dogs City Bones" - }, - { "id": "soulocoin", "symbol": "soulo", "name": "SouloCoin" }, - { "id": "soul-swap", "symbol": "soul", "name": "Soul Swap" }, - { "id": "sound-coin", "symbol": "sound", "name": "Sound Coin" }, - { "id": "souni-token", "symbol": "son", "name": "Souni" }, - { - "id": "south-african-tether", - "symbol": "xzar", - "name": "South African Tether" - }, - { "id": "southxchange-coin", "symbol": "sxcc", "name": "SouthXchange Coin" }, - { "id": "sov", "symbol": "sov", "name": "SOV" }, - { "id": "sovereign-coin", "symbol": "sov", "name": "Sovereign Coin" }, - { "id": "sovi-token", "symbol": "sovi", "name": "Sovi Token" }, - { "id": "sovranocoin", "symbol": "svr", "name": "SovranoCoin" }, - { "id": "sovryn", "symbol": "sov", "name": "Sovryn" }, - { "id": "soxycoin", "symbol": "soxy", "name": "SOXYCOIN" }, - { "id": "space", "symbol": "space", "name": "Space" }, - { "id": "spaceballs", "symbol": "balls", "name": "SpaceBalls" }, - { "id": "spacechain-erc-20", "symbol": "spc", "name": "SpaceChain (ERC-20)" }, - { "id": "spacecoin", "symbol": "space", "name": "Spacecoin" }, - { "id": "spacecorgi", "symbol": "scorgi", "name": "SpaceCorgi" }, - { "id": "space-corsair-key", "symbol": "sck", "name": "Space Corsair Key" }, - { "id": "spacecowboy", "symbol": "scb", "name": "SpaceCowBoy" }, - { "id": "space-crypto", "symbol": "spg", "name": "Space Crypto" }, - { "id": "spacedawgs", "symbol": "dawgs", "name": "SpaceDawgs" }, - { "id": "space-dog", "symbol": "Space dog", "name": "Space dog" }, - { "id": "space-dollars", "symbol": "sdollar", "name": "Space Dollars" }, - { "id": "spacefalcon", "symbol": "fcon", "name": "SpaceFalcon" }, - { "id": "spacegoat-token", "symbol": "sgt", "name": "SpaceGoat Token" }, - { "id": "spacegrime", "symbol": "grimex", "name": "SpaceGrime" }, - { "id": "space-hamster", "symbol": "hams", "name": "Space Hamster" }, - { "id": "space-iz", "symbol": "spiz", "name": "SPACE-iZ" }, - { "id": "spacelens", "symbol": "space", "name": "Spacelens" }, - { "id": "space-link", "symbol": "splink", "name": "Space Link" }, - { "id": "spacemeta", "symbol": "spmeta", "name": "SpaceMeta" }, - { "id": "spacemine", "symbol": "mine", "name": "SpaceMine" }, - { "id": "space-misfits", "symbol": "smcw", "name": "Space Misfits" }, - { - "id": "space-monkey-token", - "symbol": "MONKE", - "name": "Space Monkey Token" - }, - { "id": "space-ore", "symbol": "spo", "name": "Space Ore" }, - { "id": "spacepi", "symbol": "spacepi", "name": "SpacePi" }, - { "id": "spaceport-universe", "symbol": "spu", "name": "SpacePort Universe" }, - { "id": "spaceship-war", "symbol": "spw", "name": "Spaceship War" }, - { "id": "space-sip", "symbol": "sip", "name": "Space SIP" }, - { "id": "space-soldier", "symbol": "soldier", "name": "Space Soldier" }, - { "id": "spaceswap-milk2", "symbol": "milk2", "name": "Spaceswap MILK2" }, - { "id": "spaceswap-shake", "symbol": "shake", "name": "Spaceswap SHAKE" }, - { "id": "spacetoast", "symbol": "spacetoast", "name": "SpaceToast" }, - { "id": "space-token", "symbol": "space", "name": "Space Token" }, - { "id": "space-token-bsc", "symbol": "space", "name": "Space Token BSC" }, - { "id": "spacevikings", "symbol": "svt", "name": "SpaceVikings" }, - { "id": "spacexlife", "symbol": "safe", "name": "SpaceXliFe" }, - { "id": "spacey-2025", "symbol": "spay", "name": "SpaceY 2025" }, - { - "id": "spain-national-fan-token", - "symbol": "snft", - "name": "Spain National Fan Token" - }, - { "id": "spankchain", "symbol": "spank", "name": "SpankChain" }, - { "id": "spantale", "symbol": "AEL", "name": "Spantale" }, - { "id": "sparda-wallet", "symbol": "spw", "name": "Sparda Wallet" }, - { "id": "spark-finance", "symbol": "spark", "name": "Spark Finance" }, - { "id": "sparklab", "symbol": "spark", "name": "SparkLab" }, - { "id": "sparkle", "symbol": "sprkl", "name": "Sparkle Loyalty" }, - { "id": "sparkle-coin", "symbol": "sctk", "name": "Sparkle Coin" }, - { "id": "sparkpoint", "symbol": "srk", "name": "SparkPoint" }, - { "id": "sparkpoint-fuel", "symbol": "sfuel", "name": "SparkPoint Fuel" }, - { "id": "sparks", "symbol": "spk", "name": "SparksPay" }, - { "id": "sparta", "symbol": "spar", "name": "Sparta" }, - { "id": "spartacus", "symbol": "spa", "name": "Spartacus" }, - { "id": "spartan", "symbol": "300", "name": "Spartan" }, - { "id": "spartancoin", "symbol": "spn", "name": "SpartanCoin" }, - { "id": "spartan-index", "symbol": "spi", "name": "Spartan Index" }, - { - "id": "spartan-protocol-token", - "symbol": "sparta", - "name": "Spartan Protocol Token" - }, - { "id": "spartan-token", "symbol": "spa", "name": "Spartan Token" }, - { "id": "spd-game", "symbol": "spdc", "name": "SPD Game" }, - { - "id": "spdr-s-p-500-etf-trust-defichain", - "symbol": "dspy", - "name": "SPDR S\u0026P 500 ETF Trust Defichain" - }, - { "id": "specter-finance", "symbol": "specter", "name": "Specter Finance" }, - { "id": "specter-share", "symbol": "sshare", "name": "Specter Share" }, - { "id": "spectra", "symbol": "spc", "name": "Spectra" }, - { "id": "spectrecoin", "symbol": "alias", "name": "Alias" }, - { - "id": "spectre-dividend-token", - "symbol": "sxdt", - "name": "Spectre.ai Dividend Token" - }, - { - "id": "spectresecuritycoin", - "symbol": "xspc", - "name": "SpectreSecurityCoin" - }, - { - "id": "spectre-utility-token", - "symbol": "sxut", - "name": "Spectre.ai Utility Token" - }, - { "id": "spectrum", "symbol": "spt", "name": "SPECTRUM" }, - { "id": "spectrum-cash", "symbol": "xsm", "name": "Spectrum Cash" }, - { "id": "spectrum-token", "symbol": "spec", "name": "Spectrum Token" }, - { "id": "speedcash", "symbol": "scs", "name": "Speedcash" }, - { "id": "speed-coin", "symbol": "speed", "name": "Speed Coin" }, - { - "id": "speed-mining-service", - "symbol": "sms", - "name": "Speed Mining Service" - }, - { "id": "speed-star-joc", "symbol": "joc", "name": "Speed Star JOC" }, - { "id": "speed-star-speed", "symbol": "speed", "name": "Speed Star SPEED" }, - { "id": "speed-star-star", "symbol": "star", "name": "Speed Star STAR" }, - { "id": "spellfire", "symbol": "spellfire", "name": "Spellfire" }, - { "id": "spellprinter", "symbol": "spellp", "name": "SpellPrinter" }, - { "id": "spell-token", "symbol": "spell", "name": "Spell Token" }, - { - "id": "spell-token-wormhole", - "symbol": "spell", - "name": "Spell Token (Wormhole)" - }, - { "id": "spendcoin", "symbol": "spnd", "name": "Spendcoin" }, - { "id": "spender-x", "symbol": "spdx", "name": "SPENDER-X" }, - { "id": "sperax", "symbol": "spa", "name": "Sperax" }, - { "id": "sperax-usd", "symbol": "usds", "name": "Sperax USD" }, - { "id": "sphere", "symbol": "sphr", "name": "Sphere" }, - { "id": "sphere-finance", "symbol": "sphere", "name": "Sphere Finance" }, - { "id": "sphere-social", "symbol": "sat", "name": "Social Activity Token" }, - { "id": "spherium", "symbol": "sphri", "name": "Spherium" }, - { "id": "spheroid-universe", "symbol": "sph", "name": "Spheroid Universe" }, - { "id": "sphinxel", "symbol": "spx", "name": "Sphinxel" }, - { "id": "sphynx", "symbol": "sphynx", "name": "Sphynx" }, - { "id": "sphynx-eth", "symbol": "sphynx", "name": "Sphynx ETH" }, - { "id": "sphynx-labs", "symbol": "sphynx", "name": "Sphynx Labs" }, - { "id": "sphynx-network", "symbol": "sph", "name": "Sphynx Network" }, - { "id": "spice", "symbol": "spice", "name": "Spice Token" }, - { "id": "spice-dao", "symbol": "spice", "name": "Spice DAO" }, - { "id": "spice-finance", "symbol": "spice", "name": "SPICE" }, - { "id": "spiderdao", "symbol": "spdr", "name": "SpiderDAO" }, - { "id": "spideyfloki", "symbol": "spideyxmas", "name": "SpideyFloki" }, - { "id": "spidey-inu", "symbol": "spidey inu", "name": "Spidey Inu" }, - { "id": "spike-inu", "symbol": "spki", "name": "Spike Inu" }, - { "id": "spin", "symbol": "spin", "name": "SPIN" }, - { "id": "spinada-cash", "symbol": "spin", "name": "Spinada Cash" }, - { "id": "spindle", "symbol": "spd", "name": "SPINDLE" }, - { "id": "spintop", "symbol": "spin", "name": "Spintop" }, - { "id": "spiral", "symbol": "spr", "name": "Spiral" }, - { "id": "spiritdao-ghost", "symbol": "ghost", "name": "SpiritDAO Ghost" }, - { - "id": "spirit-orb-pets-care-token", - "symbol": "care", - "name": "Spirit Orb Pets Care Token" - }, - { "id": "spiritswap", "symbol": "spirit", "name": "SpiritSwap" }, - { "id": "splash", "symbol": "spl", "name": "Splash" }, - { "id": "splat", "symbol": "splat", "name": "SPLAT" }, - { "id": "splinterlands", "symbol": "sps", "name": "Splinterlands" }, - { "id": "splyt", "symbol": "shopx", "name": "SHOPX" }, - { "id": "spongebob-square", "symbol": "spongs", "name": "SpongeBob Square" }, - { "id": "sponsee", "symbol": "spon", "name": "Sponsee" }, - { - "id": "spookeletons-token", - "symbol": "spkl", - "name": "Spookeletons Token" - }, - { "id": "spooky10000", "symbol": "SPK10k", "name": "Spooky10000" }, - { "id": "spookyshiba", "symbol": "spookyshiba", "name": "SpookyShiba" }, - { "id": "spookyswap", "symbol": "boo", "name": "Spookyswap" }, - { "id": "spool-dao-token", "symbol": "spool", "name": "Spool DAO Token" }, - { "id": "spore", "symbol": "spore", "name": "Spore" }, - { "id": "spores-network", "symbol": "spo", "name": "Spores Network" }, - { "id": "sporkdao", "symbol": "spork", "name": "SporkDAO" }, - { "id": "sport-and-leisure", "symbol": "snl", "name": "Sport and Leisure" }, - { "id": "sportemon-go-x", "symbol": "sgox", "name": "Sportemon-Go X" }, - { "id": "sport-investing", "symbol": "sitx", "name": "Sport Investing" }, - { "id": "sportium", "symbol": "sprt", "name": "Sportium" }, - { "id": "sportoken", "symbol": "spt", "name": "Sportoken" }, - { "id": "sportsicon", "symbol": "$icons", "name": "SportsIcon" }, - { "id": "sportzchain", "symbol": "spn", "name": "Sportzchain" }, - { "id": "spots", "symbol": "spt", "name": "Spots" }, - { "id": "sprink", "symbol": "sprink", "name": "Sprink" }, - { "id": "sprint-coin", "symbol": "sprx", "name": "Sprint Coin" }, - { - "id": "spritzmoon-crypto", - "symbol": "spritzmoon", - "name": "SpritzMoon Crypto" - }, - { "id": "sprouts", "symbol": "sprts", "name": "Sprouts" }, - { "id": "sproutsextreme", "symbol": "spex", "name": "SproutsExtreme" }, - { "id": "spunk-vault-nftx", "symbol": "spunk", "name": "SPUNK Vault (NFTX)" }, - { "id": "spyritcoin", "symbol": "spyrit", "name": "SpyritCoin" }, - { "id": "spywolf", "symbol": "$spy", "name": "Spywolf" }, - { "id": "sqgl-vault-nftx", "symbol": "sqgl", "name": "SQGL Vault (NFTX)" }, - { "id": "square-token", "symbol": "SQUA", "name": "Square Token" }, - { "id": "squawk", "symbol": "squawk", "name": "Squawk" }, - { "id": "squeeze-token", "symbol": "SQUEEZE", "name": "Squeeze Token" }, - { "id": "squid", "symbol": "squid", "name": "SquidDao" }, - { "id": "squid-2", "symbol": "squid", "name": "SQUID Finance" }, - { "id": "squidanomics", "symbol": "squid", "name": "Squidanomics" }, - { "id": "squid-game", "symbol": "squid", "name": "Squid Game" }, - { "id": "squid-inu", "symbol": "squid", "name": "Squid Inu" }, - { "id": "squid-moon", "symbol": "sqm", "name": "Squid Moon" }, - { "id": "squid-pet", "symbol": "squidpet", "name": "Squid Pet" }, - { "id": "squidverse3d", "symbol": "svg", "name": "SquidVerse3D" }, - { "id": "squirrel-finance", "symbol": "nuts", "name": "Squirrel Finance" }, - { "id": "squirt-game", "symbol": "SQUIRT", "name": "Squirt Game" }, - { "id": "squishiverse", "symbol": "slime", "name": "SquishiVerse" }, - { "id": "squoge-coin", "symbol": "sqc", "name": "Squoge Coin" }, - { "id": "srcoin", "symbol": "srh", "name": "SRH" }, - { "id": "sriracha-inu", "symbol": "sriracha", "name": "Sriracha Inu" }, - { "id": "srnartgallery", "symbol": "sact", "name": "srnArtGallery" }, - { - "id": "srnartgallery-tokenized-arts", - "symbol": "sista", - "name": "srnArtGallery Tokenized Arts" - }, - { "id": "srune", "symbol": "srune", "name": "sRUNE" }, - { "id": "ssv-network", "symbol": "ssv", "name": "SSV Network" }, - { "id": "stabilize", "symbol": "stbz", "name": "Stabilize" }, - { "id": "stabilize-bsc", "symbol": "stbb", "name": "Stabilize BSC" }, - { "id": "stabilize-token", "symbol": "set", "name": "Stabilize Token" }, - { "id": "stabilize-usd", "symbol": "susd", "name": "Stabilize USD" }, - { "id": "stable-1inch", "symbol": "one1inch", "name": "Stable 1inch" }, - { "id": "stable-asset", "symbol": "sta", "name": "STABLE ASSET" }, - { "id": "stable-btc", "symbol": "onebtc", "name": "Stable BTC" }, - { "id": "stabledoc-token", "symbol": "sdt", "name": "Stabledoc Token" }, - { "id": "stable-fox", "symbol": "onefox", "name": "Stable FOX" }, - { "id": "stable-mph", "symbol": "onemph", "name": "Stable MPH" }, - { "id": "stable-uni", "symbol": "oneuni", "name": "Stable UNI" }, - { "id": "stableusd", "symbol": "USDS", "name": "Stably USD" }, - { "id": "stacker-ventures", "symbol": "stack", "name": "Stacker Ventures" }, - { "id": "stackos", "symbol": "stack", "name": "StackOS" }, - { "id": "stacktical", "symbol": "dsla", "name": "DSLA Protocol" }, - { "id": "stack-treasury", "symbol": "STACKT", "name": "Stack Treasury" }, - { "id": "stacy", "symbol": "stacy", "name": "Stacy" }, - { "id": "stader", "symbol": "sd", "name": "Stader" }, - { "id": "stader-lunax", "symbol": "lunax", "name": "Stader LunaX" }, - { "id": "stadium", "symbol": "std", "name": "Stadium" }, - { "id": "stadium-ape", "symbol": "SAPE", "name": "Stadium Ape" }, - { "id": "stadium-pepe", "symbol": "SPEP", "name": "Stadium Pepe" }, - { "id": "stafi", "symbol": "fis", "name": "Stafi" }, - { "id": "stafi-ratom", "symbol": "ratom", "name": "StaFi rATOM" }, - { "id": "stakd-token", "symbol": "stkd", "name": "Stakd Token" }, - { "id": "stakeborg-dao", "symbol": "standard", "name": "Stakeborg DAO" }, - { "id": "stakecube", "symbol": "scc", "name": "Stakecube" }, - { - "id": "staked-aave-balancer-pool-token", - "symbol": "stkabpt", - "name": "Staked Aave Balancer Pool Token" - }, - { "id": "stake-dao", "symbol": "sdt", "name": "Stake DAO" }, - { "id": "staked-ether", "symbol": "steth", "name": "Lido Staked Ether" }, - { "id": "staked-exodia", "symbol": "sexod", "name": "Staked Exodia" }, - { "id": "staked-icx", "symbol": "sICX", "name": "Staked ICX" }, - { "id": "staked-klima", "symbol": "sklima", "name": "Staked Klima" }, - { "id": "staked_kuji", "symbol": "skuji", "name": "Staked Kuji" }, - { "id": "staked-luna", "symbol": "stluna", "name": "Staked Luna" }, - { "id": "staked-near", "symbol": "stnear", "name": "Staked NEAR" }, - { "id": "staked-note", "symbol": "snote", "name": "Staked Note" }, - { "id": "staked-olympus", "symbol": "sohm", "name": "Staked Olympus" }, - { "id": "staked-olympus-v1", "symbol": "sohm", "name": "Staked Olympus v1" }, - { "id": "staked-tarot", "symbol": "xTAROT", "name": "Staked TAROT" }, - { "id": "stakedthorus", "symbol": "stho", "name": "StakedThorus" }, - { "id": "staked-yeti", "symbol": "syeti", "name": "Staked YETI" }, - { "id": "stakedzen", "symbol": "stzen", "name": "StakedZEN" }, - { "id": "stakehound", "symbol": "stfiro", "name": "StakedFIRO" }, - { - "id": "stakehound-staked-ether", - "symbol": "steth", - "name": "StakeHound Staked Ether" - }, - { "id": "stakemoon", "symbol": "SMOON", "name": "Stakemoon" }, - { "id": "stakenet", "symbol": "xsn", "name": "Stakenet" }, - { "id": "staker", "symbol": "str", "name": "Staker Token" }, - { "id": "staker-dao", "symbol": "stkr", "name": "Staker DAO" }, - { "id": "stakewise", "symbol": "swise", "name": "StakeWise" }, - { - "id": "stakewise-staked-gno", - "symbol": "sgno", - "name": "StakeWise Staked GNO" - }, - { "id": "stakholders", "symbol": "sh", "name": "StakHolders" }, - { - "id": "stamen-tellus-token", - "symbol": "stt", - "name": "Stamen Tellus Token" - }, - { "id": "standard-protocol", "symbol": "stnd", "name": "Standard Protocol" }, - { "id": "standard-token", "symbol": "tst", "name": "Standard Token" }, - { "id": "stand-cash", "symbol": "sac", "name": "Stand Cash" }, - { "id": "stand-share", "symbol": "sas", "name": "Stand Share" }, - { "id": "star-atlas", "symbol": "atlas", "name": "Star Atlas" }, - { "id": "star-atlas-dao", "symbol": "polis", "name": "Star Atlas DAO" }, - { - "id": "star-atlas-wormhole", - "symbol": "atlas", - "name": "Star Atlas (Wormhole)" - }, - { "id": "starbase", "symbol": "star", "name": "Starbase" }, - { "id": "starbase-huobi", "symbol": "se", "name": "Starbase Huobi" }, - { "id": "starbaseuniverse", "symbol": "suni", "name": "StarbaseUniverse" }, - { "id": "starbots", "symbol": "bot", "name": "Starbots" }, - { "id": "starchain", "symbol": "stc", "name": "StarChain" }, - { "id": "starchaindoge", "symbol": "starchaindoge", "name": "StarChainDoge" }, - { "id": "starchi", "symbol": "elixir", "name": "Starchi" }, - { "id": "starcoin", "symbol": "stc", "name": "Starcoin" }, - { "id": "star-foxx", "symbol": "foxx", "name": "Star Foxx" }, - { "id": "stargate-finance", "symbol": "stg", "name": "Stargate Finance" }, - { "id": "stargaze", "symbol": "stars", "name": "Stargaze" }, - { - "id": "stargazer-protocol", - "symbol": "stardust", - "name": "Stargazer Protocol" - }, - { "id": "stark-chain", "symbol": "stark", "name": "Stark Chain" }, - { "id": "starlaunch", "symbol": "stars", "name": "StarLaunch" }, - { "id": "starlink", "symbol": "starl", "name": "StarLink" }, - { "id": "starly", "symbol": "starly", "name": "Starly" }, - { "id": "starmon-token", "symbol": "smon", "name": "StarMon Token" }, - { "id": "starname", "symbol": "iov", "name": "Starname" }, - { "id": "starnodes", "symbol": "strz", "name": "Starnodes" }, - { "id": "starpad", "symbol": "srp", "name": "Starpad" }, - { "id": "starplay", "symbol": "stpc", "name": "StarPlay" }, - { "id": "starsharks", "symbol": "sss", "name": "StarSharks" }, - { "id": "starsharks-sea", "symbol": "sea", "name": "StarSharks SEA" }, - { "id": "star-shib", "symbol": "STARSB", "name": "Star Shib" }, - { "id": "starship", "symbol": "starship", "name": "StarShip" }, - { "id": "star-ship-royal", "symbol": "SSR", "name": "Star Ship Royal" }, - { "id": "starship-token", "symbol": "stars", "name": "StarShip Token" }, - { "id": "starstream", "symbol": "stars", "name": "Starstream" }, - { "id": "starterra", "symbol": "stt", "name": "StarTerra" }, - { "id": "startfi", "symbol": "stfi", "name": "StartFi" }, - { "id": "start-vesting", "symbol": "vest", "name": "START Vesting" }, - { "id": "star-wars-cat", "symbol": "swcat", "name": "Star Wars Cat" }, - { "id": "starwire", "symbol": "str", "name": "Starwire" }, - { - "id": "starworks-global-ecosystem", - "symbol": "starx", - "name": "STARX Token" - }, - { "id": "stasis-eurs", "symbol": "eurs", "name": "STASIS EURO" }, - { "id": "stater", "symbol": "str", "name": "Stater" }, - { "id": "statera", "symbol": "sta", "name": "Statera" }, - { "id": "statik", "symbol": "statik", "name": "Statik" }, - { "id": "status", "symbol": "SNT", "name": "Status" }, - { "id": "stax-protocol", "symbol": "stax", "name": "Stax Protocol" }, - { - "id": "stay-in-destiny-world", - "symbol": "siw", - "name": "Stay In Destiny World" - }, - { "id": "staysafu", "symbol": "safu", "name": "StaySAFU" }, - { "id": "steakbank-finance", "symbol": "sbf", "name": "SteakBank Finance" }, - { "id": "steaks-finance", "symbol": "steak", "name": "Steaks Finance" }, - { "id": "steak-token", "symbol": "steak", "name": "Steak Token" }, - { "id": "stealthcoin", "symbol": "xst", "name": "Stealth" }, - { "id": "steam-exchange", "symbol": "steamx", "name": "Steam Exchange" }, - { "id": "steel", "symbol": "steel", "name": "Steel" }, - { "id": "steem", "symbol": "steem", "name": "Steem" }, - { "id": "steem-dollars", "symbol": "sbd", "name": "Steem Dollars" }, - { "id": "stellar", "symbol": "xlm", "name": "Stellar" }, - { "id": "stellar-diamond", "symbol": "xld", "name": "Stellar Diamond" }, - { "id": "stellarinu", "symbol": "stellarinu", "name": "StellarInu" }, - { "id": "stellar-invictus", "symbol": "tryon", "name": "Stellar Invictus" }, - { "id": "stellarpayglobal", "symbol": "xlpg", "name": "StellarPayGlobal" }, - { "id": "stellaswap", "symbol": "stella", "name": "StellaSwap" }, - { "id": "stellite", "symbol": "xla", "name": "Scala" }, - { "id": "stemx", "symbol": "stemx", "name": "STEMX" }, - { "id": "step", "symbol": "step", "name": "Step" }, - { "id": "step-app-fitfi", "symbol": "fitfi", "name": "Step.App - FITFI" }, - { "id": "step-finance", "symbol": "step", "name": "Step Finance" }, - { "id": "step-hero", "symbol": "hero", "name": "Step Hero" }, - { "id": "step-hero-soul", "symbol": "step", "name": "Step Hero Soul" }, - { "id": "stepn", "symbol": "gmt", "name": "STEPN" }, - { "id": "stib-token", "symbol": "sti", "name": "StiB" }, - { - "id": "stickman-battleground", - "symbol": "stman", - "name": "Stickman Battleground" - }, - { "id": "stilton-musk", "symbol": "stilton", "name": "Stilton Musk" }, - { "id": "stimmy", "symbol": "STIMMY", "name": "$STIMMY" }, - { "id": "sting", "symbol": "stn", "name": "Sting" }, - { "id": "stingdefi", "symbol": "sdfi", "name": "StingDefi" }, - { "id": "stipend", "symbol": "spd", "name": "Stipend" }, - { "id": "stkd-scrt", "symbol": "stkd", "name": "Stkd SCRT" }, - { "id": "stobox-token", "symbol": "stbu", "name": "Stobox" }, - { "id": "ston", "symbol": "ston", "name": "Ston" }, - { "id": "stoneage-nft", "symbol": "ges", "name": "Stoneage NFT" }, - { - "id": "stoned-ape-crew-index", - "symbol": "sac", - "name": "Stoned Ape Crew Index" - }, - { "id": "stoned-shiba", "symbol": "stoned", "name": "Stoned Shiba" }, - { "id": "stone-token", "symbol": "stn", "name": "Stone Token" }, - { "id": "stonk", "symbol": "stonk", "name": "Stonk Swap" }, - { "id": "stonk-2", "symbol": "stonk", "name": "Stonk" }, - { "id": "stonkbase", "symbol": "sbf", "name": "StonkBase" }, - { "id": "stonk-league", "symbol": "a", "name": "Aegis Token" }, - { "id": "stopelon", "symbol": "stopelon", "name": "StopElon" }, - { - "id": "storage-area-network-anywhere", - "symbol": "sana", - "name": "Storage Area Network Anywhere" - }, - { "id": "storichain-token", "symbol": "tori", "name": "Storichain Token" }, - { "id": "storiqa", "symbol": "stq", "name": "Storiqa" }, - { "id": "storj", "symbol": "storj", "name": "Storj" }, - { "id": "storks-token", "symbol": "storks", "name": "STORKS TOKEN" }, - { "id": "storm", "symbol": "stmx", "name": "StormX" }, - { - "id": "storm-bringer-token", - "symbol": "stb", - "name": "Storm Bringer token" - }, - { "id": "storm-token", "symbol": "storm", "name": "Storm Token" }, - { "id": "storx", "symbol": "srx", "name": "StorX" }, - { "id": "story", "symbol": "story", "name": "Story" }, - { "id": "stox", "symbol": "stx", "name": "Stox" }, - { "id": "stp-network", "symbol": "stpt", "name": "STP Network" }, - { "id": "strains", "symbol": "sfn", "name": "Strains" }, - { "id": "straitsx-indonesia-rupiah", "symbol": "xidr", "name": "XIDR" }, - { "id": "stratis", "symbol": "strax", "name": "Stratis" }, - { "id": "stratos", "symbol": "stos", "name": "Stratos" }, - { "id": "strayacoin", "symbol": "nah", "name": "Strayacoin" }, - { "id": "strayinu", "symbol": "strayInu", "name": "StrayInu" }, - { "id": "stream-protocol", "symbol": "stpl", "name": "Stream Protocol" }, - { "id": "streamr", "symbol": "data", "name": "Streamr" }, - { "id": "streamr-xdata", "symbol": "xdata", "name": "Streamr XDATA" }, - { - "id": "stream-smart-business", - "symbol": "$SSB", - "name": "Stream Smart Business" - }, - { "id": "streeth", "symbol": "STREETH", "name": "STREETH" }, - { "id": "street-runner", "symbol": "srg", "name": "Street Runner" }, - { "id": "strike", "symbol": "strk", "name": "Strike" }, - { "id": "strikecoin", "symbol": "strx", "name": "StrikeX" }, - { "id": "stripcoin", "symbol": "strip", "name": "StripCoin" }, - { "id": "strip-finance", "symbol": "strip", "name": "Strip Finance" }, - { "id": "strips-finance", "symbol": "strp", "name": "Strips Finance" }, - { "id": "stripto", "symbol": "strip", "name": "Stripto" }, - { "id": "strite", "symbol": "stri", "name": "Strite" }, - { "id": "strong", "symbol": "strong", "name": "Strong" }, - { "id": "strong-bull", "symbol": "bull", "name": "Strong Bull" }, - { "id": "stronger", "symbol": "strngr", "name": "Stronger" }, - { "id": "stronghands", "symbol": "shnd", "name": "StrongHands" }, - { - "id": "stronghands-finance", - "symbol": "ishnd", - "name": "StrongHands Finance" - }, - { - "id": "stronghands-masternode", - "symbol": "shmn", - "name": "StrongHands Masternode" - }, - { "id": "stronghold-token", "symbol": "shx", "name": "Stronghold Token" }, - { "id": "strong-inu", "symbol": "sinu", "name": "Strong Inu" }, - { "id": "strongnode", "symbol": "sne", "name": "StrongNode" }, - { "id": "structure-finance", "symbol": "stf", "name": "Structure Finance" }, - { "id": "strudel-finance", "symbol": "trdl", "name": "Strudel Finance" }, - { "id": "student-coin", "symbol": "stc", "name": "Student Coin" }, - { "id": "studio-shibli", "symbol": "shibli", "name": "Studio Shibli" }, - { "id": "studyum", "symbol": "stud", "name": "Studyum" }, - { "id": "subgame", "symbol": "sgb", "name": "SubGame" }, - { "id": "subme", "symbol": "sub", "name": "Subme" }, - { "id": "subquery-network", "symbol": "sqt", "name": "SubQuery Network" }, - { "id": "substratum", "symbol": "sub", "name": "Substratum" }, - { "id": "subx-finance", "symbol": "sfx", "name": "SUBX FINANCE LAB" }, - { "id": "success-inu", "symbol": "SUCCESS", "name": "SUCCESS INU" }, - { "id": "succor-coin", "symbol": "succor", "name": "Succor Coin" }, - { "id": "such-shiba", "symbol": "such", "name": "Such Shiba" }, - { "id": "sucrecoin", "symbol": "xsr", "name": "Sucrecoin" }, - { "id": "sugarbounce", "symbol": "tip", "name": "SugarBounce" }, - { "id": "sugarchain", "symbol": "sugar", "name": "Sugarchain" }, - { "id": "sugar-kingdom", "symbol": "candy", "name": "Sugar Kingdom" }, - { "id": "sukhavati-network", "symbol": "skt", "name": "Sukhavati Network" }, - { "id": "suku", "symbol": "SUKU", "name": "SUKU" }, - { "id": "sulgecoin", "symbol": "sug", "name": "Sulgecoin" }, - { "id": "sumcoin", "symbol": "sum", "name": "Sumcoin" }, - { "id": "summeris", "symbol": "sum", "name": "Summeris" }, - { "id": "summit-defi", "symbol": "summit", "name": "Summit DeFi [OLD]" }, - { "id": "summit-defi-2", "symbol": "summit", "name": "Summit DeFi" }, - { "id": "summoner", "symbol": "sut", "name": "Summoner" }, - { "id": "sumokoin", "symbol": "sumo", "name": "Sumokoin" }, - { "id": "sumswap", "symbol": "sum", "name": "SumSwap" }, - { "id": "sun", "symbol": "sun", "name": "SUN" }, - { "id": "suncontract", "symbol": "snc", "name": "SunContract" }, - { "id": "sundaeswap", "symbol": "sundae", "name": "SundaeSwap" }, - { - "id": "sunder-goverance-token", - "symbol": "sunder", - "name": "Sunder Goverance Token" - }, - { "id": "sunflower-farm", "symbol": "sff", "name": "Sunflower Farm" }, - { "id": "sunflower-finance", "symbol": "sfo", "name": "Sunflower Finance" }, - { "id": "suni", "symbol": "suni", "name": "SUNI" }, - { "id": "sunny-aggregator", "symbol": "sunny", "name": "Sunny Aggregator" }, - { "id": "sunnysideup", "symbol": "SSU", "name": "SunnySideUp" }, - { "id": "sunrise", "symbol": "sunc", "name": "Sunrise" }, - { "id": "sunshield", "symbol": "sshld", "name": "SunShield" }, - { "id": "sun-token", "symbol": "sun", "name": "Sun Token" }, - { "id": "supa-foundation", "symbol": "supa", "name": "SUPA Foundation" }, - { "id": "supe-infinity", "symbol": "supe", "name": "Supe Infinity" }, - { "id": "super8", "symbol": "s8", "name": "Super8" }, - { "id": "superalgos", "symbol": "sa", "name": "Superalgos" }, - { "id": "superbid", "symbol": "superbid", "name": "SuperBid" }, - { - "id": "superbnb-finance", - "symbol": "SPB", - "name": "SuperBNB.Finance [OLD]" - }, - { "id": "superbonds", "symbol": "sb", "name": "SuperBonds" }, - { - "id": "superbrain-capital-dao", - "symbol": "$sbc", - "name": "SuperBrain Capital Dao" - }, - { "id": "superciety", "symbol": "super", "name": "Superciety" }, - { "id": "supercoin", "symbol": "super", "name": "SuperCoin" }, - { - "id": "super-coinview-token", - "symbol": "scv", - "name": "Super CoinView Token" - }, - { "id": "superdoge", "symbol": "SUPDOG", "name": "SuperDoge" }, - { "id": "superfarm", "symbol": "super", "name": "SuperFarm" }, - { "id": "super-floki", "symbol": "sloki", "name": "Super Floki" }, - { "id": "superfuel", "symbol": "sfuel", "name": "SuperFuel" }, - { "id": "super-hero", "symbol": "sh", "name": "Super Hero" }, - { "id": "superinu", "symbol": "sinu", "name": "SuperInu" }, - { "id": "superlaunch", "symbol": "sla", "name": "SUPERLAUNCH" }, - { - "id": "superlauncher-dao", - "symbol": "launch", - "name": "SuperLauncher DAO" - }, - { "id": "super-mario", "symbol": "MARIO", "name": "SUPER MARIO" }, - { - "id": "supermegahyperdoge", - "symbol": "smhdoge", - "name": "SuperMegaHyperDoge" - }, - { "id": "superminesweeper", "symbol": "sm", "name": "SuperMinesweeper" }, - { "id": "supernova", "symbol": "snt", "name": "Supernova" }, - { "id": "superplayer-world", "symbol": "honor", "name": "SuperPlayer World" }, - { "id": "superrare", "symbol": "rare", "name": "SuperRare" }, - { "id": "super-saiyan-blue", "symbol": "ssb", "name": "Super Saiyan Blue" }, - { "id": "super-shiba", "symbol": "$sshiba", "name": "Super Shiba" }, - { "id": "superskynet", "symbol": "ssn", "name": "SuperSkyNet" }, - { "id": "supersonic-finance", "symbol": "ssn", "name": "Supersonic Finance" }, - { - "id": "super-three-kingdoms", - "symbol": "stk", - "name": "Super Three Kingdoms" - }, - { "id": "supertron", "symbol": "stro", "name": "Supertron" }, - { - "id": "supertx-governance-token", - "symbol": "sup", - "name": "SuperTx Governance Token" - }, - { "id": "super-whale-dao", "symbol": "swdao", "name": "Super Whale DAO" }, - { "id": "super-zero", "symbol": "sero", "name": "SERO" }, - { "id": "support-doge", "symbol": "supd", "name": "Support Doge" }, - { "id": "supra-token", "symbol": "supra", "name": "Supra Token" }, - { "id": "supremacy", "symbol": "sups", "name": "Supremacy" }, - { "id": "supreme-finance", "symbol": "hype", "name": "Supreme Finance" }, - { "id": "suprenft", "symbol": "snft", "name": "SupreNFT" }, - { "id": "suqa", "symbol": "sin", "name": "SINOVATE" }, - { "id": "sureremit", "symbol": "rmt", "name": "SureRemit" }, - { "id": "suretly", "symbol": "sur", "name": "Suretly" }, - { - "id": "surfexutilitytoken", - "symbol": "surf", - "name": "SurfExUtilityToken" - }, - { "id": "surf-finance", "symbol": "surf", "name": "Surf.Finance" }, - { "id": "surfmoon", "symbol": "surfmoon", "name": "SurfMoon" }, - { "id": "surge-inu", "symbol": "SURGE", "name": "Surge Inu" }, - { - "id": "survival-game-online", - "symbol": "surv", - "name": "Survival Game Online" - }, - { "id": "surviving-soldiers", "symbol": "ssg", "name": "Surviving Soldiers" }, - { "id": "survivor-finance", "symbol": "suv", "name": "Survivor Finance" }, - { "id": "sushi", "symbol": "sushi", "name": "Sushi" }, - { "id": "sushiba", "symbol": "sushiba", "name": "Sushiba" }, - { "id": "sushi-wormhole", "symbol": "sushi", "name": "Sushi (Wormhole)" }, - { - "id": "sustainable-energy-token", - "symbol": "set", - "name": "Sustainable Energy Token" - }, - { "id": "suteku", "symbol": "suteku", "name": "Suteku" }, - { "id": "suterusu", "symbol": "suter", "name": "Suterusu" }, - { "id": "suvereno", "symbol": "suv", "name": "Suvereno" }, - { "id": "swace", "symbol": "swace", "name": "Swace" }, - { "id": "swagbucks", "symbol": "bucks", "name": "SwagBucks" }, - { "id": "swag-finance", "symbol": "swag", "name": "SWAG Finance" }, - { - "id": "swag-finance-wormhole", - "symbol": "swag", - "name": "SWAG Finance (Wormhole)" - }, - { "id": "swak", "symbol": "SWAK", "name": "SWAK" }, - { "id": "s-wallet-protocol", "symbol": "swp", "name": "S-Wallet Protocol" }, - { "id": "swamp-coin", "symbol": "swamp", "name": "Swamp Coin" }, - { "id": "swampy", "symbol": "swamp", "name": "Swampy" }, - { "id": "swanlana", "symbol": "swan", "name": "Swanlana" }, - { "id": "swap", "symbol": "xwp", "name": "Swap" }, - { "id": "swapall", "symbol": "sap", "name": "SwapAll" }, - { "id": "swapdex", "symbol": "sdx", "name": "SwapDEX" }, - { "id": "swaperry", "symbol": "perry", "name": "Swaperry" }, - { "id": "swapfolio", "symbol": "swfl", "name": "Swapfolio" }, - { "id": "swapmatic", "symbol": "swam", "name": "SwapMatic" }, - { "id": "swapp", "symbol": "swapp", "name": "SWAPP Protocol" }, - { "id": "swappery-token", "symbol": "swpr", "name": "Swappery Token" }, - { "id": "swappi", "symbol": "ppi", "name": "Swappi" }, - { "id": "swapr", "symbol": "swpr", "name": "Swapr" }, - { "id": "swaptoken", "symbol": "token", "name": "SwapToken" }, - { "id": "swaptracker", "symbol": "swpt", "name": "SwapTracker" }, - { "id": "swapx", "symbol": "xwap", "name": "SwapX" }, - { "id": "swapxi-token", "symbol": "swapxi", "name": "SwapXI Token" }, - { "id": "swapz-app", "symbol": "swapz", "name": "SWAPZ.app" }, - { "id": "swarm", "symbol": "swm", "name": "Swarm Network" }, - { "id": "swarm-bzz", "symbol": "bzz", "name": "Swarm" }, - { "id": "swarm-city", "symbol": "swt", "name": "Swarm City" }, - { "id": "swarm-markets", "symbol": "smt", "name": "Swarm Markets" }, - { "id": "swash", "symbol": "swash", "name": "Swash" }, - { "id": "swass-finance", "symbol": "swass", "name": "SWASS Finance" }, - { "id": "sway-social", "symbol": "sway", "name": "Sway Social" }, - { "id": "sw-dao", "symbol": "swd", "name": "SW DAO" }, - { "id": "sweatcoin", "symbol": "sweat", "name": "SweatCoin" }, - { "id": "sweep-capital", "symbol": "sweep", "name": "Sweep Capital" }, - { "id": "sweet-btc", "symbol": "sbtc", "name": "Sweet BTC" }, - { "id": "swerve-dao", "symbol": "swrv", "name": "Swerve" }, - { "id": "swerve-protocol", "symbol": "swerve", "name": "SWERVE Protocol" }, - { "id": "swftcoin", "symbol": "swftc", "name": "SWFTCOIN" }, - { "id": "swgtoken", "symbol": "swg", "name": "SWGToken" }, - { "id": "swiftcash", "symbol": "swift", "name": "SwiftCash" }, - { "id": "swift-finance", "symbol": "swift", "name": "Swift Finance" }, - { "id": "swiftlance-token", "symbol": "swl", "name": "Swiftlance Token" }, - { "id": "swiftmoon", "symbol": "smoon", "name": "SwiftMoon" }, - { "id": "swincoin", "symbol": "swin", "name": "SwinCoin" }, - { "id": "swing", "symbol": "swing", "name": "Swing" }, - { "id": "swingby", "symbol": "SWINGBY", "name": "Swingby" }, - { "id": "swipe", "symbol": "sxp", "name": "SXP" }, - { "id": "swipe-network", "symbol": "swipe", "name": "SWIPE Network" }, - { "id": "swipe-wormhole", "symbol": "sxp", "name": "Swipe (Wormhole)" }, - { "id": "swipp", "symbol": "swipp", "name": "Swipp" }, - { "id": "swirge", "symbol": "swgb", "name": "Swirge" }, - { "id": "swirl-cash", "symbol": "swirl", "name": "Swirl Cash" }, - { "id": "swirltoken", "symbol": "SWIRL", "name": "SwirlToken" }, - { "id": "swissborg", "symbol": "chsb", "name": "SwissBorg" }, - { "id": "swisscoin-classic", "symbol": "sicc", "name": "Swisscoin-Classic" }, - { "id": "switch", "symbol": "esh", "name": "Switch" }, - { "id": "switcheo", "symbol": "swth", "name": "Carbon Protocol" }, - { "id": "swivel-governance", "symbol": "swiv", "name": "Swivel Governance" }, - { "id": "swole-doge", "symbol": "swole", "name": "Swole Doge" }, - { "id": "swop", "symbol": "swop", "name": "Swop" }, - { "id": "swtcoin", "symbol": "swat", "name": "SWTCoin" }, - { "id": "swusd", "symbol": "swusd", "name": "Swerve.fi USD" }, - { "id": "swyft", "symbol": "swyftt", "name": "SWYFT" }, - { "id": "sx-network", "symbol": "sx", "name": "SX Network" }, - { "id": "sya-x-flooz", "symbol": "sya", "name": "SYA x Flooz" }, - { "id": "sybc-coin", "symbol": "sybc", "name": "SYBC Coin" }, - { "id": "syfin", "symbol": "syf", "name": "Syfin" }, - { "id": "sylo", "symbol": "sylo", "name": "Sylo" }, - { "id": "symbiosis-finance", "symbol": "sis", "name": "Symbiosis Finance" }, - { "id": "symbol", "symbol": "xym", "name": "Symbol" }, - { "id": "symbull", "symbol": "symbull", "name": "SymBULL" }, - { "id": "symmetric", "symbol": "symm", "name": "Symmetric" }, - { "id": "symverse", "symbol": "sym", "name": "SymVerse" }, - { "id": "synapse-2", "symbol": "syn", "name": "Synapse" }, - { "id": "synapse-network", "symbol": "snp", "name": "Synapse Network" }, - { "id": "syncdao-governance", "symbol": "sdg", "name": "SyncDAO Governance" }, - { "id": "synchrobitcoin", "symbol": "snb", "name": "SynchroBitcoin" }, - { "id": "synchrolife", "symbol": "syc", "name": "SynchroLife" }, - { "id": "synchrony", "symbol": "scy", "name": "Synchrony" }, - { "id": "sync-network", "symbol": "sync", "name": "Sync Network" }, - { "id": "syndex", "symbol": "synd", "name": "SynDEX" }, - { "id": "syndicate-2", "symbol": "synr", "name": "MOBLAND" }, - { "id": "synesis-one", "symbol": "sns", "name": "Synesis One" }, - { "id": "synex-coin", "symbol": "minecraft", "name": "Synex Coin" }, - { "id": "synthetic-btc", "symbol": "xbtc", "name": "Synthetic BTC" }, - { "id": "synthetic-eth", "symbol": "xeth", "name": "Synthetic ETH" }, - { "id": "synthetic-ftt", "symbol": "xftt", "name": "Synthetic FTT" }, - { "id": "synthetic-sol", "symbol": "xsol", "name": "Synthetic SOL" }, - { "id": "synthetic-usd", "symbol": "xusd", "name": "Synthetic USD" }, - { "id": "synthetify-token", "symbol": "sny", "name": "Synthetify Token" }, - { - "id": "synthetix-network-token-wormhole", - "symbol": "snx", - "name": "Synthetix Network Token (Wormhole)" - }, - { "id": "synth-ousd", "symbol": "ousd", "name": "Synth oUSD" }, - { "id": "sypool", "symbol": "syp", "name": "Sypool" }, - { "id": "syrex", "symbol": "srx", "name": "Syrex" }, - { "id": "syscoin", "symbol": "sys", "name": "Syscoin" }, - { "id": "t99", "symbol": "t99", "name": "T99" }, - { "id": "tabank", "symbol": "tab", "name": "Tabank" }, - { "id": "taboo-token", "symbol": "taboo", "name": "Taboo Token" }, - { "id": "tabtrader", "symbol": "ttt", "name": "TabTrader" }, - { "id": "tacocat-token", "symbol": "tct", "name": "TacoCat Token" }, - { "id": "taco-dao", "symbol": "tdao", "name": "Taco DAO" }, - { "id": "tacoenergy", "symbol": "tacoe", "name": "TacoEnergy" }, - { "id": "tacos", "symbol": "taco", "name": "Tacos" }, - { "id": "tadpole-finance", "symbol": "tad", "name": "Tadpole" }, - { "id": "tadpole-token", "symbol": "tad", "name": "Tadpole Token" }, - { "id": "taf-token", "symbol": "taf", "name": "TAF Token" }, - { "id": "tagbond", "symbol": "tag", "name": "Tagbond" }, - { "id": "tagcoin", "symbol": "tag", "name": "Tagcoin" }, - { "id": "tag-protocol", "symbol": "tag", "name": "Tag Protocol" }, - { "id": "tagrcoin", "symbol": "tagr", "name": "TAGRcoin" }, - { "id": "tahu", "symbol": "tahu", "name": "TAHU" }, - { "id": "tai", "symbol": "tai", "name": "tBridge Token" }, - { "id": "taichi", "symbol": "tac", "name": "TaiChi" }, - { "id": "tails", "symbol": "tails", "name": "Tails" }, - { "id": "tajcoin", "symbol": "taj", "name": "TajCoin" }, - { - "id": "takamaka-green-coin", - "symbol": "tkg", - "name": "Takamaka Green Coin" - }, - { "id": "takeda-shin", "symbol": "takeda", "name": "Takeda Shin" }, - { "id": "taklimakan-network", "symbol": "tan", "name": "Taklimakan Network" }, - { "id": "tako-token", "symbol": "tako", "name": "Tako Token" }, - { "id": "talan", "symbol": "talan", "name": "Talan" }, - { "id": "talaria-inu", "symbol": "tali", "name": "Talaria Inu" }, - { "id": "talecraft", "symbol": "craft", "name": "TaleCraft" }, - { "id": "talentcoin", "symbol": "tlnt", "name": "TalentCoin" }, - { "id": "talent-coin", "symbol": "tlnt", "name": "Talent Coin" }, - { "id": "talent-token", "symbol": "ttx", "name": "Talent Token" }, - { "id": "tale-of-chain", "symbol": "tale", "name": "Tales Of Chain" }, - { "id": "taler", "symbol": "tlr", "name": "Taler" }, - { "id": "talkado", "symbol": "talk", "name": "Talkado" }, - { "id": "talken", "symbol": "talk", "name": "Talken" }, - { "id": "tama-finance", "symbol": "tama", "name": "Tama Finance" }, - { "id": "tangle", "symbol": "tngl", "name": "Tangle" }, - { "id": "tangoswap", "symbol": "tango", "name": "TangoSwap" }, - { "id": "tank-battle", "symbol": "tbl", "name": "Tank Battle" }, - { "id": "tank-gold", "symbol": "tgold", "name": "Tank Gold" }, - { "id": "tanks", "symbol": "tanks", "name": "Tanks" }, - { "id": "tanuki-token", "symbol": "tanuki", "name": "Tanuki" }, - { "id": "tap", "symbol": "xtp", "name": "Tap" }, - { "id": "tap-fantasy", "symbol": "tap", "name": "Tap Fantasy" }, - { "id": "tapioca-dao_token", "symbol": "tap", "name": "Tapioca DAO Token" }, - { "id": "tapme-token", "symbol": "tap", "name": "TAPME Token" }, - { "id": "tapmydata", "symbol": "tap", "name": "Tapmydata" }, - { "id": "tarality", "symbol": "TARAL", "name": "Tarality" }, - { "id": "taraxa", "symbol": "tara", "name": "Taraxa" }, - { - "id": "tardigrades-finance", - "symbol": "trdg", - "name": "Tardigrades Finance" - }, - { "id": "tarot", "symbol": "tarot", "name": "Tarot" }, - { "id": "tart", "symbol": "tart", "name": "Tartarus Finance" }, - { "id": "tartarus", "symbol": "tar", "name": "Tartarus" }, - { "id": "tastenft", "symbol": "taste", "name": "TasteNFT" }, - { "id": "tasty-token", "symbol": "tasty", "name": "Tasty Token" }, - { "id": "tattoomoney", "symbol": "tat2", "name": "TattooMoney" }, - { "id": "taunt-battleworld", "symbol": "", "name": "Taunt Battleworld" }, - { "id": "tavittcoin", "symbol": "tavitt", "name": "Tavittcoin" }, - { "id": "taxa-token", "symbol": "txt", "name": "Taxa Token" }, - { "id": "tbcc", "symbol": "tbcc", "name": "TBCC" }, - { "id": "t-bitcoin", "symbol": "tbtc", "name": "τBitcoin" }, - { "id": "tbtc", "symbol": "tbtc", "name": "tBTC" }, - { "id": "tcash", "symbol": "tcash", "name": "TCASH" }, - { "id": "tcgcoin", "symbol": "tcgcoin", "name": "TCGCoin" }, - { "id": "tcgcoin-2-0", "symbol": "tcg2", "name": "TCGCoin 2.0" }, - { "id": "tchalla", "symbol": "tcha", "name": "TCHALLA" }, - { "id": "tcoin-fun", "symbol": "tco", "name": "Tcoin.fun" }, - { "id": "tcw-token", "symbol": "tcw", "name": "TCW Token" }, - { "id": "tdoge", "symbol": "tdoge", "name": "τDoge" }, - { "id": "teal", "symbol": "teat", "name": "TEAL" }, - { "id": "team-clean-seas", "symbol": "tcs", "name": "Team Clean Seas" }, - { - "id": "team-heretics-fan-token", - "symbol": "th", - "name": "Team Heretics Fan Token" - }, - { - "id": "team-vitality-fan-token", - "symbol": "vit", - "name": "Team Vitality Fan Token" - }, - { "id": "teaswap-art", "symbol": "tsa", "name": "Teaswap Art" }, - { "id": "tea-token", "symbol": "tea", "name": "Tea Token" }, - { "id": "techcoin", "symbol": "tlc", "name": "Techcoin" }, - { - "id": "technology-innovation-project", - "symbol": "tip", - "name": "Technology Innovation Project" - }, - { "id": "techpay", "symbol": "tpc", "name": "Techpay" }, - { "id": "techshare-token", "symbol": "tcl", "name": "Techshare Token" }, - { "id": "tecracoin", "symbol": "tcr", "name": "TecraCoin ERC20" }, - { "id": "tectonic", "symbol": "tonic", "name": "Tectonic" }, - { "id": "teddy", "symbol": "teddy", "name": "Teddy" }, - { "id": "teddy-dog", "symbol": "tdg", "name": "Teddy Dog" }, - { "id": "teddy-doge", "symbol": "teddy", "name": "Teddy Doge" }, - { "id": "teddy-dollar", "symbol": "tsd", "name": "Teddy Dollar" }, - { "id": "te-food", "symbol": "tone", "name": "TE-FOOD" }, - { "id": "tekcoin", "symbol": "tek", "name": "TEKcoin" }, - { "id": "telcoin", "symbol": "tel", "name": "Telcoin" }, - { "id": "tellor", "symbol": "trb", "name": "Tellor" }, - { "id": "telos", "symbol": "tlos", "name": "Telos" }, - { "id": "telos-coin", "symbol": "telos", "name": "Teloscoin" }, - { "id": "temco", "symbol": "temco", "name": "TEMCO" }, - { "id": "templardao", "symbol": "tem", "name": "Templar DAO" }, - { "id": "temple", "symbol": "temple", "name": "TempleDAO" }, - { "id": "tempo-dao", "symbol": "tempo", "name": "Tempo DAO" }, - { "id": "tempus", "symbol": "temp", "name": "Tempus" }, - { "id": "temtem", "symbol": "tem", "name": "Temtum" }, - { "id": "ten", "symbol": "tenfi", "name": "TEN" }, - { "id": "tena", "symbol": "tena", "name": "TENA" }, - { "id": "tendies", "symbol": "tend", "name": "Tendies" }, - { "id": "tendieswap", "symbol": "tendie", "name": "TendieSwap" }, - { "id": "teneo", "symbol": "ten", "name": "Teneo" }, - { "id": "tenet", "symbol": "ten", "name": "Tenet" }, - { "id": "tengu", "symbol": "tengu", "name": "Tengu" }, - { "id": "tenset", "symbol": "10set", "name": "Tenset" }, - { "id": "tenshi", "symbol": "tenshi", "name": "Tenshi v2" }, - { "id": "tenup", "symbol": "tup", "name": "Tenup" }, - { "id": "ten-wallet", "symbol": "tenw", "name": "TEN Wallet" }, - { "id": "tenx", "symbol": "pay", "name": "TenX" }, - { "id": "tenxcoin", "symbol": "txc", "name": "TenXCoin" }, - { "id": "tepleton", "symbol": "tep", "name": "Tepleton" }, - { "id": "terablock", "symbol": "tbc", "name": "TeraBlock" }, - { "id": "terareum", "symbol": "tera", "name": "Terareum" }, - { "id": "tera-smart-money", "symbol": "tera", "name": "TERA" }, - { "id": "ternio", "symbol": "tern", "name": "Ternio" }, - { "id": "terra-aut", "symbol": "aut", "name": "Terra AUT" }, - { "id": "terra-cat", "symbol": "cat", "name": "Terra CAT" }, - { "id": "terra-cht", "symbol": "cht", "name": "Terra CHT" }, - { "id": "terra-cnt", "symbol": "cnt", "name": "Terra CNT" }, - { "id": "terracoin", "symbol": "trc", "name": "Terracoin" }, - { "id": "terra-dkt", "symbol": "dkt", "name": "Terra DKT" }, - { "id": "terra-eut", "symbol": "eut", "name": "Terra EUT" }, - { "id": "terrafloki", "symbol": "tfloki", "name": "TerraFloki" }, - { "id": "terra-gbt", "symbol": "gbt", "name": "Terra GBT" }, - { "id": "terra-hkt", "symbol": "hkt", "name": "Terra HKT" }, - { "id": "terra-idt", "symbol": "idt", "name": "Terra IDT" }, - { "id": "terra-int", "symbol": "int", "name": "Terra INT" }, - { "id": "terra-jpt", "symbol": "jpt", "name": "Terra JPT" }, - { "id": "terra-krw", "symbol": "krt", "name": "TerraKRW" }, - { "id": "terrakub", "symbol": "tkub", "name": "TerraKub" }, - { "id": "terraland-token", "symbol": "tland", "name": "TerraLand Token" }, - { "id": "terra-luna", "symbol": "luna", "name": "Terra" }, - { "id": "terramnt", "symbol": "mnt", "name": "Terra MNT" }, - { "id": "terra-name-service", "symbol": "tns", "name": "Terra Name Service" }, - { "id": "terran-coin", "symbol": "trr", "name": "Terran Coin" }, - { "id": "terra-pht", "symbol": "pht", "name": "Terra PHT" }, - { "id": "terra-sdt", "symbol": "sdt", "name": "Terra SDT" }, - { "id": "terra-set", "symbol": "set", "name": "Terra SET" }, - { "id": "terra-sgt", "symbol": "sgt", "name": "Terra SGT" }, - { "id": "terra-shiba", "symbol": "tshiba", "name": "Terra Shiba" }, - { "id": "terra-tht", "symbol": "tht", "name": "Terra THT" }, - { "id": "terrausd", "symbol": "ust", "name": "TerraUSD" }, - { "id": "terrausd-wormhole", "symbol": "ust", "name": "TerraUSD (Wormhole)" }, - { - "id": "terra-virtua-kolect", - "symbol": "tvk", - "name": "Terra Virtua Kolect" - }, - { "id": "terra-world-token", "symbol": "twd", "name": "Terra World Token" }, - { "id": "teslafan", "symbol": "teslf", "name": "Teslafan" }, - { "id": "teslafunds", "symbol": "tsf", "name": "Transaction Service Fee" }, - { "id": "tesla-inu", "symbol": "tesinu", "name": "Tesla Inu" }, - { "id": "tesra", "symbol": "tsr", "name": "Tesra" }, - { "id": "tessla-coin", "symbol": "tsla", "name": "Tessla Coin" }, - { "id": "tether", "symbol": "usdt", "name": "Tether" }, - { - "id": "tether-3x-short", - "symbol": "tetherdoom", - "name": "Tether 3x Short" - }, - { - "id": "tether-avalanche-bridged-usdt-e", - "symbol": "usdt", - "name": "Tether Avalanche Bridged (USDT.e)" - }, - { "id": "tether-eurt", "symbol": "eurt", "name": "Euro Tether" }, - { "id": "tether-gold", "symbol": "xaut", "name": "Tether Gold" }, - { "id": "tetherino", "symbol": "ttr", "name": "Tetherino" }, - { "id": "tethermoon", "symbol": "trm", "name": "TetherMoon" }, - { - "id": "tether-usd-celer", - "symbol": "ceusdt", - "name": "Tether USD - Celer" - }, - { - "id": "tether-usd-pos-wormhole", - "symbol": "usdtpo", - "name": "Tether USD (PoS) (Wormhole)" - }, - { - "id": "tether-usd-wormhole", - "symbol": "usdtso", - "name": "Tether USD (Wormhole)" - }, - { - "id": "tether-usd-wormhole-from-bsc", - "symbol": "usdtbs", - "name": "Tether USD (Wormhole from BSC)" - }, - { - "id": "tether-usd-wormhole-from-ethereum", - "symbol": "usdtet", - "name": "Tether USD (Wormhole from Ethereum)" - }, - { "id": "tethys-finance", "symbol": "tethys", "name": "Tethys Finance" }, - { "id": "teto-inu", "symbol": "tetoinu", "name": "Teto Inu" }, - { "id": "tetrahedra", "symbol": "tth", "name": "TetraHedra" }, - { "id": "tetra-pay", "symbol": "tpay", "name": "Tetra Pay" }, - { "id": "tetsu-inu", "symbol": "tetsu", "name": "Tetsu Inu" }, - { "id": "tetu", "symbol": "tetu", "name": "TETU" }, - { "id": "tezilla", "symbol": "tezilla", "name": "Tezilla" }, - { "id": "tezos", "symbol": "xtz", "name": "Tezos" }, - { "id": "tfs-token", "symbol": "TFS", "name": "TFS Token" }, - { "id": "tg-dao", "symbol": "tgdao", "name": "TG DAO" }, - { "id": "thaicoin", "symbol": "tac", "name": "Thaicoin" }, - { "id": "thales", "symbol": "thales", "name": "Thales" }, - { - "id": "the-4th-pillar", - "symbol": "four", - "name": "4thpillar technologies" - }, - { "id": "the-abyss", "symbol": "abyss", "name": "Abyss" }, - { - "id": "the-alliance-of-eragard", - "symbol": "era", - "name": "The Alliance of Eragard" - }, - { "id": "the-amaze-world", "symbol": "amze", "name": "The Amaze World" }, - { "id": "the-apis", "symbol": "api", "name": "The APIS" }, - { "id": "the-batdoge", "symbol": "batdoge", "name": "The Batdoge" }, - { "id": "thebigcoin", "symbol": "big", "name": "TheBigCoin" }, - { - "id": "the-bitcoin-family", - "symbol": "family", - "name": "The Bitcoin Family" - }, - { "id": "theboringtoken", "symbol": "tbt", "name": "TheBoringToken" }, - { "id": "theca", "symbol": "THECA", "name": "Theca" }, - { "id": "the-cancoin", "symbol": "canna", "name": "The CanCoin" }, - { "id": "thecash", "symbol": "tch", "name": "THECASH" }, - { "id": "the-cat-inu", "symbol": "thecat", "name": "The Cat Inu" }, - { "id": "the-chad-token", "symbol": "chad", "name": "The Chad Token" }, - { "id": "the-champcoin", "symbol": "tcc", "name": "The ChampCoin" }, - { "id": "the-citadel", "symbol": "thecitadel", "name": "The Citadel" }, - { - "id": "the-collective-coin", - "symbol": "tcc", - "name": "The Collective Coin" - }, - { "id": "the-commission", "symbol": "cmsn", "name": "The Commission" }, - { "id": "the-coop-network", "symbol": "gmd", "name": "The Coop Network" }, - { - "id": "the-corgi-of-polkabridge", - "symbol": "corgib", - "name": "The Corgi of PolkaBridge" - }, - { - "id": "the-crypto-prophecies", - "symbol": "tcp", - "name": "The Crypto Prophecies" - }, - { "id": "the-crypto-you", "symbol": "milk", "name": "The Crypto You" }, - { "id": "the-crypt-space", "symbol": "crypt", "name": "The Crypt Space" }, - { "id": "the-daox-index", "symbol": "daox", "name": "The DAOX Index" }, - { "id": "the-doge-nft", "symbol": "dog", "name": "The Doge NFT" }, - { "id": "the-doge-world", "symbol": "tdw", "name": "The Doge World" }, - { "id": "the-dynasty", "symbol": "dyt", "name": "The Dynasty" }, - { - "id": "the-employment-commons-work-token", - "symbol": "work", - "name": "The Employment Commons Work Token" - }, - { "id": "the-essential-coin", "symbol": "esc", "name": "The Essential Coin" }, - { - "id": "the-everlasting-parachain", - "symbol": "elp", - "name": "The Everlasting Parachain" - }, - { "id": "the-fire-token", "symbol": "xfr", "name": "The Fire Token" }, - { "id": "theflashcurrency", "symbol": "tfc", "name": "TheFlashCurrency" }, - { - "id": "the-forbidden-forest", - "symbol": "forestplus", - "name": "The Forbidden Forest" - }, - { "id": "theforce-trade", "symbol": "foc", "name": "TheForce Trade" }, - { "id": "thefutbolcoin", "symbol": "tfc", "name": "TheFutbolCoin" }, - { "id": "the-garden", "symbol": "tgc", "name": "The Garden" }, - { "id": "thegcccoin", "symbol": "gcc", "name": "Global Cryptocurrency" }, - { "id": "the-grand-banks", "symbol": "grand", "name": "The Grand Banks" }, - { "id": "the-graph", "symbol": "grt", "name": "The Graph" }, - { - "id": "the-graph-wormhole", - "symbol": "grt", - "name": "The Graph (Wormhole)" - }, - { "id": "the-hash-speed", "symbol": "ths", "name": "The Hash Speed" }, - { "id": "theholyrogercoin", "symbol": "roger", "name": "TheHolyRogerCoin" }, - { "id": "the-husl", "symbol": "husl", "name": "The HUSL" }, - { "id": "the-idols-nft", "symbol": "idol", "name": "The Idols NFT" }, - { "id": "thekey", "symbol": "tky", "name": "THEKEY" }, - { "id": "the-killbox-game", "symbol": "kbox", "name": "The Killbox Game" }, - { "id": "the-king", "symbol": "theking", "name": "The King" }, - { "id": "the-lab-finance", "symbol": "labo", "name": "The Lab Finance" }, - { "id": "the-last-war", "symbol": "tlw", "name": "The Last War" }, - { - "id": "the-legend-of-deification", - "symbol": "tlod", - "name": "The Legend of Deification" - }, - { "id": "the-mars-shiba", "symbol": "marsshib", "name": "The Mars Shiba" }, - { "id": "the-mask", "symbol": "dmask", "name": "The Mask" }, - { - "id": "the-midas-touch-gold", - "symbol": "tmtg", - "name": "The Midas Touch Gold" - }, - { "id": "themis", "symbol": "get", "name": "Themis Network" }, - { "id": "themis-2", "symbol": "mis", "name": "Themis" }, - { "id": "themis-protocol", "symbol": "tms", "name": "Themis Protocol" }, - { "id": "the-monopolist", "symbol": "mono", "name": "The Monopolist" }, - { "id": "the-moon-shiba", "symbol": "MOONSHIB", "name": "The Moon Shiba" }, - { "id": "the-nature-token", "symbol": "nature", "name": "The Nature Token" }, - { "id": "the-neighbours", "symbol": "neibr", "name": "The Neighbours" }, - { "id": "the-neko", "symbol": "neko", "name": "The Neko" }, - { "id": "the-node", "symbol": "the", "name": "THENODE" }, - { "id": "the-one-ring", "symbol": "ring", "name": "The One Ring" }, - { "id": "the-open-network", "symbol": "ton", "name": "The Open Network" }, - { "id": "theos", "symbol": "theos", "name": "Theos" }, - { "id": "theoscoin", "symbol": "ths", "name": "Theoscoin" }, - { "id": "the-pablo-token", "symbol": "pablo", "name": "The Pablo Token" }, - { "id": "the-parallel", "symbol": "prl", "name": "The Parallel" }, - { "id": "the-people-coin", "symbol": "peeps", "name": "The People’s Coin" }, - { - "id": "the-philosophers-stone", - "symbol": "tpos", - "name": "The Philosophers Stone" - }, - { "id": "the-phoenix", "symbol": "fire", "name": "The Phoenix" }, - { "id": "the-piece", "symbol": "piece", "name": "The Piece" }, - { "id": "thepiggygarden", "symbol": "tpg", "name": "ThePiggyGarden" }, - { "id": "the-plant-dao", "symbol": "sprout", "name": "The Plant Dao" }, - { "id": "the-randomdao", "symbol": "rnd", "name": "The RandomDAO" }, - { - "id": "the-rare-antiquities-token", - "symbol": "rat", - "name": "The Rare Antiquities Token [OLD]" - }, - { - "id": "the-rare-antiquities-token-2", - "symbol": "rat", - "name": "The Rare Antiquities Token" - }, - { - "id": "the-real-golden-inu", - "symbol": "trgi", - "name": "The Real Golden Inu" - }, - { - "id": "the-realm-defenders", - "symbol": "trd", - "name": "The Realm Defenders" - }, - { "id": "the-reaper", "symbol": "rpr", "name": "The Reaper" }, - { "id": "the-red-order", "symbol": "ordr", "name": "The Red Order" }, - { "id": "the-rocks", "symbol": "therocks", "name": "The Rocks" }, - { "id": "the-sandbox", "symbol": "sand", "name": "The Sandbox" }, - { - "id": "the-sandbox-wormhole", - "symbol": "sand", - "name": "The Sandbox (Wormhole)" - }, - { "id": "the-seed-farm", "symbol": "seed", "name": "The Seed Farm" }, - { "id": "thesolandao", "symbol": "SDO", "name": "TheSolanDAO" }, - { "id": "the-spartans", "symbol": "tsp", "name": "The Spartans" }, - { - "id": "the-starship-finance", - "symbol": "bip", - "name": "The Starship Finance" - }, - { "id": "the-sun-rises", "symbol": "sunrise", "name": "The Sun Rises" }, - { "id": "thetadrop", "symbol": "tdrop", "name": "ThetaDrop" }, - { "id": "theta-fuel", "symbol": "tfuel", "name": "Theta Fuel" }, - { "id": "thetan-arena", "symbol": "thg", "name": "Thetan Arena" }, - { "id": "thetan-coin", "symbol": "thc", "name": "Thetan Coin" }, - { "id": "theta-token", "symbol": "theta", "name": "Theta Network" }, - { "id": "the-three-kingdoms", "symbol": "ttk", "name": "The Three Kingdoms" }, - { - "id": "the-tokenized-bitcoin", - "symbol": "imbtc", - "name": "The Tokenized Bitcoin" - }, - { "id": "the-transfer-token", "symbol": "ttt", "name": "The Transfer Token" }, - { - "id": "the-transplant-coin", - "symbol": "tpc", - "name": "The Transplant Coin" - }, - { "id": "the-troller-coin", "symbol": "troller", "name": "The Troller Coin" }, - { "id": "the-wasted-lands", "symbol": "wal", "name": "The Wasted Lands" }, - { "id": "the-web3-project", "symbol": "web3", "name": "The Web3 Project" }, - { "id": "the-winkyverse", "symbol": "wnk", "name": "The Winkyverse" }, - { "id": "the-wolf-pack", "symbol": "PACK", "name": "The Wolf Pack" }, - { - "id": "the-xenobots-project", - "symbol": "xeno", - "name": "The Xenobots Project" - }, - { "id": "thingschain", "symbol": "tic", "name": "Thingschain" }, - { - "id": "thingsoperatingsystem", - "symbol": "tos", - "name": "ThingsOperatingSystem" - }, - { "id": "thinkium", "symbol": "tkm", "name": "Thinkium" }, - { "id": "this", "symbol": "this", "name": "THIS" }, - { "id": "thisoption", "symbol": "tons", "name": "Thisoption" }, - { "id": "thol-token", "symbol": "thol", "name": "THOL Token" }, - { "id": "thor", "symbol": "thor", "name": "Thor" }, - { "id": "thorchain", "symbol": "rune", "name": "THORChain" }, - { "id": "thorchain-erc20", "symbol": "rune", "name": "THORChain (ERC20)" }, - { "id": "thor-doge", "symbol": "thoge", "name": "Thor Doge" }, - { "id": "thorecash", "symbol": "tch", "name": "Thorecash (ERC-20)" }, - { "id": "thorecoin", "symbol": "thr", "name": "Thorecoin" }, - { "id": "thorenext", "symbol": "thx", "name": "Thorenext" }, - { "id": "thoreum", "symbol": "thoreum", "name": "Thoreum [OLD]" }, - { "id": "thoreum-v2", "symbol": "thoreum", "name": "Thoreum V2" }, - { "id": "thorncoin", "symbol": "thrn", "name": "Thorncoin" }, - { "id": "thors-mead", "symbol": "mead", "name": "Thors Mead" }, - { "id": "thorstarter", "symbol": "xrune", "name": "Thorstarter" }, - { "id": "thorswap", "symbol": "thor", "name": "THORSwap" }, - { "id": "thorus", "symbol": "tho", "name": "Thorus" }, - { "id": "thorwallet", "symbol": "tgt", "name": "THORWallet DEX" }, - { "id": "thought", "symbol": "tht", "name": "Thought" }, - { "id": "threefold-token", "symbol": "tft", "name": "ThreeFold Token" }, - { - "id": "threshold-network-token", - "symbol": "T", - "name": "Threshold Network Token" - }, - { "id": "throne", "symbol": "thn", "name": "Throne" }, - { "id": "thropic", "symbol": "thropic", "name": "Thropic (OLD)" }, - { "id": "thropic-v2", "symbol": "thropicv2", "name": "Thropic" }, - { "id": "thtf", "symbol": "thtf", "name": "THTF" }, - { "id": "thunderada-app", "symbol": "thunderada", "name": "ThunderADA.app" }, - { "id": "thunderbnb", "symbol": "thunderbnb", "name": "ThunderBNB" }, - { "id": "thundereth", "symbol": "thundereth", "name": "ThunderETH" }, - { "id": "thunderracer", "symbol": "racerr", "name": "ThunderRacer" }, - { "id": "thunder-run-bsc", "symbol": "thundrr", "name": "Thunder Run BSC" }, - { "id": "thunder-token", "symbol": "tt", "name": "ThunderCore" }, - { "id": "thunderverse", "symbol": "THUNDER", "name": "ThunderVerse" }, - { "id": "thx", "symbol": "thx", "name": "Thx!" }, - { "id": "thx-network", "symbol": "thx", "name": "THX Network" }, - { "id": "tia", "symbol": "tia", "name": "TIA" }, - { "id": "tianya-token", "symbol": "tyt", "name": "Tianya Token" }, - { "id": "tianyu-finance", "symbol": "tyc", "name": "Tianyu Finance" }, - { "id": "tiara", "symbol": "tti", "name": "Tiara" }, - { "id": "ticket-finance", "symbol": "ticket", "name": "Ticket Finance" }, - { "id": "ticoex-token", "symbol": "tico", "name": "TICOEX Token" }, - { "id": "tictalk", "symbol": "tic", "name": "TicTalk" }, - { "id": "tidal-finance", "symbol": "tidal", "name": "Tidal Finance" }, - { "id": "tierion", "symbol": "tnt", "name": "Tierion" }, - { "id": "tierra-meta", "symbol": "trmt", "name": "Tierra Meta" }, - { "id": "ties-network", "symbol": "tie", "name": "Ties.DB" }, - { "id": "tifi-token", "symbol": "tifi", "name": "TiFi Token" }, - { "id": "tiger-baby", "symbol": "tigerbaby", "name": "Tiger Baby" }, - { "id": "tigercash", "symbol": "tch", "name": "TigerCash" }, - { "id": "tiger-coin", "symbol": "tiger", "name": "Tiger Coin" }, - { "id": "tigercoin-2", "symbol": "tiger", "name": "TigerCoin" }, - { "id": "tiger-cub", "symbol": "tcub", "name": "Tiger Cub" }, - { "id": "tigerinu2022", "symbol": "tigerinu2022", "name": "TigerInu2022" }, - { "id": "tiger-king", "symbol": "tking", "name": "Tiger King Coin" }, - { "id": "tigerqueen", "symbol": "TQUEEN", "name": "TigerQueen" }, - { "id": "tiger-shares", "symbol": "tiger", "name": "Tiger Shares" }, - { "id": "tiger-token", "symbol": "tgnb", "name": "Tiger Token" }, - { "id": "tiki-token", "symbol": "tiki", "name": "Tiki Token" }, - { "id": "tilwiki", "symbol": "tlw", "name": "TilWiki" }, - { - "id": "timechain-swap-token", - "symbol": "tcs", - "name": "Timechain Swap Token" - }, - { "id": "timecoin-protocol", "symbol": "tmcn", "name": "Timecoin Protocol" }, - { "id": "timeleap-finance", "symbol": "time", "name": "Timeleap Finance" }, - { "id": "timelockcoin", "symbol": "tym", "name": "TimeLockCoin" }, - { "id": "time-new-bank", "symbol": "tnb", "name": "Time New Bank" }, - { "id": "timerr", "symbol": "timerr", "name": "Timerr" }, - { "id": "timers", "symbol": "ipm", "name": "Timers" }, - { "id": "timexspace", "symbol": "txs", "name": "TIMEXSPACE" }, - { "id": "tinville", "symbol": "tinv", "name": "TINVILLE" }, - { "id": "tiny-bonez", "symbol": "t1ny", "name": "Tiny Bonez" }, - { "id": "tiny-coin", "symbol": "tinc", "name": "Tiny Coin" }, - { "id": "tiny-colony", "symbol": "tiny", "name": "Tiny Colony" }, - { "id": "tip-blue", "symbol": "blue", "name": "Tip.Blue" }, - { "id": "tipinu", "symbol": "tipinu", "name": "Tipinu" }, - { "id": "tipsy", "symbol": "TIPSY", "name": "Tipsy" }, - { "id": "tipsycoin", "symbol": "$tipsy", "name": "TipsyCoin" }, - { "id": "tiraverse", "symbol": "tvrs", "name": "TiraVerse" }, - { "id": "titan-coin", "symbol": "ttn", "name": "Titan Coin" }, - { "id": "titan-hunters", "symbol": "tita", "name": "Titan Hunters" }, - { "id": "titania-token", "symbol": "titania", "name": "Titania Token" }, - { "id": "titano", "symbol": "titano", "name": "Titano" }, - { "id": "titanswap", "symbol": "titan", "name": "TitanSwap" }, - { "id": "titi-financial", "symbol": "titi", "name": "Titi Financial" }, - { "id": "title-network", "symbol": "tnet", "name": "Bitcoin Clashic" }, - { "id": "tits-token", "symbol": "tits", "name": "TITS Token" }, - { "id": "titsv2", "symbol": "tits", "name": "TitsV2" }, - { "id": "ti-value", "symbol": "tv", "name": "Ti-Value" }, - { "id": "tkbtoken", "symbol": "tkb", "name": "TKBToken" }, - { "id": "tkn-token", "symbol": "tknt", "name": "TKN Token" }, - { "id": "tmc", "symbol": "tmc", "name": "TMC" }, - { "id": "tmeta", "symbol": "tmt", "name": "TMETA" }, - { "id": "tnc-coin", "symbol": "tnc", "name": "TNC Coin" }, - { "id": "tnns", "symbol": "tnns", "name": "TNNS" }, - { "id": "toad-network", "symbol": "toad", "name": "TOAD.Network" }, - { "id": "togashi-inu", "symbol": "togashi", "name": "Togashi Inu" }, - { "id": "tokamak-network", "symbol": "ton", "name": "Tokamak Network" }, - { "id": "tokel", "symbol": "tkl", "name": "Tokel" }, - { "id": "tokemak", "symbol": "toke", "name": "Tokemak" }, - { "id": "tokemak-wormhole", "symbol": "toke", "name": "Tokemak (Wormhole)" }, - { "id": "tokemon", "symbol": "tkmn", "name": "Tokemon" }, - { "id": "toke-n", "symbol": "toke.n", "name": "TOKE.N" }, - { "id": "tokenasset", "symbol": "ntb", "name": "TokenAsset" }, - { "id": "tokenbank", "symbol": "tbank", "name": "Tokenbank" }, - { "id": "tokenbook", "symbol": "tbk", "name": "TokenBook" }, - { "id": "tokenbox", "symbol": "tbx", "name": "Tokenbox" }, - { "id": "tokencard", "symbol": "tkn", "name": "Monolith" }, - { "id": "token-cashpay", "symbol": "tcp", "name": "Token CashPay" }, - { "id": "token-cheetah", "symbol": "chtt", "name": "Token Cheetah" }, - { "id": "tokenclub", "symbol": "tct", "name": "TokenClub" }, - { "id": "tokendesk", "symbol": "tds", "name": "TokenDesk" }, - { "id": "token-dforce-usd", "symbol": "usx", "name": "dForce USD" }, - { "id": "token-echo", "symbol": "echo", "name": "Token Echo" }, - { - "id": "token-engineering-commons", - "symbol": "tec", - "name": "Token Engineering Commons" - }, - { "id": "tokenfy", "symbol": "tknfy", "name": "Tokenfy" }, - { "id": "tokengo", "symbol": "gpt", "name": "GoPower" }, - { "id": "tokenize-xchange", "symbol": "tkx", "name": "Tokenize Xchange" }, - { "id": "tokenjenny", "symbol": "jenn", "name": "TokenJenny" }, - { "id": "token-kennel", "symbol": "kennel", "name": "Token Kennel" }, - { "id": "tokenlon", "symbol": "lon", "name": "Tokenlon" }, - { "id": "token-of-power", "symbol": "top", "name": "Token of Power" }, - { "id": "tokenoid", "symbol": "noid", "name": "Tokenoid" }, - { "id": "tokenomy", "symbol": "ten", "name": "Tokenomy" }, - { "id": "tokenpay", "symbol": "tpay", "name": "TokenPay" }, - { "id": "tokenplace", "symbol": "tok", "name": "Tokenplace" }, - { "id": "tokenplay", "symbol": "top", "name": "Tokenplay" }, - { "id": "token-play", "symbol": "tp3", "name": "Token Play" }, - { "id": "token-pocket", "symbol": "tpt", "name": "Token Pocket" }, - { "id": "token-shelby", "symbol": "tsy", "name": "Token Shelby" }, - { - "id": "tokenstand-network", - "symbol": "stand", - "name": "TokenStand Network" - }, - { "id": "token-tkx", "symbol": "tkx", "name": "Token TKX" }, - { "id": "tokentuber", "symbol": "tuber", "name": "TokenTuber" }, - { "id": "tokes", "symbol": "tks", "name": "Tokes" }, - { "id": "toko", "symbol": "toko", "name": "Tokoin" }, - { "id": "tokocrypto", "symbol": "tko", "name": "Tokocrypto" }, - { "id": "tokok", "symbol": "tok", "name": "Tokok" }, - { "id": "tokonft", "symbol": "tkn", "name": "TokoNFT" }, - { "id": "tokpie", "symbol": "tkp", "name": "TOKPIE" }, - { "id": "tokyo", "symbol": "tokc", "name": "Tokyo Coin" }, - { "id": "tokyo-au", "symbol": "tokau", "name": "Tokyo AU" }, - { "id": "tokyo-inu", "symbol": "toki", "name": "Tokyo Inu" }, - { "id": "tolar", "symbol": "tol", "name": "Tolar" }, - { "id": "toll-free-swap", "symbol": "toll", "name": "Toll Free Swap" }, - { "id": "tomatotoken", "symbol": "tomato", "name": "TomatoToken" }, - { "id": "tomb", "symbol": "tomb", "name": "Tomb" }, - { "id": "tombprinter", "symbol": "tombp", "name": "TombPrinter" }, - { "id": "tomb-shares", "symbol": "tshare", "name": "Tomb Shares" }, - { "id": "tom-finance", "symbol": "tom", "name": "TOM Finance" }, - { "id": "tomochain", "symbol": "tomo", "name": "TomoChain" }, - { "id": "tomoe", "symbol": "tomoe", "name": "TomoChain ERC-20" }, - { "id": "tomtomcoin", "symbol": "toms", "name": "TomTomCoin" }, - { "id": "tonestra", "symbol": "tnr", "name": "Tonestra" }, - { "id": "tonstarter", "symbol": "tos", "name": "TONStarter" }, - { "id": "tontoken", "symbol": "ton", "name": "TONToken" }, - { "id": "toolape", "symbol": "tape", "name": "ToolApe" }, - { "id": "tools", "symbol": "tools", "name": "TOOLS" }, - { "id": "too-token", "symbol": "too", "name": "TOO Token" }, - { "id": "topbidder", "symbol": "bid", "name": "TopBidder" }, - { "id": "top-cat-inu", "symbol": "tcat", "name": "Top Cat inu" }, - { "id": "topchain", "symbol": "topc", "name": "TopChain" }, - { "id": "top-coin-token", "symbol": "tct", "name": "Top Coin Token" }, - { "id": "topdog", "symbol": "$TOPDOG", "name": "TOPDOG" }, - { "id": "topflower", "symbol": "tpf", "name": "TopFlower" }, - { "id": "topmanager", "symbol": "tmt", "name": "TopManager" }, - { "id": "top-network", "symbol": "top", "name": "TOP Network" }, - { "id": "topshelf-finance", "symbol": "liqr", "name": "Topshelf Finance" }, - { "id": "tor", "symbol": "tor", "name": "TOR" }, - { "id": "torchain", "symbol": "tor", "name": "Torchain" }, - { "id": "torekko", "symbol": "trk", "name": "Torekko" }, - { "id": "torex", "symbol": "tor", "name": "Torex" }, - { "id": "torg", "symbol": "torg", "name": "TORG" }, - { "id": "torii-finance", "symbol": "torii", "name": "Torii Finance" }, - { "id": "tornadao", "symbol": "nado", "name": "Tornadao" }, - { "id": "tornado-cash", "symbol": "torn", "name": "Tornado Cash" }, - { "id": "torocus-token", "symbol": "torocus", "name": "TOROCUS Token" }, - { "id": "torpedo", "symbol": "torpedo", "name": "Torpedo" }, - { "id": "torq-coin", "symbol": "torq", "name": "TORQ Coin" }, - { "id": "torum", "symbol": "xtm", "name": "Torum" }, - { "id": "t-os", "symbol": "tosc", "name": "T.OS" }, - { "id": "tosdis", "symbol": "dis", "name": "TosDis" }, - { "id": "toshinori-inu", "symbol": "toshinori", "name": "Toshinori Inu" }, - { "id": "toshi-token", "symbol": "toshi", "name": "Toshimon" }, - { - "id": "total-crypto-market-cap-token", - "symbol": "tcap", - "name": "Total Crypto Market Cap" - }, - { - "id": "totally-a-rug-pull", - "symbol": "tarp", - "name": "Totally A Rug Pull" - }, - { "id": "totem-earth-systems", "symbol": "ctzn", "name": "Totem" }, - { "id": "totemfi", "symbol": "totm", "name": "TotemFi" }, - { "id": "totem-finance", "symbol": "totem", "name": "Totem Finance" }, - { "id": "to-the-mars", "symbol": "mars", "name": "To The Mars" }, - { "id": "to-the-moon-token", "symbol": "TON", "name": "To The Moon Token" }, - { "id": "totoro-inu", "symbol": "totoro", "name": "Totoro Inu" }, - { - "id": "toucan-protocol-base-carbon-tonne", - "symbol": "bct", - "name": "Toucan Protocol: Base Carbon Tonne" - }, - { - "id": "toucan-protocol-nature-carbon-tonne", - "symbol": "nct", - "name": "Toucan Protocol: Nature Carbon Tonne" - }, - { "id": "touchcon", "symbol": "toc", "name": "TouchCon" }, - { "id": "touchfuture", "symbol": "tf", "name": "TouchFuture" }, - { - "id": "tourist-shiba-inu", - "symbol": "tourists", - "name": "Tourist Shiba Inu" - }, - { "id": "tourist-token", "symbol": "toto", "name": "Tourist Token" }, - { "id": "touriva", "symbol": "tour", "name": "Touriva" }, - { "id": "toursim-coin", "symbol": "TIC", "name": "Toursim Coin" }, - { "id": "tower", "symbol": "tower", "name": "Tower" }, - { - "id": "tower-defense-titans", - "symbol": "titans", - "name": "Tower Defense Titans" - }, - { "id": "tower-usd", "symbol": "twr", "name": "Tower USD" }, - { "id": "town-star", "symbol": "town", "name": "Town Star" }, - { "id": "toxicgamenft", "symbol": "txc", "name": "ToxicGameNft" }, - { "id": "toxic-game-nft", "symbol": "ntxc", "name": "Toxic Game NFT" }, - { "id": "toyshiba", "symbol": "toyshiba", "name": "ToySHIBA" }, - { "id": "tozex", "symbol": "toz", "name": "Tozex" }, - { "id": "tpiggy", "symbol": "tpiggy", "name": "Tpiggy" }, - { "id": "tp-swap", "symbol": "tp", "name": "Token Swap" }, - { "id": "tr3zor", "symbol": "tr3", "name": "Tr3zor" }, - { - "id": "trabzonspor-fan-token", - "symbol": "tra", - "name": "Trabzonspor Fan Token" - }, - { - "id": "trace-network-labs", - "symbol": "trace", - "name": "Trace Network Labs" - }, - { "id": "tracer-dao", "symbol": "tcr", "name": "Tracer DAO" }, - { "id": "tractor-joe", "symbol": "tractor", "name": "Tractor Joe" }, - { "id": "tradao", "symbol": "tod", "name": "TraDAO" }, - { "id": "tradcoin", "symbol": "trad", "name": "Tradcoin" }, - { "id": "trade-fighter", "symbol": "tdf", "name": "Trade Fighter" }, - { "id": "traders-coin", "symbol": "trdc", "name": "Traders Coin" }, - { - "id": "traders-global-business", - "symbol": "tgb", - "name": "Traders Global Business" - }, - { "id": "tradestars", "symbol": "tsx", "name": "TradeStars" }, - { "id": "trade-win", "symbol": "twi", "name": "Trade.win" }, - { "id": "tranche-finance", "symbol": "SLICE", "name": "Tranche Finance" }, - { "id": "tranchess", "symbol": "chess", "name": "Tranchess" }, - { "id": "tranquil-finance", "symbol": "tranq", "name": "Tranquil Finance" }, - { "id": "tranquility-city", "symbol": "lumen", "name": "Tranquility City" }, - { - "id": "tranquil-staked-one", - "symbol": "stone", - "name": "Tranquil Staked ONE" - }, - { "id": "transcodium", "symbol": "tns", "name": "Transcodium" }, - { "id": "transhuman-coin", "symbol": "thc", "name": "Transhuman Coin" }, - { "id": "transient", "symbol": "tsct", "name": "Transient" }, - { "id": "trapeza-protocol", "symbol": "fidl", "name": "Trapeza Protocol" }, - { "id": "tratok", "symbol": "trat", "name": "Tratok" }, - { "id": "trava-finance", "symbol": "trava", "name": "Trava Finance" }, - { "id": "travel-care-2", "symbol": "travel", "name": "Travel Care" }, - { "id": "travel-coin", "symbol": "tcoin", "name": "Travel Coin" }, - { "id": "travelnote", "symbol": "tvnt", "name": "TravelNote" }, - { "id": "traverse", "symbol": "VERSE", "name": "Traverse" }, - { "id": "travgopv", "symbol": "tpv", "name": "TravGoPV" }, - { "id": "traxia", "symbol": "tmt", "name": "Traxia" }, - { "id": "traxx", "symbol": "traxx", "name": "Traxx" }, - { "id": "trazable", "symbol": "trz", "name": "Trazable" }, - { "id": "tre", "symbol": "tre", "name": "Green Land" }, - { "id": "treasure", "symbol": "treasure", "name": "Treasure" }, - { "id": "treasure-sl", "symbol": "tsl", "name": "Treasure SL" }, - { - "id": "treasure-token-finance", - "symbol": "treasure", - "name": "Treasure Token Finance" - }, - { "id": "treasure-under-sea", "symbol": "tus", "name": "Treasure Under Sea" }, - { - "id": "treasury-bond-eth-tokenized-stock-defichain", - "symbol": "dTLT", - "name": "iShares 20+ Year Treasury Bond ETF Defichain" - }, - { "id": "treat", "symbol": "treat", "name": "Treat" }, - { "id": "treatdao", "symbol": "treat", "name": "TreatDAO [OLD]" }, - { "id": "treatdao-v2", "symbol": "treat", "name": "TreatDAO" }, - { "id": "treeb", "symbol": "treeb", "name": "Retreeb" }, - { "id": "treecle", "symbol": "trcl", "name": "Treecle" }, - { "id": "treep-token", "symbol": "treep", "name": "Treep Token" }, - { "id": "tremendous-coin", "symbol": "tmds", "name": "Tremendous Coin" }, - { "id": "trendering", "symbol": "trnd", "name": "Trendering" }, - { "id": "trendsy", "symbol": "trndz", "name": "Trendsy" }, - { "id": "tresor-finance", "symbol": "$tresor", "name": "Tresor Finance" }, - { "id": "trezarcoin", "symbol": "tzc", "name": "TrezarCoin" }, - { "id": "trgold", "symbol": "trgo", "name": "TrGold" }, - { "id": "triall", "symbol": "trl", "name": "Triall" }, - { "id": "trias-token", "symbol": "trias", "name": "Trias Token" }, - { "id": "tribal", "symbol": "tribl", "name": "Tribal" }, - { "id": "tribe-2", "symbol": "tribe", "name": "Tribe" }, - { "id": "tribeland", "symbol": "trbl", "name": "Tribeland" }, - { "id": "tribeone", "symbol": "haka", "name": "TribeOne" }, - { "id": "tribe-token", "symbol": "tribex", "name": "Tribe Token" }, - { "id": "tribe-wormhole", "symbol": "tribe", "name": "Tribe (Wormhole)" }, - { "id": "trick", "symbol": "trick", "name": "Trick" }, - { "id": "trickle", "symbol": "h2o", "name": "Trickle" }, - { "id": "triipmiles", "symbol": "tiim", "name": "TriipMiles" }, - { "id": "trillion", "symbol": "tt", "name": "Trillion" }, - { "id": "trillium", "symbol": "tt", "name": "Trillium" }, - { "id": "trinity", "symbol": "tty", "name": "Trinity" }, - { "id": "trinity-defi", "symbol": "trin", "name": "Trinity Defi" }, - { - "id": "trinity-network-credit", - "symbol": "tnc", - "name": "Trinity Network Credit" - }, - { "id": "tripcandy", "symbol": "candy", "name": "TripCandy" }, - { "id": "tripedia", "symbol": "trip", "name": "Tripedia" }, - { "id": "tripio", "symbol": "trio", "name": "Tripio" }, - { - "id": "trip-leverage-token", - "symbol": "tlt", - "name": "Trip Leverage Token" - }, - { "id": "tripolar", "symbol": "tripolar", "name": "Tripolar" }, - { "id": "trips-community", "symbol": "trips", "name": "Trips Community" }, - { "id": "trism", "symbol": "trism", "name": "Trism" }, - { "id": "trisolaris", "symbol": "tri", "name": "Trisolaris" }, - { "id": "triton", "symbol": "xeq", "name": "Equilibria" }, - { "id": "trittium", "symbol": "trtt", "name": "Trittium" }, - { "id": "triumphx", "symbol": "trix", "name": "TriumphX" }, - { "id": "trodl", "symbol": "tro", "name": "Trodl" }, - { "id": "trolite", "symbol": "trl", "name": "Trolite" }, - { "id": "trollbox", "symbol": "tox", "name": "trollbox" }, - { "id": "trollcoin", "symbol": "troll", "name": "Trollcoin" }, - { "id": "trolls-token", "symbol": "trolls", "name": "TROLLS Token" }, - { "id": "tron", "symbol": "trx", "name": "TRON" }, - { "id": "tronbetdice", "symbol": "dice", "name": "TRONbetDice" }, - { "id": "tronbetlive", "symbol": "live", "name": "TRONbetLive" }, - { "id": "tron-bsc", "symbol": "trx", "name": "TRON (BSC)" }, - { "id": "tronclassic", "symbol": "trxc", "name": "TronClassic" }, - { "id": "tron-connect", "symbol": "tcx", "name": "Tron Connect" }, - { - "id": "troneuroperewardcoin", - "symbol": "terc", - "name": "TronEuropeRewardCoin" - }, - { "id": "tronipay", "symbol": "trp", "name": "Tronipay" }, - { "id": "tronpad", "symbol": "tronpad", "name": "TRONPAD" }, - { - "id": "tropical-finance", - "symbol": "daiquiri", - "name": "Tropical Finance" - }, - { "id": "troy", "symbol": "troy", "name": "Troy" }, - { "id": "trubadger", "symbol": "trubgr", "name": "TruBadger" }, - { "id": "trueaud", "symbol": "taud", "name": "TrueAUD" }, - { "id": "truebit-protocol", "symbol": "tru", "name": "Truebit Protocol" }, - { "id": "trueburn", "symbol": "true", "name": "TrueBurn" }, - { "id": "true-chain", "symbol": "true", "name": "TrueChain" }, - { "id": "truedeck", "symbol": "tdp", "name": "TrueDeck" }, - { "id": "truefeedbackchain", "symbol": "tfbx", "name": "Truefeedback Token" }, - { "id": "truefi", "symbol": "tru", "name": "TrueFi" }, - { "id": "trueflip", "symbol": "tfl", "name": "TrueFlip" }, - { "id": "truegame", "symbol": "tgame", "name": "Truegame" }, - { "id": "truegbp", "symbol": "tgbp", "name": "TrueGBP" }, - { "id": "truehkd", "symbol": "thkd", "name": "TrueHKD" }, - { "id": "true-pnl", "symbol": "pnl", "name": "True PNL" }, - { "id": "true-usd", "symbol": "tusd", "name": "TrueUSD" }, - { "id": "trust", "symbol": "trust", "name": "TrustDAO" }, - { "id": "trustbase", "symbol": "tbe", "name": "TrustBase" }, - { "id": "trusted-node", "symbol": "tnode", "name": "Trusted Node" }, - { "id": "trustercoin", "symbol": "tsc", "name": "TrusterCoin" }, - { - "id": "trust-ether-reorigin", - "symbol": "teo", - "name": "Trust Ether ReOrigin" - }, - { - "id": "trustfi-network-token", - "symbol": "tfi", - "name": "TrustFi Network Token" - }, - { - "id": "trustkeys-network", - "symbol": "trustk", - "name": "TrustKeys Network" - }, - { "id": "trustnft", "symbol": "trustnft", "name": "TrustNFT" }, - { "id": "trustpad", "symbol": "tpad", "name": "TrustPad" }, - { "id": "trustpay", "symbol": "tph", "name": "Trustpay" }, - { "id": "trust-recruit", "symbol": "trt", "name": "Trust Recruit" }, - { "id": "trustrise", "symbol": "trise", "name": "TrustRise" }, - { "id": "trustswap", "symbol": "swap", "name": "Trustswap" }, - { "id": "trustusd", "symbol": "trusd", "name": "TrustUSD" }, - { "id": "trustverse", "symbol": "trv", "name": "TrustVerse" }, - { "id": "trust-wallet-token", "symbol": "twt", "name": "Trust Wallet Token" }, - { "id": "trustworks", "symbol": "trust", "name": "Trustworks" }, - { "id": "truth-technology", "symbol": "truth", "name": "Truth Technology" }, - { "id": "tryc", "symbol": "tryc", "name": "TRYC" }, - { "id": "try-finance", "symbol": "try", "name": "Try.Finance" }, - { "id": "tryhards", "symbol": "try", "name": "TryHards" }, - { "id": "tsar-network", "symbol": "TSAR", "name": "Tsar Network" }, - { "id": "tsuki-dao", "symbol": "tsuki", "name": "Tsuki DAO" }, - { "id": "tsuki-inu", "symbol": "tkinu", "name": "Tsuki Inu" }, - { - "id": "tsukiverse-galactic-adventures", - "symbol": "tsuga", - "name": "Tsukiverse:Galactic Adventures" - }, - { "id": "tsuzuki-inu", "symbol": "tzki", "name": "Tsuzuki Inu" }, - { - "id": "ttanslateme-network-token", - "symbol": "TMN", - "name": "TranslateMe Network Token" - }, - { "id": "ttcoin", "symbol": "TC", "name": "TTcoin" }, - { "id": "ttc-protocol", "symbol": "maro", "name": "Maro" }, - { "id": "ttoken", "symbol": "TTOKEN", "name": "TTOKEN" }, - { "id": "tt-token", "symbol": "ttt", "name": "TT Token" }, - { "id": "ttx-metaverse", "symbol": "xmeta", "name": "TTX Metaverse" }, - { "id": "tube2", "symbol": "tube2", "name": "TUBE2" }, - { "id": "tudabirds", "symbol": "burd", "name": "tudaBirds" }, - { "id": "tulip", "symbol": "tulip", "name": "Tulip" }, - { "id": "tundra-token", "symbol": "tundra", "name": "Tundra Token" }, - { "id": "tune", "symbol": "tun", "name": "TUNE" }, - { "id": "tune-fm", "symbol": "jam", "name": "Tune.Fm" }, - { "id": "tune-token", "symbol": "tune", "name": "TUNE TOKEN" }, - { "id": "turex", "symbol": "tur", "name": "Turex" }, - { - "id": "turkiye-basketbol-federasyonu-token", - "symbol": "tbft", - "name": "Turkiye Basketbol Federasyonu Token" - }, - { "id": "turncoin", "symbol": "turncoin", "name": "TurnCoin" }, - { "id": "turnt-up-tikis", "symbol": "tut", "name": "Turnt Up Tikis" }, - { "id": "turtlecoin", "symbol": "trtl", "name": "TurtleCoin" }, - { "id": "turtle-racing", "symbol": "turt", "name": "Turtle Racing" }, - { "id": "turtles-token", "symbol": "TRTLS", "name": "Turtles Token" }, - { "id": "tusk-token", "symbol": "tusk", "name": "Mammoth.Bet" }, - { "id": "tutellus", "symbol": "tut", "name": "Tutellus" }, - { "id": "tutors-diary", "symbol": "tuda", "name": "Tutor's Diary" }, - { "id": "tutti-frutti-finance", "symbol": "tff", "name": "Tutti Frutti" }, - { "id": "tuxcoin", "symbol": "tux", "name": "Tuxcoin" }, - { "id": "tuzlaspor", "symbol": "tuzla", "name": "Tuzlaspor Token" }, - { "id": "tvt", "symbol": "tvt", "name": "TVT" }, - { "id": "twelve-legions", "symbol": "ctl", "name": "Twelve Legions" }, - { "id": "twinci", "symbol": "twin", "name": "Twinci" }, - { "id": "twin-finance", "symbol": "twin", "name": "Twin Finance" }, - { "id": "twinu", "symbol": "twinu", "name": "TWINU" }, - { - "id": "twirl-governance-token", - "symbol": "tgt", - "name": "Twirl Governance Token" - }, - { "id": "twist", "symbol": "TWIST", "name": "TWIST" }, - { - "id": "twitter-tokenized-stock-zipmex", - "symbol": "twtr", - "name": "Twitter Tokenized Stock Zipmex" - }, - { - "id": "two-monkey-juice-bar", - "symbol": "$tmon", - "name": "Two Monkey Juice Bar" - }, - { "id": "txa", "symbol": "txa", "name": "TXA" }, - { "id": "txbit", "symbol": "txbit", "name": "Txbit" }, - { "id": "tycheloto", "symbol": "tyche", "name": "TycheLoto" }, - { "id": "tycoon", "symbol": "tyc", "name": "Tycoon" }, - { "id": "tycoon-global", "symbol": "tct", "name": "Tycoon Global" }, - { "id": "typerium", "symbol": "type", "name": "Typerium" }, - { "id": "typhoon-cash", "symbol": "phoon", "name": "Typhoon Cash" }, - { "id": "typhoon-network", "symbol": "typh", "name": "Typhoon Network" }, - { "id": "tyv", "symbol": "tyv", "name": "TYV" }, - { "id": "tzbtc", "symbol": "tzbtc", "name": "tzBTC" }, - { "id": "uangmarket", "symbol": "uang", "name": "UangMarket" }, - { "id": "ubeswap", "symbol": "ube", "name": "Ubeswap" }, - { "id": "ubex", "symbol": "ubex", "name": "Ubex" }, - { "id": "ubg-token", "symbol": "ubg", "name": "UBG Token" }, - { "id": "ubiner", "symbol": "ubin", "name": "Ubiner" }, - { "id": "ubiq", "symbol": "ubq", "name": "Ubiq" }, - { "id": "ubiquity", "symbol": "ubq", "name": "Ubiquity" }, - { - "id": "ubiquity-algorithmic-dollar", - "symbol": "uad", - "name": "Ubiquity Algorithmic Dollar" - }, - { "id": "ubix-network", "symbol": "ubx", "name": "UBIX Network" }, - { "id": "ubu", "symbol": "ubu", "name": "UBU" }, - { "id": "ubu-finance", "symbol": "ubu", "name": "UBU Finance" }, - { "id": "ubxs-token", "symbol": "ubxs", "name": "UBXS Token" }, - { "id": "uca", "symbol": "uca", "name": "UCA Coin" }, - { "id": "ucash", "symbol": "ucash", "name": "U.CASH" }, - { "id": "uchain", "symbol": "ucn", "name": "UChain" }, - { "id": "ucoin", "symbol": "u", "name": "Ucoin" }, - { "id": "ucrowdme", "symbol": "ucm", "name": "UCROWDME" }, - { "id": "ucx", "symbol": "ucx", "name": "UCX" }, - { "id": "ufc-fan-token", "symbol": "ufc", "name": "UFC Fan Token" }, - { "id": "ufo", "symbol": "ufo", "name": "UFO" }, - { "id": "ufocoin", "symbol": "ufo", "name": "Uniform Fiscal Object" }, - { "id": "ufo-gaming", "symbol": "ufo", "name": "UFO Gaming" }, - { - "id": "ufo-gaming-wormhole", - "symbol": "ufo", - "name": "UFO Gaming (Wormhole)" - }, - { "id": "uhive", "symbol": "hve2", "name": "Uhive" }, - { "id": "ukiss", "symbol": "kiss", "name": "UKISS" }, - { - "id": "ukrainedao-flag-nft", - "symbol": "love", - "name": "UkraineDAO Flag NFT" - }, - { "id": "ulanco", "symbol": "uac", "name": "Ulanco" }, - { "id": "uland", "symbol": "uland", "name": "ULAND" }, - { "id": "ulgen-hash-power", "symbol": "uhp", "name": "Ulgen Hash Power" }, - { "id": "ulord", "symbol": "ut", "name": "Ulord" }, - { "id": "ulti-arena", "symbol": "ulti", "name": "Ulti Arena" }, - { "id": "ultiledger", "symbol": "ult", "name": "Ultiledger" }, - { "id": "ultimate-nft", "symbol": "unft", "name": "Ultimate Nft" }, - { "id": "ultimogg", "symbol": "ultgg", "name": "UltimoGG" }, - { "id": "ultra", "symbol": "uos", "name": "Ultra" }, - { "id": "ultrachad", "symbol": "uchad", "name": "UltraChad" }, - { "id": "ultra-clear", "symbol": "ucr", "name": "Ultra Clear" }, - { "id": "ultragate", "symbol": "ulg", "name": "Ultragate" }, - { "id": "ultrain", "symbol": "ugas", "name": "Ultrain" }, - { "id": "ultralpha", "symbol": "uat", "name": "UltrAlpha" }, - { "id": "ultra-nft", "symbol": "unft", "name": "Ultra NFT" }, - { "id": "ultrasafe", "symbol": "ultra", "name": "UltraSafe" }, - { "id": "uma", "symbol": "uma", "name": "UMA" }, - { "id": "umami-finance", "symbol": "umami", "name": "Umami Finance" }, - { "id": "umbra-network", "symbol": "umbr", "name": "Umbria Network" }, - { "id": "umbrellacoin", "symbol": "umc", "name": "Umbrella Coin" }, - { "id": "umbrella-network", "symbol": "umb", "name": "Umbrella Network" }, - { "id": "umee", "symbol": "umee", "name": "Umee" }, - { "id": "umetaworld", "symbol": "umw", "name": "UMetaWorld" }, - { "id": "ume-token", "symbol": "ume", "name": "UME Token" }, - { "id": "umi", "symbol": "umi", "name": "UMI" }, - { "id": "umi-digital", "symbol": "umi", "name": "Umi Digital" }, - { "id": "unagii-dai", "symbol": "udai", "name": "Unagii Dai" }, - { "id": "unagii-eth", "symbol": "ueth", "name": "Unagii ETH" }, - { "id": "unagii-tether-usd", "symbol": "uusdt", "name": "Unagii Tether USD" }, - { "id": "unagii-usd-coin", "symbol": "uusdc", "name": "Unagii USD Coin" }, - { - "id": "unagii-wrapped-bitcoin", - "symbol": "uwbtc", - "name": "Unagii Wrapped Bitcoin" - }, - { "id": "unbanked", "symbol": "unbnk", "name": "Unbanked" }, - { "id": "unbound-dollar", "symbol": "und", "name": "Unbound Dollar" }, - { "id": "unbound-finance", "symbol": "unb", "name": "Unbound Finance" }, - { "id": "uncl", "symbol": "uncl", "name": "UNCL" }, - { "id": "uncle-doge", "symbol": "udoge", "name": "Uncle Doge" }, - { "id": "unclemine", "symbol": "um", "name": "UncleMine" }, - { - "id": "uncle-scrooge-finance", - "symbol": "crooge", - "name": "Uncle Scrooge Finance" - }, - { "id": "undead-finance", "symbol": "undead", "name": "Undead Finance" }, - { - "id": "underground-warriors", - "symbol": "wp", - "name": "Underground Warriors" - }, - { "id": "underminegold", "symbol": "umg", "name": "UnderMineGold" }, - { "id": "u-network", "symbol": "uuu", "name": "U Network" }, - { "id": "unfederalreserve", "symbol": "ersdl", "name": "unFederalReserve" }, - { "id": "uniarts", "symbol": "UART", "name": "UniArts" }, - { "id": "unibright", "symbol": "ubt", "name": "Unibright" }, - { "id": "unicake", "symbol": "uct", "name": "UniCAKE" }, - { "id": "unicandy", "symbol": "ucd", "name": "UniCandy" }, - { "id": "unicap-finance", "symbol": "ucap", "name": "Unicap.Finance" }, - { "id": "unicat-token", "symbol": "unicat", "name": "Unicat Token" }, - { "id": "unicly", "symbol": "unic", "name": "Unicly" }, - { - "id": "unicly-aavegotchi-astronauts-collection", - "symbol": "ugotchi", - "name": "Unicly Aavegotchi Astronauts Collection" - }, - { - "id": "unicly-bored-ape-yacht-club-collection", - "symbol": "uAPE", - "name": "Unicly Bored Ape Yacht Club Collection" - }, - { - "id": "unicly-chris-mccann-collection", - "symbol": "ucm", - "name": "Unicly Chris McCann Collection" - }, - { - "id": "unicly-cryptopunks-collection", - "symbol": "upunk", - "name": "Unicly CryptoPunks Collection" - }, - { - "id": "unicly-doki-doki-collection", - "symbol": "udoki", - "name": "Unicly Doki Doki Collection" - }, - { - "id": "unicly-fewocious-collection", - "symbol": "ufewo", - "name": "Unicly Fewocious Collection" - }, - { - "id": "unicly-genesis-collection", - "symbol": "uunicly", - "name": "Unicly Genesis Collection" - }, - { - "id": "unicly-genesis-mooncats-collection", - "symbol": "ugmc", - "name": "Unicly Genesis MoonCats Collection" - }, - { - "id": "unicly-mooncats-collection", - "symbol": "umoon", - "name": "Unicly MoonCats Collection" - }, - { - "id": "unicly-mystic-axies-collection", - "symbol": "uaxie", - "name": "Unicly Mystic Axies Collection" - }, - { - "id": "unicly-waifu-collection", - "symbol": "uwaifu", - "name": "Unicly Waifu Collection" - }, - { "id": "unicorn-cake", "symbol": "unic", "name": "Unicorn Cake" }, - { "id": "unicorn-milk", "symbol": "unim", "name": "Unicorn Milk" }, - { "id": "unicorn-token", "symbol": "uni", "name": "UNICORN Token" }, - { "id": "unicrypt-2", "symbol": "uncx", "name": "UniCrypt" }, - { "id": "unidex", "symbol": "unidx", "name": "UniDex" }, - { "id": "unido-ep", "symbol": "udo", "name": "Unido" }, - { "id": "unifarm", "symbol": "ufarm", "name": "UniFarm" }, - { "id": "unifees", "symbol": "fees", "name": "Unifees" }, - { "id": "unifi", "symbol": "unifi", "name": "Covenants" }, - { "id": "unification", "symbol": "fund", "name": "Unification" }, - { "id": "unifi-defi", "symbol": "unifi", "name": "UNIFI DeFi" }, - { "id": "unifi-protocol", "symbol": "up", "name": "UniFi Protocol" }, - { - "id": "unifi-protocol-dao", - "symbol": "unfi", - "name": "Unifi Protocol DAO" - }, - { "id": "unifty", "symbol": "nif", "name": "Unifty" }, - { "id": "unifund", "symbol": "ifund", "name": "Unifund" }, - { "id": "unikoin-gold", "symbol": "ukg", "name": "Unikoin Gold" }, - { "id": "unilab-network", "symbol": "ulab", "name": "Unilab" }, - { "id": "unilayer", "symbol": "layer", "name": "UniLayer" }, - { "id": "unilock-network-2", "symbol": "unl", "name": "Unilock.Network" }, - { "id": "unimaxpad", "symbol": "umax", "name": "Unimaxpad" }, - { "id": "unimex-network", "symbol": "umx", "name": "UniMex Network" }, - { "id": "unimoon", "symbol": "unimoon", "name": "Unimoon" }, - { "id": "union-fair-coin", "symbol": "ufc", "name": "Union Fair Coin" }, - { - "id": "union-protocol-governance-token", - "symbol": "unn", - "name": "UNION Protocol Governance Token" - }, - { "id": "unipilot", "symbol": "pilot", "name": "Unipilot" }, - { "id": "unipower", "symbol": "power", "name": "UniPower" }, - { "id": "uniqly", "symbol": "uniq", "name": "Uniqly" }, - { "id": "unique-fans", "symbol": "fans", "name": "Unique Fans" }, - { "id": "uniquemeta", "symbol": "unqm", "name": "UniqueMeta" }, - { "id": "unique-one", "symbol": "rare", "name": "Unique One" }, - { "id": "uniqueone-photo", "symbol": "foto", "name": "UniqueOne Photo" }, - { - "id": "unique-utility-token", - "symbol": "UNQT", - "name": "Unique Utility Token" - }, - { "id": "unirealchain", "symbol": "unr", "name": "Unirealchain" }, - { "id": "unisocks", "symbol": "socks", "name": "Unisocks" }, - { "id": "unistake", "symbol": "unistake", "name": "Unistake" }, - { "id": "uniswap", "symbol": "uni", "name": "Uniswap" }, - { - "id": "uniswap-state-dollar", - "symbol": "usd", - "name": "unified Stable Dollar" - }, - { "id": "uniswap-wormhole", "symbol": "uni", "name": "Uniswap (Wormhole)" }, - { "id": "unite", "symbol": "unite", "name": "Unite" }, - { "id": "unitech", "symbol": "utc", "name": "Unitech" }, - { "id": "united-bitcoin", "symbol": "ubtc", "name": "United Bitcoin" }, - { "id": "unitedcoins", "symbol": "units", "name": "UnitedCoins" }, - { "id": "unitedcrowd", "symbol": "uct", "name": "UnitedCrowd" }, - { "id": "united-dao", "symbol": "utd", "name": "United DAO" }, - { - "id": "united-doge-finance", - "symbol": "udog", - "name": "United Doge Finance" - }, - { "id": "united-emirate", "symbol": "uec", "name": "United Emirate" }, - { - "id": "united-emirate-decentralized-coin", - "symbol": "uedc", - "name": "United Emirate Decentralized Coin" - }, - { - "id": "united-farmers-finance", - "symbol": "uff", - "name": "United Farmers Finance" - }, - { "id": "united-token", "symbol": "uted", "name": "United" }, - { - "id": "united-traders-token", - "symbol": "utt", - "name": "United Traders Token" - }, - { "id": "unit-protocol", "symbol": "col", "name": "Unit Protocol" }, - { "id": "unit-protocol-duck", "symbol": "duck", "name": "Unit Protocol New" }, - { "id": "unitrade", "symbol": "trade", "name": "Unitrade" }, - { "id": "unitus", "symbol": "uis", "name": "Unitus" }, - { "id": "unitycom", "symbol": "unitycom", "name": "UnityCom" }, - { "id": "unity-network", "symbol": "unt", "name": "Unity Network" }, - { "id": "unity-protocol", "symbol": "unity", "name": "Unity Protocol" }, - { "id": "unityventures", "symbol": "uv", "name": "UnityVentures" }, - { "id": "unium", "symbol": "unm", "name": "UNIUM" }, - { - "id": "universal-basic-income", - "symbol": "ubi", - "name": "Universal Basic Income" - }, - { "id": "universal-coin", "symbol": "ucoin", "name": "Universal Coin" }, - { - "id": "universal-currency", - "symbol": "unit", - "name": "Universal Currency" - }, - { - "id": "universal-floki-coin", - "symbol": "ufloki", - "name": "Universal Floki Coin" - }, - { "id": "universal-gold", "symbol": "upxau", "name": "Universal Gold" }, - { - "id": "universal-liquidity-union", - "symbol": "ulu", - "name": "Universal Liquidity Union" - }, - { "id": "universal-pickle", "symbol": "$upl", "name": "Universal Pickle" }, - { - "id": "universal-protocol-token", - "symbol": "upt", - "name": "Universal Protocol Token" - }, - { "id": "universe-2", "symbol": "univ", "name": "Universe" }, - { "id": "universe-coin", "symbol": "unis", "name": "Universe Coin" }, - { - "id": "universe-crystal-gene", - "symbol": "ucg", - "name": "Universe Crystal Gene" - }, - { - "id": "universe-finance-token", - "symbol": "unt", - "name": "Universe Finance Token" - }, - { "id": "universe-island", "symbol": "uim", "name": "Universe Island" }, - { "id": "universe-token", "symbol": "uni", "name": "UNIVERSE Project" }, - { "id": "universe-token-2", "symbol": "universe", "name": "Universe Token" }, - { "id": "universe-xyz", "symbol": "xyz", "name": "Universe.XYZ" }, - { - "id": "universidad-de-chile-fan-token", - "symbol": "UCH", - "name": "Universidad de Chile Fan Token" - }, - { "id": "unive-x", "symbol": "UNVX", "name": "UNIVE X" }, - { "id": "uniwhales", "symbol": "uwl", "name": "UniWhales" }, - { "id": "uniworld", "symbol": "unw", "name": "UniWorld" }, - { "id": "unix", "symbol": "unix", "name": "UniX" }, - { "id": "unizen", "symbol": "zcx", "name": "Unizen" }, - { - "id": "unknown-fair-object", - "symbol": "ufo", - "name": "Unknown Fair Object" - }, - { "id": "unlend-finance", "symbol": "uft", "name": "UniLend Finance" }, - { "id": "unlimitedip", "symbol": "uip", "name": "UnlimitedIP" }, - { "id": "unlock", "symbol": "unlock", "name": "UNLOCK" }, - { "id": "unlock-protocol", "symbol": "udt", "name": "Unlock Protocol" }, - { "id": "unmarshal", "symbol": "marsh", "name": "Unmarshal" }, - { "id": "unobtanium", "symbol": "uno", "name": "Unobtanium" }, - { "id": "unobtanium-tezos", "symbol": "uno", "name": "Unobtanium Tezos" }, - { "id": "uno-re", "symbol": "uno", "name": "Uno Re" }, - { "id": "unq", "symbol": "unq", "name": "Unique Venture clubs" }, - { "id": "unreal-finance", "symbol": "ugt", "name": "Unreal Finance" }, - { "id": "unslashed-finance", "symbol": "usf", "name": "Unslashed Finance" }, - { "id": "unus-dao", "symbol": "udo", "name": "Unus Dao" }, - { "id": "unvest", "symbol": "unv", "name": "Unvest" }, - { "id": "upbnb", "symbol": "upbnb", "name": "upBNB" }, - { "id": "upbots", "symbol": "ubxt", "name": "UpBots" }, - { "id": "upbots-wormhole", "symbol": "ubxt", "name": "UpBots (Wormhole)" }, - { "id": "upcoin", "symbol": "upcoin", "name": "Upcoin" }, - { "id": "updefi", "symbol": "up", "name": "UpDeFi" }, - { "id": "updog", "symbol": "updog", "name": "UpDog" }, - { "id": "upfi-network", "symbol": "ups", "name": "UPFI Network" }, - { "id": "upfinity", "symbol": "upf", "name": "UpFinity" }, - { "id": "upfire", "symbol": "upr", "name": "Upfire" }, - { "id": "upfiring", "symbol": "ufr", "name": "Upfiring" }, - { "id": "upfund", "symbol": "upt", "name": "UPFUND" }, - { "id": "uplexa", "symbol": "upx", "name": "uPlexa" }, - { "id": "uplift", "symbol": "lift", "name": "Uplift" }, - { "id": "uplink", "symbol": "uplink", "name": "UpLink" }, - { "id": "uponly-token", "symbol": "upo", "name": "UpOnly Token" }, - { "id": "upper-dollar", "symbol": "usdu", "name": "Upper Dollar" }, - { "id": "upper-euro", "symbol": "euru", "name": "Upper Euro" }, - { "id": "upper-pound", "symbol": "gbpu", "name": "Upper Pound" }, - { "id": "upper-swiss-franc", "symbol": "chfu", "name": "Upper Swiss Franc" }, - { "id": "uppsme", "symbol": "upps", "name": "UppsMe" }, - { "id": "upshib", "symbol": "upshib", "name": "upShib" }, - { "id": "upstabletoken", "symbol": "ustx", "name": "UpStableToken" }, - { "id": "uptoken", "symbol": "up", "name": "UpToken" }, - { "id": "up-token", "symbol": "up", "name": "UP Token" }, - { "id": "uptrend", "symbol": "trend", "name": "UpTrend" }, - { "id": "uquid-coin", "symbol": "uqc", "name": "Uquid Coin" }, - { "id": "uraniumx", "symbol": "urx", "name": "UraniumX" }, - { "id": "uranus", "symbol": "urac", "name": "Uranus" }, - { "id": "ureeqa", "symbol": "urqa", "name": "UREEQA" }, - { "id": "urgaming", "symbol": "urg", "name": "UrGaming" }, - { "id": "urg-university", "symbol": "URG-U", "name": "URG University" }, - { "id": "urubit", "symbol": "urub", "name": "Urubit" }, - { "id": "urus-token", "symbol": "urus", "name": "Aurox Token" }, - { "id": "usd-balance", "symbol": "usdb", "name": "USD Balance" }, - { "id": "usd-coin", "symbol": "usdc", "name": "USD Coin" }, - { - "id": "usd-coin-avalanche-bridged-usdc-e", - "symbol": "usdc", - "name": "USD Coin Avalanche Bridged (USDC.e)" - }, - { "id": "usd-coin-celer", "symbol": "ceusdc", "name": "USD Coin - Celer" }, - { "id": "usd-coin-nomad", "symbol": "nomadusdc", "name": "USD Coin - Nomad" }, - { - "id": "usd-coin-pos-wormhole", - "symbol": "usdcpo", - "name": "USD Coin (PoS) (Wormhole)" - }, - { - "id": "usd-coin-wormhole", - "symbol": "usdcso", - "name": "USD Coin (Wormhole)" - }, - { - "id": "usd-coin-wormhole-from-avalanche", - "symbol": "usdcav", - "name": "USD Coin (Wormhole from Avalanche)" - }, - { - "id": "usd-coin-wormhole-from-bsc", - "symbol": "usdcbs", - "name": "USD Coin (Wormhole from BSC)" - }, - { - "id": "usd-coin-wormhole-from-ethereum", - "symbol": "usdcet", - "name": "USD Coin (Wormhole from Ethereum)" - }, - { "id": "usd-gambit", "symbol": "usdg", "name": "USD Gambit" }, - { "id": "usdh", "symbol": "usdh", "name": "USDH" }, - { "id": "usdk", "symbol": "usdk", "name": "USDK" }, - { "id": "usdk-wormhole", "symbol": "usdk", "name": "USDK (Wormhole)" }, - { "id": "usd-mars", "symbol": "usdm", "name": "USD Mars" }, - { "id": "usd-open-dollar", "symbol": "usdo", "name": "USD Open Dollar" }, - { "id": "usdp", "symbol": "usdp", "name": "USDP Stablecoin" }, - { "id": "usdq", "symbol": "usdq", "name": "USDQ" }, - { "id": "usd-sports", "symbol": "usdsp", "name": "USD Sports" }, - { "id": "usdtez", "symbol": "usdtz", "name": "USDtez" }, - { "id": "usdv", "symbol": "usdv", "name": "USD Velero Stablecoin" }, - { "id": "usdx", "symbol": "usdx", "name": "USDX" }, - { "id": "usdx-stablecoin", "symbol": "usdx", "name": "USDx Stablecoin" }, - { "id": "usd-zee", "symbol": "usdz", "name": "USD ZEE" }, - { "id": "usechain", "symbol": "use", "name": "Usechain" }, - { "id": "useless", "symbol": "useless", "name": "Useless [OLD]" }, - { "id": "useless-2", "symbol": "useless", "name": "Useless" }, - { "id": "usgold", "symbol": "usg", "name": "USGold" }, - { "id": "ushare", "symbol": "ushare", "name": "USHARE" }, - { "id": "usn", "symbol": "usn", "name": "USN" }, - { "id": "utip", "symbol": "utip", "name": "uTip" }, - { "id": "utopia", "symbol": "crp", "name": "Crypton" }, - { "id": "utopia-2", "symbol": "topia", "name": "Utopia" }, - { - "id": "utopia-genesis-foundation", - "symbol": "uop", - "name": "Utopia Genesis Foundation" - }, - { "id": "utopia-token", "symbol": "uto", "name": "Utopia Token" }, - { "id": "utrust", "symbol": "utk", "name": "Utrust" }, - { "id": "utu-coin", "symbol": "utu", "name": "UTU Coin" }, - { "id": "uwu-vault-nftx", "symbol": "uwu", "name": "UWU Vault (NFTX)" }, - { "id": "uxd-protocol-token", "symbol": "uxp", "name": "UXD Protocol Token" }, - { "id": "uxd-stablecoin", "symbol": "uxd", "name": "UXD Stablecoin" }, - { "id": "uzumaki-inu", "symbol": "uzumaki", "name": "Uzumaki Inu" }, - { "id": "uzyth", "symbol": "zyth", "name": "Uzyth" }, - { "id": "v3s-share", "symbol": "vshare", "name": "V3S Share" }, - { "id": "vabble", "symbol": "vab", "name": "Vabble" }, - { "id": "vacay", "symbol": "vacay", "name": "Vacay" }, - { "id": "vader-protocol", "symbol": "vader", "name": "Vader Protocol" }, - { "id": "vagabond", "symbol": "vgo", "name": "Vagabond" }, - { "id": "vai", "symbol": "vai", "name": "Vai" }, - { "id": "vaiot", "symbol": "vai", "name": "Vaiot" }, - { "id": "valas-finance", "symbol": "valas", "name": "Valas Finance" }, - { - "id": "valencia-cf-fan-token", - "symbol": "vcf", - "name": "Valencia CF Fan Token" - }, - { "id": "valentine-floki", "symbol": "flov", "name": "Valentine Floki" }, - { "id": "valid", "symbol": "vld", "name": "Vetri" }, - { "id": "valkyrie-network", "symbol": "val", "name": "Valkyrie Network" }, - { "id": "valkyrie-protocol", "symbol": "vkr", "name": "Valkyrie Protocol" }, - { "id": "valkyrio-token", "symbol": "valk", "name": "Valkyrio Token" }, - { "id": "valleyswap", "symbol": "vs", "name": "ValleySwap" }, - { "id": "valobit", "symbol": "vbit", "name": "VALOBIT" }, - { "id": "valor", "symbol": "v$", "name": "Valor" }, - { "id": "valorfoundation", "symbol": "valor", "name": "ValorFoundation" }, - { "id": "value-finance", "symbol": "vft", "name": "Value Finance" }, - { "id": "value-liquidity", "symbol": "value", "name": "Value DeFi" }, - { - "id": "value-network-token", - "symbol": "vntw", - "name": "Value Network Token" - }, - { "id": "valuto", "symbol": "vlu", "name": "Valuto" }, - { - "id": "vampirestakecapital", - "symbol": "vsc", - "name": "VampireStakeCapital" - }, - { "id": "vancat", "symbol": "vancat", "name": "Vancat [OLD]" }, - { "id": "vancat-2", "symbol": "vancat", "name": "Vancat" }, - { "id": "vanci-finance", "symbol": "vancii", "name": "Vanci Finance" }, - { - "id": "vanguard-real-estate-tokenized-stock-defichain", - "symbol": "dVNQ", - "name": "Vanguard Real Estate Tokenized Stock Defichain" - }, - { - "id": "vanguard-sp-500-etf-tokenized-stock-defichain", - "symbol": "dVOO", - "name": "Vanguard S\u0026P 500 ETF Tokenized Stock Defichain" - }, - { "id": "vanilla", "symbol": "vnl", "name": "Vanilla" }, - { "id": "vanilla-network", "symbol": "vnla", "name": "Vanilla Network" }, - { "id": "vanity", "symbol": "vny", "name": "Vanity" }, - { "id": "vankia-chain", "symbol": "vkt", "name": "Vankia Chain" }, - { "id": "vanspor-token", "symbol": "van", "name": "Vanspor Token" }, - { "id": "vantaur", "symbol": "vtar", "name": "Vantaur" }, - { "id": "vanywhere", "symbol": "vany", "name": "Vanywhere" }, - { "id": "vaperscoin", "symbol": "vprc", "name": "VapersCoin" }, - { "id": "vapornodes", "symbol": "vpnd", "name": "VaporNodes" }, - { "id": "varen", "symbol": "vrn", "name": "Varen" }, - { - "id": "vari-stable-capital", - "symbol": "vsc", - "name": "Vari-Stable Capital" - }, - { "id": "vault", "symbol": "vault", "name": "VAULT" }, - { "id": "vaultdefi", "symbol": "vault", "name": "Vault-X" }, - { "id": "vault-hill-city", "symbol": "vhc", "name": "Vault Hill City" }, - { "id": "vault-s", "symbol": "vault-s", "name": "Vault-S" }, - { "id": "vaulty-token", "symbol": "vlty", "name": "Vaulty Token" }, - { "id": "vbswap", "symbol": "vbswap", "name": "vBSWAP" }, - { "id": "vbzrx", "symbol": "vbzrx", "name": "bZx Vesting Token" }, - { "id": "vcash", "symbol": "xvc", "name": "Vcash" }, - { "id": "vcgamers", "symbol": "vcg", "name": "VCGamers" }, - { "id": "vdv-token", "symbol": "vdv", "name": "VDV Token" }, - { "id": "vechain", "symbol": "vet", "name": "VeChain" }, - { "id": "veco", "symbol": "veco", "name": "Veco" }, - { - "id": "vecrv-dao-yvault", - "symbol": "yve-crvdao", - "name": "veCRV-DAO yVault" - }, - { "id": "vector-finance", "symbol": "vtx", "name": "Vector Finance" }, - { "id": "vectorium", "symbol": "vect", "name": "Vectorium" }, - { "id": "vectorspace", "symbol": "vxv", "name": "Vectorspace AI" }, - { "id": "vedao", "symbol": "weve", "name": "veDAO" }, - { "id": "veed", "symbol": "veed", "name": "VEED" }, - { "id": "vee-finance", "symbol": "vee", "name": "Vee Finance" }, - { "id": "veelancing", "symbol": "slbz", "name": "Veelancing" }, - { - "id": "vegannation-greencoin", - "symbol": "grnc", - "name": "VeganNation GreenCoin" - }, - { "id": "vega-protocol", "symbol": "vega", "name": "Vega Protocol" }, - { "id": "vegawallet-token", "symbol": "vgw", "name": "VegaWallet Token" }, - { "id": "vegeta-inu", "symbol": "vegi", "name": "Vegeta Inu" }, - { "id": "veggiecoin", "symbol": "vegi", "name": "VeggieCoin" }, - { "id": "veil", "symbol": "veil", "name": "VEIL" }, - { "id": "vektor", "symbol": "vektor", "name": "VEKTOR" }, - { "id": "velas", "symbol": "vlx", "name": "Velas" }, - { "id": "velaspad", "symbol": "vlxpad", "name": "VelasPad" }, - { "id": "veldorabsc", "symbol": "vdora", "name": "VeldoraBSC" }, - { "id": "velerodao", "symbol": "vdgt", "name": "VeleroDAO" }, - { "id": "veles", "symbol": "vls", "name": "Veles" }, - { "id": "velhalla", "symbol": "scar", "name": "Velhalla" }, - { "id": "velo", "symbol": "velo", "name": "Velo" }, - { "id": "velorex", "symbol": "vex", "name": "Velorex" }, - { "id": "vempire-ddao", "symbol": "vemp", "name": "vEmpire DDAO" }, - { "id": "vendit", "symbol": "vndt", "name": "Vendit" }, - { "id": "venice", "symbol": "veni", "name": "Venice" }, - { "id": "venjocoin", "symbol": "vjc", "name": "VENJOCOIN" }, - { "id": "venox", "symbol": "vnx", "name": "Venox" }, - { "id": "vent-finance", "symbol": "vent", "name": "Vent Finance" }, - { "id": "vention", "symbol": "vention", "name": "Vention" }, - { "id": "venus", "symbol": "xvs", "name": "Venus" }, - { "id": "venus-bch", "symbol": "vbch", "name": "Venus BCH" }, - { "id": "venus-beth", "symbol": "vbeth", "name": "Venus BETH" }, - { "id": "venus-btc", "symbol": "vbtc", "name": "Venus BTC" }, - { "id": "venus-busd", "symbol": "vbusd", "name": "Venus BUSD" }, - { "id": "venus-dai", "symbol": "vdai", "name": "Venus DAI" }, - { "id": "venus-doge", "symbol": "vdoge", "name": "Venus DOGE" }, - { "id": "venus-dot", "symbol": "vdot", "name": "Venus DOT" }, - { "id": "venus-eth", "symbol": "veth", "name": "Venus ETH" }, - { "id": "venus-fil", "symbol": "vfil", "name": "Venus FIL" }, - { "id": "venus-link", "symbol": "vlink", "name": "Venus LINK" }, - { "id": "venus-ltc", "symbol": "vltc", "name": "Venus LTC" }, - { "id": "venus-reward-token", "symbol": "vrt", "name": "Venus Reward Token" }, - { "id": "venus-sxp", "symbol": "vsxp", "name": "Venus SXP" }, - { "id": "venus-usdc", "symbol": "vusdc", "name": "Venus USDC" }, - { "id": "venus-usdt", "symbol": "vusdt", "name": "Venus USDT" }, - { "id": "venus-xrp", "symbol": "vxrp", "name": "Venus XRP" }, - { "id": "venus-xvs", "symbol": "vxvs", "name": "Venus XVS" }, - { "id": "vera", "symbol": "vera", "name": "Vera" }, - { "id": "vera-cruz-coin", "symbol": "vcco", "name": "Vera Cruz Coin" }, - { "id": "vera-exchange", "symbol": "vera", "name": "VERA Exchange" }, - { "id": "veraone", "symbol": "vro", "name": "VeraOne" }, - { "id": "verasity", "symbol": "vra", "name": "Verasity" }, - { "id": "veraswap", "symbol": "vrap", "name": "VeraSwap" }, - { "id": "verge", "symbol": "xvg", "name": "Verge" }, - { "id": "veriblock", "symbol": "vbk", "name": "VeriBlock" }, - { "id": "vericoin", "symbol": "vrc", "name": "VeriCoin" }, - { "id": "veridocglobal", "symbol": "vdg", "name": "VeriDocGlobal" }, - { "id": "verify", "symbol": "cred", "name": "Verify" }, - { "id": "verify-token", "symbol": "vrfy", "name": "Verify Token" }, - { "id": "verisafe", "symbol": "vsf", "name": "VeriSafe" }, - { "id": "veritaseum", "symbol": "veri", "name": "Veritaseum" }, - { "id": "vero-farm", "symbol": "vero", "name": "Vero Farm" }, - { "id": "verox", "symbol": "vrx", "name": "Verox" }, - { "id": "versa", "symbol": "versa", "name": "Versa" }, - { "id": "verse", "symbol": "verse", "name": "Verse" }, - { "id": "versewar", "symbol": "verse", "name": "VerseWar" }, - { "id": "version", "symbol": "v", "name": "Version" }, - { "id": "verso", "symbol": "vso", "name": "Verso" }, - { "id": "versoview", "symbol": "vvt", "name": "VersoView" }, - { "id": "versus-farm", "symbol": "versus", "name": "Versus Farm" }, - { "id": "vertcoin", "symbol": "vtc", "name": "Vertcoin" }, - { "id": "verus-coin", "symbol": "vrsc", "name": "Verus Coin" }, - { "id": "verve", "symbol": "verve", "name": "Verve" }, - { "id": "vesper-finance", "symbol": "vsp", "name": "Vesper Finance" }, - { "id": "vesper-vdollar", "symbol": "vusd", "name": "Vesper V-Dollar" }, - { "id": "vesq", "symbol": "vsq", "name": "VESQ" }, - { "id": "vesta-finance", "symbol": "vsta", "name": "Vesta Finance" }, - { "id": "vestallytoken", "symbol": "vtt", "name": "VesTallyToken" }, - { "id": "vesta-stable", "symbol": "vst", "name": "Vesta Stable" }, - { "id": "vestchain", "symbol": "vest", "name": "VestChain" }, - { "id": "vested-solarbeam", "symbol": "vesolar", "name": "Vested SolarBeam" }, - { "id": "vether", "symbol": "veth", "name": "Vether" }, - { "id": "vethor-token", "symbol": "vtho", "name": "VeThor Token" }, - { "id": "vetter-token", "symbol": "vetter", "name": "Vetter Token" }, - { "id": "veusd", "symbol": "veusd", "name": "VeUSD" }, - { "id": "vexanium", "symbol": "vex", "name": "Vexanium" }, - { "id": "vexchange", "symbol": "vex", "name": "Vexchange" }, - { "id": "vfox", "symbol": "vfox", "name": "VFOX" }, - { "id": "viacoin", "symbol": "via", "name": "Viacoin" }, - { "id": "viagra-token", "symbol": "viagra", "name": "Viagra Token" }, - { "id": "vibe", "symbol": "vibe", "name": "VIBE" }, - { "id": "viberate", "symbol": "vib", "name": "Viberate" }, - { "id": "vibe-token", "symbol": "vibe", "name": "Vibe Token" }, - { "id": "viblo", "symbol": "viblo", "name": "VIBLO" }, - { "id": "vibranium", "symbol": "vbn", "name": "Vibranium" }, - { "id": "vica-token", "symbol": "vica", "name": "ViCA Token" }, - { "id": "vicewrld", "symbol": "vice", "name": "Vicewrld" }, - { "id": "victoria-vr", "symbol": "vr", "name": "Victoria VR" }, - { "id": "victorum", "symbol": "vcc", "name": "Victorum" }, - { "id": "v-id-blockchain", "symbol": "vidt", "name": "VIDT Datalink" }, - { "id": "videocoin", "symbol": "vid", "name": "Vivid Labs" }, - { "id": "vidiachange", "symbol": "vida", "name": "Vidiachange" }, - { "id": "vidulum", "symbol": "vdl", "name": "Vidulum" }, - { "id": "vidy", "symbol": "vidy", "name": "VIDY" }, - { "id": "vidya", "symbol": "vidya", "name": "Vidya" }, - { "id": "vidyx", "symbol": "vidyx", "name": "VidyX" }, - { "id": "vig", "symbol": "vig", "name": "VIG" }, - { "id": "vigorus", "symbol": "vis", "name": "Vigorus" }, - { "id": "vikings-finance", "symbol": "val", "name": "Vikings Finance" }, - { "id": "vikings-inu", "symbol": "vikings", "name": "Vikings Inu" }, - { "id": "vikings-valhalla", "symbol": "vv", "name": "Vikings Valhalla" }, - { "id": "viking-swap", "symbol": "viking", "name": "Viking Swap" }, - { "id": "vikkytoken", "symbol": "vikky", "name": "VikkyToken" }, - { "id": "vince-chain", "symbol": "vce", "name": "Vince Chain" }, - { "id": "vinci", "symbol": "vinci", "name": "Vinci" }, - { "id": "vindax-coin", "symbol": "vd", "name": "VinDax Coin" }, - { "id": "vip-coin", "symbol": "vip", "name": "Vip Coin" }, - { "id": "vipcoin-gold", "symbol": "vcg", "name": "VipCoin.Gold" }, - { "id": "viper", "symbol": "viper", "name": "Viper" }, - { "id": "viperpit", "symbol": "xviper", "name": "ViperPit" }, - { "id": "viplus", "symbol": "vpl", "name": "Viplus" }, - { "id": "vipstarcoin", "symbol": "vips", "name": "VIPSTARCOIN" }, - { "id": "vip-token", "symbol": "vip", "name": "VIP Token" }, - { "id": "viral", "symbol": "viral", "name": "Viral" }, - { "id": "vira-lata-finance", "symbol": "reau", "name": "Vira-Lata Finance" }, - { "id": "viral-ethereum", "symbol": "eViral", "name": "Viral Ethereum" }, - { "id": "viral-inu", "symbol": "vinu", "name": "Viral Inu" }, - { "id": "vires-finance", "symbol": "vires", "name": "Vires Finance" }, - { "id": "virgo", "symbol": "vgo", "name": "Virgo" }, - { "id": "virgox-token", "symbol": "vxt", "name": "VirgoX Token" }, - { "id": "virtual-gamer", "symbol": "vgm", "name": "Virtual Gamer" }, - { - "id": "virtual-goods-token", - "symbol": "vgo", - "name": "Virtual Goods Token" - }, - { "id": "virtue-poker", "symbol": "vpp", "name": "Virtue Poker Points" }, - { "id": "virtus-finance", "symbol": "vap", "name": "Virtus Finance" }, - { "id": "viserion-finance", "symbol": "vefi", "name": "Viserion Finance" }, - { "id": "visio", "symbol": "visio", "name": "Visio" }, - { "id": "vision", "symbol": "vsn", "name": "Vision" }, - { "id": "vision-fi", "symbol": "vsn", "name": "Vision Fi" }, - { "id": "visiongame", "symbol": "vision", "name": "VisionGame" }, - { "id": "vision-metaverse", "symbol": "vs", "name": "Vision Metaverse" }, - { "id": "vision-network", "symbol": "vsn", "name": "Vision Network" }, - { "id": "visor", "symbol": "visr", "name": "Visor" }, - { "id": "vitadao", "symbol": "vita", "name": "VitaDAO" }, - { "id": "vita-inu", "symbol": "vinu", "name": "Vita Inu" }, - { "id": "vitality", "symbol": "vita", "name": "Vitality" }, - { "id": "vitall-markets", "symbol": "VITAL", "name": "Vitall Markets" }, - { "id": "vitamin-coin", "symbol": "vitc", "name": "Vitamin Coin" }, - { "id": "vite", "symbol": "vite", "name": "Vite" }, - { "id": "vitex", "symbol": "vx", "name": "ViteX Coin" }, - { "id": "vitoge", "symbol": "VITOGE", "name": "Vitoge" }, - { "id": "vitteey", "symbol": "vity", "name": "Vitteey" }, - { "id": "viva-classic", "symbol": "viva", "name": "Viva Classic" }, - { "id": "vivaion", "symbol": "vivaion", "name": "Vivaion" }, - { "id": "vival", "symbol": "viv", "name": "Vival" }, - { "id": "vive-la-bouje", "symbol": "vive", "name": "Vive La Bouje" }, - { "id": "vixco", "symbol": "vix", "name": "Vixco" }, - { "id": "vlad-finance", "symbol": "vlad", "name": "Vlad Finance" }, - { "id": "vlaunch", "symbol": "vpad", "name": "VLaunch" }, - { "id": "vmates", "symbol": "mate", "name": "Vmates" }, - { - "id": "vm-tycoons-businesses", - "symbol": "businesses", - "name": "VM Tycoons Businesses" - }, - { "id": "vndc", "symbol": "vndc", "name": "VNDC" }, - { "id": "vntchain", "symbol": "vnt", "name": "VNT Chain" }, - { "id": "vn-token", "symbol": "vn", "name": "VN Token" }, - { "id": "vnx-exchange", "symbol": "vnxlu", "name": "VNX Exchange" }, - { "id": "vodi-x", "symbol": "vdx", "name": "Vodi X" }, - { "id": "vodka-token", "symbol": "VODKA", "name": "Vodka Token" }, - { "id": "vodra", "symbol": "vdr", "name": "Vodra" }, - { "id": "voice-street", "symbol": "vst", "name": "Voice Street" }, - { "id": "void-games", "symbol": "void", "name": "Void Games" }, - { - "id": "volatility-protocol-token", - "symbol": "vol", - "name": "Volatility Protocol Token" - }, - { "id": "volentix-vtx", "symbol": "vtx", "name": "Volentix" }, - { "id": "vollar", "symbol": "vollar", "name": "V-Dimension" }, - { "id": "volt", "symbol": "acdc", "name": "Volt" }, - { "id": "volta", "symbol": "volta", "name": "Volta" }, - { "id": "voltage", "symbol": "volt", "name": "Voltage" }, - { "id": "volt-inu", "symbol": "VOLT", "name": "Volt Inu" }, - { "id": "volts-finance", "symbol": "volts", "name": "Volts.Finance" }, - { "id": "voltswap", "symbol": "volt", "name": "VoltSwap" }, - { "id": "voovoo", "symbol": "voo", "name": "VooVoo" }, - { "id": "vortex-dao", "symbol": "space", "name": "Vortex DAO" }, - { "id": "vortex-defi", "symbol": "vtx", "name": "Vortex DeFi" }, - { "id": "vortex-network", "symbol": "vtx", "name": "VorteX Network" }, - { "id": "votechain", "symbol": "vbsc", "name": "Votechain" }, - { "id": "voucher-eth", "symbol": "veth", "name": "Voucher ETH" }, - { "id": "vow", "symbol": "vow", "name": "Vow" }, - { "id": "voxel-x-network", "symbol": "vxl", "name": "Voxel X Network" }, - { "id": "vox-finance", "symbol": "vox", "name": "Vox.Finance" }, - { "id": "voxies", "symbol": "voxel", "name": "Voxies" }, - { "id": "voyager", "symbol": "vgr", "name": "Voyager" }, - { "id": "voyce", "symbol": "voyce", "name": "Voyce" }, - { "id": "voyr", "symbol": "voyrme", "name": "VOYR" }, - { "id": "voytek-bear-coin", "symbol": "bear", "name": "BEAR Coin" }, - { "id": "vpex-exchange", "symbol": "vpx", "name": "VPEX Exchange" }, - { "id": "vpncoin", "symbol": "vash", "name": "VPNCoin" }, - { "id": "vpunks-token", "symbol": "vpu", "name": "VPunks Token" }, - { "id": "vsolidus", "symbol": "vsol", "name": "VSolidus" }, - { "id": "vsync", "symbol": "vsx", "name": "Vsync" }, - { "id": "v-systems", "symbol": "vsys", "name": "V.SYSTEMS" }, - { "id": "vulcan-forged", "symbol": "pyr", "name": "Vulcan Forged" }, - { "id": "vulcano-2", "symbol": "vulc", "name": "Vulcano" }, - { "id": "vulkania", "symbol": "vlk", "name": "Vulkania" }, - { "id": "vulture-peak", "symbol": "vpk", "name": "Vulture Peak" }, - { "id": "vvs-finance", "symbol": "vvs", "name": "VVS Finance" }, - { "id": "vvsp", "symbol": "vvsp", "name": "vVSP" }, - { "id": "vybit", "symbol": "vi", "name": "Vybit" }, - { "id": "vyfinance", "symbol": "vyfi", "name": "VyFinance" }, - { "id": "vyndao", "symbol": "vyn", "name": "Vyndao" }, - { "id": "vynk-chain", "symbol": "vync", "name": "VYNK Chain" }, - { "id": "wabi", "symbol": "wabi", "name": "Wabi" }, - { "id": "wab-network", "symbol": "baw", "name": "BAW Network" }, - { "id": "wadzpay-token", "symbol": "wtk", "name": "WadzPay Token" }, - { "id": "wagerr", "symbol": "wgr", "name": "Wagerr" }, - { "id": "waggle-network", "symbol": "wag", "name": "Waggle Network" }, - { "id": "wagmi", "symbol": "wagmi", "name": "WAGMI" }, - { "id": "wagmidao", "symbol": "gmi", "name": "WagmiDAO" }, - { "id": "wagmi-game", "symbol": "wagmi", "name": "WAGMI Game" }, - { "id": "wagmi-on-solana", "symbol": "wagmi", "name": "WAGMI On Solana" }, - { "id": "wagyuswap", "symbol": "wag", "name": "WagyuSwap" }, - { "id": "waifer", "symbol": "waif", "name": "Waifer" }, - { "id": "waifu-token", "symbol": "waif", "name": "Waifu Token" }, - { "id": "waifu-vault-nftx", "symbol": "waifu", "name": "WAIFU Vault (NFTX)" }, - { "id": "wakanda-inu", "symbol": "wkd", "name": "Wakanda Inu" }, - { "id": "waletoken", "symbol": "wtn", "name": "Waletoken" }, - { "id": "walletnow", "symbol": "wnow", "name": "WalletNow" }, - { "id": "wallet-pay", "symbol": "xpay", "name": "Wallet Pay" }, - { "id": "wallet-plus-x", "symbol": "wpx", "name": "Wallet Plus X" }, - { "id": "wallet-swap", "symbol": "wswap", "name": "Wallet Swap" }, - { "id": "wallfair", "symbol": "wfair", "name": "Wallfair" }, - { "id": "wallstreetbets-com", "symbol": "wsb", "name": "Wallstreetbets.com" }, - { - "id": "wall-street-bets-dapp", - "symbol": "wsb", - "name": "WallStreetBets DApp" - }, - { - "id": "wallstreetbets-token", - "symbol": "wsbt", - "name": "WallStreetBets Token" - }, - { - "id": "wall-street-capital", - "symbol": "wsc", - "name": "Wall Street Capital" - }, - { "id": "wall-street-games", "symbol": "wsg", "name": "Wall Street Games" }, - { - "id": "wall-street-inu", - "symbol": "wallstreetinu", - "name": "Wall Street Inu" - }, - { "id": "wallstreetninja", "symbol": "WSN", "name": "WallStreetNinja" }, - { "id": "walter-inu", "symbol": "$winu", "name": "Walter Inu" }, - { "id": "waltonchain", "symbol": "wtc", "name": "Waltonchain" }, - { "id": "wam", "symbol": "wam", "name": "Wam" }, - { "id": "wamo", "symbol": "wamo", "name": "WAMO" }, - { "id": "wanaka-farm", "symbol": "wana", "name": "Wanaka Farm" }, - { - "id": "wanaka-farm-wairere-token", - "symbol": "wai", - "name": "Wanaka Farm WAIRERE Token" - }, - { "id": "wanamoon", "symbol": "moon", "name": "WANAMOON" }, - { "id": "wanavax", "symbol": "wanavax", "name": "wanAVAX" }, - { "id": "wanbtc", "symbol": "wanbtc", "name": "wanBTC" }, - { "id": "wanchain", "symbol": "wan", "name": "Wanchain" }, - { "id": "wanda-exchange", "symbol": "we", "name": "Wanda Exchange" }, - { "id": "waneth", "symbol": "waneth", "name": "wanETH" }, - { "id": "wannaswap", "symbol": "wanna", "name": "WannaSwap" }, - { "id": "wanswap", "symbol": "wasp", "name": "WanSwap" }, - { "id": "wanusdc", "symbol": "wanusdc", "name": "wanUSDC" }, - { "id": "wanusdt", "symbol": "wanusdt", "name": "wanUSDT" }, - { "id": "wanxrp", "symbol": "wanxrp", "name": "wanXRP" }, - { "id": "wapswap-finance", "symbol": "wap", "name": "WapSwap Finance" }, - { "id": "war-bond", "symbol": "wbond", "name": "War Bond" }, - { "id": "warden", "symbol": "wad", "name": "Warden" }, - { "id": "warena", "symbol": "RENA", "name": "Warena" }, - { "id": "war-of-tribes", "symbol": "wotg", "name": "War of Tribes" }, - { "id": "warp-bond", "symbol": "warp", "name": "Warp Bond" }, - { "id": "warp-finance", "symbol": "warp", "name": "Warp Finance" }, - { - "id": "warrior-rare-essentials", - "symbol": "ware", - "name": "Warrior Rare Essentials" - }, - { "id": "warriors-land-fuel", "symbol": "wlf", "name": "Warriors Land Fuel" }, - { "id": "warrior-token", "symbol": "war", "name": "Warrior Token" }, - { "id": "warship-battles", "symbol": "$oil", "name": "Warship Battles" }, - { "id": "wasabix", "symbol": "wasabi", "name": "WasabiX" }, - { "id": "wasdaq-finance", "symbol": "wsdq", "name": "Wasdaq Finance" }, - { "id": "wasder", "symbol": "was", "name": "Wasder" }, - { "id": "waste-coin", "symbol": "waco", "name": "Waste Digital Coin" }, - { "id": "watchdo", "symbol": "wdo", "name": "WatchDO" }, - { "id": "watchmen", "symbol": "WTM", "name": "WATCHMEN" }, - { "id": "watchtower", "symbol": "wtw", "name": "Watchtower [OLD]" }, - { "id": "watchtower-2", "symbol": "wtw", "name": "Watchtower" }, - { - "id": "waterfall-finance", - "symbol": "waterfall", - "name": "Waterfall Finance" - }, - { - "id": "waterfall-governance-token", - "symbol": "wtf", - "name": "Waterfall Governance Token" - }, - { "id": "wault", "symbol": "waultx", "name": "Wault" }, - { "id": "waultswap", "symbol": "wex", "name": "WaultSwap" }, - { - "id": "waultswap-polygon", - "symbol": "wexpoly", - "name": "WaultSwap Polygon" - }, - { "id": "wault-usd", "symbol": "wusd", "name": "Wault USD" }, - { "id": "wave-pay-coin", "symbol": "wpc", "name": "Wave Pay Coin" }, - { "id": "waves", "symbol": "waves", "name": "Waves" }, - { - "id": "waves-community-token", - "symbol": "wct", - "name": "Waves Community Token" - }, - { "id": "waves-ducks", "symbol": "egg", "name": "Waves Ducks" }, - { "id": "waves-enterprise", "symbol": "west", "name": "Waves Enterprise" }, - { "id": "waves-exchange", "symbol": "wx", "name": "Waves Exchange" }, - { "id": "wavesgo", "symbol": "wgo", "name": "WavesGo" }, - { "id": "wax", "symbol": "waxp", "name": "WAX" }, - { "id": "waxe", "symbol": "waxe", "name": "WAXE" }, - { "id": "wayawolfcoin", "symbol": "ww", "name": "WayaWolfCoin" }, - { "id": "waykichain", "symbol": "wicc", "name": "WaykiChain" }, - { - "id": "waykichain-governance-coin", - "symbol": "wgrt", - "name": "WaykiChain Governance Coin" - }, - { "id": "wazirx", "symbol": "wrx", "name": "WazirX" }, - { "id": "wbnb", "symbol": "wbnb", "name": "Wrapped BNB" }, - { "id": "wealthsecrets", "symbol": "wsc", "name": "WealthSecrets" }, - { "id": "wealthy-habits", "symbol": "whb", "name": "Wealthy Habits" }, - { "id": "wearesatoshi", "symbol": "n8v", "name": "NativeCoin" }, - { "id": "weave", "symbol": "weave", "name": "Weave" }, - { - "id": "web3-all-best-ico", - "symbol": "web3allbi", - "name": "Web3 ALL BEST ICO" - }, - { "id": "web3-dev", "symbol": "web3", "name": "Web3 Dev" }, - { "id": "web-3-development", "symbol": "3web", "name": "Web 3 Development" }, - { "id": "web3-doge", "symbol": "web3", "name": "Web3 Doge" }, - { "id": "web3games", "symbol": "w3g", "name": "Web3Games" }, - { "id": "web3-inu", "symbol": "web3", "name": "WEB3 Inu" }, - { "id": "web3land", "symbol": "web3", "name": "WEB3Land" }, - { "id": "webchain", "symbol": "mintme", "name": "MintMe.com Coin" }, - { "id": "webcoin", "symbol": "web", "name": "Webcoin" }, - { "id": "web-coin-pay", "symbol": "wec", "name": "Web Coin Pay" }, - { "id": "webdollar", "symbol": "webd", "name": "webdollar" }, - { "id": "webflix", "symbol": "wfx", "name": "WebFlix" }, - { "id": "web-four", "symbol": "webfour", "name": "WEBFOUR" }, - { "id": "web-innovation-ph", "symbol": "webn", "name": "WEBN token" }, - { - "id": "weble-ecosystem-token", - "symbol": "wet", - "name": "Weble Ecosystem Token" - }, - { "id": "webooswap", "symbol": "weboo", "name": "WebooSwap" }, - { "id": "wechain-coin", "symbol": "wxtc", "name": "WeChain Coin" }, - { "id": "wecoown", "symbol": "wcx", "name": "WeCoOwn" }, - { "id": "weecoins", "symbol": "wcs", "name": "Weecoins" }, - { "id": "weenie-inu", "symbol": "weenie", "name": "Weenie Inu" }, - { "id": "weentar", "symbol": "wntr", "name": "Weentar" }, - { "id": "ween-token", "symbol": "weens", "name": "Ween Token" }, - { "id": "wegro", "symbol": "wegro", "name": "WeGro" }, - { "id": "weird-dao", "symbol": "weird", "name": "Weird Dao" }, - { "id": "weiup", "symbol": "weiup", "name": "WeiUp" }, - { "id": "weld", "symbol": "weld", "name": "Weld" }, - { - "id": "wellness-convertible", - "symbol": "wcv", - "name": "Wellness Convertible" - }, - { "id": "welltrado", "symbol": "wtl", "name": "Welltrado" }, - { "id": "welnance-finance", "symbol": "wel", "name": "Welnance Finance" }, - { "id": "we-love-gm", "symbol": "gm", "name": "we love gm" }, - { - "id": "welups-blockchain", - "symbol": "welups", - "name": "Welups Blockchain" - }, - { "id": "wemix-token", "symbol": "wemix", "name": "WEMIX Token" }, - { "id": "wenlambo", "symbol": "wenlambo", "name": "Wenlambo" }, - { "id": "wen-lambo", "symbol": "lambo", "name": "Wen Lambo" }, - { "id": "wenmoon", "symbol": "wm", "name": "WenMoon" }, - { "id": "wepiggy-coin", "symbol": "WPC", "name": "WePiggy Coin" }, - { "id": "wepower", "symbol": "wpr", "name": "WePower" }, - { "id": "werenode", "symbol": "wrc", "name": "Werenode" }, - { "id": "werewolf-coin", "symbol": "wwc", "name": "Werewolf Coin" }, - { "id": "werewolves-game", "symbol": "wolf", "name": "Werewolves Game" }, - { "id": "westarter", "symbol": "war", "name": "WeStarter" }, - { "id": "weta-vr", "symbol": "WETA", "name": "WETA VR" }, - { "id": "weth", "symbol": "weth", "name": "WETH" }, - { "id": "wetrust", "symbol": "trst", "name": "WeTrust" }, - { "id": "weway", "symbol": "wwy", "name": "WeWay" }, - { "id": "weyu", "symbol": "weyu", "name": "WEYU" }, - { "id": "wfdp", "symbol": "wfdp", "name": "WFDP" }, - { "id": "wgmi", "symbol": "wgmi", "name": "WGMI" }, - { "id": "whale", "symbol": "whale", "name": "WHALE" }, - { "id": "whale-coin", "symbol": "whale", "name": "Whale Coin" }, - { "id": "whale-fall", "symbol": "whale", "name": "Whale Fall" }, - { "id": "whalegirl", "symbol": "WGIRL", "name": "WhaleGirl" }, - { "id": "whalemap", "symbol": "wmp", "name": "Whalemap" }, - { "id": "whaleroom", "symbol": "whl", "name": "WhaleRoom" }, - { - "id": "whalestreet-shrimp-token", - "symbol": "$hrimp", - "name": "WhaleStreet $hrimp Token" - }, - { "id": "whale-vault-nftx", "symbol": "whale", "name": "WHALE Vault (NFTX)" }, - { "id": "wheat-token", "symbol": "wheat", "name": "Wheat Token (BSC)" }, - { "id": "wheat-token-avax", "symbol": "wheat", "name": "Wheat Token (AVAX)" }, - { "id": "wheelers", "symbol": "wheel", "name": "Wheelers" }, - { "id": "when-lambo", "symbol": "lmbo", "name": "When Lambo" }, - { "id": "when-token", "symbol": "when", "name": "WHEN Token" }, - { "id": "whey", "symbol": "whey", "name": "WHEY" }, - { "id": "whey-token", "symbol": "whey", "name": "Whey Token" }, - { "id": "whirl-finance", "symbol": "whirl", "name": "Whirl Finance" }, - { "id": "whis-inu", "symbol": "whis", "name": "Whis Inu" }, - { "id": "whitecoin", "symbol": "xwc", "name": "Whitecoin" }, - { "id": "whiteheart", "symbol": "white", "name": "Whiteheart" }, - { "id": "whiterockcasino", "symbol": "wrc", "name": "WhiteRockCasino" }, - { "id": "white-whale", "symbol": "whale", "name": "White Whale" }, - { "id": "whitex", "symbol": "whx", "name": "WhiteX" }, - { "id": "whitex-community", "symbol": "whxc", "name": "WhiteX Community" }, - { "id": "whive", "symbol": "whive", "name": "Whive" }, - { "id": "whole-earth-coin", "symbol": "wec", "name": "Whole Earth Coin" }, - { "id": "whole-network", "symbol": "node", "name": "Whole Network" }, - { "id": "wibx", "symbol": "wbx", "name": "WiBX" }, - { "id": "wicrypt", "symbol": "wnt", "name": "Wicrypt" }, - { "id": "wider-coin", "symbol": "wdr", "name": "Wider Coin" }, - { "id": "widiland", "symbol": "widi", "name": "WidiLand" }, - { "id": "widi-soul", "symbol": "wso", "name": "Widi Soul" }, - { "id": "wifedoge", "symbol": "wifedoge", "name": "Wifedoge" }, - { "id": "wifi-coin", "symbol": "wifi", "name": "Wifi Coin" }, - { "id": "wiggly-finance", "symbol": "wgl", "name": "Wiggly Finance" }, - { "id": "wigoswap", "symbol": "wigo", "name": "WigoSwap" }, - { "id": "wiix-coin", "symbol": "wxc", "name": "WIIX Coin" }, - { "id": "wiki-token", "symbol": "wiki", "name": "Wiki Token" }, - { "id": "wild-beast-block", "symbol": "wbb", "name": "Wild Beast Block" }, - { "id": "wild-credit", "symbol": "wild", "name": "Wild Credit" }, - { "id": "wilder-world", "symbol": "wild", "name": "Wilder World" }, - { "id": "wildfire-token", "symbol": "wildf", "name": "Wildfire Token" }, - { "id": "wild-island-game", "symbol": "wild", "name": "Wild Island Game" }, - { "id": "wild-ride", "symbol": "wild", "name": "Wild Ride" }, - { "id": "williecoin", "symbol": "willie", "name": "Williecoin" }, - { "id": "winco", "symbol": "wco", "name": "Winco" }, - { "id": "windfall-token", "symbol": "wft", "name": "Windfall Token" }, - { "id": "winding-tree", "symbol": "lif", "name": "Lif" }, - { "id": "windoge95", "symbol": "wndg95", "name": "Windoge95" }, - { "id": "winerz", "symbol": "$wnz", "name": "Winerz" }, - { "id": "wine-shares", "symbol": "wine", "name": "Wine Shares" }, - { "id": "wing-finance", "symbol": "wing", "name": "Wing Finance" }, - { "id": "wings", "symbol": "wings", "name": "Wings" }, - { "id": "wings-protocol", "symbol": "xwip", "name": "Wings Protocol" }, - { "id": "wingswap", "symbol": "wis", "name": "WingSwap" }, - { "id": "wink", "symbol": "win", "name": "WINkLink" }, - { "id": "winklink-bsc", "symbol": "win", "name": "WINkLink BSC" }, - { "id": "winnow", "symbol": "wnnw", "name": "WinNow" }, - { "id": "winry-inu", "symbol": "winry", "name": "Winry Inu" }, - { "id": "winstars", "symbol": "wnl", "name": "WinStars Live" }, - { "id": "wipemyass", "symbol": "wipe", "name": "WipeMyAss" }, - { "id": "wirex", "symbol": "wxt", "name": "Wirex" }, - { "id": "wirtual", "symbol": "wirtual", "name": "Wirtual" }, - { "id": "wiseavax", "symbol": "wiseavax", "name": "WiseAvax" }, - { "id": "wise-token11", "symbol": "wise", "name": "Wise" }, - { "id": "wish-finance-2", "symbol": "wish", "name": "Wish Finance" }, - { "id": "wisteria-swap", "symbol": "wst", "name": "Wisteria Swap" }, - { "id": "witchain", "symbol": "wit", "name": "WITChain" }, - { "id": "witcher-inu", "symbol": "winu", "name": "Witcher Inu" }, - { "id": "witcherverse", "symbol": "wch", "name": "WitcherVerse" }, - { "id": "witch-token", "symbol": "witch", "name": "Witch Token" }, - { "id": "witnet", "symbol": "wit", "name": "Witnet" }, - { "id": "wiva", "symbol": "wiva", "name": "WIVA" }, - { "id": "wixlar", "symbol": "wix", "name": "Wixlar" }, - { "id": "wizardia", "symbol": "wzrd", "name": "Wizardia" }, - { "id": "wizardium", "symbol": "wizzy", "name": "Wizardium" }, - { - "id": "wizards-and-dragons", - "symbol": "gp", - "name": "Wizards And Dragons" - }, - { "id": "wizard-token", "symbol": "wizard", "name": "Wizard Token" }, - { - "id": "wizard-vault-nftx", - "symbol": "wizard", - "name": "WIZARD Vault (NFTX)" - }, - { "id": "wizarre-scroll", "symbol": "scrl", "name": "Wizarre Scroll" }, - { "id": "wjewel", "symbol": "wjewel", "name": "WJEWEL" }, - { "id": "wliti", "symbol": "wliti", "name": "wLITI" }, - { "id": "wlitidao", "symbol": "wld", "name": "wLitiDAO" }, - { "id": "wmatic", "symbol": "wmatic", "name": "Wrapped Matic" }, - { "id": "wodex", "symbol": "wmt", "name": "Wodex" }, - { "id": "wohpedao", "symbol": "peace", "name": "WohpeDao" }, - { "id": "wojak-finance", "symbol": "WOJ", "name": "Wojak Finance" }, - { "id": "wolfcoin", "symbol": "wolf", "name": "WOLFCOIN" }, - { "id": "wolfecoin", "symbol": "wolfe", "name": "Wolfecoin" }, - { "id": "wolf-game-wool", "symbol": "wool", "name": "Wolf Game Wool" }, - { "id": "wolfgirl", "symbol": "WLFGRL", "name": "Wolfgirl" }, - { "id": "wolf-girl", "symbol": "wolfgirl", "name": "Wolf Girl" }, - { "id": "wolfi", "symbol": "wolfi", "name": "WOLFI" }, - { "id": "wolf_king", "symbol": "wolf", "name": "Wolf King" }, - { "id": "wolf-pups", "symbol": "wolfies", "name": "WOLF PUPS" }, - { - "id": "wolfsafepoorpeople", - "symbol": "wspp", - "name": "WolfSafePoorPeople" - }, - { - "id": "wolfsafepoorpeople-polygon", - "symbol": "wspp", - "name": "WolfSafePoorPeople Polygon" - }, - { "id": "wolf-town-wool", "symbol": "wtwool", "name": "Wolf Town Wool" }, - { "id": "wolfy", "symbol": "wolfy", "name": "WOLFY" }, - { "id": "wolverine", "symbol": "wlvr", "name": "Wolverine" }, - { "id": "wolverinu", "symbol": "wolverinu", "name": "WOLVERINU" }, - { - "id": "wolves-of-wall-street", - "symbol": "wows", - "name": "Wolves of Wall Street" - }, - { - "id": "women-empowerment-token", - "symbol": "wemp", - "name": "Women Empowerment Token" - }, - { "id": "wom-token", "symbol": "wom", "name": "WOM Protocol" }, - { - "id": "wonderfi-tokenized-stock", - "symbol": "wndr", - "name": "WonderFi Tokenized Stock" - }, - { "id": "wonderhero", "symbol": "wnd", "name": "WonderHero" }, - { "id": "wonderhero-hon", "symbol": "hon", "name": "WonderHero HON" }, - { "id": "wonderland", "symbol": "time", "name": "Wonderland" }, - { "id": "woodcoin", "symbol": "log", "name": "Woodcoin" }, - { "id": "woof-token", "symbol": "woof", "name": "WOOF" }, - { "id": "woof-token-2", "symbol": "woof", "name": "Woof Token" }, - { "id": "woofy", "symbol": "woofy", "name": "Woofy" }, - { "id": "wool-token", "symbol": "wool", "name": "Wool" }, - { "id": "woo-network", "symbol": "woo", "name": "WOO Network" }, - { "id": "woonkly-power", "symbol": "woop", "name": "Woonkly Power" }, - { "id": "woop", "symbol": "woop", "name": "WOOP" }, - { "id": "woozoo-music", "symbol": "wzm", "name": "Woozoo Music" }, - { "id": "wordl-defi", "symbol": "wordl", "name": "Wordl DeFi" }, - { "id": "wordlex", "symbol": "wdx", "name": "Wordlex" }, - { "id": "workertown", "symbol": "wtn", "name": "WorkerTown" }, - { "id": "work-quest", "symbol": "wqt", "name": "Work Quest" }, - { "id": "worktips", "symbol": "wtip", "name": "Worktips" }, - { "id": "worldcoin", "symbol": "wdc", "name": "WorldCoin" }, - { "id": "worldcore", "symbol": "wrc", "name": "Worldcore" }, - { "id": "world-mobile-token", "symbol": "wmt", "name": "World Mobile Token" }, - { "id": "world-of-defish", "symbol": "WOD", "name": "World of Defish" }, - { "id": "world-of-v", "symbol": "wov", "name": "World of V" }, - { "id": "world-of-waves", "symbol": "wow", "name": "World of Waves" }, - { "id": "worldpet", "symbol": "wpt", "name": "WORLDPET" }, - { "id": "worldplus", "symbol": "wpl", "name": "WorldPlus" }, - { - "id": "world-stream-finance", - "symbol": "$tream", - "name": "World Stream Finance" - }, - { "id": "world-token", "symbol": "world", "name": "World Token" }, - { "id": "wormfi", "symbol": "worm", "name": "WormFi" }, - { "id": "worthpad", "symbol": "worth", "name": "Worthpad" }, - { "id": "worthwhile", "symbol": "whe", "name": "Worthwhile" }, - { "id": "wownero", "symbol": "wow", "name": "Wownero" }, - { "id": "wowperson", "symbol": "wowp", "name": "WowPerson" }, - { "id": "wowswap", "symbol": "wow", "name": "WOWswap" }, - { "id": "wow-token", "symbol": "wow", "name": "WOW-token" }, - { "id": "wozx", "symbol": "wozx", "name": "Efforce" }, - { "id": "wpp-token", "symbol": "wpp", "name": "WPP Token" }, - { "id": "wraith", "symbol": "wraith", "name": "Wraith" }, - { "id": "wraith-protocol", "symbol": "wraith", "name": "Wraith Protocol" }, - { - "id": "wrap-governance-token", - "symbol": "wrap", - "name": "WRAP Governance Token" - }, - { "id": "wrapped-ada", "symbol": "wada", "name": "Wrapped ADA" }, - { - "id": "wrapped-ampleforth", - "symbol": "wampl", - "name": "Wrapped Ampleforth" - }, - { "id": "wrapped-anatha", "symbol": "wanatha", "name": "Wrapped ANATHA" }, - { "id": "wrapped-atromg8", "symbol": "wag8", "name": "Wrapped ATROMG8" }, - { "id": "wrapped-avax", "symbol": "wavax", "name": "Wrapped AVAX" }, - { "id": "wrapped-bch", "symbol": "wbch", "name": "Wrapped BCH" }, - { "id": "wrapped-bind", "symbol": "wbind", "name": "Wrapped BIND" }, - { "id": "wrapped-bitcoin", "symbol": "wbtc", "name": "Wrapped Bitcoin" }, - { - "id": "wrapped-bitcoin-celer", - "symbol": "ceWBTC", - "name": "Wrapped Bitcoin - Celer" - }, - { - "id": "wrapped-bitcoin-sollet", - "symbol": "sobtc", - "name": "Wrapped Bitcoin (Sollet)" - }, - { - "id": "wrapped-bitcoin-stacks", - "symbol": "xbtc", - "name": "Wrapped Bitcoin-Stacks" - }, - { - "id": "wrapped-bnb-celer", - "symbol": "ceWBNB", - "name": "Wrapped BNB - Celer" - }, - { - "id": "wrapped-btc-wormhole", - "symbol": "wbtc", - "name": "Wrapped BTC (Wormhole)" - }, - { "id": "wrapped-btt", "symbol": "wbtt", "name": "Wrapped BTT" }, - { "id": "wrapped-busd", "symbol": "wbusd", "name": "Wrapped BUSD" }, - { - "id": "wrapped-busd-allbridge-from-bsc", - "symbol": "abbusd", - "name": "Wrapped BUSD (Allbridge from BSC)" - }, - { "id": "wrapped-celo", "symbol": "wcelo", "name": "Wrapped CELO" }, - { - "id": "wrapped-celo-dollar", - "symbol": "wcusd", - "name": "Wrapped Celo Dollar" - }, - { - "id": "wrapped-centrifuge", - "symbol": "wcfg", - "name": "Wrapped Centrifuge" - }, - { - "id": "wrapped-chainlink-sollet", - "symbol": "solink", - "name": "Wrapped Chainlink (Sollet)" - }, - { "id": "wrapped-conceal", "symbol": "wccx", "name": "Wrapped Conceal" }, - { "id": "wrapped-cro", "symbol": "wcro", "name": "Wrapped CRO" }, - { - "id": "wrapped-cryptokitties", - "symbol": "wck", - "name": "Wrapped CryptoKitties" - }, - { - "id": "wrapped-cusd-allbridge-from-celo", - "symbol": "acusd", - "name": "Wrapped CUSD (Allbridge from Celo)" - }, - { "id": "wrapped-dai", "symbol": "wdai", "name": "Wrapped DAI" }, - { "id": "wrapped-dgld", "symbol": "wdgld", "name": "Wrapped-DGLD" }, - { "id": "wrapped-ducatusx", "symbol": "wducx", "name": "Wrapped DucatusX" }, - { "id": "wrapped-ecomi", "symbol": "womi", "name": "Wrapped ECOMI" }, - { "id": "wrapped-elastos", "symbol": "wela", "name": "Wrapped Elastos" }, - { "id": "wrapped-elrond", "symbol": "wegld", "name": "Wrapped Elrond" }, - { - "id": "wrapped-ether-celer", - "symbol": "ceWETH", - "name": "Wrapped Ether - Celer" - }, - { - "id": "wrapped-ethereum-sollet", - "symbol": "soeth", - "name": "Wrapped Ethereum (Sollet)" - }, - { "id": "wrapped-fantom", "symbol": "wftm", "name": "Wrapped Fantom" }, - { - "id": "wrapped-fantom-celer", - "symbol": "ceWFTM", - "name": "Wrapped Fantom - Celer" - }, - { "id": "wrapped-filecoin", "symbol": "wfil", "name": "Wrapped Filecoin" }, - { "id": "wrapped-flow", "symbol": "wflow", "name": "Wrapped Flow" }, - { - "id": "wrapped-gen-0-cryptokitties", - "symbol": "wg0", - "name": "Wrapped Gen-0 CryptoKitties" - }, - { "id": "wrapped-hec", "symbol": "wshec", "name": "Wrapped HEC" }, - { "id": "wrapped-hoo", "symbol": "whoo", "name": "Wrapped HOO" }, - { - "id": "wrapped-huobi-token", - "symbol": "wht", - "name": "Wrapped Huobi Token" - }, - { "id": "wrapped-iotex", "symbol": "wiotx", "name": "Wrapped IoTex" }, - { "id": "wrapped-kadena", "symbol": "wkda", "name": "Wrapped Kadena" }, - { "id": "wrapped-kcs", "symbol": "wkcs", "name": "Wrapped KCS" }, - { "id": "wrapped-leo", "symbol": "wleo", "name": "Wrapped LEO" }, - { "id": "wrapped-link", "symbol": "wlink", "name": "Wrapped Link" }, - { - "id": "wrapped-matic-tezos", - "symbol": "wmatic", - "name": "Wrapped Matic Tezos" - }, - { "id": "wrapped-memory", "symbol": "wmemo", "name": "Wonderful Memories" }, - { "id": "wrapped-metrix", "symbol": "mrxb", "name": "Wrapped Metrix" }, - { "id": "wrapped-moonbeam", "symbol": "wglmr", "name": "Wrapped Moonbeam" }, - { - "id": "wrapped-moon-cats", - "symbol": "mcat20", - "name": "Wrapped Moon Cats" - }, - { "id": "wrapped-nce", "symbol": "WNCE", "name": "Wrapped NCE" }, - { "id": "wrapped-ncg", "symbol": "wncg", "name": "Wrapped NCG" }, - { "id": "wrapped-near", "symbol": "wnear", "name": "Wrapped Near" }, - { - "id": "wrapped-newyorkcoin", - "symbol": "wnyc", - "name": "Wrapped NewYorkCoin" - }, - { "id": "wrapped-nxm", "symbol": "wnxm", "name": "Wrapped NXM" }, - { "id": "wrapped-okt", "symbol": "wokt", "name": "Wrapped OKT" }, - { "id": "wrapped-one", "symbol": "wone", "name": "Wrapped One" }, - { "id": "wrapped-paycoin", "symbol": "wpci", "name": "Wrapped Paycoin" }, - { "id": "wrapped-pkt", "symbol": "wpkt", "name": "Wrapped PKT" }, - { "id": "wrapped-police", "symbol": "polis", "name": "Wrapped Police" }, - { "id": "wrapped-rose", "symbol": "wrose", "name": "Wrapped Rose" }, - { "id": "wrapped-sexod", "symbol": "wsexod", "name": "Wrapped sEXOD" }, - { - "id": "wrapped-shiden-network", - "symbol": "sdn", - "name": "Wrapped Shiden Network" - }, - { "id": "wrapped-solana", "symbol": "sol", "name": "Wrapped Solana" }, - { - "id": "wrapped-staked-metaversepro", - "symbol": "wsmeta", - "name": "Wrapped Staked MetaversePro" - }, - { - "id": "wrapped-staked-necc", - "symbol": "nnecc", - "name": "Wrapped Staked Necc" - }, - { - "id": "wrapped-staked-olympus", - "symbol": "wsohm", - "name": "Wrapped Staked Olympus" - }, - { - "id": "wrapped-staked-scr", - "symbol": "wsscr", - "name": "Wrapped Staked SCR" - }, - { "id": "wrapped-star", "symbol": "wstr", "name": "Wrapped Star" }, - { "id": "wrapped-statera", "symbol": "wsta", "name": "Wrapped Statera" }, - { "id": "wrapped-steth", "symbol": "wsteth", "name": "Wrapped stETH" }, - { "id": "wrapped-syscoin", "symbol": "wsys", "name": "Wrapped Syscoin" }, - { "id": "wrapped-telos", "symbol": "wtlos", "name": "Wrapped Telos" }, - { "id": "wrapped-terra", "symbol": "luna", "name": "Wrapped Terra" }, - { "id": "wrapped-tezos", "symbol": "wxtz", "name": "Wrapped Tezos" }, - { "id": "wrapped-tfuel", "symbol": "tfuel", "name": "Wrapped Theta Fuel" }, - { - "id": "wrapped-thunderpokt", - "symbol": "wtpokt", - "name": "Wrapped ThunderPOKT" - }, - { - "id": "wrapped-ton-crystal", - "symbol": "wton", - "name": "Wrapped TON Crystal" - }, - { "id": "wrapped-tron", "symbol": "wtrx", "name": "Wrapped Tron" }, - { - "id": "wrapped-turtlecoin", - "symbol": "wtrtl", - "name": "Wrapped TurtleCoin" - }, - { "id": "wrapped-usd", "symbol": "wusd", "name": "Wrapped USD" }, - { "id": "wrapped-usdc", "symbol": "wusdc", "name": "Wrapped USDC" }, - { "id": "wrapped-usdt", "symbol": "wusdt", "name": "Wrapped USDT" }, - { - "id": "wrapped-usdt-allbridge-from-polygon", - "symbol": "apusdt", - "name": "Wrapped USDT (Allbridge from Polygon)" - }, - { "id": "wrapped-ust", "symbol": "ust", "name": "Wrapped UST" }, - { "id": "wrapped-vechain", "symbol": "wvet", "name": "Wrapped VeChain" }, - { "id": "wrapped-velas", "symbol": "wvlx", "name": "Wrapped Velas" }, - { - "id": "wrapped-virgin-gen-0-cryptokitties", - "symbol": "wvg0", - "name": "Wrapped Virgin Gen-0 CryptoKittties" - }, - { "id": "wrapped-vsolidus", "symbol": "wvsol", "name": "Wrapped VSolidus" }, - { "id": "wrapped-wan", "symbol": "wwan", "name": "Wrapped Wan" }, - { "id": "wrapped-weth", "symbol": "wweth", "name": "Tezos Wrapped WETH" }, - { "id": "wrapped-widecoin", "symbol": "wwcn", "name": "Wrapped Widecoin" }, - { "id": "wrapped-xbtc", "symbol": "wxbtc", "name": "Wrapped xBTC" }, - { "id": "wrapped-xdai", "symbol": "wxdai", "name": "Wrapped XDAI" }, - { "id": "wrapped-xmr-btse", "symbol": "wxmr", "name": "Wrapped XMR by BTSE" }, - { "id": "wrapped-xrp", "symbol": "wxrp", "name": "Wrapped XRP" }, - { - "id": "wrapped-yfi-sollet", - "symbol": "soyfi", - "name": "Wrapped YFI (Sollet)" - }, - { "id": "wsb-sh", "symbol": "wsbt", "name": "WSB.sh" }, - { "id": "wtf-token", "symbol": "wtf", "name": "WTF Token" }, - { "id": "wula", "symbol": "wula", "name": "Wula" }, - { "id": "wwbtc", "symbol": "wwbtc", "name": "wWBTC" }, - { "id": "wxcoin", "symbol": "wxc", "name": "WXCOINS" }, - { "id": "x", "symbol": "x", "name": "SpaceGrime X" }, - { "id": "x2", "symbol": "x2", "name": "X2" }, - { "id": "x-2", "symbol": "x", "name": "X" }, - { "id": "x22", "symbol": "x22", "name": "X22" }, - { "id": "x2y2", "symbol": "x2y2", "name": "X2Y2" }, - { "id": "x42-protocol", "symbol": "x42", "name": "X42 Protocol" }, - { "id": "x8-project", "symbol": "x8x", "name": "X8X Token" }, - { "id": "x99token", "symbol": "x99", "name": "x99Token" }, - { "id": "x-ae-a-12", "symbol": "xaea12", "name": "X AE A-12" }, - { "id": "xaea-xii-token", "symbol": "xaea-xii", "name": "XAEA-Xii Token" }, - { "id": "xana", "symbol": "xeta", "name": "Xana" }, - { "id": "xanalia", "symbol": "alia", "name": "Xanalia" }, - { "id": "xape", "symbol": "xp", "name": "xApe" }, - { "id": "xaurum", "symbol": "xaur", "name": "Xaurum" }, - { "id": "xave-coin", "symbol": "xvc", "name": "Xave Coin" }, - { - "id": "xaviera-techno-solutions", - "symbol": "xts", - "name": "Xaviera Techno Solutions" - }, - { "id": "xbe-token", "symbol": "xbe", "name": "XBE Token" }, - { "id": "xbit", "symbol": "xbt", "name": "Xbit" }, - { "id": "x-block", "symbol": "ix", "name": "X-Block" }, - { "id": "xbn", "symbol": "xbn", "name": "Elastic BNB" }, - { - "id": "xbn-community-token", - "symbol": "xbc", - "name": "XBN Community Token" - }, - { "id": "xbullion", "symbol": "gold", "name": "XBullion" }, - { "id": "xbullion_silver", "symbol": "silv", "name": "XBullion Silver" }, - { "id": "xbx", "symbol": "xbx", "name": "XBX" }, - { "id": "xcad-network", "symbol": "xcad", "name": "XCAD Network" }, - { "id": "xcarnival", "symbol": "xcv", "name": "XCarnival" }, - { "id": "x-cash", "symbol": "xcash", "name": "X-CASH" }, - { "id": "xcel-swap", "symbol": "xld", "name": "Xcel Defi" }, - { "id": "xceltoken-plus", "symbol": "xlab", "name": "XCELTOKEN PLUS" }, - { "id": "xcf-token", "symbol": "xcf", "name": "XCF Token" }, - { "id": "xchainz", "symbol": "xcz", "name": "XChainZ" }, - { "id": "xcom", "symbol": "xc", "name": "XCOM" }, - { "id": "x-consoles", "symbol": "game", "name": "X-Consoles" }, - { "id": "xdai", "symbol": "xdai", "name": "XDAI" }, - { "id": "xdai-native-comb", "symbol": "xcomb", "name": "xDai Native Comb" }, - { "id": "xdai-stake", "symbol": "stake", "name": "STAKE" }, - { "id": "xdce-crowd-sale", "symbol": "xdc", "name": "XDC Network" }, - { "id": "xdefi", "symbol": "xdefi", "name": "XDEFI" }, - { - "id": "xdefi-governance-token", - "symbol": "xdex", - "name": "XDEFI Governance Token" - }, - { "id": "xdoge", "symbol": "xdoge", "name": "Xdoge" }, - { "id": "xdollar", "symbol": "xdo", "name": "xDollar" }, - { - "id": "xdollar-interverse-money", - "symbol": "xim", - "name": "xDollar Interverse Money" - }, - { - "id": "xdollar-stablecoin", - "symbol": "xusd", - "name": "xDollar Stablecoin" - }, - { "id": "xeebster", "symbol": "xeeb", "name": "Xeebster" }, - { "id": "xels", "symbol": "xels", "name": "XELS" }, - { "id": "xend-finance", "symbol": "xend", "name": "Xend Finance" }, - { "id": "xenios", "symbol": "xnc", "name": "Xenios" }, - { "id": "xeniumx", "symbol": "xemx", "name": "Xeniumx" }, - { "id": "xenon-pay", "symbol": "x2p", "name": "Xenon Pay" }, - { "id": "xenon-pay-old", "symbol": "x2p", "name": "Xenon Pay [Old]" }, - { "id": "xenon-play", "symbol": "xplay", "name": "Xenon Play" }, - { "id": "xenophondao", "symbol": "xph", "name": "XenophonDAO" }, - { "id": "xeno-token", "symbol": "xno", "name": "Xeno Token" }, - { "id": "xenoverse-crypto", "symbol": "xenox", "name": "Xenoverse Crypto" }, - { "id": "xensa", "symbol": "xensa", "name": "Xensa" }, - { "id": "xensor", "symbol": "xsr", "name": "Xensor" }, - { "id": "xeonbit", "symbol": "xnb", "name": "Xeonbit" }, - { "id": "xeonbit-token", "symbol": "xns", "name": "Xeonbit Token" }, - { "id": "xeta-reality", "symbol": "xeta", "name": "Xeta Reality" }, - { "id": "xeus", "symbol": "xeus", "name": "XEUS" }, - { "id": "xfarmer", "symbol": "xf", "name": "xFarmer" }, - { "id": "xfinance", "symbol": "xfi", "name": "Xfinance" }, - { - "id": "xfinite-entertainment-token", - "symbol": "xet", - "name": "Xfinite Entertainment Token" - }, - { "id": "xfit", "symbol": "xfit", "name": "Xfit" }, - { "id": "xfund", "symbol": "xfund", "name": "xFund" }, - { "id": "xgold-coin", "symbol": "xgold", "name": "Xgold Coin" }, - { "id": "x-hash", "symbol": "xsh", "name": "X-HASH" }, - { "id": "xhashtag", "symbol": "xtag", "name": "xHashtag" }, - { "id": "xiamipool", "symbol": "xmpt", "name": "XiamiPool" }, - { "id": "xiasi-inu", "symbol": "xiasi", "name": "Xiasi Inu" }, - { "id": "xido-finance", "symbol": "xido", "name": "Xido Finance" }, - { "id": "xiglute-coin", "symbol": "xgc", "name": "Xiglute Coin" }, - { "id": "xigua-finance", "symbol": "xplus", "name": "Xigua Finance" }, - { "id": "xincha", "symbol": "xINCHa", "name": "xINCHa" }, - { "id": "xio", "symbol": "xio", "name": "Blockzero Labs" }, - { "id": "xion-finance", "symbol": "xgt", "name": "Xion Finance" }, - { "id": "xiotri", "symbol": "xiot", "name": "Xiotri" }, - { "id": "xircus", "symbol": "xircus", "name": "Xircus" }, - { "id": "xi-token", "symbol": "xi", "name": "Xi Token" }, - { "id": "xjewel", "symbol": "xjewel", "name": "xJEWEL" }, - { "id": "xl-moon", "symbol": "xlmn", "name": "XL-Moon" }, - { "id": "xlshiba", "symbol": "xlshiba", "name": "XLSHIBA" }, - { "id": "xmark", "symbol": "xmark", "name": "xMARK" }, - { "id": "xmax", "symbol": "xmx", "name": "XMax" }, - { "id": "xmetaversal", "symbol": "xmeta", "name": "XMetaversal" }, - { "id": "xmine", "symbol": "xmn", "name": "Xmine" }, - { "id": "xmon", "symbol": "xmon", "name": "XMON" }, - { "id": "xmooney", "symbol": "xM", "name": "xMooney" }, - { "id": "xnft", "symbol": "xnft", "name": "xNFT Protocol" }, - { "id": "xolo-inu", "symbol": "XL", "name": "Xolo Inu" }, - { "id": "xolo-metaverse", "symbol": "xolo", "name": "Xolo Metaverse" }, - { "id": "xov", "symbol": "xov", "name": "XOVBank" }, - { "id": "xp", "symbol": "xp", "name": "XP" }, - { "id": "xpad", "symbol": "xpad", "name": "xPAD" }, - { "id": "xpandacoin", "symbol": "xpc", "name": "XPandaCoin" }, - { "id": "xpansion-game", "symbol": "xps", "name": "Xpansion Game" }, - { "id": "xpendium", "symbol": "xpnd", "name": "Xpendium" }, - { "id": "xp-network", "symbol": "xpnet", "name": "XP Network" }, - { "id": "xpool", "symbol": "xpo", "name": "Xpool" }, - { "id": "xpose-protocol", "symbol": "xp", "name": "Xpose Protocol" }, - { "id": "x-power-chain", "symbol": "xpo", "name": "X-power Chain" }, - { "id": "x-protocol", "symbol": "pot", "name": "X Protocol" }, - { "id": "xptp", "symbol": "xptp", "name": "xPTP" }, - { "id": "xquake", "symbol": "xqk", "name": "XQuake" }, - { "id": "xrdoge", "symbol": "xrdoge", "name": "XRdoge" }, - { "id": "xre-global", "symbol": "xre", "name": "XRE Global" }, - { "id": "xriba", "symbol": "xra", "name": "Xriba" }, - { "id": "xroad", "symbol": "xri", "name": "XRoad" }, - { "id": "xrpalike-gene", "symbol": "xag", "name": "Xrpalike Gene" }, - { "id": "xrpaynet", "symbol": "xrpaynet", "name": "XRPayNet" }, - { "id": "xrp-bep2", "symbol": "xrp-bf2", "name": "XRP BEP2" }, - { "id": "xrp-classic", "symbol": "xrpc", "name": "XRP Classic" }, - { "id": "xr-shiba-inu", "symbol": "xrshib", "name": "XR Shiba Inu" }, - { "id": "xsgd", "symbol": "xsgd", "name": "XSGD" }, - { "id": "xsigma", "symbol": "sig", "name": "xSigma" }, - { "id": "xsl-labs", "symbol": "syl", "name": "XSL Labs" }, - { "id": "xstorage", "symbol": "xstx", "name": "XStorage" }, - { "id": "xsushi", "symbol": "xsushi", "name": "xSUSHI" }, - { "id": "xsuter", "symbol": "xsuter", "name": "xSuter" }, - { "id": "xswap", "symbol": "xsp", "name": "XSwap" }, - { "id": "xswap-protocol", "symbol": "xsp", "name": "XSwap Protocol" }, - { "id": "xtblock-token", "symbol": "xtt-b20", "name": "XTblock Token" }, - { "id": "xtcom-token", "symbol": "xt", "name": "XT.com Token" }, - { "id": "xtendcash", "symbol": "XTNC", "name": "XtendCash" }, - { "id": "xtime", "symbol": "xtm", "name": "XTime" }, - { "id": "xtoken", "symbol": "xtk", "name": "xToken" }, - { "id": "xtrabytes", "symbol": "xby", "name": "XTRABYTES" }, - { "id": "xtra-fund", "symbol": "xtra", "name": "Xtra Fund" }, - { "id": "xtra-token", "symbol": "xtra", "name": "XTRA Token" }, - { "id": "xtremcoin", "symbol": "xtr", "name": "Xtremcoin" }, - { "id": "xtrm", "symbol": "xtrm", "name": "XTRM" }, - { "id": "xuez", "symbol": "xuez", "name": "Xuez Coin" }, - { "id": "xusd", "symbol": "xusd", "name": "xUSD" }, - { "id": "xusd-token", "symbol": "XUSD", "name": "xUSD Token" }, - { "id": "xverse", "symbol": "xvc", "name": "Xverse" }, - { "id": "xwin-finance", "symbol": "xwin", "name": "xWIN Finance" }, - { "id": "x-world-games", "symbol": "xwg", "name": "X World Games" }, - { "id": "xx-platform", "symbol": "xxp", "name": "XX Platform" }, - { "id": "xy-finance", "symbol": "xy", "name": "XY Finance" }, - { "id": "xyo-network", "symbol": "xyo", "name": "XYO Network" }, - { "id": "xysl", "symbol": "xysl", "name": "xYSL" }, - { "id": "y-5-finance", "symbol": "y-5", "name": "Y-5 FINANCE" }, - { "id": "yaan-launchpad", "symbol": "yaan", "name": "Yaan Launchpad" }, - { "id": "yachtx", "symbol": "yachtx", "name": "YachtX" }, - { "id": "yacoin", "symbol": "yac", "name": "YACoin" }, - { "id": "yadacoin", "symbol": "yda", "name": "YadaCoin" }, - { "id": "yaki-gold", "symbol": "yag", "name": "Yaki Gold" }, - { "id": "yam-2", "symbol": "yam", "name": "YAM" }, - { "id": "yamp-finance", "symbol": "yamp", "name": "Yamp Finance" }, - { "id": "yam-v2", "symbol": "YAMv2", "name": "YAM v2" }, - { "id": "yandere-shiba", "symbol": "yansh", "name": "Yandere Shiba" }, - { "id": "yap-stone", "symbol": "yap", "name": "Yap Stone" }, - { "id": "yarloo", "symbol": "yarl", "name": "Yarloo" }, - { "id": "yas", "symbol": "yas", "name": "YAS" }, - { "id": "yasha-dao", "symbol": "yasha", "name": "Yasha Dao" }, - { "id": "yawww", "symbol": "yaw", "name": "Yawww" }, - { "id": "yaxis", "symbol": "yaxis", "name": "yAxis" }, - { "id": "yay-games", "symbol": "yay", "name": "YAY Games" }, - { "id": "yayo-coin", "symbol": "yayo", "name": "Yayo Coin" }, - { "id": "yayswap", "symbol": "yay", "name": "YaySwap" }, - { "id": "ycash", "symbol": "yec", "name": "Ycash" }, - { "id": "y-coin", "symbol": "yco", "name": "Y Coin" }, - { "id": "ydragon", "symbol": "ydr", "name": "YDragon" }, - { "id": "yeabrswap", "symbol": "sBEAR", "name": "yBEARSwap" }, - { "id": "yeafinance", "symbol": "yea", "name": "YeaFinance" }, - { "id": "yeager-inu", "symbol": "yeager", "name": "Yeager Inu" }, - { "id": "yearn-cash", "symbol": "yfic", "name": "Yearn Cash" }, - { - "id": "yearn-classic-finance", - "symbol": "earn", - "name": "Yearn Classic Finance" - }, - { - "id": "yearn-ecosystem-token-index", - "symbol": "yeti", - "name": "Yearn Ecosystem Token Index" - }, - { - "id": "yearn-ethereum-finance", - "symbol": "yefi", - "name": "Yearn Ethereum Finance" - }, - { "id": "yearn-finance", "symbol": "yfi", "name": "yearn.finance" }, - { "id": "yearn-finance-bit", "symbol": "yfbt", "name": "Yearn Finance Bit" }, - { - "id": "yearn-finance-bit2", - "symbol": "yfb2", - "name": "Yearn Finance Bit2" - }, - { - "id": "yearn-finance-diamond-token", - "symbol": "yfdt", - "name": "Yearn Finance Diamond Token" - }, - { "id": "yearn-finance-dot", "symbol": "yfdot", "name": "Yearn Finance DOT" }, - { - "id": "yearn-finance-infrastructure-labs", - "symbol": "ylab", - "name": "Yearn-finance Infrastructure Labs" - }, - { - "id": "yearn-finance-management", - "symbol": "yefim", - "name": "Yearn Finance Management" - }, - { - "id": "yearn-finance-network", - "symbol": "yfn", - "name": "Yearn Finance Network" - }, - { - "id": "yearn-finance-passive-income", - "symbol": "yfpi", - "name": "Yearn Finance Passive Income" - }, - { - "id": "yearn-finance-red-moon", - "symbol": "yfrm", - "name": "Yearn Finance Red Moon" - }, - { - "id": "yearn-finance-value", - "symbol": "yfiv", - "name": "Yearn Finance Value" - }, - { - "id": "yearn-finance-wormhole", - "symbol": "yfi", - "name": "yearn.finance (Wormhole)" - }, - { "id": "yearnlab", "symbol": "ylb", "name": "Yearnlab" }, - { "id": "yearn-lazy-ape", "symbol": "YLA", "name": "Yearn Lazy Ape" }, - { "id": "yearn-secure", "symbol": "ysec", "name": "Yearn Secure" }, - { "id": "yearrise", "symbol": "yrt", "name": "YearRise" }, - { "id": "yee", "symbol": "yee", "name": "Yee" }, - { "id": "yefi", "symbol": "yefi", "name": "YeFi" }, - { "id": "yeld-finance", "symbol": "yeld", "name": "Yeld Finance" }, - { "id": "yel-finance", "symbol": "yel", "name": "Yel.Finance" }, - { "id": "yellow-road", "symbol": "road", "name": "Yellow Road" }, - { - "id": "yeni-malatyaspor-token", - "symbol": "yms", - "name": "Yeni Malatyaspor Token" - }, - { "id": "yenten", "symbol": "ytn", "name": "YENTEN" }, - { "id": "yesorno", "symbol": "yon", "name": "YESorNO" }, - { "id": "yeticoin", "symbol": "yetic", "name": "YetiCoin" }, - { "id": "yeti-finance", "symbol": "yeti", "name": "Yeti Finance" }, - { "id": "yetiswap", "symbol": "yts", "name": "YetiSwap" }, - { "id": "yetucoin", "symbol": "yetu", "name": "Yetucoin" }, - { "id": "yfbeta", "symbol": "yfbeta", "name": "yfBeta" }, - { "id": "yfbitcoin", "symbol": "yfbtc", "name": "YFBitcoin" }, - { "id": "yfc", "symbol": "yfc", "name": "YFC" }, - { "id": "yfdai-finance", "symbol": "yf-dai", "name": "YfDAI.finance" }, - { - "id": "yfdfi-finance", - "symbol": "yfd", - "name": "Your Finance Decentralized" - }, - { "id": "yfe-money", "symbol": "YFE", "name": "YFE Money" }, - { "id": "yff-finance", "symbol": "yff", "name": "YFF.Finance" }, - { "id": "yffi-finance", "symbol": "yffi", "name": "yffi finance" }, - { "id": "yffii-finance", "symbol": "yffii", "name": "YFFII Finance" }, - { "id": "yffs", "symbol": "yffs", "name": "YFFS Finance" }, - { - "id": "yfibalancer-finance", - "symbol": "yfib", - "name": "YFIBALANCER.FINANCE" - }, - { - "id": "yfiexchange-finance", - "symbol": "yfie", - "name": "YFIEXCHANGE.FINANCE" - }, - { "id": "yfii-finance", "symbol": "yfii", "name": "DFI.money" }, - { "id": "yfii-gold", "symbol": "yfiig", "name": "YFII Gold" }, - { "id": "yfilend-finance", "symbol": "yfild", "name": "YFILEND.FINANCE" }, - { "id": "yfimobi", "symbol": "yfim", "name": "Yfi.mobi" }, - { "id": "yfione", "symbol": "yfo", "name": "YFIONE" }, - { "id": "yfiscurity", "symbol": "yfis", "name": "YFISCURITY" }, - { "id": "yfix-finance", "symbol": "yfix", "name": "YFIX.finance" }, - { "id": "yflink", "symbol": "yfl", "name": "YF Link" }, - { "id": "yfos-finance", "symbol": "YFOS", "name": "YFOS.finance" }, - { "id": "yfox-finance", "symbol": "yfox", "name": "YFOX Finance" }, - { "id": "yfpro-finance", "symbol": "yfpro", "name": "YFPRO Finance" }, - { "id": "yfscience", "symbol": "yfsi", "name": "Yfscience" }, - { "id": "yftether", "symbol": "yfte", "name": "YFTether" }, - { "id": "yfu-finance", "symbol": "YFU", "name": "yfu.finance" }, - { "id": "yfx", "symbol": "yfx", "name": "Your Futures Exchange" }, - { "id": "yi12-stfinance", "symbol": "yi12", "name": "Yield Stake Finance" }, - { "id": "yield-app", "symbol": "yld", "name": "YIELD App" }, - { "id": "yieldblox", "symbol": "ybx", "name": "YieldBlox" }, - { "id": "yield-goat", "symbol": "ygoat", "name": "Yield Goat" }, - { "id": "yield-guild-games", "symbol": "ygg", "name": "Yield Guild Games" }, - { - "id": "yield-guild-games-south-east-asia", - "symbol": "sea", - "name": "Yield Guild Games South East Asia" - }, - { - "id": "yield-guild-games-wormhole", - "symbol": "ygg", - "name": "Yield Guild Games (Wormhole)" - }, - { "id": "yield-hunt", "symbol": "gem", "name": "Yield Hunt" }, - { "id": "yieldly", "symbol": "yldy", "name": "Yieldly" }, - { - "id": "yield-optimization-platform", - "symbol": "yop", - "name": "Yield Optimization Platform \u0026 Protocol" - }, - { "id": "yield-parrot", "symbol": "lory", "name": "Yield Parrot" }, - { "id": "yield-protocol", "symbol": "yield", "name": "Yield Protocol" }, - { - "id": "yield-protocol-token", - "symbol": "yield", - "name": "Yield Protocol Token" - }, - { "id": "yieldwars-com", "symbol": "war", "name": "YieldWars" }, - { "id": "yieldwatch", "symbol": "watch", "name": "Yieldwatch" }, - { "id": "yield-yak", "symbol": "yak", "name": "Yield Yak" }, - { "id": "yinbi", "symbol": "yinbi", "name": "Yinbi" }, - { "id": "yin-finance", "symbol": "yin", "name": "YIN Finance" }, - { "id": "ymplepay", "symbol": "ympa", "name": "YmplePay" }, - { "id": "yobit-token", "symbol": "yo", "name": "Yobit Token" }, - { "id": "yocoin", "symbol": "yoc", "name": "Yocoin" }, - { "id": "yocoinyoco", "symbol": "yoco", "name": "YocoinYOCO" }, - { "id": "yohero", "symbol": "yo", "name": "YoHero" }, - { "id": "yohero-yhc", "symbol": "yhc", "name": "YoHero (YHC)" }, - { "id": "yoi-shiba-inu", "symbol": "YOSI", "name": "Yoi Shiba Inu" }, - { "id": "yokai-money", "symbol": "yokai", "name": "Yokai Money" }, - { "id": "yokai-network", "symbol": "yokai", "name": "Yokai Network" }, - { "id": "yokcoin", "symbol": "yok", "name": "YOKcoin" }, - { "id": "yolo-cash", "symbol": "ylc", "name": "YOLOCash" }, - { "id": "yoloverse", "symbol": "yolov", "name": "YoloVerse" }, - { "id": "yooshi", "symbol": "yooshi", "name": "YooShi" }, - { "id": "yooshiba-inu", "symbol": "YSHIBAINU", "name": "Yooshiba Inu" }, - { "id": "yoplex", "symbol": "yplx", "name": "Yoplex" }, - { "id": "yoshi-exchange", "symbol": "yoshi", "name": "Yoshi.exchange" }, - { "id": "youcash", "symbol": "youc", "name": "YOUcash" }, - { "id": "you-chain", "symbol": "you", "name": "YOU Chain" }, - { "id": "youclout", "symbol": "yct", "name": "Youclout" }, - { "id": "youlive-coin", "symbol": "uc", "name": "YouLive Coin" }, - { "id": "youminter", "symbol": "umint", "name": "YouMinter" }, - { - "id": "young-boys-fan-token", - "symbol": "ybo", - "name": "Young Boys Fan Token" - }, - { "id": "your", "symbol": "your", "name": "Your" }, - { - "id": "your-open-metaverse", - "symbol": "yom", - "name": "Your Open Metaverse" - }, - { "id": "youswap", "symbol": "you", "name": "YouSwap" }, - { "id": "youves-uusd", "symbol": "uusd", "name": "Youves uUSD" }, - { - "id": "youves-you-governance", - "symbol": "you", - "name": "Youves YOU Governance" - }, - { "id": "yoyow", "symbol": "yoyow", "name": "YOYOW" }, - { "id": "yrise-finance", "symbol": "yrise", "name": "yRise Finance" }, - { "id": "ysl", "symbol": "ysl", "name": "YSL" }, - { "id": "ysl-io", "symbol": "sysl", "name": "YSL.IO" }, - { "id": "ysoy-chain", "symbol": "ysoy", "name": "YSOY Chain" }, - { "id": "ystar", "symbol": "ysr", "name": "Ystar" }, - { "id": "ytofu", "symbol": "ytofu", "name": "yTOFU" }, - { "id": "ytsla-finance", "symbol": "ytsla", "name": "yTSLA Finance" }, - { "id": "ytv-coin", "symbol": "ytv", "name": "YTV Coin" }, - { "id": "yuan-chain-coin", "symbol": "ycc", "name": "Yuan Chain Coin" }, - { "id": "yucreat", "symbol": "yuct", "name": "Yucreat" }, - { "id": "yugi", "symbol": "yugi", "name": "Yugi" }, - { "id": "yu-gi-eth", "symbol": "yge", "name": "Yu-Gi-Eth!" }, - { "id": "yukon", "symbol": "yukon", "name": "Yukon" }, - { "id": "yummy", "symbol": "yummy", "name": "Yummy" }, - { "id": "yunex", "symbol": "yun", "name": "YunEx Yun Token" }, - { "id": "yup", "symbol": "yup", "name": "Yup" }, - { "id": "yusd-stablecoin", "symbol": "yusd", "name": "YUSD Stablecoin" }, - { "id": "yusra", "symbol": "yusra", "name": "YUSRA" }, - { "id": "yuzuswap", "symbol": "yuzu", "name": "YuzuSwap" }, - { "id": "yvault-lp-ycurve", "symbol": "yvault-lp-ycurve", "name": "yUSD" }, - { - "id": "yvboost", - "symbol": "yvboost", - "name": "Yearn Compounding veCRV yVault" - }, - { "id": "yvs-finance", "symbol": "yvs", "name": "YVS Finance" }, - { "id": "yye-energy", "symbol": "yye", "name": "YYE Energy" }, - { "id": "zabaku-inu", "symbol": "zabaku", "name": "Zabaku Inu" }, - { "id": "zabu-token", "symbol": "zabu", "name": "Zabu Token" }, - { "id": "zac", "symbol": "zac", "name": "ZAC" }, - { "id": "zada", "symbol": "zada", "name": "Zada" }, - { "id": "zafira", "symbol": "zfai", "name": "Zafira" }, - { "id": "zagent-gem", "symbol": "zeg", "name": "Zagent Gem" }, - { "id": "zaif-token", "symbol": "zaif", "name": "Zaif Token" }, - { "id": "zaigar-finance", "symbol": "zaif", "name": "Zaigar Finance" }, - { "id": "zam-io", "symbol": "zam", "name": "Zam.io" }, - { "id": "zamzam", "symbol": "zamzam", "name": "ZAMZAM" }, - { "id": "zano", "symbol": "zano", "name": "Zano" }, - { "id": "zantepay", "symbol": "zantepay", "name": "Zantepay" }, - { "id": "zap", "symbol": "zap", "name": "Zap" }, - { "id": "zappy", "symbol": "zap", "name": "Zappy" }, - { "id": "zarcash", "symbol": "zarh", "name": "Zarhexcash" }, - { "id": "zasset-zusd", "symbol": "zusd", "name": "Zasset zUSD" }, - { "id": "zatcoin", "symbol": "zatcoin", "name": "ZatCoin" }, - { "id": "zb-token", "symbol": "zb", "name": "ZB Token" }, - { "id": "zcash", "symbol": "zec", "name": "Zcash" }, - { "id": "zccoin", "symbol": "zcc", "name": "ZcCoin" }, - { "id": "zclassic", "symbol": "zcl", "name": "Zclassic" }, - { "id": "zcoin", "symbol": "firo", "name": "Firo" }, - { "id": "zcon-protocol", "symbol": "zcon", "name": "Zcon Protocol" }, - { "id": "zcore", "symbol": "zcr", "name": "ZCore" }, - { "id": "zcore-finance", "symbol": "zefi", "name": "ZCore Finance" }, - { "id": "zealium", "symbol": "nzl", "name": "Zealium" }, - { "id": "zebec-protocol", "symbol": "zbc", "name": "Zebec Protocol" }, - { "id": "zebi", "symbol": "zco", "name": "Zebi" }, - { "id": "zedxion", "symbol": "zedxion", "name": "Zedxion" }, - { "id": "zeedex", "symbol": "zdex", "name": "Zeedex" }, - { "id": "zeepin", "symbol": "zpt", "name": "Zeepin" }, - { "id": "zeitcoin", "symbol": "zeit", "name": "Zeitcoin" }, - { "id": "zelaapayae", "symbol": "zpae", "name": "ZelaaPayAE" }, - { "id": "zelcash", "symbol": "flux", "name": "Flux" }, - { "id": "zelda-inu", "symbol": "zlda", "name": "Zelda Inu" }, - { "id": "zeloop-eco-reward", "symbol": "erw", "name": "ZeLoop Eco Reward" }, - { "id": "zelwin", "symbol": "zlw", "name": "Zelwin" }, - { "id": "zenad", "symbol": "znd", "name": "Zenad" }, - { "id": "zencash", "symbol": "zen", "name": "Horizen" }, - { "id": "zenc-coin", "symbol": "zenc", "name": "Zenc Coin" }, - { "id": "zenfi-ai", "symbol": "zenfi", "name": "ZenFi AI" }, - { "id": "zenfuse", "symbol": "zefu", "name": "Zenfuse" }, - { "id": "zenith-chain", "symbol": "zenith", "name": "Zenith Chain" }, - { "id": "zenith-token", "symbol": "zenx", "name": "Zenith Token" }, - { - "id": "zenlink-network-token", - "symbol": "zlk", - "name": "Zenlink Network Token" - }, - { "id": "zennies", "symbol": "zeni", "name": "Zennies" }, - { "id": "zeno-inu", "symbol": "zeno", "name": "Zeno Inu" }, - { "id": "zenon", "symbol": "znn", "name": "Zenon" }, - { "id": "zensports", "symbol": "sports", "name": "ZenSports" }, - { - "id": "zenswap-network-token", - "symbol": "znt", - "name": "Zenswap Network Token" - }, - { "id": "zent-cash", "symbol": "ztc", "name": "Zent Cash" }, - { "id": "zenzo", "symbol": "znz", "name": "ZENZO" }, - { "id": "zeon", "symbol": "zeon", "name": "ZEON Network" }, - { "id": "zeos", "symbol": "zeos", "name": "ZEOS" }, - { "id": "zeptagram", "symbol": "zptc", "name": "Zeptacoin" }, - { "id": "zer-dex", "symbol": "zdx", "name": "Zer-Dex" }, - { "id": "zero", "symbol": "zer", "name": "Zero" }, - { "id": "zero-exchange", "symbol": "zero", "name": "0.exchange" }, - { "id": "zerogoki", "symbol": "rei", "name": "Zerogoki" }, - { "id": "zerohybrid", "symbol": "zht", "name": "ZeroHybrid Network" }, - { "id": "zeronauts", "symbol": "zns", "name": "Zeronauts" }, - { "id": "zeroswap", "symbol": "zee", "name": "ZeroSwap" }, - { "id": "zero-tech", "symbol": "zero", "name": "Zero Tech" }, - { "id": "zerotwohm", "symbol": "z2o", "name": "ZeroTwOhm" }, - { "id": "zerozed", "symbol": "x0z", "name": "Zerozed" }, - { "id": "zetacoin", "symbol": "zet", "name": "Zetacoin" }, - { "id": "zeto", "symbol": "ztc", "name": "ZeTo" }, - { - "id": "zetta-bitcoin-hashrate-token", - "symbol": "zbtc", - "name": "Zetta Bitcoin Hashrate Token" - }, - { "id": "zeus10000", "symbol": "zeus10000", "name": "ZEUS10000" }, - { "id": "zeus-node-finance", "symbol": "zeus", "name": "Zeus Node Finance" }, - { "id": "zeusshield", "symbol": "zsc", "name": "Zeusshield" }, - { "id": "zeuxcoin", "symbol": "zuc", "name": "ZeuxCoin" }, - { "id": "zfarm", "symbol": "zfarm", "name": "ZFarm" }, - { "id": "zignaly", "symbol": "zig", "name": "Zignaly" }, - { "id": "zigzag", "symbol": "zag", "name": "ZigZag" }, - { "id": "zik-token", "symbol": "zik", "name": "Ziktalk" }, - { "id": "zilchess", "symbol": "zch", "name": "ZilChess" }, - { "id": "zilla", "symbol": "zla", "name": "Zilla" }, - { "id": "zilla-dao", "symbol": "zd", "name": "Zilla DAO" }, - { "id": "zilla-finance-2-0", "symbol": "zilla", "name": "Zilla Finance 2.0" }, - { "id": "zillamatrix", "symbol": "zmax", "name": "ZillaMatrix" }, - { "id": "zilla-shares", "symbol": "zshare", "name": "Zilla Shares" }, - { "id": "zilla-shares-2-0", "symbol": "zshare", "name": "Zilla Shares 2.0" }, - { "id": "zillioncoin", "symbol": "zln", "name": "ZillionCoin" }, - { "id": "zilliqa", "symbol": "zil", "name": "Zilliqa" }, - { "id": "zilpay-wallet", "symbol": "zlp", "name": "ZilPay Wallet" }, - { "id": "zilstream", "symbol": "stream", "name": "ZilStream" }, - { "id": "zilsurvey", "symbol": "srv", "name": "zilSurvey" }, - { "id": "zilswap", "symbol": "zwap", "name": "ZilSwap" }, - { "id": "zilwall", "symbol": "zwall", "name": "ZilWall" }, - { "id": "zilwall-paint", "symbol": "zpaint", "name": "ZilWall Paint" }, - { "id": "zimbocash", "symbol": "zash", "name": "ZIMBOCASH" }, - { "id": "zin", "symbol": "Zin", "name": "Zin" }, - { "id": "zinari", "symbol": "zina", "name": "ZINARI" }, - { "id": "zinja", "symbol": "z", "name": "Zinja" }, - { "id": "zion", "symbol": "zion", "name": "Zion" }, - { "id": "zioncoin", "symbol": "znc", "name": "ZionCoin" }, - { "id": "zionomics", "symbol": "ZIOX", "name": "Zionomics" }, - { "id": "ziot", "symbol": "ziot", "name": "Ziot" }, - { "id": "zip", "symbol": "zip", "name": "Zipper Network" }, - { "id": "zipmex-token", "symbol": "zmt", "name": "Zipmex Token" }, - { "id": "zippie", "symbol": "zipt", "name": "Zippie" }, - { "id": "zipswap", "symbol": "zip", "name": "ZipSwap" }, - { "id": "zirve-coin", "symbol": "zirve", "name": "Zirve Coin" }, - { "id": "ziticoin", "symbol": "ziti", "name": "Ziticoin" }, - { - "id": "zjlt-distributed-factoring-network", - "symbol": "zjlt", - "name": "ZJLT Distributed Factoring Network" - }, - { "id": "zkspace", "symbol": "zks", "name": "ZKSpace" }, - { "id": "zktube", "symbol": "zkt", "name": "zkTube" }, - { "id": "zlot", "symbol": "zlot", "name": "zLOT" }, - { "id": "zmine", "symbol": "zmn", "name": "ZMINE" }, - { "id": "zodiac", "symbol": "zdc", "name": "Zodiac" }, - { "id": "zodiacdao", "symbol": "zd", "name": "ZodiacDAO" }, - { "id": "zodiacs", "symbol": "zdc", "name": "Zodiacs" }, - { "id": "zodiacsv2", "symbol": "zdcv2", "name": "ZodiacsV2" }, - { "id": "zodium", "symbol": "zodi", "name": "Zodium" }, - { "id": "zoe-cash", "symbol": "zoe", "name": "Zoe Cash" }, - { "id": "zoid-pay", "symbol": "zpay", "name": "ZoidPay" }, - { "id": "zoints", "symbol": "zee", "name": "Zoints" }, - { "id": "zombiecake", "symbol": "zc", "name": "ZombieCake" }, - { "id": "zombie-inu", "symbol": "zinu", "name": "Zombie Inu" }, - { "id": "zombie-rising", "symbol": "zomb", "name": "Zombie Rising" }, - { - "id": "zombie-skull-games", - "symbol": "zskull", - "name": "Zombie Skull Games" - }, - { "id": "zombie-world-z", "symbol": "zwz", "name": "Zombie World Z" }, - { "id": "zomfi", "symbol": "zomfi", "name": "Zomfi" }, - { "id": "zomi", "symbol": "zomi", "name": "ZOMI" }, - { "id": "zone", "symbol": "zone", "name": "Zone" }, - { "id": "zonecoin", "symbol": "zne", "name": "Zonecoin" }, - { "id": "zoobit", "symbol": "zbt", "name": "Zoobit" }, - { "id": "zoo-coin", "symbol": "zoo", "name": "ZooCoin" }, - { "id": "zoo-crypto-world", "symbol": "zoo", "name": "ZOO Crypto World" }, - { "id": "zoodao", "symbol": "zoo", "name": "ZooDAO" }, - { "id": "zookeeper", "symbol": "zoo", "name": "ZooKeeper" }, - { "id": "zoo-labs", "symbol": "zoo", "name": "Zoo Labs" }, - { "id": "zoomswap", "symbol": "zm", "name": "ZoomSwap" }, - { "id": "zooshi", "symbol": "zooshi", "name": "Zooshi" }, - { "id": "zoo-token", "symbol": "zoot", "name": "Zoo Token" }, - { "id": "zoracles", "symbol": "zora", "name": "Zoracles" }, - { "id": "zoro-inu", "symbol": "zoro", "name": "Zoro Inu" }, - { "id": "zort", "symbol": "zort", "name": "Zort" }, - { "id": "zper", "symbol": "zpr", "name": "ZPER" }, - { "id": "zrcoin", "symbol": "zrc", "name": "ZrCoin" }, - { "id": "zro", "symbol": "ZRO", "name": "Carb0n.fi" }, - { "id": "zrocor", "symbol": "zcor", "name": "Zrocor" }, - { "id": "ztcoin", "symbol": "zt", "name": "ZBG Token" }, - { "id": "zuescrowdfunding", "symbol": "zeus", "name": "ZeusNetwork" }, - { "id": "zufinance", "symbol": "zuf", "name": "ZuFinance" }, - { "id": "zug", "symbol": "zug", "name": "EtherOrcs" }, - { "id": "zuki", "symbol": "zuki", "name": "Zuki" }, - { "id": "zuki-moba", "symbol": "zuki", "name": "Zuki Moba" }, - { "id": "zumcoin", "symbol": "zum", "name": "ZumCoin" }, - { "id": "zum-token", "symbol": "zum", "name": "ZUM TOKEN" }, - { "id": "zuna", "symbol": "zuna", "name": "Zuna" }, - { "id": "zupi-coin", "symbol": "zupi", "name": "Zupi Coin" }, - { "id": "zuplo", "symbol": "zlp", "name": "Zuplo" }, - { "id": "zurrency", "symbol": "zurr", "name": "ZURRENCY" }, - { "id": "zusd", "symbol": "zusd", "name": "ZUSD" }, - { "id": "zuz-protocol", "symbol": "zuz", "name": "ZUZ Protocol" }, - { "id": "zynecoin", "symbol": "zyn", "name": "Zynecoin" }, - { "id": "zyro", "symbol": "zyro", "name": "Zyro" }, - { "id": "zyrri", "symbol": "zyr", "name": "Zyrri" }, - { "id": "zytara-dollar", "symbol": "zusd", "name": "Zytara Dollar" }, - { "id": "zyx", "symbol": "zyx", "name": "ZYX" }, - { "id": "zzz-finance-v2", "symbol": "zzzv2", "name": "zzz.finance v2" } -] diff --git a/openbb_terminal/cryptocurrency/data/coinpaprika_coins.json b/openbb_terminal/cryptocurrency/data/coinpaprika_coins.json deleted file mode 100644 index b92f1048f1d..00000000000 --- a/openbb_terminal/cryptocurrency/data/coinpaprika_coins.json +++ /dev/null @@ -1,120449 +0,0 @@ -[ - { - "id": "btc-bitcoin", - "name": "Bitcoin", - "symbol": "BTC", - "rank": 1, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "eth-ethereum", - "name": "Ethereum", - "symbol": "ETH", - "rank": 2, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hex-hex", - "name": "HEX", - "symbol": "HEX", - "rank": 3, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnb-binance-coin", - "name": "Binance Coin", - "symbol": "BNB", - "rank": 4, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "usdt-tether", - "name": "Tether", - "symbol": "USDT", - "rank": 5, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sol-solana", - "name": "Solana", - "symbol": "SOL", - "rank": 6, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ada-cardano", - "name": "Cardano", - "symbol": "ADA", - "rank": 7, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "usdc-usd-coin", - "name": "USD Coin", - "symbol": "USDC", - "rank": 8, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xrp-xrp", - "name": "XRP", - "symbol": "XRP", - "rank": 9, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "luna-terra", - "name": "Terra", - "symbol": "LUNA", - "rank": 10, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "avax-avalanche", - "name": "Avalanche", - "symbol": "AVAX", - "rank": 11, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dot-polkadot", - "name": "Polkadot", - "symbol": "DOT", - "rank": 12, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "doge-dogecoin", - "name": "Dogecoin", - "symbol": "DOGE", - "rank": 13, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "matic-polygon", - "name": "Polygon", - "symbol": "MATIC", - "rank": 14, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shib-shiba-inu", - "name": "Shiba Inu", - "symbol": "SHIB", - "rank": 15, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "busd-binance-usd", - "name": "Binance USD", - "symbol": "BUSD", - "rank": 16, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wbtc-wrapped-bitcoin", - "name": "Wrapped Bitcoin", - "symbol": "WBTC", - "rank": 17, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "algo-algorand", - "name": "Algorand", - "symbol": "ALGO", - "rank": 18, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ltc-litecoin", - "name": "Litecoin", - "symbol": "LTC", - "rank": 19, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "uni-uniswap", - "name": "Uniswap", - "symbol": "UNI", - "rank": 20, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ust-terrausd", - "name": "TerraUSD", - "symbol": "UST", - "rank": 21, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cro-cryptocom-chain", - "name": "Crypto.com Coin", - "symbol": "CRO", - "rank": 22, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "near-near-protocol", - "name": "Near Protocol", - "symbol": "NEAR", - "rank": 23, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "atom-cosmos", - "name": "Cosmos", - "symbol": "ATOM", - "rank": 24, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bch-bitcoin-cash", - "name": "Bitcoin Cash", - "symbol": "BCH", - "rank": 25, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "okb-okb", - "name": "OKB", - "symbol": "OKB", - "rank": 26, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trx-tron", - "name": "TRON", - "symbol": "TRX", - "rank": 27, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "link-chainlink", - "name": "Chainlink", - "symbol": "LINK", - "rank": 28, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xlm-stellar", - "name": "Stellar", - "symbol": "XLM", - "rank": 29, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dai-dai", - "name": "Dai", - "symbol": "DAI", - "rank": 30, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mana-decentraland", - "name": "Decentraland", - "symbol": "MANA", - "rank": 31, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axs-axie-infinity", - "name": "Axie Infinity", - "symbol": "AXS", - "rank": 32, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ftt-ftx-token", - "name": "FTX Token", - "symbol": "FTT", - "rank": 33, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vet-vechain", - "name": "VeChain", - "symbol": "VET", - "rank": 34, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hbar-hedera-hashgraph", - "name": "Hedera Hashgraph", - "symbol": "HBAR", - "rank": 35, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ftm-fantom", - "name": "Fantom", - "symbol": "FTM", - "rank": 36, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sand-the-sandbox", - "name": "The Sandbox", - "symbol": "SAND", - "rank": 37, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fil-filecoin", - "name": "Filecoin", - "symbol": "FIL", - "rank": 38, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "theta-theta-token", - "name": "THETA", - "symbol": "THETA", - "rank": 39, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "icp-internet-computer", - "name": "Internet Computer", - "symbol": "ICP", - "rank": 40, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "egld-elrond", - "name": "Elrond", - "symbol": "EGLD", - "rank": 41, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "etc-ethereum-classic", - "name": "Ethereum Classic", - "symbol": "ETC", - "rank": 42, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "miota-iota", - "name": "IOTA", - "symbol": "MIOTA", - "rank": 43, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xmr-monero", - "name": "Monero", - "symbol": "XMR", - "rank": 44, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hnt-helium", - "name": "Helium", - "symbol": "HNT", - "rank": 45, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xtz-tezos", - "name": "Tezos", - "symbol": "XTZ", - "rank": 46, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cake-pancakeswap", - "name": "PancakeSwap", - "symbol": "CAKE", - "rank": 47, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "leo-leo-token", - "name": "LEO Token", - "symbol": "LEO", - "rank": 48, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcb-binance-bitcoin", - "name": "Binance Bitcoin", - "symbol": "BTCB", - "rank": 49, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "klay-klaytn", - "name": "Klaytn", - "symbol": "KLAY", - "rank": 50, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aave-new", - "name": "Aave", - "symbol": "AAVE", - "rank": 51, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gala-gala", - "name": "Gala", - "symbol": "GALA", - "rank": 52, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grt-the-graph", - "name": "The Graph", - "symbol": "GRT", - "rank": 53, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eos-eos", - "name": "EOS", - "symbol": "EOS", - "rank": 54, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "stx-stacks", - "name": "Stacks", - "symbol": "STX", - "rank": 55, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "flow-flow", - "name": "Flow", - "symbol": "FLOW", - "rank": 56, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btt-bittorrent", - "name": "BitTorrent", - "symbol": "BTT", - "rank": 57, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lrc-loopring", - "name": "Loopring", - "symbol": "LRC", - "rank": 58, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "one-harmony", - "name": "Harmony", - "symbol": "ONE", - "rank": 59, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ksm-kusama", - "name": "Kusama", - "symbol": "KSM", - "rank": 60, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mkr-maker", - "name": "Maker", - "symbol": "MKR", - "rank": 61, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsv-bitcoin-sv", - "name": "Bitcoin SV", - "symbol": "BSV", - "rank": 62, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "enj-enjin-coin", - "name": "Enjin Coin", - "symbol": "ENJ", - "rank": 63, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crv-curve-dao-token", - "name": "Curve DAO Token", - "symbol": "CRV", - "rank": 64, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ht-huobi-token", - "name": "Huobi Token", - "symbol": "HT", - "rank": 65, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qnt-quant", - "name": "Quant", - "symbol": "QNT", - "rank": 66, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kda-kadena", - "name": "Kadena", - "symbol": "KDA", - "rank": 67, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xec-ecash", - "name": "eCash", - "symbol": "XEC", - "rank": 68, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zec-zcash", - "name": "Zcash", - "symbol": "ZEC", - "rank": 69, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cel-celsius", - "name": "Celsius", - "symbol": "CEL", - "rank": 70, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cvx-convex-finance", - "name": "Convex Finance", - "symbol": "CVX", - "rank": 71, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hbtc-huobi-btc", - "name": "Huobi BTC", - "symbol": "HBTC", - "rank": 72, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ar-arweave", - "name": "Arweave", - "symbol": "AR", - "rank": 73, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "neo-neo", - "name": "NEO", - "symbol": "NEO", - "rank": 74, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bat-basic-attention-token", - "name": "Basic Attention Token", - "symbol": "BAT", - "rank": 75, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rune-thorchain", - "name": "THORChain", - "symbol": "RUNE", - "rank": 76, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "celo-celo", - "name": "Celo", - "symbol": "CELO", - "rank": 77, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kcs-kucoin-token", - "name": "KuCoin Token", - "symbol": "KCS", - "rank": 78, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phb-red-pulse-phoenix", - "name": "Phoenix Global", - "symbol": "PHB", - "rank": 79, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frax-frax", - "name": "Frax", - "symbol": "FRAX", - "rank": 80, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "waves-waves", - "name": "Waves", - "symbol": "WAVES", - "rank": 81, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "thr-thorecoin", - "name": "ThoreCoin", - "symbol": "THR", - "rank": 82, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rose-oasis-network", - "name": "Oasis Network", - "symbol": "ROSE", - "rank": 83, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "youc-youcash", - "name": "YOUcash", - "symbol": "YOUC", - "rank": 84, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dash-dash", - "name": "Dash", - "symbol": "DASH", - "rank": 85, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "chz-chiliz", - "name": "Chiliz", - "symbol": "CHZ", - "rank": 86, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nexo-nexo", - "name": "Nexo", - "symbol": "NEXO", - "rank": 87, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hot-holo", - "name": "Holo", - "symbol": "HOT", - "rank": 88, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctc-creditcoin", - "name": "Creditcoin", - "symbol": "CTC", - "rank": 89, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfi-defi-chain", - "name": "DeFi Chain", - "symbol": "DFI", - "rank": 90, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tusd-trueusd", - "name": "TrueUSD", - "symbol": "TUSD", - "rank": 91, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mina-mina-protocol", - "name": "Mina Protocol", - "symbol": "MINA", - "rank": 92, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sushi-sushi", - "name": "Sushi", - "symbol": "SUSHI", - "rank": 93, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iotx-iotex", - "name": "IoTeX", - "symbol": "IOTX", - "rank": 94, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdc-xdc", - "name": "XDC", - "symbol": "XDC", - "rank": 95, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xem-nem", - "name": "NEM", - "symbol": "XEM", - "rank": 96, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bit-bitdao", - "name": "BitDAO", - "symbol": "BIT", - "rank": 97, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xym-symbol", - "name": "Symbol", - "symbol": "XYM", - "rank": 98, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "yfi-yearnfinance", - "name": "yearn.finance", - "symbol": "YFI", - "rank": 99, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1inch-1inch", - "name": "1INCH", - "symbol": "1INCH", - "rank": 100, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tfuel-theta-fuel", - "name": "Theta Fuel", - "symbol": "TFUEL", - "rank": 101, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "omi-ecomi", - "name": "Ecomi", - "symbol": "OMI", - "rank": 102, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvn-ravencoin", - "name": "Ravencoin", - "symbol": "RVN", - "rank": 103, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ln-link", - "name": "LINK", - "symbol": "LN", - "rank": 104, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zil-zilliqa", - "name": "Zilliqa", - "symbol": "ZIL", - "rank": 105, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dcr-decred", - "name": "Decred", - "symbol": "DCR", - "rank": 106, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "usdp-paxos-standard-token", - "name": "Paxos Standard", - "symbol": "USDP", - "rank": 107, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lend-ethlend", - "name": "Aave", - "symbol": "LEND", - "rank": 108, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rndr-render-token", - "name": "Render Token", - "symbol": "RNDR", - "rank": 109, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qtum-qtum", - "name": "Qtum", - "symbol": "QTUM", - "rank": 110, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "audio-audius", - "name": "Audius", - "symbol": "AUDIO", - "rank": 111, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lpt-livepeer", - "name": "Livepeer", - "symbol": "LPT", - "rank": 112, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "omg-omg-network", - "name": "OMG Network", - "symbol": "OMG", - "rank": 113, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wax-wax", - "name": "WAX", - "symbol": "WAXP", - "rank": 114, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ethos-ethos", - "name": "Voyager Token", - "symbol": "VGX", - "rank": 115, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bora-bora", - "name": "BORA", - "symbol": "BORA", - "rank": 116, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ens-ethereum-name-service", - "name": "Ethereum Name Service", - "symbol": "ENS", - "rank": 117, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gno-gnosis", - "name": "Gnosis", - "symbol": "GNO", - "rank": 118, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "renbtc-renbtc", - "name": "renBTC", - "symbol": "renBTC", - "rank": 119, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rev-revain", - "name": "Revain", - "symbol": "REV", - "rank": 120, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elon-dogelon-mars", - "name": "Dogelon Mars", - "symbol": "ELON", - "rank": 121, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nft-apenft", - "name": "APENFT", - "symbol": "NFT", - "rank": 122, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sc-siacoin", - "name": "Siacoin", - "symbol": "SC", - "rank": 123, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bnt-bancor", - "name": "Bancor", - "symbol": "BNT", - "rank": 124, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scrt-secret", - "name": "Secret", - "symbol": "SCRT", - "rank": 125, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "safemoon-safemoon", - "name": "SafeMoon", - "symbol": "SAFEMOON", - "rank": 126, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zen-horizen", - "name": "Horizen", - "symbol": "ZEN", - "rank": 127, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btg-bitcoin-gold", - "name": "Bitcoin Gold", - "symbol": "BTG", - "rank": 128, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lusd-liquity-usd", - "name": "Liquity USD", - "symbol": "LUSD", - "rank": 129, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tel-telcoin", - "name": "Telcoin", - "symbol": "TEL", - "rank": 130, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zrx-0x", - "name": "0x", - "symbol": "ZRX", - "rank": 131, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "perp-perpetual-protocol", - "name": "Perpetual Protocol", - "symbol": "PERP", - "rank": 132, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ilv-illuvium", - "name": "Illuvium", - "symbol": "ILV", - "rank": 133, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "comp-compoundd", - "name": "Compound", - "symbol": "COMP", - "rank": 134, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ckb-nervos-network", - "name": "Nervos Network", - "symbol": "CKB", - "rank": 135, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "snx-synthetix-network-token", - "name": "Synthetix Network Token", - "symbol": "SNX", - "rank": 136, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icx-icon", - "name": "ICON", - "symbol": "ICX", - "rank": 137, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sys-syscoin", - "name": "Syscoin", - "symbol": "SYS", - "rank": 138, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "storj-storj", - "name": "Storj", - "symbol": "STORJ", - "rank": 139, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uma-uma", - "name": "UMA", - "symbol": "UMA", - "rank": 140, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chsb-swissborg", - "name": "SwissBorg", - "symbol": "CHSB", - "rank": 141, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ont-ontology", - "name": "Ontology", - "symbol": "ONT", - "rank": 142, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ttt-the-transfer-token", - "name": "The Transfer Token", - "symbol": "TTT", - "rank": 143, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iost-iost", - "name": "IOST", - "symbol": "IOST", - "rank": 144, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hive-hive", - "name": "HIVE", - "symbol": "HIVE", - "rank": 145, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "raca-radio-caca", - "name": "Radio Caca", - "symbol": "RACA", - "rank": 146, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ren-republic-protocol", - "name": "Ren", - "symbol": "REN", - "rank": 147, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kava-kava", - "name": "Kava", - "symbol": "KAVA", - "rank": 148, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "usdn-neutrino-usd", - "name": "Neutrino USD", - "symbol": "USDN", - "rank": 149, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nu-nucypher", - "name": "NuCypher", - "symbol": "NU", - "rank": 150, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ocean-ocean-protocol", - "name": "Ocean Protocol", - "symbol": "OCEAN", - "rank": 151, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ray-raydium", - "name": "Raydium", - "symbol": "RAY", - "rank": 152, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "woo-wootrade", - "name": "Wootrade", - "symbol": "WOO", - "rank": 153, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rdd-reddcoin", - "name": "ReddCoin", - "symbol": "RDD", - "rank": 154, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gt-gatechain-token", - "name": "Gatechain Token", - "symbol": "GT", - "rank": 155, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dgb-digibyte", - "name": "DigiByte", - "symbol": "DGB", - "rank": 156, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fxs-frax-share", - "name": "Frax Share", - "symbol": "FXS", - "rank": 157, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amp-amp", - "name": "Amp", - "symbol": "AMP", - "rank": 158, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "movr-moonriver", - "name": "Moonriver", - "symbol": "MOVR", - "rank": 159, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zel-zelcash", - "name": "FLUX", - "symbol": "FLUX", - "rank": 160, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nano-nano", - "name": "Nano", - "symbol": "NANO", - "rank": 161, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "srm-serum", - "name": "Serum", - "symbol": "SRM", - "rank": 162, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tribe-tribe", - "name": "Tribe", - "symbol": "TRIBE", - "rank": 163, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "c98-coin98", - "name": "Coin98", - "symbol": "C98", - "rank": 164, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "super-superfarm", - "name": "SuperFarm", - "symbol": "SUPER", - "rank": 165, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ant-aragon", - "name": "Aragon", - "symbol": "ANT", - "rank": 166, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "celr-celer-network", - "name": "Celer Network", - "symbol": "CELR", - "rank": 167, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ceek-ceek-vr", - "name": "CEEK VR", - "symbol": "CEEK", - "rank": 168, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "win-winklink", - "name": "WINkLink", - "symbol": "WIN", - "rank": 169, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbox-mobox", - "name": "Mobox", - "symbol": "MBOX", - "rank": 170, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blok-bloktopia", - "name": "Bloktopia", - "symbol": "BLOK", - "rank": 171, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poly-polymath", - "name": "Polymath", - "symbol": "POLY", - "rank": 172, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trac-origintrail", - "name": "OriginTrail", - "symbol": "TRAC", - "rank": 173, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xyo-xyo-network", - "name": "XYO", - "symbol": "XYO", - "rank": 174, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "glm-golem", - "name": "Golem", - "symbol": "GLM", - "rank": 175, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fei-fei-protocol", - "name": "Fei Protocol", - "symbol": "FEI", - "rank": 176, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rpl-rocket-pool", - "name": "Rocket Pool", - "symbol": "RPL", - "rank": 177, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uos-ultra", - "name": "Ultra", - "symbol": "UOS", - "rank": 178, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alt-alitas", - "name": "Alitas", - "symbol": "ALT", - "rank": 179, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jst-just", - "name": "JUST", - "symbol": "JST", - "rank": 180, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rsr-reserve-rights", - "name": "Reserve Rights", - "symbol": "RSR", - "rank": 181, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dent-dent", - "name": "Dent", - "symbol": "DENT", - "rank": 182, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "best-bitpanda-ecosystem-token", - "name": "Bitpanda Ecosystem Token", - "symbol": "BEST", - "rank": 183, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "req-request-network", - "name": "Request", - "symbol": "REQ", - "rank": 184, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dusk-dusk-network", - "name": "Dusk Network", - "symbol": "DUSK", - "rank": 185, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "keep-keep-network", - "name": "Keep network", - "symbol": "KEEP", - "rank": 186, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "starl-starlink", - "name": "StarLink", - "symbol": "STARL", - "rank": 187, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wrx-wazirx", - "name": "WazirX", - "symbol": "WRX", - "rank": 188, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jasmy-jasmycoin", - "name": "JasmyCoin", - "symbol": "JASMY", - "rank": 189, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fetch-ai", - "name": "Fetch.AI", - "symbol": "FET", - "rank": 190, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ygg-yield-guild-games", - "name": "Yield Guild Games", - "symbol": "YGG", - "rank": 191, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fx-function-x", - "name": "Function X", - "symbol": "FX", - "rank": 192, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdb-digitalbits", - "name": "DigitalBits", - "symbol": "XDB", - "rank": 193, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inj-injective-protocol", - "name": "Injective Protocol", - "symbol": "INJ", - "rank": 194, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "htr-hathor-network", - "name": "Hathor Network", - "symbol": "HTR", - "rank": 195, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sxp-swipe", - "name": "Swipe", - "symbol": "SXP", - "rank": 196, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "veri-veritaseum", - "name": "Veritaseum", - "symbol": "VERI", - "rank": 197, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solo-sologenic", - "name": "Sologenic", - "symbol": "SOLO", - "rank": 198, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orc-orbit-chain", - "name": "Orbit Chain", - "symbol": "ORC", - "rank": 199, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coti-coti", - "name": "COTI", - "symbol": "COTI", - "rank": 200, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "paxg-pax-gold", - "name": "PAX Gold", - "symbol": "PAXG", - "rank": 201, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yooshi-yooshi", - "name": "YooShi", - "symbol": "YOOSHI", - "rank": 202, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "husd-husd", - "name": "HUSD", - "symbol": "HUSD", - "rank": 203, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btrst-braintrust", - "name": "Braintrust", - "symbol": "BTRST", - "rank": 204, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lsk-lisk", - "name": "Lisk", - "symbol": "LSK", - "rank": 205, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "reef-reef", - "name": "Reef", - "symbol": "REEF", - "rank": 206, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdx-mdex", - "name": "Mdex", - "symbol": "MDX", - "rank": 207, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "med-medibloc-qrc20", - "name": "MediBloc", - "symbol": "MED", - "rank": 208, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ewt-energy-web-token", - "name": "Energy Web Token", - "symbol": "EWT", - "rank": 209, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wild-wilder-world", - "name": "Wilder World", - "symbol": "WILD", - "rank": 210, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xvg-verge", - "name": "Verge", - "symbol": "XVG", - "rank": 211, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vtho-vethor-token", - "name": "VeThor Token", - "symbol": "VTHO", - "rank": 212, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hedg-hedgetrade", - "name": "HedgeTrade", - "symbol": "HEDG", - "rank": 213, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mines-of-dalarnia-dar", - "name": "Mines of Dalarnia", - "symbol": "DAR", - "rank": 214, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nkn-nkn", - "name": "NKN", - "symbol": "NKN", - "rank": 215, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dag-constellation", - "name": "Constellation", - "symbol": "DAG", - "rank": 216, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bcd-bitcoin-diamond", - "name": "Bitcoin Diamond", - "symbol": "BCD", - "rank": 217, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "twt-trust-wallet-token", - "name": "Trust Wallet Token", - "symbol": "TWT", - "rank": 218, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rlc-iexec-rlc", - "name": "iExec RLC", - "symbol": "RLC", - "rank": 219, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ogn-origin-protocol", - "name": "Origin Protocol", - "symbol": "OGN", - "rank": 220, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "soul-phantasma", - "name": "Phantasma", - "symbol": "SOUL", - "rank": 221, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snt-status", - "name": "Status", - "symbol": "SNT", - "rank": 222, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rad-radicle", - "name": "Radicle", - "symbol": "RAD", - "rank": 223, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pols-polkastarter", - "name": "Polkastarter", - "symbol": "POLS", - "rank": 224, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfx-conflux-network", - "name": "Conflux Network", - "symbol": "CFX", - "rank": 225, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ankr-ankr-network", - "name": "Ankr", - "symbol": "ANKR", - "rank": 226, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sapp-sapphire", - "name": "Sapphire", - "symbol": "SAPP", - "rank": 227, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ardr-ardor", - "name": "Ardor", - "symbol": "ARDR", - "rank": 228, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cvc-civic", - "name": "Civic", - "symbol": "CVC", - "rank": 229, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mngo-mango-markets", - "name": "Mango Markets", - "symbol": "MNGO", - "rank": 230, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ach-alchemy-pay", - "name": "Alchemy Pay", - "symbol": "ACH", - "rank": 231, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "asd-ascendex-token", - "name": "AscendEx Token", - "symbol": "ASD", - "rank": 232, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alcx-alchemix", - "name": "Alchemix", - "symbol": "ALCX", - "rank": 233, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arrr-pirate", - "name": "Pirate", - "symbol": "ARRR", - "rank": 234, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ubt-unibright", - "name": "Unibright", - "symbol": "UBT", - "rank": 235, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orn-orion-protocol", - "name": "Orion Protocol", - "symbol": "ORN", - "rank": 236, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elf-aelf", - "name": "aelf", - "symbol": "ELF", - "rank": 237, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "divi-divi", - "name": "Divi", - "symbol": "DIVI", - "rank": 238, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "prom-prometeus", - "name": "Prometeus", - "symbol": "PROM", - "rank": 239, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tlm-alien-worlds", - "name": "Alien Worlds", - "symbol": "TLM", - "rank": 240, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "akt-akash-network", - "name": "Akash Network", - "symbol": "AKT", - "rank": 241, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pla-playdapp", - "name": "PlayDapp", - "symbol": "PLA", - "rank": 242, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xsgd-xsgd", - "name": "XSGD", - "symbol": "XSGD", - "rank": 243, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vxv-vectorspace-ai", - "name": "Vectorspace AI", - "symbol": "VXV", - "rank": 244, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "powr-power-ledger", - "name": "Power Ledger", - "symbol": "POWR", - "rank": 245, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "exrd-e-radix", - "name": "E-RADIX", - "symbol": "eXRD", - "rank": 246, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rep-augur", - "name": "Augur", - "symbol": "REP", - "rank": 247, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xaut-tether-gold", - "name": "Tether Gold", - "symbol": "XAUT", - "rank": 248, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orbs-orbs", - "name": "Orbs", - "symbol": "ORBS", - "rank": 249, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nmr-numeraire", - "name": "Numeraire", - "symbol": "NMR", - "rank": 250, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mof-molecular-future", - "name": "Molecular Future", - "symbol": "MOF", - "rank": 251, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mir-mirror-protocol", - "name": "Mirror Protocol", - "symbol": "MIR", - "rank": 252, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "strax-stratis", - "name": "Stratis", - "symbol": "STRAX", - "rank": 253, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xvs-venus", - "name": "Venus", - "symbol": "XVS", - "rank": 254, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "band-band-protocol", - "name": "Band Protocol", - "symbol": "BAND", - "rank": 255, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ampl-ampleforth", - "name": "Ampleforth", - "symbol": "AMPL", - "rank": 256, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "maid-maidsafecoin", - "name": "MaidSafeCoin", - "symbol": "MAID", - "rank": 257, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meta-metadium", - "name": "Metadium", - "symbol": "META", - "rank": 258, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hxro-hxro", - "name": "Hxro", - "symbol": "HXRO", - "rank": 259, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "c20-crypto20", - "name": "CRYPTO20", - "symbol": "C20", - "rank": 260, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "agix-singularitynet", - "name": "Singularity NET", - "symbol": "AGIX", - "rank": 261, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blct-bloomzed-loyalty-club-ticket", - "name": "Bloomzed Loyalty Club Ticket", - "symbol": "BLCT", - "rank": 262, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfund-seedifyfund", - "name": "Seedify.fund", - "symbol": "SFUND", - "rank": 263, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tlos-telos", - "name": "Telos", - "symbol": "TLOS", - "rank": 264, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "steem-steem", - "name": "Steem", - "symbol": "STEEM", - "rank": 265, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "etn-electroneum", - "name": "Electroneum", - "symbol": "ETN", - "rank": 266, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tomo-tomochain", - "name": "TomoChain", - "symbol": "TOMO", - "rank": 267, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mln-enzyme", - "name": "Enzyme", - "symbol": "MLN", - "rank": 268, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ark-ark", - "name": "Ark", - "symbol": "ARK", - "rank": 269, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xch-chia-", - "name": "Chia", - "symbol": "XCH", - "rank": 270, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "idex-idex", - "name": "IDEX", - "symbol": "IDEX", - "rank": 271, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rif-rif-token", - "name": "RIF Token", - "symbol": "RIF", - "rank": 272, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gxs-gxchain", - "name": "GXChain", - "symbol": "GXC", - "rank": 273, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "efyt-ergo", - "name": "Ergo", - "symbol": "ERG", - "rank": 274, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safe-safe", - "name": "Safe", - "symbol": "SAFE", - "rank": 275, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vra-verasity", - "name": "Verasity", - "symbol": "VRA", - "rank": 276, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsw-biswap", - "name": "Biswap", - "symbol": "BSW", - "rank": 277, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dpi-defi-pulse-index", - "name": "DeFi Pulse Index", - "symbol": "DPI", - "rank": 278, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "titan-titanswap", - "name": "TitanSwap", - "symbol": "TITAN", - "rank": 279, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssx-somesing", - "name": "SOMESING", - "symbol": "SSX", - "rank": 280, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cennz-centrality", - "name": "Centrality", - "symbol": "CENNZ", - "rank": 281, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snl-sport-and-leisure", - "name": "Sport and Leisure", - "symbol": "SNL", - "rank": 282, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "utk-utrust", - "name": "Utrust", - "symbol": "UTK", - "rank": 283, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rkn-rakon", - "name": "RAKON", - "symbol": "RKN", - "rank": 284, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gusd-gemini-dollar", - "name": "Gemini Dollar", - "symbol": "GUSD", - "rank": 285, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zlw-zelwin", - "name": "Zelwin", - "symbol": "ZLW", - "rank": 286, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kp3r-keep3rv1", - "name": "Keep3rV1", - "symbol": "KP3R", - "rank": 287, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eps-ellipsis", - "name": "Ellipsis", - "symbol": "EPS", - "rank": 288, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "peak-peakdefi", - "name": "PEAKDEFI", - "symbol": "PEAK", - "rank": 289, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpr-proton", - "name": "Proton", - "symbol": "XPR", - "rank": 290, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stpt-stpt", - "name": "STPT", - "symbol": "STPT", - "rank": 291, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wan-wanchain", - "name": "Wanchain", - "symbol": "WAN", - "rank": 292, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "baby-babyswap", - "name": "BabySwap", - "symbol": "BABY", - "rank": 293, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtl-metal", - "name": "Metal", - "symbol": "MTL", - "rank": 294, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slim-solanium", - "name": "Solanium", - "symbol": "SLIM", - "rank": 295, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abbc-alibabacoin", - "name": "ABBC Coin", - "symbol": "ABBC", - "rank": 296, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "susd-susd", - "name": "sUSD", - "symbol": "SUSD", - "rank": 297, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lcx-lcx", - "name": "LCX", - "symbol": "LCX", - "rank": 298, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnx-binaryx", - "name": "BinaryX", - "symbol": "BNX", - "rank": 299, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mxc-machine-xchange-coin", - "name": "Machine Xchange Coin", - "symbol": "MXC", - "rank": 300, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "derc-derace", - "name": "DeRace", - "symbol": "DERC", - "rank": 301, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qkc-quarkchain", - "name": "QuarkChain", - "symbol": "QKC", - "rank": 302, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kin-kin", - "name": "Kin", - "symbol": "KIN", - "rank": 303, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dero-dero", - "name": "Dero", - "symbol": "DERO", - "rank": 304, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tvk-terra-virtua-kolect", - "name": "Terra Virtua Kolect", - "symbol": "TVK", - "rank": 305, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pre-presearch", - "name": "Presearch", - "symbol": "PRE", - "rank": 306, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "time-chronotech", - "name": "Chrono.tech", - "symbol": "TIME", - "rank": 307, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "albt-allianceblock", - "name": "AllianceBlock", - "symbol": "ALBT", - "rank": 308, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "noia-syntropy", - "name": "Syntropy", - "symbol": "NOIA", - "rank": 309, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fun-funfair", - "name": "FunFair", - "symbol": "FUN", - "rank": 310, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfg-centrifuge", - "name": "Centrifuge", - "symbol": "CFG", - "rank": 311, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zb-zb", - "name": "ZB", - "symbol": "ZB", - "rank": 312, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btse-btse-token", - "name": "BTSE Token", - "symbol": "BTSE", - "rank": 313, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iq-everipedia", - "name": "Everipedia", - "symbol": "IQ", - "rank": 314, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "badger-badger", - "name": "Badger", - "symbol": "BADGER", - "rank": 315, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "agld-adventure-gold", - "name": "Adventure Gold", - "symbol": "AGLD", - "rank": 316, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aury-aurory", - "name": "Aurory", - "symbol": "AURY", - "rank": 317, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfa-define", - "name": "DeFine", - "symbol": "DFA", - "rank": 318, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "strk-strike", - "name": "Strike", - "symbol": "STRK", - "rank": 319, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "klv-klever", - "name": "Klever", - "symbol": "KLV", - "rank": 320, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skl-skale", - "name": "SKALE", - "symbol": "SKL", - "rank": 321, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbtc-smart-bitcoin", - "name": "RSK Smart Bitcoin", - "symbol": "RBTC", - "rank": 322, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xwc-whitecoin", - "name": "WhiteCoin", - "symbol": "XWC", - "rank": 323, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ern-ethernity-chain", - "name": "Ethernity Chain", - "symbol": "ERN", - "rank": 324, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddx-derivadao", - "name": "DerivaDAO", - "symbol": "DDX", - "rank": 325, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dka-dkargo", - "name": "dKargo", - "symbol": "DKA", - "rank": 326, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eurs-stasis-eurs", - "name": "STASIS EURO", - "symbol": "EURS", - "rank": 327, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bal-balancer", - "name": "Balancer", - "symbol": "BAL", - "rank": 328, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lto-lto-network", - "name": "LTO Network", - "symbol": "LTO", - "rank": 329, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "arpa-arpa-chain", - "name": "ARPA Chain", - "symbol": "ARPA", - "rank": 330, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pha-phala-network", - "name": "Phala Network", - "symbol": "PHA", - "rank": 331, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hns-handshake", - "name": "Handshake", - "symbol": "HNS", - "rank": 332, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bscpad-bscpad", - "name": "BSCPAD", - "symbol": "BSCPAD", - "rank": 333, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfii-dfimoney", - "name": "DFI.Money", - "symbol": "YFII", - "rank": 334, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swap-trustswap", - "name": "TrustSwap", - "symbol": "SWAP", - "rank": 335, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mobi-mobius", - "name": "Mobius", - "symbol": "MOBI", - "rank": 336, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coval-circuits-of-value", - "name": "Circuits of Value", - "symbol": "COVAL", - "rank": 337, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "quick-quickswap", - "name": "Quickswap", - "symbol": "QUICK", - "rank": 338, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aioz-aioz-network", - "name": "AIOZ Network", - "symbol": "AIOZ", - "rank": 339, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pro-propy", - "name": "Propy", - "symbol": "PRO", - "rank": 340, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cos-contentos", - "name": "Contentos", - "symbol": "COS", - "rank": 341, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "samo-samoyedcoin", - "name": "Samoyedcoin", - "symbol": "SAMO", - "rank": 342, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bts-bitshares", - "name": "BitShares", - "symbol": "BTS", - "rank": 343, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bnana-banana-token", - "name": "Chimpion", - "symbol": "BNANA", - "rank": 344, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wnxm-wrapped-nxm", - "name": "Wrapped NXM", - "symbol": "WNXM", - "rank": 345, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbn-ribbon-finance", - "name": "Ribbon Finance", - "symbol": "RBN", - "rank": 346, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iris-irisnet", - "name": "IRISnet", - "symbol": "IRIS", - "rank": 347, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cusd-celo-dollar", - "name": "Celo Dollar", - "symbol": "cUSD", - "rank": 348, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcv-bitcoin-vault", - "name": "Bitcoin Vault", - "symbol": "BTCV", - "rank": 349, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mrph-morpheusnetwork", - "name": "Morpheus Network", - "symbol": "MRPH", - "rank": 350, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rook-keeperdao", - "name": "KeeperDAO", - "symbol": "ROOK", - "rank": 351, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dodo-dodo", - "name": "DODO", - "symbol": "DODO", - "rank": 352, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kmd-komodo", - "name": "Komodo", - "symbol": "KMD", - "rank": 353, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "chess-tranchess", - "name": "Tranchess", - "symbol": "CHESS", - "rank": 354, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snm-sonm", - "name": "SONM", - "symbol": "SNM", - "rank": 355, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctxc-cortex", - "name": "Cortex", - "symbol": "CTXC", - "rank": 356, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "troy-troy", - "name": "Troy", - "symbol": "TROY", - "rank": 357, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "forth-ampleforth-governance-token", - "name": "Ampleforth Governance Token", - "symbol": "FORTH", - "rank": 358, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aqt-alpha-quark-token", - "name": "Alpha Quark Token", - "symbol": "AQT", - "rank": 359, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ava-travala", - "name": "Travala.com", - "symbol": "AVA", - "rank": 360, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "farm-harvest-finance", - "name": "Harvest Finance", - "symbol": "FARM", - "rank": 361, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "taboo-taboo-token", - "name": "Taboo Token", - "symbol": "TABOO", - "rank": 362, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crts-cratos", - "name": "Cratos", - "symbol": "CRTS", - "rank": 363, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lit-litentry", - "name": "Litentry", - "symbol": "LIT", - "rank": 364, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "data-streamr-datacoin", - "name": "Streamr DATAcoin", - "symbol": "DATA", - "rank": 365, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flx-reflexer-ungovernance-token", - "name": "Reflexer Ungovernance Token", - "symbol": "FLX", - "rank": 366, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdt-measurable-data-token", - "name": "Measurable Data Token", - "symbol": "MDT", - "rank": 367, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msol-marinade-staked-sol", - "name": "Marinade Staked Sol", - "symbol": "MSOL", - "rank": 368, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moc-mossland", - "name": "Moss Coin", - "symbol": "MOC", - "rank": 369, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xhv-haven-protocol", - "name": "Haven Protocol", - "symbol": "XHV", - "rank": 370, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "slp-smooth-love-potion", - "name": "Smooth Love Potion", - "symbol": "SLP", - "rank": 371, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mft-hifi-finance", - "name": "Hifi Finance", - "symbol": "MFT", - "rank": 372, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kar-karura", - "name": "Karura", - "symbol": "KAR", - "rank": 373, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "loom-loom-network", - "name": "Loom Network", - "symbol": "LOOM", - "rank": 374, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aergo-aergo", - "name": "Aergo", - "symbol": "AERGO", - "rank": 375, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alpaca-alpaca-finance", - "name": "Alpaca Finance", - "symbol": "ALPACA", - "rank": 376, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdn-shiden-network", - "name": "Shiden Network", - "symbol": "SDN", - "rank": 377, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cre-carry", - "name": "Carry", - "symbol": "CRE", - "rank": 378, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bifi-beefyfinance", - "name": "Beefy.Finance", - "symbol": "BIFI", - "rank": 379, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tt-thunder-token", - "name": "Thunder Token", - "symbol": "TT", - "rank": 380, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "eum-elitium", - "name": "ELITIUM", - "symbol": "EUM", - "rank": 381, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hunt-hunt", - "name": "HUNT", - "symbol": "HUNT", - "rank": 382, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wild-wild-crypto", - "name": "Wild Crypto", - "symbol": "WILD", - "rank": 383, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nuls-nuls", - "name": "NULS", - "symbol": "NULS", - "rank": 384, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "akro-akropolis", - "name": "Akropolis", - "symbol": "AKRO", - "rank": 385, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mix-mixmarvel", - "name": "MixMarvel", - "symbol": "MIX", - "rank": 386, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nye-newyork-exchange", - "name": "NewYork Exchange", - "symbol": "NYE", - "rank": 387, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rai-rai-reflex-index", - "name": "Rai Reflex Index", - "symbol": "RAI", - "rank": 388, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cgcx-cgcx", - "name": "CGCX", - "symbol": "CGCX", - "rank": 389, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dnt-district0x", - "name": "district0x", - "symbol": "DNT", - "rank": 390, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ramp-ramp", - "name": "RAMP", - "symbol": "RAMP", - "rank": 391, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mwc-mimblewimblecoin", - "name": "Mimble Wimble Coin", - "symbol": "MWC", - "rank": 392, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "adx-adex", - "name": "AdEx", - "symbol": "ADX", - "rank": 393, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trb-tellor", - "name": "Tellor", - "symbol": "TRB", - "rank": 394, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wtc-waltonchain", - "name": "Waltonchain", - "symbol": "WTC", - "rank": 395, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dia-dia", - "name": "DIA", - "symbol": "DIA", - "rank": 396, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "six-six", - "name": "SIX", - "symbol": "SIX", - "rank": 397, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mona-monacoin", - "name": "MonaCoin", - "symbol": "MONA", - "rank": 398, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gmt-gomining-token", - "name": "GoMining Token", - "symbol": "GMT", - "rank": 399, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lgcy-lgcy-network", - "name": "LGCY Network", - "symbol": "LGCY", - "rank": 400, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fio-fio-protocol", - "name": "FIO Protocol", - "symbol": "FIO", - "rank": 401, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "core-cvaultfinance", - "name": "Cvault.finance", - "symbol": "CORE", - "rank": 402, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nif-unifty", - "name": "Unifty", - "symbol": "NIF", - "rank": 403, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pnk-kleros", - "name": "Kleros", - "symbol": "PNK", - "rank": 404, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tnc-tnc-coin", - "name": "TNC Coin", - "symbol": "TNC", - "rank": 405, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "strong-strong", - "name": "Strong", - "symbol": "STRONG", - "rank": 406, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rari-rarible", - "name": "Rarible", - "symbol": "RARI", - "rank": 407, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blz-bluzelle", - "name": "Bluzelle", - "symbol": "BLZ", - "rank": 408, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aion-aion", - "name": "Aion", - "symbol": "AION", - "rank": 409, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zks-zkswap", - "name": "ZKSwap", - "symbol": "ZKS", - "rank": 410, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmch-darma-cash", - "name": "Darma Cash", - "symbol": "DMCH", - "rank": 411, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "one-menlo-one", - "name": "Menlo One", - "symbol": "ONE", - "rank": 412, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wise-wise", - "name": "WISE", - "symbol": "WISE", - "rank": 413, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hai-hackenai", - "name": "HackenAI", - "symbol": "HAI", - "rank": 414, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qc-qcash", - "name": "Qcash", - "symbol": "QC", - "rank": 415, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "firo-firo", - "name": "Firo", - "symbol": "FIRO", - "rank": 416, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "upp-sentinel-protocol", - "name": "Sentinel Protocol", - "symbol": "UPP", - "rank": 417, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fox-fox-token", - "name": "FOX Token", - "symbol": "FOX", - "rank": 418, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rfr-refereum", - "name": "Refereum", - "symbol": "RFR", - "rank": 419, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btm-bytom", - "name": "Bytom", - "symbol": "BTM", - "rank": 420, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "om-mantra-dao", - "name": "MANTRA DAO", - "symbol": "OM", - "rank": 421, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brd-bread", - "name": "Bread", - "symbol": "BRD", - "rank": 422, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bmx-bitmart-token", - "name": "BitMart Token", - "symbol": "BMX", - "rank": 423, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "erowan-sifchain", - "name": "Sifchain", - "symbol": "EROWAN", - "rank": 424, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zash-zimbocash", - "name": "ZIMBOCASH", - "symbol": "ZASH", - "rank": 425, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rev-rchain", - "name": "RChain", - "symbol": "REV", - "rank": 426, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nwc-newscryptoio", - "name": "Newscrypto.io", - "symbol": "NWC", - "rank": 427, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "key-selfkey", - "name": "Selfkey", - "symbol": "KEY", - "rank": 428, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ela-elastos", - "name": "Elastos", - "symbol": "ELA", - "rank": 429, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "met-metronome", - "name": "Metronome", - "symbol": "MET", - "rank": 430, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prq-parsiq", - "name": "PARSIQ", - "symbol": "PRQ", - "rank": 431, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "asm-assemble-protocol", - "name": "Assemble Protocol", - "symbol": "ASM", - "rank": 432, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "znn-zenon", - "name": "Zenon", - "symbol": "ZNN", - "rank": 433, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "clt-coinloan", - "name": "CoinLoan", - "symbol": "CLT", - "rank": 434, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "epik-epik-prime", - "name": "Epik Prime", - "symbol": "EPIK", - "rank": 435, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mith-mithril", - "name": "Mithril", - "symbol": "MITH", - "rank": 436, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "beam-beam", - "name": "Beam", - "symbol": "BEAM", - "rank": 437, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nrg-energi", - "name": "Energi", - "symbol": "NRG", - "rank": 438, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cdt-blox", - "name": "Blox", - "symbol": "CDT", - "rank": 439, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vid-videocoin", - "name": "VideoCoin", - "symbol": "VID", - "rank": 440, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vbit-valobit", - "name": "VALOBIT", - "symbol": "VBIT", - "rank": 441, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grs-groestlcoin", - "name": "Groestlcoin", - "symbol": "GRS", - "rank": 442, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hoge-hoge-finance", - "name": "Hoge Finance", - "symbol": "HOGE", - "rank": 443, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gas-gas", - "name": "Gas", - "symbol": "GAS", - "rank": 444, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbl-moviebloc", - "name": "MovieBloc", - "symbol": "MBL", - "rank": 445, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "suku-suku", - "name": "SUKU", - "symbol": "SUKU", - "rank": 446, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vai-vai", - "name": "Vai", - "symbol": "VAI", - "rank": 447, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pcl-peculium", - "name": "Peculium", - "symbol": "PCL", - "rank": 448, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fsn-fusion", - "name": "Fusion", - "symbol": "FSN", - "rank": 449, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dgd-digixdao", - "name": "DigixDAO", - "symbol": "DGD", - "rank": 450, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kick-kickcoin", - "name": "KickToken", - "symbol": "KICK", - "rank": 451, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solve-solve", - "name": "SOLVE", - "symbol": "SOLVE", - "rank": 452, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbd-steem-dollars", - "name": "Steem Dollars", - "symbol": "SBD", - "rank": 453, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "con-conun", - "name": "CONUN", - "symbol": "CON", - "rank": 454, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "burger-burger-swap", - "name": "Burger Swap", - "symbol": "BURGER", - "rank": 455, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edo-eidoo", - "name": "Eidoo", - "symbol": "EDO", - "rank": 456, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ads-adshares", - "name": "Adshares", - "symbol": "ADS", - "rank": 457, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dock-dock", - "name": "Dock", - "symbol": "DOCK", - "rank": 458, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krl-kryll", - "name": "Kryll", - "symbol": "KRL", - "rank": 459, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stake-xdai-stake", - "name": "xDAI Stake", - "symbol": "STAKE", - "rank": 460, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cet-coinex-token", - "name": "CoinEx Token", - "symbol": "CET", - "rank": 461, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tnt-tierion", - "name": "Tierion", - "symbol": "TNT", - "rank": 462, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axel-axel", - "name": "AXEL", - "symbol": "AXEL", - "rank": 463, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rise-everrise", - "name": "EverRise", - "symbol": "RISE", - "rank": 464, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sntvt-sentivate", - "name": "Sentivate", - "symbol": "SNTVT", - "rank": 465, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "png-pangolin", - "name": "Pangolin", - "symbol": "PNG", - "rank": 466, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xmon-xmon", - "name": "XMON", - "symbol": "XMON", - "rank": 467, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amo-amo-coin", - "name": "AMO Coin", - "symbol": "AMO", - "rank": 468, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "for-fortube", - "name": "ForTube", - "symbol": "FOR", - "rank": 469, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vsys-v-systems", - "name": "V Systems", - "symbol": "VSYS", - "rank": 470, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "anj-aragon-court", - "name": "Aragon Court", - "symbol": "ANJ", - "rank": 471, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hegic-hegic", - "name": "HEGIC", - "symbol": "HEGIC", - "rank": 472, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gto-gifto", - "name": "Gifto", - "symbol": "GTO", - "rank": 473, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "epic-epic-cash", - "name": "Epic Cash", - "symbol": "EPIC", - "rank": 474, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "basic-basic", - "name": "BASIC", - "symbol": "BASIC", - "rank": 475, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cxo-cargox", - "name": "CargoX", - "symbol": "CXO", - "rank": 476, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scp-scprime", - "name": "ScPrime", - "symbol": "SCP", - "rank": 477, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mvc-mileverse", - "name": "MileVerse", - "symbol": "MVC", - "rank": 478, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vrsc-verus-coin", - "name": "Verus Coin", - "symbol": "VRSC", - "rank": 479, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fct-firmachain", - "name": "Firmachain", - "symbol": "FCT", - "rank": 480, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ong-ong", - "name": "Ontology Gas", - "symbol": "ONG", - "rank": 481, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hdac-hdac", - "name": "Hdac", - "symbol": "HDAC", - "rank": 482, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "math-math", - "name": "MATH", - "symbol": "MATH", - "rank": 483, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gny-gny", - "name": "GNY", - "symbol": "GNY", - "rank": 484, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcn-bytecoin", - "name": "Bytecoin", - "symbol": "BCN", - "rank": 485, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "velo-velo", - "name": "Velo", - "symbol": "VELO", - "rank": 486, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdao-singularitydao", - "name": "SingularityDAO", - "symbol": "SDAO", - "rank": 487, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xor-sora", - "name": "Sora", - "symbol": "XOR", - "rank": 488, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ae-aeternity", - "name": "Aeternity", - "symbol": "AE", - "rank": 489, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "revv-revv", - "name": "REVV", - "symbol": "REVV", - "rank": 490, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbs-new-bitshares", - "name": "New BitShares", - "symbol": "NBS", - "rank": 491, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "la-latoken", - "name": "LATOKEN", - "symbol": "LA", - "rank": 492, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evz-electric-vehicle-zone", - "name": "Electric Vehicle Zone", - "symbol": "EVZ", - "rank": 493, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "seele-seele", - "name": "Seele", - "symbol": "SEELE", - "rank": 494, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "torn-tornado-cash", - "name": "Tornado Cash", - "symbol": "TORN", - "rank": 495, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dexe-dexe", - "name": "DeXe", - "symbol": "DEXE", - "rank": 496, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ooe-openocean", - "name": "OpenOcean", - "symbol": "OOE", - "rank": 497, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shr-sharetoken", - "name": "ShareToken", - "symbol": "SHR", - "rank": 498, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aht-ahatoken", - "name": "AhaToken", - "symbol": "AHT", - "rank": 499, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "perl-perleco", - "name": "PERL.eco", - "symbol": "PERL", - "rank": 500, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtv-multivac", - "name": "MultiVAC", - "symbol": "MTV", - "rank": 501, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dobo-dogebonk", - "name": "DogeBonk", - "symbol": "DOBO", - "rank": 502, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fleta-fleta", - "name": "FLETA", - "symbol": "FLETA", - "rank": 503, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ult-ultiledger", - "name": "Ultiledger", - "symbol": "ULT", - "rank": 504, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ast-airswap", - "name": "AirSwap", - "symbol": "AST", - "rank": 505, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vidt-v-id", - "name": "VIDT Datalink", - "symbol": "VIDT", - "rank": 506, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hez-hermez-network", - "name": "Hermez Network", - "symbol": "HEZ", - "rank": 507, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ghc-galaxy-heroes-coin", - "name": "Galaxy Heroes Coin", - "symbol": "GHC", - "rank": 508, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dora-dora-factory", - "name": "Dora Factory", - "symbol": "DORA", - "rank": 509, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aleph-alephim", - "name": "aleph.im", - "symbol": "ALEPH", - "rank": 510, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wozx-efforce", - "name": "Efforce", - "symbol": "WOZX", - "rank": 511, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shill-shill-token", - "name": "SHILL Token", - "symbol": "SHILL", - "rank": 512, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pnt-pnetwork", - "name": "pNetwork", - "symbol": "PNT", - "rank": 513, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vite-vite", - "name": "VITE", - "symbol": "VITE", - "rank": 514, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "go-gochain", - "name": "GoChain", - "symbol": "GO", - "rank": 515, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nvt-nervenetwork", - "name": "NerveNetwork", - "symbol": "NVT", - "rank": 516, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pac-paccoin", - "name": "PAC Protocol", - "symbol": "PAC", - "rank": 517, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nim-nimiq", - "name": "Nimiq", - "symbol": "NIM", - "rank": 518, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sero-super-zero", - "name": "Super Zero", - "symbol": "SERO", - "rank": 519, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wicc-waykichain", - "name": "WaykiChain", - "symbol": "WICC", - "rank": 520, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pivx-pivx", - "name": "PIVX", - "symbol": "PIVX", - "rank": 521, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "apl-apollo-currency", - "name": "Apollo Currency", - "symbol": "APL", - "rank": 522, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "loki-loki", - "name": "Loki", - "symbol": "LOKI", - "rank": 523, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "stos-stratos", - "name": "Stratos", - "symbol": "STOS", - "rank": 524, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bepro-bet-protocol", - "name": "BetProtocol", - "symbol": "BEPRO", - "rank": 525, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qtcon-quiztok", - "name": "Quiztok", - "symbol": "QTCON", - "rank": 526, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ggc-global-game-coin", - "name": "Global Game Coin", - "symbol": "GGC", - "rank": 527, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "caps-ternoa", - "name": "Ternoa", - "symbol": "CAPS", - "rank": 528, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ham-hamster", - "name": "Hamster", - "symbol": "HAM", - "rank": 529, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ghx-gamercoin", - "name": "GamerCoin", - "symbol": "GHX", - "rank": 530, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wxt-wirex-token", - "name": "Wirex Token", - "symbol": "WXT", - "rank": 531, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pltc-platoncoin", - "name": "PlatonCoin", - "symbol": "PLTC", - "rank": 532, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nxs-nexus", - "name": "Nexus", - "symbol": "NXS", - "rank": 533, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nftx-nftx", - "name": "NFTX", - "symbol": "NFTX", - "rank": 534, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcmc-blockchain-monster-hunt", - "name": "Blockchain Monster Hunt", - "symbol": "BCMC", - "rank": 535, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogedash-doge-dash", - "name": "Doge Dash", - "symbol": "DOGEDASH", - "rank": 536, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "map-map-protocol", - "name": "MAP Protocol", - "symbol": "MAP", - "rank": 537, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xno-xeno-token", - "name": "Xeno Token", - "symbol": "XNO", - "rank": 538, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stack-stackos", - "name": "StackOS", - "symbol": "STACK", - "rank": 539, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "auto-auto", - "name": "Auto", - "symbol": "AUTO", - "rank": 540, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "raini-rainicorn", - "name": "Rainicorn", - "symbol": "RAINI", - "rank": 541, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "el-elysia", - "name": "ELYSIA", - "symbol": "EL", - "rank": 542, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kdag-king-dag", - "name": "King DAG", - "symbol": "KDAG", - "rank": 543, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hydra-hydra", - "name": "HYDRA", - "symbol": "HYDRA", - "rank": 544, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ersdl-unfederalreserve", - "name": "UnFederalReserve", - "symbol": "ERSDL", - "rank": 545, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sica-signaturechain", - "name": "SignatureChain", - "symbol": "SICA", - "rank": 546, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bax-babb", - "name": "BABB", - "symbol": "BAX", - "rank": 547, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xft-offshift", - "name": "Offshift", - "symbol": "XFT", - "rank": 548, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hibs-hiblocks", - "name": "Hiblocks", - "symbol": "HIBS", - "rank": 549, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dxd-dxdao", - "name": "DXdao", - "symbol": "DXD", - "rank": 550, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cgg-chain-guardians", - "name": "Chain Guardians", - "symbol": "CGG", - "rank": 551, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcb-mcdex", - "name": "MCDEX", - "symbol": "MCB", - "rank": 552, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vee-blockv", - "name": "BLOCKv", - "symbol": "VEE", - "rank": 553, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tmtg-the-midas-touch-gold", - "name": "The Midas Touch Gold", - "symbol": "TMTG", - "rank": 554, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "game-gamecredits", - "name": "GameCredits", - "symbol": "GAME", - "rank": 555, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "avt-aventus", - "name": "Aventus", - "symbol": "AVT", - "rank": 556, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "socks-unisocks", - "name": "Unisocks", - "symbol": "SOCKS", - "rank": 557, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "banana-apeswap-finance", - "name": "ApeSwap Finance", - "symbol": "BANANA", - "rank": 558, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qsp-quantstamp", - "name": "Quantstamp", - "symbol": "QSP", - "rank": 559, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tct-tokenclub", - "name": "TokenClub", - "symbol": "TCT", - "rank": 560, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rsv-reserve", - "name": "Reserve", - "symbol": "RSV", - "rank": 561, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "usdk-usdk", - "name": "USDK", - "symbol": "USDK", - "rank": 562, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfi-saffronfinance", - "name": "saffron.finance", - "symbol": "SFI", - "rank": 563, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "like-likecoin", - "name": "LikeCoin", - "symbol": "LIKE", - "rank": 564, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cru-crust", - "name": "Crust", - "symbol": "CRU", - "rank": 565, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xcp-counterparty", - "name": "Counterparty", - "symbol": "XCP", - "rank": 566, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cell-cellframe", - "name": "Cellframe", - "symbol": "CELL", - "rank": 567, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btr-bitrue-coin", - "name": "Bitrue Coin", - "symbol": "BTR", - "rank": 568, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drgn-dragonchain", - "name": "Dragonchain", - "symbol": "DRGN", - "rank": 569, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vtc-vertcoin", - "name": "Vertcoin", - "symbol": "VTC", - "rank": 570, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "swth-switcheo", - "name": "Switcheo", - "symbol": "SWTH", - "rank": 571, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbc-rubic", - "name": "Rubic", - "symbol": "RBC", - "rank": 572, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "asta-asta", - "name": "ASTA", - "symbol": "ASTA", - "rank": 573, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "val-validity", - "name": "Validity", - "symbol": "VAL", - "rank": 574, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "stc-student-coin", - "name": "Student Coin", - "symbol": "STC", - "rank": 575, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dx-dxchain-token", - "name": "DxChain Token", - "symbol": "DX", - "rank": 576, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gzone-gamezone", - "name": "GameZone", - "symbol": "GZONE", - "rank": 577, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "soc-soda-coin", - "name": "SODA Coin", - "symbol": "SOC", - "rank": 578, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "valor-valor-token", - "name": "Valor Token", - "symbol": "VALOR", - "rank": 579, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "temco-temco", - "name": "TEMCO", - "symbol": "TEMCO", - "rank": 580, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "locus-locus-chain", - "name": "Locus Chain", - "symbol": "LOCUS", - "rank": 581, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "belt-belt", - "name": "Belt", - "symbol": "BELT", - "rank": 582, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pbr-polkabridge", - "name": "PolkaBridge", - "symbol": "PBR", - "rank": 583, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egg-nestree", - "name": "Nestree", - "symbol": "EGG", - "rank": 584, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmcoin-gmcoin", - "name": "GMCoin", - "symbol": "GMCOIN", - "rank": 585, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "porto-fc-porto", - "name": "FC Porto", - "symbol": "PORTO", - "rank": 586, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmee-gamee", - "name": "GAMEE", - "symbol": "GMEE", - "rank": 587, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "civ-civilization", - "name": "Civilization", - "symbol": "CIV", - "rank": 588, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "life-life-crypto", - "name": "Life Crypto", - "symbol": "LIFE", - "rank": 589, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nct-polyswarm", - "name": "PolySwarm", - "symbol": "NCT", - "rank": 590, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mint-mint-club", - "name": "Mint Club", - "symbol": "MINT", - "rank": 591, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fara-faraland", - "name": "FaraLand", - "symbol": "FARA", - "rank": 592, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skey-skey-network", - "name": "Skey Network", - "symbol": "SKEY", - "rank": 593, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nav-navcoin", - "name": "NavCoin", - "symbol": "NAV", - "rank": 594, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ask-permission-coin", - "name": "Permission Coin", - "symbol": "ASK", - "rank": 595, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "suter-suterusu", - "name": "Suterusu", - "symbol": "SUTER", - "rank": 596, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "id-everest", - "name": "Everest", - "symbol": "ID", - "rank": 597, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gyen-gyen", - "name": "GYEN", - "symbol": "GYEN", - "rank": 598, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnk-bankera", - "name": "Bankera", - "symbol": "BNK", - "rank": 599, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "route-router-protocol", - "name": "Router Protocol", - "symbol": "ROUTE", - "rank": 600, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dacxi-dacxi", - "name": "Dacxi", - "symbol": "DACXI", - "rank": 601, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "get-get-protocol", - "name": "GET Protocol", - "symbol": "GET", - "rank": 602, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zano-zano", - "name": "Zano", - "symbol": "ZANO", - "rank": 603, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "poolz-poolz-finance", - "name": "Poolz Finance", - "symbol": "POOLZ", - "rank": 604, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dep-depth-token", - "name": "Depth Token", - "symbol": "DEP", - "rank": 605, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arv-ariva", - "name": "ARIVA", - "symbol": "ARV", - "rank": 606, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aoa-aurora", - "name": "Aurora", - "symbol": "AOA", - "rank": 607, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ppc-peercoin", - "name": "Peercoin", - "symbol": "PPC", - "rank": 608, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "opct-opacity", - "name": "Opacity", - "symbol": "OPCT", - "rank": 609, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "digg-digg", - "name": "Digg", - "symbol": "DIGG", - "rank": 610, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fab-fast-access-blockchain", - "name": "Fast Access Blockchain", - "symbol": "FAB", - "rank": 611, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xin-infinity-economics", - "name": "Infinity Economics", - "symbol": "XIN", - "rank": 612, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "grin-grin", - "name": "Grin", - "symbol": "GRIN", - "rank": 613, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ppt-populous", - "name": "Populous", - "symbol": "PPT", - "rank": 614, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nebl-neblio", - "name": "Neblio", - "symbol": "NEBL", - "rank": 615, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "like-only1", - "name": "Only1", - "symbol": "LIKE", - "rank": 616, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inxt-internxt", - "name": "Internxt", - "symbol": "INXT", - "rank": 617, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pendle-pendle", - "name": "Pendle", - "symbol": "PENDLE", - "rank": 618, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eac-earthcoin", - "name": "EarthCoin", - "symbol": "EAC", - "rank": 619, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mhc-metahash", - "name": "#MetaHash", - "symbol": "MHC", - "rank": 620, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pcx-chainx", - "name": "ChainX", - "symbol": "PCX", - "rank": 621, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "maha-mahadao", - "name": "MahaDAO", - "symbol": "MAHA", - "rank": 622, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nas-nebulas", - "name": "Nebulas", - "symbol": "NAS", - "rank": 623, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "part-particl", - "name": "Particl", - "symbol": "PART", - "rank": 624, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "chi-xaya", - "name": "XAYA", - "symbol": "CHI", - "rank": 625, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dip-etherisc-dip-token", - "name": "Etherisc DIP Token", - "symbol": "DIP", - "rank": 626, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnd-cindicator", - "name": "Cindicator", - "symbol": "CND", - "rank": 627, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lotto-lotto", - "name": "Lotto", - "symbol": "LOTTO", - "rank": 628, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "k21-k21", - "name": "K21", - "symbol": "K21", - "rank": 629, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "umb-umbrella-network", - "name": "Umbrella Network", - "symbol": "UMB", - "rank": 630, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polk-polkamarkets", - "name": "Polkamarkets", - "symbol": "POLK", - "rank": 631, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ltx-lattice-token", - "name": "Lattice Token", - "symbol": "LTX", - "rank": 632, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nmc-namecoin", - "name": "Namecoin", - "symbol": "NMC", - "rank": 633, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nex-nash-exchange-token", - "name": "Nash Exchange", - "symbol": "NEX", - "rank": 634, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blp-bullperks", - "name": "BullPerks", - "symbol": "BLP", - "rank": 635, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xrune-thorstarter", - "name": "Thorstarter", - "symbol": "XRUNE", - "rank": 636, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ngm-e-money", - "name": "e-Money", - "symbol": "NGM", - "rank": 637, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ringx-ring-x-platform", - "name": "RING X PLATFORM", - "symbol": "RINGX", - "rank": 638, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hc-hypercash", - "name": "HyperCash", - "symbol": "HC", - "rank": 639, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ipad-infinity-pad", - "name": "Infinity Pad", - "symbol": "IPAD", - "rank": 640, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "senso-senso", - "name": "SENSO", - "symbol": "SENSO", - "rank": 641, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btrs-bitball-treasure", - "name": "Bitball Treasure", - "symbol": "BTRS", - "rank": 642, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slrs-solrise-finance", - "name": "Solrise Finance", - "symbol": "SLRS", - "rank": 643, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ping-sonar", - "name": "Sonar", - "symbol": "PING", - "rank": 644, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zcn-0chain", - "name": "0Chain", - "symbol": "ZCN", - "rank": 645, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "card-cardstack", - "name": "Cardstack", - "symbol": "CARD", - "rank": 646, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qash-qash", - "name": "QASH", - "symbol": "QASH", - "rank": 647, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lbc-lbry-credits", - "name": "LBRY Credits", - "symbol": "LBC", - "rank": 648, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ipx-tachyon-protocol", - "name": "Tachyon Protocol", - "symbol": "IPX", - "rank": 649, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xcur-curate", - "name": "Curate", - "symbol": "XCUR", - "rank": 650, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wom-wom-protocol", - "name": "WOM Protocol", - "symbol": "WOM", - "rank": 651, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dps-deepspace", - "name": "DEEPSPACE", - "symbol": "DPS", - "rank": 652, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wabi-wabi", - "name": "Wabi", - "symbol": "WABI", - "rank": 653, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fair-fairgame", - "name": "FairGame", - "symbol": "FAIR", - "rank": 654, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dsla-dsla-protocol", - "name": "DSLA Protocol", - "symbol": "DSLA", - "rank": 655, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emrx-emirex-token", - "name": "Emirex Token", - "symbol": "EMRX", - "rank": 656, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "betu-betu", - "name": "Betu", - "symbol": "BETU", - "rank": 657, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpan-cryptoplanes", - "name": "CryptoPlanes", - "symbol": "CPAN", - "rank": 658, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meda-medacoin", - "name": "Medacoin", - "symbol": "MEDA", - "rank": 659, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "deri-deri-protocol", - "name": "Deri Protocol", - "symbol": "DERI", - "rank": 660, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kan-bitkan", - "name": "BitKan", - "symbol": "KAN", - "rank": 661, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dg-degate", - "name": "DeGate", - "symbol": "DG", - "rank": 662, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "foam-foam", - "name": "FOAM", - "symbol": "FOAM", - "rank": 663, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gbyte-obyte", - "name": "Obyte", - "symbol": "GBYTE", - "rank": 664, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "talk-talken", - "name": "Talken", - "symbol": "TALK", - "rank": 665, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "revo-revomon", - "name": "Revomon", - "symbol": "REVO", - "rank": 666, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safe-safecoin", - "name": "SafeCoin", - "symbol": "SAFE", - "rank": 667, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sha-safe-haven", - "name": "Safe Haven", - "symbol": "SHA", - "rank": 668, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msb-misbloc", - "name": "Misbloc", - "symbol": "MSB", - "rank": 669, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iqn-iqeon", - "name": "IQeon", - "symbol": "IQN", - "rank": 670, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clo-callisto-network", - "name": "Callisto Network", - "symbol": "CLO", - "rank": 671, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "upi-pawtocol", - "name": "Pawtocol", - "symbol": "UPI", - "rank": 672, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dehub-dehub", - "name": "DeHub", - "symbol": "DEHUB", - "rank": 673, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cummies-cumrocket", - "name": "CumRocket", - "symbol": "CUMMIES", - "rank": 674, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cws-crowns", - "name": "Crowns", - "symbol": "CWS", - "rank": 675, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slice-tranche-finance", - "name": "Tranche Finance", - "symbol": "SLICE", - "rank": 676, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mitx-morpheus-labs", - "name": "Morpheus Labs", - "symbol": "MITX", - "rank": 677, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "srk-sparkpoint", - "name": "Sparkpoint", - "symbol": "SRK", - "rank": 678, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amb-ambrosus", - "name": "Ambrosus", - "symbol": "AMB", - "rank": 679, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "uncx-unicrypt", - "name": "UniCrypt", - "symbol": "UNCX", - "rank": 680, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apy-apyfinance", - "name": "APY.Finance", - "symbol": "APY", - "rank": 681, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oax-oax", - "name": "OAX", - "symbol": "OAX", - "rank": 682, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sarco-sarcophagus", - "name": "Sarcophagus", - "symbol": "SARCO", - "rank": 683, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zee-zeroswap", - "name": "ZeroSwap", - "symbol": "ZEE", - "rank": 684, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfyn-dfyn-network", - "name": "Dfyn Network", - "symbol": "DFYN", - "rank": 685, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fuse-fuse-network", - "name": "Fuse Network", - "symbol": "FUSE", - "rank": 686, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddim-duckdaodime", - "name": "DuckDaoDime", - "symbol": "DDIM", - "rank": 687, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qrl-quantum-resistant-ledger", - "name": "Quantum Resistant Ledger", - "symbol": "QRL", - "rank": 688, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "maps-maps", - "name": "MAPS", - "symbol": "MAPS", - "rank": 689, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trix-triumphx", - "name": "TriumphX", - "symbol": "TRIX", - "rank": 690, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "glc-goldcoin", - "name": "GoldCoin", - "symbol": "GLC", - "rank": 691, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bip-minter", - "name": "Minter", - "symbol": "BIP", - "rank": 692, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tau-lamden", - "name": "Lamden", - "symbol": "TAU", - "rank": 693, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abt-arcblock", - "name": "Arcblock", - "symbol": "ABT", - "rank": 694, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wiken-project-with", - "name": "Project WITH", - "symbol": "WIKEN", - "rank": 695, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shi-shirtum", - "name": "Shirtum", - "symbol": "SHI", - "rank": 696, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tone-te-food", - "name": "TE-FOOD", - "symbol": "TONE", - "rank": 697, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ton-tokamak-network", - "name": "Tokamak Network", - "symbol": "TON", - "rank": 698, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "olt-oneledger", - "name": "OneLedger", - "symbol": "OLT", - "rank": 699, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shroom-niftyx-protocol", - "name": "Niftyx Protocol", - "symbol": "SHROOM", - "rank": 700, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "true-truechain", - "name": "TrueChain", - "symbol": "TRUE", - "rank": 701, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ignis-ignis", - "name": "Ignis", - "symbol": "IGNIS", - "rank": 702, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pacoca-pacoca", - "name": "Pacoca", - "symbol": "PACOCA", - "rank": 703, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orai-oraichain-token", - "name": "Oraichain Token", - "symbol": "ORAI", - "rank": 704, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nest-nest-protocol", - "name": "Nest Protocol", - "symbol": "NEST", - "rank": 705, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frm-ferrum-network", - "name": "Ferrum Network", - "symbol": "FRM", - "rank": 706, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "awc-atomic-wallet-coin", - "name": "Atomic Wallet Coin", - "symbol": "AWC", - "rank": 707, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dtx-data-exchange", - "name": "DaTa eXchange", - "symbol": "DTX", - "rank": 708, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "epk-epik-protocol", - "name": "EpiK Protocol", - "symbol": "EPK", - "rank": 709, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tower-tower", - "name": "Tower", - "symbol": "TOWER", - "rank": 710, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "free-free-coin", - "name": "FREE Coin", - "symbol": "FREE", - "rank": 711, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tft-threefold-token", - "name": "ThreeFold Token", - "symbol": "TFT", - "rank": 712, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pickle-pickle-finance", - "name": "Pickle Finance", - "symbol": "PICKLE", - "rank": 713, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meme-meme", - "name": "Meme", - "symbol": "MEME", - "rank": 714, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "juld-julswap", - "name": "JulSwap", - "symbol": "JULD", - "rank": 715, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ncash-nucleus-vision", - "name": "Nucleus Vision", - "symbol": "NCASH", - "rank": 716, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rfuel-riodefi", - "name": "RioDeFi", - "symbol": "RFUEL", - "rank": 717, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prob-probit-token", - "name": "ProBit Token", - "symbol": "PROB", - "rank": 718, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wsg-wall-street-games", - "name": "Wall Street Games", - "symbol": "WSG", - "rank": 719, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "don-don-key", - "name": "Don-key", - "symbol": "DON", - "rank": 720, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fct-factom", - "name": "Factom", - "symbol": "FCT", - "rank": 721, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dbc-deepbrain-chain", - "name": "DeepBrain Chain", - "symbol": "DBC", - "rank": 722, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rht-reward-hunters-token", - "name": "Reward Hunters Token", - "symbol": "RHT", - "rank": 723, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cas-cashaa", - "name": "Cashaa", - "symbol": "CAS", - "rank": 724, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "signa-signa", - "name": "Signum", - "symbol": "SIGNA", - "rank": 725, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "palla-pallapay", - "name": "Pallapay", - "symbol": "PALLA", - "rank": 726, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "doggy-doggy", - "name": "Doggy", - "symbol": "DOGGY", - "rank": 727, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtrg-meter-governance", - "name": "Meter Governance", - "symbol": "MTRG", - "rank": 728, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xsn-stakenet", - "name": "Stakenet", - "symbol": "XSN", - "rank": 729, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fis-stafi", - "name": "Stafi", - "symbol": "FIS", - "rank": 730, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mod-modefi", - "name": "Modefi", - "symbol": "MOD", - "rank": 731, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cwv-cwv-chain", - "name": "CWV Chain", - "symbol": "CWV", - "rank": 732, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bone-bone-shibaswap", - "name": "Bone ShibaSwap", - "symbol": "BONE", - "rank": 733, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "palg-palgold", - "name": "PalGold", - "symbol": "PALG", - "rank": 734, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "enq-enecuum", - "name": "Enecuum", - "symbol": "ENQ", - "rank": 735, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "apm-apm-coin", - "name": "apM Coin", - "symbol": "APM", - "rank": 736, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etp-metaverse-etp", - "name": "Metaverse ETP", - "symbol": "ETP", - "rank": 737, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dafi-dafi-protocol", - "name": "Dafi Protocol", - "symbol": "DAFI", - "rank": 738, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "npxsxem-pundi-x-nem", - "name": "Pundi X NEM", - "symbol": "NPXSXEM", - "rank": 739, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mth-monetha", - "name": "Monetha", - "symbol": "MTH", - "rank": 740, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rdn-raiden-network-token", - "name": "Raiden Network Token", - "symbol": "RDN", - "rank": 741, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idv-idavoll-network", - "name": "Idavoll Network", - "symbol": "IDV", - "rank": 742, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nord-nord-finance", - "name": "Nord Finance", - "symbol": "NORD", - "rank": 743, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fina-defina-finance", - "name": "Defina Finance", - "symbol": "FINA", - "rank": 744, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nxt-nxt", - "name": "Nxt", - "symbol": "NXT", - "rank": 745, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cov-covesting", - "name": "Covesting", - "symbol": "COV", - "rank": 746, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dcn-dentacoin", - "name": "Dentacoin", - "symbol": "DCN", - "rank": 747, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mta-meta", - "name": "Meta", - "symbol": "MTA", - "rank": 748, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arch-archer-dao-governance-token", - "name": "Archer DAO Governance Token", - "symbol": "ARCH", - "rank": 749, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vrt-venus-reward-token", - "name": "Venus Reward Token", - "symbol": "VRT", - "rank": 750, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "doe-dogs-of-elon", - "name": "Dogs Of Elon", - "symbol": "DOE", - "rank": 751, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gth-gather", - "name": "Gather", - "symbol": "GTH", - "rank": 752, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fkx-knoxstertoken", - "name": "FortKnoxster", - "symbol": "FKX", - "rank": 753, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tulip-solfarm", - "name": "SolFarm", - "symbol": "TULIP", - "rank": 754, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bly-blocery", - "name": "Blocery", - "symbol": "BLY", - "rank": 755, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fxf-finxflo", - "name": "Finxflo", - "symbol": "FXF", - "rank": 756, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "geeq-geeq", - "name": "GEEQ", - "symbol": "GEEQ", - "rank": 757, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "juv-juventus-fan-token", - "name": "Juventus Fan Token", - "symbol": "JUV", - "rank": 758, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pzm-prizm", - "name": "PRIZM", - "symbol": "PZM", - "rank": 759, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lith-lith-token", - "name": "Lith Token", - "symbol": "LITH", - "rank": 760, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snow-snowblossom", - "name": "SnowBlossom", - "symbol": "SNOW", - "rank": 761, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "klo-kalao", - "name": "Kalao", - "symbol": "KLO", - "rank": 762, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "labs-labs-group", - "name": "LABS Group", - "symbol": "LABS", - "rank": 763, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gleec-gleec-coin", - "name": "Gleec Coin", - "symbol": "GLEEC", - "rank": 764, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ubxt-upbots", - "name": "UpBots", - "symbol": "UBXT", - "rank": 765, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctcn-contracoin", - "name": "Contracoin", - "symbol": "CTCN", - "rank": 766, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pets-micropets", - "name": "MicroPets", - "symbol": "PETS", - "rank": 767, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ten-tokenomy", - "name": "Tokenomy", - "symbol": "TEN", - "rank": 768, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bst-blockstamp", - "name": "BlockStamp", - "symbol": "BST", - "rank": 769, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "insur-insurace", - "name": "InsurAce", - "symbol": "INSUR", - "rank": 770, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bondly-bondly", - "name": "Bondly", - "symbol": "BONDLY", - "rank": 771, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pay-tenx", - "name": "TenX", - "symbol": "PAY", - "rank": 772, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ardx-ardcoin", - "name": "ArdCoin", - "symbol": "ARDX", - "rank": 773, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tra-trabzonspor-fan-token", - "name": "Trabzonspor Fan Token", - "symbol": "TRA", - "rank": 774, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anct-anchor", - "name": "Anchor", - "symbol": "ANCT", - "rank": 775, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swop-swop", - "name": "Swop", - "symbol": "SWOP", - "rank": 776, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vidya-vidya", - "name": "Vidya", - "symbol": "VIDYA", - "rank": 777, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sny-synthetify", - "name": "Synthetify", - "symbol": "SNY", - "rank": 778, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbc-cashbet-coin", - "name": "CashBet Coin", - "symbol": "CBC", - "rank": 779, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "repo-repo", - "name": "REPO", - "symbol": "REPO", - "rank": 780, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bir-birake", - "name": "Birake", - "symbol": "BIR", - "rank": 781, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "crpt-crypterium", - "name": "Crypterium", - "symbol": "CRPT", - "rank": 782, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "muse-muse", - "name": "Muse", - "symbol": "MUSE", - "rank": 783, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roobee-roobee", - "name": "ROOBEE", - "symbol": "ROOBEE", - "rank": 784, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bank-float-protocol", - "name": "Float Protocol", - "symbol": "BANK", - "rank": 785, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jade-jade-protocol", - "name": "Jade Protocol", - "symbol": "JADE", - "rank": 786, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bao-bao-finance", - "name": "Bao Finance", - "symbol": "BAO", - "rank": 787, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oly-olyseum", - "name": "Olyseum", - "symbol": "OLY", - "rank": 788, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bmon-binamon", - "name": "Binamon", - "symbol": "BMON", - "rank": 789, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lime-ime-lab", - "name": "iMe Lab", - "symbol": "LIME", - "rank": 790, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amlt-amlt", - "name": "AMLT", - "symbol": "AMLT", - "rank": 791, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bft-bnktothefuture", - "name": "BnkToTheFuture", - "symbol": "BFT", - "rank": 792, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nabox-nabox", - "name": "Nabox", - "symbol": "NABOX", - "rank": 793, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pbx-paribus", - "name": "Paribus", - "symbol": "PBX", - "rank": 794, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dyp-defi-yield-protocol", - "name": "DeFi Yield Protocol", - "symbol": "DYP", - "rank": 795, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rth-rotharium", - "name": "Rotharium", - "symbol": "RTH", - "rank": 796, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hord-hord", - "name": "HORD", - "symbol": "HORD", - "rank": 797, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wgr-wagerr", - "name": "Wagerr", - "symbol": "WGR", - "rank": 798, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "salt-salt", - "name": "SALT", - "symbol": "SALT", - "rank": 799, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "west-waves-enterprise", - "name": "Waves Enterprise", - "symbol": "WEST", - "rank": 800, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dpet-my-defi-pet", - "name": "My DeFi Pet", - "symbol": "DPET", - "rank": 801, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rena-warena", - "name": "Warena", - "symbol": "RENA", - "rank": 802, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yoyow-yoyow", - "name": "YOYOW", - "symbol": "YOYOW", - "rank": 803, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kingshib-king-shiba", - "name": "King Shiba", - "symbol": "KINGSHIB", - "rank": 804, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mda-moeda-loyalty-points", - "name": "Moeda Loyalty Points", - "symbol": "MDA", - "rank": 805, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ousd-origin-dollar", - "name": "Origin Dollar", - "symbol": "OUSD", - "rank": 806, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myst-mysterium", - "name": "Mysterium", - "symbol": "MYST", - "rank": 807, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "att-attila", - "name": "Attila", - "symbol": "ATT", - "rank": 808, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnx-genaro-network", - "name": "Genaro Network", - "symbol": "GNX", - "rank": 809, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bird-birdmoney", - "name": "Bird.Money", - "symbol": "BIRD", - "rank": 810, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lamb-lambda", - "name": "Lambda", - "symbol": "LAMB", - "rank": 811, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bin-binemon", - "name": "Binemon", - "symbol": "BIN", - "rank": 812, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mint-public-mint", - "name": "Public Mint", - "symbol": "MINT", - "rank": 813, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ppay-plasma-finance", - "name": "Plasma Finance", - "symbol": "PPAY", - "rank": 814, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swingby-swingby", - "name": "Swingby", - "symbol": "SWINGBY", - "rank": 815, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qlc-qlc-chain", - "name": "QLC Chain", - "symbol": "QLC", - "rank": 816, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moov-dotmoovs", - "name": "dotmoovs", - "symbol": "MOOV", - "rank": 817, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rfox-redfox-labs", - "name": "RedFOX Labs", - "symbol": "RFOX", - "rank": 818, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lz-launchzone", - "name": "LaunchZone", - "symbol": "LZ", - "rank": 819, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swise-stakewise", - "name": "StakeWise", - "symbol": "SWISE", - "rank": 820, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ibp-innovation-blockchain-payment", - "name": "Innovation Blockchain Payment", - "symbol": "IBP", - "rank": 821, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fear-fear", - "name": "Fear", - "symbol": "FEAR", - "rank": 822, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmb-gamb", - "name": "GAMB", - "symbol": "GMB", - "rank": 823, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbk-cobak-token", - "name": "Cobak Token", - "symbol": "CBK", - "rank": 824, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lua-lua-swap", - "name": "LuaSwap", - "symbol": "LUA", - "rank": 825, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "layer-unilayer", - "name": "UniLayer", - "symbol": "LAYER", - "rank": 826, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "0xbtc-0xbitcoin", - "name": "0xBitcoin", - "symbol": "0xBTC", - "rank": 827, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmd-diamond", - "name": "Diamond", - "symbol": "DMD", - "rank": 828, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btcp-bitcoin-private", - "name": "Bitcoin Private", - "symbol": "BTCP", - "rank": 829, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lcc-litecoin-cash", - "name": "Litecoin Cash", - "symbol": "LCC", - "rank": 830, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vibe-vibe", - "name": "VIBE", - "symbol": "VIBE", - "rank": 831, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eqz-equalizer", - "name": "Equalizer", - "symbol": "EQZ", - "rank": 832, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nlg-gulden", - "name": "Gulden", - "symbol": "NLG", - "rank": 833, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sylo-sylo", - "name": "Sylo", - "symbol": "SYLO", - "rank": 834, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "man-matrix-ai-network", - "name": "Matrix AI Network", - "symbol": "MAN", - "rank": 835, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stpl-stream-protocol", - "name": "Stream Protocol", - "symbol": "STPL", - "rank": 836, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "txl-tixl", - "name": "Tixl", - "symbol": "TXL", - "rank": 837, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vib-viberate", - "name": "Viberate", - "symbol": "VIB", - "rank": 838, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "standard-stakeborg-dao", - "name": "Stakeborg DAO", - "symbol": "STANDARD", - "rank": 839, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ace-acent", - "name": "Acent", - "symbol": "ACE", - "rank": 840, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpm-primecoin", - "name": "Primecoin", - "symbol": "XPM", - "rank": 841, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "oin-oin-finance", - "name": "OIN Finance", - "symbol": "OIN", - "rank": 842, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abyss-the-abyss", - "name": "Abyss Token", - "symbol": "ABYSS", - "rank": 843, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "esd-empty-set-dollar", - "name": "Empty Set Dollar", - "symbol": "ESD", - "rank": 844, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "launch-superlauncher", - "name": "SuperLauncher", - "symbol": "LAUNCH", - "rank": 845, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skill-cryptoblades", - "name": "CryptoBlades", - "symbol": "SKILL", - "rank": 846, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pai-pchain", - "name": "PCHAIN", - "symbol": "PI", - "rank": 847, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hpb-high-performance-blockchain", - "name": "High Performance Blockchain", - "symbol": "HPB", - "rank": 848, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hyve-hyve", - "name": "Hyve", - "symbol": "HYVE", - "rank": 849, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sata-signata", - "name": "Signata", - "symbol": "SATA", - "rank": 850, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fevr-realfevr", - "name": "RealFevr", - "symbol": "FEVR", - "rank": 851, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rio-realio-network", - "name": "Realio Network", - "symbol": "RIO", - "rank": 852, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lym-lympo", - "name": "Lympo", - "symbol": "LYM", - "rank": 853, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hvn-hive-project", - "name": "Hiveterminal Token", - "symbol": "HVN", - "rank": 854, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "satt-satt", - "name": "SaTT", - "symbol": "SATT", - "rank": 855, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pkr-polker", - "name": "Polker", - "symbol": "PKR", - "rank": 856, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lnc-linker-coin", - "name": "Linker Coin", - "symbol": "LNC", - "rank": 857, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "basid-basid-coin", - "name": "Basid Coin", - "symbol": "BASID", - "rank": 858, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfti-nft-index", - "name": "NFT Index", - "symbol": "NFTI", - "rank": 859, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tking-tiger-king", - "name": "Tiger King", - "symbol": "TKING", - "rank": 860, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbank-blockbank", - "name": "BlockBank", - "symbol": "BBANK", - "rank": 861, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scc-stakecubecoin", - "name": "StakeCubeCoin", - "symbol": "SCC", - "rank": 862, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "meetone-meetone", - "name": "MEET.ONE", - "symbol": "MEETONE", - "rank": 863, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fb-fenerbahce-token", - "name": "Fenerbahçe Token", - "symbol": "FB", - "rank": 864, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idle-idle", - "name": "IDLE", - "symbol": "IDLE", - "rank": 865, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bscs-bsc-station", - "name": "BSC Station", - "symbol": "BSCS", - "rank": 866, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wow-wownero", - "name": "Wownero", - "symbol": "WOW", - "rank": 867, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mrx-metrix", - "name": "Metrix", - "symbol": "MRX", - "rank": 868, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ooks-onooks", - "name": "Onooks", - "symbol": "OOKS", - "rank": 869, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "if-impossible-finance", - "name": "Impossible Finance", - "symbol": "IF", - "rank": 870, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "block-blocknet", - "name": "Blocknet", - "symbol": "BLOCK", - "rank": 871, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "1-up-1-up", - "name": "1-UP", - "symbol": "1-UP", - "rank": 872, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "new-newton", - "name": "Newton", - "symbol": "NEW", - "rank": 873, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "crp-crypton", - "name": "Crypton", - "symbol": "CRP", - "rank": 874, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "idna-idena", - "name": "Idena", - "symbol": "IDNA", - "rank": 875, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dec-decentr", - "name": "Decentr", - "symbol": "DEC", - "rank": 876, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dough-piedao-dough-v2", - "name": "PieDAO DOUGH v2", - "symbol": "DOUGH", - "rank": 877, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "naft-nafter", - "name": "Nafter", - "symbol": "NAFT", - "rank": 878, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "locg-locgame", - "name": "LOCGame", - "symbol": "LOCG", - "rank": 879, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emc2-einsteinium", - "name": "Einsteinium", - "symbol": "EMC2", - "rank": 880, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "opium-opium", - "name": "Opium", - "symbol": "OPIUM", - "rank": 881, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wow-wowswap", - "name": "WOWswap", - "symbol": "WOW", - "rank": 882, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcdt-evidenz", - "name": "EvidenZ", - "symbol": "BCDT", - "rank": 883, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "exm-exmo-coin", - "name": "EXMO Coin", - "symbol": "EXM", - "rank": 884, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "euno-euno", - "name": "EUNO", - "symbol": "EUNO", - "rank": 885, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mbn-mobilian-coin", - "name": "Mobilian Coin", - "symbol": "MBN", - "rank": 886, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cyc-cyclone-protocol", - "name": "Cyclone Protocol", - "symbol": "CYC", - "rank": 887, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "play-herocoin", - "name": "HEROcoin", - "symbol": "PLAY", - "rank": 888, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aly-ally", - "name": "Ally", - "symbol": "ALY", - "rank": 889, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swftc-swftcoin", - "name": "SwftCoin", - "symbol": "SWFTC", - "rank": 890, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rcn-ripio-credit-network", - "name": "Ripio Credit Network", - "symbol": "RCN", - "rank": 891, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cti-clintex-cti", - "name": "ClinTex CTi", - "symbol": "CTI", - "rank": 892, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "byn-beyond-finance", - "name": "Beyond Finance", - "symbol": "BYN", - "rank": 893, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "toko-tokoin", - "name": "Tokoin", - "symbol": "TOKO", - "rank": 894, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ryoshi-ryoshi-token", - "name": "Ryoshi Token", - "symbol": "RYOSHI", - "rank": 895, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "niox-autonio", - "name": "Autonio", - "symbol": "NIOX", - "rank": 896, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pma-pumapay", - "name": "PumaPay", - "symbol": "PMA", - "rank": 897, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zyn-zynecoin", - "name": "Zynecoin", - "symbol": "ZYN", - "rank": 898, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cgt-cache-gold", - "name": "CACHE Gold", - "symbol": "CGT", - "rank": 899, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ubq-ubiq", - "name": "Ubiq", - "symbol": "UBQ", - "rank": 900, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bolt-bolt", - "name": "BOLT", - "symbol": "BOLT", - "rank": 901, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cs-credits", - "name": "Credits", - "symbol": "CS", - "rank": 902, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dexa-dexa-coin", - "name": "DEXA COIN", - "symbol": "DEXA", - "rank": 903, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "trade-polytrade", - "name": "Polytrade", - "symbol": "TRADE", - "rank": 904, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sky-skycoin", - "name": "Skycoin", - "symbol": "SKY", - "rank": 905, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "white-whiteheart", - "name": "Whiteheart", - "symbol": "WHITE", - "rank": 906, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vlxpad-velaspad", - "name": "VelasPad", - "symbol": "VLXPAD", - "rank": 907, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zt-ztcoin", - "name": "ZTCoin", - "symbol": "ZT", - "rank": 908, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pai-project-pai", - "name": "Project Pai", - "symbol": "PAI", - "rank": 909, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sph-spheroid-universe", - "name": "Spheroid Universe", - "symbol": "SPH", - "rank": 910, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poa-poa-network", - "name": "POA", - "symbol": "POA", - "rank": 911, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "urqa-ureeqa", - "name": "UREEQA", - "symbol": "URQA", - "rank": 912, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csp-caspian", - "name": "Caspian", - "symbol": "CSP", - "rank": 913, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pwar-polkawar", - "name": "PolkaWar", - "symbol": "PWAR", - "rank": 914, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rhythm-rhythm", - "name": "Rhythm", - "symbol": "RHYTHM", - "rank": 915, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "masq-masq", - "name": "MASQ", - "symbol": "MASQ", - "rank": 916, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ghost-ghost", - "name": "Ghost", - "symbol": "GHOST", - "rank": 917, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bdp-big-data-protocol", - "name": "Big Data Protocol", - "symbol": "BDP", - "rank": 918, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kat-kambria", - "name": "Kambria", - "symbol": "KAT", - "rank": 919, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "efx-effect-network", - "name": "Effect Network", - "symbol": "EFX", - "rank": 920, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hero-step-hero", - "name": "Step Hero", - "symbol": "HERO", - "rank": 921, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "int-internet-node-token", - "name": "INT Chain", - "symbol": "INT", - "rank": 922, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "polis-polis", - "name": "Polis", - "symbol": "POLIS", - "rank": 923, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dinu-dogey-inu", - "name": "Dogey-Inu", - "symbol": "DINU", - "rank": 924, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brush-brush", - "name": "BRUSH", - "symbol": "BRUSH", - "rank": 925, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stnd-standard-protocol", - "name": "Standard Protocol", - "symbol": "STND", - "rank": 926, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swrv-swerve", - "name": "Swerve", - "symbol": "SWRV", - "rank": 927, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hzn-horizon-protocol", - "name": "Horizon Protocol", - "symbol": "HZN", - "rank": 928, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nyc-newyorkcoin", - "name": "NewYorkCoin", - "symbol": "NYC", - "rank": 929, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cor-coreto", - "name": "Coreto", - "symbol": "COR", - "rank": 930, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edg-edgeless", - "name": "Edgeless", - "symbol": "EDG", - "rank": 931, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpnet-xp-network", - "name": "XP Network", - "symbol": "XPNET", - "rank": 932, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmt-cybermiles", - "name": "CyberMiles", - "symbol": "CMT", - "rank": 933, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "octo-octofi", - "name": "OctoFi", - "symbol": "OCTO", - "rank": 934, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plu-pluton", - "name": "Pluton", - "symbol": "PLU", - "rank": 935, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "equad-quadrantprotocol", - "name": "QuadrantProtocol", - "symbol": "EQUAD", - "rank": 936, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "capp-cappasity", - "name": "Cappasity", - "symbol": "CAPP", - "rank": 937, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "buy-burency", - "name": "Burency", - "symbol": "BUY", - "rank": 938, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ara-adora-token", - "name": "Adora Token", - "symbol": "ARA", - "rank": 939, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdag-dagger", - "name": "Dagger", - "symbol": "XDAG", - "rank": 940, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "top-top", - "name": "TOP", - "symbol": "TOP", - "rank": 941, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitcny-bitcny", - "name": "bitCNY", - "symbol": "BITCNY", - "rank": 942, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "act-achain", - "name": "Achain", - "symbol": "ACT", - "rank": 943, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lqx-lqx-coin", - "name": "LQX COIN", - "symbol": "LQX", - "rank": 944, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kom-kommunitas", - "name": "Kommunitas", - "symbol": "KOM", - "rank": 945, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "terc-troneuroperewardcoin", - "name": "TronEuropeRewardCoin", - "symbol": "TERC", - "rank": 946, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xio-blockzero-labs", - "name": "Blockzero Labs", - "symbol": "XIO", - "rank": 947, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xfi-xfinance", - "name": "Xfinance", - "symbol": "XFI", - "rank": 948, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zap-zap", - "name": "Zap", - "symbol": "ZAP", - "rank": 949, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "html-htmlcoin", - "name": "HTMLCOIN", - "symbol": "HTML", - "rank": 950, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "via-viacoin", - "name": "Viacoin", - "symbol": "VIA", - "rank": 951, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fly-franklin", - "name": "Franklin", - "symbol": "FLY", - "rank": 952, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pawth-pawthereum", - "name": "Pawthereum", - "symbol": "PAWTH", - "rank": 953, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trtl-turtlecoin", - "name": "TurtleCoin", - "symbol": "TRTL", - "rank": 954, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kt-kuai-token", - "name": "Kuai Token", - "symbol": "KT", - "rank": 955, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prdx-paramountdax-token", - "name": "ParamountDax Token", - "symbol": "PRDX", - "rank": 956, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hot-hydro-protocol", - "name": "Hydro Protocol", - "symbol": "HOT", - "rank": 957, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ilc-ilcoin", - "name": "ILCOIN", - "symbol": "ILC", - "rank": 958, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "smart-smartcash", - "name": "SmartCash", - "symbol": "SMART", - "rank": 959, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "asko-asko", - "name": "Asko", - "symbol": "ASKO", - "rank": 960, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aspo-aspo-world", - "name": "ASPO World", - "symbol": "ASPO", - "rank": 961, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vrx-verox", - "name": "Verox", - "symbol": "VRX", - "rank": 962, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cv-carvertical", - "name": "carVertical", - "symbol": "CV", - "rank": 963, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "onion-deeponion", - "name": "DeepOnion", - "symbol": "ONION", - "rank": 964, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "prt-portion", - "name": "Portion", - "symbol": "PRT", - "rank": 965, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egt-egretia", - "name": "Egretia", - "symbol": "EGT", - "rank": 966, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "umx-unimex-network", - "name": "UniMex Network", - "symbol": "UMX", - "rank": 967, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ugas-ugas", - "name": "UGAS", - "symbol": "UGAS", - "rank": 968, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metax-metaversex", - "name": "MetaverseX", - "symbol": "METAX", - "rank": 969, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bid-topbidder", - "name": "TopBidder", - "symbol": "BID", - "rank": 970, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etho-ethoprotocol", - "name": "Etho Protocol", - "symbol": "ETHO", - "rank": 971, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "isikc-isiklar-coin", - "name": "Isiklar Coin", - "symbol": "ISIKC", - "rank": 972, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mwat-restart-energy-mwat", - "name": "Restart Energy MWAT", - "symbol": "MWAT", - "rank": 973, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "42-42-coin", - "name": "42-coin", - "symbol": "42", - "rank": 974, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "moon-moonswap", - "name": "MoonSwap", - "symbol": "MOON", - "rank": 975, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbx-mobiepay", - "name": "MobiePay", - "symbol": "MBX", - "rank": 976, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfiii-difyfinance", - "name": "Dify.Finance", - "symbol": "YFIII", - "rank": 977, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vnla-vanilla-network", - "name": "Vanilla Network", - "symbol": "VNLA", - "rank": 978, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grc-gridcoin", - "name": "GridCoin", - "symbol": "GRC", - "rank": 979, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "qrk-quark", - "name": "Quark", - "symbol": "QRK", - "rank": 980, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "phnx-phoenixdao", - "name": "PhoenixDAO", - "symbol": "PHNX", - "rank": 981, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "s4f-s4fe", - "name": "S4FE", - "symbol": "S4F", - "rank": 982, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trtt-trittium", - "name": "Trittium", - "symbol": "TRTT", - "rank": 983, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "baz-bazooka-token", - "name": "Bazooka Token", - "symbol": "BAZ", - "rank": 984, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mat-my-master-war", - "name": "My Master War", - "symbol": "MAT", - "rank": 985, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "8pay-8pay", - "name": "8Pay", - "symbol": "8PAY", - "rank": 986, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "2crz-2crazynft", - "name": "2crazyNFT", - "symbol": "2CRZ", - "rank": 987, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptf-powertrade-fuel", - "name": "PowerTrade Fuel", - "symbol": "PTF", - "rank": 988, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yee-yee", - "name": "YEE", - "symbol": "YEE", - "rank": 989, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lba-libra-credit", - "name": "Cred", - "symbol": "LBA", - "rank": 990, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vso-verso", - "name": "Verso", - "symbol": "VSO", - "rank": 991, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qcx-quickx-protocol", - "name": "QuickX Protocol", - "symbol": "QCX", - "rank": 992, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccx-conceal", - "name": "Conceal", - "symbol": "CCX", - "rank": 993, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "srn-sirin-labs-token", - "name": "SIRIN LABS Token", - "symbol": "SRN", - "rank": 994, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uca-uca-coin", - "name": "UCA Coin", - "symbol": "UCA", - "rank": 995, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bist-bistroo", - "name": "Bistroo", - "symbol": "BIST", - "rank": 996, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sta-statera", - "name": "STATERA", - "symbol": "STA", - "rank": 997, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcz-bitcoinz", - "name": "BitcoinZ", - "symbol": "BTCZ", - "rank": 998, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tyc-tycoon", - "name": "Tycoon", - "symbol": "TYC", - "rank": 999, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crp-cropperfinance", - "name": "CropperFinance", - "symbol": "CRP", - "rank": 1000, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tkl-tokel", - "name": "Tokel", - "symbol": "TKL", - "rank": 1001, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tsx-tradestars", - "name": "TradeStars", - "symbol": "TSX", - "rank": 1002, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "news-publish", - "name": "PUBLISH", - "symbol": "NEWS", - "rank": 1003, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smt-swarm-markets", - "name": "Swarm Markets", - "symbol": "SMT", - "rank": 1004, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flo-flo", - "name": "FLO", - "symbol": "FLO", - "rank": 1005, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "crbn-carbon", - "name": "Carbon", - "symbol": "CRBN", - "rank": 1006, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tnb-time-new-bank", - "name": "Time New Bank", - "symbol": "TNB", - "rank": 1007, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hit-hitchain", - "name": "HitChain", - "symbol": "HIT", - "rank": 1008, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lowb-loser-coin", - "name": "Loser Coin", - "symbol": "LOWB", - "rank": 1009, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1wo-1world", - "name": "1World", - "symbol": "1WO", - "rank": 1010, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bix-bibox-token", - "name": "Bibox Token", - "symbol": "BIX", - "rank": 1011, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tky-thekey", - "name": "THEKEY", - "symbol": "TKY", - "rank": 1012, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snc-suncontract", - "name": "SunContract", - "symbol": "SNC", - "rank": 1013, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddl-defi-degen-land", - "name": "DeFi Degen Land", - "symbol": "DDL", - "rank": 1014, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zoon-cryptozoon", - "name": "CryptoZoon", - "symbol": "ZOON", - "rank": 1015, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xsuter-xsuter", - "name": "xSuter", - "symbol": "XSUTER", - "rank": 1016, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cntr-centaur", - "name": "Centaur", - "symbol": "CNTR", - "rank": 1017, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vin-vinchain", - "name": "VINchain", - "symbol": "VIN", - "rank": 1018, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "maki-makiswap", - "name": "MakiSwap", - "symbol": "MAKI", - "rank": 1019, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fyd-fydcoin", - "name": "FYDcoin", - "symbol": "FYD", - "rank": 1020, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vex-vexanium", - "name": "Vexanium", - "symbol": "VEX", - "rank": 1021, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfx-safex-cash", - "name": "Safex Cash", - "symbol": "SFX", - "rank": 1022, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "itc-iot-chain", - "name": "IoT Chain", - "symbol": "ITC", - "rank": 1023, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmat-gowithmi", - "name": "GoWithMi", - "symbol": "GMAT", - "rank": 1024, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zuki-zuki-moba", - "name": "Zuki Moba", - "symbol": "ZUKI", - "rank": 1025, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nyzo-nyzo", - "name": "Nyzo", - "symbol": "NYZO", - "rank": 1026, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ftc-feathercoin", - "name": "Feathercoin", - "symbol": "FTC", - "rank": 1027, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "axpr-axpire", - "name": "aXpire", - "symbol": "AXPR", - "rank": 1028, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lanc-lanceria", - "name": "Lanceria", - "symbol": "LANC", - "rank": 1029, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icnq-iconiq-lab-token", - "name": "Iconiq Lab Token", - "symbol": "ICNQ", - "rank": 1030, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trade-unitrade", - "name": "Unitrade", - "symbol": "TRADE", - "rank": 1031, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "openc-open-platform", - "name": "Open Platform", - "symbol": "OPENC", - "rank": 1032, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xcash-x-cash", - "name": "X-CASH", - "symbol": "XCASH", - "rank": 1033, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rxs-rune-shards", - "name": "Rune Shards", - "symbol": "RXS", - "rank": 1034, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plf-playfuel", - "name": "PlayFuel", - "symbol": "PLF", - "rank": 1035, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitd-8bit-doge", - "name": "8Bit Doge", - "symbol": "BITD", - "rank": 1036, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vidy-vidy", - "name": "VIDY", - "symbol": "VIDY", - "rank": 1037, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xmc-monero-classic", - "name": "Monero Classic", - "symbol": "XMC", - "rank": 1038, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "grlc-garlicoin", - "name": "Garlicoin", - "symbol": "GRLC", - "rank": 1039, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sumo-sumokoin", - "name": "Sumokoin", - "symbol": "SUMO", - "rank": 1040, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dop-drops-ownership-power", - "name": "Drops Ownership Power", - "symbol": "DOP", - "rank": 1041, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dos-dos-network", - "name": "DOS Network", - "symbol": "DOS", - "rank": 1042, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "appc-appcoins", - "name": "AppCoins", - "symbol": "APPC", - "rank": 1043, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpn-pantheon-x", - "name": "PANTHEON X", - "symbol": "XPN", - "rank": 1044, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aeon-aeon", - "name": "Aeon", - "symbol": "AEON", - "rank": 1045, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pla-planet", - "name": "PLANET", - "symbol": "PLA", - "rank": 1046, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hpt-huobi-pool-token", - "name": "Huobi Pool Token", - "symbol": "HPT", - "rank": 1047, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kaby-kaby-arena", - "name": "Kaby Arena", - "symbol": "KABY", - "rank": 1048, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nax-nextdao", - "name": "NextDAO", - "symbol": "NAX", - "rank": 1049, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lml-link-machine-learning", - "name": "Link Machine Learning", - "symbol": "LML", - "rank": 1050, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dows-shadows", - "name": "Shadows", - "symbol": "DOWS", - "rank": 1051, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ess-essentia", - "name": "Essentia", - "symbol": "ESS", - "rank": 1052, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lrn-loopring-neo", - "name": "Loopring [NEO]", - "symbol": "LRN", - "rank": 1053, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dax-daex", - "name": "DAEX", - "symbol": "DAX", - "rank": 1054, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "let-linkeye", - "name": "LinkEye", - "symbol": "LET", - "rank": 1055, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gdao-governor-dao", - "name": "Governor DAO", - "symbol": "GDAO", - "rank": 1056, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smt-smartmesh", - "name": "SmartMesh", - "symbol": "SMT", - "rank": 1057, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mir-mir-coin", - "name": "MIR COIN", - "symbol": "MIR", - "rank": 1058, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gvt-genesis-vision", - "name": "Genesis Vision", - "symbol": "GVT", - "rank": 1059, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zora-zoracles", - "name": "Zoracles", - "symbol": "ZORA", - "rank": 1060, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spwn-bitspawn", - "name": "Bitspawn", - "symbol": "SPWN", - "rank": 1061, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cns-centric-swap", - "name": "Centric Swap", - "symbol": "CNS", - "rank": 1062, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ost-ost", - "name": "OST", - "symbol": "OST", - "rank": 1063, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gen-daostack", - "name": "DAOstack", - "symbol": "GEN", - "rank": 1064, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oce-oceanex-token", - "name": "OceanEx Token", - "symbol": "OCE", - "rank": 1065, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gsc-global-social-chain", - "name": "Global Social Chain", - "symbol": "GSC", - "rank": 1066, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fnt-falcon-project", - "name": "Falcon Project", - "symbol": "FNT", - "rank": 1067, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "auc-auctus", - "name": "Auctus", - "symbol": "AUC", - "rank": 1068, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drf-drife", - "name": "Drife", - "symbol": "DRF", - "rank": 1069, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cloak-cloakcoin", - "name": "CloakCoin", - "symbol": "CLOAK", - "rank": 1070, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "soc-all-sports", - "name": "All Sports", - "symbol": "SOC", - "rank": 1071, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ff-forefront", - "name": "Forefront", - "symbol": "FF", - "rank": 1072, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "efc-everton-fan-token", - "name": "Everton Fan Token", - "symbol": "EFC", - "rank": 1073, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kton-darwinia-commitment-token", - "name": "Darwinia Commitment Token", - "symbol": "KTON", - "rank": 1074, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mx-marsx", - "name": "MarsX", - "symbol": "MX", - "rank": 1075, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shopx-splyt", - "name": "Splyt", - "symbol": "SHOPX", - "rank": 1076, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dgtx-digitex-futures", - "name": "Digitex Futures", - "symbol": "DGTX", - "rank": 1077, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plot-plotx", - "name": "PlotX", - "symbol": "PLOT", - "rank": 1078, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "syp-sypool", - "name": "Sypool", - "symbol": "SYP", - "rank": 1079, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blk-blackcoin", - "name": "BlackCoin", - "symbol": "BLK", - "rank": 1080, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "watch-yieldwatch", - "name": "Yieldwatch", - "symbol": "WATCH", - "rank": 1081, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aca-aca-token", - "name": "ACA Token", - "symbol": "ACA", - "rank": 1082, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpc-cpchain", - "name": "CPChain", - "symbol": "CPC", - "rank": 1083, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nrv-nerve-finance", - "name": "Nerve Finance", - "symbol": "NRV", - "rank": 1084, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snow-snowswap", - "name": "Snowswap", - "symbol": "SNOW", - "rank": 1085, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bath-battle-hero", - "name": "Battle Hero", - "symbol": "BATH", - "rank": 1086, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ocn-odyssey", - "name": "Odyssey", - "symbol": "OCN", - "rank": 1087, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xaur-xaurum", - "name": "Xaurum", - "symbol": "XAUR", - "rank": 1088, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uip-unlimitedip", - "name": "UnlimitedIP", - "symbol": "UIP", - "rank": 1089, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ut-ulord", - "name": "Ulord", - "symbol": "UT", - "rank": 1090, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tips-fedoracoin", - "name": "FedoraCoin", - "symbol": "TIPS", - "rank": 1091, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ame-amepay", - "name": "Amepay", - "symbol": "AME", - "rank": 1092, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "res-resfinex-token", - "name": "Resfinex Token", - "symbol": "RES", - "rank": 1093, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rmt-sureremit", - "name": "SureRemit", - "symbol": "RMT", - "rank": 1094, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ait-aichain", - "name": "AICHAIN", - "symbol": "AIT", - "rank": 1095, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snet-snetwork", - "name": "Snetwork", - "symbol": "SNET", - "rank": 1096, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tkp-tokpie", - "name": "TOKPIE", - "symbol": "TKP", - "rank": 1097, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcash-mcashchain", - "name": "Mcashchain", - "symbol": "MCASH", - "rank": 1098, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gtc-gamecom", - "name": "Game.com", - "symbol": "GTC", - "rank": 1099, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmg-dmm-governance", - "name": "DMM: Governance", - "symbol": "DMG", - "rank": 1100, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dgx-digix-gold-token", - "name": "Digix Gold Token", - "symbol": "DGX", - "rank": 1101, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spo-spores-network", - "name": "Spores Network", - "symbol": "SPO", - "rank": 1102, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdn-digitalnote", - "name": "DigitalNote", - "symbol": "XDN", - "rank": 1103, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "eng-enigma", - "name": "Enigma", - "symbol": "ENG", - "rank": 1104, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cora-corrafinance", - "name": "Corra.Finance", - "symbol": "CORA", - "rank": 1105, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "llg-lucid-lands", - "name": "Lucid Lands", - "symbol": "LLG", - "rank": 1106, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "owc-oduwa", - "name": "ODUWA", - "symbol": "OWC", - "rank": 1107, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sync-sync-network", - "name": "SYNC Network", - "symbol": "SYNC", - "rank": 1108, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtc-docademic", - "name": "doc.com Token", - "symbol": "MTC", - "rank": 1109, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cvn-cvcoin", - "name": "CVCoin", - "symbol": "CVN", - "rank": 1110, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "world-world-token", - "name": "World Token", - "symbol": "WORLD", - "rank": 1111, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aac-acute-angle-cloud", - "name": "Acute Angle Cloud", - "symbol": "AAC", - "rank": 1112, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mds-medishares", - "name": "MediShares", - "symbol": "MDS", - "rank": 1113, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xmy-myriad", - "name": "Myriad", - "symbol": "XMY", - "rank": 1114, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sub-subme", - "name": "Subme", - "symbol": "SUB", - "rank": 1115, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "paint-murall", - "name": "MurAll", - "symbol": "PAINT", - "rank": 1116, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flixx-flixxo", - "name": "Flixxo", - "symbol": "FLIXX", - "rank": 1117, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adb-adbank", - "name": "adbank", - "symbol": "ADB", - "rank": 1118, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dac-davinci-coin", - "name": "Davinci Coin", - "symbol": "DAC", - "rank": 1119, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skm-skrumble-network", - "name": "Skrumble Network", - "symbol": "SKM", - "rank": 1120, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vld-vetri", - "name": "Vetri", - "symbol": "VLD", - "rank": 1121, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tera-tera", - "name": "TERA", - "symbol": "TERA", - "rank": 1122, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bison-bishares", - "name": "BiShares", - "symbol": "BISON", - "rank": 1123, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnu-bytenext", - "name": "ByteNext", - "symbol": "BNU", - "rank": 1124, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xst-stealth", - "name": "Stealth", - "symbol": "XST", - "rank": 1125, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "earnx-earnx", - "name": "EarnX", - "symbol": "EARNX", - "rank": 1126, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "avxt-avaxtars-token", - "name": "Avaxtars Token", - "symbol": "AVXT", - "rank": 1127, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvl-revival", - "name": "REVIVAL", - "symbol": "RVL", - "rank": 1128, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fund-unification", - "name": "Unification", - "symbol": "FUND", - "rank": 1129, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pny-peony", - "name": "Peony", - "symbol": "PNY", - "rank": 1130, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rocks-rocki", - "name": "Rocki", - "symbol": "ROCKS", - "rank": 1131, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mvp-merculet", - "name": "Merculet", - "symbol": "MVP", - "rank": 1132, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trio-tripio", - "name": "Tripio", - "symbol": "TRIO", - "rank": 1133, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "veo-amoveo", - "name": "Amoveo", - "symbol": "VEO", - "rank": 1134, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gof-golff", - "name": "Golff", - "symbol": "GOF", - "rank": 1135, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "buni-bunicorn", - "name": "Bunicorn", - "symbol": "BUNI", - "rank": 1136, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ieth-iethereum", - "name": "iEthereum", - "symbol": "IETH", - "rank": 1137, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtn-medicalchain", - "name": "Medicalchain", - "symbol": "MTN", - "rank": 1138, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fsw-falconswap", - "name": "Falconswap", - "symbol": "FSW", - "rank": 1139, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mpg-max-property-group", - "name": "Max Property Group", - "symbol": "MPG", - "rank": 1140, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lthn-lethean", - "name": "Lethean", - "symbol": "LTHN", - "rank": 1141, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pat-patron", - "name": "Patron", - "symbol": "PAT", - "rank": 1142, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wings-wings", - "name": "Wings", - "symbol": "WINGS", - "rank": 1143, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swm-swarm", - "name": "Swarm", - "symbol": "SWM", - "rank": 1144, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xbc-bitcoin-plus", - "name": "Bitcoin Plus", - "symbol": "XBC", - "rank": 1145, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "par-parachute", - "name": "Parachute", - "symbol": "PAR", - "rank": 1146, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hydro-hydro", - "name": "Hydro", - "symbol": "HYDRO", - "rank": 1147, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "airi-airight", - "name": "aiRight", - "symbol": "AIRI", - "rank": 1148, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kcash-kcash", - "name": "Kcash", - "symbol": "KCASH", - "rank": 1149, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mct-master-contract-token", - "name": "Master Contract Token", - "symbol": "MCT", - "rank": 1150, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xtl-stellite", - "name": "Scala", - "symbol": "XLA", - "rank": 1151, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "evx-everex", - "name": "Everex", - "symbol": "EVX", - "rank": 1152, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cure-curecoin", - "name": "Curecoin", - "symbol": "CURE", - "rank": 1153, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pgt-polyient-games-governance-token", - "name": "Polyient Games Governance Token", - "symbol": "PGT", - "rank": 1154, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eved-evedo", - "name": "Evedo", - "symbol": "EVED", - "rank": 1155, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdt-stake-dao", - "name": "Stake DAO", - "symbol": "SDT", - "rank": 1156, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etna-etna-network", - "name": "ETNA Network", - "symbol": "ETNA", - "rank": 1157, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "got-parkingo", - "name": "ParkinGo", - "symbol": "GOT", - "rank": 1158, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bac-basis-cash", - "name": "Basis Cash", - "symbol": "BAC", - "rank": 1159, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pxl-pixel", - "name": "PIXEL", - "symbol": "PXL", - "rank": 1160, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpc-experience-chain", - "name": "eXPerience Chain", - "symbol": "XPC", - "rank": 1161, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kangal-kangal", - "name": "Kangal", - "symbol": "KANGAL", - "rank": 1162, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vnxlu-vnx-exchange", - "name": "VNX Exchange", - "symbol": "VNXLU", - "rank": 1163, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hmq-humaniq", - "name": "Humaniq", - "symbol": "HMQ", - "rank": 1164, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddd-scryinfo", - "name": "Scry.info", - "symbol": "DDD", - "rank": 1165, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ok-okcash", - "name": "OKCash", - "symbol": "OK", - "rank": 1166, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bry-berry-data", - "name": "Berry Data", - "symbol": "BRY", - "rank": 1167, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "n1-nftify", - "name": "NFTify", - "symbol": "N1", - "rank": 1168, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edr-endor-protocol", - "name": "Endor Protocol", - "symbol": "EDR", - "rank": 1169, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "corn-cornichon", - "name": "Cornichon", - "symbol": "CORN", - "rank": 1170, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfy-non-fungible-yearn", - "name": "Non-Fungible Yearn", - "symbol": "NFY", - "rank": 1171, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xmx-xmax", - "name": "XMax", - "symbol": "XMX", - "rank": 1172, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "argon-argon", - "name": "Argon", - "symbol": "ARGON", - "rank": 1173, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phr-phore", - "name": "Phore", - "symbol": "PHR", - "rank": 1174, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "step-step-finance", - "name": "Step Finance", - "symbol": "STEP", - "rank": 1175, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tup-tenup", - "name": "Tenup", - "symbol": "TUP", - "rank": 1176, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bxc-bonuscloud", - "name": "BonusCloud", - "symbol": "BXC", - "rank": 1177, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "banker-bankerdoge", - "name": "BankerDoge", - "symbol": "BANKER", - "rank": 1178, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvc-ravencoin-classic", - "name": "Ravencoin Classic", - "symbol": "RVC", - "rank": 1179, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dav-dav-coin", - "name": "DAV Coin", - "symbol": "DAV", - "rank": 1180, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kdg-kingdom-game-40", - "name": "Kingdom Game 4.0", - "symbol": "KDG", - "rank": 1181, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krb-karbo", - "name": "Karbo", - "symbol": "KRB", - "rank": 1182, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ioen-internet-of-energy-network", - "name": "Internet of Energy Network", - "symbol": "IOEN", - "rank": 1183, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpo-cryptopolis", - "name": "Cryptopolis", - "symbol": "CPO", - "rank": 1184, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pym-playermon", - "name": "Playermon", - "symbol": "PYM", - "rank": 1185, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "name-polkadomain", - "name": "PolkaDomain", - "symbol": "NAME", - "rank": 1186, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bomb-bomb", - "name": "BOMB", - "symbol": "BOMB", - "rank": 1187, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nash-neoworld-cash", - "name": "NeoWorld Cash", - "symbol": "NASH", - "rank": 1188, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atp-atlas-protocol", - "name": "Atlas Protocol", - "symbol": "ATP", - "rank": 1189, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moma-mochi-market", - "name": "Mochi Market", - "symbol": "MOMA", - "rank": 1190, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ldfi-lendefi", - "name": "Lendefi", - "symbol": "LDFI", - "rank": 1191, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blue-blue-protocol", - "name": "Blue Protocol", - "symbol": "BLUE", - "rank": 1192, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lobs-lobstex", - "name": "Lobstex", - "symbol": "LOBS", - "rank": 1193, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pst-primas", - "name": "Primas", - "symbol": "PST", - "rank": 1194, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crw-crown", - "name": "Crown", - "symbol": "CRW", - "rank": 1195, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "toc-touchcon", - "name": "TouchCon", - "symbol": "TOC", - "rank": 1196, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mgo-mobilego", - "name": "MobileGo", - "symbol": "MGO", - "rank": 1197, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ss-sharder", - "name": "Sharder", - "symbol": "SS", - "rank": 1198, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "road-yellow-road", - "name": "Yellow Road", - "symbol": "ROAD", - "rank": 1199, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gard-hashgard", - "name": "Hashgard", - "symbol": "GARD", - "rank": 1200, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eland-etherland", - "name": "Etherland", - "symbol": "ELAND", - "rank": 1201, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcug-blockchain-cuties-universe-governance", - "name": "Blockchain Cuties Universe Governance", - "symbol": "BCUG", - "rank": 1202, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ktlyo-katalyo", - "name": "Katalyo", - "symbol": "KTLYO", - "rank": 1203, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rblx-rublix", - "name": "Rublix", - "symbol": "RBLX", - "rank": 1204, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nos-nos", - "name": "nOS", - "symbol": "NOS", - "rank": 1205, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zcl-zclassic", - "name": "ZClassic", - "symbol": "ZCL", - "rank": 1206, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kampay-kampay", - "name": "Kampay", - "symbol": "KAMPAY", - "rank": 1207, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xct-citadelone", - "name": "Citadel.one", - "symbol": "XCT", - "rank": 1208, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "excc-exchangecoin", - "name": "ExchangeCoin", - "symbol": "EXCC", - "rank": 1209, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bits-bitswift", - "name": "Bitswift", - "symbol": "BITS", - "rank": 1210, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sig-xsigma", - "name": "xSigma", - "symbol": "SIG", - "rank": 1211, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chx-own", - "name": "Own", - "symbol": "CHX", - "rank": 1212, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eosdac-eosdac", - "name": "eosDAC", - "symbol": "EOSDAC", - "rank": 1213, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hgold-hollygold", - "name": "HollyGold", - "symbol": "HGOLD", - "rank": 1214, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "four-4thpillar-technologies", - "name": "4thpillar technologies", - "symbol": "FOUR", - "rank": 1215, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbx-netbox-coin", - "name": "Netbox Coin", - "symbol": "NBX", - "rank": 1216, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mer-mercury", - "name": "Mercury", - "symbol": "MER", - "rank": 1217, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sake-saketoken", - "name": "SakeToken", - "symbol": "SAKE", - "rank": 1218, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pink-pinkcoin", - "name": "Pinkcoin", - "symbol": "PINK", - "rank": 1219, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "chat-chatcoin", - "name": "ChatCoin", - "symbol": "CHAT", - "rank": 1220, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ruff-ruff", - "name": "Ruff", - "symbol": "RUFF", - "rank": 1221, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "htn-heart-number", - "name": "Heart Number", - "symbol": "HTN", - "rank": 1222, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coin-coin-artist", - "name": "Coin Artist", - "symbol": "COIN", - "rank": 1223, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ppblz-pepemon-pepeballs", - "name": "Pepemon Pepeballs", - "symbol": "PPBLZ", - "rank": 1224, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ethv-ethverse", - "name": "Ethverse", - "symbol": "ETHV", - "rank": 1225, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blkc-blackhat", - "name": "BlackHat", - "symbol": "BLKC", - "rank": 1226, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gum-gourmet-galaxy", - "name": "Gourmet Galaxy", - "symbol": "GUM", - "rank": 1227, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qch-qchi", - "name": "QChi", - "symbol": "QCH", - "rank": 1228, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jup-jupiter", - "name": "Jupiter", - "symbol": "JUP", - "rank": 1229, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tube-bittube", - "name": "BitTube", - "symbol": "TUBE", - "rank": 1230, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "foc-theforce-trade", - "name": "TheForce Trade", - "symbol": "FOC", - "rank": 1231, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tth-tetrahedra", - "name": "TetraHedra", - "symbol": "TTH", - "rank": 1232, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "buy-buyingcom", - "name": "Buying.com", - "symbol": "BUY", - "rank": 1233, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shield-shield-protocol", - "name": "Shield Protocol", - "symbol": "SHIELD", - "rank": 1234, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "welt-fabwelt", - "name": "Fabwelt", - "symbol": "WELT", - "rank": 1235, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ubex-ubex", - "name": "Ubex", - "symbol": "UBEX", - "rank": 1236, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rem-remme", - "name": "Remme", - "symbol": "REM", - "rank": 1237, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kit-dexkit", - "name": "DexKit", - "symbol": "KIT", - "rank": 1238, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fera-fera", - "name": "Fera", - "symbol": "FERA", - "rank": 1239, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "payb-paybswap", - "name": "Paybswap", - "symbol": "PAYB", - "rank": 1240, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uuu-u-network", - "name": "U Network", - "symbol": "UUU", - "rank": 1241, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dyna-dynamix", - "name": "Dynamix", - "symbol": "DYNA", - "rank": 1242, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cofi-coinfi", - "name": "CoinFi", - "symbol": "COFI", - "rank": 1243, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "heroegg-herofi", - "name": "HeroFi", - "symbol": "HEROEGG", - "rank": 1244, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drc-digital-reserve-currency", - "name": "Digital Reserve Currency", - "symbol": "DRC", - "rank": 1245, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "htz-hertz-network", - "name": "Hertz Network", - "symbol": "HTZ", - "rank": 1246, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dta-data", - "name": "DATA", - "symbol": "DTA", - "rank": 1247, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yummy-yummy", - "name": "Yummy", - "symbol": "YUMMY", - "rank": 1248, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bob-bobs-repair", - "name": "Bob`s Repair", - "symbol": "BOB", - "rank": 1249, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbt-robust-token", - "name": "Robust Token", - "symbol": "RBT", - "rank": 1250, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "warp-warp-finance", - "name": "Warp Finance", - "symbol": "WARP", - "rank": 1251, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "2key-2keynetwork", - "name": "2key.network", - "symbol": "2KEY", - "rank": 1252, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shoe-shoefy", - "name": "ShoeFy", - "symbol": "SHOE", - "rank": 1253, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tch-tigercash", - "name": "TigerCash", - "symbol": "TCH", - "rank": 1254, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tango-keytango", - "name": "keyTango", - "symbol": "TANGO", - "rank": 1255, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bull-buysell", - "name": "BuySell", - "symbol": "BULL", - "rank": 1256, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "peri-peri-finance", - "name": "PERI Finance", - "symbol": "PERI", - "rank": 1257, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "red-red", - "name": "RED", - "symbol": "RED", - "rank": 1258, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vnt-vntchain", - "name": "VNTchain", - "symbol": "VNT", - "rank": 1259, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ceres-ceres", - "name": "Ceres", - "symbol": "CERES", - "rank": 1260, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jar-jarvis", - "name": "Jarvis+", - "symbol": "JAR", - "rank": 1261, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "afen-afen-blockchain", - "name": "AFEN Blockchain", - "symbol": "AFEN", - "rank": 1262, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crx-cryptex", - "name": "Cryptex", - "symbol": "CRX", - "rank": 1263, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctt-cryptotycoon", - "name": "CryptoTycoon", - "symbol": "CTT", - "rank": 1264, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elec-electrifyasia", - "name": "Electrify.Asia", - "symbol": "ELEC", - "rank": 1265, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfuel-sparkpoint-fuel", - "name": "SparkPoint Fuel", - "symbol": "SFUEL", - "rank": 1266, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ogo-origo", - "name": "Origo", - "symbol": "OGO", - "rank": 1267, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "htdf-orient-walt", - "name": "Orient Walt", - "symbol": "HTDF", - "rank": 1268, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "banca-banca", - "name": "Banca", - "symbol": "BANCA", - "rank": 1269, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pgn-pigeoncoin", - "name": "Pigeoncoin", - "symbol": "PGN", - "rank": 1270, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cnb-coinsbit-token", - "name": "Coinsbit Token", - "symbol": "CNB", - "rank": 1271, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zpb-zebi", - "name": "Zebi Public Blockchain", - "symbol": "ZPB", - "rank": 1272, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "exe-8x8-protocol", - "name": "8X8 Protocol", - "symbol": "EXE", - "rank": 1273, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ama-mrweb-finance", - "name": "MrWeb Finance", - "symbol": "AMA", - "rank": 1274, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tcc-the-champcoin", - "name": "The ChampCoin", - "symbol": "TCC", - "rank": 1275, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lxt-litex", - "name": "Litex", - "symbol": "LXT", - "rank": 1276, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "box-contentbox", - "name": "ContentBox", - "symbol": "BOX", - "rank": 1277, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfs-defis-network", - "name": "Defis Network", - "symbol": "DFS", - "rank": 1278, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rnt-oneroot-network", - "name": "OneRoot Network", - "symbol": "RNT", - "rank": 1279, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bto-bottos", - "name": "Bottos", - "symbol": "BTO", - "rank": 1280, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbot-naka-bodhi-token", - "name": "Naka Bodhi Token", - "symbol": "NBOT", - "rank": 1281, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fdz-friendz", - "name": "Friendz", - "symbol": "FDZ", - "rank": 1282, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dyt-doyourtip", - "name": "DoYourTip", - "symbol": "DYT", - "rank": 1283, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brew-cafeswap-token", - "name": "CafeSwap Token", - "symbol": "BREW", - "rank": 1284, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "donut-donut", - "name": "Donut", - "symbol": "DONUT", - "rank": 1285, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eql-equal", - "name": "Equal", - "symbol": "EQL", - "rank": 1286, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bc-bitcoin-confidential", - "name": "Bitcoin Confidential", - "symbol": "BC", - "rank": 1287, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lcs-localcoinswap", - "name": "LocalCoinSwap", - "symbol": "LCS", - "rank": 1288, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arq-arqma", - "name": "Arqma", - "symbol": "ARQ", - "rank": 1289, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bunny-pancake-bunny", - "name": "Pancake Bunny", - "symbol": "BUNNY", - "rank": 1290, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phx-phoenix-token", - "name": "Phoenix Token", - "symbol": "PHX", - "rank": 1291, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ncc-neurochain", - "name": "NeuroChain", - "symbol": "NCC", - "rank": 1292, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fuel-etherparty", - "name": "Etherparty", - "symbol": "FUEL", - "rank": 1293, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wnt-wicrypt", - "name": "Wicrypt", - "symbol": "WNT", - "rank": 1294, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xtp-tap", - "name": "Tap", - "symbol": "XTP", - "rank": 1295, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dpy-delphy", - "name": "Delphy", - "symbol": "DPY", - "rank": 1296, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bree-cbdao", - "name": "CBDAO", - "symbol": "BREE", - "rank": 1297, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drt-domraider", - "name": "DomRaider", - "symbol": "DRT", - "rank": 1298, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "catt-catex-token", - "name": "Catex Token", - "symbol": "CATT", - "rank": 1299, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yield-yield-protocol", - "name": "Yield Protocol", - "symbol": "YIELD", - "rank": 1300, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "seth-sether", - "name": "Sether", - "symbol": "SETH", - "rank": 1301, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnf-bonfi", - "name": "BonFi", - "symbol": "BNF", - "rank": 1302, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prare-polkarare", - "name": "Polkarare", - "symbol": "PRARE", - "rank": 1303, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pasc-pascal-coin", - "name": "Pascal", - "symbol": "PASC", - "rank": 1304, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "iht-iht-real-estate-protocol", - "name": "IHT Real Estate Protocol", - "symbol": "IHT", - "rank": 1305, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jet-jetcoin", - "name": "Jetcoin", - "symbol": "JET", - "rank": 1306, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ac-acoconut", - "name": "ACoconut", - "symbol": "AC", - "rank": 1307, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rating-dprating", - "name": "DPRating", - "symbol": "RATING", - "rank": 1308, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bds-big-digital-shares", - "name": "Big Digital Shares", - "symbol": "BDS", - "rank": 1309, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drc-dracula-token", - "name": "Dracula Token", - "symbol": "DRC", - "rank": 1310, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coll-collateral-pay", - "name": "Collateral Pay", - "symbol": "COLL", - "rank": 1311, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kgo-kiwigo", - "name": "Kiwigo", - "symbol": "KGO", - "rank": 1312, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tnc-trinity-network-credit", - "name": "Trinity Network Credit", - "symbol": "TNC", - "rank": 1313, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axis-axis-defi", - "name": "Axis DeFi", - "symbol": "AXIS", - "rank": 1314, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zax-zax", - "name": "ZAX", - "symbol": "ZAX", - "rank": 1315, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ekt-educare", - "name": "EDUCare", - "symbol": "EKT", - "rank": 1316, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nvc-novacoin", - "name": "Novacoin", - "symbol": "NVC", - "rank": 1317, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "az-azbit", - "name": "Azbit", - "symbol": "AZ", - "rank": 1318, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tns-transcodium", - "name": "Transcodium", - "symbol": "TNS", - "rank": 1319, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zpt-zeepin", - "name": "Zeepin", - "symbol": "ZPT", - "rank": 1320, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecom-omnitude", - "name": "Omnitude", - "symbol": "ECOM", - "rank": 1321, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tent-tent", - "name": "TENT", - "symbol": "TENT", - "rank": 1322, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gem-gems", - "name": "Gems", - "symbol": "GEM", - "rank": 1323, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trl-triall", - "name": "Triall", - "symbol": "TRL", - "rank": 1324, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vox-voxfinance", - "name": "Vox.Finance", - "symbol": "VOX", - "rank": 1325, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "balpha-balpha", - "name": "bAlpha", - "symbol": "BALPHA", - "rank": 1326, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yce-myce", - "name": "MYCE", - "symbol": "YCE", - "rank": 1327, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ink-ink", - "name": "Ink", - "symbol": "INK", - "rank": 1328, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etgp-ethereum-gold-project", - "name": "Ethereum Gold Project", - "symbol": "ETGP", - "rank": 1329, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctask-cryptotask", - "name": "CryptoTask", - "symbol": "CTASK", - "rank": 1330, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rnbw-halodao", - "name": "HaloDAO", - "symbol": "RNBW", - "rank": 1331, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "th-team-heretics-fan-token", - "name": "Team Heretics Fan Token", - "symbol": "TH", - "rank": 1332, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "open-open-governance-token", - "name": "OPEN Governance Token", - "symbol": "OPEN", - "rank": 1333, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pvt-pivot-token", - "name": "Pivot Token", - "symbol": "PVT", - "rank": 1334, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ducato-ducato-protocol-token", - "name": "Ducato Protocol Token", - "symbol": "DUCATO", - "rank": 1335, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gbx-gobyte", - "name": "GoByte", - "symbol": "GBX", - "rank": 1336, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gene-parkgene", - "name": "Parkgene", - "symbol": "GENE", - "rank": 1337, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kek-kekcoin", - "name": "KekCoin", - "symbol": "KEK", - "rank": 1338, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "esh-switch", - "name": "Switch", - "symbol": "ESH", - "rank": 1339, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bgl-bitgesell", - "name": "Bitgesell", - "symbol": "BGL", - "rank": 1340, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bez-bezop", - "name": "Bezop", - "symbol": "BEZ", - "rank": 1341, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tpay-tokenpay", - "name": "TokenPay", - "symbol": "TPAY", - "rank": 1342, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "orao-orao-network", - "name": "ORAO Network", - "symbol": "ORAO", - "rank": 1343, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "exp-expanse", - "name": "Expanse", - "symbol": "EXP", - "rank": 1344, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bart-bartertrade", - "name": "BarterTrade", - "symbol": "BART", - "rank": 1345, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pkt-playkey", - "name": "Playkey", - "symbol": "PKT", - "rank": 1346, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "n8v-nativecoin", - "name": "NativeCoin", - "symbol": "N8V", - "rank": 1347, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mes-meschain", - "name": "MesChain", - "symbol": "MES", - "rank": 1348, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roningmz-ronin-gamez", - "name": "Ronin Gamez", - "symbol": "RONINGMZ", - "rank": 1349, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poli-polinate", - "name": "Polinate", - "symbol": "POLI", - "rank": 1350, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdt-bdt-token", - "name": "Bitdepositary", - "symbol": "BDT", - "rank": 1351, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egg-goose-finance", - "name": "Goose Finance", - "symbol": "EGG", - "rank": 1352, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "corx-corionx", - "name": "CorionX", - "symbol": "CORX", - "rank": 1353, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hsc-hashcoin", - "name": "HashCoin", - "symbol": "HSC", - "rank": 1354, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kwik-kwikswap-protocol", - "name": "KwikSwap Protocol", - "symbol": "KWIK", - "rank": 1355, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qbt-qbao", - "name": "Qbao", - "symbol": "QBT", - "rank": 1356, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnn-content-neutrality-network", - "name": "Content Neutrality Network", - "symbol": "CNN", - "rank": 1357, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bether-bethereum", - "name": "Bethereum", - "symbol": "BETHER", - "rank": 1358, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poe-poet", - "name": "Po.et", - "symbol": "POE", - "rank": 1359, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "beer-beer-money", - "name": "Beer Money", - "symbol": "BEER", - "rank": 1360, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kimchi-kimchifinance", - "name": "KIMCHI.finance", - "symbol": "KIMCHI", - "rank": 1361, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnty-bounty0x", - "name": "Bounty0x", - "symbol": "BNTY", - "rank": 1362, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rito-rito", - "name": "RITO", - "symbol": "RITO", - "rank": 1363, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bxy-beaxy", - "name": "Beaxy", - "symbol": "BXY", - "rank": 1364, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1mt-1million-token", - "name": "1Million Token", - "symbol": "1MT", - "rank": 1365, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egem-ethergem", - "name": "EtherGem", - "symbol": "EGEM", - "rank": 1366, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xlr-solaris", - "name": "Solaris", - "symbol": "XLR", - "rank": 1367, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fyz-fyooz", - "name": "Fyooz", - "symbol": "FYZ", - "rank": 1368, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hqx-hoqu", - "name": "HOQU", - "symbol": "HQX", - "rank": 1369, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stbu-stobox-token", - "name": "Stobox Token", - "symbol": "STBU", - "rank": 1370, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kicks-sessia", - "name": "Sessia", - "symbol": "KICKS", - "rank": 1371, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fti-fanstime", - "name": "FansTime", - "symbol": "FTI", - "rank": 1372, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "two-2gather", - "name": "2gather", - "symbol": "TWO", - "rank": 1373, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zpae-zelaapayae", - "name": "ZelaaPayAE", - "symbol": "ZPAE", - "rank": 1374, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pnt-penta", - "name": "Penta", - "symbol": "PNT", - "rank": 1375, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "datx-datx", - "name": "DATx", - "symbol": "DATX", - "rank": 1376, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mel-melalie", - "name": "Melalie", - "symbol": "MEL", - "rank": 1377, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pgo-pengolincoin", - "name": "PengolinCoin", - "symbol": "PGO", - "rank": 1378, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bmh-blockmesh", - "name": "BlockMesh", - "symbol": "BMH", - "rank": 1379, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fota-fortuna", - "name": "Fortuna", - "symbol": "FOTA", - "rank": 1380, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qrt-qrkita-token", - "name": "Qrkita Token", - "symbol": "QRT", - "rank": 1381, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcv-bitcapitalvendor", - "name": "BitCapitalVendor", - "symbol": "BCV", - "rank": 1382, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evc-eventchain", - "name": "EventChain", - "symbol": "EVC", - "rank": 1383, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dml-decentralized-machine-learning", - "name": "Decentralized Machine Learning", - "symbol": "DML", - "rank": 1384, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skin-skincoin", - "name": "SkinCoin", - "symbol": "SKIN", - "rank": 1385, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aux-auxilium", - "name": "Auxilium", - "symbol": "AUX", - "rank": 1386, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "metm-metamorph", - "name": "MetaMorph", - "symbol": "METM", - "rank": 1387, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fmt-finminity", - "name": "Finminity", - "symbol": "FMT", - "rank": 1388, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsl-bsclaunch", - "name": "BSClaunch", - "symbol": "BSL", - "rank": 1389, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acat-alphacat", - "name": "Alphacat", - "symbol": "ACAT", - "rank": 1390, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sphtx-sophiatx", - "name": "SophiaTX", - "symbol": "SPHTX", - "rank": 1391, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "chart-chartex", - "name": "ChartEx", - "symbol": "CHART", - "rank": 1392, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "land-landbox", - "name": "LandBox", - "symbol": "LAND", - "rank": 1393, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apr-apr-coin", - "name": "APR Coin", - "symbol": "APR", - "rank": 1394, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nino-ninneko", - "name": "Ninneko", - "symbol": "NINO", - "rank": 1395, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tbx-tokenbox", - "name": "Tokenbox", - "symbol": "TBX", - "rank": 1396, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meeb-meeb-master", - "name": "Meeb Master", - "symbol": "MEEB", - "rank": 1397, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsl-energo", - "name": "Energo", - "symbol": "TSL", - "rank": 1398, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "senc-sentinel-chain", - "name": "Sentinel Chain", - "symbol": "SENC", - "rank": 1399, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbp-biblepay", - "name": "BiblePay", - "symbol": "BBP", - "rank": 1400, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ely-elysian", - "name": "Elysian", - "symbol": "ELY", - "rank": 1401, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rte-rate3", - "name": "Rate3", - "symbol": "RTE", - "rank": 1402, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alp-coinalpha", - "name": "CoinAlpha", - "symbol": "ALP", - "rank": 1403, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pkg-pkg-token", - "name": "PKG Token", - "symbol": "PKG", - "rank": 1404, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "quin-quinads", - "name": "QUINADS", - "symbol": "QUIN", - "rank": 1405, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amm-micromoney", - "name": "MicroMoney", - "symbol": "AMM", - "rank": 1406, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "val-sora-validator-token", - "name": "Sora Validator Token", - "symbol": "VAL", - "rank": 1407, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbn-membrana", - "name": "Membrana", - "symbol": "MBN", - "rank": 1408, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chi-chi-gastoken", - "name": "Chi Gastoken", - "symbol": "CHI", - "rank": 1409, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axe-axe", - "name": "Axe", - "symbol": "AXE", - "rank": 1410, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lun-lunyr", - "name": "Lunyr", - "symbol": "LUN", - "rank": 1411, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aim-modihost", - "name": "ModiHost", - "symbol": "AIM", - "rank": 1412, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "khc-koho-chain", - "name": "KoHo Chain", - "symbol": "KHC", - "rank": 1413, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vdl-vidulum", - "name": "Vidulum", - "symbol": "VDL", - "rank": 1414, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xgm-defis", - "name": "Defis", - "symbol": "XGM", - "rank": 1415, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cova-cova", - "name": "COVA", - "symbol": "COVA", - "rank": 1416, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boost-boosted-finance", - "name": "Boosted Finance", - "symbol": "BOOST", - "rank": 1417, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ig-igtoken", - "name": "IGToken", - "symbol": "IG", - "rank": 1418, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rock2-ice-rock-mining", - "name": "ICE ROCK MINING", - "symbol": "ROCK2", - "rank": 1419, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eko-echolink", - "name": "EchoLink", - "symbol": "EKO", - "rank": 1420, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gold-golden-goose", - "name": "Golden Goose", - "symbol": "GOLD", - "rank": 1421, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gaj-gaj-finance", - "name": "Gaj Finance", - "symbol": "GAJ", - "rank": 1422, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mate-mate", - "name": "Mate", - "symbol": "MATE", - "rank": 1423, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "got-gonetwork", - "name": "GoNetwork", - "symbol": "GOT", - "rank": 1424, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "get-themis", - "name": "Themis", - "symbol": "GET", - "rank": 1425, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tdp-truedeck", - "name": "TrueDeck", - "symbol": "TDP", - "rank": 1426, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inx-insight-protocol", - "name": "Insight Protocol", - "symbol": "INX", - "rank": 1427, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btw-bitwhite", - "name": "BitWhite", - "symbol": "BTW", - "rank": 1428, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "eve-devery", - "name": "Devery", - "symbol": "EVE", - "rank": 1429, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtx-matryx", - "name": "Matryx", - "symbol": "MTX", - "rank": 1430, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lit-lition", - "name": "Lition", - "symbol": "LIT", - "rank": 1431, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wnl-winstars", - "name": "WinStars.live", - "symbol": "WNL", - "rank": 1432, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crcte-circuit-elite", - "name": "Circuit Elite", - "symbol": "CRCTE", - "rank": 1433, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1mil-1millionnfts", - "name": "1MillionNFTs", - "symbol": "1MIL", - "rank": 1434, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abx-arbidex", - "name": "Arbidex", - "symbol": "ABX", - "rank": 1435, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fbn-fivebalance", - "name": "Fivebalance", - "symbol": "FBN", - "rank": 1436, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aidoc-ai-doctor", - "name": "AI Doctor", - "symbol": "AIDOC", - "rank": 1437, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flot-fire-lotto", - "name": "Fire Lotto", - "symbol": "FLOT", - "rank": 1438, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogec-dogecash", - "name": "DogeCash", - "symbol": "DOGEC", - "rank": 1439, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xmv-monerov", - "name": "MoneroV", - "symbol": "XMV", - "rank": 1440, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ero-eroscoin", - "name": "Eroscoin", - "symbol": "ERO", - "rank": 1441, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tgame-truegame", - "name": "Truegame", - "symbol": "TGAME", - "rank": 1442, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cymt-cybermusic", - "name": "CyberMusic", - "symbol": "CYMT", - "rank": 1443, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmct-cyber-movie-chain", - "name": "Cyber Movie Chain", - "symbol": "CMCT", - "rank": 1444, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "twin-twinci", - "name": "Twinci", - "symbol": "TWIN", - "rank": 1445, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nsd-nasdacoin", - "name": "Nasdacoin", - "symbol": "NSD", - "rank": 1446, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "prix-privatix", - "name": "Privatix", - "symbol": "PRIX", - "rank": 1447, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bntx-bintex-futures", - "name": "Bintex Futures", - "symbol": "BNTX", - "rank": 1448, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adi-aditus", - "name": "Aditus", - "symbol": "ADI", - "rank": 1449, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plat-bitguild-plat", - "name": "BitGuild PLAT", - "symbol": "PLAT", - "rank": 1450, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eltcoin-eltcoin", - "name": "ELTCOIN", - "symbol": "ELTCOIN", - "rank": 1451, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dynmt-dynamite", - "name": "Dynamite", - "symbol": "DYNMT", - "rank": 1452, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfbt-yearn-finance-bit", - "name": "Yearn Finance Bit", - "symbol": "YFBT", - "rank": 1453, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idt-investdigital", - "name": "InvestDigital", - "symbol": "IDT", - "rank": 1454, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kiwi-kiwi-token", - "name": "KIWI Token", - "symbol": "KIWI", - "rank": 1455, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evn-evencoin", - "name": "EvenCoin", - "symbol": "EVN", - "rank": 1456, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "beet-beetle-coin", - "name": "Beetle Coin", - "symbol": "BEET", - "rank": 1457, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tff-tutti-frutti", - "name": "Tutti Frutti", - "symbol": "TFF", - "rank": 1458, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cou-couchain", - "name": "Couchain", - "symbol": "COU", - "rank": 1459, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "imp-ether-kingdoms-token", - "name": "Ether Kingdoms Token", - "symbol": "IMP", - "rank": 1460, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ents-eunomia", - "name": "EUNOMIA", - "symbol": "ENTS", - "rank": 1461, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "navy-boatpilot-token", - "name": "BoatPilot Token", - "symbol": "NAVY", - "rank": 1462, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpu-cpuchain", - "name": "CPUchain", - "symbol": "CPU", - "rank": 1463, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "talk-btctalkcoin", - "name": "BTCtalkcoin", - "symbol": "TALK", - "rank": 1464, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cpr-cipher", - "name": "CIPHER", - "symbol": "CPR", - "rank": 1465, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hand-showhand", - "name": "ShowHand", - "symbol": "HAND", - "rank": 1466, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "havy-havy", - "name": "Havy", - "symbol": "HAVY", - "rank": 1467, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "portal-portal", - "name": "Portal", - "symbol": "PORTAL", - "rank": 1468, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hsn-helper-search-token", - "name": "Helper Search Token", - "symbol": "HSN", - "rank": 1469, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "martk-martkist", - "name": "Martkist", - "symbol": "MARTK", - "rank": 1470, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "omc-ormeus-cash", - "name": "Ormeus Cash", - "symbol": "OMC", - "rank": 1471, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crdt-crdt", - "name": "CRDT", - "symbol": "CRDT", - "rank": 1472, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ucn-uchain", - "name": "UChain", - "symbol": "UCN", - "rank": 1473, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swg-swirge", - "name": "Swirge", - "symbol": "SWG", - "rank": 1474, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "distx-distx", - "name": "DistX", - "symbol": "DISTX", - "rank": 1475, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfg-sfinance", - "name": "S.Finance", - "symbol": "SFG", - "rank": 1476, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "actp-archetypal-network", - "name": "Archetypal Network", - "symbol": "ACTP", - "rank": 1477, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hqt-hyperquant", - "name": "HyperQuant", - "symbol": "HQT", - "rank": 1478, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "joint-joint-ventures", - "name": "Joint Ventures", - "symbol": "JOINT", - "rank": 1479, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bank-bank-coin", - "name": "Bank Coin", - "symbol": "BANK", - "rank": 1480, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cyl-crystal-token", - "name": "Crystal Token", - "symbol": "CYL", - "rank": 1481, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dep-deapcoin", - "name": "DEAPcoin", - "symbol": "DEP", - "rank": 1482, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mx-mx-token", - "name": "MX Token", - "symbol": "MX", - "rank": 1483, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mvl-mass-vehicle-ledger", - "name": "MVL", - "symbol": "MVL", - "rank": 1484, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hyn-hyperion", - "name": "Hyperion", - "symbol": "HYN", - "rank": 1485, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snn-sechain", - "name": "SeChain", - "symbol": "SNN", - "rank": 1486, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aeth-ankreth", - "name": "ankrETH", - "symbol": "AETH", - "rank": 1487, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boa-bosagora", - "name": "BOSAGORA", - "symbol": "BOA", - "rank": 1488, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btu-btu-protocol", - "name": "BTU Protocol", - "symbol": "BTU", - "rank": 1489, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lynx-lynx", - "name": "Lynx", - "symbol": "LYNX", - "rank": 1490, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mcan-medicancoin", - "name": "MedicanCoin", - "symbol": "MCAN", - "rank": 1491, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xrl-rialto", - "name": "Rialto", - "symbol": "XRL", - "rank": 1492, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ban-banano", - "name": "Banano", - "symbol": "BAN", - "rank": 1493, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cut-cutcoin", - "name": "CUTcoin", - "symbol": "CUT", - "rank": 1494, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "plc-platincoin", - "name": "PLATINCOIN", - "symbol": "PLC", - "rank": 1495, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "stax-stablexswap", - "name": "StableXSwap", - "symbol": "STAX", - "rank": 1496, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unc-unelmacoin", - "name": "UnelmaCoin", - "symbol": "UNC", - "rank": 1497, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spnd-spendcoin", - "name": "Spendcoin", - "symbol": "SPND", - "rank": 1498, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rstr-ondori", - "name": "Ondori", - "symbol": "RSTR", - "rank": 1499, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "san-santiment-network-token", - "name": "Santiment Network Token", - "symbol": "SAN", - "rank": 1500, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpx-proximax", - "name": "ProximaX", - "symbol": "XPX", - "rank": 1501, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dvpn-sentinel", - "name": "Sentinel", - "symbol": "DVPN", - "rank": 1502, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yusra-yusra", - "name": "YUSRA", - "symbol": "YUSRA", - "rank": 1503, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xep-electra-protocol", - "name": "Electra Protocol", - "symbol": "XEP", - "rank": 1504, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btc2-bitcoin-2", - "name": "Bitcoin 2", - "symbol": "BTC2", - "rank": 1505, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "arc-advanced-technology-coin", - "name": "Advanced Technology Coin", - "symbol": "ARC", - "rank": 1506, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "karma-karma", - "name": "KARMA", - "symbol": "KARMA", - "rank": 1507, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "synx-syndicate", - "name": "Syndicate", - "symbol": "SYNX", - "rank": 1508, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "imt-moneytoken", - "name": "Moneytoken", - "symbol": "IMT", - "rank": 1509, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmd-the-coop-network", - "name": "The Coop Network", - "symbol": "GMD", - "rank": 1510, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xas-asch", - "name": "Asch", - "symbol": "XAS", - "rank": 1511, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gre-greencoin", - "name": "Greencoin", - "symbol": "GRE", - "rank": 1512, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "idrt-rupiah-token", - "name": "Rupiah Token", - "symbol": "IDRT", - "rank": 1513, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sft-safex-token", - "name": "Safex Token", - "symbol": "SFT", - "rank": 1514, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gton-graviton", - "name": "Graviton", - "symbol": "GTON", - "rank": 1515, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tfl-trueflip", - "name": "TrueFlip", - "symbol": "TFL", - "rank": 1516, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kun-kun", - "name": "KUN", - "symbol": "KUN", - "rank": 1517, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "horus-horuspay", - "name": "HorusPay", - "symbol": "HORUS", - "rank": 1518, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yec-ycash", - "name": "Ycash", - "symbol": "YEC", - "rank": 1519, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "now-changenow", - "name": "NOW Token", - "symbol": "NOW", - "rank": 1520, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chips-chips", - "name": "CHIPS", - "symbol": "CHIPS", - "rank": 1521, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ldoge-litedoge", - "name": "LiteDoge", - "symbol": "LDOGE", - "rank": 1522, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xac-general-attention-currency", - "name": "General Attention Currency", - "symbol": "XAC", - "rank": 1523, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpay-cryptopay", - "name": "Cryptopay", - "symbol": "CPAY", - "rank": 1524, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dapp-liquidapps", - "name": "LiquidApps", - "symbol": "DAPP", - "rank": 1525, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "instar-insights-network", - "name": "Insights Network", - "symbol": "INSTAR", - "rank": 1526, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axis-axis-token", - "name": "AXIS Token", - "symbol": "AXIS", - "rank": 1527, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btb-bitball", - "name": "BitBall", - "symbol": "BTB", - "rank": 1528, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spore-spore", - "name": "Spore", - "symbol": "SPORE", - "rank": 1529, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "colx-colossusxt", - "name": "ColossusXT", - "symbol": "COLX", - "rank": 1530, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rise-rise", - "name": "Rise", - "symbol": "RISE", - "rank": 1531, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "raven-raven-protocol", - "name": "Raven Protocol", - "symbol": "RAVEN", - "rank": 1532, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spc-spacechain", - "name": "SpaceChain", - "symbol": "SPC", - "rank": 1533, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "peg-pegnet", - "name": "PegNet", - "symbol": "PEG", - "rank": 1534, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "unistake-unistake", - "name": "Unistake", - "symbol": "UNISTAKE", - "rank": 1535, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dyn-dynamic", - "name": "Dynamic", - "symbol": "DYN", - "rank": 1536, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hbx-hashbx", - "name": "HashBX", - "symbol": "HBX", - "rank": 1537, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "efl-e-gulden", - "name": "e-Gulden", - "symbol": "EFL", - "rank": 1538, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "moon-mooncoin", - "name": "Mooncoin", - "symbol": "MOON", - "rank": 1539, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lit-global-x-lithium-amp-battery-tech-etf", - "name": "Global X Lithium \u0026 Battery Tech ETF", - "symbol": "LIT", - "rank": 1540, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vitae-vitae", - "name": "Vitae", - "symbol": "VITAE", - "rank": 1541, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "emc-emercoin", - "name": "Emercoin", - "symbol": "EMC", - "rank": 1542, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "telos-teloscoin", - "name": "Teloscoin", - "symbol": "TELOS", - "rank": 1543, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "jade-jade-currency", - "name": "Jade Currency", - "symbol": "JADE", - "rank": 1544, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "exrn-exrnchain", - "name": "EXRNchain", - "symbol": "EXRN", - "rank": 1545, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btx-bitcore", - "name": "Bitcore", - "symbol": "BTX", - "rank": 1546, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aur-auroracoin", - "name": "Auroracoin", - "symbol": "AUR", - "rank": 1547, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wpr-wepower", - "name": "WePower", - "symbol": "WPR", - "rank": 1548, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ong-someesocial", - "name": "SoMee.Social", - "symbol": "ONG", - "rank": 1549, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bis-bismuth", - "name": "Bismuth", - "symbol": "BIS", - "rank": 1550, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "keyfi-keyfi", - "name": "KeyFi", - "symbol": "KEYFI", - "rank": 1551, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smg-smaugs-nft", - "name": "Smaugs NFT", - "symbol": "SMG", - "rank": 1552, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpc-capricoin", - "name": "Capricoin", - "symbol": "CPC", - "rank": 1553, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "thc-hempcoin", - "name": "HempCoin", - "symbol": "THC", - "rank": 1554, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "eosdt-eosdt", - "name": "EOSDT", - "symbol": "EOSDT", - "rank": 1555, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1x2-1x2-coin", - "name": "1X2 COIN", - "symbol": "1X2", - "rank": 1556, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "arco-aquariuscoin", - "name": "AquariusCoin", - "symbol": "ARCO", - "rank": 1557, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ufr-upfiring", - "name": "Upfiring", - "symbol": "UFR", - "rank": 1558, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xbase-eterbase", - "name": "Eterbase Coin", - "symbol": "XBASE", - "rank": 1559, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pot-potcoin", - "name": "PotCoin", - "symbol": "POT", - "rank": 1560, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ntk-neurotoken", - "name": "Neurotoken", - "symbol": "NTK", - "rank": 1561, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adm-adamant-messenger", - "name": "ADAMANT Messenger", - "symbol": "ADM", - "rank": 1562, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gio-graviocoin", - "name": "Graviocoin", - "symbol": "GIO", - "rank": 1563, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lunes-lunes", - "name": "Lunes", - "symbol": "LUNES", - "rank": 1564, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "d-denarius", - "name": "Denarius", - "symbol": "D", - "rank": 1565, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bca-bitcoin-atom", - "name": "Bitcoin Atom", - "symbol": "BCA", - "rank": 1566, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dime-dimecoin", - "name": "Dimecoin", - "symbol": "DIME", - "rank": 1567, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sth-smartholdem", - "name": "Smartholdem", - "symbol": "STH", - "rank": 1568, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "moto-motocoin", - "name": "Motocoin", - "symbol": "MOTO", - "rank": 1569, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mintme-com-coin", - "name": "MintMe.com Coin", - "symbol": "MINTME", - "rank": 1570, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "omni-omni", - "name": "Omni", - "symbol": "OMNI", - "rank": 1571, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wct-waves-community-token", - "name": "Waves Community Token", - "symbol": "WCT", - "rank": 1572, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bmc-blackmoon", - "name": "Blackmoon", - "symbol": "BMC", - "rank": 1573, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpt-cryptaur", - "name": "Cryptaur", - "symbol": "CPT", - "rank": 1574, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pmgt-perth-mint-gold-token", - "name": "Perth Mint Gold Token", - "symbol": "PMGT", - "rank": 1575, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "depay-depay", - "name": "DePay", - "symbol": "DEPAY", - "rank": 1576, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "suqa-suqa", - "name": "SINOVATE", - "symbol": "SIN", - "rank": 1577, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wgp-w-green-pay", - "name": "W Green Pay", - "symbol": "WGP", - "rank": 1578, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xrc-xrhodium", - "name": "xRhodium", - "symbol": "XRC", - "rank": 1579, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "box-box-token", - "name": "BOX Token", - "symbol": "BOX", - "rank": 1580, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "peps-peps-coin", - "name": "PEPS Coin", - "symbol": "PEPS", - "rank": 1581, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spaz-swapcoinz", - "name": "SWAPCOINZ", - "symbol": "SPAZ", - "rank": 1582, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ftx-fintrux-network", - "name": "FintruX Network", - "symbol": "FTX", - "rank": 1583, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcm-mochimo", - "name": "Mochimo", - "symbol": "MCM", - "rank": 1584, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cot-cotrader", - "name": "CoTrader", - "symbol": "COT", - "rank": 1585, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ioc-io-coin", - "name": "I/O Coin", - "symbol": "IOC", - "rank": 1586, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "veil-veil", - "name": "VEIL", - "symbol": "VEIL", - "rank": 1587, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wish-mywish", - "name": "MyWish", - "symbol": "WISH", - "rank": 1588, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snb-synchrobitcoin", - "name": "SynchroBitcoin", - "symbol": "SNB", - "rank": 1589, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ryo-ryo-currency", - "name": "Ryo Currency", - "symbol": "RYO", - "rank": 1590, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bmy-bristol-myers-squibb", - "name": "Bristol-Myers Squibb", - "symbol": "BMY", - "rank": 1591, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dvp-decentralized-vulnerability-platform", - "name": "Decentralized Vulnerability Platform", - "symbol": "DVP", - "rank": 1592, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pxc-phoenixcoin", - "name": "Phoenixcoin", - "symbol": "PXC", - "rank": 1593, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zer-zero", - "name": "Zero", - "symbol": "ZER", - "rank": 1594, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mxx-multiplier", - "name": "Multiplier", - "symbol": "MXX", - "rank": 1595, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alg-algory", - "name": "Algory", - "symbol": "ALG", - "rank": 1596, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gfi-gravity-finance", - "name": "Gravity Finance", - "symbol": "GFI", - "rank": 1597, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lion-coin-lion", - "name": "Coin Lion", - "symbol": "LION", - "rank": 1598, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tusc-the-universal-settlement-coin", - "name": "The Universal Settlement Coin", - "symbol": "TUSC", - "rank": 1599, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nct-name-change-token", - "name": "Name Change Token", - "symbol": "NCT", - "rank": 1600, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sub-substratum", - "name": "Substratum", - "symbol": "SUB", - "rank": 1601, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aya-aryacoin", - "name": "Aryacoin", - "symbol": "AYA", - "rank": 1602, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rogue-rogue-west", - "name": "Rogue West", - "symbol": "ROGUE", - "rank": 1603, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reli-relite-finance", - "name": "Relite Finance", - "symbol": "RELI", - "rank": 1604, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mue-monetaryunit", - "name": "MonetaryUnit", - "symbol": "MUE", - "rank": 1605, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mntt-moontrust", - "name": "MoonTrust", - "symbol": "MNTT", - "rank": 1606, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ship-shipchain", - "name": "ShipChain", - "symbol": "SHIP", - "rank": 1607, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emd-emerald-crypto", - "name": "Emerald Crypto", - "symbol": "EMD", - "rank": 1608, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bitx-bitscreener-token", - "name": "BitScreener Token", - "symbol": "BITX", - "rank": 1609, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcs-bitcoin-scrypt", - "name": "Bitcoin Scrypt", - "symbol": "BTCS", - "rank": 1610, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ptoy-patientory", - "name": "Patientory", - "symbol": "PTOY", - "rank": 1611, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daps-daps-coin", - "name": "DAPS Coin", - "symbol": "DAPS", - "rank": 1612, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "osc-oasiscity", - "name": "OasisCity", - "symbol": "OSC", - "rank": 1613, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bznt-bezant", - "name": "Bezant", - "symbol": "BZNT", - "rank": 1614, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ion-ion", - "name": "ION", - "symbol": "ION", - "rank": 1615, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ind-indorse-token", - "name": "Indorse Token", - "symbol": "IND", - "rank": 1616, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chess-chesscoin", - "name": "ChessCoin", - "symbol": "CHESS", - "rank": 1617, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btzc-beatzcoin", - "name": "BeatzCoin", - "symbol": "BTZC", - "rank": 1618, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "modic-modern-investment-coin", - "name": "Modern Investment Coin", - "symbol": "MODIC", - "rank": 1619, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "max-maxcoin", - "name": "Maxcoin", - "symbol": "MAX", - "rank": 1620, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "trst-wetrust", - "name": "WeTrust", - "symbol": "TRST", - "rank": 1621, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vlu-valuto", - "name": "Valuto", - "symbol": "VLU", - "rank": 1622, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cdn-canada-ecoin", - "name": "Canada eCoin", - "symbol": "CDN", - "rank": 1623, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kp4r-keep4r", - "name": "Keep4r", - "symbol": "KP4R", - "rank": 1624, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "doges-dogeswap", - "name": "Dogeswap", - "symbol": "DOGES", - "rank": 1625, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cone-coinone-token", - "name": "CoinOne Token", - "symbol": "CONE", - "rank": 1626, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmt-dmarket", - "name": "DMarket", - "symbol": "DMT", - "rank": 1627, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vrc-vericoin", - "name": "VeriCoin", - "symbol": "VRC", - "rank": 1628, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "face-faceter", - "name": "Faceter", - "symbol": "FACE", - "rank": 1629, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "manna-manna", - "name": "Manna", - "symbol": "MANNA", - "rank": 1630, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mota-motacoin", - "name": "MotaCoin", - "symbol": "MOTA", - "rank": 1631, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ddk-ddkoin", - "name": "DDKoin", - "symbol": "DDK", - "rank": 1632, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mcx-machi-x", - "name": "Machi X", - "symbol": "MCX", - "rank": 1633, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mon-moneybyte", - "name": "MoneyByte", - "symbol": "MON", - "rank": 1634, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "swace-swace", - "name": "Swace", - "symbol": "SWACE", - "rank": 1635, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbc-microbitcoin", - "name": "MicroBitcoin", - "symbol": "MBC", - "rank": 1636, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "star-starbase", - "name": "Starbase", - "symbol": "STAR", - "rank": 1637, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drg-dragon-coins", - "name": "Dragon Coins", - "symbol": "DRG", - "rank": 1638, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpd-petrodollar", - "name": "PetroDollar", - "symbol": "XPD", - "rank": 1639, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mnc-maincoin", - "name": "Maincoin", - "symbol": "MNC", - "rank": 1640, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atn-atn", - "name": "ATN", - "symbol": "ATN", - "rank": 1641, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "monk-monkey-project", - "name": "Monkey Project", - "symbol": "MONK", - "rank": 1642, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ird-iridium", - "name": "Iridium", - "symbol": "IRD", - "rank": 1643, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "naal-ethernaal", - "name": "Ethernaal", - "symbol": "NAAL", - "rank": 1644, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trc-terracoin", - "name": "Terracoin", - "symbol": "TRC", - "rank": 1645, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "meme-memetic-pepecoin", - "name": "Memetic / PepeCoin", - "symbol": "MEME", - "rank": 1646, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pbt-primalbase-token", - "name": "Primalbase Token", - "symbol": "PBT", - "rank": 1647, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "base-base-protocol", - "name": "Base Protocol", - "symbol": "BASE", - "rank": 1648, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dvd-daoventures", - "name": "DAOventures", - "symbol": "DVD", - "rank": 1649, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frc-freicoin", - "name": "Freicoin", - "symbol": "FRC", - "rank": 1650, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vit-vice-industry-token", - "name": "Vision Industry Token", - "symbol": "VIT", - "rank": 1651, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shdw-shadow-token", - "name": "Shadow Token", - "symbol": "SHDW", - "rank": 1652, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ustx-upstabletoken", - "name": "UpStableToken", - "symbol": "USTX", - "rank": 1653, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hush-hush", - "name": "Hush", - "symbol": "HUSH", - "rank": 1654, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cdl-coindeal-token", - "name": "CoinDeal Token", - "symbol": "CDL", - "rank": 1655, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xvc-vcash", - "name": "Vcash", - "symbol": "XVC", - "rank": 1656, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zxc-0xcert", - "name": "0xcert", - "symbol": "ZXC", - "rank": 1657, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "agcmn-agricoin-masternode", - "name": "Agricoin Masternode", - "symbol": "AGCMN", - "rank": 1658, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "oct-oraclechain", - "name": "OracleChain", - "symbol": "OCT", - "rank": 1659, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ric-riecoin", - "name": "Riecoin", - "symbol": "RIC", - "rank": 1660, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cbm-cryptobonusmiles", - "name": "CryptoBonusMiles", - "symbol": "CBM", - "rank": 1661, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitb-bean-cash", - "name": "Bean Cash", - "symbol": "BITB", - "rank": 1662, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nsr-nushares", - "name": "NuShares", - "symbol": "NSR", - "rank": 1663, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dit-digital-insurance-token", - "name": "Digital Insurance Token", - "symbol": "DIT", - "rank": 1664, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ixt-ixt", - "name": "IXT", - "symbol": "IXT", - "rank": 1665, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "becn-beacon", - "name": "Beacon", - "symbol": "BECN", - "rank": 1666, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mne-minereum", - "name": "Minereum", - "symbol": "MNE", - "rank": 1667, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pirate-piratecash", - "name": "PirateCash", - "symbol": "PIRATE", - "rank": 1668, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bdg-bitdegree", - "name": "BitDegree", - "symbol": "BDG", - "rank": 1669, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aog-smartofgiving", - "name": "smARTOFGIVING", - "symbol": "AOG", - "rank": 1670, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ttn-titan-coin", - "name": "Titan Coin", - "symbol": "TTN", - "rank": 1671, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mib-mib-coin", - "name": "MIB Coin", - "symbol": "MIB", - "rank": 1672, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acm-actinium", - "name": "Actinium", - "symbol": "ACM", - "rank": 1673, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "share-seigniorage-shares", - "name": "Seigniorage Shares", - "symbol": "SHARE", - "rank": 1674, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xiot-xiotri", - "name": "Xiotri", - "symbol": "XIOT", - "rank": 1675, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nuts-squirrel-finance", - "name": "Squirrel Finance", - "symbol": "NUTS", - "rank": 1676, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yap-yap-stone", - "name": "Yap Stone", - "symbol": "YAP", - "rank": 1677, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bwx-blue-whale-token", - "name": "Blue Whale EXchange", - "symbol": "BWX", - "rank": 1678, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bze-bzedge", - "name": "BZEdge", - "symbol": "BZE", - "rank": 1679, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "geo-geocoin", - "name": "GeoCoin", - "symbol": "GEO", - "rank": 1680, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xlab-xceltoken-plus", - "name": "XcelToken Plus", - "symbol": "XLAB", - "rank": 1681, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "maza-maza", - "name": "MAZA", - "symbol": "MAZA", - "rank": 1682, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gold-goldfarm", - "name": "GoldFarm", - "symbol": "GOLD", - "rank": 1683, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etg-ethereum-gold", - "name": "Ethereum Gold", - "symbol": "ETG", - "rank": 1684, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shnd-stronghands", - "name": "StrongHands", - "symbol": "SHND", - "rank": 1685, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rfi-reflectfinance", - "name": "reflect.finance", - "symbol": "RFI", - "rank": 1686, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "upx-uplexa", - "name": "uPlexa", - "symbol": "UPX", - "rank": 1687, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "waif-waifu-token", - "name": "Waifu Token", - "symbol": "WAIF", - "rank": 1688, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mmo-mmocoin", - "name": "MMOCoin", - "symbol": "MMO", - "rank": 1689, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rmpl-rmpl", - "name": "RMPL", - "symbol": "RMPL", - "rank": 1690, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msr-masari", - "name": "Masari", - "symbol": "MSR", - "rank": 1691, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "flp-flip", - "name": "FLIP", - "symbol": "FLP", - "rank": 1692, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cur-curio", - "name": "Curio", - "symbol": "CUR", - "rank": 1693, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zum-zum-token", - "name": "ZUM TOKEN", - "symbol": "ZUM", - "rank": 1694, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecn-e-coin", - "name": "E-coin", - "symbol": "ECN", - "rank": 1695, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "swing-swing", - "name": "Swing", - "symbol": "SWING", - "rank": 1696, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zet-zetacoin", - "name": "Zetacoin", - "symbol": "ZET", - "rank": 1697, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "swt-swarm-city", - "name": "Swarm City", - "symbol": "SWT", - "rank": 1698, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etz-ether-zero", - "name": "Ether Zero", - "symbol": "ETZ", - "rank": 1699, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pht-phoneum", - "name": "Phoneum", - "symbol": "PHT", - "rank": 1700, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbe-sombe", - "name": "Sombe", - "symbol": "SBE", - "rank": 1701, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ecob-ecobit", - "name": "Ecobit", - "symbol": "ECOB", - "rank": 1702, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsty-globalboost-y", - "name": "GlobalBoost-Y", - "symbol": "BSTY", - "rank": 1703, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xnv-nerva", - "name": "Nerva", - "symbol": "XNV", - "rank": 1704, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pass-blockpass", - "name": "Blockpass", - "symbol": "PASS", - "rank": 1705, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unv-unvest", - "name": "Unvest", - "symbol": "UNV", - "rank": 1706, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dbet-decentbet", - "name": "DecentBet", - "symbol": "DBET", - "rank": 1707, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sib-sibcoin", - "name": "SIBCoin", - "symbol": "SIB", - "rank": 1708, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xp-experience-points", - "name": "Experience Points", - "symbol": "XP", - "rank": 1709, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "excl-exclusivecoin", - "name": "ExclusiveCoin", - "symbol": "EXCL", - "rank": 1710, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ssp-smartshare", - "name": "Smartshare", - "symbol": "SSP", - "rank": 1711, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alt-altestate-token", - "name": "Alt.Estate token", - "symbol": "ALT", - "rank": 1712, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zpr-zper", - "name": "ZPER", - "symbol": "ZPR", - "rank": 1713, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fry-foundry-logistics-token", - "name": "Foundry Logistics Token", - "symbol": "FRY", - "rank": 1714, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mntp-goldmint", - "name": "GoldMint", - "symbol": "MNTP", - "rank": 1715, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zrc-zrcoin", - "name": "ZrCoin", - "symbol": "ZRC", - "rank": 1716, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "znz-zenzo", - "name": "ZENZO", - "symbol": "ZNZ", - "rank": 1717, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "day-chronologic", - "name": "Chronologic", - "symbol": "DAY", - "rank": 1718, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inve-intervalue", - "name": "InterValue", - "symbol": "INVE", - "rank": 1719, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dev-deviantcoin", - "name": "DeviantCoin", - "symbol": "DEV", - "rank": 1720, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "evil-evil-coin", - "name": "Evil Coin", - "symbol": "EVIL", - "rank": 1721, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "smly-smileycoin", - "name": "SmileyCoin", - "symbol": "SMLY", - "rank": 1722, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "usds-stablyusd", - "name": "Stably USD", - "symbol": "USDS", - "rank": 1723, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptc-pesetacoin", - "name": "Peseta Digital", - "symbol": "PTD", - "rank": 1724, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "adl-adelphoi", - "name": "Adelphoi", - "symbol": "ADL", - "rank": 1725, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbc-niobium-coin", - "name": "Niobium Coin", - "symbol": "NBC", - "rank": 1726, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sugar-sugarchain", - "name": "Sugarchain", - "symbol": "SUGAR", - "rank": 1727, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "icn-icoin", - "name": "iCoin", - "symbol": "ICN", - "rank": 1728, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "snrg-synergy", - "name": "Synergy", - "symbol": "SNRG", - "rank": 1729, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "flty-fluity", - "name": "Fluity", - "symbol": "FLTY", - "rank": 1730, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cen-coinsuper-ecosystem-network", - "name": "Coinsuper Ecosystem Network", - "symbol": "CEN", - "rank": 1731, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "croat-croat", - "name": "CROAT", - "symbol": "CROAT", - "rank": 1732, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cbx-bullion", - "name": "Bullion", - "symbol": "CBX", - "rank": 1733, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ltz-litecoinz", - "name": "LitecoinZ", - "symbol": "LTZ", - "rank": 1734, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "eti-etherinc", - "name": "EtherInc", - "symbol": "ETI", - "rank": 1735, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nor-noir", - "name": "Noir", - "symbol": "NOR", - "rank": 1736, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "inn-innova", - "name": "Innova", - "symbol": "INN", - "rank": 1737, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dlt-agrello", - "name": "Agrello", - "symbol": "DLT", - "rank": 1738, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zsc-zeusshield", - "name": "Zeusshield", - "symbol": "ZSC", - "rank": 1739, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbk-bitblocks", - "name": "Bitblocks", - "symbol": "BBK", - "rank": 1740, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "grft-graft", - "name": "Graft", - "symbol": "GRFT", - "rank": 1741, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bcpt-blockmason-credit-protocol", - "name": "Blockmason Credit Protocol", - "symbol": "BCPT", - "rank": 1742, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gfun-goldfund", - "name": "GoldFund", - "symbol": "GFUN", - "rank": 1743, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tzc-trezarcoin", - "name": "TrezarCoin", - "symbol": "TZC", - "rank": 1744, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bud-buddy", - "name": "BUDDY", - "symbol": "BUD", - "rank": 1745, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtlmc3-metal-music-coin", - "name": "Metal Music Coin", - "symbol": "MTLMC3", - "rank": 1746, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hgt-hellogold", - "name": "HelloGold", - "symbol": "HGT", - "rank": 1747, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zla-zilla", - "name": "Zilla", - "symbol": "ZLA", - "rank": 1748, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aka-akroma", - "name": "Akroma", - "symbol": "AKA", - "rank": 1749, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "women-womencoin", - "name": "WomenCoin", - "symbol": "WOMEN", - "rank": 1750, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fxt-fuzex", - "name": "FuzeX", - "symbol": "FXT", - "rank": 1751, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iog-playgroundz", - "name": "Playgroundz", - "symbol": "IOG", - "rank": 1752, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bio-biocoin", - "name": "BioCoin", - "symbol": "BIO", - "rank": 1753, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rpd-rapids", - "name": "Rapids", - "symbol": "RPD", - "rank": 1754, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "atcc-atc-coin", - "name": "ATC Coin", - "symbol": "ATCC", - "rank": 1755, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lux-luxcoin", - "name": "LUXCoin", - "symbol": "LUX", - "rank": 1756, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dgc-digitalcoin", - "name": "Digitalcoin", - "symbol": "DGC", - "rank": 1757, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "art-maecenas", - "name": "Maecenas", - "symbol": "ART", - "rank": 1758, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clx-celeum", - "name": "Celeum", - "symbol": "CLX", - "rank": 1759, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swift-swiftcash", - "name": "SwiftCash", - "symbol": "SWIFT", - "rank": 1760, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zuc-zeuxcoin", - "name": "ZeuxCoin", - "symbol": "ZUC", - "rank": 1761, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mec-megacoin", - "name": "Megacoin", - "symbol": "MEC", - "rank": 1762, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xwp-swap", - "name": "Swap", - "symbol": "XWP", - "rank": 1763, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lock-meridian-network", - "name": "Meridian Network", - "symbol": "LOCK", - "rank": 1764, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "erk-eureka-coin", - "name": "Eureka Coin", - "symbol": "ERK", - "rank": 1765, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ipl-vouchforme", - "name": "VouchForMe", - "symbol": "IPL", - "rank": 1766, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plura-pluracoin", - "name": "PluraCoin", - "symbol": "PLURA", - "rank": 1767, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "posw-posw-coin", - "name": "PoSW Coin", - "symbol": "POSW", - "rank": 1768, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bltg-block-logic", - "name": "Block-Logic", - "symbol": "BLTG", - "rank": 1769, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "anon-anon", - "name": "ANON", - "symbol": "ANON", - "rank": 1770, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "latx-latiumx", - "name": "LatiumX", - "symbol": "LATX", - "rank": 1771, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdp-bidipass", - "name": "BidiPass", - "symbol": "BDP", - "rank": 1772, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "berry-rentberry", - "name": "Rentberry", - "symbol": "BERRY", - "rank": 1773, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tlr-taler", - "name": "Taler", - "symbol": "TLR", - "rank": 1774, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hbit-hashbit-blockchain", - "name": "HashBit Blockchain", - "symbol": "HBIT", - "rank": 1775, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aro-arionum", - "name": "Arionum", - "symbol": "ARO", - "rank": 1776, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nrp-neural-protocol", - "name": "Neural Protocol", - "symbol": "NRP", - "rank": 1777, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "svx-savix", - "name": "Savix", - "symbol": "SVX", - "rank": 1778, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tend-tendies", - "name": "Tendies", - "symbol": "TEND", - "rank": 1779, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hb-heartbout", - "name": "HeartBout", - "symbol": "HB", - "rank": 1780, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tcash-tcash", - "name": "TCASH", - "symbol": "TCASH", - "rank": 1781, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gtm-gentarium", - "name": "Gentarium", - "symbol": "GTM", - "rank": 1782, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gap-gapcoin", - "name": "Gapcoin", - "symbol": "GAP", - "rank": 1783, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pnx-phantomx", - "name": "Phantomx", - "symbol": "PNX", - "rank": 1784, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dln-delion", - "name": "Delion", - "symbol": "DLN", - "rank": 1785, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sins-safeinsure", - "name": "SafeInsure", - "symbol": "SINS", - "rank": 1786, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "skb-sakura-bloom", - "name": "Sakura Bloom", - "symbol": "SKB", - "rank": 1787, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cspn-crypto-sports", - "name": "Crypto Sports", - "symbol": "CSPN", - "rank": 1788, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xnb-xeonbit", - "name": "Xeonbit", - "symbol": "XNB", - "rank": 1789, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ift-investfeed", - "name": "InvestFeed", - "symbol": "IFT", - "rank": 1790, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnm-mineum", - "name": "Mineum", - "symbol": "MNM", - "rank": 1791, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btb-bitbar", - "name": "BitBar", - "symbol": "BTB", - "rank": 1792, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "impl-impleum", - "name": "Impleum", - "symbol": "IMPL", - "rank": 1793, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "asafe2-allsafe", - "name": "AllSafe", - "symbol": "ASAFE", - "rank": 1794, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ctt-castweet", - "name": "Castweet", - "symbol": "CTT", - "rank": 1795, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "opal-opal", - "name": "Opal", - "symbol": "OPAL", - "rank": 1796, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "egg-nestegg-coin", - "name": "NestEgg Coin", - "symbol": "EGG", - "rank": 1797, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bta-bata", - "name": "Bata", - "symbol": "BTA", - "rank": 1798, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zcr-zcore", - "name": "ZCore", - "symbol": "ZCR", - "rank": 1799, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ytn-yenten", - "name": "YENTEN", - "symbol": "YTN", - "rank": 1800, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ecash-ethereum-cash", - "name": "Ethereum Cash", - "symbol": "ECASH", - "rank": 1801, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "put-putincoin", - "name": "PutinCoin", - "symbol": "PUT", - "rank": 1802, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "boli-bolivarcoin", - "name": "Bolivarcoin", - "symbol": "BOLI", - "rank": 1803, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btdx-bitcloud", - "name": "Bitcloud", - "symbol": "BTDX", - "rank": 1804, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "arata-arata", - "name": "Arata", - "symbol": "ARATA", - "rank": 1805, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "micro-micromines", - "name": "Micromines", - "symbol": "MICRO", - "rank": 1806, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xsh-shield", - "name": "SHIELD", - "symbol": "XSH", - "rank": 1807, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "scc-siacashcoin", - "name": "SiaCashCoin", - "symbol": "SCC", - "rank": 1808, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gcn-gcn-coin", - "name": "GCN Coin", - "symbol": "GCN", - "rank": 1809, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pulse-pulse", - "name": "Pulse", - "symbol": "PULSE", - "rank": 1810, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sat-social-activity-token", - "name": "Social Activity Token", - "symbol": "SAT", - "rank": 1811, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sig-spectiv", - "name": "Spectiv", - "symbol": "SIG", - "rank": 1812, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitc-bitcash", - "name": "BitCash", - "symbol": "BITC", - "rank": 1813, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bits-bitstar", - "name": "Bitstar", - "symbol": "BITS", - "rank": 1814, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nfxc-nfx-coin", - "name": "NFX Coin", - "symbol": "NFXC", - "rank": 1815, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "luc-level-up-coin", - "name": "Level Up Coin", - "symbol": "LUC", - "rank": 1816, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbt-bitboost", - "name": "BitBoost", - "symbol": "BBT", - "rank": 1817, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wtn-waletoken", - "name": "Waletoken", - "symbol": "WTN", - "rank": 1818, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lcp-litecoin-plus", - "name": "Litecoin Plus", - "symbol": "LCP", - "rank": 1819, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xbi-bitcoin-incognito", - "name": "Bitcoin Incognito", - "symbol": "XBI", - "rank": 1820, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btcn-bitcoinote", - "name": "BitcoiNote", - "symbol": "BTCN", - "rank": 1821, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wfx-webflix-token", - "name": "Webflix Token", - "symbol": "WFX", - "rank": 1822, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grimm-grimm", - "name": "Grimm", - "symbol": "GRIMM", - "rank": 1823, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "robet-robet", - "name": "RoBET", - "symbol": "ROBET", - "rank": 1824, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcred-bitcoin-red", - "name": "Bitcoin Red", - "symbol": "BTCRED", - "rank": 1825, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bern-berncash", - "name": "BERNcash", - "symbol": "BERN", - "rank": 1826, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bccx-blockchaincoinx", - "name": "BlockChain CoinX", - "symbol": "BCCX", - "rank": 1827, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wab-wabnetwork", - "name": "WABnetwork", - "symbol": "WAB", - "rank": 1828, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbt-rimbit", - "name": "Rimbit", - "symbol": "RBT", - "rank": 1829, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "start-startcoin", - "name": "Startcoin", - "symbol": "START", - "rank": 1830, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "plus1-plusonecoin", - "name": "PlusOneCoin", - "symbol": "PLUS1", - "rank": 1831, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cheese-cheesecoin", - "name": "Cheesecoin", - "symbol": "CHEESE", - "rank": 1832, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gb-goldblocks", - "name": "GoldBlocks", - "symbol": "GB", - "rank": 1833, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "more-more-coin", - "name": "More Coin", - "symbol": "MORE", - "rank": 1834, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icr-intercrone", - "name": "InterCrone", - "symbol": "ICR", - "rank": 1835, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fjc-fujicoin", - "name": "FujiCoin", - "symbol": "FJC", - "rank": 1836, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bro-bitradio", - "name": "Bitradio", - "symbol": "BRO", - "rank": 1837, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "jan-coinjanitor", - "name": "CoinJanitor", - "symbol": "JAN", - "rank": 1838, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dem-deutsche-emark", - "name": "Deutsche eMark", - "symbol": "DEM", - "rank": 1839, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sfcp-sf-capital", - "name": "SF Capital", - "symbol": "SFCP", - "rank": 1840, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elama-ela-coin", - "name": "ELA Coin", - "symbol": "ELAMA", - "rank": 1841, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rew-rewardiqa", - "name": "Rewardiqa", - "symbol": "REW", - "rank": 1842, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ftxt-futurax", - "name": "FUTURAX", - "symbol": "FTXT", - "rank": 1843, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inpay-inpay", - "name": "InPay", - "symbol": "INPAY", - "rank": 1844, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alley-nft-alley", - "name": "NFT Alley", - "symbol": "ALLEY", - "rank": 1845, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcl-btc-lite", - "name": "BTC Lite", - "symbol": "BTCL", - "rank": 1846, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbkfi-bitblocks-finance", - "name": "BitBlocks Finance", - "symbol": "BBKFI", - "rank": 1847, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bela-bela", - "name": "Bela", - "symbol": "BELA", - "rank": 1848, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ltb-litebar", - "name": "LiteBar", - "symbol": "LTB", - "rank": 1849, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "glt-globaltoken", - "name": "GlobalToken", - "symbol": "GLT", - "rank": 1850, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cesc-cryptoescudo", - "name": "CryptoEscudo", - "symbol": "CESC", - "rank": 1851, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "unit-universal-currency", - "name": "Universal Currency", - "symbol": "UNIT", - "rank": 1852, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "incx-internationalcryptox", - "name": "InternationalCryptoX", - "symbol": "INCX", - "rank": 1853, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arion-arion", - "name": "Arion", - "symbol": "ARION", - "rank": 1854, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bxt-bittokens", - "name": "BitTokens", - "symbol": "BXT", - "rank": 1855, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "units-gameunits", - "name": "GameUnits", - "symbol": "UNITS", - "rank": 1856, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cco-ccore", - "name": "Ccore", - "symbol": "CCO", - "rank": 1857, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tit-titcoin", - "name": "Titcoin", - "symbol": "TIT", - "rank": 1858, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bsd-bitsend", - "name": "BitSend", - "symbol": "BSD", - "rank": 1859, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pho-photon", - "name": "Photon", - "symbol": "PHO", - "rank": 1860, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ctrt-cryptrust", - "name": "Cryptrust", - "symbol": "CTRT", - "rank": 1861, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pyn-paycent", - "name": "PAYCENT", - "symbol": "PYN", - "rank": 1862, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fire-firecoin", - "name": "Firecoin", - "symbol": "FIRE", - "rank": 1863, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "neva-nevacoin", - "name": "NevaCoin", - "symbol": "NEVA", - "rank": 1864, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bloc-blocmoney", - "name": "BLOC.MONEY", - "symbol": "BLOC", - "rank": 1865, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lana-lanacoin", - "name": "LanaCoin", - "symbol": "LANA", - "rank": 1866, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "meri-merebel", - "name": "Merebel", - "symbol": "MERI", - "rank": 1867, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "herb-herbalist-token", - "name": "Herbalist Token", - "symbol": "HERB", - "rank": 1868, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crc-crycash", - "name": "CryCash", - "symbol": "CRC", - "rank": 1869, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evt-everitoken", - "name": "EveriToken", - "symbol": "EVT", - "rank": 1870, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vls-veles", - "name": "Veles", - "symbol": "VLS", - "rank": 1871, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kind-kind-ads-token", - "name": "Kind Ads Token", - "symbol": "KIND", - "rank": 1872, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iq-iqcash", - "name": "IQ.cash", - "symbol": "IQ", - "rank": 1873, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "all-allion", - "name": "Allion", - "symbol": "ALL", - "rank": 1874, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "foxd-foxdcoin", - "name": "Foxdcoin", - "symbol": "FOXD", - "rank": 1875, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sur-suretly", - "name": "Suretly", - "symbol": "SUR", - "rank": 1876, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbs-bbscoin", - "name": "BBSCoin", - "symbol": "BBS", - "rank": 1877, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ndx-ndex", - "name": "nDEX", - "symbol": "NDX", - "rank": 1878, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chnd-cashhand", - "name": "CashHand", - "symbol": "CHND", - "rank": 1879, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xfg-fango", - "name": "Fango", - "symbol": "XFG", - "rank": 1880, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "yi12-yield-stake-finance", - "name": "Yield Stake Finance", - "symbol": "YI12", - "rank": 1881, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dank-mu-dank", - "name": "MU DANK", - "symbol": "DANK", - "rank": 1882, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stv-sativacoin", - "name": "Sativacoin", - "symbol": "STV", - "rank": 1883, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "clc-caluracoin", - "name": "CaluraCoin", - "symbol": "CLC", - "rank": 1884, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xgox-xgox", - "name": "XGOX", - "symbol": "XGOX", - "rank": 1885, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "netko-netko", - "name": "Netko", - "symbol": "NETKO", - "rank": 1886, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xsm-spectrumcash", - "name": "SpectrumCash", - "symbol": "XSM", - "rank": 1887, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "araw-araw", - "name": "ARAW", - "symbol": "ARAW", - "rank": 1888, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "entrc-entercoin", - "name": "EnterCoin", - "symbol": "ENTRC", - "rank": 1889, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "care-carebit", - "name": "Carebit", - "symbol": "CARE", - "rank": 1890, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "itl-italian-lira", - "name": "Italian Lira", - "symbol": "ITL", - "rank": 1891, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brain-braincoin", - "name": "Braincoin", - "symbol": "BRAIN", - "rank": 1892, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "stu-bitjob", - "name": "bitJob", - "symbol": "STU", - "rank": 1893, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctl-citadel", - "name": "Citadel", - "symbol": "CTL", - "rank": 1894, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nplc-plus-coin", - "name": "Plus-Coin", - "symbol": "NPLC", - "rank": 1895, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "znt-zenswap-network-token", - "name": "Zenswap Network Token", - "symbol": "ZNT", - "rank": 1896, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "taj-tajcoin", - "name": "TajCoin", - "symbol": "TAJ", - "rank": 1897, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pyrk-pyrk", - "name": "Pyrk", - "symbol": "PYRK", - "rank": 1898, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tic-thingschain", - "name": "Thingschain", - "symbol": "TIC", - "rank": 1899, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcf-bitcoin-fast", - "name": "Bitcoin Fast", - "symbol": "BCF", - "rank": 1900, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mng-mangocoin", - "name": "Mangocoin", - "symbol": "MNG", - "rank": 1901, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mar-mchain", - "name": "Mchain", - "symbol": "MAR", - "rank": 1902, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bcz-bitcoin-cash-zero", - "name": "Bitcoin CZ", - "symbol": "BCZ", - "rank": 1903, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hth-help-the-homeless-coin", - "name": "Help The Homeless Coin", - "symbol": "HTH", - "rank": 1904, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cato-catocoin", - "name": "CatoCoin", - "symbol": "CATO", - "rank": 1905, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "voco-provoco-token", - "name": "Provoco Token", - "symbol": "VOCO", - "rank": 1906, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pyro-pyro-network", - "name": "PYRO Network", - "symbol": "PYRO", - "rank": 1907, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dlc-dollarcoin", - "name": "Dollarcoin", - "symbol": "DLC", - "rank": 1908, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "arepa-arepacoin", - "name": "Arepacoin", - "symbol": "AREPA", - "rank": 1909, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "knt-knekted", - "name": "Knekted", - "symbol": "KNT", - "rank": 1910, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dab-dabanking", - "name": "DABANKING", - "symbol": "DAB", - "rank": 1911, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gzro-gravity", - "name": "Gravity", - "symbol": "GZRO", - "rank": 1912, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "c2c-c2c-system", - "name": "C2C System", - "symbol": "C2C", - "rank": 1913, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bzx-bitcoin-zero", - "name": "Bitcoin Zero", - "symbol": "BZX", - "rank": 1914, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "datp-decentralized-asset-trading-platform", - "name": "Decentralized Asset Trading Platform", - "symbol": "DATP", - "rank": 1915, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcpc-mobile-crypto-pay-coin", - "name": "Mobile Crypto Pay Coin", - "symbol": "MCPC", - "rank": 1916, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mlm-mktcoin", - "name": "MktCoin", - "symbol": "MLM", - "rank": 1917, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cmct-crowd-machine", - "name": "Crowd Machine", - "symbol": "CMCT", - "rank": 1918, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xcre-creatio", - "name": "Creatio", - "symbol": "XCRE", - "rank": 1919, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sefa-mesefa", - "name": "MESEFA", - "symbol": "SEFA", - "rank": 1920, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btct-bitcoin-token", - "name": "Bitcoin Token", - "symbol": "BTCT", - "rank": 1921, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aby-artbyte", - "name": "ArtByte", - "symbol": "ABY", - "rank": 1922, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "icob-icobid", - "name": "ICOBID", - "symbol": "ICOB", - "rank": 1923, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spt-spectrum", - "name": "Spectrum", - "symbol": "SPT", - "rank": 1924, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xios-xios", - "name": "Xios", - "symbol": "XIOS", - "rank": 1925, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spk-sparks", - "name": "SparksPay", - "symbol": "SPK", - "rank": 1926, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "onx-onix", - "name": "Onix", - "symbol": "ONX", - "rank": 1927, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rntb-bitrent", - "name": "BitRent", - "symbol": "RNTB", - "rank": 1928, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scap-safecapital", - "name": "SafeCapital", - "symbol": "SCAP", - "rank": 1929, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "abs-absolute", - "name": "Absolute", - "symbol": "ABS", - "rank": 1930, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ntrn-neutron", - "name": "Neutron", - "symbol": "NTRN", - "rank": 1931, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "edrc-edrcoin", - "name": "EDRCoin", - "symbol": "EDRC", - "rank": 1932, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "scriv-scriv-network", - "name": "SCRIV NETWORK", - "symbol": "SCRIV", - "rank": 1933, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spr-spreadcoin", - "name": "SpreadCoin", - "symbol": "SPR", - "rank": 1934, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "foxt-fox-trading", - "name": "Fox Trading", - "symbol": "FOXT", - "rank": 1935, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acoin-acoin", - "name": "Acoin", - "symbol": "ACOIN", - "rank": 1936, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vlt-veltor", - "name": "Veltor", - "symbol": "VLT", - "rank": 1937, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "toto-tourist-token", - "name": "Tourist Token", - "symbol": "TOTO", - "rank": 1938, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hyfi-hyper-finance", - "name": "Hyper Finance", - "symbol": "HYFI", - "rank": 1939, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wit-witchain", - "name": "WITChain", - "symbol": "WIT", - "rank": 1940, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apc-alpha-coin", - "name": "Alpha Coin", - "symbol": "APC", - "rank": 1941, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xco-x-coin", - "name": "X-Coin", - "symbol": "XCO", - "rank": 1942, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "evo-evotion", - "name": "Evotion", - "symbol": "EVO", - "rank": 1943, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btad-bitcoin-adult", - "name": "Bitcoin Adult", - "symbol": "BTAD", - "rank": 1944, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gxx-gravitycoin", - "name": "GravityCoin", - "symbol": "GXX", - "rank": 1945, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sigt-signatum", - "name": "Signatum", - "symbol": "SIGT", - "rank": 1946, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nzl-zealium", - "name": "Zealium", - "symbol": "NZL", - "rank": 1947, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "crm-cream", - "name": "Cream", - "symbol": "CRM", - "rank": 1948, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "js-javascript-token", - "name": "JavaScript Token", - "symbol": "JS", - "rank": 1949, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ltcr-litecred", - "name": "Litecred", - "symbol": "LTCR", - "rank": 1950, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ele-elementrem", - "name": "Elementrem", - "symbol": "ELE", - "rank": 1951, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tes-teslacoin", - "name": "TeslaCoin", - "symbol": "TES", - "rank": 1952, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gmb-gmb", - "name": "GMB", - "symbol": "GMB", - "rank": 1953, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wtl-welltrado", - "name": "Welltrado", - "symbol": "WTL", - "rank": 1954, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kema-kemacoin", - "name": "Kemacoin", - "symbol": "KEMA", - "rank": 1955, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "snr-sonder", - "name": "SONDER", - "symbol": "SNR", - "rank": 1956, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcp-bitcashpay", - "name": "BitcashPay", - "symbol": "BCP", - "rank": 1957, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "web-webcoin", - "name": "Webcoin", - "symbol": "WEB", - "rank": 1958, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pti-paytomat", - "name": "Paytomat", - "symbol": "PTI", - "rank": 1959, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mst-mustangcoin", - "name": "MustangCoin", - "symbol": "MST", - "rank": 1960, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bunny-bunnytoken", - "name": "BunnyToken", - "symbol": "BUNNY", - "rank": 1961, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bios-bioscrypto", - "name": "BiosCrypto", - "symbol": "BIOS", - "rank": 1962, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zne-zonecoin", - "name": "Zonecoin", - "symbol": "ZNE", - "rank": 1963, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wxc-wxcoins", - "name": "WXCOINS", - "symbol": "WXC", - "rank": 1964, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nyex-nyerium", - "name": "Nyerium", - "symbol": "NYEX", - "rank": 1965, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ego-ego", - "name": "EGO", - "symbol": "EGO", - "rank": 1966, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "uvu-ccuniverse", - "name": "CCUniverse", - "symbol": "UVU", - "rank": 1967, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "luna-luna-coin", - "name": "Luna Coin", - "symbol": "LUNA", - "rank": 1968, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "benji-benjirolls", - "name": "BenjiRolls", - "symbol": "BENJI", - "rank": 1969, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "plc-polcoin", - "name": "Polcoin", - "symbol": "PLC", - "rank": 1970, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bets-betscoin", - "name": "BetsCoin", - "symbol": "BETS", - "rank": 1971, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dalc-dalecoin", - "name": "Dalecoin", - "symbol": "DALC", - "rank": 1972, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fntb-fintab", - "name": "Fintab", - "symbol": "FNTB", - "rank": 1973, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "g3n-g3n", - "name": "G3N", - "symbol": "G3N", - "rank": 1974, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "exo-exosis", - "name": "Exosis", - "symbol": "EXO", - "rank": 1975, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vip-vip-tokens", - "name": "VIP Tokens", - "symbol": "VIP", - "rank": 1976, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hsn-hyper-speed-network", - "name": "Hyper Speed Network", - "symbol": "HSN", - "rank": 1977, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "imgc-imagecash", - "name": "ImageCash", - "symbol": "IMGC", - "rank": 1978, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mnd-mindcoin", - "name": "MindCoin", - "symbol": "MND", - "rank": 1979, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "blast-blast", - "name": "BLAST", - "symbol": "BLAST", - "rank": 1980, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "biob-biobar", - "name": "BioBar", - "symbol": "BIOB", - "rank": 1981, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "quro-qurito", - "name": "Qurito", - "symbol": "QURO", - "rank": 1982, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orly-orlycoin", - "name": "Orlycoin", - "symbol": "ORLY", - "rank": 1983, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pie-piecoin", - "name": "PIECoin", - "symbol": "PIE", - "rank": 1984, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ath-atheios", - "name": "Atheios", - "symbol": "ATH", - "rank": 1985, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ccl-cyclean", - "name": "CYCLEAN", - "symbol": "CCL", - "rank": 1986, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crp-cranepay", - "name": "CranePay", - "symbol": "CRP", - "rank": 1987, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ella-ellaism", - "name": "Ellaism", - "symbol": "ELLA", - "rank": 1988, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cvcc-cryptoverificationcoin", - "name": "CryptoVerificationCoin", - "symbol": "CVCC", - "rank": 1989, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ucash-universal-cash", - "name": "UNIVERSAL CASH", - "symbol": "UCASH", - "rank": 1990, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ims-independent-money-system", - "name": "Independent Money System", - "symbol": "IMS", - "rank": 1991, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "knc-kingn-coin", - "name": "KingN Coin", - "symbol": "KNC", - "rank": 1992, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cwxt-cryptoworldx-token", - "name": "CryptoWorldX Token", - "symbol": "CWXT", - "rank": 1993, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xap-apollon", - "name": "Apollon", - "symbol": "XAP", - "rank": 1994, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tek-tekcoin", - "name": "TEKcoin", - "symbol": "TEK", - "rank": 1995, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "milo-milocoin", - "name": "MiloCoin", - "symbol": "MILO", - "rank": 1996, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tlo-talleo", - "name": "Talleo", - "symbol": "TLO", - "rank": 1997, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "stak-straks", - "name": "STRAKS", - "symbol": "STAK", - "rank": 1998, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tvnt-travelnote", - "name": "TravelNote", - "symbol": "TVNT", - "rank": 1999, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "light-lightning", - "name": "Lightning", - "symbol": "LIGHT", - "rank": 2000, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "visio-visio", - "name": "Visio", - "symbol": "VISIO", - "rank": 2001, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hmn-hostmasternode", - "name": "Hostmasternode", - "symbol": "HMN", - "rank": 2002, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xta-italo", - "name": "Italo", - "symbol": "XTA", - "rank": 2003, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cjt-connectjob", - "name": "ConnectJob", - "symbol": "CJT", - "rank": 2004, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fto-futurocoin", - "name": "FuturoCoin", - "symbol": "FTO", - "rank": 2005, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cmt-comet", - "name": "Comet", - "symbol": "CMT", - "rank": 2006, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rupx-rupaya", - "name": "Rupaya", - "symbol": "RUPX", - "rank": 2007, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "plnc-plncoin", - "name": "PLNcoin", - "symbol": "PLNC", - "rank": 2008, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ncp-newton-coin-project", - "name": "Newton Coin Project", - "symbol": "NCP", - "rank": 2009, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wbb-wild-beast-block", - "name": "Wild Beast Block", - "symbol": "WBB", - "rank": 2010, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lbtc-litebitcoin", - "name": "LiteBitcoin", - "symbol": "LBTC", - "rank": 2011, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lst-luckyseventoken", - "name": "LuckySevenToken", - "symbol": "LST", - "rank": 2012, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brat-brother", - "name": "BROTHER", - "symbol": "BRAT", - "rank": 2013, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "comp-compound-coin", - "name": "Compound Coin", - "symbol": "COMP", - "rank": 2014, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "abet-altbet", - "name": "Altbet", - "symbol": "Abet", - "rank": 2015, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kts-klimatas", - "name": "Klimatas", - "symbol": "KTS", - "rank": 2016, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nuko-nekonium", - "name": "Nekonium", - "symbol": "NUKO", - "rank": 2017, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mnp-mnpcoin", - "name": "MNPCoin", - "symbol": "MNP", - "rank": 2018, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "help-gohelpfund", - "name": "GoHelpFund", - "symbol": "HELP", - "rank": 2019, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fnx-finnexus", - "name": "FinNexus", - "symbol": "FNX", - "rank": 2020, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "din-dinero", - "name": "Dinero", - "symbol": "DIN", - "rank": 2021, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cdm-condominium", - "name": "CDMCOIN", - "symbol": "CDM", - "rank": 2022, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "erc20-erc20", - "name": "ERC20", - "symbol": "ERC20", - "rank": 2023, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blizz-blizzard-network", - "name": "Blizzard Network", - "symbol": "BLIZZ", - "rank": 2024, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bem-bemil-coin", - "name": "Bemil Coin", - "symbol": "BEM", - "rank": 2025, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spex-sproutsextreme", - "name": "SproutsExtreme", - "symbol": "SPEX", - "rank": 2026, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cnnc-cannation", - "name": "Cannation", - "symbol": "CNNC", - "rank": 2027, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ethp-ethplus", - "name": "ETHPlus", - "symbol": "ETHP", - "rank": 2028, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rap-rapture", - "name": "RAPTURE", - "symbol": "RAP", - "rank": 2029, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "socc-socialcoin", - "name": "SocialCoin", - "symbol": "SOCC", - "rank": 2030, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "swc-scanet-world-coin", - "name": "Scanetchain", - "symbol": "SWC", - "rank": 2031, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hmp-hempcoin", - "name": "HempCoin", - "symbol": "HMP", - "rank": 2032, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hnb-hashnet-biteco", - "name": "HashNet BitEco", - "symbol": "HNB", - "rank": 2033, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcv-bitcoinv", - "name": "BitcoinV", - "symbol": "BTCV", - "rank": 2034, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "song-songcoin", - "name": "SongCoin", - "symbol": "SONG", - "rank": 2035, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vip-vip-coin", - "name": "Vip Coin", - "symbol": "VIP", - "rank": 2036, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ira-diligence", - "name": "Diligence", - "symbol": "IRA", - "rank": 2037, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dashg-dash-green", - "name": "Dash Green", - "symbol": "DASHG", - "rank": 2038, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "val-valorbit", - "name": "Valorbit", - "symbol": "VAL", - "rank": 2039, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gpkr-gold-poker", - "name": "Gold Poker", - "symbol": "GPKR", - "rank": 2040, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "guess-peerguess", - "name": "Peerguess", - "symbol": "GUESS", - "rank": 2041, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ucom-united-crypto-community", - "name": "KZ Cash", - "symbol": "KZC", - "rank": 2042, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ltcu-litecoin-ultra", - "name": "LiteCoin Ultra", - "symbol": "LTCU", - "rank": 2043, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zod-krypton", - "name": "Krypton", - "symbol": "ZOD", - "rank": 2044, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pazzi-paparazzi", - "name": "Paparazzi", - "symbol": "PAZZI", - "rank": 2045, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbst-bbstoken", - "name": "BBSToken", - "symbol": "BBST", - "rank": 2046, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sono-sono", - "name": "SONO", - "symbol": "SONO", - "rank": 2047, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "btcone-bitcoin-one", - "name": "BitCoin One", - "symbol": "BTCONE", - "rank": 2048, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dow-dowcoin", - "name": "DOWCOIN", - "symbol": "DOW", - "rank": 2049, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eld-electrumdark", - "name": "electrumdark", - "symbol": "ELD", - "rank": 2050, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "creva-crevacoin", - "name": "CrevaCoin", - "symbol": "CREVA", - "rank": 2051, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bul-bulleon", - "name": "Bulleon", - "symbol": "BUL", - "rank": 2052, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptt-proton-token", - "name": "Proton Token", - "symbol": "PTT", - "rank": 2053, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "conx-concoin", - "name": "Concoin", - "symbol": "CONX", - "rank": 2054, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "labx-stakinglab", - "name": "Stakinglab", - "symbol": "LABX", - "rank": 2055, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bcash-bankcoin", - "name": "Bankcoin", - "symbol": "BCASH", - "rank": 2056, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fess-fesschain", - "name": "Fesschain", - "symbol": "FESS", - "rank": 2057, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hmc-harmonycoin", - "name": "HarmonyCoin", - "symbol": "HMC", - "rank": 2058, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "innbcl-innovativebioresearchclassic", - "name": "InnovativeBioresearchClassic", - "symbol": "INNBCL", - "rank": 2059, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cirq-cirquity", - "name": "Cirquity", - "symbol": "CIRQ", - "rank": 2060, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "slfi-selfiecoin", - "name": "Selfiecoin", - "symbol": "SLFI", - "rank": 2061, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nbxc-nibble", - "name": "Nibble", - "symbol": "NBXC", - "rank": 2062, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bold-boldman-capital", - "name": "Boldman Capital", - "symbol": "BOLD", - "rank": 2063, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eta-etheera", - "name": "Etheera", - "symbol": "ETA", - "rank": 2064, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abc-abc-chain", - "name": "ABC Chain", - "symbol": "ABC", - "rank": 2065, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "uct-universal-community-trust", - "name": "Universal Community Trust", - "symbol": "UCT", - "rank": 2066, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uctc-uct-cash", - "name": "UCT Cash", - "symbol": "UCTC", - "rank": 2067, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "weth-weth", - "name": "WETH", - "symbol": "WETH", - "rank": 2068, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dydx-dydx", - "name": "DYDX", - "symbol": "DYDX", - "rank": 2069, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mim-magic-internet-money", - "name": "Magic Internet Money", - "symbol": "MIM", - "rank": 2070, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sun-sun", - "name": "SUN", - "symbol": "SUN", - "rank": 2071, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "steth-lido-staked-ether", - "name": "Lido Staked Ether", - "symbol": "STETH", - "rank": 2072, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "usdi-usdi0", - "name": "USDi0", - "symbol": "USDi", - "rank": 2073, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "voxel-voxies", - "name": "Voxies", - "symbol": "VOXEL", - "rank": 2074, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alice-my-neighbor-alice", - "name": "My Neighbor Alice", - "symbol": "ALICE", - "rank": 2075, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gat-global-aex-token", - "name": "Global AEX Token", - "symbol": "GAT", - "rank": 2076, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "biot-bio-passport", - "name": "Bio Passport", - "symbol": "BIOT", - "rank": 2077, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wemix-wemix-token", - "name": "WEMIX Token", - "symbol": "WEMIX", - "rank": 2078, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bico-biconomy", - "name": "Biconomy", - "symbol": "BICO", - "rank": 2079, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "lina-linear", - "name": "Linear", - "symbol": "LINA", - "rank": 2080, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plug-plug-chain", - "name": "Plug Chain", - "symbol": "PLUG", - "rank": 2081, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "eur-euro-token", - "name": "Euro Token", - "symbol": "EUR", - "rank": 2082, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ohmv2-olympus-v2", - "name": "Olympus V2", - "symbol": "OHMv2", - "rank": 2083, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "itam-itam-games", - "name": "ITAM Games", - "symbol": "ITAM", - "rank": 2084, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bidr-binanceidr", - "name": "BinanceIDR", - "symbol": "BIDR", - "rank": 2085, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfup-natural-farm-union-protocol", - "name": "Natural Farm Union Protocol", - "symbol": "NFUP", - "rank": 2086, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metis-metis-token", - "name": "Metis Token", - "symbol": "METIS", - "rank": 2087, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fafi-famous-five", - "name": "Famous Five", - "symbol": "FAFI", - "rank": 2088, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plex-plex", - "name": "PLEX", - "symbol": "PLEX", - "rank": 2089, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gcr-gold-coin-reserve", - "name": "Gold Coin Reserve", - "symbol": "GCR", - "rank": 2090, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spell-spell-token", - "name": "Spell Token", - "symbol": "SPELL", - "rank": 2091, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kub-bitkub-coin", - "name": "Bitkub Coin", - "symbol": "KUB", - "rank": 2092, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "woop-woonkly-power", - "name": "Woonkly Power", - "symbol": "WOOP", - "rank": 2093, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sccn-succession", - "name": "SUCCESSION", - "symbol": "SCCN", - "rank": 2094, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mlk-milk-alliance", - "name": "MiL.k Alliance", - "symbol": "MLK", - "rank": 2095, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drep-drep", - "name": "DREP", - "symbol": "DREP", - "rank": 2096, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcoin-bomber-coin", - "name": "Bomber Coin", - "symbol": "BCOIN", - "rank": 2097, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "topb-topbtc-token", - "name": "TOPBTC Token", - "symbol": "TOPB", - "rank": 2098, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "woo-woo-network", - "name": "WOO Network", - "symbol": "WOO", - "rank": 2099, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ncr-neos-credits", - "name": "Neos Credits", - "symbol": "NCR", - "rank": 2100, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qi-benqi", - "name": "BENQI", - "symbol": "QI", - "rank": 2101, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ksc-kstarcoin", - "name": "KStarCoin", - "symbol": "KSC", - "rank": 2102, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cspr-casper-network", - "name": "Casper Network", - "symbol": "CSPR", - "rank": 2103, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ndx-nasdaq-100-token", - "name": "NASDAQ 100 Token", - "symbol": "NDX", - "rank": 2104, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gxt-gem-exchange-and-trading", - "name": "Gem Exchange And Trading", - "symbol": "GXT", - "rank": 2105, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babycasper-baby-casper", - "name": "Baby Casper", - "symbol": "BABYCASPER", - "rank": 2106, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "knc-kyber-network", - "name": "Kyber Network", - "symbol": "KNC", - "rank": 2107, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pyr-vulcan-forged", - "name": "Vulcan Forged", - "symbol": "PYR", - "rank": 2108, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "joe-trader-joe", - "name": "Trader Joe", - "symbol": "JOE", - "rank": 2109, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gbp-pound-sterling-token", - "name": "Pound Sterling Token", - "symbol": "GBP", - "rank": 2110, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tkx-tokenize-xchange", - "name": "Tokenize Xchange", - "symbol": "TKX", - "rank": 2111, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zcartz-zcartz", - "name": "ZCARTZ", - "symbol": "ZCARTZ", - "rank": 2112, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dagt-digital-asset-guarantee-token", - "name": "Digital Asset Guarantee Token", - "symbol": "DAGT", - "rank": 2113, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bake-bakeryswap", - "name": "BakerySwap", - "symbol": "BAKE", - "rank": 2114, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "floki-floki-inu", - "name": "Floki Inu", - "symbol": "FLOKI", - "rank": 2115, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eurt-tether-eurt", - "name": "Tether EURt", - "symbol": "EURT", - "rank": 2116, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anw-anchor-neural-world-token", - "name": "Anchor Neural World Token", - "symbol": "ANW", - "rank": 2117, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "monster-monster-valley", - "name": "Monster Valley", - "symbol": "MONSTER", - "rank": 2118, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnc-global-china-cash", - "name": "Global China Cash", - "symbol": "CNC", - "rank": 2119, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dt-dragon-token", - "name": "Dragon Token", - "symbol": "DT", - "rank": 2120, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lat-platon-network", - "name": "PlatON Network", - "symbol": "LAT", - "rank": 2121, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "celeb-celeb", - "name": "Celeb", - "symbol": "CELEB", - "rank": 2122, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gcz-globalchainz", - "name": "GlobalChainZ", - "symbol": "GCZ", - "rank": 2123, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bmp-brother-music-platform", - "name": "Brother Music Platform", - "symbol": "BMP", - "rank": 2124, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vlx-velas", - "name": "Velas", - "symbol": "VLX", - "rank": 2125, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "df-dforce-token", - "name": "dForce Token", - "symbol": "DF", - "rank": 2126, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adp-adappter-token", - "name": "Adappter Token", - "symbol": "ADP", - "rank": 2127, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xava-avalaunch", - "name": "Avalaunch", - "symbol": "XAVA", - "rank": 2128, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zig-zignaly", - "name": "Zignaly", - "symbol": "ZIG", - "rank": 2129, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unfi-unifi-protocol", - "name": "Unifi Protocol", - "symbol": "UNFI", - "rank": 2130, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rail-railgun", - "name": "Railgun", - "symbol": "RAIL", - "rank": 2131, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mm-millimeter", - "name": "Millimeter", - "symbol": "MM", - "rank": 2132, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gsx-gold-secured-currency", - "name": "Gold Secured Currency", - "symbol": "GSX", - "rank": 2133, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gbpu-upper-pound", - "name": "Upper Pound", - "symbol": "GBPU", - "rank": 2134, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zdc-zodiacs", - "name": "Zodiacs", - "symbol": "ZDC", - "rank": 2135, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "euru-upper-euro", - "name": "Upper Euro", - "symbol": "EURU", - "rank": 2136, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssg-surviving-soldiers", - "name": "Surviving Soldiers", - "symbol": "SSG", - "rank": 2137, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clv-clover-finance", - "name": "Clover Finance", - "symbol": "CLV", - "rank": 2138, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "onit-onbuff", - "name": "ONBUFF", - "symbol": "ONIT", - "rank": 2139, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jfin-jfin-coin", - "name": "JFIN Coin", - "symbol": "JFIN", - "rank": 2140, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctk-certik3652", - "name": "Certik", - "symbol": "CTK", - "rank": 2141, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lon-tokenlon", - "name": "Tokenlon", - "symbol": "LON", - "rank": 2142, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnr-mineral", - "name": "Mineral", - "symbol": "MNR", - "rank": 2143, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ldo-lido-dao", - "name": "Lido DAO", - "symbol": "LDO", - "rank": 2144, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xcad-xcad-network", - "name": "XCAD Network", - "symbol": "XCAD", - "rank": 2145, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pci-payprotocol-paycoin", - "name": "PayProtocol Paycoin", - "symbol": "PCI", - "rank": 2146, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "thg-thetan-arena", - "name": "Thetan Arena", - "symbol": "THG", - "rank": 2147, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bxmi-bxmi-token", - "name": "Bxmi Token", - "symbol": "BXMI", - "rank": 2148, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sparta-spartan-protocol-token", - "name": "Spartan Protocol Token", - "symbol": "SPARTA", - "rank": 2149, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bgc-bagcoin", - "name": "Bagcoin", - "symbol": "BGC", - "rank": 2150, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tko-tokocrypto", - "name": "Tokocrypto", - "symbol": "TKO", - "rank": 2151, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oxt-orchid-protocol", - "name": "Orchid Protocol", - "symbol": "OXT", - "rank": 2152, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sa-superalgos", - "name": "Superalgos", - "symbol": "SA", - "rank": 2153, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alusd-alchemixusd", - "name": "Alchemix USD", - "symbol": "ALUSD", - "rank": 2154, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bct-toucan-protocol-base-carbon-tonne", - "name": "Toucan Protocol: Base Carbon Tonne", - "symbol": "BCT", - "rank": 2155, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ata-automata", - "name": "Automata", - "symbol": "ATA", - "rank": 2156, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmcx-core-multichain", - "name": "CORE MultiChain", - "symbol": "CMCX", - "rank": 2157, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dj30-dow-jones-30-token", - "name": "Dow Jones 30 Token", - "symbol": "DJ30", - "rank": 2158, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kma-calamari-network", - "name": "Calamari Network", - "symbol": "KMA", - "rank": 2159, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uff-united-farmers-finance", - "name": "United Farmers Finance", - "symbol": "UFF", - "rank": 2160, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dog-dogecoin-cash", - "name": "Dogecoin Cash", - "symbol": "DOG", - "rank": 2161, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "people-constitutiondao", - "name": "ConstitutionDAO", - "symbol": "PEOPLE", - "rank": 2162, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fxc-futurexcrypto", - "name": "FUTUREXCRYPTO", - "symbol": "FXC", - "rank": 2163, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alpha-alpha-finance", - "name": "Alpha Finance", - "symbol": "ALPHA", - "rank": 2164, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccar-cryptocars", - "name": "CryptoCars", - "symbol": "CCAR", - "rank": 2165, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "att-artube", - "name": "Artube", - "symbol": "ATT", - "rank": 2166, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tru-truebit-protocol", - "name": "Truebit Protocol", - "symbol": "TRU", - "rank": 2167, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dbt-datbit", - "name": "Datbit", - "symbol": "DBT", - "rank": 2168, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "depo-depo", - "name": "Depo", - "symbol": "DEPO", - "rank": 2169, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "newo-new-order", - "name": "New Order", - "symbol": "NEWO", - "rank": 2170, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rgold-royal-gold", - "name": "Royal Gold", - "symbol": "RGOLD", - "rank": 2171, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rgt-rari-governance-token", - "name": "Rari Governance Token", - "symbol": "RGT", - "rank": 2172, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ngc-naga", - "name": "NAGA", - "symbol": "NGC", - "rank": 2173, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xau-gold-spot-token", - "name": "Gold Spot Token", - "symbol": "XAU", - "rank": 2174, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nms-nemesis-dao", - "name": "Nemesis DAO", - "symbol": "NMS", - "rank": 2175, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ubx-ubix-network", - "name": "UBIX Network", - "symbol": "UBX", - "rank": 2176, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gzx-greenzonex", - "name": "GreenZoneX", - "symbol": "GZX", - "rank": 2177, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "efi-efinity", - "name": "Efinity", - "symbol": "EFI", - "rank": 2178, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bpcn-blipcoin", - "name": "BlipCoin", - "symbol": "BPCN", - "rank": 2179, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hero-metahero", - "name": "Metahero", - "symbol": "HERO", - "rank": 2180, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bond-barnbridge", - "name": "BarnBridge", - "symbol": "BOND", - "rank": 2181, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stmx-stormx-11212", - "name": "StormX", - "symbol": "STMX", - "rank": 2182, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zkt-zktube", - "name": "zkTube", - "symbol": "ZKT", - "rank": 2183, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "master-beast-masters", - "name": "Beast Masters", - "symbol": "MASTER", - "rank": 2184, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tryb-bilira", - "name": "BiLira", - "symbol": "TRYB", - "rank": 2185, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anv-aniverse", - "name": "Aniverse", - "symbol": "ANV", - "rank": 2186, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsts-magic-beasties", - "name": "Magic Beasties", - "symbol": "BSTS", - "rank": 2187, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wing-wing-finance", - "name": "Wing Finance", - "symbol": "WING", - "rank": 2188, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lyxe-lukso-token", - "name": "LUKSO Token", - "symbol": "LYXE", - "rank": 2189, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adax-adax", - "name": "ADAX", - "symbol": "ADAX", - "rank": 2190, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xtra-xtra-fund", - "name": "Xtra Fund", - "symbol": "XTRA", - "rank": 2191, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "glch-glitch-protocol", - "name": "Glitch Protocol", - "symbol": "GLCH", - "rank": 2192, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sg-socialgood", - "name": "SocialGood", - "symbol": "SG", - "rank": 2193, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "naka-nakamoto-games", - "name": "Nakamoto Games", - "symbol": "NAKA", - "rank": 2194, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cirus-cirus", - "name": "Cirus", - "symbol": "CIRUS", - "rank": 2195, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mat-monster-adventure-token", - "name": "Monster Adventure Token", - "symbol": "MAT", - "rank": 2196, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rifi-rikkei-finance", - "name": "Rikkei Finance", - "symbol": "RIFI", - "rank": 2197, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ftg-fantomgo", - "name": "fantomGO", - "symbol": "FTG", - "rank": 2198, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lf-linkflow", - "name": "Linkflow", - "symbol": "LF", - "rank": 2199, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnx-cryptonex", - "name": "Cryptonex", - "symbol": "CNX", - "rank": 2200, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "trr-terran-coin", - "name": "Terran Coin", - "symbol": "TRR", - "rank": 2201, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cix100-cryptoindexcom-100", - "name": "Cryptoindex.com 100", - "symbol": "CIX100", - "rank": 2202, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lith-lithium-finance", - "name": "Lithium Finance", - "symbol": "LITH", - "rank": 2203, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gart-griffin-art", - "name": "Griffin Art", - "symbol": "GART", - "rank": 2204, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "epan-paypolitan-token", - "name": "Paypolitan Token", - "symbol": "EPAN", - "rank": 2205, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wncg-wrapped-ncg", - "name": "Wrapped NCG", - "symbol": "WNCG", - "rank": 2206, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ila-infinite-launch", - "name": "Infinite Launch", - "symbol": "ILA", - "rank": 2207, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lss-lossless", - "name": "Lossless", - "symbol": "LSS", - "rank": 2208, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pando-pando", - "name": "Pando", - "symbol": "PANDO", - "rank": 2209, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "beth-anchor-beth-token", - "name": "Anchor bETH Token", - "symbol": "BETH", - "rank": 2210, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fnb-finexboxtoken", - "name": "Finexbox Token", - "symbol": "FNB", - "rank": 2211, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mot-motherearth", - "name": "Motherearth", - "symbol": "MOT", - "rank": 2212, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pex-pexcoin", - "name": "Pexcoin", - "symbol": "PEX", - "rank": 2213, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btr-btrips", - "name": "BTRIPS", - "symbol": "BTR", - "rank": 2214, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ageur-ageur", - "name": "agEUR", - "symbol": "AGEUR", - "rank": 2215, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bf-bitforex", - "name": "Bitforex", - "symbol": "BF", - "rank": 2216, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "musk-musk", - "name": "Musk", - "symbol": "MUSK", - "rank": 2217, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcst-btc-standard-hashrate-token", - "name": "BTC Standard Hashrate Token", - "symbol": "BTCST", - "rank": 2218, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "realm-realm", - "name": "Realm", - "symbol": "REALM", - "rank": 2219, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vync-vynk-chain", - "name": "VYNK CHAIN", - "symbol": "VYNC", - "rank": 2220, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qark-qanplatform", - "name": "QANplatform", - "symbol": "QANX", - "rank": 2221, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xels-xels", - "name": "XELS", - "symbol": "XELS", - "rank": 2222, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boofi-boo-finance", - "name": "Boo Finance", - "symbol": "BOOFI", - "rank": 2223, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thor-thor", - "name": "Thor", - "symbol": "THOR", - "rank": 2224, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gera-gera-coin", - "name": "Gera Coin", - "symbol": "GERA", - "rank": 2225, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ovr-ovr", - "name": "Ovr", - "symbol": "OVR", - "rank": 2226, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xbr-brent-crude-oil-spot", - "name": "Brent Crude Oil Spot", - "symbol": "XBR", - "rank": 2227, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aud-australian-dollar-token", - "name": "Australian Dollar Token", - "symbol": "AUD", - "rank": 2228, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "godz-cryptogodz", - "name": "Cryptogodz", - "symbol": "GODZ", - "rank": 2229, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bae-bae", - "name": "Bae", - "symbol": "BAE", - "rank": 2230, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "refi-reimagined-finance", - "name": "Reimagined Finance", - "symbol": "REFI", - "rank": 2231, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cvp-powerpool-concentrated-voting-power", - "name": "PowerPool Concentrated Voting Power", - "symbol": "CVP", - "rank": 2232, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "syn-synapse", - "name": "Synapse", - "symbol": "SYN", - "rank": 2233, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sclp-scallop", - "name": "Scallop", - "symbol": "SCLP", - "rank": 2234, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "celt-celestial", - "name": "Celestial", - "symbol": "CELT", - "rank": 2235, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "town-town-star", - "name": "Town Star", - "symbol": "TOWN", - "rank": 2236, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edc-edcoin", - "name": "Edcoin", - "symbol": "EDC", - "rank": 2237, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spa-sperax", - "name": "Sperax", - "symbol": "SPA", - "rank": 2238, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sin-sin-city", - "name": "Sin City", - "symbol": "SIN", - "rank": 2239, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cliff-clifford-inu", - "name": "Clifford Inu", - "symbol": "CLIFF", - "rank": 2240, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pal-playandlike", - "name": "PlayAndLike", - "symbol": "PAL", - "rank": 2241, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "soba-soba-token", - "name": "SOBA Token", - "symbol": "SOBA", - "rank": 2242, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sure-insure", - "name": "inSure", - "symbol": "SURE", - "rank": 2243, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "don-donnie-finance", - "name": "Donnie Finance", - "symbol": "DON", - "rank": 2244, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1earth-earthfund", - "name": "EarthFund", - "symbol": "1EARTH", - "rank": 2245, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdx-beldex", - "name": "Beldex", - "symbol": "BDX", - "rank": 2246, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bkk-bkex-chain", - "name": "BKEX Chain", - "symbol": "BKK", - "rank": 2247, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mvp-mvp-coin", - "name": "MVP Coin", - "symbol": "MVP", - "rank": 2248, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meta-metaversepro", - "name": "MetaversePRO", - "symbol": "META", - "rank": 2249, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zcx-unizen", - "name": "Unizen", - "symbol": "ZCX", - "rank": 2250, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xng-us-natural-gas-spot", - "name": "US Natural Gas Spot", - "symbol": "XNG", - "rank": 2251, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "awo-aiwork", - "name": "AiWork", - "symbol": "AWO", - "rank": 2252, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sgb-songbird", - "name": "Songbird", - "symbol": "SGB", - "rank": 2253, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "leos-leonicorn-swap", - "name": "Leonicorn Swap", - "symbol": "LEOS", - "rank": 2254, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "toke-tokemak", - "name": "Tokemak", - "symbol": "TOKE", - "rank": 2255, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "posi-position-token", - "name": "Position Token", - "symbol": "POSI", - "rank": 2256, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tem-templar-dao", - "name": "Templar DAO", - "symbol": "TEM", - "rank": 2257, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbtc-binance-wrapped-btc", - "name": "Binance Wrapped BTC", - "symbol": "BBTC", - "rank": 2258, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "time-wonderland", - "name": "Wonderland", - "symbol": "TIME", - "rank": 2259, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfdai-yfdaifinance", - "name": "YFDAI.FINANCE", - "symbol": "YFDAI", - "rank": 2260, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cards-cardstarter", - "name": "Cardstarter", - "symbol": "CARDS", - "rank": 2261, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mxt-mixtrust", - "name": "MixTrust", - "symbol": "MXT", - "rank": 2262, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smon-starmon-token", - "name": "StarMon Token", - "symbol": "SMON", - "rank": 2263, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cream-cream", - "name": "CREAM", - "symbol": "CREAM", - "rank": 2264, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "akita-akita-inu", - "name": "Akita Inu", - "symbol": "AKITA", - "rank": 2265, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gict-gictrade", - "name": "GICTrade", - "symbol": "GICT", - "rank": 2266, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ann-annex-finance", - "name": "Annex Finance", - "symbol": "ANN", - "rank": 2267, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "torocus-torocus-token", - "name": "TOROCUS Token", - "symbol": "TOROCUS", - "rank": 2268, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sis-symbiosis-finance", - "name": "Symbiosis Finance", - "symbol": "SIS", - "rank": 2269, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alu-altura", - "name": "Altura", - "symbol": "ALU", - "rank": 2270, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "empire-empire-token", - "name": "Empire Token", - "symbol": "EMPIRE", - "rank": 2271, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babypig-baby-pig-token", - "name": "Baby Pig Token", - "symbol": "BABYPIG", - "rank": 2272, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skt-sukhavati-network", - "name": "Sukhavati Network", - "symbol": "SKT", - "rank": 2273, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arix-arix", - "name": "Arix", - "symbol": "ARIX", - "rank": 2274, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "silo-silo-finance", - "name": "Silo Finance", - "symbol": "SILO", - "rank": 2275, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apecoin-asia-pacific-electronic-coin", - "name": "Asia Pacific Electronic Coin", - "symbol": "APECOIN", - "rank": 2276, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xsushi-xsushi", - "name": "xSUSHI", - "symbol": "XSUSHI", - "rank": 2277, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "push-ethereum-push-notification-service-epns", - "name": "Ethereum Push Notification Service - EPNS", - "symbol": "PUSH", - "rank": 2278, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rly-rally", - "name": "Rally", - "symbol": "RLY", - "rank": 2279, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polc-polkacity", - "name": "Polkacity", - "symbol": "POLC", - "rank": 2280, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mimatic-mimatic", - "name": "miMatic", - "symbol": "MIMATIC", - "rank": 2281, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gls-glasscoin", - "name": "GlassCoin", - "symbol": "GLS", - "rank": 2282, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "asx-ads-social", - "name": "Ads Social", - "symbol": "ASX", - "rank": 2283, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "che-cherryswap", - "name": "CherrySwap", - "symbol": "CHE", - "rank": 2284, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbr-saber", - "name": "Saber", - "symbol": "SBR", - "rank": 2285, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vect-vectorium", - "name": "Vectorium", - "symbol": "VECT", - "rank": 2286, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hmr-homeros", - "name": "Homeros", - "symbol": "HMR", - "rank": 2287, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "charge-chargedefi", - "name": "ChargeDeFi", - "symbol": "CHARGE", - "rank": 2288, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "seth-seth", - "name": "sETH", - "symbol": "SETH", - "rank": 2289, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gami-gami-world", - "name": "GAMI World", - "symbol": "GAMI", - "rank": 2290, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nora-snowcrash-token", - "name": "SnowCrash Token", - "symbol": "NORA", - "rank": 2291, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ice-popsicle-finance", - "name": "Popsicle Finance", - "symbol": "ICE", - "rank": 2292, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arx-arcs", - "name": "ARCS", - "symbol": "ARX", - "rank": 2293, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inv-inv", - "name": "INV", - "symbol": "INV", - "rank": 2294, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tyt-tianya-token", - "name": "Tianya Token", - "symbol": "TYT", - "rank": 2295, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hit-hitbtc-token", - "name": "HitBTC Token", - "symbol": "HIT", - "rank": 2296, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sona-sona-network", - "name": "Sona Network", - "symbol": "SONA", - "rank": 2297, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oct-octopus-network", - "name": "Octopus Network", - "symbol": "OCT", - "rank": 2298, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uno-uno", - "name": "UNO", - "symbol": "UNO", - "rank": 2299, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fit-300fit", - "name": "300FIT", - "symbol": "FIT", - "rank": 2300, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tenfi-ten", - "name": "TEN", - "symbol": "TENFI", - "rank": 2301, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elg-escoin-token", - "name": "Escoin Token", - "symbol": "ELG", - "rank": 2302, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uft-unilend-finance", - "name": "UniLend Finance", - "symbol": "UFT", - "rank": 2303, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "b2m-bit2me", - "name": "Bit2Me", - "symbol": "B2M", - "rank": 2304, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcmc1-beforecoinmarketcap", - "name": "Before CoinMarketCap", - "symbol": "BCMC1", - "rank": 2305, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kine-kine-protocol", - "name": "Kine Protocol", - "symbol": "KINE", - "rank": 2306, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mass-mass", - "name": "MASS", - "symbol": "MASS", - "rank": 2307, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ltnm-bitcoin-latinum", - "name": "Bitcoin Latinum", - "symbol": "LTNM", - "rank": 2308, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ijc-ijascoin", - "name": "IjasCoin", - "symbol": "IJC", - "rank": 2309, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lox-lox-network", - "name": "Lox Network", - "symbol": "LOX", - "rank": 2310, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "credit-proxi-defi", - "name": "PROXI DeFi", - "symbol": "CREDIT", - "rank": 2311, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dark-dark-frontiers", - "name": "Dark Frontiers", - "symbol": "DARK", - "rank": 2312, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "artii-artii-token", - "name": "ARTII Token", - "symbol": "ARTII", - "rank": 2313, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbt-bitbook", - "name": "BitBook", - "symbol": "BBT", - "rank": 2314, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sip-space-sip", - "name": "Space SIP", - "symbol": "SIP", - "rank": 2315, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krex-kronn", - "name": "Kronn", - "symbol": "KREX", - "rank": 2316, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spin-spintop", - "name": "Spintop", - "symbol": "SPIN", - "rank": 2317, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "udo-unido", - "name": "Unido", - "symbol": "UDO", - "rank": 2318, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mlt-media-licensing-token", - "name": "Media Licensing Token", - "symbol": "MLT", - "rank": 2319, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gol-gogolcoin", - "name": "GogolCoin", - "symbol": "GOL", - "rank": 2320, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anchor-anchorswap", - "name": "AnchorSwap", - "symbol": "ANCHOR", - "rank": 2321, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lbk-lbk", - "name": "LBK", - "symbol": "LBK", - "rank": 2322, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "afr-afreum", - "name": "Afreum", - "symbol": "AFR", - "rank": 2323, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "planets-planetwatch", - "name": "PlanetWatch", - "symbol": "PLANETS", - "rank": 2324, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ohm-olympus", - "name": "Olympus", - "symbol": "OHM", - "rank": 2325, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xnft-xnft-protocol", - "name": "xNFT Protocol", - "symbol": "XNFT", - "rank": 2326, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spx-sp-500", - "name": "S\u0026P 500 Token", - "symbol": "SPX", - "rank": 2327, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "love-hunny-love-token", - "name": "Hunny Love Token", - "symbol": "LOVE", - "rank": 2328, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmd-dmd", - "name": "DMD", - "symbol": "DMD", - "rank": 2329, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "taral-tarality", - "name": "Tarality", - "symbol": "TARAL", - "rank": 2330, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfy-defi-for-you", - "name": "Defi For You", - "symbol": "DFY", - "rank": 2331, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lgb-let39s-go-brandon", - "name": "Let's Go Brandon", - "symbol": "LGB", - "rank": 2332, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cgs-crypto-gladiator-shards", - "name": "Crypto Gladiator Shards", - "symbol": "CGS", - "rank": 2333, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "luni-lady-uni", - "name": "Lady Uni", - "symbol": "LUNI", - "rank": 2334, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tag-tag-protocol", - "name": "Tag Protocol", - "symbol": "TAG", - "rank": 2335, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bp-bunnypark", - "name": "BunnyPark", - "symbol": "BP", - "rank": 2336, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rabbit-rabbit-finance", - "name": "Rabbit Finance", - "symbol": "RABBIT", - "rank": 2337, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "free-freerossdao", - "name": "FreeRossDAO", - "symbol": "FREE", - "rank": 2338, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nyt-new-year-token", - "name": "New Year Token", - "symbol": "NYT", - "rank": 2339, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spw-spaceship-war", - "name": "Spaceship War", - "symbol": "SPW", - "rank": 2340, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "maro-maro", - "name": "Maro", - "symbol": "MARO", - "rank": 2341, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wool-wolf-game-wool", - "name": "Wolf Game Wool", - "symbol": "WOOL", - "rank": 2342, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lvt-louverture", - "name": "Louverture", - "symbol": "LVT", - "rank": 2343, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cube-somnium-space-cubes", - "name": "Somnium Space CUBEs", - "symbol": "CUBE", - "rank": 2344, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daf-dafin", - "name": "DaFIN", - "symbol": "DAF", - "rank": 2345, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gm-goldminer", - "name": "GoldMiner", - "symbol": "GM", - "rank": 2346, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sxc-simplexchain", - "name": "SimplexChain", - "symbol": "SXC", - "rank": 2347, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bips-moneybrain-bips", - "name": "Moneybrain BiPS", - "symbol": "BIPS", - "rank": 2348, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "real-reallink", - "name": "RealLink", - "symbol": "REAL", - "rank": 2349, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csc-casinocoin", - "name": "CasinoCoin", - "symbol": "CSC", - "rank": 2350, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pvu-plant-vs-undead-token", - "name": "Plant vs Undead Token", - "symbol": "PVU", - "rank": 2351, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "piza-half-pizza", - "name": "Half Pizza", - "symbol": "PIZA", - "rank": 2352, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "p2ps-p2p-solutions", - "name": "P2P Solutions", - "symbol": "P2PS", - "rank": 2353, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "real-realy-metaverse", - "name": "Realy Metaverse", - "symbol": "REAL", - "rank": 2354, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yinbi-yinbi", - "name": "Yinbi", - "symbol": "YINBI", - "rank": 2355, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xag-silver-spot-token", - "name": "Silver Spot Token", - "symbol": "XAG", - "rank": 2356, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gcake-pancake-games", - "name": "Pancake Games", - "symbol": "GCAKE", - "rank": 2357, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kiba-kiba-inu", - "name": "Kiba Inu", - "symbol": "KIBA", - "rank": 2358, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "titano-titano", - "name": "Titano", - "symbol": "TITANO", - "rank": 2359, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pmon-polychain-monsters", - "name": "Polychain Monsters", - "symbol": "PMON", - "rank": 2360, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egc-evergrowcoin", - "name": "EverGrowCoin", - "symbol": "EGC", - "rank": 2361, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roco-roco-finance", - "name": "Roco Finance", - "symbol": "ROCO", - "rank": 2362, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poll-pollchain", - "name": "Pollchain", - "symbol": "POLL", - "rank": 2363, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boring-boringdao", - "name": "BoringDAO", - "symbol": "BORING", - "rank": 2364, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pxp-pointpay", - "name": "PointPay", - "symbol": "PXP", - "rank": 2365, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bca-bitcoiva", - "name": "BITCOIVA", - "symbol": "BCA", - "rank": 2366, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pea-pea-farm", - "name": "Pea Farm", - "symbol": "PEA", - "rank": 2367, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chlt-chellitcoin", - "name": "Chellitcoin", - "symbol": "CHLT", - "rank": 2368, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "god-bitcoin-god", - "name": "Bitcoin God", - "symbol": "GOD", - "rank": 2369, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mnst-moonstarter", - "name": "MoonStarter", - "symbol": "MNST", - "rank": 2370, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pol-proof-of-liquidity", - "name": "Proof Of Liquidity", - "symbol": "POL", - "rank": 2371, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lhb-lendhub", - "name": "Lendhub", - "symbol": "LHB", - "rank": 2372, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "king-king-swap", - "name": "King Swap", - "symbol": "KING", - "rank": 2373, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "marx-marx", - "name": "MarX", - "symbol": "MARX", - "rank": 2374, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "antex-antex", - "name": "Antex", - "symbol": "ANTEX", - "rank": 2375, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chf-swiss-franc-token", - "name": "Swiss Franc Token", - "symbol": "CHF", - "rank": 2376, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "viblo-viblo", - "name": "VIBLO", - "symbol": "VIBLO", - "rank": 2377, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dgp-dgpayment", - "name": "DGPayment", - "symbol": "DGP", - "rank": 2378, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ltn-latinio", - "name": "Latinio", - "symbol": "LTN", - "rank": 2379, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "app-sappchat", - "name": "SappChat", - "symbol": "APP", - "rank": 2380, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sss-starsharks", - "name": "StarSharks", - "symbol": "SSS", - "rank": 2381, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bho-bholdus-token", - "name": "Bholdus Token", - "symbol": "BHO", - "rank": 2382, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solpad-solpad-finance", - "name": "Solpad Finance", - "symbol": "SOLPAD", - "rank": 2383, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccxx-counosx", - "name": "CounosX", - "symbol": "CCXX", - "rank": 2384, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wnd-wonderhero", - "name": "WonderHero", - "symbol": "WND", - "rank": 2385, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hohoho-santa-floki", - "name": "Santa Floki", - "symbol": "HOHOHO", - "rank": 2386, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yak-yield-yak", - "name": "Yield Yak", - "symbol": "YAK", - "rank": 2387, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hnt-hymnode", - "name": "Hymnode", - "symbol": "HNT", - "rank": 2388, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ubu-ubu", - "name": "UBU", - "symbol": "UBU", - "rank": 2389, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "huh-huh", - "name": "HUH", - "symbol": "HUH", - "rank": 2390, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmn-crypto-media-network", - "name": "Crypto Media Network", - "symbol": "CMN", - "rank": 2391, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cad-canadian-dollar-token", - "name": "Canadian Dollar Token", - "symbol": "CAD", - "rank": 2392, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elmon-elemon", - "name": "Elemon", - "symbol": "ELMON", - "rank": 2393, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aimx-aimedis", - "name": "Aimedis", - "symbol": "AIMX", - "rank": 2394, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "leash-doge-killer", - "name": "DOGE KILLER", - "symbol": "LEASH", - "rank": 2395, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bmc-bountymarketcap", - "name": "BountyMarketCap", - "symbol": "BMC", - "rank": 2396, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pola-polaris-share", - "name": "Polaris Share", - "symbol": "POLA", - "rank": 2397, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lunapad-luna-pad", - "name": "Luna-Pad", - "symbol": "LUNAPAD", - "rank": 2398, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hudi-hudi", - "name": "Hudi", - "symbol": "HUDI", - "rank": 2399, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jbx-juicebox", - "name": "Juicebox", - "symbol": "JBX", - "rank": 2400, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xht-hollaex-token", - "name": "HollaEx Token", - "symbol": "XHT", - "rank": 2401, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ksp-klayswap-protocol", - "name": "KlaySwap Protocol", - "symbol": "KSP", - "rank": 2402, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "craft-talecraft", - "name": "TaleCraft", - "symbol": "CRAFT", - "rank": 2403, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "champ-nft-champions", - "name": "NFT Champions", - "symbol": "CHAMP", - "rank": 2404, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dbt-disco-burn-token", - "name": "Disco Burn Token", - "symbol": "DBT", - "rank": 2405, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "act-acet", - "name": "Acet", - "symbol": "ACT", - "rank": 2406, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rave-ravendex", - "name": "Ravendex", - "symbol": "RAVE", - "rank": 2407, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "occ-occamfi", - "name": "OccamFi", - "symbol": "OCC", - "rank": 2408, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rice-rooster-battle", - "name": "Rooster Battle", - "symbol": "RICE", - "rank": 2409, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tronpad-tronpad", - "name": "TRONPAD", - "symbol": "TRONPAD", - "rank": 2410, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "haka-haka", - "name": "HAKA", - "symbol": "HAKA", - "rank": 2411, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "euro-eurtoken", - "name": "eurToken", - "symbol": "EURO", - "rank": 2412, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nacho-nacho-finance", - "name": "Nacho Finance", - "symbol": "NACHO", - "rank": 2413, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apad-anypad", - "name": "Anypad", - "symbol": "APAD", - "rank": 2414, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mng-moon-nation-game", - "name": "Moon Nation Game", - "symbol": "MNG", - "rank": 2415, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cprx-crypto-perx", - "name": "Crypto Perx", - "symbol": "CPRX", - "rank": 2416, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xfc-football-coin", - "name": "Football Coin", - "symbol": "XFC", - "rank": 2417, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aos-aos", - "name": "AOS", - "symbol": "AOS", - "rank": 2418, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "apxp-apex-protocol", - "name": "APEX Protocol", - "symbol": "APXP", - "rank": 2419, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lnchx-launchx", - "name": "LaunchX", - "symbol": "LNCHX", - "rank": 2420, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "treeb-treeb", - "name": "Treeb", - "symbol": "TREEB", - "rank": 2421, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sold-solanax", - "name": "Solanax", - "symbol": "SOLD", - "rank": 2422, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evilsquid-evilsquidgame", - "name": "EvilSquidGame", - "symbol": "EVILSQUID", - "rank": 2423, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "irr-iranian-rial", - "name": "Iranian Rial", - "symbol": "IRR", - "rank": 2424, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cdrop-cryptodrop", - "name": "CryptoDrop", - "symbol": "CDROP", - "rank": 2425, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "seco-serum-ecosystem-token", - "name": "Serum Ecosystem Token", - "symbol": "SECO", - "rank": 2426, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hct-hero-cat-token", - "name": "Hero Cat Token", - "symbol": "HCT", - "rank": 2427, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pirates-metapirates", - "name": "MetaPirates", - "symbol": "PIRATES", - "rank": 2428, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dnxc-dinox", - "name": "DinoX", - "symbol": "DNXC", - "rank": 2429, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dacc-dacc", - "name": "DACC", - "symbol": "DACC", - "rank": 2430, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "imc-i-money-crypto", - "name": "i Money Crypto", - "symbol": "IMC", - "rank": 2431, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ring-ring-financial", - "name": "RING Financial", - "symbol": "RING", - "rank": 2432, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "game-gamestarter", - "name": "Gamestarter", - "symbol": "GAME", - "rank": 2433, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "g999-g999", - "name": "G999", - "symbol": "G999", - "rank": 2434, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "musk-musk-gold", - "name": "MUSK Gold", - "symbol": "MUSK", - "rank": 2435, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bt-btfinance", - "name": "BT.Finance", - "symbol": "BT", - "rank": 2436, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnet-mine-network", - "name": "MINE Network", - "symbol": "MNET", - "rank": 2437, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pool-pooltogether", - "name": "PoolTogether", - "symbol": "POOL", - "rank": 2438, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "value-value-defi", - "name": "Value DeFi", - "symbol": "VALUE", - "rank": 2439, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kws-knight-war-spirits", - "name": "Knight War Spirits", - "symbol": "KWS", - "rank": 2440, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "me-allme", - "name": "All.me", - "symbol": "ME", - "rank": 2441, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "onston-onston", - "name": "Onston", - "symbol": "ONSTON", - "rank": 2442, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bolly-bollycoin", - "name": "BollyCoin", - "symbol": "BOLLY", - "rank": 2443, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgod-metagods", - "name": "MetaGods", - "symbol": "MGOD", - "rank": 2444, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acyc-all-coins-yield-capital", - "name": "All Coins Yield Capital", - "symbol": "ACYC", - "rank": 2445, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mer-mercurial", - "name": "Mercurial", - "symbol": "MER", - "rank": 2446, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cope-cope", - "name": "Cope", - "symbol": "COPE", - "rank": 2447, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zoo-zoo-crypto-world", - "name": "ZOO - Crypto World", - "symbol": "ZOO", - "rank": 2448, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eq-equilibrium-games", - "name": "Equilibrium Games", - "symbol": "EQ", - "rank": 2449, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fodl-fodl-finance", - "name": "Fodl Finance", - "symbol": "FODL", - "rank": 2450, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smd-smd-coin", - "name": "SMD Coin", - "symbol": "SMD", - "rank": 2451, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bixb-bixbicoin", - "name": "BIXBICOIN", - "symbol": "BIXB", - "rank": 2452, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "meli-meli-games", - "name": "Meli Games", - "symbol": "MELI", - "rank": 2453, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "squid-squiddao", - "name": "SquidDao", - "symbol": "SQUID", - "rank": 2454, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drip-drip-network", - "name": "Drip Network", - "symbol": "DRIP", - "rank": 2455, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dba-digital-bank-of-africa", - "name": "Digital Bank of Africa", - "symbol": "DBA", - "rank": 2456, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arw-arowana-token", - "name": "Arowana Token", - "symbol": "ARW", - "rank": 2457, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mql-miraqle", - "name": "MiraQle", - "symbol": "MQL", - "rank": 2458, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dht-dhedge", - "name": "dHEDGE", - "symbol": "DHT", - "rank": 2459, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crdn-cardence", - "name": "Cardence", - "symbol": "CRDN", - "rank": 2460, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "war-westarter", - "name": "WeStarter", - "symbol": "WAR", - "rank": 2461, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fst-futureswap", - "name": "Futureswap", - "symbol": "FST", - "rank": 2462, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "htd-heroes-td", - "name": "Heroes TD", - "symbol": "HTD", - "rank": 2463, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nzd-new-zealand-dollar-token", - "name": "New Zealand Dollar Token", - "symbol": "NZD", - "rank": 2464, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "enno-enno-cash", - "name": "ENNO Cash", - "symbol": "ENNO", - "rank": 2465, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axc-axia-coin", - "name": "AXIA Coin", - "symbol": "AXC", - "rank": 2466, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lix-lixir-finance", - "name": "Lixir Finance", - "symbol": "LIX", - "rank": 2467, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brkl-brokoli", - "name": "Brokoli", - "symbol": "BRKL", - "rank": 2468, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qrx-quiverx", - "name": "QuiverX", - "symbol": "QRX", - "rank": 2469, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xyz-universexyz", - "name": "Universe.XYZ", - "symbol": "XYZ", - "rank": 2470, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oyt-oxydev", - "name": "OxyDev", - "symbol": "OYT", - "rank": 2471, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eternal-cryptomines-eternal", - "name": "CryptoMines Eternal", - "symbol": "ETERNAL", - "rank": 2472, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etch-elontech", - "name": "ElonTech", - "symbol": "ETCH", - "rank": 2473, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "htb-hotbit-token", - "name": "Hotbit Token", - "symbol": "HTB", - "rank": 2474, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lmcswap-limocoin-swap", - "name": "Limocoin Swap", - "symbol": "LMCSWAP", - "rank": 2475, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yo-yobit-token", - "name": "Yobit Token", - "symbol": "YO", - "rank": 2476, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ido-interstellar-domain-order", - "name": "Interstellar Domain Order", - "symbol": "IDO", - "rank": 2477, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgs-mgsocial", - "name": "MG.Social", - "symbol": "MGS", - "rank": 2478, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tok-tokok", - "name": "TOKOK", - "symbol": "TOK", - "rank": 2479, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ginza-ginza-network", - "name": "Ginza Network", - "symbol": "GINZA", - "rank": 2480, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dappx-dappstore", - "name": "dAppstore", - "symbol": "DAPPX", - "rank": 2481, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ioi-ioi-token", - "name": "IOI Token", - "symbol": "IOI", - "rank": 2482, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftd-nftrade", - "name": "NFTrade", - "symbol": "NFTD", - "rank": 2483, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gin-gin-token", - "name": "Gin Token", - "symbol": "GIN", - "rank": 2484, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eden-eden-network", - "name": "Eden Network", - "symbol": "EDEN", - "rank": 2485, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmlg-demole", - "name": "Demole", - "symbol": "DMLG", - "rank": 2486, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rusd-rusd", - "name": "rUSD", - "symbol": "RUSD", - "rank": 2487, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fufu-fufu", - "name": "Fufu", - "symbol": "FUFU", - "rank": 2488, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thxc-thxchain", - "name": "THXCHAIN", - "symbol": "THXC", - "rank": 2489, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sfd-safedeal", - "name": "SafeDeal", - "symbol": "SFD", - "rank": 2490, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "jsol-jpool", - "name": "JPool", - "symbol": "JSOL", - "rank": 2491, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kian-porta", - "name": "Porta", - "symbol": "KIAN", - "rank": 2492, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mmpro-mmpro-token", - "name": "MMPRO Token", - "symbol": "MMPRO", - "rank": 2493, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brz-brazilian-digital-token", - "name": "Brazilian Digital Token", - "symbol": "BRZ", - "rank": 2494, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gtc-gitcoin", - "name": "Gitcoin", - "symbol": "GTC", - "rank": 2495, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmcc-cmc-coin", - "name": "CMC Coin", - "symbol": "CMCC", - "rank": 2496, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oja-ojamu", - "name": "Ojamu", - "symbol": "OJA", - "rank": 2497, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polx-polylastic", - "name": "Polylastic", - "symbol": "POLX", - "rank": 2498, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fjb-let39s-go-brandon-coin", - "name": "Let's Go Brandon Coin", - "symbol": "$FJB", - "rank": 2499, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfc-safecap-token", - "name": "SafeCap Token", - "symbol": "SFC", - "rank": 2500, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfts-nft-stars", - "name": "NFT Stars", - "symbol": "NFTS", - "rank": 2501, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scrl-wizarre-scroll", - "name": "Wizarre Scroll", - "symbol": "SCRL", - "rank": 2502, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "frts-fruits", - "name": "Fruits", - "symbol": "FRTS", - "rank": 2503, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gyro-gyro", - "name": "GYRO", - "symbol": "GYRO", - "rank": 2504, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ai-multiverse", - "name": "Multiverse", - "symbol": "AI", - "rank": 2505, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drm-dodreamchain", - "name": "DoDreamChain", - "symbol": "DRM", - "rank": 2506, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pure-puriever", - "name": "Puriever", - "symbol": "PURE", - "rank": 2507, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cex-catena-x", - "name": "Catena X", - "symbol": "CEX", - "rank": 2508, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fabric-metafabric", - "name": "MetaFabric", - "symbol": "FABRIC", - "rank": 2509, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "niftsy-envelop-niftsy", - "name": "Envelop (Niftsy)", - "symbol": "NIFTSY", - "rank": 2510, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "helmet-helmet-insure", - "name": "Helmet Insure", - "symbol": "HELMET", - "rank": 2511, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lxf-luxfi", - "name": "LuxFi", - "symbol": "LXF", - "rank": 2512, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abl-airbloc", - "name": "Airbloc", - "symbol": "ABL", - "rank": 2513, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etlt-ethereum-lightning-token", - "name": "Ethereum Lightning Token", - "symbol": "ETLT", - "rank": 2514, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hnc-hnc-coin", - "name": "HNC Coin", - "symbol": "HNC", - "rank": 2515, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ring-darwinia-network-native-token", - "name": "Darwinia Network Native Token", - "symbol": "RING", - "rank": 2516, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lordz-meme-lordz", - "name": "Meme Lordz", - "symbol": "$LORDZ", - "rank": 2517, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "agfi-aggregatedfinance", - "name": "AggregatedFinance", - "symbol": "AGFI", - "rank": 2518, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chain-chain-games", - "name": "Chain Games", - "symbol": "CHAIN", - "rank": 2519, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctl-twelve-legions", - "name": "Twelve Legions", - "symbol": "CTL", - "rank": 2520, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ejs-enjinstarter", - "name": "Enjinstarter", - "symbol": "EJS", - "rank": 2521, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cate-catecoin", - "name": "CateCoin", - "symbol": "CATE", - "rank": 2522, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brise-bitrise-token", - "name": "Bitrise Token", - "symbol": "BRISE", - "rank": 2523, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chow-chow-inu", - "name": "Chow Inu", - "symbol": "CHOW", - "rank": 2524, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zenith-zenith-chain", - "name": "Zenith Chain", - "symbol": "ZENITH", - "rank": 2525, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atl-atlantis-loans", - "name": "Atlantis Loans", - "symbol": "ATL", - "rank": 2526, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "avt-artverse-token", - "name": "Artverse Token", - "symbol": "AVT", - "rank": 2527, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vodka-vodka-token", - "name": "Vodka Token", - "symbol": "VODKA", - "rank": 2528, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmc-gokumarket-credit", - "name": "GokuMarket Credit", - "symbol": "GMC", - "rank": 2529, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dah-dirham", - "name": "Dirham", - "symbol": "DAH", - "rank": 2530, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfo-yfione", - "name": "YFIONE", - "symbol": "YFO", - "rank": 2531, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "woj-wojak-finance", - "name": "Wojak Finance", - "symbol": "WOJ", - "rank": 2532, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blzz-blizz-finance", - "name": "Blizz Finance", - "symbol": "BLZZ", - "rank": 2533, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tem-temtum", - "name": "Temtum", - "symbol": "TEM", - "rank": 2534, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tube2-tube2", - "name": "TUBE2", - "symbol": "TUBE2", - "rank": 2535, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vdv-vdv-token", - "name": "VDV Token", - "symbol": "VDV", - "rank": 2536, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unw-uniworld", - "name": "UniWorld", - "symbol": "UNW", - "rank": 2537, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mswap-muftswap-token", - "name": "MUFTSwap Token", - "symbol": "MSWAP", - "rank": 2538, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "muft-manutd-fan-token", - "name": "ManUtd Fan Token", - "symbol": "MUFT", - "rank": 2539, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnx-cofinex", - "name": "Cofinex", - "symbol": "CNX", - "rank": 2540, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hlp-hlp-token", - "name": "HLP Token", - "symbol": "HLP", - "rank": 2541, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bhax-bithashex", - "name": "Bithashex", - "symbol": "BHAX", - "rank": 2542, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcvt-bitcoinvend", - "name": "BitcoinVend", - "symbol": "BCVT", - "rank": 2543, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xcv-xcarnival", - "name": "XCarnival", - "symbol": "XCV", - "rank": 2544, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lfw-legends-of-fantasy-war", - "name": "Legends of Fantasy War", - "symbol": "LFW", - "rank": 2545, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "carr-carnomaly", - "name": "Carnomaly", - "symbol": "CARR", - "rank": 2546, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tpt-token-pocket", - "name": "Token Pocket", - "symbol": "TPT", - "rank": 2547, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bme-bitcomine", - "name": "BitcoMine", - "symbol": "BME", - "rank": 2548, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cxc-capital-x-cell", - "name": "CAPITAL X CELL", - "symbol": "CXC", - "rank": 2549, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zodi-zodium", - "name": "Zodium", - "symbol": "ZODI", - "rank": 2550, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfs-cryptoforspeed", - "name": "CryptoForSpeed", - "symbol": "CFS", - "rank": 2551, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sta-defi-stoa", - "name": "Defi STOA", - "symbol": "STA", - "rank": 2552, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "libre-libre-defi", - "name": "Libre DeFi", - "symbol": "LIBRE", - "rank": 2553, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jub-jumbo", - "name": "Jumbo", - "symbol": "JUB", - "rank": 2554, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jojo-jojo", - "name": "JOJO", - "symbol": "JOJO", - "rank": 2555, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tok-tokenplace", - "name": "Tokenplace", - "symbol": "TOK", - "rank": 2556, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cvr-cvr", - "name": "CVR", - "symbol": "CVR", - "rank": 2557, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fwt-freeway-token", - "name": "Freeway Token", - "symbol": "FWT", - "rank": 2558, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zyro-zyro", - "name": "Zyro", - "symbol": "ZYRO", - "rank": 2559, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wx-wavesexchange-token", - "name": "Waves.Exchange Token", - "symbol": "WX", - "rank": 2560, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cave-crypto-cavemen", - "name": "Crypto Cavemen", - "symbol": "CAVE", - "rank": 2561, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cliq-deficliq", - "name": "DefiCliq", - "symbol": "CLIQ", - "rank": 2562, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kdc-dronefly", - "name": "DroneFly", - "symbol": "KDC", - "rank": 2563, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "widi-widiland", - "name": "WidiLand", - "symbol": "WIDI", - "rank": 2564, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dxct-dnaxcat", - "name": "DNAxCAT", - "symbol": "DXCT", - "rank": 2565, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xti-us-crude-oil-spot-token", - "name": "US Crude Oil Spot Token", - "symbol": "XTI", - "rank": 2566, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xet-xfinite-entertainment-token", - "name": "Xfinite Entertainment Token", - "symbol": "XET", - "rank": 2567, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chng-chainge-finance", - "name": "Chainge Finance", - "symbol": "CHNG", - "rank": 2568, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wducx-wrapped-ducatusx", - "name": "Wrapped DucatusX", - "symbol": "WDUCX", - "rank": 2569, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ibfr-ibuffer-token", - "name": "iBuffer Token", - "symbol": "IBFR", - "rank": 2570, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adk-aidos-kuneen", - "name": "Aidos Kuneen", - "symbol": "ADK", - "rank": 2571, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "titan-iron-titanium-token", - "name": "IRON Titanium Token", - "symbol": "TITAN", - "rank": 2572, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wars-metawars", - "name": "MetaWars", - "symbol": "WARS", - "rank": 2573, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swapz-swapzapp", - "name": "SWAPZ.app", - "symbol": "SWAPZ", - "rank": 2574, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gemg-gemguardian", - "name": "Gem Guardian", - "symbol": "GEMG", - "rank": 2575, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cali-calicoin", - "name": "CaliCoin", - "symbol": "CALI", - "rank": 2576, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "strm-instrumental-finance", - "name": "Instrumental Finance", - "symbol": "STRM", - "rank": 2577, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ger-ginza-eternity-reward", - "name": "Ginza Eternity Reward", - "symbol": "GER", - "rank": 2578, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fold-manifold-finance", - "name": "Manifold Finance", - "symbol": "FOLD", - "rank": 2579, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wsienna-sienna-erc-20", - "name": "Sienna [ERC-20]", - "symbol": "WSIENNA", - "rank": 2580, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptt-potent", - "name": "POTENT", - "symbol": "PTT", - "rank": 2581, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gdt-globe-derivative-exchange", - "name": "Globe Derivative Exchange", - "symbol": "GDT", - "rank": 2582, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drc-drc-mobility", - "name": "DRC Mobility", - "symbol": "DRC", - "rank": 2583, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dodi-doubledice", - "name": "DoubleDice", - "symbol": "DODI", - "rank": 2584, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cspd-casperpad", - "name": "CasperPad", - "symbol": "CSPD", - "rank": 2585, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "els-elysian-token", - "name": "Elysian Token", - "symbol": "ELS", - "rank": 2586, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ixt-ix-token", - "name": "IX Token", - "symbol": "IXT", - "rank": 2587, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kpc-keeps-coin", - "name": "KEEPs Coin", - "symbol": "KPC", - "rank": 2588, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bulls-bulls", - "name": "BULLS", - "symbol": "BULLS", - "rank": 2589, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tlb-the-luxury-coin", - "name": "The Luxury Coin", - "symbol": "TLB", - "rank": 2590, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solar-solarmine", - "name": "Solarmine", - "symbol": "SOLAR", - "rank": 2591, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbc3-ruby-currency", - "name": "RUBY CURRENCY", - "symbol": "RBC3", - "rank": 2592, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsla-tessla-coin", - "name": "Tessla Coin", - "symbol": "TSLA", - "rank": 2593, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "don-donkey-token", - "name": "Donkey Token", - "symbol": "DON", - "rank": 2594, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pkoin-pocketcoin", - "name": "Pocketcoin", - "symbol": "PKOIN", - "rank": 2595, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dashd-dash-diamond", - "name": "Dash Diamond", - "symbol": "DASHD", - "rank": 2596, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mepad-memepad", - "name": "MemePad", - "symbol": "MEPAD", - "rank": 2597, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ionx-charged-particles", - "name": "Charged Particles", - "symbol": "IONX", - "rank": 2598, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wgc-green-climate-world", - "name": "Green Climate World", - "symbol": "WGC", - "rank": 2599, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "admn-adioman", - "name": "Adioman", - "symbol": "ADMN", - "rank": 2600, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rosn-roseon-finance", - "name": "Roseon Finance", - "symbol": "ROSN", - "rank": 2601, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eps-epanus", - "name": "Epanus", - "symbol": "EPS", - "rank": 2602, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gusdt-global-utility-smart-digital-token", - "name": "Global Utility Smart Digital Token", - "symbol": "GUSDT", - "rank": 2603, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blxm-bloxmove", - "name": "bloXmove", - "symbol": "BLXM", - "rank": 2604, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atri-atari-token", - "name": "Atari Token", - "symbol": "ATRI", - "rank": 2605, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wcelo-wrapped-celo", - "name": "Wrapped CELO", - "symbol": "WCELO", - "rank": 2606, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btr-bitriver", - "name": "Bitriver", - "symbol": "BTR", - "rank": 2607, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kalam-kalamint", - "name": "Kalamint", - "symbol": "KALAM", - "rank": 2608, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xgold-xgold-coin", - "name": "Xgold Coin", - "symbol": "XGOLD", - "rank": 2609, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "torii-torii-finance", - "name": "Torii Finance", - "symbol": "TORII", - "rank": 2610, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inuyasha-inuyasha", - "name": "Inuyasha", - "symbol": "INUYASHA", - "rank": 2611, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ngl-gold-fever-native-gold", - "name": "Gold Fever Native Gold", - "symbol": "NGL", - "rank": 2612, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alk-alkemi-network-dao-token", - "name": "Alkemi Network DAO Token", - "symbol": "ALK", - "rank": 2613, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbnd-beatbind", - "name": "BeatBind", - "symbol": "BBND", - "rank": 2614, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvst-revest-finance", - "name": "Revest Finance", - "symbol": "RVST", - "rank": 2615, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zeon-zeon-network", - "name": "ZEON Network", - "symbol": "ZEON", - "rank": 2616, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jcc-junca-cash", - "name": "Junca cash", - "symbol": "JCC", - "rank": 2617, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hwl-howl-city", - "name": "Howl City", - "symbol": "HWL", - "rank": 2618, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bel-belrium", - "name": "Belrium", - "symbol": "BEL", - "rank": 2619, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cbank-crypto-bank", - "name": "Crypto Bank", - "symbol": "CBANK", - "rank": 2620, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ztc-zeto", - "name": "ZeTo", - "symbol": "ZTC", - "rank": 2621, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "entr-enterdao", - "name": "EnterDAO", - "symbol": "ENTR", - "rank": 2622, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "leo-leo", - "name": "Leo", - "symbol": "LEO", - "rank": 2623, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myra-mytheria", - "name": "Mytheria", - "symbol": "MYRA", - "rank": 2624, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babi-babylons", - "name": "Babylons", - "symbol": "BABI", - "rank": 2625, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apl-apollon-limassol-fan-token", - "name": "Apollon Limassol Fan Token", - "symbol": "APL", - "rank": 2626, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zptc-zeptagram", - "name": "Zeptagram", - "symbol": "ZPTC", - "rank": 2627, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plentycoin-plentycoin", - "name": "PlentyCoin", - "symbol": "PLENTYCOIN", - "rank": 2628, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnsd-bnsd-finance", - "name": "BNSD Finance", - "symbol": "BNSD", - "rank": 2629, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ibeur-iron-bank-euro", - "name": "Iron Bank EURO", - "symbol": "IBEUR", - "rank": 2630, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "l2p-lung-protocol", - "name": "Lung Protocol", - "symbol": "L2P", - "rank": 2631, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bent-bent-finance", - "name": "Bent Finance", - "symbol": "BENT", - "rank": 2632, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dnf-dnft-protocol", - "name": "DNFT Protocol", - "symbol": "DNF", - "rank": 2633, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wtk-wadzpay-token", - "name": "WadzPay Token", - "symbol": "WTK", - "rank": 2634, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ulti-ulti-arena", - "name": "Ulti Arena", - "symbol": "ULTI", - "rank": 2635, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pln-pollen", - "name": "Pollen", - "symbol": "PLN", - "rank": 2636, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sipher-sipher", - "name": "Sipher", - "symbol": "SIPHER", - "rank": 2637, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zatcoin-zatcoin", - "name": "ZATCOIN", - "symbol": "ZATCOIN", - "rank": 2638, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dod-day-of-defeat", - "name": "Day Of Defeat", - "symbol": "DOD", - "rank": 2639, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "run-run", - "name": "Run", - "symbol": "RUN", - "rank": 2640, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bpt-blackpool-token", - "name": "BlackPool Token", - "symbol": "BPT", - "rank": 2641, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jfi-jackpoolfinance", - "name": "JackPool finance", - "symbol": "JFI", - "rank": 2642, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "artm-artm", - "name": "ARTM", - "symbol": "ARTM", - "rank": 2643, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cind-cindrum", - "name": "Cindrum", - "symbol": "CIND", - "rank": 2644, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdefi-xdefi", - "name": "XDEFI", - "symbol": "XDEFI", - "rank": 2645, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lien-lien", - "name": "Lien", - "symbol": "LIEN", - "rank": 2646, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sqm-squid-moon", - "name": "Squid Moon", - "symbol": "SQM", - "rank": 2647, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hghg-hughug", - "name": "HUGHUG", - "symbol": "HGHG", - "rank": 2648, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tbtc-tbtc", - "name": "tBTC", - "symbol": "TBTC", - "rank": 2649, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "weapon-megaweapon", - "name": "Megaweapon", - "symbol": "$WEAPON", - "rank": 2650, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gero-gerowallet", - "name": "GeroWallet", - "symbol": "GERO", - "rank": 2651, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "comfi-complifi", - "name": "CompliFi", - "symbol": "COMFI", - "rank": 2652, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dna-metaverse-dna", - "name": "Metaverse DNA", - "symbol": "DNA", - "rank": 2653, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gamer-gamestation", - "name": "GameStation", - "symbol": "GAMER", - "rank": 2654, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qbit-project-quantum", - "name": "Project Quantum", - "symbol": "QBIT", - "rank": 2655, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "txa-txa", - "name": "TXA", - "symbol": "TXA", - "rank": 2656, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daovc-daovc", - "name": "DAOvc", - "symbol": "DAOVC", - "rank": 2657, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pearl-pearl-finance", - "name": "Pearl Finance", - "symbol": "PEARL", - "rank": 2658, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hc-hashland-coin", - "name": "HashLand Coin", - "symbol": "HC", - "rank": 2659, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bull-strong-bull", - "name": "Strong Bull", - "symbol": "BULL", - "rank": 2660, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "es-era-swap-token", - "name": "Era Swap Token", - "symbol": "ES", - "rank": 2661, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "micn-mindexnew", - "name": "Mindexnew", - "symbol": "MICN", - "rank": 2662, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vent-vent-finance", - "name": "Vent Finance", - "symbol": "VENT", - "rank": 2663, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "redi-redi", - "name": "REDi", - "symbol": "REDI", - "rank": 2664, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vsp-vesper-finance", - "name": "Vesper Finance", - "symbol": "VSP", - "rank": 2665, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfty-nfty-token", - "name": "NFTY Token", - "symbol": "NFTY", - "rank": 2666, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "erpt-encrypter", - "name": "Encrypter", - "symbol": "ERPT", - "rank": 2667, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unn-union-protocol-governance-token", - "name": "UNION Protocol Governance Token", - "symbol": "UNN", - "rank": 2668, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slt-smartlands-network", - "name": "Smartlands Network", - "symbol": "SLT", - "rank": 2669, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flq-flexq", - "name": "FlexQ", - "symbol": "FLQ", - "rank": 2670, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vx-vitex-coin", - "name": "ViteX coin", - "symbol": "VX", - "rank": 2671, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ghd-giftedhands", - "name": "Giftedhands", - "symbol": "GHD", - "rank": 2672, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvf-rocket-vault-rocketx", - "name": "Rocket Vault-RocketX", - "symbol": "RVF", - "rank": 2673, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crace-coinracer", - "name": "Coinracer", - "symbol": "CRACE", - "rank": 2674, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ple-plethori", - "name": "Plethori", - "symbol": "PLE", - "rank": 2675, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qi-qiswap", - "name": "QiSwap", - "symbol": "QI", - "rank": 2676, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nsfw-xxxnifty", - "name": "xxxNifty", - "symbol": "NSFW", - "rank": 2677, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reec-renewable-electronic-energy-coin", - "name": "Renewable Electronic Energy Coin", - "symbol": "REEC", - "rank": 2678, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vrn-varen", - "name": "Varen", - "symbol": "VRN", - "rank": 2679, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bamboo-bamboodefi", - "name": "BambooDeFi", - "symbol": "BAMBOO", - "rank": 2680, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "str-solster", - "name": "Solster", - "symbol": "STR", - "rank": 2681, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hve2-uhive", - "name": "Uhive", - "symbol": "HVE2", - "rank": 2682, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dhs-dirham-crypto", - "name": "Dirham Crypto", - "symbol": "DHS", - "rank": 2683, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "f9-falcon-nine", - "name": "Falcon Nine", - "symbol": "F9", - "rank": 2684, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jgn-juggernaut7492", - "name": "Juggernaut", - "symbol": "JGN", - "rank": 2685, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tenshi-tenshi", - "name": "Tenshi", - "symbol": "TENSHI", - "rank": 2686, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xrdoge-xrdoge", - "name": "XRdoge", - "symbol": "XRDOGE", - "rank": 2687, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frr-front-row", - "name": "Front Row", - "symbol": "FRR", - "rank": 2688, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "isdt-istardust", - "name": "Istardust", - "symbol": "ISDT", - "rank": 2689, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meld-melandai", - "name": "Meland.ai", - "symbol": "MELD", - "rank": 2690, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fcf-french-connection-finance", - "name": "French Connection Finance", - "symbol": "FCF", - "rank": 2691, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tarm-armtoken", - "name": "Armtoken", - "symbol": "TARM", - "rank": 2692, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roya-royale", - "name": "Royale", - "symbol": "ROYA", - "rank": 2693, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gzlr-guzzler", - "name": "Guzzler", - "symbol": "GZLR", - "rank": 2694, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fcc-filecash", - "name": "FileCash", - "symbol": "FCC", - "rank": 2695, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "inst-instadapp", - "name": "Instadapp", - "symbol": "INST", - "rank": 2696, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bxh-bxh-token", - "name": "BXH Token", - "symbol": "BXH", - "rank": 2697, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lpnt-luxurious-pro-network-token", - "name": "Luxurious Pro Network Token", - "symbol": "LPNT", - "rank": 2698, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftpad-nftpad", - "name": "NFTPad", - "symbol": "NFTPAD", - "rank": 2699, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mrcr-mercor-finance", - "name": "Mercor Finance", - "symbol": "MRCR", - "rank": 2700, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solr-solrazr", - "name": "SolRazr", - "symbol": "SOLR", - "rank": 2701, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pgen-polygen", - "name": "Polygen", - "symbol": "PGEN", - "rank": 2702, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fch-freecash", - "name": "Freecash", - "symbol": "FCH", - "rank": 2703, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "milk-the-crypto-you", - "name": "The Crypto You", - "symbol": "MILK", - "rank": 2704, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1sol-1sol", - "name": "1Sol", - "symbol": "1SOL", - "rank": 2705, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wegro-wegro", - "name": "WeGro", - "symbol": "WEGRO", - "rank": 2706, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zlk-zenlink-network-token", - "name": "Zenlink Network Token", - "symbol": "ZLK", - "rank": 2707, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ehb-earnhub", - "name": "EarnHub", - "symbol": "EHB", - "rank": 2708, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sunny-sunny-aggregator", - "name": "Sunny Aggregator", - "symbol": "SUNNY", - "rank": 2709, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "banana-banana-finance", - "name": "Banana Finance", - "symbol": "BANANA", - "rank": 2710, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blocks-blocks", - "name": "BLOCKS", - "symbol": "BLOCKS", - "rank": 2711, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mny-moonienft", - "name": "MoonieNFT", - "symbol": "MNY", - "rank": 2712, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "port-port-finance", - "name": "Port Finance", - "symbol": "PORT", - "rank": 2713, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bntx-biontech-se", - "name": "Biontech SE", - "symbol": "BNTX", - "rank": 2714, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krl-kart-racing-league", - "name": "Kart Racing League", - "symbol": "KRL", - "rank": 2715, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hakka-hakka-finance", - "name": "Hakka Finance", - "symbol": "HAKKA", - "rank": 2716, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zone-gridzone", - "name": "GridZone", - "symbol": "ZONE", - "rank": 2717, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "umi-universal-money-instrument", - "name": "Universal Money Instrument", - "symbol": "UMI", - "rank": 2718, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "toncoin-the-open-network", - "name": "The Open Network", - "symbol": "TONCOIN", - "rank": 2719, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "silk-silkchain", - "name": "SilkChain", - "symbol": "SILK", - "rank": 2720, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sum-sumswap", - "name": "SumSwap", - "symbol": "SUM", - "rank": 2721, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kitty-kitty-finance", - "name": "Kitty Finance", - "symbol": "KITTY", - "rank": 2722, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bhp-blockchain-of-hash-power", - "name": "Blockchain of Hash Power", - "symbol": "BHP", - "rank": 2723, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ds-destorage", - "name": "DeStorage", - "symbol": "DS", - "rank": 2724, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dre-doren", - "name": "DoRen", - "symbol": "DRE", - "rank": 2725, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rd-round-dollar", - "name": "Round Dollar", - "symbol": "RD", - "rank": 2726, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mefa-metaverse-face", - "name": "Metaverse Face", - "symbol": "MEFA", - "rank": 2727, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "byg-black-eye-galaxy", - "name": "Black Eye Galaxy", - "symbol": "BYG", - "rank": 2728, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axl-axial", - "name": "AXiaL", - "symbol": "AXL", - "rank": 2729, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "br34p-br34p", - "name": "BR34P", - "symbol": "BR34P", - "rank": 2730, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "simp-simp", - "name": "Simp", - "symbol": "SIMP", - "rank": 2731, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mars-mars", - "name": "Mars", - "symbol": "MARS", - "rank": 2732, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chiv-chiva-token", - "name": "Chiva Token", - "symbol": "CHIV", - "rank": 2733, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lace-lovelace-world", - "name": "Lovelace World", - "symbol": "LACE", - "rank": 2734, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fts-fortress", - "name": "Fortress", - "symbol": "FTS", - "rank": 2735, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "psb-planet-sandbox", - "name": "Planet Sandbox", - "symbol": "PSB", - "rank": 2736, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mia-miamicoin", - "name": "MiamiCoin", - "symbol": "MIA", - "rank": 2737, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kubic-kubic", - "name": "Kubic", - "symbol": "KUBIC", - "rank": 2738, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "deso-decentralized-social", - "name": "Decentralized Social", - "symbol": "DESO", - "rank": 2739, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gst-gunstar-metaverse", - "name": "Gunstar Metaverse", - "symbol": "GST", - "rank": 2740, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ceur-celo-euro", - "name": "Celo Euro", - "symbol": "cEUR", - "rank": 2741, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsc-trustercoin", - "name": "TrusterCoin", - "symbol": "TSC", - "rank": 2742, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pros-proswap", - "name": "ProSwap", - "symbol": "PROS", - "rank": 2743, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lcinu-lilcheems", - "name": "Lilcheems", - "symbol": "LCINU", - "rank": 2744, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "xbx-xbx", - "name": "XBX", - "symbol": "XBX", - "rank": 2745, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amc-amc-fight-night", - "name": "AMC Fight Night", - "symbol": "AMC", - "rank": 2746, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "weld-weld", - "name": "Weld", - "symbol": "WELD", - "rank": 2747, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wsb-wallstreetbets-dapp", - "name": "WallStreetBets DApp", - "symbol": "WSB", - "rank": 2748, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jack-jack-token", - "name": "Jack Token", - "symbol": "JACK", - "rank": 2749, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pornrocket-pornrocket", - "name": "PornRocket", - "symbol": "PORNROCKET", - "rank": 2750, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "forestplus-the-forbidden-forest", - "name": "The Forbidden Forest", - "symbol": "FORESTPLUS", - "rank": 2751, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cola-cola-token", - "name": "Cola Token", - "symbol": "COLA", - "rank": 2752, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smtf-smartfi", - "name": "SmartFi", - "symbol": "SMTF", - "rank": 2753, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pixel-pixelverse", - "name": "PixelVerse", - "symbol": "PIXEL", - "rank": 2754, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "a4-a4-finance", - "name": "A4 Finance", - "symbol": "A4", - "rank": 2755, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zinu-zombie-inu", - "name": "Zombie Inu", - "symbol": "ZINU", - "rank": 2756, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crwny-crowny-token", - "name": "Crowny Token", - "symbol": "CRWNY", - "rank": 2757, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bxr-blockster", - "name": "Blockster", - "symbol": "BXR", - "rank": 2758, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spi-shoppingio", - "name": "Shopping.io", - "symbol": "SPI", - "rank": 2759, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tom-tom-finance", - "name": "TOM Finance", - "symbol": "TOM", - "rank": 2760, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gpool-genesis-pool", - "name": "Genesis Pool", - "symbol": "GPOOL", - "rank": 2761, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsr-binstarter", - "name": "BinStarter", - "symbol": "BSR", - "rank": 2762, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ads-alkimi", - "name": "Alkimi", - "symbol": "ADS", - "rank": 2763, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arm-armours", - "name": "Armours", - "symbol": "ARM", - "rank": 2764, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sharpei-shar-pei", - "name": "Shar Pei", - "symbol": "SHARPEI", - "rank": 2765, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "media-media-network", - "name": "Media Network", - "symbol": "MEDIA", - "rank": 2766, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mons-monsters-clan", - "name": "Monsters Clan", - "symbol": "MONS", - "rank": 2767, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nst-nft-starter", - "name": "NFT Starter", - "symbol": "NST", - "rank": 2768, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tai-tbridge-token", - "name": "tBridge Token", - "symbol": "TAI", - "rank": 2769, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wrk-blockwrk", - "name": "BlockWRK", - "symbol": "WRK", - "rank": 2770, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hol-hololoot", - "name": "Hololoot", - "symbol": "HOL", - "rank": 2771, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qqq-poseidon-network", - "name": "Poseidon Network", - "symbol": "QQQ", - "rank": 2772, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "isr-insureum", - "name": "Insureum", - "symbol": "ISR", - "rank": 2773, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snp-synapse-network", - "name": "Synapse Network", - "symbol": "SNP", - "rank": 2774, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "asia-asia-coin", - "name": "Asia Coin", - "symbol": "ASIA", - "rank": 2775, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pbtc35a-pbtc35a", - "name": "pBTC35A", - "symbol": "PBTC35A", - "rank": 2776, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kick-kickio", - "name": "KICK.IO", - "symbol": "KICK", - "rank": 2777, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elcash-electric-cash", - "name": "Electric Cash", - "symbol": "ELCASH", - "rank": 2778, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hope-hope-token", - "name": "Hope Token", - "symbol": "HOPE", - "rank": 2779, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dld-daoland", - "name": "Daoland", - "symbol": "DLD", - "rank": 2780, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goal-goal-token", - "name": "Goal Token", - "symbol": "GOAL", - "rank": 2781, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "awt-airdrop-world", - "name": "Airdrop World", - "symbol": "AWT", - "rank": 2782, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gens-genshiro", - "name": "Genshiro", - "symbol": "GENS", - "rank": 2783, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trdc-traders-coin", - "name": "Traders Coin", - "symbol": "TRDC", - "rank": 2784, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "d11-defi11", - "name": "DeFi11", - "symbol": "D11", - "rank": 2785, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfxt-chainflix", - "name": "Chainflix", - "symbol": "CFXT", - "rank": 2786, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gfn-graphene", - "name": "Graphene", - "symbol": "GFN", - "rank": 2787, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kfs-kindness-for-soul", - "name": "Kindness For Soul", - "symbol": "KFS", - "rank": 2788, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adco-advertise-coin", - "name": "Advertise Coin", - "symbol": "ADCO", - "rank": 2789, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rtm-raptoreum", - "name": "Raptoreum", - "symbol": "RTM", - "rank": 2790, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tdf-trade-fighter", - "name": "Trade Fighter", - "symbol": "TDF", - "rank": 2791, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wpkt-wrapped-pkt", - "name": "Wrapped PKT", - "symbol": "WPKT", - "rank": 2792, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spdr-spiderdao", - "name": "SpiderDAO", - "symbol": "SPDR", - "rank": 2793, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hpl-happy-land", - "name": "Happy Land", - "symbol": "HPL", - "rank": 2794, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lbxc-lux-bio-exchange-coin", - "name": "LUX BIO EXCHANGE COIN", - "symbol": "LBXC", - "rank": 2795, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vix-vixco", - "name": "Vixco", - "symbol": "VIX", - "rank": 2796, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anpan-anpanswap-token", - "name": "AnpanSwap Token", - "symbol": "ANPAN", - "rank": 2797, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bux-bux-token", - "name": "BUX Token", - "symbol": "BUX", - "rank": 2798, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "led-ledgis", - "name": "Ledgis", - "symbol": "LED", - "rank": 2799, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plenty-plenty-defi", - "name": "Plenty DeFi", - "symbol": "PLENTY", - "rank": 2800, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tcg2-tcgcoin-20", - "name": "TCGCoin 2.0", - "symbol": "TCG2", - "rank": 2801, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skrt-skrt-token", - "name": "SKRT token", - "symbol": "SKRT", - "rank": 2802, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coc-coin-of-the-champions", - "name": "Coin of the champions", - "symbol": "COC", - "rank": 2803, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cifi-citizen-finance", - "name": "Citizen Finance", - "symbol": "CIFI", - "rank": 2804, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "keys-keys-token", - "name": "Keys Token", - "symbol": "KEYS", - "rank": 2805, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bit-biconomy-exchange-token", - "name": "Biconomy Exchange Token", - "symbol": "BIT", - "rank": 2806, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oklg-okletsgo", - "name": "ok.lets.go.", - "symbol": "OKLG", - "rank": 2807, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "credi-credefi", - "name": "Credefi", - "symbol": "CREDI", - "rank": 2808, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swift-swift-finance", - "name": "Swift Finance", - "symbol": "SWIFT", - "rank": 2809, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polo-nftyplay", - "name": "NftyPlay", - "symbol": "POLO", - "rank": 2810, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jntr-jointer", - "name": "Jointer", - "symbol": "JNTR", - "rank": 2811, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ryoshi-ryoshis-vision", - "name": "Ryoshis Vision", - "symbol": "RYOSHI", - "rank": 2812, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "haz-hazza", - "name": "Hazza", - "symbol": "HAZ", - "rank": 2813, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cnns-cnns", - "name": "CNNS", - "symbol": "CNNS", - "rank": 2814, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gold-golden-token", - "name": "Golden Token", - "symbol": "GOLD", - "rank": 2815, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hmt-human-protocol", - "name": "HUMAN Protocol", - "symbol": "HMT", - "rank": 2816, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cop-copiosa", - "name": "Copiosa", - "symbol": "COP", - "rank": 2817, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "infp-infinitypad", - "name": "InfinityPad", - "symbol": "INFP", - "rank": 2818, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcrn-macaronswap", - "name": "MacaronSwap", - "symbol": "MCRN", - "rank": 2819, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfi-cyberfi", - "name": "CyberFi", - "symbol": "CFI", - "rank": 2820, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnl-green-life-energy", - "name": "Green Life Energy", - "symbol": "GNL", - "rank": 2821, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cow-cashcow", - "name": "CashCow", - "symbol": "COW", - "rank": 2822, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ct-cojam", - "name": "Cojam", - "symbol": "CT", - "rank": 2823, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "yffi-yffi-finance", - "name": "yffi finance", - "symbol": "YFFI", - "rank": 2824, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "duck-unit-protocol-new", - "name": "Unit Protocol New", - "symbol": "DUCK", - "rank": 2825, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "monsta-cake-monster", - "name": "Cake Monster", - "symbol": "MONSTA", - "rank": 2826, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lol-emogi-network", - "name": "EMOGI Network", - "symbol": "LOL", - "rank": 2827, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "par-parallel", - "name": "Parallel", - "symbol": "PAR", - "rank": 2828, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uncl-uncl", - "name": "UNCL", - "symbol": "UNCL", - "rank": 2829, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "diver-divergence-protocol", - "name": "Divergence Protocol", - "symbol": "DIVER", - "rank": 2830, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mpd-metapad", - "name": "Metapad", - "symbol": "MPD", - "rank": 2831, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "artex-artex", - "name": "Artex", - "symbol": "ARTEX", - "rank": 2832, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sat-smartx", - "name": "SmartX", - "symbol": "SAT", - "rank": 2833, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tos-tonstarter", - "name": "TONStarter", - "symbol": "TOS", - "rank": 2834, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bzn-benzene", - "name": "Benzene", - "symbol": "BZN", - "rank": 2835, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brgx-bridge", - "name": "Bridge$", - "symbol": "BRG.X", - "rank": 2836, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ags-aegis", - "name": "Aegis", - "symbol": "AGS", - "rank": 2837, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yld-yield", - "name": "YIELD", - "symbol": "YLD", - "rank": 2838, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cgu-crypto-gaming-united", - "name": "Crypto Gaming United", - "symbol": "CGU", - "rank": 2839, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scar-velhallaio", - "name": "VELHALLA.io", - "symbol": "SCAR", - "rank": 2840, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sho-showcase-token", - "name": "Showcase Token", - "symbol": "SHO", - "rank": 2841, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flex-flex-coin", - "name": "FLEX Coin", - "symbol": "FLEX", - "rank": 2842, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mveda-medicalveda", - "name": "MedicalVeda", - "symbol": "MVEDA", - "rank": 2843, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "baba-alibaba-group-holding-limited", - "name": "Alibaba Group Holding Limited", - "symbol": "BABA", - "rank": 2844, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pai-parrot-usd", - "name": "Parrot USD", - "symbol": "PAI", - "rank": 2845, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iag-iagon", - "name": "Iagon", - "symbol": "IAG", - "rank": 2846, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ktt-k-tune", - "name": "K-Tune", - "symbol": "KTT", - "rank": 2847, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "desu-dexsport-protocol-native-token", - "name": "Dexsport Protocol Native Token", - "symbol": "DESU", - "rank": 2848, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vex-vexchange", - "name": "Vexchange", - "symbol": "VEX", - "rank": 2849, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fuel-jetfuel-finance", - "name": "Jetfuel Finance", - "symbol": "FUEL", - "rank": 2850, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swcat-star-wars-cat", - "name": "Star Wars Cat", - "symbol": "SWCAT", - "rank": 2851, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "avxl-avaxlauncher", - "name": "Avaxlauncher", - "symbol": "AVXL", - "rank": 2852, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tct-tacocat-token", - "name": "TacoCat Token", - "symbol": "TCT", - "rank": 2853, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnox-blocknotex", - "name": "BlockNoteX", - "symbol": "BNOX", - "rank": 2854, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "umy-karastar-umy", - "name": "KaraStar UMY", - "symbol": "UMY", - "rank": 2855, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yffii-yffiifinance", - "name": "Yffii.finance", - "symbol": "YFFII", - "rank": 2856, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "corn-corn", - "name": "CORN", - "symbol": "CORN", - "rank": 2857, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ihc-inflation-hedging-coin", - "name": "Inflation Hedging Coin", - "symbol": "IHC", - "rank": 2858, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "refork-efk", - "name": "Refork", - "symbol": "EFK", - "rank": 2859, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rel-release", - "name": "RELEASE", - "symbol": "REL", - "rank": 2860, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mfg-smart-mfg", - "name": "Smart MFG", - "symbol": "MFG", - "rank": 2861, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xblade-cryptowar-xblade", - "name": "CryptoWar xBlade", - "symbol": "XBLADE", - "rank": 2862, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mse-museo", - "name": "Museo", - "symbol": "MSE", - "rank": 2863, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pera-pera-finance", - "name": "Pera Finance", - "symbol": "PERA", - "rank": 2864, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgh-metagamehub-dao", - "name": "MetaGameHub DAO", - "symbol": "MGH", - "rank": 2865, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tch-thore-cash", - "name": "Thore Cash", - "symbol": "TCH", - "rank": 2866, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "engn-engine-token", - "name": "Engine Token", - "symbol": "ENGN", - "rank": 2867, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvc-revenue-coin", - "name": "Revenue Coin", - "symbol": "RVC", - "rank": 2868, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mmui-metamui", - "name": "MetaMUI", - "symbol": "MMUI", - "rank": 2869, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bios-0xnodes", - "name": "0x_nodes", - "symbol": "BIOS", - "rank": 2870, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcix-bitcolojix", - "name": "Bitcolojix", - "symbol": "BTCIX", - "rank": 2871, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plug-plgnet", - "name": "PL^Gnet", - "symbol": "PLUG", - "rank": 2872, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcf-mcfinance", - "name": "MCFinance", - "symbol": "MCF", - "rank": 2873, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "umi-umi-digital", - "name": "Umi Digital", - "symbol": "UMI", - "rank": 2874, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arena-arena-token", - "name": "Arena Token", - "symbol": "ARENA", - "rank": 2875, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prxy-proxy", - "name": "Proxy", - "symbol": "PRXY", - "rank": 2876, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfex-safex", - "name": "SafeX", - "symbol": "SFEX", - "rank": 2877, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edda-eddaswap", - "name": "EDDASwap", - "symbol": "EDDA", - "rank": 2878, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ufi-purefi", - "name": "PureFi", - "symbol": "UFI", - "rank": 2879, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rdt-ridotto", - "name": "Ridotto", - "symbol": "RDT", - "rank": 2880, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lovely-lovely-inu-finance", - "name": "Lovely Inu finance", - "symbol": "LOVELY", - "rank": 2881, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xtk-xtoken", - "name": "xToken", - "symbol": "XTK", - "rank": 2882, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cofi-cofix", - "name": "CoFiX", - "symbol": "COFI", - "rank": 2883, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oro-oro", - "name": "ORO", - "symbol": "ORO", - "rank": 2884, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gec-green-energy-coin-trx", - "name": "Green Energy Coin", - "symbol": "GEC", - "rank": 2885, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "far-farmland-protocol", - "name": "Farmland Protocol", - "symbol": "FAR", - "rank": 2886, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ops-octopus-protocol", - "name": "Octopus Protocol", - "symbol": "OPS", - "rank": 2887, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kawa-kawa", - "name": "Kawa", - "symbol": "KAWA", - "rank": 2888, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ethos-ethos-project", - "name": "Ethos Project", - "symbol": "ETHOS", - "rank": 2889, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ult-shardus", - "name": "Shardus", - "symbol": "ULT", - "rank": 2890, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jrit-jeritex", - "name": "JERITEX", - "symbol": "JRIT", - "rank": 2891, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgc-mergecoin", - "name": "MergeCoin", - "symbol": "MGC", - "rank": 2892, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "newb-newbfarm", - "name": "NewB.Farm", - "symbol": "NEWB", - "rank": 2893, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpt-platinum-spot-token", - "name": "Platinum Spot Token", - "symbol": "XPT", - "rank": 2894, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ght-global-human-trust", - "name": "Global Human Trust", - "symbol": "GHT", - "rank": 2895, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kaka-kaka-nft-world", - "name": "KAKA NFT World", - "symbol": "KAKA", - "rank": 2896, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yel-yelfinance", - "name": "YEL.Finance", - "symbol": "YEL", - "rank": 2897, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cyt-coinary-token", - "name": "Coinary Token", - "symbol": "CYT", - "rank": 2898, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lemo-lemochain", - "name": "LemoChain", - "symbol": "LEMO", - "rank": 2899, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "piggy-piggy", - "name": "Piggy", - "symbol": "PIGGY", - "rank": 2900, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ntx-nunet", - "name": "NuNet", - "symbol": "NTX", - "rank": 2901, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "payn-paynet-coin", - "name": "PayNet Coin", - "symbol": "PAYN", - "rank": 2902, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ansr-answerly", - "name": "Answerly", - "symbol": "ANSR", - "rank": 2903, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "invi-invi-token", - "name": "INVI Token", - "symbol": "INVI", - "rank": 2904, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "omax-omax-token", - "name": "Omax Token", - "symbol": "OMAX", - "rank": 2905, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "props-props", - "name": "PROPS", - "symbol": "PROPS", - "rank": 2906, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xbn-elastic-bnb", - "name": "Elastic BNB", - "symbol": "XBN", - "rank": 2907, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "birb-birb", - "name": "Birb", - "symbol": "BIRB", - "rank": 2908, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "2lc-2local", - "name": "2local", - "symbol": "2LC", - "rank": 2909, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yct-youclout", - "name": "Youclout", - "symbol": "YCT", - "rank": 2910, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "txbit-txbit-token", - "name": "Txbit Token", - "symbol": "TXBIT", - "rank": 2911, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ibnb-ibnb", - "name": "iBNB", - "symbol": "IBNB", - "rank": 2912, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kainet-kainet", - "name": "KAINET", - "symbol": "KAINET", - "rank": 2913, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ai-flourishing-ai", - "name": "Flourishing AI", - "symbol": "AI", - "rank": 2914, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "papa-papa-dao", - "name": "PAPA DAO", - "symbol": "PAPA", - "rank": 2915, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trava-trava-finance", - "name": "Trava Finance", - "symbol": "TRAVA", - "rank": 2916, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apn-apron", - "name": "Apron", - "symbol": "APN", - "rank": 2917, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmc-cryptomotorcycle", - "name": "CryptoMotorcycle", - "symbol": "CMC", - "rank": 2918, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cook-cook", - "name": "Cook", - "symbol": "COOK", - "rank": 2919, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coris-corgiswap", - "name": "CorgiSwap", - "symbol": "CORIS", - "rank": 2920, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "buidl-dfohub", - "name": "dfohub", - "symbol": "BUIDL", - "rank": 2921, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swfl-swapfolio", - "name": "Swapfolio", - "symbol": "SWFL", - "rank": 2922, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eosc-eosforce", - "name": "EosForce", - "symbol": "EOSC", - "rank": 2923, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "qbt-qubit", - "name": "Qubit", - "symbol": "QBT", - "rank": 2924, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "des-despace-protocol", - "name": "DeSpace Protocol", - "symbol": "DES", - "rank": 2925, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfx-dfx-finance", - "name": "DFX Finance", - "symbol": "DFX", - "rank": 2926, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "umad-madworld", - "name": "MADworld", - "symbol": "UMAD", - "rank": 2927, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qtf-quantfury-token", - "name": "Quantfury Token", - "symbol": "QTF", - "rank": 2928, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pots-moonpot", - "name": "Moonpot", - "symbol": "POTS", - "rank": 2929, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pfy-portify", - "name": "Portify", - "symbol": "PFY", - "rank": 2930, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ridge-ridge", - "name": "Ridge", - "symbol": "RIDGE", - "rank": 2931, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zaif-zaif-token", - "name": "Zaif Token", - "symbol": "ZAIF", - "rank": 2932, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "atolo-rizon", - "name": "Rizon", - "symbol": "ATOLO", - "rank": 2933, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ichi-ichi", - "name": "ICHI", - "symbol": "ICHI", - "rank": 2934, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dor-doragonland", - "name": "DoragonLand", - "symbol": "DOR", - "rank": 2935, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arcx-arc-governance", - "name": "ARC Governance", - "symbol": "ARCX", - "rank": 2936, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ram-ramifi-protocol", - "name": "Ramifi Protocol", - "symbol": "RAM", - "rank": 2937, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bzzone-bzzone", - "name": "Bzzone", - "symbol": "BZZONE", - "rank": 2938, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prp-pharma-pay-coin", - "name": "Pharma Pay Coin", - "symbol": "PRP", - "rank": 2939, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mm-million", - "name": "Million", - "symbol": "MM", - "rank": 2940, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lar-linkart", - "name": "LinkArt", - "symbol": "LAR", - "rank": 2941, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jet-jet", - "name": "JET", - "symbol": "JET", - "rank": 2942, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bloc-blockcloud", - "name": "Blockcloud", - "symbol": "BLOC", - "rank": 2943, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fish-polycat-finance", - "name": "Polycat Finance", - "symbol": "FISH", - "rank": 2944, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcb-bitcoin-bep2", - "name": "Bitcoin BEP2", - "symbol": "BTCB", - "rank": 2945, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sup8eme-sup8eme", - "name": "SUP8EME", - "symbol": "SUP8EME", - "rank": 2946, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "honeyd-honey-deluxe", - "name": "Honey Deluxe", - "symbol": "HONEYD", - "rank": 2947, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "docu-docusign", - "name": "DocuSign", - "symbol": "DOCU", - "rank": 2948, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cxpad-coinxpad", - "name": "CoinxPad", - "symbol": "CXPAD", - "rank": 2949, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ston-ston", - "name": "Ston", - "symbol": "STON", - "rank": 2950, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btca-bitcoin-asia8218", - "name": "Bitcoin Asia", - "symbol": "BTCA", - "rank": 2951, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wkd-wakanda-inu", - "name": "Wakanda Inu", - "symbol": "WKD", - "rank": 2952, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mntg-monetas", - "name": "Monetas", - "symbol": "MNTG", - "rank": 2953, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scop-scop", - "name": "Scop", - "symbol": "SCOP", - "rank": 2954, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "daa-double-ace", - "name": "Double Ace", - "symbol": "DAA", - "rank": 2955, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pid-pidao", - "name": "PIDAO", - "symbol": "PID", - "rank": 2956, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sap-swapall", - "name": "SwapAll", - "symbol": "SAP", - "rank": 2957, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mlnk-malinka", - "name": "Malinka", - "symbol": "MLNK", - "rank": 2958, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bas-block-ape-scissors", - "name": "Block Ape Scissors", - "symbol": "BAS", - "rank": 2959, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wgmi-wgmi", - "name": "WGMI", - "symbol": "WGMI", - "rank": 2960, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fai-fairum", - "name": "Fairum", - "symbol": "FAI", - "rank": 2961, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hedge-hedge-finance", - "name": "Hedge Finance", - "symbol": "HEDGE", - "rank": 2962, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pls-ipulse", - "name": "Ipulse", - "symbol": "PLS", - "rank": 2963, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pn-probably-nothing", - "name": "Probably Nothing", - "symbol": "PN", - "rank": 2964, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daisy-daisy-protocol", - "name": "Daisy Protocol", - "symbol": "DAISY", - "rank": 2965, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xuez-xuez-coin", - "name": "Xuez Coin", - "symbol": "XUEZ", - "rank": 2966, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hzd-horizon-dollar", - "name": "Horizon Dollar", - "symbol": "HZD", - "rank": 2967, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apt-apricot", - "name": "Apricot", - "symbol": "APT", - "rank": 2968, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "armor-armor-finance", - "name": "Armor Finance", - "symbol": "ARMOR", - "rank": 2969, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evn-evolution-finance", - "name": "Evolution Finance", - "symbol": "EVN", - "rank": 2970, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "usdp-usdp-stablecoin", - "name": "USDP Stablecoin", - "symbol": "USDP", - "rank": 2971, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kobe-shabu-shabu", - "name": "Shabu Shabu", - "symbol": "Kobe", - "rank": 2972, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnft-communifty", - "name": "Communifty", - "symbol": "CNFT", - "rank": 2973, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfl-defil", - "name": "DeFIL", - "symbol": "DFL", - "rank": 2974, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "au-autocrypto", - "name": "AutoCrypto", - "symbol": "AU", - "rank": 2975, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fluf-fluffy-coin", - "name": "Fluffy Coin", - "symbol": "FLUF", - "rank": 2976, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fibo-fibo-token", - "name": "Fibo Token", - "symbol": "FIBO", - "rank": 2977, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dop-dopple-finance", - "name": "Dopple Finance", - "symbol": "DOP", - "rank": 2978, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xrt-robonomics-network", - "name": "Robonomics Network", - "symbol": "XRT", - "rank": 2979, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frel-freela", - "name": "Freela", - "symbol": "FREL", - "rank": 2980, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xil-projectx", - "name": "ProjectX", - "symbol": "XIL", - "rank": 2981, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msd-moneydefiswap", - "name": "MoneydefiSwap", - "symbol": "MSD", - "rank": 2982, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dappt-dapp-token", - "name": "Dapp Token", - "symbol": "DAPPT", - "rank": 2983, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prch-power-cash", - "name": "Power Cash", - "symbol": "PRCH", - "rank": 2984, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prcy-privacy-coin", - "name": "Privacy coin", - "symbol": "PRCY", - "rank": 2985, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "b20-b20", - "name": "B20", - "symbol": "B20", - "rank": 2986, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wpe-opes-finance", - "name": "OPES Finance", - "symbol": "WPE", - "rank": 2987, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stn-stone-token", - "name": "Stone Token", - "symbol": "STN", - "rank": 2988, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hokk-hokkaidu-inu", - "name": "Hokkaidu Inu", - "symbol": "HOKK", - "rank": 2989, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ari10-ari10", - "name": "Ari10", - "symbol": "ARI10", - "rank": 2990, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "artr-artery-network", - "name": "Artery Network", - "symbol": "ARTR", - "rank": 2991, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cdt-checkdot", - "name": "CheckDot", - "symbol": "CDT", - "rank": 2992, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "liq-liquidus", - "name": "Liquidus", - "symbol": "LIQ", - "rank": 2993, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bed-bit-ecological-digital", - "name": "Bit Ecological Digital", - "symbol": "BED", - "rank": 2994, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "key-momo-key", - "name": "MoMo Key", - "symbol": "KEY", - "rank": 2995, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "duke-duke-token", - "name": "Duke Token", - "symbol": "DUKE", - "rank": 2996, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lunr-lunr-token", - "name": "Lunr Token", - "symbol": "LUNR", - "rank": 2997, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zoo-zoo-labs", - "name": "Zoo Labs", - "symbol": "ZOO", - "rank": 2998, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dft-dfuture", - "name": "dfuture", - "symbol": "DFT", - "rank": 2999, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cicc-caica-coin", - "name": "CAICA Coin", - "symbol": "CICC", - "rank": 3000, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tkx-token-tkx", - "name": "Token TKX", - "symbol": "TKX", - "rank": 3001, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vention-vention", - "name": "Vention", - "symbol": "VENTION", - "rank": 3002, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tac-taichi", - "name": "TaiChi", - "symbol": "TAC", - "rank": 3003, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cdz-cdzexchange", - "name": "CDzExchange", - "symbol": "CDZ", - "rank": 3004, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fscc-fisco", - "name": "Fisco", - "symbol": "FSCC", - "rank": 3005, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdna-extradna", - "name": "extraDNA", - "symbol": "XDNA", - "rank": 3006, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xnc-xenios", - "name": "XENIOS", - "symbol": "XNC", - "rank": 3007, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hmmm-hmmm", - "name": "Hmmm", - "symbol": "HMMM", - "rank": 3008, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnfi-connect-financial", - "name": "Connect Financial", - "symbol": "CNFI", - "rank": 3009, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zoo-zookeeper", - "name": "ZooKeeper", - "symbol": "ZOO", - "rank": 3010, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "srx-storx", - "name": "StorX", - "symbol": "SRX", - "rank": 3011, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gbd-great-bounty-dealer", - "name": "Great Bounty Dealer", - "symbol": "GBD", - "rank": 3012, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bas-basis-share", - "name": "Basis Share", - "symbol": "BAS", - "rank": 3013, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slnv2-slnv2", - "name": "SLNV2", - "symbol": "SLNV2", - "rank": 3014, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "build-build-finance", - "name": "BUILD Finance", - "symbol": "BUILD", - "rank": 3015, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eqx-eqifi-token", - "name": "EQIFI Token", - "symbol": "EQX", - "rank": 3016, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gspi-shoppingio-governance", - "name": "Shopping.io Governance", - "symbol": "GSPI", - "rank": 3017, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xp-polkafantasy", - "name": "PolkaFantasy", - "symbol": "XP", - "rank": 3018, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pact-pactswap", - "name": "PactSwap", - "symbol": "PACT", - "rank": 3019, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mai-mindsync", - "name": "Mindsync", - "symbol": "MAI", - "rank": 3020, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kxa-kryxivia-game", - "name": "Kryxivia Game", - "symbol": "KXA", - "rank": 3021, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pulsedoge-pulsedoge", - "name": "PulseDoge", - "symbol": "PULSEDOGE", - "rank": 3022, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bags-basis-gold-share", - "name": "Basis Gold Share", - "symbol": "BAGS", - "rank": 3023, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zmt-zipmex-token", - "name": "Zipmex Token", - "symbol": "ZMT", - "rank": 3024, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbp-cashbackpro", - "name": "CashBackPro", - "symbol": "CBP", - "rank": 3025, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "exmr-fdn", - "name": "EXMR", - "symbol": "FDN", - "rank": 3026, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bkr-balkari-token", - "name": "Balkari Token", - "symbol": "BKR", - "rank": 3027, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "candy-tripcandy", - "name": "TripCandy", - "symbol": "CANDY", - "rank": 3028, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plt-poollottofinance", - "name": "Poollotto.finance", - "symbol": "PLT", - "rank": 3029, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "onc-one-cash", - "name": "One Cash", - "symbol": "ONC", - "rank": 3030, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kst-ksm-starter", - "name": "KSM Starter", - "symbol": "KST", - "rank": 3031, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bpl-betsplayer", - "name": "BETSPLAYER", - "symbol": "BPL", - "rank": 3032, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ave-avaware", - "name": "Avaware", - "symbol": "AVE", - "rank": 3033, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nitro-nitro-league", - "name": "Nitro League", - "symbol": "NITRO", - "rank": 3034, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbf-moonbear-finance", - "name": "MoonBear Finance", - "symbol": "MBF", - "rank": 3035, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hhnft-hodler-heroes-nft", - "name": "Hodler Heroes NFT", - "symbol": "HHNFT", - "rank": 3036, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yefi-yefi", - "name": "YeFi", - "symbol": "YEFI", - "rank": 3037, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfix-yfixfinance", - "name": "YFIX.finance", - "symbol": "YFIX", - "rank": 3038, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sld-shieldex", - "name": "ShieldEX", - "symbol": "SLD", - "rank": 3039, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pusd-pusd", - "name": "PUSD", - "symbol": "PUSD", - "rank": 3040, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jem-jem", - "name": "Jem", - "symbol": "JEM", - "rank": 3041, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evny-evny-token", - "name": "EVNY Token", - "symbol": "EVNY", - "rank": 3042, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dhx-datahighway", - "name": "DataHighway", - "symbol": "DHX", - "rank": 3043, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vd-vindax-coin", - "name": "VinDax Coin", - "symbol": "VD", - "rank": 3044, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "para-paralink-network", - "name": "Paralink Network", - "symbol": "PARA", - "rank": 3045, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kdoge-koreadoge", - "name": "Koreadoge", - "symbol": "KDOGE", - "rank": 3046, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lemd-lemond", - "name": "Lemond", - "symbol": "LEMD", - "rank": 3047, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thales-thales", - "name": "Thales", - "symbol": "THALES", - "rank": 3048, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "refi-realfinance-network", - "name": "Realfinance Network", - "symbol": "REFI", - "rank": 3049, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dance-dance-coin", - "name": "DANCE coin", - "symbol": "DANCE", - "rank": 3050, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zlot-zlot", - "name": "zLOT", - "symbol": "ZLOT", - "rank": 3051, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsla-tesla", - "name": "Tesla", - "symbol": "TSLA", - "rank": 3052, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dnft-darenft", - "name": "DareNFT", - "symbol": "DNFT", - "rank": 3053, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsd-basis-dollar", - "name": "Basis Dollar", - "symbol": "BSD", - "rank": 3054, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lott-lottrade", - "name": "LOT.TRADE", - "symbol": "LOTT", - "rank": 3055, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pipi-pippi-finance", - "name": "Pippi Finance", - "symbol": "PIPI", - "rank": 3056, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pkn-poken", - "name": "Poken", - "symbol": "PKN", - "rank": 3057, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tkg-takamaka-green-coin", - "name": "Takamaka Green Coin", - "symbol": "TKG", - "rank": 3058, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "itg-itrust-governance-token", - "name": "iTrust Governance Token", - "symbol": "ITG", - "rank": 3059, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "esbc-e-sport-betting-coin", - "name": "e-Sport Betting Coin", - "symbol": "ESBC", - "rank": 3060, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aok-aok", - "name": "AOK", - "symbol": "AOK", - "rank": 3061, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "drv-dragon-verse", - "name": "Dragon Verse", - "symbol": "DRV", - "rank": 3062, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgt-megatech", - "name": "Megatech", - "symbol": "MGT", - "rank": 3063, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axial-axial-token", - "name": "Axial Token", - "symbol": "AXIAL", - "rank": 3064, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "real-real-realm", - "name": "Real Realm", - "symbol": "REAL", - "rank": 3065, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frog-frogswap", - "name": "FrogSwap", - "symbol": "FROG", - "rank": 3066, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lava-lavaswap", - "name": "LavaSwap", - "symbol": "LAVA", - "rank": 3067, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aloha-aloha", - "name": "Aloha", - "symbol": "ALOHA", - "rank": 3068, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bkbt-beekan", - "name": "BeeKan", - "symbol": "BKBT", - "rank": 3069, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vbn-vibranium", - "name": "Vibranium", - "symbol": "VBN", - "rank": 3070, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evr-everus", - "name": "Everus", - "symbol": "EVR", - "rank": 3071, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "strx-strikex", - "name": "StrikeX", - "symbol": "STRX", - "rank": 3072, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poly-farmpoly", - "name": "FarmPoly", - "symbol": "POLY", - "rank": 3073, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vow-vow", - "name": "Vow", - "symbol": "VOW", - "rank": 3074, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gfx-gamyfi-token", - "name": "GamyFi Token", - "symbol": "GFX", - "rank": 3075, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vetter-vetter-token", - "name": "Vetter Token", - "symbol": "VETTER", - "rank": 3076, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cn50-ftse-china-a50-token", - "name": "FTSE China A50 Token", - "symbol": "CN50", - "rank": 3077, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ufo-unknown-fair-object", - "name": "Unknown Fair Object", - "symbol": "UFO", - "rank": 3078, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mscp-moonscape", - "name": "Moonscape", - "symbol": "MSCP", - "rank": 3079, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alix-alinx", - "name": "AlinX", - "symbol": "ALIX", - "rank": 3080, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pie-defipie", - "name": "DeFiPie", - "symbol": "PIE", - "rank": 3081, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ons-one-share", - "name": "One Share", - "symbol": "ONS", - "rank": 3082, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unic-unicly", - "name": "Unicly", - "symbol": "UNIC", - "rank": 3083, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsy-bestay", - "name": "Bestay", - "symbol": "BSY", - "rank": 3084, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bank-bankless-dao", - "name": "Bankless DAO", - "symbol": "BANK", - "rank": 3085, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bgg-bgogo-token", - "name": "Bgogo Token", - "symbol": "BGG", - "rank": 3086, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elixir-starchi", - "name": "Starchi", - "symbol": "ELIXIR", - "rank": 3087, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "c3-charli3", - "name": "Charli3", - "symbol": "C3", - "rank": 3088, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sale-dxsalenetwork", - "name": "Dxsale.network", - "symbol": "SALE", - "rank": 3089, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pkmon-polkamonster", - "name": "PolkaMonster", - "symbol": "PKMON", - "rank": 3090, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shake-milkshake-coin", - "name": "MilkShake Coin", - "symbol": "SHAKE", - "rank": 3091, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "loc-locktrip", - "name": "LockTrip", - "symbol": "LOC", - "rank": 3092, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zada-zada", - "name": "Zada", - "symbol": "ZADA", - "rank": 3093, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dkt-duelist-king", - "name": "Duelist King", - "symbol": "DKT", - "rank": 3094, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "larix-larix", - "name": "Larix", - "symbol": "LARIX", - "rank": 3095, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amze-amaze-world", - "name": "AMAZE WORLD", - "symbol": "AMZE", - "rank": 3096, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tmds-tremendous-coin", - "name": "Tremendous Coin", - "symbol": "TMDS", - "rank": 3097, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "node-dappnode", - "name": "DAppNode", - "symbol": "NODE", - "rank": 3098, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "soga-soga-project", - "name": "SOGA Project", - "symbol": "SOGA", - "rank": 3099, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eshk-eshark-token", - "name": "eShark Token", - "symbol": "ESHK", - "rank": 3100, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecc-etherconnect", - "name": "Etherconnect", - "symbol": "ECC", - "rank": 3101, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daxe-daxe-token", - "name": "DAXE Token", - "symbol": "DAXE", - "rank": 3102, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fort-fortress-dao", - "name": "Fortress DAO", - "symbol": "FORT", - "rank": 3103, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gfi-gamefi-token", - "name": "GameFi Token", - "symbol": "GFI", - "rank": 3104, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ntr-nether", - "name": "Nether", - "symbol": "NTR", - "rank": 3105, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitorb-bitorbit", - "name": "BitOrbit", - "symbol": "BITORB", - "rank": 3106, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trcl-treecle", - "name": "Treecle", - "symbol": "TRCL", - "rank": 3107, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "star-filestar", - "name": "FileStar", - "symbol": "STAR", - "rank": 3108, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mot-mobius-finance", - "name": "Mobius Finance", - "symbol": "MOT", - "rank": 3109, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eco-ecofi", - "name": "EcoFi", - "symbol": "ECO", - "rank": 3110, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "enu-enumivo", - "name": "Enumivo", - "symbol": "ENU", - "rank": 3111, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goge-dogegayson", - "name": "DogeGaySon", - "symbol": "GOGE", - "rank": 3112, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prot-prostarter", - "name": "ProStarter", - "symbol": "PROT", - "rank": 3113, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bscgold-bscgold", - "name": "BSCGold", - "symbol": "BSCGOLD", - "rank": 3114, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "2030floki-2030-floki", - "name": "2030 Floki", - "symbol": "2030FLOKI", - "rank": 3115, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gdr-golden-roots", - "name": "Golden Roots", - "symbol": "GDR", - "rank": 3116, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fint-fintropy", - "name": "Fintropy", - "symbol": "FINT", - "rank": 3117, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clist-chainlist", - "name": "Chainlist", - "symbol": "CLIST", - "rank": 3118, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mct-microtuber", - "name": "MicroTuber", - "symbol": "MCT", - "rank": 3119, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mofi-mobifi", - "name": "MobiFi", - "symbol": "MOFI", - "rank": 3120, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kun-chemix-ecology-governance-token", - "name": "Chemix Ecology Governance Token", - "symbol": "KUN", - "rank": 3121, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ntx-nitroex", - "name": "NitroEX", - "symbol": "NTX", - "rank": 3122, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xlon-excelon", - "name": "Excelon", - "symbol": "XLON", - "rank": 3123, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "elef-elef-world", - "name": "ELEF World", - "symbol": "ELEF", - "rank": 3124, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dtube-dtube-coin", - "name": "Dtube Coin", - "symbol": "DTUBE", - "rank": 3125, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lcg-lcg", - "name": "LCG", - "symbol": "LCG", - "rank": 3126, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dville-dogeville", - "name": "DogeVille", - "symbol": "DVILLE", - "rank": 3127, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ep-ep", - "name": "E$P", - "symbol": "E$P", - "rank": 3128, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icw-icryptoworld", - "name": "iCryptoWorld", - "symbol": "ICW", - "rank": 3129, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmtr-dimitra", - "name": "Dimitra", - "symbol": "DMTR", - "rank": 3130, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rnb-rentible", - "name": "Rentible", - "symbol": "RNB", - "rank": 3131, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zort-zort", - "name": "Zort", - "symbol": "ZORT", - "rank": 3132, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sak3-sak3", - "name": "SAK3", - "symbol": "SAK3", - "rank": 3133, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evai-evaiio", - "name": "Evai.io", - "symbol": "EVAI", - "rank": 3134, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ckg-crystal-kingdoms", - "name": "Crystal Kingdoms", - "symbol": "CKG", - "rank": 3135, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "l99-lucky-unicorn-token", - "name": "Lucky Unicorn Token", - "symbol": "L99", - "rank": 3136, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ode-odem", - "name": "ODEM", - "symbol": "ODE", - "rank": 3137, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tac-thaicoin", - "name": "Thaicoin", - "symbol": "TAC", - "rank": 3138, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "seed-seed-token", - "name": "Seed Token", - "symbol": "SEED", - "rank": 3139, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mem-memecoin", - "name": "Memecoin", - "symbol": "MEM", - "rank": 3140, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coge-cogecoin", - "name": "Cogecoin", - "symbol": "COGE", - "rank": 3141, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swapp-swapp-protocol", - "name": "SWAPP Protocol", - "symbol": "SWAPP", - "rank": 3142, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "baxs-boxaxis", - "name": "BoxAxis", - "symbol": "BAXS", - "rank": 3143, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "znc-zioncoin", - "name": "ZionCoin", - "symbol": "ZNC", - "rank": 3144, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aur-aureo", - "name": "AUREO", - "symbol": "AUR", - "rank": 3145, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ufloki-universal-floki-coin", - "name": "Universal Floki Coin", - "symbol": "UFLOKI", - "rank": 3146, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdu-mdukey", - "name": "MDUKEY", - "symbol": "MDU", - "rank": 3147, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bpro-bprotocol", - "name": "B.Protocol", - "symbol": "BPRO", - "rank": 3148, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "strip-stripcoin", - "name": "StripCoin", - "symbol": "STRIP", - "rank": 3149, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lion-lion-token", - "name": "Lion Token", - "symbol": "LION", - "rank": 3150, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mars4-mars4", - "name": "MARS4", - "symbol": "MARS4", - "rank": 3151, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yldy-yieldly", - "name": "Yieldly", - "symbol": "YLDY", - "rank": 3152, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crt-certurium", - "name": "Certurium", - "symbol": "CRT", - "rank": 3153, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "elongate-elongate", - "name": "ElonGate", - "symbol": "ELONGATE", - "rank": 3154, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dog-dogeswap-token-heco", - "name": "Dogeswap Token (HECO)", - "symbol": "DOG", - "rank": 3155, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "verse-shibaverse", - "name": "Shibaverse", - "symbol": "VERSE", - "rank": 3156, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drace-deathroad", - "name": "DeathRoad", - "symbol": "DRACE", - "rank": 3157, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfxq-cfx-quantum", - "name": "CFX Quantum", - "symbol": "CFXQ", - "rank": 3158, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thoreum-thoreum", - "name": "Thoreum", - "symbol": "THOREUM", - "rank": 3159, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jed-jedstar", - "name": "JEDSTAR", - "symbol": "JED", - "rank": 3160, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kva-kevacoin", - "name": "Kevacoin", - "symbol": "KVA", - "rank": 3161, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cvd19-cvd19", - "name": "CVD19", - "symbol": "CVD19", - "rank": 3162, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmex-game-coin", - "name": "Game Coin", - "symbol": "GMEX", - "rank": 3163, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "usf-unslashed-finance", - "name": "Unslashed Finance", - "symbol": "USF", - "rank": 3164, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wcs-weecoins", - "name": "Weecoins", - "symbol": "WCS", - "rank": 3165, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bta-bitcoin-asset", - "name": "Bitcoin Asset", - "symbol": "BTA", - "rank": 3166, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sconex-sconex", - "name": "SCOneX", - "symbol": "SCONEX", - "rank": 3167, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bscwin-bscwin-bulls", - "name": "BSCWIN Bulls", - "symbol": "BSCWIN", - "rank": 3168, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uniq-uniqly", - "name": "Uniqly", - "symbol": "UNIQ", - "rank": 3169, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptd-pilot", - "name": "Pilot", - "symbol": "PTD", - "rank": 3170, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "admc-adamant-coin", - "name": "Adamant Coin", - "symbol": "ADMC", - "rank": 3171, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jd-jdcom", - "name": "Jd.Com", - "symbol": "JD", - "rank": 3172, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ulu-universal-liquidity-union", - "name": "Universal Liquidity Union", - "symbol": "ULU", - "rank": 3173, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fvt-finance-vote", - "name": "Finance Vote", - "symbol": "FVT", - "rank": 3174, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "auscm-auric-network", - "name": "Auric Network", - "symbol": "AUSCM", - "rank": 3175, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mooned-moonedge", - "name": "MoonEdge", - "symbol": "MOONED", - "rank": 3176, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pokerfi-pokerfi", - "name": "PokerFi", - "symbol": "POKERFI", - "rank": 3177, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kbox-the-killbox-game", - "name": "The Killbox Game", - "symbol": "KBOX", - "rank": 3178, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dyt-the-dynasty", - "name": "The Dynasty", - "symbol": "DYT", - "rank": 3179, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oly-oly-sport", - "name": "Oly Sport", - "symbol": "OLY", - "rank": 3180, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safemars-safemars", - "name": "Safemars", - "symbol": "SAFEMARS", - "rank": 3181, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "her-heroverse", - "name": "HeroVerse", - "symbol": "HER", - "rank": 3182, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cap-cap", - "name": "Cap", - "symbol": "CAP", - "rank": 3183, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zefi-zcore-finance", - "name": "ZCore Finance", - "symbol": "ZEFI", - "rank": 3184, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gbx-gbrick", - "name": "Gbrick", - "symbol": "GBX", - "rank": 3185, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kogecoin-kogecoin", - "name": "KogeCoin", - "symbol": "KOGECOIN", - "rank": 3186, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "too-too-token", - "name": "TOO Token", - "symbol": "TOO", - "rank": 3187, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drug-dopewarz", - "name": "DopeWarz", - "symbol": "DRUG", - "rank": 3188, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cron-cronos-group-inc", - "name": "Cronos Group Inc.", - "symbol": "CRON", - "rank": 3189, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sym-symverse", - "name": "SymVerse", - "symbol": "SYM", - "rank": 3190, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stop-satopay", - "name": "SatoPay", - "symbol": "STOP", - "rank": 3191, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcoin-mcoin", - "name": "mCoin", - "symbol": "MCOIN", - "rank": 3192, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "min-mindol", - "name": "MINDOL", - "symbol": "MIN", - "rank": 3193, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metadoge-metadoge", - "name": "MetaDoge", - "symbol": "METADOGE", - "rank": 3194, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nsbt-neutrino-system-base-token", - "name": "Neutrino System Base Token", - "symbol": "NSBT", - "rank": 3195, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flvr-flavors-bsc", - "name": "Flavors BSC", - "symbol": "FLVR", - "rank": 3196, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yay-yay-games", - "name": "YAY Games", - "symbol": "YAY", - "rank": 3197, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smbswap-simbcoin-swap", - "name": "SimbCoin Swap", - "symbol": "SMBSWAP", - "rank": 3198, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnstrs-block-monsters", - "name": "Block Monsters", - "symbol": "MNSTRS", - "rank": 3199, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pfe-pfizer-inc", - "name": "Pfizer Inc", - "symbol": "PFE", - "rank": 3200, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pink-dot-finance", - "name": "Dot Finance", - "symbol": "PINK", - "rank": 3201, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pad-smartpad", - "name": "SmartPad", - "symbol": "PAD", - "rank": 3202, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ken-keysians-network", - "name": "Keysians Network", - "symbol": "KEN", - "rank": 3203, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mvc-multiverse-capital", - "name": "Multiverse Capital", - "symbol": "MVC", - "rank": 3204, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbdo-bdollar-share", - "name": "bDollar Share", - "symbol": "SBDO", - "rank": 3205, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "euler-euler-tools", - "name": "Euler Tools", - "symbol": "EULER", - "rank": 3206, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "upr-upfire", - "name": "Upfire", - "symbol": "UPR", - "rank": 3207, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cryptogram-cryptogram", - "name": "Cryptogram", - "symbol": "CRYPTOGRAM", - "rank": 3208, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bau-bitau", - "name": "BitAU", - "symbol": "BAU", - "rank": 3209, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mogx-mogu", - "name": "Mogu", - "symbol": "MOGX", - "rank": 3210, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fortune-fortune", - "name": "Fortune", - "symbol": "FORTUNE", - "rank": 3211, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tip-sugarbounce", - "name": "SugarBounce", - "symbol": "TIP", - "rank": 3212, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pom-pomeranian", - "name": "Pomeranian", - "symbol": "POM", - "rank": 3213, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zero-zero-tech", - "name": "Zero Tech", - "symbol": "ZERO", - "rank": 3214, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ciotx-crosschain-iotx", - "name": "Crosschain IOTX", - "symbol": "CIOTX", - "rank": 3215, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gme-gamestop-finance", - "name": "GameStop Finance", - "symbol": "GME", - "rank": 3216, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "guard-guardian-token", - "name": "Guardian Token", - "symbol": "GUARD", - "rank": 3217, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roc-roxe", - "name": "Roxe", - "symbol": "ROC", - "rank": 3218, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sets-sensitrust-token", - "name": "Sensitrust Token", - "symbol": "SETS", - "rank": 3219, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ntb-tokenasset", - "name": "TokenAsset", - "symbol": "NTB", - "rank": 3220, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lev-lever-network", - "name": "Lever Network", - "symbol": "LEV", - "rank": 3221, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpress-cryptoxpress", - "name": "CryptoXpress", - "symbol": "XPRESS", - "rank": 3222, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mor-morcilla-war", - "name": "Morcilla War", - "symbol": "MOR", - "rank": 3223, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pop-pop-chest-token", - "name": "Pop Chest Token", - "symbol": "POP", - "rank": 3224, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bala-shambala", - "name": "Shambala", - "symbol": "BALA", - "rank": 3225, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "space-space-token", - "name": "Space Token", - "symbol": "SPACE", - "rank": 3226, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pli-plugin", - "name": "Plugin", - "symbol": "PLI", - "rank": 3227, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dcau-dragon-crypto-aurum", - "name": "Dragon Crypto Aurum", - "symbol": "DCAU", - "rank": 3228, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qub-qubism", - "name": "Qubism", - "symbol": "QUB", - "rank": 3229, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "up-unifi-protocol", - "name": "UniFi Protocol", - "symbol": "UP", - "rank": 3230, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crown-kingpad", - "name": "KingPad", - "symbol": "CROWN", - "rank": 3231, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wcx-wecoown-rewards", - "name": "WeCoOwn Rewards", - "symbol": "WCX", - "rank": 3232, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "soku-sokuswap", - "name": "SokuSwap", - "symbol": "SOKU", - "rank": 3233, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mono-the-monopolist", - "name": "The Monopolist", - "symbol": "MONO", - "rank": 3234, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zoe-zoe-cash", - "name": "Zoe Cash", - "symbol": "ZOE", - "rank": 3235, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "entc-enterbutton", - "name": "EnterButton", - "symbol": "ENTC", - "rank": 3236, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gym-gym-token", - "name": "GYM Token", - "symbol": "GYM", - "rank": 3237, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xeq-equilibria", - "name": "Equilibria", - "symbol": "XEQ", - "rank": 3238, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cars-cryptocars-world", - "name": "CryptoCars World", - "symbol": "CARS", - "rank": 3239, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gut-guitarswap", - "name": "GuitarSwap", - "symbol": "GUT", - "rank": 3240, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cat-capital-aggregator-token", - "name": "Capital Aggregator Token", - "symbol": "CAT", - "rank": 3241, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egame-every-game", - "name": "Every Game", - "symbol": "EGAME", - "rank": 3242, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anji-anji", - "name": "Anji", - "symbol": "ANJI", - "rank": 3243, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdog-small-doge", - "name": "Small Doge", - "symbol": "SDOG", - "rank": 3244, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "merkle-merkle-network", - "name": "Merkle Network", - "symbol": "MERKLE", - "rank": 3245, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "paper-dope-wars-paper", - "name": "Dope Wars Paper", - "symbol": "PAPER", - "rank": 3246, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smrtr-smartercoin", - "name": "SmarterCoin", - "symbol": "SMRTR", - "rank": 3247, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apefund-apefund", - "name": "ApeFund", - "symbol": "APEFUND", - "rank": 3248, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stf-structure-finance", - "name": "Structure Finance", - "symbol": "STF", - "rank": 3249, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "duck-dlp-duck-token", - "name": "DLP Duck Token", - "symbol": "DUCK", - "rank": 3250, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fbx-forthbox", - "name": "ForthBox", - "symbol": "FBX", - "rank": 3251, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polypug-polypug", - "name": "PolyPug", - "symbol": "POLYPUG", - "rank": 3252, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccf-cross-chain-farming", - "name": "Cross Chain Farming", - "symbol": "CCF", - "rank": 3253, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "syl-xsl-labs", - "name": "XSL Labs", - "symbol": "SYL", - "rank": 3254, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gain-gainpool", - "name": "GainPool", - "symbol": "GAIN", - "rank": 3255, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ycc-yuan-chain-coin", - "name": "Yuan Chain Coin", - "symbol": "YCC", - "rank": 3256, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "codi-coin-discovery", - "name": "Coin Discovery", - "symbol": "CODI", - "rank": 3257, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coral-coral-swap", - "name": "Coral Swap", - "symbol": "CORAL", - "rank": 3258, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "minikishu-minikishu", - "name": "MINIKISHU", - "symbol": "MINIKISHU", - "rank": 3259, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bns-bns-token", - "name": "BNS Token", - "symbol": "BNS", - "rank": 3260, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gooreo-gooreo", - "name": "Gooreo", - "symbol": "GOOREO", - "rank": 3261, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skc-skeincurrency", - "name": "SkeinCurrency", - "symbol": "SKC", - "rank": 3262, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "g9-goldendiamond9", - "name": "GoldenDiamond9", - "symbol": "G9", - "rank": 3263, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "honor-superplayer-world", - "name": "SuperPlayer World", - "symbol": "HONOR", - "rank": 3264, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phc-phuket-holiday-coin", - "name": "Phuket Holiday Coin", - "symbol": "PHC", - "rank": 3265, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptm-potentiam", - "name": "Potentiam", - "symbol": "PTM", - "rank": 3266, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "milk2-milkyway-coin", - "name": "Milkyway Coin", - "symbol": "MILK2", - "rank": 3267, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idtt-identity", - "name": "Identity", - "symbol": "IDTT", - "rank": 3268, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pis-polkainsure-finance", - "name": "Polkainsure Finance", - "symbol": "PIS", - "rank": 3269, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cia-cia", - "name": "CIA", - "symbol": "CIA", - "rank": 3270, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ponyo-ponyo-inu", - "name": "Ponyo-Inu", - "symbol": "PONYO", - "rank": 3271, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "omt-mars-token", - "name": "Mars Token", - "symbol": "OMT", - "rank": 3272, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pet-battle-pets", - "name": "Battle Pets", - "symbol": "PET", - "rank": 3273, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rfx-reflex", - "name": "Reflex", - "symbol": "RFX", - "rank": 3274, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ldx-londex", - "name": "Londex", - "symbol": "LDX", - "rank": 3275, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "exg-exg", - "name": "EXG", - "symbol": "EXG", - "rank": 3276, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "puml-puml-better-health", - "name": "PUML Better Health", - "symbol": "PUML", - "rank": 3277, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nft-nft-protocol", - "name": "NFT Protocol", - "symbol": "NFT", - "rank": 3278, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msn-msn", - "name": "MSN", - "symbol": "MSN", - "rank": 3279, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dkyc-don39t-kyc", - "name": "Don't KYC", - "symbol": "DKYC", - "rank": 3280, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "loud-loud-market", - "name": "Loud Market", - "symbol": "LOUD", - "rank": 3281, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tbe-trustbase", - "name": "TrustBase", - "symbol": "TBE", - "rank": 3282, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nova-shibanova", - "name": "ShibaNova", - "symbol": "NOVA", - "rank": 3283, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nrt-nft-royal-token", - "name": "NFT Royal Token", - "symbol": "NRT", - "rank": 3284, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bom-black-lemon", - "name": "Black Lemon", - "symbol": "BOM", - "rank": 3285, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pnode-pinknode", - "name": "Pinknode", - "symbol": "PNODE", - "rank": 3286, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mplay-metaplay", - "name": "MetaPlay", - "symbol": "MPLAY", - "rank": 3287, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zug-zug", - "name": "ZUG", - "symbol": "ZUG", - "rank": 3288, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpd-coinspaid", - "name": "CoinsPaid", - "symbol": "CPD", - "rank": 3289, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bee-honeybee", - "name": "HoneyBee", - "symbol": "BEE", - "rank": 3290, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsds-basis-dollar-share", - "name": "Basis Dollar Share", - "symbol": "BSDS", - "rank": 3291, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "story-story", - "name": "Story", - "symbol": "STORY", - "rank": 3292, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "incake-infinitycake", - "name": "InfinityCake", - "symbol": "INCAKE", - "rank": 3293, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "can-channels", - "name": "Channels", - "symbol": "CAN", - "rank": 3294, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zuna-zuna", - "name": "Zuna", - "symbol": "ZUNA", - "rank": 3295, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ogx-organix", - "name": "Organix", - "symbol": "OGX", - "rank": 3296, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phtr-phuture", - "name": "Phuture", - "symbol": "PHTR", - "rank": 3297, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kaiba-kaiba-inu", - "name": "Kaiba Inu", - "symbol": "KAIBA", - "rank": 3298, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slm-salmon", - "name": "Salmon", - "symbol": "SLM", - "rank": 3299, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abc-abell-coin", - "name": "Abell Coin", - "symbol": "ABC", - "rank": 3300, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elen-everlens", - "name": "Everlens", - "symbol": "ELEN", - "rank": 3301, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shiboki-shiboki", - "name": "Shiboki", - "symbol": "SHIBOKI", - "rank": 3302, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dtk-detik", - "name": "DETIK", - "symbol": "DTK", - "rank": 3303, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jdc-jd-coin", - "name": "JD Coin", - "symbol": "JDC", - "rank": 3304, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lsp-lumenswap", - "name": "Lumenswap", - "symbol": "LSP", - "rank": 3305, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnpl-bnpl-pay", - "name": "BNPL Pay", - "symbol": "BNPL", - "rank": 3306, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hfi-hecofi", - "name": "HecoFi", - "symbol": "HFI", - "rank": 3307, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aped-baddest-alpha-ape-bundle", - "name": "Baddest Alpha Ape Bundle", - "symbol": "APED", - "rank": 3308, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ica-icarus-network", - "name": "Icarus Network", - "symbol": "ICA", - "rank": 3309, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nelo-nelo-metaverse", - "name": "NELO Metaverse", - "symbol": "NELO", - "rank": 3310, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fast-fastswap", - "name": "FastSwap", - "symbol": "FAST", - "rank": 3311, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bytz-bytz", - "name": "Bytz", - "symbol": "BYTZ", - "rank": 3312, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ero-eroverse", - "name": "Eroverse", - "symbol": "ERO", - "rank": 3313, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mts-metis", - "name": "Metis", - "symbol": "MTS", - "rank": 3314, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pny-peony-coin", - "name": "Peony Coin", - "symbol": "PNY", - "rank": 3315, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spce-virgin-galactic", - "name": "Virgin Galactic", - "symbol": "SPCE", - "rank": 3316, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "relay-relay-token", - "name": "Relay Token", - "symbol": "RELAY", - "rank": 3317, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mis-mithril-share", - "name": "Mithril Share", - "symbol": "MIS", - "rank": 3318, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pib-pibble", - "name": "PIBBLE", - "symbol": "PIB", - "rank": 3319, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccs-cloutcontracts", - "name": "CloutContracts", - "symbol": "CCS", - "rank": 3320, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddt-dar-dex-token", - "name": "Dar Dex Token", - "symbol": "DDT", - "rank": 3321, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ndau-ndau", - "name": "Ndau", - "symbol": "NDAU", - "rank": 3322, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "isla-defiville-island", - "name": "DefiVille Island", - "symbol": "ISLA", - "rank": 3323, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prot-armzlegends", - "name": "ArmzLegends", - "symbol": "PROT", - "rank": 3324, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "del-decimal", - "name": "Decimal", - "symbol": "DEL", - "rank": 3325, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "omnis-omnis", - "name": "OMNIS", - "symbol": "OMNIS", - "rank": 3326, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oil-oiler", - "name": "Oiler", - "symbol": "OIL", - "rank": 3327, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfm-defi-on-mcw", - "name": "DeFi on MCW", - "symbol": "DFM", - "rank": 3328, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "azl-azul-token", - "name": "Azul Token", - "symbol": "AZL", - "rank": 3329, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cherry-cherrypick", - "name": "CherryPick", - "symbol": "CHERRY", - "rank": 3330, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nafty-nafty", - "name": "Nafty", - "symbol": "NAFTY", - "rank": 3331, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swpt-swaptracker", - "name": "SwapTracker", - "symbol": "SWPT", - "rank": 3332, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "granx-granx-chain", - "name": "GranX Chain", - "symbol": "GRANX", - "rank": 3333, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "r1-recast1", - "name": "Recast1", - "symbol": "R1", - "rank": 3334, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pizza-pizzaswap", - "name": "PizzaSwap", - "symbol": "PIZZA", - "rank": 3335, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "earn-yearn-classic-finance", - "name": "Yearn Classic Finance", - "symbol": "EARN", - "rank": 3336, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kp2r-kp2rnetwork", - "name": "kp2r.network", - "symbol": "KP2R", - "rank": 3337, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bxa-blockchain-exchange-alliance", - "name": "Blockchain Exchange Alliance", - "symbol": "BXA", - "rank": 3338, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flokim-flokimooni", - "name": "Flokimooni", - "symbol": "FLOKIM", - "rank": 3339, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ebsc-earlybsc", - "name": "EarlyBSC", - "symbol": "EBSC", - "rank": 3340, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uco-uniris", - "name": "Uniris", - "symbol": "UCO", - "rank": 3341, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "burp-coinburp", - "name": "CoinBurp", - "symbol": "BURP", - "rank": 3342, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnft-manufactory", - "name": "ManuFactory", - "symbol": "MNFT", - "rank": 3343, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "modex-modex", - "name": "Modex", - "symbol": "MODEX", - "rank": 3344, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dft-digifinextoken", - "name": "DigiFinexToken", - "symbol": "DFT", - "rank": 3345, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chs-cheeseswap", - "name": "CheeseSwap", - "symbol": "CHS", - "rank": 3346, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogira-dogira", - "name": "Dogira", - "symbol": "DOGIRA", - "rank": 3347, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sas-stand-share", - "name": "Stand Share", - "symbol": "SAS", - "rank": 3348, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mead-thors-mead", - "name": "Thors Mead", - "symbol": "MEAD", - "rank": 3349, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "esw-emiswap", - "name": "EmiSwap", - "symbol": "ESW", - "rank": 3350, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yas-yas", - "name": "YAS", - "symbol": "YAS", - "rank": 3351, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bscm-bsc-memepad", - "name": "BSC MemePad", - "symbol": "BSCM", - "rank": 3352, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "comb-combo", - "name": "Combo", - "symbol": "COMB", - "rank": 3353, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpd-palladium-spot-token", - "name": "Palladium Spot Token", - "symbol": "XPD", - "rank": 3354, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "links-links", - "name": "Links", - "symbol": "LINKS", - "rank": 3355, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gp-wizards-and-dragons", - "name": "Wizards And Dragons", - "symbol": "GP", - "rank": 3356, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "biib-biogen-inc", - "name": "Biogen Inc.", - "symbol": "BIIB", - "rank": 3357, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rak-rake-finance", - "name": "Rake Finance", - "symbol": "RAK", - "rank": 3358, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "weyu-weyu", - "name": "Weyu", - "symbol": "WEYU", - "rank": 3359, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meta-metaverse-miner", - "name": "Metaverse Miner", - "symbol": "META", - "rank": 3360, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reva-revault-network", - "name": "Revault Network", - "symbol": "REVA", - "rank": 3361, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shd-shardingdao", - "name": "ShardingDAO", - "symbol": "SHD", - "rank": 3362, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdm-medium", - "name": "MEDIUM", - "symbol": "MDM", - "rank": 3363, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "treasure-treasure", - "name": "Treasure", - "symbol": "TREASURE", - "rank": 3364, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ncdt-nucocloud", - "name": "Nuco.cloud", - "symbol": "NCDT", - "rank": 3365, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdog-safedog", - "name": "SAFEDOG", - "symbol": "SDOG", - "rank": 3366, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fren-frenchie", - "name": "Frenchie", - "symbol": "FREN", - "rank": 3367, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "som-somnium", - "name": "Somnium", - "symbol": "SOM", - "rank": 3368, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meet-coinmeet", - "name": "CoinMeet", - "symbol": "MEET", - "rank": 3369, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grc-green-flow", - "name": "Green Flow", - "symbol": "GRC", - "rank": 3370, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kpad-kickpad", - "name": "KickPad", - "symbol": "KPAD", - "rank": 3371, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snk-snake-token", - "name": "Snake Token", - "symbol": "SNK", - "rank": 3372, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dusd-dusd-network", - "name": "DUSD Network", - "symbol": "DUSD", - "rank": 3373, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "f1c-future1coin", - "name": "Future1coin", - "symbol": "F1C", - "rank": 3374, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fam-fam-token", - "name": "FAM Token", - "symbol": "FAM", - "rank": 3375, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "squid2-squid2io", - "name": "Squid2.io", - "symbol": "SQUID2", - "rank": 3376, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "seamless-seamlessswap-token", - "name": "SeamlessSwap Token", - "symbol": "SEAMLESS", - "rank": 3377, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dhr-dehr-network", - "name": "DeHR Network", - "symbol": "DHR", - "rank": 3378, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lby-lobby", - "name": "Lobby", - "symbol": "LBY", - "rank": 3379, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lift-uplift", - "name": "Uplift", - "symbol": "LIFT", - "rank": 3380, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alpa-alpaca-city", - "name": "Alpaca City", - "symbol": "ALPA", - "rank": 3381, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sovi-sovi-token", - "name": "Sovi Token", - "symbol": "SOVI", - "rank": 3382, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "exzo-exzocoin-20", - "name": "ExzoCoin 2.0", - "symbol": "EXZO", - "rank": 3383, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "um-continuum-world", - "name": "Continuum World", - "symbol": "UM", - "rank": 3384, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mpad-multipad", - "name": "MultiPad", - "symbol": "MPAD", - "rank": 3385, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fndz-fndz-token", - "name": "FNDZ Token", - "symbol": "FNDZ", - "rank": 3386, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myce-my-ceremonial-event", - "name": "MY Ceremonial Event", - "symbol": "MYCE", - "rank": 3387, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "awool-sheep-game", - "name": "Sheep Game", - "symbol": "AWOOL", - "rank": 3388, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iddx-indodex", - "name": "IndoDEX", - "symbol": "IDDX", - "rank": 3389, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcs-mcs-token", - "name": "MCS Token", - "symbol": "MCS", - "rank": 3390, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "attn-attn", - "name": "ATTN", - "symbol": "ATTN", - "rank": 3391, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hint-hintchain", - "name": "Hintchain", - "symbol": "HINT", - "rank": 3392, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lol-lol", - "name": "LOL", - "symbol": "LOL", - "rank": 3393, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lgold-lyfe-gold", - "name": "Lyfe Gold", - "symbol": "LGOLD", - "rank": 3394, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xwin-xwin-finance", - "name": "xWIN Finance", - "symbol": "XWIN", - "rank": 3395, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "joke-joke-community", - "name": "Joke Community", - "symbol": "$JOKE", - "rank": 3396, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbunny-rewards-bunny", - "name": "Rewards Bunny", - "symbol": "RBUNNY", - "rank": 3397, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "obs-one-basis-cash", - "name": "One Basis Cash", - "symbol": "OBS", - "rank": 3398, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "css-coinswap-space", - "name": "CoinSwap Space", - "symbol": "CSS", - "rank": 3399, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zik-ziktalk", - "name": "Ziktalk", - "symbol": "ZIK", - "rank": 3400, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "yvs-yvs-finance", - "name": "YVS Finance", - "symbol": "YVS", - "rank": 3401, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dis-tosdis", - "name": "TosDis", - "symbol": "DIS", - "rank": 3402, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbt-cryptoblast", - "name": "CryptoBlast", - "symbol": "CBT", - "rank": 3403, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xeus-xeus", - "name": "XEUS", - "symbol": "XEUS", - "rank": 3404, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tosc-tos", - "name": "T.OS", - "symbol": "TOSC", - "rank": 3405, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "onx-onx-finance", - "name": "OnX Finance", - "symbol": "ONX", - "rank": 3406, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hoff-hoff-coin", - "name": "Hoff Coin", - "symbol": "HOFF", - "rank": 3407, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mrna-moderna", - "name": "Moderna", - "symbol": "MRNA", - "rank": 3408, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fk-fk-coin", - "name": "FK Coin", - "symbol": "FK", - "rank": 3409, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boku-boryoku-dragonz", - "name": "Boryoku Dragonz", - "symbol": "BOKU", - "rank": 3410, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dass-dashsports", - "name": "DashSports", - "symbol": "DASS", - "rank": 3411, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pbtc-ptokens-btc", - "name": "pTokens BTC", - "symbol": "PBTC", - "rank": 3412, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmerge-coinmerge", - "name": "CoinMerge", - "symbol": "CMERGE", - "rank": 3413, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bec-betherchip", - "name": "Betherchip", - "symbol": "BEC", - "rank": 3414, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "geo-geopoly", - "name": "Geopoly", - "symbol": "GEO$", - "rank": 3415, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "miks-miks-coin", - "name": "MIKS COIN", - "symbol": "MIKS", - "rank": 3416, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myne-itsmyne", - "name": "ITSMYNE", - "symbol": "MYNE", - "rank": 3417, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zenc-zenccoin", - "name": "Zenccoin", - "symbol": "ZENC", - "rank": 3418, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "holy-holy-trinity", - "name": "Holy Trinity", - "symbol": "HOLY", - "rank": 3419, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mltpx-moonlift-capital", - "name": "Moonlift Capital", - "symbol": "MLTPX", - "rank": 3420, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "utu-utu-coin", - "name": "UTU Coin", - "symbol": "UTU", - "rank": 3421, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibic-shiba-inu-classic", - "name": "Shiba Inu Classic", - "symbol": "SHIBIC", - "rank": 3422, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gbts-gembites", - "name": "GemBites", - "symbol": "GBTS", - "rank": 3423, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xlc-liquidchain", - "name": "Liquidchain", - "symbol": "XLC", - "rank": 3424, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ixs-ix-swap", - "name": "IX Swap", - "symbol": "IXS", - "rank": 3425, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cphr-polkacipher", - "name": "PolkaCipher", - "symbol": "CPHR", - "rank": 3426, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfd-defidollar-dao", - "name": "DefiDollar DAO", - "symbol": "DFD", - "rank": 3427, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pofi-pofi", - "name": "PoFi", - "symbol": "POFI", - "rank": 3428, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pilot-unipilot", - "name": "Unipilot", - "symbol": "PILOT", - "rank": 3429, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adacash-adacash", - "name": "ADAcash", - "symbol": "ADACASH", - "rank": 3430, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfb-finbet", - "name": "FinBet", - "symbol": "CFB", - "rank": 3431, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "filda-filda", - "name": "Filda", - "symbol": "FILDA", - "rank": 3432, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kodi-kodi", - "name": "KODI", - "symbol": "KODI", - "rank": 3433, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fan-fanadise", - "name": "Fanadise", - "symbol": "FAN", - "rank": 3434, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kori-kori-inu", - "name": "Kori Inu", - "symbol": "KORI", - "rank": 3435, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dvdx-derived", - "name": "Derived", - "symbol": "DVDX", - "rank": 3436, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bhd-bitcoin-hd", - "name": "Bitcoin HD", - "symbol": "BHD", - "rank": 3437, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "copycat-copycat-finance", - "name": "Copycat Finance", - "symbol": "COPYCAT", - "rank": 3438, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfx-your-futures-exchange", - "name": "Your Futures Exchange", - "symbol": "YFX", - "rank": 3439, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "select-smart-electrum", - "name": "Smart Electrum", - "symbol": "SELECT", - "rank": 3440, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clc-cifculation", - "name": "Cifculation", - "symbol": "CLC", - "rank": 3441, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btbs-bitbase-token", - "name": "BitBase Token", - "symbol": "BTBS", - "rank": 3442, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "quam-quam-network", - "name": "Quam Network", - "symbol": "QUAM", - "rank": 3443, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "node-whole-network", - "name": "Whole Network", - "symbol": "NODE", - "rank": 3444, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nvda-nvidia", - "name": "NVIDIA", - "symbol": "NVDA", - "rank": 3445, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sld-soldiernodes", - "name": "SoldierNodes", - "symbol": "SLD", - "rank": 3446, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crox-croxswap", - "name": "CroxSwap", - "symbol": "CROX", - "rank": 3447, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dic-daikicoin", - "name": "DAIKICOIN", - "symbol": "DIC", - "rank": 3448, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dms-dragon-mainland-shards", - "name": "Dragon Mainland Shards", - "symbol": "DMS", - "rank": 3449, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dss-defi-shopping-stake", - "name": "Defi Shopping Stake", - "symbol": "DSS", - "rank": 3450, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xqc-quras-token", - "name": "Quras Token", - "symbol": "XQC", - "rank": 3451, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kit-kitty", - "name": "Kitty", - "symbol": "KIT", - "rank": 3452, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xmn-xmine", - "name": "Xmine", - "symbol": "XMN", - "rank": 3453, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "simply-simply", - "name": "Simply", - "symbol": "SIMPLY", - "rank": 3454, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ltk-linktoken", - "name": "LinkToken", - "symbol": "LTK", - "rank": 3455, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gin-ginga-finance", - "name": "Ginga Finance", - "symbol": "GIN", - "rank": 3456, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elp-the-everlasting-parachain", - "name": "The Everlasting Parachain", - "symbol": "ELP", - "rank": 3457, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cyce-crypto-carbon-energy", - "name": "Crypto Carbon Energy", - "symbol": "CYCE", - "rank": 3458, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thc-transhuman-coin", - "name": "Transhuman Coin", - "symbol": "THC", - "rank": 3459, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yrise-yrise-finance", - "name": "yRise Finance", - "symbol": "YRISE", - "rank": 3460, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pig-pig-finance", - "name": "Pig Finance", - "symbol": "PIG", - "rank": 3461, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gull-polygod", - "name": "PolyGod", - "symbol": "GULL", - "rank": 3462, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hunny-hunny-finance", - "name": "Hunny Finance", - "symbol": "HUNNY", - "rank": 3463, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnbu-bnbuniverse", - "name": "BNBUNIVERSE", - "symbol": "BNBU", - "rank": 3464, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arte-items", - "name": "Items", - "symbol": "ARTE", - "rank": 3465, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "propel-propel", - "name": "Propel", - "symbol": "PROPEL", - "rank": 3466, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safest-safufide", - "name": "SafuFide", - "symbol": "SAFEST", - "rank": 3467, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "addy-adamant", - "name": "Adamant", - "symbol": "ADDY", - "rank": 3468, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dod-defender-of-doge", - "name": "Defender of Doge", - "symbol": "DOD", - "rank": 3469, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dbz-diamond-boyz-coin", - "name": "Diamond Boyz Coin", - "symbol": "DBZ", - "rank": 3470, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sne-strongnode", - "name": "StrongNode", - "symbol": "SNE", - "rank": 3471, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inu-hachiko-inu-token", - "name": "Hachiko Inu Token", - "symbol": "INU", - "rank": 3472, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "onyx-onyx", - "name": "Onyx", - "symbol": "ONYX", - "rank": 3473, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cure-cure-token-v2", - "name": "CURE Token V2", - "symbol": "CURE", - "rank": 3474, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kltr-kollector", - "name": "KOLLECTOR", - "symbol": "KLTR", - "rank": 3475, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "2022m-2022moon", - "name": "2022MOON", - "symbol": "2022M", - "rank": 3476, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "toz-tozex", - "name": "Tozex", - "symbol": "TOZ", - "rank": 3477, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdoge-xdoge", - "name": "Xdoge", - "symbol": "XDOGE", - "rank": 3478, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evcoin-everestcoin", - "name": "EverestCoin", - "symbol": "EVCOIN", - "rank": 3479, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fol-folder-protocol", - "name": "Folder Protocol", - "symbol": "FOL", - "rank": 3480, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "n2-node-squared", - "name": "Node Squared", - "symbol": "N2", - "rank": 3481, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fit-fit-token", - "name": "FIT Token", - "symbol": "FIT", - "rank": 3482, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cff-coffe-utility-token", - "name": "COFFE utility token", - "symbol": "CFF", - "rank": 3483, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spice-spice-dao", - "name": "Spice DAO", - "symbol": "SPICE", - "rank": 3484, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "per-per-project", - "name": "PER Project", - "symbol": "PER", - "rank": 3485, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "3dog-cerberus", - "name": "Cerberus", - "symbol": "3DOG", - "rank": 3486, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtg-magnetgold", - "name": "MAGNETGOLD", - "symbol": "MTG", - "rank": 3487, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dxb-dxbpay", - "name": "DXBPay", - "symbol": "DXB", - "rank": 3488, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hop-hoppy", - "name": "HOPPY", - "symbol": "HOP", - "rank": 3489, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogo-dogemongo", - "name": "DogemonGo", - "symbol": "DOGO", - "rank": 3490, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csc-crypto-soccer", - "name": "Crypto Soccer", - "symbol": "CSC", - "rank": 3491, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blk-blink", - "name": "BLink", - "symbol": "BLK", - "rank": 3492, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aur-aurix", - "name": "Aurix", - "symbol": "AUR", - "rank": 3493, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lez-peoplez", - "name": "Peoplez", - "symbol": "LEZ", - "rank": 3494, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pod-podo-point", - "name": "Podo Point", - "symbol": "POD", - "rank": 3495, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "luxy-luxy", - "name": "Luxy", - "symbol": "LUXY", - "rank": 3496, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hbo-hash-bridge-oracle", - "name": "Hash Bridge Oracle", - "symbol": "HBO", - "rank": 3497, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coal-coalculus", - "name": "Coalculus", - "symbol": "COAL", - "rank": 3498, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bcp-bcp-token", - "name": "Blockchain Poland", - "symbol": "BCP", - "rank": 3499, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sound-sound-coin", - "name": "Sound Coin", - "symbol": "SOUND", - "rank": 3500, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bricks-mybricks", - "name": "MyBricks", - "symbol": "BRICKS", - "rank": 3501, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wag-waggle-network", - "name": "Waggle Network", - "symbol": "WAG", - "rank": 3502, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "diko-arkadiko", - "name": "Arkadiko", - "symbol": "DIKO", - "rank": 3503, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "niifi-niifi", - "name": "NiiFi", - "symbol": "NIIFI", - "rank": 3504, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "leonidas-leonidas-token", - "name": "Leonidas Token", - "symbol": "LEONIDAS", - "rank": 3505, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trubgr-trubadger", - "name": "TruBadger", - "symbol": "TRUBGR", - "rank": 3506, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ton-tontoken", - "name": "TONToken", - "symbol": "TON", - "rank": 3507, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nsc-nftsocial", - "name": "NFTSocial", - "symbol": "NSC", - "rank": 3508, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bles-blind-boxes", - "name": "Blind Boxes", - "symbol": "BLES", - "rank": 3509, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wdr-wider-coin", - "name": "Wider Coin", - "symbol": "WDR", - "rank": 3510, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ocah-omni-cash", - "name": "Omni Cash", - "symbol": "OCA$H", - "rank": 3511, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cric-cricket-foundation", - "name": "Cricket Foundation", - "symbol": "CRIC", - "rank": 3512, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ryu-hakuryu", - "name": "Hakuryu", - "symbol": "$RYU", - "rank": 3513, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mola-moonlana", - "name": "MoonLana", - "symbol": "MOLA", - "rank": 3514, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flurry-flurry-finance", - "name": "Flurry Finance", - "symbol": "FLURRY", - "rank": 3515, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atbfig-financial-intelligence-group-token", - "name": "Financial Intelligence Group Token", - "symbol": "ATBFIG", - "rank": 3516, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ebso-eblockstock", - "name": "eBlockStock", - "symbol": "EBSO", - "rank": 3517, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mt-mytoken", - "name": "MyToken", - "symbol": "MT", - "rank": 3518, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mash-marshmallowdefi", - "name": "MarshmallowDeFi", - "symbol": "MASH", - "rank": 3519, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "epichero-epichero", - "name": "EpicHero", - "symbol": "EPICHERO", - "rank": 3520, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbp-nftbomb", - "name": "NFTBomb", - "symbol": "NBP", - "rank": 3521, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mfo-moonfarm-finance", - "name": "MoonFarm Finance", - "symbol": "MFO", - "rank": 3522, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kau-kauri", - "name": "Kauri", - "symbol": "KAU", - "rank": 3523, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "teer-integritee", - "name": "Integritee", - "symbol": "TEER", - "rank": 3524, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fpl-farm-planet", - "name": "Farm Planet", - "symbol": "FPL", - "rank": 3525, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "busdx-busdx", - "name": "BUSDX", - "symbol": "BUSDX", - "rank": 3526, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mrch-merchdao", - "name": "MerchDAO", - "symbol": "MRCH", - "rank": 3527, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prz-prize-coin", - "name": "Prize Coin", - "symbol": "PRZ", - "rank": 3528, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftl-nifty-league", - "name": "Nifty League", - "symbol": "NFTL", - "rank": 3529, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nsi-nano-saitama-inu", - "name": "Nano Saitama Inu", - "symbol": "NSI", - "rank": 3530, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mswap-moneyswap", - "name": "MoneySwap", - "symbol": "MSWAP", - "rank": 3531, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ghsp-ghospers-game", - "name": "Ghospers Game", - "symbol": "GHSP", - "rank": 3532, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jt-jubi-token", - "name": "Jubi Token", - "symbol": "JT", - "rank": 3533, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tfbx-truefeedback-token", - "name": "Truefeedback Token", - "symbol": "TFBX", - "rank": 3534, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hnw-hobbs-networking", - "name": "Hobbs Networking", - "symbol": "HNW", - "rank": 3535, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nvax-novavax", - "name": "Novavax", - "symbol": "NVAX", - "rank": 3536, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dexi-dexioprotocol", - "name": "Dexioprotocol", - "symbol": "DEXI", - "rank": 3537, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cw-cardwallet", - "name": "CardWallet", - "symbol": "CW", - "rank": 3538, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tempo-tempo-dao", - "name": "Tempo DAO", - "symbol": "TEMPO", - "rank": 3539, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "helios-mission-helios", - "name": "Mission Helios", - "symbol": "HELIOS", - "rank": 3540, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "erw-zeloop-eco-reward", - "name": "ZeLoop Eco Reward", - "symbol": "ERW", - "rank": 3541, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "waultx-wault", - "name": "Wault", - "symbol": "WAULTX", - "rank": 3542, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atk-attack-wagon", - "name": "Attack Wagon", - "symbol": "ATK", - "rank": 3543, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bull-bullionfx", - "name": "BullionFX", - "symbol": "BULL", - "rank": 3544, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "loa-loa-protocol", - "name": "LOA Protocol", - "symbol": "LOA", - "rank": 3545, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pulsemoon-pulsemoon", - "name": "PulseMoon", - "symbol": "PULSEMOON", - "rank": 3546, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anvs-conquer-uranus", - "name": "Conquer Uranus", - "symbol": "ANVS", - "rank": 3547, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "genx-genx-token", - "name": "Genx Token", - "symbol": "GENX", - "rank": 3548, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dtx-digital-ticks", - "name": "Digital Ticks", - "symbol": "DTX", - "rank": 3549, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "racex-racex", - "name": "RaceX", - "symbol": "RACEX", - "rank": 3550, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "divine-divine-dao", - "name": "Divine DAO", - "symbol": "DIVINE", - "rank": 3551, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dench-denchcoin", - "name": "Denchcoin", - "symbol": "DENCH", - "rank": 3552, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogecoin-buff-doge-coin", - "name": "Buff Doge Coin", - "symbol": "DOGECOIN", - "rank": 3553, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cby-cberry", - "name": "CBerry", - "symbol": "CBY", - "rank": 3554, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "riders-crypto-bike-riders", - "name": "Crypto Bike Riders", - "symbol": "RIDERS", - "rank": 3555, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nnb-nnb-token", - "name": "NNB Token", - "symbol": "NNB", - "rank": 3556, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cadacoin-the-canada-coin", - "name": "The Canada Coin", - "symbol": "CADACOIN", - "rank": 3557, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "coinmama-mamaverse", - "name": "MamaVerse", - "symbol": "COINMAMA", - "rank": 3558, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dbuy-doont-buy", - "name": "Doont Buy", - "symbol": "DBUY", - "rank": 3559, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnfst-manifest", - "name": "Manifest", - "symbol": "MNFST", - "rank": 3560, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "income-income-island-token", - "name": "Income Island Token", - "symbol": "INCOME", - "rank": 3561, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chm-cryptochrome", - "name": "Cryptochrome", - "symbol": "CHM", - "rank": 3562, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hlo-helio", - "name": "Helio", - "symbol": "HLO", - "rank": 3563, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "feed-feeder-finance", - "name": "Feeder Finance", - "symbol": "FEED", - "rank": 3564, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "deeznuts-deez-nuts", - "name": "Deez Nuts", - "symbol": "DEEZNUTS", - "rank": 3565, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boost-boost", - "name": "Boost", - "symbol": "BOOST", - "rank": 3566, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dara-immutable", - "name": "Immutable", - "symbol": "DARA", - "rank": 3567, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hrd-hoard", - "name": "Hoard", - "symbol": "HRD", - "rank": 3568, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msh-crir-msh", - "name": "CRIR MSH", - "symbol": "MSH", - "rank": 3569, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bright-bright-union", - "name": "Bright Union", - "symbol": "BRIGHT", - "rank": 3570, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wolf-moonwolfio", - "name": "moonwolf.io", - "symbol": "WOLF", - "rank": 3571, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dsys-dsys", - "name": "DSYS", - "symbol": "DSYS", - "rank": 3572, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ork-orakuru", - "name": "Orakuru", - "symbol": "ORK", - "rank": 3573, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fb-facebook", - "name": "Facebook", - "symbol": "FB", - "rank": 3574, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "klee-kleekai", - "name": "KleeKai", - "symbol": "KLEE", - "rank": 3575, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "srt-solidray-finance", - "name": "Solidray Finance", - "symbol": "SRT", - "rank": 3576, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "naut-astronaut", - "name": "Astronaut", - "symbol": "NAUT", - "rank": 3577, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdex-xdefi-governance-token", - "name": "XDEFI Governance Token", - "symbol": "XDEX", - "rank": 3578, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pine-atrollcity", - "name": "Atrollcity", - "symbol": "PINE", - "rank": 3579, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "splash-splash", - "name": "Splash", - "symbol": "SPLASH", - "rank": 3580, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scs-shining-crystal-shard", - "name": "Shining Crystal Shard", - "symbol": "SCS", - "rank": 3581, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gemit-gemitapp", - "name": "GEMIT.app", - "symbol": "GEMIT", - "rank": 3582, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "azu-azus", - "name": "Azus", - "symbol": "AZU", - "rank": 3583, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mcu-memecoinuniverse", - "name": "MemeCoinUniverse", - "symbol": "MCU", - "rank": 3584, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctc-culture-ticket-chain", - "name": "Culture Ticket Chain", - "symbol": "CTC", - "rank": 3585, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "superbid-superbid", - "name": "SuperBid", - "symbol": "SUPERBID", - "rank": 3586, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "opa-option-panda-platform", - "name": "Option Panda Platform", - "symbol": "OPA", - "rank": 3587, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lpi-lpi-dao", - "name": "LPI DAO", - "symbol": "LPI", - "rank": 3588, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "depo-depocket", - "name": "DePocket", - "symbol": "DEPO", - "rank": 3589, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ri-ri-token", - "name": "RI Token", - "symbol": "RI", - "rank": 3590, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bwi-bitwin242232", - "name": "Bitwin24", - "symbol": "BWI", - "rank": 3591, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "suteku-suteku", - "name": "Suteku", - "symbol": "SUTEKU", - "rank": 3592, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evdc-electric-vehicle-direct-currency", - "name": "Electric Vehicle Direct Currency", - "symbol": "EVDC", - "rank": 3593, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cswap-crossswap", - "name": "CrossSwap", - "symbol": "CSWAP", - "rank": 3594, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "its-iteration-syndicate", - "name": "Iteration Syndicate", - "symbol": "ITS", - "rank": 3595, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "devo-devolution", - "name": "DeVolution", - "symbol": "DEVO", - "rank": 3596, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rnx-roonex", - "name": "ROONEX", - "symbol": "RNX", - "rank": 3597, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cuminu-cum-inu", - "name": "Cum Inu", - "symbol": "CUMINU", - "rank": 3598, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pho-phoswap", - "name": "Phoswap", - "symbol": "PHO", - "rank": 3599, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "terracredit-credit", - "name": "TerraCredit", - "symbol": "CREDIT", - "rank": 3600, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sup-supertx-governance-token", - "name": "SuperTx Governance Token", - "symbol": "SUP", - "rank": 3601, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "irt-infinity-rocket-token", - "name": "Infinity Rocket Token", - "symbol": "IRT", - "rank": 3602, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecc-empire-capital-token", - "name": "Empire Capital Token", - "symbol": "ECC", - "rank": 3603, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dto-dotoracle", - "name": "DotOracle", - "symbol": "DTO", - "rank": 3604, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "deus-deus-finance", - "name": "DEUS Finance", - "symbol": "DEUS", - "rank": 3605, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moolah-block-creatures", - "name": "Block Creatures", - "symbol": "MOOLAH", - "rank": 3606, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bito-bito-coin", - "name": "BITO Coin", - "symbol": "BITO", - "rank": 3607, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frt-fertilizer", - "name": "Fertilizer", - "symbol": "FRT", - "rank": 3608, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mega-megacryptopolis", - "name": "MegaCryptoPolis", - "symbol": "MEGA", - "rank": 3609, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eva-evanesco-network", - "name": "Evanesco Network", - "symbol": "EVA", - "rank": 3610, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sgo-sportemon-go", - "name": "Sportemon Go", - "symbol": "SGO", - "rank": 3611, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lud-ludos-protocol", - "name": "Ludos Protocol", - "symbol": "LUD", - "rank": 3612, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bfi-bearnfi", - "name": "Bearn.fi", - "symbol": "BFI", - "rank": 3613, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xy-xy-finance", - "name": "XY Finance", - "symbol": "XY", - "rank": 3614, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polp-polkaparty", - "name": "PolkaParty", - "symbol": "POLP", - "rank": 3615, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brt-base-reward-token", - "name": "Base Reward Token", - "symbol": "BRT", - "rank": 3616, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kala-kalata-protocol", - "name": "Kalata Protocol", - "symbol": "KALA", - "rank": 3617, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lorda-lord-arena", - "name": "Lord Arena", - "symbol": "LORDA", - "rank": 3618, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cyop-cyop-protocol", - "name": "CyOp | Protocol", - "symbol": "CyOp", - "rank": 3619, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gam-gamma-token", - "name": "Gamma Token", - "symbol": "GAM", - "rank": 3620, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moca-museum-of-crypto-art", - "name": "Museum of Crypto Art", - "symbol": "MOCA", - "rank": 3621, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cheems-cheems", - "name": "Cheems", - "symbol": "CHEEMS", - "rank": 3622, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gains-gains", - "name": "Gains", - "symbol": "GAINS", - "rank": 3623, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xcb-cryptobirds-xcb", - "name": "CryptoBirds XCB", - "symbol": "XCB", - "rank": 3624, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cor-corginftgame", - "name": "CorgiNFTGame", - "symbol": "COR", - "rank": 3625, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pan-pantos", - "name": "PANTOS", - "symbol": "PAN", - "rank": 3626, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cxrbn-cxrbn-token", - "name": "CXRBN Token", - "symbol": "CXRBN", - "rank": 3627, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inpa-inpa-coin", - "name": "INPA Coin", - "symbol": "INPA", - "rank": 3628, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "colos-chaincolosseum-token", - "name": "ChainColosseum Token", - "symbol": "COLOS", - "rank": 3629, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fcel-fuelcell-energy-inc", - "name": "FuelCell Energy, Inc.", - "symbol": "FCEL", - "rank": 3630, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mspc-monspac", - "name": "Monspac", - "symbol": "MSPC", - "rank": 3631, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "peeps-the-peoples-coin", - "name": "The People’s Coin", - "symbol": "PEEPS", - "rank": 3632, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bpkr-blackpoker", - "name": "BlackPoker", - "symbol": "BPKR", - "rank": 3633, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "otaku-fomo-chronicles-manga", - "name": "FOMO Chronicles Manga", - "symbol": "OTAKU", - "rank": 3634, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flokipup-floki-pup", - "name": "Floki Pup", - "symbol": "FLOKIPUP", - "rank": 3635, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "visr-visor", - "name": "Visor", - "symbol": "VISR", - "rank": 3636, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tnode-trusted-node", - "name": "Trusted Node", - "symbol": "TNODE", - "rank": 3637, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phoenix-phoenix", - "name": "Phoenix", - "symbol": "PHOENIX", - "rank": 3638, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "4ch5-4chan", - "name": "4Chan", - "symbol": "4CH5", - "rank": 3639, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rdpx-dopex-rebate-token", - "name": "Dopex Rebate Token", - "symbol": "RDPX", - "rank": 3640, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "otb-otcbtc-token", - "name": "OTCBTC Token", - "symbol": "OTB", - "rank": 3641, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hro-cryptodicehero", - "name": "CryptoDiceHero", - "symbol": "HRO", - "rank": 3642, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xai-sideshift-token", - "name": "SideShift Token", - "symbol": "XAI", - "rank": 3643, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acu-acu-platform", - "name": "ACU Platform", - "symbol": "ACU", - "rank": 3644, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vee-vee-finance", - "name": "Vee Finance", - "symbol": "VEE", - "rank": 3645, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dquick-dragon39s-quick", - "name": "Dragon's Quick", - "symbol": "DQUICK", - "rank": 3646, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egcc-engine", - "name": "Engine", - "symbol": "EGCC", - "rank": 3647, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adf-ad-flex-token", - "name": "Ad Flex Token", - "symbol": "ADF", - "rank": 3648, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snk-snook", - "name": "Snook", - "symbol": "SNK", - "rank": 3649, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "strip-strip-finance", - "name": "Strip Finance", - "symbol": "STRIP", - "rank": 3650, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bali-bali-coin", - "name": "Bali Coin", - "symbol": "BALI", - "rank": 3651, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "jk-jk-coin", - "name": "JK Coin", - "symbol": "JK", - "rank": 3652, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eagon-eagonswap-token", - "name": "EagonSwap Token", - "symbol": "EAGON", - "rank": 3653, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "agro-agro-global", - "name": "Agro Global", - "symbol": "AGRO", - "rank": 3654, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arcona-arcona", - "name": "Arcona", - "symbol": "ARCONA", - "rank": 3655, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvl-revolotto", - "name": "Revolotto", - "symbol": "RVL", - "rank": 3656, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ucap-unicapfinance", - "name": "Unicap.Finance", - "symbol": "UCAP", - "rank": 3657, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mflokiada-miniflokiada", - "name": "MiniFlokiADA", - "symbol": "MFLOKIADA", - "rank": 3658, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nan-nantrade", - "name": "NanTrade", - "symbol": "NAN", - "rank": 3659, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kphi-kephi-gallery", - "name": "Kephi Gallery", - "symbol": "KPHI", - "rank": 3660, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blwa-blockwarrior", - "name": "BlockWarrior", - "symbol": "BLWA", - "rank": 3661, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "floki-floki-musk", - "name": "Floki Musk", - "symbol": "FLOKI", - "rank": 3662, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vinci-vinci", - "name": "Vinci", - "symbol": "VINCI", - "rank": 3663, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gshiba-gambler-shiba", - "name": "Gambler Shiba", - "symbol": "GSHIBA", - "rank": 3664, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mverse-maticverse", - "name": "MaticVerse", - "symbol": "MVERSE", - "rank": 3665, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "por-portuma", - "name": "Portuma", - "symbol": "POR", - "rank": 3666, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cisla-crypto-island", - "name": "Crypto Island", - "symbol": "CISLA", - "rank": 3667, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ppad-playpad", - "name": "PlayPad", - "symbol": "PPAD", - "rank": 3668, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poodl-poodl-token", - "name": "Poodl Token", - "symbol": "POODL", - "rank": 3669, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cd3d-cd3d", - "name": "CD3D", - "symbol": "CD3D", - "rank": 3670, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "camp-camp", - "name": "Camp", - "symbol": "CAMP", - "rank": 3671, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "husky-husky-avax", - "name": "Husky AVAX", - "symbol": "HUSKY", - "rank": 3672, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gtn-glitzkoin", - "name": "GlitzKoin", - "symbol": "GTN", - "rank": 3673, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pok-pokmonsters", - "name": "Pokmonsters", - "symbol": "POK", - "rank": 3674, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "momo-momo-protocol", - "name": "Momo Protocol", - "symbol": "MOMO", - "rank": 3675, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbd-greenheart-cbd", - "name": "Greenheart CBD", - "symbol": "CBD", - "rank": 3676, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hnd-hundred-finance", - "name": "Hundred Finance", - "symbol": "HND", - "rank": 3677, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pnft-pawn-my-nft", - "name": "Pawn My NFT", - "symbol": "PNFT", - "rank": 3678, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aaa-moon-rabbit", - "name": "Moon Rabbit", - "symbol": "AAA", - "rank": 3679, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ebs-ebisu-network", - "name": "Ebisu Network", - "symbol": "EBS", - "rank": 3680, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gem-nftmall-gem", - "name": "NFTmall (GEM)", - "symbol": "GEM", - "rank": 3681, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aumi-automatic-network", - "name": "AutoMatic Network", - "symbol": "AUMI", - "rank": 3682, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mew-mew-inu", - "name": "Mew Inu", - "symbol": "MEW", - "rank": 3683, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftl-nftlaunch", - "name": "NFTLaunch", - "symbol": "NFTL", - "rank": 3684, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spon-sponsee", - "name": "Sponsee", - "symbol": "SPON", - "rank": 3685, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mst-idle-mystic", - "name": "Idle Mystic", - "symbol": "MST", - "rank": 3686, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blt-bitlorrent", - "name": "Bitlorrent", - "symbol": "BLT", - "rank": 3687, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "horny-horny-doge", - "name": "Horny Doge", - "symbol": "HORNY", - "rank": 3688, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fft-futura-finance", - "name": "Futura Finance", - "symbol": "FFT", - "rank": 3689, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nmbtc-nanometer-bitcoin", - "name": "NanoMeter Bitcoin", - "symbol": "NMBTC", - "rank": 3690, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "czf-czfarm", - "name": "CZFarm", - "symbol": "CZF", - "rank": 3691, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scy-scary-games", - "name": "Scary Games", - "symbol": "SCY", - "rank": 3692, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mazuma-mazuma-token", - "name": "MAZUMA TOKEN", - "symbol": "MAZUMA", - "rank": 3693, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pass-passport-finance", - "name": "Passport Finance", - "symbol": "PASS", - "rank": 3694, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grey-grey-token", - "name": "Grey Token", - "symbol": "GREY", - "rank": 3695, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anft-artwork-nft", - "name": "Artwork NFT", - "symbol": "ANFT", - "rank": 3696, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xusd-xusd-token", - "name": "xUSD Token", - "symbol": "XUSD", - "rank": 3697, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spy-spywolf", - "name": "Spywolf", - "symbol": "$SPY", - "rank": 3698, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbu-banque-universal", - "name": "Banque Universal", - "symbol": "CBU", - "rank": 3699, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "son-sonofshib", - "name": "SONofSHIB", - "symbol": "SON", - "rank": 3700, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mlr-mega-lottery-services-global", - "name": "Mega Lottery Services Global", - "symbol": "MLR", - "rank": 3701, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "biswap-biswap-token", - "name": "Biswap Token", - "symbol": "BISWAP", - "rank": 3702, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moo-moomonster", - "name": "MooMonster", - "symbol": "MOO", - "rank": 3703, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tetu-tetu-reward-token", - "name": "TETU Reward Token", - "symbol": "TETU", - "rank": 3704, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sla-superlaunch", - "name": "SUPERLAUNCH", - "symbol": "SLA", - "rank": 3705, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amzn-amazon-com-inc", - "name": "Amazon.com Inc", - "symbol": "AMZN", - "rank": 3706, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hypersonic-hypersonic-finance", - "name": "Hypersonic Finance", - "symbol": "HYPERSONIC", - "rank": 3707, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arc-arcadenetwork", - "name": "ArcadeNetwork", - "symbol": "ARC", - "rank": 3708, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rice-daosquare", - "name": "DAOSquare", - "symbol": "RICE", - "rank": 3709, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bst-bitsten-token", - "name": "Bitsten Token", - "symbol": "BST", - "rank": 3710, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ivg-ivogel", - "name": "IVOGEL", - "symbol": "IVG", - "rank": 3711, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metacat-metacat", - "name": "MetaCat", - "symbol": "METACAT", - "rank": 3712, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bridge-cross-chain-bridge-token", - "name": "Cross-Chain Bridge Token", - "symbol": "BRIDGE", - "rank": 3713, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chn-chain", - "name": "Chain", - "symbol": "CHN", - "rank": 3714, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xmark-xmark", - "name": "xMARK", - "symbol": "XMARK", - "rank": 3715, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dnd-dungeonswap", - "name": "DungeonSwap", - "symbol": "DND", - "rank": 3716, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bonfire-bonfire", - "name": "Bonfire", - "symbol": "BONFIRE", - "rank": 3717, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "info-infomatix", - "name": "Infomatix", - "symbol": "INFO", - "rank": 3718, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rsun-risingsun", - "name": "RisingSun", - "symbol": "RSUN", - "rank": 3719, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sinu-strong-inu", - "name": "Strong Inu", - "symbol": "SINU", - "rank": 3720, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nift-niftify", - "name": "Niftify", - "symbol": "NIFT", - "rank": 3721, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pred-predictcoin", - "name": "Predictcoin", - "symbol": "PRED", - "rank": 3722, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nxl-next-level", - "name": "Next Level", - "symbol": "NXL", - "rank": 3723, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wntr-weentar", - "name": "Weentar", - "symbol": "WNTR", - "rank": 3724, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ramen-ramenswap", - "name": "RamenSwap", - "symbol": "RAMEN", - "rank": 3725, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scorpfin-scorpion-finance", - "name": "Scorpion Finance", - "symbol": "SCORPFIN", - "rank": 3726, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eu50-euro-stoxx-50-token", - "name": "Euro Stoxx 50 Token", - "symbol": "EU50", - "rank": 3727, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emb-overline-emblem", - "name": "Overline Emblem", - "symbol": "EMB", - "rank": 3728, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idk-idk", - "name": "IDK", - "symbol": "IDK", - "rank": 3729, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rswp-rocketswap-token", - "name": "Rocketswap Token", - "symbol": "RSWP", - "rank": 3730, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yye-yye-energy", - "name": "YYE Energy", - "symbol": "YYE", - "rank": 3731, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xircus-xircus", - "name": "Xircus", - "symbol": "XIRCUS", - "rank": 3732, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pulse-pulse-token", - "name": "Pulse Token", - "symbol": "PULSE", - "rank": 3733, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trnd-trendering", - "name": "Trendering", - "symbol": "TRND", - "rank": 3734, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "afx-afrix", - "name": "Afrix", - "symbol": "AFX", - "rank": 3735, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tcr-tracer-dao", - "name": "Tracer DAO", - "symbol": "TCR", - "rank": 3736, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tad-tadpole", - "name": "Tadpole", - "symbol": "TAD", - "rank": 3737, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "satoz-satozhi", - "name": "Satozhi", - "symbol": "SATOZ", - "rank": 3738, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zedxion-zedxion", - "name": "ZEDXION", - "symbol": "ZEDXION", - "rank": 3739, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "con-conshiptrack", - "name": "Conshiptrack", - "symbol": "CON", - "rank": 3740, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "joys-joys", - "name": "JOYS", - "symbol": "JOYS", - "rank": 3741, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "orbit-orbit-token", - "name": "Orbit Token", - "symbol": "ORBIT", - "rank": 3742, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dp-dragon-pool", - "name": "Dragon Pool", - "symbol": "DP", - "rank": 3743, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sge-society-of-galactic-exploration", - "name": "Society of Galactic Exploration", - "symbol": "SGE", - "rank": 3744, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kclp-kross-chain-launchpad", - "name": "Kross Chain Launchpad", - "symbol": "KCLP", - "rank": 3745, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pxlc-pixl-coin", - "name": "Pixl Coin", - "symbol": "PXLC", - "rank": 3746, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elonone-astroelon", - "name": "AstroElon", - "symbol": "ELONONE", - "rank": 3747, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dotx-defi-of-thrones", - "name": "DeFi of Thrones", - "symbol": "DOTX", - "rank": 3748, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cp-copuppy", - "name": "CoPuppy", - "symbol": "CP", - "rank": 3749, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dios-dios-finance", - "name": "Dios Finance", - "symbol": "DIOS", - "rank": 3750, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swin-swincoin", - "name": "Swincoin", - "symbol": "SWIN", - "rank": 3751, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ylfi-yearn-loans-finance", - "name": "Yearn Loans Finance", - "symbol": "YLFI", - "rank": 3752, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dvx-drivenx", - "name": "DRIVENx", - "symbol": "DVX", - "rank": 3753, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aset-parasset", - "name": "Parasset", - "symbol": "ASET", - "rank": 3754, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "slr-salary", - "name": "Salary", - "symbol": "SLR", - "rank": 3755, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eft-eternalflow", - "name": "EternalFlow", - "symbol": "EFT", - "rank": 3756, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jam-tunefm", - "name": "Tune.Fm", - "symbol": "JAM", - "rank": 3757, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "shak-shakita-inu", - "name": "Shakita Inu", - "symbol": "SHAK", - "rank": 3758, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "symbull-symbull", - "name": "SymBULL", - "symbol": "SYMBULL", - "rank": 3759, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "affinity-safeaffinity", - "name": "SafeAffinity", - "symbol": "AFFINITY", - "rank": 3760, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdfi-stingdefi", - "name": "StingDefi", - "symbol": "SDFI", - "rank": 3761, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tbcc-tbcc", - "name": "TBCC", - "symbol": "TBCC", - "rank": 3762, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "navi-navibration", - "name": "Navibration", - "symbol": "NAVI", - "rank": 3763, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mnstp-moon-stop", - "name": "Moon Stop", - "symbol": "MNSTP", - "rank": 3764, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brand-brandpad-finance", - "name": "BrandPad Finance", - "symbol": "BRAND", - "rank": 3765, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kaizilla-kaizilla", - "name": "Kaizilla", - "symbol": "KAIZILLA", - "rank": 3766, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ethix-ethichub", - "name": "EthicHub", - "symbol": "ETHIX", - "rank": 3767, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ape-x-ape-x", - "name": "Ape-X", - "symbol": "APE-X", - "rank": 3768, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goose-goose-coin", - "name": "Goose Coin", - "symbol": "GOOSE", - "rank": 3769, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vkt-vankia-chain", - "name": "Vankia Chain", - "symbol": "VKT", - "rank": 3770, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kim-king-money", - "name": "King Money", - "symbol": "KIM", - "rank": 3771, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rbx-rbx-token", - "name": "RBX Token", - "symbol": "RBX", - "rank": 3772, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dotc-dotcpro-token", - "name": "dotc.pro token", - "symbol": "DOTC", - "rank": 3773, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goma-goma-finance", - "name": "GOMA Finance", - "symbol": "GOMA", - "rank": 3774, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sklay-sklay", - "name": "sKLAY", - "symbol": "SKLAY", - "rank": 3775, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "452b-kepler452b", - "name": "Kepler452b", - "symbol": "452B", - "rank": 3776, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "altb-altbase", - "name": "Altbase", - "symbol": "ALTB", - "rank": 3777, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kiro-kirobo", - "name": "Kirobo", - "symbol": "KIRO", - "rank": 3778, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsw-trumpswap", - "name": "Trumpswap", - "symbol": "TSW", - "rank": 3779, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yon-yesorno", - "name": "YESorNO", - "symbol": "YON", - "rank": 3780, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lat-liquidify", - "name": "Liquidify", - "symbol": "LAT", - "rank": 3781, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ups-upfi-network", - "name": "UPFI Network", - "symbol": "UPS", - "rank": 3782, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lnr-lunar", - "name": "Lunar", - "symbol": "LNR", - "rank": 3783, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pkd-petkingdom", - "name": "PetKingdom", - "symbol": "PKD", - "rank": 3784, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lvlup-levelup-gaming", - "name": "LevelUp Gaming", - "symbol": "LVLUP", - "rank": 3785, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vision-apyvision", - "name": "APY.vision", - "symbol": "VISION", - "rank": 3786, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "optcm-optimus", - "name": "Optimus", - "symbol": "OPTCM", - "rank": 3787, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ra-raido", - "name": "RAIDO", - "symbol": "RA", - "rank": 3788, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "it-infinity", - "name": "Infinity", - "symbol": "IT", - "rank": 3789, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ones-oneswap-dao-token", - "name": "OneSwap DAO Token", - "symbol": "ONES", - "rank": 3790, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elyx-elynet-token", - "name": "Elynet Token", - "symbol": "ELYX", - "rank": 3791, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bgc-be-gaming-coin", - "name": "Be Gaming Coin", - "symbol": "BGC", - "rank": 3792, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ogzd-gazprom-pjsc-adr", - "name": "Gazprom PJSC ADR", - "symbol": "OGZD", - "rank": 3793, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mona-monavale", - "name": "Monavale", - "symbol": "MONA", - "rank": 3794, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftg-nft-global-platform", - "name": "NFT Global Platform", - "symbol": "NFTG", - "rank": 3795, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bscb-bscbond", - "name": "BscBond", - "symbol": "BSCB", - "rank": 3796, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moz-mozik", - "name": "Mozik", - "symbol": "MOZ", - "rank": 3797, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bshib-baby-shiba-inu", - "name": "Baby Shiba Inu", - "symbol": "BSHIB", - "rank": 3798, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mfi-marginswap", - "name": "Marginswap", - "symbol": "MFI", - "rank": 3799, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aapx-ampnet", - "name": "AMPnet", - "symbol": "AAPX", - "rank": 3800, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gb-good-bridging", - "name": "Good Bridging", - "symbol": "GB", - "rank": 3801, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wec-web-coin-pay", - "name": "Web Coin Pay", - "symbol": "WEC", - "rank": 3802, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "brt-born-to-race", - "name": "Born To Race", - "symbol": "BRT", - "rank": 3803, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wotg-war-of-tribes", - "name": "War of Tribes", - "symbol": "WOTG", - "rank": 3804, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bifi-bifi", - "name": "BiFi", - "symbol": "BIFI", - "rank": 3805, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yve-crvdao-vecrv-dao-yvault", - "name": "veCRV-DAO yVault", - "symbol": "YVE-CRVDAO", - "rank": 3806, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kenshi-kenshi", - "name": "Kenshi", - "symbol": "KENSHI", - "rank": 3807, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stay-stay-token", - "name": "STAY TOKEN", - "symbol": "STAY", - "rank": 3808, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "panft-picartnft", - "name": "PicArtNFT", - "symbol": "PANFT", - "rank": 3809, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scb-spacecowboy", - "name": "SpaceCowBoy", - "symbol": "SCB", - "rank": 3810, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spice-spice", - "name": "Spice", - "symbol": "SPICE", - "rank": 3811, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "igirl-island-girl", - "name": "Island Girl", - "symbol": "IGIRL", - "rank": 3812, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dart-dart-inu", - "name": "Dart Inu", - "symbol": "DART", - "rank": 3813, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "digi-digible", - "name": "Digible", - "symbol": "DIGI", - "rank": 3814, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ehash-ehash", - "name": "EHash", - "symbol": "EHASH", - "rank": 3815, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pla-playchip", - "name": "PlayChip", - "symbol": "PLA", - "rank": 3816, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "c4g3-cage", - "name": "Cage", - "symbol": "C4G3", - "rank": 3817, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pmd-promodio", - "name": "Promodio", - "symbol": "PMD", - "rank": 3818, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ebox-ethbox-token", - "name": "Ethbox Token", - "symbol": "EBOX", - "rank": 3819, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nubtc-unvaxxed-sperm", - "name": "Unvaxxed Sperm", - "symbol": "nuBTC", - "rank": 3820, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "island-island-doges", - "name": "Island Doges", - "symbol": "ISLAND", - "rank": 3821, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "door-door", - "name": "DOOR", - "symbol": "DOOR", - "rank": 3822, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "light-light-defi", - "name": "Light DeFi", - "symbol": "LIGHT", - "rank": 3823, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "d2d-prime", - "name": "Prime", - "symbol": "D2D", - "rank": 3824, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "petg-pet-games", - "name": "Pet Games", - "symbol": "PETG", - "rank": 3825, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsct-transient", - "name": "Transient", - "symbol": "TSCT", - "rank": 3826, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xeta-xeta-reality", - "name": "Xeta Reality", - "symbol": "XETA", - "rank": 3827, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hbarp-hbarpad", - "name": "HbarPad", - "symbol": "HBARP", - "rank": 3828, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdl-meta-decentraland", - "name": "Meta Decentraland", - "symbol": "MDL", - "rank": 3829, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecchi-ecchi-coin", - "name": "Ecchi Coin", - "symbol": "ECCHI", - "rank": 3830, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gotem-gotem", - "name": "gotEM", - "symbol": "GOTEM", - "rank": 3831, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clu-clucoin", - "name": "CluCoin", - "symbol": "CLU", - "rank": 3832, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmp-moonpoly", - "name": "Moonpoly", - "symbol": "CMP", - "rank": 3833, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "h2o-ifoswap-token", - "name": "IFOSwap Token", - "symbol": "H2O", - "rank": 3834, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trise-trustrise", - "name": "TrustRise", - "symbol": "TRISE", - "rank": 3835, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rlb-relbit", - "name": "Relbit", - "symbol": "RLB", - "rank": 3836, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dch-doge-cash", - "name": "Doge Cash", - "symbol": "DCH", - "rank": 3837, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssv-ssv-network", - "name": "SSV Network", - "symbol": "SSV", - "rank": 3838, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfmon-nfmonsters", - "name": "NFMonsters", - "symbol": "NFMON", - "rank": 3839, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jkt-jokermanor-metaverse", - "name": "JokerManor Metaverse", - "symbol": "JKT", - "rank": 3840, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogz-dogz", - "name": "Dogz", - "symbol": "DOGZ", - "rank": 3841, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fmg-fm-gallery", - "name": "FM Gallery", - "symbol": "FMG", - "rank": 3842, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pta-la-peseta", - "name": "La Peseta", - "symbol": "PTA", - "rank": 3843, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rwn-rowan-coin", - "name": "Rowan Coin", - "symbol": "RWN", - "rank": 3844, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "solab-solabrador", - "name": "Solabrador", - "symbol": "SOLAB", - "rank": 3845, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daf-diamonds-are-forever", - "name": "Diamonds are Forever", - "symbol": "DAF", - "rank": 3846, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coon-maine-coon", - "name": "Maine Coon", - "symbol": "COON", - "rank": 3847, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dvx-derivex", - "name": "Derivex", - "symbol": "DVX", - "rank": 3848, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wbx-wibx", - "name": "WiBX", - "symbol": "WBX", - "rank": 3849, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpos-cpos-cloud-payment", - "name": "CPOS Cloud Payment", - "symbol": "CPOS", - "rank": 3850, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctx-cryptex", - "name": "Cryptex", - "symbol": "CTX", - "rank": 3851, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "floki-shiba-floki", - "name": "Shiba Floki", - "symbol": "FLOKI", - "rank": 3852, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "treat-treatdao", - "name": "TreatDAO", - "symbol": "TREAT", - "rank": 3853, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibelon-shibelon", - "name": "ShibElon", - "symbol": "SHIBELON", - "rank": 3854, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eye-eye", - "name": "EYE", - "symbol": "EYE", - "rank": 3855, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vice-vicewrld", - "name": "Vicewrld", - "symbol": "VICE", - "rank": 3856, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "luca-luca", - "name": "LUCA", - "symbol": "LUCA", - "rank": 3857, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minidoge-minidoge", - "name": "MiniDOGE", - "symbol": "MINIDOGE", - "rank": 3858, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ucoin-universal-coin", - "name": "Universal Coin", - "symbol": "UCOIN", - "rank": 3859, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "deva-deva-token", - "name": "DEVA TOKEN", - "symbol": "DEVA", - "rank": 3860, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bfk-bfk-warzone", - "name": "BFK WARZONE", - "symbol": "BFK", - "rank": 3861, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tako-tako-token", - "name": "Tako Token", - "symbol": "TAKO", - "rank": 3862, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slm-solomon-defi", - "name": "Solomon Defi", - "symbol": "SLM", - "rank": 3863, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfnd-dfund", - "name": "dFund", - "symbol": "DFND", - "rank": 3864, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "std-stadium", - "name": "Stadium", - "symbol": "STD", - "rank": 3865, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wel-welnance-finance", - "name": "Welnance Finance", - "symbol": "WEL", - "rank": 3866, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aapl-apple-inc", - "name": "Apple Inc", - "symbol": "AAPL", - "rank": 3867, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "versus-versus-farm", - "name": "Versus Farm", - "symbol": "VERSUS", - "rank": 3868, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fur-pagan-gods-fur-token", - "name": "Pagan Gods Fur token", - "symbol": "FUR", - "rank": 3869, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nrgy-nrgy-defi", - "name": "NRGY Defi", - "symbol": "NRGY", - "rank": 3870, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prp-perpetuum-coin", - "name": "Perpetuum Coin", - "symbol": "PRP", - "rank": 3871, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xin-mixin", - "name": "Mixin", - "symbol": "XIN", - "rank": 3872, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "maid-maidcoin", - "name": "MaidCoin", - "symbol": "$MAID", - "rank": 3873, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nvir-nvirworld", - "name": "NvirWorld", - "symbol": "NVIR", - "rank": 3874, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "greed-greed", - "name": "Greed", - "symbol": "$GREED", - "rank": 3875, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nmt-nftmart-token", - "name": "NFTMart Token", - "symbol": "NMT", - "rank": 3876, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gat-game-ace-token", - "name": "Game Ace Token", - "symbol": "GAT", - "rank": 3877, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gom-gomics", - "name": "Gomics", - "symbol": "GOM", - "rank": 3878, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "budg-bulldogswap", - "name": "BullDogSwap", - "symbol": "BUDG", - "rank": 3879, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "olive-olive-cash", - "name": "Olive Cash", - "symbol": "OLIVE", - "rank": 3880, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krause-krause", - "name": "KRAUSE", - "symbol": "$KRAUSE", - "rank": 3881, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gm-we-love-gm", - "name": "we love gm", - "symbol": "GM", - "rank": 3882, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xdoge-classicdoge", - "name": "ClassicDoge", - "symbol": "XDOGE", - "rank": 3883, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tc-ttcoin", - "name": "TTCOIN", - "symbol": "TC", - "rank": 3884, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boss-boss", - "name": "Boss", - "symbol": "BOSS", - "rank": 3885, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dph-digipharm", - "name": "Digipharm", - "symbol": "DPH", - "rank": 3886, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mooi-moona", - "name": "Moonaï", - "symbol": "MOOI", - "rank": 3887, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "whe-worthwhile", - "name": "Worthwhile", - "symbol": "WHE", - "rank": 3888, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbl-notable", - "name": "Notable", - "symbol": "NBL", - "rank": 3889, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ths-the-hash-speed", - "name": "The Hash Speed", - "symbol": "THS", - "rank": 3890, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pton-peloton-interactive-inc", - "name": "Peloton Interactive Inc", - "symbol": "PTON", - "rank": 3891, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bfc-bit-financial", - "name": "Bit Financial", - "symbol": "BFC", - "rank": 3892, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "greyhound-greyhound", - "name": "Greyhound", - "symbol": "GREYHOUND", - "rank": 3893, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "argo-argo-token", - "name": "ArGo Token", - "symbol": "ARGO", - "rank": 3894, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bfly-butterfly-protocol", - "name": "Butterfly Protocol", - "symbol": "BFLY", - "rank": 3895, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acsi-acryptosi", - "name": "ACryptoSI", - "symbol": "ACSI", - "rank": 3896, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scv-scvtoken", - "name": "SCVToken", - "symbol": "SCV", - "rank": 3897, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccm-car-coin", - "name": "Car Coin", - "symbol": "CCM", - "rank": 3898, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cox-coxswap", - "name": "Coxswap", - "symbol": "COX", - "rank": 3899, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plut-plutos-network", - "name": "Plutos Network", - "symbol": "PLUT", - "rank": 3900, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kaiken-kaikeninu", - "name": "KaikenInu", - "symbol": "KAIKEN", - "rank": 3901, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ylb-yearnlab", - "name": "Yearnlab", - "symbol": "YLB", - "rank": 3902, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krom-kromatikafinance", - "name": "Kromatika.Finance", - "symbol": "KROM", - "rank": 3903, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "verse-verse", - "name": "Verse", - "symbol": "VERSE", - "rank": 3904, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mara-marathon-patent-group-inc", - "name": "Marathon Patent Group, Inc.", - "symbol": "MARA", - "rank": 3905, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dex-dexira", - "name": "dexIRA", - "symbol": "DEX", - "rank": 3906, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mech-mech-master", - "name": "Mech Master", - "symbol": "MECH", - "rank": 3907, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dsc-defisportscoin", - "name": "DefiSportsCoin", - "symbol": "DSC", - "rank": 3908, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polar-polar-sync", - "name": "Polar Sync", - "symbol": "POLAR", - "rank": 3909, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oxo-oxofarm", - "name": "OXO.Farm", - "symbol": "OXO", - "rank": 3910, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "master-master-floki", - "name": "Master Floki", - "symbol": "MASTER", - "rank": 3911, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "green-green-token", - "name": "Green Token", - "symbol": "GREEN", - "rank": 3912, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "heart-heart2", - "name": "HEART", - "symbol": "HEART", - "rank": 3913, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dxb-dexzbitz", - "name": "Dexzbitz", - "symbol": "DXB", - "rank": 3914, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbtc-super-bitcoin", - "name": "Super Bitcoin", - "symbol": "SBTC", - "rank": 3915, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "leag-leaguedao-governance-token", - "name": "LeagueDAO Governance Token", - "symbol": "LEAG", - "rank": 3916, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xl-xolo-inu", - "name": "Xolo Inu", - "symbol": "XL", - "rank": 3917, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tipsy-tipsy", - "name": "Tipsy", - "symbol": "TIPSY", - "rank": 3918, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ffa-cryptofifa", - "name": "Cryptofifa", - "symbol": "FFA", - "rank": 3919, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "defido-defido", - "name": "DeFido", - "symbol": "DEFIDO", - "rank": 3920, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "momo-momo-inc", - "name": "Momo Inc.", - "symbol": "MOMO", - "rank": 3921, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogggo-dogggo", - "name": "DOGGGO", - "symbol": "DOGGGO", - "rank": 3922, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ndefi-polly-defi-nest", - "name": "Polly DeFi Nest", - "symbol": "NDEFI", - "rank": 3923, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smx-solarminex", - "name": "SolarMineX", - "symbol": "SMX", - "rank": 3924, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flokipad-flokipad", - "name": "FlokiPad", - "symbol": "FLOKIPAD", - "rank": 3925, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chat-beechat", - "name": "BeeChat", - "symbol": "CHAT", - "rank": 3926, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gem-gem-token", - "name": "Gem Token", - "symbol": "GEM", - "rank": 3927, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grap-grap-finance", - "name": "Grap Finance", - "symbol": "GRAP", - "rank": 3928, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "maga-maga-coin", - "name": "MAGA coin", - "symbol": "MAGA", - "rank": 3929, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smnc-simple-masternode-coin", - "name": "Simple Masternode Coin", - "symbol": "SMNC", - "rank": 3930, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pinkpanda-pink-panda", - "name": "Pink Panda", - "symbol": "PINKPANDA", - "rank": 3931, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alt-alt-coin", - "name": "Alt Coin", - "symbol": "ALT", - "rank": 3932, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msft-microsoft", - "name": "Microsoft", - "symbol": "MSFT", - "rank": 3933, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coinscope-coinscope", - "name": "Coinscope", - "symbol": "COINSCOPE", - "rank": 3934, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ebay-ebay", - "name": "eBay", - "symbol": "EBAY", - "rank": 3935, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "waifer-waifer", - "name": "Waifer", - "symbol": "WAIFER", - "rank": 3936, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rnl-rentalchain", - "name": "RentalChain", - "symbol": "RNL", - "rank": 3937, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vida-vidiachange", - "name": "Vidiachange", - "symbol": "VIDA", - "rank": 3938, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ths-theoscoin", - "name": "Theoscoin", - "symbol": "THS", - "rank": 3939, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pigx-pigx", - "name": "PIGX", - "symbol": "PIGX", - "rank": 3940, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hanu-hanu-yokia", - "name": "Hanu Yokia", - "symbol": "HANU", - "rank": 3941, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "doca-doge-raca", - "name": "Doge Raca", - "symbol": "DOCA", - "rank": 3942, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfx-subx-finance", - "name": "SUBX Finance", - "symbol": "SFX", - "rank": 3943, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "glms-glimpse", - "name": "Glimpse", - "symbol": "GLMS", - "rank": 3944, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uv-unityventures", - "name": "UnityVentures", - "symbol": "UV", - "rank": 3945, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bt-bitenium-token", - "name": "Bitenium Token", - "symbol": "BT", - "rank": 3946, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yoco-yocoin", - "name": "YoCoin", - "symbol": "YOCO", - "rank": 3947, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unft-ultimate-nft", - "name": "Ultimate Nft", - "symbol": "UNFT", - "rank": 3948, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ru-rifi-united", - "name": "RIFI United", - "symbol": "RU", - "rank": 3949, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dsg-d-skyscraper", - "name": "D-Skyscraper", - "symbol": "DSG", - "rank": 3950, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sphri-spherium", - "name": "Spherium", - "symbol": "SPHRI", - "rank": 3951, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wit-witnet", - "name": "Witnet", - "symbol": "WIT", - "rank": 3952, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tab-tabank", - "name": "Tabank", - "symbol": "TAB", - "rank": 3953, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xolo-xolo-metaverse", - "name": "Xolo Metaverse", - "symbol": "XOLO", - "rank": 3954, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hps-happiness-token", - "name": "Happiness Token", - "symbol": "HPS", - "rank": 3955, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mto-merchant-token", - "name": "Merchant Token", - "symbol": "MTO", - "rank": 3956, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "regu-regularpresale", - "name": "RegularPresale", - "symbol": "REGU", - "rank": 3957, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chs-chainsquare", - "name": "Chainsquare", - "symbol": "CHS", - "rank": 3958, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alphr-alphr", - "name": "Alphr", - "symbol": "ALPHR", - "rank": 3959, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fid-fidira", - "name": "Fidira", - "symbol": "FID", - "rank": 3960, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "taste-tastenft", - "name": "TasteNFT", - "symbol": "TASTE", - "rank": 3961, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gal-gallant", - "name": "Gallant", - "symbol": "GAL", - "rank": 3962, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fman-florida-man", - "name": "Florida Man", - "symbol": "FMAN", - "rank": 3963, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anatha-anatha", - "name": "ANATHA", - "symbol": "ANATHA", - "rank": 3964, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "afro-afrostar", - "name": "Afrostar", - "symbol": "AFRO", - "rank": 3965, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "4jnet-4jnet", - "name": "4JNET", - "symbol": "4JNET", - "rank": 3966, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "witcher-geralt-coin", - "name": "Geralt Coin", - "symbol": "WITCHER", - "rank": 3967, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "fren-fren", - "name": "FREN", - "symbol": "FREN", - "rank": 3968, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adaboy-adaboy", - "name": "ADABoy", - "symbol": "ADABOY", - "rank": 3969, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "epam-epam-systems-inc", - "name": "EPAM Systems, Inc.", - "symbol": "EPAM", - "rank": 3970, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xi-xi-token", - "name": "Xi Token", - "symbol": "XI", - "rank": 3971, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "theos-theos", - "name": "Theos", - "symbol": "THEOS", - "rank": 3972, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rc-reward-cycle", - "name": "Reward Cycle", - "symbol": "RC", - "rank": 3973, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msn-maison-capital", - "name": "Maison Capital", - "symbol": "MSN", - "rank": 3974, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbo-roboots", - "name": "Roboots", - "symbol": "RBO", - "rank": 3975, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "erp-entropyfi", - "name": "Entropyfi", - "symbol": "ERP", - "rank": 3976, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aqua-aqua", - "name": "AQUA", - "symbol": "AQUA", - "rank": 3977, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pixelsquid-pixelsquid", - "name": "PixelSQUID", - "symbol": "PIXELSQUID", - "rank": 3978, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kabosu-kabosu", - "name": "Kabosu", - "symbol": "KABOSU", - "rank": 3979, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vics-robofi-token", - "name": "RoboFi Token", - "symbol": "VICS", - "rank": 3980, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moonarch-moonarch", - "name": "Moonarch", - "symbol": "MOONARCH", - "rank": 3981, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "happy-happyfans", - "name": "HappyFans", - "symbol": "HAPPY", - "rank": 3982, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbs-robiniaswap-token", - "name": "RobiniaSwap Token", - "symbol": "RBS", - "rank": 3983, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hifi-hifi-gaming-society", - "name": "HiFi Gaming Society", - "symbol": "HIFI", - "rank": 3984, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kccm-kcc-memepad", - "name": "KCC MemePad", - "symbol": "KCCM", - "rank": 3985, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rasta-rasta-finance", - "name": "Rasta Finance", - "symbol": "RASTA", - "rank": 3986, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bukh-bukhi", - "name": "bUKHI", - "symbol": "BUKH", - "rank": 3987, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xld-xcel-swap", - "name": "Xcel Swap", - "symbol": "XLD", - "rank": 3988, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "b2p-block2play", - "name": "Block2Play", - "symbol": "B2P", - "rank": 3989, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "checoin-checoin", - "name": "CheCoin", - "symbol": "CHECOIN", - "rank": 3990, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gofx-goosefx", - "name": "GooseFX", - "symbol": "GOFX", - "rank": 3991, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daddydoge-daddy-doge", - "name": "Daddy Doge", - "symbol": "DADDYDOGE", - "rank": 3992, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmv-gameverse", - "name": "GameVerse", - "symbol": "GMV", - "rank": 3993, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spark-sparklab", - "name": "SparkLab", - "symbol": "SPARK", - "rank": 3994, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtcl-maticlaunch", - "name": "MaticLaunch", - "symbol": "MTCL", - "rank": 3995, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sct-safechaintoken", - "name": "Safechaintoken", - "symbol": "SCT", - "rank": 3996, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bln-blacknet", - "name": "Blacknet", - "symbol": "BLN", - "rank": 3997, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "delta-delta-financial", - "name": "Delta Financial", - "symbol": "DELTA", - "rank": 3998, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "invest-investdex", - "name": "InvestDex", - "symbol": "INVEST", - "rank": 3999, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cwap-defire", - "name": "DeFIRE", - "symbol": "CWAP", - "rank": 4000, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mil-military-finance", - "name": "Military Finance", - "symbol": "MIL", - "rank": 4001, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdoge-soldoge", - "name": "SolDoge", - "symbol": "SDOGE", - "rank": 4002, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abco-autobitco-token", - "name": "AutoBitco Token", - "symbol": "ABCO", - "rank": 4003, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lkt-locklet", - "name": "Locklet", - "symbol": "LKT", - "rank": 4004, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tractor-tractor-joe", - "name": "Tractor Joe", - "symbol": "TRACTOR", - "rank": 4005, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pearl-crypto-bay", - "name": "Crypto Bay", - "symbol": "PEARL", - "rank": 4006, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solfi-solfina", - "name": "Solfina", - "symbol": "SOLFI", - "rank": 4007, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coin-coin", - "name": "coin", - "symbol": "COIN", - "rank": 4008, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dough-dough", - "name": "Dough", - "symbol": "DOUGH", - "rank": 4009, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apidai-apidai-network", - "name": "Apidai Network", - "symbol": "APIDAI", - "rank": 4010, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abs-autobusd", - "name": "Autobusd", - "symbol": "ABS", - "rank": 4011, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eld-eldorado", - "name": "El.Dorado", - "symbol": "ELD", - "rank": 4012, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atc-aster", - "name": "Aster", - "symbol": "ATC", - "rank": 4013, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "whale-whale-fall", - "name": "Whale Fall", - "symbol": "WHALE", - "rank": 4014, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lqr-laqira-protocol", - "name": "Laqira Protocol", - "symbol": "LQR", - "rank": 4015, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flux-flux-protocol", - "name": "Flux Protocol", - "symbol": "FLUX", - "rank": 4016, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rexc-rxcgames", - "name": "RXCGames", - "symbol": "REXC", - "rank": 4017, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sta-stable-asset", - "name": "STABLE ASSET", - "symbol": "STA", - "rank": 4018, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "npxs-pundi-x", - "name": "Pundi X", - "symbol": "NPXS", - "rank": 4019, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krl-kryptolite", - "name": "KRYPTOLITE", - "symbol": "KRL", - "rank": 4020, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ore-outrace", - "name": "Outrace", - "symbol": "ORE", - "rank": 4021, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "catz-catzcoin", - "name": "CatzCoin", - "symbol": "CATZ", - "rank": 4022, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bifi-bitcoin-file", - "name": "Bitcoin File", - "symbol": "BIFI", - "rank": 4023, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "utc-unitech", - "name": "Unitech", - "symbol": "UTC", - "rank": 4024, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wnow-walletnow", - "name": "WalletNow", - "symbol": "WNOW", - "rank": 4025, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtix-matrix", - "name": "MATRIX", - "symbol": "MTIX", - "rank": 4026, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xm-xmooney", - "name": "xMooney", - "symbol": "XM", - "rank": 4027, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dkc-dukecoin", - "name": "Dukecoin", - "symbol": "DKC", - "rank": 4028, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "saitama-saitama-inu", - "name": "Saitama Inu", - "symbol": "SAITAMA", - "rank": 4029, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nia-nydronia", - "name": "Nydronia", - "symbol": "NIA", - "rank": 4030, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etos-eternal-oasis", - "name": "Eternal Oasis", - "symbol": "ETOS", - "rank": 4031, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "neu-neumark", - "name": "Neumark", - "symbol": "NEU", - "rank": 4032, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lyd-lydia-finance-token", - "name": "Lydia Finance Token", - "symbol": "LYD", - "rank": 4033, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ppoll-pancakepoll", - "name": "PancakePoll", - "symbol": "PPOLL", - "rank": 4034, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shg-shiba-generating", - "name": "Shiba Generating", - "symbol": "SHG", - "rank": 4035, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "buffs-buffswap", - "name": "BuffSwap", - "symbol": "BUFFS", - "rank": 4036, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atmssft-atmosphere-ccg", - "name": "Atmosphere CCG", - "symbol": "ATMSSFT", - "rank": 4037, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wagmi-wagmi-game", - "name": "WAGMI Game", - "symbol": "WAGMI", - "rank": 4038, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "squeeze-squeeze-token", - "name": "Squeeze Token", - "symbol": "SQUEEZE", - "rank": 4039, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ethvault-ethvault", - "name": "ETHVault", - "symbol": "ETHVAULT", - "rank": 4040, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1flr-flare-token", - "name": "Flare Token", - "symbol": "1FLR", - "rank": 4041, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dxt-dexit", - "name": "DEXIT", - "symbol": "DXT", - "rank": 4042, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yok-yokcoin", - "name": "YOKcoin", - "symbol": "YOK", - "rank": 4043, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icons-sportsicon", - "name": "SportsIcon", - "symbol": "$ICONS", - "rank": 4044, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uvxy-proshares-ultra-vix-short-term-futures-etf", - "name": "ProShares Ultra VIX Short-Term Futures ETF", - "symbol": "UVXY", - "rank": 4045, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qm-quick-mining", - "name": "Quick Mining", - "symbol": "QM", - "rank": 4046, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "foin-foin", - "name": "FOIN", - "symbol": "FOIN", - "rank": 4047, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "carat-carat", - "name": "CARAT", - "symbol": "CARAT", - "rank": 4048, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rare-unique-one", - "name": "Unique One", - "symbol": "RARE", - "rank": 4049, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wsc-wealthsecrets", - "name": "WealthSecrets", - "symbol": "WSC", - "rank": 4050, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "perry-swaperry", - "name": "Swaperry", - "symbol": "PERRY", - "rank": 4051, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ydr-ydragon", - "name": "YDragon", - "symbol": "YDR", - "rank": 4052, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "umbr-umbria-network", - "name": "Umbria Network", - "symbol": "UMBR", - "rank": 4053, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solar-solar", - "name": "Solar", - "symbol": "SOLAR", - "rank": 4054, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sefi-sefi", - "name": "SEFI", - "symbol": "SEFI", - "rank": 4055, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ign-infinity-game-nft", - "name": "Infinity Game NFT", - "symbol": "IGN", - "rank": 4056, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lkk-lykke", - "name": "Lykke", - "symbol": "LKK", - "rank": 4057, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "yay-yayswap", - "name": "YaySwap", - "symbol": "YAY", - "rank": 4058, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnx-greenex", - "name": "Greenex", - "symbol": "GNX", - "rank": 4059, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbt-solbit", - "name": "SOLBIT", - "symbol": "SBT", - "rank": 4060, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "susd-stabilize-usd", - "name": "Stabilize USD", - "symbol": "SUSD", - "rank": 4061, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bidu-baidu-inc", - "name": "Baidu, Inc.", - "symbol": "BIDU", - "rank": 4062, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smbr-sombra", - "name": "Sombra", - "symbol": "SMBR", - "rank": 4063, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zwap-zilswap", - "name": "ZilSwap", - "symbol": "ZWAP", - "rank": 4064, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "svs-silver-gateway", - "name": "Silver Gateway", - "symbol": "SVS", - "rank": 4065, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pxt-polyx", - "name": "POLYX", - "symbol": "PXT", - "rank": 4066, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "satx-satoexchange-token", - "name": "SatoExchange Token", - "symbol": "SATX", - "rank": 4067, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pchf-peachfolio", - "name": "Peachfolio", - "symbol": "PCHF", - "rank": 4068, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "barmy-bscarmy", - "name": "BscArmy", - "symbol": "BARMY", - "rank": 4069, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rat-the-rare-antiquities-token", - "name": "The Rare Antiquities Token", - "symbol": "RAT", - "rank": 4070, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfbeta-yfbeta", - "name": "yfBeta", - "symbol": "YFBETA", - "rank": 4071, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rush-rush-defi", - "name": "Rush DeFi", - "symbol": "RUSH", - "rank": 4072, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hoop-hoopoe", - "name": "Hoopoe", - "symbol": "HOOP", - "rank": 4073, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ort-omni-real-estate-token", - "name": "Omni Real Estate Token", - "symbol": "ORT", - "rank": 4074, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "but-bitup-token", - "name": "BitUP Token", - "symbol": "BUT", - "rank": 4075, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vbk-veriblock", - "name": "VeriBlock", - "symbol": "VBK", - "rank": 4076, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wasabi-wasabix", - "name": "WasabiX", - "symbol": "WASABI", - "rank": 4077, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nexus-nexus-token", - "name": "Nexus Token", - "symbol": "NEXUS", - "rank": 4078, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wliti-wliti", - "name": "wLITI", - "symbol": "WLITI", - "rank": 4079, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gse-gsenetwork", - "name": "GSENetwork", - "symbol": "GSE", - "rank": 4080, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "geo-geo", - "name": "GEO", - "symbol": "GEO", - "rank": 4081, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdza-medooza-ecosystem", - "name": "MEDOOZA Ecosystem", - "symbol": "MDZA", - "rank": 4082, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccake-cheesecakeswap", - "name": "CheesecakeSwap", - "symbol": "CCAKE", - "rank": 4083, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lem-lemur", - "name": "LEMUR", - "symbol": "LEM", - "rank": 4084, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tools-tools", - "name": "TOOLS", - "symbol": "TOOLS", - "rank": 4085, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safu-staysafu", - "name": "StaySAFU", - "symbol": "SAFU", - "rank": 4086, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babycake-baby-cake", - "name": "Baby Cake", - "symbol": "BABYCAKE", - "rank": 4087, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hitx-hithotx", - "name": "Hithotx", - "symbol": "HITX", - "rank": 4088, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pswap-porkswap", - "name": "PorkSwap", - "symbol": "PSWAP", - "rank": 4089, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fomp-fompound", - "name": "Fompound", - "symbol": "FOMP", - "rank": 4090, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mithril-bsc-mithril", - "name": "BSC Mithril", - "symbol": "MITHRIL", - "rank": 4091, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "black-blackhole-protocol", - "name": "BlackHole Protocol", - "symbol": "BLACK", - "rank": 4092, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brown-browniesswap", - "name": "BrowniesSwap", - "symbol": "BROWN", - "rank": 4093, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inu-solana-inu", - "name": "Solana Inu", - "symbol": "INU", - "rank": 4094, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tap-tapme-token", - "name": "TAPME Token", - "symbol": "TAP", - "rank": 4095, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chim-chimeras", - "name": "Chimeras", - "symbol": "CHIM", - "rank": 4096, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tape-toolape", - "name": "ToolApe", - "symbol": "TAPE", - "rank": 4097, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbbt-meebitsdao-pool", - "name": "MeebitsDAO Pool", - "symbol": "MBBT", - "rank": 4098, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "x3s-x3swap", - "name": "X3Swap", - "symbol": "X3S", - "rank": 4099, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogecola-dogecola", - "name": "DogeCola", - "symbol": "DOGECOLA", - "rank": 4100, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1box-1box", - "name": "1BOX", - "symbol": "1BOX", - "rank": 4101, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cotk-colligo-token", - "name": "Colligo Token", - "symbol": "COTK", - "rank": 4102, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogey-doge-yellow-coin", - "name": "Doge Yellow Coin", - "symbol": "DOGEY", - "rank": 4103, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wish-contextlogic-inc", - "name": "ContextLogic Inc", - "symbol": "WISH", - "rank": 4104, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "peth18c-peth18c", - "name": "pETH18C", - "symbol": "PETH18C", - "rank": 4105, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qua-qua-finance", - "name": "QUA Finance", - "symbol": "QUA", - "rank": 4106, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "truth-truth-technology", - "name": "Truth Technology", - "symbol": "TRUTH", - "rank": 4107, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfox-yfox-finance", - "name": "YFOX Finance", - "symbol": "YFOX", - "rank": 4108, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reflecto-reflecto", - "name": "Reflecto", - "symbol": "REFLECTO", - "rank": 4109, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inp-inpoker", - "name": "InPoker", - "symbol": "INP", - "rank": 4110, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eye-media-eye", - "name": "MeDIA eYe", - "symbol": "eYe", - "rank": 4111, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phm-phantom-protocol", - "name": "Phantom Protocol", - "symbol": "PHM", - "rank": 4112, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ziti-ziticoin", - "name": "Ziticoin", - "symbol": "ZITI", - "rank": 4113, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdo-bdollar", - "name": "bDollar", - "symbol": "BDO", - "rank": 4114, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flash-flash-loans", - "name": "Flash Loans", - "symbol": "FLASH", - "rank": 4115, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xblzd-blizzard", - "name": "Blizzard", - "symbol": "XBLZD", - "rank": 4116, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "islainu-island-inu", - "name": "Island Inu", - "symbol": "ISLAINU", - "rank": 4117, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ath-aetherv2", - "name": "AetherV2", - "symbol": "ATH", - "rank": 4118, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "char-charitas", - "name": "Charitas", - "symbol": "CHAR", - "rank": 4119, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "odin-odin-protocol", - "name": "Odin Protocol", - "symbol": "ODIN", - "rank": 4120, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bemt-bem", - "name": "BEM", - "symbol": "BEMT", - "rank": 4121, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coin-coinbase-tokenized-stock", - "name": "Coinbase Tokenized Stock", - "symbol": "COIN", - "rank": 4122, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "airx-aircoins", - "name": "Aircoins", - "symbol": "AIRX", - "rank": 4123, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pstv-perspective-coin", - "name": "Perspective coin", - "symbol": "PSTV", - "rank": 4124, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yetic-yeticoin", - "name": "YetiCoin", - "symbol": "YETIC", - "rank": 4125, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "byts-bytus", - "name": "Bytus", - "symbol": "BYTS", - "rank": 4126, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdt-block-duelers", - "name": "Block Duelers", - "symbol": "BDT", - "rank": 4127, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vmt-vanishing-mitilena", - "name": "Vanishing Mitilena", - "symbol": "VMT", - "rank": 4128, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "akt-akita", - "name": "Akita", - "symbol": "AKT", - "rank": 4129, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iic-intelligent-investment-chain", - "name": "Intelligent Investment Chain", - "symbol": "IIC", - "rank": 4130, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bmars-binamars", - "name": "Binamars", - "symbol": "BMARS", - "rank": 4131, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fuma-fuma-finance", - "name": "Fuma Finance", - "symbol": "FUMA", - "rank": 4132, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nsi-nsights", - "name": "nSights", - "symbol": "NSI", - "rank": 4133, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "timerr-timerr", - "name": "Timerr", - "symbol": "TIMERR", - "rank": 4134, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myobu-mybu", - "name": "Myōbu", - "symbol": "MYOBU", - "rank": 4135, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmm-gold-mining-members", - "name": "Gold Mining Members", - "symbol": "GMM", - "rank": 4136, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dwhx-diamond-whitex", - "name": "Diamond Whitex", - "symbol": "DWHX", - "rank": 4137, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hft-hodl-finance", - "name": "Hodl Finance", - "symbol": "HFT", - "rank": 4138, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vault-safevault", - "name": "SafeVault", - "symbol": "VAULT", - "rank": 4139, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfl-defily", - "name": "Defily", - "symbol": "DFL", - "rank": 4140, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogedi-dogedi", - "name": "DOGEDI", - "symbol": "DOGEDI", - "rank": 4141, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mooner-coinmooner", - "name": "CoinMooner", - "symbol": "MOONER", - "rank": 4142, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nil-nil", - "name": "NIL", - "symbol": "NIL", - "rank": 4143, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sauna-saunafinance-token", - "name": "SaunaFinance Token", - "symbol": "SAUNA", - "rank": 4144, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spin-spinada-cash", - "name": "Spinada Cash", - "symbol": "SPIN", - "rank": 4145, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "whive-the-whive-protocol", - "name": "The Whive Protocol", - "symbol": "WHIVE", - "rank": 4146, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "swamp-swampy", - "name": "Swampy", - "symbol": "SWAMP", - "rank": 4147, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krz-kranz-token", - "name": "Kranz Token", - "symbol": "KRZ", - "rank": 4148, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wad-warden", - "name": "Warden", - "symbol": "WAD", - "rank": 4149, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pussy-pussy-token", - "name": "PUSSY token", - "symbol": "PUSSY", - "rank": 4150, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "teddy-teddy-cash", - "name": "Teddy Cash", - "symbol": "TEDDY", - "rank": 4151, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trustk-trustkeys-network", - "name": "TrustKeys Network", - "symbol": "TRUSTK", - "rank": 4152, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dali-dali", - "name": "DALI", - "symbol": "DALI", - "rank": 4153, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nil-nil-dao", - "name": "Nil DAO", - "symbol": "NIL", - "rank": 4154, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wp-underground-warriors", - "name": "Underground Warriors", - "symbol": "WP", - "rank": 4155, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jets-jetoken", - "name": "JeToken", - "symbol": "JETS", - "rank": 4156, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gme-gamestop", - "name": "Gamestop", - "symbol": "GME", - "rank": 4157, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1doge-1doge", - "name": "1Doge", - "symbol": "1DOGE", - "rank": 4158, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fbx-firebottoken", - "name": "FireBotToken", - "symbol": "FBX", - "rank": 4159, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibsc-shiba-bsc", - "name": "SHIBA BSC", - "symbol": "SHIBSC", - "rank": 4160, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "corgi-corgicoin", - "name": "CorgiCoin", - "symbol": "CORGI", - "rank": 4161, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "loto-lotoblock", - "name": "Lotoblock", - "symbol": "LOTO", - "rank": 4162, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plug-plug-power-inc", - "name": "Plug Power Inc.", - "symbol": "PLUG", - "rank": 4163, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ibz-ibiza-token", - "name": "Ibiza Token", - "symbol": "IBZ", - "rank": 4164, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smash-smash-cash", - "name": "Smash Cash", - "symbol": "SMASH", - "rank": 4165, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "con-converter-finance", - "name": "Converter Finance", - "symbol": "CON", - "rank": 4166, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iris-iris-token", - "name": "Iris Token", - "symbol": "IRIS", - "rank": 4167, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "toon-pontoon", - "name": "Pontoon", - "symbol": "TOON", - "rank": 4168, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "milk-milkshake-swap", - "name": "Milkshake Swap", - "symbol": "MILK", - "rank": 4169, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metacex-metaverse-exchange", - "name": "Metaverse Exchange", - "symbol": "METACEX", - "rank": 4170, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "admonkey-admonkey", - "name": "AdMonkey", - "symbol": "ADMONKEY", - "rank": 4171, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "milit-militia-games", - "name": "Militia Games", - "symbol": "MILIT", - "rank": 4172, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meow-meowswap", - "name": "MeowSwap", - "symbol": "MEOW", - "rank": 4173, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sunc-sunrise", - "name": "Sunrise", - "symbol": "SUNC", - "rank": 4174, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dkyc-datakyc", - "name": "DataKYC", - "symbol": "DKYC", - "rank": 4175, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "libfx-libfx", - "name": "Libfx", - "symbol": "LIBFX", - "rank": 4176, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amd-advanced-micro-devices-inc", - "name": "Advanced Micro Devices Inc", - "symbol": "AMD", - "rank": 4177, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "foto-unique-photo", - "name": "Unique Photo", - "symbol": "FOTO", - "rank": 4178, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmx-gamex", - "name": "GameX", - "symbol": "GMX", - "rank": 4179, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "piasa-piasa", - "name": "PIASA", - "symbol": "PIASA", - "rank": 4180, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "monsta-meta-monstas", - "name": "Meta Monstas", - "symbol": "MONSTA", - "rank": 4181, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spy-smarty-pay", - "name": "Smarty Pay", - "symbol": "SPY", - "rank": 4182, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogev-dogevillage", - "name": "DogeVillage", - "symbol": "DOGEV", - "rank": 4183, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "when-when-token", - "name": "WHEN Token", - "symbol": "WHEN", - "rank": 4184, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dek-dekbox", - "name": "DekBox", - "symbol": "DEK", - "rank": 4185, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "degens-degens", - "name": "Degens", - "symbol": "DEGENS", - "rank": 4186, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "we-wanda-exchange", - "name": "Wanda Exchange", - "symbol": "WE", - "rank": 4187, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptu-pintu-token", - "name": "Pintu Token", - "symbol": "PTU", - "rank": 4188, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "esrc-echosoracoin", - "name": "EchoSoraCoin", - "symbol": "ESRC", - "rank": 4189, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tech-cryptomeda", - "name": "Cryptomeda", - "symbol": "TECH", - "rank": 4190, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "u-unity-software-inc", - "name": "Unity Software Inc.", - "symbol": "U", - "rank": 4191, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fin-financio", - "name": "Financio", - "symbol": "FIN", - "rank": 4192, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfd-feisty-doge-nft", - "name": "Feisty Doge NFT", - "symbol": "NFD", - "rank": 4193, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safestar-safestar", - "name": "Safestar", - "symbol": "SAFESTAR", - "rank": 4194, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "algopad-algopad", - "name": "AlgoPad", - "symbol": "ALGOPAD", - "rank": 4195, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dino-dino-exchange", - "name": "Dino Exchange", - "symbol": "DINO", - "rank": 4196, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xcc-chives-coin", - "name": "Chives Coin", - "symbol": "XCC", - "rank": 4197, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "anb-angryb", - "name": "Angryb", - "symbol": "ANB", - "rank": 4198, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amc-amc", - "name": "AMC", - "symbol": "AMC", - "rank": 4199, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chow-chow-chow-finance", - "name": "Chow Chow Finance", - "symbol": "CHOW", - "rank": 4200, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyfb-baby-floki-billionaire", - "name": "Baby Floki Billionaire", - "symbol": "BABYFB", - "rank": 4201, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bscgirl-binance-smart-chain-girl", - "name": "Binance Smart Chain Girl", - "symbol": "BSCGIRL", - "rank": 4202, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdt-blackdragon-token", - "name": "BlackDragon Token", - "symbol": "BDT", - "rank": 4203, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vest-dao-invest", - "name": "DAO Invest", - "symbol": "VEST", - "rank": 4204, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iron-iron", - "name": "Iron", - "symbol": "IRON", - "rank": 4205, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crypt-cryptertoken", - "name": "CrypterToken", - "symbol": "CRYPT", - "rank": 4206, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "matrix-matrixswap", - "name": "Matrixswap", - "symbol": "MATRIX", - "rank": 4207, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pc-promotion-coin", - "name": "Promotion Coin", - "symbol": "PC", - "rank": 4208, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lazyshiba-lazy-shiba", - "name": "Lazy Shiba", - "symbol": "LAZYSHIBA", - "rank": 4209, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cds-capital-dao-starter-token", - "name": "Capital DAO Starter Token", - "symbol": "CDS", - "rank": 4210, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dk-dragon-knight", - "name": "Dragon Knight", - "symbol": "DK", - "rank": 4211, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gxp-game-x-change-potion", - "name": "Game X Change Potion", - "symbol": "GXP", - "rank": 4212, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "he-haino", - "name": "Haino", - "symbol": "HE", - "rank": 4213, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "marsdoge-mars-doge", - "name": "Mars Doge", - "symbol": "MARSDOGE", - "rank": 4214, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sft-safety", - "name": "Safety", - "symbol": "SFT", - "rank": 4215, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crt-carrot-finance", - "name": "Carrot Finance", - "symbol": "CRT", - "rank": 4216, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bezoge-bezoge-earth", - "name": "Bezoge Earth", - "symbol": "BEZOGE", - "rank": 4217, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dexf-dexfolio", - "name": "Dexfolio", - "symbol": "DEXF", - "rank": 4218, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "feg-feg", - "name": "FEG", - "symbol": "FEG", - "rank": 4219, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "travel-travel-care", - "name": "Travel Care", - "symbol": "TRAVEL", - "rank": 4220, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bit-bitexblock", - "name": "Bitexblock", - "symbol": "BIT", - "rank": 4221, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rugpull-rugpull-prevention", - "name": "Rugpull Prevention", - "symbol": "RUGPULL", - "rank": 4222, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scie-scientia", - "name": "Scientia", - "symbol": "SCIE", - "rank": 4223, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lbet-lemon-bet", - "name": "Lemon Bet", - "symbol": "LBET", - "rank": 4224, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hart-hara-token", - "name": "Hara Token", - "symbol": "HART", - "rank": 4225, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sco-safecrypto", - "name": "SafeCrypto", - "symbol": "SCO", - "rank": 4226, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "path-pathfund", - "name": "PathFund", - "symbol": "PATH", - "rank": 4227, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "miners-minersdefi", - "name": "MinersDefi", - "symbol": "MINERS", - "rank": 4228, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hoodrat-hoodrat-finance", - "name": "Hoodrat Finance", - "symbol": "HOODRAT", - "rank": 4229, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dlta-deltatheta", - "name": "delta.theta", - "symbol": "DLTA", - "rank": 4230, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shnek-rainbow-shibaneko", - "name": "Rainbow Shibaneko", - "symbol": "SHNEK", - "rank": 4231, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "liq-liq-protocol", - "name": "LIQ Protocol", - "symbol": "LIQ", - "rank": 4232, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lofi-lofi-defi", - "name": "Lofi Defi", - "symbol": "LOFI", - "rank": 4233, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "diq-diamondq", - "name": "DiamondQ", - "symbol": "DIQ", - "rank": 4234, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ksc-kingspeed", - "name": "KingSpeed", - "symbol": "KSC", - "rank": 4235, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solideth-solideth", - "name": "SolidETH", - "symbol": "SOLIDETH", - "rank": 4236, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axiav3-axia", - "name": "Axia", - "symbol": "AXIAV3", - "rank": 4237, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wpp-wpp-token", - "name": "WPP TOKEN", - "symbol": "WPP", - "rank": 4238, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coco-coco-swap", - "name": "Coco Swap", - "symbol": "COCO", - "rank": 4239, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nttc-navigator", - "name": "Navigator", - "symbol": "NTTC", - "rank": 4240, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "april-april", - "name": "April", - "symbol": "APRIL", - "rank": 4241, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cent-centaurify", - "name": "Centaurify", - "symbol": "CENT", - "rank": 4242, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edc-earndefi", - "name": "EarnDeFi", - "symbol": "EDC", - "rank": 4243, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "neko-sakura-neko", - "name": "Sakura Neko", - "symbol": "NEKO", - "rank": 4244, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mu-micron-technology-inc", - "name": "Micron Technology, Inc.", - "symbol": "MU", - "rank": 4245, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ksw-killswitch", - "name": "KillSwitch", - "symbol": "KSW", - "rank": 4246, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "valk-valkyrio-token", - "name": "Valkyrio Token", - "symbol": "VALK", - "rank": 4247, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ibex-ibex-35-token", - "name": "IBEX 35 Token", - "symbol": "IBEX", - "rank": 4248, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "indc-nano-dogecoin", - "name": "Nano Dogecoin", - "symbol": "INDC", - "rank": 4249, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xzar-south-african-tether", - "name": "South African Tether", - "symbol": "xZAR", - "rank": 4250, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nevada-nevada", - "name": "Nevada", - "symbol": "NEVADA", - "rank": 4251, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgt-moongame", - "name": "Moongame", - "symbol": "MGT", - "rank": 4252, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bafi-bafi-finance-token", - "name": "Bafi Finance Token", - "symbol": "BAFI", - "rank": 4253, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rpay-retailpay", - "name": "RetailPay", - "symbol": "RPAY", - "rank": 4254, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cakebaker-cakebaker", - "name": "CakeBaker", - "symbol": "CAKEBAKER", - "rank": 4255, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "revo-revonetwork", - "name": "RevoNetwork", - "symbol": "REVO", - "rank": 4256, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daw-deswap", - "name": "Deswap", - "symbol": "DAW", - "rank": 4257, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdb-metadubai", - "name": "MetaDubai", - "symbol": "MDB", - "rank": 4258, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kmo-koinomo", - "name": "Koinomo", - "symbol": "KMO", - "rank": 4259, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tpv-travgopv", - "name": "TravGoPV", - "symbol": "TPV", - "rank": 4260, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "winr-winr", - "name": "WINR", - "symbol": "WINR", - "rank": 4261, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "koji-koji", - "name": "Koji", - "symbol": "KOJI", - "rank": 4262, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "psl-pastel", - "name": "Pastel", - "symbol": "PSL", - "rank": 4263, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dogy-dogeyield", - "name": "DogeYield", - "symbol": "DOGY", - "rank": 4264, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbm-nftblackmarket", - "name": "NFTBlackmarket", - "symbol": "NBM", - "rank": 4265, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "4stc-4-stock", - "name": "4-Stock", - "symbol": "4STC", - "rank": 4266, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minecraft-synex-coin", - "name": "Synex Coin", - "symbol": "MINECRAFT", - "rank": 4267, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fdr-french-digital-reserve", - "name": "French Digital Reserve", - "symbol": "FDR", - "rank": 4268, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "froge-froge-finance", - "name": "Froge Finance", - "symbol": "FROGE", - "rank": 4269, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rccc-rccc", - "name": "RCCC", - "symbol": "RCCC", - "rank": 4270, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "canu-cannumo", - "name": "Cannumo", - "symbol": "CANU", - "rank": 4271, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "imx-impermax", - "name": "Impermax", - "symbol": "IMX", - "rank": 4272, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "if1-infiniteone", - "name": "InfiniteOne", - "symbol": "IF1", - "rank": 4273, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "defi-defi", - "name": "Defi", - "symbol": "DEFI", - "rank": 4274, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfd-your-finance-decentralized", - "name": "Your Finance Decentralized", - "symbol": "YFD", - "rank": 4275, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfc-solar-full-cycle", - "name": "Solar Full Cycle", - "symbol": "SFC", - "rank": 4276, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krn-kryza-network", - "name": "KRYZA Network", - "symbol": "KRN", - "rank": 4277, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xgc-xiglute-coin", - "name": "Xiglute Coin", - "symbol": "XGC", - "rank": 4278, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pvm-privateum", - "name": "Privateum", - "symbol": "PVM", - "rank": 4279, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "maxi-maximus", - "name": "Maximus", - "symbol": "MAXI", - "rank": 4280, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfc-deflacash", - "name": "DeflaCash", - "symbol": "DFC", - "rank": 4281, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xtm-xtime", - "name": "XTime", - "symbol": "XTM", - "rank": 4282, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crypt-cryptia", - "name": "Cryptia", - "symbol": "CRYPT", - "rank": 4283, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hay-hayfever", - "name": "Hayfever", - "symbol": "HAY", - "rank": 4284, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bna-bananatok", - "name": "BananaTok", - "symbol": "BNA", - "rank": 4285, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcs-basis-coin-share", - "name": "Basis Coin Share", - "symbol": "BCS", - "rank": 4286, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fte-fishy-tank-token", - "name": "Fishy Tank Token", - "symbol": "FTE", - "rank": 4287, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dynamo-dynamo", - "name": "DYNAMO", - "symbol": "DYNAMO", - "rank": 4288, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "coshi-coshi-inu", - "name": "CoShi Inu", - "symbol": "COSHI", - "rank": 4289, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oil-warship-battles", - "name": "Warship Battles", - "symbol": "$OIL", - "rank": 4290, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "leaf-seeder-finance", - "name": "Seeder Finance", - "symbol": "LEAF", - "rank": 4291, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdt-stabledoc-token", - "name": "Stabledoc Token", - "symbol": "SDT", - "rank": 4292, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hpy-hyper-pay", - "name": "Hyper Pay", - "symbol": "HPY", - "rank": 4293, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bup-buildup", - "name": "BuildUp", - "symbol": "BUP", - "rank": 4294, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trn-treelion", - "name": "Treelion", - "symbol": "TRN", - "rank": 4295, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ocp-omni-consumer-protocol", - "name": "Omni Consumer Protocol", - "symbol": "OCP", - "rank": 4296, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zard-firezard", - "name": "FireZard", - "symbol": "ZARD", - "rank": 4297, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "glb-beglobal-finance", - "name": "BeGlobal Finance", - "symbol": "GLB", - "rank": 4298, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atlas-atlantis", - "name": "Atlantis", - "symbol": "ATLAS", - "rank": 4299, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yaan-yaan-launchpad", - "name": "Yaan Launchpad", - "symbol": "YAAN", - "rank": 4300, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swhal-safewhale", - "name": "SafeWhale", - "symbol": "SWHAL", - "rank": 4301, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nmx-nominex", - "name": "Nominex", - "symbol": "NMX", - "rank": 4302, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "candy-crypto-candy", - "name": "Crypto Candy", - "symbol": "CANDY", - "rank": 4303, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fts-footballstars", - "name": "FootballStars", - "symbol": "FTS", - "rank": 4304, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "source-resource-protocol", - "name": "ReSource Protocol", - "symbol": "SOURCE", - "rank": 4305, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plbt-polybius", - "name": "Polybius", - "symbol": "PLBT", - "rank": 4306, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ba-batorrent", - "name": "BaTorrent", - "symbol": "BA", - "rank": 4307, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sqt-squidgame-token", - "name": "Squidgame Token", - "symbol": "SQT", - "rank": 4308, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tut-tutellus-token", - "name": "Tutellus token", - "symbol": "TUT", - "rank": 4309, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wheel-wheelers", - "name": "Wheelers", - "symbol": "WHEEL", - "rank": 4310, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ugt-unreal-finance", - "name": "Unreal Finance", - "symbol": "UGT", - "rank": 4311, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ids-ideas", - "name": "IDEAS", - "symbol": "IDS", - "rank": 4312, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fti-technipfms", - "name": "Technipfms", - "symbol": "FTI", - "rank": 4313, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zomfi-zomfi", - "name": "Zomfi", - "symbol": "ZOMFI", - "rank": 4314, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ordr-the-red-order", - "name": "The Red Order", - "symbol": "ORDR", - "rank": 4315, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elf-elf-token", - "name": "Elf Token", - "symbol": "ELF", - "rank": 4316, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "energyx-safe-energy", - "name": "Safe Energy", - "symbol": "ENERGYX", - "rank": 4317, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adbe-adobe-systems-inc", - "name": "Adobe Systems Inc", - "symbol": "ADBE", - "rank": 4318, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "check-paycheck-defi", - "name": "Paycheck Defi", - "symbol": "CHECK", - "rank": 4319, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kfc-chicken", - "name": "Chicken", - "symbol": "KFC", - "rank": 4320, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bar-hierocoin", - "name": "Hierocoin", - "symbol": "BAR", - "rank": 4321, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "noone-no-one", - "name": "No One", - "symbol": "NOONE", - "rank": 4322, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dbtycoon-defi-bank-tycoon", - "name": "DeFi Bank Tycoon", - "symbol": "DBTYCOON", - "rank": 4323, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xre-xre-global", - "name": "XRE Global", - "symbol": "XRE", - "rank": 4324, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wswap-wallet-swap", - "name": "Wallet Swap", - "symbol": "WSWAP", - "rank": 4325, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xki-ki", - "name": "KI", - "symbol": "XKI", - "rank": 4326, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tyc-tianyu-finance", - "name": "Tianyu Finance", - "symbol": "TYC", - "rank": 4327, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "many-manyswap", - "name": "Manyswap", - "symbol": "MANY", - "rank": 4328, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ultgg-ultimogg", - "name": "UltimoGG", - "symbol": "ULTGG", - "rank": 4329, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eny-energy-pay", - "name": "Energy Pay", - "symbol": "ENY", - "rank": 4330, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blurt-blurt", - "name": "BLURT", - "symbol": "BLURT", - "rank": 4331, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "htc-hat-swap-city", - "name": "Hat Swap City", - "symbol": "HTC", - "rank": 4332, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oh-oh-finance", - "name": "Oh! Finance", - "symbol": "OH", - "rank": 4333, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ash-aeneas", - "name": "Aeneas", - "symbol": "ASH", - "rank": 4334, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "drct-ally-direct", - "name": "Ally Direct", - "symbol": "DRCT", - "rank": 4335, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unifi-unifi", - "name": "UNIFI", - "symbol": "UNIFI", - "rank": 4336, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "space-spacelens", - "name": "Spacelens", - "symbol": "SPACE", - "rank": 4337, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cgc-coingalaxy", - "name": "CoinGalaxy", - "symbol": "CGC", - "rank": 4338, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cooom-incooom-genesis", - "name": "Incooom Genesis", - "symbol": "COOOM", - "rank": 4339, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "avme-avme", - "name": "AVME", - "symbol": "AVME", - "rank": 4340, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abee-avalanche-honeybee", - "name": "Avalanche HoneyBee", - "symbol": "A.BEE", - "rank": 4341, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edu-educoin", - "name": "EduCoin", - "symbol": "EDU", - "rank": 4342, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "magf-magic-forest", - "name": "Magic Forest", - "symbol": "MAGF", - "rank": 4343, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shbar-shilly-bar", - "name": "Shilly Bar", - "symbol": "SHBAR", - "rank": 4344, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ali-alita", - "name": "Alita", - "symbol": "ALI", - "rank": 4345, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lmn-lemonn-token", - "name": "Lemonn Token", - "symbol": "LMN", - "rank": 4346, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swam-swapmatic-token", - "name": "SwapMatic Token", - "symbol": "SWAM", - "rank": 4347, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hibiki-hibiki-finance", - "name": "Hibiki Finance", - "symbol": "HIBIKI", - "rank": 4348, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grem-gremlins-finance", - "name": "Gremlins Finance", - "symbol": "GREM", - "rank": 4349, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clt-cexlt", - "name": "Cexlt", - "symbol": "CLT", - "rank": 4350, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "noid-tokenoid", - "name": "Tokenoid", - "symbol": "NOID", - "rank": 4351, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inft-inft-platform", - "name": "iNFT Platform", - "symbol": "INFT", - "rank": 4352, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "agn-agrinoble", - "name": "Agrinoble", - "symbol": "AGN", - "rank": 4353, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dnc-danat-coin", - "name": "Danat Coin", - "symbol": "DNC", - "rank": 4354, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogus-dogus", - "name": "Dogus", - "symbol": "DOGUS", - "rank": 4355, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lhc-lightcoin", - "name": "LightCoin", - "symbol": "LHC", - "rank": 4356, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kally-polkally", - "name": "Polkally", - "symbol": "KALLY", - "rank": 4357, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptn-pluto-network", - "name": "Pluto Network", - "symbol": "PTN", - "rank": 4358, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctr-creator-platform", - "name": "Creator Platform", - "symbol": "CTR", - "rank": 4359, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "retro-retromoon", - "name": "Retromoon", - "symbol": "RETRO", - "rank": 4360, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfuel-superfuel", - "name": "SuperFuel", - "symbol": "SFUEL", - "rank": 4361, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amt-agro-matic", - "name": "Agro-Matic", - "symbol": "AMT", - "rank": 4362, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "manga-manga-token", - "name": "Manga Token", - "symbol": "MANGA", - "rank": 4363, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tv-ti-value", - "name": "Ti-Value", - "symbol": "TV", - "rank": 4364, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "must-must", - "name": "Must", - "symbol": "MUST", - "rank": 4365, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nb-no-bull", - "name": "No Bull", - "symbol": "NB", - "rank": 4366, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "best-bitcoin-and-ethereum-standard-token", - "name": "Bitcoin and Ethereum Standard Token", - "symbol": "BEST", - "rank": 4367, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lddp-la-doge-de-papel", - "name": "La Doge de Papel", - "symbol": "LDDP", - "rank": 4368, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aer-aerdrop", - "name": "Aerdrop", - "symbol": "AER", - "rank": 4369, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arkg-ark-genomic-revolution-etf", - "name": "ARK Genomic Revolution ETF", - "symbol": "ARKG", - "rank": 4370, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wex-waultswap", - "name": "WaultSwap", - "symbol": "WEX", - "rank": 4371, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lcdp-la-casa-de-papel", - "name": "La Casa De Papel", - "symbol": "LCDP", - "rank": 4372, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sif-sif-token", - "name": "SIF TOKEN", - "symbol": "SIF", - "rank": 4373, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tmed-mdsquare", - "name": "MDsquare", - "symbol": "TMED", - "rank": 4374, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dextf-dextf", - "name": "DEXTF", - "symbol": "DEXTF", - "rank": 4375, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alm-alium-swap", - "name": "Alium Swap", - "symbol": "ALM", - "rank": 4376, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fast-fastfinance", - "name": "Fast.Finance", - "symbol": "FAST", - "rank": 4377, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aquagoat-aquagoat", - "name": "AquaGoat", - "symbol": "AQUAGOAT", - "rank": 4378, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mega-megatoken", - "name": "MegaToken", - "symbol": "MEGA", - "rank": 4379, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zeny-zeny", - "name": "ZENY", - "symbol": "ZENY", - "rank": 4380, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sashimi-sashimi", - "name": "Sashimi", - "symbol": "SASHIMI", - "rank": 4381, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shiborg-shiborg-inu", - "name": "SHIBORG INU", - "symbol": "SHIBORG", - "rank": 4382, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmr-gmr-finance", - "name": "GMR Finance", - "symbol": "GMR", - "rank": 4383, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bulldog-bulldog-coin", - "name": "BullDog Coin", - "symbol": "BULLDOG", - "rank": 4384, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "delo-decentra-lotto", - "name": "Decentra-Lotto", - "symbol": "DELO", - "rank": 4385, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arc-arcade", - "name": "Arcade", - "symbol": "ARC", - "rank": 4386, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icap-invictuscapitalcom-token", - "name": "InvictusCapital.com Token", - "symbol": "ICAP", - "rank": 4387, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tundra-tundra-token", - "name": "Tundra Token", - "symbol": "TUNDRA", - "rank": 4388, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "less-less", - "name": "LESS", - "symbol": "LESS", - "rank": 4389, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lyca-lyca-island", - "name": "Lyca Island", - "symbol": "LYCA", - "rank": 4390, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "angel-polylauncher", - "name": "Polylauncher", - "symbol": "ANGEL", - "rank": 4391, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kana-kanaloa-network", - "name": "Kanaloa Network", - "symbol": "KANA", - "rank": 4392, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sca-scaleswap-token", - "name": "Scaleswap Token", - "symbol": "SCA", - "rank": 4393, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "monke-space-monkey-token", - "name": "Space Monkey Token", - "symbol": "MONKE", - "rank": 4394, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1bit-onebit", - "name": "OneBit", - "symbol": "1BIT", - "rank": 4395, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "agspad-aegis-launchpad", - "name": "Aegis Launchpad", - "symbol": "AGSPAD", - "rank": 4396, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "usdu-upper-dollar", - "name": "Upper Dollar", - "symbol": "USDU", - "rank": 4397, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slam-slam-token", - "name": "Slam Token", - "symbol": "SLAM", - "rank": 4398, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "airt-airnft-token", - "name": "AirNFT Token", - "symbol": "AIRT", - "rank": 4399, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cheese-cheese-swap", - "name": "Cheese Swap", - "symbol": "CHEESE", - "rank": 4400, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apebusd-apebusd", - "name": "ApeBUSD", - "symbol": "APEBUSD", - "rank": 4401, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rope-rope-token", - "name": "Rope Token", - "symbol": "ROPE", - "rank": 4402, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shiblite-shibalite", - "name": "ShibaLite", - "symbol": "SHIBLITE", - "rank": 4403, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rich-richcity", - "name": "RichCity", - "symbol": "RICH", - "rank": 4404, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idh-idhus", - "name": "IDHUS", - "symbol": "IDH", - "rank": 4405, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibapup-shibapup", - "name": "ShibaPup", - "symbol": "SHIBAPUP", - "rank": 4406, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cvip-cvip", - "name": "CVIP", - "symbol": "CVIP", - "rank": 4407, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ski-skillchain", - "name": "Skillchain", - "symbol": "SKI", - "rank": 4408, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pslip-pinkslip-finance", - "name": "Pinkslip Finance", - "symbol": "PSLIP", - "rank": 4409, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dondoge-dondoge", - "name": "DONDOGE", - "symbol": "DONDOGE", - "rank": 4410, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skn-sharkcoin", - "name": "SharkCoin", - "symbol": "SKN", - "rank": 4411, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "idoscan-idoscan", - "name": "Idoscan", - "symbol": "IDOSCAN", - "rank": 4412, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vndc-vndc", - "name": "VNDC", - "symbol": "VNDC", - "rank": 4413, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "earn-earn-network", - "name": "EARN Network", - "symbol": "EARN$", - "rank": 4414, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vgo-virtual-goods-token", - "name": "Virtual Goods Token", - "symbol": "VGO", - "rank": 4415, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "licp-liquid-icp", - "name": "Liquid ICP", - "symbol": "LICP", - "rank": 4416, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myth-myth-token", - "name": "Myth Token", - "symbol": "MYTH", - "rank": 4417, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minx-innovaminex", - "name": "InnovaMinex", - "symbol": "MINX", - "rank": 4418, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csms-cosmostarter", - "name": "Cosmostarter", - "symbol": "CSMS", - "rank": 4419, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mausdc-matic-aave-interest-bearing-usdc", - "name": "Matic Aave Interest Bearing USDC", - "symbol": "MAUSDC", - "rank": 4420, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "totem-cryptotem", - "name": "Cryptotem", - "symbol": "TOTEM", - "rank": 4421, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wdefi-woonkly-defi", - "name": "Woonkly Defi", - "symbol": "WDEFI", - "rank": 4422, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mrc-moon-rocket-coin", - "name": "Moon Rocket Coin", - "symbol": "MRC", - "rank": 4423, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smhdoge-supermegahyperdoge", - "name": "SuperMegaHyperDoge", - "symbol": "SMHDOGE", - "rank": 4424, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pop-pop", - "name": "POP", - "symbol": "POP!", - "rank": 4425, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibagames-shiba-games", - "name": "Shiba Games", - "symbol": "SHIBAGAMES", - "rank": 4426, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btf-bitcoin-faith", - "name": "Bitcoin Faith", - "symbol": "BTF", - "rank": 4427, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vny-vanity", - "name": "Vanity", - "symbol": "VNY", - "rank": 4428, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kif-kittenfinance", - "name": "KittenFinance", - "symbol": "KIF", - "rank": 4429, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shield-crypto-shield", - "name": "Crypto Shield", - "symbol": "SHIELD", - "rank": 4430, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbp-shibapad", - "name": "ShibaPad", - "symbol": "SBP", - "rank": 4431, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vvt-versoview", - "name": "VersoView", - "symbol": "VVT", - "rank": 4432, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yni-yearnyfinetwork", - "name": "YEARNYFI NETWORK", - "symbol": "YNI", - "rank": 4433, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ckt-caketools", - "name": "Caketools", - "symbol": "CKT", - "rank": 4434, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atp-alaya", - "name": "Alaya", - "symbol": "ATP", - "rank": 4435, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bnbh-bnbheroes-token", - "name": "BnbHeroes Token", - "symbol": "BNBH", - "rank": 4436, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "waco-waste-digital-coin", - "name": "Waste Digital Coin", - "symbol": "WACO", - "rank": 4437, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "quack-richquack", - "name": "RichQuack", - "symbol": "QUACK", - "rank": 4438, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "woofy-woofy", - "name": "Woofy", - "symbol": "WOOFY", - "rank": 4439, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evraz-evraz", - "name": "EVRAZ", - "symbol": "EVRAZ", - "rank": 4440, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spat-meta-spatial", - "name": "Meta Spatial", - "symbol": "SPAT", - "rank": 4441, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wet-weble-ecosystem-token", - "name": "Weble Ecosystem Token", - "symbol": "WET", - "rank": 4442, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "a5t-alpha5", - "name": "Alpha5", - "symbol": "A5T", - "rank": 4443, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xri-xroad", - "name": "XRoad", - "symbol": "XRI", - "rank": 4444, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bplc-blackpearl-token", - "name": "BlackPearl Token", - "symbol": "BPLC", - "rank": 4445, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jrt-jarvis-reward-token", - "name": "Jarvis Reward Token", - "symbol": "JRT", - "rank": 4446, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stemx-stemx", - "name": "STEMX", - "symbol": "STEMX", - "rank": 4447, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ohminu-olympus-inu-dao", - "name": "Olympus Inu Dao", - "symbol": "OHMINU", - "rank": 4448, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "m-macys", - "name": "Macys", - "symbol": "M", - "rank": 4449, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sticky-flypaper", - "name": "FlyPaper", - "symbol": "STICKY", - "rank": 4450, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "afrox-afrodex", - "name": "AfroDex", - "symbol": "AfroX", - "rank": 4451, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minibtc-minibtc", - "name": "MiniBTC", - "symbol": "MINIBTC", - "rank": 4452, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnx-minix", - "name": "MiniX", - "symbol": "MNX", - "rank": 4453, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mrk-merck-co-inc", - "name": "Merck \u0026 Co Inc", - "symbol": "MRK", - "rank": 4454, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hunger-hunger-token", - "name": "Hunger Token", - "symbol": "HUNGER", - "rank": 4455, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccts-cactus-inu", - "name": "Cactus Inu", - "symbol": "CCTS", - "rank": 4456, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rbn-robinos", - "name": "Robinos", - "symbol": "RBN", - "rank": 4457, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nkla-nikola-corporation", - "name": "Nikola Corporation", - "symbol": "NKLA", - "rank": 4458, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lnko-lnko-token", - "name": "LNKO Token", - "symbol": "LNKO", - "rank": 4459, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cvc-covicoin", - "name": "CoviCoin", - "symbol": "CVC", - "rank": 4460, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fbt-fanbi-token", - "name": "FANBI TOKEN", - "symbol": "FBT", - "rank": 4461, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eubc-eub-chain", - "name": "EUB Chain", - "symbol": "EUBC", - "rank": 4462, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "urub-urubit", - "name": "Urubit", - "symbol": "URUB", - "rank": 4463, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfn-definomics", - "name": "DeFinomics", - "symbol": "DFN", - "rank": 4464, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "forint-forint", - "name": "Forint", - "symbol": "FORINT", - "rank": 4465, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minishib-minishib-eth", - "name": "miniSHIB ETH", - "symbol": "MINISHIB", - "rank": 4466, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpx-circlepod", - "name": "Circlepod", - "symbol": "CPX", - "rank": 4467, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bb-baby-bali", - "name": "Baby Bali", - "symbol": "BB", - "rank": 4468, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hd-hunterdoge", - "name": "HunterDoge", - "symbol": "$HD", - "rank": 4469, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ael-aelysir", - "name": "Aelysir", - "symbol": "AEL", - "rank": 4470, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "catgirl-catgirl", - "name": "Catgirl", - "symbol": "CATGIRL", - "rank": 4471, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "caf-carsautofinance", - "name": "CarsAutoFinance", - "symbol": "CAF", - "rank": 4472, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "devia8-devia8", - "name": "Devia8", - "symbol": "DEVIA8", - "rank": 4473, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kinta-kintaman", - "name": "Kintaman", - "symbol": "KINTA", - "rank": 4474, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btp-bitcoin-pay", - "name": "Bitcoin Pay", - "symbol": "BTP", - "rank": 4475, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rrb-renrenbit", - "name": "Renrenbit", - "symbol": "RRB", - "rank": 4476, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wgl-wiggly-finance", - "name": "Wiggly Finance", - "symbol": "WGL", - "rank": 4477, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "baby-baby-samo-coin", - "name": "Baby Samo Coin", - "symbol": "BABY", - "rank": 4478, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dis-walt-disney", - "name": "Walt Disney", - "symbol": "DIS", - "rank": 4479, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emrals-emrals", - "name": "Emrals", - "symbol": "EMRALS", - "rank": 4480, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cats-cats-token", - "name": "CATS Token", - "symbol": "CATS", - "rank": 4481, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orio-boorio", - "name": "Boorio", - "symbol": "ORIO", - "rank": 4482, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "embr-embr", - "name": "Embr", - "symbol": "EMBR", - "rank": 4483, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mph-morpher", - "name": "Morpher", - "symbol": "MPH", - "rank": 4484, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "defit-digital-fitness", - "name": "Digital Fitness", - "symbol": "DEFIT", - "rank": 4485, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csw-wolfdog-bsc", - "name": "WolfDog (BSC)", - "symbol": "CSW", - "rank": 4486, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "heist-shibaheistv2", - "name": "ShibaHeistV2", - "symbol": "HEIST", - "rank": 4487, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "astro-astronos", - "name": "Astronos", - "symbol": "ASTRO", - "rank": 4488, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sosx-social-x", - "name": "Social X", - "symbol": "SOSX", - "rank": 4489, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mta-metaaxis", - "name": "MetaAxis", - "symbol": "MTA", - "rank": 4490, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jind-jindo-inu", - "name": "Jindo Inu", - "symbol": "JIND", - "rank": 4491, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "para-parabolic", - "name": "Parabolic", - "symbol": "PARA", - "rank": 4492, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "x99-x99token", - "name": "x99Token", - "symbol": "X99", - "rank": 4493, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctc-cybertronchain", - "name": "CyberTronchain", - "symbol": "CTC", - "rank": 4494, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unity-unity-protocol", - "name": "Unity Protocol", - "symbol": "UNITY", - "rank": 4495, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yshibainu-yooshiba-inu", - "name": "Yooshiba Inu", - "symbol": "YSHIBAINU", - "rank": 4496, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ufo-uniform-fiscal-object", - "name": "Uniform Fiscal Object", - "symbol": "UFO", - "rank": 4497, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fcbtc-fc-bitcoin", - "name": "FC Bitcoin", - "symbol": "FCBTC", - "rank": 4498, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecoin-e-coin-finance", - "name": "E-coin Finance", - "symbol": "ECOIN", - "rank": 4499, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "noa-noa-play", - "name": "NOA PLAY", - "symbol": "NOA", - "rank": 4500, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metaflip-metaflip", - "name": "Metaflip", - "symbol": "METAFLIP", - "rank": 4501, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ori-orica", - "name": "Orica", - "symbol": "ORI", - "rank": 4502, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fyp-flypme", - "name": "FlypMe", - "symbol": "FYP", - "rank": 4503, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hima-himalayan-cat-coin", - "name": "Himalayan Cat Coin", - "symbol": "HIMA", - "rank": 4504, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dkey-dkey-bank", - "name": "DKEY BANK", - "symbol": "DKEY", - "rank": 4505, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egoh-egoh-finance", - "name": "EGOH Finance", - "symbol": "EGOH", - "rank": 4506, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dct-degree-crypto-token", - "name": "Degree Crypto Token", - "symbol": "DCT", - "rank": 4507, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sstx-silver-stonks", - "name": "Silver Stonks", - "symbol": "SSTX", - "rank": 4508, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qwla-qawalla-token", - "name": "Qawalla Token", - "symbol": "QWLA", - "rank": 4509, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssgtx-safeswap-token", - "name": "Safeswap Token", - "symbol": "SSGTX", - "rank": 4510, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfch-defich", - "name": "DeFi.ch", - "symbol": "DFCH", - "rank": 4511, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prdx-predix-network", - "name": "Predix Network", - "symbol": "PRDX", - "rank": 4512, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "miaw-miaw", - "name": "Miaw", - "symbol": "MIAW", - "rank": 4513, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fan-fanspel", - "name": "FANSPEL", - "symbol": "FAN", - "rank": 4514, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "psc-promo-swipe-coin", - "name": "Promo Swipe Coin", - "symbol": "PSC", - "rank": 4515, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ore-ore-token", - "name": "ORE Token", - "symbol": "ORE", - "rank": 4516, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gn-gn", - "name": "GN", - "symbol": "GN", - "rank": 4517, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cspro-cspro-chain", - "name": "CSPro Chain", - "symbol": "CSPRO", - "rank": 4518, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tipinu-tipinu", - "name": "Tipinu", - "symbol": "TIPINU", - "rank": 4519, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cevo-cardanoevo", - "name": "CardanoEvo", - "symbol": "cEvo", - "rank": 4520, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbu-nimbus", - "name": "Nimbus", - "symbol": "NBU", - "rank": 4521, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shill-shillit-app", - "name": "Shillit App", - "symbol": "SHILL", - "rank": 4522, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shiba-shibalana", - "name": "Shibalana", - "symbol": "SHIBA", - "rank": 4523, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftfy-nftfy", - "name": "Nftfy", - "symbol": "NFTFY", - "rank": 4524, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aens-aen-smart-token", - "name": "AEN Smart Token", - "symbol": "AENS", - "rank": 4525, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bigeth-big", - "name": "Big", - "symbol": "BIGETH", - "rank": 4526, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fomo-fomo-labs", - "name": "Fomo Labs", - "symbol": "FOMO", - "rank": 4527, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mars-marscoin", - "name": "Marscoin", - "symbol": "MARS", - "rank": 4528, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dmod-demodyfi", - "name": "Demodyfi", - "symbol": "DMOD", - "rank": 4529, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lucky-lucky-lion", - "name": "Lucky Lion", - "symbol": "LUCKY", - "rank": 4530, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dsd-dynamic-set-dollar", - "name": "Dynamic Set Dollar", - "symbol": "DSD", - "rank": 4531, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fish-metafish", - "name": "Metafish", - "symbol": "FISH", - "rank": 4532, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edoge-elon-doge", - "name": "ELON DOGE", - "symbol": "EDOGE", - "rank": 4533, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tbc-terablock", - "name": "TeraBlock", - "symbol": "TBC", - "rank": 4534, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drive-safe-drive", - "name": "Safe DRIVE", - "symbol": "DRIVE", - "rank": 4535, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "she-shinechain", - "name": "ShineChain", - "symbol": "SHE", - "rank": 4536, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "karen-senator-karen", - "name": "Senator Karen", - "symbol": "KAREN", - "rank": 4537, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mvrs-mets-mvrs", - "name": "Mets MVRS", - "symbol": "MVRS", - "rank": 4538, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tntt-tntt", - "name": "TNTT", - "symbol": "TNTT", - "rank": 4539, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcdn-blockcdn", - "name": "BlockCDN", - "symbol": "BCDN", - "rank": 4540, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "topc-topchain", - "name": "TopChain", - "symbol": "TOPC", - "rank": 4541, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "udo-unus-dao", - "name": "Unus Dao", - "symbol": "UDO", - "rank": 4542, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axa-foundation", - "name": "AXA Foundation", - "symbol": "AXA", - "rank": 4543, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vgo-virgo", - "name": "Virgo", - "symbol": "VGO", - "rank": 4544, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "megaland-metagalaxy-land", - "name": "Metagalaxy Land", - "symbol": "MEGALAND", - "rank": 4545, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moontoken-moontoken", - "name": "MoonToken", - "symbol": "MOONTOKEN", - "rank": 4546, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dice-betdice", - "name": "BetDice", - "symbol": "DICE", - "rank": 4547, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pitqd-pitquidity", - "name": "Pitquidity", - "symbol": "PITQD", - "rank": 4548, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wnyc-wrapped-newyorkcoin", - "name": "Wrapped NewYorkCoin", - "symbol": "WNYC", - "rank": 4549, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pulsar-pulsar-token", - "name": "Pulsar Token", - "symbol": "$PULSAR", - "rank": 4550, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cai-caietffinance", - "name": "CAIETF.Finance", - "symbol": "CAI", - "rank": 4551, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ryip-ryi-platinum", - "name": "RYI Platinum", - "symbol": "RYIP", - "rank": 4552, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "omen-augury-finance", - "name": "Augury Finance", - "symbol": "OMEN", - "rank": 4553, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ass-australian-safe-shepherd", - "name": "Australian Safe Shepherd", - "symbol": "ASS", - "rank": 4554, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wiva-wiva", - "name": "WIVA", - "symbol": "WIVA", - "rank": 4555, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tdoc-teladoc-health-inc", - "name": "Teladoc Health, Inc.", - "symbol": "TDOC", - "rank": 4556, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drdc-droid-coin", - "name": "Droid Coin", - "symbol": "DRDC", - "rank": 4557, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yetu-yetucoin", - "name": "Yetucoin", - "symbol": "YETU", - "rank": 4558, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dtop-dhedge-top-index", - "name": "dHEDGE Top Index", - "symbol": "DTOP", - "rank": 4559, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pswap-polkaswap", - "name": "Polkaswap", - "symbol": "PSWAP", - "rank": 4560, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tribex-tribe-token", - "name": "Tribe Token", - "symbol": "TRIBEX", - "rank": 4561, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyxrp-baby-ripple", - "name": "Baby Ripple", - "symbol": "BABYXRP", - "rank": 4562, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ntes-netease-inc", - "name": "NetEase, Inc.", - "symbol": "NTES", - "rank": 4563, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "musk-muskswap", - "name": "MuskSwap", - "symbol": "MUSK", - "rank": 4564, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyquick-babyquick", - "name": "BabyQuick", - "symbol": "BABYQUICK", - "rank": 4565, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sgt-snglsdao-governance-token", - "name": "snglsDAO Governance Token", - "symbol": "SGT", - "rank": 4566, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "binosaurs-binosaurs", - "name": "Binosaurs", - "symbol": "BINOSAURS", - "rank": 4567, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dcash-diamond-cash", - "name": "Diamond Cash", - "symbol": "DCASH", - "rank": 4568, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "867-867", - "name": "867", - "symbol": "867", - "rank": 4569, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uted-united", - "name": "United", - "symbol": "UTED", - "rank": 4570, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bored-bored-museum", - "name": "Bored Museum", - "symbol": "BORED", - "rank": 4571, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wheat-wheat-token", - "name": "Wheat Token", - "symbol": "WHEAT", - "rank": 4572, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wkhs-workhorse-group-inc", - "name": "Workhorse Group Inc.", - "symbol": "WKHS", - "rank": 4573, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sana-storage-area-network-anywhere", - "name": "Storage Area Network Anywhere", - "symbol": "SANA", - "rank": 4574, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pdx-pokedx", - "name": "PokeDX", - "symbol": "PDX", - "rank": 4575, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "viking-viking-swap", - "name": "Viking Swap", - "symbol": "VIKING", - "rank": 4576, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcx-bitcoinx", - "name": "BitcoinX", - "symbol": "BCX", - "rank": 4577, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dac-degenartscom-coin", - "name": "DegenArts.com Coin", - "symbol": "DAC", - "rank": 4578, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ram-herum", - "name": "Herum", - "symbol": "RAM", - "rank": 4579, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sgt-spacegoat-token", - "name": "SPACEGOAT TOKEN", - "symbol": "SGT", - "rank": 4580, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mmx-mymetax", - "name": "MyMetaX", - "symbol": "MMX", - "rank": 4581, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wap-wapswap-finance", - "name": "WapSwap Finance", - "symbol": "WAP", - "rank": 4582, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccc-cross-chain-capital", - "name": "Cross Chain Capital", - "symbol": "CCC", - "rank": 4583, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "king-kingfund-finance", - "name": "KingFund Finance", - "symbol": "KING", - "rank": 4584, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tomi-tomi", - "name": "TOMI", - "symbol": "TOMI", - "rank": 4585, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rtt-rebeltradertoken", - "name": "RebelTraderToken", - "symbol": "RTT", - "rank": 4586, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ele-eleven-finance", - "name": "Eleven Finance", - "symbol": "ELE", - "rank": 4587, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "marsrise-marsrise", - "name": "MarsRise", - "symbol": "MARSRISE", - "rank": 4588, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hk-helkin", - "name": "Helkin", - "symbol": "HK", - "rank": 4589, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gsa-global-smart-asset", - "name": "Global Smart Asset", - "symbol": "GSA", - "rank": 4590, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spb-superbnbfinance", - "name": "SuperBNB.Finance", - "symbol": "SPB", - "rank": 4591, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spot-spotify", - "name": "Spotify", - "symbol": "SPOT", - "rank": 4592, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tmcn-timecoin-protocol", - "name": "Timecoin Protocol", - "symbol": "TMCN", - "rank": 4593, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mlnx-melonx", - "name": "MELONx", - "symbol": "$MLNX", - "rank": 4594, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jfm-justfarm", - "name": "JustFarm", - "symbol": "JFM", - "rank": 4595, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "duck-duckrocket", - "name": "DuckRocket", - "symbol": "DUCK", - "rank": 4596, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "scan-scan-defi", - "name": "Scan DeFi", - "symbol": "SCAN", - "rank": 4597, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "don-deonex-token", - "name": "DEONEX Token", - "symbol": "DON", - "rank": 4598, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bomb-bomberman", - "name": "Bomberman", - "symbol": "$BOMB", - "rank": 4599, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "obtc-optical-bitcoin", - "name": "Optical Bitcoin", - "symbol": "oBTC", - "rank": 4600, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kala-kala-finance", - "name": "Kala Finance", - "symbol": "KALA", - "rank": 4601, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcbam-bitcoinbam", - "name": "BitcoinBam", - "symbol": "BTCBAM", - "rank": 4602, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sriracha-sriracha-inu", - "name": "Sriracha Inu", - "symbol": "SRIRACHA", - "rank": 4603, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gazpru-gazprom", - "name": "GAZPROM", - "symbol": "GAZPru", - "rank": 4604, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibacash-shibacash", - "name": "ShibaCash", - "symbol": "SHIBACASH", - "rank": 4605, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babydoge-baby-doge-coin", - "name": "Baby Doge Coin", - "symbol": "BABYDOGE", - "rank": 4606, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lkt-luckytoken", - "name": "LuckyToken", - "symbol": "LKT", - "rank": 4607, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "srgt-severe-rise-games", - "name": "Severe Rise Games", - "symbol": "SRGT", - "rank": 4608, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pop-popcorn", - "name": "Popcorn", - "symbol": "POP", - "rank": 4609, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "awx-aurusdefi", - "name": "AurusDeFi", - "symbol": "AWX", - "rank": 4610, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mesh-meshbox", - "name": "MeshBox", - "symbol": "MESH", - "rank": 4611, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tokau-tokyo-au", - "name": "Tokyo AU", - "symbol": "TOKAU", - "rank": 4612, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "obot-obortech", - "name": "Obortech", - "symbol": "OBOT", - "rank": 4613, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fox-fox-finance", - "name": "Fox Finance", - "symbol": "FOX", - "rank": 4614, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lucky-lucky", - "name": "LUCKY", - "symbol": "LUCKY", - "rank": 4615, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "4art-4art", - "name": "4ART", - "symbol": "4ART", - "rank": 4616, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sync-linksync", - "name": "LinkSync", - "symbol": "SYNC", - "rank": 4617, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "klayg-klaygames", - "name": "KlayGames", - "symbol": "KLAYG", - "rank": 4618, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polyru-polymetal-international", - "name": "Polymetal International", - "symbol": "POLYru", - "rank": 4619, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "riot-riot-racers", - "name": "Riot Racers", - "symbol": "RIOT", - "rank": 4620, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myf-myteamfinance", - "name": "MyteamFinance", - "symbol": "MYF", - "rank": 4621, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plg-plunge", - "name": "Plunge", - "symbol": "PLG", - "rank": 4622, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sos-sonofsaitama", - "name": "SonOfSaitama", - "symbol": "SOS", - "rank": 4623, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mns-monnos", - "name": "Monnos", - "symbol": "MNS", - "rank": 4624, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lf-life-dao", - "name": "Life DAO", - "symbol": "LF", - "rank": 4625, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "befx-belifex", - "name": "Belifex", - "symbol": "BEFX", - "rank": 4626, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mars-mars-network", - "name": "Mars Network", - "symbol": "MARS", - "rank": 4627, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anon-anonymous-bsc", - "name": "Anonymous BSC", - "symbol": "ANON", - "rank": 4628, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibgx-shibagalaxy", - "name": "ShibaGalaxy", - "symbol": "SHIBGX", - "rank": 4629, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shdc-shdcash", - "name": "SHDCash", - "symbol": "SHDC", - "rank": 4630, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "azr-aezora", - "name": "Aezora", - "symbol": "AZR", - "rank": 4631, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lsr-lasereyes", - "name": "LaserEyes", - "symbol": "LSR", - "rank": 4632, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ga-golden-age", - "name": "Golden Age", - "symbol": "GA", - "rank": 4633, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ocv-oculus-vision", - "name": "Oculus Vision", - "symbol": "OCV", - "rank": 4634, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "itr-intercoin", - "name": "Intercoin", - "symbol": "ITR", - "rank": 4635, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "samu-samusky-token", - "name": "Samusky Token", - "symbol": "SAMU", - "rank": 4636, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcc-multi-chain-capital", - "name": "Multi-Chain Capital", - "symbol": "MCC", - "rank": 4637, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "einstein-polkadog", - "name": "Polkadog", - "symbol": "EINSTEIN", - "rank": 4638, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "doken-doken", - "name": "DoKEN", - "symbol": "DOKEN", - "rank": 4639, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hua-chihuahua", - "name": "Chihuahua", - "symbol": "HUA", - "rank": 4640, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nausicaa-nausicaa-inu", - "name": "Nausicaa-Inu", - "symbol": "NAUSICAA", - "rank": 4641, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amgo-amigo", - "name": "Amigo", - "symbol": "AMGO", - "rank": 4642, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mimo-million-monke", - "name": "Million Monke", - "symbol": "MIMO", - "rank": 4643, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clac-cla-coin", - "name": "CLA Coin", - "symbol": "CLAC", - "rank": 4644, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cshop-coinshopp", - "name": "CoinShopp", - "symbol": "CSHOP", - "rank": 4645, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfn-yearn-finance-network", - "name": "Yearn Finance Network", - "symbol": "YFN", - "rank": 4646, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meta-meta-musk", - "name": "Meta Musk", - "symbol": "META", - "rank": 4647, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aquari-aquari", - "name": "Aquari", - "symbol": "AQUARI", - "rank": 4648, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poo-poomoon", - "name": "POOMOON", - "symbol": "POO", - "rank": 4649, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sld-soldiersland", - "name": "SoldiersLand", - "symbol": "SLD", - "rank": 4650, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfc-defi-city", - "name": "DeFi City", - "symbol": "DFC", - "rank": 4651, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "banketh-banketh", - "name": "BankEth", - "symbol": "BANKETH", - "rank": 4652, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dip-dipper-network", - "name": "Dipper Network", - "symbol": "DIP", - "rank": 4653, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "osm-options-market", - "name": "Options Market", - "symbol": "OSM", - "rank": 4654, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pbtc-photonic-bitcoin", - "name": "Photonic Bitcoin", - "symbol": "pBTC", - "rank": 4655, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cakegirl-cake-girl", - "name": "Cake Girl", - "symbol": "CAKEGIRL", - "rank": 4656, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bzsc-bezos-coin", - "name": "Bezos Coin", - "symbol": "BZSC", - "rank": 4657, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pndr-pandora-protocol", - "name": "Pandora Protocol", - "symbol": "PNDR", - "rank": 4658, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crusader-crusaders-of-crypto", - "name": "Crusaders of Crypto", - "symbol": "CRUSADER", - "rank": 4659, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egi-egame", - "name": "eGame", - "symbol": "EGI", - "rank": 4660, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zusd-zytara-dollar", - "name": "Zytara Dollar", - "symbol": "ZUSD", - "rank": 4661, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abu-abura-farm", - "name": "Abura Farm", - "symbol": "ABU", - "rank": 4662, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zjlt-zjlt-distributed-factoring-network", - "name": "ZJLT Distributed Factoring Network", - "symbol": "ZJLT", - "rank": 4663, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evo-evolution-token", - "name": "Evolution Token", - "symbol": "EVO", - "rank": 4664, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nac-nowlage-coin", - "name": "Nowlage Coin", - "symbol": "NAC", - "rank": 4665, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctft-coin-to-fish", - "name": "Coin To Fish", - "symbol": "CTFT", - "rank": 4666, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aen-altera", - "name": "Altera", - "symbol": "AEN", - "rank": 4667, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "karen-karencoin", - "name": "KarenCoin", - "symbol": "KAREN", - "rank": 4668, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iqeq-iqeq-token", - "name": "IQEQ Token", - "symbol": "IQEQ", - "rank": 4669, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcb-bitcoinbrand", - "name": "BitcoinBrand", - "symbol": "BTCB", - "rank": 4670, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "onion-farmersonly-onion", - "name": "FarmersOnly Onion", - "symbol": "ONION", - "rank": 4671, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbyxrp-babyxrp", - "name": "BabyXrp", - "symbol": "BBYXRP", - "rank": 4672, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ufc-union-fair-coin", - "name": "Union Fair Coin", - "symbol": "UFC", - "rank": 4673, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "deep-deepcloud-ai", - "name": "DeepCloud AI", - "symbol": "DEEP", - "rank": 4674, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cic-crypto-indian-coin", - "name": "Crypto Indian Coin", - "symbol": "CIC", - "rank": 4675, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rxc-ran-x-crypto", - "name": "Ran x Crypto", - "symbol": "RXC", - "rank": 4676, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pow-project-one-whale", - "name": "Project: One Whale", - "symbol": "POW", - "rank": 4677, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "desh-decash", - "name": "DeCash", - "symbol": "DESH", - "rank": 4678, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "v-visa-inc", - "name": "Visa Inc", - "symbol": "V", - "rank": 4679, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnk-cinemark-holdings", - "name": "Cinemark Holdings", - "symbol": "CNK", - "rank": 4680, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dw-dawn-wars", - "name": "Dawn Wars", - "symbol": "DW", - "rank": 4681, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yndx-yandex-n-v", - "name": "Yandex N.V.", - "symbol": "YNDX", - "rank": 4682, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csx-coinstox-token", - "name": "Coinstox Token", - "symbol": "CSX", - "rank": 4683, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dnky-astrodonkey", - "name": "AstroDonkey", - "symbol": "DNKY", - "rank": 4684, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "noc-new-origin", - "name": "New Origin", - "symbol": "NOC", - "rank": 4685, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "im-internet-money", - "name": "Internet Money", - "symbol": "IM", - "rank": 4686, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "18c-block-18", - "name": "Block 18", - "symbol": "18C", - "rank": 4687, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "degn-degen", - "name": "Degen", - "symbol": "DEGN", - "rank": 4688, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pocc-poc-chain", - "name": "POC Chain", - "symbol": "POCC", - "rank": 4689, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "corgi-corgidoge", - "name": "Corgidoge", - "symbol": "CORGI", - "rank": 4690, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "geg-gegem", - "name": "GeGem", - "symbol": "GEG", - "rank": 4691, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gogeta-gogeta-inu", - "name": "Gogeta Inu", - "symbol": "GOGETA", - "rank": 4692, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tiki-tiki-token", - "name": "TIKI Token", - "symbol": "TIKI", - "rank": 4693, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogegf-dogegf", - "name": "DogeGF", - "symbol": "DOGEGF", - "rank": 4694, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lpl-linkpool", - "name": "LinkPool", - "symbol": "LPL", - "rank": 4695, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "luchow-lunachow", - "name": "LunaChow", - "symbol": "LUCHOW", - "rank": 4696, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swap-safeswap-online", - "name": "SafeSwap Online", - "symbol": "SWAP", - "rank": 4697, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wander-wanderlust", - "name": "Wanderlust", - "symbol": "WANDER", - "rank": 4698, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bet-earnbet", - "name": "EarnBet", - "symbol": "BET", - "rank": 4699, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "love-lovepot-token", - "name": "LovePot Token", - "symbol": "LOVE", - "rank": 4700, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sic-sicash", - "name": "SICash", - "symbol": "SIC", - "rank": 4701, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sim-simba-empire", - "name": "Simba Empire", - "symbol": "SIM", - "rank": 4702, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtdr-matador-token", - "name": "Matador Token", - "symbol": "MTDR", - "rank": 4703, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rayons-rayons-energy", - "name": "Rayons Energy", - "symbol": "RAYONS", - "rank": 4704, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xag-xrpalike-gene", - "name": "Xrpalike Gene", - "symbol": "XAG", - "rank": 4705, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "czz-classzz", - "name": "ClassZZ", - "symbol": "CZZ", - "rank": 4706, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fgsport-footballgo", - "name": "FootBallGo", - "symbol": "FGSPORT", - "rank": 4707, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecp-eclipse", - "name": "Eclipse", - "symbol": "ECP", - "rank": 4708, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spin-spin", - "name": "SPIN", - "symbol": "SPIN", - "rank": 4709, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bvl-bullswap-protocol", - "name": "Bullswap Protocol", - "symbol": "BVL", - "rank": 4710, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyfloki-baby-floki", - "name": "Baby Floki", - "symbol": "BABYFLOKI", - "rank": 4711, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tos-thingsoperatingsystem", - "name": "ThingsOperatingSystem", - "symbol": "TOS", - "rank": 4712, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsp-ballswap", - "name": "Ballswap", - "symbol": "BSP", - "rank": 4713, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "luffy-luffy-inu", - "name": "Luffy Inu", - "symbol": "LUFFY", - "rank": 4714, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tatnru-tatneft-3", - "name": "Tatneft-3", - "symbol": "TATNru", - "rank": 4715, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtk-metakings", - "name": "Metakings", - "symbol": "MTK", - "rank": 4716, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ai-artificial-intelligence", - "name": "Artificial Intelligence", - "symbol": "AI", - "rank": 4717, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cut-carbon-utility-token", - "name": "Carbon Utility Token", - "symbol": "CUT", - "rank": 4718, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "merge-merge", - "name": "Merge", - "symbol": "MERGE", - "rank": 4719, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "em-empow", - "name": "Empow", - "symbol": "EM", - "rank": 4720, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bt-bozkurt", - "name": "Bozkurt", - "symbol": "BT", - "rank": 4721, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "czusd-czusd", - "name": "CZUSD", - "symbol": "CZUSD", - "rank": 4722, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnop-memenopoly-money", - "name": "Memenopoly Money", - "symbol": "MNOP", - "rank": 4723, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsov-bitcoinsov", - "name": "BitcoinSoV", - "symbol": "BSOV", - "rank": 4724, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfl-crypto-fantasy-league", - "name": "Crypto Fantasy League", - "symbol": "CFL", - "rank": 4725, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etsy-etsy", - "name": "Etsy", - "symbol": "ETSY", - "rank": 4726, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vga-vegaswap", - "name": "Vegaswap", - "symbol": "VGA", - "rank": 4727, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goho-goosehonk", - "name": "GooseHonk", - "symbol": "GOHO", - "rank": 4728, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dtx-detox", - "name": "Detox", - "symbol": "DTX", - "rank": 4729, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dga-doge-gamer", - "name": "Doge Gamer", - "symbol": "DGA", - "rank": 4730, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sap-sapchain", - "name": "Sapchain", - "symbol": "SAP", - "rank": 4731, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bshib-buffedshiba", - "name": "BuffedShiba", - "symbol": "BSHIB", - "rank": 4732, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chaincade-chaincade", - "name": "ChainCade", - "symbol": "CHAINCADE", - "rank": 4733, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xmasbnb-christmas-bnb", - "name": "Christmas BNB", - "symbol": "XMASBNB", - "rank": 4734, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "horn-buffaloswap-horn", - "name": "BuffaloSwap HORN", - "symbol": "HORN", - "rank": 4735, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bear-honeybear", - "name": "HoneyBear", - "symbol": "BEAR", - "rank": 4736, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pinkm-pinkmoon", - "name": "PinkMoon", - "symbol": "PINKM", - "rank": 4737, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btca-bitcoin-anonymous", - "name": "Bitcoin Anonymous", - "symbol": "BTCA", - "rank": 4738, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hegg-hummingbird-egg-token", - "name": "Hummingbird Egg Token", - "symbol": "HEGG", - "rank": 4739, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rcw-ran-crypto-world-online", - "name": "Ran Crypto World Online", - "symbol": "RCW", - "rank": 4740, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfl-cryptoflow", - "name": "Cryptoflow", - "symbol": "CFL", - "rank": 4741, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "b8-binance8", - "name": "Binance8", - "symbol": "B8", - "rank": 4742, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vjc-venjocoin", - "name": "VENJOCOIN", - "symbol": "VJC", - "rank": 4743, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "esti-easticoin", - "name": "Easticoin", - "symbol": "ESTI", - "rank": 4744, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bmxx-multiplier-bsc", - "name": "Multiplier (BSC)", - "symbol": "BMXX", - "rank": 4745, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftstyle-nftstyle", - "name": "NFTStyle", - "symbol": "NFTSTYLE", - "rank": 4746, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "weenie-weenie-inu", - "name": "Weenie Inu", - "symbol": "WEENIE", - "rank": 4747, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbnft-babynftart", - "name": "BABYNFTART", - "symbol": "BBNFT", - "rank": 4748, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "squidgames-international-squid-games", - "name": "International Squid Games", - "symbol": "SQUIDGAMES", - "rank": 4749, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minisportz-minisportzilla", - "name": "MiniSportZilla", - "symbol": "MINISPORTZ", - "rank": 4750, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "look-lookscoin", - "name": "LooksCoin", - "symbol": "LOOK", - "rank": 4751, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfpro-yfpro-finance", - "name": "YFPRO Finance", - "symbol": "YFPRO", - "rank": 4752, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfe-yfe-money", - "name": "YFE Money", - "symbol": "YFE", - "rank": 4753, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sph-sphynx-network", - "name": "Sphynx Network", - "symbol": "SPH", - "rank": 4754, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "haven-haven-token", - "name": "Haven Token", - "symbol": "HAVEN", - "rank": 4755, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lama-llamaswap", - "name": "LlamaSwap", - "symbol": "LAMA", - "rank": 4756, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nosta-nosta", - "name": "Nosta", - "symbol": "NOSTA", - "rank": 4757, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mini-mini", - "name": "Mini", - "symbol": "MINI", - "rank": 4758, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goofydoge-goofydoge", - "name": "GoofyDoge", - "symbol": "GOOFYDOGE", - "rank": 4759, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oda-eiichiro-oda-inu", - "name": "Eiichiro Oda Inu", - "symbol": "ODA", - "rank": 4760, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bqt-blockchain-quotations-index-token", - "name": "Blockchain Quotations Index Token", - "symbol": "BQT", - "rank": 4761, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcnt-bincentive", - "name": "Bincentive", - "symbol": "BCNT", - "rank": 4762, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "marsinu-mars-inu", - "name": "Mars Inu", - "symbol": "MARSINU", - "rank": 4763, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lord-overlord", - "name": "Overlord", - "symbol": "LORD", - "rank": 4764, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scavo-scavo-token", - "name": "SCAVO Token", - "symbol": "SCAVO", - "rank": 4765, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "medibit-medibit", - "name": "MediBit", - "symbol": "MEDIBIT", - "rank": 4766, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mandi-mandi-token", - "name": "Mandi Token", - "symbol": "MANDI", - "rank": 4767, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "glb-golden-ball", - "name": "Golden Ball", - "symbol": "GLB", - "rank": 4768, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fml-formula", - "name": "FormulA", - "symbol": "FML", - "rank": 4769, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metabc-meta-billionaires-club", - "name": "Meta Billionaires Club", - "symbol": "METABC", - "rank": 4770, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ucd-unicandy", - "name": "UniCandy", - "symbol": "UCD", - "rank": 4771, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plc-pluton-chain", - "name": "Pluton Chain", - "symbol": "PLC", - "rank": 4772, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gxi-genexi", - "name": "Genexi", - "symbol": "GXI", - "rank": 4773, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aat-ascensionarcade", - "name": "AscensionArcade", - "symbol": "AAT", - "rank": 4774, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmx-caribmarsx", - "name": "CaribMarsX", - "symbol": "CMX", - "rank": 4775, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cytr-cyclops-treasure", - "name": "Cyclops Treasure", - "symbol": "CYTR", - "rank": 4776, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grand-the-grand-banks", - "name": "The Grand Banks", - "symbol": "GRAND", - "rank": 4777, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eft-easy-finance-token", - "name": "Easy Finance Token", - "symbol": "EFT", - "rank": 4778, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kxc-kingxchain", - "name": "KingXChain", - "symbol": "KXC", - "rank": 4779, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kgc-krypton-galaxy-coin", - "name": "Krypton Galaxy Coin", - "symbol": "KGC", - "rank": 4780, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "magic-cosmic-universe-magic-token", - "name": "Cosmic Universe Magic Token", - "symbol": "MAGIC", - "rank": 4781, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "upf-upfinity", - "name": "UpFinity", - "symbol": "UPF", - "rank": 4782, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "move-mover", - "name": "Mover", - "symbol": "MOVE", - "rank": 4783, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trat-tratok", - "name": "Tratok", - "symbol": "TRAT", - "rank": 4784, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "passive-passive-token", - "name": "Passive Token", - "symbol": "PASSIVE", - "rank": 4785, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ce-crypto-excellence", - "name": "Crypto Excellence", - "symbol": "CE", - "rank": 4786, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftndr-nftinder", - "name": "NFTinder", - "symbol": "NFTNDR", - "rank": 4787, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "squid-squid-finance", - "name": "SQUID Finance", - "symbol": "SQUID", - "rank": 4788, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jmt-jmtime", - "name": "JMTIME", - "symbol": "JMT", - "rank": 4789, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cash-caash", - "name": "Caash", - "symbol": "CASH", - "rank": 4790, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitci-bitcicoin", - "name": "Bitcicoin", - "symbol": "BITCI", - "rank": 4791, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ddr-digidinar", - "name": "DigiDinar", - "symbol": "DDR", - "rank": 4792, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dogez-dogezilla", - "name": "DogeZilla", - "symbol": "DOGEZ", - "rank": 4793, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "paa-palace", - "name": "Palace", - "symbol": "PAA", - "rank": 4794, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ouse-ouse", - "name": "Ouse", - "symbol": "OUSE", - "rank": 4795, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "everdot-everdot", - "name": "EverDot", - "symbol": "EVERDOT", - "rank": 4796, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "seg-solar-energy", - "name": "Solar Energy", - "symbol": "SEG", - "rank": 4797, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lty-ledgity", - "name": "Ledgity", - "symbol": "LTY", - "rank": 4798, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "waroo-superwhale", - "name": "SuperWhale", - "symbol": "WAROO", - "rank": 4799, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "honor-honor-token", - "name": "Honor Token", - "symbol": "HONOR", - "rank": 4800, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bepr-blockchain-euro-project", - "name": "Blockchain Euro Project", - "symbol": "BEPR", - "rank": 4801, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "million-millionaire-maker", - "name": "Millionaire Maker", - "symbol": "MILLION", - "rank": 4802, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "delos-delos", - "name": "Delos", - "symbol": "DELOS", - "rank": 4803, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "money-moneytree", - "name": "MoneyTree", - "symbol": "MONEY", - "rank": 4804, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sberru-sberbank-rossii", - "name": "Sberbank Rossii", - "symbol": "SBERru", - "rank": 4805, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rep-repsol", - "name": "Repsol", - "symbol": "REP", - "rank": 4806, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mng-moongames", - "name": "MOONGAMES", - "symbol": "MNG", - "rank": 4807, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbecom-shebolleth-commerce", - "name": "SheBollETH Commerce", - "symbol": "SBECOM", - "rank": 4808, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yo-yohero", - "name": "YoHero", - "symbol": "YO", - "rank": 4809, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "momento-momento", - "name": "Momento", - "symbol": "MOMENTO", - "rank": 4810, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcontent-mcontent", - "name": "MContent", - "symbol": "MCONTENT", - "rank": 4811, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bceo-bitceo", - "name": "bitCEO", - "symbol": "BCEO", - "rank": 4812, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roboshib-roboshib", - "name": "RoboShib", - "symbol": "ROBOSHIB", - "rank": 4813, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rides-bit-rides", - "name": "Bit Rides", - "symbol": "RIDES", - "rank": 4814, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stimmy-stimmy", - "name": "$STIMMY", - "symbol": "STIMMY", - "rank": 4815, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idh-indahash", - "name": "indaHash", - "symbol": "IDH", - "rank": 4816, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "x22-x22", - "name": "X22", - "symbol": "X22", - "rank": 4817, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dexm-dexmex", - "name": "Dexmex", - "symbol": "DEXM", - "rank": 4818, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "melo-melo-token", - "name": "Melo Token", - "symbol": "MELO", - "rank": 4819, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "beluga-belugaswap", - "name": "Belugaswap", - "symbol": "BELUGA", - "rank": 4820, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kyan-kyanite", - "name": "KYANITE", - "symbol": "KYAN", - "rank": 4821, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dingo-dingocoin", - "name": "Dingocoin", - "symbol": "DINGO", - "rank": 4822, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "jaiho-jaiho-crypto", - "name": "Jaiho Crypto", - "symbol": "JAIHO", - "rank": 4823, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flake-iceflake-finance", - "name": "IceFlake Finance", - "symbol": "FLAKE", - "rank": 4824, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xra-xriba", - "name": "Xriba", - "symbol": "XRA", - "rank": 4825, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vibe-vibe-token", - "name": "Vibe Token", - "symbol": "VIBE", - "rank": 4826, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssa-sistema-pjsfc-gdr", - "name": "Sistema PJSFC GDR", - "symbol": "SSA", - "rank": 4827, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trd-the-realm-defenders", - "name": "The Realm Defenders", - "symbol": "TRD", - "rank": 4828, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "supdog-superdoge", - "name": "SUPERDOGE", - "symbol": "SUPDOG", - "rank": 4829, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fex-fidex-exchange", - "name": "FIDEX Exchange", - "symbol": "FEX", - "rank": 4830, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fnk-fnkcom", - "name": "Fnk.com", - "symbol": "FNK", - "rank": 4831, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gdt-gorilla-diamond", - "name": "Gorilla Diamond", - "symbol": "GDT", - "rank": 4832, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sltrbt-slittlerabbit", - "name": "sLittlerabbit", - "symbol": "SLTRBT", - "rank": 4833, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "efloki-elonflokiinu", - "name": "ElonFlokiInu", - "symbol": "EFLOKI", - "rank": 4834, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alny-alnylam-pharmaceuticals-inc", - "name": "Alnylam Pharmaceuticals, Inc.", - "symbol": "ALNY", - "rank": 4835, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "matilda-matilda-vip", - "name": "Matilda Vip", - "symbol": "MATILDA", - "rank": 4836, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cflt-coinflect", - "name": "Coinflect", - "symbol": "CFLT", - "rank": 4837, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inx-infinitx", - "name": "InfinitX", - "symbol": "INX", - "rank": 4838, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mitten-mittens", - "name": "Mittens", - "symbol": "MITTEN", - "rank": 4839, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccn-collegecoinnetwork", - "name": "CollegeCoinNetwork", - "symbol": "CCN", - "rank": 4840, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ebird-early-bird", - "name": "Early Bird", - "symbol": "EBIRD", - "rank": 4841, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hono-hono", - "name": "Hono", - "symbol": "HONO", - "rank": 4842, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "toshinori-toshinori-inu", - "name": "Toshinori Inu", - "symbol": "TOSHINORI", - "rank": 4843, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lland-lyfe-land", - "name": "Lyfe Land", - "symbol": "LLAND", - "rank": 4844, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dms-dimsum", - "name": "DimSum", - "symbol": "DMS", - "rank": 4845, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boltt-boltt-coin", - "name": "Boltt Coin", - "symbol": "BOLTT", - "rank": 4846, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "g2-g2-crypto-gaming-amp-lottery", - "name": "G2 Crypto Gaming \u0026 Lottery", - "symbol": "G2", - "rank": 4847, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cien-ciena", - "name": "Ciena", - "symbol": "CIEN", - "rank": 4848, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "saitax-saitamax", - "name": "SaitamaX", - "symbol": "SAITAX", - "rank": 4849, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "true-trueburn", - "name": "TrueBurn", - "symbol": "TRUE", - "rank": 4850, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "srx-syrex", - "name": "Syrex", - "symbol": "SRX", - "rank": 4851, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sape-stadium-ape", - "name": "Stadium Ape", - "symbol": "SAPE", - "rank": 4852, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fundx-funder-one", - "name": "Funder One", - "symbol": "FUNDX", - "rank": 4853, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bizz-bizzcoin", - "name": "BIZZCOIN", - "symbol": "BIZZ", - "rank": 4854, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boyz-beachboyz", - "name": "BeachBoyz", - "symbol": "BOYZ", - "rank": 4855, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cla-candela-coin", - "name": "Candela Coin", - "symbol": "CLA", - "rank": 4856, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "si14-si14bet", - "name": "Si14Bet", - "symbol": "Si14", - "rank": 4857, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "peech-peachfinance", - "name": "Peach.Finance", - "symbol": "PEECH", - "rank": 4858, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "worm-wormfi", - "name": "WormFi", - "symbol": "WORM", - "rank": 4859, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ifc-infinitycash", - "name": "InfinityCash", - "symbol": "IFC", - "rank": 4860, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cac-cac-40-token", - "name": "CAC 40 Token", - "symbol": "CAC", - "rank": 4861, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sky-sky-shiba", - "name": "Sky Shiba", - "symbol": "SKY", - "rank": 4862, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "me-missedeverything", - "name": "MissedEverything", - "symbol": "ME", - "rank": 4863, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "simbainu-simba-inu", - "name": "Simba Inu", - "symbol": "SIMBAINU", - "rank": 4864, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "htmoon-htmoon-fomo", - "name": "HTMoon FOMO", - "symbol": "HTMOON", - "rank": 4865, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mok-mocktailswap", - "name": "MocktailSwap", - "symbol": "MOK", - "rank": 4866, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rokt-rocket", - "name": "Rocket", - "symbol": "ROKT", - "rank": 4867, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bank-us-crypto-bank", - "name": "US Crypto Bank", - "symbol": "BANK", - "rank": 4868, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kitty-kittycoin", - "name": "KittyCoin", - "symbol": "KITTY", - "rank": 4869, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ra-radao", - "name": "RaDAO", - "symbol": "RA", - "rank": 4870, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pnl-truepnl-token", - "name": "TruePNL Token", - "symbol": "PNL", - "rank": 4871, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bumn-bumoon", - "name": "BuMooN", - "symbol": "BUMN", - "rank": 4872, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apple-appleswap", - "name": "AppleSwap", - "symbol": "APPLE", - "rank": 4873, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oje-oje-token", - "name": "Oje Token", - "symbol": "OJE", - "rank": 4874, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbr-metabullrun", - "name": "MetaBullRun", - "symbol": "MBR", - "rank": 4875, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rpt-rug-proof", - "name": "Rug Proof", - "symbol": "RPT", - "rank": 4876, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dash-doordash-inc", - "name": "DoorDash Inc", - "symbol": "DASH", - "rank": 4877, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "becoin-bepay-finance", - "name": "bePAY Finance", - "symbol": "BECOIN", - "rank": 4878, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dgat-doge-army-token", - "name": "Doge Army Token", - "symbol": "DGAT", - "rank": 4879, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "balls-spaceballs", - "name": "SpaceBalls", - "symbol": "BALLS", - "rank": 4880, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdog-big-dog-coin", - "name": "Big Dog Coin", - "symbol": "BDOG", - "rank": 4881, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apenout-apenout", - "name": "APENOUT", - "symbol": "APENOUT", - "rank": 4882, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbeth-babyethereum", - "name": "BabyEthereum", - "symbol": "BBETH", - "rank": 4883, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dac-degen-ape-club", - "name": "Degen Ape Club", - "symbol": "DAC", - "rank": 4884, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sct-solclout", - "name": "SolClout", - "symbol": "SCT", - "rank": 4885, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pyr-pyroblock", - "name": "Pyroblock", - "symbol": "PYR", - "rank": 4886, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "booty-pirate-dice", - "name": "Pirate Dice", - "symbol": "BOOTY", - "rank": 4887, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pink-pink-panther", - "name": "PINK PANTHER", - "symbol": "PINK", - "rank": 4888, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dlegends-my-defi-legends", - "name": "My DeFi Legends", - "symbol": "DLEGENDS", - "rank": 4889, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uc-youlive-coin", - "name": "YouLive Coin", - "symbol": "UC", - "rank": 4890, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rig-transocean", - "name": "Transocean", - "symbol": "RIG", - "rank": 4891, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "damn-damn-token", - "name": "DAMN Token", - "symbol": "DAMN", - "rank": 4892, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inu-koka-inu", - "name": "KOKA INU", - "symbol": "INU", - "rank": 4893, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "awg-aurusgold", - "name": "AurusGOLD", - "symbol": "AWG", - "rank": 4894, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egg-egg-protocol", - "name": "EGG Protocol", - "symbol": "EGG", - "rank": 4895, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "corgi-corgi-inu", - "name": "Corgi inu", - "symbol": "CORGI", - "rank": 4896, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "factory-meme-coin-factory", - "name": "Meme Coin Factory", - "symbol": "FACTORY", - "rank": 4897, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "voo-vanguard-s-p-500-etf", - "name": "Vanguard S\u0026P 500 ETF", - "symbol": "VOO", - "rank": 4898, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "saninu-santa-inu", - "name": "Santa Inu", - "symbol": "SANINU", - "rank": 4899, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "redfloki-red-floki", - "name": "Red Floki", - "symbol": "REDFLOKI", - "rank": 4900, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brnk-brank", - "name": "Brank", - "symbol": "BRNK", - "rank": 4901, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flokimonk-floki-monk", - "name": "Floki Monk", - "symbol": "FLOKIMONK", - "rank": 4902, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "viva-viva-classic", - "name": "VIVA Classic", - "symbol": "VIVA", - "rank": 4903, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "777-777-jackpot", - "name": "777 Jackpot", - "symbol": "777", - "rank": 4904, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "chg-charg-coin", - "name": "Charg Coin", - "symbol": "CHG", - "rank": 4905, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pow-powerada", - "name": "PowerADA", - "symbol": "POW", - "rank": 4906, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jnj-johnson-johnson", - "name": "Johnson \u0026 Johnson", - "symbol": "JNJ", - "rank": 4907, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyusdt-babyusdt", - "name": "BabyUSDT", - "symbol": "BABYUSDT", - "rank": 4908, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flo-floki-rocket", - "name": "FLOKI ROCKET", - "symbol": "FLO", - "rank": 4909, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rwsc-rewardscoin", - "name": "RewardsCoin", - "symbol": "RWSC", - "rank": 4910, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crop-farmerdoge", - "name": "FarmerDoge", - "symbol": "CROP", - "rank": 4911, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mndcc-mondo-community-coin", - "name": "Mondo Community Coin", - "symbol": "MNDCC", - "rank": 4912, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bets-betswamp", - "name": "Betswamp", - "symbol": "BETS", - "rank": 4913, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rat-ratcoin", - "name": "RATCOIN", - "symbol": "RAT", - "rank": 4914, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dmr-dreamr-platform-token", - "name": "Dreamr Platform Token", - "symbol": "DMR", - "rank": 4915, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qbu-quannabu", - "name": "Quannabu", - "symbol": "QBU", - "rank": 4916, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "risq-risq-protocol", - "name": "Risq Protocol", - "symbol": "RISQ", - "rank": 4917, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wvg0-wrapped-virgin-gen-0-cryptokittties", - "name": "Wrapped Virgin Gen-0 CryptoKittties", - "symbol": "WVG0", - "rank": 4918, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sq-square-cl-a", - "name": "Square Cl A", - "symbol": "SQ", - "rank": 4919, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eph-epoch-token", - "name": "Epoch Token", - "symbol": "EPH", - "rank": 4920, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mystic-mystic-warrior", - "name": "Mystic Warrior", - "symbol": "MYSTIC", - "rank": 4921, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bin-buildin", - "name": "Buildin", - "symbol": "BIN", - "rank": 4922, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kfx-knoxfs", - "name": "KnoxFS", - "symbol": "KFX", - "rank": 4923, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vlk-vulkania", - "name": "Vulkania", - "symbol": "VLK", - "rank": 4924, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xuc-exchange-union", - "name": "Exchange Union", - "symbol": "XUC", - "rank": 4925, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "azn-astrazeneca-usd", - "name": "AstraZeneca - USD", - "symbol": "AZN", - "rank": 4926, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "retire-retire-token", - "name": "Retire Token", - "symbol": "RETIRE", - "rank": 4927, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tusk-mammothbet", - "name": "Mammoth.Bet", - "symbol": "TUSK", - "rank": 4928, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ba-boeing", - "name": "Boeing", - "symbol": "BA", - "rank": 4929, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnt-cryption-network", - "name": "Cryption Network", - "symbol": "CNT", - "rank": 4930, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "htbx-heat-biologics", - "name": "Heat Biologics", - "symbol": "HTBX", - "rank": 4931, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rfc-royal-flush-coin", - "name": "Royal Flush Coin", - "symbol": "RFC", - "rank": 4932, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbgc-big-bang-game-coin", - "name": "Big Bang Game Coin", - "symbol": "BBGC", - "rank": 4933, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spade-polygonfarm-finance", - "name": "PolygonFarm Finance", - "symbol": "SPADE", - "rank": 4934, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "twlo-twilio-cl-a", - "name": "Twilio Cl A", - "symbol": "TWLO", - "rank": 4935, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pshibax-pumpshibax", - "name": "PumpShibaX", - "symbol": "PSHIBAX", - "rank": 4936, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pfid-pofid-dao", - "name": "Pofid Dao", - "symbol": "PFID", - "rank": 4937, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "housed-house-of-the-dragon", - "name": "HOUSE OF THE DRAGON", - "symbol": "HOUSED", - "rank": 4938, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drk-draken", - "name": "Draken", - "symbol": "DRK", - "rank": 4939, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "yndxru-yandex-nv", - "name": "Yandex N.V.", - "symbol": "YNDXru", - "rank": 4940, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qoge-qogecoin", - "name": "Qogecoin", - "symbol": "QOGE", - "rank": 4941, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "chefcake-chefcake", - "name": "ChefCake", - "symbol": "CHEFCAKE", - "rank": 4942, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpay-wallet-pay", - "name": "Wallet Pay", - "symbol": "XPAY", - "rank": 4943, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pye-creampye", - "name": "Creampye", - "symbol": "PYE", - "rank": 4944, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zcc-zucoinchain", - "name": "ZuCoinChain", - "symbol": "ZCC", - "rank": 4945, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hohoho-santa-elon", - "name": "Santa Elon", - "symbol": "HOHOHO", - "rank": 4946, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "whackd-whackd", - "name": "WHACKD", - "symbol": "WHACKD", - "rank": 4947, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sop-sopay", - "name": "SoPay", - "symbol": "SOP", - "rank": 4948, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pypl-paypal-holdings", - "name": "PayPal Holdings", - "symbol": "PYPL", - "rank": 4949, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meow-solcats", - "name": "Solcats", - "symbol": "MEOW", - "rank": 4950, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "earth-earthchain", - "name": "EarthChain", - "symbol": "EARTH", - "rank": 4951, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gshib-god-shiba-token", - "name": "God Shiba Token", - "symbol": "GSHIB", - "rank": 4952, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alpha-wolf", - "name": "WOLF", - "symbol": "ALPHA", - "rank": 4953, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bwh-baby-white-hamster", - "name": "Baby White Hamster", - "symbol": "$BWH", - "rank": 4954, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "liq-liquidity-bot-token", - "name": "Liquidity Bot Token", - "symbol": "LIQ", - "rank": 4955, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "art-artonline", - "name": "ArtOnline", - "symbol": "ART", - "rank": 4956, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snft-seedswap", - "name": "SeedSwap", - "symbol": "SNFT", - "rank": 4957, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kub-richamster-token", - "name": "Richamster Token", - "symbol": "KUB", - "rank": 4958, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cusdt-compound-usdt", - "name": "Compound USDT", - "symbol": "CUSDT", - "rank": 4959, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddn-den-domains", - "name": "Den Domains", - "symbol": "DDN", - "rank": 4960, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "freemoon-freemoon", - "name": "Freemoon", - "symbol": "FREEMOON", - "rank": 4961, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mex-mex", - "name": "MEX", - "symbol": "MEX", - "rank": 4962, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "work-the-employment-commons-work-token", - "name": "The Employment Commons Work Token", - "symbol": "WORK", - "rank": 4963, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "edc-blockchain", - "name": "EDC Blockchain", - "symbol": "EDC", - "rank": 4964, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gg-galaxygoggle-dao", - "name": "GalaxyGoggle DAO", - "symbol": "GG", - "rank": 4965, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "carma-carma-coin", - "name": "Carma Coin", - "symbol": "CARMA", - "rank": 4966, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ppegg-polygon-parrot-egg", - "name": "Polygon Parrot Egg", - "symbol": "PPEGG", - "rank": 4967, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cax-chainrobloxme", - "name": "ChainRoblox.me", - "symbol": "CAX", - "rank": 4968, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "x-gibx-swap", - "name": "GIBX Swap", - "symbol": "X", - "rank": 4969, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flokiz-flokizap", - "name": "FlokiZap", - "symbol": "FLOKIZ", - "rank": 4970, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfe-decentralized-finance-europe", - "name": "Decentralized Finance Europe", - "symbol": "DFE", - "rank": 4971, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "afloki-angryfloki", - "name": "AngryFloki", - "symbol": "$AFLOKI", - "rank": 4972, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stars-starship-token", - "name": "StarShip Token", - "symbol": "STARS", - "rank": 4973, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sibnru-gazprom-neft-pjsc", - "name": "GAZPROM NEFT PJSC", - "symbol": "SIBNru", - "rank": 4974, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "app-dappsy", - "name": "Dappsy", - "symbol": "APP", - "rank": 4975, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dwz-defi-wizard", - "name": "DeFi Wizard", - "symbol": "DWZ", - "rank": 4976, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cabo-catbonk", - "name": "Catbonk", - "symbol": "CABO", - "rank": 4977, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "end-endgame-token", - "name": "Endgame Token", - "symbol": "END", - "rank": 4978, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wsdq-wasdaq", - "name": "WASDAQ", - "symbol": "WSDQ", - "rank": 4979, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blue-tipblue", - "name": "Tip.Blue", - "symbol": "BLUE", - "rank": 4980, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reum-rewardeum", - "name": "Rewardeum", - "symbol": "REUM", - "rank": 4981, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fum-fumoney", - "name": "FUMoney", - "symbol": "FUM", - "rank": 4982, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vlty-vaulty-token", - "name": "Vaulty Token", - "symbol": "VLTY", - "rank": 4983, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dctd-dctdao", - "name": "DCTDAO", - "symbol": "DCTD", - "rank": 4984, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shiny-shiny", - "name": "Shiny", - "symbol": "SHINY", - "rank": 4985, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bct-bitcointrust", - "name": "BitcoinTrust", - "symbol": "BCT", - "rank": 4986, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mpt-mars-panda-world", - "name": "Mars Panda World", - "symbol": "MPT", - "rank": 4987, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drs-dragon-slayer", - "name": "Dragon Slayer", - "symbol": "DRS", - "rank": 4988, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nlife-night-life-crypto", - "name": "Night Life Crypto", - "symbol": "NLIFE", - "rank": 4989, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mwd-madcredits", - "name": "MadCredits", - "symbol": "MWD", - "rank": 4990, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbf-steakbank-finance", - "name": "SteakBank Finance", - "symbol": "SBF", - "rank": 4991, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "exo-exohood", - "name": "Exohood", - "symbol": "EXO", - "rank": 4992, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mic3-mousecoin", - "name": "Mousecoin", - "symbol": "MIC3", - "rank": 4993, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dok-dok-token", - "name": "DOK Token", - "symbol": "DOK", - "rank": 4994, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kimetsu-kimetsu-inu", - "name": "Kimetsu Inu", - "symbol": "KIMETSU", - "rank": 4995, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atvi-activision-blizzard-inc", - "name": "Activision Blizzard Inc", - "symbol": "ATVI", - "rank": 4996, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mmp-moon-maker-protocol", - "name": "Moon Maker Protocol", - "symbol": "MMP", - "rank": 4997, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wasp-wanswap", - "name": "WanSwap", - "symbol": "WASP", - "rank": 4998, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btnyx-bitonyx-token", - "name": "BitOnyx Token", - "symbol": "BTNYX", - "rank": 4999, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bttr-bittracksystems", - "name": "BittrackSystems", - "symbol": "BTTR", - "rank": 5000, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "jaws-autoshark", - "name": "AutoShark", - "symbol": "JAWS", - "rank": 5001, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "terra-avaterra", - "name": "Avaterra", - "symbol": "TERRA", - "rank": 5002, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nebula-nebulatoken", - "name": "NebulaToken", - "symbol": "NEBULA", - "rank": 5003, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "octa-octans", - "name": "Octans", - "symbol": "OCTA", - "rank": 5004, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ratrace-ratrace", - "name": "RatRace", - "symbol": "RATRACE", - "rank": 5005, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "silva-silva-token", - "name": "Silva Token", - "symbol": "SILVA", - "rank": 5006, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnt-greentrust", - "name": "GreenTrust", - "symbol": "GNT", - "rank": 5007, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pup-polypup", - "name": "PolyPup", - "symbol": "PUP", - "rank": 5008, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aje-ajeverse", - "name": "Ajeverse", - "symbol": "AJE", - "rank": 5009, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dirty-dirtyfinance", - "name": "dirty.finance", - "symbol": "DIRTY", - "rank": 5010, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rfg-refugees-token", - "name": "Refugees Token", - "symbol": "RFG", - "rank": 5011, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "avs-algovest", - "name": "AlgoVest", - "symbol": "AVS", - "rank": 5012, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kcake-kittycake", - "name": "KittyCake", - "symbol": "KCAKE", - "rank": 5013, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ample-ampleswap", - "name": "AmpleSwap", - "symbol": "AMPLE", - "rank": 5014, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dust-dust", - "name": "Dust", - "symbol": "DUST", - "rank": 5015, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdby-sadbaby", - "name": "Sadbaby", - "symbol": "SDBY", - "rank": 5016, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tpos-the-philosophers-stone", - "name": "The Philosophers Stone", - "symbol": "TPOS", - "rank": 5017, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gla-galaxy-adventure", - "name": "Galaxy Adventure", - "symbol": "GLA", - "rank": 5018, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trail-polkatrail", - "name": "Polkatrail", - "symbol": "TRAIL", - "rank": 5019, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "woof-shibance-token", - "name": "Shibance Token", - "symbol": "WOOF", - "rank": 5020, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clvs-clovis-oncology-inc", - "name": "Clovis Oncology, Inc.", - "symbol": "CLVS", - "rank": 5021, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hbd-hive-backed-dollar", - "name": "Hive Backed Dollar", - "symbol": "HBD", - "rank": 5022, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elm-elements", - "name": "Elements", - "symbol": "ELM", - "rank": 5023, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aureusrh-aureus-token", - "name": "Aureus Token", - "symbol": "AUREUSRH", - "rank": 5024, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bright-bright-token", - "name": "Bright Token", - "symbol": "BRIGHT", - "rank": 5025, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cashio-cashio-token", - "name": "Cashio Token", - "symbol": "CASHIO", - "rank": 5026, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hood-hoodler", - "name": "Hoodler", - "symbol": "HOOD", - "rank": 5027, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "own-ownly", - "name": "Ownly", - "symbol": "OWN", - "rank": 5028, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfn-definova", - "name": "DefiNova", - "symbol": "DFN", - "rank": 5029, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "wagmi-wagmi-on-solana", - "name": "WAGMI On Solana", - "symbol": "WAGMI", - "rank": 5030, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ndaq-nasdaq", - "name": "Nasdaq", - "symbol": "NDAQ", - "rank": 5031, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "it40-ftse-mib-40-token", - "name": "FTSE MIB 40 Token", - "symbol": "IT40", - "rank": 5032, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grosh-grosh", - "name": "Grosh", - "symbol": "Grosh", - "rank": 5033, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lkohru-lukoil", - "name": "LUKOIL", - "symbol": "LKOHru", - "rank": 5034, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bill-bill-token", - "name": "Bill Token", - "symbol": "BILL", - "rank": 5035, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogeriseup-doge-rise-up", - "name": "Doge Rise Up", - "symbol": "DOGERISEUP", - "rank": 5036, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsp-the-spartans", - "name": "The Spartans", - "symbol": "TSP", - "rank": 5037, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smx-somax", - "name": "Somax", - "symbol": "SMX", - "rank": 5038, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scam-simple-cool-automatic-money", - "name": "Simple Cool Automatic Money", - "symbol": "SCAM", - "rank": 5039, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eshib-shiba-elon", - "name": "Shiba Elon", - "symbol": "ESHIB", - "rank": 5040, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "epc-electronic-pk-chain", - "name": "Electronic PK Chain", - "symbol": "EPC", - "rank": 5041, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zeropay-zeropay-finance", - "name": "ZEROPAY FINANCE", - "symbol": "ZEROPAY", - "rank": 5042, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adat-adadex-tools", - "name": "Adadex Tools", - "symbol": "ADAT", - "rank": 5043, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "po-playersonly", - "name": "PlayersOnly", - "symbol": "PO", - "rank": 5044, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bhig-buckhathcoin", - "name": "BuckHathCoin", - "symbol": "BHIG", - "rank": 5045, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hmoon-hellmoon", - "name": "HELLMOON", - "symbol": "HMOON", - "rank": 5046, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meals-meals", - "name": "MEALS", - "symbol": "MEALS", - "rank": 5047, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bhd-bitcoinhd", - "name": "BitcoinHD", - "symbol": "BHD", - "rank": 5048, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "meong-meong-token", - "name": "Meong Token", - "symbol": "MEONG", - "rank": 5049, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "binom-binom-protocol", - "name": "Binom Protocol", - "symbol": "BINOM", - "rank": 5050, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "upbnb-upbnb", - "name": "upBNB", - "symbol": "UPBNB", - "rank": 5051, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "condoms-solcondoms", - "name": "SolCondoms", - "symbol": "CONDOMS", - "rank": 5052, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alt-altcrusaders", - "name": "AltCrusaders", - "symbol": "ALT", - "rank": 5053, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "am-alternatemoney", - "name": "AlternateMoney", - "symbol": "AM", - "rank": 5054, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gf-good-fire", - "name": "Good Fire", - "symbol": "GF", - "rank": 5055, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hotzilla-hotzilla", - "name": "HotZilla", - "symbol": "HOTZILLA", - "rank": 5056, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abic-arabic", - "name": "Arabic", - "symbol": "ABIC", - "rank": 5057, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ibg-ibg-eth", - "name": "iBG (ETH)", - "symbol": "IBG", - "rank": 5058, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ytv-ytv-coin", - "name": "YTV Coin", - "symbol": "YTV", - "rank": 5059, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "con-coin-of-nature", - "name": "Coin of Nature", - "symbol": "CON", - "rank": 5060, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lgf-let39s-go-farming", - "name": "Let's Go Farming", - "symbol": "LGF", - "rank": 5061, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fish-penguin-party-fish", - "name": "Penguin Party Fish", - "symbol": "FISH", - "rank": 5062, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lly-eli-lilly-co", - "name": "Eli Lilly \u0026 Co", - "symbol": "LLY", - "rank": 5063, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "devl-devil-token", - "name": "Devil Token", - "symbol": "DEVL", - "rank": 5064, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "c0-carboneco", - "name": "CarbonEco", - "symbol": "C0", - "rank": 5065, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kali-kalissa", - "name": "Kalissa", - "symbol": "KALI", - "rank": 5066, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cre-creator-protocol", - "name": "Creator Protocol", - "symbol": "CRE", - "rank": 5067, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pika-pika", - "name": "Pika", - "symbol": "PIKA", - "rank": 5068, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pinu-pirate-inu", - "name": "Pirate Inu", - "symbol": "PINU", - "rank": 5069, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kroot-k-root-wallet", - "name": "K-Root Wallet", - "symbol": "KROOT", - "rank": 5070, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "agf-augmented-finance", - "name": "Augmented Finance", - "symbol": "AGF", - "rank": 5071, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spice-spice-eth", - "name": "SPICE", - "symbol": "SPICE", - "rank": 5072, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spkl-spookeletons-token", - "name": "Spookeletons Token", - "symbol": "SPKL", - "rank": 5073, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metapets-metapets", - "name": "MetaPets", - "symbol": "METAPETS", - "rank": 5074, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "key-key", - "name": "KEY", - "symbol": "KEY", - "rank": 5075, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wiki-wiki-token", - "name": "Wiki Token", - "symbol": "WIKI", - "rank": 5076, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "puppy-puppy-token", - "name": "Puppy Token", - "symbol": "PUPPY", - "rank": 5077, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cmd-comodo-coin", - "name": "Comodo Coin", - "symbol": "CMD", - "rank": 5078, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ibm-international-business-machines-corp", - "name": "International Business Machines Corp", - "symbol": "IBM", - "rank": 5079, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wrc-whiterockcasino", - "name": "WhiteRockCasino", - "symbol": "WRC", - "rank": 5080, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sngsru-surgutneftegaz", - "name": "Surgutneftegaz", - "symbol": "SNGSru", - "rank": 5081, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yffs-yffs-finance", - "name": "YFFS Finance", - "symbol": "YFFS", - "rank": 5082, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wipe-wipemyass", - "name": "WipeMyAss", - "symbol": "WIPE", - "rank": 5083, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gent-genesis-token", - "name": "Genesis Token", - "symbol": "GENT", - "rank": 5084, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nft-neftipedia", - "name": "NEFTiPEDiA", - "symbol": "NFT", - "rank": 5085, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbnana-babybanana", - "name": "BabyBanana", - "symbol": "BBNANA", - "rank": 5086, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cabag-cabbagecash", - "name": "Cabbage.Cash", - "symbol": "CABAG", - "rank": 5087, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kill-memekiller", - "name": "MemeKiller", - "symbol": "KILL", - "rank": 5088, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xiasi-xiasi-inu", - "name": "Xiasi Inu", - "symbol": "XIASI", - "rank": 5089, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dgstb-dogestribute", - "name": "Dogestribute", - "symbol": "DGSTB", - "rank": 5090, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnbeer-bnbeer", - "name": "BNBeer", - "symbol": "BNBEER", - "rank": 5091, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safuyield-safuyield-protocol", - "name": "SafuYield Protocol", - "symbol": "SAFUYIELD", - "rank": 5092, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scix-scientix", - "name": "Scientix", - "symbol": "SCIX", - "rank": 5093, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metashib-metashib-token", - "name": "MetaShib Token", - "symbol": "METASHIB", - "rank": 5094, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roar-roarts", - "name": "RoArts", - "symbol": "ROAR", - "rank": 5095, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bone-bone-token", - "name": "Bone Token", - "symbol": "BONE", - "rank": 5096, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drag-drachen-lord", - "name": "Drachen Lord", - "symbol": "DRAG", - "rank": 5097, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gamebox-gamebox", - "name": "Gamebox", - "symbol": "GAMEBOX", - "rank": 5098, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mommyusdt-mommyusdt", - "name": "MommyUSDT", - "symbol": "MOMMYUSDT", - "rank": 5099, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "synd-syndex", - "name": "SynDEX", - "symbol": "SYND", - "rank": 5100, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vektor-vektor-group", - "name": "VEKTOR Group", - "symbol": "VEKTOR", - "rank": 5101, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safearn-safe-earn", - "name": "Safe Earn", - "symbol": "SAFEARN", - "rank": 5102, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shiborigin-shiborigin", - "name": "ShibOrigin", - "symbol": "SHIBORIGIN", - "rank": 5103, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dlana-dogelana", - "name": "Dogelana", - "symbol": "DLANA", - "rank": 5104, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qdrop-quizdrop", - "name": "QuizDrop", - "symbol": "QDROP", - "rank": 5105, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "efin-efin", - "name": "eFIN", - "symbol": "eFIN", - "rank": 5106, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "unr-unirealchain", - "name": "Unirealchain", - "symbol": "UNR", - "rank": 5107, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alist-a-list-royale", - "name": "A-List Royale", - "symbol": "ALIST", - "rank": 5108, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slyr-ro-slayers", - "name": "RO Slayers", - "symbol": "SLYR", - "rank": 5109, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yland-yearn-land", - "name": "Yearn Land", - "symbol": "YLAND", - "rank": 5110, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spay-smart-payment", - "name": "Smart Payment", - "symbol": "SPAY", - "rank": 5111, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zcc-zccoin", - "name": "ZcCoin", - "symbol": "ZCC", - "rank": 5112, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "msmi2-mini-safemoon-inu-v2", - "name": "Mini Safemoon Inu V2", - "symbol": "MSMI2", - "rank": 5113, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "starx-starx-token", - "name": "STARX TOKEN", - "symbol": "STARX", - "rank": 5114, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "bakedcake-bakedcake-finance", - "name": "BakedCake Finance", - "symbol": "BAKEDCAKE", - "rank": 5115, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gtfo-dumpbuster", - "name": "DumpBuster", - "symbol": "GTFO", - "rank": 5116, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "marvin-marvininu", - "name": "MarvinInu", - "symbol": "MARVIN", - "rank": 5117, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ksc-kibastablecapital", - "name": "KibaStableCapital", - "symbol": "KSC", - "rank": 5118, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alrsru-alrosa", - "name": "ALROSA", - "symbol": "ALRSru", - "rank": 5119, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsc33-bsc33dao", - "name": "BSC33DAO", - "symbol": "BSC33", - "rank": 5120, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stabl-stable-coin", - "name": "Stable Coin", - "symbol": "STABL", - "rank": 5121, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "koel-koel-coin", - "name": "Koel Coin", - "symbol": "KOEL", - "rank": 5122, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nio-nio-limited", - "name": "NIO Limited", - "symbol": "NIO", - "rank": 5123, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nova-nova", - "name": "NOVA", - "symbol": "NOVA", - "rank": 5124, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfc-dfc", - "name": "DFC", - "symbol": "DFC", - "rank": 5125, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dxsanta-doxxed-santa", - "name": "Doxxed Santa", - "symbol": "DXSANTA", - "rank": 5126, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yge-yu-gi-eth", - "name": "Yu-Gi-Eth!", - "symbol": "YGE", - "rank": 5127, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pivxl-pivx-lite", - "name": "Pivx Lite", - "symbol": "PIVXL", - "rank": 5128, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "watt-energous-corporation", - "name": "Energous Corporation", - "symbol": "WATT", - "rank": 5129, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reap-reapit", - "name": "REAPit", - "symbol": "REAP", - "rank": 5130, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xtra-taraplatform", - "name": "TaraPlatform", - "symbol": "XTRA", - "rank": 5131, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "9988-alibaba-group-holding-limited", - "name": "Alibaba Group Holding Limited", - "symbol": "9988", - "rank": 5132, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ethfin-ethernal-finance", - "name": "Ethernal Finance", - "symbol": "ETHFIN", - "rank": 5133, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mpay-metapay", - "name": "Metapay", - "symbol": "MPAY", - "rank": 5134, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safeearth-safeearth", - "name": "SafeEarth", - "symbol": "SAFEEARTH", - "rank": 5135, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "0xmr-0xmonero", - "name": "0xMonero", - "symbol": "0xMR", - "rank": 5136, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pct-pinecone-finance", - "name": "Pinecone Finance", - "symbol": "PCT", - "rank": 5137, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "surfmoon-surfmoon", - "name": "SurfMoon", - "symbol": "SURFMOON", - "rank": 5138, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qs-quantumscape-corporation", - "name": "QuantumScape Corporation", - "symbol": "QS", - "rank": 5139, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elonom-elonomics", - "name": "Elonomics", - "symbol": "$ELONOM", - "rank": 5140, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hds-hotdollars-token", - "name": "HotDollars Token", - "symbol": "HDS", - "rank": 5141, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eva-evagrow-coin", - "name": "Evagrow Coin", - "symbol": "EVA", - "rank": 5142, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "redan-redancoin", - "name": "REDANCOIN", - "symbol": "REDAN", - "rank": 5143, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inb-insight-chain", - "name": "Insight Chain", - "symbol": "INB", - "rank": 5144, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmx-dymmax", - "name": "Dymmax", - "symbol": "DMX", - "rank": 5145, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "homt-hom-token", - "name": "HOM Token", - "symbol": "HOMT", - "rank": 5146, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lbr-little-bunny-rocket", - "name": "Little Bunny Rocket", - "symbol": "LBR", - "rank": 5147, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cine-cineworld-group", - "name": "Cineworld Group", - "symbol": "CINE", - "rank": 5148, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oooor-oooor-finance", - "name": "OOOOR Finance", - "symbol": "OOOOR", - "rank": 5149, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "h2o-trickle", - "name": "Trickle", - "symbol": "H2O", - "rank": 5150, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "picipo-picipo", - "name": "Picipo", - "symbol": "PICIPO", - "rank": 5151, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtx-metamatrix", - "name": "MetaMatrix", - "symbol": "MTX", - "rank": 5152, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pyq-polyquity", - "name": "PolyQuity", - "symbol": "PYQ", - "rank": 5153, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftbs-nftbooks", - "name": "NFTBooks", - "symbol": "NFTBS", - "rank": 5154, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgntru-magnit", - "name": "MAGNIT", - "symbol": "MGNTru", - "rank": 5155, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sber-sberbank-of-russia-gdr", - "name": "Sberbank of Russia GDR", - "symbol": "SBER", - "rank": 5156, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mm-mmtoken", - "name": "MMToken", - "symbol": "MM", - "rank": 5157, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snis-shibonics", - "name": "Shibonics", - "symbol": "SNIS", - "rank": 5158, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aft-ape-fun-token", - "name": "Ape Fun Token", - "symbol": "AFT", - "rank": 5159, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coape-council-of-apes", - "name": "Council of Apes", - "symbol": "COAPE", - "rank": 5160, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xvmc-maccheese", - "name": "Mac\u0026Cheese", - "symbol": "XVMC", - "rank": 5161, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sionx-sionix", - "name": "SIONIX", - "symbol": "SIONX", - "rank": 5162, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rox-robotina", - "name": "Robotina", - "symbol": "ROX", - "rank": 5163, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "t-atampt", - "name": "AT\u0026T", - "symbol": "T", - "rank": 5164, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gold-xbullion", - "name": "XBullion", - "symbol": "GOLD", - "rank": 5165, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gysr-geyser", - "name": "Geyser", - "symbol": "GYSR", - "rank": 5166, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oxb-oxbull", - "name": "OXBULL", - "symbol": "OXB", - "rank": 5167, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dgvc-degenvc", - "name": "DegenVC", - "symbol": "DGVC", - "rank": 5168, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mvm-movie-magic", - "name": "Movie Magic", - "symbol": "MVM", - "rank": 5169, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tlry-tilray-inc", - "name": "Tilray, Inc.", - "symbol": "TLRY", - "rank": 5170, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssn-supersonic-finance", - "name": "Supersonic Finance", - "symbol": "SSN", - "rank": 5171, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "therocks-the-rocks", - "name": "The Rocks", - "symbol": "THEROCKS", - "rank": 5172, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moon-honeymoon-token", - "name": "HoneyMOON Token", - "symbol": "MOON", - "rank": 5173, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cir-circleswap", - "name": "CircleSwap", - "symbol": "CIR", - "rank": 5174, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "peanuts-peanuts", - "name": "Peanuts", - "symbol": "PEANUTS", - "rank": 5175, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tor-torchain", - "name": "Torchain", - "symbol": "TOR", - "rank": 5176, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blaze-blaze-the-cat", - "name": "Blaze the cat", - "symbol": "$BLAZE", - "rank": 5177, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wild-wild-credit", - "name": "Wild Credit", - "symbol": "WILD", - "rank": 5178, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mad-madnetwork", - "name": "MADNetwork", - "symbol": "MAD", - "rank": 5179, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bafe-bafeio", - "name": "Bafe.io", - "symbol": "BAFE", - "rank": 5180, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poordoge-poordoge", - "name": "PoorDoge", - "symbol": "POORDOGE", - "rank": 5181, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hlink-hydrolink", - "name": "HydroLink", - "symbol": "HLINK", - "rank": 5182, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frozen-frozencake", - "name": "FrozenCake", - "symbol": "FROZEN", - "rank": 5183, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bxk-bitbook-gambling", - "name": "Bitbook Gambling", - "symbol": "BXK", - "rank": 5184, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "utt-united-traders-token", - "name": "United Traders Token", - "symbol": "UTT", - "rank": 5185, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sls-salus", - "name": "SaluS", - "symbol": "SLS", - "rank": 5186, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gav-gravisible", - "name": "Gravisible", - "symbol": "GAV", - "rank": 5187, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fangs-fangs", - "name": "Fangs", - "symbol": "FANGS", - "rank": 5188, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acs-acryptos", - "name": "ACryptoS", - "symbol": "ACS", - "rank": 5189, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "usopp-usopp-inu", - "name": "Usopp Inu", - "symbol": "USOPP", - "rank": 5190, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pndmlv-panda-multiverse", - "name": "Panda Multiverse", - "symbol": "PNDMLV", - "rank": 5191, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unity-polyunity-finance", - "name": "PolyUnity Finance", - "symbol": "UNITY", - "rank": 5192, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dball-drakeball-token", - "name": "DrakeBall Token", - "symbol": "DBALL", - "rank": 5193, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rcl-royal-caribbean-cruises", - "name": "Royal Caribbean Cruises", - "symbol": "RCL", - "rank": 5194, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "val-valkyrie-network", - "name": "Valkyrie Network", - "symbol": "VAL", - "rank": 5195, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qmc-qmcoin", - "name": "QMCoin", - "symbol": "QMC", - "rank": 5196, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "catchy-catchy", - "name": "Catchy", - "symbol": "CATCHY", - "rank": 5197, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arms-2acoin", - "name": "2ACoin", - "symbol": "ARMS", - "rank": 5198, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gale-galleoncoin", - "name": "GalleonCoin", - "symbol": "GALE", - "rank": 5199, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cds-crypto-development-services", - "name": "Crypto Development Services", - "symbol": "CDS", - "rank": 5200, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jesinu-jesus-inu", - "name": "Jesus Inu", - "symbol": "JESINU", - "rank": 5201, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "txc-toxicgamenft", - "name": "ToxicGameNft", - "symbol": "TXC", - "rank": 5202, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssr-star-ship-royal", - "name": "Star Ship Royal", - "symbol": "SSR", - "rank": 5203, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ashib-alien-shiba-inu", - "name": "Alien Shiba Inu", - "symbol": "ASHIB", - "rank": 5204, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "insur-insurchain", - "name": "InsurChain", - "symbol": "INSUR", - "rank": 5205, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bgld-based-gold", - "name": "Based Gold", - "symbol": "BGLD", - "rank": 5206, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "peakavax-peak-avalanche", - "name": "Peak Avalanche", - "symbol": "PEAKAVAX", - "rank": 5207, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roar-roar-token", - "name": "Roar Token", - "symbol": "ROAR", - "rank": 5208, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mewtwo-mewtwo-inu", - "name": "Mewtwo Inu", - "symbol": "MEWTWO", - "rank": 5209, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sfo-sunflower-finance", - "name": "Sunflower Finance", - "symbol": "SFO", - "rank": 5210, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "air-airdropper", - "name": "AirDropper", - "symbol": "AIR", - "rank": 5211, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "maze-nft-maze", - "name": "NFT MAZE", - "symbol": "MAZE", - "rank": 5212, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "racerr-thunderracer", - "name": "ThunderRacer", - "symbol": "RACERR", - "rank": 5213, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mmon-mammon", - "name": "Mammon", - "symbol": "MMON", - "rank": 5214, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daddydb-daddy-dogeback", - "name": "Daddy Dogeback", - "symbol": "DADDYDB", - "rank": 5215, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "0241-alibaba-health-information-technology-limited", - "name": "Alibaba Health Information Technology Limited", - "symbol": "0241", - "rank": 5216, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gasp-gasp", - "name": "gAsp", - "symbol": "GASP", - "rank": 5217, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "momat-moma-protocol", - "name": "Moma Protocol", - "symbol": "MOMAT", - "rank": 5218, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bmrn-biomarin-pharmaceutical-inc", - "name": "BioMarin Pharmaceutical Inc", - "symbol": "BMRN", - "rank": 5219, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tacoe-tacoenergy", - "name": "TacoEnergy", - "symbol": "TACOE", - "rank": 5220, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fsinu-flappy-shiba-inu", - "name": "Flappy Shiba Inu", - "symbol": "FSINU", - "rank": 5221, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vir-vir-biotechnology", - "name": "Vir Biotechnology", - "symbol": "VIR", - "rank": 5222, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "asp-aspire", - "name": "Aspire", - "symbol": "ASP", - "rank": 5223, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "koshu-koshu-inu", - "name": "KOSHU INU", - "symbol": "KOSHU", - "rank": 5224, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qac-quasarcoin", - "name": "Quasarcoin", - "symbol": "QAC", - "rank": 5225, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bse-buy-sell", - "name": "Buy-Sell", - "symbol": "BSE", - "rank": 5226, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "merk-merk", - "name": "MERK", - "symbol": "MERK", - "rank": 5227, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ron-rocket-raccoon", - "name": "Rocket Raccoon", - "symbol": "RON", - "rank": 5228, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mfm-moonfarmer", - "name": "MoonFarmer", - "symbol": "MFM", - "rank": 5229, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "owl-owldao", - "name": "OwlDAO", - "symbol": "OWL", - "rank": 5230, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nvtkru-novatek", - "name": "Novatek", - "symbol": "NVTKru", - "rank": 5231, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "famy-farmyield", - "name": "FarmYield", - "symbol": "FAMY", - "rank": 5232, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "foxx-star-foxx", - "name": "Star Foxx", - "symbol": "FOXX", - "rank": 5233, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jungle-jungle", - "name": "Jungle", - "symbol": "JUNGLE", - "rank": 5234, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fbb-foxboy", - "name": "Foxboy", - "symbol": "FBB", - "rank": 5235, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "time-madagascar-token", - "name": "Madagascar Token", - "symbol": "$TIME", - "rank": 5236, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zfai-zafira", - "name": "Zafira", - "symbol": "ZFAI", - "rank": 5237, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qbx-qiibee", - "name": "qiibee", - "symbol": "QBX", - "rank": 5238, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "azum-azuma-coin", - "name": "Azuma coin", - "symbol": "AZUM", - "rank": 5239, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nke-nike-inc", - "name": "NIKE Inc", - "symbol": "NKE", - "rank": 5240, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bluefloki-blue-floki-inu", - "name": "Blue Floki Inu", - "symbol": "BLUEFLOKI", - "rank": 5241, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lelouch-lelouch-lamperouge", - "name": "Lelouch Lamperouge", - "symbol": "LELOUCH", - "rank": 5242, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cc-chemours", - "name": "Chemours", - "symbol": "CC", - "rank": 5243, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adot-alterdot", - "name": "Alterdot", - "symbol": "ADOT", - "rank": 5244, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "d4rk-darkpay", - "name": "DarkPay", - "symbol": "D4RK", - "rank": 5245, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mtvx-mtvx", - "name": "MTVX", - "symbol": "MTVX", - "rank": 5246, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mzk-muzika", - "name": "Muzika", - "symbol": "MZK", - "rank": 5247, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "f-ford-motor-co", - "name": "Ford Motor Co", - "symbol": "F", - "rank": 5248, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "erd-elrond", - "name": "Elrond [OLD TOKEN]", - "symbol": "ERD", - "rank": 5249, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blade-bladewarrior", - "name": "BladeWarrior", - "symbol": "BLADE", - "rank": 5250, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "x42-x42", - "name": "x42", - "symbol": "X42", - "rank": 5251, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ball-ball-token", - "name": "Ball Token", - "symbol": "BALL", - "rank": 5252, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dvt-devault", - "name": "DeVault", - "symbol": "DVT", - "rank": 5253, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cba-cabana-token", - "name": "Cabana Token", - "symbol": "CBA", - "rank": 5254, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swg-swgtoken", - "name": "SWGToken", - "symbol": "SWG", - "rank": 5255, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spacexdoge-doge-universe", - "name": "Doge Universe", - "symbol": "SPACEXDOGE", - "rank": 5256, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shit-shitcoin", - "name": "ShitCoin", - "symbol": "SHIT", - "rank": 5257, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apein-ape-in", - "name": "Ape In", - "symbol": "APEIN", - "rank": 5258, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lmch-latamcash", - "name": "Latamcash", - "symbol": "LMCH", - "rank": 5259, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nado-tornadao", - "name": "Tornadao", - "symbol": "NADO", - "rank": 5260, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slv-silverway", - "name": "Silverway", - "symbol": "SLV", - "rank": 5261, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "utopia-utopia", - "name": "Utopia", - "symbol": "UTOPIA", - "rank": 5262, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "loves-loves", - "name": "Loves", - "symbol": "LOVES", - "rank": 5263, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csco-cisco-systems", - "name": "Cisco Systems", - "symbol": "CSCO", - "rank": 5264, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vtbrru-vtb", - "name": "VTB", - "symbol": "VTBRru", - "rank": 5265, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "beta-polybeta-finance", - "name": "PolyBeta Finance", - "symbol": "BETA", - "rank": 5266, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecix-electric-cars-index", - "name": "Electric Cars Index", - "symbol": "ECIX", - "rank": 5267, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sob-solalambo", - "name": "SolaLambo", - "symbol": "SOB", - "rank": 5268, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fft-flocki-finance", - "name": "Flocki Finance", - "symbol": "FFT", - "rank": 5269, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gst-game-stars", - "name": "Game Stars", - "symbol": "GST", - "rank": 5270, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wizard-wizard-token", - "name": "Wizard Token", - "symbol": "WIZARD", - "rank": 5271, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cfl365-cfl365-finance", - "name": "CFL365 Finance", - "symbol": "CFL365", - "rank": 5272, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rokk-rokkit-fuel", - "name": "Rokkit Fuel", - "symbol": "$ROKK", - "rank": 5273, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cdtc-decredit", - "name": "DeCredit", - "symbol": "CDTC", - "rank": 5274, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "microshib-microshiba", - "name": "MicroSHIBA", - "symbol": "MICROSHIB", - "rank": 5275, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stz-99starz", - "name": "99Starz", - "symbol": "STZ", - "rank": 5276, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alpha-polyalpha-finance", - "name": "PolyAlpha Finance", - "symbol": "ALPHA", - "rank": 5277, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lyptus-lyptus-token", - "name": "Lyptus Token", - "symbol": "LYPTUS", - "rank": 5278, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dion-dionpay", - "name": "Dionpay", - "symbol": "DION", - "rank": 5279, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ainu-anon-inu", - "name": "Anon Inu", - "symbol": "AINU", - "rank": 5280, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "solbear-solar-bear", - "name": "Solar Bear", - "symbol": "SOLBEAR", - "rank": 5281, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swan-swanlana", - "name": "Swanlana", - "symbol": "SWAN", - "rank": 5282, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oct-octree", - "name": "Octree", - "symbol": "OCT", - "rank": 5283, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "br-bull-run-finance", - "name": "Bull Run Finance", - "symbol": "BR", - "rank": 5284, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mch-mktcash", - "name": "mktcash", - "symbol": "MCH", - "rank": 5285, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tshp-12ships", - "name": "12Ships", - "symbol": "TSHP", - "rank": 5286, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bidog-binancedog", - "name": "Binancedog", - "symbol": "BIDOG", - "rank": 5287, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpe-callon-petroleum", - "name": "Callon Petroleum", - "symbol": "CPE", - "rank": 5288, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "otium-otium-technologies", - "name": "Otium Technologies", - "symbol": "OTIUM", - "rank": 5289, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drt-d-reit", - "name": "D-ReiT", - "symbol": "DRT", - "rank": 5290, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gamma-polygamma-finance", - "name": "PolyGamma Finance", - "symbol": "GAMMA", - "rank": 5291, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bashtank-baby-shark-tank", - "name": "Baby Shark Tank", - "symbol": "BASHTANK", - "rank": 5292, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "butk-butkoin", - "name": "BUTKOIN", - "symbol": "BUTK", - "rank": 5293, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bob-baby-o-baby", - "name": "Baby o Baby", - "symbol": "BOB", - "rank": 5294, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "golduck-golden-duck", - "name": "Golden Duck", - "symbol": "GOLDUCK", - "rank": 5295, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wmt-wal-mart-stores-inc", - "name": "Wal-Mart Stores Inc", - "symbol": "WMT", - "rank": 5296, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clion-cryptolion", - "name": "CryptoLion", - "symbol": "CLION", - "rank": 5297, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eros-eros-token", - "name": "Eros Token", - "symbol": "EROS", - "rank": 5298, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcbr-bitcoinbr", - "name": "BitcoinBR", - "symbol": "BTCBR", - "rank": 5299, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "doos-doos-token", - "name": "DOOS TOKEN", - "symbol": "DOOS", - "rank": 5300, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "msc-monster-slayer-cash", - "name": "Monster Slayer Cash", - "symbol": "MSC", - "rank": 5301, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rckt-rocket-launchpad", - "name": "Rocket Launchpad", - "symbol": "RCKT", - "rank": 5302, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibamonk-shiba-monk", - "name": "Shiba Monk", - "symbol": "SHIBAMONK", - "rank": 5303, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fubo-fubotv-inc", - "name": "fuboTV Inc.", - "symbol": "FUBO", - "rank": 5304, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sphr-sphere", - "name": "Sphere", - "symbol": "SPHR", - "rank": 5305, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "time-timeleap", - "name": "TimeLeap", - "symbol": "TIME", - "rank": 5306, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yefim-yfi-management", - "name": "YFi Management", - "symbol": "YeFiM", - "rank": 5307, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rho-rhinos-finance", - "name": "Rhinos Finance", - "symbol": "RHO", - "rank": 5308, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chkn-chicken-zilla", - "name": "Chicken Zilla", - "symbol": "CHKN", - "rank": 5309, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "twx-twindex", - "name": "Twindex", - "symbol": "TWX", - "rank": 5310, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clam-clams", - "name": "Clams", - "symbol": "CLAM", - "rank": 5311, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "blue-bluebird-bio-inc", - "name": "bluebird bio, Inc.", - "symbol": "BLUE", - "rank": 5312, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cakepunks-cakepunks", - "name": "CAKEPUNKS", - "symbol": "CAKEPUNKS", - "rank": 5313, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scoobnbdoo-scoobnbdoo", - "name": "ScooBNBDoo", - "symbol": "SCOOBNBDOO", - "rank": 5314, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ysoy-ysoy", - "name": "Ysoy", - "symbol": "YSOY", - "rank": 5315, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lst-libartysharetoken", - "name": "Libartysharetoken", - "symbol": "LST", - "rank": 5316, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babywkd-babywakandainu", - "name": "Babywakandainu", - "symbol": "BABYWKD", - "rank": 5317, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vinu-vita-inu", - "name": "Vita Inu", - "symbol": "VINU", - "rank": 5318, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "shio-shibanomi", - "name": "Shibanomi", - "symbol": "SHIO", - "rank": 5319, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apef-apefarmer", - "name": "ApeFarmer", - "symbol": "APEF", - "rank": 5320, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbc-cryptobosscoin", - "name": "Crypto Boss Coin", - "symbol": "CBC", - "rank": 5321, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slw-solarwind-token", - "name": "SolarWind Token", - "symbol": "SLW", - "rank": 5322, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "limex-limestone-network", - "name": "Limestone Network", - "symbol": "LIMEX", - "rank": 5323, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "povo-povo-finance", - "name": "Povo Finance", - "symbol": "POVO", - "rank": 5324, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bust-busta", - "name": "BUSTA", - "symbol": "BUST", - "rank": 5325, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bacon-bacon", - "name": "BACON", - "symbol": "BACON", - "rank": 5326, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qdao-qdao", - "name": "QDAO", - "symbol": "QDAO", - "rank": 5327, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hca-hca-healthcare", - "name": "HCA Healthcare", - "symbol": "HCA", - "rank": 5328, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcl-marmara-credit-loops", - "name": "Marmara Credit Loops", - "symbol": "MCL", - "rank": 5329, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "green-green", - "name": "GREEN", - "symbol": "GREEN", - "rank": 5330, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdc-smiledirectclub-inc", - "name": "SmileDirectClub Inc", - "symbol": "SDC", - "rank": 5331, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crm-salesforce", - "name": "Salesforce", - "symbol": "CRM", - "rank": 5332, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sat-saturna", - "name": "Saturna", - "symbol": "SAT", - "rank": 5333, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "umg-underminegold", - "name": "Under Mine Gold", - "symbol": "UMG", - "rank": 5334, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pac-meta-pac-man", - "name": "Meta Pac-Man", - "symbol": "PAC", - "rank": 5335, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reu-reucoin", - "name": "REUCOIN", - "symbol": "REU", - "rank": 5336, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ktv-kmushicoin", - "name": "Kmushicoin", - "symbol": "KTV", - "rank": 5337, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ultbnb-ultimatebnb", - "name": "UltimateBNB", - "symbol": "ULTBNB", - "rank": 5338, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xsr-sucrecoin", - "name": "SucreCoin", - "symbol": "XSR", - "rank": 5339, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fsafe-fair-safe", - "name": "Fair Safe", - "symbol": "FSAFE", - "rank": 5340, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goat-goatcoin", - "name": "Goatcoin", - "symbol": "GOAT", - "rank": 5341, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rebl-rebellion-protocol", - "name": "Rebellion Protocol", - "symbol": "REBL", - "rank": 5342, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etgl-eternalgirl", - "name": "EternalGirl", - "symbol": "ETGL", - "rank": 5343, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lwazi-lwazi-project", - "name": "Lwazi Project", - "symbol": "LWAZI", - "rank": 5344, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metar-metaraca", - "name": "MetaRaca", - "symbol": "METAR", - "rank": 5345, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kennel-token-kennel", - "name": "Token Kennel", - "symbol": "KENNEL", - "rank": 5346, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hgt-hydrogen-token", - "name": "Hydrogen Token", - "symbol": "HGT", - "rank": 5347, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metainu-meta-inu", - "name": "Meta Inu", - "symbol": "METAINU", - "rank": 5348, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "karma-karma-dao", - "name": "Karma DAO", - "symbol": "KARMA", - "rank": 5349, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "claw-cats-claw", - "name": "Cats Claw", - "symbol": "CLAW", - "rank": 5350, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pasta-pasta-finance", - "name": "Pasta Finance", - "symbol": "PASTA", - "rank": 5351, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnnx-gennix", - "name": "Gennix", - "symbol": "GNNX", - "rank": 5352, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wb-weibo-corporation", - "name": "Weibo Corporation", - "symbol": "WB", - "rank": 5353, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reau-vira-lata-finance", - "name": "Vira-Lata Finance", - "symbol": "REAU", - "rank": 5354, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blg-blue-gold", - "name": "Blue Gold", - "symbol": "BLG", - "rank": 5355, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sage-polysage", - "name": "Polysage", - "symbol": "SAGE", - "rank": 5356, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prv-privacyswap", - "name": "PrivacySwap", - "symbol": "PRV", - "rank": 5357, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "omch-omniunit-cash", - "name": "Omniunit Cash", - "symbol": "OMCH", - "rank": 5358, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tdc-tidecoin", - "name": "Tidecoin", - "symbol": "TDC", - "rank": 5359, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "obrok-obrok-token", - "name": "OBRok Token", - "symbol": "OBROK", - "rank": 5360, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbfeg-babyfeg", - "name": "Babyfeg", - "symbol": "BBFEG", - "rank": 5361, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fcp-filipcoin", - "name": "Filipcoin", - "symbol": "FCP", - "rank": 5362, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trax-privi-trax", - "name": "Privi Trax", - "symbol": "TRAX", - "rank": 5363, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fls-flits", - "name": "Flits", - "symbol": "FLS", - "rank": 5364, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "moonrise-moonrise", - "name": "MoonRise", - "symbol": "MOONRISE", - "rank": 5365, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ubtc-united-bitcoin", - "name": "United Bitcoin", - "symbol": "UBTC", - "rank": 5366, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "arnc-arconic", - "name": "Arconic", - "symbol": "ARNC", - "rank": 5367, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fsc-five-star-coin", - "name": "Five Star Coin", - "symbol": "FSC", - "rank": 5368, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sbux-starbucks-corp", - "name": "Starbucks Corp", - "symbol": "SBUX", - "rank": 5369, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wiseavax-wiseavax", - "name": "WiseAvax", - "symbol": "WISEAVAX", - "rank": 5370, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tgt-target-corp", - "name": "Target Corp", - "symbol": "TGT", - "rank": 5371, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kshiba-kawai-shiba", - "name": "Kawai Shiba", - "symbol": "KSHIBA", - "rank": 5372, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stonk-stonk", - "name": "Stonk", - "symbol": "STONK", - "rank": 5373, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fire-fire-token", - "name": "Fire Token", - "symbol": "FIRE", - "rank": 5374, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sch-schillingcoin", - "name": "Schillingcoin", - "symbol": "SCH", - "rank": 5375, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ytofu-ytofu", - "name": "ytofu", - "symbol": "YTOFU", - "rank": 5376, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nflx-netflix", - "name": "Netflix", - "symbol": "NFLX", - "rank": 5377, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dge-dragonsea", - "name": "DragonSea", - "symbol": "DGE", - "rank": 5378, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sanshu-sanshu-inu", - "name": "Sanshu Inu", - "symbol": "SANSHU", - "rank": 5379, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yrd-yirendai", - "name": "Yirendai", - "symbol": "YRD", - "rank": 5380, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbar-selfbar", - "name": "Selfbar", - "symbol": "SBAR", - "rank": 5381, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tpi-thanecoin", - "name": "ThaneCoin", - "symbol": "TPI", - "rank": 5382, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adaflect-adaflect", - "name": "ADAFlect", - "symbol": "ADAFLECT", - "rank": 5383, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rock-bedrock", - "name": "Bedrock", - "symbol": "ROCK", - "rank": 5384, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bglg-big-league", - "name": "Big League", - "symbol": "BGLG", - "rank": 5385, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sqad-squad-token", - "name": "SQUAD TOKEN", - "symbol": "SQAD", - "rank": 5386, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zee-zoints", - "name": "Zoints", - "symbol": "ZEE", - "rank": 5387, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "knb-kronobit", - "name": "Kronobit", - "symbol": "KNB", - "rank": 5388, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spr-spirit-aerosystems-holdings-inc", - "name": "Spirit Aerosystems Holdings, Inc", - "symbol": "SPR", - "rank": 5389, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gold-gold-cash", - "name": "Gold Cash", - "symbol": "GOLD", - "rank": 5390, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ksh-keeshond", - "name": "Keeshond", - "symbol": "$KSH", - "rank": 5391, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "e8-energy8", - "name": "Energy8", - "symbol": "E8", - "rank": 5392, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chtt-token-cheetah", - "name": "Token Cheetah", - "symbol": "CHTT", - "rank": 5393, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lst-lendroid-support-token", - "name": "Lendroid Support Token", - "symbol": "LST", - "rank": 5394, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "axin-axienomics", - "name": "Axienomics", - "symbol": "AXIN", - "rank": 5395, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbunny-crazy-bunny-equity-token", - "name": "Crazy Bunny Equity Token", - "symbol": "CBUNNY", - "rank": 5396, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elv-e-leven", - "name": "E-leven", - "symbol": "ELV", - "rank": 5397, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "updog-updog", - "name": "UPDOG", - "symbol": "UPDOG", - "rank": 5398, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alkom-alpha-kombat", - "name": "Alpha Kombat", - "symbol": "ALKOM", - "rank": 5399, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hfsp-have-fun-staying-poor", - "name": "Have Fun Staying Poor", - "symbol": "HFSP", - "rank": 5400, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "riv2-riseupv2", - "name": "RiseUpV2", - "symbol": "RIV2", - "rank": 5401, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bins-bsocial", - "name": "BSocial", - "symbol": "BINS", - "rank": 5402, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibt-shiba-light", - "name": "Shiba Light", - "symbol": "SHIBT", - "rank": 5403, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccl-carnival", - "name": "Carnival ", - "symbol": "CCL", - "rank": 5404, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bskt-basketcoin", - "name": "BasketCoin", - "symbol": "BSKT", - "rank": 5405, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pdao-panda-dao", - "name": "Panda Dao", - "symbol": "PDAO", - "rank": 5406, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inet-ideanet-token", - "name": "Ideanet Token", - "symbol": "INET", - "rank": 5407, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ucr-ultra-clear", - "name": "Ultra Clear", - "symbol": "UCR", - "rank": 5408, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vz-verizon", - "name": "Verizon", - "symbol": "VZ", - "rank": 5409, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "krill-polywhale", - "name": "Polywhale", - "symbol": "KRILL", - "rank": 5410, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "intc-intel-corporation", - "name": "Intel Corporation", - "symbol": "INTC", - "rank": 5411, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coc-cocker", - "name": "Cocker", - "symbol": "COC", - "rank": 5412, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsh-bitcoin-stash", - "name": "Bitcoin Stash", - "symbol": "BSH", - "rank": 5413, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "spidey-inu-spidey-inu", - "name": "Spidey Inu", - "symbol": "SPIDEY INU", - "rank": 5414, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prpt-prosperity-token", - "name": "Prosperity Token", - "symbol": "PRPT", - "rank": 5415, - "is_new": true, - "is_active": false, - "type": "token" - }, - { - "id": "sohm-safeohm", - "name": "SafeOHM", - "symbol": "SOHM", - "rank": 5416, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wave-the-great-wave", - "name": "The Great Wave", - "symbol": "WAVE", - "rank": 5417, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wdx-wordlex", - "name": "Wordlex", - "symbol": "WDX", - "rank": 5418, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pcn-phoenixchain", - "name": "PhoenixChain", - "symbol": "PCN", - "rank": 5419, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "soak-soakmont", - "name": "Soakmont", - "symbol": "SOAK", - "rank": 5420, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chips-chipstars", - "name": "Chipstars", - "symbol": "CHIPS", - "rank": 5421, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ric-rielcoin", - "name": "Rielcoin", - "symbol": "RIC", - "rank": 5422, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inftee-infinitee", - "name": "Infinitee", - "symbol": "INFTEE", - "rank": 5423, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zooshi-zooshi", - "name": "Zooshi", - "symbol": "ZOOSHI", - "rank": 5424, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "feast-feast-finance", - "name": "FEAST FINANCE", - "symbol": "FEAST", - "rank": 5425, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vml-metaland", - "name": "MetaLand", - "symbol": "VML", - "rank": 5426, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dkks-daikokuten-sama", - "name": "Daikokuten Sama", - "symbol": "DKKS", - "rank": 5427, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bscx-bscex", - "name": "BSCEX", - "symbol": "BSCX", - "rank": 5428, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cosa-cosanta", - "name": "Cosanta", - "symbol": "COSA", - "rank": 5429, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zero-0exchange", - "name": "0.exchange", - "symbol": "ZERO", - "rank": 5430, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tova-tova", - "name": "TOVA", - "symbol": "TOVA", - "rank": 5431, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "earena-electric-arena", - "name": "Electric Arena", - "symbol": "EARENA", - "rank": 5432, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cgold-crimegold", - "name": "CrimeGold", - "symbol": "CGOLD", - "rank": 5433, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rac-rac", - "name": "RAC", - "symbol": "RAC", - "rank": 5434, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eut-eutaria", - "name": "Eutaria", - "symbol": "EUT", - "rank": 5435, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyfd-baby-floki-doge", - "name": "Baby Floki Doge", - "symbol": "BABYFD", - "rank": 5436, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "simpli-simpli-finance", - "name": "Simpli Finance", - "symbol": "SIMPLI", - "rank": 5437, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fanta-football-fantasy-pro", - "name": "Football Fantasy Pro", - "symbol": "FANTA", - "rank": 5438, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "saf-safcoin", - "name": "Safcoin", - "symbol": "SAF", - "rank": 5439, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nah-strayacoin", - "name": "Strayacoin", - "symbol": "NAH", - "rank": 5440, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dogefather-dogefather", - "name": "Dogefather", - "symbol": "DOGEFATHER", - "rank": 5441, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "olp-olympia", - "name": "Olympia", - "symbol": "OLP", - "rank": 5442, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elite-ethereum-lite", - "name": "Ethereum Lite", - "symbol": "ELITE", - "rank": 5443, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flip-flipstar", - "name": "FlipStar", - "symbol": "FLIP", - "rank": 5444, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rants-redfireants", - "name": "redFireAnts", - "symbol": "RANTS", - "rank": 5445, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ccf-cashcowfinance", - "name": "CashCow.Finance", - "symbol": "CCF", - "rank": 5446, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "buffshiba-buff-shiba-inu", - "name": "Buff Shiba Inu", - "symbol": "BUFFSHIBA", - "rank": 5447, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsb-bitcoin-sb", - "name": "Bitcoin SB", - "symbol": "BSB", - "rank": 5448, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "like-likeswap-token", - "name": "LikeSwap Token", - "symbol": "LIKE", - "rank": 5449, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbc-crypto-bharat-coin", - "name": "Crypto Bharat Coin", - "symbol": "CBC", - "rank": 5450, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibby-shibby", - "name": "Shibby", - "symbol": "SHIBBY", - "rank": 5451, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mada-mini-cardano", - "name": "Mini Cardano", - "symbol": "MADA", - "rank": 5452, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "afltru-aeroflot", - "name": "Aeroflot", - "symbol": "AFLTru", - "rank": 5453, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mma-mmacoin", - "name": "MMACoin", - "symbol": "MMA", - "rank": 5454, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thunderbnb-thunderbnb", - "name": "ThunderBNB", - "symbol": "THUNDERBNB", - "rank": 5455, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ldp-ldxpay", - "name": "LDXPAY", - "symbol": "LDP", - "rank": 5456, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chta-cheetahcoin", - "name": "Cheetahcoin", - "symbol": "CHTA", - "rank": 5457, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lock-contracto", - "name": "Contracto", - "symbol": "LOCK", - "rank": 5458, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kombai-kombai-inu", - "name": "Kombai Inu", - "symbol": "KOMBAI", - "rank": 5459, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aa-alcoa", - "name": "Alcoa", - "symbol": "AA", - "rank": 5460, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kite-kite-sync", - "name": "Kite Sync", - "symbol": "KITE", - "rank": 5461, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acx-accesslauncher", - "name": "AccessLauncher", - "symbol": "ACX", - "rank": 5462, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fln-flinch", - "name": "Flinch", - "symbol": "FLN", - "rank": 5463, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "street-street-money", - "name": "Street Money", - "symbol": "STREET", - "rank": 5464, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "floshin-floshin", - "name": "Floshin", - "symbol": "FLOSHIN", - "rank": 5465, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mvidru-mvideo", - "name": "M.video", - "symbol": "MVIDru", - "rank": 5466, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slf-solarfare", - "name": "Solarfare", - "symbol": "SLF", - "rank": 5467, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "islbyz-island-boyz", - "name": "Island Boyz", - "symbol": "$ISLBYZ", - "rank": 5468, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "klp-kulupu", - "name": "Kulupu", - "symbol": "KLP", - "rank": 5469, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cbg-cobragoose", - "name": "CobraGoose", - "symbol": "CBG", - "rank": 5470, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bff-bitcoffeen", - "name": "Bitcoffeen", - "symbol": "BFF", - "rank": 5471, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bfu-baby-floki-up", - "name": "Baby Floki Up", - "symbol": "BFU", - "rank": 5472, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xaea12-x-ae-a-12", - "name": "X AE A-12", - "symbol": "XAEA12", - "rank": 5473, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chad-gigachad", - "name": "GigaChad", - "symbol": "CHAD", - "rank": 5474, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbt-magicbox", - "name": "MagicBox", - "symbol": "MBT", - "rank": 5475, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "santa-rich-santa", - "name": "Rich Santa", - "symbol": "SANTA", - "rank": 5476, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bb-blackberry-limited", - "name": "BlackBerry Limited", - "symbol": "BB", - "rank": 5477, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ino-io-pharmaceuticals", - "name": "Io Pharmaceuticals", - "symbol": "INO", - "rank": 5478, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yabba-the-flintstones-token", - "name": "The Flintstones token", - "symbol": "YABBA", - "rank": 5479, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vip-vip-token", - "name": "VIP Token", - "symbol": "VIP", - "rank": 5480, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wspp-wolfsafepoorpeople", - "name": "WolfSafePoorPeople", - "symbol": "WSPP", - "rank": 5481, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lux-luxury-club", - "name": "Luxury Club", - "symbol": "LUX", - "rank": 5482, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "robin-robin-inu", - "name": "Robin Inu", - "symbol": "ROBIN", - "rank": 5483, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bhunt-binahunter", - "name": "BinaHunter", - "symbol": "BHUNT", - "rank": 5484, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pltr-palantir-technologies-inc", - "name": "Palantir Technologies Inc.", - "symbol": "PLTR", - "rank": 5485, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsuki-tsuki-dao", - "name": "Tsuki DAO", - "symbol": "TSUKI", - "rank": 5486, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lumi-luminos-mining-protocol", - "name": "Luminos Mining Protocol", - "symbol": "LUMI", - "rank": 5487, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vsc-vari-stable-capital", - "name": "Vari-Stable Capital", - "symbol": "VSC", - "rank": 5488, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bonuscake-bonus-cake", - "name": "Bonus Cake", - "symbol": "BONUSCAKE", - "rank": 5489, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "burndoge-burndoge", - "name": "BurnDoge", - "symbol": "BURNDOGE", - "rank": 5490, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ypie-piedao-yearn-ecosystem-pie", - "name": "PieDAO Yearn Ecosystem Pie", - "symbol": "YPIE", - "rank": 5491, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnbch-bnb-cash", - "name": "BNB Cash", - "symbol": "BNBCH", - "rank": 5492, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emp-emp-token", - "name": "EMP Token", - "symbol": "EMP", - "rank": 5493, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stonk-stonkswap-token", - "name": "StonkSwap Token", - "symbol": "STONK", - "rank": 5494, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cacti-cacti-club", - "name": "Cacti Club", - "symbol": "CACTI", - "rank": 5495, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "labra-labra-finance", - "name": "Labra Finance", - "symbol": "LABRA", - "rank": 5496, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sltn-skylight", - "name": "Skylight", - "symbol": "SLTN", - "rank": 5497, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dpace-defpace", - "name": "DefPace", - "symbol": "DPACE", - "rank": 5498, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "osm-supermoon", - "name": "Supermoon", - "symbol": "OSM", - "rank": 5499, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnx-bnx-finex", - "name": "Bnx Finex", - "symbol": "BNX", - "rank": 5500, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mw-masterwin", - "name": "Masterwin", - "symbol": "MW", - "rank": 5501, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bnft-bruce-non-fungible-token", - "name": "Bruce Non Fungible Token", - "symbol": "BNFT", - "rank": 5502, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dart-dart-insurance", - "name": "dART Insurance", - "symbol": "DART", - "rank": 5503, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anyp-anyprinter", - "name": "AnyPrinter", - "symbol": "ANYP", - "rank": 5504, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cgs-cougarswap", - "name": "CougarSwap", - "symbol": "CGS", - "rank": 5505, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xjp-exciting-japan-coin", - "name": "eXciting Japan Coin", - "symbol": "XJP", - "rank": 5506, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wlvr-wolverine", - "name": "Wolverine", - "symbol": "WLVR", - "rank": 5507, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdoge-miss-doge", - "name": "Miss Doge", - "symbol": "MDOGE", - "rank": 5508, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "maweth-matic-aave-interest-bearing-weth", - "name": "Matic Aave Interest Bearing WETH", - "symbol": "MAWETH", - "rank": 5509, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ksoc-ksoc-sports", - "name": "KSOC Sports", - "symbol": "KSOC", - "rank": 5510, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xrhp-robinhoodprotocol", - "name": "RobinHoodProtocol", - "symbol": "XRHP", - "rank": 5511, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pbom-pocket-bomb", - "name": "Pocket Bomb", - "symbol": "PBOM", - "rank": 5512, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fst-fans-squid", - "name": "Fans Squid", - "symbol": "FST", - "rank": 5513, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rb-royal-bnb", - "name": "Royal BNB", - "symbol": "RB", - "rank": 5514, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alias-alias", - "name": "Alias", - "symbol": "ALIAS", - "rank": 5515, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pins-pinterest", - "name": "Pinterest", - "symbol": "PINS", - "rank": 5516, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pvn-pavecoin", - "name": "Pavecoin", - "symbol": "PVN", - "rank": 5517, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "honey-honeyfarm-finance", - "name": "HoneyFarm Finance", - "symbol": "HONEY", - "rank": 5518, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibanaut-shibanaut-token", - "name": "Shibanaut Token", - "symbol": "SHIBANAUT", - "rank": 5519, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "enruru-enel-russia", - "name": "Enel Russia", - "symbol": "ENRUru", - "rank": 5520, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flokachu-flokachu-token", - "name": "Flokachu Token", - "symbol": "FLOKACHU", - "rank": 5521, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qsd-qian-second-generation-dollar", - "name": "QIAN Second Generation Dollar", - "symbol": "QSD", - "rank": 5522, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ape-apelab", - "name": "ApeLab", - "symbol": "APE", - "rank": 5523, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "socin-soccer-infinity", - "name": "Soccer Infinity", - "symbol": "SOCIN", - "rank": 5524, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ino-nogoaltoken", - "name": "NoGoalToken", - "symbol": "INO", - "rank": 5525, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecoc-ecochain", - "name": "Ecochain", - "symbol": "ECOC", - "rank": 5526, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "usds-usd-smart", - "name": "USD SMART", - "symbol": "USDs", - "rank": 5527, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gild-gilead-sciences", - "name": "Gilead Sciences", - "symbol": "GILD", - "rank": 5528, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibaduff-shibaduff", - "name": "ShibaDuff", - "symbol": "SHIBADUFF", - "rank": 5529, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cold-cold-finance", - "name": "Cold Finance", - "symbol": "COLD", - "rank": 5530, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bana-shibana", - "name": "Shibana", - "symbol": "BANA", - "rank": 5531, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shn-shinedao", - "name": "ShineDAO", - "symbol": "SHN", - "rank": 5532, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbnb-magic-bnb", - "name": "MAGIC BNB", - "symbol": "MBNB", - "rank": 5533, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nxt-next-token", - "name": "Next Token", - "symbol": "NXT", - "rank": 5534, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssb-stream-smart-business", - "name": "Stream Smart Business", - "symbol": "$SSB", - "rank": 5535, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mfloki-meta-floki", - "name": "Meta Floki", - "symbol": "MFLOKI", - "rank": 5536, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "obt-obtoken", - "name": "OBToken", - "symbol": "OBT", - "rank": 5537, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jbl-jabil", - "name": "Jabil", - "symbol": "JBL", - "rank": 5538, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ltn-life-token", - "name": "Life Token", - "symbol": "LTN", - "rank": 5539, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bili-bilibili-inc", - "name": "Bilibili Inc.", - "symbol": "BILI", - "rank": 5540, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trp-tronipay", - "name": "Tronipay", - "symbol": "TRP", - "rank": 5541, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sd-stardust", - "name": "StarDust", - "symbol": "SD", - "rank": 5542, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jks-jinkosolar-holding-co-ltd", - "name": "JinkoSolar Holding Co., Ltd.", - "symbol": "JKS", - "rank": 5543, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csmc-cosmic-music", - "name": "Cosmic Music", - "symbol": "CSMC", - "rank": 5544, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scc-stockchain", - "name": "StockChain", - "symbol": "SCC", - "rank": 5545, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sshld-sunshield", - "name": "SunShield", - "symbol": "SSHLD", - "rank": 5546, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wrkz-wrkzcoin", - "name": "WrkzCoin", - "symbol": "WRKZ", - "rank": 5547, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mgc-multigencapital", - "name": "MultiGenCapital", - "symbol": "MGC", - "rank": 5548, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rpt-represent", - "name": "Represent", - "symbol": "RPT", - "rank": 5549, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsk-bitcoinstaking", - "name": "BitcoinStaking", - "symbol": "BSK", - "rank": 5550, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ape-ape", - "name": "APE", - "symbol": "APE", - "rank": 5551, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "twtr-twitter", - "name": "Twitter", - "symbol": "TWTR", - "rank": 5552, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "joker-joker-token", - "name": "Joker Token", - "symbol": "JOKER", - "rank": 5553, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rot-rotten-token", - "name": "Rotten Token", - "symbol": "ROT", - "rank": 5554, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oap-openalexa-protocol", - "name": "OpenAlexa Protocol", - "symbol": "OAP", - "rank": 5555, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shroomz-crypto-mushroomz", - "name": "Crypto Mushroomz", - "symbol": "SHROOMZ", - "rank": 5556, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibib-shiba-inu-billionaire", - "name": "Shiba Inu Billionaire", - "symbol": "SHIBIB", - "rank": 5557, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dclub-dog-club-token", - "name": "Dog Club Token", - "symbol": "DCLUB", - "rank": 5558, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uplink-uplink", - "name": "UpLink", - "symbol": "UPLINK", - "rank": 5559, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vcc-victorum", - "name": "Victorum", - "symbol": "VCC", - "rank": 5560, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctb-catboy", - "name": "CatBoy", - "symbol": "CTB", - "rank": 5561, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gmng-global-gaming", - "name": "Global Gaming", - "symbol": "GMNG", - "rank": 5562, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ocft-oneconnect", - "name": "OneConnect", - "symbol": "OCFT", - "rank": 5563, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "byron-byron", - "name": "Byron", - "symbol": "BYRON", - "rank": 5564, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "prkl-perkle", - "name": "Perkle", - "symbol": "PRKL", - "rank": 5565, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "solar-solarmoon", - "name": "Solarmoon", - "symbol": "SOLAR", - "rank": 5566, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kp3rb-keep3r-bsc-network", - "name": "Keep3r BSC Network", - "symbol": "KP3RB", - "rank": 5567, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ltd-livetrade-token", - "name": "LiveTrade Token", - "symbol": "LTD", - "rank": 5568, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swaps-nftswaps", - "name": "NFTSwaps", - "symbol": "SWAPS", - "rank": 5569, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "profit-profit-bls", - "name": "Profit Bls", - "symbol": "PROFIT", - "rank": 5570, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fups-feed-pups", - "name": "Feed Pups", - "symbol": "FUPS", - "rank": 5571, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mount-metamounts", - "name": "MetaMounts", - "symbol": "MOUNT", - "rank": 5572, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emax-ethereummax", - "name": "EthereumMax", - "symbol": "EMAX", - "rank": 5573, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tsm-taiwan-semiconductor", - "name": "Taiwan Semiconductor", - "symbol": "TSM", - "rank": 5574, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cs-coin-sack", - "name": "Coin Sack", - "symbol": "CS", - "rank": 5575, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "0753-air-china", - "name": "Air China", - "symbol": "0753", - "rank": 5576, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bmg-black-market-gaming", - "name": "Black Market Gaming", - "symbol": "BMG", - "rank": 5577, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rst-red-shiba-token", - "name": "Red Shiba Token", - "symbol": "RST", - "rank": 5578, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "free-anti-lockdown", - "name": "Anti-Lockdown", - "symbol": "FREE", - "rank": 5579, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "funjo-funjo", - "name": "Funjo", - "symbol": "FUNJO", - "rank": 5580, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "honey-honeypad", - "name": "HONEYPAD", - "symbol": "HONEY", - "rank": 5581, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vk-vallourec", - "name": "Vallourec", - "symbol": "VK", - "rank": 5582, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nut-nut", - "name": "NUT", - "symbol": "NUT", - "rank": 5583, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hyperboost-hyperboost", - "name": "HyperBoost", - "symbol": "HYPERBOOST", - "rank": 5584, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goog-alphabet-inc", - "name": "Alphabet Inc", - "symbol": "GOOG", - "rank": 5585, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yeld-yeld-finance", - "name": "Yeld Finance", - "symbol": "YELD", - "rank": 5586, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "txs-timexspace", - "name": "TIMEXSPACE", - "symbol": "TXS", - "rank": 5587, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "deq-dequant", - "name": "Dequant", - "symbol": "DEQ", - "rank": 5588, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mvdoge-metaverse-doge", - "name": "Metaverse Doge", - "symbol": "$MVDOGE", - "rank": 5589, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tream-stream", - "name": "Stream", - "symbol": "$tream", - "rank": 5590, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sst-socialswap-token", - "name": "Socialswap Token", - "symbol": "SST", - "rank": 5591, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spe-saveplanetearth", - "name": "SavePlanetEarth", - "symbol": "SPE", - "rank": 5592, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibeth-shibeth", - "name": "Shibeth", - "symbol": "SHIBETH", - "rank": 5593, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "libertas-libertas", - "name": "Libertas", - "symbol": "LIBERTAS", - "rank": 5594, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wp-whitepigeon", - "name": "WHITEPIGEON", - "symbol": "WP", - "rank": 5595, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cavo-excavo-finance", - "name": "EXCAVO FINANCE", - "symbol": "CAVO", - "rank": 5596, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spay-synapse-pay", - "name": "Synapse Pay", - "symbol": "SPAY", - "rank": 5597, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rc-retrocade", - "name": "RetroCade", - "symbol": "RC", - "rank": 5598, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dark-darkcoin", - "name": "Darkcoin", - "symbol": "DARK", - "rank": 5599, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cash-litecash", - "name": "Litecash", - "symbol": "CASH", - "rank": 5600, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "meta-metazuckzilla", - "name": "MetaZuckZilla", - "symbol": "META", - "rank": 5601, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hrb-herobattle", - "name": "Herobattle", - "symbol": "HRB", - "rank": 5602, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dangermoon-dangermoon", - "name": "DangerMoon", - "symbol": "DANGERMOON", - "rank": 5603, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idx-index-chain", - "name": "Index Chain", - "symbol": "IDX", - "rank": 5604, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fesbnb-fesbnb", - "name": "FesBNB", - "symbol": "FESBNB", - "rank": 5605, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cw-catwoman", - "name": "CatWoman", - "symbol": "CW", - "rank": 5606, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dona-dona", - "name": "DONA", - "symbol": "DONA", - "rank": 5607, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "romeodoge-romeo-doge", - "name": "Romeo Doge", - "symbol": "ROMEODOGE", - "rank": 5608, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snakes-snakes-on-a-nft-game", - "name": "Snakes On A NFT Game", - "symbol": "SNAKES", - "rank": 5609, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icube-icecubes-finance", - "name": "IceCubes Finance", - "symbol": "ICUBE", - "rank": 5610, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rockstar-rockstar-doge", - "name": "Rockstar Doge", - "symbol": "ROCKSTAR", - "rank": 5611, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "antx-antariksh", - "name": "Antariksh", - "symbol": "ANTX", - "rank": 5612, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meta-meta-grow", - "name": "META GROW", - "symbol": "META", - "rank": 5613, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vero-vero-farm", - "name": "Vero Farm", - "symbol": "VERO", - "rank": 5614, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thundrr-thunder-run-bsc", - "name": "Thunder Run BSC", - "symbol": "THUNDRR", - "rank": 5615, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brigadeiro-brigadeirofinancev2", - "name": "Brigadeiro.FinanceV2", - "symbol": "BRIGADEIRO", - "rank": 5616, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mirai-mirai-token", - "name": "Mirai Token", - "symbol": "MIRAI", - "rank": 5617, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fp-total", - "name": "Total", - "symbol": "FP", - "rank": 5618, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bolo-bolo", - "name": "BOLO", - "symbol": "BOLO", - "rank": 5619, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pupdoge-pup-doge", - "name": "Pup Doge", - "symbol": "PUPDOGE", - "rank": 5620, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cakeup-cakeup", - "name": "CakeUp", - "symbol": "CAKEUP", - "rank": 5621, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nda-aurubis-ag", - "name": "Aurubis AG", - "symbol": "NDA", - "rank": 5622, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "main-main", - "name": "Main", - "symbol": "MAIN", - "rank": 5623, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cdex-codex", - "name": "Codex", - "symbol": "CDEX", - "rank": 5624, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yea-yeafinance", - "name": "YeaFinance", - "symbol": "YEA", - "rank": 5625, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "success-success-inu", - "name": "SUCCESS INU", - "symbol": "SUCCESS", - "rank": 5626, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ans-ans-crypto-coin", - "name": "ANS Crypto Coin", - "symbol": "ANS", - "rank": 5627, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "age-agenor", - "name": "Agenor", - "symbol": "AGE", - "rank": 5628, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ftd-fantasy-doge", - "name": "Fantasy Doge", - "symbol": "FTD", - "rank": 5629, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plt-platinium-token", - "name": "PLATINIUM TOKEN", - "symbol": "PLT", - "rank": 5630, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flokis-flokiswap", - "name": "FlokiSwap", - "symbol": "FLOKIS", - "rank": 5631, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "biden-biden-token", - "name": "BIDEN Token", - "symbol": "BIDEN", - "rank": 5632, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "balle-ballena", - "name": "Ballena", - "symbol": "BALLE", - "rank": 5633, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "melody-melody", - "name": "Melody", - "symbol": "MELODY", - "rank": 5634, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "verse-versewar", - "name": "VerseWar", - "symbol": "VERSE", - "rank": 5635, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wfc-wells-fargo-co", - "name": "Wells Fargo \u0026 Co", - "symbol": "WFC", - "rank": 5636, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "p2e-plant2earn", - "name": "Plant2Earn", - "symbol": "P2E", - "rank": 5637, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chiba-cate-shiba", - "name": "Cate Shiba", - "symbol": "CHIBA", - "rank": 5638, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hodl-hodl", - "name": "HODL", - "symbol": "HODL", - "rank": 5639, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snap-snap", - "name": "Snap", - "symbol": "SNAP", - "rank": 5640, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jpt-jackpot-token", - "name": "Jackpot Token", - "symbol": "JPT", - "rank": 5641, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hyln-hyliion-holdings-corp", - "name": "Hyliion Holdings Corp.", - "symbol": "HYLN", - "rank": 5642, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xpev-xpeng-inc", - "name": "XPENG INC.", - "symbol": "XPEV", - "rank": 5643, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sans-sans-token", - "name": "Sans Token", - "symbol": "SANS", - "rank": 5644, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mero-mero", - "name": "Mero", - "symbol": "MERO", - "rank": 5645, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poc-poc-blockchain", - "name": "POC Blockchain", - "symbol": "POC", - "rank": 5646, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dofi-doge-floki-coin", - "name": "Doge Floki Coin", - "symbol": "DOFI", - "rank": 5647, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tpx-tempur-sealy-international", - "name": "Tempur Sealy International", - "symbol": "TPX", - "rank": 5648, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "calcifer-calcifer", - "name": "Calcifer", - "symbol": "CALCIFER", - "rank": 5649, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pyx-pyxis-pyxisnetwork", - "name": "PYXIS (pyxis.network)", - "symbol": "PYX", - "rank": 5650, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bimp-bimp-finance", - "name": "Bimp Finance", - "symbol": "BIMP", - "rank": 5651, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vcl-volcano-uni", - "name": "Volcano Uni", - "symbol": "VCL", - "rank": 5652, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safu-ceezee-safu", - "name": "CEEZEE SAFU", - "symbol": "SAFU", - "rank": 5653, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aapl-apple-protocol-token", - "name": "Apple Protocol Token", - "symbol": "AAPL", - "rank": 5654, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fenix-fenix-finance", - "name": "Fenix Finance", - "symbol": "FENIX", - "rank": 5655, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mint-mintswap", - "name": "MintSwap", - "symbol": "MINT", - "rank": 5656, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sonic-sonic-token", - "name": "Sonic Token", - "symbol": "SONIC", - "rank": 5657, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibamon-shibamon", - "name": "Shibamon", - "symbol": "SHIBAMON", - "rank": 5658, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "catge-catge-coin", - "name": "Catge Coin", - "symbol": "CATGE", - "rank": 5659, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cert-certrise", - "name": "CertRise", - "symbol": "CERT", - "rank": 5660, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gamesafe-gamesafe", - "name": "Gamesafe", - "symbol": "GAMESAFE", - "rank": 5661, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gogo-gogofinance", - "name": "GOGO.finance", - "symbol": "GOGO", - "rank": 5662, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogdefi-dogdeficoin", - "name": "DogDeFiCoin", - "symbol": "DOGDEFI", - "rank": 5663, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mobtc-mother-of-bitcoin", - "name": "Mother of Bitcoin", - "symbol": "MoBTC", - "rank": 5664, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "twi-tradewin", - "name": "Trade.win", - "symbol": "TWI", - "rank": 5665, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "buu-buu-inu", - "name": "Buu Inu", - "symbol": "BUU", - "rank": 5666, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bac-bank-of-america", - "name": "Bank Of America", - "symbol": "BAC", - "rank": 5667, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "avex-aevolve-token", - "name": "Aevolve Token", - "symbol": "AVEX!", - "rank": 5668, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eca-electra", - "name": "Electra", - "symbol": "ECA", - "rank": 5669, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "feb-foreverblast", - "name": "ForeverBlast", - "symbol": "FEB", - "rank": 5670, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lkod-lukoil-pjsc-adr", - "name": "Lukoil PJSC ADR", - "symbol": "LKOD", - "rank": 5671, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gec-green-energy-coin", - "name": "Green Energy Coin", - "symbol": "GEC", - "rank": 5672, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcat-meta-cat", - "name": "Meta Cat", - "symbol": "MCAT", - "rank": 5673, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "set-stabilize-token", - "name": "Stabilize Token", - "symbol": "SET", - "rank": 5674, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yrt-yearrise", - "name": "YearRise", - "symbol": "YRT", - "rank": 5675, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "eft-eft", - "name": "EFT", - "symbol": "EFT", - "rank": 5676, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safemoona-safemoonavax", - "name": "SafemoonAvax", - "symbol": "SAFEMOONA", - "rank": 5677, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cptl-capitol", - "name": "Capitol", - "symbol": "CPTL", - "rank": 5678, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rdoge-royal-doge", - "name": "Royal Doge", - "symbol": "RDOGE", - "rank": 5679, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hugo-hugo-finance", - "name": "Hugo Finance", - "symbol": "HUGO", - "rank": 5680, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fzl-frogzilla", - "name": "FrogZilla", - "symbol": "FZL", - "rank": 5681, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tbb-trade-butler-bot", - "name": "Trade Butler Bot", - "symbol": "TBB", - "rank": 5682, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mi-mosterisland", - "name": "MosterIsland", - "symbol": "MI", - "rank": 5683, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wjxn-jax-network", - "name": "Jax Network", - "symbol": "WJXN", - "rank": 5684, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "meo-meotools", - "name": "Meo.tools", - "symbol": "MEO", - "rank": 5685, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "upt-upfund", - "name": "UPFUND", - "symbol": "UPT", - "rank": 5686, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wpx-wallet-plus-x", - "name": "Wallet Plus X", - "symbol": "WPX", - "rank": 5687, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ezj-easyjet", - "name": "EasyJet", - "symbol": "EZJ", - "rank": 5688, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "apa-apache", - "name": "Apache", - "symbol": "APA", - "rank": 5689, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kioo-kioo", - "name": "KIOO", - "symbol": "KIOO", - "rank": 5690, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "bfc-big-fund-capital-dao", - "name": "Big Fund Capital DAO", - "symbol": "BFC", - "rank": 5691, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "safu-safu-network", - "name": "SAFU NETWORK", - "symbol": "SAFU", - "rank": 5692, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lbcc-lightbeam-courier-coin", - "name": "Lightbeam Courier Coin", - "symbol": "LBCC", - "rank": 5693, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "lecliente-le-caliente", - "name": "LE CALIENTE", - "symbol": "LECLIENTE", - "rank": 5694, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chby-chubbyboy", - "name": "ChubbyBoy", - "symbol": "CHBY", - "rank": 5695, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "urac-uranus", - "name": "Uranus", - "symbol": "URAC", - "rank": 5696, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rosnru-nk-rosneft", - "name": "NK Rosneft", - "symbol": "ROSNru", - "rank": 5697, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "weens-ween-token", - "name": "Ween Token", - "symbol": "WEENS", - "rank": 5698, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rualru-united-company-rusal", - "name": "United Company Rusal", - "symbol": "RUALru", - "rank": 5699, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyegg-babyegg", - "name": "BabyEgg", - "symbol": "BABYEGG", - "rank": 5700, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibabnb-shibabnborg", - "name": "ShibaBNB.org", - "symbol": "SHIBABNB", - "rank": 5701, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sec-smilecoin", - "name": "Smilecoin", - "symbol": "SEC", - "rank": 5702, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "octane-octane-protocol-token", - "name": "Octane Protocol Token", - "symbol": "OCTANE", - "rank": 5703, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "khalifa-khalifa-finance", - "name": "Khalifa Finance", - "symbol": "KHALIFA", - "rank": 5704, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btn-bitnorm", - "name": "BitNorm", - "symbol": "BTN", - "rank": 5705, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "db-down-bad", - "name": "Down Bad", - "symbol": "DB", - "rank": 5706, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bscake-bunscake", - "name": "Bunscake", - "symbol": "BSCAKE", - "rank": 5707, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "big-thebigcoin", - "name": "THEBIGCOIN", - "symbol": "BIG", - "rank": 5708, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pbk-profit-bank", - "name": "Profit Bank", - "symbol": "PBK", - "rank": 5709, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kimj-kimjongmoon", - "name": "KimJongMoon", - "symbol": "KIMJ", - "rank": 5710, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nva-neeva-defi", - "name": "Neeva Defi", - "symbol": "NVA", - "rank": 5711, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fmon-flokimon", - "name": "FlokiMON", - "symbol": "FMON", - "rank": 5712, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pp-pool-party", - "name": "Pool Party", - "symbol": "PP", - "rank": 5713, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egg-waves-ducks", - "name": "Waves Ducks", - "symbol": "EGG", - "rank": 5714, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftsol-nft-solpad", - "name": "NFT SolPad", - "symbol": "NFTSOL", - "rank": 5715, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mat-matka", - "name": "Matka", - "symbol": "MAT", - "rank": 5716, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hpq-hp", - "name": "HP", - "symbol": "HPQ", - "rank": 5717, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chbt-chbt", - "name": "CHBT", - "symbol": "CHBT", - "rank": 5718, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bbby-bed-bath-beyond-inc", - "name": "Bed Bath \u0026 Beyond Inc.", - "symbol": "BBBY", - "rank": 5719, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyx-babyxape", - "name": "BabyXape", - "symbol": "BABYX", - "rank": 5720, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rnrc-rocknraincoin", - "name": "RocknRainCoin", - "symbol": "RNRC", - "rank": 5721, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "yelp-yelp", - "name": "Yelp", - "symbol": "YELP", - "rank": 5722, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mpig-meta-pig", - "name": "Meta Pig", - "symbol": "MPIG", - "rank": 5723, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "sayan-saiyan-inu", - "name": "Saiyan Inu", - "symbol": "SAYAN", - "rank": 5724, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ss-sharkstake", - "name": "SharkStake", - "symbol": "$SS", - "rank": 5725, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bte-bitweb", - "name": "Bitweb", - "symbol": "BTE", - "rank": 5726, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sgt-sharedstake-governance-token", - "name": "SharedStake Governance Token", - "symbol": "SGT", - "rank": 5727, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "intx-intexcoin", - "name": "Intexcoin", - "symbol": "INTX", - "rank": 5728, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pw-petworld", - "name": "PetWorld", - "symbol": "PW", - "rank": 5729, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hmng-hummingbird-finance", - "name": "HummingBird Finance", - "symbol": "HMNG", - "rank": 5730, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zch-0cash", - "name": "0cash", - "symbol": "ZCH", - "rank": 5731, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pft-pitch-finance-token", - "name": "Pitch Finance Token", - "symbol": "PFT", - "rank": 5732, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbs-baby-sharkfinance", - "name": "Baby-Shark.Finance", - "symbol": "BBS", - "rank": 5733, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zmbe-zombie", - "name": "Zombie", - "symbol": "ZMBE", - "rank": 5734, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dai-daimler-ag", - "name": "Daimler AG", - "symbol": "DAI", - "rank": 5735, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btcp-bitcoinprint", - "name": "BitcoinPrint", - "symbol": "BTCP", - "rank": 5736, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iex-internal-exchange-coin", - "name": "Internal Exchange Coin", - "symbol": "IEX", - "rank": 5737, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "beaglecake-beaglecake", - "name": "BeagleCake", - "symbol": "BEAGLECAKE", - "rank": 5738, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "burnrocket-burnrocket", - "name": "BurnRocket", - "symbol": "BURNROCKET", - "rank": 5739, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "octa-octapay", - "name": "OctaPay", - "symbol": "OCTA", - "rank": 5740, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vnet-21vianet-group-inc", - "name": "21Vianet Group, Inc.", - "symbol": "VNET", - "rank": 5741, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "upwk-upwork-inc", - "name": "Upwork Inc.", - "symbol": "UPWK", - "rank": 5742, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aspn-aspenlabs", - "name": "AspenLabs", - "symbol": "ASPN", - "rank": 5743, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hideous-hideous-finance", - "name": "Hideous Finance", - "symbol": "HIDEOUS", - "rank": 5744, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "koromaru-koromaru", - "name": "KOROMARU", - "symbol": "KOROMARU", - "rank": 5745, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "egg-eggzilla", - "name": "eggzilla", - "symbol": "EGG", - "rank": 5746, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tata-hakuna-matata", - "name": "Hakuna Matata", - "symbol": "TATA", - "rank": 5747, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nhc-neo-holistic-coin", - "name": "Neo Holistic Coin", - "symbol": "NHC", - "rank": 5748, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "avro-avrobio-inc", - "name": "AVROBIO, Inc.", - "symbol": "AVRO", - "rank": 5749, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rtngb-restaurant-group", - "name": "Restaurant Group", - "symbol": "RTNgb", - "rank": 5750, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bg-bagus-wallet", - "name": "Bagus Wallet", - "symbol": "BG", - "rank": 5751, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crex-crex-token", - "name": "Crex Token", - "symbol": "CREX", - "rank": 5752, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ppug-pizza-pug-coin", - "name": "Pizza Pug Coin", - "symbol": "PPUG", - "rank": 5753, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cer-cerealia", - "name": "Cerealia", - "symbol": "CER", - "rank": 5754, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbird-moonbird", - "name": "Moonbird", - "symbol": "MBIRD", - "rank": 5755, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tep-tepleton", - "name": "Tepleton", - "symbol": "TEP", - "rank": 5756, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mnttbsc-moontrustbsc", - "name": "MoonTrustBSC", - "symbol": "MNTTBSC", - "rank": 5757, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metashiba-metashiba-bsc", - "name": "MetaShiba BSC", - "symbol": "METASHIBA", - "rank": 5758, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nwset-2100news-ethereum-tokens-index-fund", - "name": "2100news Ethereum Tokens Index Fund", - "symbol": "NWSET", - "rank": 5759, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "part-particle-technology", - "name": "Particle Technology", - "symbol": "PART", - "rank": 5760, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "djn-fenix-danjon", - "name": "Fenix Danjon", - "symbol": "DJN", - "rank": 5761, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "payt-payaccept", - "name": "PayAccept", - "symbol": "PAYT", - "rank": 5762, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bpe-bper-banca-spa", - "name": "Bper Banca SPA", - "symbol": "BPE", - "rank": 5763, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "artx-artx", - "name": "ARTX", - "symbol": "ARTX", - "rank": 5764, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ntm-netm", - "name": "NetM", - "symbol": "NTM", - "rank": 5765, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "babyuni-babyuni", - "name": "BabyUni", - "symbol": "BABYUNI", - "rank": 5766, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thunderada-thunderadaapp", - "name": "ThunderADA.app", - "symbol": "THUNDERADA", - "rank": 5767, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slush-iceslush-finance", - "name": "IceSlush Finance", - "symbol": "SLUSH", - "rank": 5768, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nip-catnip", - "name": "CatNIP", - "symbol": "NIP", - "rank": 5769, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wgirl-whalegirl", - "name": "WhaleGirl", - "symbol": "WGIRL", - "rank": 5770, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdogex-babydogex", - "name": "BabyDogeX", - "symbol": "BDOGEX", - "rank": 5771, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fdot-fanadot", - "name": "FanaDOT", - "symbol": "FDOT", - "rank": 5772, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wamo-wamo", - "name": "WAMO", - "symbol": "WAMO", - "rank": 5773, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dimi-diminutive-coin", - "name": "Diminutive Coin", - "symbol": "DIMI", - "rank": 5774, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mcc-microcoin", - "name": "MicroCoin", - "symbol": "MCC", - "rank": 5775, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kurai-kurai-metaverse", - "name": "Kurai MetaVerse", - "symbol": "KURAI", - "rank": 5776, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "medi-mediconnect", - "name": "MediConnect", - "symbol": "MEDI", - "rank": 5777, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bwt-babywhitetiger", - "name": "BabyWhiteTiger", - "symbol": "BWT", - "rank": 5778, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ogods-gotogods", - "name": "Gotogods", - "symbol": "OGODS", - "rank": 5779, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rad-rite-aid", - "name": "Rite Aid", - "symbol": "RAD", - "rank": 5780, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lot-lottery-token", - "name": "Lottery Token", - "symbol": "LOT", - "rank": 5781, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ko-coca-cola", - "name": "Coca-Cola", - "symbol": "KO", - "rank": 5782, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctc-cryptocoin", - "name": "Cryptocoin", - "symbol": "CTC", - "rank": 5783, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slush-slushie-capital", - "name": "Slushie Capital", - "symbol": "SLUSH", - "rank": 5784, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "space-dog-space-dog", - "name": "Space dog", - "symbol": "SPACE DOG", - "rank": 5785, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zdex-zeedex", - "name": "Zeedex", - "symbol": "ZDEX", - "rank": 5786, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ets-ethersniper", - "name": "Ethersniper", - "symbol": "ETS", - "rank": 5787, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnbt-genebank-token", - "name": "Genebank Token", - "symbol": "GNBT", - "rank": 5788, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aws-aurussilver", - "name": "AurusSILVER", - "symbol": "AWS", - "rank": 5789, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "herpes-herpes", - "name": "Herpes", - "symbol": "HERPES", - "rank": 5790, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "evrt-everest-token", - "name": "Everest Token", - "symbol": "EVRT", - "rank": 5791, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "footie-footie-plus", - "name": "Footie Plus", - "symbol": "FOOTIE", - "rank": 5792, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alphashib-alpha-shiba-inu", - "name": "Alpha Shiba Inu", - "symbol": "ALPHASHIB", - "rank": 5793, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmt-dark-matter", - "name": "Dark Matter", - "symbol": "DMT", - "rank": 5794, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cats-catscoin", - "name": "Catscoin", - "symbol": "CATS", - "rank": 5795, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hkitties-hungry-kitty", - "name": "Hungry Kitty", - "symbol": "HKITTIES", - "rank": 5796, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cakebank-cake-bank", - "name": "Cake Bank", - "symbol": "CAKEBANK", - "rank": 5797, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shih-shih-tzu", - "name": "Shih Tzu", - "symbol": "SHIH", - "rank": 5798, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "buff-buffalo-swap", - "name": "Buffalo Swap", - "symbol": "BUFF", - "rank": 5799, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bullish-bullishapes", - "name": "BullishApes", - "symbol": "BULLISH", - "rank": 5800, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tetsu-tetsu-inu", - "name": "Tetsu Inu", - "symbol": "TETSU", - "rank": 5801, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ms-monster-slayer", - "name": "Monster Slayer", - "symbol": "MS", - "rank": 5802, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibcake-shibcake", - "name": "ShibCake", - "symbol": "SHIBCAKE", - "rank": 5803, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1trc-1tronic-network", - "name": "1TRONIC Network", - "symbol": "1TRC", - "rank": 5804, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zfarm-zfarm-token", - "name": "ZFARM Token", - "symbol": "ZFARM", - "rank": 5805, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "goal-goal", - "name": "GOAL", - "symbol": "GOAL", - "rank": 5806, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bull-bull-finance", - "name": "Bull Finance", - "symbol": "BULL", - "rank": 5807, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibm-shiba-moon", - "name": "Shiba Moon", - "symbol": "SHIBM", - "rank": 5808, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slush-slush-puppie", - "name": "Slush Puppie", - "symbol": "SLUSH", - "rank": 5809, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "oix-oil-portfolio-index", - "name": "Oil Portfolio Index", - "symbol": "OIX", - "rank": 5810, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcna-bitcanna", - "name": "BitCanna", - "symbol": "BCNA", - "rank": 5811, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rick-infinite-ricks", - "name": "Infinite Ricks", - "symbol": "RICK", - "rank": 5812, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ns-nodestats", - "name": "Nodestats", - "symbol": "NS", - "rank": 5813, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "krajcar-bitcoin-betyar", - "name": "BITCOIN BETYAR", - "symbol": "KRAJCAR", - "rank": 5814, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "makk-makk", - "name": "Makk", - "symbol": "MAKK", - "rank": 5815, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sug-sulgecoin", - "name": "Sulgecoin", - "symbol": "SUG", - "rank": 5816, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mks-makes", - "name": "Makes", - "symbol": "MKS", - "rank": 5817, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "leaf-legal-leaf", - "name": "Legal LEAF", - "symbol": "LEAF", - "rank": 5818, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "floki-baby-moon-floki", - "name": "Baby Moon Floki", - "symbol": "FLOKI", - "rank": 5819, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnar-gnar-token", - "name": "GNAR TOKEN", - "symbol": "GNAR", - "rank": 5820, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "miso-miso-swap", - "name": "Miso Swap", - "symbol": "MISO", - "rank": 5821, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "send-social-send", - "name": "Social Send", - "symbol": "SEND", - "rank": 5822, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tell-tellurion", - "name": "Tellurion", - "symbol": "TELL", - "rank": 5823, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bear-bear-coin", - "name": "BEAR Coin", - "symbol": "BEAR", - "rank": 5824, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "jpaw-jpaw-inu", - "name": "Jpaw Inu", - "symbol": "JPAW", - "rank": 5825, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bsi-bsi-token", - "name": "BSI Token", - "symbol": "BSI", - "rank": 5826, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flapx-flapx", - "name": "FlapX", - "symbol": "FLAPX", - "rank": 5827, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fl-freeliquid", - "name": "Freeliquid", - "symbol": "FL", - "rank": 5828, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "webn-webn-token", - "name": "WEBN token", - "symbol": "WEBN", - "rank": 5829, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dune-dune-token", - "name": "Dune Token", - "symbol": "DUNE", - "rank": 5830, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fucha-funcha-coin", - "name": "FUNCHA COIN", - "symbol": "FUCHA", - "rank": 5831, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "brp-bor-protocol", - "name": "Bor Protocol", - "symbol": "BRP", - "rank": 5832, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btrl-bitcoinregular", - "name": "BitcoinRegular", - "symbol": "BTRL", - "rank": 5833, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "viagra-viagra-token", - "name": "Viagra Token", - "symbol": "VIAGRA", - "rank": 5834, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dollar-dollar-international", - "name": "Dollar INTERNATIONAL", - "symbol": "DOLLAR", - "rank": 5835, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rmoon-rocketmoon", - "name": "RocketMoon", - "symbol": "RMOON", - "rank": 5836, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kdoge-kingdoge", - "name": "KingDoge", - "symbol": "KDOGE", - "rank": 5837, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bshiba-bscshiba", - "name": "bscShiba", - "symbol": "BSHIBA", - "rank": 5838, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kaiinu-kai-inu", - "name": "KAI INU", - "symbol": "KAIINU", - "rank": 5839, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nwslt-2100news-crypto-large-caps-index-fund", - "name": "2100news Crypto Large-caps Index Fund", - "symbol": "NWSLT", - "rank": 5840, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mpool-metapool", - "name": "Metapool", - "symbol": "MPOOL", - "rank": 5841, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aow-art-of-war", - "name": "Art of War", - "symbol": "AOW", - "rank": 5842, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "binb-buildin", - "name": "Buildin", - "symbol": "BINB", - "rank": 5843, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcau-meld-gold", - "name": "Meld Gold", - "symbol": "MCAU", - "rank": 5844, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uber-uber-technologies-inc", - "name": "Uber Technologies Inc", - "symbol": "UBER", - "rank": 5845, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stkd-stakd-token", - "name": "Stakd Token", - "symbol": "STKD", - "rank": 5846, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvlvr-revolver-token", - "name": "Revolver Token", - "symbol": "RVLVR", - "rank": 5847, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mau-egyptian-mau", - "name": "Egyptian MAU", - "symbol": "MAU", - "rank": 5848, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ethm-ethereum-meta", - "name": "Ethereum Meta", - "symbol": "ETHM", - "rank": 5849, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ain-ainori2902", - "name": "AINORI", - "symbol": "AIN", - "rank": 5850, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snb-safe-nebula", - "name": "Safe Nebula", - "symbol": "SNB", - "rank": 5851, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scsx-secure-cash", - "name": "Secure Cash", - "symbol": "SCSX", - "rank": 5852, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ptn-palletone", - "name": "PalletOne", - "symbol": "PTN", - "rank": 5853, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tkrev-token-revolution", - "name": "TOKEN REVOLUTION", - "symbol": "TKREV", - "rank": 5854, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smile-smile-token", - "name": "Smile Token", - "symbol": "SMILE", - "rank": 5855, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sby-shelby-token", - "name": "Shelby Token", - "symbol": "SBY", - "rank": 5856, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "psn-polkasocial-network", - "name": "Polkasocial Network", - "symbol": "PSN", - "rank": 5857, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cyo-calypso", - "name": "Calypso", - "symbol": "CYO", - "rank": 5858, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "il8p-infiniloop", - "name": "InfiniLooP", - "symbol": "IL8P", - "rank": 5859, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lovecoin-lovecoin-token", - "name": "Lovecoin Token", - "symbol": "LOVECOIN", - "rank": 5860, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfa-nftfundart", - "name": "NFTFundArt", - "symbol": "NFA", - "rank": 5861, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtssru-mts", - "name": "MTS", - "symbol": "MTSSru", - "rank": 5862, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bjoe-babytraderjoe", - "name": "BabyTraderJoe", - "symbol": "BJOE", - "rank": 5863, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ifex-interfinex-bills", - "name": "Interfinex Bills", - "symbol": "IFEX", - "rank": 5864, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftascii-nftascii", - "name": "NFTASCII", - "symbol": "NFTASCII", - "rank": 5865, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stakec-stakecentercash", - "name": "StakeCenterCash", - "symbol": "STAKEC", - "rank": 5866, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pock-pocket-finance", - "name": "Pocket Finance", - "symbol": "POCK", - "rank": 5867, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "theca-theca", - "name": "Theca", - "symbol": "THECA", - "rank": 5868, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mochi-mochi", - "name": "MOCHI", - "symbol": "MOCHI", - "rank": 5869, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fomobaby-fomo-baby", - "name": "FOMO BABY", - "symbol": "FOMOBABY", - "rank": 5870, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hungrydoge-hunger-doge", - "name": "Hunger Doge", - "symbol": "HUNGRYDOGE", - "rank": 5871, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iqg-iq-coin", - "name": "IQ Coin", - "symbol": "IQG", - "rank": 5872, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfrm-yearn-finance-red-moon", - "name": "Yearn Finance Red Moon", - "symbol": "YFRM", - "rank": 5873, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sandman-sandman", - "name": "SandMan", - "symbol": "SANDMAN", - "rank": 5874, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nwscot-2100news-coins-index-fund", - "name": "2100news Coins Index Fund", - "symbol": "NWSCOT", - "rank": 5875, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fshib-floki-shiba", - "name": "Floki Shiba", - "symbol": "FSHIB", - "rank": 5876, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bcoin-bryo-xchange-coin", - "name": "BRYO Xchange Coin", - "symbol": "BCOIN", - "rank": 5877, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "kshiba-kitty-shiba", - "name": "Kitty Shiba", - "symbol": "KSHIBA", - "rank": 5878, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ttt-tt-token", - "name": "TT Token", - "symbol": "TTT", - "rank": 5879, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "voyrme-voyr", - "name": "VOYR", - "symbol": "VOYRME", - "rank": 5880, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fsly-fastly-inc", - "name": "Fastly, Inc.", - "symbol": "FSLY", - "rank": 5881, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fit-financial-investment-token", - "name": "FINANCIAL INVESTMENT TOKEN", - "symbol": "FIT", - "rank": 5882, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rex-rewardsx", - "name": "RewardsX", - "symbol": "REX", - "rank": 5882, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cock-shibacock", - "name": "Shibacock", - "symbol": "COCK", - "rank": 5884, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pnixs-phoenix-defi-finance", - "name": "Phoenix Defi Finance", - "symbol": "PNIXS", - "rank": 5885, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aex-aex-25-token", - "name": "AEX 25 Token", - "symbol": "AEX", - "rank": 5886, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "c4t-coin4trade", - "name": "Coin4Trade", - "symbol": "C4T", - "rank": 5887, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dxc-dex-trade-coin", - "name": "Dex-Trade Coin", - "symbol": "DXC", - "rank": 5888, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "khc-the-kraft-heinz-company", - "name": "The Kraft Heinz Company", - "symbol": "KHC", - "rank": 5889, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hrld-haroldcoin", - "name": "Haroldcoin", - "symbol": "HRLD", - "rank": 5890, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hcs-help-coins", - "name": "Help Coins", - "symbol": "HCS", - "rank": 5891, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metacz-metacz", - "name": "MetaCZ", - "symbol": "METACZ", - "rank": 5892, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "papp-papp-mobile", - "name": "PAPP Mobile", - "symbol": "PAPP", - "rank": 5892, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ani-animecoin", - "name": "Animecoin", - "symbol": "ANI", - "rank": 5894, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fma-flama", - "name": "FLAMA", - "symbol": "FMA", - "rank": 5895, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dxm-dogexmoon", - "name": "DogeXmoon", - "symbol": "DXM", - "rank": 5895, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rtkmru-rostelecom", - "name": "Rostelecom", - "symbol": "RTKMru", - "rank": 5897, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yee-yee-token", - "name": "YEE Token", - "symbol": "YEE", - "rank": 5897, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyfrog-babyfrog", - "name": "BabyFrog", - "symbol": "BABYFROG", - "rank": 5898, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "runtz-runtzcoin", - "name": "RuntzCoin", - "symbol": "RUNTZ", - "rank": 5899, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smoon-saylormoon", - "name": "SaylorMoon", - "symbol": "SMOON", - "rank": 5901, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dao-gamedao", - "name": "GameDAO", - "symbol": "DAO", - "rank": 5902, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sphynx-sphynx-token", - "name": "Sphynx Token", - "symbol": "SPHYNX", - "rank": 5903, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pome-pomerocket", - "name": "PomeRocket", - "symbol": "POME", - "rank": 5903, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hpad-harmonypad", - "name": "HarmonyPad", - "symbol": "HPAD", - "rank": 5905, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "strike-strike-coin", - "name": "STRIKE COIN", - "symbol": "STRIKE", - "rank": 5906, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pdai-prime-dai", - "name": "Prime DAI", - "symbol": "PDAI", - "rank": 5906, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "imi-influencer", - "name": "Influencer", - "symbol": "IMI", - "rank": 5908, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ani-anime-token", - "name": "Anime Token", - "symbol": "ANI", - "rank": 5909, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dint-dint-token", - "name": "DINT Token", - "symbol": "DINT", - "rank": 5910, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "leo-astrofarms", - "name": "AstroFarms", - "symbol": "LEO", - "rank": 5911, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vancat-vancat-token", - "name": "VANCAT TOKEN", - "symbol": "VANCAT", - "rank": 5912, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lloy-lloyds", - "name": "Lloyds", - "symbol": "LLOY", - "rank": 5913, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mushu-mushu-finance", - "name": "Mushu Finance", - "symbol": "MUSHU", - "rank": 5914, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnj-binjitcoin", - "name": "Binjitcoin", - "symbol": "BNJ", - "rank": 5914, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bbc-dao-big-brain-capital-dao", - "name": "Big Brain Capital DAO", - "symbol": "BBC DAO", - "rank": 5916, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "chik-chickenkebab-finance", - "name": "ChickenKebab Finance", - "symbol": "CHIk", - "rank": 5916, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbr-niobio-cash", - "name": "Niobio Cash", - "symbol": "NBR", - "rank": 5918, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "scake-sharkcake", - "name": "SHARKCAKE", - "symbol": "SCAKE", - "rank": 5919, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "owo-oneworld", - "name": "Oneworld", - "symbol": "OWO", - "rank": 5920, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "acd-alliance-cargo-direct", - "name": "Alliance Cargo Direct", - "symbol": "ACD", - "rank": 5921, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "boocake-boocake", - "name": "BooCake", - "symbol": "BOOCAKE", - "rank": 5922, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "afo-all-for-one-business", - "name": "All For One Business", - "symbol": "AFO", - "rank": 5923, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mgoat-metagoat", - "name": "MetaGoat", - "symbol": "MGOAT", - "rank": 5923, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gntl-gntl", - "name": "GNTL", - "symbol": "GNTL", - "rank": 5924, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "swerve-swerve-protocol", - "name": "SWERVE Protocol", - "symbol": "SWERVE", - "rank": 5925, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crc-crypto-classic", - "name": "Crypto Classic", - "symbol": "CRC", - "rank": 5927, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nfteez-nfteez", - "name": "NFTeez", - "symbol": "NFTEEZ", - "rank": 5927, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mar-compumar", - "name": "CompuMar", - "symbol": "MAR", - "rank": 5929, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rudolph-rudolph-coin", - "name": "Rudolph Coin", - "symbol": "RUDOLPH", - "rank": 5929, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pos-proof-of-stake-coin", - "name": "Proof of Stake Coin", - "symbol": "PoS", - "rank": 5931, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xiaz2-xiazfinance2", - "name": "Xiazfinance2", - "symbol": "XIAZ2", - "rank": 5932, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1530-3sbio", - "name": "3SBio", - "symbol": "1530", - "rank": 5933, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fdoge-first-doge-finance", - "name": "First Doge Finance", - "symbol": "FDOGE", - "rank": 5934, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bravo-bravocoin", - "name": "BravoCoin", - "symbol": "BRAVO", - "rank": 5935, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "punks-punk-shiba", - "name": "Punk Shiba", - "symbol": "PUNKS", - "rank": 5936, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "inrtoken-inrtoken", - "name": "INRTOKEN", - "symbol": "INRTOKEN", - "rank": 5936, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rug-r-u-generous", - "name": "R U Generous", - "symbol": "RUG", - "rank": 5937, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vest-vestchain", - "name": "VestChain", - "symbol": "VEST", - "rank": 5939, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minifootball-minifootball", - "name": "MiniFootball", - "symbol": "MINIFOOTBA", - "rank": 5940, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dicash-dinheiruscash", - "name": "Dinheirus.Cash", - "symbol": "DICASH", - "rank": 5940, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flishu-flokishu", - "name": "FloKishu", - "symbol": "FLISHU", - "rank": 5941, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "butter-butter-token", - "name": "Butter Token", - "symbol": "BUTTER", - "rank": 5943, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "2777-guangzhou-rampf-properties", - "name": "Guangzhou R\u0026F Properties", - "symbol": "2777", - "rank": 5944, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brilx-brilliancex", - "name": "BrillianceX", - "symbol": "BRILX", - "rank": 5945, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kuka-kukachu", - "name": "Kukachu", - "symbol": "KUKA", - "rank": 5946, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nifty-niftynft", - "name": "NiftyNFT", - "symbol": "NIFTY", - "rank": 5947, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gems-safegem", - "name": "SafeGem", - "symbol": "GEMS", - "rank": 5948, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mskp-muresk-protocol", - "name": "Muresk Protocol", - "symbol": "MSKP", - "rank": 5949, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "opm-omega-protocol-money", - "name": "Omega Protocol Money", - "symbol": "OPM", - "rank": 5949, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpz-cashpay-token", - "name": "CashPay Token", - "symbol": "CPZ", - "rank": 5951, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yep-yep-coin", - "name": "YEP Coin", - "symbol": "YEP", - "rank": 5952, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tcda-tricida", - "name": "Tricida", - "symbol": "TCDA", - "rank": 5953, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "snrw-snrwe", - "name": "SNRW.e", - "symbol": "SNRW", - "rank": 5954, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ohl-obrasc-n-huarte-lain-s-a", - "name": "Obrascón Huarte Lain, S.A.", - "symbol": "OHL", - "rank": 5955, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hippie-hippie-inu", - "name": "Hippie Inu", - "symbol": "HIPPIE", - "rank": 5955, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fes-feedeveryshiba", - "name": "FeedEveryShiba", - "symbol": "FES", - "rank": 5956, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nwsbct-2100news-blue-chip-index-fund", - "name": "2100news Blue Chip Index Fund", - "symbol": "NWSBCT", - "rank": 5958, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftpuzzle-nftpuzzle", - "name": "NFTPuzzle", - "symbol": "NFTPUZZLE", - "rank": 5958, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tmh-trusmarkethub-token", - "name": "TrusMarketHub Token", - "symbol": "TMH", - "rank": 5960, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bzun-baozun-inc", - "name": "Baozun Inc.", - "symbol": "BZUN", - "rank": 5961, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pom-pompom", - "name": "PomPom", - "symbol": "POM", - "rank": 5961, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bushido-bushido", - "name": "BUSHIDO", - "symbol": "BUSHIDO", - "rank": 5962, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "paisa-paisacoin", - "name": "PAISACOIN", - "symbol": "PAISA", - "rank": 5964, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smbelon-self-made-billionaire-elon-token", - "name": "Self Made Billionaire Elon Token", - "symbol": "SMBELON", - "rank": 5964, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bkishu-buffed-kishu", - "name": "Buffed Kishu", - "symbol": "BKISHU", - "rank": 5965, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tmn-translateme-network-token", - "name": "TranslateMe Network Token", - "symbol": "TMN", - "rank": 5967, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "genix-genix", - "name": "Genix", - "symbol": "GENIX", - "rank": 5968, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vqr-virtual-quest-room-coin", - "name": "Virtual Quest Room Coin", - "symbol": "VQR", - "rank": 5969, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "uct-ubique-chain-of-things", - "name": "Ubique Chain Of Things", - "symbol": "UCT", - "rank": 5970, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tyme-tyme-token", - "name": "TYME TOKEN", - "symbol": "TYME", - "rank": 5971, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "2899-zijin-mining", - "name": "Zijin Mining", - "symbol": "2899", - "rank": 5971, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "post-piece-of-shit-token", - "name": "Piece of Shit Token", - "symbol": "POST", - "rank": 5972, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "plg-pledge-coin", - "name": "Pledge Coin", - "symbol": "PLG", - "rank": 5974, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "exg-exgold", - "name": "EXGold", - "symbol": "EXG", - "rank": 5975, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sora-sorachancoin", - "name": "SorachanCoin", - "symbol": "SORA", - "rank": 5976, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nra-nora-token", - "name": "Nora Token", - "symbol": "NRA", - "rank": 5977, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shx-stronghold-token", - "name": "Stronghold Token", - "symbol": "SHX", - "rank": 5977, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rosn-rosneft", - "name": "Rosneft", - "symbol": "ROSN", - "rank": 5979, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iqq-iqoniq", - "name": "Iqoniq", - "symbol": "IQQ", - "rank": 5980, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xln-lunarium-coin", - "name": "Lunarium Coin", - "symbol": "XLN", - "rank": 5981, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bulld-bulldoge-inu", - "name": "Bulldoge Inu", - "symbol": "BULLD", - "rank": 5982, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "teat-teal", - "name": "TEAL", - "symbol": "TEAT", - "rank": 5982, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "x-us-steel-corp", - "name": "US Steel Corp", - "symbol": "X", - "rank": 5984, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bntu-buntu", - "name": "Buntu", - "symbol": "BNTU", - "rank": 5984, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ttoken-ttoken", - "name": "TTOKEN", - "symbol": "TTOKEN", - "rank": 5986, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btsucn-btsunicorn", - "name": "BTSunicorn", - "symbol": "BTSUCN", - "rank": 5986, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bitup-bitup", - "name": "BitUp", - "symbol": "BITUP", - "rank": 5988, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jwn-nordstrom", - "name": "Nordstrom", - "symbol": "JWN", - "rank": 5989, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minifloki-mini-floki", - "name": "Mini Floki", - "symbol": "MINIFLOKI", - "rank": 5990, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rocks-social-rocket", - "name": "Social Rocket", - "symbol": "ROCKS", - "rank": 5991, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "anonfloki-anonfloki", - "name": "AnonFloki", - "symbol": "ANONFLOKI", - "rank": 5991, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zin-zomainfinity", - "name": "ZomaInfinity", - "symbol": "ZIN", - "rank": 5993, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wagmi-wagmi", - "name": "WAGMI", - "symbol": "WAGMI", - "rank": 5994, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bgo-bingo-cash", - "name": "Bingo Cash", - "symbol": "BGO", - "rank": 5994, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sdog-snowdog", - "name": "Snowdog", - "symbol": "SDOG", - "rank": 5995, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ox-orcax", - "name": "OrcaX", - "symbol": "OX", - "rank": 5997, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mxf-mixty-finance", - "name": "MIXTY FINANCE", - "symbol": "MXF", - "rank": 5998, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qiq-qoiniq", - "name": "QoinIQ", - "symbol": "QIQ", - "rank": 5999, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ldoge-lambdoge", - "name": "LambDoge", - "symbol": "LDOGE", - "rank": 6000, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pick-pick", - "name": "PICK", - "symbol": "PICK", - "rank": 6001, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spwr-sunpower-corporation", - "name": "SunPower Corporation", - "symbol": "SPWR", - "rank": 6002, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbdc-meta-buff-doge-coin", - "name": "Meta Buff Doge Coin", - "symbol": "MBDC", - "rank": 6002, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "porky-porkycoin", - "name": "PorkyCoin", - "symbol": "PORKY", - "rank": 6003, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bkf-bking-finance", - "name": "BKING Finance", - "symbol": "BKF", - "rank": 6005, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "robodoge-robodoge-coin", - "name": "RoboDoge Coin", - "symbol": "ROBODOGE", - "rank": 6005, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uno-unobtanium", - "name": "Unobtanium", - "symbol": "UNO", - "rank": 6007, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "edux-edufex", - "name": "Edufex", - "symbol": "EDUX", - "rank": 6008, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bodo-boozedoge", - "name": "BoozeDoge", - "symbol": "BODO", - "rank": 6009, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "isa-isacoin", - "name": "ISACoin", - "symbol": "ISA", - "rank": 6010, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jefe-jefe-token", - "name": "Jefe TOKEN", - "symbol": "JEFE", - "rank": 6011, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flrs-flourish-coin", - "name": "Flourish Coin", - "symbol": "FLRS", - "rank": 6011, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mel-caramel-swap", - "name": "Caramel Swap", - "symbol": "MEL", - "rank": 6013, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "actn-action-coin", - "name": "Action Coin", - "symbol": "ACTN", - "rank": 6014, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "huskyx-huskyx", - "name": "HuskyX", - "symbol": "HUSKYX", - "rank": 6015, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vxrt-vaxart", - "name": "Vaxart", - "symbol": "VXRT", - "rank": 6015, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brig-brig-finance", - "name": "Brig Finance", - "symbol": "BRIG", - "rank": 6016, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "windy-windswap", - "name": "WindSwap", - "symbol": "WINDY", - "rank": 6018, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "acdc-volt", - "name": "Volt", - "symbol": "ACDC", - "rank": 6019, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kie-kier-group", - "name": "Kier Group", - "symbol": "KIE", - "rank": 6020, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bte-btecoin", - "name": "BTEcoin", - "symbol": "BTE", - "rank": 6021, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jwl-jewel", - "name": "Jewel", - "symbol": "JWL", - "rank": 6022, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "uffyi-unlimited-fiscusfyi", - "name": "unlimited FiscusFYI", - "symbol": "uFFYI", - "rank": 6023, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lsv-litecoin-sv", - "name": "Litecoin SV", - "symbol": "LSV", - "rank": 6024, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fret-future-real-estate-token", - "name": "Future Real Estate Token", - "symbol": "FRET", - "rank": 6024, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thropic-thropic", - "name": "Thropic", - "symbol": "THROPIC", - "rank": 6025, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rps-rps-league", - "name": "Rps League", - "symbol": "RPS", - "rank": 6026, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kot-kotia", - "name": "Kotia", - "symbol": "KOT", - "rank": 6028, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "blow-blowup", - "name": "BlowUP", - "symbol": "$BLOW", - "rank": 6028, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sprx-sprint", - "name": "Sprint", - "symbol": "SPRX", - "rank": 6030, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "neko-neko-network", - "name": "Neko Network", - "symbol": "NEKO", - "rank": 6031, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ncov-coronacoin", - "name": "Coronacoin", - "symbol": "nCoV", - "rank": 6032, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "diamonds-black-diamond", - "name": "Black Diamond", - "symbol": "DIAMONDS", - "rank": 6033, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "curve-curvehash-coin", - "name": "Curvehash Coin", - "symbol": "CURVE", - "rank": 6034, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mexc-mexc-token", - "name": "MEXC Token", - "symbol": "MEXC", - "rank": 6035, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gtf-globaltrustfund-token", - "name": "GLOBAL TRUSTFUND TOKEN", - "symbol": "GTF", - "rank": 6036, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitbucks-bitbucks", - "name": "BitBucks", - "symbol": "BITBUCKS", - "rank": 6037, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icag-international-consolidated-airlines-group-sa", - "name": "International Consolidated Airlines Group S.A.", - "symbol": "ICAG", - "rank": 6038, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dao1-dao1", - "name": "DAO1", - "symbol": "DAO1", - "rank": 6039, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibad-shiba-dragon", - "name": "Shiba Dragon", - "symbol": "SHIBAD", - "rank": 6039, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mntis-mantis-network", - "name": "Mantis Network", - "symbol": "MNTIS", - "rank": 6041, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bcc-basis-coin-cash", - "name": "Basis Coin Cash", - "symbol": "BCC", - "rank": 6042, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lkr-lkrcoin", - "name": "LKRcoin", - "symbol": "LKR", - "rank": 6043, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "roll-polyroll", - "name": "Polyroll", - "symbol": "ROLL", - "rank": 6044, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "six-sixpence", - "name": "Sixpence", - "symbol": "SIX", - "rank": 6044, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nami-nami-inu", - "name": "Nami Inu", - "symbol": "NAMI", - "rank": 6046, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "queen-queen-elizabeth-coin", - "name": "Queen Elizabeth Coin", - "symbol": "QUEEN", - "rank": 6046, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gtc-gtib", - "name": "GTIB", - "symbol": "GTC", - "rank": 6048, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dhold-diamondhold", - "name": "DiamondHold", - "symbol": "DHOLD", - "rank": 6049, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mu-mu-continent", - "name": "Mu Continent", - "symbol": "MU", - "rank": 6050, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bab-banana-bucks", - "name": "Banana Bucks", - "symbol": "BAB", - "rank": 6050, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lof-lonelyfans", - "name": "LonelyFans", - "symbol": "LOF", - "rank": 6052, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pgnt-pigeon-sol", - "name": "Pigeon Sol", - "symbol": "PGNT", - "rank": 6052, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibu-shibu-life", - "name": "Shibu Life", - "symbol": "SHIBU", - "rank": 6053, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ren-rinnegan", - "name": "Rinnegan", - "symbol": "REN", - "rank": 6055, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coins-coinswap", - "name": "CoinSwap", - "symbol": "COINS", - "rank": 6056, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babydb-baby-doge-billionaire", - "name": "Baby Doge Billionaire", - "symbol": "BABYDB", - "rank": 6056, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bfic-bficoin", - "name": "Bficoin", - "symbol": "BFIC", - "rank": 6058, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "magiccake-magic-cake", - "name": "Magic Cake", - "symbol": "MAGICCAKE", - "rank": 6059, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1055-china-southern-airlines", - "name": "China Southern Airlines", - "symbol": "1055", - "rank": 6060, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aiv-aimco", - "name": "AIMCO", - "symbol": "AIV", - "rank": 6061, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pool-poolcoin", - "name": "POOLCOIN", - "symbol": "POOL", - "rank": 6062, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bugg-bugg-finance", - "name": "BUGG Finance", - "symbol": "BUGG", - "rank": 6063, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mochilacoin-mochila-coin", - "name": "Mochila Coin", - "symbol": "MOCHILACOI", - "rank": 6064, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fruit-fruit", - "name": "Fruit", - "symbol": "FRUIT", - "rank": 6064, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tkm-tokemon", - "name": "TOKEMON", - "symbol": "TKM", - "rank": 6066, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lady-ladybug-finance", - "name": "LADYBUG FINANCE", - "symbol": "LADYB", - "rank": 6067, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "neng-nengcoin", - "name": "Nengcoin", - "symbol": "NENG", - "rank": 6068, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wxtz-wrapped-tezos", - "name": "Wrapped Tezos", - "symbol": "WXTZ", - "rank": 6069, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddeth-daddy-ethereum", - "name": "Daddy Ethereum", - "symbol": "DDETH", - "rank": 6070, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hfr-hfrcoin", - "name": "HFRCOIN", - "symbol": "HFR", - "rank": 6070, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "msa-my-shiba-academia", - "name": "My Shiba Academia", - "symbol": "MSA", - "rank": 6072, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "silo-seed2need", - "name": "Seed2Need", - "symbol": "SILO", - "rank": 6073, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gldx-goldex-token", - "name": "Goldex Token", - "symbol": "GLDX", - "rank": 6074, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "polr-polystarter", - "name": "PolyStarter", - "symbol": "POLR", - "rank": 6075, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cpoo-cockapoo", - "name": "Cockapoo", - "symbol": "CPOO", - "rank": 6076, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "404-404-coin", - "name": "404 coin", - "symbol": "404", - "rank": 6077, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mdl-mdl", - "name": "MDL Talent Hub", - "symbol": "MDL", - "rank": 6078, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "keyt-rebit", - "name": "Rebit", - "symbol": "KEYT", - "rank": 6079, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grimex-spacegrime", - "name": "SpaceGrime", - "symbol": "GRIMEX", - "rank": 6080, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "santa-santa-coin", - "name": "Santa Coin", - "symbol": "SANTA", - "rank": 6080, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hpay-hyper-credit-network", - "name": "Hyper Credit Network", - "symbol": "HPAY", - "rank": 6082, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yolo-advisorshares-pure-cannabis-etf", - "name": "AdvisorShares Pure Cannabis ETF", - "symbol": "YOLO", - "rank": 6083, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frf-frnce-rev-finance", - "name": "FRNCE REV FINANCE", - "symbol": "FRF", - "rank": 6083, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hinv-housersinvestments", - "name": "Housersinvestments", - "symbol": "HINV", - "rank": 6084, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bix-bitmax-token", - "name": "Bitmax Token", - "symbol": "BIX", - "rank": 6086, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "safemusk-safemusk", - "name": "SafeMusk", - "symbol": "SAFEMUSK", - "rank": 6086, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "madr-mad-rabbit", - "name": "Mad Rabbit", - "symbol": "MADR", - "rank": 6088, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "black-eosblack", - "name": "eosBLACK", - "symbol": "BLACK", - "rank": 6088, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "iup-infinitup", - "name": "InfinitUp", - "symbol": "IUP", - "rank": 6090, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ilayer-ilayer", - "name": "iLayer", - "symbol": "ILAYER", - "rank": 6091, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gkcake-golden-kitty-cake", - "name": "Golden Kitty Cake", - "symbol": "GKCAKE", - "rank": 6092, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ttf-turbotrix-finance", - "name": "TurboTrix Finance", - "symbol": "TTF", - "rank": 6093, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btym-blocktyme", - "name": "Blocktyme", - "symbol": "BTYM", - "rank": 6094, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "barmy-babyarmy", - "name": "BabyArmy", - "symbol": "BARMY", - "rank": 6095, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ppunks-pumpkin-punks", - "name": "Pumpkin Punks", - "symbol": "PPUNKS", - "rank": 6095, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "minibnb-minibnb", - "name": "MiniBNB", - "symbol": "MINIBNB", - "rank": 6097, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogex-dogehouse-capital", - "name": "DogeHouse Capital", - "symbol": "DOGEX", - "rank": 6098, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mind-mindchain", - "name": "Mindchain", - "symbol": "MIND", - "rank": 6098, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdog-bulldog", - "name": "Bulldog", - "symbol": "BDOG", - "rank": 6100, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pwdx-powerdexcoin", - "name": "Powerdexcoin", - "symbol": "PWDX", - "rank": 6101, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tzt-tanzanite", - "name": "Tanzanite", - "symbol": "TZT", - "rank": 6102, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bnxx-bitcoinnexx", - "name": "Bitcoinnexx", - "symbol": "BNXX", - "rank": 6103, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "edf-edf", - "name": "Edf", - "symbol": "EDF", - "rank": 6104, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bull-bullrise", - "name": "BullRise", - "symbol": "BULL", - "rank": 6105, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tva-tovpay", - "name": "Tovpay", - "symbol": "TVA", - "rank": 6106, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zum-zumcoin-", - "name": "ZumCoin", - "symbol": "ZUM", - "rank": 6107, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ssb-satoshistreetbets", - "name": "SatoshiStreetBets", - "symbol": "SSB", - "rank": 6108, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vault-m7-vault", - "name": "M7 Vault", - "symbol": "VAULT", - "rank": 6109, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfet-yfet", - "name": "YFET", - "symbol": "YFET", - "rank": 6110, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "psy-psychic", - "name": "Psychic", - "symbol": "PSY", - "rank": 6111, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "war-warrior-token", - "name": "Warrior Token", - "symbol": "WAR", - "rank": 6112, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fsxu-flashx-ultra", - "name": "FlashX Ultra", - "symbol": "FSXU", - "rank": 6113, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbgo-bingo-share", - "name": "Bingo Share", - "symbol": "SBGO", - "rank": 6114, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xde2-xde-ii", - "name": "XDE II", - "symbol": "XDE2", - "rank": 6115, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ecop-eco-defi", - "name": "Eco DeFi", - "symbol": "ECOP", - "rank": 6116, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "skrp-skraps", - "name": "Skraps", - "symbol": "SKRP", - "rank": 6117, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cme-cashme", - "name": "Cashme", - "symbol": "CME", - "rank": 6118, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pbr-petroleo-brasileiro-sa", - "name": "Petroleo Brasileiro SA", - "symbol": "PBR", - "rank": 6119, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jojo-jojo-inu", - "name": "JoJo Inu", - "symbol": "JOJO", - "rank": 6120, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcf-moon-chain", - "name": "Moon Chain", - "symbol": "MCF", - "rank": 6121, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blk-belka", - "name": "Belka", - "symbol": "BLK", - "rank": 6122, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "charles-charles", - "name": "Charles", - "symbol": "CHARLES", - "rank": 6123, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wolverinu-wolverinu", - "name": "WOLVERINU", - "symbol": "WOLVERINU", - "rank": 6124, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nst-newsolution", - "name": "Newsolution", - "symbol": "NST", - "rank": 6125, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "omega-omega", - "name": "OMEGA", - "symbol": "OMEGA", - "rank": 6126, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sxc-sexcoin", - "name": "Sexcoin", - "symbol": "SXC", - "rank": 6127, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "shpp-shipitpro", - "name": "ShipItPro", - "symbol": "SHPP", - "rank": 6128, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pit-pitbull", - "name": "Pitbull", - "symbol": "PIT", - "rank": 6129, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tnm-travel-n-miles", - "name": "Travel N Miles", - "symbol": "TNM", - "rank": 6130, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ttb-tetherblack", - "name": "TetherBlack", - "symbol": "TTB", - "rank": 6131, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sloki-super-floki", - "name": "Super Floki", - "symbol": "SLOKI", - "rank": 6132, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dmgk-darkmagick", - "name": "DarkMAGICK", - "symbol": "DMGK", - "rank": 6133, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mb-minebee", - "name": "MineBee", - "symbol": "MB", - "rank": 6134, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "epc-epcoin", - "name": "Epcoin", - "symbol": "EPC", - "rank": 6135, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ich-ideachain", - "name": "IdeaChain", - "symbol": "ICH", - "rank": 6136, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gns-gains-network", - "name": "Gains Network", - "symbol": "GNS", - "rank": 6137, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atmc-atomic-token", - "name": "Atomic Token", - "symbol": "ATMC", - "rank": 6138, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sw-sabac-warrior", - "name": "Sabac Warrior", - "symbol": "SW", - "rank": 6139, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myra-myra-ai", - "name": "Myra AI", - "symbol": "MYRA", - "rank": 6140, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "long-long-coin", - "name": "LONG COIN", - "symbol": "LONG", - "rank": 6141, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "nftart-nft-art-finance", - "name": "NFT Art Finance", - "symbol": "NFTART", - "rank": 6142, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lrg-largo-coin", - "name": "Largo Coin", - "symbol": "LRG", - "rank": 6143, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sv7-7plus-coin", - "name": "7Plus Coin", - "symbol": "SV7", - "rank": 6144, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sqg-squid-galaxy", - "name": "Squid Galaxy", - "symbol": "SQG", - "rank": 6145, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "1810-xiaomi-corp", - "name": "Xiaomi corp.", - "symbol": "1810", - "rank": 6146, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mdtk-mdtoken", - "name": "MDTOKEN", - "symbol": "MDTK", - "rank": 6147, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nftl-nftl-token", - "name": "NFTL Token", - "symbol": "NFTL", - "rank": 6148, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cof-coffeecoin", - "name": "CoffeeCoin", - "symbol": "COF", - "rank": 6149, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "crown-midasdao", - "name": "MidasDAO", - "symbol": "CROWN", - "rank": 6150, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sds-safedollar-shares", - "name": "SafeDollar Shares", - "symbol": "SDS", - "rank": 6151, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kada-king-cardano", - "name": "King Cardano", - "symbol": "KADA", - "rank": 6152, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mhsp-melonheadsprotocol", - "name": "MelonHeadsProtocol", - "symbol": "MHSP", - "rank": 6153, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "eggc-eggchain", - "name": "EggChain", - "symbol": "EGGC", - "rank": 6154, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shaman-shaman-king-inu", - "name": "Shaman King Inu", - "symbol": "SHAMAN", - "rank": 6155, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgc-mgc-token", - "name": "MGC Token", - "symbol": "MGC", - "rank": 6156, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mpad-mousepad", - "name": "MousePad", - "symbol": "MPAD", - "rank": 6157, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yeti-yearn-ecosystem-token-index", - "name": "Yearn Ecosystem Token Index", - "symbol": "YETI", - "rank": 6158, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cva-crypto-village-accelerator", - "name": "Crypto Village Accelerator", - "symbol": "CVA", - "rank": 6159, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "husl-hustle-token", - "name": "Hustle Token", - "symbol": "HUSL", - "rank": 6160, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogepepsi-dogepepsi", - "name": "DogePepsi", - "symbol": "DOGEPEPSI", - "rank": 6161, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daddyeth-daddyeth", - "name": "DaddyETH", - "symbol": "DADDYETH", - "rank": 6162, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bump-babypumpkin-finance", - "name": "BabyPumpkin Finance", - "symbol": "BUMP", - "rank": 6163, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kings-king-coin", - "name": "King Coin", - "symbol": "KINGS", - "rank": 6164, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ca-crossy-animals", - "name": "Crossy Animals", - "symbol": "CA", - "rank": 6165, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pekc-peacockcoin", - "name": "Peacockcoin", - "symbol": "PEKC", - "rank": 6166, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtg-moontimer", - "name": "MoonTimer", - "symbol": "MTG", - "rank": 6167, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "teva-teva-pharma-ind-adr-rep-1", - "name": "Teva Pharma Ind Adr Rep 1", - "symbol": "TEVA", - "rank": 6168, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfos-yfosfinance", - "name": "YFOS.finance", - "symbol": "YFOS", - "rank": 6169, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lamboq-lambo-quest", - "name": "Lambo Quest", - "symbol": "LAMBOQ", - "rank": 6170, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cng-cng-casino", - "name": "CNG Casino", - "symbol": "CNG", - "rank": 6171, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hex-heliumx", - "name": "Heliumx", - "symbol": "HEX", - "rank": 6172, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zabu-zabu-token", - "name": "Zabu Token", - "symbol": "ZABU", - "rank": 6172, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hptf-heptafranc", - "name": "HEPTAFRANC", - "symbol": "HPTF", - "rank": 6174, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dms-documentchain", - "name": "Documentchain", - "symbol": "DMS", - "rank": 6175, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gkg-gieki-credits", - "name": "Gieki Credits", - "symbol": "GKG", - "rank": 6176, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spy-satopay-yield-token", - "name": "Satopay Yield Token", - "symbol": "SPY", - "rank": 6177, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babydinger-baby-schrodinger-coin", - "name": "Baby Schrodinger Coin", - "symbol": "BABYDINGER", - "rank": 6177, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "steel-steel", - "name": "Steel", - "symbol": "STEEL", - "rank": 6179, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bubba-bubba", - "name": "Bubba", - "symbol": "BUBBA", - "rank": 6180, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sport-sportscoin", - "name": "SportsCoin", - "symbol": "SPORT", - "rank": 6181, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wildf-wildfire-token", - "name": "Wildfire Token", - "symbol": "WILDF", - "rank": 6182, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metabean-metabean", - "name": "MetaBean", - "symbol": "METABEAN", - "rank": 6182, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "astra-astrapad", - "name": "AstraPad", - "symbol": "ASTRA", - "rank": 6184, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "foxy-foxy-equilibrium", - "name": "Foxy Equilibrium", - "symbol": "FOXY", - "rank": 6184, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dyn-dynasty-global-investments-ag", - "name": "Dynasty Global Investments AG", - "symbol": "DYN", - "rank": 6186, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shon-shontoken", - "name": "ShonToken", - "symbol": "SHON", - "rank": 6186, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tcake-pancaketools", - "name": "PANCAKETOOLS", - "symbol": "TCAKE", - "rank": 6188, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lunar-lunarswap", - "name": "LunarSwap", - "symbol": "LUNAR", - "rank": 6189, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zoc-01coin", - "name": "01coin", - "symbol": "ZOC", - "rank": 6190, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lumi-lumi-credits", - "name": "LUMI Credits", - "symbol": "LUMI", - "rank": 6191, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ezpay-eazypayza", - "name": "EazyPayZa", - "symbol": "EZPAY", - "rank": 6192, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wifedoge-wifedoge", - "name": "Wifedoge", - "symbol": "WIFEDOGE", - "rank": 6193, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctsc-crypto-trading-solutions-coin", - "name": "Crypto Trading Solutions Coin", - "symbol": "CTSC", - "rank": 6194, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ida-idamurni", - "name": "IdaMurni", - "symbol": "IDA", - "rank": 6195, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "omc-omicron", - "name": "Omicron", - "symbol": "OMC", - "rank": 6195, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "egt-evergreen-token", - "name": "Evergreen Token", - "symbol": "EGT", - "rank": 6197, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "c24-cryptex", - "name": "Cryptex", - "symbol": "C24", - "rank": 6197, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "slt-solit", - "name": "Solit", - "symbol": "SLT", - "rank": 6198, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "upps-uppsme", - "name": "UppsMe", - "symbol": "UPPS", - "rank": 6200, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jkcoin-jacek-kolodziejczak-token", - "name": "Jacek Kołodziejczak Token", - "symbol": "JKCOIN", - "rank": 6201, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emt-emanate", - "name": "Emanate", - "symbol": "EMT", - "rank": 6202, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "x-token-x-token", - "name": "X-Token", - "symbol": "X-TOKEN", - "rank": 6203, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "auop-opalcoin", - "name": "Opalcoin", - "symbol": "AUOP", - "rank": 6204, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "asx-astrax", - "name": "Astrax", - "symbol": "ASX", - "rank": 6204, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swirl-swirl-cash", - "name": "Swirl Cash", - "symbol": "SWIRL", - "rank": 6206, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mbt-magic-birds-token", - "name": "Magic Birds Token", - "symbol": "MBT", - "rank": 6207, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sact-srnartgallery", - "name": "srnArtGallery", - "symbol": "SACT", - "rank": 6208, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "siam-siamese-neko", - "name": "Siamese Neko", - "symbol": "SIAM", - "rank": 6209, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alabay-alabay", - "name": "Alabay", - "symbol": "ALABAY", - "rank": 6210, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1208-mmg", - "name": "MMG", - "symbol": "1208", - "rank": 6210, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dub-dub-token", - "name": "DUB Token", - "symbol": "DUB", - "rank": 6212, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "long-long", - "name": "Long", - "symbol": "LONG", - "rank": 6213, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fsdcoin-fsd-coin", - "name": "FSD Coin", - "symbol": "FSDCOIN", - "rank": 6214, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "can-canyacoin", - "name": "CanYaCoin", - "symbol": "CAN", - "rank": 6214, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tlw-tullow", - "name": "Tullow", - "symbol": "TLW", - "rank": 6216, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lajka-lajka", - "name": "Lajka", - "symbol": "LAJKA", - "rank": 6217, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtr-moonstarevenge-token", - "name": "MoonstaRevenge Token", - "symbol": "MTR", - "rank": 6218, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "use-usechain-token", - "name": "Usechain Token", - "symbol": "USE", - "rank": 6219, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "txc-tenxcoin", - "name": "TenXCoin", - "symbol": "TXC", - "rank": 6220, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "senshi-senshi-inu", - "name": "SENSHI INU", - "symbol": "SENSHI", - "rank": 6221, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gtc-gastrocoin", - "name": "Gastrocoin", - "symbol": "GTC", - "rank": 6222, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bsfe-bigswap-finance-token", - "name": "BigSwap Finance Token", - "symbol": "BSFE", - "rank": 6222, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "catf-catfish-coin", - "name": "Catfish coin", - "symbol": "CATF", - "rank": 6223, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "npo-npo-coin", - "name": "NPO Coin", - "symbol": "NPO", - "rank": 6225, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "war-robot-wars", - "name": "Robot Wars", - "symbol": "WAR", - "rank": 6226, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "methmoon-eth-variable-leverage-long", - "name": "ETH Variable Leverage Long", - "symbol": "METHMOON", - "rank": 6227, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xlq-alqo", - "name": "ALQO", - "symbol": "XLQ", - "rank": 6227, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "alg-bitalgo", - "name": "Bitalgo", - "symbol": "ALG", - "rank": 6229, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vinu-viral-inu", - "name": "Viral Inu", - "symbol": "VINU", - "rank": 6230, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gummie-gummy-beans", - "name": "Gummy Beans", - "symbol": "GUMMIE", - "rank": 6230, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "glf-glufco", - "name": "GLUFCO", - "symbol": "GLF", - "rank": 6232, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "beos-beos", - "name": "Beos", - "symbol": "BEOS", - "rank": 6233, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tarinu-tariku-inu", - "name": "Tariku Inu", - "symbol": "TARINU", - "rank": 6234, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gax-getart-nft", - "name": "GETART NFT", - "symbol": "GAX", - "rank": 6234, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lqd-liquidity-network", - "name": "Liquidity Network", - "symbol": "LQD", - "rank": 6236, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dcre-deltacredits", - "name": "DeltaCredits", - "symbol": "DCRE", - "rank": 6237, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "0175-geely-automobile", - "name": "Geely Automobile", - "symbol": "0175", - "rank": 6238, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orc-oracle-system", - "name": "Oracle System", - "symbol": "ORC", - "rank": 6239, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "firu-firulais", - "name": "Firulais", - "symbol": "FIRU", - "rank": 6240, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "metawave-metawave", - "name": "MetaWave", - "symbol": "METAWAVE", - "rank": 6241, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "piggy-piggy-bank-token", - "name": "Piggy Bank Token", - "symbol": "PIGGY", - "rank": 6242, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "2883-china-oilfield-services", - "name": "China Oilfield Services", - "symbol": "2883", - "rank": 6243, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "surf-surffinance", - "name": "Surf.Finance", - "symbol": "SURF", - "rank": 6244, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ksol-kittycoin-sol", - "name": "Kittycoin SOL", - "symbol": "KSOL", - "rank": 6245, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ainu-ainu-token", - "name": "Ainu Token", - "symbol": "AINU", - "rank": 6245, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elena-elena-protocol", - "name": "Elena Protocol", - "symbol": "ELENA", - "rank": 6247, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bullaf-bullish-af", - "name": "Bullish AF", - "symbol": "BULLAF", - "rank": 6248, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reddoge-reddoge", - "name": "RedDoge", - "symbol": "REDDOGE", - "rank": 6249, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ffyi-fiscus-governance-coin", - "name": "Fiscus Governance Coin", - "symbol": "FFYI", - "rank": 6250, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "flash-flash", - "name": "Flash", - "symbol": "FLASH", - "rank": 6251, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "moneta-moneta", - "name": "Moneta", - "symbol": "MONETA", - "rank": 6252, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xtnc-xtendcash", - "name": "XtendCash", - "symbol": "XTNC", - "rank": 6253, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "phenix-phenix", - "name": "PHENIX", - "symbol": "PHENIX", - "rank": 6254, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "r2r-citios", - "name": "CitiOS", - "symbol": "R2R", - "rank": 6255, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smsct-smscodes", - "name": "SMSCodes", - "symbol": "SMSCT", - "rank": 6256, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tcr-thecreed", - "name": "TheCreed", - "symbol": "TCR", - "rank": 6257, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fdm-freedom", - "name": "Freedom", - "symbol": "FDM", - "rank": 6258, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dashs-dashs", - "name": "Dashs", - "symbol": "DASHS", - "rank": 6259, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "astrogold-astro-gold", - "name": "Astro Gold", - "symbol": "ASTROGOLD", - "rank": 6260, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "batdoge-the-batdoge", - "name": "The Batdoge", - "symbol": "BATDOGE", - "rank": 6260, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnome-gnometoken", - "name": "GnomeToken", - "symbol": "GNOME", - "rank": 6262, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gm-gm-holding", - "name": "GM Holding", - "symbol": "GM", - "rank": 6262, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "milli-millionsy", - "name": "MILLIONSY", - "symbol": "MILLI", - "rank": 6264, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "well-wellness-token-economy", - "name": "Wellness Token Economy", - "symbol": "WELL", - "rank": 6264, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "click-click", - "name": "Click", - "symbol": "CLICK", - "rank": 6266, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grew-green-world", - "name": "Green World", - "symbol": "GREW", - "rank": 6267, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gma-gma", - "name": "GMA", - "symbol": "GMA", - "rank": 6267, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wlfgrl-wolfgirl", - "name": "Wolfgirl", - "symbol": "WLFGRL", - "rank": 6268, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "moon-wanamoon", - "name": "WANAMOON", - "symbol": "MOON", - "rank": 6269, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "b2b-b2bcoin", - "name": "B2Bcoin", - "symbol": "B2B", - "rank": 6271, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cdii-chisai-doge-ii", - "name": "Chisai Doge II", - "symbol": "CDII", - "rank": 6272, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "royalada-royalada", - "name": "RoyalADA", - "symbol": "ROYALADA", - "rank": 6273, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "expo-expo-token", - "name": "Expo Token", - "symbol": "EXPO", - "rank": 6273, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yokai-yokai-money", - "name": "Yokai Money", - "symbol": "YOKAI", - "rank": 6275, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iten-iten", - "name": "ITEN", - "symbol": "ITEN", - "rank": 6275, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sack-moon-sack", - "name": "Moon Sack", - "symbol": "SACK", - "rank": 6276, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "runes-runebase", - "name": "Runebase", - "symbol": "RUNES", - "rank": 6278, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "q8e20-q8e20-token", - "name": "Q8E20 Token", - "symbol": "Q8E20", - "rank": 6279, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fpet-flokipetworld", - "name": "Flokipetworld", - "symbol": "FPET", - "rank": 6280, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hcc-human-charity-coin", - "name": "Human Charity Coin", - "symbol": "HCC", - "rank": 6280, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tteb-tietoevry-corporation", - "name": "TietoEVRY Corporation", - "symbol": "TTEB", - "rank": 6281, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "put-profile-utility-token", - "name": "Profile Utility Token", - "symbol": "PUT", - "rank": 6283, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "plat-platinum", - "name": "Platinum", - "symbol": "PLAT", - "rank": 6284, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "shibosu-shibosu", - "name": "Shibosu", - "symbol": "SHIBOSU", - "rank": 6284, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wxbtc-wrapped-xbtc", - "name": "Wrapped xBTC", - "symbol": "WXBTC", - "rank": 6286, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btsc-beyond-the-scene-coin", - "name": "Beyond The Scene Coin", - "symbol": "BTSC", - "rank": 6287, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dio-deimos-token", - "name": "Deimos Token", - "symbol": "DIO", - "rank": 6288, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gnto-goldenugget", - "name": "GoldeNugget", - "symbol": "GNTO", - "rank": 6289, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ponzi-ponzicoin", - "name": "PonziCoin", - "symbol": "PONZI", - "rank": 6290, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "petal-bitflowers", - "name": "bitFlowers", - "symbol": "PETAL", - "rank": 6291, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "whirl-whirl-finance", - "name": "Whirl Finance", - "symbol": "WHIRL", - "rank": 6292, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "icash-icash", - "name": "iCash", - "symbol": "ICASH", - "rank": 6293, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xspc-spectresecuritycoin", - "name": "Spectre Security Coin", - "symbol": "XSPC", - "rank": 6294, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ecc-ecc", - "name": "ECC", - "symbol": "ECC", - "rank": 6295, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mgchi-metagochi", - "name": "Metagochi", - "symbol": "MGCHI", - "rank": 6295, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tigerbaby-tiger-baby", - "name": "Tiger Baby", - "symbol": "TIGERBABY", - "rank": 6297, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "0857-petrochina", - "name": "PetroChina", - "symbol": "0857", - "rank": 6297, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sheng-sheng", - "name": "SHENG", - "symbol": "SHENG", - "rank": 6299, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ptr-payturn", - "name": "Payturn", - "symbol": "PTR", - "rank": 6300, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sov-sovereign-coin", - "name": "Sovereign coin", - "symbol": "SOV", - "rank": 6301, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mptc-mnpostree-coin", - "name": "MNPoSTree Coin", - "symbol": "MPTC", - "rank": 6302, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "home-home-coin", - "name": "Home Coin", - "symbol": "HOME", - "rank": 6303, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "units-array", - "name": "Array", - "symbol": "UNITS", - "rank": 6304, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aib-advanced-internet-blocks", - "name": "Advanced Internet Blocks", - "symbol": "AIB", - "rank": 6305, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ros-rostofy-coin", - "name": "Rostofy Coin", - "symbol": "ROS", - "rank": 6306, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pbtt-purple-butterfly-trading", - "name": "Purple Butterfly Trading", - "symbol": "PBTT", - "rank": 6307, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "totoro-totoro-inu", - "name": "Totoro Inu", - "symbol": "TOTORO", - "rank": 6307, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "redkishu-red-kishu", - "name": "Red Kishu", - "symbol": "REDKISHU", - "rank": 6308, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbm-cryptobrewmaster", - "name": "Crypto brew master", - "symbol": "CBM", - "rank": 6310, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gamma-green-planet", - "name": "Green Planet", - "symbol": "GAMMA", - "rank": 6310, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddc-duxdoge", - "name": "DuxDoge", - "symbol": "DDC", - "rank": 6312, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lmy-lunch-money", - "name": "Lunch Money", - "symbol": "LMY", - "rank": 6313, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sum-summeris", - "name": "Summeris", - "symbol": "SUM", - "rank": 6313, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "knd-kind", - "name": "Kind", - "symbol": "KND", - "rank": 6314, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pumpkin-pumpkin-inu", - "name": "Pumpkin Inu", - "symbol": "PUMPKIN", - "rank": 6316, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitcoin-harrypotterobamasonic10inu", - "name": "HarryPotterObamaSonic10Inu", - "symbol": "BITCOIN", - "rank": 6316, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kili-kilimanjaro", - "name": "Kilimanjaro", - "symbol": "KILI", - "rank": 6318, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clc-collider-coin", - "name": "Collider Coin", - "symbol": "CLC", - "rank": 6319, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wshib-wshiba", - "name": "wShiba", - "symbol": "wSHIB", - "rank": 6320, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ds-diamondshiba", - "name": "DiamondShiba", - "symbol": "DS$", - "rank": 6320, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbit-stackbit", - "name": "StackBIT", - "symbol": "SBIT", - "rank": 6322, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sti-stib", - "name": "StiB", - "symbol": "STI", - "rank": 6323, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyethv2-babyeth-v2", - "name": "BabyETH V2", - "symbol": "BABYETHV2", - "rank": 6324, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cru-curium", - "name": "Curium", - "symbol": "CRU", - "rank": 6325, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pfb-penny-for-bit", - "name": "Penny For Bit", - "symbol": "PFB", - "rank": 6326, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cbtc-classicbitcoin", - "name": "Classic Bitcoin", - "symbol": "CBTC", - "rank": 6327, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lilfloki-lil-floki", - "name": "Lil Floki", - "symbol": "LILFLOKI", - "rank": 6328, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mtf-milktea-finance", - "name": "Milktea Finance", - "symbol": "MTF", - "rank": 6329, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnix-bnix-token", - "name": "BNIX Token", - "symbol": "BNIX", - "rank": 6330, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dyor-dyor-token", - "name": "DYOR Token", - "symbol": "DYOR", - "rank": 6330, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "help-helpico", - "name": "Helpico", - "symbol": "HELP", - "rank": 6332, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ympa-ympa", - "name": "YMPA", - "symbol": "YMPA", - "rank": 6333, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ex-elitex-ex", - "name": "EliteX EX", - "symbol": "EX", - "rank": 6333, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gml-gameleaguecoin", - "name": "GameLeagueCoin", - "symbol": "GML", - "rank": 6335, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "emb-embercoin", - "name": "EmberCoin", - "symbol": "EMB", - "rank": 6336, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rby-rubycoin", - "name": "Rubycoin", - "symbol": "RBY", - "rank": 6337, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "minisoccer-minisoccer", - "name": "Minisoccer", - "symbol": "MINISOCCER", - "rank": 6338, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hg-hygenercoin", - "name": "Hygenercoin", - "symbol": "HG", - "rank": 6339, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "space-space-farm", - "name": "Space Farm", - "symbol": "SPACE", - "rank": 6340, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fes-feed-every-squid", - "name": "Feed Every Squid", - "symbol": "FES", - "rank": 6341, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iflt-inflationcoin", - "name": "InflationCoin", - "symbol": "IFLT", - "rank": 6341, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "heth-hodl-eth", - "name": "Hodl ETH", - "symbol": "HETH", - "rank": 6343, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rbbt-rabbitcoin", - "name": "RabbitCoin", - "symbol": "RBBT", - "rank": 6343, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "shild-shield-network", - "name": "Shield Network", - "symbol": "SHILD", - "rank": 6345, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sbtc-siambitcoin", - "name": "SiamBitcoin", - "symbol": "SBTC", - "rank": 6346, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pec-poverty-eradication-coin", - "name": "Poverty Eradication Coin", - "symbol": "PEC", - "rank": 6347, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogown-dog-owner", - "name": "Dog Owner", - "symbol": "DOGOWN", - "rank": 6348, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "reex-reecore", - "name": "Reecore", - "symbol": "REEX", - "rank": 6349, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "metauniverse-metauniverse", - "name": "MetaUniverse", - "symbol": "METAUNIVER", - "rank": 6350, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sheep-sheeptoken", - "name": "SheepToken", - "symbol": "SHEEP", - "rank": 6351, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wink-wink", - "name": "Wink", - "symbol": "WINK", - "rank": 6352, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cnfi-chainfi", - "name": "ChainFi", - "symbol": "CNFI", - "rank": 6353, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sak-sharkcoin", - "name": "Sharkcoin", - "symbol": "SAK", - "rank": 6354, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "skyr-skynet-research-coin-skyrcoin", - "name": "Skynet Research coin (Skyrcoin)", - "symbol": "SKYR", - "rank": 6355, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gold-gold", - "name": "GOLD", - "symbol": "GOLD", - "rank": 6355, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ocow-ocow", - "name": "OCOW", - "symbol": "OCOW", - "rank": 6357, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "otr-otter-finance", - "name": "Otter Finance", - "symbol": "OTR", - "rank": 6358, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kwd-kwd-coin", - "name": "KWD Coin", - "symbol": "KWD", - "rank": 6358, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lkc-linkedcoin", - "name": "LinkedCoin", - "symbol": "LKC", - "rank": 6360, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "pgirl-panda-girl", - "name": "Panda Girl", - "symbol": "PGIRL", - "rank": 6361, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tn-turtle-network", - "name": "Turtle Network", - "symbol": "TN", - "rank": 6361, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "kmx-kimex", - "name": "KIMEX", - "symbol": "KMX", - "rank": 6363, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "defy-defydefi", - "name": "DefyDefi", - "symbol": "DEFY", - "rank": 6364, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "llt-lifeline", - "name": "LifeLine", - "symbol": "LLT", - "rank": 6365, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bunnygirl-bunnygirl", - "name": "BunnyGirl", - "symbol": "BUNNYGIRL", - "rank": 6366, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dfx-defirex", - "name": "Defirex", - "symbol": "DFX", - "rank": 6366, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bog-bogged-finance", - "name": "Bogged Finance", - "symbol": "BOG", - "rank": 6368, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "esir-earn-save-invest-repeat", - "name": "Earn Save Invest Repeat", - "symbol": "ESIR", - "rank": 6369, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "micro-microdexwallet", - "name": "MicroDexWallet", - "symbol": "MICRO", - "rank": 6370, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phl-placeh", - "name": "placeh", - "symbol": "PHL", - "rank": 6370, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "beast-cryptobeast", - "name": "CryptoBeast", - "symbol": "BEAST", - "rank": 6372, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "b1p-b-one-payment", - "name": "B ONE PAYMENT", - "symbol": "B1P", - "rank": 6373, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dona-donaswap", - "name": "DONASWAP", - "symbol": "DONA", - "rank": 6374, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "arcg-arch-crypton-game", - "name": "Arch Crypton Game", - "symbol": "ARCG", - "rank": 6374, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wsbc-wallstreetbets-coin", - "name": "WallStreetBets Coin", - "symbol": "WSBC", - "rank": 6376, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "stellarinu-stellarinu", - "name": "StellarInu", - "symbol": "STELLARINU", - "rank": 6376, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ztc-zent-cash", - "name": "Zent Cash", - "symbol": "ZTC", - "rank": 6378, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "viking-viking-legend", - "name": "Viking Legend", - "symbol": "VIKING", - "rank": 6379, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kunu-kuramainu", - "name": "KuramaInu", - "symbol": "KUNU", - "rank": 6380, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ripr-rise2protocol", - "name": "RISE2PROTOCOL", - "symbol": "RIPR", - "rank": 6381, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fxtc-fixed-trade-coin", - "name": "Fixed Trade Coin", - "symbol": "FxTC", - "rank": 6381, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ww-waya-wolf-coin", - "name": "Waya Wolf Coin", - "symbol": "WW", - "rank": 6383, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rpst-rock-paper-scissors-token", - "name": "Rock Paper Scissors Token", - "symbol": "RPST", - "rank": 6384, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdi-babydogeinu", - "name": "Baby Doge Inu", - "symbol": "BDI", - "rank": 6385, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "naca-nashcash", - "name": "NashCash", - "symbol": "NACA", - "rank": 6386, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "veco-veco", - "name": "Veco", - "symbol": "VECO", - "rank": 6387, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "murphy-murphycat", - "name": "MURPHYCAT", - "symbol": "MURPHY", - "rank": 6388, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "claus-santa-claus", - "name": "Santa Claus", - "symbol": "CLAUS", - "rank": 6388, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qhc-qchi-chain", - "name": "QChi Chain", - "symbol": "QHC", - "rank": 6390, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ecl-ecole", - "name": "ECOLE", - "symbol": "ECL", - "rank": 6390, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tone-nft-tone", - "name": "NFT Tone", - "symbol": "TONE", - "rank": 6392, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bnbg-bnb-gold", - "name": "BNB Gold", - "symbol": "BNBG", - "rank": 6393, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xgame-xgamecoin", - "name": "Xgamecoin", - "symbol": "XGAME", - "rank": 6394, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "konbit-konbitcoin", - "name": "Konbitcoin", - "symbol": "KONBIT", - "rank": 6394, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bork-bork", - "name": "Bork", - "symbol": "BORK", - "rank": 6395, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tac-traceability-chain", - "name": "Traceability Chain", - "symbol": "TAC", - "rank": 6397, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hxp-hexagon-pay", - "name": "HEXAGON Pay", - "symbol": "HXP", - "rank": 6398, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ekta-ekta", - "name": "Ekta", - "symbol": "EKTA", - "rank": 6398, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lbd-littlebabydoge", - "name": "LittleBabyDoge", - "symbol": "LBD", - "rank": 6399, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "milf-milf-finance", - "name": "MILF Finance", - "symbol": "MILF", - "rank": 6401, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "remit-remita-coin", - "name": "Remita Coin", - "symbol": "REMIT", - "rank": 6402, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "quid-quid-token", - "name": "Quid Token", - "symbol": "QUID", - "rank": 6402, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "swamp-swamp-coin", - "name": "Swamp Coin", - "symbol": "SWAMP", - "rank": 6404, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "venus-venusia", - "name": "Venusia", - "symbol": "VENUS", - "rank": 6405, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mrc-meritcoins", - "name": "Meritcoins", - "symbol": "MRC", - "rank": 6406, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "phar-pharming-group", - "name": "Pharming Group", - "symbol": "PHAR", - "rank": 6407, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dinkydoge-dinky-doge-bsc", - "name": "Dinky Doge BSC", - "symbol": "DINKYDOGE", - "rank": 6408, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "myfriends-myfriends", - "name": "MyFriends", - "symbol": "MYFRIENDS", - "rank": 6409, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stackcake-stackcake", - "name": "StackCake", - "symbol": "STACKCAKE", - "rank": 6410, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "frwc-frankywillcoin", - "name": "FrankyWillCoin", - "symbol": "FRWC", - "rank": 6411, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ethmny-ethereum-money", - "name": "ETHEREUM MONEY", - "symbol": "ETHMNY", - "rank": 6412, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dsg-dinosaur-eggs-token", - "name": "Dinosaur Eggs Token", - "symbol": "DSG", - "rank": 6413, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "s8-super8", - "name": "Super8", - "symbol": "S8", - "rank": 6414, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "blinky-blinky-bob", - "name": "Blinky Bob", - "symbol": "BLINKY", - "rank": 6415, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wgames-wavesgames", - "name": "WavesGames", - "symbol": "WGames", - "rank": 6416, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xrd-raven-dark", - "name": "Raven Dark", - "symbol": "XRD", - "rank": 6417, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hcc-happy-creator-coin", - "name": "Happy Creator Coin", - "symbol": "HCC", - "rank": 6418, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vkc-vitakings-coin", - "name": "VitaKings Coin", - "symbol": "VKC", - "rank": 6419, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tknt-tkn-token", - "name": "TKN Token", - "symbol": "TKNT", - "rank": 6420, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scorgi-spacecorgi", - "name": "SpaceCorgi", - "symbol": "SCORGI", - "rank": 6421, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "primu-primulon", - "name": "Primulon", - "symbol": "PRIMU", - "rank": 6422, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "svt-spacevikings", - "name": "SpaceVikings", - "symbol": "SVT", - "rank": 6423, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trad-tradcoin", - "name": "Tradcoin", - "symbol": "TRAD", - "rank": 6424, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "betxc-betxoin", - "name": "Betxoin", - "symbol": "BETXC", - "rank": 6425, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dub-dubstep", - "name": "Dubstep", - "symbol": "DUB", - "rank": 6426, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fbc-fibercoin", - "name": "FiberCoin", - "symbol": "FBC", - "rank": 6427, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rod-republic-of-dogs", - "name": "Republic of Dogs", - "symbol": "ROD", - "rank": 6428, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cloud9-cloud9bscfinance", - "name": "Cloud9bsc.finance", - "symbol": "CLOUD9", - "rank": 6428, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "luci-lucifer-shiba-inu", - "name": "Lucifer Shiba Inu", - "symbol": "LUCI", - "rank": 6429, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aus-australia-cash", - "name": "Australia Cash", - "symbol": "AUS", - "rank": 6431, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hvi-hungarian-vizsla-inu", - "name": "Hungarian Vizsla Inu", - "symbol": "HVI", - "rank": 6432, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "miniusdc-miniusdc", - "name": "MiniUSDC", - "symbol": "MINIUSDC", - "rank": 6433, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wee-wee", - "name": "Wee", - "symbol": "WEE", - "rank": 6434, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "phuck-phucks", - "name": "PHUCKS", - "symbol": "PHUCK", - "rank": 6435, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "audax-audax", - "name": "Audax", - "symbol": "AUDAX", - "rank": 6436, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "swyftt-swyft-network", - "name": "Swyft Network", - "symbol": "SWYFTT", - "rank": 6437, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wcn-widecoin", - "name": "Widecoin", - "symbol": "WCN", - "rank": 6438, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "c2o-cryptowater", - "name": "CryptoWater", - "symbol": "C2O", - "rank": 6439, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "421-fourtwentyone", - "name": "FourTwentyOne", - "symbol": "421", - "rank": 6440, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "brain-bnbrain", - "name": "BnbRain", - "symbol": "BRAIN", - "rank": 6441, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fantomapes-fantom-of-the-opera-apes", - "name": "Fantom of the Opera Apes", - "symbol": "FANTOMAPES", - "rank": 6441, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sfx-soferox", - "name": "Soferox", - "symbol": "SFX", - "rank": 6443, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "ninja-ninjacoin", - "name": "Ninjacoin", - "symbol": "NINJA", - "rank": 6444, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mom-mom-token", - "name": "MOM Token", - "symbol": "MOM", - "rank": 6445, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pic-picacoin", - "name": "Picacoin", - "symbol": "PIC", - "rank": 6446, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "quokk-polyquokka-finance", - "name": "PolyQuokka Finance", - "symbol": "QUOKK", - "rank": 6447, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elo-inu-elo-inu", - "name": "Elo Inu", - "symbol": "ELO INU", - "rank": 6448, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fire-baby-bonfire", - "name": "Baby Bonfire", - "symbol": "FIRE", - "rank": 6448, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gil-gilgamesh-eth", - "name": "Gilgamesh ETH", - "symbol": "GIL", - "rank": 6449, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyohm-babyolympus", - "name": "BabyOlympus", - "symbol": "BABYOHM", - "rank": 6450, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btour-btour-chain", - "name": "BTour Chain", - "symbol": "BTOUR", - "rank": 6452, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibaninja-shibaninja", - "name": "ShibaNinja", - "symbol": "SHIBANINJA", - "rank": 6452, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rocks-rocks-idle-game", - "name": "Rocks Idle Game", - "symbol": "ROCKS", - "rank": 6454, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mint-mintcoin", - "name": "MintCoin", - "symbol": "MINT", - "rank": 6455, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "agvc-agavecoin", - "name": "AgaveCoin", - "symbol": "AGVC", - "rank": 6456, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dcip-decentralized-community-investment-protocol", - "name": "Decentralized Community Investment Protocol", - "symbol": "DCIP", - "rank": 6457, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "seed-seedtoken", - "name": "SeedToken", - "symbol": "SEED", - "rank": 6458, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btci-bitcoin-international", - "name": "BITCOIN INTERNATIONAL", - "symbol": "BTCI", - "rank": 6459, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bait-baitcoin", - "name": "Baitcoin", - "symbol": "BAIT", - "rank": 6460, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cld-cryptopia-land-dollar", - "name": "Cryptopia Land Dollar", - "symbol": "CLD", - "rank": 6461, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babyeth-babyeth", - "name": "BabyEth", - "symbol": "BABYETH", - "rank": 6462, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tree-tree", - "name": "TREE", - "symbol": "TREE", - "rank": 6463, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "esn-escudonavacense", - "name": "Escudo Navacense", - "symbol": "ESN", - "rank": 6464, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dego-derogold", - "name": "DeroGold", - "symbol": "DEGO", - "rank": 6465, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vbswap-vbswap", - "name": "vBSWAP", - "symbol": "VBSWAP", - "rank": 6465, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sca-scalaris", - "name": "Scalaris", - "symbol": "SCA", - "rank": 6467, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bsccrop-bsccrop", - "name": "BSCCrop", - "symbol": "BSCCROP", - "rank": 6468, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sr-skyrocket", - "name": "SkyRocket", - "symbol": "SR", - "rank": 6469, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "poc-pangea-cleanup-coin", - "name": "PANGEA Cleanup Coin", - "symbol": "POC", - "rank": 6470, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bbh-beavis-and-butthead", - "name": "Beavis and Butthead", - "symbol": "BBH", - "rank": 6471, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gdoge-golden-doge", - "name": "Golden Doge", - "symbol": "GDOGE", - "rank": 6472, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lider-lider-token", - "name": "Lider Token", - "symbol": "LIDER", - "rank": 6473, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mgp-mangochain", - "name": "MangoChain", - "symbol": "MGP", - "rank": 6474, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbit-netbit", - "name": "netBit", - "symbol": "NBIT", - "rank": 6475, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "asunainu-asuna-inu", - "name": "Asuna Inu", - "symbol": "ASUNAINU", - "rank": 6476, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aime-animeinu", - "name": "AnimeInu", - "symbol": "AIME", - "rank": 6476, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sng-synergy-land", - "name": "Synergy Land", - "symbol": "SNG", - "rank": 6477, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yolov-yoloverse", - "name": "YoloVerse", - "symbol": "YOLOV", - "rank": 6479, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rshib-robot-shib", - "name": "ROBOT SHIB", - "symbol": "RSHIB", - "rank": 6480, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "azt-az-fundchain", - "name": "AZ Fundchain", - "symbol": "AZT", - "rank": 6480, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "algb-algebra", - "name": "Algebra", - "symbol": "ALGB", - "rank": 6481, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drgb-dragonbit", - "name": "Dragonbit", - "symbol": "DRGB", - "rank": 6483, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "air-aircoin", - "name": "AirCoin", - "symbol": "AIR", - "rank": 6484, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mek-meraki", - "name": "Meraki", - "symbol": "MEK", - "rank": 6485, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "alien-alien-inu", - "name": "Alien Inu", - "symbol": "ALIEN", - "rank": 6485, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dt3-dreamteam3", - "name": "DreamTeam3", - "symbol": "DT3", - "rank": 6487, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "qrn-qureno", - "name": "Qureno", - "symbol": "QRN", - "rank": 6488, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "syc-synchrolife", - "name": "SynchroLife", - "symbol": "SYC", - "rank": 6489, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ore-optimusrise", - "name": "OptimusRise", - "symbol": "ORE", - "rank": 6489, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "n808-new808coin", - "name": "New808coin", - "symbol": "N808", - "rank": 6491, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "exor-exor", - "name": "Exor", - "symbol": "EXOR", - "rank": 6492, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "vgd-vangold-token", - "name": "Vangold Token", - "symbol": "VGD", - "rank": 6493, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dpc-dappcents", - "name": "DAPPCENTS", - "symbol": "DPC", - "rank": 6494, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cocktail-cocktail", - "name": "Cocktail", - "symbol": "COCKTAIL", - "rank": 6495, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vgc-5g-cash", - "name": "5G-CASH", - "symbol": "VGC", - "rank": 6496, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hyper-hyper", - "name": "Hyper", - "symbol": "HYPER", - "rank": 6497, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "squid-squidanomics", - "name": "Squidanomics", - "symbol": "SQUID", - "rank": 6498, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bdcash-bdcash-protocol", - "name": "BDCash Protocol", - "symbol": "BDCASH", - "rank": 6498, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "zcnox-zcnox-coin", - "name": "ZCNOX Coin", - "symbol": "ZCNOX", - "rank": 6500, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orbyt-orbyt-token", - "name": "ORBYT Token", - "symbol": "ORBYT", - "rank": 6501, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blas-blakestar-20", - "name": "BlakeStar 2.0", - "symbol": "BLAS", - "rank": 6502, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lepen-lepen", - "name": "LePen", - "symbol": "LEPEN", - "rank": 6503, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bgx-bitcoingenx", - "name": "BitcoinGenX", - "symbol": "BGX", - "rank": 6504, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cyfm-cyberfm", - "name": "CyberFM", - "symbol": "CYFM", - "rank": 6505, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfim-yfimobi", - "name": "Yfi.mobi", - "symbol": "YFIM", - "rank": 6506, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "redshiba-redshiba", - "name": "RedShiba", - "symbol": "REDSHIBA", - "rank": 6507, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "scribe-scribe", - "name": "Scribe", - "symbol": "SCRIBE", - "rank": 6508, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "post-ipse", - "name": "IPSE", - "symbol": "POST", - "rank": 6509, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mars-marstoken", - "name": "MARSTOKEN", - "symbol": "MARS", - "rank": 6510, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bep-blue-earth-protocol", - "name": "Blue Earth Protocol", - "symbol": "BEP", - "rank": 6510, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pod-payment-coin", - "name": "Payment Coin", - "symbol": "POD", - "rank": 6512, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "jmc-junson-ming-chan-coin", - "name": "Junson Ming Chan Coin", - "symbol": "JMC", - "rank": 6513, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "srsb-sirius-bond", - "name": "Sirius Bond", - "symbol": "SRSB", - "rank": 6514, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spc-spaceport-coin", - "name": "SpacePort Coin", - "symbol": "SPC", - "rank": 6515, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "atfi-atlantic-finance", - "name": "Atlantic Finance", - "symbol": "ATFI", - "rank": 6516, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "svc-silvercash", - "name": "Silvercash", - "symbol": "SVC", - "rank": 6517, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kreds-kreds", - "name": "Kreds", - "symbol": "KREDS", - "rank": 6518, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "radar-radarcoin", - "name": "Radarcoin", - "symbol": "RADAR", - "rank": 6519, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "snbl-snowball", - "name": "Snowball", - "symbol": "SNBL", - "rank": 6520, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tvt-tvt", - "name": "TVT", - "symbol": "TVT", - "rank": 6521, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "b2u-b2u-coin", - "name": "B2U Coin", - "symbol": "B2U", - "rank": 6522, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ulg-ultragate", - "name": "Ultragate", - "symbol": "ULG", - "rank": 6523, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sdgo-sandego", - "name": "SanDeGo", - "symbol": "SDGO", - "rank": 6524, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mnme-masternodesme", - "name": "Masternodesme", - "symbol": "MNME", - "rank": 6525, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "aces-aces", - "name": "Aces", - "symbol": "ACES", - "rank": 6526, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rubit-rublebit", - "name": "RubleBit", - "symbol": "RUBIT", - "rank": 6527, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rc20-robocalls", - "name": "RoboCalls", - "symbol": "RC20", - "rank": 6528, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "thc-weedcoin", - "name": "Weedcoin", - "symbol": "THC", - "rank": 6529, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ohc-ohwe", - "name": "OHWE", - "symbol": "OHC", - "rank": 6530, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "clog-clo-gold", - "name": "CLO GOLD", - "symbol": "CLOG", - "rank": 6531, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sec-sunrise-energy-token", - "name": "Sunrise Energy Token", - "symbol": "SEC", - "rank": 6532, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "roger-theholyrogercoin", - "name": "TheHolyRogerCoin", - "symbol": "ROGER", - "rank": 6533, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "peppa-peppa-network", - "name": "Peppa Network", - "symbol": "PEPPA", - "rank": 6534, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "opes-opescoin", - "name": "Opescoin", - "symbol": "OPES", - "rank": 6535, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "bcct-bcc-token", - "name": "BCC TOKEN", - "symbol": "BCCT", - "rank": 6535, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jbx-jbox", - "name": "JBOX", - "symbol": "JBX", - "rank": 6537, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "emoj-emoji", - "name": "Emoji", - "symbol": "EMOJ", - "rank": 6538, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xxp-xx-platform", - "name": "XX Platform", - "symbol": "XXP", - "rank": 6539, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sntr-silent-notary", - "name": "Silent Notary", - "symbol": "SNTR", - "rank": 6540, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pokermania-pokermania-token", - "name": "PokerMania Token", - "symbol": "POKERMANIA", - "rank": 6541, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sedo-sedo-pow-token", - "name": "SEDO POW TOKEN", - "symbol": "SEDO", - "rank": 6542, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "orange-orange-token", - "name": "Orange Token", - "symbol": "ORANGE", - "rank": 6543, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bitok-bitok", - "name": "Bitok", - "symbol": "BITOK", - "rank": 6544, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "npc-nole-npc", - "name": "Nole NPC", - "symbol": "NPC", - "rank": 6545, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "daft-daftcoin1660", - "name": "DaftCoin", - "symbol": "DAFT", - "rank": 6545, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ore-starminer-ore-token", - "name": "StarMiner ORE Token", - "symbol": "ORE", - "rank": 6547, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "usd1-psyche", - "name": "Psyche", - "symbol": "USD1", - "rank": 6548, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bfi-bitfarmings", - "name": "BitFarmings", - "symbol": "BFI", - "rank": 6549, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clct-collectcoin", - "name": "CollectCoin", - "symbol": "CLCT", - "rank": 6549, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonshib-the-moon-shiba", - "name": "The Moon Shiba", - "symbol": "MOONSHIB", - "rank": 6550, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fund-fundchains", - "name": "FUNDChains", - "symbol": "FUND", - "rank": 6552, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "babydefido-baby-defido", - "name": "Baby DeFido", - "symbol": "BABYDEFIDO", - "rank": 6553, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trusd-trustusd", - "name": "TrustUSD", - "symbol": "TRUSD", - "rank": 6554, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wac-warranty-chain", - "name": "Warranty Chain", - "symbol": "WAC", - "rank": 6555, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "emt-easymine-token", - "name": "easyMINE Token", - "symbol": "EMT", - "rank": 6556, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zarh-zarcash", - "name": "Zarcash", - "symbol": "ZARH", - "rank": 6557, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cinu-cheems-inu", - "name": "CHEEMS INU", - "symbol": "$CINU", - "rank": 6557, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ssf-safe-seafood-coin", - "name": "Safe SeaFood Coin", - "symbol": "SSF", - "rank": 6559, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "wchta-wrappedcheetahcoin", - "name": "WrappedCheetahcoin", - "symbol": "WCHTA", - "rank": 6560, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hodl-hodlearn", - "name": "Hodlearn", - "symbol": "HODL", - "rank": 6561, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hatch-hatch", - "name": "Hatch", - "symbol": "HATCH", - "rank": 6562, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "x0z-zerozed", - "name": "Zerozed", - "symbol": "x0z", - "rank": 6563, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "knolix-knolix", - "name": "KNOLIX", - "symbol": "KNOLIX", - "rank": 6564, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sykle-reesykle", - "name": "Reesykle", - "symbol": "SYKLE", - "rank": 6565, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bynd-beyondcoin", - "name": "Beyondcoin", - "symbol": "BYND", - "rank": 6565, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "sela-selacoin", - "name": "SelaCoin", - "symbol": "SELA", - "rank": 6567, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dsbowl-doge-superbowl", - "name": "Doge Superbowl", - "symbol": "DSBOWL", - "rank": 6568, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "prm-prismchain", - "name": "PrismChain", - "symbol": "PRM", - "rank": 6569, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "fck-fck-banks-coin", - "name": "FCK Banks coin", - "symbol": "FCK", - "rank": 6570, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "tft-tron-flight-ticket", - "name": "Tron Flight Ticket", - "symbol": "TFT", - "rank": 6571, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lvl-levelapp-token", - "name": "LevelApp Token", - "symbol": "LVL", - "rank": 6572, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "marsshiba-the-mars-shiba", - "name": "The Mars Shiba", - "symbol": "MARSSHIBA", - "rank": 6573, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ftg-ftgigs", - "name": "FTgigs", - "symbol": "FTG", - "rank": 6573, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lop-kilopi", - "name": "Kilopi", - "symbol": "LOP", - "rank": 6575, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "becoin-becoin", - "name": "Becoin", - "symbol": "BECOIN", - "rank": 6576, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unimoon-unimoon", - "name": "Unimoon", - "symbol": "UNIMOON", - "rank": 6576, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zmax-zillamatrix", - "name": "ZillaMatrix", - "symbol": "ZMAX", - "rank": 6577, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "i9c-i9-coin", - "name": "I9 Coin", - "symbol": "I9C", - "rank": 6579, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "elph-elphyrecoin", - "name": "Elphyrecoin", - "symbol": "ELPH", - "rank": 6580, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rhinos-rhinosgame", - "name": "rhinos.game", - "symbol": "RHINOS", - "rank": 6581, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shico-shibacorgi", - "name": "ShibaCorgi", - "symbol": "SHICO", - "rank": 6581, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tcp-token-cashpay", - "name": "Token CashPay", - "symbol": "TCP", - "rank": 6583, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "space-vortex-dao", - "name": "Vortex DAO", - "symbol": "SPACE", - "rank": 6584, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "own-own-token", - "name": "OWN Token", - "symbol": "OWN", - "rank": 6584, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ltrbt-little-rabbit", - "name": "Little Rabbit", - "symbol": "LTRBT", - "rank": 6586, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "titania-titania-token", - "name": "Titania Token", - "symbol": "TITANIA", - "rank": 6586, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hare-hare-token", - "name": "Hare Token", - "symbol": "HARE", - "rank": 6587, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rgk-ragnarokdao", - "name": "RagnarokDAO", - "symbol": "RGK", - "rank": 6588, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ecpn-ecpn-token", - "name": "ECPN Token", - "symbol": "ECPN", - "rank": 6590, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ice-ice-dao", - "name": "Ice Dao", - "symbol": "ICE", - "rank": 6590, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stray-animal-token", - "name": "Animal Token", - "symbol": "STRAY", - "rank": 6591, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "noel-noel-capital", - "name": "Noel Capital", - "symbol": "NOEL", - "rank": 6593, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "etnx-electronero", - "name": "Electronero", - "symbol": "ETNX", - "rank": 6594, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "hndc-hondaiscoin", - "name": "HondaisCoin", - "symbol": "HNDC", - "rank": 6595, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "cow-cowry", - "name": "COWRY", - "symbol": "COW", - "rank": 6596, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "rocky-rocky-inu", - "name": "Rocky Inu", - "symbol": "ROCKY", - "rank": 6597, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aca-acash-coin", - "name": "Acash Coin", - "symbol": "ACA", - "rank": 6597, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "candylad-candylad", - "name": "Candylad", - "symbol": "CANDYLAD", - "rank": 6598, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nutsg-nuts-gaming", - "name": "NUTS Gaming", - "symbol": "NUTSG", - "rank": 6600, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mclb-millenniumclubcoin", - "name": "MillenniumClub Coin", - "symbol": "MCLB", - "rank": 6601, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "mz-metazilla", - "name": "MetaZilla", - "symbol": "MZ", - "rank": 6601, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shibazilla-shibazilla", - "name": "ShibaZilla", - "symbol": "SHIBAZILLA", - "rank": 6602, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "csm-consortium", - "name": "Consortium", - "symbol": "CSM", - "rank": 6604, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "dobe-dobermann", - "name": "Dobermann", - "symbol": "DOBE", - "rank": 6605, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pcn-peepcoin", - "name": "PeepCoin", - "symbol": "PCN", - "rank": 6606, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "labv2-little-angry-bunny-v2", - "name": "Little Angry Bunny v2", - "symbol": "LABV2", - "rank": 6607, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btfc-bitcoin-flash-cash2794", - "name": "Bitcoin Flash Cash", - "symbol": "BTFC", - "rank": 6608, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ddoge-daughter-doge", - "name": "Daughter Doge", - "symbol": "DDOGE", - "rank": 6609, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mkcy-markaccy", - "name": "Markaccy", - "symbol": "MKCY", - "rank": 6610, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rsc-risecity", - "name": "RiseCity", - "symbol": "RSC", - "rank": 6610, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nuke-nuke-token", - "name": "Nuke Token", - "symbol": "NUKE", - "rank": 6611, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tnet-title-network", - "name": "Title Network", - "symbol": "TNET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "merl-merlin", - "name": "Merlin", - "symbol": "MERL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xgt-xion-global-token", - "name": "Xion Global Token", - "symbol": "XGT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atmn-antimony", - "name": "Antimony", - "symbol": "ATMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brtr-barter", - "name": "Barter", - "symbol": "BRTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ipfst-ipfst", - "name": "IPFST", - "symbol": "IPFST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bucks-swagbucks", - "name": "SwagBucks", - "symbol": "BUCKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vltm-voltium", - "name": "Voltium", - "symbol": "VLTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abp-arc-block-protocol", - "name": "Arc Block Protocol", - "symbol": "ABP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pmon-pocmon", - "name": "PocMon", - "symbol": "PMON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hebe-hebeblock", - "name": "Hebeblock", - "symbol": "HEBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "coin-stake-coin", - "name": "Stake Coin", - "symbol": "COIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bhiba-baby-shiba", - "name": "Baby Shiba", - "symbol": "BHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bevo-bevo-digital-art-token", - "name": "Bevo Digital Art Token", - "symbol": "BEVO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "time-timeminer", - "name": "TimeMiner", - "symbol": "TIME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ant-antcoin", - "name": "ANTcoin", - "symbol": "ANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fnk-funkeypay", - "name": "FunKeyPay", - "symbol": "FNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yf4-yearn4-finance", - "name": "Yearn4 Finance", - "symbol": "YF4", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buz-buzcoin", - "name": "Buzcoin", - "symbol": "BUZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sct-clash-token", - "name": "Clash Token", - "symbol": "SCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blcc-bullers-coin", - "name": "Bullers Coin", - "symbol": "BLCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "neex-neexstar", - "name": "Neexstar", - "symbol": "NEEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "haus-daohaus", - "name": "DAOhaus", - "symbol": "HAUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "upunk-unicly-cryptopunks-collection", - "name": "Unicly CryptoPunks Collection", - "symbol": "UPUNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "catnip-catnip-money", - "name": "Catnip Money", - "symbol": "CATNIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "font-font", - "name": "Font", - "symbol": "FONT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tro-tronetwork", - "name": "Tro.Network", - "symbol": "TRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sren-sren", - "name": "sREN", - "symbol": "SREN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scc-siaclassic", - "name": "SiaClassic", - "symbol": "SCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xamp-antiample", - "name": "Antiample", - "symbol": "XAMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plst-philosafe-token", - "name": "Philosafe Token", - "symbol": "PLST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "homi-homihelp", - "name": "HOMIHELP", - "symbol": "HOMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aplp-apple-finance", - "name": "Apple Finance", - "symbol": "APLP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coc-cocktailbarfinance", - "name": "cocktailbar.finance", - "symbol": "COC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "soda-soda-token", - "name": "Soda Token", - "symbol": "SODA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etr-electric-token", - "name": "Electric Token", - "symbol": "ETR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shark-sharkyield", - "name": "SharkYield", - "symbol": "SHARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gsmt-grafsound", - "name": "Grafsound", - "symbol": "GSMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ntvrk-netvrk", - "name": "Netvrk", - "symbol": "NTVRK", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "spok-spock", - "name": "Spock", - "symbol": "SPOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ytsla-ytsla-finance", - "name": "yTSLA Finance", - "symbol": "YTSLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nftb-nftb", - "name": "NFTb", - "symbol": "NFTB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cft-coinbene-future-token", - "name": "CoinBene Future Token", - "symbol": "CFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "latte-latteswap", - "name": "LatteSwap", - "symbol": "LATTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kko-kineko", - "name": "Kineko", - "symbol": "KKO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pria-pria", - "name": "PRIA", - "symbol": "PRIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kxusd-kxusd", - "name": "kxUSD", - "symbol": "KXUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "limit-limitswap", - "name": "LimitSwap", - "symbol": "LIMIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wck-wrapped-cryptokitties", - "name": "Wrapped CryptoKitties", - "symbol": "WCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcfx-tcbcoin", - "name": "Tcbcoin", - "symbol": "TCFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "newton-newtonium", - "name": "Newtonium", - "symbol": "NEWTON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "suni-suni", - "name": "SUNI", - "symbol": "SUNI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ggive-globalgive", - "name": "GlobalGive", - "symbol": "GGIVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "robot-robot", - "name": "Robot", - "symbol": "ROBOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stark-stark-chain", - "name": "Stark Chain", - "symbol": "STARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vex-velorex", - "name": "Velorex", - "symbol": "VEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ibbtc-interest-bearing-bitcoin", - "name": "Interest Bearing Bitcoin", - "symbol": "IBBTC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rice-riceswap", - "name": "RiceSwap", - "symbol": "RICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nt-nexdax", - "name": "NexDAX", - "symbol": "NT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "co2b-co2bit", - "name": "CO2Bit", - "symbol": "CO2B", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "koda-koda-finance", - "name": "Koda Finance", - "symbol": "KODA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plum-plumcake-finance", - "name": "PlumCake Finance", - "symbol": "PLUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dapp-dappercoin", - "name": "DapperCoin", - "symbol": "DAPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wlt-wealth-locks", - "name": "Wealth Locks", - "symbol": "WLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stacy-stacy", - "name": "Stacy", - "symbol": "STACY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrge-rougecoin", - "name": "RougeCoin", - "symbol": "XRGE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btc-piedao-btc", - "name": "PieDAO BTC++", - "symbol": "BTC++", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dscvr-dscvrfinance", - "name": "DSCVR.Finance", - "symbol": "DSCVR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mars-to-the-mars", - "name": "To The Mars", - "symbol": "MARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "egt-elasticdao", - "name": "ElasticDAO", - "symbol": "EGT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kek-cryptokek", - "name": "CryptoKek", - "symbol": "KEK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcf-bitcoin-final", - "name": "Bitcoin Final", - "symbol": "BTCF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vlm-valireum", - "name": "Valireum", - "symbol": "VLM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gya-gya", - "name": "GYA", - "symbol": "GYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stzen-stakedzen", - "name": "StakedZEN", - "symbol": "STZEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doki-doki-doki", - "name": "Doki Doki", - "symbol": "DOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "koduro-koduro", - "name": "Koduro", - "symbol": "KODURO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fnxs-financex-exchange-token", - "name": "FinanceX Exchange Token", - "symbol": "FNXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfib-yfi-business", - "name": "YFI Business", - "symbol": "YFIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "the-thenode", - "name": "THENODE", - "symbol": "THE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clr-clear-coin", - "name": "Clear Coin", - "symbol": "CLR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onigiri-onigiri", - "name": "Onigiri", - "symbol": "ONIGIRI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qi-qi-dao", - "name": "Qi Dao", - "symbol": "QI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defil-piedao-defi-large-cap", - "name": "PieDAO DEFI Large Cap", - "symbol": "DEFI+L", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nat-nature", - "name": "Nature", - "symbol": "NAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "premia-premia", - "name": "Premia", - "symbol": "PREMIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lms-lumos", - "name": "Lumos", - "symbol": "LMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "myfi-myfichain", - "name": "MyFiChain", - "symbol": "MYFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prd-predator-coin", - "name": "Predator Coin", - "symbol": "PRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "isal-isalcoin", - "name": "Isalcoin", - "symbol": "ISAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yficg-yfi-credits-group", - "name": "YFI Credits Group", - "symbol": "YFICG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iut-ito-utility-token", - "name": "ITO Utility Token", - "symbol": "IUT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rtc-read-this-contract", - "name": "Read This Contract", - "symbol": "RTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "seed-metagame", - "name": "MetaGame", - "symbol": "SEED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rsp-real-estate-sales-platform", - "name": "Real-estate Sales Platform", - "symbol": "RSP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ditto-ditto", - "name": "Ditto", - "symbol": "DITTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lkt-lukutex", - "name": "Lukutex", - "symbol": "LKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yft-toshifyfinance", - "name": "Toshify.finance", - "symbol": "YFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flo-flourmix", - "name": "FlourMix", - "symbol": "FLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "invox-invox-finance", - "name": "Invox Finance", - "symbol": "INVOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ap3-ap3town", - "name": "AP3.town", - "symbol": "AP3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "infinity-infinity-protocol-bsc", - "name": "Infinity Protocol BSC", - "symbol": "INFINITY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mec-mecro-coin", - "name": "Mecro Coin", - "symbol": "MEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "starship-starship", - "name": "StarShip", - "symbol": "STARSHIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbc-blue-baikal", - "name": "Blue Baikal", - "symbol": "BBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mixs-streamix", - "name": "Streamix", - "symbol": "MIXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "joy-joy-coin", - "name": "Joy Coin", - "symbol": "JOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kash-kids-cash", - "name": "Kids Cash", - "symbol": "KASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lift-lift-kitchen", - "name": "Lift Kitchen", - "symbol": "LIFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spirit-spiritswap", - "name": "SpiritSwap", - "symbol": "SPIRIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pefi-penguin-finance", - "name": "Penguin Finance", - "symbol": "PEFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oryx-oryx", - "name": "ORYX", - "symbol": "ORYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defla-defla", - "name": "Defla", - "symbol": "DEFLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wbind-wrapped-bind", - "name": "Wrapped BIND", - "symbol": "WBIND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "polydoge-polydoge", - "name": "PolyDoge", - "symbol": "POLYDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stbz-stabilize", - "name": "Stabilize", - "symbol": "STBZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crwn-crown-finance", - "name": "Crown Finance", - "symbol": "CRWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rope-rope", - "name": "Rope", - "symbol": "$ROPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "baby-baby-token", - "name": "Baby Token", - "symbol": "BABY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gc-galaxy-wallet", - "name": "Galaxy Wallet", - "symbol": "GC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smdx-somidax", - "name": "SOMIDAX", - "symbol": "SMDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "torq-torq-coin", - "name": "TORQ Coin", - "symbol": "TORQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtp-multiplay", - "name": "MultiPlay", - "symbol": "MTP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "can-coinwaycoin", - "name": "Coinwaycoin", - "symbol": "CAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nrch-enreachdao", - "name": "EnreachDAO", - "symbol": "NRCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dragon-dragon-finance", - "name": "Dragon Finance", - "symbol": "DRAGON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btkc-beautyk", - "name": "BeautyK", - "symbol": "BTKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mpt-meetple", - "name": "Meetple", - "symbol": "MPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metamoon-metamoon", - "name": "MetaMoon", - "symbol": "METAMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bct-bitcratic", - "name": "Bitcratic", - "symbol": "BCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rtk-ruletka", - "name": "Ruletka", - "symbol": "RTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pvg-pilnette", - "name": "Pilnette", - "symbol": "PVG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "redpanda-redpanda-earth", - "name": "REDPANDA EARTH", - "symbol": "REDPANDA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rkg-rap-keo-group", - "name": "Rap Keo Group", - "symbol": "RKG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrg-ergon", - "name": "Ergon", - "symbol": "XRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "arow-arow", - "name": "Arow", - "symbol": "AROW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sih-salient-investment-holding", - "name": "Salient Investment Holding", - "symbol": "SIH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pcb-451pcbcom", - "name": "451PCBcom", - "symbol": "PCB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsg-basis-gold", - "name": "Basis Gold", - "symbol": "BSG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slut-slutcoin", - "name": "SlutCoin", - "symbol": "SLUT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "steth-stakehound-staked-ether", - "name": "StakeHound Staked Ether", - "symbol": "STETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mark-benchmark-protocol", - "name": "Benchmark Protocol", - "symbol": "MARK", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ucm-unicly-chris-mccann-collection", - "name": "Unicly Chris McCann Collection", - "symbol": "UCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pot-hotpot-base-token", - "name": "Hotpot Base Token", - "symbol": "POT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonshot-moonshot", - "name": "Moonshot", - "symbol": "MOONSHOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tmg-tamago", - "name": "TAMAGO", - "symbol": "TMG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbeer-cold-beer-token", - "name": "Cold Beer Token", - "symbol": "cBEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ruler-ruler-protocol", - "name": "Ruler Protocol", - "symbol": "RULER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tln-trustlines-network", - "name": "Trustlines Network", - "symbol": "TLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hks-hackerspace", - "name": "HackerSpace", - "symbol": "HKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gtm-gotomoon", - "name": "GoToMoon", - "symbol": "GTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nrdi-nirodium", - "name": "Nirodium", - "symbol": "NRDI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fen-fennec", - "name": "Fennec", - "symbol": "FEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sos-solstarter", - "name": "Solstarter", - "symbol": "SOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "creed-creed", - "name": "CREED", - "symbol": "CREED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dec-distributed-energy-coin", - "name": "Distributed Energy Coin", - "symbol": "DEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lxmt-luxurium", - "name": "Luxurium", - "symbol": "LXMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rrc-rrspace", - "name": "RRSpace", - "symbol": "RRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gldr-golder-coin", - "name": "Golder Coin", - "symbol": "GLDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dbx-dbx", - "name": "DBX", - "symbol": "DBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "boa-boa", - "name": "BOA", - "symbol": "BOA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moochii-moochii", - "name": "MOOCHII", - "symbol": "MOOCHII", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wdc-wisdom-chain", - "name": "Wisdom Chain", - "symbol": "WDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yun-yunex-yun-token", - "name": "YunEx Yun Token", - "symbol": "YUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mo-morality", - "name": "Morality", - "symbol": "MO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dbz-dogeballz", - "name": "DogeBallZ", - "symbol": "DBZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stkr-staker-dao", - "name": "Staker DAO", - "symbol": "STKR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jacq-designing-the-future", - "name": "Designing the Future", - "symbol": "JACQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pepe-pepe-coin", - "name": "PePe Coin", - "symbol": "PePe", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plant-plantcoin", - "name": "PlantCoin", - "symbol": "PLANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "infi-insured-finance", - "name": "Insured Finance", - "symbol": "INFI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "comc-community-chain", - "name": "Community Chain", - "symbol": "COMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xbe-xbe-token", - "name": "XBE Token", - "symbol": "XBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "znga-zynga-inc", - "name": "Zynga Inc.", - "symbol": "ZNGA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fork-gastroadvisor", - "name": "GastroAdvisor", - "symbol": "FORK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zm-zoom-video-communications", - "name": "Zoom Video Communications", - "symbol": "ZM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "z-zillow", - "name": "Zillow", - "symbol": "Z", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xom-exxon-mobil", - "name": "Exxon Mobil", - "symbol": "XOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "weed-canopy-growth", - "name": "Canopy Growth", - "symbol": "WEED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tls-tls-token", - "name": "TLS Token", - "symbol": "TLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonpaw-moonpaw", - "name": "MoonPaw", - "symbol": "MOONPAW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mc-monkey-coin", - "name": "Monkey Coin", - "symbol": "MC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vst-vistra-energy", - "name": "Vistra Energy", - "symbol": "VST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vrtx-vertex", - "name": "Vertex", - "symbol": "VRTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vale-vale", - "name": "Vale", - "symbol": "VALE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ubot-direxion-daily-robotics-artificial-intelligence-amp-automation-index-bull-3x-shares", - "name": "Direxion Daily Robotics Artificial Intelligence \u0026 Automation Index Bull 3X Shares", - "symbol": "UBOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "anc-aragon-china", - "name": "Aragon China", - "symbol": "ANC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rich-richierich-coin", - "name": "RichieRich Coin", - "symbol": "RICH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iote-iote", - "name": "IOTE", - "symbol": "IOTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ganja-trees-finance", - "name": "Trees Finance", - "symbol": "GANJA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ushiba-american-shiba", - "name": "American Shiba", - "symbol": "USHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ual-united", - "name": "United ", - "symbol": "UAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "izer-izeroium", - "name": "IZEROIUM", - "symbol": "IZER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "biki-biki", - "name": "BIKI", - "symbol": "BIKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tui-tui-ag", - "name": "TUI AG", - "symbol": "TUI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trb-trebit-network", - "name": "Trebit Network", - "symbol": "TRB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bpc-backpacker-coin", - "name": "BackPacker Coin", - "symbol": "BPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gldx-goldnero", - "name": "Goldnero", - "symbol": "GLDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bdi-basketdao-defi-index", - "name": "BasketDAO DeFi Index", - "symbol": "BDI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ts-tenaris-usd", - "name": "Tenaris - USD", - "symbol": "TS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tlt-ishares-20-year-treasury-bond-etf", - "name": "iShares 20+ Year Treasury Bond ETF", - "symbol": "TLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tit-telecom-italia-spa", - "name": "Telecom Italia SPA", - "symbol": "TIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mkey-medikey", - "name": "MEDIKEY", - "symbol": "MKEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tenm-tenaris-sa", - "name": "Tenaris SA", - "symbol": "TENm", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boob-boobank", - "name": "BooBank", - "symbol": "BOOB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tef-telefonica", - "name": "Telefonica ", - "symbol": "TEF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tan-invesco-solar-etf", - "name": "Invesco Solar ETF", - "symbol": "TAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cakecrypt-cakecrypt", - "name": "CakeCrypt", - "symbol": "CAKECRYPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "timec-timecoin", - "name": "TIMEcoin", - "symbol": "TIMEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sslca-sandstorm-gold", - "name": "Sandstorm Gold", - "symbol": "SSLca", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sm-sm-energy", - "name": "SM Energy", - "symbol": "SM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "skt-tanger-factory-reit", - "name": "Tanger Factory Reit", - "symbol": "SKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cid-cryptid", - "name": "Cryptid", - "symbol": "CID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "die-die", - "name": "Die", - "symbol": "DIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ecp-ecp-technology", - "name": "ECP+ Technology", - "symbol": "ECP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sgl-sgl-carbon", - "name": "Sgl Carbon", - "symbol": "SGL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "weather-weather-finance", - "name": "Weather Finance", - "symbol": "WEATHER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hd-hubdao", - "name": "HubDao", - "symbol": "HD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sedg-solaredge-technologies", - "name": "SolarEdge Technologies", - "symbol": "SEDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbry-sainsbury", - "name": "Sainsbury", - "symbol": "SBRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rr-rolls-royce", - "name": "Rolls-royce", - "symbol": "RR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rdsa-royal-dutch-shell-plc", - "name": "Royal Dutch Shell plc", - "symbol": "RDSa", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "solarite-solarite", - "name": "Solarite", - "symbol": "SOLARITE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "r64x-r64x", - "name": "R64X", - "symbol": "R64X", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "emp-electronic-move-pay", - "name": "Electronic Move Pay", - "symbol": "EMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bigo-bigocoin", - "name": "BIGOCOIN", - "symbol": "BIGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qqq-invesco-qqq-trust", - "name": "Invesco QQQ Trust", - "symbol": "QQQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pzza-papa-johns", - "name": "Papa Johns", - "symbol": "PZZA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lfbtc-lift-kitchen", - "name": "Lift Kitchen", - "symbol": "LFBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "myx-myx-network", - "name": "MYX Network", - "symbol": "MYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "porte-porte-token", - "name": "Porte Token", - "symbol": "PORTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gl-green-light", - "name": "Green Light", - "symbol": "GL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "psk-pool-of-stake", - "name": "Pool of Stake", - "symbol": "PSK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pubm-pubmatic-inc", - "name": "PubMatic, Inc.", - "symbol": "PUBM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tip-technology-innovation-project", - "name": "Technology Innovation Project", - "symbol": "TIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dark-darkbuild-v1", - "name": "Dark.Build v1", - "symbol": "DARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spup-spurt-plus", - "name": "Spurt Plus", - "symbol": "SPUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pltk-playtika-holding-corp", - "name": "Playtika Holding Corp.", - "symbol": "PLTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vcn-versacoin", - "name": "VersaCoin", - "symbol": "VCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "urus-urus-token", - "name": "Urus Token", - "symbol": "URUS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "com-complus-network", - "name": "Complus Network", - "symbol": "COM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pah3-porsche", - "name": "Porsche", - "symbol": "PAH3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtm-momentum-token", - "name": "Momentum Token", - "symbol": "MTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rel-relevant", - "name": "Relevant", - "symbol": "REL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "o2d-telefonica-de", - "name": "Telefonica DE", - "symbol": "O2D", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nrg-nrg-energy", - "name": "NRG Energy", - "symbol": "NRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ndx1-nordex", - "name": "Nordex", - "symbol": "NDX1", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "heal-etheal", - "name": "Etheal", - "symbol": "HEAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "imbtc-the-tokenized-bitcoin", - "name": "The Tokenized Bitcoin", - "symbol": "IMBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mur-murphy-oil", - "name": "Murphy Oil", - "symbol": "MUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exnt-exnetwork-token", - "name": "ExNetwork Token", - "symbol": "EXNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mts-mtblock", - "name": "MtBlock", - "symbol": "MTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asy-asyagro", - "name": "ASYAGRO", - "symbol": "ASY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msrsru-moesk", - "name": "MOESK", - "symbol": "MSRSru", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bmj-bmj-coin", - "name": "BMJ Coin", - "symbol": "BMJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pux-polypux", - "name": "PolypuX", - "symbol": "PUX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrkcru-idgc-centre", - "name": "IDGC CENTRE", - "symbol": "MRKCru", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moexru-moscow-exchange", - "name": "Moscow Exchange", - "symbol": "MOEXru", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drk-drakoin", - "name": "Drakoin", - "symbol": "DRK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flux-datamine-flux", - "name": "Datamine FLUX", - "symbol": "FLUX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "md-mednax", - "name": "Mednax", - "symbol": "MD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mchi-ishares-msci-china-etf", - "name": "iShares MSCI China ETF", - "symbol": "MCHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "magnru-magnitogorsk-iron-amp-steel-works", - "name": "Magnitogorsk Iron \u0026 Steel Works", - "symbol": "MAGNru", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "llus-li-auto-inc", - "name": "Li Auto Inc.", - "symbol": "Llus", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "li-klepierre-reit", - "name": "Klepierre Reit", - "symbol": "LI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cash2-cash2", - "name": "Cash2", - "symbol": "CASH2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chtc-cryptohashtank-coin", - "name": "CryptoHashTank Coin", - "symbol": "CHTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "float-float-protocol-float", - "name": "Float Protocol: Float", - "symbol": "FLOAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bio-biocrypt", - "name": "BioCrypt", - "symbol": "BIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bt-bithash-token", - "name": "BitHash Token", - "symbol": "BT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "udt-unlock-protocol", - "name": "Unlock Protocol", - "symbol": "UDT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ymen-ymenfinance", - "name": "Ymen.Finance", - "symbol": "YMEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bwb-bit-world-token", - "name": "Bit World Token", - "symbol": "BWB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "leo-leoni-ag", - "name": "Leoni AG", - "symbol": "LEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arc-arthur-chain", - "name": "Arthur Chain", - "symbol": "ARC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccf-cerberus-finance", - "name": "Cerberus Finance", - "symbol": "CCF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldo-leonardo-spa", - "name": "Leonardo SPA", - "symbol": "LDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thirm-thirm-protocol", - "name": "Thirm Protocol", - "symbol": "THIRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lbk-liberbank", - "name": "Liberbank", - "symbol": "LBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ks-ks", - "name": "K+S", - "symbol": "KS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kopn-kopin-corporation", - "name": "Kopin Corporation", - "symbol": "KOPN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unidx-unidex", - "name": "UniDex", - "symbol": "UNIDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ami-ammyi-coin", - "name": "AMMYI Coin", - "symbol": "AMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cvxcrv-convex-crv", - "name": "Convex CRV", - "symbol": "CVXCRV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jdst-direxion-daily-junior-gold-miners-index-bear-3x-shares", - "name": "Direxion Daily Junior Gold Miners Index Bear 3X Shares", - "symbol": "JDST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pbs-pbs-chain", - "name": "PBS Chain", - "symbol": "PBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "holx-hologic", - "name": "Hologic", - "symbol": "HOLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obr-obr", - "name": "OBR", - "symbol": "OBR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hear-turtle-beach-corporation", - "name": "Turtle Beach Corporation", - "symbol": "HEAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hd-the-home-depot", - "name": "The Home Depot", - "symbol": "HD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "water-water-finance", - "name": "Water Finance", - "symbol": "WATER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goco-gohealth-inc", - "name": "GoHealth, Inc.", - "symbol": "GOCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apw-apwine", - "name": "APWine", - "symbol": "APW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gmknru-mmc-norilsk-nickel", - "name": "MMC Norilsk Nickel", - "symbol": "GMKNru", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wlt-warlord-token", - "name": "Warlord Token", - "symbol": "WLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gm-gm", - "name": "GM", - "symbol": "GM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swipe-swipe-network", - "name": "SWIPE Network", - "symbol": "SWIPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fugr-fugro", - "name": "Fugro", - "symbol": "FUGR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethpy-etherpay", - "name": "Etherpay", - "symbol": "ETHPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fe-firstenergy-corp", - "name": "FirstEnergy Corp.", - "symbol": "FE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "epd-enterprise-products", - "name": "Enterprise Products", - "symbol": "EPD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smart-moonsmart", - "name": "MoonSmart", - "symbol": "SMART", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "phoon-typhoon-cash", - "name": "Typhoon Cash", - "symbol": "PHOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coi-coinnec", - "name": "Coinnec", - "symbol": "COI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sparta-sparta", - "name": "Sparta", - "symbol": "SPARTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crts-cryptotipsfr", - "name": "Cryptotipsfr", - "symbol": "CRTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "enph-enphase-energy-inc", - "name": "Enphase Energy, Inc.", - "symbol": "ENPH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ea-electronic-arts", - "name": "Electronic Arts", - "symbol": "EA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dsky-detskiy-mir", - "name": "Detskiy Mir", - "symbol": "DSKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "driv-global-x-autonomous-amp-electric-vehicles-etf", - "name": "Global X Autonomous \u0026 Electric Vehicles ETF", - "symbol": "DRIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sicc-swisscoin-classic", - "name": "Swisscoin Classic", - "symbol": "SICC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tgic-the-global-index-chain", - "name": "The Global Index Chain", - "symbol": "TGIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "disc-discovery", - "name": "Discovery", - "symbol": "DISC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cdy-bitcoin-candy", - "name": "Bitcoin Candy", - "symbol": "CDY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mptc-mnpostree", - "name": "MNPoSTree", - "symbol": "MPTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cvx-chevron", - "name": "Chevron", - "symbol": "CVX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpg-crescent-point", - "name": "Crescent Point", - "symbol": "CPG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zbk-zbank-token", - "name": "Zbank Token", - "symbol": "ZBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crd-crowd-one", - "name": "Crowd One", - "symbol": "CRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scash-scash", - "name": "sCASH", - "symbol": "SCASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chmfru-severstal", - "name": "Severstal", - "symbol": "CHMFru", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccli-carl-gbp", - "name": "Carl - GBP", - "symbol": "CCLI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbomru-moskovskiy-kreditnyi-bank", - "name": "Moskovskiy Kreditnyi Bank", - "symbol": "CBOMru", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prime-primedao", - "name": "PrimeDAO", - "symbol": "PRIME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brka10-berkshire-10-index", - "name": "Berkshire 10 Index", - "symbol": "BRK.A10", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cyberd-cyber-doge", - "name": "Cyber Doge", - "symbol": "CYBERD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meowcat-meowcat", - "name": "MeowCat", - "symbol": "MEOWCAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bp-bp-usd", - "name": "BP - USD", - "symbol": "BP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kmc-king-maker-coin", - "name": "King Maker Coin", - "symbol": "KMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pfi-prime-finance", - "name": "Prime Finance", - "symbol": "PFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bpgbp-bp-gbp", - "name": "BP - GBP", - "symbol": "BP_GBP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rle-rich-lab-token", - "name": "Rich Lab Token", - "symbol": "RLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "be-bloom-energy-corporation", - "name": "Bloom Energy Corporation", - "symbol": "BE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfiec-yearn-finance-ecosystem", - "name": "Yearn Finance Ecosystem", - "symbol": "YFIEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbd-banco-bradesco", - "name": "Banco Bradesco", - "symbol": "BBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "omc-omc-group", - "name": "OMC Group", - "symbol": "OMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfid-yfidapp", - "name": "YFIDapp", - "symbol": "YFID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "koge-bnb48-club-token", - "name": "BNB48 Club Token", - "symbol": "KOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cmg-cmgcoin", - "name": "CMGCoin", - "symbol": "CMG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dltx-deltaexcoin", - "name": "DeltaExCoin", - "symbol": "DLTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bayn-bayer", - "name": "Bayer", - "symbol": "BAYN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "willie-williecoin", - "name": "Williecoin", - "symbol": "WILLIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ruc-rush", - "name": "Rush", - "symbol": "RUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "start-bscstarter", - "name": "BSCstarter", - "symbol": "START", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bamn-koninklijke-bam", - "name": "Koninklijke BAM", - "symbol": "BAMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ayx-alteryx", - "name": "Alteryx", - "symbol": "AYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "auix-us-airlines-index", - "name": "US Airlines Index", - "symbol": "AUIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slink-soft-link", - "name": "Soft Link", - "symbol": "SLINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vgr-voyager", - "name": "Voyager", - "symbol": "VGR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "szc-zugacoin", - "name": "Zugacoin", - "symbol": "SZC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vnl-vanilla", - "name": "Vanilla", - "symbol": "VNL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "koin-koinos", - "name": "Koinos", - "symbol": "KOIN", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pnl-true-pnl", - "name": "True PNL", - "symbol": "PNL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arkq-ark-autonomous-technology-amp-robotics-etf", - "name": "ARK Autonomous Technology \u0026 Robotics ETF", - "symbol": "ARKQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arkk-ark-innovation-etf", - "name": "ARK Innovation ETF", - "symbol": "ARKK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apha-aphria", - "name": "Aphria", - "symbol": "APHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safelogic-safelogic", - "name": "Safelogic", - "symbol": "SAFELOGIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tst-touch-social", - "name": "Touch Social", - "symbol": "TST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ddn-data-delivery-network", - "name": "Data Delivery Network", - "symbol": "DDN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aph-amphenol", - "name": "Amphenol", - "symbol": "APH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stro-supertron", - "name": "Supertron", - "symbol": "STRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bicas-bithercash", - "name": "BitherCash", - "symbol": "BICAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ana-acciona", - "name": "Acciona", - "symbol": "ANA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kseed-kush-finance", - "name": "Kush Finance", - "symbol": "KSEED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mexp-moji-experience-points", - "name": "MOJI Experience Points", - "symbol": "MEXP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hoo-hoo-token", - "name": "Hoo Token", - "symbol": "HOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "licc-life-is-camping-community", - "name": "Life Is Camping Community", - "symbol": "LICC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nlm-nuclum", - "name": "NUCLUM", - "symbol": "NLM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amrs-amyris-inc", - "name": "Amyris, Inc.", - "symbol": "AMRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kvi-kvi", - "name": "KVI", - "symbol": "KVI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stn-sting", - "name": "Sting", - "symbol": "STN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cnct-connect", - "name": "Connect", - "symbol": "CNCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dream-dreamscoin", - "name": "DreamsCoin", - "symbol": "DREAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ujord-unicly-air-jordan-1st-drop-collection", - "name": "Unicly Air Jordan 1st Drop Collection", - "symbol": "UJORD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tfd-etf-dao", - "name": "ETF Dao", - "symbol": "TFD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aieq-ai-powered-equity-etf", - "name": "AI Powered Equity ETF", - "symbol": "AIEQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ag-first-majestic-silver-corp", - "name": "First Majestic Silver Corp.", - "symbol": "AG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bmch-bmeme-cash", - "name": "bMeme Cash", - "symbol": "BMCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cadc-cad-coin", - "name": "CAD Coin", - "symbol": "CADC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chess-chess-coin", - "name": "Chess Coin", - "symbol": "CHESS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usg-usgold", - "name": "USGold", - "symbol": "USG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vcash-vcash-token", - "name": "VCash Token", - "symbol": "VCASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "enx-enex", - "name": "ENEX", - "symbol": "ENX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agar-aga-rewards", - "name": "AGA Rewards", - "symbol": "AGAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bag-basis-gold-heco", - "name": "Basis Gold (Heco)", - "symbol": "BAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pld-pureland-project", - "name": "Pureland Project", - "symbol": "PLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cleanocean-cleanocean", - "name": "CleanOcean", - "symbol": "CLEANOCEAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "afrm-affirm-holdings-inc", - "name": "Affirm Holdings, Inc.", - "symbol": "AFRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tori-storichain-token", - "name": "Storichain Token", - "symbol": "TORI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swiss-swissfinance", - "name": "swiss.finance", - "symbol": "SWISS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pusd-pegsusd", - "name": "PegsUSD", - "symbol": "PUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "acb-aurora-cannabis", - "name": "Aurora Cannabis", - "symbol": "ACB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abnb-airbnb-inc", - "name": "Airbnb, Inc.", - "symbol": "ABNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lbd-linkbased", - "name": "LinkBased", - "symbol": "LBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abcl-abcellera-biologics-inc", - "name": "AbCellera Biologics Inc.", - "symbol": "ABCL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aal-american-airlines-group", - "name": "American Airlines Group", - "symbol": "AAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "r3t-rock3t", - "name": "ROCK3T", - "symbol": "R3T", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbt-community-business-token", - "name": "Community Business Token", - "symbol": "CBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fridge-fridge-token", - "name": "Fridge Token", - "symbol": "FRIDGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbet-sbet", - "name": "SBET", - "symbol": "SBET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibaken-shibaken-finance", - "name": "Shibaken Finance", - "symbol": "SHIBAKEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "7004-hitachi-zosen-corporation", - "name": "Hitachi Zosen Corporation", - "symbol": "7004", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "3606-fuyao-glass-industry", - "name": "Fuyao Glass Industry", - "symbol": "3606", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "3323-cnbm", - "name": "CNBM", - "symbol": "3323", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "betc-bet-chips", - "name": "Bet Chips", - "symbol": "BETC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1919-cosco-shipping", - "name": "COSCO Shipping", - "symbol": "1919", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cca-counos-coin", - "name": "Counos Coin", - "symbol": "CCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lp-leopard-coin", - "name": "LeoPard Coin", - "symbol": "LP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xsp-xswap", - "name": "XSwap", - "symbol": "XSP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mowl-moon-owl", - "name": "Moon Owl", - "symbol": "MOWL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1913-prada", - "name": "Prada", - "symbol": "1913", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fdd-frogdao-dime", - "name": "FrogDAO Dime", - "symbol": "FDD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tft-the-famous-token", - "name": "The Famous Token", - "symbol": "TFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1898-china-coal-energy", - "name": "China Coal Energy", - "symbol": "1898", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1618-metallurgical-corp", - "name": "Metallurgical Corp", - "symbol": "1618", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbucks-cryptobucks", - "name": "CRYPTOBUCKS", - "symbol": "CBUCKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "debase-debase", - "name": "Debase", - "symbol": "DEBASE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dfk-defiking", - "name": "DefiKing", - "symbol": "DFK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gly-glyph-token", - "name": "Glyph Token", - "symbol": "GLY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aem-atheneum", - "name": "Atheneum", - "symbol": "AEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drip-dripper", - "name": "Dripper", - "symbol": "DRIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1177-sino-biopharmeceutical", - "name": "Sino Biopharmeceutical", - "symbol": "1177", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1171-yanzhou-coal", - "name": "Yanzhou Coal", - "symbol": "1171", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "z502-502-bad-gateway-token", - "name": "502 Bad Gateway Token", - "symbol": "Z502", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1031-kingston-financial", - "name": "Kingston Financial", - "symbol": "1031", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eeth-eos-eth", - "name": "EOS ETH", - "symbol": "EETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bft-bitfresh", - "name": "Bitfresh", - "symbol": "BFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "0992-lenovo", - "name": "Lenovo", - "symbol": "0992", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "raku-rakun", - "name": "RAKUN", - "symbol": "RAKU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hurricane-hurricane", - "name": "Hurricane", - "symbol": "HURRICANE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gvc-gemvault-coin", - "name": "GemVault Coin", - "symbol": "GVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "komp-kompass", - "name": "Kompass", - "symbol": "KOMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "0941-china-mobile", - "name": "China Mobile", - "symbol": "0941", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "0916-china-longyuan-power", - "name": "China Longyuan Power", - "symbol": "0916", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crl-coral-farm", - "name": "Coral Farm", - "symbol": "CRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "0728-china-telecom", - "name": "China Telecom", - "symbol": "0728", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onlexpa-onlexpa-token", - "name": "onLEXpa Token", - "symbol": "ONLEXPA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aapl-aapl", - "name": "AAPL", - "symbol": "AAPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "altom-altom", - "name": "ALTOM", - "symbol": "ALTOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgnn-dragon-network", - "name": "Dragon Network", - "symbol": "DGNN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amc-anonymous-coin", - "name": "Anonymous Coin", - "symbol": "AMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xbtx-bitcoin-subsidium", - "name": "Bitcoin Subsidium", - "symbol": "XBTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "glxc-galaxy-coin", - "name": "Galaxy Coin", - "symbol": "GLXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "n0031-nyfi", - "name": "nYFI", - "symbol": "N0031", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "0358-jiangxi-copper", - "name": "Jiangxi Copper", - "symbol": "0358", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "beverage-beverage", - "name": "BEVERAGE", - "symbol": "BEVERAGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "0347-angang-steel", - "name": "Angang Steel", - "symbol": "0347", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hotcross-hot-cross", - "name": "Hot Cross", - "symbol": "HOTCROSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rise-rise-protocol", - "name": "Rise Protocol", - "symbol": "RISE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "src-simracer-coin", - "name": "Simracer Coin", - "symbol": "SRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogg-dogg-token", - "name": "DOGG Token", - "symbol": "DOGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dyx-xcoinpay", - "name": "XCoinPay", - "symbol": "DYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xiaz-xiazfinance", - "name": "Xiazfinance", - "symbol": "XIAZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exy-externity", - "name": "Externity", - "symbol": "EXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lvx-lvx", - "name": "LVX", - "symbol": "LVX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tipsy-tipsy-token", - "name": "TIPSY Token", - "symbol": "TIPSY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coom-coomcoin", - "name": "CoomCoin", - "symbol": "COOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "danua-danua", - "name": "Danua", - "symbol": "DANUA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "viking-viking-finance", - "name": "Viking Finance", - "symbol": "VIKING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "panther-pantherswap", - "name": "PantherSwapNIEMOGEDODAC", - "symbol": "PANTHER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ape-ape-punk", - "name": "APE Punk", - "symbol": "APE$", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gapt-gaptt", - "name": "GAPTT", - "symbol": "GAPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safegalaxy-safegalaxy", - "name": "SafeGalaxy", - "symbol": "SAFEGALAXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vpc-voltpotcoin", - "name": "VoltPotCoin", - "symbol": "VPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hlx-helex", - "name": "Helex", - "symbol": "HLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lvn-livenpay", - "name": "LivenPay", - "symbol": "LVN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spdx-spender-x", - "name": "SPENDER-X", - "symbol": "SPDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usdsp-usdsports", - "name": "USDSPORTS", - "symbol": "USDSP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "w1ve2-2wives", - "name": "2WIVES", - "symbol": "W1VE2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wsbt-wallstreetbets-token", - "name": "WallStreetBets Token", - "symbol": "WSBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ivory-ivorygate", - "name": "Ivorygate", - "symbol": "IVORY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ferma-ferma", - "name": "Ferma", - "symbol": "FERMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lid-liquidity-dividends-protocol", - "name": "Liquidity Dividends Protocol", - "symbol": "LID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zdr-zloadr", - "name": "Zloadr", - "symbol": "ZDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rkyu-ryukyutoken", - "name": "RyukyuToken", - "symbol": "RKYU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "niche-nicheman", - "name": "NICHEMAN", - "symbol": "NICHE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xods-xods", - "name": "XODS", - "symbol": "XODS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dcore-decore", - "name": "Decore", - "symbol": "DCORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "musd-master-usd", - "name": "MASTER USD", - "symbol": "MUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ist-ishop-token", - "name": "iShop Token", - "symbol": "IST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uzz-uzuras", - "name": "UZURAS", - "symbol": "UZZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orb-orb-v2", - "name": "Orb V2", - "symbol": "ORB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bixb-bixb-coin", - "name": "BixB Coin", - "symbol": "BIXB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "prdz-predictz", - "name": "Predictz", - "symbol": "PRDZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scooby-scooby-doo-inu", - "name": "Scooby Doo Inu", - "symbol": "SCOOBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ucos-ucos-token", - "name": "UCOS Token", - "symbol": "UCOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zombie-zombies", - "name": "Zombies", - "symbol": "ZOMBIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pocroc-pocketrocket", - "name": "PocketRocket", - "symbol": "POCROC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dmc-decentralized-mining-exchange", - "name": "Decentralized Mining Exchange", - "symbol": "DMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xazab-xazab", - "name": "xazab", - "symbol": "XAZAB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pand-pandafinance", - "name": "Panda.finance", - "symbol": "PAND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pamp-pampcc", - "name": "PAMP.CC", - "symbol": "PAMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wht-wrapped-huobi-token", - "name": "Wrapped Huobi Token", - "symbol": "WHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yffc-yffcfinance", - "name": "yffc.finance", - "symbol": "YFFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sg20-sg20", - "name": "SG20", - "symbol": "SG20", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "reb2-rebased", - "name": "Rebased", - "symbol": "REB2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sprink-sprink", - "name": "Sprink", - "symbol": "SPRINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bpop-bpop", - "name": "BPOP", - "symbol": "BPOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vana-nirvana", - "name": "Nirvana", - "symbol": "VANA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "epc-epacoin", - "name": "EpaCoin", - "symbol": "EPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tro-trodl", - "name": "Trodl", - "symbol": "TRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uwaifu-unicly-waifu-collection", - "name": "Unicly Waifu Collection", - "symbol": "UWAIFU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aurora-auroratoken", - "name": "AuroraToken", - "symbol": "AURORA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mufc-manchester-united-fan-token", - "name": "Manchester United Fan Token", - "symbol": "MUFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cred-street-credit", - "name": "Street Credit", - "symbol": "CRED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yeth-fyethfinance", - "name": "Fyeth.finance", - "symbol": "YETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unagi-unagi-token", - "name": "Unagi Token", - "symbol": "UNAGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctg-cryptorg-token", - "name": "Cryptorg Token", - "symbol": "CTG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wilc-wrapped-ilcoin", - "name": "Wrapped ILCOIN", - "symbol": "WILC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kpc-koloop-basic", - "name": "Koloop Basic", - "symbol": "KPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xtg-xtg-world", - "name": "XTG World", - "symbol": "XTG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gfcc-gold-finch-crypto-currency", - "name": "GOLD FINCH CRYPTO CURRENCY", - "symbol": "GFCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lync-lync-network", - "name": "LYNC Network", - "symbol": "LYNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doge-2-doge-2", - "name": "DOGE-2", - "symbol": "DOGE-2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "torro-bittorro", - "name": "BitTorro", - "symbol": "TORRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qcad-qcad", - "name": "QCAD", - "symbol": "QCAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safepluto-safepluto", - "name": "SafePluto", - "symbol": "SAFEPLUTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thor-asgard-finance", - "name": "Asgard Finance", - "symbol": "THOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swag-swag-finance", - "name": "SWAG Finance", - "symbol": "SWAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slds-solidus", - "name": "Solidus", - "symbol": "SLDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yfia-yfia", - "name": "YFIA", - "symbol": "YFIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hfs-holderswap", - "name": "HolderSwap", - "symbol": "HFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lc-libra-coin", - "name": "Libra Coin", - "symbol": "LC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gene-gene", - "name": "Gene", - "symbol": "GENE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfl-bitflate", - "name": "Bitflate", - "symbol": "BFL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bbra-boobanker-research-association", - "name": "BooBanker Research Association", - "symbol": "BBRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wusd-wrapped-usd", - "name": "Wrapped USD", - "symbol": "WUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tno-tnos-coin", - "name": "TNOS COIN", - "symbol": "TNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dxy-dxy-finance", - "name": "DXY Finance", - "symbol": "DXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shrimp-shrimp-finance", - "name": "Shrimp Finance", - "symbol": "SHRIMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yaxis-yaxis", - "name": "yAxis", - "symbol": "YAXIS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mph-88mph", - "name": "88mph", - "symbol": "MPH", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "psb-pesobit", - "name": "Pesobit", - "symbol": "PSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "starbound-starbound", - "name": "StarBound", - "symbol": "StarBound", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fetish-fetish-coin", - "name": "Fetish Coin", - "symbol": "FETISH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brick-rfortnitebr-bricks", - "name": "r/FortNiteBR Bricks", - "symbol": "BRICK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fpt-fuupay", - "name": "FUUPAY", - "symbol": "FPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jdi-jdi-token", - "name": "JDI Token", - "symbol": "JDI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rvp-revolution-populi", - "name": "Revolution Populi", - "symbol": "RVP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnv-benative", - "name": "BeNative", - "symbol": "BNV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vlp-valup", - "name": "Valup", - "symbol": "VLP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jaguar-jaguarswap", - "name": "JaguarSwap", - "symbol": "JAGUAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yff-yfffinance", - "name": "YFF.Finance", - "symbol": "YFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xaaveb-xaaveb", - "name": "xAAVEb", - "symbol": "XAAVEB ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dun-dune", - "name": "Dune", - "symbol": "DUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hc-hypercoin", - "name": "HyperCoin", - "symbol": "HC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crs-crypto-rewards-studio", - "name": "Crypto Rewards Studio", - "symbol": "CRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "taco-tacos", - "name": "Tacos", - "symbol": "TACO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cring-darwinia-crab-network-native-token", - "name": "Darwinia Crab Network Native Token", - "symbol": "CRING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "teslf-teslafan", - "name": "Teslafan", - "symbol": "TESLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elnc-eloniumcoin", - "name": "EloniumCoin", - "symbol": "ELNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfih2-yfih2", - "name": "YFIH2", - "symbol": "YFIH2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ggtk-gg-token", - "name": "GG Token", - "symbol": "GGTK", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wtf-walnutfinance", - "name": "Walnut.finance", - "symbol": "WTF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cht-crypto-heroes-token", - "name": "Crypto Heroes Token", - "symbol": "CHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dby-dobuy", - "name": "Dobuy", - "symbol": "DBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lic-lightening-cash", - "name": "Lightening Cash", - "symbol": "LIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctat-cryptassist", - "name": "Cryptassist", - "symbol": "CTAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gaze-gazetv", - "name": "GazeTV", - "symbol": "GAZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smoon-swiftmoon", - "name": "SwiftMoon", - "symbol": "SMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sendwhale-sendwhale", - "name": "SendWhale", - "symbol": "SENDWHALE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safebtc-safebtc", - "name": "SafeBTC", - "symbol": "SAFEBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pib-passive-income-bot", - "name": "Passive Income Bot", - "symbol": "PIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnz-bonezyard", - "name": "BonezYard", - "symbol": "BNZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgn-degen-protocol", - "name": "Degen Protocol", - "symbol": "DGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trop-interop", - "name": "Interop", - "symbol": "TROP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kombat-crypto-kombat-token", - "name": "Crypto Kombat Token", - "symbol": "KOMBAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stxym-stakedxym", - "name": "stakedXYM", - "symbol": "STXYM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pngn-spacepenguin", - "name": "SpacePenguin", - "symbol": "PNGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbt-blockbase", - "name": "BlockBase", - "symbol": "BBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gswap-gameswap", - "name": "Gameswap", - "symbol": "GSWAP", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mcbase-mcbase-finance", - "name": "McBase Finance", - "symbol": "MCBASE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cuban-cuban-coin", - "name": "Cuban Coin", - "symbol": "CUBAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "donu-donu", - "name": "Donu", - "symbol": "DONU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "safecomet-safecomet", - "name": "SafeComet", - "symbol": "SAFECOMET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "umc-universal-marketing-coin", - "name": "Universal Marketing Coin", - "symbol": "UMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trade-smart-trade-coin", - "name": "Smart Trade Coin", - "symbol": "TRADE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dsc-live-swap-coin", - "name": "Live Swap Coin", - "symbol": "DSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ean-eanto", - "name": "EANTO", - "symbol": "EAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "syfi-soft-yearn", - "name": "Soft Yearn", - "symbol": "SYFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "myid-my-identity-coin", - "name": "MY IDENTITY COIN", - "symbol": "MYID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sola-solarys", - "name": "Solarys", - "symbol": "SOLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xpix-xpixbr", - "name": "XPIXBR", - "symbol": "XPIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ndx-indexed-finance", - "name": "Indexed Finance", - "symbol": "NDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dvc-dragonvein", - "name": "DragonVein", - "symbol": "DVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "esg-empty-set-gold", - "name": "Empty Set Gold", - "symbol": "ESG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xfii-xfii", - "name": "XFII", - "symbol": "XFII", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tem-totem", - "name": "Totem", - "symbol": "TEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jam-jambo", - "name": "JAMBO", - "symbol": "JAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ddam-decentralized-data-assets-management", - "name": "Decentralized Data Assets Management", - "symbol": "DDAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rfy-rfyield-finance", - "name": "RFYield Finance", - "symbol": "RFY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jigg-jiggly-finance", - "name": "Jiggly Finance", - "symbol": "JIGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smf-safe-moon-fast", - "name": "Safe Moon Fast", - "symbol": "SMF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfi-beflectfinance", - "name": "Beflect.Finance", - "symbol": "BFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tbcc-tbcc-wallet", - "name": "TBCC Wallet", - "symbol": "TBCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arteon-arteon", - "name": "Arteon", - "symbol": "ARTEON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ohmc-ohmcoin", - "name": "Ohmcoin", - "symbol": "OHMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "safeorbit-safeorbit", - "name": "SafeOrbit", - "symbol": "SAFEORBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bark-barking", - "name": "Barking", - "symbol": "BARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dex-alphadex", - "name": "AlphaDex", - "symbol": "DEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "roz-rozeus", - "name": "Rozeus", - "symbol": "ROZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xdt-xwc-dice-token", - "name": "XWC Dice Token", - "symbol": "XDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dds-ddsstore", - "name": "DDS.Store", - "symbol": "DDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chum-chumhum-finance", - "name": "Chumhum Finance", - "symbol": "CHUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dudgx-dudgx", - "name": "DudgX", - "symbol": "DUDGX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vsn-vision", - "name": "Vision", - "symbol": "VSN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cakita-chubbyakita", - "name": "ChubbyAkita", - "symbol": "CAKITA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ceds-cedars", - "name": "CEDARS", - "symbol": "CEDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nftpunk-nftpunkfinance", - "name": "NFTPunk.Finance", - "symbol": "NFTPUNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "syax-staked-yaxis", - "name": "Staked yAxis", - "symbol": "SYAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfrb-yfrbfinance", - "name": "yfrb.Finance", - "symbol": "YFRB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dacc-degen-arts-chad-coin", - "name": "Degen Arts Chad Coin", - "symbol": "DACC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ivy-ivy-mining", - "name": "Ivy Mining", - "symbol": "IVY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "whl-whaleroom", - "name": "WhaleRoom", - "symbol": "WHL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bass-bass", - "name": "BASS", - "symbol": "BASS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "roge-rogue-doge", - "name": "Rogue Doge", - "symbol": "ROGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "force-force-dao", - "name": "Force DAO", - "symbol": "FORCE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sd-smart-dollar", - "name": "Smart Dollar", - "symbol": "SD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b21-b21", - "name": "B21", - "symbol": "B21", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rbc-robbocoach", - "name": "RobboCoach", - "symbol": "RBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "beer-beer-token", - "name": "Beer Token", - "symbol": "BEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vlad-vlad-finance", - "name": "Vlad Finance", - "symbol": "VLAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dns-bitdns", - "name": "BitDNS", - "symbol": "DNS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cot-cosplay-token", - "name": "Cosplay Token", - "symbol": "COT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jtt-joytube-token", - "name": "JoyTube Token", - "symbol": "JTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "donk-donkey", - "name": "Donkey", - "symbol": "DONK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xph-phantom", - "name": "PHANTOM", - "symbol": "XPH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rbase-rbasefinance", - "name": "rbase.finance", - "symbol": "RBASE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rlx-relax-protocol", - "name": "RELAX Protocol", - "symbol": "RLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alex-alex", - "name": "Alex", - "symbol": "ALEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "md-moondayplus", - "name": "MoonDayPlus", - "symbol": "MD+", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oil-petroleum", - "name": "PETROLEUM", - "symbol": "OIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usdx-dollars", - "name": "Dollars", - "symbol": "USDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jpyq-jpyq-stablecoin-by-q-dao-v10", - "name": "JPYQ Stablecoin by Q DAO v1.0", - "symbol": "JPYQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wenlambo-wenlambo", - "name": "Wenlambo", - "symbol": "WENLAMBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fido-fido-token", - "name": "FIDO Token", - "symbol": "FIDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tfc-triforce-protocol", - "name": "Triforce Protocol", - "symbol": "TFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tiim-triipmiles", - "name": "TriipMiles", - "symbol": "TIIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shells-shells", - "name": "SHELLS", - "symbol": "SHELLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "guard-guard", - "name": "Guard", - "symbol": "GUARD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rebound-rebound", - "name": "Rebound", - "symbol": "REBOUND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chinu-chubby-inu", - "name": "Chubby Inu", - "symbol": "CHINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crc-core-chip", - "name": "Core-Chip", - "symbol": "CRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pct-percent", - "name": "Percent", - "symbol": "PCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "veth2-veth2", - "name": "vEth2", - "symbol": "VETH2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gil-materia", - "name": "Materia", - "symbol": "GIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dscpl-disciplina", - "name": "DISCIPLINA", - "symbol": "DSCPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fxt-fxt-token", - "name": "FXT Token", - "symbol": "FXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oac-one-army-coin", - "name": "ONE ARMY COIN", - "symbol": "OAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ett-efficient-transaction-token", - "name": "Efficient Transaction Token", - "symbol": "ETT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gneiss-gneiss", - "name": "Gneiss", - "symbol": "GNEISS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obic-obic", - "name": "OBIC", - "symbol": "OBIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "comfy-comfy-token", - "name": "Comfy Token", - "symbol": "COMFY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eyes-eyes-protocol", - "name": "EYES Protocol", - "symbol": "EYES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "up-up-token", - "name": "UP Token", - "symbol": "UP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vestx-vestxcoin", - "name": "VestxCoin", - "symbol": "VESTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "twee-tweebaa", - "name": "Tweebaa", - "symbol": "TWEE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prqboost-parsiq-boost", - "name": "Parsiq Boost", - "symbol": "PRQBOOST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "strng-stronghold", - "name": "StrongHold", - "symbol": "STRNG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bir-bitron", - "name": "Bitron", - "symbol": "BIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eswap-eswapping", - "name": "eSwapping", - "symbol": "ESWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prc-partner", - "name": "Partner", - "symbol": "PRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wcres-wrapped-crescofin", - "name": "Wrapped CrescoFin", - "symbol": "WCRES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meteor-meteoritenetwork", - "name": "Meteorite.network", - "symbol": "METEOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "franc-franc", - "name": "FRANC", - "symbol": "FRANC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "long-long-asset", - "name": "Long Asset", - "symbol": "LONG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogo-dogo-coin", - "name": "DOGO COIN", - "symbol": "DOGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bone-bone", - "name": "BONE", - "symbol": "BONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctf-cybertime-finance", - "name": "CyberTime Finance", - "symbol": "CTF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mor-mirror", - "name": "Mirror", - "symbol": "MOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stonk-stonks", - "name": "STONKS", - "symbol": "STONK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "magi-magikarp-finance", - "name": "Magikarp Finance", - "symbol": "MAGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trdl-strudel-finance", - "name": "Strudel Finance", - "symbol": "TRDL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gldy-buzzshow", - "name": "Buzzshow", - "symbol": "GLDY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "seed-sesameseed", - "name": "Sesameseed", - "symbol": "SEED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgn-diagon", - "name": "Diagon", - "symbol": "DGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "emt-elon-musk-token", - "name": "Elon Musk Token", - "symbol": "EMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cim-coincome", - "name": "COINCOME", - "symbol": "CIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gdl-gondola-finance", - "name": "Gondola Finance", - "symbol": "GDL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oks-okschain", - "name": "OksChain", - "symbol": "OKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lsc-littlesesame", - "name": "Littlesesame", - "symbol": "LSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbit-mbitbooks", - "name": "MBitBooks", - "symbol": "MBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mist-alchemist-coin", - "name": "Alchemist Coin", - "symbol": "MIST", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "udai-unagii-dai", - "name": "Unagii Dai", - "symbol": "UDAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goi-goforit-walkampwin", - "name": "GoForIt Walk\u0026Win", - "symbol": "GOI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "torj-torj-world", - "name": "TORJ World", - "symbol": "TORJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcx-bitscoin", - "name": "BITSCOIN", - "symbol": "BTCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pixeos-pixeos", - "name": "PixEOS", - "symbol": "PIXEOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebsp-ebsp-token", - "name": "EBSP Token", - "symbol": "EBSP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "feta-fetatoken", - "name": "FetaToken", - "symbol": "FETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mello-mello-token", - "name": "Mello Token", - "symbol": "MELLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lips-lipchain", - "name": "LIPCHAIN", - "symbol": "LIPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mchc-mch-coin", - "name": "MCH Coin", - "symbol": "MCHC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shark-baby-shark", - "name": "Baby Shark", - "symbol": "SHARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cgb-crypto-global-bank", - "name": "Crypto Global Bank", - "symbol": "CGB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "idl-idl-token", - "name": "IDL Token", - "symbol": "IDL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bac-bowl-a-coin", - "name": "Bowl A Coin", - "symbol": "BAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fz-frozencoin-network", - "name": "Frozencoin Network", - "symbol": "FZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttm-to-the-moon", - "name": "To The Moon", - "symbol": "TTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kper-kper-network", - "name": "Kper Network", - "symbol": "KPER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "polymoon-polymoon", - "name": "PolyMoon", - "symbol": "POLYMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dsc-data-saver-coin", - "name": "Data Saver Coin", - "symbol": "DSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sbs-staysbase", - "name": "StaysBASE", - "symbol": "SBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vi-vid", - "name": "Vid", - "symbol": "VI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ujenny-jenny-metaverse-dao-token", - "name": "Jenny Metaverse DAO Token", - "symbol": "UJENNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "munch-munch-token", - "name": "MUNCH TOKEN", - "symbol": "MUNCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mvp-mvp", - "name": "MVP", - "symbol": "MVP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spring-springrole", - "name": "SpringRole", - "symbol": "SPRING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eurt-euro-ritva-token", - "name": "Euro Ritva Token", - "symbol": "EURT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jrc-finchain", - "name": "FinChain", - "symbol": "JRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sinoc-sinoc", - "name": "SINOC", - "symbol": "SINOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcr-bankcoin-reserve", - "name": "Bankcoin Reserve", - "symbol": "BCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "smpl-smpl-foundation", - "name": "SMPL Foundation", - "symbol": "SMPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hue-hue", - "name": "Hue", - "symbol": "HUE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zfl-zuflo-coin", - "name": "Zuflo Coin", - "symbol": "ZFL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exnx-exenox-mobile", - "name": "Exenox Mobile", - "symbol": "EXNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrat-moonrat", - "name": "MoonRat", - "symbol": "MRAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "peer-unilord", - "name": "Unilord", - "symbol": "PEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-the-metaonez", - "name": "The MetaONEz", - "symbol": "META", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fla-fiola", - "name": "Fiola", - "symbol": "FLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcap-total-crypto-market-cap", - "name": "Total Crypto Market Cap", - "symbol": "TCAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fff-force-for-fast", - "name": "Force For Fast", - "symbol": "FFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "100x-100x", - "name": "100X", - "symbol": "100X", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cnrg-cryptoenergy", - "name": "CryptoEnergy", - "symbol": "CNRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "umc-umbrella-coin", - "name": "Umbrella Coin", - "symbol": "UMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sifi-simian-finance", - "name": "Simian Finance", - "symbol": "SIFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "armx-armx-unidos", - "name": "Armx Unidos", - "symbol": "ARMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "space-spacecoin7367", - "name": "Spacecoin", - "symbol": "SPACE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fotc-forte-coin", - "name": "Forte Coin", - "symbol": "FOTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kupp-kupp", - "name": "KUPP", - "symbol": "KUPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "omm-omcoin", - "name": "Omcoin", - "symbol": "OMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "save-savetheworld", - "name": "SaveTheWorld", - "symbol": "SAVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ysec-yearn-secure", - "name": "Yearn Secure", - "symbol": "YSEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crypl-cryptolandy", - "name": "Cryptolandy", - "symbol": "CRYPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evc-eco-value-coin", - "name": "Eco Value Coin", - "symbol": "EVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coy-coinanalyst", - "name": "CoinAnalyst", - "symbol": "COY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssl-sergs-governance", - "name": "SERGS Governance", - "symbol": "SSL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zwx-ziwax", - "name": "Ziwax", - "symbol": "ZWX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exp-exchange-payment-coin", - "name": "Exchange Payment Coin", - "symbol": "EXP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kishu-kishu-inu", - "name": "Kishu Inu", - "symbol": "KISHU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sgb-subgame", - "name": "SubGame", - "symbol": "SGB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eubi-eubi-token", - "name": "EUBI token", - "symbol": "EUBI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "isle-island-coin", - "name": "Island Coin", - "symbol": "ISLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbadger-badger-sett-badger", - "name": "Badger Sett Badger", - "symbol": "BBADGER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aia-aianetwork", - "name": "AIANetwork", - "symbol": "AIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "burny-burnyfinance", - "name": "Burny.Finance", - "symbol": "BURNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "senpai-project-senpai", - "name": "Project Senpai", - "symbol": "SENPAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rsc-rescue-token", - "name": "RESCUE TOKEN", - "symbol": "RSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btp-bitpal", - "name": "BitPal", - "symbol": "BTP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lce-lance-coin", - "name": "Lance Coin", - "symbol": "LCE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "real-real-coin", - "name": "Real Coin", - "symbol": "REAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "root-rootkit", - "name": "Rootkit", - "symbol": "ROOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pipi-pipi-coin", - "name": "PiPi Coin", - "symbol": "PiPi", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "freak-freakchain", - "name": "FreakChain", - "symbol": "FREAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "albc-albcoin", - "name": "AlbCoin", - "symbol": "ALBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "edr-edinar", - "name": "Edinar", - "symbol": "EDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wenr-weiner", - "name": "Weiner", - "symbol": "WENR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slc-shalon-coin", - "name": "SHALON COIN", - "symbol": "SLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jdc-jundcoin", - "name": "Jundcoin", - "symbol": "JDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "10set-tenset", - "name": "Tenset", - "symbol": "10SET", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ness-privateness", - "name": "PrivateNess", - "symbol": "NESS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "spot-cryptospot-token", - "name": "Cryptospot Token", - "symbol": "SPOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bscdog-bscdog", - "name": "BSCDOG", - "symbol": "BSCDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgb-magic-balancer", - "name": "Magic Balancer", - "symbol": "MGB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sthc-soothing-coin", - "name": "Soothing Coin", - "symbol": "STHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crypto-cryptocoin9180", - "name": "Cryptocoin", - "symbol": "CRYPTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sishi-sishi-token", - "name": "Sishi Token", - "symbol": "SISHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doom-doom-token", - "name": "DOOM Token", - "symbol": "DOOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prophet-prophet", - "name": "Prophet", - "symbol": "PROPHET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "layerx-unilayerx", - "name": "UnilayerX", - "symbol": "LAYERX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "id-infinite-divs", - "name": "Infinite Divs", - "symbol": "ID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldxf-ldx-defi-finance", - "name": "LDX DEFI FINANCE", - "symbol": "LDXF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grtd-gratitude", - "name": "Gratitude", - "symbol": "GRTD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vspacex-vspacex", - "name": "vSPACEX", - "symbol": "vSPACEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "satvg-saturnv-gold", - "name": "SaturnV Gold", - "symbol": "SATVG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stri-strite", - "name": "Strite", - "symbol": "STRI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lgbtq-pride", - "name": "Pride", - "symbol": "LGBTQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "g-fi-gorilla-fi", - "name": "Gorilla-Fi", - "symbol": "G-Fi", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "big-bigcoin", - "name": "Bigcoin", - "symbol": "BIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jus-just-network", - "name": "JUST NETWORK", - "symbol": "JUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wsum-wrapped-sumcoin", - "name": "Wrapped Sumcoin", - "symbol": "WSUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "whx-whitex", - "name": "WHITEX", - "symbol": "WHX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "glq-graphlinq", - "name": "GraphLinq", - "symbol": "GLQ", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "gbk-goldblock", - "name": "Goldblock", - "symbol": "GBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pa-polar", - "name": "Polar", - "symbol": "PA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "prcx-pricecoinx", - "name": "PricecoinX", - "symbol": "PRCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "satan-s8nfinance", - "name": "S8N.FINANCE", - "symbol": "SATAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "war-nftwars", - "name": "NFTWars", - "symbol": "WAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tpad-trustpad", - "name": "TrustPad", - "symbol": "TPAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lhcoin-lhcoin", - "name": "LHCoin", - "symbol": "LHCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mp4-mp4", - "name": "MP4", - "symbol": "MP4", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "olo-olo", - "name": "OLO", - "symbol": "OLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mbud-moon-bud", - "name": "Moon Bud", - "symbol": "MBUD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctpl-cultiplan", - "name": "Cultiplan", - "symbol": "CTPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gold-digital-gold", - "name": "Digital Gold", - "symbol": "GOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pika-pika-finance", - "name": "Pika Finance", - "symbol": "PIKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pzb-prizmbit-token", - "name": "Prizmbit Token", - "symbol": "PZB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fnsp-finswap", - "name": "Finswap", - "symbol": "FNSP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pshp-payship", - "name": "Payship", - "symbol": "PSHP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "svn-7finance", - "name": "7Finance", - "symbol": "SVN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chuey-chihuahua-finance", - "name": "Chihuahua Finance", - "symbol": "CHUEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fnx-financex-token", - "name": "FinanceX token", - "symbol": "FNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hcc-holiday-chain", - "name": "Holiday Chain", - "symbol": "HCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cft-coffee-token", - "name": "Coffee Token", - "symbol": "CFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ate-albetrage", - "name": "Albetrage", - "symbol": "ATE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "feb-feb-token", - "name": "FEB Token", - "symbol": "FEB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tsc-time-space-chain", - "name": "Time Space Chain", - "symbol": "TSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "luck-lady-luck", - "name": "Lady Luck", - "symbol": "LUCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vlo-velo-token", - "name": "VELO Token", - "symbol": "VLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpte-crypto-puzzles", - "name": "Crypto Puzzles", - "symbol": "CPTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "liqlo-liquid-lottery-rtc", - "name": "Liquid Lottery RTC", - "symbol": "LIQLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "myu-myubi", - "name": "Myubi", - "symbol": "MYU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vaultz-vaultz", - "name": "Vaultz", - "symbol": "VAULTZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbs3-crypto-bits", - "name": "Crypto Bits", - "symbol": "CBS3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pazzy-pazzy", - "name": "Pazzy", - "symbol": "PAZZY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "toad-toadnetwork", - "name": "TOAD.Network", - "symbol": "TOAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "2gt-2gether", - "name": "2gether", - "symbol": "2GT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vegan-vegan-swap", - "name": "Vegan Swap", - "symbol": "VEGAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stakd-stakd-finance", - "name": "Stakd Finance", - "symbol": "STAKD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bundb-unidexbot-bsc", - "name": "UniDexBot BSC", - "symbol": "BUNDB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alts-altcoins", - "name": "Altcoins", - "symbol": "ALTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aim-ai-mining", - "name": "AI Mining", - "symbol": "AIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cmdx-cmdx", - "name": "CMDX", - "symbol": "CMDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minty-minty-art", - "name": "Minty Art", - "symbol": "MINTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "supt-super-trip-chain", - "name": "Super Trip Chain", - "symbol": "SUPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kam-bitkam", - "name": "BitKAM", - "symbol": "KAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eer-ethereum-erush", - "name": "Ethereum eRush", - "symbol": "EER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pkp-pikto-group", - "name": "Pikto Group", - "symbol": "PKP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fbr-fuzzybunnyradio", - "name": "FuzzyBunnyRadio", - "symbol": "FBR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sum-sumcoin", - "name": "Sumcoin", - "symbol": "SUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yfed-yfedfinance", - "name": "YFED.Finance", - "symbol": "YFED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yyfi-yyfiprotocol", - "name": "YYFI.Protocol", - "symbol": "YYFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bulk-bulk-network", - "name": "Bulk Network", - "symbol": "BULK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gold-cryptogalaxy", - "name": "CryptoGalaxy", - "symbol": "GOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "setc-setc", - "name": "sETC", - "symbol": "SETC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rgp-rigel-protocol", - "name": "Rigel Protocol", - "symbol": "RGP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wifi-wifi-coin", - "name": "Wifi Coin", - "symbol": "WIFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pteria-pteria", - "name": "Pteria", - "symbol": "PTERIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wsdoge-doge-of-woof-street", - "name": "Doge of Woof Street", - "symbol": "WSDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slvg-silvering", - "name": "Silvering", - "symbol": "SLVG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dog-underdog", - "name": "UnderDog", - "symbol": "DOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defis-piedao-defi-small-cap", - "name": "PieDAO DEFI Small Cap", - "symbol": "DEFI+S", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inf-infinium", - "name": "Infinium", - "symbol": "INF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "b26-b26-finance", - "name": "B26 Finance", - "symbol": "B26", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "port-packageportal", - "name": "PackagePortal", - "symbol": "PORT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rzb-rizubot", - "name": "Rizubot", - "symbol": "RZB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bct-bitcredit", - "name": "BitCredit", - "symbol": "BCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thc-thc", - "name": "THC", - "symbol": "THC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bliq-bliquid", - "name": "Bliquid", - "symbol": "BLIQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitsz-bitsz", - "name": "Bitsz", - "symbol": "BITSZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nimb-nimbocoin", - "name": "NimboCoin", - "symbol": "NIMB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rc-russellcoin", - "name": "RussellCoin", - "symbol": "RC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "acryl-acryl", - "name": "Acryl", - "symbol": "ACRYL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pway-pway", - "name": "PWAY", - "symbol": "PWAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "peach-peach", - "name": "Peach", - "symbol": "PEACH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dxh-daxhund", - "name": "Daxhund", - "symbol": "DXH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hpot-hash-pot", - "name": "Hash Pot", - "symbol": "HPOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cscj-csc-jackpot", - "name": "CSC JACKPOT", - "symbol": "CSCJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uze-uze-exchange", - "name": "UZE EXCHANGE", - "symbol": "UZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cvl-civil", - "name": "Civil", - "symbol": "CVL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tgbp-truegbp", - "name": "TrueGBP", - "symbol": "TGBP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcad-truecad", - "name": "TrueCAD", - "symbol": "TCAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "koto-koto", - "name": "Koto", - "symbol": "KOTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "trn-tronnodes", - "name": "TronNodes", - "symbol": "TRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfc-yearn-finance-center", - "name": "Yearn Finance Center", - "symbol": "YFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "taud-trueaud", - "name": "TrueAUD", - "symbol": "TAUD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lncx-luna-nusa-coin", - "name": "Luna Nusa Coin", - "symbol": "LNCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thkd-truehkd", - "name": "TrueHKD", - "symbol": "THKD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zttl-zettelkasten", - "name": "Zettelkasten", - "symbol": "ZTTL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bridge-multibridge", - "name": "MultiBridge", - "symbol": "BRIDGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "owl-owl-token", - "name": "OWL Token", - "symbol": "OWL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gcg-gulf-coin-gold", - "name": "GULF COIN GOLD", - "symbol": "GCG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tndr-thunder-swap", - "name": "Thunder Swap", - "symbol": "TNDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rain-rain-network", - "name": "RAIN Network", - "symbol": "RAIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etm-electromcoin", - "name": "Electromcoin", - "symbol": "ETM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fpt-finple", - "name": "FINPLE", - "symbol": "FPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ain-ainori", - "name": "AINORI", - "symbol": "AIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alc-alrightcoin", - "name": "AlrightCoin", - "symbol": "ALC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "duk-dukascoin", - "name": "Dukascoin", - "symbol": "DUK+", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "joon-joon", - "name": "JOON", - "symbol": "JOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "veth-vether", - "name": "Vether", - "symbol": "VETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bose-bosefinance", - "name": "Bose.Finance", - "symbol": "BOSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bast-bitcoin-association-slovenia-token", - "name": "Bitcoin association Slovenia Token", - "symbol": "BAST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agov-answer-governance", - "name": "Answer Governance", - "symbol": "AGOV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tfc-treasure-financial-coin", - "name": "Treasure Financial Coin", - "symbol": "TFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wtc-wheat-coin", - "name": "WHEAT COIN", - "symbol": "WTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smol-smol", - "name": "smol", - "symbol": "SMOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wdgld-wrapped-dgld", - "name": "Wrapped-DGLD", - "symbol": "WDGLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stt-scattercx", - "name": "Scatter.cx", - "symbol": "STT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ald-aludra-network", - "name": "Aludra Network", - "symbol": "ALD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bear-arcane-bear", - "name": "arcane bear", - "symbol": "BEAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cdsd-contraction-dynamic-set-dollar", - "name": "Contraction Dynamic Set Dollar", - "symbol": "CDSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "poocoin-poocoin", - "name": "PooCoin", - "symbol": "POOCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wav3-wav3", - "name": "WAV3", - "symbol": "WAV3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xmm-momentum", - "name": "Momentum", - "symbol": "XMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aer-aeryus", - "name": "Aeryus", - "symbol": "AER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogexnet-dogex-token", - "name": "Dogex Token", - "symbol": "DOGEXNET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blo-based-loans-ownership", - "name": "Based Loans Ownership", - "symbol": "BLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eauric-eauric", - "name": "Eauric", - "symbol": "EAURIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inflex-inflex-finance", - "name": "Inflex Finance", - "symbol": "INFLEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xgg-10xgg", - "name": "10x.gg", - "symbol": "XGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebrh-ebirah", - "name": "Ebirah", - "symbol": "EBRH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stack-stacker-ventures-token", - "name": "Stacker Ventures Token", - "symbol": "STACK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "raptor-raptor-finance", - "name": "Raptor Finance", - "symbol": "RAPTOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ykz-yakuza-dfo", - "name": "Yakuza DFO", - "symbol": "YKZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lol-loltoken", - "name": "LOLTOKEN", - "symbol": "LOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gct-gric-coin", - "name": "Gric Coin", - "symbol": "GCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scifi-scififinance", - "name": "Scifi.Finance", - "symbol": "SCIFI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jll-jllone", - "name": "Jllone", - "symbol": "JLL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ign-ignite", - "name": "Ignite", - "symbol": "IGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "maxgoat-maxgoat", - "name": "MaxGoat", - "symbol": "MAXGOAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sar-saren", - "name": "Saren", - "symbol": "SAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ambk-ambk", - "name": "AMBK", - "symbol": "AMBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nex-nexfin", - "name": "NexFin", - "symbol": "NEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sfn-safron", - "name": "Safron", - "symbol": "SFN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "emdc-emerald-coin", - "name": "Emerald Coin", - "symbol": "EMDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sapg-swaap-governance", - "name": "Swaap Governance", - "symbol": "SAPG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "topia-topia", - "name": "TOPIA", - "symbol": "TOPIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oil-crudeoil-finance", - "name": "Crudeoil Finance", - "symbol": "OIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boop-boopsy", - "name": "Boopsy", - "symbol": "BOOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbi-bigboys-industry", - "name": "BigBoys Industry", - "symbol": "BBI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lburst-loanburst", - "name": "LoanBurst", - "symbol": "LBURST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cxn-cxn-network", - "name": "CXN Network", - "symbol": "CXN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "noob-noobfinance", - "name": "noob.finance", - "symbol": "NOOB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aga-aga-token", - "name": "AGA Token", - "symbol": "AGA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sam-samurai", - "name": "Samurai", - "symbol": "SAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ypro-ypro-finance", - "name": "YPro Finance", - "symbol": "YPRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ynk-yoink", - "name": "Yoink", - "symbol": "YNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wsb-wallstreet-baby", - "name": "Wall Street baby", - "symbol": "WSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "based-based-money", - "name": "Based Money", - "symbol": "$BASED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "con-paycon-token", - "name": "Paycon Token", - "symbol": "CON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xst-xstable", - "name": "XStable", - "symbol": "XST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ahf-americanhorrorfinance", - "name": "AmericanHorror.Finance", - "symbol": "AHF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spmk-space-monkey", - "name": "Space Monkey", - "symbol": "SPMK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mer-meridaworld", - "name": "Meridaworld", - "symbol": "MER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgx-margix", - "name": "MargiX", - "symbol": "MGX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fed-fedora-gold", - "name": "Fedora Gold", - "symbol": "FED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btna-bitnart", - "name": "Bitnart", - "symbol": "BTNA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "govi-govi", - "name": "GOVI", - "symbol": "GOVI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sato-sato", - "name": "Sato", - "symbol": "SATO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fc-futurescoin", - "name": "FuturesCoin", - "symbol": "FC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "candy-skull-candy-shards", - "name": "Skull Candy Shards", - "symbol": "CANDY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "r2r-r2rcoin", - "name": "R2Rcoin", - "symbol": "R2R", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inbox-inbox-token", - "name": "INBOX TOKEN", - "symbol": "INBOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "husky-husky-token", - "name": "Husky Token", - "symbol": "Husky", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hac-hacash", - "name": "Hacash", - "symbol": "HAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xao-alloy-project", - "name": "Alloy Project", - "symbol": "XAO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "amx-armor-network", - "name": "Armor Network", - "symbol": "AMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mp3-mp3", - "name": "MP3", - "symbol": "MP3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "idea-idea-token", - "name": "IDEA Token", - "symbol": "IDEA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ea-ea-token", - "name": "EA Token", - "symbol": "EA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "n3rdz-n3rd-finance", - "name": "N3RD Finance", - "symbol": "N3RDZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hzt-black-diamond-rating", - "name": "Black Diamond Rating", - "symbol": "HZT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfdot-yearn-finance-dot", - "name": "Yearn Finance DOT", - "symbol": "YFDOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jbg-jbg-token", - "name": "JBG Token", - "symbol": "JBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stgz-stargaze-protocol", - "name": "Stargaze Protocol", - "symbol": "STGZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dec-dark-energy-crystals", - "name": "Dark Energy Crystals", - "symbol": "DEC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xipooh-winnie-xi-pooh", - "name": "Winnie Xi Pooh", - "symbol": "XIPOOH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "war-yieldwars", - "name": "YieldWars", - "symbol": "WAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xyx-burn-yield-burn", - "name": "Burn Yield Burn", - "symbol": "XYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sap-swaap-stablecoin", - "name": "Swaap Stablecoin", - "symbol": "SAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eiln-e-ilien", - "name": "e Ilien", - "symbol": "EILN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aqt-aqt-token", - "name": "AQT Token", - "symbol": "AQT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xot-okuru-token", - "name": "Okuru Token", - "symbol": "XOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hybn-hey-bitcoin", - "name": "HEY-BITCOIN", - "symbol": "HYBN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kpx-kapowx", - "name": "KapowX", - "symbol": "KPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sale-sale", - "name": "SALE", - "symbol": "SALE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitn-bitcoin-company-network", - "name": "BITCOIN \u0026 COMPANY NETWORK", - "symbol": "BITN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpc-cpcoin", - "name": "CPCoin", - "symbol": "CPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "uqb-ubiquitium-blockchain", - "name": "Ubiquitium Blockchain", - "symbol": "UQB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pfi-protocol-finance", - "name": "Protocol Finance", - "symbol": "PFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cc-cryptocart", - "name": "Cryptocart", - "symbol": "CC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcc-polar", - "name": "Polar", - "symbol": "MCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metric-metricexchange", - "name": "MetricExchange", - "symbol": "METRIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "quai-quai-dao", - "name": "QUAI DAO", - "symbol": "QUAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "autx-autradex", - "name": "Autradex", - "symbol": "AUTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rfiii-reflect-3", - "name": "Reflect 3", - "symbol": "RFIII", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xnode-xnode", - "name": "XNODE", - "symbol": "XNODE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "owl-owl", - "name": "OWL", - "symbol": "OWL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nbr2-nbr2", - "name": "NBR2", - "symbol": "NBR2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kuma-kuma-inu", - "name": "Kuma Inu", - "symbol": "KUMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ydoge-dogefarm-token", - "name": "Dogefarm Token", - "symbol": "yDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ginu-green-shiba-inu", - "name": "Green Shiba Inu", - "symbol": "GINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgt-dogetoken", - "name": "DogeToken", - "symbol": "DGT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitt-bittoken", - "name": "BITToken", - "symbol": "BITT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elx-energy-ledger", - "name": "Energy Ledger", - "symbol": "ELX", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "index-index-cooperative", - "name": "Index Cooperative", - "symbol": "INDEX", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rvmt-rivemont", - "name": "RiveMont", - "symbol": "RVMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrp-bf2-xrp-bep2", - "name": "XRP BEP2", - "symbol": "XRP-BF2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sst-simba-storage-token", - "name": "SIMBA Storage Token", - "symbol": "SST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tbtc-bitcoin", - "name": "τBitcoin", - "symbol": "TBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axn-axion", - "name": "AXION", - "symbol": "AXN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ycatt-catexfinance", - "name": "Catex.Finance", - "symbol": "YCATT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rigel-rigel-finance", - "name": "Rigel Finance", - "symbol": "RIGEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "carrot-carrotswap", - "name": "Carrotswap", - "symbol": "CARROT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vspy-sp-500-index-by-itovault", - "name": "S\u0026P 500 Index by ItoVault", - "symbol": "vSPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fr-freedom-reserve", - "name": "Freedom Reserve", - "symbol": "FR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pdx-pardox", - "name": "PardoX", - "symbol": "PDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swprl-swaprol", - "name": "Swaprol", - "symbol": "SWPRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cover-cover-protocol-governance-token", - "name": "Cover Protocol Governance Token", - "symbol": "COVER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pryz-pryz", - "name": "Pryz", - "symbol": "PRYZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fey-feyorra", - "name": "Feyorra", - "symbol": "FEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "curry-curry-swap", - "name": "CURRY SWAP", - "symbol": "CURRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hdp-hedpay", - "name": "HEdpAY", - "symbol": "HDP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usdap-bond-appetite-usd", - "name": "Bond Appetite USD", - "symbol": "USDAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gtr-golden-trillion-cash", - "name": "Golden Trillion Cash", - "symbol": "GTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bcp-piedao-balanced-crypto-pie", - "name": "PieDAO Balanced Crypto Pie", - "symbol": "BCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfig-yearn-finance-global", - "name": "Yearn Finance Global", - "symbol": "YFIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yop-yop", - "name": "YOP", - "symbol": "YOP", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mps-mt-pelerin-shares", - "name": "Mt Pelerin Shares", - "symbol": "MPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abg-abitcoingold-coin", - "name": "Abitcoingold coin", - "symbol": "ABG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rsc-rscoin", - "name": "Rscoin", - "symbol": "RSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fdo-firdaos", - "name": "Firdaos", - "symbol": "FDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pry-prophecy", - "name": "Prophecy", - "symbol": "PRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hxyf-hxyfinance", - "name": "hxy.finance", - "symbol": "HXYF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ygym-yfgym", - "name": "YFGYM", - "symbol": "YGYM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btshk-bitshark", - "name": "Bitshark", - "symbol": "BTSHK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wdf-wildfire", - "name": "Wildfire", - "symbol": "WDF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yfte-yftether", - "name": "YFTether", - "symbol": "YFTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blvr-believer", - "name": "Believer", - "symbol": "BLVR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfit-yearn-finance-token", - "name": "Yearn Finance Token", - "symbol": "YFIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hig-hig", - "name": "HIG+", - "symbol": "HIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "noon-nooncoin", - "name": "Nooncoin", - "symbol": "NOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mttcoin-money-of-tomorrow-today", - "name": "Money of Tomorrow, Today", - "symbol": "MTTCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rena-renascent-finance", - "name": "Renascent Finance", - "symbol": "RENA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gbtc-gulf-bits-coin", - "name": "Gulf Bits Coin", - "symbol": "GBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crt-cryptonits", - "name": "Cryptonits", - "symbol": "CRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mns-mnscoin", - "name": "MNSCOIN", - "symbol": "MNS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "musta-mustachecoin", - "name": "MustacheCoin", - "symbol": "MUSTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dec-dark-energy-crystal", - "name": "Dark Energy Crystal", - "symbol": "DEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "odin-odinbrowser", - "name": "OdinBrowser", - "symbol": "ODIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "300-spartan", - "name": "Spartan", - "symbol": "300", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zln-zillioncoin", - "name": "ZillionCoin", - "symbol": "ZLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dcg-daily-crypto-giveaways", - "name": "Daily Crypto Giveaways", - "symbol": "DCG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ode-ode", - "name": "ODE", - "symbol": "ODE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kch-keep-calm-and-hodl", - "name": "Keep Calm and Hodl", - "symbol": "KCH ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "box-defibox", - "name": "DefiBox", - "symbol": "BOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msc-miner-stake-coin", - "name": "Miner Stake Coin", - "symbol": "MSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yskf-yearn-shark-finance", - "name": "Yearn Shark Finance", - "symbol": "YSKF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vst-vscointraders", - "name": "VSCOINTRADERS", - "symbol": "VST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "emc-edumetrix-coin", - "name": "EduMetrix Coin", - "symbol": "EMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scc-science-chain", - "name": "Science Chain", - "symbol": "SCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnkr-bankroll-token", - "name": "Bankroll Token", - "symbol": "BNKR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tme-tama-egg-niftygotchi", - "name": "TAMA EGG NiftyGotchi", - "symbol": "TME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vybe-vybe", - "name": "Vybe", - "symbol": "VYBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dkkt-dkk-token", - "name": "DKK Token", - "symbol": "DKKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "etfx-etfx-finance", - "name": "ETFX Finance", - "symbol": "ETFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcha-bitcoin-cash-abc", - "name": "Bitcoin Cash ABC", - "symbol": "BCHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "deon-deoncash", - "name": "DeonCash", - "symbol": "DEON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wiz-wizard", - "name": "Wizard", - "symbol": "WIZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btllr-betller-coin", - "name": "Betller Coin", - "symbol": "BTLLR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xkr-kryptokrona", - "name": "Kryptokrona", - "symbol": "XKR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "oft-orient", - "name": "Orient", - "symbol": "OFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cnz-coinzo-token", - "name": "Coinzo Token", - "symbol": "CNZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rai-raicoin", - "name": "Raicoin", - "symbol": "RAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "beer-beer", - "name": "BEER", - "symbol": "BEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "upay-upayment", - "name": "Upayment", - "symbol": "UPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spkl-spoklottery", - "name": "SpokLottery", - "symbol": "SPKL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfd-yfdfifinance", - "name": "YfDFI.Finance", - "symbol": "YFD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "leo-leofinance", - "name": "Leofinance", - "symbol": "LEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gfnc-grafenocoin", - "name": "GrafenoCoin", - "symbol": "GFNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "testa-testa", - "name": "Testa", - "symbol": "TESTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcr-tecracoin", - "name": "TecraCoin", - "symbol": "TCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ethv-ethereum-vault", - "name": "Ethereum Vault", - "symbol": "ETHV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shroud-shroud-x", - "name": "Shroud X", - "symbol": "SHROUD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "whole-wormholefinance", - "name": "Wormhole.finance", - "symbol": "WHOLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nebo-csp-dao", - "name": "CSP DAO", - "symbol": "NEBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bucc-buccaneer-v2", - "name": "Buccaneer V2", - "symbol": "BUCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eux-coinquista-eur-stable-token", - "name": "Coinquista EUR Stable Token", - "symbol": "EUX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ltv-leader-tv-coin", - "name": "LeadER TV Coin", - "symbol": "LTV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "koko-kokoswap", - "name": "KokoSwap", - "symbol": "KOKO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfi-bitdefi", - "name": "BitDEFi", - "symbol": "BFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eoc-everyonescrypto", - "name": "Everyones crypto", - "symbol": "EOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arts-art-studio", - "name": "Art Studio", - "symbol": "ARTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tbf-the-bright-future", - "name": "The Bright Future", - "symbol": "TBF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zin-zin-finance", - "name": "Zin Finance", - "symbol": "ZIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zefu-zenfuse", - "name": "Zenfuse", - "symbol": "ZEFU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boob-boobanker", - "name": "BooBanker", - "symbol": "BOOB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lex-elxis", - "name": "Elxis", - "symbol": "LEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cvp-cvp-token", - "name": "CVP Token", - "symbol": "CVP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uenc-universalenergychain", - "name": "UniversalEnergyChain", - "symbol": "UENC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tex-tychexchange", - "name": "TychExchange", - "symbol": "TEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gwp-green-world-project-token", - "name": "Green World Project Token", - "symbol": "GWP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zyon-bitzyon", - "name": "BitZyon", - "symbol": "ZYON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tcore-tornado-core", - "name": "Tornado Core", - "symbol": "TCORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "love-love-coin", - "name": "LOVE Coin", - "symbol": "LOVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zfi-zfinance", - "name": "ZFinance", - "symbol": "ZFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lxag-lunesxag", - "name": "LunesXAG", - "symbol": "LXAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfimx-max", - "name": "MAX", - "symbol": "YFIMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "erex-erexcoin", - "name": "ErexCoin", - "symbol": "EREX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cbrl-cryptobrl", - "name": "CryptoBRL", - "symbol": "CBRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ymf20-yearn20moon", - "name": "Yearn20Moon", - "symbol": "YMF20", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plx-coinquista-pln-stable-token", - "name": "Coinquista PLN Stable Token", - "symbol": "PLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "azzr-azzure", - "name": "Azzure", - "symbol": "AZZR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vps-vipo-vps-solutions", - "name": "Vipo Vps Solutions", - "symbol": "VPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ton-ton-crystal", - "name": "TON Crystal", - "symbol": "TON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lead-lead", - "name": "Lead", - "symbol": "LEAD", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "idf-islamic-digital-finance", - "name": "Islamic Digital Finance", - "symbol": "IDF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfdt-yearn-finance-diamond", - "name": "Yearn Finance Diamond", - "symbol": "YFDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zuka-zukacoin", - "name": "Zukacoin", - "symbol": "ZUKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ipm-timers", - "name": "TIMERS", - "symbol": "IPM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "psi-passive-income", - "name": "Passive Income", - "symbol": "PSI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zzz-zzz-finance", - "name": "ZZZ Finance", - "symbol": "ZZZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "capy-capy", - "name": "Capy", - "symbol": "CAPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bwf-beowulf", - "name": "Beowulf", - "symbol": "BWF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "firm-firemoon", - "name": "Firemoon", - "symbol": "FIRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "foxy-foxyfi", - "name": "FoxYFI", - "symbol": "FOXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cc-climacoin", - "name": "ClimaCoin", - "symbol": "CC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "svin-saving", - "name": "Saving", - "symbol": "SVIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tct-tycoon-global", - "name": "Tycoon Global", - "symbol": "TCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shliapa-shliapa", - "name": "SHLIAPA", - "symbol": "SHLIAPA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vamp-vampire-protocol", - "name": "Vampire Protocol", - "symbol": "VAMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfib-yfibusiness", - "name": "YFIBusiness", - "symbol": "YFIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pjm-pajamafinance", - "name": "Pajama.Finance", - "symbol": "PJM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zac-zac-finance", - "name": "ZAC Finance", - "symbol": "ZAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fud-fudfinance", - "name": "FUD.finance", - "symbol": "FUD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scv-super-coinview-token", - "name": "Super CoinView Token", - "symbol": "SCV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brwn-browncoin", - "name": "Browncoin", - "symbol": "BRWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wleo-leofinance-token", - "name": "LeoFinance Token", - "symbol": "WLEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yy-yy-inc", - "name": "YY Inc.", - "symbol": "YY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xpo-xpo-logistics", - "name": "XPO Logistics", - "symbol": "XPO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xlnx-xilinx-inc", - "name": "Xilinx, Inc.", - "symbol": "XLNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xec-cimarex-energy", - "name": "Cimarex Energy", - "symbol": "XEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wll-whiting-petroleum", - "name": "Whiting Petroleum", - "symbol": "WLL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "w-wayfair-cl-a", - "name": "Wayfair Cl A", - "symbol": "W", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uaa-under-armour-cl-a", - "name": "Under Armour Cl A", - "symbol": "UAA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "txt-textron", - "name": "Textron", - "symbol": "TXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttwo-take-two-interactive-software-inc", - "name": "Take-Two Interactive Software, Inc.", - "symbol": "TTWO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trip-tripadvisor-inc", - "name": "TripAdvisor, Inc.", - "symbol": "TRIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tri-thomson-reuters", - "name": "Thomson Reuters", - "symbol": "TRI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tmus-t-mobile-us-inc", - "name": "T-Mobile US, Inc.", - "symbol": "TMUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wolfy-wolfy", - "name": "WOLFY", - "symbol": "WOLFY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tfx-teleflex", - "name": "Teleflex", - "symbol": "TFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sepa-secure-pad", - "name": "Secure Pad", - "symbol": "SEPA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgcl-digicol-token", - "name": "DigiCol Token", - "symbol": "DGCL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "noahark-noahs-ark", - "name": "Noah's Ark", - "symbol": "NOAHARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "team-atlassian-corporation-plc", - "name": "Atlassian Corporation Plc", - "symbol": "TEAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "syy-sysco", - "name": "Sysco", - "symbol": "SYY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swks-skyworks-solutions-inc", - "name": "Skyworks Solutions, Inc.", - "symbol": "SWKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stm-stmicroelectronics-adr", - "name": "Stmicroelectronics Adr", - "symbol": "STM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stld-steel-dynamics-inc", - "name": "Steel Dynamics, Inc.", - "symbol": "STLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "srpt-sarepta-therapeutics-inc", - "name": "Sarepta Therapeutics, Inc.", - "symbol": "SRPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spn-superi-ener-svcs", - "name": "Superi Ener Svcs", - "symbol": "SPN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sohu-sohu-com-limited", - "name": "Sohu.com Limited", - "symbol": "SOHU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sne-sony-corporation", - "name": "Sony Corporation", - "symbol": "SNE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smg-the-scotts-miracle-gro-company", - "name": "The Scotts Miracle-Gro Company", - "symbol": "SMG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slca-us-silica-holdings", - "name": "Us Silica Holdings", - "symbol": "SLCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sig-signet-jewelers", - "name": "Signet Jewelers", - "symbol": "SIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sesg-ses-fdr", - "name": "Ses Fdr", - "symbol": "SESG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sage-sage-therapeutics-inc", - "name": "Sage Therapeutics, Inc.", - "symbol": "SAGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsbt-bit-storage-box-token", - "name": "Bit Storage Box Token", - "symbol": "BSBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "roku-roku-inc", - "name": "Roku, Inc.", - "symbol": "ROKU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rno-renault-par", - "name": "Renault Par", - "symbol": "RNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rmd-resmed", - "name": "Resmed", - "symbol": "RMD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rh-rh", - "name": "Rh", - "symbol": "RH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "r-ryder-system", - "name": "Ryder System", - "symbol": "R", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qrvo-qorvo-inc", - "name": "Qorvo, Inc.", - "symbol": "QRVO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gmc-gokumarket-credit-12345", - "name": "GokuMarket Credit du[plikat", - "symbol": "GMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qcom-qualcomm-inc", - "name": "QUALCOMM Inc", - "symbol": "QCOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cad-candy-protocol", - "name": "Candy Protocol", - "symbol": "CAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pten-patterson-uti-energy-inc", - "name": "Patterson-UTI Energy, Inc.", - "symbol": "PTEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pnc-pnc-financial-services-group", - "name": "PNC Financial Services Group", - "symbol": "PNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pep-pepsico", - "name": "Pepsico", - "symbol": "PEP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pbyi-puma-biotechnology-inc", - "name": "Puma Biotechnology, Inc.", - "symbol": "PBYI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pbf-pbf-energy-cl-a", - "name": "Pbf Energy Cl A", - "symbol": "PBF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "payc-paycom-software-inc", - "name": "Paycom Software, Inc.", - "symbol": "PAYC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ostk-overstock-com-inc", - "name": "Overstock.com, Inc.", - "symbol": "OSTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oas-oasis-petroleum", - "name": "Oasis Petroleum", - "symbol": "OAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "now-servicenow", - "name": "ServiceNow", - "symbol": "NOW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nktr-nektar-therapeutics", - "name": "Nektar Therapeutics", - "symbol": "NKTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nee-nextera-energy", - "name": "Nextera Energy", - "symbol": "NEE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fzy-frenzy", - "name": "Frenzy", - "symbol": "FZY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nclh-norwegian-cruise-line-holdings-ltd", - "name": "Norwegian Cruise Line Holdings Ltd.", - "symbol": "NCLH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtch-match-group-inc", - "name": "Match Group, Inc.", - "symbol": "MTCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrvl-marvell-technology-group-ltd", - "name": "Marvell Technology Group Ltd.", - "symbol": "MRVL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mnk-mallinckrodt", - "name": "Mallinckrodt", - "symbol": "MNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgm-mgm-resorts-international", - "name": "MGM Resorts International", - "symbol": "MGM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mchp-microchip-technology-incorporated", - "name": "Microchip Technology Incorporated", - "symbol": "MCHP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mc-lvmh-moet-hennessy-louis-vuitton-se", - "name": "Lvmh Moet Hennessy Louis Vuitton Se", - "symbol": "MC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mac-macerich", - "name": "Macerich", - "symbol": "MAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "paper-paper", - "name": "Paper", - "symbol": "PAPER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lyft-lyft", - "name": "LYFT", - "symbol": "LYFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pt-predict", - "name": "Predict", - "symbol": "PT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lx-lexinfintech-holdings-ltd", - "name": "LexinFintech Holdings Ltd.", - "symbol": "LX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "k-kellogg", - "name": "Kellogg", - "symbol": "K", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "isrg-intuitive-surgical-inc", - "name": "Intuitive Surgical, Inc.", - "symbol": "ISRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "irbt-irobot-corporation", - "name": "iRobot Corporation", - "symbol": "IRBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ipn-ipsen", - "name": "Ipsen", - "symbol": "IPN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ip-international-paper", - "name": "International Paper", - "symbol": "IP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "incy-incyte-corporation", - "name": "Incyte Corporation", - "symbol": "INCY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "icpt-intercept-pharmaceuticals-inc", - "name": "Intercept Pharmaceuticals, Inc.", - "symbol": "ICPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hmi-huami-corporation", - "name": "Huami Corporation", - "symbol": "HMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfive-yfive-finance", - "name": "YFIVE FINANCE", - "symbol": "YFIVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttt-tapcoin", - "name": "Tapcoin", - "symbol": "TTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gwph-gw-pharmaceuticals-plc", - "name": "GW Pharmaceuticals PLC", - "symbol": "GWPH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gt-the-goodyear-tire-rubber-company", - "name": "The Goodyear Tire \u0026 Rubber Company", - "symbol": "GT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grub-grubhub", - "name": "Grubhub", - "symbol": "GRUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grmn-garmin-ltd", - "name": "Garmin Ltd.", - "symbol": "GRMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gps-the-gap-inc", - "name": "The Gap, Inc.", - "symbol": "GPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "glpg-galapagos-nv", - "name": "Galapagos NV", - "symbol": "GLPG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gd-general-dynamics", - "name": "General Dynamics", - "symbol": "GD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fiv1-five-below-inc", - "name": "Five Below, Inc.", - "symbol": "FIV1", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fdx-fedex", - "name": "Fedex", - "symbol": "FDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "expe-expedia-inc", - "name": "Expedia Inc", - "symbol": "EXPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exel-exelixis-inc", - "name": "Exelixis, Inc.", - "symbol": "EXEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exas-exact-sciences-corporation", - "name": "Exact Sciences Corporation", - "symbol": "EXAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etl-eutelsat-communications", - "name": "Eutelsat Communications", - "symbol": "ETL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "emn-eastman-chemical", - "name": "Eastman Chemical", - "symbol": "EMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dwdp-dowdupont-inc", - "name": "DowDuPont Inc.", - "symbol": "DWDP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dltr-dollar-tree-inc", - "name": "Dollar Tree, Inc.", - "symbol": "DLTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dlph-delphi-automotive", - "name": "Delphi Automotive", - "symbol": "DLPH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dhr-danaher", - "name": "Danaher", - "symbol": "DHR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chc-chunghoptoken", - "name": "ChunghopToken", - "symbol": "CHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dhg-delta-air-lines", - "name": "Delta Air Lines", - "symbol": "DHG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dg-dollar-general", - "name": "Dollar General", - "symbol": "DG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "de-deere", - "name": "Deere", - "symbol": "DE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cvs-cvs-health", - "name": "CVS Health", - "symbol": "CVS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctlt-catalent-inc", - "name": "Catalent Inc", - "symbol": "CTLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cprt-copart-inc", - "name": "Copart, Inc.", - "symbol": "CPRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpb-campbell-soup", - "name": "Campbell Soup", - "symbol": "CPB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "comm-commscope-holding-company-inc", - "name": "CommScope Holding Company, Inc.", - "symbol": "COMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cnx-consol-energy", - "name": "Consol Energy", - "symbol": "CNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cag-conagra-brands-inc", - "name": "Conagra Brands Inc", - "symbol": "CAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsx-boston-scientific", - "name": "Boston Scientific", - "symbol": "BSX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "big-big-lots", - "name": "Big Lots", - "symbol": "BIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ar-antero-resources", - "name": "Antero Resources", - "symbol": "AR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apam-aperam-s-a", - "name": "Aperam S.A.", - "symbol": "APAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "an-autonation", - "name": "Autonation", - "symbol": "AN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chads-chads-vc", - "name": "CHADS VC", - "symbol": "CHADS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amun-amundi", - "name": "Amundi", - "symbol": "AMUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alxn-alexion-pharmaceuticals-inc", - "name": "Alexion Pharmaceuticals, Inc.", - "symbol": "ALXN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ally-ally-financial", - "name": "Ally Financial", - "symbol": "ALLY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "air-airbus-se", - "name": "Airbus SE", - "symbol": "AIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agn-allergan", - "name": "Allergan", - "symbol": "AGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "af-air-france-klm", - "name": "Air France-Klm", - "symbol": "AF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcat20-wrapped-moon-cats", - "name": "Wrapped Moon Cats", - "symbol": "MCAT20", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aem-agnico-eagle", - "name": "Agnico Eagle", - "symbol": "AEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adnt-adient", - "name": "Adient", - "symbol": "ADNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1cov-covestro-ag", - "name": "Covestro AG", - "symbol": "1COV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xlv-health-care-select-sector-spdr-fund", - "name": "Health Care Select Sector SPDR Fund", - "symbol": "XLV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "work-slack-technologies-inc", - "name": "Slack Technologies Inc", - "symbol": "WORK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bacon-baconswap", - "name": "BaconSwap", - "symbol": "BACON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wday-workday-inc", - "name": "Workday Inc", - "symbol": "WDAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wba-walgreen-boots-alliance", - "name": "Walgreen Boots Alliance", - "symbol": "WBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vow3-volkswagen-ag", - "name": "Volkswagen AG", - "symbol": "VOW3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fame-saint-fame-genesis-shirt", - "name": "SAINT FAME: Genesis Shirt", - "symbol": "FAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnc-bnoincoin", - "name": "Bnoincoin", - "symbol": "BNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boo-spookyswap", - "name": "Spookyswap", - "symbol": "BOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vgt-vanguard-information-technology-etf", - "name": "Vanguard Information Technology ETF", - "symbol": "VGT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ter-teradyne", - "name": "Teradyne", - "symbol": "TER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "soak-soak-token", - "name": "Soak Token", - "symbol": "SOAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sym-symantec-corp", - "name": "Symantec Corp", - "symbol": "SYM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swn-southwestern-energy", - "name": "Southwestern Energy", - "symbol": "SWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "2based-2based-finance", - "name": "2Based Finance", - "symbol": "2BASED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mw-mirror-world-token", - "name": "Mirror World Token", - "symbol": "MW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dragon-dragon-ball", - "name": "Dragon Ball", - "symbol": "DRAGON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shop-shopify-cl-a-sub-vtg", - "name": "Shopify Cl A Sub Vtg", - "symbol": "SHOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "roy-royal-protocol", - "name": "Royal Protocol", - "symbol": "ROY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dfsocial-defisocial", - "name": "DefiSocial", - "symbol": "DFSOCIAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chiefs-kansas-city-chiefs-win-super-bowl", - "name": "Kansas City Chiefs Win Super Bowl", - "symbol": "CHIEFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mkb-maker-basic", - "name": "Maker Basic", - "symbol": "MKB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "s-sprint", - "name": "Sprint", - "symbol": "S", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orcl-oracle", - "name": "Oracle", - "symbol": "ORCL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bloody-bloody-token", - "name": "Bloody Token", - "symbol": "BLOODY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onem-1life-healthcare-inc", - "name": "1Life Healthcare Inc", - "symbol": "ONEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rug-rug", - "name": "Rug", - "symbol": "RUG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "net-cloudflare-inc", - "name": "Cloudflare Inc.", - "symbol": "NET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nem-newmont-mining", - "name": "Newmont Mining", - "symbol": "NEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mj-etfmg-alternative-harvest-etf", - "name": "ETFMG Alternative Harvest ETF", - "symbol": "MJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcd-mcdonald-s", - "name": "McDonald's", - "symbol": "MCD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ma-mastercard-inc", - "name": "Mastercard Inc", - "symbol": "MA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lha-deutsche-lufthansa-ag", - "name": "Deutsche Lufthansa AG", - "symbol": "LHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lb-l-brands", - "name": "L Brands", - "symbol": "LB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jpm-jpmorgan-chase", - "name": "JPMorgan Chase", - "symbol": "JPM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jcp-jc-penney", - "name": "Jc Penney", - "symbol": "JCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ifx-infineon-technologies-ag", - "name": "Infineon Technologies AG", - "symbol": "IFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gpro-gopro-inc", - "name": "GoPro Inc", - "symbol": "GPRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ge-general-electric-co", - "name": "General Electric Co", - "symbol": "GE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fvrr-fiverr-international-ltd", - "name": "Fiverr International Ltd", - "symbol": "FVRR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ftxr-first-trust-nasdaq-transportation-etf", - "name": "First Trust Nasdaq Transportation ETF", - "symbol": "FTXR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fcr-fromm-car", - "name": "Fromm Car", - "symbol": "FCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ftch-farfetch-ltd", - "name": "Farfetch Ltd", - "symbol": "FTCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fslr-first-solar-inc", - "name": "First Solar Inc", - "symbol": "FSLR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ddog-datadog-inc", - "name": "Datadog Inc", - "symbol": "DDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dbx-dropbox", - "name": "DropBox", - "symbol": "DBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dbk-deutsche-bank-ag", - "name": "Deutsche Bank AG", - "symbol": "DBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pylon-pylon-finance", - "name": "Pylon Finance", - "symbol": "PYLON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chk-chesapeake-energy-corp", - "name": "Chesapeake Energy Corp", - "symbol": "CHK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbk-commerzbank-ag", - "name": "Commerzbank AG", - "symbol": "CBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "c-citigroup-inc", - "name": "Citigroup Inc", - "symbol": "C", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bmw-bmw-ag", - "name": "BMW AG", - "symbol": "BMW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bill-bill-com-inc", - "name": "Bill.com Inc", - "symbol": "BILL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mora-meliora", - "name": "Meliora", - "symbol": "MORA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bby-best-buy", - "name": "Best Buy", - "symbol": "BBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ads-adidas-ag", - "name": "Adidas AG", - "symbol": "ADS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adpt-adaptive-biotechnologies-corporation", - "name": "Adaptive Biotechnologies Corporation", - "symbol": "ADPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prvs-previse", - "name": "Previse", - "symbol": "PRVS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abbv-abbvie", - "name": "Abbvie", - "symbol": "ABBV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cops-cops-finance", - "name": "Cops Finance", - "symbol": "COPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aap-advance-auto-parts", - "name": "Advance Auto Parts", - "symbol": "AAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1211-byd-company-limited", - "name": "BYD Company Limited", - "symbol": "1211", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "0522-asm-pacific-technology-limited", - "name": "ASM Pacific Technology Limited", - "symbol": "0522", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbc-casino-betting-coin", - "name": "Casino Betting Coin", - "symbol": "CBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qub-qub-coin", - "name": "Qub Coin", - "symbol": "QUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "easy-easyfi", - "name": "EasyFi", - "symbol": "EASY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stak-jigstack", - "name": "Jigstack", - "symbol": "STAK", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "whale-whale", - "name": "WHALE", - "symbol": "WHALE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dhc-deltahub-community", - "name": "DeltaHub Community", - "symbol": "DHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trib-contribute", - "name": "Contribute", - "symbol": "TRIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jvz-jiviz", - "name": "Jiviz", - "symbol": "JVZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mpt-money-plant-token", - "name": "Money Plant Token", - "symbol": "MPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "laq-laqpay-coin", - "name": "LaqPay coin", - "symbol": "LAQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cana-cannabis-seed-token", - "name": "Cannabis Seed Token", - "symbol": "CANA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hithot-hithot", - "name": "HITHOT", - "symbol": "HITHOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tsv-tronsv", - "name": "TronSV", - "symbol": "TSV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crvt-crypto-revolution", - "name": "Crypto Revolution", - "symbol": "CRVT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rize-rize", - "name": "RIZE", - "symbol": "RIZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kbi-kuberbit", - "name": "Kuberbit", - "symbol": "KBI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sss-simple-software-solutions", - "name": "Simple Software Solutions", - "symbol": "SSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "unifi-unifi-defi", - "name": "UNIFI DeFi", - "symbol": "UNIFI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfiig-yfii-gold", - "name": "YFII Gold", - "symbol": "YFIIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "p2p-p2p-cash", - "name": "P2P Cash", - "symbol": "P2P", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qoob-qoober", - "name": "QOOBER", - "symbol": "QOOB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xp-expose-protocol", - "name": "Expose Protocol", - "symbol": "XP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chonk-chonk", - "name": "Chonk", - "symbol": "CHONK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "speed-speed-coin", - "name": "Speed Coin", - "symbol": "SPEED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hxy-hxy-money", - "name": "HXY Money", - "symbol": "HXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "opt-openpreedict", - "name": "OpenPreedict", - "symbol": "OPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ppb-purpleburn", - "name": "Purpleburn", - "symbol": "PPB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coin-coinverterinfo", - "name": "Coinverter info", - "symbol": "COIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "naz-naz-coin", - "name": "Naz Coin", - "symbol": "NAZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cat-cat-token", - "name": "Cat Token", - "symbol": "CAT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "yfi2-yearn2finance", - "name": "Yearn2.Finance", - "symbol": "YFI2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mdx-mandala-exchange-token", - "name": "Mandala Exchange Token", - "symbol": "MDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obee-obee-network", - "name": "Obee Network", - "symbol": "OBEE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bgtt-baguette-token", - "name": "Baguette Token", - "symbol": "BGTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "potus-presidentfinance", - "name": "President.Finance", - "symbol": "POTUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtcn-multicoin", - "name": "Multicoin", - "symbol": "MTCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dexg-dextoken-governance", - "name": "DexToken Governance", - "symbol": "DEXG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "house-toastfinance", - "name": "Toast.finance", - "symbol": "HOUSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yamv2-yamv2", - "name": "YAMv2", - "symbol": "YAMV2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dice-dicefinance", - "name": "Dice.finance", - "symbol": "DICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfx-bitcoinflex", - "name": "BitcoinFlex", - "symbol": "BFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bid-bidao", - "name": "Bidao", - "symbol": "BID", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "bull-bull", - "name": "BULL", - "symbol": "BULL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hxg-hxg", - "name": "HXG", - "symbol": "HXG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tiktok-tiktok-coin", - "name": "TIKTOK COIN", - "symbol": "TIKTOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cubic-cubic", - "name": "Cubic", - "symbol": "CUBIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "buhund-buhundcoin", - "name": "Buhundcoin", - "symbol": "BUHUND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xt-xt-token", - "name": "XT Token", - "symbol": "XT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yt-cherry-token", - "name": "Cherry Token", - "symbol": "YT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonday-moonday-finance", - "name": "Moonday Finance", - "symbol": "MOONDAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uwl-uniwhales", - "name": "UniWhales", - "symbol": "UWL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "isla-insula", - "name": "Insula", - "symbol": "ISLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ztnz-ztranzit-coin", - "name": "Ztranzit Coin", - "symbol": "ZTNZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thpt-heliopower-token", - "name": "Heliopower Token", - "symbol": "THPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defo-defhold", - "name": "DefHold", - "symbol": "DEFO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coil-coil", - "name": "COIL", - "symbol": "COIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grano-grano-protocol", - "name": "Grano Protocol", - "symbol": "GRANO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gthr-gatherer", - "name": "Gatherer", - "symbol": "GTHR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inex-internet-exchange-token", - "name": "Internet Exchange Token", - "symbol": "INEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oks-oikos", - "name": "Oikos", - "symbol": "OKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spid-spider-finance", - "name": "Spider Finance", - "symbol": "SPID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbtc-soft-bitcoin", - "name": "Soft Bitcoin", - "symbol": "SBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bot-bounce-token", - "name": "Bounce Token", - "symbol": "BOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gear-bitgear", - "name": "Bitgear", - "symbol": "GEAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plt-plutus-defi", - "name": "Plutus DeFi", - "symbol": "PLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mph-chasyr-token", - "name": "Chasyr Token", - "symbol": "MPH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eco-ormeus-ecosystem", - "name": "Ormeus Ecosystem", - "symbol": "ECO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ruze-ruze-finance", - "name": "Ruze Finance", - "symbol": "RUZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fxl-fxl-token", - "name": "FXL Token", - "symbol": "FXL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dfio-defi-omega", - "name": "DeFi Omega", - "symbol": "DFIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bullion-bullionpay", - "name": "BullionPAY", - "symbol": "BULLION", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xtk-xtake", - "name": "Xtake", - "symbol": "XTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amal-amal", - "name": "AMAL", - "symbol": "AMAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gau-gauntlet", - "name": "Gauntlet", - "symbol": "GAU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dg-decentral-games", - "name": "Decentral Games", - "symbol": "DG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pblc-politicoin", - "name": "Politicoin", - "symbol": "PBLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orkit-orkit-coin", - "name": "Orkit Coin", - "symbol": "ORKIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "one-one-hundred-coin", - "name": "One Hundred Coin", - "symbol": "ONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vcc-veterans-compensation-coin", - "name": "Veterans Compensation Coin", - "symbol": "VCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "halv-halving-coin", - "name": "Halving Coin", - "symbol": "HALV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nds-nodeseeds", - "name": "Nodeseeds", - "symbol": "NDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tewken-tewken", - "name": "TEWKEN", - "symbol": "TEWKEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fire-fireball", - "name": "Fireball", - "symbol": "FIRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ubomb-unibomb", - "name": "Unibomb", - "symbol": "UBOMB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fch-fanticos-cash", - "name": "Fanáticos Cash", - "symbol": "FCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pamp-pamp-network", - "name": "PAMP Network", - "symbol": "PAMP", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tat-tatcoin", - "name": "Tatcoin", - "symbol": "TAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axt-alliance-x-trading", - "name": "Alliance X Trading", - "symbol": "AXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btrm-betrium", - "name": "Betrium", - "symbol": "BTRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yfmb-yfmoonbeam", - "name": "YFMoonBeam", - "symbol": "YFMB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hgc-higamecoin", - "name": "HiGameCoin", - "symbol": "HGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "itdr-itrader-today", - "name": "iTrader Today", - "symbol": "iTDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blast-blastoff", - "name": "Blastoff", - "symbol": "BLAST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdesc-descount", - "name": "DESCOUNT", - "symbol": "bDESC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spyon-spyoncrypto", - "name": "SpyOnCrypto", - "symbol": "SPYON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stamp-safepost", - "name": "SafePost", - "symbol": "STAMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdcc-bitica-coin", - "name": "BITICA COIN", - "symbol": "BDCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tpwr-thepowercoin", - "name": "ThePowerCoin", - "symbol": "TPWR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "needyex-needyex", - "name": "Needyex", - "symbol": "NEEDYEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abst-abitshadow-token", - "name": "Abitshadow Token", - "symbol": "ABST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zyx-zyx", - "name": "ZYX", - "symbol": "ZYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pusd-payfrequent-usd", - "name": "PayFrequent USD", - "symbol": "PUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "888-888token", - "name": "888token", - "symbol": "888", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sadge-sadge", - "name": "Sadge", - "symbol": "SADGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tschybrid-tron-secure-hybrid", - "name": "Tron Secure Hybrid", - "symbol": "TSCHYBRID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swo-spacewolf", - "name": "Spacewolf", - "symbol": "SWO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ski-skicoin", - "name": "Skicoin", - "symbol": "SKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dogem-moon-doge", - "name": "Moon Doge", - "symbol": "DOGEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgsc-mg-skype-coin", - "name": "MG SKYPE COIN", - "symbol": "MGSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcp-bcpcoin", - "name": "BCPCoin", - "symbol": "BCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "usde-dollar-electrino", - "name": "Dollar Electrino", - "symbol": "USDE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atm-africa-to-mars", - "name": "Africa To Mars", - "symbol": "ATM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgmt-digimax", - "name": "DigiMax", - "symbol": "DGMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lbrl-lbrl", - "name": "LBRL", - "symbol": "LBRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chai-chai", - "name": "Chai", - "symbol": "CHAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bchc-bitcherry", - "name": "BitCherry", - "symbol": "BCHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pamm-pammcoin", - "name": "PAMMCOIN", - "symbol": "PAMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dext-dextools", - "name": "DexTools", - "symbol": "DEXT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pwrb-powerbalt", - "name": "PowerBalt", - "symbol": "PWRB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ach-acash", - "name": "Acash", - "symbol": "ACH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kub-kublaicoin", - "name": "Kublaicoin", - "symbol": "KUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sfcx-safecrex", - "name": "Safecrex", - "symbol": "SFCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cmc-coin-cine-media-celebrity-coin", - "name": "Cine Media Celebrity Coin", - "symbol": "CMC COIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dam-datamine", - "name": "Datamine", - "symbol": "DAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "afcash-africunia-bank", - "name": "AFRICUNIA BANK", - "symbol": "AFCASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssc-shining-star-coin", - "name": "Shining Star Coin", - "symbol": "SSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lucky-luckybit", - "name": "LuckyBit", - "symbol": "LUCKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "akc-akikcoin", - "name": "Akikcoin", - "symbol": "AKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wllo-willowcoin", - "name": "WillowCoin", - "symbol": "WLLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "minr-minerium-coin", - "name": "Minerium Coin", - "symbol": "MINR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fts-fts-coin", - "name": "FTS Coin", - "symbol": "FTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ank-apple-network", - "name": "Apple Network", - "symbol": "ANK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nole-nolecoin", - "name": "NoleCoin", - "symbol": "NOLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plaas-plaas-farmers-token", - "name": "PLAAS FARMERS TOKEN", - "symbol": "PLAAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nokn-nokencoin", - "name": "Nokencoin", - "symbol": "NOKN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lava-lava", - "name": "Lava", - "symbol": "LAVA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bzrx-bzx-protocol", - "name": "bZx Protocol", - "symbol": "BZRX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "power-unipower", - "name": "UniPower", - "symbol": "POWER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jpx-japan-excitement-coin", - "name": "Japan Excitement Coin", - "symbol": "JPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iown-iown-token", - "name": "iOWN Token", - "symbol": "iOWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "news-news24", - "name": "News24", - "symbol": "NEWS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "acxt-acdx-exchange-token", - "name": "ACDX Exchange Token", - "symbol": "ACXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yfr-youforia", - "name": "youForia", - "symbol": "YFR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mima-kyccrypto", - "name": "KYC.Crypto", - "symbol": "MIMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btc3-bitcoin-3", - "name": "Bitcoin 3", - "symbol": "BTC3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bytn-bytn", - "name": "BYTN", - "symbol": "BYTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kyf-kryptofranc", - "name": "Kryptofranc", - "symbol": "KYF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vpp-virtue-poker-points", - "name": "Virtue Poker Points", - "symbol": "VPP", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "h3x-h3x", - "name": "H3X", - "symbol": "H3X", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adctoken-amazing-digital-crypto", - "name": "AMAZING DIGITAL CRYPTO", - "symbol": "ADCTOKEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldr-leduro", - "name": "Leduro", - "symbol": "LDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "agrd-aguard", - "name": "AGUARD", - "symbol": "AGRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cro-cronos-coin", - "name": "Cronos Coin", - "symbol": "CRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "keanu-keanu-inu", - "name": "Keanu Inu", - "symbol": "KEANU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dax-dax-30-token", - "name": "DAX 30 Token", - "symbol": "DAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mhlx-helixnetwork", - "name": "HelixNetwork", - "symbol": "mHLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lina-linear-bsc", - "name": "Linear (BSC)", - "symbol": "LINA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spero-sperocoin", - "name": "SperoCoin", - "symbol": "SPERO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dmsc-diamond-star-coin", - "name": "Diamond Star Coin", - "symbol": "DMSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dapp-dapp", - "name": "DAPP", - "symbol": "DAPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vrtn-vinyl-records-token", - "name": "VINYL RECORDS TOKEN", - "symbol": "VRTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grt-golden-ratio-token", - "name": "Golden Ratio Token", - "symbol": "GRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alk-alk-student-coin", - "name": "ALK Student Coin", - "symbol": "ALK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xxa-ixinium", - "name": "Ixinium", - "symbol": "XXA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbd-cbd-token", - "name": "CBD Token", - "symbol": "CBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ag8-atromg8", - "name": "AtromG8", - "symbol": "AG8", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cvd-covid19", - "name": "Covid19", - "symbol": "CVD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xt-extstock-token", - "name": "ExtStock Token", - "symbol": "XT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bpt-bitpayer-token", - "name": "Bitpayer Token", - "symbol": "BPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "news-newstokens", - "name": "NewsTokens", - "symbol": "NEWS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hroi-high-roi", - "name": "HIGH-ROI", - "symbol": "HROI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sai-single-collateral-dai", - "name": "Single Collateral DAI", - "symbol": "SAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "horor-halloween", - "name": "Halloween", - "symbol": "HOROR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mp3-revamped-music", - "name": "Revamped Music", - "symbol": "MP3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vinyl-vinyl", - "name": "VINYL", - "symbol": "VINYL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "twob-the-whale-of-blockchain", - "name": "The Whale of Blockchain", - "symbol": "TWOB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doi-doicoin", - "name": "Doicoin", - "symbol": "DOI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jejudoge-jejudoge", - "name": "Jejudoge", - "symbol": "JEJUDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mouse-mousemn", - "name": "MouseMN", - "symbol": "MOUSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cpr-crypto-paradise-rewards", - "name": "Crypto Paradise Rewards", - "symbol": "CPR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evox-evolution", - "name": "Evolution", - "symbol": "EVOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dzar-digital-rand", - "name": "Digital Rand", - "symbol": "DZAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trf-trifolium-coin", - "name": "Trifolium Coin", - "symbol": "TRF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gsc-gold-star-coin", - "name": "Gold Star Coin", - "symbol": "GSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gems-carbon-gems", - "name": "Carbon GEMS", - "symbol": "GEMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zai-zaigar", - "name": "Zaigar", - "symbol": "ZAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcoff-mcoff", - "name": "MCOFF", - "symbol": "MCOFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgdx-diropia", - "name": "Diropia", - "symbol": "DGDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pon-proof-of-nature-token", - "name": "Proof of Nature Token", - "symbol": "PoN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tdm-todamoon", - "name": "TODAMOON", - "symbol": "TDM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xpt-cryptobuyer", - "name": "Cryptobuyer", - "symbol": "XPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "heart-heart", - "name": "Heart", - "symbol": "HEART", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mat-mintasset", - "name": "MintAsset", - "symbol": "MAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jack-jackpot", - "name": "Jackpot", - "symbol": "JACK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tbt-tennisball", - "name": "Tennisball", - "symbol": "TBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bidcom-bidcommerce", - "name": "BIDCOMMERCE", - "symbol": "BIDCOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asac-asac-coin", - "name": "Asac Coin", - "symbol": "ASAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ems-ethereum-message-search", - "name": "Ethereum Message Search", - "symbol": "EMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zls-zelerius", - "name": "Zelerius", - "symbol": "ZLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cyber-cyberway", - "name": "CyberWay", - "symbol": "CYBER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "did-didcoin", - "name": "Didcoin", - "symbol": "DID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rich-rich-token", - "name": "RICH Token", - "symbol": "RICH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snq-santoq", - "name": "Santoq", - "symbol": "SNQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boba-boobam-1", - "name": "Boobam", - "symbol": "Boba", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "urbc-urbancash", - "name": "Urbancash", - "symbol": "URBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vya-vayla", - "name": "Vayla", - "symbol": "VYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mlt-modern-liquidity-token", - "name": "Modern Liquidity Token", - "symbol": "MLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fazr-fazer", - "name": "Fazer", - "symbol": "FAZR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "surf-surfexutilitytoken", - "name": "SurfEx Utility Token", - "symbol": "SURF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "digi-digiverse", - "name": "Digiverse", - "symbol": "DIGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "honk-honk-honk", - "name": "Honk Honk", - "symbol": "HONK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ppc-platinum-pro-coin", - "name": "Platinum Pro Coin", - "symbol": "PPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mocha-mochachain", - "name": "MochaChain", - "symbol": "MOCHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cubt-cubtoken", - "name": "CubToken", - "symbol": "CUBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdl-bundle-dao", - "name": "Bundle Dao", - "symbol": "BDL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frag-game-frag", - "name": "GAME-FRAG", - "symbol": "FRAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tico-tico", - "name": "TICO", - "symbol": "TICO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rwd-reward", - "name": "Reward", - "symbol": "RWD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plm-palmes", - "name": "Palmes", - "symbol": "PLM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hum-humanscoin", - "name": "Humanscoin", - "symbol": "HUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ash-ash", - "name": "ASH", - "symbol": "ASH", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "whale-whalecoin", - "name": "WhaleCoin", - "symbol": "WHALE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tr3b-astracoin", - "name": "Astracoin", - "symbol": "TR3B", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pgpay-pgpay", - "name": "PGPay", - "symbol": "PGPay", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pcoin-pcoin-network", - "name": "Pcoin Network", - "symbol": "PCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tgco-thaler-group-company", - "name": "Thaler Group Company", - "symbol": "TGCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "est-estate-token", - "name": "Estate Token", - "symbol": "EST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ant-ant-token", - "name": "Ant Token", - "symbol": "ANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "won-won-coin", - "name": "WON Coin", - "symbol": "WON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ket-maciej-tomczyk-token", - "name": "Maciej Tomczyk Token", - "symbol": "KET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prss-prss", - "name": "PRSS", - "symbol": "PRSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ast-apollo-space-token", - "name": "Apollo Space Token", - "symbol": "AST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clo-cryptlo", - "name": "Cryptlo", - "symbol": "CLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trd-tradecoin", - "name": "TRADECOIN", - "symbol": "TRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcp-bitcoin-pro", - "name": "Bitcoin Pro", - "symbol": "BTCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcio-blockchainio", - "name": "Blockchain.io", - "symbol": "BCIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etrx-traaittplatform", - "name": "traaitt Platform", - "symbol": "ETRX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wtp-web-token-pay", - "name": "Web Token Pay", - "symbol": "WTP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "taps-taps", - "name": "TAPS", - "symbol": "TAPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mpc-micropaymentcoin", - "name": "Micro Payment Coin", - "symbol": "MPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "teddi-tedi-inu-bsc", - "name": "Tedi Inu BSC", - "symbol": "TEDDI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grt-global-rental-token", - "name": "Global Rental Token", - "symbol": "GRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zoo-zoocoin", - "name": "ZooCoin", - "symbol": "ZOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tinku-tinku", - "name": "Tinku", - "symbol": "TINKU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nktn-niyukt", - "name": "Niyukt", - "symbol": "NKTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sammy-white-woolf-network", - "name": "White Woolf Network", - "symbol": "SAMMY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hem-hemelios", - "name": "Hemelios", - "symbol": "HEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "are-aurei", - "name": "Aurei", - "symbol": "ARE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blac-blac-coin", - "name": "BLAC coin", - "symbol": "BLAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stkn-student-token", - "name": "Student Token", - "symbol": "STKN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "999-999", - "name": "999", - "symbol": "999", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "call-global-crypto-alliance", - "name": "Global Crypto Alliance", - "symbol": "CALL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trds-traders-token", - "name": "Traders Token", - "symbol": "TRDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sat3-sat3", - "name": "SAT3", - "symbol": "SAT3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pin-public-index-network", - "name": "Public Index Network", - "symbol": "PIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vet-veteran", - "name": "Veteran", - "symbol": "VET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xscr-securus", - "name": "SECURUS", - "symbol": "XSCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "elect-elect", - "name": "ELECT", - "symbol": "ELECT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "joe-joerogan-coin", - "name": "JoeRogan Coin", - "symbol": "JOE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spf-sea-plastic-free", - "name": "Sea Plastic Free", - "symbol": "SPF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "diyt-diy-tube-coin", - "name": "DIY Tube Coin", - "symbol": "DIYT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ltcm-litecoin-mini-finance", - "name": "Litecoin Mini Finance", - "symbol": "LTCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arw-arrow", - "name": "Arrow", - "symbol": "ARW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "smars-safemars-protocol", - "name": "Safemars Protocol", - "symbol": "SMARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xido-xido-finance", - "name": "Xido Finance", - "symbol": "XIDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cir-ciredo", - "name": "Ciredo", - "symbol": "CIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dln-delorean-coin", - "name": "Delorean Coin", - "symbol": "DLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "astron-astronomia-token-bsc", - "name": "Astronomia Token BSC", - "symbol": "ASTRON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blb-blueberry", - "name": "BlueBerry", - "symbol": "BLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hentai-hentai-token", - "name": "Hentai Token", - "symbol": "HENTAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etm-etherman", - "name": "EtherMan", - "symbol": "ETM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "point-point", - "name": "Point", - "symbol": "POINT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "deuro-digitaleuro", - "name": "DigitalEuro", - "symbol": "DEURO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vtx-vortex-network", - "name": "Vortex Network", - "symbol": "VTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nmp-neuromorphic", - "name": "Neuromorphic", - "symbol": "NMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amor-amor-finance", - "name": "AMOR Finance", - "symbol": "AMOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "htx-hashtrust", - "name": "Hashtrust", - "symbol": "HTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cusd-cusd-currency", - "name": "cUSD Currency", - "symbol": "cUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aiv-aivia", - "name": "AIVIA", - "symbol": "AIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grove-grove", - "name": "GROVE", - "symbol": "GROVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hxn-havens-nook", - "name": "Havens Nook", - "symbol": "HXN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sak-stakesx", - "name": "StakesX", - "symbol": "SAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "litb-lightbit", - "name": "LightBit", - "symbol": "LITB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nrv-nerve", - "name": "NERVE", - "symbol": "NRV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xdca-decentralizecurrency", - "name": "DecentralizeCurrency", - "symbol": "XDCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "strms-streamies", - "name": "Streamies", - "symbol": "STRMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "epay-ethereumpay", - "name": "EthereumPay", - "symbol": "EPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tpc-trading-pool-coin", - "name": "Trading Pool Coin", - "symbol": "TPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ouro-ouroboros", - "name": "Ouroboros", - "symbol": "OURO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bcb-bcb-blockchain", - "name": "BCB Blockchain", - "symbol": "BCB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "newinu-newinu", - "name": "Newinu", - "symbol": "NEWINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scpt-sceptercash", - "name": "Sceptercash", - "symbol": "SCPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nort-northern", - "name": "Northern", - "symbol": "NORT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "riv-riva", - "name": "RIVA", - "symbol": "RIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "oxz-ox-zodiac", - "name": "Ox Zodiac", - "symbol": "OXZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "octax-octax", - "name": "OctaX", - "symbol": "OCTAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bles-blessing", - "name": "Blessing", - "symbol": "BLES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pfzr-pfzercoin", - "name": "Pfzercoin", - "symbol": "PFZR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "truxtr-truxtr", - "name": "TruXTR", - "symbol": "TRUXTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonstar-moonstar", - "name": "Moonstar", - "symbol": "MOONSTAR", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tchapp-tchapp", - "name": "TCHApp", - "symbol": "TCHAPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vault-vault-coin", - "name": "VAULT Coin", - "symbol": "VAULT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gcm-global-coin-market", - "name": "Global Coin Market", - "symbol": "GCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pos-poshcoin", - "name": "POSHCOIN", - "symbol": "POS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "malw-malwarechain", - "name": "MalwareChain", - "symbol": "MALW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bic-bitcom", - "name": "Bitcom", - "symbol": "BIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "drv-dravite", - "name": "Dravite", - "symbol": "DRV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ppr-ppr", - "name": "PPR", - "symbol": "PPR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "icy-icury", - "name": "ICURY", - "symbol": "ICY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ozi-ozinex-token", - "name": "OZINEX TOKEN", - "symbol": "OZI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fn-filenet", - "name": "Filenet", - "symbol": "FN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dink-dinky", - "name": "Dinky", - "symbol": "DINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fla-flacoin", - "name": "FlaCoin", - "symbol": "FLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sahu-sakhalin-husky", - "name": "Sakhalin Husky", - "symbol": "SAHU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aloe-aloe-token", - "name": "Aloe Token", - "symbol": "ALOE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "beez-safebeez", - "name": "SafeBeez", - "symbol": "BEEZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cntx-centex", - "name": "CENTEX", - "symbol": "CNTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "meow-moonkitties", - "name": "MoonKitties", - "symbol": "MEOW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mmt-mamo-coin", - "name": "Mamo Coin", - "symbol": "MMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jrc-jaguar", - "name": "Jaguar", - "symbol": "JRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccy-ccy", - "name": "CCY", - "symbol": "CCY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tibx-tonatiu-ibx", - "name": "Tonatiu IBX", - "symbol": "TIBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wpc-wave-pay-coin", - "name": "Wave pay coin", - "symbol": "WPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tna-taona", - "name": "Taona", - "symbol": "TNA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nnc-nanucoin", - "name": "Nanucoin", - "symbol": "NNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vibeon-turnvibeon", - "name": "TurnVibeOn", - "symbol": "VIBEON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ltncg-lightningcash-gold", - "name": "LightningCash Gold", - "symbol": "LTNCG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nep-neptune-mutual-token", - "name": "Neptune Mutual Token", - "symbol": "NEP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "egr-egoras", - "name": "Egoras", - "symbol": "EGR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blocs-blocs", - "name": "BLOCS", - "symbol": "BLOCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "etbold-ethbold", - "name": "ETHBOLD", - "symbol": "ETBOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "piss-pisscoin", - "name": "PISSCOIN", - "symbol": "PISS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgt-mystery-ghost-token", - "name": "Mystery Ghost Token", - "symbol": "MGT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rtt-real-trump-token", - "name": "Real Trump Token", - "symbol": "RTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "forfun-forfun", - "name": "ForFun", - "symbol": "forfun", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uct-unitedcrowd-token", - "name": "UnitedCrowd Token", - "symbol": "UCT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mehhcusd-mehhcoin", - "name": "MEHHCOIN", - "symbol": "MEHHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "peach-cyber-peach", - "name": "Cyber Peach", - "symbol": "PEACH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "codeo-codeo-token", - "name": "CODEO TOKEN", - "symbol": "CODEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nut-nut-money", - "name": "NUT MONEY", - "symbol": "NUT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "torr-torcorp", - "name": "TORcorp", - "symbol": "TORR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spl-simplicity", - "name": "Simplicity", - "symbol": "SPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "keto-ketosis-coin", - "name": "Ketosis Coin", - "symbol": "KETO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "uedc-united-emirate-decentralized-coin", - "name": "United Emirate Decentralized Coin", - "symbol": "UEDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rld-reload", - "name": "Reload", - "symbol": "RLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pwc-pawcoin", - "name": "Pawcoin", - "symbol": "PWC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yusdt-yellow-tether", - "name": "Yellow Tether", - "symbol": "YUSDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tagz5-tagz5", - "name": "TAGZ5", - "symbol": "TAGZ5", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vbsc-votechain", - "name": "Votechain", - "symbol": "VBSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "r3fi-recharge-finance", - "name": "Recharge Finance", - "symbol": "R3FI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rufus-rufus-finance", - "name": "Rufus Finance", - "symbol": "RUFUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dank-dank", - "name": "Dank", - "symbol": "DANK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nomo-bitnomo", - "name": "Bitnomo", - "symbol": "NOMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "antf-antsfinance", - "name": "Ants.Finance", - "symbol": "ANTF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dlc-delirium", - "name": "Delirium", - "symbol": "DLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aah-ahegao-dao", - "name": "Ahegao DAO", - "symbol": "AAH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hdln-hodlers-network", - "name": "Hodlers Network", - "symbol": "HDLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kos-lukasz-kos-token", - "name": "Łukasz Kos Token", - "symbol": "KOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "byt-byzbit", - "name": "Byzbit", - "symbol": "BYT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ned-needycoin", - "name": "Needycoin", - "symbol": "NED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fuel-fuel", - "name": "Fuel", - "symbol": "FUEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clg-collegicoin", - "name": "Collegicoin", - "symbol": "CLG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rakuc-raku-coin", - "name": "Raku Coin", - "symbol": "RAKUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbn-connect-business-network", - "name": "Connect Business Network", - "symbol": "CBN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dcn-dualcoin", - "name": "DualCoin", - "symbol": "DCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stpx-stash", - "name": "Stash", - "symbol": "STPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aidi-aidi-inu", - "name": "Aidi Inu", - "symbol": "AIDI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "konj-konjungate", - "name": "KONJUNGATE", - "symbol": "KONJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kowal-kowalski-token", - "name": "Kowalski Token", - "symbol": "KOWAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kuw-kuwait-coin", - "name": "Kuwait Coin", - "symbol": "KUW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "remit-remit", - "name": "REMIT", - "symbol": "REMIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cava-cavapoo", - "name": "Cavapoo", - "symbol": "CAVA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "4xb-4xbit", - "name": "4xBit", - "symbol": "4XB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spacek-space-knight", - "name": "Space Knight", - "symbol": "SPACEK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fxt-fenix", - "name": "FENIX", - "symbol": "FXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "byco-bycoin", - "name": "Bycoin", - "symbol": "BYCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nft-nft-smart-coin", - "name": "NFT Smart Coin", - "symbol": "NFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "afdlt-afrodex-labs-token", - "name": "AfroDex Labs Token", - "symbol": "AFDLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dmme-dmme", - "name": "DMme", - "symbol": "DMME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnp-benepit-protocol", - "name": "BenePit Protocol", - "symbol": "BNP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lku-lukiu", - "name": "Lukiu", - "symbol": "LKU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcp-bitcoin-platinums", - "name": "Bitcoin Platinums", - "symbol": "BCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axai-axai", - "name": "Axai", - "symbol": "AXAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fxn-fxcoin", - "name": "FXCOIN", - "symbol": "FXN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "berg-bergco-coin", - "name": "BergCo Coin", - "symbol": "BERG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "enox-ethanol-x", - "name": "Ethanol X", - "symbol": "ENOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pker-poker", - "name": "Poker", - "symbol": "PKER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safety-safety-token", - "name": "Safety Token", - "symbol": "SAFETY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nii-nii", - "name": "NIi", - "symbol": "NII", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "faw-fastway", - "name": "FastWay", - "symbol": "FAW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ksh-kahsh", - "name": "Kahsh", - "symbol": "KSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "linka-linka", - "name": "LINKA", - "symbol": "LINKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buda-budacoin", - "name": "BudaCoin", - "symbol": "BUDA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bugg-bugg-inu", - "name": "Bugg Inu", - "symbol": "BUGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ilk-inlock", - "name": "INLOCK", - "symbol": "ILK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrm-aerum", - "name": "AERUM", - "symbol": "XRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dad-dad-chain", - "name": "DAD Chain", - "symbol": "DAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usdx-usdx", - "name": "USDX", - "symbol": "USDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dlpn-dolphin-token", - "name": "Dolphin Token", - "symbol": "DLPN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "levs-levelup-bnb", - "name": "LevelUP - BNB", - "symbol": "LEVS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lambs-lambda-space-token", - "name": "Lambda Space Token", - "symbol": "LAMBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfc-bitfree-cash", - "name": "Bitfree Cash", - "symbol": "BFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ecoec-ecoearthcoin", - "name": "EcoEarthCoin", - "symbol": "ECOEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "r0ok-r0ok-token", - "name": "r0ok Token", - "symbol": "R0OK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grg-rigo-token", - "name": "Rigo Token", - "symbol": "GRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ace-acewins", - "name": "AceWins", - "symbol": "ACW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nftqr-nft-qr", - "name": "NFT-QR", - "symbol": "NFTQR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btv-bitcoininvest", - "name": "BitcoinInvest", - "symbol": "BTV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ukc-united-korea-coin", - "name": "United Korea Coin", - "symbol": "UKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ssf-secretsky-finance", - "name": "SecretSky Finance", - "symbol": "SSF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kapp-kappi-token", - "name": "Kappi Token", - "symbol": "KAPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cwt-crosswallet", - "name": "CrossWallet", - "symbol": "CWT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chex-chintai", - "name": "Chintai", - "symbol": "CHEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ahouse-animal-house", - "name": "Animal House", - "symbol": "AHOUSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "paper-papercoin", - "name": "Papercoin", - "symbol": "PAPER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spky-spookycoin", - "name": "SpookyCoin", - "symbol": "SPKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "escx-escx-token", - "name": "ESCX Token", - "symbol": "ESCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mch-meconcash", - "name": "MeconCash", - "symbol": "MCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dsc-dash-cash", - "name": "Dash Cash", - "symbol": "DSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "asgc-asgcoin", - "name": "Asgcoin", - "symbol": "ASGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xdr-xandereum", - "name": "Xandereum", - "symbol": "XDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "2x2-2x2", - "name": "2X2", - "symbol": "2X2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "etlytet-ethlyte-crypto", - "name": "Ethlyte Crypto", - "symbol": "EtLyteT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aet-aerotoken", - "name": "AeroToken", - "symbol": "AET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bab-basecoin", - "name": "Basecoin", - "symbol": "BAB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "alp-alp-coin", - "name": "ALP Coin", - "symbol": "ALP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebase-eurbase", - "name": "EURBASE", - "symbol": "EBASE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uni-unicorn-token", - "name": "UNICORN Token", - "symbol": "UNI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xsr-xensor", - "name": "Xensor", - "symbol": "XSR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cht-coinhe-token", - "name": "CoinHe Token", - "symbol": "CHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tuda-tutor39s-diary", - "name": "Tutor's Diary", - "symbol": "TUDA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gom-gomoney", - "name": "GoMoney", - "symbol": "GOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tsr-tesra", - "name": "Tesra", - "symbol": "TSR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unis-universe-coin", - "name": "Universe Coin", - "symbol": "UNIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcc-barrelcrudecoin", - "name": "BarrelCrudeCoin", - "symbol": "BCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rpzx-rapidz", - "name": "Rapidz", - "symbol": "RPZX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grond-grond", - "name": "Grond", - "symbol": "GROND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bxt-bitfxt-coin", - "name": "Bitfxt Coin", - "symbol": "BXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dragon-dragon-option", - "name": "Dragon Option", - "symbol": "DRAGON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crn-chronocoin", - "name": "ChronoCoin", - "symbol": "CRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfx-bitfex", - "name": "Bitfex", - "symbol": "BFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bny-bancacy", - "name": "Bancacy", - "symbol": "BNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wha-whallet", - "name": "Whallet", - "symbol": "WHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "everape-everape", - "name": "EverApe", - "symbol": "EVERAPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "barin-barin", - "name": "BARIN", - "symbol": "BARIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boomc-boomcoin", - "name": "BoomCoin", - "symbol": "BOOMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kelpie-kelpie-inu", - "name": "Kelpie Inu", - "symbol": "KELPIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "intro-intro-coin", - "name": "iNTRO COiN", - "symbol": "iNTRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vers-versess-coin", - "name": "Versess Coin", - "symbol": "VERS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "knight-knight-token", - "name": "Knight Token", - "symbol": "KNIGHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gmy-gameology", - "name": "Gameology", - "symbol": "GMY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "winlet-winlet-nft", - "name": "Winlet NFT", - "symbol": "WINLET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eosc-eos-force", - "name": "EOS Force", - "symbol": "EOSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "chia-chia-network-token", - "name": "CHIA NETWORK TOKEN", - "symbol": "CHIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wlink-wlink", - "name": "WLINK", - "symbol": "WLINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "self-beselfless", - "name": "BeSelfless", - "symbol": "SELF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gap-global-adversity-project", - "name": "Global Adversity Project", - "symbol": "GAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vlc-volcano", - "name": "Volcano", - "symbol": "VLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ecum-ethereumcum", - "name": "EthereumCum", - "symbol": "eCum", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nca-nuclear", - "name": "Nuclear", - "symbol": "NCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kaaso-kaaso", - "name": "KAASO", - "symbol": "KAASO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zvc-zvchain", - "name": "ZVCHAIN", - "symbol": "ZVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eon-dimension-chain", - "name": "Dimension Chain", - "symbol": "EON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonminer-moonminer", - "name": "MoonMiner", - "symbol": "MOONMINER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clr-color-platform", - "name": "Color Platform", - "symbol": "CLR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pug-pug", - "name": "PUG", - "symbol": "PUG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "give-give-token", - "name": "GIVE Token", - "symbol": "GIVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drn-doron", - "name": "DORON", - "symbol": "DRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dpx-dopex", - "name": "Dopex", - "symbol": "DPX", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cic-cichain", - "name": "CIChain", - "symbol": "CIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jump-jumpcoin", - "name": "Jumpcoin", - "symbol": "JUMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tiktak-tiktak", - "name": "TikTak", - "symbol": "TIKTAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccm-crypto-coupons-market", - "name": "Crypto Coupons Market", - "symbol": "CCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cb-coinbig", - "name": "COINBIG", - "symbol": "CB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fin-fenniecoin", - "name": "FENNIECOIN", - "symbol": "FIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onlyup-only-up-token", - "name": "Only UP Token", - "symbol": "ONLYUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sion-sion", - "name": "SION", - "symbol": "SION", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "baw-bawnetwork", - "name": "BAWnetwork", - "symbol": "BAW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcs-business-credit-substitute", - "name": "Business Credit Substitute", - "symbol": "BCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amon-amond", - "name": "AmonD", - "symbol": "AMON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vtx-volentix", - "name": "Volentix", - "symbol": "VTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brick-brick-token", - "name": "Brick Token", - "symbol": "BRICK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cipx-colletrix", - "name": "Colletrix", - "symbol": "CIPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nexxo-nexxo", - "name": "Nexxo", - "symbol": "NEXXO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pvp-playervsplayercoin", - "name": "PlayerVsPlayerCoin", - "symbol": "PVP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tfb-truefeedback", - "name": "TrueFeedBack", - "symbol": "TFB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cml-cml", - "name": "CML", - "symbol": "CML", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cybr-cybr-token", - "name": "CYBR Token", - "symbol": "CYBR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gdc-global-digital-content", - "name": "Global Digital Content", - "symbol": "GDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "3dc-3dcoin", - "name": "3DCoin", - "symbol": "3DC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "artg-goya-giant-token", - "name": "Goya Giant Token", - "symbol": "ARTG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "avn-avantage", - "name": "Avantage", - "symbol": "AVN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "evt-elevation-token", - "name": "Elevation Token", - "symbol": "EVT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ec-echoin", - "name": "Echoin", - "symbol": "EC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gap-gaps", - "name": "GAPS", - "symbol": "GAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doug-doug-coin", - "name": "Doug coin", - "symbol": "DOUG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hls-helios-protocol", - "name": "Helios Protocol", - "symbol": "HLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btcf-bitcoin-fast", - "name": "Bitcoin Fast", - "symbol": "BTCF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rally-rally", - "name": "Rally", - "symbol": "RALLY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coldkoala-coldkoala", - "name": "ColdKoala", - "symbol": "COLDKOALA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "job-jobchain", - "name": "Jobchain", - "symbol": "JOB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sove-soverain", - "name": "Soverain", - "symbol": "SOVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vena-vena", - "name": "VENA", - "symbol": "VENA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wec-wave-edu-coin", - "name": "Wave edu coin", - "symbol": "WEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "strn-saturn-classic-dao-token", - "name": "Saturn Classic DAO Token", - "symbol": "STRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spes-spescoin", - "name": "SpesCoin", - "symbol": "SPES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lht-lighthouse-token", - "name": "LHT", - "symbol": "LHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ufct-ufctoken", - "name": "UFCTOKEN", - "symbol": "UFCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cup-cupdefi", - "name": "Cupdefi", - "symbol": "CUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "glov-glovecoin", - "name": "GloveCoin", - "symbol": "GLOV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lqty-liquity", - "name": "Liquity", - "symbol": "LQTY", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nbng-nobunaga", - "name": "Nobunaga", - "symbol": "NBNG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ufo-ufo-gaming", - "name": "UFO Gaming", - "symbol": "UFO", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mpwr-empower-network", - "name": "Empower Network", - "symbol": "MPWR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dqi-dotqii", - "name": "DotQii", - "symbol": "DQI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "klown-ether-clown", - "name": "Ether Clown", - "symbol": "KLOWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thun-thunder", - "name": "Thunder", - "symbol": "THUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flk-floki-inu", - "name": "FLOKI INU", - "symbol": "FLK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "acu-aitheon", - "name": "Aitheon", - "symbol": "ACU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fab-fabrk", - "name": "FABRK", - "symbol": "FAB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rent-rentapp", - "name": "RentApp", - "symbol": "RENT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bare-bare", - "name": "BARE", - "symbol": "BARE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "subs-subsudio", - "name": "SUBSUDIO", - "symbol": "SUBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xct-xcrypt-token", - "name": "xCrypt Token", - "symbol": "XCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dcc-dogecoin-cash", - "name": "Dogecoin Cash", - "symbol": "DCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "selen-selenium-token", - "name": "Selenium Token", - "symbol": "SELEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unity-unityfund", - "name": "UnityFund", - "symbol": "UNITY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nss-nss-coin", - "name": "NSS Coin", - "symbol": "NSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amio-amino-network", - "name": "Amino Network", - "symbol": "AMIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orom-orom-token", - "name": "Orom Token", - "symbol": "OROM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xusb-xusb", - "name": "XUSB", - "symbol": "XUSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dtep-decoin", - "name": "DECOIN", - "symbol": "DTEP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ideal-idealcoin", - "name": "IDEALCOIN", - "symbol": "IDEAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wbet-wavesbet", - "name": "Wavesbet", - "symbol": "WBET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tan-taklimakan-network", - "name": "Taklimakan Network", - "symbol": "TAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crad-cryptoads-marketplace", - "name": "CryptoAds Marketplace", - "symbol": "CRAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "seal-sealchain", - "name": "Sealchain", - "symbol": "SEAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cron-cryptocean", - "name": "Cryptocean", - "symbol": "CRON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "stream-streamit-coin", - "name": "Streamit Coin", - "symbol": "STREAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tigz-tig-zodiac", - "name": "Tig Zodiac", - "symbol": "TIGZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pole-southpole", - "name": "SouthPole", - "symbol": "POLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gst-goose-token", - "name": "Goose Token", - "symbol": "GST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ncl-nuclear-platform", - "name": "Nuclear Platform", - "symbol": "NCL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebt-ebank-token", - "name": "Ebank token", - "symbol": "EBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sto-the-stone-coin", - "name": "The Stone Coin", - "symbol": "STO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "polybunny-pancake-bunny-polygon", - "name": "Pancake Bunny Polygon", - "symbol": "POLYBUNNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "utip-utip", - "name": "uTip", - "symbol": "uTip", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fxs-freixshares", - "name": "FreiXShares", - "symbol": "FXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "son-simone", - "name": "Simone", - "symbol": "SON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "guf-goesuphigher", - "name": "GoesUpHigher", - "symbol": "GUF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fo-fibos", - "name": "FIBOS", - "symbol": "FO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mhce-mhce", - "name": "MHCE", - "symbol": "MHCE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tyc-tychecoin", - "name": "TycheCoin", - "symbol": "TYC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "byt-bayan-token", - "name": "Bayan Token", - "symbol": "BYT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ete-eternode", - "name": "Eternode", - "symbol": "ETE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lad-ladder-network-token", - "name": "Ladder Network Token", - "symbol": "LAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lot-lukki-operating-token", - "name": "Lukki Operating Token", - "symbol": "LOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "het-hestia-token", - "name": "Hestia Token", - "symbol": "HET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bht-bhex-token", - "name": "BHEX Token", - "symbol": "BHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "togo-togo-inu", - "name": "TOGO INU", - "symbol": "TOGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "epch-elonnpooch", - "name": "Elonnpooch", - "symbol": "EPCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "glo-globo-token", - "name": "GLOBO TOKEN", - "symbol": "GLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wbbc-wibcoin", - "name": "Wibcoin", - "symbol": "WBBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kdx-kudex", - "name": "Kudex", - "symbol": "KDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mb8-mb8-coin", - "name": "MB8 Coin", - "symbol": "MB8", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dogeback-doge-back", - "name": "Doge Back", - "symbol": "DOGEBACK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cocos-cocos-bcx", - "name": "Cocos-BCX", - "symbol": "COCOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ver-vergo", - "name": "VERGO", - "symbol": "VER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btx-boltx", - "name": "Boltx", - "symbol": "BTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "armr-armr", - "name": "ARMR", - "symbol": "ARMR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dili-d-community", - "name": "D Community", - "symbol": "DILI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mt-monarch", - "name": "Monarch", - "symbol": "MT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prux-prux-coin", - "name": "PRUX-Coin", - "symbol": "PRUX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bcm-bitcoinmoney", - "name": "BitcoinMoney", - "symbol": "BCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kus-kuswap", - "name": "KuSwap", - "symbol": "KUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "steamx-steam-exchange", - "name": "Steam Exchange", - "symbol": "STEAMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "activ-activeightcoin", - "name": "ActivEightCoin", - "symbol": "ACTIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lazydoge-lazydoge", - "name": "LazyDoge", - "symbol": "LAZYDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xsap-xsapphire", - "name": "xSapphire", - "symbol": "XSAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buck-buck", - "name": "BUCK", - "symbol": "BUCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cryt-crytrexcoin", - "name": "CryTrExCoin", - "symbol": "CRYT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dojo-dojo", - "name": "DOJO", - "symbol": "DOJO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dawg-inumaki", - "name": "Inumaki", - "symbol": "DAWG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "igf-igf-token", - "name": "IGF Token", - "symbol": "IGF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sune-kitsune-token", - "name": "Kitsune token", - "symbol": "SUNE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bags-bags", - "name": "BAGS", - "symbol": "BAGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vol-volair", - "name": "VolAir", - "symbol": "VOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xlmx-stellar-classic", - "name": "Stellar Classic", - "symbol": "XLMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thar-thar-token", - "name": "Thar Token", - "symbol": "THAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttum-titanium", - "name": "Titanium", - "symbol": "TTUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xeur-xeuro", - "name": "xEURO", - "symbol": "xEUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "betn-bitelectroneum", - "name": "Bitelectroneum", - "symbol": "BETN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thur-thursday-ninja", - "name": "Thursday Ninja", - "symbol": "THUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hin-hinka", - "name": "HINKA", - "symbol": "HIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "melt-melt", - "name": "Melt", - "symbol": "MELT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bok-blockium", - "name": "Blockium", - "symbol": "BOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xczm-xavander-coin", - "name": "Xavander Coin", - "symbol": "XCZM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bpro-bitcloud-pro", - "name": "Bitcloud Pro", - "symbol": "BPRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "catty-catty-coin", - "name": "Catty Coin", - "symbol": "CATTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cust-custody-token", - "name": "Custody Token", - "symbol": "CUST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wolf-wolffinance", - "name": "Wolf.Finance", - "symbol": "WOLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "monto-monkey-token", - "name": "MONKEY TOKEN", - "symbol": "MONTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kccpad-kccpad", - "name": "KCCPad", - "symbol": "KCCPAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tgn-terragreen", - "name": "TerraGreen", - "symbol": "TGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "epm-extreme-private-masternode", - "name": "Extreme Private Masternode", - "symbol": "EPM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ugd-unigrid", - "name": "UNIGRID", - "symbol": "UGD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yta-yottachain", - "name": "YottaChain", - "symbol": "YTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "volc-volcanoes", - "name": "Volcanoes", - "symbol": "VOLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kiowa-kiowa", - "name": "Kiowa", - "symbol": "KIOWA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obc-oblichain", - "name": "Oblichain", - "symbol": "OBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asg-asgard", - "name": "Asgard", - "symbol": "ASG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rei-zerogoki", - "name": "Zerogoki", - "symbol": "REI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "p2c-pay2crypto", - "name": "Pay2Crypto", - "symbol": "P2C", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hgo-hirego", - "name": "HireGo", - "symbol": "HGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "diem-diem", - "name": "Diem", - "symbol": "DIEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cch-coinchase", - "name": "Coinchase Token", - "symbol": "CCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ucc-united-community-coin", - "name": "United Community Coin", - "symbol": "UCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "1up-uptrennd", - "name": "Uptrennd", - "symbol": "1UP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agro-agrocoin", - "name": "Agrocoin", - "symbol": "AGRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bird-birdchain", - "name": "Birdchain", - "symbol": "BIRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aidoge-aidoge-finance", - "name": "Aidoge Finance", - "symbol": "AIDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xiro-xiropht", - "name": "Xiropht", - "symbol": "XIRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shock-aftershock", - "name": "AfterShock", - "symbol": "SHOCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xcm-coinmetro-token", - "name": "CoinMetro Token", - "symbol": "XCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shica-shiba-cake", - "name": "Shiba Cake", - "symbol": "SHICA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "opnn-opennity", - "name": "Opennity", - "symbol": "OPNN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dfs-digital-fantasy-sports", - "name": "Digital Fantasy Sports", - "symbol": "DFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defc-defi-coin", - "name": "DeFi Coin", - "symbol": "DEFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "godl-godl", - "name": "GODL", - "symbol": "GODL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lnx-lnx-protocol", - "name": "LNX Protocol", - "symbol": "LNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dawgs-spacedawgs", - "name": "SpaceDawgs", - "symbol": "DAWGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bgbp-binance-gbp-stable-coin", - "name": "Binance GBP Stable Coin", - "symbol": "BGBP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hca-harcomia", - "name": "Harcomia", - "symbol": "HCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "upcoin-upcoin", - "name": "Upcoin", - "symbol": "UPCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "levl-levolution", - "name": "Levolution", - "symbol": "LEVL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogi-doggy-inu", - "name": "Doggy Inu", - "symbol": "DOGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "burn-blockburn", - "name": "Blockburn", - "symbol": "BURN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cruz-cruzbit", - "name": "Cruzbit", - "symbol": "CRUZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hld-holdefi", - "name": "Holdefi", - "symbol": "HLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babydogo-baby-dogo-coin", - "name": "Baby Dogo Coin", - "symbol": "BABYDOGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sto-storeum", - "name": "Storeum", - "symbol": "STO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bxx-baanx", - "name": "Baanx", - "symbol": "BXX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hype-hype-token", - "name": "Hype Token", - "symbol": "HYPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "void-void", - "name": "Void", - "symbol": "VOID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mro-mero-currency", - "name": "Mero Currency", - "symbol": "MRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boom-boom", - "name": "BOOM", - "symbol": "BOOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sage-sagecity", - "name": "SageCity", - "symbol": "SAGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eshare-evershare", - "name": "EverShare", - "symbol": "ESHARE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lil-lillion", - "name": "Lillion", - "symbol": "LIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "proge-protector-roge", - "name": "Protector Roge", - "symbol": "PROGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lils-lils", - "name": "LILS", - "symbol": "LILS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "watc-whatcoin", - "name": "WhatCoin", - "symbol": "WATC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "spx-scopx", - "name": "SCOPX", - "symbol": "SPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gold-gold2", - "name": "GOLD", - "symbol": "GOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lhc-collider", - "name": "Collider", - "symbol": "LHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "del-decimal35345", - "name": "Decimal", - "symbol": "DEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "em-eminer", - "name": "Eminer", - "symbol": "EM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pia-futurepia", - "name": "Futurepia", - "symbol": "PIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mcc-magic-cube-coin", - "name": "Magic Cube Coin", - "symbol": "MCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bpakc-bitpakcointoken", - "name": "BitpakcoinToken", - "symbol": "BPAKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ubn-ubricoin", - "name": "Ubricoin", - "symbol": "UBN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eurt-eurt", - "name": "EURT", - "symbol": "EURT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trv-trustverse", - "name": "TrustVerse", - "symbol": "TRV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pixiu-pixiuswap-token", - "name": "PixiuSwap Token", - "symbol": "PIXIU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iq7-iq7g", - "name": "IQ7G", - "symbol": "IQ7", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dot-polkadot-old", - "name": "Polkadot old", - "symbol": "DOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eost-eost-trust", - "name": "EOS TRUST", - "symbol": "EOST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uni-uni-coin", - "name": "UNI COIN", - "symbol": "UNI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "noiz-noiz-chain", - "name": "NOIZchain", - "symbol": "NOIZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crash-crashcoin", - "name": "CrashCoin", - "symbol": "CRASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blaze-inferno-token", - "name": "Inferno Token", - "symbol": "BLAZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fuze-fuze-token", - "name": "FUZE Token", - "symbol": "FUZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "merc-merlin-coins", - "name": "MERLIN COINS", - "symbol": "MERC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spike-spiking", - "name": "Spiking", - "symbol": "SPIKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qzk-qzkcoin", - "name": "QZKCoin", - "symbol": "QZK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "catdj-cat-dj", - "name": "Cat DJ", - "symbol": "CATDJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wwcn-wrapped-widecoin", - "name": "Wrapped Widecoin", - "symbol": "WWCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xeno-xenoverse", - "name": "Xenoverse", - "symbol": "XENO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "loteu-loteu", - "name": "LOTEU", - "symbol": "LOTEU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spn-spartancoin", - "name": "SpartanCoin", - "symbol": "SPN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blink-blockmason-link", - "name": "Blockmason Link", - "symbol": "BLINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agor-agora-coin", - "name": "AGORA COIN", - "symbol": "AGOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pdata-pdata", - "name": "PDATA", - "symbol": "PDATA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vpx-privix", - "name": "Privix", - "symbol": "VPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sprk-sparkster", - "name": "Sparkster", - "symbol": "SPRK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nbl-nobility", - "name": "Nobility", - "symbol": "NBL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alt-alta", - "name": "Alta", - "symbol": "ALT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gnun-golden-inu", - "name": "Golden Inu", - "symbol": "GNUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccash-campuscash", - "name": "CampusCash", - "symbol": "CCASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gulag-gulag-token", - "name": "GULAG Token", - "symbol": "GULAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "huddl-huddl", - "name": "HUDDL", - "symbol": "HUDDL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exo-exolover", - "name": "EXOLOVER", - "symbol": "EXO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "igg-ig-gold", - "name": "IG Gold", - "symbol": "IGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "skt-sealblock-token", - "name": "SealBlock Token", - "symbol": "SKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xchf-cryptofranc", - "name": "CryptoFranc", - "symbol": "XCHF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mapr-maya-preferred-223", - "name": "Maya Preferred 223", - "symbol": "MAPR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccc-coindom", - "name": "Coindom", - "symbol": "CCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spin-spin-protocol", - "name": "SPIN Protocol", - "symbol": "SPIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zb-zerobank", - "name": "Zerobank", - "symbol": "ZB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qdao-q-dao-governance-token-v10", - "name": "Q DAO Governance token v1.0", - "symbol": "QDAO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adn-aladdin", - "name": "Aladdin", - "symbol": "ADN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b91-b91", - "name": "B91", - "symbol": "B91", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ect-ethereum-chain-token", - "name": "Ethereum Chain Token", - "symbol": "ECT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mpay-menapay", - "name": "MenaPay", - "symbol": "MPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ocul-oculor", - "name": "Oculor", - "symbol": "OCUL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "snax-snax", - "name": "SNAX", - "symbol": "SNAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "witch-witch-token", - "name": "Witch Token", - "symbol": "WITCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apsc-agora-platform", - "name": "AGORA Platform", - "symbol": "APSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bvg-bitcoin-virtual-gold", - "name": "Bitcoin Virtual Gold", - "symbol": "BVG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mms-minimals", - "name": "Minimals", - "symbol": "MMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonb-moonb", - "name": "MOONB", - "symbol": "MOONB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rto-arto", - "name": "Arto", - "symbol": "RTO", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "lytx-lytix", - "name": "Lytix", - "symbol": "LYTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pine-pinecoin", - "name": "Pinecoin", - "symbol": "PINE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zedxion-zedxion-erc-20", - "name": "ZEDXION ERC-20", - "symbol": "ZEDXION", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ad-asian-dragon", - "name": "Asian Dragon", - "symbol": "AD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbix-cubiex", - "name": "Cubiex", - "symbol": "CBIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vbk-vibook", - "name": "Vibook", - "symbol": "VBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "usdq-usdq", - "name": "USDQ", - "symbol": "USDQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clm-claymore", - "name": "Claymore", - "symbol": "CLM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frecnx-freldocoinx", - "name": "FreldoCoinX", - "symbol": "FRECNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trb-tribe", - "name": "Tribe", - "symbol": "TRB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "haven-safehaven", - "name": "SafeHaven", - "symbol": "HAVEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "salsa-salsa-token", - "name": "SALSA TOKEN", - "symbol": "SALSA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wff-whiteflagfund", - "name": "WHITEFLAGFUND", - "symbol": "WFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cjx-cryptojaguars", - "name": "Cryptojaguars", - "symbol": "CJX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spacemoon-spacemoon", - "name": "SpaceMoon", - "symbol": "SPACEMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lcx-lightchain", - "name": "LightChain", - "symbol": "LCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sreur-euro-tokens", - "name": "EURO TOKENS", - "symbol": "SREUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hana-hanacoin", - "name": "Hanacoin", - "symbol": "HANA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mgc-michal-gee-token", - "name": "MICHAL GEE TOKEN", - "symbol": "MGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cphx-crypto-phoenix", - "name": "CRYPTO PHOENIX", - "symbol": "CPHX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nuke-half-life", - "name": "Half Life", - "symbol": "NUKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnby-bnb-charity-finance", - "name": "BNB Charity Finance", - "symbol": "BNBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hora-hydnora", - "name": "HYDNORA", - "symbol": "HORA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "thore-thore-token", - "name": "THORE Token", - "symbol": "THORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethplo-ethplode", - "name": "ETHPLODE", - "symbol": "ETHPLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sct-smartcryptotech", - "name": "SmartCryptoTech", - "symbol": "SCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "func-funtimecoin", - "name": "FunTimeCoin", - "symbol": "FUNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dud-jacek-dudzic-token", - "name": "Jacek Dudzic Token", - "symbol": "DUD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mfund-memefund", - "name": "Memefund", - "symbol": "MFUND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "redfeg-redfeg", - "name": "RedFeg", - "symbol": "REDFEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pln-pln", - "name": "ΩPLN", - "symbol": "ΩPLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "topaz-topaz", - "name": "TOPAZ", - "symbol": "TOPAZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dcake-dad-cake", - "name": "Dad Cake", - "symbol": "DCAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "caky-spicycake-finance", - "name": "SpicyCake Finance", - "symbol": "CAKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "andes-andescoin", - "name": "AndesCoin", - "symbol": "ANDES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lad-lemonad", - "name": "LemonAd", - "symbol": "LAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "trat-tratin", - "name": "Tratin", - "symbol": "TRAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "edx-endox", - "name": "EndoX", - "symbol": "EDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fastx-transfast", - "name": "TRANSFAST", - "symbol": "FASTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zdx-zer-dex", - "name": "Zer-Dex", - "symbol": "ZDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gex-gexan", - "name": "Gexan", - "symbol": "GEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gfncash-grafenocoin", - "name": "GrafenoCoin", - "symbol": "GFNCASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zoo-cryptozoo", - "name": "CryptoZoo", - "symbol": "ZOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "expi-expi", - "name": "EXPi", - "symbol": "EXPI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kri-krios", - "name": "Krios", - "symbol": "KRI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alp-alphacon", - "name": "Alphacon", - "symbol": "ALP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "voro-voronoi-finance", - "name": "Voronoi Finance", - "symbol": "VORO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "torg-torg", - "name": "TORG", - "symbol": "TORG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pals-pals", - "name": "PALS", - "symbol": "PALS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ioux-iou", - "name": "IOU", - "symbol": "IOUX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gos-gosama", - "name": "Gosama", - "symbol": "GOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etm-en-tan-mo", - "name": "En-Tan-Mo", - "symbol": "ETM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crad-crad-cash", - "name": "CRAD CASH", - "symbol": "CRAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tagz-tagz", - "name": "TAGZ", - "symbol": "TAGZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "betty-betty", - "name": "Betty", - "symbol": "BETTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "keg-kegcoin-gold", - "name": "Kegcoin Gold", - "symbol": "KEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "trtg-turtlegold", - "name": "TurtleGold", - "symbol": "TRTG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lbn-lucky-block-network", - "name": "Lucky Block Network", - "symbol": "LBN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "phv-pathhive", - "name": "PATHHIVE", - "symbol": "PHV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tqn-toqqn", - "name": "Toqqn", - "symbol": "TQN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "psychodoge-psycho-doge", - "name": "Psycho Doge", - "symbol": "PSYCHODOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fig-flowcom", - "name": "FlowCom", - "symbol": "FIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hitc-help-insurance-trust-consultant", - "name": "Help Insurance Trust Consultant", - "symbol": "HITC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nusd-neutral-dollar", - "name": "Neutral Dollar", - "symbol": "NUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ggn-global-green", - "name": "Global Green", - "symbol": "GGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fatcake-fatcake", - "name": "FatCake", - "symbol": "FATCAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chr-chromia", - "name": "Chromia", - "symbol": "CHR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ksg-kings-global-token", - "name": "King's Global Token", - "symbol": "KSG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grat-gratz", - "name": "Gratz", - "symbol": "GRAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "odex-one-dex", - "name": "One DEX", - "symbol": "ODEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tro-trunk-coin", - "name": "TRUNK COIN", - "symbol": "TRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "k64-komodore64", - "name": "Komodore64", - "symbol": "K64", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zyrk-zyrk", - "name": "Zyrk", - "symbol": "ZYRK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dxl-dexlab", - "name": "Dexlab", - "symbol": "DXL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ralph-save-ralph", - "name": "Save Ralph", - "symbol": "RALPH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dream-dreamteam", - "name": "DreamTeam Token", - "symbol": "DREAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "try-trias", - "name": "Trias", - "symbol": "TRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "omchf-omniunit-cash-features", - "name": "Omniunit Cash Features", - "symbol": "OMCHF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slt-safelifetoken", - "name": "SAFELIFETOKEN", - "symbol": "SLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aic-ai-coin", - "name": "AI COIN", - "symbol": "AIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flsh-flash-up-coin", - "name": "Flash Up Coin", - "symbol": "FLSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssg-sea-swap-global", - "name": "Sea Swap Global", - "symbol": "SSG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b8f-ball-eight-finance", - "name": "Ball Eight Finance", - "symbol": "B8F", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnode-beenode", - "name": "BeeNode", - "symbol": "BNODE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "alv-alluva", - "name": "ALLUVA", - "symbol": "ALV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "awr-award-coin", - "name": "Award Coin", - "symbol": "AWR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xsl-solo", - "name": "Solo", - "symbol": "XSL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kshib-kaiken-shiba", - "name": "Kaiken Shiba", - "symbol": "KSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unfld-unfoldu-group", - "name": "UNFOLDU GROUP", - "symbol": "UNFLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iotw-iotw", - "name": "IOTW", - "symbol": "IOTW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kue-kuende", - "name": "Kuende", - "symbol": "KUE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mintd-mintd", - "name": "MINTD", - "symbol": "MINTD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cbc-carbon-coin", - "name": "Carbon Coin", - "symbol": "CBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fao-farmer-and-one-farmer-and-one-token", - "name": "Farmer And One / Farmer And One Token", - "symbol": "FAO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "edc-edcash", - "name": "Edcash", - "symbol": "EDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "drap-doge-strap", - "name": "Doge Strap", - "symbol": "DRAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "koin-koinon", - "name": "Koinon", - "symbol": "KOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aevo-aevo", - "name": "AEVO", - "symbol": "AEVO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "afin-asian-fintech", - "name": "Asian Fintech", - "symbol": "AFIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ntr-netrum", - "name": "Netrum", - "symbol": "NTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "at-artfinity", - "name": "Artfinity", - "symbol": "AT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "neox-neox", - "name": "NEOX", - "symbol": "NEOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tkn-tokenot", - "name": "Tokenot", - "symbol": "TKN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xxt-xxt-token", - "name": "XXT-Token", - "symbol": "XXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hup-huplife", - "name": "HUP.LIFE", - "symbol": "HUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ast-ainu-swap-token", - "name": "Ainu Swap Token", - "symbol": "AST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sfc-safechain", - "name": "SAFECHAIN", - "symbol": "SFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qwc-qwertycoin", - "name": "Qwertycoin", - "symbol": "QWC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vdx-vodi-x", - "name": "Vodi X", - "symbol": "VDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vab-vabble", - "name": "Vabble", - "symbol": "VAB", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "p2px-p2p-global-network", - "name": "P2P Global Network", - "symbol": "P2PX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xcon-connect-coin", - "name": "Connect Coin", - "symbol": "XCON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vco-voucher-coin", - "name": "Voucher Coin", - "symbol": "VCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tftn-tft-network", - "name": "TFT Network", - "symbol": "TFTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "elet-elementeum", - "name": "Elementeum", - "symbol": "ELET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hlx-hilux", - "name": "Hilux", - "symbol": "HLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xagc-agrocash-x", - "name": "Agrocash X", - "symbol": "xAGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vol-volume-network", - "name": "Volume Network", - "symbol": "VOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ntbc-note-blockchain", - "name": "Note Blockchain", - "symbol": "NTBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ysi-yorkshire-inu", - "name": "Yorkshire Inu", - "symbol": "YSI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "earths-earths", - "name": "Earths", - "symbol": "EARTHS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "icol-icolcoin", - "name": "Icolcoin", - "symbol": "ICOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bqtx-bqt", - "name": "BQT", - "symbol": "BQTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tryl-lirasis-tryl", - "name": "Lirasis TRYL", - "symbol": "TRYL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibemp-shiba-inu-empire", - "name": "SHIBA INU EMPIRE", - "symbol": "SHIBEMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "luck-luck", - "name": "LUCK", - "symbol": "LUCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "toem-toem-token-emerald", - "name": "TOEM / TOKEN EMERALD", - "symbol": "TOEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ixi-ixian", - "name": "IxiCash", - "symbol": "IXI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "peos-peos", - "name": "pEOS", - "symbol": "PEOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atls-atlas-token", - "name": "Atlas Token", - "symbol": "ATLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etfx-ethereum-fx", - "name": "Ethereum Fx", - "symbol": "ETFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "darc-konstellation", - "name": "Konstellation", - "symbol": "DARC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcp-block-commerce-protocol", - "name": "Block Commerce Protocol", - "symbol": "BCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethpad-ethpad", - "name": "ETHPad", - "symbol": "ETHPAD", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xbg-bitgrin", - "name": "Bitgrin", - "symbol": "XBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ben-token-of-szczepan-bentyn", - "name": "Token of Szczepan Bentyn", - "symbol": "BEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xlmg-stellar-gold", - "name": "Stellar Gold", - "symbol": "XLMG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tas-tarush", - "name": "Tarush", - "symbol": "TAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "roket-starbaselon", - "name": "STARBASELON", - "symbol": "ROKET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msc-master-stake-coin", - "name": "Master Stake Coin", - "symbol": "MSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "soci-bank-society-coin", - "name": "Bank Society Coin", - "symbol": "SOCI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dcake-doge-cake", - "name": "Doge Cake", - "symbol": "DCAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "less-lordless-token", - "name": "LORDLESS Token", - "symbol": "LESS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lcrt-lucre", - "name": "LUCRE", - "symbol": "LCRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldc-little-doge-cake", - "name": "Little Doge Cake", - "symbol": "LDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ggg-gigabyte-games", - "name": "Gigabyte Games", - "symbol": "GGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doget-doge-token", - "name": "Doge Token", - "symbol": "DOGET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hi-hi-dollar", - "name": "hi Dollar", - "symbol": "HI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dpt-diamond-platform-token", - "name": "Diamond Platform Token", - "symbol": "DPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "troy-troya-coin", - "name": "TROYA COIN", - "symbol": "TROY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "puppydoge-puppy-doge-coin", - "name": "Puppy Doge Coin", - "symbol": "PUPPYDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dio-decimated", - "name": "Decimated", - "symbol": "DIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dnp-dnp-token", - "name": "DNP Token", - "symbol": "DNP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "muso-muso-finance", - "name": "MUSO Finance", - "symbol": "MUSO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "e2c-electronic-energy-coin", - "name": "Electronic Energy Coin", - "symbol": "E2C", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shitcoin-just-another-shitcoin", - "name": "Just Another Shitcoin", - "symbol": "SHITCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "duo-duo-network-token", - "name": "DUO Network Token", - "symbol": "DUO", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "7e-7eleven", - "name": "7ELEVEN", - "symbol": "7E", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hyt-horyoutoken", - "name": "HoryouToken", - "symbol": "HYT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xgp-gold-power-coin", - "name": "Gold Power Coin", - "symbol": "XGP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dxn-dexon", - "name": "DEXON", - "symbol": "DXN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vollar-v-dimension", - "name": "V-Dimension", - "symbol": "VOLLAR", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "alth-alioth", - "name": "Alioth", - "symbol": "ALTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wopc-work-place-coin", - "name": "Work Place Coin", - "symbol": "WOPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "p2p-p2pcoin-", - "name": "P2PCOIN", - "symbol": "P2P", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xbb-billionbond", - "name": "Billionbond", - "symbol": "XBB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lcnt-lucent", - "name": "Lucent", - "symbol": "LCNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ple-plenteum", - "name": "Plenteum", - "symbol": "PLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nat-natmin", - "name": "Natmin Pure Escrow", - "symbol": "NAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ict-icocalendartoday", - "name": "ICOCalendar.Today", - "symbol": "ICT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pac-pay-arrow-coin", - "name": "Pay Arrow Coin", - "symbol": "PAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zcon-zcon-protocol", - "name": "Zcon Protocol", - "symbol": "ZCON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bby-blocbuy", - "name": "BlocBuy", - "symbol": "BBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ptx-patenttx", - "name": "PatentTX", - "symbol": "PTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "stash-bitstash", - "name": "BitStash", - "symbol": "STASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bth-bithereum", - "name": "Bithereum", - "symbol": "BTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mrp-moneyrebel", - "name": "MoneyRebel", - "symbol": "MRP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goldy-goldy-coin", - "name": "GOLDY COIN", - "symbol": "GOLDY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "suni-starbaseuniverse", - "name": "StarbaseUniverse", - "symbol": "SUNI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bobo-bobo-cash", - "name": "Bobo Cash", - "symbol": "BOBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xlic-lico", - "name": "Lico", - "symbol": "XLIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rf-rf", - "name": "RF", - "symbol": "RF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mfc-mfcoin", - "name": "MFCoin", - "symbol": "MFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jera-jera", - "name": "JERA", - "symbol": "JERA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kngo-kangaroo-token", - "name": "Kangaroo Token", - "symbol": "KNGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "webd-webdollar", - "name": "WebDollar", - "symbol": "WEBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "botx-botxcoin", - "name": "botXcoin", - "symbol": "BOTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bia-bilaxy-token", - "name": "Bilaxy Token", - "symbol": "BIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hrimp-whalestreet-hrimp-token", - "name": "WhaleStreet $hrimp Token", - "symbol": "$HRIMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eses-eskiehir-fan-tokens", - "name": "Eskişehir Fan Tokens", - "symbol": "ESES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "she-sternium-huge-elligence-coin", - "name": "Sternium Huge Elligence coin", - "symbol": "SHE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pbc-profitbase", - "name": "Profitbase", - "symbol": "PBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "evc-eva-cash", - "name": "Eva Cash", - "symbol": "EVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "anka-ankaragc-fan-tokens", - "name": "Ankaragücü Fan Tokens", - "symbol": "ANKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zeon-zeon-", - "name": "Zeon", - "symbol": "ZEON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rft-rangers-fan-token", - "name": "Rangers Fan Token", - "symbol": "RFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "a-alpha-token", - "name": "Alpha Token", - "symbol": "A", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tbft-turkiye-basketbol-federasyonu-token", - "name": "Turkiye Basketbol Federasyonu Token", - "symbol": "TBFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bzky-bizkey", - "name": "BIZKEY", - "symbol": "BZKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrpdoge-xrpdoge", - "name": "XrpDoge", - "symbol": "XRPDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tre-turtle-finance", - "name": "Turtle Finance", - "symbol": "TRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lno-livenodes", - "name": "Livenodes", - "symbol": "LNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hemp-hempora", - "name": "Hempora", - "symbol": "HEMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sky-scopuly", - "name": "Scopuly", - "symbol": "SKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uts-utemis", - "name": "UTEMIS", - "symbol": "UTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inf-infinitus-token", - "name": "Infinitus Token", - "symbol": "INF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gfr-goldenfever", - "name": "GoldenFever", - "symbol": "GFR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tkb-tkbtoken", - "name": "TKBToken", - "symbol": "TKB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "p59-prometheus-59", - "name": "Prometheus 59", - "symbol": "P59", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sfshld-safe-shield", - "name": "Safe Shield", - "symbol": "SFSHLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tengu-tengu", - "name": "Tengu", - "symbol": "TENGU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "texo-texo", - "name": "tEXO", - "symbol": "TEXO", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vhs-vhs-computing", - "name": "VHS Computing", - "symbol": "VHS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lmoose-littlemoose", - "name": "Littlemoose", - "symbol": "LMOOSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbex-cbex-token", - "name": "CBEX TOKEN", - "symbol": "CBEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "genxyz-genxyz-token", - "name": "GenXYZ Token", - "symbol": "GENXYZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wneng-wrapped-nengcoin", - "name": "Wrapped Nengcoin", - "symbol": "WNENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shir-shiro-inu", - "name": "SHIRO INU", - "symbol": "SHIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xcq-executecoin", - "name": "ExecuteCoin", - "symbol": "XCQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fnb-fnb-protocol", - "name": "FNB Protocol", - "symbol": "FNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "beer-beer-inu", - "name": "Beer Inu", - "symbol": "BEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fcl-free-crypto-lotto", - "name": "Free Crypto Lotto", - "symbol": "FCL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alln-airline-amp-life-networking-token", - "name": "Airline \u0026 Life Networking Token", - "symbol": "ALLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oto-otocash", - "name": "OTOCASH", - "symbol": "OTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xtx-xtock", - "name": "Xtock", - "symbol": "XTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "punch-punch", - "name": "Punch", - "symbol": "PUNCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fst-1irstcoin", - "name": "1irstcoin", - "symbol": "FST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obx-ooobtc-token", - "name": "OOOBTC TOKEN", - "symbol": "OBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slb-solar-bankers-coin", - "name": "Solar Bankers Coin", - "symbol": "SLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ms-masternodeinvest", - "name": "MasternodeInvest", - "symbol": "MS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kgs-kingscoin", - "name": "KingsCoin", - "symbol": "KGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hnst-honest", - "name": "Honest", - "symbol": "HNST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bpet-binapet", - "name": "Binapet", - "symbol": "BPET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hesh-heshfi", - "name": "Hesh.Fi", - "symbol": "HESH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "twbt-thewatcherbottoken", - "name": "TheWatcherBotToken", - "symbol": "TWBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "voltz-voltz", - "name": "Voltz", - "symbol": "VOLTZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lec-love-earth-coin", - "name": "Love Earth Coin", - "symbol": "LEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qrs-qrstars", - "name": "QRStars", - "symbol": "QRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hisc-high-stakes-coin", - "name": "High Stakes Coin", - "symbol": "HISC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bse-best-shitcoin-ever", - "name": "Best Shitcoin Ever", - "symbol": "BSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xcy-cypruscoin", - "name": "CyprusCoin", - "symbol": "XCY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hlt-hyperloot", - "name": "Esportbits", - "symbol": "HLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "artis-artis", - "name": "Artis", - "symbol": "ARTIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "winnings-show-me-the-money", - "name": "Show Me The Money", - "symbol": "WINNINGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ire-irent", - "name": "IRENT", - "symbol": "IRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cdoge-choco-doge", - "name": "Choco Doge", - "symbol": "CDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dpp-digital-assets-power-play", - "name": "Digital Assets Power Play", - "symbol": "DPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hot-hotnow", - "name": "HOT Token", - "symbol": "HOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dxg-dexter-g", - "name": "Dexter G", - "symbol": "DXG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mog-mogwaicoin", - "name": "Mogwaicoin", - "symbol": "MOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hotx-hotchain", - "name": "Hotchain", - "symbol": "HOTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jct-japan-content-token", - "name": "Japan Content Token", - "symbol": "JCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kubo-kubocoin", - "name": "KuboCoin", - "symbol": "KUBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bqqq-bitsdaq", - "name": "Bitsdaq", - "symbol": "BQQQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pub-publyto-token", - "name": "PUBLYTO Token", - "symbol": "PUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xfrc-fructus", - "name": "FRUCTUS", - "symbol": "XFRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slc-slice", - "name": "Slice", - "symbol": "SLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shed-shed-coin", - "name": "Shed Coin", - "symbol": "SHED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alcup-alphacup", - "name": "AlphaCup", - "symbol": "ALCUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sapa-sapa", - "name": "Sapa", - "symbol": "SAPA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pubg-pubg-con", - "name": "PUBG COİN", - "symbol": "PUBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "olxa-olxa", - "name": "OLXA", - "symbol": "OLXA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brose-blackrose-con", - "name": "BLACKROSE COİN", - "symbol": "BROSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gal-galore", - "name": "Galore", - "symbol": "GAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "amit-amitycoin", - "name": "AmityCoin", - "symbol": "AMIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dwr-dogewarrior", - "name": "DogeWarrior", - "symbol": "DWR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agpc-agpc", - "name": "AGPC", - "symbol": "AGPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gpt-gopower", - "name": "GoPower", - "symbol": "GPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stfi-startfi", - "name": "StartFi", - "symbol": "STFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dream-dreamteam-token", - "name": "DreamTeam Token", - "symbol": "DREAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dof-door-of-future", - "name": "Door Of Future", - "symbol": "DOF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "info-info-coin", - "name": "INFO Coin", - "symbol": "INFO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qiwiruble-qiwiruble", - "name": "QiwiRuble", - "symbol": "QIWIRUBLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lfg-low-float-gem", - "name": "Low Float Gem", - "symbol": "LFG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slrm-solareum", - "name": "Solareum", - "symbol": "SLRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pton-pton", - "name": "PTON", - "symbol": "PTON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "raijin-raijin", - "name": "Raijin", - "symbol": "RAIJIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "x6-x6coin", - "name": "X6coin", - "symbol": "X6", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zmy-zumy", - "name": "Zumy", - "symbol": "ZMY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xmn-motion", - "name": "Motion", - "symbol": "XMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ntc-natcoin3270", - "name": "Natcoin", - "symbol": "NTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kty-kittykoin", - "name": "KittyKoin", - "symbol": "KTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mymc-msuri", - "name": "Msuri", - "symbol": "MYMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xcom-xcom-pay", - "name": "XCOM PAY", - "symbol": "XCOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fxc-flexacoin", - "name": "Flexacoin", - "symbol": "FXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ine-intellishare", - "name": "IntelliShare", - "symbol": "INE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tsa-teaswap-art", - "name": "Teaswap Art", - "symbol": "TSA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spec-spectrumnetwork", - "name": "SpectrumNetwork", - "symbol": "SPEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "genx-genesis", - "name": "Genesis", - "symbol": "GENX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bzc-bitzec", - "name": "Bitzec", - "symbol": "BZC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lyra-scrypta", - "name": "Scrypta", - "symbol": "LYRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xsc-xscoin", - "name": "XSCOIN", - "symbol": "XSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ubit-ubit-cash", - "name": "Ubit Cash", - "symbol": "UBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kodi-kodiak", - "name": "Kodiak", - "symbol": "KODI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fth-fitcash", - "name": "Fitcash", - "symbol": "FTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogedealer-dogedealer", - "name": "DogeDealer", - "symbol": "DOGEDEALER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gany-ganymede", - "name": "Ganymede", - "symbol": "GANY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dra-diruna", - "name": "Diruna", - "symbol": "DRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scoot-scootercoin", - "name": "ScooterCoin", - "symbol": "SCOOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "const-constant", - "name": "Constant", - "symbol": "CONST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brng-bringfinance", - "name": "bRing.Finance", - "symbol": "BRNG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tmb-tembocoin", - "name": "TemboCoin", - "symbol": "TMB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bankbtc-bank-btc", - "name": "Bank BTC", - "symbol": "BANKBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dway-disease-away", - "name": "Disease Away", - "symbol": "DWAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crcl-crcl-token", - "name": "CRCL Token", - "symbol": "CRCL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lab-tokenlab", - "name": "Tokenlab", - "symbol": "LAB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gptc-gptcash", - "name": "GPTCash", - "symbol": "GPTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xwg-x-world-games", - "name": "X World Games", - "symbol": "XWG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xsm-smilo", - "name": "Smilo", - "symbol": "XSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xts-xaviera-techno-solutions", - "name": "Xaviera Techno Solutions", - "symbol": "XTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ltk-litecoin-token", - "name": "Litecoin Token", - "symbol": "LTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mesg-mesg", - "name": "MESG", - "symbol": "MESG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ptlkx-potluck-protocol-token", - "name": "Potluck Protocol Token", - "symbol": "PTLKX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spdr-spidervps", - "name": "SpiderVPS", - "symbol": "SPDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fyr-fyre", - "name": "FYRE", - "symbol": "FYR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "inception-inu-ception", - "name": "Inu-ception", - "symbol": "INCEPTION", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eddie-eddie-coin", - "name": "Eddie coin", - "symbol": "EDDIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hind-hindcoin", - "name": "Hindcoin", - "symbol": "HIND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hypx-hypnoxys", - "name": "Hypnoxys", - "symbol": "HYPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kbyte-kazubyte", - "name": "KazuByte", - "symbol": "KBYTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "thex-thore-exchange", - "name": "Thore Exchange", - "symbol": "THEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onefuse-onefuse", - "name": "oneFUSE", - "symbol": "ONEFUSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "toll-toll-free-swap", - "name": "Toll Free Swap", - "symbol": "TOLL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "net-next", - "name": "NEXT", - "symbol": "NET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kgld-kazugold", - "name": "KazuGold", - "symbol": "KGLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kslv-kazusilver", - "name": "KazuSilver", - "symbol": "KSLV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kazu-kazucoin", - "name": "Kazucoin", - "symbol": "KAZU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rmx-remex", - "name": "RemeX", - "symbol": "RMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fsh-fusion-heroes", - "name": "Fusion Heroes", - "symbol": "FSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "miac-miacoin", - "name": "Miacoin", - "symbol": "MIAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "het-havethertoken", - "name": "HavEtherToken", - "symbol": "HET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bot-bostravel", - "name": "BosTravel", - "symbol": "BOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bear-voytek-bear-coin", - "name": "Voytek BEAR Coin", - "symbol": "BEAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "acr-acreage", - "name": "ACREAGE", - "symbol": "ACR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "umb-umbracoin", - "name": "UmbraCoin", - "symbol": "UMB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "slot-slot", - "name": "Alphaslot", - "symbol": "SLOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sprkl-sparkle", - "name": "Sparkle", - "symbol": "SPRKL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "beach-beach-token", - "name": "Beach Token", - "symbol": "BEACH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pyro-pyroworld", - "name": "Pyroworld", - "symbol": "PYRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgames-meme-games", - "name": "Meme Games", - "symbol": "MGAMES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vd-bitcoin-card", - "name": "Bitcoin Card", - "symbol": "VD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xpc-xpandacoin", - "name": "XPandaCoin", - "symbol": "XPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ovc-ovcode", - "name": "OVCODE", - "symbol": "OVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cadax-canada-coin", - "name": "Canada Coin", - "symbol": "CADAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aidus-aidus-token", - "name": "AIDUS TOKEN", - "symbol": "AIDUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mot-monopoly-token", - "name": "Monopoly Token", - "symbol": "MoT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onot-onotoken", - "name": "ONOToken", - "symbol": "ONOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hiphop-4hiphop", - "name": "4hiphop", - "symbol": "HIPHOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thx-thorenext", - "name": "ThoreNext", - "symbol": "THX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lbo-labrotix-coin", - "name": "Labrotix Coin", - "symbol": "LBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shillmoon-shillmoon", - "name": "ShillMoon", - "symbol": "SHILLMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xmon-bitcoin-monster", - "name": "Bitcoin Monster", - "symbol": "XMON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "boruto-boruto-inu", - "name": "Boruto Inu", - "symbol": "BORUTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goat-goat-cash", - "name": "Goat Cash", - "symbol": "GOAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gad-garuda-token", - "name": "Garuda Token", - "symbol": "GAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jurm-juriseum", - "name": "Juriseum", - "symbol": "JURM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "twins-winwin", - "name": "win.win", - "symbol": "TWINS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gcash-go-cash-coin", - "name": "Go Cash Coin", - "symbol": "GCASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blur-blur-network", - "name": "Blur Network", - "symbol": "BLUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "3ket-3etiket-3ket", - "name": "3ETIKET 3KET", - "symbol": "3KET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "afn-altafin", - "name": "AltaFin", - "symbol": "AFN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "br20-bullrun20", - "name": "BullRun2.0", - "symbol": "BR2.0", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "krc-kineticex", - "name": "Kineticex", - "symbol": "KRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccn-customcontractnetwork", - "name": "CustomContractNetwork", - "symbol": "CCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etx-ethereumx", - "name": "EthereumX", - "symbol": "ETX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scc-stem-cell-coin", - "name": "STEM CELL COIN", - "symbol": "SCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goku-goku-inu", - "name": "Goku Inu", - "symbol": "GOKU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "egwcap-egw-capital", - "name": "EGW Capital", - "symbol": "EGWCAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "isf-insifa", - "name": "Insifa", - "symbol": "ISF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lasereyes-laser-eyes", - "name": "Laser Eyes", - "symbol": "LASEREYES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vegas-vegasdoge", - "name": "VegasDoge", - "symbol": "VEGAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ncc-netcoincapital", - "name": "Netcoincapital", - "symbol": "NCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grill-grill-farm", - "name": "Grill Farm", - "symbol": "GRILL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "parq-parq", - "name": "PARQ", - "symbol": "PARQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zilla-chainzilla", - "name": "ChainZilla", - "symbol": "ZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xqr-qredit", - "name": "Qredit", - "symbol": "XQR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fln-fline", - "name": "Fline", - "symbol": "FLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cci2-coin-cryptographic-cci2", - "name": "Coin Cryptographic CCI2", - "symbol": "CCI2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lsp20-lisprocoin", - "name": "Lisprocoin", - "symbol": "LSP20", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "regi-regium", - "name": "Regium", - "symbol": "REGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "set-save-environment-token", - "name": "Save Environment Token", - "symbol": "SET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "serv-serve", - "name": "Serve", - "symbol": "SERV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wbtc-worldbtc", - "name": "WorldBTC", - "symbol": "WBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kolin-kolin", - "name": "Kolin", - "symbol": "KOLIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xfe-feirm", - "name": "Feirm", - "symbol": "XFE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wula-wula", - "name": "Wula", - "symbol": "WULA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jnb-jinbi-token", - "name": "Jinbi Token", - "symbol": "JNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mintys-mintyswap", - "name": "MintySwap", - "symbol": "MINTYS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fstar-future-star", - "name": "Future Star", - "symbol": "FSTAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dtc-datecoin", - "name": "DateCoin", - "symbol": "DTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rin-aldrin", - "name": "Aldrin", - "symbol": "RIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mnc-moneynet", - "name": "Moneynet", - "symbol": "MNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pbk-powerbank", - "name": "POWERBANK", - "symbol": "PBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cxgc-cashex-global-coin", - "name": "Cashex Global Coin", - "symbol": "CXGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evy-everycoin", - "name": "EveryCoin", - "symbol": "EVY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "liv-livconect", - "name": "LivConect", - "symbol": "LIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mnpr-mnpro", - "name": "MNPRO", - "symbol": "MNPR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lxt-lexit", - "name": "LEXIT", - "symbol": "LXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pact-pact", - "name": "PACT", - "symbol": "PACT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aunit-aunite", - "name": "Aunite", - "symbol": "AUNIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xlc-ethereum-lite-cash", - "name": "Ethereum Lite Cash", - "symbol": "XLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dca-decentralize-currency-assets", - "name": "Decentralize Currency Assets", - "symbol": "DCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boo-bamboo", - "name": "Bamboo", - "symbol": "BOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pdox-paradox", - "name": "Paradox", - "symbol": "PDOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aoe-apes-of-empires", - "name": "Apes of Empires", - "symbol": "AOE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bper-black-panther", - "name": "BLACK PANTHER", - "symbol": "BPER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "npi-ninja-panda-inu", - "name": "Ninja Panda Inu", - "symbol": "NPI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cfcc-cryptofightclub-coin", - "name": "Crypto FightClub Coin", - "symbol": "CFCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mnbc-mn-browsing-coin", - "name": "MN Browsing coin", - "symbol": "MNBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "creedx-creedx", - "name": "CREEDX", - "symbol": "CREEDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hbc-hybncash", - "name": "HYBNCASH", - "symbol": "HBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "baba-bananababy", - "name": "BananaBaby", - "symbol": "BABA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dane-danecoin", - "name": "Danecoin", - "symbol": "DANE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yoi-yoi-crypto", - "name": "Yoi Crypto", - "symbol": "YOI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trbo-trbo", - "name": "TRBO", - "symbol": "TRBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "macd-macd-finance", - "name": "MACD Finance", - "symbol": "MACD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exr-exserion", - "name": "EXSERION", - "symbol": "EXR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rdct-rdctoken", - "name": "RDCToken", - "symbol": "RDCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ichx-icechain", - "name": "IceChain", - "symbol": "ICHX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adsx-addictsx", - "name": "Addictsx", - "symbol": "ADSX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "altm-altmarkets-coin", - "name": "Altmarkets Coin", - "symbol": "ALTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "777-bingofun", - "name": "BiNGO.Fun", - "symbol": "777", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ecte-eurocointoken", - "name": "EurocoinToken", - "symbol": "ECTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nollar-nollar", - "name": "Nollar", - "symbol": "NOLLAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "babytrump-baby-trump", - "name": "Baby Trump", - "symbol": "BABYTRUMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etm-ethersmart", - "name": "EtherSmart", - "symbol": "ETM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "get-green-energy-token", - "name": "Green Energy Token", - "symbol": "GET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crd-universecreditcoin", - "name": "UniverseCreditcoin", - "symbol": "CRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "diwot-digital-world-travel-chain", - "name": "Digital World Travel Chain", - "symbol": "DIWOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sudo-cryptosudo", - "name": "CryptoSudo", - "symbol": "SUDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qusd-qusd", - "name": "QUSD", - "symbol": "QUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "psiv-phoenixsiv", - "name": "Phoenixsiv", - "symbol": "PSIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "flc-flowchain", - "name": "Flowchain", - "symbol": "FLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blcr-blacer-coin", - "name": "Blacer Coin", - "symbol": "BLCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cbg-chainbing", - "name": "Chainbing", - "symbol": "CBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thundereth-thundereth", - "name": "ThunderETH", - "symbol": "THUNDERETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wfct-wrapped-fct", - "name": "Wrapped FCT", - "symbol": "WFCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "honey-honey-defi", - "name": "Honey Defi", - "symbol": "HONEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dxo-deepspace-token", - "name": "DeepSpace Token", - "symbol": "DXO", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smnr-cryptosummoner", - "name": "Crypto Summoner", - "symbol": "SMNR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blgc-blogcoin", - "name": "BlogCoin", - "symbol": "BLGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hts-hotshot", - "name": "Hotshot", - "symbol": "HTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dyz-dyztoken", - "name": "DyzToken", - "symbol": "DYZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xnos-nos", - "name": "NOS", - "symbol": "XNOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "phifiv2-phifi-finance", - "name": "PhiFi Finance", - "symbol": "PHIFIV2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nawras-nawras", - "name": "NAWRAS", - "symbol": "NAWRAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thoge-thor-doge", - "name": "Thor Doge", - "symbol": "THOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adv2-adevplus", - "name": "AdevPlus", - "symbol": "ADV2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dns-diners", - "name": "Diners", - "symbol": "DNS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cyr-cypher", - "name": "Cypher", - "symbol": "CYR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fgp-fingerprint", - "name": "FingerPrint", - "symbol": "FGP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lyfe-lyfe", - "name": "Lyfe", - "symbol": "LYFE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "img-imagecoin", - "name": "ImageCoin", - "symbol": "IMG", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "gnft-gamenft", - "name": "GameNFT", - "symbol": "GNFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrs-marginless", - "name": "Marginless", - "symbol": "MRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minidogeinu-mini-doge-inu", - "name": "mini DOGE inu", - "symbol": "miniDOGEin", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hcash-hexcash", - "name": "HexCash", - "symbol": "HCASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfh-bigfish", - "name": "BigFish", - "symbol": "BFH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kdc-fandom-chain", - "name": "Fandom Chain", - "symbol": "KDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "stash-bitstash-marketplace", - "name": "BitStash Marketplace", - "symbol": "STASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xn35-projecton", - "name": "Projecton", - "symbol": "XN35", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crunch-crunchy", - "name": "Crunchy", - "symbol": "CRUNCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hodo-holographic-doge", - "name": "Holographic Doge", - "symbol": "HODO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agu-agouti", - "name": "Agouti", - "symbol": "AGU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dbubble-double-bubble", - "name": "Double Bubble", - "symbol": "DBUBBLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcat-the-currency-analytics", - "name": "The Currency Analytics", - "symbol": "TCAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mymn-mymn-coin", - "name": "MyMN coin", - "symbol": "MYMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "oswap-openswap", - "name": "OpenSwap", - "symbol": "OSWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mncx-mooncoinx", - "name": "MooncoinX", - "symbol": "MNCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "izi-izichain", - "name": "IZIChain", - "symbol": "IZI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "halo-halo-platform", - "name": "Halo Platform", - "symbol": "HALO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lupx-lupecoin", - "name": "Lupecoin", - "symbol": "LUPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ignx-ignite-ratings", - "name": "Ignite Ratings", - "symbol": "IGNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dooh-bidooh-dooh-token", - "name": "Bidooh DOOH Token", - "symbol": "DOOH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fflix-futureflix", - "name": "FutureFlix", - "symbol": "FFLIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wlo-wollo", - "name": "WOLLO", - "symbol": "WLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xos-oasis", - "name": "OASIS", - "symbol": "XOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xmrt-monerotoken", - "name": "MoneroToken", - "symbol": "XMRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hash-hash", - "name": "HASH", - "symbol": "HASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mynt-mynt", - "name": "Mynt", - "symbol": "MYNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lmx-lumaxcoin", - "name": "Lumaxcoin", - "symbol": "LMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pad-nearpad", - "name": "NearPad", - "symbol": "PAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "petn-pylon-eco-token", - "name": "Pylon Eco Token", - "symbol": "PETN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rcg-recharge", - "name": "Recharge", - "symbol": "RCG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stew-stewcoin", - "name": "Stewcoin", - "symbol": "STEW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btsl-bitcoin-sleek", - "name": "Bitcoin Sleek", - "symbol": "BTSL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tdw-the-doge-world", - "name": "The Doge World", - "symbol": "TDW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcim-bitcoin-improve", - "name": "Bitcoin Improve", - "symbol": "BCIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aristo-aristo", - "name": "ARISTO", - "symbol": "ARISTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deseo-token-deseo", - "name": "TOKEN DESEO", - "symbol": "DESEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atcp-atccoinplus", - "name": "Atccoinplus", - "symbol": "ATCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gfn-game-fanz", - "name": "Game Fanz", - "symbol": "GFN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "peo-proelio", - "name": "Proeliocoin", - "symbol": "PEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "candy-candy-classic", - "name": "Candy Classic", - "symbol": "CANDY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "astral-astral-diamonds", - "name": "Astral Diamonds", - "symbol": "Astral", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nobt-nobt", - "name": "Nobt", - "symbol": "NOBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "coz-coz", - "name": "COZ", - "symbol": "COZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sdusd-sdusd", - "name": "SDUSD", - "symbol": "SDUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "singer-singer-community-coin", - "name": "Singer Community Coin", - "symbol": "SINGER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "domo-domo", - "name": "Domocoin", - "symbol": "DOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sdd-steadynode", - "name": "SteadyNode", - "symbol": "SDD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "prints-fingerprintsdao", - "name": "Fingerprints DAO", - "symbol": "PRINTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slab-slink-labs", - "name": "Slink Labs", - "symbol": "SLAB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "starx-starcashx", - "name": "StarCashX", - "symbol": "STARX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wib-wibson", - "name": "Wibson", - "symbol": "WIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tm-tokenmom", - "name": "Tokenmom", - "symbol": "TM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bchc-bcash-classic-token", - "name": "BCash Classic token", - "symbol": "BCHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "move-marketmove", - "name": "MarketMove", - "symbol": "MOVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brigadeiro-brigadeiro-finance", - "name": "Brigadeiro Finance", - "symbol": "BRIGADEIRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swt-smart-wallet-token", - "name": "Smart Wallet Token", - "symbol": "SWT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethw-ethereum-wizard", - "name": "Ethereum wizard", - "symbol": "ETHW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "art-around-network", - "name": "Around Network", - "symbol": "ART", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "saturn-saturn-dao-token", - "name": "Saturn DAO Token", - "symbol": "SATURN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nxb-nxboost", - "name": "NXBoost", - "symbol": "NXB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "scudo-scudocash", - "name": "ScudoCash", - "symbol": "SCUDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ntro-intro-token", - "name": "INTRO TOKEN", - "symbol": "NTRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pbc-parinita-bansode-coin", - "name": "Parinita Bansode Coin", - "symbol": "PBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clb-cloudbric", - "name": "Cloudbric", - "symbol": "CLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xlb-stellarpay", - "name": "StellarPay", - "symbol": "XLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tbtc-tidbit-coin", - "name": "Tidbit Coin", - "symbol": "TBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bct-bitcoin-classic-token", - "name": "Bitcoin Classic Token", - "symbol": "BCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uxt-unity-classic-token", - "name": "Unity classic token", - "symbol": "UXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibush-shibush", - "name": "Shibush", - "symbol": "SHIBUSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buba-burgerbaby", - "name": "BurgerBaby", - "symbol": "BUBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uxet-unity-eth-token", - "name": "Unity ETH token", - "symbol": "UXET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pla-plair", - "name": "Plair", - "symbol": "PLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onex-onex-network", - "name": "ONEX Network", - "symbol": "ONEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fscp-five-star-coin-pro", - "name": "Five Star Coin Pro", - "symbol": "FSCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpc-cashpayz-token", - "name": "Cashpayz Token", - "symbol": "CPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "varius-varius", - "name": "VARIUS", - "symbol": "VARIUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "stish-stish", - "name": "Stish", - "symbol": "stish", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orox-cointorox", - "name": "Cointorox", - "symbol": "OROX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "c4l-crypto4likes", - "name": "Crypto4Likes", - "symbol": "C4L", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xba-bitcoin-air", - "name": "Bitcoin Air", - "symbol": "XBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gmc-geimcoin", - "name": "Geimcoin", - "symbol": "GMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alti-altillycoin", - "name": "AltillyCoin", - "symbol": "ALTI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "almn-allmasternodes", - "name": "AllMasternodes", - "symbol": "ALMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fifty-fiftyonefifty", - "name": "FIFTYONEFIFTY", - "symbol": "FIFTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tik-chronobase", - "name": "ChronoBase", - "symbol": "TIK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "city-city-coin", - "name": "City Coin", - "symbol": "CITY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rfg-refugecoin", - "name": "RefugeCoin", - "symbol": "RFG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sxy-sexycam", - "name": "SexyCam", - "symbol": "SXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fts-fantasy-tales", - "name": "Fantasy Tales", - "symbol": "FTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alb-albos", - "name": "ALBOS", - "symbol": "ALB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dxr-dexter", - "name": "DEXTER", - "symbol": "DXR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tlt-trip-leverage-token", - "name": "Trip Leverage Token", - "symbol": "TLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cht-countinghouse", - "name": "Countinghouse", - "symbol": "CHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "im-intelligent-mining", - "name": "Intelligent Mining", - "symbol": "IM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chtr-coinhunters", - "name": "Coinhunters", - "symbol": "CHTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vty-verity", - "name": "Verity", - "symbol": "VTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mpg-mpg", - "name": "MPG", - "symbol": "MPG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minicomm-minicomm", - "name": "MINICOMM", - "symbol": "MINICOMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bigsb-bigshortbets", - "name": "BigShortBets", - "symbol": "BigSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xbx-bitex", - "name": "Bitex", - "symbol": "XBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tnd-thousand-token", - "name": "Thousand Token", - "symbol": "TND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crv-crowdvilla-ownership", - "name": "Crowdvilla Ownership", - "symbol": "CRV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evereth-evereth", - "name": "EverETH", - "symbol": "EVERETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lmo-lumeneo", - "name": "Lumeneo", - "symbol": "LMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shk-shrek", - "name": "Shrek", - "symbol": "SHK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dzoo-dogezoo", - "name": "DogeZoo", - "symbol": "DZOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dcto-decentralized-crypto-token", - "name": "Decentralized Crypto Token", - "symbol": "DCTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "m2-m2", - "name": "M2", - "symbol": "M2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orm-orium", - "name": "ORIUM", - "symbol": "ORM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kmc-koumei", - "name": "Koumei", - "symbol": "KMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brix-brixcoin", - "name": "BrixCoin", - "symbol": "BRIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nac-namitrade", - "name": "Nami.Trade", - "symbol": "NAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gnait-gnait", - "name": "GNAIT", - "symbol": "GNAIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yuan-yuan", - "name": "YUAN", - "symbol": "YUAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tlc-tl-coin", - "name": "TL Coin", - "symbol": "TLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "woc-worldwoc", - "name": "WorldWoc", - "symbol": "WOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wag-wagyuswap", - "name": "WagyuSwap", - "symbol": "WAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etnxp-electronero-pulse", - "name": "Electronero Pulse", - "symbol": "ETNXP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "minisports-minisports-token", - "name": "MiniSports Token", - "symbol": "MINISPORTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "777-777bingo", - "name": "777.Bingo", - "symbol": "777", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "okboomer-okboomer", - "name": "OKBoomer", - "symbol": "OKBOOMER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "more-legends-room", - "name": "Legends Room", - "symbol": "MORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "glc-glacier", - "name": "Glacier", - "symbol": "GLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rtc-rijent-coin", - "name": "RIJENT COIN", - "symbol": "RTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gex-genex", - "name": "Genex", - "symbol": "GEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dca-dcaex", - "name": "DCAex", - "symbol": "DCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metamars-metamars", - "name": "MetaMars", - "symbol": "METAMARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oas-oases-chain", - "name": "Oases Chain", - "symbol": "OAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdt-blockonix-token", - "name": "Blockonix Token", - "symbol": "BDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "afro-afro", - "name": "Afro", - "symbol": "AFRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "reosc-reosc-ecosystem", - "name": "REOSC Ecosystem", - "symbol": "REOSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cht-countinghouse-fund", - "name": "Countinghouse Fund", - "symbol": "CHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brzx-braziliex-token", - "name": "Braziliex Token", - "symbol": "BRZX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drct-drc-token", - "name": "DRC Token", - "symbol": "DRCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lvt-lives-token", - "name": "Lives Token", - "symbol": "LVT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bchip-bluechips-token", - "name": "BLUECHIPS Token", - "symbol": "BCHIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldg-ledgit", - "name": "Ledgit", - "symbol": "LDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "npc-npcoin", - "name": "NPCoin", - "symbol": "NPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eosish-eosish", - "name": "EOSISH", - "symbol": "EOSISH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lme-lme-token", - "name": "LME Token", - "symbol": "LME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cpl-coinplace", - "name": "CoinPlace", - "symbol": "CPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nmt-new-media-technology", - "name": "New Media Technology", - "symbol": "NMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "foli-flower-of-life", - "name": "Flower of Life", - "symbol": "FOLI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "4ac-4a-coin", - "name": "4A Coin", - "symbol": "4AC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swtc-swtc", - "name": "SWTC", - "symbol": "SWTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "edra-edra", - "name": "EDRA", - "symbol": "EDRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tits-safetits", - "name": "SafeTits", - "symbol": "TITS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebkc-ekkoblock", - "name": "Ekkoblock", - "symbol": "EBKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yld-yield-coin", - "name": "Yield Coin", - "symbol": "YLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gtmr-getmoder", - "name": "GETModer", - "symbol": "GTMR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ald-aladdin-dao", - "name": "Aladdin DAO", - "symbol": "ALD", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aep-agricultural-ecology-protocol", - "name": "Agricultural Ecology Protocol", - "symbol": "AEP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "octc-octcoin", - "name": "OCTCOIN", - "symbol": "OCTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "maskdoge-maskdoge", - "name": "MaskDoge", - "symbol": "MASKDOGE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "adapad-adapad", - "name": "ADAPad", - "symbol": "ADAPAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grms-grms", - "name": "GRMS", - "symbol": "GRMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dogedrinks-dogedrinks", - "name": "DogeDrinks", - "symbol": "DOGEDRINKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "et-endless-game-token", - "name": "Endless Game Token", - "symbol": "ET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hhh-healthy-happy-harmony", - "name": "Healthy Happy Harmony", - "symbol": "HHH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bak-baconcoin", - "name": "BaconCoin", - "symbol": "BAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "satc-satoshicoin", - "name": "Satoshicoin", - "symbol": "SATC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aaa-aaachain", - "name": "AAAchain", - "symbol": "AAA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "csn-clean-solutions", - "name": "Clean Solutions", - "symbol": "CSN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kec-keyco", - "name": "Keyco", - "symbol": "KEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "elet-elementium-token", - "name": "Elementium token", - "symbol": "ELET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ivo-iveryone", - "name": "iVeryOne", - "symbol": "IVO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dsc-dreamscape", - "name": "Dreamscape", - "symbol": "DSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wthc-withcoin", - "name": "Withcoin", - "symbol": "WTHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gbc-game-bank-coin", - "name": "Game Bank Coin", - "symbol": "GBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bnl-business-chain", - "name": "Business Chain", - "symbol": "BNL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sgc-stargram-coin", - "name": "Stargram Coin", - "symbol": "SGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "chihua-chihuahuax", - "name": "Chihuahuax", - "symbol": "CHIHUA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fi-finsur-token", - "name": "FInsur Token", - "symbol": "FI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cnsc-crown-service-coin", - "name": "Crown Service Coin", - "symbol": "CNSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eon-eos-network", - "name": "EOS Network", - "symbol": "EON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pesp-pesp-coin", - "name": "PESP COIN", - "symbol": "PESP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "joker-jokerfarm", - "name": "JokerFarm", - "symbol": "JOKER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pvb-points-value-bank", - "name": "Points Value Bank", - "symbol": "PVB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hdrx-hydroxyde", - "name": "hydroxyde", - "symbol": "HDRX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wbt-whalesburg", - "name": "Whalesburg", - "symbol": "WBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcnx-tercet-network", - "name": "Tercet Network", - "symbol": "TCNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gmci-game-city", - "name": "Game City", - "symbol": "GMCI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "amt-ameitoken", - "name": "AMeiToken", - "symbol": "AMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dcnt-decenturion", - "name": "Decenturion", - "symbol": "DCNT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "avak-avakus", - "name": "Avakus", - "symbol": "AVAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inf-infbundle", - "name": "INFBUNDLE", - "symbol": "INF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vgw-vegawallet-token", - "name": "VegaWallet Token", - "symbol": "VGW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tkc-tan-ke", - "name": "TAN-KE", - "symbol": "TKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pinada-pinada", - "name": "PINADA", - "symbol": "PINADA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rvl-ravencoin-lite", - "name": "Ravencoin Lite", - "symbol": "RVL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vit-vitalcoin", - "name": "Vitalcoin", - "symbol": "VIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xht-xhunter", - "name": "xHunter", - "symbol": "XHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qcss-quality-control-safety-system", - "name": "Quality Control Safety System", - "symbol": "QCSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgg-mud-guild-game", - "name": "MUD Guild Game", - "symbol": "MGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "charles-cardano-planet", - "name": "Cardano Planet", - "symbol": "CHARLES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iftc-internet-fintech-coin", - "name": "Internet Fintech Coin", - "symbol": "IFTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hotc-hotchain", - "name": "HOTchain", - "symbol": "HOTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokipup-flokipup-inu", - "name": "FlokiPup Inu", - "symbol": "FLOKIPUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lct-liquor-chain", - "name": "Liquor chain", - "symbol": "LCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "red-health-retail-chain", - "name": "Health Retail Chain", - "symbol": "RED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "add-eosadd", - "name": "EOSADD", - "symbol": "ADD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swl-swiftlance-token", - "name": "Swiftlance Token", - "symbol": "SWL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hey-healthbeautychain", - "name": "HealthBeautyChain", - "symbol": "HEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thm-themis-chain", - "name": "Themis Chain", - "symbol": "THM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ultra-ultrasafe", - "name": "UltraSafe", - "symbol": "ULTRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "planetdoge-planet-doge", - "name": "Planet Doge", - "symbol": "PLANETDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scc-softchain", - "name": "SoftChain", - "symbol": "SCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gex-globex", - "name": "Globex", - "symbol": "GEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plg-plug", - "name": "Plug", - "symbol": "PLG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ehc-ecosystem-health-chain", - "name": "Ecosystem Health Chain", - "symbol": "EHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rik-rik-coin", - "name": "Rik Coin", - "symbol": "RIK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ske-super-keep-token", - "name": "Super Keep Token", - "symbol": "SKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "caj-cajutel", - "name": "Cajutel", - "symbol": "CAJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ivc-invoice-coin", - "name": "Invoice Coin", - "symbol": "IVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cbt-crebit", - "name": "Crebit", - "symbol": "CBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "acce-accel", - "name": "Accel", - "symbol": "ACCE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xycc-xycchain", - "name": "XYCChain", - "symbol": "XYCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tkgn-token-guard", - "name": "Token Guard", - "symbol": "TKGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "neal-coineal-token", - "name": "Coineal Token", - "symbol": "NEAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pccm-poseidon-chain", - "name": "Poseidon Chain", - "symbol": "PCCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xoxo-bitxoxo", - "name": "Bitxoxo", - "symbol": "XOXO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buc-bit-union-coin", - "name": "Bit Union Coin", - "symbol": "BUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brt-berith-token", - "name": "Berith Token", - "symbol": "BRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fewgo-fewmans-gold", - "name": "Fewmans Gold", - "symbol": "FEWGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ul-uselink-chain", - "name": "Uselink chain", - "symbol": "UL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cet-chaince-token", - "name": "Chaince Token", - "symbol": "CET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unc-unigame", - "name": "UniGame", - "symbol": "UNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "carx-carchain", - "name": "CarChain", - "symbol": "CARX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sctk-shareschain", - "name": "SharesChain", - "symbol": "SCTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yard-solyard-finance", - "name": "Solyard Finance", - "symbol": "YARD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ish-interlude", - "name": "Interlude", - "symbol": "ISH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zxt-zhixin-chain", - "name": "Zhixin Chain", - "symbol": "ZXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sead-seadex", - "name": "SEADEX", - "symbol": "SEAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xnl-chronicle", - "name": "Chronicle", - "symbol": "XNL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sunc-sunchain", - "name": "Sunchain", - "symbol": "SUNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yb-yb", - "name": "YB", - "symbol": "YB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gram-opengram", - "name": "OpenGram", - "symbol": "GRAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ccp-cryptocoinpay", - "name": "CryptoCoinPay", - "symbol": "CCP", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zpay-zantepay", - "name": "Zantepay", - "symbol": "ZPAY", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rsx-raisex-token", - "name": "Raisex Token", - "symbol": "RSX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uic-unicorn", - "name": "Unicorn", - "symbol": "UIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ben-ben", - "name": "BEN", - "symbol": "BEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fundz-fundfantasy", - "name": "FundFantasy", - "symbol": "FUNDZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onot-ono", - "name": "ONO", - "symbol": "ONOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yoke-yoke-chat", - "name": "Yoke Chat", - "symbol": "YOKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pmd-pyramid", - "name": "Pyramid", - "symbol": "PMD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mall-mbc-block", - "name": "MBC Block", - "symbol": "MALL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alza-alza", - "name": "Alza", - "symbol": "ALZA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdc-bei-dou-chain", - "name": "Bei Dou Chain", - "symbol": "BDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bstk-blockchain-source-token", - "name": "Blockchain Source Token", - "symbol": "BSTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mcs-magic-stone-fund", - "name": "Magic Stone Fund", - "symbol": "MCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cic-cloud-insurance-chain", - "name": "Cloud-Insurance Chain", - "symbol": "CIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fgc-foundgame", - "name": "FoundGame", - "symbol": "FGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gooc-goocoin", - "name": "GooCoin", - "symbol": "GOOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rea-reactor", - "name": "Reactor", - "symbol": "REA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "togen-togen", - "name": "ToGen", - "symbol": "TOGEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deku-deku-inu", - "name": "Deku Inu", - "symbol": "DEKU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zing-zing-token", - "name": "Zing Token", - "symbol": "ZING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "diy-diychain", - "name": "DIYChain", - "symbol": "DIY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ust-universal-shield-token", - "name": "Universal Shield Token", - "symbol": "UST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nucleus-nucleus", - "name": "Nucleus", - "symbol": "NUCLEUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "neb-nebula", - "name": "Nebula", - "symbol": "NEB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sw-super-wallet", - "name": "Super Wallet", - "symbol": "SW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hero-hero-floki", - "name": "Hero Floki", - "symbol": "HERO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dag-dagx", - "name": "DAGX", - "symbol": "DAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "penc-pencecoin", - "name": "Pencecoin", - "symbol": "PENC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rya-ryacoin", - "name": "RYAcoin", - "symbol": "RYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pps-prophet-set", - "name": "Prophet Set", - "symbol": "PPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bean-bean", - "name": "Bean", - "symbol": "BEAN", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "abo-abo", - "name": "ABO", - "symbol": "ABO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jic-joorschain", - "name": "JoorsChain", - "symbol": "JIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fc-feicoin", - "name": "FeiCoin", - "symbol": "FC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tip-top-intellectual-point", - "name": "Top Intellectual Point", - "symbol": "TIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gfc-gfc-gold-coin", - "name": "GFC Gold Coin", - "symbol": "GFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jbc-japan-brand-coin", - "name": "Japan Brand Coin", - "symbol": "JBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lch-litecoincash", - "name": "LitecoinCash", - "symbol": "LCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "glx-galaxer", - "name": "Galaxer", - "symbol": "GLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "acg-art-chain-global", - "name": "Art Chain Global", - "symbol": "ACG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jex-jex-token", - "name": "Jex Token", - "symbol": "JEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pok-pokerio", - "name": "Poker.io", - "symbol": "POK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "clisting-cryptolistingcom", - "name": "Cryptolisting.com", - "symbol": "CLISTING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tat-leblock", - "name": "Leblock", - "symbol": "TAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aunit-aunit", - "name": "Aunit", - "symbol": "AUNIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btmc-bit-miner-chain", - "name": "Bit Miner Chain", - "symbol": "BTMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "miami-miami", - "name": "MIAMI", - "symbol": "MIAMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "888-888-infinity", - "name": "888 Infinity", - "symbol": "888", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thpc-texas-hold39em-poker-chain", - "name": "Texas Hold'em Poker Chain", - "symbol": "THPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dregg-dragon-egg", - "name": "Dragon Egg", - "symbol": "DREGG", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "it-idc-token", - "name": "IDC Token", - "symbol": "IT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mit-mundellian-infrastructure-technology", - "name": "Mundellian Infrastructure Technology", - "symbol": "MIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mato-matocol-protocol", - "name": "Matocol Protocol", - "symbol": "MATO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tclb-tclb", - "name": "TCLB", - "symbol": "TCLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ddm-ddmcoin", - "name": "DDMCoin", - "symbol": "DDM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "spaceverse-spaceverse", - "name": "SpaceVerse", - "symbol": "SpaceVerse", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "csw-wolfdog", - "name": "WolfDog", - "symbol": "CSW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "osch-open-source-chain", - "name": "Open Source Chain", - "symbol": "OSCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "viat-viabtc-token", - "name": "ViaBTC Token", - "symbol": "VIAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mvt-the-movement", - "name": "The Movement", - "symbol": "MVT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "px-platformx", - "name": "PlatformX", - "symbol": "PX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "delc-delink", - "name": "Delink", - "symbol": "DELC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "baic-baic", - "name": "BAIC", - "symbol": "BAIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pnc-parallel-network", - "name": "Parallel network", - "symbol": "PNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rit-right-media-group", - "name": "Right Media Group", - "symbol": "RIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ccc-coin-controller-cash", - "name": "Coin Controller Cash", - "symbol": "CCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "olga-olga-token", - "name": "OLGA Token", - "symbol": "OLGA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ct-coinmex-token", - "name": "CoinMex Token", - "symbol": "CT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doc-doc", - "name": "Doc", - "symbol": "DOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vres-virtual-reality-electronic-sports", - "name": "Virtual Reality Electronic Sports", - "symbol": "VRES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bec-beauty-chain", - "name": "Beauty Chain", - "symbol": "BEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ido-idexo-token", - "name": "Idexo Token", - "symbol": "IDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ggc-gramgold-coin", - "name": "GramGold Coin", - "symbol": "GGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brc-bryllite", - "name": "Bryllite", - "symbol": "BRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spg-super-gold", - "name": "Super Gold", - "symbol": "SPG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qos-qos-chain", - "name": "QOS Chain", - "symbol": "QOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kami-kamiland", - "name": "Kamiland", - "symbol": "KAMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hada-hodlada", - "name": "HodlADA", - "symbol": "HADA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kiz-kizunacoin", - "name": "KIZUNACOIN", - "symbol": "KIZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cent-centurion-inu", - "name": "Centurion Inu", - "symbol": "CENT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smile-smile-coin", - "name": "Smile Coin", - "symbol": "SMILE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hor-hours-chain", - "name": "Hours Chain", - "symbol": "HOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ngot-ngot", - "name": "ngot", - "symbol": "NGOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "symm-symmetric", - "name": "Symmetric", - "symbol": "SYMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yue-yue-chain", - "name": "Yue Chain", - "symbol": "YUE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "skyft-skyfchain", - "name": "SKYFchain", - "symbol": "SKYFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hpc-hash-power-capital", - "name": "Hash Power Capital", - "symbol": "HPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tch-tchain", - "name": "Tchain", - "symbol": "TCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bds-borderless", - "name": "Borderless", - "symbol": "BDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eai-ethereumai", - "name": "EthereumAI", - "symbol": "EAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pyx-priorityex", - "name": "PriorityEx", - "symbol": "PYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bth-bitcoin-hot", - "name": "Bitcoin Hot", - "symbol": "BTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brc-bearchain", - "name": "BearChain", - "symbol": "BRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cfc-chain-finance", - "name": "Chain Finance", - "symbol": "CFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "byt-bytex-token", - "name": "Bytex Token", - "symbol": "BYT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brn-burn-coin", - "name": "Burn Coin", - "symbol": "BRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hkdt-hkd-tether", - "name": "HKD Tether", - "symbol": "HKDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tavitt-tavittcoin", - "name": "Tavittcoin", - "symbol": "TAVITT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cr-eos-chrome", - "name": "EOS Chrome", - "symbol": "CR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fat-fatcoin", - "name": "Fatcoin", - "symbol": "FAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "idhub-idhub", - "name": "IDHUB", - "symbol": "IDHUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drink-drinkchain", - "name": "DrinkChain", - "symbol": "DRINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lx-lunarx", - "name": "LunarX", - "symbol": "LX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pornfans-pornfans", - "name": "PornFans", - "symbol": "PORNFANS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "czo-carbonzero", - "name": "CarbonZero", - "symbol": "CZO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cai-cai-token", - "name": "Cai Token", - "symbol": "CAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zat-zatgo", - "name": "Zatgo", - "symbol": "ZAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cam-consumption-avatar-matrix", - "name": "Consumption Avatar Matrix", - "symbol": "CAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "del-delchain", - "name": "DelChain", - "symbol": "DEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ida-ida", - "name": "IDA", - "symbol": "IDA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hokage-hokage-inu", - "name": "Hokage Inu", - "symbol": "HOKAGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ytc-yibitcoin", - "name": "Yibitcoin", - "symbol": "YTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ascc-all-star-community-coin", - "name": "All Star Community Coin", - "symbol": "ASCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smartup-smartup", - "name": "Smartup", - "symbol": "SMARTUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ggt-global-gold-token", - "name": "Global Gold Token", - "symbol": "GGT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dali-dalichain", - "name": "Dalichain", - "symbol": "DALI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dove-dovecoin", - "name": "Dovecoin", - "symbol": "DOVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swc-scanetchain", - "name": "Scanetchain", - "symbol": "SWC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thrust-thrust", - "name": "THRUST", - "symbol": "THRUST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1sg-1sg", - "name": "1SG", - "symbol": "1SG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uzumaki-uzumaki-inu", - "name": "Uzumaki Inu", - "symbol": "UZUMAKI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cec-conservation-of-energy-chain", - "name": "Conservation of Energy Chain", - "symbol": "CEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lqc-line-quality-chain", - "name": "Line quality chain", - "symbol": "LQC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "balpac-baby-alpaca", - "name": "Baby Alpaca", - "symbol": "BALPAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cnet-currency-network", - "name": "Currency Network", - "symbol": "CNET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ybct-ybcoin", - "name": "Ybcoin", - "symbol": "YBCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nfty-nifty-token", - "name": "Nifty Token", - "symbol": "NFTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pea-prelax", - "name": "Prelax", - "symbol": "PEA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kop-bitkop-token", - "name": "BitKop Token", - "symbol": "KOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qtes-qtes", - "name": "QTES", - "symbol": "QTES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rolc-renewal-of-life-chain", - "name": "Renewal of Life Chain", - "symbol": "ROLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctx-centauri", - "name": "Centauri", - "symbol": "CTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lumber-lumber", - "name": "LUMBER", - "symbol": "LUMBER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gmbc-gamblica", - "name": "Gamblica", - "symbol": "GMBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obsr-observer", - "name": "Observer", - "symbol": "OBSR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pcr-paycore", - "name": "Paycore", - "symbol": "PCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "marc-market-arbitrage-coin", - "name": "Market Arbitrage Coin", - "symbol": "MARC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "quot-quotation-projects", - "name": "QUOTATION PROJECTS", - "symbol": "QUOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "oath-oath-protocol", - "name": "Oath Protocol", - "symbol": "OATH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gbt-globatalent", - "name": "Globatalent", - "symbol": "GBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inx-inmax", - "name": "INMAX", - "symbol": "INX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "armd-armada", - "name": "Armada", - "symbol": "ARMD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "m2o-m2o", - "name": "M2O", - "symbol": "M2O", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "husl-the-husl", - "name": "The HUSL", - "symbol": "HUSL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tend-tendon", - "name": "Tendon", - "symbol": "TEND", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctzn-ctzn", - "name": "CTZN", - "symbol": "CTZN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pdg-private-datagram", - "name": "Private DataGram", - "symbol": "PDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "euk-eukex-coin", - "name": "Eukex Coin", - "symbol": "EUK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chopper-chopper-inu", - "name": "Chopper Inu", - "symbol": "CHOPPER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vnt-vanta-token", - "name": "Vanta Token", - "symbol": "VNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ante-tronbet", - "name": "TRONbet", - "symbol": "ANTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "play-play", - "name": "PLAY", - "symbol": "PLAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rey-rey", - "name": "REY", - "symbol": "REY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rickmorty-rick-and-morty", - "name": "Rick And Morty", - "symbol": "RICKMORTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dice-trondice", - "name": "TRONdice", - "symbol": "DICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bet-dicebet", - "name": "DiceBet", - "symbol": "BET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fun-tronfun-token", - "name": "TronFun Token", - "symbol": "FUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "win-tronwin", - "name": "TronWin", - "symbol": "WIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "twj-tronweeklyjournal", - "name": "Tron Weekly Journal", - "symbol": "TWJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nezuko-nezuko-inu", - "name": "Nezuko Inu", - "symbol": "NEZUKO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcw-mocrow", - "name": "Mocrow", - "symbol": "MCW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b2g-bitcoiin", - "name": "Bitcoiin", - "symbol": "B2G", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cvnt-content-value-network", - "name": "Content Value Network", - "symbol": "CVNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mox-mox", - "name": "MoX", - "symbol": "MOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crowd-crowdvilla-point", - "name": "Crowdvilla Point", - "symbol": "CROWD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aen-aencoin", - "name": "Aencoin", - "symbol": "AEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sdr-solidar", - "name": "Solidar", - "symbol": "SDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wcf-world-crypto-forum", - "name": "World Crypto Forum", - "symbol": "WCF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "snpc-snapcoin", - "name": "SnapCoin", - "symbol": "SNPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cent-centercoin", - "name": "CENTERCOIN", - "symbol": "CENT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ibt-icobay", - "name": "ICOBay", - "symbol": "IBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "roco-roiyal-coin", - "name": "ROIyal Coin", - "symbol": "ROCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ctt-cryptoinvest", - "name": "Cryptoinvest", - "symbol": "CTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "altc-altecoin", - "name": "Altecoin", - "symbol": "ALTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "roi-roi-coin", - "name": "ROI Coin", - "symbol": "ROI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btcc-bitcoincore", - "name": "BitcoinCore", - "symbol": "BTCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kze-almeela", - "name": "Almeela", - "symbol": "KZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fxp-fxpay", - "name": "FXPay", - "symbol": "FXP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metanaut-metanaut", - "name": "Metanaut", - "symbol": "METANAUT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "liquid-netkoin-liquid", - "name": "Netkoin Liquid", - "symbol": "LIQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swipp-swipp", - "name": "Swipp", - "symbol": "SWIPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rom-romtoken", - "name": "ROMToken", - "symbol": "ROM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wco-winco", - "name": "Winco", - "symbol": "WCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prx-proxynode", - "name": "ProxyNode", - "symbol": "PRX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ade-adeptio", - "name": "Adeptio", - "symbol": "ADE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "usc-usdcoin", - "name": "USDCoin", - "symbol": "USC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyyoda-grogufinance", - "name": "Grogu.Finance", - "symbol": "BABYYODA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ftn-fountain", - "name": "Fountain", - "symbol": "FTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "herc-hercules", - "name": "Hercules", - "symbol": "HERC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brtk-battleroyaletoken", - "name": "BattleRoyaleToken", - "symbol": "BRTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aqua-aquafi", - "name": "AquaFi", - "symbol": "AQUA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fiii-fiii", - "name": "Fiii", - "symbol": "FIII", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hlix-helix", - "name": "Helix", - "symbol": "HLIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xrup-rupees", - "name": "RUPEES", - "symbol": "XRUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hn-hellenic-node", - "name": "Hellenic Node", - "symbol": "HN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "k2x-katana", - "name": "Katana", - "symbol": "K2X", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brc-baer-chain", - "name": "Baer Chain", - "symbol": "BRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "next-nextexchange", - "name": "Next.exchange", - "symbol": "NEXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zum-zumcoin", - "name": "ZUM COIN", - "symbol": "ZUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mario-super-mario", - "name": "SUPER MARIO", - "symbol": "MARIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frm-ferrum-network-ico", - "name": "Ferrum Network", - "symbol": "FRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sone-s-one-finance", - "name": "S-ONE Finance", - "symbol": "SONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyhmng-baby-hmng", - "name": "Baby Hmng", - "symbol": "BABYHMNG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zija-zijacoin", - "name": "Zijacoin", - "symbol": "ZIJA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xfc-footballcoin", - "name": "Footballcoin", - "symbol": "XFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cnus-coinus", - "name": "CoinUs", - "symbol": "CNUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shvr-shivers", - "name": "Shivers", - "symbol": "SHVR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xorn-xorn", - "name": "Xorn", - "symbol": "XORN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "evi-eagle-vision-ada", - "name": "Eagle Vision ADA", - "symbol": "EVI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccbc-crypto-cash-back", - "name": "Crypto Cash Back", - "symbol": "CCBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tena-tena", - "name": "TENA", - "symbol": "TENA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tiox-trade-token-x", - "name": "Trade Token X", - "symbol": "TIOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "genie-genie-protocol", - "name": "Genie Protocol", - "symbol": "GENIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "echt-e-chat", - "name": "e-Chat", - "symbol": "ECHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eli-elicoin", - "name": "Elicoin", - "symbol": "ELI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cms-cryptomoonshots", - "name": "CryptoMoonShots", - "symbol": "CMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shell-shell-token", - "name": "Shell Token", - "symbol": "SHELL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tnpc-the-new-public-coin", - "name": "THE NEW PUBLIC COIN", - "symbol": "TNPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usds-stronghold-usd", - "name": "Stronghold USD", - "symbol": "USDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tret-tourist-review-token", - "name": "Tourist Review Token", - "symbol": "TRET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zomb-zombie-rising", - "name": "Zombie Rising", - "symbol": "ZOMB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pxg-playgame-token", - "name": "PlayGame", - "symbol": "PXG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpt-contents-protocol", - "name": "Contents Protocol", - "symbol": "CPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bid-blockbid-token", - "name": "Blockbid Token", - "symbol": "BID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "skch-skychain", - "name": "Skychain", - "symbol": "SKCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dlb-dlb-token", - "name": "DLB token", - "symbol": "DLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xsc-hyperspace", - "name": "Hyperspace", - "symbol": "XSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mvs-moviestar", - "name": "MovieStar", - "symbol": "MVS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tofu-tofu-coin", - "name": "Tofu Coin", - "symbol": "TOFU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frag-fragcash", - "name": "FragCash", - "symbol": "FRAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eqli-equaliser", - "name": "Equaliser", - "symbol": "EQLI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lrm-lrm-coin", - "name": "LRM Coin", - "symbol": "LRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "seko-sekopay", - "name": "Sekopay", - "symbol": "SEKO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sp-supro", - "name": "SUPRO", - "symbol": "SP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aethb-ankr-reward-earning-staked-eth", - "name": "Ankr Reward-Earning Staked ETH", - "symbol": "AETHB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "znd-zenad", - "name": "Zenad", - "symbol": "ZND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "grbe-green-beli", - "name": "Green Beli", - "symbol": "GRBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bkc-bankcoin-cash", - "name": "Bankcoin Cash", - "symbol": "BKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "odin-odin", - "name": "ODIN Blockchain", - "symbol": "ODIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cheese-cheesefry", - "name": "CheeseFry", - "symbol": "CHEESE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "p2p-p2pcoin", - "name": "P2PCOIN", - "symbol": "P2P", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pk-pennykoin", - "name": "Pennykoin", - "symbol": "PK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "oio-online", - "name": "Online", - "symbol": "OIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sprtz-spritzcoin", - "name": "SpritzCoin", - "symbol": "SPRTZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokin-flokinomics", - "name": "Flokinomics", - "symbol": "FLOKIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inti-inticoin", - "name": "IntiCoin", - "symbol": "INTI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cedars-cedartoken", - "name": "CedarToken", - "symbol": "CEDARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sero-sero", - "name": "SERO", - "symbol": "SERO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bp-beyond-protocol", - "name": "Beyond Protocol", - "symbol": "BP", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btnt-bitnautic-token", - "name": "BitNautic Token", - "symbol": "BTNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssx-stake-share", - "name": "Stake Share", - "symbol": "SSX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cbbn-cbbn-token", - "name": "CBBN Token", - "symbol": "CBBN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ourc-crypto-union", - "name": "Crypto Union", - "symbol": "OURC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "beat-beat", - "name": "BEAT", - "symbol": "BEAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stacs-stacs", - "name": "STACS", - "symbol": "STACS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zex-zaddex", - "name": "Zaddex", - "symbol": "ZEXO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bih-bithostcoin", - "name": "BitHostCoin", - "symbol": "BIH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bcac-business-credit-alliance-chain", - "name": "Business Credit Alliance Chain", - "symbol": "BCAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scol-scolcoin", - "name": "Scolcoin", - "symbol": "SCOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vox-volix", - "name": "Volix", - "symbol": "VOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bdoge-bitdoge", - "name": "BitDoge", - "symbol": "BDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lito-lituni", - "name": "Lituni", - "symbol": "LITO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amas-amasa", - "name": "Amasa", - "symbol": "AMAS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tou-touristoken", - "name": "TOURISTOKEN", - "symbol": "TOU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "udoo-howdoo", - "name": "Howdoo", - "symbol": "UDOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vites-vites", - "name": "Vites", - "symbol": "VITES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tgch-truegalaxycash", - "name": "TrueGalaxyCash", - "symbol": "TGCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ntc-nactuxcash", - "name": "NactuxCash", - "symbol": "NTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vnx-venox", - "name": "VENOX", - "symbol": "VNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "athr-authora", - "name": "Authora", - "symbol": "ATHR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tdoge-technodoge", - "name": "Technodoge", - "symbol": "TDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dei-deimos", - "name": "Deimos", - "symbol": "DEI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "grm-greenmoon", - "name": "GreenMoon", - "symbol": "GRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aht-angelheart-token", - "name": "AngelHeart Token", - "symbol": "AHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shb-skyhub-coin", - "name": "SkyHub Coin", - "symbol": "SHB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tdg-toydoge", - "name": "ToyDOGE", - "symbol": "TDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ecot-echo-tech-coin", - "name": "Echo Tech Coin", - "symbol": "ECOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "anyan-avanyan", - "name": "AvaNyan", - "symbol": "ANYAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "archa-archangel-token", - "name": "ArchAngel Token", - "symbol": "ARCHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "klc-kalkicoin", - "name": "Kalkicoin", - "symbol": "KLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shmn-stronghands-masternode", - "name": "StrongHands Masternode", - "symbol": "SHMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hum-humanscape", - "name": "Humanscape", - "symbol": "HUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "octps-octopus", - "name": "Octopus", - "symbol": "OCTPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "osa-optimal-shelf-availability-token", - "name": "Optimal Shelf Availability Token", - "symbol": "OSA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gali-galilel", - "name": "Galilel", - "symbol": "GALI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ncc-needscoin", - "name": "NeedsCoin", - "symbol": "NCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cdzc-cryptodezirecash", - "name": "Crypto Dezire Cash", - "symbol": "CDZC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "atd-atidium", - "name": "Atidium", - "symbol": "ATD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bzl-bzlcoin", - "name": "BZLCOIN", - "symbol": "BZL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btx-bitcoin-x", - "name": "Bitcoin X", - "symbol": "BTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cryp-crypticcoin", - "name": "CrypticCoin", - "symbol": "CRYP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aias-aias-coin", - "name": "AIAS Coin", - "symbol": "AIAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "poss-posscoin", - "name": "Posscoin", - "symbol": "POSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wet-weshow-token", - "name": "WeShow Token", - "symbol": "WET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttv-tv-two", - "name": "TV-TWO", - "symbol": "TTV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apot-apot", - "name": "APOT", - "symbol": "APOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lpdi-lucky-property-development-invest", - "name": "Lucky Property Development Invest", - "symbol": "LPDI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qbs-qbase", - "name": "Qbase", - "symbol": "QBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lpk-kripton", - "name": "Kripton", - "symbol": "LPK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "messe-messe-token", - "name": "MESSE TOKEN", - "symbol": "MESSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hobo-hobonickels", - "name": "HoboNickels", - "symbol": "HOBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adec-blockade", - "name": "Blockade", - "symbol": "ADEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "paws-paws-fund", - "name": "PAWS Fund", - "symbol": "PAWS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xbul-thebullockchain", - "name": "TheBullockchain", - "symbol": "XBUL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mfit-mfit-coin", - "name": "MFIT COIN", - "symbol": "MFIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "snd-snodecoin", - "name": "SnodeCoin", - "symbol": "SND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "subx-sub-invest", - "name": "Sub Invest", - "symbol": "SUBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "luto-luto-cash", - "name": "Luto Cash", - "symbol": "LUTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brzc-breezecoin", - "name": "Breezecoin", - "symbol": "BRZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dex-dex", - "name": "DEX", - "symbol": "DEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vnx-visionx", - "name": "VisionX", - "symbol": "VNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dac-dacash", - "name": "DACash", - "symbol": "DAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "encp-enceladus", - "name": "Enceladus", - "symbol": "ENCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ionc-ionchain", - "name": "IONChain", - "symbol": "IONC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btb-bitibu-coin", - "name": "Bitibu Coin", - "symbol": "BTB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sierra-sierracoin", - "name": "Sierracoin", - "symbol": "SIERRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "botto-botto", - "name": "Botto", - "symbol": "BOTTO", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "stor-self-storage-coin", - "name": "Self Storage Coin", - "symbol": "STOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "joker-joker-coin", - "name": "Joker Coin", - "symbol": "JOKER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mol-molecule", - "name": "Molecule", - "symbol": "MOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "type-typerium", - "name": "Typerium", - "symbol": "TYPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibarmy-shib-army", - "name": "Shib Army", - "symbol": "SHIBARMY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sml-smile", - "name": "Smile", - "symbol": "SML", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "soul-cryptosoul", - "name": "CryptoSoul", - "symbol": "SOUL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "brg-bring", - "name": "Bring", - "symbol": "BRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sake-sakecoin", - "name": "SAKECOIN", - "symbol": "SAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rock-rocketcoin", - "name": "Rocketcoin", - "symbol": "ROCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xcd-capdaxtoken", - "name": "CapdaxToken", - "symbol": "XCD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtrc-modultrade", - "name": "ModulTrade", - "symbol": "MTRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rkc-rookiecoin", - "name": "Rookiecoin", - "symbol": "RKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "2go-cointogo", - "name": "CoinToGo", - "symbol": "2GO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "esce-escroco-emerald", - "name": "Escroco Emerald", - "symbol": "ESCE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pedi-pedity", - "name": "Pedity", - "symbol": "PEDI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ezw-ezoow", - "name": "EZOOW", - "symbol": "EZW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nms-nemesis", - "name": "Nemesis", - "symbol": "NMS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vegi-vegeta-inu", - "name": "Vegeta Inu", - "symbol": "VEGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ddx-dietbitcoin", - "name": "dietbitcoin", - "symbol": "DDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wemp-women-empowerment-token", - "name": "Women Empowerment Token", - "symbol": "WEMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "modx-model-x-coin", - "name": "MODEL-X-coin", - "symbol": "MODX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "omen-omencoin", - "name": "OmenCoin", - "symbol": "OMEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shibx-shibx", - "name": "ShibX", - "symbol": "$SHIBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deliz-delizia", - "name": "Delizia", - "symbol": "DELIZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "obx-obxcoin", - "name": "OBXcoin", - "symbol": "OBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yeed-yggdrash", - "name": "YGGDRASH", - "symbol": "YEED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bit-bitmoney", - "name": "BitMoney", - "symbol": "BIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pmc-primebank-coin", - "name": "Primebank Coin", - "symbol": "PMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gameone-gameonetoken", - "name": "GameoneToken", - "symbol": "GAMEONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "insta-instaraise", - "name": "Instaraise", - "symbol": "INSTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hlm-helium", - "name": "Helium", - "symbol": "HLM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jse-jsecoin", - "name": "JSECOIN", - "symbol": "JSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rpi-rpicoin", - "name": "RPICoin", - "symbol": "RPI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "per-per-token", - "name": "PER TOKEN", - "symbol": "PER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ixe-ixtus-edutainment", - "name": "IXTUS Edutainment", - "symbol": "IXE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msd-metasoldier", - "name": "MetaSoldier", - "symbol": "MSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bits-bitcoinus", - "name": "Bitcoinus", - "symbol": "BITS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "robin-nico-robin-inu", - "name": "Nico Robin Inu", - "symbol": "ROBIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "midas-midas", - "name": "Midas", - "symbol": "MIDAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ply-playcoin-erc20", - "name": "PlayCoin [ERC20]", - "symbol": "PLY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deex-deex", - "name": "DEEX", - "symbol": "DEEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cjs-cjs", - "name": "CJs", - "symbol": "CJS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dweb-decentraweb", - "name": "DecentraWeb", - "symbol": "DWEB", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "szc-shopzcoin", - "name": "ShopZcoin", - "symbol": "SZC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cdc-commerce-data-connection", - "name": "Commerce Data Connection", - "symbol": "CDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nanoshiba-nano-shiba-inu", - "name": "Nano Shiba Inu", - "symbol": "NANOSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpex-coinpulse-token", - "name": "CoinPulse Token", - "symbol": "CPEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lma-lamoneda", - "name": "Lamoneda", - "symbol": "LMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smq-simdaq", - "name": "SIMDAQ", - "symbol": "SMQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deal-idealcash", - "name": "iDealCash", - "symbol": "DEAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ponzi-bernie-madoff", - "name": "Bernie Madoff", - "symbol": "PONZI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wire-airwire", - "name": "AirWire", - "symbol": "WIRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "huzu-huzu", - "name": "HUZU", - "symbol": "HUZU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pcooom-incooom-genesis-psychedelic", - "name": "Incooom Genesis Psychedelic", - "symbol": "PCOOOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sinu-sasuke-inu", - "name": "Sasuke Inu", - "symbol": "SINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xind-indinode", - "name": "INDINODE", - "symbol": "XIND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "quid-quid-ika", - "name": "Quid Ika", - "symbol": "QUID", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "page-page", - "name": "Page", - "symbol": "PAGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moona-ms-moona-rewards", - "name": "Ms Moona Rewards", - "symbol": "MOONA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alvn-alvarenet", - "name": "AlvareNet", - "symbol": "ALVN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ndb-nodebase", - "name": "NodeBase", - "symbol": "NDB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dec-darico-ecosystem-coin", - "name": "Darico Ecosystem Coin", - "symbol": "DEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "soom-soom", - "name": "SOOM", - "symbol": "SOOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "radar-radar", - "name": "Radar", - "symbol": "RADAR", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "rptc-reptilian", - "name": "Reptilian", - "symbol": "RPTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sure-surety", - "name": "SURETY", - "symbol": "SURE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bc-block-chaincom", - "name": "Block-Chain.com", - "symbol": "BC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cobra-cobrabytes", - "name": "Cobrabytes", - "symbol": "COBRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dogetwin-dogetwincoin", - "name": "DogeTwinCoin", - "symbol": "DOGETWIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aec-emaratcoin", - "name": "EmaratCoin", - "symbol": "AEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dcm-dogecakemoon", - "name": "DogeCakeMoon", - "symbol": "DCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inco-incodium", - "name": "Incodium", - "symbol": "INCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tuna-tunacoin", - "name": "TUNACOIN", - "symbol": "TUNA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "s-sharpay", - "name": "Sharpay", - "symbol": "S", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ndoge-naughtydoge", - "name": "NaughtyDoge", - "symbol": "NDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zoro-zoro-inu", - "name": "Zoro Inu", - "symbol": "ZORO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kkc-kabberrycoin", - "name": "Kabberry Coin", - "symbol": "KKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jiyox-jiyo", - "name": "JIYO", - "symbol": "JIYOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "trgo-trgold", - "name": "TrGold", - "symbol": "TRGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aglt-agrolot", - "name": "Agrolot", - "symbol": "AGLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shping-shping", - "name": "SHPING", - "symbol": "SHPING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prsn-persona", - "name": "Persona", - "symbol": "PRSN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bitto-bitto", - "name": "BITTO", - "symbol": "BITTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dlx-diplexcoin", - "name": "Diplexcoin", - "symbol": "DLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pndm-pandemia", - "name": "Pandemia", - "symbol": "PNDM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bitant-bitant", - "name": "BitANT", - "symbol": "BITANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pyx-pyrexcoin", - "name": "GoldenPyrex", - "symbol": "GPYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "babycare-babycare", - "name": "BabyCare", - "symbol": "BABYCARE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "zeus-zeusnetwork", - "name": "ZeusNetwork", - "symbol": "ZEUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bczero-buggyra-coin-zero", - "name": "Buggyra Coin Zero", - "symbol": "BCZERO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wage-digiwage", - "name": "Digiwage", - "symbol": "WAGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "evos-evos", - "name": "EVOS", - "symbol": "EVOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bsx-bitspace", - "name": "Bitspace", - "symbol": "BSX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sim-simmitri", - "name": "Simmitri", - "symbol": "SIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "egx-eaglex", - "name": "EagleX", - "symbol": "EGX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mana-genesis-mana", - "name": "Genesis Mana", - "symbol": "MANA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xfloki-spacex-floki", - "name": "SpaceX Floki", - "symbol": "XFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "x12-x12-coin", - "name": "X12 Coin", - "symbol": "X12", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ifood-ifoods-chain", - "name": "Ifoods Chain", - "symbol": "IFOOD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tails-tails", - "name": "Tails", - "symbol": "TAILS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wsp-wispr", - "name": "Wispr", - "symbol": "WSP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wix-wixlar", - "name": "Wixlar", - "symbol": "WIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shade-shade-token", - "name": "SHADE Token", - "symbol": "SHADE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kai-kaicoin", - "name": "kaicoin", - "symbol": "KAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "idg-indigen", - "name": "INDIGEN", - "symbol": "IDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "magicdoge-magicdoge", - "name": "MagicDOGE", - "symbol": "MAGICDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "quan-quantis-net", - "name": "Quantis Network", - "symbol": "QUAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "steep-steepcoin", - "name": "SteepCoin", - "symbol": "STEEP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "our-ourcoin", - "name": "Ourcoin", - "symbol": "OUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "benz-benz", - "name": "Benz", - "symbol": "BENZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pickle-mr-pickles", - "name": "Mr. Pickles", - "symbol": "PICKLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tol-tolar", - "name": "Tolar", - "symbol": "TOL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "fox-smartfox", - "name": "SmartFox", - "symbol": "FOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mash-masternet", - "name": "MASTERNET", - "symbol": "MASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xg-giga", - "name": "GIGA", - "symbol": "XG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fegn-fegnomics", - "name": "Fegnomics", - "symbol": "FEGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "civ-civitas", - "name": "Civitas", - "symbol": "CIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "floki-flokitoken", - "name": "FlokiToken", - "symbol": "FLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shubh-shubh", - "name": "Shubh", - "symbol": "SHUBH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pass-wisepass", - "name": "Wisepass", - "symbol": "PASS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wbl-wizbl", - "name": "WIZBL", - "symbol": "WBL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "432-432", - "name": "432", - "symbol": "432", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "imp-imperial-obelisk", - "name": "Imperial Obelisk", - "symbol": "IMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "labh-labh-coin", - "name": "Labh Coin", - "symbol": "LABH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bcard-cardbuyers", - "name": "CARDbuyers", - "symbol": "BCARD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xgs-genesisx", - "name": "GenesisX", - "symbol": "XGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ragna-ragnarok", - "name": "Ragnarok", - "symbol": "RAGNA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rpm-repme", - "name": "Repme", - "symbol": "RPM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ner-nerves", - "name": "Nerves", - "symbol": "NER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crd-crdnetwork", - "name": "CRD Network", - "symbol": "CRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hci-innovasis-hotels", - "name": "Innovasis Hotels", - "symbol": "HCI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cplo-cpollo", - "name": "VeriSafe", - "symbol": "VSF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ert-eristica", - "name": "Eristica", - "symbol": "ERT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "azart-azart", - "name": "Azart", - "symbol": "AZART", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "well-well", - "name": "WELL", - "symbol": "WELL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mas-midasprotocol", - "name": "MidasProtocol", - "symbol": "MAS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "trxc-tronclassic", - "name": "TRONCLASSIC", - "symbol": "TRXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lln-lunaland", - "name": "LunaLand", - "symbol": "LLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minds-minds", - "name": "Minds", - "symbol": "MINDS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mlc-mallcoin", - "name": "Mallcoin", - "symbol": "MLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbf-stonkbase", - "name": "StonkBase", - "symbol": "SBF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "phon-phonecoin", - "name": "Phonecoin", - "symbol": "PHON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vstr-vestoria", - "name": "Vestoria", - "symbol": "VSTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pikachu-pikachu-inu", - "name": "Pikachu Inu", - "symbol": "PIKACHU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ecoreal-ecoreal-estate", - "name": "Ecoreal Estate", - "symbol": "ECOREAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dachx-dach-coin", - "name": "Dach Coin", - "symbol": "DACHX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mnd-mind-music", - "name": "Mind Music", - "symbol": "MND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thanos-thanos", - "name": "THANOS", - "symbol": "THANOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tankz-cryptotankz", - "name": "CryptoTankz", - "symbol": "TANKZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pgt-puregold-token", - "name": "Puregold Token", - "symbol": "PGTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shard-shard", - "name": "Shard", - "symbol": "SHARD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "paxex-paxex", - "name": "PAXEX", - "symbol": "PAXEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sol-sola-token", - "name": "Sola Token", - "symbol": "SOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goss-gossipcoin", - "name": "Gossip Coin", - "symbol": "GOSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gld-goldario", - "name": "Goldario", - "symbol": "GLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cmit-cmitcoin", - "name": "CMITCOIN", - "symbol": "CMIT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "che-crypto-harbor-exchange", - "name": "Crypto Harbor Exchange", - "symbol": "CHE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boxx-boxx-token-blockparty", - "name": "Blockparty (BOXX Token)", - "symbol": "BOXX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "upc-upcake", - "name": "UpCake", - "symbol": "UPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sml-super-music-league", - "name": "Super Music League", - "symbol": "SML", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chiz-sewer-rat-social-club-chiz-token", - "name": "Sewer Rat Social Club CHIZ Token", - "symbol": "CHIZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcn-tcoin", - "name": "TCOIN", - "symbol": "TCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mdc-mars-dogecoin", - "name": "Mars DogeCoin", - "symbol": "MDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gonewild-gonewild", - "name": "Gonewild", - "symbol": "GONEWILD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hwi-hawaii", - "name": "Hawaii", - "symbol": "HWI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "home-homecoin", - "name": "HomeCoin", - "symbol": "HOME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aolb-argentum-online-libre-token-b", - "name": "Argentum Online Libre Token B", - "symbol": "AOLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokielon-floki-elon", - "name": "Floki Elon", - "symbol": "FLOKIELON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lkn-linkcoin-token", - "name": "LinkCoin Token", - "symbol": "LKN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evi-evimeria", - "name": "Evimeria", - "symbol": "EVI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cstl-castle", - "name": "Castle", - "symbol": "CSTL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "foofight-fruit-fighters", - "name": "Fruit Fighters", - "symbol": "FOOFIGHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alc-allcoin", - "name": "ALLCOIN", - "symbol": "ALC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xcg-xchange", - "name": "Xchange", - "symbol": "XCG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ccc-concierge-coin", - "name": "Concierge Coin", - "symbol": "CCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "flns-falcon-swaps", - "name": "Falcon Swaps", - "symbol": "FLNS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "molk-mobilinktoken", - "name": "MobilinkToken", - "symbol": "MOLK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrt-xrt-token", - "name": "XRT Token", - "symbol": "XRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "diginu-diginu", - "name": "DIGINU", - "symbol": "DIGINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bff-bffdoom", - "name": "BFFDoom", - "symbol": "BFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yo-yorocket", - "name": "YoRocket", - "symbol": "$YO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "urals-uralscoin", - "name": "UralsCoin", - "symbol": "URALS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crbt-cruisebit", - "name": "Cruisebit", - "symbol": "CRBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ihf-invictus-hyperion-fund", - "name": "Invictus Hyperion Fund", - "symbol": "IHF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bodav2-boda-token", - "name": "BODA Token", - "symbol": "BODAV2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "edn-eden", - "name": "Eden", - "symbol": "EDN", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "mac-matrexcoin", - "name": "Matrexcoin", - "symbol": "MAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lvlup-level-up", - "name": "Level Up", - "symbol": "LVLUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbet-moonbet", - "name": "MoonBet", - "symbol": "MBET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "udog-united-doge-finance", - "name": "United Doge Finance", - "symbol": "UDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "traid-traid", - "name": "Traid", - "symbol": "TRAID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "olmp-olympic", - "name": "Olympic", - "symbol": "OLMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gene-gene-source-code-chain", - "name": "Gene Source Code Chain", - "symbol": "GENE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnm-binanomics", - "name": "Binanomics", - "symbol": "BNM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cit-carinet", - "name": "CariNet", - "symbol": "CIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bu-bumo", - "name": "BUMO", - "symbol": "BU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "elli-elliot-coin", - "name": "Elliot Coin", - "symbol": "ELLI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aaa-abulaba", - "name": "Abulaba", - "symbol": "AAA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btxc-bettex-coin", - "name": "Bettex Coin", - "symbol": "BTXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ben-bitcoen", - "name": "BitCoen", - "symbol": "BEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "daxt-digital-asset-exchange-token", - "name": "Digital Asset Exchange Token", - "symbol": "DAXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gnr-gainer", - "name": "Gainer", - "symbol": "GNR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ret-realtract", - "name": "RealTract", - "symbol": "RET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gatsbyinu-gatsby-inu", - "name": "Gatsby Inu", - "symbol": "GATSBYINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qntu-quanta-utility-token", - "name": "Quanta Utility Token", - "symbol": "QNTU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kishimoto-kishimoto-inu", - "name": "Kishimoto Inu", - "symbol": "KISHIMOTO", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "knuckles-knuckles", - "name": "KNUCKLES", - "symbol": "KNUCKLES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sno-savenode", - "name": "SaveNode", - "symbol": "SNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blast-blastoise-inu", - "name": "Blastoise Inu", - "symbol": "BLAST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zest-zest", - "name": "ZEST", - "symbol": "ZEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sunrise-the-sun-rises", - "name": "The Sun Rises", - "symbol": "SUNRISE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpmk-chipmonk-coin", - "name": "ChipMonk Coin", - "symbol": "CPMK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vsc-vsportcoin", - "name": "vSportCoin", - "symbol": "VSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "c8-carboneum-c8-token", - "name": "Carboneum [C8] Token", - "symbol": "C8", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "c2p-coin2play", - "name": "Coin2Play", - "symbol": "C2P", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vvmc-virtual-venture-media-coin", - "name": "Virtual Venture Media Coin", - "symbol": "VVMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cif-crypto-improvement-fund", - "name": "Crypto Improvement Fund", - "symbol": "CIF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dart-darextravel", - "name": "DarexTravel", - "symbol": "DART", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cup-cryptup", - "name": "CryptUP", - "symbol": "CUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prj-project-coin", - "name": "Project Coin", - "symbol": "PRJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gze-gazecoin", - "name": "GazeCoin", - "symbol": "GZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mri-mirai", - "name": "Mirai", - "symbol": "MRI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btr-bitether", - "name": "Bitether", - "symbol": "BTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpro-cpro", - "name": "CPRO", - "symbol": "CPRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mic-mindexcoin", - "name": "Mindexcoin", - "symbol": "MIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zba-zoomba", - "name": "Zoomba", - "symbol": "ZBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tsc-thunderstake", - "name": "Thunderstake", - "symbol": "TSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lucy-lucy-inu", - "name": "Lucy Inu", - "symbol": "LUCY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ali-ailink-token", - "name": "AiLink Token", - "symbol": "ALI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "misty-misty-inu", - "name": "Misty Inu", - "symbol": "MISTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ichigo-ichigo-inu", - "name": "Ichigo Inu", - "symbol": "ICHIGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ubc-ubcoin-market", - "name": "Ubcoin Market", - "symbol": "UBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pun-cryptopunt", - "name": "CryptoPunt", - "symbol": "PUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gft-globfone-coin", - "name": "Globfone Coin", - "symbol": "GFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "peng-penguin-coin", - "name": "PENG", - "symbol": "PENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xn-nodium", - "name": "Nodium", - "symbol": "XN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eship-eliteshippertoken", - "name": "EliteShipperToken", - "symbol": "ESHIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "str-staker", - "name": "Staker", - "symbol": "STR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mojov2-mojo-v2", - "name": "MOJO V2", - "symbol": "MOJOV2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flm-folmcoin", - "name": "FolmCoin", - "symbol": "FLM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "state-new-world-order", - "name": "New World Order", - "symbol": "STATE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "at-abcc-token", - "name": "ABCC Token", - "symbol": "AT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibin-shibanomics", - "name": "Shibanomics", - "symbol": "SHIBIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "esn-ethersocial", - "name": "Ethersocial", - "symbol": "ESN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tmc-timicoin", - "name": "Timicoin", - "symbol": "TMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "win-wintoken", - "name": "WinToken", - "symbol": "WIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pdx-payday-coin", - "name": "PayDay Coin", - "symbol": "PDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cen-centaure", - "name": "Centaure", - "symbol": "CEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shwa-shibawallet", - "name": "ShibaWallet", - "symbol": "SHWA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnc-bionic", - "name": "Bionic", - "symbol": "BNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dnz-adenz", - "name": "Adenz", - "symbol": "DNZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qbit-qubitica", - "name": "Qubitica", - "symbol": "QBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "erabbit-elons-rabbit", - "name": "ELONs RABBIT", - "symbol": "ERABBIT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cst-cryptosolartech", - "name": "Cryptosolartech", - "symbol": "CST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btn-bitnewchain", - "name": "BitNewChain", - "symbol": "BTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vulc-vulcano", - "name": "VULCANO", - "symbol": "VULC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yukon-yukon", - "name": "Yukon", - "symbol": "YUKON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitf-bitf", - "name": "BitF", - "symbol": "BITF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ablx-able", - "name": "ABLE", - "symbol": "ABLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "delta-deltachain", - "name": "DeltaChain", - "symbol": "DELTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "know-know", - "name": "KNOW", - "symbol": "KNOW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "more-mithril-ore", - "name": "Mithril Ore", - "symbol": "MORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldf-londefy", - "name": "Londefy", - "symbol": "LDF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "soniq-soniq", - "name": "Soniq", - "symbol": "SONIQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yuki-yuki", - "name": "YUKI", - "symbol": "YUKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grph-graphcoin", - "name": "Graphcoin", - "symbol": "GRPH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "biotech-biotech-tokens", - "name": "Biotech Tokens", - "symbol": "BIOTECH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctc-credit-tag-chain", - "name": "Credit Tag Chain", - "symbol": "CTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cotn-cottoncoin", - "name": "CottonCoin", - "symbol": "COTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "usd-n-usd-n", - "name": "USD-N", - "symbol": "USD-N", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vdg-veridocglobal", - "name": "VeriDocGlobal", - "symbol": "VDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sinu-scam-inu", - "name": "Scam Inu", - "symbol": "SINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prtx-printex", - "name": "Printex", - "symbol": "PRTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rbmc-rubex-money", - "name": "Rubex Money", - "symbol": "RBMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qno-qyno", - "name": "QYNO", - "symbol": "QNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cbc-cashbery-coin", - "name": "Cashbery Coin", - "symbol": "CBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rto2-roto2coin", - "name": "Roto2coin", - "symbol": "RTO2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "niko-niko", - "name": "NIKO", - "symbol": "NIKO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "obt-orbis-token", - "name": "Orbis Token", - "symbol": "OBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rtl-rentledger", - "name": "Rentledger", - "symbol": "RTL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zp-zen-protocol", - "name": "Zen Protocol", - "symbol": "ZP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "scusd-scientix-usd", - "name": "Scientix USD", - "symbol": "SCUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nftt-nft-tech", - "name": "NFT Tech", - "symbol": "NFTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dfs-defense", - "name": "Defense", - "symbol": "DFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ect-superedge", - "name": "SuperEdge", - "symbol": "ECT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "twist-twist", - "name": "TWIST", - "symbol": "TWIST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rrc-rrcoin", - "name": "RRCoin", - "symbol": "RRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mozo-mozo-token", - "name": "Mozo Token", - "symbol": "MOZO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rdc-ordocoin", - "name": "Ordocoin", - "symbol": "RDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mftu-mainstream-for-the-underground", - "name": "Mainstream For The Underground", - "symbol": "MFTU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doge0-dogezero", - "name": "DogeZero", - "symbol": "DOGE0", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "seal-seal-network", - "name": "Seal Network", - "symbol": "SEAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "altx-alttex", - "name": "Alttex", - "symbol": "ALTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lky-linkey", - "name": "Linkey", - "symbol": "LKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sc2-securecloudcoin", - "name": "SecureCloudCoin", - "symbol": "SC2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sgp-sgpay", - "name": "SGPay", - "symbol": "SGP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "troller-the-troller-coin", - "name": "The Troller Coin", - "symbol": "TROLLER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ven-vechain-token", - "name": "Vechain [Token]", - "symbol": "VEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bws-bitcoin-w-spectrum", - "name": "Bitcoin W Spectrum", - "symbol": "BWS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rich-werichfinance", - "name": "WeRich.Finance", - "symbol": "RICH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokig-flokigravity", - "name": "FlokiGravity", - "symbol": "FLOKIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kitty-kitty-inu", - "name": "Kitty Inu", - "symbol": "KITTY", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "talao-talao", - "name": "Talao", - "symbol": "TALAO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swc-sw-dao", - "name": "SW DAO", - "symbol": "SWC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gsonic-gold-sonic", - "name": "Gold Sonic", - "symbol": "GSONIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tdc-trendercoin", - "name": "Trendercoin", - "symbol": "TDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sac-smart-application-chain", - "name": "Smart Application Chain", - "symbol": "SAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pinu-piccolo-inu", - "name": "Piccolo Inu", - "symbol": "PINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hyb-hybrid-block", - "name": "Hybrid Block", - "symbol": "HYB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "advar-advar-protocol", - "name": "Advar Protocol", - "symbol": "ADVAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lgs-logiscoin", - "name": "LogisCoin", - "symbol": "LGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "food-foodcoin", - "name": "FoodCoin", - "symbol": "FOOD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "weiup-weiup", - "name": "WeiUp", - "symbol": "WEIUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "edu-open-source-university", - "name": "Open Source University", - "symbol": "EDU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ifp-infinipay", - "name": "Infinipay", - "symbol": "IFP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ylc-yolocash", - "name": "YoloCash", - "symbol": "YLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "svd-savedroid", - "name": "savedroid", - "symbol": "SVD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tkt-twinkle", - "name": "Twinkle", - "symbol": "TKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "quartz-sandclock", - "name": "Sandclock", - "symbol": "QUARTZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cedex-cedex-coin", - "name": "CEDEX Coin", - "symbol": "CEDEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hyc-hycon", - "name": "HYCON", - "symbol": "HYC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pco-pecunio", - "name": "Pecunio", - "symbol": "PCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obtc-obitan-chain", - "name": "Obitan Chain", - "symbol": "OBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "noku-noku", - "name": "Noku", - "symbol": "NOKU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "saja-saja", - "name": "Saja", - "symbol": "SAJA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frrn-ferron", - "name": "Ferron", - "symbol": "FRRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "baas-baasid", - "name": "BaaSid", - "symbol": "BAAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ntk-netkoin", - "name": "NetKoin", - "symbol": "NTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xdna-xdna", - "name": "XDNA", - "symbol": "XDNA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dgs-dragonglass", - "name": "Dragonglass", - "symbol": "DGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fork-forkcoin", - "name": "Forkcoin", - "symbol": "FORK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xet-eternal-token", - "name": "ETERNAL TOKEN", - "symbol": "XET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sds-alchemint-standards", - "name": "Alchemint Standards", - "symbol": "SDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pshib-pixel-shiba-inu", - "name": "Pixel Shiba Inu", - "symbol": "PSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "latino-latino-token", - "name": "Latino Token", - "symbol": "LATINO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "set-swytch-energy-token", - "name": "Swytch Energy Token", - "symbol": "SET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "akamaru-akamaru-inu", - "name": "Akamaru Inu", - "symbol": "AKAMARU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abdt-atlantis-blue-digital-token", - "name": "Atlantis Blue Digital Token", - "symbol": "ABDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kki-kakashiinuv2", - "name": "KakashiInuV2", - "symbol": "KKI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "newos-newstoken", - "name": "NewsToken", - "symbol": "NEWOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ff-fantasy-fox", - "name": "Fantasy Fox", - "symbol": "FF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rgs-rusgas", - "name": "RusGas", - "symbol": "RGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "own-owndata", - "name": "OWNDATA", - "symbol": "OWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fiat-floki-adventure", - "name": "Floki Adventure", - "symbol": "FIAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boc-bingocoin", - "name": "BingoCoin", - "symbol": "BOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aqua-aquachain", - "name": "Aquachain", - "symbol": "AQUA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gic-giant", - "name": "Giant", - "symbol": "GIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "scr-scorum-coins", - "name": "Scorum Coins", - "symbol": "SCR", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "coin" - }, - { - "id": "xpat-bitnation", - "name": "Bitnation", - "symbol": "XPAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ust-ultra-salescloud", - "name": "Ultra Salescloud", - "symbol": "UST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "opti-optitoken", - "name": "OptiToken", - "symbol": "OPTI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slt-social-lending-token", - "name": "Social Lending Token", - "symbol": "SLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "charizard-charizard-inu", - "name": "Charizard Inu", - "symbol": "CHARIZARD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exc-excaliburcoin", - "name": "Excaliburcoin", - "symbol": "EXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mxm-maximine-coin", - "name": "Maximine Coin", - "symbol": "MXM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nug-nuggets", - "name": "Nuggets", - "symbol": "NUG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cmm-commercium", - "name": "Commercium", - "symbol": "CMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "meliodas-meliodas", - "name": "Meliodas", - "symbol": "MELIODAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cmk-marketc", - "name": "MarketC", - "symbol": "CMK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sld-soldo", - "name": "Soldo", - "symbol": "SLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "akira-akira", - "name": "Akira", - "symbol": "AKIRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "avinoc-avinoc", - "name": "AVINOC", - "symbol": "AVINOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spn-sapien", - "name": "Sapien", - "symbol": "SPN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pakk-pakkun-inu", - "name": "Pakkun Inu", - "symbol": "PAKK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dinger-dinger-token", - "name": "Dinger Token", - "symbol": "DINGER", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "esco-escrowcoin", - "name": "EscrowCoin", - "symbol": "ESCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sakata-sakata-inu", - "name": "Sakata Inu", - "symbol": "SAKATA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "daddyshiba-daddyshiba", - "name": "DaddyShiba", - "symbol": "DADDYSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ino-ino-coin", - "name": "INO COIN", - "symbol": "INO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fate-fate-token", - "name": "Fate Token", - "symbol": "FATE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adn-adrenaline", - "name": "Adrenaline", - "symbol": "ADN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xov-xovbank", - "name": "XOVBank", - "symbol": "XOV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "winry-winry-inu", - "name": "Winry Inu", - "symbol": "WINRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "x8x-x8x-token", - "name": "X8X Token", - "symbol": "X8X", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eta-ethera", - "name": "Ethera", - "symbol": "ETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btt-blocktrade-token", - "name": "Blocktrade Token", - "symbol": "BTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eplus-eplus-coin", - "name": "EPLUS Coin", - "symbol": "EPLUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vfy-verify-token", - "name": "Verify Token", - "symbol": "VFY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tetoinu-teto-inu", - "name": "Teto Inu", - "symbol": "TETOINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "knt-kora-network-token", - "name": "Kora Network Token", - "symbol": "KNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xcel-xceltoken", - "name": "XcelToken", - "symbol": "XCEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "foxgirl-foxgirl", - "name": "FoxGirl", - "symbol": "FOXGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gmcn-gamblecoin", - "name": "GambleCoin", - "symbol": "GMCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bti-bitcoin-instant", - "name": "Bitcoin Instant", - "symbol": "BTI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tzki-tsuzuki-inu", - "name": "Tsuzuki Inu", - "symbol": "TZKI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "onepiece-one-piece", - "name": "ONE PIECE", - "symbol": "ONEPIECE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "alucard-baby-alucard", - "name": "Baby Alucard", - "symbol": "ALUCARD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xun-ultranote-coin", - "name": "UltraNote Coin", - "symbol": "XUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "field-yieldfields", - "name": "YieldFields", - "symbol": "FIELD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "isn-69coin", - "name": "69Coin", - "symbol": "ISN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ifmv3-infinity-money-v3", - "name": "Infinity Money V3", - "symbol": "IFMV3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pts-pitiscoin", - "name": "PitisCoin", - "symbol": "PTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "diadogs-diamond-dogs", - "name": "Diamond Dogs", - "symbol": "DIADOGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mason-mason-token", - "name": "Mason Token", - "symbol": "MASON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "appa-appa-inu", - "name": "Appa Inu", - "symbol": "APPA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goc-gocrypto", - "name": "GoCrypto", - "symbol": "GoC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arlize-arlize", - "name": "ARLIZE", - "symbol": "ARLIZE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btk-bitcoin-turbo-koin", - "name": "Bitcoin Turbo Koin", - "symbol": "BTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ext-experience-token", - "name": "Experience Token", - "symbol": "EXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgpx-monster-grand-prix-token", - "name": "Monster Grand Prix Token", - "symbol": "MGPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "opcx-opcoinx", - "name": "OPCoinX", - "symbol": "OPCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bnd-blocknode", - "name": "Blocknode", - "symbol": "BND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bhpc-bhpcash", - "name": "BHPCoin", - "symbol": "BHP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eds-endorsit", - "name": "Endorsit", - "symbol": "EDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nerdy-nerdy-inu", - "name": "Nerdy Inu", - "symbol": "NERDY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "est-esports-token", - "name": "Esports Token", - "symbol": "EST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgd-massgrid", - "name": "MassGrid", - "symbol": "MGD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dtem-dystem", - "name": "Dystem", - "symbol": "DTEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nbx-nibex-coin", - "name": "Nibex Coin", - "symbol": "NBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nam-nam-coin", - "name": "NAM COIN", - "symbol": "NAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnn-brokernekonetwork", - "name": "BrokerNekoNetwork", - "symbol": "BNN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fmf-formosa-financial", - "name": "Formosa Financial", - "symbol": "FMF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "acre-acre", - "name": "ACRE", - "symbol": "ACRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aang-aang-inu", - "name": "Aang Inu", - "symbol": "AANG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "expr-experiencer", - "name": "Experiencer", - "symbol": "EXPR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "whis-whis-inu", - "name": "Whis Inu", - "symbol": "WHIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dacs-dacsee", - "name": "DACSEE", - "symbol": "DACS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xtrd-xtrd", - "name": "XTRD", - "symbol": "XTRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eve-evelyn", - "name": "Evelyn", - "symbol": "EVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ooki-ooki", - "name": "Ooki", - "symbol": "OOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jetswap-jet", - "name": "JetSwap", - "symbol": "JET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pumpkininu-pumpkininutoken", - "name": "PumpkinInu", - "symbol": "PUMPKININU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boocakespace-boocake", - "name": "Boocake.space", - "symbol": "BOOCAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kanye-inu-kanyeinu", - "name": "Kanye Inu", - "symbol": "KANYEINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sem-semux", - "name": "Semux", - "symbol": "SEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pupfloki-pupfloki", - "name": "PUPFLOKI", - "symbol": "PUPFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eping-eping", - "name": "ePING", - "symbol": "EPING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "genart-genart", - "name": "GEN.ART", - "symbol": "GENART", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ghostbuster-inu-ginu", - "name": "GHOSTBUSTER INU", - "symbol": "GINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "anime-inu-anime", - "name": "Anime inu", - "symbol": "ANIME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sadaharu-inu-sadinu", - "name": "Sadaharu Inu", - "symbol": "SADINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ryoshimoto-ryoshi", - "name": "RYOSHIMOTO", - "symbol": "RYOSHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shepherd-inu-shinu", - "name": "Shepherd Inu", - "symbol": "SHINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dinger-dao-dinger-dao", - "name": "Dinger Dao", - "symbol": "DINGER DAO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asuka-inu-asuka", - "name": "Asuka Inu", - "symbol": "ASUKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "seiya-seiya", - "name": "Seiya", - "symbol": "SEIYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "american-inu-americaninu", - "name": "American Inu", - "symbol": "AMERICANIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "army-zilla-armyzilla", - "name": "ARMY ZILLA", - "symbol": "ARMYZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gold-floki-boy-gfb", - "name": "Gold Floki Boy", - "symbol": "GFB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zillasuperhero-zillasuperhero", - "name": "ZillaSuperhero", - "symbol": "ZILLASUPER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kim-inu-kiminu", - "name": "Kim Inu", - "symbol": "KIMINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gon-gon", - "name": "Gon", - "symbol": "GON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "floinu-floinu", - "name": "FLOINU", - "symbol": "FLOINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dinger-inu-dinger-inu", - "name": "Dinger Inu", - "symbol": "DINGER INU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elon-ghost-eghost", - "name": "Elon Ghost", - "symbol": "EGHOST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pabloshiba-pshib", - "name": "PabloShiba", - "symbol": "PSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "battousai-inu-battinu", - "name": "Battousai Inu", - "symbol": "BATTINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lucky-mario-inu-luckymario", - "name": "Lucky Mario Inu", - "symbol": "LUCKYMARIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buttcoin-butt", - "name": "ButtCoin", - "symbol": "BUTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tsla-30apr22-stonkbase-tsla-apr22", - "name": "TSLA 30Apr22 StonkBase", - "symbol": "TSLA-APR22", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "little-tsuki-inu-liltk", - "name": "Little Tsuki Inu", - "symbol": "LILTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aztec-inu-aztec", - "name": "Aztec Inu", - "symbol": "AZTEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shinobu-inu-shinobu", - "name": "Shinobu Inu", - "symbol": "SHINOBU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kedama-kedama", - "name": "Kedama", - "symbol": "$KEDAMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pochi-inu-pochi", - "name": "Pochi Inu", - "symbol": "POCHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sanji-inu-sanji", - "name": "Sanji Inu", - "symbol": "SANJI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibacake-shibacake", - "name": "ShibaCake", - "symbol": "SHIBACAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "whalezilla-whalezilla", - "name": "WhaleZilla", - "symbol": "WHALEZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sda-six-domain-chain", - "name": "SDChain", - "symbol": "SDA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gamerse-lfg", - "name": "GAMERSE", - "symbol": "LFG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zilladot-zilladot", - "name": "ZillaDot", - "symbol": "ZILLADOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cubex-cubes", - "name": "CubeX", - "symbol": "CUBES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "newtest-newtest", - "name": "NEWTEST", - "symbol": "NEWTEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokiflokimusk-flokiflokimusk", - "name": "FlokiFlokiMusk", - "symbol": "FLOKIFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttshiva-test-shiva-token", - "name": "Test Shiva Token", - "symbol": "TTSHIVA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moontest-moontest", - "name": "MoonTest", - "symbol": "MOONTEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "twozilla-twozilla", - "name": "TWOZILLA", - "symbol": "TWOZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vco-volcano", - "name": "Volcano", - "symbol": "VCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nexo-nexoio-fintech", - "name": "Nexo.io Fintech", - "symbol": "NEXO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tenderly-tenderlyco-fintech", - "name": "Tenderly.co Fintech", - "symbol": "TENDERLY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usd-bsc", - "name": "BSC", - "symbol": "USD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dana-ardana", - "name": "Ardana", - "symbol": "$DANA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "you-you-coin", - "name": "YOU COIN", - "symbol": "YOU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibawitch-shiwbawitch-token", - "name": "SHIWBAWITCH TOKEN", - "symbol": "SHIBAWITCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dfp2-defiplaza", - "name": "DefiPlaza", - "symbol": "DFP2", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "klks-kalkulus", - "name": "Kalkulus", - "symbol": "KLKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vivid-vivid-coin", - "name": "Vivid Coin", - "symbol": "VIVID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bbk-brickblock", - "name": "Brickblock", - "symbol": "BBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pipa-huj", - "name": "huj", - "symbol": "PIPA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "idol-idol-coin", - "name": "IDOL COIN", - "symbol": "IDOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zmn-zmine", - "name": "ZMINE", - "symbol": "ZMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "npw-new-power-coin", - "name": "New Power Coin", - "symbol": "NPW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hrc-haracoin", - "name": "Haracoin", - "symbol": "HRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kwh-kwhcoin", - "name": "KWHCoin", - "symbol": "KWH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "czr-canonchain", - "name": "CanonChain", - "symbol": "CZR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gfshib-ghostface-shiba", - "name": "Ghostface Shiba", - "symbol": "GFSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iov-carlive-chain", - "name": "IOV BlockChain", - "symbol": "IOV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rkt-rekt", - "name": "Rekt", - "symbol": "RKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dpn-dipnet", - "name": "DIPNET", - "symbol": "DPN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "l-l-inu", - "name": "L inu", - "symbol": "L", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "purex-pure", - "name": "Pure", - "symbol": "PUREX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "seed-the-seed-farm", - "name": "The Seed Farm", - "symbol": "SEED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "priv-privcy", - "name": "PRiVCY", - "symbol": "PRIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lstr-luna-stars", - "name": "Luna Stars", - "symbol": "LSTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wsd-white-standard", - "name": "White Standard", - "symbol": "WSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yosi-yoi-shiba-inu", - "name": "Yoi Shiba Inu", - "symbol": "YOSI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kyd-know-your-developer", - "name": "Know Your Developer", - "symbol": "KYD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aced-aced", - "name": "AceD", - "symbol": "ACED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "psm-prasm", - "name": "PRASM", - "symbol": "PSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cakezilla-cakezilla", - "name": "CakeZilla", - "symbol": "CAKEZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcc-moving-cloud-coin", - "name": "Moving Cloud Coin", - "symbol": "MCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rotts-rottschild", - "name": "ROTTSCHILD", - "symbol": "ROTTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ghost-doge-ghost-doge-token", - "name": "Ghost Doge Token", - "symbol": "GHOST DOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cnmc-cryptonodes", - "name": "Cryptonodes", - "symbol": "CNMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hight-highcoin", - "name": "HighCoin", - "symbol": "HIGHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ocp-oc-protocol", - "name": "OC Protocol", - "symbol": "OCP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aiq-artiqox", - "name": "Artiqox", - "symbol": "AIQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "otk-orchid-token", - "name": "Orchid Token", - "symbol": "OTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "godzilla-godzilla", - "name": "GodZilla", - "symbol": "GODZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bss-black-sesame-shiba", - "name": "Black Sesame Shiba", - "symbol": "BSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibagold-shiba-gold", - "name": "Shiba Gold", - "symbol": "SHIBAGOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "s-nfts-super-nfts", - "name": "Super NFTs", - "symbol": "S NFTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trust-trust-finance", - "name": "Trust Finance", - "symbol": "TRUST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "catzilla-catzilla", - "name": "CATZILLA", - "symbol": "CATZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kzilla-kishuzilla", - "name": "KishuZilla", - "symbol": "KZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mfloki-magicfloki", - "name": "MagicFloki", - "symbol": "MFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dragon-dragonswap", - "name": "DragonSwap", - "symbol": "DRAGON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stupid-stupid-coin", - "name": "Stupid Coin", - "symbol": "STUPID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "setl-setlio-finance", - "name": "SETL.io Finance", - "symbol": "SETL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "supfin-super-finance", - "name": "Super Finance", - "symbol": "SUPFIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wario-wariocoin", - "name": "WarioCoin", - "symbol": "WARIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aztarac-aztarac", - "name": "Aztarac", - "symbol": "AZTARAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bms-bnbmoonshot", - "name": "BNBMoonShot", - "symbol": "BMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibamzilla-shibamoonzilla", - "name": "ShibaMoonZilla", - "symbol": "SHIBAMZILL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hams-hamsterswap", - "name": "HamsterSwap", - "symbol": "HAMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "samo-samo", - "name": "samo", - "symbol": "SAMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aurora-aurora", - "name": "Aurora", - "symbol": "AURORA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "solnet-solana-network", - "name": "Solana Network", - "symbol": "SOLNET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokstn-flokenstein", - "name": "Flokenstein", - "symbol": "FLOKSTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "everfloki-everfloki", - "name": "EverFloki", - "symbol": "EVERFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zillashib-zillashib-token", - "name": "ZillaShib Token", - "symbol": "ZILLASHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bear-bearcoin", - "name": "BearCoin", - "symbol": "BEAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fomozilla-fomozilla", - "name": "FomoZilla", - "symbol": "FOMOZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "warzone-moon-war-zone", - "name": "Moon War Zone", - "symbol": "WARZONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "solautostk-solana-autostake", - "name": "Solana AutoStake", - "symbol": "SOLAUTOSTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fjb-fjb", - "name": "FJB", - "symbol": "FJB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "phub-pornhub", - "name": "Pornhub", - "symbol": "PHUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tm-turbomoon", - "name": "TurboMoon", - "symbol": "$TM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safeshiba-safeshiba", - "name": "SafeShiba", - "symbol": "SAFESHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokifunny-floki-funny", - "name": "Floki Funny", - "symbol": "FLOKIFUNNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bshib-brownshibainu", - "name": "BrownShibaInu", - "symbol": "BSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hkg-holdkey-global", - "name": "Holdkey Global", - "symbol": "HKG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stream-streamfi", - "name": "StreamFi", - "symbol": "STREAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bittest1-bittest1", - "name": "Bittest1", - "symbol": "BITTEST1", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "batc-batcoin", - "name": "BatCoin", - "symbol": "BATC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hex-hex-token", - "name": "HEX Token", - "symbol": "HEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiba-shibagames", - "name": "ShibaGames", - "symbol": "SHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "whale-moonwhale", - "name": "MoonWhale", - "symbol": "WHALE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kct-kubictoken", - "name": "KubicToken", - "symbol": "KCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-metaverse", - "name": "Metaverse", - "symbol": "META", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "falconzilla-falcondogezilla", - "name": "FalconDogeZilla", - "symbol": "FALCONZILL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "data-dataswap", - "name": "DataSwap", - "symbol": "DATA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zz-zillazilla", - "name": "zillaZilla", - "symbol": "ZZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gaia-gaia-everworld", - "name": "Gaia Everworld", - "symbol": "GAIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bflokin-baby-flokinomics", - "name": "Baby Flokinomics", - "symbol": "BFLOKIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibavengers-shibavengers", - "name": "Shibavengers", - "symbol": "SHIBAVENGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uswn-uniswan", - "name": "UNISWAN", - "symbol": "USWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nfs-ninja-fantasy-token", - "name": "Ninja Fantasy Token", - "symbol": "NFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defi-nfts-defi-nfts", - "name": "DeFi NFTs", - "symbol": "DEFI NFTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btk-bitkitties", - "name": "BitKitties", - "symbol": "BTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dwarf-defiwars-finance", - "name": "DeFiWars Finance", - "symbol": "$DWARF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokifloki-flokifloki", - "name": "FlokiFloki", - "symbol": "FLOKIFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oskzilla-oskzilla-token", - "name": "OSKZilla Token", - "symbol": "OSKZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lks-little-kishu-moon", - "name": "Little Kishu Moon", - "symbol": "LKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gloryd-glorydoge", - "name": "GloryDoge", - "symbol": "GLORYD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "solo-solo-token", - "name": "Solo Token", - "symbol": "SOLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "marulainu-marulainu", - "name": "MARULAINU", - "symbol": "MARULAINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gkishu-goldkishu", - "name": "GOLDKISHU", - "symbol": "GKISHU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metababy-metababy", - "name": "MetaBaby", - "symbol": "METABABY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usdt1-usdt1", - "name": "USDT1", - "symbol": "USDT1", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidgame-squidgame", - "name": "SquidGame", - "symbol": "SQUIDGAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bossb-babybosstoken", - "name": "BABYBOSSTOKEN", - "symbol": "BOSSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bull-bullrun", - "name": "BullRun", - "symbol": "BULL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsi-babyshibazillainu", - "name": "BabyShibaZillaInu", - "symbol": "BSI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zillapig-zillapig-token", - "name": "ZillaPig Token", - "symbol": "ZILLAPIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pulsefloki-pulsechainfloki", - "name": "PulseChainFloki", - "symbol": "PULSEFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bl-breadline", - "name": "BreadLine", - "symbol": "$BL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swift-swiftpay", - "name": "SwiftPay", - "symbol": "SWIFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttbk2-ttbk2", - "name": "TTBK2", - "symbol": "TTBK2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tjr-tanjiro", - "name": "Tanjiro", - "symbol": "TJR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squid-squidgames-token", - "name": "SQUIDGames Token", - "symbol": "SQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pulsepeng-pulsechainpenguins", - "name": "PulseChainPenguins", - "symbol": "PULSEPENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vint-vint", - "name": "Vint", - "symbol": "VINT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "racazilla-racazilla", - "name": "RacaZilla", - "symbol": "RACAZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eco-ecofinance", - "name": "EcoFinance", - "symbol": "ECO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogezilladash-dogezilladash", - "name": "DogeZillaDash", - "symbol": "DOGEZILLAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zillapig-zillapig", - "name": "ZillaPig", - "symbol": "ZILLAPIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pwv-wavebase", - "name": "Wavebase", - "symbol": "PWV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "potterinu-potter-inu", - "name": "Potter Inu", - "symbol": "POTTERINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nix-nix", - "name": "NIX", - "symbol": "NIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mtc-mtc-mesh-network", - "name": "MTC Mesh Network", - "symbol": "MTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bullz-bullishzilla", - "name": "BullishZilla", - "symbol": "BULLZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arb-arbitrage", - "name": "ARBITRAGE", - "symbol": "ARB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "csm-consentium", - "name": "Consentium", - "symbol": "CSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fundz-fundtoken", - "name": "FundToken", - "symbol": "FUNDZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lft-linfinity", - "name": "Linfinity", - "symbol": "LFC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aqua-aqua-token", - "name": "Aqua Token", - "symbol": "AQUA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xxx-adultchain", - "name": "AdultChain", - "symbol": "XXX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shibboo-shibboo-inu", - "name": "Shibboo Inu", - "symbol": "SHIBBOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lionisland-lionisland-inu", - "name": "LionIsland Inu brak uni v2", - "symbol": "LIONISLAND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rtb-ab-chain-rtb", - "name": "AB-Chain RTB", - "symbol": "RTB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rlx-relex", - "name": "Relex", - "symbol": "RLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "toll-bridge-protocol", - "name": "Bridge Protocol", - "symbol": "BRDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "genip-genip", - "name": "GENIP", - "symbol": "GENIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chex-chex", - "name": "CHEX", - "symbol": "CHEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "view-view", - "name": "View", - "symbol": "VIEW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "empr-empowr-coin", - "name": "empowr coin", - "symbol": "EMPR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lpc-lightpaycoin", - "name": "Lightpaycoin", - "symbol": "LPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gve-globalvillage-ecosystem", - "name": "Globalvillage Ecosystem", - "symbol": "GVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vikky-vikkytoken", - "name": "VikkyToken", - "symbol": "VIKKY", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vct-valuecybertoken", - "name": "ValueCyberToken", - "symbol": "VCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nrve-narrative", - "name": "Narrative", - "symbol": "NRVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcat-top-cat-inu", - "name": "Top Cat inu", - "symbol": "TCAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axo-axolittles-nft", - "name": "AXOLITTLES NFT", - "symbol": "AXO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ole-olive", - "name": "Olive", - "symbol": "OLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wt-wetoken", - "name": "WeToken", - "symbol": "WT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bishufi-bishu-finance", - "name": "Bishu Finance", - "symbol": "BISHUFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "godzilla-godzilla-of-musk", - "name": "Godzilla of Musk", - "symbol": "GODZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ulu-uluru-finance", - "name": "Uluru Finance", - "symbol": "ULU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cosm-cosmo-coin", - "name": "Cosmo Coin", - "symbol": "COSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gbc-gold-bits-coin", - "name": "Gold Bits Coin", - "symbol": "GBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wys-wys-token", - "name": "wys Token", - "symbol": "WYS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zeno-zeno-inu", - "name": "Zeno Inu", - "symbol": "ZENO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jot-juryonline-token", - "name": "Jury.Online Token", - "symbol": "JOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shbma-shibamask", - "name": "ShibaMask", - "symbol": "SHBMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mmm7-mmm7", - "name": "MMM7", - "symbol": "MMM7", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "engt-engagement-token", - "name": "Engagement Token", - "symbol": "ENGT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dra-drachma", - "name": "Drachma", - "symbol": "DRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aeg-aegeus", - "name": "Aegeus", - "symbol": "AEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "spx-sp8de", - "name": "Sp8de", - "symbol": "SPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xd-xd", - "name": "Data Transaction Token", - "symbol": "XD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chiro-chihiro-inu", - "name": "Chihiro Inu", - "symbol": "CHIRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xmct-xmct", - "name": "XMCT", - "symbol": "XMCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bay-cryptobay", - "name": "CryptoBay", - "symbol": "BAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nobs-no-bs-crypto", - "name": "No BS Crypto", - "symbol": "NOBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rcd-record", - "name": "RECORD", - "symbol": "RCD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ply-playcoin", - "name": "PlayCoin [QRC20]", - "symbol": "PLY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fnkos-fnkos", - "name": "FNKOS", - "symbol": "FNKOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dws-dws", - "name": "DWS", - "symbol": "DWS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitv-bitvier", - "name": "Bitvier", - "symbol": "BITV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cgen-communitygeneration", - "name": "Community Generation", - "symbol": "CGEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "finu-first-inu", - "name": "First Inu", - "symbol": "FINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boost-booster-bsc", - "name": "Booster BSC", - "symbol": "BOOST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgman-dogeman", - "name": "DogeMan", - "symbol": "DGMAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kiba-kiba", - "name": "Kiba", - "symbol": "KIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "str-seather", - "name": "Seather", - "symbol": "STR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bz-bit-z-token", - "name": "Bit-Z Token", - "symbol": "BZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sfu-saifu", - "name": "Saifu", - "symbol": "SFU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ors-ors-group", - "name": "ORS Group", - "symbol": "ORS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yup-crowdholding", - "name": "Crowdholding", - "symbol": "YUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metadoge-meta-doge", - "name": "Meta Doge", - "symbol": "METADOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiryo-inu-shiryo-inu", - "name": "Shiryo-Inu", - "symbol": "SHIRYO-INU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kbc-karatgold-coin", - "name": "Karatgold Coin", - "symbol": "KBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coni-coni", - "name": "Coni", - "symbol": "CONI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "seer-seer", - "name": "SEER", - "symbol": "SEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pch-popchain", - "name": "POPCHAIN", - "symbol": "PCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ft-fcoin-token", - "name": "FToken", - "symbol": "FT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cccx-clipper-coin", - "name": "Clipper Coin", - "symbol": "CCCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "saint-saint-inu", - "name": "Saint Inu", - "symbol": "SAINT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "onl-onlive", - "name": "On.Live", - "symbol": "ONL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thrt-thrive-token", - "name": "Thrive Token", - "symbol": "THRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibgf-shibgf", - "name": "SHIBGF", - "symbol": "SHIBGF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fsbt-fsbt-api-token", - "name": "FSBT API Token", - "symbol": "FSBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hold-hold", - "name": "HOLD", - "symbol": "HOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chli-chilliswap", - "name": "ChilliSwap", - "symbol": "CHLI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pp-pension-plan", - "name": "Pension Plan", - "symbol": "PP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rocketshib-rocket-shib", - "name": "Rocket Shib", - "symbol": "ROCKETSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gin-ginoa", - "name": "Ginoa", - "symbol": "GIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bullt-bullterriercoin", - "name": "BullTerrierCoin", - "symbol": "BULLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ucf-umachitfund", - "name": "UmaChitFund", - "symbol": "UCF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gshiba-granny-shiba", - "name": "Granny Shiba", - "symbol": "GSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tunin-tunin-token", - "name": "Tunin Token", - "symbol": "TUNIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "csct-corsac", - "name": "Corsac", - "symbol": "CSCT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ttc-ttc-protocol", - "name": "TTC", - "symbol": "TTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fbnb-foreverbnb", - "name": "ForeverBNB", - "symbol": "FBNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pxu-phoenix-unity", - "name": "Phoenix Unity", - "symbol": "PXU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "djbz-daddybezos", - "name": "DaddyBezos", - "symbol": "DJBZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jacy-jacywaya", - "name": "JACYWAYA", - "symbol": "JACY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "megacosm-megacosm", - "name": "Megacosm", - "symbol": "MEGACOSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wiz-crowdwiz", - "name": "CrowdWiz", - "symbol": "WIZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gbx-globitex", - "name": "Globitex", - "symbol": "GBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebc-ebcoin", - "name": "EBCoin", - "symbol": "EBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tits-titsv2", - "name": "TitsV2", - "symbol": "TITS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lif-winding-tree", - "name": "Winding Tree", - "symbol": "LIF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "at-arabtycoon", - "name": "ArabTycoon", - "symbol": "AT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boomshiba-boom-shiba", - "name": "Boom Shiba", - "symbol": "BOOMSHIBA", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "tits-tits-token", - "name": "TITS Token", - "symbol": "TITS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uchad-ultrachad", - "name": "UltraChad", - "symbol": "UCHAD", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vikings-vikings", - "name": "Vikings", - "symbol": "VIKINGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vikings-inu-vikings-moon-inu", - "name": "Vikings Moon Inu", - "symbol": "VIKINGS IN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "froggies-froggies", - "name": "Froggies", - "symbol": "FROGGIES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "megashib-mega-shiba-inu", - "name": "Mega Shiba Inu", - "symbol": "MEGASHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonviking-moonvikinginu", - "name": "MoonvikingInu", - "symbol": "MOONVIKING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eventhemoon-even-the-moon", - "name": "Even The Moon", - "symbol": "EVENTHEMOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "altdo-altitudo", - "name": "Altitudo", - "symbol": "ALTDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "newbabydoge-new-babydoge-coin", - "name": "New babydoge coin", - "symbol": "NEWBABYDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "miyazaki-miyazaki-inu", - "name": "Miyazaki Inu", - "symbol": "MIYAZAKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shaggy-shaggy-inu", - "name": "Shaggy Inu", - "symbol": "SHAGGY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-metacom", - "name": "meta.com", - "symbol": "META", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scrap-scrappy-inu", - "name": "Scrappy Inu", - "symbol": "SCRAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shib-floki-shibf", - "name": "SHIBF", - "symbol": "SHIB FLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tanks-tanks", - "name": "Tanks", - "symbol": "TANKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yagaminu-yagami-inu", - "name": "Yagami Inu", - "symbol": "YAGAMINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tcs-team-clean-seas", - "name": "Team Clean Seas", - "symbol": "TCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "npando-nft-pando", - "name": "Nft Pando", - "symbol": "NPANDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spki-spike-inu", - "name": "SPIKE INU", - "symbol": "SPKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mdoge-moonsoondoge", - "name": "MoonSoonDoge", - "symbol": "MDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "maic-maico-token", - "name": "MaiCo Token", - "symbol": "MAIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "carbon-carbon-chain", - "name": "carbon chain", - "symbol": "CARBON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibachi-shibachi-inu", - "name": "Shibachi Inu", - "symbol": "SHIBACHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "f1shiba-f1-shiba", - "name": "F1 Shiba", - "symbol": "F1SHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kabosuinu-kabosuinu", - "name": "KabosuInu", - "symbol": "KABOSUINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibuncle-shibas-uncle", - "name": "Shibas Uncle", - "symbol": "SHIBUNCLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gods-gods-unchained", - "name": "Gods Unchained", - "symbol": "GODS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shkooby-shkooby-inu", - "name": "SHKOOBY INU", - "symbol": "SHKOOBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mxs-marketing-samurai", - "name": "Marketing Samurai", - "symbol": "MXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bshib-based-shiba-inu", - "name": "Based Shiba Inu", - "symbol": "BSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbc-bigblackcat", - "name": "BigBlackCat", - "symbol": "BBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "som-sustainable-opportunity-matrix", - "name": "Sustainable Opportunity Matrix", - "symbol": "SOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "misaki-misaki-inu", - "name": "Misaki Inu", - "symbol": "MISAKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shitzuinu-shitzu-inu", - "name": "Shitzu Inu", - "symbol": "SHITZUINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scoobydoo-scooby-doo-inu", - "name": "Scooby Doo Inu", - "symbol": "SCOOBYDOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metafloki-metafloki-token", - "name": "MetaFloki Token", - "symbol": "METAFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evi-eevee-inu", - "name": "Eevee Inu", - "symbol": "EVI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metapie-metapie", - "name": "METApie", - "symbol": "METAPIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xlshiba-xlshiba", - "name": "XLSHIBA", - "symbol": "XLSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eevee-eevee-inu", - "name": "Eevee Inu", - "symbol": "EEVEE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ceoldf-ceo-lildogefloki", - "name": "CEO LilDogeFloki", - "symbol": "CEOLDF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "miyuki-miyuki-shiba-inu", - "name": "Miyuki Shiba Inu", - "symbol": "MIYUKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lilflokiceo-lilflokiceo", - "name": "LilFlokiCeo", - "symbol": "LILFLOKICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ymi-yugimutouinu", - "name": "YugiMutouInu", - "symbol": "YMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lildf-lildogefloki", - "name": "LILDOGEFLOKI", - "symbol": "LILDF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mewn-mewn-inu", - "name": "Mewn Inu", - "symbol": "MEWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fceo-floki-ceo", - "name": "Floki CEO", - "symbol": "FCEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "monkeyd-mini-monkey-d", - "name": "Mini Monkey D", - "symbol": "MONKEYD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lildogefloki-lil-doge-floki", - "name": "lil doge Floki", - "symbol": "LILDOGEFLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yansh-yandere-shiba", - "name": "Yandere Shiba", - "symbol": "YANSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brbg-burgerburn", - "name": "BurgerBurn", - "symbol": "BRBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "maggot-maggot", - "name": "Maggot", - "symbol": "MAGGOT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "coin-coinvest", - "name": "Coinvest", - "symbol": "COIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flom-flokimars", - "name": "Flokimars", - "symbol": "FLOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shokky-shokky", - "name": "SHOKKY", - "symbol": "SHOKKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rotten-rotten-floki", - "name": "Rotten Floki", - "symbol": "ROTTEN", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ship-secured-ship", - "name": "Secured Ship", - "symbol": "SHIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zinc-zinc", - "name": "ZINC", - "symbol": "ZINC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wwb-wowbit", - "name": "Wowbit", - "symbol": "WWB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kndc-kanadecoin", - "name": "KanadeCoin", - "symbol": "KNDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hellsing-hellsing-inu", - "name": "Hellsing Inu", - "symbol": "HELLSING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dor-dorado", - "name": "Dorado", - "symbol": "DOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ryzeinu-ryze-inu", - "name": "Ryze Inu", - "symbol": "$RYZEINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mocha-mocha-inu", - "name": "Mocha Inu", - "symbol": "MOCHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttu-tatatu", - "name": "TaTaTu", - "symbol": "TTU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dingerv-dinger-verse", - "name": "Dinger Verse", - "symbol": "DINGERV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jigpuf-jigglypuff-inu", - "name": "Jigglypuff Inu", - "symbol": "JIGPUF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "occt-official-crypto-cowboy-token", - "name": "Official Crypto Cowboy Token", - "symbol": "OCCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hyuga-hyuga-inu", - "name": "Hyuga Inu", - "symbol": "HYUGA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ape-spaceapesinuerc", - "name": "SpaceApesInuErc", - "symbol": "APE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metapussy-metapussy", - "name": "MetaPussy", - "symbol": "METAPUSSY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mega-shiba-mega-shiba-inu", - "name": "Mega Shiba Inu", - "symbol": "MEGA SHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "infi-infernapeinu", - "name": "InfernapeInu", - "symbol": "INFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blo-black-onion", - "name": "Black Onion", - "symbol": "BLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frog-leapfrog", - "name": "Leapfrog", - "symbol": "FROG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hunger-elon-world-hunger", - "name": "Elon World Hunger", - "symbol": "HUNGER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ens-ens-dao-token", - "name": "ENS DAO TOKEN", - "symbol": "ENS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metawars-metawars-gaming", - "name": "Metawars gaming", - "symbol": "METAWARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "doges-nft-ancient-doges-nft", - "name": "Ancient Doges NFT", - "symbol": "DOGES NFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frieza-frieza-inu", - "name": "Frieza Inu", - "symbol": "$FRIEZA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tatsuya-tatsuya-shiba-inu", - "name": "Tatsuya Shiba Inu", - "symbol": "TATSUYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "okabeinu-okabe-inu", - "name": "Okabe Inu", - "symbol": "OKABEINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vinu-vitalikinu", - "name": "VitalikInu", - "symbol": "VINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metatude-metatude", - "name": "Metatude", - "symbol": "METATUDE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibaka-shiba-aka-inu", - "name": "Shiba Aka Inu", - "symbol": "SHIBAKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibro-shiba-brother", - "name": "Shiba Brother", - "symbol": "SHIBRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sausage-sausage-inu-tmesausageinuofficial", - "name": "Sausage Inu (t.me/sausageinuofficial)", - "symbol": "SAUSAGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jaroinu-jaro-inu", - "name": "Jaro Inu", - "symbol": "JAROINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "loti-lordoftheinu", - "name": "LordOfTheInu", - "symbol": "LOTI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jinu-jiro-inu", - "name": "Jiro Inu", - "symbol": "JINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uzamakinu-uzamaki-inu", - "name": "Uzamaki Inu", - "symbol": "UZAMAKINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ishizaki-ishizaki-inu", - "name": "Ishizaki Inu", - "symbol": "ISHIZAKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibbff-shibbff", - "name": "ShibBFF", - "symbol": "SHIBBFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "attack-on-titan-attack-on-titan", - "name": "Attack On Titan", - "symbol": "ATTACK ON ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibgif-shibgif", - "name": "SHIBGIF", - "symbol": "SHIBGIF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jackal-jackal-inu", - "name": "Jackal Inu", - "symbol": "JACKAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibarmy-shibarmy", - "name": "SHIBARMY", - "symbol": "SHIBARMY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blackbeard-inu-blackbeard-inu", - "name": "Blackbeard Inu", - "symbol": "BLACKBEARD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ens-ens-dao", - "name": "ENS DAO", - "symbol": "ENS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bezosverse-bezosverse", - "name": "BezosVerse", - "symbol": "BEZOSVERSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gamer-gamer", - "name": "GAMER", - "symbol": "GAMER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-facebook", - "name": "Facebook", - "symbol": "META", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaberg-meta-berg", - "name": "Meta Berg", - "symbol": "METABERG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaice-metaice", - "name": "MetaIce", - "symbol": "METAICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tnft-twinfttok-projects", - "name": "TwiNFTtok Projects", - "symbol": "TNFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cmi-cage-meme-index", - "name": "Cage Meme Index", - "symbol": "CMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "anft-animenft", - "name": "AnimeNFT", - "symbol": "ANFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fum-the-fumetzu-koin", - "name": "The Fumetzu Koin", - "symbol": "FUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tatsuya-tatsuya-shiba", - "name": "Tatsuya Shiba", - "symbol": "TATSUYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metatura-metatura", - "name": "MetaTura", - "symbol": "METATURA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fes-festoken", - "name": "FEStoken", - "symbol": "FES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mhv-metahumanverse", - "name": "@MetaHumanVerse", - "symbol": "MHV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bulldog-bulldog", - "name": "BULLDOG", - "symbol": "BULLDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tsi-thesecondinu", - "name": "TheSecondInu", - "symbol": "TSI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kirara-kirara", - "name": "Kirara", - "symbol": "KIRARA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orainu-ora-inu", - "name": "Ora Inu", - "symbol": "ORAINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lovinu-love-inu", - "name": "Love Inu", - "symbol": "LOVINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dpumpkins-david-s-pumpkins", - "name": "David S. Pumpkins", - "symbol": "DPUMPKINS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "18nu-android-18nu", - "name": "Android 18nu", - "symbol": "18NU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boiledbeans-boiled-beans-inu", - "name": "Boiled Beans Inu", - "symbol": "BOILEDBEAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibox-shiba-box", - "name": "Shiba Box", - "symbol": "SHIBOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "human-human-kind", - "name": "Human Kind", - "symbol": "HUMAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "human-humankind", - "name": "Humankind", - "symbol": "HUMAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rajainu-rajainu", - "name": "RajaInu", - "symbol": "RAJAINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibaswap-shibaswap", - "name": "SHIBASWAP", - "symbol": "SHIBASWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "linu-luigi-inu", - "name": "Luigi Inu", - "symbol": "LINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coroinu-coro-inu", - "name": "Coro Inu", - "symbol": "COROINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squid-squid-violence", - "name": "SQUID Violence", - "symbol": "SQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mewtwo-mewtwo-infused-shiba", - "name": "MewTwo Infused Shiba", - "symbol": "MEWTWO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dcc-distributed-credit-chain", - "name": "Distributed Credit Chain", - "symbol": "DCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rayquazainu-rayquaza-inu", - "name": "Rayquaza Inu", - "symbol": "RAYQUAZAIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "enaira-enaira-token", - "name": "eNaira Token", - "symbol": "ENAIRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smeta-safemeta", - "name": "SafeMeta", - "symbol": "SMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "masayoshi-inu-masayoshi-inu", - "name": "Masayoshi INU", - "symbol": "MASAYOSHI ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "brazzers-brazzers-inu", - "name": "Brazzers INU", - "symbol": "BRAZZERS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "destroydick-destroydick-inu", - "name": "Destroydick Inu", - "symbol": "DESTROYDIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metamononokeinu-metamononokeinu", - "name": "MetaMononokeInu", - "symbol": "METAMONONO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "johnysins-johnny-sins", - "name": "Johnny Sins", - "symbol": "JOHNYSINS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inori-inori-inu", - "name": "Inori Inu", - "symbol": "INORI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "degenmonkey-degenerate-monkey-httpstmedegeneratemonkeyerc20", - "name": "Degenerate Monkey - https://t.me/degeneratemonkeyERC20", - "symbol": "$DEGENMONK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cujoinu-coju-inu", - "name": "Coju Inu", - "symbol": "CUJOINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gambit-gambit", - "name": "Gambit", - "symbol": "GAMBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bluinu-perfect-blue-inu", - "name": "Perfect Blue Inu", - "symbol": "BLUINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mmi-metamononokeinu", - "name": "MetaMononokeinu", - "symbol": "MMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sos-storm-of-steel", - "name": "Storm of Steel", - "symbol": "SOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1bs-1-bit-simulations", - "name": "1 Bit Simulations", - "symbol": "1BS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bmeta-baby-meta", - "name": "baby META", - "symbol": "BMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "queer-queer-elon", - "name": "Queer Elon", - "symbol": "QUEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elon-inu-elon-inu", - "name": "Elon Inu", - "symbol": "ELON INU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fht-fancyhero", - "name": "FancyHero", - "symbol": "FHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "starmeta-star-meta", - "name": "Star Meta", - "symbol": "STARMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squirt-squirting-inu", - "name": "Squirting Inu", - "symbol": "SQUIRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tern-ternio", - "name": "Ternio", - "symbol": "TERN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "madara-madara-inu", - "name": "Madara Inu", - "symbol": "MADARA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-akita-meta-akita", - "name": "Meta Akita", - "symbol": "META AKITA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaiama-meta-saitama", - "name": "Meta Saitama", - "symbol": "METAIAMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kic-kiba-inu", - "name": "Kiba-Inu", - "symbol": "KIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minipsyduck-mini-psyduck", - "name": "Mini Psyduck", - "symbol": "MINIPSYDUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "psyduck-psyduck-inu", - "name": "Psyduck Inu", - "symbol": "PSYDUCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tentaclinu-tentacle-inu", - "name": "Tentacle Inu", - "symbol": "TENTACLINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaswap-metaswap", - "name": "MetaSwap", - "symbol": "METASWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metalink-meta-link", - "name": "Meta Link", - "symbol": "METALINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "genos-genos-inu", - "name": "Genos Inu", - "symbol": "GENOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boe-bodhi-eth", - "name": "Bodhi [ETH]", - "symbol": "BOE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metahero-meta-hero", - "name": "Meta Hero", - "symbol": "METAHERO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vitc-vitamin-coin", - "name": "Vitamin Coin", - "symbol": "VITC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ttedd-testtry", - "name": "testtry", - "symbol": "TTEDD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hair-doll-hair", - "name": "Doll Hair", - "symbol": "HAIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaearth-metaearth", - "name": "METAEARTH", - "symbol": "METAEARTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obt-oobit", - "name": "Oobit", - "symbol": "OBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mets-metaspace", - "name": "MetaSpace", - "symbol": "METS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scorpinu-scorpion-inu", - "name": "Scorpion Inu", - "symbol": "SCORPINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rzill-rektzilla", - "name": "RektZilla", - "symbol": "RZILL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cateinu-cate-inu", - "name": "Cate Inu", - "symbol": "CATEINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ors-origin-sport", - "name": "Origin Sport", - "symbol": "ORS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sqz-squidzilla", - "name": "SquidZilla", - "symbol": "SQZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vegetainu-vegeta-inu", - "name": "Vegeta Inu", - "symbol": "VEGETAINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "floki-gold-floki-gold", - "name": "FLOKI GOLD", - "symbol": "FLOKI GOLD", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "squid-squid-game", - "name": "Squid Game", - "symbol": "SQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mittens-mittens-token", - "name": "MITTENS Token", - "symbol": "MITTENS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "habikinu-habiki-inu", - "name": "Habiki Inu", - "symbol": "HABIKINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apmoneyorg-apmoneyorg", - "name": "ApMoney.org", - "symbol": "APMONEY.OR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttch-truetouch", - "name": "TrueTouch", - "symbol": "TTCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metadora-metadora", - "name": "MetaDora", - "symbol": "METADORA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minitsuzuki-mini-tsuzuki", - "name": "Mini Tsuzuki", - "symbol": "MINITSUZUK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtro-metatron", - "name": "MetaTron", - "symbol": "MTRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gshib-gshiba-inu", - "name": "GShiba Inu", - "symbol": "GSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xicor-xicor-inu", - "name": "Xicor Inu", - "symbol": "XICOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dmi-daddymononokeinu", - "name": "DaddyMononokeInu", - "symbol": "DMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "takashinu-takashi-inu", - "name": "Takashi Inu", - "symbol": "TAKASHINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alx-alax", - "name": "ALAX", - "symbol": "ALX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyshib-baby-shiba", - "name": "Baby Shiba", - "symbol": "BABYSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "szil-shartzilla", - "name": "ShartZilla", - "symbol": "SZIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mankichinu-mankichinu", - "name": "MankichInu", - "symbol": "MANKICHINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaspark-metaspark", - "name": "MetaSpark", - "symbol": "METASPARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "captain-inu-captain-inu", - "name": "Captain Inu", - "symbol": "CAPTAIN IN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "efloki-elons-floki", - "name": "Elons Floki", - "symbol": "EFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hshib-hshiba-inu", - "name": "HShiba Inu", - "symbol": "HSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deidara-inu-deidara-inu", - "name": "Deidara Inu", - "symbol": "DEIDARA IN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aninu-anime-inu", - "name": "Anime Inu", - "symbol": "ANINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ioec-internet-of-everything-coin", - "name": "Internet of Everything Coin", - "symbol": "IOEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rayquaza-rayquaza", - "name": "Rayquaza", - "symbol": "RAYQUAZA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "salem-salem-the-cat", - "name": "Salem The Cat", - "symbol": "SALEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "purr-purr-token", - "name": "PURR Token", - "symbol": "PURR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaironman-meta-iron-man", - "name": "Meta Iron Man", - "symbol": "METAIRONMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "melon-inu-meta-elon-inu", - "name": "Meta Elon INU", - "symbol": "MELON INU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gotainu-gota-inu", - "name": "Gota Inu", - "symbol": "GOTAINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sonicinu-sonic-inu", - "name": "Sonic Inu", - "symbol": "SONICINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kimochinu-kimochinu", - "name": "Kimochinu", - "symbol": "KIMOCHINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bminu-babymarvininu", - "name": "BabyMarvinInu", - "symbol": "BMINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mshib-meta-shiba", - "name": "Meta Shiba", - "symbol": "MSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metastake-metastake", - "name": "MetaStake", - "symbol": "METASTAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "space-kook-spooky-space-kook", - "name": "Spooky Space Kook", - "symbol": "SPACE KOOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bogeyinu-bogeyinu", - "name": "Bogeyinu", - "symbol": "BOGEYINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jiraiya-jiraiya-inu", - "name": "Jiraiya Inu", - "symbol": "JIRAIYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "txcoin-thousandxcoin", - "name": "ThousandXcoin", - "symbol": "TXCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hellsing-hellsing-inu-token", - "name": "Hellsing Inu Token", - "symbol": "HELLSING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etk-energitoken", - "name": "EnergiToken", - "symbol": "ETK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bwt-bittwatt", - "name": "Bittwatt", - "symbol": "BWT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fgc-fantasygold", - "name": "FantasyGold", - "symbol": "FGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metasawinu-metasawinu", - "name": "METASAWINU", - "symbol": "METASAWINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "devil-devil-inu", - "name": "Devil Inu", - "symbol": "DEVIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kitty-kitty-inu-token", - "name": "Kitty Inu Token", - "symbol": "KITTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shaka-shaka-token", - "name": "Shaka Token", - "symbol": "SHAKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaf-meta-floki", - "name": "META FLOKI", - "symbol": "METAF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fvsi-freddy-vs-inu", - "name": "Freddy vs Inu", - "symbol": "FVSI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ainu-ainu-inu", - "name": "Ainu Inu", - "symbol": "AINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prpr-prometheus-protocol", - "name": "Prometheus Protocol", - "symbol": "PRPR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "friezainu-frieza-inu", - "name": "Frieza Inu", - "symbol": "FRIEZAINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scarcity-scarcity-inu", - "name": "scarcity INU", - "symbol": "SCARCITY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metadark-metadark", - "name": "METADARK", - "symbol": "METADARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sher-sherpa-inu", - "name": "Sherpa Inu", - "symbol": "SHER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pokr-pokr", - "name": "POKR", - "symbol": "POKR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "speech-freespeechfinance", - "name": "FreeSpeech.Finance", - "symbol": "SPEECH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pkc-pikciochain", - "name": "PikcioChain", - "symbol": "PKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sal-salpay", - "name": "SalPay", - "symbol": "SAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cgc-cgc-token", - "name": "CGC Token", - "symbol": "CGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrq-mirq", - "name": "MIRQ", - "symbol": "MRQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ioex-ioex", - "name": "IOEX", - "symbol": "IOEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ftt-farmatrust", - "name": "FarmaTrust", - "symbol": "FTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tmt-traxia", - "name": "TRAXIA", - "symbol": "TM2", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hur-hurify", - "name": "Hurify", - "symbol": "HUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pmnt-paymon", - "name": "Paymon", - "symbol": "PMNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "omx-shivom", - "name": "Shivom", - "symbol": "OMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogerkt-dogerocket", - "name": "DogeRocket", - "symbol": "DOGERKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atmup-automaticup", - "name": "AutoMaticUp", - "symbol": "ATMUP", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hyfi-hyfi-token", - "name": "HyFi Token", - "symbol": "HYFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ironman-ironman", - "name": "Ironman", - "symbol": "IRONMAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "medx-medibloc-erc20", - "name": "MediBloc [ERC20]", - "symbol": "MEDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbx-cropbytes", - "name": "CropBytes", - "symbol": "CBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lyl-loyalcoin", - "name": "LoyalCoin", - "symbol": "LYL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kwatt-4new", - "name": "4NEW", - "symbol": "KWATT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pinu-polkainu", - "name": "PolkaInu", - "symbol": "PINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shop-shopin", - "name": "Shopin", - "symbol": "SHOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bakeup-bake-up", - "name": "Bake Up", - "symbol": "$BAKEUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cnet-contractnet", - "name": "ContractNet", - "symbol": "CNET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ejoy-ejoy", - "name": "EJOY", - "symbol": "EJOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drb-dragon-battles", - "name": "Dragon Battles", - "symbol": "DRB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ivy-ivy", - "name": "Ivy", - "symbol": "IVY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pwr-pwr-coin", - "name": "PWR Coin", - "symbol": "PWR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nec-nectar", - "name": "Nectar", - "symbol": "NEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snogo-snoopdoggo", - "name": "SnoopDoggo", - "symbol": "SNOGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibusdt-shibusdt", - "name": "ShibUsdt", - "symbol": "SHIBUSDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mwar-memewars", - "name": "MemeWars", - "symbol": "MWAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qwk-elon-getqwik", - "name": "Elon Getqwik", - "symbol": "QWK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "att-australian-terrier", - "name": "Australian Terrier", - "symbol": "ATT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnby-bnb-yields", - "name": "BNB Yields", - "symbol": "BNBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "berserk-berserk-inu", - "name": "Berserk Inu", - "symbol": "BERSERK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buffs-buffs-coin", - "name": "Buffs Coin", - "symbol": "BUFFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frtc-fairtoken-cake", - "name": "FairToken Cake", - "symbol": "FRTC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wndg95-windoge95", - "name": "Windoge95", - "symbol": "WNDG95", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "car-carblock", - "name": "CarBlock", - "symbol": "CAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dlaunch-defi-launch", - "name": "DeFi Launch", - "symbol": "DLAUNCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drac-dracarys", - "name": "DRACARYS", - "symbol": "DRAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "falcx-falconx", - "name": "FalconX", - "symbol": "FALCX", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "vote-agora", - "name": "Agora", - "symbol": "VOTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nomy-nomy", - "name": "NOMY", - "symbol": "NOMY", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "clam-otter-clam", - "name": "Otter Clam", - "symbol": "CLAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atmi-atonomi", - "name": "Atonomi", - "symbol": "ATMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pirateboy-pirate-boy", - "name": "Pirate Boy", - "symbol": "PIRATEBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msz-megashibazilla", - "name": "MegaShibaZilla", - "symbol": "MSZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mim-mim", - "name": "MIM", - "symbol": "MIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asa-asura-coin", - "name": "Asura Coin", - "symbol": "ASA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rmesh-rightmesh", - "name": "RightMesh", - "symbol": "RMESH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbo-bigbom", - "name": "Bigbom", - "symbol": "BBO", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "unic-unicorn-cake", - "name": "Unicorn Cake", - "symbol": "UNIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bit-bitrewards", - "name": "BitRewards", - "symbol": "BIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dunes-dunes", - "name": "Dunes", - "symbol": "DUNES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "navi-naviaddress", - "name": "Naviaddress", - "symbol": "NAVI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exc-eximchain", - "name": "Eximchain", - "symbol": "EXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zip-zipper", - "name": "Zipper", - "symbol": "ZIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chakra-bnb-shinobi", - "name": "BNB Shinobi", - "symbol": "CHAKRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spd-spindle", - "name": "SPINDLE", - "symbol": "SPD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "caz-cazcoin", - "name": "Cazcoin", - "symbol": "CAZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gorgeous-gorgeous", - "name": "Gorgeous", - "symbol": "GORGEOUS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ft-fabric-token", - "name": "Fabric Token", - "symbol": "FT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bullx-bullxmooon", - "name": "BullxMooon", - "symbol": "Bullx", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gin-gincoin", - "name": "GINcoin", - "symbol": "GIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fdl-fidel-token", - "name": "FIDEL TOKEN", - "symbol": "FDL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cgb-cgb-coin", - "name": "CGB Coin", - "symbol": "CGB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apix-apix", - "name": "APIX", - "symbol": "APIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mashima-mashima-inu", - "name": "Mashima Inu", - "symbol": "MASHIMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrh-rhenium", - "name": "Rhenium", - "symbol": "XRH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "inv-invacio", - "name": "Invacio", - "symbol": "INV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1nft-1nft", - "name": "1NFT", - "symbol": "1NFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hero-hero", - "name": "Hero", - "symbol": "HERO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "guccinu-guccinu", - "name": "Guccinu", - "symbol": "GUCCINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tka-tokia", - "name": "Tokia", - "symbol": "TKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lwf-local-world-forwarders", - "name": "Local World Forwarders", - "symbol": "LWF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "her-heronode", - "name": "HeroNode", - "symbol": "HER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babel-babelfish", - "name": "BabelFish", - "symbol": "BABEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gls-glass-chain", - "name": "Glass Chain", - "symbol": "GLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metastar-metastar", - "name": "Metastar", - "symbol": "METASTAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cln-colu-local-network", - "name": "Colu Local Network", - "symbol": "CLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grn-greenpower", - "name": "GreenPower", - "symbol": "GRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sgn-signals-network", - "name": "Signals Network", - "symbol": "SGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shl-oyster-shell", - "name": "Oyster Shell", - "symbol": "SHL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dtrc-datarius-credit", - "name": "Datarius Credit", - "symbol": "DTRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fnd-fundrequest", - "name": "FundRequest", - "symbol": "FND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inc-influence-chain", - "name": "Influence Chain", - "symbol": "INC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adinu-adventure-inu", - "name": "Adventure Inu", - "symbol": "ADINU", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sgcc-super-game-chain", - "name": "Super Game Chain", - "symbol": "SGCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nper-nper", - "name": "NPER", - "symbol": "NPER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibchu-shibachu", - "name": "Shibachu", - "symbol": "SHIBCHU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bst-baby-santa-token", - "name": "Baby Santa Token", - "symbol": "$BST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zipt-zippie", - "name": "Zippie", - "symbol": "ZIPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oot-utrum", - "name": "Utrum", - "symbol": "OOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "atx-aston", - "name": "Aston", - "symbol": "ATX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "team-team-tokenstars", - "name": "TEAM (TokenStars)", - "symbol": "TEAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "k9-k-9-inu", - "name": "K-9 Inu", - "symbol": "K9", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frv-fitrova", - "name": "Fitrova", - "symbol": "FRV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frec-freyrchain", - "name": "Freyrchain", - "symbol": "FREC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pal-policypal-network", - "name": "PAL Network", - "symbol": "PAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cre-cybereits", - "name": "Cybereits", - "symbol": "CRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssb-super-saiyan-blue", - "name": "Super Saiyan Blue", - "symbol": "SSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ges-galaxy-esolutions", - "name": "Galaxy eSolutions", - "symbol": "GES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ac3-ac3", - "name": "AC3", - "symbol": "AC3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bouts-boutspro", - "name": "BoutsPro", - "symbol": "BOUTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "live-live-stars", - "name": "Live Stars", - "symbol": "LIVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nty-nexty", - "name": "Nexty", - "symbol": "NTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ieth-infinity-eth", - "name": "Infinity ETH", - "symbol": "IETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "proud-proud-money", - "name": "PROUD Money", - "symbol": "PROUD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xlc-leviar", - "name": "Leviar", - "symbol": "XLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blueberry-blueberry-network", - "name": "Blueberry Network", - "symbol": "BLUEBERRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plan-plancoin", - "name": "Plancoin", - "symbol": "PLAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btct-basecamp-trade-community", - "name": "Basecamp Trade Community", - "symbol": "BTCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amn-amon", - "name": "Amon", - "symbol": "AMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibamom-shiba-mo", - "name": "Shiba Mo", - "symbol": "SHIBAMOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metanft-meta-nft", - "name": "Meta nft", - "symbol": "METANFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "erodi-erodi-finance", - "name": "Erodi Finance", - "symbol": "ERODI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fumo-fullmoon", - "name": "FullMoon", - "symbol": "FUMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rloki-floki-rocket", - "name": "Floki Rocket", - "symbol": "RLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fox-fox", - "name": "FOX", - "symbol": "FOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orion-orion-finance", - "name": "Orion Finance", - "symbol": "ORION", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flix-netflix-and-chill", - "name": "Netflix And Chill", - "symbol": "FLIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokis-flokis", - "name": "flokis", - "symbol": "FLOKIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidgameinu-squidgameinu", - "name": "SquidGameInu", - "symbol": "SQUIDGAMEI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safemoonzilla-safemoonzilla", - "name": "SafeMoonZilla", - "symbol": "SAFEMOONZI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "huskygirl-huskygirl", - "name": "HuskyGirl", - "symbol": "HUSKYGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deip-deipworld", - "name": "deip.world", - "symbol": "DEIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onion-oniondefi", - "name": "OnionDeFi", - "symbol": "ONION", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bul-babyuplink", - "name": "BabyUpLINK", - "symbol": "BUL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibahusky-shibahusky", - "name": "SHIBAHUSKY", - "symbol": "SHIBAHUSKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "byegd-byegd", - "name": "byegd", - "symbol": "BYEGD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcas-mooncasnio", - "name": "MoonCasnio", - "symbol": "MCAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokita-flokita-inu", - "name": "Flokita Inu", - "symbol": "FLOKITA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gz-gamezonefinance", - "name": "GameZone.finance", - "symbol": "GZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "edc-ever-doge-coin", - "name": "Ever Doge Coin", - "symbol": "EDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tornado-tornado-protocol", - "name": "Tornado Protocol", - "symbol": "TORNADO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyuplink-babyuplink", - "name": "BabyUpLINK", - "symbol": "BABYUPLINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cov-core-v", - "name": "Core V", - "symbol": "COV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-metadoge", - "name": "METADOGE", - "symbol": "META", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "call-of-call-of-duty", - "name": "call of duty", - "symbol": "CALL OF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bari-the-bari-coin", - "name": "The BARI Coin", - "symbol": "BARI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stoptesla-stop-tesla", - "name": "Stop Tesla", - "symbol": "STOPTESLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaverse-metaverse", - "name": "Metaverse", - "symbol": "METAVERSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aws-allied-web-security", - "name": "Allied Web Security", - "symbol": "AWS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bgdoge-bubblegumdoge", - "name": "BubbleGumDoge", - "symbol": "BGDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metafinance-metafinance", - "name": "MetaFinance", - "symbol": "METAFINANC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bci-bitcoin-interest", - "name": "Bitcoin Interest", - "symbol": "BCI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mev-mev", - "name": "MEV", - "symbol": "MEV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibamaki-shiba-maki", - "name": "Shiba Maki", - "symbol": "SHIBAMAKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "betr-betterbetting", - "name": "BetterBetting", - "symbol": "BETR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttt-trustnote", - "name": "TrustNote", - "symbol": "TTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vme-verime", - "name": "VeriME", - "symbol": "VME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nbai-nebula-ai", - "name": "Nebula AI", - "symbol": "NBAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metashiba-metashiba", - "name": "MetaShiba", - "symbol": "METASHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogebnb-dogebnborg", - "name": "DogeBNB.org", - "symbol": "DOGEBNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hpay-hade-pay", - "name": "HadePay", - "symbol": "HPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lnd-lendingblock", - "name": "Lendingblock", - "symbol": "LND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tezilla-tezilla", - "name": "Tezilla", - "symbol": "TEZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vips-vipstar-coin", - "name": "Vipstar Coin", - "symbol": "VIPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wolfgirl-wolf-girl", - "name": "Wolf Girl", - "symbol": "WOLFGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btrn-biotron", - "name": "Biotron", - "symbol": "BTRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aph-aphelion", - "name": "Aphelion", - "symbol": "APH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xes-proxeus", - "name": "Proxeus", - "symbol": "XES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trak-trakinvest", - "name": "TrakInvest", - "symbol": "TRAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dvs-dvs", - "name": "DVS", - "symbol": "DVS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbc-tradove-b2bcoin", - "name": "TraDove B2BCoin", - "symbol": "BBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apple-apple", - "name": "Apple", - "symbol": "APPLE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ori-origami", - "name": "Origami", - "symbol": "ORI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hbz-helbiz", - "name": "HBZ coin", - "symbol": "HBZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crop-cropcoin", - "name": "Cropcoin", - "symbol": "CROP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pxc-pixie-coin", - "name": "Pixie Coin", - "symbol": "PXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "srcoin-srcoin", - "name": "SRCOIN", - "symbol": "SRCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pbuffedzilla-pandabuffedzilla", - "name": "PandaBuffedZilla", - "symbol": "PBUFFEDZIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "batman-batman", - "name": "Batman", - "symbol": "BATMAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "phi-phi-token", - "name": "PHI Token", - "symbol": "PHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "earth-earth-token", - "name": "Earth Token", - "symbol": "EARTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibelon-shibelon-mars", - "name": "Shibelon Mars", - "symbol": "SHIBELON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrk-markspace", - "name": "MARK.SPACE", - "symbol": "MRK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kst-starcoin", - "name": "StarCoin", - "symbol": "KST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sher-sherlock-wallet", - "name": "Sherlock Wallet", - "symbol": "SHER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bunnyzilla-bunny-zilla", - "name": "Bunny Zilla", - "symbol": "BUNNYZILLA", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kurai-kurai-inu", - "name": "Kurai Inu", - "symbol": "KURAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dan-daneel", - "name": "Daneel", - "symbol": "DAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mfg-syncfab", - "name": "SyncFab", - "symbol": "MFG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msq-moonsqueeze", - "name": "MoonSqueeze", - "symbol": "MSQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "toyshiba-toyshiba", - "name": "ToySHIBA", - "symbol": "TOYSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xmo-monero-original", - "name": "Monero Original", - "symbol": "XMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brm-brahmaos", - "name": "BrahmaOS", - "symbol": "BRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flobo-flokibonk", - "name": "FlokiBonk", - "symbol": "FLOBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pornhub-pornhub", - "name": "Pornhub", - "symbol": "PORNHUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rockstar-rockstar-games", - "name": "ROCKSTAR GAMES", - "symbol": "ROCKSTAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcfinance-mcfinance", - "name": "MCFinance", - "symbol": "MCFINANCE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cookie-cookie-defi", - "name": "Cookie DeFi", - "symbol": "COOKIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sb-superbon", - "name": "SUPERBON", - "symbol": "SB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bul-baby-up-link", - "name": "Baby Up LINK", - "symbol": "BUL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wars-metawarsgg-meta-wars-play2earn", - "name": "metawars.gg Meta Wars Play2Earn", - "symbol": "WARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bubo-budbo", - "name": "Budbo", - "symbol": "BUBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "katsu-katsu-kitsune", - "name": "Katsu Kitsune", - "symbol": "KATSU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "baby-buffbabyshiba", - "name": "BuffBabyShiba", - "symbol": "BABY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cvh-curriculum-vitae", - "name": "Curriculum Vitae", - "symbol": "CVH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rts-rts-rits-coin", - "name": "RTS - RITS COIN", - "symbol": "RTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xbb-bitblocks", - "name": "Bitblocks", - "symbol": "XBB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dig-dignity", - "name": "Dignity", - "symbol": "DIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibpink-pink-shiba-inu", - "name": "Pink Shiba Inu", - "symbol": "SHIBPINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nftmeta-nftmetaverse", - "name": "NFTMETAVERSE", - "symbol": "NFTMETA", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "grmd-greenmed", - "name": "GreenMed", - "symbol": "GRMD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbi-belugapay", - "name": "BelugaPay", - "symbol": "BBI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hat-hatexchange", - "name": "Hat.Exchange", - "symbol": "HAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prn-ironcoin", - "name": "IronCoin", - "symbol": "PRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rex-imbrex", - "name": "imbrex", - "symbol": "REX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cvt-cybervein", - "name": "CyberVein", - "symbol": "CVT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snip-snipcoin", - "name": "SnipCoin", - "symbol": "SNIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cbt-commerceblock", - "name": "CommerceBlock", - "symbol": "CBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adh-adhive", - "name": "AdHive", - "symbol": "ADH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsm-bitsum", - "name": "Bitsum", - "symbol": "BSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "atc-arbitracoin", - "name": "Arbitracoin", - "symbol": "ATC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "win-wcoin", - "name": "WCOIN", - "symbol": "WIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clvc-calvin-coin", - "name": "Calvin Coin", - "symbol": "CLVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fid-fidelium", - "name": "Fidelium", - "symbol": "FID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpx-apex", - "name": "Apex", - "symbol": "CPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tds-tokendesk", - "name": "TokenDesk", - "symbol": "TDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xdce-xinfin-network", - "name": "XinFin Network", - "symbol": "XDCE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "itt-intelligent-trading-foundation", - "name": "Intelligent Trading Foundation", - "symbol": "ITF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tty-trinity", - "name": "Trinity", - "symbol": "TTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kong-flokikong", - "name": "FlokiKong", - "symbol": "KONG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ckusd-ck-usd", - "name": "CK USD", - "symbol": "CKUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "frens-frens", - "name": "Frens", - "symbol": "FRENS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ghibli-ghibli-inu", - "name": "Ghibli Inu", - "symbol": "GHIBLI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiba-bitshiba", - "name": "BitShiba", - "symbol": "SHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xclr-clearcoin", - "name": "ClearCoin", - "symbol": "XCLR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sen-consensus", - "name": "Sentient Coin", - "symbol": "SEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "itz-interzone", - "name": "Interzone", - "symbol": "ITZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bstn-bitstation", - "name": "BitStation", - "symbol": "BSTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "funk-the-cypherfunks", - "name": "The Cypherfunks", - "symbol": "FUNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "unbnk-unbanked", - "name": "Unbanked", - "symbol": "UNBNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ipsx-ip-exchange", - "name": "IP Exchange", - "symbol": "IPSX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spd-stipend", - "name": "Stipend", - "symbol": "SPD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lnc-blocklancer", - "name": "Blocklancer", - "symbol": "LNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xbp-blitzpredict", - "name": "BlitzPredict", - "symbol": "XBP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flip-flipper-token", - "name": "Flipper Token", - "symbol": "FLIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gg-good-game", - "name": "Good Game", - "symbol": "GG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bmnd-baby-mind", - "name": "Baby Mind", - "symbol": "BMND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dhp-dhealth", - "name": "dHealth", - "symbol": "DHP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tbar-titanium-bar", - "name": "Titanium BAR", - "symbol": "TBAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metashib-metashiba-inu", - "name": "METASHIBA INU", - "symbol": "METASHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nxx-nexxus", - "name": "Nexxus", - "symbol": "NXX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slm-slimcoin", - "name": "Slimcoin", - "symbol": "SLM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "test1-test1", - "name": "test1", - "symbol": "TEST1", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dbzilla-dragonboyzilla", - "name": "DragonBoyZilla", - "symbol": "DBZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ga-good-afternoon", - "name": "Good Afternoon", - "symbol": "GA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fairy-fairy-coin", - "name": "fairy coin", - "symbol": "FAIRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hub-husky", - "name": "Husky", - "symbol": "HUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gmgn-gmgn", - "name": "GMGN", - "symbol": "GMGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rocket-rocket-to-the-moon", - "name": "Rocket To The Moon", - "symbol": "ROCKET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pxs-pundi-x-old", - "name": "Pundi X [OLD]", - "symbol": "PXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "suca-suca", - "name": "Suca", - "symbol": "SUCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hexagon-hexagon-protocol", - "name": "Hexagon Protocol", - "symbol": "HEXAGON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alra-alra", - "name": "Alra", - "symbol": "ALRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "maf-mafia", - "name": "Mafia", - "symbol": "MAF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chesstoken-chesstoken", - "name": "CHESSTOKEN", - "symbol": "CHESSTOKEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cakes-pancakeswapnft", - "name": "PancakeSwapNFT", - "symbol": "CAKES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gpay-global-payment", - "name": "Global Payment", - "symbol": "GPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "foxy-foxswap", - "name": "FoxSwap", - "symbol": "FOXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cookie-cookie-clicker", - "name": "Cookie Clicker", - "symbol": "COOKIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atom-atom-chain", - "name": "ATOM Chain", - "symbol": "ATOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ct-ct", - "name": "CT", - "symbol": "CT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "maji-qmaji", - "name": "Qmaji", - "symbol": "MAJI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elongm-elongm", - "name": "ElonGM", - "symbol": "ELONGM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hotmetagirl-hot-meta-zilla-girl", - "name": "HOT META ZILLA GIRL", - "symbol": "HOTMETAGIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lol-lol-studio", - "name": "LOL Studio", - "symbol": "LOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "runegame-wwwrunegame", - "name": "www.Rune.game", - "symbol": "RUNEGAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "huge-huge", - "name": "huge", - "symbol": "HUGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "terr-terrtoken", - "name": "TERRtoken", - "symbol": "TERR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pukka-pukka-juuice", - "name": "Pukka Juuice", - "symbol": "PUKKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidinu-squidinu", - "name": "SquidInu", - "symbol": "SQUIDINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buffedcatecoin-buffedcate", - "name": "BuffedCate", - "symbol": "BUFFEDCATE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cgcn-cupgamecoin", - "name": "Cupgamecoin", - "symbol": "CGCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbd-squidbabies-deluxe", - "name": "SquidBabies Deluxe", - "symbol": "SBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "psc-pinscher", - "name": "PinSCher", - "symbol": "PSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokipunks-flokipunks", - "name": "FlokiPunks", - "symbol": "FLOKIPUNKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sht-shutest", - "name": "shutest", - "symbol": "SHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alano-alano-inu", - "name": "Alano Inu", - "symbol": "ALANO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test-test", - "name": "test", - "symbol": "TEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "covid-covidcoin", - "name": "CovidCoin", - "symbol": "COVID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "astrozilla-astrozilla", - "name": "AstroZilla", - "symbol": "ASTROZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mwt-moonwars", - "name": "MoonWars", - "symbol": "MWT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mvpad-metavpad", - "name": "MetaVPad", - "symbol": "MVPAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "funnycat-funnycatstoken", - "name": "FunnyCatsToken", - "symbol": "FUNNYCAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibs-shibaswap", - "name": "ShibaSwap", - "symbol": "SHIBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "omny-orion-money", - "name": "Orion Money", - "symbol": "OMNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "medic-mediccoin", - "name": "MedicCoin", - "symbol": "MEDIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bitg-bitcoin-green", - "name": "BitCoin Green", - "symbol": "BITG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "npx-napoleonx", - "name": "NaPoleonX", - "symbol": "NPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lgo-lgo-token", - "name": "LGO Token", - "symbol": "LGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wall-launchwall", - "name": "LaunchWall", - "symbol": "WALL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "noah-noah-coin", - "name": "Noah Coin", - "symbol": "NOAH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbn-banyan-network", - "name": "Banyan Network", - "symbol": "BBN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "garfield-garfield-token", - "name": "Garfield Token", - "symbol": "GARFIELD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kok-kult-of-kek", - "name": "Kult of Kek", - "symbol": "KOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oc-ocean-chain", - "name": "Ocean Chain", - "symbol": "OC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dtf-dogethefloki", - "name": "DogeTheFloki", - "symbol": "DTF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fud-fear-uncertainty-doubt", - "name": "Fear Uncertainty Doubt", - "symbol": "FUD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "up-uptoken", - "name": "UpToken", - "symbol": "UP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nanj-nanjcoin", - "name": "NANJCOIN", - "symbol": "NANJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bankwupt-bankwupt", - "name": "Bankwupt", - "symbol": "BANKWUPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hdog-husky-inu", - "name": "Husky Inu", - "symbol": "HDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drop-dropil", - "name": "Dropil", - "symbol": "DROP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "firegirl-firegirl", - "name": "FireGirl", - "symbol": "FIREGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sky-skywalker", - "name": "Skywalker", - "symbol": "SKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wm-wenmoon", - "name": "WenMoon", - "symbol": "WM", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hire-hirematch", - "name": "HireMatch", - "symbol": "HIRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ff-follow-friday", - "name": "Follow Friday", - "symbol": "FF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lr-looks-rare", - "name": "Looks Rare", - "symbol": "LR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgm-degem", - "name": "Degem", - "symbol": "DGM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fluz-fluz-fluz", - "name": "Fluz Fluz", - "symbol": "FLUZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "getx-guaranteed-ethurance-token-extra", - "name": "Guaranteed Ethurance Token Extra", - "symbol": "GETX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stq-storiqa", - "name": "Storiqa", - "symbol": "STQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "early-early", - "name": "Early", - "symbol": "$EARLY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hyperrise-bnb-hyper-rise", - "name": "BNB Hyper Rise", - "symbol": "HYPERRISE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deb-debitum", - "name": "Debitum", - "symbol": "DEB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gminu-gm-inu", - "name": "GM Inu", - "symbol": "GMINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibnft-shibnft", - "name": "ShibNFT", - "symbol": "SHIBNFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lbb-little-baby-bunny", - "name": "Little Baby Bunny", - "symbol": "LBB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "killer-killer-token", - "name": "KILLER TOKEN", - "symbol": "KILLER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buffedcate-token-buffedcate", - "name": "BuffedCate", - "symbol": "BUFFEDCATE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibapoos-shibapoos", - "name": "ShibaPoos", - "symbol": "SHIBAPOOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "notredame-notre-dame", - "name": "Notre Dame", - "symbol": "NOTREDAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moongirl-moongirl", - "name": "MoonGirl", - "symbol": "MOONGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sd-shiba-dash", - "name": "shiba dash", - "symbol": "SD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hct-hero-cat", - "name": "Hero Cat", - "symbol": "HCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qwm-qwe-were-mok", - "name": "Qwe Were Mok", - "symbol": "QWM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squid2-squid-game-20", - "name": "Squid Game 2.0", - "symbol": "SQUID2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dth-deveth", - "name": "devETH", - "symbol": "dTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lala-lala-world", - "name": "LALA World", - "symbol": "LALA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shp-sharpe-platform-token", - "name": "Sharpe Platform Token", - "symbol": "SHP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldc-leadcoin", - "name": "Leadcoin", - "symbol": "LDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xapp-xapp", - "name": "Xapp", - "symbol": "XAPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "occ-octoin-coin", - "name": "Octoin Coin", - "symbol": "OCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "say-spherepay", - "name": "SpherePay", - "symbol": "SAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chp-coinpoker", - "name": "CoinPoker", - "symbol": "CHP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "j8t-jet8", - "name": "JET8", - "symbol": "J8T", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpy-concensum", - "name": "CONCENSUM", - "symbol": "CPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ezt-eztoken", - "name": "EZToken", - "symbol": "EZT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stac-startercoin", - "name": "StarterCoin", - "symbol": "STAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exy-experty", - "name": "Experty", - "symbol": "EXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidpet-squid-pet", - "name": "Squid Pet", - "symbol": "SQUIDPET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tdx-tidex-token", - "name": "Tidex Token", - "symbol": "TDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bee-bee-token", - "name": "Bee Token", - "symbol": "BEE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gfloki-genshinflokiinu", - "name": "GenshinFlokiInu", - "symbol": "GFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rkt-rock", - "name": "Rock", - "symbol": "RKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "auto-cube", - "name": "Cube", - "symbol": "AUTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ct-crypto-twitter", - "name": "Crypto Twitter", - "symbol": "CT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ledu-education-ecosystem", - "name": "Education Ecosystem", - "symbol": "LEDU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xnk-ink-protocol", - "name": "Ink Protocol", - "symbol": "XNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squids-baby-squid-games", - "name": "Baby Squid Games", - "symbol": "SQUIDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fbro-flokibro", - "name": "FlokiBro", - "symbol": "FBRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xsd-soundac", - "name": "SounDAC", - "symbol": "XSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dtc-datacoin", - "name": "Datacoin", - "symbol": "DTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "-money", - "name": "Money", - "symbol": "$$$", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "icd-ic-defi", - "name": "IC DeFi", - "symbol": "ICD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "edge-dadi", - "name": "Edge", - "symbol": "DADI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ech-etherecash", - "name": "Etherecash", - "symbol": "ECH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gla-gladius-token", - "name": "Gladius Token", - "symbol": "GLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dittoinu-dittoinu", - "name": "DittoInu", - "symbol": "DITTOINU", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "w3c-w3coin", - "name": "W3Coin", - "symbol": "W3C", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bat-batcoin", - "name": "BatCoin", - "symbol": "BAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "smoke-smoke", - "name": "Smoke", - "symbol": "SMOKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dth-dether", - "name": "Dether", - "symbol": "DTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tig-tigereum", - "name": "Tigereum", - "symbol": "TIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grmpp-grumpypuppy", - "name": "GrumpyPuppy", - "symbol": "GRMPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "glb-globe-token", - "name": "Globe Token", - "symbol": "GLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evn-envion", - "name": "Envion", - "symbol": "EVN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tabby-tabby-token", - "name": "Tabby Token", - "symbol": "TABBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mak-metarkane", - "name": "MetArkane", - "symbol": "MAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crypto-cryptocom-exchange", - "name": "Crypto.com Exchange", - "symbol": "CRYPTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yoo-yoo", - "name": "YOO", - "symbol": "YOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nato-natonft", - "name": "NatoNFT", - "symbol": "NATO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pugdogeboy-pugdogeboy", - "name": "PugDogeBoy", - "symbol": "PUGDOGEBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ond-onepadio", - "name": "Onepad.io", - "symbol": "OND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogswap-dogswap", - "name": "DogSwap", - "symbol": "DOGSWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "farmersworld-farmersworldio", - "name": "farmersworld.io", - "symbol": "FARMERSWOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dc-dogecoin", - "name": "DogeCoin", - "symbol": "DC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drift-drift-protocol", - "name": "Drift Protocol", - "symbol": "DRIFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inrc-inr-classic", - "name": "INR Classic", - "symbol": "INRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fegbillionaire-feg-billionaire", - "name": "FEG BILLIONAIRE", - "symbol": "FEGBILLION", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "utnp-universa", - "name": "Universa", - "symbol": "UTNP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "akong-adakong", - "name": "AdaKong", - "symbol": "$AKONG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ram-ramswap", - "name": "RamSwap", - "symbol": "RAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fuckboy-fuckboy", - "name": "FuckBoy", - "symbol": "FUCKBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nfa-nfa-token", - "name": "NFA Token", - "symbol": "NFA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "solpnk-solanapunks", - "name": "SolanaPunks", - "symbol": "SOLPNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitch-bitchboy", - "name": "BitchBoy", - "symbol": "BITCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "neptuneinu-neptuneinu", - "name": "NeptuneInu", - "symbol": "NEPTUNEINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lyra-lyra-chain", - "name": "Lyra Chain", - "symbol": "LYRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dynastydoge-dynastydoge", - "name": "DynastyDoge", - "symbol": "DYNASTYDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moon-the-moon", - "name": "The Moon", - "symbol": "MOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "source-resource-finance", - "name": "Resource Finance", - "symbol": "SOURCE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "earth-planet-earth", - "name": "PLANET EARTH", - "symbol": "EARTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shika-shika-kage", - "name": "Shika Kage", - "symbol": "SHIKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defiinu-defiinu", - "name": "DeFiInu", - "symbol": "DEFIINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abra-abracom", - "name": "abra.com", - "symbol": "ABRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bigkishugirl-bigkishugirl", - "name": "BigKishuGirl", - "symbol": "BIGKISHUGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ycook-yieldcook-finance", - "name": "YieldCook Finance", - "symbol": "YCOOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pig-pool-pig-pool", - "name": "Pig Pool", - "symbol": "PIG POOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flesh-flesh-token", - "name": "Flesh Token", - "symbol": "FLESH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mxs-matrix-samurai", - "name": "Matrix Samurai", - "symbol": "MXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unicat-unicat-token", - "name": "Unicat Token", - "symbol": "UNICAT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "levo-levocoin", - "name": "Levocoin", - "symbol": "LEVO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "abake-angrybakery", - "name": "AngryBakery", - "symbol": "ABAKE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sup-superior-coin", - "name": "Superior Coin", - "symbol": "SUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jc-jesus-coin", - "name": "Jesus Coin", - "symbol": "JC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sjw-sjwcoin", - "name": "SJWCoin", - "symbol": "SJW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "indi-indicoin", - "name": "Indicoin", - "symbol": "INDI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mnl-no-man39s-land", - "name": "No Man's Land", - "symbol": "MNL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "satax-sata-exchange", - "name": "Sata Exchange", - "symbol": "SATAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "avh-animation-vision-cash", - "name": "Animation Vision Cash", - "symbol": "AVH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "loci-locicoin", - "name": "LOCIcoin", - "symbol": "LOCI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sloth-slothcoin", - "name": "Slothcoin", - "symbol": "SLOTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jew-shekel", - "name": "Shekel", - "symbol": "JEW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "acc-acchain", - "name": "ACChain", - "symbol": "ACC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "unifan-unifantom", - "name": "UniFantom", - "symbol": "UNIFAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "leaf-leafcoin", - "name": "LeafCoin", - "symbol": "LEAF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xmrg-monero-gold", - "name": "Monero Gold", - "symbol": "XMRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "esz-ethersportz", - "name": "EtherSportz", - "symbol": "ESZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbycat-baby-cat-girl", - "name": "Baby Cat Girl", - "symbol": "BBYCAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yokai-yokai-network", - "name": "Yokai Network", - "symbol": "YOKAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dxt-datawallet", - "name": "Datawallet", - "symbol": "DXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drpu-dcorp-utility", - "name": "DCORP Utility", - "symbol": "DRPU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stk-stk", - "name": "STK", - "symbol": "STK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jnt-jibrel-network", - "name": "Jibrel Network", - "symbol": "JNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pareto-pareto-network", - "name": "PARETO Rewards", - "symbol": "PARETO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ing-iungo", - "name": "Iungo", - "symbol": "ING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gdc-grandcoin", - "name": "GrandCoin", - "symbol": "GDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "flokidash-flokidash", - "name": "FlokiDash", - "symbol": "FLOKIDASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kdc-klondikecoin", - "name": "KlondikeCoin", - "symbol": "KDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mrja-ganjacoin", - "name": "GanjaCoin", - "symbol": "MRJA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blz-blazecoin", - "name": "BlazeCoin", - "symbol": "BLZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crn-cryptorun-network", - "name": "Cryptorun Network", - "symbol": "CRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sense-sense", - "name": "Sense", - "symbol": "SENSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vlc-valuechain", - "name": "ValueChain", - "symbol": "VLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hlc-halalchain", - "name": "Qitmeer", - "symbol": "HLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trf-travelflex", - "name": "CaixaPay", - "symbol": "CXP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "scamkiller-scamkillertoken", - "name": "ScamKillerToken", - "symbol": "SCAMKILLER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sms-speed-mining-service", - "name": "Speed Mining Service", - "symbol": "SMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "candy-candy", - "name": "Candy", - "symbol": "CANDY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hmc-hi-mutual-society", - "name": "Hi Mutual Society", - "symbol": "HMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oc-oceanchain", - "name": "OceanChain", - "symbol": "OC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nkc-nework", - "name": "Nework", - "symbol": "NKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xry-royalties", - "name": "Royalties", - "symbol": "XRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "slt-smartlands", - "name": "Smartlands", - "symbol": "SLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dubi-decentralized-universal-basic-income", - "name": "Decentralized Universal Basic Income", - "symbol": "DUBI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ary-block-array", - "name": "Block Array", - "symbol": "ARY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bpt-blockport", - "name": "Blockport", - "symbol": "BPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aid-aidcoin", - "name": "AidCoin", - "symbol": "AID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ytrf-abvd", - "name": "abvd", - "symbol": "YTRF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tmc-timescoin", - "name": "TimesCoin", - "symbol": "TMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "prs-pressone", - "name": "PressOne", - "symbol": "PRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sgirl-shark-girl", - "name": "Shark Girl", - "symbol": "SGIRL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "qbic-qbic", - "name": "Qbic", - "symbol": "QBIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sed-safeelondoge", - "name": "SafeElonDoge", - "symbol": "SED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctal-crypto-tank-a-lot", - "name": "Crypto Tank A Lot", - "symbol": "CTAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "angel-angel-chain", - "name": "Angel Chain", - "symbol": "ANGEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "legoshiba-lego-shiba", - "name": "Lego Shiba", - "symbol": "LEGOSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asq-athena-squid", - "name": "Athena Squid", - "symbol": "ASQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rockstarinu-rockstarinu", - "name": "RockstarInu", - "symbol": "ROCKSTARIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babypitbull-baby-pitbull", - "name": "BABY PITBULL", - "symbol": "BABYPITBUL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fantommoon-fantom-moon", - "name": "Fantom Moon", - "symbol": "FANTOMMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metashib-metaverse-shiba-inu", - "name": "METAVERSE SHIBA INU", - "symbol": "METASHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cnk-cinematik", - "name": "Cinematik", - "symbol": "CNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttt-testtoken", - "name": "TestToken", - "symbol": "TTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "of-ofcoin", - "name": "OFCOIN", - "symbol": "OF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hexfin-hex-finance", - "name": "HEX Finance", - "symbol": "HEXFIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "catmommy-catmommy", - "name": "CatMommy", - "symbol": "$CATMOMMY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "santama-santamainu", - "name": "SantamaInu", - "symbol": "SANTAMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "duck-safeduck", - "name": "SafeDuck", - "symbol": "DUCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babysantainu-babysantainu", - "name": "BabySantaInu", - "symbol": "BABYSANTAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defiwalletburn-defiwalletburn", - "name": "DefiWalletBurn", - "symbol": "DEFIWALLET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiba-shiba-millionaire", - "name": "Shiba Millionaire", - "symbol": "SHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ever-ever-token", - "name": "Ever Token", - "symbol": "EVER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "santaboy-santaboy", - "name": "SantaBoy", - "symbol": "SANTABOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shb-shibbnb", - "name": "shibbnb", - "symbol": "SHB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kanan-kanan-swap", - "name": "Kanan Swap", - "symbol": "KANAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wpx-warrior-project", - "name": "Warrior Project", - "symbol": "WPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kxb-klixbotfinance", - "name": "KlixBotFinance", - "symbol": "KXB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "guac-guacswap", - "name": "GuacSwap", - "symbol": "GUAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "santaboy-santa-boy", - "name": "Santa Boy", - "symbol": "SANTABOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "italinu-italian-inu", - "name": "Italian Inu", - "symbol": "ITALINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "awf-wengerfans", - "name": "WengerFans", - "symbol": "AWF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sanu-santainu", - "name": "SantaInu", - "symbol": "SANU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bogape-boggedape", - "name": "BoggedApe", - "symbol": "BOGAPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evisutoken-evisutoken", - "name": "EVISUTOKEN", - "symbol": "EVISUTOKEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bros-shiba-bro", - "name": "Shiba Bro", - "symbol": "BROS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coboy-token-coboy-token", - "name": "COBOY TOKEN", - "symbol": "COBOY TOKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gigandes-gigandes", - "name": "Gigandes", - "symbol": "GIGANDES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test-testing-token", - "name": "Testing Token", - "symbol": "TEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bithigh-bithigh", - "name": "BITHIGH", - "symbol": "BITHIGH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agl-angular", - "name": "Angular", - "symbol": "AGL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tta-tiny-tina39s-assault-on-dragon-keep", - "name": "Tiny Tina's Assault on Dragon Keep", - "symbol": "TTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyeth-baby-eth", - "name": "BABY ETH", - "symbol": "BABYETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kimj2-kimjongmoon2", - "name": "KimJongMoon2", - "symbol": "KIMJ2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pillowboy-pillow-boy", - "name": "Pillow Boy", - "symbol": "PILLOWBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scar-velhalla-pad", - "name": "Velhalla Pad", - "symbol": "SCAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zishi-zuki-shiba", - "name": "ZUKI SHIBA", - "symbol": "ZISHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "disc-discswap", - "name": "DiscSwap", - "symbol": "DISC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "velhalla-velhalla", - "name": "Velhalla", - "symbol": "VELHALLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smile-dentaltoken", - "name": "DentalToken", - "symbol": "SMILE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssx-super-sayan-finance", - "name": "Super Sayan Finance", - "symbol": "SSX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aso-askals", - "name": "Askals", - "symbol": "ASO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aqp-aqp", - "name": "AQP", - "symbol": "AQP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fcx-fcx-main", - "name": "FCX MAIN", - "symbol": "FCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsap-btcswap", - "name": "BTCSWAP", - "symbol": "BSAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "formplus-formplus", - "name": "FormPlus", - "symbol": "FORMPLUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gsz-godspeed", - "name": "GodSpeed", - "symbol": "GSZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lotx-lottrex", - "name": "Lottrex", - "symbol": "LOTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "km-koalamoon", - "name": "KoalaMoon", - "symbol": "KM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vl-vlaunch", - "name": "VLaunch", - "symbol": "VL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctribe-cointribe", - "name": "CoinTribe", - "symbol": "CTRIBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bmm-bluemanmars", - "name": "BlueManMars", - "symbol": "BMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spb-spongebit", - "name": "Spongebit", - "symbol": "SPB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zomi-zomi", - "name": "ZOMI", - "symbol": "ZOMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fbshiba-footballshibainu", - "name": "FootballShibaInu", - "symbol": "FBSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cotex-test-token", - "name": "Test Token", - "symbol": "COTEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bxs-buff-x-shiba-ichi", - "name": "Buff X Shiba Ichi", - "symbol": "BXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogezilla-babydogegamezilla", - "name": "BabyDogeGameZilla", - "symbol": "DOGEZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssar-silvershibaarmy", - "name": "SilverShibaArmy", - "symbol": "SSAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btc-baby-tomo-chain", - "name": "BABY TOMO CHAIN", - "symbol": "BTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kyoki-kyoki", - "name": "Kyoki", - "symbol": "KYOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zcm-zillcristymash", - "name": "ZillCristyMash", - "symbol": "ZCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-zuck-meta-zuck", - "name": "Meta Zuck", - "symbol": "META ZUCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "srt-strangerthings", - "name": "StrangerThings", - "symbol": "SRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babysaf-babysahafa", - "name": "BABYSAHAFA", - "symbol": "BABYSAF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sit-sitadel", - "name": "Sitadel", - "symbol": "SIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msm-mini-shiba-moon", - "name": "Mini Shiba Moon", - "symbol": "MSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bffdchihuahua-buffed-chihuahua", - "name": "Buffed Chihuahua", - "symbol": "BFFDCHIHUA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rambo-rambonk", - "name": "RamBonk", - "symbol": "RAMBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cake-lp-pancake-lps", - "name": "Pancake LPs", - "symbol": "CAKE-LP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "girl-girl-story", - "name": "GIRL STORY", - "symbol": "GIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "whales-shiba-whales", - "name": "Shiba Whales", - "symbol": "$WHALES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mavab-metaverseavalanche", - "name": "METAVERSEAVALANCHE", - "symbol": "$MAVAB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buffedchihuahua-buffedchihuahua", - "name": "BuffedChihuahua", - "symbol": "BUFFEDCHIH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "donka-donk-ape", - "name": "Donk Ape", - "symbol": "DONKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hori-horizon-chain", - "name": "Horizon Chain", - "symbol": "HORI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibwhales-shiba-whales", - "name": "Shiba Whales", - "symbol": "SHIBWHALES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tita-titan-hunters", - "name": "Titan Hunters", - "symbol": "TITA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mat-maya-token", - "name": "MAYA Token", - "symbol": "MAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonsquid-moonsquid", - "name": "MoonSquid", - "symbol": "MOONSQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cern-cern-chain", - "name": "Cern Chain", - "symbol": "CERN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ref-reftoken", - "name": "RefToken", - "symbol": "REF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gdc-goldendogecoin", - "name": "GoldenDogeCoin", - "symbol": "GDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "leaf-leafyfinance", - "name": "Leafy.Finance", - "symbol": "LEAF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minidsp-mini-darkspell", - "name": "Mini DarkSpell", - "symbol": "MINIDSP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kbg-kishubeargirl", - "name": "KishuBearGirl", - "symbol": "KBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidm-squidmars", - "name": "SquidMars", - "symbol": "SQUIDM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gfc-gamefi-collection", - "name": "GameFi Collection", - "symbol": "GFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "akai-akainu-of-shiba", - "name": "AKAINU OF SHIBA", - "symbol": "AKAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "try-tryhardsio", - "name": "tryhards.io", - "symbol": "TRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dhand-diamondhands", - "name": "DiamondHands", - "symbol": "DHAND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "geno-genopets", - "name": "GenoPets", - "symbol": "GENO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hmdxbb-hmdxbb", - "name": "HMDXBB", - "symbol": "HMDXBB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaflo-metaverse-floki-inu", - "name": "METAVERSE FLOKI INU", - "symbol": "METAFLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fmm-fatfomo", - "name": "FatFOMO", - "symbol": "FMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stingy-stingy-token", - "name": "STINGY TOKEN", - "symbol": "STINGY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usdp-usdp", - "name": "USDP", - "symbol": "USDP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "milkywayex-milkywayex", - "name": "MilkywayEx", - "symbol": "MILKYWAYEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mag-maggie", - "name": "Maggie", - "symbol": "MAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gat-gatcoin", - "name": "Global Awards Token", - "symbol": "GAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "read-read", - "name": "Read", - "symbol": "READ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stc-starchain", - "name": "StarChain", - "symbol": "STC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bar-titanium-blockchain", - "name": "Titanium Blockchain", - "symbol": "BAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sexc-sharex", - "name": "ShareX", - "symbol": "SEXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ipc-ipchain", - "name": "IPChain", - "symbol": "IPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "falafel-falafel", - "name": "Falafel", - "symbol": "FALAFEL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "light-lightchain", - "name": "LightChain", - "symbol": "LIGHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssc-selfsell", - "name": "SelfSell", - "symbol": "SSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mhokk-minihokk", - "name": "MiniHokk", - "symbol": "MHOKK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kmc-kemcredit", - "name": "KemCredit", - "symbol": "KMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "floge-flokidoge", - "name": "Flokidoge", - "symbol": "$FLOGE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "drydoge-dry-doge-metaverse", - "name": "Dry Doge Metaverse", - "symbol": "DRYDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebit-ebit", - "name": "eBIT", - "symbol": "EBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snake-snakeeyes", - "name": "SnakeEyes", - "symbol": "SNAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xot-internet-of-things", - "name": "Internet of Things", - "symbol": "XOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axe-axedao", - "name": "AxeDAO", - "symbol": "AXE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gary-president-johnson", - "name": "President Johnson", - "symbol": "GARY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tesla-teslacoilcoin", - "name": "TeslaCoilCoin", - "symbol": "TESLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "guc-goldunioncoin", - "name": "GoldUnionCoin", - "symbol": "GUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ipy-infinity-pay", - "name": "Infinity Pay", - "symbol": "IPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squid-squid-inu", - "name": "Squid Inu", - "symbol": "SQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hytv-hyper-tv", - "name": "Hyper TV", - "symbol": "HYTV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiback-shiback", - "name": "SHIBACK", - "symbol": "SHIBACK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ily-i-love-you", - "name": "I Love You", - "symbol": "ILY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "county-county-metaverse", - "name": "County Metaverse", - "symbol": "COUNTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zc-zombiecake", - "name": "ZombieCake", - "symbol": "ZC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitcf-first-bitcoin-capital", - "name": "First Bitcoin Capital", - "symbol": "BITCF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eltc2-eltc", - "name": "eLTC", - "symbol": "ELTC2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eusd-eusd", - "name": "eUSD", - "symbol": "EUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fc-facecoin", - "name": "Facecoin", - "symbol": "FC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fbl-faceblock", - "name": "Faceblock", - "symbol": "FBL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ntc-natcoin", - "name": "Natcoin", - "symbol": "NTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "10mt-10m-token", - "name": "10M Token", - "symbol": "10MT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uahpay-uahpay", - "name": "UAHPay", - "symbol": "UAHPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "egold-egold", - "name": "eGold", - "symbol": "EGOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "impactx-impactx", - "name": "ImpactX", - "symbol": "IMPACTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pres-president-trump", - "name": "President Trump", - "symbol": "PRES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gshib-genshinshibinu", - "name": "GenshinShibInu", - "symbol": "GSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ox-ox-fina", - "name": "OX Fina", - "symbol": "OX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sct-soma", - "name": "Soma", - "symbol": "SCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cash-cash-poker-pro", - "name": "Cash Poker Pro", - "symbol": "CASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbsc-minisatoshibsc", - "name": "MiniSatoshiBsc", - "symbol": "SBSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ryz-anryze", - "name": "ANRYZE", - "symbol": "RYZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swp-swapcoin", - "name": "Swapcoin", - "symbol": "SWP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ibtc-ibtc", - "name": "iBTC", - "symbol": "IBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btl-btltoken", - "name": "BTLToken", - "symbol": "BTL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "darkneo-darkneo", - "name": "DarkNeo", - "symbol": "DARKNEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mint-mintea", - "name": "Mintea", - "symbol": "MINT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dfp-defi-plaza", - "name": "Defi Plaza", - "symbol": "DFP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btc2x-bitcoin2x", - "name": "Bitcoin2x", - "symbol": "BTC2X", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wic-wi-coin", - "name": "Wi Coin", - "symbol": "WIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "phtm-phantom-galaxies", - "name": "Phantom Galaxies", - "symbol": "PHTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ftc-fantrade-coin", - "name": "FanTrade Coin", - "symbol": "FTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "era-era-swap", - "name": "Era Swap", - "symbol": "ERA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibbygirl-shibbygirl", - "name": "ShibbyGirl", - "symbol": "SHIBBYGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "emoon-evermoon", - "name": "Evermoon", - "symbol": "EMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kxz-kongxzilla", - "name": "KongXZilla", - "symbol": "KXZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dna-dna-defi", - "name": "DNA DeFi", - "symbol": "DNA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moon-metamoon", - "name": "MetaMoon", - "symbol": "MOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "totalwar-crypto-total-war-medieval-ii", - "name": "Crypto Total War: Medieval II", - "symbol": "TOTALWAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minisushi-mini-sushi-token", - "name": "Mini Sushi Token", - "symbol": "MINISUSHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tig-tiger", - "name": "TIGER", - "symbol": "TIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fxp-foxup", - "name": "Foxup", - "symbol": "FXP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "milkdogeex-milky-doge-ex", - "name": "MILKY DOGE EX", - "symbol": "MILKDOGEEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "birdlife-birdlife-finance", - "name": "BIRDLIFE FINANCE", - "symbol": "BIRDLIFE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibpark-shiba-park", - "name": "Shiba park", - "symbol": "SHIBPARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minex-minex", - "name": "Minex", - "symbol": "MINEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ett-encryptotel-eth", - "name": "EncryptoTel [ETH]", - "symbol": "ETT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mybux-mybux-rocket", - "name": "MyBux Rocket", - "symbol": "MYBUX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tdrm-tomoko-dream", - "name": "Tomoko Dream", - "symbol": "TDRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blx-blockchain-index", - "name": "Blockchain Index", - "symbol": "BLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfd-blackflokidoge", - "name": "BlackFlokiDoge", - "symbol": "BFD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnbd-bnbdtoken", - "name": "BNBDToken", - "symbol": "BNBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "roscoin-rols-coin", - "name": "ROLS COIN", - "symbol": "ROSCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gemgame-gemgame", - "name": "GemGame", - "symbol": "GEMGAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crown-crown", - "name": "CROWN", - "symbol": "CROWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blw-blw", - "name": "BLW", - "symbol": "BLW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "punch-punch-chain", - "name": "Punch Chain", - "symbol": "PUNCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "epic-epic-finance", - "name": "Epic Finance", - "symbol": "EPIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dotswap-dotswap", - "name": "DotSwap", - "symbol": "DOTSWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lenny-lenny", - "name": "Lenny", - "symbol": "LENNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "diglone-digilone", - "name": "DIGILONE", - "symbol": "DIGLONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtzck-mtzck", - "name": "$MTZCK", - "symbol": "$MTZCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hmz-harmonized-app", - "name": "Harmonized App", - "symbol": "HMZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stex-stex", - "name": "STEX", - "symbol": "STEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "timemachine-time-machine", - "name": "TIME MACHINE", - "symbol": "TIMEMACHIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hflk-hyper-floki", - "name": "Hyper Floki", - "symbol": "HFLK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokibdoge-flokibabydoge", - "name": "FlokiBabyDoge", - "symbol": "FLOKIBDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sjcx-storjcoin-x", - "name": "Storjcoin X", - "symbol": "SJCX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elmn-elonmoon", - "name": "ElonMoon", - "symbol": "ELMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mci-musiconomi", - "name": "Musiconomi", - "symbol": "MCI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcr-macro", - "name": "Macro", - "symbol": "MCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "thrustsquidmoon-thrustsquidmoon", - "name": "ThrustSquidMoon", - "symbol": "THRUSTSQUI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plx-plexcoin", - "name": "PlexCoin", - "symbol": "PLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "idot-infinitydot", - "name": "InfinityDOT", - "symbol": "IDOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snd-sand-coin", - "name": "Sand Coin", - "symbol": "SND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcm-btcmoon", - "name": "BTCMoon", - "symbol": "BTCM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "can-content-and-ad-network", - "name": "Content and AD Network", - "symbol": "CAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cor-corion", - "name": "CORION", - "symbol": "COR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nox-nitro", - "name": "Nitro", - "symbol": "NOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gl-gl-token", - "name": "GL Token", - "symbol": "GL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcs-bitcoin-silver", - "name": "Bitcoin Silver", - "symbol": "BTCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mob-mob-inu", - "name": "Mob Inu", - "symbol": "MOB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zuki-zuki", - "name": "Zuki", - "symbol": "ZUKI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "porton-portoneo", - "name": "PORTONEO", - "symbol": "PORTON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gcs-gamechain-system", - "name": "GameChain System", - "symbol": "GCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fap-fapcoin", - "name": "FAPcoin", - "symbol": "FAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "krm-karma", - "name": "Karma", - "symbol": "KRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plt-plateau-finance", - "name": "Plateau Finance", - "symbol": "PLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iqt-iquant", - "name": "iQuant", - "symbol": "IQT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sisa-sisa", - "name": "SISA", - "symbol": "SISA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fudd-dimoncoin", - "name": "DimonCoin", - "symbol": "FUDD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "venoma-venoma", - "name": "Venoma", - "symbol": "VENOMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zengold-zengold", - "name": "ZenGold", - "symbol": "ZENGOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dlc-dulcet", - "name": "Dulcet", - "symbol": "DLC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dhst-dhighstar", - "name": "DHighStar", - "symbol": "DHST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaflo-metaverse-floki", - "name": "METAVERSE FLOKI", - "symbol": "METAFLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibapumpvikings-shibapumpvikings", - "name": "SHIBAPUMPVIKINGS", - "symbol": "SHIBAPUMPV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibainu-shibainu", - "name": "SHIBAINU", - "symbol": "SHIBAINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "iboy-islandboy-token", - "name": "IslandBoy Token", - "symbol": "IBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "samsung-samsung", - "name": "Samsung", - "symbol": "SAMSUNG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tsunami-tsunami-dapps", - "name": "Tsunami DApps", - "symbol": "TSUNAMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tennis-tennis", - "name": "Tennis", - "symbol": "TENNIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "facebook-facebook", - "name": "Facebook", - "symbol": "FACEBOOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "portoe-portoelon", - "name": "PORTOELON", - "symbol": "PORTOE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ocg-official-card-game", - "name": "Official Card Game", - "symbol": "OCG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cherry-cherryfinance", - "name": "Cherry.Finance", - "symbol": "CHERRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hdlb-hodl-bucks", - "name": "HODL Bucks", - "symbol": "HDLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rocketbnb-rocketbnb", - "name": "RocketBNB", - "symbol": "RocketBNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sxut-spectreai-utility-token", - "name": "Spectre.ai Utility Token", - "symbol": "SXUT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "braveman-braveman", - "name": "BraveMan", - "symbol": "BRAVEMAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebu-ecoblue", - "name": "Ecoblue", - "symbol": "EBU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sweetdoge-sweetdogetoken", - "name": "SweetDogeToken", - "symbol": "SWEETDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonsol-moonsolana", - "name": "MOONSolana", - "symbol": "MOONSOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cenx-centcex", - "name": "Centcex", - "symbol": "CENX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wolf-wolfswap", - "name": "WolfSwap", - "symbol": "WOLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wsol-wrappedsolana", - "name": "WrappedSolana", - "symbol": "WSOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "teststar2-teststar2", - "name": "TestSTAR2", - "symbol": "TESTSTAR2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kingshib-king-shiba-inu", - "name": "KING SHIBA INU", - "symbol": "KINGSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adakong-adakongfb", - "name": "ADAKONG.fb", - "symbol": "ADAKONG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kite-kite-nft", - "name": "Kite NFT", - "symbol": "KITE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "luxe-luxi-palace", - "name": "Luxi Palace", - "symbol": "LUXE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "peer-peer-finance", - "name": "Peer Finance", - "symbol": "PEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wise-wise-protocol", - "name": "Wise Protocol", - "symbol": "WISE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "akmusk-alaska-musk", - "name": "Alaska musk", - "symbol": "AKMUSK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goofydoge-goofydoge-token", - "name": "GoofyDoge TOKEN", - "symbol": "GOOFYDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metavpad-metavpad", - "name": "METAVPAD", - "symbol": "METAVPAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "oras-oracleswap", - "name": "OracleSwap", - "symbol": "ORAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "beli-beli-token", - "name": "Beli Token", - "symbol": "BELI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blood-drop-of-blood", - "name": "Drop of Blood", - "symbol": "BLOOD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fanpeng-fantom-penguins", - "name": "Fantom Penguins", - "symbol": "FANPENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sea-sea", - "name": "SEA", - "symbol": "SEA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gtoken-gametime-token", - "name": "GameTime Token", - "symbol": "GTOKEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tttt2-testest2", - "name": "testest2", - "symbol": "TTTT2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cyborgstoken-cyborgstoken", - "name": "Cyborgstoken", - "symbol": "CYBORGSTOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdnc-big-dick-niggaz-in-da-crib-coin", - "name": "Big Dick Niggaz in Da Crib Coin", - "symbol": "BDNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "osiris-osiris-nft", - "name": "OSIRIS NFT", - "symbol": "OSIRIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drink-drink", - "name": "Drink", - "symbol": "DRINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokieth-flokiethereum", - "name": "FlokiEthereum", - "symbol": "FLOKIETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "herodoge", - "name": "HERODOGE", - "symbol": "HERODOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flying-dog-flying-dog", - "name": "Flying Dog", - "symbol": "FLYING DOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tc-twin-cities", - "name": "Twin Cities", - "symbol": "TC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pulsebdoge-pulsechainbabydoge", - "name": "PulseChainBabyDoge", - "symbol": "PULSEBDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "starl-starlink-official", - "name": "STARLINK OFFICIAL", - "symbol": "STARL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "glx-galaxy", - "name": "Galaxy", - "symbol": "GLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "st-startup-token", - "name": "Startup Token", - "symbol": "ST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sky-skypiea", - "name": "Skypiea", - "symbol": "SKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpc-cryptoplantsclub-token", - "name": "CryptoPlantsClub Token", - "symbol": "CPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shib-shiba-token", - "name": "Shiba Token", - "symbol": "SHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "disney-disney-metaverse", - "name": "DISNEY METAVERSE", - "symbol": "DISNEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogezilla-dogezilla", - "name": "DogeZilla", - "symbol": "DOGEZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kiscake-kishu-cake", - "name": "KISHU CAKE", - "symbol": "KISCAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "woolong-cowboy-bebop-corgi", - "name": "Cowboy Bebop Corgi", - "symbol": "WOOLONG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ice-icecreamswap", - "name": "IceCreamSwap", - "symbol": "ICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mananomics-decentraland-nomics", - "name": "Decentraland Nomics", - "symbol": "MANANOMICS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mayoi-mayoi-chain", - "name": "Mayoi Chain", - "symbol": "MAYOI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitshiba-bitshiba", - "name": "BitShiba", - "symbol": "BITSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tttt-testest", - "name": "testest", - "symbol": "TTTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mobo-madbonk", - "name": "madBONK", - "symbol": "MOBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pi-pi-network", - "name": "Pi Network", - "symbol": "PI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "donutboy-donutboy", - "name": "DonutBoy", - "symbol": "DONUTBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ltg-litecoin-gold", - "name": "LiteCoin Gold", - "symbol": "LTG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nimfa-nimfamoney", - "name": "Nimfamoney", - "symbol": "NIMFA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cld-cloud", - "name": "Cloud", - "symbol": "CLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "motel-motel-crypto", - "name": "Motel Crypto", - "symbol": "MOTEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "neog-neo-gold", - "name": "NEO GOLD", - "symbol": "NEOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "depress-depression", - "name": "Depression", - "symbol": "DEPRESS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zskull-zombie-skull-games", - "name": "Zombie Skull Games", - "symbol": "ZSKULL", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sxdt-spectreai-dividend-token", - "name": "Spectre.ai Dividend Token", - "symbol": "SXDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "naruto-naruto-inu", - "name": "Naruto Inu", - "symbol": "NARUTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grx-gold-reward-token", - "name": "GOLD Reward Token", - "symbol": "GRX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lev-leverj", - "name": "Leverj", - "symbol": "LEV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "esc-escroco", - "name": "Escroco", - "symbol": "ESC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "genshin-genshin-nft", - "name": "Genshin NFT", - "symbol": "GENSHIN", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btca-bitair", - "name": "Bitair", - "symbol": "BTCA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mvc-maverick-chain", - "name": "Maverick Chain", - "symbol": "MVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cefs-cryptopiafeeshares", - "name": "CryptopiaFeeShares", - "symbol": "CEFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dim-dimcoin", - "name": "DIMCOIN", - "symbol": "DIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fdx-fidentiax", - "name": "FidentiaX", - "symbol": "FDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nm-not-much", - "name": "Not Much", - "symbol": "NM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cfun-cfun", - "name": "CFun", - "symbol": "CFUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vzt-vezt", - "name": "Vezt", - "symbol": "VZT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prps-purpose", - "name": "Purpose", - "symbol": "PRPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plc-pluscoin", - "name": "PlusCoin", - "symbol": "PLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xid-sphre-air", - "name": "Sphere Identity", - "symbol": "XID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jpeg-jpegvaultdao", - "name": "JPEGvaultDAO", - "symbol": "JPEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qube-qube", - "name": "Qube", - "symbol": "QUBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hac-hackspace-capital", - "name": "Hackspace Capital", - "symbol": "HAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arct-arbitragect", - "name": "ArbitrageCT", - "symbol": "ARCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bte-bitserial", - "name": "BitSerial", - "symbol": "BTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "edt-etherdelta-token", - "name": "EtherDelta Token", - "symbol": "EDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ser-ser", - "name": "SER", - "symbol": "SER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bkx-bankex", - "name": "BANKEX", - "symbol": "BKX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaportal-metaportal", - "name": "MetaPortal", - "symbol": "METAPORTAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "horse-ethorse", - "name": "Ethouse", - "symbol": "HORSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "idxm-idex-membership", - "name": "IDEX Membership", - "symbol": "IDXM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "llt-lltoken", - "name": "LLToken", - "symbol": "LLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smore-smore", - "name": "Smore", - "symbol": "SMORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "big-bigone-token", - "name": "BigONE Token", - "symbol": "ONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cms-comsa-xem", - "name": "COMSA [XEM]", - "symbol": "CMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moac-moac", - "name": "MOAC", - "symbol": "MOAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ugc-ugchain", - "name": "ugChain", - "symbol": "UGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rebl-rebl", - "name": "REBL", - "symbol": "REBL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cms-comsa-eth", - "name": "COMSA [ETH]", - "symbol": "CMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ai-poly-ai", - "name": "POLY AI", - "symbol": "AI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ogshib-original-gangsta-shiba", - "name": "Original Gangsta Shiba", - "symbol": "OGSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cl-coinlancer", - "name": "Coinlancer", - "symbol": "CL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tp-token-swap", - "name": "Token Swap", - "symbol": "TP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "at-aware", - "name": "AWARE", - "symbol": "AT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "viu-viuly", - "name": "Viuly", - "symbol": "VIU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mot-olympus-labs", - "name": "Olympus Labs", - "symbol": "MOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vra-verapay", - "name": "VeraPay", - "symbol": "VRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shield-shield-finance", - "name": "Shield Finance", - "symbol": "SHIELD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sada-strongada", - "name": "StrongADA", - "symbol": "$SADA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lion-lionswap", - "name": "LionSwap", - "symbol": "LION", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kite-kitteh", - "name": "Kitteh", - "symbol": "KITE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cakeeth-cakeethereum", - "name": "CAKEEthereum", - "symbol": "CAKEETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rainbow-rainbowfinance", - "name": "Rainbow.Finance", - "symbol": "RAINBOW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "richshibaf-richshiba", - "name": "RichShiba", - "symbol": "RICHSHIBAF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "com2verse-com2verse", - "name": "Com2Verse", - "symbol": "COM2VERSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaverseshiba-metaverseshiba", - "name": "METAVERSESHIBA", - "symbol": "METAVERSES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "game-inu-games", - "name": "Inu Games", - "symbol": "GAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ycb-yooshi-chinese-boy", - "name": "Yooshi Chinese Boy", - "symbol": "$YCB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ocean-burstocean", - "name": "BurstOcean", - "symbol": "OCEAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbdog-musclebulldog", - "name": "MuscleBullDog", - "symbol": "MBDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chi-chihuahua", - "name": "Chihuahua", - "symbol": "CHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokialien-flokialien", - "name": "FlokiAlien", - "symbol": "FLOKIALIEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "auk-auk", - "name": "AUK", - "symbol": "AUK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "curry-curryswap", - "name": "CurrySwap", - "symbol": "CURRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mnk-monke", - "name": "Monke", - "symbol": "MNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lzyj-lazyjoe", - "name": "LazyJoe", - "symbol": "LZYJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nyfty-niftypayscom", - "name": "NIFTYPAYS.com", - "symbol": "NYFTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wallstreetinu-wall-street-inu", - "name": "Wall Street Inu", - "symbol": "WALLSTREET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyfan-babyfantom", - "name": "BabyFantom", - "symbol": "BABYFAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pulsesol-pulsechainsolana", - "name": "PulseChainSolana", - "symbol": "PULSESOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eversol-eversolana", - "name": "EverSolana", - "symbol": "EVERSOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bshib-bitshiba", - "name": "BitShiba", - "symbol": "BSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "daisy-daisy-chain", - "name": "Daisy Chain", - "symbol": "DAISY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mps-microparallelspace", - "name": "MicroParallelSpace", - "symbol": "MPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogesv-dogeshibaviking", - "name": "DogeShibaViking", - "symbol": "DOGESV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibrich-shiba-rich", - "name": "Shiba Rich", - "symbol": "SHIBRICH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "solbnb-solanabnb", - "name": "SolanaBNB", - "symbol": "SOLBNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rsgp-gp", - "name": "GP", - "symbol": "RSGP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hexstk-hexstake", - "name": "HEXStake", - "symbol": "HEXSTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amazon-amazon", - "name": "Amazon", - "symbol": "AMAZON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aurora-aurora-dimension", - "name": "Aurora dimension", - "symbol": "AURORA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apeboy-apeboy", - "name": "APEBOY", - "symbol": "APEBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babypets-babymicropets", - "name": "BabyMicroPets", - "symbol": "BABYPETS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "abema-abema-finance", - "name": "Abema Finance", - "symbol": "ABEMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "db2-dontbuy2", - "name": "$DontBuy2$", - "symbol": "DB2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aot-age-of-tanks", - "name": "Age of Tanks", - "symbol": "AOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "heist-heist", - "name": "Heist", - "symbol": "HEIST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chia-chia", - "name": "Chia", - "symbol": "CHIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "c4-c4", - "name": "C4", - "symbol": "C4", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pumpit-nodump", - "name": "NoDump", - "symbol": "PUMPIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pnut-peanut", - "name": "Peanut", - "symbol": "PNUT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dit-dinar-token", - "name": "Dinar Token", - "symbol": "DIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sod-shiba-on-doge", - "name": "Shiba On Doge", - "symbol": "SOD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shockfinance-shock-finance", - "name": "Shock Finance", - "symbol": "SHOCKFINAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ksmt-kosmotoken", - "name": "KOSMOTOKEN", - "symbol": "KSMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sanlink-sanshu-link", - "name": "Sanshu LINK", - "symbol": "SANLINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dexsportl-dexsport", - "name": "DEXSPORT", - "symbol": "DEXSPORTL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bogfloki-boggedfloki", - "name": "BoggedFloki", - "symbol": "BOGFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apollo-apolloio-finance", - "name": "Apollo.io Finance", - "symbol": "APOLLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitbucket-bitbucketorg-digital", - "name": "Bitbucket.org Digital", - "symbol": "BITBUCKET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alibaba-alibaba", - "name": "ALIBABA", - "symbol": "ALIBABA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pubg-pubg", - "name": "PUBG", - "symbol": "PUBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tehno-technodium", - "name": "Technodium", - "symbol": "TEHNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gdp-goofydoge-presale", - "name": "GoofyDoge Presale", - "symbol": "GDP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibasquid-shibasquid", - "name": "ShibaSquid", - "symbol": "SHIBASQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elsh-elonshow", - "name": "ELONSHOW", - "symbol": "ELSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gd-goldedogecoin", - "name": "GoldeDogeCoin", - "symbol": "GD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lex-lex4all", - "name": "Lex4All", - "symbol": "LEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aki-aki", - "name": "Aki", - "symbol": "AKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mwoj-mini-wojak", - "name": "Mini Wojak", - "symbol": "MWOJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "musk-musk-coin", - "name": "MUSK COIN", - "symbol": "MUSK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eco-ecocom", - "name": "eco.com", - "symbol": "ECO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mnt-meta-network", - "name": "Meta Network", - "symbol": "MNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fsc-floki-space-cake", - "name": "FLOKI SPACE CAKE", - "symbol": "FSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asng-assange", - "name": "Assange", - "symbol": "ASNG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "puto-proyectundertokeno", - "name": "ProyectUnderTokenO", - "symbol": "PUTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyyoda-babyyoda", - "name": "BabyYoda", - "symbol": "BABYYODA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cakesol-cakesolana", - "name": "CAKESolana", - "symbol": "CAKESOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tts-the-third-space", - "name": "The third space", - "symbol": "TTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pumpd-pump", - "name": "pump", - "symbol": "PUMPD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metasquid-meta-squid", - "name": "Meta Squid", - "symbol": "METASQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zebra-zebraswap", - "name": "ZebraSwap", - "symbol": "ZEBRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gxxjj-ge-xiao-xia-ji-ji", - "name": "Ge Xiao Xia Ji Ji", - "symbol": "GXXJJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shdoge-shibadoge", - "name": "shibadoge", - "symbol": "SHDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tau-atlantis-metaverse", - "name": "Atlantis Metaverse", - "symbol": "TAU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiborginu-shiborginu", - "name": "SHIBORGINU", - "symbol": "SHIBORGINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sis-symbiosis", - "name": "Symbiosis", - "symbol": "SIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vr-victoria-vr", - "name": "Victoria VR", - "symbol": "VR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nxt-nextgemtoken", - "name": "NEXTGEMTOKEN", - "symbol": "NXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dream1-dream1", - "name": "Dream1", - "symbol": "DREAM1", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "catcoin-catcoin", - "name": "catCoin", - "symbol": "CATCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atpad-atompad", - "name": "Atompad", - "symbol": "ATPAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mar-marsell", - "name": "Marsell", - "symbol": "MAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dsc-dstarlights-coin", - "name": "Dstarlights Coin", - "symbol": "$DSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chokedstarved-choked-with-news-and-starved-of-history", - "name": "choked with news and starved of history", - "symbol": "CHOKEDSTAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rdoge-ryoshi-doge", - "name": "Ryoshi Doge", - "symbol": "RDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidzilla-squidzilla", - "name": "SQUIDZilla", - "symbol": "SQUIDZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gldz-goldzilla", - "name": "GoldZilla", - "symbol": "GLDZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rici-rici-elon", - "name": "RICI ELON", - "symbol": "RICI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "richyooshi-rich-yooshi", - "name": "RICH YOOSHI", - "symbol": "RICHYOOSHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hubbybubby-hubby-bubby-girl", - "name": "Hubby Bubby Girl", - "symbol": "HUBBYBUBBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chibibunnygirl-chibibunnygirl", - "name": "ChibiBunnyGirl", - "symbol": "CHIBIBUNNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wpump-wen-pump", - "name": "WEN PUMP", - "symbol": "$WPUMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dshib-dragon-shiba", - "name": "Dragon Shiba", - "symbol": "DSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "marsneedsbabies-marsneedsbabies", - "name": "MarsNeedsBabies", - "symbol": "MARSNEEDSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scar-velhalla", - "name": "Velhalla", - "symbol": "SCAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "booty-big-booty-coin", - "name": "Big Booty Coin", - "symbol": "BOOTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ec-ecocoin", - "name": "EcoCoin", - "symbol": "EC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "terk-terkehhcom", - "name": "Terkehh.com", - "symbol": "TERK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hago-ihagonet", - "name": "ihago.net", - "symbol": "HAGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dpt-diviner-protocol", - "name": "Diviner Protocol", - "symbol": "DPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cultx-cultonomics", - "name": "Cultonomics", - "symbol": "CULTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "holdem-holdem", - "name": "Holdem", - "symbol": "HOLDEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ktam-krungthaiam-token", - "name": "KrungthaiAM Token", - "symbol": "KTAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bosu-bosu-inu", - "name": "Bosu Inu", - "symbol": "BOSU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "solbdoge-solanababydoge", - "name": "SolanaBabyDoge", - "symbol": "SOLBDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "horseshib-horse-shiba", - "name": "HORSE SHIBA", - "symbol": "HORSESHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trade-polytrade-pos", - "name": "Polytrade (PoS)", - "symbol": "TRADE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "5lf-r-5lf-r", - "name": "5LF-R", - "symbol": "5LF-R", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lazy-greensloth", - "name": "GreenSloth", - "symbol": "LAZY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asinu-astroshibainu", - "name": "AstroShibaInu", - "symbol": "ASINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shbm-shibmoom", - "name": "SHIBMOOM", - "symbol": "SHBM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tttt6-testest6", - "name": "testest6", - "symbol": "TTTT6", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mimeshinu-mini-meta-shiba-inu", - "name": "Mini Meta Shiba Inu", - "symbol": "MIMESHINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "redrum-redrum-protocol", - "name": "Redrum Protocol", - "symbol": "REDRUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbsquid-baby-squid-game", - "name": "BABY SQUID GAME", - "symbol": "BBSQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ba-baby-astro", - "name": "Baby Astro", - "symbol": "BA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tweet-birdbath", - "name": "birdbath", - "symbol": "TWEET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccm100-ccminer", - "name": "CCMiner", - "symbol": "CCM100", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tttt5-testest5", - "name": "testest5", - "symbol": "TTTT5", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sonus-sonusgame", - "name": "SONUSGAME", - "symbol": "SONUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibahorse-shiba-horse", - "name": "Shiba Horse", - "symbol": "SHIBAHORSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bourbon-bourbon", - "name": "Bourbon", - "symbol": "BOURBON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adream-american-dream", - "name": "American Dream", - "symbol": "ADREAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "riot-riotgames", - "name": "RIOTgames", - "symbol": "RIOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sinu-squid-moon-inu", - "name": "Squid Moon Inu", - "symbol": "SINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qbk-qibuck-asset", - "name": "Qibuck Asset", - "symbol": "QBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gad-galaxy-defenders", - "name": "Galaxy Defenders", - "symbol": "GAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "polycity-poly-city", - "name": "Poly City", - "symbol": "POLYCITY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcb-mcdexio", - "name": "mcdex.io", - "symbol": "MCB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibasol-shibasolana", - "name": "ShibaSolana", - "symbol": "SHIBASOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "winner-winner", - "name": "winner", - "symbol": "WINNER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcdudes-btc-dudes", - "name": "BTC Dudes", - "symbol": "BTCDUDES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaface-metaface", - "name": "METAFACE", - "symbol": "METAFACE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mkzilla-metakingzilla", - "name": "METAKINGZILLA", - "symbol": "MKZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ryoshb-ryoshb-token", - "name": "Ryoshb Token", - "symbol": "RYOSHB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "puzzle-puzzle-protocol", - "name": "Puzzle Protocol", - "symbol": "PUZZLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fashiondoge-fashiondoge", - "name": "FashionDoge", - "symbol": "FASHIONDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lmoon-laika-moon", - "name": "Laika Moon", - "symbol": "LMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shero-shibahero", - "name": "ShibaHero", - "symbol": "SHERO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msd-meta-squid-doge", - "name": "Meta Squid Doge", - "symbol": "MSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sshr-star-shiba-royal", - "name": "Star Shiba Royal", - "symbol": "SSHR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crtm-corethum", - "name": "Corethum", - "symbol": "CRTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "niu-new-inuniverse", - "name": "New InUniverse", - "symbol": "NIU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xmas-christmas-shiba-inu", - "name": "Christmas Shiba Inu", - "symbol": "XMAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metas-meta-space", - "name": "META SPACE", - "symbol": "METAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axo-axolittles", - "name": "AXOLITTLES", - "symbol": "AXO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smi-squid-moon-inu", - "name": "Squid Moon Inu", - "symbol": "SMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cryptogame-cryptogame", - "name": "CryptoGame", - "symbol": "CRYPTOGAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogefv-dogeflokiviking", - "name": "DogeFlokiViking", - "symbol": "DOGEFV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "butter-butterfinance", - "name": "Butter.Finance", - "symbol": "BUTTER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yel-yellow-token", - "name": "Yellow Token", - "symbol": "YEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtm-mtmgaming", - "name": "MTMGaming", - "symbol": "MTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rbx-ripto-bux", - "name": "Ripto Bux", - "symbol": "RBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "e4row-e4row", - "name": "E4ROW", - "symbol": "E4ROW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coxst-coexistcoin", - "name": "CoExistCoin", - "symbol": "COXST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibd-shiba-dao", - "name": "SHIBA DAO", - "symbol": "SHIBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebch-ebch", - "name": "EBCH", - "symbol": "EBCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rmc-remicoin", - "name": "Remicoin", - "symbol": "RMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "roc-rasputin-online-coin", - "name": "Rasputin Online Coin", - "symbol": "ROC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b-bankcoin", - "name": "Bankcoin", - "symbol": "B@", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "casino-casino", - "name": "Casino", - "symbol": "CASINO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bpc-bitpark-coin", - "name": "Bitpark Coin", - "symbol": "BPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jns-janus", - "name": "Janus", - "symbol": "JNS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pb-piggy-bank", - "name": "Piggy Bank", - "symbol": "PB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sift-smart-investment-fund-token", - "name": "Smart Investment Fund Token", - "symbol": "SIFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "icoo-ico-openledger", - "name": "ICO OpenLedger", - "symbol": "ICOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metadef-metadefi", - "name": "MetaDefi", - "symbol": "METADEF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rustbits-rustbits", - "name": "Rustbits", - "symbol": "RUSTBITS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dar-darcrus", - "name": "Darcrus", - "symbol": "DAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arc-arcade-token", - "name": "Arcade Token", - "symbol": "ARC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ett-encryptotel-waves", - "name": "EncryptoTel [WAVES]", - "symbol": "ETT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fxe-futurxe", - "name": "FuturXe", - "symbol": "FXE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctic3-coimatic-30", - "name": "Coimatic 3.0", - "symbol": "CTIC3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "landi-landi-token", - "name": "LANDI Token", - "symbol": "LANDI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "pos-pokemonspace", - "name": "PokemonSpace", - "symbol": "POS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vrs-veros", - "name": "Veros", - "symbol": "VRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uet-useless-ethereum-token", - "name": "Useless Ethereum Token", - "symbol": "UET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "biteur-biteur", - "name": "bitEUR", - "symbol": "BITEUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dibc-dibcoin", - "name": "DIBCOIN", - "symbol": "DIBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "exn-exchangen", - "name": "ExchangeN", - "symbol": "EXN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bln-bolenum", - "name": "Bolenum", - "symbol": "BLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "300-300-token", - "name": "300 Token", - "symbol": "300", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ereal-ereal", - "name": "eREAL", - "symbol": "EREAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asa-astrosanta", - "name": "AstroSanta", - "symbol": "ASA", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ice-idice", - "name": "iDice", - "symbol": "ICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitgold-bitgold", - "name": "bitGold", - "symbol": "BITGOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kois-koiswap", - "name": "KOISWAP", - "symbol": "KOIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eagle-eaglecoin", - "name": "EagleCoin", - "symbol": "EAGLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitsilver-bitsilver", - "name": "bitSilver", - "symbol": "BITSILVER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bas-bitasean", - "name": "BitAsean", - "symbol": "BAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "egas-ethgas", - "name": "ETHGAS", - "symbol": "EGAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wsq-wolfsquid", - "name": "WolfSquid", - "symbol": "WSQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bogdanoff-crab17", - "name": "CRAB17", - "symbol": "BOGDANOFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "s1l-secureonelabs", - "name": "SecureOneLabs", - "symbol": "S1L", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiba-shiba-plus", - "name": "SHIBA PLUS", - "symbol": "SHIBA+", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyfegin-baby-fegnomics", - "name": "Baby Fegnomics", - "symbol": "BABYFEGIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "llg-lucidlandsgem", - "name": "LucidLandsGem", - "symbol": "LLG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fuckyougankers-gankers-is-making-us-pay-him-tens-of-thousands-of-dollars-for-nft-upload-costs-when-i", - "name": "Gankers is making us pay him tens of thousands of dollars for nft upload costs when it really only c", - "symbol": "FUCKYOUGAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "littlepetgirl-littlepetgirl", - "name": "LittlePetGirl", - "symbol": "LITTLEPETG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nftzuki-nftzuki-moba", - "name": "NFTZuki Moba", - "symbol": "NFTZUKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metadefi-metadefi", - "name": "MetaDefi", - "symbol": "METADEFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "microdogeking-microdogeking", - "name": "MicroDogeKing", - "symbol": "MICRODOGEK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "everyonewantstobemyenemy-everyonewantstobemyenemy", - "name": "EveryoneWantsToBeMyEnemy", - "symbol": "EVERYONEWA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitbtc-bitbtc", - "name": "bitBTC", - "symbol": "BITBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trdt-trident-group", - "name": "Trident Group", - "symbol": "TRDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mxrp-mini-xrp", - "name": "Mini XRP", - "symbol": "$MXRP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sdrn-senderon", - "name": "Senderon", - "symbol": "SDRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "imt-immortality", - "name": "Immortality", - "symbol": "IMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nto-fujinto", - "name": "Fujinto", - "symbol": "NTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogefans-fans-of-doge", - "name": "Fans of Doge", - "symbol": "DOGEFANS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ddf-digitaldevelopersfund", - "name": "DigitalDevelopersFund", - "symbol": "DDF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btsr-btsr", - "name": "BTSR", - "symbol": "BTSR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "newb-newbium", - "name": "Newbium", - "symbol": "NEWB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hero-sovereign-hero", - "name": "Sovereign Hero", - "symbol": "HERO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btwty-bit20", - "name": "Bit20", - "symbol": "BTWTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uny-unity-ingot", - "name": "Unity Ingot", - "symbol": "UNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rlt-roulettetoken", - "name": "RouletteToken", - "symbol": "RLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ntwk-network-token", - "name": "Network Token", - "symbol": "NTWK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blockpay-blockpay", - "name": "BlockPay", - "symbol": "BLOCKPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "siri-sirius", - "name": "Sirius", - "symbol": "SIRI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eot-eot-token", - "name": "EOT Token", - "symbol": "EOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dix-dix-asset", - "name": "Dix Asset", - "symbol": "DIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "func-funcoin", - "name": "FUNCoin", - "symbol": "FUNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zcg-zlancer", - "name": "Zlancer", - "symbol": "ZCG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cct-crystal-clear", - "name": "Crystal Clear", - "symbol": "CCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coco-cocomelon", - "name": "Cocomelon", - "symbol": "COCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babymeta-baby-meta-moon", - "name": "Baby Meta Moon", - "symbol": "BABYMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dtc-dogetokencoin", - "name": "DogeTokenCoin", - "symbol": "DTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sol-solana-bsc", - "name": "Solana BSC", - "symbol": "SOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bswap-bingoswap", - "name": "Bingoswap", - "symbol": "BSWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wtt-giga-watt-token", - "name": "Giga Watt Token", - "symbol": "WTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shib-rocky-shiba-inu-gold", - "name": "Shiba Inu Gold", - "symbol": "SHIB ROCKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "num-num-token-via-chainportio", - "name": "NUM Token [via ChainPort.io]", - "symbol": "NUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bladeavatar-blade-avatar-nft", - "name": "Blade Avatar NFT", - "symbol": "BLADEAVATA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lb-lightningboy", - "name": "LightningBoy", - "symbol": "LB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "noon-noon", - "name": "NOON", - "symbol": "NOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buffsquid-buffedsquid", - "name": "BuffedSquid", - "symbol": "BUFFSQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ibusddefi-ibusddefi", - "name": "iBUSDDeFi", - "symbol": "IBUSDDEFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiryo-shiryo-inu", - "name": "Shiryo-Inu", - "symbol": "SHIRYO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "google-google", - "name": "Google", - "symbol": "GOOGLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eapu-easypump", - "name": "EasyPump", - "symbol": "EAPU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "static-staticcoin", - "name": "StaticCoin", - "symbol": "STATIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cst-chainstack-protocol", - "name": "ChainStack Protocol", - "symbol": "CST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swtg-sweetgirl", - "name": "SweetGirl", - "symbol": "SWTG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wgo-wavesgo", - "name": "WavesGo", - "symbol": "WGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "otx-octanox", - "name": "Octanox", - "symbol": "OTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pos-postoken", - "name": "PoSToken", - "symbol": "POS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "king-king-floki", - "name": "King Floki", - "symbol": "KING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sgr-sugar-exchange", - "name": "Sugar Exchange", - "symbol": "SGR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xbl-billionaire-token", - "name": "Billionaire Token", - "symbol": "XBL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cfd-confido", - "name": "Confido", - "symbol": "CFD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tkr-cryptoinsight", - "name": "CryptoInsight", - "symbol": "TKR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebet-ethbet", - "name": "EthBet", - "symbol": "EBET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rutc-rumito", - "name": "Rumito", - "symbol": "RUTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metagame-metagame", - "name": "MetaGame", - "symbol": "METAGAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lnk-link-platform", - "name": "Link Platform", - "symbol": "LNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "strc-starcredits", - "name": "StarCredits", - "symbol": "STRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbi-monster-byte", - "name": "Monster Byte", - "symbol": "MBI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ace-ace-tokenstars", - "name": "ACE (TokenStars)", - "symbol": "ACE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trct-tracto", - "name": "Tracto", - "symbol": "TRCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mfc-multi-farm-capital", - "name": "Multi-Farm Capital", - "symbol": "MFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fuck-fucktoken", - "name": "FuckToken", - "symbol": "FUCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrt-miners-reward-token", - "name": "Miners' Reward Token", - "symbol": "MRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kbr-kubera-coin", - "name": "Kubera Coin", - "symbol": "KBR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ats-authorship", - "name": "Authorship", - "symbol": "ATS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ocl-oceanlab", - "name": "Oceanlab", - "symbol": "OCL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babymicroshiba-baby-micro-shiba", - "name": "Baby Micro Shiba", - "symbol": "BABYMICROS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cft-cryptoforecast", - "name": "CryptoForecast", - "symbol": "CFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kln-kolion", - "name": "Kolion", - "symbol": "KLN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pipl-piplcoin", - "name": "PiplCoin", - "symbol": "PIPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "epy-emphy", - "name": "Emphy", - "symbol": "EPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "acc-accelerator-network", - "name": "Accelerator Network", - "symbol": "ACC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fyn-fundyourselfnow", - "name": "FundYourselfNow", - "symbol": "FYN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-meta-chain", - "name": "Meta Chain", - "symbol": "META", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gjc-global-jobcoin", - "name": "Global Jobcoin", - "symbol": "GJC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gim-gimli", - "name": "Gimli", - "symbol": "GIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flik-flik", - "name": "FLiK", - "symbol": "FLIK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctx-cartaxi-token", - "name": "CarTaxi Token", - "symbol": "CTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ndc-neverdie", - "name": "NEVERDIE", - "symbol": "NDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rkc-royal-kingdom-coin", - "name": "Royal Kingdom Coin", - "symbol": "RKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "riya-etheriya", - "name": "Etheriya", - "symbol": "RIYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcap-mcap", - "name": "MCAP", - "symbol": "MCAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "void-avalanchevoid", - "name": "AvalancheVoid", - "symbol": "VOID", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "smart-smartbillions", - "name": "SmartBillions", - "symbol": "SMART", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "etbs-ethbits", - "name": "Ethbits", - "symbol": "ETBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "et-ether-terrestrial", - "name": "Ether Terrestrial", - "symbol": "ET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "good-goodomy", - "name": "Goodomy", - "symbol": "GOOD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axus-axus-coin", - "name": "Axus Coin", - "symbol": "AXUS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "wbfloki-wrappedbabyfloki", - "name": "WrappedBabyFloki", - "symbol": "WBFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apx-apx", - "name": "APX", - "symbol": "APX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ping-cryptoping", - "name": "CryptoPing", - "symbol": "PING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "real-real", - "name": "REAL", - "symbol": "REAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qvt-qvolta", - "name": "Qvolta", - "symbol": "QVT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbrs-embers", - "name": "Embers", - "symbol": "MBRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stinkcoin-thiscoinstinks", - "name": "ThisCoinStinks", - "symbol": "STINKCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apegirl-apegirl", - "name": "ApeGirl", - "symbol": "APEGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbredshib-baby-red-shiba-inu", - "name": "Baby Red Shiba Inu", - "symbol": "BBREDSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drlecter-drlecter", - "name": "DrLecter", - "symbol": "DRLECTER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b0zo-bozocash", - "name": "BozoCash", - "symbol": "B0ZO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wand-wandx", - "name": "WandX", - "symbol": "WAND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogegirl-dogegirl", - "name": "DOGEGIRL", - "symbol": "DOGEGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gunmania-gun-mania", - "name": "Gun Mania", - "symbol": "GUNMANIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b0zot30-bozocasht30", - "name": "BozoCashT30", - "symbol": "B0ZOT30", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goh-gotahub-coin", - "name": "GOTAHUB Coin", - "symbol": "GOH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bep-binance-smart-chain-play", - "name": "Binance Smart Chain Play", - "symbol": "BEP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b0zot29-bozocasht29", - "name": "BozoCashT29", - "symbol": "B0ZOT29", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b0zot28-bozocasht28", - "name": "BozoCashT28", - "symbol": "B0ZOT28", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aoatoken-aoatoken", - "name": "AOATOKEN", - "symbol": "AOATOKEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fuck-covid-fuck-covid", - "name": "FUCK COVID", - "symbol": "FUCK COVID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prl-parallel-token", - "name": "Parallel Token", - "symbol": "PRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "picopolis-picopolis", - "name": "Picopolis", - "symbol": "PICOPOLIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dnb4-dontbuy4", - "name": "$DontBuy4$", - "symbol": "DNB4", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "caitcoingirl-caitcoingirl", - "name": "CaitCoinGirl", - "symbol": "CAITCOINGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "desu-dexsportio", - "name": "dexsport.io", - "symbol": "DESU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fade-fade", - "name": "FADE", - "symbol": "FADE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kenshinfloki-kenshinfloki", - "name": "KenshinFloki", - "symbol": "KENSHINFLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "compactfinance-compactfinance", - "name": "CompactFinance", - "symbol": "COMPACTFIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evilshiba-evilshiba-game", - "name": "EvilShiba Game", - "symbol": "EVILSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "coby-corgiboy", - "name": "CorgiBoy", - "symbol": "COBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tttt8-testest8", - "name": "testest8", - "symbol": "TTTT8", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chickn-chicken", - "name": "CHICKEN", - "symbol": "CHICKN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibasignals-shibasignals", - "name": "SHIBASIGNALS", - "symbol": "SHIBASIGNA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shib-shib", - "name": "Shib", - "symbol": "SHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "krc-king-rooster", - "name": "King Rooster", - "symbol": "KRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1453-1453-token", - "name": "1453 TOKEN", - "symbol": "1453", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apebeast-apebeast", - "name": "ApeBeast", - "symbol": "APEBEAST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "conegame-conegame", - "name": "ConeGame", - "symbol": "CONEGAME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minipeng-minipenguins", - "name": "MiniPenguins", - "symbol": "MINIPENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btczilla-btczilla", - "name": "BTCzilla", - "symbol": "BTCZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pi-pi", - "name": "PI", - "symbol": "PI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "idot-idot-finance", - "name": "iDot Finance", - "symbol": "IDOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnbpr-bnbprivate", - "name": "BNBprivate", - "symbol": "BNBPR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sgsm-squidgamesafemoon", - "name": "SquidGameSafeMoon", - "symbol": "SGSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tttt7-testest7", - "name": "testest7", - "symbol": "TTTT7", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnb-dogezilla", - "name": "Dogezilla", - "symbol": "BNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vise-coinviseco", - "name": "coinvise.co", - "symbol": "VISE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sfloki-superfloki", - "name": "SuperFloki", - "symbol": "SFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bunnymini-bunnymini", - "name": "BunnyMini", - "symbol": "BUNNYMINI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bjkt-baby-jkt", - "name": "BABY JKT", - "symbol": "BJKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axoni-axonicom-finance", - "name": "Axoni.com Finance", - "symbol": "AXONI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hubspot-hubspot", - "name": "HUBSPOT", - "symbol": "HUBSPOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msz-metasquidzilla", - "name": "MetaSquidZilla", - "symbol": "MSZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wlp-whalepension", - "name": "WhalePension", - "symbol": "WLP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbc-big-black-cat", - "name": "Big Black Cat", - "symbol": "BBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frd-farad", - "name": "Farad", - "symbol": "FRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokiswap-flokiswap", - "name": "FlokiSwap", - "symbol": "FLOKISWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sta-starta", - "name": "Starta", - "symbol": "STA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gb-gorillabeast", - "name": "GorillaBeast", - "symbol": "GB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pumpkin-pumpkinswap", - "name": "PumpkinSwap", - "symbol": "PUMPKIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mooshi-mooshi", - "name": "MOOSHI", - "symbol": "MOOSHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scam-dontbuy", - "name": "DontBuy", - "symbol": "SCAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "krk-kirkcoin", - "name": "KIRKCOIN", - "symbol": "KRK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elba-elonbaby", - "name": "ElonBaby", - "symbol": "ELBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "binu-babyinu", - "name": "BabyInu", - "symbol": "BINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wizzgirl-wizzardgirl", - "name": "WizzardGirl", - "symbol": "WIZZGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "durant-starvedofhistory", - "name": "StarvedOfHistory", - "symbol": "DURANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta3-meta3", - "name": "META3", - "symbol": "META3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aht-bowhead", - "name": "Bowhead", - "symbol": "AHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pylnt-pylon-network", - "name": "Pylon Network", - "symbol": "PYLNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "freemeta-freemeta", - "name": "FreeMeta", - "symbol": "FREEMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jinja-jinja", - "name": "Jinja", - "symbol": "JINJA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nttd-notimetodie", - "name": "NoTimeToDie", - "symbol": "NTTD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trvl-trvl", - "name": "TRVL", - "symbol": "TRVL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbx-shibax", - "name": "ShibaX", - "symbol": "SBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fbd-femboydoge", - "name": "FemboyDoge", - "symbol": "FBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wolf-wolf-finance", - "name": "Wolf Finance", - "symbol": "WOLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sandpad-sandboxgame", - "name": "Sandbox.game", - "symbol": "SANDPAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaa-meta-arena", - "name": "Meta Arena", - "symbol": "METAA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "p4c-parts-of-four-coin", - "name": "Parts of Four Coin", - "symbol": "P4C", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibc-shibaclassic", - "name": "ShibaClassic", - "symbol": "SHIBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tks-tokes", - "name": "Tokes", - "symbol": "TKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hol-holiday-token", - "name": "Holiday Token", - "symbol": "HOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vsl-vslice", - "name": "vSlice", - "symbol": "VSL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drp-dcorp", - "name": "DCORP", - "symbol": "DRP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ebtc-ebitcoin", - "name": "eBitcoin", - "symbol": "EBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cdx-network", - "name": "CDX Network", - "symbol": "CDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flag-for-loot-and-glory", - "name": "For Loot And Glory", - "symbol": "FLAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "emv-ethereum-movie-venture", - "name": "Ethereum Movie Venture", - "symbol": "EMV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chainlink-obama", - "name": "Obama", - "symbol": "CHAINLINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snoop-snoopdao", - "name": "SnoopDAO", - "symbol": "SNOOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gobble-gobble-token", - "name": "Gobble Token", - "symbol": "GOBBLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aura-aurora-dao", - "name": "Aurora DAO", - "symbol": "AURA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pdc-project-decorum", - "name": "Project Decorum", - "symbol": "PDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scl-sociall", - "name": "Sociall", - "symbol": "SCL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgpt-digipulse", - "name": "DigiPulse", - "symbol": "DGPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hbt-hubii-network", - "name": "Hubii Network", - "symbol": "HBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sss-sharechain", - "name": "Sharechain", - "symbol": "SSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "opt-opus", - "name": "Opus", - "symbol": "OPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sss-star-sharks", - "name": "Star Sharks", - "symbol": "SSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b2b-b2bx", - "name": "B2BX", - "symbol": "B2B", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bike-cycle-punk", - "name": "Cycle Punk", - "symbol": "BIKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crb-creditbit", - "name": "Creditbit", - "symbol": "CRB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pgl-prospectors-gold", - "name": "Prospectors Gold", - "symbol": "PGL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qwark-qwark", - "name": "Qwark", - "symbol": "QWARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pix-lampix", - "name": "Lampix", - "symbol": "PIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sno-snowball-snowbank", - "name": "Snowball-Snowbank", - "symbol": "SNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "astro-astro", - "name": "Astro", - "symbol": "ASTRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bon-bonpay", - "name": "Bonpay", - "symbol": "BON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pra-prochain", - "name": "ProChain", - "symbol": "PRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tie-tiesdb", - "name": "Ties.DB", - "symbol": "TIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uqc-uquid-coin", - "name": "Uquid Coin", - "symbol": "UQC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bfg-boyfucksgirl", - "name": "BoyFucksGirl", - "symbol": "BFG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atl-atlant", - "name": "ATLANT", - "symbol": "ATL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alt-altcoin", - "name": "Altcoin", - "symbol": "ALT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibadogezilla-shibadogezilla", - "name": "ShibaDogeZilla", - "symbol": "SHIBADOGEZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hatter-hatter", - "name": "Hatter", - "symbol": "HATTER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hkn-hacken", - "name": "Hacken", - "symbol": "HKN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "obits-obits", - "name": "OBITS", - "symbol": "OBITS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bet-daobet", - "name": "DAOBet", - "symbol": "BET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dice-etheroll", - "name": "Etheroll", - "symbol": "DICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cat-blockcat", - "name": "BlockCAT", - "symbol": "CAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pfr-payfair", - "name": "Payfair", - "symbol": "PFR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tantan-tantan-token", - "name": "TanTan Token", - "symbol": "TANTAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitusd-bitusd", - "name": "bitUSD", - "symbol": "BITUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tlwc-tlwc", - "name": "TLWC", - "symbol": "TLWC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gmt-mercury-protocol", - "name": "Mercury Protocol", - "symbol": "GMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cag-change", - "name": "Change", - "symbol": "CAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spf-sportyco", - "name": "SportyCo", - "symbol": "SPF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "poll-clearpoll", - "name": "ClearPoll", - "symbol": "POLL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rhc-rittenhousecoin", - "name": "RittenhouseCoin", - "symbol": "RHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcy-bitcrystals", - "name": "BitCrystals", - "symbol": "BCY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnbinu-bnbinu", - "name": "BNBinu", - "symbol": "BNBINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kaka-kaka-nft-worldr", - "name": "KAKA NFT Worldr", - "symbol": "KAKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "icos-icos", - "name": "ICOS", - "symbol": "ICOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fuckgankers-gankerssewerscammer", - "name": "GankersSewerScammer", - "symbol": "FUCKGANKER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chatucoin-chatukham-token", - "name": "CHATUKHAM Token", - "symbol": "CHATUCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccr-crazy-car-racing", - "name": "Crazy Car Racing", - "symbol": "CCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ysm-yooshiba-metaverse", - "name": "YooShiba Metaverse", - "symbol": "YSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "piratesquid-piratesquid", - "name": "Piratesquid", - "symbol": "PIRATESQUI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokidogenomics-flokidogenomics", - "name": "FlokiDogenomics", - "symbol": "FLOKIDOGEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snft-seenft", - "name": "SeeNFT", - "symbol": "SNFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "droo-dog-drool", - "name": "DOG DROOL", - "symbol": "DROO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "csno-bitdice", - "name": "BitDice", - "symbol": "CSNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fldc-foldingcoin", - "name": "FoldingCoin", - "symbol": "FLDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pudgy-pudgy-penguin", - "name": "PUDGY PENGUIN", - "symbol": "PUDGY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "maudit-meta-auditors", - "name": "Meta Auditors", - "symbol": "MAUDIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ripple-potter", - "name": "Potter", - "symbol": "RIPPLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trfu-trustfuture", - "name": "TrustFuture", - "symbol": "TRFU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sib-shinobi", - "name": "Shinobi", - "symbol": "SIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pibo-pizzabonkcom", - "name": "PizzaBonk.com", - "symbol": "PIBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metashib-meta-shib", - "name": "Meta Shib", - "symbol": "METASHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bmc-bitmachain", - "name": "BitMaChain", - "symbol": "BMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snowdoge-snowdoge", - "name": "SnowDoge", - "symbol": "SNOWDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibazillanomics-shibazillanomics", - "name": "ShibaZillanomics", - "symbol": "SHIBAZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hohoho-santameta", - "name": "SantaMeta", - "symbol": "HOHOHO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "superido-superido", - "name": "SuperIDO", - "symbol": "SUPERIDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trap-transcoin", - "name": "TransCoin", - "symbol": "TRAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metacoix-metacoix", - "name": "MetaCoix", - "symbol": "METACOIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "npb-plant-base", - "name": "Plant base", - "symbol": "NPB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cafc-architecture-finance-token", - "name": "Architecture Finance TOKEN", - "symbol": "CAFC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sepp-wolfsheppard", - "name": "Wolfsheppard", - "symbol": "SEPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metashiba-meta-shiba-rocket", - "name": "Meta Shiba Rocket", - "symbol": "METASHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nansen-nansenai", - "name": "nansen.ai", - "symbol": "NANSEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mdoge-metadoge", - "name": "MetaDoge", - "symbol": "MDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethereum-harry", - "name": "Harry", - "symbol": "ETHEREUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metacat-metaverse-cate", - "name": "METAVERSE CATE", - "symbol": "METACAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "idoge-iron-doge", - "name": "IRON DOGE", - "symbol": "IDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eth-shibadogebitcoin-inu", - "name": "ShibaDogeBitcoin inu", - "symbol": "ETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chk007-chickmagnet007", - "name": "ChickMagnet007", - "symbol": "CHK007", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "igg-intergalactic-gold", - "name": "Intergalactic Gold", - "symbol": "IGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lqd-liquidity", - "name": "Liquidity", - "symbol": "LQD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wnk-winkies", - "name": "Winkies", - "symbol": "WNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test-testing", - "name": "Testing", - "symbol": "TEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kitsune-kitsuneinu", - "name": "KitsuneInu", - "symbol": "KITSUNE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbsd-minibabyshibadoge", - "name": "MiniBabyShibaDoge", - "symbol": "MBSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smg-squidmoongirl", - "name": "SquidMoonGirl", - "symbol": "SMG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ppcum-why-does-ur-pp-look-like-u-just-came", - "name": "Why does ur pp look like u just came?", - "symbol": "PPCUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hsc-hs-coin", - "name": "HS Coin", - "symbol": "HSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qbf-qbroker-finance", - "name": "QBroker Finance", - "symbol": "QBF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hgl-hot-galaxy", - "name": "Hot Galaxy", - "symbol": "HGL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "liv-livemocha-token", - "name": "Livemocha Token", - "symbol": "LIV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squiddickrocket-squiddickrocket", - "name": "SquidDickRocket", - "symbol": "SQUIDDICKR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rugged-genieswap", - "name": "GenieSwap", - "symbol": "RUGGED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "buffbit-buffbitcoin", - "name": "BUFFBITCOIN", - "symbol": "BUFFBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dydx-dydx-bsc", - "name": "DYDX (BSC)", - "symbol": "DYDX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usdt-usdt", - "name": "USDT", - "symbol": "USDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bswarrior-buffedsantawarrior", - "name": "BuffedSantaWarrior", - "symbol": "BSWARRIOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethhasher-ethereumhasher", - "name": "EthereumHasher", - "symbol": "ETHHASHER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibamoney-baby-shiba-trillionaire", - "name": "Baby Shiba Trillionaire", - "symbol": "SHIBAMONEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "soar-soarcoin", - "name": "Soarcoin", - "symbol": "SOAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pbl-publica", - "name": "Publica", - "symbol": "PBL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qau-quantum", - "name": "Quantum", - "symbol": "QAU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bq-bitqy", - "name": "bitqy", - "symbol": "BQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trojan-trojan-protocol", - "name": "Trojan Protocol", - "symbol": "TROJAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "powerinu-powerinu", - "name": "PowerInu", - "symbol": "POWERINU", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lct-lendconnect", - "name": "LendConnect", - "symbol": "LCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cred-verify", - "name": "Verify", - "symbol": "CRED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "guru-nidhi-dao", - "name": "Nidhi Dao", - "symbol": "GURU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "incnt-incent", - "name": "Incent", - "symbol": "INCNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elix-elixir", - "name": "Elixir", - "symbol": "ELIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dtb-databits", - "name": "Databits", - "symbol": "DTB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msc-multi-stake-capital", - "name": "Multi-Stake Capital", - "symbol": "MSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aix-aigang", - "name": "Aigang", - "symbol": "AIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "myb-mybit", - "name": "MyBit", - "symbol": "MYB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "otn-open-trading-network", - "name": "Open Trading Network", - "symbol": "OTN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "qun-qunqun", - "name": "QunQun", - "symbol": "QUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mpt-mexican-pizza", - "name": "Mexican Pizza", - "symbol": "MPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hdg-hedge", - "name": "Hedge", - "symbol": "HDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prg-paragon", - "name": "Paragon", - "symbol": "PRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tgt-target-coin", - "name": "Target Coin", - "symbol": "TGT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lobi-lobis", - "name": "Lobis", - "symbol": "LOBI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dsc-doggystyle-coin", - "name": "DoggyStyle Coin", - "symbol": "DSC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "life-life", - "name": "LIFE", - "symbol": "LIFE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nxc-nexium", - "name": "Nexium", - "symbol": "NXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alis-alis", - "name": "ALIS", - "symbol": "ALIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stx-stox", - "name": "Stox", - "symbol": "STX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rvt-rivetz", - "name": "Rivetz", - "symbol": "RVT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xnn-xenon", - "name": "Xenon", - "symbol": "XNN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arn-aeron", - "name": "Aeron", - "symbol": "ARN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dov-dovu", - "name": "Dovu", - "symbol": "DOV", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dogeg-doge-game", - "name": "Doge Game", - "symbol": "DOGEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bot-bodhi", - "name": "Bodhi", - "symbol": "BOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "credo-credo", - "name": "Credo", - "symbol": "CREDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bcap-bcap", - "name": "BCAP", - "symbol": "BCAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "air-airtoken", - "name": "AirToken", - "symbol": "AIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "divx-divi", - "name": "Divi Exchange Token", - "symbol": "DIVX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orme-ormeus-coin", - "name": "Ormeus Coin", - "symbol": "ORMEUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tix-blocktix", - "name": "Blocktix", - "symbol": "TIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "net-nimiq-exchange-token", - "name": "Nimiq Exchange Token", - "symbol": "NET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safemoonfork-safemoonfork", - "name": "SafeMoonfork", - "symbol": "SAFEMOONFO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nvst-nvo", - "name": "NVO", - "symbol": "NVST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gup-matchpool", - "name": "Matchpool", - "symbol": "GUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pepecash-pepe-cash", - "name": "Pepe Cash", - "symbol": "PEPECASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snov-snovio", - "name": "Snovian.Space", - "symbol": "SNOV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dna-encrypgen", - "name": "EncrypGen", - "symbol": "DNA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "polyfin-polyalpha-finance", - "name": "PolyAlpha Finance", - "symbol": "POLYFIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "supsol-supersolana", - "name": "SuperSolana", - "symbol": "SUPSOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "titano2-titano2", - "name": "Titano2", - "symbol": "TITANO2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mpt-mexicanpizza", - "name": "MexicanPizza", - "symbol": "MPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blt-bloom", - "name": "Bloom", - "symbol": "BLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ccd-chaincade", - "name": "ChainCade", - "symbol": "CCD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "santabonk-santabonk", - "name": "SantaBonk", - "symbol": "SANTABONK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidisland-squid-island", - "name": "Squid Island", - "symbol": "SQUIDISLAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swiss-swissbank", - "name": "SwissBank", - "symbol": "SWISS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eib-ein-bop", - "name": "EIN BOP", - "symbol": "EIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdogeff-babydogeflokifomo", - "name": "BabyDogeFlokiFomo", - "symbol": "BDOGEFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tm-trillionairemindet", - "name": "TrillionaireMind$et", - "symbol": "TM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pocn-popcat-coin", - "name": "PopCat Coin", - "symbol": "POCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gilimon-gilimon", - "name": "Gilimon", - "symbol": "GILIMON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stc-shitcoin", - "name": "Shitcoin", - "symbol": "STC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "st-slicktoken", - "name": "SlickToken", - "symbol": "ST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "leap-leap-token", - "name": "Leap Token", - "symbol": "LEAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shib-shiba", - "name": "Shiba", - "symbol": "SHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "starshipsquid-starshipsquid", - "name": "StarshipSquid", - "symbol": "STARSHIPSQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kanye-west-my-girlfriend-and-i-broke-up-because-of-kanye39s-meeting-with-trumpwe-got-in-a-huge-fight", - "name": "My girlfriend and I broke up because of Kanye's meeting with Trump.We got in a huge fight over it be", - "symbol": "KANYE WEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lxu-luxr", - "name": "Luxr", - "symbol": "LXU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tube-tube-protocol", - "name": "Tube Protocol", - "symbol": "TUBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtm-metanomics", - "name": "Metanomics", - "symbol": "MTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mst-mini-sushi-token", - "name": "MINI SUSHI TOKEN", - "symbol": "MST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pear-pearswap", - "name": "PearSwap", - "symbol": "PEAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "str-stars", - "name": "Stars", - "symbol": "STR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "y-yea", - "name": "yea", - "symbol": "Y", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcmj-btcmj", - "name": "BTCMJ", - "symbol": "BTCMJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "upcake-upcake", - "name": "UPCAKE", - "symbol": "UPCAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibpond-shibapond", - "name": "ShibaPOND", - "symbol": "SHIBPOND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mdg-medping", - "name": "MedPing", - "symbol": "MDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "melon-melonswap", - "name": "MelonSwap", - "symbol": "MELON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gsz-greenshibazilla", - "name": "GreenShibaZilla", - "symbol": "GSZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "santadefi-santadefi", - "name": "SantaDefi", - "symbol": "SANTADEFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tre-troneclipse", - "name": "TRONECLIPSE", - "symbol": "TRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtnt-mytracknet", - "name": "Mytracknet", - "symbol": "MTNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "x100-smart-contract", - "name": "Smart Contract", - "symbol": "X100", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibarium-shibarium", - "name": "SHIBARIUM", - "symbol": "SHIBARIUM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arc-arcadenetio", - "name": "arcadenet.io", - "symbol": "ARC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "polfin-polka-finance", - "name": "Polka Finance", - "symbol": "POLFIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sexyeth-sexyeth", - "name": "SexyETH", - "symbol": "SEXYETH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test33-test33", - "name": "test33", - "symbol": "TEST33", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibainuboy-shibainuboy", - "name": "ShibaInuBoy", - "symbol": "SHIBAINUBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lvm-livemocha-token", - "name": "Livemocha Token", - "symbol": "LVM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogeff-dogeflokifomo", - "name": "DogeFlokiFomo", - "symbol": "DOGEFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blzc-blzc-coin", - "name": "BLZC Coin", - "symbol": "BLZC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiba-beer-shiba-beer", - "name": "Shiba Beer", - "symbol": "SHIBA BEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shark-babyshark", - "name": "BabyShark", - "symbol": "SHARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ffshiba-foreverfomoshiba", - "name": "ForeverFomoShiba", - "symbol": "FFSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "citigrow-citigrow-coin", - "name": "CitiGrow Coin", - "symbol": "CITIGROW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokialien-floki-alien", - "name": "Floki Alien", - "symbol": "FLOKIALIEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bunfloki-bunshinfloki", - "name": "BunshinFloki", - "symbol": "BUNFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibels-shibaelves", - "name": "ShibaElves", - "symbol": "SHIBELS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethhasher-ethereum-hasher", - "name": "Ethereum Hasher", - "symbol": "ETHHASHER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ace-d-nft-ace-defender", - "name": "NFT ACE Defender", - "symbol": "ACE-D", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bpipe-bigpipe", - "name": "Big.Pipe", - "symbol": "B.PIPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibsol-shibasolana", - "name": "ShibaSolana", - "symbol": "SHIBSOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meta-squid-meta-squid", - "name": "Meta Squid", - "symbol": "META SQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "woj-wojak", - "name": "Wojak", - "symbol": "WOJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bone-moonbone", - "name": "MoonBone", - "symbol": "BONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "meme-memehub", - "name": "MemeHub", - "symbol": "MEME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ffflok-foreverfomofloki", - "name": "ForeverFomoFloki", - "symbol": "FFFLOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokiavax-flokiavax", - "name": "FlokiAVAX", - "symbol": "FLOKIAVAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boobsinu-boobsinu", - "name": "BoobsInu", - "symbol": "BOOBSINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msb-meta-squid-bnb", - "name": "Meta Squid Bnb", - "symbol": "$MSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdt-baby-doge-token", - "name": "Baby Doge Token", - "symbol": "BDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "m-shibarocket-moonshibarocket", - "name": "MoonShibaRocket", - "symbol": "M-SHIBAROC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test7io-test7", - "name": "TEST7", - "symbol": "TEST7.IO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "merveswap-merveswap", - "name": "MERVESWAP", - "symbol": "MERVESWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "msb-metasquidbnb", - "name": "METASQUIDBNB", - "symbol": "$MSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chacha-channelchanger", - "name": "Channelchanger", - "symbol": "CHACHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcmj-bitcoinmj", - "name": "BitcoinMJ", - "symbol": "BTCMJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rug-rugdog", - "name": "RugDog", - "symbol": "RUG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pxs-pixies", - "name": "Pixies", - "symbol": "PXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mf-meta-foreseen", - "name": "Meta Foreseen", - "symbol": "MF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibabeer-shibabeer", - "name": "ShibaBeer", - "symbol": "SHIBABEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wtw-watchtower", - "name": "Watchtower", - "symbol": "WTW", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "msp-mothership", - "name": "Mothership", - "symbol": "MSP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "payx-paypex", - "name": "Paypex", - "symbol": "PAYX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "asgemini-test", - "name": "Test", - "symbol": "ASGEMINI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "voise-voise", - "name": "Voise", - "symbol": "VOISE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tio-trade-token", - "name": "Trade Token", - "symbol": "TIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safex-safe-exchange-coin", - "name": "Safex Exchange Coin", - "symbol": "SAFEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jinn-jinn", - "name": "Jinn", - "symbol": "JINN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lazy-lazy-lions", - "name": "LAZY LIONS", - "symbol": "LAZY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mshib-mini-shib", - "name": "mini SHIB", - "symbol": "MSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metamon-metamon", - "name": "Metamon", - "symbol": "METAMON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uct-unicake", - "name": "UniCAKE", - "symbol": "UCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swan-swanswap", - "name": "SwanSwap", - "symbol": "SWAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wrc-worldcore", - "name": "Worldcore", - "symbol": "WRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokistk-flokistake", - "name": "FlokiStake", - "symbol": "FLOKISTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tkn-monolith", - "name": "Monolith", - "symbol": "TKN", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "jizz-jizztubeio", - "name": "JIZZTUBE.IO", - "symbol": "JIZZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "allzilla-allzilla", - "name": "ALLZILLA", - "symbol": "ALLZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dat-datum", - "name": "Datum", - "symbol": "DAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adt-adtoken", - "name": "adToken", - "symbol": "ADT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "whirl-omniwhirl", - "name": "OmniWhirl", - "symbol": "WHIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mythicdg-mythicdogegirl", - "name": "MythicDogeGirl", - "symbol": "MYTHICDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "067-067", - "name": "067", - "symbol": "067", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibaforever-greenshibaforever", - "name": "GreenShibaForever", - "symbol": "SHIBAFOREV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "taas-taas", - "name": "TaaS", - "symbol": "TAAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cos-coss", - "name": "COSS", - "symbol": "COS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cfi-cofoundit", - "name": "Cofound.it", - "symbol": "CFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cuminu-cummiesinu", - "name": "CummiesInu", - "symbol": "CUMINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cat-bitclave", - "name": "BitClave", - "symbol": "CAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "caesarnomics-caesarnomics", - "name": "CaesarNomics", - "symbol": "CAESARNOMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grid-grid", - "name": "Grid+", - "symbol": "GRID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gbot-gundabots", - "name": "GundaBots", - "symbol": "GBOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "marvin-elon39s-marvin", - "name": "Elon's Marvin", - "symbol": "MARVIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1st-firstblood", - "name": "FirstBlood", - "symbol": "1ST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "agrs-agoras-tokens", - "name": "Agoras Tokens", - "symbol": "AGRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hst-decision-token", - "name": "Decision Token", - "symbol": "HST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lw-lordwar", - "name": "lordWAR", - "symbol": "LW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "caesar-caesars-rocket", - "name": "CAESARS ROCKET", - "symbol": "CAESAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metawin-metawin", - "name": "MetaWin", - "symbol": "METAWIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidsanta-squidsanta", - "name": "SquidSanta", - "symbol": "SQUIDSANTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scar-scar-token", - "name": "SCAR Token", - "symbol": "SCAR", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "kong-kingkongkoin", - "name": "KINGKONGKOIN", - "symbol": "KONG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "supfloki-superfloki", - "name": "SuperFloki", - "symbol": "SUPFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "magician-magician-shiba", - "name": "Magician Shiba", - "symbol": "MAGICIAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nude-nudecoin", - "name": "Nudecoin", - "symbol": "NUDE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "man-manutd", - "name": "ManUTD", - "symbol": "MAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbx-bitblox", - "name": "BitBlox", - "symbol": "BBX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibstar-shib-starbase", - "name": "SHIB Starbase", - "symbol": "SHIBSTAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pupal-puppypal", - "name": "PuppyPal", - "symbol": "PUPAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spark-spark-finance", - "name": "Spark Finance", - "symbol": "SPARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rvc-revenuecoinio", - "name": "REVENUECOIN.io", - "symbol": "RVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibaruler-shibaruler", - "name": "SHIBARULER", - "symbol": "SHIBARULER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "greenfloishiba-greenflokishiba", - "name": "GreenFlokiShiba", - "symbol": "GREENFLOIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbullbo-babybullybonk", - "name": "BabyBullyBonk", - "symbol": "BBULLBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fatboycat-fatboycat", - "name": "FatBoyCat", - "symbol": "FATBOYCAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sspace-starship-space", - "name": "STARSHIP SPACE", - "symbol": "SSPACE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test12-test12", - "name": "test12", - "symbol": "TEST12", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "upmeta-upmeta", - "name": "UPMETA", - "symbol": "UPMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pow-defi-power", - "name": "DEFI POWER", - "symbol": "POW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nmx-narcos-mexico", - "name": "NARCOS MEXICO", - "symbol": "NMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "burncake-burncake", - "name": "BurnCake", - "symbol": "BURNCAKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tsunami-tsunamifinance", - "name": "Tsunami.Finance", - "symbol": "TSUNAMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fff-foreverfloki-finance", - "name": "ForeverFloki Finance", - "symbol": "FFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibareward-shibareward", - "name": "SHIBAREWARD", - "symbol": "SHIBAREWAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pheswap-pheonix-swap", - "name": "PHEONIX SWAP", - "symbol": "PHESWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inu-boostinu", - "name": "BoostInu", - "symbol": "INU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dx5-m-dx5-m", - "name": "DX5-M", - "symbol": "DX5-M", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nre-nft-real-estate", - "name": "NFT Real Estate", - "symbol": "NRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibj-shiba-junior", - "name": "SHIBA JUNIOR", - "symbol": "SHIBJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbc-bnbgrow-coin", - "name": "BNBGrow Coin", - "symbol": "BBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "panf-panda-finance", - "name": "Panda Finance", - "symbol": "PANF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "growzilla-growzilla", - "name": "GrowZilla", - "symbol": "GROWZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "enpai-senpaifinancecom", - "name": "SENPAIfinance.com", - "symbol": "ENPAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "giva-givanza", - "name": "Givanza", - "symbol": "GIVA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rise-riseonlineworldcom", - "name": "riseonlineworld.com", - "symbol": "RISE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sgod-shiba-god", - "name": "Shiba God", - "symbol": "SGOD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "teddy-teddybear", - "name": "TeddyBear", - "symbol": "TEDDY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mms-meta-moon-shiba", - "name": "Meta Moon Shiba", - "symbol": "MMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ecf-exchange-chain-force", - "name": "Exchange Chain Force", - "symbol": "ECF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metacycle-metacycle", - "name": "MetaCycle", - "symbol": "METACYCLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cumdoge-cumdoge", - "name": "CumDoge", - "symbol": "CUMDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kng-kanga-exchange-token", - "name": "Kanga Exchange Token", - "symbol": "KNG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sit-sheen-inu-token", - "name": "Sheen Inu Token", - "symbol": "SIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smn-smartmoon", - "name": "smartmoon", - "symbol": "SMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "waf-waifu", - "name": "Waifu", - "symbol": "WAF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chacha-channel-changer", - "name": "Channel Changer", - "symbol": "CHACHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shangchi-shang-chi", - "name": "Shang Chi", - "symbol": "SHANGCHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hooli-hooligans-fan-token", - "name": "HOOLIGANS FAN TOKEN", - "symbol": "$HOOLI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spunks-superpunks", - "name": "SuperPunks", - "symbol": "SPUNKS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nassb-national-academies-space-studies-board", - "name": "National Academies Space Studies Board", - "symbol": "NASSB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hy-husky", - "name": "Husky", - "symbol": "HY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kkr-kakarot-coin", - "name": "KAKAROT COIN", - "symbol": "KKR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbc-bull-bit-coin", - "name": "Bull Bit Coin", - "symbol": "BBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbcoin-baby-bomber-coin", - "name": "Baby Bomber Coin", - "symbol": "BBCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zeus-zeus", - "name": "ZEUS", - "symbol": "ZEUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tigr-tigerkang", - "name": "TigerKang", - "symbol": "TIGR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "prl-oyster", - "name": "Oyster", - "symbol": "PRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trig-triggers", - "name": "Triggers", - "symbol": "TRIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babygreenshiba-babygreenshiba", - "name": "BabyGreenShiba", - "symbol": "BABYGREENS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ukg-unikoin-gold", - "name": "Unikoin Gold", - "symbol": "UKG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "canna-the-cancoin", - "name": "The CanCoin", - "symbol": "CANNA", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ctr-centra", - "name": "Centra", - "symbol": "CTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mod-modum", - "name": "Modum", - "symbol": "MOD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atm-atmchain", - "name": "ATMChain", - "symbol": "ATM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spank-spankchain", - "name": "SpankChain", - "symbol": "SPANK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sngls-singulardtv", - "name": "SingularDTV", - "symbol": "SNGLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ss-starship", - "name": "Starship", - "symbol": "SS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unity-supernet", - "name": "SuperNET", - "symbol": "UNITY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mco-mco", - "name": "MCO", - "symbol": "MCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dtr-dynamic-trading-rights", - "name": "Dynamic Trading Rights", - "symbol": "DTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stormx-stormx", - "name": "StormX", - "symbol": "STORMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ppp-paypie", - "name": "PayPie", - "symbol": "PPP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xpa-xpa", - "name": "XPA", - "symbol": "XPA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dew-dew", - "name": "DEW", - "symbol": "DEW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nasem-nationalacademies", - "name": "NationalAcademies", - "symbol": "NASEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "plr-pillar", - "name": "Pillar", - "symbol": "PLR", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cob-cobinhood", - "name": "Cobinhood", - "symbol": "COB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "arcane-arcane", - "name": "Arcane", - "symbol": "ARCANE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "icn-iconomi", - "name": "Iconomi", - "symbol": "ICN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ent-entcash", - "name": "ENTCash", - "symbol": "ENT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "frct-farstcoin", - "name": "Farstcoin", - "symbol": "FRCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cryptpnksol-cryptopunks-solana", - "name": "CryptoPunks Solana", - "symbol": "CRYPTPNKSO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bt2-bt2-cst", - "name": "BT2 [CST]", - "symbol": "BT2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bt1-bt1-cst", - "name": "BT1 [CST]", - "symbol": "BT1", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "9coin-9coin", - "name": "9COIN", - "symbol": "9COIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kz-kill-zill", - "name": "KiLL ZiLL", - "symbol": "KZ", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "aif-ai-finance", - "name": "AI Finance", - "symbol": "AIF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "near-near", - "name": "Near", - "symbol": "NEAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bunnydefi-bunnydefi", - "name": "BunnyDefi", - "symbol": "BUNNYDEFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tokyo-la-casa-de-papel-token", - "name": "LA CASA DE PAPEL TOKEN", - "symbol": "TOKYO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pfizer-pfizer-inc-pfizercom", - "name": "Pfizer Inc. (pfizer.com)", - "symbol": "PFIZER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ait-artificialintelligence", - "name": "ArtificialIntelligence", - "symbol": "AIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bbm-babymoon", - "name": "BabyMoon", - "symbol": "BBM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metav-metavpadcom", - "name": "MetaVPad.com", - "symbol": "METAV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inugames-redinu-vs-blueinu", - "name": "RedInu vs BlueInu", - "symbol": "INUGAMES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "palmzilla-palmzilla", - "name": "PALMZILLA", - "symbol": "PALMZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ican-canaryislands", - "name": "CANARY_ISLANDS", - "symbol": "ICAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ramens-ramen", - "name": "Ramen", - "symbol": "RAMENS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kuala-babykuala", - "name": "BabyKuala", - "symbol": "KUALA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wc-wincoin", - "name": "WINCOIN", - "symbol": "WC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cmp-compcoin", - "name": "Compcoin", - "symbol": "CMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "men-peoplecoin", - "name": "PeopleCoin", - "symbol": "MEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mbl-mobilecash", - "name": "MobileCash", - "symbol": "MBL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mmxvi-mmxvi", - "name": "MMXVI", - "symbol": "MMXVI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "futc-futcoin", - "name": "FutCoin", - "symbol": "FUTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "top-topcoin", - "name": "TopCoin", - "symbol": "TOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "maxi-maximizer", - "name": "Maximizer", - "symbol": "MAXI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "disk-darklisk", - "name": "DarkLisk", - "symbol": "DISK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "egg-eggcoin", - "name": "EggCoin", - "symbol": "EGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "uta-utacoin", - "name": "UtaCoin", - "symbol": "UTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lth-lathaan", - "name": "LAthaan", - "symbol": "LTH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "axiom-axiom", - "name": "Axiom", - "symbol": "AXIOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cbd-cbd-crystals", - "name": "CBD Crystals", - "symbol": "CBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wa-wa-space", - "name": "WA Space", - "symbol": "WA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kashh-kashhcoin", - "name": "KashhCoin", - "symbol": "KASHH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "topaz-topaz-coin", - "name": "Topaz Coin", - "symbol": "TOPAZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shell-shellcoin", - "name": "ShellCoin", - "symbol": "SHELL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "psy-psilocybin", - "name": "Psilocybin", - "symbol": "PSY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wool-wool-token", - "name": "Wool Token", - "symbol": "WOOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xid-international-diamond", - "name": "International Diamond", - "symbol": "XID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "x2-x2", - "name": "X2", - "symbol": "X2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mshib-moonshiba", - "name": "MoonShiba", - "symbol": "MSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "greenshiba-greenshiba", - "name": "GreenShiba", - "symbol": "GREENSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtprt-metapirate", - "name": "MetaPirate", - "symbol": "MTPRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hotdoge-hotdoge", - "name": "HotDoge", - "symbol": "HOTDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "happy-happy-floki", - "name": "Happy Floki", - "symbol": "HAPPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibahunter-shiba-hunter", - "name": "Shiba Hunter", - "symbol": "SHIBAHUNTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokipeng-flokipenguins", - "name": "FlokiPenguins", - "symbol": "FLOKIPENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ivz-invisiblecoin", - "name": "InvisibleCoin", - "symbol": "IVZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "asa-astro-santa", - "name": "Astro Santa", - "symbol": "ASA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boyshiba-boyshiba", - "name": "BOYSHIBA", - "symbol": "BOYSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wolf-wolf-protocol", - "name": "Wolf Protocol", - "symbol": "WOLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mingo-mingo-token", - "name": "Mingo Token", - "symbol": "MINGO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "krakengirl-krakengirl", - "name": "KrakenGirl", - "symbol": "KRAKENGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wolf-wolfdefi", - "name": "WolfDeFi", - "symbol": "WOLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "main-mainlink", - "name": "Mainlink", - "symbol": "MAIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shd-superherodoge", - "name": "Super_Hero_Doge", - "symbol": "SHD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mesothelioma-don39t", - "name": "Don't", - "symbol": "MESOTHELIO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mcv-metacryptoverse", - "name": "MetaCryptoVerse", - "symbol": "MCV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rflc-reflections-coin", - "name": "Reflections Coin", - "symbol": "RFLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kurot-kurotest", - "name": "KuroTest", - "symbol": "KUROT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "phx-phalconx", - "name": "PhalconX", - "symbol": "PHX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fsc-fucksantaclaus", - "name": "FuckSantaClaus", - "symbol": "FSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "green-greenshiba", - "name": "GreenShiba", - "symbol": "GREEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gsn-guardian-soul-nft", - "name": "Guardian Soul NFT", - "symbol": "GSN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogemetaverse-dogemetaverse", - "name": "DogeMetaverse", - "symbol": "DOGEMETAVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nautilus-spacenautilus", - "name": "SpaceNautilus", - "symbol": "NAUTILUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "captainshib-captain-shiba", - "name": "Captain Shiba", - "symbol": "CAPTAINSHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "o-oxygen-token", - "name": "Oxygen Token", - "symbol": "O", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaig-metagaming", - "name": "metagaming", - "symbol": "METAIG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apple-pie-slice-of-my-pie", - "name": "SLICE OF MY PIE", - "symbol": "APPLE PIE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "freejoe-tigerking", - "name": "TigerKing", - "symbol": "FREEJOE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokiinu-flokiinu", - "name": "FLOKIInu", - "symbol": "FLOKIINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "supape-superape", - "name": "SuperApe", - "symbol": "SUPAPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "awz-amawzomeinu", - "name": "AmawzomeInu", - "symbol": "AWZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metasnow-metasnow", - "name": "MetaSnow", - "symbol": "METASNOW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pure-pureblood", - "name": "Pureblood", - "symbol": "PURE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zukimoba-zukimoba", - "name": "ZUKIMOBA", - "symbol": "ZUKIMOBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babywkd-baby-wakanda-inu", - "name": "BaBy Wakanda inu", - "symbol": "BABYWKD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eter-minieternal", - "name": "MiniEternal", - "symbol": "ETER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minigreen-minigreenshiba", - "name": "MiniGreenShiba", - "symbol": "MINIGREEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "starl-starlink-metaverse", - "name": "Starlink Metaverse", - "symbol": "STARL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ons-onstar", - "name": "Onstar", - "symbol": "ONS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metadot-metadot", - "name": "MetaDot", - "symbol": "METADOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alx-alienx", - "name": "AlienX", - "symbol": "ALX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tb-teslaboy", - "name": "TeslaBoy", - "symbol": "TB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "deer-deer-inu", - "name": "DEER INU", - "symbol": "DEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "psi-poodle-swap-inu", - "name": "Poodle Swap Inu", - "symbol": "PSI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "santashiba-santashiba", - "name": "SantaShiba", - "symbol": "SANTASHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chaos-chaoscollect", - "name": "ChaosCollect", - "symbol": "CHAOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyflobo-babyflokibonk", - "name": "BabyFlokiBonk", - "symbol": "BABYFLOBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibs-shibasushi", - "name": "ShibaSushi", - "symbol": "SHIBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chobit-chobits-token", - "name": "Chobits Token", - "symbol": "CHOBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibas-shibas", - "name": "Shibas", - "symbol": "SHIBAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hmoon-happymooner", - "name": "HappyMooner", - "symbol": "HMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gensinverse-gensinvers", - "name": "GensinVers", - "symbol": "GENSINVERS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shroom-mushroom", - "name": "Mushroom", - "symbol": "SHROOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yes-yescoin", - "name": "Yescoin", - "symbol": "YES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cyc-cycling-coin", - "name": "Cycling Coin", - "symbol": "CYC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bac-bitalphacoin", - "name": "BitAlphaCoin", - "symbol": "BAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shibonk-shibabonk", - "name": "ShibaBonk", - "symbol": "SHIBONK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "team-teamup", - "name": "TeamUp", - "symbol": "TEAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "op-operand", - "name": "Operand", - "symbol": "OP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "trick-trickycoin", - "name": "TrickyCoin", - "symbol": "TRICK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mavro-mavro", - "name": "Mavro", - "symbol": "MAVRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fwg-fantasy-world-gold", - "name": "Fantasy World Gold", - "symbol": "FWG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "av-avatarcoin", - "name": "AvatarCoin", - "symbol": "AV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bogelon-bogged-elon", - "name": "Bogged Elon", - "symbol": "BOGELON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "poke-pokecoin", - "name": "PokeCoin", - "symbol": "POKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rhfc-rhfcoin", - "name": "RHFCoin", - "symbol": "RHFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ffc-fireflycoin", - "name": "FireFlyCoin", - "symbol": "FFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "royal-royalcoin", - "name": "RoyalCoin", - "symbol": "ROYAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "asn-aseancoin", - "name": "Aseancoin", - "symbol": "ASN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rupx-rupaya-old", - "name": "Rupaya [OLD]", - "symbol": "RUPX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "voya-voyacoin", - "name": "Voyacoin", - "symbol": "VOYA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qora-qora", - "name": "Qora", - "symbol": "QORA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bit-first-bitcoin", - "name": "First Bitcoin", - "symbol": "BIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nnk-ninjakimmy", - "name": "NinjaKimmy", - "symbol": "NNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xauc-xaucoin", - "name": "Xaucoin", - "symbol": "XAUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tera-teracoin", - "name": "TeraCoin", - "symbol": "TERA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fid-bitfid", - "name": "BITFID", - "symbol": "FID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tle-tattoocoin-limited-edition", - "name": "Tattoocoin (Limited Edition)", - "symbol": "TLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "laz-lazaruscoin", - "name": "Lazaruscoin", - "symbol": "LAZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "birds-birds", - "name": "Birds", - "symbol": "BIRDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pdg-pinkdog", - "name": "PinkDog", - "symbol": "PDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "runners-runners", - "name": "Runners", - "symbol": "RUNNERS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "unc-uncoin", - "name": "UNCoin", - "symbol": "UNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "payp-paypeer", - "name": "PayPeer", - "symbol": "PAYP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "richx-richcoin", - "name": "RichCoin", - "symbol": "RICHX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bub-bubble", - "name": "Bubble", - "symbol": "BUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "money-moneycoin", - "name": "MoneyCoin", - "symbol": "MONEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xqn-quotient", - "name": "Quotient", - "symbol": "XQN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hpt-halfbtc", - "name": "halfbtc", - "symbol": "HPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "acn-avoncoin", - "name": "Avoncoin", - "symbol": "ACN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blazr-blazercoin", - "name": "BlazerCoin", - "symbol": "BLAZR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bsn-bastonet", - "name": "Bastonet", - "symbol": "BSN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "skull-pirate-blocks", - "name": "Pirate Blocks", - "symbol": "SKULL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bcake-burnt-cake", - "name": "Burnt Cake", - "symbol": "BCAKE", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "leek-leek-coin", - "name": "Leek Coin", - "symbol": "LEEK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "karma-karmacoin", - "name": "Karmacoin", - "symbol": "KARMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "adaref-cardano-cross-reflections", - "name": "Cardano Cross Reflections", - "symbol": "ADAREF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babymetadoge-babymetadoge", - "name": "BabyMetaDoge", - "symbol": "BABYMETADO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "daddybusd-daddybusd", - "name": "DaddyBUSD", - "symbol": "DADDYBUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nudegirls-nudegirls", - "name": "NudeGirls", - "symbol": "NUDEGIRLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chupa-el-chupacabra-coin", - "name": "El Chupacabra Coin", - "symbol": "CHUPA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "myra-mmytheria", - "name": "MMytheria", - "symbol": "MYRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyfceek-babyflokiceek", - "name": "BabyFlokiCeek", - "symbol": "BABYFCEEK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shim-shiba-moon", - "name": "SHIBA Moon", - "symbol": "SHIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nook-nookfinance", - "name": "NookFinance", - "symbol": "NOOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "njk-ninjakimmy", - "name": "NinjaKimmy", - "symbol": "NJK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bkc-babykucoin", - "name": "BABYKUCOIN", - "symbol": "BKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usa-patriot-pup", - "name": "Patriot Pup", - "symbol": "USA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zillazeos-zillazeos", - "name": "ZillaZEOS", - "symbol": "ZILLAZEOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyflok-babyelonfloki", - "name": "BabyElonFloki", - "symbol": "BABYFLOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pitbubo-pitbullybonk", - "name": "PitBullyBonk", - "symbol": "PITBUBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tic-tic", - "name": "TIC", - "symbol": "TIC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "hallo-halloween-coin", - "name": "Halloween Coin", - "symbol": "HALLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "msu-metasoccer", - "name": "MetaSoccer", - "symbol": "MSU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "baka-baka-inu", - "name": "Baka Inu", - "symbol": "BAKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "senpai-senpaifinancecom", - "name": "SENPAIfinance.com", - "symbol": "SENPAI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metamen-metamen", - "name": "MetaMen", - "symbol": "METAMEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "t8-test", - "name": "test", - "symbol": "T8", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gala-swap-gala-swap", - "name": "GALA SWAP", - "symbol": "GALA SWAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ant-antfarm", - "name": "AntFarm", - "symbol": "ANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atl-atlantis-loan", - "name": "Atlantis Loan", - "symbol": "ATL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "instantxrp-instantxrp", - "name": "INSTANTXRP", - "symbol": "INSTANTXRP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babydogev-babydogeviking", - "name": "BabyDogeViking", - "symbol": "BABYDOGEV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "podl-poodle-inu", - "name": "Poodle Inu", - "symbol": "PODL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lbr-altest", - "name": "ALTEST", - "symbol": "LBR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "emr-elonmoonrocket", - "name": "ElonMoonRocket", - "symbol": "EMR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nxg-nextgen", - "name": "NEXTGEN", - "symbol": "NXG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "among-among-us-protocol", - "name": "Among Us Protocol", - "symbol": "AMONG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goldenxrp-goldenxrp", - "name": "GoldenXRP", - "symbol": "GOLDENXRP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ht-haitian", - "name": "Haitian", - "symbol": "HT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eggz-eggzilla", - "name": "Eggzilla", - "symbol": "EGGZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "melee-meleerock", - "name": "Meleerock", - "symbol": "$MELEE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyd-babydevo", - "name": "BabyDevo", - "symbol": "BABYD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blt-bluntcoin", - "name": "Bluntcoin", - "symbol": "BLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "teslaboy-teslaboy", - "name": "TeslaBoy", - "symbol": "TESLABOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aim3-aim3", - "name": "AIM_3", - "symbol": "AIM_3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "batboy-bat-boy", - "name": "Bat Boy", - "symbol": "BATBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shillkrieg-shillkrieg", - "name": "SHILLKRIEG", - "symbol": "SHILLKRIEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tt11-testtoken11", - "name": "TestToken11", - "symbol": "TT11", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssshiba-starshipshiba", - "name": "StarshipSHIBA", - "symbol": "SSSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eethe-senpaifinancecom", - "name": "SENPAIfinance.com", - "symbol": "EETHE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokiclassic-flokc", - "name": "FLOKC", - "symbol": "FLOKICLASS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trksh-trksh", - "name": "trksh", - "symbol": "TRKSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cheap-cheapcoin", - "name": "Cheapcoin", - "symbol": "CHEAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "agrv-agora-vai", - "name": "Agora Vai", - "symbol": "AGRV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dbg-digital-bullion-gold", - "name": "Digital Bullion Gold", - "symbol": "DBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "supfan-superfantom", - "name": "SuperFantom", - "symbol": "SUPFAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ocls-oculus", - "name": "Oculus", - "symbol": "OCLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wlff-wolffin", - "name": "Wolffin", - "symbol": "WLFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rsd-reisender", - "name": "Reisender", - "symbol": "RSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sberry-strawberryswap", - "name": "StrawberrySwap", - "symbol": "SBERRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "capshib-captain-shib", - "name": "Captain SHIB", - "symbol": "CAPSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ckc-crop-kingz-coin", - "name": "CROP KINGZ COIN", - "symbol": "$CKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grand-grandino", - "name": "Grandino", - "symbol": "GRAND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nbm-nftbunnymoon", - "name": "NFTBunnyMoon", - "symbol": "$NBM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "monare-monsters-arena", - "name": "Monsters Arena", - "symbol": "MONARE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metasquid-meta-squid-inu", - "name": "Meta Squid Inu", - "symbol": "METASQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kwark-kwark", - "name": "Kwark", - "symbol": "KWARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "o2-o2-finance", - "name": "O2 Finance", - "symbol": "O2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aoh-armyofhonor", - "name": "ArmyOfHonor", - "symbol": "AOH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squid-squid-moon-inu", - "name": "Squid Moon Inu", - "symbol": "SQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rsc-rhymeickcoin", - "name": "Rhyme$ickCoin", - "symbol": "RSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogeelon-babydogeelon", - "name": "BabyDogeElon", - "symbol": "DOGEELON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "centbee-centbeetoken", - "name": "CentBeeToken", - "symbol": "CENTBEE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidelon-squidspongeelonshiba", - "name": "SquidSpongeElonShiba", - "symbol": "SQUIDELON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bulldobo-bulldogebonk", - "name": "BullDogeBonk", - "symbol": "BULLDOBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unibnb-unibnb", - "name": "UniBNB", - "symbol": "UNIBNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vst-ventiswap", - "name": "VentiSwap", - "symbol": "VST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aras-aras-media", - "name": "ARAS MEDIA", - "symbol": "ARAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "receipts-receipts", - "name": "Receipts", - "symbol": "RECEIPTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ndr-ender", - "name": "Ender", - "symbol": "NDR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "anime-anime", - "name": "ANIME", - "symbol": "ANIME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1000xshibasfriend-1000x-shiba39s-best-friend", - "name": "1000X Shiba's best friend", - "symbol": "1000XSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shb-shibadogefloki", - "name": "ShibaDogeFloki", - "symbol": "SHB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tester-test", - "name": "test", - "symbol": "TESTER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrp-ripple", - "name": "Ripple", - "symbol": "XRP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ferrari-ferrari", - "name": "Ferrari", - "symbol": "FERRARI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ma-motoa", - "name": "MotoA", - "symbol": "MA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mjt-matej-token", - "name": "Matej Token", - "symbol": "MJT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shuka-shuka-inu", - "name": "Shuka Inu", - "symbol": "SHUKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "100xsquidmoon-100x-squid-moon", - "name": "100X Squid Moon", - "symbol": "100XSQUIDM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ghm-gotham", - "name": "GOTHAM", - "symbol": "GHM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiba-world-shiba-world", - "name": "SHIBA WORLD", - "symbol": "SHIBA WORL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sharky-shark-token", - "name": "SHARK TOKEN", - "symbol": "SHARKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibabeer-shiba-beer", - "name": "SHIBA BEER", - "symbol": "SHIBABEER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babysquidmoon-baby-squid-moon", - "name": "Baby Squid Moon", - "symbol": "BABYSQUIDM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tff-test-ff", - "name": "Test FF", - "symbol": "TFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "secret-secretsanta", - "name": "SecretSanta", - "symbol": "SECRET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "avl-", - "name": "-", - "symbol": "AVL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "everpond-everpond", - "name": "EverPOND", - "symbol": "EVERPOND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pfizer-pfizer", - "name": "Pfizer", - "symbol": "PFIZER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "memusic-memusicio", - "name": "Memusic.io", - "symbol": "MEMUSIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "farmi-farmersworldio", - "name": "farmersworld.io", - "symbol": "FARMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eaglex-eaglex", - "name": "EagleX", - "symbol": "EAGLEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shreksnek-shreksnek", - "name": "SHREKSNEK", - "symbol": "SHREKSNEK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "masa-masamune", - "name": "Masamune", - "symbol": "MASA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bluecheese-baloochy", - "name": "Baloochy", - "symbol": "$BLUECHEES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shunt-shibahunter", - "name": "ShibaHunter", - "symbol": "SHUNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minizilla-minimoonzilla", - "name": "MiniMoonZilla", - "symbol": "MINIZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tt10-testtoken10", - "name": "TestToken10", - "symbol": "TT10", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cocoin-connected", - "name": "Connected", - "symbol": "COCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wfan-wrappedfantom", - "name": "WrappedFantom", - "symbol": "WFAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bet-betacoin", - "name": "BetaCoin", - "symbol": "BET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "friendshib-friendshib", - "name": "FriendShib", - "symbol": "FRIENDSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "white-whitenorth", - "name": "WhiteNorth", - "symbol": "WHITE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ant-asynorotoken", - "name": "AsyNoroToken", - "symbol": "ANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cpc-copecoin", - "name": "COPECOIN", - "symbol": "CPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "candygirl-candygirl", - "name": "CandyGirl", - "symbol": "CANDYGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldf-littledogeflokidogefloki", - "name": "LittleDogeFlokiDogeFloki", - "symbol": "LDF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dns-dinosaur", - "name": "DINOSAUR", - "symbol": "DNS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hnc-huncoin", - "name": "Huncoin", - "symbol": "HNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xve-the-vegan-initiative", - "name": "The Vegan Initiative", - "symbol": "XVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gmx-goldmaxcoin", - "name": "GoldMaxCoin", - "symbol": "GMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "babypond-babypond", - "name": "BabyPOND", - "symbol": "BABYPOND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unrc-universalroyalcoin", - "name": "UniversalRoyalCoin", - "symbol": "UNRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dutch-dutch-coin", - "name": "Dutch Coin", - "symbol": "DUTCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cc-cybercoin", - "name": "CyberCoin", - "symbol": "CC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "namo-namocoin", - "name": "NamoCoin", - "symbol": "NAMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btbc-bitbase", - "name": "Bitbase", - "symbol": "BTBc", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pcs-pabyosi-coin-special", - "name": "Pabyosi Coin (Special)", - "symbol": "PCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qbt-cubits", - "name": "Cubits", - "symbol": "QBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rcn-rcoin", - "name": "Rcoin", - "symbol": "RCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "deus-deuscoin", - "name": "DeusCoin", - "symbol": "DEUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "golf-golfcoin", - "name": "Golfcoin", - "symbol": "GOLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "marx-marxcoin", - "name": "MarxCoin", - "symbol": "MARX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "coco-coconutswap", - "name": "CoconutSwap", - "symbol": "COCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gp-grand-exchange", - "name": "Grand Exchange", - "symbol": "GP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "powerzilla-powerzilla", - "name": "PowerZilla", - "symbol": "POWERZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gay-gay-money", - "name": "GAY Money", - "symbol": "GAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ur-ur", - "name": "UR", - "symbol": "UR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gain-ugain", - "name": "UGAIN", - "symbol": "GAIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "magn-magnetcoin", - "name": "Magnetcoin", - "symbol": "MAGN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ldcn-landcoin", - "name": "LandCoin", - "symbol": "LDCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "flap-flappycoin", - "name": "FlappyCoin", - "symbol": "FLAP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "don-donationcoin", - "name": "Donationcoin", - "symbol": "DON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bank-bankheist", - "name": "BankHeist", - "symbol": "BANK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "elc-elacoin", - "name": "Elacoin", - "symbol": "ELC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zse-zsecoin", - "name": "ZSEcoin", - "symbol": "ZSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "irl-irishcoin", - "name": "IrishCoin", - "symbol": "IRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fonz-fonziecoin", - "name": "Fonziecoin", - "symbol": "FONZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sfe-safecoin", - "name": "SafeCoin", - "symbol": "SFE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "turbo-turbocoin", - "name": "TurboCoin", - "symbol": "TURBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vulc-vulcano-old", - "name": "Vulcano [OLD]", - "symbol": "VULC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gbrc-global-business-revolution", - "name": "Global Business Revolution", - "symbol": "GBRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dice-dice-inu", - "name": "Dice Inu", - "symbol": "DICE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aky-akuya-coin", - "name": "Akuya Coin", - "symbol": "AKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wow-wowcoin", - "name": "Wowcoin", - "symbol": "WOW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "apc-alpacoin", - "name": "AlpaCoin", - "symbol": "APC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "high-high-gain", - "name": "High Gain", - "symbol": "HIGH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blf-blackfriday", - "name": "BLACKFRIDAY", - "symbol": "BLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgm-dancingmike", - "name": "DancingMike", - "symbol": "DGM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ble-blackeagle", - "name": "blackeagle", - "symbol": "BLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metarex-metarex", - "name": "MetaRex", - "symbol": "METAREX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kingmeta-king-meta", - "name": "KING-META", - "symbol": "KINGMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dmc-dynamiccoin", - "name": "DynamicCoin", - "symbol": "DMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kuma-inu-kuma-inu-token", - "name": "KUMA INU TOKEN", - "symbol": "KUMA INU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyomen-baby-omen", - "name": "Baby Omen", - "symbol": "BABYOMEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "non-naughtyornice", - "name": "NaughtyOrNice", - "symbol": "NON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zsa-zenshiba", - "name": "ZenShiba", - "symbol": "$ZSA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sandinu-sandinu", - "name": "SANDINU", - "symbol": "SANDINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kng-kangaroo", - "name": "Kangaroo", - "symbol": "KNG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "memes-cometomemes", - "name": "ComeToMemes", - "symbol": "MEMES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zbc-zilbercoin", - "name": "Zilbercoin", - "symbol": "ZBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mdoge-meta-doge", - "name": "Meta Doge", - "symbol": "MDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vadim-vadyacoin", - "name": "VADYACOIN", - "symbol": "VADIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fries-friesprotocol", - "name": "FriesProtocol", - "symbol": "FRIES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttk-tototoken", - "name": "Tototoken", - "symbol": "TTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "light-lightroom", - "name": "Lightroom", - "symbol": "LIGHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibpeng-shibapenguins", - "name": "ShibaPenguins", - "symbol": "SHIBPENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amstaff-thestaffy", - "name": "TheStaffy", - "symbol": "AMSTAFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aim2-aim2", - "name": "AIM_2", - "symbol": "AIM_2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uni-unicornfinance", - "name": "Unicorn.Finance", - "symbol": "UNI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crt-cyborgrabbit", - "name": "CyborgRabbit", - "symbol": "CRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cro-cryptocom", - "name": "CRYPTO.COM", - "symbol": "CRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unic-universal-earth-currency", - "name": "Universal Earth Currency", - "symbol": "UNIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "melasty-melasty-finance", - "name": "Melasty Finance", - "symbol": "MELASTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "betadefi-betadefi", - "name": "BetaDefi", - "symbol": "BETADEFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "swmc-straight-white-male", - "name": "Straight White Male", - "symbol": "SWMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nwo-nwo", - "name": "NWO", - "symbol": "NWO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "speng-superpenguins", - "name": "SuperPenguins", - "symbol": "SPENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "telecraft-telecraft", - "name": "TELECRAFT", - "symbol": "TELECRAFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiba-inu-sky-shiba-inu-sky", - "name": "SHIBA INU SKY", - "symbol": "SHIBA INU ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vmoon-valhallamoon", - "name": "VALHALLAMOON", - "symbol": "VMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tria-triaconta", - "name": "Triaconta", - "symbol": "TRIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sm-squidmoon", - "name": "SquidMoon", - "symbol": "SM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adam-cardamoon", - "name": "CARDAMOON", - "symbol": "ADAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bot4-bot4", - "name": "BOT4", - "symbol": "BOT4", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squid-squidswap", - "name": "SquidSwap", - "symbol": "SQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wbdoge-wrappedbabydoge", - "name": "WrappedBabyDoge", - "symbol": "WBDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "godburn-godburn", - "name": "GODBURN", - "symbol": "GODBURN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safemeta-safemeta", - "name": "SAFEMETA", - "symbol": "SAFEMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spartans-spartanshiba", - "name": "SpartanShiba", - "symbol": "SPARTANS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "amoon-axie-moon", - "name": "Axie Moon", - "symbol": "AMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "reve-revecash", - "name": "Revecash", - "symbol": "REVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bgc-bigcock", - "name": "BigCock", - "symbol": "BGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mnfi-miniswap-finance", - "name": "Miniswap Finance", - "symbol": "MNFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "igr-igorium", - "name": "Igorium", - "symbol": "IGR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test-testcoin1", - "name": "testcoin1", - "symbol": "$TEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fvd-forumcoin", - "name": "Forumcoin", - "symbol": "FVD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mit-mitcoin", - "name": "MITCOIN", - "symbol": "MIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "i4sx-inspiration-4-spacex", - "name": "INSPIRATION 4 SPACEX", - "symbol": "I4SX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tbusd-test-busd", - "name": "TEST BUSD", - "symbol": "TBUSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sdoge-superdoge", - "name": "SuperDoge", - "symbol": "SDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goodgirl-goodgirl", - "name": "GoodGirl", - "symbol": "GOODGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dinar-emas-digital", - "name": "Emas Digital", - "symbol": "DINAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pgx-emas-digital", - "name": "Emas Digital", - "symbol": "PGX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shn-shian", - "name": "SHIAN", - "symbol": "SHN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cybdog-cyborgdoge", - "name": "CyborgDoge", - "symbol": "CYBDOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sigma-sigmacoin", - "name": "SIGMAcoin", - "symbol": "SIGMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "best-bestchain", - "name": "BestChain", - "symbol": "BEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eggplant-eggplantfinance", - "name": "EggplantFinance", - "symbol": "EGGPLANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonway-moonway", - "name": "MoonWay", - "symbol": "MOONWAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sha-shacoin", - "name": "SHACoin", - "symbol": "SHA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fazz-fazzcoin", - "name": "Fazzcoin", - "symbol": "FAZZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sdg-syncdao-governance", - "name": "SyncDAO Governance", - "symbol": "SDG", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "xtd-xtd-coin", - "name": "XTD Coin", - "symbol": "XTD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "frn-francs", - "name": "Francs", - "symbol": "FRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "skr-sakuracoin", - "name": "Sakuracoin", - "symbol": "SKR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vash-vpncoin", - "name": "VPNCoin", - "symbol": "VASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pec-peacecoin", - "name": "Peacecoin", - "symbol": "PEC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blueshiba-babyblueshiba", - "name": "BabyBlueShiba", - "symbol": "BLUESHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "antx-antimatter", - "name": "Antimatter", - "symbol": "ANTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tcoin-t-coin", - "name": "T-coin", - "symbol": "TCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "india-india-coin", - "name": "India Coin", - "symbol": "INDIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rega-regacoin", - "name": "Regacoin", - "symbol": "REGA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "msd-msd", - "name": "MSD", - "symbol": "MSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "numus-numuscash", - "name": "NumusCash", - "symbol": "NUMUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "today-todaycoin", - "name": "TodayCoin", - "symbol": "TODAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "grn-granite", - "name": "Granite", - "symbol": "GRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sbc-strikebitclub", - "name": "StrikeBitClub", - "symbol": "SBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ic-ignition", - "name": "Ignition", - "symbol": "IC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ore-galactrum", - "name": "Galactrum", - "symbol": "ORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gbg-golos-gold", - "name": "Golos Gold", - "symbol": "GBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lbts-lebronthanos", - "name": "LebronThanos", - "symbol": "LBTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gma-enigma-dao", - "name": "Enigma DAO", - "symbol": "GMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bos-boscoin", - "name": "BOScoin", - "symbol": "BOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mage-magiccoin", - "name": "MagicCoin", - "symbol": "MAGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tok-tokugawa", - "name": "Tokugawa", - "symbol": "TOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sic-swisscoin", - "name": "Swisscoin", - "symbol": "SIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tokc-tokyo", - "name": "TOKYO", - "symbol": "TOKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jiyo-jiyo", - "name": "Jiyo [OLD]", - "symbol": "JIYO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kb3-b3coin", - "name": "B3Coin", - "symbol": "KB3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lbtc-lightning-bitcoin", - "name": "Lightning Bitcoin", - "symbol": "LBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "swtc-jingtum-tech", - "name": "Jingtum Tech", - "symbol": "SWTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eag-ea-coin", - "name": "EA Coin", - "symbol": "EAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "club-clubcoin", - "name": "ClubCoin", - "symbol": "CLUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sbg-shibagamer", - "name": "ShibaGamer", - "symbol": "SBG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsr-bitsoar", - "name": "BitSoar", - "symbol": "BSR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hbc-homeblockcoin", - "name": "HomeBlockCoin", - "symbol": "HBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hc-harvest-masternode-coin", - "name": "Harvest Masternode Coin", - "symbol": "HC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dav-davorcoin", - "name": "DavorCoin", - "symbol": "DAV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "satboy-saturnboy", - "name": "SaturnBoy", - "symbol": "SATBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "genshinbabyfloki-genshinbabyfloki", - "name": "GenshinBabyFloki", - "symbol": "GENSHINBAB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cosmostoken-cosmos-token", - "name": "Cosmos Token", - "symbol": "COSMOSTOKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ths-techshares", - "name": "TechShares", - "symbol": "THS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shib-shib-train", - "name": "Shib Train", - "symbol": "SHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bst-bastet", - "name": "BASTET", - "symbol": "BST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bunny-bunny", - "name": "Bunny", - "symbol": "BUNNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "infinityada-infinityada", - "name": "INFINITYADA", - "symbol": "INFINITYAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "everbdoge-everbabydoge", - "name": "EverBabyDoge", - "symbol": "EVERBDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shr-runkishu", - "name": "RUNKISHU", - "symbol": "SHR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dallas-dallas", - "name": "DALLAS", - "symbol": "DALLAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spacex-spacex", - "name": "SpaceX", - "symbol": "SPACEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "safedoge-safe-doge-coin", - "name": "Safe Doge Coin", - "symbol": "SAFEDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dumbbo-dumbbo-token", - "name": "Dumbbo Token", - "symbol": "DUMBBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hado-happydoge", - "name": "HappyDoge", - "symbol": "HADO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbk-mediablock", - "name": "MEDIABLOCK", - "symbol": "MBK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tst-test", - "name": "test", - "symbol": "TST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbc-miamibeachcoin", - "name": "MiamiBeachCoin", - "symbol": "MBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minifloki-minifloki", - "name": "MiniFloki", - "symbol": "MINIFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bakayaro-bakayaro-inu-twittercomjunekomori", - "name": "Bakayaro Inu | Twitter.com/JuneKomori", - "symbol": "BAKAYARO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ny-new-york-finance", - "name": "New York Finance", - "symbol": "NY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ds-dogeswap", - "name": "DogeSwap", - "symbol": "DS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bshib-bshiba-inu", - "name": "BSHIBA INU", - "symbol": "BSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clxmtc-clixmatic", - "name": "Clixmatic", - "symbol": "CLXMTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wojak-wojak-coin", - "name": "Wojak Coin", - "symbol": "WOJAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "clow-clowns-coin", - "name": "CLOWNS COIN", - "symbol": "CLOW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pup-pixelpupnft", - "name": "PixelPupNFT", - "symbol": "PUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grape-grapeswap", - "name": "GrapeSwap", - "symbol": "GRAPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tb-richboss", - "name": "RichBoss", - "symbol": "TB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slots-slotswap", - "name": "SlotSwap", - "symbol": "SLOTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lfsd-left-side", - "name": "Left Side", - "symbol": "LFSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bislis-bistropolis", - "name": "Bistropolis", - "symbol": "BISLIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "comf-comfy-rocket", - "name": "Comfy Rocket", - "symbol": "COMF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vrts-vertus", - "name": "Vertus", - "symbol": "VRTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "grift-griftcoin", - "name": "GriftCoin", - "symbol": "GRIFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibaapemoon-shiba-ape-moon", - "name": "Shiba Ape Moon", - "symbol": "SHIBAAPEMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shr-shibarun", - "name": "SHIBARUN", - "symbol": "SHR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hts-hotsanta", - "name": "HOTSANTA", - "symbol": "HTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "knight-czechknight", - "name": "CzechKnight", - "symbol": "KNIGHT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dark-doge-dark", - "name": "DOGE DARK", - "symbol": "DARK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonchain-moonchain", - "name": "MOONCHAIN", - "symbol": "$MOONCHAIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gd-goldendoge", - "name": "GoldenDoge", - "symbol": "GD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pulse-pulsechain-token", - "name": "PulseChain Token", - "symbol": "PULSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "srt-satoshirewardstoken", - "name": "SatoshiRewardsToken", - "symbol": "SRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "inf-infchain", - "name": "InfChain", - "symbol": "INF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "powell-transitory-inflation", - "name": "Transitory Inflation", - "symbol": "POWELL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fash-fastshiba", - "name": "FastShiba", - "symbol": "FASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsqm-buffedsquidmoon", - "name": "BuffedSquidMoon", - "symbol": "BSQM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "httpstmepaperium-paperium", - "name": "Paperium", - "symbol": "HTTPS://T.", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kerb-kerbal", - "name": "KERBAL", - "symbol": "KERB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dak-dragon-and-knight", - "name": "Dragon And Knight", - "symbol": "DAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ter-terranova", - "name": "TerraNova", - "symbol": "TER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "onft-onlynft", - "name": "OnlyNFT", - "symbol": "ONFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fuk-fuckyou", - "name": "fuckyou", - "symbol": "FUK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evp-everpool", - "name": "EVERPOOL", - "symbol": "EVP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ifc-infinitecoin", - "name": "Infinitecoin", - "symbol": "IFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metag-metaversegaming", - "name": "MetaVerseGaming", - "symbol": "METAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goodboy-goodboy", - "name": "GoodBoy", - "symbol": "GOODBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "galaxy-galaxy", - "name": "Galaxy", - "symbol": "GALAXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nms-numus", - "name": "Numus", - "symbol": "NMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mmm-meta-megaman", - "name": "META MEGAMAN", - "symbol": "MMM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axmfin-axiom-finance", - "name": "Axiom Finance", - "symbol": "AXMFIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sub-subway-finance", - "name": "Subway Finance", - "symbol": "SUB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scboy-spacecatboy", - "name": "SpaceCatBoy", - "symbol": "SCBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ssm-supersquid-moon", - "name": "SUPERSQUID MOON", - "symbol": "SSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cro-cryptocom-coin", - "name": "Crypto.com Coin", - "symbol": "CRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rtsd-right-side", - "name": "Right Side", - "symbol": "RTSD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bm-baby-mana", - "name": "Baby Mana", - "symbol": "BM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "strp-streep", - "name": "Streep", - "symbol": "STRP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lol-lolinu", - "name": "LOLINU", - "symbol": "LOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lbch-lamebranch", - "name": "LameBranch", - "symbol": "LBCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "donut-donutcoin", - "name": "DonutCoin", - "symbol": "DONUT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "daddyfegback-daddyfegback", - "name": "DaddyFegBack", - "symbol": "DADDYFEGBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cyder-cyder", - "name": "Cyder", - "symbol": "CYDER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fem-feminism", - "name": "Feminism", - "symbol": "FEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xstc-safe-trade-coin", - "name": "Safe Trade Coin", - "symbol": "XSTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ortown-origina-town", - "name": "Origina Town", - "symbol": "ORTOWN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hexape-hexape", - "name": "HEXApe", - "symbol": "HEXAPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vass-vass", - "name": "vass", - "symbol": "VASS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "senpai-senpai-swap", - "name": "Senpai Swap", - "symbol": "SENPAI(", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trump-trump2024", - "name": "Trump2024", - "symbol": "TRUMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "solavax-solanaavax", - "name": "SolanaAVAX", - "symbol": "SOLAVAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "galadefi-galadefi", - "name": "GALADefi", - "symbol": "GALADEFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "krypt-krypt-token", - "name": "Krypt Token", - "symbol": "KRYPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rx-unity-coin", - "name": "Unity Coin", - "symbol": "RX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiba-shiba-nft", - "name": "SHIBA NFT", - "symbol": "SHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "banana-bananaswap", - "name": "BananaSwap", - "symbol": "BANANA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hafb-happyflokibaby", - "name": "HappyFlokiBaby", - "symbol": "HAFB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rboy-rocketboy", - "name": "RocketBoy", - "symbol": "RBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adakong-babyadakong", - "name": "BabyAdaKong", - "symbol": "ADAKONG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mars-marsrocket", - "name": "Marsrocket", - "symbol": "MARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "donuttest-donuttokentest22", - "name": "DonutTokenTest22", - "symbol": "DONUTTEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "broke-brokeboy", - "name": "BROKEBOY", - "symbol": "BROKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gorillabeast-gorillabeast", - "name": "GorillaBeast", - "symbol": "GORILLABEA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rs-roboswarm", - "name": "RoboSwarm", - "symbol": "$RS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "0zo-ozocasht27", - "name": "ozoCashT27", - "symbol": "0ZO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hafl-happyfloki", - "name": "HappyFloki", - "symbol": "HAFL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kje-kimjongelon", - "name": "KimJongElon", - "symbol": "KJE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blc-blc", - "name": "BLC", - "symbol": "BLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orbitmoon-orbitmoon", - "name": "OrbitMoon", - "symbol": "ORBITMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lilfeg-lilfeg", - "name": "LILFEG", - "symbol": "LILFEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tigertrade-tiger", - "name": "TIGER", - "symbol": "TIGER.TRAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtx-metabox", - "name": "Metabox", - "symbol": "MTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitdoge-bitdoge", - "name": "Bitdoge", - "symbol": "BITDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "daddy-dogeback-daddy-dogeback", - "name": "Daddy Dogeback", - "symbol": "DADDY DOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "monk-monks-new-moon-mission", - "name": "Monks New Moon Mission", - "symbol": "MONK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bepis-bepis", - "name": "BEPIS", - "symbol": "BEPIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sinu-santa-inu", - "name": "Santa INU", - "symbol": "SINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babypeng-babypenguins", - "name": "BabyPenguins", - "symbol": "BABYPENG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squidshiba-squidshiba", - "name": "SquidShiba", - "symbol": "SQUIDSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ape-lp-apeswapfinance-lps", - "name": "ApeSwapFinance LPs", - "symbol": "APE-LP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "skylla-skylla", - "name": "Skylla", - "symbol": "SKYLLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pgle-poogle", - "name": "POOGLE", - "symbol": "PGLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pitbulls-pitbulls", - "name": "PitBulls", - "symbol": "PITBULLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cflu-coinfluence", - "name": "Coinfluence", - "symbol": "CFLU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyfeg-babyfeg", - "name": "BabyFEG", - "symbol": "BABYFEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metamusk-elon-metamusk", - "name": "Elon Metamusk", - "symbol": "METAMUSK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "trash-trash-token", - "name": "TRASH TOKEN", - "symbol": "TRASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metasatoshi-metasatoshi", - "name": "MetaSatoshi", - "symbol": "METASATOSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "squid-squidinu", - "name": "SquidInu", - "symbol": "SQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spi-sheepainu", - "name": "SheepaInu", - "symbol": "SPI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaworld-metaworld", - "name": "MetaWorld", - "symbol": "METAWORLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "red-red-token", - "name": "red Token", - "symbol": "RED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "testcoin-testcoin", - "name": "TestCoin", - "symbol": "TESTCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ff-flurryfi", - "name": "FlurryFi", - "symbol": "FF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mworld-mafiasworld", - "name": "MafiasWorld", - "symbol": "MWORLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pitb-pitbull-baby", - "name": "Pitbull Baby", - "symbol": "PITB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lol-league-of-legends", - "name": "League of Legends", - "symbol": "LOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gemnft-gems-nft", - "name": "Gems NFT", - "symbol": "GEMNFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orbit-orbit", - "name": "ORBIT", - "symbol": "ORBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "santagirl-santagirltok", - "name": "SantaGirlTok", - "symbol": "SANTAGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bunny-bunny-token", - "name": "Bunny Token", - "symbol": "BUNNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lpkm-kripton-mobile", - "name": "Kripton Mobile", - "symbol": "LPKM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lbd-little-baby-doge", - "name": "Little Baby Doge", - "symbol": "LBD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "frgc-fargocoin", - "name": "Fargocoin", - "symbol": "FRGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ennea-ennea", - "name": "Ennea", - "symbol": "ENNEA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b2x-segwit2x", - "name": "SegWit2x", - "symbol": "B2X", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "grumpydoge-grumpy-doge", - "name": "Grumpy Doge", - "symbol": "GRUMPYDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bume-buffedmeta", - "name": "BuffedMeta", - "symbol": "BUME", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bass-extrabass", - "name": "EXTRABASS", - "symbol": "BASS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nsm-nanosquidmoon", - "name": "NanoSquidMoon", - "symbol": "NSM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "orbit-orbitlaunch", - "name": "OrbitLaunch", - "symbol": "ORBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokibnb-flokibnb", - "name": "FlokiBNB", - "symbol": "FLOKIBNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "alano-alano-token", - "name": "ALANO Token", - "symbol": "ALANO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "donutboyrev-donut-boy-revolution", - "name": "Donut Boy Revolution", - "symbol": "DONUTBOYRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ltegar-coin-ltegar-coin", - "name": "Ltegar Coin", - "symbol": "LTEGAR COI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "biox-bioma", - "name": "Bioma", - "symbol": "BIOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pudding-puddingswap", - "name": "PuddingSwap", - "symbol": "PUDDING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mworld-mafia39s-world", - "name": "Mafia's World", - "symbol": "MWORLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bote-bozotest", - "name": "BozoTest", - "symbol": "BOTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vtf-vaultfinance", - "name": "VaultFinance", - "symbol": "VTF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "betadex-betadex", - "name": "BetaDEX", - "symbol": "BETADEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vevocards-vevocards-token", - "name": "VevoCards Token", - "symbol": "VEVOCARDS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shinomics-shinomics", - "name": "SHINOMICS", - "symbol": "SHINOMICS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctox-cryptox", - "name": "Cryptox", - "symbol": "CTOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hexpond-hexpond", - "name": "HEXPOND", - "symbol": "HEXPOND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ilu-iloveyou", - "name": "iloveyou", - "symbol": "ILU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lmn-lumin-coin", - "name": "Lumin Coin", - "symbol": "LMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sqm-squid-mini", - "name": "Squid mini", - "symbol": "SQM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wpt-world-padel-tour", - "name": "World Padel Tour", - "symbol": "WPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blb-bentley", - "name": "Bentley", - "symbol": "BLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sba-squidboyastronaut", - "name": "SquidBoyAstronaut", - "symbol": "SBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ingloriousshiba-inglorious-shiba", - "name": "INGLORIOUS SHIBA", - "symbol": "INGLORIOUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vlr-valerstudios", - "name": "ValerStudios", - "symbol": "VLR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pawg-pawgnation", - "name": "PAWGNation", - "symbol": "PAWG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "donutboy-donut-boy", - "name": "Donut Boy", - "symbol": "DONUTBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "panda-panda-chain", - "name": "Panda Chain", - "symbol": "PANDA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "goldinu-goldeninu", - "name": "GoldenInu", - "symbol": "GOLDINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyminishibadoge-babyminishibadoge", - "name": "BabyMiniShibaDoge", - "symbol": "BABYMINISH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hn-honey", - "name": "Honey", - "symbol": "HN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bffdsantaverse-buffedsantaverse", - "name": "BuffedSantaVerse", - "symbol": "BFFDSANTAV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nm-neom-token", - "name": "NEOM TOKEN", - "symbol": "NM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unifi-uni-finance", - "name": "Uni Finance", - "symbol": "UNIFI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cp-closedpaperium", - "name": "ClosedPaperium", - "symbol": "CP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pepoge-pepoge", - "name": "Pepoge", - "symbol": "PEPOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onffft-onlyffnft", - "name": "OnlyffNFT", - "symbol": "ONFFFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "testbc-testbc", - "name": "TESTBC", - "symbol": "TESTBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moonpond-moonpond", - "name": "MOONPOND", - "symbol": "MOONPOND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tlusa-trail-life-usa", - "name": "Trail Life USA", - "symbol": "TLUSA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibf-fly-shiba", - "name": "FLY SHIBA", - "symbol": "SHIBF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mlit-moonlit-rockets", - "name": "Moonlit Rockets", - "symbol": "MLIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sqb-squidbaby", - "name": "SquidBaby", - "symbol": "SQB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgmx-doggymax", - "name": "Doggymax", - "symbol": "DGMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "magic-magiccoin", - "name": "MagicCoin", - "symbol": "MAGIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ldg-lil-dragon", - "name": "Lil Dragon", - "symbol": "LDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "run-semper", - "name": "Semper", - "symbol": "RUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evlxpresale-evolime-matrix-presale", - "name": "@EvoLime Matrix (presale)", - "symbol": "EVLXPRESAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bumeta-buffedmeta", - "name": "BuffedMeta", - "symbol": "BUMETA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gravity-gravaty-protocol", - "name": "Gravaty Protocol", - "symbol": "GRAVITY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "poogle-poogletoken", - "name": "PoogleToken", - "symbol": "POOGLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaelf-metaelf", - "name": "METAELF", - "symbol": "METAELF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rct-realchain", - "name": "RealChain", - "symbol": "RCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "op-openpaperium", - "name": "OpenPaperium", - "symbol": "OP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dnttoken22-dnt-token-2", - "name": "DNT Token 2", - "symbol": "DNTTOKEN22", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wmt-wodex-token", - "name": "Wodex Token", - "symbol": "WMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "beerboy-beerboybsc", - "name": "BeerBoyBSC", - "symbol": "BEERBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gucci-gucci", - "name": "Gucci", - "symbol": "GUCCI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ffx-fanflix", - "name": "Fanflix", - "symbol": "FFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pkm-printer-kishu-moon", - "name": "Printer Kishu Moon", - "symbol": "PKM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "icoti-icoti", - "name": "ICOTI", - "symbol": "ICOTI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gumbo-gumbo", - "name": "Gumbo", - "symbol": "GUMBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test2-test2", - "name": "TEST2", - "symbol": "TEST2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "papi-papi", - "name": "Papi", - "symbol": "PAPI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibarmy-shibarmy-inu", - "name": "ShibArmy Inu", - "symbol": "SHIBARMY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "opensea-opensea-nft-marketplace", - "name": "OpenSea NFT marketplace", - "symbol": "OPENSEA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sci-starved-amp-choked-inu", - "name": "Starved \u0026 Choked Inu", - "symbol": "SCI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chokedelon-choked-elon", - "name": "Choked Elon", - "symbol": "CHOKEDELON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "durant-durant", - "name": "Durant", - "symbol": "DURANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "durant-durant-inu", - "name": "Durant Inu", - "symbol": "DURANT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "starlk-starlinkdao", - "name": "StarlinkDAO", - "symbol": "STARLK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lhasa-apso-lhasa-apso-inu", - "name": "Lhasa-Apso Inu", - "symbol": "LHASA-APSO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "aquarius-inu-aquarius-inu", - "name": "Aquarius Inu", - "symbol": "AQUARIUS I", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "1punch-one-punch", - "name": "One Punch", - "symbol": "1PUNCH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nightwing-nightwing", - "name": "NIGHTWING", - "symbol": "NIGHTWING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zapdos-zapdos-inu", - "name": "Zapdos Inu", - "symbol": "ZAPDOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "egg-world-record-egg", - "name": "World Record Egg", - "symbol": "EGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vinu-vegetainu", - "name": "VegetaINU", - "symbol": "VINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ainu-alphainu", - "name": "AlphaInu", - "symbol": "AINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethereum-inu-ethereuminu", - "name": "EthereumInu", - "symbol": "ETHEREUM I", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaparty-metaparty", - "name": "MetaParty", - "symbol": "METAPARTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babybart-baby-bart-simpson", - "name": "Baby Bart Simpson", - "symbol": "BABYBART", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metaversex-metaversex", - "name": "MetaverseX", - "symbol": "METAVERSEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "organic-organic-growth-token", - "name": "Organic Growth Token", - "symbol": "ORGANIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ethu-eth-unique", - "name": "ETH Unique", - "symbol": "ETHU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "keys-keys", - "name": "KEYS", - "symbol": "KEYS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metapets-meta-pets", - "name": "Meta Pets", - "symbol": "METAPETS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "interstellarinu-interstellarinu", - "name": "InterstellarInu", - "symbol": "INTERSTELL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jeet-jeetverse", - "name": "JeetVerse", - "symbol": "JEET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ipo-initial-public-offering", - "name": "Initial Public Offering", - "symbol": "IPO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jinbe-jinbeinu", - "name": "JinbeInu", - "symbol": "JINBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stellarinu-stellar-inu", - "name": "Stellar Inu", - "symbol": "STELLARINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tarb-starburst", - "name": "Starburst", - "symbol": "TARB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "-rabbit-swap", - "name": "Rabbit Swap", - "symbol": "🐰", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "testbr2-dont-buy-br2", - "name": "Dont Buy BR2", - "symbol": "TESTBR2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tallsquidm-tallsquidmoon", - "name": "TallSquidMoon", - "symbol": "TALLSQUIDM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tb-tigerbooy", - "name": "TigerBooy", - "symbol": "TB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flokiape-flokiape", - "name": "FlokiApe", - "symbol": "FLOKIAPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lordwar-lordwar", - "name": "Lordwar", - "symbol": "LORDWAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "show-show", - "name": "Show", - "symbol": "SHOW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "starlink-starlink", - "name": "StarLink", - "symbol": "STARLINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mc-monetecoin", - "name": "MoneteCoin", - "symbol": "MC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shk-shark", - "name": "Shark", - "symbol": "SHK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "megat-megatoken", - "name": "Megatoken", - "symbol": "MEGAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sman-spiderman", - "name": "SpiderMan", - "symbol": "SMAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "unifloki-unifloki", - "name": "UniFloki", - "symbol": "UNIFLOKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "poogletoken-poogletoken", - "name": "PoogleToken", - "symbol": "POOGLETOKE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fiverse-flokiinuverse", - "name": "FlokiInuVerse", - "symbol": "FIVERSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slpc-slipcoin", - "name": "SLIPCOIN", - "symbol": "SLPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "son-son-of-kishu", - "name": "Son Of Kishu", - "symbol": "SON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ram-rick-and-morty-token", - "name": "RICK AND MORTY TOKEN", - "symbol": "RAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "snowpunk-snowpunk", - "name": "SnowPunk", - "symbol": "SNOWPUNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "poogle-poogle-token", - "name": "Poogle Token", - "symbol": "POOGLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shiburn-shiba-burn", - "name": "SHIBA BURN", - "symbol": "SHIBURN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cumt-computercoin", - "name": "computercoin", - "symbol": "CUMT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gqt-gamequick-token", - "name": "GameQuick Token", - "symbol": "GQT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sqb-squidbonk", - "name": "SquidBonk", - "symbol": "SQB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "puco-puco-swap", - "name": "PUCO SWAP", - "symbol": "PUCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uzumaki-uzumaki-naruto", - "name": "Uzumaki Naruto", - "symbol": "UZUMAKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hmg-hmong", - "name": "Hmong", - "symbol": "HMG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "stc-sonartokencoin", - "name": "SonarTokenCoin", - "symbol": "STC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mm-moonman", - "name": "MoonMan", - "symbol": "MM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tom-tommy-inu", - "name": "Tommy Inu", - "symbol": "TOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mercy-mercy", - "name": "MERCY", - "symbol": "MERCY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nm-neom", - "name": "NEOM", - "symbol": "NM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "onlyup-onlyup", - "name": "OnlyUp", - "symbol": "ONLYUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kdg-kanondoggogato", - "name": "KanonDoggoGato", - "symbol": "KDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "boyporto-boyporto", - "name": "BOYPORTO", - "symbol": "BOYPORTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b0zot24-bozocasht24", - "name": "BozoCashT24", - "symbol": "B0ZOT24", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tlc-the-last-chance", - "name": "The Last Chance", - "symbol": "TLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "miu-metainu", - "name": "MetaInu", - "symbol": "MIU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "evlxpre-evolime-matrix-presale-token", - "name": "@EvoLime Matrix (presale token)", - "symbol": "EVLXPRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atmc-atmcoin", - "name": "ATMCoin", - "symbol": "ATMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ducky-duck-inu", - "name": "Duck INU", - "symbol": "DUCKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spacedoge-spacedoge", - "name": "SPACEDOGE", - "symbol": "SPACEDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "eterl-eterland", - "name": "eterland", - "symbol": "ETERL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rb-richbossbsc", - "name": "RichBossBsc", - "symbol": "RB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "olf-onlyfans", - "name": "OnlyFans", - "symbol": "OLF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ghurt-ghurt", - "name": "ghurt", - "symbol": "GHURT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "b0zot23-bozocasht23", - "name": "BozoCashT23", - "symbol": "B0ZOT23", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "duck-duck-swap", - "name": "Duck Swap", - "symbol": "DUCK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rrt-rrt", - "name": "RRT", - "symbol": "RRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "chg-cattyhotgirl", - "name": "CattyHotGirl", - "symbol": "CHG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "defipond-defipond", - "name": "DeFiPOND", - "symbol": "DEFIPOND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "finpro-finpro", - "name": "Finpro", - "symbol": "FINPRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "phase-phase-swap", - "name": "Phase Swap", - "symbol": "PHASE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gem-gemstone-defi", - "name": "Gemstone DeFi", - "symbol": "GEM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrd-the-myriad", - "name": "The Myriad", - "symbol": "MRD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apw-applecoin", - "name": "AppleCoin", - "symbol": "APW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pizza-pizzacoin", - "name": "PizzaCoin", - "symbol": "PIZZA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cfc-coffeecoin", - "name": "CoffeeCoin", - "symbol": "CFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "soj-sojourn", - "name": "Sojourn", - "symbol": "SOJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aglc-agrolifecoin", - "name": "AgrolifeCoin", - "symbol": "AGLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metamars-metmars", - "name": "MetMars", - "symbol": "METAMARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "p7c-p7coin", - "name": "P7Coin", - "symbol": "P7C", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xeeb-xeebster", - "name": "Xeebster", - "symbol": "XEEB", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "nodc-nodecoin", - "name": "NodeCoin", - "symbol": "NODC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sdp-sydpak", - "name": "SydPak", - "symbol": "SDP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sandg-save-and-gain", - "name": "Save and Gain", - "symbol": "SANDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crt-crtcoin", - "name": "CRTCoin", - "symbol": "CRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "imps-impulsecoin", - "name": "ImpulseCoin", - "symbol": "IMPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hvco-high-voltage", - "name": "High Voltage", - "symbol": "HVCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pex-posex", - "name": "PosEx", - "symbol": "PEX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rsgp-rsgpcoin", - "name": "RSGPcoin", - "symbol": "RSGP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "os76-osmiumcoin", - "name": "OsmiumCoin", - "symbol": "OS76", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mh-metahuman", - "name": "MetaHuman", - "symbol": "MH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ust-terrausd-wormhole", - "name": "TerraUSD (Wormhole)", - "symbol": "UST", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "altc-antilitecoin", - "name": "Antilitecoin", - "symbol": "ALTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lir-letitride", - "name": "LetItRide", - "symbol": "LIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rok-return-of-the-king", - "name": "Return of The King", - "symbol": "ROK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tpc-10percent", - "name": "10PERCENT", - "symbol": "TPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xoc-xonecoin", - "name": "Xonecoin", - "symbol": "XOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "urc-unrealcoin", - "name": "Unrealcoin", - "symbol": "URC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tagr-tagrcoin", - "name": "TAGRcoin", - "symbol": "TAGR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sh-shilling", - "name": "Shilling", - "symbol": "SH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ibank-ibank", - "name": "iBank", - "symbol": "IBANK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "elonpower-elonpower", - "name": "ELONPOWER", - "symbol": "ELONPOWER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "everbnb-everbnb", - "name": "EverBNB", - "symbol": "EVERBNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dkt-drop-kings-token", - "name": "Drop-Kings Token", - "symbol": "DKT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "foreverfeg-forever-feg", - "name": "FOREVER FEG", - "symbol": "FOREVERFEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mtg-meta-game", - "name": "META GAME", - "symbol": "MTG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "polok-poloktest", - "name": "PoloKtest", - "symbol": "POLOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metag-meta-verse-gaming", - "name": "Meta Verse Gaming", - "symbol": "METAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rr-richboss", - "name": "RichBoss", - "symbol": "RR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babyshiborg-baby-shiborg-inu", - "name": "BABY SHIBORG INU", - "symbol": "BABYSHIBOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "c79-c79", - "name": "C79", - "symbol": "C79", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bzil-brozilla", - "name": "BROZILLA", - "symbol": "BZIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "desu-dexsport", - "name": "Dexsport", - "symbol": "$DESU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jezu-jezu", - "name": "Jezu", - "symbol": "JEZU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dollar-dollar-online", - "name": "Dollar Online", - "symbol": "DOLLAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zyd-zayedcoin", - "name": "Zayedcoin", - "symbol": "ZYD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metamoonlight-metamoonlight", - "name": "MetaMoonLight", - "symbol": "METAMOONLI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drm-dreamcoin", - "name": "Dreamcoin", - "symbol": "DRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "warp-warp", - "name": "WARP", - "symbol": "WARP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hellb-hellbound", - "name": "Hellbound", - "symbol": "HELLB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flok-flokelon-inu", - "name": "Flokelon Inu", - "symbol": "FLOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsc-bowscoin", - "name": "BowsCoin", - "symbol": "BSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "spt-sripro-token", - "name": "SriPro Token", - "symbol": "SPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dversel-dogeverse", - "name": "DOGEverse", - "symbol": "DVERSEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dove-dove-swap", - "name": "Dove Swap", - "symbol": "DOVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "megababy-megababy-inft", - "name": "MegaBaby INFT", - "symbol": "MEGABABY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bankburn-bankburn", - "name": "BankBurn", - "symbol": "BANKBURN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metasfm-metasafemoon", - "name": "MetaSafeMoon", - "symbol": "METASFM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "igc-intergalactic", - "name": "InterGalactic", - "symbol": "IGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "saw-saw-project-token", - "name": "SAW PROJECT TOKEN", - "symbol": "SAW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ctest-ctest", - "name": "Ctest", - "symbol": "CTEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnbp-binance-smart-chain-public", - "name": "Binance smart chain public", - "symbol": "BNBP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "madrabbit-madrabbit", - "name": "MADRABBIT", - "symbol": "MADRABBIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "captaininu-captaininu", - "name": "CaptainInu", - "symbol": "CAPTAININU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ceoshiba-ceo-shiba", - "name": "CEO Shiba", - "symbol": "CEOSHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xlmn-stellar-network", - "name": "Stellar Network", - "symbol": "XLMN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kapri-kapri-inu", - "name": "Kapri Inu", - "symbol": "KAPRI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dverse-dogeverse", - "name": "DOGEVERSE", - "symbol": "DVERSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metalink-metalink", - "name": "METALINK", - "symbol": "METALINK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pkgrl-pokegirl", - "name": "PokeGirl", - "symbol": "PKGRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lucky-luckyinu", - "name": "LuckyInu", - "symbol": "LUCKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pigl-piglet-swap", - "name": "Piglet Swap", - "symbol": "PIGL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sky-skycity-token", - "name": "SkyCity Token", - "symbol": "SKY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "netflix-netflix-token", - "name": "NETFLIX TOKEN", - "symbol": "NETFLIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "des-destiny", - "name": "Destiny", - "symbol": "DES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rlgl-red-light-green-light", - "name": "Red Light Green Light", - "symbol": "RLGL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "isquid-interstellarsquid", - "name": "InterstellarSquid", - "symbol": "ISQUID", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yugimoon-yugimoon", - "name": "YuGimoon", - "symbol": "YUGIMOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "forfeg-forever-feg", - "name": "FOREVER FEG", - "symbol": "$FORFEG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "megai2-metateam", - "name": "METATEAM", - "symbol": "MEGAI2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wt-walport", - "name": "Walport", - "symbol": "WT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gdt-gorilla-diamond-token", - "name": "Gorilla Diamond Token", - "symbol": "GDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibaecosystem-shiba-ecosystem", - "name": "Shiba Ecosystem", - "symbol": "SHIBAECOSY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mini-mini-chain", - "name": "Mini Chain", - "symbol": "MINI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sxo-spacex-orbit", - "name": "SpaceX orbit", - "symbol": "SXO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fal-calf-swap", - "name": "Calf Swap", - "symbol": "FAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mbi-megababy-inft", - "name": "MegaBaby INFT", - "symbol": "MBI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibahexpulse-shibahexpulse", - "name": "ShibaHexPulse", - "symbol": "SHIBAHEXPU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibainu-shibainu-v2", - "name": "ShibaInu V2", - "symbol": "SHIBAINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kumo-kumo-swap", - "name": "Kumo Swap", - "symbol": "KUMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibar-shibareborn", - "name": "ShibaReborn", - "symbol": "SHIBAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lsqm-little-squid-moon", - "name": "Little Squid Moon", - "symbol": "LSQM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pepeboy-pepeboy", - "name": "PepeBoy", - "symbol": "PEPEBOY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "apexmoonbsc-apexmoonbsc", - "name": "ApexMoonBSC", - "symbol": "APEXMOONBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cocacolacoin-cocacola", - "name": "CocaCola", - "symbol": "COCACOLACO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "weed-weedswap", - "name": "WeedSwap", - "symbol": "WEED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "testup-testup", - "name": "Testup", - "symbol": "TESTUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metababe-metababe", - "name": "METABABE", - "symbol": "METABABE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbgrl-sobergirl", - "name": "SoberGirl", - "symbol": "SBGRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ttc2-ttestcoinv2", - "name": "TTestCoinV2", - "symbol": "TTC2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogeking-dogeking", - "name": "Dogeking", - "symbol": "DOGEKING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "shibapunk-shibapunk", - "name": "SHIBAPUNK", - "symbol": "SHIBAPUNK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "miniquack-miniquack", - "name": "MiniQuack", - "symbol": "MINIQUACK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bvc-beavercoin", - "name": "BeaverCoin", - "symbol": "BVC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gbt-gamebet-coin", - "name": "GameBet Coin", - "symbol": "GBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xmk-meta-alfa", - "name": "meta alfa", - "symbol": "XMK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "vec2-vectorai", - "name": "VectorAI", - "symbol": "VEC2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "m31-andromeda", - "name": "Andromeda", - "symbol": "M31", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cumstar-cumstar", - "name": "CUMSTAR", - "symbol": "CUMSTAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flvr-flavorcoin", - "name": "FlavorCoin", - "symbol": "FLVR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "uro-uro", - "name": "Uro", - "symbol": "URO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bstar-blackstar", - "name": "Blackstar", - "symbol": "BSTAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gcc-guccionecoin", - "name": "GuccioneCoin", - "symbol": "GCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gbc-gbcgoldcoin", - "name": "GBCGoldCoin", - "symbol": "GBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bost-boostcoin", - "name": "BoostCoin", - "symbol": "BOST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dogev-dogeverse", - "name": "DogeVerse", - "symbol": "DOGEV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hcoin-hope-coin", - "name": "Hope Coin", - "symbol": "HCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cas-catsquid", - "name": "CatSquid", - "symbol": "$CAS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cfn-cryptofollow-network", - "name": "CryptoFollow Network", - "symbol": "CFN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blvp-belivup", - "name": "Belivup", - "symbol": "BLVP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blry-billarycoin", - "name": "BillaryCoin", - "symbol": "BLRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mgd-mygod", - "name": "MyGod", - "symbol": "MGD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metazillaverse-metazillaverse", - "name": "MetaZillaVerse", - "symbol": "METAZILLAV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gshib-shiba-gold", - "name": "Shiba Gold", - "symbol": "GSHIB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metavg-metaversegaming", - "name": "MetaVerseGaming", - "symbol": "METAVG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metagirl-metagirl", - "name": "MetaGirl", - "symbol": "METAGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "captcatgirl-captiancatgirl", - "name": "CaptianCatGirl", - "symbol": "CAPTCATGIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dgc-duschgelcoin", - "name": "Duschgelcoin", - "symbol": "DGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bsf-baby-shiba-floki", - "name": "Baby Shiba Floki", - "symbol": "BSF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pt-porntoken", - "name": "PornToken", - "symbol": "PT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bzl-busdzilla", - "name": "BUSDZILLA", - "symbol": "BZL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "adrite-adrite-finance", - "name": "Adrite Finance", - "symbol": "ADRITE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dogeverse-dogeverse", - "name": "DOGEVERSE", - "symbol": "DOGEVERSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "choochoo-choo-choo-train", - "name": "Choo Choo Train", - "symbol": "CHOOCHOO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sm-sexymoon", - "name": "SexyMoon", - "symbol": "SM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ptest3-pumptest3", - "name": "Pumptest3", - "symbol": "PTEST3", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metapay-meta-pay", - "name": "Meta Pay", - "symbol": "METAPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hntr-helium-rewards", - "name": "Helium Rewards", - "symbol": "HNTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kishuon-kishuonline", - "name": "kishuonline", - "symbol": "KISHUON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "flower-flower", - "name": "Flower", - "symbol": "FLOWER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sg-squid-game-p2e", - "name": "Squid Game P2E", - "symbol": "SG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mzv-metazillaverse", - "name": "MetaZillaVerse", - "symbol": "MZV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "wtt-washtokentest", - "name": "WASHTOKENTEST", - "symbol": "WTT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "choh-c2h5oh-coin", - "name": "C2H5OH Coin", - "symbol": "CHOH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mrr-mrrobot", - "name": "MrRobot", - "symbol": "MRR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metagirl-meta-girls", - "name": "Meta Girls", - "symbol": "METAGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kawa-kawakami", - "name": "KAWAKAMI", - "symbol": "KAWA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "a-aa", - "name": "aa", - "symbol": "A", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dummy-dummycontract", - "name": "DummyContract", - "symbol": "$DUMMY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dots-dots-chain", - "name": "Dots Chain", - "symbol": "DOTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dcs-deecoins", - "name": "Deecoins", - "symbol": "DCS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "dwg-doge-war-game", - "name": "Doge War Game", - "symbol": "DWG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mgs-meta-green-shiba", - "name": "Meta Green Shiba", - "symbol": "MGS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cobra-cobrainu", - "name": "CobraInu", - "symbol": "COBRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "caketools-caketools", - "name": "CakeTools", - "symbol": "CAKETOOLS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "avl-avalon", - "name": "Avalon", - "symbol": "AVL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "busdzilla-busdzilla", - "name": "BUSDzilla", - "symbol": "BUSDZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bnbmetaverse-bnb-metaverse-token", - "name": "BNB Metaverse Token", - "symbol": "BNBMETAVER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uwf-uwf-token", - "name": "UWF Token", - "symbol": "UWF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bull-bullverse", - "name": "BullVerse", - "symbol": "BULL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jwl-jewels", - "name": "Jewels", - "symbol": "JWL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "anti-antibitcoin", - "name": "AntiBitcoin", - "symbol": "ANTI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cto-crypto", - "name": "Crypto", - "symbol": "CTO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sac-sacoin", - "name": "SACoin", - "symbol": "SAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "frk-franko", - "name": "Franko", - "symbol": "FRK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wmc-wmcoin", - "name": "WMCoin", - "symbol": "WMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "plastik-plastiks", - "name": "Plastiks", - "symbol": "PLASTIK", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "sling-sling", - "name": "Sling", - "symbol": "SLING", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "isl-islacoin", - "name": "IslaCoin", - "symbol": "ISL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "glc-globalcoin", - "name": "GlobalCoin", - "symbol": "GLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vuc-virta-unique-coin", - "name": "Virta Unique Coin", - "symbol": "VUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zns-zeronauts", - "name": "Zeronauts", - "symbol": "ZNS", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "btq-bitquark", - "name": "BitQuark", - "symbol": "BTQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cash-cashcoin", - "name": "Cashcoin", - "symbol": "CASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "scrt-secretcoin", - "name": "SecretCoin", - "symbol": "SCRT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cyp-cypher", - "name": "Cypher", - "symbol": "CYP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vot-votecoin", - "name": "VoteCoin", - "symbol": "VOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "j-joincoin", - "name": "Joincoin", - "symbol": "J", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rbies-rubies", - "name": "Rubies", - "symbol": "RBIES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tsuki-tsuki-finance", - "name": "Tsuki Finance", - "symbol": "TSUKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bscpay-binance-smart-chain-payment-token", - "name": "Binance Smart Chain Payment Token", - "symbol": "BSCPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "city-manchester-city-fan-token", - "name": "Manchester City Fan Token", - "symbol": "CITY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "niglo-niglo", - "name": "NIGLO", - "symbol": "NIGLO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bitz-bitz", - "name": "Bitz", - "symbol": "BITZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ttet-ttet", - "name": "TTET", - "symbol": "TTET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xrm-xreim", - "name": "Xreim", - "symbol": "XRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sssss-sssss", - "name": "SSSSS", - "symbol": "SSSSS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cam-camel-token", - "name": "Camel Token", - "symbol": "CAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metazuckzilla-metazuckzillan", - "name": "MetaZuckZillan", - "symbol": "METAZUCKZI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fcoin-future-coin", - "name": "Future Coin", - "symbol": "FCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "greddydoge-greddy-doge", - "name": "Greddy Doge", - "symbol": "GREDDYDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cowc-cow-coin", - "name": "Cow Coin", - "symbol": "COWC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "way-wayguide", - "name": "WayGuide", - "symbol": "WAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "doges-dogestrong", - "name": "DogeStrong", - "symbol": "DOGES", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fdt-flokidogeteam", - "name": "FlokiDogeTeam", - "symbol": "FDT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dev88-dev-88", - "name": "Dev 88", - "symbol": "DEV88", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "moreshiba-moreshiba", - "name": "MORESHIBA", - "symbol": "MORESHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "supbnb-superbnb", - "name": "SuperBNB", - "symbol": "SUPBNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mnft-meta-nft", - "name": "META-NFT", - "symbol": "MNFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cyborg-cyborgs", - "name": "Cyborgs", - "symbol": "CYBORG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "atest-angrytest", - "name": "AngryTest", - "symbol": "ATEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nw-netwarscoin", - "name": "NetWarsCoin", - "symbol": "NW", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bog-bogdanoff", - "name": "Bogdanoff", - "symbol": "BOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "star-starlight", - "name": "StarLight", - "symbol": "STAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bscpay-binance-smartchain-payment-token", - "name": "Binance SmartChain Payment Token", - "symbol": "BSCPAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "test-test-token", - "name": "TEST Token", - "symbol": "TEST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "scamkillertoken-scamkillertoken", - "name": "ScamKillerToken", - "symbol": "SCAMKILLER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "minigirldoge-minigirldoge", - "name": "MiniGirlDoge", - "symbol": "MINIGIRLDO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "babypom-baby-pomeranian", - "name": "Baby Pomeranian", - "symbol": "BABYPOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kiki-kiki", - "name": "KIKI", - "symbol": "KIKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cash-moneymaker", - "name": "MoneyMaker", - "symbol": "CASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "fimk-fimkrypto", - "name": "FIMKrypto", - "symbol": "FIMK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mintea-mintea", - "name": "Mintea", - "symbol": "MINTEA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "spacegirl-spacegirl", - "name": "SpaceGirl", - "symbol": "SPACEGIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sts-stress", - "name": "Stress", - "symbol": "STS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btk-bluetek", - "name": "BlueTek", - "symbol": "BTK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "t2-test2", - "name": "Test2", - "symbol": "T2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "testr-tester", - "name": "TESTER", - "symbol": "TESTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "catezilla-catezilla", - "name": "CATEZILLA", - "symbol": "CATEZILLA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "zaier-zaier", - "name": "ZAIER", - "symbol": "ZAIER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "metainus-metainus", - "name": "MetaInus", - "symbol": "METAINUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kitsuneinu-kitsune-inu", - "name": "Kitsune Inu", - "symbol": "KITSUNEINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "lol-leaguecoin", - "name": "LeagueCoin", - "symbol": "LOL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "yomi-yomi", - "name": "Yomi", - "symbol": "YOMI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "axs-dogekinginuelon", - "name": "DogeKingInuElon", - "symbol": "AXS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kpsc-kirilpussycoin", - "name": "KirilPussyCoin", - "symbol": "KPSC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "uni-universe", - "name": "Universe", - "symbol": "UNI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cv2-colossuscoin-v2", - "name": "Colossuscoin V2", - "symbol": "CV2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mrty-mortydefi", - "name": "MortyDefi", - "symbol": "MRTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "pasl-pascal-lite", - "name": "Pascal Lite", - "symbol": "PASL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "metal-metalcoin", - "name": "MetalCoin", - "symbol": "METAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "trk-truckcoin", - "name": "Truckcoin", - "symbol": "TRK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vade-vade", - "name": "VADE", - "symbol": "VADE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "usc-ultimate-secure-cash", - "name": "Ultimate Secure Cash", - "symbol": "USC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brit-britcoin", - "name": "BritCoin", - "symbol": "BRIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "val-vikings-finance", - "name": "Vikings Finance", - "symbol": "VAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btcr-bitcurrency", - "name": "Bitcurrency", - "symbol": "BTCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sqdg-squid-game", - "name": "Squid Game", - "symbol": "SQDG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "avamim-ava-mim", - "name": "Ava MIM", - "symbol": "AVAMIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sbc-superbrain-capital-dao", - "name": "SuperBrain Capital Dao", - "symbol": "$SBC", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "shorty-shorty", - "name": "Shorty", - "symbol": "SHORTY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yash-yashcoin", - "name": "YashCoin", - "symbol": "YASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sod-son-of-doge", - "name": "Son of Doge", - "symbol": "SOD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crypt-cryptcoin", - "name": "CryptCoin", - "symbol": "CRYPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sdc-shadowcash", - "name": "ShadowCash", - "symbol": "SDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vta-virtacoin", - "name": "Virtacoin", - "symbol": "VTA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "abn-abncoin", - "name": "Abncoin", - "symbol": "ABN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ebt-ebittree-coin", - "name": "Ebittree Coin", - "symbol": "EBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dmb-digital-money-bits", - "name": "Digital Money Bits", - "symbol": "DMB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dgcs-digital-credits", - "name": "Digital Credits", - "symbol": "DGCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tstr-tristar-coin", - "name": "Tristar Coin", - "symbol": "TSTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gsr-geysercoin", - "name": "GeyserCoin", - "symbol": "GSR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "calc-caliphcoin", - "name": "CaliphCoin", - "symbol": "CALC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mgm-magnum", - "name": "Magnum", - "symbol": "MGM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xrc-rawcoin", - "name": "Rawcoin", - "symbol": "XRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "oracle-oracle-protocol", - "name": "Oracle Protocol", - "symbol": "ORACLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ula-ulatech", - "name": "Ulatech", - "symbol": "ULA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "athena-athena", - "name": "Athena", - "symbol": "ATHENA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "dachs-dachshund", - "name": "Dachshund", - "symbol": "DACHS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "floki10xgirl-floki10xgirl", - "name": "Floki10xGirl", - "symbol": "FLOKI10XGI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "telexfree-telexfree", - "name": "TelexFree", - "symbol": "TELEXFREE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "ushiba-ultra-shiba-inu", - "name": "Ultra Shiba Inu", - "symbol": "USHIBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sminu-supermeta-inu", - "name": "SuperMeta Inu", - "symbol": "SMINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "colligotokens-colligotokens", - "name": "ColligoTokens", - "symbol": "COLLIGOTOK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mxinu-matrix-inu", - "name": "Matrix Inu", - "symbol": "MXINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kinu-kitsune-inu", - "name": "Kitsune Inu", - "symbol": "KINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mfox-magicfox-swap", - "name": "MagicFox Swap", - "symbol": "MFOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cb-clashboy", - "name": "ClashBoy", - "symbol": "CB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "br-babyrose", - "name": "BabyRose", - "symbol": "BR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "rocketverse-rocketverse-finance", - "name": "Rocketverse Finance", - "symbol": "ROCKETVERS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nanox-project-x", - "name": "Project-X", - "symbol": "NANOX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wegg-wrapped-nestegg-coin", - "name": "Wrapped NestEGG Coin", - "symbol": "WEGG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "cab-cabbage", - "name": "Cabbage", - "symbol": "CAB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ctena-ctena-finance", - "name": "CTENA Finance", - "symbol": "CTENA", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "lvps-levoplus", - "name": "LevoPlus", - "symbol": "LVPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "coupe-coupecoin", - "name": "Coupecoin", - "symbol": "COUPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "els-elysium", - "name": "Elysium", - "symbol": "ELS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "argus-argus", - "name": "Argus", - "symbol": "ARGUS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "volt-bitvolt", - "name": "Bitvolt", - "symbol": "VOLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jobs-jobscoin", - "name": "JobsCoin", - "symbol": "JOBS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "solace-solace", - "name": "SOLACE", - "symbol": "SOLACE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crdnc-credence-coin", - "name": "Credence Coin", - "symbol": "CRDNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "prc-prcoin", - "name": "PRCoin", - "symbol": "PRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "scs-speedcash", - "name": "SpeedCash", - "symbol": "SCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vltc-vault-coin", - "name": "Vault Coin", - "symbol": "VLTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "adcn-asiadigicoin", - "name": "Asiadigicoin", - "symbol": "ADCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "geert-geertcoin", - "name": "GeertCoin", - "symbol": "GEERT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "steps-steps", - "name": "Steps", - "symbol": "STEPS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ride-ride-my-car", - "name": "Ride My Car", - "symbol": "RIDE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "placo-playercoin", - "name": "PlayerCoin", - "symbol": "PLACO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xcs-cybcsec", - "name": "CybCSec", - "symbol": "XCS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "prx-printerium", - "name": "Printerium", - "symbol": "PRX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qbc-quebecoin", - "name": "Quebecoin", - "symbol": "QBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "atx-artex-coin", - "name": "Artex Coin", - "symbol": "ATX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tycho-tychocoin", - "name": "Tychocoin", - "symbol": "TYCHO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dlisk-dappster", - "name": "DAPPSTER", - "symbol": "DLISK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "icon-iconic", - "name": "Iconic", - "symbol": "ICON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "arb-arbit", - "name": "ARbit", - "symbol": "ARB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "token-swaptoken", - "name": "SwapToken", - "symbol": "TOKEN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "roofs-roofs", - "name": "Roofs", - "symbol": "ROOFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "usde-usde", - "name": "USDe", - "symbol": "USDE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cf-californium", - "name": "Californium", - "symbol": "CF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mscn-master-swiscoin", - "name": "Master Swiscoin", - "symbol": "MSCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vprc-vaperscoin", - "name": "VapersCoin", - "symbol": "VPRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "worm-healthywormcoin", - "name": "HealthyWormCoin", - "symbol": "WORM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "skc-skeincoin", - "name": "Skeincoin", - "symbol": "SKC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "boat-boat", - "name": "BOAT", - "symbol": "BOAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dbtc-debitcoin", - "name": "Debitcoin", - "symbol": "DBTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "slevin-slevin", - "name": "Slevin", - "symbol": "SLEVIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zmc-zetamicron", - "name": "ZetaMicron", - "symbol": "ZMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "acp-anarchistsprime", - "name": "AnarchistsPrime", - "symbol": "ACP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lea-leacoin", - "name": "LeaCoin", - "symbol": "LEA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tor-torcoin", - "name": "Torcoin", - "symbol": "TOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xbtc21-bitcoin-21", - "name": "Bitcoin 21", - "symbol": "XBTC21", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "may-theresa-may-coin", - "name": "Theresa May Coin", - "symbol": "MAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "spt-spots", - "name": "Spots", - "symbol": "SPT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "aerm-aerium", - "name": "Aerium", - "symbol": "AERM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rpc-ronpaulcoin", - "name": "RonPaulCoin", - "symbol": "RPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eco-ecocoin", - "name": "EcoCoin", - "symbol": "ECO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bnx-bnrtxcoin", - "name": "BnrtxCoin", - "symbol": "BNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gtc-global-tour-coin", - "name": "Global Tour Coin", - "symbol": "GTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "off-cthulhu-offerings", - "name": "Cthulhu Offerings", - "symbol": "OFF", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nro-neuro", - "name": "Neuro", - "symbol": "NRO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "leno-lenocore", - "name": "LenoCore", - "symbol": "LENO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ctic2-coimatic-20", - "name": "Coimatic 2.0", - "symbol": "CTIC2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "flax-flaxscript", - "name": "Flaxscript", - "symbol": "FLAX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xbts-beatcoin", - "name": "Beatcoin", - "symbol": "XBTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "imx-impact", - "name": "Impact", - "symbol": "IMX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ery-eryllium", - "name": "Eryllium", - "symbol": "ERY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bip-bipcoin", - "name": "BipCoin", - "symbol": "BIP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vc-virtualcoin", - "name": "VirtualCoin", - "symbol": "VC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yac-yacoin", - "name": "Yacoin", - "symbol": "YAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ree-reecoin", - "name": "ReeCoin", - "symbol": "REE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gp-goldpieces", - "name": "GoldPieces", - "symbol": "GP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gpl-gold-pressed-latinum", - "name": "Gold Pressed Latinum", - "symbol": "GPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sfc-solarflarecoin", - "name": "Solarflarecoin", - "symbol": "SFC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "poki-polyfloki", - "name": "PolyFLOKI", - "symbol": "POKI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "drs-digital-rupees", - "name": "Digital Rupees", - "symbol": "DRS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nlx-nullex", - "name": "Nullex", - "symbol": "NLX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bria-briacoin", - "name": "BriaCoin", - "symbol": "BRIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "finu-fetch-inu", - "name": "Fetch Inu", - "symbol": "FINU", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ama-ask-me-anything", - "name": "Ask Me Anything", - "symbol": "AMA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "soil-soilcoin", - "name": "SOILcoin", - "symbol": "SOIL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "krone-kronecoin", - "name": "Kronecoin", - "symbol": "KRONE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qcn-quazarcoin", - "name": "QuazarCoin", - "symbol": "QCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mao-mao-zedong", - "name": "Mao Zedong", - "symbol": "MAO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xvp-virtacoinplus", - "name": "Virtacoinplus", - "symbol": "XVP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brains-brainiac", - "name": "Brainiac", - "symbol": "BRAINS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hwxt-howlx", - "name": "Howlx", - "symbol": "HWXT", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "honey-honey", - "name": "Honey", - "symbol": "HONEY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "safedoge-safedogecoin", - "name": "SafeDogecoin", - "symbol": "SAFEDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "jin-jin-coin", - "name": "Jin Coin", - "symbol": "JIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "con-paycon", - "name": "PayCon", - "symbol": "CON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mnc-mincoin", - "name": "Mincoin", - "symbol": "MNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btg-bitgem", - "name": "Bitgem", - "symbol": "BTG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fuzz-fuzzballs", - "name": "FuzzBalls", - "symbol": "FUZZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bumba-bumbacoin", - "name": "BumbaCoin", - "symbol": "BUMBA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pxi-prime-xi", - "name": "Prime-XI", - "symbol": "PXI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cach-cachecoin", - "name": "CacheCoin", - "symbol": "CACH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cpn-compucoin", - "name": "CompuCoin", - "symbol": "CPN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cxt-coinonat", - "name": "Coinonat", - "symbol": "CXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "src-securecoin", - "name": "SecureCoin", - "symbol": "SRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mar-marijuanacoin", - "name": "Marijuanacoin", - "symbol": "MAR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cat-catcoin", - "name": "Catcoin", - "symbol": "CAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "space-spacecoin", - "name": "SpaceCoin", - "symbol": "SPACE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zur-zurcoin", - "name": "Zurcoin", - "symbol": "ZUR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xre-revolvercoin", - "name": "RevolverCoin", - "symbol": "XRE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fly-flycoin", - "name": "Flycoin", - "symbol": "FLY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "euc-eurocoin", - "name": "Eurocoin", - "symbol": "EUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "soon-sooncoin", - "name": "SoonCoin", - "symbol": "SOON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "duo-parallelcoin", - "name": "ParallelCoin", - "symbol": "DUO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kayi-kayicoin", - "name": "Kayicoin", - "symbol": "KAYI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tag-tagcoin", - "name": "TagCoin", - "symbol": "TAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mad-satoshimadness", - "name": "SatoshiMadness", - "symbol": "MAD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "611-sixeleven", - "name": "SixEleven", - "symbol": "611", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "qtl-quatloo", - "name": "Quatloo", - "symbol": "QTL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ked-darsek", - "name": "Darsek", - "symbol": "KED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "px-px", - "name": "PX", - "symbol": "PX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zzc-zozocoin", - "name": "ZoZoCoin", - "symbol": "ZZC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pcoin-pioneer-coin", - "name": "Pioneer Coin", - "symbol": "PCOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "coal-bitcoal", - "name": "BitCoal", - "symbol": "COAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sperot-sperotoken", - "name": "SperoToken", - "symbol": "SPEROT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "sfz-safemoon-zilla", - "name": "SafeMoon Zilla", - "symbol": "SFZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "btpl-bitcoin-planet", - "name": "Bitcoin Planet", - "symbol": "BTPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cno-coino", - "name": "Coin(O)", - "symbol": "CNO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xgr-goldreserve", - "name": "GoldReserve", - "symbol": "XGR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "copi-cornucopias", - "name": "Cornucopias", - "symbol": "COPI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "slg-sterlingcoin", - "name": "Sterlingcoin", - "symbol": "SLG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "arg-argentum", - "name": "Argentum", - "symbol": "ARG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "abtm-alphabatem", - "name": "AlphaBatem", - "symbol": "ABTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "tgc-tigercoin", - "name": "Tigercoin", - "symbol": "TGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bigup-bigup", - "name": "BigUp", - "symbol": "BIGUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "chan-chancoin", - "name": "ChanCoin", - "symbol": "CHAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "akl-akil-coin", - "name": "Akil Coin", - "symbol": "AKL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "808-808coin", - "name": "808Coin", - "symbol": "808", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xra-ratecoin", - "name": "Ratecoin", - "symbol": "XRA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "post-postcoin", - "name": "PostCoin", - "symbol": "POST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xpy-paycoin", - "name": "PayCoin", - "symbol": "XPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fnc-fincoin", - "name": "FinCoin", - "symbol": "FNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pak-pakcoin", - "name": "Pakcoin", - "symbol": "PAK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "phs-philosopher-stones", - "name": "Philosopher Stones", - "symbol": "PHS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tse-tattoocoin-standard-edition", - "name": "Tattoocoin (Standard Edition)", - "symbol": "TSE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "c2-coin21", - "name": "Coin2.1", - "symbol": "C2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shibabank-shiba-bank", - "name": "Shiba Bank", - "symbol": "SHIBABANK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "super-supercoin", - "name": "SuperCoin", - "symbol": "SUPER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pr-prototanium", - "name": "Prototanium", - "symbol": "PR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "unic-unicoin", - "name": "UniCoin", - "symbol": "UNIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "888-octocoin", - "name": "OctoCoin", - "symbol": "888", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xhi-hicoin", - "name": "HiCoin", - "symbol": "XHI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crx-chronos", - "name": "Chronos", - "symbol": "CRX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "red-redcoin", - "name": "RedCoin", - "symbol": "RED", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hangry-hangrybirds", - "name": "HangryBirds", - "symbol": "HANGRY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mojo-mojocoin", - "name": "MojoCoin", - "symbol": "MOJO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "8bit-8bit", - "name": "8Bit", - "symbol": "8BIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kush-kushcoin", - "name": "KushCoin", - "symbol": "KUSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "amber-ambercoin", - "name": "AmberCoin", - "symbol": "AMBER", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "blc-blakecoin", - "name": "Blakecoin", - "symbol": "BLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mcrn-macron", - "name": "MACRON", - "symbol": "MCRN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hbn-hobonickels", - "name": "HoboNickels", - "symbol": "HBN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xjo-joulecoin", - "name": "Joulecoin", - "symbol": "XJO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "atom-atomic-coin", - "name": "Atomic Coin", - "symbol": "ATOM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ethd-ethereum-dark", - "name": "Ethereum Dark", - "symbol": "ETHD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "grim-grimcoin", - "name": "Grimcoin", - "symbol": "GRIM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "acc-adcoin", - "name": "AdCoin", - "symbol": "ACC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cade-crypcade-shares", - "name": "CrypCade Shares", - "symbol": "CADE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xptx-platinumbar", - "name": "PlatinumBAR", - "symbol": "XPTX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cube-digicube", - "name": "DigiCube", - "symbol": "CUBE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "whl-whalecoin", - "name": "WhaleCoin", - "symbol": "WHL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ccn-cannacoin", - "name": "CannaCoin", - "symbol": "CCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hpc-happycoin", - "name": "Happycoin", - "symbol": "HPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "stars-starcash-network", - "name": "StarCash Network", - "symbol": "STARS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "trump-trumpcoin", - "name": "TrumpCoin", - "symbol": "TRUMP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mdc-madcoin", - "name": "Madcoin", - "symbol": "MDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sqc-squoge-coin", - "name": "Squoge Coin", - "symbol": "SQC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "nyan-nyancoin", - "name": "Nyancoin", - "symbol": "NYAN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dsh-dashcoin", - "name": "Dashcoin", - "symbol": "DSH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cj-cryptojacks", - "name": "Cryptojacks", - "symbol": "CJ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ams-amsterdamcoin", - "name": "AmsterdamCoin", - "symbol": "AMS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "drxne-droxne", - "name": "DROXNE", - "symbol": "DRXNE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tri-triangles", - "name": "Triangles", - "symbol": "TRI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ari-aricoin", - "name": "Aricoin", - "symbol": "ARI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "iti-iticoin", - "name": "iTicoin", - "symbol": "ITI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xcxt-coinonatx", - "name": "CoinonatX", - "symbol": "XCXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "el-elcoin", - "name": "Elcoin", - "symbol": "EL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gaia-gaia", - "name": "GAIA", - "symbol": "GAIA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kobo-kobocoin", - "name": "Kobocoin", - "symbol": "KOBO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fcn-fantomcoin", - "name": "Fantomcoin", - "symbol": "FCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "daxx-daxxcoin", - "name": "DaxxCoin", - "symbol": "DAXX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "kurt-kurrent", - "name": "Kurrent", - "symbol": "KURT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bun-bunnycoin", - "name": "BunnyCoin", - "symbol": "BUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "piggy-piggycoin", - "name": "Piggycoin", - "symbol": "PIGGY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hodl-hodlcoin", - "name": "HOdlcoin", - "symbol": "HODL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ent-eternity", - "name": "Eternity", - "symbol": "ENT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ammo-ammo-reloaded", - "name": "Ammo Reloaded", - "symbol": "AMMO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "log-woodcoin", - "name": "Woodcoin", - "symbol": "LOG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "utc-ultracoin", - "name": "UltraCoin", - "symbol": "UTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "q2c-qubitcoin", - "name": "QubitCoin", - "symbol": "Q2C", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "albc-albicoin", - "name": "Albicoin", - "symbol": "ALBC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xct-c-bit", - "name": "C-Bit", - "symbol": "XCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "troll-trollcoin", - "name": "Trollcoin", - "symbol": "TROLL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tdoge-tesla-doge", - "name": "Tesla Doge", - "symbol": "TDOGE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "smc-smartcoin", - "name": "SmartCoin", - "symbol": "SMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gun-guncoin", - "name": "Guncoin", - "symbol": "GUN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fc2-fuelcoin", - "name": "FuelCoin", - "symbol": "FC2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dfs-dfscoin", - "name": "DFSCoin", - "symbol": "DFS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "i0c-i0coin", - "name": "I0Coin", - "symbol": "I0C", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zeni-zennies", - "name": "Zennies", - "symbol": "ZENI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "grwi-growers-international", - "name": "GrowersCoin", - "symbol": "GRWI", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "era-era", - "name": "ERA", - "symbol": "ERA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "htc-hitcoin", - "name": "HitCoin", - "symbol": "HTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "stn-steneum-coin", - "name": "Steneum Coin", - "symbol": "STN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "orb-orbitcoin", - "name": "Orbitcoin", - "symbol": "ORB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ccrb-cryptocarbon", - "name": "CryptoCarbon", - "symbol": "CCRB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "saga-sagacoin", - "name": "SagaCoin", - "symbol": "SAGA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vidz-purevidz", - "name": "PureVidz", - "symbol": "VIDZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ttc-tittiecoin", - "name": "TittieCoin", - "symbol": "TIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nka-incakoin", - "name": "IncaKoin", - "symbol": "NKA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "uis-unitus", - "name": "Unitus", - "symbol": "UIS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "infx-influxcoin", - "name": "Influxcoin", - "symbol": "INFX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "unb-unbreakablecoin", - "name": "UnbreakableCoin", - "symbol": "UNB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fst-fastcoin", - "name": "Fastcoin", - "symbol": "FST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "opc-op-coin", - "name": "OP Coin", - "symbol": "OPC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vrtc-virtio", - "name": "VIRTIO", - "symbol": "VRTC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "mac-machinecoin", - "name": "Machinecoin", - "symbol": "MAC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dsr-desire", - "name": "Desire", - "symbol": "DSR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xcn-cryptonite", - "name": "Cryptonite", - "symbol": "XCN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "au-aurumcoin", - "name": "Aurum Coin", - "symbol": "AU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "byc-bytecent", - "name": "Bytecent", - "symbol": "BYC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xcpo-copico", - "name": "Copico", - "symbol": "XCPO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "net-netcoin", - "name": "NetCoin", - "symbol": "NET", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "adz-adzcoin", - "name": "Adzcoin", - "symbol": "ADZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "heroes-heroes-of-crypton", - "name": "Heroes of Crypton", - "symbol": "HEROES", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "cnt-centurion", - "name": "Centurion", - "symbol": "CNT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "abjc-abjcoin-commerce", - "name": "Abjcoin Commerce", - "symbol": "ABJC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dcy-dinastycoin", - "name": "Dinastycoin", - "symbol": "DCY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "score-scorecoin", - "name": "Scorecoin", - "symbol": "SCORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dft-draftcoin", - "name": "DraftCoin", - "symbol": "DFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bash-luckchain", - "name": "LuckChain", - "symbol": "BASH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "insn-insanecoin", - "name": "InsaneCoin", - "symbol": "INSN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "moin-moin", - "name": "Moin", - "symbol": "MOIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rns-renos", - "name": "Renos", - "symbol": "RNS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hal-halcyon", - "name": "Halcyon", - "symbol": "HAL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "unify-unify", - "name": "Unify", - "symbol": "UNIFY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cmpco-campuscoin", - "name": "CampusCoin", - "symbol": "CC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "wdc-worldcoin", - "name": "WorldCoin", - "symbol": "WDC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "flt-fluttercoin", - "name": "FlutterCoin", - "symbol": "FLT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pop-popularcoin", - "name": "PopularCoin", - "symbol": "POP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "yoc-yocoin", - "name": "Yocoin", - "symbol": "YOC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "linx-linx", - "name": "Linx", - "symbol": "LINX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "jungle-metajungle", - "name": "MetaJungle", - "symbol": "JUNGLE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gcc-global-cryptocurrency", - "name": "Global Cryptocurrency", - "symbol": "GCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "anc-anoncoin", - "name": "Anoncoin", - "symbol": "ANC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dp-digitalprice", - "name": "DigitalPrice", - "symbol": "DP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pkb-parkbyte", - "name": "ParkByte", - "symbol": "PKB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fans-fantasy-cash", - "name": "Fantasy Cash", - "symbol": "FANS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "huc-huntercoin", - "name": "HunterCoin", - "symbol": "HUC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "v-version", - "name": "Version", - "symbol": "V", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "for-force", - "name": "FORCE", - "symbol": "FOR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dbd-day-by-day", - "name": "Day By Day", - "symbol": "DBD", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "blu-bluecoin", - "name": "BlueCoin", - "symbol": "BLU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zeit-zeitcoin", - "name": "Zeitcoin", - "symbol": "ZEIT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hold-stellar-holdings", - "name": "Stellar Holdings", - "symbol": "HOLD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rain-condensate", - "name": "Condensate", - "symbol": "RAIN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zeph-zephyr", - "name": "Zephyr", - "symbol": "ZEPH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xmg-magi", - "name": "Magi", - "symbol": "XMG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mxt-martexcoin", - "name": "MarteXcoin", - "symbol": "MXT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "egc-evergreencoin", - "name": "EverGreenCoin", - "symbol": "EGC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ebst-eboost", - "name": "eBoost", - "symbol": "EBST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ixc-ixcoin", - "name": "Ixcoin", - "symbol": "IXC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "eqt-equitrader", - "name": "EquiTrader", - "symbol": "EQT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "trust-trustplus", - "name": "TrustPlus", - "symbol": "TRUST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "1337-elite", - "name": "Elite", - "symbol": "1337", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "proc-procurrency", - "name": "ProCurrency", - "symbol": "PROC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "carbon-carboncoin", - "name": "Carboncoin", - "symbol": "CARBON", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vtr-vtorrent", - "name": "vTorrent", - "symbol": "VTR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bci-baby-cheems-inu", - "name": "Baby Cheems Inu", - "symbol": "BCI", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "erc-europecoin", - "name": "EuropeCoin", - "symbol": "ERC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "note-dnotes", - "name": "DNotes", - "symbol": "NOTE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "hwc-hollywoodcoin", - "name": "HollyWoodCoin", - "symbol": "HWC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "buzz-buzzcoin", - "name": "BUZZCoin", - "symbol": "BUZZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "toa-toacoin", - "name": "ToaCoin", - "symbol": "TOA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nobl-noblecoin", - "name": "NobleCoin", - "symbol": "NOBL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pure-pure", - "name": "Pure", - "symbol": "PURE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "meyers-meyers-token", - "name": "Meyers Token", - "symbol": "MEYERS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kfr-king-forever", - "name": "KING FOREVER", - "symbol": "KFR", - "rank": 0, - "is_new": false, - "is_active": true, - "type": "token" - }, - { - "id": "ael-spantale", - "name": "Spantale", - "symbol": "AEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "crea-creativecoin", - "name": "CREA", - "symbol": "CREA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "klc-kilocoin", - "name": "KiloCoin", - "symbol": "KLC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rup-rupee", - "name": "Rupee", - "symbol": "RUP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bpl-blockpool", - "name": "Blockpool", - "symbol": "BPL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "2give-2give", - "name": "2GIVE", - "symbol": "2GIVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crave-crave", - "name": "Crave", - "symbol": "CRAVE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cnc-cornatto", - "name": "Cornatto", - "symbol": "CNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "gretinu-greta-inu", - "name": "Greta INU", - "symbol": "GRETINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "kore-kore", - "name": "Kore", - "symbol": "KORE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brx-breakout-stake", - "name": "Breakout Stake", - "symbol": "BRX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mcc-macacacoin", - "name": "MacacaCoin", - "symbol": "MCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "blitz-blitzcash", - "name": "Blitzcash", - "symbol": "BLITZ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vivo-vivo", - "name": "VIVO", - "symbol": "VIVO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "cann-cannabiscoin", - "name": "CannabisCoin", - "symbol": "CANN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "chc-chaincoin", - "name": "ChainCoin", - "symbol": "CHC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dot-dotcoin", - "name": "Dotcoin", - "symbol": "DOT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "hyp-element", - "name": "Element", - "symbol": "HYP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mag-magnet", - "name": "Magnet", - "symbol": "MAG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brk-breakout", - "name": "Breakout", - "symbol": "BRK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mtnc-masternodecoin", - "name": "Masternodecoin", - "symbol": "MTNC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "zny-bitzeny", - "name": "Bitzeny", - "symbol": "ZNY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "tx-transfercoin", - "name": "TransferCoin", - "symbol": "TX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dogx-vtx-inu", - "name": "VTX INU", - "symbol": "DOGX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "usnbt-nubits", - "name": "NuBits", - "symbol": "USNBT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "crc-crowdcoin", - "name": "CrowdCoin", - "symbol": "CRC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vrm-veriumreserve", - "name": "VeriumReserve", - "symbol": "VRM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dope-dopecoin", - "name": "DopeCoin", - "symbol": "DOPE", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xto-tao", - "name": "Tao", - "symbol": "TAO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btm-bitmark", - "name": "Bitmark", - "symbol": "BTM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "atmos-atmos", - "name": "Atmos", - "symbol": "ATMOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "junkoinu-junko-inu", - "name": "Junko Inu", - "symbol": "JUNKOINU", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "bdl-bitdeal", - "name": "Bitdeal", - "symbol": "BDL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "vsx-vsync", - "name": "Vsync", - "symbol": "VSX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "sprts-sprouts", - "name": "Sprouts", - "symbol": "SPRTS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "adc-audiocoin", - "name": "AudioCoin", - "symbol": "ADC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rec-regalcoin", - "name": "Regalcoin", - "symbol": "REC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rmc-russian-miner-coin", - "name": "Russian Miner Coin", - "symbol": "RMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "esp-espers", - "name": "Espers", - "symbol": "ESP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bwk-bulwark", - "name": "Bulwark", - "symbol": "BWK", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ac-asiacoin", - "name": "AsiaCoin", - "symbol": "AC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "frst-firstcoin", - "name": "FirstCoin", - "symbol": "FRST", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bbr-boolberry", - "name": "Boolberry", - "symbol": "BBR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gcr-global-currency-reserve", - "name": "Global Currency Reserve", - "symbol": "GCR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "iop-internet-of-people", - "name": "Internet of People", - "symbol": "IOP", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pirl-pirl", - "name": "Pirl", - "symbol": "PIRL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "3crv-lp-3pool-curve", - "name": "LP 3pool Curve", - "symbol": "3CRV", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "xmcc-monoeci", - "name": "Monoeci", - "symbol": "XMCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "brah-inversebrah", - "name": "InverseBrah", - "symbol": "BRAH", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "heat-heat", - "name": "HEAT", - "symbol": "HEAT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "gam-gambit", - "name": "Gambit", - "symbol": "GAM", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "seq-sequence", - "name": "Sequence", - "symbol": "SEQ", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rc-russiacoin", - "name": "RussiaCoin", - "symbol": "RC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "music-musicoin", - "name": "Musicoin", - "symbol": "MUSIC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "enrg-energycoin", - "name": "Energycoin", - "symbol": "ENRG", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "lmc-lomocoin", - "name": "LoMoCoin", - "symbol": "LMC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "oxy-oxycoin", - "name": "Oxycoin", - "symbol": "OXY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "token" - }, - { - "id": "odn-obsidian", - "name": "Obsidian", - "symbol": "ODN", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "leo-leocoin", - "name": "LEOcoin", - "symbol": "LEO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "atb-atbcoin", - "name": "ATBCoin", - "symbol": "ATB", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "neos-neoscoin", - "name": "NeosCoin", - "symbol": "NEOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dbix-dubaicoin", - "name": "DubaiCoin", - "symbol": "DBIX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "golos-golos", - "name": "Golos", - "symbol": "GOLOS", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pnd-pandacoin", - "name": "Pandacoin", - "symbol": "PND", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "nlc2-nolimitcoin", - "name": "NoLimitCoin", - "symbol": "NLC2", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "ppy-peerplays", - "name": "Peerplays", - "symbol": "PPY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "fair-faircoin", - "name": "FairCoin", - "symbol": "FAIR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mvg-mad-viking-games", - "name": "Mad Viking Games", - "symbol": "MVG", - "rank": 0, - "is_new": true, - "is_active": true, - "type": "token" - }, - { - "id": "xel-elastic", - "name": "XEL", - "symbol": "XEL", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "slr-solarcoin", - "name": "SolarCoin", - "symbol": "SLR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "shift-shift", - "name": "Shift", - "symbol": "SHIFT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "pura-pura", - "name": "Pura", - "symbol": "PURA", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "rvr-revolutionvr", - "name": "RevolutionVR", - "symbol": "RVR", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "mnx-minexcoin", - "name": "MinexCoin", - "symbol": "MNX", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "dct-decent", - "name": "DECENT", - "symbol": "DCT", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bay-bitbay", - "name": "BitBay", - "symbol": "BAY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "xby-xtrabytes", - "name": "XTRABYTES", - "symbol": "XBY", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bco-bridgecoin", - "name": "BridgeCoin", - "symbol": "BCO", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "btcd-bitcoindark", - "name": "BitcoinDark", - "symbol": "BTCD", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - }, - { - "id": "bcc-bitconnect", - "name": "BitConnect", - "symbol": "BCC", - "rank": 0, - "is_new": false, - "is_active": false, - "type": "coin" - } -] diff --git a/openbb_terminal/cryptocurrency/data/defillama_dapps.json b/openbb_terminal/cryptocurrency/data/defillama_dapps.json deleted file mode 100644 index f86c36012f3..00000000000 --- a/openbb_terminal/cryptocurrency/data/defillama_dapps.json +++ /dev/null @@ -1,3229 +0,0 @@ -{ - "binance-cex": "Binance CEX", - "lido": "Lido", - "okx": "OKX", - "bitfinex": "Bitfinex", - "makerdao": "MakerDAO", - "polygon-bridge-&-staking": "Polygon Bridge & Staking", - "wbtc": "WBTC", - "justlend": "JustLend", - "bybit": "Bybit", - "justcryptos": "JustCryptos", - "crypto-com": "Crypto-com", - "huobi": "Huobi", - "aave-v2": "AAVE V2", - "uniswap-v3": "Uniswap V3", - "summer.fi": "Summer.fi", - "curve-dex": "Curve DEX", - "coinbase-wrapped-staked-eth": "Coinbase Wrapped Staked ETH", - "aave-v3": "AAVE V3", - "kucoin": "Kucoin", - "convex-finance": "Convex Finance", - "instadapp": "Instadapp", - "gate-io": "Gate-io", - "deribit": "Deribit", - "rocket-pool": "Rocket Pool", - "bitget": "Bitget", - "juststables": "JustStables", - "compound": "Compound", - "uniswap-v2": "Uniswap V2", - "mexc": "MEXC", - "pancakeswap-amm": "PancakeSwap AMM", - "stusdt": "stUSDT", - "bitmex": "Bitmex", - "compound-v3": "Compound V3", - "liquity": "Liquity", - "venus": "Venus", - "balancer-v2": "Balancer V2", - "swissborg": "SwissBorg", - "gmx-v1": "GMX V1", - "spark": "Spark", - "frax-ether": "Frax Ether", - "synthetix": "Synthetix", - "yearn-finance": "Yearn Finance", - "sunswap-v1": "SUNSwap V1", - "maskex": "MaskEX", - "stargate": "Stargate", - "korbit": "Korbit", - "morpho-aave": "Morpho Aave", - "dydx": "dYdX", - "lybra-finance": "Lybra Finance", - "portal": "Portal", - "aura": "Aura", - "sushiswap": "SushiSwap", - "pancakeswap-amm-v3": "PancakeSwap AMM V3", - "bwatch": "Bwatch", - "nexo": "Nexo", - "eigenlayer": "EigenLayer", - "tornado-cash": "Tornado Cash", - "hbtc": "hBTC", - "nexus-mutual": "Nexus Mutual", - "uncx-network-v2": "UNCX Network V2", - "radiant-v2": "Radiant V2", - "coinwind": "Coinwind", - "velodrome-v2": "Velodrome V2", - "beefy": "Beefy", - "arrakis-v1": "Arrakis V1", - "stakewise": "StakeWise", - "pinksale": "PinkSale", - "chainge-finance": "Chainge Finance", - "xdai-stake-bridge": "xDAI Stake Bridge", - "ondo-finance": "Ondo Finance", - "binance-staked-eth": "Binance staked ETH", - "crvusd": "crvUSD", - "synapse": "Synapse", - "fraxlend": "Fraxlend", - "phemex": "Phemex", - "abracadabra": "Abracadabra", - "parallel-liquid-crowdloan": "Parallel Liquid Crowdloan", - "sunswap-v2": "SUNSwap V2", - "vvs-finance": "VVS Finance", - "pendle": "Pendle", - "lightning-network": "Lightning Network", - "tectonic": "Tectonic", - "stader": "Stader", - "orbit-bridge": "Orbit Bridge", - "marinade-finance": "Marinade Finance", - "acala-lcdot": "Acala LCDOT", - "stakehound": "StakeHound", - "multichain": "Multichain", - "thorchain": "Thorchain", - "firi": "Firi", - "benqi-lending": "Benqi Lending", - "defichain-dex": "DefiChain DEX", - "frax": "Frax", - "axelar": "Axelar", - "kava-lend": "Kava Lend", - "balancer-v1": "Balancer V1", - "loopring": "Loopring", - "osmosis-dex": "Osmosis DEX", - "morpho-aavev3": "Morpho AaveV3", - "pulsex-v1": "PulseX V1", - "team-finance": "Team Finance", - "cbridge": "cBridge", - "poly-network": "Poly Network", - "realt-tokens": "RealT Tokens", - "defisaver": "DefiSaver", - "okc-liquid-staking": "OKC Liquid Staking", - "mento": "Mento", - "woo-x": "WOO X", - "wemix.fi-staking": "WEMIX.FI Staking", - "defichain-loans": "DefiChain Loans", - "swell": "Swell", - "klayswap": "KlaySwap", - "gamma": "Gamma", - "ankr": "Ankr", - "kava-mint": "Kava Mint", - "origin-ether": "Origin Ether", - "matrixdock": "MatrixDock", - "benqi-staked-avax": "Benqi Staked Avax", - "blur-bids": "Blur Bids", - "cake-defi": "Cake DeFi", - "alpaca-leveraged-yield-farming": "Alpaca Leveraged Yield Farming", - "sushi-bentobox": "Sushi BentoBox", - "frax-swap": "Frax Swap", - "katana-dex": "Katana DEX", - "set-protocol": "Set Protocol", - "enzyme-finance": "Enzyme Finance", - "uwu-lend": "UwU Lend", - "hop-protocol": "Hop Protocol", - "biswap-v2": "BiSwap V2", - "flamincome": "Flamincome", - "syncswap": "SyncSwap", - "4swap": "4Swap", - "mux-protocol": "MUX Protocol", - "quickswap-dex": "Quickswap Dex", - "stakedao": "StakeDAO", - "pearlfi": "PearlFi", - "benddao-lending": "BendDAO Lending", - "tranchess-yield": "Tranchess Yield", - "homora-v2": "Homora V2", - "cian": "CIAN", - "across": "Across", - "joe-v2.1": "Joe V2.1", - "gravity-bridge": "Gravity Bridge", - "index-coop": "Index Coop", - "sun.io": "SUN.io", - "quickswap-v3": "Quickswap V3", - "camelot-v2": "Camelot V2", - "concentrator": "Concentrator", - "kyberswap-elastic": "KyberSwap Elastic", - "pando-leaf": "Pando Leaf", - "coinsquare": "Coinsquare", - "solend": "Solend", - "bancor-v3": "Bancor V3", - "inverse-finance-firm": "Inverse Finance FiRM", - "paraspace-lending": "ParaSpace Lending", - "flexa": "Flexa", - "maverick-protocol": "Maverick Protocol", - "galgo-liquid-governance": "gALGO Liquid Governance", - "silo-finance": "Silo Finance", - "gains-network": "Gains Network", - "kava-earn": "Kava Earn", - "kava-liquid": "Kava Liquid", - "minswap": "Minswap", - "raft": "Raft", - "maple-rwa": "Maple RWA", - "creth2": "CRETH2", - "moneyonchain": "MoneyOnChain", - "alchemix": "Alchemix", - "penpie": "Penpie", - "apollox": "ApolloX", - "hatom-lending": "Hatom Lending", - "helio-protocol": "Helio Protocol", - "dxsale": "DxSale", - "immutablex": "ImmutableX", - "orca": "Orca", - "flux-finance": "Flux Finance", - "xexchange": "xExchange", - "idle": "Idle", - "indigo": "Indigo", - "hatom-liquid-staking": "Hatom Liquid Staking", - "vires-finance": "Vires Finance", - "sonne-finance": "Sonne Finance", - "pnetwork": "pNetwork", - "eos-rex": "EOS REX", - "moonwell": "Moonwell", - "gmx-v2": "GMX V2", - "dforce": "dForce", - "apex-protocol": "ApeX Protocol", - "qidao": "QiDao", - "astroport": "Astroport", - "baseswap": "BaseSwap", - "spl-governance": "SPL Governance", - "equilibria": "Equilibria", - "dodo": "DODO", - "jito": "Jito", - "wombat-exchange": "Wombat Exchange", - "unslashed": "Unslashed", - "raydium": "Raydium", - "gearbox": "Gearbox", - "stride": "Stride", - "tbtc": "tBTC", - "synclub-staked-bnb": "Synclub Staked BNB", - "gravita-protocol": "Gravita Protocol", - "saucerswap": "SaucerSwap", - "thena-fusion": "THENA FUSION", - "mdex": "MDEX", - "conic-finance": "Conic Finance", - "solv-protocol-v3": "Solv Protocol V3", - "ad-astra-bridge": "Ad-Astra Bridge", - "b.protocol": "B.Protocol", - "flamingo-finance": "Flamingo Finance", - "sommelier": "Sommelier", - "ferro": "Ferro", - "frax-fpi": "Frax FPI", - "tangible": "Tangible", - "angle": "Angle", - "octus-bridge": "Octus Bridge", - "sharedstake": "SharedStake", - "the-tokenized-bitcoin": "The Tokenized Bitcoin", - "shibaswap": "ShibaSwap", - "notional": "Notional", - "tetu-earn": "Tetu Earn", - "youves": "Youves", - "extra-finance": "Extra Finance", - "ether.fi": "ether.fi", - "jones-dao": "Jones DAO", - "benddao-ape-staking": "BendDAO APE Staking", - "lofty": "Lofty", - "sushiswap-v3": "SushiSwap V3", - "canto-dex": "Canto Dex", - "trader-joe-dex": "Trader Joe DEX", - "reserve": "Reserve", - "klaystation": "KLAYstation", - "keep3r-network": "Keep3r Network", - "okcswap": "OKCSwap", - "badger-dao": "Badger DAO", - "ajna": "Ajna", - "reflexer": "Reflexer", - "beethoven-x": "Beethoven X", - "parallel-lending": "Parallel Lending", - "alien-base": "Alien Base", - "blur-lending": "Blur Lending", - "agni-finance": "Agni Finance", - "dhedge": "dHEDGE", - "marginfi": "marginfi", - "vesper": "Vesper", - "folks-finance-lending": "Folks Finance Lending", - "tokemak": "Tokemak", - "terra-bridge": "Terra Bridge", - "morpho-compound": "Morpho Compound", - "acryptos": "ACryptoS", - "phux": "PHUX", - "mute.io": "Mute.io", - "bifrost-liquid-staking": "Bifrost Liquid Staking", - "coindcx": "CoinDCX", - "liqee": "Liqee", - "yield-yak-aggregator": "Yield Yak Aggregator", - "bancor-v2.1": "Bancor V2.1", - "dopex": "Dopex", - "hmx": "HMX", - "lyra": "Lyra", - "iron-bank": "Iron Bank", - "ribbon": "Ribbon", - "smardex": "SmarDex", - "nodedao": "NodeDAO", - "aktionariat": "Aktionariat", - "vector-finance": "Vector Finance", - "iziswap": "iZiSwap", - "level-finance": "Level Finance", - "defibox": "DefiBox", - "sovryn-zero": "Sovryn Zero", - "liquid-collective": "Liquid Collective", - "mimo-protocol": "Mimo Protocol", - "swapbased-amm": "SwapBased AMM", - "liqwid": "Liqwid", - "stafi": "Stafi", - "helix": "Helix", - "uxd": "UXD", - "kleva-farm": "Kleva Farm", - "acala-dollar": "Acala Dollar", - "overnight-usd+": "Overnight USD+", - "connext": "Connext", - "tezos-liquidity-baking": "Tezos Liquidity Baking", - "rhino.fi": "rhino.fi", - "spookyswap": "SpookySwap", - "paraspace-ape-staking": "ParaSpace APE Staking", - "exinpool": "ExinPool", - "origin-dollar": "Origin Dollar", - "retro": "Retro", - "renvm": "RenVM", - "linear-protocol": "LiNEAR Protocol", - "lusd-chickenbonds": "LUSD ChickenBonds", - "stfil": "STFIL", - "alex": "ALEX", - "pangolin": "Pangolin", - "arthswap": "ArthSwap", - "nftx": "NFTX", - "rex-staking": "REX Staking", - "sovryn-dex": "Sovryn Dex", - "babydogeswap": "BabyDogeSwap", - "toucan-protocol": "Toucan Protocol", - "iotube": "ioTube", - "jpegd": "JPEG'd", - "serum": "Serum", - "strike": "Strike", - "canto-lending": "Canto Lending", - "okcoin": "Okcoin", - "thena-v1": "THENA V1", - "apeswap-amm": "ApeSwap AMM", - "glif-pools-infinity-pool": "GLIF Pools Infinity Pool", - "opyn-gamma": "Opyn Gamma", - "solo-top": "Solo Top", - "stellarx": "StellarX", - "pando-rings": "Pando Rings", - "platypus-finance": "Platypus Finance", - "ipor": "IPOR", - "liquidswap": "LiquidSwap", - "bifi": "BiFi", - "nbx": "NBX", - "kyberswap-classic": "KyberSwap Classic", - "asymetrix-protocol": "Asymetrix Protocol", - "neoburger": "NeoBurger", - "injective-bridge": "Injective Bridge", - "hotbit": "Hotbit", - "toros": "Toros", - "gnd-protocol": "GND Protocol", - "biswap-v3": "BiSwap V3", - "openeden-t-bills": "OpenEden T-Bills", - "perpetual-protocol": "Perpetual Protocol", - "camelot-v3": "Camelot V3", - "wing-finance": "Wing Finance", - "paycash": "PayCash", - "nftfi": "NFTfi", - "algem": "Algem", - "vesta-finance": "Vesta Finance", - "vela-exchange": "Vela Exchange", - "01": "01", - "spartadex": "SpartaDex", - "unsheth": "unshETH", - "spool-protocol": "Spool Protocol", - "swappi": "Swappi", - "ref-finance": "Ref Finance", - "meta-pool-near": "Meta Pool Near", - "bella-protocol": "Bella Protocol", - "dfyn-network": "Dfyn Network", - "drift": "Drift", - "bifrost-liquid-crowdloan": "Bifrost Liquid Crowdloan", - "stake.ly": "Stake.ly", - "sicx": "sICX", - "gmd-protocol": "GMD Protocol", - "burrow": "Burrow", - "yeti-finance": "Yeti Finance", - "pact": "Pact", - "belt-finance": "Belt Finance", - "francium": "Francium", - "wemix.fi-amm": "WEMIX.FI AMM", - "geth": "GETH", - "tulip-protocol": "Tulip Protocol", - "neopin-staking": "NEOPIN Staking", - "wrapped": "Wrapped", - "aave-v1": "AAVE V1", - "djed-stablecoin": "Djed Stablecoin", - "hydradx": "HydraDX", - "woofi-earn": "WOOFi Earn", - "arcade-xyz": "Arcade xyz", - "exactly": "Exactly", - "pooltogether": "PoolTogether", - "jediswap": "JediSwap", - "pancakeswap-stableswap": "PancakeSwap StableSwap", - "thalaswap": "ThalaSwap", - "bunni": "Bunni", - "acala-liquid-staking": "Acala Liquid-Staking", - "vyfinance-dex": "VyFinance Dex", - "pirex": "Pirex", - "swapscanner-lsd": "Swapscanner LSD", - "ambient": "Ambient", - "relaychain": "RelayChain", - "nuls": "Nuls", - "tranchess-ether": "Tranchess Ether", - "agave": "Agave", - "saber": "Saber", - "wombex-finance": "Wombex Finance", - "chainport": "ChainPort", - "quarry": "Quarry", - "woofi-swap": "WOOFi Swap", - "vertex-protocol": "Vertex Protocol", - "harvest-finance": "Harvest Finance", - "dual-finance": "Dual Finance", - "pulsex-v2": "PulseX V2", - "hyperliquid": "Hyperliquid", - "outcome-finance": "Outcome Finance", - "meshswap": "Meshswap", - "clipper": "Clipper", - "autofarm": "Autofarm", - "realt-rmm-marketplace": "RealT RMM Marketplace", - "metronome-synth": "Metronome Synth", - "alpaca-finance-2.0": "Alpaca Finance 2.0", - "deltaprime": "DeltaPrime", - "ichi": "ICHI", - "opyn-squeeth": "Opyn Squeeth", - "jpool": "JPool", - "parrot-protocol": "Parrot Protocol", - "clever": "CLever", - "bitvenus": "BitVenus", - "wingriders": "WingRiders", - "muesliswap": "MuesliSwap", - "juicebox-v1": "Juicebox V1", - "railgun": "Railgun", - "myswap": "mySwap", - "klayswap-v3": "KlaySwap V3", - "shell-protocol": "Shell Protocol", - "thetanuts-finance": "Thetanuts Finance", - "trustake-matic": "TruStake MATIC", - "ouchi-finance": "Ouchi Finance", - "orderly-network": "Orderly Network", - "umee": "Umee", - "kamino": "Kamino", - "chronos-v1": "Chronos V1", - "llama-airforce": "Llama Airforce", - "ghost": "Ghost", - "blazestake": "BlazeStake", - "pstake-finance": "pSTAKE Finance", - "filda": "FilDA", - "cetus": "Cetus", - "plutusdao": "PlutusDAO", - "meteora-vaults": "Meteora vaults", - "uniswap-v1": "Uniswap V1", - "linear-finance": "Linear Finance", - "hegic": "Hegic", - "polymarket": "Polymarket", - "aztec": "Aztec", - "meter-passport": "Meter Passport", - "optim-finance": "Optim Finance", - "proxy": "Proxy", - "thala-cdp": "Thala CDP", - "reaper-farm": "Reaper Farm", - "kokonut-swap": "Kokonut Swap", - "green-planet": "Green Planet", - "atrix": "Atrix", - "lodestar-v1": "Lodestar V1", - "cream-lending": "CREAM Lending", - "flrfarm": "FLRFarm", - "rari-capital": "Rari Capital", - "granary-finance": "Granary Finance", - "shadeswap": "ShadeSwap", - "native": "Native", - "lumenswap": "LumenSwap", - "latoken": "Latoken", - "gyroscope-protocol": "Gyroscope Protocol", - "nestfi": "NestFi", - "parallel-liquid-staking": "Parallel Liquid Staking", - "meteora-pools": "Meteora pools", - "ultronswap": "UltronSwap", - "trader-joe-lend": "Trader Joe Lend", - "megaton-finance": "Megaton Finance", - "bumper-finance": "Bumper Finance", - "arrakis-v2": "Arrakis V2", - "velocore-v1": "Velocore V1", - "aevo": "Aevo", - "lenfi": "Lenfi", - "defiedge": "DefiEdge", - "defi-franc": "DeFi Franc", - "cvault-finance": "cVault Finance", - "sphere-finance": "Sphere Finance", - "shadelend": "ShadeLend", - "guard-helmet": "Guard-Helmet", - "tinyman": "Tinyman", - "quickperps": "QuickPerps", - "synfutures-v2": "SynFutures V2", - "mare-finance-v2": "Mare Finance V2", - "friend.tech": "friend.tech", - "pika-v4": "Pika V4", - "tarot": "Tarot", - "range-protocol": "Range Protocol", - "sundaeswap": "SundaeSwap", - "ethos-reserve": "Ethos Reserve", - "nucleon": "Nucleon", - "archi-finance": "Archi Finance", - "tokensfarm": "TokensFarm", - "spacefi-zksync": "SpaceFi zkSync", - "mstable-cdp": "mStable CDP", - "magpie": "Magpie", - "uncx-network-v3": "UNCX Network V3", - "sablier-v1": "Sablier V1", - "wepiggy": "WePiggy", - "back-finance": "Back Finance", - "inverse-finance-frontier": "Inverse Finance Frontier", - "minefi": "MineFi", - "vyfinance-vaults": "VyFinance Vaults", - "veno-finance": "Veno Finance", - "vanswap": "VanSwap", - "mars-protocol": "Mars Protocol", - "velodrome-v1": "Velodrome V1", - "wan-bridge": "Wan Bridge", - "hummus-exchange": "Hummus Exchange", - "zunami-protocol": "Zunami Protocol", - "redacted-protocol": "Redacted Protocol", - "dooar": "DOOAR", - "orion-protocol": "Orion Protocol", - "bitmake": "BitMake", - "mm-finance-cronos": "MM Finance Cronos", - "filet-finance": "Filet Finance", - "premia": "Premia", - "knightswap-finance": "KnightSwap Finance", - "10kswap": "10KSwap", - "port-finance": "Port Finance", - "unifi-staking": "UniFi Staking", - "nervebridge": "NerveBridge", - "navi-protocol": "NAVI Protocol", - "dolomite": "Dolomite", - "kaidex": "Kaidex", - "ellipsis-finance": "Ellipsis Finance", - "stake.link-liquid": "stake.link liquid", - "vitex": "ViteX", - "honeyswap": "Honeyswap", - "ease.org": "Ease.org", - "hermes-protocol": "Hermes Protocol", - "astriddao": "AstridDAO", - "creamswap": "CreamSwap", - "abc-pool": "ABC Pool", - "metastreet-v2": "MetaStreet V2", - "kine-finance": "Kine Finance", - "voltswap-v2": "VoltSwap V2", - "mimo": "Mimo", - "overnight-dai+": "Overnight DAI+", - "mummy-finance": "Mummy Finance", - "the-idols": "The Idols", - "dsu-money": "DSU Money", - "interport-finance": "Interport Finance", - "aurora-plus": "Aurora Plus", - "1inch-network": "1inch Network", - "pickle": "Pickle", - "nomiswap": "Nomiswap", - "perennial": "Perennial", - "clearpool": "Clearpool", - "kordfi": "KordFi", - "hashking": "HashKing", - "sanctum": "Sanctum", - "lifinity": "Lifinity", - "deeplock": "DeepLock", - "polynomial-trade": "Polynomial Trade", - "verse": "Verse", - "arkadiko": "Arkadiko", - "omm": "Omm", - "sio2-finance": "SiO2 Finance", - "bolide": "Bolide", - "larix": "Larix", - "unieth": "uniETH", - "ashswap": "Ashswap", - "avault": "Avault", - "apeswap-lending": "ApeSwap Lending", - "hakka-finance": "Hakka Finance", - "flrx": "FLRX", - "bakeryswap": "BakerySwap", - "equilibre": "Equilibre", - "azuro": "Azuro", - "gnosis-protocol-v1": "Gnosis Protocol v1", - "openbook": "OpenBook", - "karura-liquid-staking": "Karura Liquid-Staking", - "allbridge": "Allbridge", - "crystalvale": "Crystalvale", - "tortuga": "Tortuga", - "insurace": "InsurAce", - "flatqube": "FlatQube", - "crescent-dex": "Crescent Dex", - "sft-protocol": "SFT Protocol", - "sushi-trident": "Sushi Trident", - "bunny": "Bunny", - "heliswap": "HeliSwap", - "winr-protocol": "WINR Protocol", - "dot-dot-finance": "Dot Dot Finance", - "symbiosis": "Symbiosis", - "fulcrom": "Fulcrom", - "oraidex": "OraiDEX", - "velocimeter-v2": "Velocimeter V2", - "mars-ecosystem": "Mars Ecosystem", - "increment-swap": "Increment Swap", - "alpaca-perpetual-futures-exchange": "Alpaca Perpetual Futures Exchange", - "stellaswap-v2": "StellaSwap V2", - "zklend": "zkLend", - "bow": "Bow", - "mugenfinance": "MugenFinance", - "wrapped-bnb": "Wrapped BNB", - "netswap": "NetSwap", - "ethernity-chain": "Ethernity Chain", - "karura-dollar-(kusd)": "Karura Dollar (kUSD)", - "hope-swap": "HOPE Swap", - "balanced": "Balanced", - "babelfish": "BabelFish", - "xswap-protocol": "XSwap Protocol", - "crosschain-bridge": "CrossChain Bridge", - "moola-market": "Moola Market", - "stabl.fi-v2": "Stabl.fi V2", - "klaybank": "KlayBank", - "velocore-v2": "Velocore V2", - "spectrum-finance": "Spectrum Finance", - "neutrino": "Neutrino", - "kava-swap": "Kava Swap", - "scrub-invest": "Scrub Invest", - "hubble": "Hubble", - "meld-gold": "Meld Gold", - "goledo": "Goledo", - "single-finance": "Single Finance", - "sherlock": "Sherlock", - "zilswap": "ZilSwap", - "tender-finance": "Tender Finance", - "aark-digital": "Aark Digital", - "venus-isolated-pools": "Venus Isolated Pools", - "waterfall-defi": "Waterfall DeFi", - "synthswap-v2": "Synthswap V2", - "openleverage": "OpenLeverage", - "benswap": "BenSwap", - "ramses-v2": "Ramses V2", - "interlay-collateral": "Interlay Collateral", - "solv-protocol-v2": "Solv Protocol V2", - "umami-finance": "Umami Finance", - "eris-protocol": "Eris Protocol", - "whaleex": "WhaleEx", - "kokoa-finance": "Kokoa Finance", - "impermax-finance": "Impermax Finance", - "hord": "Hord", - "fvm-exchange": "FVM Exchange", - "jarvis-network": "Jarvis Network", - "proton-loan": "Proton Loan", - "onyx-protocol": "Onyx Protocol", - "themis-dex": "Themis DEX", - "flokifi-locker": "FlokiFi Locker", - "fin": "FIN", - "echidna-finance": "Echidna Finance", - "llamapay": "LlamaPay", - "gro": "Gro", - "depth": "Depth", - "prime-protocol": "Prime Protocol", - "kolibri": "Kolibri", - "one-dex": "One Dex", - "xy-finance": "XY Finance", - "erasure": "Erasure", - "cat-in-a-box": "Cat in a Box", - "horizon-protocol": "Horizon Protocol", - "whiteheart": "Whiteheart", - "babyswap": "BabySwap", - "anyhedge": "AnyHedge", - "swaap-v2": "Swaap V2", - "white-whale-dex": "White Whale Dex", - "reactorfusion": "ReactorFusion", - "boringdao": "BoringDAO", - "pangea-swap": "Pangea Swap", - "idex": "IDEX", - "filfi": "FilFi", - "metavault.trade": "Metavault.Trade", - "alongside": "Alongside", - "defis-network": "DeFis Network", - "bifrost-dex": "Bifrost Dex", - "hawksight": "Hawksight", - "joe-v2": "Joe V2", - "equalizer-exchange": "Equalizer Exchange", - "radioshack": "RadioShack", - "fusionx-v3": "FusionX V3", - "yield-protocol": "Yield Protocol", - "stkd-scrt": "stkd-SCRT", - "acala-swap": "Acala Swap", - "kriyadex": "KriyaDEX", - "klap": "Klap", - "wanswap-dex": "WanSwap Dex", - "ktx.finance": "KTX.Finance", - "ramses-v1": "Ramses V1", - "sushi-furo": "Sushi Furo", - "algofi-lend": "Algofi Lend", - "teahouse-managed": "Teahouse Managed", - "sunny": "Sunny", - "defi-kingdoms": "Defi Kingdoms", - "deri-protocol": "Deri Protocol", - "mellow-protocol": "Mellow Protocol", - "rook": "Rook", - "usk": "USK", - "meter-liquid-staking": "Meter Liquid Staking", - "hyphen": "Hyphen", - "wigoswap": "WigoSwap", - "trisolaris": "Trisolaris", - "klayportal": "Klayportal", - "ribbon-earn": "Ribbon Earn", - "axial": "Axial", - "mm-optimizer": "MM Optimizer", - "dyson": "Dyson", - "revert-compoundor": "Revert Compoundor", - "zkswap": "ZKSwap", - "tai.money": "tai.money", - "zeniq-swap": "Zeniq Swap", - "mendi-finance": "Mendi Finance", - "dexter": "Dexter", - "affine-defi": "Affine DeFi", - "starlay-finance": "Starlay Finance", - "sudoswap-v1": "Sudoswap V1", - "hashmix-fil": "HashMix FIL", - "katana": "Katana", - "rage-trade": "Rage Trade", - "pluto": "Pluto", - "stellaswap-v3": "StellaSwap V3", - "struct-finance": "Struct Finance", - "zeta": "Zeta", - "stakehouse": "Stakehouse", - "pepeteam-swaves": "PepeTeam sWAVES", - "opium": "Opium", - "neopin-pool": "NEOPIN Pool", - "grizzlyfi-hives": "GrizzlyFi Hives", - "credix": "Credix", - "bastion": "Bastion", - "pepeteam-bridge": "PepeTeam Bridge", - "singularitydao": "SingularityDAO", - "yama-finance": "Yama Finance", - "wx-network": "WX Network", - "channels-finance": "Channels Finance", - "moonwell-apollo": "Moonwell Apollo", - "algorai-finance": "AlgoRai Finance", - "thales": "Thales", - "nerveswap": "NerveSwap", - "saddle-finance": "Saddle Finance", - "hidden-hand": "Hidden Hand", - "vesync": "veSync", - "ston.fi": "STON.fi", - "libre-swap": "Libre Swap", - "sovryn-lend": "Sovryn Lend", - "psyoptions": "PsyOptions", - "the-ennead-farm": "The Ennead Farm", - "aurigami": "Aurigami", - "spiritswap-amm": "SpiritSwap AMM", - "xwin-finance": "xWin Finance", - "archimedes-finance": "Archimedes Finance", - "sharkswap": "SharkSwap", - "claystack": "ClayStack", - "dfx-finance": "DFX Finance", - "defi-swap": "Defi Swap", - "ubeswap": "Ubeswap", - "unidex": "UniDex", - "sienna-network": "Sienna Network", - "proton-swap": "Proton Swap", - "claimswap-v1": "ClaimSwap V1", - "solarbeam": "Solarbeam", - "interlay-btc": "Interlay BTC", - "sperax-demeter": "Sperax Demeter", - "bluefin": "Bluefin", - "thundercore-staking": "ThunderCore Staking", - "unicly": "Unicly", - "cvi-finance": "CVI Finance", - "perlin": "Perlin", - "parallel-amm": "Parallel AMM", - "astarfarm": "AstarFarm", - "maple": "Maple", - "bemo": "bemo", - "sturdy": "Sturdy", - "quicksilver-protocol": "Quicksilver Protocol", - "rabbitx": "RabbitX", - "dedust": "DeDust", - "buffer-finance": "Buffer Finance", - "aftermath-finance": "Aftermath Finance", - "proton-dex": "Proton Dex", - "cherryswap": "CherrySwap", - "tomb-swap": "Tomb Swap", - "integral": "Integral", - "lineabank": "LineaBank", - "bscswap": "BSCSwap", - "yoshi-exchange": "Yoshi Exchange", - "athena-finance": "Athena Finance", - "definix": "Definix", - "fx-protocol": "fx Protocol", - "nerve-staking": "Nerve Staking", - "gooddollar": "GoodDollar", - "swapr": "Swapr", - "sigmausd": "Sigmausd", - "sithswap": "SithSwap", - "cyclone": "Cyclone", - "tealswap": "Tealswap", - "arbis-finance": "Arbi's Finance", - "brahma": "Brahma", - "valas-finance": "Valas Finance", - "derivadex": "DerivaDEX", - "siren": "Siren", - "arbswap-amm": "Arbswap AMM", - "ifpool": "IFPool", - "scallop-lend": "Scallop Lend", - "nerve": "Nerve", - "defidollar-dao": "DefiDollar DAO", - "fx-swap": "FX Swap", - "mantisswap": "MantisSwap", - "rysk-finance": "Rysk Finance", - "olive": "Olive", - "o3-swap": "O3 Swap", - "titano-swych": "Titano Swych", - "cache.gold": "CACHE.Gold", - "superfluid": "Superfluid", - "vooi": "Vooi", - "wanlend": "WanLend", - "ceres-demeter": "Ceres Demeter", - "eklipse-finance": "Eklipse Finance", - "tidal-finance": "Tidal Finance", - "cozy-v2": "Cozy V2", - "cega": "Cega", - "spice-finance": "Spice Finance", - "starfish-finance": "Starfish Finance", - "beta-finance": "Beta Finance", - "puzzle-lend": "Puzzle Lend", - "sperax-usd": "Sperax USD", - "goodentry": "GoodEntry", - "tokenlon": "Tokenlon", - "hadouken-lend": "Hadouken Lend", - "yokaiswap": "YokaiSwap", - "grovecoin": "GroveCoin", - "metronome-v1": "Metronome V1", - "handle.fi-perps": "handle.fi Perps", - "voltz": "Voltz", - "phoenix-bonds": "Phoenix Bonds", - "0vix": "0vix", - "dackieswap": "DackieSwap", - "mango-markets-v4": "Mango Markets V4", - "rollup-finance": "Rollup Finance", - "delv": "DELV", - "goldfinch": "Goldfinch", - "bunicorn": "Bunicorn", - "socean": "Socean", - "sirius-finance": "Sirius Finance", - "flashstake": "Flashstake", - "tropykus-rsk": "Tropykus RSK", - "fstswap": "FstSwap", - "gogopool": "GoGoPool", - "rodeo": "Rodeo", - "apricot-finance": "Apricot Finance", - "quipuswap-v2": "QuipuSwap V2", - "hyper-finance": "Hyper Finance", - "augur": "Augur", - "unifarm": "UniFarm", - "vega-protocol": "Vega Protocol", - "ratio-finance": "Ratio Finance", - "zookeeper": "Zookeeper", - "ensuro": "Ensuro", - "glide-finance": "Glide Finance", - "bao-masterchef-v1": "Bao Masterchef V1", - "moon-swap": "Moon Swap", - "zyberswap-v3": "Zyberswap V3", - "alchemist": "Alchemist", - "xtoken": "xToken", - "x2y2": "X2Y2", - "arbitrum-exchange-v3": "Arbitrum Exchange V3", - "piedao": "PieDAO", - "pods-yield": "Pods Yield", - "apollodao": "ApolloDAO", - "frakt": "FRAKT", - "liquid-driver": "Liquid Driver", - "liquid-finance": "Liquid Finance", - "ddex": "DDEX", - "stlos-liquid-staking": "sTLOS Liquid Staking", - "aries-markets": "Aries Markets", - "solidlizard": "SolidLizard", - "astrovault": "Astrovault", - "revault": "Revault", - "vaultka": "Vaultka", - "phuture": "Phuture", - "planet-farm": "Planet Farm", - "lendle": "Lendle", - "grizzly-trade": "Grizzly Trade", - "golff-protocol": "Golff Protocol", - "sonic": "Sonic", - "flrloans": "FLRLoans", - "maia-v3": "Maia V3", - "horizondex": "HorizonDEX", - "fractional-art": "Fractional Art", - "zoomswap": "ZoomSwap", - "rocketswap-base": "RocketSwap Base", - "kromatika": "Kromatika", - "truefi": "TrueFi", - "eralend": "EraLend", - "concave": "Concave", - "popcorn": "Popcorn", - "omnibtc": "OmniBTC", - "aboard-exchange": "Aboard Exchange", - "arbitrove": "Arbitrove", - "mcdex": "MCDEX", - "turbos": "Turbos", - "mean-finance": "Mean Finance", - "baso-finance": "Baso Finance", - "nomad": "Nomad", - "empiredex": "EmpireDEX", - "bearn": "Bearn", - "hodl-fox": "Hodl Fox", - "enosys-bridge": "Enosys Bridge", - "equilibrium-lending": "Equilibrium Lending", - "universe-xyz": "Universe XYZ", - "tenderize": "Tenderize", - "deepbook": "DeepBook", - "antfarm-finance": "Antfarm finance", - "caviar-v1": "Caviar V1", - "elk": "Elk", - "onchain-trade": "Onchain Trade", - "mm-finance-polygon": "MM Finance Polygon", - "preon-finance": "Preon Finance", - "hashflow": "Hashflow", - "fortube": "ForTube", - "bitindi-dex": "Bitindi Dex", - "nayms": "Nayms", - "wynd-dex": "WYND DEX", - "nested": "Nested", - "synthswap-v3": "Synthswap V3", - "parcl": "Parcl", - "wrap-protocol": "Wrap Protocol", - "annex": "Annex", - "mesher": "Mesher", - "gardens-dao": "Gardens DAO", - "unipilot": "Unipilot", - "nitron": "Nitron", - "demex": "Demex", - "morphex": "Morphex", - "algomint": "Algomint", - "oasisswap": "OasisSwap", - "voltage": "Voltage", - "stella": "Stella", - "fusion-dex": "Fusion Dex", - "chronos-v2": "Chronos V2", - "smoothy": "Smoothy", - "lenen": "Lenen", - "scientix": "Scientix", - "archerswap": "ArcherSwap", - "solidly-v2": "Solidly V2", - "kagla-finance": "Kagla Finance", - "popsicle-finance": "Popsicle Finance", - "penguin": "Penguin", - "holdstation-defutures": "HoldStation DeFutures", - "paladin-vote": "Paladin Vote", - "peri-finance": "PERI Finance", - "hiveswap": "HiveSwap", - "lfgswap-core": "LFGSwap Core", - "yuzuswap": "YuzuSwap", - "timeswap-v2": "Timeswap V2", - "centrifuge": "Centrifuge", - "knightswap-lending": "KnightSwap Lending", - "cap-v4": "Cap V4", - "swarm-markets": "Swarm Markets", - "paraluni-dex": "Paraluni Dex", - "icpswap": "ICPSwap", - "niifi": "NiiFi", - "geyser": "Geyser", - "terraswap": "Terraswap", - "juicebox-v3": "Juicebox V3", - "futureswap": "FutureSwap", - "carbon-defi": "Carbon Defi", - "argo-finance": "Argo Finance", - "arbitrum-exchange-v2": "Arbitrum Exchange V2", - "hector-lending": "Hector Lending", - "increment-lending": "Increment Lending", - "glacier-finance": "Glacier Finance", - "contango": "Contango", - "madmex": "Madmex", - "selfex": "Selfex", - "noah-swap": "Noah Swap", - "keep-network": "KEEP Network", - "tangoswap": "TangoSwap", - "hats.v1": "Hats.V1", - "pika": "Pika", - "geode": "Geode", - "rubicon": "Rubicon", - "ion-dao": "ION DAO", - "overnight-usdt+": "Overnight USDT+", - "bucket-protocol": "Bucket Protocol", - "astroport-classic": "Astroport Classic", - "quipuswap-stableswap": "QuipuSwap Stableswap", - "hydradex-v3": "Hydradex V3", - "cover-protocol": "Cover Protocol", - "symmetry": "Symmetry", - "midas-capital": "Midas Capital", - "teller": "Teller", - "icecreamswap": "IcecreamSwap", - "solid-world": "Solid World", - "flowx-finance": "FlowX Finance", - "mojitoswap": "MojitoSwap", - "sphynx": "Sphynx", - "yield-yak-staked-avax": "Yield Yak Staked Avax", - "ezkalibur": "eZKalibur", - "yfx": "YFX", - "eckodao": "eckoDAO", - "messinaone": "MessinaOne", - "secretswap": "SecretSwap", - "sentiment": "Sentiment", - "wagyuswap": "WagyuSwap", - "unibot-v2": "Unibot V2", - "hats.v2": "Hats.V2", - "fractal-protocol": "Fractal Protocol", - "friktion": "Friktion", - "illuminate": "Illuminate", - "openocean": "OpenOcean", - "vendor-v1": "Vendor V1", - "karura-swap": "Karura Swap", - "lendflare": "LendFlare", - "miaswap": "MiaSwap", - "zkbob": "zkBob", - "satori": "Satori", - "aavegotchi": "Aavegotchi", - "apy-finance": "APY Finance", - "vapordex": "VaporDex", - "jetswap": "JetSwap", - "ocean-one": "Ocean One", - "rabbit-finance": "Rabbit Finance", - "smartcredit": "SmartCredit", - "revest-finance": "Revest Finance", - "blockng": "BlockNG", - "itrust-finance": "iTrust Finance", - "ditto": "Ditto", - "mantra-dao": "MANTRA DAO", - "mimas-finance": "Mimas Finance", - "socialswap": "SocialSwap", - "stream-finance": "Stream Finance", - "snowswap": "SnowSwap", - "locus-finance": "Locus Finance", - "alita-finance": "Alita Finance", - "step-exchange": "Step Exchange", - "sable-finance": "Sable Finance", - "salsa": "SALSA", - "termfinance": "TermFinance", - "kurrency": "Kurrency", - "crodex": "Crodex", - "swamp-finance": "Swamp Finance", - "liondex": "LionDEX", - "burgerswap": "BurgerSwap", - "barnbridge": "BarnBridge", - "pippi": "Pippi", - "tethys-amm": "Tethys AMM", - "yaxis": "yAxis", - "resonate-finance": "Resonate Finance", - "neptune-mutual": "Neptune Mutual", - "gotchivault": "GotchiVault", - "forthewin-network": "Forthewin Network", - "lynex": "Lynex", - "zkasino": "ZKasino", - "beamex": "Beamex", - "avely-staking": "Avely Staking", - "saros": "Saros", - "btcst": "BTCST", - "clober": "Clober", - "halofi": "HaloFi", - "spartan": "Spartan", - "lif3-swap": "Lif3 Swap", - "aequinox": "Aequinox", - "stcelo": "stCELO", - "kinza-finance": "Kinza Finance", - "vexchange": "Vexchange", - "jetfuel-finance": "JetFuel Finance", - "fluidity-money": "Fluidity Money", - "humble-defi": "Humble Defi", - "metalswap": "MetalSwap", - "strudel-finance": "Strudel Finance", - "shadowswap": "ShadowSwap", - "tonpound": "Tonpound", - "wasabi": "Wasabi", - "bxh": "BXH", - "sense": "Sense", - "plenty": "Plenty", - "quasar-vaults": "Quasar Vaults", - "kuswap": "KuSwap", - "geist-finance": "Geist Finance", - "oolongswap": "OolongSwap", - "nf3-ape": "NF3 APE", - "dogeswaporg": "DogeSwapOrg", - "pacoca": "Pacoca", - "pilot-protocol": "Pilot Protocol", - "option-dance": "Option Dance", - "swft": "SWFT", - "meta-pool-eth": "Meta Pool ETH", - "swapbased-perp": "SwapBased Perp", - "chest-finance": "Chest Finance", - "charm-finance": "Charm Finance", - "kalmy-app": "Kalmy App", - "brine.fi": "Brine.fi", - "pulse-rate": "Pulse Rate", - "jewelswap-liquid-staking": "JewelSwap Liquid Staking", - "marginx": "MarginX", - "levinswap": "Levinswap", - "julswap": "JulSwap", - "polycat-dex": "Polycat Dex", - "tethys-perpetual": "Tethys Perpetual", - "polkaswap": "Polkaswap", - "cub-finance": "Cub Finance", - "ruby.exchange-stable": "Ruby.Exchange Stable", - "gumball-protocol": "GumBall Protocol", - "purple-bridge-dex": "Purple Bridge DEX", - "ostable": "Ostable", - "flux-exchange": "Flux Exchange", - "blueshift": "Blueshift", - "daytona-finance": "Daytona Finance", - "pegasys-v3": "PegaSys V3", - "volmex": "Volmex", - "zenlink-amm": "Zenlink AMM", - "cometh": "Cometh", - "mero": "Mero", - "guru-network": "Guru Network", - "neopin-liquid": "NEOPIN Liquid", - "tronnrg": "TronNRG", - "jswap": "JSwap", - "globiance": "Globiance", - "kswap-finance": "KSwap Finance", - "zkdx": "zkDX", - "yupana": "Yupana", - "scream": "Scream", - "mochifi": "MochiFi", - "kintsugi": "Kintsugi", - "intercroneswap": "Intercroneswap", - "swapsicle": "Swapsicle", - "ghast-protocol": "Ghast Protocol", - "justmoney": "JustMoney", - "cypher": "Cypher", - "mm-finance-arbitrum": "MM Finance Arbitrum", - "snowball": "Snowball", - "atomic-green": "Atomic Green", - "killswitch": "KillSwitch", - "convergence": "Convergence", - "3xcalibur": "3xcalibur", - "cronaswap": "CronaSwap", - "dove-swap-v3": "Dove Swap V3", - "aldrin": "Aldrin", - "88mph": "88mph", - "tashi": "Tashi", - "flair-dex": "Flair Dex", - "xdefi": "XDeFi", - "pulsar-swap": "Pulsar Swap", - "ruby.exchange-yield": "Ruby.Exchange Yield", - "soswap": "Soswap", - "coinswap-space": "Coinswap Space", - "rehold-v2": "ReHold V2", - "baryon-network": "Baryon Network", - "arbswap-stableswap": "Arbswap StableSwap", - "blue-planet": "Blue Planet", - "jewelswap-leveraged-farming": "JewelSwap Leveraged Farming", - "xalgo-liquid-governance": "xALGO Liquid Governance", - "oreoswap": "OreoSwap", - "equity": "Equity", - "butter-network": "Butter Network", - "tigris": "Tigris", - "astroswap": "AstroSwap", - "quoll": "Quoll", - "beamswap-v3": "BeamSwap V3", - "kibbleswap": "KibbleSwap", - "starswap-starcoin": "Starswap Starcoin", - "ante-finance": "Ante Finance", - "puzzle-swap": "Puzzle Swap", - "yamato-protocol": "Yamato Protocol", - "opyn-convexity": "Opyn Convexity", - "tranquil-finance": "Tranquil Finance", - "ten-finance": "TEN Finance", - "defiplaza": "DefiPlaza", - "reax-assets": "Reax Assets", - "powerpool": "PowerPool", - "baklava-space": "Baklava Space", - "wavelength-dao": "Wavelength DAO", - "exinswap": "ExinSwap", - "nftb": "NFTb", - "dotoracle": "DotOracle", - "cswap": "cSwap", - "spiritswap-lending": "SpiritSwap Lending", - "big-data-protocol": "Big Data Protocol", - "sharky": "Sharky", - "crowdswap": "CrowdSwap", - "wardenswap": "WardenSwap", - "typus-finance": "Typus Finance", - "pala": "Pala", - "lfgswap-arbitrum": "LFGSwap Arbitrum", - "lodestar-v0": "Lodestar V0", - "polaris-finance": "Polaris Finance", - "y2k-v1": "Y2K V1", - "kiloex": "KiloEx", - "thetaswap": "ThetaSwap", - "risk-harbor": "Risk Harbor", - "swop": "Swop", - "binary-swap": "Binary Swap", - "lighter": "Lighter", - "neutra-finance": "Neutra Finance", - "cryptex-v1": "Cryptex V1", - "cozy-v1": "Cozy V1", - "orchai": "Orchai", - "mobius-money": "Mobius Money", - "bond-protocol": "Bond Protocol", - "router-protocol": "Router Protocol", - "radiant-v1": "Radiant V1", - "satoshicoreswap": "SatoshiCoreSwap", - "dinosaur-eggs": "Dinosaur Eggs", - "iron-finance": "Iron Finance", - "gibxswap": "GIBXSwap", - "swivel-finance": "Swivel Finance", - "thetacash": "ThetaCash", - "echodex-v3": "EchoDEX V3", - "mistswap": "MistSwap", - "steroids": "Steroids", - "pinkswap": "PinkSwap", - "taiga-acala": "Taiga Acala", - "knit-finance": "Knit Finance", - "pods-finance": "Pods Finance", - "spin-dov": "Spin DOV", - "steer-protocol": "Steer Protocol", - "quipuswap-v1": "QuipuSwap V1", - "echodex-v2": "EchoDEX V2", - "kogefarm": "Kogefarm", - "metatdex": "MetaTdex", - "energiswap": "Energiswap", - "unlimited-network": "Unlimited Network", - "increment-liquid-staking": "Increment Liquid Staking", - "component": "Component", - "monopoly-finance": "Monopoly Finance", - "fathom-dex": "Fathom Dex", - "beamswap-classic": "BeamSwap Classic", - "crystl-finance": "Crystl Finance", - "prepo": "prePO", - "debridge": "deBridge", - "proteo-defi": "Proteo Defi", - "neku": "Neku", - "steakhut-liquidity": "SteakHut Liquidity", - "xsigma": "xSigma", - "wemix.fi-lend": "WEMIX.FI Lend", - "arbor-finance": "Arbor Finance", - "reax-lending": "Reax Lending", - "veax": "Veax", - "luaswap": "LuaSwap", - "halotrade": "HaloTrade", - "swerve": "Swerve", - "cometa": "Cometa", - "quantumx-network": "QuantumX Network", - "solisnek-finance": "SoliSnek Finance", - "ram-protocol": "Ram Protocol", - "pantherswap": "PantherSwap", - "key-finance": "Key Finance", - "here-wallet-staking": "HERE Wallet staking", - "tsunami.finance": "Tsunami.Finance", - "zkboost": "zkBoost", - "synthetify": "Synthetify", - "taiga-protocol": "Taiga Protocol", - "carbonswap": "CarbonSwap", - "gard": "GARD", - "velvet.capital": "Velvet.Capital", - "pembrock-finance": "PembRock Finance", - "anetabtc": "anetaBTC", - "lendexe-finance": "LendeXe Finance", - "pawnfi-lending": "Pawnfi Lending", - "opx-finance": "OPX Finance", - "percent-finance": "Percent Finance", - "w3swap": "W3swap", - "kyotoswap": "KyotoSwap", - "zkswap-finance": "zkSwap Finance", - "oneswap": "OneSwap", - "predy-v5": "Predy V5", - "alienfi": "AlienFi", - "xbacked": "xBacked", - "bridge-mutual": "Bridge Mutual", - "axion": "Axion", - "hashdao-finance": "HashDAO Finance", - "mycelium-perpetual-pools": "Mycelium Perpetual Pools", - "union-protocol": "Union Protocol", - "abel-finance": "ABEL Finance", - "counterstake": "Counterstake", - "quadrat": "Quadrat", - "polycat": "Polycat", - "demeter": "Demeter", - "autoshark": "AutoShark", - "moremoney": "Moremoney", - "ape-finance": "Ape Finance", - "dbx-finance": "DBX Finance", - "dexfinance-arbitrum": "DexFinance Arbitrum", - "tranche-finance": "Tranche Finance", - "el-dorado-exchange": "El Dorado Exchange", - "pheasantswap": "PheasantSwap", - "citrus": "Citrus", - "beluga-dex": "Beluga Dex", - "unilend": "Unilend", - "verocket": "VeRocket", - "forge": "Forge", - "asymmetry-finance": "Asymmetry Finance", - "sector-finance": "Sector Finance", - "lunafi": "LunaFi", - "hadouken-amm": "Hadouken AMM", - "usdo": "USDO", - "grim-finance": "Grim Finance", - "ooki": "Ooki", - "hyperjump": "HyperJump", - "interest-protocol": "Interest Protocol", - "soy-finance": "Soy Finance", - "zomma-protocol": "Zomma Protocol", - "wagmi": "WAGMI", - "narwhalswap": "Narwhalswap", - "dracula-finance": "Dracula Finance", - "mm-finance-arbitrum-v3": "MM Finance Arbitrum V3", - "valuedefi": "ValueDefi", - "basis-cash": "Basis Cash", - "impossible-finance": "Impossible Finance", - "covo-v2": "Covo V2", - "polynomial-earn": "Polynomial Earn", - "mooniswap": "MooniSwap", - "penguin-finance": "Penguin Finance", - "protectorate-protocol": "Protectorate Protocol", - "sigmafi": "SigmaFi", - "mars-poolin": "Mars Poolin", - "gridex": "Gridex", - "pureswap": "PureSwap", - "market": "Market", - "unfederalreserve": "unFederalReserve", - "savvy": "Savvy", - "pinjam-labs": "Pinjam Labs", - "bonkswap": "Bonkswap", - "makiswap": "MakiSwap", - "cropper": "Cropper", - "voltswap": "VoltSwap", - "basketdao": "BasketDAO", - "dam-finance": "DAM Finance", - "concordex": "Concordex", - "wasabix": "Wasabix", - "titi-protocol": "TiTi Protocol", - "youswap": "YouSwap", - "auroraswap": "AuroraSwap", - "rbx": "RBX", - "atomichub": "AtomicHub", - "kujira-orca": "Kujira Orca", - "starswap": "StarSwap", - "megamoon": "MegaMoon", - "steth.win": "stETH.win", - "sentre": "Sentre", - "nereus-finance": "Nereus Finance", - "xbank-finance": "xBank Finance", - "astra-dao": "Astra DAO", - "growth-defi": "Growth DeFi", - "hundred-finance": "Hundred Finance", - "cap": "Cap", - "bao-swap": "BAO Swap", - "finnexus": "FinNexus", - "rcpswap": "RCPswap", - "uswap": "Uswap", - "lizard": "Lizard", - "klex-finance": "KLEX Finance", - "dystopia": "Dystopia", - "onx-finance": "OnX Finance", - "occamx": "OccamX", - "fuji-v1": "Fuji V1", - "sushi-kashi": "Sushi Kashi", - "paladin-warlord": "Paladin Warlord", - "zkera-finance": "zkEra Finance", - "spectrum-protocol": "Spectrum Protocol", - "yam-finance": "Yam Finance", - "claimswap-v2": "ClaimSwap V2", - "lemma": "Lemma", - "paraluni-masterchef": "Paraluni Masterchef", - "apwine": "APWine", - "based-v1": "Based V1", - "unore": "UnoRe", - "ellipsis-labs": "Ellipsis Labs", - "levvy.fi": "Levvy.Fi", - "ramp": "Ramp", - "xdollar": "xDollar", - "skcs": "sKCS", - "invar-finance": "InVar Finance", - "nsure": "Nsure", - "handle.fi": "handle.fi", - "padswap": "PadSwap", - "layer2-finance": "Layer2 Finance", - "crema-finance": "Crema Finance", - "0xdao": "0xDAO", - "unagii": "Unagii", - "laserswap": "LaserSwap", - "gogocoin": "GOGOcoin", - "float-protocol": "Float Protocol", - "polydex": "Polydex", - "parallax": "Parallax", - "myso": "MYSO", - "vigor": "Vigor", - "sudoswap-v2": "Sudoswap V2", - "thunderpokt": "ThunderPOKT", - "ponytaswap": "PonytaSwap", - "edebase": "EDEBASE", - "rift-finance": "Rift Finance", - "stake.link-index": "stake.link index", - "antimatter": "Antimatter", - "usdfi-lending": "USDFI Lending", - "vaporwave": "Vaporwave", - "alkemi": "Alkemi", - "izumi-liquidbox": "iZUMi LiquidBox", - "seashell": "Seashell", - "phenix-dex": "Phenix DEX", - "insuredao": "InsureDAO", - "hebeswap": "HebeSwap", - "goose-finance": "Goose Finance", - "hedge": "Hedge", - "revert-v3-staker": "Revert V3 Staker", - "metf-finance": "METF Finance", - "citycoins": "CityCoins", - "telos-swap": "Telos Swap", - "solidex": "Solidex", - "aelin": "Aelin", - "algebra-dex": "Algebra DEX", - "hurricaneswap": "HurricaneSwap", - "shrikeperps": "ShrikePerps", - "sheepdex": "SheepDex", - "wineryswap": "WinerySwap", - "frigg.eco": "Frigg.eco", - "rehold-v1": "ReHold V1", - "dao-swap": "DAO Swap", - "gravity-finance": "Gravity Finance", - "unipower": "UniPower", - "baton": "Baton", - "poof-cash": "Poof Cash", - "scanto": "sCANTO", - "unit-protocol": "Unit Protocol", - "davos-protocol": "Davos Protocol", - "adamant-finance": "Adamant Finance", - "atlendis": "Atlendis", - "snowswap-amm": "SnowSwap AMM", - "autoearn-finance": "Autoearn Finance", - "dopple-finance": "Dopple Finance", - "bored-candy-city": "Bored Candy City", - "y2k-v2": "Y2K V2", - "dogewhale": "Dogewhale", - "abex-finance": "ABEx Finance", - "nearpad": "Nearpad", - "jet-margin": "Jet Margin", - "polyquity": "PolyQuity", - "wannaswap": "WannaSwap", - "solidly": "Solidly", - "nuon-finance": "NUON Finance", - "yieldwolf": "YieldWolf", - "oswap-amm": "Oswap AMM", - "gemswap": "GemSwap", - "jumbo-exchange": "Jumbo Exchange", - "sablier-v2": "Sablier V2", - "mfil-protocol": "MFIL Protocol", - "tokenpocket": "TokenPocket", - "vendor-v2": "Vendor V2", - "indexed-finance": "Indexed Finance", - "zharta": "Zharta", - "kdswap": "KDSwap", - "ideamarket": "Ideamarket", - "koffeeswap": "KoffeeSwap", - "pandaswap": "PandaSwap", - "radiate-protocol": "Radiate Protocol", - "moonpot": "Moonpot", - "lien": "Lien", - "dmd-finance": "DMD Finance", - "brokkr-finance": "Brokkr Finance", - "moonswap": "Moonswap", - "sakeperp": "SakePerp", - "stackswap": "StackSwap", - "zyberswap-amm": "Zyberswap AMM", - "leetswap": "LeetSwap", - "photonswap-finance": "PhotonSwap Finance", - "float": "Float", - "opensky-finance": "OpenSky Finance", - "nostra": "Nostra", - "crunchy-network": "Crunchy Network", - "starmaker": "Starmaker", - "zoodao": "ZooDAO", - "onsen-swap": "Onsen Swap", - "composable-finance": "Composable Finance", - "vivaleva-protocol": "VivaLeva Protocol", - "augury-finance": "Augury Finance", - "calc": "CALC", - "zipswap": "ZipSwap", - "pinnako": "Pinnako", - "shoebill-finance": "Shoebill Finance", - "lsdx-ethx": "LSDx ETHx", - "timewarp": "Timewarp", - "swapbased-concentrated-liquidity": "SwapBased Concentrated Liquidity", - "bfly-finance": "BFly Finance", - "gemkeeper": "GemKeeper", - "standard-protocol": "Standard Protocol", - "becoswap": "BecoSwap", - "wynd-lsd": "WYND LSD", - "milkyswap": "MilkySwap", - "swipe": "Swipe", - "teahouse-permissionless": "Teahouse Permissionless", - "pandora": "Pandora", - "stabl.fi-v1": "Stabl.fi V1", - "gambit-financial": "Gambit Financial", - "synfutures-v1": "SynFutures V1", - "bent-finance": "Bent Finance", - "value-liquid": "Value Liquid", - "archly-finance": "Archly Finance", - "dexpad": "DexPad", - "unlockd": "Unlockd", - "caviar-v2": "Caviar V2", - "orbiter-one": "Orbiter One", - "bnbminer-finance": "BNBMiner Finance", - "solarflare": "Solarflare", - "firebird": "Firebird", - "prdt": "PRDT", - "muuu-finance": "Muuu Finance", - "aquarius-loan": "Aquarius Loan", - "spin-perps": "Spin Perps", - "carmine-options": "Carmine Options", - "zerolend": "ZeroLend", - "varen": "Varen", - "pine-protocol": "Pine Protocol", - "hfione": "HFIone", - "darkauto": "DarkAuto", - "darkcrypto": "DarkCrypto", - "lydia": "Lydia", - "paladin-dullahan": "Paladin Dullahan", - "capricorn-finance": "Capricorn Finance", - "fountain-protocol": "Fountain Protocol", - "pinkpea-finance": "PinkPea Finance", - "minmax-finance": "MinMax Finance", - "zyxswap": "ZYXSwap", - "eazyswap": "EazySwap", - "twindex": "Twindex", - "timeless-finance": "Timeless Finance", - "ibcx": "IBCX", - "aptin-finance": "Aptin Finance", - "serum-swap": "Serum Swap", - "avely-swap": "Avely Swap", - "fuji-v2": "Fuji V2", - "ribbon-lend": "Ribbon Lend", - "openxswap": "OpenXswap", - "epsylon-finance": "Epsylon Finance", - "streamflow": "Streamflow", - "skydex": "SkyDex", - "h2odata": "H2Odata", - "nord-finance": "Nord Finance", - "luigiswap": "LuigiSwap", - "niob-finance": "Niob Finance", - "rubydex": "RubyDex", - "elephant-money": "Elephant Money", - "babylon-finance": "Babylon Finance", - "peakdefi": "PEAKDEFI", - "bonsai-strike": "Bonsai Strike", - "swaap-v1": "Swaap V1", - "basilisk": "Basilisk", - "raccoon-finance": "Raccoon Finance", - "oswap-token": "OSWAP Token", - "fiat-dao": "FIAT DAO", - "penrose": "Penrose", - "uniwhale": "Uniwhale", - "yodeswap": "Yodeswap", - "muffin": "Muffin", - "rose": "Rose", - "snowflake-exchange": "Snowflake Exchange", - "chirpfinance": "ChirpFinance", - "bethash": "BetHash", - "comb-financial": "Comb Financial", - "alphadex": "Alphadex", - "foxswap": "FoxSwap", - "oracle-swap": "Oracle Swap", - "furucombo": "Furucombo", - "miniswap": "MiniSwap", - "urdex-finance": "UrDEX Finance", - "phoenix-ergo": "Phoenix Ergo", - "swim-protocol": "Swim Protocol", - "stakesteak": "StakeSteak", - "tendie-swap": "Tendie Swap", - "superfarm": "SuperFarm", - "fusionx-v2": "FusionX V2", - "damm-finance": "dAMM Finance", - "viperswap": "ViperSwap", - "cafeswap": "CafeSwap", - "cook-finance": "Cook Finance", - "knight-dex": "Knight DEX", - "magicfox-vaults": "MagicFox Vaults", - "commodo": "Commodo", - "thorstarter": "Thorstarter", - "hedgefarm": "HedgeFarm", - "tprotocol": "TProtocol", - "jewelswap-nft-lending": "JewelSwap NFT Lending", - "trava-finance": "Trava Finance", - "nft20": "NFT20", - "arch-ethereum-web3": "Arch Ethereum Web3", - "dyad": "DYAD", - "black-whale": "Black Whale", - "steakbank-finance": "SteakBank Finance", - "rigoblock": "Rigoblock", - "rikkei-finance": "Rikkei Finance", - "yfii": "YFII", - "vestige": "Vestige", - "orange-finance": "Orange Finance", - "doki-doki-finance": "Doki Doki Finance", - "cashcow-finance": "CashCow Finance", - "bomb-finance": "Bomb Finance", - "fluxbeam": "FluxBeam", - "alcor-exchange": "Alcor Exchange", - "olive-cash": "Olive Cash", - "hubble-exchange": "Hubble Exchange", - "stonedefi": "StoneDefi", - "metacoin-swap": "MetaCoin Swap", - "synthswap-perps": "Synthswap Perps", - "uno-farm": "UNO farm", - "roe-finance": "Roe Finance", - "metastreet-v1": "MetaStreet V1", - "jet-fixed-term": "Jet Fixed Term", - "gambit-trade": "Gambit Trade", - "auragi-finance": "Auragi Finance", - "meridian-finance": "Meridian Finance", - "drops": "Drops", - "stakeeasy": "StakeEasy", - "spicyswap": "SpicySwap", - "enterdao": "EnterDAO", - "debtdao": "DebtDAO", - "chickenswap": "ChickenSwap", - "complus-network": "Complus Network", - "volta-finance": "Volta Finance", - "adao": "ADAO", - "butterswap": "ButterSwap", - "forlend": "forlend", - "kassandra-dao": "Kassandra DAO", - "newdex": "NewDex", - "solrise-finance": "Solrise Finance", - "polywhale-finance": "Polywhale Finance", - "optimus": "Optimus", - "sportbet": "SportBet", - "derpdex": "DerpDEX", - "zencha-finance": "Zencha Finance", - "quipuswap-v3": "QuipuSwap V3", - "mm-optimizer-polygon": "MM Optimizer Polygon", - "argo": "Argo", - "ttswap": "TTswap", - "huckleberry-amm": "Huckleberry AMM", - "diffusion-finance": "Diffusion Finance", - "pegasys-v2": "PegaSys V2", - "domani-protocol": "Domani Protocol", - "hunnyswap": "HunnySwap", - "algofi-swap": "Algofi Swap", - "invariant": "Invariant", - "polycub": "PolyCub", - "steakhut": "SteakHut", - "viva": "Viva", - "cryptex-v2": "Cryptex V2", - "degen-haus": "Degen Haus", - "themis-protocol": "Themis Protocol", - "bamboodefi": "BambooDeFi", - "aperocket": "ApeRocket", - "sumswap": "SumSwap", - "shinobi": "Shinobi", - "unitrade": "UniTrade", - "crow-finance": "Crow Finance", - "fuzio": "Fuzio", - "kava-boost": "Kava Boost", - "mover": "Mover", - "veplus": "Veplus", - "soulswap": "SoulSwap", - "suterusu": "Suterusu", - "swapfish": "SwapFish", - "zkfox": "zkFox", - "duckpools": "duckpools", - "darkness": "Darkness", - "orion-money": "Orion Money", - "fodl-finance": "FODL Finance", - "cennznet-bridge": "CENNZnet Bridge", - "agility-lsd": "Agility LSD", - "impls": "IMPLS", - "anchorswap": "AnchorSwap", - "polly-finance": "Polly Finance", - "tetu-swap": "Tetu Swap", - "auctus": "Auctus", - "picnic": "Picnic", - "mind-games": "MIND Games", - "mycelium-perpetual-swaps": "Mycelium Perpetual Swaps", - "torches": "Torches", - "ubiquity-dao": "Ubiquity DAO", - "mufex": "MUFEX", - "btngroup": "BtnGroup", - "monolend": "MonoLend", - "boneswap-bonerium": "BoneSwap Bonerium", - "swapline": "Swapline", - "evmoswap": "EvmoSwap", - "ordernchaos": "OrderNChaos", - "palmswap": "Palmswap", - "dehive": "DeHive", - "luckychip": "LuckyChip", - "spin-spot": "Spin Spot", - "magicfox-swap": "MagicFox Swap", - "wowswap": "WOWswap", - "adax-pro": "ADAX Pro", - "etha-lend": "ETHA Lend", - "omnidex-lend": "Omnidex Lend", - "0.exchange": "0.exchange", - "amulet-liquidity-staking": "Amulet Liquidity Staking", - "defrost": "Defrost", - "y2r": "Y2R", - "stablekoi": "StableKoi", - "jexchange": "Jexchange", - "kwikswap": "KwikSwap", - "lfgswap-ethereumpow": "LFGSwap EthereumPoW", - "shield": "Shield", - "baguette": "Baguette", - "optyfi": "Optyfi", - "kapinus": "Kapinus", - "poison-finance": "POISON FINANCE", - "whitehole-finance": "Whitehole Finance", - "solunea": "Solunea", - "mole": "Mole", - "halodao": "HaloDAO", - "finext-finance": "Finext Finance", - "mm-stableswap-polygon": "MM Stableswap Polygon", - "mochiswap": "MochiSwap", - "arbinyan": "ArbiNYAN", - "pixelswap": "PixelSwap", - "spacefi-evmos": "SpaceFi Evmos", - "tenx": "Tenx", - "aave-arc": "Aave Arc", - "wefi": "wefi", - "stargaze": "Stargaze", - "zk-swap": "zk-Swap", - "oddz": "Oddz", - "velocimeter-v1": "Velocimeter V1", - "mobius-finance": "Mobius Finance", - "behodler": "Behodler", - "treedefi": "TreeDefi", - "duet-protocol": "Duet Protocol", - "domination-finance": "Domination Finance", - "fxdx": "FXDX", - "roketo": "Roketo", - "metavault-binary-options": "Metavault Binary Options", - "jet-v1": "Jet V1", - "cgx-finance": "CGX Finance", - "phoenix-finance": "Phoenix Finance", - "frost-finance": "Frost Finance", - "daolama": "DAOLama", - "paribus": "Paribus", - "memedex": "MemeDex", - "matter-defi": "Matter Defi", - "mahalend": "MahaLend", - "magik-farm": "Magik Farm", - "yetiswap": "YetiSwap", - "arrow": "Arrow", - "dungeonswap": "DungeonSwap", - "loop-finance": "Loop Finance", - "starkex": "StarkEx", - "euler": "Euler", - "rskswap": "RskSwap", - "complifi": "CompliFi", - "doge-shrek": "Doge Shrek", - "creditum": "Creditum", - "cougarswap": "CougarSwap", - "babena": "Babena", - "polygonfarm-finance": "PolygonFarm Finance", - "unbound": "Unbound", - "meridian-trade": "Meridian Trade", - "matrix-farm": "Matrix Farm", - "tomb": "Tomb", - "latteswap": "LatteSwap", - "paint-swap": "Paint Swap", - "interest-protocol-sui": "Interest Protocol-SUI", - "rocifi-v2": "RociFi V2", - "kaco-finance": "KACO Finance", - "0x0.ai": "0x0.ai", - "ester-finance": "Ester Finance", - "cgo-finance": "CGO Finance", - "timeswap-v1": "Timeswap V1", - "vax": "VAX", - "aptoswap": "Aptoswap", - "zena-finance": "Zena Finance", - "capitaldex": "CapitalDEX", - "polkaex": "PolkaEx", - "emiswap": "EmiSwap", - "spice-trade": "Spice Trade", - "tempest-capital": "Tempest Capital", - "primitive": "Primitive", - "hunny-finance": "Hunny Finance", - "governor-dao": "Governor DAO", - "hedgehog": "Hedgehog", - "mfinance": "MFinance", - "cesta-finance": "Cesta Finance", - "foodcourt": "Foodcourt", - "onering-v2": "OneRing V2", - "topshelf": "TopShelf", - "oin-finance": "Oin Finance", - "tulip": "Tulip", - "wingswap": "WingSwap", - "kebab-finance": "Kebab Finance", - "crescentswap": "CrescentSwap", - "sobal": "Sobal", - "thoreum-finance": "Thoreum Finance", - "forteswap": "Forteswap", - "klend": "KLend", - "parrot-defi": "Parrot Defi", - "tonic": "Tonic", - "vovo-finance": "Vovo Finance", - "lixir-finance": "Lixir Finance", - "autotronic": "Autotronic", - "bagels-finance": "Bagels Finance", - "spoon-exchange": "Spoon Exchange", - "dinoswap": "DinoSwap", - "saitaswap": "SaitaSwap", - "fringe": "Fringe", - "valleyswap": "ValleySwap", - "lif3-trade": "Lif3 Trade", - "protofi": "ProtoFi", - "aux-exchange": "AUX Exchange", - "bright-union": "Bright Union", - "magic-land": "Magic Land", - "harbor-protocol": "Harbor Protocol", - "fedex-finance": "FeDex Finance", - "bagelswap": "Bagelswap", - "vortex-protocol": "Vortex Protocol", - "whale-game": "Whale Game", - "sandclock": "SandClock", - "omnidex-swap": "OmniDex Swap", - "magician-mv": "Magician MV", - "hakuswap": "HakuSwap", - "prism-protocol": "PRISM Protocol", - "juicebox-v2": "Juicebox V2", - "mymetatrader": "MyMetaTrader", - "ampleswap": "AmpleSwap", - "numoen": "Numoen", - "exit10": "EXIT10", - "potluck-protocol": "Potluck Protocol", - "hswap": "HSwap", - "pls2e": "Pls2e", - "pop-town": "Pop Town", - "fluidtokens": "FluidTokens", - "singular-farm": "Singular Farm", - "zappy": "Zappy", - "bluemove-dex": "BlueMove DEX", - "multiplier": "Multiplier", - "duckydefi": "DuckyDeFi", - "acoconut": "ACoconut", - "waka-finance": "Waka Finance", - "tesseract": "Tesseract", - "cybertime": "CyberTime", - "llamalend": "Llamalend", - "aladdin-dao": "Aladdin DAO", - "symmetric": "Symmetric", - "warp-protocol": "Warp Protocol", - "garbi-protocol": "Garbi Protocol", - "backed": "Backed", - "duneswap": "DuneSwap", - "zlot": "zLot", - "everrise": "EverRise", - "stabilize": "Stabilize", - "fluity": "Fluity", - "tropical-finance": "Tropical Finance", - "lemuria-finance": "Lemuria Finance", - "amulet-protocol": "Amulet Protocol", - "strx-finance": "STRX Finance", - "vaulty-finance": "Vaulty Finance", - "qubit": "Qubit", - "ponyswap": "PonySwap", - "gravis-finance": "Gravis Finance", - "pawnfi-ape-staking": "Pawnfi APE Staking", - "cyborgswap": "CyborgSwap", - "uniwswap-amm": "UniWswap AMM", - "mmo-finance": "MMO Finance", - "flame-defi": "Flame DeFi", - "veniceswap": "VeniceSwap", - "ottopia": "Ottopia", - "animeswap": "AnimeSwap", - "waterfall-bsc": "Waterfall BSC", - "rocifi-v1": "RociFi V1", - "goober": "Goober", - "typhoon-cash": "Typhoon Cash", - "arcadia-finance": "Arcadia Finance", - "meowswapfi": "MeowSwapFi", - "animal-farm-v2": "Animal Farm V2", - "mobydex": "MobyDEX", - "teddy-cash": "Teddy Cash", - "shadecash": "ShadeCash", - "covo-v1": "Covo V1", - "omt-finance": "OMT Finance", - "cone": "Cone", - "bullionfx": "BullionFX", - "dexilla": "Dexilla", - "pearzap": "PearZap", - "spacedex": "SpaceDex", - "deltatheta": "DeltaTheta", - "diamond-protocol": "Diamond Protocol", - "orbit": "Orbit", - "coslend": "Coslend", - "equito-finance": "Equito Finance", - "redemption": "Redemption", - "bluebit": "BlueBit", - "croswap": "CroSwap", - "agora": "Agora", - "collectif-dao": "Collectif DAO", - "e3": "E3", - "oxygen": "Oxygen", - "trick-or-treat-farm": "Trick or Treat Farm", - "moraswap": "Moraswap", - "fairyswap-v1": "FairySwap V1", - "canary": "Canary", - "excalibur": "Excalibur", - "cobraswap": "CobraSwap", - "xusd-money": "XUSD Money", - "heraswap": "Heraswap", - "morpheus-swap": "Morpheus Swap", - "throne-v3": "Throne V3", - "pairex": "PairEx", - "hodltree": "Hodltree", - "cryptoswap": "CryptoSwap", - "forge-sx-ovens": "Forge SX Ovens", - "unifi": "UniFi", - "rocketswap": "RocketSwap", - "chaotic-finance": "Chaotic Finance", - "zoocoin": "ZooCoin", - "hpdex": "Hpdex", - "wild-credit": "Wild Credit", - "lachain-yield-market": "Lachain Yield Market", - "mushrooms-finance": "Mushrooms Finance", - "leonicornswap": "LeonicornSwap", - "orcadao": "OrcaDAO", - "thorusfi": "ThorusFi", - "sashimiswap": "SashimiSwap", - "kokomoswap": "KokomoSwap", - "crust-finance": "Crust Finance", - "gplx": "GPLX", - "miningtycoon": "MiningTycoon", - "cantoswap": "CantoSwap", - "predy-v3.2": "Predy V3.2", - "ocp-finance": "OCP Finance", - "galoswap": "GaloSwap", - "macaronswap": "MacaronSwap", - "sonicswap": "SonicSwap", - "swapperchan": "SwapperChan", - "nftperp": "nftperp", - "ladex-exchange": "Ladex Exchange", - "uniwswap-unia-farms": "UniWswap UNIA Farms", - "mantleswap": "MantleSwap", - "emdx": "EMDX", - "dpex": "DPEX", - "polydex-cryption-network": "PolyDEX-Cryption Network", - "holdr": "Holdr", - "auto-core": "Auto Core", - "beefstake": "BeefStake", - "oh-finance": "Oh Finance", - "lockless-protocol": "Lockless Protocol", - "mayfair-fund": "Mayfair Fund", - "cycle-finance": "Cycle Finance", - "obric": "Obric", - "mooncake-finance": "Mooncake Finance", - "cykura": "Cykura", - "neuron": "neuron", - "klondike-finance": "Klondike Finance", - "adaswap": "AdaSwap", - "amun": "Amun", - "yapeswap": "Yapeswap", - "dexter-exchange": "Dexter Exchange", - "bao-baskets": "BAO Baskets", - "litx": "Litx", - "chfry-finance": "CHFRY Finance", - "cofix": "CoFiX", - "investin": "Investin", - "electrik-finance": "Electrik Finance", - "1swap": "1Swap", - "bayswap": "BaySwap", - "mahadao-arth": "MahaDAO Arth", - "fcon-dex": "FCON DEX", - "gton-capital": "GTON Capital", - "genesys": "Genesys", - "zkmoonswap": "zkMoonSwap", - "robiniaswap": "RobiniaSwap", - "pepedex": "PepeDex", - "forge-sx-trade": "Forge SX Trade", - "sterling-finance": "Sterling Finance", - "universe-finance": "Universe Finance", - "bluespade": "BlueSpade", - "predy-v2": "Predy V2", - "huckleberry-lending": "Huckleberry Lending", - "vanilla": "Vanilla", - "lootswap": "LootSwap", - "vee-finance": "Vee Finance", - "dapp-account-dao": "Dapp Account DAO", - "narwhal-finance": "Narwhal Finance", - "sherpa-cash": "Sherpa Cash", - "jioswap": "JioSwap", - "stacker-ventures": "Stacker Ventures", - "sleepearn-finance": "SleepEarn Finance", - "subzero-zswap": "Subzero ZSwap", - "seadex": "SeaDex", - "flashliquidity": "FlashLiquidity", - "croblanc": "Croblanc", - "hydradex-v2": "Hydradex V2", - "dexfinance-bsc": "DexFinance BSC", - "bank-of-chain": "Bank of Chain", - "aperture-finance": "Aperture Finance", - "piggy": "Piggy", - "boofinance": "BooFinance", - "versa-finance": "Versa Finance", - "astar-exchange-v2": "Astar Exchange V2", - "alium-swap": "Alium Swap", - "fairyswap-v2": "FairySwap V2", - "blindex": "Blindex", - "yieldfields": "YieldFields", - "mm-stableswap": "MM Stableswap", - "panda-chef": "Panda Chef", - "mytrade": "MyTrade", - "pegasusdollar": "Pegasusdollar", - "partyswap": "PartySwap", - "chee-finance": "Chee Finance", - "solyard": "Solyard", - "goswap": "GoSwap", - "cashcow-protocol": "CashCow Protocol", - "kinesis-labs": "Kinesis Labs", - "qilin": "Qilin", - "smartdex": "SmartDEX", - "spiraldao": "SpiralDao", - "vedao": "veDAO", - "zkswap-(zksync)": "ZKSwap (ZKSync)", - "clever-protocol": "Clever Protocol", - "2pi-network": "2Pi Network", - "safeswap": "SafeSwap", - "openswap": "OpenSwap", - "naos-finance": "NAOS Finance", - "orbitalswap": "OrbitalSwap", - "basetrade": "Basetrade", - "deflex": "Deflex", - "asol": "aSOL", - "arbiswap": "ArbiSwap", - "liquid-bolt": "Liquid Bolt", - "polyshield": "PolyShield", - "newland": "Newland", - "tifi": "TiFi", - "opendao": "OpenDAO", - "yfdai": "YFDAI", - "shibshift": "ShibShift", - "sugarswap": "SugarSwap", - "zabu-finance": "Zabu Finance", - "manhattan": "Manhattan", - "ladao-xocolatl": "LaDAO Xocolatl", - "coinscope": "Coinscope", - "kimberlite": "Kimberlite", - "sharky-swap": "Sharky Swap", - "gaj-finance": "Gaj Finance", - "houstonswap": "HoustonSwap", - "omnidex-perpetuals": "OmniDex Perpetuals", - "konverter": "Konverter", - "squid-stake": "Squid Stake", - "kuu-finance": "Kuu Finance", - "1bch": "1BCH", - "cronus-finance": "Cronus Finance", - "monox": "MonoX", - "bossswap": "BossSwap", - "shiba-fantom": "Shiba Fantom", - "arctic": "Arctic", - "kryptodex": "KryptoDex", - "koala-defi": "Koala Defi", - "phoenix-dex": "Phoenix DEX", - "axia-protocol": "Axia Protocol", - "polkamarkets": "Polkamarkets", - "lsdx-farm": "LSDx Farm", - "animal-farm-v1": "Animal Farm V1", - "dddx": "DDDX", - "seaswap": "SeaSwap", - "corgiswap": "CorgiSwap", - "reax-swaps": "Reax Swaps", - "alligator": "Alligator", - "gin-finance": "Gin Finance", - "robo-vault": "Robo-Vault", - "bryan-on-base": "Bryan on Base", - "wojak-finance": "Wojak Finance", - "fuzz-finance": "Fuzz Finance", - "surfswap-classic": "Surfswap Classic", - "banksy-farm": "Banksy Farm", - "neutroswap": "Neutroswap", - "arenaswap": "ArenaSwap", - "sharpe-magnum": "Sharpe Magnum", - "anedak": "Anedak", - "fourdex": "FourDex", - "dark-matter": "Dark Matter", - "shibance": "Shibance", - "mercurity": "Mercurity", - "jojo": "JOJO", - "beglobal-finance": "BeGlobal Finance", - "sapphire-defi": "Sapphire Defi", - "moonbase-alpha": "Moonbase Alpha", - "tegro-finance": "Tegro Finance", - "stormswap": "StormSwap", - "swapp": "SWAPP", - "tempus-finance": "Tempus Finance", - "drachma": "Drachma", - "pwn": "PWN", - "cowswap-smartbch": "CowSwap smartBCH", - "milkomeda-c1-djed": "Milkomeda-C1 Djed", - "bishares": "BiShares", - "embr-finance": "Embr Finance", - "powaa-protocol": "Powaa Protocol", - "lendhub": "LendHub", - "edge-protocol": "Edge Protocol", - "minerva": "Minerva", - "polyyeld-finance": "PolyYeld Finance", - "glitter-finance": "Glitter Finance", - "goblingold": "GoblinGold", - "olivedao": "OliveDAO", - "neuronswap": "Neuronswap", - "symblox": "Symblox", - "arbirise-finance": "ArbiRise Finance", - "dino.exchange": "Dino.Exchange", - "brinc-finance": "Brinc Finance", - "ysl.io": "YSL.IO", - "marshmallowdefi": "MarshmallowDeFi", - "powswap": "PoWSwap", - "daoventures": "DAOventures", - "spherium": "Spherium", - "eternal-finance": "Eternal Finance", - "yieldpulse-finance": "YieldPulse Finance", - "stakedicp": "StakedICP", - "factor-v1": "Factor V1", - "swaprum": "Swaprum", - "pear-dao": "Pear DAO", - "entropyfi": "Entropyfi", - "prophet": "Prophet", - "skeleton-finance": "Skeleton Finance", - "weld-finance": "Weld Finance", - "bscstation": "BSCStation", - "dolphinswap": "DolphinSwap", - "catsluck": "CatsLuck", - "oortswap": "OortSwap", - "aquarius": "Aquarius", - "deus-finance": "DEUS Finance", - "energyfi": "Energyfi", - "logium": "Logium", - "ithil": "Ithil", - "yogurt-finance": "Yogurt Finance", - "krav": "KRAV", - "bnpl-pay": "BNPL Pay", - "buffaloswap": "Buffaloswap", - "iceswap": "IceSwap", - "carbon-finance": "Carbon Finance", - "bounce-finance": "Bounce Finance", - "unknown": "Unknown", - "sifchain": "Sifchain", - "dogmoney": "DogMoney", - "sweep-n-flip": "Sweep n Flip", - "baker-guild": "Baker Guild", - "crafting-finance": "Crafting Finance", - "smbswap-v2": "SMBSwap V2", - "symphony-finance": "Symphony Finance", - "fantompup": "FantomPup", - "bob-cdp": "BOB CDP", - "psy": "PSY", - "barnswap": "BarnSwap", - "saharadao": "SaharaDAO", - "double-club": "Double Club", - "raven": "Raven", - "onavax": "onAVAX", - "brainiac-finance": "Brainiac Finance", - "cochilli": "CoChilli", - "yieldhub-finance": "YieldHub Finance", - "borgswap": "BorgSwap", - "future-swap": "Future Swap", - "amaterasu-finance": "Amaterasu Finance", - "salem-finance": "Salem Finance", - "esdtmarket": "EsdtMarket", - "fatfire-defi": "FATFIRE DEFI", - "radial": "Radial", - "surge": "Surge", - "predy-v3": "Predy V3", - "vitcswap": "VITCSwap", - "beamswap-stable-amm": "BeamSwap Stable AMM", - "bitgert-swap": "Bitgert Swap", - "protossdex": "ProtossDex", - "earnmos": "Earnmos", - "onering-v1": "OneRing V1", - "algodex": "Algodex", - "defirex": "Defirex", - "purefi": "PureFi", - "soluna": "Soluna", - "agile-finance": "Agile Finance", - "freeriver": "FreeRiver", - "galador": "Galador", - "hopswap": "HopSwap", - "czodiac": "CZODIAC", - "zkevmswap": "zkEVMSwap", - "zyberswap-stableswap": "ZyberSwap Stableswap", - "elasticswap": "ElasticSwap", - "kalata": "Kalata", - "frogge.finance": "Frogge.Finance", - "cyber-dog": "Cyber Dog", - "polyquail": "Polyquail", - "casinocronos": "casinocronos", - "etcswap": "ETCswap", - "zkswap-era": "zkSwap Era", - "gondola-finance": "Gondola Finance", - "vultureswap": "VultureSwap", - "argano": "Argano", - "qian": "QIAN", - "kingdefi": "KingDefi", - "infinity-pad": "Infinity Pad", - "retrodefi": "RetroDefi", - "sculptor-finance": "Sculptor Finance", - "chocobase": "ChocoBase", - "farm-hero": "Farm Hero", - "flashstake-old": "Flashstake OLD", - "cryptoyieldfocus": "CryptoYieldFocus", - "blackbird-finance": "BlackBird Finance", - "openworld": "Openworld", - "etherberry-finance": "Etherberry Finance", - "goosefx": "GooseFX", - "smbswap-v3": "SMBSwap V3", - "1beam": "1Beam", - "greenhouse": "Greenhouse", - "fortress-loans": "Fortress Loans", - "fndz": "FNDZ", - "quasarswap": "QuasarSwap", - "atlantis-loans": "Atlantis Loans", - "metavisor": "Metavisor", - "tfm": "TFM", - "emberswap": "EmberSwap", - "polyroll": "Polyroll", - "wraithswap": "WraithSwap", - "farmtom": "Farmtom", - "the-grand-banks": "The Grand Banks", - "fatexfi": "FATExFi", - "tropykus-zkevm": "Tropykus zkEVM", - "tonic-cash": "Tonic Cash", - "factor-v2": "Factor V2", - "ags-finance": "AGS Finance", - "kyrios-finance": "Kyrios Finance", - "elephantdex": "Elephantdex", - "oni-exchange": "Oni Exchange", - "seeder-finance": "Seeder Finance", - "milko-farm": "Milko Farm", - "spectrefi": "SpectreFi", - "skullswap-v3": "SkullSwap V3", - "lunagens": "Lunagens", - "infinitorr": "InfiniTORR", - "bourbon-defi": "Bourbon Defi", - "robo-advisor-for-yield": "Robo-Advisor for Yield", - "summit-defi": "Summit Defi", - "derify-protocol": "Derify Protocol", - "antex": "Antex", - "blizzard": "Blizzard", - "superman-swap": "Superman Swap", - "zigzag": "ZigZag", - "nava-finance": "Nava finance", - "syrup-finance": "Syrup Finance", - "powerswap": "PowerSwap", - "waterfall-finance-yield": "Waterfall Finance Yield", - "onyxdao": "OnyxDAO", - "funbeast": "FunBeast", - "frog-nation-farm": "Frog Nation Farm", - "chargedefi": "ChargeDeFi", - "line-token": "LINE Token", - "scallop-pool": "Scallop Pool", - "milkydex": "Milkydex", - "hermes-defi": "Hermes Defi", - "damx": "DAMX", - "firedao": "FireDAO", - "uwu-protocol": "UWU Protocol", - "dynastyswap": "DynastySwap", - "smolswap": "SmolSwap", - "scarecrow-finance": "ScareCrow Finance", - "kanvas": "Kanvas", - "bonqdao": "BonqDAO", - "zenith": "Zenith", - "the-seed-farm": "The Seed Farm", - "hotfriescoin": "Hotfriescoin", - "waterfall-dex": "Waterfall DEX", - "bao-markets": "BAO Markets", - "synthex": "SynthEx", - "grappa-finance": "Grappa Finance", - "luchadores": "Luchadores", - "scaryswap": "ScarySwap", - "kungfuu": "KungFuu", - "lowcostswap": "LowcostSwap", - "scion-finance": "Scion Finance", - "yumiswap": "Yumiswap", - "trustswap": "TrustSwap", - "pinyottas": "Pinyottas", - "gamut-exchange": "Gamut Exchange", - "sweetswap": "SweetSwap", - "kimochi": "Kimochi", - "lending-pond": "Lending Pond", - "oaswap": "Oaswap", - "billion-happiness": "Billion Happiness", - "avaware": "Avaware", - "ammos-finance": "Ammos Finance", - "satin-exchange": "Satin Exchange", - "kronosdao": "KronosDAO", - "wagmidao": "WagmiDAO", - "north-pole": "North Pole", - "whaleswap": "WhaleSwap", - "parallel-daofi": "Parallel DAOfi", - "intswap": "Intswap", - "obswap": "OBSwap", - "artemis-protocol": "Artemis Protocol", - "ifswap": "IFSwap", - "unamano": "Unamano", - "furylabs-finance": "FuryLabs Finance", - "boobs.finance": "boobs.finance", - "lemonswap": "LemonSwap", - "cronofi-finance": "Cronofi Finance", - "tendieswap": "TendieSwap", - "optinyan": "OptiNyan", - "photon-protocol": "Photon Protocol", - "genesis-finance": "Genesis Finance", - "spiritdao": "SpiritDAO", - "eoswap-finance": "EOSWAP Finance", - "pillar": "Pillar", - "cookiebase-farm": "CookieBase Farm", - "nexter": "Nexter", - "monolith": "Monolith", - "baptswap": "BaptSwap", - "kivach": "Kivach", - "chocodoge": "Chocodoge", - "swapmatic": "SwapMatic", - "defily-dex": "Defily Dex", - "mintswap": "MintSwap", - "honkswap": "HonkSwap", - "koyo-finance": "Koyo Finance", - "moret": "Moret", - "meowswap": "Meowswap", - "xcarnival": "XCarnival", - "plsprint": "PlsPrint", - "auraswap": "AuraSwap", - "fruits-of-ryoshi": "Fruits of Ryoshi", - "zeus-finance": "Zeus Finance", - "helios-prime": "Helios Prime", - "defyswap": "DefySwap", - "sunflowerswap": "SunflowerSwap", - "savanna-finance": "Savanna Finance", - "bring.finance": "Bring.Finance", - "hamburger-finance": "Hamburger Finance", - "pegasus-finance": "Pegasus Finance", - "pyswap": "PySwap", - "garble.money": "Garble.Money", - "unbanked": "Unbanked", - "pyeswap": "PYEswap", - "superbots": "SuperBots", - "zenithswap": "ZenithSwap", - "openbisea": "OpenBiSea", - "liquidrium": "Liquidrium", - "abcdefx": "ABcDeFX", - "phission-finance": "Phission Finance", - "brightside": "BrightSide", - "pulsar-money": "Pulsar Money", - "energon-dex": "Energon DEX", - "dove-swap-v2": "Dove Swap V2", - "trainswap": "TrainSwap", - "aliensfarm": "AliensFarm", - "chintai": "Chintai", - "palma-swap": "Palma Swap", - "lavafall": "Lavafall", - "quickswap-lend": "Quickswap Lend", - "avatr": "Avatr", - "sierra": "Sierra", - "spartacus-exchange": "Spartacus Exchange", - "darkswap": "DarkSwap", - "piratedao": "PirateDAO", - "demodyfi": "Demodyfi", - "swift-finance": "Swift Finance", - "zinax-dao": "Zinax DAO", - "augmented-finance": "Augmented Finance", - "clayswap": "ClaySwap", - "coreswap": "CoreSwap", - "lenft": "leNFT", - "oakfarm": "OakFarm", - "brise-swap": "Brise Swap", - "sagebet": "SageBet", - "fastyield-finance": "Fastyield Finance", - "grassland-finance": "GrassLand Finance", - "throne-v2": "Throne V2", - "meso-finance": "Meso Finance", - "juggler-red": "Juggler Red", - "bas3d": "BAS3D", - "themis-lsdfil": "Themis-lsdFil", - "collection.xyz": "Collection.xyz", - "fenrir-finance": "Fenrir Finance", - "smartdexbch": "SmartDEXBCH", - "metapoly": "Metapoly", - "phantom-finance": "Phantom Finance", - "westarter": "WeStarter", - "autobark": "AutoBark", - "grave-finance": "Grave Finance", - "scranton-finance": "Scranton Finance", - "zukeswap": "ZukeSwap", - "dogedollar": "DogeDollar", - "dogemuskswap": "DogeMuskSwap", - "powsea": "PowSea", - "bridgekek": "BridgeKek", - "spectrumswap": "Spectrumswap", - "mockingbird": "Mockingbird", - "hippowswap": "HipPoWSwap", - "definer": "DeFiner", - "x-blue-finance": "X Blue Finance", - "stronghands": "Stronghands", - "metaswap": "MetaSwap", - "zolidly-finance": "Zolidly Finance", - "tomofinance": "TomoFinance", - "starbank": "Starbank", - "moonlift": "MoonLift", - "skullswap-v2": "SkullSwap V2", - "champagneswap": "ChampagneSwap", - "astar-exchange-v3": "Astar Exchange V3", - "deficurrent": "DefiCurrent", - "starfish-liquid-staking": "Starfish Liquid Staking", - "xray-swap": "Xray Swap", - "defi-yield-protocol": "Defi Yield Protocol", - "draco-force": "Draco Force", - "l2finance": "L2FINANCE", - "stake1": "Stake1", - "senpaiswap": "SenpaiSwap", - "jbc.finance": "JBC.Finance", - "ko.one": "ko.one", - "v3s-finance": "V3S Finance", - "jaguarswap": "JaguarSwap", - "pandora-swap": "Pandora Swap", - "multi-chain-miner": "Multi-Chain Miner", - "emerald-swap": "Emerald Swap", - "jlswap": "JLSwap", - "bencufi": "BencuFi", - "life": "LIFE", - "afraswap": "AfraSwap", - "gamblefi": "Gamblefi", - "dogepup": "DogePup", - "goat-swap": "Goat Swap", - "evolve": "Evolve", - "octane-finance": "Octane Finance", - "troydefi": "TroyDefi", - "lazyfi": "LazyFi", - "sahara-exchange": "Sahara Exchange", - "beluga-protocol": "Beluga Protocol", - "gmcash": "GmCash", - "mirai": "Mirai", - "luxsfi": "LuxsFi", - "elonswap": "ElonSwap", - "squid-defi": "Squid-Defi", - "sunflower-finance": "SunFlower Finance", - "risq-protocol": "Risq Protocol", - "bridge-lz": "Bridge LZ", - "spiral-finance": "Spiral Finance", - "asgard-dao": "Asgard Dao", - "cranium-exchange": "Cranium Exchange", - "thegoblins": "theGoblins", - "tenet": "Tenet", - "mymine": "MyMine", - "numisme": "NumisMe", - "newspace-finance": "NewSpace Finance", - "bank-of-cronos-loans": "Bank of Cronos Loans", - "solana-farm": "Solana Farm", - "cantohm": "cantOHM", - "printyfinance": "printyfinance", - "astral-farm": "Astral Farm", - "ice-colony": "Ice Colony", - "just-yield": "Just Yield", - "jupiter-swap": "Jupiter Swap", - "white-whale-vaults": "White Whale Vaults", - "waterloan": "WaterLoan", - "knights-of-fantom": "Knights of Fantom", - "vampireswap": "VampireSwap", - "boneswap": "BoneSwap", - "deerfi": "DeerFi", - "bitant": "BitANT", - "sugar-swap": "Sugar Swap", - "jujube-finance": "Jujube Finance", - "waterdendy": "WaterDendy", - "eggtartswap": "EggTartSwap", - "s-finance": "S Finance", - "bitlend": "Bitlend", - "omni-protocol": "Omni Protocol", - "mapledefi": "MapleDeFi", - "steadefi": "Steadefi", - "earnifty": "Earnifty", - "moneyrain-finance": "Moneyrain Finance", - "inuswap": "Inuswap", - "cupid-farm": "Cupid Farm", - "bfx-finance": "BFX Finance", - "adena-finance": "Adena Finance", - "defil": "DeFIL", - "cuanswap": "CuanSwap", - "bodh-finance": "Bodh Finance", - "nightmare-finance": "Nightmare Finance", - "frog-swap": "Frog Swap", - "pankuku": "PanKUKU", - "vivelabouje": "ViveLaBouje", - "deridex": "Deridex", - "metacrono": "MetaCrono", - "colafactory": "ColaFactory", - "sharelock": "Sharelock", - "antihero-finance": "AntiHero Finance", - "cemetery-finance": "Cemetery Finance", - "apedao": "ApeDAO", - "defi-halal": "Defi Halal", - "nex": "NEX", - "cyberfantasyfembots": "CyberFantasyFembots", - "bastilledelabouje": "BastilleDeLaBouje", - "exodia": "Exodia", - "atodex": "AtoDEX", - "octolsd": "OctoLSD", - "coffin-finance": "Coffin Finance", - "kekswap": "KekSwap", - "milky-cow": "Milky Cow", - "shiftdollar": "ShiftDollar", - "the-dragons-lair": "The Dragon's Lair", - "paperdao": "PaperDAO", - "gemmine": "GemMine", - "cakewswap": "CakeWSwap", - "kasava-dex": "Kasava Dex", - "moondao": "MoonDao", - "dubble-exchange": "Dubble Exchange", - "dogium-farm": "Dogium Farm", - "traddify-arbitrage": "Traddify Arbitrage", - "life-dao": "Life DAO", - "zest-protocol": "Zest Protocol", - "coindrip": "CoinDrip", - "apexswap": "ApexSwap", - "wtfdoge": "WTFDOGE", - "owna": "Owna", - "hotpot-finance": "Hotpot Finance", - "jetmine-dao": "JetMine DAO", - "swirllend": "SwirlLend", - "kefirswap": "KefirSwap", - "dogeclaw": "DogeClaw", - "the-story-of-draco": "The Story of Draco", - "procyon": "Procyon", - "astarnova": "Astarnova", - "tarina": "Tarina", - "flux-protocol": "Flux Protocol", - "debets": "Debets", - "baksdao": "BaksDAO", - "minipanther-dao": "MiniPanther DAO", - "stablz": "Stablz", - "silo-protocol": "Silo Protocol", - "trufin-legacy-vaults": "TruFin Legacy Vaults", - "doora-inu": "Doora Inu", - "soda-protocol": "Soda Protocol", - "feeder-finance": "Feeder Finance", - "amogus-dao": "Amogus DAO", - "ruby-finance": "Ruby Finance", - "0xlend": "0xLend", - "vortex-dao": "Vortex DAO", - "sealightswap": "SeaLightSwap", - "voodoo-trade": "Voodoo Trade", - "polytrade": "Polytrade", - "avaviking": "AvaViking", - "elyfi": "ELYFI", - "philetairus-socius": "Philetairus Socius", - "liquify": "Liquify", - "flitswap": "FlitSwap", - "strikex": "StrikeX", - "camel-farm": "Camel Farm", - "doge-compounder": "Doge Compounder", - "zkdefi": "zkDefi", - "pacman": "Pacman", - "structured-lsd": "Structured LSD", - "spiral-fi-mixer": "Spiral Fi Mixer", - "jadeswap": "Jadeswap", - "ketchup-finance": "Ketchup Finance", - "kannagi-finance": "Kannagi Finance", - "securo-finance": "Securo Finance", - "ssap": "SSAP", - "defi-master": "Defi Master", - "milkyway": "MilkyWay", - "rubikfarm": "RubikFarm", - "xmas-past": "Xmas Past", - "treehouse-finance": "TreeHouse Finance", - "1pulse": "1Pulse", - "universeftm": "UniverseFTM", - "genius": "Genius", - "unirex-finance": "Unirex Finance", - "arbiten": "ArbiTen", - "mixswap-finance": "MixSwap Finance", - "nowswap": "NowSwap", - "milkomeda-macc": "Milkomeda MACC", - "2omb-finance": "2omb Finance", - "veritable-dao": "Veritable DAO", - "coup-farm": "Coup Farm", - "v2swap": "v2swap", - "caribou-finance": "Caribou Finance", - "the-don-finance": "The Don Finance", - "mama-dao": "Mama DAO", - "flatcoin-dex": "FlatCoin DEX", - "webribe-dao": "weBribe DAO", - "astrofi": "AstroFi", - "babypig-finance": "BabyPig Finance", - "templedao": "TempleDAO", - "bouje-finance": "Bouje Finance", - "kadefi": "kaDefi", - "opankeswap": "Opankeswap", - "papa-dao": "Papa DAO", - "tornadao": "Tornadao", - "tetu-pawnshop": "Tetu Pawnshop", - "astrowar-finance": "Astrowar Finance", - "crypto-lottery": "Crypto Lottery", - "orio": "ORIO", - "goldbank-finance": "Goldbank Finance", - "ecodefi": "EcoDeFi", - "paladin-dao": "Paladin DAO", - "stipend": "Stipend", - "fei-protocol": "Fei Protocol", - "orcus-finance": "Orcus Finance", - "sapphire-mine": "Sapphire Mine", - "chad-finance": "Chad Finance", - "gaia-dao": "Gaia DAO", - "ftm-frens": "FTM Frens", - "octopow-finance": "OctoPow Finance", - "spade-finance": "Spade Finance", - "soarpls": "SoarPLS", - "beluga": "Beluga", - "tempodao": "TempoDAO", - "kmex": "KMex", - "kswap": "KSwap", - "kapaswap": "KapaSwap", - "zircon-gamma": "Zircon Gamma", - "nirvana": "Nirvana", - "weave": "Weave", - "vesq": "Vesq", - "sigmao": "Sigmao", - "beekava": "Beekava", - "gxypad": "GxyPad", - "baymax": "BayMax", - "kafidao": "KafiDAO", - "octoplace": "OCTOplace", - "undead-finance": "Undead Finance", - "mirrosset": "mirrosset", - "ethwswap": "EthwSwap", - "sashimidao": "SashimiDAO", - "fliperino": "Fliperino", - "pidao-finance": "Pidao Finance", - "metaid": "MetaID", - "infy-protocol": "Infy Protocol", - "alyx-finance": "Alyx finance", - "vacus-finance": "Vacus Finance", - "avalps": "Avalps", - "peaky-finance": "Peaky Finance", - "bakedpotatoes": "BakedPotatoes", - "magnet-dao": "Magnet DAO", - "welnance": "Welnance", - "kavastarter": "Kavastarter", - "maximizer": "Maximizer", - "deepseadao": "DeepSeaDAO", - "nemesis-dao": "Nemesis DAO", - "samodao": "SamoDAO", - "kumu-finance": "Kumu Finance", - "dinotopia-finance": "Dinotopia Finance", - "snowdog": "Snowdog", - "kyborg-exchange": "Kyborg Exchange", - "minerswap": "MinerSwap", - "hectagon": "Hectagon", - "kavacave": "Kavacave", - "dinosaur.finance": "Dinosaur.Finance", - "manifest": "Manifest", - "zilch-capital": "Zilch Capital", - "multiverse": "multiverse", - "taichi": "TaiChi", - "cashio": "Cashio", - "sevenswap": "sevenswap", - "raidshift-marketplace": "RAIDSHIFT Marketplace", - "cthulhu.finance": "Cthulhu.Finance", - "powerbomb-finance": "Powerbomb Finance", - "cookiebase-dex": "CookieBase Dex", - "bchpad": "BCHPad", - "swing-dao": "Swing DAO", - "euphoria": "Euphoria", - "mstable-yield": "mStable Yield", - "parasset": "Parasset", - "talent-protocol": "Talent Protocol", - "minotaur-money": "Minotaur Money", - "omax-swap": "Omax Swap", - "chaintools": "ChainTools", - "kronos-dao": "Kronos DAO", - "real-protocol": "Real Protocol", - "toreus": "Toreus", - "invictusdao": "InvictusDAO", - "mare-finance-v1": "Mare Finance V1", - "houses-of-rome": "Houses of Rome", - "rising-undead": "Rising Undead", - "metavault-dao": "Metavault DAO", - "lago-bridge": "Lago Bridge", - "valhalladao": "ValhallaDAO", - "unclesam-protocol": "UncleSam Protocol", - "sohei": "Sohei", - "ice-dao": "Ice DAO", - "arbicheems": "ArbiCheems", - "nest-protocol-staking": "Nest Protocol Staking", - "dios-finance": "Dios Finance", - "zeroshift": "ZeroShift", - "asgard-finance": "Asgard Finance", - "stabledoin": "StableDoin", - "piggybank-dao": "Piggybank DAO", - "balloonswap": "BalloonSwap", - "fantasm": "Fantasm", - "bepswap": "Bepswap", - "dracula": "Dracula", - "dev-protocol": "Dev Protocol", - "synlev": "SynLev", - "bt.finance": "BT.Finance", - "n3rd-finance": "N3RD Finance", - "allinx": "Allinx", - "mirror": "Mirror", - "mango-markets-v3": "Mango Markets V3", - "anchor": "Anchor", - "stacks-staking": "Stacks Staking", - "bondappetit": "BondAppetit", - "wault": "Wault", - "ruler": "Ruler", - "yogi-finance": "Yogi Finance", - "olympus-dao": "Olympus DAO", - "yieldly": "Yieldly", - "armor": "Armor", - "merlin": "Merlin", - "cardstarter": "Cardstarter", - "mint-club": "Mint Club", - "acumen": "Acumen", - "longdrink": "Longdrink", - "witswap": "WitSwap", - "loterra": "LoTerra", - "pylon-protocol": "Pylon Protocol", - "illuvium": "Illuvium", - "telcoin": "Telcoin", - "ondo-v1-(legacy)": "Ondo v1 (Legacy)", - "mensa-protocol": "Mensa Protocol", - "avalaunch": "Avalaunch", - "pizza": "Pizza", - "wonderland": "Wonderland", - "genshiro": "Genshiro", - "fabric": "Fabric", - "sunrise-gaming": "Sunrise Gaming", - "shapeshift": "ShapeShift", - "banano": "Banano", - "ecurve": "eCurve", - "amy-finance": "Amy Finance", - "bogged-finance": "Bogged Finance", - "beanstalk": "Beanstalk", - "totemfi": "TotemFi", - "sync-network": "SYNC Network", - "almond": "Almond", - "only1": "Only1", - "fegex": "Fegex", - "oraichain": "OraiChain", - "bacondao": "BaconDAO", - "yel-finance": "YEL Finance", - "smartcoin": "SmartCoin", - "decubate": "Decubate", - "nasdex": "Nasdex", - "nileriver": "Nileriver", - "nexus-protocol": "Nexus Protocol", - "spartacus": "Spartacus", - "klima-dao": "Klima DAO", - "atmossoft": "AtmosSoft", - "squiddao": "SquidDao", - "gyro": "Gyro", - "snowbank": "Snowbank", - "blizz-finance": "Blizz Finance", - "hector-treasury": "Hector Treasury", - "hades-swap": "Hades Swap", - "bunnypark": "BunnyPark", - "capital-dao": "Capital DAO", - "axedao": "AxeDAO", - "fortune-dao": "Fortune DAO", - "bear-finance": "Bear Finance", - "reverse": "Reverse", - "saffron-finance": "Saffron Finance", - "defily": "Defily", - "safeohm": "SafeOHM", - "position-exchange": "Position Exchange", - "8ightdao": "8ightDAO", - "onedao-finance": "OneDAO Finance", - "paraswap": "Paraswap", - "blackpool": "BlackPool", - "everestdao": "EverestDAO", - "dao-maker-vesting": "DAO Maker Vesting", - "metaversepro": "Metaversepro", - "fortress": "Fortress", - "radao": "RaDao", - "xensa": "Xensa", - "nidhi-dao": "Nidhi-Dao", - "hex": "Hex", - "r-u-generous": "R U Generous", - "jade-protocol": "Jade Protocol", - "freeliquid": "FreeLiquid", - "lobis": "Lobis", - "arcx": "ARCx", - "alpaca-city": "Alpaca City", - "fantohm": "Fantohm", - "retreeb": "Retreeb", - "cerberusdao": "CerberusDAO", - "zai-finance": "Zai Finance", - "xeus": "XEUS", - "omicron": "Omicron", - "kafe-finance": "Kafe Finance", - "vikingsfinance": "VikingsFinance", - "galaxygoogle-dao": "GalaxyGoogle DAO", - "honeyfarm": "HoneyFarm", - "midasdao": "MidasDAO", - "o2-dao": "O2 DAO", - "luxor-money": "Luxor Money", - "giza-dao": "Giza DAO", - "colony": "Colony", - "cheesedao": "CheeseDAO", - "pumpkin-dao": "Pumpkin DAO", - "stargate-finance": "Stargate Finance", - "atlas-usv": "Atlas USV", - "rivrkitty": "RivrKitty", - "starterra": "Starterra", - "base-protocol": "Base Protocol", - "bitpif": "BITPIF", - "ragnarokdao": "RagnarokDAO", - "numbers-protocol": "Numbers Protocol", - "unusdao": "UnusDao", - "merit-circle": "Merit Circle", - "leaguedao": "LeagueDAO", - "mobox": "MOBOX", - "sheesha-finance": "Sheesha Finance", - "cakedao": "CakeDAO", - "the-sandbox": "The Sandbox", - "brokoli-network": "Brokoli Network", - "nacho-finance": "Nacho Finance", - "kitty-finance": "Kitty Finance", - "polkastarter": "Polkastarter", - "umbria-network": "Umbria Network", - "iq": "IQ", - "spicy-dao": "Spicy DAO", - "zodiacdao": "ZodiacDAO", - "one-cash": "One Cash", - "stand-cash": "Stand Cash", - "templar-dao": "Templar DAO", - "ara-finance": "Ara Finance", - "securedao": "SecureDAO", - "optidoge": "Optidoge", - "snowcatdao": "SnowCatDAO", - "agarthadao": "AgarthaDAO", - "bourbon-dao": "Bourbon DAO", - "wisteria-swap": "Wisteria Swap", - "louverture": "Louverture", - "kawaiiswap": "KawaiiSwap", - "valkyrie": "Valkyrie", - "umbrella-network": "Umbrella Network", - "unite-finance": "Unite Finance", - "piggy-finance": "Piggy Finance", - "goblin-ftm": "Goblin FTM", - "scarab-finance": "Scarab Finance", - "metareserve": "MetaReserve", - "xenophon": "Xenophon", - "whale-loans": "Whale Loans", - "marginswap": "MarginSwap", - "rarify": "Rarify", - "galatea-cash": "Galatea Cash", - "instrumental-finance": "Instrumental Finance", - "chikn-farm": "Chikn Farm", - "chronicle": "Chronicle", - "ferrum": "Ferrum", - "bananafarm": "BananaFarm", - "predictcoin": "Predictcoin", - "ink-protocol": "INK Protocol", - "voyager": "Voyager", - "karma-dao": "Karma DAO", - "ovr": "OVR", - "trapeza-protocol": "Trapeza Protocol", - "tipidao": "TiPiDao", - "empyrean-dao": "Empyrean DAO", - "looksrare": "LooksRare", - "terrafloki": "TerraFloki", - "ultra-protocol": "Ultra Protocol", - "bomb-money": "Bomb Money", - "crabada": "Crabada", - "hermes-finance": "Hermes Finance", - "kandyland-finance": "Kandyland Finance", - "luminous-finance": "Luminous Finance", - "proof-of-humanity": "Proof of Humanity", - "delta-financial": "Delta Financial", - "smartpad": "SmartPad", - "x": "X", - "fees.wtf": "fees.wtf", - "the-open-dao-sos": "The Open DAO SOS", - "immortal": "Immortal", - "cleopatra-dao": "Cleopatra DAO", - "paprprintr": "Paprprintr", - "tornado-finance": "Tornado Finance", - "rubic": "Rubic", - "grape-finance": "Grape Finance", - "hades-money": "Hades Money", - "bloxmove": "BloXmove", - "noaharkdao": "NoahArkDAO", - "specter-finance": "Specter Finance", - "comfy-money": "Comfy Money", - "poopsicle": "Poopsicle", - "magik-finance-algostable": "Magik Finance AlgoStable", - "snowtomb": "Snowtomb", - "dna-dollar": "DNA Dollar", - "oceanus": "Oceanus", - "shiroswap": "ShiroSwap", - "unizen": "Unizen", - "warp": "Warp", - "api3": "API3", - "icecream-finance": "IceCream Finance", - "devil-finance": "Devil Finance", - "moon-flower-farmers": "Moon Flower Farmers", - "athena-money": "Athena Money", - "treasuredao": "TreasureDAO", - "astral-financial": "Astral Financial", - "dogs-of-elon": "Dogs of Elon", - "meld": "Meld", - "qao": "QAO", - "hexal": "Hexal", - "velhalla": "Velhalla", - "ripae": "Ripae", - "gensokishi": "Gensokishi", - "maia-dao": "Maia DAO", - "minimax": "Minimax", - "platinum-finance": "Platinum Finance", - "projectx": "ProjectX", - "thor-financial": "Thor Financial", - "vaporfi": "VaporFi", - "basis-markets": "Basis Markets", - "manarium": "Manarium", - "dibs-money": "Dibs Money", - "partial-finance": "Partial Finance", - "draco-finance": "Draco Finance", - "arable-protocol": "Arable Protocol", - "winkyverse": "Winkyverse", - "volta-dao": "Volta DAO", - "croissant-games": "Croissant Games", - "starcrazy": "StarCrazy", - "kommunitas": "Kommunitas", - "peel-defi": "Peel Defi", - "fdoge-finance": "FDoge Finance", - "impactmarket": "ImpactMarket", - "ogx": "OGX", - "comet-finance": "Comet Finance", - "wrapped-ilcoin": "Wrapped ILCOIN", - "2doge-finance": "2Doge Finance", - "pdollar": "pDollar", - "the-parallel": "The Parallel", - "snowyowl": "Snowyowl", - "floor-dao": "Floor Dao", - "pulse-dao": "Pulse DAO", - "zombi-finance": "Zombi Finance", - "code-7-finance": "Code 7 Finance", - "solanax": "Solanax", - "miniverse-finance": "MiniVerse Finance", - "seedify": "Seedify", - "zomb2-finance": "Zomb2 Finance", - "rhea-dao": "RHEA DAO", - "cryptex-locker": "CryptEx Locker", - "libero-financial": "Libero Financial", - "cubo": "Cubo", - "polar-bear-finance": "Polar Bear Finance", - "drip": "Drip", - "angel-protocol": "Angel Protocol", - "kleros": "Kleros", - "kdlaunch": "KDLaunch", - "ringfi": "RingFi", - "axl-inu": "Axl Inu", - "diamond-coin": "Diamond Coin", - "strips-finance": "Strips Finance", - "ergopad": "ErgoPad", - "peak-finance": "Peak Finance", - "revuto": "Revuto", - "velaspad": "VelasPad", - "ambire-wallet": "Ambire Wallet", - "darumadao": "DarumaDAO", - "solace": "Solace", - "epns": "EPNS", - "trusted-node": "Trusted Node", - "scrub": "Scrub", - "steak": "Steak", - "swapscanner-dex": "Swapscanner Dex", - "monster": "Monster", - "printer-financial": "Printer Financial", - "puddingswap": "PuddingSwap", - "rfox": "RFOX", - "ignite-finance": "Ignite Finance", - "zombie-network": "Zombie Network", - "ravcube-finance": "Ravcube Finance", - "rubik-finance": "Rubik Finance", - "yup": "Yup", - "iotex-monster-go": "IoTeX Monster Go", - "candle": "Candle", - "0x-nodes": "0x NODES", - "coinflex": "CoinFLEX", - "shibui-dao": "Shibui DAO", - "ampere": "Ampere", - "goblins-cash": "Goblins Cash", - "lns": "LNS", - "valerian": "Valerian", - "celerycash": "Celerycash", - "joystick-club": "Joystick Club", - "game-theory": "Game Theory", - "murica": "Murica", - "pandaland": "PandaLand", - "solily-protocol": "Solily Protocol", - "olympus-finance": "Olympus Finance", - "toxicdeer-finance": "ToxicDeer Finance", - "coti-treasury": "COTI Treasury", - "elf-finance": "Elf Finance", - "know-to-earn": "Know-To-Earn", - "amesdefi": "AmesDefi", - "xweowns": "xWeowns", - "jelly": "Jelly", - "blitz-labs": "Blitz Labs", - "cake-monster": "Cake Monster", - "versagames": "VersaGames", - "gale": "Gale", - "gaur-money": "Gaur Money", - "dinopool": "DinoPool", - "crown-finance": "Crown Finance", - "everlend": "Everlend", - "zeroswap": "ZeroSwap", - "tangent": "Tangent", - "velerodao": "VeleroDAO", - "strategyx-finance": "StrategyX Finance", - "minto": "Minto", - "frozen-walrus-finance": "Frozen Walrus Finance", - "blur-finance": "Blur Finance", - "sideshift": "SideShift", - "uplift-dao": "Uplift DAO", - "waterfall-wtf": "Waterfall WTF", - "bitgert": "Bitgert", - "betswirl": "BetSwirl", - "pony-finance": "Pony Finance", - "liquidity-finance": "Liquidity Finance", - "bubbleswap-v1": "Bubbleswap V1", - "homecoin": "HomeCoin", - "emp-money": "Emp Money", - "lanternswap": "LanternSwap", - "civfund": "CivFund", - "cryptoblades": "CryptoBlades", - "drachma-exchange": "Drachma Exchange", - "wolf-safe-poor-people": "Wolf Safe Poor People", - "dogeswap": "Dogeswap", - "optifi": "OptiFi", - "sudo-inu": "Sudo Inu", - "dragonfruit-protocol": "Dragonfruit Protocol", - "champion-finance": "Champion Finance", - "dawn-star-finance": "Dawn Star Finance", - "eversol": "Eversol", - "klaylend-finance": "Klaylend Finance", - "junoswap": "Junoswap", - "puli": "PULI", - "interlay-staking": "Interlay Staking", - "polyo-exchange": "Polyo Exchange", - "corgi-finance": "Corgi Finance", - "giveth": "Giveth", - "roaring-lion": "Roaring Lion", - "ethichub": "EthicHub", - "ultron-staking-hub-nft": "Ultron Staking Hub NFT", - "nudes": "NUDES", - "0x": "0x", - "cowaii-cash": "Cowaii Cash", - "kdex": "KDex", - "jupiter-aggregator": "Jupiter Aggregator", - "based-next-generation": "Based Next Generation", - "zootopia-finance": "Zootopia Finance", - "ohmpow": "OHMPOW", - "templedao-trade": "TempleDAO Trade", - "neuy": "NEUY", - "echswap": "ECHSWAP", - "bepro-network": "Bepro Network", - "stakerz": "Stakerz", - "ora-finance": "ORA Finance", - "crolend-finance": "CroLend Finance", - "hera-finance": "Hera Finance", - "dexible-v2": "Dexible V2", - "opensea-seaport": "Opensea Seaport", - "ghostmarket": "GhostMarket", - "fairfi": "FairFi", - "lympo": "Lympo", - "binance-multi-chain-capital": "Binance Multi Chain Capital", - "kperp-exchange": "KPerp Exchange", - "optitude-finance": "Optitude Finance", - "x7-finance": "X7 Finance", - "athos-finance": "Athos Finance", - "dexit-finance-swap": "Dexit Finance Swap", - "yieldbank": "YieldBank", - "genius-yield": "Genius Yield", - "freebie-life-finance": "Freebie Life Finance", - "bluemove-staking": "BlueMove Staking", - "inbuilt-finance": "Inbuilt Finance", - "bao-ballast": "BAO Ballast", - "decryption": "Decryption", - "footballmanager": "FootballManager", - "y2b-finance": "Y2B Finance", - "revoluzion": "Revoluzion", - "defender-finance": "Defender Finance", - "changex": "Changex", - "metropolis": "Metropolis", - "doglands-swap": "Doglands Swap", - "vidya": "Vidya", - "premio": "Premio", - "bitindi-staking": "Bitindi Staking", - "meme-dollar": "Meme Dollar", - "akiba-finance": "Akiba Finance", - "algoblocks": "AlgoBlocks", - "citadao": "CitaDAO", - "brewlabs": "Brewlabs", - "fundex": "FunDex", - "bankless": "Bankless", - "aragon": "Aragon", - "mantle-treasury": "Mantle Treasury", - "pleasrdao": "PleasrDAO", - "rarible": "Rarible", - "notiboy": "Notiboy", - "businessbuildersnfts": "BusinessBuildersNFTs", - "manifesto": "Manifesto", - "phezzan-protocol": "Phezzan Protocol", - "gitcoin": "Gitcoin", - "ens": "ENS", - "gnosis-dao": "Gnosis DAO", - "ethereum-foundation": "Ethereum Foundation", - "nouns": "Nouns", - "friends-with-benefits": "Friends With Benefits", - "droplit-protocol": "Droplit Protocol", - "wonderly-finance": "Wonderly Finance", - "zonic": "Zonic", - "thorwallet-dex": "THORWallet DEX", - "onepunchswap": "OnePunchSwap", - "lion-commerce": "Lion Commerce", - "bank-of-cronos-ohm": "Bank of Cronos OHM", - "nftearth": "NFTEarth", - "subzero+": "Subzero+", - "perion": "Perion", - "mosquitos-finance": "Mosquitos Finance", - "hxro-network": "HXRO Network", - "beamer-bridge": "Beamer Bridge", - "king-finance": "King Finance", - "nuls-pocm": "NULS POCM", - "truefeedback": "TrueFeedBack", - "bisq": "Bisq", - "dexalot": "Dexalot", - "pancakeswap-amm-v1": "PancakeSwap AMM V1", - "surfswap-stable-amm": "Surfswap Stable AMM", - "zora": "ZORA", - "chainlink-requests": "Chainlink Requests", - "opensea-v1": "OpenSea V1", - "opensea-v2": "OpenSea V2", - "cow-swap": "CoW Swap", - "0xacid": "0xAcid", - "forta": "Forta", - "apecoin": "ApeCoin", - "kava-football-club": "Kava Football Club", - "arc-swap": "Arc Swap", - "sakai-vault": "Sakai Vault", - "void": "Void", - "pxswap": "pxswap", - "kokomo-finance": "Kokomo Finance", - "dexioprotocol": "Dexioprotocol", - "arbitrum-dao": "Arbitrum DAO", - "india-covid-relief-fund": "India Covid Relief Fund", - "aptos": "Aptos", - "canto": "Canto", - "atlas-aggregator": "Atlas Aggregator", - "blast-api": "Blast API", - "get-protocol": "GET Protocol", - "plexus": "PLEXUS", - "tegisto": "Tegisto", - "cexdex": "CexDex", - "empyreal": "Empyreal", - "multialt": "Multialt", - "twtstake": "TWTStake", - "nft360": "NFT360", - "ysmart": "Ysmart", - "arbitrum": "Arbitrum", - "bitcoin": "Bitcoin", - "acala": "Acala", - "moonbeam": "Moonbeam", - "zks-miner": "Zks Miner", - "cron-finance": "Cron Finance", - "dypius": "Dypius", - "cortexdao": "CortexDAO", - "world-mobile-token": "World Mobile Token", - "merlin-dex": "MERLIN DEX", - "privcash": "PrivCash", - "catsapes": "CatsApes", - "array": "Array", - "snark-launch": "Snark Launch", - "superlauncher": "SuperLauncher", - "funbear": "FunBear", - "direct-exchange": "Direct Exchange", - "kei-finance": "Kei Finance", - "cells-finance": "Cells Finance", - "ajira-pay-finance": "Ajira Pay Finance", - "braindao": "BrainDAO", - "bubbleswap-v2": "Bubbleswap V2", - "airswap": "AirSwap", - "dxdao": "DXDao", - "forth-dao": "FORTH DAO", - "optimism-foundation": "Optimism Foundation", - "nutbox": "Nutbox", - "verified-credits": "Verified Credits", - "merkl": "Merkl", - "kwenta": "Kwenta", - "component-swap": "Component Swap", - "femboydao": "FemboyDAO", - "maestro": "Maestro", - "monoceros": "Monoceros", - "metamask": "MetaMask", - "firebot": "FireBot", - "rainbow": "Rainbow", - "houdini-swap": "Houdini Swap", - "the-llamas": "The Llamas", - "astaria": "Astaria", - "dezswap": "Dezswap", - "olympulsex": "OlympulseX", - "froggy-protocol": "Froggy Protocol", - "unibot": "Unibot", - "xlsd": "XLSD", - "stealcam": "Stealcam", - "my-neighbor-alice": "My Neighbor Alice", - "step-app": "Step App", - "avalanche": "Avalanche", - "defiato": "DeFiato", - "primal": "Primal", - "xana": "Xana", - "superrare": "SuperRare", - "scatter": "Scatter", - "pulsemax-finance": "Pulsemax Finance", - "avnu": "AVNU", - "themis-staking": "Themis Staking", - "foundation": "Foundation", - "sui": "Sui", - "cryptodickbutts": "CryptoDickbutts", - "arpa-staking": "ARPA Staking", - "florence-finance": "Florence Finance", - "bitget-swap": "Bitget Swap", - "tribe3": "Tribe3", - "aerie-lab": "Aerie Lab", - "near": "NEAR", - "golem-network": "Golem Network", - "metis": "Metis", - "yield-guild-games": "Yield Guild Games", - "arkham": "Arkham", - "evmos-dao": "Evmos DAO", - "l2beat": "L2BEAT", - "go-ethereum": "Go Ethereum", - "ethers": "Ethers", - "ethglobal": "ETHGlobal", - "rotki": "rotki", - "wagmi-sh": "wagmi sh", - "tangent-protocol": "Tangent Protocol", - "snapshot": "Snapshot", - "safe": "Safe", - "revoke.cash": "Revoke.cash", - "ignore-fud": "Ignore Fud", - "banana-gun": "Banana Gun", - "none-trading": "None Trading", - "chainlink-keepers": "Chainlink Keepers", - "chainlink-vrf-v1": "Chainlink VRF V1", - "chainlink-vrf-v2": "Chainlink VRF V2", - "magnate-finance": "Magnate Finance", - "mantadao": "MantaDAO", - "logx": "LogX", - "denet-file-token": "DeNet File Token", - "meowl": "Meowl", - "bad-idea-ai": "Bad Idea AI" -} \ No newline at end of file diff --git a/openbb_terminal/cryptocurrency/data/erc20_coins.json b/openbb_terminal/cryptocurrency/data/erc20_coins.json deleted file mode 100644 index 517e87d346f..00000000000 --- a/openbb_terminal/cryptocurrency/data/erc20_coins.json +++ /dev/null @@ -1,5058 +0,0 @@ -[ - { - "baseCurrency": { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "symbol": "WETH", - "name": "Wrapped Ether" - }, - "count": 309473 - }, - { - "baseCurrency": { - "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "symbol": "USDC", - "name": "USD//C" - }, - "count": 188320 - }, - { - "baseCurrency": { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "symbol": "USDT", - "name": "Tether USD" - }, - "count": 88082 - }, - { - "baseCurrency": { - "address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "symbol": "DAI", - "name": "Dai Stablecoin" - }, - "count": 53210 - }, - { - "baseCurrency": { - "address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", - "symbol": "WBTC", - "name": "Wrapped BTC" - }, - "count": 37920 - }, - { - "baseCurrency": { - "address": "0x956f47f50a910163d8bf957cf5846d573e7f87ca", - "symbol": "FEI", - "name": "Fei USD" - }, - "count": 10731 - }, - { - "baseCurrency": { - "address": "0x57ab1ec28d129707052df4df418d58a2d46d5f51", - "symbol": "sUSD", - "name": "Synth sUSD" - }, - "count": 6528 - }, - { - "baseCurrency": { - "address": "0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce", - "symbol": "SHIB", - "name": "SHIBA INU" - }, - "count": 5281 - }, - { - "baseCurrency": { - "address": "0x6b3595068778dd592e39a122f4f5a5cf09c90fe2", - "symbol": "SUSHI", - "name": "SushiToken" - }, - "count": 4919 - }, - { - "baseCurrency": { - "address": "-", - "symbol": "ETH", - "name": "Ether" - }, - "count": 3684 - }, - { - "baseCurrency": { - "address": "0x383518188c0c6d7730d91b2c03a03c837814a899", - "symbol": "OHM", - "name": "Olympus" - }, - "count": 3609 - }, - { - "baseCurrency": { - "address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", - "symbol": "UNI", - "name": "Uniswap" - }, - "count": 2943 - }, - { - "baseCurrency": { - "address": "0x514910771af9ca656af840dff83e8264ecf986ca", - "symbol": "LINK", - "name": "ChainLink Token" - }, - "count": 2813 - }, - { - "baseCurrency": { - "address": "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9", - "symbol": "AAVE", - "name": "Aave Token" - }, - "count": 2487 - }, - { - "baseCurrency": { - "address": "0xeb4c2781e4eba804ce9a9803c67d0893436bb27d", - "symbol": "renBTC", - "name": "renBTC" - }, - "count": 2204 - }, - { - "baseCurrency": { - "address": "0x0000000000085d4780b73119b644ae5ecd22b376", - "symbol": "TUSD", - "name": "TrueUSD" - }, - "count": 1853 - }, - { - "baseCurrency": { - "address": "-", - "symbol": "-", - "name": "-" - }, - "count": 1722 - }, - { - "baseCurrency": { - "address": "0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb", - "symbol": "sETH", - "name": "Synth sETH" - }, - "count": 1713 - }, - { - "baseCurrency": { - "address": "0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e", - "symbol": "YFI", - "name": "yearn.finance" - }, - "count": 1698 - }, - { - "baseCurrency": { - "address": "0x3212b29e33587a00fb1c83346f5dbfa69a458923", - "symbol": "imBTC", - "name": "The Tokenized Bitcoin" - }, - "count": 1439 - }, - { - "baseCurrency": { - "address": "0xdbdb4d16eda451d0503b854cf79d55697f90c8df", - "symbol": "ALCX", - "name": "Alchemix" - }, - "count": 1399 - }, - { - "baseCurrency": { - "address": "0xc7283b66eb1eb5fb86327f08e1b5816b0720212b", - "symbol": "TRIBE", - "name": "Tribe" - }, - "count": 1294 - }, - { - "baseCurrency": { - "address": "0x2b591e99afe9f32eaa6214f7b7629768c40eeb39", - "symbol": "HEX", - "name": "HEX" - }, - "count": 1270 - }, - { - "baseCurrency": { - "address": "0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c", - "symbol": "BNT", - "name": "Bancor Network Token" - }, - "count": 1157 - }, - { - "baseCurrency": { - "address": "0x090185f2135308bad17527004364ebcc2d37e5f6", - "symbol": "SPELL", - "name": "Spell Token" - }, - "count": 1053 - }, - { - "baseCurrency": { - "address": "0xfe18be6b3bd88a2d2a7f928d00292e7a9963cfc6", - "symbol": "sBTC", - "name": "Synth sBTC" - }, - "count": 1041 - }, - { - "baseCurrency": { - "address": "0x4fabb145d64652a948d72533023f6e7a623c7c53", - "symbol": "BUSD", - "name": "Binance USD" - }, - "count": 1036 - }, - { - "baseCurrency": { - "address": "0xa47c8bf37f92abed4a126bda807a7b7498661acd", - "symbol": "UST", - "name": "Wrapped UST Token" - }, - "count": 1034 - }, - { - "baseCurrency": { - "address": "0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b", - "symbol": "CVX", - "name": "Convex Token" - }, - "count": 1018 - }, - { - "baseCurrency": { - "address": "0x7c3e3eae4d893d11c61e74d5187c962ba5744a3b", - "symbol": "KOKO", - "name": "Kokoswap Token" - }, - "count": 1000 - }, - { - "baseCurrency": { - "address": "0x853d955acef822db058eb8505911ed77f175b99e", - "symbol": "FRAX", - "name": "Frax" - }, - "count": 962 - }, - { - "baseCurrency": { - "address": "0xaa6e8127831c9de45ae56bb1b0d4d4da6e5665bd", - "symbol": "ETH2x-FLI", - "name": "ETH 2x Flexible Leverage Index" - }, - "count": 947 - }, - { - "baseCurrency": { - "address": "0x5f98805a4e8be255a32880fdec7f6728c6568ba0", - "symbol": "LUSD", - "name": "LUSD Stablecoin" - }, - "count": 742 - }, - { - "baseCurrency": { - "address": "0x761d38e5ddf6ccf6cf7c55759d5210750b5d60f3", - "symbol": "ELON", - "name": "Dogelon" - }, - "count": 731 - }, - { - "baseCurrency": { - "address": "0x27c70cd1946795b66be9d954418546998b546634", - "symbol": "LEASH", - "name": "DOGE KILLER" - }, - "count": 670 - }, - { - "baseCurrency": { - "address": "0x03ab458634910aad20ef5f1c8ee96f1d6ac54919", - "symbol": "RAI", - "name": "Rai Reflex Index" - }, - "count": 666 - }, - { - "baseCurrency": { - "address": "0x949d48eca67b17269629c7194f4b727d4ef9e5d6", - "symbol": "MC", - "name": "Merit Circle" - }, - "count": 590 - }, - { - "baseCurrency": { - "address": "0x1559fa1b8f28238fd5d76d9f434ad86fd20d1559", - "symbol": "EDEN", - "name": "Eden" - }, - "count": 581 - }, - { - "baseCurrency": { - "address": "0x92d6c1e31e14520e676a687f0a93788b716beff5", - "symbol": "DYDX", - "name": "dYdX" - }, - "count": 574 - }, - { - "baseCurrency": { - "address": "0x111111111117dc0aa78b770fa6a738034120c302", - "symbol": "1INCH", - "name": "1INCH Token" - }, - "count": 574 - }, - { - "baseCurrency": { - "address": "0xdb25f211ab05b1c97d595516f45794528a807ad8", - "symbol": "EURS", - "name": "STASIS EURS Token" - }, - "count": 573 - }, - { - "baseCurrency": { - "address": "0x6123b0049f904d730db3c36a31167d9d4121fa6b", - "symbol": "RBN", - "name": "Ribbon" - }, - "count": 546 - }, - { - "baseCurrency": { - "address": "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2", - "symbol": "MKR", - "name": "Maker" - }, - "count": 499 - }, - { - "baseCurrency": { - "address": "0xc18360217d8f7ab5e7c516566761ea12ce7f9d72", - "symbol": "ENS", - "name": "Ethereum Name Service" - }, - "count": 453 - }, - { - "baseCurrency": { - "address": "0x1a4b46696b2bb4794eb3d4c26f1c55f9170fa4c5", - "symbol": "BIT", - "name": "BitDAO" - }, - "count": 448 - }, - { - "baseCurrency": { - "address": "0x767fe9edc9e0df98e07454847909b5e959d7ca0e", - "symbol": "ILV", - "name": "Illuvium" - }, - "count": 441 - }, - { - "baseCurrency": { - "address": "0xd533a949740bb3306d119cc777fa900ba034cd52", - "symbol": "CRV", - "name": "Curve DAO Token" - }, - "count": 437 - }, - { - "baseCurrency": { - "address": "0x9ea3b5b4ec044b70375236a281986106457b20ef", - "symbol": "DELTA", - "name": "DELTA.financial - deep DeFi derivatives" - }, - "count": 400 - }, - { - "baseCurrency": { - "address": "0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f", - "symbol": "SNX", - "name": "Synthetix Network Token" - }, - "count": 387 - }, - { - "baseCurrency": { - "address": "0x2e9d63788249371f1dfc918a52f8d799f4a38c94", - "symbol": "TOKE", - "name": "Tokemak" - }, - "count": 385 - }, - { - "baseCurrency": { - "address": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0", - "symbol": "MATIC", - "name": "Matic Token" - }, - "count": 381 - }, - { - "baseCurrency": { - "address": "0x21ad647b8f4fe333212e735bfc1f36b4941e6ad2", - "symbol": "SQUID", - "name": "Squid" - }, - "count": 370 - }, - { - "baseCurrency": { - "address": "0x0f2d719407fdbeff09d87557abb7232601fd9f29", - "symbol": "SYN", - "name": "Synapse" - }, - "count": 345 - }, - { - "baseCurrency": { - "address": "0x8b3192f5eebd8579568a2ed41e6feb402f93f73f", - "symbol": "SAITAMA", - "name": "Saitama Inu" - }, - "count": 342 - }, - { - "baseCurrency": { - "address": "0xc00e94cb662c3520282e6f5717214004a7f26888", - "symbol": "COMP", - "name": "Compound" - }, - "count": 335 - }, - { - "baseCurrency": { - "address": "0x32353a6c91143bfd6c7d363b546e62a9a2489a20", - "symbol": "AGLD", - "name": "Adventure Gold" - }, - "count": 332 - }, - { - "baseCurrency": { - "address": "0x4c2e59d098df7b6cbae0848d66de2f8a4889b9c3", - "symbol": "FODL", - "name": "Fodl" - }, - "count": 325 - }, - { - "baseCurrency": { - "address": "0xc581b735a1688071a1746c968e0798d642ede491", - "symbol": "EURT", - "name": "Euro Tether" - }, - "count": 324 - }, - { - "baseCurrency": { - "address": "0x9813037ee2218799597d83d4a5b6f3b6778218d9", - "symbol": "BONE", - "name": "BONE SHIBASWAP" - }, - "count": 320 - }, - { - "baseCurrency": { - "address": "0xbaac2b4491727d78d2b78815144570b9f2fe8899", - "symbol": "DOG", - "name": "The Doge NFT" - }, - "count": 313 - }, - { - "baseCurrency": { - "address": "0x3845badade8e6dff049820680d1f14bd3903a5d0", - "symbol": "SAND", - "name": "SAND" - }, - "count": 310 - }, - { - "baseCurrency": { - "address": "0xd46ba6d942050d489dbd938a2c909a5d5039a161", - "symbol": "AMPL", - "name": "Ampleforth" - }, - "count": 295 - }, - { - "baseCurrency": { - "address": "0xb5fe099475d3030dde498c3bb6f3854f762a48ad", - "symbol": "FNK", - "name": "Finiko" - }, - "count": 292 - }, - { - "baseCurrency": { - "address": "0x8e870d67f660d95d5be530380d0ec0bd388289e1", - "symbol": "PAX", - "name": "Paxos Token" - }, - "count": 287 - }, - { - "baseCurrency": { - "address": "0x3449fc1cd036255ba1eb19d65ff4ba2b8903a69a", - "symbol": "BAC", - "name": "BAC" - }, - "count": 257 - }, - { - "baseCurrency": { - "address": "0xf3dcbc6d72a4e1892f7917b7c43b74131df8480e", - "symbol": "BDP", - "name": "BDPToken" - }, - "count": 256 - }, - { - "baseCurrency": { - "address": "0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b", - "symbol": "DPI", - "name": "DefiPulse Index" - }, - "count": 255 - }, - { - "baseCurrency": { - "address": "0x368b3a58b5f49392e5c9e4c998cb0bb966752e51", - "symbol": "MIC", - "name": "MIC" - }, - "count": 255 - }, - { - "baseCurrency": { - "address": "0x4e15361fd6b4bb609fa63c81a2be19d873717870", - "symbol": "FTM", - "name": "Fantom Token" - }, - "count": 254 - }, - { - "baseCurrency": { - "address": "0xf65b5c5104c4fafd4b709d9d60a185eae063276c", - "symbol": "TRU", - "name": "Truebit" - }, - "count": 246 - }, - { - "baseCurrency": { - "address": "0x3472a5a71965499acd81997a54bba8d852c6e53d", - "symbol": "BADGER", - "name": "Badger" - }, - "count": 242 - }, - { - "baseCurrency": { - "address": "0xba100000625a3754423978a60c9317c58a424e3d", - "symbol": "BAL", - "name": "Balancer" - }, - "count": 241 - }, - { - "baseCurrency": { - "address": "0x0316eb71485b0ab14103307bf65a021042c6d380", - "symbol": "HBTC", - "name": "Huobi BTC" - }, - "count": 234 - }, - { - "baseCurrency": { - "address": "0xbb0e17ef65f82ab018d8edd776e8dd940327b28b", - "symbol": "AXS", - "name": "Axie Infinity Shard" - }, - "count": 230 - }, - { - "baseCurrency": { - "address": "0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0", - "symbol": "FXS", - "name": "Frax Share" - }, - "count": 221 - }, - { - "baseCurrency": { - "address": "0xbbbbca6a901c926f240b89eacb641d8aec7aeafd", - "symbol": "LRC", - "name": "LoopringCoin V2" - }, - "count": 208 - }, - { - "baseCurrency": { - "address": "0x60ef10edff6d600cd91caeca04caed2a2e605fe5", - "symbol": "MOCHI", - "name": "Mochi Inu" - }, - "count": 207 - }, - { - "baseCurrency": { - "address": "0x66a0f676479cee1d7373f3dc2e2952778bff5bd6", - "symbol": "WISE", - "name": "Wise Token" - }, - "count": 195 - }, - { - "baseCurrency": { - "address": "0x249e38ea4102d0cf8264d3701f1a0e39c4f2dc3b", - "symbol": "UFO", - "name": "THE TRUTH" - }, - "count": 184 - }, - { - "baseCurrency": { - "address": "0x5218e472cfcfe0b64a064f055b43b4cdc9efd3a6", - "symbol": "eRSDL", - "name": "UnFederalReserveToken" - }, - "count": 177 - }, - { - "baseCurrency": { - "address": "0x36f3fd68e7325a35eb768f1aedaae9ea0689d723", - "symbol": "ESD", - "name": "Empty Set Dollar" - }, - "count": 176 - }, - { - "baseCurrency": { - "address": "0xbbc455cb4f1b9e4bfc4b73970d360c8f032efee6", - "symbol": "sLINK", - "name": "Synth sLINK" - }, - "count": 174 - }, - { - "baseCurrency": { - "address": "0xd291e7a03283640fdc51b121ac401383a46cc623", - "symbol": "RGT", - "name": "Rari Governance Token" - }, - "count": 165 - }, - { - "baseCurrency": { - "address": "0x8e6cd950ad6ba651f6dd608dc70e5886b1aa6b24", - "symbol": "STARL", - "name": "StarLink" - }, - "count": 163 - }, - { - "baseCurrency": { - "address": "0x961c8c0b1aad0c0b10a51fef6a867e3091bcef17", - "symbol": "DYP", - "name": "DeFiYieldProtocol" - }, - "count": 152 - }, - { - "baseCurrency": { - "address": "0xdec41db0c33f3f6f3cb615449c311ba22d418a8d", - "symbol": "LOBI", - "name": "Lobi" - }, - "count": 148 - }, - { - "baseCurrency": { - "address": "0xf8e9f10c22840b613cda05a0c5fdb59a4d6cd7ef", - "symbol": "DOE", - "name": "Dogs Of Elon" - }, - "count": 144 - }, - { - "baseCurrency": { - "address": "0xbc396689893d065f41bc2c6ecbee5e0085233447", - "symbol": "PERP", - "name": "Perpetual" - }, - "count": 138 - }, - { - "baseCurrency": { - "address": "0x24e89bdf2f65326b94e36978a7edeac63623dafa", - "symbol": "TKING", - "name": "Tiger King" - }, - "count": 136 - }, - { - "baseCurrency": { - "address": "0x7a58c0be72be218b41c608b7fe7c5bb630736c71", - "symbol": "PEOPLE", - "name": "ConstitutionDAO" - }, - "count": 132 - }, - { - "baseCurrency": { - "address": "0x3301ee63fb29f863f2333bd4466acb46cd8323e6", - "symbol": "AKITA", - "name": "Akita Inu" - }, - "count": 131 - }, - { - "baseCurrency": { - "address": "0x0391d2021f89dc339f60fff84546ea23e337750f", - "symbol": "BOND", - "name": "BarnBridge Governance Token" - }, - "count": 131 - }, - { - "baseCurrency": { - "address": "0x35a532d376ffd9a705d0bb319532837337a398e7", - "symbol": "WDOGE", - "name": "Wrapped DogeCoin" - }, - "count": 126 - }, - { - "baseCurrency": { - "address": "0x1b40183efb4dd766f11bda7a7c3ad8982e998421", - "symbol": "VSP", - "name": "VesperToken" - }, - "count": 123 - }, - { - "baseCurrency": { - "address": "0x6b4c7a5e3f0b99fcd83e9c089bddd6c7fce5c611", - "symbol": "MM", - "name": "Million" - }, - "count": 120 - }, - { - "baseCurrency": { - "address": "0xf16e81dce15b08f326220742020379b855b87df9", - "symbol": "ICE", - "name": "IceToken" - }, - "count": 119 - }, - { - "baseCurrency": { - "address": "0x5a98fcbea516cf06857215779fd812ca3bef1b32", - "symbol": "LDO", - "name": "Lido DAO Token" - }, - "count": 114 - }, - { - "baseCurrency": { - "address": "0xba5bde662c17e2adff1075610382b9b691296350", - "symbol": "RARE", - "name": "SuperRare" - }, - "count": 113 - }, - { - "baseCurrency": { - "address": "0x1122b6a0e00dce0563082b6e2953f3a943855c1f", - "symbol": "CENNZ", - "name": "Centrality Token" - }, - "count": 111 - }, - { - "baseCurrency": { - "address": "0x8798249c2e607446efb7ad49ec89dd1865ff4272", - "symbol": "xSUSHI", - "name": "SushiBar" - }, - "count": 109 - }, - { - "baseCurrency": { - "address": "0x0309c98b1bffa350bcb3f9fb9780970ca32a5060", - "symbol": "BDI", - "name": "BasketDAO DeFi Index" - }, - "count": 108 - }, - { - "baseCurrency": { - "address": "0xbc7250c8c3eca1dfc1728620af835fca489bfdf3", - "symbol": "GM", - "name": "GM" - }, - "count": 106 - }, - { - "baseCurrency": { - "address": "0x3d6f0dea3ac3c607b3998e6ce14b6350721752d9", - "symbol": "CARDS", - "name": "CARD.STARTER" - }, - "count": 105 - }, - { - "baseCurrency": { - "address": "0xbd2f0cd039e0bfcf88901c98c0bfac5ab27566e3", - "symbol": "DSD", - "name": "Dynamic Set Dollar" - }, - "count": 104 - }, - { - "baseCurrency": { - "address": "0xa0246c9032bc3a600820415ae600c6388619a14d", - "symbol": "FARM", - "name": "FARM Reward Token" - }, - "count": 104 - }, - { - "baseCurrency": { - "address": "0x09a3ecafa817268f77be1283176b946c4ff2e608", - "symbol": "MIR", - "name": "Wrapped MIR Token" - }, - "count": 104 - }, - { - "baseCurrency": { - "address": "0xa2b4c0af19cc16a6cfacce81f192b024d625817d", - "symbol": "KISHU", - "name": "Kishu Inu" - }, - "count": 103 - }, - { - "baseCurrency": { - "address": "0xff20817765cb7f73d4bde2e66e067e58d11095c2", - "symbol": "AMP", - "name": "Amp" - }, - "count": 96 - }, - { - "baseCurrency": { - "address": "0xb4efd85c19999d84251304bda99e90b92300bd93", - "symbol": "RPL", - "name": "Rocket Pool" - }, - "count": 93 - }, - { - "baseCurrency": { - "address": "0x43f11c02439e2736800433b4594994bd43cd066d", - "symbol": "FLOKI", - "name": "FLOKI" - }, - "count": 93 - }, - { - "baseCurrency": { - "address": "0x798d1be841a82a273720ce31c822c61a67a601c3", - "symbol": "DIGG", - "name": "Digg" - }, - "count": 92 - }, - { - "baseCurrency": { - "address": "0x4b4d2e899658fb59b1d518b68fe836b100ee8958", - "symbol": "MIS", - "name": "MIS" - }, - "count": 88 - }, - { - "baseCurrency": { - "address": "0xdb5c3c46e28b53a39c255aa39a411dd64e5fed9c", - "symbol": "NCR", - "name": "Neos Credits" - }, - "count": 85 - }, - { - "baseCurrency": { - "address": "0x25f8087ead173b73d6e8b84329989a8eea16cf73", - "symbol": "YGG", - "name": "Yield Guild Games Token" - }, - "count": 84 - }, - { - "baseCurrency": { - "address": "0x62b9c7356a2dc64a1969e19c23e4f579f9810aa7", - "symbol": "cvxCRV", - "name": "Convex CRV" - }, - "count": 78 - }, - { - "baseCurrency": { - "address": "0xcbfef8fdd706cde6f208460f2bf39aa9c785f05d", - "symbol": "KINE", - "name": "Kine Governance Token" - }, - "count": 75 - }, - { - "baseCurrency": { - "address": "0xaaaebe6fe48e54f431b0c390cfaf0b017d09d42d", - "symbol": "CEL", - "name": "Celsius" - }, - "count": 75 - }, - { - "baseCurrency": { - "address": "0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c", - "symbol": "ENJ", - "name": "Enjin Coin" - }, - "count": 74 - }, - { - "baseCurrency": { - "address": "0x0000000000095413afc295d19edeb1ad7b71c952", - "symbol": "LON", - "name": "Tokenlon" - }, - "count": 69 - }, - { - "baseCurrency": { - "address": "0x0f5d2fb29fb7d3cfee444a200298f468908cc942", - "symbol": "MANA", - "name": "Decentraland MANA" - }, - "count": 68 - }, - { - "baseCurrency": { - "address": "0x6810e776880c02933d47db1b9fc05908e5386b96", - "symbol": "GNO", - "name": "Gnosis Token" - }, - "count": 66 - }, - { - "baseCurrency": { - "address": "0x4fe83213d56308330ec302a8bd641f1d0113a4cc", - "symbol": "NU", - "name": "NuCypher" - }, - "count": 65 - }, - { - "baseCurrency": { - "address": "0xb78b3320493a4efaa1028130c5ba26f0b6085ef8", - "symbol": "DRC", - "name": "Dracula Token" - }, - "count": 64 - }, - { - "baseCurrency": { - "address": "0xba7970f10d9f0531941dced1dda7ef3016b24e5b", - "symbol": "BGLD", - "name": "Based Gold" - }, - "count": 64 - }, - { - "baseCurrency": { - "address": "0x29a5c1db7321c5c9eae57f9366ee8eef00ca11fb", - "symbol": "SHKOOBY", - "name": "SHKOOBY INU" - }, - "count": 62 - }, - { - "baseCurrency": { - "address": "0xdfdb7f72c1f195c5951a234e8db9806eb0635346", - "symbol": "NFD", - "name": "Feisty Doge NFT" - }, - "count": 60 - }, - { - "baseCurrency": { - "address": "0xa1faa113cbe53436df28ff0aee54275c13b40975", - "symbol": "ALPHA", - "name": "AlphaToken" - }, - "count": 57 - }, - { - "baseCurrency": { - "address": "0xa7ed29b253d8b4e3109ce07c80fc570f81b63696", - "symbol": "BAS", - "name": "BAS" - }, - "count": 55 - }, - { - "baseCurrency": { - "address": "0x429881672b9ae42b8eba0e26cd9c73711b891ca5", - "symbol": "PICKLE", - "name": "PickleToken" - }, - "count": 55 - }, - { - "baseCurrency": { - "address": "0xf1f955016ecbcd7321c7266bccfb96c68ea5e49b", - "symbol": "RLY", - "name": "Rally" - }, - "count": 55 - }, - { - "baseCurrency": { - "address": "0x96e61422b6a9ba0e068b6c5add4ffabc6a4aae27", - "symbol": "ibEUR", - "name": "Iron Bank EUR" - }, - "count": 54 - }, - { - "baseCurrency": { - "address": "0x87d73e916d7057945c9bcd8cdd94e42a6f47f776", - "symbol": "NFTX", - "name": "NFTX" - }, - "count": 53 - }, - { - "baseCurrency": { - "address": "0x389999216860ab8e0175387a0c90e5c52522c945", - "symbol": "FEG", - "name": "FEGtoken" - }, - "count": 53 - }, - { - "baseCurrency": { - "address": "0x15d4c048f83bd7e37d49ea4c83a07267ec4203da", - "symbol": "GALA", - "name": "Gala" - }, - "count": 53 - }, - { - "baseCurrency": { - "address": "0x4691937a7508860f876c9c0a2a617e7d9e945d4b", - "symbol": "WOO", - "name": "Wootrade Network" - }, - "count": 51 - }, - { - "baseCurrency": { - "address": "0x3d7e683fc9c86b4d653c9e47ca12517440fad14e", - "symbol": "welp", - "name": "You don't blacklist delta.financial" - }, - "count": 50 - }, - { - "baseCurrency": { - "address": "0x05934eba98486693aaec2d00b0e9ce918e37dc3f", - "symbol": "SCAMMY", - "name": "You don't blacklist delta.financial" - }, - "count": 50 - }, - { - "baseCurrency": { - "address": "0x7296368fe9bcb25d3ecc19af13655b907818cc09", - "symbol": "generalize fix for rebass tokens", - "name": "You don't blacklist delta.financial" - }, - "count": 50 - }, - { - "baseCurrency": { - "address": "0x31c8eacbffdd875c74b94b077895bd78cf1e64a3", - "symbol": "RAD", - "name": "Radicle" - }, - "count": 50 - }, - { - "baseCurrency": { - "address": "0xfae9c647ad7d89e738aba720acf09af93dc535f7", - "symbol": "Ian Laphan fan token", - "name": "You don't blacklist delta.financial" - }, - "count": 50 - }, - { - "baseCurrency": { - "address": "0x1ceb5cb57c4d4e2b2433641b95dd330a33185a44", - "symbol": "KP3R", - "name": "Keep3rV1" - }, - "count": 49 - }, - { - "baseCurrency": { - "address": "0xe53ec727dbdeb9e2d5456c3be40cff031ab40a55", - "symbol": "SUPER", - "name": "SuperFarm" - }, - "count": 49 - }, - { - "baseCurrency": { - "address": "0xfa5047c9c78b8877af97bdcb85db743fd7313d4a", - "symbol": "ROOK", - "name": "ROOK" - }, - "count": 48 - }, - { - "baseCurrency": { - "address": "0x6243d8cea23066d098a15582d81a598b4e8391f4", - "symbol": "FLX", - "name": "Flex Ungovernance Token" - }, - "count": 48 - }, - { - "baseCurrency": { - "address": "0x408e41876cccdc0f92210600ef50372656052a38", - "symbol": "REN", - "name": "Republic Token" - }, - "count": 47 - }, - { - "baseCurrency": { - "address": "0x99d8a9c45b2eca8864373a26d1459e3dff1e17f3", - "symbol": "MIM", - "name": "Magic Internet Money" - }, - "count": 46 - }, - { - "baseCurrency": { - "address": "0xcafe001067cdef266afb7eb5a286dcfd277f3de5", - "symbol": "PSP", - "name": "ParaSwap" - }, - "count": 45 - }, - { - "baseCurrency": { - "address": "0x83e6f1e41cdd28eaceb20cb649155049fac3d5aa", - "symbol": "POLS", - "name": "PolkastarterToken" - }, - "count": 45 - }, - { - "baseCurrency": { - "address": "0x1a7e4e63778b4f12a199c062f3efdd288afcbce8", - "symbol": "agEUR", - "name": "agEUR" - }, - "count": 45 - }, - { - "baseCurrency": { - "address": "0x24a6a37576377f63f194caa5f518a60f45b42921", - "symbol": "BANK", - "name": "Float Bank" - }, - "count": 45 - }, - { - "baseCurrency": { - "address": "0xb62132e35a6c13ee1ee0f84dc5d40bad8d815206", - "symbol": "NEXO", - "name": "Nexo" - }, - "count": 45 - }, - { - "baseCurrency": { - "address": "0x62359ed7505efc61ff1d56fef82158ccaffa23d7", - "symbol": "CORE", - "name": "cVault.finance" - }, - "count": 44 - }, - { - "baseCurrency": { - "address": "0xfe2e637202056d30016725477c5da089ab0a043a", - "symbol": "sETH2", - "name": "StakeWise Staked ETH2" - }, - "count": 44 - }, - { - "baseCurrency": { - "address": "0xb35ed5c39f371f2cd4bc2edab1f8da314168186a", - "symbol": "FLOOR", - "name": "PUNK Floor" - }, - "count": 44 - }, - { - "baseCurrency": { - "address": "0x3155ba85d5f96b2d030a4966af206230e46849cb", - "symbol": "RUNE", - "name": "THORChain ETH.RUNE" - }, - "count": 43 - }, - { - "baseCurrency": { - "address": "0xf938424f7210f31df2aee3011291b658f872e91e", - "symbol": "VISR", - "name": "VISOR" - }, - "count": 43 - }, - { - "baseCurrency": { - "address": "0x4e352cf164e64adcbad318c3a1e222e9eba4ce42", - "symbol": "MCB", - "name": "MCDEX Token" - }, - "count": 43 - }, - { - "baseCurrency": { - "address": "0xa80f2c8f61c56546001f5fc2eb8d6e4e72c45d4c", - "symbol": "UNQT", - "name": "Unique Utility Token" - }, - "count": 42 - }, - { - "baseCurrency": { - "address": "0xf57e7e7c23978c3caec3c3548e3d615c346e79ff", - "symbol": "IMX", - "name": "Immutable X" - }, - "count": 41 - }, - { - "baseCurrency": { - "address": "0x725c263e32c72ddc3a19bea12c5a0479a81ee688", - "symbol": "BMI", - "name": "Bridge Mutual" - }, - "count": 40 - }, - { - "baseCurrency": { - "address": "0xa8b919680258d369114910511cc87595aec0be6d", - "symbol": "LYXe", - "name": "LUKSO Token" - }, - "count": 40 - }, - { - "baseCurrency": { - "address": "0x990f341946a3fdb507ae7e52d17851b87168017c", - "symbol": "STRONG", - "name": "Strong" - }, - "count": 40 - }, - { - "baseCurrency": { - "address": "0xe6279e1c65dd41b30ba3760dcac3cd8bbb4420d6", - "symbol": "REB", - "name": "Rebased" - }, - "count": 40 - }, - { - "baseCurrency": { - "address": "0xba2184520a1cc49a6159c57e61e1844e085615b6", - "symbol": "HGT", - "name": "HelloGold Token" - }, - "count": 39 - }, - { - "baseCurrency": { - "address": "0x04fa0d235c4abf4bcf4787af4cf447de572ef828", - "symbol": "UMA", - "name": "UMA Voting Token v1" - }, - "count": 39 - }, - { - "baseCurrency": { - "address": "0x48c276e8d03813224bb1e55f953adb6d02fd3e02", - "symbol": "KUMA", - "name": "Kuma Inu" - }, - "count": 39 - }, - { - "baseCurrency": { - "address": "0x9dfad1b7102d46b1b197b90095b5c4e9f5845bba", - "symbol": "BOTTO", - "name": "Botto" - }, - "count": 37 - }, - { - "baseCurrency": { - "address": "0xc4a11aaf6ea915ed7ac194161d2fc9384f15bff2", - "symbol": "WTON", - "name": "Wrapped TON" - }, - "count": 37 - }, - { - "baseCurrency": { - "address": "0xba11d00c5f74255f56a5e366f4f77f5a186d7f55", - "symbol": "BAND", - "name": "BandToken" - }, - "count": 35 - }, - { - "baseCurrency": { - "address": "0x0f51bb10119727a7e5ea3538074fb341f56b09ad", - "symbol": "DAO", - "name": "DAO Maker" - }, - "count": 33 - }, - { - "baseCurrency": { - "address": "0x18aaa7115705e8be94bffebde57af9bfc265b998", - "symbol": "AUDIO", - "name": "Audius" - }, - "count": 33 - }, - { - "baseCurrency": { - "address": "0x40fd72257597aa14c7231a7b1aaa29fce868f677", - "symbol": "XOR", - "name": "Sora Token" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0x79c75e2e8720b39e258f41c37cc4f309e0b0ff80", - "symbol": "SOUL", - "name": "Phantasma Stake" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0x7a5ce6abd131ea6b148a022cb76fc180ae3315a6", - "symbol": "bALPHA", - "name": "bAlpha" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0x77777feddddffc19ff86db637967013e6c6a116c", - "symbol": "TORN", - "name": "TornadoCash" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0x3ec8798b81485a254928b70cda1cf0a2bb0b74d7", - "symbol": "GRO", - "name": "Gro DAO Token" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0x80fb784b7ed66730e8b1dbd9820afd29931aab03", - "symbol": "LEND", - "name": "EthLend Token" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0xbbc2ae13b23d715c30720f079fcd9b4a74093505", - "symbol": "ERN", - "name": "@EthernityChain $ERN Token" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0x11e003e9ecc5a2320e8b11098acd550b928b6df2", - "symbol": "XXi", - "name": "Xi Transfer Coin" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0xc08512927d12348f6620a698105e1baac6ecd911", - "symbol": "GYEN", - "name": "GMO JPY" - }, - "count": 32 - }, - { - "baseCurrency": { - "address": "0x5f64ab1544d28732f0a24f4713c2c8ec0da089f0", - "symbol": "DEXTF", - "name": "DEXTF Token" - }, - "count": 31 - }, - { - "baseCurrency": { - "address": "0x6f40d4a6237c257fff2db00fa0510deeecd303eb", - "symbol": "INST", - "name": "Instadapp" - }, - "count": 31 - }, - { - "baseCurrency": { - "address": "0x2ba592f78db6436527729929aaf6c908497cb200", - "symbol": "CREAM", - "name": "Cream" - }, - "count": 31 - }, - { - "baseCurrency": { - "address": "0xcfeb09c3c5f0f78ad72166d55f9e6e9a60e96eec", - "symbol": "VEMP", - "name": "vEmpire Gamer Token" - }, - "count": 31 - }, - { - "baseCurrency": { - "address": "0x64b986211c0cc675143f895c437b79c3cadf364a", - "symbol": "GRPFT", - "name": "GRAPEFRUIT" - }, - "count": 30 - }, - { - "baseCurrency": { - "address": "0xc0829421c1d260bd3cb3e0f06cfe2d52db2ce315", - "symbol": "ETH", - "name": "Ether Token" - }, - "count": 29 - }, - { - "baseCurrency": { - "address": "0x6b4d5e9ec2acea23d4110f4803da99e25443c5df", - "symbol": "aKLIMA", - "name": "AlphaKlima" - }, - "count": 28 - }, - { - "baseCurrency": { - "address": "0xc52c326331e9ce41f04484d3b5e5648158028804", - "symbol": "ZCX", - "name": "ZEN Exchange Token" - }, - "count": 28 - }, - { - "baseCurrency": { - "address": "0xc4e15973e6ff2a35cc804c2cf9d2a1b817a8b40f", - "symbol": "ibBTC", - "name": "Interest-Bearing BTC" - }, - "count": 27 - }, - { - "baseCurrency": { - "address": "0x0d438f3b5175bebc262bf23753c1e53d03432bde", - "symbol": "wNXM", - "name": "Wrapped NXM" - }, - "count": 27 - }, - { - "baseCurrency": { - "address": "0x147e3d644d53adadd18a28201b91e2b98aeb7f94", - "symbol": "USE", - "name": "ElenaUSD" - }, - "count": 26 - }, - { - "baseCurrency": { - "address": "0x50d1c9771902476076ecfc8b2a83ad6b9355a4c9", - "symbol": "FTX Token", - "name": "FTT" - }, - "count": 26 - }, - { - "baseCurrency": { - "address": "0x2602278ee1882889b946eb11dc0e810075650983", - "symbol": "VADER", - "name": "Vader" - }, - "count": 26 - }, - { - "baseCurrency": { - "address": "0x47da5456bc2e1ce391b645ce80f2e97192e4976a", - "symbol": "PLUG", - "name": "Pl^g Token" - }, - "count": 25 - }, - { - "baseCurrency": { - "address": "0x37c997b35c619c21323f3518b9357914e8b99525", - "symbol": "PILOT", - "name": "Unipilot" - }, - "count": 25 - }, - { - "baseCurrency": { - "address": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84", - "symbol": "stETH", - "name": "Liquid staked Ether 2.0" - }, - "count": 24 - }, - { - "baseCurrency": { - "address": "0x8355dbe8b0e275abad27eb843f3eaf3fc855e525", - "symbol": "WOOL", - "name": "WOOL" - }, - "count": 24 - }, - { - "baseCurrency": { - "address": "0x85eee30c52b0b379b046fb0f85f4f3dc3009afec", - "symbol": "KEEP", - "name": "KEEP Token" - }, - "count": 23 - }, - { - "baseCurrency": { - "address": "0x6468e79a80c0eab0f9a2b574c8d5bc374af59414", - "symbol": "eXRD", - "name": "E-RADIX" - }, - "count": 23 - }, - { - "baseCurrency": { - "address": "0xf99d58e463a2e07e5692127302c20a191861b4d6", - "symbol": "ANY", - "name": "Anyswap" - }, - "count": 23 - }, - { - "baseCurrency": { - "address": "0xda86006036540822e0cd2861dbd2fd7ff9caa0e8", - "symbol": "TUBE2", - "name": "TUBE2" - }, - "count": 23 - }, - { - "baseCurrency": { - "address": "0x7ae0d42f23c33338de15bfa89c7405c068d9dc0a", - "symbol": "VERSE", - "name": "Shibaverse" - }, - "count": 22 - }, - { - "baseCurrency": { - "address": "0x7ff4169a6b5122b664c51c95727d87750ec07c84", - "symbol": "10SET", - "name": "10Set Token" - }, - "count": 22 - }, - { - "baseCurrency": { - "address": "0x43dfc4159d86f3a37a5a4b3d4580b888ad7d4ddd", - "symbol": "DODO", - "name": "DODO bird" - }, - "count": 22 - }, - { - "baseCurrency": { - "address": "0xbe9375c6a420d2eeb258962efb95551a5b722803", - "symbol": "STMX", - "name": "StormX" - }, - "count": 22 - }, - { - "baseCurrency": { - "address": "0x0ae055097c6d159879521c384f1d2123d1f195e6", - "symbol": "STAKE", - "name": "STAKE" - }, - "count": 21 - }, - { - "baseCurrency": { - "address": "0xc770eefad204b5180df6a14ee197d99d808ee52d", - "symbol": "FOX", - "name": "FOX" - }, - "count": 21 - }, - { - "baseCurrency": { - "address": "0x7a2bc711e19ba6aff6ce8246c546e8c4b4944dfd", - "symbol": "WAXE", - "name": "WAX Economic Token" - }, - "count": 20 - }, - { - "baseCurrency": { - "address": "0xc944e90c64b2c07662a292be6244bdf05cda44a7", - "symbol": "GRT", - "name": "Graph Token" - }, - "count": 20 - }, - { - "baseCurrency": { - "address": "0x9af15d7b8776fa296019979e70a5be53c714a7ec", - "symbol": "EVN", - "name": "Evn Token" - }, - "count": 20 - }, - { - "baseCurrency": { - "address": "0x12bb890508c125661e03b09ec06e404bc9289040", - "symbol": "RACA", - "name": "Radio Caca V2" - }, - "count": 20 - }, - { - "baseCurrency": { - "address": "0x9783b81438c24848f85848f8df31845097341771", - "symbol": "COLLAR", - "name": "DOG COLLAR" - }, - "count": 20 - }, - { - "baseCurrency": { - "address": "0xe6c3502997f97f9bde34cb165fbce191065e068f", - "symbol": "KBTC", - "name": "KBTC" - }, - "count": 20 - }, - { - "baseCurrency": { - "address": "0x70e8de73ce538da2beed35d14187f6959a8eca96", - "symbol": "XSGD", - "name": "XSGD" - }, - "count": 19 - }, - { - "baseCurrency": { - "address": "0xc5bddf9843308380375a611c18b50fb9341f502a", - "symbol": "yveCRV-DAO", - "name": "veCRV-DAO yVault" - }, - "count": 19 - }, - { - "baseCurrency": { - "address": "0xe95a203b1a91a908f9b9ce46459d101078c2c3cb", - "symbol": "aEth", - "name": "aEthereum" - }, - "count": 19 - }, - { - "baseCurrency": { - "address": "0xd2877702675e6ceb975b4a1dff9fb7baf4c91ea9", - "symbol": "LUNA", - "name": "Wrapped LUNA Token" - }, - "count": 19 - }, - { - "baseCurrency": { - "address": "0x269616d549d7e8eaa82dfb17028d0b212d11232a", - "symbol": "PUNK", - "name": "CryptoPunks" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x888888435fde8e7d4c54cab67f206e4199454c60", - "symbol": "DFX", - "name": "DFX Token" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x6dea81c8171d0ba574754ef6f8b412f2ed88c54d", - "symbol": "LQTY", - "name": "LQTY" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x9fa69536d1cda4a04cfb50688294de75b505a9ae", - "symbol": "DERC", - "name": "DeRace Token" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x232fb065d9d24c34708eedbf03724f2e95abe768", - "symbol": "SHEESHA", - "name": "Sheesha Finance" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x249f71f8d9da86c60f485e021b509a206667a079", - "symbol": "SNGJ", - "name": "Singular Japan" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x7bb594b3c757801346801f025699e39e7aaf5a49", - "symbol": "ORB", - "name": "Orb" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x037a54aab062628c9bbae1fdb1583c195585fe41", - "symbol": "LCX", - "name": "LCX" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x793b176a8b4833ea4aabcedfea55f2898bd6b753", - "symbol": "BRAIN", - "name": "BRAIN" - }, - "count": 18 - }, - { - "baseCurrency": { - "address": "0x89472e02e3550fd52971ed89a2682613b2c97199", - "symbol": "RC_WETH_750_DAI_2021_3_31", - "name": "Ruler Protocol rToken" - }, - "count": 17 - }, - { - "baseCurrency": { - "address": "0x2a8e1e676ec238d8a992307b495b45b3feaa5e86", - "symbol": "OUSD", - "name": "Origin Dollar" - }, - "count": 17 - }, - { - "baseCurrency": { - "address": "0xc4c2614e694cf534d407ee49f8e44d125e4681c4", - "symbol": "CHAIN", - "name": "Chain Games" - }, - "count": 17 - }, - { - "baseCurrency": { - "address": "0x8c15ef5b4b21951d50e53e4fbda8298ffad25057", - "symbol": "FX", - "name": "Function X" - }, - "count": 16 - }, - { - "baseCurrency": { - "address": "0x476c5e26a75bd202a9683ffd34359c0cc15be0ff", - "symbol": "SRM", - "name": "Serum" - }, - "count": 16 - }, - { - "baseCurrency": { - "address": "0xe9f84de264e91529af07fa2c746e934397810334", - "symbol": "SAK3", - "name": "Sake" - }, - "count": 16 - }, - { - "baseCurrency": { - "address": "0x1712aad2c773ee04bdc9114b32163c058321cd85", - "symbol": "LIMIT", - "name": "LimitSwap" - }, - "count": 16 - }, - { - "baseCurrency": { - "address": "0x4a220e6096b25eadb88358cb44068a3248254675", - "symbol": "QNT", - "name": "Quant" - }, - "count": 16 - }, - { - "baseCurrency": { - "address": "0x15874d65e649880c2614e7a480cb7c9a55787ff6", - "symbol": "eMax", - "name": "EthereumMax" - }, - "count": 16 - }, - { - "baseCurrency": { - "address": "0x1d350417d9787e000cc1b95d70e9536dcd91f373", - "symbol": "mIAU", - "name": "Wrapped Mirror IAU Token" - }, - "count": 16 - }, - { - "baseCurrency": { - "address": "0xa150db9b1fa65b44799d4dd949d922c0a33ee606", - "symbol": "DRC", - "name": "Digital Reserve Currency" - }, - "count": 16 - }, - { - "baseCurrency": { - "address": "0xe254e0878605a3d8b24d6cf7b4737a8431df1da1", - "symbol": "PLUG", - "name": "Pl^g Token Series A" - }, - "count": 15 - }, - { - "baseCurrency": { - "address": "0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b", - "symbol": "CRO", - "name": "CRO" - }, - "count": 15 - }, - { - "baseCurrency": { - "address": "0x674c6ad92fd080e4004b2312b45f796a192d27a0", - "symbol": "USDN", - "name": "Neutrino USD" - }, - "count": 15 - }, - { - "baseCurrency": { - "address": "0xf5581dfefd8fb0e4aec526be659cfab1f8c781da", - "symbol": "HOPR", - "name": "HOPR Token" - }, - "count": 15 - }, - { - "baseCurrency": { - "address": "0x3e5d9d8a63cc8a88748f229999cf59487e90721e", - "symbol": "XMT", - "name": "MetalSwap" - }, - "count": 15 - }, - { - "baseCurrency": { - "address": "0xa0afaa285ce85974c3c881256cb7f225e3a1178a", - "symbol": "wCRES", - "name": "Wrapped CRES" - }, - "count": 15 - }, - { - "baseCurrency": { - "address": "0x6c3f90f043a72fa612cbac8115ee7e52bde6e490", - "symbol": "3Crv", - "name": "Curve.fi DAI/USDC/USDT" - }, - "count": 15 - }, - { - "baseCurrency": { - "address": "0x8d85f8d69451d41dc266c920971b4aac1120257a", - "symbol": "CENNZ", - "name": "Centrality TOKEN" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x2b5ca2f9510cf1e3595ff219f24d75d4244585ea", - "symbol": "EPAY", - "name": "EthereumPay" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x69af81e73a73b40adf4f3d4223cd9b1ece623074", - "symbol": "MASK", - "name": "Mask Network" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x5ca30702d169637e656e7c655d9b9acb7a160e7e", - "symbol": "UFODOGE", - "name": "THE DOGE TRUTH" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x68a118ef45063051eac49c7e647ce5ace48a68a5", - "symbol": "$BASED", - "name": "$BASED" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0xef2e239b21a81719abaabc6239e2e90859b44d9b", - "symbol": "TTX", - "name": "TTX NFT" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x0ada190c81b814548ddc2f6adc4a689ce7c1fe73", - "symbol": "YAXIS", - "name": "yAxis V2" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0xc40af1e4fecfa05ce6bab79dcd8b373d2e436c4e", - "symbol": "HOKK", - "name": "Hokkaidu Inu" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x054d64b73d3d8a21af3d764efd76bcaa774f3bb2", - "symbol": "PPAY", - "name": "Plasma" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x367b072d50ac465ba47ab474c6d381bfc0e45f86", - "symbol": "CAPY", - "name": "Capy" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x80ab141f324c3d6f2b18b030f1c4e95d4d658778", - "symbol": "DEA", - "name": "DEA" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x368df85396be56f1d475e5d10339211a77e13c32", - "symbol": "RC_WBTC_25000_DAI_2021_3_31", - "name": "Ruler Protocol rToken" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x967da4048cd07ab37855c090aaf366e4ce1b9f48", - "symbol": "OCEAN", - "name": "Ocean Token" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x3b62f3820e0b035cc4ad602dece6d796bc325325", - "symbol": "DEUS", - "name": "DEUS" - }, - "count": 14 - }, - { - "baseCurrency": { - "address": "0x0dd1989e4b0e82f154b729ff47f8c9a4f4b2cc1c", - "symbol": "MILF", - "name": "Milf Token" - }, - "count": 13 - }, - { - "baseCurrency": { - "address": "0xa7de087329bfcda5639247f96140f9dabe3deed1", - "symbol": "STA", - "name": "Statera" - }, - "count": 13 - }, - { - "baseCurrency": { - "address": "0x5228a22e72ccc52d415ecfd199f99d0665e7733b", - "symbol": "pBTC", - "name": "pTokens BTC" - }, - "count": 13 - }, - { - "baseCurrency": { - "address": "0x69681f8fde45345c3870bcd5eaf4a05a60e7d227", - "symbol": "ibGBP", - "name": "Iron Bank GBP" - }, - "count": 13 - }, - { - "baseCurrency": { - "address": "0x6149c26cd2f7b5ccdb32029af817123f6e37df5b", - "symbol": "LPOOL", - "name": "Launchpool token" - }, - "count": 13 - }, - { - "baseCurrency": { - "address": "0xe31debd7abff90b06bca21010dd860d8701fd901", - "symbol": "UBOMB", - "name": "UniBomb" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0xf411903cbc70a74d22900a5de66a2dda66507255", - "symbol": "VRA", - "name": "VERA" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0xddd6a0ecc3c6f6c102e5ea3d8af7b801d1a77ac8", - "symbol": "UNIX", - "name": "UniX Gaming" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0x632fd938b98d740faee69112ed41257be3f83c7c", - "symbol": "MXS", - "name": "Matrix Samurai Token" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0x327673ae6b33bd3d90f0096870059994f30dc8af", - "symbol": "LMT", - "name": "Lympo Market Token" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0x21e783bcf445b515957a10e992ad3c8e9ff51288", - "symbol": "LGB", - "name": "Let's Go Brandon" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0x9c2dc0c3cc2badde84b0025cf4df1c5af288d835", - "symbol": "COR", - "name": "COR Token" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0xd075e95423c5c4ba1e122cae0f4cdfa19b82881b", - "symbol": "wPE", - "name": "OPES Finance" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0x3431f91b3a388115f00c5ba9fdb899851d005fb5", - "symbol": "GERO", - "name": "GeroWallet" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0x09ce2b746c32528b7d864a1e3979bd97d2f095ab", - "symbol": "DFL", - "name": "DeFIL-V2" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0xe4815ae53b124e7263f08dcdbbb757d41ed658c6", - "symbol": "ZKS", - "name": "Zks" - }, - "count": 12 - }, - { - "baseCurrency": { - "address": "0xeec2be5c91ae7f8a338e1e5f3b5de49d07afdc81", - "symbol": "DPX", - "name": "Dopex Governance Token" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x1e18821e69b9faa8e6e75dffe54e7e25754beda0", - "symbol": "KIMCHI", - "name": "KIMCHI.finance" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x48fb253446873234f2febbf9bdeaa72d9d387f94", - "symbol": "vBNT", - "name": "Bancor Governance Token" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0xbc4171f45ef0ef66e76f979df021a34b46dcc81d", - "symbol": "DORA", - "name": "Dorayaki" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x9e32b13ce7f2e80a01932b42553652e053d6ed8e", - "symbol": "Metis", - "name": "Metis Token" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x1614f18fc94f47967a3fbe5ffcd46d4e7da3d787", - "symbol": "PAID", - "name": "PAID Network" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x9d409a0a012cfba9b15f6d4b36ac57a46966ab9a", - "symbol": "yvBOOST", - "name": "Yearn Compounding veCRV yVault" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", - "symbol": "wstETH", - "name": "Wrapped liquid staked Ether 2.0" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x67b6d479c7bb412c54e03dca8e1bc6740ce6b99c", - "symbol": "KYL", - "name": "Kylin Network" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x0c7d5ae016f806603cb1782bea29ac69471cab9c", - "symbol": "BFC", - "name": "Bifrost" - }, - "count": 11 - }, - { - "baseCurrency": { - "address": "0x7c8155909cd385f120a56ef90728dd50f9ccbe52", - "symbol": "NII", - "name": "Nahmii" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x22b6c31c2beb8f2d0d5373146eed41ab9ede3caf", - "symbol": "COC", - "name": "CocktailBar" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0xdfe66b14d37c77f4e9b180ceb433d1b164f0281d", - "symbol": "stETH", - "name": "stakedETH" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x936ff029bcefb5a12275b9a72f79f2bed17a85fd", - "symbol": "BLACKHOLE", - "name": "Black Hole" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x72e5390edb7727e3d4e3436451dadaff675dbcc0", - "symbol": "HANU", - "name": "Hanu Yokia" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0xddb3422497e61e13543bea06989c0789117555c5", - "symbol": "COTI", - "name": "COTI Token" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0xdc59ac4fefa32293a95889dc396682858d52e5db", - "symbol": "BEAN", - "name": "Bean" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x5caf454ba92e6f2c929df14667ee360ed9fd5b26", - "symbol": "DEV", - "name": "Dev" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x62786eeacc9246b4018e0146cb7a3efeacd9459d", - "symbol": "LESS", - "name": "LessToken" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x398aea1c9ceb7de800284bb399a15e0efe5a9ec2", - "symbol": "sILV", - "name": "Escrowed Illuvium" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x04f2694c8fcee23e8fd0dfea1d4f5bb8c352111f", - "symbol": "sOHM", - "name": "Staked Olympus" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0xdc98c5543f3004debfaad8966ec403093d0aa4a8", - "symbol": "PEBBLE", - "name": "Etherrock #72" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0xb1191f691a355b43542bea9b8847bc73e7abb137", - "symbol": "KIRO", - "name": "Kirobo" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x8ca33160a8a2a9cb6d435f843bdd176241e2237e", - "symbol": "XES", - "name": "Proxeus Pre" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0xa9b1eb5908cfc3cdf91f9b8b3a74108598009096", - "symbol": "Auction", - "name": "Bounce Token" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x0258f474786ddfd37abce6df6bbb1dd5dfc4434a", - "symbol": "ORN", - "name": "Orion Protocol" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x056fd409e1d7a124bd7017459dfea2f387b6d5cd", - "symbol": "GUSD", - "name": "Gemini dollar" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0x7f0693074f8064cfbcf9fa6e5a3fa0e4f58ccccf", - "symbol": "gm", - "name": "we love gm" - }, - "count": 10 - }, - { - "baseCurrency": { - "address": "0xcc4304a31d09258b0029ea7fe63d032f52e44efe", - "symbol": "SWAP", - "name": "TrustSwap Token" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359", - "symbol": "DAI", - "name": "Dai Stablecoin v1.0" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x8f8221afbb33998d8584a2b05749ba73c37a938a", - "symbol": "REQ", - "name": "Request Token" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x1c7bbadc81e18f7177a95eb1593e5f5f35861b10", - "symbol": "AUSCM", - "name": "Auric Network" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x33349b282065b0284d756f0577fb39c158f935e6", - "symbol": "MPL", - "name": "Maple Token" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x68037790a0229e9ce6eaa8a99ea92964106c4703", - "symbol": "PAR", - "name": "PAR Stablecoin" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x4c19596f5aaff459fa38b0f7ed92f11ae6543784", - "symbol": "TRU", - "name": "TrueFi" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x7f3edcdd180dbe4819bd98fee8929b5cedb3adeb", - "symbol": "XTK", - "name": "xToken" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x2367012ab9c3da91290f71590d5ce217721eefe4", - "symbol": "xSNXa", - "name": "xSNX" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x10be9a8dae441d276a5027936c3aaded2d82bc15", - "symbol": "UMX", - "name": "https://unimex.network/" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0xa4eed63db85311e22df4473f87ccfc3dadcfa3e3", - "symbol": "RBC", - "name": "Rubic" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x20f6a313cb250062331fe70b9567e3ee5f01888b", - "symbol": "WGMI", - "name": "WGMI" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x80d55c03180349fff4a229102f62328220a96444", - "symbol": "OPUL", - "name": "OpulousToken" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x26ce25148832c04f3d7f26f32478a9fe55197166", - "symbol": "DEXT", - "name": "DEXTools" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0xa499648fd0e80fd911972bbeb069e4c20e68bf22", - "symbol": "uJENNY", - "name": "Jenny Metaverse DAO Token" - }, - "count": 9 - }, - { - "baseCurrency": { - "address": "0x9bb6fd000109e24eb38b0deb806382ff9247e478", - "symbol": "HXB", - "name": "hex.bet" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x808507121b80c02388fad14726482e061b8da827", - "symbol": "PENDLE", - "name": "Pendle" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x5d8d9f5b96f4438195be9b99eee6118ed4304286", - "symbol": "COVER", - "name": "Cover Protocol" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x02fdeea649d0291a987438fdaab5a71ed623471f", - "symbol": "ArtDeco", - "name": "ArtDeco" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xb5a1779af011d64e0958e5af33d7e0c1dd658b8e", - "symbol": "mFloki", - "name": "MiniFloki" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x467bccd9d29f223bce8043b84e8c8b282827790f", - "symbol": "TEL", - "name": "Telcoin" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x14eb60f5f270b059b0c788de0ddc51da86f8a06d", - "symbol": "KCAL", - "name": "Phantasma Energy" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xc276a3d9c5443de32fde5b5719763c6ff7790a10", - "symbol": "FREN", - "name": "DogeFren" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x0dc7d0192c148d7d2d6fa32dc280f953c0ad6a34", - "symbol": "MetaCat", - "name": "MetaCat" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xc3d088842dcf02c13699f936bb83dfbbc6f721ab", - "symbol": "vETH", - "name": "Voucher Ethereum" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xd4b3949b10490a9e596697e052652ca2d55abba0", - "symbol": "NAUSICAA", - "name": " Nausicaa Inu " - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xde30da39c46104798bb5aa3fe8b9e0e1f348163f", - "symbol": "GTC", - "name": "Gitcoin" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xc2bd7a597391f31d2e36c2cb769fce0e5bd6d482", - "symbol": "FREE", - "name": "Anti-Lockdown" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x71f85b2e46976bd21302b64329868fd15eb0d127", - "symbol": "AXN", - "name": "Axion" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xe2311ae37502105b442bbef831e9b53c5d2e9b3b", - "symbol": "BANANA", - "name": "Banana" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xf063fe1ab7a291c5d06a86e14730b00bf24cb589", - "symbol": "SALE", - "name": "DxSale.Network" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x2b6e03f3137b41d30d54e87517c053a1f2d96390", - "symbol": "AST1", - "name": "Aster-1H" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x8b39b70e39aa811b69365398e0aace9bee238aeb", - "symbol": "PKF", - "name": "PolkaFoundry" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x8a6d4c8735371ebaf8874fbd518b56edd66024eb", - "symbol": "BLOCKS", - "name": "BLOCKS" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x27054b13b1b798b345b591a4d22e6562d47ea75a", - "symbol": "AST", - "name": "AirSwap Token" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x03066da434e5264ef0b32f787923f974a5726fdc", - "symbol": "BCS", - "name": "BCS" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0x8a14897ea5f668f36671678593fae44ae23b39fb", - "symbol": "3DOG", - "name": "Cerberus" - }, - "count": 8 - }, - { - "baseCurrency": { - "address": "0xd23ac27148af6a2f339bd82d0e3cff380b5093de", - "symbol": "SI", - "name": "SIREN" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xeb269732ab75a6fd61ea60b06fe994cd32a83549", - "symbol": "", - "name": "" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xf5a562597d5fb5cc19482379755e1a5275a6607b", - "symbol": "XFOC", - "name": "FOOT COIN" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x26a79bd709a7ef5e5f747b8d8f83326ea044d8cc", - "symbol": "BSOCIAL", - "name": "BankSocial" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xa3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2", - "symbol": "MTA", - "name": "Meta" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x0d8775f648430679a709e98d2b0cb6250d2887ef", - "symbol": "BAT", - "name": "Basic Attention Token" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x0aacfbec6a24756c20d41914f2caba817c0d8521", - "symbol": "YAM", - "name": "YAM" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xd13c7342e1ef687c5ad21b27c2b65d772cab5c8c", - "symbol": "UOS", - "name": "Ultra Token" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xee06a81a695750e71a662b51066f2c74cf4478a0", - "symbol": "$DG", - "name": "decentral.games" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xffffffff2ba8f66d4e51811c5190992176930278", - "symbol": "COMBO", - "name": "Furucombo" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xfa6de2697d59e88ed7fc4dfe5a33dac43565ea41", - "symbol": "DEFI5", - "name": "DEFI Top 5 Tokens Index" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x9355372396e3f6daf13359b7b607a3374cc638e0", - "symbol": "WHALE", - "name": "WHALE" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x95a4492f028aa1fd432ea71146b433e7b4446611", - "symbol": "APY", - "name": "APY Governance Token" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x4e0fca55a6c3a94720ded91153a27f60e26b9aa8", - "symbol": "BOOST", - "name": "Boost" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xd0660cd418a64a1d44e9214ad8e459324d8157f1", - "symbol": "WOOFY", - "name": "Woofy" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x73968b9a57c6e53d41345fd57a6e6ae27d6cdb2f", - "symbol": "SDT", - "name": "Stake DAO Token" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x4a3e164684812dfb684ac36457e7fa805087c68e", - "symbol": "YUAN", - "name": "YUAN" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xe4cfe9eaa8cdb0942a80b7bc68fd8ab0f6d44903", - "symbol": "XEND", - "name": "XEND" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x8207c1ffc5b6804f6024322ccf34f29c3541ae26", - "symbol": "OGN", - "name": "OriginToken" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x33d0568941c0c64ff7e0fb4fba0b11bd37deed9f", - "symbol": "RAMP", - "name": "RAMP DEFI" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0x5dbcf33d8c2e976c6b560249878e6f1491bca25c", - "symbol": "yyDAI+yUSDC+yUSDT+yTUSD", - "name": "yearn Curve.fi yDAI/yUSDC/yUSDT/yTUSD" - }, - "count": 7 - }, - { - "baseCurrency": { - "address": "0xe85ca2779645705185009a4ca33b9a7f0e3aaf1e", - "symbol": "LEAFA", - "name": " Leafa Inu " - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x7418a4d1a8aaecb45d0bc32aa2a171f42aa0df81", - "symbol": "SHAMAN", - "name": " SHAMAN INU " - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xfc0d6cf33e38bce7ca7d89c0e292274031b7157a", - "symbol": "NTVRK", - "name": "NETVRK" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x491d6b7d6822d5d4bc88a1264e1b47791fd8e904", - "symbol": "LONDON", - "name": "LONDON" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x7b123f53421b1bf8533339bfbdc7c98aa94163db", - "symbol": "buidl", - "name": "dfohub" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xc73c167e7a4ba109e4052f70d5466d0c312a344d", - "symbol": "SANSHU", - "name": "Sanshu Inu" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xcc8fa225d80b9c7d42f96e9570156c65d6caaa25", - "symbol": "SLP", - "name": "Smooth Love Potion" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xd4fffa07929b1901fdb30c1c67f80e1185d4210f", - "symbol": "CERTI", - "name": "CERTI" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x45804880de22913dafe09f4980848ece6ecbaf78", - "symbol": "PAXG", - "name": "Paxos Gold" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xc690f7c7fcffa6a82b79fab7508c466fefdfc8c5", - "symbol": "LYM", - "name": "Lympo tokens" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x41f7b8b9b897276b7aae926a9016935280b44e97", - "symbol": "", - "name": " (Wormhole)" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x8ecd8139bfda5fb572983a2a097c1c84dd2ed613", - "symbol": "eCum", - "name": "EthereumCum " - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xed0c64681cd4c4c4ee94776f87b20951dbdda1ef", - "symbol": "SMINU", - "name": "Smart-Inu" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x90b831fa3bebf58e9744a14d638e25b4ee06f9bc", - "symbol": "MIMO", - "name": "MIMO Parallel Governance Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x9ed8e7c9604790f7ec589f99b94361d8aab64e5e", - "symbol": "UNISTAKE", - "name": "Unistake" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x00a8b738e453ffd858a7edf03bccfe20412f0eb0", - "symbol": "ALBT", - "name": "AllianceBlock Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x2772b0dd22381932b104d683e73c5ecb5a3a2408", - "symbol": "mLOOT", - "name": "LootMart" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xd6daecbba57eae35026a92b972e731932b0434a5", - "symbol": "SHB5", - "name": "Super Heavy Booster 5 | https://t.me/superheavybooster5" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x3e3cda3218212503883c79f6d2feef29e6bbb87d", - "symbol": "GPYX", - "name": "GpyxToken" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x9a86aabb4fbaa547b007a0ffaf86748db0f50774", - "symbol": "NOS", - "name": "NoScam" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x3622d034fbf3baec7060c7cb484a121389684986", - "symbol": "RKYU", - "name": "RyukyuToken" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x321c2fe4446c7c963dc41dd58879af648838f98d", - "symbol": "CTX", - "name": "Cryptex" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x9b7f62b942bc707e14bc0189b0f95c75a06f50e6", - "symbol": "K9", - "name": " K9 Inu " - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x01d8531005ce845485e7150e7ef8761c7ba63e20", - "symbol": "CZ", - "name": "ChangPengZhao Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xf1a91c7d44768070f711c68f33a7ca25c8d30268", - "symbol": "C3", - "name": "CHARLI3" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xdeaf554114b3481a3ed820fc06bfb51174ef84ac", - "symbol": "3TH", - "name": "3TH" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x9521c632fca479d8800aa42910d1a45efd76943a", - "symbol": "CHARLOTTE", - "name": " Charlotte Inu " - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x87b008e57f640d94ee44fd893f0323af933f9195", - "symbol": "COIN", - "name": "coin_artist" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x89ab32156e46f46d02ade3fecbe5fc4243b9aaed", - "symbol": "PNT", - "name": "pNetwork Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x939a7a577d93ad29b64c1595b1284ce660a479b9", - "symbol": "JEJUDOGE", - "name": "Jejudoge" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xa184c89d44dfe9692a71f9f936cc80749cde42af", - "symbol": "GMGN", - "name": "GMGN" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xad4f86a25bbc20ffb751f2fac312a0b4d8f88c64", - "symbol": "ROOM", - "name": "OptionRoom Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x60e46a4dd91d10506d8efa2caa266e7191fe7ea8", - "symbol": "FEWGO", - "name": "Fewmans Gold" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x00c83aecc790e8a4453e5dd3b0b4b3680501a7a7", - "symbol": "SKL", - "name": "SKALE" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xfc98e825a2264d890f9a1e68ed50e1526abccacd", - "symbol": "MCO2", - "name": "Moss Carbon Credit" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xd2dda223b2617cb616c1580db421e4cfae6a8a85", - "symbol": "BONDLY", - "name": "Bondly Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xc67b12049c2d0cf6e476bc64c7f82fc6c63cffc5", - "symbol": "GDT", - "name": "GDT" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x7659ce147d0e714454073a5dd7003544234b6aa0", - "symbol": "XCAD", - "name": "XCAD Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x695106ad73f506f9d0a9650a78019a93149ae07c", - "symbol": "BNS", - "name": "BNS Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x509eece4793b46e4d81c0d4cb0a5a0c26c45e304", - "symbol": "FGLD", - "name": "First Gold" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x4aa41bc1649c9c3177ed16caaa11482295fc7441", - "symbol": "XFIT", - "name": "XFIT" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x6fb1e018f107d3352506c23777e4cd62e063584a", - "symbol": "IDTT", - "name": "Identity" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x656c00e1bcd96f256f224ad9112ff426ef053733", - "symbol": "EFI", - "name": "Efinity Token" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xfb130d93e49dca13264344966a611dc79a456bc5", - "symbol": "DOGEGF", - "name": "DogeGF" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x511a17a6bfa199d9437f348a24be04cd2945bb29", - "symbol": "HUMAN", - "name": "Humankind" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0x948c70dc6169bfb10028fdbe96cbc72e9562b2ac", - "symbol": "XP", - "name": "PolkaFantasy" - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xf397ad6deb26428cf65748b538becaf3f9a04294", - "symbol": "Eevee Inu ", - "name": " Eevee Inu " - }, - "count": 6 - }, - { - "baseCurrency": { - "address": "0xd5525d397898e5502075ea5e830d8914f6f0affe", - "symbol": "MEME", - "name": "MEME" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xe66894c93cbbe66e0fb2fcf5e430229bf5bb5ed9", - "symbol": "PLUG-B", - "name": "Pl^g Token Series B" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xd0345d30fd918d7682398acbcdf139c808998709", - "symbol": "LIX", - "name": "Lixir Token" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x7d29a64504629172a429e64183d6673b9dacbfce", - "symbol": "VXV", - "name": "VectorspaceAI" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x2f109021afe75b949429fe30523ee7c0d5b27207", - "symbol": "OCC", - "name": "OCC" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xdddddd4301a082e62e84e43f474f044423921918", - "symbol": "DVF", - "name": "DeversiFi Token" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x8c8687fc965593dfb2f0b4eaefd55e9d8df348df", - "symbol": "PAID", - "name": "PAID Network" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x8400d94a5cb0fa0d041a3788e395285d61c9ee5e", - "symbol": "UBT", - "name": "UniBright" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xaba8cac6866b83ae4eec97dd07ed254282f6ad8a", - "symbol": "YAMv2", - "name": "YAMv2" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xa8b12cc90abf65191532a12bb5394a714a46d358", - "symbol": "pBTC35A", - "name": "POW BTC-35W/T" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x7ddc52c4de30e94be3a6a0a2b259b2850f421989", - "symbol": "GMT", - "name": "GoMining Token" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xdf801468a808a32656d2ed2d2d80b72a129739f4", - "symbol": "CUBE", - "name": "Somnium Space Cubes" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x1a7981d87e3b6a95c1516eb820e223fe979896b3", - "symbol": "MCC", - "name": "MultiChainCapital" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xeef9f339514298c6a857efcfc1a762af84438dee", - "symbol": "HEZ", - "name": "Hermez Network Token" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x379ec11344929da69ddfd805d564608089c97e55", - "symbol": "DRF", - "name": "DragonFruit" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xf418588522d5dd018b425e472991e52ebbeeeeee", - "symbol": "PUSH", - "name": "Ethereum Push Notification Service" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xa36fdbbae3c9d55a1d67ee5821d53b50b63a1ab9", - "symbol": "TEMP", - "name": "Tempus" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xe76c6c83af64e4c60245d8c7de953df673a7a33d", - "symbol": "RAIL", - "name": "Rail" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xdf5e0e81dff6faf3a7e52ba697820c5e32d806a8", - "symbol": "yDAI+yUSDC+yUSDT+yTUSD", - "name": "Curve.fi yDAI/yUSDC/yUSDT/yTUSD" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x4ceda7906a5ed2179785cd3a40a69ee8bc99c466", - "symbol": "AION", - "name": "AION" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x6399c842dd2be3de30bf99bc7d1bbf6fa3650e70", - "symbol": "PREMIA", - "name": "Premia" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xfc05205ff402849b081e72ae4f8fc93cbdf40db1", - "symbol": "Lelouch", - "name": "Lelouch Lamperouge" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x4ece5c5cfb9b960a49aae739e15cdb6cfdcc5782", - "symbol": "DBUY", - "name": "Doont Buy" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x68749665ff8d2d112fa859aa293f07a622782f38", - "symbol": "XAUt", - "name": "Tether Gold" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x4123a133ae3c521fd134d7b13a2dec35b56c2463", - "symbol": "QRDO", - "name": "Qredo Token" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x3383c5a8969dc413bfddc9656eb80a1408e4ba20", - "symbol": "wANATHA", - "name": "Wrapped ANATHA" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x16c52ceece2ed57dad87319d91b5e3637d50afa4", - "symbol": "TCAP", - "name": "TCAP Token" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0xdc349913d53b446485e98b76800b6254f43df695", - "symbol": "BEZOGE", - "name": "Bezoge Earth" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x3ec99d9ab6e6f144d47bce3c0f6183b23f9ff0ec", - "symbol": "SYLO", - "name": "SYLO Series A Token" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x1f8a626883d7724dbd59ef51cbd4bf1cf2016d13", - "symbol": "STAK", - "name": "Jigstack" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x1036c90a7dea8a392901147870f74a40af812bb4", - "symbol": "RUSD", - "name": "Ring USD" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x8290d7a64f25e6b5002d98367e8367c1b532b534", - "symbol": "oneUNI", - "name": "oneUNI" - }, - "count": 5 - }, - { - "baseCurrency": { - "address": "0x0d3b8cdd9748a1f6fa67a0ca89111de92901e29a", - "symbol": "PSHIB", - "name": "Polkashiba" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x704de5696df237c5b9ba0de9ba7e0c63da8ea0df", - "symbol": "xAAVEb", - "name": "xAAVE" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xfeee0b5ec835932e42f0c029e918ffa4ded19a70", - "symbol": "SBC DAO", - "name": " Small Brain Capital DAO " - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xebd9d99a3982d547c5bb4db7e3b1f9f14b67eb83", - "symbol": "ID", - "name": "Everest ID" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x3d56b44d1eccdeadffd5be1282762317455cda32", - "symbol": "FOMO", - "name": "FOMO World" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x2924f92520297d5b188c90d319836c93a7271c7f", - "symbol": "SCREAMINU", - "name": " Scream Inu " - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xc5c8465293cb140d3f98bd644233e93871bd604a", - "symbol": "SqINU", - "name": "Squirtle Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x417e1a6393596f2a4eb3ad667ca2fbe57802575d", - "symbol": "CHAIN", - "name": "CHAIN FURY | t.me/ChainFury" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x55296f69f40ea6d20e478533c15a6b08b654e758", - "symbol": "XYO", - "name": "XY Oracle" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xcd1faff6e578fa5cac469d2418c95671ba1a62fe", - "symbol": "XTM", - "name": "Torum" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x0575112525f2778c14c4ca3658430c85de400045", - "symbol": "WAGMI", - "name": "WAGMI" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xa42fa7e43354cefbea5c822362367dfaee092cb3", - "symbol": "STARB", - "name": "StarbaseGQ" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x19d4beb2bc410b80084fcbd0ac024326f4d3a370", - "symbol": "ViCA", - "name": "ViCA Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf80d589b3dbe130c270a69f1a69d050f268786df", - "symbol": "DAM", - "name": "Datamine" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x31429d1856ad1377a8a0079410b297e1a9e214c2", - "symbol": "ANGLE", - "name": "ANGLE" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x626e8036deb333b408be468f951bdb42433cbf18", - "symbol": "AIOZ", - "name": "AIOZ Network" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xe0e05c43c097b0982db6c9d626c4eb9e95c3b9ce", - "symbol": "USF", - "name": "Unslashed Finance Governance Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x0e192d382a36de7011f795acc4391cd302003606", - "symbol": "FST", - "name": "Futureswap Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x6710c63432a2de02954fc0f851db07146a6c0312", - "symbol": "MFG", - "name": "SyncFab Smart Manufacturing Blockchain" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x64b4aba89b57400199b5b7785cad0b5c2bfcc296", - "symbol": "SOCRAT", - "name": "Socrat" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x799ebfabe77a6e34311eeee9825190b9ece32824", - "symbol": "BTRST", - "name": "BTRST" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x7ae1d57b58fa6411f32948314badd83583ee0e8c", - "symbol": "PAPER", - "name": "Paper" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xc834fa996fa3bec7aad3693af486ae53d8aa8b50", - "symbol": "CONV", - "name": "Convergence" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xd478161c952357f05f0292b56012cd8457f1cfbf", - "symbol": "POLK", - "name": "Polkamarkets" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x1aa61c196e76805fcbe394ea00e4ffced24fc469", - "symbol": "SAFE", - "name": "yieldfarming.insure" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x37da95e0aa5a2edb84189f9aea6f9f4c38c0f038", - "symbol": "69", - "name": "SixtyNine" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x120a3879da835a5af037bb2d1456bebd6b54d4ba", - "symbol": "RVST", - "name": "Revest" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xcb3df3108635932d912632ef7132d03ecfc39080", - "symbol": "WING", - "name": "Wings" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x83eed088eb2b7005ae4b4c94cfc17fb4ffab3393", - "symbol": "CHINU", - "name": "Charmander Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x47d94ab5e824090f3fd8ab14d9033a079a97d6db", - "symbol": "MSHIBA", - "name": "Momma Shiba" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xab5d99d2c931d2bb4a9f0168a2a9778fa1e4fdb0", - "symbol": "GIZMO", - "name": " Gizmo Token " - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x5968a5d4cc5bf135d787486824c8031cd9e2291f", - "symbol": "COPE", - "name": "COPE" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x0e4e860f8bdc0a494cc9c4649ba1d0291d8d5f6b", - "symbol": "OOP", - "name": "OcarinaOfPump.com" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xe66b3aa360bb78468c00bebe163630269db3324f", - "symbol": "MTO", - "name": "Merchant Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xd8dfe718f9522525ecc44e5ddd1d7141a012c4fb", - "symbol": "METAGOKU", - "name": "Meta Goku" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x12a24acb8867f02fb06614728d0b6b64ff3c4bae", - "symbol": "BLACKSTALLION", - "name": "BLACKSTALLION" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x2bfe554b98db945d87c6262b88bb4a98ab39ae7f", - "symbol": "AKITACASH", - "name": "AkitaCash Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf4259815fe2410937ab2e5148acfcfbd27b7a5fd", - "symbol": "Interstellar", - "name": "? Interstellar ?" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xa1d6df714f91debf4e0802a542e13067f31b8262", - "symbol": "RFOX", - "name": "RFOX" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x9275e8386a5bdda160c0e621e9a6067b8fd88ea2", - "symbol": "NBNG", - "name": "NOBUNAGA" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x9a7af5307a4277d2dc95cdcd5709c806c095a44d", - "symbol": "DIADOGE", - "name": "Diamond Doge" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xfa898efdb91e35bd311c45b9b955f742b6719aa2", - "symbol": "APED", - "name": "Baddest Alpha Ape Bundle " - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x0b498ff89709d3838a063f1dfa463091f9801c2b", - "symbol": "BTC2x-FLI", - "name": "BTC 2x Flexible Leverage Index" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xec1b7eb3d3cfac7027fa60b5376e5eadef4f1300", - "symbol": "HZM", - "name": "HZMCOIN" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x6286a9e6f7e745a6d884561d88f94542d6715698", - "symbol": "TECH", - "name": "Cryptomeda" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x68a3637ba6e75c0f66b61a42639c4e9fcd3d4824", - "symbol": "MOON", - "name": "MoonToken" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x5979f50f1d4c08f9a53863c2f39a7b0492c38d0f", - "symbol": "pLTC", - "name": "pTokens LTC" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x550fd2fc0998ce37246a0a5359709f97423ae2e0", - "symbol": "MPY", - "name": "Meta Puppy" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xa77e9683c394ac065c355f6683e5a523cba388a5", - "symbol": "WOGE", - "name": "White Doge" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xb05097849bca421a3f51b249ba6cca4af4b97cb9", - "symbol": "FLOAT", - "name": "Float Protocol: FLOAT" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x8c9d0e9d27fb15a96cd8ad714929502f4f1995bf", - "symbol": "SHFL", - "name": "SHUFFLE!" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x83695d4a8b007c4dcbbed05c6ab8abf8263ffa71", - "symbol": "CAKEINU", - "name": "Cake Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x5b102768672cae6b64d06b8033ae1977e0dbfedf", - "symbol": "LEBRON", - "name": "Lebron Doge" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x8762db106b2c2a0bccb3a80d1ed41273552616e8", - "symbol": "RSR", - "name": "Reserve Rights" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xfca59cd816ab1ead66534d82bc21e7515ce441cf", - "symbol": "RARI", - "name": "Rarible" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xa02120696c7b8fe16c09c749e4598819b2b0e915", - "symbol": "WXT", - "name": "Wirex Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x2aeccb42482cc64e087b6d2e5da39f5a7a7001f8", - "symbol": "RULER", - "name": "Ruler Protocol" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf29cdfc5b50cbb396d674042a62286d2035ffbdf", - "symbol": "ODA", - "name": " Oda Inu " - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x6b785a0322126826d8226d77e173d75dafb84d11", - "symbol": "VLT", - "name": "Bankroll Vault" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xeda5b6e0a37b3ddd7f0ab1ace26b917db49b0384", - "symbol": "PKINU", - "name": "PINKY INU www.pinkyinu.site" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xc3761eb917cd790b30dad99f6cc5b4ff93c4f9ea", - "symbol": "ERC20", - "name": "ERC20" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xffd93c0252ada18ca06b881282800d9893b06223", - "symbol": "MFS", - "name": "My First Shiba" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x07ca256267128fbe1a79b74fc7b0e6ed3359ad08", - "symbol": "WWT", - "name": "Wormhole Wrapped - 1-c261d86cf5cf877128ba1f5652e5f3dda599270c1556a15bc0a9aacf8063e563" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x5166e09628b696285e3a151e84fb977736a83575", - "symbol": "VOL", - "name": "Volatility Protocol Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x3322ce66fb4b657333e2140c00934a618848bdca", - "symbol": "HINU", - "name": "High Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf94b5c5651c888d928439ab6514b93944eee6f48", - "symbol": "YLD", - "name": "Yield" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x1e05f68b29b286fb3bbad3c688d7e2abda549b80", - "symbol": "PICIPO", - "name": "PICIPO" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x639ae8f3eed18690bf451229d14953a5a5627b72", - "symbol": "GNBU", - "name": "Nimbus Governance Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x87edffde3e14c7a66c9b9724747a1c5696b742e6", - "symbol": "SWAG", - "name": "Swag Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xa1e72267084192db7387c8cc1328fade470e4149", - "symbol": "TFI-LP", - "name": "TrueFi LP" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xd796b8eff23d5c4c71f43c99ffd7d8a3119f7475", - "symbol": "METACEX", - "name": "Metaverse Exchange" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xcbd55d4ffc43467142761a764763652b48b969ff", - "symbol": "ASTRO", - "name": "AstroTools.io" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x48271da3a74834f30534f5019483b89c4021736a", - "symbol": "OPM", - "name": "OnePunchManETH" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf431dec6a097b5f4436fe952cc63db7fddb46cc9", - "symbol": "LEECH", - "name": "Parasite" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf4cd3d3fda8d7fd6c5a500203e38640a70bf9577", - "symbol": "Yf-DAI", - "name": "YfDAI.finance" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xa1ab427451f19df7445a22dea7073800ea3b687f", - "symbol": "SHINU", - "name": "Shinigami Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x62c8179b08b72acd4e745b33415aa512f2aa4097", - "symbol": "HOUND", - "name": "The Hound" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xac042d9284df95cc6bd35982f6a61e3e7a6f875b", - "symbol": "LBY", - "name": "Lobby" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xb48b3a30b297fd9a765daa2b7520765b640d486b", - "symbol": "AFT", - "name": "Another Fucking Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x194302204f275a532d005c6388bf7559042634bc", - "symbol": "ANIME", - "name": "Anime Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x0ff5a8451a839f5f0bb3562689d9a44089738d11", - "symbol": "rDPX", - "name": "Dopex Rebate Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xaea2350f80420e15e8a1f21124eddaedd482ffa5", - "symbol": "RAICHU", - "name": "RAICHU | t.me/raichutoken" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x63b8b7d4a3efd0735c4bffbd95b332a55e4eb851", - "symbol": "DGCL", - "name": "DigiCol Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x42d6622dece394b54999fbd73d108123806f6a18", - "symbol": "SPANK", - "name": "SPANK" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x39dd84dd2a6b9a731f0de21d5e3f476e5bed3f47", - "symbol": "CHININU", - "name": "CHIN-INU" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x642e2e81a7c5e1f1528657c1dfeddb4e4b5f1810", - "symbol": "MGC", - "name": "Multi Gains Capital" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x903bef1736cddf2a537176cf3c64579c3867a881", - "symbol": "ICHI", - "name": "ichi.farm" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xb3e14c863ab04c84fe6b4bad72c101f0ffe8af10", - "symbol": "ZENDOGE", - "name": "Zen Doge" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x878cf148ccbb50426043a9affe54ba408221c7fa", - "symbol": "KOMBAT", - "name": "Crypto Kombat Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf5b1fd29d23e98db2d9ebb8435e1082e3b38fb65", - "symbol": "KISHIMOTO", - "name": "Kishimoto Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xaa0045b09e96027210e3a0bd3c912171d5e9c7ec", - "symbol": "LEEEEECH", - "name": "Parasite2" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x73037dbf56716e6e7df24f87abb9860ae36552c2", - "symbol": "SAPINU", - "name": "Sapsal INU Coin" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x2956356cd2a2bf3202f771f50d3d14a367b48070", - "symbol": "WETH", - "name": "Ether Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x9203f0f60a2e49f2de2f177bb5f882559de465d5", - "symbol": "SPIDEYINU ", - "name": " Spidey Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x33e07f5055173cf8febede8b21b12d1e2b523205", - "symbol": "ELAND", - "name": "Etherland" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xb05d22ef2d41ba68ab6795c49565ae3faf8fe473", - "symbol": "SCOOP", - "name": "Pooper Scooper" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf3950f3a1f9cfd0b9601275a8f9d87cb0254e70b", - "symbol": "DONI", - "name": "Dogo Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x537edd52ebcb9f48ff2f8a28c51fcdb9d6a6e0d4", - "symbol": "SDOG", - "name": "SDOG" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x80e7fe192709b926b3d6ddef8ff158829f48624e", - "symbol": "ZINU", - "name": " Zombie Inu " - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x1cbf37694de7f75cf10505e727420eeedb7ed6c3", - "symbol": "GIVE", - "name": "Give Global" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x7b8a9965de29f52d30b49d68e3b7194e59aeb957", - "symbol": "SUPER HEAVY ", - "name": "ETH" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x72271efca94e4fd7c49fa66fcab77d418bc3b4a1", - "symbol": "LGBTSHIBA", - "name": "Lgbt Shiba" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x49828c1453d317ca3fffa60c8b5f7f42eeaae56a", - "symbol": "SHIB2", - "name": "SHIBA INU 2.0" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xae3e8fe316b3fefe62f75ce25b355d4b3b985ae6", - "symbol": "CIAO", - "name": "Ciao Bella" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x6a3fe7d0171bc8fc897171dd29d3c422940f12aa", - "symbol": "BARK", - "name": "Bark Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xc8f9882b3cc6d0d920ca7ca376e6f10d4e0c295b", - "symbol": "TOKYOGHOUL", - "name": "Tokyo Ghoul Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf1ca9cb74685755965c7458528a36934df52a3ef", - "symbol": "AVINOC", - "name": "AVINOC Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x9412d561930470e73938c10bfdba5a478f08d6cc", - "symbol": "BIN", - "name": "Bineon Game | t.me/bineon" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x00000000000045166c45af0fc6e4cf31d9e14b9a", - "symbol": "BID", - "name": "TopBidder" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x10086399dd8c1e3de736724af52587a2044c9fa2", - "symbol": "TMTG", - "name": "The Midas Touch Gold" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xe208060ee58ab4ea18aa51f399541683533632b8", - "symbol": "MLINK", - "name": "Memelink" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xb525ecee288b99216cd481c56b6efbdbe9bf90b5", - "symbol": "KUMA", - "name": "Kuma Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x1cbb83ebcd552d5ebf8131ef8c9cd9d9bab342bc", - "symbol": "NFY", - "name": "Non-Fungible Yearn" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xc2eb69ac2828ae4874fca025caf453f758b99acc", - "symbol": "KISHU", - "name": "Kishu Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x39a3c6f5009e3f7db08cf69fdf5183c8e1d11e56", - "symbol": "VAMINU", - "name": "Vampire Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x2a2005849982f0f2b734bcbf206d8a2db067cde0", - "symbol": "GG", - "name": "Gorillion" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x727f064a78dc734d33eec18d5370aef32ffd46e4", - "symbol": "ORION", - "name": "Orion Money Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x896e145568624a498c5a909187363ae947631503", - "symbol": "WASABI", - "name": "Wasabi" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xb7c8ca3d212eaf881ebc3209cc07635bb32e3eee", - "symbol": "GP", - "name": "GP" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x13215d37f94703db513b01c86d4673eca7d4c689", - "symbol": "ROLL?", - "name": "High Roller ?" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x898bad2774eb97cf6b94605677f43b41871410b1", - "symbol": "vETH2", - "name": "validator-Eth2" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x26c8afbbfe1ebaca03c2bb082e69d0476bffe099", - "symbol": "CELL", - "name": "Cellframe Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x7a74a44d9e4e2cc1990fe723fcd6c4258c0b0b38", - "symbol": "TINYSAITAMA", - "name": " Tiny Saitama " - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x2b622288827098ac11cd3dacd238a3478e41b2ad", - "symbol": "1Punch", - "name": "1Punch" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xc22b30e4cce6b78aaaadae91e44e73593929a3e9", - "symbol": "RAC", - "name": "RAC" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x3a5c99d8e43aaa5099e3bf4ea1d2762ff501311c", - "symbol": "MACROHARD", - "name": "MACROHARD ELON" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x84810bcf08744d5862b8181f12d17bfd57d3b078", - "symbol": "SGT", - "name": "SharedStake Governance Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xa49dc04d118bdf4a3471f593c1bba0c99b2187ea", - "symbol": "ODI", - "name": "odius.ai" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x997be9f68df07ee507a772c09cfb86a0d0b16f8c", - "symbol": "SCOOBY", - "name": "SCOOBYDOO" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x4da08a1bff50be96bded5c7019227164b49c2bfc", - "symbol": "Mononoke-Inu", - "name": "Mononoke Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf12815f5ecb6c4f9ec77a4a7e06a3be372017820", - "symbol": "CLAKE", - "name": "CrystalLake" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xc9f1016d336ef77aee75fc11ad64c5ecf9121332", - "symbol": "SAT", - "name": "SoMee Advertising Token V2" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xc3dd23a0a854b4f9ae80670f528094e9eb607ccb", - "symbol": "TRND", - "name": "Trendering" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x1997830b5beb723f5089bb8fc38766d419a0444d", - "symbol": "NEWINU", - "name": "New Guinea Singing Dog Inu" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xbc17729fdf562723f0267f79ff25ade441056d87", - "symbol": "KST", - "name": "KSM Starter Token" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x1b7cb8fc659ec17c4bc1e7cdff13022308885437", - "symbol": "Rainbows", - "name": "NoundlesRainbows" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x178c820f862b14f316509ec36b13123da19a6054", - "symbol": "EWTB", - "name": "Energy Web Token Bridged" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xb17548c7b510427baac4e267bea62e800b247173", - "symbol": "SMT", - "name": "Swarm Markets" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x20a8cec5fffea65be7122bcab2ffe32ed4ebf03a", - "symbol": "DNXC", - "name": "DinoX Coin" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x1796ae0b0fa4862485106a0de9b654efe301d0b2", - "symbol": "PMON", - "name": "Polkamon" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x5debbd8a9920c9ebe5a6a5315e0bbf5d6c81433f", - "symbol": "HMF", - "name": "HelloMyFren" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xdd1ad9a21ce722c151a836373babe42c868ce9a4", - "symbol": "UBI", - "name": "Universal Basic Income" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf1444fa36d477f4d1ec0b30d4eb2d3d9557914a1", - "symbol": "SER", - "name": "SER" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xf3cd95bca5a82d7418bce1f59dcd6aca346b4630", - "symbol": "MASHIMA", - "name": " Mashima Inu " - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xff7e285b87e7f9247f0953cf8cf5cb24eede4b9c", - "symbol": "STIMMY", - "name": "STIMMY" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0xbb978bb4a45878efe0cd5da543511c50be99dae8", - "symbol": "NFTD", - "name": "TATO DOGE NFT" - }, - "count": 4 - }, - { - "baseCurrency": { - "address": "0x5016f22b1166a23a02fc6cadd5e59fce1a995cc3", - "symbol": "GMI", - "name": "GMI" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x6fcb6408499a7c0f242e32d77eb51ffa1dd28a7e", - "symbol": "xHDX", - "name": "xHDX" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xfa14fa6958401314851a17d6c5360ca29f74b57b", - "symbol": "SAITO", - "name": "SAITO" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x0cdf9acd87e940837ff21bb40c9fd55f68bba059", - "symbol": "MINT", - "name": "Public Mint" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xae1eaae3f627aaca434127644371b67b18444051", - "symbol": "YOP", - "name": "YOP" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x6fc13eace26590b80cccab1ba5d51890577d83b2", - "symbol": "UMB", - "name": "Umbrella" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x0417912b3a7af768051765040a55bb0925d4ddcf", - "symbol": "LID", - "name": "Liquidity Dividends Protocol" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x9b02dd390a603add5c07f9fd9175b7dabe8d63b7", - "symbol": "SPI", - "name": "Shopping.io" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xca76543cf381ebbb277be79574059e32108e3e65", - "symbol": "wsOHM", - "name": "Wrapped sOHM" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xc221b7e65ffc80de234bbb6667abdd46593d34f0", - "symbol": "wCFG", - "name": "Wrapped Centrifuge" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xc5019e129b75d380d3d837b8e609dec6c8f5d044", - "symbol": "GN", - "name": "GN" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xc4de189abf94c57f396bd4c52ab13b954febefd8", - "symbol": "B20", - "name": "B.20" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x72b886d09c117654ab7da13a14d603001de0b777", - "symbol": "XDEFI", - "name": "XDEFI" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x1c9491865a1de77c5b6e19d2e6a5f1d7a6f2b25f", - "symbol": "MATTER", - "name": "Antimatter.Finance Governance Token" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xed1480d12be41d92f36f5f7bdd88212e381a3677", - "symbol": "FDT", - "name": "FIAT DAO Token" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xffe510a92434a0df346c5e72a3494b043cf249eb", - "symbol": "LBXC", - "name": "LUXBIO CELL" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xcf78c7dd70d6f30f6e3609e905e78305da98c863", - "symbol": "ONX", - "name": "Ownix" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xcfe99b2507e730da5b244b42ad256467131b4649", - "symbol": "SDL", - "name": "Saddle Governance" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x419d0d8bdd9af5e606ae2232ed285aff190e711b", - "symbol": "FUN", - "name": "FunFair" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x7fbec0bb6a7152e77c30d005b5d49cbc08a602c3", - "symbol": "DDOS", - "name": "Disbalancer" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x17ac188e09a7890a1844e5e65471fe8b0ccfadf3", - "symbol": "CC10", - "name": "Cryptocurrency Top 10 Tokens Index" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x668c50b1c7f46effbe3f242687071d7908aab00a", - "symbol": "CoShi", - "name": "CoShi Inu" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xae697f994fc5ebc000f8e22ebffee04612f98a0d", - "symbol": "LGCY", - "name": "LGCY Network" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xc14830e53aa344e8c14603a91229a0b925b0b262", - "symbol": "PXT", - "name": "Populous XBRL token" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x84ba4aecfde39d69686a841bab434c32d179a169", - "symbol": "MTHD", - "name": "Method" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xe0e4839e0c7b2773c58764f9ec3b9622d01a0428", - "symbol": "ENCORE", - "name": "EnCore" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x9c4a4204b79dd291d6b6571c5be8bbcd0622f050", - "symbol": "TCR", - "name": "Tracer" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xe2f2a5c287993345a840db3b0845fbc70f5935a5", - "symbol": "mUSD", - "name": "mStable USD" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xb9d99c33ea2d86ec5ec6b8a4dd816ebba64404af", - "symbol": "K21", - "name": "k21.kanon.art" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x67b66c99d3eb37fa76aa3ed1ff33e8e39f0b9c7a", - "symbol": "ibETH", - "name": "Interest Bearing ETH" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xfb7b4564402e5500db5bb6d63ae671302777c75a", - "symbol": "DEXT", - "name": "DEXTools" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xef9cd7882c067686691b6ff49e650b43afbbcc6b", - "symbol": "FNX", - "name": "FinNexus" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x557b933a7c2c45672b610f8954a3deb39a51a8ca", - "symbol": "REVV", - "name": "REVV" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xa143ac515dca260a46c742c7251ef3b268639593", - "symbol": "BULK", - "name": "Bulk Token" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x3dd98c8a089dbcff7e8fc8d4f532bd493501ab7f", - "symbol": "TOWN", - "name": "TownCoin" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xb753428af26e81097e7fd17f40c88aaa3e04902c", - "symbol": "SFI", - "name": "Spice" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x0ff6ffcfda92c53f615a4a75d982f399c989366b", - "symbol": "LAYER", - "name": "Unilayer" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x4206931337dc273a630d328da6441786bfad668f", - "symbol": "DOGE", - "name": "Dogecoin" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xc32cc5b70bee4bd54aa62b9aefb91346d18821c4", - "symbol": "ITS", - "name": "IterationSyndicate" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xe0ad1806fd3e7edf6ff52fdb822432e847411033", - "symbol": "ONX", - "name": "OnX.finance" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xd084b83c305dafd76ae3e1b4e1f1fe2ecccb3988", - "symbol": "TVK", - "name": "Terra Virtua Kolect" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x364a7381a5b378ced7ab33d1cdf6ff1bf162bfd6", - "symbol": "TGX", - "name": "DeFi-X Token" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xa017ac5fac5941f95010b12570b812c974469c2c", - "symbol": "XES", - "name": "Proxeus" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0xb5585afebbb9d955da797845b3e0c41b3ff047ce", - "symbol": "Hi", - "name": "Hi" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x0e2298e3b3390e3b945a5456fbf59ecc3f55da16", - "symbol": "YAM", - "name": "YAM" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x2a3bff78b79a009976eea096a51a948a3dc00e34", - "symbol": "WILD", - "name": "Wilder" - }, - "count": 3 - }, - { - "baseCurrency": { - "address": "0x63b4f3e3fa4e438698ce330e365e831f7ccd1ef4", - "symbol": "CFi", - "name": "CyberFi Token" - }, - "count": 3 - } -] \ No newline at end of file diff --git a/openbb_terminal/cryptocurrency/data/finbrain_coins.json b/openbb_terminal/cryptocurrency/data/finbrain_coins.json deleted file mode 100644 index a5f577cb133..00000000000 --- a/openbb_terminal/cryptocurrency/data/finbrain_coins.json +++ /dev/null @@ -1,522 +0,0 @@ -[ - { - "PAIR": "AAVE-USD", - "SYMBOL": "AAVE", - "VS": "USD" - }, - { - "PAIR": "ADA-USD", - "SYMBOL": "ADA", - "VS": "USD" - }, - { - "PAIR": "ADX-USD", - "SYMBOL": "ADX", - "VS": "USD" - }, - { - "PAIR": "AE-USD", - "SYMBOL": "AE", - "VS": "USD" - }, - { - "PAIR": "ANT-USD", - "SYMBOL": "ANT", - "VS": "USD" - }, - { - "PAIR": "ARDR-USD", - "SYMBOL": "ARDR", - "VS": "USD" - }, - { - "PAIR": "ARK-USD", - "SYMBOL": "ARK", - "VS": "USD" - }, - { - "PAIR": "ATOM-USD", - "SYMBOL": "ATOM", - "VS": "USD" - }, - { - "PAIR": "BAT-USD", - "SYMBOL": "BAT", - "VS": "USD" - }, - { - "PAIR": "BCCOIN-USD", - "SYMBOL": "BCCOIN", - "VS": "USD" - }, - { - "PAIR": "BCH-USD", - "SYMBOL": "BCH", - "VS": "USD" - }, - { - "PAIR": "BCN-USD", - "SYMBOL": "BCN", - "VS": "USD" - }, - { - "PAIR": "BLOCK-USD", - "SYMBOL": "BLOCK", - "VS": "USD" - }, - { - "PAIR": "BNB-USD", - "SYMBOL": "BNB", - "VS": "USD" - }, - { - "PAIR": "BNT-USD", - "SYMBOL": "BNT", - "VS": "USD" - }, - { - "PAIR": "BTC-USD", - "SYMBOL": "BTC", - "VS": "USD" - }, - { - "PAIR": "BTCD-USD", - "SYMBOL": "BTCD", - "VS": "USD" - }, - { - "PAIR": "BTG-USD", - "SYMBOL": "BTG", - "VS": "USD" - }, - { - "PAIR": "BTM-USD", - "SYMBOL": "BTM", - "VS": "USD" - }, - { - "PAIR": "BTS-USD", - "SYMBOL": "BTS", - "VS": "USD" - }, - { - "PAIR": "CVC-USD", - "SYMBOL": "CVC", - "VS": "USD" - }, - { - "PAIR": "DASH-USD", - "SYMBOL": "DASH", - "VS": "USD" - }, - { - "PAIR": "DCN-USD", - "SYMBOL": "DCN", - "VS": "USD" - }, - { - "PAIR": "DCR-USD", - "SYMBOL": "DCR", - "VS": "USD" - }, - { - "PAIR": "DGB-USD", - "SYMBOL": "DGB", - "VS": "USD" - }, - { - "PAIR": "DGD-USD", - "SYMBOL": "DGD", - "VS": "USD" - }, - { - "PAIR": "DNT-USD", - "SYMBOL": "DNT", - "VS": "USD" - }, - { - "PAIR": "DOGE-USD", - "SYMBOL": "DOGE", - "VS": "USD" - }, - { - "PAIR": "DOT-USD", - "SYMBOL": "DOT", - "VS": "USD" - }, - { - "PAIR": "EDG-USD", - "SYMBOL": "EDG", - "VS": "USD" - }, - { - "PAIR": "EOS-USD", - "SYMBOL": "EOS", - "VS": "USD" - }, - { - "PAIR": "ETH-USD", - "SYMBOL": "ETH", - "VS": "USD" - }, - { - "PAIR": "ETP-USD", - "SYMBOL": "ETP", - "VS": "USD" - }, - { - "PAIR": "FAIR-USD", - "SYMBOL": "FAIR", - "VS": "USD" - }, - { - "PAIR": "FCT-USD", - "SYMBOL": "FCT", - "VS": "USD" - }, - { - "PAIR": "FUN-USD", - "SYMBOL": "FUN", - "VS": "USD" - }, - { - "PAIR": "GAME-USD", - "SYMBOL": "GAME", - "VS": "USD" - }, - { - "PAIR": "GAS-USD", - "SYMBOL": "GAS", - "VS": "USD" - }, - { - "PAIR": "GBYTE-USD", - "SYMBOL": "GBYTE", - "VS": "USD" - }, - { - "PAIR": "GNO-USD", - "SYMBOL": "GNO", - "VS": "USD" - }, - { - "PAIR": "GNT-USD", - "SYMBOL": "GNT", - "VS": "USD" - }, - { - "PAIR": "HSR-USD", - "SYMBOL": "HSR", - "VS": "USD" - }, - { - "PAIR": "ICX-USD", - "SYMBOL": "ICX", - "VS": "USD" - }, - { - "PAIR": "IOC-USD", - "SYMBOL": "IOC", - "VS": "USD" - }, - { - "PAIR": "KIN-USD", - "SYMBOL": "KIN", - "VS": "USD" - }, - { - "PAIR": "KMD-USD", - "SYMBOL": "KMD", - "VS": "USD" - }, - { - "PAIR": "KNC-USD", - "SYMBOL": "KNC", - "VS": "USD" - }, - { - "PAIR": "LINK-USD", - "SYMBOL": "LINK", - "VS": "USD" - }, - { - "PAIR": "LKK-USD", - "SYMBOL": "LKK", - "VS": "USD" - }, - { - "PAIR": "LRC-USD", - "SYMBOL": "LRC", - "VS": "USD" - }, - { - "PAIR": "LSK-USD", - "SYMBOL": "LSK", - "VS": "USD" - }, - { - "PAIR": "LTC-USD", - "SYMBOL": "LTC", - "VS": "USD" - }, - { - "PAIR": "MAID-USD", - "SYMBOL": "MAID", - "VS": "USD" - }, - { - "PAIR": "MCAP-USD", - "SYMBOL": "MCAP", - "VS": "USD" - }, - { - "PAIR": "MCO-USD", - "SYMBOL": "MCO", - "VS": "USD" - }, - { - "PAIR": "MGO-USD", - "SYMBOL": "MGO", - "VS": "USD" - }, - { - "PAIR": "MKR-USD", - "SYMBOL": "MKR", - "VS": "USD" - }, - { - "PAIR": "MLN-USD", - "SYMBOL": "MLN", - "VS": "USD" - }, - { - "PAIR": "MONA-USD", - "SYMBOL": "MONA", - "VS": "USD" - }, - { - "PAIR": "MTL-USD", - "SYMBOL": "MTL", - "VS": "USD" - }, - { - "PAIR": "NAV-USD", - "SYMBOL": "NAV", - "VS": "USD" - }, - { - "PAIR": "NEBL-USD", - "SYMBOL": "NEBL", - "VS": "USD" - }, - { - "PAIR": "NEO-USD", - "SYMBOL": "NEO", - "VS": "USD" - }, - { - "PAIR": "NLC2-USD", - "SYMBOL": "NLC2", - "VS": "USD" - }, - { - "PAIR": "NXS-USD", - "SYMBOL": "NXS", - "VS": "USD" - }, - { - "PAIR": "NXT-USD", - "SYMBOL": "NXT", - "VS": "USD" - }, - { - "PAIR": "OMG-USD", - "SYMBOL": "OMG", - "VS": "USD" - }, - { - "PAIR": "PAY-USD", - "SYMBOL": "PAY", - "VS": "USD" - }, - { - "PAIR": "PIVX-USD", - "SYMBOL": "PIVX", - "VS": "USD" - }, - { - "PAIR": "PPT-USD", - "SYMBOL": "PPT", - "VS": "USD" - }, - { - "PAIR": "QASH-USD", - "SYMBOL": "QASH", - "VS": "USD" - }, - { - "PAIR": "QRL-USD", - "SYMBOL": "QRL", - "VS": "USD" - }, - { - "PAIR": "QTUM-USD", - "SYMBOL": "QTUM", - "VS": "USD" - }, - { - "PAIR": "REP-USD", - "SYMBOL": "REP", - "VS": "USD" - }, - { - "PAIR": "RLC-USD", - "SYMBOL": "RLC", - "VS": "USD" - }, - { - "PAIR": "SALT-USD", - "SYMBOL": "SALT", - "VS": "USD" - }, - { - "PAIR": "SC-USD", - "SYMBOL": "SC", - "VS": "USD" - }, - { - "PAIR": "SMART-USD", - "SYMBOL": "SMART", - "VS": "USD" - }, - { - "PAIR": "SNGLS-USD", - "SYMBOL": "SNGLS", - "VS": "USD" - }, - { - "PAIR": "STEEM-USD", - "SYMBOL": "STEEM", - "VS": "USD" - }, - { - "PAIR": "STORJ-USD", - "SYMBOL": "STORJ", - "VS": "USD" - }, - { - "PAIR": "SUB-USD", - "SYMBOL": "SUB", - "VS": "USD" - }, - { - "PAIR": "SYS-USD", - "SYMBOL": "SYS", - "VS": "USD" - }, - { - "PAIR": "TAAS-USD", - "SYMBOL": "TAAS", - "VS": "USD" - }, - { - "PAIR": "TRX-USD", - "SYMBOL": "TRX", - "VS": "USD" - }, - { - "PAIR": "UBQ-USD", - "SYMBOL": "UBQ", - "VS": "USD" - }, - { - "PAIR": "UNI-USD", - "SYMBOL": "UNI", - "VS": "USD" - }, - { - "PAIR": "USDT-USD", - "SYMBOL": "USDT", - "VS": "USD" - }, - { - "PAIR": "VEN-USD", - "SYMBOL": "VEN", - "VS": "USD" - }, - { - "PAIR": "VERI-USD", - "SYMBOL": "VERI", - "VS": "USD" - }, - { - "PAIR": "VTC-USD", - "SYMBOL": "VTC", - "VS": "USD" - }, - { - "PAIR": "WAVES-USD", - "SYMBOL": "WAVES", - "VS": "USD" - }, - { - "PAIR": "WINGS-USD", - "SYMBOL": "WINGS", - "VS": "USD" - }, - { - "PAIR": "WTC-USD", - "SYMBOL": "WTC", - "VS": "USD" - }, - { - "PAIR": "XCP-USD", - "SYMBOL": "XCP", - "VS": "USD" - }, - { - "PAIR": "XLM-USD", - "SYMBOL": "XLM", - "VS": "USD" - }, - { - "PAIR": "XMR-USD", - "SYMBOL": "XMR", - "VS": "USD" - }, - { - "PAIR": "XRP-USD", - "SYMBOL": "XRP", - "VS": "USD" - }, - { - "PAIR": "XTZ-USD", - "SYMBOL": "XTZ", - "VS": "USD" - }, - { - "PAIR": "XVG-USD", - "SYMBOL": "XVG", - "VS": "USD" - }, - { - "PAIR": "YFI-USD", - "SYMBOL": "YFI", - "VS": "USD" - }, - { - "PAIR": "ZEC-USD", - "SYMBOL": "ZEC", - "VS": "USD" - }, - { - "PAIR": "ZEN-USD", - "SYMBOL": "ZEN", - "VS": "USD" - }, - { - "PAIR": "ZRX-USD", - "SYMBOL": "ZRX", - "VS": "USD" - } -] diff --git a/openbb_terminal/cryptocurrency/data/santiment_slugs.json b/openbb_terminal/cryptocurrency/data/santiment_slugs.json deleted file mode 100644 index 13c30b24839..00000000000 --- a/openbb_terminal/cryptocurrency/data/santiment_slugs.json +++ /dev/null @@ -1 +0,0 @@ -{"marketSegment":{"0":"Platform","1":"Blockchain Network","2":"Interoperability","3":"Financial","4":"Energy","5":"Lending","6":null,"7":"Lending","8":"Other","9":"Trading","10":"Platform","11":"Cloud Storage","12":"Stablecoin","13":null,"14":"Financial","15":"Stablecoin","16":"DeFi","17":"Financial","18":null,"19":null,"20":"Other","21":"Blockchain Network","22":"Cryptocurrency","23":"Cryptocurrency","24":"AI","25":"Financial","26":"Sports","27":"Sports","28":"Cloud Computing","29":null,"30":"DeFi","31":"Social","32":"Insurance","33":"Prediction Market","34":"Exchange","35":"Stablecoin","36":"Stablecoin","37":"Advertising","38":"Cryptocurrency","39":"Exchange","40":"Cryptocurrency","41":"DeFi","42":"Ethereum","43":"Cryptocurrency","44":"Other","45":"Cryptocurrency","46":"DeFi","47":"DeFi","48":"Social","49":"DeFi","50":"Other","51":"DeFi","52":"DeFi","53":"AI","54":"Lending","55":"DeFi","56":"DeFi","57":"Ethereum","58":"Marketplace","59":"Exchange","60":"Blockchain Network","61":"IoT","62":"Ethereum","63":"DeFi","64":"Bitcoin","65":"DeFi","66":"Exchange","67":"DeFi","68":"Jobs","69":"DeFi","70":"Advertising","71":"Ethereum","72":"DeFi","73":"Cryptocurrency","74":"Privacy","75":"Cryptocurrency","76":"DeFi","77":null,"78":"Blockchain Service","79":"DeFi","80":null,"81":"DeFi","82":"DeFi","83":"Platform","84":"Decentralized Exchange","85":null,"86":"DeFi","87":"Ethereum","88":"Blockchain Service","89":null,"90":null,"91":"Data","92":null,"93":"Binance","94":"Stablecoin","95":"Insurance","96":null,"97":"Stablecoin","98":"Gaming","99":null,"100":"Insurance","101":null,"102":null,"103":"Ethereum","104":null,"105":null,"106":null,"107":null,"108":null,"109":null,"110":null,"111":null,"112":"Binance","113":"Stablecoin","114":null,"115":"Financial","116":null,"117":null,"118":"Gaming","119":"Cryptocurrency","120":null,"121":"Blockchain Service","122":"Tokenization","123":null,"124":null,"125":null,"126":null,"127":null,"128":null,"129":null,"130":"Protocol","131":null,"132":null,"133":"Gaming","134":"Protocol","135":"Cloud Computing","136":"Trading","137":"Investments","138":"Art","139":"Trading","140":"Other","141":"Other","142":"Financial","143":"Other","144":"Trading","145":"Platform","146":null,"147":null,"148":"Cloud Storage","149":"Blockchain Network","150":"Financial","151":"Trading","152":"AI","153":"Financial","154":"Binance","155":"Exchange","156":"Exchange","157":"Cloud Storage","158":"Advertising","159":"DeFi","160":"Media","161":"Advertising","162":"DeFi","163":"Other","164":"Ethereum","165":"Other","166":null,"167":null,"168":null,"169":"Blockchain Service","170":"Platform","171":"DeFi","172":"Advertising","173":"Social","174":"Exchange","175":"AI","176":"Smart Contracts","177":"Protocol","178":"Financial","179":"Entertainment","180":"Other","181":"Exchange","182":"Platform","183":"Cryptocurrency","184":"Financial","185":"Decentralized Exchange","186":null,"187":"Cloud Computing","188":null,"189":"Other","190":"Cloud Storage","191":"Financial","192":"Platform","193":"Supply","194":"Data","195":"Other","196":"AI","197":"Gaming","198":"Insurance","199":"Ethereum","200":"Ethereum","201":"Exchange","202":"Trading","203":"Social","204":"Financial","205":"Financial","206":"Platform","207":"E-Commerce","208":"Communication Network","209":"DeFi","210":"Transportation","211":"Infrastructure","212":"Exchange","213":"Supply","214":"Financial","215":"Platform","216":"Gaming","217":"Other","218":"Healthcare","219":"Platform","220":"Interoperability","221":"Financial","222":"Media","223":"Financial","224":"Data","225":"Financial","226":"Blockchain Network","227":"Other","228":"Exchange","229":"Healthcare","230":"Platform","231":null,"232":"Stablecoin","233":"Other","234":"Blockchain Network","235":"Investments","236":"Blockchain Service","237":"Other","238":"Exchange","239":"Ethereum","240":"Entertainment","241":"Data","242":"Prediction Market","243":"Blockchain Service","244":"Protocol","245":null,"246":null,"247":"Financial","248":"Gaming","249":"Protocol","250":"Marketplace","251":"Entertainment","252":"Ethereum","253":"Travel","254":"Cloud Computing","255":"Fund","256":null,"257":"Binance","258":null,"259":"Cryptocurrency","260":null,"261":null,"262":"Prediction Market","263":"Gaming","264":"DeFi","265":"Cryptocurrency","266":"Ethereum","267":"Financial","268":"Decentralized Exchange","269":"Trading","270":"DeFi","271":"Bitcoin","272":"Bitcoin","273":"Other","274":"DeFi","275":"EOS","276":"Decentralized Exchange","277":"Gaming","278":"Blockchain Network","279":null,"280":"Stablecoin","281":"DeFi","282":"Exchange","283":"DeFi","284":"Gaming","285":null,"286":"Platform","287":"Insurance","288":"Gaming","289":null,"290":null,"291":"Exchange","292":"Virtual Reality","293":"Interoperability","294":null,"295":"Tokenization","296":"DeFi","297":"Blockchain Network","298":"DeFi","299":"Travel","300":"Platform","301":"Smart Contracts","302":"DeFi","303":"DeFi","304":"DeFi","305":"DeFi","306":null,"307":null,"308":null,"309":"Social","310":null,"311":"Other","312":"Other","313":"DeFi","314":"Ethereum","315":"DeFi","316":"DeFi","317":"Financial","318":null,"319":null,"320":null,"321":null,"322":"Other","323":"DeFi","324":"Ethereum","325":"DeFi","326":"DeFi","327":"Financial","328":"Decentralized Exchange","329":null,"330":"DeFi","331":"DeFi","332":"Travel","333":null,"334":"Financial","335":"Cryptocurrency","336":"Data","337":"Privacy","338":"Financial","339":"Financial","340":null,"341":"Data","342":"Tokenization","343":"Cryptocurrency","344":"Ethereum","345":null,"346":"Financial","347":null,"348":null,"349":null,"350":null,"351":null,"352":"Investments","353":"Exchange","354":null,"355":null,"356":null,"357":null,"358":null,"359":"DeFi","360":"DeFi","361":null,"362":null,"363":"Platform","364":"Privacy","365":"Cryptocurrency","366":null,"367":null,"368":"Other","369":"Other","370":"AI","371":null,"372":null,"373":"Financial","374":null,"375":"Stellar","376":"Sports","377":"Cryptocurrency","378":"Cryptocurrency","379":null,"380":"Network","381":"Virtual Reality","382":null,"383":null,"384":"Blockchain Service","385":"Interoperability","386":null,"387":null,"388":"Lending","389":"Exchange","390":null,"391":"Stablecoin","392":null,"393":"Cryptocurrency","394":"Ethereum","395":"Financial","396":"Decentralized Exchange","397":"Prediction Market","398":"Network","399":"Gambling","400":"DeFi","401":"Cryptocurrency","402":"Protocol","403":null,"404":"Blockchain Service","405":"Interoperability","406":"Platform","407":"IoT","408":null,"409":"Insurance","410":"Neo","411":null,"412":null,"413":"Financial","414":"Cryptocurrency","415":"Binance","416":null,"417":"Investments","418":"Financial","419":null,"420":"Binance","421":null,"422":"Exchange","423":"Travel","424":"Healthcare","425":null,"426":"Gambling","427":"Cloud Computing","428":"Binance","429":"Financial","430":null,"431":"Investments","432":"Social","433":null,"434":null,"435":"Ethereum","436":"Financial","437":"Neo","438":"Cryptocurrency","439":"Platform","440":"Platform","441":null,"442":"Cryptocurrency","443":"Ethereum","444":"Protocol","445":"Binance","446":"Exchange","447":"Other","448":"Verification","449":"Smart Contracts","450":"Blockchain Service","451":null,"452":"Fund","453":"Gaming","454":"Fund","455":null,"456":"IoT","457":"Platform","458":"Binance","459":"Jobs","460":"Cryptocurrency","461":"Blockchain Network","462":"Financial","463":"Blockchain Service","464":"Transportation","465":"AI","466":"Interoperability","467":"Sports","468":"Financial","469":null,"470":"Blockchain Network","471":"Blockchain Service","472":"Marketplace","473":"Platform","474":null,"475":"DeFi","476":"Exchange","477":"Gaming","478":"Gambling","479":"E-Commerce","480":"Energy","481":"Platform","482":"Financial","483":"Smart Contracts","484":"Media","485":"Marketing","486":"DeFi","487":"Marketplace","488":"Cryptocurrency","489":"Financial","490":"Financial","491":"Platform","492":"Cryptocurrency","493":"Masternodes","494":"Entertainment","495":"Smart Contracts","496":"Cryptocurrency","497":"Cryptocurrency","498":"Cryptocurrency","499":"Cryptocurrency","500":"Cryptocurrency","501":"Blockchain Network","502":"Ethereum","503":"Financial","504":"Gambling","505":"Ethereum","506":"Decentralized Exchange","507":"Stablecoin","508":"DeFi","509":"Blockchain Service","510":"Jobs","511":"Ethereum","512":"Exchange","513":"Cryptocurrency","514":"Cryptocurrency","515":null,"516":"Social","517":"Verification","518":"Binance","519":"Ethereum","520":"Cryptocurrency","521":"Financial","522":"Verification","523":"Platform","524":"Binance","525":"Cryptocurrency","526":"Cryptocurrency","527":"Mining","528":"Platform","529":"Blockchain Service","530":"Blockchain Network","531":"Ethereum","532":"Interoperability","533":"DeFi","534":"Blockchain Service","535":"Infrastructure","536":"Cryptocurrency","537":"Data","538":"Privacy","539":"Blockchain Service","540":"Blockchain Service","541":"Social","542":"Social","543":"Decentralized Exchange","544":"Financial","545":"Gambling","546":"Lending","547":"Gambling","548":"Blockchain Service","549":"Binance","550":"Decentralized Exchange","551":"Other","552":"Data","553":"Lending","554":"Mining","555":"Platform","556":"Platform","557":"Financial","558":"Decentralized Exchange","559":"Platform","560":"Exchange","561":"Supply","562":"DeFi","563":"Blockchain Service","564":"Blockchain Service","565":"Decentralized Exchange","566":"Gambling","567":"Platform","568":"Other","569":"Ethereum","570":"DeFi","571":"Real Estate","572":null,"573":null,"574":"DeFi","575":"Gaming","576":"Blockchain Network","577":"Cryptocurrency","578":null,"579":null,"580":"Ethereum","581":"Financial","582":"Social","583":"Mining","584":"Decentralized Exchange","585":null,"586":null,"587":"Financial","588":"Healthcare","589":"Platform","590":"Financial","591":null,"592":"Financial","593":"Trading","594":"Insurance","595":"Gaming","596":"Marketplace","597":null,"598":null,"599":"Financial","600":"Verification","601":"Other","602":"Cryptocurrency","603":null,"604":"Ethereum","605":"Cryptocurrency","606":"Cryptocurrency","607":"Entertainment","608":"Cryptocurrency","609":"Binance","610":null,"611":null,"612":"Trading","613":"Marketing","614":"Blockchain Service","615":"Smart Contracts","616":"Data","617":"Gaming","618":null,"619":"Platform","620":"Gaming","621":"Data","622":"Privacy","623":"Financial","624":"Neo","625":"Other","626":"Ethereum","627":"Cloud Storage","628":"Interoperability","629":"Cryptocurrency","630":"Virtual Reality","631":"Smart Contracts","632":null,"633":"Decentralized Exchange","634":"Marketplace","635":"Travel","636":"Marketplace","637":"Financial","638":"Cryptocurrency","639":"Data","640":"Financial","641":"Tokenization","642":"Financial","643":"Energy","644":"Interoperability","645":"Decentralized Exchange","646":"Ethereum","647":"Gaming","648":"Cryptocurrency","649":"Financial","650":"Trading","651":null,"652":"Tokenization","653":"Virtual Reality","654":"AI","655":"Blockchain Service","656":"Financial","657":"Financial","658":"Infrastructure","659":"Financial","660":"Cryptocurrency","661":"Blockchain Network","662":"Exchange","663":"Sports","664":"Tokenization","665":"E-Commerce","666":"Education","667":"Cryptocurrency","668":"Social","669":"Blockchain Service","670":"Ardor","671":"Data","672":"Gaming","673":"Gambling","674":"Cryptocurrency","675":"Decentralized Exchange","676":"Data","677":"Cloud Computing","678":"IoT","679":"Decentralized Exchange","680":"Blockchain Network","681":"Lending","682":null,"683":null,"684":"Trading","685":null,"686":"Other","687":"Cryptocurrency","688":"Stablecoin","689":"Cryptocurrency","690":"Exchange","691":"Blockchain Network","692":"Platform","693":"DeFi","694":"Platform","695":"Interoperability","696":"Data","697":null,"698":"Gaming","699":"Cryptocurrency","700":"Marketplace","701":"Cryptocurrency","702":null,"703":"Interoperability","704":"Gambling","705":"Exchange","706":"Advertising","707":"Communication Network","708":"Advertising","709":"Stablecoin","710":"Prediction Market","711":"Media","712":"Decentralized Exchange","713":"Cryptocurrency","714":"Decentralized Exchange","715":"Sports","716":"Platform","717":"AI","718":"Ethereum","719":"Marketplace","720":null,"721":"Cryptocurrency","722":"Financial","723":"Lending","724":"Cryptocurrency","725":"Transportation","726":"Prediction Market","727":"Social","728":"Fund","729":"Blockchain Service","730":null,"731":"Financial","732":"Ethereum","733":"Blockchain Network","734":"Cryptocurrency","735":"Social","736":"Healthcare","737":null,"738":null,"739":null,"740":"Financial","741":null,"742":"Ethereum","743":"Transportation","744":"Gambling","745":"Blockchain Service","746":"Financial","747":null,"748":"Cryptocurrency","749":"Verification","750":"Protocol","751":"E-Commerce","752":"Social","753":"Financial","754":"Transportation","755":"Investments","756":"Blockchain Service","757":"Real Estate","758":"Entertainment","759":"Stablecoin","760":"Blockchain Service","761":null,"762":"Smart Contracts","763":"Gaming","764":"Platform","765":"DeFi","766":"Gaming","767":"Social","768":null,"769":"DeFi","770":"Media","771":"Entertainment","772":"Marketing","773":"Protocol","774":"Financial","775":"Privacy","776":"Cryptocurrency","777":"AI","778":"Ethereum","779":null,"780":"Financial","781":null,"782":"Cryptocurrency","783":"Exchange","784":"Cryptocurrency","785":"AI","786":"DeFi","787":"Cryptocurrency","788":null,"789":"Binance","790":"Decentralized Exchange","791":"Platform","792":"Insurance","793":"Cryptocurrency","794":"Financial","795":"Data","796":"Trading","797":"Financial","798":"Trading","799":"Cryptocurrency","800":"Cryptocurrency","801":"Cryptocurrency","802":"Social","803":"Exchange","804":"Exchange","805":"DeFi","806":"Financial","807":"Marketing","808":"Data","809":"Smart Contracts","810":"Blockchain Service","811":null,"812":"Financial","813":"Gaming","814":"Other","815":"Other","816":"Cryptocurrency","817":"Supply","818":"Protocol","819":"Data","820":null,"821":null,"822":"Blockchain Service","823":"Cryptocurrency","824":"Social","825":"Financial","826":"Protocol","827":"Exchange","828":"Cryptocurrency","829":"Exchange","830":"Cryptocurrency","831":"Healthcare","832":"Trading","833":"Platform","834":"Social","835":"Marketing","836":"Cryptocurrency","837":"Gas Token","838":"Cloud Computing","839":"Data","840":"Data","841":"Real Estate","842":"Advertising","843":"Transportation","844":"Supply","845":"Ethereum","846":"Cloud Storage","847":"Ethereum","848":"Other","849":"Exchange","850":"Interoperability","851":"Social","852":"Prediction Market","853":"Decentralized Exchange","854":"Social","855":"DeFi","856":"Exchange","857":"Supply","858":null,"859":null,"860":"Energy","861":"IoT","862":"Other","863":"Healthcare","864":"Cryptocurrency","865":"Financial","866":"Blockchain Service","867":"Supply","868":"Blockchain Service","869":"Other","870":"Gambling","871":null,"872":null,"873":"Lending","874":"Energy","875":"Interoperability","876":"Social","877":"DeFi","878":null,"879":null,"880":"Other","881":"Marketplace","882":"Blockchain Service","883":"Blockchain Service","884":"Smart Contracts","885":"Exchange","886":"Social","887":"Blockchain Service","888":"Financial","889":"Exchange","890":"Entertainment","891":"Financial","892":"Other","893":"Gaming","894":null,"895":"Insurance","896":"Financial","897":"IoT","898":"Mining","899":"Cryptocurrency","900":"Marketing","901":"Real Estate","902":"Exchange","903":"Verification","904":null,"905":"DeFi","906":"Marketplace","907":"Platform","908":null,"909":"Media","910":"Financial","911":"Other","912":"Financial","913":"Platform","914":null,"915":null,"916":"Gaming","917":"Investments","918":"Platform","919":"Other","920":"Financial","921":"Ethereum","922":"Gambling","923":null,"924":"Social","925":"Trading","926":"Financial","927":"Ethereum","928":"Decentralized Exchange","929":"Healthcare","930":"Smart Contracts","931":"Decentralized Exchange","932":"Platform","933":"Financial","934":"DeFi","935":"Other","936":"Entertainment","937":"Cryptocurrency","938":"Exchange","939":"Blockchain Network","940":"Ethereum","941":"Cryptocurrency","942":"Ethereum","943":"Cryptocurrency","944":"Marketplace","945":"Advertising","946":"Prediction Market","947":"Financial","948":"Entertainment","949":"Privacy","950":"Blockchain Network","951":"Gambling","952":"Mining","953":"Protocol","954":"Financial","955":"Financial","956":"Energy","957":null,"958":null,"959":"Privacy","960":"Cryptocurrency","961":"Healthcare","962":"Verification","963":"Protocol","964":"Entertainment","965":"DeFi","966":"Interoperability","967":"Verification","968":"Protocol","969":"Data","970":"Media","971":"Tokenization","972":"Gaming","973":"Financial","974":"Data","975":"Advertising","976":null,"977":"Privacy","978":"Trading","979":"IoT","980":"Cryptocurrency","981":"Verification","982":"Blockchain Service","983":"AI","984":"Gaming","985":null,"986":null,"987":null,"988":"Blockchain Service","989":"Financial","990":"Cryptocurrency","991":"Mining","992":"Platform","993":"Blockchain Network","994":"Financial","995":"Financial","996":null,"997":null,"998":null,"999":"Marketing","1000":"DeFi","1001":"Supply","1002":"Ethereum","1003":"Blockchain Service","1004":"DeFi","1005":null,"1006":null,"1007":null,"1008":"Cryptocurrency","1009":"Protocol","1010":"Protocol","1011":"Education","1012":"Financial","1013":"Platform","1014":"Cryptocurrency","1015":"DeFi","1016":"Cryptocurrency","1017":"Real Estate","1018":"Financial","1019":"Blockchain Network","1020":"Platform","1021":"Network","1022":null,"1023":"Platform","1024":"Financial","1025":"Infrastructure","1026":"Blockchain Network","1027":"Binance","1028":"Gaming","1029":"DeFi","1030":"Verification","1031":"Blockchain Service","1032":"Data","1033":"Protocol","1034":"Blockchain Service","1035":"Verification","1036":"Gaming","1037":"Jobs","1038":"Real Estate","1039":"Blockchain Network","1040":"Virtual Reality","1041":"Cryptocurrency","1042":null,"1043":"Supply","1044":"Decentralized Exchange","1045":"Interoperability","1046":"Blockchain Service","1047":"DeFi","1048":"Ethereum","1049":"Platform","1050":"Cloud Storage","1051":"Real Estate","1052":"Cryptocurrency","1053":"Gaming","1054":"Cryptocurrency","1055":"Platform","1056":"Virtual Reality","1057":"Ethereum","1058":"Ethereum","1059":"Cryptocurrency","1060":"Blockchain Network","1061":"Entertainment","1062":"DeFi","1063":"Stablecoin","1064":"Platform","1065":"Financial","1066":"Gaming","1067":"Financial","1068":"Financial","1069":"AI","1070":"Blockchain Service","1071":"Decentralized Exchange","1072":"Cloud Storage","1073":"Marketing","1074":"Gaming","1075":"Platform","1076":"Ethereum","1077":"Blockchain Service","1078":"Platform","1079":null,"1080":"Art","1081":"Transportation","1082":"Gambling","1083":"Protocol","1084":"Exchange","1085":"Stablecoin","1086":null,"1087":"Gaming","1088":null,"1089":null,"1090":null,"1091":"Data","1092":"Smart Contracts","1093":"Platform","1094":"Financial","1095":"Ethereum","1096":"Entertainment","1097":"Financial","1098":"Smart Contracts","1099":"Financial","1100":"Blockchain Network","1101":"Scaling","1102":"Data","1103":"Other","1104":"Blockchain Service","1105":"Platform","1106":"Infrastructure","1107":"Blockchain Service","1108":"Blockchain Network","1109":"Platform","1110":"Financial","1111":"Cryptocurrency","1112":"Interoperability","1113":"Healthcare","1114":null,"1115":null,"1116":null,"1117":"Platform","1118":"Exchange","1119":"Trading","1120":"Other","1121":"Platform","1122":"Blockchain Service","1123":"Cryptocurrency","1124":"Smart Contracts","1125":null,"1126":null,"1127":"IoT","1128":"Gaming","1129":"Platform","1130":"Real Estate","1131":"Platform","1132":"Blockchain Network","1133":"Entertainment","1134":"Other","1135":"Ethereum","1136":"Mining","1137":"Stablecoin","1138":"Financial","1139":"Blockchain Service","1140":"Sports","1141":"Financial","1142":"Healthcare","1143":"Platform","1144":"Cryptocurrency","1145":"DeFi","1146":"Cryptocurrency","1147":null,"1148":null,"1149":"Other","1150":"Platform","1151":"Financial","1152":"Blockchain Network","1153":"DeFi","1154":"Energy","1155":"DeFi","1156":null,"1157":null,"1158":"Blockchain Network","1159":"Cloud Computing","1160":"Ethereum","1161":"Blockchain Service","1162":"DeFi","1163":"Insurance","1164":"Energy","1165":"Smart Contracts","1166":"Financial","1167":"Metaverse","1168":"Blockchain Service","1169":"Data","1170":"Gaming","1171":"Data","1172":"Insurance","1173":"Exchange","1174":"Supply","1175":"Masternodes","1176":"Gaming","1177":null,"1178":null,"1179":null,"1180":"Marketing","1181":"Other","1182":"Cryptocurrency","1183":"Cloud Storage","1184":"Blockchain Network","1185":"Infrastructure","1186":"Gaming","1187":"Financial","1188":"Exchange","1189":"Data","1190":"Cryptocurrency","1191":null,"1192":null,"1193":"Protocol","1194":"Smart Contracts","1195":"Decentralized Exchange","1196":"Gaming","1197":"Supply","1198":"Tokenization","1199":"Lending","1200":"DeFi","1201":"Cryptocurrency","1202":"DeFi","1203":null,"1204":null,"1205":null,"1206":"Ethereum","1207":"Decentralized Exchange","1208":"Cloud Computing","1209":"Cryptocurrency","1210":"Cloud Storage","1211":"Marketing","1212":"DeFi","1213":"Smart Contracts","1214":"Smart Contracts","1215":"Smart Contracts","1216":null,"1217":null,"1218":null,"1219":"Platform","1220":"Privacy","1221":"Interoperability","1222":"Infrastructure","1223":"Verification","1224":"Financial","1225":"Education","1226":"Blockchain Service","1227":"Cloud Computing","1228":"Smart Contracts","1229":"Financial","1230":"Privacy","1231":"Blockchain Network","1232":"Gambling","1233":"Financial","1234":"Entertainment","1235":"Financial","1236":"Financial","1237":null,"1238":"Decentralized Exchange","1239":null,"1240":"Verification","1241":"Exchange","1242":"Exchange","1243":"Advertising","1244":"Financial","1245":"Prediction Market","1246":"Supply","1247":null,"1248":"Transportation","1249":"Financial","1250":null,"1251":"DeFi","1252":"Blockchain Service","1253":"Marketing","1254":"Infrastructure","1255":"Cloud Computing","1256":"Financial","1257":"Data","1258":"Decentralized Exchange","1259":"Lending","1260":"Financial","1261":"NFT","1262":"Exchange","1263":"Platform","1264":"Financial","1265":"Blockchain Network","1266":"Entertainment","1267":"Ethereum","1268":"Decentralized Exchange","1269":"Protocol","1270":"Blockchain Network","1271":"DeFi","1272":"Data","1273":"Ethereum","1274":"Cryptocurrency","1275":"Platform","1276":"Blockchain Network","1277":"Platform","1278":"Ethereum","1279":"AI","1280":"Cryptocurrency","1281":"Cryptocurrency","1282":null,"1283":"Financial","1284":"Blockchain Network","1285":null,"1286":"Trading","1287":"Sports","1288":"Ethereum","1289":"Marketplace","1290":"Cryptocurrency","1291":"Ethereum","1292":"Education","1293":"Cryptocurrency","1294":"Blockchain Service","1295":"Entertainment","1296":"Ethereum","1297":"Decentralized Exchange","1298":"Protocol","1299":"DeFi","1300":"Smart Contracts","1301":"Social","1302":null,"1303":"Financial","1304":null,"1305":"Advertising","1306":"Blockchain Network","1307":"Ethereum","1308":"Protocol","1309":"Media","1310":"Trading","1311":"Exchange","1312":"Other","1313":"Blockchain Network","1314":"Blockchain Service","1315":"Ethereum","1316":"Gambling","1317":"Media","1318":"Data","1319":"Binance","1320":"Exchange","1321":"Financial","1322":"Blockchain Network","1323":"DeFi","1324":"Financial","1325":"E-Commerce","1326":"Financial","1327":"Financial","1328":"Data","1329":"Decentralized Exchange","1330":null,"1331":null,"1332":"Cryptocurrency","1333":"Energy","1334":"Ethereum","1335":"Blockchain Network","1336":"Other","1337":"Financial","1338":"DeFi","1339":"Gaming","1340":null,"1341":"Platform","1342":"Media","1343":"Other","1344":"Other","1345":null,"1346":"Media","1347":"Blockchain Service","1348":null,"1349":null,"1350":"Platform","1351":"Marketplace","1352":"Media","1353":"Trading","1354":"Blockchain Service","1355":"AI","1356":"Gambling","1357":"Blockchain Network","1358":"Cloud Storage","1359":"Prediction Market","1360":"Communication Network","1361":"Cryptocurrency","1362":"Financial","1363":"Energy","1364":"Other","1365":"Infrastructure","1366":"Financial","1367":"Tokenization","1368":null,"1369":null,"1370":"Blockchain Service","1371":null,"1372":"Financial","1373":"Data","1374":"Financial","1375":"Trading","1376":"Protocol","1377":"Stablecoin","1378":"Cloud Computing","1379":"Virtual Reality","1380":"Other","1381":"Investments","1382":null,"1383":"Cryptocurrency","1384":null,"1385":null,"1386":null,"1387":"DeFi","1388":null,"1389":"DeFi","1390":"Healthcare","1391":"Platform","1392":"Infrastructure","1393":"IoT","1394":"Trading","1395":"Exchange","1396":null,"1397":"DeFi","1398":"Stablecoin","1399":null,"1400":"Transportation","1401":"Financial","1402":"Stablecoin","1403":"Platform","1404":"Platform","1405":"Blockchain Service","1406":"Marketing","1407":"DeFi","1408":"Decentralized Exchange","1409":"Blockchain Network","1410":"Exchange","1411":"Entertainment","1412":"Social","1413":"Advertising","1414":"Virtual Reality","1415":"Platform","1416":"Cryptocurrency","1417":"Privacy","1418":"Exchange","1419":"IoT","1420":"Other","1421":"Energy","1422":"Financial","1423":"Interoperability","1424":"Cryptocurrency","1425":"Ethereum","1426":"Cryptocurrency","1427":"DeFi","1428":"Exchange","1429":"Protocol","1430":"Healthcare","1431":"Cloud Computing","1432":"IoT","1433":"Entertainment","1434":"DeFi","1435":"Financial","1436":"Entertainment","1437":"Financial","1438":"Platform","1439":"Other","1440":null,"1441":null,"1442":null,"1443":null,"1444":"Other","1445":"Cryptocurrency","1446":"Marketplace","1447":"Entertainment","1448":"Decentralized Exchange","1449":"Exchange","1450":"Marketing","1451":"Blockchain Network","1452":"Other","1453":"Cryptocurrency","1454":null,"1455":"Data","1456":"Insurance","1457":"Financial","1458":null,"1459":"Other","1460":"Other","1461":"Lending","1462":"E-Commerce","1463":"Other","1464":null,"1465":"DeFi","1466":"Ethereum","1467":null,"1468":"Cryptocurrency","1469":"Platform","1470":"Social","1471":"Gambling","1472":"Privacy","1473":"Gaming","1474":"Binance","1475":"Cryptocurrency","1476":"Financial","1477":"Financial","1478":"Blockchain Network","1479":null,"1480":"Platform","1481":"Financial","1482":null,"1483":null,"1484":"Platform","1485":"Blockchain Network","1486":"IoT","1487":"Entertainment","1488":"Platform","1489":"Advertising","1490":"Cryptocurrency","1491":"Media","1492":"Tokenization","1493":"Investments","1494":"IoT","1495":null,"1496":null,"1497":"Stablecoin","1498":"Stablecoin","1499":"Trading","1500":"Financial","1501":"Network","1502":"Cryptocurrency","1503":"Protocol","1504":"Real Estate","1505":"Decentralized Exchange","1506":"Other","1507":"Financial","1508":"DeFi","1509":null,"1510":null,"1511":"Blockchain Network","1512":"Prediction Market","1513":"Cloud Computing","1514":"Advertising","1515":"Platform","1516":"DeFi","1517":"Other","1518":"Decentralized Exchange","1519":"Gambling","1520":"Other","1521":"Tokenization","1522":"Trading","1523":"Privacy","1524":"Financial","1525":"DeFi","1526":"Protocol","1527":"Lending","1528":"Stablecoin","1529":"Decentralized Exchange","1530":"Stablecoin","1531":"Marketplace","1532":null,"1533":"Marketplace","1534":"Trading","1535":"DeFi","1536":"Ethereum","1537":"Ethereum","1538":"Real Estate","1539":"Exchange","1540":null,"1541":"Cryptocurrency","1542":"Platform","1543":"Decentralized Exchange","1544":"Bitcoin","1545":null,"1546":"Exchange","1547":"Interoperability","1548":"Data","1549":"Blockchain Service","1550":"Cloud Storage","1551":"Solana","1552":"Platform","1553":"DeFi","1554":"DeFi","1555":"DeFi","1556":"Privacy","1557":"Social","1558":null,"1559":null,"1560":"Investments","1561":"Solana","1562":"Other","1563":"DeFi","1564":"DeFi","1565":"DeFi","1566":"Cloud Computing","1567":"Fund","1568":null,"1569":"Ethereum","1570":"Cryptocurrency","1571":"Gambling","1572":"Network","1573":"Financial","1574":"Decentralized Exchange","1575":"Smart Contracts","1576":"Financial","1577":"DeFi","1578":null,"1579":null,"1580":"Other","1581":null,"1582":"Cryptocurrency","1583":null,"1584":"Exchange","1585":"Cryptocurrency","1586":"Exchange","1587":"Stablecoin","1588":null,"1589":"Blockchain Network","1590":"IoT","1591":"IoT","1592":"Lending","1593":"Decentralized Exchange","1594":null,"1595":"Ethereum","1596":"Travel","1597":"DeFi","1598":"Media","1599":"DeFi","1600":"Blockchain Network","1601":"Financial","1602":null,"1603":"Data","1604":"Exchange","1605":null,"1606":"Other","1607":null,"1608":"DeFi","1609":"Financial","1610":"Blockchain Network","1611":"Protocol","1612":"DeFi","1613":"Financial","1614":"Stablecoin","1615":null,"1616":null,"1617":"Insurance","1618":"Trading","1619":null,"1620":"Marketing","1621":"Stablecoin","1622":"Platform","1623":"Decentralized Exchange","1624":"Financial","1625":"Media","1626":"Exchange","1627":"Other","1628":"Data","1629":"DeFi","1630":"DeFi","1631":null,"1632":"DeFi","1633":"DeFi","1634":null,"1635":null,"1636":"Stablecoin","1637":"Ethereum","1638":null,"1639":"Blockchain Network","1640":"DeFi","1641":"Ethereum","1642":null,"1643":"DeFi","1644":"Other","1645":null,"1646":"Platform","1647":null,"1648":null,"1649":"DeFi","1650":"Blockchain Service","1651":null,"1652":"Tokenization","1653":null,"1654":"Platform","1655":"Infrastructure","1656":"Gaming","1657":null,"1658":"Protocol","1659":null,"1660":null,"1661":"Protocol","1662":null,"1663":null,"1664":"Tokenization","1665":"Other","1666":null,"1667":"Lending","1668":"Blockchain Network","1669":"Exchange","1670":null,"1671":null,"1672":"Binance","1673":"Interoperability","1674":"Lending","1675":"Trading","1676":"Gaming","1677":null,"1678":null,"1679":"DeFi","1680":"Ethereum","1681":"Cryptocurrency","1682":"Protocol","1683":"Trading","1684":"Platform","1685":"Ethereum","1686":null,"1687":"Blockchain Network","1688":"DeFi","1689":"Blockchain Service","1690":"Financial","1691":"Tokenization","1692":"Infrastructure","1693":"Binance","1694":"Decentralized Exchange","1695":"Decentralized Exchange","1696":null,"1697":null,"1698":null,"1699":null,"1700":null,"1701":null,"1702":"Interoperability","1703":"Data","1704":"Decentralized Exchange","1705":"Gaming","1706":"Platform","1707":"Financial","1708":"Ethereum","1709":"Financial","1710":null,"1711":null,"1712":null,"1713":null,"1714":null,"1715":null,"1716":null,"1717":"Financial","1718":"Other","1719":"DeFi","1720":"Other","1721":"Stablecoin","1722":null,"1723":"Exchange","1724":"Blockchain Network","1725":"Lending","1726":null,"1727":"Ethereum","1728":"Stablecoin","1729":"Lending","1730":"Infrastructure","1731":null,"1732":"Interoperability","1733":null,"1734":"Blockchain Network","1735":"Platform","1736":"Financial","1737":"Ethereum","1738":"DeFi","1739":"Sports","1740":"Cryptocurrency","1741":"Ethereum","1742":"Sports","1743":"Decentralized Exchange","1744":"Financial","1745":null,"1746":null,"1747":null,"1748":"Protocol","1749":"DeFi","1750":"Cryptocurrency","1751":"Lending","1752":"Blockchain Service","1753":"DeFi","1754":"Gaming","1755":"Marketplace","1756":"Blockchain Service","1757":"Smart Contracts","1758":"Financial","1759":"Cloud Computing","1760":null,"1761":"Gaming","1762":"Decentralized Exchange","1763":null,"1764":"DeFi","1765":"Protocol","1766":"Marketplace","1767":"Data","1768":"Protocol","1769":"Marketplace","1770":"Trading","1771":"DeFi","1772":"DeFi","1773":"Sports","1774":"Decentralized Exchange","1775":"Financial","1776":null,"1777":"Exchange","1778":"Marketplace","1779":"Marketplace","1780":"Infrastructure","1781":"Financial","1782":"Gaming","1783":"Infrastructure","1784":"Decentralized Exchange","1785":"Lending","1786":"Interoperability","1787":null,"1788":"Decentralized Exchange","1789":null,"1790":"Platform","1791":"Exchange","1792":"DeFi","1793":"Ethereum","1794":"Trading","1795":null,"1796":"Cryptocurrency","1797":null,"1798":"Data","1799":"Decentralized Exchange","1800":"DeFi","1801":null,"1802":"Lending","1803":"Lending","1804":"Marketing","1805":"Insurance","1806":"Protocol","1807":"Social","1808":null,"1809":"Investments","1810":"DeFi","1811":"Exchange","1812":"Marketing","1813":"DeFi","1814":"DeFi","1815":null,"1816":"Gaming","1817":null,"1818":"Sports","1819":"Social","1820":"Supply","1821":"Exchange","1822":"Cryptocurrency","1823":"Financial","1824":"Prediction Market","1825":null,"1826":"Tron","1827":"Protocol","1828":"Data","1829":"Lending","1830":"Prediction Market","1831":"Stablecoin","1832":"Ethereum","1833":"Blockchain Service","1834":"DeFi","1835":"Entertainment","1836":"Tokenization","1837":"DeFi","1838":"Gaming","1839":"DeFi","1840":"Gaming","1841":"DeFi","1842":"Platform","1843":"DeFi","1844":null,"1845":"Social","1846":"Interoperability","1847":"Ethereum","1848":"DeFi","1849":"DeFi","1850":"Gaming","1851":"Platform","1852":"Verification","1853":"DeFi","1854":"Social","1855":"Social","1856":"Cryptocurrency","1857":null,"1858":"Protocol","1859":"Blockchain Network","1860":"Blockchain Network","1861":"Trading","1862":"Blockchain Network","1863":"DeFi","1864":"DeFi","1865":"Platform","1866":"Interoperability","1867":"Ethereum","1868":null,"1869":null,"1870":"Gambling","1871":null,"1872":"Social","1873":"Marketplace","1874":"DeFi","1875":"IoT","1876":"Exchange","1877":null,"1878":"Tron","1879":null,"1880":"Interoperability","1881":"Blockchain Service","1882":"Lending","1883":"Financial","1884":"Trading","1885":"DeFi","1886":"Stablecoin","1887":"Trading","1888":"Infrastructure","1889":"Interoperability","1890":"Platform","1891":"Infrastructure","1892":"Lending","1893":null,"1894":"Platform","1895":"Platform","1896":"DeFi","1897":"Platform","1898":"Financial","1899":"DeFi","1900":"Platform","1901":"DeFi","1902":"Interoperability","1903":"Ethereum","1904":"Polkadot","1905":"Marketplace","1906":"Platform","1907":"Financial","1908":"DeFi","1909":"Privacy","1910":"DeFi","1911":"Fund","1912":"Lending","1913":"Decentralized Exchange","1914":"Interoperability","1915":"Platform","1916":"Infrastructure","1917":"Other","1918":"Marketing","1919":"Protocol","1920":"AI","1921":null,"1922":"Lending","1923":"DeFi","1924":"Decentralized Exchange","1925":"Ethereum","1926":"Blockchain Network","1927":"Social","1928":"Interoperability","1929":"Platform","1930":null,"1931":null,"1932":null,"1933":null,"1934":"DeFi","1935":"Marketing","1936":"Lending","1937":"Media","1938":"DeFi","1939":"DeFi","1940":"Stablecoin","1941":null,"1942":"Blockchain Network","1943":null,"1944":"DeFi","1945":"Financial","1946":"DeFi","1947":"Gaming","1948":"DeFi","1949":"DeFi","1950":"DeFi","1951":"Ethereum","1952":"Exchange","1953":"DeFi","1954":"DeFi","1955":"DeFi","1956":"DeFi","1957":"Cloud Storage","1958":"DeFi","1959":null,"1960":null,"1961":"Platform","1962":"Financial","1963":"Infrastructure","1964":"Gaming","1965":"Interoperability","1966":"Interoperability","1967":"Interoperability","1968":"Marketing","1969":"DeFi","1970":null,"1971":"Decentralized Exchange","1972":"Ethereum","1973":"Smart Contracts","1974":null,"1975":"Prediction Market","1976":"DeFi","1977":"DeFi","1978":null,"1979":null,"1980":"IoT","1981":"Cryptocurrency","1982":"Masternodes","1983":"Social","1984":"Lending","1985":"Trading","1986":"Decentralized Exchange","1987":"Interoperability","1988":"Trading","1989":"Social","1990":"Smart Contracts","1991":"Cryptocurrency","1992":"Decentralized Exchange","1993":"Interoperability","1994":"DeFi","1995":null,"1996":"DeFi","1997":"Cloud Computing","1998":"Platform","1999":"Decentralized Exchange","2000":"DeFi","2001":"Decentralized Exchange","2002":"DeFi","2003":"Interoperability","2004":"Ethereum","2005":"Financial","2006":"Gaming","2007":"Decentralized Exchange","2008":"Protocol","2009":"Ethereum","2010":"Trading","2011":"Cloud Storage","2012":"DeFi","2013":"DeFi","2014":"DeFi","2015":"Lending","2016":"Gaming","2017":"Mining","2018":"Energy","2019":"Stablecoin","2020":"Decentralized Exchange","2021":"DeFi","2022":null,"2023":"Blockchain Network","2024":null,"2025":"DeFi","2026":"Trading","2027":null,"2028":null,"2029":"Trading","2030":"Ethereum","2031":"Decentralized Exchange","2032":"Exchange","2033":"Jobs","2034":null,"2035":"Decentralized Exchange","2036":"Gaming","2037":"Gaming","2038":"Ethereum","2039":"Smart Contracts","2040":"Investments","2041":"Financial","2042":"Blockchain Network","2043":"Gaming","2044":"DeFi","2045":"DeFi","2046":"DeFi","2047":"Gaming","2048":"DeFi","2049":"Media","2050":"Ethereum","2051":"Interoperability","2052":"DeFi","2053":"Interoperability","2054":"Gaming","2055":"DeFi","2056":null,"2057":null,"2058":"Decentralized Exchange","2059":"DeFi","2060":"Protocol","2061":"DeFi","2062":null,"2063":"Cloud Computing","2064":"Financial","2065":"Platform","2066":"Cryptocurrency","2067":"Gaming","2068":"DeFi","2069":"Blockchain Network","2070":"Protocol","2071":"Platform","2072":"Cryptocurrency","2073":"Financial","2074":"Financial","2075":"Ethereum","2076":"Cryptocurrency","2077":"Cryptocurrency","2078":"Blockchain Network","2079":"Cryptocurrency","2080":"Cryptocurrency","2081":"Sports","2082":"Cryptocurrency","2083":"Financial","2084":"Privacy","2085":"Network","2086":"Cryptocurrency","2087":"Gaming","2088":"Cryptocurrency","2089":null,"2090":"DeFi","2091":"Privacy","2092":"Blockchain Network","2093":"Privacy","2094":"Cryptocurrency","2095":"Masternodes","2096":"Cloud Storage","2097":"Ethereum","2098":"Cryptocurrency","2099":"Platform","2100":null,"2101":"DeFi","2102":"Cryptocurrency","2103":"Financial","2104":"DeFi","2105":"Financial","2106":"Privacy","2107":"Real Estate","2108":"Financial","2109":"Cryptocurrency","2110":"Blockchain Network","2111":"Other","2112":"DeFi","2113":"Gaming","2114":null,"2115":"Blockchain Service","2116":"Cryptocurrency","2117":"Privacy","2118":"Financial","2119":"Ethereum","2120":"Cloud Computing","2121":"Cryptocurrency","2122":"DeFi","2123":"Stablecoin","2124":"Cryptocurrency","2125":"Polygon","2126":"Decentralized Exchange","2127":"Other","2128":"Privacy","2129":"Cryptocurrency","2130":"Cryptocurrency","2131":"Cryptocurrency","2132":"Trading","2133":"Cryptocurrency","2134":"Insurance","2135":"Financial","2136":"Other","2137":"Verification","2138":"Gaming","2139":null,"2140":null,"2141":"Cryptocurrency","2142":"Interoperability","2143":"Ontology","2144":"Social","2145":"E-Commerce","2146":"Financial","2147":"Decentralized Exchange","2148":"Entertainment","2149":"Cryptocurrency","2150":"Cryptocurrency","2151":"Platform","2152":"Gambling","2153":"Platform","2154":"Privacy","2155":"Cloud Storage","2156":"Decentralized Exchange","2157":"Gaming","2158":"Stablecoin","2159":"Blockchain Service","2160":"DeFi","2161":"Media","2162":null,"2163":null,"2164":"Platform","2165":"Decentralized Exchange","2166":"Privacy","2167":"Blockchain Network","2168":"Infrastructure","2169":"Infrastructure","2170":"Cryptocurrency","2171":"Platform","2172":"Smart Contracts","2173":"Gaming","2174":"Ethereum","2175":"Adult","2176":"Other","2177":"Financial","2178":"Adult","2179":"Ethereum","2180":"Cryptocurrency","2181":"Other","2182":"Financial","2183":"Platform","2184":"Decentralized Exchange","2185":"Ethereum","2186":"Smart Contracts","2187":"Gaming","2188":"Privacy","2189":"Gaming","2190":"Decentralized Exchange","2191":"Prediction Market","2192":"Smart Contracts","2193":"Privacy","2194":"Decentralized Exchange","2195":null,"2196":null,"2197":null,"2198":null,"2199":"Network","2200":"Cryptocurrency","2201":"Cryptocurrency","2202":"Privacy","2203":"Decentralized Exchange","2204":"DeFi","2205":"Masternodes","2206":"Cryptocurrency","2207":"VeChain","2208":"DeFi","2209":"Smart Contracts","2210":"DeFi","2211":"Financial","2212":"Cryptocurrency","2213":"Blockchain Network","2214":"Cryptocurrency","2215":"Privacy","2216":"Ethereum","2217":"Other","2218":"Stablecoin","2219":null,"2220":null,"2221":null,"2222":"Prediction Market","2223":null,"2224":"Adult","2225":"Cryptocurrency","2226":"Stablecoin","2227":"Cryptocurrency","2228":"Other","2229":"Privacy","2230":"Gaming","2231":"Privacy","2232":"Smart Contracts","2233":"DeFi","2234":"Privacy","2235":"Cryptocurrency","2236":"Platform","2237":"Privacy","2238":"Platform","2239":"Decentralized Exchange","2240":null,"2241":"DeFi","2242":null,"2243":null,"2244":"Interoperability","2245":"Lending","2246":"Masternodes","2247":"Stablecoin","2248":"Cryptocurrency","2249":"DeFi","2250":"Cryptocurrency","2251":"Blockchain Network","2252":"DeFi","2253":"Lending","2254":"DeFi","2255":"Interoperability","2256":"Cryptocurrency","2257":"Stablecoin","2258":"E-Commerce","2259":"Cryptocurrency","2260":"DeFi","2261":"Gaming","2262":null,"2263":null,"2264":"DeFi","2265":"Platform","2266":"Infrastructure","2267":"Blockchain Service","2268":"DeFi","2269":"Social","2270":"DeFi","2271":"Entertainment","2272":"DeFi","2273":null,"2274":"Cloud Storage","2275":"Lending","2276":"Blockchain Service","2277":"Ethereum","2278":"Financial","2279":"DeFi","2280":"Marketing","2281":"Supply","2282":"Decentralized Exchange","2283":"Ethereum","2284":"Insurance","2285":"DeFi","2286":"Financial","2287":"Lending","2288":"Lending","2289":"DeFi","2290":"Interoperability","2291":"Protocol","2292":"Verification","2293":"Lending","2294":"Ethereum","2295":"Gaming","2296":"Protocol","2297":"DeFi","2298":"DeFi","2299":"Financial","2300":"Platform","2301":"Gaming","2302":"Protocol","2303":"Stablecoin","2304":"Interoperability","2305":"Platform","2306":"Protocol","2307":"Lending","2308":"Interoperability","2309":"Prediction Market","2310":"Decentralized Exchange","2311":"Lending","2312":"Platform","2313":null,"2314":null,"2315":"Cloud Computing","2316":"Gaming","2317":"IoT","2318":"Lending","2319":"Lending","2320":"Platform","2321":"Gaming","2322":"Platform","2323":"Ethereum","2324":"Binance","2325":"DeFi","2326":"DeFi","2327":"Decentralized Exchange","2328":"NFT","2329":"DeFi","2330":"Decentralized Exchange","2331":"Insurance","2332":"Platform","2333":"DeFi","2334":"DeFi","2335":"Ethereum","2336":"Gaming","2337":"Protocol","2338":"DeFi","2339":"Platform","2340":"DeFi","2341":"Trading","2342":"Energy","2343":"DeFi","2344":"DeFi","2345":null,"2346":"Ethereum","2347":"DeFi","2348":"Cloud Storage","2349":"Privacy","2350":"Cryptocurrency","2351":null,"2352":"DeFi","2353":"DeFi","2354":"Privacy","2355":"Financial","2356":"Insurance","2357":"Ethereum","2358":"Smart Contracts","2359":"Cryptocurrency","2360":"Protocol","2361":"Decentralized Exchange","2362":"Gambling","2363":"Gaming","2364":null,"2365":null,"2366":"Decentralized Exchange","2367":"Ethereum","2368":"Platform","2369":"Ethereum","2370":"Ethereum","2371":"Platform","2372":"Blockchain Service","2373":"DeFi","2374":"Binance","2375":"Cloud Storage","2376":"Interoperability","2377":"Cryptocurrency","2378":"Platform","2379":"Polygon","2380":"Entertainment","2381":"Cryptocurrency","2382":"AI","2383":"Social","2384":"Lending","2385":"Social","2386":"Decentralized Exchange","2387":"Blockchain Network","2388":"Cryptocurrency","2389":"Gambling","2390":"Binance","2391":"Gaming","2392":"Infrastructure","2393":"Marketing","2394":"Gaming","2395":"Media","2396":"Binance","2397":"DeFi","2398":"Gaming","2399":null,"2400":null,"2401":"Cryptocurrency","2402":"DeFi","2403":"Lending","2404":"Financial","2405":"Platform","2406":"Decentralized Exchange","2407":"Data","2408":"Cardano","2409":null,"2410":"AI","2411":"Financial","2412":"Gaming","2413":"DeFi","2414":"Ethereum","2415":"Lending","2416":null,"2417":"Interoperability","2418":"Ethereum","2419":null,"2420":null,"2421":null,"2422":null,"2423":null,"2424":null,"2425":null,"2426":null,"2427":null,"2428":"Gaming","2429":"DeFi","2430":"Gambling","2431":"Gaming","2432":"Cryptocurrency","2433":"Financial","2434":null,"2435":"DeFi","2436":"DeFi","2437":"Decentralized Exchange","2438":"DeFi","2439":"Social","2440":"Cryptocurrency","2441":"Financial","2442":"Financial","2443":"Gaming","2444":null,"2445":"Gaming","2446":"Entertainment","2447":"Entertainment","2448":"Binance","2449":"Sports","2450":"Media","2451":"Insurance","2452":"Sports","2453":"Financial","2454":"Decentralized Exchange","2455":null,"2456":"Lending","2457":"DeFi","2458":"Gaming","2459":null,"2460":"Social","2461":null,"2462":"Smart Contracts","2463":"Financial","2464":"Gaming","2465":"Interoperability","2466":"Interoperability","2467":"Blockchain Network","2468":"Interoperability","2469":"Blockchain Network","2470":"Binance","2471":null,"2472":"Blockchain Network","2473":"Gaming","2474":"Gaming","2475":"DeFi","2476":"DeFi","2477":"Gaming","2478":"Ethereum","2479":"Platform","2480":"Blockchain Network","2481":"Blockchain Service","2482":"Gaming","2483":"Entertainment","2484":"Gaming","2485":"Exchange","2486":"Exchange","2487":"Entertainment","2488":"DeFi","2489":"Metaverse","2490":"Social","2491":"Financial","2492":"Interoperability"},"name":{"0":"Color Platform","1":"Enecuum","2":"Cosmos","3":"ZeuxCoin","4":"Treelion","5":"Kava","6":"Synth sLINK","7":"TrueFi","8":"HyperExchange","9":"MargiX","10":"EOS Force","11":"YottaChain","12":"Anchor","13":"SEED Pool Token","14":"AllianceBlock","15":"Euro Tether","16":"0x Tracker","17":"Tepleton","18":"X World Games","19":"Electra Protocol","20":"7Eleven","21":"everiToken","22":"Yap Stone","23":"Dash Cash","24":"Hintchain","25":"Azbit","26":"Project WITH","27":"Chiliz","28":"DeepCloud AI","29":"Mint Club","30":"Pokeball","31":"Sessia","32":"Polkacover","33":"Helena","34":"Bitfex","35":"HUSD","36":"Fei USD","37":"DAD","38":"MeconCash","39":"GateToken","40":"Helium ","41":"Augmint","42":"Define","43":"MB8 Coin","44":"Swapcoinz","45":"ChronoCoin","46":"Nitrogen Network","47":"Totle","48":"D Community","49":"Ash","50":"LINKA","51":"Karura","52":"Poketto","53":"Tesra","54":"INLOCK","55":"InstaDApp","56":"XVIX","57":"SuperRare","58":"CryptoBossCoin","59":"CoinHe Token","60":"Flow","61":"Xensor","62":"Stake DAO","63":"FiatDex Gateway","64":"Lightning Network","65":"Zerion","66":"ALP Coin","67":"Nuo Network","68":"Braintrust","69":"UMA","70":"BenePit Protocol","71":"SSV Network","72":"Set Protocol","73":"Litecoin","74":"Typhoon Cash","75":"MimbleWimbleCoin","76":"Razor Network","77":"Snax","78":"PegaSys","79":"Chintai","80":"Coinzo Token","81":"Worbli","82":"MARKET Protocol","83":"BlockMason Credit Protocol","84":"JulSwap","85":"Synth sBCH","86":"Transledger","87":"Punk","88":"Kambria","89":"Cere Network","90":"X-power Chain","91":"GXChain","92":"Eminer","93":"Pledgecamp","94":"Basis Cash","95":"inSure DeFi","96":"Simone","97":"Celo Dollar","98":"Game.com","99":"EMOGI Network","100":"Insured Finance","101":"12Ships","102":"Lukki","103":"PLANET","104":"Tokoin","105":"FIBOS","106":"Cryptocean","107":"VideoCoin","108":"MixMarvel","109":"Sealchain","110":"Streamit Coin","111":"Yobit Token","112":"CryptoBonusMiles","113":"Paxos Standard","114":"VIDY","115":"Vodi X","116":"BidiPass","117":"Wavesbet","118":"Splintershards","119":"AXE","120":"Synth sDEFI","121":"QLC Chain","122":"DeFi Socks","123":"Emirex Token","124":"Amino Network","125":"NSS Coin","126":"Synth sXAG","127":"Xaya","128":"Valobit","129":"xCrypt Token","130":"Sentinel Protocol","131":"En-Tan-Mo","132":"Decentralized Vulnerability Platform","133":"Seedify.fund","134":"ChatCoin","135":"Ankr","136":"CNNS","137":"Molecular Future","138":"Artfinity","139":"Autonio","140":"SIRIN LABS Token","141":"WHEN Token","142":"Safe Exchange","143":"VNT Chain","144":"Spiking","145":"GoPower","146":"SpiritSwap","147":"Locus Chain","148":"Flowchain","149":"Trias","150":"QuickX Protocol","151":"ChartEx","152":"PATHHIVE","153":"BOLT","154":"PulsePad","155":"TOKOK","156":"INMAX","157":"Lambda","158":"Birdchain","159":"Yearn Finance DOT","160":"Contents Protocol","161":"AdEx","162":"Tribe","163":"P2P Global Network","164":"Paragon","165":"Asian Fintech","166":"Synth sCHF","167":"Synth sADA","168":"Synth sDASH","169":"S4FE","170":"GMB","171":"Zero Collateral","172":"Online","173":"CYCLUB","174":"Bitrue Coin","175":"Lisk Machine Learning","176":"API3","177":"Xriba","178":"Almeela ","179":"SnapCoin","180":"FNB Protocol","181":"Coineal Token","182":"BUDDY","183":"BuySell","184":"Cryptopay","185":"DIGG","186":"Synth iOIL","187":"NKN","188":"Synth sETC","189":"HashNet BitEco","190":"StackOs","191":"EveryCoin","192":"IZIChain","193":"InnovaMinex","194":"Quadrant Protocol","195":"Q DAO Governance token v1.0","196":"Fetch","197":"MOBOX","198":"ARMOR","199":"SpiderDAO","200":"Polygon","201":"DEX","202":"Hxro","203":"PTON","204":"Bonpay","205":"SwftCoin","206":"Levolution","207":"IOU","208":"SpaceChain","209":"Yfi.mobi","210":"ParkinGo","211":"Stacks","212":"Fatcoin","213":"Hercules","214":"CoinUs","215":"Assemble Protocol","216":"Decimated","217":"BOSAGORA","218":"Skychain","219":"FLETA","220":"EXRNchain","221":"Internxt","222":"Fountain","223":"EurocoinToken","224":"Carry","225":"AIDUS TOKEN","226":"Shyft Network","227":"IntelliShare","228":"B2BX","229":"SOLVE","230":"Origo","231":"Tchain","232":"CryptoFranc","233":"Membrana","234":"Solana","235":"DuckDaoDime","236":"Evedo","237":"DREP","238":"TigerCash","239":"SKALE Network","240":"PIXEL","241":"COVA","242":"Bodhi","243":"FOAM Protocol","244":"Dusk Network","245":"Clearpool","246":"Pendle","247":"Turtlecoin","248":"Binemon","249":"Sharder","250":"Herbalist Token","251":"The Currency Analytics","252":"Upfiring","253":"Tratok","254":"Akash Network","255":"Numeraire","256":"Unicly CryptoPunks Collection","257":"DeRace","258":"DigitalBits","259":"Titan Coin","260":"ProBit Token","261":"Gomics","262":"Gnosis","263":"Playkey","264":"PolkaBridge","265":"Wrapped Bitcoin","266":"MetisDAO","267":"O3Swap","268":"Lattice Token","269":"Coinvest","270":"Cherry Swap","271":"ZeroNet","272":"Bisq","273":"pEOS","274":"Band Protocol","275":"Telos","276":"SnowSwap","277":"Gods Unchained","278":"LGCY Network","279":"Hyper Speed Network","280":"Binance USD","281":"Staked","282":"KUN","283":"Alpaca Finance","284":"Crabada","285":"Curate","286":"PAID Network","287":"Tidal Finance","288":"Wall Street Games","289":"Krios","290":"Venus Reward Token","291":"Dragon Token","292":"CEEK","293":"BICONOMY","294":"TrueFeedBack","295":"PAX Gold","296":"Abra","297":"Hive","298":"MetaMoneyMarket","299":"Ariva","300":"Elitium","301":"Agrello","302":"AlphaWallet","303":"Ambo","304":"Argent","305":"Betoken","306":"LiquidApps","307":"OTOCASH","308":"Colletrix","309":"Howdoo","310":"AmonD","311":"Bob\u2019s Repair","312":"Business Credit Substitute","313":"Bitpie","314":"ClearPoll","315":"BUTTON Wallet","316":"Cobo Wallet","317":"Blockzero Labs","318":"Synth iLINK","319":"Polis","320":"NFTb","321":"Sin City Metaverse","322":"Blockchain Quotations Index Token","323":"Coinbase Wallet","324":"KEY","325":"Fetch","326":"imToken","327":"Roobee","328":"SolFarm","329":"Synth iTRX","330":"DeFi Saver","331":"DexWallet","332":"Condominium","333":"Synth sXMR","334":"Solanium","335":"CENTERCOIN","336":"LemoChain","337":"Beam","338":"SaluS","339":"Solrise Finance","340":"Synth sETH","341":"Tierion","342":"DeHive","343":"Rhenium","344":"Block Array","345":"Synth sXRP","346":"Alpha Token","347":"Synth iXRP","348":"Synth sEOS","349":"Olyseum","350":"Manchester City Fan Token","351":"Jigstack","352":"Blackmoon","353":"Bibox Token","354":"Synth iADA","355":"Synth iCEX","356":"Banano","357":"Synth iDEFI","358":"CONTRACOIN","359":"KeeperDAO","360":"Modefi","361":"Synth iBCH","362":"ICHI","363":"B.Protocol","364":"Sumokoin","365":"Pure","366":"Synth sEUR","367":"Synth iDASH","368":"NNB Token","369":"NIX","370":"Invacio","371":"BUX Token","372":"Mirrored Netflix","373":"Spendcoin","374":"Synth sBNB","375":"Smartlands","376":"Cubiex","377":"Redd","378":"Bitcoin Cash ABC","379":"LABS Group","380":"Cajutel","381":"VIBE","382":"Synth iLTC","383":"Torum","384":"Engine","385":"Lamden","386":"Synth sLTC","387":"Opacity","388":"Beta Finance","389":"DEW","390":"Synth sOIL","391":"Vai","392":"Synth sXTZ","393":"HyperCash","394":"Zilla","395":"MenaPay","396":"Polkadex","397":"SEER","398":"MaidSafeCoin","399":"CashBet","400":"Sav3Token","401":"Unobtanium","402":"Bytom","403":"Synth iBTC","404":"Cloudbric","405":"Ink","406":"Komodo","407":"Blockcloud","408":"Synth iETH","409":"Insureum","410":"Zeepin","411":"Synth iEOS","412":"Synth sAUD","413":"Zippie","414":"Decred","415":"Mines of Dalarnia","416":"Synth iETC","417":"Intelligent Investment Chain","418":"Enzyme","419":"FREE Coin","420":"RedFOX Labs","421":"Synth iXMR","422":"International Crypto X","423":"Quasarcoin","424":"MediBloc","425":"Synth iXTZ","426":"FUNToken","427":"Internet Computer","428":"Opulous","429":"TenX","430":"Elite","431":"SPINDLE","432":"ONOToken","433":"Synth iBNB","434":"Bifrost (BNC)","435":"Dogs Of Elon","436":"BeeKan","437":"Gas","438":"Emercoin","439":"COPYTRACK","440":"Burst","441":"Synth sTRX","442":"EUNO","443":"Dignity","444":"Odyssey","445":"Berry Data","446":"Themis","447":"CanonChain","448":"Experience Token","449":"Ether Zero","450":"Namecoin","451":"Mirrored Microsoft","452":"Invictus Hyperion Fund","453":"REVV","454":"PEAKDEFI","455":"Only1","456":"HDAC","457":"Primas","458":"GameFi","459":"VikkyToken","460":"MonaCoin","461":"DACC","462":"Paytomat","463":"Time New Bank","464":"ShipChain","465":"Effect.AI","466":"Zipper","467":"Esports Token","468":"Everus","469":"Venus XVS","470":"Zen Protocol","471":"Yuan Chain Coin","472":"MediShares","473":"Phoenix Global","474":"Venus USDC","475":"MoonSwap","476":"Mothership","477":"Step Hero","478":"Wagerr","479":"Connect Coin","480":"Grid+","481":"StockChain","482":"OWL Token (StealthSwap)","483":"TomoChain","484":"Scorum","485":"qiibee","486":"reflect.finance","487":"EUNOMIA","488":"Jiyo","489":"Moeda Loyalty Points","490":"Request","491":"Sun (New)","492":"Bitcoin Atom","493":"Divi","494":"Content Value Network","495":"Hurify","496":"FairCoin","497":"DigitalNote","498":"Bitcore","499":"Safe","500":"Primecoin","501":"Aeternity","502":"Snetwork","503":"Klever","504":"BlitzPredict","505":"Hermez Network","506":"Sashimi","507":"Steem Dollars","508":"Maple","509":"Eligma","510":"Chrono.tech","511":"ValueChain","512":"Graviocoin","513":"Stealth","514":"Bitsum","515":"SHIELD","516":"Pivot Token","517":"KILT Protocol","518":"ADAPad","519":"Fantom","520":"MinexCoin","521":"TokenPay","522":"Faceter","523":"VITE","524":"vEmpire DDAO","525":"Litecoin Cash","526":"Pura","527":"EvenCoin","528":"Bee Token","529":"Nebulas","530":"GoChain","531":"DomRaider","532":"Router Protocol","533":"FLUX","534":"Experty","535":"CPChain","536":"Gulden","537":"MyToken","538":"Monero Classic","539":"BrahmaOS","540":"Morpheus Labs","541":"Narrative Network","542":"U Network","543":"ParaSwap","544":"Polybius","545":"Sp8de","546":"SALT","547":"HEROcoin","548":"GoWithMi","549":"YooShi","550":"DerivaDAO","551":"Po.et","552":"CyberVein","553":"Lendingblock","554":"Speed Mining Service","555":"TosDis","556":"Stratis","557":"Particl","558":"Nash Exchange","559":"Starbase","560":"The Midas Touch Gold","561":"OriginTrail","562":"Opium","563":"Expanse","564":"Sentinel","565":"Blockport","566":"DAO.Casino","567":"DeFi Bids","568":"eosDAC","569":"No BS Crypto","570":"Polkastarter","571":"Rentberry","572":"Ravencoin Classic","573":"Venus BTC","574":"Reflexer Ungovernance Token","575":"Asura Coin","576":"HYCON ","577":"Steem","578":"Moneynet","579":"Venus BCH","580":"Sharpe Platform Token","581":"ALBOS","582":"Waletoken","583":"Eden","584":"Biswap","585":"\u03c4Bitcoin","586":"Venus DAI","587":"BABB","588":"BioPassport Token","589":"XMax","590":"ONUS","591":"Revomon","592":"WeTrust","593":"Vivid Coin","594":"Bridge Mutual","595":"Refereum","596":"LBRY Credits","597":"CoinEx Token","598":"VIMworld","599":"Genesis Vision","600":"THEKEY","601":"Sakura Bloom","602":"Nano","603":"IQ.cash","604":"ConstitutionDAO","605":"Groestlcoin","606":"Lightning Bitcoin","607":"More Coin","608":"Kalkulus","609":"Radio Caca","610":"GameZone","611":"Drep [new]","612":"QASH","613":"indaHash","614":"Proxeus","615":"Stellar","616":"UnMarshal","617":"Doge Dash","618":"Venus USDT","619":"Furucombo","620":"Aurory","621":"Bottos","622":"Bytecoin","623":"Trust Wallet Token","624":"Trinity Network Credit","625":"Globalvillage Ecosystem","626":"Name Changing Token","627":"Bitcoin File","628":"IRISnet","629":"T.OS","630":"Spectiv","631":"YUI Token","632":"WagyuSwap","633":"Futureswap","634":"Sentinel Chain","635":"MAPS","636":"LockTrip","637":"HorusPay","638":"Electroneum","639":"Biotron","640":"Nxt","641":"Convergence","642":"Stronghold Token","643":"Electrify.Asia","644":"Nerves","645":"Lykke","646":"BOMB","647":"DMarket","648":"Viacoin","649":"ION","650":"Wootrade","651":"Sociall","652":"Atlantis Blue Digital Token","653":"Metahero","654":"Nebula AI","655":"Decentralized Machine Learning","656":"Distributed Credit Chain","657":"PayPie","658":"Ardor","659":"SwissBorg","660":"Jesus Coin","661":"Ternio","662":"UNUS SED LEO","663":"SportyCo","664":"Centrifuge","665":"Pundi X NEM","666":"Educoin","667":"DNotes","668":"Influence Chain","669":"Arcblock","670":"Ignis","671":"Aeron","672":"DMScript","673":"BetterBetting","674":"The ChampCoin","675":"QuickSwap","676":"Dent","677":"ARPA Chain","678":"IONChain","679":"OAX","680":"Oasis Network","681":"Maker","682":"Maro","683":"Blockchain Monster Hunt","684":"Rublix","685":"Compound Uniswap","686":"DAEX","687":"MktCoin","688":"Gemini Dollar","689":"Valuto","690":"FTX Token","691":"TrueChain","692":"Endorsit","693":"Reef","694":"Penta","695":"RAMP","696":"DaTa EXchange","697":"Compound USDT","698":"Ultra","699":"Dascoin","700":"Font","701":"Peercoin","702":"Aave LINK","703":"Cellframe","704":"CoinPoker","705":"BlockTrade","706":"Bigbom","707":"BlockMesh","708":"Kind Ads Token","709":"bitCNY","710":"Polkamarkets","711":"Transcodium","712":"TitanSwap","713":"Ryo Currency","714":"JOE","715":"BoutsPro","716":"TERA","717":"ATN","718":"Business Credit Alliance Chain","719":"Storm","720":"Aave ETH","721":"Linda","722":"Bezant","723":"Magic Internet Money","724":"GoldMint","725":"Helbiz","726":"Bodhi [ETH]","727":"Kuende","728":"BuckHathCoin","729":"Cardstack","730":"Aave TUSD","731":"Freeway Token","732":"DigiFinexToken","733":"WaykiChain","734":"FuturoCoin","735":"Patron","736":"Docademic","737":"Aave DAI","738":"Aave MKR","739":"Aave BAT","740":"FuzeX","741":"Aave USDT","742":"Oraichain Token","743":"Parkgene","744":"Quanta Utility Token","745":"Presearch","746":"Bitex Global XBX Coin","747":"Valor Token","748":"ODUWA","749":"Civic","750":"Stafi","751":"Elysian","752":"Decentralized Social","753":"ALLUVA","754":"dKargo","755":"BnkToTheFuture","756":"Silent Notary","757":"Cybereits","758":"POPCHAIN","759":"EOSDT","760":"OneLedger","761":"Aave SUSD","762":"Aurora","763":"CryCash","764":"EDUCare","765":"Qredo","766":"FootballCoin","767":"Skrumble Network","768":"Aave MANA","769":"Kebab Token","770":"Lunyr","771":"Pepe Cash","772":"Promotion Coin","773":"Raven Protocol","774":"Debitum","775":"Aeon","776":"Soarcoin","777":"DeepBrain Chain","778":"Crypterium","779":"Echoin","780":"U.CASH","781":"Aave KNC","782":"Telcoin","783":"VinDax Coin","784":"OLXA","785":"VisionX","786":"BoringDAO","787":"Shekel","788":"Aave ZRX","789":"Blind Boxes","790":"Kava Swap","791":"Pixie Coin","792":"Pal Network","793":"PitisCoin","794":"Nexo","795":"Proton Token","796":"ARBITRAGE","797":"Kora Network Token","798":"Kryll.io","799":"E-Dinar Coin","800":"Nasdacoin","801":"CROAT","802":"Matchpool","803":"BQT","804":"Huobi Token","805":"Amp","806":"Jury.Online Token","807":"Friendz","808":"Cosmo Coin","809":"ModulTrade","810":"Blue Whale EXchange","811":"Aave BUSD","812":"Forty Seven Bank (47 Bank)","813":"Wrapped NCG (Nine Chronicles Gold)","814":"empowr coin","815":"Exosis","816":"Bitcoin BEP2","817":"Temco","818":"0xcert Protocol","819":"Prometeus","820":"Aave REP","821":"Aave LEND","822":"DAV Token","823":"Cashberry Coin","824":"HitChain","825":"Pillar","826":"PumaPay","827":"LocalCoinSwap","828":"LEOcoin","829":"OKB","830":"MIR COIN","831":"Timicoin","832":"Mango Markets","833":"Honest","834":"Consentium","835":"BitRewards","836":"ATC Coin","837":"Theta Fuel","838":"CUDOS","839":"Airbloc","840":"DxChain","841":"APIS","842":"Ubex","843":"LogisCoin","844":"Insolar","845":"Davinci Coin","846":"Coldstack","847":"NFTX Hashmasks Index","848":"Olive","849":"OceanEx","850":"Ark","851":"bitCEO","852":"IGToken","853":"Falconswap","854":"AiLink Token","855":"Index Cooperative","856":"Chintai","857":"Wabi","858":"MarsX","859":"XMON","860":"KWHCoin","861":"Six Domain Chain","862":"CariNet","863":"FarmaTrust","864":"Niobio Cash","865":"MGC Token","866":"Knoxstertoken","867":"Blocery","868":"Butterfly Protocol","869":"Kin","870":"TFL.io","871":"Govi","872":"Curio Governance","873":"HOLD","874":"WPP","875":"BUMO","876":"HeartBout","877":"Auto","878":"Carbon","879":"SEEN","880":"Ti-Value","881":"district0x","882":"Gems ","883":"EosBLACK","884":"Quantstamp","885":"Bitmart","886":"Global Social Chain","887":"DWS","888":"Formosa Financial","889":"HashCoin","890":"Contentos","891":"V-Dimension","892":"Helpico","893":"Gourmet Galaxy","894":"PolkaDomain","895":"Zeusshield","896":"DPRating","897":"MyBit","898":"Ice Rock Mining","899":"BitcoiNote","900":"Webcoin","901":"Relex","902":"MEX","903":"Remme","904":"Aave USDC","905":"yAxis","906":"Alphacat","907":"YOU COIN","908":"Aave WBTC","909":"ClearCoin","910":"Starta","911":"GSENetwork","912":"MoneyToken","913":"Rotharium","914":"Rogue West","915":"APYSwap","916":"ugChain","917":"United Traders Token","918":"ProximaX","919":"OneRoot Network","920":"LinkEye","921":"Etherparty","922":"ShowHand","923":"Beyondfi","924":"DogeBonk","925":"Lendroid Support Token","926":"Iconomi","927":"WHALE","928":"AirSwap","929":"Medicalchain","930":"Chronologic","931":"ZeroSwap","932":"GoHelpFund","933":"Metal","934":"Beefy.Finance","935":"Eva Cash","936":"WeShow Token","937":"Novacoin","938":"OTCBTC Token","939":"Couchain","940":"Razor Network","941":"Bitcoin SV","942":"DreamTeam","943":"W3Coin","944":"Earth Token","945":"DATx","946":"Stox","947":"Neumark","948":"Moviebloc","949":"MoneroV","950":"Decent","951":"Dragon Coins","952":"ZMINE","953":"Graphlinq Protocol","954":"SIX","955":"Paycent","956":"Robotina","957":"Bobo Cash","958":"Unvest","959":"DAPS Token","960":"Metronome","961":"Humanscape","962":"Nuggets","963":"Smartshare","964":"Eristica","965":"Non-Fungible Yearn","966":"Clover Finance","967":"VeriME","968":"Glitch","969":"Measurable Data Token","970":"Pibble","971":"CARAT","972":"SIDUS HEROES","973":"Daneel","974":"Insights Network","975":"Fractal","976":"CyberMiles","977":"GIGA","978":"DEXTools","979":"SDChain","980":"Wixlar","981":"Metadium","982":"ADAMANT Messenger","983":"ELA Coin","984":"PlayDapp","985":"Yearn Secure","986":"Waifu Token","987":"MetaMorph","988":"IceChain","989":"VegaWallet Token","990":"NAGA","991":"HashBX","992":"AMO Coin","993":"LTO Network","994":"Fortuna","995":"Akropolis","996":"Rainicorn","997":"$LONDON","998":"NEXT","999":"Fluz Fluz","1000":"Reserve","1001":"TE-FOOD","1002":"Efinity","1003":"Ink Protocol","1004":"Frax Share","1005":"LYNC Network","1006":"SOAR.FI","1007":"8PAY","1008":"Ormeus Coin","1009":"Atlas Protocol","1010":"Hashgard","1011":"Blockchain Certified Data Token","1012":"Darico Ecosystem Coin","1013":"HyperQuant","1014":"ILCoin","1015":"Orion Money","1016":"Tolar","1017":"Etheera","1018":"Safe Haven","1019":"Zilliqa","1020":"OnX Finance","1021":"Substratum","1022":"LinkToken","1023":"Lition","1024":"BHPCash","1025":"Unibright","1026":"EdenChain","1027":"aXpire","1028":"Big Bang Game Coin","1029":"Duck DAO","1030":"Blockpass","1031":"Newton","1032":"Streamr","1033":"Trinity Protocol","1034":"XYO Network","1035":"Radium","1036":"PlayGame Token","1037":"Canya","1038":"Max Property Group","1039":"VeriBlock","1040":"Scanet World Coin","1041":"MetaHash","1042":"DECOIN","1043":"Serve","1044":"Nectar","1045":"ICON","1046":"Merculet","1047":"SakeToken","1048":"BitDAO","1049":"Hyperion","1050":"Filecoin","1051":"IHT Real Estate Protocol","1052":"Benz","1053":"Chain Guardians","1054":"Bitcoin Gold","1055":"Simple Token (OST)","1056":"Matryx","1057":"AidCoin","1058":"Adventure Gold","1059":"Bitcoin Interest","1060":"MultiVAC","1061":"Atari Token","1062":"ORO","1063":"Tether","1064":"Callisto Network","1065":"Auctus","1066":"Magic Cube Coin","1067":"Aladdin","1068":"DAOventures","1069":"Jarvis+","1070":"Dragonchain","1071":"Loopring [NEO]","1072":"Holo","1073":"Incent","1074":"Gala","1075":"Content Neutrality Network","1076":"Covesting","1077":"Atonomi","1078":"Volt","1079":"Fundamenta","1080":"Maecenas","1081":"AVINOC","1082":"WinStars.live","1083":"PalletOne","1084":"Coinsuper Ecosystem Network","1085":"Brazilian Digital Token","1086":"WINk","1087":"GAMEE","1088":"ArGo","1089":"UniMex Network","1090":"Forefront","1091":"BaaSid","1092":"Ethereum","1093":"NPER","1094":"COTI","1095":"BOOM","1096":"Webflix Token","1097":"UTRUST","1098":"NEM","1099":"Santiment","1100":"Elastos","1101":"Boba Network","1102":"Datawallet","1103":"Neural Protocol","1104":"GoNetwork","1105":"Loom Network","1106":"Chromia","1107":"Nucleus Vision","1108":"Mastercoin (Omni)","1109":"Katalyo","1110":"Ivy","1111":"SF Capital","1112":"ChainX","1113":"Lympo","1114":"Lith Token","1115":"Public Mint","1116":"Sarcophagus","1117":"Cryptaur","1118":"Birake","1119":"Caspian","1120":"Function X","1121":"YEE","1122":"Kleros","1123":"Ulord","1124":"POA Network","1125":"DecentraWeb","1126":"Agoras: Currency of Tau","1127":"Thingschain","1128":"CWV Chain","1129":"Unification","1130":"Propy","1131":"HTMLCOIN","1132":"Neurochain","1133":"CyberMusic","1134":"Fivebalance ","1135":"Splyt","1136":"GMT Token","1137":"TrueUSD","1138":"Smartup","1139":"carVertical","1140":"Rage Fan","1141":"SafePal","1142":"Boltt Coin","1143":"Dapp Token","1144":"Bitcoin Cash","1145":"MahaDAO","1146":"ZClassic","1147":"Dogey-Inu","1148":"Kangal","1149":"BoatPilot Token","1150":"Qredit","1151":"Pluton","1152":"UGAS","1153":"IterationSyndicate","1154":"EFFORCE","1155":"APY.Finance","1156":"Hiveterminal Token","1157":"Sylo","1158":"Quantum Resistant Ledger","1159":"MassGrid","1160":"Dexter G","1161":"Apex","1162":"PRIA","1163":"InsurePal","1164":"Power Ledger","1165":"Lisk","1166":"HedgeTrade","1167":"WEMIX","1168":"nOS","1169":"Banyan Network","1170":"Mossland","1171":"Bluzelle","1172":"ShineChain","1173":"CGC Token","1174":"CargoX","1175":"Oxen","1176":"Enjin Coin","1177":"CRD Network","1178":"UBIX.Network","1179":"Collateral Pay","1180":"Vexanium","1181":"BitCapitalVendor","1182":"DDKoin","1183":"DADI","1184":"Credits","1185":"CommerceBlock","1186":"Plair","1187":"Kcash","1188":"Tcash","1189":"OWNDATA ","1190":"Flash","1191":"Zero Utility Token","1192":"Warp Finance","1193":"Hydro Protocol","1194":"EOS","1195":"Loopring","1196":"MobileGo","1197":"VeChain","1198":"Polymath","1199":"Ripio Credit Network","1200":"saffron.finance","1201":"APR Coin","1202":"BarnBridge","1203":"Tapmydata","1204":"Chonk","1205":"Nuco.cloud","1206":"OptionRoom","1207":"Uniswap","1208":"iExec RLC","1209":"FLO","1210":"Storj","1211":"HOQU","1212":"Umbrella Network","1213":"Thunder Token","1214":"Moonriver","1215":"Qtum","1216":"Fera","1217":"StakerDAO","1218":"BITTO","1219":"Consensus","1220":"Monero","1221":"YGGDRASH","1222":"Orbs","1223":"Vetri","1224":"Centauri","1225":"WOLLO","1226":"DAOstack","1227":"Flux","1228":"Tezos","1229":"Dether","1230":"Tornado Cash","1231":"Dero","1232":"Fire Lotto","1233":"Humaniq","1234":"ContentBox","1235":"Populous","1236":"BOX Token","1237":"usdc-b","1238":"Waves","1239":"FaraLand","1240":"Selfkey","1241":"Bilaxy Token","1242":"CoinMetro","1243":"AdShares","1244":"Wirex","1245":"Endor","1246":"Budbo","1247":"e-Money","1248":"DACSEE","1249":"Blank Wallet","1250":"Neutrino Token","1251":"Sora Validator Token","1252":"Syntropy","1253":"MileVerse","1254":"IOST","1255":"Crowd Machine","1256":"Puregold.io","1257":"Scry.info","1258":"BitShares","1259":"ZPER","1260":"DUO Network","1261":"Ecomi","1262":"Digitex","1263":"Red Pulse","1264":"Silverway","1265":"QuarkChain","1266":"Like","1267":"Ruff","1268":"Counterparty","1269":"AppCoins","1270":"Usechain Token","1271":"Ampleforth","1272":"Wibson","1273":"Vulcan Forged PYR","1274":"White Standard","1275":"Genaro Network","1276":"Vega Protocol","1277":"QunQun","1278":"SolarCoin","1279":"TranslateMe Network Token","1280":"Bitcoin","1281":"BHPCoin","1282":"Ternoa","1283":"MASTERNET","1284":"Aergo","1285":"tBTC","1286":"Swapfolio","1287":"S.C. Corinthians Fan Token","1288":"Raiden Network Token","1289":"BLOCKv","1290":"Arionum","1291":"adbank","1292":"ODEM","1293":"Feathercoin","1294":"Open Platform","1295":"Viberate","1296":"Devery","1297":"Switcheo","1298":"Offshift","1299":"Stone DeFi","1300":"Hacken Token","1301":"Cryptrust","1302":"GNY","1303":"Zerobank","1304":"3DCoin","1305":"TouchCon","1306":"Hero Node","1307":"Tokenbox","1308":"Essentia","1309":"Factom","1310":"NaPoleonX","1311":"Finxflo","1312":"PolySwarm","1313":"Egretia","1314":"VINChain","1315":"DAO Maker","1316":"TGAME","1317":"AIOZ Network","1318":"Dock","1319":"BinaryX","1320":"Niobium Coin","1321":"Jibrel Network","1322":"XinFin","1323":"Deri Protocol","1324":"Bread","1325":"ARAW","1326":"Amon","1327":"Litex","1328":"Neurotoken","1329":"VVS Finance","1330":"Life Crypto","1331":"Arepacoin","1332":"ZIMBOCASH","1333":"Restart Energy MWAT","1334":"UnlimitedIP","1335":"High Performance Blockchain","1336":"MEET.ONE","1337":"CoinFi","1338":"Whiteheart","1339":"UFO Gaming","1340":"Lithium","1341":"Fabric Token","1342":"Everipedia","1343":"Einsteinium","1344":"BitKan","1345":"EpiK Protocol","1346":"Engagement Token","1347":"Hifi Finance","1348":"Rangers Protocol","1349":"BigONE Token","1350":"Naviaddress","1351":"Tripio","1352":"adToken","1353":"Own","1354":"SyncFab","1355":"ORS Group","1356":"Origin Sport","1357":"Nuls","1358":"EncrypGen","1359":"Prosper","1360":"RightMesh","1361":"Mooncoin","1362":"Everex","1363":"Electronic Energy Coin","1364":"Mobius","1365":"Litentry","1366":"Gifto","1367":"Bankex","1368":"AstroSwap","1369":"Signum","1370":"Rate3 Network","1371":"Matrix AI Network","1372":"Monetha","1373":"ChainLink","1374":"Wings","1375":"Arbidex","1376":"SmartMesh","1377":"NuBits","1378":"aelf","1379":"Decentraland","1380":"RED","1381":"DRP Utility","1382":"HoDooi.com","1383":"TNC Coin","1384":"Smart MFG","1385":"Tranche Finance","1386":"Stratos","1387":"Axion","1388":"Bitball Treasure","1389":"Circuits of Value","1390":"GreenMed","1391":"Status","1392":"V Systems","1393":"Ubique Chain Of Things","1394":"Peculium","1395":"CapdaxToken","1396":"Aragon Court","1397":"Dharma","1398":"Empty Set Dollar","1399":"CACHE Gold","1400":"Carlive Chain","1401":"Cindicator","1402":"STASIS EURS","1403":"BOScoin","1404":"Universa","1405":"AMLT Token","1406":"Sharpay","1407":"Rai Reflex Index","1408":"dYdX","1409":"0chain","1410":"nDEX","1411":"YouLive Coin","1412":"Indorse Token","1413":"Basic Attention Token","1414":"SENSO","1415":"Aditus","1416":"Maincoin","1417":"Bulwark","1418":"Bitpanda","1419":"ThingsOperatingSystem","1420":"TokenClub","1421":"WePower","1422":"Swarm","1423":"Quant","1424":"Stakinglab","1425":"B20","1426":"TENT","1427":"SuperFarm","1428":"Toko Token","1429":"Persistence","1430":"Dentacoin","1431":"SONM","1432":"IoT Chain","1433":"BORA","1434":"Rari Governance Token","1435":"Atomic Wallet Coin","1436":"TokenStars","1437":"PlatonCoin","1438":"Neblio","1439":"Master Contract Token","1440":"SingularityDAO","1441":"PolygonMatic","1442":"stETH (Lido)","1443":"Wrapped BNB","1444":"Incodium","1445":"HelloGold","1446":"Privatix","1447":"Muzika","1448":"ZKSwap","1449":"Tokenomy","1450":"1World","1451":"Hathor","1452":"FirmaChain","1453":"SnowGem","1454":"SafeMoon Inu","1455":"PikcioChain","1456":"IXT","1457":"SureRemit","1458":"Nestree","1459":"ValueCyberToken","1460":"FansTime","1461":"UniLend","1462":"Pundi X","1463":"Traceability Chain","1464":"GMCoin","1465":"Goose Finance","1466":"NFTX","1467":"Venus BNB","1468":"Boolberry","1469":"Achain","1470":"Cobak Token","1471":"DecentBet","1472":"CloakCoin","1473":"IQeon","1474":"Agora","1475":"eCash","1476":"FintruX Network","1477":"Banca","1478":"UChain","1479":"The Tokenized Bitcoin","1480":"Mithril","1481":"Voyager Token","1482":"CumRocket","1483":"Taraxa","1484":"Unido EP","1485":"Conflux Network","1486":"IOTA","1487":"YOYOW","1488":"Bitnation","1489":"Thrive","1490":"Global Currency Reserve","1491":"NewsToken","1492":"ZrCoin","1493":"PHI Token","1494":"IoTeX","1495":"Phoenix Global (new)","1496":"Synth sBTC","1497":"One Cash","1498":"Midas Dollar","1499":"Kattana","1500":"Blox","1501":"Mysterium","1502":"Rapids","1503":"Enigma","1504":"ATLANT","1505":"CafeSwap Token","1506":"Diamond Platform Token","1507":"Custody Token","1508":"Structured","1509":"Egoras","1510":"Huobi BTC","1511":"Nervos Network","1512":"Bispex","1513":"Golem","1514":"yOUcash","1515":"Vesper","1516":"DEX.AG","1517":"Ormeus Ecosystem","1518":"Mdex","1519":"Decentral Games","1520":"Vinci","1521":"VNX","1522":"CAPITAL X CELL","1523":"suterusu","1524":"EXMR","1525":"LocalCryptos ","1526":"HOPR","1527":"Liquity","1528":"Single Collateral DAI","1529":"DeFiChain","1530":"Multi-collateral DAI","1531":"ShareToken","1532":"Venus LTC","1533":"Safex Token","1534":"Blockium","1535":"Connext","1536":"Chi Gastoken","1537":"LIFE","1538":"Fortem Capital","1539":"ZB Token","1540":"pTokens BTC","1541":"Ripple","1542":"Nord Finance","1543":"Aphelion","1544":"BTCPay","1545":"Unicly Mystic Axies Collection","1546":"VNX Exchange","1547":"Fusion","1548":"MVL","1549":"Zebi","1550":"Filenet","1551":"Star Atlas","1552":"Medium","1553":"Lendf","1554":"Sablier","1555":"Bao Finance","1556":"Aidos Kuneen","1557":"Rally","1558":"Venus SXP","1559":"AC Milan Fan Token","1560":"CoTrader","1561":"Star Atlas DAO","1562":"Mogu","1563":"Portis","1564":"Alchemix","1565":"Komet","1566":"CONUN","1567":"Xdef Finance","1568":"Cratos","1569":"Rakon","1570":"NPCoin","1571":"BetProtocol","1572":"Orchid","1573":"KickToken","1574":"Sparkswap","1575":"RSK","1576":"UltrAlpha","1577":"Unistake","1578":"Infinitecoin","1579":"X-CASH","1580":"Davion","1581":"CoinLoan","1582":"Zynecoin","1583":"Royale Finance","1584":"Coinsbit Token","1585":"Enzo","1586":"ZBG Token","1587":"dForce USDx","1588":"San Index","1589":"Tachyon Protocol","1590":"INT Chain","1591":"ROAD","1592":"Multiplier","1593":"Tokenlon Network Token","1594":"Complete New Commerce Chain","1595":"Immutable X","1596":"Travala","1597":"AntiMatter","1598":"Audius","1599":"One Share","1600":"Ethereum Push Notification Service","1601":"The Transfer Token","1602":"Mirrored ProShares VIX","1603":"Revolution Populi","1604":"NOW Token","1605":"WELL","1606":"Orient Walt","1607":"Pizza","1608":"Bounce Token","1609":"3X Short Bitcoin Token","1610":"LUKSO","1611":"Ethereum Name Service","1612":"Visor.Finance","1613":"3X Long Bitcoin Token","1614":"Neutrino USD","1615":"GoCrypto Token","1616":"Venus LINK","1617":"InsurAce","1618":"TROY","1619":"Centric Swap","1620":"Nexalt","1621":"Tether Gold","1622":"DATA","1623":"Curve DAO Token","1624":"MATH","1625":"Keystone of Opportunity & Knowledge","1626":"WazirX","1627":"ARCS","1628":"Big Data Protocol","1629":"Idle","1630":"Lock","1631":"ScPrime","1632":"Torque","1633":"Inverse Finance","1634":"Spartan Protocol","1635":"NFT Index","1636":"USDX [Kava]","1637":"K21","1638":"Hydro","1639":"Orbit Chain","1640":"pTokens","1641":"Hoge Finance","1642":"Thorstarter","1643":"HyperDAO","1644":"WETH","1645":"Joint Ventures","1646":"apM Coin","1647":"Degenerator Meme","1648":"Swarm Markets","1649":"Mars","1650":"Handshake","1651":"Synthetify","1652":"Perth Mint Gold Token","1653":"Validity","1654":"Keep3rV1","1655":"RSK Infrastructure Framework","1656":"Alien Worlds","1657":"GravityCoin","1658":"Tokemak","1659":"Incognito","1660":"AstroTools","1661":"TTC","1662":"Mirrored Amazon","1663":"Coin Artist","1664":"Standard Tokenization Protocol","1665":"Observer","1666":"sXAU","1667":"Cred","1668":"Elixxir","1669":"Unizen","1670":"AXEL","1671":"Oxbull.tech","1672":"Rupiah Token","1673":"Unifi Protocol DAO","1674":"XCredit","1675":"Aurox","1676":"Exeedme","1677":"WeOwn","1678":"TopBidder","1679":"merkleX","1680":"Muse","1681":"ParallelCoin","1682":"Radix","1683":"Shadows","1684":"SparkPoint","1685":"Tenset","1686":"Kobocoin","1687":"DigiByte","1688":"Ethereum Yield","1689":"Project Coin","1690":"Bondly","1691":"DEUS Finance","1692":"Kylin","1693":"Genesis Shards","1694":"Bancor","1695":"WOOF","1696":"SYNC Network","1697":"PolygonUSDCoin","1698":"PolygonChainLink","1699":"Falcon Project","1700":"Global Coin Research","1701":"Vabble","1702":"EasyFi","1703":"Ocean Protocol","1704":"1inch","1705":"MMOCoin","1706":"PARSIQ","1707":"Paypolitan Token","1708":"Paint","1709":"BSCPAD","1710":"PolygonAave","1711":"Californium","1712":"Ethereum Stake","1713":"Crust Shadow","1714":"AXPR","1715":"Receive Access Ecosystem","1716":"ExNetwork Token","1717":"Iconiq Lab","1718":"Trustlines Network Token","1719":"DEOR","1720":"Acute Angle Cloud","1721":"Frax","1722":"Polkalokr","1723":"Huobi Pool Token","1724":"CasperLabs","1725":"Yield","1726":"StakedZEN","1727":"DEAPcoin","1728":"JUST Stablecoin","1729":"JUST","1730":"KardiaChain","1731":"Uquid Coin","1732":"PCHAIN","1733":"The Force Protocol","1734":"Seele-N","1735":"Revain","1736":"Pundi X[new]","1737":"Minty Art","1738":"SURF Finance","1739":"Paris Saint-Germain Fan Token","1740":"I\/O Coin","1741":"Kishu Inu","1742":"Juventus Fan Toke","1743":"Switch","1744":"Flexacoin","1745":"Compound Dai","1746":"Compound USD Coin","1747":"Compound Ether","1748":"THORChain","1749":"Bifrost","1750":"Gold Poker","1751":"Ruler Protocol","1752":"Totem","1753":"YVS.Finance","1754":"Smooth Love Potion","1755":"WAX","1756":"Constellation","1757":"Ethereum Classic","1758":"FinNexus","1759":"Render Token","1760":"Creditcoin","1761":"Yield Guild Games","1762":"Peanut","1763":"renBTC","1764":"Ampleforth Governance Token","1765":"Olympus","1766":"StormX","1767":"Boson Protocol","1768":"Super Zero Protocol","1769":"RING X PLATFORM","1770":"HEX","1771":"KINE","1772":"WaykiChain Governance Coin","1773":"All Sports","1774":"Kyber Network Crystal","1775":"Morpheus Network","1776":"Whole Network","1777":"LCX","1778":"Treecle","1779":"Robonomics Web Services","1780":"BitCherry","1781":"BTSE","1782":"Crowns","1783":"Radicle","1784":"Smoothy","1785":"Xend Finance","1786":"Popsicle Finance","1787":"Unicly Hashmasks Collection","1788":"BakeryToken","1789":"CHADS VC","1790":"Avalanche","1791":"Hoo Token","1792":"NEST Protocol","1793":"Portion","1794":"Orion Protocol","1795":"1-UP Platform","1796":"Dogelon Mars","1797":"Multiplier","1798":"Decentr","1799":"MCDex","1800":"Alchemist","1801":"Dfyn Network","1802":"PlutusDeFi","1803":"yearn.finance","1804":"LunchMoney","1805":"Wrapped NXM","1806":"Liquidity Dividends Protocol","1807":"SHIBA INU","1808":"IG Gold","1809":"Republic Note","1810":"Method Finance","1811":"Bitbns","1812":"WOM Protocol","1813":"Tendies","1814":"Antiample","1815":"AGA Token","1816":"Fear NFTs","1817":"Libertas Token","1818":"Sport and Leisure","1819":"Mask Network","1820":"SUKU","1821":"AAX Token","1822":"Sora","1823":"MobileCoin","1824":"Spore Finance","1825":"Vanilla Network","1826":"APENFT","1827":"Dos Network","1828":"bAlpha","1829":"DeFiPie","1830":"Augur","1831":"Origin Dollar","1832":"Zap","1833":"Truebit","1834":"DIA","1835":"SOMESING","1836":"Fabric","1837":"CBDAO","1838":"Tacos","1839":"RMPL","1840":"Ethverse","1841":"SafeMoon","1842":"Keep4r","1843":"Coin98","1844":"PieDAO DEFI++","1845":"Kiba Inu","1846":"Rivex","1847":"Strong","1848":"DXdao","1849":"Plasma Finance","1850":"Tower","1851":"NEAR Protocol","1852":"VIDT Datalink","1853":"YF Link","1854":"UREEQA","1855":"DOGEFI","1856":"Loser Coin","1857":"Aave SNX","1858":"DSLA Protocol","1859":"Waves Enterprise","1860":"Casper","1861":"PowerTrade","1862":"Chia Network","1863":"WingShop","1864":"MixTrust","1865":"XTRABYTES","1866":"Keysians Network","1867":"Unisocks","1868":"Iconic Token","1869":"Bird.Money","1870":"Meme","1871":"Pepemon Pepeballs","1872":"Force For Fast","1873":"Zelwin","1874":"DefiDollar DAO","1875":"Jasmy","1876":"Catex Token","1877":"Ceres","1878":"Bridge Oracle","1879":"Strike","1880":"Polkadot","1881":"SingularityNET","1882":"YUNo.finance","1883":"Oxygen","1884":"Fox Trading","1885":"Raydium","1886":"Liquity USD","1887":"Quantfury Token","1888":"Dextoken","1889":"Axis DeFi","1890":"YFIEXCHANGE.FINANCE","1891":"Pmeer","1892":"HARD Protocol","1893":"WebDollar","1894":"AceD","1895":"DegenVC","1896":"Konomi Network","1897":"Growth DeFi","1898":"Everest","1899":"Lien","1900":"Trias Token","1901":"UniCrypt","1902":"Darwinia Network","1903":"Darwinia Commitment Token","1904":"Native Utility Token","1905":"Robonomics.network","1906":"PolkaFoundry","1907":"Rio DeFi","1908":"MANTRA DAO","1909":"Automata Network","1910":"Convex Finance","1911":"Indexed Finance","1912":"Venus","1913":"Dextrust","1914":"Syscoin","1915":"HUNT","1916":"Marlin","1917":"Trade Token X","1918":"Minter Network","1919":"HAPI","1920":"Cortex","1921":"SmartCredit Token","1922":"KIMCHI.finance","1923":"Liquidity Network","1924":"BambooDeFi","1925":"Gitcoin","1926":"Elrond","1927":"CVCoin","1928":"Raze Network","1929":"DistX","1930":"Ethereum Gold Project","1931":"Node Runners","1932":"Realio Network","1933":"Wrapped Gen-0 CryptoKitties","1934":"Harvest Finance","1935":"Props","1936":"OIN Finance","1937":"Earneo","1938":"Carrot","1939":"YFDAI.FINANCE","1940":"XSGD","1941":"RocketX","1942":"TONToken","1943":"Digital Reserve Currency","1944":"xDai","1945":"Celsius ","1946":"Swerve","1947":"Lepricon","1948":"SUN","1949":"DMD","1950":"YFFS Finance","1951":"OpenDAO","1952":"Zeedex","1953":"DAFI Protocol","1954":"Anchor Protocol","1955":"JackPool.finance","1956":"BNSD Finance","1957":"Aleph.im","1958":"JustLiquidity","1959":"Dracula Token","1960":"keyTango","1961":"DefiBox","1962":"Alchemy Pay","1963":"Sperax","1964":"MyNeighborAlice","1965":"Geeq","1966":"Swingby","1967":"Ferrum Network","1968":"MiL.k","1969":"Hedget","1970":"Krypton Galaxy","1971":"Pangolin","1972":"Base Protocol","1973":"Phala.Network","1974":"Digital Fantasy Sports","1975":"TotemFi","1976":"Nyan Finance","1977":"Tranchess","1978":"Tornado","1979":"FC Barcelona Fan Token","1980":"Waltonchain","1981":"Symbol","1982":"Peony","1983":"DeXe","1984":"Wing","1985":"Power Index Pool Token","1986":"ApeSwap Finance","1987":"Frontier","1988":"DeFi Pulse Index","1989":"Hamster","1990":"RSK Smart Bitcoin","1991":"New BitShares","1992":"PancakeSwap","1993":"Linear","1994":"Samurai","1995":"Wolves of Wall Street","1996":"Float Protocol","1997":"Beowulf","1998":"RIZON","1999":"OneSwap DAO Token","2000":"yTSLA Finance","2001":"Leverj","2002":"Rotten","2003":"pNetwork","2004":"NFT","2005":"OMG Network","2006":"Illuvium","2007":"Burger Swap","2008":"Covalent","2009":"Ethernity Chain","2010":"Newscrypto","2011":"Chainbing","2012":"cVault.finance","2013":"Lua Token","2014":"Bonded Finance","2015":"Stater","2016":"Eight Hours","2017":"Bitcoin Standard Hashrate Token","2018":"SunContract","2019":"USD Coin","2020":"FOX Token","2021":"Bidao","2022":"Netbox Coin","2023":"Mina","2024":"EnterCoin","2025":"Deflect","2026":"Metaverse Index","2027":"Venus ETH","2028":"PolygonQuickSwap","2029":"Step Finance","2030":"FM Gallery","2031":"Ellipsis","2032":"KuCoin Token","2033":"Hyve","2034":"PolygonUniswap","2035":"0x","2036":"Dawn Protocol","2037":"Axie Infinity","2038":"LooksRare","2039":"Moonbeam","2040":"A2DAO","2041":"Impossible Finance","2042":"LINK","2043":"CryptoBlades","2044":"OctoFi","2045":"Niftyx Protocol","2046":"Jade Protocol","2047":"BitGuild PLAT","2048":"Tellor","2049":"Livepeer","2050":"Celer","2051":"Wanchain","2052":"Synthetix Network Token","2053":"Anyswap","2054":"Polyient Games Governance Token","2055":"Azuki","2056":"Soverain","2057":"MX Token","2058":"DDEX","2059":"Keydonix","2060":"Hedera","2061":"dForce","2062":"Wrapped Basic CryptoKitties","2063":"Sparkle","2064":"Haven Protocol","2065":"ECC","2066":"Zenon","2067":"eBoost","2068":"Hegic","2069":"Klaytn","2070":"Phantasma Protocol","2071":"Commercium","2072":"0xBitcoin","2073":"DigixDAO","2074":"ZelCash","2075":"Throne","2076":"MicroBitcoin","2077":"Dogecoin","2078":"Nexus","2079":"Vertcoin","2080":"Stellite","2081":"ESBC","2082":"Ravencoin","2083":"TokenPocket","2084":"Bitcoin Private","2085":"Rocket Pool","2086":"Pigeoncoin","2087":"NeoWorld Cash","2088":"SUQA","2089":"Internet of Energy Network","2090":"SynLev","2091":"Pirate Chain","2092":"Semux","2093":"Grin","2094":"Vipstar Coin","2095":"Stakenet","2096":"Siacoin","2097":"StarLink","2098":"ABBC Coin","2099":"Stakeborg DAO","2100":"NFTify","2101":"Poolz Finance","2102":"Absolute","2103":"Holyheld","2104":"Value Liquidity","2105":"Jarvis Network","2106":"Bitcoin Diamond","2107":"ACRE","2108":"Thore Cash","2109":"InflationCoin","2110":"Giant","2111":"smARTOFGIVING","2112":"Doki Doki Finance","2113":"My DeFi Pet","2114":"Cyclone Protocol","2115":"Omnitude","2116":"XDNA","2117":"PRiVCY","2118":"XOVBank","2119":"BitCoin One","2120":"GridCoin","2121":"EVOS","2122":"Basis Gold Share","2123":"sUSD","2124":"Dimecoin","2125":"Bloktopia","2126":"Zero Exchange","2127":"Naka Bodhi Token","2128":"NavCoin","2129":"Shard","2130":"Nerva","2131":"VerusCoin","2132":"MidasProtocol","2133":"Labh Coin","2134":"SafeInsure","2135":"Crypto.com","2136":"SmartCash","2137":"Bridge Protocol","2138":"Mist","2139":"Kommunitas","2140":"MASQ","2141":"Amoveo","2142":"Ren","2143":"Ontology Gas","2144":"Parachute","2145":"Cazcoin","2146":"Cashaa","2147":"BridgeCoin","2148":"CMITCOIN","2149":"eXPerience Chain","2150":"LUXCoin","2151":"CyberFi Token","2152":"Edgeless","2153":"ProxyNode","2154":"Verge","2155":"Shift","2156":"DODO","2157":"The Abyss","2158":"TerraUSD","2159":"Hydrogen","2160":"Ribbon Finance","2161":"SingularDTV","2162":"ClinTex CTi","2163":"Opal","2164":"Skycoin","2165":"IDEX","2166":"bitcoin2network","2167":"Metaverse ETP","2168":"MarcoPolo Protocol","2169":"Aion","2170":"ImageCoin","2171":"Aragon","2172":"NEO","2173":"SENATE DAO","2174":"Terra Virtua Kolect","2175":"SpankChain","2176":"Mercury","2177":"YIELD App","2178":"AdultChain","2179":"Blockburn","2180":"GINcoin","2181":"NANJCOIN","2182":"Merebel","2183":"Project Pai","2184":"Kira Network","2185":"Dora Factory","2186":"Harmony","2187":"FantasyGold","2188":"ANON","2189":"GameCredits","2190":"Bonfida","2191":"Delphy","2192":"Ubiq","2193":"PIVX","2194":"win.win","2195":"Polychain Monsters","2196":"Venus BUSD","2197":"O3 Swap","2198":"Mirrored Tesla","2199":"Sentivate","2200":"OPCoinX","2201":"Blocknode","2202":"Bitcoin Confidential","2203":"Injective Protocol","2204":"Yeld Finance","2205":"Energi","2206":"Bitcoin Rhodium","2207":"VeThor Token","2208":"Alpha Finance Lab","2209":"Edgeware","2210":"Mithril Share","2211":"Monolith","2212":"Bitblocks","2213":"TOP","2214":"Masari","2215":"Iridium","2216":"STK","2217":"ROIyal Coin","2218":"Stably USD","2219":"Gemma Extending Tech","2220":"ZooKeeper","2221":"Gem Exchange And Trading","2222":"PlotX","2223":"Aegeus","2224":"TABOO TOKEN","2225":"Lightpaycoin","2226":"Digix Gold Token","2227":"Electra","2228":"Aencoin","2229":"CUTcoin","2230":"Cocos-BCX","2231":"Horizen","2232":"Kadena","2233":"SafeCapital","2234":"Zcash","2235":"Bitcoin Token","2236":"PegNet","2237":"Dash","2238":"Origin Protocol","2239":"Leverj Gluon","2240":"Defis","2241":"Veil","2242":"CREA","2243":"Qwertycoin","2244":"NIX Bridge Token","2245":"Aave","2246":"PAC Protocol","2247":"TerraKRW","2248":"Nyzo","2249":"Perlin","2250":"NewYorkCoin","2251":"CertiK","2252":"DeFiZap","2253":"88mph","2254":"Terra","2255":"Blocknet","2256":"Ergo","2257":"MITH Cash","2258":"Shopping","2259":"Algorand","2260":"pBTC35A","2261":"PoolTogether","2262":"SpookySwap","2263":"Ondori","2264":"UNION Protocol Governance Token","2265":"Tiger King","2266":"Keep Network","2267":"Cartesi","2268":"Badger DAO","2269":"2key.network","2270":"YFFII Finance","2271":"Verasity","2272":"Mirror Protocol","2273":"Threshold","2274":"Arweave","2275":"Compound","2276":"NuCypher","2277":"Monavale","2278":"Celo","2279":"DMM: Governance","2280":"BitTube","2281":"Ambrosus","2282":"Balancer","2283":"Vectorspace","2284":"NXM","2285":"Serum","2286":"TrustSwap","2287":"DFI.Money","2288":"Meta","2289":"Lido DAO Token","2290":"NerveNetwork","2291":"Meter","2292":"Ontology","2293":"bZx Protocol","2294":"Wilder World","2295":"Gameswap","2296":"FIO Protocol","2297":"Datamine","2298":"Meridian Network","2299":"Proton","2300":"Cream Finance","2301":"The Sandbox","2302":"Klima DAO","2303":"Dynamic Set Dollar","2304":"Bitgear","2305":"Hakka.Finance","2306":"GET Protocol","2307":"PROXI","2308":"Kusama","2309":"Open Predict Token","2310":"UniLayer","2311":"Xiotri","2312":"Rubic","2313":"Gravity Finance","2314":"Oddz","2315":"Secret","2316":"Vidya","2317":"Machine Xchange Coin","2318":"SushiSwap","2319":"yffi finance","2320":"Cirus Foundation","2321":"Chain Games","2322":"Fuse Network","2323":"Tokamak Network","2324":"Galaxy Heroes Coin","2325":"Pearl","2326":"Boosted Finance","2327":"SalmonSwap","2328":"RMRK","2329":"Perpetual Protocol","2330":"Unitrade","2331":"Nsure.Network","2332":"Golff","2333":"Pickle Finance","2334":"PowerPool","2335":"Spheroid Universe","2336":"Aavegotchi","2337":"The Graph","2338":"Zoracles","2339":"Bella Protocol","2340":"Flamingo","2341":"dHedge DAO","2342":"Energy Web Token","2343":"yieldfarming.insure","2344":"Dego Finance","2345":"SafeCoin","2346":"Rarible","2347":"APEcoin","2348":"Crust","2349":"Ethereum Meta","2350":"Sapphire","2351":"Mirrored Alibaba","2352":"PieDAO DOUGH v2","2353":"SmartKey","2354":"Firo","2355":"XDEFI Wallet","2356":"Lossless","2357":"Multiverse","2358":"Shiden Network","2359":"Songbird","2360":"Beyond Protocol","2361":"BabySwap","2362":"Decentral Games [new]","2363":"EPIK Prime","2364":"Rimbit","2365":"Arianee","2366":"OpenOcean","2367":"Merit Circle","2368":"Alitas","2369":"Neos Credits","2370":"Unifty","2371":"Impossible Decentralized Incubator Access","2372":"RChain","2373":"Sovryn","2374":"Dvision Network","2375":"Swarm","2376":"SifChain","2377":"XeniosCoin","2378":"Radix","2379":"Genesis Worlds","2380":"DeHub","2381":"Darma Cash","2382":"PlatON","2383":"Samoyedcoin","2384":"Spell Token","2385":"RichQUACK.com","2386":"DeversiFi","2387":"Saito","2388":"BitBall","2389":"Lotto","2390":"CryptoPlanes","2391":"Defina Finance","2392":"Deeper Network","2393":"Bloomzed Loyalty Club Ticket","2394":"StarTerra","2395":"Numbers Protocol","2396":"Alpha Quark Token","2397":"BENQI","2398":"DeFi Land","2399":"BEPRO Network","2400":"DOGGY","2401":"Epic Cash","2402":"Ooki Protocol","2403":"Polytrade","2404":"Velo","2405":"Acala Token","2406":"Osmosis","2407":"BitTorrent","2408":"World Mobile Token","2409":"STARSHIP","2410":"Alethea Artificial Liquid Intelligence Token","2411":"UnFederalReserve","2412":"Somnium Space Cubes","2413":"PERI Finance","2414":"Highstreet","2415":"YAM V2","2416":"IX Token","2417":"Astar","2418":"XCAD Network","2419":"Bone ShibaSwap","2420":"Venus DOT","2421":"Zano","2422":"Aston Villa Fan Token","2423":"Mirrored iShares Silver Trust","2424":"Mirrored Twitter","2425":"BASIC","2426":"Mirrored United States Oil Fund","2427":"Wrapped Velas","2428":"Victoria VR","2429":"DinoSwap","2430":"Decentral Games ICE","2431":"NFT Worlds","2432":"Conceal","2433":"Retreeb","2434":"BSCPancakeSwap","2435":"DeFi Yield Protocol","2436":"Quartz","2437":"Position Exchange","2438":"YAM V1","2439":"Alpine F1 Team Fan Token","2440":"Florin","2441":"Cronos","2442":"Counos X","2443":"Thetan Arena","2444":"Adappter Token","2445":"SHILL Token","2446":"Wrapped TRON","2447":"BitTorrent (new)","2448":"MetaPets","2449":"S.S. Lazio Fan Token","2450":"MContent","2451":"Etherisc","2452":"STEPN","2453":"SXP","2454":"DeGate","2455":"BSCTether","2456":"Goldfinch","2457":"ETHA Lend","2458":"Revolve Games","2459":"BSCBinance USD","2460":"1eco","2461":"Wrapped Cardano","2462":"Cardano","2463":"HI","2464":"GuildFi","2465":"EverRise","2466":"Ispolink","2467":"Hydra","2468":"Aurora","2469":"Toncoin","2470":"Polkacity","2471":"BSCUSD Coin","2472":"Everscale","2473":"GensoKishi Metaverse","2474":"Voxies","2475":"Formation Fi","2476":"Vader Protocol","2477":"League of Kingdoms Arena","2478":"ApeCoin","2479":"ThunderCore","2480":"Fruits","2481":"Chain","2482":"ZUM TOKEN","2483":"TRON","2484":"Genopets","2485":"ApolloX","2486":"BNB","2487":"Theta Network","2488":"Wonderland","2489":"MAGIC","2490":"Floki Inu","2491":"Zigcoin","2492":"Stargate Finance"},"slug":{"0":"color-platform","1":"enecuum","2":"cosmos","3":"zeuxcoin","4":"treelion","5":"kava","6":"slink","7":"truefi-token","8":"hyperexchange","9":"margix","10":"eos-force","11":"yottachain","12":"anchor","13":"pseed","14":"allianceblock","15":"tether-eurt","16":"0xtracker","17":"tepleton","18":"x-world-games","19":"electra-protocol","20":"7eleven","21":"everitoken","22":"yap-stone","23":"dash-cash","24":"hintchain","25":"azbit","26":"project-with","27":"chiliz","28":"deepcloud-ai","29":"mint-club","30":"pokeball","31":"sessia","32":"polkacover","33":"helena","34":"bitfex","35":"husd","36":"fei-protocol","37":"dad-chain","38":"meconcash","39":"gatechain-token","40":"helium","41":"augmint","42":"define","43":"mb8-coin","44":"swapcoinz","45":"chronocoin","46":"nitrogen","47":"totle","48":"d-community","49":"ash","50":"linka","51":"karura","52":"poketto","53":"tesra","54":"inlock","55":"instadapp","56":"xvix","57":"superrare","58":"cryptobosscoin","59":"coinhe-token","60":"flow","61":"xensor","62":"stake-dao","63":"fiatdex-gateway","64":"lightning-network","65":"zerion","66":"alp-coin","67":"nuo","68":"BTRST","69":"uma","70":"benepit-protocol","71":"ssv-network","72":"set-protocol","73":"litecoin","74":"typhoon-cash","75":"mimblewimblecoin","76":"razor","77":"snax","78":"pegasys","79":"chintai","80":"coinzo","81":"worbli","82":"market-protocol","83":"blockmason","84":"julswap","85":"sbch","86":"transledger","87":"punk","88":"kambria","89":"cere","90":"xpower","91":"gxchain","92":"eminer","93":"pledge-coin","94":"basis-cash","95":"insure","96":"simone","97":"celo-dollar","98":"game","99":"emogi-network","100":"insured-finance","101":"12ships","102":"lukki","103":"planet","104":"tokoin","105":"fibos","106":"cryptocean","107":"videocoin","108":"mixmarvel","109":"sealchain","110":"streamit-coin","111":"yobit","112":"cryptobonusmiles","113":"paxos-standard","114":"vidy","115":"vodi-x","116":"bidipass","117":"wavesbet","118":"splintershards","119":"axe","120":"sdefi","121":"qlink","122":"defisocks","123":"emirex","124":"amino","125":"nss","126":"sxag","127":"xaya","128":"valobit","129":"xcrypt","130":"sentinel-protocol","131":"en-tan-mo","132":"decentralized-vulnerability-platform","133":"seedify-fund","134":"chatcoin","135":"ankr","136":"cnns","137":"molecular-future","138":"artfinity","139":"autonio","140":"sirin-labs-token","141":"when-token","142":"safe-exchange-coin","143":"vnt-chain","144":"spiking","145":"gopower","146":"spiritswap","147":"locus-chain","148":"flowchain","149":"trias","150":"quickx-protocol","151":"chartex","152":"phv","153":"bolt","154":"pulsepad","155":"tokok","156":"inmax","157":"lambda","158":"birdchain","159":"yearn-finance-dot","160":"contents-protocol","161":"adx-net","162":"tribe","163":"p2p-global-network","164":"paragon","165":"asian-fintech","166":"schf","167":"sada","168":"sdash","169":"s4fe","170":"gmb","171":"zero-collateral","172":"online","173":"cyclub","174":"bitrue-coin","175":"lisk-machine-learning","176":"api3","177":"xriba","178":"almeela","179":"snapcoin","180":"fnb-protocol","181":"coineal-token","182":"buddy","183":"buysell","184":"cryptopay","185":"digg","186":"ioil","187":"nkn","188":"setc","189":"hashnet-biteco","190":"stackos","191":"everycoin","192":"izichain","193":"innovaminex","194":"quadrantprotocol","195":"q-dao-governance-token","196":"fetch","197":"mobox","198":"armor","199":"spiderdao","200":"matic-network","201":"dex","202":"hxro","203":"pton","204":"bonpay","205":"swftcoin","206":"levolution","207":"iou","208":"spacechain","209":"yfi-mobi","210":"parkingo","211":"blockstack","212":"fatcoin","213":"hercules","214":"coinus","215":"assemble-protocol","216":"decimated","217":"bosagora","218":"skychain","219":"fleta","220":"exrnchain","221":"internxt","222":"fountain","223":"eurocoin-token","224":"carry","225":"aidus-token","226":"shyft-network","227":"intellishare","228":"b2bx","229":"solve","230":"origo","231":"tchain","232":"cryptofranc","233":"membrana","234":"solana","235":"duckdaodime","236":"evedo","237":"drep","238":"tigercash","239":"skale-network","240":"pixel","241":"cova","242":"bodhi","243":"foam","244":"dusk-network","245":"clearpool","246":"pendle","247":"turtlecoin","248":"binemon","249":"sharder","250":"herbalist-token","251":"the-currency-analytics","252":"upfiring","253":"tratok","254":"akash-network","255":"numeraire","256":"unicly-cryptopunks-collection","257":"derace","258":"digitalbits","259":"titan-coin","260":"probit","261":"gomics","262":"gnosis-gno","263":"playkey","264":"polkabridge","265":"wrapped-bitcoin","266":"metisdao","267":"o3swap","268":"lattice-token","269":"coinvest","270":"cherry-swap","271":"zeronet","272":"bisq","273":"peos","274":"band-protocol","275":"telos","276":"snowswap","277":"gods-unchained","278":"lgcy-network","279":"hyper-speed-network","280":"binance-usd","281":"staked","282":"kun","283":"alpaca-finance","284":"crabada","285":"curate","286":"paid-network","287":"tidal-finance","288":"wall-street-games","289":"krios","290":"venus-reward-token","291":"dragon-token","292":"ceek-vr","293":"biconomy","294":"truefeedback","295":"pax-gold","296":"abra","297":"hive","298":"metamoneymarket","299":"ariva","300":"elitium","301":"agrello-delta","302":"alphawallet","303":"ambo","304":"argent","305":"betoken","306":"liquid-apps","307":"otocash","308":"colletrix","309":"howdoo","310":"amond","311":"bobs-repair","312":"business-credit-substitute","313":"bitpie","314":"clearpoll","315":"button-wallet","316":"cobo-wallet","317":"blockzerolabs","318":"ilink","319":"polis","320":"nftb","321":"sincity-token","322":"blockchain-quotations-index-token","323":"coinbase-wallet","324":"key","325":"fetch-wallet","326":"imtoken","327":"roobee","328":"solfarm","329":"itrx","330":"defi-saver","331":"dexwallet","332":"condominium","333":"sxmr","334":"solanium","335":"centercoin","336":"lemochain","337":"beam","338":"salus","339":"solrise-finance","340":"seth","341":"tierion","342":"dehive","343":"rhenium","344":"block-array","345":"sxrp","346":"alpha-token","347":"ixrp","348":"seos","349":"olyseum","350":"manchester-city-fan-token","351":"jigstack","352":"blackmoon","353":"bibox-token","354":"iada","355":"icex","356":"banano","357":"idefi","358":"contracoin","359":"keeperdao","360":"modefi","361":"ibch","362":"ichi","363":"b-protocol","364":"sumokoin","365":"purex","366":"seur","367":"idash","368":"nnb-token","369":"nix","370":"invacio","371":"bux-token","372":"mirrored-netflix","373":"spendcoin","374":"sbnb","375":"smartlands","376":"cubiex","377":"reddcoin","378":"bitcoin-cash-abc-2","379":"labs-group","380":"cajutel","381":"vibe","382":"iltc","383":"torum","384":"engine","385":"lamden","386":"sltc","387":"opacity","388":"beta-finance","389":"dew","390":"soil","391":"vai","392":"sxtz","393":"hypercash","394":"zilla","395":"menapay","396":"polkadex","397":"seer","398":"maidsafecoin","399":"cashbet-coin","400":"sav3token","401":"unobtanium","402":"bytom","403":"ibtc-synthetix","404":"cloudbric","405":"ink","406":"komodo","407":"blockcloud","408":"ieth","409":"insureum","410":"zeepin","411":"ieos","412":"saud","413":"zippie","414":"decred","415":"mines-of-dalarnia","416":"ietc","417":"intelligent-investment-chain","418":"melon","419":"free-coin","420":"redfox-labs","421":"ixmr","422":"internationalcryptox","423":"quasarcoin","424":"medibloc","425":"ixtz","426":"funfair","427":"internet-computer","428":"opulous","429":"tenx","430":"1337coin","431":"spindle","432":"onotoken","433":"ibnb","434":"bifrost-bnc","435":"dogs-of-elon","436":"beekan","437":"gas","438":"emercoin","439":"copytrack","440":"burst","441":"strx","442":"euno","443":"dignity","444":"odyssey","445":"berry-data","446":"themis","447":"cononchain","448":"experience-token","449":"ether-zero","450":"namecoin","451":"mirrored-microsoft","452":"invictus-hyperion-fund","453":"revv","454":"peakdefi","455":"only1","456":"hdac","457":"primas","458":"gamefi","459":"vikkytoken","460":"monacoin","461":"dacc","462":"paytomat","463":"time-new-bank","464":"shipchain","465":"effect-ai","466":"zip","467":"esports-token","468":"everus","469":"venus-xvs","470":"zen-protocol","471":"yuan-chain-coin","472":"medishares","473":"phoenix-global","474":"venus-usdc","475":"moonswap","476":"mothership","477":"step-hero","478":"wagerr","479":"connect-coin","480":"gridplus","481":"stockchain","482":"owl-token-stealthswap","483":"tomochain","484":"scorum-coins","485":"qiibee","486":"reflect-finance","487":"eunomia","488":"jiyo","489":"moeda-loyalty-points","490":"request","491":"sun-token","492":"bitcoin-atom","493":"divi","494":"content-value-network","495":"hurify","496":"faircoin","497":"digitalnote","498":"bitcore","499":"safe","500":"primecoin","501":"aeternity","502":"snetwork","503":"klever","504":"blitzpredict","505":"hermez-network","506":"sashimi","507":"steem-dollars","508":"maple","509":"eligma-token","510":"chronotech","511":"valuechain","512":"graviocoin","513":"stealth","514":"bitsum","515":"shield-xsh","516":"pivot-token","517":"kiltprotocol","518":"adapad","519":"fantom","520":"minexcoin","521":"tokenpay","522":"faceter","523":"vite","524":"vempire-ddao","525":"litecoin-cash","526":"pura","527":"evencoin","528":"bee-token","529":"nebulas-token","530":"gochain","531":"domraider","532":"router-protocol","533":"flux","534":"experty","535":"cpchain","536":"gulden","537":"mytoken","538":"monero-classic","539":"brahmaos","540":"morpheus-labs","541":"narrative","542":"u-network","543":"paraswap","544":"polybius","545":"sp8de","546":"salt","547":"herocoin","548":"gowithmi","549":"yooshi","550":"derivadao","551":"poet","552":"cybervein","553":"lendingblock","554":"speed-mining-service","555":"tosdis","556":"stratis","557":"particl","558":"nash-exchange","559":"starbase","560":"the-midas-touch-gold","561":"origintrail","562":"opium","563":"expanse","564":"sentinel","565":"blockport","566":"dao-casino","567":"defi-bids","568":"eosdac","569":"no-bs-crypto","570":"polkastarter","571":"rentberry","572":"ravencoin-classic","573":"venus-btc","574":"reflexer-ungovernance-token","575":"asura-coin","576":"hycon","577":"steem","578":"moneynet","579":"venus-bch","580":"sharpe-platform-token","581":"albos","582":"waletoken","583":"archer-dao-governance-token","584":"biswap","585":"t-bitcoin","586":"venus-dai","587":"babb","588":"biopassport-token","589":"xmax","590":"onus","591":"revomon","592":"trust","593":"vivid-coin","594":"bridge-mutual","595":"refereum","596":"library-credit","597":"coinex-token","598":"vimworld","599":"genesis-vision","600":"thekey","601":"sakura-bloom","602":"nano","603":"iqcash","604":"constitutiondao","605":"groestlcoin","606":"lightning-bitcoin","607":"more-coin","608":"kalkulus","609":"radio-caca","610":"gamezone","611":"drep-new","612":"qash","613":"indahash","614":"proxeus","615":"stellar","616":"unmarshal","617":"doge-dash","618":"venus-usdt","619":"furucombo","620":"aurory","621":"bottos","622":"bytecoin-bcn","623":"trust-wallet-token","624":"trinity-network-credit","625":"globalvillage-ecosystem","626":"name-changing-token","627":"bitcoin-file","628":"irisnet","629":"t-os","630":"signal-token","631":"yui-token","632":"wagyuswap","633":"futureswap","634":"sentinel-chain","635":"maps","636":"lockchain","637":"horuspay","638":"electroneum","639":"biotron","640":"nxt","641":"convergence","642":"stronghold-token","643":"electrifyasia","644":"nerves","645":"lykke","646":"bomb","647":"dmarket","648":"viacoin","649":"ion","650":"wootrade","651":"sociall","652":"atlantis-blue-digital-token","653":"metahero","654":"nebula-ai","655":"decentralized-machine-learning","656":"distributed-credit-chain","657":"paypie","658":"ardor","659":"swissborg","660":"jesus-coin","661":"ternio","662":"unus-sed-leo","663":"sportyco","664":"centrifuge","665":"pundi-x-nem","666":"edu-coin","667":"dnotes","668":"influence-chain","669":"arcblock","670":"ignis","671":"aeron","672":"dmscript","673":"betterbetting","674":"the-champcoin","675":"quickswap","676":"dent","677":"arpa-chain","678":"ionchain","679":"oax","680":"oasis-network","681":"maker","682":"maro","683":"blockchain-monster-hunt","684":"rublix","685":"compound-uniswap","686":"daex","687":"mktcoin","688":"gemini-dollar","689":"valuto","690":"ftx-token","691":"truechain","692":"endorsit","693":"reef","694":"penta","695":"ramp","696":"data-exchange","697":"compound-usdt","698":"ultra","699":"dascoin","700":"font","701":"peercoin","702":"aave-link","703":"cellframe","704":"coinpoker","705":"blocktrade-token","706":"bigbom","707":"blockmesh","708":"kind-ads-token","709":"bitcny","710":"polkamarkets","711":"transcodium","712":"titanswap","713":"ryo-currency","714":"joe","715":"boutspro","716":"tera","717":"atn","718":"business-credit-alliance-chain","719":"storm","720":"aave-eth","721":"linda","722":"bezant","723":"magic-internet-money","724":"goldmint","725":"helbiz","726":"bodhi-eth","727":"kuende","728":"buck-hath-coin","729":"cardstack","730":"aave-tusd","731":"freeway-token","732":"digifinextoken","733":"waykichain","734":"futurocoin","735":"patron","736":"docademic","737":"aave-dai","738":"aave-mkr","739":"aave-bat","740":"fuzex","741":"aave-usdt","742":"oraichain-token","743":"parkgene","744":"quanta-utility-token","745":"presearch","746":"bitex-global-xbx-coin","747":"valor-token","748":"oduwa","749":"civic","750":"stafi","751":"elysian","752":"deso","753":"alluva","754":"dkargo","755":"bnktothefuture","756":"silent-notary","757":"cybereits","758":"popchain","759":"eosdt","760":"oneledger","761":"aave-susd","762":"aurora","763":"crycash","764":"educare","765":"qredo","766":"footballcoin","767":"skrumble-network","768":"aave-mana","769":"kebab-token","770":"lunyr","771":"pepe-cash","772":"promotion-coin","773":"raven-protocol","774":"debitum-network","775":"aeon","776":"soarcoin","777":"deepbrain-chain","778":"crpt","779":"echoin","780":"ucash","781":"aave-knc","782":"telcoin","783":"vindax-coin","784":"olxa","785":"visionx","786":"boringdao","787":"shekel","788":"aave-zrx","789":"blind-boxes","790":"kava-swap","791":"pixie-coin","792":"pal-network","793":"pitiscoin","794":"nexo","795":"proton-token","796":"arbitrage","797":"kora-network-token","798":"kryll","799":"e-dinar-coin","800":"nasdacoin","801":"croat","802":"guppy","803":"bqt","804":"huobi-token","805":"amp","806":"jury-online-token","807":"friends","808":"cosmo-coin","809":"modultrade","810":"blue-whale-token","811":"aave-busd","812":"forty-seven-bank","813":"wrapped-ncg","814":"empowr-coin","815":"exosis","816":"bitcoin-bep2","817":"temco","818":"0xcert","819":"prometeus","820":"aave-rep","821":"aave-lend","822":"dav-coin","823":"cashberry-coin","824":"hitchain","825":"pillar","826":"pumapay","827":"local-coin-swap","828":"leocoin","829":"okb","830":"mir-coin","831":"timicoin","832":"mango-markets","833":"honest","834":"consentium","835":"bitrewards","836":"atc-coin","837":"theta-fuel","838":"cudos","839":"airbloc","840":"dxchain-token","841":"apis","842":"ubex","843":"logiscoin","844":"insolar","845":"davinci-coin","846":"coldstack","847":"nftx-hashmasks-index","848":"olive","849":"oceanex-token","850":"ark","851":"bitceo","852":"igtoken","853":"fsw-token","854":"ailink-token","855":"index-cooperative","856":"chex","857":"tael","858":"marsx","859":"xmon","860":"kwhcoin","861":"six-domain-chain","862":"carinet","863":"farmatrust","864":"niobio-cash","865":"mgc-token","866":"knoxstertoken","867":"blocery","868":"butterfly-protocol-2","869":"kin","870":"trueflip","871":"govi","872":"curio-governance","873":"hold","874":"wpp-token","875":"bumo","876":"heartbout","877":"auto","878":"carbon","879":"seen","880":"ti-value","881":"district0x","882":"gems-protocol","883":"eosblack","884":"quantstamp","885":"bitmart-token","886":"global-social-chain","887":"dws","888":"formosa-financial","889":"hashcoin","890":"contentos","891":"v-dimension","892":"helpico","893":"gourmet-galaxy","894":"polkadomain","895":"zeusshield","896":"dprating","897":"mybit","898":"ice-rock-mining","899":"bitcoinote","900":"webcoin","901":"relex","902":"mex","903":"remme","904":"aave-usdc","905":"yaxis","906":"alphacat","907":"you-coin","908":"aave-wbtc","909":"clearcoin","910":"starta","911":"gsenetwork","912":"moneytoken","913":"rotharium","914":"rogue-west","915":"apyswap","916":"ugchain","917":"uttoken","918":"proximax","919":"oneroot-network","920":"linkeye","921":"etherparty","922":"showhand","923":"beyond-finance","924":"dogebonk","925":"lendroid-support-token","926":"iconomi","927":"whale","928":"airswap","929":"medical-chain","930":"chronologic","931":"zeroswap","932":"gohelpfund","933":"metal","934":"beefy-finance","935":"eva-cash","936":"weshow-token","937":"novacoin","938":"otcbtc-token","939":"couchain","940":"razor-network","941":"bitcoin-sv","942":"dreamteam-token","943":"w3coin","944":"earth-token","945":"datx","946":"stox","947":"neumark","948":"moviebloc","949":"monero-v","950":"decent","951":"dragon-coins","952":"zmine","953":"graphlinq-protocol","954":"six","955":"paycent","956":"robotina","957":"bobo-cash","958":"unvest","959":"daps-token","960":"metronome","961":"humanscape","962":"nuggets","963":"smartshare","964":"eristica","965":"non-fungible-yearn","966":"clover","967":"verime","968":"glitch","969":"measurable-data-token","970":"pibble","971":"carat","972":"sidus-heroes-sidus-token","973":"daneel","974":"insights-network","975":"fractal","976":"cybermiles","977":"giga","978":"dextools","979":"sdchain","980":"wixlar","981":"metadium","982":"adamant-messenger","983":"ela-coin","984":"playdapp","985":"yearn-secure","986":"waifu-token","987":"metamorph","988":"icechain","989":"vegawallet-token","990":"naga","991":"hashsbx","992":"amo-coin","993":"lto-network","994":"fortuna","995":"akropolis","996":"rainicorn","997":"london","998":"next-coin","999":"fluz-fluz","1000":"reserve-rights","1001":"te-food","1002":"efinity","1003":"ink-protocol","1004":"frax-share","1005":"lync-network","1006":"soar-fi","1007":"8pay","1008":"ormeus-coin","1009":"atlas-protocol","1010":"hashgard","1011":"blockchain-certified-data-token","1012":"darcio-ecosystem-coin","1013":"hyperquant","1014":"ilcoin","1015":"orion-money","1016":"tolar","1017":"etheera","1018":"safe-haven","1019":"zilliqa","1020":"onx-finance","1021":"substratum","1022":"linktoken","1023":"lition","1024":"bhpcash","1025":"unibright","1026":"eden","1027":"axpire","1028":"big-bang-game-coin","1029":"duck-dao","1030":"blockpass","1031":"newton","1032":"streamr-datacoin","1033":"trinity-protocol","1034":"xyo","1035":"radium","1036":"playgame-token","1037":"canyacoin","1038":"max-property-group","1039":"veriblock","1040":"scanet-world-coin","1041":"metahash","1042":"decoin","1043":"serve","1044":"nectar","1045":"icon","1046":"merculet","1047":"sake-token","1048":"bitdao","1049":"hyperion","1050":"file-coin","1051":"iht-real-estate-protocol","1052":"benz","1053":"chain-guardians","1054":"bitcoin-gold","1055":"ost","1056":"matryx","1057":"aidcoin","1058":"adventure-gold","1059":"bitcoin-interest","1060":"multivac","1061":"atari-token","1062":"oro","1063":"tether","1064":"callisto-network","1065":"auctus","1066":"magic-cube-coin","1067":"aladdin","1068":"daoventures","1069":"jarvis","1070":"dragonchain","1071":"loopring-neo","1072":"holo","1073":"incent","1074":"gala","1075":"content-neutrality-network","1076":"covesting","1077":"atonomi","1078":"volt","1079":"fundamenta","1080":"maecenas","1081":"avinoc","1082":"winstars-live","1083":"palletone","1084":"coinsuper-ecosystem-network","1085":"brz","1086":"wink","1087":"gamee","1088":"argoapp","1089":"unimex-network","1090":"forefront","1091":"baasid","1092":"ethereum","1093":"nper","1094":"coti","1095":"boom","1096":"webflix-token","1097":"utrust","1098":"nem","1099":"santiment","1100":"elastos","1101":"boba-network","1102":"datawallet","1103":"neural-protocol","1104":"gonetwork","1105":"loom-network","1106":"chromia","1107":"nucleus-vision","1108":"omni","1109":"katalyo","1110":"ivy","1111":"sf-capital","1112":"chainx","1113":"lympo","1114":"lith-token","1115":"public-mint","1116":"sarcophagus","1117":"cryptaur","1118":"birake","1119":"caspian","1120":"function-x","1121":"yee","1122":"kleros","1123":"ulord","1124":"poa-network","1125":"decentraweb","1126":"agoras-tokens","1127":"thingschain","1128":"cwv-chain","1129":"unification","1130":"propy","1131":"html-coin","1132":"neurochain","1133":"cybermusic","1134":"fivebalance","1135":"splyt","1136":"gomining-token","1137":"trueusd","1138":"smartup","1139":"carvertical","1140":"rage-fan","1141":"safepal","1142":"boltt-coin","1143":"dapp-token","1144":"bitcoin-cash","1145":"mahadao","1146":"zclassic","1147":"dogey-inu","1148":"kangal","1149":"boat-pilot-token","1150":"qredit","1151":"pluton","1152":"ugas","1153":"iterationsyndicate","1154":"efforce","1155":"apy-finance","1156":"hiveterminal-token","1157":"sylo","1158":"quantum-resistant-ledger","1159":"massgrid","1160":"dexter-g","1161":"apex","1162":"pria","1163":"insurepal","1164":"power-ledger","1165":"lisk","1166":"hedgetrade","1167":"wemix","1168":"nos","1169":"banyan-network","1170":"moss-coin","1171":"bluzelle","1172":"shinechain","1173":"cgc-token","1174":"cargox","1175":"loki","1176":"enjin-coin","1177":"crdnetwork","1178":"ubix-network","1179":"collateral-pay","1180":"vexanium","1181":"bitcapitalvendor","1182":"ddkoin","1183":"dadi","1184":"credits","1185":"commerceblock","1186":"plair","1187":"kcash","1188":"tcash","1189":"owndata","1190":"flash","1191":"zero-utility-token","1192":"warp-finance","1193":"hydro-protocol","1194":"eos","1195":"loopring","1196":"mobilego","1197":"vechain","1198":"polymath-network","1199":"ripio-credit-network","1200":"saffron-finance","1201":"apr-coin","1202":"barnbridge","1203":"tapmydata","1204":"chonk","1205":"nuco-cloud","1206":"optionroom","1207":"uniswap","1208":"rlc","1209":"flo","1210":"storj","1211":"hoqu","1212":"umbrella-network","1213":"thunder-token","1214":"moonriver","1215":"qtum","1216":"fera","1217":"stakerdao","1218":"bitto","1219":"consensus","1220":"monero","1221":"yeed","1222":"orbs","1223":"vetri","1224":"centauri","1225":"wollo","1226":"daostack","1227":"zel","1228":"tezos","1229":"dether","1230":"torn","1231":"dero","1232":"fire-lotto","1233":"humaniq","1234":"contentbox","1235":"populous","1236":"box-token","1237":"usdc-b","1238":"waves","1239":"faraland","1240":"selfkey","1241":"bilaxy-token","1242":"coinmetro-token","1243":"adshares","1244":"wirex-token","1245":"endor-protocol","1246":"budbo","1247":"e-money-coin","1248":"dacsee","1249":"blank-wallet","1250":"neutrino-system-base-token","1251":"sora-validator-token","1252":"noia-network","1253":"mileverse","1254":"iostoken","1255":"crowd-machine","1256":"puregold-token","1257":"scryinfo","1258":"bitshares","1259":"zper","1260":"duo-network-token","1261":"ecomi","1262":"digitex-futures","1263":"red-pulse","1264":"silverway","1265":"quarkchain","1266":"likecoin","1267":"ruff","1268":"counterparty","1269":"appcoins","1270":"usechain-token","1271":"ampleforth","1272":"wibson","1273":"vulcan-forged-pyr","1274":"white-standard","1275":"genaro-network","1276":"vegaprotocol","1277":"qunqun","1278":"solarcoin","1279":"translateme-network-token","1280":"bitcoin","1281":"bhp-coin","1282":"ternoa","1283":"masternet","1284":"aergo","1285":"tbtc","1286":"swapfolio","1287":"sc-corinthians-fan-token","1288":"raiden-network-token","1289":"blockv","1290":"arionum","1291":"adbank","1292":"odem","1293":"feathercoin","1294":"open-platform","1295":"viberate","1296":"devery","1297":"switcheo","1298":"offshift","1299":"stone","1300":"hackenai","1301":"cryptrust","1302":"gny","1303":"zerobank","1304":"3dcoin","1305":"touchcon","1306":"heronode","1307":"tokenbox","1308":"essentia","1309":"factom","1310":"napoleonx","1311":"finxflo","1312":"polyswarm","1313":"egretia","1314":"vinchain","1315":"dao-maker","1316":"tgame","1317":"aioz-network","1318":"dock","1319":"binaryx","1320":"niobium-coin","1321":"jibrel-network","1322":"xinfin-network","1323":"deri-protocol","1324":"bread","1325":"araw","1326":"amon","1327":"litex","1328":"neurotoken","1329":"vvs-finance","1330":"life-crypto","1331":"arepacoin","1332":"zimbocash","1333":"restart-energy-mwat","1334":"unlimitedip","1335":"high-performance-blockchain","1336":"meetone","1337":"coinfi","1338":"whiteheart","1339":"ufo-gaming","1340":"lithium","1341":"fabric-token","1342":"everipedia","1343":"einsteinium","1344":"bitkan","1345":"epik-protocol","1346":"engagement-token","1347":"mainframe","1348":"rangers-protocol","1349":"bigone-token","1350":"naviaddress","1351":"tripio","1352":"adtoken","1353":"own","1354":"syncfab","1355":"ors-group","1356":"origin-sport","1357":"nuls","1358":"encrypgen","1359":"prosper","1360":"rightmesh","1361":"mooncoin","1362":"everex","1363":"electronic-energy-coin","1364":"mobius","1365":"litentry","1366":"gifto","1367":"bankex","1368":"astroswap","1369":"signum","1370":"rate3","1371":"matrix-ai-network","1372":"monetha","1373":"chainlink","1374":"wings","1375":"arbidex","1376":"smartmesh","1377":"nubits","1378":"aelf","1379":"decentraland","1380":"red","1381":"drp-utility","1382":"hodooi","1383":"tnc-coin","1384":"smart-mfg","1385":"tranche-finance","1386":"stratos","1387":"axion","1388":"bitball-treasure","1389":"circuits-of-value","1390":"greenmed","1391":"status","1392":"v-systems","1393":"ubique-chain-of-things","1394":"peculium","1395":"capdaxtoken","1396":"aragon-court","1397":"dharma","1398":"empty-set-dollar","1399":"cache-gold","1400":"carlive-chain","1401":"cindicator","1402":"stasis-euro","1403":"boscoin","1404":"universa","1405":"amlt","1406":"sharpay","1407":"rai","1408":"dydx","1409":"0chain","1410":"ndex","1411":"youlive-coin","1412":"indorse-token","1413":"basic-attention-token","1414":"senso","1415":"aditus","1416":"maincoin","1417":"bulwark","1418":"bitpanda-ecosystem-token","1419":"thingsoperatingsystem","1420":"tokenclub","1421":"wepower","1422":"swarm-fund","1423":"quant","1424":"stakinglab","1425":"b20","1426":"tent","1427":"superfarm","1428":"tokocrypto","1429":"persistence","1430":"dentacoin","1431":"sonm","1432":"iot-chain","1433":"bora","1434":"rari-governance-token","1435":"atomic-wallet-coin","1436":"tokenstars","1437":"platoncoin","1438":"neblio","1439":"master-contract-token","1440":"singularitydao","1441":"p-matic-network","1442":"steth","1443":"wbnb","1444":"incodium","1445":"hellogold","1446":"privatix","1447":"muzika","1448":"zkswap","1449":"tokenomy","1450":"1world","1451":"hathor","1452":"firmachain","1453":"snowgem","1454":"safemoon-inu","1455":"pikciochain","1456":"ixledger","1457":"sureremit","1458":"nestree","1459":"valuecybertoken","1460":"fanstime","1461":"unilend","1462":"pundi-x","1463":"traceability-chain","1464":"gmcoin","1465":"goose-finance","1466":"nftx","1467":"venus-bnb","1468":"boolberry","1469":"achain","1470":"cobak-token","1471":"decent-bet","1472":"cloakcoin","1473":"iqeon","1474":"agora","1475":"ecash","1476":"fintrux-network","1477":"banca","1478":"uchain","1479":"the-tokenized-bitcoin","1480":"mithril","1481":"ethos","1482":"cumrocket","1483":"taraxa","1484":"unido","1485":"conflux-network","1486":"iota","1487":"yoyow","1488":"bitnation","1489":"thrive-token","1490":"global-currency-reserve","1491":"newstoken","1492":"zrcoin","1493":"phi-token","1494":"iotex","1495":"phoenix-global-new","1496":"sbtc","1497":"one-cash","1498":"midas-dollar","1499":"kattana","1500":"blox","1501":"mysterium","1502":"rapids","1503":"enigma","1504":"atlant","1505":"cafeswap-token","1506":"diamond-platform-token","1507":"custody-token","1508":"structured","1509":"egoras","1510":"huobi-btc","1511":"nervos-network","1512":"bispex","1513":"golem-network-tokens","1514":"youcash","1515":"vesper","1516":"dex-ag","1517":"ormeus-ecosystem","1518":"mdex","1519":"decentral-games","1520":"vinci","1521":"vnx","1522":"capital-x-cell","1523":"suterusu","1524":"exmr","1525":"localcryptos","1526":"hopr","1527":"liquity","1528":"dai","1529":"defichain","1530":"multi-collateral-dai","1531":"sharetoken","1532":"venus-ltc","1533":"safex-token","1534":"blockium","1535":"connext","1536":"chi-gastoken","1537":"life","1538":"fortem-capital","1539":"zb","1540":"ptokens-btc","1541":"ripple","1542":"nord-finance","1543":"aphelion","1544":"btcpay","1545":"unicly-mystic-axies-collection","1546":"vnx-exchange","1547":"fusion","1548":"mvl","1549":"zebi","1550":"filenet","1551":"star-atlas","1552":"medium","1553":"lendf","1554":"sablier","1555":"bao-finance","1556":"aidos-kuneen","1557":"rally","1558":"vsxp","1559":"ac-milan-fan-token","1560":"cotrader","1561":"star-atlas-polis","1562":"mogu","1563":"portis","1564":"alchemix","1565":"komet","1566":"conun","1567":"xdef-finance","1568":"cratos","1569":"rakon","1570":"npcoin","1571":"betprotocol","1572":"orchid","1573":"kickico","1574":"sparkswap","1575":"rsk","1576":"ultralpha","1577":"unistake","1578":"infinitecoin","1579":"x-cash","1580":"davion","1581":"coinloan","1582":"zynecoin","1583":"royale-finance","1584":"coinsbit-toke","1585":"enzo","1586":"zbg-token","1587":"usdx-stablecoin","1588":"sanx","1589":"tachyon-protocol","1590":"internet-node-token","1591":"road","1592":"bmultiplier","1593":"tokenlon-network-token","1594":"complete-new-commerce-chain","1595":"immutable-x","1596":"travala","1597":"antimatter","1598":"audius","1599":"one-share","1600":"epns","1601":"the-transfer-token","1602":"mirrored-proshares-vix-short-term-futures-etf","1603":"revolution-populi","1604":"now-token","1605":"well-token","1606":"orient-walt","1607":"pizza","1608":"bounce","1609":"3x-short-bitcoin-token","1610":"lukso","1611":"ethereum-name-service","1612":"visor-finance","1613":"3x-long-bitcoin-token","1614":"neutrino-dollar","1615":"gocrypto-token","1616":"venus-link","1617":"insurace","1618":"troy","1619":"centric-swap","1620":"nexalt","1621":"tether-gold","1622":"data","1623":"curve","1624":"math","1625":"keystone-of-opportunity-knowledge","1626":"wazirx","1627":"arcs","1628":"big-data-protocol","1629":"idle","1630":"lock","1631":"scprime","1632":"torque","1633":"inverse-finance","1634":"spartan-protocol","1635":"nft-index","1636":"usdx-kava","1637":"k21","1638":"hydro","1639":"orbit-chain","1640":"ptokens","1641":"hoge-finance","1642":"thorstarter","1643":"hyperdao","1644":"weth","1645":"joint-ventures","1646":"apm-coin","1647":"degenerator-meme","1648":"swarm-markets","1649":"mars","1650":"handshake","1651":"synthetify","1652":"perth-mint-gold-token","1653":"validity","1654":"keep3rv1","1655":"rif-token","1656":"alien-worlds","1657":"gravitycoin","1658":"tokemak","1659":"incognito","1660":"astrotools","1661":"ttc-protocol","1662":"mirrored-amazon","1663":"coin-artist","1664":"stpt","1665":"observer","1666":"sxau","1667":"libra-credit","1668":"elixxir","1669":"unizen","1670":"axel","1671":"oxbull-tech","1672":"rupiah-token","1673":"unifi-protocol-dao","1674":"xcredit","1675":"urus","1676":"exeedme","1677":"we-own","1678":"topbidder","1679":"merklex","1680":"muse","1681":"parallelcoin","1682":"radix","1683":"shadows","1684":"sparkpoint","1685":"tenset","1686":"kobocoin","1687":"digibyte","1688":"ethereum-yield","1689":"project-coin","1690":"bondly","1691":"deus-finance","1692":"kylin","1693":"genesis-shards","1694":"bancor","1695":"woof","1696":"sync-network","1697":"p-usd-coin","1698":"p-chainlink","1699":"falcon-project","1700":"global-coin-research","1701":"vabble","1702":"easyfi","1703":"ocean-protocol","1704":"1inch","1705":"mmocoin","1706":"parsiq","1707":"paypolitan-token","1708":"paint","1709":"bscpad","1710":"p-aave","1711":"californium","1712":"ethereum-stake","1713":"crust-shadow","1714":"axpr-token","1715":"receive-access-ecosystem","1716":"exnetwork-token","1717":"iconiq-lab-token","1718":"trustlines","1719":"deor","1720":"acute-angle-cloud","1721":"frax","1722":"polkalokr","1723":"huobi-pool-token","1724":"casperlabs","1725":"yield","1726":"stakedzen","1727":"deapcoin","1728":"just-stablecoin","1729":"just","1730":"kardiachain","1731":"uquid_coin","1732":"pchain","1733":"forceprotocol","1734":"seele","1735":"revain","1736":"pundix-new","1737":"minty-art","1738":"surf","1739":"paris-saint-germain-fan-token","1740":"iocoin","1741":"kishu-inu","1742":"juventus-fan-token","1743":"switch","1744":"flexacoin","1745":"compound-dai","1746":"compound-usd-coin","1747":"compound-ether","1748":"thorchain","1749":"bifrost","1750":"gold-poker","1751":"ruler-protocol","1752":"totem","1753":"yvs-finance","1754":"small-love-potion","1755":"wax","1756":"constellation","1757":"ethereum-classic","1758":"finnexus","1759":"render-token","1760":"creditcoin","1761":"yield-guild-games","1762":"peanut","1763":"renbtc","1764":"ampleforth-governance-token","1765":"olympus","1766":"stormx","1767":"boson-protocol","1768":"super-zero","1769":"ring-x-platform","1770":"hex","1771":"kine","1772":"waykichain-governance-coin","1773":"all-sports","1774":"kyber-network","1775":"morpheus-network","1776":"whole-network","1777":"lcx","1778":"treecle","1779":"robonomics-web-services","1780":"bitcherry","1781":"btse","1782":"crowns","1783":"radicle","1784":"smoothy","1785":"xend-finance","1786":"popsicle-finance","1787":"unicly-hashmasks-collection","1788":"bakerytoken","1789":"chads-vc","1790":"avalanche","1791":"hoo-token","1792":"nest-protocol","1793":"portion","1794":"orion-protocol","1795":"1-up","1796":"dogelon","1797":"multiplier","1798":"decentr","1799":"mcdex","1800":"alchemist","1801":"dfyn-network","1802":"plutusdefi","1803":"yearn-finance","1804":"lunchmoney","1805":"wrapped-nxm","1806":"liquidity-dividends-protocol","1807":"shiba-inu","1808":"ig-gold","1809":"republicnote","1810":"method-finance","1811":"bitbns","1812":"wom-protocol","1813":"tendies","1814":"antiample","1815":"aga","1816":"fear-nfts","1817":"libertas-token","1818":"sport-and-leisure","1819":"mask-network","1820":"suku","1821":"aax-token","1822":"sora","1823":"mobilecoin","1824":"spore-finance","1825":"vanilla-network","1826":"apenft","1827":"dos-network","1828":"balpha","1829":"defipie","1830":"augur","1831":"origin-dollar","1832":"zap","1833":"truebit","1834":"dia-data","1835":"somesing","1836":"fabric","1837":"cbdao","1838":"tacos","1839":"rmpl","1840":"ethverse","1841":"safemoon","1842":"keep4r","1843":"coin98","1844":"piedao-defi","1845":"kiba-inu","1846":"rivex","1847":"strong","1848":"dxdao","1849":"plasma-finance","1850":"tower-token","1851":"near-protocol","1852":"v-id","1853":"yflink","1854":"ureeqa","1855":"dogefi","1856":"loser-coin","1857":"aave-snx","1858":"dsla-protocol","1859":"waves-enterprise","1860":"casper","1861":"powertrade-fuel","1862":"chia-network","1863":"wingshop","1864":"mixtrust","1865":"xtrabytes","1866":"keysians-network","1867":"unisocks","1868":"iconic-token","1869":"bird-money","1870":"degenerator","1871":"pepemon-pepeballs","1872":"force-for-fast","1873":"zelwin","1874":"defidollar-dao","1875":"jasmy","1876":"catex-token","1877":"ceres","1878":"bridge-oracle","1879":"strike","1880":"polkadot-new","1881":"singularitynet","1882":"yuno-finance","1883":"oxygen","1884":"fox-trading","1885":"raydium","1886":"liquity-usd","1887":"quantfury-token","1888":"dextoken","1889":"axis-defi","1890":"yfiexchange-finance","1891":"pmeer","1892":"hard-protocol","1893":"webdollar","1894":"aced","1895":"degenvc","1896":"konomi-network","1897":"growthdefi","1898":"everest","1899":"lien","1900":"trias-token","1901":"uncx","1902":"darwinia-network","1903":"darwinia-commitment-token","1904":"native-utility-token","1905":"robonomics-network","1906":"polkafoundry","1907":"rio-defi","1908":"mantra-dao","1909":"automata-network","1910":"convex-finance","1911":"indexed-finance","1912":"venus","1913":"dextrust","1914":"syscoin","1915":"hunt","1916":"marlin","1917":"trade-token-x","1918":"minter-network","1919":"hapi-one","1920":"cortex","1921":"smartcredit-token","1922":"kimchi-finance","1923":"liquidity-network","1924":"bamboo-defi","1925":"gitcoin","1926":"elrond-egld","1927":"cvcoin","1928":"raze-network","1929":"distx","1930":"ethereum-gold-project","1931":"node-runners","1932":"realio-network","1933":"wrapped-gen-0-cryptokitties","1934":"harvest-finance","1935":"props","1936":"oin-finance","1937":"earneo","1938":"carrot","1939":"yfdai-finance","1940":"xsgd","1941":"rocket-vault-rocketx","1942":"tontoken","1943":"digital-reserve-currency","1944":"xdai","1945":"celsius","1946":"swerve","1947":"lepricon","1948":"sun","1949":"dmd","1950":"yffs","1951":"opendao","1952":"zeedex","1953":"dafi-protocol","1954":"anchor-protocol","1955":"jackpool-finance","1956":"bnsd-finance","1957":"aleph-im","1958":"justliquidity","1959":"dracula-token","1960":"keytango","1961":"defibox","1962":"alchemy-pay","1963":"sperax","1964":"myneighboralice","1965":"geeq","1966":"swingby","1967":"ferrum-network","1968":"milk-alliance","1969":"hedget","1970":"krypton-galaxy-coin","1971":"pangolin","1972":"base-protocol","1973":"phala-network","1974":"digital-fantasy-sports","1975":"totemfi","1976":"nyan-finance","1977":"tranchess","1978":"tornado","1979":"fc-barcelona-fan-token","1980":"waltonchain","1981":"symbol","1982":"peony","1983":"dexe","1984":"wing","1985":"power-index-pool-token","1986":"apeswap-finance","1987":"frontier","1988":"defi-pulse-index","1989":"hamster","1990":"smart-bitcoin","1991":"new-bitshares","1992":"pancakeswap","1993":"linear","1994":"samurai","1995":"wolves-of-wall-street","1996":"float-protocol","1997":"beowulf","1998":"rizon-blockchain","1999":"oneswap-dao-token","2000":"ytsla-finance","2001":"leverj","2002":"rotten","2003":"pnetwork","2004":"nft","2005":"omisego","2006":"illuvium","2007":"burger-swap","2008":"covalent","2009":"ethernity-chain","2010":"newscrypto","2011":"chainbing","2012":"cvault-finance","2013":"lua-token","2014":"bonded-finance","2015":"stater","2016":"eight-hours","2017":"bitcoin-standard-hashrate-token","2018":"sun-contract","2019":"usd-coin","2020":"fox-token","2021":"bidao","2022":"netbox-coin","2023":"mina","2024":"entercoin","2025":"deflect","2026":"metaverse-index","2027":"venus-eth","2028":"p-quickswap","2029":"step-finance","2030":"fm-gallery","2031":"ellipsis","2032":"kucoin-shares","2033":"hyve","2034":"p-uniswap","2035":"0x","2036":"dawn-protocol","2037":"axie-infinity","2038":"looksrare","2039":"moonbeam","2040":"a2dao","2041":"impossible-finance","2042":"link","2043":"cryptoblades","2044":"octofi","2045":"shroom-finance","2046":"jade-protocol","2047":"bitguild-plat","2048":"tellor","2049":"livepeer","2050":"celer-network","2051":"wanchain","2052":"synthetix-network-token","2053":"anyswap","2054":"polyient-games-governance-token","2055":"azuki","2056":"soverain","2057":"mx-token","2058":"ddex","2059":"keydonix","2060":"hedera-hashgraph","2061":"dforce","2062":"wrapped-cryptokitties","2063":"sparkle","2064":"haven-protocol","2065":"eccoin","2066":"zenon","2067":"eboostcoin","2068":"hegic","2069":"klaytn","2070":"phantasma","2071":"commercium","2072":"0xbtc","2073":"digixdao","2074":"zelcash","2075":"throne","2076":"microbitcoin","2077":"dogecoin","2078":"nexus","2079":"vertcoin","2080":"stellite","2081":"esbc","2082":"ravencoin","2083":"tokenpocket","2084":"bitcoin-private","2085":"rocket-pool","2086":"pigeoncoin","2087":"neoworld-cash","2088":"suqa","2089":"internet-of-energy-network","2090":"synlev","2091":"pirate-chain","2092":"semux","2093":"grin","2094":"vipstar-coin","2095":"stakenet","2096":"siacoin","2097":"star-link","2098":"abbc-coin","2099":"stakeborg-dao","2100":"nftify","2101":"poolz-finance","2102":"absolute","2103":"holyheld","2104":"value-defi-protocol","2105":"jarvis-network","2106":"bitcoin-diamond","2107":"acre","2108":"thore-cash","2109":"inflationcoin","2110":"giant-coin","2111":"smartofgiving","2112":"doki-doki-finance","2113":"my-defi-pet","2114":"cyclone-protocol","2115":"omnitude","2116":"xdna","2117":"privcy","2118":"xovbank","2119":"bitcoin-one","2120":"gridcoin","2121":"evos","2122":"basis-gold-share","2123":"susd","2124":"dimecoin","2125":"p-bloktopia","2126":"zero-exchange","2127":"naka-bodhi-token","2128":"nav-coin","2129":"shard","2130":"nerva","2131":"veruscoin","2132":"midasprotocol","2133":"labh-coin","2134":"safeinsure","2135":"crypto-com","2136":"smartcash","2137":"bridge-protocol","2138":"mist","2139":"kommunitas","2140":"masq","2141":"amoveo","2142":"ren","2143":"ontology-gas","2144":"parachute","2145":"cazcoin","2146":"cashaa","2147":"bridgecoin","2148":"cmitcoin","2149":"experience-chain","2150":"luxcoin","2151":"cyberfi","2152":"edgeless","2153":"proxynode","2154":"verge","2155":"shift","2156":"dodo","2157":"the-abyss","2158":"terrausd","2159":"hydrogen","2160":"ribbon-finance","2161":"singulardtv","2162":"clintex-cti","2163":"opal","2164":"skycoin","2165":"idex","2166":"bitcoin2network","2167":"metaverse","2168":"marcopolo-protocol","2169":"aion","2170":"imagecoin","2171":"aragon","2172":"neo","2173":"sidus-heroes","2174":"terra-virtua-kolekt","2175":"spankchain","2176":"mercury","2177":"yield-app","2178":"adultchain","2179":"blockburn","2180":"gincoin","2181":"nanjcoin","2182":"merebel","2183":"project-pai","2184":"kira-network","2185":"dora-factory","2186":"harmony","2187":"fantasygold","2188":"anon","2189":"gamecredits","2190":"bonfida","2191":"delphy","2192":"ubiq","2193":"pivx","2194":"win-win","2195":"polkamon","2196":"venus-busd","2197":"o3-swap","2198":"mirrored-tesla","2199":"sentivate","2200":"opcoinx","2201":"blocknode","2202":"bitcoin-confidential","2203":"injective-protocol","2204":"yeld-finance","2205":"energi","2206":"bitcoin-rhodium","2207":"vethor-token","2208":"alpha-finance-lab","2209":"edgeware","2210":"mithril-share","2211":"monolith","2212":"bitblocks","2213":"top","2214":"masari","2215":"iridium","2216":"stk","2217":"roiyal-coin","2218":"stableusd","2219":"gemma-extending-tech","2220":"zookeeper","2221":"gem-exchange-and-trading","2222":"plotx","2223":"aegeus","2224":"taboo-token","2225":"lightpaycoin","2226":"digix-gold-token","2227":"electra","2228":"aencoin","2229":"cutcoin","2230":"cocos-bcx","2231":"zencash","2232":"kadena","2233":"safecapital","2234":"zcash","2235":"bitcoin-token","2236":"pegnet","2237":"dash","2238":"origin-protocol","2239":"leverj-gluon","2240":"defis","2241":"veil","2242":"crea","2243":"qwertycoin","2244":"nix-bridge-token","2245":"aave","2246":"paccoin","2247":"terra-krw","2248":"nyzo","2249":"perlin","2250":"newyorkcoin","2251":"certik","2252":"defizap","2253":"88mph","2254":"luna","2255":"blocknet","2256":"ergo","2257":"mith-cash","2258":"shopping","2259":"algorand","2260":"pbtc35a","2261":"pooltogether","2262":"spookyswap","2263":"ondori","2264":"union-protocol-governance-token","2265":"tiger-king","2266":"keep-network","2267":"cartesi","2268":"badger-dao","2269":"2key-network","2270":"yffii-finance","2271":"verasity","2272":"mirror-protocol","2273":"threshold","2274":"arweave","2275":"compound","2276":"nucypher","2277":"monavale","2278":"celo","2279":"dmm-governance","2280":"bit-tube","2281":"amber","2282":"balancer","2283":"vectorspace-ai","2284":"nexus-mutual","2285":"serum","2286":"trustswap","2287":"yearn-finance-ii","2288":"meta","2289":"lido-dao","2290":"nervenetwork","2291":"meter","2292":"ontology","2293":"bzx","2294":"wilder-world","2295":"gameswap","2296":"fio-protocol","2297":"datamine","2298":"meridian-network","2299":"proton","2300":"cream-finance","2301":"the-sandbox","2302":"p-klima-dao","2303":"dynamic-set-dollar","2304":"bitgear","2305":"hakka-finance","2306":"get-protocol","2307":"proxi","2308":"kusama","2309":"open-predict-token","2310":"unilayer","2311":"xiotri","2312":"rubic","2313":"gravity-finance","2314":"oddz","2315":"secret","2316":"vidya","2317":"machine-xchange-coin","2318":"sushi","2319":"yffi-finance","2320":"cirus-foundation","2321":"chain-games","2322":"fuse-network","2323":"tokamak-network","2324":"galaxy-heroes-coin","2325":"pearl","2326":"boosted-finance","2327":"salmonswap","2328":"rmrk","2329":"perpetual-protocol","2330":"unitrade","2331":"nsure-network","2332":"golff","2333":"pickle-finance","2334":"powerpool","2335":"spheroid-universe","2336":"aavegotchi-ghst-token","2337":"the-graph","2338":"zoracles","2339":"bella-protocol","2340":"flamingo","2341":"dhedge-dao","2342":"energy-web-token","2343":"yieldfarming-insure","2344":"dego-finance","2345":"safecoin","2346":"rarible","2347":"apecoin","2348":"crust","2349":"ethereum-meta","2350":"sapphire","2351":"mirrored-alibaba","2352":"piedao-dough-v2","2353":"smartkey","2354":"zcoin","2355":"xdefi-wallet","2356":"lossless","2357":"multiverse","2358":"shiden-network","2359":"songbird","2360":"beyond-protocol","2361":"babyswap","2362":"decentral-games-new","2363":"epik-prime","2364":"rimbit","2365":"arianee-protocol","2366":"openocean","2367":"merit-circle","2368":"alitas","2369":"neos-credits","2370":"unifty","2371":"impossible-decentralized-incubator-access","2372":"rchain","2373":"sovryn","2374":"dvision-network","2375":"ethereum-swarm","2376":"sifchain","2377":"xenioscoin","2378":"radix-protocol","2379":"p-genesis-worlds","2380":"dehub","2381":"darma-cash","2382":"platon","2383":"samoyedcoin","2384":"spell-token","2385":"richquack-com","2386":"deversifi","2387":"saito","2388":"bitball","2389":"lotto","2390":"cryptoplanes","2391":"defina-finance","2392":"deeper-network","2393":"bloomzed-token","2394":"starterra","2395":"numbers-protocol","2396":"alpha-quark-token","2397":"benqi","2398":"defi-land","2399":"bepro-network","2400":"doggy","2401":"epic-cash","2402":"ooki-protocol","2403":"p-polytrade","2404":"velo","2405":"acala","2406":"osmosis","2407":"bittorrent","2408":"world-mobile-token","2409":"starship","2410":"alethea-artificial-liquid-intelligence-token","2411":"unfederalreserve","2412":"somnium-space-cubes","2413":"p-peri-finance","2414":"highstreet","2415":"yam-v2","2416":"p-ixt-token","2417":"astar","2418":"xcad-network","2419":"bone-shibaswap","2420":"venus-dot","2421":"zano","2422":"aston-villa-fan-token","2423":"mirrored-ishares-silver-trust","2424":"mirrored-twitter","2425":"basic","2426":"mirrored-united-states-oil-fund","2427":"wrapped-velas","2428":"victoria-vr","2429":"dinoswap","2430":"p-decentral-games-ice","2431":"nft-worlds","2432":"conceal","2433":"retreeb","2434":"bsc-pancakeswap","2435":"defi-yield-protocol","2436":"sandclock","2437":"position-exchange","2438":"yamv3","2439":"alpine-f1-team-fan-token","2440":"florin","2441":"crypto-com-coin","2442":"counos-x","2443":"thetan-arena","2444":"adappter-token","2445":"project-seed","2446":"wrapped-tron","2447":"bittorrent-new","2448":"metapets","2449":"lazio-fan-token","2450":"mcontent","2451":"etherisc","2452":"green-metaverse-token","2453":"swipe","2454":"degate","2455":"bsc-tether","2456":"goldfinch-protocol","2457":"etha-lend","2458":"revolve-games","2459":"bsc-binance-usd","2460":"1eco","2461":"wrapped-cardano","2462":"cardano","2463":"hi-dollar","2464":"guildfi","2465":"everrise","2466":"ispolink","2467":"hydra","2468":"aurora-near","2469":"toncoin","2470":"polkacity","2471":"bsc-usd-coin","2472":"ton-crystal","2473":"gensokishis-metaverse","2474":"p-voxies","2475":"formation-fi","2476":"vader-protocol","2477":"league-of-kingdoms","2478":"apecoin-ape","2479":"thundercore","2480":"fruits-eco","2481":"chain","2482":"zum-token","2483":"tron","2484":"genopets","2485":"apollox","2486":"binance-coin","2487":"theta","2488":"wonderland","2489":"magic-token","2490":"floki-inu","2491":"zigcoin","2492":"stargate-finance"},"ticker":{"0":"CLR","1":"ENQ","2":"ATOM","3":"ZUC","4":"TRN","5":"KAVA","6":"sLINK","7":"TRU","8":"HX","9":"MGX","10":"EOSC","11":"YTA","12":"ANCT","13":"pSEED","14":"ALBT","15":"EURT","16":"ZRT","17":"TEP","18":"XWG","19":"XEP","20":"7E","21":"EVT","22":"YAP","23":"DSC","24":"HINT","25":"AZ","26":"WIKEN","27":"CHZ","28":"DEEP","29":"MINT","30":"POKE","31":"KICKS","32":"CVR","33":"HLN","34":"BFX","35":"HUSD","36":"FEI","37":"DAD","38":"MCH","39":"GT","40":"HNT","41":"AUG","42":"DFA","43":"MB8","44":"SPAZ","45":"CRN","46":"NGN","47":"TTL","48":"DILI","49":"ASH","50":"LINKA","51":"KAR","52":"PKT","53":"TSR","54":"ILK","55":"IDA","56":"XVIX","57":"RARE","58":"CBC","59":"CHT","60":"FLOW","61":"XSR","62":"SDT","63":"FDG","64":"LNN","65":"ZRN","66":"ALP","67":"NUO","68":"BTRST","69":"UMA","70":"BNP","71":"SSV","72":"SPX","73":"LTC","74":"PHOON","75":"MWC","76":"RZR","77":"SNAX","78":"PSS","79":"CHN","80":"CNZ","81":"WBL","82":"MPX","83":"BCPT","84":"JULD","85":"sBCH","86":"TRN","87":"PUNK","88":"KAT","89":"CERE","90":"XPO","91":"GXC","92":"EM","93":"PLG","94":"BAC","95":"SURE","96":"SON","97":"CUSD","98":"GTC","99":"LOL","100":"INFI","101":"TSHP","102":"LOT","103":"PLA","104":"TOKO","105":"FO","106":"CRON","107":"VID","108":"MIX","109":"SEAL","110":"STREAM","111":"YO","112":"CBM","113":"USDP","114":"VIDY","115":"VDX","116":"BDP","117":"WBET","118":"SPS","119":"AXE","120":"sDEFI","121":"QLC","122":"DEFISOCKS","123":"EMRX","124":"AMIO","125":"NSS","126":"sXAG","127":"CHI","128":"VBIT","129":"XCT","130":"UPP","131":"ETM","132":"DVP","133":"SFUND","134":"CHAT","135":"ANKR","136":"CNNS","137":"MOF","138":"AT","139":"NIOX","140":"SRN","141":"WHEN","142":"SAFEX","143":"VNT","144":"SPIKE","145":"GPT","146":"SPIRIT","147":"LOCUS","148":"FLC","149":"TRY","150":"QCX","151":"CHART","152":"PHV","153":"BOLT","154":"PLSPAD","155":"TOK","156":"INX","157":"LAMB","158":"BIRD","159":"YFDOT","160":"CPT","161":"ADX","162":"TRIBE","163":"P2PX","164":"PRG","165":"AFIN","166":"sCHF","167":"sADA","168":"sDASH","169":"S4F","170":"GMB","171":"ZCT","172":"OIO","173":"CYCLUB","174":"BTR","175":"LML","176":"API3","177":"XRA","178":"KZE","179":"SNPC","180":"FNB","181":"NEAL","182":"BUD","183":"BULL","184":"CPAY","185":"DIGG","186":"iOIL","187":"NKN","188":"sETC","189":"HNB","190":"STACK","191":"EVY","192":"IZI","193":"MINX","194":"EQUAD","195":"QDAO","196":"FET","197":"MBOX","198":"ARMOR","199":"SPDR","200":"MATIC","201":"DEX","202":"HXRO","203":"PTON","204":"BON","205":"SWFTC","206":"LEVL","207":"IOUX","208":"SPC","209":"YFIM","210":"GOT","211":"STX","212":"FAT","213":"HERC","214":"CNUS","215":"ASM","216":"DIO","217":"BOA","218":"SCH","219":"FLETA","220":"EXRN","221":"INXT","222":"FTN","223":"ECTE","224":"CRE","225":"AID","226":"SHFT","227":"INE","228":"B2B","229":"SOLVE","230":"OGO","231":"TCH","232":"XCHF","233":"MBN","234":"SOL","235":"DDIM","236":"EVED","237":"DREP","238":"TCH","239":"SKL","240":"PXL","241":"COVA","242":"BOT","243":"FOAM","244":"DUSK","245":"CPOOL","246":"PENDLE","247":"TRTL","248":"BIN","249":"SS","250":"HERB","251":"TCAT","252":"UFR","253":"TRAT","254":"AKT","255":"NMR","256":"UPUNK","257":"DERC","258":"XDB","259":"TTN","260":"PROB","261":"GOM","262":"GNO","263":"PKT","264":"PBR","265":"WBTC","266":"METIS","267":"O3","268":"LTX","269":"COIN","270":"CSW","271":"ZNT","272":"BISQ","273":"PEOS","274":"BAND","275":"TLOS","276":"SNOW","277":"GODS","278":"LGCY","279":"HSN","280":"BUSD","281":"STK","282":"KUN","283":"ALPACA","284":"CRA","285":"XCUR","286":"PAID","287":"TIDAL","288":"WSG","289":"KRI","290":"VRT","291":"DT","292":"CEEK","293":"BICO","294":"TFB","295":"PAXG","296":"ABR","297":"HIVE","298":"MMM","299":"ARV","300":"EUM","301":"DLT","302":"AWL","303":"AMBO","304":"ARG","305":"BTK","306":"DAPP","307":"OTO","308":"CIPX","309":"UDOO","310":"AMON","311":"BOB","312":"BCS","313":"BTP","314":"POLL","315":"BUTT","316":"COBO","317":"XIO","318":"iLINK","319":"POLIS","320":"NFTB","321":"SIN","322":"BQT","323":"CBW","324":"KEY","325":"FTW","326":"IMT","327":"ROO","328":"TULIP","329":"iTRX","330":"DFS","331":"DWL","332":"CDM","333":"sXMR","334":"SLIM","335":"CENT","336":"LEMO","337":"BEAM","338":"SLS","339":"SLRS","340":"sETH","341":"TNT","342":"DHV","343":"XRH","344":"ARY","345":"sXRP","346":"A","347":"iXRP","348":"sEOS","349":"OLY","350":"CITY","351":"STAK","352":"BMC","353":"BIX","354":"iADA","355":"iCEX","356":"BAN","357":"iDEFI","358":"CTCN","359":"ROOK","360":"MOD","361":"iBCH","362":"ICHI","363":"BPRO","364":"SUMO","365":"PUREX","366":"sEUR","367":"iDASH","368":"NNB","369":"NIX","370":"INV","371":"BUX","372":"mNFLX","373":"SPND","374":"sBNB","375":"SLT","376":"CBIX","377":"RDD","378":"BCHA","379":"LABS","380":"CAJ","381":"VIBE","382":"iLTC","383":"XTM","384":"EGC","385":"TAU","386":"sLTC","387":"OPCT","388":"BETA","389":"DEW","390":"sOIL","391":"VAI","392":"sXTZ","393":"HC","394":"ZLA","395":"MPAY","396":"PDEX","397":"SEER","398":"MAID","399":"CBC","400":"SAV3","401":"UNO","402":"BTM","403":"iBTC","404":"CLBK","405":"INK","406":"KMD","407":"IOT","408":"iETH","409":"ISR","410":"ZPT","411":"iEOS","412":"sAUD","413":"ZIPT","414":"DCR","415":"DAR","416":"iETC","417":"IIC","418":"MLN","419":"FREE","420":"RFOX","421":"iXMR","422":"INCX","423":"QAC","424":"MED","425":"iXTZ","426":"FUN","427":"ICP","428":"OPUL","429":"PAY","430":"1337","431":"SPD","432":"ONOT","433":"iBNB","434":"BNC","435":"DOE","436":"BKBT","437":"GAS","438":"EMC","439":"CPY","440":"BURST","441":"sTRX","442":"EUNO","443":"DIG","444":"OCN","445":"BRY","446":"GET","447":"CZR","448":"EXT","449":"ETZ","450":"NMC","451":"mMSFT","452":"IHF","453":"REVV","454":"PEAK","455":"LIKE","456":"DAC","457":"PST","458":"GAFI","459":"VIKKY","460":"MONA","461":"DACC","462":"PTI","463":"TNB","464":"SHIP","465":"EFX","466":"ZIP","467":"EST","468":"EVR","469":"vXVS","470":"ZP","471":"YCC","472":"MDS","473":"PHB","474":"vUSDC","475":"MOON","476":"MSP","477":"HERO","478":"WGR","479":"XCON","480":"GRID","481":"SCC","482":"OWL","483":"TOMO","484":"SCR","485":"QBX","486":"RFI","487":"ENTS","488":"JIYOX","489":"MDA","490":"REQ","491":"SUN","492":"BCA","493":"DIVI","494":"CVNT","495":"HUR","496":"FAIR","497":"XDN","498":"BTX","499":"SAFE","500":"XPM","501":"AE","502":"SNET","503":"KLV","504":"XBP","505":"HEZ","506":"SASHIMI","507":"SBD","508":"MPL","509":"ELI","510":"TIME","511":"VLC","512":"GIO","513":"XST","514":"BSM","515":"XSH","516":"PVT","517":"KILT","518":"ADAPAD","519":"FTM","520":"MNX","521":"TPAY","522":"FACE","523":"VITE","524":"VEMP","525":"LCC","526":"PURA","527":"EVN","528":"BEE","529":"NAS","530":"GO","531":"DRT","532":"ROUTE","533":"FLUX","534":"EXY","535":"CPC","536":"NLG","537":"MT","538":"XMC","539":"BRM","540":"MITX","541":"NRV","542":"UUU","543":"PSP","544":"PLBT","545":"SPX","546":"SALT","547":"PLAY","548":"GMAT","549":"YOOSHI","550":"DDX","551":"POE","552":"CVT","553":"LND","554":"SMS","555":"DIS","556":"STRAX","557":"PART","558":"NEX","559":"STAR","560":"TMTG","561":"TRAC","562":"OPIUM","563":"EXP","564":"DVPN","565":"BPT","566":"BET","567":"BID","568":"EOSDAC","569":"NOBS","570":"POLS","571":"BERRY","572":"RVC","573":"vBTC","574":"FLX","575":"ASA","576":"HYC","577":"STEEM","578":"MNC","579":"vBCH","580":"SHP","581":"ALB","582":"WTN","583":"EDEN","584":"BSW","585":"\u03a4BTC","586":"vDAI","587":"BAX","588":"BIOT","589":"XMX","590":"ONUS","591":"REVO","592":"TRST","593":"VIVID","594":"BMI","595":"RFR","596":"LBC","597":"CET","598":"VEED","599":"GVT","600":"TKY","601":"SKB","602":"XNO","603":"IQ","604":"PEOPLE","605":"GRS","606":"LBTC","607":"MORE","608":"KLKS","609":"RACA","610":"GZONE","611":"DREP","612":"QASH","613":"IDH","614":"XES","615":"XLM","616":"MARSH","617":"DOGEDASH","618":"vUSDT","619":"COMBO","620":"AURY","621":"BTO","622":"BCN","623":"TWT","624":"TNC","625":"GVE","626":"NCT","627":"BIFI","628":"IRIS","629":"TOSC","630":"SIG","631":"YUI","632":"WAG","633":"FST","634":"SENC","635":"MAPS","636":"LOC","637":"HORUS","638":"ETN","639":"BTRN","640":"NXT","641":"CONV","642":"SHX","643":"ELEC","644":"NER","645":"LKK","646":"BOMB","647":"DMT","648":"VIA","649":"ION","650":"WOO","651":"SCL","652":"ABDT","653":"HERO","654":"NBAI","655":"DML","656":"DCC","657":"PPP","658":"ARDR","659":"CHSB","660":"JC","661":"TERN","662":"LEO","663":"SPF","664":"CFG","665":"NPXSXEM","666":"EDU","667":"NOTE","668":"IFC","669":"ABT","670":"IGNIS","671":"ARN","672":"DMST","673":"BETR","674":"TCC","675":"QUICK","676":"DENT","677":"ARPA","678":"IONC","679":"OAX","680":"ROSE","681":"MKR","682":"MARO","683":"BCMC","684":"RBLX","685":"cUNI","686":"DAX","687":"MLM","688":"GUSD","689":"VLU","690":"FTT","691":"TRUE","692":"EDS","693":"REEF","694":"PNT","695":"RAMP","696":"DTX","697":"cUSDT","698":"UOS","699":"DASC","700":"FONT","701":"PPC","702":"aLINK","703":"CELL","704":"CHP","705":"BTT","706":"BBO","707":"BMH","708":"KIND","709":"BITCNY","710":"POLK","711":"TNS","712":"TITAN","713":"RYO","714":"JOE","715":"BOUTS","716":"TERA","717":"ATN","718":"BCAC","719":"STORM","720":"aETH","721":"LINDA","722":"BZNT","723":"MIM","724":"MNTP","725":"HBZ","726":"BOE","727":"KUE","728":"BHIG","729":"CARD","730":"aTUSD","731":"FWT","732":"DFT","733":"WICC","734":"FTO","735":"PAT","736":"MTC","737":"aDAI","738":"aMKR","739":"aBAT","740":"FXT","741":"aUSDT","742":"ORAI","743":"GENE","744":"QNTU","745":"PRE","746":"XBX","747":"VALOR","748":"OWC","749":"CVC","750":"FIS","751":"ELY","752":"DESO","753":"ALV","754":"DKA","755":"BFT","756":"SNTR","757":"CRE","758":"PCH","759":"EOSDT","760":"OLT","761":"aSUSD","762":"AOA","763":"CRC","764":"EKT","765":"QRDO","766":"XFC","767":"SKM","768":"aMANA","769":"KEBAB","770":"LUN","771":"PEPECASH","772":"PC","773":"RAVEN","774":"DEB","775":"AEON","776":"SOAR","777":"DBC","778":"CRPT","779":"ECH","780":"UCASH","781":"aKNC","782":"TEL","783":"VD","784":"OLXA","785":"VNX","786":"BOR","787":"JEW","788":"aZRX","789":"BLES","790":"SWP","791":"PXC","792":"PAL","793":"PTS","794":"NEXO","795":"PTT","796":"ARB","797":"KNT","798":"KRL","799":"EDR","800":"NSD","801":"CROAT","802":"GUP","803":"BQTX","804":"HT","805":"AMP","806":"JOT","807":"FDZ","808":"COSM","809":"MTRC","810":"BWX","811":"aBUSD","812":"FSBT","813":"WNCG","814":"EMPR","815":"EXO","816":"BTCB","817":"TEMCO","818":"ZXC","819":"PROM","820":"aREP","821":"aLEND","822":"DAV","823":"CBC","824":"HIT","825":"PLR","826":"PMA","827":"LCS","828":"LC4","829":"OKB","830":"MIR","831":"TMC","832":"MNGO","833":"HNST","834":"CSM","835":"BIT","836":"ATCC","837":"TFUEL","838":"CUDOS","839":"ABL","840":"DX","841":"APIS","842":"UBEX","843":"LGS","844":"XNS","845":"DAC","846":"CLS","847":"MASK","848":"OLE","849":"OCE","850":"ARK","851":"BCEO","852":"IG","853":"FSW","854":"ALI","855":"INDEX","856":"CHEX","857":"WABI","858":"MX","859":"XMON","860":"KWH","861":"SDA","862":"CIT","863":"FTT","864":"NBR","865":"MGC","866":"FKX","867":"BLY","868":"BFLY","869":"KIN","870":"TFL","871":"GOVI","872":"CGT","873":"HOLD","874":"WPP","875":"BU","876":"HB","877":"AUTO","878":"CRBN","879":"SEEN","880":"TV","881":"DNT","882":"GEM","883":"BLACK","884":"QSP","885":"BMX","886":"GSC","887":"DWS","888":"FMF","889":"HSC","890":"COS","891":"VOLLAR","892":"HELP","893":"GUM","894":"NAME","895":"ZSC","896":"RATING","897":"MYB","898":"ROCK2","899":"BTCN","900":"WEB","901":"RLX","902":"MEX","903":"REM","904":"aUSDC","905":"YAXIS","906":"ACAT","907":"YOU","908":"aWBTC","909":"XCLR","910":"STA","911":"GSE","912":"IMT","913":"RTH","914":"ROGUE","915":"APYS","916":"UGC","917":"UTT","918":"XPX","919":"RNT","920":"LET","921":"FUEL","922":"HAND","923":"BYN","924":"DOBO","925":"LST","926":"ICN","927":"WHALE","928":"AST","929":"MTN","930":"DAY","931":"ZEE","932":"HELP","933":"MTL","934":"BIFI","935":"EVC","936":"WET","937":"NVC","938":"OTB","939":"COU","940":"RAZOR","941":"BSV","942":"DTT","943":"W3C","944":"EARTH","945":"DATX","946":"STX","947":"NEU","948":"MBL","949":"XMV","950":"DCT","951":"DRG","952":"ZMN","953":"GLQ","954":"SIX","955":"PYN","956":"ROX","957":"BOBO","958":"UNV","959":"DAPS","960":"MET","961":"HUM","962":"NUG","963":"SSP","964":"ERT","965":"NFY","966":"CLV","967":"VME","968":"GLCH","969":"MDT","970":"PIB","971":"CARAT","972":"SIDUS","973":"DAN","974":"INSTAR","975":"FCL","976":"CMT","977":"XG","978":"DEXT","979":"SDA","980":"WIX","981":"META","982":"ADM","983":"ELAC","984":"PLA","985":"YSEC","986":"WAIF","987":"METM","988":"ICHX","989":"VGW","990":"NGC","991":"HBX","992":"AMO","993":"LTO","994":"FOTA","995":"AKRO","996":"RAINI","997":"LONDON","998":"NEXT","999":"FLUZ","1000":"RSR","1001":"TONE","1002":"EFI","1003":"XNK","1004":"FXS","1005":"LYNC","1006":"SOAR","1007":"8PAY","1008":"ORME","1009":"ATP","1010":"GARD","1011":"BCDT","1012":"DEC","1013":"HQT","1014":"ILC","1015":"ORION","1016":"TOL","1017":"ETA","1018":"SHA","1019":"ZIL","1020":"ONX","1021":"SUB","1022":"LTK","1023":"LIT","1024":"BHPC","1025":"UBT","1026":"EDN","1027":"AXP","1028":"BBGC","1029":"DUCK","1030":"PASS","1031":"NEW","1032":"DATA","1033":"TRI","1034":"XYO","1035":"RADS","1036":"PXG","1037":"CAN","1038":"MPG","1039":"VBK","1040":"SWC","1041":"MHC","1042":"DTEP","1043":"SERV","1044":"NEC","1045":"ICX","1046":"MVP","1047":"SAKE","1048":"BIT","1049":"HYN","1050":"FIL","1051":"IHT","1052":"BENZ","1053":"CGG","1054":"BTG","1055":"OST","1056":"MTX","1057":"AID","1058":"AGLD","1059":"BCI","1060":"MTV","1061":"ATRI","1062":"ORO","1063":"USDT","1064":"CLO","1065":"AUC","1066":"MCC","1067":"ADN","1068":"DVD","1069":"JAR","1070":"DRGN","1071":"LRN","1072":"HOT","1073":"INCNT","1074":"GALA","1075":"CNN","1076":"COV","1077":"ATMI","1078":"ACDC","1079":"FMTA","1080":"ART","1081":"AVINOC","1082":"WNL","1083":"PTN","1084":"CEN","1085":"BRZ","1086":"WIN","1087":"GMEE","1088":"ARGO","1089":"UMX","1090":"FF","1091":"BAAS","1092":"ETH","1093":"NPER","1094":"COTI","1095":"BOOM","1096":"WFX","1097":"UTK","1098":"XEM","1099":"SAN","1100":"ELA","1101":"BOBA","1102":"DXT","1103":"NRP","1104":"GOT","1105":"LOOM","1106":"CHR","1107":"NCASH","1108":"OMNI","1109":"KTLYO","1110":"IVY","1111":"SFCP","1112":"PCX","1113":"LYM","1114":"LITH","1115":"MINT","1116":"SARCO","1117":"CPT","1118":"BIR","1119":"CSP","1120":"FX","1121":"YEE","1122":"PNK","1123":"UT","1124":"POA","1125":"DWEB","1126":"AGRS","1127":"TIC","1128":"CWV","1129":"UND","1130":"PRO","1131":"HTML","1132":"NCC","1133":"CYMT","1134":"FBN","1135":"SHOPX","1136":"GMT","1137":"TUSD","1138":"SMARTUP","1139":"CV","1140":"RAGE","1141":"SFP","1142":"BOLTT","1143":"DAPPT","1144":"BCH","1145":"MAHA","1146":"ZCL","1147":"DINU","1148":"KANGAL","1149":"NAVY","1150":"QRT","1151":"PLU","1152":"UGAS","1153":"ITS","1154":"WOZX","1155":"APY","1156":"HVN","1157":"SYLO","1158":"QRL","1159":"MGD","1160":"DXG","1161":"CPX","1162":"PRIA","1163":"IPL","1164":"POWR","1165":"LSK","1166":"HEDG","1167":"WEMIX","1168":"NOS","1169":"BBN","1170":"MOC","1171":"BLZ","1172":"SHE","1173":"CGC","1174":"CXO","1175":"LOKI","1176":"ENJ","1177":"CRD","1178":"UBX","1179":"COLL","1180":"VEX","1181":"BCV","1182":"DDK","1183":"DADI","1184":"CS","1185":"CBT","1186":"PLA","1187":"KCASH","1188":"TCASH","1189":"OWN","1190":"FLASH","1191":"ZUT","1192":"WARP","1193":"HOT","1194":"EOS","1195":"LRC","1196":"MGO","1197":"VET","1198":"POLY","1199":"RCN","1200":"SFI","1201":"APR","1202":"BOND","1203":"TAP","1204":"CHONK","1205":"NCDT","1206":"ROOM","1207":"UNI","1208":"RLC","1209":"FLO","1210":"STORJ","1211":"HQX","1212":"UMB","1213":"TT","1214":"MOVR","1215":"QTUM","1216":"FERA","1217":"STKR","1218":"BITTO","1219":"SEN","1220":"XMR","1221":"YEED","1222":"ORBS","1223":"VLD","1224":"CTX","1225":"WLO","1226":"GEN","1227":"FLUX","1228":"XTZ","1229":"DTH","1230":"TORN","1231":"DERO","1232":"FLOT","1233":"HMQ","1234":"BOX","1235":"PPT","1236":"BOX","1237":"USDC-B","1238":"WAVES","1239":"FARA","1240":"KEY","1241":"BIA","1242":"XCM","1243":"ADS","1244":"WXT","1245":"EDR","1246":"BUBO","1247":"NGM","1248":"DACS","1249":"BLANK","1250":"NSBT","1251":"VAL","1252":"NOIA","1253":"MVC","1254":"IOST","1255":"CMCT","1256":"PGT","1257":"DDD","1258":"BTS","1259":"ZPR","1260":"DUO","1261":"OMI","1262":"DGTX","1263":"PHX","1264":"SLV","1265":"QKC","1266":"LIKE","1267":"RUFF","1268":"XCP","1269":"APPC","1270":"USE","1271":"AMPL","1272":"WIB","1273":"PYR","1274":"WSD","1275":"GNX","1276":"VEGA","1277":"QUN","1278":"SLR","1279":"TMN","1280":"BTC","1281":"BHP","1282":"CAPS","1283":"MASH","1284":"AERGO","1285":"TBTC","1286":"SWFL","1287":"SCCP","1288":"RDN","1289":"VEE","1290":"ARO","1291":"ADB","1292":"ODE","1293":"FTC","1294":"OPEN","1295":"VIB","1296":"EVE","1297":"SWTH","1298":"XFT","1299":"STN","1300":"HAI","1301":"CTRT","1302":"GNY","1303":"ZB","1304":"3DC","1305":"TOC","1306":"HER","1307":"TBX","1308":"ESS","1309":"FCT","1310":"NPX","1311":"FXF","1312":"NCT","1313":"EGT","1314":"VIN","1315":"DAO","1316":"TGAME","1317":"AIOZ","1318":"DOCK","1319":"BNX","1320":"NBC","1321":"JNT","1322":"XDC","1323":"DERI","1324":"BRD","1325":"ARAW","1326":"AMN","1327":"LXT","1328":"NTK","1329":"VVS","1330":"LIFE","1331":"AREPA","1332":"ZASH","1333":"MWAT","1334":"UIP","1335":"HPB","1336":"MEETONE","1337":"COFI","1338":"WHITE","1339":"UFO","1340":"LITH","1341":"FT","1342":"IQ","1343":"EMC2","1344":"KAN","1345":"EPK","1346":"ENGT","1347":"MFT","1348":"RPG","1349":"ONE","1350":"NAVI","1351":"TRIO","1352":"ADT","1353":"CHX","1354":"MFG","1355":"ORS","1356":"ORS","1357":"NULS","1358":"DNA","1359":"PROS","1360":"RMESH","1361":"MOON","1362":"EVX","1363":"E2C","1364":"MOBI","1365":"LIT","1366":"GTO","1367":"BKX","1368":"ASTRO","1369":"SIGNA","1370":"RTE","1371":"MAN","1372":"MTH","1373":"LINK","1374":"WINGS","1375":"ABX","1376":"SMT","1377":"USNBT","1378":"ELF","1379":"MANA","1380":"RED","1381":"DRPU","1382":"HOD","1383":"TNC","1384":"MFG","1385":"SLICE","1386":"STOS","1387":"AXN","1388":"BTRS","1389":"COVAL","1390":"GRMD","1391":"SNT","1392":"VSYS","1393":"UCT","1394":"PCL","1395":"XCD","1396":"ANJ","1397":"DHA","1398":"ESD","1399":"CGT","1400":"IOV","1401":"CND","1402":"EURS","1403":"BOS","1404":"UTNP","1405":"AMLT","1406":"S","1407":"RAI","1408":"DYDX","1409":"ZCN","1410":"NDEX","1411":"UC","1412":"IND","1413":"BAT","1414":"SENSO","1415":"ADI","1416":"MNC","1417":"BWK","1418":"BEST","1419":"TOS","1420":"TCT","1421":"WPR","1422":"SWM","1423":"QNT","1424":"LABX","1425":"B20","1426":"TENT","1427":"SUPER","1428":"TKO","1429":"XPRT","1430":"DCN","1431":"SNM","1432":"ITC","1433":"BORA","1434":"RGT","1435":"AWC","1436":"TEAM","1437":"PLTC","1438":"NEBL","1439":"MCT","1440":"SDAO","1441":"p-matic-network","1442":"stETH","1443":"WBNB","1444":"INCO","1445":"HGT","1446":"PRIX","1447":"MZK","1448":"ZKS","1449":"TEN","1450":"1WO","1451":"HTR","1452":"FCT,FCT2","1453":"XSG","1454":"SMI","1455":"PKC","1456":"IXT","1457":"RMT","1458":"EGG","1459":"VCT","1460":"FTI","1461":"UFT","1462":"NPXS","1463":"TAC","1464":"GMCOIN","1465":"EGG","1466":"NFTX","1467":"vBNB","1468":"BBR","1469":"ACT","1470":"CBK","1471":"DBET","1472":"CLOAK","1473":"IQN","1474":"VOTE","1475":"XEC","1476":"FTX","1477":"BANCA","1478":"UCN","1479":"imBTC","1480":"MITH","1481":"VGX","1482":"CUMMIES","1483":"TARA","1484":"UDO","1485":"CFX","1486":"MIOTA","1487":"YOYOW","1488":"XPAT","1489":"THRT","1490":"GCR","1491":"NEWOS","1492":"ZRC","1493":"PHI","1494":"IOTX","1495":"PHB","1496":"SBTC","1497":"ONC","1498":"MDO","1499":"KTN","1500":"CDT","1501":"MYST","1502":"RPD","1503":"ENG","1504":"ATL","1505":"BREW","1506":"DPT","1507":"CUST","1508":"STR","1509":"EGR","1510":"HBTC","1511":"CKB","1512":"BPX","1513":"GLM","1514":"YOUC","1515":"VSP","1516":"DXG","1517":"ECO","1518":"MDX","1519":"DG","1520":"VINCI","1521":"VNXLU","1522":"CXC","1523":"SUTER","1524":"EXMR","1525":"LCR","1526":"HOPR","1527":"LQTY","1528":"SAI","1529":"DFI","1530":"DAI","1531":"SHR","1532":"vLTC","1533":"SFT","1534":"BOK","1535":"CNX","1536":"CHI","1537":"LIFE","1538":"FCQ","1539":"ZB","1540":"PBTC","1541":"XRP","1542":"NORD","1543":"APH","1544":"BCP","1545":"UAXIE","1546":"VNXLU","1547":"FSN","1548":"MVL","1549":"ZCO","1550":"FN","1551":"ATLAS","1552":"MDM","1553":"LFM","1554":"SAB","1555":"BAO","1556":"ADK","1557":"RLY","1558":"vSXP","1559":"ACM","1560":"COT","1561":"POLIS","1562":"MOGX","1563":"PRT","1564":"ALCX","1565":"KOMET","1566":"CON","1567":"XDEF2","1568":"CRTS","1569":"RKN","1570":"NPC","1571":"BEPRO","1572":"OXT","1573":"KICK","1574":"SPR","1575":"RSK","1576":"UAT","1577":"UNISTAKE","1578":"IFC","1579":"XCASH","1580":"DAVP","1581":"CLT","1582":"ZYN","1583":"ROYA","1584":"CNB","1585":"NZO","1586":"ZT","1587":"USDX","1588":"SANX","1589":"IPX","1590":"INT","1591":"ROAD","1592":"BMXX","1593":"LON","1594":"CNCC","1595":"IMX","1596":"AVA","1597":"MATTER","1598":"AUDIO","1599":"ONS","1600":"PUSH","1601":"TTT","1602":"mVIXY","1603":"RVP","1604":"NOW","1605":"WELL","1606":"HTDF","1607":"PIZZA","1608":"AUCTION","1609":"BEAR","1610":"LYXe","1611":"ENS","1612":"VISR","1613":"BULL","1614":"USDN","1615":"GOC","1616":"vLINK","1617":"INSUR","1618":"TROY","1619":"CNS","1620":"XLT","1621":"XAUT","1622":"DTA","1623":"CRV","1624":"MATH","1625":"KOK","1626":"WRX","1627":"ARX","1628":"BDP","1629":"IDLE","1630":"LOCK","1631":"SCP","1632":"TRQ","1633":"INV","1634":"SPARTA","1635":"NFTI","1636":"USDX","1637":"K21","1638":"HYDRO","1639":"ORC","1640":"PTK","1641":"HOGE","1642":"XRUNE","1643":"HDAO","1644":"WETH","1645":"JOINT","1646":"APM","1647":"MEME","1648":"SMT","1649":"Mars","1650":"HNS","1651":"SNY","1652":"PMGT","1653":"VAL","1654":"KP3R","1655":"RIF","1656":"TLM","1657":"GXX","1658":"TOKE","1659":"PRV","1660":"ASTRO","1661":"TTC","1662":"mAMZN","1663":"COIN","1664":"STPT","1665":"OBSR","1666":"sXAU","1667":"LBA","1668":"XX","1669":"ZCX","1670":"AXEL","1671":"OXB","1672":"IDRT","1673":"UNFI","1674":"XFYI","1675":"URUS","1676":"XED","1677":"CHX","1678":"BID","1679":"MKX","1680":"MUSE","1681":"DUO","1682":"EXRD","1683":"DOWS","1684":"SRK","1685":"10SET","1686":"KOBO","1687":"DGB","1688":"ETHY","1689":"PRJ","1690":"BONDLY","1691":"DEUS","1692":"KYL","1693":"GS","1694":"BNT","1695":"WOOF","1696":"SYNC","1697":"USDC","1698":"LINK","1699":"FNT","1700":"GCR","1701":"VAB","1702":"EASY","1703":"OCEAN","1704":"1INCH","1705":"MMO","1706":"PRQ","1707":"EPAN","1708":"PAINT","1709":"BSCPAD","1710":"AAVE","1711":"CF","1712":"ETHYS","1713":"CSM","1714":"AXPR","1715":"RAE","1716":"EXNT","1717":"ICNQ","1718":"TLN","1719":"DEOR","1720":"AAC","1721":"FRAX","1722":"LKR","1723":"HPT","1724":"CLX","1725":"YLD","1726":"STZEN","1727":"DEP","1728":"USDJ","1729":"JST","1730":"KAI","1731":"UQC","1732":"PI","1733":"FOR","1734":"SEELE","1735":"REV","1736":"PUNDIX","1737":"MINTY","1738":"SURF","1739":"PSG","1740":"IOC","1741":"KISHU","1742":"JUV","1743":"ESH","1744":"FXC","1745":"CDAI","1746":"CUSDC","1747":"CETH","1748":"RUNE","1749":"BFC","1750":"GPKR","1751":"RULER","1752":"TOTEM","1753":"YVS","1754":"SLP","1755":"WAXP","1756":"DAG","1757":"ETC","1758":"FNX","1759":"RNDR","1760":"CTC","1761":"YGG","1762":"NUX","1763":"RENBTC","1764":"FORTH","1765":"OHM","1766":"STMX","1767":"BOSON","1768":"SERO","1769":"RINGX","1770":"HEX","1771":"KINE","1772":"WGRT","1773":"SOC","1774":"KNC","1775":"MNW","1776":"NODE","1777":"LCX","1778":"TRCL","1779":"RWS","1780":"BCHC","1781":"BTSE","1782":"CWS","1783":"RAD","1784":"SMTY","1785":"XEND","1786":"ICE","1787":"UMASK","1788":"BAKE","1789":"CHADS","1790":"AVAX","1791":"HOO","1792":"NEST","1793":"PRT","1794":"ORN","1795":"1-UP","1796":"ELON","1797":"MXX","1798":"DEC","1799":"MCB","1800":"MIST","1801":"DFYN","1802":"PLT","1803":"YFI","1804":"LMY","1805":"WNXM","1806":"LID","1807":"SHIB","1808":"IGG","1809":"RPN","1810":"MTHD","1811":"BNS","1812":"WOM","1813":"TEND","1814":"XAMP","1815":"AGA","1816":"FEAR","1817":"LIBERTAS","1818":"SNL","1819":"MASK","1820":"SUKU","1821":"AAB","1822":"XOR","1823":"MOB","1824":"SPORE","1825":"VNLA","1826":"NFT","1827":"DOS","1828":"BALPHA","1829":"PIE","1830":"REP","1831":"OUSD","1832":"ZAP","1833":"TRU","1834":"DIA","1835":"SSX","1836":"FAB","1837":"SBREE","1838":"TACO","1839":"RMPL","1840":"ETHV","1841":"SAFEMOON","1842":"KP4R","1843":"C98","1844":"DEFI++","1845":"KIBA","1846":"RVX","1847":"STRONG","1848":"DXD","1849":"PPAY","1850":"TOWER","1851":"NEAR","1852":"VIDT","1853":"YFL","1854":"URQA","1855":"DOGEFI","1856":"LOWB","1857":"aSNX","1858":"DSLA","1859":"WEST","1860":"CSPR","1861":"PTF","1862":"XCH","1863":"WING","1864":"MXT","1865":"XBY","1866":"KEN","1867":"SOCKS","1868":"ICNQ","1869":"BIRD","1870":"MEME","1871":"PPBLZ","1872":"FFF","1873":"ZLW","1874":"DFD","1875":"JASMY","1876":"CATT","1877":"CERES","1878":"BRG","1879":"STRK","1880":"DOT","1881":"AGIX","1882":"YUNO","1883":"OXY","1884":"FOXT","1885":"RAY","1886":"LUSD","1887":"QTF","1888":"DEXG","1889":"AXIS","1890":"YFIE","1891":"PMEER","1892":"HARD","1893":"WEBD","1894":"ACED","1895":"DGVC","1896":"KONO","1897":"GRO","1898":"ID","1899":"LIEN","1900":"TRIAS","1901":"UNCX","1902":"RING","1903":"KTON","1904":"NUT","1905":"XRT","1906":"PKF","1907":"RFUEL","1908":"OM","1909":"ATA","1910":"CVX","1911":"NDX","1912":"XVS","1913":"DETS","1914":"SYS","1915":"HUNT","1916":"POND","1917":"TIOX","1918":"BIP","1919":"HAPI","1920":"CTXC","1921":"SMARTCREDIT","1922":"KIMCHI","1923":"LQD","1924":"BAMBOO","1925":"GTC","1926":"EGLD","1927":"CVN","1928":"RAZE","1929":"DISTX","1930":"ETGP","1931":"NDR","1932":"RIO","1933":"WG0","1934":"FARM","1935":"PROPS","1936":"OIN","1937":"RNO","1938":"CRT","1939":"YF-DAI","1940":"XSGD","1941":"RVF","1942":"TON","1943":"DRC","1944":"STAKE","1945":"CEL","1946":"SWRV","1947":"L3P","1948":"SUN","1949":"DMD","1950":"YFFS","1951":"SOS","1952":"ZDEX","1953":"DAFI","1954":"ANC","1955":"JFI","1956":"BNSD","1957":"ALEPH","1958":"JUL","1959":"DRC","1960":"TANGO","1961":"BOX","1962":"ACH","1963":"SPA","1964":"ALICE","1965":"GEEQ","1966":"SWINGBY","1967":"FRM","1968":"MLK","1969":"HGET","1970":"KGC","1971":"PNG","1972":"BASE","1973":"PHA","1974":"DFS","1975":"TOTM","1976":"NYAN","1977":"CHESS","1978":"TCORE","1979":"BAR","1980":"WTC","1981":"XYM","1982":"PNY","1983":"DEXE","1984":"WING","1985":"PIPT","1986":"BANANA","1987":"FRONT","1988":"DPI","1989":"HAM","1990":"RBTC","1991":"NBS","1992":"CAKE","1993":"LINA","1994":"SAM","1995":"WOWS","1996":"BANK","1997":"BWF","1998":"ATOLO","1999":"ONES","2000":"YTSLA","2001":"LEV","2002":"ROT","2003":"PNT","2004":"NFT","2005":"OMG","2006":"ILV","2007":"BURGER","2008":"CQT","2009":"ERN","2010":"NWC","2011":"CBG","2012":"CORE","2013":"LUA","2014":"BOND","2015":"STR","2016":"EHRT","2017":"BTCST","2018":"SNC","2019":"USDC","2020":"FOX","2021":"BID","2022":"NBX","2023":"MINA","2024":"ENTRC","2025":"DEFLCT","2026":"MVI","2027":"vETH","2028":"QUICK","2029":"STEP","2030":"FMG","2031":"EPS","2032":"KCS","2033":"HYVE","2034":"UNI","2035":"ZRX","2036":"DAWN","2037":"AXS","2038":"LOOKS","2039":"GLMR","2040":"ATD","2041":"IF","2042":"LN","2043":"SKILL","2044":"OCTO","2045":"SHROOM","2046":"JADE","2047":"PLAT","2048":"TRB","2049":"LPT","2050":"CELR","2051":"WAN","2052":"SNX","2053":"ANY","2054":"PGT","2055":"AZUKI","2056":"SOVE","2057":"MX","2058":"DDEX","2059":"KDN","2060":"HBAR","2061":"DF","2062":"WCK","2063":"SPRKL","2064":"XHV","2065":"ECC","2066":"ZNN","2067":"EBST","2068":"HEGIC","2069":"KLAY","2070":"SOUL","2071":"CMM","2072":"0xBTC","2073":"DGD","2074":"ZEL","2075":"THN","2076":"MBC","2077":"DOGE","2078":"NXS","2079":"VTC","2080":"XTL","2081":"ESBC","2082":"RVN","2083":"TPT","2084":"BTCP","2085":"RPL","2086":"PGN","2087":"NASH","2088":"SUQA","2089":"IOEN","2090":"SYN","2091":"ARRR","2092":"SEM","2093":"GRIN","2094":"VIPS","2095":"XSN","2096":"SC","2097":"STARL","2098":"ABBC","2099":"STANDARD","2100":"N1","2101":"POOLZ","2102":"ABS","2103":"HOLY","2104":"VALUE","2105":"JRT","2106":"BCD","2107":"ACRE","2108":"TCH","2109":"IFLT","2110":"GIC","2111":"AOG","2112":"DOKI","2113":"DPET","2114":"CYC","2115":"ECOM","2116":"XDNA","2117":"PRIV","2118":"XOV","2119":"BTCONE","2120":"GRC","2121":"EVOS","2122":"BSGS","2123":"SUSD","2124":"DIME","2125":"BLOK","2126":"ZERO","2127":"NBOT","2128":"NAV","2129":"SHARD","2130":"XNV","2131":"VRSC","2132":"MAS","2133":"LABH","2134":"SINS","2135":"MCO","2136":"SMART","2137":"IAM","2138":"MIST","2139":"KOM","2140":"MASQ","2141":"VEO","2142":"REN","2143":"ONG","2144":"PAR","2145":"CAZ","2146":"CAS","2147":"BCO","2148":"CMIT","2149":"XPC","2150":"LUX","2151":"CFi","2152":"EDG","2153":"PRX","2154":"XVG","2155":"SHIFT","2156":"DODO","2157":"ABYSS","2158":"UST","2159":"HYDRO","2160":"RBN","2161":"SNGLS","2162":"CTI","2163":"OPAL","2164":"SKY","2165":"IDEX","2166":"B2N","2167":"ETP","2168":"MAP","2169":"AION","2170":"IMG","2171":"ANT","2172":"NEO","2173":"SENATE","2174":"TVK","2175":"SPANK","2176":"MER","2177":"YLD","2178":"XXX","2179":"BURN","2180":"GIN","2181":"NANJ","2182":"MERI","2183":"PAI","2184":"KEX","2185":"DORA","2186":"ONE","2187":"FGC","2188":"ANON","2189":"GAME","2190":"FIDA","2191":"DPY","2192":"UBQ","2193":"PIVX","2194":"TWINS","2195":"PMON","2196":"vBUSD","2197":"O3","2198":"mTSLA","2199":"SNTVT","2200":"OPCX","2201":"BND","2202":"BC","2203":"INJ","2204":"YELD","2205":"NRG","2206":"XRC","2207":"VTHO","2208":"ALPHA","2209":"EDG","2210":"MIS","2211":"TKN","2212":"BBK","2213":"TOP","2214":"MSR","2215":"IRD","2216":"STK","2217":"ROCO","2218":"USDS","2219":"GXT","2220":"ZOO","2221":"GXT","2222":"PlotX","2223":"AEG","2224":"TABOO","2225":"LPC","2226":"DGX","2227":"ECA","2228":"AEN","2229":"CUT","2230":"COCOS","2231":"ZEN","2232":"KDA","2233":"SCAP","2234":"ZEC","2235":"BTK","2236":"PEG","2237":"DASH","2238":"OGN","2239":"L2","2240":"XGM","2241":"VEIL","2242":"CREA","2243":"QWC","2244":"NBT","2245":"AAVE","2246":"PAC","2247":"KRT","2248":"NYZO","2249":"PERL","2250":"NYC","2251":"CTK","2252":"DFZ","2253":"MPH","2254":"LUNA","2255":"BLOCK","2256":"ERG","2257":"MIC","2258":"SPI","2259":"ALGO","2260":"pBTC35A","2261":"POOL","2262":"BOO","2263":"RSTR","2264":"UNN","2265":"TKING","2266":"KEEP","2267":"CTSI","2268":"BADGER","2269":"2KEY","2270":"YFFII","2271":"VRA","2272":"MIR","2273":"T","2274":"AR","2275":"COMP","2276":"NU","2277":"MONA","2278":"CELO","2279":"DMG","2280":"TUBE","2281":"AMB","2282":"BAL","2283":"VXV","2284":"NXM","2285":"SRM","2286":"SWAP","2287":"YFII","2288":"MTA","2289":"LDO","2290":"NVT","2291":"MTRG","2292":"ONT","2293":"BZRX","2294":"WILD","2295":"GSWAP","2296":"FIO","2297":"DAM","2298":"LOCK","2299":"XPR","2300":"CREAM","2301":"SAND","2302":"KLIMA","2303":"DSD","2304":"GEAR","2305":"HAKKA","2306":"GET","2307":"CREDIT","2308":"KSM","2309":"OPT","2310":"LAYER","2311":"XIOT","2312":"RBC","2313":"GFI","2314":"ODDZ","2315":"SCRT","2316":"VIDYA","2317":"MXC","2318":"SUSHI","2319":"YFFI","2320":"CIRUS","2321":"CHAIN","2322":"FUSE","2323":"TON","2324":"GHC","2325":"PEARL","2326":"BOOST","2327":"SAL","2328":"RMRK","2329":"PERP","2330":"TRADE","2331":"NSURE","2332":"GOF","2333":"PICKLE","2334":"CVP","2335":"SPH","2336":"GHST","2337":"GRT","2338":"ZORA","2339":"BEL","2340":"FLM","2341":"DHT","2342":"EWT","2343":"SAFE","2344":"DEGO","2345":"SAFE","2346":"RARI","2347":"APE","2348":"CRU","2349":"ETHM","2350":"SAPP","2351":"mBABA","2352":"DOUGH","2353":"SKEY","2354":"XZC","2355":"XDEFI","2356":"LSS","2357":"AI","2358":"SDN","2359":"SGB","2360":"BP","2361":"BABY","2362":"DG","2363":"EPIK","2364":"RBT","2365":"ARIA20","2366":"OOE","2367":"MC","2368":"ALT","2369":"NCR","2370":"NIF","2371":"IDIA","2372":"REV","2373":"SOV","2374":"DVI","2375":"BZZ","2376":"erowan","2377":"XNC","2378":"XRD","2379":"GENESIS","2380":"DEHUB","2381":"DMCH","2382":"LAT","2383":"SAMO","2384":"SPELL","2385":"QUACK","2386":"DVF","2387":"SAITO","2388":"BTB","2389":"LOTTO","2390":"CPAN","2391":"FINA","2392":"DPR","2393":"BLCT","2394":"STT","2395":"NUM","2396":"AQT","2397":"QI","2398":"DFL","2399":"BEPRO","2400":"DOGGY","2401":"EPIC","2402":"OOKI","2403":"TRADE","2404":"VELO","2405":"ACA","2406":"OSMO","2407":"BTTOLD","2408":"WMT","2409":"STARSHIP","2410":"ALI","2411":"eRSDL","2412":"CUBE","2413":"PERI","2414":"HIGH","2415":"YAMV2","2416":"IXT","2417":"ASTR","2418":"XCAD","2419":"BONE","2420":"vDOT","2421":"ZANO","2422":"AVL","2423":"mSLV","2424":"mTWTR","2425":"BASIC","2426":"mUSO","2427":"WVLX","2428":"VR","2429":"DINO","2430":"ICE","2431":"WRLD","2432":"CCX","2433":"TREEB","2434":"CAKE","2435":"DYP","2436":"QUARTZ","2437":"POSI","2438":"YAM","2439":"ALPINE","2440":"XFL","2441":"CRO","2442":"CCXX","2443":"THG","2444":"ADP","2445":"SHILL","2446":"WTRX","2447":"BTT","2448":"METAPETS","2449":"LAZIO","2450":"MCONTENT","2451":"DIP","2452":"GMT","2453":"SXP","2454":"DG","2455":"USDT","2456":"GFI","2457":"ETHA","2458":"RPG","2459":"BUSD","2460":"1ECO","2461":"WADA","2462":"ADA","2463":"HI","2464":"GF","2465":"RISE","2466":"ISP","2467":"HYDRA","2468":"AURORA","2469":"TON","2470":"POLC","2471":"USDC","2472":"EVER","2473":"MV","2474":"VOXEL","2475":"FORM","2476":"VADER","2477":"LOKA","2478":"APE","2479":"TT","2480":"FRTS","2481":"XCN","2482":"ZUM","2483":"TRX","2484":"GENE","2485":"APX","2486":"BNB","2487":"THETA","2488":"TIME","2489":"MAGIC","2490":"FLOKI","2491":"ZIG","2492":"STG"},"totalSupply":{"0":"350000000","1":"200298776.228","2":"0","3":"750000000","4":"1000000000","5":"173657543","6":"61278.144609","7":"1441129427","8":"90767835.7014","9":"1000000000","10":"1000000000","11":"4000000000","12":"759493671","13":null,"14":"1000000000","15":"50001660.54","16":null,"17":"1000000000","18":"10000000000","19":"17764656765","20":"1000000000000","21":"194322605.36644","22":"1000000000","23":"22657590.44","24":"1000000000","25":"124724662825.0904","26":"1057786429","27":"8888888888","28":"200000000","29":"1149363840000","30":"4965992","31":"100000000","32":"137865569","33":null,"34":"216525879","35":"405296299.98048353","36":"426116732.6917724","37":"1000000000","38":"1000000000","39":"300000000","40":"223000000","41":null,"42":"500000000","43":"162318938.73964253","44":"1000000","45":"5680000000","46":null,"47":null,"48":"10000000000","49":null,"50":"2000000000","51":"100000000","52":null,"53":"10000000000","54":"4400000000","55":null,"56":"177820","57":"1000000000","58":"23277.81","59":"200000000","60":"1378211739","61":"4800000000","62":"41600000","63":null,"64":null,"65":null,"66":"100000000","67":null,"68":"250000000","69":"106978900.61756237","70":"30000000000","71":"11012871","72":null,"73":"84000000","74":"30000","75":"10832902.1069404","76":null,"77":null,"78":null,"79":null,"80":null,"81":null,"82":null,"83":"116158667","84":"799383875.3500402","85":null,"86":null,"87":"0","88":"3098854699.998901","89":null,"90":null,"91":"99975728.11677","92":"2100000000","93":"10000000000","94":"54575249.82699095","95":"88000000000","96":"99900","97":"73714889.44679388","98":"2000000000","99":"21000000000","100":"120000000","101":"4980527200","102":"2000000000","103":"5000000000","104":"1893935377.68","105":"1075771761.0869","106":"19121150.61929778","107":"265000000","108":"10000000000","109":"1800000000","110":"13529414.41204769","111":"17000","112":"5000000000","113":"945642940.11","114":"10000000000","115":"1397703182.84111","116":"1500000000","117":"59499999948","118":"722390919.900285","119":"5260967.896223","120":"862.0891","121":"600000000","122":null,"123":"500000000","124":"1000000000","125":"100000000","126":"6463.877","127":"57605021.95319532","128":"1600000000","129":"200000000","130":"500000000.00000006","131":"210000000","132":"5000000000","133":"100000000","134":"1000000000","135":"10000000000","136":"10000000000","137":"50000000000","138":"1000000000","139":"315000000","140":"572166103.886","141":"875000000","142":"785836169","143":"10000000000","144":"5000000000","145":"111227045.9","146":"193320736","147":"7000000000","148":"1000000000","149":"10000000000","150":"500000000","151":"250000000","152":"3500000000","153":"995000000","154":"4989493252.178266","155":"998617461.09785","156":"6500000","157":"6000000000","158":"287504829.4830753","159":"18493.75","160":"10000000000","161":"150000000","162":"1000000000","163":"100000000","164":"164936509.401132","165":"500000000","166":null,"167":"77750.6","168":null,"169":"1000000000","170":"5000000000","171":null,"172":"2500000000","173":"3300000000","174":"801307097.3484074","175":"400000000","176":"110978134","177":"275000000","178":"8000000","179":"678333334","180":"2490000160","181":"10000000000","182":"35000000000","183":"5037375.6172","184":"90414745","185":"6613.10392834","186":null,"187":"700000000","188":null,"189":"1000000000","190":"1000000000","191":"88800000000","192":"1250000000","193":"300000000","194":"1000000000","195":"999081.58","196":"1151441225.6042874","197":"389181745","198":"1000000000","199":"1000000000","200":"10000000000","201":"1892996914","202":"1000000000","203":"23200000000","204":"21745688.3534","205":"10000000000","206":"311000000","207":"800000000","208":"392045341.5508068","209":"60000","210":"94808613.2074404","211":"1352464600","212":"200000000","213":"177593051.093575","214":"2000000000","215":"1443750000","216":"1000000000","217":"450000000","218":"12360000","219":"2000000000","220":"100000000000","221":"1119294","222":"2208919510.99","223":"100000000","224":"10000000000","225":"10000000000","226":"2520000000","227":"986000000","228":"19822337.19958543","229":"1000000000","230":"681277829.938","231":"100000000","232":"2000000","233":"588316307.739053","234":"511616946.142289","235":"1472092.49864144","236":"17756589.687825","237":"3431350063.1348667","238":"1000000000","239":"4276664349","240":"987500000","241":"6500000000","242":"100000000","243":"1000000000","244":"500000000","245":"1000000000","246":"188700000","247":"113149521580.33","248":"934870751","249":"350000000","250":"9500000000","251":"400000000","252":"24000000.00578804","253":"100000000000","254":"162863559.115862","255":"10952115.57893991","256":"250000000","257":"120000000","258":"1500000000","259":"910820150.1590686","260":"190000000","261":"75000000","262":"10000000","263":"19893267.57518124","264":"78833440.78078157","265":"280605.33068486","266":"5410000.51","267":"43741143.06878021","268":"100000000","269":"107142857","270":null,"271":null,"272":null,"273":"1000000000","274":"100000000","275":"355208370.6674","276":"500000","277":"500000000","278":"100000000000","279":"939999999.99","280":"17558654053.969254","281":null,"282":"1000000","283":"144858125.18686786","284":"1000000000","285":"50000000","286":"594717455.71","287":"19838000001","288":"969784779736925","289":"500000000","290":"30000000000","291":"21406200","292":"1000000000","293":"1000000000","294":"5000000000","295":"321242.331","296":null,"297":"405716517.119","298":null,"299":"93640000000","300":"332200000","301":"130271020.035721","302":null,"303":null,"304":null,"305":null,"306":"1056939327.1283","307":"38307412.5696","308":"1838477280","309":"888888888","310":"7176499999","311":"360000000","312":"68000000","313":null,"314":"8999991.9","315":null,"316":null,"317":"100000000","318":null,"319":"9831890.566954","320":"999800000","321":"1000000000","322":"1000000000","323":null,"324":"100000000000","325":null,"326":null,"327":"5400000000","328":"10000000","329":null,"330":null,"331":null,"332":"1607137916.7034","333":null,"334":"100000000","335":"3500002431.62882","336":"1600000000","337":"112695560","338":"1012265.271484","339":"998999999.788382","340":"29935.4","341":"1000000000","342":"2481597.0158735","343":"33891939.4450499","344":"88409932.93","345":null,"346":"278273649.09200984","347":null,"348":null,"349":"5000000000","350":"20000000","351":"3000000000","352":"60000000","353":"235972808.27","354":null,"355":null,"356":"1918969309.4961","357":null,"358":"100000000","359":"1226310","360":"22000000","361":null,"362":"5000000","363":"2194432","364":"50747651.37311921","365":"20220781.688152","366":null,"367":null,"368":"6000000000","369":"49238466.326487","370":"19424494","371":"76384000","372":"23681.225466","373":"2595786940","374":null,"375":"7186785.07945","376":"71852557","377":"30397274955","378":"18572921","379":"10000000000","380":"1780000","381":"267000000","382":null,"383":"800000000","384":"10000000000","385":"248090567","386":null,"387":"130000000","388":"1000000000","389":"200000000","390":null,"391":"57498554","392":null,"393":"45071909.329052","394":"60000000","395":"400000000","396":"5980965","397":"4100000000","398":"452552412","399":"401647957.75","400":"26734226","401":"203328.411519","402":"1750431000","403":"504.91243286","404":"0","405":"1000000000","406":"131297754.37274","407":"10000000000","408":"33199.81990523","409":"0","410":"1000000000","411":null,"412":null,"413":"1000000000","414":"13964103.24009726","415":"800000000","416":null,"417":"21000000000","418":"1824437.31909865","419":"10000000000000","420":"2000000000","421":null,"422":"599999990","423":"263449705.26","424":"7852770516","425":null,"426":"10977277070.398026","427":"480519953.0567597","428":"500000000","429":"205218255.94857776","430":"29381014024.1221","431":"10000000000","432":"75000000000","433":null,"434":"80000000","435":"940000000","436":"10000000000","437":"17190378","438":"48524899.663627","439":"52999378","440":"2120642444","441":null,"442":"6519321977.845664","443":"3000000000","444":"10000000000","445":"7500000","446":"1000000000","447":"1618033988","448":"10000000000","449":"228469282.9","450":"14736400","451":"27005.394475","452":"115465568.37822245","453":"3000000000","454":"2210014209.400847","455":"500000000","456":"2147063497","457":"101342465.753","458":"14965600","459":"12000000000","460":"65729674.871168","461":"30000000000","462":"3600000000","463":"4413848444.218691","464":"500000000","465":"650000000","466":"100000000000","467":"1000000000","468":"1000000000","469":"224433925","470":"26631450","471":"10000000000","472":"2000000000","473":"3711738526.056178","474":"4771015133","475":"34914717.40437041","476":"200000000","477":null,"478":"244956982.8109262","479":"70000000","480":"300000000","481":"10000000000","482":"10000000","483":"100000000","484":"29265074.799","485":"1380392157","486":"9439423.23468767","487":"19973330191.3784","488":"19171174.0990736","489":"19628888","490":"999877117","491":"19900730000","492":"21000000","493":"2843461442.9054317","494":"1000000000","495":"273325000","496":"53193831.469966","497":"8074043323.164733","498":"18617494.848048","499":"29600000","500":"40444062.53027372","501":"399590058.49932","502":"1000000000","503":"10000000000","504":"950873123.2766","505":"100000000","506":"2303603","507":"11104315.628","508":"10000000","509":"327902034","510":"710112.81","511":"300000000","512":"293438521.758814","513":"39630018.57645603","514":"1844674407","515":"512973375.234463","516":"31415926535","517":"151251450","518":"117642373.1","519":"2545006273","520":"6597237.5","521":"22076270.82026039","522":"936154235","523":"1033573236.6751214","524":"810720000","525":"755885824.9584944","526":"183205630.159698","527":"31247851.344495","528":"500000000","529":"77204167.35390002","530":"1173891384.6262372","531":"1300000000","532":"20000000","533":"1217752.94261417","534":"79404563.976511","535":"999999999.171729","536":"563635131","537":"4000000000","538":"19176436.19105911","539":"3000000000","540":"705999995","541":"82428004","542":"10000000000","543":"2000000000","544":"3969564.998325","545":"8888888888","546":"120000000","547":"252165028.70539504","548":"14900000000","549":"1000000000000000","550":"50297306","551":"3141592653","552":"2147483648","553":"1000000000","554":"300000","555":"100000","556":"136210815.40600657","557":"11751238.04984224","558":"50000000","559":"1000000000","560":"9161139666.073038","561":"500000000","562":"0","563":"22845437","564":"15333291319","565":"69434799.6292112","566":"167270820.687909","567":"42411190.23323367","568":"1200000000","569":"2083803239.8837934","570":"100000000","571":"301473027.68713224","572":"21000000000","573":"205836","574":"999715","575":"1000000000","576":"5000000000","577":"409190981","578":"27967628448.02272","579":"154392","580":"32000000","581":"28710000000","582":"19918713666.786705","583":"100000000","584":"257904694","585":"2100","586":"274537625","587":"50000000000","588":"8800000000","589":"30000000000","590":"98220390","591":"100000000","592":"100000000","593":"7910746.172528","594":"160000000","595":"4999650000","596":"767800721","597":"3830645898","598":"100000000000","599":"4436643.92853333","600":"9795844687","601":"6000000000","602":"133248297.197","603":"19821218.95644556","604":"5060137334.7","605":"79914473.88736624","606":"7465926","607":"30000000","608":"20236834.11186126","609":"431418233271","610":"63890301.56","611":"100000000","612":"1000000000","613":"399999999.991697","614":"300000000","615":"50001787885.13164","616":"100000000","617":"90286721009.31483","618":"1486153169","619":"100000000","620":"100000000","621":"0","622":"184066828814.058","623":"858094211","624":"1000000000","625":"1000000000","626":"39442342.51678241","627":"21050000000","628":"2074396673.351934","629":"2088368121.484905","630":"378851756.13266844","631":"27823","632":"500000000","633":"3481138.86850952","634":"500000000","635":"9999999999.42866","636":"18579040.57192072","637":"1200000000","638":"17918192499.48","639":"411092461","640":"998999941.50769","641":"10000000000","642":"99756866344","643":"749999945.2724185","644":"5000000000","645":"1285690000","646":"894202","647":"56921773.1719715","648":"23174754.6077522","649":"19730488.13320757","650":"2989743329.711403","651":"16714019.6643","652":"399998765","653":"9766213274","654":"4966351487","655":"272937006.72553134","656":"3423933690.00001","657":"165000000","658":"998999495","659":"1000000000","660":"20325184344.1018","661":"899921008","662":"985239504","663":"70673453.9859041","664":"430011123","665":"44748244753.68","666":"15000000000","667":"155043262.252","668":"1000000000","669":"186000000","670":"999449694","671":"20000000","672":"100000000","673":"300585863.0178498","674":"198824132","675":"1000000","676":"100000000000","677":"1500000000","678":"520000000","679":"100000000","680":"10000000000","681":"977631.03695089","682":"956559371","683":"994444444","684":"100000000","685":"0","686":"2000000000","687":"2274139409.85096","688":"237968902.93","689":"62964625","690":"332861484.4859715","691":"100000000","692":"100000000000","693":"18718991997","694":"49999999999.9999","695":"1000000000","696":"225000000","697":"4558972537","698":"1000000000","699":"3294166500.60993","700":"2000000","701":"27507069.48290935","702":"26144473","703":"29735000","704":"278237314.35457474","705":"57746762","706":"1993751536.605432","707":"2000000000","708":"61000000","709":"28245600","710":"100000000","711":"85900000","712":"10000000000","713":"37587959.45","714":"199010126.2","715":"2000000000","716":"1000000000","717":"0","718":"512122609.3851905","719":"9967745869.45655","720":"472179","721":"13437714976.6654","722":"999999820","723":"1933352207","724":"10000000","725":"1005250000","726":"100000000","727":"3013984637","728":"500000000","729":"6000000000","730":"150533312","731":"10000000000","732":"2100000000","733":"210000000","734":"100000000","735":"400000000","736":"850000000.0","737":"27109786","738":"14773","739":"10619595","740":"1087156610","741":"115251917","742":"19779272","743":"928840384.33","744":"120000000000","745":"500000000","746":"298021513","747":"75000000","748":"14895857.8285","749":"1000000000","750":"114911733.05","751":"260000000","752":"10808492","753":"3000000000","754":"5000000000","755":"1000000000","756":"186462812051","757":"1000000000","758":"2000000000","759":"2642505.293308","760":"1000000000","761":"2148854","762":"10000000000","763":"7057137.0998682","764":"724297908.475","765":"960000000","766":"1000000000","767":"1500000000","768":"60287273","769":"0","770":"2703356.07853158","771":"701884009","772":"8750000000","773":"10000000000","774":"400000000","775":"15831459.311211","776":"5000000000","777":"10000000000","778":"96390788.00187267","779":"2000000000","780":"21000000000","781":"4654328","782":"100000000000","783":"125000000","784":"2600000000","785":"10000000000","786":"200000","787":"133109989.976718","788":"4714502","789":"0","790":"250000000","791":"10000000000","792":"1000000000","793":"1000000000","794":"1000000000","795":"10000000000","796":"8910934.51117645","797":"712500000","798":"49417348","799":"1011211987.679","800":"27860582.87061938","801":"92139788.91112308","802":"98855149.62","803":"800000000","804":"500000000","805":"99213408535","806":"18601983.9507074","807":"1129842156.3806603","808":"923000000","809":"78125410.7521347","810":"64320000","811":"9850345","812":"3699483.0","813":"312269730.4","814":"12000000000000","815":"681268.10087038","816":"112501","817":"6000000000","818":"336893262.402","819":"19250000","820":"4935","821":null,"822":"1380276937.5528216","823":"90005603.0","824":"102400000000","825":"800000000","826":"78042956829","827":"69052396.22886488","828":"21000000","829":"300000000","830":"1300000000","831":"317149560.162186","832":"10000000000","833":"400000000","834":"240000000","835":"544070955.5837462","836":"410000000","837":"5301214400","838":"10000000000","839":"0","840":"100000000000","841":"9520000000","842":"3923877491.5183","843":"2351215.43440527","844":"1000000000","845":"8800000000","846":"50000000","847":"179.76462526","848":"10000000000","849":"8811756112.4","850":"161278856","851":"2970135500","852":"6562786056.1979","853":"100000000","854":"10000000000","855":"10000000","856":"500000000","857":"100000000","858":"227936637","859":"10000","860":"2193100000","861":"2000000000","862":"1000000000","863":"1000000000","864":"140803742.762465","865":"600000000","866":"150000000","867":"1000000000","868":"0","869":"10000000000000","870":"8924667","871":"32000000","872":"51000000","873":"923453365","874":"5000000000","875":"1500000000","876":"63695267","877":"53002.53352229","878":"50000000","879":"1000000","880":"210000000","881":"1000000000","882":"8000000000","883":"3000000000","884":"976442388.321185","885":"640881615.9639114","886":"1000000000","887":"500000000","888":"2000000000","889":"50000000000","890":"9931243927.6","891":"2100000000","892":"5008576.24641","893":"20000000","894":"10000000","895":"5642500000","896":"9362961677.089724","897":"179996749.91415927","898":"15724272","899":"13513159","900":"1000000000","901":"2000000000","902":"1939999970","903":"1000000000","904":"166565229","905":"954180.59483165","906":"6250000000","907":"2856000000","908":"15853","909":"1000000000","910":"4995565","911":"100000000000","912":"19155705310.11","913":"5207470.24862486","914":"10000000","915":"100000000","916":"1000000000","917":"69403130.9798553","918":"9000000000","919":"400000000","920":"1000000000","921":"1000000000","922":"403000000000","923":"100000000","924":"582959739166037","925":"1210071487.342","926":"98930339.7729087","927":"10000000","928":"500000000","929":"500000000","930":"1429719.89330873","931":"100000000","932":"23000000","933":"66588888","934":"80000","935":"1000000","936":"1250000000","937":"2335756.713897","938":"199470887.762562","939":"23000000000","940":"1000000000","941":"19041851.64423905","942":"55191260","943":"99998988889","944":"750000000","945":"10000000000","946":"53790743.07994793","947":"64395863.1113031","948":"30000000000","949":"13598604.127477","950":"73197775","951":"442695970.07088","952":"220381404.9900193","953":"499999972.79754","954":"999999970","955":"100000000","956":"331614076.92142236","957":"1000000000000","958":"1000000000","959":"60000000000","960":"14032374.99403337","961":"1084734273.38","962":"9729464161.02413","963":"10000000000","964":"230502120.2949735","965":"100000","966":"1000000000","967":"400000000","968":"88888888","969":"1000000000","970":"0","971":"2000000","972":"29999983513.74548","973":"69320719","974":"290875763.7038","975":"465000000","976":"1000000000","977":"472151842.053","978":"146733255","979":"1999999978.51766","980":"5330000000","981":"2000000000","982":"108814942.4","983":"10000000000","984":"700000000","985":"1000000","986":"308173247.06595224","987":"362949997","988":"1000000000","989":"200000000","990":"77910266.157691","991":"1000000000","992":"19679012761.81035","993":"397969832.9859243","994":"1000000000","995":"4000000000","996":"500000000","997":"45557739.812","998":"16788345.6826","999":"204780000","1000":"100000000000","1001":"1000000000","1002":"2000000000","1003":"500000000","1004":"99899045.23782395","1005":"1000000","1006":"10000000","1007":"88888888","1008":"200000000","1009":"4000001000","1010":"10000000000","1011":"40835044.38886192","1012":"99006341.50190786","1013":"100000000","1014":"1774234805.3860521","1015":"1000000000","1016":"760019385.3676834","1017":"9919303956.4122","1018":"8500000000","1019":"15988637523.976025","1020":"787439.339231","1021":"472000000","1022":"1500000000","1023":"130039256.689","1024":"46915621.12","1025":"150000000","1026":"1000000000","1027":"346274001.40087","1028":"10000000000","1029":"95986126.46717","1030":"1000000000","1031":"98823661261.46","1032":"861800041.8100061","1033":"0","1034":"13960974963","1035":"4195412.03757967","1036":"1000000000","1037":"95826983.065","1038":"1000000000","1039":"1091289380.162983","1040":"1000000000","1041":"3387361004","1042":"79881907.27782872","1043":"10000000000","1044":"618133330.107871","1045":"926210351","1046":"10000000000","1047":"136278605.52495152","1048":"10000000000","1049":"7876642307","1050":"193576266","1051":"996491162","1052":"6680472.88","1053":"120000000","1054":"17513923.589","1055":"800000000","1056":"314159265","1057":"42547118.7","1058":"70170001","1059":"18326891.5","1060":"10000000000","1061":"1975082183","1062":"100000000","1063":"85713951815.53362","1064":"3104773221","1065":"65829630.62007512","1066":"20000000000","1067":"100000000000","1068":"15070000","1069":"600000000","1070":"433494437","1071":"113602931.2","1072":"177619433541.14133","1073":"46015890.96620686","1074":"35240112493","1075":"100000000000","1076":"18866825.21152613","1077":"1000000000","1078":"4000000000","1079":"16432768.82031443","1080":"100000000","1081":"1000000000","1082":"77777777","1083":"1000000000","1084":"898614083.223577","1085":"1000000000","1086":"993780859243.4863","1087":"3180000000","1088":"65541667","1089":"10000000","1090":"10000000","1091":"10000000000","1092":"120520873.749","1093":"218999990","1094":"2000000000","1095":"966998953.5617033","1096":"27000000000","1097":"500000000","1098":"8999999999","1099":"83337000","1100":"24940211.89010385","1101":"500000000","1102":"1000000000","1103":"9500000000","1104":"100000000","1105":"1000000000","1106":"471970667.472656","1107":"10000000000","1108":"619338.14952607","1109":"85000000","1110":"1610924200","1111":"100000000","1112":"12505374.7","1113":"999999999.017098","1114":"10000000000","1115":"250000000","1116":"100000000","1117":"27662180148.278465","1118":"107107211.29054286","1119":"1000000000","1120":"408520357","1121":"10000000000","1122":"764626704","1123":"181135560.402","1124":"294965004.38229126","1125":"100000000","1126":"42000000","1127":"26000000000","1128":"10000000000","1129":"120000000","1130":"100000000","1131":"80434575000","1132":"657440000","1133":"15500000000","1134":"1098280626.7322183","1135":"500000000","1136":"263581074","1137":"1356073852.4976616","1138":"5000000000","1139":"9835745291.659302","1140":"400000000","1141":"500000000","1142":"170000000","1143":"5000000000","1144":"19045925","1145":"9997846.07721052","1146":"9287024.33","1147":"955970168188516.5","1148":"100000000000","1149":"161513803.918","1150":"721685470","1151":"20000000","1152":"1000000000","1153":"100000","1154":"1000000000","1155":"100000000","1156":"500000000","1157":"10000000000","1158":"76189767.04038996","1159":"139594968.222342","1160":"91834502.8781996","1161":"676989090","1162":"0","1163":"300000000","1164":"999506122.99937","1165":"144818773","1166":"1000000000","1167":"1018187200","1168":"330000000","1169":"1000000000","1170":"500000000","1171":"500000000","1172":"5000000000","1173":"92314325.0","1174":"215119015.62456924","1175":"58044597","1176":"1000000000","1177":"1000000000","1178":"1000000000","1179":"50000000","1180":"1008772305","1181":"1200000000","1182":"45000000","1183":"100000000","1184":"249471071.21","1185":"174866686","1186":"100000000000","1187":"1000000000","1188":"0","1189":"110000000000","1190":"900000000","1191":"968.75577487","1192":"150000","1193":"1560000000","1194":"1053021245.2089","1195":"1373873439.4424574","1196":"99996877","1197":"86712634466","1198":"1000000000","1199":"999942647.353","1200":"92122.50184922","1201":"14013343.94182704","1202":"10000000","1203":"100000000","1204":"39000","1205":"50000000","1206":"100000000","1207":"1000000000","1208":"86999784.9868455","1209":"152351008.348","1210":"424999998.00001335","1211":"275854380","1212":"498500000","1213":"10000000000","1214":"10312860","1215":"103944501","1216":"300000000","1217":"300000000","1218":"17709627","1219":"1762262602.4235","1220":"18112344.43989508","1221":"10000000000","1222":"10000000000","1223":"449848163.6903","1224":"47879123.3895954","1225":"674999515","1226":"60000000","1227":"220347379.7495742","1228":"913820186.87064","1229":"100000000","1230":"10000000","1231":"18400000","1232":"100000000","1233":"207143695.03559843","1234":"3000000000","1235":"53252246","1236":"100000000","1237":null,"1238":"108095650","1239":"99932669","1240":"5999999954.464072","1241":"2000000000","1242":"326798666.0705","1243":"38758206","1244":"10000000000","1245":"1468902335.5060341","1246":"200000000","1247":"102993397","1248":"3000000000","1249":"124999999.99998416","1250":"2582557.458802","1251":"100000000","1252":"1000000000","1253":"3000000000","1254":"18588745668","1255":"2000000000","1256":"60000000","1257":"1000000000","1258":"2994610000","1259":"1850000000","1260":"100000000","1261":"750000000000","1262":"2300000000","1263":"3391976087.0243707","1264":"1000000000","1265":"10000000000","1266":"1081994160.4933648","1267":"1880000000","1268":"2613892","1269":"240672142.3038867","1270":"20000000000","1271":"84465398.19880284","1272":"9000000000","1273":"50000000","1274":"439402","1275":"650000000","1276":"64999723","1277":"1557000000","1278":"98035260984.55385","1279":"300000000","1280":"19021675","1281":"52881277.67","1282":"2500000000","1283":"200000000","1284":"500000000","1285":null,"1286":"0","1287":"20000000","1288":"99999999.99999996","1289":"3646271241.2002554","1290":"545399000","1291":"1000000000","1292":"268946131","1293":"336000000","1294":"1745447045","1295":"200000000","1296":"99998697","1297":"1719507587.7316053","1298":"10000000","1299":"0","1300":"1000000000","1301":"8966516683.735167","1302":"400000000","1303":"1600000000","1304":"76477814","1305":"990000000","1306":"2000000000","1307":"16051589.78269311","1308":"1755313373","1309":"10306081.05199586","1310":"29800000","1311":"150000000","1312":"1885913075.8515422","1313":"8000000000","1314":"840108901","1315":"274956231.6951411","1316":"101945017","1317":"1000000000","1318":"869619708.773492","1319":"2485629.41653268","1320":"102289706.27784455","1321":"200000000","1322":"37705012698.75","1323":"466032435.592577","1324":"88862718","1325":"4665366360","1326":"1209963685.36","1327":"2000000000","1328":"99987499.9","1329":"42038500798753.555","1330":"10000000000","1331":"17186897.23837551","1332":"4500000000","1333":"500000000","1334":"2839985525.01","1335":"101119949.75","1336":"10000000000","1337":"300000000","1338":"8888","1339":"25757575757575.5","1340":"10000000000","1341":"43593614.8928","1342":"10021453884.929","1343":"222506775.59999955","1344":"10000000000","1345":"130057090","1346":"1000000000","1347":"10000000000","1348":"21000000","1349":"6021071877.392","1350":"1000000000","1351":"5000000000","1352":"1000000000","1353":"168956522.093084","1354":"868459135.7414333","1355":"557670519.646","1356":"300000000","1357":"111697638.27680597","1358":"70938084.47283192","1359":"100000000","1360":"129498559","1361":"227490931369.9521","1362":"25000000","1363":"977233870.6499683","1364":"888000000","1365":"100000000","1366":"1000000000","1367":"400000000","1368":"242000000","1369":"2130801392","1370":"1000000000","1371":"647450055.959024","1372":"402400000","1373":"1000000000","1374":"100000000","1375":"25000000","1376":"3141592653","1377":"70510851.3915","1378":"879999999.9864658","1379":"2193690027.320146","1380":"200000000","1381":"3530372.73","1382":"1000000000","1383":"196719999947","1384":"868459135.7414333","1385":"20000000","1386":"100000000","1387":"156630345427","1388":"1000000","1389":"1786752135.9070642","1390":"14899993","1391":"6804870174","1392":"4480040303","1393":"1050000000","1394":"220456030","1395":"2500000000","1396":"128645461.38846613","1397":null,"1398":"446012144.97595006","1399":"100771.01","1400":"10000000000","1401":"2000000004.6079144","1402":"124125940","1403":"1248426475","1404":"4997891952","1405":"400000000","1406":"4000000000","1407":"17353988.68414465","1408":"1000000000","1409":"200000000","1410":"15000000000","1411":"10000000000","1412":"170622046.999999","1413":"1500000000","1414":"715280000","1415":"750000000","1416":"374000000","1417":"15237644.1329747","1418":"815801392","1419":"1000000000","1420":"995239500","1421":"745248183.0967742","1422":"99535052.4739791","1423":"14612493.080826","1424":"4042772.32214405","1425":"10000000","1426":"38741780","1427":"999998077.4169173","1428":"500000000","1429":"135561184.162158","1430":"7283485741241","1431":"444000000","1432":"99999999","1433":"1205750000","1434":"12485047.99443493","1435":"100000000","1436":"17818682.1654","1437":"300000000","1438":"19136817.13827556","1439":"580000000","1440":"100000000","1441":"10000000000","1442":"3545583.27816329","1443":"5895739.80984789","1444":"10000000000","1445":"1000000000","1446":"1275455.31177803","1447":"1000000000","1448":"1000000000","1449":"200000000","1450":"37219452.96","1451":"905854144","1452":"600000000","1453":"30907160","1454":"1000000000000","1455":"83927273","1456":"65778843.83119764","1457":"744298152","1458":"2994901340","1459":"1000000000","1460":"10000000000","1461":"100000000","1462":"258498693019.07","1463":"1000000000","1464":"80000000","1465":"1867616.50450207","1466":"650000","1467":"84327095","1468":"13207453.542509","1469":"1000000000","1470":"100000000","1471":"188632973.02179232","1472":"5829982.24822935","1473":"5491860.1987553","1474":"1000000000","1475":"18899704673313","1476":"100000000","1477":"20000000000","1478":"823600646.899701","1479":"21000000","1480":"1000000000","1481":"278482214.1856918","1482":"1320428308.6200805","1483":"10000000000","1484":"114954387","1485":"1693841059.9905357","1486":"2779530283","1487":"1062756904.83163","1488":"42000000000","1489":"202027489.6","1490":"107024221.63874947","1491":"10000000000","1492":"4988893","1493":"13578190.26911044","1494":"9626124332.32","1495":"37136774.79","1496":"512.53","1497":"7407","1498":"160119","1499":"10000000","1500":"1000000000","1501":"32433365","1502":"16719886.01","1503":"35238560.94940967","1504":"54175040.67775495","1505":"13207423.75057638","1506":"10000000","1507":"10000000000","1508":null,"1509":"99097732541.23735","1510":"39884.08220474","1511":"36465704452.29019","1512":"40000000000","1513":"1000000000","1514":"11000000000","1515":"10000000","1516":null,"1517":"5000000000","1518":"1000000000","1519":"1000000","1520":"300000000","1521":"80000000","1522":"400552000","1523":"10000000000","1524":"0","1525":null,"1526":"210203458","1527":"100000000","1528":"6660106.02362148","1529":"784881510.3564407","1530":"9114753229.70494","1531":"6434460139.8","1532":"4142378","1533":"1885974016","1534":"650000000","1535":null,"1536":"1155019","1537":"99999999999.99998","1538":"210000000","1539":"2100000000","1540":"21000000","1541":"99989594553","1542":"9800000","1543":"70188440","1544":null,"1545":"10000000","1546":"80000000","1547":"70354038.92307134","1548":"30000000000","1549":"450099286.73634","1550":"0","1551":"36000000000","1552":"1000000000","1553":null,"1554":null,"1555":"560270974250","1556":"25000000","1557":"15000000000","1558":"396674884","1559":"20000000","1560":"20000000000","1561":"360000000","1562":"1000000000","1563":null,"1564":"1483330.10673126","1565":"30000","1566":"5000000000","1567":"17000000","1568":"100000000000","1569":"285714286","1570":"150000000","1571":"10000000000","1572":"1000000000","1573":"1251233467898.1963","1574":null,"1575":null,"1576":"500000000","1577":"279999999.9998125","1578":"90595753021.14714","1579":"62820378619","1580":"42000000","1581":"22000000","1582":"94771875","1583":"72000000","1584":"1350000000","1585":"21000000000","1586":"500000000","1587":"7079558.7412","1588":null,"1589":"1000000000","1590":"901948162.6726475","1591":"2000000000","1592":"1047729.586219","1593":"116298814.85629334","1594":null,"1595":"2000000000","1596":"61011389","1597":"100000000","1598":"1091417308","1599":"100001","1600":"100000000","1601":"2000000000","1602":"2204232.270521","1603":"2000000000","1604":"199763509.8661694","1605":"1496163846","1606":"96000000","1607":"1000000000","1608":"2083955.597602","1609":"178313.91665","1610":"100000000","1611":"100000000","1612":"100000000","1613":"454.930863","1614":"972376804.1889302","1615":"299095759","1616":"30907312","1617":"100000000","1618":"10000000000","1619":"159158246060","1620":"35936705.98180369","1621":"246524","1622":"11499999989","1623":"1733796311.1560354","1624":"200000000","1625":"5000000000","1626":"962646668.99","1627":"400000000","1628":"64923252.85185185","1629":null,"1630":null,"1631":"46313031","1632":null,"1633":"140000","1634":"105742186.59471287","1635":"2230.17","1636":"173870324","1637":"21000000","1638":"111111110.9","1639":"975387819.7085366","1640":null,"1641":"402276464812","1642":"500000000","1643":null,"1644":"1153916.966737","1645":"616000000","1646":"1812500000","1647":"28000","1648":"250000000","1649":"0","1650":"485187506.81599","1651":"100000000","1652":"1156.8193","1653":"4561323.16166897","1654":"200001","1655":"1000000000","1656":"5189345239.4728","1657":"7137564.294365","1658":"100000000","1659":null,"1660":"5000000","1661":"866531275.933","1662":"2888.901986","1663":"3470000","1664":"1942420283","1665":"14000000000","1666":null,"1667":"1000000000","1668":null,"1669":"0","1670":"770933110.5085934","1671":"9801505.29","1672":"130112000000","1673":"8629041.51706566","1674":"0","1675":"1000000","1676":"100000000","1677":"168956522.0930844","1678":"100000000","1679":null,"1680":"508554","1681":"314718.641345","1682":"0","1683":"39500000","1684":"13444612245.205578","1685":"210000000","1686":"24700247.684453","1687":"15281064974.757526","1688":"500000","1689":"56834485.351899","1690":"983620758","1691":"1793062.577204","1692":"1000000000","1693":"208911248","1694":"262409792.33707893","1695":"17902999424","1696":"156295536.80859593","1697":"49892351394.13943","1698":"1000000000","1699":"99360000000","1700":"4000000","1701":"1456250000","1702":"10000000","1703":"613099141","1704":"1500000000","1705":"118993176.77355419","1706":"310256872","1707":"94697000","1708":"0","1709":"175600000","1710":"16000000","1711":"2446961.25","1712":"500000","1713":"200000000","1714":"344674001.40087","1715":"6184935.69204759","1716":"100000000","1717":"20000000","1718":null,"1719":"0","1720":"1000000000","1721":"2697095597.8401165","1722":"100000000","1723":"8565109430.916","1724":null,"1725":"0","1726":"16075.22044141","1727":"30000000000","1728":"4227158.28542326","1729":"9900000000","1730":"5000000000","1731":"40000000","1732":"1561546868","1733":"1000000000","1734":"30000000000","1735":"85061485689.83401","1736":"258491637.23","1737":"0","1738":"0","1739":"20000000","1740":"19538540.97434563","1741":"96835135595251650","1742":"20000000","1743":"12113793.0641682","1744":"7273713838","1745":"2025596474","1746":"9803299805.03644","1747":"0","1748":"334937974.91478544","1749":"2368584073","1750":"6061848.762569","1751":"0","1752":null,"1753":"1853610.1820075","1754":"5375374133","1755":"3860304851.9487076","1756":"3711998690.36","1757":"210700000","1758":"176495406.66","1759":"530962614.97878325","1760":"599999997","1761":"1000000000","1762":"50000000","1763":null,"1764":"15297897.14455933","1765":"1416029","1766":"10000000000","1767":"200000000","1768":"623008894","1769":"357117790.3168","1770":"633542658973","1771":"0","1772":"21000000000","1773":"1500000000","1774":"177809349.53012875","1775":"47897218","1776":"10000000000","1777":"950000000","1778":"0","1779":null,"1780":"10000000000","1781":"165904711","1782":"10000000","1783":"99998580","1784":"100000000","1785":"200000000","1786":"11248229.1","1787":"100000000","1788":"289770788.00724965","1789":"58344742.45950471","1790":"395891289.9134107","1791":"100000000","1792":"9978035000","1793":"1500000000","1794":"100000000","1795":"61170994","1796":"1000000000000000","1797":"186586282.17457947","1798":"1000000000","1799":"2268644","1800":"1138093","1801":"198284457","1802":"107790337.524","1803":"36666","1804":"189000000","1805":"6884560.95953039","1806":"0","1807":"589735030408322.8","1808":"48132126676","1809":null,"1810":"100000000","1811":null,"1812":"1000000000","1813":"7896632.18027489","1814":null,"1815":"5999999.4","1816":"65000000","1817":"100000000","1818":"963252000","1819":"100000000","1820":"1500000000","1821":"25000000.00000028","1822":"406855.25444109","1823":"250000000","1824":"16495545592621500","1825":"799908.839536","1826":"999990000000000","1827":"949292114.345","1828":"18000.1","1829":"0","1830":"11000000","1831":"87954625.65016264","1832":"520000000","1833":"279976074","1834":"175583746.1333865","1835":"6000000000","1836":"500000000","1837":"1508819","1838":"0","1839":"706101","1840":"36968268.00915949","1841":"1000000000000000","1842":"100000","1843":"1000000000","1844":"335150","1845":"1000000000000","1846":"25000000","1847":"523990","1848":"148977","1849":"1000000000","1850":"10000000000","1851":"1000000000","1852":"57386799","1853":"52000","1854":"100000000","1855":null,"1856":"60296840158.155594","1857":null,"1858":"5709763023","1859":"400000000","1860":"10821858784","1861":"400000000","1862":"22582025","1863":"10000000","1864":"10000000000","1865":"650000000","1866":"10000000","1867":"302","1868":"9843782.3912809","1869":"140000","1870":"28000","1871":"14000","1872":"0","1873":"300000000","1874":"100000000","1875":"50000000000","1876":"1262219478","1877":"24937.97","1878":"10000000000","1879":"6540888","1880":"1103303471.382273","1881":"1140265783.0433967","1882":"2648654.44990445","1883":"10000000000","1884":"8487614","1885":"554999999.186191","1886":"719446094.8060437","1887":"100000000","1888":"20000","1889":"24000000","1890":"0","1891":"200287810","1892":"200000000","1893":"18195556128","1894":"250000000","1895":"0","1896":"100000000","1897":"578594.24583325","1898":"0","1899":"0","1900":"10000000","1901":"47650","1902":"2141106652.032967","1903":"91227.38121537","1904":"9998686","1905":"9078205.50765607","1906":"200000000","1907":"318019580","1908":"888888888","1909":"1000000000","1910":"88928379.73944187","1911":"10000000","1912":"29960733","1913":"0","1914":"640945052.695864","1915":"199104720","1916":"5033753055","1917":"223534822.66102275","1918":"5774714616","1919":"591929.20666767","1920":"299792458","1921":"25000000","1922":"0","1923":"100000000","1924":"0","1925":"100000000","1926":"23016298","1927":"15000000","1928":"120000000","1929":"50000000","1930":"6000000000","1931":"28000","1932":"96381237.9575625","1933":"1921","1934":"692613.66","1935":"703259044.9235393","1936":"100000000","1937":"650000000","1938":"0","1939":"21000","1940":"217557153.83","1941":"99700000","1942":"5047558527.7","1943":"1000000000","1944":"8386162.48266532","1945":"695658160","1946":"18473023.48323413","1947":"777777777","1948":"19900730","1949":"0","1950":"0","1951":"73545908892348","1952":"10000000","1953":"2250000000","1954":"1000000000","1955":"0","1956":"11037567.272727","1957":"500000000","1958":"963148.10248","1959":"14761081.52830862","1960":"50000000","1961":"5000000","1962":"10000000000","1963":"4987901389.94","1964":"100000000","1965":"100000000","1966":"975000000","1967":"167234716.101352","1968":"1300000000","1969":"1751448","1970":"3000000000","1971":"230000000","1972":"480679.61945696","1973":"1000000000","1974":"50000000","1975":"10000000","1976":"33000","1977":"300000000","1978":"6000","1979":"40000000","1980":"84918596","1981":"7967988929.50477","1982":"208189566.9933974","1983":"96504599.33609451","1984":"3427418.78058595","1985":"199884","1986":"105843377.99716738","1987":"100000000","1988":"558983.42579149","1989":"10000000000000000","1990":"20999763.5577","1991":"3600000000","1992":"728413075.7510512","1993":"10000000000","1994":"30000","1995":"26424.5656228","1996":"181501.23217777","1997":"3000000000","1998":"2193561740.574438","1999":"99995528","2000":"69590.65623104","2001":"1000000000","2002":"124487479.936901","2003":"86105381.278134","2004":"88888888","2005":"140245398.24513277","2006":"7000000","2007":"17804214.56755905","2008":"1000000000","2009":"30000000","2010":"270050481.2686112","2011":"10000000000","2012":"10000","2013":"232922539.28600025","2014":"1000000000","2015":"100000000","2016":"0","2017":"15000000","2018":"122707502.69296218","2019":"49892351394.13943","2020":"1000001337","2021":"2650000000","2022":"80054774.51263428","2023":"824104972","2024":"6000000","2025":"0","2026":"39602.22521405","2027":"2033994","2028":"1000000","2029":"4000000","2030":"0","2031":"723701571.7997887","2032":"145879861","2033":"99207917.48398775","2034":"1000000000","2035":"1000000000","2036":"76588156.82742885","2037":"270000000","2038":"378180600","2039":"1016661676","2040":"17124457","2041":"15551000","2042":"6044999","2043":"1000000","2044":"800000","2045":"65557424","2046":"0","2047":"9096679264","2048":"2386719.67291295","2049":"22859012.33252989","2050":"10000000000","2051":"192701891.18846744","2052":"215258834.2449152","2053":"100000000","2054":"20000","2055":"11310689.72647731","2056":"8248170.48351179","2057":"450000000","2058":null,"2059":null,"2060":"50000000000","2061":null,"2062":"65610","2063":"70000000","2064":"23978311","2065":"24999999999.98308","2066":"7102886.41835725","2067":"99990001.568832","2068":"3012009888","2069":"10782297980.8","2070":"105220281","2071":"44297171.854904","2072":"9706750","2073":"75225.05730568","2074":"86939300","2075":"1974198687","2076":"48015783231","2077":"132670764299.89409","2078":"73247111.652204","2079":"63987772","2080":"8302966967","2081":"31123570","2082":"10473290000","2083":"3466457400","2084":"22815307","2085":"17922514.606585","2086":"7514364865","2087":"100000000000","2088":"637719772.761245","2089":"1000000000","2090":"83000000","2091":"191759650.75997","2092":"18432369","2093":"98212860","2094":"63499128193.01","2095":"129843505.90294932","2096":"50730352992","2097":"10000000000000","2098":"1500000000","2099":"20000000","2100":"200000000","2101":"4992280","2102":"13454447.9413708","2103":"100000000","2104":"0","2105":"225861521.29","2106":"189492897.953","2107":"4516668.74720128","2108":"111111111","2109":"44790092020.0795","2110":"19696084.7378533","2111":"2003485057.90082","2112":"49999.95339766","2113":"100000000","2114":"26282.01228713","2115":"74413301.337384","2116":"8822013.56576023","2117":"22175970.265768","2118":"150000000","2119":"13757704.8560313","2120":"438721014.20707","2121":"10606778.39712606","2122":"0","2123":"131638530.147033","2124":"555998059584","2125":"200000000000","2126":"0","2127":"100000000","2128":"73342295.93484142","2129":"77160176.76","2130":"17017723.485","2131":"67996308.80542012","2132":"270540521.4758478","2133":"13458518714.6272","2134":"22587140.37","2135":"31587682.363206","2136":"2014903576.72334","2137":"450000000","2138":"1000000000","2139":"2000000000","2140":"37500000","2141":"69838.177447","2142":"999999632.80375","2143":"1000000000","2144":"999628334.0000002","2145":"48540693.0894982","2146":"1000000000","2147":"27000000","2148":"50000000000","2149":"113215461333","2150":"14301738.33605275","2151":"2400000","2152":"132046997","2153":"191569148.63192427","2154":"16502735637.823189","2155":"0","2156":"1000000000","2157":"508628132.04107","2158":"18188752552.408165","2159":"11111111111","2160":"1000000000","2161":"1000000000","2162":"191311840.17229617","2163":"15156364.330655","2164":"25000000","2165":"1000000000","2166":"1326415282","2167":"82073165.6996422","2168":"10000000000","2169":"501505081","2170":"12456844.22253576","2171":"39609523.80952381","2172":"100000000","2173":"299995171.3053124","2174":"1200000000","2175":"1000000000","2176":"100000000","2177":"300000000","2178":"54917265.9898441","2179":"1977738957.6","2180":"8027008.285193","2181":"30000000000","2182":"6195344.246828","2183":"1801109660","2184":"300000000","2185":"10000000","2186":"13432966966.10068","2187":"177609675","2188":"8554514","2189":"200000000","2190":"998729963.595","2191":"100000000","2192":"42609099.353966","2193":"68928510.34973244","2194":"4819635323.7067","2195":"9166468.0705","2196":"2694770470","2197":"57122866.07629106","2198":"8340.684913","2199":"4200000000","2200":"163774057.19807637","2201":"214926181.030019","2202":"7367797268.15","2203":"100000000","2204":"60000","2205":"52190535.66162335","2206":"1316162.5","2207":"45630180356","2208":"1000000000","2209":"6812985457","2210":"1000001","2211":"39406759.9283204","2212":"273203440","2213":"20000000000","2214":"16045916.20395019","2215":"23972057.69436989","2216":"500000000","2217":"2231239.45628998","2218":"90000435970.37416","2219":"500000000","2220":"110017607.220524","2221":"500000000","2222":"200000000","2223":"43280999.8597271","2224":"9782678080","2225":"5803454.93497902","2226":"58000","2227":"29631448665.828377","2228":"4000000000","2229":"163087862.627","2230":"100000000","2231":"12093400","2232":"1000000000","2233":"2108509.11","2234":"14244337.5","2235":"14706517.63794122","2236":"2227955499.0044","2237":"10690595.77160164","2238":"1000000000","2239":"842535023.1675905","2240":"134558580","2241":null,"2242":"21213630.839","2243":"130721819192.0019","2244":"0","2245":"16000000","2246":"16023129469.79202","2247":"36219775370.84244","2248":"100000000","2249":"1033200000","2250":"143761098437.99847","2251":"105635268.684179","2252":null,"2253":"485535.64526896","2254":"731703275.273257","2255":"8648417.86531109","2256":"35316150","2257":"0","2258":"1000000","2259":"7103423255.19516","2260":"0","2261":"10000000","2262":"9390930.32215674","2263":"43903795596.8916","2264":"1000000000","2265":"710999305799.6914","2266":"999848780.8","2267":"1000000000","2268":"21000000","2269":"599020418.7745987","2270":"5000000","2271":"19400882350.003914","2272":"370575000","2273":"10000000000","2274":"63190435","2275":"10000000","2276":"1380688920.6442547","2277":"10454.89101693","2278":"1000000000","2279":"249511024.98987937","2280":"341721439.035334","2281":"769873000","2282":"35725000","2283":"50000000","2284":"6884560.95953039","2285":"1092844982","2286":"99995164.26708125","2287":"39375","2288":"99995244.58572492","2289":"1000000000","2290":"1122857486.3837585","2291":"40000000","2292":"1000000000","2293":"1030000000","2294":"500000000","2295":"20000000","2296":"821851429.0654694","2297":"16876778.9","2298":"10947704.54242852","2299":"12428594195.9218","2300":"2925000","2301":"3000000000","2302":"446725.774611","2303":"35331663","2304":"91000000","2305":"635982513","2306":"23368773.4","2307":"100000000","2308":"9651217.45126206","2309":"9900000","2310":"40000000","2311":"5000","2312":"124000000","2313":"1199999974","2314":"100000000","2315":"190165060","2316":"50000000","2317":"2642132373","2318":"241765911.57560712","2319":"0","2320":"250000000","2321":"0","2322":"314655930.553138","2323":"50000000","2324":"1000000000000000","2325":"35000","2326":"99118.16852074","2327":"1000000","2328":"10000000","2329":"150000000","2330":"50000000","2331":"45000000","2332":"9999337.73","2333":"1912382.81269924","2334":"100000000","2335":"2116013418.748361","2336":"61707411.97400063","2337":"10000000000","2338":"10000","2339":"100000000","2340":"312284062","2341":"100000000","2342":"48638535.4433","2343":"80000","2344":"8607383","2345":"29739166.606595","2346":"25000000","2347":"10000000","2348":"20000000","2349":"0","2350":"555719404.5221562","2351":"67722.354172","2352":"87161996.46257955","2353":"1000000000","2354":"12427854.74033085","2355":"240000000","2356":"100000000","2357":"0","2358":"71394391.64402872","2359":"15883561643.835615","2360":"500000000","2361":"1000000000","2362":"255215897.6886553","2363":"2000000000","2364":"401578117.818245","2365":"200000000","2366":"1000000000","2367":"1000000000","2368":"1000000000","2369":"50000000","2370":"5000000","2371":"1000000000","2372":"870663574","2373":"100000000","2374":"1000000000","2375":"65173519.26931319","2376":"1240459122.6883788","2377":"100413374","2378":"12106864914.2308","2379":"87568206.45076299","2380":"3825341162.13561","2381":"217216812","2382":"10250000000","2383":"7746913929","2384":"103214939480","2385":"46775855335611000","2386":"100000000","2387":"3000000000","2388":"999999999","2389":"2000000000","2390":"100000000","2391":"100000000","2392":"10000000000","2393":"100000000","2394":"75866975.705684","2395":"103781097","2396":"30000000","2397":"7200000000","2398":"10000000000","2399":"10000000000","2400":"4042931231.0575","2401":"14123768","2402":"3821636824.569313","2403":"100000000","2404":"27999998947","2405":"1000000000","2406":"325000000","2407":"990000000000","2408":"2000000000","2409":"20000000","2410":"10000000000","2411":"454278599.6636339","2412":"100000000","2413":"13615440.46392014","2414":"100000000","2415":"3726411","2416":null,"2417":"7000000000","2418":"199105531","2419":"158797646.31186867","2420":"16649243","2421":"13165018.69","2422":"10000000","2423":"412532.669469","2424":"145713.015255","2425":"8326258166.69","2426":"87481.460244","2427":"2279175680.382793","2428":"16800000000","2429":"160454123.30081597","2430":"165518370","2431":"489738178.91","2432":"20917639","2433":"1000000000","2434":null,"2435":"30000000","2436":"100000000","2437":"60400000","2438":"32575725","2439":"40000000","2440":"136913.65426127","2441":"30263013692","2442":"18402729.795","2443":"420000000","2444":"4960497045","2445":"1000000000","2446":"101676078190.20642","2447":"990000000000000","2448":"98696588852226820","2449":"40000000","2450":"6021660337260150","2451":"1000000000","2452":"6000000000","2453":"522965750.2557162","2454":"1000000000","2455":null,"2456":"114285714","2457":"30000000","2458":"95000000","2459":null,"2460":"500000000","2461":"34277702081.605","2462":"34277702081.605","2463":"9193524008","2464":"1000000000","2465":"71618033988","2466":"10000000000","2467":"18496775.16888806","2468":"1000000000","2469":"5047558528","2470":"0","2471":null,"2472":"2049945261","2473":"400000000","2474":"300000000","2475":"1000000000","2476":"25000000000","2477":"0","2478":"1000000000","2479":"10000000000","2480":"40000000000","2481":"68891354133.29164","2482":"68604208832.42886","2483":"101630208890.49464","2484":"100000000","2485":"4168952331.5495","2486":"163276974.63","2487":"1000000000","2488":"1155830.092","2489":"0","2490":"10000000000000","2491":"2000000000","2492":"1000000000"}} \ No newline at end of file diff --git a/openbb_terminal/cryptocurrency/data/yahoofinance_coins.json b/openbb_terminal/cryptocurrency/data/yahoofinance_coins.json deleted file mode 100644 index 26b4b745d8b..00000000000 --- a/openbb_terminal/cryptocurrency/data/yahoofinance_coins.json +++ /dev/null @@ -1,16797 +0,0 @@ -[ - { - "id": "AAVE-CAD", - "symbol": "aave", - "name": "Aave CAD" - }, - { - "id": "AAVE-CNY", - "symbol": "aave", - "name": "Aave CNY" - }, - { - "id": "AAVE-ETH", - "symbol": "aave", - "name": "Aave ETH" - }, - { - "id": "AAVE-EUR", - "symbol": "aave", - "name": "Aave EUR" - }, - { - "id": "AAVE-GBP", - "symbol": "aave", - "name": "Aave GBP" - }, - { - "id": "AAVE-INR", - "symbol": "aave", - "name": "Aave INR" - }, - { - "id": "AAVE-JPY", - "symbol": "aave", - "name": "Aave JPY" - }, - { - "id": "AAVE-KRW", - "symbol": "aave", - "name": "Aave KRW" - }, - { - "id": "AAVE-RUB", - "symbol": "aave", - "name": "Aave RUB" - }, - { - "id": "AAVE-USD", - "symbol": "aave", - "name": "Aave USD" - }, - { - "id": "ABBC-CAD", - "symbol": "abbc", - "name": "ABBCCoin CAD" - }, - { - "id": "ABBC-CNY", - "symbol": "abbc", - "name": "ABBCCoin CNY" - }, - { - "id": "ABBC-ETH", - "symbol": "abbc", - "name": "ABBCCoin ETH" - }, - { - "id": "ABBC-EUR", - "symbol": "abbc", - "name": "ABBCCoin EUR" - }, - { - "id": "ABBC-GBP", - "symbol": "abbc", - "name": "ABBCCoin GBP" - }, - { - "id": "ABBC-INR", - "symbol": "abbc", - "name": "ABBCCoin INR" - }, - { - "id": "ABBC-JPY", - "symbol": "abbc", - "name": "ABBCCoin JPY" - }, - { - "id": "ABBC-KRW", - "symbol": "abbc", - "name": "ABBCCoin KRW" - }, - { - "id": "ABBC-RUB", - "symbol": "abbc", - "name": "ABBCCoin RUB" - }, - { - "id": "ABBC-USD", - "symbol": "abbc", - "name": "ABBCCoin USD" - }, - { - "id": "ACH-CAD", - "symbol": "ach", - "name": "AlchemyPay CAD" - }, - { - "id": "ACH-CNY", - "symbol": "ach", - "name": "AlchemyPay CNY" - }, - { - "id": "ACH-ETH", - "symbol": "ach", - "name": "AlchemyPay ETH" - }, - { - "id": "ACH-EUR", - "symbol": "ach", - "name": "AlchemyPay EUR" - }, - { - "id": "ACH-GBP", - "symbol": "ach", - "name": "AlchemyPay GBP" - }, - { - "id": "ACH-INR", - "symbol": "ach", - "name": "AlchemyPay INR" - }, - { - "id": "ACH-JPY", - "symbol": "ach", - "name": "AlchemyPay JPY" - }, - { - "id": "ACH-KRW", - "symbol": "ach", - "name": "AlchemyPay KRW" - }, - { - "id": "ACH-RUB", - "symbol": "ach", - "name": "AlchemyPay RUB" - }, - { - "id": "ACH-USD", - "symbol": "ach", - "name": "AlchemyPay USD" - }, - { - "id": "ACT-CAD", - "symbol": "act", - "name": "Achain CAD" - }, - { - "id": "ACT-CNY", - "symbol": "act", - "name": "Achain CNY" - }, - { - "id": "ACT-ETH", - "symbol": "act", - "name": "Achain ETH" - }, - { - "id": "ACT-EUR", - "symbol": "act", - "name": "Achain EUR" - }, - { - "id": "ACT-GBP", - "symbol": "act", - "name": "Achain GBP" - }, - { - "id": "ACT-INR", - "symbol": "act", - "name": "Achain INR" - }, - { - "id": "ACT-JPY", - "symbol": "act", - "name": "Achain JPY" - }, - { - "id": "ACT-KRW", - "symbol": "act", - "name": "Achain KRW" - }, - { - "id": "ACT-RUB", - "symbol": "act", - "name": "Achain RUB" - }, - { - "id": "ACT-USD", - "symbol": "act", - "name": "Achain USD" - }, - { - "id": "ADA-AUD", - "symbol": "ada", - "name": "Cardano AUD" - }, - { - "id": "ADA-CAD", - "symbol": "ada", - "name": "Cardano CAD" - }, - { - "id": "ADA-ETH", - "symbol": "ada", - "name": "Cardano ETH" - }, - { - "id": "ADA-EUR", - "symbol": "ada", - "name": "Cardano EUR" - }, - { - "id": "ADA-GBP", - "symbol": "ada", - "name": "Cardano GBP" - }, - { - "id": "ADA-INR", - "symbol": "ada", - "name": "Cardano INR" - }, - { - "id": "ADA-JPY", - "symbol": "ada", - "name": "Cardano JPY" - }, - { - "id": "ADA-KRW", - "symbol": "ada", - "name": "Cardano KRW" - }, - { - "id": "ADA-RUB", - "symbol": "ada", - "name": "Cardano RUB" - }, - { - "id": "ADA-USD", - "symbol": "ada", - "name": "Cardano USD" - }, - { - "id": "ADK-CAD", - "symbol": "adk", - "name": "AidosKuneen CAD" - }, - { - "id": "ADK-CNY", - "symbol": "adk", - "name": "AidosKuneen CNY" - }, - { - "id": "ADK-ETH", - "symbol": "adk", - "name": "AidosKuneen ETH" - }, - { - "id": "ADK-EUR", - "symbol": "adk", - "name": "AidosKuneen EUR" - }, - { - "id": "ADK-GBP", - "symbol": "adk", - "name": "AidosKuneen GBP" - }, - { - "id": "ADK-INR", - "symbol": "adk", - "name": "AidosKuneen INR" - }, - { - "id": "ADK-JPY", - "symbol": "adk", - "name": "AidosKuneen JPY" - }, - { - "id": "ADK-KRW", - "symbol": "adk", - "name": "AidosKuneen KRW" - }, - { - "id": "ADK-RUB", - "symbol": "adk", - "name": "AidosKuneen RUB" - }, - { - "id": "ADK-USD", - "symbol": "adk", - "name": "AidosKuneen USD" - }, - { - "id": "ADX-INR", - "symbol": "adx", - "name": "AdEx INR" - }, - { - "id": "ADX-USD", - "symbol": "adx", - "name": "AdEx USD" - }, - { - "id": "AE-CAD", - "symbol": "ae", - "name": "Aeternity CAD" - }, - { - "id": "AE-CNY", - "symbol": "ae", - "name": "Aeternity CNY" - }, - { - "id": "AE-ETH", - "symbol": "ae", - "name": "Aeternity ETH" - }, - { - "id": "AE-EUR", - "symbol": "ae", - "name": "Aeternity EUR" - }, - { - "id": "AE-GBP", - "symbol": "ae", - "name": "Aeternity GBP" - }, - { - "id": "AE-INR", - "symbol": "ae", - "name": "Aeternity INR" - }, - { - "id": "AE-JPY", - "symbol": "ae", - "name": "Aeternity JPY" - }, - { - "id": "AE-KRW", - "symbol": "ae", - "name": "Aeternity KRW" - }, - { - "id": "AE-RUB", - "symbol": "ae", - "name": "Aeternity RUB" - }, - { - "id": "AE-USD", - "symbol": "ae", - "name": "Aeternity USD" - }, - { - "id": "AIB-CAD", - "symbol": "aib", - "name": "AdvancedInternetBlocks CAD" - }, - { - "id": "AIB-CNY", - "symbol": "aib", - "name": "AdvancedInternetBlocks CNY" - }, - { - "id": "AIB-ETH", - "symbol": "aib", - "name": "AdvancedInternetBlocks ETH" - }, - { - "id": "AIB-EUR", - "symbol": "aib", - "name": "AdvancedInternetBlocks EUR" - }, - { - "id": "AIB-GBP", - "symbol": "aib", - "name": "AdvancedInternetBlocks GBP" - }, - { - "id": "AIB-INR", - "symbol": "aib", - "name": "AdvancedInternetBlocks INR" - }, - { - "id": "AIB-JPY", - "symbol": "aib", - "name": "AdvancedInternetBlocks JPY" - }, - { - "id": "AIB-KRW", - "symbol": "aib", - "name": "AdvancedInternetBlocks KRW" - }, - { - "id": "AIB-RUB", - "symbol": "aib", - "name": "AdvancedInternetBlocks RUB" - }, - { - "id": "AIB-USD", - "symbol": "aib", - "name": "AdvancedInternetBlocks USD" - }, - { - "id": "AION-EUR", - "symbol": "aion", - "name": "Aion EUR" - }, - { - "id": "AION-GBP", - "symbol": "aion", - "name": "Aion GBP" - }, - { - "id": "AION-INR", - "symbol": "aion", - "name": "Aion INR" - }, - { - "id": "AION-JPY", - "symbol": "aion", - "name": "Aion JPY" - }, - { - "id": "AION-KRW", - "symbol": "aion", - "name": "Aion KRW" - }, - { - "id": "AION-RUB", - "symbol": "aion", - "name": "Aion RUB" - }, - { - "id": "AION-USD", - "symbol": "aion", - "name": "Aion USD" - }, - { - "id": "ALGO-AUD", - "symbol": "algo", - "name": "Algorand AUD" - }, - { - "id": "ALGO-BTC", - "symbol": "algo", - "name": "Algorand BTC" - }, - { - "id": "ALGO-CAD", - "symbol": "algo", - "name": "Algorand CAD" - }, - { - "id": "ALGO-CNY", - "symbol": "algo", - "name": "Algorand CNY" - }, - { - "id": "ALGO-ETH", - "symbol": "algo", - "name": "Algorand ETH" - }, - { - "id": "ALGO-EUR", - "symbol": "algo", - "name": "Algorand EUR" - }, - { - "id": "ALGO-GBP", - "symbol": "algo", - "name": "Algorand GBP" - }, - { - "id": "ALGO-INR", - "symbol": "algo", - "name": "Algorand INR" - }, - { - "id": "ALGO-JPY", - "symbol": "algo", - "name": "Algorand JPY" - }, - { - "id": "ALGO-KRW", - "symbol": "algo", - "name": "Algorand KRW" - }, - { - "id": "ALGO-RUB", - "symbol": "algo", - "name": "Algorand RUB" - }, - { - "id": "ALGO-USD", - "symbol": "algo", - "name": "Algorand USD" - }, - { - "id": "ALIAS-CAD", - "symbol": "alias", - "name": "Alias CAD" - }, - { - "id": "ALIAS-CNY", - "symbol": "alias", - "name": "Alias CNY" - }, - { - "id": "ALIAS-ETH", - "symbol": "alias", - "name": "Alias ETH" - }, - { - "id": "ALIAS-EUR", - "symbol": "alias", - "name": "Alias EUR" - }, - { - "id": "ALIAS-GBP", - "symbol": "alias", - "name": "Alias GBP" - }, - { - "id": "ALIAS-INR", - "symbol": "alias", - "name": "Alias INR" - }, - { - "id": "ALIAS-JPY", - "symbol": "alias", - "name": "Alias JPY" - }, - { - "id": "ALIAS-KRW", - "symbol": "alias", - "name": "Alias KRW" - }, - { - "id": "ALIAS-USD", - "symbol": "alias", - "name": "Alias USD" - }, - { - "id": "AMB-BTC", - "symbol": "amb", - "name": "Ambrosus BTC" - }, - { - "id": "AMB-CAD", - "symbol": "amb", - "name": "Ambrosus CAD" - }, - { - "id": "AMB-CNY", - "symbol": "amb", - "name": "Ambrosus CNY" - }, - { - "id": "AMB-ETH", - "symbol": "amb", - "name": "Ambrosus ETH" - }, - { - "id": "AMB-EUR", - "symbol": "amb", - "name": "Ambrosus EUR" - }, - { - "id": "AMB-GBP", - "symbol": "amb", - "name": "Ambrosus GBP" - }, - { - "id": "AMB-INR", - "symbol": "amb", - "name": "Ambrosus INR" - }, - { - "id": "AMB-JPY", - "symbol": "amb", - "name": "Ambrosus JPY" - }, - { - "id": "AMB-KRW", - "symbol": "amb", - "name": "Ambrosus KRW" - }, - { - "id": "AMB-RUB", - "symbol": "amb", - "name": "Ambrosus RUB" - }, - { - "id": "AMB-USD", - "symbol": "amb", - "name": "Ambrosus USD" - }, - { - "id": "AMP1-CAD", - "symbol": "amp1", - "name": "Amp CAD" - }, - { - "id": "AMP1-CNY", - "symbol": "amp1", - "name": "Amp CNY" - }, - { - "id": "AMP1-ETH", - "symbol": "amp1", - "name": "Amp ETH" - }, - { - "id": "AMP1-EUR", - "symbol": "amp1", - "name": "Amp EUR" - }, - { - "id": "AMP1-GBP", - "symbol": "amp1", - "name": "Amp GBP" - }, - { - "id": "AMP1-INR", - "symbol": "amp1", - "name": "Amp INR" - }, - { - "id": "AMP1-JPY", - "symbol": "amp1", - "name": "Amp JPY" - }, - { - "id": "AMP1-KRW", - "symbol": "amp1", - "name": "Amp KRW" - }, - { - "id": "AMP1-RUB", - "symbol": "amp1", - "name": "Amp RUB" - }, - { - "id": "AMP1-USD", - "symbol": "amp1", - "name": "Amp USD" - }, - { - "id": "ANKR-CAD", - "symbol": "ankr", - "name": "Ankr CAD" - }, - { - "id": "ANKR-CNY", - "symbol": "ankr", - "name": "Ankr CNY" - }, - { - "id": "ANKR-ETH", - "symbol": "ankr", - "name": "Ankr ETH" - }, - { - "id": "ANKR-EUR", - "symbol": "ankr", - "name": "Ankr EUR" - }, - { - "id": "ANKR-GBP", - "symbol": "ankr", - "name": "Ankr GBP" - }, - { - "id": "ANKR-INR", - "symbol": "ankr", - "name": "Ankr INR" - }, - { - "id": "ANKR-JPY", - "symbol": "ankr", - "name": "Ankr JPY" - }, - { - "id": "ANKR-KRW", - "symbol": "ankr", - "name": "Ankr KRW" - }, - { - "id": "ANKR-RUB", - "symbol": "ankr", - "name": "Ankr RUB" - }, - { - "id": "ANKR-USD", - "symbol": "ankr", - "name": "Ankr USD" - }, - { - "id": "ANT-ETH", - "symbol": "ant", - "name": "Aragon ETH" - }, - { - "id": "ANT-INR", - "symbol": "ant", - "name": "Aragon INR" - }, - { - "id": "ANT-JPY", - "symbol": "ant", - "name": "Aragon JPY" - }, - { - "id": "ANT-KRW", - "symbol": "ant", - "name": "Aragon KRW" - }, - { - "id": "ANT-RUB", - "symbol": "ant", - "name": "Aragon RUB" - }, - { - "id": "ANT-USD", - "symbol": "ant", - "name": "Aragon USD" - }, - { - "id": "APL-AUD", - "symbol": "apl", - "name": "ApolloCurrency AUD" - }, - { - "id": "APL-BTC", - "symbol": "apl", - "name": "ApolloCurrency BTC" - }, - { - "id": "APL-CAD", - "symbol": "apl", - "name": "ApolloCurrency CAD" - }, - { - "id": "APL-CNY", - "symbol": "apl", - "name": "ApolloCurrency CNY" - }, - { - "id": "APL-ETH", - "symbol": "apl", - "name": "ApolloCurrency ETH" - }, - { - "id": "APL-EUR", - "symbol": "apl", - "name": "ApolloCurrency EUR" - }, - { - "id": "APL-GBP", - "symbol": "apl", - "name": "ApolloCurrency GBP" - }, - { - "id": "APL-INR", - "symbol": "apl", - "name": "ApolloCurrency INR" - }, - { - "id": "APL-JPY", - "symbol": "apl", - "name": "ApolloCurrency JPY" - }, - { - "id": "APL-KRW", - "symbol": "apl", - "name": "ApolloCurrency KRW" - }, - { - "id": "APL-RUB", - "symbol": "apl", - "name": "ApolloCurrency RUB" - }, - { - "id": "APL-USD", - "symbol": "apl", - "name": "ApolloCurrency USD" - }, - { - "id": "AR-CAD", - "symbol": "ar", - "name": "Arweave CAD" - }, - { - "id": "AR-CNY", - "symbol": "ar", - "name": "Arweave CNY" - }, - { - "id": "AR-ETH", - "symbol": "ar", - "name": "Arweave ETH" - }, - { - "id": "AR-EUR", - "symbol": "ar", - "name": "Arweave EUR" - }, - { - "id": "AR-GBP", - "symbol": "ar", - "name": "Arweave GBP" - }, - { - "id": "AR-INR", - "symbol": "ar", - "name": "Arweave INR" - }, - { - "id": "AR-JPY", - "symbol": "ar", - "name": "Arweave JPY" - }, - { - "id": "AR-KRW", - "symbol": "ar", - "name": "Arweave KRW" - }, - { - "id": "AR-RUB", - "symbol": "ar", - "name": "Arweave RUB" - }, - { - "id": "AR-USD", - "symbol": "ar", - "name": "Arweave USD" - }, - { - "id": "ARDR-CAD", - "symbol": "ardr", - "name": "Ardor CAD" - }, - { - "id": "ARDR-CNY", - "symbol": "ardr", - "name": "Ardor CNY" - }, - { - "id": "ARDR-ETH", - "symbol": "ardr", - "name": "Ardor ETH" - }, - { - "id": "ARDR-EUR", - "symbol": "ardr", - "name": "Ardor EUR" - }, - { - "id": "ARDR-GBP", - "symbol": "ardr", - "name": "Ardor GBP" - }, - { - "id": "ARDR-INR", - "symbol": "ardr", - "name": "Ardor INR" - }, - { - "id": "ARDR-JPY", - "symbol": "ardr", - "name": "Ardor JPY" - }, - { - "id": "ARDR-KRW", - "symbol": "ardr", - "name": "Ardor KRW" - }, - { - "id": "ARDR-RUB", - "symbol": "ardr", - "name": "Ardor RUB" - }, - { - "id": "ARDR-USD", - "symbol": "ardr", - "name": "Ardor USD" - }, - { - "id": "ARRR-AUD", - "symbol": "arrr", - "name": "PirateChain AUD" - }, - { - "id": "ARRR-BTC", - "symbol": "arrr", - "name": "PirateChain BTC" - }, - { - "id": "ARRR-CAD", - "symbol": "arrr", - "name": "PirateChain CAD" - }, - { - "id": "ARRR-CNY", - "symbol": "arrr", - "name": "PirateChain CNY" - }, - { - "id": "ARRR-ETH", - "symbol": "arrr", - "name": "PirateChain ETH" - }, - { - "id": "ARRR-EUR", - "symbol": "arrr", - "name": "PirateChain EUR" - }, - { - "id": "ARRR-GBP", - "symbol": "arrr", - "name": "PirateChain GBP" - }, - { - "id": "ARRR-INR", - "symbol": "arrr", - "name": "PirateChain INR" - }, - { - "id": "ARRR-JPY", - "symbol": "arrr", - "name": "PirateChain JPY" - }, - { - "id": "ARRR-KRW", - "symbol": "arrr", - "name": "PirateChain KRW" - }, - { - "id": "ARRR-RUB", - "symbol": "arrr", - "name": "PirateChain RUB" - }, - { - "id": "ARRR-USD", - "symbol": "arrr", - "name": "PirateChain USD" - }, - { - "id": "ATB-AUD", - "symbol": "atb", - "name": "ATBCoin AUD" - }, - { - "id": "ATB-BTC", - "symbol": "atb", - "name": "ATBCoin BTC" - }, - { - "id": "ATB-ETH", - "symbol": "atb", - "name": "ATBCoin ETH" - }, - { - "id": "ATB-EUR", - "symbol": "atb", - "name": "ATBCoin EUR" - }, - { - "id": "ATB-GBP", - "symbol": "atb", - "name": "ATBCoin GBP" - }, - { - "id": "ATB-INR", - "symbol": "atb", - "name": "ATBCoin INR" - }, - { - "id": "ATB-JPY", - "symbol": "atb", - "name": "ATBCoin JPY" - }, - { - "id": "ATB-KRW", - "symbol": "atb", - "name": "ATBCoin KRW" - }, - { - "id": "ATB-RUB", - "symbol": "atb", - "name": "ATBCoin RUB" - }, - { - "id": "ATB-USD", - "symbol": "atb", - "name": "ATBCoin USD" - }, - { - "id": "ATOM1-CAD", - "symbol": "atom1", - "name": "Cosmos CAD" - }, - { - "id": "ATOM1-CNY", - "symbol": "atom1", - "name": "Cosmos CNY" - }, - { - "id": "ATOM1-ETH", - "symbol": "atom1", - "name": "Cosmos ETH" - }, - { - "id": "ATOM1-EUR", - "symbol": "atom1", - "name": "Cosmos EUR" - }, - { - "id": "ATOM1-GBP", - "symbol": "atom1", - "name": "Cosmos GBP" - }, - { - "id": "ATOM1-INR", - "symbol": "atom1", - "name": "Cosmos INR" - }, - { - "id": "ATOM1-JPY", - "symbol": "atom1", - "name": "Cosmos JPY" - }, - { - "id": "ATOM1-KRW", - "symbol": "atom1", - "name": "Cosmos KRW" - }, - { - "id": "ATOM1-RUB", - "symbol": "atom1", - "name": "Cosmos RUB" - }, - { - "id": "ATOM1-USD", - "symbol": "atom1", - "name": "Cosmos USD" - }, - { - "id": "ATRI-CAD", - "symbol": "atri", - "name": "AtariToken CAD" - }, - { - "id": "ATRI-CNY", - "symbol": "atri", - "name": "AtariToken CNY" - }, - { - "id": "ATRI-ETH", - "symbol": "atri", - "name": "AtariToken ETH" - }, - { - "id": "ATRI-EUR", - "symbol": "atri", - "name": "AtariToken EUR" - }, - { - "id": "ATRI-GBP", - "symbol": "atri", - "name": "AtariToken GBP" - }, - { - "id": "ATRI-INR", - "symbol": "atri", - "name": "AtariToken INR" - }, - { - "id": "ATRI-JPY", - "symbol": "atri", - "name": "AtariToken JPY" - }, - { - "id": "ATRI-KRW", - "symbol": "atri", - "name": "AtariToken KRW" - }, - { - "id": "ATRI-RUB", - "symbol": "atri", - "name": "AtariToken RUB" - }, - { - "id": "ATRI-USD", - "symbol": "atri", - "name": "AtariToken USD" - }, - { - "id": "AVA-AUD", - "symbol": "ava", - "name": "Travalacom AUD" - }, - { - "id": "AVA-CAD", - "symbol": "ava", - "name": "Travalacom CAD" - }, - { - "id": "AVA-CNY", - "symbol": "ava", - "name": "Travalacom CNY" - }, - { - "id": "AVA-ETH", - "symbol": "ava", - "name": "Travalacom ETH" - }, - { - "id": "AVA-EUR", - "symbol": "ava", - "name": "Travalacom EUR" - }, - { - "id": "AVA-GBP", - "symbol": "ava", - "name": "Travalacom GBP" - }, - { - "id": "AVA-INR", - "symbol": "ava", - "name": "Travalacom INR" - }, - { - "id": "AVA-JPY", - "symbol": "ava", - "name": "Travalacom JPY" - }, - { - "id": "AVA-KRW", - "symbol": "ava", - "name": "Travalacom KRW" - }, - { - "id": "AVA-RUB", - "symbol": "ava", - "name": "Travalacom RUB" - }, - { - "id": "AVA-USD", - "symbol": "ava", - "name": "Travalacom USD" - }, - { - "id": "AVAX-CAD", - "symbol": "avax", - "name": "Avalanche CAD" - }, - { - "id": "AVAX-CNY", - "symbol": "avax", - "name": "Avalanche CNY" - }, - { - "id": "AVAX-ETH", - "symbol": "avax", - "name": "Avalanche ETH" - }, - { - "id": "AVAX-EUR", - "symbol": "avax", - "name": "Avalanche EUR" - }, - { - "id": "AVAX-GBP", - "symbol": "avax", - "name": "Avalanche GBP" - }, - { - "id": "AVAX-INR", - "symbol": "avax", - "name": "Avalanche INR" - }, - { - "id": "AVAX-JPY", - "symbol": "avax", - "name": "Avalanche JPY" - }, - { - "id": "AVAX-KRW", - "symbol": "avax", - "name": "Avalanche KRW" - }, - { - "id": "AVAX-RUB", - "symbol": "avax", - "name": "Avalanche RUB" - }, - { - "id": "AVAX-USD", - "symbol": "avax", - "name": "Avalanche USD" - }, - { - "id": "AXEL-EUR", - "symbol": "axel", - "name": "AXEL EUR" - }, - { - "id": "AXEL-GBP", - "symbol": "axel", - "name": "AXEL GBP" - }, - { - "id": "AXEL-INR", - "symbol": "axel", - "name": "AXEL INR" - }, - { - "id": "AXEL-JPY", - "symbol": "axel", - "name": "AXEL JPY" - }, - { - "id": "AXEL-KRW", - "symbol": "axel", - "name": "AXEL KRW" - }, - { - "id": "AXEL-RUB", - "symbol": "axel", - "name": "AXEL RUB" - }, - { - "id": "AXEL-USD", - "symbol": "axel", - "name": "AXEL USD" - }, - { - "id": "BAND-CAD", - "symbol": "band", - "name": "BandProtocol CAD" - }, - { - "id": "BAND-CNY", - "symbol": "band", - "name": "BandProtocol CNY" - }, - { - "id": "BAND-ETH", - "symbol": "band", - "name": "BandProtocol ETH" - }, - { - "id": "BAND-EUR", - "symbol": "band", - "name": "BandProtocol EUR" - }, - { - "id": "BAND-GBP", - "symbol": "band", - "name": "BandProtocol GBP" - }, - { - "id": "BAND-INR", - "symbol": "band", - "name": "BandProtocol INR" - }, - { - "id": "BAND-JPY", - "symbol": "band", - "name": "BandProtocol JPY" - }, - { - "id": "BAND-KRW", - "symbol": "band", - "name": "BandProtocol KRW" - }, - { - "id": "BAND-RUB", - "symbol": "band", - "name": "BandProtocol RUB" - }, - { - "id": "BAND-USD", - "symbol": "band", - "name": "BandProtocol USD" - }, - { - "id": "BAT-AUD", - "symbol": "bat", - "name": "BasicAttentionToken AUD" - }, - { - "id": "BAT-BTC", - "symbol": "bat", - "name": "BasicAttentionToken BTC" - }, - { - "id": "BAT-CAD", - "symbol": "bat", - "name": "BasicAttentionToken CAD" - }, - { - "id": "BAT-CNY", - "symbol": "bat", - "name": "BasicAttentionToken CNY" - }, - { - "id": "BAT-ETH", - "symbol": "bat", - "name": "BasicAttentionToken ETH" - }, - { - "id": "BAT-EUR", - "symbol": "bat", - "name": "BasicAttentionToken EUR" - }, - { - "id": "BAT-GBP", - "symbol": "bat", - "name": "BasicAttentionToken GBP" - }, - { - "id": "BAT-INR", - "symbol": "bat", - "name": "BasicAttentionToken INR" - }, - { - "id": "BAT-JPY", - "symbol": "bat", - "name": "BasicAttentionToken JPY" - }, - { - "id": "BAT-KRW", - "symbol": "bat", - "name": "BasicAttentionToken KRW" - }, - { - "id": "BAT-RUB", - "symbol": "bat", - "name": "BasicAttentionToken RUB" - }, - { - "id": "BAT-USD", - "symbol": "bat", - "name": "BasicAttentionToken USD" - }, - { - "id": "BTC-USD", - "symbol": "btc", - "name": "Bitcoin USD" - }, - { - "id": "BTC-EUR", - "symbol": "btc", - "name": "Bitcoin EUR" - }, - { - "id": "BCA-CAD", - "symbol": "bca", - "name": "BitcoinAtom CAD" - }, - { - "id": "BCA-CNY", - "symbol": "bca", - "name": "BitcoinAtom CNY" - }, - { - "id": "BCA-ETH", - "symbol": "bca", - "name": "BitcoinAtom ETH" - }, - { - "id": "BCA-EUR", - "symbol": "bca", - "name": "BitcoinAtom EUR" - }, - { - "id": "BCA-GBP", - "symbol": "bca", - "name": "BitcoinAtom GBP" - }, - { - "id": "BCA-INR", - "symbol": "bca", - "name": "BitcoinAtom INR" - }, - { - "id": "BCA-JPY", - "symbol": "bca", - "name": "BitcoinAtom JPY" - }, - { - "id": "BCA-KRW", - "symbol": "bca", - "name": "BitcoinAtom KRW" - }, - { - "id": "BCA-RUB", - "symbol": "bca", - "name": "BitcoinAtom RUB" - }, - { - "id": "BCA-USD", - "symbol": "bca", - "name": "BitcoinAtom USD" - }, - { - "id": "BCD-BTC", - "symbol": "bcd", - "name": "BitcoinDiamond BTC" - }, - { - "id": "BCD-CAD", - "symbol": "bcd", - "name": "BitcoinDiamond CAD" - }, - { - "id": "BCD-ETH", - "symbol": "bcd", - "name": "BitcoinDiamond ETH" - }, - { - "id": "BCD-EUR", - "symbol": "bcd", - "name": "BitcoinDiamond EUR" - }, - { - "id": "BCD-GBP", - "symbol": "bcd", - "name": "BitcoinDiamond GBP" - }, - { - "id": "BCD-INR", - "symbol": "bcd", - "name": "BitcoinDiamond INR" - }, - { - "id": "BCD-JPY", - "symbol": "bcd", - "name": "BitcoinDiamond JPY" - }, - { - "id": "BCD-KRW", - "symbol": "bcd", - "name": "BitcoinDiamond KRW" - }, - { - "id": "BCD-RUB", - "symbol": "bcd", - "name": "BitcoinDiamond RUB" - }, - { - "id": "BCD-USD", - "symbol": "bcd", - "name": "BitcoinDiamond USD" - }, - { - "id": "BCH-AUD", - "symbol": "bch", - "name": "BitcoinCash AUD" - }, - { - "id": "BCH-BTC", - "symbol": "bch", - "name": "BitcoinCash BTC" - }, - { - "id": "BCH-CAD", - "symbol": "bch", - "name": "BitcoinCash CAD" - }, - { - "id": "BCH-CNY", - "symbol": "bch", - "name": "BitcoinCash CNY" - }, - { - "id": "BCH-ETH", - "symbol": "bch", - "name": "BitcoinCash ETH" - }, - { - "id": "BCH-EUR", - "symbol": "bch", - "name": "BitcoinCash EUR" - }, - { - "id": "BCH-GBP", - "symbol": "bch", - "name": "BitcoinCash GBP" - }, - { - "id": "BCH-INR", - "symbol": "bch", - "name": "BitcoinCash INR" - }, - { - "id": "BCH-JPY", - "symbol": "bch", - "name": "BitcoinCash JPY" - }, - { - "id": "BCH-KRW", - "symbol": "bch", - "name": "BitcoinCash KRW" - }, - { - "id": "BCH-RUB", - "symbol": "bch", - "name": "BitcoinCash RUB" - }, - { - "id": "BCH-USD", - "symbol": "bch", - "name": "BitcoinCash USD" - }, - { - "id": "BCN-AUD", - "symbol": "bcn", - "name": "Bytecoin AUD" - }, - { - "id": "BCN-BTC", - "symbol": "bcn", - "name": "Bytecoin BTC" - }, - { - "id": "BCN-CAD", - "symbol": "bcn", - "name": "Bytecoin CAD" - }, - { - "id": "BCN-ETH", - "symbol": "bcn", - "name": "Bytecoin ETH" - }, - { - "id": "BCN-EUR", - "symbol": "bcn", - "name": "Bytecoin EUR" - }, - { - "id": "BCN-GBP", - "symbol": "bcn", - "name": "Bytecoin GBP" - }, - { - "id": "BCN-INR", - "symbol": "bcn", - "name": "Bytecoin INR" - }, - { - "id": "BCN-JPY", - "symbol": "bcn", - "name": "Bytecoin JPY" - }, - { - "id": "BCN-KRW", - "symbol": "bcn", - "name": "Bytecoin KRW" - }, - { - "id": "BCN-RUB", - "symbol": "bcn", - "name": "Bytecoin RUB" - }, - { - "id": "BCN-USD", - "symbol": "bcn", - "name": "Bytecoin USD" - }, - { - "id": "BDX-CAD", - "symbol": "bdx", - "name": "Beldex CAD" - }, - { - "id": "BDX-CNY", - "symbol": "bdx", - "name": "Beldex CNY" - }, - { - "id": "BDX-ETH", - "symbol": "bdx", - "name": "Beldex ETH" - }, - { - "id": "BDX-EUR", - "symbol": "bdx", - "name": "Beldex EUR" - }, - { - "id": "BDX-GBP", - "symbol": "bdx", - "name": "Beldex GBP" - }, - { - "id": "BDX-INR", - "symbol": "bdx", - "name": "Beldex INR" - }, - { - "id": "BDX-JPY", - "symbol": "bdx", - "name": "Beldex JPY" - }, - { - "id": "BDX-KRW", - "symbol": "bdx", - "name": "Beldex KRW" - }, - { - "id": "BDX-RUB", - "symbol": "bdx", - "name": "Beldex RUB" - }, - { - "id": "BDX-USD", - "symbol": "bdx", - "name": "Beldex USD" - }, - { - "id": "BEPRO-CAD", - "symbol": "bepro", - "name": "BetProtocol CAD" - }, - { - "id": "BEPRO-CNY", - "symbol": "bepro", - "name": "BetProtocol CNY" - }, - { - "id": "BEPRO-ETH", - "symbol": "bepro", - "name": "BetProtocol ETH" - }, - { - "id": "BEPRO-EUR", - "symbol": "bepro", - "name": "BetProtocol EUR" - }, - { - "id": "BEPRO-GBP", - "symbol": "bepro", - "name": "BetProtocol GBP" - }, - { - "id": "BEPRO-INR", - "symbol": "bepro", - "name": "BetProtocol INR" - }, - { - "id": "BEPRO-JPY", - "symbol": "bepro", - "name": "BetProtocol JPY" - }, - { - "id": "BEPRO-KRW", - "symbol": "bepro", - "name": "BetProtocol KRW" - }, - { - "id": "BEPRO-RUB", - "symbol": "bepro", - "name": "BetProtocol RUB" - }, - { - "id": "BEPRO-USD", - "symbol": "bepro", - "name": "BetProtocol USD" - }, - { - "id": "BHD-CAD", - "symbol": "bhd", - "name": "BitcoinHD CAD" - }, - { - "id": "BHD-CNY", - "symbol": "bhd", - "name": "BitcoinHD CNY" - }, - { - "id": "BHD-ETH", - "symbol": "bhd", - "name": "BitcoinHD ETH" - }, - { - "id": "BHD-EUR", - "symbol": "bhd", - "name": "BitcoinHD EUR" - }, - { - "id": "BHD-GBP", - "symbol": "bhd", - "name": "BitcoinHD GBP" - }, - { - "id": "BHD-INR", - "symbol": "bhd", - "name": "BitcoinHD INR" - }, - { - "id": "BHD-JPY", - "symbol": "bhd", - "name": "BitcoinHD JPY" - }, - { - "id": "BHD-KRW", - "symbol": "bhd", - "name": "BitcoinHD KRW" - }, - { - "id": "BHD-RUB", - "symbol": "bhd", - "name": "BitcoinHD RUB" - }, - { - "id": "BHD-USD", - "symbol": "bhd", - "name": "BitcoinHD USD" - }, - { - "id": "BHP-CAD", - "symbol": "bhp", - "name": "BHPCoin CAD" - }, - { - "id": "BHP-CNY", - "symbol": "bhp", - "name": "BHPCoin CNY" - }, - { - "id": "BHP-ETH", - "symbol": "bhp", - "name": "BHPCoin ETH" - }, - { - "id": "BHP-EUR", - "symbol": "bhp", - "name": "BHPCoin EUR" - }, - { - "id": "BHP-GBP", - "symbol": "bhp", - "name": "BHPCoin GBP" - }, - { - "id": "BHP-INR", - "symbol": "bhp", - "name": "BHPCoin INR" - }, - { - "id": "BHP-JPY", - "symbol": "bhp", - "name": "BHPCoin JPY" - }, - { - "id": "BHP-KRW", - "symbol": "bhp", - "name": "BHPCoin KRW" - }, - { - "id": "BHP-RUB", - "symbol": "bhp", - "name": "BHPCoin RUB" - }, - { - "id": "BHP-USD", - "symbol": "bhp", - "name": "BHPCoin USD" - }, - { - "id": "BIP-BTC", - "symbol": "bip", - "name": "MinterNetwork BTC" - }, - { - "id": "BIP-CAD", - "symbol": "bip", - "name": "MinterNetwork CAD" - }, - { - "id": "BIP-CNY", - "symbol": "bip", - "name": "MinterNetwork CNY" - }, - { - "id": "BIP-ETH", - "symbol": "bip", - "name": "MinterNetwork ETH" - }, - { - "id": "BIP-EUR", - "symbol": "bip", - "name": "MinterNetwork EUR" - }, - { - "id": "BIP-GBP", - "symbol": "bip", - "name": "MinterNetwork GBP" - }, - { - "id": "BIP-INR", - "symbol": "bip", - "name": "MinterNetwork INR" - }, - { - "id": "BIP-JPY", - "symbol": "bip", - "name": "MinterNetwork JPY" - }, - { - "id": "BIP-KRW", - "symbol": "bip", - "name": "MinterNetwork KRW" - }, - { - "id": "BIP-RUB", - "symbol": "bip", - "name": "MinterNetwork RUB" - }, - { - "id": "BIP-USD", - "symbol": "bip", - "name": "MinterNetwork USD" - }, - { - "id": "BLK-BTC", - "symbol": "blk", - "name": "BlackCoin BTC" - }, - { - "id": "BLK-CAD", - "symbol": "blk", - "name": "BlackCoin CAD" - }, - { - "id": "BLK-CNY", - "symbol": "blk", - "name": "BlackCoin CNY" - }, - { - "id": "BLK-ETH", - "symbol": "blk", - "name": "BlackCoin ETH" - }, - { - "id": "BLK-EUR", - "symbol": "blk", - "name": "BlackCoin EUR" - }, - { - "id": "BLK-GBP", - "symbol": "blk", - "name": "BlackCoin GBP" - }, - { - "id": "BLK-INR", - "symbol": "blk", - "name": "BlackCoin INR" - }, - { - "id": "BLK-JPY", - "symbol": "blk", - "name": "BlackCoin JPY" - }, - { - "id": "BLK-KRW", - "symbol": "blk", - "name": "BlackCoin KRW" - }, - { - "id": "BLK-RUB", - "symbol": "blk", - "name": "BlackCoin RUB" - }, - { - "id": "BLK-USD", - "symbol": "blk", - "name": "BlackCoin USD" - }, - { - "id": "BLOCK-AUD", - "symbol": "block", - "name": "Blocknet AUD" - }, - { - "id": "BLOCK-BTC", - "symbol": "block", - "name": "Blocknet BTC" - }, - { - "id": "BLOCK-CAD", - "symbol": "block", - "name": "Blocknet CAD" - }, - { - "id": "BLOCK-CNY", - "symbol": "block", - "name": "Blocknet CNY" - }, - { - "id": "BLOCK-ETH", - "symbol": "block", - "name": "Blocknet ETH" - }, - { - "id": "BLOCK-EUR", - "symbol": "block", - "name": "Blocknet EUR" - }, - { - "id": "BLOCK-GBP", - "symbol": "block", - "name": "Blocknet GBP" - }, - { - "id": "BLOCK-INR", - "symbol": "block", - "name": "Blocknet INR" - }, - { - "id": "BLOCK-RUB", - "symbol": "block", - "name": "Blocknet RUB" - }, - { - "id": "BLOCK-USD", - "symbol": "block", - "name": "Blocknet USD" - }, - { - "id": "BNB-AUD", - "symbol": "bnb", - "name": "BinanceCoin AUD" - }, - { - "id": "BNB-BTC", - "symbol": "bnb", - "name": "BinanceCoin BTC" - }, - { - "id": "BNB-CAD", - "symbol": "bnb", - "name": "BinanceCoin CAD" - }, - { - "id": "BNB-CNY", - "symbol": "bnb", - "name": "BinanceCoin CNY" - }, - { - "id": "BNB-ETH", - "symbol": "bnb", - "name": "BinanceCoin ETH" - }, - { - "id": "BNB-EUR", - "symbol": "bnb", - "name": "BinanceCoin EUR" - }, - { - "id": "BNB-GBP", - "symbol": "bnb", - "name": "BinanceCoin GBP" - }, - { - "id": "BNB-INR", - "symbol": "bnb", - "name": "BinanceCoin INR" - }, - { - "id": "BNB-JPY", - "symbol": "bnb", - "name": "BinanceCoin JPY" - }, - { - "id": "BNB-KRW", - "symbol": "bnb", - "name": "BinanceCoin KRW" - }, - { - "id": "BNB-RUB", - "symbol": "bnb", - "name": "BinanceCoin RUB" - }, - { - "id": "BNB-USD", - "symbol": "bnb", - "name": "BinanceCoin USD" - }, - { - "id": "BNT-ETH", - "symbol": "bnt", - "name": "Bancor ETH" - }, - { - "id": "BNT-USD", - "symbol": "bnt", - "name": "Bancor USD" - }, - { - "id": "BONO-CAD", - "symbol": "bono", - "name": "Bonorum CAD" - }, - { - "id": "BONO-CNY", - "symbol": "bono", - "name": "Bonorum CNY" - }, - { - "id": "BONO-ETH", - "symbol": "bono", - "name": "Bonorum ETH" - }, - { - "id": "BONO-EUR", - "symbol": "bono", - "name": "Bonorum EUR" - }, - { - "id": "BONO-GBP", - "symbol": "bono", - "name": "Bonorum GBP" - }, - { - "id": "BONO-INR", - "symbol": "bono", - "name": "Bonorum INR" - }, - { - "id": "BONO-JPY", - "symbol": "bono", - "name": "Bonorum JPY" - }, - { - "id": "BONO-KRW", - "symbol": "bono", - "name": "Bonorum KRW" - }, - { - "id": "BONO-RUB", - "symbol": "bono", - "name": "Bonorum RUB" - }, - { - "id": "BONO-USD", - "symbol": "bono", - "name": "Bonorum USD" - }, - { - "id": "BPC-CAD", - "symbol": "bpc", - "name": "BackPackerCoin CAD" - }, - { - "id": "BPC-CNY", - "symbol": "bpc", - "name": "BackPackerCoin CNY" - }, - { - "id": "BPC-ETH", - "symbol": "bpc", - "name": "BackPackerCoin ETH" - }, - { - "id": "BPC-EUR", - "symbol": "bpc", - "name": "BackPackerCoin EUR" - }, - { - "id": "BPC-GBP", - "symbol": "bpc", - "name": "BackPackerCoin GBP" - }, - { - "id": "BPC-INR", - "symbol": "bpc", - "name": "BackPackerCoin INR" - }, - { - "id": "BPC-JPY", - "symbol": "bpc", - "name": "BackPackerCoin JPY" - }, - { - "id": "BPC-KRW", - "symbol": "bpc", - "name": "BackPackerCoin KRW" - }, - { - "id": "BPC-RUB", - "symbol": "bpc", - "name": "BackPackerCoin RUB" - }, - { - "id": "BPC-USD", - "symbol": "bpc", - "name": "BackPackerCoin USD" - }, - { - "id": "BPS-CAD", - "symbol": "bps", - "name": "BitcoinPoS CAD" - }, - { - "id": "BPS-CNY", - "symbol": "bps", - "name": "BitcoinPoS CNY" - }, - { - "id": "BPS-ETH", - "symbol": "bps", - "name": "BitcoinPoS ETH" - }, - { - "id": "BPS-EUR", - "symbol": "bps", - "name": "BitcoinPoS EUR" - }, - { - "id": "BPS-GBP", - "symbol": "bps", - "name": "BitcoinPoS GBP" - }, - { - "id": "BPS-INR", - "symbol": "bps", - "name": "BitcoinPoS INR" - }, - { - "id": "BPS-JPY", - "symbol": "bps", - "name": "BitcoinPoS JPY" - }, - { - "id": "BPS-KRW", - "symbol": "bps", - "name": "BitcoinPoS KRW" - }, - { - "id": "BPS-RUB", - "symbol": "bps", - "name": "BitcoinPoS RUB" - }, - { - "id": "BPS-USD", - "symbol": "bps", - "name": "BitcoinPoS USD" - }, - { - "id": "BRC-CAD", - "symbol": "brc", - "name": "BaerChain CAD" - }, - { - "id": "BRC-CNY", - "symbol": "brc", - "name": "BaerChain CNY" - }, - { - "id": "BRC-ETH", - "symbol": "brc", - "name": "BaerChain ETH" - }, - { - "id": "BRC-EUR", - "symbol": "brc", - "name": "BaerChain EUR" - }, - { - "id": "BRC-GBP", - "symbol": "brc", - "name": "BaerChain GBP" - }, - { - "id": "BRC-INR", - "symbol": "brc", - "name": "BaerChain INR" - }, - { - "id": "BRC-JPY", - "symbol": "brc", - "name": "BaerChain JPY" - }, - { - "id": "BRC-KRW", - "symbol": "brc", - "name": "BaerChain KRW" - }, - { - "id": "BRC-RUB", - "symbol": "brc", - "name": "BaerChain RUB" - }, - { - "id": "BRC-USD", - "symbol": "brc", - "name": "BaerChain USD" - }, - { - "id": "BST-CAD", - "symbol": "bst", - "name": "BlockStamp CAD" - }, - { - "id": "BST-CNY", - "symbol": "bst", - "name": "BlockStamp CNY" - }, - { - "id": "BST-ETH", - "symbol": "bst", - "name": "BlockStamp ETH" - }, - { - "id": "BST-EUR", - "symbol": "bst", - "name": "BlockStamp EUR" - }, - { - "id": "BST-GBP", - "symbol": "bst", - "name": "BlockStamp GBP" - }, - { - "id": "BST-INR", - "symbol": "bst", - "name": "BlockStamp INR" - }, - { - "id": "BST-JPY", - "symbol": "bst", - "name": "BlockStamp JPY" - }, - { - "id": "BST-KRW", - "symbol": "bst", - "name": "BlockStamp KRW" - }, - { - "id": "BST-RUB", - "symbol": "bst", - "name": "BlockStamp RUB" - }, - { - "id": "BST-USD", - "symbol": "bst", - "name": "BlockStamp USD" - }, - { - "id": "BSV-BTC", - "symbol": "bsv", - "name": "BitcoinSV BTC" - }, - { - "id": "BSV-CAD", - "symbol": "bsv", - "name": "BitcoinSV CAD" - }, - { - "id": "BSV-ETH", - "symbol": "bsv", - "name": "BitcoinSV ETH" - }, - { - "id": "BSV-EUR", - "symbol": "bsv", - "name": "BitcoinSV EUR" - }, - { - "id": "BSV-GBP", - "symbol": "bsv", - "name": "BitcoinSV GBP" - }, - { - "id": "BSV-INR", - "symbol": "bsv", - "name": "BitcoinSV INR" - }, - { - "id": "BSV-JPY", - "symbol": "bsv", - "name": "BitcoinSV JPY" - }, - { - "id": "BSV-KRW", - "symbol": "bsv", - "name": "BitcoinSV KRW" - }, - { - "id": "BSV-RUB", - "symbol": "bsv", - "name": "BitcoinSV RUB" - }, - { - "id": "BSV-USD", - "symbol": "bsv", - "name": "BitcoinSV USD" - }, - { - "id": "BTC2-CAD", - "symbol": "btc2", - "name": "Bitcoin2 CAD" - }, - { - "id": "BTC2-CNY", - "symbol": "btc2", - "name": "Bitcoin2 CNY" - }, - { - "id": "BTC2-ETH", - "symbol": "btc2", - "name": "Bitcoin2 ETH" - }, - { - "id": "BTC2-EUR", - "symbol": "btc2", - "name": "Bitcoin2 EUR" - }, - { - "id": "BTC2-GBP", - "symbol": "btc2", - "name": "Bitcoin2 GBP" - }, - { - "id": "BTC2-INR", - "symbol": "btc2", - "name": "Bitcoin2 INR" - }, - { - "id": "BTC2-JPY", - "symbol": "btc2", - "name": "Bitcoin2 JPY" - }, - { - "id": "BTC2-KRW", - "symbol": "btc2", - "name": "Bitcoin2 KRW" - }, - { - "id": "BTC2-RUB", - "symbol": "btc2", - "name": "Bitcoin2 RUB" - }, - { - "id": "BTC2-USD", - "symbol": "btc2", - "name": "Bitcoin2 USD" - }, - { - "id": "BTG-AUD", - "symbol": "btg", - "name": "BitcoinGold AUD" - }, - { - "id": "BTG-BTC", - "symbol": "btg", - "name": "BitcoinGold BTC" - }, - { - "id": "BTG-CNY", - "symbol": "btg", - "name": "BitcoinGold CNY" - }, - { - "id": "BTG-ETH", - "symbol": "btg", - "name": "BitcoinGold ETH" - }, - { - "id": "BTG-EUR", - "symbol": "btg", - "name": "BitcoinGold EUR" - }, - { - "id": "BTG-GBP", - "symbol": "btg", - "name": "BitcoinGold GBP" - }, - { - "id": "BTG-INR", - "symbol": "btg", - "name": "BitcoinGold INR" - }, - { - "id": "BTG-JPY", - "symbol": "btg", - "name": "BitcoinGold JPY" - }, - { - "id": "BTG-KRW", - "symbol": "btg", - "name": "BitcoinGold KRW" - }, - { - "id": "BTG-RUB", - "symbol": "btg", - "name": "BitcoinGold RUB" - }, - { - "id": "BTG-USD", - "symbol": "btg", - "name": "BitcoinGold USD" - }, - { - "id": "BTM-CAD", - "symbol": "btm", - "name": "Bytom CAD" - }, - { - "id": "BTM-CNY", - "symbol": "btm", - "name": "Bytom CNY" - }, - { - "id": "BTM-ETH", - "symbol": "btm", - "name": "Bytom ETH" - }, - { - "id": "BTM-EUR", - "symbol": "btm", - "name": "Bytom EUR" - }, - { - "id": "BTM-GBP", - "symbol": "btm", - "name": "Bytom GBP" - }, - { - "id": "BTM-INR", - "symbol": "btm", - "name": "Bytom INR" - }, - { - "id": "BTM-JPY", - "symbol": "btm", - "name": "Bytom JPY" - }, - { - "id": "BTM-KRW", - "symbol": "btm", - "name": "Bytom KRW" - }, - { - "id": "BTM-RUB", - "symbol": "btm", - "name": "Bytom RUB" - }, - { - "id": "BTM-USD", - "symbol": "btm", - "name": "Bytom USD" - }, - { - "id": "BTS-BTC", - "symbol": "bts", - "name": "BitShares BTC" - }, - { - "id": "BTS-CAD", - "symbol": "bts", - "name": "BitShares CAD" - }, - { - "id": "BTS-CNY", - "symbol": "bts", - "name": "BitShares CNY" - }, - { - "id": "BTS-ETH", - "symbol": "bts", - "name": "BitShares ETH" - }, - { - "id": "BTS-EUR", - "symbol": "bts", - "name": "BitShares EUR" - }, - { - "id": "BTS-GBP", - "symbol": "bts", - "name": "BitShares GBP" - }, - { - "id": "BTS-INR", - "symbol": "bts", - "name": "BitShares INR" - }, - { - "id": "BTS-JPY", - "symbol": "bts", - "name": "BitShares JPY" - }, - { - "id": "BTS-KRW", - "symbol": "bts", - "name": "BitShares KRW" - }, - { - "id": "BTS-RUB", - "symbol": "bts", - "name": "BitShares RUB" - }, - { - "id": "BTS-USD", - "symbol": "bts", - "name": "BitShares USD" - }, - { - "id": "BTT1-AUD", - "symbol": "btt1", - "name": "BitTorrent AUD" - }, - { - "id": "BTT1-BTC", - "symbol": "btt1", - "name": "BitTorrent BTC" - }, - { - "id": "BTT1-CAD", - "symbol": "btt1", - "name": "BitTorrent CAD" - }, - { - "id": "BTT1-CNY", - "symbol": "btt1", - "name": "BitTorrent CNY" - }, - { - "id": "BTT1-ETH", - "symbol": "btt1", - "name": "BitTorrent ETH" - }, - { - "id": "BTT1-EUR", - "symbol": "btt1", - "name": "BitTorrent EUR" - }, - { - "id": "BTT1-GBP", - "symbol": "btt1", - "name": "BitTorrent GBP" - }, - { - "id": "BTT1-INR", - "symbol": "btt1", - "name": "BitTorrent INR" - }, - { - "id": "BTT1-JPY", - "symbol": "btt1", - "name": "BitTorrent JPY" - }, - { - "id": "BTT1-KRW", - "symbol": "btt1", - "name": "BitTorrent KRW" - }, - { - "id": "BTT1-RUB", - "symbol": "btt1", - "name": "BitTorrent RUB" - }, - { - "id": "BTT1-USD", - "symbol": "btt1", - "name": "BitTorrent USD" - }, - { - "id": "BTX-CAD", - "symbol": "btx", - "name": "Bitcore CAD" - }, - { - "id": "BTX-CNY", - "symbol": "btx", - "name": "Bitcore CNY" - }, - { - "id": "BTX-ETH", - "symbol": "btx", - "name": "Bitcore ETH" - }, - { - "id": "BTX-EUR", - "symbol": "btx", - "name": "Bitcore EUR" - }, - { - "id": "BTX-GBP", - "symbol": "btx", - "name": "Bitcore GBP" - }, - { - "id": "BTX-INR", - "symbol": "btx", - "name": "Bitcore INR" - }, - { - "id": "BTX-JPY", - "symbol": "btx", - "name": "Bitcore JPY" - }, - { - "id": "BTX-KRW", - "symbol": "btx", - "name": "Bitcore KRW" - }, - { - "id": "BTX-RUB", - "symbol": "btx", - "name": "Bitcore RUB" - }, - { - "id": "BTX-USD", - "symbol": "btx", - "name": "Bitcore USD" - }, - { - "id": "BURST-CAD", - "symbol": "burst", - "name": "Burst CAD" - }, - { - "id": "BURST-CNY", - "symbol": "burst", - "name": "Burst CNY" - }, - { - "id": "BURST-ETH", - "symbol": "burst", - "name": "Burst ETH" - }, - { - "id": "BURST-EUR", - "symbol": "burst", - "name": "Burst EUR" - }, - { - "id": "BURST-GBP", - "symbol": "burst", - "name": "Burst GBP" - }, - { - "id": "BURST-INR", - "symbol": "burst", - "name": "Burst INR" - }, - { - "id": "BURST-JPY", - "symbol": "burst", - "name": "Burst JPY" - }, - { - "id": "BURST-KRW", - "symbol": "burst", - "name": "Burst KRW" - }, - { - "id": "BURST-RUB", - "symbol": "burst", - "name": "Burst RUB" - }, - { - "id": "BURST-USD", - "symbol": "burst", - "name": "Burst USD" - }, - { - "id": "CCA-CAD", - "symbol": "cca", - "name": "CounosCoin CAD" - }, - { - "id": "CCA-CNY", - "symbol": "cca", - "name": "CounosCoin CNY" - }, - { - "id": "CCA-ETH", - "symbol": "cca", - "name": "CounosCoin ETH" - }, - { - "id": "CCA-EUR", - "symbol": "cca", - "name": "CounosCoin EUR" - }, - { - "id": "CCA-GBP", - "symbol": "cca", - "name": "CounosCoin GBP" - }, - { - "id": "CCA-INR", - "symbol": "cca", - "name": "CounosCoin INR" - }, - { - "id": "CCA-JPY", - "symbol": "cca", - "name": "CounosCoin JPY" - }, - { - "id": "CCA-KRW", - "symbol": "cca", - "name": "CounosCoin KRW" - }, - { - "id": "CCA-RUB", - "symbol": "cca", - "name": "CounosCoin RUB" - }, - { - "id": "CCA-USD", - "symbol": "cca", - "name": "CounosCoin USD" - }, - { - "id": "CCXX-AUD", - "symbol": "ccxx", - "name": "CounosX AUD" - }, - { - "id": "CCXX-BTC", - "symbol": "ccxx", - "name": "CounosX BTC" - }, - { - "id": "CCXX-CAD", - "symbol": "ccxx", - "name": "CounosX CAD" - }, - { - "id": "CCXX-CNY", - "symbol": "ccxx", - "name": "CounosX CNY" - }, - { - "id": "CCXX-ETH", - "symbol": "ccxx", - "name": "CounosX ETH" - }, - { - "id": "CCXX-EUR", - "symbol": "ccxx", - "name": "CounosX EUR" - }, - { - "id": "CCXX-GBP", - "symbol": "ccxx", - "name": "CounosX GBP" - }, - { - "id": "CCXX-INR", - "symbol": "ccxx", - "name": "CounosX INR" - }, - { - "id": "CCXX-JPY", - "symbol": "ccxx", - "name": "CounosX JPY" - }, - { - "id": "CCXX-KRW", - "symbol": "ccxx", - "name": "CounosX KRW" - }, - { - "id": "CCXX-RUB", - "symbol": "ccxx", - "name": "CounosX RUB" - }, - { - "id": "CCXX-USD", - "symbol": "ccxx", - "name": "CounosX USD" - }, - { - "id": "CEL-JPY", - "symbol": "cel", - "name": "Celsius JPY" - }, - { - "id": "CEL-KRW", - "symbol": "cel", - "name": "Celsius KRW" - }, - { - "id": "CEL-RUB", - "symbol": "cel", - "name": "Celsius RUB" - }, - { - "id": "CEL-USD", - "symbol": "cel", - "name": "Celsius USD" - }, - { - "id": "CELO-ETH", - "symbol": "celo", - "name": "Celo ETH" - }, - { - "id": "CELO-EUR", - "symbol": "celo", - "name": "Celo EUR" - }, - { - "id": "CELO-GBP", - "symbol": "celo", - "name": "Celo GBP" - }, - { - "id": "CELO-INR", - "symbol": "celo", - "name": "Celo INR" - }, - { - "id": "CELO-JPY", - "symbol": "celo", - "name": "Celo JPY" - }, - { - "id": "CELO-KRW", - "symbol": "celo", - "name": "Celo KRW" - }, - { - "id": "CELO-RUB", - "symbol": "celo", - "name": "Celo RUB" - }, - { - "id": "CELO-USD", - "symbol": "celo", - "name": "Celo USD" - }, - { - "id": "CET-CAD", - "symbol": "cet", - "name": "CoinExToken CAD" - }, - { - "id": "CET-CNY", - "symbol": "cet", - "name": "CoinExToken CNY" - }, - { - "id": "CET-ETH", - "symbol": "cet", - "name": "CoinExToken ETH" - }, - { - "id": "CET-EUR", - "symbol": "cet", - "name": "CoinExToken EUR" - }, - { - "id": "CET-GBP", - "symbol": "cet", - "name": "CoinExToken GBP" - }, - { - "id": "CET-INR", - "symbol": "cet", - "name": "CoinExToken INR" - }, - { - "id": "CET-JPY", - "symbol": "cet", - "name": "CoinExToken JPY" - }, - { - "id": "CET-KRW", - "symbol": "cet", - "name": "CoinExToken KRW" - }, - { - "id": "CET-RUB", - "symbol": "cet", - "name": "CoinExToken RUB" - }, - { - "id": "CET-USD", - "symbol": "cet", - "name": "CoinExToken USD" - }, - { - "id": "CHI-CNY", - "symbol": "chi", - "name": "Xaya CNY" - }, - { - "id": "CHI-ETH", - "symbol": "chi", - "name": "Xaya ETH" - }, - { - "id": "CHI-EUR", - "symbol": "chi", - "name": "Xaya EUR" - }, - { - "id": "CHI-GBP", - "symbol": "chi", - "name": "Xaya GBP" - }, - { - "id": "CHI-INR", - "symbol": "chi", - "name": "Xaya INR" - }, - { - "id": "CHI-JPY", - "symbol": "chi", - "name": "Xaya JPY" - }, - { - "id": "CHI-KRW", - "symbol": "chi", - "name": "Xaya KRW" - }, - { - "id": "CHI-RUB", - "symbol": "chi", - "name": "Xaya RUB" - }, - { - "id": "CHI-USD", - "symbol": "chi", - "name": "Xaya USD" - }, - { - "id": "CHZ-CAD", - "symbol": "chz", - "name": "Chiliz CAD" - }, - { - "id": "CHZ-CNY", - "symbol": "chz", - "name": "Chiliz CNY" - }, - { - "id": "CHZ-ETH", - "symbol": "chz", - "name": "Chiliz ETH" - }, - { - "id": "CHZ-EUR", - "symbol": "chz", - "name": "Chiliz EUR" - }, - { - "id": "CHZ-GBP", - "symbol": "chz", - "name": "Chiliz GBP" - }, - { - "id": "CHZ-INR", - "symbol": "chz", - "name": "Chiliz INR" - }, - { - "id": "CHZ-JPY", - "symbol": "chz", - "name": "Chiliz JPY" - }, - { - "id": "CHZ-KRW", - "symbol": "chz", - "name": "Chiliz KRW" - }, - { - "id": "CHZ-RUB", - "symbol": "chz", - "name": "Chiliz RUB" - }, - { - "id": "CHZ-USD", - "symbol": "chz", - "name": "Chiliz USD" - }, - { - "id": "CKB-BTC", - "symbol": "ckb", - "name": "NervosNetwork BTC" - }, - { - "id": "CKB-CAD", - "symbol": "ckb", - "name": "NervosNetwork CAD" - }, - { - "id": "CKB-CNY", - "symbol": "ckb", - "name": "NervosNetwork CNY" - }, - { - "id": "CKB-ETH", - "symbol": "ckb", - "name": "NervosNetwork ETH" - }, - { - "id": "CKB-EUR", - "symbol": "ckb", - "name": "NervosNetwork EUR" - }, - { - "id": "CKB-GBP", - "symbol": "ckb", - "name": "NervosNetwork GBP" - }, - { - "id": "CKB-INR", - "symbol": "ckb", - "name": "NervosNetwork INR" - }, - { - "id": "CKB-JPY", - "symbol": "ckb", - "name": "NervosNetwork JPY" - }, - { - "id": "CKB-KRW", - "symbol": "ckb", - "name": "NervosNetwork KRW" - }, - { - "id": "CKB-RUB", - "symbol": "ckb", - "name": "NervosNetwork RUB" - }, - { - "id": "CKB-USD", - "symbol": "ckb", - "name": "NervosNetwork USD" - }, - { - "id": "CLAM-CAD", - "symbol": "clam", - "name": "Clams CAD" - }, - { - "id": "CLAM-CNY", - "symbol": "clam", - "name": "Clams CNY" - }, - { - "id": "CLAM-ETH", - "symbol": "clam", - "name": "Clams ETH" - }, - { - "id": "CLAM-EUR", - "symbol": "clam", - "name": "Clams EUR" - }, - { - "id": "CLAM-GBP", - "symbol": "clam", - "name": "Clams GBP" - }, - { - "id": "CLAM-INR", - "symbol": "clam", - "name": "Clams INR" - }, - { - "id": "CLAM-JPY", - "symbol": "clam", - "name": "Clams JPY" - }, - { - "id": "CLAM-KRW", - "symbol": "clam", - "name": "Clams KRW" - }, - { - "id": "CLAM-RUB", - "symbol": "clam", - "name": "Clams RUB" - }, - { - "id": "CLAM-USD", - "symbol": "clam", - "name": "Clams USD" - }, - { - "id": "CMT1-BTC", - "symbol": "cmt1", - "name": "CyberMiles BTC" - }, - { - "id": "CMT1-CAD", - "symbol": "cmt1", - "name": "CyberMiles CAD" - }, - { - "id": "CMT1-CNY", - "symbol": "cmt1", - "name": "CyberMiles CNY" - }, - { - "id": "CMT1-ETH", - "symbol": "cmt1", - "name": "CyberMiles ETH" - }, - { - "id": "CMT1-EUR", - "symbol": "cmt1", - "name": "CyberMiles EUR" - }, - { - "id": "CMT1-GBP", - "symbol": "cmt1", - "name": "CyberMiles GBP" - }, - { - "id": "CMT1-INR", - "symbol": "cmt1", - "name": "CyberMiles INR" - }, - { - "id": "CMT1-JPY", - "symbol": "cmt1", - "name": "CyberMiles JPY" - }, - { - "id": "CMT1-KRW", - "symbol": "cmt1", - "name": "CyberMiles KRW" - }, - { - "id": "CMT1-RUB", - "symbol": "cmt1", - "name": "CyberMiles RUB" - }, - { - "id": "CMT1-USD", - "symbol": "cmt1", - "name": "CyberMiles USD" - }, - { - "id": "CNX-BTC", - "symbol": "cnx", - "name": "Cryptonex BTC" - }, - { - "id": "CNX-CAD", - "symbol": "cnx", - "name": "Cryptonex CAD" - }, - { - "id": "CNX-CNY", - "symbol": "cnx", - "name": "Cryptonex CNY" - }, - { - "id": "CNX-ETH", - "symbol": "cnx", - "name": "Cryptonex ETH" - }, - { - "id": "CNX-EUR", - "symbol": "cnx", - "name": "Cryptonex EUR" - }, - { - "id": "CNX-GBP", - "symbol": "cnx", - "name": "Cryptonex GBP" - }, - { - "id": "CNX-INR", - "symbol": "cnx", - "name": "Cryptonex INR" - }, - { - "id": "CNX-JPY", - "symbol": "cnx", - "name": "Cryptonex JPY" - }, - { - "id": "CNX-KRW", - "symbol": "cnx", - "name": "Cryptonex KRW" - }, - { - "id": "CNX-RUB", - "symbol": "cnx", - "name": "Cryptonex RUB" - }, - { - "id": "COLX-CAD", - "symbol": "colx", - "name": "ColossusXT CAD" - }, - { - "id": "COLX-CNY", - "symbol": "colx", - "name": "ColossusXT CNY" - }, - { - "id": "COLX-ETH", - "symbol": "colx", - "name": "ColossusXT ETH" - }, - { - "id": "COLX-EUR", - "symbol": "colx", - "name": "ColossusXT EUR" - }, - { - "id": "COLX-GBP", - "symbol": "colx", - "name": "ColossusXT GBP" - }, - { - "id": "COLX-INR", - "symbol": "colx", - "name": "ColossusXT INR" - }, - { - "id": "COLX-JPY", - "symbol": "colx", - "name": "ColossusXT JPY" - }, - { - "id": "COLX-KRW", - "symbol": "colx", - "name": "ColossusXT KRW" - }, - { - "id": "COLX-RUB", - "symbol": "colx", - "name": "ColossusXT RUB" - }, - { - "id": "COLX-USD", - "symbol": "colx", - "name": "ColossusXT USD" - }, - { - "id": "COMP-EUR", - "symbol": "comp", - "name": "Compound EUR" - }, - { - "id": "COMP-USD", - "symbol": "comp", - "name": "Compound USD" - }, - { - "id": "COMP1-BTC", - "symbol": "comp1", - "name": "CompoundCoin BTC" - }, - { - "id": "COMP1-CNY", - "symbol": "comp1", - "name": "CompoundCoin CNY" - }, - { - "id": "COMP1-ETH", - "symbol": "comp1", - "name": "CompoundCoin ETH" - }, - { - "id": "COMP1-EUR", - "symbol": "comp1", - "name": "CompoundCoin EUR" - }, - { - "id": "COMP1-GBP", - "symbol": "comp1", - "name": "CompoundCoin GBP" - }, - { - "id": "COMP1-INR", - "symbol": "comp1", - "name": "CompoundCoin INR" - }, - { - "id": "COMP1-JPY", - "symbol": "comp1", - "name": "CompoundCoin JPY" - }, - { - "id": "COMP1-KRW", - "symbol": "comp1", - "name": "CompoundCoin KRW" - }, - { - "id": "COMP1-RUB", - "symbol": "comp1", - "name": "CompoundCoin RUB" - }, - { - "id": "COMP1-USD", - "symbol": "comp1", - "name": "CompoundCoin USD" - }, - { - "id": "COTI-EUR", - "symbol": "coti", - "name": "COTI EUR" - }, - { - "id": "COTI-GBP", - "symbol": "coti", - "name": "COTI GBP" - }, - { - "id": "COTI-INR", - "symbol": "coti", - "name": "COTI INR" - }, - { - "id": "COTI-JPY", - "symbol": "coti", - "name": "COTI JPY" - }, - { - "id": "COTI-KRW", - "symbol": "coti", - "name": "COTI KRW" - }, - { - "id": "COTI-RUB", - "symbol": "coti", - "name": "COTI RUB" - }, - { - "id": "COTI-USD", - "symbol": "coti", - "name": "COTI USD" - }, - { - "id": "CPS-BTC", - "symbol": "cps", - "name": "Capricoin BTC" - }, - { - "id": "CPS-CAD", - "symbol": "cps", - "name": "Capricoin CAD" - }, - { - "id": "CPS-CNY", - "symbol": "cps", - "name": "Capricoin CNY" - }, - { - "id": "CPS-ETH", - "symbol": "cps", - "name": "Capricoin ETH" - }, - { - "id": "CPS-EUR", - "symbol": "cps", - "name": "Capricoin EUR" - }, - { - "id": "CPS-GBP", - "symbol": "cps", - "name": "Capricoin GBP" - }, - { - "id": "CPS-INR", - "symbol": "cps", - "name": "Capricoin INR" - }, - { - "id": "CPS-JPY", - "symbol": "cps", - "name": "Capricoin JPY" - }, - { - "id": "CPS-KRW", - "symbol": "cps", - "name": "Capricoin KRW" - }, - { - "id": "CPS-RUB", - "symbol": "cps", - "name": "Capricoin RUB" - }, - { - "id": "CRO-AUD", - "symbol": "cro", - "name": "CryptocomCoin AUD" - }, - { - "id": "CRO-BTC", - "symbol": "cro", - "name": "CryptocomCoin BTC" - }, - { - "id": "CRO-CAD", - "symbol": "cro", - "name": "CryptocomCoin CAD" - }, - { - "id": "CRO-ETH", - "symbol": "cro", - "name": "CryptocomCoin ETH" - }, - { - "id": "CRO-EUR", - "symbol": "cro", - "name": "CryptocomCoin EUR" - }, - { - "id": "CRO-GBP", - "symbol": "cro", - "name": "CryptocomCoin GBP" - }, - { - "id": "CRO-INR", - "symbol": "cro", - "name": "CryptocomCoin INR" - }, - { - "id": "CRO-JPY", - "symbol": "cro", - "name": "CryptocomCoin JPY" - }, - { - "id": "CRO-KRW", - "symbol": "cro", - "name": "CryptocomCoin KRW" - }, - { - "id": "CRO-RUB", - "symbol": "cro", - "name": "CryptocomCoin RUB" - }, - { - "id": "CRO-USD", - "symbol": "cro", - "name": "CryptocomCoin USD" - }, - { - "id": "CRU-CAD", - "symbol": "cru", - "name": "Crust CAD" - }, - { - "id": "CRU-CNY", - "symbol": "cru", - "name": "Crust CNY" - }, - { - "id": "CRU-ETH", - "symbol": "cru", - "name": "Crust ETH" - }, - { - "id": "CRU-EUR", - "symbol": "cru", - "name": "Crust EUR" - }, - { - "id": "CRU-GBP", - "symbol": "cru", - "name": "Crust GBP" - }, - { - "id": "CRU-INR", - "symbol": "cru", - "name": "Crust INR" - }, - { - "id": "CRU-JPY", - "symbol": "cru", - "name": "Crust JPY" - }, - { - "id": "CRU-KRW", - "symbol": "cru", - "name": "Crust KRW" - }, - { - "id": "CRU-RUB", - "symbol": "cru", - "name": "Crust RUB" - }, - { - "id": "CRU-USD", - "symbol": "cru", - "name": "Crust USD" - }, - { - "id": "CRV-BTC", - "symbol": "crv", - "name": "CurveDAOToken BTC" - }, - { - "id": "CRV-CAD", - "symbol": "crv", - "name": "CurveDAOToken CAD" - }, - { - "id": "CRV-CNY", - "symbol": "crv", - "name": "CurveDAOToken CNY" - }, - { - "id": "CRV-ETH", - "symbol": "crv", - "name": "CurveDAOToken ETH" - }, - { - "id": "CRV-EUR", - "symbol": "crv", - "name": "CurveDAOToken EUR" - }, - { - "id": "CRV-GBP", - "symbol": "crv", - "name": "CurveDAOToken GBP" - }, - { - "id": "CRV-INR", - "symbol": "crv", - "name": "CurveDAOToken INR" - }, - { - "id": "CRV-JPY", - "symbol": "crv", - "name": "CurveDAOToken JPY" - }, - { - "id": "CRV-KRW", - "symbol": "crv", - "name": "CurveDAOToken KRW" - }, - { - "id": "CRV-RUB", - "symbol": "crv", - "name": "CurveDAOToken RUB" - }, - { - "id": "CRV-USD", - "symbol": "crv", - "name": "CurveDAOToken USD" - }, - { - "id": "CSC-BTC", - "symbol": "csc", - "name": "CasinoCoin BTC" - }, - { - "id": "CSC-CAD", - "symbol": "csc", - "name": "CasinoCoin CAD" - }, - { - "id": "CSC-CNY", - "symbol": "csc", - "name": "CasinoCoin CNY" - }, - { - "id": "CSC-ETH", - "symbol": "csc", - "name": "CasinoCoin ETH" - }, - { - "id": "CSC-EUR", - "symbol": "csc", - "name": "CasinoCoin EUR" - }, - { - "id": "CSC-GBP", - "symbol": "csc", - "name": "CasinoCoin GBP" - }, - { - "id": "CSC-INR", - "symbol": "csc", - "name": "CasinoCoin INR" - }, - { - "id": "CSC-JPY", - "symbol": "csc", - "name": "CasinoCoin JPY" - }, - { - "id": "CSC-KRW", - "symbol": "csc", - "name": "CasinoCoin KRW" - }, - { - "id": "CSC-RUB", - "symbol": "csc", - "name": "CasinoCoin RUB" - }, - { - "id": "CSC-USD", - "symbol": "csc", - "name": "CasinoCoin USD" - }, - { - "id": "CTC-BTC", - "symbol": "ctc", - "name": "CreditTagChain BTC" - }, - { - "id": "CTC-CAD", - "symbol": "ctc", - "name": "CreditTagChain CAD" - }, - { - "id": "CTC-CNY", - "symbol": "ctc", - "name": "CreditTagChain CNY" - }, - { - "id": "CTC-ETH", - "symbol": "ctc", - "name": "CreditTagChain ETH" - }, - { - "id": "CTC-EUR", - "symbol": "ctc", - "name": "CreditTagChain EUR" - }, - { - "id": "CTC-GBP", - "symbol": "ctc", - "name": "CreditTagChain GBP" - }, - { - "id": "CTC-INR", - "symbol": "ctc", - "name": "CreditTagChain INR" - }, - { - "id": "CTC-JPY", - "symbol": "ctc", - "name": "CreditTagChain JPY" - }, - { - "id": "CTC-KRW", - "symbol": "ctc", - "name": "CreditTagChain KRW" - }, - { - "id": "CTC-RUB", - "symbol": "ctc", - "name": "CreditTagChain RUB" - }, - { - "id": "CTC1-CAD", - "symbol": "ctc1", - "name": "Creditcoin CAD" - }, - { - "id": "CTC1-CNY", - "symbol": "ctc1", - "name": "Creditcoin CNY" - }, - { - "id": "CTC1-ETH", - "symbol": "ctc1", - "name": "Creditcoin ETH" - }, - { - "id": "CTC1-EUR", - "symbol": "ctc1", - "name": "Creditcoin EUR" - }, - { - "id": "CTC1-GBP", - "symbol": "ctc1", - "name": "Creditcoin GBP" - }, - { - "id": "CTC1-INR", - "symbol": "ctc1", - "name": "Creditcoin INR" - }, - { - "id": "CTC1-JPY", - "symbol": "ctc1", - "name": "Creditcoin JPY" - }, - { - "id": "CTC1-KRW", - "symbol": "ctc1", - "name": "Creditcoin KRW" - }, - { - "id": "CTC1-RUB", - "symbol": "ctc1", - "name": "Creditcoin RUB" - }, - { - "id": "CTC1-USD", - "symbol": "ctc1", - "name": "Creditcoin USD" - }, - { - "id": "CTXC-CAD", - "symbol": "ctxc", - "name": "Cortex CAD" - }, - { - "id": "CTXC-CNY", - "symbol": "ctxc", - "name": "Cortex CNY" - }, - { - "id": "CTXC-ETH", - "symbol": "ctxc", - "name": "Cortex ETH" - }, - { - "id": "CTXC-EUR", - "symbol": "ctxc", - "name": "Cortex EUR" - }, - { - "id": "CTXC-GBP", - "symbol": "ctxc", - "name": "Cortex GBP" - }, - { - "id": "CTXC-INR", - "symbol": "ctxc", - "name": "Cortex INR" - }, - { - "id": "CTXC-JPY", - "symbol": "ctxc", - "name": "Cortex JPY" - }, - { - "id": "CTXC-KRW", - "symbol": "ctxc", - "name": "Cortex KRW" - }, - { - "id": "CTXC-RUB", - "symbol": "ctxc", - "name": "Cortex RUB" - }, - { - "id": "CTXC-USD", - "symbol": "ctxc", - "name": "Cortex USD" - }, - { - "id": "CURE-CAD", - "symbol": "cure", - "name": "Curecoin CAD" - }, - { - "id": "CURE-CNY", - "symbol": "cure", - "name": "Curecoin CNY" - }, - { - "id": "CURE-ETH", - "symbol": "cure", - "name": "Curecoin ETH" - }, - { - "id": "CURE-EUR", - "symbol": "cure", - "name": "Curecoin EUR" - }, - { - "id": "CURE-GBP", - "symbol": "cure", - "name": "Curecoin GBP" - }, - { - "id": "CURE-INR", - "symbol": "cure", - "name": "Curecoin INR" - }, - { - "id": "CURE-JPY", - "symbol": "cure", - "name": "Curecoin JPY" - }, - { - "id": "CURE-KRW", - "symbol": "cure", - "name": "Curecoin KRW" - }, - { - "id": "CURE-RUB", - "symbol": "cure", - "name": "Curecoin RUB" - }, - { - "id": "CURE-USD", - "symbol": "cure", - "name": "Curecoin USD" - }, - { - "id": "CUT-CAD", - "symbol": "cut", - "name": "CUTcoin CAD" - }, - { - "id": "CUT-CNY", - "symbol": "cut", - "name": "CUTcoin CNY" - }, - { - "id": "CUT-ETH", - "symbol": "cut", - "name": "CUTcoin ETH" - }, - { - "id": "CUT-EUR", - "symbol": "cut", - "name": "CUTcoin EUR" - }, - { - "id": "CUT-GBP", - "symbol": "cut", - "name": "CUTcoin GBP" - }, - { - "id": "CUT-INR", - "symbol": "cut", - "name": "CUTcoin INR" - }, - { - "id": "CUT-JPY", - "symbol": "cut", - "name": "CUTcoin JPY" - }, - { - "id": "CUT-KRW", - "symbol": "cut", - "name": "CUTcoin KRW" - }, - { - "id": "CUT-RUB", - "symbol": "cut", - "name": "CUTcoin RUB" - }, - { - "id": "CUT-USD", - "symbol": "cut", - "name": "CUTcoin USD" - }, - { - "id": "CVC-AUD", - "symbol": "cvc", - "name": "Civic AUD" - }, - { - "id": "CVC-CNY", - "symbol": "cvc", - "name": "Civic CNY" - }, - { - "id": "CVC-ETH", - "symbol": "cvc", - "name": "Civic ETH" - }, - { - "id": "CVC-EUR", - "symbol": "cvc", - "name": "Civic EUR" - }, - { - "id": "CVC-GBP", - "symbol": "cvc", - "name": "Civic GBP" - }, - { - "id": "CVC-INR", - "symbol": "cvc", - "name": "Civic INR" - }, - { - "id": "CVC-JPY", - "symbol": "cvc", - "name": "Civic JPY" - }, - { - "id": "CVC-KRW", - "symbol": "cvc", - "name": "Civic KRW" - }, - { - "id": "CVC-RUB", - "symbol": "cvc", - "name": "Civic RUB" - }, - { - "id": "CVC-USD", - "symbol": "cvc", - "name": "Civic USD" - }, - { - "id": "DAG-USD", - "symbol": "dag", - "name": "Constellation USD" - }, - { - "id": "DASH-BTC", - "symbol": "dash", - "name": "Dash BTC" - }, - { - "id": "DASH-JPY", - "symbol": "dash", - "name": "Dash JPY" - }, - { - "id": "DASH-KRW", - "symbol": "dash", - "name": "Dash KRW" - }, - { - "id": "DASH-USD", - "symbol": "dash", - "name": "Dash USD" - }, - { - "id": "DCN-AUD", - "symbol": "dcn", - "name": "Dentacoin AUD" - }, - { - "id": "DCN-CAD", - "symbol": "dcn", - "name": "Dentacoin CAD" - }, - { - "id": "DCN-CNY", - "symbol": "dcn", - "name": "Dentacoin CNY" - }, - { - "id": "DCN-ETH", - "symbol": "dcn", - "name": "Dentacoin ETH" - }, - { - "id": "DCN-EUR", - "symbol": "dcn", - "name": "Dentacoin EUR" - }, - { - "id": "DCN-GBP", - "symbol": "dcn", - "name": "Dentacoin GBP" - }, - { - "id": "DCN-INR", - "symbol": "dcn", - "name": "Dentacoin INR" - }, - { - "id": "DCN-JPY", - "symbol": "dcn", - "name": "Dentacoin JPY" - }, - { - "id": "DCN-RUB", - "symbol": "dcn", - "name": "Dentacoin RUB" - }, - { - "id": "DCN-USD", - "symbol": "dcn", - "name": "Dentacoin USD" - }, - { - "id": "DCR-BTC", - "symbol": "dcr", - "name": "Decred BTC" - }, - { - "id": "DCR-CAD", - "symbol": "dcr", - "name": "Decred CAD" - }, - { - "id": "DCR-CNY", - "symbol": "dcr", - "name": "Decred CNY" - }, - { - "id": "DCR-ETH", - "symbol": "dcr", - "name": "Decred ETH" - }, - { - "id": "DCR-EUR", - "symbol": "dcr", - "name": "Decred EUR" - }, - { - "id": "DCR-GBP", - "symbol": "dcr", - "name": "Decred GBP" - }, - { - "id": "DCR-INR", - "symbol": "dcr", - "name": "Decred INR" - }, - { - "id": "DCR-JPY", - "symbol": "dcr", - "name": "Decred JPY" - }, - { - "id": "DCR-KRW", - "symbol": "dcr", - "name": "Decred KRW" - }, - { - "id": "DCR-USD", - "symbol": "dcr", - "name": "Decred USD" - }, - { - "id": "DCY-CAD", - "symbol": "dcy", - "name": "Dinastycoin CAD" - }, - { - "id": "DCY-CNY", - "symbol": "dcy", - "name": "Dinastycoin CNY" - }, - { - "id": "DCY-ETH", - "symbol": "dcy", - "name": "Dinastycoin ETH" - }, - { - "id": "DCY-EUR", - "symbol": "dcy", - "name": "Dinastycoin EUR" - }, - { - "id": "DCY-GBP", - "symbol": "dcy", - "name": "Dinastycoin GBP" - }, - { - "id": "DCY-INR", - "symbol": "dcy", - "name": "Dinastycoin INR" - }, - { - "id": "DCY-JPY", - "symbol": "dcy", - "name": "Dinastycoin JPY" - }, - { - "id": "DCY-KRW", - "symbol": "dcy", - "name": "Dinastycoin KRW" - }, - { - "id": "DCY-RUB", - "symbol": "dcy", - "name": "Dinastycoin RUB" - }, - { - "id": "DCY-USD", - "symbol": "dcy", - "name": "Dinastycoin USD" - }, - { - "id": "DDK-CAD", - "symbol": "ddk", - "name": "DDKoin CAD" - }, - { - "id": "DDK-CNY", - "symbol": "ddk", - "name": "DDKoin CNY" - }, - { - "id": "DDK-ETH", - "symbol": "ddk", - "name": "DDKoin ETH" - }, - { - "id": "DDK-EUR", - "symbol": "ddk", - "name": "DDKoin EUR" - }, - { - "id": "DDK-GBP", - "symbol": "ddk", - "name": "DDKoin GBP" - }, - { - "id": "DDK-INR", - "symbol": "ddk", - "name": "DDKoin INR" - }, - { - "id": "DDK-JPY", - "symbol": "ddk", - "name": "DDKoin JPY" - }, - { - "id": "DDK-KRW", - "symbol": "ddk", - "name": "DDKoin KRW" - }, - { - "id": "DDK-RUB", - "symbol": "ddk", - "name": "DDKoin RUB" - }, - { - "id": "DDK-USD", - "symbol": "ddk", - "name": "DDKoin USD" - }, - { - "id": "DERO-CAD", - "symbol": "dero", - "name": "Dero CAD" - }, - { - "id": "DERO-CNY", - "symbol": "dero", - "name": "Dero CNY" - }, - { - "id": "DERO-ETH", - "symbol": "dero", - "name": "Dero ETH" - }, - { - "id": "DERO-EUR", - "symbol": "dero", - "name": "Dero EUR" - }, - { - "id": "DERO-GBP", - "symbol": "dero", - "name": "Dero GBP" - }, - { - "id": "DERO-INR", - "symbol": "dero", - "name": "Dero INR" - }, - { - "id": "DERO-JPY", - "symbol": "dero", - "name": "Dero JPY" - }, - { - "id": "DERO-KRW", - "symbol": "dero", - "name": "Dero KRW" - }, - { - "id": "DERO-RUB", - "symbol": "dero", - "name": "Dero RUB" - }, - { - "id": "DERO-USD", - "symbol": "dero", - "name": "Dero USD" - }, - { - "id": "DEV-BTC", - "symbol": "dev", - "name": "DeviantCoin BTC" - }, - { - "id": "DEV-CAD", - "symbol": "dev", - "name": "DeviantCoin CAD" - }, - { - "id": "DEV-CNY", - "symbol": "dev", - "name": "DeviantCoin CNY" - }, - { - "id": "DEV-ETH", - "symbol": "dev", - "name": "DeviantCoin ETH" - }, - { - "id": "DEV-EUR", - "symbol": "dev", - "name": "DeviantCoin EUR" - }, - { - "id": "DEV-GBP", - "symbol": "dev", - "name": "DeviantCoin GBP" - }, - { - "id": "DEV-INR", - "symbol": "dev", - "name": "DeviantCoin INR" - }, - { - "id": "DEV-JPY", - "symbol": "dev", - "name": "DeviantCoin JPY" - }, - { - "id": "DEV-KRW", - "symbol": "dev", - "name": "DeviantCoin KRW" - }, - { - "id": "DEV-RUB", - "symbol": "dev", - "name": "DeviantCoin RUB" - }, - { - "id": "DFI-BTC", - "symbol": "dfi", - "name": "DeFiChain BTC" - }, - { - "id": "DFI-CAD", - "symbol": "dfi", - "name": "DeFiChain CAD" - }, - { - "id": "DFI-CNY", - "symbol": "dfi", - "name": "DeFiChain CNY" - }, - { - "id": "DFI-ETH", - "symbol": "dfi", - "name": "DeFiChain ETH" - }, - { - "id": "DFI-EUR", - "symbol": "dfi", - "name": "DeFiChain EUR" - }, - { - "id": "DFI-GBP", - "symbol": "dfi", - "name": "DeFiChain GBP" - }, - { - "id": "DFI-INR", - "symbol": "dfi", - "name": "DeFiChain INR" - }, - { - "id": "DFI-JPY", - "symbol": "dfi", - "name": "DeFiChain JPY" - }, - { - "id": "DFI-KRW", - "symbol": "dfi", - "name": "DeFiChain KRW" - }, - { - "id": "DFI-RUB", - "symbol": "dfi", - "name": "DeFiChain RUB" - }, - { - "id": "DFI-USD", - "symbol": "dfi", - "name": "DeFiChain USD" - }, - { - "id": "DGB-AUD", - "symbol": "dgb", - "name": "DigiByte AUD" - }, - { - "id": "DGB-BTC", - "symbol": "dgb", - "name": "DigiByte BTC" - }, - { - "id": "DGB-CAD", - "symbol": "dgb", - "name": "DigiByte CAD" - }, - { - "id": "DGB-CNY", - "symbol": "dgb", - "name": "DigiByte CNY" - }, - { - "id": "DGB-ETH", - "symbol": "dgb", - "name": "DigiByte ETH" - }, - { - "id": "DGB-EUR", - "symbol": "dgb", - "name": "DigiByte EUR" - }, - { - "id": "DGB-GBP", - "symbol": "dgb", - "name": "DigiByte GBP" - }, - { - "id": "DGB-INR", - "symbol": "dgb", - "name": "DigiByte INR" - }, - { - "id": "DGB-JPY", - "symbol": "dgb", - "name": "DigiByte JPY" - }, - { - "id": "DGB-RUB", - "symbol": "dgb", - "name": "DigiByte RUB" - }, - { - "id": "DGB-USD", - "symbol": "dgb", - "name": "DigiByte USD" - }, - { - "id": "DGD-BTC", - "symbol": "dgd", - "name": "DigixDAO BTC" - }, - { - "id": "DGD-CAD", - "symbol": "dgd", - "name": "DigixDAO CAD" - }, - { - "id": "DGD-CNY", - "symbol": "dgd", - "name": "DigixDAO CNY" - }, - { - "id": "DGD-EUR", - "symbol": "dgd", - "name": "DigixDAO EUR" - }, - { - "id": "DGD-GBP", - "symbol": "dgd", - "name": "DigixDAO GBP" - }, - { - "id": "DGD-INR", - "symbol": "dgd", - "name": "DigixDAO INR" - }, - { - "id": "DGD-JPY", - "symbol": "dgd", - "name": "DigixDAO JPY" - }, - { - "id": "DGD-KRW", - "symbol": "dgd", - "name": "DigixDAO KRW" - }, - { - "id": "DGD-RUB", - "symbol": "dgd", - "name": "DigixDAO RUB" - }, - { - "id": "DGD-USD", - "symbol": "dgd", - "name": "DigixDAO USD" - }, - { - "id": "DIME-CAD", - "symbol": "dime", - "name": "Dimecoin CAD" - }, - { - "id": "DIME-CNY", - "symbol": "dime", - "name": "Dimecoin CNY" - }, - { - "id": "DIME-ETH", - "symbol": "dime", - "name": "Dimecoin ETH" - }, - { - "id": "DIME-EUR", - "symbol": "dime", - "name": "Dimecoin EUR" - }, - { - "id": "DIME-GBP", - "symbol": "dime", - "name": "Dimecoin GBP" - }, - { - "id": "DIME-INR", - "symbol": "dime", - "name": "Dimecoin INR" - }, - { - "id": "DIME-JPY", - "symbol": "dime", - "name": "Dimecoin JPY" - }, - { - "id": "DIME-KRW", - "symbol": "dime", - "name": "Dimecoin KRW" - }, - { - "id": "DIME-RUB", - "symbol": "dime", - "name": "Dimecoin RUB" - }, - { - "id": "DIME-USD", - "symbol": "dime", - "name": "Dimecoin USD" - }, - { - "id": "DMCH-CAD", - "symbol": "dmch", - "name": "DARMACash CAD" - }, - { - "id": "DMCH-CNY", - "symbol": "dmch", - "name": "DARMACash CNY" - }, - { - "id": "DMCH-ETH", - "symbol": "dmch", - "name": "DARMACash ETH" - }, - { - "id": "DMCH-EUR", - "symbol": "dmch", - "name": "DARMACash EUR" - }, - { - "id": "DMCH-GBP", - "symbol": "dmch", - "name": "DARMACash GBP" - }, - { - "id": "DMCH-INR", - "symbol": "dmch", - "name": "DARMACash INR" - }, - { - "id": "DMCH-JPY", - "symbol": "dmch", - "name": "DARMACash JPY" - }, - { - "id": "DMCH-KRW", - "symbol": "dmch", - "name": "DARMACash KRW" - }, - { - "id": "DMCH-RUB", - "symbol": "dmch", - "name": "DARMACash RUB" - }, - { - "id": "DMCH-USD", - "symbol": "dmch", - "name": "DARMACash USD" - }, - { - "id": "DMD-KRW", - "symbol": "dmd", - "name": "Diamond KRW" - }, - { - "id": "DMD-RUB", - "symbol": "dmd", - "name": "Diamond RUB" - }, - { - "id": "DMD-USD", - "symbol": "dmd", - "name": "Diamond USD" - }, - { - "id": "DNA1-CAD", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture CAD" - }, - { - "id": "DNA1-CNY", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture CNY" - }, - { - "id": "DNA1-ETH", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture ETH" - }, - { - "id": "DNA1-EUR", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture EUR" - }, - { - "id": "DNA1-GBP", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture GBP" - }, - { - "id": "DNA1-INR", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture INR" - }, - { - "id": "DNA1-JPY", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture JPY" - }, - { - "id": "DNA1-KRW", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture KRW" - }, - { - "id": "DNA1-RUB", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture RUB" - }, - { - "id": "DNA1-USD", - "symbol": "dna1", - "name": "MetaverseDualchainNetworkArchitecture USD" - }, - { - "id": "DNT-BTC", - "symbol": "dnt", - "name": "district0x BTC" - }, - { - "id": "DNT-CAD", - "symbol": "dnt", - "name": "district0x CAD" - }, - { - "id": "DNT-CNY", - "symbol": "dnt", - "name": "district0x CNY" - }, - { - "id": "DNT-ETH", - "symbol": "dnt", - "name": "district0x ETH" - }, - { - "id": "DNT-EUR", - "symbol": "dnt", - "name": "district0x EUR" - }, - { - "id": "DNT-GBP", - "symbol": "dnt", - "name": "district0x GBP" - }, - { - "id": "DNT-INR", - "symbol": "dnt", - "name": "district0x INR" - }, - { - "id": "DNT-JPY", - "symbol": "dnt", - "name": "district0x JPY" - }, - { - "id": "DNT-KRW", - "symbol": "dnt", - "name": "district0x KRW" - }, - { - "id": "DNT-USD", - "symbol": "dnt", - "name": "district0x USD" - }, - { - "id": "DOGE-AUD", - "symbol": "doge", - "name": "Dogecoin AUD" - }, - { - "id": "DOGE-BTC", - "symbol": "doge", - "name": "Dogecoin BTC" - }, - { - "id": "DOGE-CAD", - "symbol": "doge", - "name": "Dogecoin CAD" - }, - { - "id": "DOGE-ETH", - "symbol": "doge", - "name": "Dogecoin ETH" - }, - { - "id": "DOGE-EUR", - "symbol": "doge", - "name": "Dogecoin EUR" - }, - { - "id": "DOGE-GBP", - "symbol": "doge", - "name": "Dogecoin GBP" - }, - { - "id": "DOGE-INR", - "symbol": "doge", - "name": "Dogecoin INR" - }, - { - "id": "DOGE-JPY", - "symbol": "doge", - "name": "Dogecoin JPY" - }, - { - "id": "DOGE-KRW", - "symbol": "doge", - "name": "Dogecoin KRW" - }, - { - "id": "DOGE-RUB", - "symbol": "doge", - "name": "Dogecoin RUB" - }, - { - "id": "DOGE-USD", - "symbol": "doge", - "name": "Dogecoin USD" - }, - { - "id": "DOT1-AUD", - "symbol": "dot1", - "name": "Polkadot AUD" - }, - { - "id": "DOT1-BTC", - "symbol": "dot1", - "name": "Polkadot BTC" - }, - { - "id": "DOT1-CAD", - "symbol": "dot1", - "name": "Polkadot CAD" - }, - { - "id": "DOT1-CNY", - "symbol": "dot1", - "name": "Polkadot CNY" - }, - { - "id": "DOT1-ETH", - "symbol": "dot1", - "name": "Polkadot ETH" - }, - { - "id": "DOT1-EUR", - "symbol": "dot1", - "name": "Polkadot EUR" - }, - { - "id": "DOT1-GBP", - "symbol": "dot1", - "name": "Polkadot GBP" - }, - { - "id": "DOT1-INR", - "symbol": "dot1", - "name": "Polkadot INR" - }, - { - "id": "DOT1-JPY", - "symbol": "dot1", - "name": "Polkadot JPY" - }, - { - "id": "DOT1-KRW", - "symbol": "dot1", - "name": "Polkadot KRW" - }, - { - "id": "DOT1-RUB", - "symbol": "dot1", - "name": "Polkadot RUB" - }, - { - "id": "DOT1-USD", - "symbol": "dot1", - "name": "Polkadot USD" - }, - { - "id": "DTEP-CAD", - "symbol": "dtep", - "name": "DECOIN CAD" - }, - { - "id": "DTEP-CNY", - "symbol": "dtep", - "name": "DECOIN CNY" - }, - { - "id": "DTEP-ETH", - "symbol": "dtep", - "name": "DECOIN ETH" - }, - { - "id": "DTEP-EUR", - "symbol": "dtep", - "name": "DECOIN EUR" - }, - { - "id": "DTEP-GBP", - "symbol": "dtep", - "name": "DECOIN GBP" - }, - { - "id": "DTEP-INR", - "symbol": "dtep", - "name": "DECOIN INR" - }, - { - "id": "DTEP-JPY", - "symbol": "dtep", - "name": "DECOIN JPY" - }, - { - "id": "DTEP-KRW", - "symbol": "dtep", - "name": "DECOIN KRW" - }, - { - "id": "DTEP-RUB", - "symbol": "dtep", - "name": "DECOIN RUB" - }, - { - "id": "DTEP-USD", - "symbol": "dtep", - "name": "DECOIN USD" - }, - { - "id": "DUN-CAD", - "symbol": "dun", - "name": "DuneNetwork CAD" - }, - { - "id": "DUN-CNY", - "symbol": "dun", - "name": "DuneNetwork CNY" - }, - { - "id": "DUN-ETH", - "symbol": "dun", - "name": "DuneNetwork ETH" - }, - { - "id": "DUN-EUR", - "symbol": "dun", - "name": "DuneNetwork EUR" - }, - { - "id": "DUN-GBP", - "symbol": "dun", - "name": "DuneNetwork GBP" - }, - { - "id": "DUN-INR", - "symbol": "dun", - "name": "DuneNetwork INR" - }, - { - "id": "DUN-JPY", - "symbol": "dun", - "name": "DuneNetwork JPY" - }, - { - "id": "DUN-KRW", - "symbol": "dun", - "name": "DuneNetwork KRW" - }, - { - "id": "DUN-RUB", - "symbol": "dun", - "name": "DuneNetwork RUB" - }, - { - "id": "DUN-USD", - "symbol": "dun", - "name": "DuneNetwork USD" - }, - { - "id": "ECA-USD", - "symbol": "eca", - "name": "Electra USD" - }, - { - "id": "EDC-BTC", - "symbol": "edc", - "name": "EDCBlockchain BTC" - }, - { - "id": "EDC-CAD", - "symbol": "edc", - "name": "EDCBlockchain CAD" - }, - { - "id": "EDC-CNY", - "symbol": "edc", - "name": "EDCBlockchain CNY" - }, - { - "id": "EDC-ETH", - "symbol": "edc", - "name": "EDCBlockchain ETH" - }, - { - "id": "EDC-EUR", - "symbol": "edc", - "name": "EDCBlockchain EUR" - }, - { - "id": "EDC-GBP", - "symbol": "edc", - "name": "EDCBlockchain GBP" - }, - { - "id": "EDC-INR", - "symbol": "edc", - "name": "EDCBlockchain INR" - }, - { - "id": "EDC-JPY", - "symbol": "edc", - "name": "EDCBlockchain JPY" - }, - { - "id": "EDC-KRW", - "symbol": "edc", - "name": "EDCBlockchain KRW" - }, - { - "id": "EDC-RUB", - "symbol": "edc", - "name": "EDCBlockchain RUB" - }, - { - "id": "EDG-AUD", - "symbol": "edg", - "name": "Edgeless AUD" - }, - { - "id": "EDG-BTC", - "symbol": "edg", - "name": "Edgeless BTC" - }, - { - "id": "EDG-ETH", - "symbol": "edg", - "name": "Edgeless ETH" - }, - { - "id": "EDG-EUR", - "symbol": "edg", - "name": "Edgeless EUR" - }, - { - "id": "EDG-GBP", - "symbol": "edg", - "name": "Edgeless GBP" - }, - { - "id": "EDG-INR", - "symbol": "edg", - "name": "Edgeless INR" - }, - { - "id": "EDG-JPY", - "symbol": "edg", - "name": "Edgeless JPY" - }, - { - "id": "EDG-KRW", - "symbol": "edg", - "name": "Edgeless KRW" - }, - { - "id": "EDG-RUB", - "symbol": "edg", - "name": "Edgeless RUB" - }, - { - "id": "EDG-USD", - "symbol": "edg", - "name": "Edgeless USD" - }, - { - "id": "EGLD-CAD", - "symbol": "egld", - "name": "Elrond CAD" - }, - { - "id": "EGLD-CNY", - "symbol": "egld", - "name": "Elrond CNY" - }, - { - "id": "EGLD-ETH", - "symbol": "egld", - "name": "Elrond ETH" - }, - { - "id": "EGLD-EUR", - "symbol": "egld", - "name": "Elrond EUR" - }, - { - "id": "EGLD-GBP", - "symbol": "egld", - "name": "Elrond GBP" - }, - { - "id": "EGLD-INR", - "symbol": "egld", - "name": "Elrond INR" - }, - { - "id": "EGLD-JPY", - "symbol": "egld", - "name": "Elrond JPY" - }, - { - "id": "EGLD-KRW", - "symbol": "egld", - "name": "Elrond KRW" - }, - { - "id": "EGLD-RUB", - "symbol": "egld", - "name": "Elrond RUB" - }, - { - "id": "EGLD-USD", - "symbol": "egld", - "name": "Elrond USD" - }, - { - "id": "ELA-CAD", - "symbol": "ela", - "name": "Elastos CAD" - }, - { - "id": "ELA-CNY", - "symbol": "ela", - "name": "Elastos CNY" - }, - { - "id": "ELA-ETH", - "symbol": "ela", - "name": "Elastos ETH" - }, - { - "id": "ELA-EUR", - "symbol": "ela", - "name": "Elastos EUR" - }, - { - "id": "ELA-GBP", - "symbol": "ela", - "name": "Elastos GBP" - }, - { - "id": "ELA-INR", - "symbol": "ela", - "name": "Elastos INR" - }, - { - "id": "ELA-JPY", - "symbol": "ela", - "name": "Elastos JPY" - }, - { - "id": "ELA-KRW", - "symbol": "ela", - "name": "Elastos KRW" - }, - { - "id": "ELA-RUB", - "symbol": "ela", - "name": "Elastos RUB" - }, - { - "id": "ELA-USD", - "symbol": "ela", - "name": "Elastos USD" - }, - { - "id": "EMC2-CAD", - "symbol": "emc2", - "name": "Einsteinium CAD" - }, - { - "id": "EMC2-CNY", - "symbol": "emc2", - "name": "Einsteinium CNY" - }, - { - "id": "EMC2-ETH", - "symbol": "emc2", - "name": "Einsteinium ETH" - }, - { - "id": "EMC2-EUR", - "symbol": "emc2", - "name": "Einsteinium EUR" - }, - { - "id": "EMC2-GBP", - "symbol": "emc2", - "name": "Einsteinium GBP" - }, - { - "id": "EMC2-INR", - "symbol": "emc2", - "name": "Einsteinium INR" - }, - { - "id": "EMC2-JPY", - "symbol": "emc2", - "name": "Einsteinium JPY" - }, - { - "id": "EMC2-KRW", - "symbol": "emc2", - "name": "Einsteinium KRW" - }, - { - "id": "EMC2-RUB", - "symbol": "emc2", - "name": "Einsteinium RUB" - }, - { - "id": "EMC2-USD", - "symbol": "emc2", - "name": "Einsteinium USD" - }, - { - "id": "ENJ-BTC", - "symbol": "enj", - "name": "EnjinCoin BTC" - }, - { - "id": "ENJ-CAD", - "symbol": "enj", - "name": "EnjinCoin CAD" - }, - { - "id": "ENJ-CNY", - "symbol": "enj", - "name": "EnjinCoin CNY" - }, - { - "id": "ENJ-ETH", - "symbol": "enj", - "name": "EnjinCoin ETH" - }, - { - "id": "ENJ-EUR", - "symbol": "enj", - "name": "EnjinCoin EUR" - }, - { - "id": "ENJ-GBP", - "symbol": "enj", - "name": "EnjinCoin GBP" - }, - { - "id": "ENJ-INR", - "symbol": "enj", - "name": "EnjinCoin INR" - }, - { - "id": "ENJ-JPY", - "symbol": "enj", - "name": "EnjinCoin JPY" - }, - { - "id": "ENJ-KRW", - "symbol": "enj", - "name": "EnjinCoin KRW" - }, - { - "id": "ENJ-RUB", - "symbol": "enj", - "name": "EnjinCoin RUB" - }, - { - "id": "ENJ-USD", - "symbol": "enj", - "name": "EnjinCoin USD" - }, - { - "id": "EOS-USD", - "symbol": "eos", - "name": "EOS USD" - }, - { - "id": "ERK-BTC", - "symbol": "erk", - "name": "EurekaCoin BTC" - }, - { - "id": "ERK-CAD", - "symbol": "erk", - "name": "EurekaCoin CAD" - }, - { - "id": "ERK-CNY", - "symbol": "erk", - "name": "EurekaCoin CNY" - }, - { - "id": "ERK-ETH", - "symbol": "erk", - "name": "EurekaCoin ETH" - }, - { - "id": "ERK-EUR", - "symbol": "erk", - "name": "EurekaCoin EUR" - }, - { - "id": "ERK-GBP", - "symbol": "erk", - "name": "EurekaCoin GBP" - }, - { - "id": "ERK-INR", - "symbol": "erk", - "name": "EurekaCoin INR" - }, - { - "id": "ERK-JPY", - "symbol": "erk", - "name": "EurekaCoin JPY" - }, - { - "id": "ERK-KRW", - "symbol": "erk", - "name": "EurekaCoin KRW" - }, - { - "id": "ERK-RUB", - "symbol": "erk", - "name": "EurekaCoin RUB" - }, - { - "id": "ERK-USD", - "symbol": "erk", - "name": "EurekaCoin USD" - }, - { - "id": "ETC-AUD", - "symbol": "etc", - "name": "EthereumClassic AUD" - }, - { - "id": "ETC-BTC", - "symbol": "etc", - "name": "EthereumClassic BTC" - }, - { - "id": "ETC-CAD", - "symbol": "etc", - "name": "EthereumClassic CAD" - }, - { - "id": "ETC-CNY", - "symbol": "etc", - "name": "EthereumClassic CNY" - }, - { - "id": "ETC-ETH", - "symbol": "etc", - "name": "EthereumClassic ETH" - }, - { - "id": "ETC-EUR", - "symbol": "etc", - "name": "EthereumClassic EUR" - }, - { - "id": "ETC-GBP", - "symbol": "etc", - "name": "EthereumClassic GBP" - }, - { - "id": "ETC-INR", - "symbol": "etc", - "name": "EthereumClassic INR" - }, - { - "id": "ETC-KRW", - "symbol": "etc", - "name": "EthereumClassic KRW" - }, - { - "id": "ETC-USD", - "symbol": "etc", - "name": "EthereumClassic USD" - }, - { - "id": "ETH-BTC", - "symbol": "eth", - "name": "Ethereum BTC" - }, - { - "id": "ETH-CAD", - "symbol": "eth", - "name": "Ethereum CAD" - }, - { - "id": "ETH-EUR", - "symbol": "eth", - "name": "Ethereum EUR" - }, - { - "id": "ETH-GBP", - "symbol": "eth", - "name": "Ethereum GBP" - }, - { - "id": "ETH-USD", - "symbol": "eth", - "name": "Ethereum USD" - }, - { - "id": "ETN-CAD", - "symbol": "etn", - "name": "Electroneum CAD" - }, - { - "id": "ETN-CNY", - "symbol": "etn", - "name": "Electroneum CNY" - }, - { - "id": "ETN-ETH", - "symbol": "etn", - "name": "Electroneum ETH" - }, - { - "id": "ETN-EUR", - "symbol": "etn", - "name": "Electroneum EUR" - }, - { - "id": "ETN-GBP", - "symbol": "etn", - "name": "Electroneum GBP" - }, - { - "id": "ETN-INR", - "symbol": "etn", - "name": "Electroneum INR" - }, - { - "id": "ETN-JPY", - "symbol": "etn", - "name": "Electroneum JPY" - }, - { - "id": "ETN-KRW", - "symbol": "etn", - "name": "Electroneum KRW" - }, - { - "id": "ETN-RUB", - "symbol": "etn", - "name": "Electroneum RUB" - }, - { - "id": "ETN-USD", - "symbol": "etn", - "name": "Electroneum USD" - }, - { - "id": "ETP-BTC", - "symbol": "etp", - "name": "MetaverseETP BTC" - }, - { - "id": "ETP-CAD", - "symbol": "etp", - "name": "MetaverseETP CAD" - }, - { - "id": "ETP-CNY", - "symbol": "etp", - "name": "MetaverseETP CNY" - }, - { - "id": "ETP-ETH", - "symbol": "etp", - "name": "MetaverseETP ETH" - }, - { - "id": "ETP-EUR", - "symbol": "etp", - "name": "MetaverseETP EUR" - }, - { - "id": "ETP-GBP", - "symbol": "etp", - "name": "MetaverseETP GBP" - }, - { - "id": "ETP-INR", - "symbol": "etp", - "name": "MetaverseETP INR" - }, - { - "id": "ETP-JPY", - "symbol": "etp", - "name": "MetaverseETP JPY" - }, - { - "id": "ETP-KRW", - "symbol": "etp", - "name": "MetaverseETP KRW" - }, - { - "id": "ETP-RUB", - "symbol": "etp", - "name": "MetaverseETP RUB" - }, - { - "id": "ETP-USD", - "symbol": "etp", - "name": "MetaverseETP USD" - }, - { - "id": "EWT-AUD", - "symbol": "ewt", - "name": "EnergyWebToken AUD" - }, - { - "id": "EWT-BTC", - "symbol": "ewt", - "name": "EnergyWebToken BTC" - }, - { - "id": "EWT-CAD", - "symbol": "ewt", - "name": "EnergyWebToken CAD" - }, - { - "id": "EWT-CNY", - "symbol": "ewt", - "name": "EnergyWebToken CNY" - }, - { - "id": "EWT-ETH", - "symbol": "ewt", - "name": "EnergyWebToken ETH" - }, - { - "id": "EWT-EUR", - "symbol": "ewt", - "name": "EnergyWebToken EUR" - }, - { - "id": "EWT-GBP", - "symbol": "ewt", - "name": "EnergyWebToken GBP" - }, - { - "id": "EWT-INR", - "symbol": "ewt", - "name": "EnergyWebToken INR" - }, - { - "id": "EWT-JPY", - "symbol": "ewt", - "name": "EnergyWebToken JPY" - }, - { - "id": "EWT-KRW", - "symbol": "ewt", - "name": "EnergyWebToken KRW" - }, - { - "id": "EWT-RUB", - "symbol": "ewt", - "name": "EnergyWebToken RUB" - }, - { - "id": "EWT-USD", - "symbol": "ewt", - "name": "EnergyWebToken USD" - }, - { - "id": "FAIR-BTC", - "symbol": "fair", - "name": "FairCoin BTC" - }, - { - "id": "FAIR-CNY", - "symbol": "fair", - "name": "FairCoin CNY" - }, - { - "id": "FAIR-ETH", - "symbol": "fair", - "name": "FairCoin ETH" - }, - { - "id": "FAIR-EUR", - "symbol": "fair", - "name": "FairCoin EUR" - }, - { - "id": "FAIR-GBP", - "symbol": "fair", - "name": "FairCoin GBP" - }, - { - "id": "FAIR-INR", - "symbol": "fair", - "name": "FairCoin INR" - }, - { - "id": "FAIR-JPY", - "symbol": "fair", - "name": "FairCoin JPY" - }, - { - "id": "FAIR-KRW", - "symbol": "fair", - "name": "FairCoin KRW" - }, - { - "id": "FAIR-RUB", - "symbol": "fair", - "name": "FairCoin RUB" - }, - { - "id": "FAIR-USD", - "symbol": "fair", - "name": "FairCoin USD" - }, - { - "id": "FCT-AUD", - "symbol": "fct", - "name": "Factom AUD" - }, - { - "id": "FCT-BTC", - "symbol": "fct", - "name": "Factom BTC" - }, - { - "id": "FCT-CAD", - "symbol": "fct", - "name": "Factom CAD" - }, - { - "id": "FCT-CNY", - "symbol": "fct", - "name": "Factom CNY" - }, - { - "id": "FCT-ETH", - "symbol": "fct", - "name": "Factom ETH" - }, - { - "id": "FCT-EUR", - "symbol": "fct", - "name": "Factom EUR" - }, - { - "id": "FCT-GBP", - "symbol": "fct", - "name": "Factom GBP" - }, - { - "id": "FCT-JPY", - "symbol": "fct", - "name": "Factom JPY" - }, - { - "id": "FCT-KRW", - "symbol": "fct", - "name": "Factom KRW" - }, - { - "id": "FCT-USD", - "symbol": "fct", - "name": "Factom USD" - }, - { - "id": "FET-CAD", - "symbol": "fet", - "name": "Fetch CAD" - }, - { - "id": "FET-CNY", - "symbol": "fet", - "name": "Fetch CNY" - }, - { - "id": "FET-ETH", - "symbol": "fet", - "name": "Fetch ETH" - }, - { - "id": "FET-EUR", - "symbol": "fet", - "name": "Fetch EUR" - }, - { - "id": "FET-GBP", - "symbol": "fet", - "name": "Fetch GBP" - }, - { - "id": "FET-INR", - "symbol": "fet", - "name": "Fetch INR" - }, - { - "id": "FET-JPY", - "symbol": "fet", - "name": "Fetch JPY" - }, - { - "id": "FET-KRW", - "symbol": "fet", - "name": "Fetch KRW" - }, - { - "id": "FET-RUB", - "symbol": "fet", - "name": "Fetch RUB" - }, - { - "id": "FET-USD", - "symbol": "fet", - "name": "Fetch USD" - }, - { - "id": "FIL-AUD", - "symbol": "fil", - "name": "FilecoinFutures AUD" - }, - { - "id": "FIL-CAD", - "symbol": "fil", - "name": "FilecoinFutures CAD" - }, - { - "id": "FIL-CNY", - "symbol": "fil", - "name": "FilecoinFutures CNY" - }, - { - "id": "FIL-ETH", - "symbol": "fil", - "name": "FilecoinFutures ETH" - }, - { - "id": "FIL-EUR", - "symbol": "fil", - "name": "FilecoinFutures EUR" - }, - { - "id": "FIL-GBP", - "symbol": "fil", - "name": "FilecoinFutures GBP" - }, - { - "id": "FIL-INR", - "symbol": "fil", - "name": "FilecoinFutures INR" - }, - { - "id": "FIL-JPY", - "symbol": "fil", - "name": "FilecoinFutures JPY" - }, - { - "id": "FIL-KRW", - "symbol": "fil", - "name": "FilecoinFutures KRW" - }, - { - "id": "FIL-RUB", - "symbol": "fil", - "name": "FilecoinFutures RUB" - }, - { - "id": "FIL-USD", - "symbol": "fil", - "name": "FilecoinFutures USD" - }, - { - "id": "FIO-CAD", - "symbol": "fio", - "name": "FIOProtocol CAD" - }, - { - "id": "FIO-CNY", - "symbol": "fio", - "name": "FIOProtocol CNY" - }, - { - "id": "FIO-ETH", - "symbol": "fio", - "name": "FIOProtocol ETH" - }, - { - "id": "FIO-EUR", - "symbol": "fio", - "name": "FIOProtocol EUR" - }, - { - "id": "FIO-GBP", - "symbol": "fio", - "name": "FIOProtocol GBP" - }, - { - "id": "FIO-INR", - "symbol": "fio", - "name": "FIOProtocol INR" - }, - { - "id": "FIO-JPY", - "symbol": "fio", - "name": "FIOProtocol JPY" - }, - { - "id": "FIO-KRW", - "symbol": "fio", - "name": "FIOProtocol KRW" - }, - { - "id": "FIO-RUB", - "symbol": "fio", - "name": "FIOProtocol RUB" - }, - { - "id": "FIO-USD", - "symbol": "fio", - "name": "FIOProtocol USD" - }, - { - "id": "FIRO-CAD", - "symbol": "firo", - "name": "Firo CAD" - }, - { - "id": "FIRO-CNY", - "symbol": "firo", - "name": "Firo CNY" - }, - { - "id": "FIRO-ETH", - "symbol": "firo", - "name": "Firo ETH" - }, - { - "id": "FIRO-EUR", - "symbol": "firo", - "name": "Firo EUR" - }, - { - "id": "FIRO-GBP", - "symbol": "firo", - "name": "Firo GBP" - }, - { - "id": "FIRO-INR", - "symbol": "firo", - "name": "Firo INR" - }, - { - "id": "FIRO-JPY", - "symbol": "firo", - "name": "Firo JPY" - }, - { - "id": "FIRO-KRW", - "symbol": "firo", - "name": "Firo KRW" - }, - { - "id": "FIRO-RUB", - "symbol": "firo", - "name": "Firo RUB" - }, - { - "id": "FIRO-USD", - "symbol": "firo", - "name": "Firo USD" - }, - { - "id": "FLASH-ETH", - "symbol": "flash", - "name": "Flash ETH" - }, - { - "id": "FLASH-EUR", - "symbol": "flash", - "name": "Flash EUR" - }, - { - "id": "FLASH-GBP", - "symbol": "flash", - "name": "Flash GBP" - }, - { - "id": "FLASH-INR", - "symbol": "flash", - "name": "Flash INR" - }, - { - "id": "FLASH-JPY", - "symbol": "flash", - "name": "Flash JPY" - }, - { - "id": "FLASH-KRW", - "symbol": "flash", - "name": "Flash KRW" - }, - { - "id": "FLASH-RUB", - "symbol": "flash", - "name": "Flash RUB" - }, - { - "id": "FLASH-USD", - "symbol": "flash", - "name": "Flash USD" - }, - { - "id": "FO-CAD", - "symbol": "fo", - "name": "FIBOS CAD" - }, - { - "id": "FO-CNY", - "symbol": "fo", - "name": "FIBOS CNY" - }, - { - "id": "FO-ETH", - "symbol": "fo", - "name": "FIBOS ETH" - }, - { - "id": "FO-EUR", - "symbol": "fo", - "name": "FIBOS EUR" - }, - { - "id": "FO-GBP", - "symbol": "fo", - "name": "FIBOS GBP" - }, - { - "id": "FO-INR", - "symbol": "fo", - "name": "FIBOS INR" - }, - { - "id": "FO-JPY", - "symbol": "fo", - "name": "FIBOS JPY" - }, - { - "id": "FO-KRW", - "symbol": "fo", - "name": "FIBOS KRW" - }, - { - "id": "FO-RUB", - "symbol": "fo", - "name": "FIBOS RUB" - }, - { - "id": "FO-USD", - "symbol": "fo", - "name": "FIBOS USD" - }, - { - "id": "FRST-AUD", - "symbol": "frst", - "name": "FirstCoin AUD" - }, - { - "id": "FRST-BTC", - "symbol": "frst", - "name": "FirstCoin BTC" - }, - { - "id": "FRST-CAD", - "symbol": "frst", - "name": "FirstCoin CAD" - }, - { - "id": "FRST-CNY", - "symbol": "frst", - "name": "FirstCoin CNY" - }, - { - "id": "FRST-ETH", - "symbol": "frst", - "name": "FirstCoin ETH" - }, - { - "id": "FRST-EUR", - "symbol": "frst", - "name": "FirstCoin EUR" - }, - { - "id": "FRST-GBP", - "symbol": "frst", - "name": "FirstCoin GBP" - }, - { - "id": "FRST-INR", - "symbol": "frst", - "name": "FirstCoin INR" - }, - { - "id": "FRST-KRW", - "symbol": "frst", - "name": "FirstCoin KRW" - }, - { - "id": "FRST-USD", - "symbol": "frst", - "name": "FirstCoin USD" - }, - { - "id": "FTC-CAD", - "symbol": "ftc", - "name": "Feathercoin CAD" - }, - { - "id": "FTC-CNY", - "symbol": "ftc", - "name": "Feathercoin CNY" - }, - { - "id": "FTC-ETH", - "symbol": "ftc", - "name": "Feathercoin ETH" - }, - { - "id": "FTC-EUR", - "symbol": "ftc", - "name": "Feathercoin EUR" - }, - { - "id": "FTC-GBP", - "symbol": "ftc", - "name": "Feathercoin GBP" - }, - { - "id": "FTC-INR", - "symbol": "ftc", - "name": "Feathercoin INR" - }, - { - "id": "FTC-JPY", - "symbol": "ftc", - "name": "Feathercoin JPY" - }, - { - "id": "FTC-KRW", - "symbol": "ftc", - "name": "Feathercoin KRW" - }, - { - "id": "FTC-RUB", - "symbol": "ftc", - "name": "Feathercoin RUB" - }, - { - "id": "FTC-USD", - "symbol": "ftc", - "name": "Feathercoin USD" - }, - { - "id": "FUN-BTC", - "symbol": "fun", - "name": "FunFair BTC" - }, - { - "id": "FUN-CAD", - "symbol": "fun", - "name": "FunFair CAD" - }, - { - "id": "FUN-CNY", - "symbol": "fun", - "name": "FunFair CNY" - }, - { - "id": "FUN-ETH", - "symbol": "fun", - "name": "FunFair ETH" - }, - { - "id": "FUN-EUR", - "symbol": "fun", - "name": "FunFair EUR" - }, - { - "id": "FUN-GBP", - "symbol": "fun", - "name": "FunFair GBP" - }, - { - "id": "FUN-INR", - "symbol": "fun", - "name": "FunFair INR" - }, - { - "id": "FUN-KRW", - "symbol": "fun", - "name": "FunFair KRW" - }, - { - "id": "FUN-RUB", - "symbol": "fun", - "name": "FunFair RUB" - }, - { - "id": "FUN-USD", - "symbol": "fun", - "name": "FunFair USD" - }, - { - "id": "GAME-AUD", - "symbol": "game", - "name": "GameCredits AUD" - }, - { - "id": "GAME-BTC", - "symbol": "game", - "name": "GameCredits BTC" - }, - { - "id": "GAME-ETH", - "symbol": "game", - "name": "GameCredits ETH" - }, - { - "id": "GAME-EUR", - "symbol": "game", - "name": "GameCredits EUR" - }, - { - "id": "GAME-GBP", - "symbol": "game", - "name": "GameCredits GBP" - }, - { - "id": "GAME-INR", - "symbol": "game", - "name": "GameCredits INR" - }, - { - "id": "GAME-JPY", - "symbol": "game", - "name": "GameCredits JPY" - }, - { - "id": "GAME-KRW", - "symbol": "game", - "name": "GameCredits KRW" - }, - { - "id": "GAME-RUB", - "symbol": "game", - "name": "GameCredits RUB" - }, - { - "id": "GAME-USD", - "symbol": "game", - "name": "GameCredits USD" - }, - { - "id": "GBYTE-BTC", - "symbol": "gbyte", - "name": "Obyte BTC" - }, - { - "id": "GBYTE-CAD", - "symbol": "gbyte", - "name": "Obyte CAD" - }, - { - "id": "GBYTE-CNY", - "symbol": "gbyte", - "name": "Obyte CNY" - }, - { - "id": "GBYTE-ETH", - "symbol": "gbyte", - "name": "Obyte ETH" - }, - { - "id": "GBYTE-EUR", - "symbol": "gbyte", - "name": "Obyte EUR" - }, - { - "id": "GBYTE-GBP", - "symbol": "gbyte", - "name": "Obyte GBP" - }, - { - "id": "GBYTE-INR", - "symbol": "gbyte", - "name": "Obyte INR" - }, - { - "id": "GBYTE-JPY", - "symbol": "gbyte", - "name": "Obyte JPY" - }, - { - "id": "GBYTE-KRW", - "symbol": "gbyte", - "name": "Obyte KRW" - }, - { - "id": "GBYTE-USD", - "symbol": "gbyte", - "name": "Obyte USD" - }, - { - "id": "GCC1-BTC", - "symbol": "gcc1", - "name": "GlobalCryptocurrency BTC" - }, - { - "id": "GCC1-CAD", - "symbol": "gcc1", - "name": "GlobalCryptocurrency CAD" - }, - { - "id": "GCC1-CNY", - "symbol": "gcc1", - "name": "GlobalCryptocurrency CNY" - }, - { - "id": "GCC1-ETH", - "symbol": "gcc1", - "name": "GlobalCryptocurrency ETH" - }, - { - "id": "GCC1-EUR", - "symbol": "gcc1", - "name": "GlobalCryptocurrency EUR" - }, - { - "id": "GCC1-GBP", - "symbol": "gcc1", - "name": "GlobalCryptocurrency GBP" - }, - { - "id": "GCC1-INR", - "symbol": "gcc1", - "name": "GlobalCryptocurrency INR" - }, - { - "id": "GCC1-JPY", - "symbol": "gcc1", - "name": "GlobalCryptocurrency JPY" - }, - { - "id": "GCC1-KRW", - "symbol": "gcc1", - "name": "GlobalCryptocurrency KRW" - }, - { - "id": "GCC1-RUB", - "symbol": "gcc1", - "name": "GlobalCryptocurrency RUB" - }, - { - "id": "GCC1-USD", - "symbol": "gcc1", - "name": "GlobalCryptocurrency USD" - }, - { - "id": "GHOST-CAD", - "symbol": "ghost", - "name": "GHOSTPRISM CAD" - }, - { - "id": "GHOST-CNY", - "symbol": "ghost", - "name": "GHOSTPRISM CNY" - }, - { - "id": "GHOST-ETH", - "symbol": "ghost", - "name": "GHOSTPRISM ETH" - }, - { - "id": "GHOST-EUR", - "symbol": "ghost", - "name": "GHOSTPRISM EUR" - }, - { - "id": "GHOST-GBP", - "symbol": "ghost", - "name": "GHOSTPRISM GBP" - }, - { - "id": "GHOST-INR", - "symbol": "ghost", - "name": "GHOSTPRISM INR" - }, - { - "id": "GHOST-JPY", - "symbol": "ghost", - "name": "GHOSTPRISM JPY" - }, - { - "id": "GHOST-KRW", - "symbol": "ghost", - "name": "GHOSTPRISM KRW" - }, - { - "id": "GHOST-RUB", - "symbol": "ghost", - "name": "GHOSTPRISM RUB" - }, - { - "id": "GHOST-USD", - "symbol": "ghost", - "name": "GHOSTPRISM USD" - }, - { - "id": "GHOST1-CAD", - "symbol": "ghost1", - "name": "GHOST CAD" - }, - { - "id": "GHOST1-CNY", - "symbol": "ghost1", - "name": "GHOST CNY" - }, - { - "id": "GHOST1-ETH", - "symbol": "ghost1", - "name": "GHOST ETH" - }, - { - "id": "GHOST1-EUR", - "symbol": "ghost1", - "name": "GHOST EUR" - }, - { - "id": "GHOST1-GBP", - "symbol": "ghost1", - "name": "GHOST GBP" - }, - { - "id": "GHOST1-INR", - "symbol": "ghost1", - "name": "GHOST INR" - }, - { - "id": "GHOST1-JPY", - "symbol": "ghost1", - "name": "GHOST JPY" - }, - { - "id": "GHOST1-KRW", - "symbol": "ghost1", - "name": "GHOST KRW" - }, - { - "id": "GHOST1-RUB", - "symbol": "ghost1", - "name": "GHOST RUB" - }, - { - "id": "GHOST1-USD", - "symbol": "ghost1", - "name": "GHOST USD" - }, - { - "id": "GLEEC-CAD", - "symbol": "gleec", - "name": "Gleec CAD" - }, - { - "id": "GLEEC-CNY", - "symbol": "gleec", - "name": "Gleec CNY" - }, - { - "id": "GLEEC-ETH", - "symbol": "gleec", - "name": "Gleec ETH" - }, - { - "id": "GLEEC-EUR", - "symbol": "gleec", - "name": "Gleec EUR" - }, - { - "id": "GLEEC-GBP", - "symbol": "gleec", - "name": "Gleec GBP" - }, - { - "id": "GLEEC-INR", - "symbol": "gleec", - "name": "Gleec INR" - }, - { - "id": "GLEEC-JPY", - "symbol": "gleec", - "name": "Gleec JPY" - }, - { - "id": "GLEEC-KRW", - "symbol": "gleec", - "name": "Gleec KRW" - }, - { - "id": "GLEEC-RUB", - "symbol": "gleec", - "name": "Gleec RUB" - }, - { - "id": "GLEEC-USD", - "symbol": "gleec", - "name": "Gleec USD" - }, - { - "id": "GLM-CAD", - "symbol": "glm", - "name": "Golem CAD" - }, - { - "id": "GLM-CNY", - "symbol": "glm", - "name": "Golem CNY" - }, - { - "id": "GLM-ETH", - "symbol": "glm", - "name": "Golem ETH" - }, - { - "id": "GLM-EUR", - "symbol": "glm", - "name": "Golem EUR" - }, - { - "id": "GLM-GBP", - "symbol": "glm", - "name": "Golem GBP" - }, - { - "id": "GLM-INR", - "symbol": "glm", - "name": "Golem INR" - }, - { - "id": "GLM-JPY", - "symbol": "glm", - "name": "Golem JPY" - }, - { - "id": "GLM-KRW", - "symbol": "glm", - "name": "Golem KRW" - }, - { - "id": "GLM-RUB", - "symbol": "glm", - "name": "Golem RUB" - }, - { - "id": "GLM-USD", - "symbol": "glm", - "name": "Golem USD" - }, - { - "id": "GNO-CAD", - "symbol": "gno", - "name": "Gnosis CAD" - }, - { - "id": "GNO-CNY", - "symbol": "gno", - "name": "Gnosis CNY" - }, - { - "id": "GNO-ETH", - "symbol": "gno", - "name": "Gnosis ETH" - }, - { - "id": "GNO-EUR", - "symbol": "gno", - "name": "Gnosis EUR" - }, - { - "id": "GNO-GBP", - "symbol": "gno", - "name": "Gnosis GBP" - }, - { - "id": "GNO-INR", - "symbol": "gno", - "name": "Gnosis INR" - }, - { - "id": "GNO-JPY", - "symbol": "gno", - "name": "Gnosis JPY" - }, - { - "id": "GNO-KRW", - "symbol": "gno", - "name": "Gnosis KRW" - }, - { - "id": "GNO-RUB", - "symbol": "gno", - "name": "Gnosis RUB" - }, - { - "id": "GNO-USD", - "symbol": "gno", - "name": "Gnosis USD" - }, - { - "id": "GO-CAD", - "symbol": "go", - "name": "GoChain CAD" - }, - { - "id": "GO-CNY", - "symbol": "go", - "name": "GoChain CNY" - }, - { - "id": "GO-ETH", - "symbol": "go", - "name": "GoChain ETH" - }, - { - "id": "GO-EUR", - "symbol": "go", - "name": "GoChain EUR" - }, - { - "id": "GO-GBP", - "symbol": "go", - "name": "GoChain GBP" - }, - { - "id": "GO-INR", - "symbol": "go", - "name": "GoChain INR" - }, - { - "id": "GO-JPY", - "symbol": "go", - "name": "GoChain JPY" - }, - { - "id": "GO-KRW", - "symbol": "go", - "name": "GoChain KRW" - }, - { - "id": "GO-RUB", - "symbol": "go", - "name": "GoChain RUB" - }, - { - "id": "GO-USD", - "symbol": "go", - "name": "GoChain USD" - }, - { - "id": "GRC-CAD", - "symbol": "grc", - "name": "GridCoin CAD" - }, - { - "id": "GRC-CNY", - "symbol": "grc", - "name": "GridCoin CNY" - }, - { - "id": "GRC-ETH", - "symbol": "grc", - "name": "GridCoin ETH" - }, - { - "id": "GRC-EUR", - "symbol": "grc", - "name": "GridCoin EUR" - }, - { - "id": "GRC-GBP", - "symbol": "grc", - "name": "GridCoin GBP" - }, - { - "id": "GRC-INR", - "symbol": "grc", - "name": "GridCoin INR" - }, - { - "id": "GRC-JPY", - "symbol": "grc", - "name": "GridCoin JPY" - }, - { - "id": "GRC-KRW", - "symbol": "grc", - "name": "GridCoin KRW" - }, - { - "id": "GRC-RUB", - "symbol": "grc", - "name": "GridCoin RUB" - }, - { - "id": "GRC-USD", - "symbol": "grc", - "name": "GridCoin USD" - }, - { - "id": "GRN-BTC", - "symbol": "grn", - "name": "GreenPower BTC" - }, - { - "id": "GRN-RUB", - "symbol": "grn", - "name": "GreenPower RUB" - }, - { - "id": "GRN-USD", - "symbol": "grn", - "name": "GreenPower USD" - }, - { - "id": "GRS-CAD", - "symbol": "grs", - "name": "Groestlcoin CAD" - }, - { - "id": "GRS-CNY", - "symbol": "grs", - "name": "Groestlcoin CNY" - }, - { - "id": "GRS-ETH", - "symbol": "grs", - "name": "Groestlcoin ETH" - }, - { - "id": "GRS-EUR", - "symbol": "grs", - "name": "Groestlcoin EUR" - }, - { - "id": "GRS-GBP", - "symbol": "grs", - "name": "Groestlcoin GBP" - }, - { - "id": "GRS-INR", - "symbol": "grs", - "name": "Groestlcoin INR" - }, - { - "id": "GRS-JPY", - "symbol": "grs", - "name": "Groestlcoin JPY" - }, - { - "id": "GRS-KRW", - "symbol": "grs", - "name": "Groestlcoin KRW" - }, - { - "id": "GRS-RUB", - "symbol": "grs", - "name": "Groestlcoin RUB" - }, - { - "id": "GRS-USD", - "symbol": "grs", - "name": "Groestlcoin USD" - }, - { - "id": "GRT2-CAD", - "symbol": "grt2", - "name": "TheGraph CAD" - }, - { - "id": "GRT2-CNY", - "symbol": "grt2", - "name": "TheGraph CNY" - }, - { - "id": "GRT2-ETH", - "symbol": "grt2", - "name": "TheGraph ETH" - }, - { - "id": "GRT2-EUR", - "symbol": "grt2", - "name": "TheGraph EUR" - }, - { - "id": "GRT2-GBP", - "symbol": "grt2", - "name": "TheGraph GBP" - }, - { - "id": "GRT2-INR", - "symbol": "grt2", - "name": "TheGraph INR" - }, - { - "id": "GRT2-JPY", - "symbol": "grt2", - "name": "TheGraph JPY" - }, - { - "id": "GRT2-KRW", - "symbol": "grt2", - "name": "TheGraph KRW" - }, - { - "id": "GRT2-RUB", - "symbol": "grt2", - "name": "TheGraph RUB" - }, - { - "id": "GRT2-USD", - "symbol": "grt2", - "name": "TheGraph USD" - }, - { - "id": "GXC-CAD", - "symbol": "gxc", - "name": "GXChain CAD" - }, - { - "id": "GXC-CNY", - "symbol": "gxc", - "name": "GXChain CNY" - }, - { - "id": "GXC-ETH", - "symbol": "gxc", - "name": "GXChain ETH" - }, - { - "id": "GXC-EUR", - "symbol": "gxc", - "name": "GXChain EUR" - }, - { - "id": "GXC-GBP", - "symbol": "gxc", - "name": "GXChain GBP" - }, - { - "id": "GXC-INR", - "symbol": "gxc", - "name": "GXChain INR" - }, - { - "id": "GXC-JPY", - "symbol": "gxc", - "name": "GXChain JPY" - }, - { - "id": "GXC-KRW", - "symbol": "gxc", - "name": "GXChain KRW" - }, - { - "id": "GXC-RUB", - "symbol": "gxc", - "name": "GXChain RUB" - }, - { - "id": "GXC-USD", - "symbol": "gxc", - "name": "GXChain USD" - }, - { - "id": "HBAR-AUD", - "symbol": "hbar", - "name": "HederaHashgraph AUD" - }, - { - "id": "HBAR-BTC", - "symbol": "hbar", - "name": "HederaHashgraph BTC" - }, - { - "id": "HBAR-CAD", - "symbol": "hbar", - "name": "HederaHashgraph CAD" - }, - { - "id": "HBAR-CNY", - "symbol": "hbar", - "name": "HederaHashgraph CNY" - }, - { - "id": "HBAR-ETH", - "symbol": "hbar", - "name": "HederaHashgraph ETH" - }, - { - "id": "HBAR-EUR", - "symbol": "hbar", - "name": "HederaHashgraph EUR" - }, - { - "id": "HBAR-GBP", - "symbol": "hbar", - "name": "HederaHashgraph GBP" - }, - { - "id": "HBAR-INR", - "symbol": "hbar", - "name": "HederaHashgraph INR" - }, - { - "id": "HBAR-JPY", - "symbol": "hbar", - "name": "HederaHashgraph JPY" - }, - { - "id": "HBAR-KRW", - "symbol": "hbar", - "name": "HederaHashgraph KRW" - }, - { - "id": "HBAR-RUB", - "symbol": "hbar", - "name": "HederaHashgraph RUB" - }, - { - "id": "HBAR-USD", - "symbol": "hbar", - "name": "HederaHashgraph USD" - }, - { - "id": "HC-CAD", - "symbol": "hc", - "name": "HyperCash CAD" - }, - { - "id": "HC-CNY", - "symbol": "hc", - "name": "HyperCash CNY" - }, - { - "id": "HC-ETH", - "symbol": "hc", - "name": "HyperCash ETH" - }, - { - "id": "HC-EUR", - "symbol": "hc", - "name": "HyperCash EUR" - }, - { - "id": "HC-GBP", - "symbol": "hc", - "name": "HyperCash GBP" - }, - { - "id": "HC-INR", - "symbol": "hc", - "name": "HyperCash INR" - }, - { - "id": "HC-JPY", - "symbol": "hc", - "name": "HyperCash JPY" - }, - { - "id": "HC-KRW", - "symbol": "hc", - "name": "HyperCash KRW" - }, - { - "id": "HC-RUB", - "symbol": "hc", - "name": "HyperCash RUB" - }, - { - "id": "HC-USD", - "symbol": "hc", - "name": "HyperCash USD" - }, - { - "id": "HIVE-KRW", - "symbol": "hive", - "name": "Hive KRW" - }, - { - "id": "HIVE-RUB", - "symbol": "hive", - "name": "Hive RUB" - }, - { - "id": "HIVE-USD", - "symbol": "hive", - "name": "Hive USD" - }, - { - "id": "HNC-AUD", - "symbol": "hnc", - "name": "Helleniccoin AUD" - }, - { - "id": "HNC-CNY", - "symbol": "hnc", - "name": "Helleniccoin CNY" - }, - { - "id": "HNC-ETH", - "symbol": "hnc", - "name": "Helleniccoin ETH" - }, - { - "id": "HNC-EUR", - "symbol": "hnc", - "name": "Helleniccoin EUR" - }, - { - "id": "HNC-GBP", - "symbol": "hnc", - "name": "Helleniccoin GBP" - }, - { - "id": "HNC-INR", - "symbol": "hnc", - "name": "Helleniccoin INR" - }, - { - "id": "HNC-JPY", - "symbol": "hnc", - "name": "Helleniccoin JPY" - }, - { - "id": "HNC-KRW", - "symbol": "hnc", - "name": "Helleniccoin KRW" - }, - { - "id": "HNC-RUB", - "symbol": "hnc", - "name": "Helleniccoin RUB" - }, - { - "id": "HNC-USD", - "symbol": "hnc", - "name": "Helleniccoin USD" - }, - { - "id": "HNS-CAD", - "symbol": "hns", - "name": "Handshake CAD" - }, - { - "id": "HNS-CNY", - "symbol": "hns", - "name": "Handshake CNY" - }, - { - "id": "HNS-ETH", - "symbol": "hns", - "name": "Handshake ETH" - }, - { - "id": "HNS-EUR", - "symbol": "hns", - "name": "Handshake EUR" - }, - { - "id": "HNS-GBP", - "symbol": "hns", - "name": "Handshake GBP" - }, - { - "id": "HNS-INR", - "symbol": "hns", - "name": "Handshake INR" - }, - { - "id": "HNS-JPY", - "symbol": "hns", - "name": "Handshake JPY" - }, - { - "id": "HNS-KRW", - "symbol": "hns", - "name": "Handshake KRW" - }, - { - "id": "HNS-RUB", - "symbol": "hns", - "name": "Handshake RUB" - }, - { - "id": "HNS-USD", - "symbol": "hns", - "name": "Handshake USD" - }, - { - "id": "HNT1-CAD", - "symbol": "hnt1", - "name": "Helium CAD" - }, - { - "id": "HNT1-CNY", - "symbol": "hnt1", - "name": "Helium CNY" - }, - { - "id": "HNT1-ETH", - "symbol": "hnt1", - "name": "Helium ETH" - }, - { - "id": "HNT1-EUR", - "symbol": "hnt1", - "name": "Helium EUR" - }, - { - "id": "HNT1-GBP", - "symbol": "hnt1", - "name": "Helium GBP" - }, - { - "id": "HNT1-INR", - "symbol": "hnt1", - "name": "Helium INR" - }, - { - "id": "HNT1-JPY", - "symbol": "hnt1", - "name": "Helium JPY" - }, - { - "id": "HNT1-KRW", - "symbol": "hnt1", - "name": "Helium KRW" - }, - { - "id": "HNT1-RUB", - "symbol": "hnt1", - "name": "Helium RUB" - }, - { - "id": "HNT1-USD", - "symbol": "hnt1", - "name": "Helium USD" - }, - { - "id": "HPB-CAD", - "symbol": "hpb", - "name": "HighPerformanceBlockchain CAD" - }, - { - "id": "HPB-CNY", - "symbol": "hpb", - "name": "HighPerformanceBlockchain CNY" - }, - { - "id": "HPB-ETH", - "symbol": "hpb", - "name": "HighPerformanceBlockchain ETH" - }, - { - "id": "HPB-EUR", - "symbol": "hpb", - "name": "HighPerformanceBlockchain EUR" - }, - { - "id": "HPB-GBP", - "symbol": "hpb", - "name": "HighPerformanceBlockchain GBP" - }, - { - "id": "HPB-INR", - "symbol": "hpb", - "name": "HighPerformanceBlockchain INR" - }, - { - "id": "HPB-JPY", - "symbol": "hpb", - "name": "HighPerformanceBlockchain JPY" - }, - { - "id": "HPB-KRW", - "symbol": "hpb", - "name": "HighPerformanceBlockchain KRW" - }, - { - "id": "HPB-RUB", - "symbol": "hpb", - "name": "HighPerformanceBlockchain RUB" - }, - { - "id": "HPB-USD", - "symbol": "hpb", - "name": "HighPerformanceBlockchain USD" - }, - { - "id": "HSS-CAD", - "symbol": "hss", - "name": "Hashshare CAD" - }, - { - "id": "HSS-CNY", - "symbol": "hss", - "name": "Hashshare CNY" - }, - { - "id": "HSS-ETH", - "symbol": "hss", - "name": "Hashshare ETH" - }, - { - "id": "HSS-EUR", - "symbol": "hss", - "name": "Hashshare EUR" - }, - { - "id": "HSS-GBP", - "symbol": "hss", - "name": "Hashshare GBP" - }, - { - "id": "HSS-INR", - "symbol": "hss", - "name": "Hashshare INR" - }, - { - "id": "HSS-JPY", - "symbol": "hss", - "name": "Hashshare JPY" - }, - { - "id": "HSS-KRW", - "symbol": "hss", - "name": "Hashshare KRW" - }, - { - "id": "HSS-RUB", - "symbol": "hss", - "name": "Hashshare RUB" - }, - { - "id": "HSS-USD", - "symbol": "hss", - "name": "Hashshare USD" - }, - { - "id": "HTDF-CAD", - "symbol": "htdf", - "name": "OrientWalt CAD" - }, - { - "id": "HTDF-CNY", - "symbol": "htdf", - "name": "OrientWalt CNY" - }, - { - "id": "HTDF-ETH", - "symbol": "htdf", - "name": "OrientWalt ETH" - }, - { - "id": "HTDF-EUR", - "symbol": "htdf", - "name": "OrientWalt EUR" - }, - { - "id": "HTDF-GBP", - "symbol": "htdf", - "name": "OrientWalt GBP" - }, - { - "id": "HTDF-INR", - "symbol": "htdf", - "name": "OrientWalt INR" - }, - { - "id": "HTDF-JPY", - "symbol": "htdf", - "name": "OrientWalt JPY" - }, - { - "id": "HTDF-KRW", - "symbol": "htdf", - "name": "OrientWalt KRW" - }, - { - "id": "HTDF-RUB", - "symbol": "htdf", - "name": "OrientWalt RUB" - }, - { - "id": "HTDF-USD", - "symbol": "htdf", - "name": "OrientWalt USD" - }, - { - "id": "HTML-CAD", - "symbol": "html", - "name": "HTMLCOIN CAD" - }, - { - "id": "HTML-CNY", - "symbol": "html", - "name": "HTMLCOIN CNY" - }, - { - "id": "HTML-ETH", - "symbol": "html", - "name": "HTMLCOIN ETH" - }, - { - "id": "HTML-EUR", - "symbol": "html", - "name": "HTMLCOIN EUR" - }, - { - "id": "HTML-GBP", - "symbol": "html", - "name": "HTMLCOIN GBP" - }, - { - "id": "HTML-INR", - "symbol": "html", - "name": "HTMLCOIN INR" - }, - { - "id": "HTML-JPY", - "symbol": "html", - "name": "HTMLCOIN JPY" - }, - { - "id": "HTML-KRW", - "symbol": "html", - "name": "HTMLCOIN KRW" - }, - { - "id": "HTML-RUB", - "symbol": "html", - "name": "HTMLCOIN RUB" - }, - { - "id": "HTML-USD", - "symbol": "html", - "name": "HTMLCOIN USD" - }, - { - "id": "HYC-CNY", - "symbol": "hyc", - "name": "HYCON CNY" - }, - { - "id": "HYC-ETH", - "symbol": "hyc", - "name": "HYCON ETH" - }, - { - "id": "HYC-EUR", - "symbol": "hyc", - "name": "HYCON EUR" - }, - { - "id": "HYC-GBP", - "symbol": "hyc", - "name": "HYCON GBP" - }, - { - "id": "HYC-INR", - "symbol": "hyc", - "name": "HYCON INR" - }, - { - "id": "HYC-JPY", - "symbol": "hyc", - "name": "HYCON JPY" - }, - { - "id": "HYC-KRW", - "symbol": "hyc", - "name": "HYCON KRW" - }, - { - "id": "HYC-RUB", - "symbol": "hyc", - "name": "HYCON RUB" - }, - { - "id": "HYC-USD", - "symbol": "hyc", - "name": "HYCON USD" - }, - { - "id": "ICX-AUD", - "symbol": "icx", - "name": "ICON AUD" - }, - { - "id": "ICX-BTC", - "symbol": "icx", - "name": "ICON BTC" - }, - { - "id": "ICX-CNY", - "symbol": "icx", - "name": "ICON CNY" - }, - { - "id": "ICX-ETH", - "symbol": "icx", - "name": "ICON ETH" - }, - { - "id": "ICX-EUR", - "symbol": "icx", - "name": "ICON EUR" - }, - { - "id": "ICX-GBP", - "symbol": "icx", - "name": "ICON GBP" - }, - { - "id": "ICX-INR", - "symbol": "icx", - "name": "ICON INR" - }, - { - "id": "ICX-JPY", - "symbol": "icx", - "name": "ICON JPY" - }, - { - "id": "ICX-RUB", - "symbol": "icx", - "name": "ICON RUB" - }, - { - "id": "ICX-USD", - "symbol": "icx", - "name": "ICON USD" - }, - { - "id": "IDNA-CAD", - "symbol": "idna", - "name": "Idena CAD" - }, - { - "id": "IDNA-CNY", - "symbol": "idna", - "name": "Idena CNY" - }, - { - "id": "IDNA-ETH", - "symbol": "idna", - "name": "Idena ETH" - }, - { - "id": "IDNA-EUR", - "symbol": "idna", - "name": "Idena EUR" - }, - { - "id": "IDNA-GBP", - "symbol": "idna", - "name": "Idena GBP" - }, - { - "id": "IDNA-INR", - "symbol": "idna", - "name": "Idena INR" - }, - { - "id": "IDNA-JPY", - "symbol": "idna", - "name": "Idena JPY" - }, - { - "id": "IDNA-KRW", - "symbol": "idna", - "name": "Idena KRW" - }, - { - "id": "IDNA-RUB", - "symbol": "idna", - "name": "Idena RUB" - }, - { - "id": "IDNA-USD", - "symbol": "idna", - "name": "Idena USD" - }, - { - "id": "ILC-CAD", - "symbol": "ilc", - "name": "ILCoin CAD" - }, - { - "id": "ILC-CNY", - "symbol": "ilc", - "name": "ILCoin CNY" - }, - { - "id": "ILC-ETH", - "symbol": "ilc", - "name": "ILCoin ETH" - }, - { - "id": "ILC-EUR", - "symbol": "ilc", - "name": "ILCoin EUR" - }, - { - "id": "ILC-GBP", - "symbol": "ilc", - "name": "ILCoin GBP" - }, - { - "id": "ILC-INR", - "symbol": "ilc", - "name": "ILCoin INR" - }, - { - "id": "ILC-JPY", - "symbol": "ilc", - "name": "ILCoin JPY" - }, - { - "id": "ILC-KRW", - "symbol": "ilc", - "name": "ILCoin KRW" - }, - { - "id": "ILC-RUB", - "symbol": "ilc", - "name": "ILCoin RUB" - }, - { - "id": "ILC-USD", - "symbol": "ilc", - "name": "ILCoin USD" - }, - { - "id": "INSTAR-CAD", - "symbol": "instar", - "name": "InsightsNetwork CAD" - }, - { - "id": "INSTAR-CNY", - "symbol": "instar", - "name": "InsightsNetwork CNY" - }, - { - "id": "INSTAR-ETH", - "symbol": "instar", - "name": "InsightsNetwork ETH" - }, - { - "id": "INSTAR-EUR", - "symbol": "instar", - "name": "InsightsNetwork EUR" - }, - { - "id": "INSTAR-GBP", - "symbol": "instar", - "name": "InsightsNetwork GBP" - }, - { - "id": "INSTAR-INR", - "symbol": "instar", - "name": "InsightsNetwork INR" - }, - { - "id": "INSTAR-JPY", - "symbol": "instar", - "name": "InsightsNetwork JPY" - }, - { - "id": "INSTAR-KRW", - "symbol": "instar", - "name": "InsightsNetwork KRW" - }, - { - "id": "INSTAR-RUB", - "symbol": "instar", - "name": "InsightsNetwork RUB" - }, - { - "id": "INSTAR-USD", - "symbol": "instar", - "name": "InsightsNetwork USD" - }, - { - "id": "INT-CAD", - "symbol": "int", - "name": "InternetNodeToken CAD" - }, - { - "id": "INT-CNY", - "symbol": "int", - "name": "InternetNodeToken CNY" - }, - { - "id": "INT-ETH", - "symbol": "int", - "name": "InternetNodeToken ETH" - }, - { - "id": "INT-EUR", - "symbol": "int", - "name": "InternetNodeToken EUR" - }, - { - "id": "INT-GBP", - "symbol": "int", - "name": "InternetNodeToken GBP" - }, - { - "id": "INT-INR", - "symbol": "int", - "name": "InternetNodeToken INR" - }, - { - "id": "INT-JPY", - "symbol": "int", - "name": "InternetNodeToken JPY" - }, - { - "id": "INT-KRW", - "symbol": "int", - "name": "InternetNodeToken KRW" - }, - { - "id": "INT-RUB", - "symbol": "int", - "name": "InternetNodeToken RUB" - }, - { - "id": "INT-USD", - "symbol": "int", - "name": "InternetNodeToken USD" - }, - { - "id": "IOC-BTC", - "symbol": "ioc", - "name": "IOCoin BTC" - }, - { - "id": "IOC-CAD", - "symbol": "ioc", - "name": "IOCoin CAD" - }, - { - "id": "IOC-CNY", - "symbol": "ioc", - "name": "IOCoin CNY" - }, - { - "id": "IOC-ETH", - "symbol": "ioc", - "name": "IOCoin ETH" - }, - { - "id": "IOC-EUR", - "symbol": "ioc", - "name": "IOCoin EUR" - }, - { - "id": "IOC-GBP", - "symbol": "ioc", - "name": "IOCoin GBP" - }, - { - "id": "IOC-INR", - "symbol": "ioc", - "name": "IOCoin INR" - }, - { - "id": "IOC-KRW", - "symbol": "ioc", - "name": "IOCoin KRW" - }, - { - "id": "IOC-RUB", - "symbol": "ioc", - "name": "IOCoin RUB" - }, - { - "id": "IOC-USD", - "symbol": "ioc", - "name": "IOCoin USD" - }, - { - "id": "IOST-CAD", - "symbol": "iost", - "name": "IOST CAD" - }, - { - "id": "IOST-CNY", - "symbol": "iost", - "name": "IOST CNY" - }, - { - "id": "IOST-ETH", - "symbol": "iost", - "name": "IOST ETH" - }, - { - "id": "IOST-EUR", - "symbol": "iost", - "name": "IOST EUR" - }, - { - "id": "IOST-GBP", - "symbol": "iost", - "name": "IOST GBP" - }, - { - "id": "IOST-INR", - "symbol": "iost", - "name": "IOST INR" - }, - { - "id": "IOST-JPY", - "symbol": "iost", - "name": "IOST JPY" - }, - { - "id": "IOST-KRW", - "symbol": "iost", - "name": "IOST KRW" - }, - { - "id": "IOST-RUB", - "symbol": "iost", - "name": "IOST RUB" - }, - { - "id": "IOST-USD", - "symbol": "iost", - "name": "IOST USD" - }, - { - "id": "IOTX-CAD", - "symbol": "iotx", - "name": "IoTeX CAD" - }, - { - "id": "IOTX-CNY", - "symbol": "iotx", - "name": "IoTeX CNY" - }, - { - "id": "IOTX-ETH", - "symbol": "iotx", - "name": "IoTeX ETH" - }, - { - "id": "IOTX-EUR", - "symbol": "iotx", - "name": "IoTeX EUR" - }, - { - "id": "IOTX-GBP", - "symbol": "iotx", - "name": "IoTeX GBP" - }, - { - "id": "IOTX-INR", - "symbol": "iotx", - "name": "IoTeX INR" - }, - { - "id": "IOTX-JPY", - "symbol": "iotx", - "name": "IoTeX JPY" - }, - { - "id": "IOTX-KRW", - "symbol": "iotx", - "name": "IoTeX KRW" - }, - { - "id": "IOTX-RUB", - "symbol": "iotx", - "name": "IoTeX RUB" - }, - { - "id": "IOTX-USD", - "symbol": "iotx", - "name": "IoTeX USD" - }, - { - "id": "IRIS-CAD", - "symbol": "iris", - "name": "IRISnet CAD" - }, - { - "id": "IRIS-CNY", - "symbol": "iris", - "name": "IRISnet CNY" - }, - { - "id": "IRIS-ETH", - "symbol": "iris", - "name": "IRISnet ETH" - }, - { - "id": "IRIS-EUR", - "symbol": "iris", - "name": "IRISnet EUR" - }, - { - "id": "IRIS-GBP", - "symbol": "iris", - "name": "IRISnet GBP" - }, - { - "id": "IRIS-INR", - "symbol": "iris", - "name": "IRISnet INR" - }, - { - "id": "IRIS-JPY", - "symbol": "iris", - "name": "IRISnet JPY" - }, - { - "id": "IRIS-KRW", - "symbol": "iris", - "name": "IRISnet KRW" - }, - { - "id": "IRIS-RUB", - "symbol": "iris", - "name": "IRISnet RUB" - }, - { - "id": "IRIS-USD", - "symbol": "iris", - "name": "IRISnet USD" - }, - { - "id": "JDC-CAD", - "symbol": "jdc", - "name": "JDCoin CAD" - }, - { - "id": "JDC-CNY", - "symbol": "jdc", - "name": "JDCoin CNY" - }, - { - "id": "JDC-ETH", - "symbol": "jdc", - "name": "JDCoin ETH" - }, - { - "id": "JDC-EUR", - "symbol": "jdc", - "name": "JDCoin EUR" - }, - { - "id": "JDC-GBP", - "symbol": "jdc", - "name": "JDCoin GBP" - }, - { - "id": "JDC-INR", - "symbol": "jdc", - "name": "JDCoin INR" - }, - { - "id": "JDC-JPY", - "symbol": "jdc", - "name": "JDCoin JPY" - }, - { - "id": "JDC-KRW", - "symbol": "jdc", - "name": "JDCoin KRW" - }, - { - "id": "JDC-RUB", - "symbol": "jdc", - "name": "JDCoin RUB" - }, - { - "id": "JDC-USD", - "symbol": "jdc", - "name": "JDCoin USD" - }, - { - "id": "JUL-EUR", - "symbol": "jul", - "name": "Joule EUR" - }, - { - "id": "JUL-GBP", - "symbol": "jul", - "name": "Joule GBP" - }, - { - "id": "JUL-INR", - "symbol": "jul", - "name": "Joule INR" - }, - { - "id": "JUL-JPY", - "symbol": "jul", - "name": "Joule JPY" - }, - { - "id": "JUL-KRW", - "symbol": "jul", - "name": "Joule KRW" - }, - { - "id": "JUL-RUB", - "symbol": "jul", - "name": "Joule RUB" - }, - { - "id": "KAVA-ETH", - "symbol": "kava", - "name": "Kava ETH" - }, - { - "id": "KAVA-EUR", - "symbol": "kava", - "name": "Kava EUR" - }, - { - "id": "KAVA-GBP", - "symbol": "kava", - "name": "Kava GBP" - }, - { - "id": "KAVA-INR", - "symbol": "kava", - "name": "Kava INR" - }, - { - "id": "KAVA-JPY", - "symbol": "kava", - "name": "Kava JPY" - }, - { - "id": "KAVA-KRW", - "symbol": "kava", - "name": "Kava KRW" - }, - { - "id": "KAVA-RUB", - "symbol": "kava", - "name": "Kava RUB" - }, - { - "id": "KAVA-USD", - "symbol": "kava", - "name": "Kava USD" - }, - { - "id": "KDA-CAD", - "symbol": "kda", - "name": "Kadena CAD" - }, - { - "id": "KDA-CNY", - "symbol": "kda", - "name": "Kadena CNY" - }, - { - "id": "KDA-ETH", - "symbol": "kda", - "name": "Kadena ETH" - }, - { - "id": "KDA-EUR", - "symbol": "kda", - "name": "Kadena EUR" - }, - { - "id": "KDA-GBP", - "symbol": "kda", - "name": "Kadena GBP" - }, - { - "id": "KDA-INR", - "symbol": "kda", - "name": "Kadena INR" - }, - { - "id": "KDA-JPY", - "symbol": "kda", - "name": "Kadena JPY" - }, - { - "id": "KDA-KRW", - "symbol": "kda", - "name": "Kadena KRW" - }, - { - "id": "KDA-RUB", - "symbol": "kda", - "name": "Kadena RUB" - }, - { - "id": "KDA-USD", - "symbol": "kda", - "name": "Kadena USD" - }, - { - "id": "KMD-CAD", - "symbol": "kmd", - "name": "Komodo CAD" - }, - { - "id": "KMD-CNY", - "symbol": "kmd", - "name": "Komodo CNY" - }, - { - "id": "KMD-ETH", - "symbol": "kmd", - "name": "Komodo ETH" - }, - { - "id": "KMD-EUR", - "symbol": "kmd", - "name": "Komodo EUR" - }, - { - "id": "KMD-GBP", - "symbol": "kmd", - "name": "Komodo GBP" - }, - { - "id": "KMD-INR", - "symbol": "kmd", - "name": "Komodo INR" - }, - { - "id": "KMD-JPY", - "symbol": "kmd", - "name": "Komodo JPY" - }, - { - "id": "KMD-KRW", - "symbol": "kmd", - "name": "Komodo KRW" - }, - { - "id": "KMD-RUB", - "symbol": "kmd", - "name": "Komodo RUB" - }, - { - "id": "KMD-USD", - "symbol": "kmd", - "name": "Komodo USD" - }, - { - "id": "KNC-AUD", - "symbol": "knc", - "name": "KyberNetwork AUD" - }, - { - "id": "KNC-BTC", - "symbol": "knc", - "name": "KyberNetwork BTC" - }, - { - "id": "KNC-CAD", - "symbol": "knc", - "name": "KyberNetwork CAD" - }, - { - "id": "KNC-CNY", - "symbol": "knc", - "name": "KyberNetwork CNY" - }, - { - "id": "KNC-ETH", - "symbol": "knc", - "name": "KyberNetwork ETH" - }, - { - "id": "KNC-EUR", - "symbol": "knc", - "name": "KyberNetwork EUR" - }, - { - "id": "KNC-GBP", - "symbol": "knc", - "name": "KyberNetwork GBP" - }, - { - "id": "KNC-INR", - "symbol": "knc", - "name": "KyberNetwork INR" - }, - { - "id": "KNC-KRW", - "symbol": "knc", - "name": "KyberNetwork KRW" - }, - { - "id": "KNC-USD", - "symbol": "knc", - "name": "KyberNetwork USD" - }, - { - "id": "KRT-CAD", - "symbol": "krt", - "name": "TerraKRW CAD" - }, - { - "id": "KRT-CNY", - "symbol": "krt", - "name": "TerraKRW CNY" - }, - { - "id": "KRT-ETH", - "symbol": "krt", - "name": "TerraKRW ETH" - }, - { - "id": "KRT-EUR", - "symbol": "krt", - "name": "TerraKRW EUR" - }, - { - "id": "KRT-GBP", - "symbol": "krt", - "name": "TerraKRW GBP" - }, - { - "id": "KRT-INR", - "symbol": "krt", - "name": "TerraKRW INR" - }, - { - "id": "KRT-JPY", - "symbol": "krt", - "name": "TerraKRW JPY" - }, - { - "id": "KRT-KRW", - "symbol": "krt", - "name": "TerraKRW KRW" - }, - { - "id": "KRT-RUB", - "symbol": "krt", - "name": "TerraKRW RUB" - }, - { - "id": "KRT-USD", - "symbol": "krt", - "name": "TerraKRW USD" - }, - { - "id": "KSM-CAD", - "symbol": "ksm", - "name": "Kusama CAD" - }, - { - "id": "KSM-CNY", - "symbol": "ksm", - "name": "Kusama CNY" - }, - { - "id": "KSM-ETH", - "symbol": "ksm", - "name": "Kusama ETH" - }, - { - "id": "KSM-EUR", - "symbol": "ksm", - "name": "Kusama EUR" - }, - { - "id": "KSM-GBP", - "symbol": "ksm", - "name": "Kusama GBP" - }, - { - "id": "KSM-INR", - "symbol": "ksm", - "name": "Kusama INR" - }, - { - "id": "KSM-JPY", - "symbol": "ksm", - "name": "Kusama JPY" - }, - { - "id": "KSM-KRW", - "symbol": "ksm", - "name": "Kusama KRW" - }, - { - "id": "KSM-RUB", - "symbol": "ksm", - "name": "Kusama RUB" - }, - { - "id": "KSM-USD", - "symbol": "ksm", - "name": "Kusama USD" - }, - { - "id": "LBC-BTC", - "symbol": "lbc", - "name": "LBRYCredits BTC" - }, - { - "id": "LBC-CAD", - "symbol": "lbc", - "name": "LBRYCredits CAD" - }, - { - "id": "LBC-CNY", - "symbol": "lbc", - "name": "LBRYCredits CNY" - }, - { - "id": "LBC-ETH", - "symbol": "lbc", - "name": "LBRYCredits ETH" - }, - { - "id": "LBC-EUR", - "symbol": "lbc", - "name": "LBRYCredits EUR" - }, - { - "id": "LBC-GBP", - "symbol": "lbc", - "name": "LBRYCredits GBP" - }, - { - "id": "LBC-INR", - "symbol": "lbc", - "name": "LBRYCredits INR" - }, - { - "id": "LBC-JPY", - "symbol": "lbc", - "name": "LBRYCredits JPY" - }, - { - "id": "LBC-KRW", - "symbol": "lbc", - "name": "LBRYCredits KRW" - }, - { - "id": "LBC-RUB", - "symbol": "lbc", - "name": "LBRYCredits RUB" - }, - { - "id": "LBC-USD", - "symbol": "lbc", - "name": "LBRYCredits USD" - }, - { - "id": "LCC-CAD", - "symbol": "lcc", - "name": "LitecoinCash CAD" - }, - { - "id": "LCC-CNY", - "symbol": "lcc", - "name": "LitecoinCash CNY" - }, - { - "id": "LCC-ETH", - "symbol": "lcc", - "name": "LitecoinCash ETH" - }, - { - "id": "LCC-EUR", - "symbol": "lcc", - "name": "LitecoinCash EUR" - }, - { - "id": "LCC-GBP", - "symbol": "lcc", - "name": "LitecoinCash GBP" - }, - { - "id": "LCC-INR", - "symbol": "lcc", - "name": "LitecoinCash INR" - }, - { - "id": "LCC-JPY", - "symbol": "lcc", - "name": "LitecoinCash JPY" - }, - { - "id": "LCC-KRW", - "symbol": "lcc", - "name": "LitecoinCash KRW" - }, - { - "id": "LCC-RUB", - "symbol": "lcc", - "name": "LitecoinCash RUB" - }, - { - "id": "LCC-USD", - "symbol": "lcc", - "name": "LitecoinCash USD" - }, - { - "id": "LINK-AUD", - "symbol": "link", - "name": "Chainlink AUD" - }, - { - "id": "LINK-BTC", - "symbol": "link", - "name": "Chainlink BTC" - }, - { - "id": "LINK-CAD", - "symbol": "link", - "name": "Chainlink CAD" - }, - { - "id": "LINK-CNY", - "symbol": "link", - "name": "Chainlink CNY" - }, - { - "id": "LINK-ETH", - "symbol": "link", - "name": "Chainlink ETH" - }, - { - "id": "LINK-EUR", - "symbol": "link", - "name": "Chainlink EUR" - }, - { - "id": "LINK-GBP", - "symbol": "link", - "name": "Chainlink GBP" - }, - { - "id": "LINK-INR", - "symbol": "link", - "name": "Chainlink INR" - }, - { - "id": "LINK-JPY", - "symbol": "link", - "name": "Chainlink JPY" - }, - { - "id": "LINK-KRW", - "symbol": "link", - "name": "Chainlink KRW" - }, - { - "id": "LINK-RUB", - "symbol": "link", - "name": "Chainlink RUB" - }, - { - "id": "LINK-USD", - "symbol": "link", - "name": "Chainlink USD" - }, - { - "id": "LKK-AUD", - "symbol": "lkk", - "name": "Lykke AUD" - }, - { - "id": "LKK-CNY", - "symbol": "lkk", - "name": "Lykke CNY" - }, - { - "id": "LKK-ETH", - "symbol": "lkk", - "name": "Lykke ETH" - }, - { - "id": "LKK-EUR", - "symbol": "lkk", - "name": "Lykke EUR" - }, - { - "id": "LKK-GBP", - "symbol": "lkk", - "name": "Lykke GBP" - }, - { - "id": "LKK-INR", - "symbol": "lkk", - "name": "Lykke INR" - }, - { - "id": "LKK-JPY", - "symbol": "lkk", - "name": "Lykke JPY" - }, - { - "id": "LKK-KRW", - "symbol": "lkk", - "name": "Lykke KRW" - }, - { - "id": "LKK-RUB", - "symbol": "lkk", - "name": "Lykke RUB" - }, - { - "id": "LKK-USD", - "symbol": "lkk", - "name": "Lykke USD" - }, - { - "id": "LOKI-CAD", - "symbol": "loki", - "name": "Loki CAD" - }, - { - "id": "LOKI-CNY", - "symbol": "loki", - "name": "Loki CNY" - }, - { - "id": "LOKI-ETH", - "symbol": "loki", - "name": "Loki ETH" - }, - { - "id": "LOKI-EUR", - "symbol": "loki", - "name": "Loki EUR" - }, - { - "id": "LOKI-GBP", - "symbol": "loki", - "name": "Loki GBP" - }, - { - "id": "LOKI-INR", - "symbol": "loki", - "name": "Loki INR" - }, - { - "id": "LOKI-JPY", - "symbol": "loki", - "name": "Loki JPY" - }, - { - "id": "LOKI-KRW", - "symbol": "loki", - "name": "Loki KRW" - }, - { - "id": "LOKI-RUB", - "symbol": "loki", - "name": "Loki RUB" - }, - { - "id": "LOKI-USD", - "symbol": "loki", - "name": "Loki USD" - }, - { - "id": "LRC-BTC", - "symbol": "lrc", - "name": "Loopring BTC" - }, - { - "id": "LRC-CAD", - "symbol": "lrc", - "name": "Loopring CAD" - }, - { - "id": "LRC-CNY", - "symbol": "lrc", - "name": "Loopring CNY" - }, - { - "id": "LRC-ETH", - "symbol": "lrc", - "name": "Loopring ETH" - }, - { - "id": "LRC-EUR", - "symbol": "lrc", - "name": "Loopring EUR" - }, - { - "id": "LRC-GBP", - "symbol": "lrc", - "name": "Loopring GBP" - }, - { - "id": "LRC-INR", - "symbol": "lrc", - "name": "Loopring INR" - }, - { - "id": "LRC-JPY", - "symbol": "lrc", - "name": "Loopring JPY" - }, - { - "id": "LRC-RUB", - "symbol": "lrc", - "name": "Loopring RUB" - }, - { - "id": "LRC-USD", - "symbol": "lrc", - "name": "Loopring USD" - }, - { - "id": "LRG-CAD", - "symbol": "lrg", - "name": "LargoCoin CAD" - }, - { - "id": "LRG-CNY", - "symbol": "lrg", - "name": "LargoCoin CNY" - }, - { - "id": "LRG-ETH", - "symbol": "lrg", - "name": "LargoCoin ETH" - }, - { - "id": "LRG-EUR", - "symbol": "lrg", - "name": "LargoCoin EUR" - }, - { - "id": "LRG-GBP", - "symbol": "lrg", - "name": "LargoCoin GBP" - }, - { - "id": "LRG-INR", - "symbol": "lrg", - "name": "LargoCoin INR" - }, - { - "id": "LRG-JPY", - "symbol": "lrg", - "name": "LargoCoin JPY" - }, - { - "id": "LRG-KRW", - "symbol": "lrg", - "name": "LargoCoin KRW" - }, - { - "id": "LRG-RUB", - "symbol": "lrg", - "name": "LargoCoin RUB" - }, - { - "id": "LRG-USD", - "symbol": "lrg", - "name": "LargoCoin USD" - }, - { - "id": "LSK-AUD", - "symbol": "lsk", - "name": "Lisk AUD" - }, - { - "id": "LSK-BTC", - "symbol": "lsk", - "name": "Lisk BTC" - }, - { - "id": "LSK-CAD", - "symbol": "lsk", - "name": "Lisk CAD" - }, - { - "id": "LSK-EUR", - "symbol": "lsk", - "name": "Lisk EUR" - }, - { - "id": "LSK-GBP", - "symbol": "lsk", - "name": "Lisk GBP" - }, - { - "id": "LSK-INR", - "symbol": "lsk", - "name": "Lisk INR" - }, - { - "id": "LSK-JPY", - "symbol": "lsk", - "name": "Lisk JPY" - }, - { - "id": "LSK-KRW", - "symbol": "lsk", - "name": "Lisk KRW" - }, - { - "id": "LSK-USD", - "symbol": "lsk", - "name": "Lisk USD" - }, - { - "id": "LTC-AUD", - "symbol": "ltc", - "name": "Litecoin AUD" - }, - { - "id": "LTC-BTC", - "symbol": "ltc", - "name": "Litecoin BTC" - }, - { - "id": "LTC-CAD", - "symbol": "ltc", - "name": "Litecoin CAD" - }, - { - "id": "LTC-ETH", - "symbol": "ltc", - "name": "Litecoin ETH" - }, - { - "id": "LTC-EUR", - "symbol": "ltc", - "name": "Litecoin EUR" - }, - { - "id": "LTC-GBP", - "symbol": "ltc", - "name": "Litecoin GBP" - }, - { - "id": "LTC-INR", - "symbol": "ltc", - "name": "Litecoin INR" - }, - { - "id": "LTC-JPY", - "symbol": "ltc", - "name": "Litecoin JPY" - }, - { - "id": "LTC-USD", - "symbol": "ltc", - "name": "Litecoin USD" - }, - { - "id": "LUNA1-CAD", - "symbol": "luna", - "name": "Terra CAD" - }, - { - "id": "LUNA1-CNY", - "symbol": "luna", - "name": "Terra CNY" - }, - { - "id": "LUNA1-ETH", - "symbol": "luna", - "name": "Terra ETH" - }, - { - "id": "LUNA1-EUR", - "symbol": "luna", - "name": "Terra EUR" - }, - { - "id": "LUNA1-GBP", - "symbol": "luna", - "name": "Terra GBP" - }, - { - "id": "LUNA1-INR", - "symbol": "luna", - "name": "Terra INR" - }, - { - "id": "LUNA1-JPY", - "symbol": "luna", - "name": "Terra JPY" - }, - { - "id": "LUNA1-KRW", - "symbol": "luna", - "name": "Terra KRW" - }, - { - "id": "LUNA1-RUB", - "symbol": "luna", - "name": "Terra RUB" - }, - { - "id": "LUNA1-USD", - "symbol": "luna", - "name": "Terra USD" - }, - { - "id": "LYNX-JPY", - "symbol": "lynx", - "name": "Lynx JPY" - }, - { - "id": "LYNX-KRW", - "symbol": "lynx", - "name": "Lynx KRW" - }, - { - "id": "LYNX-RUB", - "symbol": "lynx", - "name": "Lynx RUB" - }, - { - "id": "MAID-BTC", - "symbol": "maid", - "name": "MaidSafeCoin BTC" - }, - { - "id": "MAID-CAD", - "symbol": "maid", - "name": "MaidSafeCoin CAD" - }, - { - "id": "MAID-CNY", - "symbol": "maid", - "name": "MaidSafeCoin CNY" - }, - { - "id": "MAID-ETH", - "symbol": "maid", - "name": "MaidSafeCoin ETH" - }, - { - "id": "MAID-EUR", - "symbol": "maid", - "name": "MaidSafeCoin EUR" - }, - { - "id": "MAID-GBP", - "symbol": "maid", - "name": "MaidSafeCoin GBP" - }, - { - "id": "MAID-INR", - "symbol": "maid", - "name": "MaidSafeCoin INR" - }, - { - "id": "MAID-KRW", - "symbol": "maid", - "name": "MaidSafeCoin KRW" - }, - { - "id": "MAID-RUB", - "symbol": "maid", - "name": "MaidSafeCoin RUB" - }, - { - "id": "MAID-USD", - "symbol": "maid", - "name": "MaidSafeCoin USD" - }, - { - "id": "MAN-CAD", - "symbol": "man", - "name": "MatrixAINetwork CAD" - }, - { - "id": "MAN-CNY", - "symbol": "man", - "name": "MatrixAINetwork CNY" - }, - { - "id": "MAN-ETH", - "symbol": "man", - "name": "MatrixAINetwork ETH" - }, - { - "id": "MAN-EUR", - "symbol": "man", - "name": "MatrixAINetwork EUR" - }, - { - "id": "MAN-GBP", - "symbol": "man", - "name": "MatrixAINetwork GBP" - }, - { - "id": "MAN-INR", - "symbol": "man", - "name": "MatrixAINetwork INR" - }, - { - "id": "MAN-JPY", - "symbol": "man", - "name": "MatrixAINetwork JPY" - }, - { - "id": "MAN-KRW", - "symbol": "man", - "name": "MatrixAINetwork KRW" - }, - { - "id": "MAN-RUB", - "symbol": "man", - "name": "MatrixAINetwork RUB" - }, - { - "id": "MAN-USD", - "symbol": "man", - "name": "MatrixAINetwork USD" - }, - { - "id": "MANA-AUD", - "symbol": "mana", - "name": "Decentraland AUD" - }, - { - "id": "MANA-BTC", - "symbol": "mana", - "name": "Decentraland BTC" - }, - { - "id": "MANA-CAD", - "symbol": "mana", - "name": "Decentraland CAD" - }, - { - "id": "MANA-CNY", - "symbol": "mana", - "name": "Decentraland CNY" - }, - { - "id": "MANA-ETH", - "symbol": "mana", - "name": "Decentraland ETH" - }, - { - "id": "MANA-EUR", - "symbol": "mana", - "name": "Decentraland EUR" - }, - { - "id": "MANA-GBP", - "symbol": "mana", - "name": "Decentraland GBP" - }, - { - "id": "MANA-INR", - "symbol": "mana", - "name": "Decentraland INR" - }, - { - "id": "MANA-JPY", - "symbol": "mana", - "name": "Decentraland JPY" - }, - { - "id": "MANA-KRW", - "symbol": "mana", - "name": "Decentraland KRW" - }, - { - "id": "MANA-RUB", - "symbol": "mana", - "name": "Decentraland RUB" - }, - { - "id": "MANA-USD", - "symbol": "mana", - "name": "Decentraland USD" - }, - { - "id": "MARO-ETH", - "symbol": "maro", - "name": "Maro ETH" - }, - { - "id": "MARO-EUR", - "symbol": "maro", - "name": "Maro EUR" - }, - { - "id": "MARO-GBP", - "symbol": "maro", - "name": "Maro GBP" - }, - { - "id": "MARO-INR", - "symbol": "maro", - "name": "Maro INR" - }, - { - "id": "MARO-JPY", - "symbol": "maro", - "name": "Maro JPY" - }, - { - "id": "MARO-KRW", - "symbol": "maro", - "name": "Maro KRW" - }, - { - "id": "MARO-RUB", - "symbol": "maro", - "name": "Maro RUB" - }, - { - "id": "MARO-USD", - "symbol": "maro", - "name": "Maro USD" - }, - { - "id": "MASS-CAD", - "symbol": "mass", - "name": "Massnet CAD" - }, - { - "id": "MASS-CNY", - "symbol": "mass", - "name": "Massnet CNY" - }, - { - "id": "MASS-ETH", - "symbol": "mass", - "name": "Massnet ETH" - }, - { - "id": "MASS-EUR", - "symbol": "mass", - "name": "Massnet EUR" - }, - { - "id": "MASS-GBP", - "symbol": "mass", - "name": "Massnet GBP" - }, - { - "id": "MASS-INR", - "symbol": "mass", - "name": "Massnet INR" - }, - { - "id": "MASS-JPY", - "symbol": "mass", - "name": "Massnet JPY" - }, - { - "id": "MASS-KRW", - "symbol": "mass", - "name": "Massnet KRW" - }, - { - "id": "MASS-RUB", - "symbol": "mass", - "name": "Massnet RUB" - }, - { - "id": "MASS-USD", - "symbol": "mass", - "name": "Massnet USD" - }, - { - "id": "MATIC-AUD", - "symbol": "matic", - "name": "MaticNetwork AUD" - }, - { - "id": "MATIC-BTC", - "symbol": "matic", - "name": "MaticNetwork BTC" - }, - { - "id": "MATIC-CAD", - "symbol": "matic", - "name": "MaticNetwork CAD" - }, - { - "id": "MATIC-CNY", - "symbol": "matic", - "name": "MaticNetwork CNY" - }, - { - "id": "MATIC-ETH", - "symbol": "matic", - "name": "MaticNetwork ETH" - }, - { - "id": "MATIC-EUR", - "symbol": "matic", - "name": "MaticNetwork EUR" - }, - { - "id": "MATIC-GBP", - "symbol": "matic", - "name": "MaticNetwork GBP" - }, - { - "id": "MATIC-INR", - "symbol": "matic", - "name": "MaticNetwork INR" - }, - { - "id": "MATIC-JPY", - "symbol": "matic", - "name": "MaticNetwork JPY" - }, - { - "id": "MATIC-KRW", - "symbol": "matic", - "name": "MaticNetwork KRW" - }, - { - "id": "MATIC-RUB", - "symbol": "matic", - "name": "MaticNetwork RUB" - }, - { - "id": "MATIC-USD", - "symbol": "matic", - "name": "MaticNetwork USD" - }, - { - "id": "MBC-AUD", - "symbol": "mbc", - "name": "MicroBitcoin AUD" - }, - { - "id": "MBC-BTC", - "symbol": "mbc", - "name": "MicroBitcoin BTC" - }, - { - "id": "MBC-CNY", - "symbol": "mbc", - "name": "MicroBitcoin CNY" - }, - { - "id": "MBC-ETH", - "symbol": "mbc", - "name": "MicroBitcoin ETH" - }, - { - "id": "MBC-EUR", - "symbol": "mbc", - "name": "MicroBitcoin EUR" - }, - { - "id": "MBC-GBP", - "symbol": "mbc", - "name": "MicroBitcoin GBP" - }, - { - "id": "MBC-INR", - "symbol": "mbc", - "name": "MicroBitcoin INR" - }, - { - "id": "MBC-JPY", - "symbol": "mbc", - "name": "MicroBitcoin JPY" - }, - { - "id": "MBC-KRW", - "symbol": "mbc", - "name": "MicroBitcoin KRW" - }, - { - "id": "MBC-RUB", - "symbol": "mbc", - "name": "MicroBitcoin RUB" - }, - { - "id": "MBC-USD", - "symbol": "mbc", - "name": "MicroBitcoin USD" - }, - { - "id": "MED-CAD", - "symbol": "med", - "name": "MediBlocQRC20 CAD" - }, - { - "id": "MED-CNY", - "symbol": "med", - "name": "MediBlocQRC20 CNY" - }, - { - "id": "MED-ETH", - "symbol": "med", - "name": "MediBlocQRC20 ETH" - }, - { - "id": "MED-EUR", - "symbol": "med", - "name": "MediBlocQRC20 EUR" - }, - { - "id": "MED-GBP", - "symbol": "med", - "name": "MediBlocQRC20 GBP" - }, - { - "id": "MED-INR", - "symbol": "med", - "name": "MediBlocQRC20 INR" - }, - { - "id": "MED-JPY", - "symbol": "med", - "name": "MediBlocQRC20 JPY" - }, - { - "id": "MED-KRW", - "symbol": "med", - "name": "MediBlocQRC20 KRW" - }, - { - "id": "MED-RUB", - "symbol": "med", - "name": "MediBlocQRC20 RUB" - }, - { - "id": "MED-USD", - "symbol": "med", - "name": "MediBlocQRC20 USD" - }, - { - "id": "META-CAD", - "symbol": "meta", - "name": "Metadium CAD" - }, - { - "id": "META-CNY", - "symbol": "meta", - "name": "Metadium CNY" - }, - { - "id": "META-ETH", - "symbol": "meta", - "name": "Metadium ETH" - }, - { - "id": "META-EUR", - "symbol": "meta", - "name": "Metadium EUR" - }, - { - "id": "META-GBP", - "symbol": "meta", - "name": "Metadium GBP" - }, - { - "id": "META-INR", - "symbol": "meta", - "name": "Metadium INR" - }, - { - "id": "META-JPY", - "symbol": "meta", - "name": "Metadium JPY" - }, - { - "id": "META-KRW", - "symbol": "meta", - "name": "Metadium KRW" - }, - { - "id": "META-RUB", - "symbol": "meta", - "name": "Metadium RUB" - }, - { - "id": "META-USD", - "symbol": "meta", - "name": "Metadium USD" - }, - { - "id": "MGO-CAD", - "symbol": "mgo", - "name": "MobileGo CAD" - }, - { - "id": "MGO-CNY", - "symbol": "mgo", - "name": "MobileGo CNY" - }, - { - "id": "MGO-ETH", - "symbol": "mgo", - "name": "MobileGo ETH" - }, - { - "id": "MGO-EUR", - "symbol": "mgo", - "name": "MobileGo EUR" - }, - { - "id": "MGO-GBP", - "symbol": "mgo", - "name": "MobileGo GBP" - }, - { - "id": "MGO-INR", - "symbol": "mgo", - "name": "MobileGo INR" - }, - { - "id": "MGO-JPY", - "symbol": "mgo", - "name": "MobileGo JPY" - }, - { - "id": "MGO-KRW", - "symbol": "mgo", - "name": "MobileGo KRW" - }, - { - "id": "MGO-RUB", - "symbol": "mgo", - "name": "MobileGo RUB" - }, - { - "id": "MGO-USD", - "symbol": "mgo", - "name": "MobileGo USD" - }, - { - "id": "MHC-CAD", - "symbol": "mhc", - "name": "MetaHash CAD" - }, - { - "id": "MHC-CNY", - "symbol": "mhc", - "name": "MetaHash CNY" - }, - { - "id": "MHC-ETH", - "symbol": "mhc", - "name": "MetaHash ETH" - }, - { - "id": "MHC-EUR", - "symbol": "mhc", - "name": "MetaHash EUR" - }, - { - "id": "MHC-GBP", - "symbol": "mhc", - "name": "MetaHash GBP" - }, - { - "id": "MHC-INR", - "symbol": "mhc", - "name": "MetaHash INR" - }, - { - "id": "MHC-JPY", - "symbol": "mhc", - "name": "MetaHash JPY" - }, - { - "id": "MHC-KRW", - "symbol": "mhc", - "name": "MetaHash KRW" - }, - { - "id": "MHC-RUB", - "symbol": "mhc", - "name": "MetaHash RUB" - }, - { - "id": "MHC-USD", - "symbol": "mhc", - "name": "MetaHash USD" - }, - { - "id": "MIDAS-INR", - "symbol": "midas", - "name": "Midas INR" - }, - { - "id": "MIDAS-JPY", - "symbol": "midas", - "name": "Midas JPY" - }, - { - "id": "MIDAS-KRW", - "symbol": "midas", - "name": "Midas KRW" - }, - { - "id": "MIDAS-RUB", - "symbol": "midas", - "name": "Midas RUB" - }, - { - "id": "MIDAS-USD", - "symbol": "midas", - "name": "Midas USD" - }, - { - "id": "MINT-AUD", - "symbol": "mint", - "name": "MintCoin AUD" - }, - { - "id": "MINT-CAD", - "symbol": "mint", - "name": "MintCoin CAD" - }, - { - "id": "MINT-CNY", - "symbol": "mint", - "name": "MintCoin CNY" - }, - { - "id": "MINT-ETH", - "symbol": "mint", - "name": "MintCoin ETH" - }, - { - "id": "MINT-EUR", - "symbol": "mint", - "name": "MintCoin EUR" - }, - { - "id": "MINT-GBP", - "symbol": "mint", - "name": "MintCoin GBP" - }, - { - "id": "MINT-INR", - "symbol": "mint", - "name": "MintCoin INR" - }, - { - "id": "MINT-JPY", - "symbol": "mint", - "name": "MintCoin JPY" - }, - { - "id": "MINT-KRW", - "symbol": "mint", - "name": "MintCoin KRW" - }, - { - "id": "MINT-RUB", - "symbol": "mint", - "name": "MintCoin RUB" - }, - { - "id": "MINT-USD", - "symbol": "mint", - "name": "MintCoin USD" - }, - { - "id": "MIOTA-CAD", - "symbol": "miota", - "name": "IOTA CAD" - }, - { - "id": "MIOTA-CNY", - "symbol": "miota", - "name": "IOTA CNY" - }, - { - "id": "MIOTA-ETH", - "symbol": "miota", - "name": "IOTA ETH" - }, - { - "id": "MIOTA-EUR", - "symbol": "miota", - "name": "IOTA EUR" - }, - { - "id": "MIOTA-GBP", - "symbol": "miota", - "name": "IOTA GBP" - }, - { - "id": "MIOTA-INR", - "symbol": "miota", - "name": "IOTA INR" - }, - { - "id": "MIOTA-JPY", - "symbol": "miota", - "name": "IOTA JPY" - }, - { - "id": "MIOTA-KRW", - "symbol": "miota", - "name": "IOTA KRW" - }, - { - "id": "MIOTA-RUB", - "symbol": "miota", - "name": "IOTA RUB" - }, - { - "id": "MIOTA-USD", - "symbol": "miota", - "name": "IOTA USD" - }, - { - "id": "MIR-CAD", - "symbol": "mir", - "name": "MIRCOIN CAD" - }, - { - "id": "MIR-CNY", - "symbol": "mir", - "name": "MIRCOIN CNY" - }, - { - "id": "MIR-ETH", - "symbol": "mir", - "name": "MIRCOIN ETH" - }, - { - "id": "MIR-EUR", - "symbol": "mir", - "name": "MIRCOIN EUR" - }, - { - "id": "MIR-GBP", - "symbol": "mir", - "name": "MIRCOIN GBP" - }, - { - "id": "MIR-INR", - "symbol": "mir", - "name": "MIRCOIN INR" - }, - { - "id": "MIR-JPY", - "symbol": "mir", - "name": "MIRCOIN JPY" - }, - { - "id": "MIR-KRW", - "symbol": "mir", - "name": "MIRCOIN KRW" - }, - { - "id": "MIR-RUB", - "symbol": "mir", - "name": "MIRCOIN RUB" - }, - { - "id": "MIR-USD", - "symbol": "mir", - "name": "MIRCOIN USD" - }, - { - "id": "MIR1-CAD", - "symbol": "mir1", - "name": "MirrorProtocol CAD" - }, - { - "id": "MIR1-CNY", - "symbol": "mir1", - "name": "MirrorProtocol CNY" - }, - { - "id": "MIR1-ETH", - "symbol": "mir1", - "name": "MirrorProtocol ETH" - }, - { - "id": "MIR1-EUR", - "symbol": "mir1", - "name": "MirrorProtocol EUR" - }, - { - "id": "MIR1-GBP", - "symbol": "mir1", - "name": "MirrorProtocol GBP" - }, - { - "id": "MIR1-INR", - "symbol": "mir1", - "name": "MirrorProtocol INR" - }, - { - "id": "MIR1-JPY", - "symbol": "mir1", - "name": "MirrorProtocol JPY" - }, - { - "id": "MIR1-KRW", - "symbol": "mir1", - "name": "MirrorProtocol KRW" - }, - { - "id": "MIR1-RUB", - "symbol": "mir1", - "name": "MirrorProtocol RUB" - }, - { - "id": "MIR1-USD", - "symbol": "mir1", - "name": "MirrorProtocol USD" - }, - { - "id": "MKR-KRW", - "symbol": "mkr", - "name": "Maker KRW" - }, - { - "id": "MKR-RUB", - "symbol": "mkr", - "name": "Maker RUB" - }, - { - "id": "MKR-USD", - "symbol": "mkr", - "name": "Maker USD" - }, - { - "id": "MLN-CNY", - "symbol": "mln", - "name": "Melon CNY" - }, - { - "id": "MLN-ETH", - "symbol": "mln", - "name": "Melon ETH" - }, - { - "id": "MLN-EUR", - "symbol": "mln", - "name": "Melon EUR" - }, - { - "id": "MLN-GBP", - "symbol": "mln", - "name": "Melon GBP" - }, - { - "id": "MLN-INR", - "symbol": "mln", - "name": "Melon INR" - }, - { - "id": "MLN-JPY", - "symbol": "mln", - "name": "Melon JPY" - }, - { - "id": "MLN-KRW", - "symbol": "mln", - "name": "Melon KRW" - }, - { - "id": "MLN-RUB", - "symbol": "mln", - "name": "Melon RUB" - }, - { - "id": "MLN-USD", - "symbol": "mln", - "name": "Melon USD" - }, - { - "id": "MOAC-CAD", - "symbol": "moac", - "name": "MOAC CAD" - }, - { - "id": "MOAC-CNY", - "symbol": "moac", - "name": "MOAC CNY" - }, - { - "id": "MOAC-ETH", - "symbol": "moac", - "name": "MOAC ETH" - }, - { - "id": "MOAC-EUR", - "symbol": "moac", - "name": "MOAC EUR" - }, - { - "id": "MOAC-GBP", - "symbol": "moac", - "name": "MOAC GBP" - }, - { - "id": "MOAC-INR", - "symbol": "moac", - "name": "MOAC INR" - }, - { - "id": "MOAC-JPY", - "symbol": "moac", - "name": "MOAC JPY" - }, - { - "id": "MOAC-KRW", - "symbol": "moac", - "name": "MOAC KRW" - }, - { - "id": "MOAC-RUB", - "symbol": "moac", - "name": "MOAC RUB" - }, - { - "id": "MOAC-USD", - "symbol": "moac", - "name": "MOAC USD" - }, - { - "id": "MONA-BTC", - "symbol": "mona", - "name": "MonaCoin BTC" - }, - { - "id": "MONA-CAD", - "symbol": "mona", - "name": "MonaCoin CAD" - }, - { - "id": "MONA-ETH", - "symbol": "mona", - "name": "MonaCoin ETH" - }, - { - "id": "MONA-EUR", - "symbol": "mona", - "name": "MonaCoin EUR" - }, - { - "id": "MONA-GBP", - "symbol": "mona", - "name": "MonaCoin GBP" - }, - { - "id": "MONA-INR", - "symbol": "mona", - "name": "MonaCoin INR" - }, - { - "id": "MONA-JPY", - "symbol": "mona", - "name": "MonaCoin JPY" - }, - { - "id": "MONA-KRW", - "symbol": "mona", - "name": "MonaCoin KRW" - }, - { - "id": "MONA-RUB", - "symbol": "mona", - "name": "MonaCoin RUB" - }, - { - "id": "MONA-USD", - "symbol": "mona", - "name": "MonaCoin USD" - }, - { - "id": "MOON-BTC", - "symbol": "moon", - "name": "Mooncoin BTC" - }, - { - "id": "MOON-CAD", - "symbol": "moon", - "name": "Mooncoin CAD" - }, - { - "id": "MOON-CNY", - "symbol": "moon", - "name": "Mooncoin CNY" - }, - { - "id": "MOON-ETH", - "symbol": "moon", - "name": "Mooncoin ETH" - }, - { - "id": "MOON-EUR", - "symbol": "moon", - "name": "Mooncoin EUR" - }, - { - "id": "MOON-GBP", - "symbol": "moon", - "name": "Mooncoin GBP" - }, - { - "id": "MOON-INR", - "symbol": "moon", - "name": "Mooncoin INR" - }, - { - "id": "MOON-JPY", - "symbol": "moon", - "name": "Mooncoin JPY" - }, - { - "id": "MOON-KRW", - "symbol": "moon", - "name": "Mooncoin KRW" - }, - { - "id": "MOON-RUB", - "symbol": "moon", - "name": "Mooncoin RUB" - }, - { - "id": "MRX-BTC", - "symbol": "mrx", - "name": "MetrixCoin BTC" - }, - { - "id": "MRX-CAD", - "symbol": "mrx", - "name": "MetrixCoin CAD" - }, - { - "id": "MRX-CNY", - "symbol": "mrx", - "name": "MetrixCoin CNY" - }, - { - "id": "MRX-ETH", - "symbol": "mrx", - "name": "MetrixCoin ETH" - }, - { - "id": "MRX-EUR", - "symbol": "mrx", - "name": "MetrixCoin EUR" - }, - { - "id": "MRX-GBP", - "symbol": "mrx", - "name": "MetrixCoin GBP" - }, - { - "id": "MRX-INR", - "symbol": "mrx", - "name": "MetrixCoin INR" - }, - { - "id": "MRX-JPY", - "symbol": "mrx", - "name": "MetrixCoin JPY" - }, - { - "id": "MRX-KRW", - "symbol": "mrx", - "name": "MetrixCoin KRW" - }, - { - "id": "MRX-RUB", - "symbol": "mrx", - "name": "MetrixCoin RUB" - }, - { - "id": "MRX-USD", - "symbol": "mrx", - "name": "MetrixCoin USD" - }, - { - "id": "MTC2-CAD", - "symbol": "mtc2", - "name": "Metacoin CAD" - }, - { - "id": "MTC2-CNY", - "symbol": "mtc2", - "name": "Metacoin CNY" - }, - { - "id": "MTC2-ETH", - "symbol": "mtc2", - "name": "Metacoin ETH" - }, - { - "id": "MTC2-EUR", - "symbol": "mtc2", - "name": "Metacoin EUR" - }, - { - "id": "MTC2-GBP", - "symbol": "mtc2", - "name": "Metacoin GBP" - }, - { - "id": "MTC2-INR", - "symbol": "mtc2", - "name": "Metacoin INR" - }, - { - "id": "MTC2-JPY", - "symbol": "mtc2", - "name": "Metacoin JPY" - }, - { - "id": "MTC2-KRW", - "symbol": "mtc2", - "name": "Metacoin KRW" - }, - { - "id": "MTC2-RUB", - "symbol": "mtc2", - "name": "Metacoin RUB" - }, - { - "id": "MTC2-USD", - "symbol": "mtc2", - "name": "Metacoin USD" - }, - { - "id": "MWC-CAD", - "symbol": "mwc", - "name": "MimbleWimbleCoin CAD" - }, - { - "id": "MWC-CNY", - "symbol": "mwc", - "name": "MimbleWimbleCoin CNY" - }, - { - "id": "MWC-ETH", - "symbol": "mwc", - "name": "MimbleWimbleCoin ETH" - }, - { - "id": "MWC-EUR", - "symbol": "mwc", - "name": "MimbleWimbleCoin EUR" - }, - { - "id": "MWC-GBP", - "symbol": "mwc", - "name": "MimbleWimbleCoin GBP" - }, - { - "id": "MWC-INR", - "symbol": "mwc", - "name": "MimbleWimbleCoin INR" - }, - { - "id": "MWC-JPY", - "symbol": "mwc", - "name": "MimbleWimbleCoin JPY" - }, - { - "id": "MWC-KRW", - "symbol": "mwc", - "name": "MimbleWimbleCoin KRW" - }, - { - "id": "MWC-RUB", - "symbol": "mwc", - "name": "MimbleWimbleCoin RUB" - }, - { - "id": "MWC-USD", - "symbol": "mwc", - "name": "MimbleWimbleCoin USD" - }, - { - "id": "NAS-CAD", - "symbol": "nas", - "name": "Nebulas CAD" - }, - { - "id": "NAS-CNY", - "symbol": "nas", - "name": "Nebulas CNY" - }, - { - "id": "NAS-ETH", - "symbol": "nas", - "name": "Nebulas ETH" - }, - { - "id": "NAS-EUR", - "symbol": "nas", - "name": "Nebulas EUR" - }, - { - "id": "NAS-GBP", - "symbol": "nas", - "name": "Nebulas GBP" - }, - { - "id": "NAS-INR", - "symbol": "nas", - "name": "Nebulas INR" - }, - { - "id": "NAS-JPY", - "symbol": "nas", - "name": "Nebulas JPY" - }, - { - "id": "NAS-KRW", - "symbol": "nas", - "name": "Nebulas KRW" - }, - { - "id": "NAS-RUB", - "symbol": "nas", - "name": "Nebulas RUB" - }, - { - "id": "NAS-USD", - "symbol": "nas", - "name": "Nebulas USD" - }, - { - "id": "NAV-AUD", - "symbol": "nav", - "name": "NavCoin AUD" - }, - { - "id": "NAV-BTC", - "symbol": "nav", - "name": "NavCoin BTC" - }, - { - "id": "NAV-CAD", - "symbol": "nav", - "name": "NavCoin CAD" - }, - { - "id": "NAV-CNY", - "symbol": "nav", - "name": "NavCoin CNY" - }, - { - "id": "NAV-ETH", - "symbol": "nav", - "name": "NavCoin ETH" - }, - { - "id": "NAV-EUR", - "symbol": "nav", - "name": "NavCoin EUR" - }, - { - "id": "NAV-JPY", - "symbol": "nav", - "name": "NavCoin JPY" - }, - { - "id": "NAV-KRW", - "symbol": "nav", - "name": "NavCoin KRW" - }, - { - "id": "NAV-RUB", - "symbol": "nav", - "name": "NavCoin RUB" - }, - { - "id": "NAV-USD", - "symbol": "nav", - "name": "NavCoin USD" - }, - { - "id": "NEBL-AUD", - "symbol": "nebl", - "name": "Neblio AUD" - }, - { - "id": "NEBL-BTC", - "symbol": "nebl", - "name": "Neblio BTC" - }, - { - "id": "NEBL-CAD", - "symbol": "nebl", - "name": "Neblio CAD" - }, - { - "id": "NEBL-ETH", - "symbol": "nebl", - "name": "Neblio ETH" - }, - { - "id": "NEBL-EUR", - "symbol": "nebl", - "name": "Neblio EUR" - }, - { - "id": "NEBL-GBP", - "symbol": "nebl", - "name": "Neblio GBP" - }, - { - "id": "NEBL-INR", - "symbol": "nebl", - "name": "Neblio INR" - }, - { - "id": "NEBL-KRW", - "symbol": "nebl", - "name": "Neblio KRW" - }, - { - "id": "NEBL-RUB", - "symbol": "nebl", - "name": "Neblio RUB" - }, - { - "id": "NEBL-USD", - "symbol": "nebl", - "name": "Neblio USD" - }, - { - "id": "NIM-CAD", - "symbol": "nim", - "name": "Nimiq CAD" - }, - { - "id": "NIM-CNY", - "symbol": "nim", - "name": "Nimiq CNY" - }, - { - "id": "NIM-ETH", - "symbol": "nim", - "name": "Nimiq ETH" - }, - { - "id": "NIM-EUR", - "symbol": "nim", - "name": "Nimiq EUR" - }, - { - "id": "NIM-GBP", - "symbol": "nim", - "name": "Nimiq GBP" - }, - { - "id": "NIM-INR", - "symbol": "nim", - "name": "Nimiq INR" - }, - { - "id": "NIM-JPY", - "symbol": "nim", - "name": "Nimiq JPY" - }, - { - "id": "NIM-KRW", - "symbol": "nim", - "name": "Nimiq KRW" - }, - { - "id": "NIM-RUB", - "symbol": "nim", - "name": "Nimiq RUB" - }, - { - "id": "NIM-USD", - "symbol": "nim", - "name": "Nimiq USD" - }, - { - "id": "NLC2-AUD", - "symbol": "nlc2", - "name": "NoLimitCoin AUD" - }, - { - "id": "NLC2-BTC", - "symbol": "nlc2", - "name": "NoLimitCoin BTC" - }, - { - "id": "NLC2-CAD", - "symbol": "nlc2", - "name": "NoLimitCoin CAD" - }, - { - "id": "NLC2-CNY", - "symbol": "nlc2", - "name": "NoLimitCoin CNY" - }, - { - "id": "NLC2-ETH", - "symbol": "nlc2", - "name": "NoLimitCoin ETH" - }, - { - "id": "NLC2-EUR", - "symbol": "nlc2", - "name": "NoLimitCoin EUR" - }, - { - "id": "NLC2-GBP", - "symbol": "nlc2", - "name": "NoLimitCoin GBP" - }, - { - "id": "NLC2-JPY", - "symbol": "nlc2", - "name": "NoLimitCoin JPY" - }, - { - "id": "NLC2-RUB", - "symbol": "nlc2", - "name": "NoLimitCoin RUB" - }, - { - "id": "NLC2-USD", - "symbol": "nlc2", - "name": "NoLimitCoin USD" - }, - { - "id": "NLG-CAD", - "symbol": "nlg", - "name": "Gulden CAD" - }, - { - "id": "NLG-CNY", - "symbol": "nlg", - "name": "Gulden CNY" - }, - { - "id": "NLG-ETH", - "symbol": "nlg", - "name": "Gulden ETH" - }, - { - "id": "NLG-EUR", - "symbol": "nlg", - "name": "Gulden EUR" - }, - { - "id": "NLG-GBP", - "symbol": "nlg", - "name": "Gulden GBP" - }, - { - "id": "NLG-INR", - "symbol": "nlg", - "name": "Gulden INR" - }, - { - "id": "NLG-JPY", - "symbol": "nlg", - "name": "Gulden JPY" - }, - { - "id": "NLG-KRW", - "symbol": "nlg", - "name": "Gulden KRW" - }, - { - "id": "NLG-RUB", - "symbol": "nlg", - "name": "Gulden RUB" - }, - { - "id": "NLG-USD", - "symbol": "nlg", - "name": "Gulden USD" - }, - { - "id": "NMC-CAD", - "symbol": "nmc", - "name": "Namecoin CAD" - }, - { - "id": "NMC-CNY", - "symbol": "nmc", - "name": "Namecoin CNY" - }, - { - "id": "NMC-ETH", - "symbol": "nmc", - "name": "Namecoin ETH" - }, - { - "id": "NMC-EUR", - "symbol": "nmc", - "name": "Namecoin EUR" - }, - { - "id": "NMC-GBP", - "symbol": "nmc", - "name": "Namecoin GBP" - }, - { - "id": "NMC-INR", - "symbol": "nmc", - "name": "Namecoin INR" - }, - { - "id": "NMC-JPY", - "symbol": "nmc", - "name": "Namecoin JPY" - }, - { - "id": "NMC-KRW", - "symbol": "nmc", - "name": "Namecoin KRW" - }, - { - "id": "NMC-RUB", - "symbol": "nmc", - "name": "Namecoin RUB" - }, - { - "id": "NMC-USD", - "symbol": "nmc", - "name": "Namecoin USD" - }, - { - "id": "NPC-CAD", - "symbol": "npc", - "name": "NPCoin CAD" - }, - { - "id": "NPC-CNY", - "symbol": "npc", - "name": "NPCoin CNY" - }, - { - "id": "NPC-ETH", - "symbol": "npc", - "name": "NPCoin ETH" - }, - { - "id": "NPC-EUR", - "symbol": "npc", - "name": "NPCoin EUR" - }, - { - "id": "NPC-GBP", - "symbol": "npc", - "name": "NPCoin GBP" - }, - { - "id": "NPC-INR", - "symbol": "npc", - "name": "NPCoin INR" - }, - { - "id": "NPC-JPY", - "symbol": "npc", - "name": "NPCoin JPY" - }, - { - "id": "NPC-KRW", - "symbol": "npc", - "name": "NPCoin KRW" - }, - { - "id": "NPC-RUB", - "symbol": "npc", - "name": "NPCoin RUB" - }, - { - "id": "NPC-USD", - "symbol": "npc", - "name": "NPCoin USD" - }, - { - "id": "NU-CAD", - "symbol": "nu", - "name": "NuCypher CAD" - }, - { - "id": "NU-CNY", - "symbol": "nu", - "name": "NuCypher CNY" - }, - { - "id": "NU-ETH", - "symbol": "nu", - "name": "NuCypher ETH" - }, - { - "id": "NU-EUR", - "symbol": "nu", - "name": "NuCypher EUR" - }, - { - "id": "NU-GBP", - "symbol": "nu", - "name": "NuCypher GBP" - }, - { - "id": "NU-INR", - "symbol": "nu", - "name": "NuCypher INR" - }, - { - "id": "NU-JPY", - "symbol": "nu", - "name": "NuCypher JPY" - }, - { - "id": "NU-KRW", - "symbol": "nu", - "name": "NuCypher KRW" - }, - { - "id": "NU-RUB", - "symbol": "nu", - "name": "NuCypher RUB" - }, - { - "id": "NU-USD", - "symbol": "nu", - "name": "NuCypher USD" - }, - { - "id": "NULS-CAD", - "symbol": "nuls", - "name": "NULS CAD" - }, - { - "id": "NULS-CNY", - "symbol": "nuls", - "name": "NULS CNY" - }, - { - "id": "NULS-ETH", - "symbol": "nuls", - "name": "NULS ETH" - }, - { - "id": "NULS-EUR", - "symbol": "nuls", - "name": "NULS EUR" - }, - { - "id": "NULS-GBP", - "symbol": "nuls", - "name": "NULS GBP" - }, - { - "id": "NULS-INR", - "symbol": "nuls", - "name": "NULS INR" - }, - { - "id": "NULS-JPY", - "symbol": "nuls", - "name": "NULS JPY" - }, - { - "id": "NULS-KRW", - "symbol": "nuls", - "name": "NULS KRW" - }, - { - "id": "NULS-RUB", - "symbol": "nuls", - "name": "NULS RUB" - }, - { - "id": "NULS-USD", - "symbol": "nuls", - "name": "NULS USD" - }, - { - "id": "NVT-CAD", - "symbol": "nvt", - "name": "NerveNetwork CAD" - }, - { - "id": "NVT-CNY", - "symbol": "nvt", - "name": "NerveNetwork CNY" - }, - { - "id": "NVT-ETH", - "symbol": "nvt", - "name": "NerveNetwork ETH" - }, - { - "id": "NVT-EUR", - "symbol": "nvt", - "name": "NerveNetwork EUR" - }, - { - "id": "NVT-GBP", - "symbol": "nvt", - "name": "NerveNetwork GBP" - }, - { - "id": "NVT-INR", - "symbol": "nvt", - "name": "NerveNetwork INR" - }, - { - "id": "NVT-JPY", - "symbol": "nvt", - "name": "NerveNetwork JPY" - }, - { - "id": "NVT-KRW", - "symbol": "nvt", - "name": "NerveNetwork KRW" - }, - { - "id": "NVT-RUB", - "symbol": "nvt", - "name": "NerveNetwork RUB" - }, - { - "id": "NVT-USD", - "symbol": "nvt", - "name": "NerveNetwork USD" - }, - { - "id": "NXS-CAD", - "symbol": "nxs", - "name": "Nexus CAD" - }, - { - "id": "NXS-EUR", - "symbol": "nxs", - "name": "Nexus EUR" - }, - { - "id": "NXS-USD", - "symbol": "nxs", - "name": "Nexus USD" - }, - { - "id": "NYE-CAD", - "symbol": "nye", - "name": "NewYorkExchange CAD" - }, - { - "id": "NYE-CNY", - "symbol": "nye", - "name": "NewYorkExchange CNY" - }, - { - "id": "NYE-ETH", - "symbol": "nye", - "name": "NewYorkExchange ETH" - }, - { - "id": "NYE-EUR", - "symbol": "nye", - "name": "NewYorkExchange EUR" - }, - { - "id": "NYE-GBP", - "symbol": "nye", - "name": "NewYorkExchange GBP" - }, - { - "id": "NYE-INR", - "symbol": "nye", - "name": "NewYorkExchange INR" - }, - { - "id": "NYE-JPY", - "symbol": "nye", - "name": "NewYorkExchange JPY" - }, - { - "id": "NYE-KRW", - "symbol": "nye", - "name": "NewYorkExchange KRW" - }, - { - "id": "NYE-RUB", - "symbol": "nye", - "name": "NewYorkExchange RUB" - }, - { - "id": "NYE-USD", - "symbol": "nye", - "name": "NewYorkExchange USD" - }, - { - "id": "NYZO-CAD", - "symbol": "nyzo", - "name": "Nyzo CAD" - }, - { - "id": "NYZO-CNY", - "symbol": "nyzo", - "name": "Nyzo CNY" - }, - { - "id": "NYZO-ETH", - "symbol": "nyzo", - "name": "Nyzo ETH" - }, - { - "id": "NYZO-EUR", - "symbol": "nyzo", - "name": "Nyzo EUR" - }, - { - "id": "NYZO-GBP", - "symbol": "nyzo", - "name": "Nyzo GBP" - }, - { - "id": "NYZO-INR", - "symbol": "nyzo", - "name": "Nyzo INR" - }, - { - "id": "NYZO-JPY", - "symbol": "nyzo", - "name": "Nyzo JPY" - }, - { - "id": "NYZO-KRW", - "symbol": "nyzo", - "name": "Nyzo KRW" - }, - { - "id": "NYZO-RUB", - "symbol": "nyzo", - "name": "Nyzo RUB" - }, - { - "id": "NYZO-USD", - "symbol": "nyzo", - "name": "Nyzo USD" - }, - { - "id": "OBSR-CAD", - "symbol": "obsr", - "name": "Observer CAD" - }, - { - "id": "OBSR-CNY", - "symbol": "obsr", - "name": "Observer CNY" - }, - { - "id": "OBSR-ETH", - "symbol": "obsr", - "name": "Observer ETH" - }, - { - "id": "OBSR-EUR", - "symbol": "obsr", - "name": "Observer EUR" - }, - { - "id": "OBSR-GBP", - "symbol": "obsr", - "name": "Observer GBP" - }, - { - "id": "OBSR-INR", - "symbol": "obsr", - "name": "Observer INR" - }, - { - "id": "OBSR-JPY", - "symbol": "obsr", - "name": "Observer JPY" - }, - { - "id": "OBSR-KRW", - "symbol": "obsr", - "name": "Observer KRW" - }, - { - "id": "OBSR-RUB", - "symbol": "obsr", - "name": "Observer RUB" - }, - { - "id": "OBSR-USD", - "symbol": "obsr", - "name": "Observer USD" - }, - { - "id": "OMG-AUD", - "symbol": "omg", - "name": "OmiseGO AUD" - }, - { - "id": "OMG-BTC", - "symbol": "omg", - "name": "OmiseGO BTC" - }, - { - "id": "OMG-ETH", - "symbol": "omg", - "name": "OmiseGO ETH" - }, - { - "id": "OMG-EUR", - "symbol": "omg", - "name": "OmiseGO EUR" - }, - { - "id": "OMG-GBP", - "symbol": "omg", - "name": "OmiseGO GBP" - }, - { - "id": "OMG-INR", - "symbol": "omg", - "name": "OmiseGO INR" - }, - { - "id": "OMG-JPY", - "symbol": "omg", - "name": "OmiseGO JPY" - }, - { - "id": "OMG-KRW", - "symbol": "omg", - "name": "OmiseGO KRW" - }, - { - "id": "OMG-RUB", - "symbol": "omg", - "name": "OmiseGO RUB" - }, - { - "id": "OMG-USD", - "symbol": "omg", - "name": "OmiseGO USD" - }, - { - "id": "ONE2-CAD", - "symbol": "one2", - "name": "Harmony CAD" - }, - { - "id": "ONE2-CNY", - "symbol": "one2", - "name": "Harmony CNY" - }, - { - "id": "ONE2-ETH", - "symbol": "one2", - "name": "Harmony ETH" - }, - { - "id": "ONE2-EUR", - "symbol": "one2", - "name": "Harmony EUR" - }, - { - "id": "ONE2-GBP", - "symbol": "one2", - "name": "Harmony GBP" - }, - { - "id": "ONE2-INR", - "symbol": "one2", - "name": "Harmony INR" - }, - { - "id": "ONE2-JPY", - "symbol": "one2", - "name": "Harmony JPY" - }, - { - "id": "ONE2-KRW", - "symbol": "one2", - "name": "Harmony KRW" - }, - { - "id": "ONE2-RUB", - "symbol": "one2", - "name": "Harmony RUB" - }, - { - "id": "ONE2-USD", - "symbol": "one2", - "name": "Harmony USD" - }, - { - "id": "ONT-CAD", - "symbol": "ont", - "name": "Ontology CAD" - }, - { - "id": "ONT-CNY", - "symbol": "ont", - "name": "Ontology CNY" - }, - { - "id": "ONT-ETH", - "symbol": "ont", - "name": "Ontology ETH" - }, - { - "id": "ONT-EUR", - "symbol": "ont", - "name": "Ontology EUR" - }, - { - "id": "ONT-GBP", - "symbol": "ont", - "name": "Ontology GBP" - }, - { - "id": "ONT-INR", - "symbol": "ont", - "name": "Ontology INR" - }, - { - "id": "ONT-JPY", - "symbol": "ont", - "name": "Ontology JPY" - }, - { - "id": "ONT-KRW", - "symbol": "ont", - "name": "Ontology KRW" - }, - { - "id": "ONT-RUB", - "symbol": "ont", - "name": "Ontology RUB" - }, - { - "id": "ONT-USD", - "symbol": "ont", - "name": "Ontology USD" - }, - { - "id": "OTO-CAD", - "symbol": "oto", - "name": "OTOCASH CAD" - }, - { - "id": "OTO-CNY", - "symbol": "oto", - "name": "OTOCASH CNY" - }, - { - "id": "OTO-ETH", - "symbol": "oto", - "name": "OTOCASH ETH" - }, - { - "id": "OTO-EUR", - "symbol": "oto", - "name": "OTOCASH EUR" - }, - { - "id": "OTO-GBP", - "symbol": "oto", - "name": "OTOCASH GBP" - }, - { - "id": "OTO-INR", - "symbol": "oto", - "name": "OTOCASH INR" - }, - { - "id": "OTO-JPY", - "symbol": "oto", - "name": "OTOCASH JPY" - }, - { - "id": "OTO-KRW", - "symbol": "oto", - "name": "OTOCASH KRW" - }, - { - "id": "OTO-RUB", - "symbol": "oto", - "name": "OTOCASH RUB" - }, - { - "id": "OTO-USD", - "symbol": "oto", - "name": "OTOCASH USD" - }, - { - "id": "OURO-CAD", - "symbol": "ouro", - "name": "Ouroboros CAD" - }, - { - "id": "OURO-CNY", - "symbol": "ouro", - "name": "Ouroboros CNY" - }, - { - "id": "OURO-ETH", - "symbol": "ouro", - "name": "Ouroboros ETH" - }, - { - "id": "OURO-EUR", - "symbol": "ouro", - "name": "Ouroboros EUR" - }, - { - "id": "OURO-GBP", - "symbol": "ouro", - "name": "Ouroboros GBP" - }, - { - "id": "OURO-INR", - "symbol": "ouro", - "name": "Ouroboros INR" - }, - { - "id": "OURO-JPY", - "symbol": "ouro", - "name": "Ouroboros JPY" - }, - { - "id": "OURO-KRW", - "symbol": "ouro", - "name": "Ouroboros KRW" - }, - { - "id": "OURO-RUB", - "symbol": "ouro", - "name": "Ouroboros RUB" - }, - { - "id": "OURO-USD", - "symbol": "ouro", - "name": "Ouroboros USD" - }, - { - "id": "OWC-CAD", - "symbol": "owc", - "name": "ODUWA CAD" - }, - { - "id": "OWC-CNY", - "symbol": "owc", - "name": "ODUWA CNY" - }, - { - "id": "OWC-ETH", - "symbol": "owc", - "name": "ODUWA ETH" - }, - { - "id": "OWC-EUR", - "symbol": "owc", - "name": "ODUWA EUR" - }, - { - "id": "OWC-GBP", - "symbol": "owc", - "name": "ODUWA GBP" - }, - { - "id": "OWC-INR", - "symbol": "owc", - "name": "ODUWA INR" - }, - { - "id": "OWC-JPY", - "symbol": "owc", - "name": "ODUWA JPY" - }, - { - "id": "OWC-KRW", - "symbol": "owc", - "name": "ODUWA KRW" - }, - { - "id": "OWC-RUB", - "symbol": "owc", - "name": "ODUWA RUB" - }, - { - "id": "OWC-USD", - "symbol": "owc", - "name": "ODUWA USD" - }, - { - "id": "OXT-CNY", - "symbol": "oxt", - "name": "Orchid CNY" - }, - { - "id": "OXT-ETH", - "symbol": "oxt", - "name": "Orchid ETH" - }, - { - "id": "OXT-EUR", - "symbol": "oxt", - "name": "Orchid EUR" - }, - { - "id": "OXT-GBP", - "symbol": "oxt", - "name": "Orchid GBP" - }, - { - "id": "OXT-INR", - "symbol": "oxt", - "name": "Orchid INR" - }, - { - "id": "OXT-JPY", - "symbol": "oxt", - "name": "Orchid JPY" - }, - { - "id": "OXT-KRW", - "symbol": "oxt", - "name": "Orchid KRW" - }, - { - "id": "OXT-RUB", - "symbol": "oxt", - "name": "Orchid RUB" - }, - { - "id": "OXT-USD", - "symbol": "oxt", - "name": "Orchid USD" - }, - { - "id": "PAC-AUD", - "symbol": "pac", - "name": "PACGlobal AUD" - }, - { - "id": "PAC-CAD", - "symbol": "pac", - "name": "PACGlobal CAD" - }, - { - "id": "PAC-CNY", - "symbol": "pac", - "name": "PACGlobal CNY" - }, - { - "id": "PAC-ETH", - "symbol": "pac", - "name": "PACGlobal ETH" - }, - { - "id": "PAC-EUR", - "symbol": "pac", - "name": "PACGlobal EUR" - }, - { - "id": "PAC-GBP", - "symbol": "pac", - "name": "PACGlobal GBP" - }, - { - "id": "PAC-INR", - "symbol": "pac", - "name": "PACGlobal INR" - }, - { - "id": "PAC-JPY", - "symbol": "pac", - "name": "PACGlobal JPY" - }, - { - "id": "PAC-KRW", - "symbol": "pac", - "name": "PACGlobal KRW" - }, - { - "id": "PAC-RUB", - "symbol": "pac", - "name": "PACGlobal RUB" - }, - { - "id": "PAC-USD", - "symbol": "pac", - "name": "PACGlobal USD" - }, - { - "id": "PAI-BTC", - "symbol": "pai", - "name": "ProjectPai BTC" - }, - { - "id": "PAI-CNY", - "symbol": "pai", - "name": "ProjectPai CNY" - }, - { - "id": "PAI-ETH", - "symbol": "pai", - "name": "ProjectPai ETH" - }, - { - "id": "PAI-EUR", - "symbol": "pai", - "name": "ProjectPai EUR" - }, - { - "id": "PAI-GBP", - "symbol": "pai", - "name": "ProjectPai GBP" - }, - { - "id": "PAI-INR", - "symbol": "pai", - "name": "ProjectPai INR" - }, - { - "id": "PAI-JPY", - "symbol": "pai", - "name": "ProjectPai JPY" - }, - { - "id": "PAI-KRW", - "symbol": "pai", - "name": "ProjectPai KRW" - }, - { - "id": "PAI-RUB", - "symbol": "pai", - "name": "ProjectPai RUB" - }, - { - "id": "PAI-USD", - "symbol": "pai", - "name": "ProjectPai USD" - }, - { - "id": "PART-BTC", - "symbol": "part", - "name": "Particl BTC" - }, - { - "id": "PART-CAD", - "symbol": "part", - "name": "Particl CAD" - }, - { - "id": "PART-CNY", - "symbol": "part", - "name": "Particl CNY" - }, - { - "id": "PART-ETH", - "symbol": "part", - "name": "Particl ETH" - }, - { - "id": "PART-EUR", - "symbol": "part", - "name": "Particl EUR" - }, - { - "id": "PART-GBP", - "symbol": "part", - "name": "Particl GBP" - }, - { - "id": "PART-INR", - "symbol": "part", - "name": "Particl INR" - }, - { - "id": "PART-JPY", - "symbol": "part", - "name": "Particl JPY" - }, - { - "id": "PART-RUB", - "symbol": "part", - "name": "Particl RUB" - }, - { - "id": "PART-USD", - "symbol": "part", - "name": "Particl USD" - }, - { - "id": "PAY-ETH", - "symbol": "pay", - "name": "TenX ETH" - }, - { - "id": "PAY-EUR", - "symbol": "pay", - "name": "TenX EUR" - }, - { - "id": "PAY-GBP", - "symbol": "pay", - "name": "TenX GBP" - }, - { - "id": "PAY-INR", - "symbol": "pay", - "name": "TenX INR" - }, - { - "id": "PAY-JPY", - "symbol": "pay", - "name": "TenX JPY" - }, - { - "id": "PAY-KRW", - "symbol": "pay", - "name": "TenX KRW" - }, - { - "id": "PAY-RUB", - "symbol": "pay", - "name": "TenX RUB" - }, - { - "id": "PAY-USD", - "symbol": "pay", - "name": "TenX USD" - }, - { - "id": "PCX-CAD", - "symbol": "pcx", - "name": "ChainX CAD" - }, - { - "id": "PCX-CNY", - "symbol": "pcx", - "name": "ChainX CNY" - }, - { - "id": "PCX-ETH", - "symbol": "pcx", - "name": "ChainX ETH" - }, - { - "id": "PCX-EUR", - "symbol": "pcx", - "name": "ChainX EUR" - }, - { - "id": "PCX-GBP", - "symbol": "pcx", - "name": "ChainX GBP" - }, - { - "id": "PCX-INR", - "symbol": "pcx", - "name": "ChainX INR" - }, - { - "id": "PCX-JPY", - "symbol": "pcx", - "name": "ChainX JPY" - }, - { - "id": "PCX-KRW", - "symbol": "pcx", - "name": "ChainX KRW" - }, - { - "id": "PCX-RUB", - "symbol": "pcx", - "name": "ChainX RUB" - }, - { - "id": "PCX-USD", - "symbol": "pcx", - "name": "ChainX USD" - }, - { - "id": "PHA-CAD", - "symbol": "pha", - "name": "PhalaNetwork CAD" - }, - { - "id": "PHA-CNY", - "symbol": "pha", - "name": "PhalaNetwork CNY" - }, - { - "id": "PHA-ETH", - "symbol": "pha", - "name": "PhalaNetwork ETH" - }, - { - "id": "PHA-EUR", - "symbol": "pha", - "name": "PhalaNetwork EUR" - }, - { - "id": "PHA-GBP", - "symbol": "pha", - "name": "PhalaNetwork GBP" - }, - { - "id": "PHA-INR", - "symbol": "pha", - "name": "PhalaNetwork INR" - }, - { - "id": "PHA-JPY", - "symbol": "pha", - "name": "PhalaNetwork JPY" - }, - { - "id": "PHA-KRW", - "symbol": "pha", - "name": "PhalaNetwork KRW" - }, - { - "id": "PHA-RUB", - "symbol": "pha", - "name": "PhalaNetwork RUB" - }, - { - "id": "PHA-USD", - "symbol": "pha", - "name": "PhalaNetwork USD" - }, - { - "id": "PHR-CAD", - "symbol": "phr", - "name": "Phore CAD" - }, - { - "id": "PHR-CNY", - "symbol": "phr", - "name": "Phore CNY" - }, - { - "id": "PHR-ETH", - "symbol": "phr", - "name": "Phore ETH" - }, - { - "id": "PHR-EUR", - "symbol": "phr", - "name": "Phore EUR" - }, - { - "id": "PHR-GBP", - "symbol": "phr", - "name": "Phore GBP" - }, - { - "id": "PHR-INR", - "symbol": "phr", - "name": "Phore INR" - }, - { - "id": "PHR-JPY", - "symbol": "phr", - "name": "Phore JPY" - }, - { - "id": "PHR-KRW", - "symbol": "phr", - "name": "Phore KRW" - }, - { - "id": "PHR-RUB", - "symbol": "phr", - "name": "Phore RUB" - }, - { - "id": "PHR-USD", - "symbol": "phr", - "name": "Phore USD" - }, - { - "id": "PI-CAD", - "symbol": "pi", - "name": "PCHAIN CAD" - }, - { - "id": "PI-CNY", - "symbol": "pi", - "name": "PCHAIN CNY" - }, - { - "id": "PI-ETH", - "symbol": "pi", - "name": "PCHAIN ETH" - }, - { - "id": "PI-EUR", - "symbol": "pi", - "name": "PCHAIN EUR" - }, - { - "id": "PI-GBP", - "symbol": "pi", - "name": "PCHAIN GBP" - }, - { - "id": "PI-INR", - "symbol": "pi", - "name": "PCHAIN INR" - }, - { - "id": "PI-JPY", - "symbol": "pi", - "name": "PCHAIN JPY" - }, - { - "id": "PI-KRW", - "symbol": "pi", - "name": "PCHAIN KRW" - }, - { - "id": "PI-RUB", - "symbol": "pi", - "name": "PCHAIN RUB" - }, - { - "id": "PI-USD", - "symbol": "pi", - "name": "PCHAIN USD" - }, - { - "id": "PIVX-CNY", - "symbol": "pivx", - "name": "PIVX CNY" - }, - { - "id": "PIVX-ETH", - "symbol": "pivx", - "name": "PIVX ETH" - }, - { - "id": "PIVX-EUR", - "symbol": "pivx", - "name": "PIVX EUR" - }, - { - "id": "PIVX-GBP", - "symbol": "pivx", - "name": "PIVX GBP" - }, - { - "id": "PIVX-INR", - "symbol": "pivx", - "name": "PIVX INR" - }, - { - "id": "PIVX-JPY", - "symbol": "pivx", - "name": "PIVX JPY" - }, - { - "id": "PIVX-KRW", - "symbol": "pivx", - "name": "PIVX KRW" - }, - { - "id": "PIVX-RUB", - "symbol": "pivx", - "name": "PIVX RUB" - }, - { - "id": "PIVX-USD", - "symbol": "pivx", - "name": "PIVX USD" - }, - { - "id": "PLC-CAD", - "symbol": "plc", - "name": "PLATINCOIN CAD" - }, - { - "id": "PLC-CNY", - "symbol": "plc", - "name": "PLATINCOIN CNY" - }, - { - "id": "PLC-ETH", - "symbol": "plc", - "name": "PLATINCOIN ETH" - }, - { - "id": "PLC-EUR", - "symbol": "plc", - "name": "PLATINCOIN EUR" - }, - { - "id": "PLC-GBP", - "symbol": "plc", - "name": "PLATINCOIN GBP" - }, - { - "id": "PLC-INR", - "symbol": "plc", - "name": "PLATINCOIN INR" - }, - { - "id": "PLC-JPY", - "symbol": "plc", - "name": "PLATINCOIN JPY" - }, - { - "id": "PLC-KRW", - "symbol": "plc", - "name": "PLATINCOIN KRW" - }, - { - "id": "PLC-RUB", - "symbol": "plc", - "name": "PLATINCOIN RUB" - }, - { - "id": "PLC-USD", - "symbol": "plc", - "name": "PLATINCOIN USD" - }, - { - "id": "PMEER-CAD", - "symbol": "pmeer", - "name": "Pmeer CAD" - }, - { - "id": "PMEER-CNY", - "symbol": "pmeer", - "name": "Pmeer CNY" - }, - { - "id": "PMEER-ETH", - "symbol": "pmeer", - "name": "Pmeer ETH" - }, - { - "id": "PMEER-EUR", - "symbol": "pmeer", - "name": "Pmeer EUR" - }, - { - "id": "PMEER-GBP", - "symbol": "pmeer", - "name": "Pmeer GBP" - }, - { - "id": "PMEER-INR", - "symbol": "pmeer", - "name": "Pmeer INR" - }, - { - "id": "PMEER-JPY", - "symbol": "pmeer", - "name": "Pmeer JPY" - }, - { - "id": "PMEER-KRW", - "symbol": "pmeer", - "name": "Pmeer KRW" - }, - { - "id": "PMEER-RUB", - "symbol": "pmeer", - "name": "Pmeer RUB" - }, - { - "id": "PMEER-USD", - "symbol": "pmeer", - "name": "Pmeer USD" - }, - { - "id": "POA-CAD", - "symbol": "poa", - "name": "POANetwork CAD" - }, - { - "id": "POA-CNY", - "symbol": "poa", - "name": "POANetwork CNY" - }, - { - "id": "POA-ETH", - "symbol": "poa", - "name": "POANetwork ETH" - }, - { - "id": "POA-EUR", - "symbol": "poa", - "name": "POANetwork EUR" - }, - { - "id": "POA-GBP", - "symbol": "poa", - "name": "POANetwork GBP" - }, - { - "id": "POA-INR", - "symbol": "poa", - "name": "POANetwork INR" - }, - { - "id": "POA-JPY", - "symbol": "poa", - "name": "POANetwork JPY" - }, - { - "id": "POA-KRW", - "symbol": "poa", - "name": "POANetwork KRW" - }, - { - "id": "POA-RUB", - "symbol": "poa", - "name": "POANetwork RUB" - }, - { - "id": "POA-USD", - "symbol": "poa", - "name": "POANetwork USD" - }, - { - "id": "POLIS-INR", - "symbol": "polis", - "name": "Polis INR" - }, - { - "id": "POLIS-JPY", - "symbol": "polis", - "name": "Polis JPY" - }, - { - "id": "POLIS-KRW", - "symbol": "polis", - "name": "Polis KRW" - }, - { - "id": "POLIS-RUB", - "symbol": "polis", - "name": "Polis RUB" - }, - { - "id": "POLIS-USD", - "symbol": "polis", - "name": "Polis USD" - }, - { - "id": "PPC-CAD", - "symbol": "ppc", - "name": "Peercoin CAD" - }, - { - "id": "PPC-CNY", - "symbol": "ppc", - "name": "Peercoin CNY" - }, - { - "id": "PPC-ETH", - "symbol": "ppc", - "name": "Peercoin ETH" - }, - { - "id": "PPC-EUR", - "symbol": "ppc", - "name": "Peercoin EUR" - }, - { - "id": "PPC-GBP", - "symbol": "ppc", - "name": "Peercoin GBP" - }, - { - "id": "PPC-INR", - "symbol": "ppc", - "name": "Peercoin INR" - }, - { - "id": "PPC-JPY", - "symbol": "ppc", - "name": "Peercoin JPY" - }, - { - "id": "PPC-KRW", - "symbol": "ppc", - "name": "Peercoin KRW" - }, - { - "id": "PPC-RUB", - "symbol": "ppc", - "name": "Peercoin RUB" - }, - { - "id": "PPC-USD", - "symbol": "ppc", - "name": "Peercoin USD" - }, - { - "id": "PPT-BTC", - "symbol": "ppt", - "name": "Populous BTC" - }, - { - "id": "PPT-CAD", - "symbol": "ppt", - "name": "Populous CAD" - }, - { - "id": "PPT-CNY", - "symbol": "ppt", - "name": "Populous CNY" - }, - { - "id": "PPT-ETH", - "symbol": "ppt", - "name": "Populous ETH" - }, - { - "id": "PPT-EUR", - "symbol": "ppt", - "name": "Populous EUR" - }, - { - "id": "PPT-GBP", - "symbol": "ppt", - "name": "Populous GBP" - }, - { - "id": "PPT-JPY", - "symbol": "ppt", - "name": "Populous JPY" - }, - { - "id": "PPT-KRW", - "symbol": "ppt", - "name": "Populous KRW" - }, - { - "id": "PPT-RUB", - "symbol": "ppt", - "name": "Populous RUB" - }, - { - "id": "PPT-USD", - "symbol": "ppt", - "name": "Populous USD" - }, - { - "id": "PZM-CNY", - "symbol": "pzm", - "name": "PRIZM CNY" - }, - { - "id": "PZM-ETH", - "symbol": "pzm", - "name": "PRIZM ETH" - }, - { - "id": "PZM-EUR", - "symbol": "pzm", - "name": "PRIZM EUR" - }, - { - "id": "PZM-GBP", - "symbol": "pzm", - "name": "PRIZM GBP" - }, - { - "id": "PZM-INR", - "symbol": "pzm", - "name": "PRIZM INR" - }, - { - "id": "PZM-JPY", - "symbol": "pzm", - "name": "PRIZM JPY" - }, - { - "id": "PZM-KRW", - "symbol": "pzm", - "name": "PRIZM KRW" - }, - { - "id": "PZM-RUB", - "symbol": "pzm", - "name": "PRIZM RUB" - }, - { - "id": "PZM-USD", - "symbol": "pzm", - "name": "PRIZM USD" - }, - { - "id": "QASH-CAD", - "symbol": "qash", - "name": "QASH CAD" - }, - { - "id": "QASH-CNY", - "symbol": "qash", - "name": "QASH CNY" - }, - { - "id": "QASH-ETH", - "symbol": "qash", - "name": "QASH ETH" - }, - { - "id": "QASH-EUR", - "symbol": "qash", - "name": "QASH EUR" - }, - { - "id": "QASH-GBP", - "symbol": "qash", - "name": "QASH GBP" - }, - { - "id": "QASH-INR", - "symbol": "qash", - "name": "QASH INR" - }, - { - "id": "QASH-JPY", - "symbol": "qash", - "name": "QASH JPY" - }, - { - "id": "QASH-KRW", - "symbol": "qash", - "name": "QASH KRW" - }, - { - "id": "QASH-RUB", - "symbol": "qash", - "name": "QASH RUB" - }, - { - "id": "QASH-USD", - "symbol": "qash", - "name": "QASH USD" - }, - { - "id": "QRK-CAD", - "symbol": "qrk", - "name": "Quark CAD" - }, - { - "id": "QRK-CNY", - "symbol": "qrk", - "name": "Quark CNY" - }, - { - "id": "QRK-ETH", - "symbol": "qrk", - "name": "Quark ETH" - }, - { - "id": "QRK-EUR", - "symbol": "qrk", - "name": "Quark EUR" - }, - { - "id": "QRK-GBP", - "symbol": "qrk", - "name": "Quark GBP" - }, - { - "id": "QRK-INR", - "symbol": "qrk", - "name": "Quark INR" - }, - { - "id": "QRK-JPY", - "symbol": "qrk", - "name": "Quark JPY" - }, - { - "id": "QRK-KRW", - "symbol": "qrk", - "name": "Quark KRW" - }, - { - "id": "QRK-RUB", - "symbol": "qrk", - "name": "Quark RUB" - }, - { - "id": "QRK-USD", - "symbol": "qrk", - "name": "Quark USD" - }, - { - "id": "QRL-AUD", - "symbol": "qrl", - "name": "QuantumResistantLedger AUD" - }, - { - "id": "QRL-BTC", - "symbol": "qrl", - "name": "QuantumResistantLedger BTC" - }, - { - "id": "QRL-CAD", - "symbol": "qrl", - "name": "QuantumResistantLedger CAD" - }, - { - "id": "QRL-CNY", - "symbol": "qrl", - "name": "QuantumResistantLedger CNY" - }, - { - "id": "QRL-ETH", - "symbol": "qrl", - "name": "QuantumResistantLedger ETH" - }, - { - "id": "QRL-EUR", - "symbol": "qrl", - "name": "QuantumResistantLedger EUR" - }, - { - "id": "QRL-GBP", - "symbol": "qrl", - "name": "QuantumResistantLedger GBP" - }, - { - "id": "QRL-INR", - "symbol": "qrl", - "name": "QuantumResistantLedger INR" - }, - { - "id": "QRL-KRW", - "symbol": "qrl", - "name": "QuantumResistantLedger KRW" - }, - { - "id": "QRL-RUB", - "symbol": "qrl", - "name": "QuantumResistantLedger RUB" - }, - { - "id": "QRL-USD", - "symbol": "qrl", - "name": "QuantumResistantLedger USD" - }, - { - "id": "QTUM-AUD", - "symbol": "qtum", - "name": "Qtum AUD" - }, - { - "id": "QTUM-INR", - "symbol": "qtum", - "name": "Qtum INR" - }, - { - "id": "QTUM-KRW", - "symbol": "qtum", - "name": "Qtum KRW" - }, - { - "id": "QTUM-RUB", - "symbol": "qtum", - "name": "Qtum RUB" - }, - { - "id": "QTUM-USD", - "symbol": "qtum", - "name": "Qtum USD" - }, - { - "id": "RBTC-BTC", - "symbol": "rbtc", - "name": "RSKSmartBitcoin BTC" - }, - { - "id": "RBTC-CAD", - "symbol": "rbtc", - "name": "RSKSmartBitcoin CAD" - }, - { - "id": "RBTC-CNY", - "symbol": "rbtc", - "name": "RSKSmartBitcoin CNY" - }, - { - "id": "RBTC-ETH", - "symbol": "rbtc", - "name": "RSKSmartBitcoin ETH" - }, - { - "id": "RBTC-EUR", - "symbol": "rbtc", - "name": "RSKSmartBitcoin EUR" - }, - { - "id": "RBTC-GBP", - "symbol": "rbtc", - "name": "RSKSmartBitcoin GBP" - }, - { - "id": "RBTC-INR", - "symbol": "rbtc", - "name": "RSKSmartBitcoin INR" - }, - { - "id": "RBTC-JPY", - "symbol": "rbtc", - "name": "RSKSmartBitcoin JPY" - }, - { - "id": "RBTC-KRW", - "symbol": "rbtc", - "name": "RSKSmartBitcoin KRW" - }, - { - "id": "RBTC-RUB", - "symbol": "rbtc", - "name": "RSKSmartBitcoin RUB" - }, - { - "id": "RBTC-USD", - "symbol": "rbtc", - "name": "RSKSmartBitcoin USD" - }, - { - "id": "RBY-CAD", - "symbol": "rby", - "name": "Rubycoin CAD" - }, - { - "id": "RBY-CNY", - "symbol": "rby", - "name": "Rubycoin CNY" - }, - { - "id": "RBY-ETH", - "symbol": "rby", - "name": "Rubycoin ETH" - }, - { - "id": "RBY-EUR", - "symbol": "rby", - "name": "Rubycoin EUR" - }, - { - "id": "RBY-GBP", - "symbol": "rby", - "name": "Rubycoin GBP" - }, - { - "id": "RBY-INR", - "symbol": "rby", - "name": "Rubycoin INR" - }, - { - "id": "RBY-JPY", - "symbol": "rby", - "name": "Rubycoin JPY" - }, - { - "id": "RBY-KRW", - "symbol": "rby", - "name": "Rubycoin KRW" - }, - { - "id": "RBY-RUB", - "symbol": "rby", - "name": "Rubycoin RUB" - }, - { - "id": "RBY-USD", - "symbol": "rby", - "name": "Rubycoin USD" - }, - { - "id": "RDD-AUD", - "symbol": "rdd", - "name": "ReddCoin AUD" - }, - { - "id": "RDD-CAD", - "symbol": "rdd", - "name": "ReddCoin CAD" - }, - { - "id": "RDD-CNY", - "symbol": "rdd", - "name": "ReddCoin CNY" - }, - { - "id": "RDD-ETH", - "symbol": "rdd", - "name": "ReddCoin ETH" - }, - { - "id": "RDD-EUR", - "symbol": "rdd", - "name": "ReddCoin EUR" - }, - { - "id": "RDD-GBP", - "symbol": "rdd", - "name": "ReddCoin GBP" - }, - { - "id": "RDD-INR", - "symbol": "rdd", - "name": "ReddCoin INR" - }, - { - "id": "RDD-JPY", - "symbol": "rdd", - "name": "ReddCoin JPY" - }, - { - "id": "RDD-KRW", - "symbol": "rdd", - "name": "ReddCoin KRW" - }, - { - "id": "RDD-RUB", - "symbol": "rdd", - "name": "ReddCoin RUB" - }, - { - "id": "RDD-USD", - "symbol": "rdd", - "name": "ReddCoin USD" - }, - { - "id": "REP-BTC", - "symbol": "rep", - "name": "Augur BTC" - }, - { - "id": "REP-CNY", - "symbol": "rep", - "name": "Augur CNY" - }, - { - "id": "REP-ETH", - "symbol": "rep", - "name": "Augur ETH" - }, - { - "id": "REP-EUR", - "symbol": "rep", - "name": "Augur EUR" - }, - { - "id": "REP-GBP", - "symbol": "rep", - "name": "Augur GBP" - }, - { - "id": "REP-INR", - "symbol": "rep", - "name": "Augur INR" - }, - { - "id": "REP-JPY", - "symbol": "rep", - "name": "Augur JPY" - }, - { - "id": "REP-KRW", - "symbol": "rep", - "name": "Augur KRW" - }, - { - "id": "REP-RUB", - "symbol": "rep", - "name": "Augur RUB" - }, - { - "id": "REP-USD", - "symbol": "rep", - "name": "Augur USD" - }, - { - "id": "REV-CAD", - "symbol": "rev", - "name": "RChain CAD" - }, - { - "id": "REV-CNY", - "symbol": "rev", - "name": "RChain CNY" - }, - { - "id": "REV-ETH", - "symbol": "rev", - "name": "RChain ETH" - }, - { - "id": "REV-EUR", - "symbol": "rev", - "name": "RChain EUR" - }, - { - "id": "REV-GBP", - "symbol": "rev", - "name": "RChain GBP" - }, - { - "id": "REV-INR", - "symbol": "rev", - "name": "RChain INR" - }, - { - "id": "REV-JPY", - "symbol": "rev", - "name": "RChain JPY" - }, - { - "id": "REV-KRW", - "symbol": "rev", - "name": "RChain KRW" - }, - { - "id": "REV-RUB", - "symbol": "rev", - "name": "RChain RUB" - }, - { - "id": "REV-USD", - "symbol": "rev", - "name": "RChain USD" - }, - { - "id": "RINGX-CAD", - "symbol": "ringx", - "name": "RINGXPLATFORM CAD" - }, - { - "id": "RINGX-CNY", - "symbol": "ringx", - "name": "RINGXPLATFORM CNY" - }, - { - "id": "RINGX-ETH", - "symbol": "ringx", - "name": "RINGXPLATFORM ETH" - }, - { - "id": "RINGX-EUR", - "symbol": "ringx", - "name": "RINGXPLATFORM EUR" - }, - { - "id": "RINGX-GBP", - "symbol": "ringx", - "name": "RINGXPLATFORM GBP" - }, - { - "id": "RINGX-INR", - "symbol": "ringx", - "name": "RINGXPLATFORM INR" - }, - { - "id": "RINGX-JPY", - "symbol": "ringx", - "name": "RINGXPLATFORM JPY" - }, - { - "id": "RINGX-KRW", - "symbol": "ringx", - "name": "RINGXPLATFORM KRW" - }, - { - "id": "RINGX-RUB", - "symbol": "ringx", - "name": "RINGXPLATFORM RUB" - }, - { - "id": "RINGX-USD", - "symbol": "ringx", - "name": "RINGXPLATFORM USD" - }, - { - "id": "RLC-BTC", - "symbol": "rlc", - "name": "iExecRLC BTC" - }, - { - "id": "RLC-CAD", - "symbol": "rlc", - "name": "iExecRLC CAD" - }, - { - "id": "RLC-ETH", - "symbol": "rlc", - "name": "iExecRLC ETH" - }, - { - "id": "RLC-EUR", - "symbol": "rlc", - "name": "iExecRLC EUR" - }, - { - "id": "RLC-GBP", - "symbol": "rlc", - "name": "iExecRLC GBP" - }, - { - "id": "RLC-INR", - "symbol": "rlc", - "name": "iExecRLC INR" - }, - { - "id": "RLC-JPY", - "symbol": "rlc", - "name": "iExecRLC JPY" - }, - { - "id": "RLC-KRW", - "symbol": "rlc", - "name": "iExecRLC KRW" - }, - { - "id": "RLC-RUB", - "symbol": "rlc", - "name": "iExecRLC RUB" - }, - { - "id": "RLC-USD", - "symbol": "rlc", - "name": "iExecRLC USD" - }, - { - "id": "RSTR-CAD", - "symbol": "rstr", - "name": "Ondori CAD" - }, - { - "id": "RSTR-CNY", - "symbol": "rstr", - "name": "Ondori CNY" - }, - { - "id": "RSTR-ETH", - "symbol": "rstr", - "name": "Ondori ETH" - }, - { - "id": "RSTR-EUR", - "symbol": "rstr", - "name": "Ondori EUR" - }, - { - "id": "RSTR-GBP", - "symbol": "rstr", - "name": "Ondori GBP" - }, - { - "id": "RSTR-INR", - "symbol": "rstr", - "name": "Ondori INR" - }, - { - "id": "RSTR-JPY", - "symbol": "rstr", - "name": "Ondori JPY" - }, - { - "id": "RSTR-KRW", - "symbol": "rstr", - "name": "Ondori KRW" - }, - { - "id": "RSTR-RUB", - "symbol": "rstr", - "name": "Ondori RUB" - }, - { - "id": "RSTR-USD", - "symbol": "rstr", - "name": "Ondori USD" - }, - { - "id": "RVN-CAD", - "symbol": "rvn", - "name": "Ravencoin CAD" - }, - { - "id": "RVN-CNY", - "symbol": "rvn", - "name": "Ravencoin CNY" - }, - { - "id": "RVN-ETH", - "symbol": "rvn", - "name": "Ravencoin ETH" - }, - { - "id": "RVN-EUR", - "symbol": "rvn", - "name": "Ravencoin EUR" - }, - { - "id": "RVN-GBP", - "symbol": "rvn", - "name": "Ravencoin GBP" - }, - { - "id": "RVN-INR", - "symbol": "rvn", - "name": "Ravencoin INR" - }, - { - "id": "RVN-JPY", - "symbol": "rvn", - "name": "Ravencoin JPY" - }, - { - "id": "RVN-KRW", - "symbol": "rvn", - "name": "Ravencoin KRW" - }, - { - "id": "RVN-RUB", - "symbol": "rvn", - "name": "Ravencoin RUB" - }, - { - "id": "RVN-USD", - "symbol": "rvn", - "name": "Ravencoin USD" - }, - { - "id": "SALT-USD", - "symbol": "salt", - "name": "SALT USD" - }, - { - "id": "SAPP-USD", - "symbol": "sapp", - "name": "Sapphire USD" - }, - { - "id": "SBD-BTC", - "symbol": "sbd", - "name": "SteemDollars BTC" - }, - { - "id": "SBD-CAD", - "symbol": "sbd", - "name": "SteemDollars CAD" - }, - { - "id": "SBD-CNY", - "symbol": "sbd", - "name": "SteemDollars CNY" - }, - { - "id": "SBD-ETH", - "symbol": "sbd", - "name": "SteemDollars ETH" - }, - { - "id": "SBD-EUR", - "symbol": "sbd", - "name": "SteemDollars EUR" - }, - { - "id": "SBD-GBP", - "symbol": "sbd", - "name": "SteemDollars GBP" - }, - { - "id": "SBD-INR", - "symbol": "sbd", - "name": "SteemDollars INR" - }, - { - "id": "SBD-JPY", - "symbol": "sbd", - "name": "SteemDollars JPY" - }, - { - "id": "SBD-KRW", - "symbol": "sbd", - "name": "SteemDollars KRW" - }, - { - "id": "SBD-RUB", - "symbol": "sbd", - "name": "SteemDollars RUB" - }, - { - "id": "SBD-USD", - "symbol": "sbd", - "name": "SteemDollars USD" - }, - { - "id": "SC-BTC", - "symbol": "sc", - "name": "Siacoin BTC" - }, - { - "id": "SC-CAD", - "symbol": "sc", - "name": "Siacoin CAD" - }, - { - "id": "SC-CNY", - "symbol": "sc", - "name": "Siacoin CNY" - }, - { - "id": "SC-ETH", - "symbol": "sc", - "name": "Siacoin ETH" - }, - { - "id": "SC-EUR", - "symbol": "sc", - "name": "Siacoin EUR" - }, - { - "id": "SC-GBP", - "symbol": "sc", - "name": "Siacoin GBP" - }, - { - "id": "SC-INR", - "symbol": "sc", - "name": "Siacoin INR" - }, - { - "id": "SC-JPY", - "symbol": "sc", - "name": "Siacoin JPY" - }, - { - "id": "SC-RUB", - "symbol": "sc", - "name": "Siacoin RUB" - }, - { - "id": "SC-USD", - "symbol": "sc", - "name": "Siacoin USD" - }, - { - "id": "SCC3-CAD", - "symbol": "scc3", - "name": "StakeCubeCoin CAD" - }, - { - "id": "SCC3-CNY", - "symbol": "scc3", - "name": "StakeCubeCoin CNY" - }, - { - "id": "SCC3-ETH", - "symbol": "scc3", - "name": "StakeCubeCoin ETH" - }, - { - "id": "SCC3-EUR", - "symbol": "scc3", - "name": "StakeCubeCoin EUR" - }, - { - "id": "SCC3-GBP", - "symbol": "scc3", - "name": "StakeCubeCoin GBP" - }, - { - "id": "SCC3-INR", - "symbol": "scc3", - "name": "StakeCubeCoin INR" - }, - { - "id": "SCC3-JPY", - "symbol": "scc3", - "name": "StakeCubeCoin JPY" - }, - { - "id": "SCC3-KRW", - "symbol": "scc3", - "name": "StakeCubeCoin KRW" - }, - { - "id": "SCC3-RUB", - "symbol": "scc3", - "name": "StakeCubeCoin RUB" - }, - { - "id": "SCC3-USD", - "symbol": "scc3", - "name": "StakeCubeCoin USD" - }, - { - "id": "SCP-CAD", - "symbol": "scp", - "name": "ScPrime CAD" - }, - { - "id": "SCP-CNY", - "symbol": "scp", - "name": "ScPrime CNY" - }, - { - "id": "SCP-ETH", - "symbol": "scp", - "name": "ScPrime ETH" - }, - { - "id": "SCP-EUR", - "symbol": "scp", - "name": "ScPrime EUR" - }, - { - "id": "SCP-GBP", - "symbol": "scp", - "name": "ScPrime GBP" - }, - { - "id": "SCP-INR", - "symbol": "scp", - "name": "ScPrime INR" - }, - { - "id": "SCP-JPY", - "symbol": "scp", - "name": "ScPrime JPY" - }, - { - "id": "SCP-KRW", - "symbol": "scp", - "name": "ScPrime KRW" - }, - { - "id": "SCP-RUB", - "symbol": "scp", - "name": "ScPrime RUB" - }, - { - "id": "SCP-USD", - "symbol": "scp", - "name": "ScPrime USD" - }, - { - "id": "SERO-CAD", - "symbol": "sero", - "name": "SuperZero CAD" - }, - { - "id": "SERO-CNY", - "symbol": "sero", - "name": "SuperZero CNY" - }, - { - "id": "SERO-ETH", - "symbol": "sero", - "name": "SuperZero ETH" - }, - { - "id": "SERO-EUR", - "symbol": "sero", - "name": "SuperZero EUR" - }, - { - "id": "SERO-GBP", - "symbol": "sero", - "name": "SuperZero GBP" - }, - { - "id": "SERO-INR", - "symbol": "sero", - "name": "SuperZero INR" - }, - { - "id": "SERO-JPY", - "symbol": "sero", - "name": "SuperZero JPY" - }, - { - "id": "SERO-KRW", - "symbol": "sero", - "name": "SuperZero KRW" - }, - { - "id": "SERO-RUB", - "symbol": "sero", - "name": "SuperZero RUB" - }, - { - "id": "SERO-USD", - "symbol": "sero", - "name": "SuperZero USD" - }, - { - "id": "SFT-CAD", - "symbol": "sft", - "name": "SafexToken CAD" - }, - { - "id": "SFT-CNY", - "symbol": "sft", - "name": "SafexToken CNY" - }, - { - "id": "SFT-ETH", - "symbol": "sft", - "name": "SafexToken ETH" - }, - { - "id": "SFT-EUR", - "symbol": "sft", - "name": "SafexToken EUR" - }, - { - "id": "SFT-GBP", - "symbol": "sft", - "name": "SafexToken GBP" - }, - { - "id": "SFT-INR", - "symbol": "sft", - "name": "SafexToken INR" - }, - { - "id": "SFT-JPY", - "symbol": "sft", - "name": "SafexToken JPY" - }, - { - "id": "SFT-KRW", - "symbol": "sft", - "name": "SafexToken KRW" - }, - { - "id": "SFT-RUB", - "symbol": "sft", - "name": "SafexToken RUB" - }, - { - "id": "SFT-USD", - "symbol": "sft", - "name": "SafexToken USD" - }, - { - "id": "SKY-CAD", - "symbol": "sky", - "name": "Skycoin CAD" - }, - { - "id": "SKY-CNY", - "symbol": "sky", - "name": "Skycoin CNY" - }, - { - "id": "SKY-ETH", - "symbol": "sky", - "name": "Skycoin ETH" - }, - { - "id": "SKY-EUR", - "symbol": "sky", - "name": "Skycoin EUR" - }, - { - "id": "SKY-GBP", - "symbol": "sky", - "name": "Skycoin GBP" - }, - { - "id": "SKY-INR", - "symbol": "sky", - "name": "Skycoin INR" - }, - { - "id": "SKY-JPY", - "symbol": "sky", - "name": "Skycoin JPY" - }, - { - "id": "SKY-KRW", - "symbol": "sky", - "name": "Skycoin KRW" - }, - { - "id": "SKY-RUB", - "symbol": "sky", - "name": "Skycoin RUB" - }, - { - "id": "SKY-USD", - "symbol": "sky", - "name": "Skycoin USD" - }, - { - "id": "SLS-CAD", - "symbol": "sls", - "name": "SaluS CAD" - }, - { - "id": "SLS-CNY", - "symbol": "sls", - "name": "SaluS CNY" - }, - { - "id": "SLS-ETH", - "symbol": "sls", - "name": "SaluS ETH" - }, - { - "id": "SLS-EUR", - "symbol": "sls", - "name": "SaluS EUR" - }, - { - "id": "SLS-GBP", - "symbol": "sls", - "name": "SaluS GBP" - }, - { - "id": "SLS-INR", - "symbol": "sls", - "name": "SaluS INR" - }, - { - "id": "SLS-JPY", - "symbol": "sls", - "name": "SaluS JPY" - }, - { - "id": "SLS-KRW", - "symbol": "sls", - "name": "SaluS KRW" - }, - { - "id": "SLS-RUB", - "symbol": "sls", - "name": "SaluS RUB" - }, - { - "id": "SLS-USD", - "symbol": "sls", - "name": "SaluS USD" - }, - { - "id": "SMART-BTC", - "symbol": "smart", - "name": "SmartCash BTC" - }, - { - "id": "SMART-CNY", - "symbol": "smart", - "name": "SmartCash CNY" - }, - { - "id": "SMART-ETH", - "symbol": "smart", - "name": "SmartCash ETH" - }, - { - "id": "SMART-EUR", - "symbol": "smart", - "name": "SmartCash EUR" - }, - { - "id": "SMART-GBP", - "symbol": "smart", - "name": "SmartCash GBP" - }, - { - "id": "SMART-INR", - "symbol": "smart", - "name": "SmartCash INR" - }, - { - "id": "SMART-JPY", - "symbol": "smart", - "name": "SmartCash JPY" - }, - { - "id": "SMART-KRW", - "symbol": "smart", - "name": "SmartCash KRW" - }, - { - "id": "SMART-RUB", - "symbol": "smart", - "name": "SmartCash RUB" - }, - { - "id": "SMART-USD", - "symbol": "smart", - "name": "SmartCash USD" - }, - { - "id": "SNGLS-AUD", - "symbol": "sngls", - "name": "SingularDTV AUD" - }, - { - "id": "SNGLS-BTC", - "symbol": "sngls", - "name": "SingularDTV BTC" - }, - { - "id": "SNGLS-CAD", - "symbol": "sngls", - "name": "SingularDTV CAD" - }, - { - "id": "SNGLS-ETH", - "symbol": "sngls", - "name": "SingularDTV ETH" - }, - { - "id": "SNGLS-EUR", - "symbol": "sngls", - "name": "SingularDTV EUR" - }, - { - "id": "SNGLS-INR", - "symbol": "sngls", - "name": "SingularDTV INR" - }, - { - "id": "SNGLS-JPY", - "symbol": "sngls", - "name": "SingularDTV JPY" - }, - { - "id": "SNGLS-KRW", - "symbol": "sngls", - "name": "SingularDTV KRW" - }, - { - "id": "SNGLS-RUB", - "symbol": "sngls", - "name": "SingularDTV RUB" - }, - { - "id": "SNGLS-USD", - "symbol": "sngls", - "name": "SingularDTV USD" - }, - { - "id": "SNM-ETH", - "symbol": "snm", - "name": "SONM ETH" - }, - { - "id": "SNM-EUR", - "symbol": "snm", - "name": "SONM EUR" - }, - { - "id": "SNM-GBP", - "symbol": "snm", - "name": "SONM GBP" - }, - { - "id": "SNM-INR", - "symbol": "snm", - "name": "SONM INR" - }, - { - "id": "SNM-JPY", - "symbol": "snm", - "name": "SONM JPY" - }, - { - "id": "SNM-KRW", - "symbol": "snm", - "name": "SONM KRW" - }, - { - "id": "SNM-USD", - "symbol": "snm", - "name": "SONM USD" - }, - { - "id": "SNT-CNY", - "symbol": "snt", - "name": "Status CNY" - }, - { - "id": "SNT-ETH", - "symbol": "snt", - "name": "Status ETH" - }, - { - "id": "SNT-EUR", - "symbol": "snt", - "name": "Status EUR" - }, - { - "id": "SNT-GBP", - "symbol": "snt", - "name": "Status GBP" - }, - { - "id": "SNT-INR", - "symbol": "snt", - "name": "Status INR" - }, - { - "id": "SNT-JPY", - "symbol": "snt", - "name": "Status JPY" - }, - { - "id": "SNT-KRW", - "symbol": "snt", - "name": "Status KRW" - }, - { - "id": "SNT-RUB", - "symbol": "snt", - "name": "Status RUB" - }, - { - "id": "SNT-USD", - "symbol": "snt", - "name": "Status USD" - }, - { - "id": "SNX-AUD", - "symbol": "snx", - "name": "SynthetixNetworkToken AUD" - }, - { - "id": "SNX-BTC", - "symbol": "snx", - "name": "SynthetixNetworkToken BTC" - }, - { - "id": "SNX-CAD", - "symbol": "snx", - "name": "SynthetixNetworkToken CAD" - }, - { - "id": "SNX-CNY", - "symbol": "snx", - "name": "SynthetixNetworkToken CNY" - }, - { - "id": "SNX-ETH", - "symbol": "snx", - "name": "SynthetixNetworkToken ETH" - }, - { - "id": "SNX-EUR", - "symbol": "snx", - "name": "SynthetixNetworkToken EUR" - }, - { - "id": "SNX-GBP", - "symbol": "snx", - "name": "SynthetixNetworkToken GBP" - }, - { - "id": "SNX-INR", - "symbol": "snx", - "name": "SynthetixNetworkToken INR" - }, - { - "id": "SNX-JPY", - "symbol": "snx", - "name": "SynthetixNetworkToken JPY" - }, - { - "id": "SNX-KRW", - "symbol": "snx", - "name": "SynthetixNetworkToken KRW" - }, - { - "id": "SNX-RUB", - "symbol": "snx", - "name": "SynthetixNetworkToken RUB" - }, - { - "id": "SNX-USD", - "symbol": "snx", - "name": "SynthetixNetworkToken USD" - }, - { - "id": "SOL1-CAD", - "symbol": "sol1", - "name": "Solana CAD" - }, - { - "id": "SOL1-CNY", - "symbol": "sol1", - "name": "Solana CNY" - }, - { - "id": "SOL1-ETH", - "symbol": "sol1", - "name": "Solana ETH" - }, - { - "id": "SOL1-EUR", - "symbol": "sol1", - "name": "Solana EUR" - }, - { - "id": "SOL1-GBP", - "symbol": "sol1", - "name": "Solana GBP" - }, - { - "id": "SOL1-INR", - "symbol": "sol1", - "name": "Solana INR" - }, - { - "id": "SOL1-JPY", - "symbol": "sol1", - "name": "Solana JPY" - }, - { - "id": "SOL1-KRW", - "symbol": "sol1", - "name": "Solana KRW" - }, - { - "id": "SOL1-RUB", - "symbol": "sol1", - "name": "Solana RUB" - }, - { - "id": "SOL1-USD", - "symbol": "sol1", - "name": "Solana USD" - }, - { - "id": "SONO1-CAD", - "symbol": "sono1", - "name": "SonoCoin CAD" - }, - { - "id": "SONO1-CNY", - "symbol": "sono1", - "name": "SonoCoin CNY" - }, - { - "id": "SONO1-ETH", - "symbol": "sono1", - "name": "SonoCoin ETH" - }, - { - "id": "SONO1-EUR", - "symbol": "sono1", - "name": "SonoCoin EUR" - }, - { - "id": "SONO1-GBP", - "symbol": "sono1", - "name": "SonoCoin GBP" - }, - { - "id": "SONO1-INR", - "symbol": "sono1", - "name": "SonoCoin INR" - }, - { - "id": "SONO1-JPY", - "symbol": "sono1", - "name": "SonoCoin JPY" - }, - { - "id": "SONO1-KRW", - "symbol": "sono1", - "name": "SonoCoin KRW" - }, - { - "id": "SONO1-RUB", - "symbol": "sono1", - "name": "SonoCoin RUB" - }, - { - "id": "SONO1-USD", - "symbol": "sono1", - "name": "SonoCoin USD" - }, - { - "id": "SPHR-ETH", - "symbol": "sphr", - "name": "Sphere ETH" - }, - { - "id": "SPHR-EUR", - "symbol": "sphr", - "name": "Sphere EUR" - }, - { - "id": "SPHR-GBP", - "symbol": "sphr", - "name": "Sphere GBP" - }, - { - "id": "SPHR-INR", - "symbol": "sphr", - "name": "Sphere INR" - }, - { - "id": "SPHR-JPY", - "symbol": "sphr", - "name": "Sphere JPY" - }, - { - "id": "SPHR-KRW", - "symbol": "sphr", - "name": "Sphere KRW" - }, - { - "id": "SPHR-RUB", - "symbol": "sphr", - "name": "Sphere RUB" - }, - { - "id": "SPHR-USD", - "symbol": "sphr", - "name": "Sphere USD" - }, - { - "id": "SRK-CAD", - "symbol": "srk", - "name": "Sparkpoint CAD" - }, - { - "id": "SRK-CNY", - "symbol": "srk", - "name": "Sparkpoint CNY" - }, - { - "id": "SRK-ETH", - "symbol": "srk", - "name": "Sparkpoint ETH" - }, - { - "id": "SRK-EUR", - "symbol": "srk", - "name": "Sparkpoint EUR" - }, - { - "id": "SRK-GBP", - "symbol": "srk", - "name": "Sparkpoint GBP" - }, - { - "id": "SRK-INR", - "symbol": "srk", - "name": "Sparkpoint INR" - }, - { - "id": "SRK-JPY", - "symbol": "srk", - "name": "Sparkpoint JPY" - }, - { - "id": "SRK-KRW", - "symbol": "srk", - "name": "Sparkpoint KRW" - }, - { - "id": "SRK-RUB", - "symbol": "srk", - "name": "Sparkpoint RUB" - }, - { - "id": "SRK-USD", - "symbol": "srk", - "name": "Sparkpoint USD" - }, - { - "id": "SRM-CNY", - "symbol": "srm", - "name": "Serum CNY" - }, - { - "id": "SRM-ETH", - "symbol": "srm", - "name": "Serum ETH" - }, - { - "id": "SRM-EUR", - "symbol": "srm", - "name": "Serum EUR" - }, - { - "id": "SRM-GBP", - "symbol": "srm", - "name": "Serum GBP" - }, - { - "id": "SRM-INR", - "symbol": "srm", - "name": "Serum INR" - }, - { - "id": "SRM-JPY", - "symbol": "srm", - "name": "Serum JPY" - }, - { - "id": "SRM-KRW", - "symbol": "srm", - "name": "Serum KRW" - }, - { - "id": "SRM-RUB", - "symbol": "srm", - "name": "Serum RUB" - }, - { - "id": "SRM-USD", - "symbol": "srm", - "name": "Serum USD" - }, - { - "id": "STEEM-AUD", - "symbol": "steem", - "name": "Steem AUD" - }, - { - "id": "STEEM-BTC", - "symbol": "steem", - "name": "Steem BTC" - }, - { - "id": "STEEM-CNY", - "symbol": "steem", - "name": "Steem CNY" - }, - { - "id": "STEEM-ETH", - "symbol": "steem", - "name": "Steem ETH" - }, - { - "id": "STEEM-EUR", - "symbol": "steem", - "name": "Steem EUR" - }, - { - "id": "STEEM-GBP", - "symbol": "steem", - "name": "Steem GBP" - }, - { - "id": "STEEM-INR", - "symbol": "steem", - "name": "Steem INR" - }, - { - "id": "STEEM-JPY", - "symbol": "steem", - "name": "Steem JPY" - }, - { - "id": "STEEM-KRW", - "symbol": "steem", - "name": "Steem KRW" - }, - { - "id": "STEEM-USD", - "symbol": "steem", - "name": "Steem USD" - }, - { - "id": "STORJ-AUD", - "symbol": "storj", - "name": "Storj AUD" - }, - { - "id": "STORJ-CAD", - "symbol": "storj", - "name": "Storj CAD" - }, - { - "id": "STORJ-CNY", - "symbol": "storj", - "name": "Storj CNY" - }, - { - "id": "STORJ-ETH", - "symbol": "storj", - "name": "Storj ETH" - }, - { - "id": "STORJ-EUR", - "symbol": "storj", - "name": "Storj EUR" - }, - { - "id": "STORJ-GBP", - "symbol": "storj", - "name": "Storj GBP" - }, - { - "id": "STORJ-INR", - "symbol": "storj", - "name": "Storj INR" - }, - { - "id": "STORJ-JPY", - "symbol": "storj", - "name": "Storj JPY" - }, - { - "id": "STORJ-KRW", - "symbol": "storj", - "name": "Storj KRW" - }, - { - "id": "STORJ-USD", - "symbol": "storj", - "name": "Storj USD" - }, - { - "id": "STRAX-CAD", - "symbol": "strax", - "name": "Stratis CAD" - }, - { - "id": "STRAX-CNY", - "symbol": "strax", - "name": "Stratis CNY" - }, - { - "id": "STRAX-ETH", - "symbol": "strax", - "name": "Stratis ETH" - }, - { - "id": "STRAX-EUR", - "symbol": "strax", - "name": "Stratis EUR" - }, - { - "id": "STRAX-GBP", - "symbol": "strax", - "name": "Stratis GBP" - }, - { - "id": "STRAX-INR", - "symbol": "strax", - "name": "Stratis INR" - }, - { - "id": "STRAX-JPY", - "symbol": "strax", - "name": "Stratis JPY" - }, - { - "id": "STRAX-KRW", - "symbol": "strax", - "name": "Stratis KRW" - }, - { - "id": "STRAX-RUB", - "symbol": "strax", - "name": "Stratis RUB" - }, - { - "id": "STRAX-USD", - "symbol": "strax", - "name": "Stratis USD" - }, - { - "id": "STX1-CAD", - "symbol": "stx1", - "name": "Stacks CAD" - }, - { - "id": "STX1-CNY", - "symbol": "stx1", - "name": "Stacks CNY" - }, - { - "id": "STX1-ETH", - "symbol": "stx1", - "name": "Stacks ETH" - }, - { - "id": "STX1-EUR", - "symbol": "stx1", - "name": "Stacks EUR" - }, - { - "id": "STX1-GBP", - "symbol": "stx1", - "name": "Stacks GBP" - }, - { - "id": "STX1-INR", - "symbol": "stx1", - "name": "Stacks INR" - }, - { - "id": "STX1-JPY", - "symbol": "stx1", - "name": "Stacks JPY" - }, - { - "id": "STX1-KRW", - "symbol": "stx1", - "name": "Stacks KRW" - }, - { - "id": "STX1-RUB", - "symbol": "stx1", - "name": "Stacks RUB" - }, - { - "id": "STX1-USD", - "symbol": "stx1", - "name": "Stacks USD" - }, - { - "id": "SUB-AUD", - "symbol": "sub", - "name": "Substratum AUD" - }, - { - "id": "SUB-BTC", - "symbol": "sub", - "name": "Substratum BTC" - }, - { - "id": "SUB-CAD", - "symbol": "sub", - "name": "Substratum CAD" - }, - { - "id": "SUB-ETH", - "symbol": "sub", - "name": "Substratum ETH" - }, - { - "id": "SUB-EUR", - "symbol": "sub", - "name": "Substratum EUR" - }, - { - "id": "SUB-GBP", - "symbol": "sub", - "name": "Substratum GBP" - }, - { - "id": "SUB-INR", - "symbol": "sub", - "name": "Substratum INR" - }, - { - "id": "SUB-JPY", - "symbol": "sub", - "name": "Substratum JPY" - }, - { - "id": "SUB-RUB", - "symbol": "sub", - "name": "Substratum RUB" - }, - { - "id": "SUB-USD", - "symbol": "sub", - "name": "Substratum USD" - }, - { - "id": "SUSHI-JPY", - "symbol": "sushi", - "name": "Sushi JPY" - }, - { - "id": "SUSHI-KRW", - "symbol": "sushi", - "name": "Sushi KRW" - }, - { - "id": "SUSHI-RUB", - "symbol": "sushi", - "name": "Sushi RUB" - }, - { - "id": "SUSHI-USD", - "symbol": "sushi", - "name": "Sushi USD" - }, - { - "id": "SYS-BTC", - "symbol": "sys", - "name": "Syscoin BTC" - }, - { - "id": "SYS-CAD", - "symbol": "sys", - "name": "Syscoin CAD" - }, - { - "id": "SYS-CNY", - "symbol": "sys", - "name": "Syscoin CNY" - }, - { - "id": "SYS-ETH", - "symbol": "sys", - "name": "Syscoin ETH" - }, - { - "id": "SYS-EUR", - "symbol": "sys", - "name": "Syscoin EUR" - }, - { - "id": "SYS-GBP", - "symbol": "sys", - "name": "Syscoin GBP" - }, - { - "id": "SYS-INR", - "symbol": "sys", - "name": "Syscoin INR" - }, - { - "id": "SYS-KRW", - "symbol": "sys", - "name": "Syscoin KRW" - }, - { - "id": "SYS-RUB", - "symbol": "sys", - "name": "Syscoin RUB" - }, - { - "id": "SYS-USD", - "symbol": "sys", - "name": "Syscoin USD" - }, - { - "id": "TAAS-BTC", - "symbol": "taas", - "name": "TaaS BTC" - }, - { - "id": "TAAS-CAD", - "symbol": "taas", - "name": "TaaS CAD" - }, - { - "id": "TAAS-CNY", - "symbol": "taas", - "name": "TaaS CNY" - }, - { - "id": "TAAS-ETH", - "symbol": "taas", - "name": "TaaS ETH" - }, - { - "id": "TAAS-EUR", - "symbol": "taas", - "name": "TaaS EUR" - }, - { - "id": "TAAS-GBP", - "symbol": "taas", - "name": "TaaS GBP" - }, - { - "id": "TAAS-INR", - "symbol": "taas", - "name": "TaaS INR" - }, - { - "id": "TAAS-KRW", - "symbol": "taas", - "name": "TaaS KRW" - }, - { - "id": "TAAS-RUB", - "symbol": "taas", - "name": "TaaS RUB" - }, - { - "id": "TFUEL-CAD", - "symbol": "tfuel", - "name": "ThetaFuel CAD" - }, - { - "id": "TFUEL-CNY", - "symbol": "tfuel", - "name": "ThetaFuel CNY" - }, - { - "id": "TFUEL-ETH", - "symbol": "tfuel", - "name": "ThetaFuel ETH" - }, - { - "id": "TFUEL-EUR", - "symbol": "tfuel", - "name": "ThetaFuel EUR" - }, - { - "id": "TFUEL-GBP", - "symbol": "tfuel", - "name": "ThetaFuel GBP" - }, - { - "id": "TFUEL-INR", - "symbol": "tfuel", - "name": "ThetaFuel INR" - }, - { - "id": "TFUEL-JPY", - "symbol": "tfuel", - "name": "ThetaFuel JPY" - }, - { - "id": "TFUEL-KRW", - "symbol": "tfuel", - "name": "ThetaFuel KRW" - }, - { - "id": "TFUEL-RUB", - "symbol": "tfuel", - "name": "ThetaFuel RUB" - }, - { - "id": "TFUEL-USD", - "symbol": "tfuel", - "name": "ThetaFuel USD" - }, - { - "id": "TOMO-CAD", - "symbol": "tomo", - "name": "TomoChain CAD" - }, - { - "id": "TOMO-CNY", - "symbol": "tomo", - "name": "TomoChain CNY" - }, - { - "id": "TOMO-ETH", - "symbol": "tomo", - "name": "TomoChain ETH" - }, - { - "id": "TOMO-EUR", - "symbol": "tomo", - "name": "TomoChain EUR" - }, - { - "id": "TOMO-GBP", - "symbol": "tomo", - "name": "TomoChain GBP" - }, - { - "id": "TOMO-INR", - "symbol": "tomo", - "name": "TomoChain INR" - }, - { - "id": "TOMO-JPY", - "symbol": "tomo", - "name": "TomoChain JPY" - }, - { - "id": "TOMO-KRW", - "symbol": "tomo", - "name": "TomoChain KRW" - }, - { - "id": "TOMO-RUB", - "symbol": "tomo", - "name": "TomoChain RUB" - }, - { - "id": "TOMO-USD", - "symbol": "tomo", - "name": "TomoChain USD" - }, - { - "id": "TRUE-CAD", - "symbol": "true", - "name": "TrueChain CAD" - }, - { - "id": "TRUE-CNY", - "symbol": "true", - "name": "TrueChain CNY" - }, - { - "id": "TRUE-ETH", - "symbol": "true", - "name": "TrueChain ETH" - }, - { - "id": "TRUE-EUR", - "symbol": "true", - "name": "TrueChain EUR" - }, - { - "id": "TRUE-GBP", - "symbol": "true", - "name": "TrueChain GBP" - }, - { - "id": "TRUE-INR", - "symbol": "true", - "name": "TrueChain INR" - }, - { - "id": "TRUE-JPY", - "symbol": "true", - "name": "TrueChain JPY" - }, - { - "id": "TRUE-KRW", - "symbol": "true", - "name": "TrueChain KRW" - }, - { - "id": "TRUE-RUB", - "symbol": "true", - "name": "TrueChain RUB" - }, - { - "id": "TRUE-USD", - "symbol": "true", - "name": "TrueChain USD" - }, - { - "id": "TRX-ETH", - "symbol": "trx", - "name": "TRON ETH" - }, - { - "id": "TRX-USD", - "symbol": "trx", - "name": "TRON USD" - }, - { - "id": "TT-CAD", - "symbol": "tt", - "name": "ThunderToken CAD" - }, - { - "id": "TT-CNY", - "symbol": "tt", - "name": "ThunderToken CNY" - }, - { - "id": "TT-ETH", - "symbol": "tt", - "name": "ThunderToken ETH" - }, - { - "id": "TT-EUR", - "symbol": "tt", - "name": "ThunderToken EUR" - }, - { - "id": "TT-GBP", - "symbol": "tt", - "name": "ThunderToken GBP" - }, - { - "id": "TT-INR", - "symbol": "tt", - "name": "ThunderToken INR" - }, - { - "id": "TT-JPY", - "symbol": "tt", - "name": "ThunderToken JPY" - }, - { - "id": "TT-KRW", - "symbol": "tt", - "name": "ThunderToken KRW" - }, - { - "id": "TT-RUB", - "symbol": "tt", - "name": "ThunderToken RUB" - }, - { - "id": "TT-USD", - "symbol": "tt", - "name": "ThunderToken USD" - }, - { - "id": "TUBE-CAD", - "symbol": "tube", - "name": "BitTube CAD" - }, - { - "id": "TUBE-CNY", - "symbol": "tube", - "name": "BitTube CNY" - }, - { - "id": "TUBE-ETH", - "symbol": "tube", - "name": "BitTube ETH" - }, - { - "id": "TUBE-EUR", - "symbol": "tube", - "name": "BitTube EUR" - }, - { - "id": "TUBE-GBP", - "symbol": "tube", - "name": "BitTube GBP" - }, - { - "id": "TUBE-INR", - "symbol": "tube", - "name": "BitTube INR" - }, - { - "id": "TUBE-JPY", - "symbol": "tube", - "name": "BitTube JPY" - }, - { - "id": "TUBE-KRW", - "symbol": "tube", - "name": "BitTube KRW" - }, - { - "id": "TUBE-RUB", - "symbol": "tube", - "name": "BitTube RUB" - }, - { - "id": "TUBE-USD", - "symbol": "tube", - "name": "BitTube USD" - }, - { - "id": "TUSD-CAD", - "symbol": "tusd", - "name": "TrueUSD CAD" - }, - { - "id": "TUSD-CNY", - "symbol": "tusd", - "name": "TrueUSD CNY" - }, - { - "id": "TUSD-ETH", - "symbol": "tusd", - "name": "TrueUSD ETH" - }, - { - "id": "TUSD-EUR", - "symbol": "tusd", - "name": "TrueUSD EUR" - }, - { - "id": "TUSD-GBP", - "symbol": "tusd", - "name": "TrueUSD GBP" - }, - { - "id": "TUSD-INR", - "symbol": "tusd", - "name": "TrueUSD INR" - }, - { - "id": "TUSD-JPY", - "symbol": "tusd", - "name": "TrueUSD JPY" - }, - { - "id": "TUSD-KRW", - "symbol": "tusd", - "name": "TrueUSD KRW" - }, - { - "id": "TUSD-RUB", - "symbol": "tusd", - "name": "TrueUSD RUB" - }, - { - "id": "TUSD-USD", - "symbol": "tusd", - "name": "TrueUSD USD" - }, - { - "id": "TWT-CAD", - "symbol": "twt", - "name": "TrustWalletToken CAD" - }, - { - "id": "TWT-CNY", - "symbol": "twt", - "name": "TrustWalletToken CNY" - }, - { - "id": "TWT-ETH", - "symbol": "twt", - "name": "TrustWalletToken ETH" - }, - { - "id": "TWT-EUR", - "symbol": "twt", - "name": "TrustWalletToken EUR" - }, - { - "id": "TWT-GBP", - "symbol": "twt", - "name": "TrustWalletToken GBP" - }, - { - "id": "TWT-INR", - "symbol": "twt", - "name": "TrustWalletToken INR" - }, - { - "id": "TWT-JPY", - "symbol": "twt", - "name": "TrustWalletToken JPY" - }, - { - "id": "TWT-KRW", - "symbol": "twt", - "name": "TrustWalletToken KRW" - }, - { - "id": "TWT-RUB", - "symbol": "twt", - "name": "TrustWalletToken RUB" - }, - { - "id": "TWT-USD", - "symbol": "twt", - "name": "TrustWalletToken USD" - }, - { - "id": "UNI3-CAD", - "symbol": "uni3", - "name": "Uniswap CAD" - }, - { - "id": "UNI3-CNY", - "symbol": "uni3", - "name": "Uniswap CNY" - }, - { - "id": "UNI3-ETH", - "symbol": "uni3", - "name": "Uniswap ETH" - }, - { - "id": "UNI3-EUR", - "symbol": "uni3", - "name": "Uniswap EUR" - }, - { - "id": "UNI3-GBP", - "symbol": "uni3", - "name": "Uniswap GBP" - }, - { - "id": "UNI3-INR", - "symbol": "uni3", - "name": "Uniswap INR" - }, - { - "id": "UNI3-JPY", - "symbol": "uni3", - "name": "Uniswap JPY" - }, - { - "id": "UNI3-KRW", - "symbol": "uni3", - "name": "Uniswap KRW" - }, - { - "id": "UNI3-RUB", - "symbol": "uni3", - "name": "Uniswap RUB" - }, - { - "id": "UNI3-USD", - "symbol": "uni3", - "name": "Uniswap USD" - }, - { - "id": "UNO-CAD", - "symbol": "uno", - "name": "Unobtanium CAD" - }, - { - "id": "UNO-CNY", - "symbol": "uno", - "name": "Unobtanium CNY" - }, - { - "id": "UNO-ETH", - "symbol": "uno", - "name": "Unobtanium ETH" - }, - { - "id": "UNO-EUR", - "symbol": "uno", - "name": "Unobtanium EUR" - }, - { - "id": "UNO-GBP", - "symbol": "uno", - "name": "Unobtanium GBP" - }, - { - "id": "UNO-INR", - "symbol": "uno", - "name": "Unobtanium INR" - }, - { - "id": "UNO-JPY", - "symbol": "uno", - "name": "Unobtanium JPY" - }, - { - "id": "UNO-KRW", - "symbol": "uno", - "name": "Unobtanium KRW" - }, - { - "id": "UNO-RUB", - "symbol": "uno", - "name": "Unobtanium RUB" - }, - { - "id": "UNO-USD", - "symbol": "uno", - "name": "Unobtanium USD" - }, - { - "id": "USDC-CAD", - "symbol": "usdc", - "name": "USDCoin CAD" - }, - { - "id": "USDC-CNY", - "symbol": "usdc", - "name": "USDCoin CNY" - }, - { - "id": "USDC-ETH", - "symbol": "usdc", - "name": "USDCoin ETH" - }, - { - "id": "USDC-EUR", - "symbol": "usdc", - "name": "USDCoin EUR" - }, - { - "id": "USDC-GBP", - "symbol": "usdc", - "name": "USDCoin GBP" - }, - { - "id": "USDC-INR", - "symbol": "usdc", - "name": "USDCoin INR" - }, - { - "id": "USDC-JPY", - "symbol": "usdc", - "name": "USDCoin JPY" - }, - { - "id": "USDC-KRW", - "symbol": "usdc", - "name": "USDCoin KRW" - }, - { - "id": "USDC-RUB", - "symbol": "usdc", - "name": "USDCoin RUB" - }, - { - "id": "USDC-USD", - "symbol": "usdc", - "name": "USDCoin USD" - }, - { - "id": "USDT-BTC", - "symbol": "usdt", - "name": "Tether BTC" - }, - { - "id": "USDT-CNY", - "symbol": "usdt", - "name": "Tether CNY" - }, - { - "id": "USDT-ETH", - "symbol": "usdt", - "name": "Tether ETH" - }, - { - "id": "USDT-EUR", - "symbol": "usdt", - "name": "Tether EUR" - }, - { - "id": "USDT-GBP", - "symbol": "usdt", - "name": "Tether GBP" - }, - { - "id": "USDT-INR", - "symbol": "usdt", - "name": "Tether INR" - }, - { - "id": "USDT-JPY", - "symbol": "usdt", - "name": "Tether JPY" - }, - { - "id": "USDT-KRW", - "symbol": "usdt", - "name": "Tether KRW" - }, - { - "id": "USDT-RUB", - "symbol": "usdt", - "name": "Tether RUB" - }, - { - "id": "USDT-USD", - "symbol": "usdt", - "name": "Tether USD" - }, - { - "id": "USNBT-CAD", - "symbol": "usnbt", - "name": "NuBits CAD" - }, - { - "id": "USNBT-CNY", - "symbol": "usnbt", - "name": "NuBits CNY" - }, - { - "id": "USNBT-ETH", - "symbol": "usnbt", - "name": "NuBits ETH" - }, - { - "id": "USNBT-EUR", - "symbol": "usnbt", - "name": "NuBits EUR" - }, - { - "id": "USNBT-GBP", - "symbol": "usnbt", - "name": "NuBits GBP" - }, - { - "id": "USNBT-INR", - "symbol": "usnbt", - "name": "NuBits INR" - }, - { - "id": "USNBT-JPY", - "symbol": "usnbt", - "name": "NuBits JPY" - }, - { - "id": "USNBT-KRW", - "symbol": "usnbt", - "name": "NuBits KRW" - }, - { - "id": "USNBT-RUB", - "symbol": "usnbt", - "name": "NuBits RUB" - }, - { - "id": "USNBT-USD", - "symbol": "usnbt", - "name": "NuBits USD" - }, - { - "id": "VAL1-BTC", - "symbol": "val1", - "name": "Validity BTC" - }, - { - "id": "VAL1-CAD", - "symbol": "val1", - "name": "Validity CAD" - }, - { - "id": "VAL1-CNY", - "symbol": "val1", - "name": "Validity CNY" - }, - { - "id": "VAL1-ETH", - "symbol": "val1", - "name": "Validity ETH" - }, - { - "id": "VAL1-EUR", - "symbol": "val1", - "name": "Validity EUR" - }, - { - "id": "VAL1-GBP", - "symbol": "val1", - "name": "Validity GBP" - }, - { - "id": "VAL1-INR", - "symbol": "val1", - "name": "Validity INR" - }, - { - "id": "VAL1-JPY", - "symbol": "val1", - "name": "Validity JPY" - }, - { - "id": "VAL1-KRW", - "symbol": "val1", - "name": "Validity KRW" - }, - { - "id": "VAL1-RUB", - "symbol": "val1", - "name": "Validity RUB" - }, - { - "id": "VAL1-USD", - "symbol": "val1", - "name": "Validity USD" - }, - { - "id": "VBK-CAD", - "symbol": "vbk", - "name": "VeriBlock CAD" - }, - { - "id": "VBK-CNY", - "symbol": "vbk", - "name": "VeriBlock CNY" - }, - { - "id": "VBK-ETH", - "symbol": "vbk", - "name": "VeriBlock ETH" - }, - { - "id": "VBK-EUR", - "symbol": "vbk", - "name": "VeriBlock EUR" - }, - { - "id": "VBK-GBP", - "symbol": "vbk", - "name": "VeriBlock GBP" - }, - { - "id": "VBK-INR", - "symbol": "vbk", - "name": "VeriBlock INR" - }, - { - "id": "VBK-JPY", - "symbol": "vbk", - "name": "VeriBlock JPY" - }, - { - "id": "VBK-KRW", - "symbol": "vbk", - "name": "VeriBlock KRW" - }, - { - "id": "VBK-RUB", - "symbol": "vbk", - "name": "VeriBlock RUB" - }, - { - "id": "VBK-USD", - "symbol": "vbk", - "name": "VeriBlock USD" - }, - { - "id": "VERI-CAD", - "symbol": "veri", - "name": "Veritaseum CAD" - }, - { - "id": "VERI-CNY", - "symbol": "veri", - "name": "Veritaseum CNY" - }, - { - "id": "VERI-ETH", - "symbol": "veri", - "name": "Veritaseum ETH" - }, - { - "id": "VERI-EUR", - "symbol": "veri", - "name": "Veritaseum EUR" - }, - { - "id": "VERI-GBP", - "symbol": "veri", - "name": "Veritaseum GBP" - }, - { - "id": "VERI-INR", - "symbol": "veri", - "name": "Veritaseum INR" - }, - { - "id": "VERI-JPY", - "symbol": "veri", - "name": "Veritaseum JPY" - }, - { - "id": "VERI-KRW", - "symbol": "veri", - "name": "Veritaseum KRW" - }, - { - "id": "VERI-RUB", - "symbol": "veri", - "name": "Veritaseum RUB" - }, - { - "id": "VERI-USD", - "symbol": "veri", - "name": "Veritaseum USD" - }, - { - "id": "VET-AUD", - "symbol": "vet", - "name": "VeChain AUD" - }, - { - "id": "VET-BTC", - "symbol": "vet", - "name": "VeChain BTC" - }, - { - "id": "VET-CAD", - "symbol": "vet", - "name": "VeChain CAD" - }, - { - "id": "VET-CNY", - "symbol": "vet", - "name": "VeChain CNY" - }, - { - "id": "VET-ETH", - "symbol": "vet", - "name": "VeChain ETH" - }, - { - "id": "VET-EUR", - "symbol": "vet", - "name": "VeChain EUR" - }, - { - "id": "VET-GBP", - "symbol": "vet", - "name": "VeChain GBP" - }, - { - "id": "VET-INR", - "symbol": "vet", - "name": "VeChain INR" - }, - { - "id": "VET-JPY", - "symbol": "vet", - "name": "VeChain JPY" - }, - { - "id": "VET-KRW", - "symbol": "vet", - "name": "VeChain KRW" - }, - { - "id": "VET-USD", - "symbol": "vet", - "name": "VeChain USD" - }, - { - "id": "VEX-CAD", - "symbol": "vex", - "name": "Vexanium CAD" - }, - { - "id": "VEX-CNY", - "symbol": "vex", - "name": "Vexanium CNY" - }, - { - "id": "VEX-ETH", - "symbol": "vex", - "name": "Vexanium ETH" - }, - { - "id": "VEX-EUR", - "symbol": "vex", - "name": "Vexanium EUR" - }, - { - "id": "VEX-GBP", - "symbol": "vex", - "name": "Vexanium GBP" - }, - { - "id": "VEX-INR", - "symbol": "vex", - "name": "Vexanium INR" - }, - { - "id": "VEX-JPY", - "symbol": "vex", - "name": "Vexanium JPY" - }, - { - "id": "VEX-KRW", - "symbol": "vex", - "name": "Vexanium KRW" - }, - { - "id": "VEX-RUB", - "symbol": "vex", - "name": "Vexanium RUB" - }, - { - "id": "VEX-USD", - "symbol": "vex", - "name": "Vexanium USD" - }, - { - "id": "VGX-AUD", - "symbol": "vgx", - "name": "VoyagerToken AUD" - }, - { - "id": "VGX-BTC", - "symbol": "vgx", - "name": "VoyagerToken BTC" - }, - { - "id": "VGX-CAD", - "symbol": "vgx", - "name": "VoyagerToken CAD" - }, - { - "id": "VGX-CNY", - "symbol": "vgx", - "name": "VoyagerToken CNY" - }, - { - "id": "VGX-ETH", - "symbol": "vgx", - "name": "VoyagerToken ETH" - }, - { - "id": "VGX-EUR", - "symbol": "vgx", - "name": "VoyagerToken EUR" - }, - { - "id": "VGX-GBP", - "symbol": "vgx", - "name": "VoyagerToken GBP" - }, - { - "id": "VGX-INR", - "symbol": "vgx", - "name": "VoyagerToken INR" - }, - { - "id": "VGX-JPY", - "symbol": "vgx", - "name": "VoyagerToken JPY" - }, - { - "id": "VGX-KRW", - "symbol": "vgx", - "name": "VoyagerToken KRW" - }, - { - "id": "VGX-RUB", - "symbol": "vgx", - "name": "VoyagerToken RUB" - }, - { - "id": "VGX-USD", - "symbol": "vgx", - "name": "VoyagerToken USD" - }, - { - "id": "VIA-CAD", - "symbol": "via", - "name": "Viacoin CAD" - }, - { - "id": "VIA-CNY", - "symbol": "via", - "name": "Viacoin CNY" - }, - { - "id": "VIA-ETH", - "symbol": "via", - "name": "Viacoin ETH" - }, - { - "id": "VIA-EUR", - "symbol": "via", - "name": "Viacoin EUR" - }, - { - "id": "VIA-GBP", - "symbol": "via", - "name": "Viacoin GBP" - }, - { - "id": "VIA-INR", - "symbol": "via", - "name": "Viacoin INR" - }, - { - "id": "VIA-JPY", - "symbol": "via", - "name": "Viacoin JPY" - }, - { - "id": "VIA-KRW", - "symbol": "via", - "name": "Viacoin KRW" - }, - { - "id": "VIA-RUB", - "symbol": "via", - "name": "Viacoin RUB" - }, - { - "id": "VIA-USD", - "symbol": "via", - "name": "Viacoin USD" - }, - { - "id": "VIN-CAD", - "symbol": "vin", - "name": "VINchain CAD" - }, - { - "id": "VIN-CNY", - "symbol": "vin", - "name": "VINchain CNY" - }, - { - "id": "VIN-ETH", - "symbol": "vin", - "name": "VINchain ETH" - }, - { - "id": "VIN-EUR", - "symbol": "vin", - "name": "VINchain EUR" - }, - { - "id": "VIN-GBP", - "symbol": "vin", - "name": "VINchain GBP" - }, - { - "id": "VIN-INR", - "symbol": "vin", - "name": "VINchain INR" - }, - { - "id": "VIN-JPY", - "symbol": "vin", - "name": "VINchain JPY" - }, - { - "id": "VIN-KRW", - "symbol": "vin", - "name": "VINchain KRW" - }, - { - "id": "VIN-RUB", - "symbol": "vin", - "name": "VINchain RUB" - }, - { - "id": "VIN-USD", - "symbol": "vin", - "name": "VINchain USD" - }, - { - "id": "VITAE-CAD", - "symbol": "vitae", - "name": "Vitae CAD" - }, - { - "id": "VITAE-CNY", - "symbol": "vitae", - "name": "Vitae CNY" - }, - { - "id": "VITAE-ETH", - "symbol": "vitae", - "name": "Vitae ETH" - }, - { - "id": "VITAE-EUR", - "symbol": "vitae", - "name": "Vitae EUR" - }, - { - "id": "VITAE-GBP", - "symbol": "vitae", - "name": "Vitae GBP" - }, - { - "id": "VITAE-INR", - "symbol": "vitae", - "name": "Vitae INR" - }, - { - "id": "VITAE-JPY", - "symbol": "vitae", - "name": "Vitae JPY" - }, - { - "id": "VITAE-KRW", - "symbol": "vitae", - "name": "Vitae KRW" - }, - { - "id": "VITAE-RUB", - "symbol": "vitae", - "name": "Vitae RUB" - }, - { - "id": "VITAE-USD", - "symbol": "vitae", - "name": "Vitae USD" - }, - { - "id": "VLX-CAD", - "symbol": "vlx", - "name": "Velas CAD" - }, - { - "id": "VLX-CNY", - "symbol": "vlx", - "name": "Velas CNY" - }, - { - "id": "VLX-ETH", - "symbol": "vlx", - "name": "Velas ETH" - }, - { - "id": "VLX-EUR", - "symbol": "vlx", - "name": "Velas EUR" - }, - { - "id": "VLX-GBP", - "symbol": "vlx", - "name": "Velas GBP" - }, - { - "id": "VLX-INR", - "symbol": "vlx", - "name": "Velas INR" - }, - { - "id": "VLX-JPY", - "symbol": "vlx", - "name": "Velas JPY" - }, - { - "id": "VLX-KRW", - "symbol": "vlx", - "name": "Velas KRW" - }, - { - "id": "VLX-RUB", - "symbol": "vlx", - "name": "Velas RUB" - }, - { - "id": "VLX-USD", - "symbol": "vlx", - "name": "Velas USD" - }, - { - "id": "VRA-AUD", - "symbol": "vra", - "name": "Verasity AUD" - }, - { - "id": "VRA-BTC", - "symbol": "vra", - "name": "Verasity BTC" - }, - { - "id": "VRA-CAD", - "symbol": "vra", - "name": "Verasity CAD" - }, - { - "id": "VRA-CNY", - "symbol": "vra", - "name": "Verasity CNY" - }, - { - "id": "VRA-ETH", - "symbol": "vra", - "name": "Verasity ETH" - }, - { - "id": "VRA-EUR", - "symbol": "vra", - "name": "Verasity EUR" - }, - { - "id": "VRA-GBP", - "symbol": "vra", - "name": "Verasity GBP" - }, - { - "id": "VRA-INR", - "symbol": "vra", - "name": "Verasity INR" - }, - { - "id": "VRA-JPY", - "symbol": "vra", - "name": "Verasity JPY" - }, - { - "id": "VRA-KRW", - "symbol": "vra", - "name": "Verasity KRW" - }, - { - "id": "VRA-RUB", - "symbol": "vra", - "name": "Verasity RUB" - }, - { - "id": "VRA-USD", - "symbol": "vra", - "name": "Verasity USD" - }, - { - "id": "VSYS-CAD", - "symbol": "vsys", - "name": "VSystems CAD" - }, - { - "id": "VSYS-CNY", - "symbol": "vsys", - "name": "VSystems CNY" - }, - { - "id": "VSYS-ETH", - "symbol": "vsys", - "name": "VSystems ETH" - }, - { - "id": "VSYS-EUR", - "symbol": "vsys", - "name": "VSystems EUR" - }, - { - "id": "VSYS-GBP", - "symbol": "vsys", - "name": "VSystems GBP" - }, - { - "id": "VSYS-INR", - "symbol": "vsys", - "name": "VSystems INR" - }, - { - "id": "VSYS-JPY", - "symbol": "vsys", - "name": "VSystems JPY" - }, - { - "id": "VSYS-KRW", - "symbol": "vsys", - "name": "VSystems KRW" - }, - { - "id": "VSYS-RUB", - "symbol": "vsys", - "name": "VSystems RUB" - }, - { - "id": "VSYS-USD", - "symbol": "vsys", - "name": "VSystems USD" - }, - { - "id": "VTC-BTC", - "symbol": "vtc", - "name": "Vertcoin BTC" - }, - { - "id": "VTC-CAD", - "symbol": "vtc", - "name": "Vertcoin CAD" - }, - { - "id": "VTC-ETH", - "symbol": "vtc", - "name": "Vertcoin ETH" - }, - { - "id": "VTC-EUR", - "symbol": "vtc", - "name": "Vertcoin EUR" - }, - { - "id": "VTC-GBP", - "symbol": "vtc", - "name": "Vertcoin GBP" - }, - { - "id": "VTC-INR", - "symbol": "vtc", - "name": "Vertcoin INR" - }, - { - "id": "VTC-JPY", - "symbol": "vtc", - "name": "Vertcoin JPY" - }, - { - "id": "VTC-KRW", - "symbol": "vtc", - "name": "Vertcoin KRW" - }, - { - "id": "VTC-RUB", - "symbol": "vtc", - "name": "Vertcoin RUB" - }, - { - "id": "VTC-USD", - "symbol": "vtc", - "name": "Vertcoin USD" - }, - { - "id": "VTHO-AUD", - "symbol": "vtho", - "name": "VeThorToken AUD" - }, - { - "id": "VTHO-BTC", - "symbol": "vtho", - "name": "VeThorToken BTC" - }, - { - "id": "VTHO-CAD", - "symbol": "vtho", - "name": "VeThorToken CAD" - }, - { - "id": "VTHO-CNY", - "symbol": "vtho", - "name": "VeThorToken CNY" - }, - { - "id": "VTHO-ETH", - "symbol": "vtho", - "name": "VeThorToken ETH" - }, - { - "id": "VTHO-EUR", - "symbol": "vtho", - "name": "VeThorToken EUR" - }, - { - "id": "VTHO-GBP", - "symbol": "vtho", - "name": "VeThorToken GBP" - }, - { - "id": "VTHO-INR", - "symbol": "vtho", - "name": "VeThorToken INR" - }, - { - "id": "VTHO-JPY", - "symbol": "vtho", - "name": "VeThorToken JPY" - }, - { - "id": "VTHO-KRW", - "symbol": "vtho", - "name": "VeThorToken KRW" - }, - { - "id": "VTHO-RUB", - "symbol": "vtho", - "name": "VeThorToken RUB" - }, - { - "id": "VTHO-USD", - "symbol": "vtho", - "name": "VeThorToken USD" - }, - { - "id": "WABI-CAD", - "symbol": "wabi", - "name": "Tael CAD" - }, - { - "id": "WABI-CNY", - "symbol": "wabi", - "name": "Tael CNY" - }, - { - "id": "WABI-ETH", - "symbol": "wabi", - "name": "Tael ETH" - }, - { - "id": "WABI-EUR", - "symbol": "wabi", - "name": "Tael EUR" - }, - { - "id": "WABI-GBP", - "symbol": "wabi", - "name": "Tael GBP" - }, - { - "id": "WABI-INR", - "symbol": "wabi", - "name": "Tael INR" - }, - { - "id": "WABI-JPY", - "symbol": "wabi", - "name": "Tael JPY" - }, - { - "id": "WABI-KRW", - "symbol": "wabi", - "name": "Tael KRW" - }, - { - "id": "WABI-RUB", - "symbol": "wabi", - "name": "Tael RUB" - }, - { - "id": "WABI-USD", - "symbol": "wabi", - "name": "Tael USD" - }, - { - "id": "WAN-CAD", - "symbol": "wan", - "name": "Wanchain CAD" - }, - { - "id": "WAN-CNY", - "symbol": "wan", - "name": "Wanchain CNY" - }, - { - "id": "WAN-ETH", - "symbol": "wan", - "name": "Wanchain ETH" - }, - { - "id": "WAN-EUR", - "symbol": "wan", - "name": "Wanchain EUR" - }, - { - "id": "WAN-GBP", - "symbol": "wan", - "name": "Wanchain GBP" - }, - { - "id": "WAN-INR", - "symbol": "wan", - "name": "Wanchain INR" - }, - { - "id": "WAN-JPY", - "symbol": "wan", - "name": "Wanchain JPY" - }, - { - "id": "WAN-KRW", - "symbol": "wan", - "name": "Wanchain KRW" - }, - { - "id": "WAN-RUB", - "symbol": "wan", - "name": "Wanchain RUB" - }, - { - "id": "WAN-USD", - "symbol": "wan", - "name": "Wanchain USD" - }, - { - "id": "WAVES-BTC", - "symbol": "waves", - "name": "Waves BTC" - }, - { - "id": "WAVES-EUR", - "symbol": "waves", - "name": "Waves EUR" - }, - { - "id": "WAVES-INR", - "symbol": "waves", - "name": "Waves INR" - }, - { - "id": "WAVES-RUB", - "symbol": "waves", - "name": "Waves RUB" - }, - { - "id": "WAVES-USD", - "symbol": "waves", - "name": "Waves USD" - }, - { - "id": "WAXP-CAD", - "symbol": "waxp", - "name": "WAX CAD" - }, - { - "id": "WAXP-CNY", - "symbol": "waxp", - "name": "WAX CNY" - }, - { - "id": "WAXP-ETH", - "symbol": "waxp", - "name": "WAX ETH" - }, - { - "id": "WAXP-EUR", - "symbol": "waxp", - "name": "WAX EUR" - }, - { - "id": "WAXP-GBP", - "symbol": "waxp", - "name": "WAX GBP" - }, - { - "id": "WAXP-INR", - "symbol": "waxp", - "name": "WAX INR" - }, - { - "id": "WAXP-JPY", - "symbol": "waxp", - "name": "WAX JPY" - }, - { - "id": "WAXP-KRW", - "symbol": "waxp", - "name": "WAX KRW" - }, - { - "id": "WAXP-RUB", - "symbol": "waxp", - "name": "WAX RUB" - }, - { - "id": "WAXP-USD", - "symbol": "waxp", - "name": "WAX USD" - }, - { - "id": "WGR-CAD", - "symbol": "wgr", - "name": "Wagerr CAD" - }, - { - "id": "WGR-CNY", - "symbol": "wgr", - "name": "Wagerr CNY" - }, - { - "id": "WGR-ETH", - "symbol": "wgr", - "name": "Wagerr ETH" - }, - { - "id": "WGR-EUR", - "symbol": "wgr", - "name": "Wagerr EUR" - }, - { - "id": "WGR-GBP", - "symbol": "wgr", - "name": "Wagerr GBP" - }, - { - "id": "WGR-INR", - "symbol": "wgr", - "name": "Wagerr INR" - }, - { - "id": "WGR-JPY", - "symbol": "wgr", - "name": "Wagerr JPY" - }, - { - "id": "WGR-KRW", - "symbol": "wgr", - "name": "Wagerr KRW" - }, - { - "id": "WGR-RUB", - "symbol": "wgr", - "name": "Wagerr RUB" - }, - { - "id": "WGR-USD", - "symbol": "wgr", - "name": "Wagerr USD" - }, - { - "id": "WICC-CAD", - "symbol": "wicc", - "name": "WaykiChain CAD" - }, - { - "id": "WICC-CNY", - "symbol": "wicc", - "name": "WaykiChain CNY" - }, - { - "id": "WICC-ETH", - "symbol": "wicc", - "name": "WaykiChain ETH" - }, - { - "id": "WICC-EUR", - "symbol": "wicc", - "name": "WaykiChain EUR" - }, - { - "id": "WICC-GBP", - "symbol": "wicc", - "name": "WaykiChain GBP" - }, - { - "id": "WICC-INR", - "symbol": "wicc", - "name": "WaykiChain INR" - }, - { - "id": "WICC-JPY", - "symbol": "wicc", - "name": "WaykiChain JPY" - }, - { - "id": "WICC-KRW", - "symbol": "wicc", - "name": "WaykiChain KRW" - }, - { - "id": "WICC-RUB", - "symbol": "wicc", - "name": "WaykiChain RUB" - }, - { - "id": "WICC-USD", - "symbol": "wicc", - "name": "WaykiChain USD" - }, - { - "id": "WINGS-AUD", - "symbol": "wings", - "name": "Wings AUD" - }, - { - "id": "WINGS-CNY", - "symbol": "wings", - "name": "Wings CNY" - }, - { - "id": "WINGS-EUR", - "symbol": "wings", - "name": "Wings EUR" - }, - { - "id": "WINGS-GBP", - "symbol": "wings", - "name": "Wings GBP" - }, - { - "id": "WINGS-INR", - "symbol": "wings", - "name": "Wings INR" - }, - { - "id": "WINGS-JPY", - "symbol": "wings", - "name": "Wings JPY" - }, - { - "id": "WINGS-USD", - "symbol": "wings", - "name": "Wings USD" - }, - { - "id": "WOZX-CAD", - "symbol": "wozx", - "name": "EFFORCE CAD" - }, - { - "id": "WOZX-CNY", - "symbol": "wozx", - "name": "EFFORCE CNY" - }, - { - "id": "WOZX-ETH", - "symbol": "wozx", - "name": "EFFORCE ETH" - }, - { - "id": "WOZX-EUR", - "symbol": "wozx", - "name": "EFFORCE EUR" - }, - { - "id": "WOZX-GBP", - "symbol": "wozx", - "name": "EFFORCE GBP" - }, - { - "id": "WOZX-INR", - "symbol": "wozx", - "name": "EFFORCE INR" - }, - { - "id": "WOZX-JPY", - "symbol": "wozx", - "name": "EFFORCE JPY" - }, - { - "id": "WOZX-KRW", - "symbol": "wozx", - "name": "EFFORCE KRW" - }, - { - "id": "WOZX-RUB", - "symbol": "wozx", - "name": "EFFORCE RUB" - }, - { - "id": "WOZX-USD", - "symbol": "wozx", - "name": "EFFORCE USD" - }, - { - "id": "WTC-AUD", - "symbol": "wtc", - "name": "Waltonchain AUD" - }, - { - "id": "WTC-BTC", - "symbol": "wtc", - "name": "Waltonchain BTC" - }, - { - "id": "WTC-CAD", - "symbol": "wtc", - "name": "Waltonchain CAD" - }, - { - "id": "WTC-CNY", - "symbol": "wtc", - "name": "Waltonchain CNY" - }, - { - "id": "WTC-ETH", - "symbol": "wtc", - "name": "Waltonchain ETH" - }, - { - "id": "WTC-EUR", - "symbol": "wtc", - "name": "Waltonchain EUR" - }, - { - "id": "WTC-GBP", - "symbol": "wtc", - "name": "Waltonchain GBP" - }, - { - "id": "WTC-JPY", - "symbol": "wtc", - "name": "Waltonchain JPY" - }, - { - "id": "WTC-KRW", - "symbol": "wtc", - "name": "Waltonchain KRW" - }, - { - "id": "WTC-USD", - "symbol": "wtc", - "name": "Waltonchain USD" - }, - { - "id": "XAS-CAD", - "symbol": "xas", - "name": "Asch CAD" - }, - { - "id": "XAS-CNY", - "symbol": "xas", - "name": "Asch CNY" - }, - { - "id": "XAS-ETH", - "symbol": "xas", - "name": "Asch ETH" - }, - { - "id": "XAS-EUR", - "symbol": "xas", - "name": "Asch EUR" - }, - { - "id": "XAS-GBP", - "symbol": "xas", - "name": "Asch GBP" - }, - { - "id": "XAS-INR", - "symbol": "xas", - "name": "Asch INR" - }, - { - "id": "XAS-JPY", - "symbol": "xas", - "name": "Asch JPY" - }, - { - "id": "XAS-KRW", - "symbol": "xas", - "name": "Asch KRW" - }, - { - "id": "XAS-RUB", - "symbol": "xas", - "name": "Asch RUB" - }, - { - "id": "XAS-USD", - "symbol": "xas", - "name": "Asch USD" - }, - { - "id": "XBY-CAD", - "symbol": "xby", - "name": "XTRABYTES CAD" - }, - { - "id": "XBY-CNY", - "symbol": "xby", - "name": "XTRABYTES CNY" - }, - { - "id": "XBY-ETH", - "symbol": "xby", - "name": "XTRABYTES ETH" - }, - { - "id": "XBY-EUR", - "symbol": "xby", - "name": "XTRABYTES EUR" - }, - { - "id": "XBY-GBP", - "symbol": "xby", - "name": "XTRABYTES GBP" - }, - { - "id": "XBY-INR", - "symbol": "xby", - "name": "XTRABYTES INR" - }, - { - "id": "XBY-JPY", - "symbol": "xby", - "name": "XTRABYTES JPY" - }, - { - "id": "XBY-KRW", - "symbol": "xby", - "name": "XTRABYTES KRW" - }, - { - "id": "XBY-RUB", - "symbol": "xby", - "name": "XTRABYTES RUB" - }, - { - "id": "XBY-USD", - "symbol": "xby", - "name": "XTRABYTES USD" - }, - { - "id": "XCP-BTC", - "symbol": "xcp", - "name": "Counterparty BTC" - }, - { - "id": "XCP-CAD", - "symbol": "xcp", - "name": "Counterparty CAD" - }, - { - "id": "XCP-CNY", - "symbol": "xcp", - "name": "Counterparty CNY" - }, - { - "id": "XCP-ETH", - "symbol": "xcp", - "name": "Counterparty ETH" - }, - { - "id": "XCP-EUR", - "symbol": "xcp", - "name": "Counterparty EUR" - }, - { - "id": "XCP-GBP", - "symbol": "xcp", - "name": "Counterparty GBP" - }, - { - "id": "XCP-INR", - "symbol": "xcp", - "name": "Counterparty INR" - }, - { - "id": "XCP-JPY", - "symbol": "xcp", - "name": "Counterparty JPY" - }, - { - "id": "XCP-KRW", - "symbol": "xcp", - "name": "Counterparty KRW" - }, - { - "id": "XCP-USD", - "symbol": "xcp", - "name": "Counterparty USD" - }, - { - "id": "XDC-AUD", - "symbol": "xdc", - "name": "XinFinNetwork AUD" - }, - { - "id": "XDC-CAD", - "symbol": "xdc", - "name": "XinFinNetwork CAD" - }, - { - "id": "XDC-CNY", - "symbol": "xdc", - "name": "XinFinNetwork CNY" - }, - { - "id": "XDC-ETH", - "symbol": "xdc", - "name": "XinFinNetwork ETH" - }, - { - "id": "XDC-EUR", - "symbol": "xdc", - "name": "XinFinNetwork EUR" - }, - { - "id": "XDC-GBP", - "symbol": "xdc", - "name": "XinFinNetwork GBP" - }, - { - "id": "XDC-INR", - "symbol": "xdc", - "name": "XinFinNetwork INR" - }, - { - "id": "XDC-JPY", - "symbol": "xdc", - "name": "XinFinNetwork JPY" - }, - { - "id": "XDC-KRW", - "symbol": "xdc", - "name": "XinFinNetwork KRW" - }, - { - "id": "XDC-RUB", - "symbol": "xdc", - "name": "XinFinNetwork RUB" - }, - { - "id": "XDC-USD", - "symbol": "xdc", - "name": "XinFinNetwork USD" - }, - { - "id": "XDN-BTC", - "symbol": "xdn", - "name": "DigitalNote BTC" - }, - { - "id": "XDN-CAD", - "symbol": "xdn", - "name": "DigitalNote CAD" - }, - { - "id": "XDN-CNY", - "symbol": "xdn", - "name": "DigitalNote CNY" - }, - { - "id": "XDN-ETH", - "symbol": "xdn", - "name": "DigitalNote ETH" - }, - { - "id": "XDN-EUR", - "symbol": "xdn", - "name": "DigitalNote EUR" - }, - { - "id": "XDN-GBP", - "symbol": "xdn", - "name": "DigitalNote GBP" - }, - { - "id": "XDN-INR", - "symbol": "xdn", - "name": "DigitalNote INR" - }, - { - "id": "XDN-JPY", - "symbol": "xdn", - "name": "DigitalNote JPY" - }, - { - "id": "XDN-KRW", - "symbol": "xdn", - "name": "DigitalNote KRW" - }, - { - "id": "XDN-RUB", - "symbol": "xdn", - "name": "DigitalNote RUB" - }, - { - "id": "XDN-USD", - "symbol": "xdn", - "name": "DigitalNote USD" - }, - { - "id": "XEM-JPY", - "symbol": "xem", - "name": "NEM JPY" - }, - { - "id": "XEM-RUB", - "symbol": "xem", - "name": "NEM RUB" - }, - { - "id": "XEM-USD", - "symbol": "xem", - "name": "NEM USD" - }, - { - "id": "XHV-BTC", - "symbol": "xhv", - "name": "HavenProtocol BTC" - }, - { - "id": "XHV-CAD", - "symbol": "xhv", - "name": "HavenProtocol CAD" - }, - { - "id": "XHV-CNY", - "symbol": "xhv", - "name": "HavenProtocol CNY" - }, - { - "id": "XHV-ETH", - "symbol": "xhv", - "name": "HavenProtocol ETH" - }, - { - "id": "XHV-EUR", - "symbol": "xhv", - "name": "HavenProtocol EUR" - }, - { - "id": "XHV-GBP", - "symbol": "xhv", - "name": "HavenProtocol GBP" - }, - { - "id": "XHV-INR", - "symbol": "xhv", - "name": "HavenProtocol INR" - }, - { - "id": "XHV-JPY", - "symbol": "xhv", - "name": "HavenProtocol JPY" - }, - { - "id": "XHV-KRW", - "symbol": "xhv", - "name": "HavenProtocol KRW" - }, - { - "id": "XHV-RUB", - "symbol": "xhv", - "name": "HavenProtocol RUB" - }, - { - "id": "XHV-USD", - "symbol": "xhv", - "name": "HavenProtocol USD" - }, - { - "id": "XLM-BTC", - "symbol": "xlm", - "name": "Stellar BTC" - }, - { - "id": "XLM-CNY", - "symbol": "xlm", - "name": "Stellar CNY" - }, - { - "id": "XLM-EUR", - "symbol": "xlm", - "name": "Stellar EUR" - }, - { - "id": "XLM-GBP", - "symbol": "xlm", - "name": "Stellar GBP" - }, - { - "id": "XLM-JPY", - "symbol": "xlm", - "name": "Stellar JPY" - }, - { - "id": "XLM-USD", - "symbol": "xlm", - "name": "Stellar USD" - }, - { - "id": "XLQ-CAD", - "symbol": "xlq", - "name": "ALQO CAD" - }, - { - "id": "XLQ-CNY", - "symbol": "xlq", - "name": "ALQO CNY" - }, - { - "id": "XLQ-ETH", - "symbol": "xlq", - "name": "ALQO ETH" - }, - { - "id": "XLQ-EUR", - "symbol": "xlq", - "name": "ALQO EUR" - }, - { - "id": "XLQ-GBP", - "symbol": "xlq", - "name": "ALQO GBP" - }, - { - "id": "XLQ-INR", - "symbol": "xlq", - "name": "ALQO INR" - }, - { - "id": "XLQ-JPY", - "symbol": "xlq", - "name": "ALQO JPY" - }, - { - "id": "XLQ-KRW", - "symbol": "xlq", - "name": "ALQO KRW" - }, - { - "id": "XLQ-RUB", - "symbol": "xlq", - "name": "ALQO RUB" - }, - { - "id": "XLQ-USD", - "symbol": "xlq", - "name": "ALQO USD" - }, - { - "id": "XLT-CAD", - "symbol": "xlt", - "name": "Nexalt CAD" - }, - { - "id": "XLT-CNY", - "symbol": "xlt", - "name": "Nexalt CNY" - }, - { - "id": "XLT-ETH", - "symbol": "xlt", - "name": "Nexalt ETH" - }, - { - "id": "XLT-EUR", - "symbol": "xlt", - "name": "Nexalt EUR" - }, - { - "id": "XLT-GBP", - "symbol": "xlt", - "name": "Nexalt GBP" - }, - { - "id": "XLT-INR", - "symbol": "xlt", - "name": "Nexalt INR" - }, - { - "id": "XLT-JPY", - "symbol": "xlt", - "name": "Nexalt JPY" - }, - { - "id": "XLT-KRW", - "symbol": "xlt", - "name": "Nexalt KRW" - }, - { - "id": "XLT-RUB", - "symbol": "xlt", - "name": "Nexalt RUB" - }, - { - "id": "XLT-USD", - "symbol": "xlt", - "name": "Nexalt USD" - }, - { - "id": "XMC-BTC", - "symbol": "xmc", - "name": "MoneroClassic BTC" - }, - { - "id": "XMC-CAD", - "symbol": "xmc", - "name": "MoneroClassic CAD" - }, - { - "id": "XMC-CNY", - "symbol": "xmc", - "name": "MoneroClassic CNY" - }, - { - "id": "XMC-ETH", - "symbol": "xmc", - "name": "MoneroClassic ETH" - }, - { - "id": "XMC-EUR", - "symbol": "xmc", - "name": "MoneroClassic EUR" - }, - { - "id": "XMC-GBP", - "symbol": "xmc", - "name": "MoneroClassic GBP" - }, - { - "id": "XMC-INR", - "symbol": "xmc", - "name": "MoneroClassic INR" - }, - { - "id": "XMC-JPY", - "symbol": "xmc", - "name": "MoneroClassic JPY" - }, - { - "id": "XMC-KRW", - "symbol": "xmc", - "name": "MoneroClassic KRW" - }, - { - "id": "XMC-RUB", - "symbol": "xmc", - "name": "MoneroClassic RUB" - }, - { - "id": "XMC-USD", - "symbol": "xmc", - "name": "MoneroClassic USD" - }, - { - "id": "XMR-AUD", - "symbol": "xmr", - "name": "Monero AUD" - }, - { - "id": "XMR-CNY", - "symbol": "xmr", - "name": "Monero CNY" - }, - { - "id": "XMR-ETH", - "symbol": "xmr", - "name": "Monero ETH" - }, - { - "id": "XMR-EUR", - "symbol": "xmr", - "name": "Monero EUR" - }, - { - "id": "XMR-GBP", - "symbol": "xmr", - "name": "Monero GBP" - }, - { - "id": "XMR-INR", - "symbol": "xmr", - "name": "Monero INR" - }, - { - "id": "XMR-JPY", - "symbol": "xmr", - "name": "Monero JPY" - }, - { - "id": "XMR-KRW", - "symbol": "xmr", - "name": "Monero KRW" - }, - { - "id": "XMR-RUB", - "symbol": "xmr", - "name": "Monero RUB" - }, - { - "id": "XMR-USD", - "symbol": "xmr", - "name": "Monero USD" - }, - { - "id": "XMY-KRW", - "symbol": "xmy", - "name": "Myriad KRW" - }, - { - "id": "XMY-RUB", - "symbol": "xmy", - "name": "Myriad RUB" - }, - { - "id": "XMY-USD", - "symbol": "xmy", - "name": "Myriad USD" - }, - { - "id": "XNC-CAD", - "symbol": "xnc", - "name": "XeniosCoin CAD" - }, - { - "id": "XNC-CNY", - "symbol": "xnc", - "name": "XeniosCoin CNY" - }, - { - "id": "XNC-ETH", - "symbol": "xnc", - "name": "XeniosCoin ETH" - }, - { - "id": "XNC-EUR", - "symbol": "xnc", - "name": "XeniosCoin EUR" - }, - { - "id": "XNC-GBP", - "symbol": "xnc", - "name": "XeniosCoin GBP" - }, - { - "id": "XNC-INR", - "symbol": "xnc", - "name": "XeniosCoin INR" - }, - { - "id": "XNC-JPY", - "symbol": "xnc", - "name": "XeniosCoin JPY" - }, - { - "id": "XNC-KRW", - "symbol": "xnc", - "name": "XeniosCoin KRW" - }, - { - "id": "XNC-RUB", - "symbol": "xnc", - "name": "XeniosCoin RUB" - }, - { - "id": "XNC-USD", - "symbol": "xnc", - "name": "XeniosCoin USD" - }, - { - "id": "XNS1-BTC", - "symbol": "xns1", - "name": "Insolar BTC" - }, - { - "id": "XNS1-CAD", - "symbol": "xns1", - "name": "Insolar CAD" - }, - { - "id": "XNS1-CNY", - "symbol": "xns1", - "name": "Insolar CNY" - }, - { - "id": "XNS1-ETH", - "symbol": "xns1", - "name": "Insolar ETH" - }, - { - "id": "XNS1-EUR", - "symbol": "xns1", - "name": "Insolar EUR" - }, - { - "id": "XNS1-GBP", - "symbol": "xns1", - "name": "Insolar GBP" - }, - { - "id": "XNS1-INR", - "symbol": "xns1", - "name": "Insolar INR" - }, - { - "id": "XNS1-JPY", - "symbol": "xns1", - "name": "Insolar JPY" - }, - { - "id": "XNS1-KRW", - "symbol": "xns1", - "name": "Insolar KRW" - }, - { - "id": "XNS1-RUB", - "symbol": "xns1", - "name": "Insolar RUB" - }, - { - "id": "XRC-AUD", - "symbol": "xrc", - "name": "BitcoinRhodium AUD" - }, - { - "id": "XRC-BTC", - "symbol": "xrc", - "name": "BitcoinRhodium BTC" - }, - { - "id": "XRC-CAD", - "symbol": "xrc", - "name": "BitcoinRhodium CAD" - }, - { - "id": "XRC-CNY", - "symbol": "xrc", - "name": "BitcoinRhodium CNY" - }, - { - "id": "XRC-ETH", - "symbol": "xrc", - "name": "BitcoinRhodium ETH" - }, - { - "id": "XRC-EUR", - "symbol": "xrc", - "name": "BitcoinRhodium EUR" - }, - { - "id": "XRC-GBP", - "symbol": "xrc", - "name": "BitcoinRhodium GBP" - }, - { - "id": "XRC-INR", - "symbol": "xrc", - "name": "BitcoinRhodium INR" - }, - { - "id": "XRC-JPY", - "symbol": "xrc", - "name": "BitcoinRhodium JPY" - }, - { - "id": "XRC-KRW", - "symbol": "xrc", - "name": "BitcoinRhodium KRW" - }, - { - "id": "XRC-RUB", - "symbol": "xrc", - "name": "BitcoinRhodium RUB" - }, - { - "id": "XRC-USD", - "symbol": "xrc", - "name": "BitcoinRhodium USD" - }, - { - "id": "XRP-BTC", - "symbol": "xrp", - "name": "XRP BTC" - }, - { - "id": "XRP-CAD", - "symbol": "xrp", - "name": "XRP CAD" - }, - { - "id": "XRP-EUR", - "symbol": "xrp", - "name": "XRP EUR" - }, - { - "id": "XRP-GBP", - "symbol": "xrp", - "name": "XRP GBP" - }, - { - "id": "XRP-INR", - "symbol": "xrp", - "name": "XRP INR" - }, - { - "id": "XRP-JPY", - "symbol": "xrp", - "name": "XRP JPY" - }, - { - "id": "XRP-USD", - "symbol": "xrp", - "name": "XRP USD" - }, - { - "id": "XSN-BTC", - "symbol": "xsn", - "name": "Stakenet BTC" - }, - { - "id": "XSN-CAD", - "symbol": "xsn", - "name": "Stakenet CAD" - }, - { - "id": "XSN-CNY", - "symbol": "xsn", - "name": "Stakenet CNY" - }, - { - "id": "XSN-ETH", - "symbol": "xsn", - "name": "Stakenet ETH" - }, - { - "id": "XSN-EUR", - "symbol": "xsn", - "name": "Stakenet EUR" - }, - { - "id": "XSN-GBP", - "symbol": "xsn", - "name": "Stakenet GBP" - }, - { - "id": "XSN-INR", - "symbol": "xsn", - "name": "Stakenet INR" - }, - { - "id": "XSN-JPY", - "symbol": "xsn", - "name": "Stakenet JPY" - }, - { - "id": "XSN-KRW", - "symbol": "xsn", - "name": "Stakenet KRW" - }, - { - "id": "XSN-RUB", - "symbol": "xsn", - "name": "Stakenet RUB" - }, - { - "id": "XSN-USD", - "symbol": "xsn", - "name": "Stakenet USD" - }, - { - "id": "XTZ-CNY", - "symbol": "xtz", - "name": "Tezos CNY" - }, - { - "id": "XTZ-ETH", - "symbol": "xtz", - "name": "Tezos ETH" - }, - { - "id": "XTZ-EUR", - "symbol": "xtz", - "name": "Tezos EUR" - }, - { - "id": "XTZ-GBP", - "symbol": "xtz", - "name": "Tezos GBP" - }, - { - "id": "XTZ-INR", - "symbol": "xtz", - "name": "Tezos INR" - }, - { - "id": "XTZ-JPY", - "symbol": "xtz", - "name": "Tezos JPY" - }, - { - "id": "XTZ-KRW", - "symbol": "xtz", - "name": "Tezos KRW" - }, - { - "id": "XTZ-RUB", - "symbol": "xtz", - "name": "Tezos RUB" - }, - { - "id": "XTZ-USD", - "symbol": "xtz", - "name": "Tezos USD" - }, - { - "id": "XUC-AUD", - "symbol": "xuc", - "name": "ExchangeUnion AUD" - }, - { - "id": "XUC-BTC", - "symbol": "xuc", - "name": "ExchangeUnion BTC" - }, - { - "id": "XUC-CAD", - "symbol": "xuc", - "name": "ExchangeUnion CAD" - }, - { - "id": "XUC-CNY", - "symbol": "xuc", - "name": "ExchangeUnion CNY" - }, - { - "id": "XUC-ETH", - "symbol": "xuc", - "name": "ExchangeUnion ETH" - }, - { - "id": "XUC-EUR", - "symbol": "xuc", - "name": "ExchangeUnion EUR" - }, - { - "id": "XUC-INR", - "symbol": "xuc", - "name": "ExchangeUnion INR" - }, - { - "id": "XUC-KRW", - "symbol": "xuc", - "name": "ExchangeUnion KRW" - }, - { - "id": "XUC-RUB", - "symbol": "xuc", - "name": "ExchangeUnion RUB" - }, - { - "id": "XUC-USD", - "symbol": "xuc", - "name": "ExchangeUnion USD" - }, - { - "id": "XVG-AUD", - "symbol": "xvg", - "name": "Verge AUD" - }, - { - "id": "XVG-BTC", - "symbol": "xvg", - "name": "Verge BTC" - }, - { - "id": "XVG-CNY", - "symbol": "xvg", - "name": "Verge CNY" - }, - { - "id": "XVG-ETH", - "symbol": "xvg", - "name": "Verge ETH" - }, - { - "id": "XVG-GBP", - "symbol": "xvg", - "name": "Verge GBP" - }, - { - "id": "XVG-INR", - "symbol": "xvg", - "name": "Verge INR" - }, - { - "id": "XVG-JPY", - "symbol": "xvg", - "name": "Verge JPY" - }, - { - "id": "XVG-KRW", - "symbol": "xvg", - "name": "Verge KRW" - }, - { - "id": "XVG-RUB", - "symbol": "xvg", - "name": "Verge RUB" - }, - { - "id": "XVG-USD", - "symbol": "xvg", - "name": "Verge USD" - }, - { - "id": "XWC-CAD", - "symbol": "xwc", - "name": "WhiteCoin CAD" - }, - { - "id": "XWC-CNY", - "symbol": "xwc", - "name": "WhiteCoin CNY" - }, - { - "id": "XWC-ETH", - "symbol": "xwc", - "name": "WhiteCoin ETH" - }, - { - "id": "XWC-EUR", - "symbol": "xwc", - "name": "WhiteCoin EUR" - }, - { - "id": "XWC-GBP", - "symbol": "xwc", - "name": "WhiteCoin GBP" - }, - { - "id": "XWC-INR", - "symbol": "xwc", - "name": "WhiteCoin INR" - }, - { - "id": "XWC-JPY", - "symbol": "xwc", - "name": "WhiteCoin JPY" - }, - { - "id": "XWC-KRW", - "symbol": "xwc", - "name": "WhiteCoin KRW" - }, - { - "id": "XWC-RUB", - "symbol": "xwc", - "name": "WhiteCoin RUB" - }, - { - "id": "XWC-USD", - "symbol": "xwc", - "name": "WhiteCoin USD" - }, - { - "id": "YEP-CAD", - "symbol": "yep", - "name": "YEPCOIN CAD" - }, - { - "id": "YEP-CNY", - "symbol": "yep", - "name": "YEPCOIN CNY" - }, - { - "id": "YEP-ETH", - "symbol": "yep", - "name": "YEPCOIN ETH" - }, - { - "id": "YEP-EUR", - "symbol": "yep", - "name": "YEPCOIN EUR" - }, - { - "id": "YEP-GBP", - "symbol": "yep", - "name": "YEPCOIN GBP" - }, - { - "id": "YEP-INR", - "symbol": "yep", - "name": "YEPCOIN INR" - }, - { - "id": "YEP-JPY", - "symbol": "yep", - "name": "YEPCOIN JPY" - }, - { - "id": "YEP-KRW", - "symbol": "yep", - "name": "YEPCOIN KRW" - }, - { - "id": "YEP-RUB", - "symbol": "yep", - "name": "YEPCOIN RUB" - }, - { - "id": "YEP-USD", - "symbol": "yep", - "name": "YEPCOIN USD" - }, - { - "id": "YFI-AUD", - "symbol": "yfi", - "name": "yearnfinance AUD" - }, - { - "id": "YFI-BTC", - "symbol": "yfi", - "name": "yearnfinance BTC" - }, - { - "id": "YFI-CAD", - "symbol": "yfi", - "name": "yearnfinance CAD" - }, - { - "id": "YFI-CNY", - "symbol": "yfi", - "name": "yearnfinance CNY" - }, - { - "id": "YFI-ETH", - "symbol": "yfi", - "name": "yearnfinance ETH" - }, - { - "id": "YFI-EUR", - "symbol": "yfi", - "name": "yearnfinance EUR" - }, - { - "id": "YFI-GBP", - "symbol": "yfi", - "name": "yearnfinance GBP" - }, - { - "id": "YFI-INR", - "symbol": "yfi", - "name": "yearnfinance INR" - }, - { - "id": "YFI-JPY", - "symbol": "yfi", - "name": "yearnfinance JPY" - }, - { - "id": "YFI-KRW", - "symbol": "yfi", - "name": "yearnfinance KRW" - }, - { - "id": "YFI-RUB", - "symbol": "yfi", - "name": "yearnfinance RUB" - }, - { - "id": "YFI-USD", - "symbol": "yfi", - "name": "yearnfinance USD" - }, - { - "id": "YOYOW-CAD", - "symbol": "yoyow", - "name": "YOYOW CAD" - }, - { - "id": "YOYOW-CNY", - "symbol": "yoyow", - "name": "YOYOW CNY" - }, - { - "id": "YOYOW-ETH", - "symbol": "yoyow", - "name": "YOYOW ETH" - }, - { - "id": "YOYOW-EUR", - "symbol": "yoyow", - "name": "YOYOW EUR" - }, - { - "id": "YOYOW-GBP", - "symbol": "yoyow", - "name": "YOYOW GBP" - }, - { - "id": "YOYOW-INR", - "symbol": "yoyow", - "name": "YOYOW INR" - }, - { - "id": "YOYOW-JPY", - "symbol": "yoyow", - "name": "YOYOW JPY" - }, - { - "id": "YOYOW-KRW", - "symbol": "yoyow", - "name": "YOYOW KRW" - }, - { - "id": "YOYOW-RUB", - "symbol": "yoyow", - "name": "YOYOW RUB" - }, - { - "id": "YOYOW-USD", - "symbol": "yoyow", - "name": "YOYOW USD" - }, - { - "id": "ZANO-CAD", - "symbol": "zano", - "name": "Zano CAD" - }, - { - "id": "ZANO-CNY", - "symbol": "zano", - "name": "Zano CNY" - }, - { - "id": "ZANO-ETH", - "symbol": "zano", - "name": "Zano ETH" - }, - { - "id": "ZANO-EUR", - "symbol": "zano", - "name": "Zano EUR" - }, - { - "id": "ZANO-GBP", - "symbol": "zano", - "name": "Zano GBP" - }, - { - "id": "ZANO-INR", - "symbol": "zano", - "name": "Zano INR" - }, - { - "id": "ZANO-JPY", - "symbol": "zano", - "name": "Zano JPY" - }, - { - "id": "ZANO-KRW", - "symbol": "zano", - "name": "Zano KRW" - }, - { - "id": "ZANO-RUB", - "symbol": "zano", - "name": "Zano RUB" - }, - { - "id": "ZANO-USD", - "symbol": "zano", - "name": "Zano USD" - }, - { - "id": "ZEC-BTC", - "symbol": "zec", - "name": "Zcash BTC" - }, - { - "id": "ZEC-CAD", - "symbol": "zec", - "name": "Zcash CAD" - }, - { - "id": "ZEC-ETH", - "symbol": "zec", - "name": "Zcash ETH" - }, - { - "id": "ZEC-EUR", - "symbol": "zec", - "name": "Zcash EUR" - }, - { - "id": "ZEC-GBP", - "symbol": "zec", - "name": "Zcash GBP" - }, - { - "id": "ZEC-INR", - "symbol": "zec", - "name": "Zcash INR" - }, - { - "id": "ZEC-JPY", - "symbol": "zec", - "name": "Zcash JPY" - }, - { - "id": "ZEC-KRW", - "symbol": "zec", - "name": "Zcash KRW" - }, - { - "id": "ZEC-RUB", - "symbol": "zec", - "name": "Zcash RUB" - }, - { - "id": "ZEC-USD", - "symbol": "zec", - "name": "Zcash USD" - }, - { - "id": "ZEL-CAD", - "symbol": "zel", - "name": "ZelCash CAD" - }, - { - "id": "ZEL-CNY", - "symbol": "zel", - "name": "ZelCash CNY" - }, - { - "id": "ZEL-ETH", - "symbol": "zel", - "name": "ZelCash ETH" - }, - { - "id": "ZEL-EUR", - "symbol": "zel", - "name": "ZelCash EUR" - }, - { - "id": "ZEL-GBP", - "symbol": "zel", - "name": "ZelCash GBP" - }, - { - "id": "ZEL-INR", - "symbol": "zel", - "name": "ZelCash INR" - }, - { - "id": "ZEL-JPY", - "symbol": "zel", - "name": "ZelCash JPY" - }, - { - "id": "ZEL-KRW", - "symbol": "zel", - "name": "ZelCash KRW" - }, - { - "id": "ZEL-RUB", - "symbol": "zel", - "name": "ZelCash RUB" - }, - { - "id": "ZEL-USD", - "symbol": "zel", - "name": "ZelCash USD" - }, - { - "id": "ZEN-BTC", - "symbol": "zen", - "name": "Horizen BTC" - }, - { - "id": "ZEN-CAD", - "symbol": "zen", - "name": "Horizen CAD" - }, - { - "id": "ZEN-CNY", - "symbol": "zen", - "name": "Horizen CNY" - }, - { - "id": "ZEN-ETH", - "symbol": "zen", - "name": "Horizen ETH" - }, - { - "id": "ZEN-EUR", - "symbol": "zen", - "name": "Horizen EUR" - }, - { - "id": "ZEN-GBP", - "symbol": "zen", - "name": "Horizen GBP" - }, - { - "id": "ZEN-INR", - "symbol": "zen", - "name": "Horizen INR" - }, - { - "id": "ZEN-KRW", - "symbol": "zen", - "name": "Horizen KRW" - }, - { - "id": "ZEN-RUB", - "symbol": "zen", - "name": "Horizen RUB" - }, - { - "id": "ZEN-USD", - "symbol": "zen", - "name": "Horizen USD" - }, - { - "id": "ZIL-AUD", - "symbol": "zil", - "name": "Zilliqa AUD" - }, - { - "id": "ZIL-BTC", - "symbol": "zil", - "name": "Zilliqa BTC" - }, - { - "id": "ZIL-CAD", - "symbol": "zil", - "name": "Zilliqa CAD" - }, - { - "id": "ZIL-CNY", - "symbol": "zil", - "name": "Zilliqa CNY" - }, - { - "id": "ZIL-ETH", - "symbol": "zil", - "name": "Zilliqa ETH" - }, - { - "id": "ZIL-EUR", - "symbol": "zil", - "name": "Zilliqa EUR" - }, - { - "id": "ZIL-GBP", - "symbol": "zil", - "name": "Zilliqa GBP" - }, - { - "id": "ZIL-INR", - "symbol": "zil", - "name": "Zilliqa INR" - }, - { - "id": "ZIL-JPY", - "symbol": "zil", - "name": "Zilliqa JPY" - }, - { - "id": "ZIL-KRW", - "symbol": "zil", - "name": "Zilliqa KRW" - }, - { - "id": "ZIL-RUB", - "symbol": "zil", - "name": "Zilliqa RUB" - }, - { - "id": "ZIL-USD", - "symbol": "zil", - "name": "Zilliqa USD" - }, - { - "id": "ZNN-CAD", - "symbol": "znn", - "name": "Zenon CAD" - }, - { - "id": "ZNN-CNY", - "symbol": "znn", - "name": "Zenon CNY" - }, - { - "id": "ZNN-ETH", - "symbol": "znn", - "name": "Zenon ETH" - }, - { - "id": "ZNN-EUR", - "symbol": "znn", - "name": "Zenon EUR" - }, - { - "id": "ZNN-GBP", - "symbol": "znn", - "name": "Zenon GBP" - }, - { - "id": "ZNN-INR", - "symbol": "znn", - "name": "Zenon INR" - }, - { - "id": "ZNN-JPY", - "symbol": "znn", - "name": "Zenon JPY" - }, - { - "id": "ZNN-KRW", - "symbol": "znn", - "name": "Zenon KRW" - }, - { - "id": "ZNN-RUB", - "symbol": "znn", - "name": "Zenon RUB" - }, - { - "id": "ZNN-USD", - "symbol": "znn", - "name": "Zenon USD" - }, - { - "id": "ZRX-CAD", - "symbol": "zrx", - "name": "0x CAD" - }, - { - "id": "ZRX-CNY", - "symbol": "zrx", - "name": "0x CNY" - }, - { - "id": "ZRX-ETH", - "symbol": "zrx", - "name": "0x ETH" - }, - { - "id": "ZRX-EUR", - "symbol": "zrx", - "name": "0x EUR" - }, - { - "id": "ZRX-GBP", - "symbol": "zrx", - "name": "0x GBP" - }, - { - "id": "ZRX-INR", - "symbol": "zrx", - "name": "0x INR" - }, - { - "id": "ZRX-JPY", - "symbol": "zrx", - "name": "0x JPY" - }, - { - "id": "ZRX-KRW", - "symbol": "zrx", - "name": "0x KRW" - }, - { - "id": "ZRX-RUB", - "symbol": "zrx", - "name": "0x RUB" - }, - { - "id": "ZRX-USD", - "symbol": "zrx", - "name": "0x USD" - }, - { - "id": "ZVC-BTC", - "symbol": "zvc", - "name": "ZVCHAIN BTC" - }, - { - "id": "ZVC-CAD", - "symbol": "zvc", - "name": "ZVCHAIN CAD" - }, - { - "id": "ZVC-CNY", - "symbol": "zvc", - "name": "ZVCHAIN CNY" - }, - { - "id": "ZVC-ETH", - "symbol": "zvc", - "name": "ZVCHAIN ETH" - }, - { - "id": "ZVC-EUR", - "symbol": "zvc", - "name": "ZVCHAIN EUR" - }, - { - "id": "ZVC-GBP", - "symbol": "zvc", - "name": "ZVCHAIN GBP" - }, - { - "id": "ZVC-INR", - "symbol": "zvc", - "name": "ZVCHAIN INR" - }, - { - "id": "ZVC-JPY", - "symbol": "zvc", - "name": "ZVCHAIN JPY" - }, - { - "id": "ZVC-KRW", - "symbol": "zvc", - "name": "ZVCHAIN KRW" - }, - { - "id": "ZVC-RUB", - "symbol": "zvc", - "name": "ZVCHAIN RUB" - }, - { - "id": "ZYN-CAD", - "symbol": "zyn", - "name": "Zynecoin CAD" - }, - { - "id": "ZYN-CNY", - "symbol": "zyn", - "name": "Zynecoin CNY" - }, - { - "id": "ZYN-ETH", - "symbol": "zyn", - "name": "Zynecoin ETH" - }, - { - "id": "ZYN-EUR", - "symbol": "zyn", - "name": "Zynecoin EUR" - }, - { - "id": "ZYN-GBP", - "symbol": "zyn", - "name": "Zynecoin GBP" - }, - { - "id": "ZYN-INR", - "symbol": "zyn", - "name": "Zynecoin INR" - }, - { - "id": "ZYN-JPY", - "symbol": "zyn", - "name": "Zynecoin JPY" - }, - { - "id": "ZYN-KRW", - "symbol": "zyn", - "name": "Zynecoin KRW" - }, - { - "id": "ZYN-RUB", - "symbol": "zyn", - "name": "Zynecoin RUB" - }, - { - "id": "ZYN-USD", - "symbol": "zyn", - "name": "Zynecoin USD" - } - ] diff --git a/openbb_terminal/cryptocurrency/dataframe_helpers.py b/openbb_terminal/cryptocurrency/dataframe_helpers.py deleted file mode 100644 index c52e0e16cf1..00000000000 --- a/openbb_terminal/cryptocurrency/dataframe_helpers.py +++ /dev/null @@ -1,207 +0,0 @@ -"""Dataframe helpers""" - -__docformat__ = "numpy" - -import math -import re -import textwrap -from typing import Any, Optional, Union - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.helper_funcs import lambda_long_number_format - - -def wrap_text_in_df(df: pd.DataFrame, w: int = 55) -> pd.DataFrame: # pragma: no cover - """ - Parameters - ---------- - df: pd.DataFrame - Data Frame with some data - w: int - length of text in column after which text is wrapped into new line - - Returns - ------- - pd.DataFrame - """ - - return df.applymap( - lambda x: "\n".join(textwrap.wrap(x, width=w)) if isinstance(x, str) else x - ) - - -def percent_to_float(s: str) -> float: - """Helper method to replace string pct like "123.56%" to float 1.2356 - Parameters - ---------- - s: string - string to replace - Returns - ------- - float - """ - - s = str(float(s.rstrip("%"))) - i = s.find(".") - if i == -1: - return int(s) / 100 - if s.startswith("-"): - return -percent_to_float(s.lstrip("-")) - s = s.replace(".", "") - i -= 2 - if i < 0: - return float("." + "0" * abs(i) + s) - return float(s[:i] + "." + s[i:]) - - -def create_df_index(df: pd.DataFrame, name: str = "rank") -> None: - """Helper method that creates new index for given data frame, with provided index name - Parameters - ---------- - df: - pd.DataFrame - name: str - index name - """ - - df.index = df.index + 1 - df.reset_index(inplace=True) - df.rename(columns={"index": name}, inplace=True) - - -def lambda_long_number_format_with_type_check(x: Union[int, float]) -> Union[str, Any]: - """Helper which checks if type of x is int or float and it's smaller then 10^18. - If yes it apply long_num_format - - Parameters - ---------- - x: int/float - number to apply long_number_format method - Returns - ------- - Union[str, Any] - """ - - if get_current_user().preferences.USE_INTERACTIVE_DF: - return x - if isinstance(x, (int, float)) and x < 10**18: - return lambda_long_number_format(x) - return x - - -def lambda_replace_underscores_in_column_names(string: str) -> str: - return string.title().replace("_", " ") - - -def lambda_very_long_number_formatter(num: Union[str, int, float]) -> str: - """Apply nice string format for very big numbers like Trillions, Quadrillions, Billions etc. - - Parameters - ---------- - num: Union[str, int, float] - number to format - Returns - ------- - str: - formatted number - """ - - if get_current_user().preferences.USE_INTERACTIVE_DF: - return num # type:ignore - if isinstance(num, str): - try: - num = float(num) - except (TypeError, ValueError): - return str(num) - - if isinstance(num, (int, float)): - if math.isnan(num): - num = 0 - num = int(num) - magnitude = 0 - while abs(num) >= 1000 and magnitude <= 3: - magnitude += 1 - num /= 1000.0 - num = round(num, 1) - formatted_num = f"{num:f}".rstrip("0").rstrip(".") - - return f'{formatted_num}{["", "K", "M", "B", "T"][magnitude]}' - - return num - - -def prettify_paragraph(text): - # Add tab to the beginning of paragraph - text = "\t" + text - pat = "(? list: - """Helper method that change column names into more human readable format. E.g. - - tradeAmount => Trade amount, - - tokenValue => Token value - - mediumGasPrice => Medium Gas Price - - Parameters - ---------- - columns: list - list of column names - - Returns - ------- - list with reformatted columns - """ - return [" ".join(re.findall(".[^A-Z]*", val)).capitalize() for val in columns] - - -def denominate_number( - number: Any, divider: int = 1000000, round_digits: Optional[int] = 4 -) -> float: - """Denominate numbers base on provided divider and round number by provided digit - - Parameters - ---------- - number: Any - value to round - divider: int - divide by value - round_digits: - round number to n digits - Returns - ------- - float: - denominated number - """ - - if round_digits: - return round(float(number) / divider, round_digits) - return round(float(number) / divider) - - -def lambda_replace_unicode(x: Any) -> Any: - """Replace unicode characters to ? - - Parameters - ---------- - x: Any - value to replace unicode chars - - Returns - ------- - Any - replaced value - """ - - if isinstance(x, str): - return x.encode("ascii", "replace").decode() - return x diff --git a/openbb_terminal/cryptocurrency/defi/__init__.py b/openbb_terminal/cryptocurrency/defi/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/cryptocurrency/defi/coindix_model.py b/openbb_terminal/cryptocurrency/defi/coindix_model.py deleted file mode 100644 index 477e11f8b70..00000000000 --- a/openbb_terminal/cryptocurrency/defi/coindix_model.py +++ /dev/null @@ -1,175 +0,0 @@ -"""Coindix model""" - -__docformat__ = "numpy" - -import logging -from typing import Optional - -import pandas as pd -import urllib3 - -from openbb_terminal import rich_config -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - get_user_agent, - lambda_long_number_format, - request, -) - -urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - -logger = logging.getLogger(__name__) - -VAULTS_FILTERS = ["name", "chain", "protocol", "apy", "tvl", "link"] -CHAINS = [ - "ethereum", - "polygon", - "avalanche", - "bsc", - "terra", - "fantom", - "moonriver", - "celo", - "heco", - "okex", - "cronos", - "arbitrum", - "eth", - "harmony", - "fuse", - "defichain", - "solana", - "optimism", - "kusama", - "metis", - "osmosis", -] -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", - "osmosis", - "tulip", -] -VAULT_KINDS = [ - "lp", - "single", - "noimploss", - "stable", -] - - -@log_start_end(log=logger) -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()} - - -@log_start_end(log=logger) -def get_defi_vaults( - chain: Optional[str] = None, - protocol: Optional[str] = None, - kind: Optional[str] = None, - ascend: bool = True, - sortby: str = "apy", -) -> 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. - """ - - headers = {"User-Agent": get_user_agent()} - params = _prepare_params(chain=chain, protocol=protocol, kind=kind) - response = request( - "https://apiv2.coindix.com/search", headers=headers, params=params, verify=False - ) - 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] - except Exception as e: - logger.exception(e) - raise ValueError(f"Invalid Response: {response.text}") from e - - df = df.sort_values(by=sortby, ascending=ascend).fillna("NA") - - if rich_config.USE_COLOR and not get_current_user().preferences.USE_INTERACTIVE_DF: - df["tvl"] = df["tvl"].apply(lambda x: lambda_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) - return df diff --git a/openbb_terminal/cryptocurrency/defi/coindix_view.py b/openbb_terminal/cryptocurrency/defi/coindix_view.py deleted file mode 100644 index 90547666109..00000000000 --- a/openbb_terminal/cryptocurrency/defi/coindix_view.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Coindix view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.defi import coindix_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_defi_vaults( - chain: Optional[str] = None, - protocol: Optional[str] = None, - kind: Optional[str] = None, - limit: int = 10, - sortby: str = "apy", - ascend: bool = True, - link: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing 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'] - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: 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, sortby=sortby, ascend=ascend - ) - if df.empty: - console.print( - f"Couldn't find any vaults for " - f"{'' if not chain else 'chain: ' + chain}" - f"{'' if not protocol else ', protocol: ' + protocol}" - f"{'' if not kind else ', kind:' + kind}" - ) - return - - if link is True: - df.drop("Link", axis=1, inplace=True) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Top DeFi Vaults", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "vaults", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/defi/cryptosaurio_model.py b/openbb_terminal/cryptocurrency/defi/cryptosaurio_model.py deleted file mode 100644 index ad9d442bd50..00000000000 --- a/openbb_terminal/cryptocurrency/defi/cryptosaurio_model.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Cryptosaurio model""" - -__docformat__ = "numpy" - -import logging -from typing import Tuple - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -api_url = "https://barney.cryptosaurio.com" - - -@log_start_end(log=logger) -def get_anchor_data(address: str = "") -> Tuple[pd.DataFrame, pd.DataFrame, str]: - """Returns anchor protocol earnings data of a certain terra address - [Source: https://cryptosaurio.com/] - - Parameters - ---------- - address : str - Terra address. Valid terra addresses start with 'terra' - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame, str] - - pd.DataFrame: Earnings over time in UST - - pd.DataFrame: History of transactions - - str: Overall statistics - """ - - if not address.startswith("terra"): - console.print( - "Select a valid address. Valid terra addresses start with 'terra'" - ) - return pd.DataFrame(), pd.DataFrame(), "" - - response = request(f"{api_url}/get-anchor-protocol-data-v2/{address}") - if response.status_code != 200: - console.print(f"Status code: {response.status_code}. Reason: {response.reason}") - return pd.DataFrame(), pd.DataFrame(), "" - - data = response.json() - df = pd.DataFrame(reversed(data["historicalData"])) - df["time"] = pd.to_datetime(df["time"]) - df["yield"] = df["yield"].astype("float64") - df_deposits = pd.DataFrame(data["deposits"], columns=["out", "fee", "time"]) - df_deposits["out"] = df_deposits["out"].astype("float64") - df_deposits["fee"] = df_deposits["fee"].astype("float64") - df_deposits["time"] = pd.to_datetime(df_deposits["time"]) - df_deposits["Type"] = df_deposits.apply( - lambda row: "Deposit" if row.out > 0 else "Withdrawal", axis=1 - ) - df_deposits.columns = ["Amount [UST]", "Fee [UST]", "Date", "Type"] - df_deposits = df_deposits[["Type", "Amount [UST]", "Fee [UST]", "Date"]] - - stats_str = f"""Current anchor APY is {data['currentRate']}% -Deposit amount in Anchor Earn of address {address} is {data["totalYield"]["ustHoldings"]} UST. -You already earned [bold]{df.iloc[-1, 1]}[/bold] UST in Anchor Earn. -Your deposit is generating approximately: -- {data["estimatedYield"]["perHour"]} UST hourly -- {data["estimatedYield"]["perDay"]} UST daily -- {data["estimatedYield"]["perWeek"]} UST weekly -- {data["estimatedYield"]["perMonth"]} UST monthly -- {data["estimatedYield"]["perYear"]} UST yearly""" - - return df, df_deposits, stats_str diff --git a/openbb_terminal/cryptocurrency/defi/cryptosaurio_view.py b/openbb_terminal/cryptocurrency/defi/cryptosaurio_view.py deleted file mode 100644 index ad23920d318..00000000000 --- a/openbb_terminal/cryptocurrency/defi/cryptosaurio_view.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Cryptosaurio View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.defi import cryptosaurio_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_anchor_data( - address: str = "", - export: str = "", - sheet_name: Optional[str] = None, - show_transactions: bool = False, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots anchor protocol earnings data of a certain terra address - [Source: https://cryptosaurio.com/] - - Parameters - ---------- - asset : str - Terra asset {ust,luna,sdt} - address : str - Terra address. Valid terra addresses start with 'terra' - show_transactions : bool - Flag to show history of transactions in Anchor protocol for address. Default False - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df, df_deposits, stats_str = cryptosaurio_model.get_anchor_data(address=address) - - if stats_str: - console.print(f"\n{stats_str}\n") - - if not df_deposits.empty and show_transactions: - print_rich_table( - df_deposits, - headers=list(df_deposits.columns), - show_index=False, - title="Transactions history in Anchor Earn", - export=bool(export), - ) - - if not df.empty: - fig = OpenBBFigure(yaxis_title="Earnings Value [UST]") - fig.set_title("Earnings in Anchor Earn") - - fig.add_scatter(x=df["time"], y=df["yield"], name="Earnings") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "anchor", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - return None diff --git a/openbb_terminal/cryptocurrency/defi/defi_controller.py b/openbb_terminal/cryptocurrency/defi/defi_controller.py deleted file mode 100644 index 160b7599db5..00000000000 --- a/openbb_terminal/cryptocurrency/defi/defi_controller.py +++ /dev/null @@ -1,364 +0,0 @@ -"""Defi Controller Module""" - -__docformat__ = "numpy" - -# pylint: disable=C0302 - -import argparse -import logging -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.defi import ( - coindix_model, - coindix_view, - llama_model, - llama_view, - substack_view, -) -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class DefiController(BaseController): - """Defi Controller class""" - - CHOICES_COMMANDS = [ - "ldapps", - "gdapps", - "stvl", - "dtvl", - "newsletter", - "vaults", - ] - - PATH = "/crypto/defi/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("crypto/defi/") - mt.add_cmd("newsletter") - mt.add_cmd("vaults") - mt.add_cmd("ldapps") - mt.add_cmd("gdapps") - mt.add_cmd("stvl") - mt.add_cmd("dtvl") - console.print(text=mt.menu_text, menu="Cryptocurrency - Decentralized Finance") - - @log_start_end(log=logger) - def call_gdapps(self, other_args: List[str]): - """Process gdapps command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="gdapps", - description=""" - Display top dApps (in terms of TVL) grouped by chain. - [Source: https://docs.llama.fi/api] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of top dApps to display", - default=40, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - llama_view.display_grouped_defi_protocols(limit=ns_parser.limit) - - @log_start_end(log=logger) - def call_dtvl(self, other_args: List[str]): - """Process dtvl command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dtvl", - description=""" - Displays historical TVL of different dApps. - [Source: https://docs.llama.fi/api] - """, - ) - parser.add_argument( - "-d", - "--dapps", - dest="dapps", - choices=["anchor", "sushiswap", "pancakeswap"], - required="-h" not in other_args, - help="dApps to search historical TVL. Should be split by , e.g.: anchor,sushiswap,pancakeswap", - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-d") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - llama_view.display_historical_tvl( - dapps=ns_parser.dapps, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ldapps(self, other_args: List[str]): - """Process ldapps command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ldapps", - description=""" - Display information about listed dApps on DeFi Llama. - [Source: https://docs.llama.fi/api] - """, - ) - parser.add_argument( - "-l", - "--limit", - 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: tvl", - default="tvl", - choices=llama_model.LLAMA_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - - parser.add_argument( - "--desc", - action="store_true", - help="Flag to display description of protocol", - dest="description", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - llama_view.display_defi_protocols( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - description=ns_parser.description, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_stvl(self, other_args: List[str]): - """Process stvl command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="stvl", - description=""" - Displays historical values of the total sum of TVLs from all listed dApps. - [Source: https://docs.llama.fi/api] - """, - ) - - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of records to display", - default=10, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - llama_view.display_defi_tvl( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_newsletter(self, other_args: List[str]): - """Process newsletter command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="newsletter", - description=""" - Display DeFi related substack newsletters. - [Source: substack.com] - """, - ) - - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of records to display", - default=10, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - substack_view.display_newsletters( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_vaults(self, other_args: List[str]): - """Process vaults 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, - choices=range(1, 1000), - metavar="TOP", - ) - 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( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-l", - "--links", - action="store_false", - help="Flag to show vault link", - dest="link", - default=True, - ) - ns_parser = self.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, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - link=ns_parser.link, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/cryptocurrency/defi/llama_model.py b/openbb_terminal/cryptocurrency/defi/llama_model.py deleted file mode 100644 index 186038279e5..00000000000 --- a/openbb_terminal/cryptocurrency/defi/llama_model.py +++ /dev/null @@ -1,209 +0,0 @@ -"""Llama model""" - -__docformat__ = "numpy" - -import logging -import textwrap -from datetime import datetime - -import numpy as np -import pandas as pd - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_replace_underscores_in_column_names, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - -API_URL = "https://api.llama.fi" - -LLAMA_FILTERS = [ - "tvl", - "symbol", - "category", - "chains", - "change_1h", - "change_1d", - "change_7d", - "name", -] - - -@log_start_end(log=logger) -def get_chains() -> pd.DataFrame: - """Returns information about chains supported by Llama.fi. - [Source: https://docs.llama.fi/api] - - Returns - ------- - pd.DataFrame - Information about chains - """ - response = request(API_URL + "/chains") - if response.status_code != 200: - raise Exception(f"Status code: {response.status_code}. Reason: {response.text}") - try: - df = pd.DataFrame(response.json()) - df.fillna(value=None, inplace=True) - df = df.set_index("name") - except Exception as e: - logger.exception("Wrong response type: %s", str(e)) - raise ValueError("Wrong response type\n") from e - return df - - -@log_start_end(log=logger) -def get_defi_protocols( - limit: int = 100, - sortby: str = "", - ascend: bool = False, - description: bool = False, - drop_chain: bool = True, -) -> pd.DataFrame: - """Returns information about listed DeFi protocols, their current TVL and changes to it in the last hour/day/week. - [Source: https://docs.llama.fi/api] - - Parameters - ---------- - limit: int - The number of dApps to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - description: bool - Flag to display description of protocol - drop_chain: bool - Whether to drop the chain column - - Returns - ------- - pd.DataFrame - Information about DeFi protocols - """ - - response = request(API_URL + "/protocols") - columns = [ - "name", - "symbol", - "category", - "chains", - "change_1h", - "change_1d", - "change_7d", - "tvl", - "url", - "description", - "chain", - ] - if response.status_code != 200: - raise Exception(f"Status code: {response.status_code}. Reason: {response.text}") - try: - df = pd.DataFrame(response.json()) - df.replace({float(np.nan): None}, inplace=True) - df["chains"] = df["chains"].apply( - lambda x: "\n".join(textwrap.wrap(", ".join(x), width=50)) - ) - df["description"] = df["description"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=70)) if isinstance(x, str) else x - ) - df = df[columns] - - except Exception as e: - logger.exception("Wrong response type: %s", str(e)) - raise ValueError("Wrong response type\n") from e - - df = df.set_index("name") - if sortby: - df = df.sort_values(by=sortby, ascending=ascend) - if drop_chain: - df = df.drop(columns="chain") - - if description: - orig = ["name", "symbol", "category", "description", "url"] - selection = [x for x in orig if x in df.columns] - df = df[selection] - else: - df.drop(["description", "url"], axis=1, inplace=True) - - df.columns = [lambda_replace_underscores_in_column_names(val) for val in df.columns] - df.rename( - columns={ - "Change 1H": "Change 1H (%)", - "Change 1D": "Change 1D (%)", - "Change 7D": "Change 7D (%)", - "Tvl": "TVL ($)", - }, - inplace=True, - ) - return df.head(limit) - - -@log_start_end(log=logger) -def get_defi_protocol(protocol: str) -> pd.DataFrame: - """Returns information about historical tvl of a defi protocol. - [Source: https://docs.llama.fi/api] - - Parameters - ---------- - protocol: str - Name of the protocol - - Returns - ------- - pd.DataFrame - Historical tvl - """ - url = f"{API_URL}/protocol/{protocol}" - r = request(url) - data = r.json() - - df = pd.DataFrame(data["tvl"]) - df.date = pd.to_datetime(df.date, unit="s") - df = df.set_index("date") - return df - - -@log_start_end(log=logger) -def get_grouped_defi_protocols( - limit: int = 50, -) -> pd.DataFrame: - """Display top dApps (in terms of TVL) grouped by chain. - [Source: https://docs.llama.fi/api] - - Parameters - ---------- - limit: int - Number of top dApps to display - - Returns - ------- - pd.DataFrame - Information about DeFi protocols grouped by chain - """ - df = get_defi_protocols(limit, drop_chain=False) - return df.groupby("Chain").size().index.values.tolist() - - -@log_start_end(log=logger) -def get_defi_tvl() -> pd.DataFrame: - """Returns historical values of the total sum of TVLs from all listed protocols. - [Source: https://docs.llama.fi/api] - - Returns - ------- - pd.DataFrame - Historical values of total sum of Total Value Locked from all listed protocols. - """ - response = request(API_URL + "/charts") - if response.status_code != 200: - raise Exception(f"Status code: {response.status_code}. Reason: {response.text}") - try: - df = pd.DataFrame(response.json()) - df["date"] = df["date"].apply(lambda x: datetime.fromtimestamp(int(x)).date()) - return df - except Exception as e: - logger.exception("Wrong response data: %s", str(e)) - raise ValueError("Wrong response data") from e diff --git a/openbb_terminal/cryptocurrency/defi/llama_view.py b/openbb_terminal/cryptocurrency/defi/llama_view.py deleted file mode 100644 index d4deed45456..00000000000 --- a/openbb_terminal/cryptocurrency/defi/llama_view.py +++ /dev/null @@ -1,225 +0,0 @@ -"""Llama View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.cryptocurrency_helpers import read_data_file -from openbb_terminal.cryptocurrency.defi import llama_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_grouped_defi_protocols( - limit: int = 50, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots top dApps (in terms of TVL) grouped by chain. - [Source: https://docs.llama.fi/api] - - Parameters - ---------- - num: int - Number of top dApps to display - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = llama_model.get_defi_protocols(limit, drop_chain=False) - - df["TVL ($)"] = df["TVL ($)"].apply(lambda x: lambda_long_number_format(x)) - - chains = llama_model.get_grouped_defi_protocols(limit) - - fig = OpenBBFigure( - xaxis_title="Total Value Locked ($)", - yaxis_title="Decentralized Application Name", - ) - fig.set_title(f"Top {limit} dApp TVL grouped by chain") - - colors = iter(theme.get_colors(reverse=True)) - - for chain in chains: - chain_filter = df.loc[df.Chain == chain] - fig.add_bar( - y=chain_filter.index, - x=chain_filter["TVL ($)"], - name=chain, - orientation="h", - marker_color=next(colors, "#B6A9CB"), - ) - - fig.update_layout( - margin=dict(l=150), - yaxis=dict(side="left", tickfont=dict(size=8)), - legend=dict(yanchor="bottom", y=0, xanchor="right", x=1), - ) - fig.update_xaxes(tickvals=list(range(0, 40)), ticktext=list(range(0, 40))) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gdapps", - chains, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_defi_protocols( - sortby: str, - limit: int = 20, - ascend: bool = False, - description: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing information about listed DeFi protocols, their current TVL and changes to it in - the last hour/day/week. [Source: https://docs.llama.fi/api] - - Parameters - ---------- - sortby: str - Key by which to sort data - limit: int - Number of records to display - ascend: bool - Flag to sort data descending - description: bool - Flag to display description of protocol - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = llama_model.get_defi_protocols(limit, sortby, ascend, description) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ldapps", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_historical_tvl( - dapps: str, - export: Optional[str] = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots historical TVL of different dApps - [Source: https://docs.llama.fi/api] - - Parameters - ---------- - dapps: str - dApps to search historical TVL. Should be split by , e.g.: anchor,sushiswap,pancakeswap - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - fig = OpenBBFigure(yaxis_title="Total Value Locked ($)") - fig.set_title("TVL in dApps") - - available_protocols = read_data_file("defillama_dapps.json") - dapp: str = "" - if isinstance(available_protocols, dict): - for dapp in dapps.split(","): - if dapp in available_protocols: - df = llama_model.get_defi_protocol(dapp) - df = df.query("`totalLiquidityUSD` > 0") - df.index = pd.DatetimeIndex(df.index.strftime("%Y-%m-%d")) - if not df.empty: - fig.add_scatter( - x=df.index, - y=df["totalLiquidityUSD"].values, - name=available_protocols[dapp], - ) - else: - console.print(f"{dapp} not found\n") - if export and export != "": - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"dtvl_{dapp}", - df, - sheet_name, - fig, - ) - return None - return fig.show(external=external_axes) - return None - - -@log_start_end(log=logger) -def display_defi_tvl( - limit: int = 5, - export: Optional[str] = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots historical values of the total sum of TVLs from all listed protocols. - [Source: https://docs.llama.fi/api] - - Parameters - ---------- - limit: int - Number of records to display, by default 5 - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - fig = OpenBBFigure(yaxis_title="Total Value Locked ($)") - fig.set_title("Total Value Locked in DeFi") - - df = llama_model.get_defi_tvl() - df_data = df.copy() - df = df.tail(limit) - - if export and export != "": - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "stvl", - df_data, - sheet_name, - fig, - ) - return None - fig.add_scatter(x=df["date"], y=df["totalLiquidityUSD"], name="TVL") - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/defi/smartstake_model.py b/openbb_terminal/cryptocurrency/defi/smartstake_model.py deleted file mode 100644 index 217f51c3644..00000000000 --- a/openbb_terminal/cryptocurrency/defi/smartstake_model.py +++ /dev/null @@ -1,72 +0,0 @@ -"""SmartStake Model""" - -__docformat__ = "numpy" - -from typing import Dict, Union - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - - -@check_api_key(["API_SMARTSTAKE_KEY", "API_SMARTSTAKE_TOKEN"]) -def get_luna_supply_stats( - supply_type: str = "lunaSupplyChallengeStats", days: int = 30 -) -> pd.DataFrame: - """Get supply history of the Terra ecosystem - - Source: [Smartstake.io] - - Parameters - ---------- - supply_type: str - Supply type to unpack json - days: int - Day count to fetch data - - Returns - ------- - pd.DataFrame - Dataframe of supply history data - """ - - current_user = get_current_user() - - payload: Dict[str, Union[int, str]] = { - "type": "history", - "dayCount": days, - "key": current_user.credentials.API_SMARTSTAKE_KEY, - "token": current_user.credentials.API_SMARTSTAKE_TOKEN, - } - - response = request( - "https://prod.smartstakeapi.com/listData?app=TERRA", - params=payload, - ) - response_json = response.json() - - df = pd.DataFrame() - - if "errors" in response_json: - if "DENIED" in response_json["errors"]: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(response_json["errors"]) - - else: - result = response_json[supply_type] - - # check if result is not empty - if result: - df = pd.DataFrame(result) - df = df.set_index("title") - df.index.name = "timestamp_date" - df.index = pd.to_datetime(df.index) - - else: - console.print("No data found") - - return df diff --git a/openbb_terminal/cryptocurrency/defi/smartstake_view.py b/openbb_terminal/cryptocurrency/defi/smartstake_view.py deleted file mode 100644 index ffc1bc17e68..00000000000 --- a/openbb_terminal/cryptocurrency/defi/smartstake_view.py +++ /dev/null @@ -1,119 +0,0 @@ -"""SentimentInvestor View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.defi import smartstake_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -# pylint: disable=E1101 - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_SMARTSTAKE_KEY", "API_SMARTSTAKE_TOKEN"]) -def display_luna_circ_supply_change( - days: int = 30, - export: str = "", - sheet_name: Optional[str] = None, - supply_type: str = "lunaSupplyChallengeStats", - limit: int = 5, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots and prints table showing Luna circulating supply stats - - Parameters - ---------- - days: int - Number of days - supply_type: str - Supply type to unpack json - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export type - limit: int - Number of results display on the terminal - Default: 5 - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = smartstake_model.get_luna_supply_stats(supply_type, days) - - if df.empty: - return None - - fig = OpenBBFigure(xaxis_title="Time", yaxis_title="Luna Circulating Supply [M]") - fig.set_title("Luna Circulating Supply Changes (In Millions)") - - fig.add_scatter( - x=df.index, - y=df["circulatingSupplyInMil"], - mode="lines", - name="Circulating Supply", - line=dict(color="black"), - ) - fig.add_scatter( - x=df.index, - y=df["liquidCircSupplyInMil"], - mode="lines", - name="Liquid Circulating Supply", - line=dict(color="red"), - ) - fig.add_scatter( - x=df.index, - y=df["stakeFromCircSupplyInMil"], - mode="lines", - name="Stake of Supply", - line=dict(color="green"), - ) - fig.add_scatter( - x=df.index, - y=df["recentTotalLunaBurntInMil"], - mode="lines", - name="Supply Reduction (Luna Burnt)", - line=dict(color="blue"), - ) - - RAW_COLS = [ - "circulatingSupplyInMil", - "liquidCircSupplyInMil", - "circSupplyChangeInMil", - "recentTotalLunaBurntInMil", - ] - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "lcsc", - df[RAW_COLS], - sheet_name, - fig, - ) - - df.index = df.index.strftime("%Y-%m-%d") - df = df.sort_index(ascending=False) - - print_rich_table( - df[RAW_COLS], - headers=[ - "Circ Supply", - "Liquid Circ Supply", - "Supply Change", - "Supply Reduction (Luna Burnt)", - ], - show_index=True, - index_name="Time", - title="Luna Circulating Supply Changes (in Millions)", - export=bool(export), - limit=limit, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/defi/substack_model.py b/openbb_terminal/cryptocurrency/defi/substack_model.py deleted file mode 100644 index aac82377cf5..00000000000 --- a/openbb_terminal/cryptocurrency/defi/substack_model.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Substack model""" - -__docformat__ = "numpy" - -import concurrent.futures -import logging -import textwrap -from typing import List - -import pandas as pd -from bs4 import BeautifulSoup -from dateutil import parser - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def scrape_substack_rss(url: str, limit: int = 10) -> List[List[str]]: - """Helper method to scrape newsletters from a substack rss feed. - [Source: substack.com] - - Parameters - ---------- - url: str - Url to newsletter on substack domain. - - Returns - ------- - List[List[str]] - List of lists containing: - - title of newsletter - - url to newsletter - - str datetime of newsletter [Format: "%Y-%m-%d %H:%M:%S"] - """ - - req = request(url) - soup = BeautifulSoup(req.text, features="xml") - results: List[List[str]] = [] - rss = soup.find("rss") - if rss: - posts = rss.find_all("item")[:limit] - for post in posts: - title: str = post.title.text - post_url: str = post.link.text - time_str = post.pubDate.text.split(" (")[0] - time: str = time_str - results.append([title, post_url, time]) - return results - - -@log_start_end(log=logger) -def get_newsletters() -> pd.DataFrame: - """Scrape all substack newsletters from url list. - [Source: substack.com] - - Returns - ------- - pd.DataFrame - DataFrame with recent news from most popular DeFi related newsletters. - """ - - urls_rss = [ - "https://kermankohli.substack.com/feed", - "https://thedefiant.io/api/feed", - "https://thedailygwei.substack.com/feed", - "https://todayindefi.substack.com/feed", - "https://defislate.substack.com/feed", - ] - - threads = len(urls_rss) - newsletters = [] - with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor: - for newsletter in executor.map(scrape_substack_rss, urls_rss): - try: - newsletters.append(pd.DataFrame(newsletter)) - except KeyError as e: - logger.exception(str(e)) - console.print(e, "\n") - continue - - df = pd.concat(newsletters, ignore_index=True) - df.columns = ["Title", "Link", "Date"] - - df["Title"] = df["Title"].apply(lambda x: "".join(i for i in x if ord(i) < 128)) - df["Date"] = df["Date"].apply( - lambda x: parser.parse(x).strftime("%Y-%m-%d %H:%M:%S") - ) - df["Title"] = df["Title"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=50)) if isinstance(x, str) else x - ) - return ( - df[["Title", "Date", "Link"]] - .sort_values(by="Date", ascending=False) - .reset_index(drop="index") - ) diff --git a/openbb_terminal/cryptocurrency/defi/substack_view.py b/openbb_terminal/cryptocurrency/defi/substack_view.py deleted file mode 100644 index e0aae153648..00000000000 --- a/openbb_terminal/cryptocurrency/defi/substack_view.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Substack View""" - -__docformat__ = "numpy" - - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.defi import substack_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_newsletters( - limit: int = 10, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing DeFi related substack newsletters. - [Source: substack.com] - - Parameters - ---------- - limit: int - Number of records to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = substack_model.get_newsletters() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Substack Newsletters", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "newsletter", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/defi/terraengineer_model.py b/openbb_terminal/cryptocurrency/defi/terraengineer_model.py deleted file mode 100644 index a344ce19d79..00000000000 --- a/openbb_terminal/cryptocurrency/defi/terraengineer_model.py +++ /dev/null @@ -1,77 +0,0 @@ -"""Terra Engineer model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - -api_url = "https://terra.engineer/en" - -ASSETS = ["ust", "luna", "sdt"] - - -@log_start_end(log=logger) -def get_history_asset_from_terra_address( - asset: str = "ust", address: str = "terra1tmnqgvg567ypvsvk6rwsga3srp7e3lg6u0elp8" -) -> pd.DataFrame: - """Returns historical data of an asset in a certain terra address - [Source: https://terra.engineer/] - - Parameters - ---------- - asset : str - Terra asset {ust,luna,sdt} - address : str - Terra address. Valid terra addresses start with 'terra' - - Returns - ------- - pd.DataFrame - historical data - """ - - if asset.lower() not in ASSETS: - raise Exception(f"Select a valid asset from {', '.join(ASSETS)}") # noqa: S608 - - if not address.startswith("terra"): - raise Exception( - "Select a valid address. Valid terra addresses start with 'terra'" - ) - - response = request( - f"{api_url}/terra_addresses/{address}/show_snapshot_data.json?asset={asset.lower()}" - ) - if response.status_code != 200: - raise Exception(f"Status code: {response.status_code}. Reason: {response.text}") - - data = response.json() - if data[0]["data"]: - df = pd.DataFrame(data[0]["data"]) - df["x"] = pd.to_datetime(df["x"]) - else: - df = pd.DataFrame() - - return df - - -@log_start_end(log=logger) -def get_anchor_yield_reserve() -> pd.DataFrame: - """Displays the 30-day history of the Anchor Yield Reserve. - [Source: https://terra.engineer/] - - Returns - ------- - pd.DataFrame - Dataframe containing historical data - """ - - df = get_history_asset_from_terra_address( - address="terra1tmnqgvg567ypvsvk6rwsga3srp7e3lg6u0elp8" - ) - return df diff --git a/openbb_terminal/cryptocurrency/defi/terraengineer_view.py b/openbb_terminal/cryptocurrency/defi/terraengineer_view.py deleted file mode 100644 index 945999a4519..00000000000 --- a/openbb_terminal/cryptocurrency/defi/terraengineer_view.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Terra Engineer View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.defi import terraengineer_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_terra_asset_history( - asset: str = "", - address: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots the 30-day history of specified asset in terra address - [Source: https://terra.engineer/] - - Parameters - ---------- - asset : str - Terra asset {ust,luna,sdt} - address : str - Terra address. Valid terra addresses start with 'terra' - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = terraengineer_model.get_history_asset_from_terra_address( - address=address, asset=asset - ) - if df.empty: - console.print("[red]No data in the provided dataframe[/red]\n") - - fig = OpenBBFigure(yaxis_title=f"{asset.upper()} Amount") - fig.set_title(f"{asset.upper()} Amount in {address}") - fig.add_scatter(x=df["x"], y=df["y"], name=f"{asset.upper()} Amount") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "aterra", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_anchor_yield_reserve( - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots the 30-day history of the Anchor Yield Reserve. - [Source: https://terra.engineer/] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file, by default False - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = terraengineer_model.get_anchor_yield_reserve() - if df.empty: - return console.print("[red]No data was found[/red]\n") - - fig = OpenBBFigure(yaxis_title="UST Amount") - fig.set_title("Anchor UST Yield Reserve") - - fig.add_scatter(x=df["x"], y=df["y"], name="UST Amount") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ayr", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/defi/terramoney_fcd_model.py b/openbb_terminal/cryptocurrency/defi/terramoney_fcd_model.py deleted file mode 100644 index d0514ff35c8..00000000000 --- a/openbb_terminal/cryptocurrency/defi/terramoney_fcd_model.py +++ /dev/null @@ -1,335 +0,0 @@ -"""Terra Money FCD model""" - -__docformat__ = "numpy" - -import logging -import textwrap -from datetime import datetime -from typing import Any, Dict, Tuple - -import pandas as pd - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - denominate_number, - lambda_replace_unicode, - prettify_column_names, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -GOV_COLUMNS = [ - "submitTime", - "id", - "depositEndTime", - "status", - "type", - "title", - "Yes", - "No", -] -GOV_STATUSES = ["voting", "deposit", "passed", "rejected", "all"] -VALIDATORS_COLUMNS = [ - "validatorName", - "tokensAmount", - "votingPower", - "commissionRate", - "status", - "uptime", -] - - -@log_start_end(log=logger) -def _make_request(endpoint: str) -> dict: - """Helper method handles terra fcd api requests. [Source: https://fcd.terra.dev/v1] - - Parameters - ---------- - endpoint: str - endpoint url - - Returns - ------- - dict: - dictionary with response data - """ - - url = f"https://fcd.terra.dev/v1/{endpoint}" - response = request(url, headers={"Accept": "application/json", "User-Agent": "GST"}) - if not 200 <= response.status_code < 300: - console.print( - f"[red]fcd terra api exception: {response.json()['type']}[/red]\n" - ) - return {} - try: - return response.json() - except Exception as e: - logger.exception("Invalid Response: %s", str(e)) - console.print( - f"[red]fcd terra api exception: {response.json()['type']}[/red]\n" - ) - return {} - - -@log_start_end(log=logger) -def _adjust_delegation_info(delegation: dict) -> dict: - """Helper method which removes redundant fields from delegation info dictionary, - and denominate value fields. [Source: https://fcd.terra.dev/v1] - - Parameters - ---------- - delegation: - dictionary object with delegation data e.g. - - Returns - ------- - dict - adjusted dictionary with delegation data - """ - - delegation_info = {} - for key, value in delegation.items(): - if key in ["amountDelegated", "totalReward"]: - delegation_info[key] = denominate_number(value) - elif key in ["validatorAddress", "rewards"]: - continue - else: - delegation_info[key] = value - return delegation_info - - -@log_start_end(log=logger) -def get_staking_account_info(address: str = "") -> Tuple[pd.DataFrame, str]: - """Get staking info for provided terra account [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - address: str - terra blockchain address e.g. terra1jvwelvs7rdk6j3mqdztq5tya99w8lxk6l9hcqg - Returns - ------- - Tuple[pd.DataFrame, str] - luna delegations and summary report for given address - """ - - response = _make_request(f"staking/{address}") - results: Dict[str, Any] = {"myDelegations": []} - - for field in ["availableLuna", "delegationTotal"]: - results[field] = denominate_number(response.get(field, 0)) - - my_delegations = response.get("myDelegations") - if my_delegations: - for delegation in my_delegations: - validator = _adjust_delegation_info(delegation) - results["myDelegations"].append(validator) - - df = pd.DataFrame(results["myDelegations"]) - - try: - df["validatorName"] = df["validatorName"].apply( - lambda x: lambda_replace_unicode(x) - ) - df.columns = prettify_column_names(list(df.columns)) - except KeyError: - df = pd.DataFrame() - - results["totalRewards"] = denominate_number( - response.get("rewards", {}).get("total", 0) - ) - - report = f"""Overview: - Address: {address} - Available Luna: {results['availableLuna']} - Delegated Luna: {results['delegationTotal']} - Total Rewards: {results['totalRewards']}\n""" - report += "\nDelegations: " if not df.empty else "\nNo delegations found\n" - - return df, report - - -@log_start_end(log=logger) -def get_validators(sortby: str = "votingPower", ascend: bool = True) -> pd.DataFrame: - """Get information about terra validators [Source: https://fcd.terra.dev/swagger] - - Parameters - ----------- - sortby: str - Key by which to sort data. Choose from: - validatorName, tokensAmount, votingPower, commissionRate, status, uptime - ascend: bool - Flag to sort data descending - - Returns - ------- - pd.DataFrame - terra validators details - """ - - response = _make_request("staking")["validators"] - results = [ - { - "accountAddress": validator["accountAddress"], - "validatorName": validator["description"].get("moniker"), - "tokensAmount": denominate_number(validator["tokens"]), - "votingPower": round( - (float(validator["votingPower"].get("weight")) * 100), 2 - ), - "commissionRate": round( - (float(validator["commissionInfo"].get("rate", 0)) * 100), 2 - ), - "status": validator["status"], - "uptime": round((float(validator.get("upTime", 0)) * 100), 2), - } - for validator in response - ] - - df = pd.DataFrame(results) - if not df.empty: - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_proposals( - status: str = "", sortby: str = "id", ascend: bool = True, limit: int = 10 -) -> pd.DataFrame: - """Get terra blockchain governance proposals list [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - status: str - status of proposal, one from list: ['Voting','Deposit','Passed','Rejected'] - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - limit: int - Number of records to display - - Returns - ------- - pd.DataFrame - Terra blockchain governance proposals list - """ - - statuses = ["Voting", "Deposit", "Passed", "Rejected"] - response = _make_request("gov/proposals")["proposals"] - results = [] - votes_options = ["Yes", "Abstain", "No", "NoWithVeto"] - for proposal in response: - deposit = proposal.pop("deposit") - proposal["depositEndTime"] = deposit.get("depositEndTime") - vote = proposal.pop("vote") - proposal.pop("proposer") - for opt in votes_options: - proposal[opt] = vote["count"].get(opt) - - results.append(proposal) - columns = [ - "id", - "submitTime", - "depositEndTime", - "status", - "type", - "title", - "Yes", - "No", - "Abstain", - "NoWithVeto", - ] - df = pd.DataFrame(results)[columns] - df[["id", "Yes", "No", "Abstain", "NoWithVeto"]] = df[ - ["id", "Yes", "No", "Abstain", "NoWithVeto"] - ].astype(int, errors="ignore") - df["title"] = df["title"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=40)) if isinstance(x, str) else x - ) - - for col in ["submitTime", "depositEndTime"]: - df[col] = df[col].apply(lambda x: pd.to_datetime(x).strftime("%Y-%m-%d %H:%M")) - - if status.title() in statuses: - df = df[df["status"] == status.title()] - df = df.sort_values(by=sortby, ascending=ascend).head(limit) - df.columns = prettify_column_names(df.columns) - return df - - -@log_start_end(log=logger) -def get_account_growth(cumulative: bool = True) -> pd.DataFrame: - """Get terra blockchain account growth history [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - cumulative: bool - distinguish between periodical and cumulative account growth data - - Returns - ------- - pd.DataFrame - historical data of accounts growth - """ - - response = _make_request("dashboard/account_growth") - kind = "cumulative" if cumulative else "periodic" - df = pd.DataFrame(response[kind]) - df["date"] = df["datetime"].apply(lambda x: datetime.fromtimestamp(x / 1000).date()) - df = df[["date", "totalAccountCount", "activeAccountCount"]] - df.columns = ["date", "Total accounts", "Active accounts"] - return df - - -@log_start_end(log=logger) -def get_staking_ratio_history(limit: int = 200): - """Get terra blockchain staking ratio history [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - limit: int - The number of ratios to show - - Returns - ------- - pd.DataFrame - historical staking ratio - """ - - response = _make_request("dashboard/staking_ratio") - df = pd.DataFrame(response) - df["date"] = df["datetime"].apply(lambda x: datetime.fromtimestamp(x / 1000).date()) - df["stakingRatio"] = df["stakingRatio"].apply(lambda x: round(float(x) * 100, 2)) - df = df[["date", "stakingRatio"]] - df = df.sort_values("date", ascending=False).head(limit) - df = df.set_index("date") - return df - - -@log_start_end(log=logger) -def get_staking_returns_history(limit: int = 200): - """Get terra blockchain staking returns history [Source: https://fcd.terra.dev/v1] - - Parameters - ---------- - limit: int - The number of returns to show - - Returns - ------- - pd.DataFrame - historical staking returns - """ - - response = _make_request("dashboard/staking_return") - df = pd.DataFrame(response) - df["date"] = df["datetime"].apply(lambda x: datetime.fromtimestamp(x / 1000).date()) - df["annualizedReturn"] = df["annualizedReturn"].apply( - lambda x: round(float(x) * 100, 2) - ) - df = df[["date", "annualizedReturn"]] - df = df.sort_values("date", ascending=False).head(limit) - df = df.set_index("date") - - return df diff --git a/openbb_terminal/cryptocurrency/defi/terramoney_fcd_view.py b/openbb_terminal/cryptocurrency/defi/terramoney_fcd_view.py deleted file mode 100644 index 78a2295f1f0..00000000000 --- a/openbb_terminal/cryptocurrency/defi/terramoney_fcd_view.py +++ /dev/null @@ -1,302 +0,0 @@ -"""Terra Money FCD view""" - -__docformat__ = "numpy" - -import logging -import os -import textwrap -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_very_long_number_formatter, - prettify_column_names, -) -from openbb_terminal.cryptocurrency.defi import terramoney_fcd_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_account_staking_info( - address: str = "", - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing staking info for provided terra account address [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - address: str - terra blockchain address e.g. terra1jvwelvs7rdk6j3mqdztq5tya99w8lxk6l9hcqg - limit: int - Number of records to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df, report = terramoney_fcd_model.get_staking_account_info(address) - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=report, - export=bool(export), - limit=limit, - ) - else: - console.print(f"[red]No data found for address {address}\n[/red]") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "sinfo", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_validators( - limit: int = 10, - sortby: str = "votingPower", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing information about terra validators [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key by which to sort data. Choose from: - validatorName, tokensAmount, votingPower, commissionRate, status, uptime - ascend: bool - Flag to sort data descending - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = terramoney_fcd_model.get_validators(sortby, ascend) - df_data = df.copy() - df["tokensAmount"] = df["tokensAmount"].apply( - lambda x: lambda_very_long_number_formatter(x) - ) - df.columns = [ - x if x not in ["Voting power", "Commission rate", "Uptime"] else f"{x} %" - for x in prettify_column_names(df.columns) - ] - - df["Account address"] = df["Account address"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=20)) - ) - - print_rich_table( - df, - headers=list(df.columns), - floatfmt=".2f", - show_index=False, - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "validators", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_gov_proposals( - limit: int = 10, - status: str = "all", - sortby: str = "id", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing terra blockchain governance proposals list [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - limit: int - Number of records to display - status: str - status of proposal, one from list: ['Voting','Deposit','Passed','Rejected'] - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascend - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = terramoney_fcd_model.get_proposals(status, sortby, ascend, limit) - - print_rich_table( - df, - headers=list(df.columns), - floatfmt=".2f", - show_index=False, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "govp", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_account_growth( - kind: str = "total", - cumulative: bool = False, - limit: int = 90, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots terra blockchain account growth history [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - limit: int - Number of records to display - kind: str - display total account count or active account count. One from list [active, total] - cumulative: bool - Flag to show cumulative or discrete values. For active accounts only discrete value are available. - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = terramoney_fcd_model.get_account_growth(cumulative) - if kind not in ["active", "total"]: - kind = "total" - options = {"total": "Total accounts", "active": "Active accounts"} - - opt = options[kind] - label = "Cumulative" if cumulative and opt == "total" else "Daily" - - fig = OpenBBFigure(yaxis_title=f"{opt}") - - df = df.sort_values("date", ascending=False).head(limit) - df = df.set_index("date") - - start, end = df.index[-1], df.index[0] - - if cumulative: - fig.add_scatter(x=df.index, y=df[opt], mode="lines", name=opt) - else: - fig.add_bar(x=df.index, y=df[opt], name=opt) - - fig.set_title(f"{label} number of {opt.lower()} in period from {start} to {end}") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gacc", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_staking_ratio_history( - limit: int = 90, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots terra blockchain staking ratio history [Source: https://fcd.terra.dev/v1] - - Parameters - ---------- - limit: int - Number of records to display - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = terramoney_fcd_model.get_staking_ratio_history(limit) - - start, end = df.index[-1], df.index[0] - - fig = OpenBBFigure(yaxis_title="Staking ratio [%]") - fig.set_title(f"Staking ratio from {start} to {end}") - - fig.add_scatter(x=df.index, y=df["stakingRatio"], mode="lines", name="stakingRatio") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "sratio", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_staking_returns_history( - limit: int = 90, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots terra blockchain staking returns history [Source: https://fcd.terra.dev/swagger] - - Parameters - ---------- - limit: int - Number of records to display - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - - """ - - fig = OpenBBFigure(yaxis_title="Staking returns [%]") - - df = terramoney_fcd_model.get_staking_returns_history(limit) - - start, end = df.index[-1], df.index[0] - - fig.add_scatter( - x=df.index, y=df["annualizedReturn"], mode="lines", name="annualizedReturn" - ) - fig.set_title(f"Staking returns from {start} to {end}") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "sreturn", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/discovery/__init__.py b/openbb_terminal/cryptocurrency/discovery/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/cryptocurrency/discovery/coinmarketcap_model.py b/openbb_terminal/cryptocurrency/discovery/coinmarketcap_model.py deleted file mode 100644 index b9c10d4825c..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/coinmarketcap_model.py +++ /dev/null @@ -1,77 +0,0 @@ -"""CoinMarketCap model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd -from coinmarketcapapi import CoinMarketCapAPI, CoinMarketCapAPIError - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -FILTERS = ["Symbol", "CMC_Rank", "LastPrice", "DayPctChange", "MarketCap"] - -sort_map = { - "Symbol": "Symbol", - "CMC_Rank": "CMC_Rank", - "LastPrice": "Last Price", - "DayPctChange": "1 Day Pct Change", - "MarketCap": "Market Cap ($B)", -} - - -@log_start_end(log=logger) -@check_api_key(["API_CMC_KEY"]) -def get_cmc_top_n(sortby: str = "CMC_Rank", ascend: bool = True) -> pd.DataFrame: - """Shows top n coins. [Source: CoinMarketCap] - - Parameters - ---------- - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - Coin Market Cap:s API documentation, see: - https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyListingsLatest - ascend: bool - Whether to sort ascending or descending - - Returns - ------- - pd.DataFrame - Top coin on CoinMarketCap - - """ - df = pd.DataFrame() - - try: - cmc = CoinMarketCapAPI(get_current_user().credentials.API_CMC_KEY) - ratings = cmc.cryptocurrency_listings_latest().data - - symbol, rank, price, pchange1d, mkt_cap = [], [], [], [], [] - - for coin in ratings: - symbol.append(coin["symbol"]) - rank.append(coin["cmc_rank"]) - price.append(coin["quote"]["USD"]["price"]) - pchange1d.append(coin["quote"]["USD"]["percent_change_24h"]) - mkt_cap.append(coin["quote"]["USD"]["market_cap"] / (10**9)) - - df = pd.DataFrame(data=[symbol, rank, price, pchange1d, mkt_cap]).transpose() - df.columns = [ - "Symbol", - "CMC_Rank", - "Last Price", - "1 Day Pct Change", - "Market Cap ($B)", - ] - except CoinMarketCapAPIError as e: - if "API Key" in str(e): - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(e) - - df = df.sort_values(by=sort_map[sortby], ascending=ascend) - return df diff --git a/openbb_terminal/cryptocurrency/discovery/coinmarketcap_view.py b/openbb_terminal/cryptocurrency/discovery/coinmarketcap_view.py deleted file mode 100644 index 02c4615bbd3..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/coinmarketcap_view.py +++ /dev/null @@ -1,64 +0,0 @@ -"""CoinMarketCap view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.discovery import coinmarketcap_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_CMC_KEY"]) -def display_cmc_top_coins( - limit: int = 15, - sortby: str = "CMC_Rank", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing top n coins. [Source: CoinMarketCap] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - Coin Market Cap:s API documentation, see: - https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyListingsLatest - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - - """ - - df = coinmarketcap_model.get_cmc_top_n(sortby, ascend) - - if df.empty: - console.print("No Data Found\n") - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Top Coins", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cmctop", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/discovery/coinpaprika_model.py b/openbb_terminal/cryptocurrency/discovery/coinpaprika_model.py deleted file mode 100644 index 88f56e48757..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/coinpaprika_model.py +++ /dev/null @@ -1,80 +0,0 @@ -"""CoinPaprika model""" - -__docformat__ = "numpy" - -import logging -from typing import Any, Optional - -import pandas as pd - -from openbb_terminal.cryptocurrency.coinpaprika_helpers import PaprikaSession -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - -CATEGORIES = [ - "currencies", - "exchanges", - "icos", - "people", - "tags", - "all", -] - -FILTERS = ["category", "id", "name"] - - -@log_start_end(log=logger) -def get_search_results( - query: str, - category: Optional[Any] = None, - modifier: Optional[Any] = None, - sortby: str = "id", - ascend: bool = True, -) -> pd.DataFrame: - """Search CoinPaprika. [Source: CoinPaprika] - - Parameters - ---------- - query: str - phrase for search - category: Optional[Any] - one or more categories (comma separated) to search. - Available options: currencies|exchanges|icos|people|tags - Default: currencies,exchanges,icos,people,tags - modifier: Optional[Any] - set modifier for search results. Available options: symbol_search - - search only by symbol (works for currencies only) - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - API documentation (see https://api.coinpaprika.com/docs#tag/Tools/paths/~1search/get) - ascend: bool - Flag to sort data descending - - Returns - ------- - pd.DataFrame - Search Results - Columns: Metric, Value - """ - - session = PaprikaSession() - if category is None: - category = "currencies,exchanges,icos,people,tags" - data = session.make_request( - session.ENDPOINTS["search"], q=query, c=category, modifier=modifier, limit=100 - ) - results = [] - for item in data: - category = data[item] - for r in category: - results.append( - { - "id": r.get("id"), - "name": r.get("name"), - "category": item, - } - ) - df = pd.DataFrame(results) - df = df.sort_values(by=sortby, ascending=ascend) - return df diff --git a/openbb_terminal/cryptocurrency/discovery/coinpaprika_view.py b/openbb_terminal/cryptocurrency/discovery/coinpaprika_view.py deleted file mode 100644 index a31285ee8b3..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/coinpaprika_view.py +++ /dev/null @@ -1,74 +0,0 @@ -"""CoinPaprika view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import openbb_terminal.cryptocurrency.discovery.coinpaprika_model as paprika -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_search_results( - query: str, - category: str = "all", - limit: int = 10, - sortby: str = "id", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing Search over CoinPaprika. [Source: CoinPaprika] - - Parameters - ---------- - query: str - Search query - category: str - Categories to search: currencies|exchanges|icos|people|tags|all. Default: all - limit: int - Number of records to display - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - API documentation (see https://api.coinpaprika.com/docs#tag/Tools/paths/~1search/get) - ascend: bool - Flag to sort data descending - export : str - Export dataframe data to csv,json,xlsx file - """ - - if category.lower() == "all": - category = "currencies,exchanges,icos,people,tags" - - df = paprika.get_search_results( - query=query, category=category, sortby=sortby, ascend=ascend - ) - - if df.empty: - console.print( - f"No results for search query '{query}' in category '{category}'\n" - ) - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="CoinPaprika Results", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "search", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/discovery/cryptostats_model.py b/openbb_terminal/cryptocurrency/discovery/cryptostats_model.py deleted file mode 100644 index 8f82c0d32b1..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/cryptostats_model.py +++ /dev/null @@ -1,92 +0,0 @@ -"""CryptoStats model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.cryptocurrency.defi.llama_model import ( - get_chains, - get_defi_protocols, -) -from openbb_terminal.cryptocurrency.discovery.pycoingecko_model import get_coins -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_fees(marketcap: bool, tvl: bool, date) -> pd.DataFrame: - """Show cryptos with most fees. [Source: CryptoStats] - - Parameters - ---------- - marketcap: bool - Whether to show marketcap or not - tvl: bool - Whether to show tvl or not - date: datetime - Date to get data from (YYYY-MM-DD) - - Returns - ------- - pd.DataFrame - Top coins with most fees - """ - df = pd.DataFrame() - if not isinstance(date, str): - date = date.strftime("%Y-%m-%d") - response = request( - f"https://api.cryptostats.community/api/v1/fees/oneDayTotalFees/{date}" - ) - if response.status_code != 200: - raise Exception(f"Status code: {response.status_code}. Reason: {response.text}") - res = response.json() - data = res["data"] - df = pd.DataFrame(columns=["Symbol", "Name", "Category", "One Day Fees"]) - - for i, item in enumerate(data): - metadata = item["metadata"] - results = item["results"] - symbol = metadata.get("tokenTicker", "") - name = metadata.get("name", "") - category = metadata.get("category", "") - fees = results.get("oneDayTotalFees", "") - df.loc[i] = [symbol, name, category, fees] - - df = df.groupby(["Symbol", "Name", "Category"]).sum().reset_index() - df = df.sort_values("One Day Fees", ascending=False) - - if marketcap: - coins = get_coins() - coins["symbol"] = coins["symbol"].str.upper() - coins = coins[["symbol", "market_cap", "market_cap_rank"]] - coins = coins.rename( - columns={ - "symbol": "Symbol", - "market_cap": "Market Cap", - "market_cap_rank": "MC Rank", - } - ) - df = df.merge(coins, left_on="Symbol", right_on="Symbol", how="left") - if tvl: - protocols = get_defi_protocols(1000) - protocols = protocols[["Symbol", "TVL ($)"]] - protocols = protocols.rename(columns={"TVL ($)": "TVL"}) - protocols = protocols.groupby(["Symbol"]).sum().reset_index() - - df = df.merge(protocols, left_on="Symbol", right_on="Symbol", how="left") - chains = get_chains() - chains = chains[["tokenSymbol", "tvl"]] - chains = chains.rename(columns={"tokenSymbol": "Symbol", "tvl": "TVL"}) - - df = df.fillna(0) - - for index, row in df.iterrows(): - chain_tvl = chains[chains["Symbol"] == row["Symbol"]] - if not chain_tvl.empty: - df.loc[index, "TVL"] = chain_tvl["TVL"].iloc[0] - - return df diff --git a/openbb_terminal/cryptocurrency/discovery/cryptostats_view.py b/openbb_terminal/cryptocurrency/discovery/cryptostats_view.py deleted file mode 100644 index 1d3624a24a0..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/cryptostats_view.py +++ /dev/null @@ -1,69 +0,0 @@ -"""CryptoStats view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.discovery import cryptostats_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_fees( - marketcap: bool, - tvl: bool, - date: str, - limit: int = 15, - sortby: str = "", - ascend: bool = True, - sheet_name: Optional[str] = None, - export: str = "", -) -> None: - """Display crypto with most fees paid [Source: CryptoStats] - - Parameters - ---------- - marketcap: bool - Flag to include marketcap - date: str - Date to get data from (YYYY-MM-DD) - limit: int - Number of records to display - sortby: str - Key to sort data. - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = cryptostats_model.get_fees(marketcap, tvl, date) - - if df.empty: - console.print("No Data Found\n") - return - - if sortby: - df = df.sort_values(sortby, ascending=ascend) - - if "One Day Fees" in df.columns: - one_day_fees = df.pop("One Day Fees").map(lambda x: f"{x:,.2f}") - df.insert(len(df.columns), "One Day Fees", one_day_fees) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Crypto Fees", - limit=limit, - ) - - export_data( - export, os.path.dirname(os.path.abspath(__file__)), "fees", df, sheet_name - ) diff --git a/openbb_terminal/cryptocurrency/discovery/dappradar_model.py b/openbb_terminal/cryptocurrency/discovery/dappradar_model.py deleted file mode 100644 index 352dbd17af4..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/dappradar_model.py +++ /dev/null @@ -1,387 +0,0 @@ -"""DappRadar model""" - -__docformat__ = "numpy" - -# pylint: disable=C0301,E1137 -import logging -from typing import Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -NFT_COLUMNS = [ - "Name", - "Dapp ID", - "Logo", - "Chains", - "Avg Price [$]", - "Avg Price Change [%]", - "Volume [$]", - "Volume Change [%]", - "Traders", - "Traders Change [%]", -] - - -@log_start_end(log=logger) -def _make_request(url: str, verbose: bool = False) -> Optional[dict]: - """Helper method handles dappradar api requests. [Source: https://dappradar.com/] - - Parameters - ---------- - url: str - endpoint url - verbose: bool - whether to print the text from the response - - Returns - ------- - Optional[dict]: - dictionary with response data - """ - current_user = get_current_user() - - headers = { - "Accept": "application/json", - "User-Agent": get_user_agent(), - # "referer": "https://api.dappradar.com/4tsxo4vuhotaojtl/", - "X-BLOBR-KEY": current_user.credentials.API_DAPPRADAR_KEY, - } - response = request(url, headers=headers) - if not 200 <= response.status_code < 300: - if verbose: - console.print(f"[red]dappradar api exception: {response.text}[/red]") - return None - try: - return response.json() - except Exception as e: # noqa: F841 - logger.exception("Invalid Response: %s", str(e)) - if verbose: - console.print(f"[red]Invalid Response:: {response.text}[/red]") - return None - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def get_nft_marketplaces( - chain: str = "", sortby: str = "", order: str = "", limit: int = 10 -) -> pd.DataFrame: - """Get top nft collections [Source: https://dappradar.com/] - - Parameters - ---------- - chain: str - Name of the chain - sortby: str - Key by which to sort data - order: str - Order of sorting - limit: int - Number of records to display - - Returns - ------- - pd.DataFrame - Columns: Name, Dapp ID, Logo, Chains, Avg Price [$], Avg Price Change [%], - Volume [$], Volume Change [%], Traders, Traders Change [%] - """ - - args = { - "chain": chain, - "order": order, - "sort": sortby, - "resultsPerPage": limit, - } - args = {k: v for k, v in args.items() if v} - query_string = "&".join([f"{k}={v}" for k, v in args.items()]) - - response = _make_request( - f"https://api.dappradar.com/4tsxo4vuhotaojtl/nfts/marketplaces?{query_string}" - ) - if response: - data = response.get("results") - - return pd.DataFrame( - data, - columns=[ - "name", - "dappId", - "logo", - "chains", - "avgPrice", - "avgPricePercentageChange", - "volume", - "volumePercentageChange", - "traders", - "tradersPercentageChange", - ], - ).rename( - columns={ - "name": "Name", - "dappId": "Dapp ID", - "logo": "Logo", - "chains": "Chains", - "avgPrice": "Avg Price [$]", - "avgPricePercentageChange": "Avg Price Change [%]", - "volume": "Volume [$]", - "volumePercentageChange": "Volume Change [%]", - "traders": "Traders", - "tradersPercentageChange": "Traders Change [%]", - } - ) - return pd.DataFrame() - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def get_nft_marketplace_chains() -> pd.DataFrame: - """Get nft marketplaces chains [Source: https://dappradar.com/] - - Returns - ------- - pd.DataFrame - Columns: Chain - """ - - response = _make_request( - "https://api.dappradar.com/4tsxo4vuhotaojtl/nfts/marketplaces/chains" - ) - if response: - data = response.get("chains") - - return pd.DataFrame( - data, - columns=["Chain"], - ) - return pd.DataFrame() - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def get_dapps(chain: str = "", page: int = 1, resultPerPage: int = 15): - """Get dapps [Source: https://dappradar.com/] - - Parameters - ---------- - chain: str - Name of the chain - page: int - Page number - resultPerPage: int - Number of records to display - - Returns - ------- - pd.DataFrame - Columns: Dapp ID, Name, Description, Full Description, Logo, Link, Website, - Chains, Categories - """ - - args = { - "chain": chain, - "page": page, - } - args = {k: v for k, v in args.items() if v} - query_string = "&".join([f"{k}={v}" for k, v in args.items()]) - - response = _make_request( - f"https://api.dappradar.com/4tsxo4vuhotaojtl/dapps?{query_string}" - ) - - if response: - data = response.get("results") - return ( - pd.DataFrame( - data, - columns=[ - "dappId", - "name", - "description", - "fullDescription", - "chains", - "categories", - "logo", - "link", - "website", - ], - ) - .rename( - columns={ - "dappId": "Dapp ID", - "name": "Name", - "description": "Description", - "fullDescription": "Full Description", - "chains": "Chains", - "categories": "Categories", - "logo": "Logo", - "link": "Link", - "website": "Website", - } - ) - .head(resultPerPage) # DappRadar resultsPerPage is broken - ) - - return pd.DataFrame() - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def get_dapp_categories() -> pd.DataFrame: - """Get dapp categories [Source: https://dappradar.com/] - - Returns - ------- - pd.DataFrame - Columns: Category - """ - - response = _make_request( - "https://api.dappradar.com/4tsxo4vuhotaojtl/dapps/categories" - ) - if response: - data = response.get("categories") - - return pd.DataFrame( - data, - columns=["Category"], - ) - return pd.DataFrame() - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def get_dapp_chains() -> pd.DataFrame: - """Get dapp chains [Source: https://dappradar.com/] - - Returns - ------- - pd.DataFrame - Columns: Chain - """ - - response = _make_request("https://api.dappradar.com/4tsxo4vuhotaojtl/dapps/chains") - if response: - data = response.get("chains") - - return pd.DataFrame( - data, - columns=["Chain"], - ) - return pd.DataFrame() - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def get_dapp_metrics( - dappId: int, chain: str = "", time_range: str = "" -) -> pd.DataFrame: - """Get dapp metrics [Source: https://dappradar.com/] - - Parameters - ---------- - dappId: int - Dapp ID - chain: str - Name of the chain if the dapp is multi-chain - range: str - Time range for the metrics. Can be 24h, 7d, 30d - - Returns - ------- - pd.DataFrame - Columns: Transactions, Transactions Change [%], Users, UAW, UAW Change [%], - Volume [$], Volume Change [%], Balance [$], Balance Change [%] - """ - if not dappId: - console.print("[red]Please provide a dappId[/red]") - return pd.DataFrame() - - query_string = ( - f"range={time_range}" if not chain else f"chain={chain}&range={time_range}" - ) - - response = _make_request( - f"https://api.dappradar.com/4tsxo4vuhotaojtl/dapps/{dappId}?{query_string}" - ) - - if response: - data = response["results"].get("metrics") - return pd.DataFrame( - data, - columns=[ - "transactions", - "transactionsPercentageChange", - "users", - "uaw", - "uawPercentageChange", - "volume", - "volumePercentageChange", - "balance", - "balancePercentageChange", - ], - index=[response["results"]["name"]], - ).rename( - columns={ - "transactions": "Transactions", - "transactionsPercentageChange": "Transactions Change [%]", - "users": "Users", - "uaw": "UAW", - "uawPercentageChange": "UAW Change [%]", - "volume": "Volume [$]", - "volumePercentageChange": "Volume Change [%]", - "balance": "Balance [$]", - "balancePercentageChange": "Balance Change [%]", - } - ) - - return pd.DataFrame() - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def get_defi_chains() -> pd.DataFrame: - """Get defi chains [Source: https://dappradar.com/] - - Returns - ------- - pd.DataFrame - Columns: Chains - """ - - response = _make_request("https://api.dappradar.com/4tsxo4vuhotaojtl/defi/chains") - if response: - data = response.get("chains") - - return pd.DataFrame( - data, - columns=["Chains"], - ) - return pd.DataFrame() - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def get_token_chains() -> pd.DataFrame: - """Get chains that support tokens [Source: https://dappradar.com/] - - Returns - ------- - pd.DataFrame - Columns: Chains - """ - - response = _make_request("https://api.dappradar.com/4tsxo4vuhotaojtl/tokens/chains") - if response: - data = response.get("chains") - - return pd.DataFrame( - data, - columns=["Chains"], - ) - return pd.DataFrame() diff --git a/openbb_terminal/cryptocurrency/discovery/dappradar_view.py b/openbb_terminal/cryptocurrency/discovery/dappradar_view.py deleted file mode 100644 index 05e1c44959b..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/dappradar_view.py +++ /dev/null @@ -1,335 +0,0 @@ -"""DappRadar view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import numpy as np - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_very_long_number_formatter, -) -from openbb_terminal.cryptocurrency.discovery import dappradar_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def display_nft_marketplaces( - limit: int = 10, - sortby: str = "", - order: str = "", - chain: str = "", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing nft marketplaces [Source: https://dappradar.com/] - - Parameters - ---------- - chain: str - Name of the chain - order: str - Order of sorting (asc/desc) - limit: int - Number of records to display - sortby: str - Key by which to sort data - export : str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Name of the sheet in excel or csv file - """ - - df = dappradar_model.get_nft_marketplaces( - chain=chain, - sortby=sortby, - order=order, - limit=limit, - ) - if df.empty: - console.print("[red]Failed to fetch data from DappRadar[/red]") - return - for col in ["Avg Price [$]", "Volume [$]"]: - if col in df.columns: - df[col] = ( - df[col] - .fillna(-1) - .apply(lambda x: lambda_very_long_number_formatter(x)) - .replace(-1, np.nan) - ) - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="NFT marketplaces", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "drnft", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def display_nft_marketplace_chains( - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing nft marketplaces chains [Source: https://dappradar.com/] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Name of the sheet in excel or csv file - """ - - df = dappradar_model.get_nft_marketplace_chains() - if df.empty: - console.print("[red]Failed to fetch data from DappRadar[/red]") - return - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="NFT marketplace chains", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "drnftchains", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def display_dapps( - chain: str = "", - page: int = 1, - resultPerPage: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -): - """Prints table showing dapps [Source: https://dappradar.com/] - - Parameters - ---------- - chain: str - Name of the chain - page: int - Page number - resultPerPage: int - Number of records per page - export : str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Name of the sheet in excel or csv file - """ - - df = dappradar_model.get_dapps( - chain=chain, - page=page, - resultPerPage=resultPerPage, - ) - if df.empty: - console.print("[red]Failed to fetch data from DappRadar[/red]") - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Dapps", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "drdapps", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def display_dapp_categories( - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing dapp categories [Source: https://dappradar.com/]""" - - df = dappradar_model.get_dapp_categories() - if df.empty: - console.print("[red]Failed to fetch data from DappRadar[/red]") - return - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Dapp categories", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "drdappcategories", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def display_dapp_chains( - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing dapp chains [Source: https://dappradar.com/]""" - - df = dappradar_model.get_dapp_chains() - if df.empty: - console.print("[red]Failed to fetch data from DappRadar[/red]") - return - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Dapp chains", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "drdappchains", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def display_dapp_metrics( - dappId: int, - chain: str = "", - time_range: str = "", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing dapp metrics [Source: https://dappradar.com/] - - Parameters - ---------- - dappId: int - Dapp id - chain: str - Name of the chain - range: str - Range of data to display (24h, 7d, 30d) - export : str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Name of the sheet in excel or csv file - """ - - df = dappradar_model.get_dapp_metrics( - dappId=dappId, - chain=chain, - time_range=time_range, - ) - if df.empty: - console.print("[red]Failed to fetch data from DappRadar[/red]") - return - for col in ["Volume [$]", "Balance [$]"]: - if col in df.columns: - df[col] = ( - df[col] - .fillna(-1) - .apply(lambda x: lambda_very_long_number_formatter(x)) - .replace(-1, np.nan) - ) - print_rich_table( - df.T, - show_index=True, - title=f"Dapp metrics for dappId: {dappId}", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "drdappmetrics", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def display_defi_chains(export: str = "", sheet_name: Optional[str] = None) -> None: - """Prints table showing defi chains [Source: https://dappradar.com/]""" - - df = dappradar_model.get_defi_chains() - if df.empty: - console.print("[red]Failed to fetch data from DappRadar[/red]") - return - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Defi chains", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "drdefichains", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_DAPPRADAR_KEY"]) -def display_token_chains( - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing chains that support tokens [Source: https://dappradar.com/]""" - - df = dappradar_model.get_token_chains() - if df.empty: - console.print("[red]Failed to fetch data from DappRadar[/red]") - return - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Token chains", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "drtokenchains", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/discovery/discovery_controller.py b/openbb_terminal/cryptocurrency/discovery/discovery_controller.py deleted file mode 100644 index 9bd65f258ad..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/discovery_controller.py +++ /dev/null @@ -1,802 +0,0 @@ -"""Cryptocurrency Discovery Controller""" - -__docformat__ = "numpy" - -# pylint: disable=R0904, C0302, W0622, C0201 -import argparse -import logging -from datetime import datetime, timedelta -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.discovery import ( - coinmarketcap_model, - coinmarketcap_view, - coinpaprika_model, - coinpaprika_view, - cryptostats_view, - dappradar_model, - dappradar_view, - pycoingecko_model, - pycoingecko_view, -) -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console, get_ordered_list_sources -from openbb_terminal.stocks import stocks_helper - -logger = logging.getLogger(__name__) - - -class DiscoveryController(BaseController): - """Discovery Controller class""" - - CHOICES_COMMANDS = [ - "search", - "top", - "trending", - "gainers", - "losers", - "nft_mktp_chains", - "nft_mktp", - "dapps", - "fees", - "dapp_categories", - "dapp_chains", - "dapp_metrics", - "defi_chains", - "tokens", - ] - - PATH = "/crypto/disc/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - ordered_list_sources_top = get_ordered_list_sources(f"{self.PATH}top") - if ordered_list_sources_top and ordered_list_sources_top[0] == "CoinGecko": - choices["top"]["--sort"] = { - c: {} - for c in stocks_helper.format_parse_choices( - pycoingecko_view.COINS_COLUMNS - ) - } - else: - choices["top"]["--sort"] = { - c: {} - for c in stocks_helper.format_parse_choices( - coinmarketcap_model.FILTERS - ) - } - - choices["top"]["-s"] = choices["top"]["--sort"] - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("crypto/disc/") - mt.add_cmd("top") - mt.add_cmd("trending") - mt.add_cmd("gainers") - mt.add_cmd("losers") - mt.add_cmd("search") - mt.add_cmd("nft_mktp_chains") - mt.add_cmd("nft_mktp") - mt.add_cmd("dapps") - mt.add_cmd("fees") - mt.add_cmd("dapp_categories") - mt.add_cmd("dapp_chains") - mt.add_cmd("dapp_metrics") - mt.add_cmd("defi_chains") - mt.add_cmd("tokens") - console.print(text=mt.menu_text, menu="Cryptocurrency - Discovery") - - @log_start_end(log=logger) - def call_fees(self, other_args: List[str]): - """Process fees command""" - - parser = argparse.ArgumentParser( - prog="fees", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Cryptos where users pay most fees on [Source: CryptoStats] - """, - ) - - parser.add_argument( - "--mc", - action="store_true", - dest="marketcap", - default=False, - help="Include the market cap rank", - ) - parser.add_argument( - "--tvl", - action="store_true", - dest="tvl", - default=False, - help="Include the total value locked", - ) - - parser.add_argument( - "-d", - "--date", - dest="date", - type=valid_date, - help="Initial date. Default: yesterday", - default=(datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-s", - "--sort", - dest="sortby", - nargs="+", - help="Sort by given column. Default: One Day Fees", - default="One Day Fees", - choices=["One Day Fees", "Market Cap Rank"], - metavar="SORTBY", - ) - - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10 - ) - - if ns_parser: - cryptostats_view.display_fees( - marketcap=ns_parser.marketcap, - tvl=ns_parser.tvl, - date=ns_parser.date, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_top(self, other_args): - """Process top command""" - ordered_list_sources_top = get_ordered_list_sources(f"{self.PATH}top") - - if ordered_list_sources_top and ordered_list_sources_top[0] == "CoinGecko": - argument_sort_default = "Market Cap Rank" - else: - argument_sort_default = "CMC_Rank" - - parser = argparse.ArgumentParser( - prog="top", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Display N coins from the data source, if the data source is CoinGecko it - can receive a category as argument (-c decentralized-finance-defi or -c stablecoins) - and will show only the top coins in that category. - can also receive sort arguments (these depend on the source), e.g., --sort Volume [$] - You can sort by {Symbol,Name,Price [$],Market Cap,Market Cap Rank,Volume [$]} with CoinGecko - Number of coins to show: -l 10 - """, - ) - - parser.add_argument( - "-c", - "--category", - default="", - dest="category", - help="Category (e.g., stablecoins). Empty for no category. Only works for 'CoinGecko' source.", - choices=pycoingecko_model.get_categories_keys(), - metavar="CATEGORY", - ) - - parser.add_argument( - "-l", - "--limit", - default=10, - dest="limit", - help="Limit of records", - type=check_positive, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - nargs="+", - help="Sort by given column. Default: Market Cap Rank", - default=stocks_helper.format_parse_choices([argument_sort_default]), - metavar="SORTBY", - ) - - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.source == "CoinGecko": - pycoingecko_view.display_coins( - sortby=" ".join(ns_parser.sortby), - category=ns_parser.category, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ascend=ns_parser.reverse, - ) - elif ns_parser.source == "CoinMarketCap": - coinmarketcap_view.display_cmc_top_coins( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dapps(self, other_args): - """Process dapps command""" - parser = argparse.ArgumentParser( - prog="dapps", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows available decentralized applications [Source: https://dappradar.com/] - Accepts --chain argument to filter by blockchain - --page argument to show a specific page. Default: 1 - --limit argument to limit the number of records per page. Default: 15 - """, - ) - parser.add_argument( - "-c", - "--chain", - dest="chain", - help="Filter by blockchain", - metavar="CHAIN", - ) - parser.add_argument( - "-p", - "--page", - dest="page", - type=check_positive, - help="Page number", - default=1, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of records to display per page", - default=15, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - dappradar_view.display_dapps( - chain=ns_parser.chain, - page=ns_parser.page, - resultPerPage=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dapp_categories(self, other_args): - """Process dapp_categories command""" - parser = argparse.ArgumentParser( - prog="dapp_categories", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows available dapp categories [Source: https://dappradar.com/] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - dappradar_view.display_dapp_categories( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dapp_chains(self, other_args): - """Process dapp_chains command""" - parser = argparse.ArgumentParser( - prog="dapp_chains", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows available dapp chains [Source: https://dappradar.com/] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - dappradar_view.display_dapp_chains( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dapp_metrics(self, other_args): - """Process dapp_metrics command""" - parser = argparse.ArgumentParser( - prog="dapp_metrics", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows dapp metrics [Source: https://dappradar.com/] - Accepts --dappId argument to specify the dapp - --chain argument to filter by blockchain for multi-chain dapps - --time_range argument to specify the time range. Default: 7d (can be 24h, 7d, 30d) - """, - ) - parser.add_argument( - "-d", - "--dappId", - dest="dappId", - help="Dapp ID", - metavar="DAPP_ID", - ) - parser.add_argument( - "-c", - "--chain", - dest="chain", - help="Filter by blockchain", - metavar="CHAIN", - ) - parser.add_argument( - "-t", - "--time_range", - dest="time_range", - help="Time range", - metavar="TIME_RANGE", - choices=["24h", "7d", "30d"], - default="7d", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - dappradar_view.display_dapp_metrics( - dappId=ns_parser.dappId, - chain=ns_parser.chain, - time_range=ns_parser.time_range, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_gainers(self, other_args): - """Process gainers command""" - parser = argparse.ArgumentParser( - prog="gainers", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows Largest Gainers - coins which gain the most in given period. - You can use parameter --interval to set which timeframe are you interested in: {14d,1h,1y,200d,24h,30d,7d} - You can look on only N number of records with --limit, - You can sort by {Symbol,Name,Price [$],Market Cap,Market Cap Rank,Volume [$]} with --sort. - """, - ) - - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=str, - help="time period, one from {14d,1h,1y,200d,24h,30d,7d}", - default="1h", - choices=pycoingecko_model.API_PERIODS, - ) - - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of records to display", - default=15, - ) - - parser.add_argument( - "-s", - "--sort", - dest="sortby", - nargs="+", - help="Sort by given column. Default: Market Cap Rank", - default=["market_cap"], - choices=stocks_helper.format_parse_choices( - pycoingecko_model.GAINERS_LOSERS_COLUMNS - ), - metavar="SORTBY", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_gainers( - interval=ns_parser.interval, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=" ".join(ns_parser.sortby), - ascend=ns_parser.reverse, - ) - - @log_start_end(log=logger) - def call_losers(self, other_args): - """Process losers command""" - parser = argparse.ArgumentParser( - prog="losers", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows Largest Losers - coins which price dropped the most in given period - You can use parameter --interval to set which timeframe are you interested in: {14d,1h,1y,200d,24h,30d,7d} - You can look on only N number of records with --limit, - You can sort by {Symbol,Name,Price [$],Market Cap,Market Cap Rank,Volume [$]} with --sort. - """, - ) - - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=str, - help="time period, one from {14d,1h,1y,200d,24h,30d,7d}", - default="1h", - choices=pycoingecko_model.API_PERIODS, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of records to display", - default=15, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - nargs="+", - help="Sort by given column. Default: Market Cap Rank", - default=["Market Cap"], - choices=stocks_helper.format_parse_choices( - pycoingecko_model.GAINERS_LOSERS_COLUMNS - ), - metavar="SORTBY", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_losers( - interval=ns_parser.interval, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=" ".join(ns_parser.sortby), - ascend=ns_parser.reverse, - ) - - @log_start_end(log=logger) - def call_trending(self, other_args): - """Process trending command""" - parser = argparse.ArgumentParser( - prog="trending", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Discover trending coins (Top-7) on CoinGecko in the last 24 hours - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - if ns_parser: - pycoingecko_view.display_trending( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_search(self, other_args): - """Process search command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="search", - description="""Search over CoinPaprika API - You can display only N number of results with --limit parameter. - You can sort data by id, name , category --sort parameter and also with --reverse flag to sort descending. - To choose category in which you are searching for use --cat/-c parameter. Available categories: - currencies|exchanges|icos|people|tags|all - Displays: - id, name, category""", - ) - parser.add_argument( - "-q", - "--query", - help="phrase for search", - dest="query", - nargs="+", - type=str, - required="-h" not in other_args, - ) - parser.add_argument( - "-c", - "--category", - help="Categories to search: currencies|exchanges|icos|people|tags|all. Default: all", - dest="category", - default="all", - type=str, - choices=coinpaprika_model.CATEGORIES, - ) - parser.add_argument( - "-l", - "--limit", - default=10, - dest="limit", - help="Limit of records", - type=check_positive, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: id", - default="id", - choices=coinpaprika_model.FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-q") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - coinpaprika_view.display_search_results( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - query=" ".join(ns_parser.query), - category=ns_parser.category, - ) - - @log_start_end(log=logger) - def call_nft_mktp_chains(self, other_args): - """Process nft_mktp_chains command""" - parser = argparse.ArgumentParser( - prog="nft_mktp_chains", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows NFT marketplace chains [Source: https://dappradar.com/] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - dappradar_view.display_nft_marketplace_chains( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_defi_chains(self, other_args): - """Process defi_chains command""" - parser = argparse.ArgumentParser( - prog="defi_chains", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows DeFi chains [Source: https://dappradar.com/] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - dappradar_view.display_defi_chains( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_nft_mktp(self, other_args): - """Process nft_mktp command""" - parser = argparse.ArgumentParser( - prog="nft_mktp", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows NFT marketplaces [Source: https://dappradar.com/] - Accepts --chain to filter by blockchain - --sortby {name, avgPrice, volume, traders...} to sort by column - --order {asc, desc} to sort ascending or descending - --limit to limit number of records - """, - ) - parser.add_argument( - "-c", - "--chain", - dest="chain", - help="Name of the blockchain to filter by.", - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - nargs="+", - help="Sort by given column.", - choices=stocks_helper.format_parse_choices(dappradar_model.NFT_COLUMNS), - metavar="SORTBY", - ) - parser.add_argument( - "-o", - "--order", - dest="order", - help="Order of sorting. Default: desc", - default="desc", - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of records to display", - default=10, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - dappradar_view.display_nft_marketplaces( - sortby=ns_parser.sortby, - order=ns_parser.order, - chain=ns_parser.chain, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_tokens(self, other_args): - """Process tokens command""" - parser = argparse.ArgumentParser( - prog="tokens", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows chains that support tokens [Source: https://dappradar.com/] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - dappradar_view.display_token_chains( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/cryptocurrency/discovery/pycoingecko_model.py b/openbb_terminal/cryptocurrency/discovery/pycoingecko_model.py deleted file mode 100644 index 53864269e02..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/pycoingecko_model.py +++ /dev/null @@ -1,421 +0,0 @@ -"""CoinGecko model""" - -__docformat__ = "numpy" - -import json -import logging -import os -from typing import List - -import pandas as pd -from pycoingecko import CoinGeckoAPI - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - -COINS_COLUMNS_MAP = { - "Symbol": "symbol", - "Name": "name", - "Volume [$]": "total_volume", - "Market Cap": "market_cap", - "Market Cap Rank": "market_cap_rank", - "7D Change [%]": "price_change_percentage_7d_in_currency", - "24H Change [%]": "price_change_percentage_24h_in_currency", -} - -PERIODS = { - "1h": "?time=h1", - "24h": "?time=h24", - "7d": "?time=d7", - "14d": "?time=d14", - "30d": "?time=d30", - "60d": "?time=d60", - "1y": "?time=y1", -} - -API_PERIODS = ["14d", "1h", "1y", "200d", "24h", "30d", "7d"] - -CATEGORIES = { - "trending": 0, - "most_voted": 1, - "positive_sentiment": 2, - "recently_added": 3, - "most_visited": 4, -} - -GAINERS_FILTERS = ["Rank", "Symbol", "Name", "Volume", "Price", "Change"] -TRENDING_FILTERS = [ - "Rank", - "Name", - "Price_BTC", - "Price_USD", -] -RECENTLY_FILTERS = [ - "Rank", - "Name", - "Symbol", - "Price", - "Change_1h", - "Change_24h", - "Added", - "Url", -] - -YFARMS_FILTERS = [ - "Rank", - "Name", - "Value_Locked", - "Return_Year", -] - -CAP_FILTERS = [ - "Rank", - "Name", - "Symbol", - "Price", - "Change_1h", - "Change_24h", - "Change_7d", - "Volume_24h", - "Market_Cap", -] - -DEX_FILTERS = [ - "Name", - "Rank", - "Volume_24h", - "Coins", - "Pairs", - "Visits", - "Most_Traded", - "Market_Share", -] - -GAINERS_LOSERS_COLUMNS = [ - "Symbol", - "Name", - "Price [$]", - "Market Cap", - "Market Cap Rank", - "Volume [$]", -] - - -@log_start_end(log=logger) -def read_file_data(file_name: str) -> dict: - if file_name.split(".")[1] != "json": - raise TypeError("Please load json file") - - par_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - path = os.path.join(par_dir, "data", file_name) - with open(path, encoding="utf8") as f: - data = json.load(f) - return data - - -@log_start_end(log=logger) -def get_categories_keys() -> List[str]: - """Get list of categories keys - - Returns - ------- - List[str] - List of categories keys - """ - categories = read_file_data("coingecko_categories.json") - return list(categories.keys()) - - -@log_start_end(log=logger) -def get_coins( - limit: int = 250, - category: str = "", - sortby: str = "Symbol", - ascend: bool = False, -) -> pd.DataFrame: - """Get N coins from CoinGecko [Source: CoinGecko] - - Parameters - ---------- - limit: int - Number of top coins to grab from CoinGecko - category: str - Category of the coins we want to retrieve - sortby: str - Key to sort data - ascend: bool - Sort data in ascending order - - Returns - ------- - pd.DataFrame - N coins - """ - - sortby = sortby.replace("_", " ").title() - - client = CoinGeckoAPI() - df = pd.DataFrame() - table_size = limit - # CoinGecko's API returns all coins without limit when the category is set. - if category: - kwargs = { - "vs_currency": "usd", - "order": "market_cap_desc", - "per_page": limit, - "sparkline": False, - "price_change_percentage": "1h,24h,7d,14d,30d,200d,1y", - "category": category, - } - data = client.get_coins_markets(**kwargs) - df = pd.concat([df, pd.DataFrame(data)], ignore_index=True) - else: - page_size = min(limit, 250) - page = 1 - while limit > 0: - kwargs = { - "vs_currency": "usd", - "order": "market_cap_desc", - "per_page": page_size, - "sparkline": False, - "price_change_percentage": "1h,24h,7d,14d,30d,200d,1y", - "page": page, - } - - data = client.get_coins_markets(**kwargs) - df = pd.concat([df, pd.DataFrame(data)], ignore_index=True) - limit -= page_size - page += 1 - - if sortby in COINS_COLUMNS_MAP: - df = df[(df["total_volume"].notna()) & (df["market_cap"].notna())] - df = df.sort_values(by=COINS_COLUMNS_MAP[sortby], ascending=ascend) - - df = df.astype({"market_cap_rank": "Int64"}) - - return df.head(table_size) - - -@log_start_end(log=logger) -def get_gainers_or_losers( - limit: int = 20, - interval: str = "1h", - typ: str = "gainers", - sortby: str = "market_cap", - ascend: bool = True, -) -> pd.DataFrame: - """Returns data about top gainers - coins which gain the most in given period and - top losers - coins that lost the most in given period of time. [Source: CoinGecko] - - Parameters - ---------- - limit: int - Num of coins to get - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - API documentation (see /coins/markets in https://www.coingecko.com/en/api/documentation) - ascend: bool - Sort data in ascending order - interval: str - One from {14d,1h,1y,200d,24h,30d,7d} - typ: str - Either "gainers" or "losers" - - Returns - ------- - pd.DataFrame - Top Gainers / Top Losers - coins which gain/lost most in price in given period of time. - Columns: Symbol, Name, Volume, Price, %Change_{interval}, Url - """ - - sortby = sortby.replace("_", " ").title() - - if interval not in API_PERIODS: - raise ValueError( - f"Wrong time period\nPlease chose one from list: {API_PERIODS}" - ) - df = get_coins(limit) - sorted_df = df.sort_values( - by=[f"price_change_percentage_{interval}_in_currency"], - ascending=typ != "gainers", - ) - sorted_df = sorted_df[ - [ - "symbol", - "name", - "current_price", - "market_cap", - "market_cap_rank", - "total_volume", - f"price_change_percentage_{interval}_in_currency", - ] - ] - sorted_df = sorted_df.set_axis( - GAINERS_LOSERS_COLUMNS + [f"Change {interval} [%]"], - axis=1, - copy=True, - ) - - if sortby in GAINERS_LOSERS_COLUMNS: - sorted_df = sorted_df[ - (sorted_df["Volume [$]"].notna()) & (sorted_df["Market Cap"].notna()) - ] - sorted_df = sorted_df.sort_values(by=sortby, ascending=ascend) - - return sorted_df - - -def get_gainers( - interval: str = "1h", - limit: int = 50, - sortby: str = "market_cap_rank", - ascend: bool = True, -) -> pd.DataFrame: - """Shows Largest Gainers - coins which gain the most in given period. [Source: CoinGecko] - - Parameters - ---------- - interval: str - Time interval by which data is displayed. One from [1h, 24h, 7d, 14d, 30d, 60d, 1y] - limit: int - Number of records to display - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - API documentation (see /coins/markets in https://www.coingecko.com/en/api/documentation) - ascend: bool - Sort data in ascending order - - Returns - ------- - pd.DataFrame - Top Gainers - coins which gain most in price in given period of time. - Columns: Symbol, Name, Volume, Price, %Change_{interval}, Url - """ - return get_gainers_or_losers( - limit=limit, - interval=interval, - typ="gainers", - sortby=sortby, - ascend=ascend, - ) - - -def get_losers( - interval: str = "1h", - limit: int = 50, - sortby: str = "market_cap_rank", - ascend: bool = True, -) -> pd.DataFrame: - """Shows Largest Losers - coins which lose the most in given period. [Source: CoinGecko] - - Parameters - ---------- - interval: str - Time interval by which data is displayed. One from [1h, 24h, 7d, 14d, 30d, 60d, 1y] - limit: int - Number of records to display - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - API documentation (see /coins/markets in https://www.coingecko.com/en/api/documentation) - ascend: bool - Sort data in ascending order - - Returns - ------- - pd.DataFrame - Top Losers - coins which lost most in price in given period of time. - Columns: Symbol, Name, Volume, Price, %Change_{interval}, Url - """ - return get_gainers_or_losers( - limit=limit, interval=interval, typ="losers", sortby=sortby, ascend=ascend - ) - - -@log_start_end(log=logger) -def get_trending_coins() -> pd.DataFrame: - """Returns trending coins [Source: CoinGecko] - - Parameters - ---------- - - Returns - ------- - pd.DataFrame - Trending Coins - """ - client = CoinGeckoAPI() - data = client.get_search_trending() - coins = data["coins"] - df = pd.DataFrame(columns=["Symbol", "Name", "market_cap Cap Rank"]) - for i, coin in enumerate(coins): - coin_data = coin["item"] - df.loc[i] = [coin_data["id"], coin_data["name"], coin_data["market_cap_rank"]] - return df - - -@log_start_end(log=logger) -def get_coin_list() -> pd.DataFrame: - """Get list of coins available on CoinGecko [Source: CoinGecko] - - Returns - ------- - pd.DataFrame - Coins available on CoinGecko - Columns: id, symbol, name - """ - - client = CoinGeckoAPI() - return pd.DataFrame( - client.get_coins_list(), - columns=["id", "symbol", "name"], - ).reset_index() - - -@log_start_end(log=logger) -def get_coins_for_given_exchange(exchange_id: str = "binance", page: int = 1) -> dict: - """Helper method to get all coins available on binance exchange [Source: CoinGecko] - - Parameters - ---------- - exchange_id: str - id of exchange - page: int - number of page. One page contains 100 records - - Returns - ------- - dict - dictionary with all trading pairs on binance - """ - - client = CoinGeckoAPI() - binance_coins = client.get_exchanges_tickers_by_id(id=exchange_id, page=page) - return binance_coins["tickers"] - - -@log_start_end(log=logger) -def get_mapping_matrix_for_exchange(exchange_id: str, pages: int = 12) -> dict: - """Creates a matrix with all coins available on Binance with corresponding coingecko coin_id. [Source: CoinGecko] - - Parameters - ---------- - exchange_id: str - id of exchange: binance - pages: int - number of pages. One page contains 100 records - - Returns - ------- - dict - dictionary with all coins: {"ETH" : "ethereum"} - """ - - coins_dct = {} - for i in range(pages): - coins = get_coins_for_given_exchange(exchange_id=exchange_id, page=i) - for coin in coins: - bin_symbol, gecko_id = coin["base"], coin["coin_id"] - if bin_symbol not in coins_dct: - coins_dct[bin_symbol] = gecko_id - return coins_dct diff --git a/openbb_terminal/cryptocurrency/discovery/pycoingecko_view.py b/openbb_terminal/cryptocurrency/discovery/pycoingecko_view.py deleted file mode 100644 index 906f5e8649a..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/pycoingecko_view.py +++ /dev/null @@ -1,237 +0,0 @@ -"""CoinGecko view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_very_long_number_formatter, -) -from openbb_terminal.cryptocurrency.discovery import pycoingecko_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -# pylint: disable=R0904, C0302 - -COINS_COLUMNS = [ - "Symbol", - "Name", - "Volume [$]", - "Market Cap", - "Market Cap Rank", - "7D Change [%]", - "24H Change [%]", -] - - -@log_start_end(log=logger) -def display_coins( - category: str, - limit: int = 250, - sortby: str = "Symbol", - export: str = "", - sheet_name: Optional[str] = None, - ascend: bool = False, -) -> None: - """Prints table showing top coins [Source: CoinGecko] - - Parameters - ---------- - category: str - If no category is passed it will search for all coins. (E.g., smart-contract-platform) - limit: int - Number of records to display - sortby: str - Key to sort data - export : str - Export dataframe data to csv,json,xlsx file - ascend: bool - Sort data in ascending order - """ - df = pycoingecko_model.get_coins( - limit=limit, - category=category, - sortby=sortby, - ascend=ascend, - ) - if not df.empty: - df = df[ - [ - "symbol", - "name", - "total_volume", - "market_cap", - "market_cap_rank", - "price_change_percentage_7d_in_currency", - "price_change_percentage_24h_in_currency", - ] - ] - df = df.set_axis( - COINS_COLUMNS, - axis=1, - copy=True, - ) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cgtop", - df, - sheet_name, - ) - else: - console.print("\nUnable to retrieve data from CoinGecko.\n") - - -@log_start_end(log=logger) -def display_gainers( - interval: str = "1h", - limit: int = 20, - sortby: str = "market_cap_rank", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing Largest Gainers - coins which gain the most in given period. [Source: CoinGecko] - - Parameters - ---------- - interval: str - Time period by which data is displayed. One from [1h, 24h, 7d, 14d, 30d, 60d, 1y] - limit: int - Number of records to display - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - API documentation (see /coins/markets in https://www.coingecko.com/en/api/documentation) - ascend: bool - Sort data in ascending order - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = pycoingecko_model.get_gainers( - limit=limit, interval=interval, sortby=sortby, ascend=ascend - ) - - if not df.empty: - for col in ["Volume [$]", "Market Cap"]: - if col in df.columns: - df[col] = df[col].apply(lambda x: lambda_very_long_number_formatter(x)) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gainers", - df, - sheet_name, - ) - else: - console.print("\nUnable to retrieve data from CoinGecko.\n") - - -@log_start_end(log=logger) -def display_losers( - interval: str = "1h", - limit: int = 20, - export: str = "", - sheet_name: Optional[str] = None, - sortby: str = "Market Cap Rank", - ascend: bool = False, -) -> None: - """Prints table showing Largest Losers - coins which lost the most in given period of time. [Source: CoinGecko] - - Parameters - ---------- - interval: str - Time period by which data is displayed. One from [1h, 24h, 7d, 14d, 30d, 60d, 1y] - limit: int - Number of records to display - sortby: str - Key to sort data. The table can be sorted by every of its columns. Refer to - API documentation (see /coins/markets in https://www.coingecko.com/en/api/documentation) - ascend: bool - Sort data in ascending order - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = pycoingecko_model.get_losers( - limit=limit, interval=interval, sortby=sortby, ascend=ascend - ) - - if not df.empty: - for col in ["Volume [$]", "Market Cap"]: - if col in df.columns: - df[col] = df[col].apply(lambda x: lambda_very_long_number_formatter(x)) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cglosers", - df, - sheet_name, - ) - else: - console.print("\nUnable to retrieve data from CoinGecko.\n") - - -@log_start_end(log=logger) -def display_trending(export: str = "", sheet_name: Optional[str] = None) -> None: - """Prints table showing trending coins [Source: CoinGecko] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = pycoingecko_model.get_trending_coins() - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - floatfmt=".4f", - show_index=False, - title="Trending coins on CoinGecko", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cgtrending", - df, - sheet_name, - ) - else: - console.print("\nUnable to retrieve data from CoinGecko.\n") diff --git a/openbb_terminal/cryptocurrency/discovery/sdk_helpers.py b/openbb_terminal/cryptocurrency/discovery/sdk_helpers.py deleted file mode 100644 index bf23ae134c1..00000000000 --- a/openbb_terminal/cryptocurrency/discovery/sdk_helpers.py +++ /dev/null @@ -1,43 +0,0 @@ -"""SDK Helper Functions.""" - -__docfromat__ = "numpy" - -import pandas as pd - -from openbb_terminal.cryptocurrency.discovery import ( - coinmarketcap_model, - pycoingecko_model, -) - - -def top_coins(source: str = "CoinGecko", limit: int = 10) -> pd.DataFrame: - """Get top cryptp coins. - - Parameters - ---------- - source : str, optional - Source of data, by default "CoinGecko" - limit : int, optional - Number of coins to return, by default 10 - - Returns - ------- - pd.DataFrame - DataFrame with top coins - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> top_coins = openbb.crypto.disc.top_coins() - - To get 30 results from coinmarketcap, use the source parameter and the limit parameter: - >>> top_coins = openbb.crypto.disc.top_coins(source="CoinMarketCap", limit=30) - - """ - if source.lower() == "coingecko": - df = pycoingecko_model.get_coins(limit=limit) - return df - if source.lower() == "coinmarketcap": - df = coinmarketcap_model.get_cmc_top_n() - return df - return pd.DataFrame() diff --git a/openbb_terminal/cryptocurrency/due_diligence/__init__.py b/openbb_terminal/cryptocurrency/due_diligence/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/cryptocurrency/due_diligence/binance_model.py b/openbb_terminal/cryptocurrency/due_diligence/binance_model.py deleted file mode 100644 index 195cc89e964..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/binance_model.py +++ /dev/null @@ -1,236 +0,0 @@ -"""Binance model""" - -__docformat__ = "numpy" - -import argparse -import logging -from collections import defaultdict -from typing import List, Tuple, Union - -import pandas as pd -from binance.client import Client -from binance.exceptions import BinanceAPIException - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_BINANCE_KEY", "API_BINANCE_SECRET"]) -def _get_trading_pairs() -> List[dict]: - """Helper method that return all trading pairs on binance. Methods ause this data for input for e.g - building dataframe with all coins, or to build dict of all trading pairs. [Source: Binance] - - Returns - ------- - List[dict] - list of dictionaries in format: - [ - {'symbol': 'ETHBTC', 'status': 'TRADING', 'baseAsset': 'ETH', 'baseAssetPrecision': 8, - 'quoteAsset': 'BTC', 'quotePrecision': 8, 'quoteAssetPrecision': 8, - 'baseCommissionPrecision': 8, 'quoteCommissionPrecision': 8, - 'orderTypes': ['LIMIT', 'LIMIT_MAKER', 'MARKET', 'STOP_LOSS_LIMIT', 'TAKE_PROFIT_LIMIT'], - 'icebergAllowed': True, - 'ocoAllowed': True, - 'quoteOrderQtyMarketAllowed': True, - 'isSpotTradingAllowed': True, - 'isMarginTradingAllowed': True, - 'filters': [{'filterType': 'PRICE_FILTER', 'minPrice': '0.00000100', - 'maxPrice': '922327.00000000', 'tickSize': '0.00000100'}, - {'filterType': 'PERCENT_PRICE', 'multiplierUp': '5', 'multiplierDown': '0.2', 'avgPriceMins': 5}, - {'filterType': 'LOT_SIZE', 'minQty': '0.00100000', 'maxQty': '100000.00000000', 'stepSize': '0.00100000'}, - {'filterType': 'MIN_NOTIONAL', 'minNotional': '0.00010000', 'applyToMarket': True, 'avgPriceMins': 5}, - {'filterType': 'ICEBERG_PARTS', 'limit': 10}, {'filterType': 'MARKET_LOT_SIZE', 'minQty': '0.00000000', - 'maxQty': '930.49505347', 'stepSize': '0.00000000'}, {'filterType': 'MAX_NUM_ORDERS', 'maxNumOrders': 200}, - {'filterType': 'MAX_NUM_ALGO_ORDERS', 'maxNumAlgoOrders': 5}], 'permissions': ['SPOT', 'MARGIN']}, - ... - ] - """ - current_user = get_current_user() - client = Client( - current_user.credentials.API_BINANCE_KEY, - current_user.credentials.API_BINANCE_SECRET, - ) - ex_info = client.get_exchange_info()["symbols"] - trading_pairs = [p for p in ex_info if p["status"] == "TRADING"] - return trading_pairs - - -@log_start_end(log=logger) -@check_api_key(["API_BINANCE_KEY", "API_BINANCE_SECRET"]) -def get_all_binance_trading_pairs() -> pd.DataFrame: - """Returns all available pairs on Binance in DataFrame format. DataFrame has 3 columns symbol, baseAsset, quoteAsset - example row: ETHBTC | ETH | BTC - [Source: Binance] - - - Returns - ------- - pd.DataFrame - All available pairs on Binance - Columns: symbol, baseAsset, quoteAsset - - """ - trading_pairs = _get_trading_pairs() - return pd.DataFrame(trading_pairs)[["symbol", "baseAsset", "quoteAsset"]] - - -@log_start_end(log=logger) -@check_api_key(["API_BINANCE_KEY", "API_BINANCE_SECRET"]) -def get_binance_available_quotes_for_each_coin() -> dict: - """Helper methods that for every coin available on Binance add all quote assets. [Source: Binance] - - Returns - ------- - dict - All quote assets for given coin - {'ETH' : ['BTC', 'USDT' ...], 'UNI' : ['ETH', 'BTC','BUSD', ...] - - """ - trading_pairs = _get_trading_pairs() - results = defaultdict(list) - for pair in trading_pairs: - results[pair["baseAsset"]].append(pair["quoteAsset"]) - return results - - -@log_start_end(log=logger) -@check_api_key(["API_BINANCE_KEY", "API_BINANCE_SECRET"]) -def check_valid_binance_str(symbol: str) -> str: - """Check if symbol is in defined binance. [Source: Binance] - - Returns - ------- - str - Symbol - """ - current_user = get_current_user() - client = Client( - current_user.credentials.API_BINANCE_KEY, - current_user.credentials.API_BINANCE_SECRET, - ) - try: - client.get_avg_price(symbol=symbol.upper()) - return symbol.upper() - except BinanceAPIException as e: - logger.exception("%s is not a valid binance symbol", str(symbol)) - raise argparse.ArgumentTypeError( - f"{symbol} is not a valid binance symbol" - ) from e - - -@log_start_end(log=logger) -@check_api_key(["API_BINANCE_KEY", "API_BINANCE_SECRET"]) -def show_available_pairs_for_given_symbol( - symbol: str = "ETH", -) -> Tuple[Union[str, None], list]: - """Return all available quoted assets for given symbol. [Source: Binance] - - Parameters - ---------- - symbol: str - Uppercase symbol of coin e.g BTC, ETH, UNI, LUNA, DOT ... - - Returns - ------- - Tuple[Union[str, None], list] - Tuple of symbol, - list of quoted assets for given symbol: ["BTC", "USDT" , "BUSD"] - """ - - symbol_upper = symbol.upper() - pairs = get_binance_available_quotes_for_each_coin() - for k, v in pairs.items(): - if k == symbol_upper: - return k, v - return None, [] - - -@log_start_end(log=logger) -@check_api_key(["API_BINANCE_KEY", "API_BINANCE_SECRET"]) -def get_order_book( - from_symbol: str, - limit: int = 100, - to_symbol: str = "USDT", -) -> pd.DataFrame: - """Get order book for currency. [Source: Binance] - - Parameters - ---------- - from_symbol: str - Cryptocurrency symbol - limit: int - Limit parameter. Adjusts the weight - to_symbol: str - Quote currency (what to view coin vs) - - Returns - ------- - - pd.DataFrame - Dataframe containing orderbook - """ - pair = from_symbol + to_symbol - - current_user = get_current_user() - client = Client( - current_user.credentials.API_BINANCE_KEY, - current_user.credentials.API_BINANCE_SECRET, - ) - - try: - market_book = client.get_order_book(symbol=pair, limit=limit) - except BinanceAPIException: - console.print(f"{to_symbol} is not a valid binance symbol") - return pd.DataFrame(market_book) - - -@log_start_end(log=logger) -@check_api_key(["API_BINANCE_KEY", "API_BINANCE_SECRET"]) -def get_balance( - from_symbol: str, - to_symbol: str = "USDT", -) -> pd.DataFrame: - """Get account holdings for asset. [Source: Binance] - - Parameters - ---------- - from_symbol: str - Cryptocurrency - to_symbol: str - Cryptocurrency - - Returns - ------- - pd.DataFrame - Dataframe with account holdings for an asset - """ - current_user = get_current_user() - client = Client( - current_user.credentials.API_BINANCE_KEY, - current_user.credentials.API_BINANCE_SECRET, - ) - - pair = from_symbol + to_symbol - current_balance = client.get_asset_balance(asset=from_symbol) - if current_balance is None: - console.print("Check loaded coin\n") - return pd.DataFrame() - - last_price = client.get_ticker(symbol=pair).get("lastPrice") - amounts = [float(current_balance["free"]), float(current_balance["locked"])] - - df = pd.DataFrame(amounts) - df.columns = ["Amount"] - df.index = ["Free", "Locked"] - - if last_price: - last_price = float(last_price) - df[f"Amount [{to_symbol}]"] = df["Amount"].mul(last_price) - - df["Percent"] = df["Amount"].mul(100).div(df["Amount"].sum(axis=0)).round(3) - - return df diff --git a/openbb_terminal/cryptocurrency/due_diligence/binance_view.py b/openbb_terminal/cryptocurrency/due_diligence/binance_view.py deleted file mode 100644 index 853e3ed9cfa..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/binance_view.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Binance view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import numpy as np - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.cryptocurrency_helpers import plot_order_book -from openbb_terminal.cryptocurrency.due_diligence.binance_model import ( - get_balance, - get_order_book, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_order_book( - from_symbol: str, - limit: int = 100, - to_symbol: str = "USDT", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots order book for currency. [Source: Binance] - - Parameters - ---------- - - from_symbol: str - Cryptocurrency symbol - limit: int - Limit parameter. Adjusts the weight - to_symbol: str - Quote currency (what to view coin vs) - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - market_book = get_order_book(from_symbol, limit, to_symbol) - 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) - fig = plot_order_book(bids, asks, to_symbol) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "book", - market_book, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_balance( - from_symbol: str, - to_symbol: str = "USDT", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing account holdings for asset. [Source: Binance] - - Parameters - ---------- - from_symbol: str - Cryptocurrency - to_symbol: str - Cryptocurrency - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx - """ - - df = get_balance(from_symbol, to_symbol) - - if df is None or df.empty: - console.print("[red]No data found[/red]\n") - return - - total = np.sum(df["Amount"]) - console.print(f"You currently have {total} coins and the breakdown is:\n") - - print_rich_table( - df, - headers=df.columns, - show_index=True, - title="Account Holdings for Assets", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "book", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/ccxt_model.py b/openbb_terminal/cryptocurrency/due_diligence/ccxt_model.py deleted file mode 100644 index 2681dca9cb8..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/ccxt_model.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Ccxt model""" - -__docformat__ = "numpy" - -from typing import Any, Dict, List - -import ccxt -import pandas as pd - -from openbb_terminal.cryptocurrency.dataframe_helpers import prettify_column_names - - -def get_exchanges() -> List[str]: - """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_binance_currencies() -> List[str]: - """Helper method to get all the currenices supported by ccxt - [Source: https://docs.ccxt.com/en/latest/manual.html] - - Parameters - ---------- - - Returns - ------- - List[str] - list of all the currenices supported by ccxt - """ - - # Refactor this eventually to allow for any entered exchange - - # right now only works on default binace for "ob" and "trades" - - # Commented out for now, since binance started blocking requests - # exchange = ccxt.binance({"fetchCurrencies": True}) - # exchange.load_markets() - # currencies = exchange.quoteCurrencies - # return [c["code"] for c in currencies.values()] - return [ - "AUD", - "BIDR", - "BKRW", - "BNB", - "BRL", - "BTC", - "BUSD", - "BVND", - "DAI", - "DOGE", - "DOT", - "ETH", - "EUR", - "GBP", - "IDRT", - "NGN", - "PAX", - "PLN", - "RUB", - "TRX", - "TRY", - "TUSD", - "UAH", - "USDC", - "USDP", - "USDS", - "USDT", - "UST", - "VAI", - "XRP", - "ZAR", - ] - - -def get_orderbook(exchange: str, symbol: str, to_symbol: str) -> Dict[str, Any]: - """Returns orderbook for a coin in a given exchange - [Source: https://docs.ccxt.com/en/latest/manual.html] - - Parameters - ---------- - exchange : str - exchange id - symbol : str - coin symbol - to_symbol : str - currency to compare coin against - - Returns - ------- - Dict[str, Any] - With bids and asks - """ - exchange_class = getattr(ccxt, exchange) - exchange_cls = exchange_class() - ob = exchange_cls.fetch_order_book(f"{symbol.upper()}/{to_symbol.upper()}") - return ob - - -def get_trades(exchange_id: str, symbol: str, to_symbol: 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 - symbol : str - coin symbol - to_symbol : 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"{symbol.upper()}/{to_symbol.upper()}") - df = pd.DataFrame(trades, columns=["datetime", "price", "amount", "cost", "side"]) - df["datetime"] = pd.to_datetime(df["datetime"]) - df.rename(columns={"datetime": "date"}, inplace=True) - df.columns = prettify_column_names(df.columns) - return df diff --git a/openbb_terminal/cryptocurrency/due_diligence/ccxt_view.py b/openbb_terminal/cryptocurrency/due_diligence/ccxt_view.py deleted file mode 100644 index cd28acb3e35..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/ccxt_view.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Ccxt view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.cryptocurrency_helpers import plot_order_book -from openbb_terminal.cryptocurrency.due_diligence import ccxt_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_order_book( - exchange: str, - symbol: str, - to_symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots order book for a coin in a given exchange - [Source: https://docs.ccxt.com/en/latest/manual.html] - - Parameters - ---------- - exchange : str - exchange id - symbol : 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=exchange, symbol=symbol, to_symbol=to_symbol - ) - 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) - fig = plot_order_book( - bids, asks, f"{exchange.upper()}:{symbol.upper()}/{to_symbol.upper()}" - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ob", - pd.DataFrame(market_book), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_trades( - exchange: str, - symbol: str, - to_symbol: str, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """Prints table showing trades for a coin in a given exchange - [Source: https://docs.ccxt.com/en/latest/manual.html] - - Parameters - ---------- - exchange : str - exchange id - symbol : str - coin symbol - to_symbol : str - currency to compare coin against - limit : int - number of trades to display - export : str - Export dataframe data to csv,json,xlsx file - """ - df = ccxt_model.get_trades(exchange_id=exchange, symbol=symbol, to_symbol=to_symbol) - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"Trades for {exchange.upper()}:{symbol.upper()}/{to_symbol.upper()}", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "trades", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/coinbase_model.py b/openbb_terminal/cryptocurrency/due_diligence/coinbase_model.py deleted file mode 100644 index 0221bcca144..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/coinbase_model.py +++ /dev/null @@ -1,227 +0,0 @@ -"""Coinbase model""" - -__docformat__ = "numpy" - -import logging -from typing import Any, Optional, Tuple - -import numpy as np -import pandas as pd - -from openbb_terminal.cryptocurrency.coinbase_helpers import ( - check_validity_of_product, - make_coinbase_request, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def show_available_pairs_for_given_symbol(symbol: str = "ETH") -> Tuple[str, list]: - """Return all available quoted assets for given symbol. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Uppercase symbol of coin e.g BTC, ETH, UNI, LUNA, DOT ... - - Returns - ------- - Tuple[str, list] - Symbol and list of available pairs - """ - pairs = make_coinbase_request("/products") - df = pd.DataFrame(pairs)[["base_currency", "quote_currency"]] - - if not isinstance(symbol, str): - console.print( - f"You did not provide correct symbol {symbol}. Symbol needs to be a string.\n" - ) - return symbol, [] - - coin_df = df[df["base_currency"] == symbol.upper()] - return symbol, coin_df["quote_currency"].to_list() - - -@log_start_end(log=logger) -def get_trading_pair_info(symbol: str) -> pd.DataFrame: - """Get information about chosen trading pair. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - - Returns - ------- - pd.DataFrame - Basic information about given trading pair - """ - - product_id = check_validity_of_product(symbol) - pair = make_coinbase_request(f"/products/{product_id}") - df = pd.Series(pair).to_frame().reset_index() - df.columns = ["Metric", "Value"] - console.print(df) - return df - - -@log_start_end(log=logger) -def get_order_book(symbol: str) -> Tuple[np.ndarray, np.ndarray, str, dict]: - """Get orders book for chosen trading pair. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - - Returns - ------- - Tuple[np.array, np.array, str, dict] - array with bid prices, order sizes and cumulative order sizes - array with ask prices, order sizes and cumulative order sizes - trading pair - dict with raw data - """ - - # TODO: Order price > current price. E.g current price 200 USD, sell order with 10000 USD - # makes chart look very ugly (bad scaling). Think about removing outliers or add log scale ? - - product_id = check_validity_of_product(symbol) - market_book = make_coinbase_request(f"/products/{product_id}/book?level=2") - - size = min( - len(market_book["bids"]), len(market_book["asks"]) - ) # arrays needs to have equal size. - - market_book["bids"] = market_book["bids"][:size] - market_book["asks"] = market_book["asks"][:size] - market_book.pop("sequence") - - bids = np.asarray(market_book["bids"], dtype=float)[:size] - asks = np.asarray(market_book["asks"], dtype=float)[:size] - - bids = np.insert(bids, 3, (bids[:, 1] * bids[:, 2]).cumsum(), axis=1) - asks = np.insert(asks, 3, np.flipud(asks[:, 1] * asks[:, 2]).cumsum(), axis=1) - bids = np.delete(bids, 2, axis=1) - asks = np.delete(asks, 2, axis=1) - return bids, asks, product_id, market_book - - -@log_start_end(log=logger) -def get_trades( - symbol: str, limit: int = 1000, side: Optional[Any] = None -) -> pd.DataFrame: - """Get last N trades for chosen trading pair. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - limit: int - Last `limit` of trades. Maximum is 1000. - side: str - You can chose either sell or buy side. If side is not set then all trades will be displayed. - - Returns - ------- - pd.DataFrame - Last N trades for chosen trading pairs. - """ - - params = {"limit": limit} - if side is not None and side in ["buy", "sell"]: - params["side"] = side - - product_id = check_validity_of_product(symbol) - product = make_coinbase_request(f"/products/{product_id}/trades", params=params) - return pd.DataFrame(product)[["time", "price", "size", "side"]] - - -@log_start_end(log=logger) -def get_candles(symbol: str, interval: str = "24hour") -> pd.DataFrame: - """Get candles for chosen trading pair and time interval. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - interval: str - Time interval. One from 1min, 5min ,15min, 1hour, 6hour, 24hour, 1day - - Returns - ------- - pd.DataFrame - Candles for chosen trading pair. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.crypto.dd.candle(symbol="eth-usdt", interval="24hour") - """ - - interval_map = { - "1min": 60, - "5min": 300, - "15min": 900, - "1hour": 3600, - "6hour": 21600, - "24hour": 86400, - "1day": 86400, - } - if interval not in interval_map: - console.print( - f"Wrong interval. Please use on from {list(interval_map.keys())}\n" - ) - return pd.DataFrame() - - granularity: int = interval_map[interval] - - product_id = check_validity_of_product(symbol) - candles = make_coinbase_request( - f"/products/{product_id}/candles", params={"granularity": granularity} - ) - df = pd.DataFrame(candles) - df.columns = [ - "date", - "Low", - "High", - "Open", - "Close", - "Volume", - ] - return df[ - [ - "date", - "Open", - "High", - "Low", - "Close", - "Volume", - ] - ] - - -@log_start_end(log=logger) -def get_product_stats(symbol: str) -> pd.DataFrame: - """Get 24 hr stats for the product. Volume is in base currency units. - Open, high and low are in quote currency units. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - - Returns - ------- - pd.DataFrame - 24h stats for chosen trading pair - """ - - product_id = check_validity_of_product(symbol) - product = make_coinbase_request(f"/products/{product_id}/stats") - df = pd.Series(product).reset_index() - df.columns = ["Metric", "Value"] - return df diff --git a/openbb_terminal/cryptocurrency/due_diligence/coinbase_view.py b/openbb_terminal/cryptocurrency/due_diligence/coinbase_view.py deleted file mode 100644 index 7e8523c6be0..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/coinbase_view.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Coinbase view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.cryptocurrency_helpers import plot_order_book -from openbb_terminal.cryptocurrency.due_diligence import coinbase_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_order_book( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots a list of available currency pairs for trading. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - bids, asks, pair, market_book = coinbase_model.get_order_book(symbol) - fig = plot_order_book(bids, asks, pair) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "book", - pd.DataFrame(market_book), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_trades( - symbol: str, - limit: int = 20, - side: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing last N trades for chosen trading pair. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - limit: int - Last `limit` of trades. Maximum is 1000. - side: Optional[str] - You can chose either sell or buy side. If side is not set then all trades will be displayed. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = coinbase_model.get_trades(symbol, limit, side) - - print_rich_table( - df, headers=list(df.columns), show_index=False, export=bool(export) - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "trades", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_candles( - symbol: str, - interval: str = "24hour", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing candles for chosen trading pair and time interval. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - interval: str - Time interval. One from 1min, 5min ,15min, 1hour, 6hour, 24hour, 1day - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = coinbase_model.get_candles(symbol, interval) - - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title="Trading Pair Candles", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "candles", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_stats( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing 24 hr stats for the product. Volume is in base currency units. - Open, high and low are in quote currency units. [Source: Coinbase] - - Parameters - ---------- - symbol: str - Trading pair of coins on Coinbase e.g ETH-USDT or UNI-ETH - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = coinbase_model.get_product_stats(symbol) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"Coinbase:{symbol.upper()} 24 hr Product Stats", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "stats", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/coinglass_model.py b/openbb_terminal/cryptocurrency/due_diligence/coinglass_model.py deleted file mode 100644 index 8a7c0baff57..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/coinglass_model.py +++ /dev/null @@ -1,208 +0,0 @@ -import datetime as dt -import json -import logging - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -api_url = "https://open-api.coinglass.com/api/pro/v1/" - -# Prompt toolkit does not allow integers, so these items need to be strings -INTERVALS = [0, 1, 2, 4] - - -@log_start_end(log=logger) -@check_api_key(["API_COINGLASS_KEY"]) -def get_liquidations(symbol: str) -> pd.DataFrame: - """Returns liquidations per day for a certain symbol - [Source: https://coinglass.github.io/API-Reference/#liquidation-chart] - - Parameters - ---------- - symbol : str - Crypto Symbol to search daily liquidations (e.g., BTC) - - Returns - ------- - pd.DataFrame - daily liquidations for loaded symbol - """ - - url = api_url + f"futures/liquidation_chart?symbol={symbol.upper()}" - - headers = {"coinglassSecret": get_current_user().credentials.API_COINGLASS_KEY} - - response = request(url, headers=headers) - - df = pd.DataFrame() - - if response.status_code == 200: - res_json = json.loads(response.text) - - if res_json["success"]: - if "data" in res_json: - 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"], - "Shorts": data["buyList"], - "Longs": data["sellList"], - } - ) - df = df.set_index("date") - else: - console.print(f"No data found for {symbol}.\n") - elif "secret invalid" in res_json["msg"]: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(res_json["msg"]) - - elif response.status_code == 429: - console.print("[red]Exceeded number of calls per minute[/red]\n") - elif response.status_code == 429: - console.print( - "[red]IP address autobanned for exceeding calls limit multiple times.[/red]\n" - ) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_COINGLASS_KEY"]) -def get_funding_rate(symbol: str) -> pd.DataFrame: - """Returns open interest by exchange for a certain symbol - [Source: https://coinglass.github.io/API-Reference/] - - Parameters - ---------- - symbol : str - Crypto Symbol to search open interest futures (e.g., BTC) - - Returns - ------- - pd.DataFrame - funding rate per exchange - """ - - url = api_url + f"futures/funding_rates_chart?symbol={symbol.upper()}&type=C" - - headers = {"coinglassSecret": get_current_user().credentials.API_COINGLASS_KEY} - - response = request(url, headers=headers) - - df = pd.DataFrame() - - if response.status_code == 200: - res_json = json.loads(response.text) - - if res_json["success"]: - if "data" in res_json: - 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") - else: - console.print(f"No data found for {symbol}.\n") - elif "secret invalid" in res_json["msg"]: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(res_json["msg"]) - - elif response.status_code == 429: - console.print("[red]Exceeded number of calls per minute[/red]\n") - elif response.status_code == 429: - console.print( - "[red]IP address autobanned for exceeding calls limit multiple times.[/red]\n" - ) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_COINGLASS_KEY"]) -def get_open_interest_per_exchange(symbol: str, interval: int = 0) -> pd.DataFrame: - """Returns open interest by exchange for a certain symbol - [Source: https://coinglass.github.io/API-Reference/] - - Parameters - ---------- - symbol : str - Crypto Symbol to search open interest futures (e.g., BTC) - interval : int - Frequency (possible values are: 0 for ALL, 2 for 1H, 1 for 4H, 4 for 12H), by default 0 - - Returns - ------- - pd.DataFrame - open interest by exchange and price - """ - - url = ( - api_url - + f"futures/openInterest/chart?symbol={symbol.upper()}&interval={interval}" - ) - - headers = {"coinglassSecret": get_current_user().credentials.API_COINGLASS_KEY} - - response = request(url, headers=headers) - df = pd.DataFrame() - - if response.status_code == 200: - res_json = json.loads(response.text) - - if res_json["success"]: - if "data" in res_json: - 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") - else: - console.print(f"No data found for {symbol}.\n") - elif "secret invalid" in res_json["msg"]: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(res_json["msg"]) - - elif response.status_code == 429: - console.print("[red]Exceeded number of calls per minute[/red]\n") - elif response.status_code == 429: - console.print( - "[red]IP address autobanned for exceeding calls limit multiple times.[/red]\n" - ) - - return df diff --git a/openbb_terminal/cryptocurrency/due_diligence/coinglass_view.py b/openbb_terminal/cryptocurrency/due_diligence/coinglass_view.py deleted file mode 100644 index 375565becbf..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/coinglass_view.py +++ /dev/null @@ -1,264 +0,0 @@ -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.due_diligence.coinglass_model import ( - get_funding_rate, - get_liquidations, - get_open_interest_per_exchange, -) -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_COINGLASS_KEY"]) -def display_funding_rate( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> None: - """Plots funding rate by exchange for a certain cryptocurrency - [Source: https://coinglass.github.io/API-Reference/] - - Parameters - ---------- - symbol : str - Crypto symbol to search funding rate (e.g., BTC) - export : str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes : bool, optional - Whether to return the figure object or not, by default False - - """ - df = get_funding_rate(symbol) - if df.empty: - return None - - fig = plot_data( - df, symbol, f"Exchange {symbol} Funding Rate", "Funding Rate [%]", True - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fundrate", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_COINGLASS_KEY"]) -def display_open_interest( - symbol: str, - interval: int = 0, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> None: - """Plots open interest by exchange for a certain cryptocurrency - [Source: https://coinglass.github.io/API-Reference/] - - Parameters - ---------- - symbol : str - Crypto symbol to search open interest (e.g., BTC) - interval : int - Frequency (possible values are: 0 for ALL, 2 for 1H, 1 for 4H, 4 for 12H), by default 0 - export : str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = get_open_interest_per_exchange(symbol, interval) - if df.empty: - return None - - fig = plot_data( - df, - symbol, - f"Exchange {symbol} Futures Open Interest", - "Open futures value [$]", - True, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "oi", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_COINGLASS_KEY"]) -def display_liquidations( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots liquidation per day data for a certain cryptocurrency - [Source: https://coinglass.github.io/API-Reference/#liquidation-chart] - - Parameters - ---------- - symbol : str - Crypto symbol to search open interest (e.g., BTC) - export : str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = get_liquidations(symbol) - if df.empty: - return None - - fig = plot_data_bar( - df, - symbol, - f"Total liquidations for {symbol}", - "Liquidations value [$]", - True, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "liquidations", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def plot_data( - df: pd.DataFrame, - symbol: str = "", - title: str = "", - ylabel: str = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - fig = OpenBBFigure.create_subplots(2, 1) - - df_price = df[["price"]].copy() - df_without_price = df.drop("price", axis=1) - - groups = {"2": "two", "4": "three", "6": "four", "8": "five"} - legendgroup = "one" - for i, column in enumerate(df_without_price.columns): - if i > 0: - legendgroup = groups.get(str(i), legendgroup) - - fig.add_scatter( - x=df_without_price.index, - y=df_without_price[column], - name=column, - row=1, - col=1, - stackgroup="one", - legendgroup=legendgroup, - ) - - fig.add_scatter( - x=df_price.index, - y=df_price["price"], - name=f"{symbol} price", - showlegend=False, - line=dict(color=theme.get_colors()[0]), - row=2, - col=1, - ) - - if title: - fig.set_title(title) - if ylabel: - fig.set_yaxis_title(ylabel, 1, 1) - - if symbol: - fig.set_yaxis_title(f"{symbol} Price [$]", 2, 1) - - fig.update_layout(legend=dict(orientation="h")) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def plot_data_bar( - df: pd.DataFrame, - symbol: str = "", - title: str = "", - ylabel: str = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - fig = OpenBBFigure.create_subplots(2, 1) - - df_price = df[["price"]].copy() - df_without_price = df.drop("price", axis=1) - - df_without_price["Shorts"] = df_without_price["Shorts"] * -1 - - fig.add_bar( - x=df_without_price.index, - y=df_without_price["Shorts"], - name="Shorts", - row=1, - col=1, - marker_color=theme.down_color, - ) - fig.add_bar( - x=df_without_price.index, - y=df_without_price["Longs"], - name="Longs", - row=1, - col=1, - marker_color=theme.up_color, - ) - - if title: - fig.set_title(title) - if ylabel: - fig.set_yaxis_title(ylabel, 1, 1) - - fig.add_scatter( - x=df_price.index, - y=df_price["price"], - name=f"{symbol} price", - showlegend=False, - line=dict(color=theme.get_colors()[0]), - row=2, - col=1, - ) - if symbol: - fig.set_yaxis_title(f"{symbol} Price [$]", 2, 1) - - fig.update_layout( - legend=dict(orientation="h"), - bargap=0, - bargroupgap=0, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/due_diligence/coinpaprika_model.py b/openbb_terminal/cryptocurrency/due_diligence/coinpaprika_model.py deleted file mode 100644 index 14bfcf217ad..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/coinpaprika_model.py +++ /dev/null @@ -1,491 +0,0 @@ -"""CoinPaprika model""" - -__docformat__ = "numpy" - -import logging -import textwrap -from datetime import datetime, timedelta -from typing import Optional, Tuple - -import pandas as pd -from dateutil import parser - -from openbb_terminal.cryptocurrency.coinpaprika_helpers import PaprikaSession -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -# pylint: disable=unsupported-assignment-operation - -logger = logging.getLogger(__name__) -# pylint: disable=unsupported-assignment-operation - - -@log_start_end(log=logger) -def get_coin_twitter_timeline( - symbol: str = "BTC", sortby: str = "date", ascend: bool = True -) -> pd.DataFrame: - """Get twitter timeline for given coin id. Not more than last 50 tweets [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - sortby: str - Key by which to sort data. Every column name is valid - (see for possible values: - https://api.coinpaprika.com/docs#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1twitter/get). - ascend: bool - Flag to sort data descending - - Returns - ------- - pd.DataFrame - Twitter timeline for given coin. - Columns: date, user_name, status, retweet_count, like_count - """ - # get coinpaprika id using crypto symbol - cp_id = get_coinpaprika_id(symbol) - - session = PaprikaSession() - res = session.make_request(session.ENDPOINTS["coin_tweeter"].format(cp_id)) - if "error" in res: - console.print(res) - return pd.DataFrame() - if isinstance(res, list) and len(res) == 0: - return pd.DataFrame() - df = pd.DataFrame(res)[ - ["date", "user_name", "status", "retweet_count", "like_count"] - ] - - df = df.applymap( - lambda x: "\n".join(textwrap.wrap(x, width=80)) if isinstance(x, str) else x - ) - df["status"] = df["status"].apply(lambda x: x.replace(" ", "")) - df["date"] = pd.to_datetime(df["date"], format="%Y-%m-%dT%H:%M:%SZ").dt.strftime( - "%Y-%m-%d %H:%M:%S" - ) - df = df.sort_values(by=sortby, ascending=ascend) - # Remove unicode chars (it breaks pretty tables) - df["status"] = df["status"].apply( - lambda text: "".join(i if ord(i) < 128 else "" for i in text) - ) - return df - - -@log_start_end(log=logger) -def get_coin_events_by_id( - symbol: str = "BTC", sortby: str = "date", ascend: bool = False -) -> pd.DataFrame: - """Get all events related to given coin like conferences, start date of futures trading etc. - [Source: CoinPaprika] - - Example of response from API: - - .. code-block:: json - - { - "id": "17398-cme-april-first-trade", - "date": "2018-04-02T00:00:00Z", - "date_to": "string", - "name": "CME: April First Trade", - "description": "First trade of Bitcoin futures contract for April 2018.", - "is_conference": false, - "link": "http://www.cmegroup.com/trading/equity-index/us-index/bitcoin_product_calendar_futures.html", - "proof_image_link": "https://static.coinpaprika.com/storage/cdn/event_images/16635.jpg" - } - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - sortby: str - Key by which to sort data. Every column name is valid - (see for possible values: - https://api.coinpaprika.com/docs#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1events/get). - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Events found for given coin - Columns: id, date , date_to, name, description, is_conference, link, proof_image_link - """ - # get coinpaprika id using crypto symbol - cp_id = get_coinpaprika_id(symbol) - - session = PaprikaSession() - res = session.make_request(session.ENDPOINTS["coin_events"].format(cp_id)) - if not res or "error" in res: - return pd.DataFrame() - data = pd.DataFrame(res) - data["description"] = data["description"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=40)) if isinstance(x, str) else x - ) - data.drop(["id", "proof_image_link"], axis=1, inplace=True) - data["date"] = pd.to_datetime( - data["date"], format="%Y-%m-%dT%H:%M:%SZ" - ).dt.strftime("%Y-%m-%d %H:%M:%S") - data["date_to"] = pd.to_datetime( - data["date_to"], format="%Y-%m-%dT%H:%M:%SZ" - ).dt.strftime("%Y-%m-%d %H:%M:%S") - data = data.sort_values(by=sortby, ascending=ascend) - - return data - - -@log_start_end(log=logger) -def get_coin_exchanges_by_id( - symbol: str = "BTC", - sortby: str = "adjusted_volume_24h_share", - ascend: bool = True, -) -> pd.DataFrame: - """Get all exchanges for given coin id. [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - sortby: str - Key by which to sort data. Every column name is valid (see for possible values: - https://api.coinpaprika.com/v1). - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - All exchanges for given coin - Columns: id, name, adjusted_volume_24h_share, fiats - """ - # get coinpaprika id using crypto symbol - cp_id = get_coinpaprika_id(symbol) - - session = PaprikaSession() - res = session.make_request(session.ENDPOINTS["coin_exchanges"].format(cp_id)) - df = pd.DataFrame(res) - - if "fiats" in df.columns.tolist(): - df["fiats"] = ( - df["fiats"].copy().apply(lambda x: len([i["symbol"] for i in x if x])) - ) - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_coin_markets_by_id( - symbol: str = "BTC", - quotes: str = "USD", - sortby: str = "pct_volume_share", - ascend: bool = True, -) -> pd.DataFrame: - """All markets for given coin and currency [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - quotes: str - Comma separated list of quotes to return. - Example: quotes=USD,BTC - Allowed values: - BTC, ETH, USD, EUR, PLN, KRW, GBP, CAD, JPY, RUB, TRY, NZD, AUD, CHF, UAH, HKD, SGD, NGN, - PHP, MXN, BRL, THB, CLP, CNY, CZK, DKK, HUF, IDR, ILS, INR, MYR, NOK, PKR, SEK, TWD, ZAR, - VND, BOB, COP, PEN, ARS, ISK - sortby: str - Key by which to sort data. Every column name is valid (see for possible values: - https://api.coinpaprika.com/v1). - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - All markets for given coin and currency - """ - if sortby in ["volume", "price"]: - sortby = f"{str(symbol).lower()}_{sortby}" - - # get coinpaprika id using crypto symbol - cp_id = get_coinpaprika_id(symbol) - - session = PaprikaSession() - markets = session.make_request( - session.ENDPOINTS["coin_markets"].format(cp_id), quotes=quotes - ) - if "error" in markets: - console.print(markets) - return pd.DataFrame() - - data = [] - for r in markets: - dct = { - "exchange": r.get("exchange_name"), - "pair": r.get("pair"), - "trust_score": r.get("trust_score"), - "pct_volume_share": r.get("adjusted_volume_24h_share"), - } - _quotes: dict = r.get("quotes") - for k, v in _quotes.items(): - dct[f"{k.lower()}_price"] = v.get("price") - dct[f"{k.lower()}_volume"] = v.get("volume_24h") - dct["market_url"] = r.get("market_url") - data.append(dct) - - df = pd.DataFrame(data) - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_ohlc_historical( - symbol: str = "eth-ethereum", quotes: str = "USD", days: int = 90 -) -> pd.DataFrame: - """ - Open/High/Low/Close values with volume and market_cap. [Source: CoinPaprika] - Request example: https://api.coinpaprika.com/v1/coins/btc-bitcoin/ohlcv/historical?start=2019-01-01&end=2019-01-20 - if the last day is current day it can an change with every request until actual close of the day at 23:59:59 - - - Parameters - ---------- - symbol: str - Paprika coin identifier e.g. eth-ethereum - quotes: str - returned data quote (available values: usd btc) - days: int - time range for chart in days. Maximum 365 - - Returns - ------- - pd.DataFrame - Open/High/Low/Close values with volume and market_cap. - """ - - if quotes.lower() not in ["usd", "btc"]: - quotes = "USD" - - if abs(int(days)) > 365: - days = 365 - - end = datetime.now().strftime("%Y-%m-%d") - start = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d") - - session = PaprikaSession() - data = session.make_request( - session.ENDPOINTS["ohlcv_hist"].format(symbol), - quotes=quotes, - start=start, - end=end, - ) - if "error" in data: - # console.print( - # "Could not load data. Try use symbol (e.g., btc) instead of coin name (e.g., bitcoin)" - # ) - return pd.DataFrame() - return pd.DataFrame(data) - - -@log_start_end(log=logger) -def get_tickers_info_for_coin(symbol: str = "BTC", quotes: str = "USD") -> pd.DataFrame: - """Get all most important ticker related information for given coin id [Source: CoinPaprika] - - .. code-block:: json - - { - "id": "btc-bitcoin", - "name": "Bitcoin", - "symbol": "BTC", - "rank": 1, - "circulating_supply": 17007062, - "total_supply": 17007062, - "max_supply": 21000000, - "beta_value": 0.735327, - "first_data_at": "2010-11-14T07:20:41Z", - "last_updated": "2018-11-14T07:20:41Z", - "quotes": { - "USD": { - "price": 5162.15941296, - "volume_24h": 7304207651.1585, - "volume_24h_change_24h": -2.5, - "market_cap": 91094433242, - "market_cap_change_24h": 1.6, - "percent_change_15m": 0, - "percent_change_30m": 0, - "percent_change_1h": 0, - "percent_change_6h": 0, - "percent_change_12h": -0.09, - "percent_change_24h": 1.59, - "percent_change_7d": 0.28, - "percent_change_30d": 27.39, - "percent_change_1y": -37.99, - "ath_price": 20089, - "ath_date": "2017-12-17T12:19:00Z", - "percent_from_price_ath": -74.3 - } - } - } - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - quotes: str - Comma separated quotes to return e.g quotes = USD, BTC - - Returns - ------- - pd.DataFrame - Most important ticker related information - Columns: Metric, Value - """ - # get coinpaprika id using crypto symbol - cp_id = get_coinpaprika_id(symbol) - - session = PaprikaSession() - tickers = session.make_request( - session.ENDPOINTS["ticker_info"].format(cp_id), quotes=quotes - ) - - for key, date in tickers.items(): - if "date" in key or "data" in key: - try: - tickers[key] = parser.parse(date).strftime("%Y-%m-%d %H:%M:%S") - except (KeyError, ValueError, TypeError) as e: - logger.exception(str(e)) - console.print(e) - if key == "quotes": - try: - tickers[key][quotes]["ath_date"] = parser.parse( - tickers[key][quotes]["ath_date"] - ).strftime("%Y-%m-%d %H:%M:%S") - except (KeyError, ValueError, TypeError) as e: - logger.exception(str(e)) - console.print(e) - - df = pd.json_normalize(tickers) - try: - df.columns = [col.replace("quotes.", "") for col in list(df.columns)] - df.columns = [col.replace(".", "_").lower() for col in list(df.columns)] - except KeyError as e: - logger.exception(str(e)) - console.print(e) - df = df.T.reset_index() - df.columns = ["Metric", "Value"] - return df - - -@log_start_end(log=logger) -def basic_coin_info(symbol: str = "BTC") -> pd.DataFrame: - """Basic coin information [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - - Returns - ------- - pd.DataFrame - Metric, Value - """ - # get coinpaprika id using crypto symbol - cp_id = get_coinpaprika_id(symbol) - - coin = get_coin(cp_id) - tags = coin.get("tags") or [] - keys = [ - "id", - "name", - "symbol", - "rank", - "type", - "description", - "platform", - "proof_type", - "contract", - ] - results = {key: coin.get(key) for key in keys} - try: - tags = ", ".join(t.get("name") for t in tags) - parent = coin.get("parent") or {} - except (KeyError, IndexError): - tags, parent = [], {} - - results["tags"] = tags - results["parent"] = parent.get("id") - df = pd.Series(results).reset_index() - df.columns = ["Metric", "Value"] - df["Value"] = df["Value"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=80)) if isinstance(x, str) else x - ) - df.dropna(subset=["Value"], inplace=True) - return df - - -@log_start_end(log=logger) -def get_coin(symbol: str = "eth-ethereum") -> dict: - """Get coin by id [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - id of coin from coinpaprika e.g. Ethereum - > 'eth-ethereum' - Returns - ------- - dict - Coin response - """ - - session = PaprikaSession() - coin = session.make_request(session.ENDPOINTS["coin"].format(symbol)) - return coin - - -def get_coinpaprika_id(symbol: str) -> Optional[str]: - paprika_coins = get_coin_list() - paprika_coins_dict = dict(zip(paprika_coins.id, paprika_coins.symbol)) - coinpaprika_id, _ = validate_coin(symbol.upper(), paprika_coins_dict) - return coinpaprika_id - - -def get_coin_list() -> pd.DataFrame: - """Get list of all available coins on CoinPaprika [Source: CoinPaprika] - - Returns - ------- - pandas.DataFrame - Available coins on CoinPaprika - rank, id, name, symbol, type - """ - - session = PaprikaSession() - coins = session.make_request(session.ENDPOINTS["coins"]) - df = pd.DataFrame(coins) - df = df[df["is_active"]] - return df[["rank", "id", "name", "symbol", "type"]] - - -def validate_coin(symbol: str, coins_dct: dict) -> Tuple[Optional[str], Optional[str]]: - """ - Helper method that validates if proper coin id or symbol was provided - [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - id or symbol of coin for CoinPaprika - coins_dct: dict - dictionary of coins - - Returns - ------- - Tuple[Optional[str], Optional[str]] - coin id, coin symbol - """ - - for key, value in coins_dct.items(): - if symbol == value: - return key, value.lower() - return None, None diff --git a/openbb_terminal/cryptocurrency/due_diligence/coinpaprika_view.py b/openbb_terminal/cryptocurrency/due_diligence/coinpaprika_view.py deleted file mode 100644 index 0a5baf16854..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/coinpaprika_view.py +++ /dev/null @@ -1,392 +0,0 @@ -"""CoinPaprika view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_long_number_format_with_type_check, -) -from openbb_terminal.cryptocurrency.due_diligence import coinpaprika_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -# pylint: disable=inconsistent-return-statements -# pylint: disable=C0302, too-many-lines - -TWEETS_FILTERS = ["date", "user_name", "status", "retweet_count", "like_count"] - -EVENTS_FILTERS = ["date", "date_to", "name", "description", "is_conference"] - -EX_FILTERS = ["id", "name", "adjusted_volume_24h_share", "fiats"] - -MARKET_FILTERS = [ - "pct_volume_share", - "exchange", - "pair", - "trust_score", - "volume", - "price", -] - -CURRENCIES = [ - "BTC", - "ETH", - "USD", - "EUR", - "PLN", - "KRW", - "GBP", - "CAD", - "JPY", - "RUB", - "TRY", - "NZD", - "AUD", - "CHF", - "UAH", - "HKD", - "SGD", - "NGN", - "PHP", - "MXN", - "BRL", - "THB", - "CLP", - "CNY", - "CZK", - "DKK", - "HUF", - "IDR", - "ILS", - "INR", - "MYR", - "NOK", - "PKR", - "SEK", - "TWD", - "ZAR", - "VND", - "BOB", - "COP", - "PEN", - "ARS", - "ISK", -] - - -@log_start_end(log=logger) -def display_twitter( - symbol: str = "BTC", - limit: int = 10, - sortby: str = "date", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing twitter timeline for given coin id. Not more than last 50 tweets [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - limit: int - Number of records to display - sortby: str - Key by which to sort data. Every column name is valid - (see for possible values: - https://api.coinpaprika.com/docs#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1twitter/get). - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - """ - df = coinpaprika_model.get_coin_twitter_timeline(symbol, sortby, ascend) - - if df.empty: - console.print(f"Couldn't find any tweets for coin {symbol}", "\n") - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Twitter Timeline", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "twitter", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_events( - symbol: str = "BTC", - limit: int = 10, - sortby: str = "date", - ascend: bool = False, - links: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing all events for given coin id. [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - limit: int - Number of records to display - sortby: str - Key by which to sort data. Every column name is valid - (see for possible values: - https://api.coinpaprika.com/docs#tag/Coins/paths/~1coins~1%7Bcoin_id%7D~1events/get). - ascend: bool - Flag to sort data ascending - links: bool - Flag to display urls - export : str - Export dataframe data to csv,json,xlsx file - """ - df = coinpaprika_model.get_coin_events_by_id(symbol, sortby, ascend) - - if df.empty: - console.print(f"Couldn't find any events for coin {symbol}\n") - return - - df_data = df.copy() - - if links is True: - df = df[["date", "name", "link"]] - else: - df.drop("link", axis=1, inplace=True) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="All Events", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "events", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_exchanges( - symbol: str = "btc", - limit: int = 10, - sortby: str = "adjusted_volume_24h_share", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing all exchanges for given coin id. [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - limit: int - Number of records to display - sortby: str - Key by which to sort data. Every column name is valid (see for possible values: - https://api.coinpaprika.com/v1). - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - """ - df = coinpaprika_model.get_coin_exchanges_by_id(symbol, sortby, ascend) - - if df.empty: - console.print("No data found", "\n") - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="All Exchanges", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ex", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_markets( - from_symbol: str = "BTC", - to_symbol: str = "USD", - limit: int = 20, - sortby: str = "pct_volume_share", - ascend: bool = True, - links: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing all markets for given coin id. [Source: CoinPaprika] - - Parameters - ---------- - from_symbol: str - Cryptocurrency symbol (e.g. BTC) - to_symbol: str - Quoted currency - limit: int - Number of records to display - sortby: str - Key by which to sort data. Every column name is valid (see for possible values: - https://api.coinpaprika.com/v1). - ascend: bool - Flag to sort data ascending - links: bool - Flag to display urls - export : str - Export dataframe data to csv,json,xlsx file - """ - df = coinpaprika_model.get_coin_markets_by_id( - from_symbol, to_symbol, sortby, ascend - ) - - if df.empty: - console.print("There is no data \n") - return - - df_data = df.copy() - - if links is True: - df = df[["exchange", "pair", "trust_score", "market_url"]] - else: - df.drop("market_url", axis=1, inplace=True) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="All Markets", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mkt", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_price_supply( - from_symbol: str = "BTC", - to_symbol: str = "USD", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing ticker information for single coin [Source: CoinPaprika] - - Parameters - ---------- - from_symbol: str - Cryptocurrency symbol (e.g. BTC) - to_symbol: str - Quoted currency - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx - - """ - df = coinpaprika_model.get_tickers_info_for_coin(from_symbol, to_symbol) - - if df.empty: - console.print("No data found", "\n") - return - - df = df.applymap(lambda x: lambda_long_number_format_with_type_check(x)) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Coin Information", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ps", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_basic( - symbol: str = "BTC", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing basic information for coin. Like: - name, symbol, rank, type, description, platform, proof_type, contract, tags, parent. - [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Cryptocurrency symbol (e.g. BTC) - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx - """ - df = coinpaprika_model.basic_coin_info(symbol) - - if df.empty: - console.print("No data available\n") - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Basic Coin Information", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "basic", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/cryptopanic_view.py b/openbb_terminal/cryptocurrency/due_diligence/cryptopanic_view.py deleted file mode 100644 index 3680bd5ac76..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/cryptopanic_view.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Cryptopanic view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.dataframe_helpers import prettify_column_names -from openbb_terminal.cryptocurrency.overview import cryptopanic_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -@check_api_key(["API_CRYPTO_PANIC_KEY"]) -def display_news( - post_kind: str = "news", - region: str = "en", - filter_: Optional[str] = None, - source: Optional[str] = None, - symbol: Optional[str] = None, - limit: int = 25, - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing recent posts from CryptoPanic news aggregator platform. - [Source: https://cryptopanic.com/] - - Parameters - ---------- - limit: int - number of news to display - post_kind: str - Filter by category of news. Available values: news or media. - filter_: Optional[str] - Filter by kind of news. One from list: rising|hot|bullish|bearish|important|saved|lol - region: str - Filter news by regions. Available regions are: en (English), de (Deutsch), nl (Dutch), - es (Español), fr (Français), it (Italiano), pt (Português), ru (Русский) - ascend: bool - Sort in ascending order. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = cryptopanic_model.get_news( - limit=limit, - post_kind=post_kind, - filter_=filter_, - region=region, - symbol=symbol, - source=source, - ascend=ascend, - ) - - if not df.empty: - df.drop(["negative_votes", "positive_votes", "domain"], axis=1, inplace=True) - - df.columns = prettify_column_names(df.columns) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Most Recent News", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "news", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py b/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py deleted file mode 100644 index 133acc467d3..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py +++ /dev/null @@ -1,1744 +0,0 @@ -"""Cryptocurrency Due diligence Controller""" - -__docformat__ = "numpy" - -# pylint: disable=R0904, C0302, W0622, C0201, consider-using-dict-items -import argparse -import logging -from datetime import datetime, timedelta -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency import cryptocurrency_helpers -from openbb_terminal.cryptocurrency.due_diligence import ( - binance_model, - binance_view, - ccxt_model, - ccxt_view, - coinbase_model, - coinbase_view, - coinpaprika_view, - cryptopanic_view, - glassnode_model, - glassnode_view, - messari_model, - messari_view, - pycoingecko_view, - santiment_view, -) -from openbb_terminal.cryptocurrency.overview import cryptopanic_model -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import CryptoBaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - -FILTERS_VS_USD_BTC = ["usd", "btc"] - - -def check_cg_id(symbol: str): - cg_id = cryptocurrency_helpers.get_coingecko_id(symbol) - if not cg_id: - console.print(f"\n{symbol} not found on CoinGecko") - return "" - return symbol - - -class DueDiligenceController(CryptoBaseController): - """Due Diligence Controller class""" - - CHOICES_COMMANDS = ["load", "active", "change", "nonzero", "eb"] - - SPECIFIC_CHOICES = { - "cp": [ - "events", - "twitter", - "ex", - "mkt", - "ps", - "basic", - ], - "cg": [ - "info", - "market", - "ath", - "atl", - "score", - "web", - "social", - "bc", - "dev", - ], - "bin": [ - "balance", - ], - "ccxt": ["ob", "trades"], - "cb": ["stats"], - "mes": ["mcapdom", "links", "rm", "tk", "pi", "mt", "team", "gov", "fr", "inv"], - "san": ["gh"], - "cpanic": ["news"], - } - - DD_VIEWS_MAPPING = { - "cg": pycoingecko_view, - "cp": coinpaprika_view, - "bin": binance_view, - "mes": messari_view, - "san": santiment_view, - "cpanic": cryptopanic_view, - } - - PATH = "/crypto/dd/" - CHOICES_GENERATION = True - - def __init__( - self, - symbol=None, - source=None, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - for _, value in self.SPECIFIC_CHOICES.items(): - self.controller_choices.extend(value) - - self.source = source - self.symbol = symbol - self.messari_timeseries = [] - df_mt = messari_model.get_available_timeseries() - self.ccxt_exchanges = ccxt_model.get_exchanges() - self.binance_currencies = ccxt_model.get_binance_currencies() - self.coinbase_currencies = {"USD", "USDC", "GBP", "USDT", "EUR"} - - if not df_mt.empty: - self.messari_timeseries = df_mt.index.to_list() - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - choices["ob"].update({c: {} for c in self.ccxt_exchanges}) - choices["trades"].update({c: {} for c in self.ccxt_exchanges}) - choices["change"].update( - {c: {} for c in glassnode_model.GLASSNODE_SUPPORTED_EXCHANGES} - ) - choices["eb"].update( - {c: {} for c in glassnode_model.GLASSNODE_SUPPORTED_EXCHANGES} - ) - choices["mt"].update({c: None for c in self.messari_timeseries}) - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("crypto/dd/", 120) - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_symbol", self.symbol) - mt.add_param("_source", self.source) - mt.add_raw("\n") - - mt.add_info("_overview_") - mt.add_cmd("info") - mt.add_cmd("ath") - mt.add_cmd("atl") - mt.add_cmd("web") - mt.add_cmd("bc") - mt.add_cmd("pi") - mt.add_cmd("gov") - mt.add_cmd("basic") - mt.add_cmd("stats") - mt.add_cmd("desc") - - mt.add_info("_market_") - mt.add_cmd("market") - mt.add_cmd("mkt") - mt.add_cmd("ex") - mt.add_cmd("balance") - mt.add_cmd("eb") - mt.add_cmd("trades") - mt.add_cmd("ob") - - mt.add_info("_metrics_") - mt.add_cmd("active") - mt.add_cmd("nonzero") - mt.add_cmd("change") - mt.add_cmd("ps") - mt.add_cmd("mcapdom") - mt.add_cmd("mt") - - mt.add_info("_contributors_") - mt.add_cmd("team") - mt.add_cmd("inv") - - mt.add_info("_tokenomics_") - mt.add_cmd("tk") - mt.add_cmd("fr") - - mt.add_info("_roadmap_") - mt.add_cmd("rm") - mt.add_cmd("events") - mt.add_cmd("news") - - mt.add_info("_activity_") - mt.add_cmd("links") - mt.add_cmd("twitter") - mt.add_cmd("social") - mt.add_cmd("score") - mt.add_cmd("dev") - mt.add_cmd("gh") - console.print(text=mt.menu_text, menu="Crypto - Due Diligence") - - def custom_reset(self): - """Class specific component of reset command""" - if self.symbol: - return ["crypto", f"load {self.symbol}", "dd"] - return [] - - @log_start_end(log=logger) - def call_nonzero(self, other_args: List[str]): - """Process nonzero command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="nonzero", - description=""" - Display addresses with nonzero assets in a certain blockchain - [Source: https://glassnode.org] - Note that free api keys only allow fetching data with a 1y lag - """, - ) - - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help="Initial date. Default: 2 years ago", - default=(datetime.now() - timedelta(days=365 * 2)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help="Final date. Default: 1 year ago", - default=(datetime.now() - timedelta(days=367)).strftime("%Y-%m-%d"), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - if self.symbol.upper() in glassnode_model.GLASSNODE_SUPPORTED_ASSETS: - glassnode_view.display_non_zero_addresses( - symbol=self.symbol.upper(), - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - else: - console.print(f"[red]{self.symbol} not supported on Glassnode.[/red]") - - @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, ns_parser.sheet_name) - - @log_start_end(log=logger) - def call_active(self, other_args: List[str]): - """Process active command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="active", - description=""" - Display active blockchain addresses over time - [Source: https://glassnode.org] - """, - ) - - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=str, - help="Frequency interval. Default: 24h", - default="24h", - choices=glassnode_model.INTERVALS_ACTIVE_ADDRESSES, - ) - - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help="Initial date. Default: 1 year ago", - default=(datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help="Final date. Default: Today", - default=(datetime.now()).strftime("%Y-%m-%d"), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - if self.symbol.upper() in glassnode_model.GLASSNODE_SUPPORTED_ASSETS: - glassnode_view.display_active_addresses( - symbol=self.symbol.upper(), - interval=ns_parser.interval, - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print(f"[red]{self.symbol} not supported on Glassnode.[/red]") - - @log_start_end(log=logger) - def call_change(self, other_args: List[str]): - """Process change command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="change", - description=""" - Display active blockchain addresses over time - [Source: https://glassnode.org] - Note that free api keys only allow fetching data with a 1y lag - """, - ) - - parser.add_argument( - "-e", - "--exchange", - dest="exchange", - type=str, - help="Exchange to check change. Default: aggregated", - default="aggregated", - choices=glassnode_model.GLASSNODE_SUPPORTED_EXCHANGES, - ) - - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help="Initial date. Default: 2 years ago", - default=(datetime.now() - timedelta(days=365 * 2)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help="Final date. Default: 1 year ago", - default=(datetime.now() - timedelta(days=367)).strftime("%Y-%m-%d"), - ) - - if other_args and 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: - if self.symbol.upper() in glassnode_model.GLASSNODE_SUPPORTED_ASSETS: - glassnode_view.display_exchange_net_position_change( - symbol=self.symbol.upper(), - exchange=ns_parser.exchange, - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print(f"[red]{self.symbol} not supported on Glassnode.[/red]") - - @log_start_end(log=logger) - def call_eb(self, other_args: List[str]): - """Process eb command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="eb", - description=""" - Display active blockchain addresses over time - [Source: https://glassnode.org] - Note that free api keys only allow fetching data with a 1y lag - """, - ) - - parser.add_argument( - "-p", - "--pct", - dest="percentage", - action="store_true", - help="Show percentage instead of stacked value. Default: False", - default=False, - ) - - parser.add_argument( - "-e", - "--exchange", - dest="exchange", - type=str, - help="Exchange to check change. Default: aggregated", - default="aggregated", - choices=glassnode_model.GLASSNODE_SUPPORTED_EXCHANGES, - ) - - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help="Initial date. Default: 2 years ago", - default=(datetime.now() - timedelta(days=365 * 2)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help="Final date. Default: 1 year ago", - default=(datetime.now() - timedelta(days=367)).strftime("%Y-%m-%d"), - ) - - if other_args and 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: - if self.symbol.upper() in glassnode_model.GLASSNODE_SUPPORTED_ASSETS: - glassnode_view.display_exchange_balances( - symbol=self.symbol.upper(), - exchange=ns_parser.exchange, - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - percentage=ns_parser.percentage, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print(f"[red]{self.symbol} not supported on Glassnode.[/red]") - - @log_start_end(log=logger) - def call_info(self, other_args): - """Process info command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="info", - description=""" - Shows basic information about loaded coin like: - Name, Symbol, Description, Market Cap, Public Interest, Supply, and Price related metrics - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser and self.symbol: - pycoingecko_view.display_info( - symbol=self.symbol, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_market(self, other_args): - """Process market command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="market", - description=""" - Market data for loaded coin. There you find metrics like: - Market Cap, Supply, Circulating Supply, Price, Volume and many others.""", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - cg_id = check_cg_id(self.symbol) - if cg_id: - pycoingecko_view.display_market( - cg_id, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_web(self, other_args): - """Process web command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="web", - description="""Websites found for given Coin. You can find there urls to - homepage, forum, announcement site and others.""", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser and self.symbol: - pycoingecko_view.display_web( - self.symbol, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_social(self, other_args): - """Process social command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="social", - description="""Shows social media corresponding to loaded coin. You can find there name of - telegram channel, urls to twitter, reddit, bitcointalk, facebook and discord.""", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cg_id = check_cg_id(self.symbol) - if cg_id: - pycoingecko_view.display_social( - cg_id, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dev(self, other_args): - """Process dev command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dev", - description=""" - Developers data for loaded coin. If the development data is available you can see - how the code development of given coin is going on. - There are some statistics that shows number of stars, forks, subscribers, pull requests, - commits, merges, contributors on github.""", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cg_id = check_cg_id(self.symbol) - if cg_id: - pycoingecko_view.display_dev( - cg_id, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ath(self, other_args): - """Process ath command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ath", - description="""All time high data for loaded coin""", - ) - - parser.add_argument( - "--vs", - dest="vs", - help="currency", - default="usd", - choices=FILTERS_VS_USD_BTC, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cg_id = check_cg_id(self.symbol) - if cg_id: - pycoingecko_view.display_ath( - cg_id, - ns_parser.vs, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_atl(self, other_args): - """Process atl command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="atl", - description="""All time low data for loaded coin""", - ) - - parser.add_argument( - "--vs", - dest="vs", - help="currency", - default="usd", - choices=FILTERS_VS_USD_BTC, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cg_id = check_cg_id(self.symbol) - if cg_id: - pycoingecko_view.display_atl( - cg_id, - ns_parser.vs, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_score(self, other_args): - """Process score command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="score", - description=""" - In this view you can find different kind of scores for loaded coin. - Those scores represents different rankings, sentiment metrics, some user stats and others. - You will see CoinGecko scores, Developer Scores, Community Scores, Sentiment, Reddit scores - and many others.""", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cg_id = check_cg_id(self.symbol) - if cg_id: - pycoingecko_view.display_score( - cg_id, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bc(self, other_args): - """Process bc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bc", - description=""" - Blockchain explorers URLs for loaded coin. Those are sites like etherescan.io or polkascan.io - in which you can see all blockchain data e.g. all txs, all tokens, all contracts... - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cg_id = check_cg_id(self.symbol) - if cg_id: - pycoingecko_view.display_bc( - cg_id, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ob(self, other_args): - """Process order book command""" - parser = argparse.ArgumentParser( - prog="ob", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Get the order book for selected 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", - default="usdt", - type=str.lower, - choices=self.binance_currencies, - metavar="VS", - ) - - if other_args and 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: - ccxt_view.display_order_book( - ns_parser.exchange, - symbol=self.symbol, - to_symbol=ns_parser.vs, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @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="Get the latest trades for selected 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", - default="usdt", - type=str.lower, - choices=self.binance_currencies, - metavar="VS", - ) - - if other_args and 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, limit=10 - ) - - if ns_parser: - ccxt_view.display_trades( - ns_parser.exchange, - symbol=self.symbol, - to_symbol=ns_parser.vs, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - limit=ns_parser.limit, - ) - - @log_start_end(log=logger) - def call_balance(self, other_args): - """Process balance command""" - coin = self.symbol.upper() - values = binance_model.show_available_pairs_for_given_symbol(coin) - quotes = values[1] if values else None - - parser = argparse.ArgumentParser( - prog="balance", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Display balance", - ) - - parser.add_argument( - "--vs", - help="Quote currency (what to view coin vs)", - dest="vs", - type=str, - default="USDT", - choices=quotes, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - binance_view.display_balance( - from_symbol=coin, - to_symbol=ns_parser.vs, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ps(self, other_args): - """Process ps command""" - parser = argparse.ArgumentParser( - prog="ps", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Get price and supply related metrics for given coin.""", - ) - - parser.add_argument( - "--vs", - help="Quoted currency. Default USD", - dest="vs", - default="USD", - type=str, - choices=coinpaprika_view.CURRENCIES, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser and self.symbol: - coinpaprika_view.display_price_supply( - self.symbol, - ns_parser.vs, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_basic(self, other_args): - """Process basic command""" - parser = argparse.ArgumentParser( - prog="basic", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Get basic information for coin. Like: - name, symbol, rank, type, description, platform, proof_type, - contract, tags, parent""", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser and self.symbol: - coinpaprika_view.display_basic( - self.symbol, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_mkt(self, other_args): - """Process mkt command""" - parser = argparse.ArgumentParser( - prog="mkt", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Get all markets found for given coin. - You can display only N number of markets with --limt parameter. - You can sort data by pct_volume_share, exchange, pair, trust_score, volume, price --sort parameter - and also with --reverse flag to sort ascending. - You can use additional flag --urls to see urls for each market - Displays: - exchange, pair, trust_score, volume, price, pct_volume_share,""", - ) - parser.add_argument( - "--vs", - help="Quoted currency. Default USD", - dest="vs", - default="USD", - type=str, - choices=coinpaprika_view.CURRENCIES, - ) - parser.add_argument( - "-l", - "--limit", - default=20, - dest="limit", - help="Limit of records", - type=check_positive, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: pct_volume_share", - default="pct_volume_share", - choices=coinpaprika_view.MARKET_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-u", - "--urls", - dest="urls", - action="store_true", - help="""Flag to show urls. If you will use that flag you will see only: - exchange, pair, trust_score, market_url columns""", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser and self.symbol: - coinpaprika_view.display_markets( - from_symbol=self.symbol, - to_symbol=ns_parser.vs, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - links=ns_parser.urls, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ex(self, other_args): - """Process ex command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ex", - description="""Get all exchanges found for given coin. - You can display only top N number of exchanges with --top parameter. - You can sort data by id, name, adjusted_volume_24h_share, fiats --sort parameter - and also with --reverse flag to sort ascending. - Displays: - id, name, adjusted_volume_24h_share, fiats""", - ) - parser.add_argument( - "-l", - "--limit", - default=10, - dest="limit", - help="Limit of records", - type=check_positive, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: date", - default="adjusted_volume_24h_share", - choices=coinpaprika_view.EX_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser and self.symbol: - coinpaprika_view.display_exchanges( - symbol=self.symbol, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_events(self, other_args): - """Process events command""" - parser = argparse.ArgumentParser( - prog="events", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Show information about most important coins events. Most of coins doesn't have any events. - You can display only top N number of events with --limit parameter. - You can sort data by id, date , date_to, name, description, is_conference --sort parameter - and also with --reverse flag to sort ascending. - You can use additional flag --urls to see urls for each event - Displays: - date , date_to, name, description, is_conference, link, proof_image_link""", - ) - parser.add_argument( - "-l", - "--limit", - default=10, - dest="limit", - help="Limit of records", - type=check_positive, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: date", - default="date", - choices=coinpaprika_view.EVENTS_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-u", - "--urls", - dest="urls", - action="store_true", - help="Flag to show urls. If you will use that flag you will see only date, name, link columns", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser and self.symbol: - coinpaprika_view.display_events( - symbol=self.symbol, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - links=ns_parser.urls, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_twitter(self, other_args): - """Process twitter command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="twitter", - description="""Show last 10 tweets for given coin. - You can display only N number of tweets with --limit parameter. - You can sort data by date, user_name, status, retweet_count, like_count --sort parameter - and also with --reverse flag to sort ascending. - Displays: - date, user_name, status, retweet_count, like_count - """, - ) - parser.add_argument( - "-l", - "--limit", - default=10, - dest="limit", - help="Limit of records", - type=check_positive, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: date", - default="date", - choices=coinpaprika_view.TWEETS_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser and self.symbol: - coinpaprika_view.display_twitter( - symbol=self.symbol, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_mcapdom(self, other_args: List[str]): - """Process mcapdom command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mcapdom", - description=""" - Display asset's percentage share of total crypto circulating market cap - [Source: https://messari.io] - """, - ) - - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=str, - help="Frequency interval. Default: 1d", - default="1d", - choices=messari_model.INTERVALS_TIMESERIES, - ) - - parser.add_argument( - "-s", - "--start", - dest="start", - type=valid_date, - help="Initial date. Default: A year ago", - default=(datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-end", - "--end", - dest="end", - type=valid_date, - help="End date. Default: Today", - default=datetime.now().strftime("%Y-%m-%d"), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - messari_view.display_marketcap_dominance( - symbol=self.symbol.upper(), - interval=ns_parser.interval, - start_date=ns_parser.start, - end_date=ns_parser.end, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_links(self, other_args: List[str]): - """Process links command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="links", - description=""" - Display asset's links - [Source: https://messari.io] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - messari_view.display_links( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_gh(self, other_args: List[str]): - """Process gh command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="gh", - description=""" - Display github activity over time for a given coin. - Github activity includes the following actions: creating a Pull Request, an Issue, - commenting on an issue / PR, and many more. - - See detailed definition at https://academy.santiment.net/metrics/development-activity/ - - [Source: https://santiment.net/] - """, - ) - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=str, - help="Frequency interval. Default: 1d", - default="1d", - ) - - parser.add_argument( - "-d", - "--dev", - dest="dev", - type=bool, - help="Filter only for development activity. Default: False", - default=False, - ) - - parser.add_argument( - "-s", - "--start", - dest="start", - type=valid_date, - help="Initial date. Default: A year ago", - default=(datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-end", - "--end", - dest="end", - type=valid_date, - help="End date. Default: Today", - default=datetime.now().strftime("%Y-%m-%d"), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - santiment_view.display_github_activity( - symbol=self.symbol.upper(), - interval=ns_parser.interval, - dev_activity=ns_parser.dev, - start_date=ns_parser.start.strftime("%Y-%m-%dT%H:%M:%SZ"), - end_date=ns_parser.end.strftime("%Y-%m-%dT%H:%M:%SZ"), - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_rm(self, other_args: List[str]): - """Process rm command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rm", - description=""" - Display asset's roadmap - [Source: https://messari.io] - """, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=5 - ) - - if ns_parser: - messari_view.display_roadmap( - ascend=ns_parser.reverse, - symbol=self.symbol.upper(), - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_tk(self, other_args: List[str]): - """Process tk command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tk", - description=""" - Display asset's tokenomics - [Source: https://messari.io] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser and self.symbol: - messari_view.display_tokenomics( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_pi(self, other_args: List[str]): - """Process pi command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="pi", - description=""" - Display asset's project info - [Source: https://messari.io] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - messari_view.display_project_info( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_team(self, other_args: List[str]): - """Process team command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="team", - description=""" - Display asset's team - [Source: https://messari.io] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - messari_view.display_team( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_inv(self, other_args: List[str]): - """Process inv command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="inv", - description=""" - Display asset's investors - [Source: https://messari.io] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - messari_view.display_investors( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fr(self, other_args: List[str]): - """Process fr command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fr", - description=""" - Display asset's fundraising details - [Source: https://messari.io] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - messari_view.display_fundraising( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_gov(self, other_args: List[str]): - """Process gov command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="gov", - description=""" - Display asset's governance - [Source: https://messari.io] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - messari_view.display_governance( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_mt(self, other_args: List[str]): - """Process mt command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mt", - description=""" - Display messari timeseries - [Source: https://messari.io] - """, - ) - - parser.add_argument( - "--list", - action="store_true", - help="Flag to show available timeseries", - dest="list", - default=False, - ) - - parser.add_argument( - "-t", - "--timeseries", - dest="timeseries", - type=str, - help="Messari timeseries id", - default="", - choices=self.messari_timeseries, - metavar="TIMESERIES", - ) - - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=str, - help="Frequency interval", - default="1d", - choices=messari_model.INTERVALS_TIMESERIES, - ) - parser.add_argument( - "-s", - "--start", - dest="start", - type=valid_date, - help="Initial date. Default: A year ago", - default=(datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-end", - "--end", - dest="end", - type=valid_date, - help="End date. Default: Today", - default=datetime.now().strftime("%Y-%m-%d"), - ) - parser.add_argument( - "--include-paid", - action="store_true", - help="Flag to show both paid and free sources", - dest="include_paid", - default=False, - ) - - parser.add_argument( - "-q", - "--query", - type=str, - dest="query", - nargs="+", - help="Query to search across all messari timeseries", - default="", - ) - - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, limit=10 - ) - - if ns_parser: - if ( - ns_parser.timeseries - and ns_parser.timeseries not in self.messari_timeseries - ): - console.print("\nTimeseries {ns_parser.timeseries} not found") - return - if ns_parser.list or ns_parser.query or ns_parser.timeseries == "": - messari_view.display_messari_timeseries_list( - ns_parser.limit, - " ".join(ns_parser.query), - not ns_parser.include_paid, - ns_parser.export, - ) - else: - messari_view.display_messari_timeseries( - timeseries_id=ns_parser.timeseries, - symbol=self.symbol.upper(), - interval=ns_parser.interval, - start_date=ns_parser.start, - end_date=ns_parser.end, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_news(self, other_args): - """Process news command""" - parser = argparse.ArgumentParser( - prog="news", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Display most recent news on the given coin from CryptoPanic aggregator platform. - [Source: https://cryptopanic.com/]""", - ) - - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - - parser.add_argument( - "-k", - "--kind", - dest="kind", - type=str, - help="Filter by category of news. Available values: news or media.", - default="news", - choices=cryptopanic_model.CATEGORIES, - ) - - parser.add_argument( - "--filter", - dest="filter", - type=str, - help="Filter by kind of news. From: rising|hot|bullish|bearish|important|saved|lol", - default=None, - required=False, - choices=cryptopanic_model.FILTERS, - ) - - parser.add_argument( - "-r", - "--region", - dest="region", - type=str, - help=( - "Filter news by regions. Available regions are: en (English), de (Deutsch), nl" - " (Dutch), es (Español), fr (Français), it (Italiano), pt (Português), ru " - "(Русский)" - ), - default="en", - choices=cryptopanic_model.REGIONS, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: published_at", - default="published_at", - choices=cryptopanic_model.SORT_FILTERS, - ) - parser.add_argument( - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-u", - "--urls", - dest="urls", - action="store_false", - help="Flag to disable urls. Hides column with URL.", - default=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cryptopanic_view.display_news( - limit=ns_parser.limit, - source=None, - symbol=self.symbol, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ascend=ns_parser.reverse, - post_kind=ns_parser.kind, - filter_=ns_parser.filter, - region=ns_parser.region, - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/due_diligence_api.py b/openbb_terminal/cryptocurrency/due_diligence/due_diligence_api.py deleted file mode 100644 index 41fce591730..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/due_diligence_api.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Due Diligence API.""" - -import os - -from openbb_terminal.helper_classes import ModelsNamespace as _models - -# flake8: noqa -# pylint: disable=unused-import - -from .binance_view import display_balance as balance, display_order_book as book -from .coinbase_view import ( - display_order_book as cbbook, - display_stats as stats, - display_trades as trades, -) -from .coinglass_view import display_open_interest as oi -from .coinpaprika_view import ( - display_basic as basic, - display_events as events, - display_exchanges as ex, - display_markets as mkt, - display_price_supply as ps, - display_twitter as twitter, -) -from .cryptopanic_view import display_news as news - -# Menu commands -from .glassnode_view import ( - display_active_addresses as active, - display_exchange_balances as eb, - display_exchange_net_position_change as change, - display_hashrate as hr, - display_non_zero_addresses as nonzero, -) -from .messari_view import ( - display_fundraising as fr, - display_governance as gov, - display_investors as inv, - display_links as links, - display_marketcap_dominance as mcapdom, - display_messari_timeseries as mt, - display_messari_timeseries_list as get_mt, - display_project_info as pi, - display_roadmap as rm, - display_team as team, - display_tokenomics as tk, -) -from .pycoingecko_view import ( - display_ath as ath, - display_atl as atl, - display_bc as bc, - display_dev as dev, - display_info as info, - display_market as market, - display_score as score, - display_social as social, - display_web as web, -) -from .santiment_view import display_github_activity as gh -from .tokenterminal_view import ( - display_description as desc, - display_fundamental_metric_from_project_over_time as funot, -) - -# Models -models = _models(os.path.abspath(os.path.dirname(__file__))) diff --git a/openbb_terminal/cryptocurrency/due_diligence/finbrain_crypto_view.py b/openbb_terminal/cryptocurrency/due_diligence/finbrain_crypto_view.py deleted file mode 100644 index b7f0f8b27dc..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/finbrain_crypto_view.py +++ /dev/null @@ -1,139 +0,0 @@ -"""Finbrain Crypto Sentiment Analysis""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure, rich_config, theme -from openbb_terminal.common.behavioural_analysis.finbrain_model import get_sentiment -from openbb_terminal.common.behavioural_analysis.finbrain_view import ( - lambda_sentiment_coloring, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -try: - COINS_JSON = pd.read_json(PATH + "/data/finbrain_coins.json") - COINS = COINS_JSON["SYMBOL"].tolist() -except ValueError: - COINS = None - - -@log_start_end(log=logger) -def display_crypto_sentiment_analysis( - symbol: str, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Sentiment analysis from FinBrain for Cryptocurrencies - - FinBrain collects the news headlines from 15+ major financial news - sources on a daily basis and analyzes them to generate sentiment scores - for more than 4500 US stocks. FinBrain Technologies develops deep learning - algorithms for financial analysis and prediction, which currently serves - traders from more than 150 countries all around the world. - [Source: https://finbrain.tech] - - Parameters - ---------- - symbol: str - Cryptocurrency - raw : False - Display raw table data - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure() - - sentiment = get_sentiment(f"{symbol}-USD") # Currently only USD pairs are available - - if sentiment.empty: - return console.print(f"Couldn't find Sentiment Data for {symbol}\n") - - if not fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "finbrain", - sentiment, - sheet_name, - ) - - if raw and not fig.is_image_export(export): - sentiment.sort_index(ascending=True, inplace=True) - - if rich_config.USE_COLOR: - return console.print( - sentiment["Sentiment Analysis"] - .apply(lambda_sentiment_coloring, last_val=0) - .to_string(), - "\n", - ) - - return console.print(sentiment.to_string(), "\n") - - start_date = sentiment.index[0].strftime("%Y/%m/%d") - - fig = OpenBBFigure(xaxis_title="Time", yaxis_title="Finbrain's Sentiment Score") - fig.set_title(f"FinBrain's Sentiment Analysis for {symbol}-USD since {start_date}") - - senValues = np.array(pd.to_numeric(sentiment["Sentiment Analysis"].values)) - senNone = np.array(0 * len(sentiment)) - df_sentiment = sentiment["Sentiment Analysis"] - negative_yloc = np.where(senValues < senNone)[0] - positive_yloc = np.where(senValues > senNone)[0] - - fig.add_scatter( - x=df_sentiment.index[positive_yloc], - y=pd.to_numeric(df_sentiment.values)[positive_yloc], - marker=dict(color=theme.up_color, size=10), - mode="lines+markers", - line_width=0, - name=symbol, - ) - fig.add_scatter( - x=[df_sentiment.index[0], df_sentiment.index[-1]], - y=[0, 0], - fillcolor=theme.up_color, - line=dict(color="white", dash="dash"), - fill="tonexty", - mode="lines", - name=symbol, - ) - fig.add_scatter( - x=df_sentiment.index[negative_yloc], - y=pd.to_numeric(df_sentiment.values)[negative_yloc], - fill="tonexty", - fillcolor=theme.down_color, - marker=dict(color=theme.down_color, size=10), - line_width=0, - mode="lines+markers", - name=symbol, - ) - fig.update_traces(showlegend=False) - - if fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "finbrain", - sentiment, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/cryptocurrency/due_diligence/glassnode_model.py b/openbb_terminal/cryptocurrency/due_diligence/glassnode_model.py deleted file mode 100644 index 856fdba49a0..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/glassnode_model.py +++ /dev/null @@ -1,612 +0,0 @@ -import json -import logging -from datetime import datetime, timedelta -from typing import Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request, str_date_to_timestamp -from openbb_terminal.rich_config import console - -# pylint: disable=unsupported-assignment-operation - -logger = logging.getLogger(__name__) -# pylint: disable=unsupported-assignment-operation - -api_url = "https://api.glassnode.com/v1/metrics/" - -GLASSNODE_SUPPORTED_HASHRATE_ASSETS = ["BTC", "ETH"] - -GLASSNODE_SUPPORTED_EXCHANGES = [ - "aggregated", - "binance", - "bittrex", - "coinex", - "gate.io", - "gemini", - "huobi", - "kucoin", - "poloniex", - "bibox", - "bigone", - "bitfinex", - "hitbtc", - "kraken", - "okex", - "bithumb", - "zb.com", - "cobinhood", - "bitmex", - "bitstamp", - "coinbase", - "coincheck", - "luno", -] - -GLASSNODE_SUPPORTED_ASSETS = [ - "BTC", - "ETH", - "LTC", - "AAVE", - "ABT", - "AMPL", - "ANT", - "ARMOR", - "BADGER", - "BAL", - "BAND", - "BAT", - "BIX", - "BNT", - "BOND", - "BRD", - "BUSD", - "BZRX", - "CELR", - "CHSB", - "CND", - "COMP", - "CREAM", - "CRO", - "CRV", - "CVC", - "CVP", - "DAI", - "DDX", - "DENT", - "DGX", - "DHT", - "DMG", - "DODO", - "DOUGH", - "DRGN", - "ELF", - "ENG", - "ENJ", - "EURS", - "FET", - "FTT", - "FUN", - "GNO", - "GUSD", - "HEGIC", - "HOT", - "HPT", - "HT", - "HUSD", - "INDEX", - "KCS", - "LAMB", - "LBA", - "LDO", - "LEO", - "LINK", - "LOOM", - "LRC", - "MANA", - "MATIC", - "MCB", - "MCO", - "MFT", - "MIR", - "MKR", - "MLN", - "MTA", - "MTL", - "MX", - "NDX", - "NEXO", - "NFTX", - "NMR", - "Nsure", - "OCEAN", - "OKB", - "OMG", - "PAX", - "PAY", - "PERP", - "PICKLE", - "PNK", - "PNT", - "POLY", - "POWR", - "PPT", - "QASH", - "QKC", - "QNT", - "RDN", - "REN", - "REP", - "RLC", - "ROOK", - "RPL", - "RSR", - "SAI", - "SAN", - "SNT", - "SNX", - "STAKE", - "STORJ", - "sUSD", - "SUSHI", - "TEL", - "TOP", - "UBT", - "UMA", - "UNI", - "USDC", - "USDK", - "USDT", - "UTK", - "VERI", - "WaBi", - "WAX", - "WBTC", - "WETH", - "wNMX", - "WTC", - "YAM", - "YFI", - "ZRX", -] - -INTERVALS_HASHRATE = ["24h", "1w", "1month"] -INTERVALS_ACTIVE_ADDRESSES = ["24h", "1w", "1month"] - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def get_close_price( - symbol: str, - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - print_errors: bool = True, -) -> pd.DataFrame: - """Returns the price of a cryptocurrency - [Source: https://glassnode.com] - - Parameters - ---------- - symbol : str - Crypto to check close price (BTC or ETH) - start_date : str - Initial date, format YYYY-MM-DD - end_date : str - Final date, format YYYY-MM-DD - print_errors: bool - Flag to print errors. Default: True - - Returns - ------- - pd.DataFrame - price over time - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - url = api_url + "market/price_usd_close" - - parameters = { - "api_key": get_current_user().credentials.API_GLASSNODE_KEY, - "a": symbol, - "i": "24h", - "s": str(ts_start_date), - "u": str(ts_end_date), - } - - r = request(url, params=parameters) - - df = pd.DataFrame() - - if r.status_code == 200: - df = pd.DataFrame(json.loads(r.text)) - - if df.empty: - if print_errors: - console.print(f"No data found for {symbol} price.\n") - else: - df = df.set_index("t") - df.index = pd.to_datetime(df.index, unit="s") - - elif r.status_code == 401: - if print_errors: - console.print("[red]Invalid API Key[/red]\n") - elif print_errors: - console.print(r.text) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def get_non_zero_addresses( - symbol: str, - start_date: str = "2010-01-01", - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Returns addresses with non-zero balance of a certain symbol - [Source: https://glassnode.com] - - Parameters - ---------- - symbol : str - Asset to search (e.g., BTC) - start_date : str - Initial date, format YYYY-MM-DD - end_date : str - Final date, format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - addresses with non-zero balances - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - url = api_url + "addresses/non_zero_count" - - parameters = { - "api_key": get_current_user().credentials.API_GLASSNODE_KEY, - "a": symbol, - "i": "24h", - "s": str(ts_start_date), - "u": str(ts_end_date), - } - - r = request(url, params=parameters) - - df = pd.DataFrame() - - if r.status_code == 200: - df = pd.DataFrame(json.loads(r.text)) - - if df.empty: - console.print(f"No data found for {symbol}'s non-zero addresses.\n") - else: - df["t"] = pd.to_datetime(df["t"], unit="s") - df = df.set_index("t") - - elif r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def get_active_addresses( - symbol: str, - interval: str = "24h", - start_date: str = "2010-01-01", - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Returns active addresses of a certain symbol - [Source: https://glassnode.com] - - Parameters - ---------- - symbol : str - Asset to search active addresses (e.g., BTC) - interval : str - Interval frequency (e.g., 24h) - start_date : str - Initial date, format YYYY-MM-DD - end_date : str - Final date, format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - active addresses over time - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - url = api_url + "addresses/active_count" - - parameters = { - "api_key": get_current_user().credentials.API_GLASSNODE_KEY, - "a": symbol, - "i": interval, - "s": str(ts_start_date), - "u": str(ts_end_date), - } - - r = request(url, params=parameters) - df = pd.DataFrame() - - if r.status_code == 200: - df = pd.DataFrame(json.loads(r.text)) - - if df.empty: - console.print(f"No data found for {symbol}'s active addresses.\n") - else: - df["t"] = pd.to_datetime(df["t"], unit="s") - df = df.set_index("t") - - elif r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def get_hashrate( - symbol: str, - interval: str = "24h", - start_date: str = "2010-01-01", - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Returns dataframe with mean hashrate of btc or eth blockchain and symbol price - [Source: https://glassnode.com] - - Parameters - ---------- - symbol : str - Blockchain to check hashrate (BTC or ETH) - start_date : str - Initial date, format YYYY-MM-DD - end_date : str - Final date, format YYYY-MM-DD - interval : str - Interval frequency (e.g., 24h) - - Returns - ------- - pd.DataFrame - mean hashrate and symbol price over time - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - url = api_url + "mining/hash_rate_mean" - url2 = api_url + "market/price_usd_close" - - parameters = { - "api_key": get_current_user().credentials.API_GLASSNODE_KEY, - "a": symbol, - "i": interval, - "s": str(ts_start_date), - "u": str(ts_end_date), - } - - df = pd.DataFrame() - - r = request(url, params=parameters) - r2 = request(url2, params=parameters) - - if r.status_code == 200 and r2.status_code == 200: - df = pd.DataFrame(json.loads(r.text)) - df2 = pd.DataFrame(json.loads(r2.text)) - - if df.empty or df2.empty: - console.print(f"No data found for {symbol}'s hashrate or price.\n") - else: - df = df.set_index("t") - df2 = df2.set_index("t") - df.index = pd.to_datetime(df.index, unit="s") - df = df.rename(columns={"v": "hashrate"}) - df2.index = pd.to_datetime(df2.index, unit="s") - df2 = df2.rename(columns={"v": "price"}) - df = df.merge(df2, left_index=True, right_index=True, how="outer") - - elif r.status_code == 401 or r2.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - - else: - if r.status_code != 200: - console.print(f"Error getting hashrate: {r.text}") - - if r2.status_code != 200: - console.print(f"Error getting {symbol} price: {r2.text}") - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def get_exchange_balances( - symbol: str, - exchange: str = "aggregated", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Returns the total amount of coins held on exchange addresses in units and percentage. - [Source: https://glassnode.com] - - Parameters - ---------- - symbol : str - Asset to search active addresses (e.g., BTC) - exchange : str - Exchange to check net position change (possible values are: aggregated, binance, bittrex, - coinex, gate.io, gemini, huobi, kucoin, poloniex, bibox, bigone, bitfinex, hitbtc, kraken, - okex, bithumb, zb.com, cobinhood, bitmex, bitstamp, coinbase, coincheck, luno), by default "aggregated" - start_date : Optional[str], optional - Initial date (format YYYY-MM-DD) by default 2 years ago - end_date : Optional[str], optional - Final date (format YYYY-MM-DD) by default 1 year ago - - Returns - ------- - pd.DataFrame - total amount of coins in units/percentage and symbol price over time - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.crypto.dd.eb(symbol="BTC") - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=365 * 2)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = (datetime.now() - timedelta(days=367)).strftime("%Y-%m-%d") - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - url = api_url + "distribution/balance_exchanges" - url2 = api_url + "distribution/balance_exchanges_relative" - url3 = api_url + "market/price_usd_close" - - parameters = { - "api_key": get_current_user().credentials.API_GLASSNODE_KEY, - "a": symbol, - "i": "24h", - "e": exchange, - "s": str(ts_start_date), - "u": str(ts_end_date), - } - df = pd.DataFrame() - - r1 = request(url, params=parameters) # get balances - r2 = request(url2, params=parameters) # get relative (percentage) balances - r3 = request( - url3, params=parameters - ) # get price TODO: grab data from loaded symbol - - if r1.status_code == 200 and r2.status_code == 200 and r3.status_code == 200: - df1 = pd.DataFrame(json.loads(r1.text)) - df1.set_index("t", inplace=True) - df1.rename(columns={"v": "stacked"}, inplace=True) - - df2 = pd.DataFrame(json.loads(r2.text)) - df2.set_index("t", inplace=True) - df2.rename(columns={"v": "percentage"}, inplace=True) - - df3 = pd.DataFrame(json.loads(r3.text)) - df3.set_index("t", inplace=True) - df3.rename(columns={"v": "price"}, inplace=True) - - df = pd.merge(df1, df2, left_index=True, right_index=True) - df = pd.merge(df, df3, left_index=True, right_index=True) - df.index = pd.to_datetime(df.index, unit="s") - - if df.empty or df1.empty or df2.empty or df3.empty: - console.print(f"No data found for {symbol}'s exchange balance or price.\n") - - elif r1.status_code == 401 or r2.status_code == 401 or r3.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - if r1.status_code != 200: - console.print(f"Error getting {symbol}'s exchange balance: {r1.text}") - - if r2.status_code != 200: - console.print( - f"Error getting {symbol}'s exchange balance relatives: {r2.text}" - ) - - if r3.status_code != 200: - console.print(f"Error getting {symbol} price: {r3.text}") - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def get_exchange_net_position_change( - symbol: str, - exchange: str = "binance", - start_date: str = "2010-01-01", - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Returns 30d change of the supply held in exchange wallets of a certain symbol. - [Source: https://glassnode.com] - - Parameters - ---------- - symbol : str - Asset symbol to search supply (e.g., BTC) - exchange : str - Exchange to check net position change (e.g., binance) - start_date : Optional[str] - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - supply change in exchange wallets of a certain symbol over time - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - url = api_url + "distribution/exchange_net_position_change" - - parameters = { - "api_key": get_current_user().credentials.API_GLASSNODE_KEY, - "a": symbol, - "i": "24h", - "e": exchange, - "s": str(ts_start_date), - "u": str(ts_end_date), - } - - r = request(url, params=parameters) - df = pd.DataFrame() - - if r.status_code == 200: - df = pd.DataFrame(json.loads(r.text)) - - if df.empty: - console.print(f"No data found for {symbol}'s net position change.\n") - else: - df["t"] = pd.to_datetime(df["t"], unit="s") - df = df.set_index("t") - elif r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return df diff --git a/openbb_terminal/cryptocurrency/due_diligence/glassnode_view.py b/openbb_terminal/cryptocurrency/due_diligence/glassnode_view.py deleted file mode 100644 index 7102d5ebf2f..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/glassnode_view.py +++ /dev/null @@ -1,392 +0,0 @@ -import logging -import os -from datetime import datetime -from typing import Optional, Union - -import numpy as np - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.due_diligence.glassnode_model import ( - get_active_addresses, - get_exchange_balances, - get_exchange_net_position_change, - get_hashrate, - get_non_zero_addresses, -) -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def display_active_addresses( - symbol: str, - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - interval: str = "24h", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots active addresses of a certain symbol over time - [Source: https://glassnode.org] - - Parameters - ---------- - symbol : str - Asset to search active addresses (e.g., BTC) - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - interval : str - Interval frequency (possible values are: 24h, 1w, 1month) - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df_addresses = get_active_addresses(symbol, interval, start_date, end_date) - - if df_addresses.empty: - return None - - fig = OpenBBFigure(yaxis_title="Addresses") - fig.set_title(f"Active {symbol} addresses over time") - - fig.add_scatter( - x=df_addresses.index, - y=df_addresses["v"], - name="Active Addresses", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "active", - df_addresses, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def display_non_zero_addresses( - symbol: str, - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots addresses with non-zero balance of a certain symbol - [Source: https://glassnode.org] - - Parameters - ---------- - symbol : str - Asset to search (e.g., BTC) - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df_addresses = get_non_zero_addresses(symbol, start_date, end_date) - - if df_addresses.empty: - return None - - fig = OpenBBFigure(yaxis_title="Addresses") - fig.set_title(f"{symbol} Addresses with non-zero balances") - - fig.add_scatter(x=df_addresses.index, y=df_addresses["v"], name="Addresses") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "nonzero", - df_addresses, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def display_exchange_net_position_change( - symbol: str, - exchange: str = "binance", - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots 30d change of the supply held in exchange wallets. - [Source: https://glassnode.org] - - Parameters - ---------- - symbol : str - Asset to search active addresses (e.g., BTC) - exchange : str - Exchange to check net position change (possible values are: aggregated, binance, - bittrex, coinex, gate.io, gemini, huobi, kucoin, poloniex, bibox, bigone, bitfinex, - hitbtc, kraken, okex, bithumb, zb.com, cobinhood, bitmex, bitstamp, coinbase, coincheck, luno) - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df_addresses = get_exchange_net_position_change( - symbol, exchange, start_date, end_date - ) - - if df_addresses.empty: - return None - - fig = OpenBBFigure( - yaxis_title=f"30d change of {symbol} supply held in exchange wallets" - ) - fig.set_title( - f"{symbol}: Exchange Net Position Change - {'all exchanges' if exchange == 'aggregated' else exchange}", - ) - - fig.add_scatter( - x=df_addresses[df_addresses["v"] < 0].index, - y=df_addresses[df_addresses["v"] < 0]["v"].values, - mode="lines", - name="Negative", - line_color=theme.down_color, - ) - fig.add_scatter( - x=df_addresses[df_addresses["v"] < 0].index, - y=np.zeros(len(df_addresses[df_addresses["v"] < 0])), - mode="lines", - fill="tonexty", - name="Negative", - line_color=theme.down_color, - fillcolor=theme.down_color, - ) - fig.add_scatter( - x=df_addresses[df_addresses["v"] >= 0].index, - y=df_addresses[df_addresses["v"] >= 0]["v"].values, - mode="lines", - name="Positive", - line_color=theme.up_color, - ) - fig.add_scatter( - x=df_addresses[df_addresses["v"] >= 0].index, - y=np.zeros(len(df_addresses[df_addresses["v"] >= 0])), - mode="lines", - fill="tonexty", - name="Positive", - line_color=theme.up_color, - fillcolor=theme.up_color, - ) - fig.update_traces(showlegend=False) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "change", - df_addresses, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def display_exchange_balances( - symbol: str, - exchange: str = "aggregated", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - percentage: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots total amount of coins held on exchange addresses in units and percentage. - [Source: https://glassnode.org] - - Parameters - ---------- - symbol : str - Asset to search active addresses (e.g., BTC) - exchange : str - Exchange to check net position change (possible values are: aggregated, binance, bittrex, - coinex, gate.io, gemini, huobi, kucoin, poloniex, bibox, bigone, bitfinex, hitbtc, kraken, - okex, bithumb, zb.com, cobinhood, bitmex, bitstamp, coinbase, coincheck, luno), by default "aggregated" - start_date : Optional[str], optional - Initial date (format YYYY-MM-DD) by default 2 years ago - end_date : Optional[str], optional - Final date (format YYYY-MM-DD) by default 1 year ago - percentage : bool - Show percentage instead of stacked value. - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.crypto.dd.eb_chart(symbol="BTC") - """ - - df_balance = get_exchange_balances(symbol, exchange, start_date, end_date) - - if df_balance.empty: - return None - - fig = OpenBBFigure.create_subplots(specs=[[{"secondary_y": True}]]) - fig.set_title( - f"{symbol}: Total Balance in" - f" {'all exchanges' if exchange == 'aggregated' else exchange}" - ) - - fig.set_yaxis_title( - f"{symbol} units {'[%]' if percentage else ''}", secondary_y=True, side="left" - ) - fig.set_yaxis_title(f"{symbol} price [$]", secondary_y=False, showgrid=False) - - fig.add_scatter( - x=df_balance.index, - y=df_balance["percentage"] * 100 if percentage else df_balance["stacked"], - mode="lines", - name=f"{symbol} Unit", - secondary_y=True, - ) - - fig.add_scatter( - x=df_balance.index, - y=df_balance["price"], - mode="lines", - name=f"{symbol} Price", - line_color="orange", - secondary_y=False, - ) - - fig.update_layout(margin=dict(t=30)) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "eb", - df_balance, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def display_hashrate( - symbol: str, - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - interval: str = "24h", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots dataframe with mean hashrate of btc or eth blockchain and symbol price. - [Source: https://glassnode.org] - - Parameters - ---------- - symbol : str - Blockchain to check mean hashrate (BTC or ETH) - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - interval : str - Interval frequency (possible values are: 24, 1w, 1month) - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df = get_hashrate(symbol, interval, start_date, end_date) - - if df.empty: - return None - - fig = OpenBBFigure.create_subplots(specs=[[{"secondary_y": True}]]) - - fig.add_scatter( - x=df.index, - y=df["hashrate"] / 1e12, - mode="lines", - name="Hash Rate", - line_color=theme.down_color, - secondary_y=True, - ) - - fig.set_yaxis_title( - f"{symbol} hashrate (Terahashes/second)", - secondary_y=True, - side="left", - tickformat=".2s", - ) - fig.set_yaxis_title(f"{symbol} price [$]", secondary_y=False, showgrid=False) - fig.set_title(f"{symbol}: Mean hashrate") - - fig.add_scatter( - x=df.index, - y=df["price"], - mode="lines", - name="Price", - line_color=theme.up_color, - secondary_y=False, - ) - fig.update_layout(margin=dict(t=30)) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hr", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/due_diligence/messari_model.py b/openbb_terminal/cryptocurrency/due_diligence/messari_model.py deleted file mode 100644 index d38f57d257e..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/messari_model.py +++ /dev/null @@ -1,781 +0,0 @@ -"""Messari model""" - -__docformat__ = "numpy" -# flake8: noqa -# pylint: disable=C0301,C0302 - -import logging -import re -from datetime import datetime, timedelta -from typing import Any, Optional, Tuple - -import pandas as pd - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_replace_underscores_in_column_names, - prettify_column_names, -) -from openbb_terminal.cryptocurrency.due_diligence.pycoingecko_model import ( - get_coin_tokenomics, -) -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import lambda_long_number_format, request -from openbb_terminal.rich_config import console -from openbb_terminal.core.session.current_user import get_current_user - -# pylint: disable=unsupported-assignment-operation - -logger = logging.getLogger(__name__) - -INTERVALS_TIMESERIES = ["5m", "15m", "30m", "1h", "1d", "1w"] -# pylint: disable=unsupported-assignment-operation - - -@log_start_end(log=logger) -def get_available_timeseries(only_free: bool = True) -> pd.DataFrame: - """Returns available messari timeseries - [Source: https://messari.io/] - - Parameters - ---------- - only_free : bool - Display only timeseries available for free - - Returns - ------- - pd.DataFrame - available timeseries - """ - r = request("https://data.messari.io/api/v1/assets/metrics") - if r.status_code == 200: - data = r.json() - metrics = data["data"]["metrics"] - arr = [] - for metric in metrics: - sources = "" - for source in metric["source_attribution"]: - sources += source["name"] - sources += "," - sources = sources.rstrip(",") - arr.append( - { - "id": metric["metric_id"], - "Title": metric["name"], - "Description": metric["description"], - "Requires Paid Key": "role_restriction" in metric, - "Sources": sources, - } - ) - df = pd.DataFrame(arr) - df.set_index("id", inplace=True) - if only_free: - df = df.drop(df[df["Requires Paid Key"]].index) - return df - return pd.DataFrame() - - -base_url = "https://data.messari.io/api/v1/" -base_url2 = "https://data.messari.io/api/v2/" - - -@log_start_end(log=logger) -def get_marketcap_dominance( - symbol: str, - interval: str = "1d", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Returns market dominance of a coin over time - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check market cap dominance - interval : str - Interval frequency (possible values are: 5m, 15m, 30m, 1h, 1d, 1w) - start_date : Optional[str] - Initial date like string (e.g., 2021-10-01) - end_date : Optional[str] - End date like string (e.g., 2021-10-01) - - Returns - ------- - pd.DataFrame - market dominance percentage over time - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> mcapdom_df = openbb.crypto.dd.mcapdom(symbol="BTC") - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - messari_timeseries = get_messari_timeseries( - symbol=symbol, - end_date=end_date, - start_date=start_date, - interval=interval, - timeseries_id="mcap.dom", - ) - df, _ = messari_timeseries if messari_timeseries else (pd.DataFrame(), "") - return df - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_messari_timeseries( - symbol: str, - timeseries_id: str, - interval: str = "1d", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> Tuple[pd.DataFrame, str]: - """Returns messari timeseries - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check messari timeseries - timeseries_id : str - Messari timeserie id - interval : str - Interval frequency (possible values are: 5m, 15m, 30m, 1h, 1d, 1w) - start : Optional[str] - Initial date like string (e.g., 2021-10-01) - end : Optional[str] - End date like string (e.g., 2021-10-01) - - Returns - ------- - Tuple[pd.DataFrame, str] - Messari timeseries over time, - Timeseries title - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - url = base_url + f"assets/{symbol}/metrics/{timeseries_id}/time-series" - - headers = {"x-messari-api-key": get_current_user().credentials.API_MESSARI_KEY} - - parameters = { - "start": start_date, - "end": end_date, - "interval": interval, - } - - r = request(url, params=parameters, headers=headers) - - df = pd.DataFrame() - title = "" - - if r.status_code == 200: - data = r.json()["data"] - title = data["schema"]["name"] - df = pd.DataFrame(data["values"], columns=data["parameters"]["columns"]) - - if df.empty: - console.print(f"No data found for {symbol}.\n") - else: - df = df.set_index("timestamp") - df.index = pd.to_datetime(df.index, unit="ms") - elif r.status_code == 401: - if "requires a pro or enterprise subscription" in r.text: - console.print("[red]API Key not authorized for Premium Feature[/red]\n") - else: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - return df, title - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_links(symbol: str) -> pd.DataFrame: - """Returns asset's links - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check links - - Returns - ------- - pd.DataFrame - asset links - """ - - url = base_url2 + f"assets/{symbol}/profile" - - headers = {"x-messari-api-key": get_current_user().credentials.API_MESSARI_KEY} - - params = {"fields": "profile/general/overview/official_links"} - - r = request(url, headers=headers, params=params) - - df = pd.DataFrame() - - if r.status_code == 200: - data = r.json()["data"] - df = pd.DataFrame(data["profile"]["general"]["overview"]["official_links"]) - df.columns = map(str.capitalize, df.columns) - return df - if r.status_code == 401: - print("[red]Invalid API Key[/red]\n") - else: - print(r.text) - return pd.DataFrame() - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_roadmap(symbol: str, ascend: bool = True) -> pd.DataFrame: - """Returns coin roadmap - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check roadmap - ascend: bool - reverse order - - Returns - ------- - pd.DataFrame - roadmap - """ - - url = base_url2 + f"assets/{symbol}/profile" - - headers = {"x-messari-api-key": get_current_user().credentials.API_MESSARI_KEY} - - params = {"fields": "profile/general/roadmap"} - - r = request(url, headers=headers, params=params) - - df = pd.DataFrame() - - if r.status_code == 200: - data = r.json()["data"] - df = pd.DataFrame(data["profile"]["general"]["roadmap"]) - df["date"] = pd.to_datetime(df["date"]) - df.columns = map(str.capitalize, df.columns) - df = df.dropna(axis=1, how="all") - df["Date"] = df["Date"].dt.date - show_df = df - show_df = show_df.sort_values(by="Date", ascending=ascend) - show_df.fillna("Unknown", inplace=True) - elif r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_tokenomics(symbol: str, coingecko_id: str) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Returns coin tokenomics - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check tokenomics - coingecko_id : str - ID from coingecko - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame] - Metric Value tokenomics, - Circulating supply overtime - """ - - url = base_url2 + f"assets/{symbol}/profile" - - headers = {"x-messari-api-key": ""} - - params = {"fields": "profile/economics/consensus_and_emission"} - - r = request(url, headers=headers, params=params) - - df = pd.DataFrame() - circ_df = pd.DataFrame() - if r.status_code == 200: - data = r.json()["data"] - tokenomics_data = data["profile"]["economics"]["consensus_and_emission"] - df = pd.DataFrame( - { - "Metric": [ - "Emission Type", - "Consensus Mechanism", - "Consensus Details", - "Mining Algorithm", - "Block Reward", - ], - "Value": [ - tokenomics_data["supply"]["general_emission_type"], - tokenomics_data["consensus"]["general_consensus_mechanism"], - tokenomics_data["consensus"]["consensus_details"], - tokenomics_data["consensus"]["mining_algorithm"], - tokenomics_data["consensus"]["block_reward"], - ], - } - ) - df["Value"] = df["Value"].str.replace("n/a", "-") - cg_df = get_coin_tokenomics(coingecko_id) - df = pd.concat([df, cg_df], ignore_index=True, sort=False) - df.fillna("-", inplace=True) - circ_df, _ = get_messari_timeseries( - symbol=symbol, - timeseries_id="sply.circ", - interval="1d", - start_date="", - end_date="", - ) - elif r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return df, circ_df - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_project_product_info( - symbol: str, -) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame]: - """Returns coin product info - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check product info - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame] - Metric, Value with project and technology details, - Coin public repos, - Coin audits, - Coin known exploits/vulns - """ - - url = base_url2 + f"assets/{symbol}/profile" - - headers = {"x-messari-api-key": ""} - - params = {"fields": "profile/general/overview/project_details,profile/technology"} - - r = request(url, headers=headers, params=params) - - df = pd.DataFrame() - if r.status_code == 200: - data = r.json()["data"] - project_details = data["profile"]["general"]["overview"]["project_details"] - technology_data = data["profile"]["technology"] - technology_details = technology_data["overview"]["technology_details"] - df_info = pd.DataFrame( - { - "Metric": ["Project Details", "Technology Details"], - "Value": [project_details, technology_details], - } - ) - df_repos = pd.DataFrame(technology_data["overview"]["client_repositories"]) - df_repos.columns = prettify_column_names(df_repos.columns) - df_repos.fillna("-", inplace=True) - df_audits = pd.DataFrame(technology_data["security"]["audits"]) - df_audits.columns = prettify_column_names(df_audits.columns) - if not df_audits.empty: - df_audits["Date"] = pd.to_datetime(df_audits["Date"]) - df_audits.fillna("-", inplace=True) - df_vulns = pd.DataFrame( - technology_data["security"]["known_exploits_and_vulnerabilities"] - ) - df_vulns.columns = prettify_column_names(df_vulns.columns) - if not df_vulns.empty: - df_vulns["Date"] = pd.to_datetime(df_vulns["Date"]) - df_vulns.fillna("-", inplace=True) - return df_info, df_repos, df_audits, df_vulns - if r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return df, df, df, df - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_team(symbol: str) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Returns coin team - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check team - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame] - Individuals, - Organizations - """ - - url = base_url2 + f"assets/{symbol}/profile" - - headers = {"x-messari-api-key": get_current_user().credentials.API_MESSARI_KEY} - - params = {"fields": "profile/contributors"} - - r = request(url, headers=headers, params=params) - - df = pd.DataFrame() - if r.status_code == 200: - data = r.json()["data"] - df_individual_contributors = pd.DataFrame( - data["profile"]["contributors"]["individuals"] - ) - if not df_individual_contributors.empty: - df_individual_contributors.fillna("-", inplace=True) - df_individual_contributors.insert( - 0, - "Name", - df_individual_contributors[["first_name", "last_name"]].apply( - lambda x: " ".join(x), axis=1 - ), - ) - df_individual_contributors.drop( - ["slug", "avatar_url", "first_name", "last_name"], - axis=1, - inplace=True, - errors="ignore", - ) - df_individual_contributors.columns = map( - str.capitalize, df_individual_contributors.columns - ) - df_individual_contributors.replace( - to_replace=[None], value="-", inplace=True - ) - df_organizations_contributors = pd.DataFrame( - data["profile"]["contributors"]["organizations"] - ) - if not df_organizations_contributors.empty: - df_organizations_contributors.drop( - ["slug", "logo"], axis=1, inplace=True, errors="ignore" - ) - df_organizations_contributors.columns = map( - str.capitalize, df_organizations_contributors.columns - ) - df_organizations_contributors.replace( - to_replace=[None], value="-", inplace=True - ) - return df_individual_contributors, df_organizations_contributors - if r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return df, df - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_investors(symbol: str) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Returns coin investors - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check investors - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame] - Individuals, - Organizations - """ - - url = base_url2 + f"assets/{symbol}/profile" - - headers = {"x-messari-api-key": get_current_user().credentials.API_MESSARI_KEY} - - params = {"fields": "profile/investors"} - - r = request(url, headers=headers, params=params) - - df = pd.DataFrame() - if r.status_code == 200: - data = r.json()["data"] - df_individual_investors = pd.DataFrame( - data["profile"]["investors"]["individuals"] - ) - if not df_individual_investors.empty: - df_individual_investors.fillna("-", inplace=True) - df_individual_investors.insert( - 0, - "Name", - df_individual_investors[["first_name", "last_name"]].apply( - lambda x: " ".join(x), axis=1 - ), - ) - df_individual_investors.drop( - ["slug", "avatar_url", "first_name", "last_name"], - axis=1, - inplace=True, - errors="ignore", - ) - df_individual_investors.columns = map( - str.capitalize, df_individual_investors.columns - ) - df_individual_investors.replace(to_replace=[None], value="-", inplace=True) - df_organizations_investors = pd.DataFrame( - data["profile"]["investors"]["organizations"] - ) - if not df_organizations_investors.empty: - df_organizations_investors.drop( - ["slug", "logo"], axis=1, inplace=True, errors="ignore" - ) - df_organizations_investors.columns = map( - str.capitalize, df_organizations_investors.columns - ) - df_organizations_investors.replace( - to_replace=[None], value="-", inplace=True - ) - return df_individual_investors, df_organizations_investors - if r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return df, df - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_governance(symbol: str) -> Tuple[str, pd.DataFrame]: - """Returns coin governance - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check governance - - Returns - ------- - Tuple[str, pd.DataFrame] - Governance summary, - Metric Value with governance details - """ - - url = base_url2 + f"assets/{symbol}/profile" - - headers = {"x-messari-api-key": get_current_user().credentials.API_MESSARI_KEY} - - params = {"fields": "profile/governance"} - - r = request(url, headers=headers, params=params) - - df = pd.DataFrame() - if r.status_code == 200: - data = r.json()["data"] - governance_data = data["profile"]["governance"] - if ( - governance_data["onchain_governance"]["onchain_governance_type"] is not None - and governance_data["onchain_governance"]["onchain_governance_details"] - is not None - ): - return ( - re.sub("<[^>]*>", "", governance_data["governance_details"]), - pd.DataFrame( - { - "Metric": ["Type", "Details"], - "Value": [ - governance_data["onchain_governance"][ - "onchain_governance_type" - ], - governance_data["onchain_governance"][ - "onchain_governance_details" - ], - ], - } - ), - ) - return ( - re.sub("<[^>]*>", "", governance_data["governance_details"]), - df, - ) - if r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return "", df - - -def format_addresses(x: Any): - final_str = "" - for address in x: - final_str += f"{address['name']}: {address['link']}" - return final_str - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def get_fundraising( - symbol: str, -) -> Tuple[str, pd.DataFrame, pd.DataFrame, pd.DataFrame]: - """Returns coin fundraising - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check fundraising - - Returns - ------- - Tuple[str, pd.DataFrame, pd.DataFrame, pd.DataFrame] - Launch summary, - Sales rounds, - Treasury Accounts, - Metric Value launch details - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> fundraise = openbb.crypto.dd.fr(symbol="BTC") - """ - - url = base_url2 + f"assets/{symbol}/profile" - - headers = {"x-messari-api-key": get_current_user().credentials.API_MESSARI_KEY} - - params = {"fields": "profile/economics/launch"} - - r = request(url, headers=headers, params=params) - - df = pd.DataFrame() - if r.status_code == 200: - data = r.json()["data"] - launch_data = data["profile"]["economics"]["launch"] - launch_details = launch_data["general"]["launch_details"] - launch_type = launch_data["general"]["launch_style"] - launch_fundraising_rounds = pd.DataFrame( - launch_data["fundraising"]["sales_rounds"] - ) - if not launch_fundraising_rounds.empty: - launch_fundraising_rounds.fillna("-", inplace=True) - launch_fundraising_rounds.drop( - [ - "details", - "asset_collected", - "price_per_token_in_asset", - "amount_collected_in_asset", - "is_kyc_required", - "restricted_jurisdictions", - ], - axis=1, - inplace=True, - errors="ignore", - ) - launch_fundraising_rounds.columns = [ - lambda_replace_underscores_in_column_names(val) - for val in launch_fundraising_rounds.columns - ] - launch_fundraising_rounds["Start Date"] = launch_fundraising_rounds.apply( - lambda x: x["Start Date"].split("T")[0], axis=1 - ) - launch_fundraising_rounds["End Date"] = launch_fundraising_rounds.apply( - lambda x: x["End Date"].split("T")[0], axis=1 - ) - launch_fundraising_rounds.rename( - columns={ - "Native Tokens Allocated": "Tokens Allocated", - "Equivalent Price Per Token In Usd": "Price [$]", - "Amount Collected In Usd": "Amount Collected [$]", - }, - inplace=True, - ) - launch_fundraising_rounds.fillna("-", inplace=True) - - launch_fundraising_accounts = pd.DataFrame( - launch_data["fundraising"]["sales_treasury_accounts"] - ) - if not launch_fundraising_accounts.empty: - launch_fundraising_accounts.columns = [ - lambda_replace_underscores_in_column_names(val) - for val in launch_fundraising_accounts.columns - ] - launch_fundraising_accounts.drop( - ["Asset Held", "Security"], inplace=True, axis=1 - ) - launch_fundraising_accounts["Addresses"] = launch_fundraising_accounts[ - "Addresses" - ].map(format_addresses) - launch_distribution = pd.DataFrame( - { - "Metric": [ - "Genesis Date", - "Type", - "Total Supply", - "Investors [%]", - "Organization/Founders [%]", - "Rewards/Airdrops [%]", - ], - "Value": [ - ( - launch_data["initial_distribution"]["genesis_block_date"].split( - "T" - )[0] - if launch_data["initial_distribution"]["genesis_block_date"] - else "-" - ), - launch_type, - lambda_long_number_format( - launch_data["initial_distribution"]["initial_supply"] - ), - launch_data["initial_distribution"]["initial_supply_repartition"][ - "allocated_to_investors_percentage" - ], - launch_data["initial_distribution"]["initial_supply_repartition"][ - "allocated_to_organization_or_founders_percentage" - ], - launch_data["initial_distribution"]["initial_supply_repartition"][ - "allocated_to_premined_rewards_or_airdrops_percentage" - ], - ], - } - ) - return ( - launch_details, - launch_fundraising_rounds, - launch_fundraising_accounts, - launch_distribution, - ) - if r.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(r.text) - - return "", df, df, df diff --git a/openbb_terminal/cryptocurrency/due_diligence/messari_view.py b/openbb_terminal/cryptocurrency/due_diligence/messari_view.py deleted file mode 100644 index 971d18a2416..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/messari_view.py +++ /dev/null @@ -1,769 +0,0 @@ -"""Messari view""" - -__docformat__ = "numpy" - -# pylint: disable=C0201 - -import logging -import os -from datetime import datetime, timedelta -from typing import Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency import cryptocurrency_helpers -from openbb_terminal.cryptocurrency.dataframe_helpers import prettify_paragraph -from openbb_terminal.cryptocurrency.due_diligence.messari_model import ( - get_available_timeseries, - get_fundraising, - get_governance, - get_investors, - get_links, - get_marketcap_dominance, - get_messari_timeseries, - get_project_product_info, - get_roadmap, - get_team, - get_tokenomics, -) -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_messari_timeseries_list( - limit: int = 10, - query: str = "", - only_free: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing messari timeseries list - [Source: https://messari.io/] - - Parameters - ---------- - limit : int - number to show - query : str - Query to search across all messari timeseries - only_free : bool - Display only timeseries available for free - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = get_available_timeseries(only_free) - if not df.empty: - if query: - mask = np.column_stack( - [ - df[col].str.contains(query, na=False, regex=False, case=False) - for col in ["Title", "Description"] - ] - ) - df = df.loc[mask.any(axis=1)] - if df.empty: - console.print(f"\nNo timeseries found with query {query}\n") - else: - print_rich_table( - df, - index_name="ID", - headers=list(df.columns), - show_index=True, - title="Messari Timeseries", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mt", - df, - sheet_name, - ) - else: - console.print("\nUnable to retrieve data from Messari.\n") - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_messari_timeseries( - symbol: str, - timeseries_id: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - interval: str = "1d", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots messari timeseries - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check market cap dominance - timeseries_id: str - Obtained by api.crypto.dd.get_mt command - start_date : Optional[str] - Initial date like string (e.g., 2021-10-01) - end_date : Optional[str] - End date like string (e.g., 2021-10-01) - interval : str - Interval frequency (possible values are: 5m, 15m, 30m, 1h, 1d, 1w) - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df, title = get_messari_timeseries( - symbol=symbol, - timeseries_id=timeseries_id, - start_date=start_date, - end_date=end_date, - interval=interval, - ) - - if not df.empty: - fig = OpenBBFigure(yaxis_title=title) - fig.set_title(f"{symbol}'s {title}") - - fig.add_scatter( - x=df.index, - y=df[df.columns[0]], - mode="lines", - name=df.columns[0], - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mt", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_marketcap_dominance( - symbol: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - interval: str = "1d", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots market dominance of a coin over time - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check market cap dominance - start_date : Optional[str] - Initial date like string (e.g., 2021-10-01) - end_date : Optional[str] - End date like string (e.g., 2021-10-01) - interval : str - Interval frequency (possible values are: 5m, 15m, 30m, 1h, 1d, 1w) - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df = get_marketcap_dominance( - symbol=symbol, start_date=start_date, end_date=end_date, interval=interval - ) - - if not df.empty: - fig = OpenBBFigure(yaxis_title="Percentage share") - fig.set_title(f"{symbol}'s Market Cap Dominance over time") - - fig.add_scatter( - x=df.index, - y=df["marketcap_dominance"], - mode="lines", - name=symbol.upper(), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mcapdom", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_links( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing coin links - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check links - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = get_links(symbol) - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"{symbol} Links", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "links", - df, - sheet_name, - ) - else: - console.print("\nUnable to retrieve data from Messari.\n") - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_roadmap( - symbol: str, - ascend: bool = True, - limit: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots coin roadmap - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check roadmap - ascend: bool - reverse order - limit : int - number to show - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure() - - df = get_roadmap(symbol, ascend) - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"{symbol} Roadmap", - export=bool(export), - limit=limit, - ) - if not fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rm", - df, - sheet_name, - ) - - df_prices, _ = cryptocurrency_helpers.load_yf_data( - symbol=symbol, - currency="USD", - days=4380, - interval="1d", - ) - if not df_prices.empty: - fig = OpenBBFigure(yaxis_title="Price [$]") - fig.set_title(f"{symbol.upper()} Price and Roadmap") - - roadmap_dates = pd.to_datetime( - df["Date"], format="%Y-%m-%d", errors="coerce" - ) - - df_copy = df - df_copy["Date"] = pd.to_datetime( - df_copy["Date"], format="%Y-%m-%d", errors="coerce" - ) - - df_copy = df_copy[df_copy["Date"].notnull()] - - max_price = df_prices["Close"].max() - for counter, x in enumerate(roadmap_dates): - if x > df_prices.index[0]: - fig.add_annotation( - x=x, - y=max_price * 0.7, - text=df.iloc[counter]["Title"], - textangle=90, - font=dict(size=15), - xshift=10, - ) - fig.add_vline( - x=x, - line=dict( - color="orange", - width=1.5, - dash="dash", - ), - ) - - fig.add_scatter( - x=df_prices.index, - y=df_prices["Close"].values, - mode="lines", - ) - - if fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rm", - df, - sheet_name, - fig, - ) - return fig.show(external=external_axes) - - return console.print("\nUnable to retrieve data from Messari.\n") - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_tokenomics( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots coin tokenomics - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check tokenomics - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - coingecko_id = cryptocurrency_helpers.get_coingecko_id(symbol) - df, circ_df = get_tokenomics(symbol, coingecko_id) - - if not df.empty and not circ_df.empty: - df = df.applymap(lambda x: lambda_long_number_format(x, 2)) - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"{symbol} Tokenomics", - export=bool(export), - ) - fig = OpenBBFigure.create_subplots( - specs=[[{"secondary_y": True}]], vertical_spacing=0.1 - ) - - df_prices, _ = cryptocurrency_helpers.load_yf_data( - symbol=symbol, - currency="USD", - days=4380, - interval="1d", - ) - merged_df = pd.concat([circ_df, df_prices], axis=1) - - color_palette = theme.get_colors() - - fig.add_scatter( - x=merged_df.index, - y=merged_df["circulating_supply"], - mode="lines", - name="Circ Supply", - line=dict(color=color_palette[0]), - secondary_y=True, - ) - if not df_prices.empty: - fig.add_scatter( - x=merged_df.index, - y=merged_df["Close"], - mode="lines", - name="Price", - line=dict(color=color_palette[1]), - secondary_y=False, - ) - - fig.set_yaxis_title(f"{symbol} price [$]", secondary_y=False) - - fig.set_title(f"{symbol} circulating supply over time") - fig.set_yaxis_title("Number of tokens", secondary_y=True, side="left") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "tk", - df, - sheet_name, - fig, - ) - return fig.show(external=external_axes) - - return console.print("\nUnable to retrieve data from Messari.\n") - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_project_info( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing project info - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check project info - export : str - Export dataframe data to csv,json,xlsx file - """ - (df_info, df_repos, df_audits, df_vulns) = get_project_product_info(symbol) - - for df, title in zip( - [df_info, df_repos, df_audits, df_vulns], - ["General Info", "Public Repos", "Audits", "Vulnerabilities"], - ): - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"{symbol} {title}", - export=bool(export), - ) - else: - console.print(f"\n{title} not found\n") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "pi", - df_info, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_investors( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing coin investors - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check coin investors - export : str - Export dataframe data to csv,json,xlsx file - """ - (df_individuals, df_organizations) = get_investors(symbol) - if not df_individuals.empty or not df_organizations.empty: - if not df_individuals.empty: - print_rich_table( - df_individuals, - headers=list(df_individuals.columns), - show_index=False, - title=f"{symbol} Investors - Individuals", - export=bool(export), - ) - else: - console.print("\nIndividual investors not found\n") - if not df_organizations.empty: - print_rich_table( - df_organizations, - headers=list(df_organizations.columns), - show_index=False, - title=f"{symbol} Investors - Organizations", - export=bool(export), - ) - else: - console.print("\nInvestors - Organizations not found\n") - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "inv", - df_individuals, - sheet_name, - ) - else: - console.print("\nInvestors not found\n") - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_team( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing coin team - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check coin team - export : str - Export dataframe data to csv,json,xlsx file - """ - (df_individuals, df_organizations) = get_team(symbol) - if not df_individuals.empty or not df_organizations.empty: - if not df_individuals.empty: - print_rich_table( - df_individuals, - headers=list(df_individuals.columns), - show_index=False, - title=f"{symbol} Team - Individuals", - export=bool(export), - ) - else: - console.print("\nIndividual team members not found\n") - if not df_organizations.empty: - print_rich_table( - df_organizations, - headers=list(df_organizations.columns), - show_index=False, - title=f"{symbol} Team - Organizations", - export=bool(export), - ) - else: - console.print("\nTeam organizations not found\n") - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "team", - df_individuals, - sheet_name, - ) - else: - console.print("\nTeam not found\n") - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_governance( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing coin governance - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check coin governance - export : str - Export dataframe data to csv,json,xlsx file - """ - (summary, df) = get_governance(symbol) - if summary: - summary = prettify_paragraph(summary) - console.print(summary, "\n") - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"{symbol} Governance details", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gov", - df, - sheet_name, - ) - else: - console.print(f"\n{symbol} governance details not found\n") - - -@log_start_end(log=logger) -@check_api_key(["API_MESSARI_KEY"]) -def display_fundraising( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display coin fundraising - [Source: https://messari.io/] - - Parameters - ---------- - symbol : str - Crypto symbol to check coin fundraising - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - (summary, df_sales_rounds, df_treasury_accs, df_details) = get_fundraising(symbol) - if summary: - summary = prettify_paragraph(summary) - console.print(summary, "\n") - if not df_sales_rounds.empty: - df_sales_rounds = df_sales_rounds.applymap( - lambda x: lambda_long_number_format(x, 2) - ) - print_rich_table( - df_sales_rounds, - headers=list(df_sales_rounds.columns), - show_index=False, - title=f"{symbol} Sales Rounds", - export=bool(export), - ) - else: - console.print("\nSales rounds not found\n") - - if not df_treasury_accs.empty: - print_rich_table( - df_treasury_accs, - headers=list(df_treasury_accs.columns), - show_index=False, - title=f"{symbol} Treasury Accounts", - export=bool(export), - ) - else: - console.print("\nTreasury accounts not found\n") - - if not df_details.empty: - values = [] - labels = [] - investors = df_details.loc[df_details["Metric"] == "Investors [%]"][ - "Value" - ].item() - founders = df_details.loc[df_details["Metric"] == "Organization/Founders [%]"][ - "Value" - ].item() - airdrops = ( - df_details.loc[df_details["Metric"] == "Rewards/Airdrops [%]"][ - "Value" - ].item(), - ) - - df_details.fillna("-", inplace=True) - - print_rich_table( - df_details, - headers=list(df_details.columns), - show_index=False, - title=f"{symbol} Fundraising Details", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fr", - df_details, - sheet_name, - ) - - if isinstance(investors, (int, float)) and investors > 0: - values.append(investors) - labels.append("Investors") - if isinstance(founders, (int, float)) and founders > 0: - values.append(founders) - labels.append("Organization/Founders") - if isinstance(airdrops[0], (int, float)) and airdrops[0] > 0: - values.append(airdrops[0]) - labels.append("Rewards/Airdrops") - if len(values) > 0 and sum(values) > 0: - fig = OpenBBFigure.create_subplots( - 1, - 3, - specs=[[{"type": "domain"}, {"type": "pie", "colspan": 2}, None]], - row_heights=[1], - column_widths=[0.1, 0.8, 0.1], - ) - fig.set_title(f"{symbol} Fundraising Distribution") - fig.add_pie( - labels=labels, - values=[s / 100 for s in values], - textinfo="label+percent", - hoverinfo="label+percent", - automargin=True, - rotation=45, - row=1, - col=2, - ) - fig.update_traces( - textposition="outside", - textfont_size=15, - marker=dict( - colors=theme.get_colors()[1:4], - line=dict(color="#F5EFF3", width=0.8), - ), - ) - return fig.show(external=external_axes) - - return console.print("\nFundraising details not found\n") diff --git a/openbb_terminal/cryptocurrency/due_diligence/pycoingecko_model.py b/openbb_terminal/cryptocurrency/due_diligence/pycoingecko_model.py deleted file mode 100644 index 5bd64355d3f..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/pycoingecko_model.py +++ /dev/null @@ -1,834 +0,0 @@ -"""CoinGecko model""" - -__docformat__ = "numpy" -# pylint:disable=unsupported-assignment-operation - -import logging -from typing import Any, Dict, List, Optional, Tuple, Union - -import numpy as np -import pandas as pd -import regex as re -from pycoingecko import CoinGeckoAPI - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_replace_underscores_in_column_names, -) -from openbb_terminal.cryptocurrency.discovery.pycoingecko_model import read_file_data -from openbb_terminal.cryptocurrency.pycoingecko_helpers import ( - DENOMINATION, - calc_change, - create_dictionary_with_prefixes, - filter_list, - find_discord, - remove_keys, - rename_columns_in_dct, -) -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - -CHANNELS = { - "telegram_channel_identifier": "telegram", - "twitter_screen_name": "twitter", - "subreddit_url": "subreddit", - "bitcointalk_thread_identifier": "bitcointalk", - "facebook_username": "facebook", - "discord": "discord", -} - -BASE_INFO = [ - "id", - "name", - "symbol", - "asset_platform_id", - "description", - "contract_address", - "market_cap_rank", - "public_interest_score", -] - - -def format_df(df: pd.DataFrame): - df["Potential Market Cap ($)"] = df.apply( - lambda x: f"{int(x['Potential Market Cap ($)']):n}", axis=1 - ) - - df["Current Market Cap ($)"] = df.apply( - lambda x: f"{int(x['Current Market Cap ($)']):n}", axis=1 - ) - return df - - -@log_start_end(log=logger) -def get_coin_potential_returns( - main_coin: str, - to_symbol: Union[str, None] = None, - limit: Union[int, None] = None, - price: Union[int, None] = None, -) -> pd.DataFrame: - """Fetch data to calculate potential returns of a certain coin. [Source: CoinGecko] - - Parameters - ---------- - main_coin : str - Coin loaded to check potential returns for (e.g., algorand) - to_symbol : str | None - Coin to compare main_coin with (e.g., bitcoin) - limit : int | None - Number of coins with highest market cap to compare main_coin with (e.g., 5) - price - Target price of main_coin to check potential returns (e.g., 5) - - Returns - ------- - pd.DataFrame - Potential returns data - Columns: Coin, Current Price, Target Coin, Potential Price, Potential Market Cap ($), Change (%) - """ - client = CoinGeckoAPI() - COLUMNS = [ - "Coin", - "Current Price ($)", - "Current Market Cap ($)", - "Target Coin", - "Potential Price ($)", - "Potential Market Cap ($)", - "Change (%)", - ] - if limit and limit > 0: # user wants to compare with top coins - data = client.get_price( - ids=f"{main_coin}", - vs_currencies="usd", - include_market_cap=True, - include_24hr_vol=False, - include_24hr_change=False, - include_last_updated_at=False, - ) - top_coins_data = client.get_coins_markets( - vs_currency="usd", per_page=limit, order="market_cap_desc" - ) - main_coin_data = data[main_coin] - diff_arr = [] - for coin in top_coins_data: - market_cap_difference_percentage = calc_change( - coin["market_cap"], main_coin_data["usd_market_cap"] - ) - future_price = main_coin_data["usd"] * ( - 1 + market_cap_difference_percentage / 100 - ) - diff_arr.append( - [ - main_coin, - main_coin_data["usd"], - main_coin_data["usd_market_cap"], - coin["id"], - future_price, - coin["market_cap"], - market_cap_difference_percentage, - ] - ) - df = pd.DataFrame( - data=diff_arr, - columns=COLUMNS, - ) - return format_df(df) - - if to_symbol: # user passed a coin - data = client.get_price( - ids=f"{main_coin},{to_symbol}", - vs_currencies="usd", - include_market_cap=True, - include_24hr_vol=False, - include_24hr_change=False, - include_last_updated_at=False, - ) - main_coin_data = data[main_coin] - vs_coin_data = data[to_symbol] - - if main_coin_data and vs_coin_data: - market_cap_difference_percentage = calc_change( - vs_coin_data["usd_market_cap"], main_coin_data["usd_market_cap"] - ) - future_price = main_coin_data["usd"] * ( - 1 + market_cap_difference_percentage / 100 - ) - df = pd.DataFrame( - data=[ - [ - main_coin, - main_coin_data["usd"], - main_coin_data["usd_market_cap"], - to_symbol, - future_price, - vs_coin_data["usd_market_cap"], - market_cap_difference_percentage, - ] - ], - columns=COLUMNS, - ) - return format_df(df) - - if price and price > 0: # user passed a price - data = client.get_price( - ids=main_coin, - vs_currencies="usd", - include_market_cap=True, - include_24hr_vol=False, - include_24hr_change=False, - include_last_updated_at=False, - ) - main_coin_data = data[main_coin] - if main_coin_data: - final_market_cap = ( - main_coin_data["usd_market_cap"] * price / main_coin_data["usd"] - ) - market_cap_difference_percentage = calc_change( - final_market_cap, main_coin_data["usd_market_cap"] - ) - future_price = main_coin_data["usd"] * ( - 1 + market_cap_difference_percentage / 100 - ) - df = pd.DataFrame( - data=[ - [ - main_coin, - main_coin_data["usd"], - main_coin_data["usd_market_cap"], - main_coin, - future_price, - final_market_cap, - market_cap_difference_percentage, - ] - ], - columns=COLUMNS, - ) - return format_df(df) - - return pd.DataFrame() - - -@log_start_end(log=logger) -def check_coin(symbol: str): - coins = read_file_data("coingecko_coins.json") - for coin in coins: - if coin["id"] == symbol: - return coin["id"] - if coin["symbol"] == symbol: - return coin["id"] - return None - - -@log_start_end(log=logger) -def get_coin_market_chart( - symbol: str = "", vs_currency: str = "usd", days: int = 30, **kwargs: Any -) -> pd.DataFrame: - """Get prices for given coin. [Source: CoinGecko] - - Parameters - ---------- - vs_currency: str - currency vs which display data - days: int - number of days to display the data - kwargs - unspecified keyword arguments - - Returns - ------- - pd.DataFrame - Prices for given coin - Columns: time, price, currency - """ - client = CoinGeckoAPI() - prices = client.get_coin_market_chart_by_id(symbol, vs_currency, days, **kwargs) - prices = prices["prices"] - df = pd.DataFrame(data=prices, columns=["time", "price"]) - df["time"] = pd.to_datetime(df.time, unit="ms") - df = df.set_index("time") - df["currency"] = vs_currency - return df - - -@log_start_end(log=logger) -def get_coin_tokenomics(symbol: str = "") -> pd.DataFrame: - """Get tokenomics for given coin. [Source: CoinGecko] - - Parameters - ---------- - symbol: str - coin symbol to check tokenomics - - Returns - ------- - pd.DataFrame - Metric, Value with tokenomics - """ - client = CoinGeckoAPI() - coin_data = client.get_coin_by_id(symbol) - block_time = coin_data["block_time_in_minutes"] - total_supply = coin_data["market_data"]["total_supply"] - max_supply = coin_data["market_data"]["max_supply"] - circulating_supply = coin_data["market_data"]["circulating_supply"] - return pd.DataFrame( - { - "Metric": [ - "Block time [min]", - "Total Supply", - "Max Supply", - "Circulating Supply", - ], - "Value": [block_time, total_supply, max_supply, circulating_supply], - } - ) - - -class Coin: - """Coin class, it holds loaded coin""" - - @log_start_end(log=logger) - def __init__(self, symbol: str, load_from_api: bool = True): - self.client = CoinGeckoAPI() - if load_from_api: - self._coin_list = self.client.get_coins_list() - else: - self._coin_list = read_file_data("coingecko_coins.json") - - self.coin_symbol, self.symbol = self._validate_coin(symbol) - - if self.coin_symbol: - self.coin: Dict[Any, Any] = self._get_coin_info() - else: - pass - - @log_start_end(log=logger) - def __str__(self): - return f"{self.coin_symbol}" - - @log_start_end(log=logger) - 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] - - Parameters - ---------- - symbol: str - Either coin symbol or coin id - - Returns - ---------- - Tuple[Optional[Any], Optional[Any]] - - str with coin - - str with symbol - """ - - coin = None - symbol = None - - for dct in self._coin_list: - if search_coin.lower() in [dct["symbol"], dct["id"]]: - coin = dct.get("id") - symbol = dct.get("symbol") - return coin, symbol - return None, None - - @log_start_end(log=logger) - def coin_list(self) -> List[Dict[str, Any]]: - """List all available coins [Source: CoinGecko] - - Returns - ---------- - List[Dict[str, Any]] - list of all available coin ids - """ - - return [token.get("id") for token in self._coin_list] - - @log_start_end(log=logger) - def _get_coin_info(self) -> Dict[str, Any]: - """Helper method which fetch the coin information by id from CoinGecko API like: - (name, price, market, ... including exchange tickers) [Source: CoinGecko] - - Returns - ---------- - Dict[str, Any] - Coin information - """ - - params = dict(localization="false", tickers="false", sparkline=True) - return self.client.get_coin_by_id(self.coin_symbol, **params) - - @log_start_end(log=logger) - def _get_links(self) -> Dict[str, Any]: - """Helper method that extracts links from coin [Source: CoinGecko] - - Returns - ---------- - Dict[str, Any] - Links related to coin - """ - - return self.coin.get("links", {}) - - @log_start_end(log=logger) - def get_repositories(self) -> Optional[Dict[str, Any]]: - """Get list of all repositories for given coin [Source: CoinGecko] - - Returns - ---------- - Dict[str, Any] - Repositories related to coin - """ - - return self._get_links().get("repos_url") - - @log_start_end(log=logger) - def get_developers_data(self) -> pd.DataFrame: - """Get coin development data from GitHub or BitBucket like: - number of pull requests, contributor etc [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - Developers Data - Columns: Metric, Value - """ - - dev = self.coin.get("developer_data", {}) - useless_keys = ( - "code_additions_deletions_4_weeks", - "last_4_weeks_commit_activity_series", - ) - remove_keys(useless_keys, dev) - df = pd.Series(dev).to_frame().reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - - return df[df["Value"].notna()] - - @log_start_end(log=logger) - def get_blockchain_explorers(self) -> Union[pd.DataFrame, Any]: - """Get list of URLs to blockchain explorers for given coin. [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - Blockchain Explorers - Columns: Metric, Value - """ - - blockchain = self._get_links().get("blockchain_site") - if blockchain: - dct = filter_list(blockchain) - df = pd.Series(dct).to_frame().reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - return df[df["Value"].notna()] - return None - - @log_start_end(log=logger) - def get_social_media(self) -> pd.DataFrame: - """Get list of URLs to social media like twitter, facebook, reddit... [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - Urls to social media - Columns: Metric, Value - """ - - social_dct = {} - links = self._get_links() - for channel in CHANNELS: # pylint: disable=consider-iterating-dictionary) - if channel in links: - value = links.get(channel, "") - if channel == "twitter_screen_name": - value = "https://twitter.com/" + value - elif channel == "bitcointalk_thread_identifier" and value is not None: - value = f"https://bitcointalk.org/index.php?topic={value}" - social_dct[channel] = value - social_dct["discord"] = find_discord(links.get("chat_url")) - dct = rename_columns_in_dct(social_dct, CHANNELS) - df = pd.Series(dct).to_frame().reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - return df[df["Value"].notna()] - - @log_start_end(log=logger) - def get_websites(self) -> pd.DataFrame: - """Get list of URLs to websites like homepage of coin, forum. [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - Urls to website, homepage, forum - Columns: Metric, Value - """ - - websites_dct = {} - links = self._get_links() - sites = ["homepage", "official_forum_url", "announcement_url"] - for site in sites: - websites_dct[site] = filter_list(links.get(site)) - df = pd.Series(websites_dct).to_frame().reset_index() - df.columns = ["Metric", "Value"] - df["Value"] = df["Value"].apply(lambda x: ",".join(x)) - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - return df[df["Value"].notna()] - - @log_start_end(log=logging) - def get_categories(self) -> Union[Dict[Any, Any], List[Any]]: - """Coins categories. [Source: CoinGecko] - - Returns - ---------- - Union[Dict[Any, Any], List[Any]] - Coin categories - """ - - return self.coin.get("categories", {}) - - @log_start_end(log=logger) - def _get_base_market_data_info(self) -> Union[Dict[str, Any], Any]: - """Helper method that fetches all the base market/price information about given coin. [Source: CoinGecko] - - Returns - ---------- - Dict[str, Any] - All market related information for given coin - """ - market_dct = {} - market_data = self.coin.get("market_data", {}) - for stat in [ - "total_supply", - "max_supply", - "circulating_supply", - "price_change_percentage_24h", - "price_change_percentage_7d", - "price_change_percentage_30d", - ]: - market_dct[stat] = market_data.get(stat) - prices = create_dictionary_with_prefixes( - ["current_price"], market_data, DENOMINATION - ) - market_dct.update(prices) - return market_dct - - @log_start_end(log=logger) - def get_base_info(self) -> pd.DataFrame: - """Get all the base information about given coin. [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - Base information about coin - """ - - regx = r'
|' - - results = {} - for attr in BASE_INFO: - info_obj = self.coin.get(attr, {}) - if attr == "description": - info_obj = info_obj.get("en") - info_obj = re.sub(regx, "", info_obj) - info_obj = re.sub(r"\r\n\r\n", " ", info_obj) - results[attr] = info_obj - results.update(self._get_base_market_data_info()) - df = pd.Series(results).to_frame().reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - - return df[df["Value"].notna()] - - @log_start_end(log=logger) - def get_market_data(self) -> pd.DataFrame: - """Get all the base market information about given coin. [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - Base market information about coin - Metric,Value - """ - - market_data = self.coin.get("market_data", {}) - market_columns_denominated = [ - "market_cap", - "fully_diluted_valuation", - "total_volume", - "high_24h", - "low_24h", - ] - denominated_data = create_dictionary_with_prefixes( - market_columns_denominated, market_data, DENOMINATION - ) - - market_single_columns = [ - "market_cap_rank", - "total_supply", - "max_supply", - "circulating_supply", - "price_change_percentage_24h", - "price_change_percentage_7d", - "price_change_percentage_30d", - "price_change_percentage_60d", - "price_change_percentage_1y", - "market_cap_change_24h", - ] - single_stats = {col: market_data.get(col) for col in market_single_columns} - single_stats.update(denominated_data) - - if ( - (single_stats["total_supply"] is not None) - and (single_stats["circulating_supply"] is not None) - and (single_stats["total_supply"] != 0) - ): - single_stats["circulating_supply_to_total_supply_ratio"] = ( - single_stats["circulating_supply"] / single_stats["total_supply"] - ) - else: - single_stats["circulating_supply_to_total_supply_ratio"] = np.nan - df = pd.Series(single_stats).to_frame().reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - return df[df["Value"].notna()] - - @log_start_end(log=logger) - def get_all_time_high(self, currency: str = "usd") -> pd.DataFrame: - """Get all time high data for given coin. [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - All time high price data - Metric,Value - """ - - market_data = self.coin.get("market_data", {}) - if market_data == {}: - return pd.DataFrame() - ath_columns = [ - "current_price", - "ath", - "ath_date", - "ath_change_percentage", - ] - - results = {column: market_data[column].get(currency) for column in ath_columns} - df = pd.Series(results).to_frame().reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - df["Metric"] = df["Metric"].apply(lambda x: x.replace("Ath", "All Time High")) - df["Metric"] = df["Metric"] + f" {currency.upper()}" - return df[df["Value"].notna()] - - @log_start_end(log=logger) - def get_all_time_low(self, currency: str = "usd") -> pd.DataFrame: - """Get all time low data for given coin. [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - All time low price data - Metric,Value - """ - - market_data = self.coin.get("market_data", {}) - if market_data == {}: - return pd.DataFrame() - - ath_columns = [ - "current_price", - "atl", - "atl_date", - "atl_change_percentage", - ] - results = {column: market_data[column].get(currency) for column in ath_columns} - df = pd.Series(results).to_frame().reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - df["Metric"] = df["Metric"].apply(lambda x: x.replace("Atl", "All Time Low")) - df["Metric"] = df["Metric"] + f" {currency.upper()}" - return df[df["Value"].notna()] - - @log_start_end(log=logger) - def get_scores(self) -> pd.DataFrame: - """Get different kind of scores for given coin. [Source: CoinGecko] - - Returns - ---------- - pd.DataFrame - Social, community, sentiment scores for coin - Metric,Value - """ - - score_columns = [ - "coingecko_rank", - "coingecko_score", - "developer_score", - "community_score", - "liquidity_score", - "sentiment_votes_up_percentage", - "sentiment_votes_down_percentage", - "public_interest_score", - "community_data", - "public_interest_stats", - ] - - single_stats = {col: self.coin.get(col) for col in score_columns[:-2]} - nested_stats = {} - for col in score_columns[-2:]: - _dct = self.coin.get(col, {}) - for k, _ in _dct.items(): - nested_stats[k] = _dct.get(k, {}) - - single_stats.update(nested_stats) - df = pd.Series(single_stats).reset_index() - df.replace({0: ""}, inplace=True) - df = df.fillna("") - df.columns = ["Metric", "Value"] - - # pylint: disable=unsupported-assignment-operation - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ) - ) - return df[df["Value"].notna()] - - @log_start_end(log=logger) - def get_coin_market_chart( - self, vs_currency: str = "usd", days: int = 30, **kwargs: Any - ) -> pd.DataFrame: - """Get prices for given coin. [Source: CoinGecko] - - Parameters - ---------- - vs_currency: str - currency vs which display data - days: int - number of days to display the data - kwargs - - Returns - ---------- - pd.DataFrame - Prices for given coin - Columns: time, price, currency - """ - - prices = self.client.get_coin_market_chart_by_id( - self.coin_symbol, vs_currency, days, **kwargs - ) - prices = prices["prices"] - df = pd.DataFrame(data=prices, columns=["time", "price"]) - df["time"] = pd.to_datetime(df.time, unit="ms") - df = df.set_index("time") - df["currency"] = vs_currency - return df - - @log_start_end(log=logger) - def get_ohlc(self, vs_currency: str = "usd", days: int = 90) -> pd.DataFrame: - """Get Open, High, Low, Close prices for given coin. [Source: CoinGecko] - - Parameters - ---------- - vs_currency: str - currency vs which display data - days: int - number of days to display the data - on from (1/7/14/30/90/180/365, max) - - Returns - ---------- - pd.DataFrame - OHLC data for coin - Columns: time, price, currency - """ - - prices = self.client.get_coin_ohlc_by_id(self.coin_symbol, vs_currency, days) - df = pd.DataFrame(data=prices, columns=["time", "open", "high", "low", "close"]) - df["time"] = pd.to_datetime(df.time, unit="ms") - df = df.set_index("time") - df["currency"] = vs_currency - return df - - -@log_start_end(log=logger) -def get_ohlc(symbol: str, vs_currency: str = "usd", days: int = 90) -> pd.DataFrame: - """Get Open, High, Low, Close prices for given coin. [Source: CoinGecko] - - Parameters - ---------- - vs_currency: str - currency vs which display data - days: int - number of days to display the data - on from (1/7/14/30/90/180/365, max) - - Returns - ------- - pd.DataFrame - OHLC data for coin - Columns: time, price, currency - """ - client = CoinGeckoAPI() - prices = client.get_coin_ohlc_by_id(symbol, vs_currency, days) - df = pd.DataFrame(data=prices, columns=["date", "Open", "High", "Low", "Close"]) - df["date"] = pd.to_datetime(df.date, unit="ms") - df = df.set_index("date") - return df diff --git a/openbb_terminal/cryptocurrency/due_diligence/pycoingecko_view.py b/openbb_terminal/cryptocurrency/due_diligence/pycoingecko_view.py deleted file mode 100644 index 75c339d3941..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/pycoingecko_view.py +++ /dev/null @@ -1,384 +0,0 @@ -"""CoinGecko view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import openbb_terminal.cryptocurrency.due_diligence.pycoingecko_model as gecko -from openbb_terminal.cryptocurrency import cryptocurrency_helpers -from openbb_terminal.cryptocurrency.dataframe_helpers import wrap_text_in_df -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_coin_potential_returns( - to_symbol: str, - from_symbol: Optional[str] = None, - limit: Optional[int] = None, - price: Optional[int] = None, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing potential returns of a certain coin. [Source: CoinGecko] - - Parameters - ---------- - to_symbol : str - Coin loaded to check potential returns for (e.g., algorand) - from_symbol : str | None - Coin to compare main_coin with (e.g., bitcoin) - limit : int | None - Number of coins with highest market cap to compare main_coin with (e.g., 5) - price - Target price of main_coin to check potential returns (e.g., 5) - export : str - Export dataframe data to csv,json,xlsx file - """ - df = gecko.get_coin_potential_returns(to_symbol, from_symbol, limit, price) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Potential Coin Returns", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "prt", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_info( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing basic information about loaded coin. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency symbol - export : str - Export dataframe data to csv,json,xlsx file - """ - - cg_id = cryptocurrency_helpers.check_cg_id(symbol) - - if not cg_id: - return - - coin = gecko.Coin(cg_id) - - df = wrap_text_in_df(coin.get_base_info(), w=80) - df = df.applymap(lambda x: x if not isinstance(x, dict) else "") - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Basic Coin Information", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "info", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_web( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing found websites corresponding to loaded coin. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency symbol - export : str - Export dataframe data to csv,json,xlsx file - """ - - cg_id = cryptocurrency_helpers.check_cg_id(symbol) - - if not cg_id: - return - - coin = gecko.Coin(cg_id) - - df = coin.get_websites() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Websites for Loaded Coin", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "web", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_social( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing social media corresponding to loaded coin. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency - export : str - Export dataframe data to csv,json,xlsx file - """ - coin = gecko.Coin(symbol) - df = coin.get_social_media() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Social Media for Loaded Coin", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "social", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_dev( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing developers data for loaded coin. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency - export : str - Export dataframe data to csv,json,xlsx file - """ - coin = gecko.Coin(symbol) - - df = coin.get_developers_data() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Developers Data for Loaded Coin", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dev", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_ath( - symbol: str, - currency: str = "usd", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing all time high data for loaded coin. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency - currency: str - currency vs which coin ath will be displayed: usd or btc - export : str - Export dataframe data to csv,json,xlsx file - """ - coin = gecko.Coin(symbol) - - df = coin.get_all_time_high(currency=currency) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Coin Highs", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ath", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_atl( - symbol: str, - currency: str = "usd", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing all time low data for loaded coin. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency - currency: str - currency vs which coin ath will be displayed: usd or btc - export : str - Export dataframe data to csv,json,xlsx file - """ - coin = gecko.Coin(symbol) - - df = coin.get_all_time_low(currency=currency) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Coin Lows", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "atl", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_score( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing different kind of scores for loaded coin. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency - export : str - Export dataframe data to csv,json,xlsx file - """ - coin = gecko.Coin(symbol) - - df = coin.get_scores() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Different Scores for Loaded Coin", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "score", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_bc(symbol: str, export: str = "", sheet_name: Optional[str] = None) -> None: - """Prints table showing urls to blockchain explorers. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency - export : str - Export dataframe data to csv,json,xlsx file - """ - coin = gecko.Coin(symbol) - - df = coin.get_blockchain_explorers() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Blockchain URLs", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "bc", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_market( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Prints table showing market data for loaded coin. [Source: CoinGecko] - - Parameters - ---------- - symbol : str - Cryptocurrency - export : str - Export dataframe data to csv,json,xlsx file - """ - coin = gecko.Coin(symbol) - - df = coin.get_market_data() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Market Data", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "market", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/santiment_model.py b/openbb_terminal/cryptocurrency/due_diligence/santiment_model.py deleted file mode 100644 index dfcfd324fb0..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/santiment_model.py +++ /dev/null @@ -1,101 +0,0 @@ -import logging -from datetime import datetime, timedelta -from typing import Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.discovery.pycoingecko_model import read_file_data -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def get_slug(symbol: str) -> str: - """ - Get Santiment slug mapping and return corresponding slug for a given coin - """ - df = pd.DataFrame(read_file_data("santiment_slugs.json")) - - slug = df.loc[df["ticker"] == symbol.upper()]["slug"].values[0] - - return slug - - -@log_start_end(log=logger) -@check_api_key(["API_SANTIMENT_KEY"]) -def get_github_activity( - symbol: str, - dev_activity: bool = False, - interval: str = "1d", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Returns a list of developer activity for a given coin and time interval. - - [Source: https://santiment.net/] - - Parameters - ---------- - symbol : str - Crypto symbol to check github activity - dev_activity: bool - Whether to filter only for development activity - interval : str - Interval frequency (e.g., 1d) - start_date : Optional[str] - Initial date like string (e.g., 2021-10-01) - end_date : Optional[str] - End date like string (e.g., 2021-10-01) - - Returns - ------- - pd.DataFrame - developer activity over time - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=365)).strftime( - "%Y-%m-%dT%H:%M:%SZ" - ) - - if end_date is None: - end_date = (datetime.now()).strftime("%Y-%m-%dT%H:%M:%SZ") - - activity_type = "dev_activity" if dev_activity else "github_activity" - - slug = get_slug(symbol) - - headers = { - "Content-Type": "application/graphql", - "Authorization": f"Apikey {get_current_user().credentials.API_SANTIMENT_KEY}", - } - - data = ( - f'\n{{ getMetric(metric: "{activity_type}"){{ timeseriesData( slug: "{slug}"' - f'from: "{start_date}" to: "{end_date}" interval: "{interval}"){{ datetime value }} }} }}' - ) - - response = request( - "https://api.santiment.net/graphql", method="POST", headers=headers, data=data - ) - - df = pd.DataFrame() - - if response.status_code == 200: - if "getMetric" in response.json()["data"]: - df = pd.DataFrame(response.json()["data"]["getMetric"]["timeseriesData"]) - df["datetime"] = pd.to_datetime(df["datetime"]) - df = df.set_index("datetime") - else: - console.print(f"Could not find github activity found for {symbol}\n") - - elif response.status_code == 400: - if "Apikey" in response.json()["errors"]["details"]: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(f"Error in request: {response.json()['error']}", "\n") - - return df diff --git a/openbb_terminal/cryptocurrency/due_diligence/santiment_view.py b/openbb_terminal/cryptocurrency/due_diligence/santiment_view.py deleted file mode 100644 index a2cff7528c1..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/santiment_view.py +++ /dev/null @@ -1,74 +0,0 @@ -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.due_diligence.santiment_model import ( - get_github_activity, -) -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_SANTIMENT_KEY"]) -def display_github_activity( - symbol: str, - start_date: Optional[str] = None, - dev_activity: bool = False, - end_date: Optional[str] = None, - interval: str = "1d", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Returns a list of github activity for a given coin and time interval. - - [Source: https://santiment.net/] - - Parameters - ---------- - symbol : str - Crypto symbol to check github activity - dev_activity: bool - Whether to filter only for development activity - start_date : Optional[str] - Initial date like string (e.g., 2021-10-01) - end_date : Optional[str] - End date like string (e.g., 2021-10-01) - interval : str - Interval frequency (some possible values are: 1h, 1d, 1w) - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = get_github_activity( - symbol=symbol, - dev_activity=dev_activity, - start_date=start_date, - end_date=end_date, - interval=interval, - ) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title=f"{symbol}'s Activity count") - fig.set_title(f"{symbol}'s Github activity over time") - - fig.add_scatter(x=df.index, y=df["value"], name=f"{symbol}'s Activity count") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gh", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/due_diligence/sdk_helper.py b/openbb_terminal/cryptocurrency/due_diligence/sdk_helper.py deleted file mode 100644 index 151903ba5fb..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/sdk_helper.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Crypto DD SDK helper""" - -__docformat__ = "numpy" - -import pandas as pd - -import openbb_terminal.cryptocurrency.due_diligence.pycoingecko_model as gecko - - -def dev_stats(symbol: str) -> pd.DataFrame: - """Get developer stats for a coin - - Parameters - ---------- - symbol : str - Coin to get stats for - - Returns - ------- - pd.DataFrame - Dataframe of stats - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> btc_dev_stats = openbb.crypto.dd.dev("btc") - """ - coin = gecko.Coin(symbol) - return coin.get_developers_data() - - -def score(symbol: str) -> pd.DataFrame: - """Get scores for a coin from CoinGecko - - Parameters - ---------- - symbol : str - Coin to get scores for - - Returns - ------- - pd.DataFrame - Dataframe of scores - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> btc_scores = openbb.crypto.dd.score("btc") - """ - return gecko.Coin(symbol).get_scores() - - -def social(symbol: str) -> pd.DataFrame: - """Get social media stats for a coin - - Parameters - ---------- - symbol : str - Coin to get social stats for - - Returns - ------- - pd.DataFrame - Dataframe of social stats - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> btc_socials = openbb.crypto.dd.social("btc") - """ - return gecko.Coin(symbol).get_social_media() - - -def ath(symbol: str, currency: str = "USD") -> pd.DataFrame: - """Get all time high for a coin in a given currency - - Parameters - ---------- - symbol : str - Coin to get all time high for - currency: str - Currency to get all time high in - - Returns - ------- - pd.DataFrame - Dataframe of all time high - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> btc_ath = openbb.crypto.dd.ath("btc") - """ - return gecko.Coin(symbol).get_all_time_high(currency=currency.lower()) - - -def atl(symbol: str, currency: str = "USD") -> pd.DataFrame: - """Get all time low for a coin in a given currency - - Parameters - ---------- - symbol : str - Coin to get all time low for - currency: str - Currency to get all time low in - - Returns - ------- - pd.DataFrame - Dataframe of all time low - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> btc_atl = openbb.crypto.dd.atl("btc") - """ - return gecko.Coin(symbol).get_all_time_low(currency=currency.lower()) diff --git a/openbb_terminal/cryptocurrency/nft/__init__.py b/openbb_terminal/cryptocurrency/nft/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/cryptocurrency/nft/nft_controller.py b/openbb_terminal/cryptocurrency/nft/nft_controller.py deleted file mode 100644 index b8b1a388e94..00000000000 --- a/openbb_terminal/cryptocurrency/nft/nft_controller.py +++ /dev/null @@ -1,169 +0,0 @@ -import argparse -import logging -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.nft import ( - nftpricefloor_model, - nftpricefloor_view, - opensea_view, -) -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import EXPORT_ONLY_RAW_DATA_ALLOWED -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -# flake8: noqa - - -logger = logging.getLogger(__name__) - - -class NFTController(BaseController): - """NFT Controller class""" - - CHOICES_COMMANDS = [ - "stats", - "collections", - "fp", - ] - PATH = "/crypto/nft/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.nft_price_floor_collections = nftpricefloor_model.get_collection_slugs() - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - choices["fp"].update({c: {} for c in self.nft_price_floor_collections}) - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("crypto/nft/", 70) - mt.add_cmd("stats") - mt.add_cmd("fp") - mt.add_cmd("collections") - console.print(text=mt.menu_text, menu="Cryptocurrency - Non Fungible Token") - - @log_start_end(log=logger) - def call_fp(self, other_args: List[str]): - """Process fp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fp", - description=""" - Display floor price of a certain NFT collection. - [Source: https://nftpricefloor.com/] - """, - ) - - parser.add_argument( - "-s", - "--slug", - type=str, - help="NFT floor price collection slug (e.g., bored-ape-yacht-club)", - dest="slug", - required="-h" not in other_args, - choices=self.nft_price_floor_collections, - metavar="SLUG", - ) - if other_args and not other_args[0][0] == "-": - other_args.insert(0, "--slug") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, raw=True, limit=10 - ) - - if ns_parser: - nftpricefloor_view.display_floor_price( - slug=ns_parser.slug, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - raw=ns_parser.raw, - limit=ns_parser.limit, - ) - - @log_start_end(log=logger) - def call_stats(self, other_args: List[str]): - """Process stats command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="info", - description=""" - Display stats about an opensea nft collection. e.g. alien-frens - [Source: https://opensea.io] - """, - ) - - parser.add_argument( - "-s", - "--slug", - type=str, - help="Opensea collection slug (e.g., mutant-ape-yacht-club)", - dest="slug", - required="-h" not in other_args, - ) - if other_args and not other_args[0][0] == "-": - other_args.insert(0, "--slug") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - opensea_view.display_collection_stats( - slug=ns_parser.slug, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_collections(self, other_args: List[str]): - """Process collections command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="collections", - description="NFT Collections [Source: https://nftpricefloor.com/]", - ) - parser.add_argument( - "--fp", - dest="fp", - action="store_true", - default=False, - help="Flag to display floor price over time for top collections", - ) - parser.add_argument( - "--sales", - dest="sales", - action="store_true", - default=False, - help="Flag to display sales over time for top collections", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=5 - ) - if ns_parser: - nftpricefloor_view.display_collections( - show_sales=ns_parser.sales, - show_fp=ns_parser.fp, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/cryptocurrency/nft/nftpricefloor_model.py b/openbb_terminal/cryptocurrency/nft/nftpricefloor_model.py deleted file mode 100644 index 8d0dab2295a..00000000000 --- a/openbb_terminal/cryptocurrency/nft/nftpricefloor_model.py +++ /dev/null @@ -1,71 +0,0 @@ -""" NFT Price Floor Model """ - -import logging -from typing import List - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - -API_URL = "https://api-bff.nftpricefloor.com" - - -@log_start_end(log=logger) -def get_collection_slugs() -> List[str]: - df = get_collections() - if not df.empty and "slug" in df.columns: - return df["slug"].values.tolist() - return [] - - -@log_start_end(log=logger) -def get_collections() -> pd.DataFrame: - """Get nft collections [Source: https://nftpricefloor.com/] - - Parameters - ---------- - - Returns - ------- - pd.DataFrame - nft collections - """ - res = request(f"{API_URL}/projects", timeout=10) - if res.status_code == 200: - data = res.json() - df = pd.DataFrame(data) - df_stats = pd.json_normalize(df["stats"]) - df = pd.concat([df, df_stats], axis=1) - return df - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_floor_price(slug: str) -> pd.DataFrame: - """Get nft collections [Source: https://nftpricefloor.com/] - - Parameters - ---------- - slug: str - nft collection slug - - Returns - ------- - pd.DataFrame - nft collections - """ - res = request(f"{API_URL}/projects/{slug}/charts/all", timeout=10) - if res.status_code == 200: - data = res.json() - df = pd.DataFrame( - data, columns=["timestamps", "floorEth", "volumeEth", "salesCount"] - ) - df = df.set_index("timestamps") - df.index = pd.to_datetime(df.index * 1_000_000) - df.index = pd.DatetimeIndex(df.index.rename("date").strftime("%Y-%m-%d")) - df = df.reset_index().drop_duplicates(subset=["date"]) - return df.set_index("date") - return pd.DataFrame() diff --git a/openbb_terminal/cryptocurrency/nft/nftpricefloor_view.py b/openbb_terminal/cryptocurrency/nft/nftpricefloor_view.py deleted file mode 100644 index a2dc78dd3c0..00000000000 --- a/openbb_terminal/cryptocurrency/nft/nftpricefloor_view.py +++ /dev/null @@ -1,173 +0,0 @@ -""" NFT Price Floor View """ - -__docformat__ = "numpy" - -# flake8: noqa - -import logging -import os -from typing import Optional, Union -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.nft import nftpricefloor_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_collections( - show_fp: bool = False, - show_sales: bool = False, - limit: int = 5, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display NFT collections. [Source: https://nftpricefloor.com/] - - Parameters - ---------- - show_fp : bool - Show NFT Price Floor for top collections - limit: int - Number of NFT collections to display - export : str - Export dataframe data to csv,json,xlsx file - """ - fig = OpenBBFigure() - df = nftpricefloor_model.get_collections() - - if df.empty: - return console.print("No data found.", "\n") - - df = df[ - [ - "slug", - "floorInfo.currentFloorEth", - "totalSupply", - "listedCount", - "blockchain", - ] - ] - - if show_fp or show_sales: - fig = OpenBBFigure(yaxis_title="Floor Price [ETH]" if show_fp else "Sales") - fig.set_title("Collections Floor Price" if show_fp else "Collections Sales") - for collection in df["slug"].head(limit).values: - df_collection = nftpricefloor_model.get_floor_price(collection) - df_collection.index = pd.DatetimeIndex( - df_collection.index.rename("date").strftime("%Y-%m-%d") - ) - if not df_collection.empty: - values = ( - df_collection["floorEth"].values - if show_fp - else df_collection["salesCount"].values - ) - fig.add_scatter( - x=df_collection.index, y=values, mode="lines", name=collection - ) - - if not fig.is_image_export(export): - fig.show() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="NFT Collections", - export=bool(export), - limit=limit, - ) - - return export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "collections", - df, - sheet_name, - fig, - ) - - -@log_start_end(log=logger) -def display_floor_price( - slug: str, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, - raw: bool = False, -) -> Union[None, OpenBBFigure]: - """Display NFT collection floor price over time. [Source: https://nftpricefloor.com/] - - Parameters - ---------- - slug: str - NFT collection slug - raw: bool - Flag to display raw data - limit: int - Number of raw data to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = nftpricefloor_model.get_floor_price(slug) - if df.empty: - return console.print("No data found.", "\n") - - if raw: - print_rich_table( - df, - index_name="date", - headers=list(df.columns), - show_index=True, - title=f"{slug} Floor Price", - export=bool(export), - limit=limit, - ) - - fig = OpenBBFigure.create_subplots( - 1, 1, shared_yaxes=False, specs=[[{"secondary_y": True}]] - ) - fig.set_title(f"{slug} Floor Price") - fig.set_yaxis_title("Floor Price [ETH]", secondary_y=False) - fig.set_yaxis_title("Sales", side="left", secondary_y=True) - fig.set_xaxis_title("Date") - - fig.add_bar( - x=df.index, - y=df["salesCount"], - name="Sales", - marker_color=theme.down_color, - secondary_y=True, - ) - fig.add_scatter( - x=df.index, - y=df["floorEth"], - name="Floor Price", - mode="lines", - line_color=theme.up_color, - secondary_y=False, - ) - fig.update_layout( - legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1) - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fp", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/cryptocurrency/nft/opensea_model.py b/openbb_terminal/cryptocurrency/nft/opensea_model.py deleted file mode 100644 index d6e6c46ca83..00000000000 --- a/openbb_terminal/cryptocurrency/nft/opensea_model.py +++ /dev/null @@ -1,80 +0,0 @@ -""" opensea.io Model """ - -import logging -from datetime import datetime - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - -API_URL = "https://api.opensea.io/api/v1" - - -@log_start_end(log=logger) -def get_collection_stats(slug: str) -> pd.DataFrame: - """Get stats of a nft collection [Source: opensea.io] - - Parameters - ---------- - slug : str - Opensea collection slug. If the name of the collection is Mutant Ape Yacht Club the slug is mutant-ape-yacht-club - - Returns - ------- - pd.DataFrame - collection stats - """ - res = request(f"{API_URL}/collection/{slug}") - if res.status_code == 200: - data = res.json() - collection = data["collection"] - stats = collection["stats"] - metrics = [ - "Name", - "Floor Price (ETH)", - "Number of Owners", - "Market Cap (ETH)", - "Average Price ETH", - "One day volume (ETH)", - "One day change (%)", - "One day sales (ETH)", - "One day average price (ETH)", - "Thirty day volume (ETH)", - "Thirty day change (%)", - "Thirty day sales (ETH)", - "Thirty day average price (ETH)", - "Total Supply (ETH)", - "Total Sales (ETH)", - "Total Volume (ETH)", - "Creation Date", - "URL", - ] - # T his variable is here because sometimes the created dates also have - # milliseconds in the string and we dont it, so this just gets the m,d,y,h,m,s - created_date = collection["created_date"][0:19] - values = [ - collection["name"], - "-" if not stats["floor_price"] else float(stats["floor_price"]), - round(float(stats["num_owners"]), 2), - round(float(stats["market_cap"]), 2), - round(float(stats["average_price"]), 2), - round(float(stats["one_day_volume"]), 2), - round(float(stats["one_day_change"]) * 100, 2), - round(float(stats["one_day_sales"]), 2), - round(float(stats["one_day_average_price"]), 2), - round(float(stats["thirty_day_volume"]), 2), - round(float(stats["thirty_day_change"]) * 100, 2), - round(float(stats["thirty_day_sales"]), 2), - round(float(stats["thirty_day_average_price"]), 2), - round(float(stats["total_supply"]), 2), - round(float(stats["total_sales"]), 2), - round(float(stats["total_volume"]), 2), - datetime.strptime(created_date, "%Y-%m-%dT%H:%M:%S").strftime("%b %d, %Y"), - "-" if not collection["external_url"] else collection["external_url"], - ] - df = pd.DataFrame({"Metric": metrics, "Value": values}) - return df - return pd.DataFrame() diff --git a/openbb_terminal/cryptocurrency/nft/opensea_view.py b/openbb_terminal/cryptocurrency/nft/opensea_view.py deleted file mode 100644 index b75ddf63fd8..00000000000 --- a/openbb_terminal/cryptocurrency/nft/opensea_view.py +++ /dev/null @@ -1,45 +0,0 @@ -""" opensea.io View """ - -import logging -import os - -from openbb_terminal.cryptocurrency.nft.opensea_model import get_collection_stats -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_collection_stats(slug: str, export: str, sheet_name: str): - """Prints table showing collection stats. [Source: opensea.io] - - Parameters - ---------- - slug: str - Opensea collection slug. - If the name of the collection is Mutant Ape Yacht Club the slug is mutant-ape-yacht-club - export : str - Export dataframe data to csv,json,xlsx file - """ - - collection_stats_df = get_collection_stats(slug) - if collection_stats_df.empty: - console.print("No data found.", "\n") - else: - print_rich_table( - collection_stats_df, - headers=list(collection_stats_df.columns), - show_index=False, - title="Collection Stats", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "stats", - collection_stats_df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/onchain/__init__.py b/openbb_terminal/cryptocurrency/onchain/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/cryptocurrency/onchain/bitquery_model.py b/openbb_terminal/cryptocurrency/onchain/bitquery_model.py deleted file mode 100644 index 7fb48fb8f60..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/bitquery_model.py +++ /dev/null @@ -1,823 +0,0 @@ -"""BitQuery model""" - -__docformat__ = "numpy" - -import datetime -import json -import logging -import os -from typing import Optional - -import numpy as np -import pandas as pd -import requests -from requests.adapters import HTTPAdapter -from requests.exceptions import HTTPError - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.dataframe_helpers import prettify_column_names -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -class BitQueryApiKeyException(Exception): - """Bit Query Api Key Exception object""" - - @log_start_end(log=logger) - def __init__(self, message: str): - super().__init__(message) - self.message = message - - @log_start_end(log=logger) - def __str__(self) -> str: - return f"BitQueryApiKeyException: {self.message}" - - -class BitQueryTimeoutException(Exception): - """BitQuery Timeout Exception class""" - - -BQ_URL = "https://graphql.bitquery.io" -CURRENCIES = ["ETH", "USD", "BTC", "USDT"] -LT_FILTERS = ["exchange", "trades", "tradeAmount"] -LT_KIND = ["dex", "time"] -INTERVALS = ["day", "month", "week"] -DVCP_FILTERS = [ - "date", - "exchange", - "base", - "quote", - "open", - "high", - "low", - "close", - "tradeAmount", - "trades", -] -UEAT_FILTERS = [ - "date", - "uniqueSenders", - "transactions", - "averageGasPrice", - "mediumGasPrice", - "maximumGasPrice", -] -TTCP_FILTERS = ["base", "quoted", "trades", "tradeAmount"] -BAAS_FILTERS = [ - "date", - "baseCurrency", - "quoteCurrency", - "dailySpread", - "averageBidPrice", - "averageAskPrice", -] -DECENTRALIZED_EXCHANGES = [ - "1inch", - "AfroDex", - "AirSwap", - "Amplbitcratic", - "Balancer", - "BestSwap", - "Bitox", - "CellSwap", - "Cellswap", - "Cofix", - "Coinchangex", - "Curve", - "DDEX", - "DUBIex", - "DecentrEx", - "DeversiFi", - "Dodo", - "ETHERCExchange", - "EtherBlockchain", - "EtherDelta", - "Ethernext", - "Ethfinex", - "FEGex", - "FFFSwap", - "Fordex", - "GUDecks", - "GUDeks", - "HiSwap", - "IDEX", - "LedgerDex", - "Matcha", - "Miniswap", - "Mooniswap", - "Oasis", - "OpenRelay", - "S.Finance", - "SakeSwap", - "SeedDex", - "SingularX", - "StarBitEx", - "SushiSwap", - "SwapX", - "SwitchDex", - "TacoSwap", - "TokenJar", - "TokenStore", - "TokenTrove", - "Tokenlon", - "TradexOne", - "Uniswap", - "ZeusSwap", - "dYdX", - "dex.blue", -] -DECENTRALIZED_EXCHANGES_MAP = {e.lower(): e for e in DECENTRALIZED_EXCHANGES} -NETWORKS = ["bsc", "ethereum", "matic"] - - -@log_start_end(log=logger) -def _extract_dex_trades(data: dict) -> pd.DataFrame: - """Helper method that extracts from bitquery api response data from nested dictionary: - response = {'ethereum' : {'dexTrades' : }}. If 'dexTrades' is None, raises Exception. - - Parameters - ---------- - data: dict - response data from bitquery api. - - Returns - ------- - pd.DataFrame - normalized pandas data frame with data - """ - - dex_trades = data["ethereum"]["dexTrades"] - if not dex_trades: - raise ValueError("No data was returned in request response\n") - return pd.json_normalize(dex_trades) - - -@log_start_end(log=logger) -@check_api_key(["API_BITQUERY_KEY"]) -def query_graph(url: str, query: str) -> dict: - """Helper methods for querying graphql api. [Source: https://bitquery.io/] - - Parameters - ---------- - url: str - Endpoint url - query: str - Graphql query - - Returns - ------- - dict - Dictionary with response data - """ - - session = requests.Session() - session.mount("https://", HTTPAdapter(max_retries=5)) - headers = {"x-api-key": get_current_user().credentials.API_BITQUERY_KEY} # type: ignore - timeout = 30 - - try: - response = session.post( - url, json={"query": query}, headers=headers, timeout=timeout - ) - except requests.Timeout as e: - logger.exception("BitQuery timeout") - raise BitQueryTimeoutException( - f"BitQuery API didn't respond within {timeout} seconds." - ) from e - - if response.status_code == 500: - raise HTTPError(f"Internal sever error {response.reason}") # type: ignore - - if not 200 <= response.status_code < 300: - raise BitQueryApiKeyException( - f"Invalid Authentication: {response.status_code}. " - f"Please visit https://bitquery.io/pricing and generate you free api key" - ) - try: - data = response.json() - if "error" in data: - raise ValueError(f"Invalid Response: {data['error']}") - except Exception as e: - logger.exception("Invalid Response: %s", str(e)) - raise ValueError(f"Invalid Response: {response.text}") from e - return data["data"] - - -@log_start_end(log=logger) -def get_erc20_tokens() -> pd.DataFrame: - """Helper method that loads ~1500 most traded erc20 token. - [Source: json file] - - Returns - ------- - pd.DataFrame - ERC20 tokens with address, symbol and name - """ - file_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "data", "erc20_coins.json" - ) - with open(file_path) as f: - data = json.load(f) - df = pd.json_normalize(data) - df.columns = ["count", "address", "symbol", "name"] - df = df[~df["symbol"].isin(["", None, np.NaN])] - return df[["name", "symbol", "address", "count"]] - - -@log_start_end(log=logger) -def find_token_address(symbol: str) -> Optional[str]: - """Helper methods that search for ERC20 coin base on provided symbol or token address. - If erc20 token address is provided, then checks if it's proper address and returns it back. - In other case mapping data is loaded from file, and lookup for belonging token address. - - Parameters - ---------- - symbol: str - ERC20 token symbol e.g. UNI, SUSHI, ETH, WBTC or token address e.g. - 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 - - Returns - ------- - str or None - ERC20 token address, or None if nothing found. - """ - - if symbol.startswith("0x") and len(symbol) >= 38: - return symbol - - if symbol == "ETH": - return symbol - - token = "WBTC" if symbol == "BTC" else symbol - tokens_map: pd.DataFrame = get_erc20_tokens() - - found_token = tokens_map.loc[tokens_map["symbol"] == token] - if found_token.empty: - return None - if len(found_token) > 1: - return found_token.sort_values(by="count", ascending=False).iloc[0]["address"] - return found_token.iloc[0]["address"] - - -@log_start_end(log=logger) -def get_dex_trades_by_exchange( - trade_amount_currency: str = "USD", - limit: int = 90, - sortby: str = "tradeAmount", - ascend: bool = True, -) -> pd.DataFrame: - """Get trades on Decentralized Exchanges aggregated by DEX [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - trade_amount_currency: str - Currency of displayed trade amount. Default: USD - limit: int - Last n days to query data. Maximum 365 (bigger numbers can cause timeouts - on server side) - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Trades on Decentralized Exchanges aggregated by DEX - """ - - dt = (datetime.date.today() - datetime.timedelta(min(limit, 365))).strftime( - "%Y-%m-%d" - ) - - if trade_amount_currency not in CURRENCIES: - trade_amount_currency = "USD" - - query = f""" - {{ - ethereum {{ - dexTrades(options: {{limit: 40, desc: ["count"]}} - date: {{since: "{dt}"}} - ) {{ - exchange {{ - name - }} - count - tradeAmount(in: {trade_amount_currency}) - }} - }} - }} - """ - - try: - data = query_graph(BQ_URL, query) - except BitQueryApiKeyException: - logger.exception("Invalid API Key") - console.print("[red]Invalid API Key[/red]\n") - return pd.DataFrame() - - if not data: - return pd.DataFrame() - - df = _extract_dex_trades(data) - df.columns = ["trades", "tradeAmount", "exchange"] - df = df[~df["exchange"].isin([None, np.NaN, ""])] - df = df[["exchange", "trades", "tradeAmount"]].sort_values( - by="tradeAmount", ascending=True - ) - df = df.sort_values(by=sortby, ascending=ascend) - df.columns = prettify_column_names(df.columns) - return df - - -@log_start_end(log=logger) -def get_dex_trades_monthly( - trade_amount_currency: str = "USD", limit: int = 90, ascend: bool = True -) -> pd.DataFrame: - """Get list of trades on Decentralized Exchanges monthly aggregated. - [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - trade_amount_currency: str - Currency of displayed trade amount. Default: USD - limit: int - Last n days to query data. Maximum 365 (bigger numbers can cause timeouts - on server side) - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Trades on Decentralized Exchanges monthly aggregated - """ - - if trade_amount_currency not in CURRENCIES: - trade_amount_currency = "USD" - - dt = (datetime.date.today() - datetime.timedelta(min(limit, 365))).strftime( - "%Y-%m-%d" - ) - - query = f""" - {{ - ethereum {{ - dexTrades( - options: {{desc: ["date.year", "date.month", "count"]}} - date: {{since: "{dt}"}} - ) {{ - count - date {{ - month - year - }} - tradeAmount(in: {trade_amount_currency}) - }} - }} - }} - """ - try: - data = query_graph(BQ_URL, query) - except BitQueryApiKeyException: - logger.exception("Invalid API Key") - console.print("[red]Invalid API Key[/red]\n") - return pd.DataFrame() - if not data: - return pd.DataFrame() - - df = _extract_dex_trades(data) - df["date"] = df.apply( - lambda x: datetime.date(int(x["date.year"]), int(x["date.month"]), 1), axis=1 - ) - df.rename(columns={"count": "trades"}, inplace=True) - df = df[["date", "trades", "tradeAmount"]] - df = df.sort_values(by="date", ascending=ascend) - df.columns = prettify_column_names(df.columns) - return df - - -@log_start_end(log=logger) -def get_daily_dex_volume_for_given_pair( - limit: int = 100, - symbol: str = "UNI", - to_symbol: str = "USDT", - sortby: str = "date", - ascend: bool = True, -) -> pd.DataFrame: - """Get daily volume for given pair [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - limit: int - Last n days to query data - symbol: str - ERC20 token symbol - to_symbol: str - Quote currency. - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Daily volume for given pair - """ - - dt = (datetime.date.today() - datetime.timedelta(min(limit, 365))).strftime( - "%Y-%m-%d" - ) - - base, quote = find_token_address(symbol), find_token_address(to_symbol) - if not base or not quote: - raise ValueError("Provided coin or quote currency doesn't exist\n") - - query = f""" - {{ - ethereum(network: ethereum) {{ - dexTrades( - options: {{desc: ["timeInterval.day", "trades"]}} - baseCurrency: {{is: "{base}"}} - quoteCurrency: {{is: "{quote}"}} - date: {{since: "{dt}" }} - ) {{ - timeInterval {{ - day(count: 1) - }} - baseCurrency {{ - symbol - }} - quoteCurrency {{ - symbol - }} - exchange {{ - fullName - }} - trades: count - tradeAmount(in: USD) - quotePrice - maximum_price: quotePrice(calculate: maximum) - minimum_price: quotePrice(calculate: minimum) - open_price: minimum(of: block, get: quote_price) - close_price: maximum(of: block, get: quote_price) - }} - }} - }} - """ - - try: - data = query_graph(BQ_URL, query) - except BitQueryApiKeyException: - logger.exception("Invalid API Key") - console.print("[red]Invalid API Key[/red]\n") - return pd.DataFrame() - - if not data: - return pd.DataFrame() - - df = _extract_dex_trades(data) - df.columns = [ - "trades", - "tradeAmount", - "price", - "high", - "low", - "open", - "close", - "date", - "base", - "quote", - "exchange", - ] - df = df[ - [ - "date", - "exchange", - "base", - "quote", - "open", - "high", - "low", - "close", - "tradeAmount", - "trades", - ] - ] - df = df.sort_values(by=sortby, ascending=ascend) - df.columns = prettify_column_names(df.columns) - return df - - -@log_start_end(log=logger) -def get_token_volume_on_dexes( - symbol: str = "UNI", - trade_amount_currency: str = "USD", - sortby: str = "tradeAmount", - ascend: bool = True, -) -> pd.DataFrame: - """Get token volume on different Decentralized Exchanges. [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - symbol: str - ERC20 token symbol. - trade_amount_currency: str - Currency to display trade amount in. - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Token volume on Decentralized Exchanges - """ - - if trade_amount_currency not in CURRENCIES: - trade_amount_currency = "USD" - - token_address = find_token_address(symbol) - if token_address is None: - raise ValueError(f"Couldn't find token with symbol {symbol}\n") - query = f""" - {{ - ethereum {{ - dexTrades( - baseCurrency: {{is:"{token_address}"}} - ) {{ - baseCurrency{{ - symbol - }} - exchange {{ - name - fullName - }} - count - tradeAmount(in: {trade_amount_currency}) - - }} - }} - }} - - """ - try: - data = query_graph(BQ_URL, query) - except BitQueryApiKeyException: - logger.exception("Invalid API Key") - console.print("[red]Invalid API Key[/red]\n") - return pd.DataFrame() - - if not data: - return pd.DataFrame() - - df = _extract_dex_trades(data)[["exchange.fullName", "tradeAmount", "count"]] - df.columns = LT_FILTERS - df = df[~df["exchange"].str.startswith("<")] - df = df.sort_values(by=sortby, ascending=ascend) - df.columns = prettify_column_names(df.columns) - return df - - -@log_start_end(log=logger) -def get_ethereum_unique_senders( - interval: str = "day", - limit: int = 90, - sortby: str = "tradeAmount", - ascend: bool = True, -) -> pd.DataFrame: - """Get number of unique ethereum addresses which made a transaction in given time interval. - - Parameters - ---------- - interval: str - Time interval in which count unique ethereum addresses which made transaction. day, - month or week. - limit: int - Number of records for data query. - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Unique ethereum addresses which made a transaction - """ - - intervals = { - "day": 1, - "month": 30, - "week": 7, - } - - if interval not in intervals: - interval = "day" - - days = min(limit * intervals[interval], 90) - - dt = (datetime.date.today() - datetime.timedelta(days)).strftime("%Y-%m-%d") - - query = f""" - {{ - ethereum(network: ethereum) {{ - transactions(options: {{desc: "date.date"}}, date: {{since: "{dt}"}}) {{ - uniqueSenders: count(uniq: senders) - date {{ - date:startOfInterval(unit: {interval}) - }} - averageGasPrice: gasPrice(calculate: average) - mediumGasPrice: gasPrice(calculate: median) - maximumGasPrice: gasPrice(calculate: maximum) - transactions: count - }} - }} - }} - """ - - try: - data = query_graph(BQ_URL, query) - except BitQueryApiKeyException: - logger.exception("Invalid API Key") - console.print("[red]Invalid API Key[/red]\n") - return pd.DataFrame() - if not data: - return pd.DataFrame() - - df = pd.DataFrame(data["ethereum"]["transactions"]) - df["date"] = df["date"].apply(lambda x: x["date"]) - df = df[UEAT_FILTERS] - df = df.sort_values(by=sortby, ascending=ascend) - df.columns = prettify_column_names(df.columns) - return df - - -@log_start_end(log=logger) -def get_most_traded_pairs( - network: str = "ethereum", - exchange: str = "Uniswap", - limit: int = 90, - sortby: str = "tradeAmount", - ascend: bool = True, -) -> pd.DataFrame: - """Get most traded crypto pairs on given decentralized exchange in chosen time period. - [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - network: str - EVM network. One from list: bsc (binance smart chain), ethereum or matic - exchange: st - Decentralized exchange name - limit: int - Number of days taken into calculation account. - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Most traded crypto pairs on given decentralized exchange in chosen time period. - """ - - dt = (datetime.date.today() - datetime.timedelta(limit)).strftime("%Y-%m-%d") - exchange = DECENTRALIZED_EXCHANGES_MAP.get(exchange, "Uniswap") - query = f""" - {{ - ethereum(network: {network}){{ - dexTrades(options: {{limit: 100, desc: "tradeAmount"}}, - exchangeName: {{is: "{exchange}"}} - date: {{since: "{dt}"}}) {{ - buyCurrency {{ - symbol - }} - sellCurrency{{ - symbol - }} - trades: count - tradeAmount(in: USD) - }} - }} - }} - """ - try: - data = query_graph(BQ_URL, query) - except BitQueryApiKeyException: - logger.exception("Invalid API Key") - console.print("[red]Invalid API Key[/red]\n") - return pd.DataFrame() - if not data: - return pd.DataFrame() - - df = _extract_dex_trades(data) - df.columns = ["trades", "tradeAmount", "base", "quoted"] - df = df[TTCP_FILTERS] - df = df.sort_values(by=sortby, ascending=ascend) - df.columns = prettify_column_names(df.columns) - return df - - -@log_start_end(log=logger) -def get_spread_for_crypto_pair( - symbol: str = "WETH", - to_symbol: str = "USDT", - limit: int = 10, - sortby: str = "date", - ascend: bool = True, -) -> pd.DataFrame: - """Get an average bid and ask prices, average spread for given crypto pair for chosen time period. - [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - symbol: str - ERC20 token symbol - to_symbol: str - Quoted currency. - limit: int - Last n days to query data - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Average bid and ask prices, spread for given crypto pair for chosen time period - """ - - dt = (datetime.date.today() - datetime.timedelta(limit)).strftime("%Y-%m-%d") - base, quote = find_token_address(symbol), find_token_address(to_symbol) - - if not base or not quote: - raise ValueError("Provided coin or quote currency doesn't exist\n") - - query = f""" - {{ - ethereum(network: ethereum){{ - dexTrades( - date: {{since:"{dt}"}} - baseCurrency: {{is: "{base}"}}, - quoteCurrency: {{is: "{quote}"}}) {{ - date {{date}} - baseCurrency {{symbol}} - baseAmount - quoteCurrency {{ - symbol - }} - quoteAmount - trades: count - quotePrice - side - }} - }} - }} - """ - try: - data = query_graph(BQ_URL, query) - except BitQueryApiKeyException: - logger.exception("Invalid API Key") - console.print("[red]Invalid API Key[/red]\n") - return pd.DataFrame() - if not data: - return pd.DataFrame() - - df = _extract_dex_trades(data) - columns = ["quotePrice", "date.date", "baseCurrency.symbol", "quoteCurrency.symbol"] - bids = df.query("side == 'SELL'")[columns] - asks = df.query("side == 'BUY'")[columns] - - bids.columns = ["averageBidPrice", "date", "baseCurrency", "quoteCurrency"] - asks.columns = ["averageAskPrice", "date", "baseCurrency", "quoteCurrency"] - - daily_spread = pd.merge(asks, bids, on=["date", "baseCurrency", "quoteCurrency"]) - daily_spread["dailySpread"] = abs( - daily_spread["averageBidPrice"] - daily_spread["averageAskPrice"] - ) - df = daily_spread[ - [ - "date", - "baseCurrency", - "quoteCurrency", - "dailySpread", - "averageBidPrice", - "averageAskPrice", - ] - ] - df = df.sort_values(by=sortby, ascending=ascend) - df.columns = prettify_column_names(df.columns) - return df - - -@log_start_end(log=logger) -def get_possible_crypto_symbols(): - """Get possible crypto symbols.""" - return list(get_erc20_tokens()["symbol"].unique()) diff --git a/openbb_terminal/cryptocurrency/onchain/bitquery_view.py b/openbb_terminal/cryptocurrency/onchain/bitquery_view.py deleted file mode 100644 index 93fd77b86ee..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/bitquery_view.py +++ /dev/null @@ -1,415 +0,0 @@ -"""The BitQuery view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_very_long_number_formatter, - prettify_column_names, -) -from openbb_terminal.cryptocurrency.onchain import bitquery_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_BITQUERY_KEY"]) -def display_dex_trades( - trade_amount_currency: str = "USD", - kind: str = "dex", - limit: int = 20, - days: int = 90, - sortby: str = "tradeAmount", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing Trades on Decentralized Exchanges aggregated by DEX or Month - [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - kind: str - Aggregate trades by dex or time - trade_amount_currency: str - Currency of displayed trade amount. Default: USD - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - days: int - Last n days to query data. Maximum 365 (bigger numbers can cause timeouts - on server side) - export : str - Export dataframe data to csv,json,xlsx file - """ - - if kind == "time": - df = bitquery_model.get_dex_trades_monthly(trade_amount_currency, days, ascend) - else: - df = bitquery_model.get_dex_trades_by_exchange( - trade_amount_currency, days, sortby, ascend - ) - - if not df.empty: - df_data = df.copy() - - column_names = ["tradeAmount", "trades"] - column_names = prettify_column_names(column_names) - - df[column_names] = df[column_names].applymap( - lambda x: lambda_very_long_number_formatter(x) - ) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Trades on Decentralized Exchanges", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "lt", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_BITQUERY_KEY"]) -def display_daily_volume_for_given_pair( - symbol: str = "WBTC", - to_symbol: str = "USDT", - limit: int = 20, - sortby: str = "date", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing daily volume for given pair - [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - symbol: str - ERC20 token symbol or address - to_symbol: str - Quote currency. - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - - Returns - ------- - pd.DataFrame - Token volume on different decentralized exchanges - """ - - df = bitquery_model.get_daily_dex_volume_for_given_pair( - symbol=symbol, - to_symbol=to_symbol, - limit=limit, - sortby=sortby, - ascend=ascend, - ) - - if df.empty: - return - - df_data = df.copy() - - df[["Trade amount", "Trades"]] = df[["Trade amount", "Trades"]].applymap( - lambda x: lambda_very_long_number_formatter(x) - ) - # The -d command takes the place of what would normally be -l. This means - # we want to print out all of the data from each --day. If there is - # more exchange data per day then we will have more than -d amount of - # rows. If we do not change this value then only -d amount of rows will - # be printed out, not -d amount of days which is what we want. So we set - # this to an arbitrary amount to cover the potential for more than - # one row per day - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Daily Volume for Pair", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dvcp", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_BITQUERY_KEY"]) -def display_dex_volume_for_token( - symbol: str = "WBTC", - trade_amount_currency: str = "USD", - limit: int = 10, - sortby: str = "tradeAmount", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing token volume on different Decentralized Exchanges. - [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - symbol: str - ERC20 token symbol or address - trade_amount_currency: str - Currency of displayed trade amount. Default: USD - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - - Returns - ------- - pd.DataFrame - Token volume on different decentralized exchanges - """ - - df = bitquery_model.get_token_volume_on_dexes( - symbol=symbol, - trade_amount_currency=trade_amount_currency, - sortby=sortby, - ascend=ascend, - ) - if not df.empty: - df_data = df.copy() - - column_names = ["tradeAmount", "trades"] - column_names = prettify_column_names(column_names) - - df[column_names] = df[column_names].applymap( - lambda x: lambda_very_long_number_formatter(x) - ) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Token Volume on Exchanges", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "tv", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_BITQUERY_KEY"]) -def display_ethereum_unique_senders( - interval: str = "days", - limit: int = 10, - sortby: str = "date", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing number of unique ethereum addresses which made a transaction in given time interval - [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - interval: str - Time interval in which ethereum address made transaction. month, week or day - limit: int - Number of records to display. It's calculated base on provided interval. - If interval is month then calculation is made in the way: limit * 30 = time period, - in case if interval is set to week, then time period is calculated as limit * 7. - For better user experience maximum time period in days is equal to 90. - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - - Returns - ------- - pd.DataFrame - Number of unique ethereum addresses which made a transaction in given time interval - """ - - df = bitquery_model.get_ethereum_unique_senders(interval, limit, sortby, ascend) - if not df.empty: - column_names = ["uniqueSenders", "transactions", "maximumGasPrice"] - column_names = prettify_column_names(column_names) - - df[column_names] = df[column_names].applymap( - lambda x: lambda_very_long_number_formatter(x) - ) - - df_data = df.copy() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Unique Ethereum Addresses", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ueat", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_BITQUERY_KEY"]) -def display_most_traded_pairs( - exchange: str = "Uniswap", - days: int = 10, - limit: int = 10, - sortby: str = "tradeAmount", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing most traded crypto pairs on given decentralized exchange in chosen time period. - [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - exchange: str - Decentralized exchange name - days: int - Number of days taken into calculation account. - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - - Returns - ------- - pd.DataFrame - Most traded crypto pairs on given decentralized exchange in chosen time period. - """ - - df = bitquery_model.get_most_traded_pairs( - exchange=exchange, limit=days, sortby=sortby, ascend=ascend - ) - if not df.empty: - df_data = df.copy() - - column_names = ["tradeAmount", "trades"] - column_names = prettify_column_names(column_names) - - df[column_names] = df[column_names].applymap( - lambda x: lambda_very_long_number_formatter(x) - ) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Most Traded Crypto Pairs", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ttcp", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_BITQUERY_KEY"]) -def display_spread_for_crypto_pair( - symbol: str = "WETH", - to_symbol: str = "USDT", - limit: int = 10, - sortby: str = "date", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing an average bid and ask prices, average spread for given crypto pair for chosen - time period. [Source: https://graphql.bitquery.io/] - - Parameters - ---------- - symbol: str - ERC20 token symbol - to_symbol: str - Quoted currency. - limit: int - Last n days to query data - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - - Returns - ------- - pd.DataFrame - Average bid and ask prices, spread for given crypto pair for chosen time period - """ - - df = bitquery_model.get_spread_for_crypto_pair( - symbol=symbol, to_symbol=to_symbol, limit=limit, sortby=sortby, ascend=ascend - ) - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Average Spread for Given Crypto", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "baas", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/onchain/blockchain_model.py b/openbb_terminal/cryptocurrency/onchain/blockchain_model.py deleted file mode 100644 index abce44bab8a..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/blockchain_model.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Blockchain model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def _make_request(endpoint: str) -> dict: - """Helper method handles Blockchain API requests. [Source: https://api.blockchain.info/] - - Parameters - ---------- - endpoint: str - endpoint url - - Returns - ------- - dict: - dictionary with response data - """ - - url = f"https://api.blockchain.info/{endpoint}" - response = request( - url, - headers={ - "Accept": "application/json", - "User-Agent": "OBB", - "Accept-Encoding": "gzip", - }, - ) - if not 200 <= response.status_code < 300: - raise Exception(f"blockchain.info api exception: {response.text}") - try: - return response.json() - except Exception as e: - logger.exception("Invalid Response: %s", str(e)) - raise ValueError(f"Invalid Response: {response.text}") from e - - -@log_start_end(log=logger) -def _blockchain_data_api_make_request(endpoint: str) -> dict: - """Helper method handles Blockchain API requests. [Source: https://blockchain.info/] - - Parameters - ---------- - endpoint: str - endpoint url - Returns - ------- - dict: - dictionary with response data - """ - - url = f"https://blockchain.info/{endpoint}" - response = request( - url, - headers={ - "Accept": "application/json", - "User-Agent": "OBB", - "Accept-Encoding": "gzip", - }, - ) - if not 200 <= response.status_code < 300: - raise Exception(f"blockchain.info api exception: {response.text}") - try: - return response.json() - except Exception as e: - logger.exception("Invalid Response: %s", str(e)) - raise ValueError(f"Invalid Response: {response.text}") from e - - -@log_start_end(log=logger) -def get_btc_circulating_supply() -> pd.DataFrame: - """Returns BTC circulating supply [Source: https://api.blockchain.info/] - - Returns - ------- - pd.DataFrame - BTC circulating supply - """ - - data = _make_request( - "charts/total-bitcoins?timespan=all&sampled=true&metadata=false&cors=true&format=json" - )["values"] - df = pd.DataFrame(data) - df["x"] = df["x"] * 1_000_000_000 - df["x"] = pd.to_datetime(df["x"]) - return df - - -@log_start_end(log=logger) -def get_btc_confirmed_transactions() -> pd.DataFrame: - """Returns BTC confirmed transactions [Source: https://api.blockchain.info/] - - Returns - ------- - pd.DataFrame - BTC confirmed transactions - """ - - data = _make_request( - "charts/n-transactions?timespan=all&sampled=true&metadata=false&cors=true&format=json" - )["values"] - df = pd.DataFrame(data) - df["x"] = df["x"] * 1_000_000_000 - df["x"] = pd.to_datetime(df["x"]) - return df - - -@log_start_end(log=logger) -def get_btc_single_block(blockhash: str) -> pd.DataFrame: - """Returns BTC block data in json format. [Source: https://blockchain.info/] - Returns - ------- - pd.DataFrame - BTC single block - """ - - data = _blockchain_data_api_make_request(f"rawblock/{blockhash}?format=json") - - if data: - df = pd.json_normalize(data) - return df - - return pd.DataFrame() diff --git a/openbb_terminal/cryptocurrency/onchain/blockchain_view.py b/openbb_terminal/cryptocurrency/onchain/blockchain_view.py deleted file mode 100644 index 3cdf8e9310a..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/blockchain_view.py +++ /dev/null @@ -1,181 +0,0 @@ -"""Blockchain View""" - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.onchain import blockchain_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, - str_date_to_timestamp, -) - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_btc_circulating_supply( - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Returns BTC circulating supply [Source: https://api.blockchain.info/] - - Parameters - ---------- - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df = blockchain_model.get_btc_circulating_supply() - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - df = df[ - (df["x"] > datetime.fromtimestamp(ts_start_date)) - & (df["x"] < datetime.fromtimestamp(ts_end_date)) - ] - - fig = OpenBBFigure(yaxis_title="BTC") - fig.set_title("BTC Circulating Supply") - - fig.add_scatter(x=df["x"], y=df["y"], mode="lines", showlegend=False) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "btccp", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_btc_confirmed_transactions( - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Returns BTC confirmed transactions [Source: https://api.blockchain.info/] - - Parameters - ---------- - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df = blockchain_model.get_btc_confirmed_transactions() - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - df = df[ - (df["x"] > datetime.fromtimestamp(ts_start_date)) - & (df["x"] < datetime.fromtimestamp(ts_end_date)) - ] - - fig = OpenBBFigure(yaxis_title="Transactions") - fig.set_title("BTC Confirmed Transactions") - - fig.add_scatter(x=df["x"], y=df["y"], mode="lines", showlegend=False) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "btcct", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_btc_single_block( - blockhash: str, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Returns BTC block data. [Source: https://api.blockchain.info/] - Parameters - ---------- - blockhash : str - Hash of the block you are looking for. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = blockchain_model.get_btc_single_block(blockhash) - if not df.empty: - df.rename(index={0: "Value"}, inplace=True) - df_data = df.copy() - - df_essentials = df[ - [ - "hash", - "ver", - "prev_block", - "mrkl_root", - "bits", - "next_block", - "fee", - "nonce", - "n_tx", - "size", - "block_index", - "main_chain", - "height", - "weight", - ] - ] - - df_flipped = df_essentials.transpose() - - print_rich_table( - df_flipped, - show_index=True, - index_name="Metric", - title=f"Block {int(df['height'])}", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "btcblockdata", - df_data, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/onchain/ethgasstation_model.py b/openbb_terminal/cryptocurrency/onchain/ethgasstation_model.py deleted file mode 100644 index 7c804c1f419..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/ethgasstation_model.py +++ /dev/null @@ -1,63 +0,0 @@ -"""ETH Gas Station model""" - -import logging - -import pandas as pd -import requests - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_gwei_fees() -> pd.DataFrame: - """Returns the most recent Ethereum gas fees in gwei - [Source: https://ethgasstation.info] - - Parameters - ---------- - - Returns - ------- - pd.DataFrame - four gas fees and durations - (fees for slow, average, fast and - fastest transactions in gwei and - its average durations in seconds) - """ - - r = request("https://ethgasstation.info/json/ethgasAPI.json") - - try: - if r.status_code == 200: - apiData = r.json() - return pd.DataFrame( - data=[ - [ - "Fastest", - int(apiData["fastest"] / 10), - round(apiData["fastestWait"], 1), - ], - ["Fast", int(apiData["fast"] / 10), round(apiData["fastWait"], 1)], - [ - "Average", - int(apiData["average"] / 10), - round(apiData["avgWait"], 1), - ], - [ - "Slow", - int(apiData["safeLow"] / 10), - round(apiData["safeLowWait"], 1), - ], - ], - columns=["Tx Type", "Fee (gwei)", "Duration (min)"], - ) - return pd.DataFrame() - except TypeError as terr: - logger.exception(str(terr)) - return pd.DataFrame() - except requests.exceptions.RequestException as re: - logger.exception(str(re)) - return pd.DataFrame() diff --git a/openbb_terminal/cryptocurrency/onchain/ethgasstation_view.py b/openbb_terminal/cryptocurrency/onchain/ethgasstation_view.py deleted file mode 100644 index b8ec69a687e..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/ethgasstation_view.py +++ /dev/null @@ -1,50 +0,0 @@ -"""ETH Gas Station view""" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.onchain.ethgasstation_model import get_gwei_fees -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_gwei_fees( - export: str = "", sheet_name: Optional[str] = None, limit: int = 10 -) -> None: - """Current gwei fees - [Source: https://ethgasstation.info] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - - df_fees = get_gwei_fees() - - if df_fees.empty: - console.print("\nError in ethgasstation request\n") - else: - console.print("\nCurrent ETH gas fees (gwei):") - - print_rich_table( - df_fees, - headers=list(df_fees.columns), - show_index=False, - title="Current GWEI Fees", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gwei", - df_fees, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/onchain/ethplorer_model.py b/openbb_terminal/cryptocurrency/onchain/ethplorer_model.py deleted file mode 100644 index b180d53a391..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/ethplorer_model.py +++ /dev/null @@ -1,587 +0,0 @@ -"""Ethplorer model""" - -__docformat__ = "numpy" - -import logging -import textwrap -from datetime import datetime -from time import sleep -from typing import Any, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.dataframe_helpers import create_df_index -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -PRICES_FILTERS = [ - "date", - "cap", - "volumeConverted", - "open", - "high", - "close", - "low", -] - -TOP_FILTERS = [ - "rank", - "name", - "symbol", - "price", - "txsCount", - "transfersCount", - "holdersCount", -] - -TH_FILTERS = [ - "value", -] - -BALANCE_FILTERS = [ - "index", - "balance", - "tokenName", - "tokenSymbol", -] - -HIST_FILTERS = ["timestamp", "transactionHash", "token", "value"] - -HOLDERS_FILTERS = [ - "balance", - "share", -] - - -@log_start_end(log=logger) -def split_cols_with_dot(column: str) -> str: - """Split column name in data frame columns whenever there is a dot between 2 words. - E.g. price.availableSupply -> priceAvailableSupply. - - Parameters - ---------- - column: str - Pandas dataframe column value - - Returns - ------- - str - Value of column with replaced format. - """ - - @log_start_end(log=logger) - def replace(string: str, char: str, index: int) -> str: - """Helper method which replaces values with dot as a separator and converts it to camelCase format - - Parameters - ---------- - string: str - String in which we remove dots and convert it to camelcase format. - char: str - First letter of given word. - index: - Index of string element. - - Returns - ---------- - str - Camel case string with no dots. E.g. price.availableSupply -> priceAvailableSupply. - """ - - return string[:index] + char + string[index + 1 :] - - if "." in column: - part1, part2 = column.split(".") - part2 = replace(part2, part2[0].upper(), 0) - return part1 + part2 - return column - - -@log_start_end(log=logger) -def enrich_social_media(dct: dict) -> None: - """Searching inside dictionary if there are any information about twitter, reddit or coingecko. - If yes it updates dictionary with url to given social media site. - - Parameters - ---------- - dct: dict - Dictionary in which we search for coingecko, twitter or reddit url. - """ - - social_media = { - "twitter": "https://www.twitter.com/", - "reddit": "https://www.reddit.com/r/", - "coingecko": "https://www.coingecko.com/en/coins/", - } - - for k, v in social_media.items(): - if k in dct: - dct[k] = v + dct[k] - - -@log_start_end(log=logger) -def make_request( - endpoint: str, address: Optional[str] = None, **kwargs: Any -) -> Optional[dict]: - """Helper method that handles request for Ethplorer API [Source: https://ethplorer.io/] - - Parameters - ---------- - endpoint: str - endpoint we want to query e.g. https://api.ethplorer.io/?=apiKey=freekey - address: str - balance argument for given endpoint. In most cases it's tx hash, or eth balance. - kwargs: Any - Additional keywords arguments e.g. limit of transactions - - Returns - ------- - Optional[dict] - dictionary with response data - """ - - base_url = "https://api.ethplorer.io/" - url = f"{base_url}{endpoint}" - - if address: - url = url + "/" + address - - url += f"?apiKey={get_current_user().credentials.API_ETHPLORER_KEY}" - - if "limit" in kwargs: - url += f"&limit={kwargs['limit']}" - - sleep(0.5) # Limit is 2 API calls per 1 sec. - response = request(url) - result = {} - - if response.status_code == 200: - result = response.json() - - if not result: - console.print("No data found") - - elif response.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(response.json()["error"]["message"]) - - return result - - -@log_start_end(log=logger) -def get_token_decimals(address: str) -> Optional[int]: - """Helper methods that gets token decimals number. [Source: Ethplorer] - - Parameters - ---------- - address: str - Blockchain balance e.g. 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 - - Returns - ------- - Optional[int] - Number of decimals for given token. - """ - response = make_request("getTokenInfo", address) - if response and "decimals" in response: - return 10 ** int(response["decimals"]) - return None - - -@log_start_end(log=logger) -def get_address_info( - address: str, sortby: str = "index", ascend: bool = False -) -> pd.DataFrame: - """Get info about tokens on you ethereum blockchain balance. Eth balance, balance of all tokens which - have name and symbol. [Source: Ethplorer] - - Parameters - ---------- - address: str - Blockchain balance e.g. 0x3cD751E6b0078Be393132286c442345e5DC49699 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - - Returns - ------- - pd.DataFrame - DataFrame with list of tokens and their balances. - """ - - response = make_request("getAddressInfo", address) - tokens = [] - if "tokens" in response: - tokens = response.pop("tokens") - for token in tokens: - token_info = token.pop("tokenInfo") - token.update( - { - "tokenName": token_info.get("name"), - "tokenSymbol": token_info.get("symbol"), - "tokenAddress": token_info.get("balance"), - "balance": token.get("balance") - / (10 ** int(token_info.get("decimals"))), - } - ) - elif "token_info" in response: - token_info = response.get("tokenInfo") or {} - tokens = [ - { - "tokenName": token_info.get("name"), - "tokenSymbol": token_info.get("symbol"), - "tokenAddress": token_info.get("balance"), - "balance": token_info.get("balance") - / (10 ** int(token_info.get("decimals"))), - } - ] - - eth = response.get("ETH") or {} - eth_balance = eth.get("balance") - eth_row = [ - "Ethereum", - "ETH", - "0x0000000000000000000000000000000000000000", - eth_balance, - ] - cols = [ - "tokenName", - "tokenSymbol", - "tokenAddress", - "balance", - ] - df = pd.DataFrame(tokens) - eth_row_df = pd.DataFrame([eth_row], columns=cols) - df = pd.concat([eth_row_df, df], ignore_index=True) - df = df[df["tokenName"].notna()][cols] - create_df_index(df, "index") - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_top_tokens(sortby: str = "rank", ascend: bool = False) -> pd.DataFrame: - """Get top 50 tokens. [Source: Ethplorer] - - Parameters - ---------- - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - - Returns - ------- - pd.DataFrame - DataFrame with list of top 50 tokens. - """ - - response = make_request("getTopTokens") - tokens = response["tokens"] - df = pd.DataFrame(tokens)[ - [ - "name", - "symbol", - "price", - "txsCount", - "transfersCount", - "holdersCount", - "twitter", - "coingecko", - ] - ] - df["price"] = df["price"].apply(lambda x: x["rate"] if x and "rate" in x else None) - create_df_index(df, "rank") - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_top_token_holders( - address: str, sortby: str = "balance", ascend: bool = True -) -> pd.DataFrame: - """Get info about top token holders. [Source: Ethplorer] - - Parameters - ---------- - address: str - Token balance e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - - Returns - ------- - pd.DataFrame - DataFrame with list of top token holders. - """ - - response = make_request("getTopTokenHolders", address, limit=100) - df = pd.DataFrame(response["holders"]) - sleep(0.5) - token_decimals_divider = get_token_decimals(address) - if token_decimals_divider: - df["balance"] = df["balance"] / token_decimals_divider - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_address_history( - address: str, sortby: str = "timestamp", ascend: bool = True -) -> pd.DataFrame: - """Get information about balance historical transactions. [Source: Ethplorer] - - Parameters - ---------- - address: str - Blockchain balance e.g. 0x3cD751E6b0078Be393132286c442345e5DC49699 - sortby: str - Key to sort by. - ascend: str - Sort in ascending order. - - Returns - ------- - pd.DataFrame - DataFrame with balance historical transactions (last 100) - """ - response = make_request("getAddressHistory", address, limit=100) - operations = response.pop("operations") - if operations: - for operation in operations: - token = operation.pop("tokenInfo") - if token: - operation["token"] = token["name"] - operation["tokenAddress"] = token["address"] - operation["decimals"] = int(token["decimals"]) - operation["timestamp"] = datetime.fromtimestamp(operation["timestamp"]) - - df = pd.DataFrame(operations) - cols = ["timestamp", "transactionHash", "token", "value"] - df["value"] = df["value"].astype(float) / (10 ** df["decimals"]) - - if df.empty: - console.print(f"No historical transaction found for {address}") - return pd.DataFrame(columns=cols) - - df = df[cols] - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_token_info(address) -> pd.DataFrame: - """Get info about ERC20 token. [Source: Ethplorer] - - Parameters - ---------- - address: str - Token balance e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - - Returns - ------- - pd.DataFrame - DataFrame with information about provided ERC20 token. - """ - - response = make_request("getTokenInfo", address) - decimals = response.pop("decimals") if "decimals" in response else None - - for name in [ - "issuancesCount", - "lastUpdated", - "image", - "transfersCount", - "ethTransfersCount", - ]: - try: - response.pop(name) - except KeyError as e: - logger.exception(str(e)) - continue - - enrich_social_media(response) - df = pd.json_normalize(response) - df.columns = [split_cols_with_dot(x) for x in df.columns.tolist()] - if "priceTs" in df: - df.drop("priceTs", axis=1, inplace=True) - - for col in [ - "owner", - "slot", - "facebook", - "priceDiff", - "priceDiff7d", - "priceDiff30d", - "priceVolDiff1", - "priceVolDiff7", - "priceVolDiff30", - "priceCurrency", - ]: - if col in df.columns: - df.drop(col, axis=1, inplace=True) - - df["totalSupply"] = df["totalSupply"].astype(float) / (10 ** int(decimals)) - - df = df.T.reset_index() - df.columns = ["Metric", "Value"] - # pylint: disable=unsupported-assignment-operation - df["Value"] = df["Value"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=70)) if isinstance(x, str) else x - ) - - return df - - -@log_start_end(log=logger) -def get_tx_info(tx_hash: str) -> pd.DataFrame: - """Get info about transaction. [Source: Ethplorer] - - Parameters - ---------- - tx_hash: str - Transaction hash e.g. 0x9dc7b43ad4288c624fdd236b2ecb9f2b81c93e706b2ffd1d19b112c1df7849e6 - - Returns - ------- - pd.DataFrame - DataFrame with information about ERC20 token transaction. - """ - decimals = None - response = make_request("getTxInfo", tx_hash) - - try: - response.pop("logs") - operations = response.pop("operations")[0] - if operations: - operations.pop("addresses") - token = operations.pop("tokenInfo") - decimals = token.get("decimals") - if token: - operations["token"] = token["name"] - operations["tokenAddress"] = token["address"] - operations["timestamp"] = datetime.fromtimestamp(operations["timestamp"]) - response.update(operations) - response.pop("input") - - df = pd.Series(response) - - if decimals: - for col in ["intValue", "value"]: - df[col] = float(df[col]) // (10 ** int(decimals)) - - df = df.to_frame().reset_index() - df.columns = ["Metric", "Value"] - except KeyError as e: - logger.exception(str(e)) - return pd.DataFrame() - return df - - -@log_start_end(log=logger) -def get_token_history( - address: str, sortby: str = "timestamp", ascend: bool = False -) -> pd.DataFrame: - """Get info about token historical transactions. [Source: Ethplorer] - - Parameters - ---------- - address: str - Token e.g. 0xf3db5fa2c66b7af3eb0c0b782510816cbe4813b8 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - - Returns - ------- - pd.DataFrame - DataFrame with token historical transactions. - """ - - response = make_request("getTokenHistory", address, limit=1000) - all_operations = [] - operations = response["operations"] - try: - first_row = operations[0]["tokenInfo"] - name, symbol, _ = ( - first_row.get("name"), - first_row.get("symbol"), - first_row.get("balance"), - ) - decimals = first_row.get("decimals") - except Exception as e: - logger.exception(str(e)) - name, symbol = "", "" - decimals = None - - for operation in operations: - operation.pop("type") - operation.pop("tokenInfo") - operation["timestamp"] = datetime.fromtimestamp(operation["timestamp"]) - all_operations.append(operation) - - df = pd.DataFrame(all_operations) - - if df.empty: - console.print(f"No historical transaction found for {address}") - return df - - df[["name", "symbol"]] = name, symbol - df["value"] = df["value"].astype(float) / (10 ** int(decimals)) - df = df[["timestamp", "name", "symbol", "value", "from", "to", "transactionHash"]] - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_token_historical_price( - address: str, - sortby: str = "date", - ascend: bool = False, -) -> pd.DataFrame: - """Get token historical prices with volume and market cap, and average price. [Source: Ethplorer] - - Parameters - ---------- - address: str - Token e.g. 0xf3db5fa2c66b7af3eb0c0b782510816cbe4813b8 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - - Returns - ------- - pd.DataFrame - DataFrame with token historical prices. - """ - - response = make_request("getTokenPriceHistoryGrouped", address) - data = response["history"] - data.pop("current") - prices = data.get("prices") - - if not prices: - console.print(f"No historical price found for {address}") - - return pd.DataFrame() - - prices_df = pd.DataFrame(prices) - prices_df["ts"] = prices_df["ts"].apply(lambda x: datetime.fromtimestamp(x)) - if "tmp" in prices_df.columns: - prices_df.drop("tmp", axis=1, inplace=True) - - df = prices_df[ - ["date", "open", "close", "high", "low", "volumeConverted", "cap", "average"] - ] - df = df.sort_values(by=sortby, ascending=ascend) - return df diff --git a/openbb_terminal/cryptocurrency/onchain/ethplorer_view.py b/openbb_terminal/cryptocurrency/onchain/ethplorer_view.py deleted file mode 100644 index 603aec26710..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/ethplorer_view.py +++ /dev/null @@ -1,423 +0,0 @@ -"""Ethplorer view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_very_long_number_formatter, -) -from openbb_terminal.cryptocurrency.onchain import ethplorer_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -# pylint: disable=unsupported-assignment-operation - - -@log_start_end(log=logger) -@check_api_key(["API_ETHPLORER_KEY"]) -def display_address_info( - address: str, - limit: int = 15, - sortby: str = "index", - ascend: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display info about tokens for given ethereum blockchain balance e.g. ETH balance, - balance of all tokens with name and symbol. [Source: Ethplorer] - - Parameters - ---------- - address: str - Ethereum balance. - limit: int - Limit of transactions. Maximum 100 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = ethplorer_model.get_address_info(address, sortby=sortby, ascend=ascend) - df_data = df.copy() - df["balance"] = df["balance"].apply( - lambda x: ( - lambda_very_long_number_formatter(x) if x >= 10000 else round(float(x), 4) - ) - ) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Blockchain Token Information", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "balance", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_ETHPLORER_KEY"]) -def display_top_tokens( - limit: int = 15, - sortby: str = "rank", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display top ERC20 tokens [Source: Ethplorer] - - Parameters - ---------- - limit: int - Limit of transactions. Maximum 100 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = ethplorer_model.get_top_tokens(sortby, ascend) - df_data = df.copy() - df.fillna("", inplace=True) - for col in ["txsCount", "transfersCount", "holdersCount"]: - if col in df.columns: - df[col] = df[col].apply(lambda x: lambda_very_long_number_formatter(x)) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Top ERC20 Tokens", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "top", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_ETHPLORER_KEY"]) -def display_top_token_holders( - address: str, - limit: int = 10, - sortby: str = "balance", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display info about top ERC20 token holders. [Source: Ethplorer] - - Parameters - ---------- - address: str - Token balance e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - limit: int - Limit of transactions. Maximum 100 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = ethplorer_model.get_top_token_holders(address, sortby, ascend) - df_data = df.copy() - df["balance"] = df["balance"].apply(lambda x: lambda_very_long_number_formatter(x)) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="ERC20 Token Holder Info", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "holders", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_ETHPLORER_KEY"]) -def display_address_history( - address: str, - limit: int = 10, - sortby: str = "timestamp", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display information about balance historical transactions. [Source: Ethplorer] - - Parameters - ---------- - address: str - Ethereum blockchain balance e.g. 0x3cD751E6b0078Be393132286c442345e5DC49699 - limit: int - Limit of transactions. Maximum 100 - sortby: str - Key to sort by. - ascend: str - Sort in ascending order. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = ethplorer_model.get_address_history(address, sortby, ascend) - df_data = df.copy() - df["value"] = df["value"].apply( - lambda x: ( - lambda_very_long_number_formatter(x) if x >= 10000 else round(float(x), 4) - ) - ) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Historical Transactions Information", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hist", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_ETHPLORER_KEY"]) -def display_token_info( - address: str, - social: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display info about ERC20 token. [Source: Ethplorer] - - Parameters - ---------- - address: str - Token balance e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - social: bool - Flag to display social media links - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = ethplorer_model.get_token_info(address) - df_data = df.copy() - df.loc[:, "Value"] = df["Value"].apply( - lambda x: lambda_very_long_number_formatter(x) - ) - - socials = ["website", "telegram", "reddit", "twitter", "coingecko"] - df = ( - df[df["Metric"].isin(["balance", "name", "symbol"] + socials)] - if social - else df[~df["Metric"].isin(socials)] - ) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="ERC20 Token Information", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "info", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_ETHPLORER_KEY"]) -def display_tx_info( - tx_hash: str, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display info about transaction. [Source: Ethplorer] - - Parameters - ---------- - tx_hash: str - Transaction hash e.g. 0x9dc7b43ad4288c624fdd236b2ecb9f2b81c93e706b2ffd1d19b112c1df7849e6 - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = ethplorer_model.get_tx_info(tx_hash) - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Information About Transactions", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "tx", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_ETHPLORER_KEY"]) -def display_token_history( - address: str, - limit: int = 10, - sortby: str = "timestamp", - ascend: bool = False, - hash_: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display info about token history. [Source: Ethplorer] - - Parameters - ---------- - address: str - Token balance e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - limit: int - Limit of transactions. Maximum 100 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - hash_: bool, - Flag to show transaction hash. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = ethplorer_model.get_token_history(address, sortby, ascend) - df_data = df.copy() - if df.empty: - console.print(f"No results found for balance: {address}\n") - return - - df.loc[:, "value"] = df["value"].apply( - lambda x: lambda_very_long_number_formatter(x) - ) - - if hash_: - df.drop(["from", "to"], axis=1, inplace=True) - else: - df.drop("transactionHash", inplace=True, axis=1) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Token History Information", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "th", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_ETHPLORER_KEY"]) -def display_token_historical_prices( - address: str, - limit: int = 30, - sortby: str = "date", - ascend: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display token historical prices with volume and market cap, and average price. - [Source: Ethplorer] - - Parameters - ---------- - address: str - Token balance e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - limit: int - Limit of transactions. Maximum 100 - sortby: str - Key to sort by. - ascend: str - Sort in descending order. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = ethplorer_model.get_token_historical_price(address, sortby, ascend) - df_data = df.copy() - - if df.empty: - console.print(f"No results found for balance: {address}\n") - return - - df["volumeConverted"] = df["volumeConverted"].apply( - lambda x: lambda_very_long_number_formatter(x) - ) - df.loc[:, "cap"] = df["cap"].apply(lambda x: lambda_very_long_number_formatter(x)) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Historical Token Prices", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "prices", - df_data, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/onchain/onchain_controller.py b/openbb_terminal/cryptocurrency/onchain/onchain_controller.py deleted file mode 100644 index f9eb6b635f3..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/onchain_controller.py +++ /dev/null @@ -1,1433 +0,0 @@ -"""Onchain Controller Module""" - -__docformat__ = "numpy" - -# pylint: disable=C0302 - -import argparse -import difflib -import logging -from datetime import datetime -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.onchain import ( - bitquery_model, - bitquery_view, - blockchain_view, - ethplorer_model, - ethplorer_view, - topledger_model, - topledger_view, - whale_alert_model, - whale_alert_view, -) -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_int_range, - check_positive, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class OnchainController(BaseController): - """Onchain Controller class""" - - SPECIFIC_CHOICES = { - "account": [ - "balance", - "hist", - ], - "token": [ - "info", - "th", - "prices", - "holders", - ], - "tx": ["tx"], - } - - CHOICES_COMMANDS = [ - "whales", - "balance", - "top", - "holders", - "tx", - "hist", - "info", - "th", - "prices", - "address", - "lt", - "dvcp", - "tv", - "ueat", - "ttcp", - "baas", - "btccp", - "btcct", - "btcblockdata", - "topledger", - ] - - PATH = "/crypto/onchain/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.address = "" - self.address_type = "" - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("crypto/onchain/") - mt.add_cmd("btccp") - mt.add_cmd("btcct") - mt.add_cmd("btcblockdata") - mt.add_cmd("whales") - mt.add_cmd("topledger") - mt.add_cmd("lt") - mt.add_cmd("dvcp") - mt.add_cmd("tv") - mt.add_cmd("ueat") - mt.add_cmd("ttcp") - mt.add_cmd("baas") - mt.add_raw("\n") - mt.add_param("_address", self.address or "") - mt.add_param("_type", self.address_type or "") - mt.add_raw("\n") - mt.add_info("_ethereum_") - mt.add_cmd("address") - mt.add_cmd("top") - mt.add_cmd("balance", self.address_type == "account") - mt.add_cmd("hist", self.address_type == "account") - mt.add_cmd("info", self.address_type == "token") - mt.add_cmd("holders", self.address_type == "token") - mt.add_cmd("th", self.address_type == "token") - mt.add_cmd("prices", self.address_type == "token") - mt.add_cmd("tx", self.address_type == "tx") - console.print(text=mt.menu_text, menu="Cryptocurrency - Onchain") - - @log_start_end(log=logger) - def call_btcct(self, other_args: List[str]): - """Process btcct command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="btcct", - description=""" - Display BTC confirmed transactions [Source: https://api.blockchain.info/] - """, - ) - - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help="Initial date. Default: 2010-01-01", - default=datetime(2010, 1, 1).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help=f"Final date. Default: {(datetime.now()).strftime('%Y-%m-%d')}", - default=(datetime.now()).strftime("%Y-%m-%d"), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - blockchain_view.display_btc_confirmed_transactions( - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_btccp(self, other_args: List[str]): - """Process btccp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="btccp", - description=""" - Display BTC circulating supply [Source: https://api.blockchain.info/] - """, - ) - - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help="Initial date. Default: 2010-01-01", - default=datetime(2010, 1, 1).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help="Final date. Default: 2021-01-01", - default=(datetime.now()).strftime("%Y-%m-%d"), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - blockchain_view.display_btc_circulating_supply( - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_whales(self, other_args: List[str]): - """Process whales command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="whales", - description=""" - Display crypto whales transactions. - [Source: https://docs.whale-alert.io/] - """, - ) - - parser.add_argument( - "-m", - "--min", - dest="min", - type=check_int_range(500000, 100**7), - help="Minimum value of transactions.", - default=1000000, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: date", - default="date", - choices=whale_alert_model.FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-a", - "--address", - dest="address", - action="store_true", - help="Flag to show addresses of transaction", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - whale_alert_view.display_whales_transactions( - min_value=ns_parser.min, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - show_address=ns_parser.address, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_topledger(self, other_args: List[str]): - """Process topledger command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="topledger", - description=""" - Display on-chain data from topledger. - [Source: Topledger] - """, - ) - - parser.add_argument( - "-o", - "--org", - dest="org_slug", - type=str, - help="Organization Slug", - choices=list(topledger_model.MAPPING.keys()), - default=None, - ) - parser.add_argument( - "-q", - "--query", - dest="query_slug", - type=str, - help="Query Slug", - choices=[ - query.get("slug") - for section in topledger_model.MAPPING.values() - for query in section.get("queries", []) - if query.get("slug") - ], - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - topledger_view.display_topledger_data( - org_slug=ns_parser.org_slug, - query_slug=ns_parser.query_slug, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_address(self, other_args: List[str]): - """Process address command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="address", - description=""" - Load address for further analysis. You can analyze account address, token address or transaction hash. - [Source: Ethplorer] - """, - ) - - parser.add_argument( - "-a", - action="store_true", - help="Account address", - dest="account", - default=False, - ) - - parser.add_argument( - "-t", - action="store_true", - help="ERC20 token address", - dest="token", - default=False, - ) - - parser.add_argument( - "-tx", - action="store_true", - help="Transaction hash", - dest="transaction", - default=False, - ) - - parser.add_argument( - "--address", - dest="address", - help="Ethereum address", - default=False, - type=str, - required="-h" not in other_args, - ) - - if other_args and other_args[0][0] != "-": - other_args.insert(0, "--address") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if len(ns_parser.address) not in [42, 66]: - console.print( - f"Couldn't load address {ns_parser.address}. " - f"Token or account address should be 42 characters long. " - f"Transaction hash should be 66 characters long\n" - ) - - if ns_parser.account: - address_type = "account" - elif ns_parser.token: - address_type = "token" - elif ns_parser.transaction: - address_type = "tx" - else: - address_type = "account" - - if len(ns_parser.address) == 66: - address_type = "tx" - - self.address = ns_parser.address - self.address_type = address_type - - @log_start_end(log=logger) - def call_balance(self, other_args: List[str]): - """Process balance command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="balance", - description=""" - Display info about tokens on given ethereum blockchain balance. - [Source: Ethplorer] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: index", - default="index", - choices=ethplorer_model.BALANCE_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.address: - ethplorer_view.display_address_info( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - address=self.address, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("You need to set an ethereum address\n") - - @log_start_end(log=logger) - def call_hist(self, other_args: List[str]): - """Process hist command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hist", - description=""" - Display history for given ethereum blockchain balance. - e.g. 0x3cD751E6b0078Be393132286c442345e5DC49699 - [Source: Ethplorer] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: timestamp", - default="timestamp", - choices=ethplorer_model.HIST_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.address: - ethplorer_view.display_address_history( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - address=self.address, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("You need to set an ethereum address\n") - - @log_start_end(log=logger) - def call_holders(self, other_args: List[str]): - """Process holders command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="holders", - description=""" - Display top ERC20 token holders: e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - [Source: Ethplorer] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: share", - default="share", - choices=ethplorer_model.HOLDERS_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.address: - ethplorer_view.display_top_token_holders( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - address=self.address, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("You need to set an ethereum address\n") - - @log_start_end(log=logger) - def call_top(self, other_args: List[str]): - """Process top command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="top", - description=""" - Display top ERC20 tokens. - [Source: Ethplorer] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: rank", - default="rank", - choices=ethplorer_model.TOP_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - ethplorer_view.display_top_tokens( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_info(self, other_args: List[str]): - """Process info command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="info", - description=""" - Display info about ERC20 token. e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - [Source: Ethplorer] - """, - ) - - parser.add_argument( - "--social", - action="store_false", - help="Flag to show social media links", - dest="social", - default=False, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if self.address: - ethplorer_view.display_token_info( - social=ns_parser.social, - address=self.address, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("You need to set an ethereum address\n") - - @log_start_end(log=logger) - def call_th(self, other_args: List[str]): - """Process th command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="th", - description=""" - Displays info about token history. - e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - [Source: Ethplorer] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: value", - default="value", - choices=ethplorer_model.TH_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "--hash", - action="store_false", - help="Flag to show transaction hash", - dest="hash", - default=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if self.address: - ethplorer_view.display_token_history( - limit=ns_parser.limit, - hash_=ns_parser.hash, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - address=self.address, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("You need to set an ethereum address\n") - - @log_start_end(log=logger) - def call_tx(self, other_args: List[str]): - """Process tx command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tx", - description=""" - Display info ERC20 token transaction on ethereum blockchain. - e.g. 0x9dc7b43ad4288c624fdd236b2ecb9f2b81c93e706b2ffd1d19b112c1df7849e6 - [Source: Ethplorer] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if self.address: - ethplorer_view.display_tx_info( - tx_hash=self.address, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("You need to set an ethereum address\n") - - @log_start_end(log=logger) - def call_prices(self, other_args: List[str]): - """Process prices command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="prices", - description=""" - Display token historical prices. e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 - [Source: Ethplorer] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: date", - default="date", - choices=ethplorer_model.PRICES_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.address: - ethplorer_view.display_token_historical_prices( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - address=self.address, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("You need to set an ethereum address\n") - - @log_start_end(log=logger) - def call_lt(self, other_args: List[str]): - """Process lt command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lt", - description=""" - Display Trades on Decentralized Exchanges aggregated by DEX or Month - [Source: https://graphql.bitquery.io/] - """, - ) - parser.add_argument( - "-k", - "--kind", - dest="kind", - type=str, - help="Aggregate trades by dex or time Default: dex", - default="dex", - choices=bitquery_model.LT_KIND, - ) - parser.add_argument( - "-vs", - "--vs", - dest="vs", - type=str, - help="Currency of displayed trade amount.", - default="USD", - choices=bitquery_model.CURRENCIES, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-d", - "--days", - dest="days", - type=check_positive, - help="Number of days to display data for.", - default=90, - choices=range(1, 360), - metavar="DAYS", - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: tradeAmount. For monthly trades date.", - default="tradeAmount", - choices=bitquery_model.LT_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - bitquery_view.display_dex_trades( - kind=ns_parser.kind, - trade_amount_currency=ns_parser.vs, - limit=ns_parser.limit, - days=ns_parser.days, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dvcp(self, other_args: List[str]): - """Process dvcp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dvcp", - description=""" - Display daily volume for given crypto pair - [Source: https://graphql.bitquery.io/] - """, - ) - parser.add_argument( - "-c", - "--coin", - dest="coin", - type=str, - help="ERC20 token symbol or address.", - required="-h" not in other_args, - ) - parser.add_argument( - "-vs", - "--vs", - dest="vs", - type=str, - help="Quote currency", - default="USDT", - choices=bitquery_model.CURRENCIES, - ) - parser.add_argument( - "-d", - "--days", - dest="days", - type=check_positive, - help="Number of days to display data for.", - default=10, - choices=range(1, 100), - metavar="DAYS", - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column.", - default="date", - choices=bitquery_model.DVCP_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - bitquery_view.display_daily_volume_for_given_pair( - symbol=ns_parser.coin, - to_symbol=ns_parser.vs, - limit=ns_parser.days, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_tv(self, other_args: List[str]): - """Process tv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tv", - description=""" - Display token volume on different Decentralized Exchanges. - [Source: https://graphql.bitquery.io/] - """, - ) - parser.add_argument( - "-c", - "--coin", - dest="coin", - type=str, - help="ERC20 token symbol or address.", - required="-h" not in other_args, - ) - parser.add_argument( - "-vs", - "--vs", - dest="vs", - type=str, - help="Currency of displayed trade amount.", - default="USD", - choices=bitquery_model.CURRENCIES, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column.", - default="trades", - choices=bitquery_model.LT_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - bitquery_view.display_dex_volume_for_token( - symbol=ns_parser.coin, - trade_amount_currency=ns_parser.vs, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ueat(self, other_args: List[str]): - """Process ueat command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ueat", - description=""" - Display number of unique ethereum addresses which made a transaction in given time interval, - [Source: https://graphql.bitquery.io/] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records. (Maximum available time period is 90 days." - "Depending on chosen time period, display N records will be recalculated. E.g." - "For interval: month, and number: 10, period of calculation equals to 300, " - "but because of max days limit: 90, it will only return last 3 months (3 records). ", - default=10, - choices=range(1, 90), - metavar="LIMIT", - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column.", - default="date", - choices=bitquery_model.UEAT_FILTERS, - ) - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=str, - help="Time interval in which ethereum address made transaction. month, week or day. " - "Maximum time period is 90 days (3 months, 14 weeks)", - default="day", - choices=bitquery_model.INTERVALS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - - if ns_parser: - bitquery_view.display_ethereum_unique_senders( - interval=ns_parser.interval, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ttcp(self, other_args: List[str]): - """Process ttcp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ttcp", - description=""" - Display most traded crypto pairs on given decentralized exchange in chosen time period. - [Source: https://graphql.bitquery.io/] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-e", - "--exchange", - dest="exchange", - type=str, - help="Decentralized exchange name.", - choices=bitquery_model.DECENTRALIZED_EXCHANGES, - ) - parser.add_argument( - "-d", - "--days", - dest="days", - type=check_positive, - help="Number of days to display data for.", - default=30, - choices=range(1, 100), - metavar="DAYS", - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column.", - default="tradeAmount", - choices=bitquery_model.TTCP_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-e") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - exchange = "Uniswap" - if ns_parser.exchange: - if ns_parser.exchange in bitquery_model.DECENTRALIZED_EXCHANGES: - exchange = ns_parser.exchange - else: - similar_cmd = difflib.get_close_matches( - ns_parser.exchange, - bitquery_model.DECENTRALIZED_EXCHANGES, - n=1, - cutoff=0.75, - ) - - if similar_cmd: - console.print(f"Replacing by '{similar_cmd[0]}'") - exchange = similar_cmd[0] - - else: - similar_cmd = difflib.get_close_matches( - ns_parser.exchange, - bitquery_model.DECENTRALIZED_EXCHANGES, - n=1, - cutoff=0.5, - ) - if similar_cmd: - console.print(f"Did you mean '{similar_cmd[0]}'?") - - console.print( - f"Couldn't find any exchange with provided name: {ns_parser.exchange}. " - f"Please choose one from list: {bitquery_model.DECENTRALIZED_EXCHANGES}\n" - ) - - else: - console.print("Exchange not provided setting default to Uniswap.\n") - - bitquery_view.display_most_traded_pairs( - days=ns_parser.days, - limit=ns_parser.limit, - exchange=exchange, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_baas(self, other_args: List[str]): - """Process baas command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="baas", - description=""" - Display average bid, ask prices, spread for given crypto pair for chosen time period - [Source: https://graphql.bitquery.io/] - """, - ) - parser.add_argument( - "-c", - "--coin", - dest="coin", - type=str, - help="ERC20 token symbol or address.", - choices=bitquery_model.get_possible_crypto_symbols(), - metavar="COIN", - ) - parser.add_argument( - "-vs", - "--vs", - dest="vs", - type=str, - help="Quote currency", - default="USDT", - choices=bitquery_model.CURRENCIES, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of days to display data for.", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column.", - default="date", - choices=bitquery_model.BAAS_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.coin: - possible_cryptos = bitquery_model.get_possible_crypto_symbols() - if ns_parser.coin in possible_cryptos: - bitquery_view.display_spread_for_crypto_pair( - symbol=ns_parser.coin, - to_symbol=ns_parser.vs, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - - else: - console.print(f"Coin '{ns_parser.coin}' does not exist.") - if ns_parser.coin.upper() == "BTC": - token = "WBTC" # noqa: S105 - else: - similar_cmd = difflib.get_close_matches( - ns_parser.coin, - possible_cryptos, - n=1, - cutoff=0.75, - ) - try: - token = similar_cmd[0] - if similar_cmd[0]: - console.print(f"Replacing with '{token}'") - bitquery_view.display_spread_for_crypto_pair( - token=token, - to_symbol=ns_parser.vs, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - except Exception: - similar_cmd = difflib.get_close_matches( - ns_parser.coin, - possible_cryptos, - n=1, - cutoff=0.5, - ) - if similar_cmd: - console.print(f"Did you mean '{similar_cmd[0]}'?") - - else: - console.print("You didn't provide coin symbol.\n") - - @log_start_end(log=logger) - def call_btcblockdata(self, other_args: List[str]): - """Process btcblockdata command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="btcblockdata", - description=""" - Display block data from Blockchain.com, - [Source: https://api.blockchain.info/] - """, - ) - - if other_args and "-" not in other_args[0]: - other_args.insert(0, "--blockhash") - - parser.add_argument( - "--blockhash", - action="store", - help="Flag for block hash of block.", - dest="blockhash", - default=False, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - blockchain_view.display_btc_single_block( - blockhash=ns_parser.blockhash, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/cryptocurrency/onchain/topledger_model.py b/openbb_terminal/cryptocurrency/onchain/topledger_model.py deleted file mode 100644 index 14a455053b3..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/topledger_model.py +++ /dev/null @@ -1,310 +0,0 @@ -"""Topledger model""" - -__docformat__ = "numpy" - -import logging -from typing import Any, Dict, Optional, Tuple - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -MAPPING: Dict[str, Any] = { - "tensor": { - "api_key": "8eSjJnSuOC7uLWkrTQoWADStkNS2ZIy94pH5CsNn", - "tl_org_slug": "tensor", - "queries": [ - {"id": 846, "slug": "daily-transactions"}, - {"id": 847, "slug": "weekly-transactions"}, - {"id": 848, "slug": "monthly-transactions"}, - {"id": 849, "slug": "daily-users"}, - {"id": 852, "slug": "monthly-users"}, - {"id": 851, "slug": "weekly-users"}, - {"id": 1092, "slug": "daily-gmv-sol"}, - {"id": 1104, "slug": "weekly-gmv"}, - {"id": 1105, "slug": "monthly-gmv"}, - {"id": 1110, "slug": "daily-tvl"}, - {"id": 1111, "slug": "weekly-tvl"}, - {"id": 1112, "slug": "monthly-tvl"}, - {"id": 1115, "slug": "top-traders-by-gmv"}, - ], - }, - "genopets": { - "api_key": "67F9IFzXma0iusqMAb2VynopTfHqzri2zTeacQnh", - "tl_org_slug": "genopets", - "queries": [ - {"id": 1206, "slug": "daily-users"}, - {"id": 1207, "slug": "weekly-users"}, - {"id": 1208, "slug": "monthly-users"}, - {"id": 1209, "slug": "weekly-retention"}, - {"id": 1210, "slug": "monthly-retention"}, - {"id": 1214, "slug": "daily-transactions"}, - {"id": 1215, "slug": "weekly-transactions"}, - {"id": 1216, "slug": "monthly-transactions"}, - {"id": 1221, "slug": "daily-$ki-volume"}, - {"id": 1222, "slug": "weekly-$ki-volume"}, - {"id": 1223, "slug": "monthly-$ki-volume"}, - {"id": 1224, "slug": "daily-$gene-volume"}, - {"id": 1225, "slug": "weekly-$gene-volume"}, - {"id": 1226, "slug": "monthly-$gene-volume"}, - {"id": 1229, "slug": "ghmp-daily-instruction-type-split"}, - {"id": 1230, "slug": "ghmp-weekly-instruction-type-split"}, - {"id": 1231, "slug": "ghmp-monthly-instruction-type-split"}, - {"id": 1234, "slug": "gcmp-daily-instruction-type-split"}, - {"id": 1235, "slug": "gcmp-weekly-instruction-type-split"}, - {"id": 1236, "slug": "gcmp-monthly-instruction-type-split"}, - {"id": 1238, "slug": "gsp-daily-instruction-type-split"}, - {"id": 1239, "slug": "gsp-weekly-instruction-type-split"}, - {"id": 1242, "slug": "harvestki-daily"}, - {"id": 1245, "slug": "withdrawki-daily"}, - ], - }, - "staratlas": { - "api_key": "l8Q9sOseZRAoFVqvTExxWJ8W9j4ETySHiDFGIHg3", - "tl_org_slug": "staratlas", - "queries": [ - {"id": 1033, "slug": "daily-users"}, - {"id": 1026, "slug": "weekly-users"}, - {"id": 1040, "slug": "monthly-users"}, - {"id": 1048, "slug": "weekly-retention"}, - {"id": 1051, "slug": "monthly-retention"}, - {"id": 1049, "slug": "daily-transactions"}, - {"id": 1034, "slug": "weekly-transactions"}, - {"id": 1043, "slug": "monthly-transactions"}, - {"id": 1056, "slug": "ssp-daily-instruction-type-split"}, - {"id": 1058, "slug": "ssp-weekly-instruction-type-split"}, - {"id": 1037, "slug": "ssp-monthly-instruction-type-split"}, - {"id": 1059, "slug": "daily-process-harvest-atlas-volume"}, - {"id": 1036, "slug": "weekly-process-harvest-atlas-volume"}, - {"id": 1039, "slug": "monthly-process-harvest-atlas-volume"}, - {"id": 1038, "slug": "daily-gas-fee"}, - {"id": 1046, "slug": "weekly-gas-fee"}, - {"id": 1027, "slug": "monthly-gas-fee"}, - ], - }, - "aurory": { - "api_key": "LzJODEsQnkySsqbCoRxPqj8KuN5x8SGK3AESatgm", - "tl_org_slug": "aurory", - "queries": [ - {"id": 1147, "slug": "daily-users"}, - {"id": 1132, "slug": "weekly-users"}, - {"id": 1127, "slug": "monthly-users"}, - {"id": 1166, "slug": "weekly-retention"}, - {"id": 1135, "slug": "monthly-retention"}, - {"id": 1143, "slug": "daily-transactions"}, - {"id": 1153, "slug": "weekly-transactions"}, - {"id": 1160, "slug": "monthly-transactions"}, - {"id": 1149, "slug": "daily-aury-volume"}, - {"id": 1165, "slug": "weekly-aury-volume"}, - {"id": 1152, "slug": "monthly-aury-volume"}, - {"id": 1151, "slug": "daily-xaury-volume"}, - {"id": 1150, "slug": "weekly-xaury-volume"}, - {"id": 1152, "slug": "monthly-xaury-volume"}, - ], - }, - "sol_casino": { - "api_key": "biEGK5IM7ciI3GCOJ0QJVrHWHMYflGsAcfcCixcP", - "tl_org_slug": "tl", - "queries": [ - {"id": 1468, "slug": "daily-transactions"}, - {"id": 1471, "slug": "weekly-transactions"}, - {"id": 1473, "slug": "monthly-transactions"}, - {"id": 1481, "slug": "daily-active-wallets"}, - {"id": 1482, "slug": "weekly-active-wallets"}, - {"id": 1483, "slug": "monthly-active-wallets"}, - {"id": 1505, "slug": "daily-volume"}, - {"id": 1485, "slug": "daily-sol-deposits"}, - {"id": 1486, "slug": "daily-usdc-usdt-deposits"}, - {"id": 1488, "slug": "daily-bonk-deposits"}, - ], - }, - "exchange_art": { - "api_key": "biEGK5IM7ciI3GCOJ0QJVrHWHMYflGsAcfcCixcP", - "tl_org_slug": "tl", - "queries": [ - {"id": 1331, "slug": "daily-transactions"}, - {"id": 1332, "slug": "weekly-transactions"}, - {"id": 1333, "slug": "monthly-transactions"}, - {"id": 1334, "slug": "daily-transaction-fee"}, - {"id": 1335, "slug": "daily-users"}, - {"id": 1336, "slug": "weekly-users"}, - {"id": 1337, "slug": "monthly-users"}, - {"id": 1338, "slug": "daily-transactions-by-programs"}, - {"id": 1339, "slug": "weekly-transactions-by-programs"}, - {"id": 1340, "slug": "monthly-transactions-by-programs"}, - {"id": 1349, "slug": "daily-new-users"}, - {"id": 1358, "slug": "weekly-new-users"}, - {"id": 1359, "slug": "monthly-new-users"}, - {"id": 1367, "slug": "daily-bids-placed"}, - {"id": 1371, "slug": "daily-finearts-listed"}, - {"id": 1418, "slug": "daily-primary-gmv"}, - {"id": 1422, "slug": "weekly-primary-gmv"}, - {"id": 1423, "slug": "monthly-primary-gmv"}, - {"id": 1427, "slug": "daily-secondary-gmv"}, - {"id": 1428, "slug": "weekly-secondary-gmv"}, - {"id": 1429, "slug": "monthly-secondary-gmv"}, - ], - }, - "xNFT_Backpack": { - "api_key": "biEGK5IM7ciI3GCOJ0QJVrHWHMYflGsAcfcCixcP", - "tl_org_slug": "tl", - "queries": [ - {"id": 2318, "slug": "xnft-installed"}, - {"id": 1738, "slug": "daily-xnft-installed"}, - {"id": 1736, "slug": "daily-xnft-users"}, - {"id": 1760, "slug": "xnft-by-tag"}, - {"id": 1762, "slug": "xnft-by-rating"}, - {"id": 2320, "slug": "top20-users-by-nft-installs"}, - {"id": 1764, "slug": "mad-lad-nft"}, - {"id": 1757, "slug": "users-by-xnft"}, - {"id": 1731, "slug": "cumulative-xnft-installed"}, - ], - }, - "bonk": { - "api_key": "biEGK5IM7ciI3GCOJ0QJVrHWHMYflGsAcfcCixcP", - "tl_org_slug": "tl", - "queries": [ - {"id": 486, "slug": "bonk-transaction-fee"}, - {"id": 506, "slug": "daily-bonk-burn"}, - {"id": 507, "slug": "top-100-accounts-burning-bonk"}, - {"id": 477, "slug": "daily-active-users"}, - {"id": 488, "slug": "user-split-by-pools"}, - {"id": 479, "slug": "bonk-transactions"}, - {"id": 487, "slug": "transactions-split-by-pools"}, - {"id": 514, "slug": "bonk-volume-usd"}, - {"id": 483, "slug": "bonk-volume"}, - {"id": 509, "slug": "bonk-holders"}, - {"id": 484, "slug": "top-100-bonk-holders"}, - {"id": 485, "slug": "top-wallets-by-transactions"}, - {"id": 492, "slug": "daily-users-split-by-dex"}, - ], - }, - "light_protocol": { - "api_key": "De2qJPBJKt19Yh24Z2n6kezIlaEBA4UYkBBjdbXn", - "tl_org_slug": "luminouslabs", - "queries": [ - {"id": 1846, "slug": "hourly-transactions"}, - {"id": 1506, "slug": "daily-transactions"}, - {"id": 1690, "slug": "weekly-transactions"}, - {"id": 1691, "slug": "monthly-transaction"}, - {"id": 1692, "slug": "daily-fee"}, - {"id": 1697, "slug": "daily-shielded-and-unshielded-volume"}, - {"id": 1699, "slug": "weekly-shielded-and-unshielded-volume"}, - {"id": 1700, "slug": "monthly-shielded-and-unshielded-volume"}, - {"id": 1701, "slug": "shielded-and-unshielded-transactions-by-wallets"}, - ], - }, -} - - -@log_start_end(log=logger) -def make_request(org_slug="", query_slug="") -> Tuple[Optional[int], Any]: - """Helper methods for requests to topledger's query results [Source: Topledger] - - Parameters - ---------- - org_slug: str - Organization Slug - query_slug: str - Query Slug - - Returns - ------- - Tuple[Optional[int], Any] - status code, response from api request - """ - org = MAPPING[org_slug] - if not org: - console.print(f"[red]Organization Slug[{org_slug}] is invalid[/red]\n") - logger.error("Organization Slug[%s] is invalid", org_slug) - return None, None - - query_items = [x for x in org["queries"] if x["slug"] == query_slug] - if len(query_items) == 0: - console.print( - f"[red]Query Slug[{query_slug}] is invalid for Organization[{org_slug}][/red]\n" - ) - logger.error( - "Query Slug[%s] is invalid for Organization[%s]", query_slug, org_slug - ) - return None, None - - query = query_items[0] - query_id = query["id"] - api_key = org["api_key"] - tl_org_slug = org["tl_org_slug"] - - # """ - # API Documentation - # ---------- - # :param string tl_org_slug: Slug of Topledger Organization - # :param string query_id: the ID of the query to fetch result of - # :param string api_key: Authentication Key to fetch result - # - # API Format: HOST//api/queries//results.json?api_key= - # """ - - url = f"https://analytics.topledger.xyz/{tl_org_slug}/api/queries/{query_id}/results.json?api_key={api_key}" - try: - response = request(url) - except Exception: - return None, None - - result = {} - - if response.status_code == 200: - result = response.json() - elif response.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - logger.error("Invalid Authentication: %s", response.text) - elif response.status_code == 401: - console.print("[red]API Key not authorized[/red]\n") - logger.error("Insufficient Authorization: %s", response.text) - elif response.status_code == 429: - console.print("[red]Exceeded number of calls per minute[/red]\n") - logger.error("Calls limit exceeded: %s", response.text) - else: - console.print(response.json()["message"]) - logger.error("Error in request: %s", response.text) - - return response.status_code, result - - -@log_start_end(log=logger) -def get_topledger_data(org_slug: str, query_slug: str) -> pd.DataFrame: - """Returns Topledger's Data for the given Organization's Slug[org_slug] based - on Query Slug[query_slug] [Source: Topledger] - - Parameters - ---------- - org_slug: str - Organization Slug - query_slug: str - Query Slug - - Returns - ------- - pd.DataFrame - Topledger Data - """ - - if not org_slug: - console.print("[red]Org is blank[/red]\n") - logger.error("Org is blank") - return pd.DataFrame() - - if not query_slug: - console.print("[red]Query is blank[/red]\n") - logger.error("Query is blank") - return pd.DataFrame() - - status_code, response = make_request(org_slug, query_slug) - if status_code != 200: - return pd.DataFrame() - - data = pd.json_normalize(response["query_result"]["data"]["rows"]) - return pd.DataFrame(data) diff --git a/openbb_terminal/cryptocurrency/onchain/topledger_view.py b/openbb_terminal/cryptocurrency/onchain/topledger_view.py deleted file mode 100644 index 4d25007d5ac..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/topledger_view.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Topledger Data view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.onchain import topledger_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, -) -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_topledger_data( - org_slug: str, - query_slug: str, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display on-chain data from Topledger. [Source: Topledger] - - Parameters - ---------- - org_slug: str - Organization Slug - query_slug: str - Query Slug - export: str - Export dataframe data to csv,json,xlsx file - """ - df = topledger_model.get_topledger_data(org_slug, query_slug) - if df.empty: - console.print("Failed to retrieve data.") - return - df_data = df.copy() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"Topledger Query Result {query_slug} for {org_slug}", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{query_slug} for {org_slug}", - df_data, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/onchain/whale_alert_model.py b/openbb_terminal/cryptocurrency/onchain/whale_alert_model.py deleted file mode 100644 index 0b4f4ac5476..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/whale_alert_model.py +++ /dev/null @@ -1,182 +0,0 @@ -"""Whale Alert model""" - -__docformat__ = "numpy" - -import logging -import textwrap -from typing import Any, Optional, Tuple - -import numpy as np -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -FILTERS = [ - "date", - "symbol", - "blockchain", - "amount", - "amount_usd", - "from", - "to", -] - - -class ApiKeyException(Exception): - """Api Key Exception object""" - - @log_start_end(log=logger) - def __init__(self, message: str): - super().__init__(message) - self.message = message - - @log_start_end(log=logger) - def __str__(self) -> str: - return f"ApiKeyException: {self.message}" - - -@log_start_end(log=logger) -@check_api_key(["API_WHALE_ALERT_KEY"]) -def make_request(params: Optional[dict] = None) -> Tuple[Optional[int], Any]: - """Helper methods for requests [Source: https://docs.whale-alert.io/] - - Parameters - ---------- - params: dict - additional param - - Returns - ------- - Tuple[Optional[int], Any] - status code, response from api request - """ - - api_key = get_current_user().credentials.API_WHALE_ALERT_KEY or "" - url = "https://api.whale-alert.io/v1/transactions?api_key=" + api_key - try: - response = request(url, params=params) - except Exception: - return None, None - - result = {} - - if response.status_code == 200: - result = response.json() - elif response.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - logger.error("Invalid Authentication: %s", response.text) - elif response.status_code == 401: - console.print("[red]API Key not authorized for Premium Feature[/red]\n") - logger.error("Insufficient Authorization: %s", response.text) - elif response.status_code == 429: - console.print("[red]Exceeded number of calls per minute[/red]\n") - logger.error("Calls limit exceeded: %s", response.text) - else: - console.print(response.json()["message"]) - logger.error("Error in request: %s", response.text) - - return response.status_code, result - - -@log_start_end(log=logger) -def get_whales_transactions( - min_value: int = 800000, - limit: int = 100, - sortby: str = "date", - ascend: bool = False, -) -> pd.DataFrame: - """Whale Alert's API allows you to retrieve live and historical transaction data from major blockchains. - Supported blockchain: Bitcoin, Ethereum, Ripple, NEO, EOS, Stellar and Tron. [Source: https://docs.whale-alert.io/] - - Parameters - ---------- - min_value: int - Minimum value of trade to track. - limit: int - Limit of transactions. Max 100 - sortby: str - Key to sort by. - ascend: str - Sort in ascending order. - - Returns - ------- - pd.DataFrame - Crypto wales transactions - """ - - min_value = max(min_value, 800000) - limit = max(limit, 100) - - params = {"limit": limit, "min_value": min_value} - - status_code, response = make_request(params) - - if status_code != 200: - return pd.DataFrame() - - data = pd.json_normalize(response["transactions"]).sort_values( - "timestamp", ascending=False - ) - - data["date"] = pd.to_datetime(data["timestamp"], unit="s") - data.columns = [col.replace(".balance", "") for col in data.columns] - data["to_address"] = data["to.address"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=45)) if isinstance(x, str) else x - ) - data["from_address"] = data["from.address"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=45)) if isinstance(x, str) else x - ) - - data["from"] = data.apply( - lambda x: ( - x["from.owner"] - if x["from.owner"] not in [np.nan, None, np.NaN] - else x["from.owner_type"] - ), - axis=1, - ) - data["to"] = data.apply( - lambda x: ( - x["to.owner"] - if x["to.owner"] not in [np.nan, None, np.NaN] - else x["to.owner_type"] - ), - axis=1, - ) - data.drop( - [ - "id", - "transaction_count", - "from.owner_type", - "to.owner_type", - "to.owner", - "from.owner", - "transaction_type", - "hash", - "timestamp", - ], - axis=1, - inplace=True, - ) - - df = data[ - [ - "date", - "symbol", - "blockchain", - "amount", - "amount_usd", - "from", - "to", - "from_address", - "to_address", - ] - ] - df = df.sort_values(by=sortby, ascending=ascend) - return df diff --git a/openbb_terminal/cryptocurrency/onchain/whale_alert_view.py b/openbb_terminal/cryptocurrency/onchain/whale_alert_view.py deleted file mode 100644 index 235329aee3b..00000000000 --- a/openbb_terminal/cryptocurrency/onchain/whale_alert_view.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Whale Alert view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.onchain import whale_alert_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_WHALE_ALERT_KEY"]) -def display_whales_transactions( - min_value: int = 800000, - limit: int = 100, - sortby: str = "date", - ascend: bool = False, - show_address: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display huge value transactions from major blockchains. [Source: https://docs.whale-alert.io/] - - Parameters - ---------- - min_value: int - Minimum value of trade to track. - limit: int - Limit of transactions. Maximum 100 - sortby: str - Key to sort by. - ascend: str - Sort in ascending order. - show_address: bool - Flag to show addresses of transactions. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = whale_alert_model.get_whales_transactions( - min_value=min_value, - sortby=sortby, - ascend=ascend, - ) - - if df.empty: - console.print("Failed to retrieve data.") - return - - df_data = df.copy() - - if not show_address: - df = df.drop(["from_address", "to_address"], axis=1) - else: - df = df.drop(["from", "to", "blockchain"], axis=1) - - for col in ["amount_usd", "amount"]: - df[col] = df[col].apply(lambda x: lambda_long_number_format(x)) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Large Value Transactions", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "whales", - df_data, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/overview/__init__.py b/openbb_terminal/cryptocurrency/overview/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/cryptocurrency/overview/blockchaincenter_model.py b/openbb_terminal/cryptocurrency/overview/blockchaincenter_model.py deleted file mode 100644 index a8ac70880b0..00000000000 --- a/openbb_terminal/cryptocurrency/overview/blockchaincenter_model.py +++ /dev/null @@ -1,79 +0,0 @@ -"""Blockchain Center Model""" - -import json -import logging -from datetime import datetime -from typing import Optional - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request, str_date_to_timestamp - -logger = logging.getLogger(__name__) - -DAYS = [30, 90, 365] - - -@log_start_end(log=logger) -def get_altcoin_index( - period: int = 30, - start_date: str = "2010-01-01", - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get altcoin index overtime - [Source: https://blockchaincenter.net] - - Parameters - ---------- - period: int - Number of days {30,90,365} to check performance of coins and calculate the altcoin index. - E.g., 365 checks yearly performance, 90 will check seasonal performance (90 days), - 30 will check monthly performance (30 days). - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - Date, Value (Altcoin Index) - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - if period not in DAYS: - return pd.DataFrame() - soup = BeautifulSoup( - request( - "https://www.blockchaincenter.net/altcoin-season-index/", - headers={"User-Agent": get_user_agent()}, - ).content, - "html.parser", - ) - script = soup.select_one(f'script:-soup-contains("chartdata[{period}]")') - - string = script.contents[0].strip() - initiator = string.index(f"chartdata[{period}] = ") + len(f"chartdata[{period}] = ") - terminator = string.index(";") - dict_data = json.loads(string[initiator:terminator]) - df = pd.DataFrame( - zip(dict_data["labels"]["all"], dict_data["values"]["all"]), - columns=("Date", "Value"), - ) - df["Date"] = pd.to_datetime(df["Date"]) - df["Value"] = df["Value"].astype(int) - df = df.set_index("Date") - - ts_start_date = str_date_to_timestamp(start_date) - ts_end_date = str_date_to_timestamp(end_date) - - df = df[ - (df.index > datetime.fromtimestamp(ts_start_date)) - & (df.index < datetime.fromtimestamp(ts_end_date)) - ] - - return df diff --git a/openbb_terminal/cryptocurrency/overview/blockchaincenter_view.py b/openbb_terminal/cryptocurrency/overview/blockchaincenter_view.py deleted file mode 100644 index 07f1841ed3a..00000000000 --- a/openbb_terminal/cryptocurrency/overview/blockchaincenter_view.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Blockchain Center View""" - -import logging -import os -from datetime import datetime -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.overview.blockchaincenter_model import ( - DAYS, - get_altcoin_index, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_altcoin_index( - period: int = 365, - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Displays altcoin index overtime - [Source: https://blockchaincenter.net] - - Parameters - ---------- - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - period: int - Number of days to check the performance of coins and calculate the altcoin index. - E.g., 365 will check yearly performance , 90 will check seasonal performance (90 days), - 30 will check monthly performance (30 days). - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - if period in DAYS: - df = get_altcoin_index(period, start_date, end_date) - - if df.empty: - return console.print("\nError scraping blockchain central\n") - - fig = OpenBBFigure(yaxis_title="Altcoin Index") - fig.set_title(f"Altcoin Index (Performance based on {period} days)") - fig.add_scatter(x=df.index, y=df["Value"], mode="lines", name="Altcoin Index") - - fig.add_hline_legend( - y=75, line=dict(color=theme.up_color), name="Altcoin Season (75)" - ) - fig.add_hline_legend( - y=25, - line=dict(color=theme.down_color), - name="Bitcoin Season (25)", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "altindex", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - return None diff --git a/openbb_terminal/cryptocurrency/overview/coinbase_model.py b/openbb_terminal/cryptocurrency/overview/coinbase_model.py deleted file mode 100644 index 21172c464eb..00000000000 --- a/openbb_terminal/cryptocurrency/overview/coinbase_model.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Coinbase model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.cryptocurrency.coinbase_helpers import make_coinbase_request -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - -PAIRS_FILTERS = [ - "id", - "display_name", - "base_currency", - "quote_currency", - "base_min_size", - "base_max_size", - "min_market_funds", - "max_market_funds", -] - - -@log_start_end(log=logger) -def get_trading_pairs( - limit: int = 50, sortby: str = "quote_increment", ascend: bool = True -) -> pd.DataFrame: - """Get a list of available currency pairs for trading. [Source: Coinbase] - - base_min_size - min order size - base_max_size - max order size - min_market_funds - min funds allowed in a market order. - max_market_funds - max funds allowed in a market order. - - Parameters - ---------- - limit: int - Top n of pairs - sortby: str - Key to sortby data - ascend: bool - Sort descending flag - - Returns - ------- - pd.DataFrame - Available trading pairs on Coinbase - """ - - columns = [ - "id", - "display_name", - "base_currency", - "quote_currency", - "quote_increment", - "base_increment", - "min_market_funds", - ] - pairs = make_coinbase_request("/products") - df = pd.DataFrame(pairs)[columns] - df = df.sort_values(by=sortby, ascending=ascend).head(limit) - return df diff --git a/openbb_terminal/cryptocurrency/overview/coinbase_view.py b/openbb_terminal/cryptocurrency/overview/coinbase_view.py deleted file mode 100644 index 35fc6af2a25..00000000000 --- a/openbb_terminal/cryptocurrency/overview/coinbase_view.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Coinbase view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.overview import coinbase_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_COINBASE_KEY", "API_COINBASE_SECRET", "API_COINBASE_PASS_PHRASE"]) -def display_trading_pairs( - limit: int = 20, - sortby: str = "quote_increment", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Displays a list of available currency pairs for trading. [Source: Coinbase] - - Parameters - ---------- - limit: int - Top n of pairs - sortby: str - Key to sortby data - ascend: bool - Sort ascending flag - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = coinbase_model.get_trading_pairs(limit, sortby, ascend) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Available Pairs for Trading", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cbpairs", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/overview/coinpaprika_model.py b/openbb_terminal/cryptocurrency/overview/coinpaprika_model.py deleted file mode 100644 index 92fa3034dbd..00000000000 --- a/openbb_terminal/cryptocurrency/overview/coinpaprika_model.py +++ /dev/null @@ -1,424 +0,0 @@ -"""CoinPaprika model""" - -__docformat__ = "numpy" - -import logging -import textwrap -from datetime import datetime - -import pandas as pd -from dateutil import parser - -from openbb_terminal.cryptocurrency.coinpaprika_helpers import PaprikaSession -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -MARKETS_FILTERS = [ - "rank", - "name", - "symbol", - "price", - "volume_24h", - "mcap_change_24h", - "pct_change_1h", - "pct_change_24h", - "ath_price", - "pct_from_ath", -] - -EXMARKETS_FILTERS = [ - "pair", - "base_currency_name", - "quote_currency_name", - "category", - "reported_volume_24h_share", - "trust_score", - "market_url", -] - -INFO_FILTERS = [ - "rank", - "name", - "symbol", - "price", - "volume_24h", - "circulating_supply", - "total_supply", - "max_supply", - "ath_price", - "market_cap", - "beta_value", -] - -EXCHANGES_FILTERS = [ - "rank", - "name", - "currencies", - "markets", - "fiats", - "confidence", - "volume_24h", - "volume_7d", - "volume_30d", - "sessions_per_month", -] - - -CONTRACTS_FILTERS = ["id", "type", "active"] - - -@log_start_end(log=logger) -def get_global_info() -> pd.DataFrame: - """Return data frame with most important global crypto statistics like: - market_cap_usd, volume_24h_usd, bitcoin_dominance_percentage, cryptocurrencies_number, - market_cap_ath_value, market_cap_ath_date, volume_24h_ath_value, volume_24h_ath_date, - market_cap_change_24h, volume_24h_change_24h, last_updated. [Source: CoinPaprika] - - Returns - ------- - pd.DataFrame - Most important global crypto statistics - Metric, Value - """ - - session = PaprikaSession() - global_markets = session.make_request(session.ENDPOINTS["global"]) - global_markets["last_updated"] = datetime.fromtimestamp( - global_markets["last_updated"] - ) - - for key, date in global_markets.items(): - if "date" in key: - try: - new_date = date if isinstance(date, datetime) else parser.parse(date) - global_markets[key] = new_date.strftime("%Y-%m-%d %H:%M:%S") - except (KeyError, ValueError, TypeError) as e: - logger.exception(str(e)) - console.print(e) - df = pd.Series(global_markets).to_frame().reset_index() - df.columns = ["Metric", "Value"] - return df - - -@log_start_end(log=logger) -def _get_coins_info_helper(symbols: str = "USD") -> pd.DataFrame: - """Helper method that call /tickers endpoint which returns for all coins quoted in provided currency/crypto - - { - "id": "btc-bitcoin", - "name": "Bitcoin", - "symbol": "BTC", - "rank": 1, - "circulating_supply": 17007062, - "total_supply": 17007062, - "max_supply": 21000000, - "beta_value": 0.735327, - "first_data_at": "2010-11-14T07:20:41Z", - "last_updated": "2018-11-14T07:20:41Z", - "quotes" : { - "USD": { - "price": 5162.15941296, - "volume_24h": 7304207651.1585, - "volume_24h_change_24h": -2.5, - "market_cap": 91094433242, - "market_cap_change_24h": 1.6, - "percent_change_15m": 0, - "percent_change_30m": 0, - "percent_change_1h": 0, - "percent_change_6h": 0, - "percent_change_12h": -0.09, - "percent_change_24h": 1.59, - "percent_change_7d": 0.28, - "percent_change_30d": 27.39, - "percent_change_1y": -37.99, - "ath_price": 20089, - "ath_date": "2017-12-17T12:19:00Z", - "percent_from_price_ath": -74.3 - } - } - } - - [Source: CoinPaprika] - - Parameters - ---------- - symbols: Comma separated quotes to return e.g quotes=USD,BTC - - Returns - ------- - pd.DataFrame - id, name, symbol, rank, circulating_supply, total_supply, max_supply, beta_value, first_data_at, - last_updated, price, volume_24h, volume_24h_change_24h, market_cap, market_cap_change_24h, - percent_change_15m, percent_change_30m, percent_change_1h, percent_change_6h, percent_change_12h, - percent_change_24h, percent_change_7d, percent_change_30d, percent_change_1y, - ath_price, ath_date, percent_from_price_ath - """ - - session = PaprikaSession() - tickers = session.make_request(session.ENDPOINTS["tickers"], quotes=symbols) - data = pd.json_normalize(tickers) - try: - data.columns = [ - col.replace(f"quotes.{symbols}.", "") for col in data.columns.tolist() - ] - data.columns = [col.replace("percent", "pct") for col in list(data.columns)] - except KeyError as e: - logger.exception(str(e)) - console.print(e) - data.rename( - columns={ - "market_cap_change_24h": "mcap_change_24h", - "pct_from_price_ath": "pct_from_ath", - }, - inplace=True, - ) - return data - - -@log_start_end(log=logger) -def get_coins_info( - symbols: str = "USD", sortby: str = "rank", ascend: bool = True -) -> pd.DataFrame: # > format big numbers fix - """Returns basic coin information for all coins from CoinPaprika API [Source: CoinPaprika] - - Parameters - ---------- - symbols: str - Comma separated quotes to return e.g quotes=USD,BTC - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - - Returns - ------- - pd.DataFrame - rank, name, symbol, price, volume_24h, circulating_supply, total_supply, - max_supply, market_cap, beta_value, ath_price, - """ - - cols = [ - "rank", - "name", - "symbol", - "price", - "volume_24h", - "circulating_supply", - "total_supply", - "max_supply", - "market_cap", - "beta_value", - "ath_price", - ] - df = _get_coins_info_helper(symbols)[cols] - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_coins_market_info( - symbols: str = "USD", sortby: str = "rank", ascend: bool = True -) -> pd.DataFrame: - """Returns basic coin information for all coins from CoinPaprika API [Source: CoinPaprika] - - Parameters - ---------- - symbols: str - Comma separated quotes to return e.g quotes=USD,BTC - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascend - - Returns - ------- - pd.DataFrame - rank, name, symbol, price, volume_24h, mcap_change_24h, - pct_change_1h, pct_change_24h, ath_price, pct_from_ath, - """ - - cols = [ - "rank", - "name", - "symbol", - "price", - "volume_24h", - "mcap_change_24h", - "pct_change_1h", - "pct_change_24h", - "ath_price", - "pct_from_ath", - ] - df = _get_coins_info_helper(symbols=symbols)[cols].sort_values(by="rank") - if sortby == "rank": - df = df.sort_values(by=sortby, ascending=not ascend) - else: - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_list_of_exchanges( - symbols: str = "USD", sortby: str = "rank", ascend: bool = True -) -> pd.DataFrame: - """ - List exchanges from CoinPaprika API [Source: CoinPaprika] - - Parameters - ---------- - symbols: str - Comma separated quotes to return e.g quotes=USD,BTC - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascend - - Returns - ------- - pd.DataFrame - rank, name, currencies, markets, fiats, confidence_score, reported_volume_24h, - reported_volume_7d ,reported_volume_30d, sessions_per_month, - """ - - session = PaprikaSession() - exchanges = session.make_request(session.ENDPOINTS["exchanges"], quotes=symbols) - df = pd.json_normalize(exchanges) - try: - df.columns = [ - col.replace(f"quotes.{symbols}.", "") for col in df.columns.tolist() - ] - except KeyError as e: - logger.exception(str(e)) - console.print(e) - df = df[df["active"]] - cols = [ - "adjusted_rank", - "id", - "name", - "currencies", - "markets", - "fiats", - "confidence_score", - "reported_volume_24h", - "reported_volume_7d", - "reported_volume_30d", - "sessions_per_month", - ] - df["fiats"] = df["fiats"].apply(lambda x: len([i["symbol"] for i in x if x])) - df = df[cols] - df = df.applymap( - lambda x: "\n".join(textwrap.wrap(x, width=28)) if isinstance(x, str) else x - ) - df = df.rename( - columns={"adjusted_rank": "Rank", "confidence_score": "confidence"}, - ) - df.columns = [x.replace("reported_", "") for x in df.columns] - if sortby.lower() == "rank": - df = df.sort_values(by="Rank", ascending=not ascend) - else: - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_exchanges_market( - exchange_id: str = "binance", - symbols: str = "USD", - sortby: str = "pair", - ascend: bool = True, -) -> pd.DataFrame: - """List markets by exchange ID [Source: CoinPaprika] - - Parameters - ---------- - exchange_id: str - identifier of exchange e.g for Binance Exchange -> binance - symbols: str - Comma separated quotes to return e.g quotes=USD,BTC - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - pair, base_currency_name, quote_currency_name, market_url, - category, reported_volume_24h_share, trust_score, - """ - - session = PaprikaSession() - data = session.make_request( - session.ENDPOINTS["exchange_markets"].format(exchange_id), quotes=symbols - ) - if "error" in data: - console.print(data) - return pd.DataFrame() - cols = [ - "exchange_id", - "pair", - "base_currency_name", - "quote_currency_name", - "category", - "reported_volume_24h_share", - "trust_score", - "market_url", - ] - df = pd.DataFrame(data) - df["exchange_id"] = exchange_id - df = df[cols] - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_all_contract_platforms() -> pd.DataFrame: - """List all smart contract platforms like ethereum, solana, cosmos, polkadot, kusama ... [Source: CoinPaprika] - - Returns - ------- - pd.DataFrame - index, platform_id - """ - - session = PaprikaSession() - contract_platforms = session.make_request(session.ENDPOINTS["contract_platforms"]) - df = pd.DataFrame(contract_platforms).reset_index() - df.columns = ["index", "platform_id"] - - # pylint: disable=unsupported-assignment-operation - df["index"] = df["index"] + 1 - - return df - - -@log_start_end(log=logger) -def get_contract_platform( - platform_id: str = "eth-ethereum", sortby: str = "active", ascend: bool = True -) -> pd.DataFrame: - """Gets all contract addresses for given platform [Source: CoinPaprika] - Parameters - ---------- - platform_id: str - Blockchain platform like eth-ethereum - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascend - - Returns - ------- - pd.DataFrame - id, type, active - """ - - session = PaprikaSession() - contract_platforms = session.make_request( - session.ENDPOINTS["contract_platform_addresses"].format(platform_id) - ) - - df = pd.DataFrame(contract_platforms)[["id", "type", "active"]] - df = df.sort_values(by=sortby, ascending=ascend) - return df diff --git a/openbb_terminal/cryptocurrency/overview/coinpaprika_view.py b/openbb_terminal/cryptocurrency/overview/coinpaprika_view.py deleted file mode 100644 index eff0f010c1e..00000000000 --- a/openbb_terminal/cryptocurrency/overview/coinpaprika_view.py +++ /dev/null @@ -1,419 +0,0 @@ -"""CoinPaprika view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import openbb_terminal.cryptocurrency.overview.coinpaprika_model as paprika -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_long_number_format_with_type_check, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -# pylint: disable=inconsistent-return-statements -# pylint: disable=C0302, too-many-lines - -CURRENCIES = [ - "BTC", - "ETH", - "USD", - "EUR", - "PLN", - "KRW", - "GBP", - "CAD", - "JPY", - "RUB", - "TRY", - "NZD", - "AUD", - "CHF", - "UAH", - "HKD", - "SGD", - "NGN", - "PHP", - "MXN", - "BRL", - "THB", - "CLP", - "CNY", - "CZK", - "DKK", - "HUF", - "IDR", - "ILS", - "INR", - "MYR", - "NOK", - "PKR", - "SEK", - "TWD", - "ZAR", - "VND", - "BOB", - "COP", - "PEN", - "ARS", - "ISK", -] - -# see https://github.com/OpenBB-finance/OpenBBTerminal/pull/562#issuecomment-887842888 -# EXCHANGES = paprika.get_list_of_exchanges() - - -@log_start_end(log=logger) -def display_global_market(export: str = "", sheet_name: Optional[str] = None) -> None: - """Return data frame with most important global crypto statistics like: - market_cap_usd, volume_24h_usd, bitcoin_dominance_percentage, cryptocurrencies_number, - market_cap_ath_value, market_cap_ath_date, volume_24h_ath_value, volume_24h_ath_date, - market_cap_change_24h, volume_24h_change_24h, last_updated [Source: CoinPaprika] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = paprika.get_global_info() - df_data = df.copy() - df["Value"] = df["Value"].apply( # pylint:disable=unsupported-assignment-operation - lambda x: lambda_long_number_format_with_type_check(x) - ) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Global Crypto Statistics", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "global", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_all_coins_market_info( - symbol: str, - sortby: str = "rank", - ascend: bool = True, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Displays basic market information for all coins from CoinPaprika API. [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Quoted currency - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - links: bool - Flag to display urls - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = paprika.get_coins_market_info(symbols=symbol, sortby=sortby, ascend=ascend) - - df_data = df.copy() - - if df.empty: - console.print("No data found", "\n") - return - - cols = [col for col in df.columns if col != "rank"] - df[cols] = df[cols].applymap(lambda x: lambda_long_number_format_with_type_check(x)) - - console.print(f"\nDisplaying data vs {symbol}") - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Basic Market Information", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "markets", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_all_coins_info( - symbol: str, - sortby: str = "rank", - ascend: bool = True, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Displays basic coin information for all coins from CoinPaprika API. [Source: CoinPaprika] - - Parameters - ---------- - symbol: str - Quoted currency - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - links: bool - Flag to display urls - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = paprika.get_coins_info(symbols=symbol, sortby=sortby, ascend=ascend) - - df_data = df.copy() - - if df.empty: - console.print("Not data found", "\n") - return - - cols = [col for col in df.columns if col != "rank"] - df[cols] = df[cols].applymap(lambda x: lambda_long_number_format_with_type_check(x)) - - console.print(f"Displaying data vs {symbol}") - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Basic Coin Information", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "info", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_all_exchanges( - symbol: str, - sortby: str = "rank", - ascend: bool = True, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """List exchanges from CoinPaprika API. [Source: CoinPaprika] - - Parameters - ---------- - currency: str - Quoted currency - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - links: bool - Flag to display urls - export : str - Export dataframe data to csv,json,xlsx file - - """ - - df = paprika.get_list_of_exchanges(symbols=symbol, sortby=sortby, ascend=ascend) - - df_data = df.copy() - - if df.empty: - console.print("No data found", "\n") - return - - cols = [col for col in df.columns if col != "rank"] - df[cols] = df[cols].applymap(lambda x: lambda_long_number_format_with_type_check(x)) - console.print(f"\nDisplaying data vs {symbol}") - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="List Exchanges", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "exchanges", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_exchange_markets( - exchange: str = "binance", - sortby: str = "pair", - ascend: bool = True, - limit: int = 15, - links: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Get all markets for given exchange [Source: CoinPaprika] - - Parameters - ---------- - exchange: str - Exchange identifier e.g Binance - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - links: bool - Flag to display urls - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = paprika.get_exchanges_market( - exchange_id=exchange, sortby=sortby, ascend=ascend - ) - - df_data = df.copy() - - if df.empty: - console.print("No data found", "\n") - return - - if links is True: - df = df[["exchange_id", "pair", "trust_score", "market_url"]] - else: - df.drop("market_url", axis=1, inplace=True) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Exchange Markets", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "exmarkets", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_all_platforms(export: str = "", sheet_name: Optional[str] = None) -> None: - """List all smart contract platforms like ethereum, solana, cosmos, polkadot, kusama. - [Source: CoinPaprika] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = paprika.get_all_contract_platforms() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Smart Contract Platforms", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "platforms", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_contracts( - symbol: str, - sortby: str = "active", - ascend: bool = True, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Gets all contract addresses for given platform. [Source: CoinPaprika] - - Parameters - ---------- - platform: str - Blockchain platform like eth-ethereum - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = paprika.get_contract_platform(symbol, sortby, ascend) - - if df.empty: - console.print(f"Nothing found for platform: {symbol}", "\n") - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Contract Addresses", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "contracts", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/overview/cryptopanic_model.py b/openbb_terminal/cryptocurrency/overview/cryptopanic_model.py deleted file mode 100644 index eaff77f8f26..00000000000 --- a/openbb_terminal/cryptocurrency/overview/cryptopanic_model.py +++ /dev/null @@ -1,246 +0,0 @@ -"""Cryptopanic model""" - -__docformat__ = "numpy" - -import logging -import math -import textwrap -import time -from typing import Any, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.cryptocurrency_helpers import prepare_all_coins_df -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.parent_classes import CRYPTO_SOURCES -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -CATEGORIES = ["news", "media"] - -SORT_FILTERS = [ - "published_at", - "domain", - "title", - "negative_votes", - "positive_votes", -] - -FILTERS = [ - "rising", - "hot", - "bullish", - "bearish", - "important", - "saved", - "lol", -] - -REGIONS = ["en", "de", "es", "fr", "nl", "it", "pt", "ru"] - - -class ApiKeyException(Exception): - """Api Key Exception object""" - - @log_start_end(log=logger) - def __init__(self, message: str): - super().__init__(message) - self.message = message - - @log_start_end(log=logger) - def __str__(self) -> str: - return f"ApiKeyException: {self.message}" - - -@log_start_end(log=logger) -@check_api_key(["API_CRYPTO_PANIC_KEY"]) -def make_request(**kwargs: Any) -> Optional[dict]: - """Helper methods for requests [Source: https://cryptopanic.com/developers/api/] - - Parameters - ---------- - kwargs: Any - Keyword arguments with parameters for GET request to cryptopanic api. - - Returns - ------- - Optional[dict] - response from api request - """ - - api_key = get_current_user().credentials.API_CRYPTO_PANIC_KEY - crypto_panic_url = "https://cryptopanic.com/api/v1" - - post_kind = kwargs.get("post_kind") - filter_ = kwargs.get("filter_") - region = kwargs.get("region") - currency = kwargs.get("currency") - source = kwargs.get("source") - - if post_kind not in ["news", "media", "all"]: - post_kind = "all" - - url = f"{crypto_panic_url}/posts/?auth_token={api_key}" + f"&kind={post_kind}" - - if filter_ and filter_ in [ - "rising", - "hot", - "bullish", - "bearish", - "important", - "saved", - "lol", - ]: - url += f"&filter={filter_}" - - if region and region in REGIONS: - url += f"®ions={region}" - - if currency and source: - source_full = CRYPTO_SOURCES[source] - df_mapp = prepare_all_coins_df() - - try: - mapped_coin = df_mapp.loc[df_mapp[source_full] == currency][ - "Symbol" - ].values[0] - url += f"¤cy={mapped_coin.upper()}" - except IndexError: - console.print(f"Cannot find news for {currency}.\n") - return {} - - response = request(url) - response_json = response.json() - result = None - - if response.status_code == 200: - result = response_json - else: - if "Token not found" in response_json["info"]: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(response_json["info"]) - - logger.warning("Invalid authentication: %s", response.text) - - return result - - -@log_start_end(log=logger) -def _parse_post(post: dict) -> dict: - """Helper method - parse news response object to dictionary with target structure. - [Source: https://cryptopanic.com/] - - Parameters - ---------- - post: dict - Response object from cryptopanic api. - - Returns - ------- - dict - Parsed dictionary with target data structure. - - """ - return { - "published_at": post.get("published_at"), - "domain": post.get("domain"), - "title": post.get("title"), - "negative_votes": post["votes"].get("negative"), - "positive_votes": post["votes"].get("positive"), - "link": post["url"], - } - - -@log_start_end(log=logger) -def get_news( - limit: int = 60, - post_kind: str = "news", - filter_: Optional[str] = None, - region: str = "en", - source: Optional[str] = None, - symbol: Optional[str] = None, - sortby: str = "published_at", - ascend: bool = True, -) -> pd.DataFrame: - """Get recent posts from CryptoPanic news aggregator platform. [Source: https://cryptopanic.com/] - - Parameters - ---------- - limit: int - number of news to fetch - post_kind: str - Filter by category of news. Available values: news or media. - filter_: Optional[str] - Filter by kind of news. One from list: rising|hot|bullish|bearish|important|saved|lol - region: str - Filter news by regions. Available regions are: en (English), de (Deutsch), nl (Dutch), - es (Español), fr (Français), it (Italiano), pt (Português), ru (Русский) - sortby: str - Key to sort by. - ascend: bool - Sort in ascend order. - - Returns - ------- - pd.DataFrame - DataFrame with recent news from different sources filtered by provided parameters. - """ - - if post_kind not in CATEGORIES: - post_kind = "news" - - results = [] - - kwargs = {} - if source: - kwargs["source"] = source - response = make_request( - post_kind=post_kind, filter_=filter_, region=region, currency=symbol, **kwargs - ) - - if response: - data, next_page, _ = ( - response["results"], - response.get("next"), - response.get("count"), - ) - - for post in data: - results.append(_parse_post(post)) - - number_of_pages = math.ceil(limit // 20) - counter = 0 - while counter < number_of_pages and next_page: - counter += 1 - try: - time.sleep(0.2) - res = request(next_page).json() - for post in res["results"]: - results.append(_parse_post(post)) - next_page = res.get("next") - except Exception as e: # noqa: F841 - logger.exception(str(e)) - console.print( - "[red]Something went wrong while fetching news from API[/red]\n" - ) - return pd.DataFrame() - - try: - df = pd.DataFrame(results) - df["title"] = df["title"].apply( - lambda x: ( - "\n".join(textwrap.wrap(x, width=66)) if isinstance(x, str) else x - ) - ) - df["published_at"] = pd.to_datetime(df["published_at"]).dt.date - df = df.sort_values(by=sortby, ascending=ascend) - return df - except Exception as e: # noqa: F841 - logger.exception(str(e)) - console.print("[red]Something went wrong with DataFrame creation[/red]\n") - return pd.DataFrame() - return pd.DataFrame() diff --git a/openbb_terminal/cryptocurrency/overview/cryptopanic_view.py b/openbb_terminal/cryptocurrency/overview/cryptopanic_view.py deleted file mode 100644 index ab2fa2613b6..00000000000 --- a/openbb_terminal/cryptocurrency/overview/cryptopanic_view.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Cryptopanic view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.overview import cryptopanic_model -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -@check_api_key(["API_CRYPTO_PANIC_KEY"]) -def display_news( - post_kind: str = "news", - region: str = "en", - filter_: Optional[str] = None, - limit: int = 25, - sortby: str = "published_at", - ascend: bool = False, - links: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display recent posts from CryptoPanic news aggregator platform. - [Source: https://cryptopanic.com/] - - Parameters - ---------- - limit: int - number of news to display - post_kind: str - Filter by category of news. Available values: news or media. - filter_: Optional[str] - Filter by kind of news. One from list: rising|hot|bullish|bearish|important|saved|lol - region: str - Filter news by regions. Available regions are: en (English), de (Deutsch), nl (Dutch), - es (Español), fr (Français), it (Italiano), pt (Português), ru (Русский) - sortby: str - Key to sort by. - ascend: bool - Sort in ascending order. - links: bool - Show urls for news - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = cryptopanic_model.get_news( - limit=limit, - post_kind=post_kind, - filter_=filter_, - region=region, - sortby=sortby, - ascend=ascend, - ) - if not df.empty: - if not links: - df.drop("link", axis=1, inplace=True) - else: - df = df[["title", "link"]] - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Recent CryptoPanic Posts", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "news", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/overview/glassnode_model.py b/openbb_terminal/cryptocurrency/overview/glassnode_model.py deleted file mode 100644 index b7c18fd96fa..00000000000 --- a/openbb_terminal/cryptocurrency/overview/glassnode_model.py +++ /dev/null @@ -1,45 +0,0 @@ -import logging -from datetime import datetime -from typing import Optional - -from pandas import DataFrame - -from openbb_terminal.cryptocurrency.due_diligence.glassnode_model import get_close_price -from openbb_terminal.decorators import log_start_end - -# pylint: disable=unsupported-assignment-operation - -logger = logging.getLogger(__name__) -# pylint: disable=unsupported-assignment-operation - -api_url = "https://api.glassnode.com/v1/metrics/" - - -@log_start_end(log=logger) -def get_btc_rainbow( - start_date: str = "2010-01-01", - end_date: Optional[str] = None, -) -> DataFrame: - """Get bitcoin price data - [Price data from source: https://glassnode.com] - [Inspired by: https://blockchaincenter.net] - - Parameters - ---------- - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - price over time - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df_data = get_close_price("BTC", start_date, end_date) - - return df_data diff --git a/openbb_terminal/cryptocurrency/overview/glassnode_view.py b/openbb_terminal/cryptocurrency/overview/glassnode_view.py deleted file mode 100644 index fc14ab24569..00000000000 --- a/openbb_terminal/cryptocurrency/overview/glassnode_view.py +++ /dev/null @@ -1,160 +0,0 @@ -import logging -import os -from datetime import datetime -from typing import Optional - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.overview.glassnode_model import get_btc_rainbow -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_GLASSNODE_KEY"]) -def display_btc_rainbow( - start_date: str = "2010-01-01", - end_date: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Displays bitcoin rainbow chart - [Price data from source: https://glassnode.com] - [Inspired by: https://blockchaincenter.net] - - Parameters - ---------- - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - df_data = get_btc_rainbow(start_date, end_date) - - if df_data.empty: - return None - - fig = OpenBBFigure(yaxis_title="Price [USD]") - fig.set_title("Bitcoin Rainbow Chart") - - d0 = datetime.strptime("2012-01-01", "%Y-%m-%d") - dend = datetime.strptime(end_date, "%Y-%m-%d") - - x = range((df_data.index[0] - d0).days, (dend - d0).days + 1) - - y0 = [10 ** ((2.90 * ln_x) - 19.463) for ln_x in [np.log(val + 1400) for val in x]] - y1 = [10 ** ((2.886 * ln_x) - 19.463) for ln_x in [np.log(val + 1375) for val in x]] - y2 = [10 ** ((2.872 * ln_x) - 19.463) for ln_x in [np.log(val + 1350) for val in x]] - y3 = [10 ** ((2.859 * ln_x) - 19.463) for ln_x in [np.log(val + 1320) for val in x]] - y4 = [ - 10 ** ((2.8445 * ln_x) - 19.463) for ln_x in [np.log(val + 1293) for val in x] - ] - y5 = [ - 10 ** ((2.8295 * ln_x) - 19.463) for ln_x in [np.log(val + 1275) for val in x] - ] - y6 = [10 ** ((2.815 * ln_x) - 19.463) for ln_x in [np.log(val + 1250) for val in x]] - y7 = [10 ** ((2.801 * ln_x) - 19.463) for ln_x in [np.log(val + 1225) for val in x]] - y8 = [10 ** ((2.788 * ln_x) - 19.463) for ln_x in [np.log(val + 1200) for val in x]] - - x_dates = pd.date_range(df_data.index[0], dend, freq="d") - - color_list = [ - "rgba(255, 0, 0, 0.7)", - "rgba(255, 165, 0, 0.7)", - "rgba(255, 255, 0, 0.7)", - "rgba(0, 128, 0, 0.7)", - "rgba(0, 0, 255, 0.7)", - "rgba(238, 130, 238, 0.7)", - "rgba(75, 0, 130, 0.7)", - "rgba(128, 0, 128, 0.7)", - ] - labels = [ - "Bubble bursting imminent!!", - "SELL!", - "Everyone FOMO'ing....", - "Is this a bubble??", - "Still cheap", - "Accumulate", - "BUY!", - "Basically a Fire Sale", - "Bitcoin Price", - ] - for i, (ytop, ybottom, color, label) in enumerate( - zip( - [y1, y2, y3, y4, y5, y6, y7, y8], - [y0, y1, y2, y3, y4, y5, y6, y7], - color_list, - labels, - ) - ): - fig.add_scatter( - x=x_dates, - y=ytop, - mode="lines", - line_color=color, - showlegend=False, - name="", - ) - fig.add_scatter( - x=x_dates, - y=ybottom, - mode="lines", - fill="tonexty", - fillcolor=color, - line_color=color, - name=label, - ) - - fig.add_scatter( - x=df_data.index, - y=df_data["v"].values, - mode="lines", - line_color=theme.get_colors()[0], - name="Bitcoin Price", - ) - - sample_dates = [ - datetime(2012, 11, 28), - datetime(2016, 7, 9), - datetime(2020, 5, 11), - datetime(2024, 4, 4), - ] - - for i, date in enumerate(sample_dates): - if d0 < date < dend: - fig.add_vline(x=date, line_width=2, line_color="grey") - fig.add_annotation( - x=date, - y=min(y0), - text=f"Halving {i+1}", - textangle=90, - xshift=10, - yshift=-120, - font=dict(color="grey"), - ) - - fig.update_layout(legend=dict(traceorder="normal"), yaxis_type="log") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "btcrb", - df_data, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/overview/loanscan_model.py b/openbb_terminal/cryptocurrency/overview/loanscan_model.py deleted file mode 100644 index 047d80a239d..00000000000 --- a/openbb_terminal/cryptocurrency/overview/loanscan_model.py +++ /dev/null @@ -1,336 +0,0 @@ -"""LoanScan Model""" - -import argparse -import logging -from typing import Any, Dict - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, log_and_raise, request - -logger = logging.getLogger(__name__) - -api_url = "https://api.loanscan.io" - -PLATFORMS = [ - "MakerDao", - "Compound", - "Poloniex", - "Bitfinex", - "dYdX", - "CompoundV2", - "Linen", - "Hodlonaut", - "InstaDapp", - "Zerion", - "Argent", - "DeFiSaver", - "MakerDaoV2", - "Ddex", - "AaveStable", - "AaveVariable", - "YearnFinance", - "BlockFi", - "Nexo", - "CryptoCom", - "Soda", - "Coinbase", - "SaltLending", - "Ledn", - "Bincentive", - "Inlock", - "Bitwala", - "Zipmex", - "Vauld", - "Delio", - "Yield", - "Vesper", - "Reflexer", - "SwissBorg", - "MushroomsFinance", - "ElementFi", - "Maple", - "CoinRabbit", - "WirexXAccounts", - "Youhodler", - "YieldApp", - "NotionalFinance", - "IconFi", -] - -CRYPTOS = [ - "ZRX", - "BAT", - "REP", - "ETH", - "SAI", - "BTC", - "XRP", - "LTC", - "EOS", - "BCH", - "XMR", - "DOGE", - "USDC", - "USDT", - "BSV", - "NEO", - "ETC", - "OMG", - "ZEC", - "BTG", - "SAN", - "DAI", - "UNI", - "WBTC", - "COMP", - "LUNA", - "UST", - "BUSD", - "KNC", - "LEND", - "LINK", - "MANA", - "MKR", - "SNX", - "SUSD", - "TUSD", - "eCRV-DAO", - "HEGIC", - "YFI", - "1INCH", - "CRV-IB", - "CRV-HBTC", - "BOOST", - "CRV-sBTC", - "CRV-renBTC", - "CRV-sAave", - "CRV-oBTC", - "CRV-pBTC", - "CRV-LUSD", - "CRV-BBTC", - "CRV-tBTC", - "CRV-FRAX", - "CRV-yBUSD", - "CRV-COMP", - "CRV-GUSD", - "yUSD", - "CRV-3pool", - "CRV-TUSD", - "CRV-BUSD", - "CRV-DUSD", - "CRV-UST", - "CRV-mUSD", - "sUSD", - "CRV-sUSD", - "CRV-LINK", - "CRV-USDN", - "CRV-USDP", - "CRV-alUSD", - "CRV-Aave", - "CRV-HUSD", - "CRV-EURS", - "RAI", - "CRV-triCrypto", - "CRV-Pax", - "CRV-USDT", - "CRV-USDK", - "CRV-RSV", - "CRV-3Crypto", - "GUSD", - "PAX", - "USD", - "ILK", - "BNB", - "PAXG", - "ADA", - "FTT", - "SOL", - "SRM", - "RAY", - "XLM", - "SUSHI", - "CRV", - "BAL", - "AAVE", - "MATIC", - "GRT", - "ENJ", - "USDP", - "IOST", - "AMP", - "PERP", - "SHIB", - "ALICE", - "ALPHA", - "ANKR", - "ATA", - "AVA", - "AXS", - "BAKE", - "BAND", - "BNT", - "BTCST", - "CELR", - "CFX", - "CHR", - "COS", - "COTI", - "CTSI", - "DUSK", - "EGLD", - "ELF", - "FET", - "FLOW", - "FTM", - "INJ", - "IOTX", - "MDX", - "NEAR", - "OCEAN", - "ONT", - "POLS", - "REEF", - "WRX", - "XEC", - "XTZ", - "XVS", - "ZIL", - "DOT", - "FIL", - "TRX", - "CAKE", - "ADX", - "FIRO", - "SXP", - "ATOM", - "IOTA", - "AKRO", - "AUDIO", - "BADGER", - "CVC", - "DENT", - "DYDX", - "FORTH", - "GNO", - "HOT", - "LPT", - "LRC", - "NKN", - "NMR", - "NU", - "OGN", - "OXT", - "POLY", - "QNT", - "RLC", - "RSR", - "SAND", - "SKL", - "STMX", - "STORJ", - "TRB", - "UMA", - "DPI", - "VSP", - "CHSB", - "EURT", - "GHST", - "3CRV", - "CRVRENWBTC", - "MIR-UST UNI LP", - "ALCX", - "ALUSD", - "USDP3CRV", - "RENBTC", - "YVECRV", - "CVX", - "USDTTRC20", - "AUD", - "HKD", - "GBP", - "EUR", - "HUSD", - "HT", - "DASH", - "EURS", - "AVAX", - "BTT", - "GALA", - "ILV", - "APE", -] - - -@log_start_end(log=logger) -def get_rates(rate_type: str = "borrow") -> pd.DataFrame: - """Returns crypto {borrow,supply} interest rates for cryptocurrencies across several platforms - [Source: https://loanscan.io/] - - Parameters - ---------- - rate_type : str - Interest rate type: {borrow, supply}. Default: supply - - Returns - ------- - pd.DataFrame - crypto interest rates per platform - """ - if rate_type not in ("supply", "borrow"): - raise Exception("Rate type not supported. Supported rates: borrow, supply") - - response = request( - f"{api_url}/v1/interest-rates", - headers={"User-Agent": get_user_agent()}, - ) - if response.status_code != 200: - raise Exception(f"Status code: {response.status_code}. Reason: {response.text}") - - data = response.json() - cryptos: Dict[Any, Any] = {} - for provider in data: - provider_name = provider["provider"].lower() - for crypto in provider[rate_type]: - symbol = crypto["symbol"] - if symbol not in cryptos: - cryptos[symbol] = {} - cryptos[symbol][provider_name] = crypto["rate"] - df = pd.DataFrame(cryptos, columns=sorted(cryptos.keys())) - for platform in PLATFORMS: - if platform.lower() not in df.index: - df = pd.concat( - [df, pd.Series(name=platform.lower(), dtype="object")], - axis=0, - join="outer", - ) - return df - - -def check_valid_coin(value) -> str: - """Argparse type to check valid coins argument""" - cryptos = value.split(",") - cryptos_lowered = [x.lower() for x in CRYPTOS] - for crypto in cryptos: - if crypto.lower() not in cryptos_lowered: - log_and_raise( - argparse.ArgumentTypeError( - f"{crypto} is not supported. Options: {','.join(CRYPTOS)}" - ) - ) - return value - - -def check_valid_platform(value) -> str: - """Argparse type to check valid platform argument""" - platforms = value.split(",") - platforms_lowered = [x.lower() for x in PLATFORMS] - for platform in platforms: - if platform.lower() not in platforms_lowered: - log_and_raise( - argparse.ArgumentTypeError( - f"{platform} is not supported. Options: {','.join(PLATFORMS)}" - ) - ) - return value diff --git a/openbb_terminal/cryptocurrency/overview/loanscan_view.py b/openbb_terminal/cryptocurrency/overview/loanscan_view.py deleted file mode 100644 index aaf8c6b83c7..00000000000 --- a/openbb_terminal/cryptocurrency/overview/loanscan_view.py +++ /dev/null @@ -1,108 +0,0 @@ -"""LoanScan view""" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.overview import loanscan_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_crypto_rates( - symbols: str, - platforms: str, - rate_type: str = "borrow", - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Displays crypto {borrow,supply} interest rates for cryptocurrencies across several platforms - [Source: https://loanscan.io/] - - Parameters - ---------- - rate_type: str - Interest rate type: {borrow, supply}. Default: supply - symbols: str - Crypto separated by commas. Default: BTC,ETH,USDT,USDC - platforms: str - Platforms separated by commas. Default: BlockFi,Ledn,SwissBorg,Youhodler - limit: int - Number of records to show - export : str - Export dataframe data to csv,json,xlsx file - """ - df = loanscan_model.get_rates(rate_type=rate_type) - if df.empty: - return console.print("\nError in loanscan request\n") - - valid_platforms = [ - platform for platform in platforms.lower().split(",") if platform in df.index - ] - df = df[symbols.upper().split(",")].loc[valid_platforms] - df = df.sort_values(df.columns[0], ascending=False, na_position="last") - - fig = OpenBBFigure.create_subplots( - 1, - 3, - specs=[[{"type": "domain"}, {"type": "bar", "colspan": 2}, None]], - column_widths=[0.1, 0.8, 0.1], - ) - - df_non_null = pd.melt(df.reset_index(), id_vars=["index"]).dropna() - - assets = df_non_null.variable.unique().tolist() - colors = iter(theme.get_colors(reverse=True)) - - for asset in assets: - width = df_non_null.loc[(df_non_null.variable == asset)] - # silence Setcopywarnings - pd.options.mode.chained_assignment = None - width["id"] = width["index"] + width["variable"] - - fig.add_bar( - x=width.value * 100, - y=width["id"], - orientation="h", - name=asset, - hovertext=width.value * 100, - hovertemplate="%{hovertext:.2f}%", - marker_color=next(colors), - row=1, - col=2, - ) - - fig.update_layout( - title=f"Cryptos {rate_type} interest rate", - xaxis_title="Rate (%)", - yaxis=dict(side="left", title="Platform"), - legend=dict(yanchor="bottom", y=0.01, xanchor="right", x=1), - ) - - df = df.fillna("-") - df = df.applymap(lambda x: str(round(100 * x, 2)) + "%" if x != "-" else x) - - print_rich_table( - df, - headers=list(df.columns), - index_name="Platform", - show_index=True, - title=f"Crypto {rate_type.capitalize()} Interest Rates", - export=bool(export), - limit=limit, - ) - - export_data( - export, os.path.dirname(os.path.abspath(__file__)), "cr", df, sheet_name, fig - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/overview/overview_controller.py b/openbb_terminal/cryptocurrency/overview/overview_controller.py deleted file mode 100644 index 733887e86c1..00000000000 --- a/openbb_terminal/cryptocurrency/overview/overview_controller.py +++ /dev/null @@ -1,1575 +0,0 @@ -"""Cryptocurrency Overview Controller""" - -__docformat__ = "numpy" - -# pylint: disable=R0904, C0302, W0622 -import argparse -import difflib -import logging -from datetime import datetime, timedelta -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.discovery.pycoingecko_model import ( - get_categories_keys, -) -from openbb_terminal.cryptocurrency.overview import ( - blockchaincenter_view, - coinbase_model, - coinbase_view, - coinpaprika_model, - coinpaprika_view, - cryptopanic_model, - cryptopanic_view, - loanscan_model, - loanscan_view, - pycoingecko_model, - pycoingecko_view, - rekt_model, - rekt_view, - tokenterminal_model, - tokenterminal_view, - withdrawalfees_model, - withdrawalfees_view, -) -from openbb_terminal.cryptocurrency.overview.blockchaincenter_model import DAYS -from openbb_terminal.cryptocurrency.overview.coinpaprika_model import ( - get_all_contract_platforms, -) -from openbb_terminal.cryptocurrency.overview.coinpaprika_view import CURRENCIES -from openbb_terminal.cryptocurrency.overview.glassnode_view import display_btc_rainbow -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class OverviewController(BaseController): - """Overview Controller class""" - - CHOICES_COMMANDS = [ - "hm", - "global", - "defi", - "stables", - "exchanges", - "exrates", - "indexes", - "derivatives", - "categories", - "hold", - "markets", - "exmarkets", - "info", - "platforms", - "contracts", - "pairs", - "news", - "wf", - "ewf", - "wfpe", - "btcrb", - "altindex", - "ch", - "cr", - "fun", - ] - - PATH = "/crypto/ov/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - choices["wfpe"].update( - {c: {} for c in withdrawalfees_model.POSSIBLE_CRYPTOS} - ) - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("crypto/ov/", 105) - mt.add_cmd("global") - mt.add_cmd("defi") - mt.add_cmd("stables") - mt.add_cmd("exchanges") - mt.add_cmd("exrates") - mt.add_cmd("indexes") - mt.add_cmd("derivatives") - mt.add_cmd("categories") - mt.add_cmd("hold") - mt.add_cmd("hm") - mt.add_cmd("info") - mt.add_cmd("markets") - mt.add_cmd("exmarkets") - mt.add_cmd("platforms") - mt.add_cmd("contracts") - mt.add_cmd("pairs") - mt.add_cmd("news") - mt.add_cmd("wf") - mt.add_cmd("ewf") - mt.add_cmd("wfpe") - mt.add_cmd("altindex") - mt.add_cmd("btcrb") - mt.add_cmd("ch") - mt.add_cmd("cr") - mt.add_cmd("fun") - console.print(text=mt.menu_text, menu="Cryptocurrency - Overview") - - @log_start_end(log=logger) - def call_hm(self, other_args): - """Process hm command""" - parser = argparse.ArgumentParser( - prog="hm", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Display cryptocurrencies heatmap with daily percentage change [Source: https://coingecko.com] - Accepts --category or -c to display only coins of a certain category - (default no category to display all coins ranked by market cap). - You can look on only top N number of records with --limit. - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=int, - help="Display N items", - default=10, - ) - parser.add_argument( - "-c", - "--category", - default="", - dest="category", - help="Category (e.g., stablecoins). Empty for no category", - choices=get_categories_keys(), - metavar="CATEGORY", - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_crypto_heatmap( - category=ns_parser.category, - limit=ns_parser.limit, - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_fun(self, other_args): - """Process fun command""" - parser = argparse.ArgumentParser( - prog="fun", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Display fundamental metrics overview [Source: Token Terminal]""", - ) - parser.add_argument( - "-m", - "--metric", - required=True, - choices=tokenterminal_model.METRICS, - dest="metric", - help="Choose metric of interest", - ) - parser.add_argument( - "-c", - "--category", - default="", - choices=tokenterminal_model.CATEGORIES, - dest="category", - help="Choose category of interest", - ) - parser.add_argument( - "-t", - "--timeline", - default="24h", - choices=tokenterminal_model.TIMELINES, - dest="timeline", - help="Choose timeline of interest", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=int, - help="Display N items", - default=10, - ) - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-m") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - tokenterminal_view.display_fundamental_metrics( - metric=ns_parser.metric, - category=ns_parser.category, - timeline=ns_parser.timeline, - ascend=ns_parser.reverse, - limit=ns_parser.limit, - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_ch(self, other_args): - """Process ch command""" - parser = argparse.ArgumentParser( - prog="ch", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Display list of major crypto-related hacks [Source: https://rekt.news] - Can be sorted by {Platform,Date,Amount [$],Audit,Slug,URL} with --sortby - and reverse the display order with --reverse - Show only N elements with --limit - Accepts --slug or -s to check individual crypto hack (e.g., -s polynetwork-rekt) - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=int, - help="Display N items", - default=15, - ) - parser.add_argument( - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: Amount [$]", - default="Amount [$]", - nargs="+", - choices=rekt_model.HACKS_COLUMNS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--slug", - dest="slug", - type=str, - help="Slug to check crypto hack (e.g., polynetwork-rekt)", - default="", - choices=rekt_model.get_crypto_hack_slugs(), - metavar="SORTBY", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - rekt_view.display_crypto_hacks( - slug=ns_parser.slug, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=" ".join(ns_parser.sortby), - ascend=not ns_parser.reverse, - ) - - @log_start_end(log=logger) - def call_btcrb(self, other_args: List[str]): - """Process btcrb command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="btcrb", - description="""Display bitcoin rainbow chart overtime including halvings. - [Price data from source: https://glassnode.com] - [Inspired by: https://blockchaincenter.net]""", - ) - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help="Initial date. Default is initial BTC date: 2010-01-01", - default=datetime(2010, 1, 1).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help="Final date. Default is current date", - default=datetime.now().strftime("%Y-%m-%d"), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - display_btc_rainbow( - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_altindex(self, other_args: List[str]): - """Process altindex command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="altindex", - description="""Display altcoin index overtime. - If 75% of the Top 50 coins performed better than Bitcoin over periods of time - (30, 90 or 365 days) it is Altcoin Season. Excluded from the Top 50 are - Stablecoins (Tether, DAI…) and asset backed tokens (WBTC, stETH, cLINK,…) - [Source: https://blockchaincenter.net] - """, - ) - - parser.add_argument( - "-p", - "--period", - type=int, - help="Period of time to check if how altcoins have performed against btc (30, 90, 365)", - dest="period", - default=365, - choices=DAYS, - ) - - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help="Start date (default: 1 year before, e.g., 2021-01-01)", - default=(datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help="Final date. Default is current date", - default=datetime.now().strftime("%Y-%m-%d"), - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - blockchaincenter_view.display_altcoin_index( - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - period=ns_parser.period, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - 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, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - withdrawalfees_view.display_overall_withdrawal_fees( - limit=ns_parser.limit, export=ns_parser.export - ) - - @log_start_end(log=logger) - 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/] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - withdrawalfees_view.display_overall_exchange_withdrawal_fees( - export=ns_parser.export - ) - - @log_start_end(log=logger) - 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)", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.coin: - if ns_parser.coin in withdrawalfees_model.POSSIBLE_CRYPTOS: - withdrawalfees_view.display_crypto_withdrawal_fees( - symbol=ns_parser.coin, export=ns_parser.export - ) - else: - console.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: - console.print(f"Replacing by '{similar_cmd[0]}'") - withdrawalfees_view.display_crypto_withdrawal_fees( - symbol=similar_cmd[0], export=ns_parser.export - ) - else: - similar_cmd = difflib.get_close_matches( - ns_parser.coin, - withdrawalfees_model.POSSIBLE_CRYPTOS, - n=1, - cutoff=0.5, - ) - if similar_cmd: - console.print(f"Did you mean '{similar_cmd[0]}'?") - else: - console.print( - f"Couldn't find any coin with provided name: {ns_parser.coin}. " - f"Please choose one from list: {withdrawalfees_model.POSSIBLE_CRYPTOS}\n" - ) - - @log_start_end(log=logger) - def call_hold(self, other_args): - """Process hold command""" - parser = argparse.ArgumentParser( - prog="hold", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows overview of public companies that holds ethereum or bitcoin. - You can find there most important metrics like: - Total Bitcoin Holdings, Total Value (USD), Public Companies Bitcoin Dominance, Companies - """, - ) - - parser.add_argument( - "-c", - "--coin", - dest="coin", - type=str, - help="companies with ethereum or bitcoin", - default="bitcoin", - choices=pycoingecko_model.HOLD_COINS, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number of records", - default=5, - ) - parser.add_argument( - "--bar", - action="store_true", - help="Flag to show bar chart", - dest="bar", - default=False, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_holdings_overview( - symbol=ns_parser.coin, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - show_bar=ns_parser.bar, - limit=ns_parser.limit, - ) - - @log_start_end(log=logger) - def call_categories(self, other_args): - """Process top_categories command""" - parser = argparse.ArgumentParser( - prog="categories", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Shows top cryptocurrency categories by market capitalization. It includes categories like: - stablecoins, defi, solana ecosystem, polkadot ecosystem and many others. - You can sort by {}, using --sortby parameter""", - ) - - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number of records", - default=15, - ) - - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: market_cap_desc", - default="Market_Cap", - choices=[ - "Name", - "Market_Cap", - "Market_Cap_Change_24H", - "Top_3_Coins", - "Volume_24H", - ], - ) - - parser.add_argument( - "--pie", - action="store_true", - help="Flag to show pie chart", - dest="pie", - default=False, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_categories( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=ns_parser.sortby, - pie=ns_parser.pie, - ) - - # TODO: solve sort (similar to losers from discovery) - @log_start_end(log=logger) - def call_stables(self, other_args): - """Process stables command""" - parser = argparse.ArgumentParser( - prog="stables", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Shows stablecoins by market capitalization. - Stablecoins are cryptocurrencies that attempt to peg their market value to some external reference - like the U.S. dollar or to a commodity's price such as gold. - You can display only N number of coins with --limit parameter. - You can sort data by {} with --sortby""", - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=15, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: market_cap", - default="Market_Cap_[$]", - choices=[ - "Symbol", - "Name", - "Price_[$]", - "Market_Cap_[$]", - "Market_Cap_Rank", - "Change_7d_[%]", - "Change_24h_[%]", - "Volume_[$]", - ], - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "--pie", - action="store_true", - help="Flag to show pie chart", - dest="pie", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_stablecoins( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - pie=ns_parser.pie, - ) - - @log_start_end(log=logger) - def call_cr(self, other_args): - """Process cr command""" - parser = argparse.ArgumentParser( - prog="cr", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Displays crypto {borrow,supply} interest rates for cryptocurrencies across several platforms. - You can select rate type with --type {borrow,supply} - You can display only N number of platforms with --limit parameter.""", - ) - - parser.add_argument( - "-t", - "--type", - dest="type", - type=str, - help="Select interest rate type", - default="supply", - choices=["borrow", "supply"], - ) - parser.add_argument( - "-c", - "--cryptocurrrencies", - dest="cryptos", - type=loanscan_model.check_valid_coin, - help=f"""Cryptocurrencies to search interest rates for separated by comma. - Default: BTC,ETH,USDT,USDC. Options: {",".join(loanscan_model.CRYPTOS)}""", - default="BTC,ETH,USDT,USDC", - choices=loanscan_model.CRYPTOS, - metavar="CRYPTOS", - ) - - parser.add_argument( - "-p", - "--platforms", - dest="platforms", - type=loanscan_model.check_valid_platform, - help=f"""Platforms to search interest rates in separated by comma. - Default: BlockFi,Ledn,SwissBorg,Youhodler. Options: {",".join(loanscan_model.PLATFORMS)}""", - default="BlockFi,Ledn,SwissBorg,Youhodler", - choices=loanscan_model.PLATFORMS, - metavar="PLATFORMS", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10 - ) - if ns_parser: - loanscan_view.display_crypto_rates( - rate_type=ns_parser.type, - symbols=ns_parser.cryptos, - platforms=ns_parser.platforms, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_exchanges(self, other_args): - """Process exchanges command""" - filters = ( - pycoingecko_model.EXCHANGES_FILTERS + coinpaprika_model.EXCHANGES_FILTERS - ) - parser = argparse.ArgumentParser( - prog="exchanges", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Shows Top Crypto Exchanges - You can display only N number exchanges with --limit parameter. - You can sort data by Trust_Score, Id, Name, Country, Year_Established, Trade_Volume_24h_BTC with --sortby - Or you can sort data by 'name', 'currencies', 'markets', 'fiats', 'confidence', - 'volume_24h', 'volume_7d', 'volume_30d', 'sessions_per_month' - if you are using the alternative source CoinPaprika - and also with --reverse flag to sort ascending. - Flag --urls will display urls. - Displays: Trust_Score, Id, Name, Country, Year_Established, Trade_Volume_24h_BTC""", - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=15, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: Rank", - default="Rank", - choices=filters, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-u", - "--urls", - dest="urls", - action="store_true", - help="Flag to add a url column. Works only with CoinGecko source", - default=False, - ) - parser.add_argument( - "--vs", - help="Quoted currency. Default: USD. Works only with CoinPaprika source", - dest="vs", - default="USD", - type=str, - choices=CURRENCIES, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.source == "CoinGecko": - pycoingecko_view.display_exchanges( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - links=ns_parser.urls, - ) - elif ns_parser.source == "CoinPaprika": - coinpaprika_view.display_all_exchanges( - symbol=ns_parser.vs, - limit=ns_parser.limit, - ascend=ns_parser.reverse, - sortby=ns_parser.sortby, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_exrates(self, other_args): - """Process exchange_rates command""" - parser = argparse.ArgumentParser( - prog="exrates", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Shows list of crypto, fiats, commodity exchange rates from CoinGecko - You can look on only N number of records with --limit, - You can sort by Index, Name, Unit, Value, Type, and also use --reverse flag to sort descending.""", - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=15, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: Index", - default="Index", - choices=pycoingecko_model.EXRATES_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in ascending order by default. " - "Reverse flag will sort it in an descending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_exchange_rates( - sortby=ns_parser.sortby, - limit=ns_parser.limit, - ascend=not ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_indexes(self, other_args): - """Process indexes command""" - parser = argparse.ArgumentParser( - prog="indexes", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Shows list of crypto indexes from CoinGecko. - Each crypto index is made up of a selection of cryptocurrencies, - grouped together and weighted by market cap. - You can display only N number of indexes with --limit parameter. - You can sort data by Rank, Name, Id, Market, Last, MultiAsset with --sortby - and also with --reverse flag to sort descending. - Displays: Rank, Name, Id, Market, Last, MultiAsset - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=15, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: Rank", - default="Rank", - choices=pycoingecko_model.INDEXES_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in ascending order by default. " - "Reverse flag will sort it in an descending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_indexes( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=not ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_derivatives(self, other_args): - """Process derivatives command""" - parser = argparse.ArgumentParser( - prog="derivatives", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Shows list of crypto derivatives from CoinGecko - Crypto derivatives are secondary contracts or financial tools that derive their value from a primary - underlying asset. In this case, the primary asset would be a cryptocurrency such as Bitcoin. - The most popular crypto derivatives are crypto futures, crypto options, and perpetual contracts. - You can look on only N number of records with --limit, - You can sort by Rank, Market, Symbol, Price, Pct_Change_24h, Contract_Type, Basis, Spread, Funding_Rate, - Volume_24h with by and also with --reverse flag to set it to sort descending. - Displays: - Rank, Market, Symbol, Price, Pct_Change_24h, Contract_Type, Basis, Spread, Funding_Rate, Volume_24h - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=15, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: Rank", - default="Rank", - choices=pycoingecko_model.DERIVATIVES_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in ascending order by default. " - "Reverse flag will sort it in an descending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_derivatives( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=not ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_global(self, other_args): - """Process global command""" - parser = argparse.ArgumentParser( - prog="global", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Shows global statistics about Crypto Market""", - ) - - parser.add_argument( - "--pie", - action="store_true", - help="Flag to show pie chart with market cap distribution. Works only with CoinGecko source", - dest="pie", - default=False, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.source == "CoinGecko": - pycoingecko_view.display_global_market_info( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - pie=ns_parser.pie, - ) - elif ns_parser.source == "CoinPaprika": - coinpaprika_view.display_global_market( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_defi(self, other_args): - """Process defi command""" - parser = argparse.ArgumentParser( - prog="defi", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Shows global DeFi statistics - DeFi or Decentralized Finance refers to financial services that are built - on top of distributed networks with no central intermediaries. - Displays metrics like: - Market Cap, Trading Volume, Defi Dominance, Top Coins...""", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - pycoingecko_view.display_global_defi_info( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_markets(self, other_args): - """Process markets command""" - parser = argparse.ArgumentParser( - prog="markets", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Show market related (price, supply, volume) coin information for all coins on CoinPaprika. - You can display only N number of coins with --limit parameter. - You can sort data by rank, name, symbol, price, volume_24h, mcap_change_24h, pct_change_1h, pct_change_24h, - ath_price, pct_from_ath, --sortby parameter and also with --reverse flag to sort ascending. - Displays: - rank, name, symbol, price, volume_24h, mcap_change_24h, - pct_change_1h, pct_change_24h, ath_price, pct_from_ath, - """, - ) - parser.add_argument( - "--vs", - help="Quoted currency. Default USD", - dest="vs", - default="USD", - type=str, - choices=CURRENCIES, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=15, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: rank", - default="rank", - choices=coinpaprika_model.MARKETS_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - coinpaprika_view.display_all_coins_market_info( - symbol=ns_parser.vs, - limit=ns_parser.limit, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=ns_parser.sortby, - ) - - @log_start_end(log=logger) - def call_exmarkets(self, other_args): - """Process exmarkets command""" - parser = argparse.ArgumentParser( - prog="exmarkets", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Get all exchange markets found for given exchange - You can display only N number of records with --limit parameter. - You can sort data by pair, base_currency_name, quote_currency_name, market_url, category, - reported_volume_24h_share, trust_score --sortby parameter and also with --reverse flag to sort ascending. - You can use additional flag --urls to see urls for each market - Displays: - exchange_id, pair, base_currency_name, quote_currency_name, market_url, - category, reported_volume_24h_share, trust_score,""", - ) - parser.add_argument( - "-e", - "--exchange", - help="Identifier of exchange e.g for Binance Exchange -> binance", - dest="exchange", - default="binance", - type=str, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=10, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: reported_volume_24h_share", - default="reported_volume_24h_share", - choices=coinpaprika_model.EXMARKETS_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-u", - "--urls", - dest="urls", - action="store_true", - help="""Flag to show urls. If you will use that flag you will see only: - exchange, pair, trust_score, market_url columns""", - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-e") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - coinpaprika_view.display_exchange_markets( - exchange=ns_parser.exchange, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - links=ns_parser.urls, - ) - - @log_start_end(log=logger) - def call_info(self, other_args): - """Process info command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="info", - description="""Show basic coin information for all coins from CoinPaprika API - You can display only N number of coins with --limit parameter. - You can sort data by rank, name, symbol, price, volume_24h, circulating_supply, - total_supply, max_supply, market_cap, beta_value, ath_price --sortby parameter - and also with --reverse flag to sort descending. - - Displays: - rank, name, symbol, price, volume_24h, circulating_supply, - total_supply, max_supply, market_cap, beta_value, ath_price - """, - ) - parser.add_argument( - "--vs", - help="Quoted currency. Default USD", - dest="vs", - default="USD", - type=str, - choices=CURRENCIES, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=20, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: rank", - default="rank", - choices=coinpaprika_model.INFO_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in ascending order by default. " - "Reverse flag will sort it in an descending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - coinpaprika_view.display_all_coins_info( - symbol=ns_parser.vs, - limit=ns_parser.limit, - ascend=not ns_parser.reverse, - sortby=ns_parser.sortby, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_platforms(self, other_args): - """Process platforms command""" - parser = argparse.ArgumentParser( - prog="platforms", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""List all smart contract platforms like ethereum, solana, cosmos, polkadot, kusama""", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - coinpaprika_view.display_all_platforms( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_contracts(self, other_args): - """Process contracts command""" - platforms = get_all_contract_platforms()["platform_id"].tolist() - - parser = argparse.ArgumentParser( - prog="contracts", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Gets all contract addresses for given platform. - Provide platform id with -p/--platform parameter - You can display only N number of smart contracts with --limit parameter. - You can sort data by id, type, active, balance --sortby parameter - and also with --reverse flag to sort descending. - - Displays: - id, type, active, balance - """, - ) - parser.add_argument( - "-p", - "--platform", - help="Blockchain platform like eth-ethereum", - dest="platform", - default="eth-ethereum", - type=str, - choices=platforms, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=15, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column", - default="id", - choices=coinpaprika_model.CONTRACTS_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in ascending order by default. " - "Reverse flag will sort it in an descending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - coinpaprika_view.display_contracts( - symbol=ns_parser.platform, - limit=ns_parser.limit, - ascend=not ns_parser.reverse, - sortby=ns_parser.sortby, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_pairs(self, other_args): - """Process pairs command""" - parser = argparse.ArgumentParser( - prog="pairs", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Shows available trading pairs on Coinbase ", - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=int, - help="display N number of pairs >=10", - default=15, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: id", - default="id", - choices=coinbase_model.PAIRS_FILTERS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in ascending order by default. " - "Reverse flag will sort it in an descending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - coinbase_view.display_trading_pairs( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=ns_parser.sortby, - ascend=not ns_parser.reverse, - ) - - @log_start_end(log=logger) - def call_news(self, other_args): - """Process news command""" - parser = argparse.ArgumentParser( - prog="news", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Display recent news from CryptoPanic aggregator platform. [Source: https://cryptopanic.com/]", - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="display N number records", - default=20, - ) - parser.add_argument( - "-k", - "--kind", - dest="kind", - type=str, - help="Filter by category of news. Available values: news or media.", - default="news", - choices=cryptopanic_model.CATEGORIES, - ) - parser.add_argument( - "--filter", - dest="filter", - type=str, - help="Filter by kind of news. One from list: rising|hot|bullish|bearish|important|saved|lol", - default=None, - required=False, - choices=cryptopanic_model.FILTERS, - ) - parser.add_argument( - "-r", - "--region", - dest="region", - type=str, - help="Filter news by regions. Available regions are: en (English), de (Deutsch), nl (Dutch), es (Español), " - "fr (Français), it (Italiano), pt (Português), ru (Русский)", - default="en", - choices=cryptopanic_model.REGIONS, - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - help="Sort by given column. Default: published_at", - default="published_at", - choices=cryptopanic_model.SORT_FILTERS, - ) - parser.add_argument( - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-u", - "--urls", - dest="urls", - action="store_true", - help="Flag to show urls column.", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cryptopanic_view.display_news( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - links=ns_parser.urls, - post_kind=ns_parser.kind, - filter_=ns_parser.filter, - region=ns_parser.region, - ) diff --git a/openbb_terminal/cryptocurrency/overview/pycoingecko_model.py b/openbb_terminal/cryptocurrency/overview/pycoingecko_model.py deleted file mode 100644 index f7f98058257..00000000000 --- a/openbb_terminal/cryptocurrency/overview/pycoingecko_model.py +++ /dev/null @@ -1,535 +0,0 @@ -"""CoinGecko model""" - -__docformat__ = "numpy" - -# pylint: disable=C0301, E1101 -# pylint: disable=unsupported-assignment-operation - -import contextlib -import logging -import re -from typing import List, Union - -import numpy as np -import pandas as pd -from pycoingecko import CoinGeckoAPI - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - create_df_index, - lambda_long_number_format_with_type_check, - lambda_replace_underscores_in_column_names, -) -from openbb_terminal.cryptocurrency.discovery.pycoingecko_model import get_coins -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) -# pylint: disable=unsupported-assignment-operation - -HOLD_COINS = ["ethereum", "bitcoin"] - -NEWS_FILTERS = ["Index", "Title", "Author", "Posted"] - -CATEGORIES_FILTERS = [ - "Rank", - "Name", - "Change_1h", - "Change_24h", - "Change_7d", - "Market_Cap", - "Volume_24h", - "Coins", -] - -STABLES_FILTERS = [ - "Rank", - "Name", - "Symbol", - "Price", - "Change_24h", - "Exchanges", - "Market_Cap", - "Change_30d", -] - -PRODUCTS_FILTERS = [ - "Rank", - "Platform", - "Identifier", - "Supply_Rate", - "Borrow_Rate", -] - -PLATFORMS_FILTERS = ["Rank", "Name", "Category", "Centralized"] - -EXCHANGES_FILTERS = [ - "Rank", - "Trust_Score", - "Id", - "Name", - "Country", - "Year Established", - "Trade_Volume_24h_BTC", -] - -EXRATES_FILTERS = ["Index", "Name", "Unit", "Value", "Type"] - -INDEXES_FILTERS = ["Rank", "Name", "Id", "Market", "Last", "MultiAsset"] - -DERIVATIVES_FILTERS = [ - "Rank", - "Market", - "Symbol", - "Price", - "Pct_Change_24h", - "Contract_Type", - "Basis", - "Spread", - "Funding_Rate", - "Volume_24h", -] - -COINS_COLUMNS = [ - "symbol", - "name", - "current_price", - "market_cap", - "market_cap_rank", - "price_change_percentage_7d_in_currency", - "price_change_percentage_24h_in_currency", - "total_volume", -] - - -@log_start_end(log=logger) -def get_holdings_overview(endpoint: str = "bitcoin") -> List[Union[str, pd.DataFrame]]: - """Returns public companies that holds ethereum or bitcoin [Source: CoinGecko] - - Parameters - ---------- - endpoint : str - "bitcoin" or "ethereum" - - Returns - ------- - List[Union[str, pd.DataFrame]] - - str: Overall statistics - - pd.DataFrame: Companies holding crypto - """ - cg = CoinGeckoAPI() - data = cg.get_companies_public_treasury_by_coin_id(coin_id=endpoint) - - stats_str = f"""{len(data["companies"])} companies hold a total of {lambda_long_number_format_with_type_check(data["total_holdings"])} {endpoint} ({data["market_cap_dominance"]}% of market cap dominance) with the current value of {lambda_long_number_format_with_type_check(int(data["total_value_usd"]))} USD dollars""" # noqa - - df = pd.json_normalize(data, record_path=["companies"]) - - df.columns = list( - map( - lambda x: ( - lambda_replace_underscores_in_column_names(x) - if isinstance(x, str) - else x - ), - df.columns, - ) - ) - - return [stats_str, df] - - -SORT_VALUES = ["market_cap", "name", "market_cap_change_24h"] - - -def lambda_coin_formatter(n): - # TODO: can be improved - coins = [] - re_str = "small/(.*)(.jpg|.png|.JPG|.PNG)" - for coin in n: - if re.search(re_str, coin): - coin_stripped = re.search(re_str, coin).group(1) - coins.append(coin_stripped) - return ",".join(coins) - - -@log_start_end(log=logger) -def get_top_crypto_categories(sort_filter: str = SORT_VALUES[0]) -> pd.DataFrame: - """Returns top crypto categories [Source: CoinGecko] - - Parameters - ---------- - sort_filter : str - Can be one of - "market_cap_desc", "market_cap_asc", "name_desc", "name_asc", - "market_cap_change_24h_desc", "market_cap_change_24h_asc" - - Returns - ------- - pd.DataFrame - Rank, Name, Change_1h, Change_7d, Market_Cap, Volume_24h,Coins, Url - """ - if sort_filter.lower() in SORT_VALUES: - client = CoinGeckoAPI() - data = client.get_coins_categories() - df = pd.DataFrame(data) - del df["id"] - del df["content"] - del df["updated_at"] - df["top_3_coins"] = df["top_3_coins"].apply(lambda_coin_formatter) - df.columns = [ - ( - lambda_replace_underscores_in_column_names(col) - if isinstance(col, str) - else col - ) - for col in df.columns - ] - return df - return pd.DataFrame() - - -# TODO: add string with overview -@log_start_end(log=logger) -def get_stable_coins( - limit: int = 15, sortby: str = "Market_Cap_[$]", ascend: bool = False -) -> pd.DataFrame: - """Returns top stable coins [Source: CoinGecko] - - Parameters - ---------- - limit: int - How many rows to show - sortby: str - Key by which to sort data, default is Market_Cap_[$] - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Dataframe with stable coins data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.crypto.ov.stables(sortby="Volume_[$]", ascend=True, limit=10) - """ - - df = get_coins(limit=limit, category="stablecoins") - df = df[COINS_COLUMNS] - df = df.set_axis( - [ - "Symbol", - "Name", - "Price_[$]", - "Market_Cap_[$]", - "Market_Cap_Rank", - "Change_7d_[%]", - "Change_24h_[%]", - "Volume_[$]", - ], - axis=1, - copy=False, - ) - total_market_cap = int(df["Market_Cap_[$]"].sum()) - df[f"Percentage_[%]_of_top_{limit}"] = ( - df["Market_Cap_[$]"] / total_market_cap - ) * 100 - sortby = sortby.replace(" ", "_") - df = df.sort_values(by=sortby, ascending=ascend) - - return df - - -@log_start_end(log=logger) -def get_exchanges(sortby: str = "Rank", ascend: bool = True) -> pd.DataFrame: - """Get list of top exchanges from CoinGecko API [Source: CoinGecko] - - Parameters - ---------- - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - - Returns - ------- - pd.DataFrame - Trust_Score, Id, Name, Country, Year_Established, Trade_Volume_24h_BTC, Url - """ - - client = CoinGeckoAPI() - df = pd.DataFrame(client.get_exchanges_list(per_page=250)) - df.replace({float(np.NaN): None}, inplace=True) - df = df[ - [ - "trust_score", - "id", - "name", - "country", - "year_established", - "trade_volume_24h_btc", - "url", - ] - ] - df.columns = [ - "Trust_Score", - "Id", - "Name", - "Country", - "Year_Established", - "Trade_Volume_24h_BTC", - "Url", - ] - create_df_index(df, "Rank") - if sortby == "Rank": - df = df.sort_values(by=sortby, ascending=not ascend) - else: - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_financial_platforms(sortby: str = "Name", ascend: bool = True) -> pd.DataFrame: - """Get list of financial platforms from CoinGecko API [Source: CoinGecko] - - Parameters - ---------- - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Rank, Name, Category, Centralized, Url - """ - - client = CoinGeckoAPI() - df = pd.DataFrame(client.get_finance_platforms()) - df.drop("facts", axis=1, inplace=True) - create_df_index(df, "rank") - df.columns = ["Rank", "Name", "Category", "Centralized", "Url"] - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_finance_products(sortby: str = "Name", ascend: bool = True) -> pd.DataFrame: - """Get list of financial products from CoinGecko API - - Parameters - ---------- - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Rank, Platform, Identifier, Supply_Rate, Borrow_Rate - """ - - client = CoinGeckoAPI() - df = pd.DataFrame( - client.get_finance_products(per_page=250), - columns=[ - "platform", - "identifier", - "supply_rate_percentage", - "borrow_rate_percentage", - ], - ) - df.columns = ["Platform", "Identifier", "Supply_Rate", "Borrow_Rate"] - create_df_index(df, "Rank") - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_indexes(sortby: str = "Name", ascend: bool = True) -> pd.DataFrame: - """Get list of crypto indexes from CoinGecko API [Source: CoinGecko] - - Parameters - ---------- - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Name, Id, Market, Last, MultiAsset - """ - - client = CoinGeckoAPI() - df = pd.DataFrame(client.get_indexes(per_page=250)) - df.columns = ["Name", "Id", "Market", "Last", "MultiAsset"] - create_df_index(df, "Rank") - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_derivatives(sortby: str = "Rank", ascend: bool = False) -> pd.DataFrame: - """Get list of crypto derivatives from CoinGecko API [Source: CoinGecko] - - Parameters - ---------- - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - - Returns - ------- - pd.DataFrame - Rank, Market, Symbol, Price, Pct_Change_24h, Contract_Type, Basis, Spread, - Funding_Rate, Volume_24h, - """ - - client = CoinGeckoAPI() - df = pd.DataFrame(client.get_derivatives(include_tickers="unexpired")) - df.drop( - ["index", "last_traded_at", "expired_at", "index_id", "open_interest"], - axis=1, - inplace=True, - ) - - df.rename(columns={"price_percentage_change_24h": "pct_change_24h"}, inplace=True) - create_df_index(df, "rank") - df["price"] = df["price"].apply( - lambda x: "" if not x else float(x.strip("$").replace(",", "")) - ) - - df.columns = [ - "Rank", - "Market", - "Symbol", - "Price", - "Pct_Change_24h", - "Contract_Type", - "Basis", - "Spread", - "Funding_Rate", - "Volume_24h", - ] - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_exchange_rates(sortby: str = "Name", ascend: bool = False) -> pd.DataFrame: - """Get list of crypto, fiats, commodity exchange rates from CoinGecko API [Source: CoinGecko] - - Parameters - ---------- - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Index, Name, Unit, Value, Type - """ - - client = CoinGeckoAPI() - df = pd.DataFrame(client.get_exchange_rates()["rates"]).T.reset_index() - df.drop("index", axis=1, inplace=True) - create_df_index(df, "index") - df.columns = ["Index", "Name", "Unit", "Value", "Type"] - df = df.sort_values(by=sortby, ascending=ascend) - return df - - -@log_start_end(log=logger) -def get_global_info() -> pd.DataFrame: - """Get global statistics about crypto from CoinGecko API like: - - market cap change - - number of markets - - icos - - number of active crypto - - [Source: CoinGecko] - - Returns - ------- - pd.DataFrame - Metric, Value - """ - - client = CoinGeckoAPI() - results = client.get_global() - - total_mcap = results.pop("market_cap_percentage") - btc, eth = total_mcap.get("btc"), total_mcap.get("eth") - for key in ["total_market_cap", "total_volume", "updated_at"]: - del results[key] - results["btc_market_cap_in_pct"] = btc - results["eth_market_cap_in_pct"] = eth - results["altcoin_market_cap_in_pct"] = 100 - (float(eth) + float(btc)) - df = pd.Series(results).reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) if isinstance(x, str) else x - ) - ) - return df - - -@log_start_end(log=logger) -def get_global_markets_info() -> pd.DataFrame: - """Get global statistics about crypto markets from CoinGecko API like: - Market_Cap, Volume, Market_Cap_Percentage - - [Source: CoinGecko] - - Returns - ------- - pd.DataFrame - Market_Cap, Volume, Market_Cap_Percentage - """ - - columns = [ - "Market_Cap", - "Volume", - "Market_Cap_Percentage", - ] - data = [] - client = CoinGeckoAPI() - results = client.get_global() - for key in columns: - data.append(results.get(key)) - df = pd.DataFrame(data).T - df.columns = columns - df.replace({float("nan"): None}, inplace=True) - return df.reset_index() - - -@log_start_end(log=logger) -def get_global_defi_info() -> pd.DataFrame: - """Get global statistics about Decentralized Finances [Source: CoinGecko] - - Returns - ------- - pd.DataFrame - Metric, Value - """ - - client = CoinGeckoAPI() - results = client.get_global_decentralized_finance_defi() - for key, value in results.items(): - with contextlib.suppress(ValueError, TypeError): - results[key] = round(float(value), 4) - - df = pd.Series(results).reset_index() - df.columns = ["Metric", "Value"] - df["Metric"] = df["Metric"].apply( - lambda x: ( - lambda_replace_underscores_in_column_names(x) if isinstance(x, str) else x - ) - ) - return df diff --git a/openbb_terminal/cryptocurrency/overview/pycoingecko_view.py b/openbb_terminal/cryptocurrency/overview/pycoingecko_view.py deleted file mode 100644 index a7364a6a8f6..00000000000 --- a/openbb_terminal/cryptocurrency/overview/pycoingecko_view.py +++ /dev/null @@ -1,797 +0,0 @@ -"""CoinGecko view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd -import plotly.express as px - -import openbb_terminal.cryptocurrency.overview.pycoingecko_model as gecko -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_long_number_format_with_type_check, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint: disable=R0904, C0302 - - -@log_start_end(log=logger) -def plot_pie_chart( - labels: list, - values: list, - title: str, -) -> OpenBBFigure: - """Plots a pie chart from a dataframe - - Parameters - ---------- - labels_list : list - List of labels - values_list : list - List of values - title : str - Title of the chart - - Returns - ------- - OpenBBFigure - Plotly figure object - """ - fig = OpenBBFigure.create_subplots( - 1, - 3, - specs=[[{"type": "domain"}, {"type": "pie", "colspan": 2}, None]], - row_heights=[0.8], - vertical_spacing=0.1, - column_widths=[0.3, 0.8, 0.1], - ) - fig.add_pie( - labels=labels, - values=values, - textinfo="label+percent", - showlegend=False, - hoverinfo="label+percent+value", - outsidetextfont=dict(size=14), - marker=dict(line=dict(color="white", width=1)), - automargin=True, - textposition="outside", - rotation=180, - row=1, - col=2, - ) - fig.update_layout( - margin=dict(l=0, r=0, t=60, b=0), - title=dict(text=title, y=0.97, x=0.5, xanchor="center", yanchor="top"), - ) - - return fig - - -@log_start_end(log=logger) -def display_crypto_heatmap( - category: str = "", - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Shows cryptocurrencies heatmap [Source: CoinGecko] - - Parameters - ---------- - caterogy: str - Category (e.g., stablecoins). Empty for no category (default: ) - limit: int - Number of top cryptocurrencies to display - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = gecko.get_coins(limit, category) - if df.empty: - return console.print("\nNo cryptocurrencies found\n") - - df = df.fillna(0) # to prevent errors with rounding when values aren't available - - category_str = f"[{category}]" if category else "" - fig = OpenBBFigure.create_subplots( - print_grid=False, - vertical_spacing=0, - horizontal_spacing=-0, - specs=[[{"type": "domain"}]], - rows=1, - cols=1, - ) - fig.set_title(f"Top {limit} Cryptocurrencies {category_str}") - - df_copy = df.copy() - the_row = "price_change_percentage_24h_in_currency" - df_copy["symbol"] = df_copy.apply(lambda row: row["symbol"].upper(), axis=1) - df_copy["change"] = df_copy.apply(lambda row: round(row[the_row], 2), axis=1) - - # index needs to get sorted - was matching with different values - df.sort_index(inplace=True) - df_copy.sort_index(inplace=True) - - color_bin = [-100, -2, -1, -0.001, 0.001, 1, 2, 100] - df_copy["colors"] = pd.cut( - df_copy["change"], - bins=color_bin, - labels=[ - "rgb(246, 53, 56)", - "rgb(191, 64, 69)", - "rgb(139, 68, 78)", - "grey", - "rgb(53, 118, 78)", - "rgb(47, 158, 79)", - "rgb(48, 204, 90)", - ], - ) - - treemap = px.treemap( - df_copy, - path=["symbol"], - values="market_cap", - custom_data=[the_row], - color="colors", - color_discrete_map={ - "(?)": "#262931", - "grey": "grey", - "rgb(246, 53, 56)": "rgb(246, 53, 56)", - "rgb(191, 64, 69)": "rgb(191, 64, 69)", - "rgb(139, 68, 78)": "rgb(139, 68, 78)", - "rgb(53, 118, 78)": "rgb(53, 118, 78)", - "rgb(47, 158, 79)": "rgb(47, 158, 79)", - "rgb(48, 204, 90)": "rgb(48, 204, 90)", - }, - ) - fig.add_trace(treemap["data"][0], row=1, col=1) - - fig.data[0].texttemplate = ( - "

%{label}
%{customdata[0]:.2f}%



" - ) - fig.data[0].insidetextfont = dict( - family="Arial Black", - size=30, - color="white", - ) - - fig.update_traces( - textinfo="label+text", - textposition="middle center", - selector=dict(type="treemap"), - marker_line_width=0.3, - marker_pad_b=20, - marker_pad_l=0, - marker_pad_r=0, - marker_pad_t=50, - tiling_pad=2, - ) - fig.update_layout( - margin=dict(t=0, l=0, r=0, b=0), - title=dict(y=0.97, x=0.5, xanchor="center", yanchor="top"), - hovermode=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hm", - df, - sheet_name, - fig, - margin=False, - ) - - return fig.show(external=external_axes, margin=False) - - -@log_start_end(log=logger) -def display_holdings_overview( - symbol: str, - show_bar: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - limit: int = 15, -) -> None: - """Shows overview of public companies that holds ethereum or bitcoin. [Source: CoinGecko] - - Parameters - ---------- - symbol: str - Cryptocurrency: ethereum or bitcoin - show_bar : bool - Whether to show a bar graph for the data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx - limit: int - The number of rows to show - """ - fig = OpenBBFigure() - res = gecko.get_holdings_overview(symbol) - stats_string = res[0] - df = res[1] - - df = df.head(limit) - - if df.empty: - return console.print("\nZero companies holding this crypto\n") - - if show_bar or fig.is_image_export(export): - fig = OpenBBFigure(xaxis_title="Company Symbol") - - fig.add_bar(x=df["Symbol"], y=df["Total Holdings"], name="Total Holdings") - - ylabel = "ETH Number" - title = "Total ETH Holdings per company" - - if symbol == "bitcoin": - ylabel = "BTC Number" - title = "Total BTC Holdings per company" - - fig.set_title(title) - fig.set_yaxis_title(ylabel) - - if not fig.is_image_export(export): - fig.show() - - console.print(f"\n{stats_string}\n") - df = df.applymap(lambda x: lambda_long_number_format_with_type_check(x)) - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Public Companies Holding BTC or ETH", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cghold", - df, - sheet_name, - fig, - ) - - return None - - -@log_start_end(log=logger) -def display_exchange_rates( - sortby: str = "Name", - ascend: bool = False, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Shows list of crypto, fiats, commodity exchange rates. [Source: CoinGecko] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = gecko.get_exchange_rates(sortby, ascend) - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Exchange Rates", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "exrates", - df, - sheet_name, - ) - else: - console.print("Unable to retrieve data from CoinGecko.") - - -@log_start_end(log=logger) -def display_global_market_info( - pie: bool = False, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Shows global statistics about crypto. [Source: CoinGecko] - - market cap change - - number of markets - - icos - - number of active crypto - - market_cap_pct - - Parameters - ---------- - pie: bool - Whether to show a pie chart - export : str - Export dataframe data to csv,json,xlsx file - """ - fig = OpenBBFigure() - df = gecko.get_global_info() - - if not df.empty: - if pie or fig.is_image_export(export): - df = df.loc[ - df["Metric"].isin( - [ - "Btc Market Cap In Pct", - "Eth Market Cap In Pct", - "Altcoin Market Cap In Pct", - ] - ) - ] - fig = plot_pie_chart( - labels=["BTC", "ETH", "Altcoins"], - values=df["Value"], - title="Market Cap Distribution", - ) - if not fig.is_image_export(export): - fig.show() - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Global Statistics", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cgglobal", - df, - sheet_name, - fig, - ) - else: - console.print("Unable to retrieve data from CoinGecko.") - - -@log_start_end(log=logger) -def display_global_defi_info( - export: str = "", sheet_name: Optional[str] = None -) -> None: - """Shows global statistics about Decentralized Finances. [Source: CoinGecko] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = gecko.get_global_defi_info() - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Global DEFI Statistics", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "defi", - df, - sheet_name, - ) - else: - console.print("Unable to retrieve data from CoinGecko.") - - -@log_start_end(log=logger) -def display_stablecoins( - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, - sortby: str = "Market_Cap_[$]", - ascend: bool = False, - pie: bool = True, -) -> None: - """Shows stablecoins data [Source: CoinGecko] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key by which to sort data, default is Market_Cap_[$] - ascend: bool - Flag to sort data ascending - pie: bool - Whether to show a pie chart, default is True - export : str - Export dataframe data to csv,json,xlsx file - pie : bool - Whether to show a pie chart - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.crypto.ov.stables_chart(sortby="Volume_[$]", ascend=True, limit=10) - """ - fig = OpenBBFigure() - - df = gecko.get_stable_coins(limit, sortby=sortby, ascend=ascend) - - if not df.empty: - total_market_cap = int(df["Market_Cap_[$]"].sum()) - df.columns = df.columns.str.replace("_", " ") - - if pie or fig.is_image_export(export): - stables_to_display = df[df[f"Percentage [%] of top {limit}"] >= 1] - other_stables = df[df[f"Percentage [%] of top {limit}"] < 1] - values_list = list( - stables_to_display[f"Percentage [%] of top {limit}"].values - ) - values_list.append(other_stables[f"Percentage [%] of top {limit}"].sum()) - labels_list = list(stables_to_display["Name"].values) - labels_list.append("Others") - - fig = plot_pie_chart( - labels=labels_list, - values=values_list, - title=f"Market cap distribution of top {limit} Stablecoins", - ) - if not fig.is_image_export(export): - fig.show() - - console.print( - f"First {limit} stablecoins have a total " - f"{lambda_long_number_format_with_type_check(total_market_cap)}" - "dollars of market cap.\n" - ) - - df = df.applymap(lambda x: lambda_long_number_format_with_type_check(x)) - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Stablecoin Data", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cgstables", - df, - sheet_name, - fig, - ) - else: - console.print("\nUnable to retrieve data from CoinGecko.\n") - - -@log_start_end(log=logger) -def display_categories( - sortby: str = "market_cap_desc", - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, - pie: bool = False, -) -> None: - """Shows top cryptocurrency categories by market capitalization - - The cryptocurrency category ranking is based on market capitalization. [Source: CoinGecko] - - Parameters - ---------- - sortby: str - Key by which to sort data - limit: int - Number of records to display - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx file - pie: bool - Whether to show the pie chart - """ - fig = OpenBBFigure() - - df = gecko.get_top_crypto_categories(sortby) - df_data = df - if not df.empty: - if pie or fig.is_image_export(export): - df_data[f"% relative to top {limit}"] = ( - df_data["Market Cap"] / df_data["Market Cap"].sum() - ) * 100 - stables_to_display = df_data[df_data[f"% relative to top {limit}"] >= 1] - other_stables = df_data[df_data[f"% relative to top {limit}"] < 1] - values_list = list(stables_to_display[f"% relative to top {limit}"].values) - values_list.append(other_stables[f"% relative to top {limit}"].sum()) - labels_list = list(stables_to_display["Name"].values) - labels_list.append("Others") - - fig = plot_pie_chart( - labels=labels_list, - values=values_list, - title=f"Market Cap distribution of top {limit} crypto categories", - ) - if not fig.is_image_export(export): - fig.show() - - df = df.applymap(lambda x: lambda_long_number_format_with_type_check(x)) - print_rich_table( - df, - headers=list(df.columns), - floatfmt=".2f", - show_index=False, - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cgcategories", - df_data, - sheet_name, - fig, - ) - else: - console.print("\nUnable to retrieve data from CoinGecko.\n") - - -@log_start_end(log=logger) -def display_exchanges( - sortby: str = "Rank", - ascend: bool = False, - limit: int = 15, - links: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Shows list of top exchanges from CoinGecko. [Source: CoinGecko] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - links: bool - Flag to display urls - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = gecko.get_exchanges(sortby, ascend) - - if not df.empty: - if links is True: - df = df[["Rank", "Name", "Url"]] - else: - df.drop("Url", axis=1, inplace=True) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Top CoinGecko Exchanges", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "exchanges", - df, - sheet_name, - ) - else: - console.print("Unable to retrieve data from CoinGecko.") - - -@log_start_end(log=logger) -def display_platforms( - sortby: str = "Name", - ascend: bool = True, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Shows list of financial platforms. [Source: CoinGecko] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = gecko.get_financial_platforms(sortby, ascend) - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Financial Platforms", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "platforms", - df, - sheet_name, - ) - else: - console.print("\nUnable to retrieve data from CoinGecko.\n") - - -@log_start_end(log=logger) -def display_products( - sortby: str = "Platform", - ascend: bool = False, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Shows list of financial products. [Source: CoinGecko] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = gecko.get_finance_products(sortby=sortby, ascend=ascend) - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Financial Products", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "products", - df, - sheet_name, - ) - else: - console.print("Unable to retrieve data from CoinGecko.") - - -@log_start_end(log=logger) -def display_indexes( - sortby: str = "Name", - ascend: bool = True, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Shows list of crypto indexes. [Source: CoinGecko] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = gecko.get_indexes(sortby=sortby, ascend=ascend) - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Crypto Indexes", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "indexes", - df, - sheet_name, - ) - else: - console.print("Unable to retrieve data from CoinGecko.") - - -@log_start_end(log=logger) -def display_derivatives( - sortby: str = "Rank", - ascend: bool = False, - limit: int = 15, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Shows list of crypto derivatives. [Source: CoinGecko] - - Parameters - ---------- - limit: int - Number of records to display - sortby: str - Key by which to sort data - ascend: bool - Flag to sort data descending - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = gecko.get_derivatives(sortby=sortby, ascend=ascend) - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Crypto Derivatives", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "derivatives", - df, - sheet_name, - ) - else: - console.print("Unable to retrieve data from CoinGecko.") diff --git a/openbb_terminal/cryptocurrency/overview/rekt_model.py b/openbb_terminal/cryptocurrency/overview/rekt_model.py deleted file mode 100644 index 1a3d679d6c6..00000000000 --- a/openbb_terminal/cryptocurrency/overview/rekt_model.py +++ /dev/null @@ -1,200 +0,0 @@ -"""Rekt Model""" - -import logging -from typing import List, Union - -import pandas as pd -import requests -from bs4 import BeautifulSoup -from requests.adapters import HTTPAdapter, RetryError -from urllib3.util.retry import Retry - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -HACKS_COLUMNS = ["Platform", "Date", "Amount [$]", "Audit", "Slug", "URL"] - - -@log_start_end(log=logger) -def _retry_session( - url: str, retries: int = 3, backoff_factor: float = 1.0 -) -> requests.Session: - """Helper methods that retries to make request - - - 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 - - -@log_start_end(log=logger) -def _make_request(url: str) -> Union[BeautifulSoup, None]: - """Helper method to scrap - - Parameters - ---------- - url : str - url to scrape - - Returns - ------- - Union[BeautifulSoup, None] - BeautifulSoup object or None - """ - headers = {"User-Agent": get_user_agent()} - session = _retry_session("https://www.coingecko.com") - try: - req = session.get(url, headers=headers) - except Exception as error: - logger.exception(str(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 == 404: - return None - - 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") - - -@log_start_end(log=logger) -def get_crypto_hacks(sortby: str = "Platform", ascend: bool = False) -> pd.DataFrame: - """Get major crypto-related hacks - [Source: https://rekt.news] - - Parameters - ---------- - sortby: str - Key by which to sort data {Platform,Date,Amount [$],Audit,Slug,URL} - ascend: bool - Flag to sort data ascending - - Returns - ------- - pd.DataFrame - Hacks with columns {Platform,Date,Amount [$],Audited,Slug,URL} - """ - soup = _make_request("https://rekt.news/leaderboard") - if soup: - rekt_list = soup.find("ol", {"class": "leaderboard-content"}).find_all("li") - df = pd.DataFrame(columns=HACKS_COLUMNS) - for item in rekt_list: - a = item.find("a", href=True) - audit = item.find("span", {"class": "leaderboard-audit"}).text - details = item.find("div", {"class": "leaderboard-row-details"}).text.split( - "|" - ) - url = a["href"] - title = a.text - amount = int(details[0][1:].replace(",", "")) - date = details[1].replace(" ", "") - df.loc[len(df.index)] = [ - title, - date, - amount, - audit, - url.replace("/", ""), - f"https://rekt.news{url}", - ] - df["Date"] = pd.to_datetime(df["Date"]) - if sortby in HACKS_COLUMNS: - df = df.sort_values(by=sortby, ascending=ascend) - return df - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_crypto_hack(slug: str) -> Union[str, None]: - """Get crypto hack - [Source: https://rekt.news] - - Parameters - ---------- - slug: str - slug of crypto hack - - Returns - ------- - Union[str, None] - Crypto hack - """ - url = f"https://rekt.news/{slug}" - soup = _make_request(url) - if not soup: - console.print(f'Slug "{slug}" not found\n') - return None - title = soup.find("h1", {"class": "post-title"}).text - date = soup.find("time").text - content = ( - soup.find("section", {"class": "post-content"}) - .get_text("\n") - .replace("\r\n,", ", ") - .replace("\n,", ", ") - .replace("\r\n.", ".\n\t") - .replace("\n.", ".\n\t") - .replace("\r\n ", " ") - .replace("\n ", " ") - ).split("""SUBSCRIBE""")[0] - final_str = f""" - {title} - {date} - - {content} - - Detailed history in {url} - """ - return final_str - - -@log_start_end(log=logger) -def get_crypto_hack_slugs() -> List[str]: - """Get all crypto hack slugs - [Source: https://rekt.news] - - Returns - ------- - List[str] - List with slugs - """ - soup = _make_request("https://rekt.news/leaderboard") - href_list = [] - if soup: - rekt_list = soup.find("ol", {"class": "leaderboard-content"}).find_all("li") - for item in rekt_list: - a = item.find("a", href=True)["href"].replace("/", "") - href_list.append(a) - return href_list - return href_list diff --git a/openbb_terminal/cryptocurrency/overview/rekt_view.py b/openbb_terminal/cryptocurrency/overview/rekt_view.py deleted file mode 100644 index 1cbc4b01f4e..00000000000 --- a/openbb_terminal/cryptocurrency/overview/rekt_view.py +++ /dev/null @@ -1,83 +0,0 @@ -"""Rekt view""" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.overview import rekt_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_crypto_hacks( - limit: int = 15, - sortby: str = "Platform", - ascend: bool = False, - slug: str = "polyntwork-rekt", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display list of major crypto-related hacks. If slug is passed - individual crypto hack is displayed instead of list of crypto hacks - [Source: https://rekt.news] - - Parameters - ---------- - slug: str - Crypto hack slug to check (e.g., polynetwork-rekt) - limit: int - Number of hacks to search - sortby: str - Key by which to sort data {Platform,Date,Amount [$],Audit,Slug,URL} - ascend: bool - Flag to sort data ascending - export : str - Export dataframe data to csv,json,xlsx file - """ - if slug: - text = rekt_model.get_crypto_hack(slug) - if text: - console.print(text) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ch", - text, - sheet_name, - ) - else: - df = rekt_model.get_crypto_hacks(sortby, ascend) - - if df.empty: - console.print("\nError in rekt request\n") - else: - df["Amount [$]"] = df["Amount [$]"].apply( - lambda x: lambda_long_number_format(x) - ) - df["Date"] = df["Date"].dt.date - - print_rich_table( - df, - headers=list(df.columns), - floatfmt=".1f", - show_index=False, - title="Major Crypto Hacks", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ch", - df, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/overview/sdk_helpers.py b/openbb_terminal/cryptocurrency/overview/sdk_helpers.py deleted file mode 100644 index c553ebfdde7..00000000000 --- a/openbb_terminal/cryptocurrency/overview/sdk_helpers.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Crypto OV SDK Helper Functions.""" - -__docformat__ = "numpy" - -import pandas as pd - -from openbb_terminal.cryptocurrency.dataframe_helpers import ( - lambda_long_number_format_with_type_check, -) -from openbb_terminal.cryptocurrency.overview import coinpaprika_model, pycoingecko_model - - -def globe(source: str = "CoinGecko") -> pd.DataFrame: - """Get global crypto market data. - - Parameters - ---------- - source : str, optional - Source of data, by default "CoinGecko" - - Returns - ------- - pd.DataFrame - DataFrame with global crypto market data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> global_market_data = openbb.crypto.ov.globe() - - To get data from CoinPaprika, use the source parameter: - >>> global_market_data = openbb.crypto.ov.globe(source="coinpaprika") - - """ - if source.lower() == "coingecko": - df = pycoingecko_model.get_global_info() - return df - if source.lower() == "coinpaprika": - df = coinpaprika_model.get_global_info() - return df - return pd.DataFrame() - - -def exchanges(source: str = "CoinGecko") -> pd.DataFrame: - """Show top crypto exchanges. - - Parameters - ---------- - source : str, optional - Source to get exchanges, by default "CoinGecko" - - Returns - ------- - pd.DataFrame - DataFrame with top crypto exchanges - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> exchanges = openbb.crypto.ov.exchanges() - """ - if source.lower() == "coingecko": - df = pycoingecko_model.get_exchanges().sort_values(by="Rank", ascending=True) - return df - if source.lower() == "coinpaprika": - df = coinpaprika_model.get_list_of_exchanges("USD") - cols = [col for col in df.columns if col != "Rank"] - df[cols] = df[cols].applymap( - lambda x: lambda_long_number_format_with_type_check(x) - ) - return df.sort_values(by="Rank", ascending=True).reset_index(drop=True).head(20) - return pd.DataFrame() diff --git a/openbb_terminal/cryptocurrency/overview/tokenterminal_model.py b/openbb_terminal/cryptocurrency/overview/tokenterminal_model.py deleted file mode 100644 index 6a3fd57b9b4..00000000000 --- a/openbb_terminal/cryptocurrency/overview/tokenterminal_model.py +++ /dev/null @@ -1,302 +0,0 @@ -"""Token Terminal Model""" - -import logging -from typing import List - -import pandas as pd -from tokenterminal import TokenTerminal - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - -token_terminal = TokenTerminal( - key=get_current_user().credentials.API_TOKEN_TERMINAL_KEY -) - -# Fetch all data for projects' -try: - PROJECTS_DATA = token_terminal.get_all_projects() -except Exception as e: - logger.error(e) - PROJECTS_DATA = [ - "0x", - "1inch", - "88mph", - "aave", - "abracadabra-money", - "alchemist", - "alchemix-finance", - "algorand", - "alpha-finance", - "arweave", - "autofarm", - "avalanche", - "axie-infinity", - "balancer", - "bancor", - "barnbridge", - "basket-dao", - "benqi", - "binance-smart-chain", - "bitcoin", - "cap", - "cardano", - "centrifuge", - "clipper", - "compound", - "convex-finance", - "cosmos", - "cryptex", - "curve", - "decentral-games", - "decred", - "dforce", - "dhedge", - "dodo", - "dogecoin", - "dydx", - "ellipsis-finance", - "elrond", - "enzyme-finance", - "erasure-protocol", - "ethereum", - "ethereum-name-service", - "euler", - "fantom", - "fei-protocol", - "filecoin", - "futureswap", - "gmx", - "goldfinch", - "harvest-finance", - "helium", - "hurricaneswap", - "idle-finance", - "index-cooperative", - "instadapp", - "integral-protocol", - "karura", - "keeperdao", - "keep-network", - "kusama", - "kyber", - "lido-finance", - "liquity", - "litecoin", - "livepeer", - "looksrare", - "loopring", - "maiar", - "makerdao", - "maple-finance", - "mcdex", - "metamask", - "mstable", - "near-protocol", - "nexus-mutual", - "nftx", - "notional-finance", - "opensea", - "optimism", - "osmosis", - "pancakeswap", - "pangolin", - "perpetual-protocol", - "piedao", - "pocket-network", - "polkadot", - "polygon", - "polymarket", - "pooltogether", - "powerpool", - "quickswap", - "rarible", - "rari-capital", - "reflexer", - "ren", - "ribbon-finance", - "rocket-pool", - "saddle-finance", - "set-protocol", - "solana", - "solend", - "spookyswap", - "stake-dao", - "stellar", - "sushiswap", - "synthetix", - "terra", - "tezos", - "the-graph", - "thorchain", - "tokemak", - "tokenlon", - "tornado-cash", - "trader-joe", - "uma", - "uniswap", - "unit-protocol", - "venus", - "vesper-finance", - "volmex", - "wakaswap", - "yearn-finance", - "yield-guild-games", - "yield-yak", - "zcash", - "zora", - ] - -TIMELINES = ["24h", "7d", "30d", "90d", "180d", "365d"] - -CATEGORIES = [ - "Asset Management", - "Blockchain", - "DeFi", - "Exchange", - "Gaming", - "Insurance", - "Interoperability", - "Lending", - "NFT", - "Other", - "Prediction Market", - "Stablecoin", -] - -METRICS = [ - "twitter_followers", - "gmv_annualized", - "market_cap", - "take_rate", - "revenue", - "revenue_protocol", - "tvl", - "pe", - "pe_circulating", - "ps", - "ps_circulating", -] - - -@log_start_end(log=logger) -def get_possible_timelines() -> List[str]: - """This function returns the available timelines. - - Returns - ------- - List[str] - A list with the available timelines values. - """ - return TIMELINES - - -@log_start_end(log=logger) -def get_possible_categories() -> List[str]: - """This function returns the available categories. - - Returns - ------- - List[str] - A list with the available categories values. - """ - return CATEGORIES - - -@log_start_end(log=logger) -def get_possible_metrics() -> List[str]: - """This function returns the available metrics. - - Returns - ------- - List[str] - A list with the available metrics values. - """ - return METRICS - - -@log_start_end(log=logger) -def get_fundamental_metrics( - metric: str, - category: str = "", - timeline: str = "24h", - ascend: bool = False, -) -> pd.Series: - """Get fundamental metrics [Source: Token Terminal] - - Parameters - ---------- - metric : str - The metric of interest. See `get_possible_metrics()` for available metrics. - category : str - The category of interest. See `get_possible_categories()` for available categories. - The default value is an empty string which means that all categories are considered. - timeline : str - The category of interest. See `get_possible_timelines()` for available timelines. - ascend : bool - Direction of the sort. If True, the data is sorted in ascending order. - - Returns - ------- - pd.Series - Project, Metric value - """ - metric_values = {} - for project in PROJECTS_DATA: - if category and ( - ( - "," in project["category_tags"] - and category in project["category_tags"].split(",") - ) - or project["category_tags"] == category - ): - # check that is in metrics with a timeline - if metric in [ - "revenue", - "revenue_protocol", - "tvl", - "pe", - "pe_circulating", - "ps", - "ps_circulating", - ]: - val = project[metric + "_" + timeline] - else: - val = project[metric] - if isinstance(val, (float, int)): - if project["symbol"]: - metric_values[f"{project['name']} ({project['symbol']})"] = float( - val - ) - else: - metric_values[f"{project['name']}"] = float(val) - - else: - # check that is in metrics with a timeline - if metric in [ - "revenue", - "revenue_protocol", - "tvl", - "pe", - "pe_circulating", - "ps", - "ps_circulating", - ]: - val = project[metric + "_" + timeline] - else: - val = project[metric] - if isinstance(val, (float, int)): - if project["symbol"]: - metric_values[f"{project['name']} ({project['symbol']})"] = float( - val - ) - else: - metric_values[f"{project['name']}"] = float(val) - - metric_values = dict( - sorted(metric_values.items(), key=lambda item: item[1], reverse=not ascend) - ) - - return pd.Series(metric_values) diff --git a/openbb_terminal/cryptocurrency/overview/tokenterminal_view.py b/openbb_terminal/cryptocurrency/overview/tokenterminal_view.py deleted file mode 100644 index dcee51bc58a..00000000000 --- a/openbb_terminal/cryptocurrency/overview/tokenterminal_view.py +++ /dev/null @@ -1,100 +0,0 @@ -"""Token Terminal View""" - -import logging -import os - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.cryptocurrency.overview.tokenterminal_model import ( - CATEGORIES, - METRICS, - TIMELINES, - get_fundamental_metrics, -) -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_TOKEN_TERMINAL_KEY"]) -def display_fundamental_metrics( - metric: str, - category: str = "", - timeline: str = "24h", - ascend: bool = False, - limit: int = 10, - export: str = "", - external_axes: bool = False, -): - """Display fundamental metrics [Source: Token Terminal] - - Parameters - ---------- - metric : str - The metric of interest. See `get_possible_metrics()` for available metrics. - category : str - The category of interest. See `get_possible_categories()` for available categories. - The default value is an empty string which means that all categories are considered. - timeline : str - The category of interest. See `get_possible_timelines()` for available timelines. - ascend : bool - Direction of the sort. If True, the data is sorted in ascending order. - limit : int - The number of rows to display. - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if metric not in METRICS: - return console.print( - "[red]Invalid metric selected. See available metrics with get_possible_metrics()[/red]\n" - ) - - if category not in CATEGORIES and category != "": - return console.print( - "[red]Invalid category selected. See available categories with get_possible_categories()[/red]\n" - ) - - if timeline not in TIMELINES: - return console.print( - "[red]Invalid timeline selected. See available timelines with get_possible_timelines()[/red]\n" - ) - - metric_series = get_fundamental_metrics( - metric=metric, category=category, timeline=timeline, ascend=ascend - ) - - if metric_series.empty: - return console.print("\n[/red]No data found[/red]\n") - - fig = OpenBBFigure() - - fig.add_bar( - x=metric_series[:limit].index, - y=metric_series[:limit].values, - marker_color=theme.get_colors(reverse=True)[:limit], - ) - - if category: - fig.set_xaxis_title(category, tickangle=-15) - else: - fig.set_xaxis_title("Dapps and Blockchains", tickangle=-15) - - labeltouse = "Followers" if metric == "twitter_followers" else "[USD]" - - fig.set_yaxis_title(f"{metric.replace('_', ' ').capitalize()} {labeltouse}") - - fig.set_title(f"{metric.replace('_', ' ').capitalize()} from past {timeline}") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fun", - metric_series, - figure=fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/cryptocurrency/overview/withdrawalfees_model.py b/openbb_terminal/cryptocurrency/overview/withdrawalfees_model.py deleted file mode 100644 index 1734409d20b..00000000000 --- a/openbb_terminal/cryptocurrency/overview/withdrawalfees_model.py +++ /dev/null @@ -1,268 +0,0 @@ -"""Withdrawal Fees model""" - -import logging -import math -from typing import Any, List - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - -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", -] - - -@log_start_end(log=logger) -def get_overall_withdrawal_fees(limit: int = 100) -> pd.DataFrame: - """Scrapes top coins withdrawal fees - [Source: https://withdrawalfees.com/] - - Parameters - ---------- - limit: int - Number of coins to search, by default n=100, one page has 100 coins, so 1 page is scraped. - - Returns - ------- - pd.DataFrame - Coin, Lowest, Average, Median, Highest, Exchanges Compared - """ - - COINS_PER_PAGE = 100 - withdrawal_fees_homepage = BeautifulSoup( - request( - "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["Lowest"] = df["Lowest"].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(limit / COINS_PER_PAGE)) - if num_pages > 1: - for idx in range(2, num_pages + 1): - withdrawal_fees_homepage = BeautifulSoup( - request( - 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 - - -@log_start_end(log=logger) -def get_overall_exchange_withdrawal_fees() -> pd.DataFrame: - """Scrapes exchange withdrawal fees - [Source: https://withdrawalfees.com/] - - Returns - ------- - pd.DataFrame - Exchange, Coins, Lowest, Average, Median, Highest - """ - exchange_withdrawal_fees = BeautifulSoup( - request( - "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 - - -@log_start_end(log=logger) -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) - - pd.DataFrame: Exchange, Withdrawal Fee, Minimum Withdrawal Amount - """ - crypto_withdrawal_fees = BeautifulSoup( - request( - 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/openbb_terminal/cryptocurrency/overview/withdrawalfees_view.py b/openbb_terminal/cryptocurrency/overview/withdrawalfees_view.py deleted file mode 100644 index 1b8f4a63c25..00000000000 --- a/openbb_terminal/cryptocurrency/overview/withdrawalfees_view.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Withdrawal Fees view""" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.overview.withdrawalfees_model import ( - get_crypto_withdrawal_fees, - get_overall_exchange_withdrawal_fees, - get_overall_withdrawal_fees, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_overall_withdrawal_fees( - limit: int = 15, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Top coins withdrawal fees - [Source: https://withdrawalfees.com/] - - Parameters - ---------- - limit: int - Number of coins to search - export : str - Export dataframe data to csv,json,xlsx file - """ - - df_fees = get_overall_withdrawal_fees(limit) - - if df_fees.empty: - console.print("\nError in withdrawal fees request\n") - else: - console.print("\nWithdrawal fees on exchanges:") - - print_rich_table( - df_fees, - headers=list(df_fees.columns), - show_index=False, - title="Top Withdrawal Fees", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "withdrawal_fees", - df_fees, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_overall_exchange_withdrawal_fees( - export: str = "", sheet_name: Optional[str] = None -) -> 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: - console.print("\nError in withdrawal fees request\n") - else: - console.print("\nWithdrawal fees per exchange:") - - print_rich_table( - df_fees, - headers=list(df_fees.columns), - show_index=False, - title="Withdrawal Fees", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "exchange_withdrawal_fees", - df_fees, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_crypto_withdrawal_fees( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Coin withdrawal fees per exchange - [Source: https://withdrawalfees.com/] - - Parameters - ---------- - symbol: str - Coin to check withdrawal fees - export : str - Export dataframe data to csv,json,xlsx file - """ - - res = get_crypto_withdrawal_fees(symbol) - stats_string = res[0] - df_fees = res[1] - if df_fees.empty: - console.print("\nError in withdrawal fees request\n") - else: - console.print(f"\nWithdrawal fees for {symbol}:") - - console.print(f"\n{stats_string}\n") - - print_rich_table( - df_fees, - headers=list(df_fees.columns), - show_index=False, - title="Withdrawal Fees per Exchange", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "crypto_withdrawal_fees", - df_fees, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/pycoingecko_helpers.py b/openbb_terminal/cryptocurrency/pycoingecko_helpers.py deleted file mode 100644 index 8e1de26874c..00000000000 --- a/openbb_terminal/cryptocurrency/pycoingecko_helpers.py +++ /dev/null @@ -1,285 +0,0 @@ -"""CoinGecko helpers""" - -__docformat__ = "numpy" - -import datetime as dt -import json -import logging -import math -import textwrap -from datetime import timezone -from typing import Any, Dict, List, Optional, Sequence, Tuple, Union - -import pandas as pd -import requests -from bs4 import BeautifulSoup -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, request -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 = request( - "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) - 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 diff --git a/openbb_terminal/cryptocurrency/pyth_model.py b/openbb_terminal/cryptocurrency/pyth_model.py deleted file mode 100644 index 658bd1d76d1..00000000000 --- a/openbb_terminal/cryptocurrency/pyth_model.py +++ /dev/null @@ -1,108 +0,0 @@ -import logging -from typing import Tuple - -from pythclient.pythaccounts import PythPriceAccount, PythPriceStatus -from pythclient.solana import ( - SOLANA_DEVNET_HTTP_ENDPOINT, - SOLANA_DEVNET_WS_ENDPOINT, - SolanaClient, - SolanaPublicKey, -) - -logger = logging.getLogger(__name__) - -ASSETS = { - "AAVE-USD": {"feedID": "FT7Cup6ZiFDF14uhFD3kYS3URMCf2RZ4iwfNEVUgndHW"}, - "ADA-USD": {"feedID": "8oGTURNmSQkrBS1AQ5NjB2p8qY34UVmMA9ojrw8vnHus"}, - "ALGO-USD": {"feedID": "c1A946dY5NHuVda77C8XXtXytyR3wK1SCP3eA9VRfC3"}, - "ANC-USD": {"feedID": "5Si2Pdm7B87ojYvkegg7ct8Y446RHJyEjeREAyZEZcAV"}, - "APE-USD": {"feedID": "EfnLcrwxCgwALc5vXr4cwPZMVcmotZAuqmHa8afG8zJe"}, - "ATLAS-USD": {"feedID": "Dzs6SE1cssUqBpWCKzE4jeS5PrmRK1Fp2Kw1WMaDCiVR"}, - "ATOM-USD": {"feedID": "7YAze8qFUMkBnyLVdKT4TFUUFui99EwS5gfRArMcrvFk"}, - "AVAX-USD": {"feedID": "FVb5h1VmHPfVb1RfqZckchq18GxRv4iKt8T4eVTQAqdz"}, - "BCH-USD": {"feedID": "4EQrNZYk5KR1RnjyzbaaRbHsv8VqZWzSUtvx58wLsZbj"}, - "BETH-USD": {"feedID": "HyShqBUTtwAaCas9Dnib3ut6GmEDk9hTdKsrNfRffX8E"}, - "BNB-USD": {"feedID": "GwzBgrXb4PG59zjce24SF2b9JXbLEjJJTBkmytuEZj1b"}, - "BRZ-USD": {"feedID": "5g4XtpqLynP6YUSQwncw6CrdAEoy5a7QNDevgAgLsfyC"}, - "BTC-USD": {"feedID": "HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J"}, - "BUSD-USD": {"feedID": "TRrB75VTpiojCy99S5BHmYkjARgtfBqZKk5JbeouUkV"}, - "C98-USD": {"feedID": "Dxp7vob2NTGhmodyWyeEkqtNEpSfvSMoGKMYjmaY6pg1"}, - "COPE-USD": {"feedID": "BAXDJUXtz6P5ARhHH1aPwgv4WENzHwzyhmLYK4daFwiM"}, - "CUSD-USD": {"feedID": "DDwzo3aAjgYk8Vn8D3Zbxo62rTmBVdJv1WjaKQseiHKk"}, - "DOGE-USD": {"feedID": "4L6YhY8VvUgmqG5MvJkUJATtzB2rFqdrJwQCmFLv4Jzy"}, - "DOT-USD": {"feedID": "4dqq5VBpN4EwYb7wyywjjfknvMKu7m78j9mKZRXTj462"}, - "ETH-USD": {"feedID": "EdVCmQ9FSPcVe5YySXDPCRmc8aDQLKJ9xvYBMZPie1Vw"}, - "FIDA-USD": {"feedID": "7teETxN9Y8VK6uJxsctHEwST75mKLLwPH1jaFdvTQCpD"}, - "FTM-USD": {"feedID": "BTwrLU4so1oJMViWA3BTzh8YmFwiLZ6CL4U3JryG7Q5S"}, - "FTT-USD": {"feedID": "6vivTRs5ZPeeXbjo7dfburfaYDWoXjBtdtuYgQRuGfu"}, - "GMT-USD": {"feedID": "EZy99wkoqohyyNxT1QCwW3epQtMQ1Dfqx4sXKqkHiSox"}, - "GOFX-USD": {"feedID": "A9r7BHsXJQ2w9B7cdJV8BkfRoBWkxRichVGm72vVS1s5"}, - "HXRO-USD": {"feedID": "6VrSw4Vxg5zs9shfdCxLqfUy2qSD3NCS9AsdBQUgbjnt"}, - "INJ-USD": {"feedID": "44uRsNnT35kjkscSu59MxRr9CfkLZWf6gny8bWqUbVxE"}, - "JET-USD": {"feedID": "3JnVPNY878pRH6TQ9f4wuwfNqGh6okyshmqmKsyvewMs"}, - "LTC-USD": {"feedID": "BLArYBCUYhdWiY8PCUTpvFE21iaJq85dvxLk9bYMobcU"}, - "LUNA-USD": {"feedID": "7xzCBiE2d9UwV9CYLV9vrbJPipJzMEaycPBoZg2LjhUf"}, - "LUNC-USD": {"feedID": "8PugCXTAHLM9kfLSQWe2njE5pzAgUdpPk3Nx5zSm7BD3"}, - "MATIC-USD": {"feedID": "FBirwuDFuRAu4iSGc7RGxN5koHB7EJM1wbCmyPuQoGur"}, - "MER-USD": {"feedID": "6Z3ejn8DCWQFBuAcw29d3A5jgahEpmycn7YDMX7yRNrn"}, - "MIR-USD": {"feedID": "4BDvhA5emySfqyyTHPHofTJqRw1cwDabK1yiEshetPv9"}, - "MNGO-USD": {"feedID": "DCNw5mwZgjfTcoNsSZWUiXqU61ushNvr3JRQJRi1Nf95"}, - "MSOL-USD": {"feedID": "9a6RNx3tCu1TSs6TBSfV2XRXEPEZXQ6WB7jRojZRvyeZ"}, - "NEAR-USD": {"feedID": "3gnSbT7bhoTdGkFVZc1dW1PvjreWzpUNUD5ppXwv1N59"}, - "ONE-USD": {"feedID": "BScN1mER6QJ2nFKpnP4PcqffQp97NXAvzAbVPjLKyRaF"}, - "ORCA-USD": {"feedID": "A1WttWF7X3Rg6ZRpB2YQUFHCRh1kiXV8sKKLV3S9neJV"}, - "PAI-USD": {"feedID": "8EjmYPrH9oHxLqk2oFG1qwY6ert7M9cv5WpXyWHxKiMb"}, - "PORT-USD": {"feedID": "33ugpDWbC2mLrYSQvu1BHfykR8bt3MVc4S3YuuXMVRH3"}, - "RAY-USD": {"feedID": "EhgAdTrgxi4ZoVZLQx1n93vULucPpiFi2BQtz9RJr1y6"}, - "SBR-USD": {"feedID": "4WSN3XDSTfBX9A1YXGg8HJ7n2GtWMDNbtz1ab6aGGXfG"}, - "SCNSOL-USD": {"feedID": "HoDAYYYhFvCNQNFPui51H8qvpcdz6KuVtq77ZGtHND2T"}, - "SLND-USD": {"feedID": "FtwKARNAnZK2Nx1W4KVXzbyDzuRJqmApHRBtQpZ49HDv"}, - "SNY-USD": {"feedID": "DEmEX28EgrdQEBwNXdfMsDoJWZXCHRS5pbgmJiTkjCRH"}, - "SOL-USD": {"feedID": "J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix"}, - "SRM-USD": {"feedID": "992moaMQKs32GKZ9dxi8keyM2bUmbrwBZpK4p2K6X5Vs"}, - "STEP-USD": {"feedID": "DKjdYzkPEZLBsfRzUaCjze5jjgCYu5kFCB19wVa9sy6j"}, - "STSOL-USD": {"feedID": "2LwhbcswZekofMNRtDRMukZJNSRUiKYMFbqtBwqjDfke"}, - "TUSD-USD": {"feedID": "2sbXow64dSbktGM6gG9FpszwVu7GNhr6Qi2WHRCP9ULn"}, - "USDC-USD": {"feedID": "5SSkXsEKQepHHAewytPVwdej4epN1nxgLVM84L4KXgy7"}, - "USDT-USD": {"feedID": "38xoQ4oeJCBrcVvca2cGk7iV1dAfrmTR1kmhSCJQ8Jto"}, - "USTC-USD": {"feedID": "AUKjh1oVPZyudi3nzYSsdZxSjq42afUCvsdbKFc5CbD"}, - "VAI-USD": {"feedID": "Gvm85Pbjq4Tv7qyaS4y9ZMqCdY3nynGDBFYAu7mjPoGM"}, - "XVS-USD": {"feedID": "8Y4jhVcQvQZWjMarM855NMkVuua78FS8Uwy58TjcnUWs"}, - "ZBC-USD": {"feedID": "7myonvBWD5zfh6qfScRP5E4anEue4Bqnu8XS8cdtJTQx"}, -} - - -async def get_price(symbol: str) -> Tuple[float, float, float]: - """Returns price and confidence interval from pyth live feed. [Source: Pyth] - - Parameters - ---------- - symbol : str - Symbol of the asset to get price and confidence interval from - - Returns - ------- - Tuple[float, float, float] - Price of the asset, - Confidence level, - Previous price of the asset - """ - - account_key = SolanaPublicKey(ASSETS[symbol]["feedID"]) - solana_client = SolanaClient( - endpoint=SOLANA_DEVNET_HTTP_ENDPOINT, ws_endpoint=SOLANA_DEVNET_WS_ENDPOINT - ) - price: PythPriceAccount = PythPriceAccount(account_key, solana_client) - - await price.update() - - price_status = price.aggregate_price_status - aggregate_price = -1 - confidence = -1 - if price_status == PythPriceStatus.TRADING: - aggregate_price = price.aggregate_price - previous_price = price.prev_price - confidence = price.aggregate_price_confidence_interval - - await solana_client.close() - return aggregate_price, confidence, previous_price diff --git a/openbb_terminal/cryptocurrency/pyth_view.py b/openbb_terminal/cryptocurrency/pyth_view.py deleted file mode 100644 index c35914db340..00000000000 --- a/openbb_terminal/cryptocurrency/pyth_view.py +++ /dev/null @@ -1,31 +0,0 @@ -# pylint: disable= C0301 - -import asyncio -import logging - -from openbb_terminal.cryptocurrency.pyth_model import get_price -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_price(symbol: str) -> None: - """Displays live price from pyth live feed [Source: https://pyth.network/] - - Parameters - ---------- - symbol : str - Symbol of the asset to get price for - """ - try: - while True: - price, confidence, previous_price = asyncio.run(get_price(symbol)) - console.print( - f"{symbol} = {'[red]' if previous_price >= price else '[green]'}" - f"{price}{'[/red]' if previous_price >= price else '[/green]'} ± {confidence}", - end="\r", - ) - except KeyboardInterrupt: - console.print(f"\n\nStopped watching {symbol} price and confidence interval\n") diff --git a/openbb_terminal/cryptocurrency/quantitative_analysis/__init__.py b/openbb_terminal/cryptocurrency/quantitative_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/cryptocurrency/quantitative_analysis/qa_controller.py b/openbb_terminal/cryptocurrency/quantitative_analysis/qa_controller.py deleted file mode 100644 index caeb09529f2..00000000000 --- a/openbb_terminal/cryptocurrency/quantitative_analysis/qa_controller.py +++ /dev/null @@ -1,778 +0,0 @@ -"""Quantitative Analysis Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -from typing import List, Optional - -import numpy as np -import pandas as pd - -from openbb_terminal.common.quantitative_analysis import qa_view, rolling_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_list_dates, - check_positive, - check_proportion_range, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import CryptoBaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class QaController(CryptoBaseController): - """Quantitative Analysis Controller class""" - - CHOICES_COMMANDS = [ - "load", - "pick", - "raw", - "summary", - "line", - "hist", - "cdf", - "bw", - "rolling", - "decompose", - "cusum", - "acf", - "spread", - "quantile", - "skew", - "kurtosis", - "normality", - "qqplot", - "unitroot", - ] - FULLER_REG = ["c", "ct", "ctt", "nc"] - KPS_REG = ["c", "ct"] - - PATH = "/crypto/qa/" - CHOICES_GENERATION = True - - def __init__( - self, - symbol: str, - data: pd.DataFrame, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - data["Returns"] = data["Close"].pct_change() - data["LogRet"] = np.log(data["Close"]) - np.log(data["Close"].shift(1)) - data = data.dropna() - - self.data = data - self.symbol = symbol - self.target = "Close" - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - choices["pick"].update({c: {} for c in self.data.columns.tolist()}) - choices["load"] = { - "--interval": { - c: {} - for c in [ - "1", - "5", - "15", - "30", - "60", - "240", - "1440", - "10080", - "43200", - ] - }, - "-i": "--interval", - "--exchange": {c: {} for c in self.exchanges}, - "--source": {c: {} for c in ["CCXT", "YahooFinance", "CoingGecko"]}, - "--vs": {c: {} for c in ["usd", "eur"]}, - "--start": None, - "-s": "--start", - "--end": None, - "-e": "--end", - } - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("crypto/qa/") - mt.add_cmd("load") - mt.add_cmd("pick") - mt.add_raw("\n") - mt.add_param("_ticker", self.symbol) - mt.add_param("_target", self.target) - mt.add_raw("\n") - mt.add_info("_statistics_") - mt.add_cmd("summary") - mt.add_cmd("normality") - mt.add_cmd("unitroot") - mt.add_info("_plots_") - mt.add_cmd("line") - mt.add_cmd("hist") - mt.add_cmd("cdf") - mt.add_cmd("bw") - mt.add_cmd("acf") - mt.add_cmd("qqplot") - mt.add_info("_rolling_metrics_") - mt.add_cmd("rolling") - mt.add_cmd("spread") - mt.add_cmd("quantile") - mt.add_cmd("skew") - mt.add_cmd("kurtosis") - mt.add_info("_other_") - mt.add_cmd("raw") - mt.add_cmd("decompose") - mt.add_cmd("cusum") - console.print(text=mt.menu_text, menu="Cryptocurrency - Quantitative Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - if self.symbol: - return ["crypto", f"load {self.symbol}", "qa"] - return [] - - @log_start_end(log=logger) - def call_pick(self, other_args: List[str]): - """Process pick command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="pick", - description=""" - Change target variable - """, - ) - parser.add_argument( - "-t", - "--target", - dest="target", - choices=self.data.columns.tolist(), - help="Select variable to analyze", - ) - if other_args and "-t" not in other_args and "-h" not in other_args: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - self.target = ns_parser.target - - @log_start_end(log=logger) - def call_raw(self, other_args: List[str]): - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="raw", - description=""" - Print raw data to console - """, - ) - parser.add_argument( - "-l", - "--limit", - help="Number to show", - type=check_positive, - default=20, - dest="limit", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - default=False, - dest="reverse", - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sortby", - help="The column to sort by", - choices=[x.lower().replace(" ", "") for x in self.data.columns], - type=str.lower, - dest="sortby", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_raw( - data=self.data, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_summary(self, other_args: List[str]): - """Process summary command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="summary", - description=""" - Summary statistics - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_summary( - data=self.data, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_line(self, other_args: List[str]): - """Process line command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="line", - description="Show line plot of selected data or highlight specific datetimes.", - ) - parser.add_argument( - "--log", - help="Plot with y on log scale", - dest="log", - action="store_true", - default=False, - ) - parser.add_argument( - "--ml", - help="Draw vertical line markers to highlight certain events (comma separated dates, e.g. 2020-01-01,2020-02-01)", # noqa: E501 - dest="ml", - type=check_list_dates, - default="", - ) - parser.add_argument( - "--ms", - help="Draw scatter markers to highlight certain events (comma separated dates, e.g. 2021-01-01,2021-02-01)", - dest="ms", - type=check_list_dates, - default="", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - qa_view.display_line( - self.data[self.target], - title=f"{self.symbol} {self.target}", - log_y=ns_parser.log, - markers_lines=ns_parser.ml, - markers_scatter=ns_parser.ms, - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_hist(self, other_args: List[str]): - """Process hist command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="hist", - description=""" - Histogram with density and rug - """, - ) - parser.add_argument( - "-b", - "--bins", - type=check_positive, - default=15, - dest="n_bins", - choices=range(10, 100), - metavar="N_BINS", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_hist( - symbol=self.symbol, - data=self.data, - target=self.target, - bins=ns_parser.n_bins, - ) - - @log_start_end(log=logger) - def call_cdf(self, other_args: List[str]): - """Process cdf command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="cdf", - description=""" - Cumulative distribution function - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_cdf( - symbol=self.symbol, - data=self.data, - target=self.target, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bw(self, other_args: List[str]): - """Process bwy command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="bw", - description=""" - Box and Whisker plot - """, - ) - parser.add_argument( - "-y", - "--yearly", - action="store_true", - default=False, - dest="year", - help="Flag to show yearly bw plot", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_bw( - symbol=self.symbol, - data=self.data, - target=self.target, - yearly=ns_parser.year, - ) - - @log_start_end(log=logger) - def call_decompose(self, other_args: List[str]): - """Process decompose command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="decompose", - description=""" - Decompose time series as: - - Additive Time Series = Level + CyclicTrend + Residual + Seasonality - - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality - """, - ) - parser.add_argument( - "-m", - "--multiplicative", - action="store_true", - default=False, - dest="multiplicative", - help="decompose using multiplicative model instead of additive", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_seasonal( - symbol=self.symbol, - data=self.data, - target=self.target, - multiplicative=ns_parser.multiplicative, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cusum(self, other_args: List[str]): - """Process cusum command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cusum", - description=""" - Cumulative sum algorithm (CUSUM) to detect abrupt changes in data - """, - ) - parser.add_argument( - "-t", - "--threshold", - dest="threshold", - type=float, - default=( - max(self.data[self.target].values) - min(self.data[self.target].values) - ) - / 40, - help="threshold", - ) - parser.add_argument( - "-d", - "--drift", - dest="drift", - type=float, - default=( - max(self.data[self.target].values) - min(self.data[self.target].values) - ) - / 80, - help="drift", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_cusum( - data=self.data, - target=self.target, - threshold=ns_parser.threshold, - drift=ns_parser.drift, - ) - - @log_start_end(log=logger) - def call_acf(self, other_args: List[str]): - """Process acf command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="acf", - description=""" - Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff crypto data - """, - ) - parser.add_argument( - "-l", - "--lags", - dest="lags", - type=check_positive, - default=15, - help="maximum lags to display in plots", - choices=range(5, 100), - metavar="LAGS", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.target != "Close": - console.print( - "Target not Close. For best results, use `pick Close` first." - ) - - qa_view.display_acf( - symbol=self.symbol, - data=self.data, - target=self.target, - lags=ns_parser.lags, - ) - - @log_start_end(log=logger) - def call_rolling(self, other_args: List[str]): - """Process rolling command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rolling", - description=""" - Rolling mean and std deviation - """, - ) - parser.add_argument( - "-w", - "--window", - action="store", - dest="n_window", - type=check_positive, - default=14, - help="Window length", - choices=range(5, 100), - metavar="N_WINDOW", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - rolling_view.display_mean_std( - symbol=self.symbol, - data=self.data, - target=self.target, - window=ns_parser.n_window, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_spread(self, other_args: List[str]): - """Process spread command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="spread", - description="""Shows rolling spread measurement - """, - ) - parser.add_argument( - "-w", - "--window", - action="store", - dest="n_window", - type=check_positive, - default=14, - help="Window length", - choices=range(5, 100), - metavar="N_WINDOW", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - rolling_view.display_spread( - symbol=self.symbol, - data=self.data, - target=self.target, - window=ns_parser.n_window, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_quantile(self, other_args: List[str]): - """Process quantile command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="quantile", - description=""" - The quantiles are values which divide the distribution such that - there is a given proportion of observations below the quantile. - For example, the median is a quantile. The median is the central - value of the distribution, such that half the points are less than - or equal to it and half are greater than or equal to it. - - By default, q is set at 0.5, which effectively is median. Change q to - get the desired quantile (0 float: - """Helper to convert change variation - - Parameters - ---------- - change: float - percentage change - - Returns - ------- - float: - converted value - """ - return (100 + change) / 100 - - -def calculate_hold_value(changeA: float, changeB: float, proportion: float) -> float: - """Calculates hold value of two different coins - - Parameters - ---------- - changeA: float - price change of crypto A in percentage - changeB: float - price change of crypto B in percentage - proportion: float - percentage of first token in pool - - Returns - ------- - float: - hold value - """ - return (change_variation(changeA) * proportion) / 100 + ( - (change_variation(changeB) * (100 - proportion)) / 100 - ) - - -def calculate_pool_value(changeA, changeB, proportion): - """Calculates pool value of two different coins - - Parameters - ---------- - changeA: float - price change of crypto A in percentage - changeB: float - price change of crypto B in percentage - proportion: float - percentage of first token in pool - - Returns - ------- - float: - pool value - """ - return pow(change_variation(changeA), proportion / 100) * pow( - change_variation(changeB), (100 - proportion) / 100 - ) diff --git a/openbb_terminal/cryptocurrency/tools/tools_model.py b/openbb_terminal/cryptocurrency/tools/tools_model.py deleted file mode 100644 index 0401baac238..00000000000 --- a/openbb_terminal/cryptocurrency/tools/tools_model.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Tools Model""" - -__docformat__ = "numpy" - -import logging -from typing import Tuple - -import pandas as pd - -from openbb_terminal.cryptocurrency.tools.tools_helpers import ( - calculate_hold_value, - calculate_pool_value, -) -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def calculate_apy(apr: float, compounding_times: int) -> Tuple[pd.DataFrame, str]: - """Converts apr into apy - - Parameters - ---------- - apr: float - value in percentage - compounding_times: int - number of compounded periods in a year - - Returns - ------- - Tuple[pd.DataFrame, str] - - pd.DataFrame: dataframe with results - - str: narrative version of results - """ - apy = (pow((1 + (apr / 100) / compounding_times), compounding_times) - 1) * 100 - apy_str = f""" -An APR of {apr}% compounded {compounding_times} times per year equals to an APY of {round(apy,3)}% - """ - df = pd.DataFrame( - { - "Metric": [ - "APR", - "Compounding Times", - "APY", - ], - "Value": [ - f"{apr}%", - compounding_times, - f"{round(apy, 3)}%", - ], - } - ) - return df, apy_str - - -@log_start_end(log=logger) -def calculate_il( - price_changeA: float, - price_changeB: float, - proportion: float, - initial_pool_value: float, -) -> Tuple[pd.DataFrame, str]: - """Calculates Impermanent Loss in a custom liquidity pool - - Parameters - ---------- - price_changeA: float - price change of crypto A in percentage - price_changeB: float - price change of crypto B in percentage - proportion: float - percentage of first token in pool - initial_pool_value: float - initial value that pool contains - - Returns - ------- - Tuple[pd.DataFrame, str] - - pd.DataFrame: dataframe with results - - str: narrative version of results - """ - pool_value = calculate_pool_value(price_changeA, price_changeB, proportion) - hold_value = calculate_hold_value(price_changeA, price_changeB, proportion) - il = abs(((pool_value / hold_value) - 1) * 100) - hold_value = hold_value * initial_pool_value - pool_value = pool_value * initial_pool_value - il_str = f""" -Ignoring fees/rewards and only accounting for impermanent loss: - -A change of {price_changeA}% in token A and {price_changeB}% in token B in -a pool with a proportion of {proportion}/{100-proportion} and with an initial -value of ${initial_pool_value} would result in an impermant loss of {round(il,2)}% -If you just hold the tokens you would have ${round(hold_value,2)} whereas -in the pool you would have ${round(pool_value,2)} - """ - df = pd.DataFrame( - { - "Metric": [ - "Price Change Token A", - "Price Change Token B", - "Initial Pool Value", - "Proportion", - "Impermanent Loss", - "Hold Value", - "Pool Value", - ], - "Value": [ - f"{price_changeA}%", - f"{price_changeB}%", - f"${initial_pool_value}", - f"{proportion}/{100-proportion}", - f"{round(il,2)}%", - f"${round(hold_value,2)}", - f"${round(pool_value,2)}", - ], - } - ) - return df, il_str diff --git a/openbb_terminal/cryptocurrency/tools/tools_view.py b/openbb_terminal/cryptocurrency/tools/tools_view.py deleted file mode 100644 index 8fa19898e56..00000000000 --- a/openbb_terminal/cryptocurrency/tools/tools_view.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Tools View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.cryptocurrency.tools.tools_model import calculate_apy, calculate_il -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_apy( - apr: float, - compounding_times: int, - narrative: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Displays APY value converted from APR - - Parameters - ---------- - apr: float - value in percentage - compounding_times: int - number of compounded periods in a year - narrative: str - display narrative version instead of dataframe - export : str - Export dataframe data to csv,json,xlsx file - - Returns - ------- - """ - df, apy_str = calculate_apy(apr, compounding_times) - - if narrative: - console.print(apy_str) - else: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="APR/APY Calculator", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "aprtoapy", - apy_str, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_il( - price_changeA: int, - price_changeB: int, - proportion: int, - initial_pool_value: int, - narrative: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Displays Impermanent Loss in a custom liquidity pool - - Parameters - ---------- - price_changeA: float - price change of crypto A in percentage - price_changeB: float - price change of crypto B in percentage - proportion: float - percentage of first token in pool - initial_pool_value: float - initial value that pool contains - narrative: str - display narrative version instead of dataframe - export : str - Export dataframe data to csv,json,xlsx file - - Returns - ------- - """ - df, il_str = calculate_il( - price_changeA=price_changeA, - price_changeB=price_changeB, - proportion=proportion, - initial_pool_value=initial_pool_value, - ) - if narrative: - console.print(il_str) - else: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Impermanent Loss Calculator", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "il", - df, - sheet_name, - ) diff --git a/openbb_terminal/custom_reports/lunar_crash/coin_highs.png b/openbb_terminal/custom_reports/lunar_crash/coin_highs.png deleted file mode 100644 index 953aa4c98ae3c540fb5679971877317808b6b032..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55138 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%0A5u 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 diff --git a/openbb_terminal/custom_reports/lunar_crash/luna_crash.html b/openbb_terminal/custom_reports/lunar_crash/luna_crash.html deleted file mode 100644 index cfefb8b0615..00000000000 --- a/openbb_terminal/custom_reports/lunar_crash/luna_crash.html +++ /dev/null @@ -1,346 +0,0 @@ - - - - - - luna_crash - - - - - - - -
- OpenBB -
-

Analyst: Minh Hoang

-

Date : 16 May, 2022

-

Time : 00:46 BST

-
-

LUNA CRASH EVENTS

-
-

Over the past few days, we witnessed the bank run of the largest decentralized stablecoin, UST, together with the demise of LUNA. -

-Its effect has spread over the entire Terra ecosystem as well as the entire crypto market. -


Just a few weeks ago, LUNA and the Terra ecosystem are still the darlings of crypto investors.
-Experiencing a 20,000% increase since the beginning of 2021, LUNA reached its ATH of $119 in January 2021. -

Overall, just a few days ago, Terra was the second biggest smart contract platform in terms of total value lock, with almost $22 billion.
-Its biggest dApp is Anchor, a lending and borrowing protocol with the (in)famous APY of 20%.
-This lucrative return is too hard to ignore, even within the crypto market. Within a year, its TVL has shot up from $500M to almost $17.5B. -


-The bloodbath initially started with UST losing its peg against USD. -After that, both LUNA and UST have been completely destroyed. -You might be wondering, how did this happen in less than a week? -To explain this, we first have to understand the stablizing mechanism of the Terra ecosystem. -
-
-A centralized stablecoin such as USDT would needs actual backed assets, such as BTC, to consistently track the dollar. -As UST is a decentralized, and algorithmic stablecoin, the mint and burnt mechanism between LUNA & UST plays the crucial role in sustaining the peg. -
-
-In essence, Terra protocol allow its users to burn 1$ worth of LUNA to mint 1 UST and vice versa, burn UST to mint 1$ worth of LUNA. -
-
-This creates an arbitrage opportunity whenever UST loses its peg against the dollar. -Let’s say when UST is traded at $0.90, you can buy UST and burn it for 1$ worth of LUNA, making a profit of 10 cent. -By burning UST, UST supply would decrease and therefore, raising price back to $1 as in Economics 101. -
-
-The other way around, when UST is traded at $1.10, you can burn $1 worth of LUNA to mint UST. -This would flood more UST to the market, forcing price to go back to $1. -Whenever there’s a price discrepancy between USD and UST, Terra relies on this genius arbitrage incentives to bring price back to $1. -

-But why can’t the burn-and-mint mechanism save the day? Let’s go back to what happened. -
-
-The crash started it off with UST losing its dollar peg repeatedly. -This causes investors, and whales in specific, losing their confidence in this algorithmic stablecoins. -The peak of uncertainty was on the weekend of 7th and 8th of May. -During two days, a massive amount of UST were withdrawn from Anchor protocol. -On Friday, 6th of May, there was still around 14 billion of UST locked in Anchor protocol as deposits. -On Monday, this figure was down to 10.8 billion. This means that around 3.2 billion of UST were taken out over the weekend. -Inevitably, this put a massive selling pressure on UST and lead to UST further losing its peg. -
-
-As UST peg is too far away from 1 dollar, a massive amount of LUNA needs to minted in order to push the price back. -With such a large amount of LUNA dumped in the market, the circulating supply shot up and caused luna price to fall. -Let’s keep in mind that this happened during a market crash, and hence the selling pressure has doubled up. -

-This is clearly reflected in the LUNA’s circulating supply. -In just few days, number of Luna went to the moon, from less than 400 millions token to over 6 trillions. -
-
-Eventually, with the enormous selling pressure, Luna price has fallen below $1 mark, even lower than UST’s supposed value. -This continues until Luna’s market cap became smaller than UST’s. -With this, the peg stablization mechanism can no longer work, ended the hope of many investors that price can still go back up. -


-As a revival plan, Do Kwon proposed to hard-fork Luna to a completely new chain, with a snapshot before the attack. -
-
-This new chain would have maximum supply of 1 billion coins. Out of these:
- * 400M would be given to Luna holders before the depeg happened
- * 400M to UST holders at the time of the upgrade
- * 100M to Luna holders when the blockchain stops functioning
- * 100M for community pool -
-
-He also mentioned to change the mint-and-burnt mechanism to a collateralized version, similar to what MakerDAO does with their DAI stablecoin. -
-
-The biggest question is: do investors have the courage to give Do Kwon and Terra’s team their trust one more time? -This bank run has put an end to a once $40 billion cryptocurrency and once the top 3 biggest stablecoins by market caps, wiping out fortunes of investors and validators. -

- - - \ No newline at end of file diff --git a/openbb_terminal/custom_reports/lunar_crash/luna_supply.png b/openbb_terminal/custom_reports/lunar_crash/luna_supply.png deleted file mode 100644 index 03585b8ad35c13e22a9c2efc4662e5baa9439f22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 350260 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%0DYzpYLSX7^R2Sw|weiOk6) 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
)989f=@w&NI2ve?BaHI2`@g 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 z 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°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
rfGwgtJa`{TWt#mHg}2 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%0Di1Y%!nTVW}EJ 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%0AmM45%ziO34dBtwta)heDy?LtSG>6q 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

o@!PN*w6gk0YhA7~dSW#nO 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⩔I7h*T&BIiy>s}MG2A{sh{Y=z5`z= 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 diff --git a/openbb_terminal/custom_reports/lunar_crash/lunatics.ipynb b/openbb_terminal/custom_reports/lunar_crash/lunatics.ipynb deleted file mode 100644 index 08df658b0be..00000000000 --- a/openbb_terminal/custom_reports/lunar_crash/lunatics.ipynb +++ /dev/null @@ -1,285 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70ba2c8a", - "metadata": {}, - "source": [ - "## Notebook setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "driven-billion", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import io\n", - "import warnings\n", - "import datetime\n", - "import numpy as np\n", - "import pandas as pd\n", - "import base64\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib_inline.backend_inline\n", - "\n", - "import sys\n", - "\n", - "sys.path.append(\"../../../\")\n", - "\n", - "from openbb_terminal import api as openbb\n", - "from openbb_terminal.helper_classes import TerminalStyle\n", - "\n", - "%matplotlib inline\n", - "matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3fe7db5-ec6a-42cf-9e66-52dc1de22370", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " theme = TerminalStyle(\"dark\", \"dark\", \"dark\")\n", - "except:\n", - " pass\n", - "stylesheet = openbb.widgets.html_report_stylesheet()\n", - "# with open(\"./OpenBB_reports_logo.png\", \"rb\") as image_file:\n", - "with open(\"../../reports/OpenBB_reports_logo.png\", \"rb\") as image_file:\n", - " openbb_image_encoded = base64.b64encode(image_file.read())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6aa8b34-aa60-45ce-8ba0-0182addffab8", - "metadata": {}, - "outputs": [], - "source": [ - "author = \"Minh Hoang\"\n", - "report_title = f\"LUNATICS REPORT\"\n", - "report_date = datetime.datetime.now().strftime(\"%d %B, %Y\")\n", - "report_time = datetime.datetime.now().strftime(\"%H:%M\")\n", - "report_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo\n", - "report_name = \"luna_crash\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "abfe37e4-ea38-42c3-8fb5-7cceb233fb12", - "metadata": {}, - "outputs": [], - "source": [ - "body = \"\"\n", - "\n", - "img = f'\"OpenBB\"'\n", - "body += openbb.widgets.header(\n", - " img, author, report_date, report_time, report_timezone, f\"LUNA CRASH EVENTS\"\n", - ")\n", - "\n", - "body += openbb.widgets.tablinks([\"Overview\", \"Before\", \"During\", \"After\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "381b353b-3e01-4db7-ab3d-429b5785e98e", - "metadata": {}, - "outputs": [], - "source": [ - "# OVERVIEW\n", - "text = \"\"\"Over the past few days, we witnessed the bank run on the largest decentralized stablecoin, UST, together with the demise of LUNA. \n", - "

\n", - "Its effect has spread over the entire Terra ecosystem as well as the entire crypto market. \n", - "\"\"\"\n", - "htmlcode = openbb.widgets.p(text, \"width:1200px; margin-bottom:8px\")\n", - "body += openbb.widgets.add_tab(\"Overview\", htmlcode, False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "adc40a71-f245-4542-a303-fbf0970b9759", - "metadata": {}, - "outputs": [], - "source": [ - "# BEFORE\n", - "htmlcode = openbb.widgets.add_external_fig(\n", - " \"./luna_supply.png\", \"width:500px; margin-bottom:16px\"\n", - ")\n", - "text = \"\"\"Just a few weeks ago, LUNA and the Terra ecosystem were the darlings of crypto investors.
\n", - "Experiencing a 20,000% increase since the beginning of 2021, LUNA reached its ATH of $119 in January 2021.\n", - "\"\"\"\n", - "htmlcode += openbb.widgets.p(text, \"width:1200px; margin-bottom:8px\")\n", - "htmlcode += openbb.widgets.add_external_fig(\n", - " \"./coin_highs.png\", \"width:500px; margin-bottom:16px\"\n", - ")\n", - "text = \"\"\"Overall, before the crisis, Terra was the second biggest smart contract platform in terms of total value locked, with almost $22 billion.
\n", - "Its biggest dApp is Anchor, a lending and borrowing protocol with the (in)famous APY of 20%.
\n", - "This lucrative return is too hard to ignore, even within the crypto market. Within a year, its TVL has shot up from $500M to almost $17.5B.\n", - "\"\"\"\n", - "htmlcode += openbb.widgets.p(text, \"width:1200px; margin-bottom:8px\")\n", - "htmlcode += openbb.widgets.add_external_fig(\n", - " \"./tvl.png\", \"width:500px; margin-bottom:16px\"\n", - ")\n", - "body += openbb.widgets.add_tab(\"Before\", htmlcode, False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e08062d0-cc29-4308-87c6-b54d5d7339a7", - "metadata": {}, - "outputs": [], - "source": [ - "# DURING\n", - "text = \"\"\"\n", - "The bloodbath initially started with UST losing its peg against USD. \n", - "After that, both LUNA and UST have been completely destroyed. \n", - "You might be wondering, how did this happen in less than a week? \n", - "To explain this, we first have to understand the stablizing mechanism of the Terra ecosystem.\n", - "
\n", - "
\n", - "A centralized stablecoin such as USDT would need actual backed assets, such as BTC, to consistently track the dollar. \n", - " As UST is a decentralized, and algorithmic stablecoin, the mint and burnt mechanism between LUNA & UST play a crucial role in sustaining the peg.\n", - "
\n", - "
\n", - "In essence, Terra protocol allow its users to burn 1$ worth of LUNA to mint 1 UST and vice versa, burn UST to mint 1$ worth of LUNA.\n", - "
\n", - "
\n", - "This creates an arbitrage opportunity whenever UST loses its peg against the dollar. \n", - "Let’s say when UST is traded at $0.90, you can buy UST and burn it for 1$ worth of LUNA, making a profit of 10 cent. \n", - "By burning UST, UST supply would decrease and therefore, raising price back to $1 as in Economics 101.\n", - "
\n", - "
\n", - "The other way around, when UST is traded at $1.10, you can burn $1 worth of LUNA to mint UST. \n", - "This would flood more UST into the market, forcing the price to go back to $1.\n", - "Whenever there’s a price discrepancy between USD and UST, Terra relies on this genius arbitrage incentives to bring price back to $1.\n", - "\"\"\"\n", - "htmlcode = openbb.widgets.p(text, \"width:1200px; margin-bottom:8px; margin-top:16px\")\n", - "htmlcode += openbb.widgets.add_external_fig(\n", - " \"./ust_terra.png\", \"width:500px; margin-left:2em\"\n", - ")\n", - "\n", - "text = \"\"\"\n", - "But why can’t the burn-and-mint mechanism save the day? Let’s go back to what happened.\n", - "
\n", - "
\n", - "The crash started it off with UST losing its dollar peg repeatedly. \n", - "This causes investors, and whales in specific, to lose their confidence in the security of an algorithmic stablecoin.\n", - "The peak of uncertainty was on the weekend of the 7th and 8th of May.\n", - "During two days, a massive amount of UST was withdrawn from the Anchor protocol.\n", - "On Friday, the 6th of May, there were still around 14 billion UST locked in the Anchor protocol as deposits.\n", - "Then on Monday, this figure was down to 10.8 billion. This means that around 3.2 billion of UST were taken out over the weekend. \n", - "Inevitably, this created massive selling pressure on UST and lead to UST further losing its peg. \n", - "
\n", - "
\n", - "As the UST peg is too far away from 1 dollar, a massive amount of LUNA needs to be minted to bring the price back. \n", - "With such a large amount of LUNA dumped into the market, the circulating supply shot up and caused LUNA's price to fall. \n", - " Let’s keep in mind that this happened during a market crash, and hence the selling pressure has doubled up.\n", - "\"\"\"\n", - "htmlcode += openbb.widgets.p(text, \"width:1200px; margin-bottom:8px; margin-top:16px\")\n", - "htmlcode += openbb.widgets.add_external_fig(\"./luna_terra.png\", \"width:500px;\")\n", - "\n", - "text = \"\"\"\n", - "This was reflected in the LUNA’s circulating supply.\n", - "In just a few days, the number of Luna went to the moon, from less than 400 million tokens to over 6 trillion. \n", - "
\n", - "
\n", - "Eventually, with the enormous selling pressure, Luna's price has fallen below the $1 mark, even lower than UST’s supposed value.\n", - "This continues until Luna’s market cap became smaller than UST’s.\n", - "With this, the peg stabilization mechanism can no longer work, ending the hope of many investors that its price can still go back up.\n", - "This continues until Luna’s market cap became smaller than UST’s. With this, the peg stabilization mechanism can no longer work, ending the hope of many investors that its price can still go back up.\n", - "\"\"\"\n", - "htmlcode += openbb.widgets.p(text, \"width:1200px; margin-bottom:8px; margin-top:16px\")\n", - "body += openbb.widgets.add_tab(\"During\", htmlcode, False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "660ea74b-316a-45b0-af95-ee0a4ec162ef", - "metadata": {}, - "outputs": [], - "source": [ - "# AFTER\n", - "text = \"\"\"\n", - "As a revival plan, Do Kwon proposed to hard-fork Luna to a completely new chain, with a snapshot before the attack. \n", - "
\n", - "
\n", - "This new chain would have maximum supply of 1 billion coins. Out of these:
\n", - " * 400M would be given to Luna holders before the depeg happened
\n", - " * 400M to UST holders at the time of the upgrade
\n", - " * 100M to Luna holders when the blockchain stops functioning
\n", - " * 100M for community pool\n", - "
\n", - "
\n", - "He also mentioned changing the mint-and-burnt mechanism to a collateralized version, similar to what MakerDAO does with their DAI stablecoin.\n", - "Ironically, just early this year, he posted on Twitter: “By my hand $DAI will die.” When UST was struggling to maintain its peg, $DAI stood still with its peg. \n", - "
\n", - "
\n", - "The biggest question is: do investors have the courage to give Do Kwon and Terra’s team their trust one more time? \n", - "This bank run has put an end to a once $40 billion cryptocurrency and once the top 3 biggest stablecoins by market caps, wiping out fortunes of investors and validators. \n", - "\"\"\"\n", - "htmlcode = openbb.widgets.p(text, \"width:1200px; margin-bottom:8px; margin-top:16px\")\n", - "body += openbb.widgets.add_tab(\"After\", htmlcode, False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ddfca2f-658c-41ae-be37-e588cd2daa37", - "metadata": {}, - "outputs": [], - "source": [ - "body += openbb.widgets.tab_clickable_evt()\n", - "\n", - "report = openbb.widgets.html_report(title=report_name, stylesheet=stylesheet, body=body)\n", - "\n", - "# to save the results\n", - "with open(report_name + \".html\", \"w\") as fh:\n", - " fh.write(report)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e15cef06-0d10-4153-b6d3-9762be0ed1dd", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "celltoolbar": "Tags", - "interpreter": { - "hash": "aa1c4d436101635411b2afa2ce061827695b6e3d0efa7f05338cbf11ad26165e" - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/custom_reports/lunar_crash/tvl.png b/openbb_terminal/custom_reports/lunar_crash/tvl.png deleted file mode 100644 index e221bd759d5d5bca1c3dabe61bde06e12c45a715..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 667206 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 jA3Kzy(2xWZR)Bh@*Phfj+^vFght!7^#?-_w3T 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�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 z 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

YuV1z`O2! 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�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 +am>d9+ci2 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 z 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 diff --git a/openbb_terminal/custom_reports/lunar_crash/ust_terra.png b/openbb_terminal/custom_reports/lunar_crash/ust_terra.png deleted file mode 100644 index 3dd13824ca40ed164b6db27d171621d066a64972..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 465754 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�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�{`aFp~u=%9Ri*%CH!IV-E{3( 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 CBuah& diff --git a/openbb_terminal/dashboards/__init__.py b/openbb_terminal/dashboards/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/dashboards/dashboards_controller.py b/openbb_terminal/dashboards/dashboards_controller.py deleted file mode 100644 index 60d8ae61dad..00000000000 --- a/openbb_terminal/dashboards/dashboards_controller.py +++ /dev/null @@ -1,268 +0,0 @@ -"""Dashboards Module.""" - -__docformat__ = "numpy" - -import argparse -import atexit -import logging -import os -import re -import socket -import sys -import threading -import time -from pathlib import Path -from subprocess import PIPE, STDOUT -from typing import List, Optional - -import numpy as np -import psutil - -from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY -from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -# pylint: disable=consider-using-with - - -logger = logging.getLogger(__name__) -JUPYTER_STARTED = False - - -class DashboardsController(BaseController): - """Dashboards Controller class.""" - - CHOICES_COMMANDS = [ - "stocks", - "correlation", - "chains", - "shortdata", - "futures", - "forecasting", - "indicators", - "options", - ] - PATH = "/dashboards/" - - def __init__(self, queue: Optional[List[str]] = None): - """Construct controller.""" - super().__init__(queue) - self.processes: List[psutil.Process] = [] - # pylint: disable=E1101,W0212 - self.parent_path = ( - Path(sys._MEIPASS) if hasattr(sys, "frozen") else REPOSITORY_DIRECTORY # type: ignore - ) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} - - choices["support"] = self.SUPPORT_CHOICES - choices["about"] = self.ABOUT_CHOICES - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help.""" - mt = MenuText("dashboards/") - mt.add_raw("Streamlit Apps:\n") - mt.add_cmd("stocks") - mt.add_cmd("chains") - mt.add_cmd("correlation") - mt.add_cmd("indicators") - mt.add_cmd("forecasting") - mt.add_cmd("futures") - mt.add_cmd("shortdata") - mt.add_cmd("options") - console.print(text=mt.menu_text, menu="Dashboards") - - @log_start_end(log=logger) - def call_stocks(self, other_args: List[str]): - """Process stocks command.""" - self.create_call_streamlit(other_args, "Stocks") - - @log_start_end(log=logger) - def call_correlation(self, other_args: List[str]): - """Process correlation command.""" - self.create_call_streamlit(other_args, "Correlation") - - @log_start_end(log=logger) - def call_chains(self, other_args: List[str]): - """Process chains command.""" - self.create_call_streamlit(other_args, "Chains") - - @log_start_end(log=logger) - def call_shortdata(self, other_args: List[str]): - """Process shortdata command.""" - self.create_call_streamlit(other_args, "Short_Data") - - @log_start_end(log=logger) - def call_futures(self, other_args: List[str]): - """Process futures command.""" - self.create_call_streamlit(other_args, "Futures") - - @log_start_end(log=logger) - def call_forecasting(self, other_args: List[str]): - """Process forecasting command.""" - self.create_call_streamlit(other_args, "Forecasting") - - @log_start_end(log=logger) - def call_indicators(self, other_args: List[str]): - """Process indicators command.""" - self.create_call_streamlit(other_args, "Indicators") - - @log_start_end(log=logger) - def call_options(self, other_args: List[str]): - """Process indicators command.""" - self.create_call_streamlit(other_args, "Options") - - def kill_processes(self) -> None: - """Kills all processes started by this class.""" - for process in [p for p in self.processes if p.is_running()]: - for child in process.children(recursive=True): - if child.is_running(): - child.kill() - - process.kill() - - def check_processes(self, name: Optional[str] = None) -> str: - """Check if a process is already running, and returns the url.""" - - for process in self.processes: - if not process.is_running(): - self.processes.remove(process) - continue - - cmdline = " ".join(process.cmdline()) - port = re.findall(r"--port=(\d+)", cmdline) - port = port[0] if port else "" - - if re.findall(r"-m\s+.*streamlit_run|streamlit", cmdline): - return f"http://localhost:{port}/{name}" - - return "" - - @staticmethod - def get_free_port() -> int: - """Search for a random free port number.""" - not_free = True - while not_free: - port = np.random.randint(7000, 7999) - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - res = sock.connect_ex(("localhost", port)) - if res != 0: - not_free = False - return port - - def create_call_streamlit( - self, other_args: List[str], name: str, filename: Optional[str] = None - ) -> None: - """Create a streamlit call command. - - A metafunction that creates a launch command for a streamlit dashboard. - - Parameters - ---------- - other_args : List[str] - Other arguments to pass to the streamlit command. - name : str - Name of the dashboard. - filename : Optional[str], optional - Filename of the dashboard, by default None - """ - filename = filename if filename else name - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog=name, - description=f"""Shows streamlit {name} dashboard""", - ) - parser.add_argument( - "-n", - "--no-input", - action="store_true", - default=False, - dest="input", - help="Skips confirmation to run server.", - ) - ns_parser = self.parse_simple_args(parser, other_args) - - if ns_parser: - streamlit_run = Path(__file__).parent / "streamlit_run.py" - python_path = streamlit_run.relative_to(self.parent_path).with_suffix("") - cmd = ( - [sys.executable, "-m", ".".join(python_path.parts)] - if not hasattr(sys, "frozen") - else [sys.executable, "--streamlit"] - ) - - folder = "stream" if name == "Forecasting" else "stream/pages" - filepath = Path(__file__).parent / folder / f"{name}.py" - file = filepath.relative_to(self.parent_path).as_posix() - - process_check = self.check_processes() - response = "n" - if not ns_parser.input and not process_check: - response = console.input( - "\nWarning: opens a port on your computer to run a streamlit server.\n" - "[green]Would you like us to run the server for you Y/n? [/]" - ) - - if ns_parser.input or response.lower() == "y" and not process_check: - port = self.get_free_port() - os.environ["PYTHONPATH"] = str(self.parent_path) - cmd += [f"--port={port}"] - - self.processes.append( - psutil.Popen( - cmd, - stdout=PIPE, - stderr=STDOUT, - stdin=PIPE, - env=os.environ, - cwd=str(self.parent_path), - ) - ) - atexit.register(self.kill_processes) - - console.print( - "[green]Waiting for streamlit to start. This may take a few seconds.[/green]" - ) - - thread = threading.Thread( - target=non_blocking_streamlit, - args=(self.processes[-1],), - daemon=True, - ) - thread.start() - time.sleep(6 if sys.platform == "darwin" else 3) - - if not self.processes[-1].is_running(): - self.processes.remove(self.processes[-1]) - console.print( - "[red]Error: streamlit server failed to start.[/]\n" - "It might need to be updated. Try running:\n" - "[green]pip install streamlit --upgrade[/]" - ) - return - elif response.lower() == "n": - console.print( - f"\n\nType: streamlit run '{file}'\ninto a terminal to run." - ) - - if self.check_processes(): - plots_backend().send_url( - url=self.check_processes(name), - title=f"{filename.replace('_', ' ').title()} Dashboard", - ) - - -def non_blocking_streamlit(process: psutil.Popen) -> None: - """We need this or else streamlit engine will not run the modules.""" - while process.is_running(): - process.communicate() diff --git a/openbb_terminal/dashboards/stream/Forecasting.py b/openbb_terminal/dashboards/stream/Forecasting.py deleted file mode 100644 index 6c9383bf92f..00000000000 --- a/openbb_terminal/dashboards/stream/Forecasting.py +++ /dev/null @@ -1,376 +0,0 @@ -import re -from datetime import date, datetime, timedelta -from typing import Any, Optional, Union -from unittest.mock import patch - -import pandas as pd -import streamlit as st -import yfinance as yf -from rich.table import Table - -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.dashboards.stream import ( - common_vars, - streamlit_helpers as st_helpers, -) -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - -st.set_page_config( - layout="wide", - page_title="Forecasting", - page_icon="📈", - initial_sidebar_state="expanded", -) -st_helpers.set_css() -st_helpers.set_current_page("Forecasting") - - -EXPLAINABILITY_FIGURE: Union[OpenBBFigure, None] = None -PAST_COVERAGE_PRINT: str = "
" - -# Rows and columns for the dashboard layout -st.sidebar.markdown( - "

OpenBB Forecasting

", - unsafe_allow_html=True, -) -r1c1 = st.sidebar.container() -( - r1c2, - r1c3, -) = st.sidebar.columns([1, 1]) - -r1c4, r1c5 = st.sidebar.columns([1, 1]) -r2c1, r2c2 = st.sidebar.columns([1, 1]) -r2c3 = st.sidebar.container() - -st.sidebar.markdown("""---""") -forecast_button = st.sidebar.button("Get forecast") - -plotly_chart, table_container = st.columns([4, 1]) -explainability, past_cov_legend = st.columns([4, 1]) - - -def special_st(text: Optional[str] = None) -> Optional[str]: - if isinstance(text, Table): - with table_container: - text.title = re.sub(st_helpers.REGEX_RICH, "", text.title).replace( - "Actual price:", - "

Actual price:" - "", - ) - st.write( - f"{text.title}

", - unsafe_allow_html=True, - ) - st.table(st_helpers.rich_to_dataframe(text)) - elif isinstance(text, str) and "[green]" in text: - global PAST_COVERAGE_PRINT # pylint: disable=W0603 # noqa - PAST_COVERAGE_PRINT += re.sub(st_helpers.REGEX_RICH, "", text) + "
" - - return text - - -def mock_show(self: OpenBBFigure, *args, **kwargs): # pylint: disable=W0613 - if "Target" not in self.layout.title.text: - return self - - # pylint: disable=W0603 - global EXPLAINABILITY_FIGURE # noqa - EXPLAINABILITY_FIGURE = self - return self - - -class Handler: - def __init__(self): - st_helpers.load_state("last_tickers", "") - st_helpers.load_state("last_intervals", "1d") - st_helpers.load_state("df", pd.DataFrame()) - self.default_opts = { - key: [] for key in ["target_widget", "column_widget", "past_covs_widget"] - } - st_helpers.load_widget_options(self.default_opts) - - self.feature_model = None - self.feature_target = None - - # pylint: disable=R0913 - def handle_changes( - self, - past_covariates: list[str], - start: date, - end: date, - interval: str, - tickers: str, - target_column: str, - model: str, - n_predict: int, - naive: bool, - forecast_only: bool, - ): - del naive, forecast_only - if not tickers and not target_column: - return - - start_n = datetime(start.year, start.month, start.day).date() - end_n = datetime(end.year, end.month, end.day).date() - - if interval in ["1d", "5d", "1wk", "1mo", "3mo"]: - result = st.session_state["df"].loc[ - (st.session_state["df"]["date"].dt.date >= start_n) - & (st.session_state["df"]["date"].dt.date <= end_n) - ] - else: - result = st.session_state["df"] - - # we format the datatime column to be a string - # otherwise the model will throw an error - result["date"] = result["date"].dt.date.astype(str) - - if not target_column: - target_column = st.session_state["df"].columns[0] - - with patch.object(console, "print", special_st): - if helpers.check_data(result, target_column): - final_df = helpers.clean_data(result) - kwargs: dict[str, Any] = {} - - forecast_model = common_vars.FORECAST_MODEL_OPTS[model] - contains_covariates = st_helpers.has_parameter( - forecast_model, "past_covariates" - ) - if contains_covariates and past_covariates != []: - kwargs["past_covariates"] = ",".join(past_covariates) - if st_helpers.has_parameter(forecast_model, "output_chunk_length"): - kwargs["output_chunk_length"] = n_predict - - if final_df.empty: - st.warning("There was an error with the data") - return - - # n_predict and output_chunk_length must be the same if there are past covariates - # run a spinner while we wait for the model to run - kwargs = dict( - data=final_df, - target_column=target_column, - n_predict=n_predict, - dataset_name=tickers.upper(), - **kwargs, - external_axes=True, - ) - - with st.spinner("Running model..."), patch.object( - OpenBBFigure, "show", mock_show - ): - fig: OpenBBFigure = getattr( - common_vars.openbb.forecast, f"{model}_chart" - )(**kwargs) - - with plotly_chart: - dt_now = datetime.now().strftime("%Y%m%d_%H%M%S") - filename = ( - f"{dt_now}_{tickers}_{model}_{target_column.replace(' ', '_')}" - ) - - fig.update_layout( - title=dict(x=0.5, xanchor="center", yanchor="top", y=0.99), - showlegend=True, - margin=dict(t=40, l=30), - height=500, - legend=dict( - bgcolor="rgba(0,0,0,0.5)", - bordercolor="#F5EFF3", - borderwidth=1, - x=0.99, - xanchor="right", - ), - ) - fig.show(external=True) - st.plotly_chart( - fig, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - toImageButtonOptions=dict( - format="png", - filename=filename, - ), - ), - ) - with explainability: - if EXPLAINABILITY_FIGURE: - fig2 = EXPLAINABILITY_FIGURE - - fig2.show(external=True) - fig2.update_layout( - margin=dict(r=190, l=30), - showlegend=True, - height=600, - width=1000, - ) - st.plotly_chart( - EXPLAINABILITY_FIGURE, - config=dict( - scrollZoom=True, - displaylogo=False, - toImageButtonOptions=dict( - format="png", - filename=filename, - ), - ), - ) - with past_cov_legend: - global PAST_COVERAGE_PRINT # pylint: disable=W0603 # noqa - text = PAST_COVERAGE_PRINT - if text != "
": - st.write( - f"{text}", - unsafe_allow_html=True, - ) - PAST_COVERAGE_PRINT = "
" - - def handle_eng(self, target, feature): - self.feature_target = target - self.feature_model = common_vars.FORECAST_FEAT_ENGS[feature] - - def on_ticker_change(self): - tickers = st.session_state.ticker - if tickers: - interval = st.session_state.interval - start = st.session_state.start - end = st.session_state.end - if ( - tickers != st.session_state["last_tickers"] - or interval != st.session_state["last_interval"] - ): - kwargs = {} - if interval in ["1d", "5d", "1wk", "1mo", "3mo"]: - kwargs.update({"period": "max"}) - else: - kwargs.update({"start": start, "end": end + timedelta(days=1)}) - - df: pd.DataFrame = yf.download( # type: ignore - tickers, - interval=interval, - progress=False, - **kwargs, - ) - df = df.dropna() - if not df.empty: - df.index = pd.to_datetime(df.index).tz_localize(None) - st.session_state["df"] = st_helpers.format_df(df) - st.session_state["last_tickers"] = tickers - st.session_state["last_interval"] = interval - st.session_state["widget_options"]["target_widget"] = sorted( - [x for x in st.session_state["df"] if x != "date"] - ) - st.session_state["widget_options"]["past_covs_widget"] = sorted( - [x for x in st.session_state["df"] if x != "date"] - ) - st.session_state["widget_options"]["column_widget"] = sorted( - [x for x in st.session_state["df"] if x != "date"] - ) - - def run(self): - with r1c1: - ticker = st.text_input( - "Ticker", "", key="ticker", on_change=self.on_ticker_change - ) - with r1c2: - start_date = st.date_input( - "Start date", date.today() - timedelta(weeks=104), key="start" - ) - with r1c3: - end_date = st.date_input("End date", date.today(), key="end") - with r1c4: - target_interval = st.selectbox( - "Interval", - index=0, - key="interval", - options=common_vars.INTERVAL_OPTS[3:], - ) - with r1c5: - n_predict = st.selectbox( - "Prediction Days", index=3, key="n_predict", options=list(range(2, 31)) - ) - - model = ( - "expo" - if not hasattr(st.session_state, "model") - else st.session_state["model"] - ) - enable_past_covs = st_helpers.has_parameter( - common_vars.FORECAST_MODEL_OPTS[model], "past_covariates" - ) - - col_order = [r2c3, r2c1, r2c2] - if enable_past_covs: - col_order = [r2c1, r2c2, r2c3] - - with col_order[1]: - target_widget = st.selectbox( - "Target", - options=st_helpers.get_widget_options( - self.default_opts, "target_widget" - ), - ) - - with col_order[2]: - model_widget = st.selectbox( - "Model", - options=list(common_vars.FORECAST_MODEL_OPTS), - key="model", - ) - - past_covs_widget = None - if enable_past_covs: - with col_order[0]: - past_covs_widget = st.multiselect( - "Past Covariates", - options=st_helpers.get_widget_options( - self.default_opts, "past_covs_widget" - ), - disabled=not enable_past_covs, - label_visibility="hidden" if not enable_past_covs else "visible", - ) - - if forecast_button: - # pylint: disable=W0603 - global EXPLAINABILITY_FIGURE # noqa - EXPLAINABILITY_FIGURE = None - if ticker: - self.handle_changes( - past_covariates=past_covs_widget, - start=start_date, - end=end_date, - interval=target_interval, - tickers=ticker, - target_column=target_widget, - model=model_widget, - n_predict=n_predict, - naive=False, - forecast_only=False, - ) - else: - st.write("Please select a ticker") - - # TODO need to new add in business days and merge to make new DF before plotting - # create df with 5 next business days from a start date - # next_business_days = pd.date_range(start=end_date, periods=5, freq="B") - - # if st.button("Add Column"): - # kwargs = {} - # if has_parameter(handler.feature_model, "target_column"): - # kwargs["target_column"] = handler.feature_target - # handler.df = handler.feature_model(handler.df, **kwargs) - # st.session_state["widget_options"]["past_covs_widget"] = handler.df.columns - - -if __name__ == "__main__": - handler = Handler() - handler.run() diff --git a/openbb_terminal/dashboards/stream/__init__.py b/openbb_terminal/dashboards/stream/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/dashboards/stream/common_vars.py b/openbb_terminal/dashboards/stream/common_vars.py deleted file mode 100644 index cfbf3419012..00000000000 --- a/openbb_terminal/dashboards/stream/common_vars.py +++ /dev/null @@ -1,125 +0,0 @@ -from openbb_terminal.core.session.current_system import set_system_variable - -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - -# Import the OpenBB SDK -# pylint: disable=wrong-import-position -from openbb_terminal.sdk import openbb # noqa: E402 - -INTERVAL_OPTS = [ - "5m", - "15m", - "30m", - "1d", - "5d", - "1wk", - "1mo", - "3mo", -] - -STOCKS_ROWS = [ - "sector", - "market_cap", - "beta", - "year_high", - "year_low", - "floatShares", - "sharesShort", - "exDividendDate", -] - -STOCKS_VIEWS = { - "Raw Data": lambda x, y: x, - "Percent Change": lambda x, y: x.pct_change(), - "Rolling Average": lambda x, y: x.rolling(y).mean(), - "Rolling Variance": lambda x, y: x.rolling(y).var(), - "Rolling Standard Deviation": lambda x, y: x.rolling(y).var() ** 0.5, - "Rolling Coefficient of Variation": lambda x, y: (x.rolling(y).var() ** 0.5) - / (x.rolling(y).mean()), -} - - -STOCKS_CLEAN_ROW = { - "sector": "Sector", - "market_cap": "M Cap", - "beta": "Beta", - "year_high": "52W High", - "year_low": "52W Low", - "floatShares": "Floats", - "sharesShort": "Shorts", - "exDividendDate": "Ex-Div", -} - -# pylint: disable=E1101 -FORECAST_MODEL_OPTS = { - "expo": openbb.forecast.expo, # type: ignore - "theta": openbb.forecast.theta, # type: ignore - "linregr": openbb.forecast.linregr, # type: ignore - "regr": openbb.forecast.regr, # type: ignore - "rnn": openbb.forecast.rnn, # type: ignore - "brnn": openbb.forecast.brnn, # type: ignore - "nbeats": openbb.forecast.nbeats, # type: ignore - "tcn": openbb.forecast.tcn, # type: ignore - "trans": openbb.forecast.trans, # type: ignore - "tft": openbb.forecast.tft, # type: ignore -} - -FORECAST_FEAT_ENGS = { - "ema": openbb.forecast.ema, # type: ignore - "sto": openbb.forecast.sto, # type: ignore - "rsi": openbb.forecast.rsi, # type: ignore - "roc": openbb.forecast.roc, # type: ignore - "mom": openbb.forecast.mom, # type: ignore - "atr": openbb.forecast.atr, # type: ignore - "delta": openbb.forecast.delta, # type: ignore - "signal": openbb.forecast.signal, # type: ignore -} -# pylint: enable=E1101 - - -PLOTLY_MODEBAR = """ - -""" - - -PLOTLY_CONFIG = { - "modeBarButtonsToRemove": ["lasso2d", "select2d"], - "modeBarButtons": [ - ["toImage"], - [ - "drawline", - "drawopenpath", - "drawcircle", - "drawrect", - "eraseshape", - ], - ["zoomIn2d", "zoomOut2d", "autoScale2d", "zoom2d", "pan2d"], - ], -} diff --git a/openbb_terminal/dashboards/stream/pages/Chains.py b/openbb_terminal/dashboards/stream/pages/Chains.py deleted file mode 100644 index c6508715209..00000000000 --- a/openbb_terminal/dashboards/stream/pages/Chains.py +++ /dev/null @@ -1,144 +0,0 @@ -import pandas as pd -import streamlit as st -import yfinance - -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.dashboards.stream import streamlit_helpers as st_helpers - -pd.options.plotting.backend = "plotly" -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - -st.set_page_config( - layout="wide", - page_title="Chains", - initial_sidebar_state="expanded", -) -st_helpers.set_current_page("Chains") -st_helpers.set_css() - -st.sidebar.markdown( - "

Option Chain Dashboard

", - unsafe_allow_html=True, -) -st.sidebar.write("Select a ticker and expiry to view the option chain") -TICKER_WIDGET = st.sidebar.empty() -EXPIRY_WIDGET = st.sidebar.empty() -INST_WIDGET = st.sidebar.empty() -X_WIDGET = st.sidebar.empty() -Y_WIDGET = st.sidebar.empty() - -OPTS = [ - "lastTradeDate", - "strike", - "lastPrice", - "bid", - "ask", - "change", - "percentChange", - "volume", - "openInterest", - "impliedVolatility", -] - - -def clean_str(string): - new_str = "" - for letter in string: - if letter.isupper(): - new_str += " " - new_str += letter - return new_str.title() - - -def format_plotly(fig: OpenBBFigure, x, y, ticker, expiry, inst): - fig.update_yaxes(title=clean_str(y)) - fig.update_xaxes(title=clean_str(x)) - expires = ", ".join(expiry) - title = ( - f"{clean_str(y)} vs. {clean_str(x)} for {ticker.upper()} {inst}s on {expires}" - ) - fig.update_layout( - margin=dict(t=40), - autosize=False, - width=1000, - height=500, - title=dict( - text=title, - y=0.98, - x=0.5, - xanchor="center", - yanchor="top", - ), - ) - - -class Chart: - def __init__(self): - self.stock = st_helpers.load_state("stock", {}) - self.last_ticker = st_helpers.load_state("last_ticker", "") - self.expiry = st_helpers.load_state("expiry", []) - self.dfs = st_helpers.load_state("dfs", {}) - self.options = st_helpers.load_state("options", []) - - def update(self, ticker, inst, x, y): - if self.expiry: - fig = OpenBBFigure() - for expire in self.expiry: - if expire not in self.dfs: - self.dfs[expire] = self.stock.option_chain(expire) - group = self.dfs[expire] - df = group.calls if inst == "Call" else group.puts - - fig.add_scatter( - x=df[x], - y=df[y], - mode="lines", - connectgaps=True, - name=expire, - ) - - format_plotly(fig, x, y, ticker, self.expiry, inst) - fig.update_layout(margin=dict(l=40)) - st.plotly_chart(fig, use_container_width=True) - - def on_change(self): - ticker = st_helpers.load_state("ticker", "") - if ticker and ticker != self.last_ticker: - stock = yfinance.Ticker(ticker) - self.options = list([*stock.options]) - st_helpers.save_state("options", self.options) - self.last_ticker = ticker - st_helpers.save_state("last_ticker", self.last_ticker) - self.stock = stock - st_helpers.save_state("stock", self.stock) - - def run(self): - with TICKER_WIDGET.container(): - ticker = TICKER_WIDGET.text_input( - "Ticker", key="ticker", on_change=self.on_change - ) - if self.options: - with EXPIRY_WIDGET.container(): - exp = EXPIRY_WIDGET.multiselect( - "Expiry", - st_helpers.load_state("options", []), - key="exp", - default=self.options[0] if self.options else None, - ) - self.expiry = exp - - with INST_WIDGET.container(): - inst = INST_WIDGET.selectbox("Type", ["Put", "Call"]) - - with X_WIDGET.container(): - x = X_WIDGET.selectbox("X", OPTS, index=1) - with Y_WIDGET.container(): - y = Y_WIDGET.selectbox("Y", OPTS, index=3) - - self.update(ticker, inst, x, y) - - -if __name__ == "__main__": - Chart().run() diff --git a/openbb_terminal/dashboards/stream/pages/Correlation.py b/openbb_terminal/dashboards/stream/pages/Correlation.py deleted file mode 100644 index 3b875dcf05a..00000000000 --- a/openbb_terminal/dashboards/stream/pages/Correlation.py +++ /dev/null @@ -1,97 +0,0 @@ -from datetime import datetime, timedelta - -import pandas as pd -import streamlit as st -import yfinance as yf - -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.dashboards.stream import streamlit_helpers as st_helpers - -pd.options.plotting.backend = "plotly" -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - -st.set_page_config( - layout="wide", - page_title="Correlation", - initial_sidebar_state="expanded", -) - -st_helpers.set_current_page("Correlation") -st_helpers.set_css() -st.sidebar.markdown( - "

Correlation Analysis

", - unsafe_allow_html=True, -) - - -class Chart: - def __init__(self): - self.last_tickers = st_helpers.load_state("last_tickers", "") - self.last_interval = st_helpers.load_state("last_interval", "1d") - self.df = st_helpers.load_state("df", pd.DataFrame()) - - def create( - self, data: str, tickers: str, start: datetime, end: datetime, interval: str - ): - if tickers: - if tickers != self.last_tickers or interval != self.last_interval: - kwargs = {} - if interval in ["1d", "5d", "1wk", "1mo", "3mo"]: - kwargs.update({"period": "max"}) - else: - kwargs.update({"start": start, "end": end}) # type: ignore - - self.df = yf.download( - tickers, interval=interval, progress=False, **kwargs - ) - self.last_tickers = tickers - self.last_interval = interval - - start_n = datetime(start.year, start.month, start.day) - end_n = datetime(end.year, end.month, end.day) - - df = self.df[data] - - if not isinstance(df, pd.Series): - if interval in ["1d", "5d", "1wk", "1mo", "3mo"]: - result = df.loc[(df.index >= start_n) & (df.index <= end_n)].corr() - else: - result = df.corr() - - base = [ - [ - "black" if x == 1 else "lightgreen" if x > 0 else "lightpink" - for x in result[y].tolist() - ] - for y in result.columns - ] - base = [["lightgray" for _ in range(result.shape[0])]] + base - result = result.reset_index() - result.index = result["index"] - result = result.drop(columns=["index"]) - - st.table(result) - - def run(self): - data_opts = ["Open", "Close", "High", "Low", "Volume"] - tickers_widget = st.sidebar.text_input( - "Tickers", "TSLA,AAPL", help="Separate tickers with commas" - ) - data_widget = st.sidebar.selectbox( - "Data", data_opts, index=2, help="Data Column to use" - ) - - base_date = datetime.today() - timedelta(days=365) - start_widget = st.sidebar.date_input("Start", base_date) - end_widget = st.sidebar.date_input("End", datetime.today()) - interval_opts = ["1d", "5d", "1wk", "1mo", "3mo"] - interval_widget = st.sidebar.selectbox("Interval", interval_opts, index=0) - - self.create( - data_widget, tickers_widget, start_widget, end_widget, interval_widget - ) - - -if __name__ == "__main__": - Chart().run() diff --git a/openbb_terminal/dashboards/stream/pages/Futures.py b/openbb_terminal/dashboards/stream/pages/Futures.py deleted file mode 100644 index 6f982bcd64b..00000000000 --- a/openbb_terminal/dashboards/stream/pages/Futures.py +++ /dev/null @@ -1,248 +0,0 @@ -import calendar -import os -import sys -from datetime import datetime - -import pandas as pd -import streamlit as st -import yfinance as yf - -from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.dashboards.stream import streamlit_helpers as st_helpers - -pd.options.plotting.backend = "plotly" -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - -st.set_page_config( - layout="wide", - page_title="Futures", - initial_sidebar_state="expanded", -) -st_helpers.set_current_page("Futures") -st_helpers.set_css() - -df = pd.read_csv(MISCELLANEOUS_DIRECTORY / "futures" / "futures.csv") - -# These are the symbols that futures use for each month -months = ["F", "G", "H", "J", "K", "M", "N", "Q", "U", "V", "X", "Z"] -st.sidebar.markdown( - "

Futures Analysis

", - unsafe_allow_html=True, -) - - -class HiddenPrints: - def __enter__(self): - self._original_stdout = sys.stdout - sys.stdout = open(os.devnull, "w") # noqa - - # pylint: disable=unused-argument - def __exit__(self, exc_type, exc_val, exc_tb): # noqa - sys.stdout.close() - sys.stdout = self._original_stdout - - -def format_plotly(fig: OpenBBFigure): - fig.update_yaxes(title="Price") - fig.update_xaxes(title="Date") - fig.update_layout( - margin=dict(l=0, r=10, t=10, b=10), - autosize=False, - width=900, - height=500, - legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), - ) - - -def create_line(visual, x, y, name, fig: OpenBBFigure): - if visual == "line": - fig.add_scatter(x=x, y=y, mode="lines", name=name) # , connectgaps=True - if visual == "scatter": - fig.add_scatter(x=x, y=y, mode="markers", name=name) - if visual == "candle": - fig.add_candlestick( - x=x, - open=y["Open"], - close=y["Close"], - high=y["High"], - low=y["Low"], - name=name, - ) - - -def build_ticker(ticker: str, month: int, year: int) -> str: - if ticker: - row = df[df["Ticker"] == f"{ticker.split(':')[0]}"].iloc[0] - row = row.to_dict() - the_tick = row["Ticker"].replace("=F", "") - return f"{the_tick}{months[month-1]}{str(year)[-2:]}.{row['Exchange']}" - return "" - - -def next_ticker(ticker: str) -> str: - symbol, exchange = ticker.split(".") - month = symbol[-3] - if month == "Z": - new_month = "F" - new_year = int(symbol[-2:]) + 1 - else: - index = months.index(month) - new_month = months[index + 1] - new_year = int(symbol[-2:]) - return f"{symbol[:-3]}{new_month}{new_year}.{exchange}" - - -def get_column(df: pd.DataFrame, column: str): - sub_df = df.xs(column, level=1, axis=1, drop_level=False) - sub_df.columns = sub_df.columns.to_flat_index().map(lambda x: x[0]) - sub_df = sub_df.dropna(how="all") - sub_df = sub_df.sort_index() - if sub_df.empty: - return None - return sub_df - - -def get_date(x: str) -> datetime: - ticker, _ = x.split(".") - month_str = ticker[-3] - month = months.index(month_str) + 1 - year = int(ticker[-2:]) + 2000 - day = calendar.monthrange(year, month) - return datetime(year, month, day[1]) - - -CATEGORIES = df["Category"].unique().tolist() -EXCHANGES = df[df["Category"].isin([CATEGORIES[0]])]["Exchange"].unique().tolist() -FILT_DF = df[df["Category"].isin([CATEGORIES[0]]) & df["Exchange"].isin([EXCHANGES[0]])] -TICKERS_RAW = FILT_DF[["Ticker", "Description"]].values.tolist() -TICKERS = [f"{x.replace('=F', '')}: {y}" for x, y in TICKERS_RAW] - -CATEGORIES.sort() -EXCHANGES.sort() -TICKERS.sort() - - -class Chart: - def __init__(self): - self.exchange = st_helpers.load_state("exchanges", [EXCHANGES[0]]) - self.tickers = st_helpers.load_state("tickers", {}) - self.last_ticker = st_helpers.load_state("last_ticker", TICKERS[0]) - self.last_exchange = st_helpers.load_state("last_exchange", EXCHANGES[0]) - self.default_opts = { - "exch_widget": EXCHANGES, - "tickers_widget": TICKERS, - "cat_widget": CATEGORIES, - } - st_helpers.load_widget_options(self.default_opts) - - def create_stock(self, chart_type, contracts, ticker): - if not ticker: - return - if self.last_ticker != ticker or not self.tickers: - now = datetime.now() - clean_ticker = build_ticker(ticker, now.month, now.year) - if clean_ticker: - raw_tickers = [clean_ticker] - for _ in range(36): - new_ticker = next_ticker(raw_tickers[-1]) - raw_tickers.append(new_ticker) - raw_ticker = ",".join(raw_tickers) - with HiddenPrints(): - # ,period="max" - dfs = yf.download(raw_ticker, progress=False) - self.tickers = { - x: {"data": get_column(dfs, x), "date": get_date(x)} - for x in raw_tickers - } - self.last_ticker = ticker - - fig = OpenBBFigure() - if chart_type[0][:4] == "Hist": - for i, (key, item) in enumerate(self.tickers.items()): - if i == contracts: - break - result = item["data"] - if result is not None: - create_line( - visual="line", - x=result.index, - y=result["Adj Close"], - name=key, - fig=fig, - ) - else: - x = [] - y = [] - for _, value in self.tickers.items(): - if len(x) > contracts: - break - if value["data"] is None: - continue - x.append(value["date"]) - y.append(value["data"]["Adj Close"].iloc[-1]) - create_line(visual="line", x=x, y=y, name="Future Curve", fig=fig) - - format_plotly(fig) - fig.show(external=True) - st.plotly_chart( - fig, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - ), - ) - - def on_change(self): - category = st.session_state.cat_widget - exchange = st.session_state.exch_widget - - # Filter exchange widget - exch_filtered = df[df["Category"].isin(category)]["Exchange"].unique().tolist() - exch_filtered.sort() - st.session_state["widget_options"]["exch_widget"] = exch_filtered - - # Filter ticker widget - filtered = df[df["Category"].isin(category) & df["Exchange"].isin(exchange)] - tick_raw = filtered[["Ticker", "Description"]].values.tolist() - tick = [f"{x.replace('=F', '')}: {y}" for x, y in tick_raw] - tick.sort() - st.session_state["widget_options"]["tickers_widget"] = tick - - def run(self): - chart_type = ["Historical Time Series", "Future Curve"] - - st.sidebar.multiselect( - "Category", - st_helpers.get_widget_options(self.default_opts, "cat_widget"), - on_change=self.on_change, - key="cat_widget", - ) - st.sidebar.multiselect( - "Exchange", - st_helpers.get_widget_options(self.default_opts, "exch_widget"), - key="exch_widget", - on_change=self.on_change, - ) - ticker_widget = st.sidebar.selectbox( - "Ticker", - st_helpers.get_widget_options(self.default_opts, "tickers_widget"), - key="tickers_widget", - index=0, - ) - - chart_widget = st.sidebar.selectbox( - "Chart Type", chart_type, key="chart_widget", index=0 - ) - contracts_widget = st.sidebar.slider( - "Contracts", 1, 24, 6, key="contracts_widget" - ) - - self.create_stock(chart_widget, contracts_widget, ticker_widget) - - -if __name__ == "__main__": - Chart().run() diff --git a/openbb_terminal/dashboards/stream/pages/Indicators.py b/openbb_terminal/dashboards/stream/pages/Indicators.py deleted file mode 100644 index e280bc170a3..00000000000 --- a/openbb_terminal/dashboards/stream/pages/Indicators.py +++ /dev/null @@ -1,519 +0,0 @@ -import asyncio -import re -from datetime import date, datetime, timedelta -from typing import List -from unittest.mock import patch - -import pandas as pd -import streamlit as st -import streamlit.components.v1 as components -from streamlit.delta_generator import DeltaGenerator - -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.dashboards.stream import ( - common_vars, - streamlit_helpers as st_helpers, -) -from openbb_terminal.dashboards.stream.streamlit_helpers import load_state -from openbb_terminal.rich_config import console - -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - - -pd.options.plotting.backend = "plotly" - -st.set_page_config( - layout="wide", - page_title="Indicators", - initial_sidebar_state="expanded", -) -st_helpers.set_current_page("Indicators") -st_helpers.set_css() - -logger = st.empty() -page_title = st.empty() -plotly_chart, table_container = st.columns([4, 1.5]) - -TA_CLASS = PlotlyTA() -indicators_opts = sorted( - [c.name.replace("plot_", "") for c in TA_CLASS if c.name != "plot_ma"] - + TA_CLASS.ma_mode -) - -source_opts = [ - "AlphaVantage", - "YahooFinance", - "EODHD", - "Polygon", - "Intrinio", - "DataBento", -] -MAIN_LOOP: asyncio.AbstractEventLoop = None # type: ignore - - -def special_st(text: str): - rich_color = re.search(r"\[([a-z ]+)\]", text) - text = re.sub(r"(\[\/{0,1}[a-z ]+\])|(\[\/\])", "", text) - - async def _special_st(): - with logger.container(): - if rich_color: - color = rich_color.group(1) - if "green" in color: - st.success(text, icon="🎉") - elif "red" in color: - st.error(text, icon="🔥") - await asyncio.sleep(5) - elif "yellow" in color: - st.warning(text, icon="⚠️") - else: - st.write(text) - await asyncio.sleep(4) - - MAIN_LOOP.create_task(_special_st()) - - -async def plot_indicators( - data: pd.DataFrame, tickers: List[str], indicators: List[str] -) -> OpenBBFigure: - main_ticker = tickers[0] - tickers = tickers[1:] - data = data.copy() - - indicators_dict: dict = {} - for indicator in indicators: - indicators_dict[indicator] = st.session_state["indicators_args"].get( - indicator, {} - ) - - title = ( - f"{main_ticker} Technical Analysis" - if not tickers - else f"{main_ticker} Technical Analysis with {', '.join(tickers)}" - ) - - fig = TA_CLASS.plot( - data, - indicators_dict, - main_ticker, - candles=not tickers, - volume=not tickers, - volume_ticks_x=7, - ) - - fig.update_traces(showlegend=False) - rows = fig.subplots_kwargs["rows"] - - for ticker in tickers: - df_ticker = st.session_state["indicators_dfs"][ticker] - df_ticker["% Change"] = df_ticker["Close"].apply( - lambda x, df_ticker=df_ticker: (x - df_ticker["Close"].iloc[0]) - / df_ticker["Close"].iloc[0] - ) - - fig.add_scatter( - x=df_ticker.index, - y=df_ticker["% Change"], - name=f"{ticker} % Change", - customdata=df_ticker["Close"], - secondary_y=True, - connectgaps=True, - hovertemplate="Close: %{customdata:.2f}
%{y:.2%}", - yaxis=f"y{rows}", - showlegend=True, - row=1, - col=1, - ) - - if tickers: - fig.update_traces( - selector=dict(type="scatter", name=f"{main_ticker} Close"), showlegend=True - ) - fig.update_yaxes( - selector=dict(title_text="Price ($)"), - title_text=f"{main_ticker} Price ($)", - row=1, - col=1, - secondary_y=False, - ) - fig.update_yaxes( - secondary_y=True, - showgrid=False, - showticklabels=True, - row=1, - col=1, - showline=False, - zeroline=False, - title_text="% Change", - side="left", - title_standoff=5, - tickformat=".2%", - overlaying="y", - ) - - for annotation in fig.select_annotations( - selector=lambda x: hasattr(x, "xshift") and x.xshift < 0 - ): - annotation.xshift += -5 - - min_max = {"min": data["Low"].min(), "max": data["High"].max()} - - for attr, val in min_max.items(): - if hasattr(val, attr): - min_max[attr] = getattr(val, attr)() - - y_min, y_max = min_max["min"], min_max["max"] - y_range = y_max - y_min - y_min -= y_range * 0.05 - y_max += y_range * 0.05 - - fig.update_layout( - yaxis=dict(range=[y_min, y_max], autorange=False), - title=dict(x=0.5, xanchor="center", yanchor="top", y=0.99, text=title), - showlegend=True, - height=550 + (20 * rows), - legend=dict( - bgcolor="rgba(0,0,0,0.5)", - bordercolor="#F5EFF3", - borderwidth=1, - x=0.01, - y=0.01, - xanchor="left", - yanchor="bottom", - ), - ) - - return fig - - -class Handler: - def __init__(self, loop_: asyncio.AbstractEventLoop): - default_args = { - "last_tickers": "", - "last_interval": "1d", - "last_source": "YahooFinance", - "interval": "1d", - "args": {indicator: {} for indicator in indicators_opts}, - "dfs": {}, - } - for key, value in default_args.items(): - load_state(f"indicators_{key}", value) - - default_opts = { - "start_date": date.today() - timedelta(days=365), - "end_date": date.today(), - "indicators_tickers": st.session_state["indicators_last_tickers"], - "indicators_interval": st.session_state["indicators_last_interval"], - "indicators": [], - "source": st.session_state["indicators_last_source"], - } - for key, value in default_opts.items(): - load_state(key, value) - - self.loop = loop_ - - global MAIN_LOOP # pylint: disable=W0603 # noqa: PLW0603 - MAIN_LOOP = loop_ - - # pylint: disable=R0913 - async def handle_changes( - self, - start: date, - end: date, - tickers: str, - indicators: list, - ): - tickers_l = tickers.split(",") - main_ticker = tickers_l[0].upper().strip() - if ( - not tickers_l - or st.session_state["indicators_dfs"].get(main_ticker, pd.DataFrame()).empty - ): - with logger.empty(): - st.error("Please select at least one valid ticker", icon="🔥") - return None - - start_n = datetime(start.year, start.month, start.day).date() - end_n = datetime(end.year, end.month, end.day).date() - - for ticker in tickers_l: - clean_ticker = ticker.upper().strip() - st.session_state["indicators_dfs"][clean_ticker] = st.session_state[ - "indicators_dfs" - ][clean_ticker].loc[ - (st.session_state["indicators_dfs"][clean_ticker].index.date >= start_n) - & (st.session_state["indicators_dfs"][clean_ticker].index.date <= end_n) - ] - - result: pd.DataFrame = st.session_state["indicators_dfs"][main_ticker] - with st.spinner("Calculating indicators..."), patch.object( - console, "print", special_st - ): - if result.empty: - with logger.container(): - logger.error("There was an error with the data", icon="🔥") - await asyncio.sleep(2) - return None - - data = pd.DataFrame(result) - data.index.name = "date" - - if ta_helpers.check_columns(data) is None: - return None - - fig = await plot_indicators(data, tickers_l, indicators) - - with plotly_chart.container(): - dt_now = datetime.now().strftime("%Y%m%d_%H%M%S") - filename = f"{dt_now}_{main_ticker}_technical_analysis" - fig.show(external=True, bar_width=0.00001) - - if list( - set(indicators).intersection( - set(TA_CLASS.ma_mode + TA_CLASS.inchart) - ) - ): - margin = fig.layout.margin - margin.l += 30 - fig.update_layout(margin=margin) - - st.plotly_chart( - fig, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - toImageButtonOptions=dict( - format="png", - filename=filename, - ), - **common_vars.PLOTLY_CONFIG, - ), - ) - components.html(common_vars.PLOTLY_MODEBAR) - - with table_container: - last_day = data.loc[data.index.date == data.index.date[-1]] - - if not last_day.empty: - last_day.index = pd.to_datetime(last_day.index).date - weeks52 = data.loc[ - data.index.date >= data.index.date[-1] - timedelta(weeks=52) - ] - stats_df = pd.DataFrame( - { - "Open": last_day["Open"].head(1).values[0], - "High": last_day["High"].max(), - "Low": last_day["Low"].min(), - "Close": last_day["Close"].tail(1).values[0], - "Volume": last_day["Volume"].sum(), - "52w high": weeks52["High"].values.max(), - "52w low": weeks52["Low"].values.min(), - }, - index=[last_day.tail(1).index.values[0].strftime("%Y-%m-%d")], - ) - stats_df["Volume"] = stats_df["Volume"].apply(lambda x: f"{x:,.0f}") - st.table( - stats_df.transpose().style.format(precision=2, thousands=",") - ) - - async def load_ticker_data( - self, ticker: str, interval: str, start: date, end: date, source: str - ): - kwargs = {} - if interval == "1d": - kwargs.update(dict(start_date=start, end_date=end)) - else: - end += timedelta(days=1) - kwargs.update(dict(interval=int(interval.replace("m", "")))) # type: ignore - - with patch.object(console, "print", special_st): - df = common_vars.openbb.stocks.load(ticker, **kwargs, source=source) - - if df.empty: - with logger.container(): - st.error( - f"Could not load data for {ticker}. Is it a valid ticker?", - icon="❗", - ) - await asyncio.sleep(2) - return pd.DataFrame() - - df = df.dropna() - return df - - def on_ticker_change(self): - self.loop.run_until_complete(self.async_on_ticker_change()) - - async def async_on_ticker_change(self): - """Load ticker data when ticker changes""" - new_data = False - tickers = list(set(st.session_state.indicators_ticker.split(","))) # type: ignore - tickers = [ticker.strip().upper() for ticker in tickers if ticker.strip()] # type: ignore - - try: - interval = st.session_state.indicators_interval - source = st.session_state.indicators_source - start = st.session_state.start_date - end = st.session_state.end_date - except AttributeError: - interval = "1d" - source = "YahooFinance" - start = date.today() - timedelta(days=365) - end = date.today() - - check_last = [ - ("indicators_last_tickers", ",".join(tickers)), - ("indicators_last_interval", interval), - ("indicators_last_source", source), - ] - new_params = any( - st.session_state.get(key, None) != value for key, value in check_last - ) - if new_params: - st.session_state["indicators_dfs"] = {} - for key, value in check_last: - st.session_state[key] = value - new_data = True - - for ticker in tickers: - if ticker not in st.session_state["indicators_dfs"]: - st.session_state["indicators_dfs"][ticker] = ( - await self.load_ticker_data(ticker, interval, start, end, source) - ) - if st.session_state["indicators_dfs"][ticker].empty: - indicators_dfs = st.session_state["indicators_dfs"] - del indicators_dfs[ticker] - st.session_state["indicators_dfs"] = indicators_dfs - tickers.remove(ticker) - else: - with logger.container(): - st.success(f"Loaded data for {ticker}", icon="📈") - await asyncio.sleep(0.5) - - if new_data: - st.session_state["indicators_ticker"] = ",".join(tickers) - - def handle_indicators(self, options_col: DeltaGenerator): - """Handle the indicators selection""" - indicators = [ - indicator - for indicator in list( - set(st.session_state.indicators).intersection(set(TA_CLASS.ma_mode)) - ) - if not st.session_state["indicators_args"][indicator].get("length", []) - ] - - for indicator in st.session_state["indicators_args"]: - if indicator not in indicators: - st.session_state["indicators_args"][indicator] = {} - - if indicators: - with options_col.container().form("indicators_form", clear_on_submit=True): - st.write("Select window lengths:") - for indicator in indicators: - st.multiselect( - f"{indicator.upper()} Lengths", - options=[7, 10, 20, 50, 100, 200], - key=f"{indicator}_length", - ) - st.form_submit_button( - "Submit", - on_click=self.on_length_submit, - args=(indicators,), - ) - - def on_length_submit(self, indicators: list): - """Handle the length submit""" - for indicator in indicators: - st.session_state["indicators_args"][indicator] = { - "length": st.session_state[f"{indicator}_length"] - } - - async def on_update(self): - """Handle the update button""" - st.session_state["indicators_dfs"] = {} - await self.async_on_ticker_change() - - async def run(self): - """Run the app""" - - st.sidebar.markdown( - "

OpenBB Technical Analysis

", - unsafe_allow_html=True, - ) - ticker = st.sidebar.text_input( - "Ticker(s) (comma separated)", - "", - key="indicators_ticker", - on_change=self.on_ticker_change, - ) - data_opts = st.sidebar.expander("Data options") - length_opts = st.sidebar.container() - indicators_sideopts = st.sidebar.container() - - with data_opts.container(): - data_opts.selectbox( - "Source", - index=1, - key="indicators_source", - options=source_opts, - on_change=self.on_ticker_change, - ) - start_date = data_opts.date_input( - "Start date", date.today() - timedelta(days=365), key="start_date" - ) - end_date = data_opts.date_input("End date", date.today(), key="end_date") - - data_opts.selectbox( - "Interval", - index=3, - key="indicators_interval", - options=common_vars.INTERVAL_OPTS, - on_change=self.on_ticker_change, - ) - with indicators_sideopts: - indicators = indicators_sideopts.multiselect( - "Indicators", - key="indicators", - options=indicators_opts, - on_change=self.handle_indicators, - kwargs=dict(options_col=length_opts), - ) - - st.sidebar.markdown("""---""") - plot_data, reset_data = st.sidebar.columns([1, 1]) - if plot_data.button("Plot", use_container_width=True): - if ticker: - await self.handle_changes( - start_date, - end_date, - ticker, - indicators, - ) - else: - with logger.container(): - logger.error("Please enter a ticker", icon="❗") - if reset_data.button("Update", use_container_width=True): - await self.on_update() - if ticker: - await self.handle_changes( - start_date, - end_date, - ticker, - indicators, - ) - - -if __name__ == "__main__": - try: - loop = asyncio.get_running_loop() - except RuntimeError: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - - loop.run_until_complete(Handler(loop).run()) diff --git a/openbb_terminal/dashboards/stream/pages/Options.py b/openbb_terminal/dashboards/stream/pages/Options.py deleted file mode 100644 index ec616c4f5a1..00000000000 --- a/openbb_terminal/dashboards/stream/pages/Options.py +++ /dev/null @@ -1,959 +0,0 @@ -# type: ignore -# pylint: disable=[W0621,R1714] -from typing import Any, Tuple - -import pandas as pd -import streamlit as st - -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.dashboards.stream import streamlit_helpers as st_helpers -from openbb_terminal.stocks.options.cboe_model import INDEXES, SYMBOLS -from openbb_terminal.stocks.options.options_chains_model import ( - get_nearest_call_strike, - get_nearest_otm_strike, - get_nearest_put_strike, -) -from openbb_terminal.stocks.options.options_sdk_helper import ( - OptionsChains, - load_options_chains, -) - -EXCEPTIONS = ["NDX", "RUT"] -analysis_type: str = "" -strike_price: float = 0 -moneyness: float = 0 -strangle_moneyness: float = 0 -price_data = pd.DataFrame() -strategy_data = pd.DataFrame() -title: str = "" -_strikes: Any = None -chart_data_type: str = "" -target_column: dict[str, str] = { - "Last Price": "lastTradePrice", - "Bid Price": "bid", - "Ask Price": "ask", - "Theoretical Price": "theoretical", - "Breakeven Price": "Breakeven", - "Open Interest": "openInterest", - "Volume": "volume", - "Implied Volatility": "impliedVolatility", - "Gamma": "gamma", - "Theta": "theta", - "Vega": "vega", - "Delta Dollars": "DEX", - "GEX Per 1% Move": "GEX", -} -strategy_target_column_dict = { - "Cost of Position": "Cost", - "Cost as % of Underlying": "Cost Percent", -} -surface_choice_dict = { - "OTM Only": "otm", - "ITM Only": "itm", - "Calls Only": "calls", - "Puts Only": "puts", -} -chains_df = pd.DataFrame() -symbol_choices = SYMBOLS.sort_index().reset_index() -default_symbol = int(symbol_choices.query("`Symbol` == 'SPY'").index[0]) -chains_strikes: dict[str, float] = {} - -pd.options.plotting.backend = "plotly" -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - -st.set_page_config( - layout="wide", - page_title="CBOE Options Dashboard", - initial_sidebar_state="expanded", - menu_items={ - "Get help": "https://discord.com/invite/Y4HDyB6Ypu", - }, -) - -st_helpers.set_current_page("Options") -st_helpers.set_css() - - -def format_plotly(fig: OpenBBFigure): - fig.update_layout( - margin=dict(t=40), - autosize=False, - width=1050, - height=700, - title=dict(y=0.99, x=0.5, xanchor="center", yanchor="top", automargin=True), - legend=dict(bgcolor="rgba(0,0,0,0)"), - ) - return fig - - -@st.cache_data(show_spinner=False, experimental_allow_widgets=True) -def load_data(ticker): - df = load_options_chains(ticker) - - _calls = df.chains[df.chains["optionType"] == "call"].copy() - _puts = df.chains[df.chains["optionType"] == "put"].copy() - - _calls.loc[:, ("$ To Spot")] = ( - (_calls.loc[:, ("strike")]) + (_calls.loc[:, ("ask")]) - (df.last_price) - ) - _calls.loc[:, ("% To Spot")] = (_calls.loc[:, ("$ To Spot")] / df.last_price) * 100 - _calls.loc[:, ("Breakeven")] = _calls.loc[:, ("strike")] + _calls.loc[:, ("ask")] - _calls.loc[:, ("DEX")] = ( - (_calls.loc[:, ("delta")] * 100) - * (_calls.loc[:, ("openInterest")]) - * df.last_price - ) - _calls["DEX"] = _calls["DEX"].convert_dtypes(convert_floating=True) - _calls.loc[:, ("GEX")] = ( - _calls.loc[:, ("gamma")] - * 100 - * _calls.loc[:, ("openInterest")] - * (df.last_price * df.last_price) - * 0.01 - ) - _calls.GEX = _calls.GEX.convert_dtypes(convert_floating=True) - _calls.set_index(keys=["expiration", "strike", "optionType"], inplace=True) - - _puts.loc[:, ("$ To Spot")] = ( - (_puts.loc[:, ("strike")]) - (_puts.loc[:, ("ask")]) - (df.last_price) - ) - _puts.loc[:, ("% To Spot")] = (_puts.loc[:, ("$ To Spot")] / df.last_price) * 100 - _puts.loc[:, ("Breakeven")] = _puts.loc[:, ("strike")] - _puts.loc[:, ("ask")] - _puts.loc[:, ("DEX")] = ( - (_puts.loc[:, ("delta")] * 100) - * (_puts.loc[:, ("openInterest")]) - * df.last_price - ) - _puts.loc[:, ("GEX")] = ( - _puts.loc[:, ("gamma")] - * 100 - * _puts.loc[:, ("openInterest")] - * (df.last_price * df.last_price) - * 0.01 - * (-1) - ) - _puts.GEX = _puts.GEX.convert_dtypes(convert_floating=True) - _puts["DEX"] = _puts["DEX"].convert_dtypes(convert_floating=True) - _puts.set_index(keys=["expiration", "strike", "optionType"], inplace=True) - - _calls.GEX = round(_calls.GEX, ndigits=2) - _calls["DEX"] = round(_calls["DEX"], ndigits=2) - _puts.GEX = round(_puts.GEX, ndigits=2) - _puts["DEX"] = round(_puts["DEX"], ndigits=2) - - df.chains = pd.concat([_puts, _calls]) - df.chains.sort_index(inplace=True) - df.chains.reset_index(inplace=True) - - return df - - -def get_exposure(df: OptionsChains) -> Tuple[pd.DataFrame, pd.DataFrame]: - by_strike = pd.DataFrame() - by_expiration = pd.DataFrame() - puts_by_strike = ( - df.chains.query("`optionType` == 'put'").groupby("strike")[["DEX", "GEX"]].sum() - ) - puts_by_strike.columns = ["Put DEX", "Put GEX"] - calls_by_strike = ( - df.chains.query("`optionType` == 'call'") - .groupby("strike")[["DEX", "GEX"]] - .sum() - ) - calls_by_strike.columns = ["Call DEX", "Call GEX"] - by_strike = pd.concat([calls_by_strike, puts_by_strike], axis=1) - puts_by_expiration = ( - df.chains.query("`optionType` == 'put'") - .groupby("expiration")[["DEX", "GEX"]] - .sum() - ) - puts_by_expiration.columns = ["Put DEX", "Put GEX"] - calls_by_expiration = ( - df.chains.query("`optionType` == 'call'") - .groupby("expiration")[["DEX", "GEX"]] - .sum() - ) - calls_by_expiration.columns = ["Call DEX", "Call GEX"] - by_expiration = pd.concat([calls_by_expiration, puts_by_expiration], axis=1) - return by_strike.astype("int64"), by_expiration.astype("int64") - - -def get_highest_oi(df: OptionsChains) -> pd.DataFrame: - highest_oi = df.chains[df.chains["openInterest"] == max(df.chains["openInterest"])][ - [ - "optionType", - "strike", - "dte", - "openInterest", - "volume", - "impliedVolatility", - "bid", - "ask", - "Breakeven", - ] - ] - highest_oi.columns = [ - "Type", - "Strike", - "DTE", - "OI", - "Volume", - "IV", - "Bid", - "Ask", - "Breakeven", - ] - return highest_oi - - -def get_highest_volume(df: OptionsChains) -> pd.DataFrame: - highest_volume = df.chains[df.chains["volume"] == max(df.chains["volume"])][ - [ - "optionType", - "strike", - "dte", - "openInterest", - "volume", - "impliedVolatility", - "bid", - "ask", - "Breakeven", - ] - ] - highest_volume.columns = [ - "Type", - "Strike", - "DTE", - "OI", - "Volume", - "IV", - "Bid", - "Ask", - "Breakeven", - ] - return highest_volume - - -with st.sidebar: - ticker = st.selectbox( - label="Ticker", - options=symbol_choices["Symbol"].tolist(), - index=default_symbol, - key="ticker", - ) - expiration_choice = None - df = load_data(ticker) - chains_df = df.chains - if ticker == "": - st.write("Please enter a symbol") - if hasattr(df, "underlying_name"): - ticker_good = True - stats = df.get_stats() - net_calls = stats.sum()["Calls OI"].astype(int) - net_puts = stats.sum()["Puts OI"].astype(int) - pcr_ratio = round((net_puts / net_calls), 4) - net_calls_vol = stats.sum()["Calls Volume"].astype(int) - net_puts_vol = stats.sum()["Puts Volume"].astype(int) - straddle_30d = df.get_straddle() - st.write(df.underlying_name) - st.write("Last Price: ", f"${df.last_price:0,.2f}") - _change = round(df.underlying_price["changePercent"], 2) - st.write("Change: ", f"{_change:0,.2f}%") - st.write( - f"Cost of {straddle_30d.loc['DTE']['Long Straddle']} DTE Straddle: ", - f"${round(straddle_30d.loc['Cost']['Long Straddle'], 2)}", - ) - if df.underlying_price["ivThirty"] != 0: - st.write("IV 30: ", str(df.underlying_price["ivThirty"])) - if ticker.upper() not in INDEXES and ticker.upper() not in EXCEPTIONS: - st.write("Stock Volume :", format(df.underlying_price["volume"], ",")) - st.write("Call Volume: ", format(net_calls_vol, ",")) - st.write("Put Volume: ", format(net_puts_vol, ",")) - st.write("Put/Call Ratio: ", str(pcr_ratio)) - analysis_type = st.selectbox( - "Analysis Type", options=["Charts", "Tables"], key="analysis_type" - ) - if analysis_type == "Tables": - table_choices = ["Chains", "Stats"] - table_choice = st.selectbox("Table Type", options=table_choices) - if table_choice == "Chains": - chains_selections = ["% OTM", "Delta", "None"] - chains_select = st.selectbox( - "Limit Range of Results", - options=chains_selections, - key="chains_select", - ) - if chains_select == "% OTM": - chains_moneyness: float = st.number_input( - label="% OTM Range", - min_value=0.01, - max_value=99.99, - value=2.0, - step=0.25, - ) - chains_strikes = get_nearest_otm_strike(df, chains_moneyness) - if chains_select == "Delta": - delta_choice: float = st.number_input( - label="Delta Value", - min_value=0.01, - max_value=1.0, - value=0.5, - step=0.05, - ) - chains_choice = st.selectbox( - "Chain Type", options=["Calls", "Puts", "Both"], key="chains_choice" - ) - expiration_choices = ["All"] - expiration_choices.extend(df.expirations) - expiration_choice = st.selectbox( - "Expiration Date", - options=expiration_choices, - key="expiration_choice", - ) - if analysis_type == "Charts": - stats_type: str = "" - chart_data_type = st.selectbox( - "Chart Data Type", - options=["Stats", "Strikes", "Strategies", "Volatility"], - key="chart_data_type", - ) - expiry = "All" - if chart_data_type == "Stats": - stats_type = st.selectbox( - "Stat Type", - options=["Open Interest", "Volume", "Ratios"], - key="stats_type", - ) - if stats_type in ("Open Interest", "Volume"): - is_percent = st.checkbox( - "Percent of Total", value=False, key="is_percent" - ) - if is_percent is False and stats_type == "Open Interest": - expiries = ["All"] - expiries.extend(df.expirations) - expiry = st.selectbox("Expiry", options=expiries, key="expiry") - if chart_data_type == "Strikes": - strike_choice = st.selectbox( - "Strike Selection Type", - options=(["Single Strike", "Separate Calls and Puts", "% OTM"]), - key="strike_choice", - ) - if strike_choice == "Separate Calls and Puts": - _default_call = get_nearest_call_strike( - df, strike_price=df.last_price - ) - _default_put = get_nearest_put_strike( - df, strike_price=df.last_price - ) - _default_strikes = pd.Series(df.strikes) - default_call = int( - _default_strikes[_default_strikes == _default_call].index[0] - ) - default_put = int( - _default_strikes[_default_strikes == _default_put].index[0] - ) - col1, col2 = st.columns(2) - with col1: - _call = st.selectbox( - "Call Strike", - options=df.strikes, - index=default_call, - key="call_strike", - ) - with col2: - _put = st.selectbox( - "Put Strike", - options=df.strikes, - index=default_put, - key="put_strike", - ) - _strikes = {"call": _call, "put": _put} - if strike_choice == "% OTM": - moneyness: float = st.number_input( - label="% OTM Moneyness", - min_value=0.01, - max_value=99.99, - value=2.0, - step=0.25, - key="moneyness", - ) - _strikes = get_nearest_otm_strike(df, moneyness) - if strike_choice == "Single Strike": - default_strike_price = get_nearest_call_strike( - df, strike_price=df.last_price - ) - _strikes = pd.Series(df.strikes) - default_strike = int( - _strikes[_strikes == default_strike_price].index[0] - ) - strike_price = st.selectbox( - "Strike Price", - options=df.strikes, - index=default_strike, - key="strike_price", - ) - strike_data_point = st.selectbox( - "Data Point", - options=list(target_column.keys()), - key="strike_data_point", - ) - - if chart_data_type == "Strategies": - _strikes = pd.Series(df.strikes) - strategy_choice = st.selectbox( - "Strategy Type", - options=( - [ - "Straddle", - "Strangle", - "Synthetic Long", - "Synthetic Short", - "Vertical Call", - "Vertical Put", - ] - ), - key="strategy_choice", - ) - if strategy_choice == "Strangle": - strangle_moneyness: float = st.number_input( - label="% OTM Moneyness", - min_value=0.01, - max_value=99.99, - value=2.0, - step=0.25, - key="strangle_moneyness", - ) - if strategy_choice == "Straddle": - default_strike_price = get_nearest_call_strike( - df, strike_price=df.last_price - ) - default_strike = int( - _strikes[_strikes == default_strike_price].index[0] - ) - straddle_strike: float = st.selectbox( - "Strike Price", - options=df.strikes, - index=default_strike, - key="straddle_strike", - ) - if strategy_choice in ("Synthetic Long", "Synthetic Short"): - default_strike_price = get_nearest_call_strike( - df, strike_price=df.last_price - ) - default_strike = int( - _strikes[_strikes == default_strike_price].index[0] - ) - synthetic_strike = st.selectbox( - "Strike Price", - options=df.strikes, - index=default_strike, - key="synthetic_strike", - ) - if strategy_choice in ("Vertical Call", "Vertical Put"): - default_strike_price1 = get_nearest_call_strike( - df, - strike_price=( - df.last_price + 5 - if strategy_choice == "Vertical Call" - else df.last_price - 5 - ), - ) - default_strike_price2 = get_nearest_call_strike( - df, strike_price=df.last_price - ) - default_strike1 = int( - _strikes[_strikes == default_strike_price1].index[0] - ) - default_strike2 = int( - _strikes[_strikes == default_strike_price2].index[0] - ) - col1, col2 = st.columns(2) - with col1: - strike1 = st.selectbox( - "Sold Strike Price", - options=df.strikes, - index=default_strike1, - key="strike1", - ) - with col2: - strike2 = st.selectbox( - "Bought Strike Price", - options=df.strikes, - index=default_strike2, - key="strike2", - ) - - strategy_target_column = st.selectbox( - "Data Type", - options=list(strategy_target_column_dict.keys()), - key="strategy_target_column", - ) - - if chart_data_type == "Volatility": - vol_choices = ["Smile", "Skew", "Surface"] - vol_choice = st.selectbox( - "Volatility Type", options=vol_choices, index=0, key="vol_choice" - ) - if vol_choice in ("Smile", "Skew"): - if vol_choice == "Smile": - col1, col2 = st.columns(2) - with col1: - with_volume = st.checkbox( - "With Volume", value=False, key="with_volume" - ) - with col2: - with_oi = st.checkbox("With OI", value=False, key="with_oi") - if vol_choice == "Skew": - otm_only = st.checkbox("OTM Only", value=False, key="otm_only") - with st.expander("Select Expiration Dates"): - expiration_choice = st.multiselect( - "Expiration Date", - options=df.expirations, - default=df.expirations[2], - key="expiration_choice", - ) - if vol_choice == "Surface": - surface_choices = [ - "Calls Only", - "Puts Only", - "OTM Only", - "ITM Only", - ] - surface_choice = st.selectbox( - "Surface Type", - options=surface_choices, - index=2, - key="surface_choice", - ) - else: - st.write("Please enter a valid symbol.") - ticker_good = False - - -if analysis_type == "Tables" and table_choice == "Chains" and ticker_good is True: - if chains_select == "% OTM": - upper = chains_strikes["call"] - lower = chains_strikes["put"] - chains_df = df.chains.query("@lower <= `strike` <= @upper") - if chains_select == "Delta": - upper_call = delta_choice + 0.01 - lower_call = delta_choice - 0.01 - upper_put = (delta_choice + 0.01) * (-1) - lower_put = (delta_choice - 0.01) * (-1) - chains_df = df.chains.query( - "@lower_call <= `delta` <= @upper_call or @lower_put >= `delta` >= @upper_put" - ) - if chains_select == "None": - chains_df = df.chains - if expiration_choice != "All": - chains_df = chains_df[chains_df["expiration"] == expiration_choice] - if expiration_choice == "All": - chains_df = chains_df[chains_df["dte"] >= 0] - chains_df = chains_df[ - [ - "strike", - "dte", - "optionType", - "theoretical", - "bidSize", - "bid", - "ask", - "askSize", - "lastTradePrice", - "openInterest", - "volume", - "impliedVolatility", - "delta", - "gamma", - "theta", - "rho", - "vega", - "open", - "high", - "low", - "previousClose", - "DEX", - "GEX", - "Breakeven", - "$ To Spot", - "% To Spot", - ] - ] - chains_df = chains_df.rename( - columns={ - "strike": "Strike", - "dte": "DTE", - "optionType": "Type", - "theoretical": "Theoretical", - "bidSize": "Bid Size", - "bid": "Bid", - "ask": "Ask", - "askSize": "Ask Size", - "lastTradePrice": "Last", - "openInterest": "OI", - "volume": "Volume", - "impliedVolatility": "IV", - "delta": "Delta", - "gamma": "Gamma", - "theta": "Theta", - "rho": "Rho", - "vega": "Vega", - "open": "Open", - "high": "High", - "low": "Low", - "previousClose": "Prev Close", - } - ) - chains_df.Theoretical = round(chains_df.Theoretical, 2) - chains_df = chains_df.set_index("Strike") - chains_df = chains_df[chains_df["Last"] > 0] - if chains_choice == "Calls": - chains_df = chains_df[chains_df["Type"] == "call"] - if chains_choice == "Puts": - chains_df = chains_df[chains_df["Type"] == "put"] - st.dataframe(chains_df, use_container_width=True, height=700) - -if analysis_type == "Tables" and table_choice == "Stats" and ticker_good is True: - exposure_by_strike, exposure_by_expiration = get_exposure(df) - stats_df_expiration = df.get_stats() - stats_df_strike = df.get_stats("strike") - sc1, sc2, sc3, sc4, sc5 = st.columns(5) - with sc1: - st.write("Total OI: ", format(stats_df_expiration["Total OI"].sum(), ",")) - with sc2: - st.write("Call OI: ", format(stats_df_expiration["Calls OI"].sum(), ",")) - with sc3: - st.write("Put OI: ", format(stats_df_expiration["Puts OI"].sum(), ",")) - with sc4: - st.write("Call DEX: $", format(exposure_by_expiration["Call DEX"].sum(), ",")) - with sc5: - st.write( - "Put DEX: $", format(abs(exposure_by_expiration["Put DEX"]).sum(), ",") - ) - - with st.expander("Stats by Expiration", expanded=True): - st.dataframe(stats_df_expiration, use_container_width=True) - with st.expander("Stats by Strike", expanded=False): - st.dataframe(stats_df_strike, use_container_width=True) - with st.expander("Delta Dollars and GEX per 1% Move", expanded=False): - col1, col2 = st.columns(2) - with col1: - st.dataframe(exposure_by_expiration.abs(), use_container_width=True) - with col2: - st.dataframe(exposure_by_strike.abs(), use_container_width=True) - -if analysis_type == "Charts" and ticker_good is True and chart_data_type == "Stats": - if stats_type == "Open Interest" and expiry != "All": - fig_oi_exp = df.chart_stats( - "strike", expiry=expiry, external_axes=True, oi=True, percent=False - ) - fig_oi_exp = format_plotly(fig_oi_exp) - st.plotly_chart( - fig_oi_exp, - use_container_width=True, - ) - - elif stats_type in ("Open Interest", "Volume"): - tab1, tab2 = st.tabs(["By Expiration", "By Strike"]) - with tab1: - if stats_type == "Open Interest": - fig_oi_exp = df.chart_stats( - percent=bool(is_percent), oi=True, external_axes=True - ) - fig_oi_exp = format_plotly(fig_oi_exp) - st.plotly_chart(fig_oi_exp, use_container_width=True) - if stats_type == "Volume": - fig_vol_exp = df.chart_stats( - percent=bool(is_percent), oi=False, external_axes=True - ) - fig_vol_exp = format_plotly(fig_vol_exp) - st.plotly_chart(fig_vol_exp, use_container_width=True) - with tab2: - if stats_type == "Open Interest": - fig_oi_strike = df.chart_stats( - "strike", percent=bool(is_percent), oi=True, external_axes=True - ) - fig_oi_strike = format_plotly(fig_oi_strike) - st.plotly_chart(fig_oi_strike, use_container_width=True) - if stats_type == "Volume": - fig_vol_exp = df.chart_stats( - "strike", percent=bool(is_percent), oi=False, external_axes=True - ) - fig_vol_exp = format_plotly(fig_vol_exp) - st.plotly_chart(fig_vol_exp, use_container_width=True) - - elif stats_type == "Ratios": - ratios_fig = df.chart_stats(ratios=True, external_axes=True) - ratios_fig.update_layout( - legend=dict( - orientation="h", - yanchor="top", - y=1, - xanchor="left", - x=0.01, - ) - ) - ratios_fig = format_plotly(ratios_fig) - st.plotly_chart( - ratios_fig, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - ), - ) - -if analysis_type == "Charts" and chart_data_type == "Strikes" and ticker_good is True: - title = f"{strike_data_point} of {df.symbol} Options @ ${strike_price} Strike Price" - title = ( - f"{strike_data_point} of {df.symbol} Options @ {moneyness}% OTM" - if strike_choice == "% OTM" - else title - ) - title = ( - f"{strike_data_point} of {df.symbol} Options" - if strike_choice == "Separate Calls and Puts" - else title - ) - title = ( - f"Gamma Exposure per 1% Move of {df.symbol}" - if strike_data_point == "GEX Per 1% Move" - else title - ) - if strike_choice == "Single Strike": - _price_data = df.chains[df.chains["strike"] == strike_price] - call_price_data = _price_data[_price_data["optionType"] == "call"] - put_price_data = _price_data[_price_data["optionType"] == "put"] - call_price_data = call_price_data.drop_duplicates("expiration").set_index( - "expiration" - ) - put_price_data = put_price_data.drop_duplicates("expiration").set_index( - "expiration" - ) - price_data["Call"] = call_price_data[target_column[strike_data_point]] - price_data["Put"] = put_price_data[target_column[strike_data_point]] - if strike_choice in ("Separate Calls and Puts", "% OTM"): - call_price_data = ( - df.chains[df.chains["strike"] == _strikes["call"]] - .query("`optionType` == 'call'") - .drop_duplicates("expiration") - .set_index("expiration") - ) - put_price_data = ( - df.chains[df.chains["strike"] == _strikes["put"]] - .query("`optionType` == 'put'") - .drop_duplicates("expiration") - .set_index("expiration") - ) - price_data["Call"] = call_price_data[target_column[strike_data_point]] - price_data["Put"] = put_price_data[target_column[strike_data_point]] - fig_price_data = OpenBBFigure() - fig_price_data.add_scatter( - x=price_data.index, - y=price_data["Call"], - mode="lines+markers", - name=( - "Calls" - if strike_choice == "Single Strike" - else f"Calls @ ${_strikes['call']}" - ), - marker=dict(color="blue"), - ) - fig_price_data.add_scatter( - x=price_data.index, - y=price_data["Put"], - mode="lines+markers", - name=( - "Puts" if strike_choice == "Single Strike" else f"Puts @ {_strikes['put']}" - ), - marker=dict(color="red"), - ) - fig_price_data.update_xaxes(type="category") - fig_price_data.update_layout( - title=dict(text=title, x=0.5, y=0.97), - legend=dict( - orientation="h", - yanchor="top", - y=1.125, - xanchor="left", - x=0.01, - ), - ) - fig_price_data = format_plotly(fig_price_data) - st.plotly_chart( - fig_price_data, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - ), - ) - -if ( - analysis_type == "Charts" - and chart_data_type == "Strategies" - and ticker_good is True -): - if strategy_choice == "Strangle": - title = f"Cost of {df.symbol} Strangle @ {strangle_moneyness}% OTM" - strategy_data = df.get_strategies(strangle_moneyness=strangle_moneyness) - - if strategy_choice == "Straddle": - title = f"Cost of {df.symbol} Straddle @ ${straddle_strike} Strike" - strategy_data = df.get_strategies(straddle_strike=straddle_strike) - - if strategy_choice == "Synthetic Long": - title = f"Cost of {df.symbol} Synthetic Long @ ${synthetic_strike} Strike" - strategy_data = df.get_strategies(synthetic_longs=synthetic_strike) - - if strategy_choice == "Synthetic Short": - title = f"Cost of {df.symbol} Synthetic Short @ ${synthetic_strike} Strike" - strategy_data = df.get_strategies(synthetic_shorts=synthetic_strike) - - if strategy_choice == "Vertical Call": - strategy_data = df.get_strategies(vertical_calls=[strike1, strike2]) - strategy_name = strategy_data.iloc[0]["Strategy"] - title = f"Cost of {df.symbol} {strategy_name} @ ${strike1} & ${strike2} Strikes" - - if strategy_choice == "Vertical Put": - strategy_data = df.get_strategies(vertical_puts=[strike1, strike2]) - strategy_name = strategy_data.iloc[0]["Strategy"] - title = f"Cost of {df.symbol} {strategy_name} @ ${strike1} & ${strike2} Strikes" - - title = ( - title + " As % of Underlying Price" - if strategy_target_column == "Cost as % of Underlying" - else title - ) - - fig_strategy = OpenBBFigure() - fig_strategy.add_scatter( - x=strategy_data.Expiration, - y=strategy_data[strategy_target_column_dict[strategy_target_column]], - mode="lines+markers", - name="Cost of Long Strangle", - marker=dict(color="red"), - ) - fig_strategy.update_xaxes(type="category") - fig_strategy.update_layout(title=dict(text=title, x=0.5, y=0.97)) - fig_strategy = format_plotly(fig_strategy) - st.plotly_chart( - fig_strategy, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - ), - ) - -if ( - analysis_type == "Charts" - and chart_data_type == "Volatility" - and ticker_good is True -): - if vol_choice == "Smile": - if len(expiration_choice) == 0: - expiration_choice = df.expirations[2] - if len(expiration_choice) > 5: - expiration_choice = expiration_choice[0:5] - smile_fig = df.chart_volatility( - expirations=expiration_choice, - oi=with_oi, - volume=with_volume, - external_axes=True, - ) - smile_fig = format_plotly(smile_fig) - smile_fig.update_layout( - legend=dict( - orientation="h", yanchor="top", y=1.025, xanchor="center", x=0.5 - ) - ) - st.plotly_chart( - smile_fig, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - ), - ) - if vol_choice == "Skew": - if len(expiration_choice) == 0: - expiration_choice = df.expirations[2] - if otm_only is False and len(expiration_choice) > 5: - expiration_choice = expiration_choice[0:5] - if otm_only is True and len(expiration_choice) > 10: - expiration_choice = expiration_choice[0:10] - skew_fig = df.chart_skew( - expirations=expiration_choice, otm_only=otm_only, external_axes=True - ) - skew_fig = format_plotly(skew_fig) - skew_fig.update_layout( - legend=dict( - orientation="h", yanchor="top", y=1.025, xanchor="center", x=0.5 - ) - ) - st.plotly_chart( - skew_fig, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - ), - ) - if vol_choice == "Surface": - dte_ranges = df.chains.dte.unique().tolist() - col1, col2, col3, col4 = st.columns(4) - with col1: - near_dte_limit = int( - st.select_slider( - "Near DTE Boundary", - options=dte_ranges, - value=dte_ranges[0], - key="near_dte_limit", - ) - ) - with col2: - far_dte_limit = int( - st.select_slider( - "Far DTE Boundary", - options=dte_ranges, - value=dte_ranges[-1], - key="far_dte_limit", - ) - ) - near = near_dte_limit if near_dte_limit < far_dte_limit else far_dte_limit - far = far_dte_limit if near_dte_limit < far_dte_limit else near_dte_limit - if near == far: - far += 1 - dte_range = [far, near] - with col3: - lower_strike = float( - st.select_slider( - "Lower Strike", - options=df.strikes, - value=df.strikes[0], - key="lower_strike", - ) - ) - with col4: - upper_strike = float( - st.select_slider( - "Upper Strike", - options=df.strikes, - value=df.strikes[-1], - key="upper_strike", - ) - ) - lower = lower_strike if lower_strike < upper_strike else upper_strike - upper = upper_strike if lower_strike < upper_strike else lower_strike - strike_range = [lower, upper] - surface_fig = df.chart_surface( - surface_choice_dict[surface_choice], - dte_range, - strike_range, - external_axes=True, - ) - surface_fig = format_plotly(surface_fig) - st.plotly_chart(surface_fig, use_container_width=True) diff --git a/openbb_terminal/dashboards/stream/pages/Short_Data.py b/openbb_terminal/dashboards/stream/pages/Short_Data.py deleted file mode 100644 index 215b7e3168e..00000000000 --- a/openbb_terminal/dashboards/stream/pages/Short_Data.py +++ /dev/null @@ -1,214 +0,0 @@ -import asyncio -import io -from datetime import datetime, timedelta - -import pandas as pd -import requests -import streamlit as st - -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure, theme -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.dashboards.stream import streamlit_helpers as st_helpers - -pd.options.plotting.backend = "plotly" -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - -st.set_page_config( - layout="wide", - page_title="Short Data", - initial_sidebar_state="expanded", -) -st.sidebar.markdown( - "

Finra Short Data

", - unsafe_allow_html=True, -) -TICKER = st.sidebar.container() -DAYS = st.sidebar.container() -COUNT = st.sidebar.container() - -load_button, ticker_button, show_button = st.sidebar.columns([1, 1, 1]) -output1 = st.empty() -output2 = st.empty() -title_html = """ -

Load Data:
-This widget downloads the consolidated NMS short data from FINRA and aggregates -the data by summing over the entire time period.

-

Note that clicking the this button will reload all data. -This can get time consuming, so if you pick a few hundred days, -expect a few minutes for loading time.

-""" -middle_html = """ - -Plot Ticker:
Query for a single stock. This will work with the loaded data. -Note that if you want to reload the data, this will once again take some time. -""" -st.markdown(title_html, unsafe_allow_html=True) - -st.markdown(middle_html, unsafe_allow_html=True) - -MAIN_LOOP: asyncio.AbstractEventLoop = None # type: ignore - -st_helpers.set_current_page("Short Data") -st_helpers.set_css() - - -class FinraShortData: - def __init__(self, days_slider=30, count_slider=10): - self.df = st_helpers.load_state("df", pd.DataFrame()) - self.days_slider = st_helpers.load_state("days_slider", days_slider) - self.count_slider = st_helpers.load_state("count_slider", count_slider) - self.ticker_button = None - self.load_button = None - self.show_button = None - self.loaded = False - - def activate_buttons(self): - if not self.loaded: - with ticker_button.container(): - self.ticker_button = ticker_button.button( - "Plot Ticker", key="ticker_button" - ) - with show_button.container(): - self.show_button = show_button.button("Show", key="show_button") - self.loaded = True - - def show_button_click(self): - output1.empty() - self.update() - - def load_button_click(self): - output1.empty() - output2.empty() - with st.spinner(f"Loading data for {self.days_slider} days"): - self.fetch_new_data() - self.update() - - def ticker_button_click(self): - output2.empty() - self.ticker_plot() - - def fetch_new_data(self): - self.df = pd.DataFrame() - today = datetime.now().date() - start_date = today - timedelta(days=self.days_slider) - dates = pd.date_range(start_date, today) - for date in dates: - r = requests.get( - f"https://cdn.finra.org/equity/regsho/daily/CNMSshvol{date.strftime('%Y%m%d')}.txt", - timeout=30, - ) - if r.status_code == 200: - self.df = pd.concat( - [self.df, pd.read_csv(io.StringIO(r.text), sep="|")], axis=0 - ) - - self.df = self.df[self.df.Date > 20100101] - self.df.Date = self.df["Date"].apply( - lambda x: datetime.strptime(str(x), "%Y%m%d") - ) - st_helpers.save_state("df", self.df) - - def update(self): - if not self.df.empty: - temp = ( - self.df.groupby("Symbol")[["ShortVolume", "TotalVolume"]] - .agg("sum") - .sort_values(by="ShortVolume", ascending=False) - .head(self.count_slider)[::-1] - ) - fig = OpenBBFigure() - fig.add_bar( - x=temp.TotalVolume, - y=temp.index, - orientation="h", - name="Total Volume", - marker_color=theme.up_color, - ) - fig.add_bar( - x=temp.ShortVolume, - y=temp.index, - orientation="h", - name="Short Volume", - marker_color=theme.down_color, - ) - fig.update_layout( - title=f"Top {self.count_slider} Short Volume in Last {self.days_slider} Days", - margin=dict(l=30), - xaxis_title="Volume", - yaxis_title="Ticker", - barmode="stack", - bargap=0.1, - hovermode="y unified", - legend=dict( - orientation="h", - yanchor="bottom", - y=1.02, - xanchor="right", - x=1, - ), - ) - fig.show(external=True) - with output1: - st.plotly_chart(fig, use_container_width=True) - - def ticker_plot(self): - stock_data = self.df.copy().loc[ - self.df.Symbol == self.stock_input.upper(), - ["Date", "ShortVolume", "TotalVolume"], - ] - fig2 = OpenBBFigure() - fig2.add_scatter( - x=stock_data.Date, - y=stock_data.TotalVolume, - name="Total Volume", - marker_color=theme.up_color, - ) - fig2.add_scatter( - x=stock_data.Date, - y=stock_data.ShortVolume, - name="Short Volume", - marker_color=theme.down_color, - ) - fig2.update_layout( - title=f"Stock Volume and Short Volume for {self.stock_input.upper()}", - margin=dict(l=30), - xaxis_title="Date", - yaxis_title="Volume", - ) - fig2.show(external=True) - with output2: - st.plotly_chart(fig2) - - def build_app(self): - with TICKER: - self.stock_input = TICKER.text_input("Ticker", "GME", key="ticker") - with DAYS: - self.days_slider = DAYS.slider( - "Days", 1, 1000, 100, help="Number of days to load" - ) - with COUNT: - self.count_slider = COUNT.slider( - "Count", 1, 100, 20, help="Number of stocks to plot" - ) - - with load_button.container(): - self.load_button = load_button.button("Load Data", key="load") - - if not self.df.empty: - self.activate_buttons() - - if self.load_button: - self.load_button_click() - if self.show_button: - self.show_button_click() - if self.ticker_button: - self.ticker_button_click() - - if not self.df.empty: - self.activate_buttons() - - -if __name__ == "__main__": - app = FinraShortData() - app.build_app() diff --git a/openbb_terminal/dashboards/stream/pages/Stocks.py b/openbb_terminal/dashboards/stream/pages/Stocks.py deleted file mode 100644 index 826a1118046..00000000000 --- a/openbb_terminal/dashboards/stream/pages/Stocks.py +++ /dev/null @@ -1,401 +0,0 @@ -import asyncio -import re -from datetime import datetime, timedelta -from unittest.mock import patch - -import pandas as pd -import streamlit as st -import yfinance as yf - -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.dashboards.stream import ( - common_vars, - streamlit_helpers as st_helpers, -) -from openbb_terminal.rich_config import console - -pd.options.plotting.backend = "plotly" -# Suppressing sdk logs -set_system_variable("LOGGING_SUPPRESS", True) - -st.set_page_config( - layout="wide", - page_title="Stocks", - initial_sidebar_state="expanded", -) -st_helpers.set_current_page("Stocks") - -logger = st.empty() -page_title = st.empty() -main_chart, table_container = st.columns([4, 1.5]) -volume_chart = st.empty() - -MAIN_LOOP: asyncio.AbstractEventLoop = None # type: ignore - - -def special_st(text: str): - rich_color = re.search(r"\[([a-z ]+)\]", text) - text = re.sub(r"(\[\/{0,1}[a-z ]+\])|(\[\/\])", "", text) - - async def _special_st(): - with logger.container(): - if rich_color: - color = rich_color.group(1) - if "green" in color: - st.success(text, icon="🎉") - elif "red" in color: - st.error(text, icon="🔥") - await asyncio.sleep(5) - elif "yellow" in color: - st.warning(text, icon="⚠️") - else: - st.write(text) - await asyncio.sleep(4) - - MAIN_LOOP.create_task(_special_st()) - - -st_helpers.set_css() -st.sidebar.markdown( - "

Stock Analysis Dashboard

", - unsafe_allow_html=True, -) -r2c1, r2c2 = st.sidebar.columns([1, 1]) -r2c3 = st.sidebar.container() - - -def format_plotly( - fig: OpenBBFigure, - data: str, - start: datetime, - end: datetime, - calc: pd.DataFrame = None, -): - fig.update_yaxes(title=None) - fig.update_xaxes(title=None) - start_t = start.strftime("%Y/%m/%d") - end_t = end.strftime("%Y/%m/%d") - if calc: - if len(calc) == 1: - fig_title = f"{calc[0]} of {data} from {start_t} to {end_t}" - else: - fig_title = f"{', '.join(calc)} of {data} from {start_t} to {end_t}" - height = 400 - else: - fig_title = "Volume" - height = 250 - fig.update_layout( - autosize=False, - height=height, - title=dict( - text=fig_title, - y=1, - x=0.5, - xanchor="center", - yanchor="top", - ), - legend=dict( - bgcolor="rgba(0,0,0,0.5)", - bordercolor="#F5EFF3", - borderwidth=1, - x=0.99, - xanchor="right", - ), - ) - - -def create_line( - visual: str, - x: pd.DataFrame, - y: pd.DataFrame, - name: str, - data: str, - fig: OpenBBFigure, -): - if visual == "line": - fig.add_scatter( - x=x, - y=y[data], - mode="lines", - name=name, - connectgaps=True, - hovertemplate="%{y}", - ) - if visual == "scatter": - fig.add_scatter(x=x, y=y[data], mode="markers", name=name, hovertemplate="%{y}") - if visual == "candle": - fig.add_candlestick( - x=x, - open=y["Open"], - close=y["Close"], - high=y["High"], - low=y["Low"], - name=name, - ) - - -def show_fig(fig: OpenBBFigure, margin: bool = True, volume: bool = False): - fig.show(external=True, margin=margin) - with_container = main_chart if not volume else volume_chart - with with_container.container(): - st.plotly_chart( - fig, - use_container_width=True, - config=dict( - scrollZoom=True, - displaylogo=False, - ), - ) - - -def table_data(infos: dict): - cols = ["Ticker"] + list(infos) - data = pd.DataFrame(columns=cols) - data["Ticker"] = [common_vars.STOCKS_CLEAN_ROW[x] for x in common_vars.STOCKS_ROWS] - for ticker in list(infos): - data[ticker] = [ - ( - st_helpers.STOCKS_CLEAN_DATA[x](infos[ticker].get(x, None)) - if infos[ticker].get(x, None) - else st_helpers.STOCKS_CLEAN_DATA[x]( - getattr(yf.Ticker(ticker).fast_info, x, None) - ) - ) - for x in common_vars.STOCKS_ROWS - ] - return data - - -def stock_data( - tickers: list, interval: str, start: datetime, end: datetime -) -> pd.DataFrame: - with patch.object(console, "print", special_st): - try: - kwargs = {} - if interval in ["1d", "5d", "1wk", "1mo", "3mo"]: - kwargs.update({"period": "max"}) - else: - s_start_dt = datetime.utcnow() - timedelta(days=59) - s_date_start = s_start_dt.strftime("%Y-%m-%d") - start = s_date_start if s_start_dt.date() > start else start # type: ignore - kwargs.update({"start": start, "end": end + timedelta(days=1)}) # type: ignore - - df: pd.DataFrame = yf.download( - tickers, - interval=interval, - progress=False, - **kwargs, - ) - if not df.empty: - df.index = pd.to_datetime(df.index).tz_localize(None) - return df - except Exception: # noqa: S110 - pass - return pd.DataFrame() - - -def volume_data(infos: dict, start: datetime, end: datetime, interval: str) -> dict: - result = {} - for ticker in infos: - try: - df: pd.DataFrame = yf.download( - ticker, start=start, end=end, interval=interval - ) - df = df[["Volume"]] - df = df.rename(columns={"Volume": ticker}) - result[ticker] = df - except Exception: # noqa: S110 - pass - return result - - -class Chart: - # pylint: disable=too-many-instance-attributes - def __init__(self, loop_: asyncio.AbstractEventLoop): - self.df: pd.DataFrame = st_helpers.load_state("df", pd.DataFrame()) - self.tickers: str = st_helpers.load_state("tickers", "TSLA") - self.interval: str = st_helpers.load_state("interval", "") - self.last_tickers: str = st_helpers.load_state("last_tickers", "") - self.last_interval: str = st_helpers.load_state("last_interval", "") - self.infos: dict = st_helpers.load_state("infos", {}) - self.start: datetime = st_helpers.load_state( - "start_date", (datetime.today() - timedelta(days=365)) - ) - self.end: datetime = st_helpers.load_state("end_date", datetime.now()) - self.calculation: list = st_helpers.load_state("calculation", ["Raw Data"]) - self.data: str = st_helpers.load_state("data", "Close") - self.rolling: int = st_helpers.load_state("rolling", 60) - self.chart: str = st_helpers.load_state("chart", "line") - - self.loop = loop_ - - global MAIN_LOOP # pylint: disable=W0603 # noqa: PLW0603 - MAIN_LOOP = loop_ - - def create_stock(self): - self.tickers = self.tickers.split(",") - if not self.tickers: - st.error("Missing Tickers") - if self.tickers: - if self.tickers != self.last_tickers or self.interval != self.last_interval: - # get stock data - self.df = stock_data(self.tickers, self.interval, self.start, self.end) - self.last_tickers = self.tickers - self.last_interval = self.interval - - fig = OpenBBFigure() - for item in self.calculation: - calcs = st_helpers.get_calc(item, self.df, self.rolling) - if self.interval in ["1d", "5d", "1wk", "1mo", "3mo"]: - result = calcs.loc[ - (calcs.index >= self.start) & (calcs.index <= self.end) - ] - else: - result = calcs - - if len(result.columns) == 6: - name = f"{self.tickers[0]} {item}" - create_line(self.chart, result.index, result, name, self.data, fig) - - else: - for val in result.columns.levels[1]: - vals = result.xs(val, axis=1, level=1, drop_level=True) - name = f"{val.upper()} {item}" - create_line( - self.chart, result.index, vals, name, self.data, fig - ) - format_plotly(fig, self.data, self.start, self.end, self.calculation) - fig.update_layout(margin=dict(l=30, r=0, t=25, b=0)) - show_fig(fig, False) - - def create_volume(self): - result = self.df.loc[ - (self.df.index >= self.start) & (self.df.index <= self.end) - ] - fig = OpenBBFigure.create_subplots( - 1, 1, horizontal_spacing=0.001, vertical_spacing=0.001 - ) - if len(result.columns) == 6: - name = f"{self.tickers[0]}" - create_line("line", result.index, result, name, "Volume", fig) - else: - for val in result.columns.levels[1]: - vals = result.xs(val, axis=1, level=1, drop_level=True) - name = f"{val.upper()}" - create_line("line", result.index, vals, name, "Volume", fig) - - format_plotly(fig, "Volume", self.start, self.end) - fig.update_layout(margin=dict(l=30, r=0, t=25, b=0)) - - show_fig(fig, False) - - def create_table(self): - if not self.tickers: - return - for ticker in self.tickers: - if ticker not in self.infos: - try: - self.infos[ticker] = yf.Ticker(ticker).info - except Exception: - self.infos[ticker] = {} - delete = [ticker for ticker in self.infos if ticker not in self.tickers] - for ticker in delete: - self.infos.pop(ticker) - result = table_data(self.infos) - result.index = result.set_index("Ticker").index - result = result.drop("Ticker", axis=1) - with table_container: - st.table(result) - - async def async_on_ticker_change(self): - new_data = False - tickers = list(set(st.session_state.tickers.split(","))) # type: ignore - tickers = [ticker.upper().strip() for ticker in tickers if ticker.strip()] # type: ignore - - try: - interval = st.session_state.interval - except AttributeError: - interval = "1d" - - check_last = [ - ("last_tickers", ",".join(tickers)), - ("last_interval", interval), - ] - new_params = any( - st.session_state.get(key, None) != value for key, value in check_last - ) - if new_params: - for key, value in check_last: - st.session_state[key] = value - new_data = True - - if new_data: - st.session_state["tickers"] = ",".join(tickers) - - def on_ticker_change(self): - self.loop.run_until_complete(self.async_on_ticker_change()) - - async def run(self): - tickers = st.sidebar.text_input( - "Ticker(s) (comma separated)", - "", - key="tickers", - on_change=self.on_ticker_change, - help="Enter a comma separated list of tickers", - ) - date_opts = st.sidebar.expander("Date options") - data_opts = st.sidebar.expander("Data options") - ( - r1c2, - r1c3, - ) = date_opts.columns([1, 1]) - - r1c4, r1c5 = data_opts.columns([1, 1]) - - with date_opts.container(): - with r1c2: - self.start = st.date_input( - "Start", (datetime.today() - timedelta(days=365)), key="start_date" - ) - with r1c3: - self.end = st.date_input("End", datetime.today(), key="end_date") - self.interval = st.selectbox( - "Interval", common_vars.INTERVAL_OPTS[1:], index=2, key="interval" - ) - - with data_opts.container(): - with r1c4: - self.data = st.selectbox( - "Target", ["Open", "Close", "High", "Low"], index=1, key="data" - ) - with r1c5: - self.chart = st.selectbox( - "Chart", ["line", "scatter", "candle"], index=0, key="chart" - ) - self.calculation = st.multiselect( - "Calculations", - list(common_vars.STOCKS_VIEWS.keys()), - ["Raw Data"], - key="calculation", - ) - self.rolling = st.number_input("Rolling", 3, 100, 60, key="rolling") - - st.sidebar.markdown("""---""") - submit_button = st.sidebar.button("Display") - if submit_button and tickers: - self.start = datetime.combine(self.start, datetime.min.time()) - self.end = datetime.combine(self.end, datetime.max.time()) - self.create_stock() - self.create_volume() - self.create_table() - - -if __name__ == "__main__": - try: - loop = asyncio.get_running_loop() - except RuntimeError: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - - loop.run_until_complete(Chart(loop).run()) diff --git a/openbb_terminal/dashboards/stream/streamlit_helpers.py b/openbb_terminal/dashboards/stream/streamlit_helpers.py deleted file mode 100644 index 573df9a37be..00000000000 --- a/openbb_terminal/dashboards/stream/streamlit_helpers.py +++ /dev/null @@ -1,167 +0,0 @@ -import re -from datetime import datetime -from inspect import signature -from typing import Any, Callable - -import pandas as pd -import streamlit as st -from rich.table import Table - -from openbb_terminal.dashboards.stream.common_vars import STOCKS_VIEWS - -REGEX_RICH = re.compile(r"\[\/{0,1}[a-zA-Z0-9#]+\]|\[\/\]") - -STOCKS_CLEAN_DATA = { - "sector": lambda x: "N/A" if x is None else x, - "market_cap": lambda x: "N/A" if x is None else big_num(x), - "beta": lambda x: "N/A" if x is None else f"{round(x,2)}", - "year_high": lambda x: "N/A" if x is None else f"${round(x,2)}", - "year_low": lambda x: "N/A" if x is None else f"${round(x,2)}", - "floatShares": lambda x: "N/A" if x is None else big_num(x), - "sharesShort": lambda x: "N/A" if x is None else big_num(x), - "exDividendDate": lambda x: ( - "N/A" if x is None else datetime.fromtimestamp(x).strftime("%Y/%m/%d") - ), -} - - -def update_current_page() -> None: - """Updates the current page to the set page""" - st.session_state["set_page"] = st.session_state["current_page"] - - -def set_current_page(page: str) -> None: - """Sets the current page to the given page""" - st.session_state["current_page"] = page - - -def get_calc(item, df, rolling) -> pd.DataFrame: - return STOCKS_VIEWS[item](df, rolling) - - -def big_num(num): - if num > 1_000_000_000_000: - return f"{round(num/1_000_000_000_000,2)}T" - if num > 1_000_000_000: - return f"{round(num/1_000_000_000,2)}B" - if num > 1_000_000: - return f"{round(num/1_000_000,2)}M" - if num > 1_000: - return f"{num/round(1_000,2)}K" - return f"{round(num,2)}" - - -def clean_str(string): - new_str = "" - for letter in string: - if letter.isupper(): - new_str += " " - new_str += letter - return new_str.title() - - -def format_df(df: pd.DataFrame) -> pd.DataFrame: - if len(df.columns) != 6: - df.columns = ["_".join(col).strip() for col in df.columns.values] - df.reset_index(inplace=True) - df.columns = [x.lower() for x in df.columns] - return df - - -def has_parameter(func: Callable[..., Any], parameter: str) -> bool: - params = signature(func).parameters - parameters = params.keys() - return parameter in parameters - - -def load_state(name: str, default: Any) -> Any: - if name not in st.session_state: - st.session_state[name] = default - elif st.session_state.get("current_page", None) != st.session_state.get( - "set_page", None - ): - update_current_page() - st.session_state[name] = default - - return st.session_state[name] - - -def load_widget_options(default: Any) -> Any: - name = "widget_options" - if name not in st.session_state: - st.session_state[name] = default - elif st.session_state.get("current_page", None) != st.session_state.get( - "set_page", None - ): - update_current_page() - st.session_state[name] = default - - return st.session_state[name] - - -def get_widget_options(default: dict, key: str) -> Any: - options = load_widget_options(default) - if key not in options: - options[key] = default.get(key, None) - - return options[key] - - -def save_state(name: str, value: Any): - st.session_state[name] = value - - -def rich_to_dataframe(table: Table) -> pd.DataFrame: - columns = [column.header for column in table.columns] - rows: dict = {column: [] for column in columns} - for column in table.columns: - for cell in column.cells: - text: str = re.sub(REGEX_RICH, "", cell) # type: ignore - rows[column.header].append(text) - - df = pd.DataFrame(rows, columns=columns) - if "Datetime" in df.columns: - df.index = pd.to_datetime(df["Datetime"]).dt.date - df.drop("Datetime", axis=1, inplace=True) - - return df - - -def set_css(): - """Set the CSS for the app.""" - css_container_style = """ - - """ - st.markdown(css_container_style, unsafe_allow_html=True) diff --git a/openbb_terminal/dashboards/streamlit_run.py b/openbb_terminal/dashboards/streamlit_run.py deleted file mode 100644 index 3fe378a5884..00000000000 --- a/openbb_terminal/dashboards/streamlit_run.py +++ /dev/null @@ -1,48 +0,0 @@ -import re -import sys -from pathlib import Path - -import streamlit.web.bootstrap - -from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.core.session.current_user import set_preference - -set_system_variable("LOGGING_SUPPRESS", True) -set_preference("CHART_STYLE", "dark") -set_preference("PLOT_ENABLE_PYWRY", "False") - - -def main(): - # pylint: disable=E1101,W0212 - parent_path = Path(sys._MEIPASS) if hasattr(sys, "frozen") else REPOSITORY_DIRECTORY # type: ignore - filepath = Path(__file__).parent / "stream" / "Forecasting.py" - file = filepath.relative_to(parent_path).as_posix() - - cmdline = " ".join(sys.argv) - port = re.findall(r"--port=(\d+)", cmdline) - port = int(port[0]) if port else 8501 - - flag_options = { - "server.port": port, - "server.headless": True, - "global.developmentMode": False, - "server.enableCORS": False, - "server.enableXsrfProtection": False, - "browser.serverAddress": "localhost", - "theme.base": "dark", - "browser.gatherUsageStats": False, - } - - streamlit.web.bootstrap.load_config_options(flag_options=flag_options) - flag_options["_is_running_with_streamlit"] = True - streamlit.web.bootstrap.run( - str(file), - "streamlit run", - [], - flag_options, - ) - - -if __name__ == "__main__": - main() diff --git a/openbb_terminal/dashboards/voila/chains.ipynb b/openbb_terminal/dashboards/voila/chains.ipynb deleted file mode 100644 index 037b1ddaa66..00000000000 --- a/openbb_terminal/dashboards/voila/chains.ipynb +++ /dev/null @@ -1,181 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e9ad60bc", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import ipywidgets as widgets\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "\n", - "import yfinance as yf\n", - "from IPython.display import display" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "beb36881", - "metadata": {}, - "outputs": [], - "source": [ - "opts = [\n", - " \"lastTradeDate\",\n", - " \"strike\",\n", - " \"lastPrice\",\n", - " \"bid\",\n", - " \"ask\",\n", - " \"change\",\n", - " \"percentChange\",\n", - " \"volume\",\n", - " \"openInterest\",\n", - " \"impliedVolatility\",\n", - "]\n", - "\n", - "\n", - "def clean_str(string):\n", - " new_str = \"\"\n", - " for letter in string:\n", - " if letter.isupper():\n", - " new_str += \" \"\n", - " new_str += letter\n", - " return new_str.title()\n", - "\n", - "\n", - "def format_plotly(fig: OpenBBFigure, x, y, ticker, expiry, inst):\n", - " fig.update_yaxes(title=clean_str(y))\n", - " fig.update_xaxes(title=clean_str(x))\n", - " expires = \", \".join(expiry)\n", - " title = (\n", - " f\"{clean_str(y)} vs. {clean_str(x)} for {ticker.upper()} {inst}s on {expires}\"\n", - " )\n", - " fig.update_layout(\n", - " margin=dict(t=40),\n", - " autosize=False,\n", - " width=1000,\n", - " height=500,\n", - " title=dict(\n", - " text=title,\n", - " y=0.98,\n", - " x=0.5,\n", - " xanchor=\"center\",\n", - " yanchor=\"top\",\n", - " ),\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8c2f65c", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "class Chart:\n", - " def __init__(self):\n", - " self.last_ticker = \"\"\n", - " self.dfs = {}\n", - "\n", - " def update(self, ticker, expiry, inst, x, y):\n", - " if ticker:\n", - " stock = yf.Ticker(ticker)\n", - " if ticker != self.last_ticker:\n", - " exp_w.options = stock.options\n", - " self.last_ticker = ticker\n", - "\n", - " if expiry:\n", - " fig = OpenBBFigure()\n", - " for expire in expiry:\n", - " if expire not in self.dfs:\n", - " self.dfs[expire] = stock.option_chain(expire)\n", - " group = self.dfs[expire]\n", - " df = group.calls if inst == \"Call\" else group.puts\n", - "\n", - " fig.add_scatter(\n", - " x=df[x],\n", - " y=df[y],\n", - " mode=\"lines\",\n", - " connectgaps=True,\n", - " name=expire,\n", - " )\n", - "\n", - " format_plotly(fig, x, y, ticker, expiry, inst)\n", - "\n", - " if os.environ.get(\"SERVER_SOFTWARE\", \"jupyter\").startswith(\"voila\"):\n", - " fig.show(\n", - " config={\"showTips\": False, \"displaylogo\": False},\n", - " renderer=\"notebook\",\n", - " )\n", - " else:\n", - " fig.show(config={\"showTips\": False, \"displaylogo\": False})\n", - "\n", - "\n", - "x_w = widgets.Dropdown(options=opts, value=\"strike\", description=\"X\")\n", - "y_w = widgets.Dropdown(options=opts, value=\"lastPrice\", description=\"Y\")\n", - "tickers_w = widgets.Text(description=\"Ticker\")\n", - "exp_w = widgets.SelectMultiple(description=\"Expiry\")\n", - "inst_w = widgets.Dropdown(options=[\"Put\", \"Call\"], value=\"Call\", description=\"Type\")\n", - "\n", - "select_opt = widgets.VBox([tickers_w, exp_w])\n", - "select_plot = widgets.VBox([inst_w, x_w, y_w])\n", - "controls = widgets.HBox([select_opt, select_plot])\n", - "\n", - "chart = Chart()\n", - "stocks_view = widgets.interactive_output(\n", - " chart.update,\n", - " {\"ticker\": tickers_w, \"expiry\": exp_w, \"inst\": inst_w, \"x\": x_w, \"y\": y_w},\n", - ")\n", - "\n", - "title_html = \"

Option Chain Dashboard

\"\n", - "app_contents = [\n", - " widgets.HTML(title_html),\n", - " controls,\n", - " stocks_view,\n", - "]\n", - "app = widgets.VBox(app_contents)\n", - "display(app)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a84c7390-6b43-4a25-8aa2-31aa84f52605", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.12" - }, - "metadata": { - "interpreter": { - "hash": "e896a00ead8b521528d79ac9ef24990696f2b751eb283ab8e0d078c9c4971ffc" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/dashboards/voila/correlation.ipynb b/openbb_terminal/dashboards/voila/correlation.ipynb deleted file mode 100644 index a6c2bf06d68..00000000000 --- a/openbb_terminal/dashboards/voila/correlation.ipynb +++ /dev/null @@ -1,200 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e9ad60bc", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from datetime import datetime, timedelta\n", - "import ipywidgets as widgets\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "import yfinance as yf\n", - "import pandas as pd\n", - "from IPython.display import display\n", - "\n", - "theme.apply_style(\"light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6c554cd2-462c-4832-b73c-c214aae11ce4", - "metadata": {}, - "outputs": [], - "source": [ - "interval_opts = [\n", - " \"1m\",\n", - " \"2m\",\n", - " \"5m\",\n", - " \"15m\",\n", - " \"30m\",\n", - " \"60m\",\n", - " \"90m\",\n", - " \"1h\",\n", - " \"1d\",\n", - " \"5d\",\n", - " \"1wk\",\n", - " \"1mo\",\n", - " \"3mo\",\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8c2f65c", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "class Chart:\n", - " def __init__(self):\n", - " self.last_tickers = \"\"\n", - " self.last_interval = \"1d\"\n", - " self.df = pd.DataFrame()\n", - "\n", - " def create(self, data, start, end, interval, tickers):\n", - " global last_tickers, df, last_interval\n", - " if tickers and tickers[-1] == \",\":\n", - " if tickers != self.last_tickers or interval != self.last_interval:\n", - " if interval in [\"1d\", \"5d\", \"1wk\", \"1mo\", \"3mo\"]:\n", - " self.df = yf.download(\n", - " tickers, period=\"max\", interval=interval, progress=False\n", - " )\n", - " else:\n", - " self.df = yf.download(\n", - " tickers, start=start, end=end, interval=interval, progress=False\n", - " )\n", - " self.last_tickers = tickers\n", - " self.last_interval = interval\n", - "\n", - " start_n = datetime(start.year, start.month, start.day)\n", - " end_n = datetime(end.year, end.month, end.day)\n", - "\n", - " df = self.df[data]\n", - "\n", - " if not isinstance(df, pd.Series):\n", - " if interval in [\"1d\", \"5d\", \"1wk\", \"1mo\", \"3mo\"]:\n", - " result = df.loc[(df.index >= start_n) & (df.index <= end_n)].corr()\n", - " else:\n", - " result = df.corr()\n", - "\n", - " base = [\n", - " [\n", - " \"black\" if x == 1 else \"lightgreen\" if x > 0 else \"lightpink\"\n", - " for x in result[y].tolist()\n", - " ]\n", - " for y in result.columns\n", - " ]\n", - " base = [[\"lightgray\" for _ in range(result.shape[0])]] + base\n", - " result = result.reset_index()\n", - " result.rename(columns={\"index\": \"\"}, inplace=True)\n", - "\n", - " fig = OpenBBFigure.to_table(result, print_index=False)\n", - " fig.update_layout(\n", - " margin=dict(l=0, r=0, t=0, b=0),\n", - " paper_bgcolor=\"rgba(0,0,0,0)\",\n", - " plot_bgcolor=\"rgba(0,0,0,0)\",\n", - " autosize=True,\n", - " height=600,\n", - " showlegend=False,\n", - " )\n", - " if os.environ.get(\"SERVER_SOFTWARE\", \"jupyter\").startswith(\"voila\"):\n", - " fig.show(\n", - " margin=False, config={\"showTips\": False}, renderer=\"notebook\"\n", - " )\n", - " else:\n", - " fig.show(margin=False, config={\"showTips\": False})\n", - "\n", - "\n", - "w_auto = widgets.Layout(width=\"auto\")\n", - "data_opts = [\"Open\", \"Close\", \"High\", \"Low\", \"Volume\"]\n", - "data_widget = widgets.Dropdown(\n", - " options=data_opts, value=\"Close\", layout=w_auto, description=\"Data\"\n", - ")\n", - "\n", - "base_date = (datetime.today() - timedelta(days=365)).date()\n", - "start_widget = widgets.DatePicker(value=base_date, layout=w_auto, description=\"Start\")\n", - "end_widget = widgets.DatePicker(\n", - " value=datetime.today().date(), layout=w_auto, description=\"End\"\n", - ")\n", - "interval_widget = widgets.Dropdown(\n", - " options=interval_opts, value=\"1d\", layout=w_auto, description=\"Interval\"\n", - ")\n", - "tickers_widget = widgets.Textarea(value=\"TSLA,AAPL,\", layout=w_auto)\n", - "\n", - "data_box = widgets.VBox([tickers_widget, data_widget])\n", - "date_box = widgets.VBox([start_widget, end_widget, interval_widget])\n", - "controls = widgets.HBox([data_box, date_box], layout=widgets.Layout(width=\"60%\"))\n", - "\n", - "chart = Chart()\n", - "stocks_view = widgets.interactive_output(\n", - " chart.create,\n", - " {\n", - " \"data\": data_widget,\n", - " \"start\": start_widget,\n", - " \"end\": end_widget,\n", - " \"interval\": interval_widget,\n", - " \"tickers\": tickers_widget,\n", - " },\n", - ")\n", - "\n", - "title_html = \"

Correlation Analysis Dashboard

\"\n", - "warning_html = '

Use a comma after EVERY stock typed.

'\n", - "app_contents = [\n", - " widgets.HTML(title_html),\n", - " controls,\n", - " widgets.HTML(warning_html),\n", - " stocks_view,\n", - "]\n", - "app = widgets.VBox(app_contents)\n", - "display(app)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "37f46146-a206-4c25-b969-81c5abec4b1d", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - }, - "metadata": { - "interpreter": { - "hash": "e896a00ead8b521528d79ac9ef24990696f2b751eb283ab8e0d078c9c4971ffc" - } - }, - "vscode": { - "interpreter": { - "hash": "381a209f752e9104bff95ec5e80679ea2e476866b80e4326f1ee6b7658af6049" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/dashboards/voila/forecast.ipynb b/openbb_terminal/dashboards/voila/forecast.ipynb deleted file mode 100644 index 50ec40fe0e2..00000000000 --- a/openbb_terminal/dashboards/voila/forecast.ipynb +++ /dev/null @@ -1,377 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e9ad60bc", - "metadata": {}, - "outputs": [], - "source": [ - "from datetime import datetime, timedelta\n", - "from typing import Callable, Any\n", - "from inspect import signature\n", - "import warnings, logging\n", - "\n", - "import ipywidgets as widgets\n", - "import yfinance as yf\n", - "import pandas as pd\n", - "from IPython.display import display\n", - "\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "\n", - "theme.apply_style(\"light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "404cb982", - "metadata": {}, - "outputs": [], - "source": [ - "# Suppressing sdk logs\n", - "from openbb_terminal.core.session.current_system import set_system_variable\n", - "\n", - "set_system_variable(\"LOGGING_SUPPRESS\", True)\n", - "\n", - "# Import the OpenBB SDK\n", - "from openbb_terminal.sdk import openbb" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "beb36881", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " has_forecast = True\n", - " model_opts = {\n", - " \"expo\": openbb.forecast.expo_chart,\n", - " \"theta\": openbb.forecast.theta_chart,\n", - " \"linregr\": openbb.forecast.linregr_chart,\n", - " \"regr\": openbb.forecast.regr_chart,\n", - " \"rnn\": openbb.forecast.rnn_chart,\n", - " \"brnn\": openbb.forecast.brnn_chart,\n", - " \"nbeats\": openbb.forecast.nbeats_chart,\n", - " \"tcn\": openbb.forecast.tcn_chart,\n", - " \"trans\": openbb.forecast.trans_chart,\n", - " \"tft\": openbb.forecast.tft_chart,\n", - " \"nhits\": openbb.forecast.nhits_chart,\n", - " }\n", - "\n", - " feat_engs = {\n", - " \"ema\": openbb.forecast.ema,\n", - " \"sto\": openbb.forecast.sto,\n", - " \"rsi\": openbb.forecast.rsi,\n", - " \"roc\": openbb.forecast.roc,\n", - " \"mom\": openbb.forecast.mom,\n", - " \"atr\": openbb.forecast.atr,\n", - " \"delta\": openbb.forecast.delta,\n", - " \"signal\": openbb.forecast.signal,\n", - " }\n", - "except AttributeError as e:\n", - " print(e)\n", - " has_forecast = False\n", - " model_opts = {}\n", - " feat_engs = {}\n", - "\n", - "interval_opts = [\n", - " \"1m\",\n", - " \"2m\",\n", - " \"5m\",\n", - " \"15m\",\n", - " \"30m\",\n", - " \"60m\",\n", - " \"90m\",\n", - " \"1h\",\n", - " \"1d\",\n", - " \"5d\",\n", - " \"1wk\",\n", - " \"1mo\",\n", - " \"3mo\",\n", - "]\n", - "\n", - "\n", - "def format_df(df: pd.DataFrame) -> pd.DataFrame:\n", - " if len(df.columns) != 6:\n", - " df.columns = [\"_\".join(col).strip() for col in df.columns.values]\n", - " df.reset_index(inplace=True)\n", - " df.columns = [x.lower() for x in df.columns]\n", - " return df\n", - "\n", - "\n", - "def has_parameter(func: Callable[..., Any], parameter: str) -> bool:\n", - " params = signature(func).parameters\n", - " parameters = params.keys()\n", - " return parameter in parameters" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8c2f65c", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "class Chart:\n", - " def __init__(self):\n", - " self.last_tickers = \"\"\n", - " self.last_interval = \"1d\"\n", - " self.df = pd.DataFrame()\n", - " self.result = pd.DataFrame()\n", - " self.infos = {}\n", - "\n", - " def handle_changes(\n", - " self,\n", - " past_covariates,\n", - " start,\n", - " end,\n", - " interval,\n", - " tickers,\n", - " target_column,\n", - " model,\n", - " naive,\n", - " forecast_only,\n", - " ):\n", - " ticker_list = tickers.split(\",\")\n", - " if ticker_list:\n", - " if tickers != self.last_tickers or interval != self.last_interval:\n", - " if interval in [\"1d\", \"5d\", \"1wk\", \"1mo\", \"3mo\"]:\n", - " self.df = yf.download(\n", - " tickers, period=\"max\", interval=interval, progress=False\n", - " )\n", - " else:\n", - " end_date = end + timedelta(days=1)\n", - " self.df = yf.download(\n", - " tickers,\n", - " start=start,\n", - " end=end_date,\n", - " interval=interval,\n", - " progress=False,\n", - " )\n", - " self.df = format_df(self.df)\n", - " self.last_tickers = tickers\n", - " self.last_interval = interval\n", - " forecast_model = model_opts[model]\n", - " self.forecast_model = forecast_model\n", - " contains_covariates = has_parameter(forecast_model, \"past_covariates\")\n", - "\n", - " # Update Inputs\n", - " if list(target_widget.options) != [\n", - " x for x in self.df.columns if x != \"date\"\n", - " ]:\n", - " target_widget.options = [x for x in self.df.columns if x != \"date\"]\n", - " return\n", - " if list(past_covs_widget.options) != [\n", - " x for x in self.df.columns if x != \"date\"\n", - " ]:\n", - " past_covs_widget.options = [x for x in self.df.columns if x != \"date\"]\n", - " past_covs_widget.disabled = not contains_covariates\n", - " return\n", - " if past_covs_widget.disabled == contains_covariates:\n", - " past_covs_widget.disabled = not contains_covariates\n", - " column_widget.options = [x for x in self.df.columns if x != \"date\"]\n", - "\n", - " start_n = datetime(start.year, start.month, start.day)\n", - " end_n = datetime(end.year, end.month, end.day)\n", - " calcs = self.df\n", - " if interval in [\"1d\", \"5d\", \"1wk\", \"1mo\", \"3mo\"]:\n", - " result = calcs.loc[\n", - " (calcs[\"date\"] >= start_n) & (calcs[\"date\"] <= end_n)\n", - " ]\n", - " else:\n", - " result = calcs\n", - " if not target_column:\n", - " target_column = self.df.columns[0]\n", - " kwargs = {}\n", - " if contains_covariates and past_covariates != ():\n", - " kwargs[\"past_covariates\"] = \",\".join(past_covariates)\n", - " if has_parameter(forecast_model, \"naive\"):\n", - " kwargs[\"naive\"] = naive\n", - " if has_parameter(forecast_model, \"forecast_only\"):\n", - " kwargs[\"forecast_only\"] = forecast_only\n", - " # This sets up everything to run the function on button click\n", - "\n", - " self.result = result\n", - " self.target_column = target_column\n", - " self.n_predict = 5\n", - " self.kwargs = kwargs\n", - "\n", - " def handle_click(self, to_run):\n", - " if to_run:\n", - " run_widget.value = False\n", - " else:\n", - " df = self.result.dropna()\n", - " if not df.empty:\n", - " self.forecast_model(\n", - " self.result,\n", - " target_column=self.target_column,\n", - " n_predict=5,\n", - " **self.kwargs\n", - " )\n", - "\n", - " def handle_eng(self, target, feature):\n", - " self.feature_target = target\n", - " self.feature_model = feat_engs[feature]\n", - "\n", - " def handle_click2(self, to_run):\n", - " if to_run:\n", - " add_widget.value = False\n", - " else:\n", - " kwargs = {}\n", - " if has_parameter(self.feature_model, \"target_column\"):\n", - " kwargs[\"target_column\"] = self.feature_target\n", - " self.df = self.feature_model(self.df, **kwargs)\n", - " past_covs_widget.options = self.df.columns" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e0d4de31-1b4f-4c1f-8467-ca314a578d56", - "metadata": {}, - "outputs": [], - "source": [ - "w_auto = widgets.Layout(width=\"auto\")\n", - "model_value = list(model_opts)[0] if model_opts else None\n", - "model_widget = widgets.Select(\n", - " options=list(model_opts),\n", - " value=model_value,\n", - " layout=widgets.Layout(\n", - " width=\"8%\",\n", - " ),\n", - ")\n", - "past_covs_widget = widgets.SelectMultiple(\n", - " options=[\"\"],\n", - " value=[\"\"],\n", - " layout=widgets.Layout(\n", - " width=\"8%\",\n", - " ),\n", - ")\n", - "\n", - "base_date = (datetime.today() - timedelta(days=365)).date()\n", - "start_widget = widgets.DatePicker(value=base_date, layout=w_auto, description=\"Start\")\n", - "end_widget = widgets.DatePicker(\n", - " value=datetime.today().date(), layout=w_auto, description=\"End\"\n", - ")\n", - "\n", - "target_widget = widgets.Dropdown(options=[\"\"], value=\"\", description=\"Target\")\n", - "interval_widget = widgets.Dropdown(\n", - " options=interval_opts, value=\"1d\", layout=w_auto, description=\"Interval\"\n", - ")\n", - "tickers_widget = widgets.Textarea(\n", - " value=\"TSLA,\", layout=widgets.Layout(width=\"auto\", height=\"100%\")\n", - ")\n", - "\n", - "# Output box\n", - "naive_widget = widgets.ToggleButton(value=False, description=\"Show Naive\")\n", - "forecast_only_widget = widgets.ToggleButton(value=False, description=\"Forecast Only\")\n", - "run_widget = widgets.ToggleButton(value=False, description=\"Run Model\")\n", - "\n", - "# feat_box\n", - "feat_value = list(feat_engs.keys())[0] if feat_engs else None\n", - "column_widget = widgets.Dropdown(options=[\"\"], value=\"\", description=\"Target\")\n", - "feat_widget = widgets.Dropdown(\n", - " options=list(feat_engs.keys()), value=feat_value, description=\"Feature\"\n", - ")\n", - "add_widget = widgets.ToggleButton(\n", - " value=False, description=\"Add Feature\", layout=widgets.Layout(align=\"flex_end\")\n", - ") # get this to work\n", - "\n", - "selection_box = widgets.VBox([tickers_widget, target_widget])\n", - "date_box = widgets.VBox([start_widget, end_widget, interval_widget])\n", - "output_box = widgets.VBox([naive_widget, forecast_only_widget, run_widget])\n", - "feat_box = widgets.VBox([column_widget, feat_widget, add_widget])\n", - "controls = widgets.HBox(\n", - " [selection_box, model_widget, past_covs_widget, date_box, output_box, feat_box],\n", - ")\n", - "if has_forecast:\n", - " chart = Chart()\n", - " widgets.interactive_output(\n", - " chart.handle_changes,\n", - " {\n", - " \"past_covariates\": past_covs_widget,\n", - " \"start\": start_widget,\n", - " \"end\": end_widget,\n", - " \"interval\": interval_widget,\n", - " \"tickers\": tickers_widget,\n", - " \"target_column\": target_widget,\n", - " \"model\": model_widget,\n", - " \"naive\": naive_widget,\n", - " \"forecast_only\": forecast_only_widget,\n", - " },\n", - " )\n", - "\n", - " widgets.interactive_output(\n", - " chart.handle_eng, {\"target\": column_widget, \"feature\": feat_widget}\n", - " )\n", - " widgets.interactive_output(chart.handle_click2, {\"to_run\": add_widget})\n", - "\n", - " stocks_view = widgets.interactive_output(chart.handle_click, {\"to_run\": run_widget})\n", - "\n", - " title_html = \"

Timeseries Forecasting Dashboard

\"\n", - " warning_html = '

Use a comma after EVERY stock typed.

'\n", - "\n", - " app_contents = [\n", - " widgets.HTML(title_html),\n", - " controls,\n", - " widgets.HTML(warning_html),\n", - " stocks_view,\n", - " ]\n", - " app = widgets.VBox(app_contents)\n", - "else:\n", - " title_html = \"

Timeseries Forecasting Dashboard

\"\n", - " warning_html = (\n", - " '

The forecasting dependencies are not installed.

'\n", - " )\n", - " app = widgets.VBox([widgets.HTML(title_html), widgets.HTML(warning_html)])\n", - "display(app)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9dbf43a2-70e1-44e9-a524-be3e97008b58", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.13" - }, - "metadata": { - "interpreter": { - "hash": "e896a00ead8b521528d79ac9ef24990696f2b751eb283ab8e0d078c9c4971ffc" - } - }, - "vscode": { - "interpreter": { - "hash": "bd3088e2ce83c7406a082a5b650cfc74fd1480320510d276c9fa5780b96e5fae" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/dashboards/voila/futures.ipynb b/openbb_terminal/dashboards/voila/futures.ipynb deleted file mode 100644 index 5d387e5d248..00000000000 --- a/openbb_terminal/dashboards/voila/futures.ipynb +++ /dev/null @@ -1,311 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e9ad60bc", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import sys\n", - "import calendar\n", - "from datetime import datetime, timedelta\n", - "import pandas as pd\n", - "import ipywidgets as widgets\n", - "import yfinance as yf\n", - "import pandas as pd\n", - "from IPython.display import display\n", - "from pathlib import Path\n", - "\n", - "from openbb_terminal import OpenBBFigure\n", - "from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "01e1841a-ab16-4111-9b45-8ac66b85ab9f", - "metadata": {}, - "outputs": [], - "source": [ - "df = pd.read_csv(MISCELLANEOUS_DIRECTORY / \"futures\" / \"futures.csv\")\n", - "\n", - "# These are the symbols that futures use for each month\n", - "months = [\"F\", \"G\", \"H\", \"J\", \"K\", \"M\", \"N\", \"Q\", \"U\", \"V\", \"X\", \"Z\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "beb36881", - "metadata": {}, - "outputs": [], - "source": [ - "class HiddenPrints:\n", - " def __enter__(self):\n", - " self._original_stdout = sys.stdout\n", - " sys.stdout = open(os.devnull, \"w\")\n", - "\n", - " def __exit__(self, exc_type, exc_val, exc_tb):\n", - " sys.stdout.close()\n", - " sys.stdout = self._original_stdout\n", - "\n", - "\n", - "def format_plotly(fig: OpenBBFigure, data, chart, calc=None):\n", - " fig.update_yaxes(title=None)\n", - " fig.update_xaxes(title=None)\n", - " height = 500 if chart == \"main\" else 300\n", - " fig.update_layout(\n", - " margin=dict(l=0, r=10, t=10, b=10),\n", - " autosize=False,\n", - " width=900,\n", - " height=height,\n", - " legend=dict(orientation=\"h\", yanchor=\"bottom\", y=1.02, xanchor=\"right\", x=1),\n", - " )\n", - "\n", - "\n", - "def create_line(visual, x, y, name, fig: OpenBBFigure):\n", - " if visual == \"line\":\n", - " fig.add_scatter(x=x, y=y, mode=\"lines\", name=name) # , connectgaps=True\n", - " if visual == \"scatter\":\n", - " fig.add_scatter(x=x, y=y, mode=\"markers\", name=name)\n", - " if visual == \"candle\":\n", - " fig.add_candlestick(\n", - " x=x,\n", - " open=y[\"Open\"],\n", - " close=y[\"Close\"],\n", - " high=y[\"High\"],\n", - " low=y[\"Low\"],\n", - " name=name,\n", - " )\n", - "\n", - "\n", - "def show_fig(fig: OpenBBFigure):\n", - " config = {\"showTips\": False, \"scrollZoom\": True, \"displaylogo\": False}\n", - " if os.environ.get(\"SERVER_SOFTWARE\", \"jupyter\").startswith(\"voila\"):\n", - " fig.show(config=config, renderer=\"notebook\")\n", - " else:\n", - " fig.show(config=config)\n", - "\n", - "\n", - "def build_ticker(ticker: str, month: int, year: int) -> str:\n", - " if ticker:\n", - " row = df[df[\"Ticker\"] == f\"{ticker.split(':')[0]}\"].iloc[0]\n", - " row = row.to_dict()\n", - " the_tick = row[\"Ticker\"].replace(\"=F\", \"\")\n", - " return f\"{the_tick}{months[month-1]}{str(year)[-2:]}.{row['Exchange']}\"\n", - " return \"\"\n", - "\n", - "\n", - "def next_ticker(ticker: str) -> str:\n", - " symbol, exchange = ticker.split(\".\")\n", - " month = symbol[-3]\n", - " if month == \"Z\":\n", - " new_month = \"F\"\n", - " new_year = int(symbol[-2:]) + 1\n", - " else:\n", - " index = months.index(month)\n", - " new_month = months[index + 1]\n", - " new_year = int(symbol[-2:])\n", - " return f\"{symbol[:-3]}{new_month}{new_year}.{exchange}\"\n", - "\n", - "\n", - "def get_column(df: pd.DataFrame, column: str):\n", - " sub_df = df.xs(column, level=1, axis=1, drop_level=False)\n", - " sub_df.columns = sub_df.columns.to_flat_index().map(lambda x: x[0])\n", - " sub_df = sub_df.dropna(how=\"all\")\n", - " sub_df = sub_df.sort_index()\n", - " if sub_df.empty:\n", - " return None\n", - " return sub_df\n", - "\n", - "\n", - "def get_date(x: str) -> datetime:\n", - " ticker, _ = x.split(\".\")\n", - " month_str = ticker[-3]\n", - " month = months.index(month_str) + 1\n", - " year = int(ticker[-2:]) + 2000\n", - " day = calendar.monthrange(year, month)\n", - " return datetime(year, month, day[1])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8c2f65c", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "class Chart:\n", - " def __init__(self):\n", - " self.tickers = {}\n", - " self.last_ticker = \"\"\n", - "\n", - " def create_stock(self, chart_type, contracts, ticker):\n", - " if not ticker:\n", - " return\n", - "\n", - " if self.last_ticker != ticker:\n", - " now = datetime.now()\n", - " clean_ticker = build_ticker(ticker, now.month, now.year)\n", - " if clean_ticker:\n", - " raw_tickers = [clean_ticker]\n", - " for _ in range(36):\n", - " new_ticker = next_ticker(raw_tickers[-1])\n", - " raw_tickers.append(new_ticker)\n", - " raw_ticker = \",\".join(raw_tickers)\n", - " with HiddenPrints():\n", - " # ,period=\"max\"\n", - " dfs = yf.download(raw_ticker, progress=False)\n", - " self.tickers = {\n", - " x: {\"data\": get_column(dfs, x), \"date\": get_date(x)}\n", - " for x in raw_tickers\n", - " }\n", - " self.last_ticker = ticker\n", - "\n", - " fig = OpenBBFigure()\n", - " if chart_type[0][:4] == \"Hist\":\n", - " for i, (key, item) in enumerate(self.tickers.items()):\n", - " if i == contracts:\n", - " break\n", - " result = item[\"data\"]\n", - " if result is not None:\n", - " create_line(\n", - " visual=\"line\",\n", - " x=result.index,\n", - " y=result[\"Adj Close\"],\n", - " name=key,\n", - " fig=fig,\n", - " )\n", - " else:\n", - " x = []\n", - " y = []\n", - " for _, value in self.tickers.items():\n", - " if len(x) > contracts:\n", - " break\n", - " if value[\"data\"] is None:\n", - " continue\n", - " x.append(value[\"date\"])\n", - " y.append(value[\"data\"][\"Adj Close\"].iloc[-1])\n", - " create_line(visual=\"line\", x=x, y=y, name=\"Future Curve\", fig=fig)\n", - "\n", - " format_plotly(fig, \"Close\", \"main\", \"raw\")\n", - " show_fig(fig)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e0d4de31-1b4f-4c1f-8467-ca314a578d56", - "metadata": {}, - "outputs": [], - "source": [ - "category = df[\"Category\"].unique().tolist()\n", - "exchange = df[df[\"Category\"].isin([category[0]])][\"Exchange\"].unique().tolist()\n", - "filt_df = df[df[\"Category\"].isin([category[0]]) & df[\"Exchange\"].isin([exchange[0]])]\n", - "tickers_raw = filt_df[[\"Ticker\", \"Description\"]].values.tolist()\n", - "tickers = [f\"{x.replace('=F', '')}: {y}\" for x, y in tickers_raw]\n", - "\n", - "category.sort()\n", - "exchange.sort()\n", - "tickers.sort()\n", - "\n", - "chart_type = [\"Historical Time Series\", \"Future Curve\"]\n", - "cat_widget = widgets.SelectMultiple(options=category, value=[category[0]])\n", - "exch_widget = widgets.SelectMultiple(options=exchange, value=[exchange[0]])\n", - "tickers_widget = widgets.Select(options=tickers, value=tickers[0])\n", - "chart_widget = widgets.SelectMultiple(options=chart_type, value=[chart_type[0]])\n", - "\n", - "\n", - "def on_change(change):\n", - " cat_value = cat_widget.value\n", - " exch_value = exch_widget.value\n", - "\n", - " # Filter exchange widget\n", - " exch_filtered = df[df[\"Category\"].isin(cat_value)][\"Exchange\"].unique().tolist()\n", - " exch_filtered.sort()\n", - " exch_widget.options = exch_filtered\n", - "\n", - " # Filter ticker widget\n", - " filtered = df[df[\"Category\"].isin(cat_value) & df[\"Exchange\"].isin(exch_value)]\n", - " tick_raw = filtered[[\"Ticker\", \"Description\"]].values.tolist()\n", - " tick = [f\"{x.replace('=F', '')}: {y}\" for x, y in tick_raw]\n", - " tick.sort()\n", - " tickers_widget.options = tick\n", - "\n", - "\n", - "cat_widget.observe(on_change)\n", - "exch_widget.observe(on_change)\n", - "\n", - "contracts_widget = widgets.Dropdown(\n", - " options=list(range(1, 25)), value=6, description=\"Contracts\"\n", - ")\n", - "\n", - "controls = widgets.HBox(\n", - " [cat_widget, exch_widget, tickers_widget, chart_widget, contracts_widget],\n", - " layout=widgets.Layout(width=\"90%\"),\n", - ")\n", - "chart = Chart()\n", - "stocks_view = widgets.interactive_output(\n", - " chart.create_stock,\n", - " {\n", - " \"chart_type\": chart_widget,\n", - " \"contracts\": contracts_widget,\n", - " \"ticker\": tickers_widget,\n", - " },\n", - ")\n", - "\n", - "\n", - "title_html = \"

Futures Analysis Dashboard

\"\n", - "\n", - "app_contents = [widgets.HTML(title_html), controls, stocks_view]\n", - "app = widgets.VBox(app_contents)\n", - "display(app)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6e4c3424-91c9-47c5-b336-fe8e40944905", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.6" - }, - "metadata": { - "interpreter": { - "hash": "e896a00ead8b521528d79ac9ef24990696f2b751eb283ab8e0d078c9c4971ffc" - } - }, - "vscode": { - "interpreter": { - "hash": "811a8ebb02a69a0058b89c341233bddbf2d95a877272f2a215e4ccb65781d230" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/dashboards/voila/shortdata.ipynb b/openbb_terminal/dashboards/voila/shortdata.ipynb deleted file mode 100644 index 36f79ba77cc..00000000000 --- a/openbb_terminal/dashboards/voila/shortdata.ipynb +++ /dev/null @@ -1,269 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "jupyter": { - "outputs_hidden": true - } - }, - "outputs": [], - "source": [ - "import io\n", - "import pandas as pd\n", - "\n", - "import requests\n", - "from datetime import datetime, timedelta\n", - "import ipywidgets as widgets\n", - "from IPython.display import display\n", - "\n", - "from openbb_terminal import OpenBBFigure, theme" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "jupyter": { - "outputs_hidden": false - }, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "class ShortDataDash:\n", - " def __init__(self):\n", - " self.df = pd.DataFrame()\n", - " self.days_slider = widgets.IntSlider(\n", - " value=5,\n", - " min=5,\n", - " max=252,\n", - " step=1,\n", - " description=\"Days Back\",\n", - " style={\"description_width\": \"initial\"},\n", - " )\n", - " self.count_slider = widgets.IntSlider(\n", - " value=10,\n", - " min=1,\n", - " max=25,\n", - " step=1,\n", - " description=\"Number to show.\",\n", - " style={\"description_width\": \"initial\"},\n", - " )\n", - " self.output1 = widgets.Output()\n", - " self.output2 = widgets.Output()\n", - " self.load_button = widgets.Button(\n", - " description=\"Load Data\", layout=widgets.Layout(width=\"200px\", height=\"40px\")\n", - " )\n", - " self.load_button.on_click(self.load_button_click)\n", - " self.show_button = widgets.Button(\n", - " description=\"Change Number Shown\", layout=self.load_button.layout\n", - " )\n", - " self.show_button.on_click(self.show_button_click)\n", - "\n", - " self.slider_box = widgets.HBox([self.days_slider, self.count_slider])\n", - " self.button_box = widgets.VBox([self.load_button, self.show_button])\n", - "\n", - " self.stock_input = widgets.Text(\n", - " value=\"GME\",\n", - " placeholder=\"GME\",\n", - " description=\"Ticker:\",\n", - " )\n", - " self.ticker_button = widgets.Button(description=\"Plot Ticker\")\n", - " self.ticker_button.on_click(self.ticker_button_click)\n", - "\n", - " def show_button_click(self, b):\n", - " self.output1.clear_output()\n", - " with self.output1:\n", - " self.update()\n", - "\n", - " def load_button_click(self, b):\n", - " self.output1.clear_output()\n", - " self.output2.clear_output()\n", - " with self.output1:\n", - " print(f\"Data Loading for {self.days_slider.value} days\")\n", - " self.fetch_new_data()\n", - " self.update()\n", - "\n", - " def ticker_button_click(self, b):\n", - " self.output2.clear_output()\n", - " with self.output2:\n", - " self.ticker_plot()\n", - "\n", - " def fetch_new_data(self):\n", - " self.df = pd.DataFrame()\n", - " today = datetime.now().date()\n", - " idx = 0\n", - " len_df = 0\n", - " while len_df < self.days_slider.value:\n", - " date = today - timedelta(days=idx)\n", - " r = requests.get(\n", - " f\"https://cdn.finra.org/equity/regsho/daily/CNMSshvol{date.strftime('%Y%m%d')}.txt\"\n", - " )\n", - " if r.status_code == 200:\n", - " self.df = pd.concat(\n", - " [self.df, pd.read_csv(io.StringIO(r.text), sep=\"|\")], axis=0\n", - " )\n", - " len_df += 1\n", - " idx += 1\n", - " self.df = self.df[self.df.Date > 20100101]\n", - " self.df.Date = self.df[\"Date\"].apply(\n", - " lambda x: datetime.strptime(str(x), \"%Y%m%d\")\n", - " )\n", - "\n", - " def update(self):\n", - " if not self.df.empty:\n", - " temp = (\n", - " self.df.groupby(\"Symbol\")[[\"ShortVolume\", \"TotalVolume\"]]\n", - " .agg(\"sum\")\n", - " .sort_values(by=\"ShortVolume\", ascending=False)\n", - " .head(self.count_slider.value)[::-1]\n", - " )\n", - " self.fig = OpenBBFigure()\n", - " self.fig.add_bar(\n", - " x=temp.TotalVolume,\n", - " y=temp.index,\n", - " orientation=\"h\",\n", - " name=\"Total Volume\",\n", - " marker_color=theme.up_color,\n", - " )\n", - " self.fig.add_bar(\n", - " x=temp.ShortVolume,\n", - " y=temp.index,\n", - " orientation=\"h\",\n", - " name=\"Short Volume\",\n", - " marker_color=theme.down_color,\n", - " )\n", - " self.fig.update_layout(\n", - " title=f\"Top {self.count_slider.value} Short Volume in Last {self.days_slider.value} Days\",\n", - " xaxis_title=\"Volume\",\n", - " yaxis_title=\"Ticker\",\n", - " barmode=\"stack\",\n", - " bargap=0.1,\n", - " legend=dict(\n", - " orientation=\"h\",\n", - " yanchor=\"bottom\",\n", - " y=1.02,\n", - " xanchor=\"right\",\n", - " x=1,\n", - " ),\n", - " )\n", - " self.fig.show()\n", - "\n", - " def ticker_plot(self):\n", - " stock_data = self.df.copy().loc[\n", - " self.df.Symbol == self.stock_input.value,\n", - " [\"Date\", \"ShortVolume\", \"TotalVolume\"],\n", - " ]\n", - " self.fig2 = OpenBBFigure()\n", - " self.fig2.add_scatter(\n", - " x=stock_data.Date,\n", - " y=stock_data.TotalVolume,\n", - " name=\"Total Volume\",\n", - " marker_color=theme.up_color,\n", - " )\n", - " self.fig2.add_scatter(\n", - " x=stock_data.Date,\n", - " y=stock_data.ShortVolume,\n", - " name=\"Short Volume\",\n", - " marker_color=theme.down_color,\n", - " )\n", - " self.fig2.update_layout(\n", - " title=f\"Stock Volume and Short Volume for {self.stock_input.value.upper()}\",\n", - " xaxis_title=\"Date\",\n", - " yaxis_title=\"Volume\",\n", - " )\n", - " self.fig2.show()\n", - "\n", - " def build_app(self):\n", - " title_html = \"\"\"\n", - "

Finra Short Data

\n", - "

This widget downloads the consolidated NMS short data from FINRA and aggregates the data by summing over the entire time period.

\n", - "

Note that clicking the 'Load Data' button will reload all data. This can get time consuming, so if you pick a few hundred days, expect a few minutes for loading time.

\n", - "\"\"\"\n", - " middle_html = \"\"\"\n", - "Here we allow the user to query for a single stock. This will work with the loaded data. Note that if you want to reload the data, this will once again take some time.\n", - " \"\"\"\n", - " return [\n", - " widgets.HTML(\n", - " title_html, layout=widgets.Layout(margin=\"0 0 3em 0\", max_width=\"800px\")\n", - " ),\n", - " self.slider_box,\n", - " self.button_box,\n", - " self.output1,\n", - " widgets.HTML(\n", - " middle_html,\n", - " layout=widgets.Layout(margin=\"0 0 3em 0\", max_width=\"800px\"),\n", - " ),\n", - " self.stock_input,\n", - " self.ticker_button,\n", - " self.output2,\n", - " ]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "jupyter": { - "outputs_hidden": false - }, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "dash = ShortDataDash()\n", - "app = widgets.VBox(\n", - " dash.build_app(), layout=widgets.Layout(max_width=\"1024px\", margin=\"0 auto 0 auto\")\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "jupyter": { - "outputs_hidden": false - }, - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "display(app)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - }, - "vscode": { - "interpreter": { - "hash": "381a209f752e9104bff95ec5e80679ea2e476866b80e4326f1ee6b7658af6049" - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/openbb_terminal/dashboards/voila/stocks.ipynb b/openbb_terminal/dashboards/voila/stocks.ipynb deleted file mode 100644 index 8a48f8bdd44..00000000000 --- a/openbb_terminal/dashboards/voila/stocks.ipynb +++ /dev/null @@ -1,406 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e9ad60bc", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from datetime import datetime, timedelta\n", - "import ipywidgets as widgets\n", - "import yfinance as yf\n", - "import pandas as pd\n", - "from IPython.display import display\n", - "\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "\n", - "theme.apply_style(\"light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "beb36881", - "metadata": {}, - "outputs": [], - "source": [ - "interval_opts = [\n", - " \"1m\",\n", - " \"2m\",\n", - " \"5m\",\n", - " \"15m\",\n", - " \"30m\",\n", - " \"60m\",\n", - " \"90m\",\n", - " \"1h\",\n", - " \"1d\",\n", - " \"5d\",\n", - " \"1wk\",\n", - " \"1mo\",\n", - " \"3mo\",\n", - "]\n", - "\n", - "rows = [\n", - " \"sector\",\n", - " \"market_cap\",\n", - " \"beta\",\n", - " \"year_high\",\n", - " \"year_low\",\n", - " \"floatShares\",\n", - " \"sharesShort\",\n", - " \"exDividendDate\",\n", - "]\n", - "\n", - "views = {\n", - " \"Raw Data\": lambda x, y: x,\n", - " \"Percent Change\": lambda x, y: x.pct_change(),\n", - " \"Rolling Average\": lambda x, y: x.rolling(y).mean(),\n", - " \"Rolling Variance\": lambda x, y: x.rolling(y).var(),\n", - " \"Rolling Standard Deviation\": lambda x, y: x.rolling(y).var() ** 0.5,\n", - " \"Rolling Coefficient of Variation\": lambda x, y: (x.rolling(y).var() ** 0.5)\n", - " / (x.rolling(y).mean()),\n", - "}\n", - "\n", - "clean_row = {\n", - " \"sector\": \"Sector\",\n", - " \"market_cap\": \"M Cap\",\n", - " \"beta\": \"Beta\",\n", - " \"year_high\": \"52W High\",\n", - " \"year_low\": \"52W Low\",\n", - " \"floatShares\": \"Floats\",\n", - " \"sharesShort\": \"Shorts\",\n", - " \"exDividendDate\": \"Ex-Div\",\n", - "}\n", - "\n", - "clean_data = {\n", - " \"sector\": lambda x: \"N/A\" if x is None else x,\n", - " \"market_cap\": lambda x: \"N/A\" if x is None else big_num(x),\n", - " \"beta\": lambda x: \"N/A\" if x is None else f\"{round(x,2)}\",\n", - " \"year_high\": lambda x: \"N/A\" if x is None else f\"${round(x,2)}\",\n", - " \"year_low\": lambda x: \"N/A\" if x is None else f\"${round(x,2)}\",\n", - " \"floatShares\": lambda x: \"N/A\" if x is None else big_num(x),\n", - " \"sharesShort\": lambda x: \"N/A\" if x is None else big_num(x),\n", - " \"exDividendDate\": lambda x: \"N/A\"\n", - " if x is None\n", - " else datetime.fromtimestamp(x).strftime(\"%Y/%m/%d\"),\n", - "}\n", - "\n", - "\n", - "def big_num(num):\n", - " if num > 1_000_000_000_000:\n", - " return f\"{round(num/1_000_000_000_000,2)}T\"\n", - " if num > 1_000_000_000:\n", - " return f\"{round(num/1_000_000_000,2)}B\"\n", - " if num > 1_000_000:\n", - " return f\"{round(num/1_000_000,2)}M\"\n", - " if num > 1_000:\n", - " return f\"{num/round(1_000,2)}K\"\n", - " return f\"{round(num,2)}\"\n", - "\n", - "\n", - "def clean_str(string):\n", - " new_str = \"\"\n", - " for letter in string:\n", - " if letter.isupper():\n", - " new_str += \" \"\n", - " new_str += letter\n", - " return new_str.title()\n", - "\n", - "\n", - "def format_plotly(fig: OpenBBFigure, data, start, end, chart, calc=None):\n", - " fig.update_yaxes(title=None)\n", - " fig.update_xaxes(title=None)\n", - " start_t = start.strftime(\"%Y/%m/%d\")\n", - " end_t = end.strftime(\"%Y/%m/%d\")\n", - " if calc:\n", - " if len(calc) == 1:\n", - " fig_title = f\"{calc[0]} of {data} from {start_t} to {end_t}\"\n", - " else:\n", - " fig_title = f\"{', '.join(calc)} of {data} from {start_t} to {end_t}\"\n", - " else:\n", - " fig_title = \"Volume\"\n", - " height = 500 if chart == \"main\" else 300\n", - " fig.update_layout(\n", - " autosize=False,\n", - " width=900,\n", - " height=height,\n", - " legend=dict(orientation=\"h\"),\n", - " title=dict(\n", - " text=fig_title,\n", - " y=0.95,\n", - " x=0.5,\n", - " xanchor=\"center\",\n", - " yanchor=\"top\",\n", - " ),\n", - " )\n", - "\n", - "\n", - "def create_line(visual, x, y, name, data, fig: OpenBBFigure):\n", - " if visual == \"line\":\n", - " fig.add_scatter(x=x, y=y[data], mode=\"lines\", name=name, connectgaps=True)\n", - " if visual == \"scatter\":\n", - " fig.add_scatter(x=x, y=y[data], mode=\"markers\", name=name)\n", - " if visual == \"candle\":\n", - " fig.add_candlestick(\n", - " x=x,\n", - " open=y[\"Open\"],\n", - " close=y[\"Close\"],\n", - " high=y[\"High\"],\n", - " low=y[\"Low\"],\n", - " name=name,\n", - " )\n", - "\n", - "\n", - "def show_fig(fig: OpenBBFigure, margin: bool = True):\n", - " config = {\"showTips\": False, \"scrollZoom\": True, \"displaylogo\": False}\n", - " if os.environ.get(\"SERVER_SOFTWARE\", \"jupyter\").startswith(\"voila\"):\n", - " fig.show(config=config, renderer=\"notebook\", margin=margin)\n", - " else:\n", - " fig.show(config=config, margin=margin)\n", - "\n", - "\n", - "def table_data(infos):\n", - " cols = [\"Ticker\"] + list(infos)\n", - " data = pd.DataFrame(columns=cols)\n", - " data[\"Ticker\"] = [clean_row[x] for x in rows]\n", - " for ticker in list(infos):\n", - " data[ticker] = [\n", - " clean_data[x](infos[ticker].get(x, None))\n", - " if infos[ticker].get(x, None)\n", - " else clean_data[x](getattr(yf.Ticker(ticker).fast_info, x, None))\n", - " for x in rows\n", - " ]\n", - " new_cols = {k: clean_str(k) for k in rows}\n", - " return data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8c2f65c", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "class Chart:\n", - " def __init__(self):\n", - " self.last_tickers = \"\"\n", - " self.last_interval = \"1d\"\n", - " self.df = pd.DataFrame()\n", - " self.infos = {}\n", - "\n", - " def create_stock(\n", - " self, calculation, data, rolling, start, end, interval, tickers, chart\n", - " ):\n", - " if tickers and tickers[-1] == \",\":\n", - " if tickers != self.last_tickers or interval != self.last_interval:\n", - " if interval in [\"1d\", \"5d\", \"1wk\", \"1mo\", \"3mo\"]:\n", - " self.df = yf.download(\n", - " tickers, period=\"max\", interval=interval, progress=False\n", - " )\n", - " else:\n", - " end_date = end + timedelta(days=1)\n", - " self.df = yf.download(\n", - " tickers,\n", - " start=start,\n", - " end=end_date,\n", - " interval=interval,\n", - " progress=False,\n", - " )\n", - " if not self.df.empty:\n", - " self.df.index = self.df.index.tz_localize(None)\n", - " self.last_tickers = tickers\n", - " self.last_interval = interval\n", - "\n", - " start_n = datetime(start.year, start.month, start.day)\n", - " end_n = datetime(end.year, end.month, end.day)\n", - " fig = OpenBBFigure()\n", - " for item in calculation:\n", - " calcs = views[item](self.df, rolling)\n", - " if interval in [\"1d\", \"5d\", \"1wk\", \"1mo\", \"3mo\"]:\n", - " result = calcs.loc[\n", - " (calcs.index >= start_n) & (calcs.index <= end_n)\n", - " ]\n", - " else:\n", - " result = calcs\n", - "\n", - " if len(result.columns) == 6:\n", - " name = f\"{tickers.split(',')[0]} {item}\"\n", - " create_line(chart, result.index, result, name, data, fig)\n", - "\n", - " else:\n", - " for val in result.columns.levels[1]:\n", - " vals = result.xs(val, axis=1, level=1, drop_level=True)\n", - " name = f\"{val.upper()} {item}\"\n", - " create_line(chart, result.index, vals, name, data, fig)\n", - "\n", - " format_plotly(fig, data, start, end, \"main\", calculation)\n", - " show_fig(fig)\n", - "\n", - " def create_volume(self, start, end, interval, tickers):\n", - " start_n = datetime(start.year, start.month, start.day)\n", - " end_n = datetime(end.year, end.month, end.day)\n", - " result = self.df.loc[(self.df.index >= start_n) & (self.df.index <= end_n)]\n", - " fig = OpenBBFigure()\n", - " if len(result.columns) == 6:\n", - " name = f\"{tickers.split(',')[0]}\"\n", - " create_line(\"line\", result.index, result, name, \"Volume\", fig)\n", - " else:\n", - " for val in result.columns.levels[1]:\n", - " vals = result.xs(val, axis=1, level=1, drop_level=True)\n", - " name = f\"{val.upper()}\"\n", - " create_line(\"line\", result.index, vals, name, \"Volume\", fig)\n", - " format_plotly(fig, \"Volume\", start, end, \"volume\")\n", - " show_fig(fig)\n", - "\n", - " def create_table(self, tickers):\n", - " ticker_list = tickers.split(\",\")\n", - " if ticker_list:\n", - " clean_tickers = [x for x in tickers.split(\",\") if x]\n", - " for ticker in clean_tickers:\n", - " if ticker not in self.infos:\n", - " try:\n", - " self.infos[ticker] = yf.Ticker(ticker).info\n", - " except Exception:\n", - " self.infos[ticker] = {}\n", - " pass\n", - " delete = [ticker for ticker in self.infos if ticker not in tickers]\n", - " for ticker in delete:\n", - " self.infos.pop(ticker)\n", - " result = table_data(self.infos)\n", - " fig = OpenBBFigure.to_table(result, print_index=False)\n", - " fig.update_layout(margin=dict(l=0, r=0, t=0, b=0), width=350)\n", - " show_fig(fig, margin=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e0d4de31-1b4f-4c1f-8467-ca314a578d56", - "metadata": {}, - "outputs": [], - "source": [ - "w_auto = widgets.Layout(width=\"auto\")\n", - "calc_widget = widgets.SelectMultiple(\n", - " options=list(views.keys()), value=[\"Raw Data\"], layout=w_auto\n", - ")\n", - "\n", - "data_opts = [\"Open\", \"Close\", \"High\", \"Low\"]\n", - "data_widget = widgets.Dropdown(\n", - " options=data_opts, value=\"Close\", layout=w_auto, description=\"Data\"\n", - ")\n", - "rolling_widget = widgets.Dropdown(\n", - " options=list(range(2, 101)), value=60, layout=w_auto, description=\"Rolling\"\n", - ")\n", - "\n", - "base_date = (datetime.today() - timedelta(days=365)).date()\n", - "start_widget = widgets.DatePicker(value=base_date, layout=w_auto, description=\"Start\")\n", - "end_widget = widgets.DatePicker(\n", - " value=datetime.today().date(), layout=w_auto, description=\"End\"\n", - ")\n", - "\n", - "interval_widget = widgets.Dropdown(\n", - " options=interval_opts, value=\"1d\", layout=w_auto, description=\"Interval\"\n", - ")\n", - "tickers_widget = widgets.Textarea(\n", - " value=\"TSLA,\", layout=widgets.Layout(width=\"auto\", height=\"100%\")\n", - ")\n", - "chart_opts = [\"line\", \"scatter\", \"candle\"]\n", - "chart_widget = widgets.Dropdown(\n", - " options=chart_opts, value=\"line\", layout=w_auto, description=\"Chart\"\n", - ")\n", - "data_box = widgets.VBox([data_widget, rolling_widget, chart_widget])\n", - "date_box = widgets.VBox([start_widget, end_widget, interval_widget])\n", - "controls = widgets.HBox(\n", - " [tickers_widget, calc_widget, date_box, data_box],\n", - " layout=widgets.Layout(width=\"90%\"),\n", - ")\n", - "chart = Chart()\n", - "stocks_view = widgets.interactive_output(\n", - " chart.create_stock,\n", - " {\n", - " \"calculation\": calc_widget,\n", - " \"data\": data_widget,\n", - " \"rolling\": rolling_widget,\n", - " \"start\": start_widget,\n", - " \"end\": end_widget,\n", - " \"interval\": interval_widget,\n", - " \"tickers\": tickers_widget,\n", - " \"chart\": chart_widget,\n", - " },\n", - ")\n", - "\n", - "volume_view = widgets.interactive_output(\n", - " chart.create_volume,\n", - " {\n", - " \"start\": start_widget,\n", - " \"end\": end_widget,\n", - " \"interval\": interval_widget,\n", - " \"tickers\": tickers_widget,\n", - " },\n", - ")\n", - "\n", - "table_view = widgets.interactive_output(chart.create_table, {\"tickers\": tickers_widget})\n", - "\n", - "charts = widgets.VBox(\n", - " [stocks_view, volume_view],\n", - " layout=widgets.Layout(width=\"100%\", padding=\"0\", margin=\"0\"),\n", - ")\n", - "figures = widgets.HBox(\n", - " [charts, table_view], layout=widgets.Layout(padding=\"0\", margin=\"0\")\n", - ")\n", - "\n", - "title_html = \"

Stock Analysis Dashboard

\"\n", - "warning_html = '

Use a comma after EVERY stock typed.

'\n", - "\n", - "app_contents = [widgets.HTML(title_html), controls, widgets.HTML(warning_html), figures]\n", - "app = widgets.VBox(app_contents)\n", - "display(app)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f41a16d", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - }, - "metadata": { - "interpreter": { - "hash": "e896a00ead8b521528d79ac9ef24990696f2b751eb283ab8e0d078c9c4971ffc" - } - }, - "vscode": { - "interpreter": { - "hash": "381a209f752e9104bff95ec5e80679ea2e476866b80e4326f1ee6b7658af6049" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/decorators.py b/openbb_terminal/decorators.py deleted file mode 100644 index 49b000e0e20..00000000000 --- a/openbb_terminal/decorators.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Decorators""" - -__docformat__ = "numpy" -import functools -import logging -from ssl import SSLError - -import pandas as pd -from requests.exceptions import RequestException - -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.core.session.current_user import ( - get_current_user, - set_current_user, -) -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def log_start_end(func=None, log=None): - """Wrap function to add a log entry at execution start and end. - - Parameters - ---------- - func : optional - Function, by default None - log : optional - Logger, by default None - - Returns - ------- - Wrapped function - """ - assert callable(func) or func is None # noqa: S101 - - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - logging_name = "" - - args_passed_in_function = [ - repr(a) for a in args if isinstance(a, (pd.DataFrame, pd.Series)) or a - ] - - if ( - len(args) == 2 - and args_passed_in_function - and ( - "__main__.TerminalController" in args_passed_in_function[0] - or ( - "openbb_terminal." in args_passed_in_function[0] - and "_controller" in args_passed_in_function[0] - ) - ) - ): - logging_name = args_passed_in_function[0].split()[0][1:] - args_passed_in_function = args_passed_in_function[1:] - - logger_used = logging.getLogger(logging_name) if logging_name else log - - logger_used.info( - "START", - extra={"func_name_override": func.__name__}, - ) - - if get_current_system().DEBUG_MODE: - value = func(*args, **kwargs) - log.info("END", extra={"func_name_override": func.__name__}) - return value - try: - value = func(*args, **kwargs) - logger_used.info("END", extra={"func_name_override": func.__name__}) - return value - except KeyboardInterrupt: - logger_used.info( - "Interrupted by user", - extra={"func_name_override": func.__name__}, - ) - return [] - except RequestException as e: - console.print( - "[red]There was an error connecting to the API." - " Please try again later.\n[/red]" - ) - logger_used.exception( - "Exception: %s", - str(e), - extra={"func_name_override": func.__name__}, - ) - return [] - except SSLError as e: - console.print( - "[red]There was an error connecting to the API." - " Please check whether your wifi is blocking this site.\n[/red]" - ) - logger_used.exception( - "Exception: %s", - str(e), - extra={"func_name_override": func.__name__}, - ) - return [] - except Exception as e: - console.print(f"[red]Error: {e}\n[/red]") - logger_used.exception( - "Exception: %s", - str(e), - extra={"func_name_override": func.__name__}, - ) - return [] - - return wrapper - - return decorator(func) if callable(func) else decorator - - -# pylint: disable=import-outside-toplevel -def check_api_key(api_keys): - """ - Wrapper around the view or controller function and - print message statement to the console if API keys are not yet defined. - """ - - def decorator(func): - @functools.wraps(func) - def wrapper_decorator(*args, **kwargs): - if get_current_user().preferences.ENABLE_CHECK_API: - current_user = get_current_user() - undefined_apis = [] - for key in api_keys: - # Get value of the API Keys - if ( - getattr(current_user.credentials, key) == "REPLACE_ME" - ) and key not in ["API_KEY_ALPHAVANTAGE"]: - undefined_apis.append(key) - - if undefined_apis: - undefined_apis_name = ", ".join(undefined_apis) - console.print( - f"[red]{undefined_apis_name} not defined. " - "Set the API key under keys menu.[/red]" - ) # pragma: allowlist secret - return None - return func(*args, **kwargs) - - return wrapper_decorator - - return decorator - - -def disable_check_api(): - current_user = get_current_user() - current_user.preferences.ENABLE_CHECK_API = False - set_current_user(current_user) diff --git a/openbb_terminal/econometrics/__init__.py b/openbb_terminal/econometrics/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/econometrics/econometrics_controller.py b/openbb_terminal/econometrics/econometrics_controller.py deleted file mode 100644 index c8c2cf30b12..00000000000 --- a/openbb_terminal/econometrics/econometrics_controller.py +++ /dev/null @@ -1,2203 +0,0 @@ -"""Econometrics Controller Module""" - -__docformat__ = "numpy" - -# pylint: disable=too-many-arguments,too-many-lines,too-many-branches,inconsistent-return-statements,R0904 - -import argparse -import logging -import os -from itertools import chain -from pathlib import Path -from typing import Any, Dict, List, Optional - -import numpy as np -import pandas as pd - -from openbb_terminal.common import common_model -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.econometrics import ( - econometrics_helpers, - econometrics_model, - econometrics_view, - regression_model, - regression_view, -) -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - NO_EXPORT, - check_list_values, - check_positive, - check_positive_float, - export_data, - print_rich_table, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - -# pylint: disable=R0902,C0302 - - -class EconometricsController(BaseController): - """Econometrics class""" - - CHOICES_COMMANDS: List[str] = [ - "load", - "export", - "remove", - "plot", - "show", - "type", - "desc", - "corr", - "index", - "clean", - "add", - "eval", - "delete", - "combine", - "rename", - "lag", - "ret", - "ols", - "norm", - "root", - "panel", - "compare", - "dwat", - "bgod", - "bpag", - "garch", - "granger", - "coint", - "vif", - ] - CHOICES_MENUS: List[str] = [ - "qa", - ] - pandas_plot_choices = [ - "line", - "scatter", - "bar", - "barh", - "hist", - "box", - "kde", - "area", - "pie", - "hexbin", - ] - - PANEL_CHOICES = [ - "pols", - "re", - "bols", - "fe", - "fdols", - "POLS", - "RE", - "BOLS", - "FE", - "FDOLS", - ] - PATH = "/econometrics/" - - loaded_dataset_cols = "\n" - list_dataset_cols: List = list() - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - self.files: List[str] = list() - self.datasets: Dict[str, pd.DataFrame] = dict() - self.regression: Dict[Any[Dict, Any], Any] = dict() - - self.DATA_TYPES: List[str] = ["int", "float", "str", "bool", "category", "date"] - - current_user = get_current_user() - - for regression in [ - "OLS", - "POLS", - "BOLS", - "RE", - "FE", - "FE_EE", - "FE_IE", - "FE_EE_IE", - "FDOLS", - ]: - self.regression[regression] = { - "data": {}, - "independent": {}, - "dependent": {}, - "model": {}, - } - - self.signs: Dict[Any, Any] = { - "div": "/", - "mul": "*", - "add": "+", - "sub": "-", - "mod": "%", - "pow": "**", - } - self.DATA_FILES = { - filepath.name: filepath - for file_type in common_model.file_types - for filepath in chain( - Path(current_user.preferences.USER_EXPORTS_DIRECTORY).rglob( - f"*.{file_type}" - ), - Path( - current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY - / "econometrics" - ).rglob(f"*.{file_type}"), - ) - if filepath.is_file() - } - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - choices["load"] = { - "--file": {c: {} for c in self.DATA_FILES}, - "-f": "--file", - "-alias": None, - "-a": "-alias", - "--examples": None, - "-e": "--examples", - "--sheet-name": None, - } - - for feature in ["export", "show", "desc", "clear", "index"]: - choices[feature] = {c: {} for c in self.files} - - for feature in [ - "type", - "plot", - "norm", - "root", - "garch", - "granger", - "coint", - "corr", - "lag", - "vif", - "panel", - ]: - choices[feature] = dict() - - # Initialize this for regressions to be able to use -h flag - choices["regressions"] = {} - self.choices = choices - - choices["support"] = self.SUPPORT_CHOICES - choices["about"] = self.ABOUT_CHOICES - choices["panel"]["-r"] = {c: {} for c in self.PANEL_CHOICES} - self.completer = NestedCompleter.from_nested_dict(choices) - else: - self.choices = {} - - def update_runtime_choices(self): - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - dataset_columns = { - f"{dataset}.{column}": {} - for dataset, dataframe in self.datasets.items() - for column in dataframe.columns - } - - for feature in [ - "plot", - "norm", - "root", - "coint", - "lag", - "regressions", - "ols", - "panel", - "delete", - "garch", - ]: - self.choices[feature] = dataset_columns - for feature in [ - "export", - "show", - "clean", - "index", - "remove", - "combine", - "rename", - "corr", - ]: - self.choices[feature] = {c: {} for c in self.files} - - for feature in ["type", "desc", "vif"]: - self.choices[feature] = { - c: {} for c in self.files + list(dataset_columns.keys()) - } - self.choices["vif"] = dict( - self.choices["vif"], - **{"-d": self.choices["vif"], "--data": self.choices["vif"]}, - ) - - pairs_timeseries = list() - for dataset_col in list(dataset_columns.keys()): - pairs_timeseries += [ - f"{dataset_col},{dataset_col2}" - for dataset_col2 in list(dataset_columns.keys()) - if dataset_col != dataset_col2 - ] - - self.choices["granger"] = {c: {} for c in pairs_timeseries} - - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def print_help(self): - """Print help""" - current_user = get_current_user() - mt = MenuText("econometrics/") - mt.add_param( - "_data_loc", - f"\n\t{str(current_user.preferences.USER_EXPORTS_DIRECTORY)}\n" - f"\t{str(current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY/'econometrics')}", - ) - mt.add_raw("\n") - mt.add_cmd("load") - mt.add_cmd("remove", self.files) - mt.add_raw("\n") - mt.add_param("_loaded", self.loaded_dataset_cols) - - mt.add_info("_exploration_") - mt.add_cmd("show", self.files) - mt.add_cmd("plot", self.files) - mt.add_cmd("type", self.files) - mt.add_cmd("desc", self.files) - mt.add_cmd("corr", self.files) - mt.add_cmd("index", self.files) - mt.add_cmd("clean", self.files) - mt.add_cmd("add", self.files) - mt.add_cmd("eval", self.files) - mt.add_cmd("delete", self.files) - mt.add_cmd("combine", self.files) - mt.add_cmd("rename", self.files) - mt.add_cmd("lag", self.files) - mt.add_cmd("ret", self.files) - mt.add_cmd("export", self.files) - mt.add_info("_assumption_testing_") - mt.add_cmd("norm", self.files) - mt.add_cmd("granger", self.files) - mt.add_cmd("root", self.files) - mt.add_cmd("coint", self.files) - mt.add_cmd("vif", self.files) - mt.add_cmd("dwat", self.files and self.regression["OLS"]["model"]) - mt.add_cmd("bgod", self.files and self.regression["OLS"]["model"]) - mt.add_cmd("bpag", self.files and self.regression["OLS"]["model"]) - mt.add_info("_time_series_") - mt.add_cmd("ols", self.files) - mt.add_cmd("garch", self.files) - mt.add_info("_panel_") - mt.add_cmd("panel", self.files) - mt.add_cmd("compare", self.files) - - console.print(text=mt.menu_text, menu="Econometrics") - console.print() - - def custom_reset(self): - """Class specific component of reset command""" - return ["econometrics"] - - def update_loaded(self): - self.list_dataset_cols = [] - self.loaded_dataset_cols = "\n" - - if not self.files: - self.list_dataset_cols.append("") - return - - maxfile = max(len(file) for file in self.files) - - for dataset, data in self.datasets.items(): - dataset_columns = ", ".join(data.columns) - dataset_name = f"{dataset} {(maxfile - len(dataset)) * ' '}:" - self.loaded_dataset_cols += f"\t{dataset_name} {dataset_columns}\n" - self.list_dataset_cols.extend([f"{dataset}.{col}" for col in data.columns]) - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - """Process load""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Load dataset (from previous export, custom imports or StatsModels).", - ) - parser.add_argument( - "-f", - "--file", - help="File to load data in (can be custom import, " - "may have been exported before or can be from Statsmodels)", - type=str, - ) - parser.add_argument( - "-a", - "--alias", - help="Alias name to give to the dataset", - type=str, - ) - - parser.add_argument( - "-e", - "--examples", - help="Use this argument to show examples of Statsmodels to load in. " - "See: https://www.statsmodels.org/devel/datasets/index.html", - action="store_true", - default=False, - dest="examples", - ) - parser.add_argument( - "--sheet-name", - dest="sheet_name", - default=None, - nargs="+", - help="Name of excel sheet to save data to. Only valid for .xlsx files.", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-f") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - # show examples from statsmodels - if ns_parser.examples: - df = pd.DataFrame.from_dict(common_model.DATA_EXAMPLES, orient="index") - print_rich_table( - df, - headers=list(["description"]), - show_index=True, - index_name="file name", - title="Examples from Statsmodels", - ) - return - - if not ns_parser.file: - return - possible_data = list(common_model.DATA_EXAMPLES.keys()) + list( - self.DATA_FILES.keys() - ) - if ns_parser.file not in possible_data: - file = "" - # Try to see if the user is just missing the extension - for file_ext in list(self.DATA_FILES.keys()): - if file_ext.startswith(ns_parser.file): - # found the correct file - file = file_ext - break - - if not file: - console.print( - "[red]The file/dataset selected does not exist.[/red]" - ) - return - else: - file = ns_parser.file - - if ns_parser.alias: - alias = ns_parser.alias - else: - alias = ( - ".".join(ns_parser.file.split(".")[:-1]) - if "." in ns_parser.file - else ns_parser.file - ) - - # check if this dataset has been added already - if alias in self.files: - console.print( - "[red]The file/dataset selected has already been loaded.[/red]" - ) - return - - data = common_model.load( - file, - data_files=self.DATA_FILES, - data_examples=common_model.DATA_EXAMPLES, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - if not data.empty: - data.columns = data.columns.map(lambda x: x.lower().replace(" ", "_")) - self.files.append(alias) - self.datasets[alias] = data - self.update_runtime_choices() - self.update_loaded() - - @log_start_end(log=logger) - def call_export(self, other_args: List[str]): - """Process export command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="export", - description="Export dataset to Excel", - ) - - parser.add_argument( - "-n", - "--name", - dest="name", - help="The name of the dataset you wish to export", - type=str, - ) - - export_group = parser.add_mutually_exclusive_group(required=False) - export_group.add_argument( - "-f", - "--file", - dest="file", - help="The name of the file you wish to export to", - type=str, - ) - export_group.add_argument( - "-t", - "--type", - help="The file type you wish to export to", - dest="type", - choices=common_model.file_types, - type=str, - default="xlsx", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if not ns_parser.name or ns_parser.name not in self.datasets: - console.print("Please enter a valid dataset.") - else: - export_data( - ns_parser.file if ns_parser.file else ns_parser.type, - os.path.dirname(os.path.abspath(__file__)), - ns_parser.name, - self.datasets[ns_parser.name], - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - console.print() - - @log_start_end(log=logger) - def call_remove(self, other_args: List[str]): - """Process clear""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="remove", - description="Remove a dataset from the loaded dataset list", - ) - parser.add_argument( - "-n", - "--name", - help="The name of the dataset you want to remove", - dest="name", - type=str, - choices=list(self.datasets.keys()), - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if not ns_parser: - return - - if not ns_parser.name: - console.print("Please enter a valid dataset.\n") - return - - if ns_parser.name not in self.datasets: - console.print(f"[red]'{ns_parser.name}' is not a loaded dataset.[/red]\n") - return - - del self.datasets[ns_parser.name] - self.files.remove(ns_parser.name) - - self.update_runtime_choices() - - self.update_loaded() - - @log_start_end(log=logger) - def call_plot(self, other_args: List[str]): - """Process plot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="plot", - description="Plot data based on the index", - ) - parser.add_argument( - "-v", - "--values", - help="Dataset.column values to be displayed in a plot", - dest="values", - type=check_list_values(self.choices["plot"]), - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - - if ns_parser and ns_parser.values: - data: Dict = {} - for datasetcol in ns_parser.values: - dataset, col = datasetcol.split(".") - data[datasetcol] = self.datasets[dataset][col] - - econometrics_view.display_plot( - data, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_show(self, other_args: List[str]): - """Process show command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="show", - description="Show a portion of the DataFrame", - ) - - parser.add_argument( - "-n", - "--name", - type=str, - choices=self.files, - dest="name", - help="The name of the database you want to show data for", - ) - - parser.add_argument( - "-s", - "--sortby", - help="Sort based on a column in the DataFrame", - type=str, - dest="sortby", - default="", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10 - ) - - if ns_parser: - dataset_names = ( - list(self.datasets.keys()) if not ns_parser.name else [ns_parser.name] - ) - - for name in dataset_names: - df = self.datasets[name] - - if name in self.datasets and self.datasets[name].empty: - return console.print( - f"[red]No data available for {ns_parser.name}.[/red]\n" - ) - if ns_parser.sortby: - sort_column = ns_parser.sortby - if sort_column not in self.datasets[name].columns: - console.print( - f"[red]{sort_column} not a valid column. Showing without sorting.\n[/red]" - ) - else: - df = df.sort_values(by=sort_column, ascending=ns_parser.reverse) - - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=f"Dataset {name}", - export=bool(ns_parser.export), - limit=ns_parser.limit, - ) - - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - f"{ns_parser.name}_show", - df.head(ns_parser.limit), - ns_parser.sheet_name, - ) - - @log_start_end(log=logger) - def call_desc(self, other_args: List[str]): - """Process desc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="desc", - description="Show the descriptive statistics of the dataset", - ) - parser.add_argument( - "-n", - "--name", - type=str, - choices=self.choices.get("desc", []), - dest="name", - help="The name of the dataset.column you want to show the descriptive statistics", - required="-h" not in other_args, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if "." in ns_parser.name: - dataset, col = ns_parser.name.split(".") - - df = self.datasets[dataset][col].describe() - print_rich_table( - df.to_frame(), - headers=[col], - show_index=True, - title=f"Statistics for dataset: '{dataset}'", - export=bool(ns_parser.export), - ) - - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - f"{dataset}_{col}_desc", - df, - ns_parser.sheet_name, - ) - else: - df = self.datasets[ns_parser.name] - if not df.empty: - df = df.describe() - print_rich_table( - df, - headers=self.datasets[ns_parser.name].columns, - show_index=True, - title=f"Statistics for dataset: '{ns_parser.name}'", - export=bool(ns_parser.export), - ) - - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - f"{ns_parser.name}_desc", - df, - ns_parser.sheet_name, - ) - else: - console.print("Empty dataset") - - @log_start_end(log=logger) - def call_corr(self, other_args: List[str]): - """Process correlation command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="corr", - description="Plot correlation coefficients.", - ) - parser.add_argument( - "-d", - "--dataset", - help="The name of the dataset you want to select", - dest="target_dataset", - type=str, - choices=list(self.datasets.keys()), - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - # check proper file name is provided - if not ns_parser.target_dataset: - console.print("[red]Please enter valid dataset.\n[/red]") - return - - data = self.datasets[ns_parser.target_dataset] - - econometrics_view.display_corr( - data, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_type(self, other_args: List[str]): - """Process type command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="type", - description="Show the type of the columns of the dataset and/or change the type of the column", - ) - parser.add_argument( - "-n", - "--name", - type=str, - dest="name", - help="Provide dataset.column series to change type or dataset to see types.", - choices=self.choices.get("type", []), - ) - parser.add_argument( - "--format", - type=str, - choices=self.DATA_TYPES, - dest="format", - help=( - "Set the format for the dataset.column defined. This can be: " - "date, int, float, str, bool or category" - ), - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - if ns_parser.name: - if "." in ns_parser.name: - dataset, column = ns_parser.name.split(".") - if ns_parser.format: - if ns_parser.format == "date": - self.datasets[dataset][column] = pd.to_datetime( - self.datasets[dataset][column].values, - ) - else: - self.datasets[dataset][column] = self.datasets[dataset][ - column - ].astype(ns_parser.format) - - console.print( - f"Update '{ns_parser.name}' with type '{ns_parser.format}'" - ) - else: - console.print( - f"The type of '{ns_parser.name}' is '{self.datasets[dataset][column].dtypes}'" - ) - - else: - print_rich_table( - pd.DataFrame(self.datasets[ns_parser.name].dtypes), - headers=list(["dtype"]), - show_index=True, - index_name="column", - title=str(ns_parser.name), - ) - else: - for dataset_name, data in self.datasets.items(): - print_rich_table( - pd.DataFrame(data.dtypes), - headers=list(["dtype"]), - show_index=True, - index_name="column", - title=str(dataset_name), - ) - - console.print() - - @log_start_end(log=logger) - def call_index(self, other_args: List[str]): - """Process index command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="index", - description="Set a (multi) index for the dataset", - ) - parser.add_argument( - "-n", - "--name", - type=str, - dest="name", - choices=list(self.datasets.keys()), - help="Name of dataset to select index from", - required="-h" not in other_args, - ) - parser.add_argument( - "-i", - "--index", - type=str, - dest="index", - help="Columns from the dataset the user wishes to set as default", - default="", - ) - parser.add_argument( - "-a", - "--adjustment", - help="Whether to allow for making adjustments to the dataset to align it with the use case for " - "Timeseries and Panel Data regressions", - dest="adjustment", - action="store_true", - default=False, - ) - parser.add_argument( - "-d", - "--drop", - help="Whether to drop the column(s) the index is set for.", - dest="drop", - action="store_true", - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - name = ns_parser.name - index = ns_parser.index - - if index: - values_found = ( - [val.strip() for val in index.split(",")] - if "," in index - else [index] - ) - - columns = list() - for value in values_found: - # check if the value is valid - if value in self.datasets[name].columns: - columns.append(value) - else: - console.print(f"[red]'{value}' is not valid.[/red]") - - dataset = self.datasets[name] - - if not pd.Index(np.arange(0, len(dataset))).equals(dataset.index): - console.print( - "As an index has been set, resetting the current index." - ) - if dataset.index.name in dataset.columns: - dataset = dataset.reset_index(drop=True) - else: - dataset = dataset.reset_index(drop=False) - - for column in columns: - if column not in dataset.columns: - console.print( - f"[red]The column '{column}' is not available in the dataset {name}." - f"Please choose one of the following: {', '.join(dataset.columns)}[/red]" - ) - return - - if ns_parser.adjustment: - if len(columns) > 1 and dataset[columns[0]].isnull().any(): - null_values = dataset[dataset[columns[0]].isnull()] - console.print( - f"The column '{columns[0]}' contains {len(null_values)} NaN " - "values. As multiple columns are provided, it is assumed this " - "column represents entities (i), the NaN values are forward " - "filled. Remove the -a argument to disable this." - ) - dataset[columns[0]] = dataset[columns[0]].fillna(method="ffill") - if dataset[columns[-1]].isnull().any(): - # This checks whether NaT (missing values) exists within the DataFrame - null_values = dataset[dataset[columns[-1]].isnull()] - console.print( - f"The time index '{columns[-1]}' contains {len(null_values)} " - "NaNs which are removed from the dataset. Remove the -a argument to disable this." - ) - dataset = dataset[dataset[columns[-1]].notnull()] - - self.datasets[name] = dataset.set_index(columns, drop=ns_parser.drop) - console.print( - f"Successfully updated '{name}' index to be '{', '.join(columns)}'\n" - ) - - self.update_runtime_choices() - else: - print_rich_table( - self.datasets[name].head(3), - headers=list(self.datasets[name].columns), - show_index=True, - title=f"Dataset '{name}'", - ) - - @log_start_end(log=logger) - def call_clean(self, other_args: List[str]): - """Process clean command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="clean", - description="Clean a dataset by filling and dropping NaN values.", - ) - parser.add_argument( - "-n", - "--name", - help="The name of the dataset you want to clean up", - dest="name", - type=str, - choices=list(self.datasets.keys()), - ) - parser.add_argument( - "--fill", - help="The method of filling NaNs. This has options to fill rows (rfill, rbfill, rffill) or fill " - "columns (cfill, cbfill, cffill). Furthermore, it has the option to forward fill and backward fill " - "(up to --limit) which refer to how many rows/columns can be set equal to the last non-NaN value", - dest="fill", - choices=["rfill", "cfill", "rbfill", "cbfill", "rffill", "bffill"], - default="", - ) - parser.add_argument( - "-d", - "--drop", - help="The method of dropping NaNs. This either has the option rdrop (drop rows that contain NaNs) " - "or cdrop (drop columns that contain NaNs)", - dest="drop", - choices=["rdrop", "cdrop"], - default="", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, NO_EXPORT, limit=5 - ) - if ns_parser: - self.datasets[ns_parser.name] = econometrics_model.clean( - self.datasets[ns_parser.name], - ns_parser.fill, - ns_parser.drop, - ns_parser.limit, - ) - console.print(f"Successfully cleaned '{ns_parser.name}' dataset") - console.print() - - @log_start_end(log=logger) - def call_add(self, other_args: List[str]): - """Process add command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="add", - description="Add columns to your dataframe with the option to use formulas. E.g." - " newdatasetcol = basedatasetcol sign criteriaordatasetcol" - " thesis.high_revenue = thesis.revenue > 1000" - " dataset.debt_ratio = dataset.debt div dataset2.assets", - ) - parser.add_argument( - "-n", - "--newdatasetcol", - help="New dataset column to be added with format: dataset.column", - dest="newdatasetcol", - type=str, - required="-h" not in other_args, - ) - parser.add_argument( - "-b", - "--basedatasetcol", - help="Base dataset column to be used as base with format: dataset.column", - dest="basedatasetcol", - type=str, - required="-h" not in other_args, - ) - parser.add_argument( - "-s", - "--sign", - help="Sign to be applied to the base dataset column", - dest="sign", - choices=list(self.signs.keys()) + [">", "<", ">=", "<=", "=="], - required="-h" not in other_args, - type=str, - ) - parser.add_argument( - "-c", - "--criteriaordatasetcol", - help="Either dataset column to be applied on top of base dataset or criteria", - dest="criteriaordatasetcol", - required="-h" not in other_args, - type=str, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - dataset, new_column = ns_parser.newdatasetcol.split(".") - dataset2, existing_column = ns_parser.basedatasetcol.split(".") - - for sign, operator in self.signs.items(): - if sign == ns_parser.sign: - ns_parser.sign = operator - - if dataset not in self.datasets: - console.print( - f"Not able to find the dataset {dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - elif dataset2 not in self.datasets: - console.print( - f"Not able to find the dataset {dataset2}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - elif existing_column not in self.datasets[dataset2]: - console.print( - f"Not able to find the column {existing_column}. Please choose one of " - f"the following: {', '.join(self.datasets[dataset2].columns)}" - ) - elif len(ns_parser.criteriaordatasetcol.split(".")) > 1: - dataset3, existing_column2 = ns_parser.criteriaordatasetcol.split(".") - - if dataset3 not in self.datasets: - console.print( - f"Not able to find the dataset {dataset3}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - - elif existing_column2 not in self.datasets[dataset3]: - console.print( - f"Not able to find the column {existing_column2}. Please choose one of " - f"the following: {', '.join(self.datasets[dataset3].columns)}" - ) - else: - pd.eval( - f"{new_column} = self.datasets[dataset2][existing_column] " - f"{ns_parser.sign} self.datasets[dataset3][existing_column2]", - target=self.datasets[dataset], - inplace=True, - ) - else: - pd.eval( - f"{new_column} = self.datasets[dataset2][existing_column] " - f"{ns_parser.sign} {str(ns_parser.criteriaordatasetcol)}", - target=self.datasets[dataset], - inplace=True, - ) - - self.update_runtime_choices() - self.update_loaded() - console.print() - - @log_start_end(log=logger) - def call_lag(self, other_args: List[str]): - """Process lag command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lag", - description="Add lag to a variable by shifting a column.", - ) - parser.add_argument( - "-v", - "--values", - help="Dataset.column values to add lag to.", - dest="values", - choices={ - f"{dataset}.{column}": {column: None, dataset: None} - for dataset, dataframe in self.datasets.items() - for column in dataframe.columns - }, - type=str, - required="-h" not in other_args, - ) - parser.add_argument( - "-l", - "--lags", - action="store", - dest="lags", - type=check_positive, - default=5, - help="How many periods to lag the selected column.", - required="-h" not in other_args, - ) - parser.add_argument( - "-f", - "--fill-value", - action="store", - dest="fill_value", - help="The value used for filling the newly introduced missing values.", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=NO_EXPORT - ) - - if not ns_parser: - return - - try: - dataset, col = ns_parser.values.split(".") - data = self.datasets[dataset] - except ValueError: - console.print("[red]Please enter 'dataset'.'column'.[/red]\n") - return - - data[col + "_with_" + str(ns_parser.lags) + "_lags"] = data[col].shift( - ns_parser.lags, fill_value=ns_parser.fill_value - ) - self.datasets[dataset] = data - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_ret(self, other_args: List[str]): - """Process ret command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ret", - description="Calculate returns for the given column.", - ) - parser.add_argument( - "-v", - "--values", - help="Dataset.column values to calculate returns.", - dest="values", - choices={ - f"{dataset}.{column}": {column: None, dataset: None} - for dataset, dataframe in self.datasets.items() - for column in dataframe.columns - }, - type=str, - required="-h" not in other_args, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=NO_EXPORT - ) - - if not ns_parser: - return - - try: - dataset, col = ns_parser.values.split(".") - data = self.datasets[dataset] - except ValueError: - console.print("[red]Please enter 'dataset'.'column'.[/red]\n") - return - - data[col + "_returns"] = econometrics_model.get_returns(data[col]) - self.datasets[dataset] = data - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_eval(self, other_args): - """Process eval command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="eval", - description="""Create custom data column from loaded datasets. Can be mathematical expressions supported - by pandas.eval() function. - - Example. If I have loaded `fred DGS2,DGS5` and I want to create a new column that is the difference - between these two, I can create a new column by doing `eval spread = DGS2 - DGS5`. - Notice that the command is case sensitive, i.e., `DGS2` is not the same as `dgs2`. - """, - ) - parser.add_argument( - "-q", - "--query", - type=str, - nargs="+", - dest="query", - required="-h" not in other_args, - help="Query to evaluate on loaded datasets", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-q") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - self.datasets = econometrics_helpers.create_new_entry( - self.datasets, " ".join(ns_parser.query) - ) - self.update_runtime_choices() - self.update_loaded() - - @log_start_end(log=logger) - def call_delete(self, other_args: List[str]): - """Process delete command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="delete", - description="The column you want to delete from a dataset.", - ) - parser.add_argument( - "-d", - "--delete", - help="The columns you want to delete from a dataset. Use format: or" - " multiple with ,", - dest="delete", - type=check_list_values(self.choices.get("delete", [])), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - for option in ns_parser.delete: - dataset, column = option.split(".") - - if dataset not in self.datasets: - console.print( - f"Not able to find the dataset {dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - elif column not in self.datasets[dataset]: - console.print( - f"Not able to find the column {column}. Please choose one of " - f"the following: {', '.join(self.datasets[dataset].columns)}" - ) - else: - del self.datasets[dataset][column] - - self.update_runtime_choices() - console.print() - - @log_start_end(log=logger) - def call_combine(self, other_args: List[str]): - """Process combine command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="combine", - description="The columns you want to add to a dataset. The first argument is the dataset to add columns in" - "and the remaining could be: ,", - ) - parser.add_argument( - "-d", - "--dataset", - help="Dataset to add columns to", - dest="dataset", - choices=self.choices.get("combine", []), - ) - parser.add_argument( - "-c", - "--columns", - help="The columns we want to add ,", - dest="columns", - type=check_list_values(self.choices.get("delete", [])), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - if ns_parser.dataset not in self.datasets: - console.print( - f"Not able to find the dataset {ns_parser.dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - return - - data = self.datasets[ns_parser.dataset] - - for option in ns_parser.columns: - dataset, column = option.split(".") - - if dataset not in self.datasets: - console.print( - f"Not able to find the dataset {dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - elif column not in self.datasets[dataset]: - console.print( - f"Not able to find the column {column}. Please choose one of " - f"the following: {', '.join(self.datasets[dataset].columns)}" - ) - else: - data[f"{dataset}_{column}"] = self.datasets[dataset][column] - - self.update_runtime_choices() - - console.print() - - @log_start_end(log=logger) - def call_rename(self, other_args: List[str]): - """Process rename command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rename", - description="The column you want to rename from a dataset.", - ) - parser.add_argument( - "-d", - "--dataset", - help="Dataset that will get a column renamed", - dest="dataset", - choices=self.choices.get("rename", []), - type=str, - ) - parser.add_argument( - "-o", - "--oldcol", - help="Old column from dataset to be renamed", - dest="oldcol", - type=str, - required="-h" not in other_args, - ) - parser.add_argument( - "-n", - "--newcol", - help="New column from dataset to be renamed", - dest="newcol", - type=str, - required="-h" not in other_args, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - dataset = ns_parser.dataset - column_old = ns_parser.oldcol - column_new = ns_parser.newcol - - if dataset not in self.datasets: - console.print( - f"Not able to find the dataset {dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - elif column_old not in self.datasets[dataset]: - console.print( - f"Not able to find the column {column_old}. Please choose one of " - f"the following: {', '.join(self.datasets[dataset].columns)}" - ) - else: - self.datasets[dataset] = self.datasets[dataset].rename( - columns={column_old: column_new} - ) - - self.update_runtime_choices() - - console.print() - - @log_start_end(log=logger) - def call_ols(self, other_args: List[str]): - """Process ols command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ols", - description="Performs an OLS regression on timeseries data.", - ) - parser.add_argument( - "-d", - "--dependent", - type=str, - dest="dependent", - help="The dependent variable on the regression you would like to perform", - required="-h" not in other_args, - ) - parser.add_argument( - "-i", - "--independent", - type=check_list_values(self.choices.get("regressions", [])), - dest="independent", - help=( - "The independent variables on the regression you would like to perform. " - "E.g. historical.high,historical.low" - ), - required="-h" not in other_args, - ) - parser.add_argument( - "--no-output", - action="store_true", - default=False, - help="Hide the output of the regression", - dest="no_output", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if "," in ns_parser.dependent: - console.print( - "It appears you have selected multiple variables for the dependent variable. " - "Please select one.\nDid you intend to include these variables as independent " - f"variables? Use -i {ns_parser.dependent} in this case.\n" - ) - elif ns_parser.dependent in self.choices.get("regressions", []): - ( - regression_df, - dependent_variable, - independent_variables, - ) = regression_model.get_regression_data( - [ns_parser.dependent] + ns_parser.independent, - self.datasets, - "OLS", - ) - self.regression["OLS"]["data"] = regression_df - self.regression["OLS"]["dependent"] = dependent_variable - self.regression["OLS"]["independent"] = independent_variables - model = regression_model.get_ols( - regression_df[dependent_variable], - regression_df[independent_variables], - ) - self.regression["OLS"]["model"] = model - if not ns_parser.no_output: - console.print(model.summary()) - - else: - console.print( - f"{ns_parser.dependent} not in {','.join(self.choices.get('regressions', []))}\n" - f"Please choose a valid dataset and column combination.\n" - ) - - @log_start_end(log=logger) - def call_norm(self, other_args: List[str]): - """Process normality command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="norm", - description="Test whether the used data is normally distributed.", - ) - parser.add_argument( - "-v", - "--value", - type=str, - choices=self.choices.get("norm", []), - dest="column", - help="The dataset.column you want to test normality for", - required="-h" not in other_args, - ) - parser.add_argument( - "-p", - "--plot", - dest="plot", - help="Whether you wish to plot a histogram to visually depict normality", - action="store_true", - default=False, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser and ns_parser.column: - dataset, column = ns_parser.column.split(".") - - if isinstance(self.datasets[dataset][column].index, pd.MultiIndex): - return console.print( - f"The column '{column}' in '{dataset}' is a MultiIndex. To test for normality" - ", make sure to set a singular time index.\n" - ) - - if dataset in self.datasets: - if isinstance(self.datasets[dataset], pd.Series): - data = self.datasets[dataset] - elif isinstance(self.datasets[dataset], pd.DataFrame): - data = self.datasets[dataset][column] - else: - return console.print( - f"The type of {dataset} ({type(dataset)} is not an option." - ) - else: - return console.print(f"Can not find {dataset}. Did you load the data?") - - econometrics_view.display_norm( - data, dataset, column, ns_parser.plot, ns_parser.export - ) - - @log_start_end(log=logger) - def call_root(self, other_args: List[str]): - """Process unit root command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="root", - description="Show unit root tests of a column of a dataset", - ) - parser.add_argument( - "-v", - "--value", - type=str, - choices=self.choices.get("root", []), - dest="column", - help="The column and name of the database you want test unit root for", - required="-h" not in other_args, - ) - - parser.add_argument( - "-r", - "--fuller_reg", - help="Type of regression. Can be 'c','ct','ctt','nc'. c - Constant and t - trend order", - choices=["c", "ct", "ctt", "n"], - default="c", - type=str, - dest="fuller_reg", - ) - parser.add_argument( - "-k", - "--kps_reg", - help="Type of regression. Can be 'c', 'ct'. c - Constant and t - trend order", - choices=["c", "ct"], - type=str, - dest="kpss_reg", - default="c", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser and ns_parser.column: - if "." in ns_parser.column: - dataset, column = ns_parser.column.split(".") - else: - console.print( - "[red]Column must be formatted as 'dataset.column'[/red]\n" - ) - - if isinstance(self.datasets[dataset][column].index, pd.MultiIndex): - console.print( - f"The column '{column}' from the dataset '{dataset}' is a MultiIndex. To test for unitroot in a " - "timeseries, make sure to set a singular time index.\n" - ) - else: - if isinstance(self.datasets[dataset], pd.Series): - data = self.datasets[dataset] - elif isinstance(self.datasets[dataset], pd.DataFrame): - data = self.datasets[dataset][column] - else: - return console.print( - "Can not select data due to the data not being a DataFrame or Series." - ) - - econometrics_view.display_root( - data, - dataset, - column, - ns_parser.fuller_reg, - ns_parser.kpss_reg, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_panel(self, other_args: List[str]): - """Process panel command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="panel", - description="Performs regression analysis on Panel Data. There are a multitude of options to select " - "from to fit the needs of restrictions of the dataset.", - ) - parser.add_argument( - "-d", - "--dependent", - type=str, - dest="dependent", - help="The dependent variable on the regression you would like to perform", - required="-h" not in other_args, - ) - parser.add_argument( - "-i", - "--independent", - type=check_list_values(self.choices.get("regressions", [])), - dest="independent", - help=( - "The independent variables on the regression you would like to perform. " - "E.g. wage_panel.married,wage_panel.union" - ), - required="-h" not in other_args, - ) - parser.add_argument( - "-r", - "--regression", - type=str, - choices=self.PANEL_CHOICES, - dest="type", - help="The type of regression you wish to perform. This can be either pols (Pooled OLS), " - "re (Random Effects), bols (Between OLS), fe (Fixed Effects) or fdols (First Difference OLS)", - default="pols", - ) - parser.add_argument( - "-e", - "--entity_effects", - dest="entity_effects", - help="Using this command creates entity effects, which is equivalent to including dummies for each entity. " - "This is only used within Fixed Effects estimations (when type is set to 'fe')", - action="store_true", - default=False, - ) - parser.add_argument( - "-t", - "--time_effects", - dest="time_effects", - help="Using this command creates time effects, which is equivalent to including dummies for each time. " - "This is only used within Fixed Effects estimations (when type is set to 'fe')", - action="store_true", - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if "," in ns_parser.dependent: - console.print( - "It appears you have selected multiple variables for the dependent variable. " - "The model only accepts one.\nDid you intend to include these variables as independent " - f"variables? Use -i {ns_parser.dependent} in this case.\n" - ) - elif ns_parser.dependent in self.choices.get("regressions", []): - regression_vars = [ns_parser.dependent] + ns_parser.independent - - if regression_vars and len(regression_vars) > 1: - for variable in regression_vars: - if "." not in variable: - console.print( - "[red]Please follow the format 'dataset.column'[/red]\n" - ) - continue - dataset, column = variable.split(".") - if not isinstance( - self.datasets[dataset][column].index, pd.MultiIndex - ): - other_column = ( - self.datasets[dataset].drop(column, axis=1).columns[0] - ) - return console.print( - f"The column '{column}' from the dataset '{dataset}' is not a MultiIndex. Make sure " - f"you set the index correctly with the index (e.g. index {dataset} -i {column}," - f"{other_column}) command where the first level is the entity (e.g. Tesla Inc.) and " - f"the second level the date (e.g. 2021-03-31)\n" - ) - - # Ensure that OLS is always ran to be able to perform tests - regression_types = [ns_parser.type.upper(), "OLS"] - - for regression in regression_types: - regression_name = regression - if regression == "FE": - if ns_parser.entity_effects: - regression_name = regression_name + "_EE" - if ns_parser.time_effects: - regression_name = regression_name + "_IE" - - ( - regression_df, - dependent_variable, - independent_variables, - ) = regression_model.get_regression_data( - [ns_parser.dependent] + ns_parser.independent, - self.datasets, - regression, - ) - self.regression[regression]["data"] = regression_df - self.regression[regression]["dependent"] = dependent_variable - self.regression[regression][ - "independent" - ] = independent_variables - self.regression[regression_name]["model"] = ( - regression_view.display_panel( - regression_df[dependent_variable], - regression_df[independent_variables], - regression, - ns_parser.entity_effects, - ns_parser.time_effects, - ns_parser.export, - ) - ) - else: - console.print( - f"{ns_parser.dependent} not in {','.join(self.choices['regressions'])}\n" - f"Please choose a valid dataset and column combination.\n" - ) - - @log_start_end(log=logger) - def call_compare(self, other_args: List[str]): - """Process compare command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="compare", - description="Compare results between all activated Panel regression models", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - regression_model.get_comparison( - self.regression, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - console.print() - - @log_start_end(log=logger) - def call_dwat(self, other_args: List[str]): - """Process unitroot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dwat", - description=( - "Show autocorrelation tests from Durbin-Watson. " - "Needs OLS to be run in advance with independent and dependent variables" - ), - ) - parser.add_argument( - "-p", - "--plot", - help="Plot the residuals", - dest="plot", - action="store_true", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if not self.regression["OLS"]["model"]: - console.print( - "Please perform an OLS regression before estimating the Durbin-Watson statistic.\n" - ) - else: - dependent_variable = self.regression["OLS"]["data"][ - self.regression["OLS"]["dependent"] - ] - regression_view.display_dwat( - self.regression["OLS"]["model"], - dependent_variable, - ns_parser.plot, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_bgod(self, other_args): - """Process bgod command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bgod", - description=( - "Show Breusch-Godfrey autocorrelation test results. " - "Needs OLS to be run in advance with independent and dependent variables" - ), - ) - parser.add_argument( - "-l", - "--lags", - type=check_positive, - dest="lags", - help="The lags for the Breusch-Godfrey test", - default=3, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if not self.regression["OLS"]["model"]: - console.print( - "Perform an OLS regression before estimating the Breusch-Godfrey statistic.\n" - ) - else: - regression_view.display_bgod( - self.regression["OLS"]["model"], ns_parser.lags, ns_parser.export - ) - - @log_start_end(log=logger) - def call_bpag(self, other_args): - """Process bpag command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bpag", - description=( - "Show Breusch-Pagan heteroscedasticity test results. " - "Needs OLS to be run in advance with independent and dependent variables" - ), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if not self.regression["OLS"]["model"]: - console.print( - "Perform an OLS regression before estimating the Breusch-Pagan statistic.\n" - ) - else: - regression_view.display_bpag( - self.regression["OLS"]["model"], ns_parser.export - ) - - @log_start_end(log=logger) - def call_garch(self, other_args: List[str]): - """Process garch command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="garch", - description=r"""Calculates annualized volatility forecasts based on GARCH. - GARCH (Generalized autoregressive conditional heteroskedasticity) is stochastic model for time series, - which is for instance used to model volatility clusters, stock return and inflation. It is a - generalisation of the ARCH models. - - $\text{GARCH}(p, q) = (1 - \alpha - \beta) \sigma_l + \sum_{i=1}^q \alpha u_{t-i}^2 + \sum_{i=1}^p \beta - \sigma_{t-i}^2$ [1] - - The GARCH-model assumes that the variance estimate consists of 3 components: - - $\sigma_l$ ; the long term component, which is unrelated to the current market conditions - - $u_t$ ; the innovation/discovery through current market price changes - - $\sigma_t$ ; the last estimate - - GARCH can be understood as a model, which allows to optimize these 3 variance components to the time - series. This is done assigning weights to variance components: $(1 - \alpha - \beta)$ for $\sigma_l$ , - $\alpha$ for $u_t$ and $\beta$ for $\sigma_t$ . [2] - - The weights can be estimated by iterating over different values of $(1 - \alpha - \beta) \sigma_l$ - which we will call $\omega$ , $\alpha$ and $\beta$ , while maximizing: - $\sum_{i} -ln(v_i) - (u_i ^ 2) / v_i$ . With the constraints: - - $\alpha > 0$ - - $\beta > 0$ - - $\alpha + \beta < 1$ - Note that there is no restriction on $\omega$ . - - Another method used for estimation is "variance targeting", where one first sets $\omega$ - equal to the variance of the time series. This method nearly as effective as the previously mentioned and - is less computationally effective. - - One can measure the fit of the time series to the GARCH method by using the Ljung-Box statistic. [3] - - See the sources below for reference and for greater detail. - - Sources: - [1] Generalized Autoregressive Conditional Heteroskedasticity, by Tim Bollerslev - [2] Finance Compact Plus Band 1, by Yvonne Seler Zimmerman and Heinz Zimmerman; ISBN: 978-3-907291-31-1 - [3] Options, Futures & other Derivates, by John C. Hull; ISBN: 0-13-022444-8""", - ) - parser.add_argument( - "-v", - "--value", - type=str, - choices=self.choices.get("garch", []), - dest="column", - help="The column and name of the database you want to estimate volatility for", - required="-h" not in other_args, - ) - parser.add_argument( - "-p", - help="The lag order of the symmetric innovation", - dest="p", - type=int, - default=1, - ) - parser.add_argument( - "-o", - help="The lag order of the asymmetric innovation", - dest="o", - type=int, - default=0, - ) - parser.add_argument( - "-q", - help="The lag order of lagged volatility or equivalent", - dest="q", - type=int, - default=1, - ) - parser.add_argument( - "-m", - "--mean", - help="Choose mean model", - choices=["LS", "AR", "ARX", "HAR", "HARX", "constant", "zero"], - default="constant", - type=str, - dest="mean", - ) - parser.add_argument( - "-l", - "--length", - help="The length of the estimate", - dest="horizon", - type=int, - default=100, - ) - parser.add_argument( - "-d", - "--detailed", - help="Display the details about the parameter fit, for instance the confidence interval", - dest="detailed", - action="store_true", - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - dataset, column = ns_parser.column.split(".") - econometrics_view.display_garch( - self.datasets[dataset], - column, - ns_parser.p, - ns_parser.o, - ns_parser.q, - ns_parser.mean, - ns_parser.horizon, - ns_parser.detailed, - ) - - @log_start_end(log=logger) - def call_granger(self, other_args: List[str]): - """Process granger command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="granger", - description="Show Granger causality between two timeseries", - ) - parser.add_argument( - "-t", - "--timeseries", - choices=self.choices["granger"], - help="Requires two time series, the first time series is assumed to be Granger-caused " - "by the second time series.", - type=str, - dest="ts", - metavar="Available time series", - ) - parser.add_argument( - "-l", - "--lags", - help="How many lags should be included", - type=int, - dest="lags", - default=3, - ) - parser.add_argument( - "-c", - "--confidence", - help="Set the confidence level", - type=check_positive_float, - dest="confidence", - default=0.05, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser and ns_parser.ts: - datasetcol_y, datasetcol_x = ns_parser.ts.split(",") - - dataset_y, column_y = datasetcol_y.split(".") - dataset_x, column_x = datasetcol_x.split(".") - - econometrics_view.display_granger( - self.datasets[dataset_y][column_y].rename(datasetcol_y), - self.datasets[dataset_x][column_x].rename(datasetcol_x), - ns_parser.lags, - ns_parser.confidence, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_coint(self, other_args: List[str]): - """Process coint command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="coint", - description="Show co-integration between two timeseries", - ) - parser.add_argument( - "-t", - "--time_series", - help="The time series you wish to test co-integration on. E.g. historical.open,historical2.close.", - dest="ts", - type=check_list_values(self.choices["coint"]), - required="-h" not in other_args, - ) - parser.add_argument( - "-p", - "--plot", - help="Plot Z-Values", - dest="plot", - action="store_true", - default=False, - ) - parser.add_argument( - "-s", - "--significant", - help="Show only companies that have p-values lower than this percentage", - dest="significant", - type=float, - default=0, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser and ns_parser.ts: - # We are going to pass through a variable number of series, so datasets will be a list of series - if len(ns_parser.ts) > 1: - datasets = [] - for series in ns_parser.ts: - if "." not in series: - console.print( - "[red]Invalid time series format. Should be dataset.column, " - "e.g. historical.open[/red]\n" - ) - else: - dataset, column = series.split(".") - datasets.append(self.datasets[dataset][column]) - - econometrics_view.display_cointegration_test( - *datasets, - significant=ns_parser.significant, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - else: - console.print( - "[red]More than one dataset.column must be provided.\n[/red]" - ) - - @log_start_end(log=logger) - def call_vif(self, other_args: List[str]): - """Process vif command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="vif", - description=r"""Calculates VIF (variance inflation factor), which tests collinearity. - - It quantifies the severity of multicollinearity in an ordinary least squares regression analysis. The square - root of the variance inflation factor indicates how much larger the standard error increases compared to if - that variable had 0 correlation to other predictor variables in the model. - - It is defined as: - - $ VIF_i = 1 / (1 - R_i^2) $ - where $ R_i $ is the coefficient of determination of the regression equation with the column i being the - result from the i:th series being the exogenous variable. - - A VIF over 5 indicates a high collinearity and correlation. Values over 10 indicates causes problems, - while a value of 1 indicates no correlation. Thus VIF values between 1 and 5 are most commonly considered - acceptable. In order to improve the results one can often remove a column with high VIF. - - For further information see: https://en.wikipedia.org/wiki/Variance_inflation_factor""", - ) - parser.add_argument( - "-d", - "--data", - help="The datasets and columns we want to add ,,", - dest="data", - type=check_list_values(self.choices["vif"]), - default=None, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - data = pd.DataFrame() - if ns_parser: - if ns_parser.data is None: - console.print("[red]Please enter a dataset to calculate vif for.[/red]") - return - if len(ns_parser.data) == 1 and "." in ns_parser.data[0]: - console.print( - "[red]Please enter at least a dataset or two columns to calculate vif for." - "vif can only be calculated for at least two columns.[/red]" - ) - for option in ns_parser.data: - if "." in option: - dataset, column = option.split(".") - else: - dataset = option - column = None - - if dataset not in self.datasets: - console.print( - f"[red]Not able to find the dataset {dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}[/red]" - ) - elif column is not None: - if column not in self.datasets[dataset]: - console.print( - f"[red]Not able to find the column {column}. Please choose one of " - f"the following: {', '.join(self.datasets[dataset].data)}[/red]" - ) - else: - data[f"{dataset}_{column}"] = self.datasets[dataset][column] - else: - for column in list(self.datasets[dataset].columns): - data[f"{dataset}_{column}"] = self.datasets[dataset][column] - - econometrics_view.display_vif(data) diff --git a/openbb_terminal/econometrics/econometrics_helpers.py b/openbb_terminal/econometrics/econometrics_helpers.py deleted file mode 100644 index 63b9df280f9..00000000000 --- a/openbb_terminal/econometrics/econometrics_helpers.py +++ /dev/null @@ -1,90 +0,0 @@ -from typing import Dict - -import pandas as pd - -from openbb_terminal.rich_config import console - - -def get_datasets(data: Dict[str, pd.DataFrame]): - datasets = {} - for key, value in data.items(): - for column in value: - datasets[f"{key}.{column}"] = {column: None, key: None} - return datasets - - -def get_ending(dataset: str, column: str) -> str: - ending = "" - if dataset: - ending += f" from dataset '{dataset}'" - if column: - ending += f" of '{column}'" - return ending - - -def create_new_entry(dataset: Dict[str, pd.DataFrame], query: str) -> Dict: - """Create a new series based off previously loaded columns - - Parameters - ---------- - dataset: Dict[str,pd.DataFrame] - Economy datasets that are loaded - query: str - Query to execute - - Returns - ------- - Dict[str, pd.DataFrame] - """ - # Create a single dataframe from dictionary of dataframes - columns = [] - data = pd.DataFrame() - for _, df in dataset.items(): - if not df.empty: - columns.extend(df.columns) - data = pd.concat([data, df], axis=1) - # In order to account for potentially different index time steps, lets dropNans here. - # Potentially problematic down the road - data = data.dropna(axis=0) - - # Eval the query to generate new sequence - # if there is an = in the query, then there will be a new named column - if "=" in query: - new_column = query.split("=")[0].replace(" ", "") - if new_column in data.columns: - query = query.replace(new_column, new_column + "_duplicate") - new_column += "_duplicate" - # Wrap the eval in a syntax error in case the user does something not allowed - try: - new_df = data.eval(query) - except SyntaxError: - console.print( - "[red]Invalid syntax in query. Please enter something of the form `newcol=col1 + col2`[/red]\n" - ) - return dataset - except pd.errors.UndefinedVariableError as e: - console.print(f"[red]{e}[/red]") - return dataset - - # If custom exists in the dictionary, we need to append the current dataframe - if "custom" in dataset: - dataset["custom"] = pd.concat([dataset["custom"], new_df[[new_column]]]) - else: - dataset["custom"] = new_df[[new_column]] - return dataset - - # If there is not an equal (namely .eval(colA + colB), the result will be a series - # and not a dataframe. We can just call this custom_exp - - try: - data = pd.DataFrame(data.eval(query), columns=["custom_exp"]) - dataset["custom"] = data - except SyntaxError: - console.print( - "Invalid syntax in query. Please enter something of the form `newcol=col1 + col2`" - ) - return dataset - except pd.errors.UndefinedVariableError as e: - console.print(f"[red]{e}[/red]") - return dataset - return dataset diff --git a/openbb_terminal/econometrics/econometrics_model.py b/openbb_terminal/econometrics/econometrics_model.py deleted file mode 100644 index 5ebbf1ee53f..00000000000 --- a/openbb_terminal/econometrics/econometrics_model.py +++ /dev/null @@ -1,535 +0,0 @@ -"""Econometrics Model""" - -__docformat__ = "numpy" - -# pylint: disable=eval-used - -import logging -import warnings -from itertools import combinations -from typing import Any, Dict, Optional, Tuple, Union - -import numpy as np -import pandas as pd -import statsmodels.api as sm -from arch import arch_model -from scipy import stats -from statsmodels.stats.outliers_influence import variance_inflation_factor -from statsmodels.tools.tools import add_constant -from statsmodels.tsa.stattools import adfuller, grangercausalitytests, kpss - -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def get_options( - datasets: Dict[str, pd.DataFrame], dataset_name: str = "" -) -> Dict[Union[str, Any], pd.DataFrame]: - """Obtain columns-dataset combinations from loaded in datasets that can be used in other commands - - Parameters - ---------- - datasets: dict - The available datasets. - dataset_name: str - The dataset you wish to show the options for. - - Returns - ------- - Dict[Union[str, Any], pd.DataFrame] - A dictionary with a DataFrame for each option. With dataset_name set, only shows one - options table. - """ - option_tables = {} - - if dataset_name: - columns = datasets[dataset_name].columns - option_tables[dataset_name] = pd.DataFrame( - { - "column": columns, - "option": [f"{dataset_name}.{column}" for column in columns], - } - ) - else: - for dataset, data_values in datasets.items(): - columns = data_values.columns - option_tables[dataset] = pd.DataFrame( - { - "column": columns, - "option": [f"{dataset}.{column}" for column in columns], - } - ) - - return option_tables - - -def get_corr_df(data: pd.DataFrame) -> pd.DataFrame: - """Returns correlation for a given df - - Parameters - ---------- - data: pd.DataFrame - The df to produce statistics for - - Returns - ------- - df: pd.DataFrame - The df with the new data - """ - corr = data.corr(numeric_only=True) - return corr - - -def clean( - dataset: pd.DataFrame, - fill: str = "", - drop: str = "", - limit: Optional[int] = None, -) -> pd.DataFrame: - """Clean up NaNs from the dataset - - Parameters - ---------- - dataset : pd.DataFrame - The dataset you wish to clean - fill : str - The method of filling NaNs. Choose from: - rfill, cfill, rbfill, cbfill, rffill, cffill - drop : str - The method of dropping NaNs. Choose from: - rdrop, cdrop - limit : int - The maximum limit you wish to apply that can be forward or backward filled - - Returns - ------- - pd.DataFrame - Dataframe with cleaned up data - """ - fill_dict = { - "rfill": lambda x: x.fillna(axis="index", value=0), - "cfill": lambda x: x.fillna(axis="columns", value=0), - "rbfill": lambda x: x.fillna(axis="index", method="bfill", limit=limit), - "cbfill": lambda x: x.fillna(axis="columns", method="bfill", limit=limit), - "rffill": lambda x: x.fillna(axis="index", method="ffill", limit=limit), - "cffill": lambda x: x.fillna(axis="columns", method="ffill", limit=limit), - } - - if fill and fill in fill_dict: - dataset = fill_dict[fill](dataset) - - if drop: - if drop == "rdrop": - dataset = dataset.dropna(how="any", axis="index") - elif drop == "cdrop": - dataset = dataset.dropna(how="any", axis="columns") - - return dataset - - -def get_normality(data: pd.Series) -> pd.DataFrame: - """ - The distribution of returns and generate statistics on the relation to the normal curve. - This function calculates skew and kurtosis (the third and fourth moments) and performs both - a Jarque-Bera and Shapiro Wilk test to determine if data is normally distributed. - - Parameters - ---------- - data : pd.Series - A series or column of a DataFrame to test normality for - - Returns - ------- - pd.DataFrame - Dataframe containing statistics of normality - """ - # Kurtosis: measures height and sharpness of the central peak relative to standard bell curve - k, kpval = stats.kurtosistest(data) - kpval = round(kpval, 5) - - # Skewness: measure of the asymmetry of the probability distribution of a random variable about its mean - s, spval = stats.skewtest(data) - spval = round(spval, 5) - - # Jarque-Bera goodness of fit test on sample data: tests if the sample data has the skewness and kurtosis - # matching a normal distribution - jb, jbpval = stats.jarque_bera(data) - jbpval = round(jbpval, 5) - - # The Shapiro-Wilk test: tests the null hypothesis that the data was drawn from a normal distribution. - sh, shpval = stats.shapiro(data) - shpval = round(shpval, 5) - - # Kolmogorov-Smirnov: the one-sample test compares the underlying distribution F(x) of a sample - # against a given distribution G(x). Comparing to normal here. - ks, kspval = stats.kstest(data, "norm") - kspval = round(kspval, 5) - - l_statistic = [k, s, jb, sh, ks] - l_pvalue = [kpval, spval, jbpval, shpval, kspval] - - return pd.DataFrame( - [l_statistic, l_pvalue], - columns=[ - "Kurtosis", - "Skewness", - "Jarque-Bera", - "Shapiro-Wilk", - "Kolmogorov-Smirnov", - ], - index=["Statistic", "p-value"], - ) - - -def get_root( - data: pd.Series, fuller_reg: str = "c", kpss_reg: str = "c" -) -> pd.DataFrame: - """Calculate test statistics for unit roots - - Parameters - ---------- - data : pd.Series - Series or column of DataFrame of target variable - fuller_reg : str - Type of regression of ADF test - kpss_reg : str - Type of regression for KPSS test - - Returns - ------- - pd.DataFrame - Dataframe with results of ADF test and KPSS test - """ - # The Augmented Dickey-Fuller test: used to test for a unit root in a univariate process in the - # presence of serial correlation. - result = adfuller(data, regression=fuller_reg) - cols = ["Test Statistic", "P-Value", "NLags", "Nobs", "ICBest"] - vals = [result[0], result[1], result[2], result[3], result[5]] - data = pd.DataFrame(data=vals, index=cols, columns=["ADF"]) - - # Kwiatkowski-Phillips-Schmidt-Shin test: test for level or trend stationarity - # Note: this test seems to produce an Interpolation Error which says - # The test statistic is outside the range of p-values available in the - # look-up table. The actual p-value is greater than the p-value returned. - # Wrap this in catch_warnings to prevent this - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - res2 = kpss(data, regression=kpss_reg, nlags="auto") - - vals2 = [res2[0], res2[1], res2[2], 0, 0] - data["KPSS"] = vals2 - - return data - - -def get_granger_causality( - dependent_series: pd.Series, independent_series: pd.Series, lags: int = 3 -) -> pd.DataFrame: - """Calculate granger tests - - Parameters - ---------- - dependent_series: Series - The series you want to test Granger Causality for. - independent_series: Series - The series that you want to test whether it Granger-causes time_series_y - lags : int - The amount of lags for the Granger test. By default, this is set to 3. - - Returns - ------- - pd.DataFrame - Dataframe containing results of Granger test - """ - granger_set = pd.concat([dependent_series, independent_series], axis=1) - - granger = grangercausalitytests(granger_set, [lags], verbose=False) - - for test in granger[lags][0]: - # As ssr_chi2test and lrtest have one less value in the tuple, we fill - # this value with a '-' to allow the conversion to a DataFrame - if len(granger[lags][0][test]) != 4: - pars = granger[lags][0][test] - granger[lags][0][test] = (pars[0], pars[1], "-", pars[2]) - - granger_df = pd.DataFrame( - granger[lags][0], index=["F-test", "P-value", "Count", "Lags"] - ).T - - return granger_df - - -# TODO: Maybe make a new function to return z instead of having this flag. -# TODO: Allow for numpy arrays as well -def get_coint_df( - *datasets: pd.Series, return_z: bool = False -) -> Union[pd.DataFrame, Dict]: - """Calculate cointegration tests between variable number of input series - - Parameters - ---------- - datasets : pd.Series - Input series to test cointegration for - return_z : bool - Flag to return the z data to plot - - Returns - ------- - Union[pd.DataFrame,Dict] - Dataframe with results of cointegration tests or a Dict of the z results - """ - result: Dict[str, list] = {} - z_values: Dict[str, pd.Series] = {} - - # The *datasets lets us pass in a variable number of arguments - # Here we are getting all possible combinations of unique inputs - - pairs = list(combinations(datasets, 2)) - for x, y in pairs: - if sum(y.isnull()) > 0: - console.print( - f"The Series {y} has nan-values. Please consider dropping or filling these " - f"values with 'clean'." - ) - elif sum(x.isnull()) > 0: - console.print( - f"The Series {x.name} has nan-values. Please consider dropping or filling these " - f"values with 'clean'." - ) - elif not y.index.equals(x.index): - console.print( - f"The Series {y.name} and {x.name} do not have the same index." - ) - ( - c, - gamma, - alpha, - z, - adfstat, - pvalue, - ) = get_engle_granger_two_step_cointegration_test(x, y) - result[f"{x.name}/{y.name}"] = [c, gamma, alpha, adfstat, pvalue] - z_values[f"{x.name}/{y.name}"] = z - - if result and z_values: - if return_z: - return z_values - df = pd.DataFrame.from_dict( - result, - orient="index", - columns=["Constant", "Gamma", "Alpha", "Dickey-Fuller", "P Value"], - ) - return df - return pd.DataFrame() - - -def get_returns(data: pd.Series): - """Calculate returns for the given time series - - Parameters - ---------- - data: pd.Series - The data series to calculate returns for - """ - return 100 * data.pct_change().dropna() - - -def get_garch( - data: pd.Series, - p: int = 1, - o: int = 0, - q: int = 1, - mean: str = "constant", - horizon: int = 100, -): - r"""Calculates volatility forecasts based on GARCH. - - GARCH (Generalized autoregressive conditional heteroskedasticity) is stochastic model for time series, - which is for instance used to model volatility clusters, stock return and inflation. It is a - generalisation of the ARCH models. - - $ GARCH(p, q) = (1 - \alpha - \beta) \sigma_l + \sum_{i=1}^q \alpha u_{t-i}^2 + \sum_{i=1}^p \beta \sigma_{t-i}^2 - $ [1] - - The GARCH-model assumes that the variance estimate consists of 3 components: - - $ \sigma_l $; the long term component, which is unrelated to the current market conditions - - $ u_t $; the innovation/discovery through current market price changes - - $ \sigma_t $; the last estimate - - GARCH can be understood as a model, which allows to optimize these 3 variance components to the time series. - This is done assigning weights to variance components: $ (1 - \alpha - \beta) $ for $ \sigma_l $, $ \alpha $ for - $ u_t $ and $ \beta $ for $ \sigma_t $. [2] - - The weights can be estimated by iterating over different values of $ (1 - \alpha - \beta) \sigma_l $ which we - will call $ \omega $, $ \alpha $ and $ \beta $, while maximizing: $ \sum_{i} -ln(v_i) - (u_i ^ 2) / v_i $. - With the constraints: - - $ \alpha > 0 $ - - $ \beta > 0 $ - - $ \alpha + \beta < 1 $ - Note that there is no restriction on $ \omega $. - - Another method used for estimation is "variance targeting", where one first sets $ \omega $ - equal to the variance of the time series. This method nearly as effective as the previously mentioned and - is less computationally effective. - - One can measure the fit of the time series to the GARCH method by using the Ljung-Box statistic. [3] - - See the sources below for reference and for greater detail. - - Sources: - [1] Generalized Autoregressive Conditional Heteroskedasticity, by Tim Bollerslev - [2] Finance Compact Plus Band 1, by Yvonne Seler Zimmerman and Heinz Zimmerman; ISBN: 978-3-907291-31-1 - [3] Options, Futures & other Derivates, by John C. Hull; ISBN: 0-13-022444-8 - - Parameters - ---------- - data: pd.Series - The time series (often returns) to estimate volatility from - p: int - Lag order of the symmetric innovation - o: int - Lag order of the asymmetric innovation - q: int - Lag order of lagged volatility or equivalent - mean: str - The name of the mean model - horizon: int - The horizon of the forecast - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.econometrics.garch(openbb.stocks.load("AAPL").iloc[:, 0].pct_change()*100) - """ - model = arch_model(data.dropna(), vol="GARCH", p=p, o=o, q=q, mean=mean) - model_fit = model.fit(disp="off") - pred = model_fit.forecast(horizon=horizon, reindex=False) - - return np.sqrt(pred.variance.values[-1, :]), model_fit - - -def get_engle_granger_two_step_cointegration_test( - dependent_series: pd.Series, independent_series: pd.Series -) -> Tuple[float, float, float, pd.Series, float, float]: - """Estimates long-run and short-run cointegration relationship for series y and x and apply - the two-step Engle & Granger test for cointegration. - - Uses a 2-step process to first estimate coefficients for the long-run relationship - y_t = c + gamma * x_t + z_t - - and then the short-term relationship, - y_t - y_(t-1) = alpha * z_(t-1) + epsilon_t, - - with z the found residuals of the first equation. - - Then tests cointegration by Dickey-Fuller phi=1 vs phi < 1 in - z_t = phi * z_(t-1) + eta_t - - If this implies phi < 1, the z series is stationary is concluded to be - stationary, and thus the series y and x are concluded to be cointegrated. - - Parameters - ---------- - dependent_series : pd.Series - The first time series of the pair to analyse. - independent_series : pd.Series - The second time series of the pair to analyse. - - Returns - ------- - Tuple[float, float, float, pd.Series, float, float] - c : float - The constant term in the long-run relationship y_t = c + gamma * x_t + z_t. This - describes the static shift of y with respect to gamma * x. - - gamma : float - The gamma term in the long-run relationship y_t = c + gamma * x_t + z_t. This - describes the ratio between the const-shifted y and x. - - alpha : float - The alpha term in the short-run relationship y_t - y_(t-1) = alpha * z_(t-1) + epsilon. This - gives an indication of the strength of the error correction toward the long-run mean. - - z : pd.Series - Series of residuals z_t from the long-run relationship y_t = c + gamma * x_t + z_t, representing - the value of the error correction term. - - dfstat : float - The Dickey Fuller test-statistic for phi = 1 vs phi < 1 in the second equation. A more - negative value implies the existence of stronger cointegration. - - pvalue : float - The p-value corresponding to the Dickey Fuller test-statistic. A lower value implies - stronger rejection of no-cointegration, thus stronger evidence of cointegration. - - """ - warnings.simplefilter(action="ignore", category=FutureWarning) - long_run_ols = sm.OLS(dependent_series, sm.add_constant(independent_series)) - warnings.simplefilter(action="default", category=FutureWarning) - - long_run_ols_fit = long_run_ols.fit() - - c, gamma = long_run_ols_fit.params - z = long_run_ols_fit.resid - - short_run_ols = sm.OLS(dependent_series.diff().iloc[1:], (z.shift().iloc[1:])) - short_run_ols_fit = short_run_ols.fit() - - alpha = short_run_ols_fit.params[0] - - # NOTE: The p-value returned by the adfuller function assumes we do not estimate z - # first, but test stationarity of an unestimated series directly. This assumption - # should have limited effect for high N, however. Critical values taking this into - # account more accurately are provided in e.g. McKinnon (1990) and Engle & Yoo (1987). - - adfstat, pvalue, _, _, _ = adfuller(z, maxlag=1, autolag=None) - - return c, gamma, alpha, z, adfstat, pvalue - - -def get_vif(dataset: pd.DataFrame, columns: Optional[list] = None) -> pd.DataFrame: - r"""Calculates VIF (variance inflation factor), which tests collinearity. - - It quantifies the severity of multicollinearity in an ordinary least squares regression analysis. The square - root of the variance inflation factor indicates how much larger the standard error increases compared to if - that variable had 0 correlation to other predictor variables in the model. - - It is defined as: - - $ VIF_i = 1 / (1 - R_i^2) $ - where $ R_i $ is the coefficient of determination of the regression equation with the column i being the result - from the i:th series being the exogenous variable. - - A VIF over 5 indicates a high collinearity and correlation. Values over 10 indicates causes problems, while a - value of 1 indicates no correlation. Thus VIF values between 1 and 5 are most commonly considered acceptable. - In order to improve the results one can often remove a column with high VIF. - - For further information see: https://en.wikipedia.org/wiki/Variance_inflation_factor - - Parameters - ---------- - dataset: pd.Series - Dataset to calculate VIF on - columns: Optional[list] - The columns to calculate to test for collinearity - - Returns - ------- - pd.DataFrame - Dataframe with the resulting VIF values for the selected columns - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> longley = openbb.econometrics.load("longley") - >>> openbb.econometrics.vif(longley, ["TOTEMP","UNEMP","ARMED"]) - """ - df = add_constant(dataset if columns is None else dataset[columns]) - vif = pd.DataFrame( - { - "VIF Values": [ - variance_inflation_factor(df.values, i) for i in range(df.shape[1]) - ][1:] - }, - index=df.columns[1:], - ) - return vif diff --git a/openbb_terminal/econometrics/econometrics_view.py b/openbb_terminal/econometrics/econometrics_view.py deleted file mode 100644 index db03c12d7d4..00000000000 --- a/openbb_terminal/econometrics/econometrics_view.py +++ /dev/null @@ -1,611 +0,0 @@ -"""Econometrics View""" - -__docformat__ = "numpy" - -# pylint: disable=too-many-arguments - -import logging -import os -from typing import Dict, Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.econometrics import econometrics_model -from openbb_terminal.econometrics.econometrics_helpers import get_ending -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def show_options( - datasets: Dict[str, pd.DataFrame], - dataset_name: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Plot custom data - - Parameters - ---------- - datasets: dict - The loaded in datasets - dataset_name: str - The name of the dataset you wish to show options for - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export image - """ - if not datasets: - console.print( - "Please load in a dataset by using the 'load' command before using this feature." - ) - else: - option_tables = econometrics_model.get_options( - datasets, dataset_name if dataset_name is not None else "" - ) - - for dataset, data_values in option_tables.items(): - print_rich_table( - data_values, - headers=list(data_values.columns), - show_index=False, - title=f"Options for dataset: '{dataset}'", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{dataset}_options", - data_values.set_index("column"), - sheet_name, - ) - - -@log_start_end(log=logger) -def display_plot( - data: Union[pd.Series, pd.DataFrame, Dict[str, pd.DataFrame]], - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plot data from a dataset - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame, Dict[str: pd.DataFrame] - Dictionary with key being dataset.column and dataframes being values - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export image - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure() - - if isinstance(data, pd.Series): - data = {data.name: data} - elif isinstance(data, pd.DataFrame): - data = {x: data[x] for x in data.columns} - - for dataset_col in data: - if isinstance(data[dataset_col].index, pd.MultiIndex): - console.print( - "The index appears to be a multi-index. " - "Therefore, it is not possible to plot the data." - ) - del data[dataset_col] - - if not fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "plot", - sheet_name, - ) - - # Check that there's at least a valid dataframe - if data: - for dataset_col in data: - try: - if isinstance(data[dataset_col], (pd.Series, pd.DataFrame)): - fig.add_scatter( - x=data[dataset_col].index, - y=data[dataset_col].values, - name=dataset_col, - ) - - except ValueError: - console.print(f"Could not convert column: {dataset_col}") - except TypeError: - console.print(f"Could not convert column: {dataset_col}") - - if fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "plot", - sheet_name=sheet_name, - figure=fig, - ) - - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -def display_corr( - dataset: pd.DataFrame, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot correlation coefficients for dataset features - - Parameters - ---------- - dataset : pd.DataFrame - The dataset fore calculating correlation coefficients - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export image - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - fig = OpenBBFigure() - - # correlation - correlation = econometrics_model.get_corr_df(dataset) - fig.add_heatmap( - z=correlation, - x=correlation.columns, - y=correlation.index, - zmin=correlation.values.min(), - zmax=1, - showscale=True, - text=correlation, - texttemplate="%{text:.2f}", - colorscale="electric", - colorbar=dict( - thickness=10, - thicknessmode="pixels", - x=1.2, - y=1, - xanchor="right", - yanchor="top", - xpad=10, - ), - xgap=1, - ygap=1, - ) - fig.update_yaxes(autorange="reversed") - fig.update_layout(margin=dict(l=0, r=120, t=0, b=0), title="Correlation Matrix") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "plot", - sheet_name=sheet_name, - figure=fig, - ) - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_norm( - data: pd.Series, - dataset: str = "", - column: str = "", - plot: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Determine the normality of a timeseries. - - Parameters - ---------- - data: pd.Series - Series of custom data - dataset: str - Dataset name - column: str - Column for y data - plot : bool - Whether you wish to plot a histogram - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure() - - if data.dtype not in [int, float, np.float64, np.int64]: - console.print( - f"The column type must be numeric. The provided column type is {data.dtype}. " - f"Consider using the command 'type' to change this.\n" - ) - else: - results = econometrics_model.get_normality(data) - - ending = get_ending(dataset, column) - print_rich_table( - results, - headers=list(results.columns), - show_index=True, - title=f"Normality test{ending}", - export=bool(export), - ) - if not fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{column}_{dataset}_norm", - results, - sheet_name, - ) - - if plot or fig.is_image_export(export): - fig.set_title(f"Histogram{ending}", wrap=True, wrap_width=55) - - fig.add_histogram(x=data, name="Histogram", nbinsx=100) - - fig.update_layout(margin=dict(t=65)) - - if fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{column}_{dataset}_norm", - results, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -def display_root( - data: pd.Series, - dataset: str = "", - column: str = "", - fuller_reg: str = "c", - kpss_reg: str = "c", - export: str = "", - sheet_name: Optional[str] = None, -): - """Determine the normality of a timeseries. - - Parameters - ---------- - data : pd.Series - Series of target variable - dataset: str - Name of the dataset - column: str - Name of the column - fuller_reg : str - Type of regression of ADF test. Choose c, ct, ctt, or nc - kpss_reg : str - Type of regression for KPSS test. Choose c or ct - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data. - """ - if data.dtype not in [int, float, np.float64, np.int64]: - console.print( - f"The column type must be numeric. The provided " - f"type is {data.dtype}. Consider using the command 'type' to change this.\n" - ) - else: - results = econometrics_model.get_root(data, fuller_reg, kpss_reg) - - ending = get_ending(dataset, column) - print_rich_table( - results, - headers=list(results.columns), - show_index=True, - title=f"Unitroot {ending}", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{dataset}_{column}_root", - results, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_garch( - dataset: pd.DataFrame, - column: str, - p: int = 1, - o: int = 0, - q: int = 1, - mean: str = "constant", - horizon: int = 1, - detailed: bool = False, - export: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots the volatility forecasts based on GARCH - - Parameters - ---------- - dataset: pd.DataFrame - The dataframe to use - column: str - The column of the dataframe to use - p: int - Lag order of the symmetric innovation - o: int - Lag order of the asymmetric innovation - q: int - Lag order of lagged volatility or equivalent - mean: str - The name of the mean model - horizon: int - The horizon of the forecast - detailed: bool - Whether to display the details about the parameter fit, for instance the confidence interval - export: str - Format to export data - external_axes: bool - Whether to return the figure object or not, by default False - """ - data = dataset[column] - result, garch_fit = econometrics_model.get_garch(data, p, o, q, mean, horizon) - - fig = OpenBBFigure() - - fig.add_scatter(x=list(range(1, horizon + 1)), y=result) - fig.set_title( - f"{f'GARCH({p}, {o}, {q})' if o != 0 else f'GARCH({p}, {q})'} volatility forecast" - ) - - if fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{column}_{dataset}_GARCH({p},{q})", - result, - figure=fig, - ) - - if not detailed: - print_rich_table( - garch_fit.params.to_frame(), - headers=["Values"], - show_index=True, - index_name="Parameters", - title=f"GARCH({p}, {o}, {q})" if o != 0 else f"GARCH({p}, {q})", - export=bool(export), - ) - else: - console.print(garch_fit) - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_granger( - dependent_series: pd.Series, - independent_series: pd.Series, - lags: int = 3, - confidence_level: float = 0.05, - export: str = "", - sheet_name: Optional[str] = None, -): - """Show granger tests - - Parameters - ---------- - dependent_series: Series - The series you want to test Granger Causality for. - independent_series: Series - The series that you want to test whether it Granger-causes dependent_series - lags : int - The amount of lags for the Granger test. By default, this is set to 3. - confidence_level: float - The confidence level you wish to use. By default, this is set to 0.05. - export : str - Format to export data - """ - if dependent_series.dtype not in [int, float, np.float64, np.int64]: - console.print( - f"The time series {dependent_series.name} needs to be numeric but is type " - f"{dependent_series.dtype}. Consider using the command 'type' to change this." - ) - elif independent_series.dtype not in [int, float, np.float64, np.int64]: - console.print( - f"The time series {independent_series.name} needs to be numeric but is type " - f"{independent_series.dtype}. Consider using the command 'type' to change this." - ) - else: - granger_df = econometrics_model.get_granger_causality( - dependent_series, independent_series, lags - ) - - print_rich_table( - granger_df, - headers=list(granger_df.columns), - show_index=True, - title=f"Granger Causality Test [Y: {dependent_series.name} | X: {independent_series.name} | Lags: {lags}]", - export=bool(export), - ) - - result_ftest = round(granger_df.loc["params_ftest"]["P-value"], 3) - - if result_ftest > confidence_level: - console.print( - f"As the p-value of the F-test is {result_ftest}, we can not reject the null hypothesis at " - f"the {confidence_level} confidence level.\n" - ) - else: - console.print( - f"As the p-value of the F-test is {result_ftest}, we can reject the null hypothesis at " - f"the {confidence_level} confidence level and find the Series '{independent_series.name}' " - f"to Granger-cause the Series '{dependent_series.name}'\n" - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f'{dependent_series.name.replace(".","-")}_{independent_series.name.replace(".","-")}_granger', - granger_df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_cointegration_test( - *datasets: pd.Series, - significant: bool = False, - plot: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Estimates long-run and short-run cointegration relationship for series y and x and apply - the two-step Engle & Granger test for cointegration. - - Uses a 2-step process to first estimate coefficients for the long-run relationship - y_t = c + gamma * x_t + z_t - - and then the short-term relationship, - y_t - y_(t-1) = alpha * z_(t-1) + epsilon_t, - - with z the found residuals of the first equation. - - Then tests co-integration with the Dickey-Fuller phi=1 vs phi < 1 in - z_t = phi * z_(t-1) + eta_t - - If this implies phi < 1, the z series is stationary is concluded to be - stationary, and thus the series y and x are concluded to be cointegrated. - - Parameters - ---------- - datasets: pd.Series - Variable number of series to test for cointegration - significant: float - Show only companies that have p-values lower than this percentage - plot: bool - Whether you wish to plot the z-values of all pairs. - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if len(datasets) < 2: - return console.print( - "[red]Co-integration requires at least two time series.[/red]" - ) - fig = OpenBBFigure().set_title("Error correction terms") - - df: pd.DataFrame = econometrics_model.get_coint_df(*datasets) - - if significant: - console.print( - f"Only showing pairs that are statistically significant ({significant} > p-value)." - ) - df = df[significant > df["P Value"]] - console.print() - - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - index_name="Pairs", - title="Cointegration Tests", - export=bool(export), - ) - - if not fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "coint", - df, - sheet_name, - ) - if plot or fig.is_image_export(export): - z_values = econometrics_model.get_coint_df(*datasets, return_z=True) - - for pair, values in z_values.items(): - fig.add_scatter(x=values.index, y=values, name=pair, mode="lines") - - if fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "coint", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -def display_vif( - dataset: pd.DataFrame, - columns: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Displays the VIF (variance inflation factor), which tests for collinearity, values for each column. - - Parameters - ---------- - dataset: pd.Series - Dataset to calculate VIF on - columns: Optional[list] - The columns to calculate to test for collinearity - sheet_name: Optional[str] - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data. - """ - columns = dataset.columns if columns is None else columns - if any(dataset[column].dtype not in [int, float] for column in columns): - console.print( - "All column types must be numeric. Consider using the command 'type' to change this.\n" - ) - else: - results = econometrics_model.get_vif(dataset, columns) - - print_rich_table( - results, - headers=list(results.columns), - show_index=True, - title="Collinearity Test", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{dataset}_{','.join(columns)}_vif", - results, - sheet_name, - ) diff --git a/openbb_terminal/econometrics/regression_model.py b/openbb_terminal/econometrics/regression_model.py deleted file mode 100644 index ec725818a5c..00000000000 --- a/openbb_terminal/econometrics/regression_model.py +++ /dev/null @@ -1,574 +0,0 @@ -"""Regression Model""" - -__docformat__ = "numpy" - -import logging -import os -import warnings -from typing import Any, Dict, List, Optional, Tuple - -import pandas as pd -import statsmodels -import statsmodels.api as sm -from linearmodels import PooledOLS -from linearmodels.panel import ( - BetweenOLS, - FirstDifferenceOLS, - PanelOLS, - RandomEffects, - compare, -) -from pandas import DataFrame -from statsmodels.api import add_constant -from statsmodels.stats.api import het_breuschpagan -from statsmodels.stats.diagnostic import acorr_breusch_godfrey -from statsmodels.stats.stattools import durbin_watson - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.econometrics.econometrics_helpers import get_datasets -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def get_regressions_results( - Y: pd.DataFrame, - X: pd.DataFrame, - regression_type: str = "OLS", - entity_effects: bool = False, - time_effects: bool = False, -) -> Any: - """Based on the regression type, this function decides what regression to run. - - Parameters - ---------- - Y: pd.DataFrame - Dataframe containing the dependent variable. - X: pd.DataFrame - Dataframe containing the independent variables. - regression_type: str - The type of regression you wish to execute. - entity_effects: bool - Whether to apply Fixed Effects on entities. - time_effects: bool - Whether to apply Fixed Effects on time. - - Returns - ------- - Any - A regression model - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.econometrics.load("wage_panel") - >>> df = df.set_index(["nr","year"]) - >>> X = df[["exper","educ","union"]] - >>> Y = df["lwage"] - >>> pooled_ols_model = openbb.econometrics.panel(Y,X,"POLS") - >>> print(pooled_ols_model.summary) - PooledOLS Estimation Summary - ================================================================================ - Dep. Variable: lwage R-squared: 0.1634 - Estimator: PooledOLS R-squared (Between): 0.1686 - No. Observations: 4360 R-squared (Within): 0.1575 - Date: Sun, Nov 13 2022 R-squared (Overall): 0.1634 - Time: 13:04:02 Log-likelihood -3050.4 - Cov. Estimator: Unadjusted - F-statistic: 283.68 - Entities: 545 P-value 0.0000 - Avg Obs: 8.0000 Distribution: F(3,4356) - Min Obs: 8.0000 - Max Obs: 8.0000 F-statistic (robust): 283.68 - P-value 0.0000 - Time periods: 8 Distribution: F(3,4356) - Avg Obs: 545.00 - Min Obs: 545.00 - Max Obs: 545.00 - Parameter Estimates - ============================================================================== - Parameter Std. Err. T-stat P-value Lower CI Upper CI - ------------------------------------------------------------------------------ - const -0.0308 0.0620 -0.4965 0.6196 -0.1523 0.0908 - exper 0.0561 0.0028 20.220 0.0000 0.0507 0.0616 - educ 0.1080 0.0045 24.034 0.0000 0.0992 0.1168 - union 0.1777 0.0172 10.344 0.0000 0.1441 0.2114 - ============================================================================== - """ - regressions = { - "OLS": lambda: get_ols(Y, X), - "POLS": lambda: get_pols(Y, X), - "RE": lambda: get_re(Y, X), - "BOLS": lambda: get_bols(Y, X), - "FE": lambda: get_fe(Y, X, entity_effects, time_effects), - "FDOLS": lambda: get_fdols(Y, X), - } - if regression_type in regressions: - return regressions[regression_type]() - - return console.print(f"{regression_type} is not an option.") - - -def get_regression_data( - regression_variables: List[tuple], - data: Dict[str, pd.DataFrame], - regression_type: str = "", -) -> Tuple[DataFrame, Any, List[Any]]: - """This function creates a DataFrame with the required regression data as - well sets up the dependent and independent variables. - - Parameters - ---------- - regression_variables : list - The regressions variables entered where the first variable is - the dependent variable. - data : dict - A dictionary containing the datasets. - regression_type: str - The type of regression that is executed. - - Returns - ------- - Tuple[DataFrame, Any, List[Any]] - The dataset used, - Dependent variable, - Independent variable, - OLS model. - """ - - datasets = get_datasets(data) - regression = {} - independent_variables = [] - dependent_variable = None - - for variable in regression_variables: - column, dataset = datasets[variable].keys() - regression[f"{dataset}.{column}"] = data[dataset][column] - - if variable == regression_variables[0]: - dependent_variable = f"{dataset}.{column}" - elif variable in regression_variables[1:]: - independent_variables.append(f"{dataset}.{column}") - - regression_df = pd.DataFrame(regression) - nan_values = regression_df.isnull().sum().sum() - - if nan_values > 0: - regression_df = regression_df.dropna(how="any", axis="index") - - if regression_df.empty: - console.print( - f"The resulting DataFrame only consists of NaN values. This is usually due to an index " - f"mismatch. Therefore, no {regression_type} regression can be performed. Consider revisiting your " - "dataset(s) and adjust accordingly." - ) - else: - console.print( - f"The resulting DataFrame has {nan_values} NaN values. This is usually due to " - f"an index mismatch. Rows that contain NaNs are dropped to be able to perform the {regression_type} " - f"regression. Consider revisiting your dataset(s) and adjust accordingly." - ) - - return regression_df, dependent_variable, independent_variables - - -@log_start_end(log=logger) -def get_ols(Y: pd.DataFrame, X: pd.DataFrame) -> Any: - """Performs an OLS regression on timeseries data. [Source: Statsmodels] - - Parameters - ---------- - Y: pd.DataFrame - Dependent variable series. - X: pd.DataFrame - Dataframe of independent variables series. - - Returns - ------- - statsmodels.regression.linear_model.RegressionResultsWrapper - Regression model wrapper from statsmodels. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.econometrics.load("wage_panel") - >>> OLS_model = openbb.econometrics.ols(df["lwage"], df[["educ", "exper", "expersq"]]) - >>> print(OLS_model.summary())` - OLS Regression Results - ======================================================================================= - Dep. Variable: lwage R-squared (uncentered): 0.920 - Model: OLS Adj. R-squared (uncentered): 0.919 - Method: Least Squares F-statistic: 1.659e+04 - Date: Thu, 10 Nov 2022 Prob (F-statistic): 0.00 - Time: 15:28:11 Log-Likelihood: -3091.3 - No. Observations: 4360 AIC: 6189. - Df Residuals: 4357 BIC: 6208. - Df Model: 3 - Covariance Type: nonrobust - ============================================================================== - coef std err t P>|t| [0.025 0.975] - ------------------------------------------------------------------------------ - educ 0.0986 0.002 39.879 0.000 0.094 0.103 - exper 0.1018 0.009 10.737 0.000 0.083 0.120 - expersq -0.0034 0.001 -4.894 0.000 -0.005 -0.002 - ============================================================================== - Omnibus: 1249.642 Durbin-Watson: 0.954 - Prob(Omnibus): 0.000 Jarque-Bera (JB): 9627.436 - Skew: -1.152 Prob(JB): 0.00 - Kurtosis: 9.905 Cond. No. 86.4 - ============================================================================== - Notes: - [1] R² is computed without centering (uncentered) since the model does not contain a constant. - [2] Standard Errors assume that the covariance matrix of the errors is correctly specified. - """ - - if X.empty or Y.empty: - model = None - else: - with warnings.catch_warnings(record=True) as warning_messages: - model = sm.OLS(Y, X).fit() - if len(warning_messages) > 0: - console.print("Warnings:") - for warning in warning_messages: - console.print(f"[red]{warning.message}[/red]".replace("\n", "")) - - return model - - -@log_start_end(log=logger) -def get_pols(Y: pd.DataFrame, X: pd.DataFrame) -> Any: - """PooledOLS is just plain OLS that understands that various panel data structures. - It is useful as a base model. [Source: LinearModels] - - Parameters - ---------- - regression_variables : list - The regressions variables entered where the first variable is - the dependent variable. - data : dict - A dictionary containing the datasets. - - Returns - ------- - Tuple[DataFrame, Any, List[Any], Any] - The dataset used, - Dependent variable, - Independent variable, - PooledOLS model - """ - - if Y.empty or X.empty: - model = None - else: - with warnings.catch_warnings(record=True) as warning_messages: - exogenous = add_constant(X) - model = PooledOLS(Y, exogenous).fit() - - if len(warning_messages) > 0: - console.print("Warnings:") - for warning in warning_messages: - console.print(f"[red]{warning.message}[/red]".replace("\n", "")) - - return model - - -@log_start_end(log=logger) -def get_re(Y: pd.DataFrame, X: pd.DataFrame) -> Any: - """The random effects model is virtually identical to the pooled OLS model except that is accounts for the - structure of the model and so is more efficient. Random effects uses a quasi-demeaning strategy which - subtracts the time average of the within entity values to account for the common shock. [Source: LinearModels] - - Parameters - ---------- - regression_variables : list - The regressions variables entered where the first variable is - the dependent variable. - data : dict - A dictionary containing the datasets. - - Returns - ------- - Tuple[DataFrame, Any, List[Any], Any] - The dataset used, - Dependent variable, - Independent variable, - RandomEffects model - """ - - if X.empty or Y.empty: - model = None - else: - with warnings.catch_warnings(record=True) as warning_messages: - exogenous = add_constant(X) - model = RandomEffects(Y, exogenous).fit() - - if len(warning_messages) > 0: - console.print("Warnings:") - for warning in warning_messages: - console.print(f"[red]{warning.message}[/red]".replace("\n", "")) - - return model - - -@log_start_end(log=logger) -def get_bols(Y: pd.DataFrame, X: pd.DataFrame) -> Any: - """The between estimator is an alternative, usually less efficient estimator, can can be used to - estimate model parameters. It is particular simple since it first computes the time averages of - y and x and then runs a simple regression using these averages. [Source: LinearModels] - - Parameters - ---------- - regression_variables : list - The regressions variables entered where the first variable is - the dependent variable. - data : dict - A dictionary containing the datasets. - - Returns - ------- - Tuple[DataFrame, Any, List[Any], Any] - The dataset used, - Dependent variable, - Independent variable, - Between OLS model. - """ - if Y.empty or X.empty: - model = None - else: - with warnings.catch_warnings(record=True) as warning_messages: - exogenous = add_constant(X) - model = BetweenOLS(Y, exogenous).fit() - - if len(warning_messages) > 0: - console.print("Warnings:") - for warning in warning_messages: - console.print(f"[red]{warning.message}[/red]".replace("\n", "")) - - return model - - -@log_start_end(log=logger) -def get_fe( - Y: pd.DataFrame, - X: pd.DataFrame, - entity_effects: bool = False, - time_effects: bool = False, -) -> Any: - """When effects are correlated with the regressors the RE and BE estimators are not consistent. - The usual solution is to use Fixed Effects which are called entity_effects when applied to - entities and time_effects when applied to the time dimension. [Source: LinearModels] - - Parameters - ---------- - regression_variables : list - The regressions variables entered where the first variable is - the dependent variable. - data : dict - A dictionary containing the datasets. - entity_effects : bool - Whether to include entity effects - time_effects : bool - Whether to include time effects - - Returns - ------- - Tuple[DataFrame, Any, List[Any], Any] - The dataset used, - Dependent variable, - Independent variable, - PanelOLS model with Fixed Effects - """ - if X.empty or Y.empty: - model = None - else: - with warnings.catch_warnings(record=True) as warning_messages: - exogenous = add_constant(X) - model = PanelOLS( - Y, exogenous, entity_effects=entity_effects, time_effects=time_effects - ).fit() - - if len(warning_messages) > 0: - console.print("Warnings:") - for warning in warning_messages: - console.print(f"[red]{warning.message}[/red]".replace("\n", "")) - - return model - - -@log_start_end(log=logger) -def get_fdols(Y: pd.DataFrame, X: pd.DataFrame) -> Any: - """First differencing is an alternative to using fixed effects when there might be correlation. - When using first differences, time-invariant variables must be excluded. Additionally, - only one linear time-trending variable can be included since this will look like a constant. - This variable will soak up all time-trends in the data, and so interpretations of - these variable can be challenging. [Source: LinearModels] - - Parameters - ---------- - regression_variables : list - The regressions variables entered where the first variable is - the dependent variable. - data : dict - A dictionary containing the datasets. - - Returns - ------- - Tuple[DataFrame, Any, List[Any], Any] - The dataset used, - Dependent variable, - Independent variable, - First Difference OLS model - """ - if X.empty or Y.empty: - model = None - else: - with warnings.catch_warnings(record=True) as warning_messages: - model = FirstDifferenceOLS(Y, X).fit() - - if len(warning_messages) > 0: - console.print("Warnings:") - for warning in warning_messages: - console.print(f"[red]{warning.message}[/red]".replace("\n", "")) - - return model - - -@log_start_end(log=logger) -def get_comparison( - regressions: Dict, export: str = "", sheet_name: Optional[str] = None -): - """Compare regression results between Panel Data regressions. - - Parameters - ---------- - regressions : Dict - Dictionary with regression results. - export : str - Format to export data - - Returns - ------- - dict - Returns a PanelModelComparison which shows an overview of the different regression results. - """ - comparison = {} - - for regression_type, data in regressions.items(): - if regression_type == "OLS": - continue - if data["model"]: - comparison[regression_type] = data["model"] - - if not comparison: - # When the dictionary is empty, it means no Panel regression - # estimates are available and thus the function will have no output - return console.print( - "No Panel regression estimates available. Please use the " - "command 'panel' before using this command." - ) - - comparison_result = compare(comparison) - console.print(comparison_result) - - if export: - results_as_html = comparison_result.summary.tables[0].as_html() - df = pd.read_html(results_as_html, header=0, index_col=0)[0] - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "regressions_compare", - df, - sheet_name, - ) - - return comparison_result - - -@log_start_end(log=logger) -def get_dwat( - model: statsmodels.regression.linear_model.RegressionResultsWrapper, -) -> float: - """Calculate test statistics for Durbin Watson autocorrelation - - Parameters - ---------- - model : statsmodels.regression.linear_model.RegressionResultsWrapper - Previously fit statsmodels OLS. - - Returns - ------- - float - Test statistic of the Durbin Watson test. - - Notes - ------ - When using chart = True, the dependent variable in the regression must be passed to view the residuals - - Example - ------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.econometrics.load("wage_panel") - >>> Y, X = df["lwage"], df[["exper","educ"]] - >>> model = openbb.econometrics.ols(Y,X) - >>> durbin_watson_value = openbb.econometrics.dwat(model) - 0.96 - """ - # Durbin Watson test: The test statistic is approximately equal to 2*(1-r) where r is the - # sample autocorrelation of the residuals. Thus, for r == 0, indicating no serial correlation, - # the test statistic equals 2. This statistic will always be between 0 and 4. The closer - # to 0 the statistic, the more evidence for positive serial correlation. The closer to 4, - # the more evidence for negative serial correlation. - result = durbin_watson(model.resid) - - return round(result, 2) - - -@log_start_end(log=logger) -def get_bgod(model: pd.DataFrame, lags: int = 3) -> Tuple[float, float, float, float]: - """Calculate test statistics for autocorrelation - - Parameters - ---------- - model : OLS Model - OLS model that has been fit. - lags : int - The amount of lags. - - Returns - ------- - pd.DataFrame - Test results from the Breusch-Godfrey Test - """ - - lm_stat, p_value, f_stat, fp_value = acorr_breusch_godfrey(model, nlags=lags) - - return pd.DataFrame( - [lm_stat, p_value, f_stat, fp_value], - index=["lm-stat", "p-value", "f-stat", "fp-value"], - ) - - -@log_start_end(log=logger) -def get_bpag( - model: statsmodels.regression.linear_model.RegressionResultsWrapper, -) -> pd.DataFrame: - """Calculate test statistics for heteroscedasticity - - Parameters - ---------- - model : OLS Model - Model containing residual values. - - Returns - ------- - pd.DataFrame - Test results from the Breusch-Pagan Test - """ - - lm_stat, p_value, f_stat, fp_value = het_breuschpagan(model.resid, model.model.exog) - return pd.DataFrame( - [lm_stat, p_value, f_stat, fp_value], - index=["lm-stat", "p-value", "f-stat", "fp-value"], - ) diff --git a/openbb_terminal/econometrics/regression_view.py b/openbb_terminal/econometrics/regression_view.py deleted file mode 100644 index c619b69c230..00000000000 --- a/openbb_terminal/econometrics/regression_view.py +++ /dev/null @@ -1,247 +0,0 @@ -"""Regression View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd -import statsmodels - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.econometrics import regression_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_panel( - Y: pd.DataFrame, - X: pd.DataFrame, - regression_type: str = "OLS", - entity_effects: bool = False, - time_effects: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Based on the regression type, this function decides what regression to run. - - Parameters - ---------- - data : dict - A dictionary containing the datasets. - regression_variables : list - The regressions variables entered where the first variable is - the dependent variable. - each column/dataset combination. - regression_type: str - The type of regression you wish to execute. Choose from: - OLS, POLS, RE, BOLS, FE - entity_effects: bool - Whether to apply Fixed Effects on entities. - time_effects: bool - Whether to apply Fixed Effects on time. - export : str - Format to export data - - Returns - ------- - The dataset used, the dependent variable, the independent variable and - the regression model. - """ - model = regression_model.get_regressions_results( - Y, - X, - regression_type, - entity_effects, - time_effects, - ) - if regression_type != "OLS": - console.print(model) - - if export: - results_as_html = model.summary.tables[1].as_html() - df = pd.read_html(results_as_html, header=0, index_col=0)[0] - dependent = Y.columns[0] - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{dependent}_{regression_type}_regression", - df, - sheet_name, - ) - - return model - - -@log_start_end(log=logger) -def display_dwat( - model: statsmodels.regression.linear_model.RegressionResultsWrapper, - dependent_variable: pd.Series, - plot: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Show Durbin-Watson autocorrelation tests - - Parameters - ---------- - model : OLS Model - A fit statsmodels OLS model. - dependent_variable : pd.Series - The dependent variable for plotting - plot : bool - Whether to plot the residuals - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure( - yaxis_title="Residual", xaxis_title=dependent_variable.name.capitalize() - ) - autocorr = regression_model.get_dwat(model) - - if 1.5 < autocorr < 2.5: - console.print( - f"The result {autocorr} is within the range 1.5 and 2.5 which therefore indicates " - f"autocorrelation not to be problematic." - ) - else: - console.print( - f"The result {autocorr} is outside the range 1.5 and 2.5 and could " - f"be problematic. Please consider lags of the dependent or independent variable." - ) - - if not fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{dependent_variable.name}_dwat", - autocorr, - sheet_name, - ) - - if plot or fig.is_image_export(export): - fig.set_title("Plot of Residuals") - - fig.add_scatter( - x=dependent_variable, - y=model.resid, - mode="markers", - ) - fig.add_hline(y=0, line=dict(color="red", dash="dash")) - - if fig.is_image_export(export): - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{dependent_variable.name}_residuals", - dependent_variable, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -def display_bgod( - model: statsmodels.regression.linear_model.RegressionResultsWrapper, - lags: int = 3, - export: str = "", - sheet_name: Optional[str] = None, -): - """Show Breusch-Godfrey autocorrelation test - - Parameters - ---------- - model : OLS Model - OLS model that has been fit. - lags : int - The amount of lags included. - export : str - Format to export data - """ - df = regression_model.get_bgod(model, lags) - - print_rich_table( - df, - headers=list(["Breusch-Godfrey"]), - show_index=True, - title=f"Breusch-Godfrey autocorrelation test [Lags: {lags}]", - export=bool(export), - ) - p_value = df.loc["p-value"][0] - if p_value > 0.05: - console.print( - f"{round(p_value, 2)} indicates the autocorrelation. Consider re-estimating with " - "clustered standard errors and applying the Random Effects or Fixed Effects model." - ) - else: - console.print( - f"The result {round(p_value, 2)} indicates no existence of autocorrelation." - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "results_bgod", - df, - sheet_name, - ) - - console.print() - - -@log_start_end(log=logger) -def display_bpag( - model: statsmodels.regression.linear_model.RegressionResultsWrapper, - export: str = "", - sheet_name: Optional[str] = None, -): - """Show Breusch-Pagan heteroscedasticity test - - Parameters - ---------- - model : OLS Model - OLS model that has been fit. - export : str - Format to export data - """ - df = regression_model.get_bpag(model) - - print_rich_table( - df, - headers=list(["Breusch-Pagan"]), - show_index=True, - title="Breusch-Pagan heteroscedasticity test", - export=bool(export), - ) - p_value = df.loc["p-value"][0] - if p_value > 0.05: - console.print( - f"{round(p_value, 2)} indicates heteroscedasticity. Consider taking the log " - f"or a rate for the dependent variable." - ) - else: - console.print( - f"The result {round(p_value, 2)} indicates no existence of heteroscedasticity." - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "results_bpag", - df, - sheet_name, - ) - - console.print() diff --git a/openbb_terminal/economy/README.md b/openbb_terminal/economy/README.md deleted file mode 100644 index 64ace32d8e9..00000000000 --- a/openbb_terminal/economy/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Resources - -* Federal Reserve Economic Data : diff --git a/openbb_terminal/economy/__init__.py b/openbb_terminal/economy/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/economy/commodity_model.py b/openbb_terminal/economy/commodity_model.py deleted file mode 100644 index 883f42666c6..00000000000 --- a/openbb_terminal/economy/commodity_model.py +++ /dev/null @@ -1,59 +0,0 @@ -import logging -import re - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -def format_number(text: str) -> float: - numbers = re.findall(r"[-+]?(?:\d*\.\d+|\d+)", text) - if "t" in text.lower(): - multiplier = 1_000_000_000_000 - elif "b" in text.lower(): - multiplier = 1_000_000_000 - elif "m" in text.lower(): - multiplier = 1_000_000 - else: - multiplier = 1 - - return round(float(numbers[0]) * multiplier, 2) - - -@log_start_end(log=logger) -def get_debt() -> pd.DataFrame: - """Retrieves national debt information for various countries. [Source: wikipedia.org] - - Returns - ------- - pd.DataFrame - Country, Debt [$], Debt [% of GDP], Debt per capita [$], Debt per capita [% of GDP] - """ - url = "https://en.wikipedia.org/wiki/List_of_countries_by_external_debt" - response = request(url, headers={"User-Agent": get_user_agent()}) - df = pd.read_html(response.content)[0] - df = df.rename( - columns={ - "Country/Region": "Country", - "External debt US dollars": "USD Debt", - "Per capitaUS dollars": "Per Capita", - "Per capita US dollars": "USD Per Capita", - "Date": "As Of", - } - ) - df["USD Debt"] = df["USD Debt"].apply(lambda x: format_number(x)).astype("int64") - df["USD Per Capita"] = df["USD Per Capita"].astype("int64") - df["Rank"] = df["USD Debt"].rank(ascending=False).astype("int64") - date = df["As Of"].astype(str) - dates = [] - for i in date.index: - dates.append(date[i].split("[")[0]) - df["As Of"] = dates - df["As Of"] = df["As Of"].str.replace("31 September", "30 September").str.strip() - indexes = ["Rank", "As Of", "Country", "USD Per Capita", "USD Debt", "% of GDP"] - df = df[indexes] - - return df diff --git a/openbb_terminal/economy/commodity_view.py b/openbb_terminal/economy/commodity_view.py deleted file mode 100644 index 87e57291386..00000000000 --- a/openbb_terminal/economy/commodity_view.py +++ /dev/null @@ -1,61 +0,0 @@ -import logging -import os -from typing import Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.economy import commodity_model -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -def format_large_numbers(num: float) -> str: - """Converts floats into strings and adds commas to the number - - Parameters - ---------- - num: float - The number to convert - - Returns - ------- - num: str - The formatted number - """ - return f"{num:,}" - - -@log_start_end(log=logger) -def display_debt(export: str = "", sheet_name: Optional[str] = None, limit: int = 20): - """Displays external debt for given countries [Source: Wikipedia] - - Parameters - ---------- - export : str - The path to export to - limit : int - The number of countries to show - """ - debt_df = commodity_model.get_debt() - - if not get_current_user().preferences.USE_INTERACTIVE_DF: - for col in ["USD Debt", "USD Per Capita"]: - debt_df[col] = debt_df[col].apply(lambda x: format_large_numbers(x)) - - print_rich_table( - debt_df, - show_index=False, - headers=debt_df.columns, - title="External Debt (USD)", - export=bool(export), - limit=limit, - ) - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cdebt", - debt_df, - sheet_name, - ) diff --git a/openbb_terminal/economy/datasets/NASDAQ_CountryCodes.csv b/openbb_terminal/economy/datasets/NASDAQ_CountryCodes.csv deleted file mode 100644 index c052122774c..00000000000 --- a/openbb_terminal/economy/datasets/NASDAQ_CountryCodes.csv +++ /dev/null @@ -1,59 +0,0 @@ -,Country,Code -0,Vietnam,VNM -1,Argentina,ARG -2,Australia,AUS -3,Brazil,BRA -4,Britain,GBR -5,Canada,CAN -6,Chile,CHL -7,China,CHN -8,Colombia,COL -9,Costa Rica,CRI -10,Czech Republic,CZE -11,Denmark,DNK -12,Egypt,EGY -13,Euro area,EUR -14,Hong Kong,HKG -15,Hungary,HUN -16,India,IND -17,Indonesia,IDN -18,Israel,ISR -19,Japan,JPN -20,Lithuania,LTU -21,Malaysia,MYS -22,Mexico,MEX -23,New Zealand,NZL -24,Norway,NOR -25,Pakistan,PAK -26,Peru,PER -27,Philippines,PHL -28,Poland,POL -29,Russia,RUS -30,Saudi Arabia,SAU -31,Singapore,SIN -32,South Africa,ZAF -33,South Korea,KOR -34,Sri Lanka,LKA -35,Sweden,SWE -36,Switzerland,CHE -37,Taiwan,ROC -38,Thailand,THA -39,Turkey,TUR -40,UAE,UAE -41,Ukraine,UKR -42,United States,USA -43,Uruguay,URY -44,Venezuela,VEN -45,Austria,AUT -46,Belgium,BEL -47,Estonia,EST -48,Finland,FIN -49,France,FRA -50,Germany,DEU -51,Greece,GRC -52,Ireland,IRL -53,Italy,ITA -54,Netherlands,NLD -55,Portugal,PRT -56,Spain,ESP -57,Latvia,LVA diff --git a/openbb_terminal/economy/datasets/cpi.csv b/openbb_terminal/economy/datasets/cpi.csv deleted file mode 100644 index 7671215ca5a..00000000000 --- a/openbb_terminal/economy/datasets/cpi.csv +++ /dev/null @@ -1,256 +0,0 @@ -title,country,frequency,units,series_id -Consumer Price Index for Australia,australia,annual,growth_previous,CPALTT01AUA657N -Consumer Price Index for Australia,australia,annual,growth_same,CPALTT01AUA659N -Consumer Price Index for Australia,australia,quarterly,growth_previous,CPALTT01AUQ657N -Consumer Price Index for Australia,australia,quarterly,growth_same,CPALTT01AUQ659N -Consumer Price Index for Austria,austria,annual,growth_previous,CPALTT01ATA657N -Consumer Price Index for Austria,austria,annual,growth_same,CPALTT01ATA659N -Consumer Price Index for Austria,austria,monthly,growth_previous,CPALTT01ATM657N -Consumer Price Index for Austria,austria,monthly,growth_same,CPALTT01ATM659N -Consumer Price Index for Austria,austria,quarterly,growth_previous,CPALTT01ATQ657N -Consumer Price Index for Austria,austria,quarterly,growth_same,CPALTT01ATQ659N -Consumer Price Index for Belgium,belgium,annual,growth_previous,CPALTT01BEA657N -Consumer Price Index for Belgium,belgium,annual,growth_same,CPALTT01BEA659N -Consumer Price Index for Belgium,belgium,monthly,growth_previous,CPALTT01BEM657N -Consumer Price Index for Belgium,belgium,monthly,growth_same,CPALTT01BEM659N -Consumer Price Index for Belgium,belgium,quarterly,growth_previous,CPALTT01BEQ657N -Consumer Price Index for Belgium,belgium,quarterly,growth_same,CPALTT01BEQ659N -Consumer Price Index for Brazil,brazil,annual,growth_previous,CPALTT01BRA657N -Consumer Price Index for Brazil,brazil,annual,growth_same,CPALTT01BRA659N -Consumer Price Index for Brazil,brazil,monthly,growth_previous,CPALTT01BRM657N -Consumer Price Index for Brazil,brazil,monthly,growth_same,CPALTT01BRM659N -Consumer Price Index for Brazil,brazil,quarterly,growth_previous,CPALTT01BRQ657N -Consumer Price Index for Brazil,brazil,quarterly,growth_same,CPALTT01BRQ659N -Consumer Price Index for Canada,canada,annual,index_2015,CPALCY01CAA661N -Consumer Price Index for Canada,canada,monthly,index_2015,CPALCY01CAM661N -Consumer Price Index for Canada,canada,quarterly,index_2015,CPALCY01CAQ661N -Consumer Price Index for Canada,canada,annual,growth_previous,CPALTT01CAA657N -Consumer Price Index for Canada,canada,annual,growth_same,CPALTT01CAA659N -Consumer Price Index for Canada,canada,monthly,growth_previous,CPALTT01CAM657N -Consumer Price Index for Canada,canada,monthly,growth_same,CPALTT01CAM659N -Consumer Price Index for Canada,canada,quarterly,growth_previous,CPALTT01CAQ657N -Consumer Price Index for Canada,canada,quarterly,growth_same,CPALTT01CAQ659N -Consumer Price Index for Chile,chile,annual,growth_previous,CPALTT01CLA657N -Consumer Price Index for Chile,chile,annual,growth_same,CPALTT01CLA659N -Consumer Price Index for Chile,chile,monthly,growth_previous,CPALTT01CLM657N -Consumer Price Index for Chile,chile,monthly,growth_same,CPALTT01CLM659N -Consumer Price Index for Chile,chile,quarterly,growth_previous,CPALTT01CLQ657N -Consumer Price Index for Chile,chile,quarterly,growth_same,CPALTT01CLQ659N -Consumer Price Index for China,china,annual,growth_previous,CPALTT01CNA657N -Consumer Price Index for China,china,monthly,growth_previous,CPALTT01CNM657N -Consumer Price Index for China,china,monthly,growth_same,CPALTT01CNM659N -Consumer Price Index for China,china,quarterly,growth_previous,CPALTT01CNQ657N -Consumer Price Index for China,china,quarterly,growth_same,CPALTT01CNQ659N -Consumer Price Index for Denmark,denmark,annual,growth_previous,CPALTT01DKA657N -Consumer Price Index for Denmark,denmark,annual,growth_same,CPALTT01DKA659N -Consumer Price Index for Denmark,denmark,monthly,growth_previous,CPALTT01DKM657N -Consumer Price Index for Denmark,denmark,monthly,growth_same,CPALTT01DKM659N -Consumer Price Index for Denmark,denmark,quarterly,growth_previous,CPALTT01DKQ657N -Consumer Price Index for Denmark,denmark,quarterly,growth_same,CPALTT01DKQ659N -Consumer Price Index for Estonia,estonia,annual,growth_previous,CPALTT01EEA657N -Consumer Price Index for Estonia,estonia,annual,growth_same,CPALTT01EEA659N -Consumer Price Index for Estonia,estonia,annual,index_2015,CPALTT01EEA661N -Consumer Price Index for Estonia,estonia,monthly,growth_previous,CPALTT01EEM657N -Consumer Price Index for Estonia,estonia,monthly,growth_same,CPALTT01EEM659N -Consumer Price Index for Estonia,estonia,monthly,index_2015,CPALTT01EEM661N -Consumer Price Index for Estonia,estonia,quarterly,growth_previous,CPALTT01EEQ657N -Consumer Price Index for Estonia,estonia,quarterly,growth_same,CPALTT01EEQ659N -Consumer Price Index for Estonia,estonia,quarterly,index_2015,CPALTT01EEQ661N -Consumer Price Index for Finland,finland,annual,growth_previous,CPALTT01FIA657N -Consumer Price Index for Finland,finland,annual,growth_same,CPALTT01FIA659N -Consumer Price Index for Finland,finland,monthly,growth_previous,CPALTT01FIM657N -Consumer Price Index for Finland,finland,monthly,growth_same,CPALTT01FIM659N -Consumer Price Index for Finland,finland,quarterly,growth_previous,CPALTT01FIQ657N -Consumer Price Index for Finland,finland,quarterly,growth_same,CPALTT01FIQ659N -Consumer Price Index for France,france,annual,growth_previous,CPALTT01FRA657N -Consumer Price Index for France,france,annual,growth_same,CPALTT01FRA659N -Consumer Price Index for France,france,monthly,growth_previous,CPALTT01FRM657N -Consumer Price Index for France,france,monthly,growth_same,CPALTT01FRM659N -Consumer Price Index for France,france,quarterly,growth_previous,CPALTT01FRQ657N -Consumer Price Index for France,france,quarterly,growth_same,CPALTT01FRQ659N -Consumer Price Index for Germany,germany,annual,growth_previous,CPALTT01DEA657N -Consumer Price Index for Germany,germany,annual,growth_same,CPALTT01DEA659N -Consumer Price Index for Germany,germany,monthly,growth_previous,CPALTT01DEM657N -Consumer Price Index for Germany,germany,monthly,growth_same,CPALTT01DEM659N -Consumer Price Index for Germany,germany,quarterly,growth_previous,CPALTT01DEQ657N -Consumer Price Index for Germany,germany,quarterly,growth_same,CPALTT01DEQ659N -Consumer Price Index for Greece,greece,annual,growth_previous,CPALTT01GRA657N -Consumer Price Index for Greece,greece,annual,growth_same,CPALTT01GRA659N -Consumer Price Index for Greece,greece,monthly,growth_previous,CPALTT01GRM657N -Consumer Price Index for Greece,greece,monthly,growth_same,CPALTT01GRM659N -Consumer Price Index for Greece,greece,quarterly,growth_previous,CPALTT01GRQ657N -Consumer Price Index for Greece,greece,quarterly,growth_same,CPALTT01GRQ659N -Consumer Price Index for Hungary,hungary,annual,growth_previous,CPALTT01HUA657N -Consumer Price Index for Hungary,hungary,annual,growth_same,CPALTT01HUA659N -Consumer Price Index for Hungary,hungary,monthly,growth_previous,CPALTT01HUM657N -Consumer Price Index for Hungary,hungary,monthly,growth_same,CPALTT01HUM659N -Consumer Price Index for Hungary,hungary,quarterly,growth_previous,CPALTT01HUQ657N -Consumer Price Index for Hungary,hungary,quarterly,growth_same,CPALTT01HUQ659N -Consumer Price Index for Iceland,iceland,annual,growth_previous,CPALTT01ISA657N -Consumer Price Index for Iceland,iceland,annual,growth_same,CPALTT01ISA659N -Consumer Price Index for Iceland,iceland,monthly,growth_previous,CPALTT01ISM657N -Consumer Price Index for Iceland,iceland,monthly,growth_same,CPALTT01ISM659N -Consumer Price Index for Iceland,iceland,quarterly,growth_previous,CPALTT01ISQ657N -Consumer Price Index for Iceland,iceland,quarterly,growth_same,CPALTT01ISQ659N -Consumer Price Index for India,india,annual,growth_previous,CPALTT01INA657N -Consumer Price Index for India,india,annual,growth_same,CPALTT01INA659N -Consumer Price Index for India,india,monthly,growth_previous,CPALTT01INM657N -Consumer Price Index for India,india,monthly,growth_same,CPALTT01INM659N -Consumer Price Index for India,india,quarterly,growth_previous,CPALTT01INQ657N -Consumer Price Index for India,india,quarterly,growth_same,CPALTT01INQ659N -Consumer Price Index for Indonesia,indonesia,annual,growth_previous,CPALTT01IDA657N -Consumer Price Index for Indonesia,indonesia,annual,growth_same,CPALTT01IDA659N -Consumer Price Index for Indonesia,indonesia,monthly,growth_previous,CPALTT01IDM657N -Consumer Price Index for Indonesia,indonesia,monthly,growth_same,CPALTT01IDM659N -Consumer Price Index for Indonesia,indonesia,quarterly,growth_previous,CPALTT01IDQ657N -Consumer Price Index for Indonesia,indonesia,quarterly,growth_same,CPALTT01IDQ659N -Consumer Price Index for Ireland,ireland,annual,growth_previous,CPALTT01IEA657N -Consumer Price Index for Ireland,ireland,annual,growth_same,CPALTT01IEA659N -Consumer Price Index for Ireland,ireland,monthly,growth_previous,CPALTT01IEM657N -Consumer Price Index for Ireland,ireland,monthly,growth_same,CPALTT01IEM659N -Consumer Price Index for Ireland,ireland,quarterly,growth_previous,CPALTT01IEQ657N -Consumer Price Index for Ireland,ireland,quarterly,growth_same,CPALTT01IEQ659N -Consumer Price Index for Israel,israel,annual,growth_previous,CPALTT01ILA657N -Consumer Price Index for Israel,israel,annual,growth_same,CPALTT01ILA659N -Consumer Price Index for Israel,israel,monthly,growth_previous,CPALTT01ILM657N -Consumer Price Index for Israel,israel,monthly,growth_same,CPALTT01ILM659N -Consumer Price Index for Israel,israel,quarterly,growth_previous,CPALTT01ILQ657N -Consumer Price Index for Israel,israel,quarterly,growth_same,CPALTT01ILQ659N -Consumer Price Index for Italy,italy,annual,growth_previous,CPALTT01ITA657N -Consumer Price Index for Italy,italy,annual,growth_same,CPALTT01ITA659N -Consumer Price Index for Italy,italy,monthly,growth_previous,CPALTT01ITM657N -Consumer Price Index for Italy,italy,monthly,growth_same,CPALTT01ITM659N -Consumer Price Index for Italy,italy,quarterly,growth_previous,CPALTT01ITQ657N -Consumer Price Index for Italy,italy,quarterly,growth_same,CPALTT01ITQ659N -Consumer Price Index for Japan,japan,monthly,index_2015,CPALCY01JPM661N -Consumer Price Index for Japan,japan,quarterly,index_2015,CPALCY01JPQ661N -Consumer Price Index for Japan,japan,monthly,growth_previous,CPALTT01JPM657N -Consumer Price Index for Japan,japan,monthly,growth_same,CPALTT01JPM659N -Consumer Price Index for Japan,japan,monthly,index_2015,CPALTT01JPM661S -Consumer Price Index for Japan,japan,quarterly,growth_previous,CPALTT01JPQ657N -Consumer Price Index for Japan,japan,quarterly,growth_same,CPALTT01JPQ659N -Consumer Price Index for Japan,japan,quarterly,index_2015,CPALTT01JPQ661S -Consumer Price Index for Luxembourg,luxembourg,annual,growth_previous,CPALTT01LUA657N -Consumer Price Index for Luxembourg,luxembourg,annual,growth_same,CPALTT01LUA659N -Consumer Price Index for Luxembourg,luxembourg,monthly,growth_previous,CPALTT01LUM657N -Consumer Price Index for Luxembourg,luxembourg,monthly,growth_same,CPALTT01LUM659N -Consumer Price Index for Luxembourg,luxembourg,quarterly,growth_previous,CPALTT01LUQ657N -Consumer Price Index for Luxembourg,luxembourg,quarterly,growth_same,CPALTT01LUQ659N -Consumer Price Index for Mexico,mexico,annual,index_2015,CPALCY01MXA661N -Consumer Price Index for Mexico,mexico,monthly,index_2015,CPALCY01MXM661N -Consumer Price Index for Mexico,mexico,quarterly,index_2015,CPALCY01MXQ661N -Consumer Price Index for Mexico,mexico,annual,growth_previous,CPALTT01MXA657N -Consumer Price Index for Mexico,mexico,annual,growth_same,CPALTT01MXA659N -Consumer Price Index for Mexico,mexico,monthly,growth_previous,CPALTT01MXM657N -Consumer Price Index for Mexico,mexico,monthly,growth_same,CPALTT01MXM659N -Consumer Price Index for Mexico,mexico,quarterly,growth_previous,CPALTT01MXQ657N -Consumer Price Index for Mexico,mexico,quarterly,growth_same,CPALTT01MXQ659N -Consumer Price Index for New Zealand,new_zealand,annual,index_2015,CPALCY01NZA661N -Consumer Price Index for New Zealand,new_zealand,quarterly,index_2015,CPALCY01NZQ661N -Consumer Price Index for New Zealand,new_zealand,annual,growth_previous,CPALTT01NZA657N -Consumer Price Index for New Zealand,new_zealand,annual,growth_same,CPALTT01NZA659N -Consumer Price Index for New Zealand,new_zealand,quarterly,growth_previous,CPALTT01NZQ657N -Consumer Price Index for New Zealand,new_zealand,quarterly,growth_same,CPALTT01NZQ659N -Consumer Price Index for Norway,norway,annual,growth_previous,CPALTT01NOA657N -Consumer Price Index for Norway,norway,annual,growth_same,CPALTT01NOA659N -Consumer Price Index for Norway,norway,monthly,growth_previous,CPALTT01NOM657N -Consumer Price Index for Norway,norway,monthly,growth_same,CPALTT01NOM659N -Consumer Price Index for Norway,norway,quarterly,growth_previous,CPALTT01NOQ657N -Consumer Price Index for Norway,norway,quarterly,growth_same,CPALTT01NOQ659N -Consumer Price Index for Poland,poland,annual,growth_previous,CPALTT01PLA657N -Consumer Price Index for Poland,poland,annual,growth_same,CPALTT01PLA659N -Consumer Price Index for Poland,poland,monthly,growth_previous,CPALTT01PLM657N -Consumer Price Index for Poland,poland,monthly,growth_same,CPALTT01PLM659N -Consumer Price Index for Poland,poland,quarterly,growth_previous,CPALTT01PLQ657N -Consumer Price Index for Poland,poland,quarterly,growth_same,CPALTT01PLQ659N -Consumer Price Index for Portugal,portugal,annual,growth_previous,CPALTT01PTA657N -Consumer Price Index for Portugal,portugal,annual,growth_same,CPALTT01PTA659N -Consumer Price Index for Portugal,portugal,monthly,growth_previous,CPALTT01PTM657N -Consumer Price Index for Portugal,portugal,monthly,growth_same,CPALTT01PTM659N -Consumer Price Index for Portugal,portugal,quarterly,growth_previous,CPALTT01PTQ657N -Consumer Price Index for Portugal,portugal,quarterly,growth_same,CPALTT01PTQ659N -Consumer Price Index for Slovenia,slovenia,annual,growth_previous,CPALTT01SIA657N -Consumer Price Index for Slovenia,slovenia,annual,growth_same,CPALTT01SIA659N -Consumer Price Index for Slovenia,slovenia,monthly,growth_previous,CPALTT01SIM657N -Consumer Price Index for Slovenia,slovenia,monthly,growth_same,CPALTT01SIM659N -Consumer Price Index for Slovenia,slovenia,quarterly,growth_previous,CPALTT01SIQ657N -Consumer Price Index for Slovenia,slovenia,quarterly,growth_same,CPALTT01SIQ659N -Consumer Price Index for South Africa,south_africa,annual,growth_previous,CPALTT01ZAA657N -Consumer Price Index for South Africa,south_africa,annual,growth_same,CPALTT01ZAA659N -Consumer Price Index for South Africa,south_africa,monthly,growth_previous,CPALTT01ZAM657N -Consumer Price Index for South Africa,south_africa,monthly,growth_same,CPALTT01ZAM659N -Consumer Price Index for South Africa,south_africa,quarterly,growth_previous,CPALTT01ZAQ657N -Consumer Price Index for South Africa,south_africa,quarterly,growth_same,CPALTT01ZAQ659N -Consumer Price Index for Spain,spain,annual,growth_previous,CPALTT01ESA657N -Consumer Price Index for Spain,spain,annual,growth_same,CPALTT01ESA659N -Consumer Price Index for Spain,spain,monthly,growth_previous,CPALTT01ESM657N -Consumer Price Index for Spain,spain,monthly,growth_same,CPALTT01ESM659N -Consumer Price Index for Spain,spain,quarterly,growth_previous,CPALTT01ESQ657N -Consumer Price Index for Spain,spain,quarterly,growth_same,CPALTT01ESQ659N -Consumer Price Index for Sweden,sweden,annual,growth_previous,CPALTT01SEA657N -Consumer Price Index for Sweden,sweden,annual,growth_same,CPALTT01SEA659N -Consumer Price Index for Sweden,sweden,monthly,growth_previous,CPALTT01SEM657N -Consumer Price Index for Sweden,sweden,monthly,growth_same,CPALTT01SEM659N -Consumer Price Index for Sweden,sweden,quarterly,growth_previous,CPALTT01SEQ657N -Consumer Price Index for Sweden,sweden,quarterly,growth_same,CPALTT01SEQ659N -Consumer Price Index for Switzerland,switzerland,annual,growth_previous,CPALTT01CHA657N -Consumer Price Index for Switzerland,switzerland,annual,growth_same,CPALTT01CHA659N -Consumer Price Index for Switzerland,switzerland,monthly,growth_previous,CPALTT01CHM657N -Consumer Price Index for Switzerland,switzerland,monthly,growth_same,CPALTT01CHM659N -Consumer Price Index for Switzerland,switzerland,quarterly,growth_previous,CPALTT01CHQ657N -Consumer Price Index for Switzerland,switzerland,quarterly,growth_same,CPALTT01CHQ659N -Consumer Price Index for the Czech Republic,czech_republic,annual,growth_previous,CPALTT01CZA657N -Consumer Price Index for the Czech Republic,czech_republic,annual,growth_same,CPALTT01CZA659N -Consumer Price Index for the Czech Republic,czech_republic,monthly,growth_previous,CPALTT01CZM657N -Consumer Price Index for the Czech Republic,czech_republic,monthly,growth_same,CPALTT01CZM659N -Consumer Price Index for the Czech Republic,czech_republic,quarterly,growth_previous,CPALTT01CZQ657N -Consumer Price Index for the Czech Republic,czech_republic,quarterly,growth_same,CPALTT01CZQ659N -Consumer Price Index for the Netherlands,netherlands,annual,growth_previous,CPALTT01NLA657N -Consumer Price Index for the Netherlands,netherlands,annual,growth_same,CPALTT01NLA659N -Consumer Price Index for the Netherlands,netherlands,monthly,growth_previous,CPALTT01NLM657N -Consumer Price Index for the Netherlands,netherlands,monthly,growth_same,CPALTT01NLM659N -Consumer Price Index for the Netherlands,netherlands,quarterly,growth_previous,CPALTT01NLQ657N -Consumer Price Index for the Netherlands,netherlands,quarterly,growth_same,CPALTT01NLQ659N -Consumer Price Index for the Republic of Korea,korea,annual,growth_previous,CPALTT01KRA657N -Consumer Price Index for the Republic of Korea,korea,annual,growth_same,CPALTT01KRA659N -Consumer Price Index for the Republic of Korea,korea,monthly,growth_previous,CPALTT01KRM657N -Consumer Price Index for the Republic of Korea,korea,monthly,growth_same,CPALTT01KRM659N -Consumer Price Index for the Republic of Korea,korea,quarterly,growth_previous,CPALTT01KRQ657N -Consumer Price Index for the Republic of Korea,korea,quarterly,growth_same,CPALTT01KRQ659N -Consumer Price Index for the Russian Federation,russian_federation,monthly,growth_previous,CPALTT01RUM657N -Consumer Price Index for the Russian Federation,russian_federation,monthly,growth_same,CPALTT01RUM659N -Consumer Price Index for the Russian Federation,russian_federation,quarterly,growth_previous,CPALTT01RUQ657N -Consumer Price Index for the Russian Federation,russian_federation,quarterly,growth_same,CPALTT01RUQ659N -Consumer Price Index for the Slovak Republic,slovak_republic,annual,growth_previous,CPALTT01SKA657N -Consumer Price Index for the Slovak Republic,slovak_republic,annual,growth_same,CPALTT01SKA659N -Consumer Price Index for the Slovak Republic,slovak_republic,monthly,growth_previous,CPALTT01SKM657N -Consumer Price Index for the Slovak Republic,slovak_republic,monthly,growth_same,CPALTT01SKM659N -Consumer Price Index for the Slovak Republic,slovak_republic,quarterly,growth_previous,CPALTT01SKQ657N -Consumer Price Index for the Slovak Republic,slovak_republic,quarterly,growth_same,CPALTT01SKQ659N -Consumer Price Index for the United Kingdom,the united kingdom,annual,growth_previous,CPALTT01GBA657N -Consumer Price Index for the United Kingdom,united_kingdom,annual,growth_same,CPALTT01GBA659N -Consumer Price Index for the United Kingdom,united_kingdom,monthly,growth_previous,CPALTT01GBM657N -Consumer Price Index for the United Kingdom,united_kingdom,monthly,growth_same,CPALTT01GBM659N -Consumer Price Index for the United Kingdom,united_kingdom,quarterly,growth_previous,CPALTT01GBQ657N -Consumer Price Index for the United Kingdom,united_kingdom,quarterly,growth_same,CPALTT01GBQ659N -Consumer Price Index for the United States,united_states,annual,index_2015,CPALCY01USA661N -Consumer Price Index for the United States,united_states,monthly,index_2015,CPALCY01USM661N -Consumer Price Index for the United States,united_states,quarterly,index_2015,CPALCY01USQ661N -Consumer Price Index for the United States,united_states,annual,growth_previous,CPALTT01USA657N -Consumer Price Index for the United States,united_states,annual,growth_same,CPALTT01USA659N -Consumer Price Index for the United States,united_states,annual,index_2015,CPALTT01USA661S -Consumer Price Index for the United States,united_states,monthly,growth_previous,CPALTT01USM657N -Consumer Price Index for the United States,united_states,monthly,growth_same,CPALTT01USM659N -Consumer Price Index for the United States,united_states,monthly,index_2015,CPALTT01USM661S -Consumer Price Index for the United States,united_states,quarterly,growth_previous,CPALTT01USQ657N -Consumer Price Index for the United States,united_states,quarterly,growth_same,CPALTT01USQ659N -Consumer Price Index for the United States,united_states,quarterly,index_2015,CPALTT01USQ661S -Consumer Price Index for Turkey,turkey,annual,index_2015,CPALCY01TRA661N -Consumer Price Index for Turkey,turkey,monthly,index_2015,CPALCY01TRM661N -Consumer Price Index for Turkey,turkey,quarterly,index_2015,CPALCY01TRQ661N -Consumer Price Index for Turkey,turkey,annual,growth_previous,CPALTT01TRA657N -Consumer Price Index for Turkey,turkey,annual,growth_same,CPALTT01TRA659N -Consumer Price Index for Turkey,turkey,monthly,growth_previous,CPALTT01TRM657N -Consumer Price Index for Turkey,turkey,monthly,growth_same,CPALTT01TRM659N -Consumer Price Index for Turkey,turkey,quarterly,growth_previous,CPALTT01TRQ657N -Consumer Price Index for Turkey,turkey,quarterly,growth_same,CPALTT01TRQ659N \ No newline at end of file diff --git a/openbb_terminal/economy/datasets/harmonized_cpi.csv b/openbb_terminal/economy/datasets/harmonized_cpi.csv deleted file mode 100644 index 1d37f47343e..00000000000 --- a/openbb_terminal/economy/datasets/harmonized_cpi.csv +++ /dev/null @@ -1,137 +0,0 @@ -title,country,frequency,units,series_id -Harmonised CPI for Austria,austria,annual,growth_previous,CPHPTT01ATA657N -Harmonised CPI for Austria,austria,annual,growth_same,CPHPTT01ATA659N -Harmonised CPI for Austria,austria,monthly,growth_previous,CPHPTT01ATM657N -Harmonised CPI for Austria,austria,monthly,growth_same,CPHPTT01ATM659N -Harmonised CPI for Austria,austria,monthly,index_2015,CP0000ATM086NEST -Harmonised CPI for Belgium,belgium,annual,growth_previous,CPHPTT01BEA657N -Harmonised CPI for Belgium,belgium,annual,growth_same,CPHPTT01BEA659N -Harmonised CPI for Belgium,belgium,monthly,growth_previous,CPHPTT01BEM657N -Harmonised CPI for Belgium,belgium,monthly,growth_same,CPHPTT01BEM659N -Harmonised CPI for Belgium,belgium,monthly,index_2015,CP0000BEM086NEST -Harmonised CPI for Bulgaria,bulgaria,monthly,index_2015,CP0000BGM086NEST -Harmonised CPI for Croatia,croatia,monthly,index_2015,CP0000HRM086NEST -Harmonised CPI for Cyprus,cyprus,monthly,index_2015,CP0000CYM086NEST -Harmonised CPI for the Czech Republic,czech_republic,annual,growth_previous,CPHPTT01CZA657N -Harmonised CPI for the Czech Republic,czech_republic,annual,growth_same,CPHPTT01CZA659N -Harmonised CPI for the Czech Republic,czech_republic,monthly,growth_previous,CPHPTT01CZM657N -Harmonised CPI for the Czech Republic,czech_republic,monthly,growth_same,CPHPTT01CZM659N -Harmonised CPI for Czech Republic,czech_republic,monthly,index_2015,CP0000CZM086NEST -Harmonised CPI for Denmark,denmark,annual,growth_previous,CPHPTT01DKA657N -Harmonised CPI for Denmark,denmark,annual,growth_same,CPHPTT01DKA659N -Harmonised CPI for Denmark,denmark,monthly,growth_previous,CPHPTT01DKM657N -Harmonised CPI for Denmark,denmark,monthly,growth_same,CPHPTT01DKM659N -Harmonised CPI for Denmark,denmark,monthly,index_2015,CP0000DKM086NEST -Harmonised CPI for Estonia,estonia,annual,growth_previous,CPHPTT01EEA657N -Harmonised CPI for Estonia,estonia,annual,growth_same,CPHPTT01EEA659N -Harmonised CPI for Estonia,estonia,annual,index_2015,CPHPTT01EEA661N -Harmonised CPI for Estonia,estonia,monthly,growth_previous,CPHPTT01EEM657N -Harmonised CPI for Estonia,estonia,monthly,growth_same,CPHPTT01EEM659N -Harmonised CPI for Estonia,estonia,monthly,index_2015,CPHPTT01EEM661N -Harmonised CPI for the Euro Area ,euro_area,annual,growth_previous,CPHPTT01EZA657N -Harmonised CPI for the Euro Area ,euro_area,annual,growth_same,CPHPTT01EZA659N -Harmonised CPI for the Euro Area ,euro_area,annual,index_2015,CPHPTT01EZA661N -Harmonised CPI for the Euro Area ,euro_area,monthly,growth_previous,CPHPTT01EZM657N -Harmonised CPI for the Euro Area ,euro_area,monthly,growth_same,CPHPTT01EZM659N -Harmonised CPI for the Euro Area ,euro_area,monthly,index_2015,CPHPTT01EZM661N -Harmonised CPI for Finland,finland,annual,growth_previous,CPHPTT01FIA657N -Harmonised CPI for Finland,finland,annual,growth_same,CPHPTT01FIA659N -Harmonised CPI for Finland,finland,monthly,growth_previous,CPHPTT01FIM657N -Harmonised CPI for Finland,finland,monthly,growth_same,CPHPTT01FIM659N -Harmonised CPI for Finland,finland,monthly,index_2015,CP0000FIM086NEST -Harmonised CPI for France,france,monthly,index_2015,CP0000FRM086NEST -Harmonised CPI for France,france,annual,growth_previous,CPHPTT01FRA657N -Harmonised CPI for France,france,annual,growth_same,CPHPTT01FRA659N -Harmonised CPI for France,france,monthly,growth_previous,CPHPTT01FRM657N -Harmonised CPI for France,france,monthly,growth_same,CPHPTT01FRM659N -Harmonised CPI for Germany,germany,annual,growth_previous,CPHPTT01DEA657N -Harmonised CPI for Germany,germany,annual,growth_same,CPHPTT01DEA659N -Harmonised CPI for Germany,germany,monthly,growth_previous,CPHPTT01DEM657N -Harmonised CPI for Germany,germany,monthly,growth_same,CPHPTT01DEM659N -Harmonised CPI for Germany,germany,monthly,index_2015,CP0000DEM086NEST -Harmonised CPI for Greece,greece,monthly,index_2015,CP0000GRM086NEST -Harmonised CPI for Greece,greece,annual,growth_previous,CPHPTT01GRA657N -Harmonised CPI for Greece,greece,annual,growth_same,CPHPTT01GRA659N -Harmonised CPI for Greece,greece,monthly,growth_previous,CPHPTT01GRM657N -Harmonised CPI for Greece,greece,monthly,growth_same,CPHPTT01GRM659N -Harmonised CPI for Hungary,hungary,monthly,index_2015,CP0000HUM086NEST -Harmonised CPI for Hungary,hungary,annual,growth_previous,CPHPTT01HUA657N -Harmonised CPI for Hungary,hungary,annual,growth_same,CPHPTT01HUA659N -Harmonised CPI for Hungary,hungary,monthly,growth_previous,CPHPTT01HUM657N -Harmonised CPI for Hungary,hungary,monthly,growth_same,CPHPTT01HUM659N -Harmonised CPI for Iceland,iceland,monthly,index_2015,CP0000ISM086NEST -Harmonised CPI for Iceland,iceland,annual,growth_previous,CPHPTT01ISA657N -Harmonised CPI for Iceland,iceland,annual,growth_same,CPHPTT01ISA659N -Harmonised CPI for Iceland,iceland,monthly,growth_previous,CPHPTT01ISM657N -Harmonised CPI for Iceland,iceland,monthly,growth_same,CPHPTT01ISM659N -Harmonised CPI for Italy,italy,monthly,index_2015,CP0000ITM086NEST -Harmonised CPI for Italy,italy,annual,growth_previous,CPHPTT01ITA657N -Harmonised CPI for Italy,italy,annual,growth_same,CPHPTT01ITA659N -Harmonised CPI for Italy,italy,monthly,growth_previous,CPHPTT01ITM657N -Harmonised CPI for Italy,italy,monthly,growth_same,CPHPTT01ITM659N -Harmonised CPI for Latvia,latvia,monthly,index_2015,CP0000LVM086NEST -Harmonised CPI for Lithuania,lithuania,monthly,index_2015,CP0000LTM086NEST -Harmonised CPI for Luxembourg,luxembourg,monthly,index_2015,CP0000LUM086NEST -Harmonised CPI for Luxembourg,luxembourg,annual,growth_previous,CPHPTT01LUA657N -Harmonised CPI for Luxembourg,luxembourg,annual,growth_same,CPHPTT01LUA659N -Harmonised CPI for Luxembourg,luxembourg,monthly,growth_previous,CPHPTT01LUM657N -Harmonised CPI for Luxembourg,luxembourg,monthly,growth_same,CPHPTT01LUM659N -Harmonised CPI for Malta,malta,monthly,index_2015,CP0000MTM086NEST -Harmonised CPI for Netherlands,netherlands,monthly,index_2015,CP0000NLM086NEST -Harmonised CPI for the Netherlands,netherlands,annual,growth_previous,CPHPTT01NLA657N -Harmonised CPI for the Netherlands,netherlands,annual,growth_same,CPHPTT01NLA659N -Harmonised CPI for the Netherlands,netherlands,monthly,growth_previous,CPHPTT01NLM657N -Harmonised CPI for the Netherlands,netherlands,monthly,growth_same,CPHPTT01NLM659N -Harmonised CPI for Norway,norway,monthly,index_2015,CP0000NOM086NEST -Harmonised CPI for Norway,norway,annual,growth_previous,CPHPTT01NOA657N -Harmonised CPI for Norway,norway,annual,growth_same,CPHPTT01NOA659N -Harmonised CPI for Norway,norway,monthly,growth_previous,CPHPTT01NOM657N -Harmonised CPI for Norway,norway,monthly,growth_same,CPHPTT01NOM659N -Harmonised CPI for Poland,poland,monthly,index_2015,CP0000PLM086NEST -Harmonised CPI for Poland,poland,annual,growth_previous,CPHPTT01PLA657N -Harmonised CPI for Poland,poland,annual,growth_same,CPHPTT01PLA659N -Harmonised CPI for Poland,poland,monthly,growth_previous,CPHPTT01PLM657N -Harmonised CPI for Poland,poland,monthly,growth_same,CPHPTT01PLM659N -Harmonised CPI for Portugal,portugal,monthly,index_2015,CP0000PTM086NEST -Harmonised CPI for Portugal,portugal,annual,growth_previous,CPHPTT01PTA657N -Harmonised CPI for Portugal,portugal,annual,growth_same,CPHPTT01PTA659N -Harmonised CPI for Portugal,portugal,monthly,growth_previous,CPHPTT01PTM657N -Harmonised CPI for Portugal,portugal,monthly,growth_same,CPHPTT01PTM659N -Harmonised CPI for Romania,romania,monthly,index_2015,CP0000ROM086NEST -Harmonised CPI for the Slovak Republic,slovak_republic,annual,growth_previous,CPHPTT01SKA657N -Harmonised CPI for the Slovak Republic,slovak_republic,annual,growth_same,CPHPTT01SKA659N -Harmonised CPI for the Slovak Republic,slovak_republic,monthly,growth_previous,CPHPTT01SKM657N -Harmonised CPI for the Slovak Republic,slovak_republic,monthly,growth_same,CPHPTT01SKM659N -Harmonised CPI for Slovakia,slovakia,monthly,index_2015,CP0000SKM086NEST -Harmonised CPI for Slovenia,slovenia,monthly,index_2015,CP0000SIM086NEST -Harmonised CPI for Slovenia,slovenia,annual,growth_previous,CPHPTT01SIA657N -Harmonised CPI for Slovenia,slovenia,annual,growth_same,CPHPTT01SIA659N -Harmonised CPI for Slovenia,slovenia,monthly,growth_previous,CPHPTT01SIM657N -Harmonised CPI for Slovenia,slovenia,monthly,growth_same,CPHPTT01SIM659N -Harmonised CPI for Spain,spain,annual,growth_previous,CPHPTT01ESA657N -Harmonised CPI for Spain,spain,annual,growth_same,CPHPTT01ESA659N -Harmonised CPI for Spain,spain,monthly,growth_previous,CPHPTT01ESM657N -Harmonised CPI for Spain,spain,monthly,growth_same,CPHPTT01ESM659N -Harmonised CPI for Spain,spain,monthly,index_2015,CP0000ESM086NEST -Harmonised CPI for Sweden,sweden,monthly,index_2015,CP0000SEM086NEST -Harmonised CPI for Sweden,sweden,annual,growth_previous,CPHPTT01SEA657N -Harmonised CPI for Sweden,sweden,annual,growth_same,CPHPTT01SEA659N -Harmonised CPI for Sweden,sweden,monthly,growth_previous,CPHPTT01SEM657N -Harmonised CPI for Sweden,sweden,monthly,growth_same,CPHPTT01SEM659N -Harmonised CPI for Switzerland,switzerland,annual,growth_previous,CPHPTT01CHA657N -Harmonised CPI for Switzerland,switzerland,annual,growth_same,CPHPTT01CHA659N -Harmonised CPI for Switzerland,switzerland,monthly,growth_previous,CPHPTT01CHM657N -Harmonised CPI for Switzerland,switzerland,monthly,growth_same,CPHPTT01CHM659N -Harmonised CPI for Switzerland,switzerland,monthly,index_2015,CP0000CHM086NEST -Harmonised CPI for Turkey,turkey,monthly,index_2015,CP0000TRM086NEST -Harmonised CPI for Turkey,turkey,annual,growth_previous,CPHPTT01TRA657N -Harmonised CPI for Turkey,turkey,annual,growth_same,CPHPTT01TRA659N -Harmonised CPI for Turkey,turkey,monthly,growth_previous,CPHPTT01TRM657N -Harmonised CPI for Turkey,turkey,monthly,growth_same,CPHPTT01TRM659N -Harmonised CPI for the United Kingdom,united_kingdom,annual,growth_same,CPHPTT01GBA659N -Harmonised CPI for the United Kingdom,united_kingdom,monthly,growth_same,CPHPTT01GBM659N -Harmonised CPI for United States,united_states,monthly,index_2015,CP0000USM086NEST -Harmonised CPI for the United States,united_states,annual,growth_previous,CPHPTT01USA657N -Harmonised CPI for the United States,united_states,annual,growth_same,CPHPTT01USA659N -Harmonised CPI for the United States,united_states,monthly,growth_previous,CPHPTT01USM657N -Harmonised CPI for the United States,united_states,monthly,growth_same,CPHPTT01USM659N \ No newline at end of file diff --git a/openbb_terminal/economy/econdb_model.py b/openbb_terminal/economy/econdb_model.py deleted file mode 100644 index d523b994d0a..00000000000 --- a/openbb_terminal/economy/econdb_model.py +++ /dev/null @@ -1,889 +0,0 @@ -""" EconDB Model """ - -__docformat__ = "numpy" - -# pylint: disable=no-member - -import logging -from datetime import datetime -from typing import Any, Dict, Optional, Tuple, Union -from urllib.error import HTTPError - -import pandas as pd -import pandas_datareader.data as web -import yfinance as yf - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.helpers_denomination import transform as transform_by_denomination -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -COUNTRY_CODES = { - "Albania": "AL", - "Argentina": "AR", - "Australia": "AU", - "Austria": "AT", - "Azerbaijan": "AZ", - "Bangladesh": "BD", - "Belarus": "BY", - "Belgium": "BE", - "Bhutan": "BT", - "Bosnia_and_Herzegovina": "BA", - "Botswana": "BW", - "Brazil": "BR", - "Bulgaria": "BG", - "Cambodia": "KH", - "Cameroon": "CM", - "Canada": "CA", - "Chile": "CL", - "China": "CN", - "Colombia": "CO", - "Croatia": "HR", - "Cyprus": "CY", - "Czechia": "CZ", - "Denmark": "DK", - "Dominican_Republic": "DO", - "Egypt": "EG", - "Estonia": "EE", - "European_Union": "EU", - "Finland": "FI", - "France": "FR", - "Germany": "DE", - "Greece": "GR", - "Honduras": "HN", - "Hong Kong": "HK", - "Hungary": "HU", - "India": "IN", - "Indonesia": "ID", - "Iran": "IR", - "Ireland": "IE", - "Israel": "IL", - "Italy": "IT", - "Japan": "JP", - "Kazakhstan": "KZ", - "Laos": "LA", - "Latvia": "LV", - "Lebanon": "LB", - "Lithuania": "LT", - "Luxembourg": "LU", - "Macedonia": "MK", - "Malaysia": "MY", - "Malta": "MT", - "Mexico": "MX", - "Mongolia": "MN", - "Netherlands": "NL", - "New_Zealand": "NZ", - "Nigeria": "NG", - "Norway": "NO", - "Oman": "OM", - "Pakistan": "PK", - "Panama": "PA", - "Paraguay": "PYG", - "Peru": "PE", - "Philippines": "PH", - "Poland": "PL", - "Portugal": "PT", - "Qatar": "QA", - "Romania": "RO", - "Russia": "RU", - "Saudi_Arabia": "SA", - "Serbia": "RS", - "Singapore": "SG", - "Slovakia": "SK", - "Slovenia": "SI", - "South_Africa": "ZA", - "South_Korea": "KR", - "Spain": "ES", - "Sweden": "SE", - "Switzerland": "CH", - "Taiwan": "TW", - "Thailand": "TH", - "Tunisia": "TN", - "Turkey": "TR", - "Ukraine": "UA", - "United_Arab_Emirates": "AE", - "United_Kingdom": "UK", - "United_States": "US", - "Uzbekistan": "UZ", - "Venezuela": "VE", - "Vietnam": "VN", -} - -COUNTRY_CURRENCIES = { - "Albania": "ALL", - "Argentina": "ARS", - "Australia": "AUD", - "Austria": "EUR", - "Azerbaijan": "AZN", - "Bangladesh": "BDT", - "Belarus": "BYR", - "Belgium": "EUR", - "Bhutan": "BTN", - "Bosnia_and_Herzegovina": "BAM", - "Botswana": "BWP", - "Brazil": "BRL", - "Bulgaria": "BGN", - "Cambodia": "KHR", - "Cameroon": "XAF", - "Canada": "CAD", - "Chile": "CLP", - "China": "CNY", - "Colombia": "COP", - "Croatia": "HRK", - "Cyprus": "EUR", - "Czechia": "CZK", - "Denmark": "DKK", - "Dominican_Republic": "DOP", - "Egypt": "EGP", - "Estonia": "EUR", - "European_Union": "EUR", - "Finland": "EUR", - "France": "EUR", - "Germany": "EUR", - "Greece": "EUR", - "Honduras": "HNL", - "Hong Kong": "HKD", - "Hungary": "HUF", - "India": "INR", - "Indonesia": "IDR", - "Iran": "IRR", - "Ireland": "EUR", - "Israel": "ILS", - "Italy": "EUR", - "Japan": "JPY", - "Kazakhstan": "KZT", - "Laos": "LAK", - "Latvia": "EUR", - "Lebanon": "LBP", - "Lithuania": "EUR", - "Luxembourg": "EUR", - "Macedonia": "MKD", - "Malaysia": "MYR", - "Malta": "EUR", - "Mexico": "MXN", - "Mongolia": "MNT", - "Netherlands": "EUR", - "New_Zealand": "NZD", - "Nigeria": "NGN", - "Norway": "NOK", - "Oman": "OMR", - "Pakistan": "PKR", - "Panama": "PAB", - "Paraguay": "PYG", - "Peru": "PEN", - "Philippines": "PHP", - "Poland": "PLN", - "Portugal": "EUR", - "Qatar": "QAR", - "Romania": "RON", - "Russia": "RUB", - "Saudi_Arabia": "SAR", - "Serbia": "RSD", - "Singapore": "SGD", - "Slovakia": "EUR", - "Slovenia": "EUR", - "South_Africa": "ZAR", - "South_Korea": "KRW", - "Spain": "EUR", - "Sweden": "SEK", - "Switzerland": "CHF", - "Taiwan": "TWD", - "Thailand": "THB", - "Tunisia": "TND", - "Turkey": "TRY", - "Ukraine": "UAH", - "United_Arab_Emirates": "AED", - "United_Kingdom": "GBP", - "United_States": "USD", - "Uzbekistan": "UZS", - "Venezuela": "VEF", - "Vietnam": "VND", -} - -PARAMETERS = { - "RGDP": { - "name": "Real gross domestic product", - "period": "Quarterly", - "description": "Inflation-adjusted measure that reflects the value of all goods and services produced by " - "an economy in a given year (chain-linked series).", - }, - "RPRC": { - "name": "Real private consumption", - "period": "Quarterly", - "description": "All purchases made by consumers adjusted by inflation (chain-linked series).", - }, - "RPUC": { - "name": "Real public consumption", - "period": "Quarterly", - "description": "All purchases made by the government adjusted by inflation (chain-linked series).", - }, - "RGFCF": { - "name": "Real gross fixed capital formation", - "period": "Quarterly", - "description": "The acquisition of produced assets adjusted by inflation (chain-linked series).", - }, - "REXP": { - "name": "Real exports of goods and services", - "period": "Quarterly", - "description": "Transactions in goods and services from residents to non-residents adjusted for " - "inflation (chain-linked series)", - }, - "RIMP": { - "name": "Real imports of goods and services", - "period": "Quarterly", - "description": "Transactions in goods and services to residents from non-residents adjusted for " - "inflation (chain-linked series)", - }, - "GDP": { - "name": "Gross domestic product", - "period": "Quarterly", - "description": "Measure that reflects the value of all goods and services produced by " - "an economy in a given year (chain-linked series).", - }, - "PRC": { - "name": "Private consumption", - "period": "Quarterly", - "description": "All purchases made by consumers (chain-linked series).", - }, - "PUC": { - "name": "Public consumption", - "period": "Quarterly", - "description": "All purchases made by the government (chain-linked series)", - }, - "GFCF": { - "name": "Gross fixed capital formation", - "period": "Quarterly", - "description": "The acquisition of produced assets (chain-linked series).", - }, - "EXP": { - "name": "Exports of goods and services", - "period": "Quarterly", - "description": "Transactions in goods and services from residents to non-residents (chain-linked series)", - }, - "IMP": { - "name": "Imports of goods and services", - "period": "Quarterly", - "description": "Transactions in goods and services to residents from non-residents (chain-linked series)", - }, - "CPI": { - "name": "Consumer price index", - "period": "Monthly", - "description": "Purchasing power defined with base 2015 for Europe with varying bases for others. See: " - "https://www.econdb.com/main-indicators", - }, - "PPI": { - "name": "Producer price index", - "period": "Monthly", - "description": "Change in selling prices with base 2015 for Europe with varying bases for others. See: " - "https://www.econdb.com/main-indicators", - }, - "CORE": { - "name": "Core consumer price index", - "period": "Monthly", - "description": "Purchasing power excluding food and energy defined with base 2015 for Europe with varying " - "bases for others. See: https://www.econdb.com/main-indicators", - }, - "URATE": { - "name": "Unemployment", - "period": "Monthly", - "description": "Monthly average % of the working-age population that is unemployed.", - }, - "EMP": { - "name": "Employment", - "period": "Quarterly", - "description": "The employed population within a country (in thousands).", - }, - "ACOIO": { - "name": "Active population", - "period": "Quarterly", - "description": "The active population, unemployed and employed, in thousands.", - }, - "EMRATIO": { - "name": "Employment to working age population", - "period": "Quarterly", - "description": "Unlike the unemployment rate, the employment-to-population ratio includes unemployed " - "people not looking for jobs.", - }, - "RETA": { - "name": "Retail trade", - "period": "Monthly", - "description": "Turnover of sales in wholesale and retail trade", - }, - "CONF": { - "name": "Consumer confidence index", - "period": "Monthly", - "description": "Measures how optimistic or pessimistic consumers are regarding their expected financial " - "situation.", - }, - "IP": { - "name": "Industrial production", - "period": "Monthly", - "description": "Measures monthly changes in the price-adjusted output of industry.", - }, - "CP": { - "name": "Construction production", - "period": "Monthly", - "description": "Measures monthly changes in the price-adjusted output of construction.", - }, - "GBAL": { - "name": "Government balance", - "period": "Quarterly", - "description": "The government balance (or EMU balance) is the overall difference between government " - "revenues and spending.", - }, - "GREV": { - "name": "General government total revenue", - "period": "Quarterly", - "description": "The total amount of revenues collected by governments is determined by past and " - "current political decisions.", - }, - "GSPE": { - "name": "General government total expenditure", - "period": "Quarterly", - "description": "Total expenditure consists of total expense and the net acquisition of " - "non-financial assets. ", - }, - "GDEBT": { - "name": "Government debt", - "period": "Quarterly", - "description": "The financial liabilities of the government.", - }, - "CA": { - "name": "Current account balance", - "period": "Monthly", - "description": "A record of a country's international transactions with the rest of the world", - }, - "TB": { - "name": "Trade balance", - "period": "Monthly", - "description": "The difference between the monetary value of a nation's exports and imports over a " - "certain time period.", - }, - "NIIP": { - "name": "Net international investment position", - "period": "Quarterly", - "description": "Measures the gap between a nation's stock of foreign assets and a foreigner's stock " - "of that nation's assets", - }, - "IIPA": { - "name": "Net international investment position (Assets)", - "period": "Quarterly", - "description": "A nation's stock of foreign assets.", - }, - "IIPL": { - "name": "Net international investment position (Liabilities)", - "period": "Quarterly", - "description": "A foreigner's stock of the nation's assets.", - }, - "Y10YD": { - "name": "Long term yield (10-year)", - "period": "Monthly", - "description": "The 10-year yield is used as a proxy for mortgage rates. It's also seen as a " - "sign of investor sentiment about the country's economy.", - }, - "M3YD": { - "name": "3 month yield", - "period": "Monthly", - "description": "The yield received for investing in a government issued treasury security " - "that has a maturity of 3 months", - }, - "HOU": { - "name": "House price index", - "period": "Monthly", - "description": "House price index defined with base 2015 for Europe with varying " - "bases for others. See: https://www.econdb.com/main-indicators", - }, - "OILPROD": { - "name": "Oil production", - "period": "Monthly", - "description": "The amount of oil barrels produced per day in a month within a country.", - }, - "POP": { - "name": "Population", - "period": "Monthly", - "description": "The population of a country. This can be in thousands or, " - "when relatively small, in actual units.", - }, -} - -TRANSFORM = { - "": "No transformation", - "TPOP": "Total percentage change on period", - "TOYA": "Total percentage since 1 year ago", - "TUSD": "Level USD", - "TPGP": "Percentage of GDP", - "TNOR": "Start = 100", -} - -SCALES = { - "Thousands": 1_000, - "Tens of thousands": 10_000, - "Hundreds of thousands": 100_000, - "Millions": 1_000_000, - "Tens of millions": 10_000_000, - "Hundreds of millions": 100_000_000, - "Billions": 1_000_000_000, - "Tens of billions": 10_000_000_000, - "Units": 1, -} - -TREASURIES: Dict = { - "frequencies": { - "annually": 203, - "monthly": 129, - "weekly": 21, - "daily": 9, - }, - "instruments": { - "nominal": { - "identifier": "TCMNOM", - "maturities": { - "1m": "1-month", - "3m": "3-month", - "6m": "6-month", - "1y": "1-year", - "2y": "2-year", - "3y": "3-year", - "5y": "5-year", - "7y": "7-year", - "10y": "10-year", - "20y": "20-year", - "30y": "30-year", - }, - }, - "inflation": { - "identifier": "TCMII", - "maturities": { - "5y": "5-year", - "7y": "7-year", - "10y": "10-year", - "20y": "20-year", - "30y": "30-year", - }, - }, - "average": { - "identifier": "LTAVG", - "maturities": { - "Longer than 10-year": "Longer than 10-year", - }, - }, - "secondary": { - "identifier": "TB", - "maturities": { - "4w": "4-week", - "3m": "3-month", - "6m": "6-month", - "1y": "1-year", - }, - }, - }, -} - - -@log_start_end(log=logger) -def get_macro_data( - parameter: str, - country: str, - transform: str = "", - start_date: str = "1900-01-01", - end_date: Optional[str] = None, - symbol: str = "", -) -> Tuple[pd.Series, Union[str, Any]]: - """Query the EconDB database to find specific macro data about a company [Source: EconDB] - - Parameters - ---------- - parameter: str - The type of data you wish to display - country : str - the selected country - transform : str - select data transformation from: - '' - no transformation - 'TPOP' - total percentage change on period, - 'TOYA' - total percentage since 1 year ago, - 'TUSD' - level USD, - 'TPGP' - Percentage of GDP, - 'TNOR' - Start = 100 - start_date : str - The starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. - end_date : Optional[str] - The end date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. - symbol : str - In what currency you wish to convert all values. - - Returns - ------- - Tuple[pd.Series, Union[str, Any]] - A series with the requested macro data of the chosen country, - The units of the macro data, e.g. 'Bbl/day" for oil. - """ - - if end_date is None: - end_date = datetime.today().strftime("%Y-%m-%d") - - df, units = pd.DataFrame(), "" - country = country.replace("_", " ") - country = country.title() - country = country.replace(" ", "_") - parameter = parameter.upper() - - if country not in COUNTRY_CODES: - console.print(f"No data available for the country {country}.") - return pd.Series(dtype=float), "" - if parameter not in PARAMETERS: - console.print(f"The parameter {parameter} is not an option for {country}.") - return pd.Series(dtype=float), "" - if transform not in TRANSFORM: - console.print(f"The transform {transform} is not a valid option.") - return pd.Series(dtype=float), "" - - country_code = COUNTRY_CODES[country] - country_currency = COUNTRY_CURRENCIES[country] - - try: - code = f"{parameter}{country_code}" - if transform: - code += f"~{transform}" - - r = request(f"https://www.econdb.com/series/context/?tickers={code}") - res_json = r.json() - if res_json: - data = res_json[0] - scale = data["td"]["scale"] - units = data["td"]["units"] - - df = pd.DataFrame(data["dataarray"]) - - if code not in df.columns: - console.print( - f"No data available for {parameter} of {country} " - f"{f'with transform method {transform}' if transform else ''}" - ) - return pd.DataFrame(), "NA/NA" - - df = df.set_index(pd.to_datetime(df["date"]))[code] * SCALES[scale] - df = df.sort_index().dropna() - - # Since a percentage is done through differences, the first value is NaN - if transform in ["TPOP", "TOYA"]: - df = df.iloc[1:] - - if not res_json or df.empty: - console.print( - f"No data available for {parameter} ({PARAMETERS[parameter]['name']}) " - f"of country {country.replace('_', ' ')}" - ) - return pd.Series(dtype=float), "" - - if start_date or end_date: - try: - dt_start = pd.to_datetime(start_date) - dt_end = pd.to_datetime(end_date) - df = df.loc[dt_start:dt_end] - except TypeError: - console.print("[red]Invalid date sent. Format as YYYY-MM-DD[/red]\n") - return pd.DataFrame(), "NA/NA" - - if ( - symbol - and country_currency != symbol - and units in COUNTRY_CURRENCIES.values() - ): - if units in COUNTRY_CURRENCIES.values(): - units = symbol - - currency_data = yf.download( - f"{country_currency}{symbol}=X", - start=df.index[0], - end=df.index[-1], - progress=False, - )["Adj Close"] - - merged_df = pd.merge_asof( - df, currency_data, left_index=True, right_index=True - ) - df = merged_df[code] * merged_df["Adj Close"] - - if pd.isna(df).any(): - df_old_oldest, df_old_newest = df.index[0].date(), df.index[-1].date() - df = df.dropna() - df_new_oldest, df_new_newest = df.index[0].date(), df.index[-1].date() - console.print( - f"Due to missing exchange values, some data was dropped from {parameter} of {country}. " - f"Consider using the native currency if you want to prevent this. \n" - f"OLD: {df_old_oldest} - {df_old_newest}\n" - f"NEW: {df_new_oldest} - {df_new_newest}" - ) - - if not df.empty: - df = df.groupby(df.index.strftime("%Y-%m")).head(1) - df.index = df.index.strftime("%Y-%m") - df = pd.to_numeric(df, errors="coerce").dropna() - - except HTTPError: - return console.print( - f"There is no data available for the combination {parameter} and {country}." - ) - - return df, units - - -@log_start_end(log=logger) -def get_macro_transform() -> Dict[str, str]: - """This function returns the available macro transform with detail. - - Returns - ------- - Dict[str, str] - A dictionary with the available macro transforms. - """ - return TRANSFORM - - -@log_start_end(log=logger) -def get_macro_parameters() -> Dict[str, Dict[str, str]]: - """This function returns the available macro parameters with detail. - - Returns - ------- - Dict[str, Dict[str, str]] - A dictionary with the available macro parameters. - """ - return PARAMETERS - - -@log_start_end(log=logger) -def get_macro_countries() -> Dict[str, str]: - """This function returns the available countries and respective currencies. - - Returns - ------- - Dict[str, str] - A dictionary with the available countries and respective currencies. - """ - return COUNTRY_CURRENCIES - - -@log_start_end(log=logger) -def get_aggregated_macro_data( - parameters: Optional[list] = None, - countries: Optional[list] = None, - transform: str = "", - start_date: str = "1900-01-01", - end_date: Optional[str] = None, - symbol: str = "", -) -> Tuple[pd.DataFrame, Dict[Any, Dict[Any, Any]], str]: - """This functions groups the data queried from the EconDB database [Source: EconDB] - - Parameters - ---------- - parameters: list - The type of data you wish to download. Available parameters can be accessed through economy.macro_parameters(). - countries : list - The selected country or countries. Available countries can be accessed through economy.macro_countries(). - transform : str - The selected transform. Available transforms can be accessed through get_macro_transform(). - start_date : str - The starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. - end_date : Optional[str] - The end date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. - symbol : str - In what currency you wish to convert all values. - - Returns - ------- - Tuple[pd.DataFrame, Dict[Any, Dict[Any, Any]], str] - A DataFrame with the requested macro data of all chosen countries, - A dictionary containing the units of each country's parameter (e.g. EUR), - A string denomination which can be Trillions, Billions, Millions, Thousands - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> macro_df = openbb.economy.macro() - """ - - if end_date is None: - end_date = datetime.today().strftime("%Y-%m-%d") - - if parameters is None: - parameters = ["CPI"] - if countries is None: - countries = ["United_States"] - - country_data: Dict[Any, Dict[Any, pd.Series]] = {} - units: Dict[Any, Dict[Any, Any]] = {} - - for country in countries: - country_data[country] = {} - units[country] = {} - for parameter in parameters: - ( - country_data[country][parameter], - units[country][parameter], - ) = get_macro_data( - parameter, country, transform, start_date, end_date, symbol - ) - - if country_data[country][parameter].empty: - del country_data[country][parameter] - del units[country][parameter] - - country_data_df = ( - pd.DataFrame.from_dict(country_data, orient="index").stack().to_frame() - ) - country_data_df = pd.DataFrame( - country_data_df[0].values.tolist(), index=country_data_df.index - ).T - - (df_rounded, denomination) = transform_by_denomination(country_data_df) - df_rounded.index = pd.DatetimeIndex(df_rounded.index) - - if transform: - denomination_string = f" [{TRANSFORM[transform]}]" - else: - denomination_string = f" [in {denomination}]" if denomination != "Units" else "" - - df_rounded = df_rounded.dropna() - - return (df_rounded, units, denomination_string) - - -@log_start_end(log=logger) -def get_treasuries( - instruments: Optional[list] = None, - maturities: Optional[list] = None, - frequency: str = "monthly", - start_date: str = "1900-01-01", - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get U.S. Treasury rates [Source: EconDB] - - Parameters - ---------- - instruments: list - Type(s) of treasuries, nominal, inflation-adjusted (long term average) or secondary market. - Available options can be accessed through economy.treasury_maturities(). - maturities : list - Treasury maturities to get. Available options can be accessed through economy.treasury_maturities(). - frequency : str - Frequency of the data, this can be annually, monthly, weekly or daily. - start_date : str - Starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. - end_date : Optional[str] - End date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. - - Returns - ------- - treasury_data: pd.Dataframe - Holds data of the selected types and maturities - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.economy.treasury() - """ - - if end_date is None: - end_date = datetime.today().strftime("%Y-%m-%d") - - if instruments is None: - instruments = ["nominal"] - if maturities is None: - maturities = ["10y"] - - treasury_data: Dict[Any, Dict[Any, pd.Series]] = {} - - for instrument in instruments: - if instrument not in TREASURIES["instruments"]: - console.print( - f"{instrument} is not an option. Please choose between: " - f"{', '.join(TREASURIES['instruments'].keys())}" - ) - else: - instrument_identifier = TREASURIES["instruments"][instrument]["identifier"] - frequency_number = TREASURIES["frequencies"][frequency] - df = web.DataReader( - "&".join( - [ - "dataset=FRB_H15", - "v=Instrument", - "h=TIME", - f"instrument=[{instrument_identifier}]", - f"from={start_date}", - f"to={end_date}", - f"freq=[{frequency_number}", - "UNIT=[PERCENT:_PER_YEAR]", - ] - ), - "econdb", - ) - - if instrument == "average": - maturities_list = ["Longer than 10-year"] - type_string = "Long-term average" - else: - maturities_list = maturities - type_string = instrument.capitalize() - - treasury_data[type_string] = {} - - for maturity in maturities_list: - if maturity not in TREASURIES["instruments"][instrument]["maturities"]: - console.print( - f"The maturity {maturity} is not an option for {instrument}. Please choose between " - f"{', '.join(TREASURIES['instruments'][instrument]['maturities'].keys())}" - ) - else: - maturity_string = TREASURIES["instruments"][instrument][ - "maturities" - ][maturity] - - for column in df.columns: - # check if type inside the name and maturity inside the maturity string - if ( - type_string.lower() in column[2].lower() - and maturity_string in column[3] - ): - treasury_data[type_string][maturity_string] = df[ - column - ].dropna() - break - - if maturity_string not in treasury_data[type_string]: - console.print( - f"No data found for the combination {instrument} and {maturity}." - ) - - df = pd.DataFrame.from_dict(treasury_data, orient="index").stack().to_frame() - df = pd.DataFrame(df[0].values.tolist(), index=df.index).T - df.columns = ["_".join(column) for column in df.columns] - - return df - - -@log_start_end(log=logger) -def get_treasury_maturities() -> pd.DataFrame: - """Get treasury maturity options [Source: EconDB] - - Returns - ------- - df: pd.DataFrame - Contains the name of the instruments and a string containing all options. - """ - - instrument_maturities = { - instrument: ", ".join(values["maturities"].keys()) - for instrument, values in TREASURIES["instruments"].items() - } - - df = pd.DataFrame.from_dict(instrument_maturities, orient="index") - df.loc["average"] = "Defined by function" - - df.index.name = "Instrument" - df.columns = ["Maturities"] - - return df diff --git a/openbb_terminal/economy/econdb_view.py b/openbb_terminal/economy/econdb_view.py deleted file mode 100644 index 255735450cb..00000000000 --- a/openbb_terminal/economy/econdb_view.py +++ /dev/null @@ -1,252 +0,0 @@ -""" EconDB View """ - -__docformat__ = "numpy" -# pylint:disable=too-many-arguments,unused-argument -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.economy import econdb_model -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def show_macro_data( - parameters: Optional[list] = None, - countries: Optional[list] = None, - transform: str = "", - start_date: str = "1900-01-01", - end_date: Optional[str] = None, - symbol: str = "", - raw: bool = False, - external_axes: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - limit: int = 10, -) -> Union[OpenBBFigure, None]: - """Show the received macro data about a company [Source: EconDB] - - Parameters - ---------- - parameters: list - The type of data you wish to display. Available parameters can be accessed through get_macro_parameters(). - countries : list - The selected country or countries. Available countries can be accessed through get_macro_countries(). - transform : str - select data transformation from: - '' - no transformation - 'TPOP' - total percentage change on period, - 'TOYA' - total percentage since 1 year ago, - 'TUSD' - level USD, - 'TPGP' - Percentage of GDP, - 'TNOR' - Start = 100 - start_date : str - The starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. - end_date : Optional[str] - The end date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. - symbol : str - In what currency you wish to convert all values. - raw : bool - Whether to display the raw output. - external_axes : bool, optional - Whether to return the figure object or not, by default False - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - - Returns - ------- - Plots the Series. - """ - - if parameters is None: - parameters = ["CPI"] - if countries is None: - countries = ["United_States"] - - df_rounded, units, denomination = econdb_model.get_aggregated_macro_data( - parameters, countries, transform, start_date, end_date, symbol - ) - - fig = OpenBBFigure(yaxis=dict(side="right")) - - for column in df_rounded.columns: - if transform: - if transform in ["TPOP", "TOYA", "TPGP"]: - parameter_units = "Units: %" - elif transform in ["TUSD", "TNOR"]: - parameter_units = "Units: Level" - else: - parameter_units = f"Units: {units[column[0]][column[1]]}" - country_label = column[0].replace("_", " ") - parameter_label = econdb_model.PARAMETERS[column[1]]["name"] - if len(parameters) > 1 and len(countries) > 1: - fig.add_scatter( - x=df_rounded.index, - y=df_rounded[column], - mode="lines", - name=f"{country_label} [{parameter_label}, {parameter_units}]", - ) - fig.set_title(f"Macro data{denomination}") - elif len(parameters) > 1: - fig.add_scatter( - x=df_rounded.index, - y=df_rounded[column], - mode="lines", - name=f"{parameter_label} [{parameter_units}]", - ) - fig.set_title(f"{country_label}{denomination}") - elif len(countries) > 1: - fig.add_scatter( - x=df_rounded.index, - y=df_rounded[column], - mode="lines", - name=f"{country_label} [{parameter_units}]", - ) - fig.set_title(f"{parameter_label}{denomination}") - else: - fig.add_scatter( - x=df_rounded.index, - y=df_rounded[column], - mode="lines", - name=f"{country_label} [{parameter_label}, {parameter_units}]", - ) - fig.set_title( - f"{parameter_label} of {country_label}{denomination} [{parameter_units}]" - ) - - df_rounded.columns = ["_".join(column) for column in df_rounded.columns] - - if raw: - # was a -iloc so we need to flip the index as we use head - df_rounded = df_rounded.sort_index(ascending=False) - print_rich_table( - df_rounded.fillna("-"), - headers=list(df_rounded.columns), - show_index=True, - title=f"Macro Data {denomination}", - export=bool(export), - limit=limit, - ) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "macro_data", - df_rounded, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def show_treasuries( - instruments: Optional[list] = None, - maturities: Optional[list] = None, - frequency: str = "monthly", - start_date: str = "1900-01-01", - end_date: Optional[str] = None, - raw: bool = False, - external_axes: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - limit: int = 10, -) -> Union[OpenBBFigure, None]: - """Display U.S. Treasury rates [Source: EconDB] - - Parameters - ---------- - instruments: list - Type(s) of treasuries, nominal, inflation-adjusted or secondary market. - Available options can be accessed through economy.treasury_maturities(). - maturities : list - Treasury maturities to display. Available options can be accessed through economy.treasury_maturities(). - frequency : str - Frequency of the data, this can be daily, weekly, monthly or annually - start_date : str - Starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. - end_date : Optional[str] - End date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. - raw : bool - Whether to display the raw output. - external_axes : bool, optional - Whether to return the figure object or not, by default False - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - - Returns - ------- - Plots the Treasury Series. - """ - - if instruments is None: - instruments = ["nominal"] - if maturities is None: - maturities = ["10y"] - - treasury_data = econdb_model.get_treasuries( - instruments, maturities, frequency, start_date, end_date - ) - fig = OpenBBFigure( - yaxis=dict(side="right", title="Yield (%)"), - title="U.S. Treasuries", - ) - - for col in treasury_data.columns: - col_label = col.split("_") - fig.add_scatter( - x=treasury_data.index, - y=treasury_data[col], - mode="lines", - name=f"{col_label[0]} [{col_label[1]}]", - ) - - if raw: - # was a -iloc so we need to flip the index as we use head - treasury_data = treasury_data.sort_index(ascending=False) - print_rich_table( - treasury_data, - headers=list(treasury_data.columns), - show_index=True, - title="U.S. Treasuries", - export=bool(export), - limit=limit, - ) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "treasuries_data", - treasury_data / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def show_treasury_maturities(): - """Get treasury maturity options [Source: EconDB] - - Returns - ------- - A table containing the instruments and maturities. - """ - - instrument_maturities = econdb_model.get_treasury_maturities() - - print_rich_table( - instrument_maturities, - headers=list(["Maturities"]), - show_index=True, - index_name="Instrument", - title="Maturity options per instrument", - ) diff --git a/openbb_terminal/economy/economy_controller.py b/openbb_terminal/economy/economy_controller.py deleted file mode 100644 index 99c2022b1af..00000000000 --- a/openbb_terminal/economy/economy_controller.py +++ /dev/null @@ -1,2044 +0,0 @@ -""" Econ Controller """ - -__docformat__ = "numpy" -# pylint: disable=C0302 ,R1710,R0904,C0415,too-many-branches,unnecessary-dict-index-lookup - -import argparse -import logging -import os -from datetime import ( - date, - datetime as dt, -) -from typing import Any, Dict, List, Optional - -import pandas as pd -from dateutil.relativedelta import relativedelta - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.economy import ( - commodity_view, - econdb_model, - econdb_view, - fedreserve_model, - fedreserve_view, - finviz_model, - finviz_view, - fred_model, - fred_view, - nasdaq_model, - nasdaq_view, - oecd_model, - oecd_view, - wsj_view, - yfinance_model, - yfinance_view, -) -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - list_from_str, - parse_and_split_input, - print_rich_table, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class EconomyController(BaseController): - """Economy Controller class""" - - CHOICES_COMMANDS = [ - "overview", - "futures", - "gdp", - "rgdp", - "fgdp", - "debt", - "cpi", - "ccpi", - "balance", - "revenue", - "spending", - "trust", - "macro", - "fred", - "index", - "treasury", - "valuation", - "performance", - "map", - "events", - "edebt", - "usdli", - ] - - wsj_sortby_cols_dict = {c: None for c in ["ticker", "last", "change", "prevClose"]} - map_period_list = ["1d", "1w", "1m", "3m", "6m", "1y"] - map_filter_list = ["sp500", "world", "full", "etf"] - macro_us_interval = [ - "annual", - "quarter", - "semiannual", - "monthly", - "weekly", - "daily", - ] - macro_us_types = [ - "GDP", - "GDPC", - "INF", - "CPI", - "TYLD", - "UNEMP", - "gdp", - "gpdc", - "inf", - "cpi", - "tyld", - "unemp", - ] - overview_options = ["indices", "usbonds", "glbonds", "currencies"] - tyld_maturity = ["3m", "5y", "10y", "30y"] - valuation_sort_cols_dict = { - "Name": "Name", - "MarketCap": "Market Cap", - "P/E": "P/E", - "FwdP/E": "Fwd P/E", - "PEG": "PEG", - "P/S": "P/S", - "P/B": "P/B", - "P/C": "P/C", - "P/FCF": "P/FCF", - "EPSpast5Y": "EPS past 5Y", - "EPSnext5Y": "EPS next 5Y", - "Salespast5Y": "Sales past 5Y", - "Change": "Change", - "Volume": "Volume", - } - performance_sort_dict = { - "Name": "Name", - "1W": "1W", - "1M": "1M", - "3M": "3M", - "6M": "6M", - "1Y": "1Y", - "YTD": "YTD", - "Recom": "Recom", - "AvgVolume": "Avg Volume", - "RelVolume": "Rel Volume", - "Change": "Change", - "Volume": "Volume", - } - index_interval = [ - "1m", - "2m", - "5m", - "15m", - "30m", - "60m", - "90m", - "1h", - "1d", - "5d", - "1wk", - "1mo", - "3mo", - ] - futures_commodities = ["energy", "metals", "meats", "grains", "softs"] - macro_show = ["parameters", "countries", "transform"] - d_GROUPS = finviz_model.GROUPS - PATH = "/economy/" - - stored_datasets = "" - - FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md") - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.current_series: Dict = dict() - self.fred_query: pd.Series = pd.Series(dtype=float) - self.UNITS: Dict[Any, Dict[Any, Any]] = dict() - self.FRED_TITLES: Dict = dict() - self.choices: Dict = dict() - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - # This is still needed because we can't use choices and nargs separated by comma - choices["gdp"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_GDP - } - choices["gdp"]["-c"] = "--countries" - - choices["rgdp"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_RGDP - } - choices["rgdp"]["-c"] = "--countries" - - choices["fgdp"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_GDP_FORECAST - } - choices["fgdp"]["-c"] = "--countries" - - choices["debt"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_DEBT - } - choices["debt"]["-c"] = "--countries" - - choices["cpi"]["--countries"] = {c: None for c in fred_model.CPI_COUNTRIES} - choices["cpi"]["-c"] = "--countries" - - choices["ccpi"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_CPI - } - choices["ccpi"]["-c"] = "--countries" - - choices["balance"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_BALANCE - } - choices["balance"]["-c"] = "--countries" - - choices["revenue"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_REVENUE - } - choices["revenue"]["-c"] = "--countries" - - choices["spending"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_SPENDING - } - choices["spending"]["-c"] = "--countries" - - choices["trust"]["--countries"] = { - c: {} for c in oecd_model.COUNTRY_TO_CODE_TRUST - } - choices["trust"]["-c"] = "--countries" - - choices["macro"]["--parameters"] = {c: {} for c in econdb_model.PARAMETERS} - choices["macro"]["-p"] = "--parameters" - choices["macro"]["--countries"] = { - c: {} for c in econdb_model.COUNTRY_CODES - } - choices["macro"]["-c"] = "--countries" - choices["fred"]["--parameter"] = {c: {} for c in self.fred_query.tolist()} - choices["fred"]["-p"] = "--parameter" - choices["index"]["--indices"] = {c: {} for c in yfinance_model.INDICES} - choices["index"]["-i"] = "--indices" - choices["events"]["--countries"] = { - c: {} for c in nasdaq_model.get_country_names() - } - choices["events"]["-c"] = "--countries" - choices["treasury"]["--maturity"] = { - c: None for c in fedreserve_model.all_mat - } - choices["treasury"]["-m"] = "--maturity" - self.choices = choices - self.completer = NestedCompleter.from_nested_dict(choices) - - def parse_input(self, an_input: str) -> List: - """Parse controller input - - Overrides the parent class function to handle github org/repo path convention. - See `BaseController.parse_input()` for details. - """ - # Filtering out sorting parameters with forward slashes like P/E - sort_filter = r"((\ -s |\ --sortby ).*?(P\/E|Fwd|P\/E|P\/S|P\/B|P\/C|P\/FCF)*)" - - custom_filters = [sort_filter] - - commands = parse_and_split_input( - an_input=an_input, custom_filters=custom_filters - ) - return commands - - def update_runtime_choices(self): - if ( - session - and get_current_user().preferences.USE_PROMPT_TOOLKIT - and not self.fred_query.empty - ): - self.choices["fred"]["--parameter"] = {c: None for c in self.fred_query} - - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def print_help(self): - """Print help""" - mt = MenuText("economy/", column_sources=115) - mt.add_cmd("overview") - mt.add_cmd("futures") - mt.add_cmd("map") - mt.add_cmd("events") - mt.add_cmd("edebt") - mt.add_cmd("valuation") - mt.add_cmd("performance") - mt.add_cmd("usdli") - mt.add_raw("\n") - mt.add_info("_country_") - mt.add_cmd("gdp") - mt.add_cmd("rgdp") - mt.add_cmd("fgdp") - mt.add_cmd("debt") - mt.add_cmd("cpi") - mt.add_cmd("ccpi") - mt.add_cmd("balance") - mt.add_cmd("revenue") - mt.add_cmd("spending") - mt.add_cmd("trust") - mt.add_raw("\n") - mt.add_info("_database_") - mt.add_cmd("macro") - mt.add_cmd("treasury") - mt.add_cmd("fred") - mt.add_cmd("index") - - console.print(text=mt.menu_text, menu="Economy") - - @log_start_end(log=logger) - def call_overview(self, other_args: List[str]): - """Process overview command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="overview", - description=""" - Provide a market overview of a variety of options. This can be a general overview, - indices, bonds and currencies. [Source: Wall St. Journal] - """, - ) - - parser.add_argument( - "-t", - "--type", - dest="type", - help="Obtain either US indices, US Bonds, Global Bonds or Currencies", - type=str, - choices=self.overview_options, - default="", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if not ns_parser.type: - wsj_view.display_overview( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.type == "indices": - wsj_view.display_indices( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - if ns_parser.type == "usbonds": - wsj_view.display_usbonds( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - if ns_parser.type == "glbonds": - wsj_view.display_glbonds( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - if ns_parser.type == "currencies": - wsj_view.display_currencies( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_futures(self, other_args: List[str]): - """Process futures command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="futures", - description="Futures/Commodities from Wall St. Journal and FinViz.", - ) - - parser.add_argument( - "-c", - "--commodity", - dest="commodity", - help="Obtain commodity futures from FinViz", - type=str, - choices=self.futures_commodities, - default="", - ) - - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - choices=self.wsj_sortby_cols_dict.keys(), - default="ticker", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.source == "Finviz": - if ns_parser.commodity: - finviz_view.display_future( - future_type=ns_parser.commodity.capitalize(), - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - console.print( - "[red]Commodity group must be specified on Finviz.[/red]" - ) - elif ns_parser.source == "WallStreetJournal": - if ns_parser.commodity: - console.print("[red]Commodity flag valid with Finviz only.[/red]") - wsj_view.display_futures( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_map(self, other_args: List[str]): - """Process map command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="map", - description=""" - Performance index stocks map categorized by sectors and industries. - Size represents market cap. Opens web-browser. [Source: Finviz] - """, - ) - parser.add_argument( - "-p", - "--period", - action="store", - dest="s_period", - type=str, - default="1d", - choices=self.map_period_list, - help="Performance period.", - ) - parser.add_argument( - "-t", - "--type", - action="store", - dest="s_type", - type=str, - default="sp500", - choices=self.map_filter_list, - help="Map filter type.", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - finviz_view.display_performance_map( - period=ns_parser.s_period, - map_filter=ns_parser.s_type, - ) - - @log_start_end(log=logger) - def call_gdp(self, other_args: List[str]): - """Process gdp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="gdp", - description="This indicator is based on nominal GDP (also called GDP at current prices or GDP in value)" - " and is available in different measures: US dollars and US dollars per capita (current PPPs).", - ) - - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="Countries to get data for", - default="united_states", - ) - - parser.add_argument( - "-u", - "--units", - type=str, - dest="units", - help="Use either USD or USD_CAP (USD Per Capita)", - choices=["USD", "USD_CAP"], - default="USD", - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=(dt.now() - relativedelta(years=30)).date(), - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=dt.now().date(), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - countries = ( - list_from_str(ns_parser.countries.lower()) - if ns_parser.countries - else None - ) - - oecd_view.plot_gdp( - countries=countries, - units=ns_parser.units, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rgdp(self, other_args: List[str]): - """Process rgdp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rgdp", - description="This indicator is based on real GDP (also called GDP at constant prices or GDP in volume), " - "i.e. the developments over time are adjusted for price changes.", - ) - - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="Countries to get data for", - default="united_states", - ) - - parser.add_argument( - "-u", - "--units", - type=str, - dest="units", - help="Use either PC_CHGPP (percentage change previous quarter), PC_CHGPY (percentage " - "change from the same quarter of the previous year) or IDX (index with base at 2015) " - "for units", - choices=["PC_CHGPP", "PC_CHGPY", "IDX"], - default="PC_CHGPY", - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=(dt.now() - relativedelta(years=10)).date(), - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=dt.now().date(), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - countries = ( - list_from_str(ns_parser.countries.lower()) - if ns_parser.countries - else None - ) - - oecd_view.plot_real_gdp( - countries=countries, - units=ns_parser.units, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fgdp(self, other_args: List[str]): - """Process fgdp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fgdp", - description="Forecast is based on an assessment of the economic climate in " - "individual countries and the world economy, using a combination of model-based " - "analyses and expert judgement. This indicator is measured in growth rates compared to previous year.", - ) - - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="Countries to get data for", - default="united_states", - ) - - parser.add_argument( - "-t", - "--types", - type=str, - dest="types", - help="Use either 'real' or 'nominal'", - choices=["real", "nominal"], - default="real", - ) - - parser.add_argument( - "-q", - "--quarterly", - action="store_true", - dest="quarterly", - help="Whether to plot quarterly results.", - default=False, - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=(dt.now() - relativedelta(years=10)).date(), - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=(dt.now() + relativedelta(years=10)).date(), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - countries = ( - list_from_str(ns_parser.countries.lower()) - if ns_parser.countries - else None - ) - - oecd_view.plot_gdp_forecast( - countries=countries, - types=ns_parser.types, - quarterly=ns_parser.quarterly, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_debt(self, other_args: List[str]): - """Process debt command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="debt", - description="General government debt-to-GDP ratio measures the gross debt of the general " - "government as a percentage of GDP. It is a key indicator for the sustainability of government finance.", - ) - - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="Countries to get data for", - default="united_states", - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=(dt.now() - relativedelta(years=30)).date(), - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=dt.now().date(), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - countries = ( - list_from_str(ns_parser.countries.lower()) - if ns_parser.countries - else None - ) - - oecd_view.plot_debt( - countries=countries, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_balance(self, other_args: List[str]): - """Process balance command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="balance", - description=" General government balance is defined as the balance of income and expenditure of government," - " including capital income and capital expenditures. 'Net lending' means that government has a surplus, " - "and is providing financial resources to other sectors, while 'net borrowing' means that " - "government has a deficit, and requires financial resources from other sectors. " - "This indicator is measured as a percentage of GDP.", - ) - - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="Countries to get data for", - default="united_states", - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=(dt.now() - relativedelta(years=30)).date(), - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=dt.now().date(), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - countries = ( - list_from_str(ns_parser.countries.lower()) - if ns_parser.countries - else None - ) - - oecd_view.plot_balance( - countries=countries, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_revenue(self, other_args: List[str]): - """Process revenue command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="revenue", - description="Governments collect revenues mainly for two purposes: to finance the goods " - "and services they provide to citizens and businesses, and to fulfil their redistributive " - "role. Comparing levels of government revenues across countries provides an " - "indication of the importance of the government sector in the economy in " - "terms of available financial resources.", - ) - - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="Countries to get data for", - default="united_states", - ) - - parser.add_argument( - "-u", - "--units", - type=str, - dest="units", - help="Use either THND_USD_CAP (thousands of USD per capity) " - "or PC_GDP (percentage of GDP)", - choices=["PC_GDP", "THND_USD_CAP"], - default="PC_GDP", - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=(dt.now() - relativedelta(years=30)).date(), - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=dt.now().date(), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - countries = ( - list_from_str(ns_parser.countries.lower()) - if ns_parser.countries - else None - ) - - oecd_view.plot_revenue( - countries=countries, - units=ns_parser.units, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_spending(self, other_args: List[str]): - """Process spending command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="spending", - description="General government spending provides an indication of the size " - "of government across countries. The large variation in this indicator highlights " - "the variety of countries' approaches to delivering public goods and services " - "and providing social protection, not necessarily differences in resources spent", - ) - - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="Countries to get data for", - default="united_states", - ) - - parser.add_argument( - "-p", - "--perspective", - type=str, - dest="perspective", - help="Use either TOT (Total), RECULTREL (Recreation, culture and religion), " - "HOUCOMM (Housing and community amenities), PUBORD (Public order and safety), " - "EDU (Education), ENVPROT (Environmental protection), GRALPUBSER (General public services), " - "SOCPROT (Social protection), ECOAFF (Economic affairs), DEF (Defence), HEALTH (Health)", - choices=[ - "TOT", - "RECULTREL", - "HOUCOMM", - "PUBORD", - "EDU", - "ENVPROT", - "GRALPUBSER", - "SOCPROT", - "ECOAFF", - "DEF", - "HEALTH", - ], - default="TOT", - ) - - parser.add_argument( - "-u", - "--units", - type=str, - dest="units", - help="Use either THND_USD_CAP (thousands of USD per capity) " - "or PC_GDP (percentage of GDP)", - choices=["PC_GDP", "THND_USD_CAP"], - default="PC_GDP", - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=(dt.now() - relativedelta(years=30)).date(), - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=dt.now().date(), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - countries = ( - list_from_str(ns_parser.countries.lower()) - if ns_parser.countries - else None - ) - - oecd_view.plot_spending( - countries=countries, - perspective=ns_parser.perspective, - units=ns_parser.units, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_trust(self, other_args: List[str]): - """Process trust command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="trust", - description="Trust in government refers to the share of people who report " - "having confidence in the national government.", - ) - - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="Countries to get data for", - default="united_states", - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=(dt.now() - relativedelta(years=30)).date(), - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=dt.now().date(), - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - countries = ( - list_from_str(ns_parser.countries.lower()) - if ns_parser.countries - else None - ) - - oecd_view.plot_trust( - countries=countries, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_macro(self, other_args: List[str]): - """Process macro command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="macro", - description="Get a broad selection of macro data from one or multiple countries. This includes Gross " - "Domestic Product (RGDP & GDP) and the underlying components, Treasury Yields (Y10YD & M3YD), " - "Employment figures (URATE, EMP, AC0I0 and EMRATIO), Government components (e.g. GBAL & GREV), " - "Consumer and Producer Indices (CPI & PPI) and a variety of other indicators. [Source: EconDB]", - ) - parser.add_argument( - "-p", - "--parameters", - type=str, - dest="parameters", - help="Abbreviation(s) of the Macro Economic data", - default="CPI", - ) - parser.add_argument( - "-c", - "--countries", - type=str, - dest="countries", - help="The country or countries you wish to show data for", - default="united_states", - ) - parser.add_argument( - "-t", - "--transform", - dest="transform", - help="The transformation to apply to the data", - default="", - choices=econdb_model.TRANSFORM, - ) - parser.add_argument( - "--show", - dest="show", - help="Show parameters and what they represent using 'parameters'" - " or countries and their currencies using 'countries'", - choices=self.macro_show, - default=None, - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - help="The start date of the data (format: YEAR-MONTH-DAY, i.e. 2010-12-31)", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - help="The end date of the data (format: YEAR-MONTH-DAY, i.e. 2021-06-20)", - default=None, - ) - parser.add_argument( - "--convert", - dest="currency", - help="Convert the currency of the chosen country to a specified currency. To find the " - "currency symbols use '--show countries'", - choices=econdb_model.COUNTRY_CURRENCIES.values(), - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ) - if ns_parser: - parameters = list_from_str(ns_parser.parameters.upper()) - countries = list_from_str(ns_parser.countries.lower()) - if ns_parser.show: - if ns_parser.show == "parameters": - print_rich_table( - pd.DataFrame.from_dict(econdb_model.PARAMETERS, orient="index"), - show_index=True, - index_name="Parameter", - headers=["Name", "Period", "Description"], - ) - elif ns_parser.show == "countries": - print_rich_table( - pd.DataFrame(econdb_model.COUNTRY_CURRENCIES.items()), - show_index=False, - headers=["Country", "Currency"], - ) - elif ns_parser.show == "transform": - print_rich_table( - pd.DataFrame(econdb_model.TRANSFORM.items()), - show_index=False, - headers=["Code", "Transform"], - ) - return self.queue - - if ns_parser.parameters and ns_parser.countries: - # Store data - (df, units, _) = econdb_model.get_aggregated_macro_data( - parameters=parameters, - countries=countries, - transform=ns_parser.transform, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - symbol=ns_parser.currency, - ) - - if not df.empty: - if ns_parser.transform: - df.columns = [ - f"_{ns_parser.transform}_".join(column) - for column in df.columns - ] - else: - df.columns = ["_".join(column) for column in df.columns] - - # Display data just loaded - econdb_view.show_macro_data( - parameters=parameters, - countries=countries, - transform=ns_parser.transform, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - symbol=ns_parser.currency, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - - self.update_runtime_choices() - if get_current_user().preferences.ENABLE_EXIT_AUTO_HELP: - self.print_help() - - @check_api_key(["API_FRED_KEY"]) - def call_fred(self, other_args: List[str]): - """Process fred command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fred", - description="Query the FRED database and plot data based on the Series ID. [Source: FRED]", - ) - parser.add_argument( - "-p", - "--parameter", - type=str, - dest="parameter", - default="", - help="Series ID of the Macro Economic data from FRED", - required="-h" not in other_args - and "--help" not in other_args - and "-q" not in other_args - and "--query" not in other_args, - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-q", - "--query", - type=str, - action="store", - nargs="+", - dest="query", - help="Query the FRED database to obtain Series IDs given the query search term.", - required="-h" not in other_args - and "--help" not in other_args - and "-p" not in other_args - and "--parameter" not in other_args, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=100, - ) - if ns_parser: - parameters = list_from_str(ns_parser.parameter.upper()) - if ns_parser.query: - query = " ".join(ns_parser.query) - df_search = fred_model.get_series_notes(search_query=query) - - if not df_search.empty: - fred_view.notes(search_query=query, limit=ns_parser.limit) - - self.fred_query = df_search["id"].head(ns_parser.limit) - self.update_runtime_choices() - - if parameters: - console.print( - "\nWarning: -p/--parameter is ignored when using -q/--query." - ) - - return self.queue - - if parameters: - series_dict = {} - for series in parameters: - information = fred_model.check_series_id(series) - - if "seriess" in information: - series_dict[series] = { - "title": information["seriess"][0]["title"], - "units": information["seriess"][0]["units_short"], - } - - self.current_series = {series: series_dict[series]} - - if not series_dict: - return self.queue - - df, detail = fred_view.display_fred_series( - series_ids=parameters, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - get_data=True, - ) - - if not df.empty: - for series_id, data in detail.items(): - self.FRED_TITLES[series_id] = ( - f"{data['title']} ({data['units']})" - ) - - self.update_runtime_choices() - if get_current_user().preferences.ENABLE_EXIT_AUTO_HELP: - self.print_help() - - else: - console.print("[red]No data found for the given Series ID[/red]") - - elif not parameters and ns_parser.raw: - console.print( - "Warning: -r/--raw should be combined with -p/--parameter." - ) - - @log_start_end(log=logger) - def call_index(self, other_args: List[str]): - """Process index command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="index", - description="Obtain any set of indices and plot them together. With the -si argument the major indices are " - "shown. By using the arguments (for example 'nasdaq' and 'sp500') you can collect data and " - "plot the graphs together. [Source: Yahoo finance / FinanceDatabase]", - ) - parser.add_argument( - "-i", - "--indices", - type=str, - dest="indices", - help="One or multiple indices", - ) - parser.add_argument( - "--show", - dest="show_indices", - help="Show the major indices, their arguments and ticker", - action="store_true", - default=False, - ) - parser.add_argument( - "--interval", - type=str, - dest="interval", - help="The preferred interval data is shown at. This can be 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, " - "1d, 5d, 1wk, 1mo or 3mo", - choices=self.index_interval, - default="1d", - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - help="The start date of the data (format: YEAR-MONTH-DAY, i.e. 2010-12-31)", - default="2000-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - help="The end date of the data (format: YEAR-MONTH-DAY, i.e. 2021-06-20)", - default=None, - ) - parser.add_argument( - "-c", - "--column", - type=str, - dest="column", - help="The column you wish to load in, by default this is the Adjusted Close column", - default="Adj Close", - ) - parser.add_argument( - "-q", - "--query", - type=str, - nargs="+", - dest="query", - help="Search for indices with given keyword", - ) - parser.add_argument( - "-r", - "--returns", - help="Flag to show compounded returns over interval.", - dest="returns", - action="store_true", - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-i") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - limit=10, - ) - if ns_parser: - indices = list_from_str(ns_parser.indices) - if ns_parser.query and ns_parser.limit: - query = " ".join(ns_parser.query) - yfinance_view.search_indices(query, ns_parser.limit) - return self.queue - - if ns_parser.show_indices: - print_rich_table( - pd.DataFrame.from_dict(yfinance_model.INDICES, orient="index"), - show_index=True, - index_name="Argument", - headers=["Name", "Ticker"], - title="Major Indices", - ) - return self.queue - - if indices: - # We create a list of dataframes and a list of columns - # We then concatenate the dataframes and set the columns - dfs_indices, columns = [], [] - - for index in indices: - df = yfinance_model.get_index( - index, - interval=ns_parser.interval, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - column=ns_parser.column, - ) - dfs_indices.append(df) - - if not df.empty: - columns.append(index) - - # If no data is found, we print a message and return - if not dfs_indices: - text = "Indices" if len(indices) > 1 else "Index" - console.print(f"[red]No data found for the given {text}[/red]") - return self.queue - - # We concatenate the dataframes here to avoid having - # to regrab the data in the view - indices_data = pd.concat(dfs_indices, axis=1) - indices_data.columns = columns - - # If returns are requested, we calculate them here as well - if ns_parser.returns: - indices_data = indices_data.pct_change().dropna() - indices_data = indices_data + 1 - indices_data = indices_data.cumprod() - - yfinance_view.show_indices( - indices=indices_data, - interval=ns_parser.interval, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - column=ns_parser.column, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - returns=ns_parser.returns, - ) - - self.update_runtime_choices() - if get_current_user().preferences.ENABLE_EXIT_AUTO_HELP: - self.print_help() - - @log_start_end(log=logger) - def call_treasury(self, other_args: List[str]): - """Process treasury command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="treasury", - description="Obtain US treasury rates. Note: 3-month and 10-year treasury yields for other countries " - "are available via the command 'macro' and parameter 'M3YD' and 'Y10YD'. [Source: EconDB / FED]", - ) - parser.add_argument( - "-m", - "--maturity", - type=str, - dest="maturity", - help="The preferred maturity which is dependent on the type of the treasury", - default="10y", - ) - parser.add_argument( - "--show", - dest="show_maturities", - help="Show the maturities available for every instrument.", - action="store_true", - default=False, - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - help="The start date of the data (format: YEAR-MONTH-DAY, i.e. 2010-12-31)", - default="1934-01-31", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - help="The end date of the data (format: YEAR-DAY-MONTH, i.e. 2021-06-02)", - default=date.today(), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-m") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - limit=10, - ) - if ns_parser: - maturities = list_from_str(ns_parser.maturity) - if ns_parser.show_maturities: - console.print(",".join(fedreserve_model.all_mat)) - return None - - fedreserve_view.show_treasuries( - maturities=maturities, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - if get_current_user().preferences.ENABLE_EXIT_AUTO_HELP: - self.print_help() - - @log_start_end(log=logger) - def call_cpi(self, other_args: List[str]): - """Process cpi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cpi", - description="Plot (harmonized) consumer price indices for a " - "variety of countries and regions.", - ) - - parser.add_argument( - "-c", - "--countries", - dest="countries", - type=str, - help="What countries you'd like to collect data for", - default="united_states", - ) - - parser.add_argument( - "-u", - "--units", - dest="units", - type=str, - help="What units you'd like to collect data for", - default="growth_same", - choices=fred_model.CPI_UNITS, - ) - - parser.add_argument( - "--frequency", - dest="frequency", - type=str, - help="What frequency you'd like to collect data for", - default="monthly", - choices=fred_model.CPI_FREQUENCY, - ) - - parser.add_argument( - "--harmonized", - action="store_true", - dest="harmonized", - help="Whether to use harmonized cpi data", - default=False, - ) - - parser.add_argument( - "--no-smart-select", - action="store_false", - dest="smart_select", - help="Whether to assist with selection", - default=True, - ) - - parser.add_argument( - "-o", - "--options", - dest="options", - action="store_true", - help="See the available options", - default=False, - ) - - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=(dt.now() - relativedelta(years=30)).date(), - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=dt.now().date(), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - countries = list_from_str(ns_parser.countries) - - fred_view.plot_cpi( - countries=countries, - units=ns_parser.units, - frequency=ns_parser.frequency, - harmonized=ns_parser.harmonized, - smart_select=ns_parser.smart_select, - options=ns_parser.options, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ccpi(self, other_args: List[str]): - """Process ccpi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ccpi", - description="Inflation is measured in terms of the annual growth rate and in index, " - "2015 base year with a breakdown for food, energy and total excluding food and energy. " - "Inflation measures the erosion of living standards", - ) - - parser.add_argument( - "-c", - "--countries", - dest="countries", - type=str, - help="What countries you'd like to collect data for", - default="united_states", - ) - - parser.add_argument( - "-p", - "--perspective", - dest="perspective", - type=str, - help="Perspective of CPI you wish to obtain. This can be ENRG (energy), FOOD (food), " - "TOT (total) or TOT_FOODENRG (total excluding food and energy)", - default="TOT", - choices=["ENRG", "FOOD", "TOT", "TOT_FOODENRG"], - ) - - parser.add_argument( - "--frequency", - dest="frequency", - type=str, - help="What frequency you'd like to collect data for", - default="M", - choices=["M", "Q", "A"], - ) - - parser.add_argument( - "-u", - "--units", - dest="units", - type=str, - help="Units to get data in. Either 'AGRWTH' (annual growth rate) or IDX2015 (base = 2015)." - " Default is Annual Growth Rate (AGRWTH).", - default="AGRWTH", - choices=["AGRWTH", "IDX2015"], - ) - - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=(dt.now() - relativedelta(years=5)).date(), - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=dt.now().date(), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - countries = list_from_str(ns_parser.countries) - - oecd_view.plot_cpi( - countries=countries, - perspective=ns_parser.perspective, - frequency=ns_parser.frequency, - units=ns_parser.units, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_events(self, other_args: List[str]): - """Process events command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="events", - description="Economic calendar. If no start or end dates," - "default is the current day high importance events.", - ) - parser.add_argument( - "-c", - "--countries", - action="store", - dest="countries", - type=str, - default="", - help="Display calendar for specific country.", - ) - parser.add_argument( - "-n", - "--names", - help="Flag to show all available country names", - dest="names", - action="store_true", - default=False, - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="The start date of the data (format: YEAR-MONTH-DAY, i.e. 2010-12-31)", - default=dt.now().strftime("%Y-%m-%d"), - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="The start date of the data (format: YEAR-MONTH-DAY, i.e. 2010-12-31)", - default=dt.now().strftime("%Y-%m-%d"), - ) - parser.add_argument( - "-d", - "--date", - dest="spec_date", - type=valid_date, - help="Get a specific date for events. Overrides start and end dates.", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - limit=100, - ) - - if ns_parser: - if ns_parser.names: - for name in nasdaq_model.get_country_names(): - console.print(name) - return - - if ns_parser.names: - for name in nasdaq_model.get_country_names(): - console.print(name) - return - - if ns_parser.names: - for name in nasdaq_model.get_country_names(): - console.print(name) - return - - start_date = ( - ns_parser.start_date.strftime("%Y-%m-%d") - if ns_parser.start_date - else None - ) - - end_date = ( - ns_parser.end_date.strftime("%Y-%m-%d") if ns_parser.end_date else None - ) - - # TODO: Add `Investing` to sources again when `investpy` is fixed - - if ns_parser.spec_date: - start_date = ns_parser.spec_date.strftime("%Y-%m-%d") - end_date = ns_parser.spec_date.strftime("%Y-%m-%d") - - else: - start_date, end_date = sorted([start_date, end_date]) - - countries = list_from_str(ns_parser.countries) - - nasdaq_view.display_economic_calendar( - countries=countries, - start_date=start_date, - end_date=end_date, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_valuation(self, other_args: List[str]): - """Process valuation command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="valuation", - description=""" - View group (sectors, industry or country) valuation data. [Source: Finviz] - """, - ) - parser.add_argument( - "-g", - "--group", - type=str, - choices=list(self.d_GROUPS.keys()), - default="sector", - dest="group", - help="Data group (sectors, industry or country)", - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - type=str, - choices=list(self.valuation_sort_cols_dict.keys()), - default="Name", - help="Column to sort by", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-g") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - ns_group = ( - " ".join(ns_parser.group) - if isinstance(ns_parser.group, list) - else ns_parser.group - ) - finviz_view.display_valuation( - group=ns_group, - sortby=self.valuation_sort_cols_dict[ns_parser.sortby], - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_performance(self, other_args: List[str]): - """Process performance command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="performance", - description=""" - View group (sectors, industry or country) performance data. [Source: Finviz] - """, - ) - parser.add_argument( - "-g", - "--group", - type=str, - choices=list(self.d_GROUPS.keys()), - default="sector", - dest="group", - help="Data group (sector, industry or country)", - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - choices=list(self.performance_sort_dict.keys()), - default="Name", - help="Column to sort by", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-g") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - ns_group = ( - " ".join(ns_parser.group) - if isinstance(ns_parser.group, list) - else ns_parser.group - ) - finviz_view.display_performance( - group=ns_group, - sortby=self.performance_sort_dict[ns_parser.sortby], - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_edebt(self, other_args: List[str]): - """Process edebt command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="edebt", - description=""" - National debt statistics for various countries. [Source: Wikipedia] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, limit=20 - ) - if ns_parser: - commodity_view.display_debt( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - limit=ns_parser.limit, - ) - - @log_start_end(log=logger) - def call_usdli(self, other_args: List[str]): - """Process usdli command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="usdli", - description=""" - The USD Liquidity Index is defined as: [WALCL - WLRRAL - WDTGAL]. It is expressed in billions of USD. - """, - ) - parser.add_argument( - "-o", - "--overlay", - type=str, - choices=list(fred_model.EQUITY_INDICES.keys()), - default="SP500", - dest="overlay", - help="The equity index to compare against. Set `show = True` for the list of choices.", - ) - parser.add_argument( - "-s", - "--show", - action="store_true", - dest="show", - default=False, - help="Show the list of available equity indices to overlay.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ) - if ns_parser: - fred_view.display_usd_liquidity( - overlay=ns_parser.overlay, - show=ns_parser.show, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/economy/economy_helpers.py b/openbb_terminal/economy/economy_helpers.py deleted file mode 100644 index a77092a4d5e..00000000000 --- a/openbb_terminal/economy/economy_helpers.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Economy helpers""" - -__docformat__ = "numpy" - -from typing import Dict - -import pandas as pd - -from openbb_terminal.economy import econdb_model, fred_view, yfinance_model -from openbb_terminal.rich_config import console - - -def text_transform(raw_text: str) -> str: - return f"{round(float(raw_text)/100, 3)}%" - - -def create_new_entry(dataset: Dict[str, pd.DataFrame], query: str) -> Dict: - """Create a new series based off previously loaded columns - - Parameters - ---------- - dataset: Dict[str,pd.DataFrame] - Economy datasets that are loaded - query: str - Query to execute - - Returns - ------- - Dict[str, pd.DataFrame] - """ - # Create a single dataframe from dictionary of dataframes - columns = [] - data = pd.DataFrame() - for _, df in dataset.items(): - if not df.empty: - columns.extend(df.columns) - data = pd.concat([data, df], axis=1) - # In order to account for potentially different index time steps, lets dropNans here. - # Potentially problematic down the road - data = data.dropna(axis=0) - - # Eval the query to generate new sequence - # if there is an = in the query, then there will be a new named column - if "=" in query: - new_column = query.split("=")[0].replace(" ", "") - if new_column in data.columns: - query = query.replace(new_column, new_column + "_duplicate") - new_column += "_duplicate" - # Wrap the eval in a syntax error in case the user does something not allowed - try: - new_df = data.eval(query) - except SyntaxError: - console.print( - "[red]Invalid syntax in query. Please enter something of the form `newcol=col1 + col2`[/red]\n" - ) - return dataset - except pd.errors.UndefinedVariableError as e: - console.print(f"[red]{e}[/red]") - return dataset - - # If custom exists in the dictionary, we need to append the current dataframe - if "custom" in dataset: - dataset["custom"] = pd.concat([dataset["custom"], new_df[[new_column]]]) - else: - dataset["custom"] = new_df[[new_column]] - return dataset - - # If there is not an equal (namely .eval(colA + colB), the result will be a series - # and not a dataframe. We can just call this custom_exp - - try: - data = pd.DataFrame(data.eval(query), columns=["custom_exp"]) - dataset["custom"] = data - except SyntaxError: - console.print( - "Invalid syntax in query. Please enter something of the form `newcol=col1 + col2`" - ) - return dataset - except pd.errors.UndefinedVariableError as e: - console.print(f"[red]{e}[/red]") - return dataset - return dataset - - -def update_stored_datasets_string(datasets) -> str: - stored_datasets_string = "" - for key in datasets: - # ensure that column name is not empty - if not datasets[key].columns.empty: - # update string - for i, col in enumerate(datasets[key].columns): - if i == 0: - stored_datasets_string += ( - "\n" - + " " * 2 - + key - + " " * (len("Stored datasets") - len(key) - 2) - + ": " - ) - stored_datasets_string += col - else: - stored_datasets_string += ", " + col - - return stored_datasets_string - - -def get_plot_macro(dataset, variable: str, units, data): - split = variable.split("_") - transform = "" - if len(split) == 3 and split[1] in econdb_model.TRANSFORM: - country, transform, parameter_abbreviation = split - elif len(split) == 4 and split[2] in econdb_model.TRANSFORM: - country = f"{split[0]} {split[1]}" - transform = split[2] - parameter_abbreviation = split[3] - elif len(split) == 2: - country, parameter_abbreviation = split - else: - country = f"{split[0]} {split[1]}" - parameter_abbreviation = split[2] - - parameter = econdb_model.PARAMETERS[parameter_abbreviation]["name"] - - units = units[country.replace(" ", "_")][parameter_abbreviation] - transformtype = f" ({econdb_model.TRANSFORM[transform]}) " if transform else " " - dataset[f"{country}{transformtype}[{parameter}, Units: {units}]"] = data[variable] - return dataset - - -def get_yaxis_data(datasets, units, titles, ys): - dataset_yaxis = pd.DataFrame() - if not ys: - return dataset_yaxis - for variable in ys: - for key, data in datasets.items(): - if variable in data.columns: - if key == "macro": - dataset_yaxis = get_plot_macro(dataset_yaxis, variable, units, data) - elif key == "fred": - compound_detail = titles[variable] - detail = { - "units": compound_detail.split("(")[-1].split(")")[0], - "title": compound_detail.split("(")[0].strip(), - } - data_to_plot, title = fred_view.format_data_to_plot( - data[variable], detail - ) - dataset_yaxis[title] = data_to_plot - elif key == "index" and variable in yfinance_model.INDICES: - dataset_yaxis[yfinance_model.INDICES[variable]["name"]] = data[ - variable - ] - elif key == "treasury": - parameter, maturity = variable.split("_") - dataset_yaxis[f"{parameter} [{maturity}]"] = data[variable] - else: - dataset_yaxis[variable] = data[variable] - break - return dataset_yaxis diff --git a/openbb_terminal/economy/fedreserve_model.py b/openbb_terminal/economy/fedreserve_model.py deleted file mode 100644 index a99b84f9d7e..00000000000 --- a/openbb_terminal/economy/fedreserve_model.py +++ /dev/null @@ -1,82 +0,0 @@ -from datetime import datetime -from io import BytesIO -from typing import List, Literal, Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal.helper_funcs import request - -maturities = Literal[ - "1m", - "3m", - "6m", - "1y", - "2y", - "3y", - "5y", - "7y", - "10y", - "20y", - "30y", -] -maturityType = Union[maturities, List[maturities]] -all_mat = [ - "1m", - "3m", - "6m", - "1y", - "2y", - "3y", - "5y", - "7y", - "10y", - "20y", - "30y", -] - - -def get_treasury_rates( - maturity: Optional[maturityType] = None, - start_date: str = "2005-01-01", - end_date: Optional[str] = datetime.now().strftime("%Y-%m-%d"), -) -> pd.DataFrame: - """Get treasury rates from Federal Reserve - - Parameters - ---------- - maturity : maturityType, optional - Maturity to get, by default all - start_date : str, optional - Start date of data, by default "2005-01-01" - end_date : str, optional - End date , by default today - - Returns - ------- - pd.DataFrame - Dataframe with date as index and maturity as columns - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> treasury_rates = openbb.economy.treasury() - """ - url = ( - "https://www.federalreserve.gov/datadownload/Output.aspx?rel=H15&series=bf17364827e38702b42a58cf8eaa3f78" - "&lastobs=&from=&to=&filetype=csv&label=include&layout=seriescolumn" - ) - r = request(url) - df = pd.read_csv(BytesIO(r.content), header=5, index_col=None, parse_dates=True) - df.columns = ["date"] + all_mat - df = df.replace("ND", np.nan).fillna("-").dropna(axis=0) - df = df[ - (pd.to_datetime(df.date) >= pd.to_datetime(start_date)) - & (pd.to_datetime(df.date) <= pd.to_datetime(end_date)) - ] - df[all_mat] = df[all_mat].applymap(lambda x: float(x) if x != "-" else x) - df["date"] = pd.to_datetime(df["date"]) - df = df.reset_index(drop=True).set_index("date") - if not maturity: - return df - return df[maturity] diff --git a/openbb_terminal/economy/fedreserve_view.py b/openbb_terminal/economy/fedreserve_view.py deleted file mode 100644 index cd24cdcd41d..00000000000 --- a/openbb_terminal/economy/fedreserve_view.py +++ /dev/null @@ -1,115 +0,0 @@ -import logging -import os -from datetime import datetime -from typing import List, Literal, Optional, Union - -import numpy as np - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.economy import fedreserve_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -maturities_types = Literal[ - "1m", - "3m", - "6m", - "1y", - "2y", - "3y", - "5y", - "7y", - "10y", - "20y", - "30y", -] -maturityType = Union[maturities_types, List[maturities_types]] - - -@log_start_end(log=logger) -def show_treasuries( - maturities: maturityType = "1y", - start_date: str = "1900-01-01", - end_date: Optional[str] = datetime.now().strftime("%Y-%m-%d"), - raw: bool = False, - external_axes: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> Union[OpenBBFigure, None]: - """Display U.S. Treasury rates [Source: EconDB] - - Parameters - ---------- - maturities : list - Treasury maturities to display. - start_date : str - Starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. - end_date : Optional[str] - End date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. - raw : bool - Whether to display the raw output. - external_axes : bool, optional - Whether to return the figure object or not, by default False - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - - Returns - ------- - Plots the Treasury Series. - """ - treasury_data = fedreserve_model.get_treasury_rates( - maturity=maturities, start_date=start_date, end_date=end_date - ) - if treasury_data.empty: - console.print("No data found matching the input.\n") - return None - - # This drops any nan values so that the ful index isn't shown when not all data present - treasury_data = treasury_data.replace("-", np.nan).dropna(how="all") - - title = ( - "U.S. Treasuries" - if treasury_data.shape[1] > 1 - else f"{maturities[0].replace('m',' Month').replace('y',' Year')} U.S. Treasury Rate" - ) - - fig = OpenBBFigure( - yaxis=dict(side="right", title="Yield (%)"), - title=title, - ) - - for col in treasury_data.columns: - non_nan = treasury_data[col].notna() - fig.add_scatter( - x=treasury_data.index[non_nan], - y=treasury_data[col][non_nan], - mode="lines", - name=col.replace("m", " Month").replace("y", " Year"), - ) - - if raw: - # was a -iloc so we need to flip the index as we use head - treasury_data = treasury_data.sort_index(ascending=False) - print_rich_table( - treasury_data, - headers=list(treasury_data.columns), - show_index=True, - title="U.S. Treasuries", - export=bool(export), - ) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "treasuries_data", - treasury_data, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/economy/finviz_model.py b/openbb_terminal/economy/finviz_model.py deleted file mode 100644 index cb71b5818e2..00000000000 --- a/openbb_terminal/economy/finviz_model.py +++ /dev/null @@ -1,250 +0,0 @@ -""" Finviz Model """ - -__docformat__ = "numpy" - -import logging -import webbrowser -from ast import literal_eval -from typing import List - -import pandas as pd -from finvizfinance.group import performance, spectrum, valuation - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -# pylint: disable=unsupported-assignment-operation - -logger = logging.getLogger(__name__) -# pylint: disable=unsupported-assignment-operation - -GROUPS = { - "sector": "Sector", - "industry": "Industry", - "basic_materials": "Industry (Basic Materials)", - "communication_services": "Industry (Communication Services)", - "consumer_cyclical": "Industry (Consumer Cyclical)", - "consumer_defensive": "Industry (Consumer Defensive)", - "energy": "Industry (Energy)", - "financial": "Industry (Financial)", - "healthcare": "Industry (Healthcare)", - "industrials": "Industry (Industrials)", - "real_Estate": "Industry (Real Estate)", - "technology": "Industry (Technology)", - "utilities": "Industry (Utilities)", - "country": "Country (U.S. listed stocks only)", - "capitalization": "Capitalization", -} - - -@log_start_end(log=logger) -def get_performance_map(period: str = "1d", map_filter: str = "sp500"): - """Opens Finviz map website in a browser. [Source: Finviz] - - Parameters - ---------- - period : str - Performance period. Available periods are 1d, 1w, 1m, 3m, 6m, 1y. - scope : str - Map filter. Available map filters are sp500, world, full, etf. - """ - # Conversion from period and type, to fit url requirements - d_period = {"1d": "", "1w": "w1", "1m": "w4", "3m": "w13", "6m": "w26", "1y": "w52"} - d_type = {"sp500": "sec", "world": "geo", "full": "sec_all", "etf": "etf"} - url = f"https://finviz.com/map.ashx?t={d_type[map_filter]}&st={d_period[period]}" - webbrowser.open(url) - - -@log_start_end(log=logger) -def get_groups() -> List[str]: - """Get group available""" - return list(GROUPS.keys()) - - -@log_start_end(log=logger) -def get_valuation_data( - group: str = "sector", sortby: str = "Name", ascend: bool = True -) -> pd.DataFrame: - """Get group (sectors, industry or country) valuation data. [Source: Finviz] - - Parameters - ---------- - group : str - Group by category. Available groups can be accessed through get_groups(). - sortby : str - Column to sort by - ascend : bool - Flag to sort in ascending order - - Returns - ------- - pd.DataFrame - dataframe with valuation/performance data - """ - - if group not in GROUPS: - console.print( - f"[red]Group {group} not found. Check available groups through get_groups().[/red]\n" - ) - return pd.DataFrame() - - try: - group = GROUPS[group] - df_group = valuation.Valuation().screener_view(group=group) - df_group.columns = [col.strip() for col in df_group.columns] - df_group = df_group.sort_values(by=sortby, ascending=ascend) - df_group.fillna("", inplace=True) - - # Passing Raw data to Pandas DataFrame if using interactive mode - if get_current_user().preferences.USE_INTERACTIVE_DF: - return df_group - df_group["Market Cap"] = df_group["Market Cap"].apply( - lambda x: ( - float(x.strip("B")) if x.endswith("B") else float(x.strip("M")) / 1000 - ) - ) - df_group["Volume"] = df_group["Volume"] / 1_000_000 - df_group = df_group.rename(columns={"Volume": "Volume [1M]"}) - return df_group - - except IndexError: - console.print("Data not found.\n") - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_performance_data( - group: str = "sector", sortby: str = "Name", ascend: bool = True -) -> pd.DataFrame: - """Get group (sectors, industry or country) performance data. [Source: Finviz] - - Parameters - ---------- - group : str - Group by category. Available groups can be accessed through get_groups(). - sortby : str - Column to sort by - ascend : bool - Flag to sort in ascending order - - Returns - ------- - pd.DataFrame - dataframe with performance data - """ - - if group not in GROUPS: - console.print( - f"[red]Group {group} not found. Check available groups through get_groups().[/red]\n" - ) - return pd.DataFrame() - - try: - group = GROUPS[group] - df_group = performance.Performance().screener_view(group=group) - df_group = df_group.rename( - columns={ - "Perf Week": "1W", - "Perf Month": "1M", - "Perf Quart": "3M", - "Perf Half": "6M", - "Perf Year": "1Y", - "Perf YTD": "YTD", - "Avg Volume": "Avg Volume", - } - ) - df_group.columns = [col.strip() for col in df_group.columns] - df_group["1W"] = df_group["1W"].apply(lambda x: float(x.strip("%")) / 100) - df_group = df_group.sort_values(by=sortby, ascending=ascend) - df_group.fillna("", inplace=True) - - # Passing Raw data to Pandas DataFrame if using interactive mode - if get_current_user().preferences.USE_INTERACTIVE_DF: - return df_group - - df_group["Volume"] = df_group["Volume"] / 1_000_000 - df_group["Avg Volume"] = df_group["Avg Volume"] / 1_000_000 - df_group = df_group.rename( - columns={"Volume": "Volume [1M]", "Avg Volume": "Avg Volume [1M]"} - ) - return df_group - - except IndexError: - console.print("Data not found.\n") - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_spectrum_data(group: str = "sector"): - """Get group (sectors, industry or country) valuation/performance data. [Source: Finviz] - - Parameters - ---------- - group : str - Group by category. Available groups can be accessed through get_groups(). - """ - if group not in GROUPS: - console.print( - f"[red]Group {group} not found. Check available groups through get_groups().[/red]\n" - ) - return - - group = GROUPS[group] - spectrum.Spectrum().screener_view(group=group) - - -@log_start_end(log=logger) -def get_futures( - future_type: str = "Indices", sortby: str = "ticker", ascend: bool = False -) -> pd.DataFrame: - """Get futures data. [Source: Finviz] - - Parameters - ---------- - future_type : str - From the following: Indices, Energy, Metals, Meats, Grains, Softs, Bonds, Currencies - sortby : str - Column to sort by - ascend : bool - Flag to sort in ascending order - - Returns - ------- - pd.Dataframe - Indices, Energy, Metals, Meats, Grains, Softs, Bonds, Currencies - """ - source = request( - "https://finviz.com/futures.ashx", headers={"User-Agent": get_user_agent()} - ).text - - slice_source = source[ - source.find("var groups = ") : source.find( # noqa: E203 - "\r\n\r\n groups.forEach(function(group) " - ) - ] - groups = literal_eval( - slice_source[ - : slice_source.find("\r\n var tiles = ") - 1 - ].strip("var groups = ") - ) - titles = literal_eval( - slice_source[ - slice_source.find("\r\n var tiles = ") : -1 # noqa: E203 - ].strip("\r\n var tiles = ") - ) - - d_futures: dict = {} - for future in groups: - d_futures[future["label"]] = [] - for ticker in future["contracts"]: - d_futures[future["label"]].append(titles[ticker["ticker"]]) - - df = pd.DataFrame(d_futures[future_type]) - df = df.set_index("label") - df = df.sort_values(by=sortby, ascending=ascend) - - df = df[["prevClose", "last", "change"]].fillna("") - - return df diff --git a/openbb_terminal/economy/finviz_view.py b/openbb_terminal/economy/finviz_view.py deleted file mode 100644 index 2a6519e3828..00000000000 --- a/openbb_terminal/economy/finviz_view.py +++ /dev/null @@ -1,161 +0,0 @@ -""" Finviz View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.economy import finviz_model -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_performance_map(period: str = "1d", map_filter: str = "sp500"): - """Opens Finviz map website in a browser. [Source: Finviz] - - Parameters - ---------- - period : str - Performance period. Available periods are 1d, 1w, 1m, 3m, 6m, 1y. - map_filter : str - Map filter. Available map filters are sp500, world, full, etf. - """ - finviz_model.get_performance_map(period, map_filter) - - -@log_start_end(log=logger) -def display_valuation( - group: str = "sector", - sortby: str = "Name", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display group (sectors, industry or country) valuation data. [Source: Finviz] - - Parameters - ---------- - group : str - Group by category. Available groups can be accessed through get_groups(). - sortby : str - Column to sort by - ascend : bool - Flag to sort in ascending order - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - """ - df_group = finviz_model.get_valuation_data(group, sortby, ascend) - - if df_group.empty: - return - df_group = df_group.rename(columns={"Name": ""}) - print_rich_table( - df_group, - show_index=False, - headers=list(df_group.columns), - title=f"{group.replace('_',' ').title()} Valuation Data", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "valuation", - df_group, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_performance( - group: str = "sector", - sortby: str = "Name", - ascend: bool = True, - export: str = "", - sheet_name: Optional[str] = None, -): - """View group (sectors, industry or country) performance data. [Source: Finviz] - - Parameters - ---------- - group : str - Group by category. Available groups can be accessed through get_groups(). - sortby : str - Column to sort by - ascend : bool - Flag to sort in ascending order - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - """ - df_group = finviz_model.get_performance_data(group, sortby, ascend) - df_group = df_group.rename( - columns={ - "Name": "", - "Week": "1W", - "Month": "1M", - "3Month": "3M", - "6Month": "6M", - "1Year": "1Y", - } - ) - if df_group.empty: - return - print_rich_table( - df_group, - show_index=False, - headers=df_group.columns, - title=f"{group.replace('_',' ').title()} Performance Data", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "performance", - df_group, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_future( - future_type: str = "Indices", - sortby: str = "ticker", - ascend: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display table of a particular future type. [Source: Finviz] - - Parameters - ---------- - future_type : str - From the following: Indices, Energy, Metals, Meats, Grains, Softs, Bonds, Currencies - sortby : str - Column to sort by - ascend : bool - Flag to sort in ascending order - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - """ - df = finviz_model.get_futures(future_type, sortby, ascend) - - print_rich_table( - df, - show_index=True, - headers=["prevClose", "last", "change (%)"], - title="Future Table [Source: FinViz]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - future_type.lower(), - df, - sheet_name, - ) diff --git a/openbb_terminal/economy/fred_model.py b/openbb_terminal/economy/fred_model.py deleted file mode 100644 index 933c7525c2c..00000000000 --- a/openbb_terminal/economy/fred_model.py +++ /dev/null @@ -1,443 +0,0 @@ -""" Fred Model """ - -__docformat__ = "numpy" - -import logging -import os -import pathlib -import textwrap -from typing import List, Optional, Tuple - -import certifi -import fred -import pandas as pd -from fredapi import Fred -from requests import HTTPError - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -harmonized_cpi_path = pathlib.Path(__file__).parent / "datasets" / "harmonized_cpi.csv" -cpi_path = pathlib.Path(__file__).parent / "datasets" / "cpi.csv" - -CPI_COUNTRIES = [ - "australia", - "austria", - "belgium", - "brazil", - "bulgaria", - "canada", - "chile", - "china", - "croatia", - "cyprus", - "czech_republic", - "denmark", - "estonia", - "euro_area", - "finland", - "france", - "germany", - "greece", - "hungary", - "iceland", - "india", - "indonesia", - "ireland", - "israel", - "italy", - "japan", - "korea", - "latvia", - "lithuania", - "luxembourg", - "malta", - "mexico", - "netherlands", - "new_zealand", - "norway", - "poland", - "portugal", - "romania", - "russian_federation", - "slovak_republic", - "slovakia", - "slovenia", - "south_africa", - "spain", - "sweden", - "switzerland", - "turkey", - "united_kingdom", - "united_states", -] - -CPI_UNITS = ["growth_previous", "growth_same", "index_2015"] - -CPI_FREQUENCY = ["monthly", "quarterly", "annual"] - -EQUITY_INDICES = { - "SP500": "S&P 500 Index", - "CBBTCUSD": "Coinbase Bitcoin", - "CBETHUSD": "Coinbase Ethereum", - "DJCA": "Dow Jones Composite Average", - "DJIA": "Dow Jones Industrial Average", - "DJTA": "Dow Jones Transportation Average", - "DJUA": "Dow Jones Utility Average", - "NASDAQCOM": "Nasdaq Composite Index", - "NASDAQ100": "Nasdaq 100 Index", - "WILL2500PR": "Wilshire 2500 Price Index", - "WILL2500PRGR": "Wilshire 2500 Growth Price Index", - "WILL2500INDGR": "Wilshire 2500 Growth Total Market Index", - "WILL2500PRVAL": "Wilshire 2500 Value Price Index", - "WILL2500INDVAL": "Wilshire 2500 Value Total Market Index", - "WILL4500PR": "Wilshire 4500 Price Index", - "WILL5000PR": "Wilshire 5000 Price Index", - "WILL5000PRFC": "Wilshire 5000 Full Cap Price Index", - "WILLLRGCAP": "Wilshire US Large-Cap Total Market Index", - "WILLLRGCAPPR": "Wilshire US Large-Cap Price Index", - "WILLLRGCAPGRPR": "Wilshire US Large-Cap Growth Price Index", - "WILLLRGCAPVALPR": "Wilshire US Large-Cap Value Price Index", - "WILLMIDCAP": "Wilshire US Mid-Cap Total Market Index", - "WILLMIDCAPPR": "Wilshire US Mid-Cap Price Index", - "WILLMIDCAPGRPR": "Wilshire US Mid-Cap Growth Price Index", - "WILLMIDCAPVALPR": "Wilshire US Mid-Cap Value Price Index", - "WILLSMLCAP": "Wilshire US Small-Cap Total Market Index", - "WILLSMLCAPPR": "Wilshire US Small-Cap Price Index", - "WILLSMLCAPGR": "Wilshire US Small-Cap Growth Total Market Index", - "WILLSMLCAPVAL": "Wilshire US Small-Cap Value Total Market Index", - "WILLMICROCAP": "Wilshire US Micro-Cap Total Market Index", - "WILLREITIND": "Wilshire US Real Estate Investment Trust Price Index", - "WILLRESIPR": "Wilshire US Real Estate Securities Price Index", - "DTWEXBGS": "Nominal Broad US Dollar Index", -} - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def check_series_id(series_id: str) -> Tuple[bool, dict]: - """Checks if series ID exists in fred - - Parameters - ---------- - series_id: str - Series ID to check - - Returns - ------- - Tuple[bool, Dict] - Boolean if series ID exists, - Dictionary of series information - """ - current_user = get_current_user() - url = ( - f"https://api.stlouisfed.org/fred/series?series_id={series_id}&api_key=" - f"{current_user.credentials.API_FRED_KEY}&file_type=json" - ) - r = request(url, headers={"User-Agent": get_user_agent()}) - # The above returns 200 if series is found - # There seems to be an occasional bug giving a 503 response where the json decoding fails - if r.status_code == 200: - payload = r.json() - - elif r.status_code >= 500: - payload = {} - # cover invalid api keys & series does not exist - elif r.status_code == 400: - payload = {} - if "api_key" in r.json()["error_message"]: - console.print("[red]Invalid API Key[/red]\n") - logger.error("[red]Invalid API Key[/red]\n") - elif "The series does not exist" in r.json()["error_message"]: - console.print(f"[red]{series_id} not found.[/red]\n") - logger.error("%s not found", str(series_id)) - else: - console.print(r.json()["error_message"]) - logger.error(r.json()["error_message"]) - - return payload - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_series_notes( - search_query: str, -) -> pd.DataFrame: - """Get series notes. [Source: FRED] - - Parameters - ---------- - search_query : str - Text query to search on fred series notes database - - Returns - ------- - pd.DataFrame - DataFrame of matched series - """ - - fred.key(get_current_user().credentials.API_FRED_KEY) - d_series = fred.search(search_query) - - df_fred = pd.DataFrame() - - if "error_message" in d_series: - if "api_key" in d_series["error_message"]: - console.print("[red]Invalid API Key[/red]\n") - else: - console.print(d_series["error_message"]) - else: - if "seriess" in d_series: - if d_series["seriess"]: - df_fred = pd.DataFrame(d_series["seriess"]) - df_fred["notes"] = df_fred["notes"].fillna("No description provided.") - else: - console.print("No matches found. \n") - else: - console.print("No matches found. \n") - - if "notes" in df_fred.columns: - df_fred["notes"] = df_fred["notes"].apply( - lambda x: ( - "\n".join(textwrap.wrap(x, width=100)) if isinstance(x, str) else x - ) - ) - if "title" in df_fred.columns: - df_fred["title"] = df_fred["title"].apply( - lambda x: ( - "\n".join(textwrap.wrap(x, width=50)) if isinstance(x, str) else x - ) - ) - - return df_fred - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_series_data( - series_id: str, start_date: Optional[str] = None, end_date: Optional[str] = None -) -> pd.DataFrame: - """Get Series data. [Source: FRED] - - Parameters - ---------- - series_id : str - Series ID to get data from - start_date : Optional[str] - Start date to get data from, format yyyy-mm-dd - end_date : Optional[str] - End data to get from, format yyyy-mm-dd - - Returns - ------- - pd.DataFrame - Series data - """ - df = pd.DataFrame() - - try: - # Necessary for installer so that it can locate the correct certificates for - # API calls and https - # https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/73270162#73270162 - os.environ["REQUESTS_CA_BUNDLE"] = certifi.where() - os.environ["SSL_CERT_FILE"] = certifi.where() - fredapi_client = Fred(get_current_user().credentials.API_FRED_KEY) - df = fredapi_client.get_series(series_id, start_date, end_date) - # Series does not exist & invalid api keys - except HTTPError as e: - console.print(e) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_aggregated_series_data( - series_ids: List[str], - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> Tuple[pd.DataFrame, dict]: - """Get Series data. [Source: FRED] - - Parameters - ---------- - series_ids : List[str] - Series ID to get data from - start_date : str - Start date to get data from, format yyyy-mm-dd - end_date : str - End data to get from, format yyyy-mm-dd - - Returns - ------- - pd.DataFrame - Series data - dict - Dictionary of series ids and titles - """ - - data = pd.DataFrame() - - detail = {} - for ids in series_ids: - information = check_series_id(ids) - - if "seriess" in information: - detail[ids] = { - "title": information["seriess"][0]["title"], - "units": information["seriess"][0]["units_short"], - } - - for s_id in series_ids: - series = pd.DataFrame( - get_series_data(s_id, start_date, end_date), columns=[s_id] - ).dropna() - - data[s_id] = series[s_id] - - return data, detail - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_cpi( - countries: list, - units: str = "growth_same", - frequency: str = "monthly", - harmonized: bool = False, - smart_select: bool = True, - options: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Obtain CPI data from FRED. [Source: FRED] - - Parameters - ---------- - countries: list - The country or countries you want to see. - units: str - The units you want to see, can be "growth_previous", "growth_same" or "index_2015". - frequency: str - The frequency you want to see, either "annual", monthly" or "quarterly". - harmonized: bool - Whether you wish to obtain harmonized data. - smart_select: bool - Whether to assist with the selection. - options: bool - Whether to return the options. - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series = pd.read_csv(harmonized_cpi_path) if harmonized else pd.read_csv(cpi_path) - - if options: - return series.drop(["series_id"], axis=1) - - step_1 = series[series["country"].str.contains("|".join(countries))] - step_2 = step_1[step_1["units"] == units] - step_3 = step_2[step_2["frequency"] == frequency] - - if smart_select: - if not step_3.empty and step_3["country"].nunique() == len(countries): - series = step_3 - elif not step_2.empty and step_2["country"].nunique() == len(countries): - console.print( - f"No {frequency} data available for all countries. Using common frequency." - ) - frequency_new = step_1["frequency"].mode()[0] - series = step_2[step_2["frequency"] == frequency_new] - else: - console.print( - f"No {frequency} and {units} data available for all countries. Using remaining options." - ) - series = step_3 if not step_3.empty else step_2 - else: - series = step_3 - - if series.empty: - console.print( - "The combination of parameters does not result in any data. Please consider " - "using the `options` parameter to see the available options. Note that there " - "are two options list, one with `harmonized` and one without." - ) - return pd.DataFrame() - - series_dictionary = {} - - for series_id, country_value, frequency_value, unit_value in series[ - ["series_id", "country", "frequency", "units"] - ].values: - series_dictionary[f"{country_value}-{frequency_value}-{unit_value}"] = ( - get_series_data( - series_id=series_id, start_date=start_date, end_date=end_date - ) - ) - - df = pd.DataFrame.from_dict(series_dictionary) - df.index = pd.to_datetime(df.index).date - df = df.dropna() - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_usd_liquidity(overlay: str = "", show: bool = False) -> pd.DataFrame: - """The USD Liquidity Index is defined as: [WALCL - WLRRAL - WDTGAL]. It is expressed in billions of USD. - - Parameters - ----------- - overlay: str - An equity index to overlay, as a FRED Series ID. Defaults to none. - show: bool - Shows the list of valid equity indices to overlay. - - Returns - -------- - pd.DataFrame - DataFrame with the USD Liquidity Index. - - Examples - ---------- - >>> from openbb_terminal.economy import fred_model - >>> usd_liquidity = fred_model.get_usd_liquidity() - - Display the list of equity indices: - >>> fred_model.get_usd_liquidity(show = True) - """ - - if show: - return pd.DataFrame.from_dict(data=EQUITY_INDICES, orient="index").rename( - columns={0: "Index Name"} - ) - - data = pd.DataFrame() - data["WALCL"] = get_series_data("WALCL").astype("int64") * 1000000 - data["WLRRAL"] = get_series_data("WLRRAL").astype("int64") * 1000000 - data["WDTGAL"] = get_series_data("WDTGAL").astype("int64") * 1000000 - data["USD Liquidity Index (Billions of USD)"] = round( - (data["WALCL"] - data["WLRRAL"] - data["WDTGAL"]) / 1000000000, 2 - ) - - if overlay != "": - overlay = overlay.upper() - if overlay not in EQUITY_INDICES: - console.print( - "Invalid choice for the overlay." - "Use `get_usd_liquidity(show = True)` to see display the list of choices." - ) - return pd.DataFrame() - - overlay_df = pd.DataFrame() - overlay_df[f"{EQUITY_INDICES[overlay]}"] = get_series_data(overlay) - liquidity_df = data[["USD Liquidity Index (Billions of USD)"]].join(overlay_df) - return liquidity_df.dropna() - - return data[["USD Liquidity Index (Billions of USD)"]] diff --git a/openbb_terminal/economy/fred_view.py b/openbb_terminal/economy/fred_view.py deleted file mode 100644 index 21fc2fa0624..00000000000 --- a/openbb_terminal/economy/fred_view.py +++ /dev/null @@ -1,412 +0,0 @@ -""" Fred View """ - -__docformat__ = "numpy" - -import logging -import os -import textwrap -from typing import List, Optional, Tuple, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.economy import fred_model -from openbb_terminal.economy.plot_view import show_plot -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -# pylint: disable=too-many-arguments,inconsistent-return-statements - - -@log_start_end(log=logger) -def format_units(num: int) -> str: - """Helper to format number into string with K,M,B,T. Number will be in form of 10^n""" - number_zeros = int(np.log10(num)) - if number_zeros < 3: - return str(num) - if number_zeros < 6: - return f"{int(num/1000)}K" - if number_zeros < 9: - return f"{int(num/1_000_000)}M" - if number_zeros < 12: - return f"{int(num/1_000_000_000)}B" - if number_zeros < 15: - return f"{int(num/1_000_000_000_000)}T" - return f"10^{number_zeros}" - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def notes( - search_query: str, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display series notes. [Source: FRED] - - Parameters - ---------- - search_query : str - Text query to search on fred series notes database - limit : int - Maximum number of series notes to display - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - sheet_name : Optional[str] - The name of the sheet - """ - df_search = fred_model.get_series_notes(search_query) - - if df_search.empty: - return - - print_rich_table( - df_search[["id", "title", "notes"]], - title=f"[bold]Search results for {search_query}[/bold]", - show_index=False, - headers=["Series ID", "Title", "Description"], - limit=limit, - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fred", - df_search, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def display_fred_series( - series_ids: List[str], - start_date: Optional[str] = None, - end_date: Optional[str] = None, - limit: int = 10, - get_data: bool = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display (multiple) series from https://fred.stlouisfed.org. [Source: FRED] - - Parameters - ---------- - series_ids : List[str] - FRED Series ID from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3 - start_date : Optional[str] - Starting date (YYYY-MM-DD) of data - end_date : Optional[str] - Ending date (YYYY-MM-DD) of data - limit : int - Number of data points to display. - raw : bool - Output only raw data - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - sheet_name : Optional[str] - The name of the sheet - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - data, detail = fred_model.get_aggregated_series_data( - series_ids, start_date, end_date - ) - - if data.empty: - logger.error("No data") - return console.print("[red]No data available.[/red]\n") - - # Try to get everything onto the same 0-10 scale. - # To do so, think in scientific notation. Divide the data by whatever the E would be - - fig = OpenBBFigure(title="FRED Series") - for s_id, sub_dict in detail.items(): - data_to_plot, title = format_data_to_plot(data[s_id], sub_dict) - title = title.replace( - "Assets: Total Assets: Total Assets", "Assets: Total Assets" - ) - - fig.add_scatter( - x=data_to_plot.index, - y=data_to_plot, - showlegend=len(series_ids) > 1, - name=( - "
".join(textwrap.wrap(title, 80)) if len(series_ids) < 5 else title - ), - ) - if len(series_ids) == 1: - fig.set_title(title) - - fig.update_layout( - margin=dict(b=20), - legend=dict(yanchor="top", y=-0.06, xanchor="left", x=0), - ) - data.index = [x.strftime("%Y-%m-%d") for x in data.index] - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fred", - data, - sheet_name, - fig, - ) - return None, None - - if raw: - data = data.sort_index(ascending=False) - print_rich_table( - data, - headers=list(data.columns), - show_index=True, - index_name="Date", - export=bool(export), - limit=limit, - ) - return None, None - - if get_data: - fig.show(external=external_axes) - return data, detail - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_cpi( - countries: list, - units: str = "growth_same", - frequency: str = "monthly", - harmonized: bool = False, - smart_select: bool = True, - options: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -) -> Union[None, OpenBBFigure]: - """ - Inflation measured by consumer price index (CPI) is defined as the change in - the prices of a basket of goods and services that are typically purchased by - specific groups of households. Inflation is measured in terms of the annual - growth rate and in index, 2015 base year with a breakdown for food, energy - and total excluding food and energy. Inflation measures the erosion of living standards. - [Source: FRED / OECD] - - Parameters - ---------- - countries: list - List of countries to plot - units: str - Units of the data, either "growth_same", "growth_previous", "index_2015" - frequency: str - Frequency of the data, either "monthly", "quarterly" or "annual" - harmonized: bool - Whether to use harmonized data - smart_select: bool - Whether to automatically select the best series - options: bool - Whether to show options - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - raw: bool - Show raw data - export: str - Export data to csv or excel file - sheet_name: str - Name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series = ( - pd.read_csv(fred_model.harmonized_cpi_path) - if harmonized - else pd.read_csv(fred_model.cpi_path) - ) - - df = fred_model.get_cpi( - countries=countries, - units=units, - frequency=frequency, - harmonized=harmonized, - smart_select=smart_select, - options=options, - start_date=start_date, - end_date=end_date, - ) - - if options: - return print_rich_table(series.drop(["series_id"], axis=1)) - - ylabel_dict = { - "growth_same": "Growth Same Period of Previous Year (%)", - "growth_previous": "Growth Previous Period (%)", - } - - country = str(countries[0]).replace("_", " ").title() - title = f"{'Harmonized ' if harmonized else ''} Consumer Price" - title += ( - " Indices" - if len(df.columns) > 1 - else f" Index for {country}" if country else "" - ) - - fig = OpenBBFigure(yaxis_title=ylabel_dict.get(units, "Index (2015=100)")) - fig.set_title(title) - - for column in df.columns: - country, frequency, units = column.split("-") - label = f"{str(country).replace('_', ' ').title()}" - - fig.add_scatter(x=df.index, y=df[column].values, name=label) - - if raw: - # was a -iloc so we need to flip the index as we use head - df = df.sort_index(ascending=False) - print_rich_table( - df, - title=title, - show_index=True, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "CP", - df / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -def format_data_to_plot(data: pd.DataFrame, detail: dict) -> Tuple[pd.DataFrame, str]: - """Helper to format data to plot""" - - data_to_plot = data.dropna() - exponent = int(np.log10(data_to_plot.max())) - data_to_plot /= 10**exponent - multiplier = f"x {format_units(10**exponent)}" if exponent > 0 else "" - title = f"{detail['title']} ({detail['units']}) {'['+multiplier+']' if multiplier else ''}" - - data_to_plot.index = pd.to_datetime(data_to_plot.index) - - return data_to_plot, title - - -def display_usd_liquidity( - overlay: str = "SP500", - show: bool = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Display US Dollar Liquidity - Parameters - ----------- - overlay: str - An equity index to overlay, as a FRED Series ID. Defaults to "SP500". - show: bool - Shows the list of valid equity indices to overlay. - raw: bool - Show raw data - export: str - Export data to csv or excel file - sheet_name: str - Name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if show: - print_rich_table( - fred_model.get_usd_liquidity(show=True), - title="Available Equity Indices to Overlay", - show_index=True, - index_name="FRED Series ID", - export=bool(export), - ) - return None - - overlay = overlay.upper() - if overlay not in fred_model.EQUITY_INDICES: - console.print( - "Invalid choice. Display available choices by adding the parameter, `show = True`." - ) - return None - - data = fred_model.get_usd_liquidity(overlay=overlay) - y1 = pd.DataFrame(data.iloc[:, 0]) - y2 = pd.DataFrame(data.iloc[:, 1]) - fig = show_plot(y1, y2, external_axes=True) - - fig.update_layout( - title=dict(text="USD Liquidity Index"), - margin=dict(l=125, t=100), - yaxis=dict( - showgrid=False, - title=dict( - text="USD Liquidity Index (Billions of USD)", - ), - ), - yaxis2=dict( - title=data.columns[1], - showgrid=True, - ), - title_y=0.97, - title_x=0.5, - legend=dict( - orientation="h", - yanchor="top", - y=1.075, - xanchor="center", - x=0.5, - ), - ) - fig.update_yaxes(title_font=dict(size=16)) - - if raw: - print_rich_table( - data, - title="USD Liquidity Index", - show_index=True, - index_name="Date", - floatfmt=".2f", - export=bool(export), - ) - return None - - if export and export != "": - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "usd_liquidity", - data, - sheet_name, - fig, - ) - return None - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/economy/nasdaq_model.py b/openbb_terminal/economy/nasdaq_model.py deleted file mode 100644 index bb2a0184d87..00000000000 --- a/openbb_terminal/economy/nasdaq_model.py +++ /dev/null @@ -1,234 +0,0 @@ -"""NASDAQ Data Link Model""" - -__docformat__ = "numpy" - -import argparse -import logging -import pathlib -from datetime import datetime as dt -from typing import List, Optional, Union - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -NASDAQ_COUNTRY_CODES_PATH = ( - pathlib.Path(__file__).parent / "datasets" / "NASDAQ_CountryCodes.csv" -) - - -@log_start_end(log=logger) -def get_economic_calendar( - countries: Union[List[str], str] = "", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get economic calendar for countries between specified dates - - Parameters - ---------- - countries : [List[str],str] - List of countries to include in calendar. Empty returns all - start_date : Optional[str] - Start date for calendar - end_date : Optional[str] - End date for calendar - - Returns - ------- - pd.DataFrame - Economic calendar - - Examples - -------- - Get todays economic calendar for the United States - >>> from openbb_terminal.sdk import openbb - >>> calendar = openbb.economy.events("united_states") - - To get multiple countries for a given date, pass the same start and end date as well as - a list of countries - >>> calendars = openbb.economy.events(["united_states", "canada"], start_date="2022-11-18", end_date="2022-11-18") - """ - - if start_date is None: - start_date = dt.now().strftime("%Y-%m-%d") - - if end_date is None: - end_date = dt.now().strftime("%Y-%m-%d") - - if countries == "": - countries = [] - if isinstance(countries, str): - countries = [countries] - - countries = [country.replace("_", " ").title() for country in countries] - - if start_date == end_date: - dates = [start_date] - else: - dates = ( - pd.date_range(start=start_date, end=end_date).strftime("%Y-%m-%d").tolist() - ) - calendar = pd.DataFrame() - for date in dates: - try: - df = pd.DataFrame( - request( - f"https://api.nasdaq.com/api/calendar/economicevents?date={date}", - headers={ - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " - "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36" - }, - ).json()["data"]["rows"] - ).replace(" ", "-") - df.loc[:, "Date"] = date - calendar = pd.concat([calendar, df], axis=0) - except TypeError: - continue - - if calendar.empty: - console.print("[red]No data found for date range.[/red]") - return pd.DataFrame() - - calendar = calendar.rename( - columns={ - "gmt": "Time (ET)", - "country": "Country", - "eventName": "Event", - "actual": "Actual", - "consensus": "Consensus", - "previous": "Previous", - } - ) - - calendar = calendar.drop(columns=["description"]) - if not countries: - return calendar - - calendar = calendar[calendar["Country"].isin(countries)].reset_index(drop=True) - if calendar.empty: - console.print(f"[red]No data found for {', '.join(countries)}[/red]") - return pd.DataFrame() - return calendar - - -@log_start_end(log=logger) -def check_country_code_type(list_of_codes: str) -> List[str]: - """Check that codes are valid for NASDAQ API""" - nasdaq_codes = list(pd.read_csv(NASDAQ_COUNTRY_CODES_PATH)["Code"]) - valid_codes = [ - code.upper() - for code in list_of_codes.split(",") - if code.upper() in nasdaq_codes - ] - - if valid_codes: - return valid_codes - raise argparse.ArgumentTypeError("No valid codes provided.") - - -@log_start_end(log=logger) -def get_country_codes() -> List[str]: - """Get available country codes for Bigmac index - - Returns - ------- - List[str] - List of ISO-3 letter country codes. - """ - file = NASDAQ_COUNTRY_CODES_PATH - codes = pd.read_csv(file, index_col=0) - return codes - - -@log_start_end(log=logger) -def get_country_names() -> List[str]: - """Get available country names in Nasdaq API - - Returns - ------- - List[str] - List of country names. - """ - file = NASDAQ_COUNTRY_CODES_PATH - df = pd.read_csv(file, index_col=0) - countries = df["Country"] - countries_list = [x.lower().replace(" ", "_") for x in countries] - return countries_list - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_QUANDL"]) -def get_big_mac_index(country_code: str = "USA") -> pd.DataFrame: - """Get the Big Mac index calculated by the Economist - - Parameters - ---------- - country_code : str - ISO-3 letter country code to retrieve. Codes available through get_country_codes(). - - Returns - ------- - pd.DataFrame - Dataframe with Big Mac index converted to USD equivalent. - """ - URL = f"https://data.nasdaq.com/api/v3/datasets/ECONOMIST/BIGMAC_{country_code}" - URL += f"?column_index=3&api_key={get_current_user().credentials.API_KEY_QUANDL}" - try: - r = request(URL) - except Exception: - console.print("[red]Error connecting to NASDAQ API[/red]\n") - return pd.DataFrame() - - df = pd.DataFrame() - - if r.status_code == 200: - response_json = r.json() - df = pd.DataFrame(response_json["dataset"]["data"]) - df.columns = response_json["dataset"]["column_names"] - df["Date"] = pd.to_datetime(df["Date"]) - - else: - console.print(r.text) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_QUANDL"]) -def get_big_mac_indices(country_codes: Optional[List[str]] = None) -> pd.DataFrame: - """Display Big Mac Index for given countries - - Parameters - ---------- - country_codes : List[str] - List of country codes (ISO-3 letter country code). Codes available through economy.country_codes(). - - Returns - ------- - pd.DataFrame - Dataframe with Big Mac indices converted to USD equivalent. - """ - big_mac = pd.DataFrame() - - if country_codes is None: - country_codes = ["USA"] - - dfs = [] - for country in country_codes: - df1 = get_big_mac_index(country) - if not df1.empty: - df1 = df1.rename(columns={"dollar_price": country}) - df1 = df1.set_index("Date") - dfs.append(df1) - if dfs: - big_mac = pd.concat(dfs, axis=1) - big_mac = big_mac.reset_index() - big_mac = big_mac.set_index("Date") - - return big_mac diff --git a/openbb_terminal/economy/nasdaq_view.py b/openbb_terminal/economy/nasdaq_view.py deleted file mode 100644 index e78570444f9..00000000000 --- a/openbb_terminal/economy/nasdaq_view.py +++ /dev/null @@ -1,118 +0,0 @@ -"""NASDAQ Data Link View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import List, Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.economy import nasdaq_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_economic_calendar( - countries: List[str], - start_date: str, - end_date: str, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display economic calendar for specified country between start and end dates - - Parameters - ---------- - countries : List[str] - List of countries to include in calendar. Empty returns all - start_date : str - Start date for calendar - end_date : str - End date for calendar - limit : int - Limit number of rows to display - export : str - Export data to csv or excel file - """ - df = nasdaq_model.get_economic_calendar(countries, start_date, end_date) - if df.empty: - return - print_rich_table( - df, - title="Economic Calendar", - show_index=False, - headers=df.columns, - export=bool(export), - limit=limit, - ) - console.print() - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "events", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_QUANDL"]) -def display_big_mac_index( - country_codes: Optional[List[str]] = None, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display Big Mac Index for given countries - - Parameters - ---------- - country_codes : List[str] - List of country codes (ISO-3 letter country code). Codes available through economy.country_codes(). - raw : bool, optional - Flag to display raw data, by default False - export : str, optional - Format data, by default "" - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - big_mac = nasdaq_model.get_big_mac_indices(country_codes) - - if not big_mac.empty: - fig = OpenBBFigure(title="Big Mac Index", yaxis_title="Price of Big Mac in USD") - for country in big_mac.columns: - fig.add_scatter( - x=big_mac.index, - y=big_mac[country], - mode="lines+markers", - name=country, - ) - - if raw: - print_rich_table( - big_mac, - headers=list(big_mac.columns), - title="Big Mac Index", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "bigmac", - big_mac, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - logger.error("Unable to get big mac data") - return console.print("[red]Unable to get big mac data[/red]\n") diff --git a/openbb_terminal/economy/oecd_model.py b/openbb_terminal/economy/oecd_model.py deleted file mode 100644 index 9de69682b24..00000000000 --- a/openbb_terminal/economy/oecd_model.py +++ /dev/null @@ -1,1274 +0,0 @@ -""" OECD model """ - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import List, Optional - -import pandas as pd -from dateutil.relativedelta import relativedelta - -from openbb_terminal.decorators import console, log_start_end - -logger = logging.getLogger(__name__) - -# pylint: disable=C0302 - -COUNTRY_TO_CODE_GDP = { - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "canada": "CAN", - "chile": "CHL", - "colombia": "COL", - "costa_rica": "CRI", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "euro_area": "EA", - "european_union": "EU", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "indonesia": "IDN", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "norway": "NOR", - "poland": "POL", - "portugal": "PRT", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "south_africa": "ZAF", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", -} - -COUNTRY_TO_CODE_RGDP = { - "G20": "G-20", - "G7": "G-7", - "argentina": "ARG", - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "bulgaria": "BGR", - "canada": "CAN", - "chile": "CHL", - "china": "CHN", - "colombia": "COL", - "costa_rica": "CRI", - "croatia": "HRV", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "euro_area_19": "EA19", - "europe": "OECDE", - "european_union_27": "EU27_2020", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "india": "IND", - "indonesia": "IDN", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "norway": "NOR", - "oecd_total": "OECD", - "poland": "POL", - "portugal": "PRT", - "romania": "ROU", - "russia": "RUS", - "saudi_arabia": "SAU", - "slovak_republic": "SVK", - "slovenia": "SVN", - "south_africa": "ZAF", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", -} - -COUNTRY_TO_CODE_GDP_FORECAST = { - "argentina": "ARG", - "asia": "DAE", - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "bulgaria": "BGR", - "canada": "CAN", - "chile": "CHL", - "china": "CHN", - "colombia": "COL", - "costa_rica": "CRI", - "croatia": "HRV", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "euro_area_17": "EA17", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "india": "IND", - "indonesia": "IDN", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "non-oecd": "NMEC", - "norway": "NOR", - "oecd_total": "OECD", - "peru": "PER", - "poland": "POL", - "portugal": "PRT", - "romania": "ROU", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "south_africa": "ZAF", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", - "world": "WLD", -} - -COUNTRY_TO_CODE_CPI = { - "G20": "G-20", - "G7": "G-7", - "argentina": "ARG", - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "canada": "CAN", - "chile": "CHL", - "china": "CHN", - "colombia": "COL", - "costa_rica": "CRI", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "euro_area_19": "EA19", - "europe": "OECDE", - "european_union_27": "EU27_2020", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "india": "IND", - "indonesia": "IDN", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "norway": "NOR", - "oecd_total": "OECD", - "poland": "POL", - "portugal": "PRT", - "russia": "RUS", - "saudi_arabia": "SAU", - "slovak_republic": "SVK", - "slovenia": "SVN", - "south_africa": "ZAF", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", -} - -COUNTRY_TO_CODE_BALANCE = { - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "canada": "CAN", - "chile": "CHL", - "colombia": "COL", - "costa_rica": "CRI", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "euro_area": "EA", - "european_union": "EU", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "indonesia": "IDN", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "norway": "NOR", - "poland": "POL", - "portugal": "PRT", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "south_africa": "ZAF", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", -} - -COUNTRY_TO_CODE_REVENUE = { - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "canada": "CAN", - "chile": "CHL", - "colombia": "COL", - "costa_rica": "CRI", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "euro_area": "EA", - "european_union": "EU", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "indonesia": "IDN", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "norway": "NOR", - "oecd_average": "OAVG", - "oecd_europe": "OEU", - "oecd_total": "OECD", - "poland": "POL", - "portugal": "PRT", - "romania": "ROU", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", -} - -COUNTRY_TO_CODE_SPENDING = { - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "canada": "CAN", - "chile": "CHL", - "colombia": "COL", - "costa_rica": "CRI", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "indonesia": "IDN", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "norway": "NOR", - "oecd_average": "OAVG", - "oecd_europe": "OEU", - "oecd_total": "OECD", - "poland": "POL", - "portugal": "PRT", - "romania": "ROU", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", -} - -COUNTRY_TO_CODE_DEBT = { - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "canada": "CAN", - "chile": "CHL", - "colombia": "COL", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "norway": "NOR", - "oecd_average": "OAVG", - "oecd_total": "OECD", - "poland": "POL", - "portugal": "PRT", - "romania": "ROU", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", -} - -COUNTRY_TO_CODE_TRUST = { - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "brazil": "BRA", - "canada": "CAN", - "chile": "CHL", - "colombia": "COL", - "costa_rica": "CRI", - "czech_republic": "CZE", - "denmark": "DNK", - "estonia": "EST", - "finland": "FIN", - "france": "FRA", - "germany": "DEU", - "greece": "GRC", - "hungary": "HUN", - "iceland": "ISL", - "ireland": "IRL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "latvia": "LVA", - "lithuania": "LTU", - "luxembourg": "LUX", - "mexico": "MEX", - "netherlands": "NLD", - "new_zealand": "NZL", - "norway": "NOR", - "poland": "POL", - "portugal": "PRT", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "south_africa": "ZAF", - "spain": "ESP", - "sweden": "SWE", - "switzerland": "CHE", - "turkey": "TUR", - "united_kingdom": "GBR", - "united_states": "USA", -} - - -def no_data_message(error: str): - """Print message when no data available or error""" - console.print(f"Error getting data from OECD: [red]{error}[/red]") - - -@log_start_end(log=logger) -def get_gdp( - countries: Optional[str] = "united_states", - units: str = "USD", - start_date="", - end_date="", -) -> pd.DataFrame: - """ - Gross domestic product (GDP) is the standard measure of the value added created - through the production of goods and services in a country during a certain period. - As such, it also measures the income earned from that production, or the total amount - spent on final goods and services (less imports). While GDP is the single most important - indicator to capture economic activity, it falls short of providing a suitable measure of - people's material well-being for which alternative indicators may be more appropriate. - This indicator is based on nominal GDP (also called GDP at current prices or GDP in value) - and is available in different measures: US dollars and US dollars per capita (current PPPs). - All OECD countries compile their data according to the 2008 System of National Accounts (SNA). - This indicator is less suited for comparisons over time, as developments are not only caused - by real growth, but also by changes in prices and PPPs. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - units: str - Units to get data in. Either 'USD' or 'USD_CAP'. - Default is US dollars per capita. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with gdp data - """ - if not start_date: - start_date = datetime.now() - relativedelta(years=30) - if not end_date: - end_date = datetime.now() - - if isinstance(start_date, datetime): - start_date = start_date.date() - if isinstance(end_date, datetime): - end_date = end_date.date() - - units_dict = {"USD": "MLN_USD", "USD_CAP": "USD_CAP"} - - if units not in units_dict: - console.print( - "Invalid choice, choices are either USD or USD_CAP. Defaulting to USD." - ) - units = "USD" - - countries = [countries] if isinstance(countries, str) else countries # type: ignore[assignment] - - df = pd.DataFrame() - - try: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.GDP.TOT.{units_dict[units]}.A/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_GDP[country]]["Value"] - ) - temp.index = temp.index.map(str) - temp.columns = [country] - result = pd.concat([result, temp], axis=1) - except KeyError: - console.print(f"No data available for {country}.") - - result.index = pd.to_datetime(result.index, format="%Y") - result.sort_index(inplace=True) - result = result * 1000000 if units == "USD" else result - - return result - - -@log_start_end(log=logger) -def get_real_gdp( - countries: Optional[List[str]], - units: str = "PC_CHGPY", - start_date="", - end_date="", -) -> pd.DataFrame: - """ - Gross domestic product (GDP) is the standard measure of the value added - created through the production of goods and services in a country during - a certain period. As such, it also measures the income earned from that - production, or the total amount spent on final goods and services (less imports). - While GDP is the single most important indicator to capture economic activity, it - falls short of providing a suitable measure of people's material well-being for - which alternative indicators may be more appropriate. This indicator is based on - real GDP (also called GDP at constant prices or GDP in volume), i.e. the developments - over time are adjusted for price changes. The numbers are also adjusted for seasonal - influences. The indicator is available in different measures: percentage change from - the previous quarter, percentage change from the same quarter of the previous year and - volume index (2015=100). All OECD countries compile their data according to the 2008 - System of National Accounts (SNA). [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - units: str - Units to get data in. Either 'PC_CHGPP', 'PC_CHGPY' or 'IDX. - Default is percentage change from the same quarter of the previous year. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with the gdp data - """ - if not start_date: - start_date = (datetime.now() - relativedelta(years=10)).date() - elif isinstance(start_date, datetime): - start_date = start_date.date() - if not end_date: - end_date = datetime.now().date() - elif isinstance(end_date, datetime): - end_date = end_date.date() - - if units not in ["PC_CHGPP", "PC_CHGPY", "IDX"]: - return console.print( - "Use either PC_CHGPP (percentage change previous quarter), " - "PC_CHGPY (percentage change from the same quarter of the " - "previous year) or IDX (index with base at 2015) " - "for units" - ) - - df = pd.DataFrame() - - try: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.QGDP." - f"{'VOLIDX' if units == 'IDX' else 'TOT'}.{units}.Q/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_RGDP[country]]["Value"] - ) - temp.index = temp.index.map(str) - temp.columns = [country] - result = pd.concat([result, temp], axis=1) - except KeyError: - console.print(f"No data available for {country}.") - - result.index = ( - pd.PeriodIndex(result.index, freq="Q").to_timestamp().strftime("%Y-%m") - ) - result.sort_index(inplace=True) - - return result - - -@log_start_end(log=logger) -def get_gdp_forecast( - countries: Optional[List[str]], - types: str = "real", - units: str = "Q", - start_date="", - end_date="", -) -> pd.DataFrame: - """ - Real gross domestic product (GDP) is GDP given in constant prices and - refers to the volume level of GDP. Constant price estimates of GDP are - obtained by expressing values of all goods and services produced in a - given year, expressed in terms of a base period. Forecast is based on an - assessment of the economic climate in individual countries and the world economy, - using a combination of model-based analyses and expert judgement. This indicator - is measured in growth rates compared to previous year. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - type: str - Type of GDP to get data for. Either 'real' or 'nominal'. - Default s real GDP (real). - units: str - Units to get data in. Either 'Q' or 'A. - Default is Quarterly (Q). - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with the gdp data - """ - if not start_date: - start_date = (datetime.now() - relativedelta(years=10)).date() - elif isinstance(start_date, datetime): - start_date = start_date.date() - if not end_date: - end_date = (datetime.now() + relativedelta(years=10)).date() - elif isinstance(end_date, datetime): - end_date = end_date.date() - - if units not in ["Q", "A"]: - return console.print("Use either Q (quarterly) or A (annually) for units") - if types not in ["real", "nominal"]: - return console.print("Use either 'real' or 'nominal' for type") - - df = pd.DataFrame() - - try: - if types == "real": - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.REALGDPFORECAST.TOT.AGRWTH.{units}/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - else: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.NOMGDPFORECAST.TOT.AGRWTH.{units}/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_GDP_FORECAST[country]]["Value"] - ) - temp.index = temp.index.map(str) - temp.columns = [country] - - if temp.empty: - console.print(f"No {types} GDP data for {country} with {units}.") - else: - result = pd.concat([result, temp], axis=1) - - except KeyError: - console.print(f"No data available for {country}.") - - if units == "Q": - result.index = ( - pd.PeriodIndex(result.index, freq="Q").to_timestamp().strftime("%Y-%m") - ) - result.index = pd.to_datetime(result.index, format="%Y-%m") - else: - result.index = pd.to_datetime(result.index, format="%Y") - - result.sort_index(inplace=True) - - return result - - -@log_start_end(log=logger) -def get_debt( - countries: Optional[List[str]], - start_date="", - end_date="", -) -> pd.DataFrame: - """ - General government debt-to-GDP ratio measures the gross debt of the general - government as a percentage of GDP. It is a key indicator for the sustainability - of government finance. Debt is calculated as the sum of the following liability - categories (as applicable): currency and deposits; debt securities, loans; insurance, - pensions and standardised guarantee schemes, and other accounts payable. Changes in - government debt over time primarily reflect the impact of past government deficits. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with the debt data - """ - if not start_date: - start_date = (datetime.now() - relativedelta(years=30)).date() - elif isinstance(start_date, datetime): - start_date = start_date.date() - if not end_date: - end_date = datetime.now().date() - elif isinstance(end_date, datetime): - end_date = end_date.date() - - df = pd.DataFrame() - - try: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.GGDEBT.TOT.PC_GDP.A/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_DEBT[country]]["Value"] - ) - temp.index = temp.index.map(str) - temp.columns = [country] - result = pd.concat([result, temp], axis=1) - except KeyError: - console.print(f"No data available for {country}.") - - result.index = pd.to_datetime(result.index, format="%Y") - result.sort_index(inplace=True) - - return result - - -@log_start_end(log=logger) -def get_cpi( - countries: Optional[List[str]], - perspective: str = "TOT", - frequency: str = "Q", - units: str = "AGRWTH", - start_date="", - end_date="", -) -> pd.DataFrame: - """ - Inflation measured by consumer price index (CPI) is defined as the change in the prices - of a basket of goods and services that are typically purchased by specific groups of - households. Inflation is measured in terms of the annual growth rate and in index, - 2015 base year with a breakdown for food, energy and total excluding food and energy. - Inflation measures the erosion of living standards. A consumer price index is estimated - as a series of summary measures of the period-to-period proportional change in the - prices of a fixed set of consumer goods and services of constant quantity and - characteristics, acquired, used or paid for by the reference population. Each summary - measure is constructed as a weighted average of a large number of elementary aggregate indices. - Each of the elementary aggregate indices is estimated using a sample of prices for a defined - set of goods and services obtained in, or by residents of, a specific region from a given - set of outlets or other sources of consumption goods and services. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - perspective: str - Perspective of CPI you wish to obtain. This can be ENRG (energy), FOOD (food), - TOT (total) or TOT_FOODENRG (total excluding food and energy) - Default is Total CPI. - frequency: str - Frequency to get data in. Either 'M', 'Q' or 'A. - Default is Quarterly (Q). - units: str - Units to get data in. Either 'AGRWTH' (annual growth rate) or IDX2015 (base = 2015). - Default is Annual Growth Rate (AGRWTH). - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with cpi data - """ - if not start_date: - start_date = (datetime.now() - relativedelta(years=5)).date() - elif isinstance(start_date, datetime): - start_date = start_date.date() - if not end_date: - end_date = datetime.now().date() - elif isinstance(end_date, datetime): - end_date = end_date.date() - - if perspective not in ["ENRG", "FOOD", "TOT", "TOT_FOODENRG"]: - return console.print( - "Use either ENRG (energy), FOOD (food), " - "TOT (total) or TOT_FOODENRG (total excluding food and energy) for perspective" - ) - if frequency not in ["M", "Q", "A"]: - return console.print( - "Use either M, (monthly), Q (quarterly) or A (annually) for frequency" - ) - if units not in ["AGRWTH", "IDX2015"]: - return console.print("Use either 'AGRWTH' or 'IDX2015' for type") - - df = pd.DataFrame() - try: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.CPI.{perspective}.{units}.{frequency}/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_CPI[country]]["Value"] - ) - temp.index = temp.index.map(str) - temp.columns = [country] - - if temp.empty: - console.print( - f"No {perspective} CPI data available for " - f"{country.title()} with set parameters." - ) - else: - result = pd.concat([result, temp], axis=1) - except KeyError: - console.print(f"No data available for {country}.") - - if frequency in ["M", "Q"]: - result.index = pd.to_datetime(result.index).strftime("%Y-%m") - result.index = pd.to_datetime(result.index, format="%Y-%m") - else: - result.index = pd.to_datetime(result.index, format="%Y") - - result.sort_index(inplace=True) - - return result - - -@log_start_end(log=logger) -def get_balance( - countries: Optional[List[str]], - start_date="", - end_date="", -) -> pd.DataFrame: - """ - General government deficit is defined as the balance of income and expenditure of government, - including capital income and capital expenditures. "Net lending" means that governmen - has a surplus, and is providing financial resources to other sectors, while - "net borrowing" means that government has a deficit, and requires financial - esources from other sectors. This indicator is measured as a percentage of GDP. - All OECD countries compile their data according to the - 2008 System of National Accounts (SNA 2008). [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with the balance data - """ - if not start_date: - start_date = (datetime.now() - relativedelta(years=30)).date() - elif isinstance(start_date, datetime): - start_date = start_date.date() - if not end_date: - end_date = datetime.now().date() - elif isinstance(end_date, datetime): - end_date = end_date.date() - - df = pd.DataFrame() - try: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.GGNLEND.TOT.PC_GDP.A/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_BALANCE[country]]["Value"] - ) - temp.index = temp.index.map(str) - temp.columns = [country] - result = pd.concat([result, temp], axis=1) - except KeyError: - console.print(f"No data available for {country}.") - - result.index = pd.to_datetime(result.index, format="%Y") - result.sort_index(inplace=True) - - return result - - -@log_start_end(log=logger) -def get_revenue( - countries: Optional[List[str]], - units: str = "PC_GDP", - start_date="", - end_date="", -) -> pd.DataFrame: - """ - Governments collect revenues mainly for two purposes: to finance the goods - and services they provide to citizens and businesses, and to fulfil their - redistributive role. Comparing levels of government revenues across - countries provides an indication of the importance of the government - sector in the economy in terms of available financial resources. - The total amount of revenues collected by governments is determined - by past and current political decisions. This indicator is measured - in terms of thousand USD per capita, and as a percentage of GDP. All - OECD countries compile their data according to the 2008 System of - National Accounts (SNA 2008). [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - units: str - Units to get data in. Either 'PC_GDP' or 'THOUSAND_USD_PER_CAPITA'. - Default is Percentage of GDP. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with revenue data - """ - if not start_date: - start_date = (datetime.now() - relativedelta(years=30)).date() - elif isinstance(start_date, datetime): - start_date = start_date.date() - if not end_date: - end_date = datetime.now().date() - elif isinstance(end_date, datetime): - end_date = end_date.date() - - if units not in ["THND_USD_CAP", "PC_GDP"]: - return console.print( - "Use either THND_USD_CAP (thousands of USD per capity) " - "or PC_GDP (percentage of GDP) for units" - ) - - df = pd.DataFrame() - try: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.GGREV.TOT.{units}.A/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_REVENUE[country]]["Value"] - ) - - temp.index = temp.index.map(str) - temp.columns = [country] - result = pd.concat([result, temp], axis=1) - except KeyError: - console.print(f"No data available for {country}.") - - result.index = pd.to_datetime(result.index, format="%Y") - result.sort_index(inplace=True) - - return result - - -@log_start_end(log=logger) -def get_spending( - countries: Optional[List[str]], - perspective: str = "TOT", - units: str = "PC_GDP", - start_date="", - end_date="", -) -> pd.DataFrame: - """ - General government spending provides an indication of the size - of government across countries. The large variation in this indicator - highlights the variety of countries' approaches to delivering public - goods and services and providing social protection, not necessarily - differences in resources spent. This indicator is measured in terms of - thousand USD per capita, and as percentage of GDP. All OECD countries - compile their data according to the 2008 System of - National Accounts (SNA).[ Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - perspective: str - The type of spending. Choose form the following: - TOT (Total) - RECULTREL (Recreation, culture and religion) - HOUCOMM (Housing and community amenities) - PUBORD (Public order and safety) - EDU (Education) - ENVPROT (Environmental protection) - GRALPUBSER (General public services) - SOCPROT (Social protection) - ECOAFF (Economic affairs) - DEF (Defence) - HEALTH (Health) - units: str - Units to get data in. Either 'PC_GDP' or 'THOUSAND_USD_PER_CAPITA'. - Default is Percentage of GDP. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with the spending data - """ - if not start_date: - start_date = (datetime.now() - relativedelta(years=30)).date() - elif isinstance(start_date, datetime): - start_date = start_date.date() - if not end_date: - end_date = datetime.now().date() - elif isinstance(end_date, datetime): - end_date = end_date.date() - - if units not in ["THND_USD_CAP", "PC_GDP"]: - console.print( - "Use either THND_USD_CAP (thousands of USD per capity) " - "or PC_GDP (percentage of GDP) for units" - ) - return pd.DataFrame() - if perspective not in [ - "TOT", - "RECULTREL", - "HOUCOMM", - "PUBORD", - "EDU", - "ENVPROT", - "GRALPUBSER", - "SOCPROT", - "ECOAFF", - "DEF", - "HEALTH", - ]: - console.print( - "Use either TOT (Total), RECULTREL (Recreation, culture and religion), " - "HOUCOMM (Housing and community amenities), PUBORD (Public order and safety), " - "EDU (Education), ENVPROT (Environmental protection), GRALPUBSER (General public services), " - "SOCPROT (Social protection), ECOAFF (Economic affairs), DEF (Defence), HEALTH (Health)" - ) - - df = pd.DataFrame() - try: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.GGEXP.{perspective}.{units}.A/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_SPENDING[country]]["Value"] - ) - - temp.index = temp.index.map(str) - temp.columns = [country] - result = pd.concat([result, temp], axis=1) - except KeyError: - console.print(f"No data available for {country}.") - - result.index = pd.to_datetime(result.index, format="%Y") - result.sort_index(inplace=True) - - return result - - -@log_start_end(log=logger) -def get_trust( - countries: Optional[List[str]], - start_date="", - end_date="", -) -> pd.DataFrame: - """ - Trust in government refers to the share of people who report having confidence in - the national government. The data shown reflect the share of respondents answering - “yes” (the other response categories being “no”, and “don’t know”) to the - survey question: “In this country, do you have confidence in… national - government? Due to small sample sizes, country averages for horizontal inequalities - (by age, gender and education) are pooled between 2010-18 to improve the accuracy - of the estimates. The sample is ex ante designed to be nationally representative of - the population aged 15 and over. This indicator is measured as a percentage - of all survey respondents. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with the trust data - """ - if not start_date: - start_date = (datetime.now() - relativedelta(years=30)).date() - elif isinstance(start_date, datetime): - start_date = start_date.date() - if not end_date: - end_date = datetime.now().date() - elif isinstance(end_date, datetime): - end_date = end_date.date() - - df = pd.DataFrame() - try: - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.TRUSTGOV.TOT.PC.A/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - except Exception as e: - no_data_message(error=str(e)) - return df - - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - try: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_TRUST[country]]["Value"] - ) - - temp.index = temp.index.map(str) - temp.columns = [country] - result = pd.concat([result, temp], axis=1) - except KeyError: - console.print(f"No data available for {country}.") - - result.index = pd.to_datetime(result.index, format="%Y") - result.sort_index(inplace=True) - - return result diff --git a/openbb_terminal/economy/oecd_view.py b/openbb_terminal/economy/oecd_view.py deleted file mode 100644 index eef3aaf8e4a..00000000000 --- a/openbb_terminal/economy/oecd_view.py +++ /dev/null @@ -1,897 +0,0 @@ -""" OECD view """ - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import logging -import os -from datetime import datetime -from typing import Any, Dict, List, Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.economy import oecd_model -from openbb_terminal.helper_funcs import console, export_data, print_rich_table - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments, too-many-function-args - - -YAXIS_TITLES = { - "AGRWTH": "Annual Growth Rate (%)", - "IDX": "Index (Base = 2015)", - "IDX2015": "Index (Base = 2015)", - "USD": "Nominal Value in USD", - "PC_CHGPY": "Same Quarter of the Previous Year (% Change)", - "PC_CHGPP": "Previous Quarter (% Change)", - "PC_GDP": "Percentage of GDP (%)", - "THND_USD_CAP": "Thousands of USD per Capita", - "USD_CAP": "USD per Capita", -} - - -@log_start_end(log=logger) -def plot_gdp( - countries: Optional[str] = "united_states", - units: str = "USD", - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - Gross domestic product (GDP) is the standard measure of the value added created - through the production of goods and services in a country during a certain period. - As such, it also measures the income earned from that production, or the total amount - spent on final goods and services (less imports). While GDP is the single most important - indicator to capture economic activity, it falls short of providing a suitable measure of - people's material well-being for which alternative indicators may be more appropriate. - This indicator is based on nominal GDP (also called GDP at current prices or GDP in value) - and is available in different measures: US dollars and US dollars per capita (current PPPs). - All OECD countries compile their data according to the 2008 System of National Accounts (SNA). - This indicator is less suited for comparisons over time, as developments are not only caused - by real growth, but also by changes in prices and PPPs. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - units: str - Units to get data in. Either 'USD' or 'USD_CAP'. - Default is US dollars per capita. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - countries = [countries] if isinstance(countries, str) else countries # type: ignore[assignment] - - if units not in ["USD", "USD_CAP"]: - console.print( - "Invalid choice, choices are either USD or USD_CAP. Defaulting to USD." - ) - units = "USD" - - df = oecd_model.get_gdp(countries, units, start_date, end_date) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title=YAXIS_TITLES[units]) - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - title = "Nominal Gross Domestic Product (USD)" - title = title + " Per Capita" if units == "USD_CAP" else title - fig.set_title(title) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"nominal_gdp_{units}", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_real_gdp( - countries: Optional[List[str]], - units: str = "PC_CHGPY", - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - Gross domestic product (GDP) is the standard measure of the value added - created through the production of goods and services in a country during - a certain period. As such, it also measures the income earned from that - production, or the total amount spent on final goods and services (less imports). - While GDP is the single most important indicator to capture economic activity, it - falls short of providing a suitable measure of people's material well-being for - which alternative indicators may be more appropriate. This indicator is based on - real GDP (also called GDP at constant prices or GDP in volume), i.e. the developments - over time are adjusted for price changes. The numbers are also adjusted for seasonal - influences. The indicator is available in different measures: percentage change from - the previous quarter, percentage change from the same quarter of the previous year and - volume index (2015=100). All OECD countries compile their data according to the 2008 - System of National Accounts (SNA). [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - units: str - Units to get data in. Either 'PC_CHGPP', 'PC_CHGPY' or 'IDX. - Default is percentage change from the same quarter of the previous year. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - if units not in ["PC_CHGPP", "PC_CHGPY", "IDX"]: - return console.print( - "Use either PC_CHGPP (percentage change previous quarter), " - "PC_CHGPY (percentage change from the same quarter of the " - "previous year) or IDX (index with base at 2015) " - "for units" - ) - - df = oecd_model.get_real_gdp(countries, units, start_date, end_date) - - if df.empty: - return None - - kwargs: Dict[str, Any] = {"yaxis_title": YAXIS_TITLES[units]} - - if units == "PC_CHGPY": - kwargs["yaxis_title_font_size"] = 14 - - fig = OpenBBFigure(**kwargs) - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - title = "Real Gross Domestic Product (GDP)" - fig.set_title(title) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"real_gdp_{units}", - df if units == "IDX" else df / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_gdp_forecast( - countries: Optional[List[str]], - types: str = "real", - quarterly: bool = False, - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - Real gross domestic product (GDP) is GDP given in constant prices and - refers to the volume level of GDP. Constant price estimates of GDP are - obtained by expressing values of all goods and services produced in a - given year, expressed in terms of a base period. Forecast is based on an - assessment of the economic climate in individual countries and the world economy, - using a combination of model-based analyses and expert judgement. This indicator - is measured in growth rates compared to previous year. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - type: str - Type of GDP to get data for. Either 'real' or 'nominal'. - Default s real GDP (real). - quarterly: bool - Whether to get quarterly results. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - if types not in ["real", "nominal"]: - return console.print("Use either 'real' or 'nominal' for type") - - units = "Q" if quarterly else "A" - df = oecd_model.get_gdp_forecast(countries, types, units, start_date, end_date) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title="Growth rates Compared to Previous Year (%)") - - future_dates = df[ - df.index > str(datetime.now()) if units == "Q" else df.index >= datetime.now() - ] - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - if not future_dates.empty: - fig.add_vrect( - x0=future_dates.index[0], - x1=future_dates.index[-1], - annotation_text="Forecast", - fillcolor="yellow", - opacity=0.20, - line_width=0, - ) - - title = ( - f"Forecast of {'Quarterly' if units == 'Q' else 'Annual'} " - f"{'Real' if types == 'real' else 'Nominal'} Gross Domestic Product (GDP)" - ) - fig.set_title(title, font_size=20) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "forecast_gdp", - df / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_cpi( - countries: Optional[List[str]], - perspective: str = "TOT", - frequency: str = "Q", - units: str = "AGRWTH", - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - Inflation measured by consumer price index (CPI) is defined as the change in the prices - of a basket of goods and services that are typically purchased by specific groups of - households. Inflation is measured in terms of the annual growth rate and in index, - 2015 base year with a breakdown for food, energy and total excluding food and energy. - Inflation measures the erosion of living standards. A consumer price index is estimated - as a series of summary measures of the period-to-period proportional change in the - prices of a fixed set of consumer goods and services of constant quantity and - characteristics, acquired, used or paid for by the reference population. Each summary - measure is constructed as a weighted average of a large number of elementary aggregate indices. - Each of the elementary aggregate indices is estimated using a sample of prices for a defined - set of goods and services obtained in, or by residents of, a specific region from a given - set of outlets or other sources of consumption goods and services. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - perspective: str - Type of CPI you wish to obtain. This can be ENRG (energy), FOOD (food), - TOT (total) or TOT_FOODENRG (total excluding food and energy) - Default is Total CPI. - frequency: str - Frequency to get data in. Either 'M', 'Q' or 'A. - Default is Quarterly (Q). - units: str - Units to get data in. Either 'AGRWTH' (annual growth rate) or IDX2015 (base = 2015). - Default is Annual Growth Rate (AGRWTH). - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - df = oecd_model.get_cpi( - countries, perspective, frequency, units, start_date, end_date - ) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title=YAXIS_TITLES[units]) - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - perspective_naming = { - "FOOD": "Food", - "ENRG": "Energy", - "TOT": "Total", - "TOT_FOODENRG": "Total Excluding Food and Energy", - } - - title = f"Consumer Price Index (CPI) ({perspective_naming[perspective]})" - fig.set_title(title) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cpi", - df / 100 if units == "AGRWTH" else df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_balance( - countries: Optional[List[str]], - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - General government balance is defined as the balance of income and expenditure of government, - including capital income and capital expenditures. "Net lending" means that governmen - has a surplus, and is providing financial resources to other sectors, while - "net borrowing" means that government has a deficit, and requires financial - esources from other sectors. This indicator is measured as a percentage of GDP. - All OECD countries compile their data according to the - 2008 System of National Accounts (SNA 2008). [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - df = oecd_model.get_balance(countries, start_date, end_date) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title="Percentage of GDP (%)") - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - title = "Government Deficit" - fig.set_title(title) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "balance_percentage_gdp", - df / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_revenue( - countries: Optional[List[str]], - units: str = "PC_GDP", - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - Governments collect revenues mainly for two purposes: to finance the goods - and services they provide to citizens and businesses, and to fulfil their - redistributive role. Comparing levels of government revenues across - countries provides an indication of the importance of the government - sector in the economy in terms of available financial resources. - The total amount of revenues collected by governments is determined - by past and current political decisions. This indicator is measured - in terms of thousand USD per capita, and as a percentage of GDP. All - OECD countries compile their data according to the 2008 System of - National Accounts (SNA 2008). [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - units: str - Units to get data in. Either 'PC_GDP' or 'THND_USD_CAP'. - Default is Percentage of GDP. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - if units not in ["THND_USD_CAP", "PC_GDP"]: - return console.print( - "Use either THND_USD_CAP (thousands of USD per capity) " - "or PC_GDP (percentage of GDP) for units" - ) - - df = oecd_model.get_revenue(countries, units, start_date, end_date) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title=YAXIS_TITLES[units]) - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - title = "Government Revenue" - fig.set_title(title) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"revenue_{units}", - df / 100 if units == "PC_GDP" else df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_spending( - countries: Optional[List[str]], - perspective: str = "TOT", - units: str = "PC_GDP", - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - General government spending provides an indication of the size - of government across countries. The large variation in this indicator - highlights the variety of countries' approaches to delivering public - goods and services and providing social protection, not necessarily - differences in resources spent. This indicator is measured in terms of - thousand USD per capita, and as percentage of GDP. All OECD countries - compile their data according to the 2008 System of - National Accounts (SNA). [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - units: str - Units to get data in. Either 'PC_GDP' or 'THND_USD_CAP'. - Default is Percentage of GDP. - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - df = oecd_model.get_spending(countries, perspective, units, start_date, end_date) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title=YAXIS_TITLES[units]) - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - perspective_naming = { - "TOT": "Total", - "RECULTREL": "Recreation, culture and religion)", - "HOUCOMM": "Housing and community amenities", - "PUBORD": "Public order and safety)", - "EDU": "Education", - "ENVPROT": "Environmental protection", - "GRALPUBSER": "General public services)", - "SOCPROT": "Social protection", - "ECOAFF": "Economic affairs", - "DEF": "Defence", - "HEALTH": "Health", - } - - title = f"Government Spending ({perspective_naming[perspective]})" - fig.set_title(title) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"spending_{units}_{perspective}", - df / 100 if units == "PC_GDP" else df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_debt( - countries: Optional[List[str]], - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - General government debt-to-GDP ratio measures the gross debt of the general - government as a percentage of GDP. It is a key indicator for the sustainability - of government finance. Debt is calculated as the sum of the following liability - categories (as applicable): currency and deposits; debt securities, loans; insurance, - pensions and standardised guarantee schemes, and other accounts payable. Changes in - government debt over time primarily reflect the impact of past government deficits. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - df = oecd_model.get_debt(countries, start_date, end_date) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title="Percentage of GDP (%)") - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - title = "Government Debt-to-GDP Ratio" - fig.set_title(title) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "debt_to_gdp", - df / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_trust( - countries: Optional[List[str]], - start_date: str = "", - end_date: str = "", - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """ - Trust in government refers to the share of people who report having confidence in - the national government. The data shown reflect the share of respondents answering - “yes” (the other response categories being “no”, and “don’t know”) to the - survey question: “In this country, do you have confidence in national - government? Due to small sample sizes, country averages for horizontal inequalities - (by age, gender and education) are pooled between 2010-18 to improve the accuracy - of the estimates. The sample is ex ante designed to be nationally representative of - the population aged 15 and over. This indicator is measured as a percentage - of all survey respondents. [Source: OECD] - - Parameters - ---------- - countries: list - List of countries to get data for - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - Whether to display raw data in a table - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[OpenBBFigure, None] - OpenBBFigure object if external_axes is True, else None (opens plot in a window) - """ - df = oecd_model.get_trust(countries, start_date, end_date) - - if df.empty: - return None - - fig = OpenBBFigure(yaxis_title="Percentage of all Survey Respondents (%)") - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - title = "Trust in the Government" - fig.set_title(title) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "trust", - df / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/economy/plot_view.py b/openbb_terminal/economy/plot_view.py deleted file mode 100644 index 490bd824c1a..00000000000 --- a/openbb_terminal/economy/plot_view.py +++ /dev/null @@ -1,139 +0,0 @@ -""" Plot Controller """ - -import os -from typing import Any, Dict, Optional - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.helper_funcs import export_data, print_rich_table - - -def show_plot( - dataset_yaxis_1, - dataset_yaxis_2, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, -): - """ - The ability to plot any data coming from EconDB, FRED or Yahoo Finance. - - Parameters - ---------- - dataset_yaxis_1: pd.DataFrame - Data you wish to plot on the first y-axis. - dataset_yaxis_2 : pd.DataFrame - Data you wish to plot on the second y-axis. - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Plots the data. - """ - fig = OpenBBFigure.create_subplots( - 1, 1, shared_xaxes=True, shared_yaxes=False, specs=[[{"secondary_y": True}]] - ) - - dataset_yaxis_1 = dataset_yaxis_1.dropna() - dataset_yaxis_1.index = pd.to_datetime(dataset_yaxis_1.index) - - for column in dataset_yaxis_1: - fig.add_scatter( - x=dataset_yaxis_1.index, - y=dataset_yaxis_1[column], - name=column.replace("_", " "), - mode="lines", - yaxis="y1", - secondary_y=False, - ) - - if not dataset_yaxis_2.empty: - dataset_yaxis_2 = dataset_yaxis_2.dropna() - dataset_yaxis_2.index = pd.to_datetime(dataset_yaxis_2.index) - - for column in dataset_yaxis_2: - fig.add_scatter( - x=dataset_yaxis_2.index, - y=dataset_yaxis_2[column], - name=column, - mode="lines", - yaxis="y2", - secondary_y=True, - ) - - fig.update_yaxes(side="left", secondary_y=False) - - if export: - df = pd.concat([dataset_yaxis_1, dataset_yaxis_2], axis=1) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "plot_macro_data", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -def show_options( - datasets: Dict[Any, pd.DataFrame], - raw: str = "", - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """ - The ability to plot any data coming from EconDB, FRED or Yahoo Finance. - - Parameters - ---------- - datasets: Dict[Any, pd.DataFrame] - A dictionary with the format {command: data}. - raw : bool - Whether you wish to show the data available. - limit: int - The amount of rows you wish to show. - export: bool - Whether you want to export the data. - - Returns - ------- - Plots the data. - """ - if raw or export: - df = pd.DataFrame() - for _, data in datasets.items(): - df = pd.concat([df, data], axis=1) - - if raw: - df = df.sort_index(ascending=False) - print_rich_table( - df, - show_index=True, - headers=list(df.columns), - title="Macro data", - export=bool(export), - limit=limit, - ) - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dataset", - df, - sheet_name, - ) - else: - options = { - command: ", ".join(values.keys()) for command, values in datasets.items() - } - print_rich_table( - pd.DataFrame.from_dict(options, orient="index", columns=["Options"]), - show_index=True, - index_name="Command", - title="Options available to plot", - ) diff --git a/openbb_terminal/economy/quantitative_analysis/__init__.py b/openbb_terminal/economy/quantitative_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/economy/quantitative_analysis/qa_controller.py b/openbb_terminal/economy/quantitative_analysis/qa_controller.py deleted file mode 100644 index 6ffcee16d29..00000000000 --- a/openbb_terminal/economy/quantitative_analysis/qa_controller.py +++ /dev/null @@ -1,811 +0,0 @@ -"""Quantitative Analysis Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -from typing import Dict, List, Optional - -import numpy as np -import pandas as pd - -from openbb_terminal.common.quantitative_analysis import qa_view, rolling_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_list_dates, - check_positive, - check_proportion_range, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class QaController(StockBaseController): - """Quantitative Analysis Controller class""" - - CHOICES_COMMANDS = [ - "pick", - "raw", - "summary", - "line", - "hist", - "cdf", - "bw", - "rolling", - "decompose", - "cusum", - "acf", - "spread", - "quantile", - "skew", - "kurtosis", - "normality", - "qqplot", - "unitroot", - "goodness", - "unitroot", - ] - FULLER_REG = ["c", "ct", "ctt", "nc"] - KPS_REG = ["c", "ct"] - - PATH = "/economy/qa/" - - def __init__( - self, - all_economy_data: Dict, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.datasets = all_economy_data - self.options = [] - for _, sub_df in all_economy_data.items(): - self.options.extend(list(sub_df.columns)) - self.sources = list(self.datasets.keys()) - self.current_source = self.sources[0] - # The common functions take in a df and a 'target' column. In order to replicate this, - # the following code uses the dictionary of dataframes and finds which source the selected column comes from. - self.current_source_dataframe = self.datasets[self.current_source].copy() - self.current_source_dataframe.index.name = "date" - self.data = self.current_source_dataframe.iloc[:, 0].copy() - if np.any(pd.isna(self.data)): - self.current_source_dataframe = self.current_source_dataframe.dropna(axis=0) - self.data = self.data.dropna(axis=0) - self.current_id = self.current_source_dataframe.columns[0] - self.start_date = self.data.index[0] - self.resolution = "" # For the views - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - choices["pick"] = {c: {} for c in self.options} - choices["unitroot"] = { - "--fuller_reg": {c: {} for c in self.FULLER_REG}, - "-r": "--fuller_reg", - "--kps_reg": {c: {} for c in self.KPS_REG}, - "-k": "--kps_reg", - } - choices["line"] = { - "--log": {}, - "--ml": None, - "--ms": None, - } - choices["hist"] = { - "--bins": {str(c): {} for c in range(10, 100)}, - "-b": "--bins", - } - choices["bw"] = { - "--yearly": {}, - "-y": {}, - } - choices["acf"] = { - "--lags": {str(c): {} for c in range(5, 100)}, - "-l": "--lags", - } - choices["rolling"] = { - "--window": {str(c): {} for c in range(5, 100)}, - "-w": "--window", - } - choices["spread"] = { - "--window": {str(c): {} for c in range(5, 100)}, - "-w": "--window", - } - choices["quantile"] = { - "--window": {str(c): {} for c in range(5, 100)}, - "-w": "--window", - "--quantile": {str(c): {} for c in np.arange(0.0, 1.0, 0.01)}, - "-q": "--quantile", - } - choices["skew"] = { - "--window": {str(c): {} for c in range(5, 100)}, - "-w": "--window", - } - choices["kurtosis"] = { - "--window": {str(c): {} for c in range(5, 100)}, - "-w": "--window", - } - choices["raw"] = { - "--limit": None, - "-l": "--limit", - "--sortby": {}, - "-s": "--sortby", - "--reverse": {}, - "-r": "--reverse", - } - choices["decompose"] = { - "--multiplicative": None, - "-m": "--multiplicative", - } - choices["cusum"] = { - "--threshold": None, - "-t": "--threshold", - "--drift": None, - "-d": "--drift", - } - - choices["support"] = self.SUPPORT_CHOICES - choices["about"] = self.ABOUT_CHOICES - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("economy/qa/") - mt.add_cmd("pick") - mt.add_raw("\n") - mt.add_param("_series", self.current_id) - mt.add_raw("\n") - mt.add_info("_statistics_") - mt.add_cmd("summary") - mt.add_cmd("normality") - mt.add_cmd("unitroot") - mt.add_info("_plots_") - mt.add_cmd("line") - mt.add_cmd("hist") - mt.add_cmd("cdf") - mt.add_cmd("bw") - mt.add_cmd("acf") - mt.add_cmd("qqplot") - mt.add_info("_rolling_metrics_") - mt.add_cmd("rolling") - mt.add_cmd("spread") - mt.add_cmd("quantile") - mt.add_cmd("skew") - mt.add_cmd("kurtosis") - mt.add_info("_other_") - mt.add_cmd("raw") - mt.add_cmd("decompose") - mt.add_cmd("cusum") - console.print(text=mt.menu_text, menu="Economy - Quantitative Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - return ["economy"] - - @log_start_end(log=logger) - def call_pick(self, other_args: List[str]): - """Process add command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="load", - description="Load a FRED series to current selection", - ) - parser.add_argument( - "-c", - "--column", - dest="column", - type=str, - help="Which loaded source to get data from", - choices=self.options, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - for source, sub_df in self.datasets.items(): - if ns_parser.column in sub_df.columns: - self.current_source = source - self.current_source_dataframe = sub_df.copy() - self.current_source_dataframe.index.name = "date" - # This menu throws errors if there are nans, so lets manipulate them here - self.data = sub_df[ns_parser.column].copy() - if np.any(pd.isna(self.data)): - self.current_source_dataframe = ( - self.current_source_dataframe.dropna(axis=0) - ) - self.data = self.data.dropna(axis=0) - self.current_id = ns_parser.column - self.start_date = self.data.index[0] - console.print(f"{ns_parser.column} loaded from {source}.\n") - return - console.print(f"[red]{ns_parser.column} not found in data.[/red]\n") - - @log_start_end(log=logger) - def call_raw(self, other_args: List[str]): - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="raw", - description=""" - Print raw data to console - """, - ) - parser.add_argument( - "-l", - "--limit", - help="Number to show", - type=check_positive, - default=20, - dest="limit", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sortby", - help="The column to sort by", - type=str.lower, - dest="sortby", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - data = self.data.to_frame() if isinstance(self.data, pd.Series) else self.data - if ns_parser: - qa_view.display_raw( - data=data, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_summary(self, other_args: List[str]): - """Process summary command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="summary", - description=""" - Summary statistics - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_summary( - data=self.current_source_dataframe, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_line(self, other_args: List[str]): - """Process line command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="line", - description="Show line plot of selected data or highlight specific datetimes.", - ) - parser.add_argument( - "--log", - help="Plot with y on log scale", - dest="log", - action="store_true", - default=False, - ) - parser.add_argument( - "--ml", - help="Draw vertical line markers to highlight certain events", - dest="ml", - type=check_list_dates, - default="", - ) - parser.add_argument( - "--ms", - help="Draw scatter markers to highlight certain events", - dest="ms", - type=check_list_dates, - default="", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - qa_view.display_line( - self.data, - title=f"{self.current_id.upper()}", - log_y=ns_parser.log, - markers_lines=ns_parser.ml, - markers_scatter=ns_parser.ms, - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_hist(self, other_args: List[str]): - """Process hist command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="hist", - description=""" - Histogram with density and rug - """, - ) - parser.add_argument( - "-b", "--bins", type=check_positive, default=15, dest="n_bins" - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_hist( - symbol="", - data=self.current_source_dataframe, - target=self.current_id, - bins=ns_parser.n_bins, - ) - - @log_start_end(log=logger) - def call_cdf(self, other_args: List[str]): - """Process cdf command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="cdf", - description=""" - Cumulative distribution function - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_cdf( - symbol="", - data=self.current_source_dataframe, - target=self.current_id, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bw(self, other_args: List[str]): - """Process bwy command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="bw", - description=""" - Box and Whisker plot - """, - ) - parser.add_argument( - "-y", - "--yearly", - action="store_true", - default=False, - dest="year", - help="Flag to show yearly bw plot", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_bw( - symbol="", - data=self.current_source_dataframe, - target=self.current_id, - yearly=ns_parser.year, - ) - - @log_start_end(log=logger) - def call_decompose(self, other_args: List[str]): - """Process decompose command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="decompose", - description=""" - Decompose time series as: - - Additive Time Series = Level + CyclicTrend + Residual + Seasonality - - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality - """, - ) - parser.add_argument( - "-m", - "--multiplicative", - action="store_true", - default=False, - dest="multiplicative", - help="decompose using multiplicative model instead of additive", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_seasonal( - symbol="", - data=self.current_source_dataframe, - target=self.current_id, - multiplicative=ns_parser.multiplicative, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cusum(self, other_args: List[str]): - """Process cusum command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cusum", - description=""" - Cumulative sum algorithm (CUSUM) to detect abrupt changes in data - """, - ) - parser.add_argument( - "-t", - "--threshold", - dest="threshold", - type=float, - default=(max(self.data.values) - min(self.data.values)) / 40, - help="threshold", - ) - parser.add_argument( - "-d", - "--drift", - dest="drift", - type=float, - default=(max(self.data.values) - min(self.data.values)) / 80, - help="drift", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_cusum( - data=self.current_source_dataframe, - target=self.current_id, - threshold=ns_parser.threshold, - drift=ns_parser.drift, - ) - - @log_start_end(log=logger) - def call_acf(self, other_args: List[str]): - """Process acf command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="acf", - description=""" - Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff stock data - """, - ) - parser.add_argument( - "-l", - "--lags", - dest="lags", - type=check_positive, - default=15, - help="maximum lags to display in plots", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_acf( - symbol="", - data=self.current_source_dataframe, - target=self.current_id, - lags=ns_parser.lags, - ) - - @log_start_end(log=logger) - def call_rolling(self, other_args: List[str]): - """Process rolling command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rolling", - description=""" - Rolling mean and std deviation - """, - ) - parser.add_argument( - "-w", - "--window", - action="store", - dest="n_window", - type=check_positive, - default=14, - help="Window length", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - rolling_view.display_mean_std( - symbol="", - data=self.current_source_dataframe, - target=self.current_id, - window=ns_parser.n_window, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_spread(self, other_args: List[str]): - """Process spread command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="spread", - description="""Shows rolling spread measurement - """, - ) - parser.add_argument( - "-w", - "--window", - action="store", - dest="n_window", - type=check_positive, - default=14, - help="Window length", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - rolling_view.display_spread( - symbol="", - data=self.current_source_dataframe, - target=self.current_id, - window=ns_parser.n_window, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_quantile(self, other_args: List[str]): - """Process quantile command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="quantile", - description=""" - The quantiles are values which divide the distribution such that - there is a given proportion of observations below the quantile. - For example, the median is a quantile. The median is the central - value of the distribution, such that half the points are less than - or equal to it and half are greater than or equal to it. - - By default, q is set at 0.5, which effectively is median. Change q to - get the desired quantile (0 pd.DataFrame: - """Get futures data. - - Parameters - ---------- - source : str - Data source for futures data. From the following: WSJ, Finviz - future_type : str - (Finviz only) Future type to get. Can be: Indices, Energy, Metals, Meats, Grains, Softs, Bonds, Currencies. - - Returns - ------- - pd.DataFrame - Dataframe of futures data. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> wsj_futures = openbb.economy.futures() - - To sort by the largest percent change: - >>> futures_sorted = openbb.economy.futures().sort_values(by="%Chg", ascending=False) - - FinViz provides different options for future types. We can get Meats with the following command: - >>> meat_futures = openbb.economy.futures(source="Finviz", future_type="Meats") - - """ - - if source.lower() == "wsj": - return wsj_model.top_commodities() - if source.lower() == "finviz": - return finviz_model.get_futures(future_type.title()) - return pd.DataFrame() diff --git a/openbb_terminal/economy/wsj_model.py b/openbb_terminal/economy/wsj_model.py deleted file mode 100644 index 32001b2be6c..00000000000 --- a/openbb_terminal/economy/wsj_model.py +++ /dev/null @@ -1,282 +0,0 @@ -"""WSJ model""" - -__docformat__ = "numpy" -import logging - -import pandas as pd -import requests - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def us_indices() -> pd.DataFrame: - """Get the top US indices - - Returns - ------- - indices: pd.DataFrame - Dataframe containing name, price, net change and percent change - """ - url = ( - "https://www.wsj.com/market-data/stocks?id=%7B%22application%22%3A%22WSJ%22%2C%22instruments%22%3A%5B%7B" - "%22symbol%22%3A%22INDEX%2FUS%2F%2FDJIA%22%2C%22name%22%3A%22DJIA%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FUS%2F" - "%2FCOMP%22%2C%22name%22%3A%22Nasdaq%20Composite%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FUS%2F%2FSPX%22%2C%22name" - "%22%3A%22S%26P%20500%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FUS%2F%2FDWCF%22%2C%22name%22%3A%22DJ%20Total%20Stock" - "%20Market%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FUS%2F%2FRUT%22%2C%22name%22%3A%22Russell%202000%22%7D%2C%7B" - "%22symbol%22%3A%22INDEX%2FUS%2F%2FNYA%22%2C%22name%22%3A%22NYSE%20Composite%22%7D%2C%7B%22symbol%22%3A%22INDEX" - "%2FUS%2F%2FB400%22%2C%22name%22%3A%22Barron%27s%20400%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FUS%2F%2FVIX%22%2C%22" - "name%22%3A%22CBOE%20Volatility%22%7D%2C%7B%22symbol%22%3A%22FUTURE%2FUS%2F%2FDJIA%20FUTURES%22%2C%22name%22%3A%" - "22DJIA%20Futures%22%7D%2C%7B%22symbol%22%3A%22FUTURE%2FUS%2F%2FS%26P%20500%20FUTURES%22%2C%22name%22%3A%22S%26P" - "%20500%20Futures%22%7D%5D%7D&type=mdc_quotes" - ) - try: - response = request( - url, - headers={"User-Agent": get_user_agent()}, - ) - except requests.exceptions.RequestException: - console.print("[red]Could not retrieve data from wsj.[/red]\n") - return pd.DataFrame() - data = response.json() - - name, last_price, net_change, percent_change = [], [], [], [] - - for entry in data["data"]["instruments"]: - name.append(entry["formattedName"]) - last_price.append(entry["lastPrice"]) - net_change.append(entry["priceChange"]) - percent_change.append(entry["percentChange"]) - - indices = pd.DataFrame( - {" ": name, "Price": last_price, "Chg": net_change, "%Chg": percent_change} - ) - - return indices - - -@log_start_end(log=logger) -def market_overview() -> pd.DataFrame: - """Scrape data for market overview - - Returns - ------- - overview: pd.DataFrame - Dataframe containing name, price, net change and percent change - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> ov_df = openbb.economy.overview() - """ - url = ( - "https://www.wsj.com/market-data?id=%7B%22application%22%3A%22WSJ%22%2C%22instruments%22%3A%5B%7B%22symbol%22" - "%3A%22INDEX%2FUS%2F%2FDJIA%22%2C%22name%22%3A%22DJIA%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FUS%2F%2FSPX%22%2C%22" - "name%22%3A%22S%26P%20500%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FUS%2F%2FCOMP%22%2C%22name%22%3A%22Nasdaq%20" - "Composite%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FJP%2F%2FNIK%22%2C%22name%22%3A%22Japan%3A%20Nikkei%20225%22%7D%" - "2C%7B%22symbol%22%3A%22INDEX%2FUK%2F%2FUKX%22%2C%22name%22%3A%22UK%3A%20FTSE%20100%22%7D%2C%7B%22symbol%22%3A%" - "22FUTURE%2FUS%2F%2FCRUDE%20OIL%20-%20ELECTRONIC%22%2C%22name%22%3A%22Crude%20Oil%20Futures%22%7D%2C%7B%22symbol" - "%22%3A%22FUTURE%2FUS%2F%2FGOLD%22%2C%22name%22%3A%22Gold%20Futures%22%7D%2C%7B%22symbol%22%3A%22CURRENCY%2FUS%2" - "F%2FUSDJPY%22%2C%22name%22%3A%22Yen%22%7D%2C%7B%22symbol%22%3A%22CURRENCY%2FUS%2F%2FEURUSD%22%2C%22name%22%3A%" - "22Euro%22%7D%5D%7D&type=mdc_quotes" - ) - try: - response = request(url, headers={"User-Agent": get_user_agent()}) - except requests.exceptions.RequestException: - console.print("[red]Could not retrieve data from wsj.[/red]\n") - return pd.DataFrame() - data = response.json() - name, last_price, net_change, percent_change = [], [], [], [] - - for entry in data["data"]["instruments"]: - name.append(entry["formattedName"]) - last_price.append(entry["lastPrice"]) - net_change.append(entry["priceChange"]) - percent_change.append(entry["percentChange"]) - - overview = pd.DataFrame( - {" ": name, "Price": last_price, "Chg": net_change, "%Chg": percent_change} - ) - - return overview - - -@log_start_end(log=logger) -def top_commodities() -> pd.DataFrame: - """Scrape data for top commodities - - Returns - ------- - commodities: pd.DataFrame - Dataframe containing name, price, net change and percent change - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> fut_df = openbb.economy.futures() - """ - url = ( - "https://www.wsj.com/market-data/commodities?id=%7B%22application%22%3A%22WSJ%22%2C%22instruments%22%3A%5B%7" - "B%22symbol%22%3A%22FUTURE%2FUS%2F%2FCRUDE%20OIL%20-%20ELECTRONIC%22%2C%22name%22%3A%22Crude%20Oil%20Futures" - "%22%7D%2C%7B%22symbol%22%3A%22FUTURE%2FUK%2F%2FBRENT%20CRUDE%22%2C%22name%22%3A%22Brent%20Crude%20Futures%22" - "%7D%2C%7B%22symbol%22%3A%22FUTURE%2FUS%2F%2FGOLD%22%2C%22name%22%3A%22Gold%20Futures%22%7D%2C%7B%22symbol%22%" - "3A%22FUTURE%2FUS%2F%2FSILVER%22%2C%22name%22%3A%22Silver%20Futures%22%7D%2C%7B%22symbol%22%3A%22FUTURE%2FUS%2F" - "%2FNATURAL%20GAS%22%2C%22name%22%3A%22Natural%20Gas%20Futures%22%7D%2C%7B%22symbol%22%3A%22FUTURE%2FUS%2F%2" - "FUNLEADED%20GASOLINE%22%2C%22name%22%3A%22Unleaded%20Gasoline%20Futures%22%7D%2C%7B%22symbol%22%3A%22FUTURE%" - "2FUS%2F%2FCOPPER%22%2C%22name%22%3A%22Copper%20Futures%22%7D%2C%7B%22symbol%22%3A%22FUTURE%2FUS%2F%2FCORN%22%2" - "C%22name%22%3A%22Corn%20Futures%22%7D%2C%7B%22symbol%22%3A%22FUTURE%2FUS%2F%2FWHEAT%22%2C%22name%22%3A%22Wheat" - "%20Futures%22%7D%2C%7B%22symbol%22%3A%22INDEX%2FXX%2F%2FBCOM%22%7D%5D%7D&type=mdc_quotes" - ) - try: - response = request(url, headers={"User-Agent": get_user_agent()}) - except requests.exceptions.RequestException: - console.print("[red]Could not retrieve data from wsj.[/red]\n") - return pd.DataFrame() - data = response.json() - name, last_price, net_change, percent_change = [], [], [], [] - - for entry in data["data"]["instruments"]: - name.append(entry["formattedName"]) - last_price.append(entry["lastPrice"]) - net_change.append(entry["priceChange"]) - percent_change.append(entry["percentChange"]) - - commodities = pd.DataFrame( - {" ": name, "Price": last_price, "Chg": net_change, "%Chg": percent_change} - ) - - commodities[commodities.columns[0]] = commodities[ - commodities.columns[0] - ].str.replace("Futures", "") - - commodities["%Chg"] = pd.to_numeric(commodities["%Chg"], downcast="float") - - return commodities - - -@log_start_end(log=logger) -def us_bonds() -> pd.DataFrame: - """Scrape data for us bonds - - Returns - ------- - bonds: pd.DataFrame - Dataframe containing name, coupon rate, yield and change in yield - """ - url = ( - "https://www.wsj.com/market-data?id=%7B%22application%22%3A%22WSJ%22%2C%22instruments%22%3A%5B" - "%7B%22symbol%22%3A%22BOND%2FBX%2F%2FTMUBMUSD30Y%22%2C%22name%22%3A%2230-Year%20Bond%22%7D%2C%7" - "B%22symbol%22%3A%22BOND%2FBX%2F%2FTMUBMUSD10Y%22%2C%22name%22%3A%2210-Year%20Note%22%7D%2C%7B%2" - "2symbol%22%3A%22BOND%2FBX%2F%2FTMUBMUSD07Y%22%2C%22name%22%3A%227-Year%20Note%22%7D%2C%7B%22sym" - "bol%22%3A%22BOND%2FBX%2F%2FTMUBMUSD05Y%22%2C%22name%22%3A%225-Year%20Note%22%7D%2C%7B%22symbol" - "%22%3A%22BOND%2FBX%2F%2FTMUBMUSD03Y%22%2C%22name%22%3A%223-Year%20Note%22%7D%2C%7B%22symbol%22%" - "3A%22BOND%2FBX%2F%2FTMUBMUSD02Y%22%2C%22name%22%3A%222-Year%20Note%22%7D%2C%7B%22symbol%22%3A%" - "22BOND%2FBX%2F%2FTMUBMUSD01Y%22%2C%22name%22%3A%221-Year%20Bill%22%7D%2C%7B%22symbol%22%3A%22" - "BOND%2FBX%2F%2FTMUBMUSD06M%22%2C%22name%22%3A%226-Month%20Bill%22%7D%2C%7B%22symbol%22%3A%22BON" - "D%2FBX%2F%2FTMUBMUSD03M%22%2C%22name%22%3A%223-Month%20Bill%22%7D%2C%7B%22symbol%22%3A%22BOND%" - "2FBX%2F%2FTMUBMUSD01M%22%2C%22name%22%3A%221-Month%20Bill%22%7D%5D%7D&type=mdc_quotes" - ) - - try: - response = request(url, headers={"User-Agent": get_user_agent()}) - except requests.exceptions.RequestException: - console.print("[red]Could not retrieve data from wsj.[/red]\n") - return pd.DataFrame() - data = response.json() - name, yield_pct, rate, yld_chng = [], [], [], [] - - for entry in data["data"]["instruments"]: - name.append(entry["formattedName"]) - yield_pct.append(entry["bond"]["yield"]) - rate.append(entry["bond"]["couponRate"]) - yld_chng.append(entry["bond"]["yieldChange"]) - - bonds = pd.DataFrame( - {" ": name, "Rate (%)": rate, "Yld (%)": yield_pct, "Yld Chg (%)": yld_chng} - ) - return bonds - - -@log_start_end(log=logger) -def global_bonds() -> pd.DataFrame: - """Scrape data for global bonds - - Returns - ------- - bonds: pd.DataFrame - Dataframe containing name, coupon rate, yield and change in yield - """ - url = ( - "https://www.wsj.com/market-data?id=%7B%22application%22%3A%22WSJ%22%2C%22bonds%22%3A%5" - "B%7B%22symbol%22%3A%22TMUBMUSD10Y%22%2C%22name%22%3A%22U.S.%2010%20Year%22%7D%2C%7B%22symbol" - "%22%3A%22TMBMKDE-10Y%22%2C%22name%22%3A%22Germany%2010%20Year%22%7D%2C%7B%22symbol%22%3A%22TMB" - "MKGB-10Y%22%2C%22name%22%3A%22U.K.%2010%20Year%22%7D%2C%7B%22symbol%22%3A%22TMBMKJP-10Y%22%2C%" - "22name%22%3A%22Japan%2010%20Year%22%7D%2C%7B%22symbol%22%3A%22TMBMKAU-10Y%22%2C%22name%22%3A%2" - "2Australia%2010%20Year%22%7D%2C%7B%22symbol%22%3A%22AMBMKRM-10Y%22%2C%22name%22%3A%22China%2010" - "%20Year%22%7D%5D%7D&type=mdc_governmentbonds" - ) - try: - response = request(url, headers={"User-Agent": get_user_agent()}) - except requests.exceptions.RequestException: - console.print("[red]Could not retrieve data from wsj.[/red]\n") - return pd.DataFrame() - data = response.json() - name, yield_pct, rate, yld_chng = [], [], [], [] - - for entry in data["data"]["instruments"]: - name.append(entry["djLegalName"]) - yield_pct.append(entry["yieldPercent"]) - rate.append(entry["couponPercent"]) - yld_chng.append(entry["yieldChange"]) - - bonds = pd.DataFrame( - {" ": name, "Rate (%)": rate, "Yld (%)": yield_pct, "Yld Chg (%)": yld_chng} - ) - return bonds - - -@log_start_end(log=logger) -def global_currencies() -> pd.DataFrame: - """Scrape data for global currencies - - Returns - ------- - currencies: pd.DataFrame - Dataframe containing name, price, net change and percent change - """ - url = ( - "https://www.wsj.com/market-data?id=%7B%22application%22%3A%22WSJ%22%2C%22instruments%22%3A%5" - "B%7B%22symbol%22%3A%22CURRENCY%2FUS%2F%2FEURUSD%22%2C%22name%22%3A%22Euro%20(EUR%2FUSD)%22%7D%" - "2C%7B%22symbol%22%3A%22CURRENCY%2FUS%2F%2FUSDJPY%22%2C%22name%22%3A%22Japanese%20Yen%20(USD%2F" - "JPY)%22%7D%2C%7B%22symbol%22%3A%22CURRENCY%2FUS%2F%2FGBPUSD%22%2C%22name%22%3A%22U.K.%20Poun" - "d%20(GBP%2FUSD)%22%7D%2C%7B%22symbol%22%3A%22CURRENCY%2FUS%2F%2FUSDCHF%22%2C%22name%22%3A%22Sw" - "iss%20Franc%20(USD%2FCHF)%22%7D%2C%7B%22symbol%22%3A%22CURRENCY%2FUS%2F%2FUSDCNY%22%2C%22name%2" - "2%3A%22Chinese%20Yuan%20(USD%2FCNY)%22%7D%2C%7B%22symbol%22%3A%22CURRENCY%2FUS%2F%2FUSDCAD%22%2C" - "%22name%22%3A%22Canadian%20%24%20(USD%2FCAD)%22%7D%2C%7B%22symbol%22%3A%22CURRENCY%2FUS%2F%2F" - "USDMXN%22%2C%22name%22%3A%22Mexican%20Peso%20(USD%2FMXN)%22%7D%2C%7B%22symbol%22%3A%22CRYPTO" - "CURRENCY%2FUS%2F%2FBTCUSD%22%2C%22name%22%3A%22Bitcoin%20(BTC%2FUSD)%22%7D%2C%7B%22symbol%22%3A" - "%22INDEX%2FXX%2F%2FBUXX%22%2C%22name%22%3A%22WSJ%20Dollar%20Index%22%7D%2C%7B%22symbol%22%3A%2" - "2INDEX%2FUS%2F%2FDXY%22%2C%22name%22%3A%22U.S.%20Dollar%20Index%22%7D%5D%7D&type=mdc_quotes" - ) - try: - response = request(url, headers={"User-Agent": get_user_agent()}) - except requests.exceptions.RequestException: - console.print("[red]Could not retrieve data from wsj.[/red]\n") - return pd.DataFrame() - data = response.json() - - name, last_price, price_change, pct_change = [], [], [], [] - for entry in data["data"]["instruments"]: - name.append(entry["formattedName"]) - last_price.append(entry["lastPrice"]) - price_change.append(entry["priceChange"]) - pct_change.append(entry["percentChange"]) - - currencies = pd.DataFrame( - {" ": name, "Last": last_price, "Chng": price_change, "%Chng": pct_change} - ) - return currencies diff --git a/openbb_terminal/economy/wsj_view.py b/openbb_terminal/economy/wsj_view.py deleted file mode 100644 index e76cf7c1ea8..00000000000 --- a/openbb_terminal/economy/wsj_view.py +++ /dev/null @@ -1,200 +0,0 @@ -"""WSJ view """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.economy import wsj_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_overview(export: str = "", sheet_name: Optional[str] = None): - """Market overview with daily change. [Source: Wall St. Journal] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - df_data = wsj_model.market_overview() - if df_data.empty: - console.print("No overview data available\n") - return - - print_rich_table( - df_data, - show_index=False, - headers=list(df_data.columns), - title="Market Overview", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "overview", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_indices(export: str = "", sheet_name: Optional[str] = None): - """US indices. [Source: Wall St. Journal] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - df_data = wsj_model.us_indices() - if df_data.empty: - console.print("No indices data available\n") - return - - print_rich_table( - df_data, - show_index=False, - headers=list(df_data.columns), - title="US Indices", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "indices", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_futures(export: str = "", sheet_name: Optional[str] = None): - """Futures/Commodities. [Source: Wall St. Journal] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - df_data = wsj_model.top_commodities() - if df_data.empty: - console.print("No futures/commodities data available\n") - return - - print_rich_table( - df_data, - show_index=False, - headers=list(df_data.columns), - title="Futures/Commodities [Source: Wall St. Journal]", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "futures", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_usbonds(export: str = "", sheet_name: Optional[str] = None): - """US bonds. [Source: Wall St. Journal] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - df_data = wsj_model.us_bonds() - if df_data.empty: - console.print("No US bonds data available\n") - return - - print_rich_table( - df_data, - show_index=False, - headers=list(df_data.columns), - title="US Bonds", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "usbonds", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_glbonds(export: str = "", sheet_name: Optional[str] = None): - """Global bonds. [Source: Wall St. Journal] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - df_data = wsj_model.global_bonds() - if df_data.empty: - console.print("No global bonds data available\n") - return - - print_rich_table( - df_data, - show_index=False, - headers=list(df_data.columns), - title="Global Bonds", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "glbonds", - df_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_currencies(export: str = "", sheet_name: Optional[str] = None): - """Display currencies. [Source: Wall St. Journal] - - Parameters - ---------- - export : str - Export dataframe data to csv,json,xlsx file - """ - df_data = wsj_model.global_currencies() - if df_data.empty: - console.print("No currencies data available\n") - return - - print_rich_table( - df_data, - show_index=False, - headers=list(df_data.columns), - title="Currencies", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "currencies", - df_data, - sheet_name, - ) diff --git a/openbb_terminal/economy/yfinance_model.py b/openbb_terminal/economy/yfinance_model.py deleted file mode 100644 index 287ee841a65..00000000000 --- a/openbb_terminal/economy/yfinance_model.py +++ /dev/null @@ -1,762 +0,0 @@ -""" Yahoo Finance Model """ - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Dict, Optional - -import financedatabase as fd -import pandas as pd -import yfinance as yf - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -INDICES = { - "sp500": {"name": "S&P 500 Index", "ticker": "^GSPC"}, - "sp400": {"name": "S&P 400 Mid Cap Index", "ticker": "^SP400"}, - "sp600": {"name": "S&P 600 Small Cap Index", "ticker": "^SP600"}, - "sp500tr": {"name": "S&P 500 TR Index", "ticker": "^SP500TR"}, - "sp_xsp": {"name": "S&P 500 Mini SPX Options Index", "ticker": "^XSP"}, - "nyse_ny": {"name": "NYSE US 100 Index", "ticker": "^NY"}, - "dow_djus": {"name": "Dow Jones US Index", "ticker": "^DJUS"}, - "nyse": {"name": "NYSE Composite Index", "ticker": "^NYA"}, - "amex": {"name": "NYSE-AMEX Composite Index", "ticker": "^XAX"}, - "nasdaq": {"name": "Nasdaq Composite Index", "ticker": "^IXIC"}, - "nasdaq100": {"name": "NASDAQ 100", "ticker": "^NDX"}, - "nasdaq100_ew": {"name": "NASDAQ 100 Equal Weighted Index", "ticker": "^NDXE"}, - "nasdaq50": {"name": "NASDAQ Q50 Index", "ticker": "^NXTQ"}, - "russell1000": {"name": "Russell 1000 Index", "ticker": "^RUI"}, - "russell2000": {"name": "Russell 2000 Index", "ticker": "^RUT"}, - "cboe_bxr": {"name": "CBOE Russell 2000 Buy-Write Index", "ticker": "^BXR"}, - "cboe_bxrt": { - "name": "CBOE Russell 2000 30-Delta Buy-Write Index", - "ticker": "^BXRT", - }, - "russell3000": {"name": "Russell 3000 Index", "ticker": "^RUA"}, - "russellvalue": {"name": "Russell 2000 Value Index", "ticker": "^RUJ"}, - "russellgrowth": {"name": "Russell 2000 Growth Index", "ticker": "^RUO"}, - "w5000": {"name": "Wilshire 5000", "ticker": "^W5000"}, - "w5000flt": {"name": "Wilshire 5000 Float Adjusted Index", "ticker": "^W5000FLT"}, - "dow_dja": {"name": "Dow Jones Composite Average Index", "ticker": "^DJA"}, - "dow_dji": {"name": "Dow Jones Industrial Average Index", "ticker": "^DJI"}, - "ca_tsx": {"name": "TSX Composite Index (CAD)", "ticker": "^GSPTSE"}, - "ca_banks": {"name": "S&P/TSX Composite Banks Index (CAD)", "ticker": "TXBA.TS"}, - "mx_ipc": {"name": "IPC Mexico Index (MXN)", "ticker": "^MXX"}, - "arca_mxy": {"name": "NYSE ARCA Mexico Index (USD)", "ticker": "^MXY"}, - "br_bvsp": {"name": "IBOVESPA Sao Paulo Brazil Index (BRL)", "ticker": "^BVSP"}, - "br_ivbx": {"name": "IVBX2 Indice Valour (BRL)", "ticker": "^IVBX"}, - "ar_mervel": {"name": "S&P MERVAL TR Index (USD)", "ticker": "M.BA"}, - "eu_fteu1": {"name": "FTSE Eurotop 100 Index (EUR)", "ticker": "^FTEU1"}, - "eu_speup": {"name": "S&P Europe 350 Index (EUR)", "ticker": "^SPEUP"}, - "eu_n100": {"name": "Euronext 100 Index (EUR)", "ticker": "^N100"}, - "ftse100": {"name": "FTSE Global 100 Index (GBP)", "ticker": "^FTSE"}, - "ftse250": {"name": "FTSE Global 250 Index (GBP)", "ticker": "^FTMC"}, - "ftse350": {"name": "FTSE Global 350 Index (GBP)", "ticker": "^FTLC"}, - "ftai": {"name": "FTSE AIM All-Share Global Index (GBP)", "ticker": "^FTAI"}, - "uk_ftas": {"name": "UK FTSE All-Share Index (GBP)", "ticker": "^FTAS"}, - "uk_spuk": {"name": "S&P United Kingdom Index (PDS)", "ticker": "^SPUK"}, - "uk_100": {"name": "CBOE UK 100 Index (GBP)", "ticker": "^BUK100P"}, - "ie_iseq": {"name": "ISEQ Irish All Shares Index (EUR)", "ticker": "^ISEQ"}, - "nl_aex": {"name": "Euronext Dutch 25 Index (EUR)", "ticker": "^AEX"}, - "nl_amx": {"name": "Euronext Dutch Mid Cap Index (EUR)", "ticker": "^AMX"}, - "at_atx": {"name": "Wiener Börse Austrian 20 Index (EUR)", "ticker": "^ATX"}, - "at_atx5": {"name": "Vienna ATX Five Index (EUR)", "ticker": "^ATX5"}, - "at_prime": {"name": "Vienna ATX Prime Index (EUR)", "ticker": "^ATXPRIME"}, - "ch_stoxx": {"name": "Zurich STXE 600 PR Index (EUR)", "ticker": "^STOXX"}, - "ch_stoxx50e": {"name": "Zurich ESTX 50 PR Index (EUR)", "ticker": "^STOXX50E"}, - "se_omx30": {"name": "OMX Stockholm 30 Index (SEK)", "ticker": "^OMX"}, - "se_omxspi": {"name": "OMX Stockholm All Share PI (SEK)", "ticker": "^OMXSPI"}, - "se_benchmark": {"name": "OMX Stockholm Benchmark GI (SEK)", "ticker": "^OMXSBGI"}, - "dk_benchmark": {"name": "OMX Copenhagen Benchmark GI (DKK)", "ticker": "^OMXCBGI"}, - "dk_omxc25": {"name": "OMX Copenhagen 25 Index (DKK)", "ticker": "^OMXC25"}, - "fi_omxh25": {"name": "OMX Helsinki 25 (EUR)", "ticker": "^OMXH25"}, - "de_dax40": {"name": "DAX Performance Index (EUR)", "ticker": "^GDAXI"}, - "de_mdax60": {"name": "DAX Mid Cap Performance Index (EUR)", "ticker": "^MDAXI"}, - "de_sdax70": {"name": "DAX Small Cap Performance Index (EUR)", "ticker": "^SDAXI"}, - "de_tecdax30": {"name": "DAX Tech Sector TR Index (EUR)", "ticker": "^TECDAX"}, - "fr_cac40": {"name": "CAC 40 PR Index (EUR)", "ticker": "^FCHI"}, - "fr_next20": {"name": "CAC Next 20 Index (EUR)", "ticker": "^CN20"}, - "it_mib40": {"name": "FTSE MIB 40 Index (EUR)", "ticker": "FTSEMIB.MI"}, - "be_bel20": {"name": "BEL 20 Brussels Index (EUR)", "ticker": "^BFX"}, - "pt_bvlg": { - "name": "Lisbon PSI All-Share Index GR (EUR)", - "ticker": "^BVLG", - }, - "es_ibex35": {"name": "IBEX 35 - Madrid CATS (EUR)", "ticker": "^IBEX"}, - "in_bse": {"name": "S&P Bombay SENSEX (INR)", "ticker": "^BSESN"}, - "in_bse500": { - "name": "S&P BSE 500 Index (INR)", - "ticker": "BSE-500.BO", - }, - "in_bse200": { - "name": "S&P BSE 200 Index (INR)", - "ticker": "BSE-200.BO", - }, - "in_bse100": { - "name": "S&P BSE 100 Index (INR)", - "ticker": "BSE-100.BO", - }, - "in_bse_mcap": { - "name": "S&P Bombay Mid Cap Index (INR)", - "ticker": "BSE-MIDCAP.BO", - }, - "in_bse_scap": { - "name": "S&P Bombay Small Cap Index (INR)", - "ticker": "BSE-SMLCAP.BO", - }, - "in_nse50": {"name": "NSE Nifty 50 Index (INR)", "ticker": "^NSEI"}, - "in_nse_mcap": {"name": "NSE Nifty 50 Mid Cap Index (INR)", "ticker": "^NSEMDCP50"}, - "in_nse_bank": { - "name": "NSE Nifty Bank Industry Index (INR)", - "ticker": "^NSEBANK", - }, - "in_nse500": {"name": "NSE Nifty 500 Index (INR)", "ticker": "^CRSLDX"}, - "il_ta125": {"name": "Tel-Aviv 125 Index (ILS)", "ticker": "^TA125.TA"}, - "za_shariah": { - "name": "Johannesburg Shariah All Share Index (ZAR)", - "ticker": "^J143.JO", - }, - "za_jo": {"name": "Johannesburg All Share Index (ZAR)", "ticker": "^J203.JO"}, - "za_jo_mcap": { - "name": "Johannesburg Large and Mid Cap Index (ZAR)", - "ticker": "^J206.JO", - }, - "za_jo_altex": { - "name": "Johannesburg Alt Exchange Index (ZAR)", - "ticker": "^J232.JO", - }, - "ru_moex": {"name": "MOEX Russia Index (RUB)", "ticker": "IMOEX.ME"}, - "au_aord": {"name": "Australia All Ordinary Share Index (AUD)", "ticker": "^AORD"}, - "au_small": {"name": "S&P/ASX Small Ordinaries Index (AUD)", "ticker": "^AXSO"}, - "au_asx20": { - "name": "S&P/ASX 20 Index (AUD)", - "ticker": "^ATLI", - }, - "au_asx50": { - "name": "S&P/ASX 50 Index (AUD)", - "ticker": "^AFLI", - }, - "au_asx50_mid": { - "name": "S&P/ASX Mid Cap 50 Index (AUD)", - "ticker": "^AXMD", - }, - "au_asx100": { - "name": "S&P/ASX 100 Index (AUD)", - "ticker": "^ATOI", - }, - "au_asx200": {"name": "S&P/ASX 200 Index (AUD)", "ticker": "^AXJO"}, - "au_asx300": { - "name": "S&P/ASX 300 Index (AUD)", - "ticker": "^AXKO", - }, - "au_energy": { - "name": "S&P/ASX 200 Energy Sector Index (AUD)", - "ticker": "^AXEJ", - }, - "au_resources": { - "name": "S&P/ASX 200 Resources Sector Index (AUD)", - "ticker": "^AXJR", - }, - "au_materials": { - "name": "S&P/ASX 200 Materials Sector Index (AUD)", - "ticker": "^AXMJ", - }, - "au_mining": { - "name": "S&P/ASX 300 Metals and Mining Sector Index (AUD)", - "ticker": "^AXMM", - }, - "au_industrials": { - "name": "S&P/ASX 200 Industrials Sector Index (AUD)", - "ticker": "^AXNJ", - }, - "au_discretionary": { - "name": "S&P/ASX 200 Consumer Discretionary Sector Index (AUD)", - "ticker": "^AXDJ", - }, - "au_staples": { - "name": "S&P/ASX 200 Consumer Staples Sector Index (AUD)", - "ticker": "^AXSJ", - }, - "au_health": { - "name": "S&P/ASX 200 Health Care Sector Index (AUD)", - "ticker": "^AXHJ", - }, - "au_financials": { - "name": "S&P/ASX 200 Financials Sector Index (AUD)", - "ticker": "^AXFJ", - }, - "au_reit": {"name": "S&P/ASX 200 A-REIT Industry Index (AUD)", "ticker": "^AXPJ"}, - "au_tech": {"name": "S&P/ASX 200 Info Tech Sector Index (AUD)", "ticker": "^AXIJ"}, - "au_communications": { - "name": "S&P/ASX 200 Communications Sector Index (AUD)", - "ticker": "^AXTJ", - }, - "au_utilities": { - "name": "S&P/ASX 200 Utilities Sector Index (AUD)", - "ticker": "^AXUJ", - }, - "nz50": {"name": "S&P New Zealand 50 Index (NZD)", "ticker": "^nz50"}, - "nz_small": {"name": "S&P/NZX Small Cap Index (NZD)", "ticker": "^NZSC"}, - "kr_kospi": {"name": "KOSPI Composite Index (KRW)", "ticker": "^KS11"}, - "jp_arca": {"name": "NYSE ARCA Japan Index (JPY)", "ticker": "^JPN"}, - "jp_n225": {"name": "Nikkei 255 Index (JPY)", "ticker": "^N225"}, - "jp_n300": {"name": "Nikkei 300 Index (JPY)", "ticker": "^N300"}, - "jp_nknr": {"name": "Nikkei Avg Net TR Index (JPY)", "ticker": "^NKVI.OS"}, - "jp_nkrc": {"name": "Nikkei Avg Risk Control Index (JPY)", "ticker": "^NKRC.OS"}, - "jp_nklv": {"name": "Nikkei Avg Leverage Index (JPY)", "ticker": "^NKLV.OS"}, - "jp_nkcc": {"name": "Nikkei Avg Covered Call Index (JPY)", "ticker": "^NKCC.OS"}, - "jp_nkhd": { - "name": "Nikkei Avg High Dividend Yield Index (JPY)", - "ticker": "^NKHD.OS", - }, - "jp_auto": { - "name": "Nikkei 500 Auto & Auto Parts Index (JPY)", - "ticker": "^NG17.OS", - }, - "jp_fintech": { - "name": "Global Fintech Japan Hedged Index (JPY)", - "ticker": "^FDSFTPRJPY", - }, - "jp_nkdh": {"name": "Nikkei Average USD Hedge Index (JPY)", "ticker": "^NKDH.OS"}, - "jp_nkeh": {"name": "Nikkei Average EUR Hedge Index (JPY)", "ticker": "^NKEH.OS"}, - "jp_ndiv": { - "name": "Nikkei Average Double Inverse Index (JPY)", - "ticker": "^NDIV.OS", - }, - "cn_csi300": {"name": "China CSI 300 Index (CNY)", "ticker": "000300.SS"}, - "cn_sse_comp": {"name": "SSE Composite Index (CNY)", "ticker": "000001.SS"}, - "cn_sse_a": {"name": "SSE A Share Index (CNY)", "ticker": "000002.SS"}, - "cn_szse_comp": {"name": "SZSE Component Index (CNY)", "ticker": "399001.SZ"}, - "cn_szse_a": {"name": "SZSE A-Shares Index (CNY)", "ticker": "399107.SZ"}, - "tw_twii": {"name": "TSEC Weighted Index (TWD)", "ticker": "^TWII"}, - "tw_tcii": {"name": "TSEC Cement and Ceramics Subindex (TWD)", "ticker": "^TCII"}, - "tw_tfii": {"name": "TSEC Foods Subindex (TWD)", "ticker": "^TFII"}, - "tw_tfni": {"name": "TSEC Finance Subindex (TWD)", "ticker": "^TFNI"}, - "tw_tpai": {"name": "TSEC Paper and Pulp Subindex (TWD)", "ticker": "^TPAI"}, - "hk_hsi": {"name": "Hang Seng Index (HKD)", "ticker": "^HSI"}, - "hk_utilities": { - "name": "Hang Seng Utilities Sector Index (HKD)", - "ticker": "^HSNU", - }, - "hk_china": { - "name": "Hang Seng China-Affiliated Corporations Index (HKD)", - "ticker": "^HSCC", - }, - "hk_finance": {"name": "Hang Seng Finance Sector Index (HKD)", "ticker": "^HSNF"}, - "hk_properties": { - "name": "Hang Seng Properties Sector Index (HKD)", - "ticker": "^HSNP", - }, - "hk_hko": {"name": "NYSE ARCA Hong Kong Options Index (USD)", "ticker": "^HKO"}, - "hk_titans30": { - "name": "Dow Jones Hong Kong Titans 30 Index (HKD)", - "ticker": "^XLHK", - }, - "id_jkse": {"name": "Jakarta Composite Index (IDR)", "ticker": "^JKSE"}, - "id_lq45": { - "name": "Indonesia Stock Exchange LQ45 Index (IDR)", - "ticker": "^JKLQ45", - }, - "my_klci": {"name": "FTSE Kuala Lumpur Composite Index (MYR)", "ticker": "^KLSE"}, - "ph_psei": {"name": "Philippine Stock Exchange Index (PHP)", "ticker": "PSEI.PS"}, - "sg_sti": {"name": "STI Singapore Index (SGD)", "ticker": "^STI"}, - "th_set": {"name": "Thailand SET Index (THB)", "ticker": "^SET.BK"}, - "sp_energy_ig": { - "name": "S&P 500 Energy (Industry Group) Index", - "ticker": "^SP500-1010", - }, - "sp_energy_equipment": { - "name": "S&P 500 Energy Equipment & Services Industry Index", - "ticker": "^SP500-101010", - }, - "sp_energy_oil": { - "name": "S&P 500 Oil, Gas & Consumable Fuels Industry Index", - "ticker": "^SP500-101020", - }, - "sp_materials_sector": { - "name": "S&P 500 Materials Sector Index", - "ticker": "^SP500-15", - }, - "sp_materials_ig": { - "name": "S&P 500 Materials (Industry Group) Index", - "ticker": "^SP500-1510", - }, - "sp_materials_construction": { - "name": "S&P 500 Construction Materials Industry Index", - "ticker": "^SP500-151020", - }, - "sp_materials_metals": { - "name": "S&P 500 Mining & Metals Industry Index", - "ticker": "^SP500-151040", - }, - "sp_industrials_sector": { - "name": "S&P 500 Industrials Sector Index", - "ticker": "^SP500-20", - }, - "sp_industrials_goods_ig": { - "name": "S&P 500 Capital Goods (Industry Group) Index", - "ticker": "^SP500-2010", - }, - "sp_industrials_aerospace": { - "name": "S&P 500 Aerospace & Defense Industry Index", - "ticker": "^SP500-201010", - }, - "sp_industrials_building": { - "name": "S&P 500 Building Products Industry Index", - "ticker": "^SP500-201020", - }, - "sp_industrials_construction": { - "name": "S&P 500 Construction & Engineering Industry Index", - "ticker": "^SP500-201030", - }, - "sp_industrials_electrical": { - "name": "S&P 500 Electrical Equipment Industry Index", - "ticker": "^SP500-201040", - }, - "sp_industrials_conglomerates": { - "name": "S&P 500 Industrial Conglomerates Industry Index", - "ticker": "^SP500-201050", - }, - "sp_industrials_machinery": { - "name": "S&P 500 Machinery Industry Index", - "ticker": "^SP500-201060", - }, - "sp_industrials_distributors": { - "name": "S&P 500 Trading Companies & Distributors Industry Index", - "ticker": "^SP500-201070", - }, - "sp_industrials_services_ig": { - "name": "S&P 500 Commercial & Professional Services (Industry Group) Index", - "ticker": "^SP500-2020", - }, - "sp_industrials_services_supplies": { - "name": "S&P 500 Commercial Services & Supplies Industry Index", - "ticker": "^SP500-202010", - }, - "sp_industrials_transport_ig": { - "name": "S&P 500 Transportation (Industry Group) Index", - "ticker": "^SP500-2030", - }, - "sp_industrials_transport_air": { - "name": "S&P 500 Air Freight & Logistics Industry", - "ticker": "^SP500-203010", - }, - "sp_industrials_transport_airlines": { - "name": "S&P 500 Airlines Industry Index", - "ticker": "^SP500-203020", - }, - "sp_industrials_transport_ground": { - "name": "S&P 500 Road & Rail Industry Index", - "ticker": "^SP500-203040", - }, - "sp_discretionary_sector": { - "name": "S&P 500 Consumer Discretionary Index", - "ticker": "^SP500-25", - }, - "sp_discretionary_autos-ig": { - "name": "S&P 500 Automobiles and Components (Industry Group) Index", - "ticker": "^SP500-2510", - }, - "sp_discretionary_auto_components": { - "name": "S&P 500 Auto Components Industry Index", - "ticker": "^SP500-251010", - }, - "sp_discretionary_autos": { - "name": "S&P 500 Automobiles Industry Index", - "ticker": "^SP500-251020", - }, - "sp_discretionary_durables_ig": { - "name": "S&P 500 Consumer Durables & Apparel (Industry Group) Index", - "ticker": "^SP500-2520", - }, - "sp_discretionary_durables_household": { - "name": "S&P 500 Household Durables Industry Index", - "ticker": "^SP500-252010", - }, - "sp_discretionary_leisure": { - "name": "S&P 500 Leisure Products Industry Index", - "ticker": "^SP500-252020", - }, - "sp_discretionary_textiles": { - "name": "S&P 500 Textiles, Apparel & Luxury Goods Industry Index", - "ticker": "^SP500-252030", - }, - "sp_discretionary_services_consumer": { - "name": "S&P 500 Consumer Services (Industry Group) Index", - "ticker": "^SP500-2530", - }, - "sp_staples_sector": { - "name": "S&P 500 Consumer Staples Sector Index", - "ticker": "^SP500-30", - }, - "sp_staples_retail_ig": { - "name": "S&P 500 Food & Staples Retailing (Industry Group) Index", - "ticker": "^SP500-3010", - }, - "sp_staples_food_ig": { - "name": "S&P 500 Food Beverage & Tobacco (Industry Group) Index", - "ticker": "^SP500-3020", - }, - "sp_staples_beverages": { - "name": "S&P 500 Beverages Industry Index", - "ticker": "^SP500-302010", - }, - "sp_staples_products_food": { - "name": "S&P 500 Food Products Industry Index", - "ticker": "^SP500-302020", - }, - "sp_staples_tobacco": { - "name": "S&P 500 Tobacco Industry Index", - "ticker": "^SP500-302030", - }, - "sp_staples_household_ig": { - "name": "S&P 500 Household & Personal Products (Industry Group) Index", - "ticker": "^SP500-3030", - }, - "sp_staples_products_household": { - "name": "S&P 500 Household Products Industry Index", - "ticker": "^SP500-303010", - }, - "sp_staples_products_personal": { - "name": "S&P 500 Personal Products Industry Index", - "ticker": "^SP500-303020", - }, - "sp_health_sector": { - "name": "S&P 500 Health Care Sector Index", - "ticker": "^SP500-35", - }, - "sp_health_equipment": { - "name": "S&P 500 Health Care Equipment & Services (Industry Group) Index", - "ticker": "^SP500-3510", - }, - "sp_health_supplies": { - "name": "S&P 500 Health Care Equipment & Supplies Industry Index", - "ticker": "^SP500-351010", - }, - "sp_health_providers": { - "name": "S&P 500 Health Care Providers & Services Industry Index", - "ticker": "^SP500-351020", - }, - "sp_health_sciences": { - "name": "S&P 500 Pharmaceuticals, Biotechnology & Life Sciences (Industry Group) Index", - "ticker": "^SP500-3520", - }, - "sp_health_biotech": { - "name": "S&P 500 Biotechnology Industry Index", - "ticker": "^SP500-352010", - }, - "sp_health_pharma": { - "name": "S&P 500 Pharmaceuticals Industry Index", - "ticker": "^SP500-352020", - }, - "sp_financials_sector": { - "name": "S&P 500 Financials Sector Index", - "ticker": "^SP500-40", - }, - "sp_financials_diversified_ig": { - "name": "S&P 500 Diversified Financials (Industry Group) Index", - "ticker": "^SP500-4020", - }, - "sp_financials_services": { - "name": "S&P 500 Diversified Financial Services Industry Index", - "ticker": "^SP500-402010", - }, - "sp_financials_consumer": { - "name": "S&P 500 Consumer Finance Industry Index", - "ticker": "^SP500-402020", - }, - "sp_financials_capital": { - "name": "S&P 500 Capital Markets Industry Index", - "ticker": "^SP500-402030", - }, - "sp_it_sector": { - "name": "S&P 500 IT Sector Index", - "ticker": "^SP500-45", - }, - "sp_it_saas_ig": { - "name": "S&P 500 Software and Services (Industry Group) Index", - "ticker": "^SP500-4510", - }, - "sp_it_software": { - "name": "S&P 500 Software Industry Index", - "ticker": "^SP500-451030", - }, - "sp_it_hardware": { - "name": "S&P 500 Technology Hardware Equipment (Industry Group) Index", - "ticker": "^SP500-4520", - }, - "sp_it_semi": { - "name": "S&P 500 Semiconductor & Semiconductor Equipment Industry", - "ticker": "^SP500-453010", - }, - "sp_communications_sector": { - "name": "S&P 500 Communications Sector Index", - "ticker": "^SP500-50", - }, - "sp_communications_telecom": { - "name": "S&P 500 Diversified Telecommunications Services Industry Index", - "ticker": "^SP500-501010", - }, - "sp_utilities_sector": { - "name": "S&P 500 Utilities Sector Index", - "ticker": "^SP500-55", - }, - "sp_utilities_electricity": { - "name": "S&P 500 Electric Utilities Index", - "ticker": "^SP500-551010", - }, - "sp_utilities_multis": { - "name": "S&P 500 Multi-Utilities Industry Index", - "ticker": "^SP500-551030", - }, - "sp_re_sector": { - "name": "S&P 500 Real Estate Sector Index", - "ticker": "^SP500-60", - }, - "sp_re_ig": { - "name": "S&P 500 Real Estate (Industry Group) Index", - "ticker": "^SP500-6010", - }, - "sphyda": {"name": "S&P High Yield Aristocrats Index", "ticker": "^SPHYDA"}, - "dow_djt": {"name": "Dow Jones Transportation Average Index", "ticker": "^DJT"}, - "dow_dju": {"name": "Dow Jones Utility Average Index", "ticker": "^DJU"}, - "dow_rci": {"name": "Dow Jones Composite All REIT Index", "ticker": "^RCI"}, - "reit_fnar": {"name": "FTSE Nareit All Equity REITs Index", "ticker": "^FNAR"}, - "nq_ixch": {"name": "NASDAQ Health Care Index", "ticker": "^IXCH"}, - "nq_nbi": {"name": "NASDAQ Biotech Index", "ticker": "^NBI"}, - "nq_tech": {"name": "NASDAQ 100 Technology Sector Index", "ticker": "^NDXT"}, - "nq_ex_tech": {"name": "NASDAQ 100 Ex-Tech Sector Index", "ticker": "^NDXX"}, - "nq_ixtc": {"name": "NASDAQ Telecommunications Index", "ticker": "^IXTC"}, - "nq_inds": {"name": "NASDAQ Industrial Index", "ticker": "^INDS"}, - "nq_ixco": {"name": "NASDAQ Computer Index", "ticker": "^INCO"}, - "nq_bank": {"name": "NASDAQ Bank Index", "ticker": "^BANK"}, - "nq_bkx": {"name": "KBW NASDAQ Bank Index", "ticker": "^BKX"}, - "nq_krx": {"name": "KBW NASDAQ Regional Bank Index", "ticker": "^KRX"}, - "nq_kix": {"name": "KBW NASDAQ Insurance Index", "ticker": "^KIX"}, - "nq_ksx": {"name": "KBW NASDAQ Capital Markets Index", "ticker": "^KSX"}, - "nq_tran": {"name": "NASDAQ Transportation Index", "ticker": "^TRAN"}, - "ice_auto": {"name": "ICE FactSet Global NextGen Auto Index", "ticker": "^ICEFSNA"}, - "ice_comm": { - "name": "ICE FactSet Global NextGen Communications Index", - "ticker": "^ICEFSNC", - }, - "nyse_nyl": {"name": "NYSE World Leaders Index", "ticker": "^NYL"}, - "nyse_nyi": {"name": "NYSE International 100 Index", "ticker": "^NYI"}, - "nyse_nyy": {"name": "NYSE TMT Index", "ticker": "^NYY"}, - "nyse_fang": {"name": "NYSE FANG+TM index", "ticker": "^NYFANG"}, - "arca_xmi": {"name": "NYSE ARCA Major Market Index", "ticker": "^XMI"}, - "arca_xbd": {"name": "NYSE ARCA Securities Broker/Dealer Index", "ticker": "^XBD"}, - "arca_xii": {"name": "NYSE ARCA Institutional Index", "ticker": "^XII"}, - "arca_xoi": {"name": "NYSE ARCA Oil and Gas Index", "ticker": "^XOI"}, - "arca_xng": {"name": "NYSE ARCA Natural Gas Index", "ticker": "^XNG"}, - "arca_hui": {"name": "NYSE ARCA Gold Bugs Index", "ticker": "^HUI"}, - "arca_ixb": {"name": "NYSE Materials Select Sector Index", "ticker": "^IXB"}, - "arca_drg": {"name": "NYSE ARCA Phramaceutical Index", "ticker": "^DRG"}, - "arca_btk": {"name": "NYSE ARCA Biotech Index", "ticker": "^BKT"}, - "arca_pse": {"name": "NYSE ARCA Tech 100 Index", "ticker": "^PSE"}, - "arca_nwx": {"name": "NYSE ARCA Networking Index", "ticker": "^NWX"}, - "arca_xci": {"name": "NYSE ARCA Computer Tech Index", "ticker": "^XCI"}, - "arca_xal": {"name": "NYSE ARCA Airline Index", "ticker": "^XAL"}, - "arca_xtc": {"name": "NYSE ARCA N.A. Telecom Industry Index", "ticker": "^XTC"}, - "phlx_sox": {"name": "PHLX Semiconductor Index", "ticker": "^SOX"}, - "phlx_xau": {"name": "PHLX Gold/Silver Index", "ticker": "^XAU"}, - "phlx_hgx": {"name": "PHLX Housing Sector Index", "ticker": "^HGX"}, - "phlx_osx": {"name": "PHLX Oil Services Sector Index", "ticker": "^OSX"}, - "phlx_uty": {"name": "PHLX Utility Sector Index", "ticker": "^UTY"}, - "w5klcg": {"name": "Wilshire US Large Cap Growth Index", "ticker": "^W5KLCG"}, - "w5klcv": {"name": "Wilshire US Large Cap Value Index", "ticker": "^W5KLCV"}, - "reit_wgreit": {"name": "Wilshire Global REIT Index", "ticker": "^WGREIT"}, - "reit_wgresi": { - "name": "Wilshire Global Real Estate Sector Index", - "ticker": "^WGRESI", - }, - "reit_wilreit": {"name": "Wilshire US REIT Index", "ticker": "^WILREIT"}, - "reit_wilresi": { - "name": "Wilshire US Real Estate Security Index", - "ticker": "^WILRESI", - }, - "cboe_bxm": {"name": "CBOE Buy-Write Monthly Index", "ticker": "^BXM"}, - "cboe_vix": {"name": "CBOE S&P 500 Volatility Index", "ticker": "^VIX"}, - "cboe_vix9d": {"name": "CBOE S&P 500 9-Day Volatility Index", "ticker": "^VIX9D"}, - "cboe_vix3m": {"name": "CBOE S&P 500 3-Month Volatility Index", "ticker": "^VIX3M"}, - "cboe_vin": {"name": "CBOE Near-Term VIX Index", "ticker": "^VIN"}, - "cboe_vvix": {"name": "CBOE VIX Volatility Index", "ticker": "^VVIX"}, - "cboe_shortvol": {"name": "CBOE Short VIX Futures Index", "ticker": "^SHORTVOL"}, - "cboe_skew": {"name": "CBOE Skew Index", "ticker": "^SKEW"}, - "cboe_vxn": {"name": "CBOE NASDAQ 100 Volatility Index", "ticker": "^VXN"}, - "cboe_gvz": {"name": "CBOE Gold Volatility Index", "ticker": "^GVZ"}, - "cboe_ovx": {"name": "CBOE Crude Oil Volatility Index", "ticker": "^OVX"}, - "cboe_tnx": {"name": "CBOE Interest Rate 10 Year T-Note", "ticker": "^TNX"}, - "cboe_tyx": {"name": "CBOE 30 year Treasury Yields", "ticker": "^TYX"}, - "cboe_irx": {"name": "CBOE 13 Week Treasury Bill", "ticker": "^IRX"}, - "cboe_evz": {"name": "CBOE Euro Currency Volatility Index", "ticker": "^EVZ"}, - "cboe_rvx": {"name": "CBOE Russell 2000 Volatility Index", "ticker": "^RVX"}, - "move": {"name": "ICE BofAML Move Index", "ticker": "^MOVE"}, - "dxy": {"name": "US Dollar Index", "ticker": "DX-Y.NYB"}, - "crypto200": {"name": "CMC Crypto 200 Index by Solacti", "ticker": "^CMC200"}, -} - - -@log_start_end(log=logger) -def get_index( - index: str, - interval: str = "1d", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - column: str = "Adj Close", -) -> pd.Series: - """Obtain data on any index [Source: Yahoo Finance] - - Parameters - ---------- - index: str - The index you wish to collect data for. - start_date : Optional[str] - the selected country - end_date : Optional[str] - The currency you wish to convert the data to. - interval : str - Valid intervals: 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 5d, 1wk, 1mo or 3mo - Intraday data cannot extend last 60 days - column : str - The column you wish to select, by default this is Adjusted Close. - - Returns - ------- - pd.Series - A series with the requested index - """ - ticker = INDICES[index.lower()]["ticker"] if index.lower() in INDICES else index - - try: - if start_date: - datetime.strptime(str(start_date), "%Y-%m-%d") - if end_date: - datetime.strptime(str(end_date), "%Y-%m-%d") - except ValueError: - console.print("[red]Please format date as YYYY-MM-DD[/red]\n") - return pd.Series(dtype="object") - - index_data = yf.download( - ticker, - start=start_date, - end=end_date, - interval=interval, - progress=False, - ignore_tz=True, - ) - - if column not in index_data.columns: - console.print( - f"The chosen column is not available for {ticker}. Please choose " - f"between: {', '.join(index_data.columns)}\n" - ) - return pd.Series(dtype="float64") - if index_data.empty or len(index_data) < 2: - console.print( - f"The chosen index {ticker}, returns no data. Please check if " - f"there is any data available.\n" - ) - return pd.Series(dtype="float64") - - return index_data[column] - - -@log_start_end(log=logger) -def get_available_indices() -> Dict[str, Dict[str, str]]: - """Get available indices - - Returns - ------- - Dict[str, Dict[str, str]] - Dictionary with available indices and respective detail - """ - return INDICES - - -@log_start_end(log=logger) -def get_indices( - indices: list, - interval: str = "1d", - start_date: Optional[int] = None, - end_date: Optional[int] = None, - column: str = "Adj Close", - returns: bool = False, -) -> pd.DataFrame: - """Get data on selected indices over time [Source: Yahoo Finance] - - Parameters - ---------- - indices: list - A list of indices to get data. Available indices can be accessed through economy.available_indices(). - interval: str - Valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo - Intraday data cannot extend last 60 days - start_date : str - The starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. - end_date : str - The end date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. - column : str - Which column to load in, by default "Adjusted Close". - returns: bool - Flag to show cumulative returns on index - - Returns - ------- - pd.Dataframe - Dataframe with historical data on selected indices. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.economy.available_indices() - >>> openbb.economy.index(["^GSPC", "sp400"]) - """ - - indices_data: pd.DataFrame = pd.DataFrame() - - for index in indices: - indices_data[index] = get_index(index, interval, start_date, end_date, column) - - if returns: - indices_data = indices_data.pct_change().dropna() - indices_data = indices_data + 1 - indices_data = indices_data.cumprod() - - return indices_data - - -@log_start_end(log=logger) -def get_search_indices(keyword: list) -> pd.DataFrame: - """Search indices by keyword. [Source: FinanceDatabase] - - Parameters - ---------- - keyword: list - The keyword you wish to search for. This can include spaces. - - Returns - ------- - pd.Dataframe - Dataframe with the available options. - """ - keyword_adjusted = ( - keyword.replace(",", " ") if isinstance(keyword, str) else " ".join(keyword) # type: ignore - ) - - indices = fd.Indices() - - queried_indices = indices.search(name=keyword_adjusted, exclude_exchanges=True) - queried_indices = pd.concat( - [ - queried_indices, - indices.search(index=keyword_adjusted), - ] - ) - queried_indices = queried_indices.drop_duplicates() - - return keyword_adjusted, queried_indices diff --git a/openbb_terminal/economy/yfinance_view.py b/openbb_terminal/economy/yfinance_view.py deleted file mode 100644 index 6961255f854..00000000000 --- a/openbb_terminal/economy/yfinance_view.py +++ /dev/null @@ -1,159 +0,0 @@ -""" EconDB View """ - -__docformat__ = "numpy" -# pylint:disable=too-many-arguments,unused-argument - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.economy.yfinance_model import ( - INDICES, - get_indices, - get_search_indices, -) -from openbb_terminal.helper_funcs import export_data, print_rich_table, reindex_dates -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def show_indices( - indices: Union[list, pd.DataFrame], - interval: str = "1d", - start_date: Optional[int] = None, - end_date: Optional[int] = None, - column: str = "Adj Close", - returns: bool = False, - raw: bool = False, - external_axes: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - limit: int = 10, -) -> Union[pd.DataFrame, OpenBBFigure]: - """Load (and show) the selected indices over time [Source: Yahoo Finance] - Parameters - ---------- - indices: Union[list, pd.DataFrame] - A list of indices to load in, or a DataFrame with indices as columns (to plot) - Available indices can be accessed through economy.available_indices(). - interval: str - Valid intervals: 1m,2m,5m,15m,30m,60m,90m,1h,1d,5d,1wk,1mo,3mo - Intraday data cannot extend last 60 days - start_date : str - The starting date, format "YEAR-MONTH-DAY", i.e. 2010-12-31. - end_date : str - The end date, format "YEAR-MONTH-DAY", i.e. 2020-06-05. - column : str - Which column to load in, by default this is the Adjusted Close. - returns: bool - Flag to show cumulative returns on index - raw : bool - Whether to display the raw output. - external_axes : bool, optional - Whether to return the figure object or not, by default False - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - Returns - ------- - Plots the Series. - """ - if isinstance(indices, list): - indices_data = get_indices( - indices, interval, start_date, end_date, column, returns - ) - elif isinstance(indices, pd.DataFrame): - indices_data = indices - indices = indices_data.columns - else: - return console.print( - "Indices must be a list or a DataFrame with indices values as columns (column names = indices)" - ) - - fig = OpenBBFigure(title="Indices") - - new_title = [] - - for index in indices: - label = INDICES[index.lower()]["name"] if index.lower() in INDICES else index - new_title.append(label) - if not indices_data[index].empty: - if returns: - indices_data.index.name = "date" - data_to_percent = 100 * (indices_data[index].values - 1) - plot_data = reindex_dates(indices_data) - fig.add_scatter( - x=plot_data["date"].to_list(), - y=data_to_percent, - mode="lines", - name=label, - ) - fig.update_layout( - yaxis_title="Percentage (%)", - xaxis_range=[plot_data.index[0], plot_data.index[-1]], - ) - else: - fig.add_scatter( - x=indices_data.index, - y=indices_data[index], - mode="lines", - name=label, - ) - - if len(indices) < 2: - fig.update_layout(title=f"{' - '.join(new_title)}", yaxis=dict(side="right")) - - if raw: - # was a -iloc so we need to flip the index as we use head - indices_data = indices_data.sort_index(ascending=False) - print_rich_table( - indices_data.fillna("-"), - headers=list(indices_data.columns), - show_index=True, - title=f"Indices [column: {column}]", - export=bool(export), - limit=limit, - ) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "index_data", - indices_data, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def search_indices(query: list, limit: int = 10): - """Load (and show) the selected indices over time [Source: Yahoo Finance] - Parameters - ---------- - query: list - The keyword you wish to search for. This can include spaces. - limit: int - The amount of views you want to show, by default this is set to 10. - Returns - ------- - Shows a rich table with the available options. - """ - - keyword_adjusted, queried_indices = get_search_indices(query) - - print_rich_table( - queried_indices, - show_index=True, - index_name="ticker", - headers=list(queried_indices.columns), - title=f"Queried Indices with keyword {keyword_adjusted}", - limit=limit, - ) diff --git a/openbb_terminal/etf/README.md b/openbb_terminal/etf/README.md deleted file mode 100644 index 9d309811c62..00000000000 --- a/openbb_terminal/etf/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Resources - -* How to invest in ETFs for beginners : diff --git a/openbb_terminal/etf/__init__.py b/openbb_terminal/etf/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/etf/discovery/disc_controller.py b/openbb_terminal/etf/discovery/disc_controller.py deleted file mode 100644 index 29d4c2e2501..00000000000 --- a/openbb_terminal/etf/discovery/disc_controller.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Discovery Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.etf.discovery import wsj_view -from openbb_terminal.helper_funcs import EXPORT_ONLY_RAW_DATA_ALLOWED -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class DiscoveryController(BaseController): - """Discovery Controller class""" - - CHOICES_COMMANDS = [ - "gainers", - "decliners", - "active", - ] - PATH = "/etf/disc/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("etf/disc/", 60) - mt.add_cmd("gainers") - mt.add_cmd("decliners") - mt.add_cmd("active") - console.print(text=mt.menu_text, menu="ETF - Discovery") - - @log_start_end(log=logger) - def call_gainers(self, other_args): - """Process gainers command""" - parser = argparse.ArgumentParser( - prog="gainers", - description="Displays top ETF/Mutual fund gainers from wsj.com/market-data", - add_help=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10 - ) - if ns_parser: - wsj_view.show_top_mover( - "gainers", - ns_parser.limit, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_decliners(self, other_args): - """Process decliners command""" - parser = argparse.ArgumentParser( - prog="decliners", - description="Displays top ETF/Mutual fund decliners from wsj.com/market-data", - add_help=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=10, - ) - if ns_parser: - wsj_view.show_top_mover( - "decliners", - ns_parser.limit, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_active(self, other_args): - """Process gainers command""" - parser = argparse.ArgumentParser( - prog="active", - description="Displays most active ETF/Mutual funds from wsj.com/market-data", - add_help=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=10, - ) - if ns_parser: - wsj_view.show_top_mover( - "active", - ns_parser.limit, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/etf/discovery/wsj_model.py b/openbb_terminal/etf/discovery/wsj_model.py deleted file mode 100644 index abc3d1dcf78..00000000000 --- a/openbb_terminal/etf/discovery/wsj_model.py +++ /dev/null @@ -1,93 +0,0 @@ -"""WSJ model""" - -__docformat__ = "numpy" -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def etf_movers(sort_type: str = "gainers", export: bool = False) -> pd.DataFrame: - """ - Scrape data for top etf movers. - Parameters - ---------- - sort_type: str - Data to get. Can be "gainers", "decliners" or "active" - - Returns - ------- - etfmovers: pd.DataFrame - Datafame containing the name, price, change and the volume of the etf - """ - - if sort_type.lower() == "gainers": - url = ( - "https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application" - "%22%3A%22WSJ%22%2C%22etfMover%22%3A%22leaders%22%2C%22count%22%3A25%7D&type=" - "mdc_etfmovers" - ) - elif sort_type.lower() == "decliners": - url = ( - "https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application" - "%22%3A%22WSJ%22%2C%22etfMover%22%3A%22laggards%22%2C%22count%22%3A25%7D&type=" - "mdc_etfmovers" - ) - elif sort_type.lower() == "active": - url = ( - "https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application" - "%22%3A%22WSJ%22%2C%22etfMover%22%3A%22most_active%22%2C%22count%22%3A25%7D&type=" - "mdc_etfmovers" - ) - else: - url = "" - - if url: - data = request(url, headers={"User-Agent": get_user_agent()}).json() - symbol, name, last_price, net_change, percent_change, volume = ( - [], - [], - [], - [], - [], - [], - ) - - for entry in data["data"]["instruments"]: - symbol.append(entry["ticker"]) - name.append(entry["name"]) - last_price.append(entry["lastPrice"]) - net_change.append(entry["priceChange"]) - percent_change.append(entry["percentChange"]) - volume.append(entry["formattedVolume"]) - - if export: - etfmovers = pd.DataFrame( - { - " ": symbol, - "Name": name, - "Price": last_price, - "Chg": net_change, - "%Chg": percent_change, - "Vol": volume, - } - ) - else: - etfmovers = pd.DataFrame( - { - " ": name, - "Price": last_price, - "Chg": net_change, - "%Chg": percent_change, - "Vol": volume, - } - ) - - return etfmovers - - return pd.DataFrame() diff --git a/openbb_terminal/etf/discovery/wsj_view.py b/openbb_terminal/etf/discovery/wsj_view.py deleted file mode 100644 index 610eb5bfa98..00000000000 --- a/openbb_terminal/etf/discovery/wsj_view.py +++ /dev/null @@ -1,58 +0,0 @@ -"""WSJ view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.etf.discovery import wsj_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def show_top_mover( - sort_type: str = "gainers", - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """ - Show top ETF movers from wsj.com - - Parameters - ---------- - sort_type: str - What to show. Either Gainers, Decliners or Activity - limit: int - Number of etfs to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - data = wsj_model.etf_movers(sort_type) - if data.empty: - console.print("No data available\n") - return - - print_rich_table( - data, - show_index=False, - headers=list(data.columns), - title="ETF Movers", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - sort_type, - data, - sheet_name, - ) diff --git a/openbb_terminal/etf/etf_controller.py b/openbb_terminal/etf/etf_controller.py deleted file mode 100644 index 46b018da3fe..00000000000 --- a/openbb_terminal/etf/etf_controller.py +++ /dev/null @@ -1,704 +0,0 @@ -"""ETF Controller""" - -__docformat__ = "numpy" - -import argparse -import logging -import os -from datetime import datetime, timedelta -from typing import List, Optional - -import yfinance as yf - -from openbb_terminal.common import newsapi_view -from openbb_terminal.common.quantitative_analysis import qa_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.etf import ( - financedatabase_view, - fmp_view, - stockanalysis_model, - stockanalysis_view, -) -from openbb_terminal.etf.discovery import disc_controller -from openbb_terminal.etf.technical_analysis import ta_controller -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - export_data, - list_from_str, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.comparison_analysis import ca_controller - -# pylint: disable=C0415,C0302 - - -logger = logging.getLogger(__name__) - - -class ETFController(BaseController): - """ETF Controller class""" - - CHOICES_COMMANDS = [ - "search", - "load", - "overview", - "holdings", - "holding_perf", - "news", - "candle", - "weights", - "compare", - "resources", - ] - CHOICES_MENUS = [ - "ta", - "ca", - # "scr", - "disc", - ] - CANDLE_COLUMNS = [ - "adjclose", - "open", - "close", - "high", - "low", - "volume", - "returns", - "logret", - ] - - PATH = "/etf/" - FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md") - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.etf_name = "" - self.etf_data = "" - self.etf_holdings: List = list() - self.TRY_RELOAD = True - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("etf/") - mt.add_cmd("search") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_symbol", self.etf_name) - mt.add_param("_major_holdings", ", ".join(self.etf_holdings)) - mt.add_raw("\n") - mt.add_menu("ca", len(self.etf_holdings)) - mt.add_menu("disc") - # mt.add_menu("scr") - mt.add_raw("\n") - mt.add_cmd("overview", self.etf_name) - mt.add_cmd("holdings", self.etf_name) - mt.add_cmd("holding_perf", self.etf_name) - mt.add_cmd("weights", self.etf_name) - mt.add_cmd("news", self.etf_name) - mt.add_cmd("candle", self.etf_name) - mt.add_raw("\n") - mt.add_cmd("compare", self.etf_name) - mt.add_raw("\n") - mt.add_menu("ta", self.etf_name) - console.print(text=mt.menu_text, menu="ETF") - - def custom_reset(self): - """Class specific component of reset command""" - if self.etf_name: - return ["etf", f"load {self.etf_name}"] - return [] - - @log_start_end(log=logger) - def call_search(self, other_args: List[str]): - """Process search command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="search", - description="Search ETF by name [Source: FinanceDatabase/StockAnalysis.com]", - ) - parser.add_argument( - "-n", - "--name", - type=str, - dest="name", - nargs="+", - help="Name to look for ETFs", - default="", - required="-h" not in other_args - and "-d" not in other_args - and "--description" not in other_args, - ) - parser.add_argument( - "-d", - "--description", - type=str, - dest="description", - nargs="+", - help="Name to look for ETFs", - default="", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=5, - ) - if ns_parser: - name_to_search = " ".join(ns_parser.name) - - if ns_parser.name: - if ns_parser.source == "FinanceDatabase": - financedatabase_view.display_etf_by_name( - name=name_to_search, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - elif ns_parser.source == "StockAnalysis": - stockanalysis_view.display_etf_by_name( - name=name_to_search, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - console.print("Wrong source choice!\n") - else: - description_to_search = " ".join(ns_parser.description) - financedatabase_view.display_etf_by_description( - description=description_to_search, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - """Process load command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Load ETF ticker to perform analysis on.", - ) - parser.add_argument( - "-t", - "--ticker", - action="store", - dest="ticker", - required="-h" not in other_args, - help="ETF ticker", - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - default=(datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d"), - dest="start", - help="The starting date (format YYYY-MM-DD) of the ETF", - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=datetime.now().strftime("%Y-%m-%d"), - dest="end", - help="The ending date (format YYYY-MM-DD) of the ETF", - ) - - parser.add_argument( - "-l", - "--limit", - type=check_positive, - default=5, - dest="limit", - help="Limit of holdings to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - df_etf_candidate = yf.download( - ns_parser.ticker, - start=ns_parser.start, - end=ns_parser.end, - progress=False, - ) - if df_etf_candidate.empty: - console.print("ETF ticker provided does not exist!\n") - return - - df_etf_candidate.index.name = "date" - - self.etf_name = ns_parser.ticker.upper() - self.etf_data = df_etf_candidate - - console.print( - f"Loading Daily data for {self.etf_name} with starting period {ns_parser.start.strftime('%Y-%m-%d')}.", - ) - - try: - self.etf_holdings = list( - stockanalysis_model.get_etf_holdings(self.etf_name)[ - : ns_parser.limit - ].index - ) - except Exception as e: - console.print(f"Error: {e}") - - @log_start_end(log=logger) - def call_overview(self, other_args: List[str]): - """Process overview command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="overview", - description="Get overview data for selected etf", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - stockanalysis_view.view_overview( - symbol=self.etf_name, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_holdings(self, other_args: List[str]): - """Process holdings command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="holdings", - description="Look at ETF company holdings", - ) - parser.add_argument( - "-l", - "--limit", - type=int, - dest="limit", - help="Number of holdings to get", - default=10, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.etf_name: - stockanalysis_view.view_holdings( - symbol=self.etf_name, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - console.print() - else: - console.print("Please load a ticker using . \n") - - @log_start_end(log=logger) - def call_news(self, other_args: List[str]): - """Process news command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="news", - description=""" - Prints latest news about ETF, including date, title and web link. - [Source: News API] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Limit of latest news being printed.", - ) - parser.add_argument( - "-d", - "--date", - action="store", - dest="n_start_date", - type=valid_date, - default=datetime.now() - timedelta(days=7), - help="The starting date (format YYYY-MM-DD) to search articles from", - ) - parser.add_argument( - "-o", - "--oldest", - action="store_false", - dest="n_oldest", - default=True, - help="Show oldest articles first", - ) - parser.add_argument( - "-s", - "--sources", - default="", - type=str, - help="Show news only from the sources specified (e.g bbc yahoo.com)", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.etf_name: - sources = list_from_str(ns_parser.sources) - for idx, source in enumerate(sources): - if source.find(".") == -1: - sources[idx] += ".com" - clean_sources = ",".join(sources) - - newsapi_view.display_news( - query=self.etf_name, - limit=ns_parser.limit, - start_date=ns_parser.n_start_date.strftime("%Y-%m-%d"), - show_newest=ns_parser.n_oldest, - sources=clean_sources, - ) - else: - console.print("Use 'load ' prior to this command!") - console.print() - - @log_start_end(log=logger) - def call_candle(self, other_args: List[str]): - """Process candle command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="candle", - description="Shows historic data for an ETF", - ) - parser.add_argument( - "-p", - "--plotly", - dest="plotly", - action="store_false", - default=True, - help="Flag to show interactive plotly chart.", - ) - parser.add_argument( - "--sort", - "-s", - choices=self.CANDLE_COLUMNS, - default="", - type=str.lower, - dest="sort", - help="Choose a column to sort by", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-n", - "--num", - type=check_positive, - help="Number to show if raw selected", - dest="num", - default=20, - choices=range(1, 100), - metavar="NUM", - ) - parser.add_argument( - "-t", - "--trend", - action="store_true", - default=False, - help="Flag to add high and low trends to candle.", - dest="trendlines", - ) - parser.add_argument( - "--ma", - dest="mov_avg", - type=str, - help=( - "Add moving average in number of days to plot and separate by a comma. " - "Value for ma (moving average) keyword needs to be greater than 1." - ), - default="", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ) - if ns_parser: - figure = None - if not self.etf_name: - console.print("No ticker loaded. First use `load {ticker}`\n") - return - if ns_parser.raw: - qa_view.display_raw( - data=self.etf_data, - sortby=ns_parser.sort, - ascend=ns_parser.reverse, - limit=ns_parser.num, - ) - - else: - data = stocks_helper.process_candle(self.etf_data) - mov_avgs = [] - - if ns_parser.mov_avg: - mov_list = (num for num in ns_parser.mov_avg.split(",")) - - for num in mov_list: - try: - clean_num = int(num) - - if clean_num <= 1: - raise ValueError - - mov_avgs.append(clean_num) - except ValueError: - console.print( - f"[red]{num} is not a valid moving average, must be an integer " - "greater than 1.[/red]\n" - ) - - figure = stocks_helper.display_candle( - symbol=self.etf_name, - data=data, - add_trend=ns_parser.trendlines, - ma=mov_avgs, - asset_type="ETF", - external_axes=True, - ) - - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - f"{self.etf_name}", - self.etf_data, - ns_parser.sheet_name, - figure=figure, - ) - if figure: - figure.show() # type: ignore - - @log_start_end(log=logger) - def call_weights(self, other_args: List[str]): - """Process weights command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="weights", - description="Look at ETF sector holdings", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ) - if ns_parser: - fmp_view.display_etf_weightings( - name=self.etf_name, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ta(self, _): - """Process ta command""" - if self.etf_name and not self.etf_data.empty: - self.queue = self.load_class( - ta_controller.TechnicalAnalysisController, - self.etf_name, - self.etf_data.index[0], - self.etf_data, - self.queue, - ) - else: - console.print("Use 'load ' prior to this command!") - - @log_start_end(log=logger) - def call_ca(self, other_args: List[str]): - """Process ca command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ca", - description="Compare ETF's holdings with each other", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ) - - if ns_parser: - if len(self.etf_holdings) == 0: - console.print( - "Load a ticker with major holdings to compare them on this menu\n" - ) - elif len(self.etf_holdings) > 0: - self.queue = ca_controller.ComparisonAnalysisController( - self.etf_holdings, self.queue - ).menu(custom_path_menu_above="/stocks/") - - @log_start_end(log=logger) - def call_disc(self, _): - """Process disc command""" - self.queue = self.load_class(disc_controller.DiscoveryController, self.queue) - - @log_start_end(log=logger) - def call_compare(self, other_args): - """Process compare command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="compare", - description="Compare selected ETFs [Source: StockAnalysis]", - ) - parser.add_argument( - "-e", - "--etfs", - type=str, - dest="names", - help="Symbols to compare", - required="-h" not in other_args, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-e") - - # to allow inputs with spaces in between them instead of commas (inputs with spaces are not parsed correctly) - - if len(other_args) > 2: - for i in range(2, len(other_args)): - other_args[1] += "," + other_args[i] - del other_args[2 : len(other_args)] - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - etf_list = ns_parser.names.upper().split(",") - stockanalysis_view.view_comparisons( - etf_list, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_holding_perf(self, other_args: List[str]): - """Process holdings performance command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="holding_perf", - description="Look at ETF company holdings' performance", - ) - parser.add_argument( - "-s", - "--start-date", - type=valid_date, - default=(datetime.now().date() - timedelta(days=366)), - dest="start", - help="The starting date (format YYYY-MM-DD) to get each holding's price", - ) - parser.add_argument( - "-e", - "--end-date", - type=valid_date, - default=datetime.now().date(), - dest="end", - help="The ending date (format YYYY-MM-DD) to get each holding's price", - ) - parser.add_argument( - "-l", - "--limit", - type=check_positive, - dest="limit", - help="Number of holdings to get", - default=20, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ) - if ns_parser: - if self.etf_name: - fmp_view.view_etf_holdings_performance( - ticker=self.etf_name, - start_date=ns_parser.start, - end_date=ns_parser.end, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=ns_parser.sheet_name, - raw=ns_parser.raw, - ) - else: - console.print("Please load a ticker using . \n") diff --git a/openbb_terminal/etf/etf_helper.py b/openbb_terminal/etf/etf_helper.py deleted file mode 100644 index 736bfc387c4..00000000000 --- a/openbb_terminal/etf/etf_helper.py +++ /dev/null @@ -1,25 +0,0 @@ -"""ETF Helper module.""" - -__docformat__ = "numpy" - -import yfinance as yf - - -def get_quote_type(symbol: str) -> str: - """Get the quote type of the symbol. - - Parameters - ---------- - symbol : str - Ticker to check. - - Returns - ------- - str - Returns the quote type of the symbol. - """ - try: - quote_type = yf.Ticker(symbol).info["quoteType"] - return quote_type - except TypeError: - return "N/A" diff --git a/openbb_terminal/etf/etfs.csv b/openbb_terminal/etf/etfs.csv deleted file mode 100644 index 814ef2481c1..00000000000 --- a/openbb_terminal/etf/etfs.csv +++ /dev/null @@ -1,2987 +0,0 @@ -s,n,i,m -AAA,AAF First Priority CLO Bond ETF,Fixed Income,7311400.0 -AAAU,Goldman Sachs Physical Gold ETF,Commodity,410168000.0 -AADR,AdvisorShares Dorsey Wright ADR ETF,Equity,38844300.0 -AAPB,GraniteShares 1.75x Long AAPL Daily ETF,Equity,611100.0 -AAPD,Direxion Daily AAPL Bear 1X Shares,Equity,10513100.0 -AAPU,Direxion Daily AAPL Bull 1.5X Shares,Equity,6366000.0 -AAXJ,iShares MSCI All Country Asia ex Japan ETF,Equity,2677168000.0 -ABEQ,Absolute Core Strategy ETF,Equity,84840000.0 -ACES,ALPS Clean Energy ETF,Equity,699903000.0 -ACIO,Aptus Collared Income Opportunity ETF,Equity,536848000.0 -ACSI,American Customer Satisfaction Core Alpha ETF,Equity,69687100.0 -ACTV,LeaderShares Activist Leaders ETF,Equity,86478700.0 -ACVF,American Conservative Values ETF,Equity,34061600.0 -ACWF,iShares Edge MSCI Multifactor Global ETF,Equity,119601400.0 -ACWI,iShares MSCI ACWI ETF,Equity,16603004800.0 -ACWV,iShares Edge MSCI Min Vol Global ETF,Equity,4595705600.0 -ACWX,iShares MSCI ACWI ex U.S. ETF,Equity,3931816000.0 -ADFI,Anfield Dynamic Fixed Income ETF,Fixed Income,16036000.0 -ADIV,SmartETFs Asia Pacific Dividend Builder ETF,Equity,3302400.0 -ADME,APTUS Drawdown Managed Equity ETF,Equity,346082400.0 -ADPV,Adaptiv Select ETF,Equity,7763300.0 -ADRE,Invesco BLDRS Emerging Markets 50 ADR Index Fund,Equity,125591500.0 -AEMB,American Century Emerging Markets Bond ETF,Fixed Income,26052100.0 -AESR,Anfield Equity Sector Rotation ETF,Equity,129798400.0 -AFIF,Anfield Universal Fixed Income ETF,Fixed Income,121026000.0 -AFK,VanEck Vectors Africa Index ETF,Equity,49211000.0 -AFLG,First Trust Active Factor Large Cap ETF,Equity,4888000.0 -AFMC,First Trust Active Factor Mid Cap ETF,Equity,1166700.0 -AFSM,First Trust Active Factor Small Cap ETF,Equity,3586900.0 -AFTY,CSOP FTSE China A50 ETF,Equity,5303200.0 -AGG,iShares Core U.S. Aggregate Bond ETF,Fixed Income,78994643200.0 -AGGH,Simplify Aggregate Bond PLUS Credit Hedge ETF,Fixed Income,2922300.0 -AGGY,WisdomTree Barclays Yield Enhanced U.S. Aggregate Bond Fund,Fixed Income,829813100.0 -AGIH,iShares Inflation Hedged U.S. Aggregate Bond ETF,Fixed Income,2424100.0 -AGNG,Global X Aging Population ETF,Equity,41174800.0 -AGOV,Gavekal Asian Government Bond ETF,Fixed Income,48027400.0 -AGOX,Adaptive Growth Opportunities ETF,Asset Allocation,164550600.0 -AGQ,ProShares Ultra Silver,Commodity,402364600.0 -AGRH,iShares Interest Rate Hedged U.S. Aggregate Bond ETF,Fixed Income,2530900.0 -AGZ,iShares Agency Bond ETF,Fixed Income,580850100.0 -AGZD,WisdomTree Barclays Interest Rate Hedged U.S. Aggregate Bond Fund,Fixed Income,334007500.0 -AHHX,Adaptive High Income ETF,Asset Allocation,35450800.0 -AHOY,Newday Ocean Health ETF,Equity,1560200.0 -AHYB,American Century Select High Yield ETF,Fixed Income,28164600.0 -AIA,iShares Asia 50 ETF,Equity,1280176000.0 -AIEQ,AI Powered Equity ETF,Equity,105557400.0 -AILG,Alpha Intelligent - Large Cap Growth ETF,Equity,1573900.0 -AILV,Alpha Intelligent - Large Cap Value ETF,Equity,493300.0 -AIQ,Global X Future Analytics Tech ETF,Equity,137628800.0 -AIRR,First Trust RBA American Industrial Renaissance ETF,Equity,209506000.0 -AIVI,WisdomTree International AI Enhanced Value Fund,Equity,79550900.0 -AIVL,WisdomTree U.S. AI Enhanced Value Fund,Equity,432862800.0 -ALTL,Pacer Lunt Large Cap Alternator ETF,Equity,827887200.0 -ALTY,Global X SuperDividend Alternatives ETF,Alternatives,36885600.0 -AMAX,RH Hedged Multi-Asset Income ETF,Asset Allocation,43303100.0 -AMER,Emles Made in America ETF,Equity,2094800.0000000002 -AMID,Argent Mid Cap ETF,Equity,26724500.0 -AMLP,Alerian MLP ETF,Equity,6831158400.0 -AMOM,QRAFT AI Enhanced U.S. Large Cap Momentum ETF,Equity,11961400.0 -AMZA,InfraCap MLP ETF,Equity,328052600.0 -AMZD,Direxion Daily AMZN Bear 1X Shares,Equity,4122600.0000000005 -AMZU,Direxion Daily AMZN Bull 1.5X Shares,Equity,2329500.0 -ANEW,ProShares MSCI Transformational Changes ETF,Equity,24480700.0 -ANGL,VanEck Vectors Fallen Angel High Yield Bond ETF,Fixed Income,2947368000.0 -AOA,iShares Core Aggressive Allocation ETF,Asset Allocation,1506865700.0 -AOK,iShares Core Conservative Allocation ETF,Asset Allocation,858102500.0 -AOM,iShares Core Moderate Allocation ETF,Asset Allocation,1435513900.0 -AOR,iShares Core Growth Allocation ETF,Asset Allocation,2125830200.0000002 -AOTG,AOT Growth and Innovation ETF,Equity,18853800.0 -APRT,AllianzIM U.S. Large Cap Buffer10 Apr ETF,Equity,19052500.0 -APRW,AllianzIM U.S. Large Cap Buffer20 Apr ETF,Equity,59390600.0 -APRZ,TrueShares Structured Outcome (April) ETF,Equity,5645900.0 -APXH,APEX Healthcare ETF,Equity,6838400.0 -AQGX,AI Quality Growth ETF,Equity,31242300.0 -AQWA,Global X Clean Water ETF,Equity,7663800.0 -ARB,AltShares Merger Arbitrage ETF,Alternatives,83338000.0 -ARCM,Arrow Reserve Capital Management ETF,Fixed Income,50658300.0 -ARGT,Global X MSCI Argentina ETF,Equity,27477000.0 -ARKF,ARK FinTech Innovation ETF,Equity,770201600.0 -ARKG,ARK Genomic Revolution ETF,Equity,2226832200.0 -ARKK,ARK Innovation ETF,Equity,7270733600.0 -ARKQ,ARK Autonomous Technology & Robotics ETF,Equity,915343900.0 -ARKW,ARK Next Generation Internet ETF,Equity,1119149200.0 -ARKX,ARK Space Exploration & Innovation ETF,Equity,275462000.0 -ARMR,Armor US Equity Index ETF,Equity,52078100.0 -ARVR,First Trust Indxx Metaverse ETF,Equity,1259500.0 -ASEA,Global X FTSE Southeast Asia ETF,Equity,39022200.0 -ASET,FlexShares Real Assets Allocation Index Fund,Equity,30764100.0 -ASHR,Xtrackers Harvest CSI 300 China A-Shares ETF,Equity,1579138800.0 -ASHS,Xtrackers Harvest CSI 500 China A-Shares Small Cap ETF,Equity,31122700.0 -ASHX,Xtrackers MSCI China A Inclusion Equity ETF,Equity,5308400.0 -ASPY,ASYMshares ASYMmetric 500 ETF,Alternatives,26571900.0 -ATFV,Alger 35 ETF,Equity,9349500.0 -AUGZ,TrueShares Structured Outcome (August) ETF,Equity,14572300.0 -AUSF,Global X Adaptive U.S. Factor ETF,Equity,170665000.0 -AVDE,Avantis International Equity ETF,Equity,1949732000.0 -AVDV,Avantis International Small Cap Value ETF,Equity,2006278200.0 -AVEM,Avantis Emerging Markets Equity ETF,Equity,1658014700.0 -AVES,Avantis Emerging Markets Value ETF,Equity,152708200.0 -AVGE,Avantis All Equity Markets ETF,Equity,19573700.0 -AVIE,Avantis Inflation Focused Equity ETF,Equity,2373300.0 -AVIG,Avantis Core Fixed Income ETF,Fixed Income,230235000.0 -AVIV,Avantis International Large Cap Value ETF,Equity,140448900.0 -AVLV,Avantis U.S. Large Cap Value ETF,Equity,597630100.0 -AVMU,Avantis Core Municipal Fixed Income ETF,Fixed Income,40941500.0 -AVRE,Avantis Real Estate ETF,Equity,175342800.0 -AVSC,Avantis U.S Small Cap Equity ETF,Equity,46702900.0 -AVSD,Avantis Responsible International Equity ETF,Equity,47367900.0 -AVSE,Avantis Responsible Emerging Markets Equity ETF,Equity,27019900.0 -AVSF,Avantis Short-Term Fixed Income ETF,Fixed Income,140133200.0 -AVSU,Avantis Responsible U.S. Equity ETF,Equity,73839600.0 -AVUS,Avantis U.S. Equity ETF,Equity,2953035000.0 -AVUV,Avantis U.S. Small Cap Value ETF,Equity,4402812400.0 -AWAY,ETFMG Travel Tech ETF,Equity,148003200.0 -AWYX,ETFMG 2x Daily Travel Tech ETF,Equity,245100.0 -AZTD,Aztlan Global Stock Selection DM SMID ETF,Equity,3034800.0 -BAB,Invesco Taxable Municipal Bond ETF,Fixed Income,1523208100.0 -BAD,B.A.D. ETF,Equity,8905200.0 -BALT,Innovator Defined Wealth Shield ETF,Equity,252278500.0 -BAPR,Innovator S&P 500 Buffer ETF (April),Equity,118946300.0 -BAR,Graniteshares Gold Trust,Commodity,874884300.0 -BATT,Amplify Advanced Battery Metals and Materials ETF,Equity,168764000.0 -BAUG,Innovator S&P 500 Buffer ETF (August),Equity,105780600.0 -BBAX,JPMorgan BetaBuilders Developed Asia ex-Japan ETF,Equity,3867906000.0 -BBC,BioShares Biotechnology Clinical Trials ETF,Equity,17500200.0 -BBCA,JPMorgan BetaBuilders Canada ETF,Equity,6029184800.0 -BBEU,JPMorgan BetaBuilders Europe ETF,Equity,3950347200.0 -BBH,VanEck Vectors Biotech ETF,Equity,503982000.0 -BBIN,JPMorgan BetaBuilders International Equity ETF,Equity,3030605800.0 -BBJP,JPMorgan BetaBuilders Japan ETF,Equity,6909129600.0 -BBLU,EA Bridgeway Blue Chip ETF,Equity,218201500.0 -BBMC,JPMorgan BetaBuilders U.S. Mid Cap Equity ETF,Equity,1439717200.0 -BBP,BioShares Biotechnology Products ETF,Equity,16835000.0 -BBRE,JPMorgan BetaBuilders MSCI US REIT ETF,Equity,993851200.0 -BBSA,JPMorgan BetaBuilders 1-5 Year U.S. Aggregate Bond ETF,Fixed Income,37604000.0 -BBSC,JPMorgan BetaBuilders U.S. Small Cap Equity ETF,Equity,500201600.0 -BBUS,JPMorgan BetaBuilders U.S. Equity ETF,Equity,1327744100.0 -BCD,Aberdeen Standard Bloomberg All Commodity Longer Dated Strategy K-1 Free ETF,Commodity,276855800.0 -BCDF,Horizon Kinetics Blockchain Development ETF,Equity,2041100.0 -BCI,Aberdeen Standard Bloomberg All Commodity Strategy K-1 ETF,Commodity,1035781600.0 -BCIM,Aberdeen Bloomberg Industrial Metals Strategy K-1 Free ETF,Commodity,29936200.0 -BDEC,Innovator S&P 500 Buffer ETF (December),Equity,109528000.0 -BDRY,Breakwave Dry Bulk Shipping ETF,Commodity,28710100.0 -BECO,BlackRock Future Climate and Sustainable Economy ETF,Equity,4122800.0 -BEDZ,AdvisorShares Hotel ETF,Equity,4748500.0 -BETZ,Roundhill Sports Betting & iGaming ETF,Equity,114154800.0 -BFEB,Innovator S&P 500 Buffer ETF (February),Equity,71202700.0 -BFIT,Global X Health & Wellness Thematic ETF,Equity,22111600.0 -BFIX,Build Bond Innovation ETF,Asset Allocation,30514600.0 -BFOR,Barron's 400 ETF,Equity,136836700.0 -BFTR,BlackRock Future Innovators ETF,Equity,7141900.0 -BGLD,FT Cboe Vest Gold Strategy Quarterly Buffer ETF,Commodity,12581500.0 -BGRN,iShares Global Green Bond ETF,Fixed Income,289250000.0 -BIB,ProShares Ultra Nasdaq Biotechnology,Equity,109594000.0 -BIBL,Inspire 100 ETF,Equity,271527000.0 -BICK,First Trust BICK Index Fund,Equity,47193500.0 -BIDS,Amplify Digital and Online Trading ETF,Equity,405400.0 -BIL,SPDR Bloomberg Barclays 1-3 Month T-Bill ETF,Fixed Income,26452729600.0 -BILS,SPDR Bloomberg Barclays 3-12 Month T-Bill ETF,Fixed Income,357887900.0 -BIS,ProShares UltraShort Nasdaq Biotechnology,Equity,5865000.0 -BITI,ProShares Short Bitcoin Strategy ETF,Currency,91612100.0 -BITO,ProShares Bitcoin Strategy ETF,Currency,550127200.0 -BITQ,Bitwise Crypto Industry Innovators ETF,Equity,44805000.0 -BITS,Blockchain & Bitcoin Strategy ETF,Asset Allocation,5857600.0 -BIV,Vanguard Intermediate-Term Bond Fund,Fixed Income,12631885600.0 -BIZD,VanEck Vectors BDC Income ETF,Equity,531380699.99999994 -BJAN,Innovator S&P 500 Buffer ETF (January),Equity,150879600.0 -BJK,VanEck Vectors Gaming ETF,Equity,75348000.0 -BJUL,Innovator S&P 500 Buffer ETF (July),Equity,176257700.0 -BJUN,Innovator S&P 500 Buffer ETF (June),Equity,78834300.0 -BKAG,BNY Mellon Core Bond ETF,Fixed Income,384208500.0 -BKCH,Global X Blockchain ETF,Equity,45223000.0 -BKCI,BNY Mellon Concentrated International ETF,Equity,62852700.0 -BKEM,BNY Mellon Emerging Markets Equity ETF,Equity,28870000.0 -BKES,BNY Mellon Sustainable Global Emerging Markets ETF,Equity,9761600.0 -BKF,iShares MSCI BRIC ETF,Equity,76619900.0 -BKGI,BNY Mellon Global Infrastructure Income ETF,Equity,0.0 -BKHY,BNY Mellon High Yield Beta ETF,Fixed Income,53912000.0 -BKIE,BNY Mellon International Equity ETF,Equity,141649000.0 -BKIS,BNY Mellon Sustainable International Equity ETF,Equity,7964600.0 -BKLC,BNY Mellon US Large Cap Core Equity ETF,Equity,454915500.0 -BKLN,Invesco Senior Loan ETF,Fixed Income,3707160000.0 -BKMC,BNY Mellon US Mid Cap Core Equity ETF,Equity,90821600.0 -BKSB,BNY Mellon Short Duration Corporate Bond ETF,Fixed Income,53728000.0 -BKSE,BNY Mellon US Small Cap Core Equity ETF,Equity,36809100.0 -BKUI,BNY Mellon Ultra Short Income ETF,Fixed Income,26867400.0 -BKUS,BNY Mellon Sustainable US Equity ETF,Equity,8225700.000000001 -BLCN,Reality Shares Nasdaq NexGen Economy ETF,Equity,107733600.0 -BLDG,Cambria Global Real Estate ETF,Equity,20745800.0 -BLES,Inspire Global Hope ETF,Equity,130503000.0 -BLHY,Virtus Newfleet Dynamic Credit ETF,Fixed Income,4156300.0 -BLKC,Invesco Alerian Galaxy Blockchain Users and Decentralized Commerce ETF,Equity,0.0 -BLLD,JPMorgan Sustainable Infrastructure ETF,Equity,10364900.0 -BLOK,Amplify Transformational Data Sharing ETF,Equity,444758000.0 -BLV,Vanguard Long-Term Bond ETF,Fixed Income,4225352000.0 -BMAR,Innovator S&P 500 Buffer ETF (March),Equity,80286500.0 -BMAY,Innovator S&P 500 Buffer ETF (May),Equity,180936800.0 -BMED,BlackRock Future Health ETF,Equity,5548100.0 -BND,Vanguard Total Bond Market ETF,Fixed Income,81290406400.0 -BNDC,FlexShares Core Select Bond Fund,Fixed Income,164602700.0 -BNDD,Quadratic Deflation ETF,Fixed Income,57375000.0 -BNDI,NEOS Enhanced Income Aggregate Bond ETF,Fixed Income,964400.0 -BNDW,Vanguard Total World Bond ETF,Fixed Income,557500800.0 -BNDX,Vanguard Total International Bond ETF,Fixed Income,45032838400.0 -BNE,Blue Horizon BNE ETF,Equity,6925800.0 -BNGE,First Trust S-Network Streaming and Gaming ETF,Equity,3553100.0 -BNO,United States Brent Oil Fund,Commodity,238476900.0 -BNOV,Innovator S&P 500 Buffer ETF (November),Equity,64001600.0 -BOAT,SonicShares Global Shipping ETF,Equity,21257100.0 -BOB,Merlyn.AI Best-of-Breed Core Momentum ETF,Asset Allocation,6774500.0 -BOCT,Innovator S&P 500 Buffer ETF (October),Equity,231725000.0 -BOIL,ProShares Ultra Bloomberg Natural Gas,Commodity,667602700.0 -BOND,PIMCO Total Return Active ETF,Fixed Income,2985290800.0 -BOSS,Global X Founder-Run Companies ETF,Equity,8277500.0 -BOTZ,Global X Robotics & Artificial Intelligence ETF,Equity,1357238400.0 -BOUT,Innovator IBD Breakout Opportunities ETF,Equity,14001600.0 -BPAY,BlackRock Future Financial and Technology ETF,Equity,4274700.0 -BRF,VanEck Vectors Brazil Small-Cap ETF,Equity,24926100.0 -BRKY,Direxion Breakfast Commodities Strategy ETF,Commodity,6254200.0 -BRLN,BlackRock Floating Rate Loan ETF,Fixed Income,0.0 -BRNY,Burney U.S. Factor Rotation ETF,Equity,142319600.0 -BRZU,Direxion Daily MSCI Brazil Bull 2X Shares,Equity,112154900.0 -BSBE,Invesco BulletShares 2022 USD Emerging Markets Debt ETF,Fixed Income,16758000.0 -BSCE,Invesco BulletShares 2023 USD Emerging Markets Debt ETF,Fixed Income,25663000.0 -BSCM,Invesco BulletShares 2022 Corporate Bond ETF,Fixed Income,1680624000.0 -BSCN,Invesco BulletShares 2023 Corporate Bond ETF,Fixed Income,2673576000.0 -BSCO,Invesco BulletShares 2024 Corporate Bond ETF,Fixed Income,3383452800.0 -BSCP,Invesco BulletShares 2025 Corporate Bond ETF,Fixed Income,1736212400.0 -BSCQ,Invesco BulletShares 2026 Corporate Bond ETF,Fixed Income,1291175900.0 -BSCR,Invesco BulletShares 2027 Corporate Bond ETF,Fixed Income,590415800.0 -BSCS,Invesco BulletShares 2028 Corporate Bond ETF,Fixed Income,380070800.0 -BSCT,Invesco BulletShares 2029 Corporate Bond ETF,Fixed Income,169249500.0 -BSCU,Invesco BulletShares 2030 Corporate Bond ETF,Fixed Income,126126000.0 -BSCV,Invesco BulletShares 2031 Corporate Bond ETF,Fixed Income,59887500.0 -BSCW,Invesco BulletShares 2032 Corporate Bond ETF,Fixed Income,6043500.0 -BSDE,Invesco BulletShares 2024 USD Emerging Markets Debt ETF,Fixed Income,38424000.0 -BSEA,ETFMG Breakwave Sea Decarbonization Tech ETF,Equity,2186600.0 -BSEP,Innovator S&P 500 Buffer ETF (September),Equity,171484500.0 -BSJM,Invesco BulletShares 2022 High Yield Corporate Bond ETF,Fixed Income,507840000.0 -BSJN,Invesco BulletShares 2023 High Yield Corporate Bond ETF,Fixed Income,715530000.0 -BSJO,Invesco BulletShares 2024 High Yield Corporate Bond ETF,Fixed Income,487230000.0 -BSJP,Invesco BulletShares 2025 High Yield Corporate Bond ETF,Fixed Income,411792000.0 -BSJQ,Invesco BulletShares 2026 High Yield Corporate Bond ETF,Fixed Income,200816000.0 -BSJR,Invesco BulletShares 2027 High Yield Corporate Bond ETF,Fixed Income,51840000.0 -BSJS,Invesco BulletShares 2028 High Yield Corporate Bond ETF,Fixed Income,25248000.0 -BSJT,Invesco BulletShares 2029 High Yield Corporate Bond ETF,Fixed Income,10152500.0 -BSJU,Invesco BulletShares 2030 High Yield Corporate Bond ETF,Fixed Income,4960000.0 -BSMM,Invesco BulletShares Municipal Bond 2022 Index,Fixed Income,80928000.0 -BSMN,Invesco BulletShares 2023 Municipal Bond ETF,Fixed Income,110396000.0 -BSMO,Invesco BulletShares 2024 Municipal Bond ETF,Fixed Income,113666000.0 -BSMP,Invesco BulletShares 2025 Municipal Bond ETF,Fixed Income,97080000.0 -BSMQ,Invesco BulletShares 2026 Municipal Bond ETF,Fixed Income,56211600.0 -BSMR,Invesco BulletShares 2027 Municipal Bond ETF,Fixed Income,37384000.0 -BSMS,Invesco BulletShares 2028 Municipal Bond ETF,Fixed Income,27720000.0 -BSMT,Invesco BulletShares Municipal Bond 2029 Index,Fixed Income,22730700.0 -BSMU,Invesco BulletShares 2030 Municipal Bond ETF,Fixed Income,27970500.0 -BSMV,Invesco BulletShares 2031 Municipal Bond ETF,Fixed Income,12387100.0 -BSTP,Innovator Buffer Step-Up Strategy ETF,Equity,29844700.0 -BSV,Vanguard Short-Term Bond ETF,Fixed Income,38891452800.0 -BTAL,AGFiQ US Market Neutral Anti-Beta Fund,Alternatives,355198900.0 -BTCR,Volt Crypto Industry Revolution and Tech ETF,Equity,1992600.0 -BTEC,Principal Healthcare Innovators Index ETF,Equity,57158300.0 -BTEK,BlackRock Future Tech ETF,Equity,11076400.0 -BTF,Valkyrie Bitcoin Strategy ETF,Currency,17948000.0 -BTHM,BlackRock Future U.S. Themes ETF,Equity,4381500.0 -BUCK,Simplify Stable Income ETF,Fixed Income,1254400.0 -BUFB,Innovator Laddered Allocation Buffer ETF,Equity,27305100.0 -BUFD,FT Cboe Vest Fund of Deep Buffer ETF,Equity,542352500.0 -BUFF,Innovator Laddered Fund of S&P 500 Power Buffer ETF,Equity,152250000.0 -BUFG,FT Cboe Vest Buffered Allocation Growth ETF,Equity,161875000.0 -BUFQ,FT Cboe Vest Fund of Nasdaq-100 Buffer ETF,Equity,0.0 -BUFR,FT Cboe Vest Fund of Buffer ETFs,Equity,749612500.0 -BUFT,FT Cboe Vest Buffered Allocation Defensive ETF,Equity,164675000.0 -BUG,Global X Cybersecurity ETF,Equity,1024395000.0 -BUL,Pacer US Cash Cows Growth ETF,Equity,22407000.0 -BULD,Pacer BlueStar Engineering the Future ETF,Equity,1442000.0 -BUYW,Main BuyWrite ETF,Equity,157356000.0 -BUYZ,Franklin Disruptive Commerce ETF,Equity,8813600.0 -BUZZ,VanEck Vectors Social Sentiment ETF,Equity,63762700.0 -BWEB,Bitwise Web3 ETF,Equity,1934400.0 -BWX,SPDR Bloomberg Barclays International Treasury Bond ETF,Fixed Income,905135400.0 -BWZ,SPDR Bloomberg Barclays Short Term International Treasury Bond ETF,Fixed Income,140328200.0 -BYLD,iShares Yield Optimized Bond ETF,Fixed Income,79594100.0 -BYOB,SoFi Be Your Own Boss ETF,Equity,7320500.0 -BYRE,Principal Real Estate Active Opportunities ETF,Equity,5113400.0 -BYTE,Roundhill IO Digital Infrastructure ETF,Equity,1702500.0 -BZQ,ProShares UltraShort MSCI Brazil Capped,Equity,17836100.0 -CACG,ClearBridge All Cap Growth ETF,Equity,145431000.0 -CALF,Pacer US Small Cap Cash Cows 100 ETF,Equity,1254757500.0 -CANE,Teucrium Sugar Fund,Commodity,27009800.0 -CAPE,DoubleLine Shiller CAPE U.S. Equities ETF,Equity,191000000.0 -CARY,Angel Oak Income ETF,Fixed Income,20070100.0 -CARZ,First Trust Nasdaq Global Auto Index Fund,Equity,45360000.0 -CATH,Global X S&P 500 Catholic Values ETF,Equity,572338800.0 -CBLS,Changebridge Capital Long\u002FShort Equity ETF,Alternatives,5152500.0 -CBON,VanEck Vectors ChinaAMC China Bond ETF,Fixed Income,85567600.0 -CBSE,Changebridge Capital Sustainable Equity ETF,Equity,6207800.0 -CCOR,Cambria Core Equity ETF,Alternatives,552712700.0 -CCRV,iShares Commodity Curve Carry Strategy ETF,Commodity,36823800.0 -CCSO,Carbon Collective Climate Solutions U.S. Equity ETF,Equity,7193400.0 -CDC,VictoryShares US EQ Income Enhanced Volatility Wtd ETF,Equity,2099440000.0 -CDL,VictoryShares US Large Cap High Div Volatility Wtd ETF,Equity,342720000.0 -CDX,Simplify High Yield PLUS Credit Hedge ETF,Fixed Income,53266300.0 -CEFA,Global X S&P Catholic Values Developed ex-U.S. ETF,Equity,6271600.0 -CEFS,Saba Closed-End Funds ETF,Asset Allocation,91102000.0 -CEMB,iShares J.P. Morgan Emerging Markets Corporate Bond ETF,Fixed Income,299052000.0 -CEW,WisdomTree Emerging Currency Strategy Fund,Currency,8239600.0 -CEY,VictoryShares Emerging Market High Div Volatility Wtd ETF,Equity,8980000.0 -CFA,VictoryShares US 500 Volatility Wtd ETF,Equity,585418500.0 -CFCV,ClearBridge Focus Value ETF,Equity,3520700.0 -CFO,VictoryShares US 500 Enhanced Volatility Wtd ETF,Equity,897350000.0 -CGCP,Capital Group Core Plus Income ETF,Fixed Income,308170600.0 -CGDV,Capital Group Dividend Value ETF,Equity,941203500.0 -CGGO,Capital Group Global Growth Equity ETF,Equity,739218100.0 -CGGR,Capital Group Growth ETF,Equity,856543900.0 -CGMS,Capital Group U.S. Multi-Sector Income ETF,Fixed Income,55887800.0 -CGMU,Capital Group Municipal Income ETF,Fixed Income,35808900.0 -CGSD,Capital Group Short Duration Income ETF,Fixed Income,33284100.0 -CGUS,Capital Group Core Equity ETF,Equity,418196000.0 -CGV,Conductor Global Equity Value ETF,Equity,69642000.0 -CGW,Invesco S&P Global Water Index ETF,Equity,1009086000.0 -CGXU,Capital Group International Focus Equity ETF,Equity,649710600.0 -CHAD,Direxion Daily CSI 300 China A Share Bear 1X Shares,Equity,70015400.0 -CHAU,Direxion Daily CSI 300 China A Share Bull 2X Shares,Equity,47942700.0 -CHB,China Biotech Innovation ETF,Equity,2486700.0 -CHGX,Change Finance Diversified Impact US Large Cap Fossil Fuel Free ETF,Equity,102800900.0 -CHIC,Global X MSCI China Communication Services ETF,Equity,6120000.0 -CHIE,Global X China Energy ETF,Equity,10009800.0 -CHIH,Global X MSCI China Health Care ETF,Equity,10987200.0 -CHII,Global X China Industrials ETF,Equity,5625000.0 -CHIK,Global X MSCI China Information Technology ETF,Equity,15284400.0 -CHIM,Global X China Materials ETF,Equity,3446000.0 -CHIQ,Global X China Consumer ETF,Equity,260839200.0 -CHIR,Global X MSCI China Real Estate ETF,Equity,5942100.0 -CHIS,Global X MSCI Consumer Staples ETF,Equity,9915000.0 -CHIU,Global X MSCI China Utilities ETF,Equity,2774000.0 -CHIX,Global X China Financials ETF,Equity,31922400.0 -CHNA,Loncar China BioPharma ETF,Equity,5170400.0 -CIBR,First Trust Nasdaq Cybersecurity ETF,Equity,5019247600.0 -CID,VictoryShares International High Div Volatility Wtd ETF,Equity,15972000.0 -CIL,VictoryShares International Volatility Wtd ETF,Equity,87185000.0 -CIRC,JPMorgan Sustainable Consumption ETF,Equity,11171600.0 -CIZ,VictoryShares Developed Enhanced Volatility Wtd ETF,Equity,45051500.0 -CLDL,Direxion Daily Cloud Computing Bull 2X Shares,Equity,7017800.0 -CLIX,ProShares Long Online\u002FShort Stores ETF,Equity,13355100.0 -CLNR,IQ Cleaner Transport ETF,Equity,4840000.0 -CLOI,VanEck CLO ETF,Fixed Income,25149200.0 -CLOU,Global X Cloud Computing ETF,Equity,579012900.0 -CLRG,IQ Chaikin U.S. Large Cap ETF,Equity,288360000.0 -CLSA,Cabana Target Leading Sector Aggressive ETF,Asset Allocation,61637600.0 -CLSC,Cabana Target Leading Sector Conservative ETF,Asset Allocation,299441600.0 -CLSE,Convergence Long\u002FShort Equity ETF,Alternatives,24423800.0 -CLSM,Cabana Target Leading Sector Moderate ETF,Asset Allocation,34549000.0 -CLTL,Invesco Treasury Collateral ETF,Fixed Income,977753900.0 -CMBS,iShares CMBS ETF,Fixed Income,597104300.0 -CMDY,iShares Bloomberg Roll Select Commodity Strategy ETF,Commodity,318873500.0 -CMF,iShares California Muni Bond ETF,Fixed Income,1741721400.0 -CN,Xtrackers MSCI All China Equity ETF,Equity,8112200.0 -CNBS,Amplify Seymour Cannabis ETF,Equity,43849500.0 -CNCR,Loncar Cancer Immunotherapy ETF,Equity,19019000.0 -CNRG,SPDR Kensho Clean Power ETF,Equity,357206800.0 -CNXT,VanEck Vectors ChinaAMC SME-ChiNext ETF,Equity,19660400.0 -CNYA,iShares MSCI China A ETF,Equity,461929700.0 -COM,Direxion Auspice Broad Commodity Strategy ETF,Commodity,72251200.0 -COMB,GraniteShares Bloomberg Commodity Broad Strategy No K-1 ETF,Commodity,285914500.0 -COMT,iShares Commodities Select Strategy ETF,Commodity,2305754000.0 -CONL,GraniteShares 1.5x Long Coinbase Daily ETF,Equity,688000.0 -COPX,Global X Copper Miners ETF,Equity,1578494400.0 -CORN,Teucrium Corn Fund,Commodity,204879500.0 -CORP,PIMCO Investment Grade Corporate Bond Index ETF,Fixed Income,731069100.0 -COWZ,Pacer US Cash Cows 100 ETF,Equity,9133012800.0 -CPER,United States Copper Index Fund,Commodity,169231300.0 -CPI,IQ Real Return ETF,Alternatives,10082000.0 -CPII,Ionic Inflation Protection ETF,Fixed Income,9860400.0 -CQQQ,Invesco China Technology ETF,Equity,825139000.0 -CRAK,VanEck Vectors Oil Refiners ETF,Equity,41382900.0 -CRBN,iShares MSCI ACWI Low Carbon Target ETF,Equity,855138600.0 -CRIT,Optica Rare Earths & Critical Materials ETF,Equity,2353200.0 -CRPT,First Trust SkyBridge Crypto Industry and Digital Economy ETF,Equity,15235400.0 -CRUZ,"Defiance Hotel, Airline, and Cruise ETF",Equity,39224200.0 -CRYP,AdvisorShares Managed Bitcoin Strategy ETF,Asset Allocation,152000.0 -CSA,VictoryShares US Small Cap Volatility Wtd ETF,Equity,24538000.0 -CSB,VictoryShares US Small Cap High Div Volatility Wtd ETF,Equity,356647500.0 -CSD,Invesco S&P Spin-Off ETF,Equity,56874100.0 -CSF,VictoryShares US Discovery Enhanced Volatility Wtd ETF,Equity,87150000.0 -CSH,Exos Active SPAC Arbitrage ETF,Equity,8818100.0 -CSHI,NEOS Enhanced Income Cash Alternative ETF,Fixed Income,999700.0 -CSM,ProShares Large Cap Core Plus,Equity,442076900.0 -CSML,IQ Chaikin U.S. Small Cap ETF,Equity,245471300.0 -CTA,Simplify Managed Futures Strategy ETF,Alternatives,213940000.0 -CTEC,Global X CleanTech ETF,Equity,125168800.0 -CTEX,ProShares S&P Kensho Cleantech ETF,Equity,6096100.0 -CUBS,Asian Growth Cubs ETF,Equity,9869000.0 -CURE,Direxion Daily Healthcare Bull 3x Shares,Equity,209895800.0 -CUT,Invesco MSCI Global Timber ETF,Equity,65640800.0 -CVAR,Cultivar ETF,Equity,24865200.0 -CVY,Invesco Zacks Multi-Asset Income ETF,Asset Allocation,108552100.0 -CWB,SPDR Bloomberg Barclays Convertible Securities ETF,Fixed Income,4301163600.0 -CWEB,Direxion Daily CSI China Internet Index Bull 2X Shares,Equity,259569600.0 -CWI,SPDR MSCI ACWI ex-US ETF,Equity,1530992200.0 -CWS,AdvisorShares Focused Equity ETF,Equity,31866600.0 -CXSE,WisdomTree China ex-State-Owned Enterprises Fund,Equity,701933500.0 -CYA,Simplify Tail Risk Strategy ETF,Asset Allocation,52519000.0 -CYB,WisdomTree Chinese Yuan Strategy Fund,Currency,26962700.0 -CZA,Invesco Zacks Mid-Cap ETF,Equity,215168000.0 -DALI,First Trust Dorsey Wright DALI 1 ETF,Asset Allocation,150356500.0 -DALT,Anfield Diversified Alternatives ETF,Alternatives,57837800.0 -DAM,VanEck Digital Assets Mining ETF,Equity,813500.0 -DAPP,VanEck Vectors Digital Transformation ETF,Equity,21343500.0 -DAPR,FT Cboe Vest U.S. Equity Deep Buffer ETF (April),Equity,274317000.0 -DAT,ProShares Big Data Refiners ETF,Equity,1747400.0 -DAUG,FT Cboe Vest U.S. Equity Deep Buffer ETF (August),Equity,284597500.0 -DAX,Horizons DAX Germany ETF,Equity,44980000.0 -DBA,Invesco DB Agriculture Fund,Commodity,1412772100.0 -DBAW,Deutsche X-trackers MSCI All World ex US Hedged Equity ETF,Equity,141241600.0 -DBB,Invesco DB Base Metals Fund,Commodity,253570200.0 -DBC,Invesco DB Commodity Index Tracking Fund,Commodity,3045062000.0 -DBE,Invesco DB Energy Fund,Commodity,192211400.0 -DBEF,Deutsche X-trackers MSCI EAFE Hedged Equity ETF,Equity,4328283200.0 -DBEH,iM DBi Hedge Strategy ETF,Alternatives,15870000.0 -DBEM,Deutsche X-trackers MSCI Emerging Markets Hedged Equity ETF,Equity,90432300.0 -DBEU,Deutsche X-trackers MSCI Europe Hedged Equity ETF,Equity,495896600.0 -DBEZ,Deutsche X-trackers MSCI Eurozone Hedged Equity ETF,Equity,10881400.0 -DBGR,Deutsche X-trackers MSCI Germany Hedged Equity ETF,Equity,8404900.0 -DBJA,Innovator Double Stacker 9 Buffer ETF (January),Equity,8364400.0 -DBJP,Deutsche X-trackers MSCI Japan Hedged Equity ETF,Equity,211631400.0 -DBMF,iM DBi Managed Futures Strategy ETF,Alternatives,1009926400.0 -DBND,DoubleLine Opportunistic Bond ETF,Fixed Income,51000000.0 -DBO,Invesco DB Oil Fund,Commodity,336954300.0 -DBOC,Innovator Double Stacker 9 Buffer ETF (October),Equity,14605500.0 -DBP,Invesco DB Precious Metals Fund,Commodity,81683600.0 -DBS,Invesco DB Silver Fund,Commodity,20114700.0 -DBV,Invesco DB G10 Currency Harvest Fund,Alternatives,42328000.0 -DDEC,FT Cboe Vest U.S. Equity Deep Buffer ETF (December),Equity,272840800.0 -DDIV,First Trust Dorsey Wright Momentum & Dividend ETF,Equity,71713300.0 -DDLS,WisdomTree Dynamic Currency Hedged International SmallCap Equity Fund,Equity,103582000.0 -DDM,ProShares Ultra Dow30,Equity,380235500.0 -DDWM,WisdomTree Dynamic Currency Hedged International Equity Fund,Equity,187960000.0 -DECZ,TrueShares Structured Outcome (December) ETF,Equity,6302400.0 -DEED,First Trust TCW Securitized Plus ETF,Fixed Income,127290000.0 -DEEF,Deutsche X-trackers FTSE Developed Ex US Comprehensive Factor ETF,Equity,63041000.0 -DEEP,Roundhill Acquirers Deep Value ETF,Equity,41624400.0 -DEF,Invesco Defensive Equity ETF,Equity,285531300.0 -DEFI,Hashdex Bitcoin Futures ETF,Currency,2010100.0 -DEHP,Dimensional Emerging Markets High Profitability ETF,Equity,78810100.0 -DEIF,Sterling Capital Diverse Multi-Manager Active ETF,Equity,94721000.0 -DEM,WisdomTree Emerging Markets High Dividend Fund,Equity,1661711700.0 -DEMZ,Demz Political Contributions ETF,Equity,21564500.0 -DES,WisdomTree SmallCap Dividend Fund,Equity,1898374800.0 -DEUS,Deutsche X-trackers Russell 1000 Comprehensive Factor ETF,Equity,142429000.0 -DEW,WisdomTree Global Equity Income Fund,Equity,118064800.0 -DFAC,Dimensional U.S. Core Equity 2 ETF,Equity,16079992000.0 -DFAE,Dimensional Emerging Core Equity Market ETF,Equity,1674846800.0 -DFAI,Dimensional International Core Equity Market ETF,Equity,2454824000.0 -DFAR,Dimensional US Real Estate ETF,Equity,296180200.0 -DFAS,Dimensional U.S. Small Cap ETF,Equity,4835550800.0 -DFAT,Dimensional U.S. Targeted Value ETF,Equity,7498751200.0 -DFAU,Dimensional US Core Equity Market ETF,Equity,2646863600.0 -DFAX,Dimensional World ex U.S. Core Equity 2 ETF,Equity,4704396800.0 -DFCF,Core Fixed Income ETF,Fixed Income,1391817900.0 -DFE,WisdomTree Europe SmallCap Dividend Fund,Equity,189368200.0 -DFEB,FT Cboe Vest U.S. Equity Deep Buffer ETF (February),Equity,303476600.0 -DFEM,Dimensional Emerging Markets Core Equity 2 ETF,Equity,719769500.0 -DFEN,Direxion Daily Aerospace & Defense Bull 3X Shares,Equity,209646800.0 -DFEV,Dimensional Emerging Markets Value ETF,Equity,173598500.0 -DFHY,TrimTabs Donoghue Forlines Tactical High Yield ETF,Fixed Income,37488600.0 -DFIC,Dimensional International Core Equity 2 ETF,Equity,1556451000.0 -DFIP,Dimensional Inflation-Protected Securities ETF,Fixed Income,265365800.0 -DFIS,Dimensional International Small Cap ETF,Equity,360220000.0 -DFIV,Dimensional International Value ETF,Equity,4062322400.0 -DFJ,WisdomTree Japan SmallCap Dividend Fund,Equity,155974600.0 -DFND,Reality Shares DIVCON Dividend Defender ETF,Equity,18550100.0 -DFNL,Davis Select Financial ETF,Equity,182067800.0 -DFNM,Dimensional National Municipal Bond ETF,Fixed Income,597408800.0 -DFNV,TrimTabs Donoghue Forlines Risk Managed Innovation ETF,Equity,60035800.0 -DFRA,Donoghue Forlines Yield Enhanced Real Asset ETF,Equity,30893000.0 -DFSB,Global Sustainability Fixed Income ETF,Fixed Income,35497500.0 -DFSD,Dimensional Short-Duration Fixed Income ETF,Fixed Income,809258600.0 -DFSE,Emerging Markets Sustainability Core 1 ETF,Equity,76280100.0 -DFSI,International Sustainability Core 1 ETF,Equity,76842900.0 -DFSU,US Sustainability Core 1 ETF,Equity,92014900.0 -DFSV,Dimensional US Small Cap Value ETF,Equity,711732900.0 -DFUS,Dimensional U.S. Equity ETF,Equity,5656852000.0 -DFUV,Dimensional US Marketwide Value ETF,Equity,8259858400.0 -DGIN,VanEck Digital India ETF,Equity,1466500.0 -DGL,Invesco DB Gold Fund,Commodity,63976900.0 -DGRE,WisdomTree Emerging Markets Quality Dividend Growth Fund,Equity,71858700.0 -DGRO,iShares Core Dividend Growth ETF,Equity,24966656000.0 -DGRS,WisdomTree U.S. SmallCap Quality Dividend Growth Fund,Equity,215337100.0 -DGRW,WisdomTree U.S. Quality Dividend Growth Fund,Equity,7514904000.0 -DGS,WisdomTree Emerging Markets SmallCap Dividend Fund,Equity,2327333400.0 -DGT,SPDR Global Dow ETF,Equity,101823500.0 -DHS,WisdomTree High Dividend Fund,Equity,1354923000.0 -DIA,SPDR Dow Jones Industrial Average ETF Trust,Equity,30374643200.0 -DIAL,Columbia Diversified Fixed Income Allocation ETF,Fixed Income,605151100.0 -DIEM,Franklin Emerging Market Core Dividend Tilt Index ETF,Equity,13432900.0 -DIG,ProShares Ultra Oil & Gas,Equity,184314100.0 -DIHP,Dimensional International High Profitability ETF,Equity,513912000.0 -DIM,WisdomTree International MidCap Dividend Fund,Equity,125594700.0 -DINT,Davis Select International ETF,Equity,134946000.0 -DISV,Dimensional International Small Cap Value ETF,Equity,525742000.0 -DIV,Global X SuperDividend U.S. ETF,Equity,697482500.0 -DIVB,iShares U.S. Dividend and Buyback ETF,Equity,250593900.0 -DIVD,Altrius Global Dividend ETF,Equity,871900.0 -DIVI,Franklin International Core Dividend Tilt Index ETF,Equity,44942700.0 -DIVO,Amplify CWP Dividend & Option Income ETF,Equity,2143984000.0 -DIVS,SmartETFs Dividend Builder ETF,Equity,22058500.0 -DIVZ,TrueShares Low Volatility Equity Income ETF,Equity,77781200.0 -DJAN,FT Cboe Vest U.S. Equity Deep Buffer ETF (January),Equity,170386100.0 -DJD,Invesco Dow Jones Industrial Average Dividend ETF,Equity,267023099.99999997 -DJIA,Global X Dow 30 Covered Call ETF,Equity,51329900.0 -DJUL,FT Cboe Vest U.S. Equity Deep Buffer ETF (July),Equity,179607000.0 -DJUN,FT Cboe Vest U.S. Equity Deep Buffer ETF (June),Equity,183928100.0 -DLN,WisdomTree LargeCap Dividend Fund,Equity,3797204400.0 -DLS,WisdomTree International SmallCap Dividend Fund,Equity,1112087500.0 -DMAR,FT Cboe Vest U.S. Equity Deep Buffer ETF - March,Equity,215705700.0 -DMAT,Global X Disruptive Materials ETF,Equity,3955800.0 -DMAY,FT Cboe Vest U.S. Equity Deep Buffer ETF (May),Equity,234979500.0 -DMCY,Democracy International Fund,Equity,4428000.0 -DMDV,AAM S&P Emerging Markets High Dividend Value ETF,Equity,2046000.0 -DMXF,iShares ESG Advanced MSCI EAFE ETF,Equity,401208000.0 -DNL,WisdomTree Global ex-US Quality Dividend Growth Fund,Equity,433526900.0 -DNOV,FT Cboe Vest U.S. Equity Deep Buffer ETF (November),Equity,341170500.0 -DOCT,FT Cboe Vest U.S. Equity Deep Buffer ETF (October),Equity,198812500.0 -DOG,ProShares Short Dow30,Equity,273963900.0 -DOL,WisdomTree International LargeCap Dividend Fund,Equity,424918200.0 -DON,WisdomTree MidCap Dividend Fund,Equity,3301615600.0 -DOZR,Direxion Daily US Infrastructure Bull 2X Shares ETF,Equity,2929100.0 -DPST,Direxion Daily Regional Banks Bull 3X Shares,Equity,208889800.0 -DRIP,Direxion Daily S&P Oil & Gas Exp. & Prod. Bear 2X Shares,Equity,97992700.0 -DRIV,Global X Autonomous and Electric Vehicles ETF,Equity,885852800.0 -DRLL,Strive U.S. Energy ETF,Equity,387026300.0 -DRN,Direxion Daily MSCI Real Estate Bull 3X Shares,Equity,50977200.0 -DRSK,Aptus Defined Risk ETF,Asset Allocation,828480000.0 -DRV,Direxion Daily MSCI Real Estate Bear 3X Shares,Equity,165060100.0 -DSCF,Discipline Fund ETF,Asset Allocation,32772100.0 -DSEP,FT Cboe Vest U.S. Equity Deep Buffer ETF (September),Equity,242892000.0 -DSI,iShares MSCI KLD 400 Social ETF,Equity,3720010400.0 -DSJA,Innovator Double Stacker ETF (January),Equity,3278800.0 -DSMC,Distillate Small\u002FMid Cash Flow ETF,Equity,10746700.0 -DSOC,Innovator Double Stacker ETF (October),Equity,4161800.0 -DSPC,AXS De-SPAC ETF,Equity,619100.0 -DSTL,Distillate U.S. Fundamental Stability & Value ETF,Equity,857598300.0 -DSTX,Distillate International Fundamental Stability & Value ETF,Equity,14245600.0 -DTD,WisdomTree Total Dividend Fund,Equity,1111747600.0 -DTEC,ALPS Disruptive Technologies ETF,Equity,120145200.0 -DTH,WisdomTree International High Dividend Fund,Equity,230584900.0 -DTRE,First Trust Alerian Disruptive Technology Real Estate ETF,Equity,26373100.0 -DUDE,Merlyn.AI SectorSurfer Momentum ETF,Asset Allocation,54132000.0 -DUG,ProShares UltraShort Oil & Gas,Equity,28386900.0 -DUHP,Dimensional US High Profitability ETF,Equity,1145975500.0 -DURA,VanEck Vectors Morningstar Durable Dividend ETF,Equity,86051400.0 -DUSA,Davis Select U.S. Equity ETF,Equity,340412500.0 -DUSL,Direxion Daily Industrials Bull 3X Shares,Equity,22777500.0 -DUST,Direxion Daily Gold Miners Bear 2X Shares,Equity,61118500.0 -DVAL,BrandywineGLOBAL - Dynamic US Large Cap Value ETF,Equity,183961400.0 -DVLU,First Trust Dorsey Wright Momentum & Value ETF,Equity,23740000.0 -DVND,Touchstone Dividend Select ETF Fund,Equity,26163700.0 -DVOL,First Trust Dorsey Wright Momentum & Low Volatility ETF,Equity,80740200.0 -DVY,iShares Select Dividend ETF,Equity,22798140800.0 -DVYA,iShares Asia\u002FPacific Dividend ETF,Equity,33571500.0 -DVYE,iShares Emerging Markets Dividend ETF,Equity,558532400.0 -DWAS,Invesco DWA SmallCap Momentum ETF,Equity,387611800.0 -DWAT,Arrow DWA Tactical ETF,Asset Allocation,3417000.0 -DWAW,AdvisorShares Dorsey Wright FSM All Cap World ETF,Equity,92778400.0 -DWCR,Arrow DWA Tactical: International ETF,Equity,11716100.0 -DWEQ,AdvisorShares Dorsey Wright Alpha Equal Weight ETF,Equity,41993100.0 -DWLD,Davis Select Worldwide ETF,Equity,231522000.0 -DWM,WisdomTree International Equity Fund,Equity,549334900.0 -DWMC,AdvisorShares Dorsey Wright Micro-Cap ETF,Equity,4989000.0 -DWMF,WisdomTree International Multifactor Fund,Equity,31364300.0 -DWPP,First Trust Dorsey Wright Peoples Portfolio ETF,Equity,4011000.0 -DWSH,AdvisorShares Dorsey Wright Short ETF,Equity,45205600.0 -DWUS,AdvisorShares Dorsey Wright FSM US Core ETF,Equity,81970800.0 -DWX,SPDR S&P International Dividend ETF,Equity,528639300.00000006 -DXD,ProShares UltraShort Dow30,Equity,102321700.0 -DXGE,WisdomTree Germany Hedged Equity Fund,Equity,23065000.0 -DXJ,WisdomTree Japan Hedged Equity Fund,Equity,2043175800.0 -DXJS,WisdomTree Japan Hedged SmallCap Equity Fund,Equity,23500000.0 -DYLD,LeaderShares Dynamic Yield ETF,Fixed Income,93492000.0 -DYNF,BlackRock U.S. Equity Factor Rotation ETF,Equity,45613300.0 -EAFD,Simplify Developed Ex-US PLUS Downside Convexity ETF,Equity,21928600.0 -EAGG,iShares ESG U.S. Aggregate Bond ETF,Fixed Income,2106308799.9999998 -EAOA,iShares ESG Aware Aggressive Allocation ETF,Asset Allocation,20048400.0 -EAOK,iShares ESG Aware Conservative Allocation ETF,Asset Allocation,8283600.0 -EAOM,iShares ESG Aware Moderate Allocation ETF,Asset Allocation,4941900.0 -EAOR,iShares ESG Aware Growth Allocation ETF,Asset Allocation,13309600.0 -EAPR,Innovator MSCI Emerging Markets Power Buffer ETF,Equity,32334100.0 -EASG,Xtrackers MSCI EAFE ESG Leaders Equity ETF,Equity,32190400.0 -EATV,VegTech Plant-based Innovation & Climate ETF,Equity,5057100.0 -EATZ,AdvisorShares Restaurant ETF,Equity,2593800.0 -EBIZ,Global X E-commerce ETF,Equity,45281700.0 -EBLU,Tortoise Water Fund,Equity,63185600.0 -EBND,SPDR Bloomberg Barclays Emerging Markets Local Bond ETF ,Fixed Income,2051039000.0 -ECH,iShares MSCI Chile ETF,Equity,504086300.0 -ECLN,First Trust EIP Carbon Impact ETF,Equity,32882500.0 -ECNS,iShares MSCI China Small-Cap ETF,Equity,53617700.0 -ECON,Columbia Emerging Markets Consumer ETF,Equity,89534100.0 -ECOW,Pacer Emerging Markets Cash Cows 100 ETF,Equity,13801400.0 -ECOZ,TrueShares ESG Active Opportunities ETF,Equity,6756400.0 -EDC,Direxion Daily MSCI Emerging Markets Bull 3x Shares,Equity,65972000.0 -EDEN,iShares MSCI Denmark ETF,Equity,200587900.0 -EDIV,SPDR S&P Emerging Markets Dividend ETF,Equity,185877400.0 -EDOC,Global X Telemedicine & Digital Health ETF,Equity,156459600.0 -EDOG,ALPS Emerging Sector Dividend Dogs ETF,Equity,27398700.0 -EDOW,First Trust Dow 30 Equal Weight ETF,Equity,142936800.0 -EDUT,Global X Education ETF,Equity,3293600.0 -EDV,Vanguard Extended Duration Treasury ETF,Fixed Income,1277091000.0 -EDZ,Direxion Daily MSCI Emerging Markets Bear 3x Shares,Equity,40983000.0 -EELV,Invesco S&P Emerging Markets Low Volatility ETF,Equity,936822600.0 -EEM,iShares MSCI Emerging Markets ETF,Equity,21817025600.0 -EEMA,iShares MSCI Emerging Markets Asia ETF,Equity,478170000.0 -EEMD,AAM S&P Emerging Markets High Dividend Value ETF,Equity,6358400.0 -EEMO,Invesco S&P Emerging Markets Momentum ETF,Equity,4971800.0 -EEMS,iShares MSCI Emerging Markets Small-Cap ETF,Equity,352624300.0 -EEMV,iShares Edge MSCI Min Vol Emerging Markets ETF,Equity,7921405600.0 -EEMX,SPDR MSCI Emerging Markets Fossil Fuel Reserves Free ETF,Equity,91872000.0 -EES,WisdomTree SmallCap Earnings Fund,Equity,649957400.0 -EET,ProShares Ultra MSCI Emerging Markets,Equity,20539300.0 -EEV,ProShares UltraShort MSCI Emerging Markets,Equity,15005900.0 -EFA,iShares MSCI EAFE ETF,Equity,45949696000.0 -EFAD,ProShares MSCI EAFE Dividend Growers ETF,Equity,89288600.0 -EFAS,Global X MSCI SuperDividend EAFE ETF,Equity,9275000.0 -EFAV,iShares Edge MSCI Min Vol EAFE ETF,Equity,7612532800.0 -EFAX,SPDR MSCI EAFE Fossil Fuel Reserves Free ETF,Equity,237601500.0 -EFG,iShares MSCI EAFE Growth ETF,Equity,9917035200.0 -EFIV,SPDR S&P 500 ESG ETF,Equity,530578300.00000006 -EFIX,First Trust TCW Emerging Markets Debt ETF,Fixed Income,7589900.0 -EFNL,iShares MSCI Finland ETF,Equity,24128000.0 -EFO,ProShares Ultra MSCI EAFE,Equity,7081000.0 -EFRA,iShares Environmental Infrastructure and Industrials ETF,Equity,4375200.0 -EFU,ProShares UltraShort MSCI EAFE,Equity,5223400.0 -EFV,iShares MSCI EAFE Value ETF,Equity,15521216000.0 -EFZ,ProShares Short MSCI EAFE,Equity,143628200.0 -EGIS,2ndVote Society Defended ETF,Equity,26210800.0 -EGPT,VanEck Vectors Egypt Index ETF,Equity,13803800.0 -EIDO,iShares MSCI Indonesia ETF,Equity,522749300.0 -EINC,VanEck Vectors Energy Income ETF,Equity,33241900.0 -EIPX,FT Energy Income Partners Strategy ETF,Equity,77088400.0 -EIRL,iShares MSCI Ireland ETF,Equity,47756200.0 -EIS,iShares MSCI Israel ETF,Equity,152561000.0 -EJAN,MSCI Emerging Markets Power Buffer ETF (January),Equity,56144300.0 -EJUL,Innovator MSCI Emerging Markets Power Buffer ETF (July),Equity,55133300.0 -EKAR,Innovation Shares NextGen Vehicles & Technology ETF,Equity,7262700.0 -EKG,First Trust Nasdaq Lux Digital Health Solutions ETF,Equity,824000.0 -ELD,WisdomTree Emerging Markets Local Debt Fund,Fixed Income,91018100.0 -ELQD,iShares ESG Advanced Investment Grade Corporate Bond ETF,Fixed Income,16065600.0 -EMB,iShares J.P. Morgan USD Emerging Markets Bond ETF,Fixed Income,15150563200.0 -EMBD,Global X Emerging Markets Bond ETF,Fixed Income,92089500.0 -EMCA,Emerge EMPWR Sustainable Dividend Equity ETF,Equity,543100.0 -EMCB,WisdomTree Emerging Markets Corporate Bond Fund,Fixed Income,51633300.0 -EMCH,Emerge EMPWR Sustainable Emerging Markets Equity ETF,Equity,496900.0 -EMCR,Xtrackers Emerging Markets Carbon Reduction and Climate Improvers ETF,Equity,466925700.0 -EMDV,ProShares MSCI Emerging Markets Dividend Growers ETF,Equity,16420500.0 -EMFM,Global X Next Emerging & Frontier ETF,Equity,20034000.0 -EMFQ,Amplify Emerging Markets FinTech ETF,Equity,2623200.0 -EMGC,Emerge EMPWR Sustainable Select Growth Equity ETF,Equity,492500.0 -EMGD,Simplify Emerging Markets Equity PLUS Downside Convexity ETF,Equity,6974200.0 -EMGF,iShares Edge MSCI Multifactor Emerging Markets ETF,Equity,777108000.0 -EMHC,SPDR Bloomberg Barclays Emerging Markets USD Bond ETF,Fixed Income,108983100.0 -EMHY,iShares Emerging Markets High Yield Bond ETF,Fixed Income,311682000.0 -EMIF,iShares Emerging Markets Infrastructure ETF,Equity,22081500.0 -EMLC,VanEck Vectors J.P. Morgan EM Local Currency Bond ETF,Fixed Income,2744066400.0 -EMLP,First Trust North American Energy Infrastructure Fund,Equity,2650631600.0 -EMMF,WisdomTree Emerging Markets Multifactor Fund,Equity,2083800.0000000002 -EMNT,PIMCO Enhanced Short Maturity Active ESG ETF,Fixed Income,184917200.0 -EMPW,Emerge EMPWR Unified Sustainable Equity ETF,Equity,507900.0 -EMQQ,Emerging Markets Internet and Ecommerce ETF,Equity,499854000.0 -EMSG,Xtrackers MSCI Emerging Markets ESG Leaders Equity ETF,Equity,21919000.0 -EMTL,SPDR DoubleLine Emerging Markets Fixed Income ETF,Fixed Income,69816300.0 -EMTY,ProShares Decline of the Retail Store ETF,Equity,19371100.0 -EMXC,iShares MSCI Emerging Markets ex China ETF,Equity,2318004000.0 -EMXF,iShares ESG Advanced MSCI EM ETF,Equity,37851000.0 -EMZA,Emerge EMPWR Sustainable Global Core Equity ETF,Equity,508100.0 -ENFR,Alerian Energy Infrastructure ETF,Equity,136141900.0 -ENOR,iShares MSCI Norway ETF,Equity,30970600.0 -ENRG,SoFi Smart Energy ETF,Equity,3135200.0 -ENTR,Entrepreneur 30 ETF,Equity,30464000.0 -ENZL,iShares MSCI New Zealand ETF,Equity,112200000.0 -EOCT,Innovator Emerging Markets Power Buffer ETF - October,Equity,27221800.0 -EOPS,Emles Alpha Opportunities ETF,Equity,21480100.0 -EPHE,iShares MSCI Philippines ETF,Equity,104954300.0 -EPI,WisdomTree India Earnings Fund,Equity,704121200.0 -EPOL,iShares MSCI Poland ETF,Equity,196755500.0 -EPP,iShares MSCI Pacific ex Japan ETF,Equity,2169606200.0 -EPRE,First Trust TCW ESG Premier Equity ETF,Equity,825100.0 -EPRF,Elkhorn S&P High Quality Preferred ETF,Fixed Income,126483000.0 -EPS,WisdomTree Earnings 500 Fund,Equity,631535900.0 -EPU,iShares MSCI All Peru ETF,Equity,133729100.0 -EPV,ProShares UltraShort FTSE Europe,Equity,132418400.0 -EQAL,Invesco Russell 1000 Equal Weight ETF,Equity,618664200.0 -EQL,ALPS Equal Sector Weight ETF,Equity,310749300.0 -EQOP,Natixis U.S. Equity Opportunities ETF,Equity,9421200.0 -EQRR,ProShares Equities for Rising Rates ETF,Equity,70850000.0 -EQUL,IQ Engender Equality ETF,Equity,5541700.0 -EQWL,Invesco Russell Top 200 Equal Weight ETF,Equity,143143600.0 -ERET, iShares Environmentally Aware Real Estate ETF,Equity,8992800.0 -ERM,EquityCompass Risk Manager ETF,Asset Allocation,17450800.0 -ERSX,ERShares NextGen Entrepreneurs ETF,Equity,21029000.0 -ERTH,Invesco MSCI Sustainable Future ETF,Equity,327270400.0 -ERX,Direxion Daily Energy Bull 2X Shares,Equity,590474800.0 -ERY,Direxion Daily Energy Bear 2x Shares,Equity,35845500.0 -ESCR,Xtrackers Bloomberg Barclays US Investment Grade Corporate ESG ETF,Fixed Income,6338500.0 -ESEB,Xtrackers J.P. Morgan ESG Emerging Markets Sovereign ETF,Fixed Income,17288300.0 -ESG,FlexShares STOXX US ESG Impact Index Fund,Equity,179052100.0 -ESGA,American Century Sustainable Equity ETF,Equity,110251600.0 -ESGB,IQ MacKay ESG Core Plus Bond ETF,Fixed Income,236041600.0 -ESGD,iShares MSCI EAFE ESG Optimized ETF,Equity,6834144000.0 -ESGE,iShares MSCI EM ESG Optimized ETF,Equity,3799882000.0 -ESGG,FlexShares STOXX Global ESG Impact Index Fund,Equity,157264600.0 -ESGN,Columbia Sustainable International Equity Income ETF,Equity,7325000.0 -ESGS,Columbia Sustainable U.S. Equity Income ETF,Equity,42341500.0 -ESGU,iShares MSCI USA ESG Optimized ETF,Equity,20899174400.0 -ESGV,Vanguard ESG U.S. Stock ETF,Equity,5865080800.0 -ESGY,American Century Sustainable Growth ETF,Equity,6475500.0 -ESHY,Xtrackers J.P. Morgan ESG USD High Yield Corporate Bond ETF,Fixed Income,19031500.0 -ESIX,SPDR S&P SmallCap 600 ESG ETF,Equity,3001300.0 -ESML,iShares MSCI USA Small-Cap ESG Optimized ETF,Equity,1503378000.0 -ESMV,iShares ESG MSCI USA Min Vol Factor ETF,Equity,4679300.0 -ESPO,VanEck Vectors Video Gaming and eSports ETF,Equity,275250000.0 -ETHO,Etho Climate Leadership U.S. ETF,Equity,166700800.0 -ETPA,Ecofin Digital Payments Infrastructure Fund,Equity,6294000.0 -EUDG,WisdomTree Europe Quality Dividend Growth Fund,Equity,52613300.0 -EUDV,ProShares MSCI Europe Dividend Growers ETF,Equity,10346200.0 -EUFN,iShares MSCI Europe Financials ETF,Equity,1318362600.0 -EUM,ProShares Short MSCI Emerging Markets,Equity,71218900.0 -EUO,ProShares UltraShort Euro,Currency,91551300.0 -EURL,Direxion Daily FTSE Europe Bull 3X Shares,Equity,24678600.0 -EUSA,iShares MSCI USA Equal Weighted ETF,Equity,423851700.0 -EUSB,iShares ESG Advanced Total USD Bond Market ETF,Fixed Income,772508900.0 -EUSC,WisdomTree Europe Hedged SmallCap Equity Fund,Equity,31992100.0 -EVAV,Direxion Daily Electric and Autonomous Vehicles Bull 2X Shares,Equity,2128700.0 -EVEN,Direxion Daily S&P 500 Equal Weight Bull 2X Shares,Equity,4149700.0 -EVMT,Invesco Electric Vehicle Metals Commodity Strategy No K-1 ETF,Commodity,20763000.0 -EVNT,AltShares Event-Driven ETF,Alternatives,3031100.0 -EVX,VanEck Vectors Environmental Services ETF,Equity,75067200.0 -EWA,iShares MSCI Australia ETF,Equity,1649590200.0 -EWC,iShares MSCI Canada ETF,Equity,3854215600.0 -EWCO,Invesco S&P 500 Equal Weight Communication Services ETF,Equity,31538500.0 -EWD,iShares MSCI Sweden ETF,Equity,415439400.0 -EWEB,Global X Emerging Markets Internet & E-commerce ETF,Equity,2339100.0 -EWG,iShares MSCI Germany ETF,Equity,1352203200.0 -EWGS,iShares MSCI Germany Small-Cap ETF,Equity,22134000.0 -EWH,iShares MSCI Hong Kong ETF,Equity,688649800.0 -EWI,iShares MSCI Italy ETF,Equity,227030900.0 -EWJ,iShares MSCI Japan ETF,Equity,9330777600.0 -EWJV,iShares MSCI Japan Value ETF,Equity,83091200.0 -EWK,iShares MSCI Belgium ETF,Equity,15185500.0 -EWL,iShares MSCI Switzerland ETF,Equity,1328249500.0 -EWM,iShares MSCI Malaysia ETF,Equity,226164200.0 -EWMC,Invesco S&P MidCap 400 Equal Weight ETF,Equity,122793400.0 -EWN,iShares MSCI Netherlands ETF,Equity,320646300.0 -EWO,iShares MSCI Austria ETF,Equity,47332100.0 -EWP,iShares MSCI Spain ETF,Equity,481427400.0 -EWQ,iShares MSCI France ETF,Equity,944856600.0 -EWRE,Invesco S&P 500 Equal Weight Real Estate ETF,Equity,117954900.0 -EWS,iShares MSCI Singapore ETF,Equity,539531300.0 -EWSC,Invesco S&P SmallCap 600 Equal Weight ETF,Equity,51587400.0 -EWT,iShares MSCI Taiwan ETF,Equity,3335321000.0 -EWU,iShares MSCI United Kingdom ETF,Equity,3262467400.0 -EWUS,iShares MSCI United Kingdom Small-Cap ETF,Equity,49879000.0 -EWV,ProShares UltraShort MSCI Japan,Equity,6931600.0 -EWW,iShares MSCI Mexico ETF,Equity,862233200.0 -EWX,SPDR S&P Emerging Markets Small Cap ETF,Equity,599340400.0 -EWY,iShares MSCI South Korea ETF,Equity,2897229400.0 -EWZ,iShares MSCI Brazil ETF,Equity,5175017600.0 -EWZS,iShares MSCI Brazil Small-Cap ETF,Equity,78432000.0 -EXI,iShares Global Industrials ETF,Equity,287829000.0 -EYLD,Cambria Emerging Shareholder Yield ETF,Equity,108069500.0 -EZA,iShares MSCI South Africa ETF,Equity,351892100.0 -EZJ,ProShares Ultra MSCI Japan,Equity,5799100.0 -EZM,WisdomTree MidCap Earnings Fund,Equity,757321100.0 -EZU,iShares MSCI Eurozone ETF,Equity,5575134000.0 -FAAR,First Trust Alternative Absolute Return Strategy ETF,Alternatives,206125400.0 -FAB,First Trust Multi Cap Value AlphaDEX Fund,Equity,201852000.0 -FAD,First Trust Multi Cap Growth AlphaDEX Fund,Equity,165891000.0 -FAIL,Cambria Global Tail Risk ETF,Asset Allocation,5129200.0 -FALN,iShares U.S. Fallen Angels ETF,Fixed Income,1756881000.0 -FAN,First Trust Global Wind Energy ETF,Equity,279743200.0 -FAPR,FT Cboe Vest U.S. Equity Buffer ETF (April),Equity,271737200.0 -FAS,Direxion Daily Financial Bull 3X Shares,Equity,2244809200.0 -FAUG,FT Cboe Vest U.S. Equity Buffer ETF (August),Equity,281265500.0 -FAZ,Direxion Daily Financial Bear 3X Shares,Equity,146918100.0 -FBCG,Fidelity Blue Chip Growth ETF,Equity,340305600.0 -FBCV,Fidelity Blue Chip Value ETF,Equity,117855000.0 -FBND,Fidelity Total Bond ETF,Fixed Income,2365285200.0 -FBT,First Trust NYSE Arca Biotechnology Index Fund,Equity,1431217700.0 -FBZ,First Trust Brazil AlphaDEX Fund,Equity,7287000.0 -FCA,First Trust China AlphaDEX Fund,Equity,7643900.0 -FCAL,First Trust California Municipal High Income ETF,Fixed Income,120425000.0 -FCEF,First Trust CEF Income Opportunity ETF,Asset Allocation,25928500.0 -FCG,First Trust Natural Gas ETF,Equity,1025359100.0 -FCLD,Fidelity Cloud Computing ETF,Equity,3017600.0 -FCOM,Fidelity MSCI Telecommunication Services ETF,Equity,518466400.0 -FCOR,Fidelity Corporate Bond ETF,Fixed Income,148074000.0 -FCPI,Fidelity Stocks for Inflation ETF,Equity,267402000.0 -FCSH,Federated Hermes Short Duration Corporate ETF,Fixed Income,25397900.0 -FCTR,First Trust Lunt U.S. Factor Rotation ETF,Equity,321768000.0 -FCVT,First Trust SSI Strategic Convertible Securities ETF,Fixed Income,175338000.0 -FDD,First Trust STOXX European Select Dividend Index Fund,Equity,185015000.0 -FDEC,FT Cboe Vest U.S. Equity Buffer ETF (December),Equity,227010000.0 -FDEM,Fidelity Targeted Emerging Markets Factor ETF,Equity,21085000.0 -FDEV,Fidelity Targeted International Factor ETF,Equity,14592000.0 -FDG,American Century Focused Dynamic Growth ETF,Equity,127464300.0 -FDHT,Fidelity Digital Health ETF,Equity,3747900.0 -FDHY,Fidelity High Yield Factor ETF,Fixed Income,259286700.0 -FDIG,Fidelity Crypto Industry and Digital Payments ETF,Equity,13282500.0 -FDIS,Fidelity MSCI Consumer Discretionary ETF,Equity,1019817600.0 -FDL,First Trust Morningstar Dividend Leaders Index Fund,Equity,4069884000.0 -FDLO,Fidelity Low Volatility Factor ETF,Equity,434402500.0 -FDLS,Inspire Fidelis Multi Factor ETF,Equity,44478300.0 -FDM,First Trust Dow Jones Select MicroCap Index Fund,Equity,155530900.0 -FDMO,Fidelity Momentum Factor ETF,Equity,105037900.0 -FDN,First Trust Dow Jones Internet Index Fund,Equity,4020403600.0 -FDNI,First Trust Dow Jones International Internet ETF,Equity,28144500.0 -FDRR,Fidelity Dividend ETF for Rising Rates,Equity,546433900.0 -FDRV,Fidelity Electric Vehicles and Future Transportation ETF,Equity,3473800.0 -FDT,First Trust Developed Markets Ex-US AlphaDEX Fund,Equity,408496000.0 -FDTS,First Trust Developed Markets Ex-US Small Cap AlphaDEX Fund,Equity,7612800.0 -FDV,U.S. Strategic Dividend ETF,Equity,5138000.0 -FDVV,Fidelity Core Dividend ETF,Equity,1218584600.0 -FDWM,Fidelity Women's Leadership ETF,Equity,2647800.0 -FEBZ,TrueShares Structured Outcome (February) ETF,Equity,2210000.0 -FEDM,FlexShares ESG & Climate Developed Markets ex-US Core Index Fund ETF,Equity,19060200.0 -FEDX,Emles Federal Contractors ETF,Equity,4916000.0 -FEEM,FlexShares ESG & Climate Emerging Markets Core Index Fund,Equity,4299800.0 -FEHY,FlexShares ESG & Climate High Yield Corporate Core Index Fund ETF,Fixed Income,20790200.0 -FEIG,FlexShares ESG & Climate Investment Grade Corporate Core Index Fund ETF,Fixed Income,24261100.0 -FEM,First Trust Emerging Markets AlphaDEX Fund,Equity,315945000.0 -FEMA,Procure Disaster Recovery Strategy ETF,Equity,713000.0 -FEMB,First Trust Emerging Markets Local Currency Bond ETF,Fixed Income,121520200.0 -FEMS,First Trust Emerging Markets Small Cap AlphaDEX Fund,Equity,122375500.0 -FENY,Fidelity MSCI Energy Index ETF,Equity,1722709400.0 -FEP,First Trust Europe AlphaDEX Fund,Equity,271414000.0 -FEUS,FlexShares ESG & Climate US Large Cap Core Index Fund ETF,Equity,23084000.0 -FEUZ,First Trust Eurozone AlphaDEX Fund,Equity,17475000.0 -FEVR,Inspire Faithward Large Cap Momentum ESG ETF,Equity,21961800.0 -FEX,First Trust Large Cap Core AlphaDEX Fund,Equity,1142400000.0 -FEZ,SPDR EURO STOXX 50 ETF,Equity,1672110200.0 -FFEB,FT Cboe Vest U.S. Equity Buffer ETF (February),Equity,257900500.0 -FFHG,FormulaFolios Hedged Growth ETF,Asset Allocation,25990600.0 -FFIU,Fieldstone UVA Unconstrained Medium-Term Fixed Income ETF,Fixed Income,68090700.0 -FFND,The Future Fund Active ETF,Equity,9846000.0 -FFSG,FormulaFolios Smart Growth ETF,Equity,28058500.0 -FFTG,FormulaFolios Tactical Growth ETF,Asset Allocation,27784000.0 -FFTI,FormulaFolios Tactical Income ETF,Fixed Income,98447500.0 -FFTY,Innovator IBD 50 Fund,Equity,101464000.0 -FGD,First Trust Dow Jones Global Select Dividend Index Fund,Equity,542580500.0 -FGLD,Franklin Responsibly Sourced Gold ETF,Commodity,43455200.0 -FGM,First Trust Germany AlphaDEX Fund,Equity,13684000.0 -FGRO,Fidelity Growth Opportunities ETF,Equity,58752000.0 -FHLC,Fidelity MSCI Health Care Index ETF,Equity,3107539400.0 -FHYS,Federated Hermes Short Duration High Yield ETF,Fixed Income,35406600.0 -FIBR,iShares Edge U.S. Fixed Income Balanced Risk ETF,Fixed Income,71552900.0 -FICS,First Trust International Developed Capital Strength ETF,Equity,22297500.0 -FID,First Trust S&P International Dividend Aristocrats ETF,Equity,48864000.0 -FIDI,Fidelity International High Dividend ETF,Equity,81724200.0 -FIDU,Fidelity MSCI Industrials Index ETF,Equity,697442800.0 -FIG,Simplify Macro Strategy ETF,Asset Allocation,23028100.0 -FIGB,Fidelity Investment Grade Bond ETF,Fixed Income,8499400.0 -FILL,iShares MSCI Global Energy Producers ETF,Equity,135250400.0 -FINX,Global X FinTech ETF,Equity,489306600.0 -FISR,SPDR SSGA Fixed Income Sector Rotation ETF,Fixed Income,126019800.0 -FITE,SPDR Kensho Future Security ETF,Equity,31072100.0 -FIVA,Fidelity International Value Factor ETF,Equity,37659200.0 -FIVG,Defiance Next Gen Connectivity ETF,Equity,798731800.0 -FIVR,Strategy Shares Nasdaq 5HANDL Index ETF,Asset Allocation,2742600.0 -FIW,First Trust Water ETF,Equity,1322802100.0 -FIXD,First Trust TCW Opportunistic Fixed Income ETF,Fixed Income,3147584600.0 -FJAN,FT Cboe Vest U.S. Equity Buffer ETF (January),Equity,177809000.0 -FJP,First Trust Japan AlphaDEX Fund,Equity,31654700.0 -FJUL,FT Cboe Vest U.S. Equity Buffer ETF (July),Equity,222302400.0 -FJUN,FT Cboe Vest U.S. Equity Buffer ETF (June),Equity,317861300.0 -FKU,First Trust United Kingdom AlphaDEX Fund,Equity,32088000.0 -FLAU,Franklin FTSE Australia ETF,Equity,30119000.0 -FLAX,Franklin FTSE Asia ex Japan ETF,Equity,32002100.0 -FLBL,Franklin Senior Loan ETF,Fixed Income,221130000.0 -FLBR,Franklin FTSE Brazil ETF,Equity,277842200.0 -FLCA,Franklin FTSE Canada ETF,Equity,274756800.0 -FLCB,Franklin U.S. Core Bond ETF,Fixed Income,1461498500.0 -FLCH,Franklin FTSE China ETF,Equity,94711100.0 -FLCO,Franklin Investment Grade Corporate ETF,Fixed Income,594264200.0 -FLDR,Fidelity Low Duration Bond Factor ETF,Fixed Income,206889100.0 -FLDZ,RiverNorth Volition America Patriot ETF,Equity,3441400.0 -FLEE,Franklin FTSE Europe ETF,Equity,43196600.0 -FLEH,Franklin FTSE Europe Hedged ETF,Equity,16062100.0 -FLFR,Franklin FTSE France ETF,Equity,7953500.0 -FLGB,Franklin FTSE United Kingdom ETF,Equity,440636400.0 -FLGR,Franklin FTSE Germany ETF,Equity,16346900.0 -FLGV,Franklin U.S. Treasury Bond ETF,Fixed Income,375752300.0 -FLHK,Franklin FTSE Hong Kong ETF,Equity,11673900.0 -FLHY,Franklin High Yield Corporate ETF,Fixed Income,197120000.0 -FLIA,Franklin International Aggregate Bond ETF,Fixed Income,190758800.0 -FLIN,Franklin FTSE India ETF,Equity,58127000.0 -FLIY,Franklin FTSE Italy ETF,Equity,3355500.0 -FLJH,Franklin FTSE Japan Hedged ETF,Equity,31583900.0 -FLJP,Franklin FTSE Japan ETF,Equity,765333800.0 -FLKR,Franklin FTSE South Korea ETF,Equity,55180700.0 -FLLA,Franklin FTSE Latin America ETF,Equity,64506000.0 -FLLV,Franklin U.S. Low Volatility ETF,Equity,133564799.99999999 -FLMB,Franklin Liberty Municipal Bond ETF,Fixed Income,104003600.0 -FLMI,Franklin Liberty Intermediate Municipal Opportunities ETF,Fixed Income,91727700.0 -FLMX,Franklin FTSE Mexico ETF,Equity,12122700.0 -FLN,First Trust Latin America AlphaDEX Fund,Equity,45306000.0 -FLOT,iShares Floating Rate Bond ETF,Fixed Income,9177091200.0 -FLQL,Franklin LibertyQ U.S. Equity ETF,Equity,975912000.0 -FLQM,Franklin U.S. Mid Cap Equity ETF,Equity,117234000.0 -FLQS,Franklin U.S. Small Cap Equity ETF,Equity,15553500.0 -FLRG,Fidelity U.S. Multifactor ETF,Equity,18591000.0 -FLRN,SPDR Bloomberg Barclays Investment Grade Floating Rate ETF,Fixed Income,3107625000.0 -FLRT,AdvisorShares Pacific Asset Enhanced Floating Rate ETF,Fixed Income,76191100.0 -FLSA,Franklin FTSE Saudi Arabia ETF,Equity,5249800.0 -FLSP,Franklin Systematic Style Premia ETF,Alternatives,102222200.0 -FLSW,Franklin FTSE Switzerland ETF,Equity,52645300.0 -FLTB,Fidelity Limited Term Bond ETF,Fixed Income,234724700.0 -FLTN,Rareview Inflation\u002FDeflation ETF,Fixed Income,0.0 -FLTR,VanEck Vectors Investment Grade Floating Rate ETF,Fixed Income,1131184600.0 -FLTW,Franklin FTSE Taiwan ETF,Equity,30869700.0 -FLUD,Franklin Ultra Short Bond ETF,Fixed Income,2442600.0 -FLV,American Century Focused Large Cap Value ETF,Equity,233885600.0 -FLZA,Franklin FTSE South Africa ETF,Equity,3473100.0 -FM,iShares MSCI Frontier 100 ETF,Equity,313680100.0 -FMAG,Fidelity Magellan ETF,Equity,43852500.0 -FMAR,FT Cboe Vest U.S. Equity Buffer ETF - March,Equity,191995200.0 -FMAT,Fidelity MSCI Materials Index ETF,Equity,457067200.0 -FMAY,FT Cboe Vest U.S. Equity Buffer ETF (May),Equity,365418000.0 -FMB,First Trust Managed Municipal ETF,Fixed Income,1797140600.0 -FMCX,FMC Excelsior Focus Equity ETF,Equity,72010400.0 -FMET,Fidelity Metaverse ETF,Equity,9905000.0 -FMF,First Trust Morningstar Managed Futures Strategy Fund,Alternatives,205854700.0 -FMHI,First Trust Municipal High Income ETF,Fixed Income,289812500.0 -FMIL,Fidelity New Millennium ETF,Equity,58948500.0 -FMNY,First Trust New York Municipal High Income ETF,Fixed Income,10423900.0 -FMQQ,The Next Frontier Internet & Ecommerce ETF,Equity,14504000.0 -FNCL,Fidelity MSCI Financials Index ETF,Equity,1630075800.0 -FNDA,Schwab Fundamental U.S. Small Company Index ETF,Equity,5923702800.0 -FNDB,Schwab Fundamental U.S. Broad Market Index ETF,Equity,474751900.0 -FNDC,Schwab Fundamental International Small Company Index ETF,Equity,2635930200.0 -FNDE,Schwab Fundamental Emerging Markets Large Company Index ETF,Equity,4336291200.0 -FNDF,Schwab Fundamental International Large Company Index ETF,Equity,8419436000.0 -FNDX,Schwab Fundamental U.S. Large Company Index ETF,Equity,10191584000.0 -FNGG,Direxion Daily Select Large Caps & FANGs Bull 2X Shares,Equity,11536200.0 -FNI,First Trust Chindia ETF,Equity,108301000.0 -FNK,First Trust Mid Cap Value AlphaDEX Fund,Equity,243972000.0 -FNOV,FT Cboe Vest U.S. Equity Buffer ETF (November),Equity,306936300.0 -FNTC,Direxion Daily FinTech Bull 2X Shares,Equity,2782800.0 -FNX,First Trust Mid Cap Core AlphaDEX Fund,Equity,992736400.0 -FNY,First Trust Mid Cap Growth AlphaDEX Fund,Equity,271269500.0 -FOCT,FT Cboe Vest U.S. Equity Buffer ETF (October),Equity,253457100.0 -FOMO,AXS FOMO ETF,Asset Allocation,5380500.0 -FORH,Formidable ETF,Alternatives,27849900.0 -FOVL,iShares Focused Value Factor ETF,Equity,24310100.0 -FPA,First Trust Asia Pacific Ex-Japan AlphaDEX Fund,Equity,12930000.0 -FPAG,FPA Global Equity ETF,Equity,0.0 -FPE,First Trust Preferred Securities and Income ETF,Fixed Income,6158152400.0 -FPEI,First Trust Institutional Preferred Securities and Income ETF,Fixed Income,649743600.0 -FPFD,Fidelity Preferred Securities & Income ETF,Fixed Income,32120000.0 -FPRO,Fidelity Real Estate Investment ETF,Equity,17807500.0 -FPX,First Trust US Equity Opportunities ETF,Equity,929813600.0 -FPXE,First Trust IPOX Europe Equity Opportunities ETF,Equity,6048800.0 -FPXI,First Trust International IPO ETF,Equity,313830000.0 -FQAL,Fidelity Quality Factor ETF,Equity,239340200.0 -FRDM,Freedom 100 Emerging Markets ETF,Equity,257400000.0 -FREL,Fidelity MSCI Real Estate Index ETF,Equity,1530037000.0 -FRI,First Trust S&P REIT Index Fund,Equity,161888500.0 -FRNW,Fidelity Clean Energy ETF,Equity,4652000.0 -FRTY,Alger Mid Cap 40 ETF,Equity,29929800.0 -FSBD,Fidelity Sustainable Core Plus Bond ETF,Fixed Income,11654700.0 -FSEC,Fidelity Investment Grade Securitized ETF,Fixed Income,3382800.0 -FSEP,FT Cboe Vest U.S. Equity Buffer ETF (September),Equity,240611000.0 -FSIG,First Trust Limited Duration Investment Grade Corporate ETF,Fixed Income,15815300.0 -FSLD,Fidelity Sustainable Low Duration Bond ETF,Fixed Income,4982700.0 -FSMB,First Trust Short Duration Managed Municipal ETF,Fixed Income,386533200.0 -FSMD,Fidelity Small-Mid Factor ETF,Equity,73512700.0 -FSMO,Fidelity Small-Mid Cap Opportunities ETF,Equity,27996500.0 -FSST,Fidelity Sustainability U.S. Equity ETF,Equity,7215300.0 -FSTA,Fidelity MSCI Consumer Staples Index ETF,Equity,1118646200.0 -FSYD,Fidelity Sustainable High Yield ETF,Fixed Income,8895700.0 -FSZ,First Trust Switzerland AlphaDEX Fund,Equity,33468500.0 -FTA,First Trust Large Cap Value AlphaDEX Fund,Equity,1263344100.0 -FTAG,First Trust Indxx Global Agriculture ETF,Equity,25603200.0 -FTC,First Trust Large Cap Growth AlphaDEX Fund,Equity,932479500.0 -FTCS,First Trust Capital Strength ETF,Equity,8711065600.0 -FTDS,First Trust Dividend Strength ETF,Equity,18038700.0 -FTEC,Fidelity MSCI Information Technology Index ETF,Equity,5323094000.0 -FTGC,First Trust Global Tactical Commodity Strategy Fund,Commodity,4144056400.0 -FTGS,First Trust Growth Strength ETF,Equity,1080500.0 -FTHI,First Trust High Income ETF,Equity,86120500.0 -FTLS,First Trust Long\u002FShort Equity ETF,Alternatives,537314400.0 -FTQI,First Trust Nasdaq BuyWrite Income ETF,Equity,30772500.0 -FTRI,First Trust Indxx Global Natural Resources Income ETF,Equity,236622000.0 -FTSD,Franklin Short Duration U.S. Government ETF,Fixed Income,232859900.0 -FTSL,First Trust Senior Loan Fund,Fixed Income,2960158600.0 -FTSM,First Trust Enhanced Short Maturity ETF,Fixed Income,6944289600.0 -FTXG,First Trust Nasdaq Food & Beverage ETF,Equity,915420000.0 -FTXH,First Trust Nasdaq Pharmaceuticals ETF,Equity,43968000.0 -FTXL,First Trust Nasdaq Semiconductor ETF,Equity,80724000.0 -FTXN,First Trust Nasdaq Oil & Gas ETF,Equity,1274050000.0 -FTXO,First Trust Nasdaq Bank ETF,Equity,223314000.0 -FTXR,First Trust Nasdaq Transportation ETF,Equity,74425500.0 -FUMB,First Trust Ultra Short Duration Municipal ETF,Fixed Income,327563900.0 -FUNL,CornerCap Fundametrics Large Cap ETF,Equity,143274100.0 -FUTY,Fidelity MSCI Utilities Index ETF,Equity,2095931800.0 -FV,First Trust Dorsey Wright Focus 5 ETF,Equity,3149765000.0 -FVAL,Fidelity Value Factor ETF,Equity,426990600.0 -FVC,First Trust Dorsey Wright Dynamic Focus 5 ETF,Equity,261026800.0 -FVD,First Trust Value Line Dividend Index Fund,Equity,12658804000.0 -FXA,Invesco CurrencyShares Australian Dollar Trust,Currency,79852100.0 -FXB,Invesco CurrencyShares British Pound Sterling Trust,Currency,110219400.0 -FXC,Invesco CurrencyShares Canadian Dollar Trust,Currency,102355100.0 -FXD,First Trust Consumer Discretionary AlphaDEX Fund,Equity,362691600.0 -FXE,Invesco CurrencyShares Euro Trust,Currency,387706100.0 -FXF,Invesco CurrencyShares Swiss Franc Trust,Currency,232025800.0 -FXG,First Trust Consumer Staples AlphaDEX Fund,Equity,663789700.0 -FXH,First Trust Health Care AlphaDEX Fund,Equity,1510423300.0 -FXI,iShares China Large-Cap ETF,Equity,4533980400.0 -FXL,First Trust Technology AlphaDEX Fund,Equity,1056821700.0 -FXN,First Trust Energy AlphaDEX Fund,Equity,2005374400.0 -FXO,First Trust Financial AlphaDEX Fund,Equity,1199147200.0 -FXP,ProShares UltraShort FTSE China 50,Equity,23221200.0 -FXR,First Trust Industrials\u002FProducer Durables AlphaDEX Fund,Equity,1546894900.0 -FXU,First Trust Utilities AlphaDEX Fund,Equity,455632300.0 -FXY,Invesco CurrencyShares Japanese Yen Trust,Currency,200401200.0 -FXZ,First Trust Materials AlphaDEX Fund,Equity,1482350000.0 -FYC,First Trust Small Cap Growth AlphaDEX Fund,Equity,214802500.0 -FYLD,Cambria Foreign Shareholder Yield ETF,Equity,90363800.0 -FYLG,Global X Financials Covered Call & Growth ETF,0,2512100.0 -FYT,First Trust Small Cap Value AlphaDEX Fund,Equity,192003200.0 -FYX,First Trust Small Cap Core AlphaDEX Fund,Equity,834300000.0 -GAA,Cambria Global Asset Allocation ETF,Alternatives,43671700.0 -GABF,Gabelli Financial Services Opportunities ETF,Equity,5636300.0 -GAL,SPDR SSGA Global Allocation ETF,Asset Allocation,241422700.0 -GAMR,PureFunds Video Game Tech ETF,Equity,54666600.0 -GAST,Gabelli Asset ETF,Equity,4492300.0 -GBF,iShares Government\u002FCredit Bond ETF,Fixed Income,155288600.0 -GBIL,Goldman Sachs TreasuryAccess 0-1 Year ETF,Fixed Income,4031374800.0 -GBLD,Invesco MSCI Green Building ETF,Equity,5250000.0 -GBUY,Goldman Sachs Future Consumer Equity ETF,Equity,84369300.0 -GCC,WisdomTree Continuous Commodity Index Fund,Commodity,196272700.0 -GCIG,Genuine Investors ETF,Equity,9633600.0 -GCLN,Goldman Sachs Bloomberg Clean Energy Equity ETF,Equity,7662100.0 -GCOR,Goldman Sachs Access U.S. Aggregate Bond ETF,Fixed Income,423493800.0 -GCOW,Pacer Global Cash Cows Dividend ETF,Equity,775899000.0 -GDE,WisdomTree Efficient Gold Plus Equity Strategy Fund,Asset Allocation,0.0 -GDIV,Harbor Dividend Growth Leaders ETF,Equity,150844600.0 -GDMA,Gadsden Dynamic Multi-Asset ETF,Asset Allocation,145945300.0 -GDMN,WisdomTree Efficient Gold Plus Gold Miners Strategy Fund,Asset Allocation,0.0 -GDOC,Goldman Sachs Future Health Care Equity ETF,Equity,108739500.0 -GDVD,R3 Global Dividend Growth ETF,Equity,4494000.0 -GDX,VanEck Vectors Gold Miners ETF,Equity,11712244000.0 -GDXJ,VanEck Vectors Junior Gold Miners ETF,Equity,3651572800.0 -GEM,Goldman Sachs ActiveBeta Emerging Markets Equity ETF,Equity,961428500.0 -GEMD,Goldman Sachs Access Emerging Markets USD Bond ETF,Fixed Income,44262400.0 -GENY,Principal Millennials Index ETF,Equity,24687000.0 -GERM,ETFMG Treatments Testing and Advancements ETF,Equity,24651200.0 -GFGF,Guru Favorite Stocks ETF,Equity,29498000.0 -GFOF,Grayscale Future of Finance ETF,Equity,4113100.0000000005 -GGLL,Direxion Daily GOOGL Bull 1.5X Shares,Equity,4725000.0 -GGLS,Direxion Daily GOOGL Bear 1X Shares,Equity,3939600.0 -GGRW,Gabelli Growth Innovators ETF,Equity,2246200.0 -GHTA,Goose Hollow Tactical Allocation ETF,Asset Allocation,11743600.0 -GHYB,Goldman Sachs Access High Yield Corporate Bond ETF,Fixed Income,131962400.0 -GHYG,iShares U.S. and Intl High Yield Corp Bond ETF,Fixed Income,104012500.0 -GIGB,Goldman Sachs Access Investment Grade Corporate Bond ETF,Fixed Income,548146000.0 -GII,SPDR S&P Global Infrastructure ETF,Equity,562352800.0 -GINN,Goldman Sachs Innovate Equity ETF,Equity,320496800.0 -GK,AdvisorShares Gerber Kawasaki ETF,Equity,15863300.0 -GLCN,VanEck Vectors ChinaAMC CSI 300 ETF,Equity,15008400.0 -GLD,SPDR Gold Shares,Commodity,51586396800.0 -GLDB,Strategy Shares Gold-Hedged Bond ETF,Asset Allocation,12992100.0 -GLDM,SPDR Gold MiniShares Trust,Commodity,4959609200.0 -GLDX,USCF Gold Strategy Plus Income Fund,Commodity,3639400.0 -GLIF,AGFiQ Global Infrastructure ETF,Equity,3901500.0 -GLIN,VanEck Vectors India Small-Cap Index ETF,Equity,50055200.0 -GLL,ProShares UltraShort Gold,Commodity,20352000.0 -GLOV,Goldman Sachs ActiveBeta World Low Vol Plus Equity ETF,Equity,589302500.0 -GLRY,Inspire Faithward Mid Cap Momentum ESG ETF,Equity,22124700.0 -GLTR,Aberdeen Standard Physical Precious Metals Basket Shares ETF,Commodity,954077100.0 -GMET,VanEck Green Metals ETF,Equity,22818400.0 -GMF,SPDR S&P Emerging Asia Pacific ETF,Equity,350043000.0 -GMOM,Cambria Global Momentum ETF,Asset Allocation,176290000.0 -GNMA,iShares GNMA Bond ETF,Fixed Income,335046000.0 -GNOM,Global X Genomics & Biotechnology ETF,Equity,214995000.0 -GNR,SPDR S&P Global Natural Resources ETF,Equity,3572294800.0 -GOAU,U.S. Global GO GOLD and Precious Metal Miners ETF,Equity,81313000.0 -GOEX,Global X Gold Explorers ETF,Equity,34305300.0 -GOVT,iShares U.S. Treasury Bond ETF,Fixed Income,21823422400.0 -GOVZ,iShares 25 plus Year Treasury STRIPS Bond ETF,Fixed Income,298820000.0 -GPAL,Goldman Sachs ActiveBeta Paris-Aligned Climate US Large Cap Equity ETF,Equity,8385000.0 -GQRE,FlexShares Global Quality Real Estate Index Fund,Equity,342100700.0 -GREI,Goldman Sachs Future Real Estate and Infrastructure Equity ETF,Equity,23062100.0 -GREK,Global X MSCI Greece ETF,Equity,115884000.0 -GRES,IQ Global Resources ETF,Equity,44496800.0 -GRID,First Trust NASDAQ Clean Edge Smart Grid Infrastructure Index Fund,Equity,661375000.0 -GRNB,VanEck Vectors Green Bond ETF,Fixed Income,80249400.0 -GRNR,Global X Green Building ETF,Equity,2066100.0 -GRZZ,Grizzle Growth ETF,Equity,1088500.0 -GSEE,Goldman Sachs MarketBeta Emerging Markets Equity ETF,Equity,31253400.0 -GSEU,Goldman Sachs ActiveBeta Europe Equity ETF,Equity,11988000.0 -GSEW,Goldman Sachs Equal Weight U.S. Large Cap Equity ETF,Equity,636825000.0 -GSFP,Goldman Sachs Future Planet Equity ETF,Equity,179412000.0 -GSG,iShares S&P GSCI Commodity-Indexed Trust,Commodity,1482061800.0 -GSID,Goldman Sachs MarketBeta International Equity ETF,Equity,340663700.0 -GSIE,Goldman Sachs ActiveBeta International Equity ETF,Equity,2851266600.0 -GSIG,Goldman Sachs Access Investment Grade Corporate 1-5 Year Bond ETF,Fixed Income,6879800.0 -GSJY,Goldman Sachs ActiveBeta Japan Equity ETF,Equity,12240900.0 -GSLC,Goldman Sachs ActiveBeta U.S. Large Cap Equity ETF,Equity,11606940000.0 -GSPY,Gotham Enhanced 500 ETF,Equity,296178200.0 -GSSC,Goldman Sachs ActiveBeta U.S. Small Cap Equity ETF,Equity,480433900.0 -GSST,Goldman Sachs Access Ultra Short Bond ETF,Fixed Income,515892000.0 -GSUS,Goldman Sachs MarketBeta U.S. Equity ETF,Equity,815179000.0 -GSY,Invesco Ultra Short Duration ETF,Fixed Income,2146642200.0000002 -GTEK,Goldman Sachs Future Tech Leaders Equity ETF,Equity,217685000.0 -GTIP,Goldman Sachs Access Inflation Protected USD Bond ETF,Fixed Income,151187000.0 -GTO,Invesco Total Return Bond ETF,Fixed Income,775853700.0 -GTR,WisdomTree Target Range Fund,Equity,55229900.0 -GUNR,FlexShares Morningstar Global Upstream Natural Resources Index ETF Index Fund,Equity,7314293600.0 -GURU,Global X Guru Index ETF,Equity,48538800.0 -GUSA,Goldman Sachs MarketBeta U.S. 1000 Equity ETF,Equity,1253687300.0 -GUSH,Direxion Daily S&P Oil & Gas Exp. & Prod. Bull 2X Shares,Equity,831372600.0 -GVAL,Cambria Global Value ETF,Equity,101198200.0 -GVI,iShares Intermediate Government\u002FCredit Bond ETF,Fixed Income,2325880800.0 -GVIP,Goldman Sachs Hedge Industry VIP ETF,Equity,138285800.0 -GVLU,Gotham 1000 Value ETF,Equity,40731700.0 -GWX,SPDR S&P International Small Cap ETF,Equity,680504500.0 -GXC,SPDR S&P China ETF,Equity,1061617899.9999999 -GXG,Global X MSCI Columbia ETF,Equity,21730000.0 -GXTG,Global X Thematic Growth ETF,Equity,52748800.0 -GYLD,Arrow Dow Jones Global Yield ETF,Asset Allocation,26836600.0 -HACK,PureFunds ISE Cyber Security ETF,Equity,1480212400.0 -HAIL,SPDR Kensho Smart Mobility ETF,Equity,69758200.0 -HAP,VanEck Vectors Natural Resources ETF,Equity,139947000.0 -HAPI,Harbor Corporate Culture ETF,Equity,63031800.0 -HAPY,Harbor Corporate Culture Leaders ETF,Equity,6941700.0 -HART,IQ Healthy Hearts ETF,Equity,6704500.0 -HAUS,Home Appreciation U.S. REIT ETF,Equity,3131500.0 -HAUZ,Xtrackers International Real Estate ETF,Equity,522405000.0 -HAWX,iShares Currency Hedged MSCI ACWI ex U.S. ETF,Equity,142500400.0 -HCOM,Hartford Schroders Commodity Strategy ETF,Commodity,40108600.0 -HCRB,Hartford Core Bond ETF,Fixed Income,236877000.0 -HDAW,Deutsche X-trackers MSCI All World ex US High Dividend Yield Hedged Equity ETF,Equity,9802200.0 -HDEF,Deutsche X-trackers MSCI EAFE High Dividend Yield Hedged Equity ETF,Equity,1040518900.0 -HDG,ProShares Hedge Replication ETF,Alternatives,41501100.0 -HDGE,AdvisorShares Ranger Equity Bear ETF,Equity,139078200.0 -HDIV,QRAFT AI-Enhanced U.S. High Dividend ETF,Equity,4747600.0 -HDMV,First Trust Horizon Managed Volatility Developed International ETF,Equity,39032000.0 -HDRO,Defiance Next Gen Hydrogen ETF,Equity,39858000.0 -HDUS,Hartford Disciplined US Equity ETF,Equity,5068600.0 -HDV,iShares Core High Dividend ETF,Equity,12501104800.0 -HEDJ,WisdomTree Europe Hedged Equity Fund,Equity,1294252000.0 -HEEM,iShares Currency Hedged MSCI Emerging Markets ETF,Equity,147647900.0 -HEET,Hartford Schroders ESG US Equity ETF,Equity,9029900.0 -HEFA,iShares Currency Hedged MSCI EAFE ETF,Equity,3771372000.0 -HEGD,Swan Hedged Equity U.S. Large Cap ETF,Equity,139308000.0 -HELX,Franklin Genomic Advancements ETF,Equity,11008000.0 -HEQT,Simplify Hedged Equity ETF,Equity,104421700.0 -HERD,Pacer Cash Cows Fund of Funds ETF,Equity,26848000.0 -HERO,Global X Video Games & Esports ETF,Equity,172220600.0 -HEWC,iShares Currency Hedged MSCI Canada ETF,Equity,17448600.0 -HEWG,iShares Currency Hedged MSCI Germany ETF,Equity,37948500.0 -HEWJ,iShares Currency Hedged MSCI Japan ETF,Equity,349068300.0 -HEWU,iShares Currency Hedged MSCI United Kingdom ETF,Equity,15010500.0 -HEZU,iShares Currency Hedged MSCI Eurozone ETF,Equity,327464700.0 -HFGO,Hartford Large Cap Growth ETF,Equity,76361300.0 -HFND,Unlimited HFND Multi Strategy Return Tracker ETF,Alternatives,3102900.0 -HFXI,IQ 50 Percent Hedged FTSE International ETF,Equity,285239500.0 -HGER,Harbor All-Weather Inflation Focus ETF,Commodity,65813500.0 -HIBL,Daily S&P 500 High Beta Bull 3X Shares,Equity,80358600.0 -HIBS,Daily S&P 500 High Beta Bear 3X Shares,Equity,49253800.0 -HIGH,Simplify Enhanced Income ETF,Fixed Income,1258400.0 -HIPS,Master Income ETF,Asset Allocation,64892400.0 -HISF,First Trust High Income Strategic Focus ETF,Fixed Income,39717000.0 -HJEN,Direxion Hydrogen ETF,Equity,24873400.0 -HKND,Humankind US Stock ETF,Equity,127968000.0 -HLAL,Wahed FTSE USA Shariah ETF,Equity,175028000.0 -HLGE,Hartford Longevity Economy ETF,Equity,2468100.0 -HMOP,Hartford Municipal Opportunities ETF,Fixed Income,291372900.0 -HNDL,Nasdaq 7HANDL Index ETF,Asset Allocation,1231499000.0 -HOLD,AdvisorShares Sage Core Reserves ETF,Fixed Income,33023300.000000004 -HOM,LifeGoal Homeowner Investment ETF,Asset Allocation,1281000.0 -HOMZ,Hoya Capital Housing ETF,Equity,35685600.0 -HOTL,Kelly Hotel & Lodging Sector ETF,Equity,679100.0 -HSCZ,iShares Currency Hedged MSCI EAFE Small-Cap ETF,Equity,113957600.0 -HSMV,First Trust Horizon Managed Volatility Small\u002FMid ETF,Equity,16137800.0 -HSRT,Hartford Short Duration ETF,Fixed Income,75530000.0 -HSUN,Hartford Sustainable Income ETF,Fixed Income,45787000.0 -HTAB,Hartford Schroders Tax-Aware Bond ETF,Fixed Income,70380700.0 -HTEC,ROBO Global Healthcare Technology and Innovation ETF,Equity,102870600.0 -HTRB,Hartford Total Return Bond ETF,Fixed Income,973152100.0 -HTUS,HULL Tactical US ETF,Alternatives,24256000.0 -HUSV,First Trust Horizon Managed Volatility Domestic ETF,Equity,102386400.0 -HVAL,ALPS Hillman Active Value ETF,Equity,7817400.0 -HYBB,iShares BB Rated Corporate Bond ETF,Fixed Income,328385500.0 -HYBL,SPDR Blackstone High Income ETF,Fixed Income,0.0 -HYD,VanEck Vectors High-Yield Municipal Index ETF,Fixed Income,2772900000.0 -HYDB,iShares Edge High Yield Defensive Bond ETF,Fixed Income,113285300.0 -HYDR,Global X Hydrogen ETF,Equity,35280000.0 -HYDW,Xtrackers Low Beta High Yield Bond ETF,Fixed Income,1298772700.0 -HYEM,VanEck Vectors Emerging Markets High Yield Bond ETF,Fixed Income,400954400.0 -HYG,iShares iBoxx $ High Yield Corporate Bond ETF,Fixed Income,13627323200.0 -HYGH,iShares Interest Rate Hedged High Yield Bond ETF,Fixed Income,108585400.0 -HYGI,iShares Inflation Hedged High Yield Bond ETF,Fixed Income,2524300.0 -HYGV,FlexShares High Yield Value-Scored Bond Index Fund,Fixed Income,898638800.0 -HYGW,iShares High Yield Corporate Bond BuyWrite Strategy ETF,Fixed Income,9345000.0 -HYHG,ProShares High Yield-Interest Rate Hedged ETF,Fixed Income,127243500.0 -HYIN,WisdomTree Alternative Income Fund,Asset Allocation,0.0 -HYLB,Deutsche X-trackers USD High Yield Corporate Bond ETF,Fixed Income,3715717600.0 -HYLD,AdvisorShares Peritus High Yield ETF,Fixed Income,87360200.0 -HYLG,Global X Health Care Covered Call & Growth ETF,0,2532100.0 -HYLS,First Trust Tactical High Yield ETF,Fixed Income,1621935100.0 -HYLV,IQ S&P High Yield Low Volatility Bond ETF,Fixed Income,19433900.0 -HYMB,SPDR Nuveen S&P High Yield Municipal Bond ETF,Fixed Income,2015040800.0 -HYMU,BlackRock High Yield Muni Income Bond ETF,Fixed Income,23243000.0 -HYRM,Xtrackers Risk Managed USD High Yield Strategy ETF,Fixed Income,49959300.0 -HYS,PIMCO 0-5 Year US High Yield Corporate Bond Index ETF,Fixed Income,1411313200.0 -HYTR,CP High Yield Trend ETF,Fixed Income,7469000.0 -HYUP,Xtrackers High Beta High Yield Bond ETF,Fixed Income,11821500.0 -HYXF,iShares ESG Advanced High Yield Corporate Bond ETF,Fixed Income,132150000.0 -HYXU,iShares International High Yield Corporate Bond ETF,Fixed Income,34749000.0 -HYZD,WisdomTree Interest Rate Hedged High Yield Bond Fund,Fixed Income,165821000.0 -IAGG,iShares Core International Aggregate Bond ETF,Fixed Income,3758390000.0 -IAI,iShares U.S. Broker-Dealers & Securities Exchanges ETF,Equity,557724200.0 -IAK,iShares U.S. Insurance ETF,Equity,409135800.0 -IAPR,Innovator International Developed Power Buffer ETF April,Equity,45569500.0 -IAT,iShares U.S. Regional Banks ETF,Equity,783812400.0 -IAU,iShares Gold Trust,Commodity,25640963200.0 -IAUF,iShares Gold Strategy ETF,Commodity,37282000.0 -IAUM,iShares Gold Trust Micro,Commodity,1050778000.0 -IBB,iShares Nasdaq Biotechnology ETF,Equity,8306810400.0 -IBBQ,Invesco Nasdaq Biotechnology ETF,Equity,21300000.0 -IBCE,iShares iBonds Mar 2023 Corporate ex-Financials ETF,Fixed Income,38773800.0 -IBD,Inspire Corporate Bond Impact ETF,Fixed Income,256816000.0 -IBDD,iShares iBonds Mar 2023 Term Corporate ETF,Fixed Income,82025700.0 -IBDN,iShares iBonds Dec 2022 Term Corporate ETF,Fixed Income,1341523300.0 -IBDO,iShares iBonds Dec 2023 Term Corporate ETF,Fixed Income,1979542200.0 -IBDP,iShares iBonds Dec 2024 Term Corporate ETF,Fixed Income,1945130000.0 -IBDQ,iShares iBonds Dec 2025 Term Corporate ETF,Fixed Income,1493103200.0 -IBDR,iShares iBonds Dec 2026 Term Corporate ETF,Fixed Income,1255337000.0 -IBDS,iShares iBonds Dec 2027 Term Corporate ETF,Fixed Income,894101400.0 -IBDT,iShares iBonds Dec 2028 Term Corporate ETF,Fixed Income,567368100.0 -IBDU,iShares iBonds Dec 2029 Term Corporate ETF,Fixed Income,292519100.0 -IBDV,iShares iBonds Dec 2030 Term Corporate ETF,Fixed Income,191757500.0 -IBDW,iShares iBonds Dec 2031 Term Corporate ETF,Fixed Income,107752700.0 -IBDX,iShares iBonds Dec 2032 Term Corporate ETF,Fixed Income,17013800.0 -IBET,iBET Sports Betting and Gaming ETF,Equity,1125000.0 -IBHB,iShares iBonds 2022 Term High Yield and Income ETF,Fixed Income,108135000.0 -IBHC,iShares iBonds 2023 Term High Yield and Income ETF,Fixed Income,164220000.0 -IBHD,iShares iBonds 2024 Term High Yield and Income ETF,Fixed Income,148850000.0 -IBHE,iShares iBonds 2025 Term High Yield and Income ETF,Fixed Income,79590000.0 -IBHF,iShares iBonds 2026 Term High Yield and Income ETF,Fixed Income,36292800.0 -IBHG,iShares iBonds 2027 Term High Yield and Income ETF,Fixed Income,15235500.0 -IBHH,iShares iBonds 2028 Term High Yield and Income ETF,Fixed Income,0.0 -IBHI,iShares iBonds 2029 Term High Yield and Income ETF,Fixed Income,0.0 -IBIT,Defiance Daily Short Digitizing the Economy ETF,Equity,865500.0 -IBLC,iShares Blockchain and Tech ETF,Equity,4821500.0 -IBMK,iShares iBonds Dec 2022 Term Muni Bond ETF,Fixed Income,417194500.0 -IBML,iShares iBonds Dec 2023 Term Muni Bond ETF,Fixed Income,492566000.0 -IBMM,iShares iBonds Dec 2024 Term Muni Bond ETF,Fixed Income,511733000.0 -IBMN,iShares iBonds Dec 2025 Term Muni Bond ETF,Fixed Income,317156000.0 -IBMO,iBonds Dec 2026 Term Muni Bond ETF,Fixed Income,215860000.0 -IBMP,iShares iBonds Dec 2027 Term Muni Bond ETF,Fixed Income,177074000.0 -IBMQ,iShares iBonds Dec 2028 Term Muni Bond ETF,Fixed Income,100764000.0 -IBND,SPDR Bloomberg Barclays International Corporate Bond ETF,Fixed Income,110408500.0 -IBRN,iShares Neuroscience and Healthcare ETF,Equity,5188300.0 -IBTB,iShares iBonds Dec 2022 Term Treasury ETF,Fixed Income,200778500.0 -IBTD,iShares iBonds Dec 2023 Term Treasury ETF,Fixed Income,1094302500.0 -IBTE,iShares iBonds Dec 2024 Term Treasury ETF,Fixed Income,707745500.0 -IBTF,iShares iBonds Dec 2025 Term Treasury ETF,Fixed Income,335664000.0 -IBTG,iShares iBonds Dec 2026 Term Treasury ETF,Fixed Income,121741400.0 -IBTH,iShares iBonds Dec 2027 Term Treasury ETF,Fixed Income,138694000.0 -IBTI,iShares iBonds Dec 2028 Term Treasury ETF,Fixed Income,34224200.0 -IBTJ,iShares iBonds Dec 2029 Term Treasury ETF,Fixed Income,39213000.0 -IBTK,iShares iBonds Dec 2030 Term Treasury ETF,Fixed Income,76206100.0 -IBTL,iShares iBonds Dec 2031 Term Treasury ETF,Fixed Income,14497000.0 -IBTM,iShares iBonds Dec 2032 Term Treasury ETF,Fixed Income,4681400.0 -IBUY,Amplify Online Retail ETF,Equity,194044500.0 -ICAP,InfraCap Equity Income Fund ETF,Equity,27971200.0 -ICF,iShares Cohen & Steers REIT ETF,Equity,2283651000.0 -ICLN,iShares Global Clean Energy ETF,Equity,5273706000.0 -ICOW,Pacer Developed Markets International Cash Cows 100 ETF,Equity,201168000.0 -ICSH,iShares Ultra Short-Term ETF,Fixed Income,7059938400.0 -ICVT,iShares Convertible Bond ETF,Fixed Income,1848678000.0 -IDAT,iShares Cloud 5G and Tech ETF,Equity,7114100.0 -IDEV,iShares Core MSCI International Developed Markets ETF,Equity,8036466400.0 -IDHD,Invesco S&P International Developed High Dividend Low Volatility ETF,Equity,19249300.0 -IDHQ,Invesco S&P International Developed Quality ETF,Equity,101494900.0 -IDIV,US Equity Cumulative Dividends Fund-Series 2027,Asset Allocation,30302500.0 -IDLB,Invesco FTSE International Low Beta Equal Weight ETF,Equity,5961300.0 -IDLV,Invesco S&P International Developed Low Volatility ETF,Equity,457528200.0 -IDME,International Drawdown Managed Equity ETF,Equity,124086400.0 -IDMO,Invesco S&P International Developed Momentum ETF,Equity,9365000.0 -IDNA,iShares Genomics Immunology and Healthcare ETF,Equity,176199200.0 -IDOG,ALPS International Sector Dividend Dogs ETF,Equity,168521200.0 -IDRV,iShares Self-Driving EV and Tech ETF,Equity,454511200.0 -IDU,iShares U.S. Utilities ETF,Equity,1040400500.0 -IDV,iShares International Select Dividend ETF,Equity,4376172000.0 -IDVO,Amplify International Enhanced Dividend Income ETF,Equity,2625600.0 -IDX,VanEck Vectors Indonesia Index ETF,Equity,38038200.0 -IEDI,iShares Evolved U.S. Discretionary Spending ETF,Equity,13632500.0 -IEF,iShares 7-10 Year Treasury Bond ETF,Fixed Income,22118777600.0 -IEFA,iShares Core MSCI EAFE ETF,Equity,89314604800.0 -IEI,iShares 3-7 Year Treasury Bond ETF,Fixed Income,12242736000.0 -IEMG,iShares Core MSCI Emerging Markets ETF,Equity,61496243200.0 -IEO,iShares U.S. Oil & Gas Exploration & Production ETF,Equity,1183347700.0 -IETC,iShares Evolved U.S. Technology ETF,Equity,93276300.0 -IEUR,iShares Core MSCI Europe ETF,Equity,3655412400.0 -IEUS,iShares MSCI Europe Small-Cap ETF,Equity,105252000.0 -IEV,iShares Europe ETF,Equity,1635200900.0 -IEZ,iShares U.S. Oil Equipment & Services ETF,Equity,235205900.0 -IFGL,iShares International Developed Real Estate ETF,Equity,168318000.0 -IFRA,iShares U.S. Infrastructure ETF,Equity,1749982600.0 -IFV,First Trust Dorsey Wright International Focus 5 ETF,Equity,142229300.0 -IG,Principal Investment Grade Corporate Active ETF,Fixed Income,18845300.0 -IGBH,iShares Interest Rate Hedged Long-Term Corporate Bond ETF,Fixed Income,105567300.0 -IGE,iShares North American Natural Resources ETF,Equity,921974100.0 -IGEB,iShares Edge Investment Grade Enhanced Bond ETF,Fixed Income,164512500.0 -IGF,iShares Global Infrastructure ETF,Equity,3724068000.0 -IGHG,ProShares Investment Grade-Interest Rate Hedged ETF,Fixed Income,409591100.0 -IGIB,iShares Intermediate-Term Corporate Bond ETF,Fixed Income,9510940000.0 -IGLB,iShares Long-Term Corporate Bond ETF,Fixed Income,1354476700.0 -IGLD,FT Cboe Vest Gold Strategy Target Income ETF,Commodity,39408000.0 -IGM,iShares North American Tech ETF,Equity,3015890400.0 -IGN,iShares North American Tech-Multimedia Networking ETF,Equity,109941600.0 -IGOV,iShares International Treasury Bond ETF,Fixed Income,1120818700.0 -IGRO,iShares International Dividend Growth ETF,Equity,445632800.0 -IGSB,iShares Short-Term Corporate Bond ETF,Fixed Income,22739710400.0 -IGTR,Gradient Tactical Rotation Strategy ETF,Equity,326358300.0 -IGV,iShares North American Tech-Software ETF,Equity,5369339600.0 -IHAK,iShares Cybersecurity and Tech ETF,Equity,505833800.0 -IHDG,WisdomTree International Hedged Quality Dividend Growth Fund,Equity,1284276700.0 -IHE,iShares U.S. Pharmaceuticals ETF,Equity,413519400.0 -IHF,iShares U.S. Healthcare Providers ETF,Equity,1587260100.0 -IHI,iShares U.S. Medical Devices ETF,Equity,6353013600.0 -IHY,VanEck Vectors International High Yield Bond ETF,Fixed Income,55181500.0 -IHYF,Invesco High Yield Bond Factor ETF,Fixed Income,34708500.0 -IIGD,Invesco Investment Grade Defensive ETF,Fixed Income,53766200.0 -IIGV,Invesco Investment Grade Value ETF,Fixed Income,25013500.0 -IJAN,MSCI EAFE Power Buffer ETF (January),Equity,66035300.0 -IJH,iShares Core S&P Mid-Cap ETF,Equity,65029497600.0 -IJJ,iShares S&P Mid-Cap 400 Value ETF,Equity,8079722400.0 -IJK,iShares S&P Mid-Cap 400 Growth ETF,Equity,7265849600.0 -IJR,iShares Core S&P Small-Cap ETF,Equity,68103372800.0 -IJS,iShares S&P Small-Cap 600 Value ETF,Equity,7036451200.0 -IJT,iShares S&P Small-Cap 600 Growth ETF,Equity,5343888000.0 -IJUL,Innovator MSCI EAFE Power Buffer ETF (July),Equity,187988000.0 -ILCB,iShares Morningstar U.S. Equity,Equity,743343800.0 -ILCG,iShares Morningstar Growth ETF,Equity,1517991200.0 -ILCV,iShares Morningstar Value ETF,Equity,785974800.0 -ILDR,First Trust Innovation Leaders ETF,Equity,2919900.0 -ILF,iShares Latin America 40 ETF,Equity,1047849600.0 -ILTB,iShares Core 10 plus Year USD Bond ETF,Fixed Income,291340000.0 -IMCB,iShares Morningstar Mid-Cap ETF,Equity,767105000.0 -IMCG,iShares Morningstar Mid-Cap Growth ETF,Equity,1203972100.0 -IMCV,iShares Morningstar Mid-Cap Value ETF,Equity,496791000.0 -IMFL,Invesco International Developed Dynamic Multifactor ETF,Equity,0.0 -IMOM,International Quantitative Momentum ETF,Equity,64481100.0 -IMTB,iShares Core 5-10 Year USD Bond ETF,Fixed Income,90019400.0 -IMTM,iShares Edge MSCI Intl Momentum Factor ETF,Equity,848795000.0 -INC,VanEck Dynamic High Income ETF,Asset Allocation,525600.0 -INCO,Columbia India Consumer ETF,Equity,81563400.0 -INDA,iShares MSCI India ETF,Equity,4440366000.0 -INDF,Nifty India Financials ETF,Equity,8296000.0 -INDL,Direxion Daily MSCI India Bull 3X Shares,Equity,53836200.0 -INDS,Pacer Benchmark Industrial Real Estate SCTR ETF,Equity,224446400.0 -INDY,iShares India 50 ETF,Equity,594837600.0 -INFL,Horizon Kinetics Inflation Beneficiaries ETF,Equity,1351331800.0 -INKM,SPDR SSGA Income Allocation ETF,Asset Allocation,83217100.0 -INMU,BlackRock Intermediate Muni Income Bond ETF,Fixed Income,32402700.0 -INNO,Harbor Disruptive Innovation ETF,Equity,7171200.0 -INQQ,India Internet & Ecommerce ETF,Equity,765800.0 -INTF,iShares Edge MSCI Multifactor Intl ETF,Equity,819833100.0 -IOCT,Innovator International Developed Power Buffer ETF - October,Equity,100261300.0 -IOO,iShares Global 100 ETF,Equity,3675107200.0 -IPAC,iShares Core MSCI Pacific ETF,Equity,1647979100.0 -IPAY,PureFunds ISE Mobile Payments ETF,Equity,525496500.0 -IPDP,Dividend Performers ETF,Equity,17512000.0 -IPKW,Invesco International BuyBack Achievers ETF,Equity,88284700.0 -IPO,Renaissance IPO ETF,Equity,144106300.0 -IPOS,Renaissance International IPO ETF,Equity,8476700.0 -IPPP,Preferred-Plus ETF,Fixed Income,12894000.0 -IQDE,FlexShares International Quality Dividend Defensive Index Fund,Equity,44334600.0 -IQDF,FlexShares International Quality Dividend Index Fund,Equity,479335000.0 -IQDG,WisdomTree International Quality Dividend Growth Fund,Equity,358165500.0 -IQDY,FlexShares International Quality Dividend Dynamic Index Fund,Equity,70229600.0 -IQHI,IQ MacKay ESG High Income ETF,Fixed Income,25557400.0 -IQIN,IQ 500 International ETF,Equity,201544000.0 -IQLT,iShares Edge MSCI Intl Quality Factor ETF,Equity,4090773200.0 -IQM,Franklin Intelligent Machines ETF,Equity,7824900.0 -IQSI,IQ Candriam ESG International Equity ETF,Equity,202709000.0 -IQSM,IQ Candriam ESG U.S. Mid Cap Equity ETF,Equity,5492400.0 -IQSU,IQ Candriam ESG US Equity ETF,Equity,388151800.0 -IRBA,iMGP RBA Responsible Global Allocation ETF,Asset Allocation,8114500.0 -IRBO,iShares Robotics and Artificial Intelligence ETF,Equity,234991800.0 -IRVH,Global X Interest Rate Volatility & Inflation Hedge ETF,Fixed Income,2249000.0 -ISCB,iShares Morningstar Small-Cap ETF,Equity,204007700.0 -ISCF,iShares Edge MSCI Multifactor Intl Small-Cap ETF,Equity,450297400.0 -ISCG,iShares Morningstar Small-Cap Growth ETF,Equity,371415300.0 -ISCV,iShares Morningstar Small-Cap Value ETF,Equity,402261900.0 -ISDX,Invesco Strategic Developed ex-US ETF,Equity,413122500.0 -ISEM,Invesco Strategic Emerging Markets ETF,Equity,31144100.0 -ISHG,iShares 1-3 Year International Treasury Bond ETF,Fixed Income,58055000.0 -ISHP,First Trust S-Network E-Commerce ETF,Equity,4682000.0 -ISMD,Inspire Small\u002FMid Cap Impact ETF,Equity,121992000.0 -ISRA,VanEck Vectors Israel ETF,Equity,63713800.0 -ISTB,iShares Core 1-5 Year USD Bond ETF,Fixed Income,5105954000.0 -ISVL,iShares International Developed Small Cap Value Factor ETF,Equity,149156000.0 -ISWN,Amplify BlackSwan ISWN ETF,Asset Allocation,32961100.0 -ISZE,iShares Edge MSCI Intl Size Factor ETF,Equity,14432300.0 -ITA,iShares U.S. Aerospace & Defense ETF,Equity,4140803600.0 -ITAN,Sparkline Intangible Value ETF,Equity,5519800.0 -ITB,iShares U.S. Home Construction ETF,Equity,1231076300.0 -ITEQ,BlueStar TA-BIGITech Israel Technology,Equity,118795600.0 -ITM,VanEck Vectors AMT-Free Intermediate Municipal Index ETF,Fixed Income,1713848000.0 -ITOT,iShares Core S&P Total U.S. Stock Market ETF,Equity,41038947200.0 -IUS,Invesco Strategic US ETF,Equity,193705300.0 -IUSA,Amberwave Invest USA JSG Fund,Equity,1636000.0 -IUSB,iShares Core Total USD Bond Market ETF,Fixed Income,18725014400.0 -IUSG,iShares Core S&P U.S. Growth ETF,Equity,11329584800.0 -IUSS,Invesco Strategic US Small Company ETF,Equity,78592500.0 -IUSV,iShares Core S&P U.S. Value ETF,Equity,12735415200.0 -IVAL,International Quantitative Value ETF,Equity,126472500.0 -IVDG,Invesco Focused Discovery Growth ETF,Equity,618100.0 -IVE,iShares S&P 500 Value ETF,Equity,25176419200.0 -IVEG,iShares Emergent Food and AgTech Multisector ETF,Equity,5353700.0 -IVES,Wedbush ETFMG Global Cloud Technology ETF,Equity,22464400.0 -IVLC,Invesco US Large Cap Core ESG ETF,Equity,5876600.0 -IVLU,iShares Edge MSCI Intl Value Factor ETF,Equity,1611463100.0 -IVOG,Vanguard S&P Mid-Cap 400 Growth ETF,Equity,758756000.0 -IVOL,Quadratic Interest Rate Volatility and Inflation Hedge ETF,Fixed Income,1093356000.0 -IVOO,Vanguard S&P Mid-Cap 400 ETF,Equity,1521669000.0 -IVOV,Vanguard S&P Mid-Cap 400 Value ETF,Equity,836600400.0 -IVRA,Invesco Real Assets ESG ETF,Equity,3193900.0 -IVSG,Invesco Select Growth ETF,Equity,987200.0 -IVV,iShares Core S&P 500 ETF,Equity,306942880000.0 -IVW,iShares S&P 500 Growth ETF,Equity,28953718400.0 -IWB,iShares Russell 1000 ETF,Equity,28896041600.0 -IWC,iShares Micro-Cap ETF,Equity,907191000.0 -IWD,iShares Russell 1000 Value ETF,Equity,55280403200.0 -IWF,iShares Russell 1000 Growth ETF,Equity,61018681600.0 -IWFG,IQ Winslow Focused Large Cap Growth ETF,Equity,5486300.0 -IWFH,iShares Virtual Work and Life Multisector ETF,Equity,2565200.0 -IWIN,Amplify Inflation Fighter ETF,Asset Allocation,11626100.0 -IWL,iShares Russell Top 200 ETF,Equity,809769200.0 -IWLG,IQ Winslow Large Cap Growth ETF,Equity,13602400.0 -IWM,iShares Russell 2000 ETF,Equity,54314739200.0 -IWN,iShares Russell 2000 Value ETF,Equity,12448504800.0 -IWO,iShares Russell 2000 Growth ETF,Equity,9765948800.0 -IWP,iShares Russell Mid-Cap Growth ETF,Equity,12148863200.0 -IWR,iShares Russell Mid-Cap ETF,Equity,28419740800.0 -IWS,iShares Russell Mid-Cap Value ETF,Equity,13569529600.0 -IWTR,iShares MSCI Water Management Multisector ETF,Equity,5436200.0 -IWV,iShares Russell 3000 ETF,Equity,10730412000.0 -IWX,iShares Russell Top 200 Value ETF,Equity,1396407900.0 -IWY,iShares Russell Top 200 Growth ETF,Equity,4532080800.0 -IXC,iShares Global Energy ETF,Equity,2289646400.0 -IXG,iShares Global Financials ETF,Equity,784934100.0 -IXJ,iShares Global Healthcare ETF,Equity,3891225200.0 -IXN,iShares Global Tech ETF,Equity,2964450600.0 -IXP,iShares Global Telecom ETF,Equity,289351100.0 -IXSE,WisdomTree India ex-State-Owned Enterprises Fund,Equity,6511400.0 -IXUS,iShares Core MSCI Total International Stock ETF,Equity,28396748800.0 -IYC,iShares U.S. Consumer Services ETF,Equity,703094000.0 -IYE,iShares U.S. Energy ETF,Equity,2410607400.0 -IYF,iShares U.S. Financials ETF,Equity,2159082000.0 -IYG,iShares U.S. Financial Services ETF,Equity,1505281400.0 -IYH,iShares U.S. Healthcare ETF,Equity,3274654400.0 -IYJ,iShares U.S. Industrials ETF,Equity,1100227400.0 -IYK,iShares U.S. Consumer Goods ETF,Equity,1726323600.0 -IYLD,iShares Morningstar Multi-Asset Income ETF,Asset Allocation,150774000.0 -IYM,iShares U.S. Basic Materials ETF,Equity,941654300.0 -IYR,iShares U.S. Real Estate ETF,Equity,3513289600.0 -IYT,iShares Transportation Average ETF,Equity,842732000.0 -IYW,iShares U.S. Technology ETF,Equity,8385177600.0 -IYY,iShares Dow Jones U.S. ETF,Equity,1534733100.0 -IYZ,iShares U.S. Telecommunications ETF,Equity,334382600.0 -IZRL,ARK Israel Innovative Technology ETF,Equity,103878000.0 -JAAA,Janus Henderson AAA CLO ETF,Fixed Income,1674238400.0 -JAGG,JPMorgan U.S. Aggregate Bond ETF,Fixed Income,1126304000.0 -JANT,AllianzIM U.S. Large Cap Buffer10 Jan ETF,Equity,68048500.0 -JANW,AllianzIM U.S. Large Cap Buffer20 Jan ETF,Equity,56118400.0 -JANZ,TrueShares Structured Outcome (January) ETF,Equity,4047800.0 -JAVA,JPMorgan Active Value ETF,Equity,211379100.0 -JBBB,Janus Henderson B-BBB CLO ETF,Fixed Income,81575300.0 -JCPB,JPMorgan Core Plus Bond ETF,Fixed Income,486990000.0 -JCPI,JPMorgan Inflation Managed Bond ETF,Fixed Income,0.0 -JCTR,JPMorgan Carbon Transition U.S. Equity ETF,Equity,21757800.0 -JDST,Direxion Daily Junior Gold Miners Index Bear 2X Shares,Equity,69356700.0 -JEMA,JPMorgan Emerging Markets Equity Core ETF,Equity,1012454500.0 -JEPI,JPMorgan Equity Premium Income ETF,Equity,14814478400.0 -JEPQ,JPMorgan Nasdaq Equity Premium Income ETF,Equity,609462000.0 -JETS,U.S. Global Jets ETF,Equity,2228878000.0 -JFWD,Jacob Forward ETF,Equity,2930000.0 -JGRO,JPMorgan Active Growth ETF,Equity,83125200.0 -JHCB,John Hancock Corporate Bond ETF,Fixed Income,17519300.0 -JHCS,John Hancock Multifactor Media and Communications ETF,Equity,12377700.0 -JHDV, John Hancock U.S. High Dividend ETF,Equity,5655600.0 -JHEM,John Hancock Multifactor Emerging Markets ETF,Equity,676416300.0 -JHMA,John Hancock Multifactor Materials ETF,Equity,15547900.0 -JHMB,John Hancock Mortgage-Backed Securities ETF,Fixed Income,23968900.0 -JHMC,John Hancock Multifactor Consumer Discretionary ETF,Equity,9122200.0 -JHMD,John Hancock Multifactor Developed International ETF,Equity,439965800.0 -JHME,John Hancock Multifactor Energy ETF,Equity,25969200.0 -JHMF,John Hancock Multifactor Financials ETF,Equity,16869600.0 -JHMH,John Hancock Multifactor Healthcare ETF,Equity,24173500.0 -JHMI,John Hancock Multifactor Industrials ETF,Equity,10357300.0 -JHML,John Hancock Multifactor Large Cap ETF,Equity,748934600.0 -JHMM,John Hancock Multifactor Mid Cap ETF,Equity,2660931600.0 -JHMS,John Hancock Multifactor Consumer Staples ETF,Equity,34406300.0 -JHMT,John Hancock Multifactor Technology ETF,Equity,26671400.0 -JHMU,John Hancock Multifactor Utilities ETF,Equity,17967100.0 -JHPI,John Hancock Preferred Income ETF,Fixed Income,22478700.0 -JHSC,John Hancock Multifactor Small Cap ETF,Equity,350401400.0 -JIB,Janus Henderson Sustainable & Impact Core Bond ETF,Fixed Income,35538500.0 -JIDA,JPMorgan ActiveBuilders International Equity ETF,Equity,25152900.0 -JIG,JPMorgan International Growth ETF,Equity,62256600.0 -JIGB,JPMorgan Corporate Bond Research Enhanced ETF,Fixed Income,38198600.0 -JIRE,JPMorgan International Research Enhanced Equity ETF,Equity,5627283600.0 -JMBS,Janus Henderson Mortgage-Backed Securities ETF,Fixed Income,798294000.0 -JMEE,JPMorgan Market Expansion Enhanced Equity ETF,Equity,897017200.0 -JMOM,JPMorgan U.S. Momentum Factor ETF,Equity,236090100.0 -JMST,JPMorgan Ultra-Short Municipal ETF,Fixed Income,2514365000.0 -JMUB,JPMorgan Municipal ETF,Fixed Income,247200000.0 -JNK,SPDR Bloomberg Barclays High Yield Bond ETF,Fixed Income,7609701600.0 -JNUG,Direxion Daily Junior Gold Miners Index Bull 2X Shares,Equity,309646000.0 -JOET,Virtus Terranova U.S. Quality Momentum ETF,Equity,93225000.0 -JOJO,ATAC Credit Rotation ETF,Fixed Income,5453600.0 -JPEM,JPMorgan Diversified Return Emerging Markets Equity ETF,Equity,174805900.0 -JPHY,JPMorgan Diversified High Yield ETF,Fixed Income,393360000.0 -JPIB,JPMorgan International Bond Opportunities ETF,Fixed Income,306592000.0 -JPIE,JPMorgan Income ETF,Fixed Income,153170500.0 -JPIN,JPMorgan Diversified Return International Equity ETF,Equity,747702200.0 -JPMB,JPMorgan USD Emerging Markets Sovereign Bond ETF,Fixed Income,68944500.0 -JPME,JPMorgan Diversified Return U.S. Mid Cap Equity ETF,Equity,280799000.0 -JPRE,JPMorgan Realty Income ETF,Equity,841623700.0 -JPSE,JPMorgan Diversified Return U.S. Small Cap Equity ETF,Equity,296980600.0 -JPST,JPMorgan Ultra-Short Income ETF,Fixed Income,22625198400.0 -JPUS,JPMorgan Diversified Return U.S. Equity ETF,Equity,531368699.99999994 -JPXN,iShares JPX-Nikkei 400 ETF,Equity,52535000.0 -JQUA,JPMorgan U.S. Quality Factor ETF,Equity,616224400.0 -JRE,Janus Henderson U.S. Real Estate ETF,Equity,3348000.0 -JRNY,ALPS Global Travel Beneficiaries ETF,Equity,11588200.0 -JSCP,JPMorgan Short Duration Core Plus ETF,Fixed Income,195783200.0 -JSMD,Janus Henderson Small\u002FMid Cap Growth Alpha,Equity,176454900.0 -JSML,Janus Henderson Small Cap Growth Alpha ETF,Equity,79200000.0 -JSTC,Adasina Social Justice All Cap Global ETF,Equity,106285200.0 -JUCY,Aptus Enhanced Yield ETF,Fixed Income,0.0 -JULT,AllianzIM U.S. Large Cap Buffer10 Jul ETF,Equity,45565900.0 -JULW,AllianzIM U.S. Large Cap Buffer20 Jul ETF,Equity,123750900.0 -JULZ,TrueShares Structured Outcome (July) ETF,Equity,12464800.0 -JUNZ,TrueShares Structured Outcome (June) ETF,Equity,4892900.0 -JUSA,JPMorgan ActiveBuilders U.S. Large Cap Equity ETF,Equity,25653800.0 -JUST,Goldman Sachs JUST U.S. Large Cap Equity ETF,Equity,277687700.0 -JVAL,JPMorgan U.S. Value Factor ETF,Equity,468848300.0 -JXI,iShares Global Utilities ETF,Equity,147361900.0 -JZRO,Janus Henderson Net Zero Transition Resources ETF,Equity,50821800.0 -KALL,KraneShares MSCI All China Index ETF,Equity,5035000.0 -KAPR,Russell 2000 Power Buffer ETF (April),Equity,87019100.0 -KARB,Carbon Strategy ETF,Commodity,307100.0 -KARS,KraneShares Electric Vehicles & Future Mobility ETF,Equity,213596000.0 -KBA,KraneShares Bosera MSCI China A ETF,Equity,561260000.0 -KBE,SPDR S&P Bank ETF,Equity,2223179600.0 -KBND,KraneShares Bloomberg Barclays China Bond Inclusion Index ETF,Fixed Income,15350000.0 -KBUY,KraneShares CICC China Consumer Leaders Index ETF,Equity,6806000.0 -KBWB,Invesco KBW Bank ETF,Equity,2113777200.0000002 -KBWD,Invesco KBW High Dividend Yield Financial ETF,Equity,367934100.0 -KBWP,Invesco KBW Property & Casualty Insurance ETF,Equity,325069500.0 -KBWR,Invesco KBW Regional Banking ETF,Equity,81272400.0 -KBWY,Invesco KBW Premium Yield Equity REIT ETF,Equity,253942500.0 -KCCA,Kraneshares California Carbon Allowance ETF,Commodity,238631300.0 -KCE,SPDR S&P Capital Markets ETF,Equity,108164200.0 -KDIV,KraneShares S&P Pan Asia Dividend Aristocrats ETF,Equity,2449000.0 -KDRN,Kingsbarn Tactical Bond ETF,Fixed Income,2164000.0 -KEJI,Global X China Disruption ETF,Equity,2093400.0 -KEMQ,KraneShares Emerging Markets Consumer Technology ETF,Equity,31464000.0 -KEMX,KraneShares MSCI Emerging Markets ex-China Index ETF,Equity,15078000.0 -KESG,KraneShares MSCI China ESG Leaders ETF,Equity,4312500.0 -KEUA,KraneShares European Carbon Allowance ETF,Commodity,25425000.0 -KFVG,KraneShares CICC China 5G & Semiconductor ETF,Equity,13914000.0 -KFYP,KraneShares Zacks New China ETF,Equity,5637500.0 -KGHG,KraneShares Global Carbon Transformation ETF,Equity,2432000.0 -KGRN,KraneShares MSCI China Environment ETF,Equity,101346000.0 -KGRO,KraneShares China Innovation ETF,Equity,1554000.0 -KHYB,KraneShares Asia Pacific High Yield Bond ETF,Fixed Income,20416000.0 -KIE,SPDR S&P Insurance ETF,Equity,471013900.0 -KJAN,Russell 2000 Power Buffer ETF (January),Equity,91005400.0 -KJUL,Innovator Russell 2000 Power Buffer ETF (July),Equity,79201300.0 -KLCD,KFA Large Cap Quality Dividend Index ETF,Equity,9816000.0 -KLDW,Knowledge Leaders Developed World ETF,Equity,133701200.00000001 -KLNE,Direxion Daily Global Clean Energy Bull 2X Shares,Equity,5321200.0 -KMED,KraneShares Emerging Market Healthcare Index ETF,Equity,2106000.0 -KMET,KraneShares Electrification Metals,Commodity,21104000.0 -KMLM,KFA Mount Lucas Index Strategy ETF,Alternatives,309127000.0 -KNG,Cboe Vest S&P 500 Dividend Aristocrats Target Income ETF,Equity,538752100.0 -KNGS,UPHOLDINGS Compound Kings ETF,Equity,7418600.0 -KOCG,FIS Knights of Columbus Global Belief ETF,Equity,18465400.0 -KOCT,Innovator Russell 2000 Power Buffer ETF (October),Equity,60482200.0 -KOIN,Innovation Shares NextGen Protocol ETF,Equity,17905700.0 -KOKU,Xtrackers MSCI Kokusai Equity ETF,Equity,516764700.0 -KOLD,ProShares UltraShort Bloomberg Natural Gas,Commodity,91925900.0 -KOMP,SPDR Kensho New Economies Composite ETF,Equity,1661423300.0 -KONG,Formidable Fortress ETF,Equity,25599500.0 -KORP,American Century Diversified Corporate Bond ETF,Fixed Income,143640000.0 -KORU,Direxion Daily MSCI South Korea Bull 3X Shares,Equity,25749100.0 -KPOP,KPOP and Korean Entertainment ETF,Equity,1847200.0 -KRBN,KFA Global Carbon ETF,Commodity,744832000.0 -KRE,SPDR S&P Regional Banking ETF,Equity,3654565600.0 -KRMA,Global X Conscious Companies ETF,Equity,661342500.0 -KROP,Global X AgTech & Food Innovation ETF,Equity,6495200.0 -KSA,iShares MSCI Saudi Arabia ETF,Equity,967742700.0 -KSCD,KFA Small Cap Quality Dividend Index ETF,Equity,6952500.0 -KSET,KraneShares Global Carbon Offset Strategy ETF,Commodity,4816000.0 -KSTR,KraneShares SSE STAR Market 50 Index ETF,Equity,46650000.0 -KTEC,KraneShares Hang Seng TECH Index ETF,Equity,8920000.0 -KURE,KraneShares MSCI All China Health Care Index ETF,Equity,94608000.0 -KVLE,KFA Value Line Dynamic Core Equity Index ETF,Equity,27252000.0 -KWEB,KraneShares CSI China Internet ETF,Equity,5127664000.0 -KWT,iShares MSCI Kuwait ETF,Equity,31977000.0 -KXI,iShares Global Consumer Staples ETF,Equity,1307286000.0 -LABD,Direxion Daily S&P Biotech Bear 3x Shares,Equity,191437000.0 -LABU,Direxion Daily S&P Biotech Bull 3x Shares,Equity,1078932000.0 -LBAY,Leatherback Long\u002FShort Alternative Yield ETF,Alternatives,60962500.0 -LCF,Touchstone US Large Cap Focused ETF,Equity,26416500.0 -LCG,Sterling Capital Focus Equity ETF,Equity,53950000.0 -LCLG,Logan Capital Broad Innovative Growth ETF,Equity,45810100.0 -LCR,Leuthold Core ETF,Asset Allocation,52257700.0 -LCTD,BlackRock World ex U.S. Carbon Transition Readiness ETF,Equity,417789800.0 -LCTU,BlackRock U.S. Carbon Transition Readiness ETF,Equity,1447235000.0 -LDEM,iShares ESG MSCI EM Leaders ETF,Equity,57551000.0 -LDSF,First Trust Low Duration Strategic Focus ETF,Fixed Income,195386000.0 -LDUR,PIMCO Enhanced Low Duration Active ETF,Fixed Income,1458777200.0 -LEAD,Reality Shares DIVCON Leaders Dividend ETF,Equity,41427800.0 -LEGR,First Trust Indxx Innovative Transaction and Process ETF,Equity,115797000.0 -LEMB,iShares J.P. Morgan Emerging Markets Local Currency Bond ETF,Fixed Income,364706300.0 -LETB,AdvisorShares Let Bob AI Powered Momentum ETF,Equity,26121600.0 -LEXI,Alexis Practical Tactical ETF,Asset Allocation,54156800.0 -LFEQ,VanEck Vectors NDR CMG Long\u002FFlat Allocation ETF,Equity,33445199.999999996 -LGH,HCM Defender 500 Index ETF,Equity,251928000.0 -LGLV,SPDR SSGA US Large Cap Low Volatility Index ETF,Equity,623590900.0 -LGOV,First Trust Long Duration Opportunities ETF,Fixed Income,19031000.0 -LIT,Global X Lithium & Battery Tech ETF,Equity,3983042000.0 -LIV,Emles @Home ETF,Equity,786300.0 -LKOR,FlexShares Credit-Scored US Long Corporate Bond Index Fund,Fixed Income,39151000.0 -LMBS,First Trust Low Duration Opportunities ETF,Fixed Income,4824735600.0 -LONZ,PIMCO Senior Loan Active ExchangeTraded Fund,Fixed Income,112029900.0 -LOPP,Gabelli Love Our Planet & People ETF,Equity,13300000.0 -LOUP,Innovator Loup Frontier Tech ETF,Equity,34390000.0 -LQD,iShares iBoxx $ Investment Grade Corporate Bond ETF,Fixed Income,36559065600.0 -LQDB,iShares BBB Rated Corporate Bond ETF,Fixed Income,25228700.0 -LQDH,iShares Interest Rate Hedged Corporate Bond ETF,Fixed Income,763291300.0 -LQDI,iShares Inflation Hedged Corporate Bond ETF,Fixed Income,58638500.0 -LQDW,iShares Investment Grade Corporate Bond BuyWrite Strategy ETF,Fixed Income,0.0 -LQIG,SPDR MarketAxess Investment Grade 400 Corporate Bond ETF,Fixed Income,100996700.0 -LRGE,ClearBridge Large Cap Growth ESG ETF,Equity,169338000.0 -LRGF,iShares Edge MSCI Multifactor USA ETF,Equity,1185894800.0 -LRND,IQ U.S. Large Cap R&D Leaders ETF,Equity,4227200.0 -LRNZ,"TrueShares Technology, AI and Deep Learning ETF",Equity,16406400.000000002 -LSAF,LeaderShares AlphaFactor US Core Equity ETF,Equity,154327000.0 -LSAT,LeaderShares AlphaFactor Tactical Focused ETF,Equity,161538500.0 -LSST,Natixis Loomis Sayles Short Duration Income ETF,Fixed Income,34048600.0 -LTL,ProShares Ultra Telecommunications,Equity,1696000.0 -LTPZ,PIMCO 15 Plus Year U.S. TIPS Index ETF,Fixed Income,612446600.0 -LUXE,Emles Luxury Goods ETF,Equity,4513300.0 -LVHD,Legg Mason Low Volatility High Dividend ETF,Equity,839477000.0 -LVHI,Legg Mason International Low Volatility High Dividend ETF,Equity,187502400.0 -LVOL,American Century Low Volatility ETF,Equity,8216900.0 -LYFE,2ndVote Life Neutral Plus ETF,Equity,17709800.0 -MAAX,VanEck Vectors Municipal Allocation ETF,Fixed Income,2706000.0 -MAGA,Point Bridge GOP Stock Tracker ETF,Equity,16098400.0 -MAKX,ProShares S&P Kensho Smart Factories ETF,Equity,972600.0 -MAMB,Monarch Ambassador Income ETF,Asset Allocation,41287000.0 -MARB,First Trust Merger Arbitrage ETF,Alternatives,87037200.0 -MARZ,TrueShares Structured Outcome ETF - March,Equity,4305900.0 -MAXI,Simplify Bitcoin Strategy PLUS Income ETF,Currency,4304200.0 -MAYZ,TrueShares Structured Outcome (May) ETF,Equity,0.0 -MBB,iShares MBS ETF,Fixed Income,22712880000.0 -MBBB,VanEck Vectors Moody's Analytics BBB Corporate Bond ETF,Fixed Income,8248400.0 -MBCC,Monarch Blue Chips Core ETF,Equity,33708500.0 -MBND,SPDR Nuveen Municipal Bond ETF,Fixed Income,38634200.0 -MBNE,SPDR Nuveen Municipal Bond ESG ETF,Fixed Income,34938000.0 -MBOX,Freedom Day Dividend ETF,Equity,53073000.0 -MBSD,FlexShares Disciplined Duration MBS Index Fund,Fixed Income,99476400.0 -MCH,Matthews China Active ETF,Equity,1271100.0 -MCHI,iShares MSCI China ETF,Equity,6191808000.0 -MCRO,IQ Hedge Macro Tracker ETF,Alternatives,2534800.0 -MCSE,Martin Currie Sustainable International Equity ETF,Equity,34613300.0 -MDCP,VictoryShares THB Mid Cap ESG ETF,Equity,2273100.0 -MDEV,First Trust Indxx Medical Devices ETF,Equity,1863200.0 -MDIV,First Trust Multi-Asset Diversified Income Index Fund,Asset Allocation,459892600.0 -MDY,SPDR S&P MidCap 400 ETF,Equity,18807572800.0 -MDYG,SPDR S&P 400 Mid Cap Growth ETF,Equity,1633451500.0 -MDYV,SPDR S&P 400 Mid Cap Value ETF,Equity,2427443800.0 -MEAR,iShares Short Maturity Municipal Bond ETF,Fixed Income,522480000.0 -MEDI,Harbor Health Care ETF,Equity,2517400.0 -MEM,Matthews Emerging Markets Equity Active ETF,Equity,3655900.0 -MEME,Roundhill Meme Stock ETF,Equity,948900.0 -METV,Roundhill Ball Metaverse ETF,Equity,421133900.0 -MEXX,Direxion Daily MSCI Mexico Bull 3X Shares,Equity,13442300.0 -MFDX,PIMCO RAFI Dynamic Multi-Factor International Equity ETF,Equity,103040400.0 -MFEM,PIMCO RAFI Dynamic Multi-Factor Emerging Market Equity ETF,Equity,80508000.0 -MFLX,First Trust Flexible Municipal High Income ETF,Fixed Income,11814400.0 -MFUL,Mindful Conservative ETF,Asset Allocation,90174000.0 -MFUS,PIMCO RAFI Dynamic Multi-Factor U.S. Equity ETF,Equity,116735200.0 -MGC,Vanguard Mega Cap ETF,Equity,3675285200.0 -MGK,Vanguard Mega Cap Growth ETF,Equity,10610508800.0 -MGMT,Ballast Small\u002FMid Cap ETF,Equity,69468000.0 -MGV,Vanguard Mega Cap Value ETF,Equity,5817504000.0 -MID,American Century Mid Cap Growth Impact ETF,Equity,24811400.0 -MIDE,Xtrackers S&P MidCap 400 ESG ETF,Equity,11615500.0 -MIDU,Direxion Daily Mid Cap Bull 3X Shares,Equity,53739400.0 -MIG,VanEck Vectors Moody's Analytics IG Corporate Bond ETF,Fixed Income,12399700.0 -MILN,Global X Millennials Thematic ETF,Equity,104895000.0 -MINC,AdvisorShares Newfleet Multi-Sector Income ETF,Fixed Income,39249200.0 -MINN,Minnesota Municipal Bond ETF,Fixed Income,0.0 -MINO,PIMCO Municipal Income Opportunities Active ETF,Fixed Income,51249500.0 -MINT,PIMCO Enhanced Short Maturity ETF,Fixed Income,10634952800.0 -MINV,Matthews Asia Innovators Active ETF,Equity,3358900.0 -MISL,First Trust Indxx Aerospace & Defense ETF,Equity,1100000.0 -MJ,ETFMG Alternative Harvest ETF,Equity,399076900.0 -MJIN,ETFMG 2x Daily Inverse Alternative Harvest ETF,Equity,921600.0 -MJUS,ETFMG U.S. Alternative Harvest ETF,Equity,111735600.0 -MJXL,ETFMG 2x Daily Alternative Harvest ETF,Equity,573900.0 -MLN,VanEck Vectors AMT-Free Long Municipal Index ETF,Fixed Income,219240000.0 -MLPA,Global X MLP ETF,Equity,1349225300.0 -MLPX,Global X MLP & Energy Infrastructure ETF,Equity,1065853100.0000001 -MMCA,IQ MacKay California Municipal Intermediate ETF,Fixed Income,44615100.0 -MMIN,IQ MacKay Shields Municipal Insured ETF,Fixed Income,339307600.0 -MMIT,IQ MacKay Shields Municipal Intermediate ETF,Fixed Income,311586600.0 -MMLG,First Trust Multi-Manager Large Growth ETF,Equity,50985100.0 -MMSB,IQ MacKay Multi-Sector Income ETF,Fixed Income,23754000.0 -MMSC,First Trust Multi-Manager Small Cap Opportunities ETF,Equity,2265500.0 -MMTM,SPDR S&P 1500 Momentum Tilt ETF,Equity,76811900.0 -MNA,IQ Merger Arbitrage ETF,Alternatives,558654500.0 -MNBD,ALPS Intermediate Municipal Bond ETF,Fixed Income,30817900.0 -MNM,Direxion Daily Metal Miners Bull 2X Shares,Equity,13480200.0 -MOAT,VanEck Vectors Morningstar Wide Moat ETF,Equity,6641676000.0 -MODL,VictoryShares WestEnd U.S. Sector ETF,Equity,103226600.0 -MOHR,Mohr Growth ETF,Equity,119872200.0 -MOO,VanEck Vectors Agribusiness ETF,Equity,1481844000.0 -MOOD,Relative Sentiment Tactical Allocation ETF,Asset Allocation,4258600.0 -MOON,Direxion Moonshot Innovators ETF,Equity,34933800.0 -MORT,VanEck Vectors Mortgage REIT Income ETF,Equity,169337500.0 -MOTE,VanEck Morningstar ESG Moat ETF,Equity,3886600.0 -MOTG,VanEck Morningstar Global Wide Moat ETF,Equity,20345100.0 -MOTI,VanEck Vectors Morningstar International Moat ETF,Equity,58900000.0 -MOTO,SmartETFs Smart Transportation and Technology ETF,Equity,11286100.0 -MPAY,Akros Monthly Payout ETF,Asset Allocation,2247600.0 -MPRO,Monarch ProCap ETF,Asset Allocation,96482000.0 -MRAD,SmartETFs Advertising & Marketing Technology ETF,Equity,1911600.0 -MRGR,ProShares Merger ETF,Alternatives,25535300.0 -MRND,IQ U.S. Mid Cap R&D Leaders ETF,Equity,4177600.0000000005 -MRSK,Agility Shares Managed Risk ETF,Asset Allocation,28130700.0 -MSFD,Direxion Daily MSFT Bear 1X Shares,Equity,3713000.0 -MSFU,Direxion Daily MSFT Bull 1.5X Shares,Equity,3549000.0 -MSGR,Direxion mRNA ETF,Equity,3469000.0 -MSMR,McElhenny Sheffield Managed Risk ETF,Asset Allocation,32444700.0 -MSOS,AdvisorShares Pure US Cannabis ETF,Equity,722698500.0 -MSOX,AdvisorShares MSOS 2x Daily ETF,Equity,4937400.0 -MSTB,Lha Market State Tactical Beta ETF,Equity,206448000.0 -MSTQ,LHA Market State Tactical Q ETF,Equity,3669100.0 -MSVX,LHA Market State Alpha Seeker ETF,Alternatives,44439000.0 -MTGP,WisdomTree Mortgage Plus Bond Fund,Fixed Income,21772700.0 -MTUM,iShares Edge MSCI USA Momentum Factor ETF,Equity,12518176800.0 -MTVR,Fount Metaverse ETF,Equity,6569300.0 -MUB,iShares National Muni Bond ETF,Fixed Income,29834966400.0 -MUNI,PIMCO Intermediate Municipal Bond Active ETF,Fixed Income,866929500.0 -MUSI,American Century Multisector Income ETF,Fixed Income,111211900.0 -MUST,Columbia Multi-Sector Municipal Income ETF,Fixed Income,231188000.0 -MVPS,Amplify Thematic All-Stars ETF,Equity,4894600.0 -MVV,ProShares Ultra MidCap400,Equity,130792900.0 -MXI,iShares Global Materials ETF,Equity,295018900.0 -MYY,ProShares Short MidCap400,Equity,13546700.0 -MZZ,ProShares UltraShort MidCap400,Equity,2513000.0 -NACP,NAACP Minority Empowerment ETF,Equity,37556900.0 -NAIL,Direxion Daily Homebuilders & Supplies Bull 3X Shares,Equity,150340400.0 -NANR,SPDR S&P North American Natural Resources ETF,Equity,620318600.0 -NAPR,Innovator Nasdaq-100 Power Buffer ETF (April),Equity,53988600.0 -NBCC,Neuberger Berman Next Generation Connected Consumer ETF,Equity,6250000.0 -NBCM,Neuberger Berman Commodity Strategy ETF,Commodity,211156700.0 -NBCT,Neuberger Berman Carbon Transition Infrastructure ETF,Equity,5500000.0 -NBDS,Neuberger Berman Disrupters ETF,Equity,7000000.0 -NDIV,Amplify Natural Resources Dividend Income ETF,Equity,2139600.0 -NDJI,Nationwide Dow Jones Risk-Managed Income ETF,Equity,29665100.0 -NDVG,Nuveen Dividend Growth ETF,Equity,8147600.0 -NEAR,iShares Short Maturity Bond ETF,Fixed Income,4290394400.0000005 -NERD,Roundhill BITKRAFT Esports & Digital Entertainment ETF,Equity,26553300.0 -NETL,NETLease Corporate Real Estate ETF,Equity,93225700.0 -NETZ,Engine No. 1 Transform Climate ETF,Equity,92988000.0 -NFLT,Virtus Newfleet Multi-Sector Unconstrained Bond ETF,Fixed Income,36001300.0 -NFRA,FlexShares STOXX Global Broad Infrastructure Index Fund,Equity,2676178800.0 -NFTY,First Trust India NIFTY 50 Equal Weight ETF,Equity,47663500.0 -NFTZ,Defiance Digital Revolution ETF,Equity,4543500.0 -NGE,Global X MSCI Nigeria ETF,Equity,40432700.0 -NIWM,NightShares 2000 ETF,Equity,2333600.0 -NJAN,Nasdaq-100 Power Buffer ETF (January),Equity,86292000.0 -NJUL,Nasdaq-100 Power Buffer ETF (July),Equity,120523200.0 -NKEL,AXS 2X NKE Bull Daily ETF,Equity,1438500.0 -NKEQ,AXS 2X NKE Bear Daily ETF,Equity,1889800.0 -NLR,VanEck Vectors Uranium Plus Nuclear Energy ETF,Equity,56675000.0 -NOBL,ProShares S&P 500 Dividend Aristocrats ETF,Equity,11168661600.0 -NOCT,Innovator Nasdaq-100 Power Buffer ETF (October),Equity,83536000.0 -NOPE,Noble Absolute Return ETF,Alternatives,16370700.0 -NORW,Global X MSCI Norway ETF,Equity,105844200.0 -NOVZ,TrueShares Structured Outcome (November) ETF,Equity,10896200.0 -NSCS,Nuveen Small Cap Select ETF,Equity,5784200.0 -NSPI,Nationwide S&P 500 Risk-Managed Income ETF,Equity,27837600.0 -NSPL,NightShares 500 1x\u002F1.5x ETF,Equity,1637500.0 -NSPY,NightShares 500 ETF,Equity,4004000.0 -NTKI,Nationwide Russell 2000 Risk-Managed Income ETF,Equity,13098900.0 -NTSE,WisdomTree Emerging Markets Efficient Core Fund,Asset Allocation,30949300.0 -NTSI,WisdomTree International Efficient Core Fund,Asset Allocation,145462700.0 -NTSX,WisdomTree 90\u002F60 U.S. Balanced Fund,Asset Allocation,680494500.0 -NTZG,Nuveen Global Net Zero Transition ETF,Equity,5384600.0 -NUAG,NuShares Enhanced Yield U.S. Aggregate Bond ETF,Fixed Income,37260000.0 -NUBD,NuShares ESG U.S. Aggregate Bond ETF,Fixed Income,239564600.0 -NUDM,NuShares ESG International Developed Markets Equity ETF,Equity,302329100.0 -NUDV,Nuveen ESG Dividend ETF,Equity,8778900.0 -NUEM,NuShares ESG Emerging Markets Equity ETF,Equity,197964000.0 -NUGO,Nuveen Growth Opportunities ETF,Equity,2375433800.0 -NUGT,Direxion Daily Gold Miners Bull 2X Shares,Equity,503667800.0 -NUHY,Nuveen ESG High Yield Corporate Bond ETF,Fixed Income,95794000.0 -NULC,Nuveen ESG Large-Cap ETF,Equity,25782000.0 -NULG,NuShares ESG Large-Cap Growth ETF,Equity,824960000.0 -NULV,NuShares ESG Large-Cap Value ETF,Equity,1627206000.0 -NUMG,NuShares ESG Mid-Cap Growth ETF,Equity,326690000.0 -NUMV,NuShares ESG Mid-Cap Value ETF,Equity,326600000.0 -NURE,NuShares Short-Term REIT ETF,Equity,103686000.0 -NUSA,NuShares Enhanced Yield 1-5 Year U.S. Aggregate Bond ETF,Fixed Income,34027200.0 -NUSC,NuShares ESG Small-Cap ETF,Equity,936960000.0 -NUSI,Nationwide Risk-Managed Income ETF,Equity,522644700.0 -NVBT,AllianzIM U.S. Large Cap Buffer10 Nov ETF,Equity,10954800.0 -NVBW,AllianzIM U.S. Large Cap Buffer20 Nov ETF,Equity,29300800.0 -NVDS,AXS 1.25X NVDA Bear Daily ETF,Equity,3739300.0 -NVQ,QRAFT AI-Enhanced U.S. Next Value ETF,Equity,5640000.0 -NWLG,Nuveen Winslow Large-Cap Growth ESG ETF,Equity,4622300.0 -NXTE,AXS Green Alpha ETF,Equity,52425300.0 -NXTG,First Trust NASDAQ Smartphone Index Fund,Equity,507870000.0 -NYF,iShares New York Muni Bond ETF,Fixed Income,513382000.0 -NZAC,SPDR MSCI ACWI Climate Paris Aligned ETF,Equity,229925000.0 -NZRO,Strategy Shares Halt Climate Change ETF,Equity,1092000.0 -NZUS,SPDR MSCI USA Climate Paris Aligned ETF,Equity,91248000.0 -OACP,OneAscent Core Plus Bond ETF,Fixed Income,101460000.0 -OAEM,OneAscent Emerging Markets ETF,Equity,755100.0 -OAIE,Optimize AI Smart Sentiment EventDriven ETF,Alternatives,517400.0 -OAIM,OneAscent International Equity ETF,Equity,800100.0 -OALC,OneAscent Large Cap Core ETF,Equity,58602400.0 -OARK,YieldMax Innovation Option Income Strategy ETF,0,500100.0 -OBIL,US Treasury 12 Month Bill ETF,Fixed Income,5509400.0 -OBND,SPDR Loomis Sayles Opportunistic Bond ETF,Fixed Income,28325000.0 -OBOR,KraneShares MSCI One Belt One Road ETF,Equity,4766000.0 -OCEN,IQ Clean Oceans ETF,Equity,5887700.0 -OCIO,ClearShares OCIO ETF,Asset Allocation,124366000.0 -OCTT,AllianzIM U.S. Large Cap Buffer10 Oct ETF,Equity,66367399.99999999 -OCTW,AllianzIM U.S. Large Cap Buffer20 Oct ETF,Equity,159308100.0 -OCTZ,TrueShares Structured Outcome (October) ETF,Equity,6075000.0 -ODDS,Pacer BlueStar Digital Entertainment ETF,Equity,658400.0 -OEF,iShares S&P 100 ETF,Equity,7929700800.0 -OEUR,O'Shares FTSE Europe Quality Dividend ETF,Equity,41038000.0 -OGIG,O'Shares Global Internet Giants ETF,Equity,188557500.0 -OIH,VanEck Vectors Oil Services ETF,Equity,2627807000.0 -OILK,ProShares K-1 Free Crude Oil Strategy ETF,Commodity,85272500.0 -OMFL,Oppenheimer Russell 1000 Dynamic Multifactor ETF,Equity,2167653000.0 -OMFS,Oppenheimer Russell 2000 Dynamic Multifactor ETF,Equity,161606400.0 -OND,ProShares On-Demand ETF,Equity,963900.0 -ONEO,SPDR Russell 1000 Momentum Focus ETF,Equity,307594500.0 -ONEQ,Fidelity Nasdaq Composite Index ETF,Equity,3903638800.0 -ONEV,SPDR Russell 1000 Low Volatility Focus ETF,Equity,618705000.0 -ONEY,SPDR Russell 1000 Yield Focus ETF,Equity,903076400.0 -ONG,Direxion Daily Oil Services Bull 2X Shares,Equity,16592200.0 -ONLN,ProShares Online Retail ETF,Equity,122092300.0 -ONOF,Global X Adaptive U.S. Risk Management ETF,Asset Allocation,64124000.0 -OOTO,Direxion Daily Travel & Vacation Bull 2X Shares,Equity,25046500.0 -OPER,ClearShares Ultra-Short Maturity ETF,Fixed Income,261058700.0 -OPPX,Corbett Road Tactical Opportunity ETF,Asset Allocation,19812800.0 -ORFN,Constrained Capital ESG Orphans ETF,Equity,2076600.0 -OSCV,Opus Small Cap Value Plus ETF,Equity,220146500.0 -OSEA,Harbor International Compounders ETF,Equity,7077100.0 -OUNZ,Vaneck Merk Gold Trust,Commodity,563450400.0 -OUSA,O'Shares FTSE US Quality Dividend ETF,Equity,788922000.0 -OUSM,O'Shares FTSE Russell US Small Cap Quality Dividend ETF,Equity,169653000.0 -OVB,Overlay Shares Core Bond ETF,Fixed Income,53926900.0 -OVF,Overlay Shares Foreign Equity ETF,Equity,12847900.0 -OVL,Overlay Shares Large Cap Equity ETF,Equity,254693600.0 -OVLH,Overlay Shares Hedged Large Cap Equity ETF,Equity,23480600.0 -OVM,Overlay Shares Municipal Bond ETF,Fixed Income,15280300.0 -OVS,Overlay Shares Small Cap Equity ETF,Equity,7765100.0 -OVT,Overlay Shares Short Term Bond ETF,Fixed Income,55823600.0 -OWNS,Impact Shares Affordable Housing MBS ETF,Fixed Income,95457800.0 -PAB,PGIM Active Aggregate Bond ETF,Fixed Income,41782600.0 -PABU,iShares Paris-Aligned Climate MSCI USA ETF,Equity,821163400.0 -PAK,Global X MSCI Pakistan ETF,Equity,14859600.0 -PALC,Pacer Lunt Large Cap Multi-Factor Alternator ETF,Equity,201429600.0 -PALL,Aberdeen Standard Physical Palladium Shares ETF,Commodity,313166500.0 -PAMC,Pacer Lunt Mid Cap Multi-Factor Alternator ETF,Equity,35909400.0 -PAPR,Innovator S&P 500 Power Buffer ETF (April),Equity,316168300.0 -PAUG,Innovator S&P 500 Power Buffer ETF (August),Equity,448050000.0 -PAVE,Global X U.S. Infrastructure Development ETF,Equity,3762835200.0 -PAWZ,ProShares Pet Care ETF,Equity,120978100.0 -PBD,Invesco Global Clean Energy ETF,Equity,230507700.0 -PBDC,Putnam BDC Income ETF,Equity,5811000.0 -PBDM,Invesco PureBeta FTSE Developed ex-North America ETF,Equity,86726500.0 -PBE,Invesco Dynamic Biotechnology & Genome ETF,Equity,256533800.0 -PBEE,Invesco PureBeta FTSE Emerging Markets ETF,Equity,14931000.0 -PBJ,Invesco Dynamic Food & Beverage ETF,Equity,343319000.0 -PBND,Invesco PureBeta US Aggregate Bond ETF,Fixed Income,48237000.0 -PBP,Invesco S&P 500 BuyWrite ETF,Equity,101830500.0 -PBS,Invesco Dynamic Media ETF,Equity,32649600.0 -PBSM,Invesco PureBeta MSCI USA Small Cap ETF,Equity,31943400.0 -PBTP,Invesco PureBeta 0-5 Yr US TIPS ETF,Fixed Income,119312000.0 -PBUS,Invesco PureBeta MSCI USA ETF,Equity,2314510400.0 -PBW,Invesco WilderHill Clean Energy ETF,Equity,933170500.0 -PCEF,Invesco CEF Income Composite ETF,Asset Allocation,693021200.0 -PCY,Invesco Emerging Markets Sovereign Debt ETF,Fixed Income,1548974300.0 -PDBA,Invesco Agriculture Commodity Strategy No K-1 ETF,Commodity,14620000.0 -PDBC,Invesco Optimum Yield Diversified Commodity Strategy No K-1 ETF,Commodity,7285206400.0 -PDEC,Innovator S&P 500 Power Buffer ETF (December),Equity,213062900.0 -PDN,Invesco FTSE RAFI Developed Markets ex-U.S. Small-Mid ETF,Equity,535416400.0 -PDP,Invesco DWA Momentum ETF,Equity,1139516400.0 -PEJ,Invesco Dynamic Leisure and Entertainment ETF,Equity,617333000.0 -PEX,ProShares Global Listed Private Equity ETF,Equity,11959200.0 -PEXL,Pacer US Export Leaders ETF,Equity,8612900.0 -PEY,Invesco High Yield Equity Dividend Achievers ETF,Equity,1446676000.0 -PEZ,Invesco DWA Consumer Cyclicals Momentum ETF,Equity,25528400.0 -PFEB,Innovator S&P 500 Power Buffer ETF (February),Equity,246006900.0 -PFEL,AXS 2X PFE Bull Daily ETF,Equity,1423700.0 -PFES,AXS 2X PFE Bear Daily ETF,Equity,2323200.0 -PFF,iShares Preferred and Income Securities ETF,Fixed Income,14241728000.0 -PFFA,Virtus InfraCap US Preferred Stock ETF,Fixed Income,467357100.0 -PFFD,Global X U.S. Preferred ETF,Fixed Income,2056892400.0 -PFFR,InfraCap REIT Preferred ETF,Fixed Income,56548100.0 -PFFV,Global X Variable Rate Preferred ETF,Fixed Income,268997400.0 -PFI,Invesco DWA Financial Momentum ETF,Equity,65709600.00000001 -PFIG,Invesco Fundamental Investment Grade Corporate Bond ETF,Fixed Income,28773600.0 -PFIX,Simplify Interest Rate Hedge ETF,Alternatives,359571900.0 -PFLD,AAM Low Duration Preferred and Income Securities ETF,Fixed Income,156826800.0 -PFM,Invesco Dividend Achievers ETF,Equity,714380900.0 -PFRL,PGIM Floating Rate Income ETF,Fixed Income,46833700.0 -PFUT,Putnam Sustainable Future ETF,Equity,7063200.0 -PFXF,VanEck Vectors Preferred Securities ex Financials ETF,Fixed Income,936106700.0 -PGAL,Global X FTSE Portugal 20 ETF,Equity,16313400.0 -PGF,Invesco Financial Preferred ETF,Fixed Income,1189176300.0 -PGHY,Invesco Global Short Term High Yield Bond ETF,Fixed Income,188261900.0 -PGJ,Invesco Golden Dragon China ETF,Equity,207208000.0 -PGRO,Putnam Focused Large Cap Growth ETF,Equity,9525400.0 -PGX,Invesco Preferred ETF,Fixed Income,5019466400.0 -PHB,Invesco Fundamental High Yield Corporate Bond ETF,Fixed Income,712246100.0 -PHDG,Invesco S&P 500 Downside Hedged ETF,Equity,271234100.0 -PHO,Invesco Water Resources ETF,Equity,1770534000.0 -PHYL,PGIM Active High Yield Bond ETF,Fixed Income,88852700.0 -PICB,Invesco International Corporate Bond ETF,Fixed Income,103558500.0 -PICK,iShares MSCI Global Metals & Mining Producers ETF,Equity,1397020200.0 -PID,Invesco International Dividend Achievers ETF,Equity,865396000.0 -PIE,Invesco DWA Emerging Markets Momentum ETF,Equity,137973500.0 -PIFI,Clearshares Piton Intermediate Fixed Income ETF,Fixed Income,127031000.0 -PILL,Direxion Daily Pharmaceutical & Medical Bull 3X Shares,Equity,10658200.0 -PIN,Invesco India ETF,Equity,98598500.0 -PINK,Simplify Health Care ETF,Equity,45047800.0 -PIO,Invesco Global Water ETF,Equity,280309500.0 -PIZ,Invesco DWA Developed Markets Momentum ETF,Equity,110916000.0 -PJAN,Innovator S&P 500 Power Buffer ETF (January),Equity,462960000.0 -PJP,Invesco Dynamic Pharmaceuticals ETF,Equity,326796600.0 -PJUL,Innovator S&P 500 Power Buffer ETF (July),Equity,518306000.0 -PJUN,Innovator S&P 500 Power Buffer ETF (June),Equity,354218300.0 -PKB,Invesco Dynamic Building & Construction ETF,Equity,115555700.0 -PKW,Invesco BuyBack Achievers ETF,Equity,1606342300.0 -PLAT,WisdomTree Modern Tech Platforms Fund,Equity,9579100.0 -PLDR,Putnam Sustainable Leaders ETF,Equity,6308300.0 -PLRG,Principal U.S. Large-Cap Adaptive Multi-Factor ETF,Equity,6006200.0 -PLTL,Principal U.S. Small-Cap Adaptive Multi-Factor ETF,Equity,5938700.0 -PLTM,GraniteShares Platinum Trust,Commodity,44574400.0 -PLW,Invesco 1-30 Laddered Treasury ETF,Fixed Income,492896000.0 -PMAR,Innovator S&P 500 Power Buffer ETF (March),Equity,233295000.0 -PMAY,Innovator S&P 500 Power Buffer ETF (May),Equity,403517800.0 -PNOV,Innovator S&P 500 Buffer ETF (November),Equity,170711100.0 -PNQI,Invesco NASDAQ Internet ETF,Equity,435145900.0 -POCT,Innovator S&P 500 Power Buffer ETF (October),Equity,666417000.0 -POTX,Global X Cannabis ETF,Equity,53070000.0 -PPA,Invesco Aerospace & Defense ETF,Equity,1646391300.0 -PPH,VanEck Vectors Pharmaceutical ETF,Equity,579595300.0 -PPI,AXS Astoria Inflation Sensitive ETF,Alternatives,66212800.0 -PPLT,Aberdeen Standard Physical Platinum Shares ETF,Commodity,1072354500.0 -PPTY,U.S. Diversified Real Estate ETF,Equity,120733900.0 -PQDI,Principal Spectrum Tax-Advantaged Dividend Active ETF,Fixed Income,20855300.0 -PQIN,PGIM QMA Strategic Alpha International Equity ETF,Equity,33955700.0 -PRAY,FIS Biblically Responsible Risk Managed ETF,Equity,20458300.0 -PREF,Principal Spectrum Preferred Securities Active ETF,Fixed Income,346181500.0 -PRF,Invesco FTSE RAFI US 1500 Small- Mid ETF,Equity,6033793200.0 -PRFZ,Invesco FTSE RAFI US 1500 Small- Mid ETF,Equity,2039475200.0 -PRN,Invesco DWA Industrials Momentum ETF,Equity,113526100.0 -PRNT,The 3D Printing ETF,Equity,185281800.0 -PSC,Principal U.S. Small Cap Index ETF,Equity,247103500.0 -PSCC,Invesco S&P SmallCap Consumer Staples ETF,Equity,62420700.0 -PSCD,Invesco S&P SmallCap Consumer Discretionary ETF,Equity,25273600.0 -PSCE,Invesco S&P SmallCap Energy ETF,Equity,147936000.0 -PSCF,Invesco S&P SmallCap Financials ETF,Equity,36295000.0 -PSCH,Invesco S&P SmallCap Health Care ETF,Equity,328939900.0 -PSCI,Invesco S&P SmallCap Industrials ETF,Equity,73101400.0 -PSCJ,Pacer Swan SOS Conservative (July) ETF,Equity,6387400.0 -PSCM,Invesco S&P SmallCap Materials ETF,Equity,20306900.0 -PSCQ,Pacer Swan SOS Conservative (October) ETF,Equity,5094300.0 -PSCT,Invesco S&P SmallCap Information Technology ETF,Equity,312223200.0 -PSCU,Invesco S&P SmallCap Utilities ETF,Equity,21023400.0 -PSCW,Pacer Swan SOS Conservative April ETF,Equity,16131500.0 -PSCX,Pacer Swan SOS Conservative (December) ETF,Equity,15708800.0 -PSDN,AdvisorShares Poseidon Dynamic Cannabis ETF,Equity,5135000.0 -PSEP,Innovator S&P 500 Power Buffer ETF (September),Equity,544347800.0 -PSET,Principal Price Setters Index ETF,Equity,36064000.0 -PSFD,Pacer Swan SOS Flex (December) ETF,Equity,17696400.0 -PSFF,Pacer Swan SOS Fund of Funds ETF,Equity,120994200.0 -PSFJ,Pacer Swan SOS Flex (July) ETF,Equity,12870000.0 -PSFM,Pacer Swan SOS Flex April ETF,Equity,12620900.0 -PSFO,Pacer Swan SOS Flex (October) ETF,Equity,11014700.0 -PSI,Invesco Dynamic Semiconductors ETF,Equity,522856300.0 -PSIL,AdvisorShares Psychedelics ETF,Equity,7077600.0 -PSJ,Invesco Dynamic Software ETF,Equity,194464600.0 -PSK,SPDR Wells Fargo Preferred Stock ETF,Fixed Income,1036003500.0 -PSL,Invesco DWA Consumer Staples Momentum ETF,Equity,126114800.0 -PSMB,Invesco Balanced Multi-Asset Allocation ETF,Asset Allocation,37400800.0 -PSMC,Invesco Conservative Multi-Asset Allocation ETF,Asset Allocation,12454300.0 -PSMD,Pacer Swan SOS Moderate (December) ETF,Equity,20952900.0 -PSMG,Invesco Growth Multi-Asset Allocation ETF,Asset Allocation,27604500.0 -PSMJ,Pacer Swan SOS Moderate (July) ETF,Equity,34548000.0 -PSMM,Invesco Moderately Conservative Multi-Asset Allocation ETF,Asset Allocation,22280200.0 -PSMO,Pacer Swan SOS Moderate (October) ETF,Equity,23998600.0 -PSMR,Pacer Swan SOS Moderate April ETF,Equity,18344000.0 -PSP,Invesco Global Listed Private Equity ETF,Equity,183978300.0 -PSQ,ProShares Short QQQ,Equity,1511851800.0 -PSR,Invesco Active U.S. Real Estate ETF,Equity,117277500.0 -PST,ProShares UltraShort 7-10 Year Treasury,Fixed Income,73417300.0 -PSTP,Innovator Power Buffer Step-Up Strategy ETF,Equity,34901400.0 -PSYK,PSYK ETF,Equity,1995600.0 -PTBD,Pacer Trendpilot US Bond ETF,Fixed Income,327410000.0 -PTEU,Pacer Trendpilot European Index ETF,Equity,51569600.0 -PTF,Invesco DWA Technology Momentum ETF,Equity,217816200.0 -PTH,Invesco DWA Healthcare Momentum ETF,Equity,231187200.0 -PTIN,Pacer Trendpilot International ETF,Equity,116049100.0 -PTLC,Pacer Trendpilot 750 ETF,Equity,2124716000.0 -PTMC,Pacer Trendpilot 450 ETF,Equity,533142000.0 -PTNQ,Pacer Trendpilot 100 ETF,Equity,684222300.0 -PTRB,PGIM Total Return Bond ETF,Fixed Income,98933300.0 -PUI,Invesco DWA Utilities Momentum ETF,Equity,81098000.0 -PULS,PGIM Ultra Short Bond ETF,Fixed Income,2767745400.0 -PUNK,Subversive Metaverse ETF,Equity,916800.0 -PUTW,WisdomTree CBOE S&P 500 PutWrite Strategy Fund,Equity,97069500.0 -PVAL,Putnam Focused Large Cap Value ETF,Equity,130134900.0 -PVI,Invesco VRDO Tax-Free Weekly ETF,Fixed Income,69769800.0 -PWB,Invesco Dynamic Large Cap Growth ETF,Equity,570957600.0 -PWC,Invesco Dynamic Market ETF,Equity,103411000.0 -PWS,Pacer WealthShield ETF,Asset Allocation,36678000.0 -PWV,Invesco Dynamic Large Cap Value ETF,Equity,835852900.0 -PWZ,Invesco California AMT-Free Municipal Bond ETF,Fixed Income,582092200.0 -PXE,Invesco Dynamic Energy Exploration & Production ETF,Equity,336019200.0 -PXF,Invesco FTSE RAFI Developed Markets ex-U.S. ETF,Equity,1217443700.0 -PXH,Invesco FTSE RAFI Emerging Markets ETF,Equity,1321461700.0 -PXI,Invesco DWA Energy Momentum ETF,Equity,241865300.0 -PXJ,Invesco Dynamic Oil & Gas Services ETF,Equity,54455500.0 -PXQ,Invesco Dynamic Networking ETF,Equity,41607700.0 -PXUS,Principal International Adaptive Multi-Factor ETF,Equity,12337100.0 -PY,Principal Shareholder Yield Index ETF,Equity,85317200.0 -PYPS,AXS 1.5X PYPL Bear Daily ETF,Equity,1647500.0 -PYPT,AXS 1.5X PYPL Bull Daily ETF,Equity,1737500.0 -PYZ,Invesco DWA Basic Materials Momentum ETF,Equity,118733800.0 -PZA,Invesco National AMT-Free Municipal Bond ETF,Fixed Income,2053947200.0 -PZT,Invesco New York AMT-Free Municipal Bond ETF,Fixed Income,102339100.0 -QABA,First Trust NASDAQ ABA Community Bank Index Fund,Equity,127529300.0 -QAI,IQ Hedge Multi-Strategy Tracker ETF,Alternatives,670362000.0 -QARP,Xtrackers Russell 1000 US QARP ETF,Equity,9692800.0 -QAT,iShares MSCI Qatar ETF,Equity,81560000.0 -QCLN,First Trust NASDAQ Clean Edge Green Energy Index Fund,Equity,2063887400.0 -QCLR,Global X Nasdaq 100 Collar 95-110 ETF,Equity,3150000.0 -QCON,American Century Quality Convertible Securities ETF,Fixed Income,18609300.0 -QDEC,FT Cboe Vest Growth-100 Buffer ETF (December),Equity,68834500.0 -QDEF,FlexShares Quality Divided Defensive Index Fund,Equity,362496000.0 -QDF,FlexShares Quality Dividend Index Fund,Equity,1603055900.0 -QDIV,Global X S&P 500 Quality Dividend ETF,Equity,54663800.0 -QDPL,Pacer Metaurus US Large Cap Dividend Multiplier 400 ETF,Asset Allocation,78429400.0 -QDYN,FlexShares Quality Dividend Dynamic Index Fund,Equity,18777700.0 -QED,IQ Hedge Event-Driven Tracker ETF,Alternatives,6274200.0 -QEFA,SPDR MSCI EAFE StrategicFactors ETF,Equity,801022600.0 -QEMM,SPDR MSCI Emerging Markets StrategicFactors ETF,Equity,41748900.0 -QGRO,American Century STOXX U.S. Quality Growth ETF,Equity,262756200.0 -QID,ProShares UltraShort QQQ,Equity,397644700.0 -QINT,American Century Quality Diversified International ETF,Equity,205835000.0 -QJUN,FT Cboe Vest Growth-100 Buffer ETF - June,Equity,0.0 -QLC,FlexShares US Quality Large Cap Index Fund,Equity,133744200.00000001 -QLD,ProShares Ultra QQQ,Equity,2828456000.0 -QLS,IQ Hedge Long\u002FShort Tracker ETF,Alternatives,22462100.0 -QLTA,iShares AAA - A Rated Corporate Bond ETF,Fixed Income,809621000.0 -QLV,FlexShares US Quality Low Volatility Index Fund,Equity,169333400.0 -QLVD,FlexShares Developed Markets ex-US Quality Low Volatility Index Fund,Equity,66938800.0 -QLVE,FlexShares Emerging Markets Quality Low Volatility Index Fund,Equity,8712400.0 -QMAR,FT Cboe Vest Growth-100 Buffer ETF - March,Equity,44018500.0 -QMN,IQ Hedge Market Neutral Tracker ETF,Alternatives,7559300.0 -QMOM,MomentumShares U.S. Quantitative Momentum ETF,Equity,113515400.0 -QPFF,American Century Quality Preferred ETF,Fixed Income,21867400.0 -QPT,AdvisorShares Q Portfolio Blended Allocation ETF,Asset Allocation,1909600.0 -QPX,AdvisorShares Q Dynamic Growth ETF,Asset Allocation,32451300.0 -QQC,Simplify Growth Equity PLUS Convexity ETF,Equity,4473900.0 -QQD,Simplify Growth Equity PLUS Downside Convexity ETF,Equity,13387800.0 -QQEW,First Trust NASDAQ-100 Equal Weighted Index Fund,Equity,1180242000.0 -QQH,HCM Defender 100 Index ETF,Equity,249642000.0 -QQJG,Invesco ESG NASDAQ Next Gen 100 ETF,Equity,2778300.0 -QQMG,Invesco ESG NASDAQ 100 ETF,Equity,9785000.0 -QQQ,Invesco QQQ Trust,Equity,159298675200.0 -QQQA,ProShares Nasdaq-100 Dorsey Wright Momentum ETF,Equity,12232200.0 -QQQE,Direxion NASDAQ-100 Equal Weighted Index Shares,Equity,738159300.0 -QQQJ,Invesco NASDAQ Next Gen 100 ETF,Equity,813780000.0 -QQQM,Invesco NASDAQ 100 ETF,Equity,5367146000.0 -QQQN,VictoryShares Nasdaq Next 50 ETF,Equity,101475400.0 -QQQS,Invesco NASDAQ Future Gen 200 ETF,Equity,3590900.0 -QQXT,First Trust NASDAQ-100 Ex-Technology Sector Index Fund,Equity,131857600.0 -QRFT,QRAFT AI Enhanced U.S. Large Cap ETF,Equity,6404900.0 -QRMI,Global X Nasdaq 100 Risk Managed Income ETF,Equity,10643600.0 -QSPT,FT Cboe Vest Nasdaq-100 Buffer ETF - September,Equity,42360000.0 -QSWN,Amplify BlackSwan Tech & Treasury ETF,Asset Allocation,2939800.0 -QTAP,Innovator Growth Accelerated Plus ETF,Equity,7282400.0 -QTEC,First Trust NASDAQ-100 Technology Sector Index Fund,Equity,1498112000.0 -QTJA,Innovator Growth Accelerated Plus ETF - January,Equity,4563200.0 -QTJL,Innovator Growth Accelerated Plus ETF (July),Equity,18775500.0 -QTOC,Innovator Growth Accelerated Plus ETF (Oct),Equity,6165200.0 -QTR,Global X Nasdaq 100 Tail Risk ETF,Equity,1957500.0 -QTUM,Defiance Quantum ETF,Equity,112918000.0 -QUAL,iShares Edge MSCI USA Quality Factor ETF,Equity,18744888000.0 -QUS,SPDR MSCI USA StrategicFactors ETF,Equity,892422000.0 -QVAL,U.S. Quantitative Value ETF,Equity,236704100.0 -QVML,Invesco S&P 500 QVM Multi-factor ETF,Equity,743654700.0 -QVMM,Invesco S&P MidCap 400 QVM Multi-factor ETF,Equity,261088900.0 -QVMS,Invesco S&P SmallCap 600 QVM Multi-factor ETF,Equity,104850900.0 -QWLD,SPDR MSCI World StrategicFactors ETF,Equity,67691600.0 -QYLD,Horizons NASDAQ 100 Covered Call ETF,Equity,6534762800.0 -QYLG,Global X Nasdaq 100 Covered Call & Growth ETF,Equity,61670100.0 -RAAX,VanEck Vectors Real Asset Allocation ETF,Alternatives,142440200.0 -RAFE,PIMCO RAFI ESG U.S. ETF,Equity,27884000.0 -RATE,Global X Interest Rate Hedge ETF,Alternatives,3893500.0 -RAVI,FlexShares Ready Access Variable Income FundBarclays ETN Schiller CAPE ETN,Fixed Income,856992500.0 -RAYC,Rayliant Quantamental China Equity ETF,Equity,97832000.0 -RAYD,Rayliant Quantitative Developed Market Equity ETF,Equity,18352000.0 -RAYE,Rayliant Quantamental Emerging Market Equity ETF,Equity,20230000.0 -RAYS,Global X Solar ETF,Equity,7084800.0 -RBLD,First Trust Alerian US NextGen Infrastructure ETF,Equity,7316800.0 -RBND,SPDR Bloomberg SASB Corporate Bond ESG Select ETF,Fixed Income,22331700.0 -RCD,Invesco S&P 500 Equal Weight Consumer Discretionary ETF,Equity,378497600.0 -RDFI,Rareview Dynamic Fixed Income ETF,Fixed Income,39745800.0 -RDIV,Invesco S&P Ultra Dividend Revenue ETF,Equity,892031200.0 -RDMX,SPDR Bloomberg SASB Developed Markets Ex US ESG Select ETF,Equity,14378200.0 -RDOG,ALPS REIT Dividend Dogs ETF,Equity,21069800.0 -RDVI,FT Cboe Vest Rising Dividend Achievers Target Income ETF,Equity,0.0 -RDVY,First Trust NASDAQ Rising Dividend Achievers ETF,Equity,8606715200.0 -REC,Emles Real Estate Credit ETF,Fixed Income,2005500.0 -RECS,Columbia Research Enhanced Core ETF,Equity,81321200.0 -REET,iShares Global REIT ETF,Equity,2836419800.0 -REGL,ProShares S&P MidCap 400 Dividend Aristocrats ETF,Equity,1460256600.0 -REIT,ALPS Active REIT ETF,Equity,17346000.0 -REK,ProShares Short Real Estate,Equity,66249300.0 -REM,iShares Mortgage Real Estate ETF,Equity,660248200.0 -REMG,SPDR Bloomberg SASB Emerging Markets ESG Select ETF,Equity,13461100.0 -REMX,VanEck Vectors Rare Earth\u002FStrategic Metals ETF,Equity,741139900.0 -RENW,Harbor Energy Transition Strategy ETF,Commodity,22195700.0 -RESD,WisdomTree Dynamic Currency Hedged International Quality Dividend Growth Fund,Equity,7631400.0 -RESE,WisdomTree Emerging Markets Dividend Fund,Equity,24465900.0 -RESI,Kelly Residential & Apartment Real Estate ETF,Equity,327300.0 -RESP,WisdomTree Total Earnings Fund,Equity,71030500.0 -RETL,Direxion Daily Retail Bull 3X Shares,Equity,57964900.0 -REVS,Columbia Research Enhanced Value ETF,Equity,18592900.0 -REW,ProShares UltraShort Technology,Equity,9948600.0 -REZ,iShares Residential Real Estate Capped ETF,Equity,692493700.0 -RFCI,RiverFront Dynamic Core Income ETF,Fixed Income,47274300.0 -RFDA,RiverFront Dynamic US Dividend Advantage ETF,Equity,94011800.0 -RFDI,First Trust RiverFront Dynamic Developed International ETF,Equity,132688400.0 -RFEM,First Trust RiverFront Dynamic Emerging Markets ETF,Equity,31278000.0 -RFEU,First Trust RiverFront Dynamic Europe ETF,Equity,20191500.0 -RFFC,RiverFront Dynamic US Flex-Cap ETF,Equity,28638700.0 -RFG,Invesco S&P MidCap 400 Pure Growth ETF,Equity,278991000.0 -RFV,Invesco S&P MidCap 400 Pure Value ETF,Equity,157864500.0 -RGI,Invesco S&P 500 Equal Weight Industrials ETF,Equity,333656300.0 -RHCB,BNY Mellon Responsible Horizons Corporate Bond ETF,Fixed Income,22543600.0 -RHRX,RH Tactical Rotation ETF,Asset Allocation,24709800.0 -RHS,Invesco S&P 500 Equal Weight Consumer Staples ETF,Equity,679144900.0 -RHTX,RH Tactical Outlook ETF,Asset Allocation,22711000.0 -RIET,Hoya Capital High Dividend Yield ETF,Equity,26093700.0 -RIGS,Riverfront Strategic Income Fund,Fixed Income,100334200.0 -RIGZ,Viridi Cleaner Energy Crypto-Mining & Semiconductor ETF,Equity,3569100.0 -RINF,ProShares Inflation Expectations ETF,Alternatives,72813400.0 -RING,iShares MSCI Global Gold Miners ETF,Equity,398034000.0 -RISN,Inspire Tactical Balanced ESG ETF,Asset Allocation,106618100.0 -RISR,FolioBeyond Rising Rates ETF,Fixed Income,82410000.0 -RITA,ETFB Green SRI REITs ETF,Equity,11902800.0 -RLY,SPDR SSGA Multi-Asset Real Return ETF,Alternatives,562523700.0 -RNDM,First Trust Developed International Equity Select ET,Equity,18300000.0 -RNDV,First Trust US Equity Dividend Select ETF,Equity,20541500.0 -RNEM,First Trust Emerging Markets Equity Select ETF,Equity,8402900.0 -RNEW,VanEck Green Infrastructure ETF,Equity,1389600.0 -RNLC,First Trust Large Cap US Equity Select ETF,Equity,20303000.0 -RNMC,First Trust Mid Cap US Equity Select ETF,Equity,18382300.0 -RNRG,Global X Renewable Energy Producers ETF,Equity,95489000.0 -RNSC,First Trust Small Cap US Equity Select ETF,Equity,29647900.0 -ROAM,Hartford Multifactor Emerging Markets ETF,Equity,27646100.0 -ROBO,ROBO Global Robotics & Automation Index ETF,Equity,1236109000.0 -ROBT,First Trust Nasdaq Artificial Intelligence and Robotics ETF,Equity,195236500.0 -ROCI,Return on Character ETF,Equity,5908400.0 -RODE,Hartford Multifactor Diversified International ETF,Equity,17044200.0 -RODM,Hartford Multifactor Developed Markets (ex-US) ETF,Equity,1417318500.0 -ROKT,SPDR Kensho Final Frontiers ETF,Equity,18100400.0 -ROM,ProShares Ultra Technology,Equity,342145500.0 -ROMO,Strategy Shares Newfound\u002FReSolve Robust Momentum ETF,Asset Allocation,51817900.0 -ROOF,IQ US Real Estate Small Cap ETF,Equity,29539800.0 -RORO,ATAC US Rotation ETF,Asset Allocation,7392000.0 -ROSC,Hartford Multifactor Small Cap ETF,Equity,32347600.0 -ROUS,Hartford Multifactor US Equity ETF,Equity,428460500.0 -RPAR,RPAR Risk Parity ETF,Alternatives,1159583000.0 -RPG,Invesco S&P 500 Pure Growth ETF,Equity,2394198000.0 -RPHS,Regents Park Hedged Market Strategy ETF,Equity,160661000.0 -RPV,Invesco S&P 500 Pure Value ETF,Equity,3509709600.0 -RRH,Advocate Rising Rate Hedge ETF,Asset Allocation,46480000.0 -RSEE,Rareview Systematic Equity ETF,Equity,0.0 -RSP,Invesco S&P 500 Equal Weight ETF,Equity,32430780800.0 -RSPE,Invesco ESG S&P 500 Equal Weight ETF,Equity,9034600.0 -RSPY,Revere Sector Opportunity ETF,Equity,6722200.0 -RTAI,Rareview Tax Advantaged Income ETF,Fixed Income,16636200.0 -RTH,VanEck Vectors Retail ETF,Equity,173695800.0 -RTM,Invesco S&P 500 Equal Weight Materials ETF,Equity,378316100.0 -RTYD,Simplify US Small Cap PLUS Downside Convexity ETF,Equity,2649800.0 -RUFF,Alpha Dog ETF,Equity,63621900.0 -RULE,Adaptive Core ETF,Asset Allocation,68764400.0 -RVNU,Deutsche X-trackers Municipal Infrastructure Revenue Bond ETF,Fixed Income,128637900.0 -RVRB,Reverb ETF,Equity,0.0 -RWJ,Oppenheimer Small Cap Revenue ETF,Equity,926748200.0 -RWK,Oppenheimer Mid Cap Revenue ETF,Equity,383596200.0 -RWL,Oppenheimer Large Cap Revenue ETF,Equity,1586919400.0 -RWM,ProShares Short Russell2000,Equity,533136800.00000006 -RWO,SPDR Dow Jones Global Real Estate ETF,Equity,1198293900.0 -RWR,SPDR Dow Jones REIT ETF,Equity,1572183200.0 -RWX,SPDR Dow Jones International Real Estate ETF,Equity,492324600.0 -RXD,ProShares UltraShort Health Care,Equity,3385500.0 -RXI,iShares Global Consumer Discretionary ETF,Equity,251947600.0 -RXL,ProShares Ultra Health Care,Equity,95151300.0 -RYE,Invesco S&P 500 Equal Weight Energy ETF,Equity,603920300.0 -RYF,Invesco S&P 500 Equal Weight Financials ETF,Equity,436151900.0 -RYH,Invesco S&P 500 Equal Weight Health Care ETF,Equity,965715600.0 -RYJ,Invesco Raymond James SB-1 Equity ETF,Equity,122550200.0 -RYLD,Global X Russell 2000 Covered Call ETF,Equity,1329594900.0 -RYLG,Russell 2000 Covered Call & Growth ETF,Equity,2662500.0 -RYT,Invesco S&P 500 Equal Weight Technology ETF,Equity,2096231600.0 -RYU,Invesco S&P 500 Equal Weight Utilities ETF,Equity,410697000.0 -RZG,Invesco S&P SmallCap 600 Pure Growth ETF,Equity,108687200.0 -RZV,Invesco S&P SmallCap 600 Pure Value ETF,Equity,273342700.0 -SAA,ProShares Ultra S&P SmallCap600,Equity,31383100.0 -SAEF,Schwab Ariel ESG ETF,Equity,12437000.0 -SAGP,Strategas Global Policy Opportunities ETF,Equity,17842500.0 -SAMT,Strategas Macro Thematic Opportunities ETF,Equity,72598800.0 -SARK,AXS Short Innovation Daily ETF,Equity,340290000.0 -SATO,Invesco Alerian Galaxy Crypto Economy ETF,Equity,2161100.0 -SAVN,LifeGoal General Conservative Investment ETF,Asset Allocation,3087000.0 -SBB,ProShares Short SmallCap600,Equity,12652100.0 -SBIO,ALPS Medical Breakthroughs ETF,Equity,98918600.0 -SBND,Columbia Short Duration Bond ETF,Fixed Income,48299000.0 -SCC,ProShares UltraShort Consumer Services,Equity,5527000.0 -SCHA,Schwab U.S. Small-Cap ETF,Equity,14793041600.0 -SCHB,Schwab U.S. Broad Market ETF,Equity,20987844800.0 -SCHC,Schwab International Small-Cap Equity ETF,Equity,3388936800.0 -SCHD,Schwab U.S. Dividend Equity ETF,Equity,42815910400.0 -SCHE,Schwab Emerging Markets Equity ETF,Equity,7981385600.0 -SCHF,Schwab International Equity ETF,Equity,28574857600.0 -SCHG,Schwab U.S. Large-Cap Growth ETF,Equity,14075012800.0 -SCHH,Schwab U.S. REIT ETF,Equity,5694696400.0 -SCHI,Schwab 5-10 Year Corporate Bond ETF,Fixed Income,448462000.0 -SCHJ,Schwab 1-5 Year Corporate Bond ETF,Fixed Income,351637500.0 -SCHK,Schwab 1000 Index ETF,Equity,2385343200.0 -SCHM,Schwab U.S. Mid-Cap ETF,Equity,9559560800.0 -SCHO,Schwab Short-Term U.S. Treasury ETF,Fixed Income,9972144800.0 -SCHP,Schwab U.S. TIPS ETF,Fixed Income,14375934400.0 -SCHQ,Schwab Long-Term U.S. Treasury ETF,Fixed Income,129255500.0 -SCHR,Schwab Intermediate-Term U.S. Treasury ETF,Fixed Income,7788970400.0 -SCHV,Schwab U.S. Large-Cap Value ETF,Equity,10207818400.0 -SCHX,Schwab U.S. Large-Cap ETF,Equity,30507328000.0 -SCHY,Schwab International Dividend Equity ETF,Equity,514136300.0 -SCHZ,Schwab U.S. Aggregate Bond ETF,Fixed Income,6749919200.0 -SCJ,iShares MSCI Japan Small-Cap ETF,Equity,64049000.0 -SCMB,Schwab Municipal Bond ETF,Fixed Income,38286500.0 -SCO,ProShares UltraShort Bloomberg Crude Oil,Commodity,355354600.0 -SCRD,Janus Henderson Sustainable Corporate Bond ETF,Fixed Income,32616000.0 -SCZ,iShares MSCI EAFE Small-Cap ETF,Equity,10623705600.0 -SDCI,USCF SummerHaven Dynamic Commodity Strategy No K-1 Fund,Commodity,10129200.0 -SDD,ProShares UltraShort SmallCap600,Equity,4162800.0 -SDEF,Sound Enhanced Fixed Income ETF,Fixed Income,22717400.0 -SDEI,Sound Equity Income ETF,Equity,22914100.0 -SDEM,Global X MSCI SuperDividend Emerging Markets ETF,Equity,42900000.0 -SDG,iShares Sustainable MSCI Global Impact ETF,Equity,426600000.0 -SDGA,Impact Shares Sustainable Development Goals Equity ETF,Equity,6860500.0 -SDIV,Global X SuperDividend ETF,Equity,720720000.0 -SDOG,ALPS Sector Dividend Dogs ETF,Equity,1280028900.0 -SDOW,ProShares UltraPro Short Dow30,Equity,427915800.0 -SDP,ProShares UltraShort Utilites,Equity,3594800.0 -SDS,ProShares UltraShort S&P500,Equity,852317900.0 -SDSI,American Century Short Duration Strategic Income ETF,Fixed Income,10145000.0 -SDVY,First Trust SMID Cap Rising Dividend Achievers ETF,Equity,983784000.0 -SDY,SPDR S&P Dividend ETF,Equity,25756764800.0 -SEA,U.S. Global Sea to Sky Cargo ETF,Equity,5149900.0 -SECT,Main Sector Rotation ETF,Equity,998022000.0 -SEF,ProShares Short Financials,Equity,32784800.000000004 -SEIM,SEI Enhanced U.S. Large Cap Momentum Factor ETF,Equity,20734600.0 -SEIQ,SEI Enhanced U.S. Large Cap Quality Factor ETF,Equity,10416000.0 -SEIV,SEI Enhanced U.S. Large Cap Value Factor ETF,Equity,17375100.0 -SEIX,Virtus Seix Senior Loan ETF,Fixed Income,84754000.0 -SELV,SEI Enhanced Low Volatility U.S. Large Cap ETF,Equity,10179600.0 -SEMI,Columbia Seligman Semiconductor and Technology ETF,Equity,12815000.0 -SENT,AdvisorShares Alpha DNA Equity Sentiment ETF,Equity,48871200.0 -SEPZ,TrueShares Structured Outcome (September) ETF,Equity,22838500.0 -SESG,Sprott ESG Gold ETF,Commodity,11685700.0 -SFIG,WisdomTree Fundamental U.S. Short-Term Corporate Bond Fund,Fixed Income,37389100.0 -SFLR,Innovator Equity Managed Floor ETF,Equity,7095300.0 -SFY,SoFi Select 500 ETF,Equity,390142200.0 -SFYF,SoFi 50 ETF,Equity,14812600.0 -SFYX,SoFi Next 500 ETF,Equity,48771500.0 -SGDJ,Sprott Junior Gold Miners ETF,Equity,100467500.0 -SGDM,Sprott Gold Miners ETF,Equity,218477200.0 -SGOL,Aberdeen Standard Physical Swiss Gold Shares ETF,Commodity,2317394000.0 -SGOV,iShares 0-3 Month Treasury Bond ETF,Fixed Income,5164358400.0 -SH,ProShares Short S&P500,Equity,2789515400.0 -SHAG,WisdomTree Barclays Yield Enhanced U.S. Short-Term Aggregate Bond Fund,Fixed Income,83842400.0 -SHDG,Soundwatch Hedged Equity ETF,Equity,0.0 -SHE,SPDR SSGA Gender Diversity Index ETF,Equity,215262600.0 -SHM,SPDR Nuveen Bloomberg Barclays Short Term Municipal Bond ETF,Fixed Income,4637909600.0 -SHOC,Strive U.S. Semiconductor ETF,Equity,7317800.0 -SHPP,Pacer Industrials and Logistics ETF,Equity,947300.0 -SHUS,Syntax Stratified U.S. Total Market Hedged ETF,Equity,40871600.0 -SHV,iShares Short Treasury Bond ETF,Fixed Income,24475097600.0 -SHY,iShares 1-3 Year Treasury Bond ETF,Fixed Income,29354688000.0 -SHYD,VanEck Vectors Short High-Yield Municipal Index ETF,Fixed Income,414392000.0 -SHYG,iShares 0-5 Year High Yield Corporate Bond ETF,Fixed Income,6878826400.0 -SHYL,Xtrackers Short Duration High Yield Bond ETF,Fixed Income,75067800.0 -SIFI,Harbor Scientific Alpha Income ETF,Fixed Income,29643400.0 -SIHY,Harbor Scientific Alpha High-Yield ETF,Fixed Income,30059300.0 -SIJ,ProShares UltraShort Industrials,Equity,3541100.0 -SIL,Global X Silver Miners ETF,Equity,968861300.0 -SILJ,PureFunds ISE Junior Silver ETF,Equity,724689800.0 -SILX,ETFMG Prime 2x Daily Junior Silver Miners ETF,Equity,2827700.0 -SIMS,SPDR Kensho Intelligent Structures ETF,Equity,31059300.0 -SIO,Touchstone Strategic Income Opportunities ETF,Fixed Income,98046000.0 -SIVR,Aberdeen Standard Physical Silver Shares ETF,Commodity,992883400.0 -SIXA,6 Meridian Mega Cap Equity ETF,Equity,173511400.0 -SIXH,6 Meridian Hedged Equity - Index Option ETF,Equity,307086000.0 -SIXJ,AllianzIM U.S. Large Cap 6 Month Buffer10 Jan\u002FJul ETF,Equity,27556100.0 -SIXL,6 Meridian Low Beta Equity ETF,Equity,155332800.0 -SIXO,AllianzIM U.S. Large Cap 6 Month Buffer10 Apr\u002FOct ETF,Equity,34758500.0 -SIXS,6 Meridian Small Cap Equity ETF,Equity,63326100.0 -SIZE,iShares Edge MSCI USA Size Factor ETF,Equity,336135600.0 -SJB,ProShares Short High Yield,Fixed Income,337797000.0 -SJNK,SPDR Bloomberg Barclays Short Term High Yield Bond ETF,Fixed Income,3411338000.0 -SKF,ProShares UltraShort Financials,Equity,16573599.999999998 -SKOR,FlexShares Credit-Scored US Corporate Bond Index Fund,Fixed Income,249157000.0 -SKYU,ProShares Ultra Nasdaq Cloud ETF,Equity,1260300.0 -SKYY,First Trust Cloud Computing ETF,Equity,2973061000.0 -SLQD,iShares 0-5 Year Investment Grade Corporate Bond ETF,Fixed Income,3020095000.0 -SLV,iShares Silver Trust,Commodity,10278990400.0 -SLVP,iShares MSCI Global Silver Miners ETF,Equity,193862800.0 -SLX,VanEck Vectors Steel ETF,Equity,101035000.0 -SLY,SPDR S&P 600 Small Cap ETF,Equity,1765562800.0 -SLYG,SPDR S&P 600 Small Cap Growth ETF,Equity,2265605200.0 -SLYV,SPDR S&P 600 Small Cap Value ETF,Equity,4109794000.0 -SMB,VanEck Vectors AMT-Free Short Municipal Index ETF,Fixed Income,337441500.0 -SMCP,AlphaMark Actively Managed Small Cap ETF,Equity,22546600.0 -SMDD,ProShares UltraPro Short MidCap400,Equity,8948600.0 -SMDV,Russell 2000 Dividend Growers ETF,Equity,852932700.0 -SMDY,Syntax Stratified MidCap ETF,Equity,9192300.0 -SMH,VanEck Vectors Semiconductor ETF,Equity,7493264000.0 -SMI,VanEck HIP Sustainable Muni ETF,Fixed Income,17987000.0 -SMIG,AAM Bahl & Gaynor Small\u002FMid Cap Income Growth ETF,Equity,141022200.0 -SMIN,iShares MSCI India Small-Cap ETF,Equity,296352000.0 -SMLE,Xtrackers S&P SmallCap 400 ESG ETF,Equity,10949000.0 -SMLF,iShares Edge MSCI Multifactor USA Small-Cap ETF,Equity,1020545400.0 -SMLV,SPDR SSGA US Small Cap Low Volatility Index ETF,Equity,201041900.0 -SMMD,iShares Russell 2500 ETF,Equity,555712500.0 -SMMU,PIMCO Short-Term Municipal Bond Active ETF,Fixed Income,560045100.0 -SMMV,iShares Edge MSCI Min Vol USA Small-Cap ETF,Equity,791559000.0 -SMN,ProShares UltraShort Basic Materials,Equity,3817800.0 -SMOG,VanEck Vectors Global Alternative Energy ETF,Equity,211517400.0 -SMOT,VanEck Morningstar SMID Moat ETF,Equity,0.0 -SNLN,Highland iBoxx Senior Loan ETF,Fixed Income,30922500.0 -SNPD,S&P ESG Dividend Aristocrats ETF,Equity,0.0 -SNPE,Xtrackers S&P 500 ESG ETF,Equity,714373000.0 -SNPG,S&P 500 Growth ESG ETF,Equity,0.0 -SNPV,S&P 500 Value ESG ETF,Equity,0.0 -SNSR,Global X Internet of Things ETF,Equity,308510000.0 -SNUG,Merlyn.AI Tactical Growth and Income ETF,Asset Allocation,7531000.0 -SOCL,Global X Social Media ETF,Equity,120225500.0 -SOGU,AXS Short De-SPAC Daily ETF,Equity,24165000.0 -SOLR,SmartETFs Sustainable Energy II ETF,Equity,4637900.0 -SOXL,Direxion Daily Semiconductor Bull 3X Shares,Equity,5199450400.0 -SOXQ,Invesco PHLX Semiconductor ETF,Equity,73446600.0 -SOXS,Direxion Daily Semiconductor Bear 3X Shares,Equity,288796600.0 -SOXX,iShares PHLX Semiconductor ETF,Equity,6455800000.0 -SOYB,Teucrium Soybean Fund,Commodity,63674000.0 -SPAB,SPDR Portfolio Aggregate Bond ETF,Fixed Income,6075603600.0 -SPAX,Robinson Alternative Yield Pre-Merger SPAC ETF,Equity,27049100.0 -SPBC,Simplify US Equity PLUS GBTC ETF,Asset Allocation,48520500.0 -SPBO,SPDR Portfolio Corporate Bond ETF,Fixed Income,454072500.0 -SPC,CrossingBridge Pre-Merger SPAC ETF,Equity,64232000.0 -SPCX,SPAC and New Issue ETF,Equity,29582000.0 -SPCZ,RiverNorth Enhanced Pre-Merger SPAC ETF,Equity,3795900.0 -SPD,Simplify US Equity PLUS Downside Convexity ETF,Equity,378332700.0 -SPDN,Direxion Daily S&P 500 Bear 1X Shares,Equity,597510800.0 -SPDV,AAM S&P 500 High Dividend Value ETF,Equity,72065700.0 -SPDW,SPDR Portfolio Developed World ex-US ETF,Equity,13054800000.0 -SPEM,SPDR Portfolio Emerging Markets ETF,Equity,5461512400.0 -SPEU,SPDR Portfolio Europe ETF,Equity,206523600.0 -SPFF,Global X SuperIncome Preferred ETF,Fixed Income,200983200.0 -SPGM,SPDR Portfolio MSCI Global Stock Market ETF,Equity,496128000.0 -SPGP,Invesco S&P 500 GARP ETF,Equity,1517613600.0 -SPHB,Invesco S&P 500 High Beta ETF,Equity,537818900.0 -SPHD,Invesco S&P 500 High Dividend Low Volatility ETF,Equity,3871839200.0 -SPHQ,Invesco S&P 500 Quality ETF,Equity,3674118400.0 -SPHY,SPDR Portfolio High Yield Bond ETF,Fixed Income,733516000.0 -SPIB,SPDR Portfolio Intermediate Term Corporate Bond ETF,Fixed Income,6067693600.0 -SPIP,SPDR Portfolio TIPS ETF,Fixed Income,2413542000.0 -SPKX,ConvexityShares 1x SPIKES Futures ETF,Alternatives,2294300.0 -SPKY,ConvexityShares Daily 1.5x SPIKES Futures ETF,Alternatives,2212200.0 -SPLB,SPDR Portfolio Long Term Corporate Bond ETF,Fixed Income,519743700.0 -SPLG,SPDR Portfolio Large Cap ETF,Equity,14807750400.0 -SPLV,Invesco S&P 500 Low Volatility ETF,Equity,10555777600.0 -SPMB,SPDR Portfolio Mortgage Backed Bond ETF,Fixed Income,3881512000.0 -SPMD,SPDR Portfolio Mid Cap ETF,Equity,5404561600.0 -SPMO,Invesco S&P 500 Momentum ETF,Equity,125381500.0 -SPMV,Invesco S&P 500 Minimum Variance ETF,Equity,22069000.0 -SPRE,SP Funds S&P Global REIT Sharia ETF,Equity,33736500.0 -SPRX,Spear Alpha ETF,Equity,2917600.0 -SPSB,SPDR Portfolio Short Term Treasury ETF,Fixed Income,7153176800.0 -SPSK,SP Funds Dow Jones Global Sukuk ETF,Fixed Income,57737100.0 -SPSM,SPDR Portfolio Small Cap ETF,Equity,4610003200.0 -SPTI,SPDR Portfolio Intermediate Term Treasury ETF,Fixed Income,3763000000.0 -SPTL,SPDR Portfolio Long Term Treasury ETF,Fixed Income,5044984000.0 -SPTM,SPDR Portfolio Total Stock Market ETF,Equity,5840438800.0 -SPTS,SPDR Portfolio Short Term Treasury ETF,Fixed Income,3852632000.0 -SPUC,Simplify US Equity PLUS Upside Convexity ETF,Equity,12053900.0 -SPUS,SP Funds S&P 500 Sharia Industry Exclusions ETF,Equity,165683300.0 -SPUU,Direxion Daily S&P 500 Bull 2X Shares,Equity,53129400.0 -SPVM,Invesco S&P 500 Value with Momentum ETF,Equity,50178700.0 -SPVU,Invesco S&P 500 Enhanced Value ETF,Equity,134874200.0 -SPXB,ProShares S&P 500 Bond ETF,Fixed Income,12683400.0 -SPXE,ProShares S&P 500 Ex-Energy ETF,Equity,28743800.0 -SPXL,Direxion Daily S&P 500 Bull 3X Shares,Equity,2707929000.0 -SPXN,ProShares S&P 500 Ex-Financials ETF,Equity,11025200.0 -SPXS,Direxion Daily S&P 500 Bear 3X Shares,Equity,865616400.0 -SPXT,ProShares S&P 500 Ex-Technology ETF,Equity,10523000.0 -SPXU,ProShares UltraPro Short S&P500,Equity,944581800.0 -SPXV,ProShares S&P 500 Ex-Health Care ETF,Equity,4940200.0 -SPY,SPDR S&P 500 ETF Trust,Equity,383668640000.0 -SPYC,Simplify US Equity PLUS Convexity ETF,Equity,65856700.0 -SPYD,SPDR Portfolio S&P 500 High Dividend ETF,Equity,8195706400.0 -SPYG,SPDR Portfolio S&P 500 Growth ETF,Equity,13169555200.0 -SPYI,NEOS S&P 500 High Income ETF,Equity,961300.0 -SPYV,SPDR Portfolio S&P 500 Value ETF,Equity,13723331200.0 -SPYX,SPDR S&P 500 Fossil Fuel Reserves Free ETF,Equity,1243733200.0 -SQEW,LeaderShares AlphaFactor US Core Equity ETF,Equity,121968500.0 -SQLV,Legg Mason Small-Cap Quality Value ETF,Equity,23747400.0 -SQQQ,ProShares UltraPro Short QQQ,Equity,3515200000.0 -SRET,Global X SuperDividend REIT ETF,Equity,295988500.0 -SRHQ,SRH U.S. Quality ETF,Equity,94017000.0 -SRLN,SPDR Blackstone\u002FGSO Senior Loan ETF,Fixed Income,6160826000.0 -SRS,ProShares UltraShort Real Estate,Equity,60680900.0 -SRTY,ProShares UltraPro Short Russell2000,Equity,180683100.0 -SRVR,Pacer Benchmark Data & Infrastructure Real Estate SCTR ETF,Equity,996762700.0 -SSFI,Day Hagan\u002FNed Davis Research Smart Sector Fixed Income ETF,Fixed Income,29426900.0 -SSG,ProShares UltraShort Semiconductors,Equity,9037100.0 -SSLY,Syntax Stratified SmallCap ETF,Equity,21719900.0 -SSO,ProShares Ultra S&P500,Equity,2965536200.0 -SSPX,Janus Henderson U.S. Sustainable Equity ETF,Equity,20842500.0 -SSPY,Syntax Stratified LargeCap ETF,Equity,107093000.0 -SSUS,Day Hagan\u002FNed Davis Research Smart Sector ETF,Equity,478176300.0 -SSXU,Day Hagan\u002FNed Davis Research Smart Sector International ETF,Equity,7492400.0 -STCE,Schwab Crypto Thematic ETF,Equity,6861800.0 -STGF,Merk Stagflation ETF,Asset Allocation,1794600.0 -STIP,iShares 0-5 Year TIPS Bond ETF,Fixed Income,12086322400.0 -STLG,iShares Factors US Growth Style ETF,Equity,3140400.0 -STLV,iShares Factors US Value Style ETF,Equity,4199500.0 -STNC,Stance Equity ESG Large Cap Core ETF,Equity,44869800.0 -STOT,SPDR DoubleLine Short Duration Total Return Tactical ETF,Fixed Income,128604000.0 -STPZ,PIMCO 1-5 Year U.S. TIPS Index ETF,Fixed Income,1327331800.0 -STRV,Strive 500 ETF,Equity,67688400.0 -STXD,Strive 1000 Dividend Growth ETF,Equity,2628500.0 -STXK,Strive 2000 ETF,Equity,2587500.0 -STXV,Strive 1000 Value ETF,Equity,2590000.0 -SUB,iShares Short-Term National Muni Bond ETF,Fixed Income,9188323200.0 -SUBS,Fount Subscription Economy ETF,Equity,2585200.0 -SUPL,ProShares Supply Chain Logistics ETF,Equity,1764800.0 -SURE,AdvisorShares Insider Advantage ETF,Equity,52358600.0 -SUSA,iShares MSCI USA ESG Select ETF,Equity,3397379200.0 -SUSB,iShares ESG 1-5 Year USD Corporate Bond ETF,Fixed Income,931000000.0 -SUSC,iShares ESG USD Corporate Bond ETF,Fixed Income,858049500.0 -SUSL,iShares ESG MSCI USA Leaders ETF,Equity,3073605000.0 -SVAL,iShares US Small Cap Value Factor ETF,Equity,128730000.0 -SVIX,1x Short VIX Futures ETF,Alternatives,66598399.99999999 -SVOL,Simplify Volatility Premium ETF,Alternatives,124795000.0 -SVXY,ProShares Short VIX Short-Term Futures ETF,Alternatives,392504000.0 -SWAN,Amplify BlackSwan Growth & Treasury Core ETF,Asset Allocation,287116100.0 -SWAR,Direxion Daily Software Bull 2X Shares,Equity,3191200.0 -SWEB,AXS Short China Internet ETF,Equity,1747900.0 -SXQG,6 Meridian Quality Growth Equity ETF,Equity,28655100.0 -SXUS,Janus Henderson International Sustainable Equity ETF,Equity,28140000.0 -SYII,Syntax Stratified Total Market II ETF,Equity,14934900.0 -SYLD,Cambria Shareholder Yield ETF,Equity,638878500.0 -SYNB,Putnam BioRevolution ETF,Equity,4011600.0 -SYUS,Syntax Stratified U.S. Total Market ETF,Equity,9296200.0 -SZK,ProShares UltraShort Consumer Goods,Equity,2584000.0 -SZNE,Pacer CFRA-Stovall Equal Weight Seasonal Rotation ETF,Equity,74815600.0 -TACK,Fairlead Tactical Sector ETF,Asset Allocation,176700000.0 -TAFI,AB Tax-Aware Short Duration Municipal ETF,Fixed Income,27319800.0 -TAGG,T. Rowe Price QM U.S. Bond ETF,Fixed Income,37215600.0 -TAGS,Teucrium Agricultural Fund,Commodity,43858100.0 -TAIL,Cambria Tail Risk ETF,Asset Allocation,328520000.0 -TAN,Invesco Solar ETF,Equity,2616503800.0 -TARK,AXS 2X Innovation ETF,Equity,39780000.0 -TAXF,American Century Diversified Municipal Bond ETF,Fixed Income,277297500.0 -TBF,ProShares Short 20 Plus Year Treasury,Fixed Income,533346199.99999994 -TBIL,US Treasury 3 Month Bill ETF,Fixed Income,119784000.0 -TBJL,Innovator 20 plus Year Treasury Bond 9 Buffer ETF (July),Fixed Income,34281000.0 -TBT,ProShares UltraShort 20 Plus Year Treasury,Fixed Income,975976500.0 -TBUX,T. Rowe Price Ultra Short-Term Bond ETF,Fixed Income,50843300.0 -TBX,ProShares Short 7-10 Year Treasury,Fixed Income,134350500.0 -TCHI,iShares MSCI China Multisector Tech ETF,Equity,6822200.0 -TCHP,T. Rowe Price Blue Chip Growth ETF,Equity,257710400.0 -TDIV,First Trust NASDAQ Technology Dividend Index Fund,Equity,1660579000.0 -TDSA,Cabana Target Drawdown 5 ETF,Asset Allocation,24761400.0 -TDSB,Cabana Target Drawdown 7 ETF,Asset Allocation,194304000.0 -TDSC,Cabana Target Drawdown 10 ETF,Asset Allocation,582069000.0 -TDSD,Cabana Target Drawdown 13 ETF,Asset Allocation,201870000.0 -TDSE,Cabana Target Drawdown 16 ETF,Asset Allocation,184042500.0 -TDTF,FlexShares iBoxx 5-Year Target Duration TIPS Index Fund,Fixed Income,649661900.0 -TDTT,FlexShares iBoxx 3-Year Target Duration TIPS Index Fund,Fixed Income,2169584400.0 -TDV,ProShares S&P Technology Dividend Aristocrats ETF,Equity,122986900.0 -TDVG,T. Rowe Price Dividend Growth ETF,Equity,201853100.0 -TECB,iShares U.S. Tech Breakthrough Multisector ETF,Equity,322826900.0 -TECL,Direxion Daily Technology Bull 3X Shares,Equity,1522147700.0 -TECS,Direxion Daily Technology Bear 3X Shares,Equity,124900900.0 -TEMP,JPMorgan Climate Change Solutions ETF,Equity,21645400.0 -TEQI,T. Rowe Price Equity Income ETF,Equity,104713300.0 -TFI,SPDR Nuveen Bloomberg Barclays Municipal Bond ETF,Fixed Income,3453532000.0 -TFJL,Innovator 20 plus Year Treasury Bond 5 Floor ETF (July),Fixed Income,20850400.0 -TFLO,iShares Treasury Floating Rate Bond ETF,Fixed Income,3750121600.0 -TFLR,T. Rowe Price Floating Rate ETF,Fixed Income,21094900.0 -TGIF,SoFi Weekly Income ETF,Fixed Income,23622500.0 -TGN,AXS Brendan Wood TopGun Index ETF,Equity,1548900.0 -TGRW,T. Rowe Price Growth Stock ETF,Equity,37589000.0 -THCX,The Cannabis ETF,Equity,28140000.0 -THD,iShares MSCI Thailand ETF,Equity,322057700.0 -THLV,THOR Low Volatility ETF,Equity,26790400.0 -THNQ,ROBO Global Artificial Intelligence ETF,Equity,23250100.0 -THY,Agility Shares Dynamic Tactical Income ETF,Asset Allocation,38643800.0 -THYF,U.S. High Yield ETF,Fixed Income,21536700.0 -TILL,Teucrium Agricultural Strategy No K-1 ETF,Commodity,86169700.0 -TILT,FlexShares Morningstar U.S. Market Factor Tilt Index Fund,Equity,1451712700.0 -TIME,Clockwise Capital Innovation ETF,Equity,8565000.0 -TINT,ProShares Smart Materials ETF,Equity,1537300.0 -TINY,ProShares Nanotechnology ETF,Equity,2471600.0 -TIP,iShares TIPS Bond ETF,Fixed Income,25366444800.0 -TIPD,Direxion Daily TIPS Bear 2X Shares,Fixed Income,8885500.0 -TIPL,Direxion Daily TIPS Bull 2X Shares,Fixed Income,7103500.0 -TIPX,SPDR Bloomberg Barclays 1-10 Year TIPS ETF,Fixed Income,1425724300.0 -TIPZ,PIMCO Broad U.S. TIPS Index ETF,Fixed Income,182005500.0 -TLH,iShares 10-20 Year Treasury Bond ETF,Fixed Income,5461273600.0 -TLT,iShares 20+ Year Treasury Bond ETF,Fixed Income,26380374400.0 -TLTD,FlexShares Morningstar Developed Markets ex-US Factor Tilt Index Fund,Equity,494950400.0 -TLTE,FlexShares Morningstar Emerging Markets Factor Tilt Index Fund,Equity,225284900.0 -TLTW,iShares 20+ Year Treasury Bond BuyWrite Strategy ETF,Fixed Income,22594000.0 -TMAT,Main Thematic Innovation ETF,Equity,55943000.0 -TMDV,ProShares Russell U.S. Dividend Growers ETF,Equity,13777600.0 -TMF,Direxion Daily 20 Plus Year Treasury Bull 3X Shares,Fixed Income,506879300.0 -TMFC,Motley Fool 100 ETF,Equity,396275000.0 -TMFE,Motley Fool Capital Efficiency 100 Index ETF,Equity,19844200.0 -TMFG,Motley Fool Global Opportunities ETF,Equity,420736000.0 -TMFM,Motley Fool Mid Cap Growth ETF,Equity,0.0 -TMFS,Motley Fool Small-Cap Growth ETF,Equity,71137000.0 -TMFX,Motley Fool Next Index ETF,Equity,30981300.0 -TMV,Direxion Daily 20 Plus Year Treasury Bear 3X Shares,Fixed Income,536934200.0 -TNA,Direxion Daily Small Cap Bull 3X Shares,Equity,1214520500.0 -TOK,iShares MSCI Kokusai ETF,Equity,165262500.0 -TOKE,Cambria Cannabis ETF,Equity,14563300.0 -TOLZ,ProShares DJ Brookfield Global Infrastructure ETF,Equity,168114300.0 -TOTL,SPDR DoubleLine Total Return Tactical ETF,Fixed Income,2133623400.0 -TOTR,T. Rowe Price Total Return ETF,Fixed Income,23267500.0 -TPHD,Timothy Plan High Dividend Stock ETF,Equity,183543400.0 -TPHE,Timothy Plan High Dividend Stock Enhanced ETF,Equity,65888399.99999999 -TPIF,Timothy Plan International ETF,Equity,70830300.0 -TPLC,Timothy Plan US Large Cap Core ETF,Equity,170794300.0 -TPLE,Timothy Plan US Large\u002FMid Cap Core Enhanced ETF,Equity,73021400.0 -TPOR,Direxion Daily Transportation Bull 3X Shares,Equity,27576000.0 -TPSC,Timothy Plan US Small Cap Core ETF,Equity,64073600.0 -TPYP,Tortoise North American Pipeline Fund,Equity,556226500.0 -TQQQ,ProShares UltraPro QQQ,Equity,12308294400.0 -TRFK,Pacer Data and Digital Revolution ETF,Equity,934900.0 -TRFM,AAM Transformers ETF,Equity,4525100.0 -TRND,Pacer Trendpilot Fund of Funds ETF,Equity,59751300.0 -TRPL,Pacer Metaurus US Large Cap Dividend Multiplier 300 ETF,Asset Allocation,1791900.0 -TRTY,Cambria Trinity ETF,Alternatives,111714000.0 -TSJA,Innovator Triple Stacker ETF (January),Equity,6225700.0 -TSL,GraniteShares 1.25x Long Tesla Daily ETF,Equity,678700.0 -TSLH,Innovator Hedged TSLA Strategy ETF,Equity,2251000.0 -TSLI,GraniteShares 1x Short TSLA Daily ETF,Equity,721600.0 -TSLL,Direxion Daily TSLA Bull 1.5X Shares,Equity,66192000.0 -TSLQ,AXS TSLA Bear Daily ETF,Equity,63514000.0 -TSLS,Direxion Daily TSLA Bear 1X Shares,Equity,13816800.0 -TSLY,YieldMax TSLA Option Income Strategy ETF,0,500100.0 -TSME,Thrivent Small-Mid Cap ESG ETF,Equity,49715900.0 -TSOC,Innovator Triple Stacker ETF (October),Equity,5427000.0 -TSPA,T. Rowe Price U.S. Equity Research ETF,Equity,23080300.0 -TTAC,TrimTabs Float Shrink ETF,Equity,164909500.0 -TTAI,TrimTabs All Cap International Free-Cash-Flow ETF,Equity,43751800.0 -TTT,ProShares UltraPro Short 20 Plus Year Treasury,Fixed Income,417918000.0 -TUA,Simplify Short Term Treasury Futures Strategy ETF,Fixed Income,21603000.0 -TUG,STF Tactical Growth ETF,Asset Allocation,132839799.99999999 -TUGN,STF Tactical Growth & Income ETF,Asset Allocation,28855000.0 -TUR,iShares MSCI Turkey ETF,Equity,373750000.0 -TUSI,Touchstone Ultra Short Income ETF,Fixed Income,0.0 -TWEB,SoFi Web 3 ETF,Equity,709500.0 -TWIO,Trajan Wealth Income Opportunities ETF,Asset Allocation,26600400.0 -TWM,ProShares UltraShort Russell2000,Equity,88020700.0 -TYA,Simplify Risk Parity Treasury ETF,Alternatives,40156900.0 -TYD,Direxion Daily 10-Year Treasury Bull 3X Shares,Fixed Income,26411500.0 -TYLG,Global X Information Technology Covered Call & Growth ETF,0,2528800.0 -TYNE,Direxion Nanotechnology ETF,Equity,1740900.0 -TYO,Direxion Daily 7-10 Year Treasury Bear 3X Shares,Fixed Income,52127800.0 -TZA,Direxion Daily Small Cap Bear 3X Shares,Equity,497760800.0 -UAE,iShares MSCI UAE ETF,Equity,36707000.0 -UAPR,Innovator S&P 500 Ultra Buffer ETF (April),Equity,123814800.0 -UAUG,Innovator S&P 500 Ultra Buffer ETF (August),Equity,84081100.0 -UAV,AdvisorShares Drone Technology ETF,Equity,442400.0 -UBCB,UBC Algorithmic Fundamentals ETF,Equity,941400.0 -UBND,VictoryShares ESG Core Plus Bond ETF,Fixed Income,211885300.0 -UBOT,"Daily Robotics, Artificial Intelligence & Automation Index Bull 3X Shares",Equity,12746400.0 -UBR,ProShares Ultra MSCI Brazil Capped,Equity,3825500.0 -UBT,ProShares Ultra 20 Plus Year Treasury,Fixed Income,37752500.0 -UCC,ProShares Ultra Consumer Services,Equity,11020600.0 -UCO,ProShares Ultra Bloomberg Crude Oil,Commodity,729612200.0 -UCON,First Trust TCW Unconstrained Plus Bond ETF,Fixed Income,1260469600.0 -UCRD,VictoryShares ESG Corporate Bond ETF,Fixed Income,102285100.0 -UCYB,ProShares Ultra Nasdaq Cybersecurity ETF,Equity,2090699.9999999998 -UDEC,Innovator S&P 500 Ultra Buffer ETF (December),Equity,46299000.0 -UDI,USCF Dividend Income Fund,Equity,5678100.0 -UDIV,Franklin U.S. Core Dividend Tilt Index ETF,Equity,36552500.0 -UDN,Invesco DB US Dollar Index Bearish Fund,Currency,74512600.0 -UDOW,ProShares UltraPro Dow30,Equity,746827900.0 -UEVM, VictoryShares Emerging Markets Value Momentum ETF,Equity,182219000.0 -UFEB,Innovator S&P 500 Ultra Buffer ETF (February),Equity,53622500.0 -UFO,Procure Space ETF,Equity,60469200.0 -UGA,United States Gasoline Fund,Commodity,103500200.0 -UGE,ProShares Ultra Consumer Goods,Equity,5637400.0 -UGL,ProShares Ultra Gold,Commodity,167938300.0 -UITB,VictoryShares USAA Core Intermediate-Term Bond ETF,Fixed Income,1252561500.0 -UIVM,VictoryShares International Value Momentum ETF,Equity,285019500.0 -UJAN,Innovator S&P 500 Ultra Buffer ETF (January),Equity,289185000.0 -UJB,ProShares Ultra High Yield,Fixed Income,17200900.0 -UJUL,Innovator S&P 500 Ultra Buffer ETF (July),Equity,75441600.0 -UJUN,Innovator S&P 500 Ultra Buffer ETF (June),Equity,117380300.0 -ULE,ProShares Ultra Euro,Currency,14682600.0 -ULST,SPDR SSGA Ultra Short Term Bond ETF,Fixed Income,377569100.0 -ULTR,IQ Ultra Short Duration ETF,Fixed Income,71347100.0 -ULVM,VictoryShares US Value Momentum ETF,Equity,288068000.0 -UMAR,Innovator S&P 500 Ultra Buffer ETF (March),Equity,69333000.0 -UMAY,Innovator S&P 500 Ultra Buffer ETF (May),Equity,100529600.0 -UMDD,ProShares UltraPro MidCap400,Equity,30564100.0 -UMI,USCF Midstream Energy Income Fund,Equity,185465300.0 -UMMA,Wahed Dow Jones Islamic World ETF,Equity,30225000.0 -UNG,United States Natural Gas Fund,Commodity,601767400.0 -UNL,United States 12 Month Natural Gas Fund,Commodity,38413700.0 -UNOV,Innovator S&P 500 Ultra Buffer ETF (November),Equity,54124800.0 -UOCT,Innovator S&P 500 Ultra Buffer ETF (October),Equity,115668000.0 -UPAR,UPAR Ultra Risk Parity ETF,Alternatives,46623900.0 -UPRO,ProShares UltraPro S&P500,Equity,2534587200.0 -UPV,ProShares Ultra Europe,Equity,3595400.0 -UPW,ProShares Ultra Utilities,Equity,15406100.0 -UPWD,JPMorgan Social Advancement ETF,Equity,11118600.0 -URA,Global X Uranium ETF,Equity,1631408000.0 -URE,ProShares Ultra Real Estate,Equity,72695000.0 -URNM,Sprott Funds Uranium Mining ETF,Equity,876800800.0 -URTH,iShares MSCI World ETF,Equity,2170014600.0 -URTY,ProShares UltraPro Russell2000,Equity,230184600.0 -USAI,American Energy Independence ETF,Equity,48760900.0 -USBF,iShares USD Bond Factor ETF,Fixed Income,16836400.0 -USCI,United States Commodity Index Fund,Commodity,273898900.0 -USD,ProShares Ultra Semiconductors,Equity,158439100.0 -USDU,WisdomTree Bloomberg U.S. Dollar Bullish Fund,Currency,437404300.0 -USEP,Innovator S&P 500 Ultra Buffer ETF (September),Equity,162801700.0 -USEQ,Invesco Russell 1000 Enhanced Equal Weight ETF,Equity,2790500.0 -USFR,WisdomTree Bloomberg Floating Rate Treasury Fund,Fixed Income,11824963200.0 -USHY,iShares Broad USD High Yield Corporate Bond ETF,Fixed Income,8233296800.0 -USI,Principal Ultra-Short Active Income ETF,Fixed Income,7225500.0 -USIG,iShares Broad USD Investment Grade Corporate Bond ETF,Fixed Income,6708576800.0 -USL,United States 12 Month Oil Fund,Commodity,101971000.0 -USLB,Invesco Russell 1000 Low Beta Equal Weight ETF,Equity,9033300.0 -USMC,Principal U.S. Mega-Cap Multi-Factor Index ETF,Equity,1115100000.0 -USMF,WisdomTree U.S. Multifactor Fund,Equity,238327000.0 -USMV,iShares Edge MSCI Min Vol USA ETF,Equity,30403760000.0 -USNZ,Xtrackers Net Zero Pathway Paris Aligned US Equity ETF,Equity,79472800.0 -USO,United States Oil Fund,Commodity,1969720400.0 -USPX,Franklin U.S. Equity Index ETF,Equity,633411000.0 -USRT,iShares Core U.S. REIT ETF,Equity,1974480000.0 -USSG,Xtrackers MSCI USA ESG Leaders Equity ETF,Equity,3083432200.0 -UST,ProShares Ultra 7-10 Year Treasury Fund,Fixed Income,9613800.0 -USTB,VictoryShares USAA Core Short-Term Bond ETF,Fixed Income,369335100.0 -USVM,VictoryShares US Small Mid Cap Value Momentum ETF,Equity,278628000.0 -USVT,U.S. Value ETF,Equity,15686400.0 -USXF,iShares ESG Advanced MSCI USA ETF,Equity,637200000.0 -UTEN,US Treasury 10 Year Note ETF,Fixed Income,9220400.0 -UTES,Reaves Utilities ETF,Equity,46890200.0 -UTRN,Vesper US Large Cap Short-Term Reversal Strategy ETF,Equity,73779500.0 -UTSL,Direxion Daily Utilities Bull 3X Shares,Equity,23594900.0 -UTWO,US Treasury 2 Year Note ETF,Fixed Income,122400000.0 -UUP,Invesco DB US Dollar Index Bullish Fund,Currency,2156259200.0 -UVDV,UVA Dividend Value ETF,Equity,18936600.0 -UVIX,2x Long VIX Futures ETF,Alternatives,26144000.0 -UVXY,ProShares Ultra VIX Short-Term Futures ETF,Alternatives,519782400.0 -UWM,ProShares Ultra Russell 2000,Equity,149671400.0 -UXI,ProShares Ultra Industrials,Equity,17025000.0 -UYG,ProShares Ultra Financials,Equity,607668900.0 -UYLD,Angel Oak UltraShort Income ETF,Fixed Income,10045400.0 -UYM,ProShares Ultra Basic Materials,Equity,48454700.0 -VABS,Virtus Newfleet ABS\u002FMBS ETF,Fixed Income,12520000.0 -VALQ,American Century STOXX U.S. Quality Value ETF,Equity,216683400.0 -VALT,ETFMG SIT Ultra Short ETF,Fixed Income,124297200.0 -VAMO,Cambria Value and Momentum ETF,Equity,121626000.0 -VAW,Vanguard Materials ETF,Equity,3028441000.0 -VB,Vanguard Small-Cap ETF,Equity,42529891200.0 -VBB,Valkyrie Balance Sheet Opportunities ETF,Equity,573000.0 -VBK,Vanguard Small-Cap Growth ETF,Equity,12309380000.0 -VBND,Vident Core U.S. Bond Strategy ETF,Fixed Income,362467200.0 -VBR,Vanguard Small-Cap Value ETF,Equity,24428937600.0 -VCAR,Simplify Volt RoboCar Disruption and Tech ETF,Equity,3179000.0 -VCEB,Vanguard ESG U.S. Corporate Bond ETF,Fixed Income,326214000.0 -VCIT,Vanguard Intermediate-Term Corporate Bond ETF,Fixed Income,38878048000.0 -VCLN,Virtus Duff & Phelps Clean Energy ETF,Equity,3553700.0 -VCLO,Simplify Volt Cloud and Cybersecurity Disruption ETF,Equity,4502100.0 -VCLT,Vanguard Long-Term Corporate Bond ETF,Fixed Income,4594448000.0 -VCR,Vanguard Consumer Discretionary ETF,Equity,4297744400.0 -VCSH,Vanguard Short-Term Corporate Bond ETF,Fixed Income,41786505600.0 -VDC,Vanguard Consumer Staples ETF,Equity,6870763200.0 -VDE,Vanguard Energy ETF,Equity,8917263200.0 -VDNI,V-Shares US Leadership Diversity ETF,Equity,1084400.0 -VEA,Vanguard FTSE Developed Markets ETF,Equity,99213081600.0 -VEGA,AdvisorShares STAR Global Buy-Write ETF,Alternatives,39386000.0 -VEGI,iShares MSCI Global Agriculture Producers ETF,Equity,319884800.0 -VEGN,US Vegan Climate ETF,Equity,63561800.0 -VERS,ProShares Metaverse ETF,Equity,5919000.0 -VEU,Vanguard FTSE All-World ex-US ETF,Equity,32086732800.0 -VFH,Vanguard Financials ETF,Equity,9365702400.0 -VFLQ,Vanguard U.S. Liquidity Factor ETF,Equity,44892500.0 -VFMF,Vanguard U.S. Multifactor ETF,Equity,145642000.0 -VFMO,Vanguard U.S. Momentum Factor ETF,Equity,238463500.0 -VFMV,Vanguard U.S. Minimum Volatility ETF,Equity,73274100.0 -VFQY,Vanguard U.S. Quality Factor ETF,Equity,201203900.0 -VFVA,Vanguard U.S. Value Factor ETF,Equity,726937000.0 -VGIT,Vanguard Intermediate-Term Government Bond ETF,Fixed Income,11812232800.0 -VGK,Vanguard FTSE Europe ETF,Equity,15200760000.0 -VGLT,Vanguard Long-Term Government Bond ETF,Fixed Income,3404073200.0 -VGSH,Vanguard Short-Term Government Bond ETF,Fixed Income,17419854400.0 -VGT,Vanguard Information Technology ETF,Equity,41726384000.0 -VHT,Vanguard Health Care ETF,Equity,17071110399.999998 -VICE,AdvisorShares Vice ETF,Equity,9510100.0 -VIDI,Vident International Equity Fund,Equity,334632600.0 -VIG,Vanguard Dividend Appreciation ETF,Equity,65962374400.0 -VIGI,Vanguard International Dividend Appreciation ETF,Equity,3828282400.0 -VIOG,Vanguard S&P Small-Cap 600 Growth ETF,Equity,523852000.0 -VIOO,Vanguard S&P Small-Cap 600 ETF,Equity,2211328600.0 -VIOV,Vanguard S&P Small-Cap 600 Value ETF,Equity,1255109400.0 -VIRS,Pacer BioThreat Strategy ETF,Equity,4320300.0 -VIS,Vanguard Industrials ETF,Equity,3725938800.0 -VIXM,ProShares VIX Mid-Term Futures ETF,Alternatives,73254400.0 -VIXY,ProShares VIX Short-Term Futures ETF,Alternatives,268986300.0 -VLU,SPDR S&P 1500 Value Tilt ETF,Equity,225066300.0 -VLUE,iShares Edge MSCI USA Value Factor ETF,Equity,8258677600.0 -VMAT,V-Shares MSCI World ESG Materiality and Carbon Transition ETF,Equity,2394800.0 -VMBS,Vanguard Mortgage-Backed Securities ETF,Fixed Income,13922014400.0 -VMOT,Value Momentum Trend ETF,Equity,42695700.0 -VNAM,Global X MSCI Vietnam ETF,Equity,2519400.0 -VNLA,Janus Henderson Short Duration Income ETF,Fixed Income,2547164000.0 -VNM,VanEck Vectors Vietnam ETF,Equity,296348000.0 -VNMC,Natixis Vaughan Nelson Mid Cap ETF,Equity,7986900.0 -VNQ,Vanguard REIT ETF,Equity,35489020800.0 -VNQI,Vanguard Global ex-U.S. Real Estate ETF,Equity,3699814000.0 -VNSE,Natixis Vaughan Nelson Select ETF,Equity,10522000.0 -VO,Vanguard Mid-Cap ETF,Equity,51398812800.0 -VOE,Vanguard Mid-Cap Value ETF,Equity,16411481600.0 -VONE,Vanguard Russell 1000 ETF,Equity,3036139800.0 -VONG,Vanguard Russell 1000 Growth ETF,Equity,8359676800.0 -VONV,Vanguard Russell 1000 Value ETF,Equity,6515487600.0 -VOO,Vanguard S&P 500 ETF,Equity,272199040000.0 -VOOG,Vanguard S&P 500 Growth ETF,Equity,6627054000.0 -VOOV,Vanguard S&P 500 Value ETF,Equity,2978888400.0 -VOT,Vanguard Mid-Cap Growth ETF,Equity,9747566400.0 -VOTE,Engine No. 1 Transform 500 ETF,Equity,338388600.0 -VOX,Vanguard Telecommunication Services ETF,Equity,2460620800.0 -VPC,Virtus Private Credit Strategy ETF,Asset Allocation,22871100.0 -VPL,Vanguard FTSE Pacific ETF,Equity,5724721600.0 -VPN,Global X Data Center REITs & Digital Infrastructure ETF,Equity,68031500.0 -VPU,Vanguard Utilities ETF,Equity,5801933200.0 -VR,Global X Metaverse ETF,Equity,1898000.0 -VRAI,Virtus Real Asset Income ETF,Equity,48406800.0 -VRIG,Invesco Variable Rate Investment Grade ETF,Fixed Income,571485000.0 -VRP,Invesco Variable Rate Preferred ETF,Fixed Income,1798556800.0 -VSDA,VictoryShares Dividend Accelerator ETF,Equity,205456500.0 -VSGX,Vanguard ESG International Stock ETF,Equity,2961684000.0 -VSLU,Applied Finance Valuation Large Cap ETF,Equity,12890400.0 -VSMV,VictoryShares US Multi-Factor Minimum Volatility ETF,Equity,130233900.0 -VSPY,VectorShares Min Vol ETF,Asset Allocation,15752300.0 -VSS,Vanguard FTSE All World ex-US Small-Cap ETF,Equity,7527449600.0 -VT,Vanguard Total World Stock ETF,Equity,23112716800.0 -VTC,Vanguard Total Corporate Bond ETF,Fixed Income,575662500.0 -VTEB,Vanguard Tax-Exempt Bond ETF,Fixed Income,18012150400.0 -VTHR,Vanguard Russell 3000 ETF ,Equity,1250587800.0 -VTI,Vanguard Total Stock Market Index Fund ETF,Equity,267748800000.0 -VTIP,Vanguard Short-Term Inflation-Protected Securities ETF,Fixed Income,19036940800.0 -VTV,Vanguard Value ETF,Equity,105789619200.0 -VTWG,Vanguard Russell 2000 Growth ETF,Equity,648646500.0 -VTWO,Vanguard Russell 2000 ETF,Equity,5282292000.0 -VTWV,Vanguard Russell 2000 Value ETF,Equity,843570000.0 -VUG,Vanguard Growth ETF,Equity,70969446400.0 -VUSB,Vanguard Ultra-Short Bond ETF,Fixed Income,2885013000.0 -VUSE,Vident Core U.S. Equity ETF,Equity,500018800.0 -VV,Vanguard Large-Cap ETF,Equity,25312867200.0 -VWID,Virtus WMC International Dividend ETF,Equity,7165300.0 -VWO,Vanguard FTSE Emerging Markets ETF,Equity,66590707200.0 -VWOB,Vanguard Emerging Markets Government Bond ETF,Fixed Income,2746941400.0 -VXF,Vanguard Extended Market ETF,Equity,14063148800.0 -VXUS,Vanguard Total International Stock ETF,Equity,48855449600.0 -VYM,Vanguard High Dividend Yield ETF,Equity,50476473600.0 -VYMI,Vanguard International High Dividend Yield,Equity,4378630400.0 -WANT,Direxion Daily Consumer Discretionary Bull 3X Shares,Equity,22132200.0 -WBAT,WisdomTree Battery Value Chain and Innovation Fund,Equity,5233500.0 -WBIF,WBI Bull-Bear Value 1000 ETF,Equity,44158200.0 -WBIG,WBI Bull-Bear Yield 1000 ETF,Equity,66590600.00000001 -WBIL,WBI Bull-Bear Quality 1000 ETF,Equity,44378700.0 -WBIY,WBI Power Factor High Dividend ETF,Equity,81028900.0 -WBND,Western Asset Total Return ETF,Fixed Income,35163800.0 -WCBR,WisdomTree Cybersecurity Fund,Equity,27578000.0 -WCLD,WisdomTree Cloud Computing Fund,Equity,573932500.0 -WDIV,SPDR S&P Global Dividend ETF,Equity,234359500.0 -WDNA,WisdomTree BioRevolution Fund,Equity,0.0 -WEAT,Teucrium Wheat Fund,Commodity,324504500.0 -WEBL,Daily Dow Jones Internet Bull 3X Shares,Equity,95512500.0 -WEBS,Daily Dow Jones Internet Bear 3X Shares,Equity,52348800.0 -WEED,Roundhill Cannabis ETF,Equity,2160400.0 -WEIX,Dynamic Short Short Term Volatility Futures ETF,Alternatives,2649600.0 -WFH,Direxion Work From Home ETF,Equity,37419800.0 -WFHY,WisdomTree Fundamental U.S. High Yield Corporate Bond Fund,Fixed Income,143552000.0 -WFIG,WisdomTree Fundamental U.S. Corporate Bond Fund,Fixed Income,8805700.0 -WGMI,Valkyrie Bitcoin Miners ETF,Equity,1610000.0 -WGRO,WisdomTree U.S. Growth & Momentum Fund,Equity,2914800.0 -WINC,Western Asset Short Duration Income ETF,Fixed Income,24554300.0 -WINN,Harbor Long-Term Growers ETF,Equity,79534800.0 -WIP,SPDR FTSE International Government Inflation-Protected Bond ETF,Fixed Income,483722600.0 -WIZ,Merlyn.AI Bull-Rider Bear-Fighter ETF,Asset Allocation,26986300.0 -WKLY,SoFi Weekly Dividend ETF,Equity,9491500.0 -WLDR,Affinity World Leaders Equity ETF,Equity,36382500.0 -WLTG,WealthTrust DBS Long Term Growth ETF,Asset Allocation,10734000.0 -WLTH,LifeGoal Wealth Builder ETF,Asset Allocation,3780000.0 -WNDY,Global X Wind Energy ETF,Equity,3347500.0 -WOMN,Impact Shares YWCA Womens Empowerment ETF,Equity,33461900.0 -WOOD,iShares Global Timber & Forestry ETF,Equity,238773600.0 -WPS,iShares International Developed Property ETF,Equity,46149100.0 -WRND,IQ Global Equity R&D Leaders ETF,Equity,4128000.0 -WTAI,WisdomTree Artificial Intelligence and Innovation Fund,Equity,0.0 -WTMF,WisdomTree Managed Futures Strategy,Alternatives,129154700.0 -WTRE,WisdomTree New Economy Real Estate Fund,Equity,33026600.0 -WTV,WisdomTree U.S. Value Fund,Equity,119572800.0 -WUGI,Esoterica NextG Economy ETF,Equity,17074500.0 -WWJD,Inspire International ESG ETF,Equity,139528000.0 -XAR,SPDR S&P Aerospace & Defense ETF,Equity,1316291600.0 -XB,BondBloxx B-Rated USD High Yield Corporate Bond ETF,Fixed Income,11641500.0 -XBAP,Innovator U.S. Equity Accelerated 9 Buffer ETF,Equity,20050300.0 -XBB,BondBloxx BB-Rated USD High Yield Corporate Bond ETF,Fixed Income,42989200.0 -XBI,SPDR S&P Biotech ETF,Equity,7160870400.0 -XBJA,Innovator U.S. Equity Accelerated 9 Buffer ETF - January,Equity,0.0 -XBJL,Innovator U.S. Equity Accelerated 9 Buffer ETF (July),Equity,32613000.0 -XBOC,Innovator U.S. Equity Accelerated 9 Buffer ETF (Oct),Equity,35214900.0 -XBTF,VanEck Bitcoin Strategy ETF,Currency,17889300.0 -XC,WisdomTree Emerging Markets Ex-China,Equity,15118600.0 -XCCC,BondBloxx CCC-Rated USD High Yield Corporate Bond ETF,Fixed Income,11203100.0 -XCEM,Columbia EM ex-China ETF,Equity,122833600.0 -XCLR,Global X S&P 500 Collar 95-110 ETF,Equity,4033600.0 -XCOR,FundX ETF,Equity,155493000.0 -XDAP,Innovator U.S. Equity Accelerated ETF,Equity,3907500.0 -XDAT,Franklin Exponential Data ETF,Equity,2337000.0 -XDEC,FT Cboe Vest U.S. Equity Enhance & Moderate Buffer ETF - December,Equity,81922500.0 -XDJA,Innovator U.S. Equity Accelerated ETF - January,Equity,1032700.0 -XDJL,Innovator U.S. Equity Accelerated ETF (July),Equity,4827300.0 -XDNA,Kelly CRISPR & Gene Editing Technology ETF,Equity,4902000.0 -XDOC,Innovator U.S. Equity Accelerated ETF (Oct),Equity,36983500.0 -XDQQ,Innovator Growth Accelerated ETF,Equity,18905400.0 -XDSQ,Innovator U.S. Equity Accelerated ETF,Equity,11843400.0 -XEMD,BondBloxx JP Morgan USD Emerging Markets 1-10 Year Bond ETF,Fixed Income,23828300.0 -XES,SPDR S&P Oil & Gas Equipment & Services ETF,Equity,360480100.0 -XFIV,BondBloxx Bloomberg Five Year Target Duration US Treasury ETF,Fixed Income,20638200.0 -XHB,SPDR S&P Homebuilders ETF,Equity,1013937100.0 -XHE,SPDR S&P Health Care Equipment ETF,Equity,405580700.0 -XHLF,BondBloxx Bloomberg Six Month Target Duration US Treasury ETF,Fixed Income,47590900.0 -XHS,SPDR S&P Health Care Services ETF,Equity,135653800.0 -XHYC,BondBloxx USD High Yield Bond Consumer Cyclicals Sector ETF,Fixed Income,5259900.0 -XHYD,BondBloxx USD High Yield Bond Consumer Non-Cyclicals Sector ETF,Fixed Income,5424900.0 -XHYE,BondBloxx USD High Yield Bond Energy Sector ETF,Fixed Income,7479000.0 -XHYF,BondBloxx USD High Yield Bond Financial & REIT Sector ETF,Fixed Income,3538700.0 -XHYH,BondBloxx USD High Yield Bond Healthcare Sector ETF,Fixed Income,6678300.0 -XHYI,BondBloxx USD High Yield Bond Industrial Sector ETF,Fixed Income,3632600.0 -XHYT,"BondBloxx USD High Yield Bond Telecom, Media & Technology Sector ETF",Fixed Income,3487800.0 -XITK,SPDR FactSet Innovative Technology ETF,Equity,105512200.0 -XJH,iShares ESG Screened S&P Mid-Cap ETF,Equity,96292000.0 -XJR,iShares ESG Screened S&P Small-Cap ETF,Equity,37118800.0 -XJUN,FT Cboe Vest U.S. Equity Enhance & Moderate Buffer ETF (June),Equity,150548000.0 -XLB,Materials Select Sector SPDR Fund,Equity,5815434000.0 -XLC,Communication Services Select Sector SPDR Fund,Equity,8547516800.000001 -XLE,Energy Select Sector SPDR Fund,Equity,43957916800.0 -XLF,Financial Select Sector SPDR Fund,Equity,33197174400.0 -XLG,Invesco S&P 500 Top 50 ETF,Equity,1997713600.0 -XLI,Industrial Select Sector SPDR Fund,Equity,13726339200.0 -XLK,Technology Select Sector SPDR Fund,Equity,40685523200.0 -XLP,Consumer Staples Select Sector SPDR Fund,Equity,16371502400.0 -XLRE,Real Estate Select Sector SPDR Fund,Equity,4989896800.0 -XLSR,SPDR SSGA US Sector Rotation ETF,Equity,237220800.0 -XLU,Utilities Select Sector SPDR Fund,Equity,16363697600.0 -XLV,Health Care Select Sector SPDR Fund,Equity,41049056000.0 -XLY,Consumer Discretionary Select Sector SPDR Fund,Equity,14287518400.0 -XME,SPDR S&P Metals & Mining ETF,Equity,2061973600.0 -XMHQ,Invesco S&P MidCap Quality ETF,Equity,337166400.0 -XMLV,Invesco S&P MidCap Low Volatility ETF,Equity,1212843900.0 -XMMO,Invesco S&P MidCap Momentum ETF,Equity,1033491200.0 -XMPT,VanEck Vectors CEF Municipal Income ETF,Fixed Income,142004000.0 -XMVM,Invesco S&P MidCap Value with Momentum ETF,Equity,220575000.0 -XNAV,FundX Aggressive ETF,Equity,28401400.0 -XNTK,SPDR NYSE Technology ETF,Equity,378235700.0 -XONE,BondBloxx Bloomberg One Year Target Duration US Treasury ETF,Fixed Income,39339900.0 -XOP,SPDR S&P Oil & Gas Exploration & Production ETF,Equity,5286403200.0 -XOUT,GraniteShares XOUT U.S. Large Cap ETF,Equity,84744800.0 -XPH,SPDR S&P Pharmaceuticals ETF,Equity,214413200.0 -XPND,First Trust Expanded Technology ETF,Equity,17827200.0 -XPP,ProShares Ultra FTSE China 50,Equity,5989100.0 -XRLV,Invesco S&P 500 ex-Rate Sensitive Low Volatility ETF,Equity,56508200.0 -XRMI,Global X S&P 500 Risk Managed Income ETF,Equity,24523200.0 -XRT,SPDR S&P Retail ETF,Equity,466265800.0 -XSD,SPDR S&P Semiconductor ETF,Equity,1133403900.0 -XSEP,FT Cboe Vest U.S. Equity Enhance & Moderate Buffer ETF - September,Equity,118674000.0 -XSHD,Invesco S&P SmallCap High Dividend Low Volatility ETF,Equity,24215300.0 -XSHQ,Invesco S&P SmallCap Quality ETF,Equity,28048600.0 -XSLV,Invesco S&P SmallCap Low Volatility ETF,Equity,740496300.0 -XSMO,Invesco S&P SmallCap Momentum ETF,Equity,171721000.0 -XSOE,WisdomTree Emerging Markets ex-State-Owned Enterprises Fund,Equity,2173223800.0 -XSVM,Invesco S&P SmallCap Value with Momentum ETF,Equity,719059000.0 -XSVN,BondBloxx Bloomberg Seven Year Target Duration US Treasury ETF,Fixed Income,21020400.0 -XSW,SPDR S&P Software & Services ETF,Equity,204598500.0 -XT,iShares Exponential Technologies ETF,Equity,3001915400.0 -XTAP,Innovator U.S. Equity Accelerated Plus ETF,Equity,3891700.0 -XTEN,BondBloxx Bloomberg Ten Year Target Duration US Treasury ETF,Fixed Income,20903400.0 -XTJA,Innovator U.S. Equity Accelerated Plus ETF - January,Equity,5167500.0 -XTJL,Innovator U.S. Equity Accelerated Plus ETF (July),Equity,13248400.0 -XTL,SPDR S&P Telecom ETF,Equity,64550000.0 -XTN,SPDR S&P Transportation ETF,Equity,447897600.0 -XTOC,Innovator U.S. Equity Accelerated Plus ETF (Oct),Equity,15797200.0 -XTR,Global X S&P 500 Tail Risk ETF,Equity,3122600.0 -XTRE,BondBloxx Bloomberg Three Year Target Duration US Treasury ETF,Fixed Income,20232300.0 -XTWO,BondBloxx Bloomberg Two Year Target Duration US Treasury ETF,Fixed Income,36605700.0 -XTWY,BondBloxx Bloomberg Twenty Year Target Duration US Treasury ETF,Fixed Income,23200800.0 -XUSP,Innovator Uncapped Accelerated U.S. Equity ETF,Equity,5637200.0 -XVOL,Acruence Active Hedge U.S. Equity ETF,Equity,12949900.0 -XVV,iShares ESG Screened S&P 500 ETF,Equity,224851500.0 -XWEB,SPDR S&P Internet ETF,Equity,22850300.0 -XYLD,Global X S&P 500 Covered Call ETF,Equity,1939719600.0 -XYLG,Global X S&P 500 Covered Call & Growth ETF,Equity,42377700.0 -YALL,God Bless America ETF,Equity,25124900.0 -YANG,Direxion Daily FTSE China Bear 3X Shares,Equity,93993200.0 -YCL,ProShares Ultra Yen,Currency,15077000.0 -YCS,ProShares UltraShort Yen,Currency,51428100.0 -YDEC,FT Cboe Vest International Equity Buffer ETF (December),Equity,10955300.0 -YEAR,AB Ultra Short Income ETF,Fixed Income,149629800.0 -YINN,Direxion Daily FTSE China Bull 3X Shares,Equity,347192200.0 -YJUN,FT Cboe Vest International Equity Buffer ETF - June,Equity,0.0 -YLD,Principal EDGE Active Income ETF,Asset Allocation,247021300.0 -YLDE,ClearBridge Dividend Strategy ESG ETF,Equity,22308200.0 -YMAR,FT Cboe Vest International Equity Buffer ETF - March,Equity,25506100.0 -YOLO,AdvisorShares Pure Cannabis ETF,Equity,69947700.0 -YPS,Arrow Reverse Cap 500 ETF,Equity,17412500.0 -YSEP,FT Cboe Vest International Equity Buffer ETF - September,Equity,19668800.0 -YUMY,VanEck Future of Food ETF,Equity,2836100.0 -YXI,ProShares Short FTSE China 50,Equity,12995500.0 -YYY,Amplify High Income ETF,Asset Allocation,312783400.0 -ZECP,Zacks Earnings Consistent Portfolio ETF,Equity,14429700.0 -ZGEN,The Generation Z ETF,Equity,1516700.0 -ZHDG,ZEGA Buy and Hedge ETF,Asset Allocation,89430600.0 -ZIG,Acquirers Fund,Equity,40348600.0 -ZROZ,PIMCO 25 Plus Year Zero Coupon U.S. Treasury Index ETF,Fixed Income,566038200.0 -ZSL,ProShares UltraShort Silver,Commodity,13564800.0 diff --git a/openbb_terminal/etf/financedatabase_model.py b/openbb_terminal/etf/financedatabase_model.py deleted file mode 100644 index c8cc2a8b572..00000000000 --- a/openbb_terminal/etf/financedatabase_model.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Finance Database model""" - -__docformat__ = "numpy" - -import logging -from typing import Dict, List - -import financedatabase as fd -import numpy as np -import pandas as pd - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_etfs_by_name(name: str) -> Dict: - """Return a selection of ETFs based on name filtered by total assets. [Source: Finance Database] - - Parameters - ---------- - name: str - Search by name to find ETFs matching the criteria. - - Returns - ------- - data : Dict[str, Any] - Dictionary with ETFs that match a certain name - """ - etfs = fd.ETFs() - - data = etfs.search(name=name, exclude_exchanges=True) - data = pd.concat([data, etfs.search(index=name, exclude_exchanges=True)]) - - # Ensures that data that's missing values is at the bottom of the DataFrame - data = data.iloc[np.argsort(-data.isnull().mul(-1).sum(axis=1))] - - return data - - -@log_start_end(log=logger) -def get_etfs_by_description(description: str) -> Dict: - """Return a selection of ETFs based on description filtered by total assets. - [Source: Finance Database] - - Parameters - ---------- - description: str - Search by description to find ETFs matching the criteria. - - Returns - ------- - data: Dict[str, Any] - Dictionary with ETFs that match a certain description - """ - etfs = fd.ETFs() - - data = etfs.search(summary=description, exclude_exchanges=True) - - # Ensures that data that's missing values is at the bottom of the DataFrame - data = data.iloc[np.argsort(-data.isnull().mul(-1).sum(axis=1))] - - return data - - -@log_start_end(log=logger) -def get_etfs_by_category(category: str) -> Dict: - """Return a selection of ETFs based on category filtered by total assets. - [Source: Finance Database] - - Parameters - ---------- - category: str - Search by category to find ETFs matching the criteria. - - Returns - ------- - data: Dict[str, Any] - Dictionary with ETFs that match a certain description - """ - etfs = fd.ETFs() - - data = etfs.select(category=category, exclude_exchanges=True) - - # Ensures that data that's missing values is at the bottom of the DataFrame - data = data.iloc[np.argsort(-data.isnull().mul(-1).sum(axis=1))] - - return data - - -@log_start_end(log=logger) -def get_etfs_categories() -> List[str]: - """Return a selection of ETF categories. [Source: Finance Database] - - Returns - ------- - List[str] - ETF categories - """ - etfs = fd.ETFs() - - return etfs.options("category") diff --git a/openbb_terminal/etf/financedatabase_view.py b/openbb_terminal/etf/financedatabase_view.py deleted file mode 100644 index b8834aa3450..00000000000 --- a/openbb_terminal/etf/financedatabase_view.py +++ /dev/null @@ -1,152 +0,0 @@ -"""Finance Database view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.etf import financedatabase_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_etf_by_name( - name: str, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display a selection of ETFs based on name. [Source: Finance Database] - - Parameters - ---------- - name: str - Search by name to find ETFs matching the criteria. - limit: int - Limit of ETFs to display - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data - """ - data = financedatabase_model.get_etfs_by_name(name) - - if data.empty: - console.print("No data was found with that name.") - return - - table_data = data[["name", "family", "category_group", "category"]] - - print_rich_table( - table_data, - show_index=True, - headers=["Name", "Family", "Category Group", "Category"], - title="ETFs", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ln_fd", - table_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_etf_by_description( - description: str, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display a selection of ETFs based on description. [Source: Finance Database] - - Parameters - ---------- - description: str - Search by description to find ETFs matching the criteria. - limit: int - Limit of ETFs to display - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data - """ - data = financedatabase_model.get_etfs_by_description(description) - - if data.empty: - console.print("No data was found with that description.") - return - - table_data = data[["name", "family", "category_group", "category"]] - - print_rich_table( - table_data, - show_index=True, - headers=["Name", "Family", "Category Group", "Category"], - title="ETFs", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ld", - table_data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_etf_by_category( - category: str, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display a selection of ETFs based on a category. [Source: Finance Database] - - Parameters - ---------- - description: str - Search by description to find ETFs matching the criteria. - limit: int - Limit of ETFs to display - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data - """ - data = financedatabase_model.get_etfs_by_category(category) - - if data.empty: - console.print("No data was found on that category.") - return - - table_data = data[["name", "family", "category_group", "category"]] - - print_rich_table( - table_data, - show_index=True, - headers=["Name", "Family", "Category Group", "Category"], - title="ETFs by Category", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.join(os.path.dirname(os.path.abspath(__file__)), "screener"), - "sbc", - table_data, - sheet_name, - ) diff --git a/openbb_terminal/etf/fmp_model.py b/openbb_terminal/etf/fmp_model.py deleted file mode 100644 index a64c556ca50..00000000000 --- a/openbb_terminal/etf/fmp_model.py +++ /dev/null @@ -1,208 +0,0 @@ -"""FinancialModelingPrep model""" - -__docformat__ = "numpy" - -import json -import logging -from typing import Any, Dict, List -from urllib.error import HTTPError -from urllib.request import urlopen - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console, optional_rich_track - -logger = logging.getLogger(__name__) - -# pylint: disable=consider-using-with - - -@log_start_end(log=logger) -def get_etf_sector_weightings(name: str) -> Dict: - """Return sector weightings allocation of ETF. [Source: FinancialModelingPrep] - - Parameters - ---------- - name: str - ETF name - - Returns - ------- - Dict[str, Any] - Dictionary with sector weightings allocation - """ - try: - response = urlopen( # noqa: S310 - "https://financialmodelingprep.com/api/v3/etf-sector-weightings/" - f"{name}?apikey={get_current_user().credentials.API_KEY_FINANCIALMODELINGPREP}" - ) - data = json.loads(response.read().decode("utf-8")) - except HTTPError: - console.print( - "This endpoint is only for premium members. Please visit the subscription page to upgrade the " - "plan (Starter or higher) at https://financialmodelingprep.com/developer/docs/pricing" - ) - return dict() - - if "Error Message" in data: - raise ValueError(data["Error Message"]) - - return data - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_stock_price_change( - tickers: List[str], start_date: str, end_date: str -) -> Dict[str, float]: - """Get stock's price percent change over specified time period. - - Parameters - ---------- - tickers : List[str] - Ticker(s) to get information for. - start: str - Date from which data is fetched in format YYYY-MM-DD - end: str - Date from which data is fetched in format YYYY-MM-DD - - Returns - ------- - Dict[str, float] - Percent change of closing price over time period, or dictionary of ticker, change pairs. - """ - tickers_tracker = optional_rich_track( - tickers, False, "Gathering stock prices", len(tickers) - ) - current_user = get_current_user() - data_aggregate = dict() - - for tick in tickers_tracker: - tickers_req = str(tick) + "," - for _ in range(4): - try: - _tick = next(tickers_tracker) - tickers_req += _tick + "," - except StopIteration: - break - - url = f"""https://financialmodelingprep.com/api/v3/historical-price-full/{tickers_req}?\ -from={start_date}&to={end_date}&serietype=line\ -&apikey={current_user.credentials.API_KEY_FINANCIALMODELINGPREP}""" - - response = request(url) - if response.status_code != 200 or "Error Message" in response.json(): - message = f"Error, Status Code: {response.status_code}." - message = ( - message - if "Error Message" not in response.json() - else message + "\n" + response.json()["Error Message"] + ".\n" - ) - console.print(message) - return dict() - - data = response.json() - stock_list = data - if "historicalStockList" in data: - stock_list = data["historicalStockList"] - - for stock in stock_list: - close_end = stock["historical"][0]["close"] - close_start = stock["historical"][-1]["close"] - pct_change = 100 * (close_end - close_start) / close_start - data_aggregate[stock["symbol"]] = pct_change - - return data_aggregate - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_etf_holdings(ticker: str, limit: int = 10) -> List[Dict[str, Any]]: - """This endpoint returns all stocks held by a specific ETF. - - Parameters - ---------- - ticker : str - ETF ticker. - limit: int - Limit amount of stocks to return. FMP returns data - by descending weighting. - - Returns - ------- - List[Dict[str,any]] - Info for stock holdings in the ETF. - """ - - current_user = get_current_user() - url = f"""https://financialmodelingprep.com/api/v3/etf-holder/{ticker}\ -?apikey={current_user.credentials.API_KEY_FINANCIALMODELINGPREP}""" - response = request(url) - if response.status_code != 200 or "Error Message" in response.json(): - message = f"Error, Status Code: {response.status_code}." - message = ( - message - if "Error Message" not in response.json() - else message + "\n" + response.json()["Error Message"] + ".\n" - ) - console.print(message) - return [] - - return response.json()[0:limit] - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_holdings_pct_change( - ticker: str, - start_date: str, - end_date: str, - limit: int = 10, -) -> pd.DataFrame: - """Calculate percent change for each holding in ETF. - - Parameters - ---------- - ticker : str - ETF ticker. - limit: int - Limit amount of stocks to return. FMP returns data - by descending weighting. - - Returns - ------- - pd.DataFrame - Calculated percentage change for each stock in the ETF, in descending order. - """ - - df = pd.DataFrame(columns=["Ticker", "Name", "Percent Change"], data=[]) - holdings = get_etf_holdings(ticker, limit) - tickers = [] - for stock in holdings: - tickers.append(stock.get("asset", " ")) - - pct_changes = get_stock_price_change(tickers, start_date, end_date) - - for stock in holdings: - pct_change = pct_changes.get(stock["asset"], 0) - if pct_change == 0: - console.print( - f"""Percent change not found for: {stock["asset"]}: {stock["name"]}""" - ) - new_df = pd.DataFrame( - { - "Ticker": stock["asset"], - "Name": stock["name"], - "Percent Change": pct_changes.get(stock["asset"], 0), - }, - index=[0], - ) - - df = pd.concat([df, new_df], ignore_index=True) - - sorted_df = df.sort_values(by="Percent Change", ascending=False, inplace=False) - - return sorted_df diff --git a/openbb_terminal/etf/fmp_view.py b/openbb_terminal/etf/fmp_view.py deleted file mode 100644 index d7cccb4971a..00000000000 --- a/openbb_terminal/etf/fmp_view.py +++ /dev/null @@ -1,197 +0,0 @@ -"""FinancialModelingPrep view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.etf import fmp_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_etf_weightings( - name: str, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display sector weightings allocation of ETF. [Source: FinancialModelingPrep] - - Parameters - ---------- - name: str - ETF name - raw: bool - Display sector weighting allocation - min_pct_to_display: float - Minimum percentage to display sector - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - sectors = fmp_model.get_etf_sector_weightings(name) - if not sectors: - return console.print("No data was found for that ETF\n") - - title = f"Sector holdings of {name}" - - sector_weights_formatted = {} - for sector_weight in sectors: - sector_weights_formatted[sector_weight["sector"]] = ( - float(sector_weight["weightPercentage"].strip("%")) / 100 - ) - sector_weights_formatted = dict(sorted(sector_weights_formatted.items())) - - legend, values = zip(*sector_weights_formatted.items()) - colors = theme.get_colors() - - fig = OpenBBFigure.create_subplots( - 1, - 3, - specs=[[{"type": "domain"}, {"type": "pie", "colspan": 2}, None]], - row_heights=[1], - column_widths=[0.1, 0.8, 0.1], - ) - - fig.add_pie( - labels=legend, - values=values, - textinfo="label+percent", - hoverinfo="label+percent", - automargin=True, - rotation=45, - row=1, - col=2, - ) - fig.update_traces( - textposition="outside", - textfont_size=15, - marker=dict( - colors=colors, - line=dict(color="#F5EFF3", width=0.8), - ), - ) - - fig.update_layout( - margin=dict(t=40, b=20), - title=dict( - text=title, - y=0.98, - x=0.5, - xanchor="center", - yanchor="top", - ), - colorway=colors, - showlegend=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "weights", - pd.DataFrame([sector_weights_formatted]).T, - sheet_name, - fig, - ) - - if raw: - sectors_df = pd.DataFrame(sectors).sort_values(by="sector") - return print_rich_table( - sectors_df, - headers=["Sector", "Weight"], - show_index=False, - title=f"\n{title}", - export=bool(export), - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def view_etf_holdings_performance( - ticker: str, - start_date: str, - end_date: str, - limit: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display ETF's holdings' performance over specified time. [Source: FinancialModelingPrep] - Parameters - ---------- - ticker: str - ETF ticker. - start_date: str - Date from which data is fetched in format YYYY-MM-DD. - end: str - Date from which data is fetched in format YYYY-MM-DD. - limit: int - Limit number of holdings to view. Sorted by holding percentage (desc). - raw: bool - Display holding performance - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data. - """ - data = fmp_model.get_holdings_pct_change(ticker, start_date, end_date, limit)[::-1] - - if raw: - print_rich_table( - data, - show_index=False, - headers=["Ticker", "Name", "Percent Change"], - title="ETF Holdings' Performance", - limit=limit, - export=bool(export), - ) - - fig = OpenBBFigure() - - if not raw or fig.is_image_export(export): - fig.add_bar( - hovertext=[f"{x:.2f}" + "%" for x in data["Percent Change"]], - x=data["Percent Change"], - y=data["Name"], - name="Stock", - orientation="h", - marker_color=[ - "darkgreen" if x > 0 else "darkred" for x in data["Percent Change"] - ], - ) - - fig.update_layout( - title=f"Percent Change in Price for Each Holding from {start_date} to {end_date} for {ticker}", - xaxis=dict(title="Percent Change"), - yaxis=dict(title="Asset Name"), - ) - - if export: - export_data( - export_type=export, - dir_path=os.path.dirname(os.path.abspath(__file__)), - func_name=f"{ticker}_holdings_perf", - df=data, - sheet_name=sheet_name, - figure=fig, - ) - return - - fig.show(external=raw or bool(export)) - - return diff --git a/openbb_terminal/etf/stockanalysis_model.py b/openbb_terminal/etf/stockanalysis_model.py deleted file mode 100644 index 91cf707a653..00000000000 --- a/openbb_terminal/etf/stockanalysis_model.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Stockanalysis.com/etf Model""" - -__docformat__ = "numpy" - -import logging -import pathlib -from typing import List, Tuple - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - -csv_path = pathlib.Path(__file__).parent / "etfs.csv" - - -@log_start_end(log=logger) -def get_all_names_symbols() -> Tuple[List[str], List[str]]: - """Gets all etf names and symbols - - Returns - ------- - Tuple[List[str], List[str]] - List of all available etf symbols, List of all available etf names - """ - - etf_symbols = [] - etf_names = [] - - # 11/25 I am hard coding the etf lists because of stockanalysis changing the format of their website - data = pd.read_csv(csv_path) - etf_symbols = data.s.to_list() - etf_names = data.n.to_list() - return etf_symbols, etf_names - - -@log_start_end(log=logger) -def get_etf_overview(symbol: str) -> pd.DataFrame: - """Get overview data for selected etf - - Parameters - ---------- - etf_symbol : str - Etf symbol to get overview for - - Returns - ------- - df : pd.DataFrame - Dataframe of stock overview data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.etf.overview("SPY") - """ - r = request( - f"https://stockanalysis.com/etf/{symbol}", - headers={"User-Agent": get_user_agent()}, - ) - soup = BeautifulSoup(r.text, "html.parser") - tables = soup.findAll("table") - texts = [] - for tab in tables[:2]: - entries = tab.findAll("td") - for ent in entries: - texts.append(ent.get_text()) - - var_cols = [0, 2, 4, 6, 8, 10, 12, 18, 20, 22, 26, 28, 30, 32] - vals = [idx + 1 for idx in var_cols] - columns = [texts[idx] for idx in var_cols] - data = [texts[idx] for idx in vals] - df = pd.DataFrame(data, index=columns, columns=[symbol.upper()]) - return df - - -@log_start_end(log=logger) -def get_etf_holdings(symbol: str) -> pd.DataFrame: - """Get ETF holdings - - Parameters - ---------- - symbol: str - Symbol to get holdings for - - Returns - ------- - df: pd.DataFrame - Dataframe of holdings - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.etf.holdings("SPY") - """ - - link = f"https://stockanalysis.com/etf/{symbol}/holdings/" - r = request(link, headers={"User-Agent": get_user_agent()}) - try: - df = pd.read_html(r.content)[0] - df["Symbol"] = df["Symbol"].fillna("N/A") - df = df.set_index("Symbol") - df = df[["Name", "% Weight", "Shares"]] - df = df.rename(columns={"% Weight": "% Of Etf"}) - except ValueError: - df = pd.DataFrame() - return df - - -@log_start_end(log=logger) -def compare_etfs(symbols: List[str]) -> pd.DataFrame: - """Compare selected ETFs - - Parameters - ---------- - symbols : List[str] - ETF symbols to compare - - Returns - ------- - df_compare : pd.DataFrame - Dataframe of etf comparisons - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> compare_etfs = openbb.etf.compare(["SPY", "QQQ", "IWM"]) - """ - - df_compare = pd.DataFrame() - for symbol in symbols: - df_compare = pd.concat([df_compare, get_etf_overview(symbol)], axis=1) - - return df_compare - - -@log_start_end(log=logger) -def get_etfs_by_name(name_to_search: str) -> pd.DataFrame: - """Get an ETF symbol and name based on ETF string to search. [Source: StockAnalysis] - - Parameters - ---------- - name_to_search: str - ETF name to match - - Returns - ------- - df: pd.Dataframe - Dataframe with symbols and names - """ - all_symbols, all_names = get_all_names_symbols() - - filtered_symbols = list() - filtered_names = list() - for symbol, name in zip(all_symbols, all_names): - if name_to_search.lower() in name.lower(): - filtered_symbols.append(symbol) - filtered_names.append(name) - - df = pd.DataFrame( - list(zip(filtered_symbols, filtered_names)), columns=["Symbol", "Name"] - ) - - return df diff --git a/openbb_terminal/etf/stockanalysis_view.py b/openbb_terminal/etf/stockanalysis_view.py deleted file mode 100644 index e7fd76dccf0..00000000000 --- a/openbb_terminal/etf/stockanalysis_view.py +++ /dev/null @@ -1,166 +0,0 @@ -"""StockAnalysis.com view functions""" - -__docformat__ = "numpy" - -import logging -import os -from typing import List, Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.etf import stockanalysis_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def view_overview(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Print etf overview information - - Parameters - ---------- - symbol:str - ETF symbols to display overview for - export:str - Format to export data - """ - - if symbol.upper() not in stockanalysis_model.get_all_names_symbols()[0]: - console.print(f"{symbol.upper()} not found in ETFs\n") - return - - data = stockanalysis_model.get_etf_overview(symbol) - - print_rich_table( - data, - headers=list(data.columns), - title="ETF Overview Information", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "overview", - data, - sheet_name, - ) - - -@log_start_end(log=logger) -def view_holdings( - symbol: str, limit: int = 10, export: str = "", sheet_name: Optional[str] = None -): - """ - - Parameters - ---------- - symbol: str - ETF symbol to show holdings for - limit: int - Number of holdings to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - - data = stockanalysis_model.get_etf_holdings(symbol) - print_rich_table( - data, - headers=list(data.columns), - title="ETF Holdings", - show_index=True, - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "holdings", - data, - sheet_name, - ) - - -@log_start_end(log=logger) -def view_comparisons( - symbols: List[str], export: str = "", sheet_name: Optional[str] = None -): - """Show ETF comparisons - - Parameters - ---------- - symbols: List[str] - List of ETF symbols - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - - etf_list = stockanalysis_model.get_all_names_symbols()[0] - for etf in symbols: - if etf not in etf_list: - console.print(f"{etf} not a known symbol.\n") - symbols.remove(etf) - - data = stockanalysis_model.compare_etfs(symbols) - if data.empty: - console.print("No data found for given ETFs\n") - return - print_rich_table( - data, - headers=list(data.columns), - title="ETF Comparisons", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "overview", - data, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_etf_by_name( - name: str, limit: int = 10, export: str = "", sheet_name: Optional[str] = None -): - """Display ETFs matching search string. [Source: StockAnalysis] - - Parameters - ---------- - name: str - String being matched - limit: int - Limit of ETFs to display - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export to given file type - - """ - matching_etfs = stockanalysis_model.get_etfs_by_name(name) - - print_rich_table( - matching_etfs, - show_index=False, - title="ETF Search Result", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ln_sa", - matching_etfs, - sheet_name, - ) diff --git a/openbb_terminal/etf/technical_analysis/ta_controller.py b/openbb_terminal/etf/technical_analysis/ta_controller.py deleted file mode 100644 index 5d19a4c5da3..00000000000 --- a/openbb_terminal/etf/technical_analysis/ta_controller.py +++ /dev/null @@ -1,1456 +0,0 @@ -"""Technical Analysis Controller Module""" - -__docformat__ = "numpy" -# pylint:disable=too-many-lines -# pylint:disable=R0904,C0201 - -import argparse -import logging -from datetime import datetime -from typing import List, Optional - -import numpy as np -import pandas as pd - -from openbb_terminal.common.technical_analysis import ( - custom_indicators_view, - momentum_view, - overlap_model, - overlap_view, - trend_indicators_view, - volatility_model, - volatility_view, - volume_view, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_FIGURES_ALLOWED, - check_non_negative, - check_positive, - check_positive_float, - check_positive_list, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class TechnicalAnalysisController(BaseController): - """Technical Analysis Controller class""" - - CHOICES_COMMANDS = [ - "ema", - "sma", - "wma", - "hma", - "vwap", - "zlma", - "cci", - "macd", - "rsi", - "stoch", - "fisher", - "cg", - "adx", - "aroon", - "bbands", - "donchian", - "kc", - "ad", - "adosc", - "obv", - "fib", - "clenow", - "demark", - "atr", - ] - - PATH = "/etf/ta/" - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - start: datetime, - data: pd.DataFrame, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - self.start = start - self.data = data - self.interval = "1440min" - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("etf/ta/", 90) - mt.add_param("_ticker", self.ticker) - mt.add_raw("\n") - mt.add_info("_overlap_") - mt.add_cmd("ema") - mt.add_cmd("sma") - mt.add_cmd("wma") - mt.add_cmd("hma") - mt.add_cmd("zlma") - mt.add_cmd("vwap") - mt.add_info("_momentum_") - mt.add_cmd("cci") - mt.add_cmd("macd") - mt.add_cmd("rsi") - mt.add_cmd("stoch") - mt.add_cmd("fisher") - mt.add_cmd("cg") - mt.add_cmd("clenow") - mt.add_cmd("demark") - mt.add_info("_trend_") - mt.add_cmd("adx") - mt.add_cmd("aroon") - mt.add_info("_volatility_") - mt.add_cmd("bbands") - mt.add_cmd("donchian") - mt.add_cmd("kc") - mt.add_cmd("atr") - mt.add_info("_volume_") - mt.add_cmd("ad") - mt.add_cmd("adosc") - mt.add_cmd("obv") - mt.add_info("_custom_") - mt.add_cmd("fib") - console.print(text=mt.menu_text, menu="ETF - Technical Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - return ["etf", f"load {self.ticker}", "ta"] - return [] - - @log_start_end(log=logger) - def call_ema(self, other_args: List[str]): - """Process ema command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ema", - description=""" - The Exponential Moving Average is a staple of technical - analysis and is used in countless technical indicators. In a Simple Moving - Average, each value in the time period carries equal weight, and values outside - of the time period are not included in the average. However, the Exponential - Moving Average is a cumulative calculation, including all data. Past values have - a diminishing contribution to the average, while more recent values have a greater - contribution. This method allows the moving average to be more responsive to changes - in the data. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=overlap_model.WINDOW_LENGTHS, - help="Window lengths. Multiple values indicated as comma separated values.", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_non_negative, - default=0, - help="offset", - choices=range(0, 100), - metavar="N_OFFSET", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - overlap_view.view_ma( - ma_type="EMA", - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_sma(self, other_args: List[str]): - """Process sma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sma", - description=""" - Moving Averages are used to smooth the data in an array to - help eliminate noise and identify trends. The Simple Moving Average is literally - the simplest form of a moving average. Each output value is the average of the - previous n values. In a Simple Moving Average, each value in the time period carries - equal weight, and values outside of the time period are not included in the average. - This makes it less responsive to recent changes in the data, which can be useful for - filtering out those changes. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=overlap_model.WINDOW_LENGTHS, - help="Window lengths. Multiple values indicated as comma separated values. ", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_non_negative, - default=0, - help="offset", - choices=range(0, 100), - metavar="N_OFFSET", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - overlap_view.view_ma( - ma_type="SMA", - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_wma(self, other_args: List[str]): - """Process wma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="wma", - description=""" - A Weighted Moving Average puts more weight on recent data and less on past data. - This is done by multiplying each bar’s price by a weighting factor. Because of its - unique calculation, WMA will follow prices more closely than a corresponding Simple - Moving Average. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=overlap_model.WINDOW_LENGTHS, - help="Window lengths. Multiple values indicated as comma separated values. ", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_non_negative, - default=0, - help="offset", - choices=range(0, 100), - metavar="N_OFFSET", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - overlap_view.view_ma( - ma_type="WMA", - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_hma(self, other_args: List[str]): - """Process hma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hma", - description=""" - The Hull Moving Average solves the age old dilemma of making a moving average - more responsive to current price activity whilst maintaining curve smoothness. - In fact the HMA almost eliminates lag altogether and manages to improve smoothing - at the same time. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=overlap_model.WINDOW_LENGTHS2, - help="Window lengths. Multiple values indicated as comma separated values. ", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_non_negative, - default=0, - help="offset", - choices=range(0, 100), - metavar="N_OFFSET", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - overlap_view.view_ma( - ma_type="HMA", - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_zlma(self, other_args: List[str]): - """Process zlma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="zlma", - description=""" - The zero lag exponential moving average (ZLEMA) indicator - was created by John Ehlers and Ric Way. The idea is do a - regular exponential moving average (EMA) calculation but - on a de-lagged data instead of doing it on the regular data. - Data is de-lagged by removing the data from "lag" days ago - thus removing (or attempting to) the cumulative effect of - the moving average. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=[20], - help="Window lengths. Multiple values indicated as comma separated values.", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_non_negative, - default=0, - help="offset", - choices=range(0, 100), - metavar="N_OFFSET", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - overlap_view.view_ma( - ma_type="ZLMA", - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_vwap(self, other_args: List[str]): - """Process vwap command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="vwap", - description=""" - The Volume Weighted Average Price that measures the average typical price - by volume. It is typically used with intraday charts to identify general direction. - """, - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_non_negative, - default=0, - help="offset", - choices=range(0, 100), - metavar="N_OFFSET", - ) - parser.add_argument( - "--start", - dest="start", - type=valid_date, - help="Starting date to select", - required="--end" in other_args, - ) - parser.add_argument( - "--end", - dest="end", - type=valid_date, - help="Ending date to select", - required="--start" in other_args, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - # Daily - if self.interval == "1440min": - if not ns_parser.start: - console.print( - "If no date conditions, VWAP should be used with intraday data. \n" - ) - return - interval_text = "Daily" - else: - interval_text = self.interval - - overlap_view.view_vwap( - data=self.data, - symbol=self.ticker, - start_date=ns_parser.start, - end_date=ns_parser.end, - offset=ns_parser.n_offset, - interval=interval_text, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cci(self, other_args: List[str]): - """Process cci command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cci", - description=""" - The CCI is designed to detect beginning and ending market trends. - The range of 100 to -100 is the normal trading range. CCI values outside of this - range indicate overbought or oversold conditions. You can also look for price - divergence in the CCI. If the price is making new highs, and the CCI is not, - then a price correction is likely. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - choices=range(1, 100), - metavar="N_LENGTH", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=0.015, - help="scalar", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_cci( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_macd(self, other_args: List[str]): - """Process macd command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="macd", - description=""" - The Moving Average Convergence Divergence (MACD) is the difference - between two Exponential Moving Averages. The Signal line is an Exponential Moving - Average of the MACD. \n \n The MACD signals trend changes and indicates the start - of new trend direction. High values indicate overbought conditions, low values - indicate oversold conditions. Divergence with the price indicates an end to the - current trend, especially if the MACD is at extreme high or low values. When the MACD - line crosses above the signal line a buy signal is generated. When the MACD crosses - below the signal line a sell signal is generated. To confirm the signal, the MACD - should be above zero for a buy, and below zero for a sell. - """, - ) - parser.add_argument( - "--fast", - action="store", - dest="n_fast", - type=check_positive, - default=12, - help="The short period.", - choices=range(1, 100), - metavar="N_FAST", - ) - parser.add_argument( - "--slow", - action="store", - dest="n_slow", - type=check_positive, - default=26, - help="The long period.", - choices=range(1, 100), - metavar="N_SLOW", - ) - parser.add_argument( - "--signal", - action="store", - dest="n_signal", - type=check_positive, - default=9, - help="The signal period.", - choices=range(1, 100), - metavar="N_SIGNAL", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_macd( - symbol=self.ticker, - data=self.data["Adj Close"], - n_fast=ns_parser.n_fast, - n_slow=ns_parser.n_slow, - n_signal=ns_parser.n_signal, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rsi(self, other_args: List[str]): - """Process rsi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rsi", - description=""" - The Relative Strength Index (RSI) calculates a ratio of the - recent upward price movements to the absolute price movement. The RSI ranges - from 0 to 100. The RSI is interpreted as an overbought/oversold indicator when - the value is over 70/below 30. You can also look for divergence with price. If - the price is making new highs/lows, and the RSI is not, it indicates a reversal. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - choices=range(1, 100), - metavar="N_LENGTH", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - parser.add_argument( - "-d", - "--drift", - action="store", - dest="n_drift", - type=check_positive, - default=1, - help="drift", - choices=range(1, 100), - metavar="N_DRIFT", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_rsi( - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - drift=ns_parser.n_drift, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_stoch(self, other_args: List[str]): - """Process stoch command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="stoch", - description=""" - The Stochastic Oscillator measures where the close is in relation - to the recent trading range. The values range from zero to 100. %D values over 75 - indicate an overbought condition; values under 25 indicate an oversold condition. - When the Fast %D crosses above the Slow %D, it is a buy signal; when it crosses - below, it is a sell signal. The Raw %K is generally considered too erratic to use - for crossover signals. - """, - ) - parser.add_argument( - "-k", - "--fastkperiod", - action="store", - dest="n_fastkperiod", - type=check_positive, - default=14, - help="The time period of the fastk moving average", - choices=range(1, 100), - metavar="N_FASTKPERIOD", - ) - parser.add_argument( - "-d", - "--slowdperiod", - action="store", - dest="n_slowdperiod", - type=check_positive, - default=3, - help="The time period of the slowd moving average", - choices=range(1, 100), - metavar="N_SLOWDPERIOD", - ) - parser.add_argument( - "--slowkperiod", - action="store", - dest="n_slowkperiod", - type=check_positive, - default=3, - help="The time period of the slowk moving average", - choices=range(1, 100), - metavar="N_SLOWKPERIOD", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_stoch( - symbol=self.ticker, - data=self.data, - fastkperiod=ns_parser.n_fastkperiod, - slowdperiod=ns_parser.n_slowdperiod, - slowkperiod=ns_parser.n_slowkperiod, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fisher(self, other_args: List[str]): - """Process fisher command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fisher", - description=""" - The Fisher Transform is a technical indicator created by John F. Ehlers - that converts prices into a Gaussian normal distribution.1 The indicator - highlights when prices have moved to an extreme, based on recent prices. - This may help in spotting turning points in the price of an asset. It also - helps show the trend and isolate the price waves within a trend. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - choices=range(1, 100), - metavar="N_LENGTH", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_fisher( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cg(self, other_args: List[str]): - """Process cg command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cg", - description=""" - The Center of Gravity indicator, in short, is used to anticipate future price movements - and to trade on price reversals as soon as they happen. However, just like other oscillators, - the COG indicator returns the best results in range-bound markets and should be avoided when - the price is trending. Traders who use it will be able to closely speculate the upcoming - price change of the asset. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - choices=range(1, 100), - metavar="N_LENGTH", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_cg( - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_adx(self, other_args: List[str]): - """Process adx command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="adx", - description=""" - The ADX is a Welles Wilder style moving average of the Directional Movement Index (DX). - The values range from 0 to 100, but rarely get above 60. To interpret the ADX, consider - a high number to be a strong trend, and a low number, a weak trend. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - choices=range(1, 100), - metavar="N_LENGTH", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - parser.add_argument( - "-d", - "--drift", - action="store", - dest="n_drift", - type=check_positive, - default=1, - help="drift", - choices=range(1, 100), - metavar="N_DRIFT", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - trend_indicators_view.display_adx( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - drift=ns_parser.n_drift, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_aroon(self, other_args: List[str]): - """Process aroon command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="aroon", - description=""" - The word aroon is Sanskrit for "dawn's early light." The Aroon - indicator attempts to show when a new trend is dawning. The indicator consists - of two lines (Up and Down) that measure how long it has been since the highest - high/lowest low has occurred within an n period range. \n \n When the Aroon Up is - staying between 70 and 100 then it indicates an upward trend. When the Aroon Down - is staying between 70 and 100 then it indicates an downward trend. A strong upward - trend is indicated when the Aroon Up is above 70 while the Aroon Down is below 30. - Likewise, a strong downward trend is indicated when the Aroon Down is above 70 while - the Aroon Up is below 30. Also look for crossovers. When the Aroon Down crosses above - the Aroon Up, it indicates a weakening of the upward trend (and vice versa). - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=25, - help="length", - choices=range(1, 100), - metavar="N_LENGTH", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - trend_indicators_view.display_aroon( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bbands(self, other_args: List[str]): - """Process bbands command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bbands", - description=""" - Bollinger Bands consist of three lines. The middle band is a simple - moving average (generally 20 periods) of the typical price (TP). The upper and lower - bands are F standard deviations (generally 2) above and below the middle band. - The bands widen and narrow when the volatility of the price is higher or lower, - respectively. \n \nBollinger Bands do not, in themselves, generate buy or sell signals; - they are an indicator of overbought or oversold conditions. When the price is near the - upper or lower band it indicates that a reversal may be imminent. The middle band - becomes a support or resistance level. The upper and lower bands can also be - interpreted as price targets. When the price bounces off of the lower band and crosses - the middle band, then the upper band becomes the price target. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=15, - help="length", - choices=range(1, 100), - metavar="N_LENGTH", - ) - parser.add_argument( - "-s", - "--std", - action="store", - dest="n_std", - type=check_positive_float, - default=2, - help="std", - choices=np.arange(0.0, 10, 0.25).tolist(), - metavar="N_STD", - ) - parser.add_argument( - "-m", - "--mamode", - action="store", - dest="s_mamode", - default="sma", - help="mamode", - choices=volatility_model.MAMODES, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volatility_view.display_bbands( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - n_std=ns_parser.n_std, - mamode=ns_parser.s_mamode, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_donchian(self, other_args: List[str]): - """Process donchian command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="donchian", - description=""" - Donchian Channels are three lines generated by moving average - calculations that comprise an indicator formed by upper and lower - bands around a midrange or median band. The upper band marks the - highest price of a security over N periods while the lower band - marks the lowest price of a security over N periods. The area - between the upper and lower bands represents the Donchian Channel. - """, - ) - parser.add_argument( - "-u", - "--length_upper", - action="store", - dest="n_length_upper", - type=check_positive, - default=20, - help="length", - choices=range(1, 100), - metavar="N_LENGTH_UPPER", - ) - parser.add_argument( - "-l", - "--length_lower", - action="store", - dest="n_length_lower", - type=check_positive, - default=20, - help="length", - choices=range(1, 100), - metavar="N_LENGTH_LOWER", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volatility_view.display_donchian( - symbol=self.ticker, - data=self.data, - upper_length=ns_parser.n_length_upper, - lower_length=ns_parser.n_length_lower, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_kc(self, other_args: List[str]): - """Process kc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="kc", - description=""" - Keltner Channels are volatility-based bands that are placed - on either side of an asset's price and can aid in determining - the direction of a trend.The Keltner channel uses the average - true range (ATR) or volatility, with breaks above or below the top - and bottom barriers signaling a continuation. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=20, - help="Window length", - choices=range(1, 100), - metavar="N_LENGTH", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=2, - help="scalar", - ) - parser.add_argument( - "-m", - "--mamode", - action="store", - dest="s_mamode", - default="ema", - choices=volatility_model.MAMODES, - help="mamode", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_non_negative, - default=0, - help="offset", - choices=range(0, 100), - metavar="N_OFFSET", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volatility_view.view_kc( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - mamode=ns_parser.s_mamode, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ad(self, other_args: List[str]): - """Process ad command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ad", - description=""" - The Accumulation/Distribution Line is similar to the On Balance - Volume (OBV), which sums the volume times +1/-1 based on whether the close is - higher than the previous close. The Accumulation/Distribution indicator, however - multiplies the volume by the close location value (CLV). The CLV is based on the - movement of the issue within a single bar and can be +1, -1 or zero. \n \n - The Accumulation/Distribution Line is interpreted by looking for a divergence in - the direction of the indicator relative to price. If the Accumulation/Distribution - Line is trending upward it indicates that the price may follow. Also, if the - Accumulation/Distribution Line becomes flat while the price is still rising (or falling) - then it signals an impending flattening of the price. - """, - ) - parser.add_argument( - "--open", - action="store_true", - default=False, - dest="b_use_open", - help="uses open value of stock", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volume_view.display_ad( - symbol=self.ticker, - data=self.data, - use_open=ns_parser.b_use_open, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_adosc(self, other_args: List[str]): - """Process adosc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="adosc", - description=""" - Accumulation/Distribution Oscillator, also known as the Chaikin Oscillator - is essentially a momentum indicator, but of the Accumulation-Distribution line - rather than merely price. It looks at both the strength of price moves and the - underlying buying and selling pressure during a given time period. The oscillator - reading above zero indicates net buying pressure, while one below zero registers - net selling pressure. Divergence between the indicator and pure price moves are - the most common signals from the indicator, and often flag market turning points. - """, - ) - parser.add_argument( - "--open", - action="store_true", - default=False, - dest="b_use_open", - help="uses open value of stock", - ) - parser.add_argument( - "--fast", - action="store", - dest="n_length_fast", - type=check_positive, - default=3, - help="fast length", - choices=range(1, 100), - metavar="N_LENGTH_FAST", - ) - parser.add_argument( - "--slow", - action="store", - dest="n_length_slow", - type=check_positive, - default=10, - help="slow length", - choices=range(1, 100), - metavar="N_LENGTH_SLOW", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volume_view.display_adosc( - symbol=self.ticker, - data=self.data, - use_open=ns_parser.b_use_open, - fast=ns_parser.n_length_fast, - slow=ns_parser.n_length_slow, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_obv(self, other_args: List[str]): - """Process obv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="obv", - description=""" - The On Balance Volume (OBV) is a cumulative total of the up and - down volume. When the close is higher than the previous close, the volume is added - to the running total, and when the close is lower than the previous close, the volume - is subtracted from the running total. \n \n To interpret the OBV, look for the OBV - to move with the price or precede price moves. If the price moves before the OBV, - then it is a non-confirmed move. A series of rising peaks, or falling troughs, in the - OBV indicates a strong trend. If the OBV is flat, then the market is not trending. - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volume_view.display_obv( - symbol=self.ticker, - data=self.data, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fib(self, other_args: List[str]): - """Process fib command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fib", - description="Calculates the fibonacci retracement levels", - ) - parser.add_argument( - "-p", - "--period", - dest="period", - type=check_positive, - help="Days to look back for retracement", - default=120, - choices=range(1, 960), - metavar="PERIOD", - ) - parser.add_argument( - "--start", - dest="start", - type=valid_date, - help="Starting date to select", - required="--end" in other_args, - ) - parser.add_argument( - "--end", - dest="end", - type=valid_date, - help="Ending date to select", - required="--start" in other_args, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - custom_indicators_view.fibonacci_retracement( - symbol=self.ticker, - data=self.data, - limit=ns_parser.period, - start_date=ns_parser.start, - end_date=ns_parser.end, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_clenow(self, other_args: List[str]): - """Process clenow command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="clenow", - description="Calculates the Clenow Volatility Adjusted Momentum.", - ) - parser.add_argument( - "-p", - "--period", - dest="period", - help="Lookback period for regression", - default=90, - type=check_positive, - ) - - if self.interval != "1440min": - console.print( - "[red]This regression should be performed with daily data and at least 90 days.[/red]" - ) - return - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - momentum_view.display_clenow_momentum( - self.data["Adj Close"], - self.ticker.upper(), - ns_parser.period, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_demark(self, other_args: List[str]): - """Process demark command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="demark", - description="Calculates the Demark sequential indicator.", - ) - parser.add_argument( - "-m", - "--min", - help="Minimum value of indicator to show (declutters plot).", - dest="min_to_show", - type=check_positive, - default=5, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_demark( - self.data, - self.ticker.upper(), - min_to_show=ns_parser.min_to_show, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_atr(self, other_args: List[str]): - """Process atr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="atr", - description=""" - Averge True Range is used to measure volatility, especially volatility caused by - gaps or limit moves. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="Window length", - ) - parser.add_argument( - "-m", - "--mamode", - action="store", - dest="s_mamode", - default="ema", - choices=volatility_model.MAMODES, - help="mamode", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - volatility_view.display_atr( - data=self.data, - symbol=self.ticker, - window=ns_parser.n_length, - mamode=ns_parser.s_mamode, - offset=ns_parser.n_offset, - export=ns_parser.export, - ) diff --git a/openbb_terminal/featflags_controller.py b/openbb_terminal/featflags_controller.py deleted file mode 100644 index 0f29a6d5d9e..00000000000 --- a/openbb_terminal/featflags_controller.py +++ /dev/null @@ -1,190 +0,0 @@ -"""Feature Flags Controller Module""" - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import logging -from pathlib import Path -from typing import List, Optional, Union - -# IMPORTATION THIRDPARTY -from openbb_terminal.core.session.current_user import ( - get_current_user, - set_preference, -) -from openbb_terminal.core.session.env_handler import write_to_dotenv - -# IMPORTATION INTERNAL -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -# pylint: disable=too-many-lines,no-member,too-many-public-methods,C0302 -# pylint:disable=import-outside-toplevel - -logger = logging.getLogger(__name__) - - -def set_and_save_preference(name: str, value: Union[bool, Path, str]): - """Set preference and write to .env - - Parameters - ---------- - name : str - Preference name - value : Union[bool, Path, str] - Preference value - """ - set_preference(name, value) - write_to_dotenv("OPENBB_" + name, str(value)) - - -class FeatureFlagsController(BaseController): - """Feature Flags Controller class""" - - CHOICES_COMMANDS: List[str] = [ - "retryload", - "tab", - "interactive", - "cls", - "watermark", - "promptkit", - "thoughts", - "reporthtml", - "exithelp", - "rcontext", - "richpanel", - "tbhint", - "overwrite", - "version", - ] - PATH = "/featflags/" - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - current_user = get_current_user() - - mt = MenuText("featflags/") - mt.add_info("_info_") - mt.add_raw("\n") - mt.add_setting("retryload", current_user.preferences.RETRY_WITH_LOAD) - mt.add_setting("interactive", current_user.preferences.USE_INTERACTIVE_DF) - mt.add_setting("cls", current_user.preferences.USE_CLEAR_AFTER_CMD) - mt.add_setting("promptkit", current_user.preferences.USE_PROMPT_TOOLKIT) - mt.add_setting("thoughts", current_user.preferences.ENABLE_THOUGHTS_DAY) - mt.add_setting("reporthtml", current_user.preferences.OPEN_REPORT_AS_HTML) - mt.add_setting("exithelp", current_user.preferences.ENABLE_EXIT_AUTO_HELP) - mt.add_setting("rcontext", current_user.preferences.REMEMBER_CONTEXTS) - mt.add_setting("richpanel", current_user.preferences.ENABLE_RICH_PANEL) - mt.add_setting("tbhint", current_user.preferences.TOOLBAR_HINT) - mt.add_setting("overwrite", current_user.preferences.FILE_OVERWRITE) - mt.add_setting("version", current_user.preferences.SHOW_VERSION) - - console.print(text=mt.menu_text, menu="Feature Flags") - - def call_overwrite(self, _): - """Process overwrite command""" - set_and_save_preference( - "FILE_OVERWRITE", not get_current_user().preferences.FILE_OVERWRITE - ) - - def call_version(self, _): - """Process version command""" - set_and_save_preference( - "SHOW_VERSION", not get_current_user().preferences.SHOW_VERSION - ) - - def call_retryload(self, _): - """Process retryload command""" - set_and_save_preference( - "RETRY_WITH_LOAD", not get_current_user().preferences.RETRY_WITH_LOAD - ) - - @log_start_end(log=logger) - def call_interactive(self, _): - """Process interactive command""" - set_and_save_preference( - "USE_INTERACTIVE_DF", not get_current_user().preferences.USE_INTERACTIVE_DF - ) - - @log_start_end(log=logger) - def call_cls(self, _): - """Process cls command""" - set_and_save_preference( - "USE_CLEAR_AFTER_CMD", - not get_current_user().preferences.USE_CLEAR_AFTER_CMD, - ) - - @log_start_end(log=logger) - def call_promptkit(self, _): - """Process promptkit command""" - set_and_save_preference( - "USE_PROMPT_TOOLKIT", - not get_current_user().preferences.USE_PROMPT_TOOLKIT, - ) - - @log_start_end(log=logger) - def call_thoughts(self, _): - """Process thoughts command""" - set_and_save_preference( - "ENABLE_THOUGHTS_DAY", - not get_current_user().preferences.ENABLE_THOUGHTS_DAY, - ) - - @log_start_end(log=logger) - def call_reporthtml(self, _): - """Process reporthtml command""" - set_and_save_preference( - "OPEN_REPORT_AS_HTML", - not get_current_user().preferences.OPEN_REPORT_AS_HTML, - ) - - @log_start_end(log=logger) - def call_exithelp(self, _): - """Process exithelp command""" - set_and_save_preference( - "ENABLE_EXIT_AUTO_HELP", - not get_current_user().preferences.ENABLE_EXIT_AUTO_HELP, - ) - - @log_start_end(log=logger) - def call_rcontext(self, _): - """Process rcontext command""" - set_and_save_preference( - "REMEMBER_CONTEXTS", - not get_current_user().preferences.REMEMBER_CONTEXTS, - ) - - @log_start_end(log=logger) - def call_dt(self, _): - """Process dt command""" - set_and_save_preference( - "USE_DATETIME", not get_current_user().preferences.USE_DATETIME - ) - - @log_start_end(log=logger) - def call_richpanel(self, _): - """Process richpanel command""" - set_and_save_preference( - "ENABLE_RICH_PANEL", - not get_current_user().preferences.ENABLE_RICH_PANEL, - ) - - @log_start_end(log=logger) - def call_tbhint(self, _): - """Process tbhint command""" - if get_current_user().preferences.TOOLBAR_HINT: - console.print("Will take effect when running terminal next.") - set_and_save_preference( - "TOOLBAR_HINT", not get_current_user().preferences.TOOLBAR_HINT - ) diff --git a/openbb_terminal/fixedincome/__init__.py b/openbb_terminal/fixedincome/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/fixedincome/commercial_paper.csv b/openbb_terminal/fixedincome/commercial_paper.csv deleted file mode 100644 index 8897cb334c5..00000000000 --- a/openbb_terminal/fixedincome/commercial_paper.csv +++ /dev/null @@ -1,241 +0,0 @@ -Maturity,Category,Grade,Title,Description,FRED Series ID -overnight,asset_backed,aa,Overnight AA Asset-Backed Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPAAAD01NB -7d,asset_backed,aa,7-Day AA Asset-Backed Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPAAAD07NB -15d,asset_backed,aa,15-Day AA Asset-Backed Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPAAAD15NB -30d,asset_backed,aa,30-Day AA Asset-Backed Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPAAAD30NB -60d,asset_backed,aa,60-Day AA Asset-Backed Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPAAAD60NB -90d,asset_backed,aa,90-Day AA Asset-Backed Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPAAAD90NB -overnight,financial,aa,Overnight AA Financial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPFAAD01NB -7d,financial,aa,7-Day AA Financial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPFAAD07NB -15d,financial,aa,15-Day AA Financial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPFAAD15NB -30d,financial,aa,30-Day AA Financial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPFAAD30NB -60d,financial,aa,60-Day AA Financial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPFAAD60NB -90d,financial,aa,90-Day AA Financial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPFAAD90NB -overnight,non_financial,aa,Overnight AA Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNAAD01NB -7d,non_financial,aa,7-Day AA Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNAAD07NB -15d,non_financial,aa,15-Day AA Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNAAD15NB -30d,non_financial,aa,30-Day AA Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNAAD30NB -60d,non_financial,aa,60-Day AA Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNAAD60NB -90d,non_financial,aa,90-Day AA Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNAAD90NB -overnight,non_financial,a2_p2,Overnight A2/P2 Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNA2P2D01NB -7d,non_financial,a2_p2,7-Day A2/P2 Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNA2P2D07NB -15d,non_financial,a2_p2,15-Day A2/P2 Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNA2P2D15NB -30d,non_financial,a2_p2,30-Day A2/P2 Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNA2P2D30NB -60d,non_financial,a2_p2,60-Day A2/P2 Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNA2P2D60NB -90d,non_financial,a2_p2,90-Day A2/P2 Nonfinancial Commercial Paper Interest Rate,"To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades to estimate a relation between interest rates on the traded securities and their maturities. In this calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is, the offer side) and are weighted according to the face value of the CP so that larger trades have a greater effect on the resulting index. With the relation between interest rates and maturities established, the reported interest rates represent the estimated interest rates for the specified maturities. - -Interest rates calculated through the process described above are a statistical aggregation of numerous data reflecting many trades for different issuers, maturities, and so forth. Accordingly, the reported interest rates purport to reflect activity in certain segments of the market, but they may not equal interest rates for any specific trade. As with other statistical processes, this one is designed to minimize the difference between the interest rates at which actual trades occur and the estimated interest rates. - -CP trades included in the calculation are chosen according to the specifications listed in the table below. Data to assess CP trades relative to these criteria are updated daily from numerous publicly available sources. Standard Industrial Classification (SIC) code classifications are taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File Annual Reports with the SEC. When an issuer's primary SIC code is not reported in the SEC directory, the primary SIC code reported in the issuer's financial reports is used; otherwise, SIC codes are determined upon consultation with the Office of Management and Budget's Standard Industrial Classification Manual or its Supplement. - -For a discussion of econometric techniques for fitting the term structure of interest rates, including bibliographic information, see, for example, William S. Cleveland, 1979, ""Robust Locally Weighted Regression and Smoothing Scatterplots,"" Journal of the American Statistical Association, 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988, ""Regression by Local Fitting,"" Journal of Econometrics, 37, 87-114. - -Source: https://www.federalreserve.gov/releases/cp/about.htm -",RIFSPPNA2P2D90NB diff --git a/openbb_terminal/fixedincome/corporate_spot_rates.csv b/openbb_terminal/fixedincome/corporate_spot_rates.csv deleted file mode 100644 index f1b2d0687b5..00000000000 --- a/openbb_terminal/fixedincome/corporate_spot_rates.csv +++ /dev/null @@ -1,205 +0,0 @@ -Maturity,Category,Title,Description,FRED Series ID -1y,spot_rate,1-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 1-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 1 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB1YR -1.5y,spot_rate,1.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 1.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 1.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB1Y6M -2y,par_yield,2-Year High Quality Market (HQM) Corporate Bond Par Yield,The par yield for 2-Year High Quality Market (HQM) Corporate Bond Par Yield is the coupon rate at which bond prices are zero.,HQMCB2YRP -2y,spot_rate,2-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 2-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 2 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB2YR -2.5y,spot_rate,2.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 2.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 2.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB2Y6M -3y,spot_rate,3-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 3-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 3 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB3YR -3.5y,spot_rate,3.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 3.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 3.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB3Y6M -4y,spot_rate,4-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 4-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 4 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB4YR -4.5y,spot_rate,4.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 4.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 4.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB4Y6M -5y,par_yield,5-Year High Quality Market (HQM) Corporate Bond Par Yield,The par yield for 5-Year High Quality Market (HQM) Corporate Bond Par Yield is the coupon rate at which bond prices are zero.,HQMCB5YRP -5y,spot_rate,5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB5YR -5.5y,spot_rate,5.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 5.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 5.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB5Y6M -6 y,spot_rate,6 -Month High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 6 -Month High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 6 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB6MT -6y,spot_rate,6-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 6-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 6 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB6YR -6.5y,spot_rate,6.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 6.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 6.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB6Y6M -7y,spot_rate,7-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 7-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 7 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB7YR -7.5y,spot_rate,7.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 7.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 7.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB7Y6M -8y,spot_rate,8-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 8-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 8 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB8YR -8.5y,spot_rate,8.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 8.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 8.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB8Y6M -9y,spot_rate,9-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 9-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 9 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB9YR -9.5y,spot_rate,9.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 9.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 9.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB9Y6M -10y,par_yield,10-Year High Quality Market (HQM) Corporate Bond Par Yield,The par yield for 10-Year High Quality Market (HQM) Corporate Bond Par Yield is the coupon rate at which bond prices are zero.,HQMCB10YRP -10y,spot_rate,10-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 10-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 10 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB10YR -10.5y,spot_rate,10.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 10.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 10.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB10Y6M -11y,spot_rate,11-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 11-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 11 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB11YR -11.5y,spot_rate,11.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 11.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 11.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB11Y6M -12y,spot_rate,12-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 12-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 12 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB12YR -12.5y,spot_rate,12.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 12.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 12.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB12Y6M -13y,spot_rate,13-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 13-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 13 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB13YR -13.5y,spot_rate,13.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 13.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 13.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB13Y6M -14y,spot_rate,14-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 14-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 14 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB14YR -14.5y,spot_rate,14.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 14.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 14.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB14Y6M -15y,spot_rate,15-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 15-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 15 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB15YR -15.5y,spot_rate,15.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 15.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 15.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB15Y6M -16y,spot_rate,16-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 16-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 16 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB16YR -16.5y,spot_rate,16.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 16.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 16.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB16Y6M -17y,spot_rate,17-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 17-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 17 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB17YR -17.5y,spot_rate,17.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 17.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 17.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB17Y6M -18y,spot_rate,18-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 18-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 18 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB18YR -18.5y,spot_rate,18.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 18.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 18.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB18Y6M -19y,spot_rate,19-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 19-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 19 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB19YR -19.5y,spot_rate,19.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 19.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 19.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB19Y6M -20y,spot_rate,20-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 20-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 20 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB20YR -20.5y,spot_rate,20.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 20.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 20.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB20Y6M -21y,spot_rate,21-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 21-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 21 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB21YR -21.5y,spot_rate,21.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 21.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 21.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB21Y6M -22y,spot_rate,22-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 22-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 22 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB22YR -22.5y,spot_rate,22.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 22.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 22.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB22Y6M -23y,spot_rate,23-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 23-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 23 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB23YR -23.5y,spot_rate,23.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 23.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 23.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB23Y6M -24y,spot_rate,24-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 24-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 24 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB24YR -24.5y,spot_rate,24.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 24.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 24.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB24Y6M -25y,spot_rate,25-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 25-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 25 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB25YR -25.5y,spot_rate,25.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 25.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 25.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB25Y6M -26y,spot_rate,26-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 26-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 26 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB26YR -26.5y,spot_rate,26.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 26.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 26.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB26Y6M -27y,spot_rate,27-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 27-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 27 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB27YR -27.5y,spot_rate,27.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 27.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 27.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB27Y6M -28y,spot_rate,28-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 28-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 28 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB28YR -28.5y,spot_rate,28.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 28.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 28.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB28Y6M -29y,spot_rate,29-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 29-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 29 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB29YR -29.5y,spot_rate,29.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 29.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 29.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB29Y6M -30y,par_yield,30-Year High Quality Market (HQM) Corporate Bond Par Yield,The par yield for 30-Year High Quality Market (HQM) Corporate Bond Par Yield is the coupon rate at which bond prices are zero.,HQMCB30YRP -30y,spot_rate,30-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 30-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 30 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB30YR -30.5y,spot_rate,30.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 30.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 30.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB30Y6M -31y,spot_rate,31-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 31-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 31 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB31YR -31.5y,spot_rate,31.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 31.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 31.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB31Y6M -32y,spot_rate,32-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 32-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 32 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB32YR -32.5y,spot_rate,32.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 32.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 32.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB32Y6M -33y,spot_rate,33-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 33-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 33 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB33YR -33.5y,spot_rate,33.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 33.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 33.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB33Y6M -34y,spot_rate,34-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 34-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 34 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB34YR -34.5y,spot_rate,34.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 34.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 34.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB34Y6M -35y,spot_rate,35-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 35-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 35 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB35YR -35.5y,spot_rate,35.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 35.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 35.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB35Y6M -36y,spot_rate,36-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 36-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 36 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB36YR -36.5y,spot_rate,36.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 36.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 36.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB36Y6M -37y,spot_rate,37-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 37-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 37 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB37YR -37.5y,spot_rate,37.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 37.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 37.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB37Y6M -38y,spot_rate,38-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 38-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 38 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB38YR -38.5y,spot_rate,38.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 38.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 38.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB38Y6M -39y,spot_rate,39-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 39-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 39 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB39YR -39.5y,spot_rate,39.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 39.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 39.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB39Y6M -40y,spot_rate,40-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 40-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 40 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB40YR -40.5y,spot_rate,40.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 40.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 40.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB40Y6M -41y,spot_rate,41-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 41-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 41 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB41YR -41.5y,spot_rate,41.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 41.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 41.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB41Y6M -42y,spot_rate,42-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 42-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 42 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB42YR -42.5y,spot_rate,42.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 42.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 42.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB42Y6M -43y,spot_rate,43-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 43-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 43 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB43YR -43.5y,spot_rate,43.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 43.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 43.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB43Y6M -44y,spot_rate,44-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 44-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 44 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB44YR -44.5y,spot_rate,44.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 44.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 44.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB44Y6M -45y,spot_rate,45-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 45-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 45 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB45YR -45.5y,spot_rate,45.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 45.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 45.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB45Y6M -46y,spot_rate,46-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 46-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 46 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB46YR -46.5y,spot_rate,46.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 46.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 46.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB46Y6M -47y,spot_rate,47-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 47-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 47 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB47YR -47.5y,spot_rate,47.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 47.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 47.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB47Y6M -48y,spot_rate,48-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 48-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 48 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB48YR -48.5y,spot_rate,48.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 48.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 48.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB48Y6M -49y,spot_rate,49-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 49-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 49 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB49YR -49.5y,spot_rate,49.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 49.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 49.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB49Y6M -50y,spot_rate,50-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 50-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 50 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB50YR -50.5y,spot_rate,50.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 50.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 50.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB50Y6M -51y,spot_rate,51-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 51-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 51 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB51YR -51.5y,spot_rate,51.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 51.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 51.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB51Y6M -52y,spot_rate,52-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 52-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 52 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB52YR -52.5y,spot_rate,52.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 52.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 52.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB52Y6M -53y,spot_rate,53-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 53-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 53 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB53YR -53.5y,spot_rate,53.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 53.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 53.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB53Y6M -54y,spot_rate,54-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 54-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 54 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB54YR -54.5y,spot_rate,54.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 54.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 54.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB54Y6M -55y,spot_rate,55-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 55-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 55 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB55YR -55.5y,spot_rate,55.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 55.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 55.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB55Y6M -56y,spot_rate,56-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 56-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 56 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB56YR -56.5y,spot_rate,56.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 56.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 56.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB56Y6M -57y,spot_rate,57-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 57-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 57 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB57YR -57.5y,spot_rate,57.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 57.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 57.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB57Y6M -58y,spot_rate,58-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 58-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 58 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB58YR -58.5y,spot_rate,58.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 58.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 58.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB58Y6M -59y,spot_rate,59-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 59-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 59 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB59YR -59.5y,spot_rate,59.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 59.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 59.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB59Y6M -60y,spot_rate,60-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 60-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 60 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB60YR -60.5y,spot_rate,60.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 60.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 60.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB60Y6M -61y,spot_rate,61-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 61-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 61 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB61YR -61.5y,spot_rate,61.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 61.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 61.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB61Y6M -62y,spot_rate,62-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 62-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 62 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB62YR -62.5y,spot_rate,62.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 62.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 62.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB62Y6M -63y,spot_rate,63-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 63-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 63 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB63YR -63.5y,spot_rate,63.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 63.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 63.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB63Y6M -64y,spot_rate,64-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 64-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 64 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB64YR -64.5y,spot_rate,64.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 64.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 64.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB64Y6M -65y,spot_rate,65-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 65-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 65 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB65YR -65.5y,spot_rate,65.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 65.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 65.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB65Y6M -66y,spot_rate,66-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 66-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 66 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB66YR -66.5y,spot_rate,66.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 66.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 66.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB66Y6M -67y,spot_rate,67-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 67-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 67 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB67YR -67.5y,spot_rate,67.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 67.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 67.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB67Y6M -68y,spot_rate,68-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 68-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 68 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB68YR -68.5y,spot_rate,68.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 68.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 68.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB68Y6M -69y,spot_rate,69-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 69-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 69 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB69YR -69.5y,spot_rate,69.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 69.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 69.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB69Y6M -70y,spot_rate,70-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 70-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 70 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB70YR -70.5y,spot_rate,70.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 70.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 70.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB70Y6M -71y,spot_rate,71-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 71-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 71 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB71YR -71.5y,spot_rate,71.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 71.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 71.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB71Y6M -72y,spot_rate,72-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 72-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 72 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB72YR -72.5y,spot_rate,72.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 72.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 72.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB72Y6M -73y,spot_rate,73-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 73-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 73 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB73YR -73.5y,spot_rate,73.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 73.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 73.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB73Y6M -74y,spot_rate,74-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 74-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 74 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB74YR -74.5y,spot_rate,74.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 74.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 74.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB74Y6M -75y,spot_rate,75-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 75-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 75 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB75YR -75.5y,spot_rate,75.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 75.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 75.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB75Y6M -76y,spot_rate,76-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 76-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 76 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB76YR -76.5y,spot_rate,76.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 76.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 76.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB76Y6M -77y,spot_rate,77-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 77-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 77 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB77YR -77.5y,spot_rate,77.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 77.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 77.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB77Y6M -78y,spot_rate,78-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 78-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 78 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB78YR -78.5y,spot_rate,78.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 78.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 78.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB78Y6M -79y,spot_rate,79-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 79-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 79 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB79YR -79.5y,spot_rate,79.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 79.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 79.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB79Y6M -80y,spot_rate,80-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 80-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 80 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB80YR -80.5y,spot_rate,80.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 80.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 80.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB80Y6M -81y,spot_rate,81-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 81-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 81 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB81YR -81.5y,spot_rate,81.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 81.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 81.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB81Y6M -82y,spot_rate,82-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 82-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 82 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB82YR -82.5y,spot_rate,82.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 82.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 82.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB82Y6M -83y,spot_rate,83-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 83-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 83 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB83YR -83.5y,spot_rate,83.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 83.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 83.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB83Y6M -84y,spot_rate,84-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 84-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 84 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB84YR -84.5y,spot_rate,84.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 84.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 84.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB84Y6M -85y,spot_rate,85-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 85-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 85 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB85YR -85.5y,spot_rate,85.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 85.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 85.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB85Y6M -86y,spot_rate,86-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 86-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 86 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB86YR -86.5y,spot_rate,86.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 86.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 86.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB86Y6M -87y,spot_rate,87-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 87-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 87 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB87YR -87.5y,spot_rate,87.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 87.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 87.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB87Y6M -88y,spot_rate,88-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 88-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 88 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB88YR -88.5y,spot_rate,88.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 88.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 88.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB88Y6M -89y,spot_rate,89-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 89-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 89 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB89YR -89.5y,spot_rate,89.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 89.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 89.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB89Y6M -90y,spot_rate,90-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 90-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 90 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB90YR -90.5y,spot_rate,90.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 90.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 90.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB90Y6M -91y,spot_rate,91-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 91-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 91 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB91YR -91.5y,spot_rate,91.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 91.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 91.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB91Y6M -92y,spot_rate,92-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 92-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 92 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB92YR -92.5y,spot_rate,92.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 92.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 92.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB92Y6M -93y,spot_rate,93-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 93-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 93 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB93YR -93.5y,spot_rate,93.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 93.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 93.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB93Y6M -94y,spot_rate,94-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 94-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 94 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB94YR -94.5y,spot_rate,94.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 94.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 94.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB94Y6M -95y,spot_rate,95-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 95-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 95 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB95YR -95.5y,spot_rate,95.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 95.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 95.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB95Y6M -96y,spot_rate,96-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 96-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 96 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB96YR -96.5y,spot_rate,96.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 96.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 96.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB96Y6M -97y,spot_rate,97-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 97-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 97 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB97YR -97.5y,spot_rate,97.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 97.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 97.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB97Y6M -98y,spot_rate,98-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 98-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 98 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB98YR -98.5y,spot_rate,98.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 98.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 98.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB98Y6M -99y,spot_rate,99-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 99-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 99 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB99YR -99.5y,spot_rate,99.5-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 99.5-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 99.5 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB99Y6M -100y,spot_rate,100-Year High Quality Market (HQM) Corporate Bond Spot Rate,"The spot rate for 100-Year High Quality Market (HQM) Corporate Bond Spot Rate is defined as the yield on a bond that gives a single payment after 100 year. This is called a zero coupon bond. Because high quality zero coupon bonds are not generally available, the HQM methodology computes the spot rates so as to make them consistent with the yields on other high quality bonds. The HQM yield curve uses data from a set of high quality corporate bonds rated AAA, AA, or A that accurately represent the high quality corporate bond market.",HQMCB100YR diff --git a/openbb_terminal/fixedincome/ecb_model.py b/openbb_terminal/fixedincome/ecb_model.py deleted file mode 100644 index 0a577ae6517..00000000000 --- a/openbb_terminal/fixedincome/ecb_model.py +++ /dev/null @@ -1,235 +0,0 @@ -""" ECB model """ - -__docformat__ = "numpy" - -import logging -import sys -import time -from datetime import datetime, timedelta -from functools import partial -from multiprocessing import Pool, cpu_count -from typing import Tuple -from urllib.error import HTTPError - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console, optional_rich_track - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-return-statements - - -@log_start_end(log=logger) -def get_series_data( - series_id: str = "EST.B.EU000A2X2A25.WT", start_date: str = "", end_date: str = "" -): - """Get ECB data - - Parameters - ---------- - series_id: str - ECB ID of data - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - start_date = start_date.replace("-", "") - end_date = end_date.replace("-", "") - url = ( - "https://sdw.ecb.europa.eu/quickviewexport.do?trans=N" - f"&start={start_date}&end={end_date}&SERIES_KEY={series_id}&type=csv" - ) - time.sleep(0.5) - - def _get_data(max_retries: int = 5): - try: - df = pd.read_csv( - url, header=5, usecols=[0, 1], index_col=0, parse_dates=True - ) - df = df.iloc[::-1] - return df - except KeyboardInterrupt as interrupt: - raise interrupt - except (HTTPError, Exception): - max_retries -= 1 - if max_retries == 0: - return pd.DataFrame() - time.sleep(0.5) - return _get_data(max_retries=max_retries) - - return _get_data() - - -@log_start_end(log=logger) -def get_ecb_yield_curve( - date: str = "", - yield_type: str = "spot_rate", - return_date: bool = False, - detailed: bool = False, - any_rating: bool = True, -) -> Tuple[pd.DataFrame, str]: - """Gets euro area yield curve data from ECB. - - The graphic depiction of the relationship between the yield on bonds of the same credit quality but different - maturities is known as the yield curve. In the past, most market participants have constructed yield curves from - the observations of prices and yields in the Treasury market. Two reasons account for this tendency. First, - Treasury securities are viewed as free of default risk, and differences in creditworthiness do not affect yield - estimates. Second, as the most active bond market, the Treasury market offers the fewest problems of illiquidity - or infrequent trading. The key function of the Treasury yield curve is to serve as a benchmark for pricing bonds - and setting yields in other sectors of the debt market. - - It is clear that the market’s expectations of future rate changes are one important determinant of the - yield-curve shape. For example, a steeply upward-sloping curve may indicate market expectations of near-term Fed - tightening or of rising inflation. However, it may be too restrictive to assume that the yield differences across - bonds with different maturities only reflect the market’s rate expectations. The well-known pure expectations - hypothesis has such an extreme implication. The pure expectations hypothesis asserts that all government bonds - have the same near-term expected return (as the nominally riskless short-term bond) because the return-seeking - activity of risk-neutral traders removes all expected return differentials across bonds. - - Parameters - ---------- - date: str - Date to get curve for. If empty, gets most recent date (format yyyy-mm-dd) - yield_type: str - What type of yield curve to get, options: ['spot_rate', 'instantaneous_forward', 'par_yield'] - return_date: bool - If True, returns date of yield curve - detailed: bool - If True, returns detailed data. Note that this is very slow. - aaa_only: bool - If True, it only returns rates for AAA rated bonds. If False, it returns rates for all bonds - - Returns - ------- - Tuple[pd.DataFrame, str] - Dataframe of yields and maturities, - Date for which the yield curve is obtained - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> ycrv_df = openbb.fixedincome.ycrv() - - Since there is a delay with the data, the most recent date is returned and can be accessed with return_date=True - >>> ycrv_df, ycrv_date = openbb.fixedincome.ycrv(return_date=True) - """ - base_return = pd.DataFrame(), date if return_date else pd.DataFrame() - if yield_type == "spot_rate": - yield_type = f"YC.B.U2.EUR.4F.G_N_{'A' if any_rating else 'C'}.SV_C_YM.SR_" - elif yield_type == "instantaneous_forward": - yield_type = f"YC.B.U2.EUR.4F.G_N_{'A' if any_rating else 'C'}.SV_C_YM.IF_" - elif yield_type == "par_yield": - yield_type = f"YC.B.U2.EUR.4F.G_N_{'A' if any_rating else 'C'}.SV_C_YM.PY" - else: - console.print("[red]Incorrect input for parameter yield_type[/red]") - return base_return - - if detailed: - console.print( - "Note that due to the large amount of data, this may take a while. Use CTRL + C to cancel." - ) - series_id = [ - f"{yield_type}{y}Y{m}M" if y != 0 else f"{yield_type}{m}M" - for y in range(0, 30) - for m in range(1, 12) - if (y != 0 or m >= 3) - ] - series_id.append(f"{yield_type}30Y") - labels = [ - f"{y}Year{m}Month" if y != 0 else f"{m}Month" - for y in range(0, 30) - for m in range(1, 12) - if (y != 0 or m >= 3) - ] - labels.append("30Year") - years = [ - y + m / 12 for y in range(0, 30) for m in range(1, 12) if (y != 0 or m >= 3) - ] - years.append(30) - else: - series_id = [f"{yield_type}{m}M" for m in [3, 6]] - series_id += [f"{yield_type}{y}Y" for y in [1, 2, 3, 5, 7, 10, 20, 30]] - labels = [f"{m}Month" for m in [3, 6]] - labels += [f"{y}Year" for y in [1, 2, 3, 5, 7, 10, 20, 30]] - years = [0.25, 0.5, 1, 2, 3, 5, 7, 10, 20, 30] - - df = pd.DataFrame() - # Check that the date is in the past - today = datetime.now().strftime("%Y-%m-%d") - if date and date >= today: - console.print("[red]Date cannot be today or in the future[/red]") - return base_return - - # Add in logic that will get the most recent date. - - if not date: - date = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") - - try: - if detailed and not hasattr(sys, "frozen"): - with Pool(cpu_count() - 1) as pool: - # To be able to use optional_rich_track with multiprocessing, - # we need to use imap_unordered - - results = optional_rich_track( - pool.imap_unordered( - partial(get_series_data, start_date=date, end_date=date), - series_id, - ), - total=len(series_id), - desc="Obtaining yield curve data", - ) - for i, result in enumerate(results): - if isinstance(result, pd.DataFrame) and result.empty: - console.print(f"\n[red]No data for {series_id[i]}[/red]") - # we remove the corresponding index from the list of years - years.pop(i) - continue - - result.columns = [labels[i]] - df = pd.concat([df, result], axis=1) - - else: - for i in optional_rich_track( - range(len(series_id)), desc="Obtaining yield curve data" - ): - temp = get_series_data(series_id[i], date, date) - temp.columns = [labels[i]] - df = pd.concat([df, temp], axis=1) - - except (Exception, KeyboardInterrupt): - console.print("\n[yellow]Data collection was canceled.[/yellow]") - return pd.DataFrame(), date - - if df.empty: - return base_return - - # Drop rows with NaN -- corresponding to weekends typically - df = df.dropna() - if datetime.strptime(date, "%Y-%m-%d") not in df.index: - nearest_value = df.index.get_indexer( - [datetime.strptime(date, "%Y-%m-%d")], method="nearest" - ) - date_of_yield = df.iloc[nearest_value].index[0].date() - series = df[df.index == df.iloc[nearest_value].index[0]] - if series.empty: - return pd.DataFrame(), date_of_yield - rates = pd.DataFrame(series.values.T, columns=["Rate"]) - console.print( - f"{date} not available, therefore selecting the nearest date, {date_of_yield}." - ) - else: - date_of_yield = date - series = df[df.index == datetime.strptime(date, "%Y-%m-%d")] - if series.empty: - return pd.DataFrame(), date_of_yield - rates = pd.DataFrame(series.values.T, columns=["Rate"]) - - rates.insert(0, "Maturity", years) - - if return_date: - return rates, date_of_yield - return rates diff --git a/openbb_terminal/fixedincome/ecb_view.py b/openbb_terminal/fixedincome/ecb_view.py deleted file mode 100644 index d4ef8051ff6..00000000000 --- a/openbb_terminal/fixedincome/ecb_view.py +++ /dev/null @@ -1,180 +0,0 @@ -""" ECB view """ - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import logging -import os -from typing import Optional - -# IMPORTATION THIRDPARTY -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.fixedincome import ecb_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-function-args - -ID_TO_NAME = { - "EST.B.EU000A2X2A25.WT": "Euro Short-Term Rate: Volume-Weighted Trimmed Mean Rate [Percent]", - "EST.B.EU000A2X2A25.TT": "Euro Short-Term Rate: Total Volume [Millions of EUR]", - "EST.B.EU000A2X2A25.NT": "Euro Short-Term Rate: Number of Transactions", - "EST.B.EU000A2X2A25.R75": "Euro Short-Term Rate: Rate at 75th Percentile of Volume [Percent]", - "EST.B.EU000A2X2A25.NB": "Euro Short-Term Rate: Number of Active Banks", - "EST.B.EU000A2X2A25.VL": "Euro Short-Term Rate: Share of Volume of the 5 Largest Active Banks [Percent]", - "EST.B.EU000A2X2A25.R25": "Euro Short-Term Rate: Rate at 25th Percentile of Volume [Percent]", -} - -ESTR_PARAMETER_TO_ECB_ID = { - "volume_weighted_trimmed_mean_rate": "EST.B.EU000A2X2A25.WT", - "number_of_transactions": "EST.B.EU000A2X2A25.NT", - "number_of_active_banks": "EST.B.EU000A2X2A25.NB", - "total_volume": "EST.B.EU000A2X2A25.TT", - "share_of_volume_of_the_5_largest_active_banks": "EST.B.EU000A2X2A25.VL", - "rate_at_75th_percentile_of_volume": "EST.B.EU000A2X2A25.R75", - "rate_at_25th_percentile_of_volume": "EST.B.EU000A2X2A25.R25", -} - - -@log_start_end(log=logger) -def plot_estr( - parameter: str = "EST.B.EU000A2X2A25.WT", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Euro Short-Term Rate (ESTR) - - Parameters - ---------- - series_id: str - ECB ID of ESTR data to plot, options: ['EST.B.EU000A2X2A25.WT', 'EST.B.EU000A2X2A25.TT', - 'EST.B.EU000A2X2A25.NT', 'EST.B.EU000A2X2A25.R75', 'EST.B.EU000A2X2A25.NB', - 'EST.B.EU000A2X2A25.VL', 'EST.B.EU000A2X2A25.R25'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = ESTR_PARAMETER_TO_ECB_ID.get(parameter, "") - - df = ecb_model.get_series_data( - series_id, start_date if start_date else "", end_date if end_date else "" - ) - - fig = OpenBBFigure() - fig.set_title(ID_TO_NAME[series_id]) - - fig.add_scatter(x=df.index, y=df.values, name=series_id, mode="lines") - - if export: - if "[Percent]" in ID_TO_NAME[series_id]: - # Check whether it is a percentage, relevant for exporting - df_transformed = pd.DataFrame(df, columns=[series_id]) / 100 - else: - df_transformed = pd.DataFrame(df, columns=[series_id]) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - df_transformed, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_ecb_yield_curve( - date: str = "", - yield_type: str = "spot_rate", - detailed: bool = False, - any_rating: bool = True, - external_axes: bool = False, - raw: bool = False, - export: str = "", - sheet_name: str = "", -): - """Display yield curve based on ECB Treasury rates for a specified date. - - The graphic depiction of the relationship between the yield on bonds of the same credit quality but different - maturities is known as the yield curve. In the past, most market participants have constructed yield curves from - the observations of prices and yields in the Treasury market. Two reasons account for this tendency. First, - Treasury securities are viewed as free of default risk, and differences in creditworthiness do not affect yield - estimates. Second, as the most active bond market, the Treasury market offers the fewest problems of illiquidity - or infrequent trading. The key function of the Treasury yield curve is to serve as a benchmark for pricing bonds - and setting yields in other sectors of the debt market. - - It is clear that the market’s expectations of future rate changes are one important determinant of the - yield-curve shape. For example, a steeply upward-sloping curve may indicate market expectations of near-term Fed - tightening or of rising inflation. However, it may be too restrictive to assume that the yield differences across - bonds with different maturities only reflect the market’s rate expectations. The well-known pure expectations - hypothesis has such an extreme implication. The pure expectations hypothesis asserts that all government bonds - have the same near-term expected return (as the nominally riskless short-term bond) because the return-seeking - activity of risk-neutral traders removes all expected return differentials across bonds. - - Parameters - ---------- - date: str - Date to get curve for. If None, gets most recent date (format yyyy-mm-dd) - yield_type: str - What type of yield curve to get, options: ['spot_rate', 'instantaneous_forward', 'par_yield'] - detailed: bool - If True, returns detailed data. Note that this is very slow. - aaa_only: bool - If True, it only returns rates for AAA rated bonds. If False, it returns rates for all bonds - external_axes : bool, optional - Whether to return the figure object or not, by default False - raw : bool - Output only raw data - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - """ - rates, date_of_yield = ecb_model.get_ecb_yield_curve( - date, yield_type, True, detailed, any_rating - ) - if rates.empty: - return console.print(f"[red]Yield data not found for {date_of_yield}.[/red]\n") - - fig = OpenBBFigure(xaxis_title="Maturity", yaxis_title="Rate (%)") - fig.set_title( - f"Euro Area{' AAA Bonds' if not any_rating else ' All Bonds'} {yield_type.replace('_', ' ').title()} " - f"Yield Curve for {date_of_yield}", - ) - - fig.add_scatter(x=rates["Maturity"], y=rates["Rate"], name="Yield") - - if raw: - print_rich_table( - rates, - headers=list(rates.columns), - show_index=False, - title=f"Euro Area{' AAA Bonds' if not any_rating else ' All Bonds'} {yield_type.replace('_', ' ').title()} " - f"Yield Curve for {date_of_yield}", - floatfmt=".3f", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ecbycrv", - rates / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/fixedincome/fixedincome_controller.py b/openbb_terminal/fixedincome/fixedincome_controller.py deleted file mode 100644 index b038e5ef9d9..00000000000 --- a/openbb_terminal/fixedincome/fixedincome_controller.py +++ /dev/null @@ -1,1546 +0,0 @@ -""" Futures Controller """ - -__docformat__ = "numpy" - -# pylint:disable=C0302 - -# IMPORTATION STANDARD -import argparse -import logging -from typing import List, Optional - -# IMPORTATION THIRDPARTY -import pandas as pd - -# IMPORTATION INTERNAL -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.fixedincome import ( - ecb_view, - fred_model, - fred_view, - oecd_model, - oecd_view, -) -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - list_from_str, - print_rich_table, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class FixedIncomeController(BaseController): - """Fixed Income Controller class""" - - CHOICES_COMMANDS = [ - "estr", - "sofr", - "sonia", - "ameribor", - "fed", - "iorb", - "projection", - "dwpcr", - "ecb", - "treasury", - "tmc", - "ffrmc", - "ycrv", - "ecbycrv", - "usrates", - "icebofa", - "moody", - "cp", - "spot", - "hqm", - "icespread", - "tbffr", - ] - - PATH = "/fixedincome/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - self.choices: dict = self.choices_default - - # These values need to work with multi options which - # is why they are defined here - self.choices["treasury"]["--short"] = { - c: None for c in oecd_model.COUNTRY_TO_CODE_SHORT - } - self.choices["treasury"]["--long"] = { - c: None for c in oecd_model.COUNTRY_TO_CODE_LONG - } - - self.choices["spot"]["--maturity"] = { - c: None for c in fred_model.SPOT_TO_OPTIONS["Maturity"] - } - self.choices["spot"]["-m"] = "--maturity" - self.choices["spot"]["--category"] = { - c: None for c in fred_model.SPOT_TO_OPTIONS["Category"] - } - self.choices["spot"]["-c"] = "--category" - self.completer = NestedCompleter.from_nested_dict(self.choices) # type: ignore - - def print_help(self): - """Print help""" - mt = MenuText("fixedincome/", column_sources=120) - mt.add_info("_reference_rates_") - mt.add_cmd("estr") - mt.add_cmd("sofr") - mt.add_cmd("sonia") - mt.add_cmd("ameribor") - mt.add_raw("\n") - mt.add_info("_central_bank_rates_") - mt.add_cmd("fed") - mt.add_cmd("projection") - mt.add_cmd("iorb") - mt.add_cmd("dwpcr") - mt.add_cmd("ecb") - mt.add_raw("\n") - mt.add_info("_government_bonds_") - mt.add_cmd("treasury") - mt.add_cmd("usrates") - mt.add_cmd("ycrv") - mt.add_cmd("ecbycrv") - mt.add_raw("\n") - mt.add_info("_corporate_bonds_") - mt.add_cmd("icebofa") - mt.add_cmd("moody") - mt.add_cmd("cp") - mt.add_cmd("spot") - mt.add_cmd("hqm") - mt.add_raw("\n") - mt.add_info("_spreads_") - mt.add_cmd("icespread") - mt.add_cmd("tmc") - mt.add_cmd("ffrmc") - mt.add_cmd("tbffr") - - console.print(text=mt.menu_text, menu="Fixed Income") - - @log_start_end(log=logger) - def call_estr(self, other_args: List[str]): - """Process estr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="estr", - description="The euro short-term rate (€STR) reflects the wholesale euro " - "unsecured overnight borrowing costs of banks located in the euro area. The " - "€STR is published on each TARGET2 business day based on transactions conducted " - "and settled on the previous TARGET2 business day (the reporting date “T”) " - "with a maturity date of T+1 which are deemed to have been executed at arm’s " - "length and thus reflect market rates in an unbiased way.", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Specific Euro Short-Term Rate data to retrieve", - default="volume_weighted_trimmed_mean_rate", - choices=ecb_view.ESTR_PARAMETER_TO_ECB_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if ns_parser.source == "FRED": - fred_view.plot_estr( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "ECB": - ecb_view.plot_estr( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_sofr(self, other_args: List[str]): - """Process sofr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sofr", - description="The Secured Overnight Financing Rate (SOFR) is a " - "broad measure of the cost of borrowing cash overnight collateralized " - "by Treasury securities. The SOFR is calculated as a volume-weighted " - "median of transaction-level tri-party repo data. ", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Specific SOFR data to retrieve", - default="overnight", - choices=fred_model.SOFR_PARAMETER_TO_FRED_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default="1980-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - fred_view.plot_sofr( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_sonia(self, other_args: List[str]): - """Process sonia command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sonia", - description="SONIA (Sterling Overnight Index Average) is an important " - "interest rate benchmark. SONIA is based on actual transactions and " - "reflects the average of the interest rates that banks pay to borrow " - "sterling overnight from other financial institutions and other institutional investors.", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Specific SONIA data to retrieve", - default="rate", - choices=fred_model.SONIA_PARAMETER_TO_FRED_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - fred_view.plot_sonia( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ameribor(self, other_args: List[str]): - """Process ameribor command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ameribor", - description="Ameribor (short for the American interbank offered rate) is " - "a benchmark interest rate that reflects the true cost of short-term interbank borrowing. " - "This rate is based on transactions in overnight unsecured loans conducted on the American " - "Financial Exchange (AFX).", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Specific AMERIBOR data to retrieve", - default="overnight", - choices=fred_model.AMERIBOR_PARAMETER_TO_FRED_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - fred_view.plot_ameribor( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fed(self, other_args: List[str]): - """Process fed command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fed", - description="Get Effective Federal Funds Rate data.\nA bank rate is the interest rate a nation's central " - "bank charges to its domestic banks to borrow money. The rates central banks charge are set " - "to stabilize the economy. In the United States, the Federal Reserve System's Board of " - "Governors set the bank rate, also known as the discount rate.", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Specific Effective Federal Funds Rate data to retrieve", - default="monthly", - choices=fred_model.FED_PARAMETER_TO_FRED_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default="1900-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - - parser.add_argument( - "-o", - "--overnight", - dest="overnight", - action="store_true", - help="Gets the Overnight Bank Funding Rate", - default=False, - ) - parser.add_argument( - "-q", - "--quantiles", - dest="quantiles", - action="store_true", - help="Whether to show 1, 25, 75 and 99 percentiles", - default=False, - ) - parser.add_argument( - "-t", - "--target", - dest="target", - action="store_true", - help="Whether to show the target range data", - default=False, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - if ns_parser.overnight and (ns_parser.target or ns_parser.quantiles): - console.print( - "The Overnight Bank Funding Rate has no target and quantiles data." - ) - else: - fred_view.plot_fed( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - overnight=ns_parser.overnight, - quantiles=ns_parser.quantiles, - target=ns_parser.target, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_iorb(self, other_args: List[str]): - """Process iorb command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="iorb", - description="Get Interest Rate on Reserve Balances data\nA bank rate is the interest rate a nation's " - "central bank charges to its domestic banks to borrow money. The rates central banks charge " - "are set to stabilize the economy. In the United States, the Federal Reserve System's Board " - "of Governors set the bank rate, also known as the discount rate.", - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - fred_view.plot_iorb( - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_projection(self, other_args: List[str]): - """Process projection command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="projection", - description="Get the Federal Reserve's projection of the federal funds rate.", - ) - parser.add_argument( - "-l", - "--long-run", - dest="long_run", - action="store_true", - help="Whether to show the long run projection", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - fred_view.plot_projection( - long_run=ns_parser.long_run, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dwpcr(self, other_args: List[str]): - """Process dwpcr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dwpcr", - description="Get Discount Window Primary Credit Rate data.\nA bank rate is the interest rate a nation's " - "central bank charges to its domestic banks to borrow money. The rates central banks charge " - "are set to stabilize the economy. In the United States, the Federal Reserve System's Board " - "of Governors set the bank rate, also known as the discount rate.", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Specific Discount Window Primary Credit Rate data to retrieve", - default="daily_excl_weekend", - choices=fred_model.DWPCR_PARAMETER_TO_FRED_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - fred_view.plot_dwpcr( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ecb(self, other_args: List[str]): - """Process ecb command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ecb", - description="Plot the three interest rates the ECB sets " - "every six weeks as part of its monetary policy, these are the " - "interest rate on the main refinancing operations (MRO), the rate on " - "the deposit facility and the rate on the marginal lending facility.", - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-t", - "--type", - dest="type", - type=str, - help="Whether to choose the deposit, marginal lending or main refinancing rate", - choices=["deposit", "lending", "refinancing"], - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - fred_view.plot_ecb( - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - interest_type=ns_parser.type, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_treasury(self, other_args: List[str]): - """Process treasury command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="treasury", - description="Plot short (3 month) and long (10 year) term interest rates from selected countries " - "including the possibility to plot forecasts for the next years.", - ) - - parser.add_argument( - "--short", - type=str, - dest="short", - help="Countries to get short term (3 month) interest rates for.", - default=None, - ) - - parser.add_argument( - "--long", - type=str, - dest="long", - help="Countries to get long term (10 year) interest rates for.", - default=None, - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format", - dest="start_date", - default=None, - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - help="End date of data, in YYYY-MM-DD format", - dest="end_date", - default=None, - ) - parser.add_argument( - "--forecast", - action="store_true", - dest="forecast", - default=False, - help="If True, plot forecasts for each interest rate", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - if ns_parser.short is None and ns_parser.long is None: - console.print( - "[red]Please provide at least one country to plot " - "with --short (3 months) and/or --long (10 years).[/red]" - ) - else: - short_term_countries = ( - list_from_str(ns_parser.short.lower()) if ns_parser.short else None - ) - long_term_countries = ( - list_from_str(ns_parser.long.lower()) if ns_parser.long else None - ) - oecd_view.plot_treasuries( - short_term=short_term_countries, - long_term=long_term_countries, - forecast=ns_parser.forecast, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_tmc(self, other_args: List[str]): - """Process tmc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tmc", - description="Get 10-Year Treasury Constant Maturity Minus Selected Treasury Constant Maturity data. " - "Constant maturity is the theoretical value of a U.S. Treasury that is based on recent values " - "of auctioned U.S. Treasuries. The value is obtained by the U.S. Treasury on a daily basis " - "through interpolation of the Treasury yield curve which, in turn, is based on closing " - "bid-yields of actively-traded Treasury securities.", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Selected treasury constant maturity to subtract", - default="3_month", - choices=fred_model.TMC_PARAMETER_TO_FRED_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - fred_view.plot_tmc( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ffrmc(self, other_args: List[str]): - """Process ffrmc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ffrmc", - description="Get Selected Treasury Constant Maturity Minus Federal Funds Rate. " - "Constant maturity is the theoretical value of a U.S. Treasury that is based on recent values " - "of auctioned U.S. Treasuries. The value is obtained by the U.S. Treasury on a daily basis " - "through interpolation of the Treasury yield curve which, in turn, is based on closing " - "bid-yields of actively-traded Treasury securities.", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Selected Treasury Constant Maturity", - default="10_year", - choices=fred_model.FFRMC_PARAMETER_TO_FRED_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - fred_view.plot_ffrmc( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ycrv(self, other_args: List[str]): - """Process ycrv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ycrv", - description="Generate US yield curve. \nThe yield curve shows the bond rates" - "at different maturities.\nThe graphic depiction of the relationship between the yield on bonds of the " - "same credit quality but different maturities is known as the yield curve. In the past, " - "most market participants have constructed yield curves from the observations of prices and " - "yields in the Treasury market. Two reasons account for this tendency. First, " - "Treasury securities are viewed as free of default risk, and differences in creditworthiness " - "do not affect yield estimates. Second, as the most active bond market, the Treasury market " - "offers the fewest problems of illiquidity or infrequent trading. The key function of the " - "Treasury yield curve is to serve as a benchmark for pricing bonds and setting yields in " - "other sectors of the debt market.\nIt is clear that the market’s expectations of future rate " - "changes are one important determinant of the yield-curve shape. For example, " - "a steeply upward-sloping curve may indicate market expectations of near-term Fed tightening " - "or of rising inflation. However, it may be too restrictive to assume that the yield " - "differences across bonds with different maturities only reflect the market’s rate " - "expectations. The well-known pure expectations hypothesis has such an extreme implication. " - "The pure expectations hypothesis asserts that all government bonds have the same near-term " - "expected return (as the nominally riskless short-term bond) because the return-seeking " - "activity of risk-neutral traders removes all expected return differentials across bonds.", - ) - parser.add_argument( - "-d", - "--date", - type=valid_date, - help="Date to get data from FRED. If not supplied, the most recent entry will be used.", - dest="date", - default=None, - ) - parser.add_argument( - "-i", - "--inflation-adjusted", - action="store_true", - help="Whether to plot the inflation adjusted yield curve.", - dest="inflation_adjusted", - default=False, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - fred_view.display_yield_curve( - date=ns_parser.date.strftime("%Y-%m-%d") if ns_parser.date else "", - inflation_adjusted=ns_parser.inflation_adjusted, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ecbycrv(self, other_args: List[str]): - """Process ecbycrv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ecbycrv", - description="Generate euro area yield curve from ECB. \nThe yield curve shows the bond rates" - "at different maturities.\nThe graphic depiction of the relationship between the yield on bonds of the " - "same credit quality but different maturities is known as the yield curve. In the past, " - "most market participants have constructed yield curves from the observations of prices and " - "yields in the Treasury market. Two reasons account for this tendency. First, " - "Treasury securities are viewed as free of default risk, and differences in creditworthiness " - "do not affect yield estimates. Second, as the most active bond market, the Treasury market " - "offers the fewest problems of illiquidity or infrequent trading. The key function of the " - "Treasury yield curve is to serve as a benchmark for pricing bonds and setting yields in " - "other sectors of the debt market.\nIt is clear that the market’s expectations of future rate " - "changes are one important determinant of the yield-curve shape. For example, " - "a steeply upward-sloping curve may indicate market expectations of near-term Fed tightening " - "or of rising inflation. However, it may be too restrictive to assume that the yield " - "differences across bonds with different maturities only reflect the market’s rate " - "expectations. The well-known pure expectations hypothesis has such an extreme implication. " - "The pure expectations hypothesis asserts that all government bonds have the same near-term " - "expected return (as the nominally riskless short-term bond) because the return-seeking " - "activity of risk-neutral traders removes all expected return differentials across bonds.", - ) - parser.add_argument( - "-d", - "--date", - type=valid_date, - help="Date to get data from ECB. If not supplied, the most recent entry will be used.", - dest="date", - default=None, - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Selected type of yield curve", - default="spot_rate", - choices=["spot_rate", "instantaneous_forward", "par_yield"], - ) - parser.add_argument( - "--detailed", - action="store_true", - dest="detailed", - default=False, - help="If True, returns detailed data. Note that this is very slow.", - ) - parser.add_argument( - "--any-rating", - action="store_true", - dest="any_rating", - default=False, - help="If False, it only returns rates for AAA rated bonds. If True, it returns rates for all bonds.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - if ns_parser: - ecb_view.display_ecb_yield_curve( - date=ns_parser.date.strftime("%Y-%m-%d") if ns_parser.date else "", - yield_type=ns_parser.parameter, - detailed=ns_parser.detailed, - any_rating=ns_parser.any_rating, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_usrates(self, other_args: List[str]): - """Process usrates command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="usrates", - description="Plot various rates from the United States. This includes tbill (Treasury Bills), " - "Constant Maturity treasuries (cmn) and Inflation Protected Treasuries (TIPS)", - ) - parser.add_argument( - "-m", - "--maturity", - dest="maturity", - type=str, - help="Specific Treasury Bill Secondary Market Rate data to plot", - default="3_month", - choices=list(fred_model.USARATES_TO_FRED_ID.keys()), - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Choose either tbill (Treasury Bills), Constant Maturity treasuries (cmn) " - "or Inflation Protected Treasuries (TIPS)", - default="tbill", - choices=["tbill", "cmn", "tips"], - ) - parser.add_argument( - "-o", - "--options", - dest="options", - action="store_true", - help="See the available options", - default=False, - ) - - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default="1980-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - if ns_parser.options: - print_rich_table( - pd.DataFrame.from_dict(fred_model.USARATES_TO_FRED_ID).T.fillna( - "-" - ), - show_index=True, - title="Available options including FRED Series name", - ) - elif ( - ns_parser.parameter - not in fred_model.USARATES_TO_FRED_ID[ns_parser.maturity] - ): - console.print( - f"[red]Maturity {ns_parser.maturity.replace('_', ' ')} is not " - f"available for {ns_parser.parameter}. Please use 'usrates --options'.[/red]" - ) - else: - fred_view.plot_usrates( - parameter=ns_parser.parameter, - maturity=ns_parser.maturity, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_icebofa(self, other_args: List[str]): - """Process icebofa command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="icebofa", - description="Plot various rates from the United States. This includes tbill (Treasury Bills), " - "Constant Maturity treasuries (cmn) and Inflation Protected Treasuries (TIPS)", - ) - - parser.add_argument( - "-t", - "--type", - dest="data_type", - type=str, - help="What type you'd like to collect data for", - default="yield", - choices=fred_model.ICE_BOFA_TO_OPTIONS["Type"], - ) - - parser.add_argument( - "-c", - "--category", - dest="category", - type=str, - help="What category you'd like to collect data for", - default="all", - choices=fred_model.ICE_BOFA_TO_OPTIONS["Category"], - ) - - parser.add_argument( - "-a", - "--area", - dest="area", - type=str, - help="What region you'd like to collect data for", - default="us", - choices=fred_model.ICE_BOFA_TO_OPTIONS["Area"], - ) - - parser.add_argument( - "-g", - "--grade", - dest="grade", - type=str, - help="What grade you'd like to collect data for", - default="non_sovereign", - choices=fred_model.ICE_BOFA_TO_OPTIONS["Grade"], - ) - - parser.add_argument( - "-o", - "--options", - dest="options", - action="store_true", - help="See the available options", - default=False, - ) - - parser.add_argument( - "-d", - "--description", - dest="description", - action="store_true", - help="Whether to provide a description of the data.", - default=False, - ) - - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default="1980-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - fred_view.plot_icebofa( - data_type=ns_parser.data_type, - category=ns_parser.category, - area=ns_parser.area, - grade=ns_parser.grade, - options=ns_parser.options, - description=ns_parser.description, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_moody(self, other_args: List[str]): - """Process moody command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="moody", - description="Moody's Aaa and Baa are investment bonds that acts as an index of " - "the performance of all bonds given an Aaa or Baa rating by Moody's Investors Service respectively" - "These corporate bonds often are used in macroeconomics as an alternative to the federal ten-year " - "Treasury Bill as an indicator of the interest rate.", - ) - - parser.add_argument( - "-t", - "--type", - dest="data_type", - type=str, - help="What type you'd like to collect data for", - default="aaa", - choices=fred_view.MOODY_TO_OPTIONS["Type"].keys(), - ) - - parser.add_argument( - "--spread", - dest="spread", - type=str, - help="Whether you want to show the spread", - default=None, - choices=fred_view.MOODY_TO_OPTIONS["Spread"], - ) - - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default="1980-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - fred_view.plot_moody( - data_type=ns_parser.data_type, - spread=ns_parser.spread, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cp(self, other_args: List[str]): - """Process cp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cp", - description="Commercial paper (CP) consists of short-term, promissory notes " - "issued primarily by corporations. Maturities range up to 270 days but average " - "about 30 days. Many companies use CP to raise cash needed for current " - "transactions, and many find it to be a lower-cost alternative to bank loans.", - ) - - parser.add_argument( - "-m", - "--maturity", - dest="maturity", - type=str, - help="What type you'd like to collect data for", - default="30d", - choices=fred_model.CP_TO_OPTIONS["Maturity"], - ) - - parser.add_argument( - "-c", - "--category", - dest="category", - type=str, - help="What category you'd like to collect data for", - default="financial", - choices=fred_model.CP_TO_OPTIONS["Category"], - ) - - parser.add_argument( - "-g", - "--grade", - dest="grade", - type=str, - help="What grade you'd like to collect data for", - default="aa", - choices=fred_model.CP_TO_OPTIONS["Grade"], - ) - - parser.add_argument( - "-o", - "--options", - dest="options", - action="store_true", - help="See the available options", - default=False, - ) - - parser.add_argument( - "-d", - "--description", - dest="description", - action="store_true", - help="Whether to provide a description of the data.", - default=False, - ) - - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default="1980-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - fred_view.plot_cp( - maturity=ns_parser.maturity, - category=ns_parser.category, - grade=ns_parser.grade, - options=ns_parser.options, - description=ns_parser.description, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_spot(self, other_args: List[str]): - """Process spot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="spot", - description="The spot rate for any maturity is the yield on a bond that provides " - "a single payment at that maturity. This is a zero coupon bond. Because each " - "spot rate pertains to a single cashflow, it is the relevant interest rate " - "concept for discounting a pension liability at the same maturity.", - ) - - parser.add_argument( - "-m", - "--maturity", - dest="maturity", - type=str, - help="What maturity you'd like to collect data for", - default="10y", - ) - - parser.add_argument( - "-c", - "--category", - dest="category", - type=str, - help="What category you'd like to collect data for", - default="spot_rate", - ) - - parser.add_argument( - "-d", - "--description", - dest="description", - action="store_true", - help="Whether to provide a description of the data.", - default=False, - ) - - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default="1980-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - maturity = [m.replace(".0", "") for m in list_from_str(ns_parser.maturity)] - category = list_from_str(ns_parser.category) - - fred_view.plot_spot( - maturity=maturity, - category=category, - description=ns_parser.description, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_hqm(self, other_args: List[str]): - """Process hqdm command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hqm", - description="The HQM yield curve represents the high quality corporate bond market, i.e., " - "corporate bonds rated AAA, AA, or A. The HQM curve contains two regression terms. These " - "terms are adjustment factors that blend AAA, AA, and A bonds into a single HQM yield curve " - "that is the market-weighted average (MWA) quality of high quality bonds.", - ) - - parser.add_argument( - "-d", - "--date", - dest="date", - type=valid_date, - help="Define the date of the yield curve.", - default=None, - ) - - parser.add_argument( - "-p", - "--par", - dest="par", - action="store_true", - help="Whether to include the Par Yield.", - default=False, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - fred_view.plot_hqm( - date=ns_parser.date.strftime("%Y-%m-%d") if ns_parser.date else "", - par=ns_parser.par, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_icespread(self, other_args: List[str]): - """Process icespread command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="icespread", - description="Plot various rates from the United States. This includes tbill (Treasury Bills), " - "Constant Maturity treasuries (cmn) and Inflation Protected Treasuries (TIPS)", - ) - - parser.add_argument( - "-c", - "--category", - dest="category", - type=str, - help="What category you'd like to collect data for", - default="all", - choices=fred_model.ICE_BOFA_TO_OPTIONS["Category"], - ) - - parser.add_argument( - "-a", - "--area", - dest="area", - type=str, - help="What region you'd like to collect data for", - default="us", - choices=fred_model.ICE_BOFA_TO_OPTIONS["Area"], - ) - - parser.add_argument( - "-g", - "--grade", - dest="grade", - type=str, - help="What grade you'd like to collect data for", - default="non_sovereign", - choices=fred_model.ICE_BOFA_TO_OPTIONS["Grade"], - ) - - parser.add_argument( - "-o", - "--options", - dest="options", - action="store_true", - help="See the available options", - default=False, - ) - - parser.add_argument( - "-d", - "--description", - dest="description", - action="store_true", - help="Whether to provide a description of the data.", - default=False, - ) - - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default="1980-01-01", - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - fred_view.plot_icebofa( - data_type="spread", - category=ns_parser.category, - area=ns_parser.area, - grade=ns_parser.grade, - options=ns_parser.options, - description=ns_parser.description, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_tbffr(self, other_args: List[str]): - """Process tbffr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tbffr", - description="Get Selected Treasury Bill Minus Federal Funds Rate.", - ) - parser.add_argument( - "-p", - "--parameter", - dest="parameter", - type=str, - help="Selected Treasury Bill", - default="3_month", - choices=fred_model.TBFFR_PARAMETER_TO_FRED_ID.keys(), - ) - parser.add_argument( - "-s", - "--start", - dest="start_date", - type=valid_date, - help="Starting date (YYYY-MM-DD) of data", - default=None, - ) - parser.add_argument( - "-e", - "--end", - dest="end_date", - type=valid_date, - help="Ending date (YYYY-MM-DD) of data", - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - fred_view.plot_tbffr( - parameter=ns_parser.parameter, - start_date=ns_parser.start_date, - end_date=ns_parser.end_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/fixedincome/fred_model.py b/openbb_terminal/fixedincome/fred_model.py deleted file mode 100644 index be50282872d..00000000000 --- a/openbb_terminal/fixedincome/fred_model.py +++ /dev/null @@ -1,1148 +0,0 @@ -""" FRED model """ - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import logging -import os -from datetime import datetime, timedelta -from pathlib import Path -from typing import Dict, List, Optional, Tuple - -# IMPORTATION THIRDPARTY -import certifi -import numpy as np -import pandas as pd -from fredapi import Fred -from requests import HTTPError - -# IMPORTATION INTERNAL -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.rich_config import console - -# pylint: disable=attribute-defined-outside-init,dangerous-default-value -# pylint: disable=C0302 - -logger = logging.getLogger(__name__) - -ice_bofa_path = (Path(__file__).parent / "ice_bofa_indices.csv").resolve() -commercial_paper_path = (Path(__file__).parent / "commercial_paper.csv").resolve() -spot_rates_path = (Path(__file__).parent / "corporate_spot_rates.csv").resolve() - -YIELD_CURVE_SERIES_NOMINAL = { - "1Month": "DGS1MO", - "3Month": "DGS3MO", - "6Month": "DGS6MO", - "1Year": "DGS1", - "2Year": "DGS2", - "3Year": "DGS3", - "5Year": "DGS5", - "7Year": "DGS7", - "10Year": "DGS10", - "20Year": "DGS20", - "30Year": "DGS30", -} -YIELD_CURVE_SERIES_REAL = { - "5Year": "DFII5", - "7Year": "DFII7", - "10Year": "DFII10", - "20Year": "DFII20", - "30Year": "DFII30", -} - -YIELD_CURVE_SERIES_CORPORATE_SPOT = { - "6Month": "HQMCB6MT", - "1Year": "HQMCB1YR", - "2Year": "HQMCB2YR", - "3Year": "HQMCB3YR", - "5Year": "HQMCB5YR", - "7Year": "HQMCB7YR", - "10Year": "HQMCB10YR", - "20Year": "HQMCB20YR", - "30Year": "HQMCB30YR", - "50Year": "HQMCB50YR", - "75Year": "HQMCB75YR", - "100Year": "HQMCB100YR", -} -YIELD_CURVE_SERIES_CORPORATE_PAR = { - "2Year": "HQMCB2YRP", - "5Year": "HQMCB5YRP", - "10Year": "HQMCB10YRP", - "30Year": "HQMCB30YRP", -} - -YIELD_CURVE_NOMINAL_RATES = [1 / 12, 0.25, 0.5, 1, 2, 3, 5, 7, 10, 20, 30] -YIELD_CURVE_SPOT_RATES = [0.5, 1, 2, 3, 5, 7, 10, 20, 30, 50, 75, 100] -YIELD_CURVE_REAL_RATES = [5, 7, 10, 20, 30] -YIELD_CURVE_PAR_RATES = [2, 5, 10, 30] - -ice_bofa_path = (Path(__file__).parent / "ice_bofa_indices.csv").resolve() -commercial_paper_path = (Path(__file__).parent / "commercial_paper.csv").resolve() -spot_rates_path = (Path(__file__).parent / "corporate_spot_rates.csv").resolve() - - -NAME_TO_ID_PROJECTION = { - "Range High": ["FEDTARRH", "FEDTARRHLR"], - "Central tendency High": ["FEDTARCTH", "FEDTARCTHLR"], - "Median": ["FEDTARMD", "FEDTARMDLR"], - "Range Midpoint": ["FEDTARRM", "FEDTARRMLR"], - "Central tendency Midpoint": ["FEDTARCTM", "FEDTARCTMLR"], - "Range Low": ["FEDTARRL", "FEDTARRLLR"], - "Central tendency Low": ["FEDTARCTL", "FEDTARCTLLR"], -} - -NAME_TO_ID_ECB = {"deposit": "ECBDFR", "lending": "ECBMLFR", "refinancing": "ECBMRRFR"} - -USARATES_TO_FRED_ID = { - "4_week": {"tbill": "DTB4WK"}, - "1_month": {"cmn": "DGS1MO"}, - "3_month": {"tbill": "TB3MS", "cmn": "DGS3MO"}, - "6_month": {"tbill": "DTB6", "cmn": "DGS6MO"}, - "1_year": {"tbill": "DTB1YR", "cmn": "DGS1"}, - "2_year": {"cmn": "DGS2"}, - "3_year": {"cmn": "DGS3"}, - "5_year": {"tips": "DFII5", "cmn": "DGS5"}, - "7_year": {"tips": "DFII7", "cmn": "DGS7"}, - "10_year": {"tips": "DFII10", "cmn": "DGS10"}, - "20_year": {"tips": "DFII20", "cmn": "DGS20"}, - "30_year": {"tips": "DFII30", "cmn": "DGS30"}, -} - -ICE_BOFA_TO_OPTIONS = { - "Type": ["total_return", "yield", "yield_to_worst"], - "Category": ["all", "duration", "eur", "usd"], - "Area": ["asia", "emea", "eu", "ex_g10", "latin_america", "us"], - "Grade": [ - "a", - "aa", - "aaa", - "b", - "bb", - "bbb", - "ccc", - "crossover", - "high_grade", - "high_yield", - "non_financial", - "non_sovereign", - "private_sector", - "public_sector", - ], -} - -MOODY_TO_OPTIONS: Dict[str, Dict] = { - "Type": { - "aaa": { - "index": { - "id": "DAAA", - "name": "Moody's Seasoned Aaa Corporate Bond Yield", - }, - "treasury": { - "id": "AAA10Y", - "name": "Moody's Seasoned Aaa Corporate Bond Yield Relative to Yield " - "on 10-Year Treasury Constant Maturity", - }, - "fed_funds": { - "id": "AAAFF", - "name": "Moody's Seasoned Aaa Corporate Bond Minus Federal Funds Rate", - }, - }, - "baa": { - "index": { - "id": "DBAA", - "name": "Moody's Seasoned Baa Corporate Bond Yield", - }, - "treasury": { - "id": "BAA10Y", - "name": "Moody's Seasoned Baa Corporate Bond Yield Relative " - "to Yield on 10-Year Treasury Constant Maturity", - }, - "fed_funds": { - "id": "BAAFF", - "name": "Moody's Seasoned Baa Corporate Bond Minus Federal Funds Rate", - }, - }, - }, - "Spread": ["treasury", "fed_funds"], # type: ignore -} -SPOT_TO_OPTIONS = { - "Maturity": [f"{i}y".replace(".0", "") for i in np.arange(1, 100.5, 0.5)], - "Category": ["spot_rate", "par_yield"], -} -CP_TO_OPTIONS = { - "Maturity": ["15d", "30d", "60d", "7d", "90d", "overnight"], - "Category": ["asset_backed", "financial", "non_financial", "spread"], - "Grade": ["a2_p2", "aa"], -} - -ESTR_PARAMETER_TO_ECB_ID = { - "volume_weighted_trimmed_mean_rate": "ECBESTRVOLWGTTRMDMNRT", - "number_of_transactions": "ECBESTRNUMTRANS", - "number_of_active_banks": "ECBESTRNUMACTBANKS", - "total_volume": "ECBESTRTOTVOL", - "share_of_volume_of_the_5_largest_active_banks": "ECBESTRSHRVOL5LRGACTBNK", - "rate_at_75th_percentile_of_volume": "ECBESTRRT75THPCTVOL", - "rate_at_25th_percentile_of_volume": "ECBESTRRT25THPCTVOL", -} - -SOFR_PARAMETER_TO_FRED_ID = { - "overnight": "SOFR", - "30_day_average": "SOFR30DAYAVG", - "90_day_average": "SOFR90DAYAVG", - "180_day_average": "SOFR180DAYAVG", - "index": "SOFRINDEX", -} -SONIA_PARAMETER_TO_FRED_ID = { - "rate": "IUDSOIA", - "index": "IUDZOS2", - "10th_percentile": "IUDZLS6", - "25th_percentile": "IUDZLS7", - "75th_percentile": "IUDZLS8", - "90th_percentile": "IUDZLS9", - "total_nominal_value": "IUDZLT2", -} -AMERIBOR_PARAMETER_TO_FRED_ID = { - "overnight": "AMERIBOR", - "term_30": "AMBOR30T", - "term_90": "AMBOR90T", - "1_week_term_structure": "AMBOR1W", - "1_month_term_structure": "AMBOR1M", - "3_month_term_structure": "AMBOR3M", - "6_month_term_structure": "AMBOR6M", - "1_year_term_structure": "AMBOR1Y", - "2_year_term_structure": "AMBOR2Y", - "30_day_ma": "AMBOR30", - "90_day_ma": "AMBOR90", -} - -FED_PARAMETER_TO_FRED_ID = { - "monthly": "FEDFUNDS", - "daily": "DFF", - "weekly": "FF", - "daily_excl_weekend": "RIFSPFFNB", - "annual": "RIFSPFFNA", - "biweekly": "RIFSPFFNBWAW", - "volume": "EFFRVOL", -} - -OBFR_PARAMETER_TO_FRED_ID = { - "daily": "OBFR", - "volume": "OBFRVOL", -} - -DWPCR_PARAMETER_TO_FRED_ID = { - "daily_excl_weekend": "DPCREDIT", - "monthly": "MPCREDIT", - "weekly": "WPCREDIT", - "daily": "RIFSRPF02ND", - "annual": "RIFSRPF02NA", -} -TMC_PARAMETER_TO_FRED_ID = { - "3_month": "T10Y3M", - "2_year": "T10Y2Y", -} -FFRMC_PARAMETER_TO_FRED_ID = { - "10_year": "T10YFF", - "5_year": "T5YFF", - "1_year": "T1YFF", - "6_month": "T6MFF", - "3_month": "T3MFF", -} - -TBFFR_PARAMETER_TO_FRED_ID = { - "3_month": "TB3SMFFM", - "6_month": "TB6SMFFM", -} - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_series_data( - series_id: str, start_date: Optional[str] = None, end_date: Optional[str] = None -) -> pd.DataFrame: - """Get Series data. [Source: FRED] - - Parameters - ---------- - series_id : str - Series ID to get data from - start_date : Optional[str] - Start date to get data from, format yyyy-mm-dd - end_date : Optional[str] - End data to get from, format yyyy-mm-dd - - Returns - ------- - pd.DataFrame - Series data - """ - try: - # Necessary for installer so that it can locate the correct certificates for - # API calls and https - # https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/73270162#73270162 - os.environ["REQUESTS_CA_BUNDLE"] = certifi.where() - os.environ["SSL_CERT_FILE"] = certifi.where() - fredapi_client = Fred(get_current_user().credentials.API_FRED_KEY) - df = fredapi_client.get_series(series_id, start_date, end_date) - # Series does not exist & invalid api keys - except HTTPError as e: - console.print(e) - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_yield_curve( - date: str = "", - return_date: bool = False, - inflation_adjusted: bool = False, - spot_or_par: Optional[str] = None, -) -> Tuple[pd.DataFrame, str]: - """Gets yield curve data from FRED. - - The graphic depiction of the relationship between the yield on bonds of the same credit quality but different - maturities is known as the yield curve. In the past, most market participants have constructed yield curves from - the observations of prices and yields in the Treasury market. Two reasons account for this tendency. First, - Treasury securities are viewed as free of default risk, and differences in creditworthiness do not affect yield - estimates. Second, as the most active bond market, the Treasury market offers the fewest problems of illiquidity - or infrequent trading. The key function of the Treasury yield curve is to serve as a benchmark for pricing bonds - and setting yields in other sectors of the debt market. - - It is clear that the market’s expectations of future rate changes are one important determinant of the - yield-curve shape. For example, a steeply upward-sloping curve may indicate market expectations of near-term Fed - tightening or of rising inflation. However, it may be too restrictive to assume that the yield differences across - bonds with different maturities only reflect the market’s rate expectations. The well-known pure expectations - hypothesis has such an extreme implication. The pure expectations hypothesis asserts that all government bonds - have the same near-term expected return (as the nominally riskless short-term bond) because the return-seeking - activity of risk-neutral traders removes all expected return differentials across bonds. - - Parameters - ---------- - date: str - Date to get curve for. If empty, gets most recent date (format yyyy-mm-dd) - return_date: bool - If True, returns date of yield curve - - Returns - ------- - Tuple[pd.DataFrame, str] - Dataframe of yields and maturities, - Date for which the yield curve is obtained - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> ycrv_df = openbb.fixedincome.ycrv() - - Since there is a delay with the data, the most recent date is returned and can be accessed with return_date=True - >>> ycrv_df, ycrv_date = openbb.fixedincome.ycrv(return_date=True) - """ - - # Necessary for installer so that it can locate the correct certificates for - # API calls and https - # https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error/73270162#73270162 - # os.environ["REQUESTS_CA_BUNDLE"] = certifi.where() - # os.environ["SSL_CERT_FILE"] = certifi.where() - - fredapi_client = Fred(get_current_user().credentials.API_FRED_KEY) - - df = pd.DataFrame() - - # Check that the date is in the past - today = datetime.now().strftime("%Y-%m-%d") - if date and date >= today: - console.print("[red]Date cannot be today or in the future[/red]") - if return_date: - return pd.DataFrame(), date - return pd.DataFrame() - - # Add in logic that will get the most recent date - if date: - get_last = False - start_date = ( - datetime.strptime(date, "%Y-%m-%d") - timedelta(days=50) - ).strftime("%Y-%m-%d") - else: - date = datetime.now().strftime("%Y-%m-%d") - start_date = (datetime.now() - timedelta(days=50)).strftime("%Y-%m-%d") - get_last = True - - if inflation_adjusted: - fred_series = YIELD_CURVE_SERIES_REAL - years = YIELD_CURVE_REAL_RATES - elif spot_or_par: - if spot_or_par == "spot": - years = YIELD_CURVE_SPOT_RATES # type: ignore - fred_series = YIELD_CURVE_SERIES_CORPORATE_SPOT - elif spot_or_par == "par": - years = YIELD_CURVE_PAR_RATES - fred_series = YIELD_CURVE_SERIES_CORPORATE_PAR - else: - console.print("Please select either 'spot' or 'par' rates.") - else: - fred_series = YIELD_CURVE_SERIES_NOMINAL - years = YIELD_CURVE_NOMINAL_RATES # type: ignore - - for key, s_id in fred_series.items(): - df = pd.concat( - [ - df, - pd.DataFrame( - fredapi_client.get_series(s_id, start_date), columns=[key] - ), - ], - axis=1, - ) - if df.empty: - if return_date: - return pd.DataFrame(), date - return pd.DataFrame() - # Drop rows with NaN -- corresponding to weekends typically - df = df.dropna() - - if date not in df.index or get_last: - # If the get_last flag is true, we want the first date, otherwise we want the last date. - idx = -1 if get_last else 0 - date_of_yield = df.index[idx].strftime("%Y-%m-%d") - rates = pd.DataFrame(df.iloc[idx, :].values, columns=["Rate"]) - - if spot_or_par: - console.print( - f"Because {spot_or_par.title()} rates are published monthly, " - f"the nearest date to {date} is used which is {date_of_yield}." - ) - else: - date_of_yield = date - series = df[df.index == date] - if series.empty: - return pd.DataFrame(), date_of_yield - rates = pd.DataFrame(series.values.T, columns=["Rate"]) - - rates.insert(0, "Maturity", years) - if return_date: - return rates, date_of_yield - return rates - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_estr( - parameter: str = "volume_weighted_trimmed_mean_rate", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Obtain data for Euro Short-Term Rate (ESTR) - - Parameters - ---------- - parameter: str - The parameter to get data for. - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = ESTR_PARAMETER_TO_ECB_ID.get(parameter, "") - - return pd.DataFrame( - get_series_data(series_id=series_id, start_date=start_date, end_date=end_date), - columns=[series_id], - ) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_sofr( - parameter: str = "overnight", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Obtain data for Secured Overnight Financing Rate (SOFR) - - Parameters - ---------- - parameter: str - The parameter to get data for. Choose from: - "overnight" - "30_day_average" - "90_day_average" - "180_day_average" - "index" - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = SOFR_PARAMETER_TO_FRED_ID.get(parameter, "") - - return pd.DataFrame( - get_series_data(series_id=series_id, start_date=start_date, end_date=end_date), - columns=[series_id], - ) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_sonia( - parameter: str = "rate", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Obtain data for Sterling Overnight Index Average (SONIA) - - Parameters - ---------- - parameter: str - The parameter to get data for. - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = SONIA_PARAMETER_TO_FRED_ID.get(parameter, "") - - return pd.DataFrame( - get_series_data(series_id=series_id, start_date=start_date, end_date=end_date), - columns=[series_id], - ) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def get_ameribor( - parameter: str = "overnight", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Obtain data for American Interbank Offered Rate (AMERIBOR) - - Parameters - ---------- - parameter: str - The parameter to get data for. Choose from: - "overnight": "AMERIBOR", - "term_30": "AMBOR30T", - "term_90": "AMBOR90T", - "1_week_term_structure": "AMBOR1W", - "1_month_term_structure": "AMBOR1M", - "3_month_term_structure": "AMBOR3M", - "6_month_term_structure": "AMBOR6M", - "1_year_term_structure": "AMBOR1Y", - "2_year_term_structure": "AMBOR2Y", - "30_day_ma": "AMBOR30", - "90_day_ma": "AMBOR90", - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = AMERIBOR_PARAMETER_TO_FRED_ID.get(parameter, "") - - return pd.DataFrame( - get_series_data(series_id=series_id, start_date=start_date, end_date=end_date), - columns=[series_id], - ) - - -def get_fed( - parameter: str = "monthly", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - overnight: bool = False, - quantiles: bool = False, - target: bool = False, -) -> pd.DataFrame: - """Obtain data for Effective Federal Funds Rate. - - Parameters - ---------- - parameter: str - The parameter to get data for. Choose from: - "monthly" - "daily" - "weekly" - "daily_excl_weekend" - "annual" - "biweekly" - "volume" - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - overnight: bool - Whether you want to plot the Overnight Banking Federal Rate - quantiles: bool - Whether you want to see the 1, 25, 75 and 99 percentiles - target: bool - Whether you want to see the high and low target range - """ - series_id = FED_PARAMETER_TO_FRED_ID[parameter] - - if overnight: - # This piece of code adjusts the series id when the user wants to plot the overnight rate - if series_id == "DFF": - series_id = "OBFR" - elif series_id == "EFFRVOL": - series_id = "OBFRVOL" - else: - console.print( - "The Overnight Banking Federal Rate only supports Daily data." - ) - series_id = "OBFR" - - if quantiles or target and not overnight: - data_series = [series_id if series_id != "EFFRVOL" else "EFFR"] - series_id = series_id if series_id != "EFFRVOL" else "EFFR" - - if quantiles: - data_series.extend(["EFFR1", "EFFR25", "EFFR75", "EFFR99"]) - if target: - data_series.extend(["DFEDTARU", "DFEDTARL"]) - - df = pd.DataFrame() - - for series in data_series: - data = pd.DataFrame( - get_series_data( - series_id=series, start_date=start_date, end_date=end_date - ), - columns=[series], - ) - df = data if series == series_id else pd.concat([df, data], axis=1) - - df = df.dropna() - return df - return pd.DataFrame( - get_series_data(series_id=series_id, start_date=start_date, end_date=end_date), - columns=[series_id], - ) - - -def get_iorb( - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Obtain data for Interest Rate on Reserve Balances. - - Parameters - ---------- - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - return pd.DataFrame( - get_series_data(series_id="IORB", start_date=start_date, end_date=end_date), - columns=["IORB"], - ) - - -def get_projection(long_run: bool = False): - """Obtain data for the Federal Reserve's projection of the federal funds rate. - - Parameters - ---------- - long_run: str - Whether to obtain data for the long run projection. - """ - data_series = {} - - for projection, values in NAME_TO_ID_PROJECTION.items(): - data_series[projection] = get_series_data(series_id=values[long_run]) - - data_series_df = pd.DataFrame.from_dict(data_series).dropna() - data_series_df.index = pd.to_datetime(data_series_df.index).date - data_series_df.index.name = "Date" - return data_series_df - - -def get_dwpcr( - parameter: str = "daily_excl_weekend", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Obtain data for the Discount Window Primary Credit Rate. - - Parameters - ---------- - parameter: str - The parameter to get data for. Choose from: - "daily_excl_weekend" - "monthly" - "weekly" - "daily" - "annual" - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = DWPCR_PARAMETER_TO_FRED_ID[parameter] - return pd.DataFrame( - get_series_data(series_id=series_id, start_date=start_date, end_date=end_date), - columns=[series_id], - ) - - -def get_ecb( - interest_type: Optional[str] = None, - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Obtain data for ECB interest rates. - - The Governing Council of the ECB sets the key interest rates for the euro area: - - - The interest rate on the main refinancing operations (MRO), which provide - the bulk of liquidity to the banking system. - - The rate on the deposit facility, which banks may use to make overnight deposits with the Eurosystem. - - The rate on the marginal lending facility, which offers overnight credit to banks from the Eurosystem. - - Parameters - ---------- - interest_type: Optional[str] - The ability to decide what interest rate to plot. Choose from: - "deposit" - "lending" - "refinancing" - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - if interest_type: - df = pd.DataFrame( - get_series_data( - series_id=NAME_TO_ID_ECB[interest_type], - start_date=start_date, - end_date=end_date, - ), - columns=[interest_type], - ) - - else: - series_dictionary = {} - - for interest_name, value in NAME_TO_ID_ECB.items(): - series_dictionary[interest_name.title()] = get_series_data( - series_id=value, start_date=start_date, end_date=end_date - ) - - df = pd.DataFrame.from_dict(series_dictionary) - df.index = pd.to_datetime(df.index).date - df.index.name = "Date" - - return df - - -def get_usrates( - parameter: str = "tbills", - maturity: str = "3_months", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -): - """Plot various treasury rates from the United States - - A Treasury Bill (T-Bill) is a short-term U.S. government debt obligation backed by the Treasury Department with a - maturity of one year or less. Treasury bills are usually sold in denominations of $1,000. However, some can reach - a maximum denomination of $5 million in non-competitive bids. These securities are widely regarded as low-risk - and secure investments. - - Yields on Treasury nominal securities at “constant maturity” are interpolated by the U.S. Treasury from the daily - yield curve for non-inflation-indexed Treasury securities. This curve, which relates the yield on a security to - its time to maturity, is based on the closing market bid yields on actively traded Treasury securities in the - over-the-counter market. These market yields are calculated from composites of quotations obtained by the Federal - Reserve Bank of New York. The constant maturity yield values are read from the yield curve at fixed maturities, - currently 1, 3, and 6 months and 1, 2, 3, 5, 7, 10, 20, and 30 years. This method provides a yield for a 10-year - maturity, for example, even if no outstanding security has exactly 10 years remaining to maturity. Similarly, - yields on inflation-indexed securities at “constant maturity” are interpolated from the daily yield curve for - Treasury inflation protected securities in the over-the-counter market. The inflation-indexed constant maturity - yields are read from this yield curve at fixed maturities, currently 5, 7, 10, 20, and 30 years. - - Parameters - ---------- - parameter: str - Either "tbills", "cmn", or "tips". - maturity: str - Depending on the chosen parameter, a set of maturities is available. - "4_week": {"tbill": "DTB4WK"}, - "1_month": {"cmn": "DGS1MO"}, - "3_month": {"tbill": "TB3MS", "cmn": "DGS3MO"}, - "6_month": {"tbill": "DTB6", "cmn": "DGS6MO"}, - "1_year": {"tbill": "DTB1YR", "cmn": "DGS1"}, - "2_year": {"cmn": "DGS2"}, - "3_year": {"cmn": "DGS3"}, - "5_year": {"tips": "DFII5", "cmn": "DGS5"}, - "7_year": {"tips": "DFII7", "cmn": "DGS7"}, - "10_year": {"tips": "DFII10", "cmn": "DGS10"}, - "20_year": {"tips": "DFII20", "cmn": "DGS20"}, - "30_year": {"tips": "DFII30", "cmn": "DGS30"}, - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = USARATES_TO_FRED_ID[maturity][parameter] - return pd.DataFrame( - get_series_data(series_id=series_id, start_date=start_date, end_date=end_date), - columns=[series_id], - ) - - -def get_icebofa( - data_type: str = "yield", - category: str = "all", - area: str = "us", - grade: str = "non_sovereign", - options: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get data for ICE BofA US Corporate Bond Indices. - - Find the available options by using the options parameter. - - Parameters - ---------- - data_type: str - The type of data you want to see, either "yield", "yield_to_worst", "total_return", or "spread" - category: str - The type of category you want to see, either "all", "duration", "eur" or "usd". - area: str - The type of area you want to see, either "asia", "emea", "eu", "ex_g10", "latin_america" or "us" - grade: str - The type of grade you want to see, either "a", "aa", "aaa", "b", "bb", "bbb", "ccc", "crossover", - "high_grade", "high_yield", "non_financial", "non_sovereign", "private_sector", "public_sector" - options: bool - Set to True to obtain the available options. - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series = pd.read_csv(ice_bofa_path) - - if options: - return series.drop( - ["Frequency", "Units", "Asset Class", "FRED Series ID", "Description"], - axis=1, - )[ - series["Type"].isin( - ["yield", "yield_to_worst", "total_return"] - if data_type != "spread" - else ["spread"] - ) - ] - - if data_type == "total_return": - units = "index" - elif data_type in ["yield", "yield_to_worst", "spread"]: - units = "percent" - else: - console.print( - "Please choose either 'yield', 'yield_to_worst', " - "'total_return' or 'spread'." - ) - return pd.DataFrame() - - series = series[ - (series["Type"] == data_type) - & (series["Units"] == units) - & (series["Frequency"] == "daily") - & (series["Asset Class"] == "bonds") - & (series["Category"] == category) - & (series["Area"] == area) - & (series["Grade"] == grade) - ] - - if series.empty: - console.print("The combination of parameters does not result in any data.") - return pd.DataFrame() - - series_dictionary = {} - - for series_id, title in series[["FRED Series ID", "Title"]].values: - series_dictionary[title] = get_series_data( - series_id=series_id, start_date=start_date, end_date=end_date - ) - - df = pd.DataFrame.from_dict(series_dictionary) - df.index = pd.to_datetime(df.index).date - df.index.name = "Date" - return df - - -def get_moody( - data_type: str = "aaa", - spread: Optional[str] = None, - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get data for Moody Corporate Bond Index - - Parameters - ---------- - data_type: str - The type of data you want to see, either "aaa" or "baa" - spread: Optional[str] - Whether you want to show the spread for treasury or fed_funds - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = MOODY_TO_OPTIONS["Type"][data_type][spread if spread else "index"]["id"] - - series = get_series_data( - series_id=series_id, start_date=start_date, end_date=end_date - ) - - df = pd.DataFrame(series, columns=[f"{data_type}_{spread if spread else 'index'}"]) - df.index = pd.to_datetime(df.index).date - df.index.name = "Date" - return df - - -def get_cp( - maturity: str = "30d", - category: str = "financial", - grade: str = "aa", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -): - """Obtain Commercial Paper data - - Parameters - ---------- - maturity: str - The maturity you want to see, either "overnight", "7d", "15d", "30d", "60d" or "90d" - category: str - The category you want to see, either "asset_backed", "financial" or "non_financial" - grade: str - The type of grade you want to see, either "a2_p2" or "aa" - description: bool - Whether you wish to obtain a description of the data. - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - if grade == "a2_p2" and category != "non_financial": - category = "non_financial" - series = pd.read_csv(commercial_paper_path) - series = series[ - (series["Maturity"] == maturity) - & (series["Category"] == category) - & (series["Grade"] == grade) - ] - - if series.empty: - console.print("The combination of parameters does not result in any data.") - return pd.DataFrame() - - series_dictionary = {} - - for series_id, title in series[["FRED Series ID", "Title"]].values: - series_dictionary[title] = get_series_data( - series_id=series_id, start_date=start_date, end_date=end_date - ) - - df = pd.DataFrame.from_dict(series_dictionary) - df.index = pd.to_datetime(df.index).date - df.index.name = "Date" - return df - - -def get_spot( - maturity: List = ["10y"], - category: List = ["spot_rate"], - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """ - The spot rate for any maturity is the yield on a bond that provides - a single payment at that maturity. This is a zero coupon bond. Because each - spot rate pertains to a single cashflow, it is the relevant interest rate - concept for discounting a pension liability at the same maturity. - - Parameters - ---------- - maturity: str - The maturity you want to see (ranging from '1y' to '100y' in interval of 0.5, e.g. '50.5y') - category: list - The category you want to see ('par_yield' and/or 'spot_rate') - description: bool - Whether you wish to obtain a description of the data. - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series = pd.read_csv(spot_rates_path) - - series = series[ - (series["Maturity"].isin(maturity)) & (series["Category"].isin(category)) - ] - - if "par_yield" in category and "par_yield" not in series["Category"].values: - console.print( - "No Par Yield data available for (some of) the selected maturities. " - "Only 2y, 5y, 10y and 30y is available." - ) - - series_dictionary = {} - - for series_id, title in series[["FRED Series ID", "Title"]].values: - series_dictionary[title] = get_series_data( - series_id=series_id, start_date=start_date, end_date=end_date - ) - - df = pd.DataFrame.from_dict(series_dictionary) - df.index = pd.to_datetime(df.index).date - df.index.name = "Date" - return df - - -def get_hqm( - date: Optional[str] = None, - par: bool = False, -) -> Tuple[pd.DataFrame, str]: - """ - The HQM yield curve represents the high quality corporate bond market, i.e., - corporate bonds rated AAA, AA, or A. The HQM curve contains two regression terms. These - terms are adjustment factors that blend AAA, AA, and A bonds into a single HQM yield curve - that is the market-weighted average (MWA) quality of high quality bonds. - - Parameters - ---------- - date: str - The date of the yield curve you wish to plot - par: bool - Whether you wish to plot the par yield curve as well - """ - df = pd.DataFrame() - data_types = ["spot", "par"] if par else ["spot"] - - for types in data_types: - subset, date_of_yield = get_yield_curve(date, True, spot_or_par=types) - subset.set_index("Maturity", inplace=True) - subset.columns = [types] - - df = pd.concat([df, subset], axis=1) - - if par: - # Drop NaNs because of length mismatch - df = df.dropna() - - if df.empty: - console.print(f"[red]Yield data not found at {date_of_yield}.[/red]\n") - - return df, date_of_yield - - -def get_tbffr( - parameter: str = "3_month", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get data for Selected Treasury Bill Minus Federal Funds Rate. - - Parameters - ---------- - parameter: str - FRED ID of TBFFR data to plot, options: ["3_month", "6_month"] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = TBFFR_PARAMETER_TO_FRED_ID[parameter] - - return pd.DataFrame( - get_series_data(series_id, start_date, end_date), columns=[series_id] - ) - - -def get_icespread( - category: str = "all", - area: str = "us", - grade: str = "non_sovereign", - options: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get data for ICE BofA US Corporate Bond Spreads - - Parameters - ---------- - category: str - The type of category you want to see, either "all", "duration", "eur" or "usd". - area: str - The type of area you want to see, either "asia", "emea", "eu", "ex_g10", "latin_america" or "us" - grade: str - The type of grade you want to see, either "a", "aa", "aaa", "b", "bb", "bbb", "ccc", "crossover", - "high_grade", "high_yield", "non_financial", "non_sovereign", "private_sector", "public_sector" - """ - return get_icebofa( - data_type="spread", - category=category, - area=area, - grade=grade, - options=options, - start_date=start_date, - end_date=end_date, - ) - - -def get_ffrmc( - parameter: str = "10_year", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get data for Selected Treasury Constant Maturity Minus Federal Funds Rate - - Constant maturity is the theoretical value of a U.S. Treasury that is based on recent values of auctioned U.S. - Treasuries. The value is obtained by the U.S. Treasury on a daily basis through interpolation of the Treasury - yield curve which, in turn, is based on closing bid-yields of actively-traded Treasury securities. - - Parameters - ---------- - parameter: str - FRED ID of FFRMC data to plot - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = FFRMC_PARAMETER_TO_FRED_ID[parameter] - return pd.DataFrame( - get_series_data(series_id, start_date, end_date), columns=[series_id] - ) - - -def get_tmc( - parameter: str = "3_month", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get data for 10-Year Treasury Constant Maturity Minus Selected Treasury Constant Maturity. - - Constant maturity is the theoretical value of a U.S. Treasury that is based on recent values of auctioned U.S. - Treasuries. The value is obtained by the U.S. Treasury on a daily basis through interpolation of the Treasury - yield curve which, in turn, is based on closing bid-yields of actively-traded Treasury securities. - - Parameters - ---------- - parameter: str - FRED ID of TMC data to plot, options: ["T10Y3M", "T10Y3M"] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - """ - series_id = TMC_PARAMETER_TO_FRED_ID[parameter] - return pd.DataFrame( - get_series_data(series_id, start_date, end_date), columns=[series_id] - ) diff --git a/openbb_terminal/fixedincome/fred_view.py b/openbb_terminal/fixedincome/fred_view.py deleted file mode 100644 index 9c5cf791758..00000000000 --- a/openbb_terminal/fixedincome/fred_view.py +++ /dev/null @@ -1,1676 +0,0 @@ -""" FRED view """ - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import logging -import os -from pathlib import Path -from typing import Dict, List, Optional - -# IMPORTATION THIRDPARTY -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.fixedincome import fred_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint: disable=C0302,R0913,W0102 - -ice_bofa_path = (Path(__file__).parent / "ice_bofa_indices.csv").resolve() -commercial_paper_path = (Path(__file__).parent / "commercial_paper.csv").resolve() -spot_rates_path = (Path(__file__).parent / "corporate_spot_rates.csv").resolve() - -ID_TO_NAME_ESTR = { - "ECBESTRVOLWGTTRMDMNRT": "Euro Short-Term Rate: Volume-Weighted Trimmed Mean Rate", - "ECBESTRTOTVOL": "Euro Short-Term Rate: Total Volume", - "ECBESTRNUMTRANS": "Euro Short-Term Rate: Number of Transactions", - "ECBESTRRT75THPCTVOL": "Euro Short-Term Rate: Rate at 75th Percentile of Volume", - "ECBESTRNUMACTBANKS": "Euro Short-Term Rate: Number of Active Banks", - "ECBESTRSHRVOL5LRGACTBNK": "Euro Short-Term Rate: Share of Volume of the 5 Largest Active Bank", - "ECBESTRRT25THPCTVOL": "Euro Short-Term Rate: Rate at 25th Percentile of Volume", -} -ID_TO_NAME_SOFR = { - "SOFR": "Secured Overnight Financing Rate (SOFR)", - "SOFR30DAYAVG": "30-Day Average SOFR", - "SOFR90DAYAVG": "90-Day Average SOFR", - "SOFR180DAYAVG": "180-Day Average SOFR", - "SOFRINDEX": "SOFR Index", -} -ID_TO_NAME_SONIA = { - "IUDSOIA": "Daily Sterling Overnight Index Average (SONIA) Rate", - "IUDZOS2": "SONIA Compounded Index", - "IUDZLS6": "SONIA Rate: 10th percentile", - "IUDZLS7": "SONIA Rate: 25th percentile", - "IUDZLS8": "SONIA Rate: 75th percentile", - "IUDZLS9": "SONIA Rate: 90th percentile", - "IUDZLT2": "SONIA Rate Total Nominal Value", -} -ID_TO_NAME_AMERIBOR = { - "AMERIBOR": "Overnight Unsecured AMERIBOR Benchmark Interest Rate", - "AMBOR30T": "AMERIBOR Term-30 Derived Interest Rate Index", - "AMBOR90T": "AMERIBOR Term-90 Derived Interest Rate Index", - "AMBOR1W": "1-Week AMERIBOR Term Structure of Interest Rates", - "AMBOR1M": "1-Month AMERIBOR Term Structure of Interest Rates", - "AMBOR3M": "3-Month AMERIBOR Term Structure of Interest Rates", - "AMBOR6M": "6-Month AMERIBOR Term Structure of Interest Rates", - "AMBOR1Y": "1-Year AMERIBOR Term Structure of Interest Rates", - "AMBOR2Y": "2-Year AMERIBOR Term Structure of Interest Rates", - "AMBOR30": "30-Day Moving Average AMERIBOR Benchmark Interest Rate", - "AMBOR90": "90-Day Moving Average AMERIBOR Benchmark Interest Rate", -} -ID_TO_NAME_FED = { - "FEDFUNDS": "Monthly Effective Federal Funds Rate", - "EFFR": "Daily Effective Federal Funds Rate", - "DFF": "Daily Effective Federal Funds Rate", - "OBFR": "Daily Overnight Bank Funding Rate", - "FF": "Weekly Effective Federal Funds Rate", - "RIFSPFFNB": "Daily (Excl. Weekends) Effective Federal Funds Rate", - "RIFSPFFNA": "Annual Effective Federal Funds Rate", - "RIFSPFFNBWAW": "Biweekly Effective Federal Funds Rate", - "EFFRVOL": "Effective Federal Funds Volume", - "OBFRVOL": "Overnight Bank Funding Volume", -} - -ID_TO_NAME_DWPCR = { - "MPCREDIT": "Monthly", - "RIFSRPF02ND": "Daily (incl. Weekends)", - "WPCREDIT": "Weekly", - "DPCREDIT": "Daily (excl. Weekends)", - "RIFSRPF02NA": "Annual", -} -ID_TO_NAME_TMC = { - "T10Y3M": "3-Month", - "T10Y2Y": "2-Year", -} -ID_TO_NAME_FFRMC = { - "T10YFF": "10-Year", - "T5YFF": "5-Year", - "T1YFF": "1-Year", - "T6MFF": "6-Month", - "T3MFF": "3-Month", -} -ID_TO_NAME_SECONDARY = { - "TB3MS": "3-Month", - "DTB4WK": "4-Week", - "DTB1YR": "1-Year", - "DTB6": "6-Month", -} -ID_TO_NAME_TIPS = { - "DFII5": "5-Year", - "DFII7": "7-Year", - "DFII10": "10-Year", - "DFII20": "20-Year", - "DFII30": "30-Year", -} -ID_TO_NAME_CMN = { - "DGS1MO": "1 Month", - "DGS3MO": "3 Month", - "DGS6MO": "6 Month", - "DGS1": "1 Year", - "DGS2": "2 Year", - "DGS3": "3 Year", - "DGS5": "5 Year", - "DGS7": "7 Year", - "DGS10": "10 Year", - "DGS20": "20 Year", - "DGS30": "30 Year", -} -ID_TO_NAME_TBFFR = { - "TB3SMFFM": "3 Month", - "TB6SMFFM": "6 Month", -} - -NAME_TO_ID_PROJECTION = { - "Range High": ["FEDTARRH", "FEDTARRHLR"], - "Central tendency High": ["FEDTARCTH", "FEDTARCTHLR"], - "Median": ["FEDTARMD", "FEDTARMDLR"], - "Range Midpoint": ["FEDTARRM", "FEDTARRMLR"], - "Central tendency Midpoint": ["FEDTARCTM", "FEDTARCTMLR"], - "Range Low": ["FEDTARRL", "FEDTARRLLR"], - "Central tendency Low": ["FEDTARCTL", "FEDTARCTLLR"], -} - -NAME_TO_ID_ECB = {"deposit": "ECBDFR", "lending": "ECBMLFR", "refinancing": "ECBMRRFR"} - -MOODY_TO_OPTIONS: Dict[str, Dict] = { - "Type": { - "aaa": { - "index": { - "id": "DAAA", - "name": "Moody's Seasoned Aaa Corporate Bond Yield", - }, - "treasury": { - "id": "AAA10Y", - "name": "Moody's Seasoned Aaa Corporate Bond Yield Relative to Yield " - "on 10-Year Treasury Constant Maturity", - }, - "fed_funds": { - "id": "AAAFF", - "name": "Moody's Seasoned Aaa Corporate Bond Minus Federal Funds Rate", - }, - }, - "baa": { - "index": { - "id": "DBAA", - "name": "Moody's Seasoned Baa Corporate Bond Yield", - }, - "treasury": { - "id": "BAA10Y", - "name": "Moody's Seasoned Baa Corporate Bond Yield Relative " - "to Yield on 10-Year Treasury Constant Maturity", - }, - "fed_funds": { - "id": "BAAFF", - "name": "Moody's Seasoned Baa Corporate Bond Minus Federal Funds Rate", - }, - }, - }, - "Spread": ["treasury", "fed_funds"], # type: ignore -} - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_estr( - parameter: str = "volume_weighted_trimmed_mean_rate", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Euro Short-Term Rate (ESTR) - - Parameters - ---------- - parameter: str - FRED ID of ESTR data to plot, options: ['ECBESTRVOLWGTTRMDMNRT', - 'ECBESTRTOTVOL', 'ECBESTRNUMTRANS', 'ECBESTRRT75THPCTVOL', - 'ECBESTRNUMACTBANKS', 'ECBESTRSHRVOL5LRGACTBNK', 'ECBESTRRT25THPCTVOL'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = fred_model.get_estr(parameter, start_date, end_date) - series_id = fred_model.ESTR_PARAMETER_TO_ECB_ID[parameter] - - ylabel_dict = { - "ECBESTRTOTVOL": "Millions in EUR", - "ECBESTRNUMACTBANKS": "Number of Banks", - "ECBESTRNUMTRANS": "Number of Transactions", - } - - fig = OpenBBFigure(yaxis_title=ylabel_dict.get(series_id, "Yield (%)")) - fig.set_title(ID_TO_NAME_ESTR[series_id]) - - fig.add_scatter( - x=df.index, y=df[series_id], mode="lines", name=series_id, line_width=2 - ) - - if export: - if series_id not in ["ECBESTRTOTVOL", "ECBESTRNUMACTBANKS", "ECBESTRNUMTRANS"]: - # Check whether it is a percentage, relevant for exporting - df_transformed = pd.DataFrame(df, columns=[series_id]) - else: - df_transformed = pd.DataFrame(df, columns=[series_id]) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - df_transformed, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_sofr( - parameter: str = "overnight", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Secured Overnight Financing Rate (SOFR) - - Parameters - ---------- - parameter: str - FRED ID of SOFR data to plot, options: ['SOFR', 'SOFR30DAYAVG', 'SOFR90DAYAVG', 'SOFR180DAYAVG', 'SOFRINDEX'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.SOFR_PARAMETER_TO_FRED_ID[parameter] - - df = fred_model.get_sofr( - parameter=parameter, start_date=start_date, end_date=end_date - ) - - fig = OpenBBFigure( - yaxis_title=( - "Yield (%)" if series_id != "SOFRINDEX" else "Index [Base = 04-2018]" - ) - ) - fig.set_title(ID_TO_NAME_SOFR[series_id]) - - fig.add_scatter(x=df.index, y=df[series_id], mode="lines", name=series_id) - - if export: - if series_id != "SOFRINDEX": - # Check whether it is a percentage, relevant for exporting - df_transformed = pd.DataFrame(df, columns=[series_id]) - else: - df_transformed = pd.DataFrame(df, columns=[series_id]) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - df_transformed, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_sonia( - parameter: str = "rate", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Sterling Overnight Index Average (SONIA) - - Parameters - ---------- - parameter: str - FRED ID of SONIA data to plot, options: ['IUDSOIA', 'IUDZOS2', - 'IUDZLS6', 'IUDZLS7', 'IUDZLS8', 'IUDZLS9', 'IUDZLT2'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.SONIA_PARAMETER_TO_FRED_ID[parameter] - - df = fred_model.get_sonia( - parameter=parameter, start_date=start_date, end_date=end_date - ) - - ylabel_dict = { - "IUDZLT2": "Millions of GDP", - "IUDZOS2": "Index [Base = 04-2018]", - } - - fig = OpenBBFigure(yaxis_title=ylabel_dict.get(series_id, "Yield (%)")) - fig.set_title(ID_TO_NAME_SONIA[series_id]) - - fig.add_scatter(x=df.index, y=df[series_id], mode="lines", name=series_id) - - if export: - if series_id not in ["IUDZOS2", "IUDZLT2"]: - # Check whether it is a percentage, relevant for exporting - df_transformed = pd.DataFrame(df, columns=[series_id]) - else: - df_transformed = pd.DataFrame(df, columns=[series_id]) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - df_transformed, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_ameribor( - parameter: str = "overnight", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot American Interbank Offered Rate (AMERIBOR) - - Parameters - ---------- - parameter: str - FRED ID of AMERIBOR data to plot, options: ['AMERIBOR', 'AMBOR30T', - 'AMBOR90T', 'AMBOR1W', 'AMBOR1M', 'AMBOR3M', 'AMBOR6M', 'AMBOR1Y', - 'AMBOR2Y', 'AMBOR30', 'AMBOR90'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.AMERIBOR_PARAMETER_TO_FRED_ID[parameter] - - df = fred_model.get_ameribor( - parameter=parameter, start_date=start_date, end_date=end_date - ) - - fig = OpenBBFigure( - yaxis_title="Index" if series_id in ["AMBOR30T", "AMBOR90T"] else "Yield (%)" - ) - fig.set_title(ID_TO_NAME_AMERIBOR[series_id]) - - fig.add_scatter(x=df.index, y=df[series_id], mode="lines", name=series_id) - - if export: - if series_id not in ["AMBOR30T", "AMBOR90T"]: - # Check whether it is a percentage, relevant for exporting - df_transformed = pd.DataFrame(df, columns=[series_id]) - else: - df_transformed = pd.DataFrame(df, columns=[series_id]) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - df_transformed, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_fftr( - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Federal Funds Target Range. - - A bank rate is the interest rate a nation's central bank charges to its domestic banks to borrow money. The rates - central banks charge are set to stabilize the economy. In the United States, the Federal Reserve System's Board - of Governors set the bank rate, also known as the discount rate. - - Parameters - ---------- - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_upper = fred_model.get_series_data( - series_id="DFEDTARU", start_date=start_date, end_date=end_date - ) - df_lower = fred_model.get_series_data( - series_id="DFEDTARL", start_date=start_date, end_date=end_date - ) - df = pd.DataFrame([df_upper, df_lower]).transpose() - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title("Federal Funds Target Range") - - for series in df.columns: - fig.add_scatter(x=df.index, y=df[series], name=series) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fftr", - pd.DataFrame(df, columns=["FFTR"]), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_fed( - parameter: str = "monthly", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - overnight: bool = False, - quantiles: bool = False, - target: bool = False, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -): - """Plot Effective Federal Funds Rate. - - A bank rate is the interest rate a nation's central bank charges to its domestic banks to borrow money. The rates - central banks charge are set to stabilize the economy. In the United States, the Federal Reserve System's Board - of Governors set the bank rate, also known as the discount rate. - - Parameters - ---------- - parameter: str - FRED ID of EFFER data to plot, options: ['EFFR', 'EFFRVOL', 'EFFR1', 'EFFR25', 'EFFR75', 'EFFR99'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - overnight: bool - Whether you want to plot the Overnight Banking Federal Rate - quantiles: bool - Whether you want to see the 1, 25, 75 and 99 percentiles - target: bool - Whether you want to see the high and low target range - raw : bool - Show raw data - export: str - Export data to csv or excel file - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.FED_PARAMETER_TO_FRED_ID[parameter] - - if overnight: - # This piece of code adjusts the series id when the user wants to plot the overnight rate - if series_id == "DFF": - series_id = "OBFR" - elif series_id == "EFFRVOL": - series_id = "OBFRVOL" - else: - console.print( - "The Overnight Banking Federal Rate only supports Daily data." - ) - series_id = "OBFR" - - fig = OpenBBFigure() - - if quantiles or target and not overnight: - series_id = series_id if series_id != "EFFRVOL" else "EFFR" - - df = fred_model.get_fed( - parameter, start_date, end_date, overnight, quantiles, target - ) - - for column in df.columns: - fig.add_scatter( - x=df.index, - y=df[column], - name=column, - line_width=3 if column == series_id else 1.3, - mode="lines", - ) - fig.set_title(ID_TO_NAME_FED[series_id]) - fig.set_yaxis_title("Yield (%)") - - else: - df = fred_model.get_fed( - parameter, start_date, end_date, overnight, quantiles, target - ) - - fig.add_scatter( - x=df.index, y=df[series_id], name=series_id, line_width=2.5, mode="lines" - ) - fig.set_yaxis_title( - "Yield (%)" - if series_id not in ["EFFRVOL", "OBFRVOL"] - else "Billions in USD" - ) - - fig.set_title(ID_TO_NAME_FED[series_id]) - - if export or raw: - if not quantiles and not target: - if series_id != "EFFRVOL": - # Check whether it is a percentage, relevant for exporting - df_transformed = pd.DataFrame(df, columns=[series_id]) - else: - df_transformed = pd.DataFrame(df, columns=[series_id]) - else: - df_transformed = df - - if raw: - # was a -iloc so we need to flip the index as we use head - df_transformed = df_transformed.sort_index(ascending=False) - print_rich_table( - df_transformed, - headers=list(df_transformed.columns), - show_index=True, - title=ID_TO_NAME_FED[series_id], - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - df_transformed, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_iorb( - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Interest Rate on Reserve Balances. - - Parameters - ---------- - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = fred_model.get_iorb(start_date=start_date, end_date=end_date) - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title("Interest Rate on Reserve Balances") - - fig.add_scatter(x=df.index, y=df["IORB"], name="IORB", line_width=2, mode="lines") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "iorb", - pd.DataFrame(df, columns=["IORB"]), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_projection( - long_run: bool = False, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot the Federal Reserve's projection of the federal funds rate. - - Parameters - ---------- - long_run: str - Whether to plot the long run projection. - export: str - Export data to csv or excel file - raw : bool - Show raw data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data_series_df = fred_model.get_projection(long_run=long_run) - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title( - f"FOMC {'Long Run ' if long_run else ''}Summary of Economic Projections\n" - "for the Federal Funds Rate" - ) - - for legend, df in data_series_df.items(): - fig.add_scatter( - x=df.index, - y=df.values, - name=legend, - line_width=1.3 if legend != "Median" else 2, - line_dash="dash" if legend != "Median" else "solid", - mode="lines", - ) - - fig.update_layout( - legend=dict(x=0.01, y=0.01, xanchor="left", yanchor="bottom"), - xaxis=dict(tickformat="%Y-%m-%d"), - ) - - if raw: - print_rich_table( - data_series_df, - headers=list(data_series_df.columns), - show_index=True, - title=f"FOMC {'Long Run ' if long_run else ''}Summary of " - "Economic Projections for the Federal Funds Rate", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "projection", - data_series_df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_dwpcr( - parameter: str = "DPCREDIT", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Discount Window Primary Credit Rate. - - Parameters - ---------- - parameter: str - FRED ID of DWPCR data to plot, options: ['MPCREDIT', 'RIFSRPF02ND', 'WPCREDIT', 'DPCREDIT', 'RIFSRPF02NA'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.DWPCR_PARAMETER_TO_FRED_ID[parameter] - - df = fred_model.get_dwpcr( - parameter=parameter, start_date=start_date, end_date=end_date - ) - - fig = OpenBBFigure() - fig.set_title(f"Discount Window Primary Credit Rate {ID_TO_NAME_DWPCR[series_id]}") - - fig.add_scatter(x=df.index, y=df[series_id], name=series_id, mode="lines") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - pd.DataFrame(df, columns=[series_id]), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_ecb( - start_date: Optional[str] = None, - end_date: Optional[str] = None, - interest_type: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -): - """Plot the key ECB interest rates. - - The Governing Council of the ECB sets the key interest rates for the euro area: - - - The interest rate on the main refinancing operations (MRO), which provide - the bulk of liquidity to the banking system. - - The rate on the deposit facility, which banks may use to make overnight deposits with the Eurosystem. - - The rate on the marginal lending facility, which offers overnight credit to banks from the Eurosystem. - - Parameters - ---------- - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - interest_type: Optional[str] - The ability to decide what interest rate to plot - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = fred_model.get_ecb( - interest_type=interest_type, start_date=start_date, end_date=end_date - ) - - title = ( - f"ECB {interest_type.title()} Rate for Euro Area" - if interest_type - else "ECB Interest Rates for Euro Area" - ) - - fig = OpenBBFigure() - fig.set_title(title) - - for series in df: - fig.add_scatter(x=df.index, y=df[series], name=series.title(), mode="lines") - - fig.update_layout(legend=dict(x=0.01, y=0.01, xanchor="left", yanchor="bottom")) - - if raw: - # was a -iloc so we need to flip the index as we use head - df = df.sort_index(ascending=False) - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ecbdfr", - pd.DataFrame(df, columns=df.columns), - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_tmc( - parameter: str = "3_month", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot 10-Year Treasury Constant Maturity Minus Selected Treasury Constant Maturity data. - - Constant maturity is the theoretical value of a U.S. Treasury that is based on recent values of auctioned U.S. - Treasuries. The value is obtained by the U.S. Treasury on a daily basis through interpolation of the Treasury - yield curve which, in turn, is based on closing bid-yields of actively-traded Treasury securities. - - Parameters - ---------- - parameter: str - FRED ID of TMC data to plot, options: ['T10Y3M', 'T10Y3M'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.TMC_PARAMETER_TO_FRED_ID[parameter] - - df = fred_model.get_tmc( - parameter=parameter, start_date=start_date, end_date=end_date - ) - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title( - f"10-Year Treasury Constant Maturity Minus {ID_TO_NAME_TMC[series_id]}" - " Treasury Constant Maturity" - ) - - fig.add_scatter(x=df.index, y=df[series_id], name=series_id, mode="lines") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - pd.DataFrame(df, columns=[series_id]), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_ffrmc( - parameter: str = "10_year", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Selected Treasury Constant Maturity Minus Federal Funds Rate data. - - Constant maturity is the theoretical value of a U.S. Treasury that is based on recent values of auctioned U.S. - Treasuries. The value is obtained by the U.S. Treasury on a daily basis through interpolation of the Treasury - yield curve which, in turn, is based on closing bid-yields of actively-traded Treasury securities. - - Parameters - ---------- - parameter: str - FRED ID of FFRMC data to plot - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.FFRMC_PARAMETER_TO_FRED_ID[parameter] - - df = fred_model.get_ffrmc(parameter, start_date, end_date) - - fig = OpenBBFigure() - fig.set_title( - f"{ID_TO_NAME_FFRMC[series_id]} Treasury Constant Maturity Minus Federal Funds Rate" - ) - - fig.add_scatter(x=df.index, y=df[series_id], name=series_id, mode="lines") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - pd.DataFrame(df, columns=[series_id]), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def display_yield_curve( - date: str = "", - inflation_adjusted: bool = False, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Display yield curve based on US Treasury rates for a specified date. - - The graphic depiction of the relationship between the yield on bonds of the same credit quality but different - maturities is known as the yield curve. In the past, most market participants have constructed yield curves from - the observations of prices and yields in the Treasury market. Two reasons account for this tendency. First, - Treasury securities are viewed as free of default risk, and differences in creditworthiness do not affect yield - estimates. Second, as the most active bond market, the Treasury market offers the fewest problems of illiquidity - or infrequent trading. The key function of the Treasury yield curve is to serve as a benchmark for pricing bonds - and setting yields in other sectors of the debt market. - - It is clear that the market’s expectations of future rate changes are one important determinant of the - yield-curve shape. For example, a steeply upward-sloping curve may indicate market expectations of near-term Fed - tightening or of rising inflation. However, it may be too restrictive to assume that the yield differences across - bonds with different maturities only reflect the market’s rate expectations. The well-known pure expectations - hypothesis has such an extreme implication. The pure expectations hypothesis asserts that all government bonds - have the same near-term expected return (as the nominally riskless short-term bond) because the return-seeking - activity of risk-neutral traders removes all expected return differentials across bonds. - - Parameters - ---------- - date: str - Date to get curve for. If None, gets most recent date (format yyyy-mm-dd) - external_axes : bool, optional - Whether to return the figure object or not, by default False - raw : bool - Output only raw data - export : str - Export data to csv,json,xlsx or png,jpg,pdf,svg file - """ - rates, date_of_yield = fred_model.get_yield_curve(date, True, inflation_adjusted) - if rates.empty: - return console.print(f"[red]Yield data not found for {date_of_yield}.[/red]\n") - - fig = OpenBBFigure(xaxis_title="Maturity", yaxis_title="Yield (%)") - fig.set_title( - f"US {'Real' if inflation_adjusted else 'Nominal'} Yield Curve for {date_of_yield} " - ) - - fig.add_scatter(x=rates["Maturity"], y=rates["Rate"], name="Yield") - - if raw: - print_rich_table( - rates, - headers=list(rates.columns), - show_index=False, - title=f"United States {'Real' if inflation_adjusted else 'Nominal'} Yield Curve for {date_of_yield}", - floatfmt=".3f", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ycrv", - rates.set_index("Maturity"), - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_usrates( - parameter: str = "tbills", - maturity: str = "3_months", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -): - """Plot various treasury rates from the United States - - A Treasury Bill (T-Bill) is a short-term U.S. government debt obligation backed by the Treasury Department with a - maturity of one year or less. Treasury bills are usually sold in denominations of $1,000. However, some can reach - a maximum denomination of $5 million in non-competitive bids. These securities are widely regarded as low-risk - and secure investments. - - Yields on Treasury nominal securities at “constant maturity” are interpolated by the U.S. Treasury from the daily - yield curve for non-inflation-indexed Treasury securities. This curve, which relates the yield on a security to - its time to maturity, is based on the closing market bid yields on actively traded Treasury securities in the - over-the-counter market. These market yields are calculated from composites of quotations obtained by the Federal - Reserve Bank of New York. The constant maturity yield values are read from the yield curve at fixed maturities, - currently 1, 3, and 6 months and 1, 2, 3, 5, 7, 10, 20, and 30 years. This method provides a yield for a 10-year - maturity, for example, even if no outstanding security has exactly 10 years remaining to maturity. Similarly, - yields on inflation-indexed securities at “constant maturity” are interpolated from the daily yield curve for - Treasury inflation protected securities in the over-the-counter market. The inflation-indexed constant maturity - yields are read from this yield curve at fixed maturities, currently 5, 7, 10, 20, and 30 years. - - Parameters - ---------- - parameter: str - Either "tbills", "cmn", or "tips". - maturity: str - Depending on the chosen parameter, a set of maturities is available. - series_id: str - FRED ID of Treasury Bill Secondary Market Rate data to plot, options: ['TB3MS', 'DTB4WK', 'DTB1YR', 'DTB6'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.USARATES_TO_FRED_ID[maturity][parameter] - df = fred_model.get_usrates( - parameter=parameter, maturity=maturity, start_date=start_date, end_date=end_date - ) - - if df.empty: - return console.print(f"[red]No data found for {parameter} {maturity}[/red]\n") - - title_dict = { - "tbills": "Treasury Bill Secondary Market Rate, Discount Basis", - "cmn": "Treasury Constant Maturity Nominal Market Yield", - "tips": "Yields on Treasury inflation protected securities (TIPS)" - " adjusted to constant maturities", - } - title = f"{maturity.replace('_', ' ').title()} {title_dict.get(parameter, '')}" - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title(title) - - fig.add_scatter(x=df.index, y=df[series_id], name="Yield") - - if raw: - # was a -iloc so we need to flip the index as we use head - df = pd.DataFrame(df, columns=[series_id]) - df = df.sort_index(ascending=False) - print_rich_table( - df, - title=title, - show_index=True, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - pd.DataFrame(df, columns=[series_id]), - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_tbffr( - parameter: str = "TB3SMFFM", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot Selected Treasury Bill Minus Federal Funds Rate data. - - Parameters - ---------- - parameter: str - FRED ID of TBFFR data to plot, options: ['TB3SMFFM', 'TB6SMFFM'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - series_id = fred_model.TBFFR_PARAMETER_TO_FRED_ID[parameter] - - df = fred_model.get_tbffr( - parameter=parameter, start_date=start_date, end_date=end_date - ) - - if df.empty: - return console.print( - f"[red]No data found for {ID_TO_NAME_TBFFR[series_id]}[/red]" - ) - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title( - f"{ID_TO_NAME_TBFFR[series_id]} Treasury Bill Minus Federal Funds Rate" - ) - - fig.add_scatter(x=df.index, y=df[series_id], name="Yield") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - series_id, - pd.DataFrame(df, columns=["TBFFR"]), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_icebofa( - data_type: str = "yield", - category: str = "all", - area: str = "us", - grade: str = "non_sovereign", - options: bool = False, - description: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -): - """Plot ICE BofA US Corporate Bond Index data. - - Parameters - ---------- - data_type: str - The type of data you want to see, either "yield", "yield_to_worst", "total_return", or "spread" - category: str - The type of category you want to see, either "all", "duration", "eur" or "usd". - area: str - The type of area you want to see, either "asia", "emea", "eu", "ex_g10", "latin_america" or "us" - grade: str - The type of grade you want to see, either "a", "aa", "aaa", "b", "bb", "bbb", "ccc", "crossover", - "high_grade", "high_yield", "non_financial", "non_sovereign", "private_sector", "public_sector" - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - raw: bool - Show raw data - export: str - Export data to csv or excel file - sheet_name: str - Name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if data_type == "total_return": - units = "index" - elif data_type in ["yield", "yield_to_worst", "spread"]: - units = "percent" - - series = pd.read_csv(ice_bofa_path) - - if options: - return print_rich_table( - series.drop( - ["Frequency", "Units", "Asset Class", "FRED Series ID", "Description"], - axis=1, - )[ - series["Type"].isin( - ["yield", "yield_to_worst", "total_return"] - if data_type != "spread" - else ["spread"] - ) - ], - export=bool(export), - ) - - df = fred_model.get_icebofa( - data_type=data_type, - category=category, - area=area, - grade=grade, - start_date=start_date, - end_date=end_date, - ) - - if df.empty: - return console.print() - - title = ( - "" - if df.empty - else "ICE BofA Bond Benchmark Indices" if len(df.columns) > 1 else df.columns[0] - ) - - fig = OpenBBFigure(yaxis_title="Yield (%)" if units == "percent" else "Index") - fig.set_title(title) - - for column in df.columns: - fig.add_scatter( - x=df.index, - y=df[column].values, - name=column, - showlegend=True, - connectgaps=True, - ) - - if raw: - # was a -iloc so we need to flip the index as we use head - df = df.sort_index(ascending=False) - print_rich_table( - df, - title=title, - show_index=True, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - if description and title: - for index_title in df.columns: - description_text = series[series["Title"] == index_title][ - "Description" - ].values[0] - console.print(f"\n[bold]{title}[/bold]") - console.print(description_text) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ICEBOFA", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_moody( - data_type: str = "aaa", - spread: Optional[str] = None, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -): - """Plot Moody Corporate Bond Index data - Parameters - ---------- - data_type: str - The type of data you want to see, either "aaa" or "baa" - spread: Optional[str] - Whether you want to show the spread for treasury or fed_funds - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - raw: bool - Show raw data - export: str - Export data to csv or excel file - sheet_name: str - Name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - name = fred_model.MOODY_TO_OPTIONS["Type"][data_type][ - spread if spread else "index" - ]["name"] - - df = fred_model.get_moody( - data_type=data_type, spread=spread, start_date=start_date, end_date=end_date - ) - - if df.empty: - return console.print(f"[bold red]No data found for {name}[/bold red]") - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title(name) - - for series in df.columns: - fig.add_scatter(x=df.index, y=df[series], name=series) - - if raw: - # was a -iloc so we need to flip the index as we use head - df = df.sort_index(ascending=False) - print_rich_table( - df, - title=name, - show_index=True, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "MOODY", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_cp( - maturity: str = "30d", - category: str = "financial", - grade: str = "aa", - options: bool = False, - description: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -): - """Plot Commercial Paper - - Parameters - ---------- - maturity: str - The maturity you want to see, either "overnight", "7d", "15d", "30d", "60d" or "90d" - category: str - The category you want to see, either "asset_backed", "financial" or "non_financial" - grade: str - The type of grade you want to see, either "a2_p2" or "aa" - description: bool - Whether you wish to obtain a description of the data. - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - raw: bool - Show raw data - export: str - Export data to csv or excel file - sheet_name: str - Name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if grade == "a2_p2" and category != "non_financial": - console.print( - "Setting category to 'non_financial' given " - f"the chosen {grade} grade only has data for 'non_financial'." - ) - category = "non_financial" - - series = pd.read_csv(commercial_paper_path) - - if options: - return print_rich_table( - series.drop(["Description", "FRED Series ID"], axis=1), export=bool(export) - ) - - df = fred_model.get_cp( - maturity=maturity, - category=category, - grade=grade, - start_date=start_date, - end_date=end_date, - ) - - if df.empty: - return console.print() - - title = "Commercial Paper Interest Rates" if len(df.columns) > 1 else df.columns[0] - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title(title) - - for column in df.columns: - fig.add_scatter( - x=df.index, y=df[column].values, name=column, showlegend=len(df.columns) > 1 - ) - - if raw: - # was a -iloc so we need to flip the index as we use head - df = df.sort_index(ascending=False) - print_rich_table( - df, - title=title, - show_index=True, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - if description: - message = ( - "To calculate CP interest rate indexes, the Federal Reserve Board uses DTCC's data for certain trades" - " to estimate a relation between interest rates on the traded securities and their maturities. In this" - " calculation, the trades represent sales of CP by dealers or direct issuers to investors (that is," - " the offer side) and are weighted according to the face value of the CP so that larger trades have" - " a greater effect on the resulting index. With the relation between interest rates and maturities" - " established, the reported interest rates represent the estimated interest rates for the specified" - " maturities.\n\n" - "Interest rates calculated through the process described above are a statistical aggregation" - " of numerous data reflecting many trades for different issuers, maturities, and so forth." - " Accordingly, the reported interest rates purport to reflect activity in certain segments" - " of the market, but they may not equal interest rates for any specific trade. As with other" - " statistical processes, this one is designed to minimize the difference between the interest" - " rates at which actual trades occur and the estimated interest rates.\n\n" - "CP trades included in the calculation are chosen according to the specifications listed in the" - " table below. Data to assess CP trades relative to these criteria are updated daily from numerous" - " publicly available sources. Standard Industrial Classification (SIC) code classifications are" - " taken from the Securities and Exchange Commission (SEC) Directory of Companies Required to File" - ' Annual Reports with the SEC. When an issuer"s primary SIC code is not reported in the SEC' - ' directory, the primary SIC code reported in the issuer"s financial reports is used; otherwise,' - " SIC codes are determined upon consultation with the Office of Management and Budget's Standard " - "Industrial Classification Manual or its Supplement.\n\n" - "For a discussion of econometric techniques for fitting the term structure of interest rates, including" - ' bibliographic information, see, for example, William S. Cleveland, 1979, "Robust Locally' - ' Weighted Regression and Smoothing Scatterplots," Journal of the American Statistical Association,' - " 74, 829-36, or William S. Cleveland, Susan J. Devlin, and Eric Grosse, 1988," - ' "Regression by Local Fitting," Journal of Econometrics, 37, 87-114.\n\n' - "Source: https://www.federalreserve.gov/releases/cp/about.htm" - ) - console.print(message) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "CP", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_spot( - maturity: List = ["10y"], - category: List = ["spot_rate"], - description: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -): - """Plot Spot Rates for a given maturity - - The spot rate for any maturity is the yield on a bond that provides - a single payment at that maturity. This is a zero coupon bond. Because each - spot rate pertains to a single cashflow, it is the relevant interest rate - concept for discounting a pension liability at the same maturity. - - Parameters - ---------- - maturity: str - The maturity you want to see (ranging from '1y' to '100y') - category: list - The category you want to see ('par_yield' and/or 'spot_rate') - description: bool - Whether you wish to obtain a description of the data. - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - raw: bool - Show raw data - export: str - Export data to csv or excel file - sheet_name: str - Name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = fred_model.get_spot( - maturity=maturity, category=category, start_date=start_date, end_date=end_date - ) - - if df.empty: - return console.print( - f"[bold red]No data found for maturity {maturity} and category {category}[/bold red]" - ) - - title = ( - "High Quality Market (HQM) Corporate Bond Rates" - if len(df.columns) > 1 - else df.columns[0] - ) - - fig = OpenBBFigure(yaxis_title="Yield (%)") - fig.set_title(title) - - for column in df.columns: - fig.add_scatter( - x=df.index, y=df[column].values, name=column, showlegend=len(df.columns) > 1 - ) - - if raw: - # was a -iloc so we need to flip the index as we use head - df = df.sort_index(ascending=False) - print_rich_table( - df, - title=title, - show_index=True, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - if description: - header1 = "T-Year High Quality Market (HQM) Corporate Bond Spot Rate" - message1 = ( - "The spot rate for T-Year High Quality Market (HQM) Corporate Bond Spot Rate" - " is defined as the yield on a bond that gives a single payment after T year." - " This is called a zero coupon bond. Because high quality zero coupon bonds are" - " not generally available, the HQM methodology computes the spot rates so as to" - " make them consistent with the yields on other high quality bonds. The HQM" - " yield curve uses data from a set of high quality corporate bonds rated AAA" - ", AA, or A that accurately represent the high quality corporate bond market." - ) - header2 = "T-Year High Quality Market (HQM) Corporate Bond Par Yield" - message2 = ( - "The par yield for T-Year High Quality Market (HQM) Corporate Bond Par" - " Yield is the coupon rate at which bond prices are zero." - ) - console.print(f"\n[bold]{header1}[/bold]") - console.print(message1) - console.print(f"\n[bold]{header2}[/bold]") - console.print(message2) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "SPOT", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_hqm( - date: Optional[str] = None, - par: bool = False, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot the HQM yield curve - - The HQM yield curve represents the high quality corporate bond market, i.e., - corporate bonds rated AAA, AA, or A. The HQM curve contains two regression terms. These - terms are adjustment factors that blend AAA, AA, and A bonds into a single HQM yield curve - that is the market-weighted average (MWA) quality of high quality bonds. - - Parameters - ---------- - date: str - The date of the yield curve you wish to plot - par: bool - Whether you wish to plot the par yield curve as well - raw: bool - Show raw data - export: str - Export data to csv or excel file - sheet_name: str - Name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data_types = ["spot", "par"] if par else ["spot"] - - df, date_of_yield = fred_model.get_hqm(date=date, par=par) - - if df.empty: - return console.print() - - fig = OpenBBFigure(xaxis_title="Maturity", yaxis_title="Yield (%)") - fig.set_title(f"Spot{'and Par' if par else ''} Yield Curve for {date_of_yield}") - - for types in data_types: - fig.add_scatter(x=df.index, y=df[types], name=f"{types.title()} Yield") - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - index_name="Maturity", - title=f"Spot {'and Par' if par else ''} Yield Curve for {date_of_yield}", - floatfmt=".3f", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cycrv", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/fixedincome/ice_bofa_indices.csv b/openbb_terminal/fixedincome/ice_bofa_indices.csv deleted file mode 100644 index 1955ef70e3d..00000000000 --- a/openbb_terminal/fixedincome/ice_bofa_indices.csv +++ /dev/null @@ -1,983 +0,0 @@ -Type,Units,Frequency,Asset Class,Category,Area,Grade,FRED Series ID,Title,Description -total_return,index,daily,bonds,duration,us,non_sovereign,BAMLCC1A013YTRIV,ICE BofA 1-3 Year US Corporate Index Total Return Index Value,"This data represents the ICE BofA 1-3 Year US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of less than 3 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,duration,us,non_sovereign,BAMLC1A0C13YEY,ICE BofA 1-3 Year US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA 1-3 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of less than 3 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,duration,us,non_sovereign,BAMLC1A0C13Y,ICE BofA 1-3 Year US Corporate Index Option-Adjusted Spread,"The ICE BofA Option-Adjusted Spreads (OASs) are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. The US Corporate 1-3 Year OAS is a subset of the ICE BofA US Corporate Master OAS, BAMLC0A0CM. This subset includes all securities with a remaining term to maturity of less than 3 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,duration,us,non_sovereign,BAMLC1A0C13YSYTW,ICE BofA 1-3 Year US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA 1-3 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of less than 3 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,duration,us,non_sovereign,BAMLCC2A035YTRIV,ICE BofA 3-5 Year US Corporate Index Total Return Index Value,"This data represents the ICE BofA 3-5 Year US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 3 years and less than 5 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,duration,us,non_sovereign,BAMLC2A0C35YEY,ICE BofA 3-5 Year US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA 3-5 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 3 years and less than 5 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,duration,us,non_sovereign,BAMLC2A0C35Y,ICE BofA 3-5 Year US Corporate Index Option-Adjusted Spread,"The ICE BofA Option-Adjusted Spreads (OASs) are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. The US Corporate 3-5 Year OAS is a subset of the ICE BofA US Corporate Master OAS, BAMLC0A0CM. This subset includes all securities with a remaining term to maturity of greater than or equal to 3 years and less than 5 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,duration,us,non_sovereign,BAMLC2A0C35YSYTW,ICE BofA 3-5 Year US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA 3-5 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 3 years and less than 5 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -total_return,index,daily,bonds,duration,us,non_sovereign,BAMLCC3A057YTRIV,ICE BofA 5-7 Year US Corporate Index Total Return Index Value,"This data represents the ICE BofA 5-7 Year US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 5 years and less than 7 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -yield,percent,daily,bonds,duration,us,non_sovereign,BAMLC3A0C57YEY,ICE BofA 5-7 Year US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA 5-7 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 5 years and less than 7 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -spread,percent,daily,bonds,duration,us,non_sovereign,BAMLC3A0C57Y,ICE BofA 5-7 Year US Corporate Index Option-Adjusted Spread,"The ICE BofA Option-Adjusted Spreads (OASs) are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. The US Corporate 5-7 Year OAS is a subset of the ICE BofA US Corporate Master OAS, BAMLC0A0CM. This subset includes all securities with a remaining term to maturity of greater than or equal to 5 years and less than 7 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,duration,us,non_sovereign,BAMLC3A0C57YSYTW,ICE BofA 5-7 Year US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA 5-7 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 5 years and less than 7 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,duration,us,non_sovereign,BAMLCC4A0710YTRIV,ICE BofA 7-10 Year US Corporate Index Total Return Index Value,"This data represents the ICE BofA 7-10 Year US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 7 years and less than 10 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -yield,percent,daily,bonds,duration,us,non_sovereign,BAMLC4A0C710YEY,ICE BofA 7-10 Year US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA 7-10 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 7 years and less than 10 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,duration,us,non_sovereign,BAMLC4A0C710Y,ICE BofA 7-10 Year US Corporate Index Option-Adjusted Spread,"The ICE BofA Option-Adjusted Spreads (OASs) are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. The US Corporate 7-10 Year OAS is a subset of the ICE BofA US Corporate Master OAS, BAMLC0A0CM. This subset includes all securities with a remaining term to maturity of greater than or equal to 7 years and less than 10 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,duration,us,non_sovereign,BAMLC4A0C710YSYTW,ICE BofA 7-10 Year US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA 7-10 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 7 years and less than 10 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,duration,us,non_sovereign,BAMLCC7A01015YTRIV,ICE BofA 10-15 Year US Corporate Index Total Return Index Value,"This data represents the ICE BofA 10-15 Year US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 10 years and less than 15 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,duration,us,non_sovereign,BAMLC7A0C1015YEY,ICE BofA 10-15 Year US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA 10-15 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 10 years and less than 15 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,duration,us,non_sovereign,BAMLC7A0C1015Y,ICE BofA 10-15 Year US Corporate Index Option-Adjusted Spread,"The ICE BofA Option-Adjusted Spreads (OASs) are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. The US Corporate 10-15 Year OAS is a subset of the ICE BofA US Corporate Master OAS, BAMLC0A0CM. This subset includes all securities with a remaining term to maturity of greater than or equal to 10 years and less than 15 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,duration,us,non_sovereign,BAMLC7A0C1015YSYTW,ICE BofA 10-15 Year US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA 10-15 Year US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 10 years and less than 15 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,duration,us,non_sovereign,BAMLCC8A015PYTRIV,ICE BofA 15+ Year US Corporate Index Total Return Index Value,"This data represents the ICE BofA US Corporate 15+ Year Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 15 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,duration,us,non_sovereign,BAMLC8A0C15PYEY,ICE BofA 15+ Year US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA US Corporate 15+ Year Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 15 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,duration,us,non_sovereign,BAMLC8A0C15PY,ICE BofA 15+ Year US Corporate Index Option-Adjusted Spread,"The ICE BofA Option-Adjusted Spreads (OASs) are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. The US Corporate 15+ Year OAS is a subset of the ICE BofA US Corporate Master OAS, BAMLC0A0CM. This subset includes all securities with a remaining term to maturity of greater than or equal to 15 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,duration,us,non_sovereign,BAMLC8A0C15PYSYTW,ICE BofA 15+ Year US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA US Corporate 15+ Year Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a remaining term to maturity of greater than or equal to 15 years. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,us,non_sovereign,BAMLCC0A0CMTRIV,ICE BofA US Corporate Index Total Return Index Value,"This data represents the ICE BofA US Corporate Index value, which tracks the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. To qualify for inclusion in the index, securities must have an investment grade rating (based on an average of Moody's, S&P, and Fitch) and an investment grade rated country of risk (based on an average of Moody's, S&P, and Fitch foreign currency long term sovereign debt ratings). Each security must have greater than 1 year of remaining maturity, a fixed coupon schedule, and a minimum amount outstanding of $250 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and US domestic bond markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. DRD-eligible and defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,us,non_sovereign,BAMLC0A0CMEY,ICE BofA US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA US Corporate Index, which tracks the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. To qualify for inclusion in the index, securities must have an investment grade rating (based on an average of Moody's, S&P, and Fitch) and an investment grade rated country of risk (based on an average of Moody's, S&P, and Fitch foreign currency long term sovereign debt ratings). Each security must have greater than 1 year of remaining maturity, a fixed coupon schedule, and a minimum amount outstanding of $250 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and US domestic bond markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. DRD-eligible and defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,us,non_sovereign,BAMLC0A0CM,ICE BofA US Corporate Index Option-Adjusted Spread,"The ICE BofA Option-Adjusted Spreads (OASs) are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. The Corporate Master OAS uses an index of bonds that are considered investment grade (those rated BBB or better). When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -This data represents the ICE BofA US Corporate Index value, which tracks the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. To qualify for inclusion in the index, securities must have an investment grade rating (based on an average of Moody's, S&P, and Fitch) and an investment grade rated country of risk (based on an average of Moody's, S&P, and Fitch foreign currency long term sovereign debt ratings). Each security must have greater than 1 year of remaining maturity, a fixed coupon schedule, and a minimum amount outstanding of $250 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and US -domestic bond markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. DRD-eligible and defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month -remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,us,non_sovereign,BAMLC0A0CMSYTW,ICE BofA US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA US Corporate Index, which tracks the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. To qualify for inclusion in the index, securities must have an investment grade rating (based on an average of Moody's, S&P, and Fitch) and an investment grade rated country of risk (based on an average of Moody's, S&P, and Fitch foreign currency long term sovereign debt ratings). Each security must have greater than 1 year of remaining maturity, a fixed coupon schedule, and a minimum amount outstanding of $250 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and US domestic bond markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. DRD-eligible and defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: - -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,us,high_yield,BAMLHYH0A0HYM2TRIV,ICE BofA US High Yield Index Total Return Index Value,"This data represents the ICE BofA US High Yield Index value, which tracks the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. To qualify for inclusion in the index, securities must have a below investment grade rating (based on an average of Moody's, S&P, and Fitch) and an investment grade rated country of risk (based on an average of Moody's, S&P, and Fitch foreign currency long term sovereign debt ratings). Each security must have greater than 1 year of remaining maturity, a fixed coupon schedule, and a minimum amount outstanding of $100 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and US domestic bond markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. DRD-eligible and defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,us,high_yield,BAMLH0A0HYM2EY,ICE BofA US High Yield Index Effective Yield,"This data represents the effective yield of the ICE BofA US High Yield Index, which tracks the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. To qualify for inclusion in the index, securities must have a below investment grade rating (based on an average of Moody's, S&P, and Fitch) and an investment grade rated country of risk (based on an average of Moody's, S&P, and Fitch foreign currency long term sovereign debt ratings). Each security must have greater than 1 year of remaining maturity, a fixed coupon schedule, and a minimum amount outstanding of $100 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and US domestic bond markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. DRD-eligible and defaulted securities are excluded from the Index. ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,us,high_yield,BAMLH0A0HYM2,ICE BofA US High Yield Index Option-Adjusted Spread,"The ICE BofA Option-Adjusted Spreads (OASs) are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. The ICE BofA High Yield Master II OAS uses an index of bonds that are below investment grade (those rated BB or below). -This data represents the ICE BofA US High Yield Index value, which tracks the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. To qualify for inclusion in the index, securities must have a below investment grade rating (based on an average of Moody's, S&P, and Fitch) and an investment grade rated country of risk (based on an average of Moody's, S&P, and Fitch foreign currency long term sovereign debt ratings). Each security must have greater than 1 year of remaining maturity, a fixed coupon schedule, and a minimum amount outstanding of $100 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and US domestic bond markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. DRD-eligible and defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,us,high_yield,BAMLH0A0HYM2SYTW,ICE BofA US High Yield Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA US High Yield Index, which tracks the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. To qualify for inclusion in the index, securities must have a below investment grade rating (based on an average of Moody's, S&P, and Fitch) and an investment grade rated country of risk (based on an average of Moody's, S&P, and Fitch foreign currency long term sovereign debt ratings). Each security must have greater than 1 year of remaining maturity, a fixed coupon schedule, and a minimum amount outstanding of $100 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and US domestic bond markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. DRD-eligible and defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payment frequencies of annual, semi-annual, quarterly, and monthly. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,eur,eu,high_yield,BAMLHE00EHYITRIV,ICE BofA Euro High Yield Index Total Return Index Value,"This data represents the ICE BofA Euro High Yield Index value, which tracks the performance of Euro denominated below investment grade corporate debt publicly issued in the euro domestic or eurobond markets. Qualifying securities must have a below investment grade rating (based on an average of Moody's, S&P, and Fitch). Qualifying securities must have at least one year remaining term to maturity, a fixed coupon schedule, and a minimum amount outstanding of Euro 100 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and euro domestic markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. Defaulted, warrant-bearing and euro legacy currency securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until -the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,eur,eu,high_yield,BAMLHE00EHYIEY,ICE BofA Euro High Yield Index Effective Yield,"This data represents the effective yield of the ICE BofA Euro High Yield Index tracks the performance of Euro denominated below investment grade corporate debt publicly issued in the euro domestic or eurobond markets. Qualifying securities must have a below investment grade rating (based on an average of Moody's, S&P, and Fitch). Qualifying securities must have at least one year remaining term to maturity, a fixed coupon schedule, and a minimum amount outstanding of Euro 100 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and euro domestic markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. Defaulted, warrant-bearing and euro legacy currency securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until -the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,eur,eu,high_yield,BAMLHE00EHYIOAS,ICE BofA Euro High Yield Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) of the ICE BofA Euro High Yield Index tracks the performance of Euro denominated below investment grade corporate debt publicly issued in the euro domestic or eurobond markets. Qualifying securities must have a below investment grade rating (based on an average of Moody's, S&P, and Fitch). Qualifying securities must have at least one year remaining term to maturity, a fixed coupon schedule, and a minimum amount outstanding of Euro 100 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and euro domestic markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. Defaulted, warrant-bearing and euro legacy currency securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until -the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,eur,eu,high_yield,BAMLHE00EHYISYTW,ICE BofA Euro High Yield Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Euro High Yield Index tracks the performance of Euro denominated below investment grade corporate debt publicly issued in the euro domestic or eurobond markets. Qualifying securities must have a below investment grade rating (based on an average of Moody's, S&P, and Fitch). Qualifying securities must have at least one year remaining term to maturity, a fixed coupon schedule, and a minimum amount outstanding of Euro 100 million. Original issue zero coupon bonds, ""global"" securities (debt issued simultaneously in the eurobond and euro domestic markets), 144a securities and pay-in-kind securities, including toggle notes, qualify for inclusion in the Index. Callable perpetual securities qualify provided they are at least one year from the first call date. Fixed-to-floating rate securities also qualify provided they are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security. Defaulted, warrant-bearing and euro legacy currency securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of U.S. mortgage pass-throughs and U.S. structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for U.S. mortgage pass-through and U.S. structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until -the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payment frequencies of annual, semi-annual, quarterly, and monthly. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,ex_g10,non_sovereign,BAMLEMCBPITRIV,ICE BofA Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA Emerging Markets Corporate Plus Index tracks the performance of US dollar (USD) and Euro denominated emerging markets non-sovereign debt publicly issued within the major domestic and Eurobond markets. To qualify for inclusion in the index, the issuer of debt must have risk exposure to countries other than members of the FX G10 (US, Japan, New Zealand, Australia, Canada, Sweden, UK, Switzerland, Norway, and Euro Currency Members), all Western European countries, and territories of the US and Western European countries. Each security must also be denominated in USD or Euro with a time to maturity greater than 1 year and have a fixed coupon. For inclusion in the index, investment grade rated bonds of qualifying issuers must have at least 250 million (Euro or USD) in outstanding face value, and below investment grade rated bonds must have at least 100 million (Euro or USD) in outstanding face value. The index includes corporate and quasi-government debt of qualifying countries, but excludes sovereign and supranational debt. Other types of securities acceptable for inclusion in this index are: original issue zero coupon bonds, ""global"" securities (debt issued in the US domestic bond markets as well the Eurobond Market simultaneously), 144a securities, pay-in-kind securities (includes toggle notes), callable perpetual securities (qualify if they are at least one year from the first call date), fixed-to-floating rate securities (qualify if the securities are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security). Defaulted securities are excluded from the Index. -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of US mortgage pass-throughs and US structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for US mortgage pass-through and US structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until -the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use." -yield,percent,daily,bonds,all,ex_g10,non_sovereign,BAMLEMCBPIEY,ICE BofA Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Emerging Markets Corporate Plus Index, which tracks the performance of US dollar (USD) and Euro denominated emerging markets non-sovereign debt publicly issued within the major domestic and Eurobond markets. To qualify for inclusion in the index, the issuer of debt must have risk exposure to countries other than members of the FX G10 (US, Japan, New Zealand, Australia, Canada, Sweden, UK, Switzerland, Norway, and Euro Currency Members), all Western European countries, and territories of the US and Western European countries. Each security must also be denominated in USD or Euro with a time to maturity greater than 1 year and have a fixed coupon. For inclusion in the index, investment grade rated bonds of qualifying issuers must have at least 250 million (Euro or USD) in outstanding face value, and below investment grade rated bonds must have at least 100 million (Euro or USD) in outstanding face value. The index includes corporate and quasi-government debt of qualifying countries, but excludes sovereign and supranational debt. Other types of securities acceptable for inclusion in this index are: original issue zero coupon bonds, ""global"" securities (debt issued in the US domestic bond markets as well the Eurobond Market simultaneously), 144a securities, pay-in-kind securities (includes toggle notes), callable perpetual securities (qualify if they are at least one year from the first call date), fixed-to-floating rate securities (qualify if the securities are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security). Defaulted securities are excluded from the Index. -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of US mortgage pass-throughs and US structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for US mortgage pass-through and US structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,ex_g10,non_sovereign,BAMLEMCBPIOAS,ICE BofA Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Emerging Markets Corporate Plus Index, which tracks the performance of US dollar (USD) and Euro denominated emerging markets non-sovereign debt publicly issued within the major domestic and Eurobond markets. To qualify for inclusion in the index, the issuer of debt must have risk exposure to countries other than members of the FX G10 (US, Japan, New Zealand, Australia, Canada, Sweden, UK, Switzerland, Norway, and Euro Currency Members), all Western European countries, and territories of the US and Western European countries. Each security must also be denominated in USD or Euro with a time to maturity greater than 1 year and have a fixed coupon. For inclusion in the index, investment grade rated bonds of qualifying issuers must have at least 250 million (Euro or USD) in outstanding face value, and below investment grade rated bonds must have at least 100 million (Euro or USD) in outstanding face value. The index includes corporate and quasi-government debt of qualifying countries, but excludes sovereign and supranational debt. Other types of securities acceptable for inclusion in this index are: original issue zero coupon bonds, ""global"" securities (debt issued in the US domestic bond markets as well the Eurobond Market simultaneously), 144a securities, pay-in-kind securities (includes toggle notes), callable perpetual securities (qualify if they are at least one year from the first call date), fixed-to-floating rate securities (qualify if the securities are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security). Defaulted securities are excluded from the Index. -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of US mortgage pass-throughs and US structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for US mortgage pass-through and US structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until -the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,non_sovereign,BAMLEMCBPISYTW,ICE BofA Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Emerging Markets Corporate Plus Index, which tracks the performance of US dollar (USD) and Euro denominated emerging markets non-sovereign debt publicly issued within the major domestic and Eurobond markets. To qualify for inclusion in the index, the issuer of debt must have risk exposure to countries other than members of the FX G10 (US, Japan, New Zealand, Australia, Canada, Sweden, UK, Switzerland, Norway, and Euro Currency Members), all Western European countries, and territories of the US and Western European countries. Each security must also be denominated in USD or Euro with a time to maturity greater than 1 year and have a fixed coupon. For inclusion in the index, investment grade rated bonds of qualifying issuers must have at least 250 million (Euro or USD) in outstanding face value, and below investment grade rated bonds must have at least 100 million (Euro or USD) in outstanding face value. The index includes corporate and quasi-government debt of qualifying countries, but excludes sovereign and supranational debt. Other types of securities acceptable for inclusion in this index are: original issue zero coupon bonds, ""global"" securities (debt issued in the US domestic bond markets as well the Eurobond Market simultaneously), 144a securities, pay-in-kind securities (includes toggle notes), callable perpetual securities (qualify if they are at least one year from the first call date), fixed-to-floating rate securities (qualify if the securities are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security). Defaulted securities are excluded from the Index. -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding. With the exception of US mortgage pass-throughs and US structured products (ABS, CMBS and CMOs), accrued interest is calculated assuming next-day settlement. Accrued interest for US mortgage pass-through and US structured products is calculated assuming same-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payment frequencies of annual, semi-annual, quarterly, and monthly. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -total_return,index,daily,bonds,all,ex_g10,private_sector,BAMLEMFSFCRPITRIV,ICE BofA Private Sector Financial Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA Financial Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes all Financial securities except the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,ex_g10,private_sector,BAMLEMFSFCRPIEY,ICE BofA Private Sector Financial Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Financial Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes all Financial securities except the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,ex_g10,private_sector,BAMLEMFSFCRPIOAS,ICE BofA Private Sector Financial Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Financial Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes all Financial securities except the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,private_sector,BAMLEMFSFCRPISYTW,ICE BofA Private Sector Financial Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Financial Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes all Financial securities except the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,ex_g10,non_sovereign,BAMLEMUBCRPIUSTRIV,ICE BofA US Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA US Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities denominated in US Dollars. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,ex_g10,non_sovereign,BAMLEMUBCRPIUSEY,ICE BofA US Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA US Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities denominated in US Dollars. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,ex_g10,non_sovereign,BAMLEMUBCRPIUSOAS,ICE BofA US Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA US Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities denominated in US Dollars. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,ex_g10,non_sovereign,BAMLEMUBCRPIUSSYTW,ICE BofA US Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA US Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities denominated in US Dollars. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,eur,ex_g10,non_sovereign,BAMLEMEBCRPIETRIV,ICE BofA Euro Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA Euro Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities denominated in Euros. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,eur,ex_g10,non_sovereign,BAMLEMEBCRPIEEY,ICE BofA Euro Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Euro Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities denominated in Euros. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,eur,ex_g10,non_sovereign,BAMLEMEBCRPIEOAS,ICE BofA Euro Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Euro Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities denominated in Euros. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,eur,ex_g10,non_sovereign,BAMLEMEBCRPIESYTW,ICE BofA Euro Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Euro Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities denominated in Euros. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,ex_g10,non_sovereign,BAMLEMCLLCRPIUSTRIV,ICE BofA US Emerging Markets Liquid Corporate Plus Index Total Return Index Value,"The ICE BofA US Emerging Markets Liquid Corporate Plus Index tracks the performance of US dollar (USD) denominated emerging markets non-sovereign debt publicly issued in the major domestic and Eurobond markets. To qualify for inclusion in the index, the issuer of debt must have risk exposure to countries other than members of the FX G10 (US, Japan, New Zealand, Australia, Canada, Sweden, UK, Switzerland, Norway, and Euro Currency Members), all Western European countries, and territories of the US and Western European countries. Each security must also be denominated in USD with a time to maturity greater than 1 year until final maturity and have a fixed coupon. For inclusion in the index, investment grade rated bonds of qualifying issuers must have at least $500 million in outstanding face value, and below investment grade rated bonds must have at least $300 million in outstanding face value. The index includes corporate and quasi-government debt of qualifying countries, but excludes sovereign and supranational debt. Other types of securities acceptable for inclusion in this index are: original issue zero coupon bonds, ""global"" securities (debt issued in the US domestic bond markets as well the Eurobond Market simultaneously), 144a securities, pay-in-kind securities (includes toggle notes), callable perpetual securities (qualify if they are at least one year from the first call date), fixed-t-floating rate securities (qualify if the securities are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security). Defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding times the market price plus accrued interest, subject to a 10% country of risk cap and a 2% issuer cap. Countries and issuers that exceed the limits are reduced to 10% and 2%, respectively, and the face value of each of their bonds is adjusted on a pro-rata basis. Similarly, the face values of bonds of all other countries and issuers that fall below their respective caps are increased on a pro-rata basis. In the event there are fewer than 10 countries in the Index, or fewer than 50 issuers, each is equally weighted and the face values of their respective bonds are increased or decreased on a pro-rata basis. Accrued interest is calculated assuming next-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,ex_g10,non_sovereign,BAMLEMCLLCRPIUSEY,ICE BofA US Emerging Markets Liquid Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA US Emerging Markets Liquid Corporate Plus Index which tracks the performance of US dollar (USD) denominated emerging markets non-sovereign debt publicly issued in the major domestic and Eurobond markets. To qualify for inclusion in the index, the issuer of debt must have risk exposure to countries other than members of the FX G10 (US, Japan, New Zealand, Australia, Canada, Sweden, UK, Switzerland, Norway, and Euro Currency Members), all Western European countries, and territories of the US and Western European countries. Each security must also be denominated in USD with a time to maturity greater than 1 year until final maturity and have a fixed coupon. For inclusion in the index, investment grade rated bonds of qualifying issuers must have at least $500 million in outstanding face value, and below investment grade rated bonds must have at least $300 million in outstanding face value. The index includes corporate and quasi-government debt of qualifying countries, but excludes sovereign and supranational debt. Other types of securities acceptable for inclusion in this index are: original issue zero coupon bonds, ""global"" securities (debt issued in the US domestic bond markets as well the Eurobond Market simultaneously), 144a securities, pay-in-kind securities (includes toggle notes), callable perpetual securities (qualify if they are at least one year from the first call date), fixed-t-floating rate securities (qualify if the securities are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security). Defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding times the market price plus accrued interest, subject to a 10% country of risk cap and a 2% issuer cap. Countries and issuers that exceed the limits are reduced to 10% and 2%, respectively, and the face value of each of their bonds is adjusted on a pro-rata basis. Similarly, the face values of bonds of all other countries and issuers that fall below their respective caps are increased on a pro-rata basis. In the event there are fewer than 10 countries in the Index, or fewer than 50 issuers, each is equally weighted and the face values of their respective bonds are increased or decreased on a pro-rata basis. Accrued interest is calculated assuming next-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. -When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use." -spread,percent,daily,bonds,usd,ex_g10,non_sovereign,BAMLEMCLLCRPIUSOAS,ICE BofA US Emerging Markets Liquid Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA US Emerging Markets Liquid Corporate Plus Index which tracks the performance of US dollar (USD) denominated emerging markets non-sovereign debt publicly issued in the major domestic and Eurobond markets. To qualify for inclusion in the index, the issuer of debt must have risk exposure to countries other than members of the FX G10 (US, Japan, New Zealand, Australia, Canada, Sweden, UK, Switzerland, Norway, and Euro Currency Members), all Western European countries, and territories of the US and Western European countries. Each security must also be denominated in USD with a time to maturity greater than 1 year until final maturity and have a fixed coupon. For inclusion in the index, investment grade rated bonds of qualifying issuers must have at least $500 million in outstanding face value, and below investment grade rated bonds must have at least $300 million in outstanding face value. The index includes corporate and quasi-government debt of qualifying countries, but excludes sovereign and supranational debt. Other types of securities acceptable for inclusion in this index are: original issue zero coupon bonds, ""global"" securities (debt issued in the US domestic bond markets as well the Eurobond Market simultaneously), 144a securities, pay-in-kind securities (includes toggle notes), callable perpetual securities (qualify if they are at least one year from the first call date), fixed-t-floating rate securities (qualify if the securities are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security). Defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding times the market price plus accrued interest, subject to a 10% country of risk cap and a 2% issuer cap. Countries and issuers that exceed the limits are reduced to 10% and 2%, respectively, and the face value of each of their bonds is adjusted on a pro-rata basis. Similarly, the face values of bonds of all other countries and issuers that fall below their respective caps are increased on a pro-rata basis. In the event there are fewer than 10 countries in the Index, or fewer than 50 issuers, each is equally weighted and the face values of their respective bonds are increased or decreased on a pro-rata basis. Accrued interest is calculated assuming next-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,ex_g10,non_sovereign,BAMLEMCLLCRPIUSSYTW,ICE BofA US Emerging Markets Liquid Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA US Emerging Markets Liquid Corporate Plus Index which tracks the performance of US dollar (USD) denominated emerging markets non-sovereign debt publicly issued in the major domestic and Eurobond markets. To qualify for inclusion in the index, the issuer of debt must have risk exposure to countries other than members of the FX G10 (US, Japan, New Zealand, Australia, Canada, Sweden, UK, Switzerland, Norway, and Euro Currency Members), all Western European countries, and territories of the US and Western European countries. Each security must also be denominated in USD with a time to maturity greater than 1 year until final maturity and have a fixed coupon. For inclusion in the index, investment grade rated bonds of qualifying issuers must have at least $500 million in outstanding face value, and below investment grade rated bonds must have at least $300 million in outstanding face value. The index includes corporate and quasi-government debt of qualifying countries, but excludes sovereign and supranational debt. Other types of securities acceptable for inclusion in this index are: original issue zero coupon bonds, ""global"" securities (debt issued in the US domestic bond markets as well the Eurobond Market simultaneously), 144a securities, pay-in-kind securities (includes toggle notes), callable perpetual securities (qualify if they are at least one year from the first call date), fixed-t-floating rate securities (qualify if the securities are callable within the fixed rate period and are at least one year from the last call prior to the date the bond transitions from a fixed to a floating rate security). Defaulted securities are excluded from the Index. - -ICE BofA Explains the Construction Methodology of this series as: -Index constituents are capitalization-weighted based on their current amount outstanding times the market price plus accrued interest, subject to a 10% country of risk cap and a 2% issuer cap. Countries and issuers that exceed the limits are reduced to 10% and 2%, respectively, and the face value of each of their bonds is adjusted on a pro-rata basis. Similarly, the face values of bonds of all other countries and issuers that fall below their respective caps are increased on a pro-rata basis. In the event there are fewer than 10 countries in the Index, or fewer than 50 issuers, each is equally weighted and the face values of their respective bonds are increased or decreased on a pro-rata basis. Accrued interest is calculated assuming next-day settlement. Cash flows from bond payments that are received during the month are retained in the index until the end of the month and then are removed as part of the rebalancing. Cash does not earn any reinvestment income while it is held in the Index. The Index is rebalanced on the last calendar day of the month, based on information available up to and including the third business day before the last business day of the month. Issues that meet the qualifying criteria are included in the Index for the following month. Issues that no longer meet the criteria during the course of the month remain in the Index until the next month-end rebalancing at which point they are removed from the Index. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payment frequencies of annual, semi-annual, quarterly, and monthly. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,ex_g10,non_financial,BAMLEMNFNFLCRPIUSTRIV,ICE BofA Non-Financial US Emerging Markets Liquid Corporate Plus Index Total Return Index Value,"The ICE BofA Non-Financial US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which excludes all Financial securities as well as the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,ex_g10,non_financial,BAMLEMNFNFLCRPIUSEY,ICE BofA Non-Financial US Emerging Markets Liquid Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Non-Financial US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which excludes all Financial securities as well as the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,ex_g10,non_financial,BAMLEMNFNFLCRPIUSOAS,ICE BofA Non-Financial US Emerging Markets Liquid Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Non-Financial US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which excludes all Financial securities as well as the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,ex_g10,non_financial,BAMLEMNFNFLCRPIUSSYTW,ICE BofA Non-Financial US Emerging Markets Liquid Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Non-Financial US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which excludes all Financial securities as well as the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,ex_g10,public_sector,BAMLEMPUPUBSLCRPIUSTRIV,ICE BofA Public Sector Issuers US Emerging Markets Liquid Corporate Plus Index Total Return Index Value,"The ICE BofA Public Sector US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes all quasi-government securities as well as the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -yield,percent,daily,bonds,usd,ex_g10,public_sector,BAMLEMPUPUBSLCRPIUSEY,ICE BofA Public Sector Issuers US Emerging Markets Liquid Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Public Sector US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes all quasi-government securities as well as the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,ex_g10,public_sector,BAMLEMPUPUBSLCRPIUSOAS,ICE BofA Public Sector Issuers US Emerging Markets Liquid Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Public Sector US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes all quasi-government securities as well as the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,ex_g10,public_sector,BAMLEMPUPUBSLCRPIUSSYTW,ICE BofA Public Sector Issuers US Emerging Markets Liquid Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Public Sector US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes all quasi-government securities as well as the debt of corporate issuers designated as government owned or controlled by ICE BofA emerging markets credit research. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,emea,non_sovereign,BAMLEMRECRPIEMEATRIV,ICE BofA EMEA Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA Europe, the Middle East, and Africa (EMEA) Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Europe, the Middle East and Africa, also including Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,emea,non_sovereign,BAMLEMRECRPIEMEAEY,ICE BofA EMEA Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Europe, the Middle East, and Africa (EMEA) Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Europe, the Middle East and Africa, also including Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -spread,percent,daily,bonds,all,emea,non_sovereign,BAMLEMRECRPIEMEAOAS,ICE BofA EMEA Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Europe, the Middle East, and Africa (EMEA) Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Europe, the Middle East and Africa, also including Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,emea,non_sovereign,BAMLEMRECRPIEMEASYTW,ICE BofA EMEA Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Europe, the Middle East, and Africa (EMEA) Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Europe, the Middle East and Africa, also including Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,emea,non_sovereign,BAMLEMELLCRPIEMEAUSTRIV,ICE BofA EMEA US Emerging Markets Liquid Corporate Plus Index Total Return Index Value,"The ICE BofA Europe, the Middle East, and Africa (EMEA) US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Europe, the Middle East and Africa, also including Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,emea,non_sovereign,BAMLEMELLCRPIEMEAUSEY,ICE BofA EMEA US Emerging Markets Liquid Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Europe, the Middle East, and Africa (EMEA) US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Europe, the Middle East and Africa, also including Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -spread,percent,daily,bonds,usd,emea,non_sovereign,BAMLEMELLCRPIEMEAUSOAS,ICE BofA EMEA US Emerging Markets Liquid Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Europe, the Middle East, and Africa (EMEA) US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Europe, the Middle East and Africa, also including Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,emea,non_sovereign,BAMLEMELLCRPIEMEAUSSYTW,ICE BofA EMEA US Emerging Markets Liquid Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Europe, the Middle East, and Africa (EMEA) US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Europe, the Middle East and Africa, also including Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,asia,non_sovereign,BAMLEMRACRPIASIATRIV,ICE BofA Asia Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA Asia Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Asia, excluding Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -yield,percent,daily,bonds,all,asia,non_sovereign,BAMLEMRACRPIASIAEY,ICE BofA Asia Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Asia Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Asia, excluding Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,asia,non_sovereign,BAMLEMRACRPIASIAOAS,ICE BofA Asia Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Asia Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Asia, excluding Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,asia,non_sovereign,BAMLEMRACRPIASIASYTW,ICE BofA Asia Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Asia Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Asia, excluding Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,asia,non_sovereign,BAMLEMALLCRPIASIAUSTRIV,ICE BofA Asia US Emerging Markets Liquid Corporate Plus Index Total Return Index Value,"The ICE BofA Asia US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Asia, excluding Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,asia,non_sovereign,BAMLEMALLCRPIASIAUSEY,ICE BofA Asia US Emerging Markets Liquid Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Asia US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Asia, excluding Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -spread,percent,daily,bonds,usd,asia,non_sovereign,BAMLEMALLCRPIASIAUSOAS,ICE BofA Asia US Emerging Markets Liquid Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Asia US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Asia, excluding Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,asia,non_sovereign,BAMLEMALLCRPIASIAUSSYTW,ICE BofA Asia US Emerging Markets Liquid Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Asia US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Asia, excluding Kazakhstan, Kyrgyzstan, Tajikistan, Turkmenistan, and Uzbekistan. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,latin_america,non_sovereign,BAMLEMRLCRPILATRIV,ICE BofA Latin America Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA Latin America Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Latin America. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,latin_america,non_sovereign,BAMLEMRLCRPILAEY,ICE BofA Latin America Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Latin America Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Latin America. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,latin_america,non_sovereign,BAMLEMRLCRPILAOAS,ICE BofA Latin America Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Latin America Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Latin America. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,latin_america,non_sovereign,BAMLEMRLCRPILASYTW,ICE BofA Latin America Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Latin America Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities issued by countries associated with the region of Latin America. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,latin_america,non_sovereign,BAMLEMLLLCRPILAUSTRIV,ICE BofA Latin America US Emerging Markets Liquid Corporate Plus Index Total Return Index Value,"The ICE BofA Latin America US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Latin America. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,latin_america,non_sovereign,BAMLEMLLLCRPILAUSEY,ICE BofA Latin America US Emerging Markets Liquid Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Latin America US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Latin America. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,latin_america,non_sovereign,BAMLEMLLLCRPILAUSOAS,ICE BofA Latin America US Emerging Markets Liquid Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Latin America US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Latin America. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,latin_america,non_sovereign,BAMLEMLLLCRPILAUSSYTW,ICE BofA Latin America US Emerging Markets Liquid Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Latin America US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities issued by countries associated with the region of Latin America. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,ex_g10,aaa,BAMLEM1BRRAAA2ACRPITRIV,ICE BofA AAA-A Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA AAA-A Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated AAA through A3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,ex_g10,aaa,BAMLEM1BRRAAA2ACRPIEY,ICE BofA AAA-A Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA AAA-A Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated AAA through A3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,ex_g10,aaa,BAMLEM1BRRAAA2ACRPIOAS,ICE BofA AAA-A Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA AAA-A Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated AAA through A3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,aaa,BAMLEM1BRRAAA2ACRPISYTW,ICE BofA AAA-A Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA AAA-A Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated AAA through A3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,ex_g10,aaa,BAMLEM1RAAA2ALCRPIUSTRIV,ICE BofA AAA-A US Emerging Markets Liquid Corporate Plus Index Total Return Index Value,"The ICE BofA AAA-A US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA US Emerging Markets Liquid Corporate Plus Index, which includes only securities rated AAA through A3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,ex_g10,aaa,BAMLEM1RAAA2ALCRPIUSEY,ICE BofA AAA-A US Emerging Markets Liquid Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA AAA-A US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA US Emerging Markets Liquid Corporate Plus Index, which includes only securities rated AAA through A3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,ex_g10,aaa,BAMLEM1RAAA2ALCRPIUSOAS,ICE BofA AAA-A US Emerging Markets Liquid Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA AAA-A US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA US Emerging Markets Liquid Corporate Plus Index, which includes only securities rated AAA through A3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,ex_g10,aaa,BAMLEM1RAAA2ALCRPIUSSYTW,ICE BofA AAA-A US Emerging Markets Liquid Corporate Plus Index Semi-Annual Yield to Worst ,"This data represents the semi-annual yield to worst of the ICE BofA AAA-A US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA US Emerging Markets Liquid Corporate Plus Index, which includes only securities rated AAA through A3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -total_return,index,daily,bonds,all,ex_g10,high_grade,BAMLEMIBHGCRPITRIV,ICE BofA High Grade Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA High Grade Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated AAA through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,ex_g10,high_grade,BAMLEMIBHGCRPIEY,ICE BofA High Grade Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA High Grade Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated AAA through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use." -spread,percent,daily,bonds,all,ex_g10,high_grade,BAMLEMIBHGCRPIOAS,ICE BofA High Grade Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA High Grade Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated AAA through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,high_grade,BAMLEMIBHGCRPISYTW,ICE BofA High Grade Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA High Grade Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated AAA through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,ex_g10,bbb,BAMLEM2BRRBBBCRPITRIV,ICE BofA BBB Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA BBB Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BBB1 through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,ex_g10,bbb,BAMLEM2BRRBBBCRPIEY,ICE BofA BBB Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA BBB Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BBB1 through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -spread,percent,daily,bonds,all,ex_g10,bbb,BAMLEM2BRRBBBCRPIOAS,ICE BofA BBB Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA BBB Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BBB1 through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,bbb,BAMLEM2BRRBBBCRPISYTW,ICE BofA BBB Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA BBB Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BBB1 through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,ex_g10,bbb,BAMLEM2RBBBLCRPIUSTRIV,ICE BofA BBB US Emerging Markets Liquid Corporate Plus Index Total Return Index Value,"The ICE BofA BBB US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities rated BBB1 through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,ex_g10,bbb,BAMLEM2RBBBLCRPIUSEY,ICE BofA BBB US Emerging Markets Liquid Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA BBB US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities rated BBB1 through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,ex_g10,bbb,BAMLEM2RBBBLCRPIUSOAS,ICE BofA BBB US Emerging Markets Liquid Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA BBB US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities rated BBB1 through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,ex_g10,bbb,BAMLEM2RBBBLCRPIUSSYTW,ICE BofA BBB US Emerging Markets Liquid Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA BBB US Emerging Markets Liquid Corporate Plus Index is a subset of the ICE BofA Emerging Markets Liquid Corporate Plus Index, which includes only securities rated BBB1 through BBB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Liquid Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,ex_g10,crossover,BAMLEM5BCOCRPITRIV,ICE BofA Crossover Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA Crossover Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BBB1 through BB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,ex_g10,crossover,BAMLEM5BCOCRPIEY,ICE BofA Crossover Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA Crossover Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BBB1 through BB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -spread,percent,daily,bonds,all,ex_g10,crossover,BAMLEM5BCOCRPIOAS,ICE BofA Crossover Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA Crossover Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BBB1 through BB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,crossover,BAMLEM5BCOCRPISYTW,ICE BofA Crossover Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Crossover Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BBB1 through BB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,ex_g10,bb,BAMLEM3BRRBBCRPITRIV,ICE BofA BB Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA BB Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BB1 through BB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,ex_g10,bb,BAMLEM3BRRBBCRPIEY,ICE BofA BB Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA BB Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BB1 through BB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,ex_g10,bb,BAMLEM3BRRBBCRPIOAS,ICE BofA BB Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA BB Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BB1 through BB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,bb,BAMLEM3BRRBBCRPISYTW,ICE BofA BB Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA BB Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BB1 through BB3. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,ex_g10,high_yield,BAMLEMHBHYCRPITRIV,ICE BofA High Yield Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA High Yield Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BB1 or lower. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,ex_g10,high_yield,BAMLEMHBHYCRPIEY,ICE BofA High Yield Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA High Yield Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BB1 or lower. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,ex_g10,high_yield,BAMLEMHBHYCRPIOAS,ICE BofA High Yield Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA High Yield Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BB1 or lower. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,high_yield,BAMLEMHBHYCRPISYTW,ICE BofA High Yield Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA High Yield Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated BB1 or lower. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,all,ex_g10,b,BAMLEM4BRRBLCRPITRIV,ICE BofA B & Lower Emerging Markets Corporate Plus Index Total Return Index Value,"The ICE BofA B and Lower Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated B1 or lower. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,all,ex_g10,b,BAMLEM4BRRBLCRPIEY,ICE BofA B & Lower Emerging Markets Corporate Plus Index Effective Yield,"This data represents the effective yield of the ICE BofA B and Lower Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated B1 or lower. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,all,ex_g10,b,BAMLEM4BRRBLCRPIOAS,ICE BofA B & Lower Emerging Markets Corporate Plus Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) for the ICE BofA B and Lower Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated B1 or lower. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. - -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,all,ex_g10,b,BAMLEM4BRRBLCRPISYTW,ICE BofA B & Lower Emerging Markets Corporate Plus Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA B and Lower Emerging Markets Corporate Plus Index is a subset of the ICE BofA Emerging Markets Corporate Plus Index, which includes only securities rated B1 or lower. The same inclusion rules apply for this series as those that apply for ICE BofA Emerging Markets Corporate Plus Index. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,us,aaa,BAMLCC0A1AAATRIV,ICE BofA AAA US Corporate Index Total Return Index Value,"This data represents the ICE BofA AAA US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating AAA. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,us,aaa,BAMLC0A1CAAAEY,ICE BofA AAA US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA AAA US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating AAA. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,us,aaa,BAMLC0A1CAAA,ICE BofA AAA US Corporate Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) of the ICE BofA AAA US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating AAA. -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,us,aaa,BAMLC0A1CAAASYTW,ICE BofA AAA US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA AAA US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating AAA. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,us,aa,BAMLCC0A2AATRIV,ICE BofA AA US Corporate Index Total Return Index Value,"This data represents the ICE BofA AA US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating AA. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -yield,percent,daily,bonds,usd,us,aa,BAMLC0A2CAAEY,ICE BofA AA US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA AA US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating AA. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission. -" -spread,percent,daily,bonds,usd,us,aa,BAMLC0A2CAA,ICE BofA AA US Corporate Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) of the ICE BofA AA US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating AA. -The ICE BofA OAS are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,us,aa,BAMLC0A2CAASYTW,ICE BofA AA US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA AA US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating AA. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,us,a,BAMLCC0A3ATRIV,ICE BofA Single-A US Corporate Index Total Return Index Value,"This data represents the ICE BofA Single-A US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating A. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,us,a,BAMLC0A3CAEY,ICE BofA Single-A US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA Single-A US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating A. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,us,a,BAMLC0A3CA,ICE BofA Single-A US Corporate Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) of the ICE BofA Single-A US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating A. -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,us,a,BAMLC0A3CASYTW,ICE BofA Single-A US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA Single-A US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating A. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,us,bbb,BAMLCC0A4BBBTRIV,ICE BofA BBB US Corporate Index Total Return Index Value,"This data represents the ICE BofA BBB US Corporate Index value, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating BBB. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,us,bbb,BAMLC0A4CBBBEY,ICE BofA BBB US Corporate Index Effective Yield,"This data represents the effective yield of the ICE BofA BBB US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating BBB. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,us,bbb,BAMLC0A4CBBB,ICE BofA BBB US Corporate Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) of the ICE BofA BBB US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating BBB. -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,us,bbb,BAMLC0A4CBBBSYTW,ICE BofA BBB US Corporate Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA BBB US Corporate Index, a subset of the ICE BofA US Corporate Master Index tracking the performance of US dollar denominated investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating BBB. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,us,bb,BAMLHYH0A1BBTRIV,ICE BofA BB US High Yield Index Total Return Index Value,"This data represents the ICE BofA US Corporate BB Index value, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating BB. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,us,bb,BAMLH0A1HYBBEY,ICE BofA BB US High Yield Index Effective Yield,"This data represents the effective yield of the ICE BofA US Corporate BB Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating BB. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,us,bb,BAMLH0A1HYBB,ICE BofA BB US High Yield Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) of the ICE BofA US Corporate BB Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating BB. -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,us,bb,BAMLH0A1HYBBSYTW,ICE BofA BB US High Yield Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA US Corporate BB Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating BB. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,us,b,BAMLHYH0A2BTRIV,ICE BofA Single-B US High Yield Index Total Return Index Value,"This data represents the ICE BofA US Corporate B Index value, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating B. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,us,b,BAMLH0A2HYBEY,ICE BofA Single-B US High Yield Index Effective Yield,"This data represents the effective yield of the ICE BofA US Corporate B Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating B. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,us,b,BAMLH0A2HYB,ICE BofA Single-B US High Yield Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) of the ICE BofA US Corporate B Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating B. -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,us,b,BAMLH0A2HYBSYTW,ICE BofA Single-B US High Yield Index Semi-Annual Yield to Worst ,"This data represents the semi-annual yield to worst of the ICE BofA US Corporate B Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating B. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -total_return,index,daily,bonds,usd,us,ccc,BAMLHYH0A3CMTRIV,ICE BofA CCC & Lower US High Yield Index Total Return Index Value,"This data represents the ICE BofA US Corporate C Index value, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating CCC or below. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield,percent,daily,bonds,usd,us,ccc,BAMLH0A3HYCEY,ICE BofA CCC & Lower US High Yield Index Effective Yield,"This data represents the effective yield of the ICE BofA US Corporate C Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating CCC or below. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -spread,percent,daily,bonds,usd,us,ccc,BAMLH0A3HYC,ICE BofA CCC & Lower US High Yield Index Option-Adjusted Spread,"This data represents the Option-Adjusted Spread (OAS) of the ICE BofA US Corporate C Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating CCC or below. -The ICE BofA OASs are the calculated spreads between a computed OAS index of all bonds in a given rating category and a spot Treasury curve. An OAS index is constructed using each constituent bond's OAS, weighted by market capitalization. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." -yield_to_worst,percent,daily,bonds,usd,us,ccc,BAMLH0A3HYCSYTW,ICE BofA CCC & Lower US High Yield Index Semi-Annual Yield to Worst,"This data represents the semi-annual yield to worst of the ICE BofA US Corporate C Index, a subset of the ICE BofA US High Yield Master II Index tracking the performance of US dollar denominated below investment grade rated corporate debt publicly issued in the US domestic market. This subset includes all securities with a given investment grade rating CCC or below. When the last calendar day of the month takes place on the weekend, weekend observations will occur as a result of month ending accrued interest adjustments. - -Yield to worst is the lowest potential yield that a bond can generate without the issuer defaulting. The standard US convention for this series is to use semi-annual coupon payments, whereas the standard in the foreign markets is to use coupon payments with frequencies of annual, semi-annual, quarterly, and monthly. - -The index data referenced herein is the property of ICE Data Indices, LLC, its affiliates, (""ICE"") and/or its Third Party Suppliers and has been licensed for use by the Federal Reserve Bank of St. Louis. ICE, its affiliates and Third Party Suppliers accept no liability in connection with its use. - -Copyright, 2017, ICE Benchmark Administration. Reprinted with permission." diff --git a/openbb_terminal/fixedincome/oecd_model.py b/openbb_terminal/fixedincome/oecd_model.py deleted file mode 100644 index 0096a693957..00000000000 --- a/openbb_terminal/fixedincome/oecd_model.py +++ /dev/null @@ -1,304 +0,0 @@ -""" OECD model """ - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - -COUNTRY_TO_CODE_SHORT = { - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "bulgaria": "BGR", - "canada": "CAN", - "switzerland": "CHE", - "chile": "CHL", - "china": "CHN", - "colombia": "COL", - "costa_rica": "CRI", - "czech_republic": "CZE", - "germany": "DEU", - "denmark": "DNK", - "euro_area": "EA19", - "spain": "ESP", - "estonia": "EST", - "finland": "FIN", - "france": "FRA", - "united_kingdom": "GBR", - "greece": "GRC", - "croatia": "HRV", - "hungary": "HUN", - "indonesia": "IDN", - "india": "IND", - "ireland": "IRL", - "iceland": "ISL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "lithuania": "LTU", - "luxembourg": "LUX", - "latvia": "LVA", - "mexico": "MEX", - "netherlands": "NLD", - "norway": "NOR", - "new_zealand": "NZL", - "poland": "POL", - "portugal": "PRT", - "romania": "ROU", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "sweden": "SWE", - "united_states": "USA", - "south_africa": "ZAF", -} - - -COUNTRY_TO_CODE_LONG = { - "australia": "AUS", - "austria": "AUT", - "belgium": "BEL", - "bulgaria": "BGR", - "brazil": "BRA", - "canada": "CAN", - "switzerland": "CHE", - "chile": "CHL", - "china": "CHN", - "colombia": "COL", - "costa_rica": "CRI", - "czech_republic": "CZE", - "germany": "DEU", - "denmark": "DNK", - "euro_area": "EA19", - "spain": "ESP", - "estonia": "EST", - "finland": "FIN", - "france": "FRA", - "united_kingdom": "GBR", - "greece": "GRC", - "croatia": "HRV", - "hungary": "HUN", - "indonesia": "IDN", - "india": "IND", - "ireland": "IRL", - "iceland": "ISL", - "israel": "ISR", - "italy": "ITA", - "japan": "JPN", - "korea": "KOR", - "lithuania": "LTU", - "luxembourg": "LUX", - "latvia": "LVA", - "mexico": "MEX", - "netherlands": "NLD", - "norway": "NOR", - "new_zealand": "NZL", - "poland": "POL", - "portugal": "PRT", - "romania": "ROU", - "russia": "RUS", - "slovak_republic": "SVK", - "slovenia": "SVN", - "sweden": "SWE", - "united_states": "USA", - "south_africa": "ZAF", -} - - -@log_start_end(log=logger) -def get_interest_rate_data( - data: str = "short", - countries: Optional[List[str]] = None, - start_date: str = "", - end_date: str = "", -) -> pd.DataFrame: - """Gets interest rate data from selected countries. - - Long-term interest rates refer to government bonds maturing in ten years. Rates are mainly determined by the - price charged by the lender, the risk from the borrower and the fall in the capital value. Long-term interest - rates are generally averages of daily rates, measured as a percentage. These interest rates are implied by the - prices at which the government bonds are traded on financial markets, not the interest rates at which the loans - were issued. In all cases, they refer to bonds whose capital repayment is guaranteed by governments. Long-term - interest rates are one of the determinants of business investment. Low long-term interest rates encourage - investment in new equipment and high interest rates discourage it. Investment is, in turn, a major source of - economic growth. - - Short-term interest rates are the rates at which short-term borrowings are effected between financial - institutions or the rate at which short-term government paper is issued or traded in the market. Short-term - interest rates are generally averages of daily rates, measured as a percentage. Short-term interest rates are - based on three-month money market rates where available. Typical standardised names are "money market rate" and - "treasury bill rate". - - Parameters - ---------- - data: str - Type of data to getz, options ['short', 'short_forecast', 'long', 'long_forecast'] - countries: list - List of countries to get data for - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - - Returns - ------- - pd.DataFrame - Dataframe with the interest rate data - """ - if data == "short": - if isinstance(start_date, datetime): - start_date = start_date.strftime("%Y-%m") - elif len(start_date) > 8: - start_date = start_date[:8] - if isinstance(end_date, datetime): - end_date = end_date.strftime("%Y-%m") - elif len(end_date) > 8: - end_date = end_date[:8] - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.STINT.TOT.PC_PA.M/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - data_name = "3 month" - elif data == "short_forecast": - if isinstance(start_date, datetime): - month = start_date.month - start_date = start_date.strftime("%Y") + "-Q" + str((month - 1) // 3 + 1) - elif len(start_date) > 7: - month = int(start_date[5:7]) - start_date = start_date[:4] + "-Q" + str((month - 1) // 3 + 1) - if isinstance(end_date, datetime): - month = end_date.month - end_date = end_date.strftime("%Y") + "-Q" + str((month - 1) // 3 + 1) - elif len(end_date) > 7: - month = int(end_date[5:7]) - end_date = end_date[:4] + "-Q" + str((month - 1) // 3 + 1) - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.STINTFORECAST.TOT.PC_PA.Q/OECD?contentType=csv&detail" - f"=code&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - data_name = "3 month with forecast" - - elif data == "long": - if isinstance(start_date, datetime): - start_date = start_date.strftime("%Y-%m") - elif len(start_date) > 8: - start_date = start_date[:8] - if isinstance(end_date, datetime): - end_date = end_date.strftime("%Y-%m") - elif len(end_date) > 8: - end_date = end_date[:8] - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.LTINT.TOT.PC_PA.M/OECD?contentType=csv&detail=code" - f"&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - data_name = "10 year" - elif data == "long_forecast": - if isinstance(start_date, datetime): - month = start_date.month - start_date = start_date.strftime("%Y") + "-Q" + str((month - 1) // 3 + 1) - elif len(start_date) > 7: - month = int(start_date[5:7]) - start_date = start_date[:4] + "-Q" + str((month - 1) // 3 + 1) - if isinstance(end_date, datetime): - month = end_date.month - end_date = end_date.strftime("%Y") + "-Q" + str((month - 1) // 3 + 1) - elif len(end_date) > 7: - month = int(end_date[5:7]) - end_date = end_date[:4] + "-Q" + str((month - 1) // 3 + 1) - df = pd.read_csv( - f"https://stats.oecd.org/sdmx-json/data/DP_LIVE/.LTINTFORECAST.TOT.PC_PA.Q/OECD?contentType=csv&detail" - f"=code&separator=comma&csv-lang=en&startPeriod={start_date}&endPeriod={end_date}", - index_col=5, - ) - data_name = "10 year with forecast" - else: - return pd.DataFrame() - df = df.iloc[:, [0, 5]] - - result = pd.DataFrame() - for country in countries: # type: ignore - if data in ["short", "short_forecast"]: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_SHORT[country]]["Value"] - ) - else: - temp = pd.DataFrame( - df[df["LOCATION"] == COUNTRY_TO_CODE_LONG[country]]["Value"] - ) - temp.columns = [f"{country} ({data_name})"] - result = pd.concat([result, temp], axis=1) - - result.index = pd.to_datetime(result.index).date - result.sort_index(inplace=True) - - return result - - -def get_treasury( - short_term: Optional[list] = None, - long_term: Optional[list] = None, - forecast: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Gets interest rates data from selected countries (3 month and 10 year) - - Short-term interest rates are the rates at which short-term borrowings are effected between financial - institutions or the rate at which short-term government paper is issued or traded in the market. Short-term - interest rates are generally averages of daily rates, measured as a percentage. Short-term interest rates are - based on three-month money market rates where available. Typical standardised names are "money market rate" and - "treasury bill rate". - - Long-term interest rates refer to government bonds maturing in ten years. Rates are mainly determined by the - price charged by the lender, the risk from the borrower and the fall in the capital value. Long-term interest - rates are generally averages of daily rates, measured as a percentage. These interest rates are implied by the - prices at which the government bonds are traded on financial markets, not the interest rates at which the loans - were issued. In all cases, they refer to bonds whose capital repayment is guaranteed by governments. Long-term - interest rates are one of the determinants of business investment. Low long-term interest rates encourage - investment in new equipment and high interest rates discourage it. Investment is, in turn, a major source of - economic growth. - - Parameters - ---------- - short_term: list - Countries you wish to plot the 3-month interest rate for - long_term: list - Countries you wish to plot the 10-year interest rate for - forecast: bool - If True, plot forecasts for short term interest rates - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - """ - df_short, df_long = pd.DataFrame(), pd.DataFrame() - - if short_term: - df_short = get_interest_rate_data( - f"short{'_forecast' if forecast else ''}", - short_term, - start_date if start_date is not None else "", - end_date if end_date is not None else "", - ) - if long_term: - df_long = get_interest_rate_data( - f"long{'_forecast' if forecast else ''}", - long_term, - start_date if start_date is not None else "", - end_date if end_date is not None else "", - ) - - df = pd.concat([df_short, df_long], axis=1) - - return df diff --git a/openbb_terminal/fixedincome/oecd_view.py b/openbb_terminal/fixedincome/oecd_view.py deleted file mode 100644 index b273dda9c3f..00000000000 --- a/openbb_terminal/fixedincome/oecd_view.py +++ /dev/null @@ -1,113 +0,0 @@ -""" OECD view """ - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import logging -import os -from typing import Optional - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.fixedincome import oecd_model -from openbb_terminal.helper_funcs import export_data, print_rich_table - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments, too-many-function-args - - -@log_start_end(log=logger) -def plot_treasuries( - short_term: Optional[list] = None, - long_term: Optional[list] = None, - forecast: bool = False, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, - limit: int = 10, -): - """Gets interest rates data from selected countries (3 month and 10 year) - - Short-term interest rates are the rates at which short-term borrowings are effected between financial - institutions or the rate at which short-term government paper is issued or traded in the market. Short-term - interest rates are generally averages of daily rates, measured as a percentage. Short-term interest rates are - based on three-month money market rates where available. Typical standardised names are "money market rate" and - "treasury bill rate". - - Long-term interest rates refer to government bonds maturing in ten years. Rates are mainly determined by the - price charged by the lender, the risk from the borrower and the fall in the capital value. Long-term interest - rates are generally averages of daily rates, measured as a percentage. These interest rates are implied by the - prices at which the government bonds are traded on financial markets, not the interest rates at which the loans - were issued. In all cases, they refer to bonds whose capital repayment is guaranteed by governments. Long-term - interest rates are one of the determinants of business investment. Low long-term interest rates encourage - investment in new equipment and high interest rates discourage it. Investment is, in turn, a major source of - economic growth. - - Parameters - ---------- - short_term: list - Countries you wish to plot the 3-month interest rate for - long_term: list - Countries you wish to plot the 10-year interest rate for - forecast: bool - If True, plot forecasts for short term interest rates - start_date: str - Start date of data, in YYYY-MM-DD format - end_date: str - End date of data, in YYYY-MM-DD format - raw: bool - If True, print raw data - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = oecd_model.get_treasury(short_term, long_term, forecast, start_date, end_date) - - fig = OpenBBFigure(yaxis_title="Yield (%)") - - for country in df.columns: - fig.add_scatter( - x=df.index, - y=df[country], - name=country.replace("_", " ").title(), - mode="lines", - line_width=2.5, - showlegend=True, - ) - - term = ( - "Short and Long" - if short_term and long_term - else "Long" if long_term else "Short" - ) - title = f"{term} Term Interest Rates {' with forecasts' if forecast else ''}" - fig.set_title(title) - - if raw: - # was a -iloc so we need to flip the index as we use head - df = df.sort_index(ascending=False) - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title=title, - floatfmt=".3f", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "treasury", - df / 100, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/fixedincome/yfinance_model.py b/openbb_terminal/fixedincome/yfinance_model.py deleted file mode 100644 index 5ef59fa3787..00000000000 --- a/openbb_terminal/fixedincome/yfinance_model.py +++ /dev/null @@ -1,78 +0,0 @@ -""" Yahoo Finance Model """ - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional - -import pandas as pd -import yfinance as yf - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_series( - series_id: str, - interval: str = "1d", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - column: str = "Adj Close", -) -> pd.Series: - """Obtain data on any index [Source: Yahoo Finance] - - Parameters - ---------- - series_id: str - The series you wish to collect data for. - start_date : Optional[str] - the selected country - end_date : Optional[str] - The currency you wish to convert the data to. - interval : str - Valid intervals: 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 5d, 1wk, 1mo or 3mo - Intraday data cannot extend last 60 days - column : str - The column you wish to select, by default this is Adjusted Close. - - Returns - ------- - pd.Series - A series with the requested index - """ - try: - if start_date: - datetime.strptime(str(start_date), "%Y-%m-%d") - if end_date: - datetime.strptime(str(end_date), "%Y-%m-%d") - except ValueError: - console.print("[red]Please format date as YYYY-MM-DD[/red]\n") - return pd.Series(dtype="object") - - index_data = yf.download( - series_id, - start=start_date, - end=end_date, - interval=interval, - progress=False, - show_errors=False, - ) - - if column not in index_data.columns: - console.print( - f"The chosen column is not available for {series_id}. Please choose " - f"between: {', '.join(index_data.columns)}\n" - ) - return pd.Series(dtype="float64") - if index_data.empty or len(index_data) < 2: - console.print( - f"The chosen index {series_id}, returns no data. Please check if " - f"there is any data available.\n" - ) - return pd.Series(dtype="float64") - - return index_data[column] diff --git a/openbb_terminal/fixedincome/yfinance_view.py b/openbb_terminal/fixedincome/yfinance_view.py deleted file mode 100644 index 970b94d9b57..00000000000 --- a/openbb_terminal/fixedincome/yfinance_view.py +++ /dev/null @@ -1,68 +0,0 @@ -""" Yahoo Finance view """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.fixedincome import yfinance_model -from openbb_terminal.helper_funcs import export_data - -logger = logging.getLogger(__name__) - -TY_TO_ID = { - "5_year": "^FVX", - "10_year": "^TNX", - "30_year": "^TYX", -} - - -@log_start_end(log=logger) -@check_api_key(["API_FRED_KEY"]) -def plot_ty( - maturity: str = "5_year", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - external_axes: bool = False, -): - """Plot Treasury Yield. - - Parameters - ---------- - maturity: str - Maturity to plot, options: ['5_year', '10_year', '30_year'] - start_date: Optional[str] - Start date, formatted YYYY-MM-DD - end_date: Optional[str] - End date, formatted YYYY-MM-DD - export: str - Export data to csv or excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = yfinance_model.get_series( - TY_TO_ID[maturity], start_date=start_date, end_date=end_date - ) - - fig = OpenBBFigure() - fig.set_title(f"{maturity.replace('-', ' ')} Treasury Yield [Percent]") - - fig.add_scatter( - x=df.index, y=df.values, name=maturity.replace("_", " "), mode="lines" - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - maturity, - pd.DataFrame(df, columns=[maturity]) / 100, - figure=fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/forecast/__init__.py b/openbb_terminal/forecast/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/forecast/anom_model.py b/openbb_terminal/forecast/anom_model.py deleted file mode 100644 index 0778943c335..00000000000 --- a/openbb_terminal/forecast/anom_model.py +++ /dev/null @@ -1,60 +0,0 @@ -# pylint: disable=too-many-arguments -"""Quantile Anomaly Detection Model""" -__docformat__ = "numpy" - -import logging -from typing import List, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.ad import KMeansScorer, QuantileDetector - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_anomaly_detection_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - train_split: float = 0.6, -) -> Tuple[TimeSeries, List[TimeSeries], List[TimeSeries]]: - """Get Quantile Anomaly Detection Data - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - - Input Data - ------- - target_column: str - Target column to forecast. Defaults to "close". - train_split: (float, optional) - Train/val split. Defaults to 0.85. - - Returns - ------- - Tuple[ - List[TimeSeries], - List[TimeSeries], - List[TimeSeries], - ] - """ - use_scalers = False - - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - - # split dataset into train and val - train, val = ticker_series.split_before(train_split) - - scorer = KMeansScorer(k=2, window=5) - scorer.fit(train) - anom_score = scorer.score(val) - - detector = QuantileDetector(high_quantile=0.99) - detector.fit(scorer.score(train)) - binary_anom = detector.detect(anom_score) - - return ticker_series, anom_score, binary_anom diff --git a/openbb_terminal/forecast/anom_view.py b/openbb_terminal/forecast/anom_view.py deleted file mode 100644 index abf85d94839..00000000000 --- a/openbb_terminal/forecast/anom_view.py +++ /dev/null @@ -1,100 +0,0 @@ -"""Quantile Anomaly Detector View""" - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import anom_model, helpers -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_anomaly_detection( - data: Union[pd.Series, pd.DataFrame], - dataset_name="", - target_column: str = "close", - train_split: float = 0.6, - export: str = "", - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Display Quantile Anomaly Detection - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - - Input Data - ---------- - target_column: str - Target column to forecast. Defaults to "close". - train_split: (float, optional) - Train/val split. Defaults to 0.85. - export: (str, optional) - Export data to csv, jpg, png, or pdf. Defaults to "". - start_date: (Optional[datetime], optional) - Start date. Defaults to None. - end_date: (Optional[datetime], optional) - End date. Defaults to None. - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - Union[None, go.Figure] - None if external_axes is True, otherwise the figure object - """ - data = helpers.clean_data(data, start_date, end_date, target_column) - if not helpers.check_data(data, target_column): - return None - - (ticker_series, _, binary_anom) = anom_model.get_anomaly_detection_data( - data=data, - target_column=target_column, - train_split=train_split, - ) - - ticker_max = np.max(ticker_series.values()) - df_ticker = ticker_series.pd_dataframe()[target_column] - df_binary_anom = binary_anom.pd_dataframe() - - fig = OpenBBFigure(xaxis_title="Date") - fig.set_title(f"Quantile Anomaly Detection for {dataset_name}") - - fig.add_scatter(x=df_ticker.index, y=df_ticker, name=target_column, mode="lines") - - for column in df_binary_anom.columns: - fig.add_scatter( - x=df_binary_anom.index, - y=df_binary_anom[column] * int(ticker_max), - name="detected binary anomaly", - mode="lines", - line=dict(color="red"), - ) - - console.print( - f" [green]Quantile Anomaly Detection for {dataset_name} calculated.[/green]" - ) - - # user wants to export plot - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{dataset_name}_Anomaly_Detection", - figure=fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/forecast/autoarima_model.py b/openbb_terminal/forecast/autoarima_model.py deleted file mode 100644 index 5b4c2904e94..00000000000 --- a/openbb_terminal/forecast/autoarima_model.py +++ /dev/null @@ -1,144 +0,0 @@ -# pylint: disable=too-many-arguments,E1101 -"""Automatic ARIMA Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from statsforecast.core import StatsForecast -from statsforecast.models import AutoARIMA - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -warnings.simplefilter("ignore") - -logger = logging.getLogger(__name__) - -# pylint: disable=E1123 - - -@log_start_end(log=logger) -def get_autoarima_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, -) -> Tuple[ - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[float], - Optional[StatsForecast], -]: - """Performs Automatic ARIMA forecasting - This is a wrapper around StatsForecast AutoARIMA; - we refer to this link for the original and more complete documentation of the parameters. - - - https://nixtla.github.io/statsforecast/models.html#autoarima - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str] - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year (7 for daily data) - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], float, StatsForecast] - Adjusted Data series, - List of historical fcast values, - List of predicted fcast values, - Optional[float] - precision - Fit AutoaRIMA model object. - """ - - use_scalers = False - # statsforecast preprocessing - # when including more time series - # the preprocessing is similar - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - freq = ticker_series.freq_str - ticker_series = ticker_series.pd_dataframe().reset_index() - ticker_series.columns = ["ds", "y"] - ticker_series.insert(0, "unique_id", target_column) - - try: - # Model Init - model = AutoARIMA(season_length=int(seasonal_periods)) - fcst = StatsForecast(df=ticker_series, models=[model], freq=freq, verbose=True) - except Exception as e: # noqa - error = str(e) - if "got an unexpected keyword argument" in error: - console.print( - "[red]Please update statsforecast to version 1.1.3 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None - - # Historical backtesting - last_training_point = int((len(ticker_series) - 1) * start_window) - historical_fcast = fcst.cross_validation( - h=int(forecast_horizon), - test_size=len(ticker_series) - last_training_point, - n_windows=None, - input_size=min(10 * forecast_horizon, len(ticker_series)), - ) - - # train new model on entire timeseries to provide best current forecast - # we have the historical fcast, now lets predict. - forecast = fcst.forecast(int(n_predict)) - y_true = historical_fcast["y"].values - y_hat = historical_fcast["AutoARIMA"].values - precision = helpers.mean_absolute_percentage_error(y_true, y_hat) - console.print(f"AutoARIMA obtains MAPE: {precision:.2f}% \n") - - # transform outputs to make them compatible with - # plots - use_scalers = False - _, ticker_series = helpers.get_series( - ticker_series.rename(columns={"y": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, forecast = helpers.get_series( - forecast.rename(columns={"AutoARIMA": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, historical_fcast = helpers.get_series( - historical_fcast.groupby("ds") - .head(1) - .rename(columns={"AutoARIMA": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - - return ( - ticker_series, - historical_fcast, - forecast, - float(precision), - fcst, - ) diff --git a/openbb_terminal/forecast/autoarima_view.py b/openbb_terminal/forecast/autoarima_view.py deleted file mode 100644 index 4f92bfeae74..00000000000 --- a/openbb_terminal/forecast/autoarima_view.py +++ /dev/null @@ -1,123 +0,0 @@ -"""Automatic ARIMA View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import autoarima_model, helpers - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_autoarima_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display Automatic ARIMA model. - - Parameters - ---------- - data : Union[pd.Series, np.array] - Data to forecast - dataset_name str - The name of the ticker to be predicted - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = autoarima_model.get_autoarima_data( - data=data, - target_column=target_column, - seasonal_periods=seasonal_periods, - n_predict=n_predict, - start_window=start_window, - forecast_horizon=forecast_horizon, - ) - - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="AutoARIMA", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, None, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/autoces_model.py b/openbb_terminal/forecast/autoces_model.py deleted file mode 100644 index e11b7960a23..00000000000 --- a/openbb_terminal/forecast/autoces_model.py +++ /dev/null @@ -1,144 +0,0 @@ -# pylint: disable=too-many-arguments,E1101 -"""Automatic Comples Exponential Smoothing Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from statsforecast.core import StatsForecast -from statsforecast.models import AutoCES - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -warnings.simplefilter("ignore") - -logger = logging.getLogger(__name__) - -# pylint: disable=E1123 - - -@log_start_end(log=logger) -def get_autoces_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, -) -> Tuple[ - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[float], - Optional[StatsForecast], -]: - """Performs Automatic Complex Exponential Smoothing forecasting - This is a wrapper around StatsForecast AutoCES; - we refer to this link for the original and more complete documentation of the parameters. - - - https://nixtla.github.io/statsforecast/models.html#autoces - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year (7 for daily data) - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], StatsForecast] - Adjusted Data series, - List of historical fcast values, - List of predicted fcast values, - Optional[float] - precision, - Fit CES model object. - """ - - use_scalers = False - # statsforecast preprocessing - # when including more time series - # the preprocessing is similar - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - freq = ticker_series.freq_str - ticker_series = ticker_series.pd_dataframe().reset_index() - ticker_series.columns = ["ds", "y"] - ticker_series.insert(0, "unique_id", target_column) - - try: - # Model Init - model = AutoCES( - season_length=int(seasonal_periods), - ) - fcst = StatsForecast(df=ticker_series, models=[model], freq=freq, verbose=True) - except Exception as e: # noqa - error = str(e) - if "got an unexpected keyword argument" in error: - console.print( - "[red]Please update statsforecast to version 1.1.3 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None - - # Historical backtesting - last_training_point = int((len(ticker_series) - 1) * start_window) - historical_fcast = fcst.cross_validation( - h=int(forecast_horizon), - test_size=len(ticker_series) - last_training_point, - n_windows=None, - input_size=min(10 * forecast_horizon, len(ticker_series)), - ) - - # train new model on entire timeseries to provide best current forecast - # we have the historical fcast, now lets predict. - forecast = fcst.forecast(int(n_predict)) - y_true = historical_fcast["y"].values - y_hat = historical_fcast["CES"].values - precision = helpers.mean_absolute_percentage_error(y_true, y_hat) - console.print(f"AutoCES obtains MAPE: {precision:.2f}% \n") - - # transform outputs to make them compatible with - # plots - use_scalers = False - _, ticker_series = helpers.get_series( - ticker_series.rename(columns={"y": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, forecast = helpers.get_series( - forecast.rename(columns={"CES": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, historical_fcast = helpers.get_series( - historical_fcast.groupby("ds").head(1).rename(columns={"CES": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - - return ( - ticker_series, - historical_fcast, - forecast, - float(precision), - fcst, - ) diff --git a/openbb_terminal/forecast/autoces_view.py b/openbb_terminal/forecast/autoces_view.py deleted file mode 100644 index dff06353f83..00000000000 --- a/openbb_terminal/forecast/autoces_view.py +++ /dev/null @@ -1,123 +0,0 @@ -"""Automatic Complex Exponential Smoothing View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import autoces_model, helpers - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_autoces_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display Automatic Complex Exponential Smoothing Model - - Parameters - ---------- - data : Union[pd.Series, np.array] - Data to forecast - dataset_name str - The name of the ticker to be predicted - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = autoces_model.get_autoces_data( - data=data, - target_column=target_column, - seasonal_periods=seasonal_periods, - n_predict=n_predict, - start_window=start_window, - forecast_horizon=forecast_horizon, - ) - - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="AutoCES", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, None, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/autoets_model.py b/openbb_terminal/forecast/autoets_model.py deleted file mode 100644 index 26f1b508683..00000000000 --- a/openbb_terminal/forecast/autoets_model.py +++ /dev/null @@ -1,148 +0,0 @@ -# pylint: disable=too-many-arguments,E1101 -"""Automatic ETS (Error, Trend, and Seasonality) Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from statsforecast.core import StatsForecast -from statsforecast.models import ETS - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -warnings.simplefilter("ignore") - -logger = logging.getLogger(__name__) - -# pylint: disable=E1123 - - -@log_start_end(log=logger) -def get_autoets_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, -) -> Tuple[ - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[float], - Optional[StatsForecast], -]: - """Performs Automatic ETS forecasting - This is a wrapper around StatsForecast ETS; - we refer to this link for the original and more complete documentation of the parameters. - - - https://nixtla.github.io/statsforecast/models.html#ets - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year (7 for daily data) - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], StatsForecast] - Adjusted Data series, - List of historical fcast values, - List of predicted fcast values, - Optional[float] - precision, - Fit ETS model object. - """ - - use_scalers = False - # statsforecast preprocessing - # when including more time series - # the preprocessing is similar - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - freq = ticker_series.freq_str - ticker_series = ticker_series.pd_dataframe().reset_index() - ticker_series.columns = ["ds", "y"] - ticker_series.insert(0, "unique_id", target_column) - - try: - # Model Init - model_ets = ETS( - season_length=int(seasonal_periods), - ) - fcst = StatsForecast( - df=ticker_series, models=[model_ets], freq=freq, verbose=True - ) - except Exception as e: # noqa - error = str(e) - if "got an unexpected keyword argument" in error: - console.print( - "[red]Please update statsforecast to version 1.1.3 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None - - # Historical backtesting - last_training_point = int((len(ticker_series) - 1) * start_window) - historical_fcast_ets = fcst.cross_validation( - h=int(forecast_horizon), - test_size=len(ticker_series) - last_training_point, - n_windows=None, - input_size=min(10 * forecast_horizon, len(ticker_series)), - ) - - # train new model on entire timeseries to provide best current forecast - # we have the historical fcast, now lets predict. - forecast = fcst.forecast(int(n_predict)) - y_true = historical_fcast_ets["y"].values - y_hat = historical_fcast_ets["ETS"].values - precision = helpers.mean_absolute_percentage_error(y_true, y_hat) - console.print(f"AutoETS obtains MAPE: {precision:.2f}% \n") - - # transform outputs to make them compatible with - # plots - use_scalers = False - _, ticker_series = helpers.get_series( - ticker_series.rename(columns={"y": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, forecast = helpers.get_series( - forecast.rename(columns={"ETS": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, historical_fcast_ets = helpers.get_series( - historical_fcast_ets.groupby("ds") - .head(1) - .rename(columns={"ETS": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - - return ( - ticker_series, - historical_fcast_ets, - forecast, - float(precision), - fcst, - ) diff --git a/openbb_terminal/forecast/autoets_view.py b/openbb_terminal/forecast/autoets_view.py deleted file mode 100644 index 872e27da430..00000000000 --- a/openbb_terminal/forecast/autoets_view.py +++ /dev/null @@ -1,123 +0,0 @@ -"""Automatic ETS (Error, Trend, Sesonality) View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import autoets_model, helpers - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_autoets_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display Automatic ETS (Error, Trend, Sesonality) Model - - Parameters - ---------- - data : Union[pd.Series, np.array] - Data to forecast - dataset_name str - The name of the ticker to be predicted - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = autoets_model.get_autoets_data( - data=data, - target_column=target_column, - seasonal_periods=seasonal_periods, - n_predict=n_predict, - start_window=start_window, - forecast_horizon=forecast_horizon, - ) - - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="AutoETS", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, None, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/autoselect_model.py b/openbb_terminal/forecast/autoselect_model.py deleted file mode 100644 index 53282e35403..00000000000 --- a/openbb_terminal/forecast/autoselect_model.py +++ /dev/null @@ -1,228 +0,0 @@ -# pylint: disable=too-many-arguments,E1101 -"""Automatic Statistical Forecast""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from statsforecast.core import StatsForecast - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import USE_COLOR, console - -warnings.simplefilter("ignore") - -logger = logging.getLogger(__name__) - -# pylint: disable=E1123,E1137 - - -def precision_format(best_model: str, index: str, val: float) -> Union[str, float]: - if ( - index == best_model - and USE_COLOR - and not get_current_user().preferences.USE_INTERACTIVE_DF - ): - return f"[#00AAFF]{val:.2f}% [/#00AAFF]" - return val - - -@log_start_end(log=logger) -def get_autoselect_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, -) -> Tuple[ - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[float], - Optional[StatsForecast], - Optional[Union[int, str]], -]: - """Performs Automatic Statistical forecasting - This is a wrapper around StatsForecast models; - we refer to this link for the original and more complete documentation of the parameters. - - - https://nixtla.github.io/statsforecast/models.html - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year (7 for daily data) - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], StatsForecast, Union[int, str]] - list[np.ndarray] - Adjusted Data series - list[np.ndarray] - List of historical fcast values - list[np.ndarray] - List of predicted fcast values - Optional[float] - precision - StatsForecast - Fit ETS model object. - Union[int, str] - Best model - """ - - use_scalers = False - # statsforecast preprocessing - # when including more time series - # the preprocessing is similar - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - freq = ticker_series.freq_str - ticker_series = ticker_series.pd_dataframe().reset_index() - ticker_series.columns = ["ds", "y"] - ticker_series.insert(0, "unique_id", target_column) - - # check statsforecast dependency - try: - from statsforecast.models import ( # pylint: disable=import-outside-toplevel - ETS, - MSTL, - AutoARIMA, - AutoCES, - Naive, - RandomWalkWithDrift, - SeasonalNaive, - SeasonalWindowAverage, - ) - except Exception as e: - error = str(e) - if "cannot import name" in error: - console.print( - "[red]Please update statsforecast to version 1.2.0 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None, None - - try: - # Model Init - season_length = int(seasonal_periods) - models = [ - AutoARIMA(season_length=season_length), - ETS(season_length=season_length), - AutoCES(season_length=season_length), - MSTL(season_length=season_length), - SeasonalNaive(season_length=season_length), - SeasonalWindowAverage( - season_length=season_length, window_size=season_length - ), - RandomWalkWithDrift(), - ] - fcst = StatsForecast( - df=ticker_series, - models=models, - freq=freq, - verbose=True, - fallback_model=Naive(), - ) - except Exception as e: # noqa - error = str(e) - if "got an unexpected keyword argument" in error: - console.print( - "[red]Please update statsforecast to version 1.1.3 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None, None - - # Historical backtesting - last_training_point = int((len(ticker_series) - 1) * start_window) - historical_fcast = fcst.cross_validation( - h=int(forecast_horizon), - test_size=len(ticker_series) - last_training_point, - n_windows=None, - input_size=min(10 * forecast_horizon, len(ticker_series)), - ) - # change name to AutoETS and AutoCES - cols = [ - c if (c not in ["ETS", "CES"]) else f"Auto{c}" for c in historical_fcast.columns - ] - historical_fcast.columns = cols - - # train new model on entire timeseries to provide best current forecast - # we have the historical fcast, now lets predict. - forecast = fcst.forecast(int(n_predict)) - # change name to AutoETS and AutoCES - cols = [c if (c not in ["ETS", "CES"]) else f"Auto{c}" for c in forecast.columns] - forecast.columns = cols - # Backtesting evaluation - y_true = historical_fcast["y"].values - model_names = historical_fcast.drop(columns=["ds", "cutoff", "y"]).columns - precision_per_model = [ - helpers.mean_absolute_percentage_error(y_true, historical_fcast[model].values) - for model in model_names - ] - - precision: pd.DataFrame = pd.DataFrame( - {"precision": precision_per_model}, index=model_names - ) - precision = precision.sort_values(by="precision") - # select best model - best_precision: float = precision["precision"].min() - best_model = precision["precision"].idxmin() - - # print results - precision["precision"] = [ # pylint: disable=unsupported-assignment-operation - precision_format(best_model, index, val) - for index, val in precision["precision"].iteritems() - ] - console.print("\n") - - title = ( - f"[#00AAFF]{best_model}[/#00AAFF]" - if not get_current_user().preferences.USE_INTERACTIVE_DF - else best_model - ) - helpers.print_rich_table( - precision, - show_index=True, - index_name="Model", - headers=["MAPE"], - title=f"Performance per model.\nBest model: {title}", - ) - - # transform outputs to make them compatible with - # plots - use_scalers = False - _, ticker_series = helpers.get_series( - ticker_series.rename(columns={"y": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, forecast = helpers.get_series( - forecast.rename(columns={best_model: target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, historical_fcast = helpers.get_series( - historical_fcast.groupby("ds") - .head(1) - .rename(columns={best_model: target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - - return (ticker_series, historical_fcast, forecast, best_precision, fcst, best_model) diff --git a/openbb_terminal/forecast/autoselect_view.py b/openbb_terminal/forecast/autoselect_view.py deleted file mode 100644 index 37152a7a768..00000000000 --- a/openbb_terminal/forecast/autoselect_view.py +++ /dev/null @@ -1,123 +0,0 @@ -"""Automatic Statistical Forecasting View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import autoselect_model, helpers - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_autoselect_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - external_axes: bool = False, -): - """Display Automatic Statistical Forecasting Model - - Parameters - ---------- - data : Union[pd.Series, np.array] - Data to forecast - dataset_name str - The name of the ticker to be predicted - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - best_model, - ) = autoselect_model.get_autoselect_data( - data=data, - target_column=target_column, - seasonal_periods=seasonal_periods, - n_predict=n_predict, - start_window=start_window, - forecast_horizon=forecast_horizon, - ) - - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name=f"Best Model: {best_model}", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, None, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/brnn_model.py b/openbb_terminal/forecast/brnn_model.py deleted file mode 100644 index cd1516d107f..00000000000 --- a/openbb_terminal/forecast/brnn_model.py +++ /dev/null @@ -1,184 +0,0 @@ -# pylint: disable=too-many-arguments -"""RNN Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models import BlockRNNModel -from darts.models.forecasting.torch_forecasting_model import GlobalForecastingModel -from darts.utils.likelihood_models import GaussianLikelihood - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_brnn_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - train_split: float = 0.85, - past_covariates: Optional[str] = None, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - model_type: str = "LSTM", - n_rnn_layers: int = 1, - dropout: float = 0.0, - batch_size: int = 32, - n_epochs: int = 100, - learning_rate: float = 1e-3, - model_save_name: str = "brnn_model", - force_reset: bool = True, - save_checkpoints: bool = True, - metric: str = "mape", -) -> Tuple[ - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[float], - Optional[type[GlobalForecastingModel]], -]: - """Performs Block RNN forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - model_type: str - Either a string specifying the RNN module type ("RNN", "LSTM" or "GRU"). Defaults to "LSTM". - n_rnn_layers: int - Number of layers in the RNN module. Defaults to 1. - dropout: float - Fraction of neurons affected by Dropout. Defaults to 0.0. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset (all checkpoints will be - discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. Defaults to True. - metric: str - Metric to use for model selection. Defaults to "mape". - - Returns - ------- - Tuple[ - List[TimeSeries], - List[TimeSeries], - List[TimeSeries], - Optional[Union[float, ndarray]], - type[GlobalForecastingModel] - ] - Adjusted Data series, - Historical forecast by best RNN model, - list of Predictions, - Mean average precision error, - Best BRNN Model. - """ - - # TODO Check if torch GPU AVAILABLE - - use_scalers = True - probabilistic = False - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - train, val = ticker_series.split_before(train_split) - valid = helpers.check_data_length( - train, val, input_chunk_length, output_chunk_length - ) - if not valid: - return [], [], [], None, None - - ( - past_covariate_whole, - past_covariate_train, - past_covariate_val, - ) = helpers.past_covs(past_covariates, data, train_split, use_scalers) - - # Early Stopping - - brnn_model = BlockRNNModel( - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - model=model_type, - n_rnn_layers=n_rnn_layers, - dropout=dropout, - batch_size=batch_size, - n_epochs=n_epochs, - optimizer_kwargs={"lr": learning_rate}, - model_name=model_save_name, - random_state=42, - pl_trainer_kwargs=helpers.get_pl_kwargs(accelerator="cpu"), - force_reset=force_reset, - save_checkpoints=save_checkpoints, - likelihood=GaussianLikelihood(), - log_tensorboard=True, - work_dir=get_current_user().preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - # fit model on train series for historical forecasting - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - helpers.fit_model( - brnn_model, - train, - val, - past_covariate_train, - past_covariate_val, - ) - best_model = BlockRNNModel.load_from_checkpoint( - model_name=model_save_name, - best=True, - work_dir=get_current_user().preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - helpers.print_tensorboard_logs( - model_save_name, - str(get_current_user().preferences.USER_FORECAST_MODELS_DIRECTORY), - ) - - # Showing historical backtesting without retraining model (too slow) - return helpers.get_prediction( - "Block RNN", - probabilistic, - use_scalers, - scaler, - past_covariates, - best_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/brnn_view.py b/openbb_terminal/forecast/brnn_view.py deleted file mode 100644 index b32dce361fd..00000000000 --- a/openbb_terminal/forecast/brnn_view.py +++ /dev/null @@ -1,178 +0,0 @@ -"""Block RNN View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import brnn_model, helpers - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_brnn_forecast( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - model_type: str = "LSTM", - n_rnn_layers: int = 1, - dropout: float = 0.0, - batch_size: int = 32, - n_epochs: int = 100, - learning_rate: float = 1e-3, - model_save_name: str = "brnn_model", - force_reset: bool = True, - save_checkpoints: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display BRNN forecast - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - dataset_name: str - The name of the ticker to be predicted - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - model_type: str - Either a string specifying the RNN module type ("RNN", "LSTM" or "GRU"). Defaults to "LSTM". - n_rnn_layers: int - Number of layers in the RNN module. Defaults to 1. - dropout: float - Fraction of neurons affected by Dropout. Defaults to 0.0. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 101. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - export_pred_raw: bool - Whether to export the raw predicted values. Defaults to False. - metric: str - The metric to use for the model. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data( - data, start_date, end_date, target_column, past_covariates - ) - if not helpers.check_data(data, target_column, past_covariates): - return None - output_chunk_length = helpers.check_output( - output_chunk_length, n_predict, bool(past_covariates) - ) - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = brnn_model.get_brnn_data( - data=data, - n_predict=n_predict, - target_column=target_column, - past_covariates=past_covariates, - train_split=train_split, - forecast_horizon=forecast_horizon, - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - model_type=model_type, - n_rnn_layers=n_rnn_layers, - dropout=dropout, - batch_size=batch_size, - n_epochs=n_epochs, - learning_rate=learning_rate, - model_save_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - metric=metric, - ) - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="BRNN", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - metric=metric, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/expo_model.py b/openbb_terminal/forecast/expo_model.py deleted file mode 100644 index 74ace57dd59..00000000000 --- a/openbb_terminal/forecast/expo_model.py +++ /dev/null @@ -1,175 +0,0 @@ -# pylint: disable=too-many-arguments -"""Probabilistic Exponential Smoothing Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.metrics import mape, mse, rmse, smape -from darts.models import ExponentialSmoothing -from darts.utils.utils import ModelMode, SeasonalityMode -from numpy import ndarray -from statsmodels.tools.sm_exceptions import ConvergenceWarning - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -warnings.simplefilter("ignore", ConvergenceWarning) - - -TRENDS = ["N", "A", "M"] -SEASONS = ["N", "A", "M"] -PERIODS = [4, 5, 7] -DAMPEN = ["T", "F"] - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_expo_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - trend: str = "A", - seasonal: str = "A", - seasonal_periods: int = 7, - dampen: str = "F", - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - metric: str = "mape", -) -> Tuple[ - List[TimeSeries], - List[TimeSeries], - List[TimeSeries], - Optional[Union[float, ndarray]], - ExponentialSmoothing, -]: - """Performs Probabilistic Exponential Smoothing forecasting - This is a wrapper around statsmodels Holt-Winters' Exponential Smoothing; - we refer to this link for the original and more complete documentation of the parameters. - - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.exponential_smoothing.html - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - trend: str - Trend component. One of [N, A, M] - Defaults to ADDITIVE. - seasonal: str - Seasonal component. One of [N, A, M] - Defaults to ADDITIVE. - seasonal_periods: int - Number of seasonal periods in a year (7 for daily data) - If not set, inferred from frequency of the series. - dampen: str - Dampen the function - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - metric: str - Metric to use for backtesting. Defaults to MAPE. - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[Union[float, ndarray]], ExponentialSmoothing] - Adjusted Data series, - List of historical fcast values, - List of predicted fcast values, - Optional[float] - precision, - Fit Prob. Expo model object. - """ - - use_scalers = False - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - - if trend == "M": - trend_model = ModelMode.MULTIPLICATIVE - elif trend == "N": - trend_model = ModelMode.NONE - else: # Default - trend_model = ModelMode.ADDITIVE - - if seasonal == "M": - seasonal_model = SeasonalityMode.MULTIPLICATIVE - elif seasonal == "N": - seasonal_model = SeasonalityMode.NONE - else: # Default - seasonal_model = SeasonalityMode.ADDITIVE - - damped = True - if dampen == "F": - damped = False - - # Model Init - model_es = ExponentialSmoothing( - trend=trend_model, - seasonal=seasonal_model, - seasonal_periods=int(seasonal_periods), - damped=damped, - random_state=42, - ) - - try: - # Historical backtesting - historical_fcast_es = model_es.historical_forecasts( - ticker_series, # backtest on entire ts - start=float(start_window), - forecast_horizon=int(forecast_horizon), - verbose=True, - ) - except Exception as e: # noqa - error = str(e) - # lets translate this to something everyone understands - if "with`overlap_end` set to `False`." in error: - console.print( - "[red]Dataset too small.[/red]" - "[red] Please increase size to at least 100 data points.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None - - # train new model on entire timeseries to provide best current forecast - best_model = ExponentialSmoothing( - trend=trend_model, - seasonal=seasonal_model, - seasonal_periods=int(seasonal_periods), - damped=damped, - random_state=42, - ) - - # we have the historical fcast, now lets train on entire set and predict. - best_model.fit(ticker_series) - probabilistic_forecast = best_model.predict(int(n_predict), num_samples=500) - - if metric == "rmse": - precision = rmse(actual_series=ticker_series, pred_series=historical_fcast_es) - elif metric == "mse": - precision = mse(actual_series=ticker_series, pred_series=historical_fcast_es) - elif metric == "mape": - precision = mape(actual_series=ticker_series, pred_series=historical_fcast_es) - elif metric == "smape": - precision = smape(actual_series=ticker_series, pred_series=historical_fcast_es) - - console.print( - f"Exponential smoothing obtains {metric.upper()}: {precision:.2f}% \n" - ) - - return ( - ticker_series, - historical_fcast_es, - probabilistic_forecast, - precision, - best_model, - ) diff --git a/openbb_terminal/forecast/expo_view.py b/openbb_terminal/forecast/expo_view.py deleted file mode 100644 index b818cef9e47..00000000000 --- a/openbb_terminal/forecast/expo_view.py +++ /dev/null @@ -1,146 +0,0 @@ -"""Probabilistic Exponential Smoothing View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import expo_model, helpers -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_expo_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - trend: str = "A", - seasonal: str = "A", - seasonal_periods: int = 7, - dampen: str = "F", - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display Probabilistic Exponential Smoothing forecast - - Parameters - ---------- - data : Union[pd.Series, np.array] - Data to forecast - dataset_name: str - The name of the ticker to be predicted - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - trend: str - Trend component. One of [N, A, M] - Defaults to ADDITIVE. - seasonal: str - Seasonal component. One of [N, A, M] - Defaults to ADDITIVE. - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - dampen: str - Dampen the function - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - export_pred_raw: bool - Whether to export the raw predicted values. Defaults to False. - metric: str - The metric to use when backtesting. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = expo_model.get_expo_data( - data=data, - target_column=target_column, - trend=trend, - seasonal=seasonal, - seasonal_periods=seasonal_periods, - dampen=dampen, - n_predict=n_predict, - start_window=start_window, - forecast_horizon=forecast_horizon, - metric=metric, - ) - - if ticker_series == []: - return None - - probabilistic = True - fig = helpers.plot_forecast( - name="PES", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - metric=metric, - external_axes=external_axes, - ) - if residuals: - console.print("[red]Expo model does not support residuals at this time[/red]\n") - # helpers.plot_residuals( - # _model, None, ticker_series, forecast_horizon=forecast_horizon - # ) - - return fig diff --git a/openbb_terminal/forecast/forecast.ipynb b/openbb_terminal/forecast/forecast.ipynb deleted file mode 100644 index 1195b14cda5..00000000000 --- a/openbb_terminal/forecast/forecast.ipynb +++ /dev/null @@ -1,89 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "a3abc620-1a2c-4c6d-a273-b30ce74a58c9", - "metadata": {}, - "outputs": [], - "source": [ - "from datetime import datetime, timedelta\n", - "from openbb_terminal import api\n", - "import os\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib_inline.backend_inline\n", - "import ipywidgets as widgets\n", - "import plotly.graph_objs as go\n", - "from IPython.display import display\n", - "\n", - "%matplotlib inline\n", - "matplotlib_inline.backend_inline.set_matplotlib_formats(\"svg\")\n", - "api.theme.apply_style()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2bad2400-6f8a-4a8a-8161-5e319ae13ba8", - "metadata": {}, - "outputs": [], - "source": [ - "df = api.forecast.load(\"weather.csv\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5eb728df-76e3-4cf2-901b-7275b7ce8f4c", - "metadata": {}, - "outputs": [], - "source": [ - "df = api.forecast.ema(df, target_column=\"MaxTemp\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "261d1eb2-7a0b-4358-8aa5-c1bae1237dab", - "metadata": {}, - "outputs": [], - "source": [ - "api.forecast.expo(df, target_column=\"MaxTemp\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0c1d1945-f819-442a-a8e6-66822681b901", - "metadata": {}, - "outputs": [], - "source": [ - "api.forecast.brnn(\n", - " df, target_column=\"MaxTemp\", past_covariates=\"MinTemp,Rainfall,Evaporation\"\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/forecast/forecast_controller.py b/openbb_terminal/forecast/forecast_controller.py deleted file mode 100644 index 30d0835bc53..00000000000 --- a/openbb_terminal/forecast/forecast_controller.py +++ /dev/null @@ -1,3572 +0,0 @@ -"""Forecast Controller Module""" - -__docformat__ = "numpy" - -# pylint: disable=C0302,too-many-branches,too-many-arguments,R0904,R0902,W0707 -# flake8: noqa - -# IMPORT STANDARD -import argparse -import logging -from typing import Any, Dict, List, Optional - -# IMPORT THIRDPARTY -import pandas as pd -import psutil - -try: - import darts - import torch - - darts_latest = "0.23.0" - # check darts version - if darts.__version__ != darts_latest: - print(f"You are currently using Darts version {darts.__version__}") - print( - "Follow instructions on creating a new conda environment with the latest " - f"Darts version ({darts_latest}):" - ) - print("https://my.openbb.co/app/sdk/installation") -except ModuleNotFoundError: - raise ModuleNotFoundError( - "Please install the forecast version of the terminal. Instructions can be found " - "under the python tab: https://my.openbb.co/app/sdk/installation" - ) - -try: - import whisper - import transformers - from whisper.tokenizer import LANGUAGES, TO_LANGUAGE_CODE - from openbb_terminal.forecast.whisper_utils import str2bool - - transformers_ver = transformers.__version__ - # if imports are successful, set flag to True - WHISPER_AVAILABLE = True - -except ModuleNotFoundError: - raise ModuleNotFoundError( - "Please use poetry to install latest whisper model and dependencies. \n" - "poetry install -E forecast \n" - "\n" - "If you are not using poetry, please install whisper model. Instructions can be found here: \n" - "https://github.com/openai/whisper \n" - "Please install the transformers library with the following command: \n" - "pip install transformers \n" - ) - - -# ignore pylint(ungrouped-imports) -# pylint: disable=ungrouped-imports - -# IMPORT INTERNAL -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.common import common_model -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end - -from openbb_terminal.helper_funcs import ( - check_positive, - check_positive_float, - NO_EXPORT, - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - log_and_raise, - valid_date, - parse_and_split_input, - check_non_negative, - check_positive_float_list, - check_list_values, - check_valid_date, -) - -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import console, MenuText - -from openbb_terminal.forecast import ( - anom_view, - autoarima_view, - autoces_view, - autoets_view, - autoselect_view, - brnn_view, - expo_model, - expo_view, - forecast_model, - forecast_view, - helpers, - linregr_view, - mstl_view, - nbeats_view, - nhits_view, - regr_view, - rnn_view, - rwd_view, - seasonalnaive_view, - tcn_view, - tft_view, - theta_view, - trans_view, - whisper_model, - timegpt_view, -) - -logger = logging.getLogger(__name__) -empty_df = pd.DataFrame() - - -def check_greater_than_one(value) -> int: - """Argparse type to check positive int above 1""" - new_value = int(value) - if new_value <= 1: - log_and_raise( - argparse.ArgumentTypeError( - f"{value} is an invalid positive int value. Must be greater than 1." - ) - ) - return new_value - - -class ForecastController(BaseController): - """Forecast class""" - - CHOICES_COMMANDS: List[str] = [ - "load", - "show", - "plot", - "clean", - "combine", - "setndays", - "desc", - "corr", - "rename", - "delete", - "export", - "ema", - "sto", - "rsi", - "roc", - "mom", - "delta", - "atr", - "signal", - "autoselect", - "autoarima", - "autoces", - "autoets", - "mstl", - "rwd", - "seasonalnaive", - "expo", - "theta", - "rnn", - "brnn", - "nbeats", - "tcn", - "regr", - "linregr", - "trans", - "tft", - "season", - "which", - "nhits", - "anom", - "whisper", - "timegpt", - ] - pandas_plot_choices = [ - "line", - "scatter", - "bar", - "barh", - "hist", - "box", - "kde", - "area", - "pie", - "hexbin", - ] - disclaimer = """ - All models are for educational purposes only. The techniques available in this menu are not - fine tuned or guaranteed to work. Backtesting results is not a guarantee of future accuracy. - Investing involves monetary risk that OpenBB does not take a role in. Please research any - prediction techniques fully before attempting to use. OpenBB is not liable for any loss or - damages.""" - - PATH = "/forecast/" - CHOICES_GENERATION = True - - loaded_dataset_cols = "\n" - list_dataset_cols: list = list() - - def __init__( - self, - ticker: str = "", - data: pd.DataFrame = empty_df, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - self.files: List[str] = [] - self.choices = {} - # The full file name with extension, this allows the rest command to work - self.files_full: List[List[str]] = [] - self.datasets: Dict[str, pd.DataFrame] = dict() - self.MINIMUM_DATA_LENGTH = 100 - self.ndays = 5 - - if ticker and not data.empty: - data = data.reset_index() - data.columns = data.columns.map(lambda x: x.lower().replace(" ", "_")) - - self.files.append(ticker) - self.datasets[ticker] = data - self.loaded_dataset_cols = "\n" - - self.loaded_dataset_cols += ( - f" {ticker} {(20 - len(ticker)) * ' '}: " - f"{', '.join(data.columns)}\n" - ) - - for col in data.columns: - self.list_dataset_cols.append(f"{ticker}.{col}") - - self.DATA_TYPES: List[str] = ["int", "float", "str", "bool", "category", "date"] - - self.signs: Dict[Any, Any] = { - "div": "/", - "mul": "*", - "add": "+", - "sub": "-", - "mod": "%", - "pow": "**", - } - self.DATA_FILES = forecast_model.get_default_files() - - # setting device on GPU if available, else CPU - self.device = "cuda" if torch.cuda.is_available() else "cpu" - self.comp_ram = f"{round(psutil.virtual_memory().total / (1024.0 ** 3),2)}G" - self.rec_data_size = f"{(psutil.virtual_memory().total / (1024.0 ** 3))//3}G" - self.torch_version = torch.__version__ - self.darts_version = darts.__version__ - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.choices = choices - self.completer = NestedCompleter.from_nested_dict(choices) - - def call_exit(self, _) -> None: - """Process exit terminal command from forecast menu.""" - self.save_class() - for _ in range(self.PATH.count("/") + 1): - self.queue.insert(0, "quit") - - def get_dataset_columns(self): - return { - f"{dataset}.{column}": {column: None, dataset: None} - for dataset, dataframe in self.datasets.items() - for column in dataframe.columns - } - - def parse_input(self, an_input: str) -> List: - """Parse controller input - - Overrides the parent class function to handle YouTube video URL conventions. - See `BaseController.parse_input()` for details. - """ - - # Filtering out YouTube video parameters like "v=" and removing the domain name - youtube_filter = r"(youtube\.com/watch\?v=)" - - custom_filters = [youtube_filter] - - commands = parse_and_split_input( - an_input=an_input.replace("https://", ""), custom_filters=custom_filters - ) - return commands - - def update_runtime_choices(self): - # Load in any newly exported files - self.DATA_FILES = forecast_model.get_default_files() - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default # type: ignore - - self.choices = choices - self.completer = NestedCompleter.from_nested_dict(choices) - - def refresh_datasets_on_menu(self): - """Refresh datasets on menu with new columns when adding new features""" - - self.list_dataset_cols = list() - maxfile = max(len(file) for file in self.files) - self.loaded_dataset_cols = "\n" - for dataset, data in self.datasets.items(): - self.loaded_dataset_cols += ( - f" {dataset} {(maxfile - len(dataset)) * ' '}: " - f"{', '.join(data.columns)}\n" - ) - - for col in data.columns: - self.list_dataset_cols.append(f"{dataset}.{col}") - - def print_help(self): - """Print help""" - self.update_runtime_choices() - current_user = get_current_user() - mt = MenuText("forecast/") - mt.add_param("_disclaimer_", self.disclaimer) - mt.add_raw("\n") - mt.add_param( - "_data_loc", - f"\n\t{current_user.preferences.USER_EXPORTS_DIRECTORY}\n" - f"\t{current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY}", - ) - mt.add_raw("\n") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_loaded", self.loaded_dataset_cols) - mt.add_info("_exploration_") - mt.add_cmd("show", self.files) - mt.add_cmd("plot", self.files) - mt.add_cmd("clean", self.files) - mt.add_cmd("combine", self.files) - mt.add_cmd("setndays", self.ndays) - mt.add_cmd("desc", self.files) - mt.add_cmd("corr", self.files) - mt.add_cmd("season", self.files) - mt.add_cmd("delete", self.files) - mt.add_cmd("rename", self.files) - mt.add_cmd("export", self.files) - mt.add_raw("\n") - mt.add_info("_feateng_") - mt.add_cmd("ema", self.files) - mt.add_cmd("sto", self.files) - mt.add_cmd("rsi", self.files) - mt.add_cmd("roc", self.files) - mt.add_cmd("mom", self.files) - mt.add_cmd("delta", self.files) - mt.add_cmd("atr", self.files) - mt.add_cmd("signal", self.files) - mt.add_raw("\n") - mt.add_info("_tsforecasting_") - mt.add_cmd("autoselect", self.files) - mt.add_cmd("autoarima", self.files) - mt.add_cmd("autoces", self.files) - mt.add_cmd("autoets", self.files) - mt.add_cmd("mstl", self.files) - mt.add_cmd("rwd", self.files) - mt.add_cmd("seasonalnaive", self.files) - mt.add_cmd("expo", self.files) - mt.add_cmd("theta", self.files) - mt.add_cmd("linregr", self.files) - mt.add_cmd("regr", self.files) - mt.add_cmd("rnn", self.files) - mt.add_cmd("brnn", self.files) - mt.add_cmd("nbeats", self.files) - mt.add_cmd("nhits", self.files) - mt.add_cmd("tcn", self.files) - mt.add_cmd("trans", self.files) - mt.add_cmd("tft", self.files) - mt.add_raw("\n") - mt.add_info("_anomaly_") - mt.add_cmd("anom", self.files) - mt.add_raw("\n") - mt.add_info("_misc_") - mt.add_cmd("timegpt", self.files) - mt.add_cmd("whisper", WHISPER_AVAILABLE) - - console.print(text=mt.menu_text, menu="Forecast") - - def custom_reset(self): - """Class specific component of reset command""" - if self.files_full: - queue = ["forecast"] - for file, alias in self.files_full: - load = f"'load {file}" - if alias: - load += f" -a {alias}'" - else: - load += "'" - queue.append(load) - return queue - return [] - - def add_standard_args( - self, - parser: argparse.ArgumentParser, - target_dataset: bool = False, - target_column: bool = False, - period: Optional[int] = None, - n_days: bool = False, - seasonal: Optional[str] = None, - periods: bool = False, - window: bool = False, - train_split: bool = False, - input_chunk_length: bool = False, - output_chunk_length: bool = False, - force_reset: bool = False, - save_checkpoints: bool = False, - model_save_name: Optional[str] = None, - n_epochs: bool = False, - model_type: bool = False, - dropout: Optional[float] = None, - batch_size: Optional[int] = None, - learning_rate: bool = False, - past_covariates: bool = False, - all_past_covariates: bool = False, - lags: bool = False, - hidden_size: int = 0, - n_jumps: bool = False, - end: bool = False, - start: bool = False, - residuals: bool = False, - forecast_only: bool = False, - naive: bool = False, - explainability_raw: bool = False, - export_pred_raw: bool = False, - metric: bool = False, - ): - if hidden_size: - parser.add_argument( - "--hidden-size", - action="store", - dest="hidden_size", - default=hidden_size, - type=check_positive, - help="Size for feature maps for each hidden RNN layer (h_n)", - ) - if past_covariates: - parser.add_argument( - "--past-covariates", - action="store", - dest="past_covariates", - default=None, - type=str, - help="Past covariates(columns/features) in same dataset. Comma separated.", - ) - if all_past_covariates: - parser.add_argument( - "--all-past-covariates", - action="store_true", - dest="all_past_covariates", - default=False, - help="Adds all rows as past covariates except for date and the target column.", - ) - if naive: - parser.add_argument( - "--naive", - action="store_true", - dest="naive", - default=False, - help="Show the naive baseline for a model.", - ) - if target_dataset: - parser.add_argument( - "-d", - "--dataset", - help="The name of the dataset you want to select", - dest="target_dataset", - type=str, - choices=list(self.datasets.keys()), - ) - if target_column: - parser.add_argument( - "-c", - "--target-column", - help="The name of the specific column you want to use", - dest="target_column", - type=str, - default="close", - ) - if period is not None: - parser.add_argument( - "--period", - help="The period to use", - dest="period", - type=check_greater_than_one, - default=period, - ) - if n_days: - parser.add_argument( - "-n", - "--n-days", - action="store", - dest="n_days", - type=check_positive, - default=self.ndays, - help="prediction days.", - ) - if seasonal is not None: - parser.add_argument( - "-s", - "--seasonal", - action="store", - dest="seasonal", - choices=expo_model.SEASONS, - default=seasonal, - help="Seasonality: N: None, A: Additive, M: Multiplicative.", - ) - if periods: - parser.add_argument( - "-p", - "--periods", - action="store", - dest="seasonal_periods", - type=check_positive, - default=7, - help="Seasonal periods: 4: Quarterly, 7: Daily", - ) - if window: - parser.add_argument( - "-w", - "--window", - action="store", - dest="start_window", - default=0.85, - type=check_positive_float, - help="Start point for rolling training and forecast window. 0.0-1.0", - ) - if train_split: - parser.add_argument( - "-t", - "--train-split", - action="store", - dest="train_split", - default=0.85, - type=check_positive_float, - help="Start point for rolling training and forecast window. 0.0-1.0", - ) - if input_chunk_length: - parser.add_argument( - "-i", - "--input-chunk-length", - action="store", - dest="input_chunk_length", - default=14, - type=check_positive, - help="Number of past time steps for forecasting module at prediction time.", - ) - if output_chunk_length: - parser.add_argument( - "-o", - "--output-chunk-length", - action="store", - dest="output_chunk_length", - default=5, - type=check_positive, - help="The length of the forecast of the model.", - ) - if force_reset: - parser.add_argument( - "--force-reset", - action="store", - dest="force_reset", - default=True, - type=bool, - help="""If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded).""", - ) - if save_checkpoints: - parser.add_argument( - "--save-checkpoints", - action="store", - dest="save_checkpoints", - default=True, - type=bool, - help="Whether to automatically save the untrained model and checkpoints.", - ) - if model_save_name is not None: - parser.add_argument( - "--model-save-name", - type=str, - action="store", - dest="model_save_name", - default=model_save_name, - help="Name of the model to save.", - ) - if n_epochs: - parser.add_argument( - "--n-epochs", - action="store", - dest="n_epochs", - default=300, - type=check_positive, - help="Number of epochs over which to train the model.", - ) - if model_type: - parser.add_argument( - "--model-type", - type=str, - action="store", - dest="model_type", - default="LSTM", - help='Enter a string specifying the RNN module type ("RNN", "LSTM" or "GRU")', - ) - if dropout is not None: - parser.add_argument( - "--dropout", - action="store", - dest="dropout", - default=dropout, - type=check_positive_float, - help="Fraction of neurons affected by Dropout, from 0 to 1.", - ) - if batch_size is not None: - parser.add_argument( - "--batch-size", - action="store", - dest="batch_size", - default=batch_size, - type=check_positive, - help="Number of time series (input and output) used in each training pass", - ) - if end: - parser.add_argument( - "--end", - action="store", - type=valid_date, - dest="s_end_date", - default=None, - help="The end date (format YYYY-MM-DD) to select for testing", - ) - if start: - parser.add_argument( - "--start", - action="store", - type=valid_date, - dest="s_start_date", - default=None, - help="The start date (format YYYY-MM-DD) to select for testing", - ) - if learning_rate: - parser.add_argument( - "--learning-rate", - action="store", - dest="learning_rate", - default=1e-3, - type=check_positive_float, - help="Learning rate during training.", - ) - if n_jumps: - parser.add_argument( - "-j", - "--jumps", - action="store", - dest="n_jumps", - type=check_positive, - default=1, - help="number of jumps in training data.", - ) - if lags: - parser.add_argument( - "--lags", - action="store", - dest="lags", - type=check_greater_than_one, - default=14, - help="Lagged target values used to predict the next time step.", - ) - if residuals: - parser.add_argument( - "--residuals", - help="Show the residuals for the model.", - action="store_true", - default=False, - dest="residuals", - ) - if forecast_only: - parser.add_argument( - "--forecast-only", - help="Do not plot the historical data without forecasts.", - action="store_true", - default=False, - dest="forecast_only", - ) - if explainability_raw: - parser.add_argument( - "--explainability-raw", - action="store_true", - dest="explainability_raw", - default=False, - help="Prints out a raw dataframe showing explainability results.", - ) - - if export_pred_raw: - parser.add_argument( - "--export-pred-raw", - action="store_true", - dest="export_pred_raw", - default=False, - help="Export predictions to a csv file.", - ) - - if metric: - parser.add_argument( - "--metric", - type=str, - action="store", - dest="metric", - default="mape", - choices=["rmse", "mse", "mape", "smape"], - help="Calculate precision based on a specific metric (rmse, mse, mape)", - ) - - # if user does not put in --dataset - return parser - - def load(self, ticker: str, data: pd.DataFrame): - """Loads news dataframes into memory""" - - # check if data has minimum number of rows - if ticker and len(data) < self.MINIMUM_DATA_LENGTH: - console.print( - f"[red]Dataset is smaller than recommended minimum {self.MINIMUM_DATA_LENGTH} data points. [/red]" - ) - console.print( - f"[red]Please increase the number of data points for [ {ticker} ] and try again.[/red]" - ) - return - - if not data.empty: - data.columns = data.columns.map(lambda x: x.lower().replace(" ", "_")) - - # If the index is a date, move this into a normal column - if data.index.name == "date": - data = data.reset_index() - # Convert date to datetime - # TODO: for now we drop time, once we add handling for time remove this - if "date" in data.columns: - data["date"] = data["date"].apply(helpers.dt_format) - data["date"] = pd.to_datetime(data["date"]) - - # if we import a custom dataset, remove the old index "unnamed:_0" - if "unnamed:_0" in data.columns: - # Some loaded datasets have the date as unnamed, which is not helpful - if check_valid_date(data["unnamed:_0"].iloc[0]): - data["date"] = data["unnamed:_0"].copy() - data = data.drop(columns=["unnamed:_0"]) - - self.files.append(ticker) - self.datasets[ticker] = data - - # Process new datasets to be updated - self.list_dataset_cols = list() - maxfile = max(len(file) for file in self.files) - self.loaded_dataset_cols = "\n" - for dataset, x_data in self.datasets.items(): - self.loaded_dataset_cols += ( - f" {dataset} {(maxfile - len(dataset)) * ' '}: " - f"{', '.join(x_data.columns)}\n" - ) - - for col in data.columns: - self.list_dataset_cols.append(f"{dataset}.{col}") - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - """Process load""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Load custom dataset (from previous export, custom imports).", - ) - parser.add_argument( - "-f", - "--file", - help="File to load data in (can be custom import, may have been exported before.)", - choices=self.DATA_FILES.keys(), - type=str, - ) - parser.add_argument( - "-a", - "--alias", - help="Alias name to give to the dataset", - type=str, - ) - parser.add_argument( - "--sheet-name", - dest="sheet_name", - default=None, - nargs="+", - help="Name of excel sheet to save data to. Only valid for .xlsx files.", - ) - - # Load in any newly exported files - self.DATA_FILES = forecast_model.get_default_files() - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-f") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if ns_parser.file: - file = ns_parser.file - if ns_parser.alias: - alias = ns_parser.alias - else: - if "." in ns_parser.file: - alias = ".".join(ns_parser.file.split(".")[:-1]) - else: - alias = ns_parser.file - - # check if this dataset has been added already - if alias in self.files: - console.print( - "[red]The file/dataset selected has already been loaded.[/red]\n" - ) - return - data = common_model.load( - file, - data_files=self.DATA_FILES, - data_examples={}, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - if not data.empty: - self.files_full.append([ns_parser.file, ns_parser.alias]) - self.load(alias, data) - self.update_runtime_choices() - - # Show selected dataframe on console - @log_start_end(log=logger) - def call_which(self, other_args: List[str]): - """Process which command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="which", - description="Show library versions of required packages.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - ) - - if ns_parser: - console.print( - f"[green]Current Compute Device (CPU or GPU):[/green] {self.device.upper()}" - ) - console.print(f"[green]Current RAM:[/green] {self.comp_ram}") - console.print( - f"[green]Recommended Max dataset size based on current RAM:[/green] {self.rec_data_size}" - ) - console.print(f"[green]Torch version:[/green] {self.torch_version}") - console.print(f"[green]Darts version:[/green] {self.darts_version}") - - # Show selected dataframe on console - @log_start_end(log=logger) - def call_show(self, other_args: List[str]): - """Process show command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="show", - description="Show a portion of the DataFrame", - ) - parser.add_argument( - "-n", - "--name", - type=str, - choices=self.files, - dest="name", - help="The name of the database you want to show data for", - ) - parser.add_argument( - "-s", - "--sortby", - help="Sort based on a column in the DataFrame", - nargs="+", - type=str, - dest="sortby", - default="", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "--limit-col", - action="store", - dest="limit_col", - default=10, - type=check_positive, - help="Set the number of columns to display when showing the dataset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=10, - ) - - if ns_parser: - if not ns_parser.name: - dataset_names = list(self.datasets.keys()) - else: - dataset_names = [ns_parser.name] - - for name in dataset_names: - df = self.datasets[name] - - if name in self.datasets and self.datasets[name].empty: - console.print( - f"[red]No data available for {ns_parser.name}.[/red]\n" - ) - elif ns_parser.sortby: - sort_column = " ".join(ns_parser.sortby) - if sort_column not in self.datasets[name].columns: - console.print( - f"[red]{sort_column} not a valid column." - "Showing without sorting.\n[/red]" - ) - else: - df = df.sort_values(by=sort_column, ascending=ns_parser.reverse) - - forecast_view.show_df( - df, ns_parser.limit, ns_parser.limit_col, name, ns_parser.export - ) - - @log_start_end(log=logger) - def call_rename(self, other_args: List[str]): - """Process rename""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rename", - description="The column you want to rename from a dataset.", - ) - parser.add_argument( - "-d", - "--dataset", - help="Dataset that will get a column renamed", - dest="dataset", - choices=self.datasets.keys(), - type=str, - ) - parser.add_argument( - "-o", - "--oldcol", - help="Old column from dataset to be renamed", - dest="oldcol", - type=str, - required="-h" not in other_args, - ) - parser.add_argument( - "-n", - "--newcol", - help="New column from dataset to be renamed", - dest="newcol", - type=str, - required="-h" not in other_args, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - dataset = ns_parser.dataset - column_old = ns_parser.oldcol - column_new = ns_parser.newcol - - if dataset not in self.datasets: - console.print( - f"Not able to find the dataset {dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - else: - self.datasets[dataset] = forecast_model.rename_column( - self.datasets[dataset], column_old, column_new - ) - self.update_runtime_choices() - self.refresh_datasets_on_menu() - - console.print( - f"[green]Successfully renamed {column_old} into {column_new}, in {dataset}[/green]" - ) - - @log_start_end(log=logger) - def call_setndays(self, other_args: List[str]): - """Process setndays command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="setndays", - description="Set the number of days to forecast", - ) - parser.add_argument( - "-n", - "--n-days", - help="Number of days to forecast", - dest="n_days", - type=check_positive, - default=5, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - self.ndays = ns_parser.n_days - console.print( - f"[green]Number of days to forecast set to {self.ndays}[/green]" - ) - self.update_runtime_choices() - - # Show selected dataframe on console - @log_start_end(log=logger) - def call_desc(self, other_args: List[str]): - """Process descriptive stats command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="desc", - description="Show descriptive statistics of a dataset", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args(parser, target_dataset=True) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - - if ns_parser: - # check proper file name is provided - if not ns_parser.target_dataset: - console.print( - "[red]Please select a valid dataset with the -d flag.\n[/red]" - ) - return - - df = self.datasets[ns_parser.target_dataset] - forecast_view.describe_df( - df, - ns_parser.target_dataset, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_plot(self, other_args: List[str]): - """Process plot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="plot", - description="Plot data based on the index", - ) - parser.add_argument( - "-v", - "--values", - help="Dataset.column values to be displayed in a plot. Use comma to separate multiple", - choices=self.get_dataset_columns(), - dest="values", - type=str, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - if not ns_parser: - return - - if not ns_parser.values: - console.print( - "[red]Please select a valid dataset with the -d flag.\n[/red]" - ) - return - - values = [x.strip() for x in ns_parser.values.split(",")] - target_df = values[0].split(".")[0] - if target_df not in self.datasets: - console.print( - "[red]Please select a valid dataset with the -d flag.\n[/red]" - ) - return - - for value in values: - if value.split(".")[0] != target_df: - console.print("[red]Please enter values from the same dataset.\n[/red]") - return - - forecast_view.display_plot( - self.datasets[target_df], - [x.split(".")[1] for x in values], - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_season(self, other_args: List[str]): - """Process season command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="season", - description="The seasonality for a given column", - ) - parser.add_argument( - "-d", - "--dataset", - help="Dataset.column values to be displayed in a plot", - dest="values", - choices=self.get_dataset_columns(), - type=str, - ) - parser.add_argument( - "-m", - help="A time lag to highlight on the plot", - dest="m", - type=int, - default=None, - ) - parser.add_argument( - "--max_lag", - help="The maximal lag order to consider", - dest="max_lag", - type=int, - default=24, - ) - parser.add_argument( - "-a", - "--alpha", - help="The confidence interval to display", - dest="alpha", - type=float, - default=0.05, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - - if not ns_parser: - return - - if not ns_parser.values: - console.print( - "[red]Please select a valid dataset with the -d flag.\n[/red]" - ) - return - - try: - dataset, col = ns_parser.values.split(".") - data = self.datasets[dataset] - except ValueError: - console.print("[red]Please enter 'dataset'.'column'.[/red]\n") - return - - forecast_view.display_seasonality( - data=data, - column=col, - export=ns_parser.export, - m=ns_parser.m, - max_lag=ns_parser.max_lag, - alpha=ns_parser.alpha, - ) - - @log_start_end(log=logger) - def call_corr(self, other_args: List[str]): - """Process correlation command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="corr", - description="Plot correlation coefficients.", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args(parser, target_dataset=True) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - # check proper file name is provided - if not ns_parser.target_dataset: - console.print( - "[red]Please select a valid dataset with the -d flag.\n[/red]" - ) - return - - data = self.datasets[ns_parser.target_dataset] - - forecast_view.display_corr( - data, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_combine(self, other_args: List[str]): - """Process combine""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="combine", - description="Combine two entire datasets, or add specific columns. Add specific" - "columns with the syntax: ", - ) - parser.add_argument( - "--dataset", - help="Dataset to add columns to", - dest="dataset", - choices=list(self.datasets.keys()), - ) - # pylint: disable=consider-using-dict-items - column_choices = [ - f"{x}.{y}" for x in self.datasets for y in self.datasets[x].columns - ] - column_choices.extend(list(self.datasets)) - parser.add_argument( - "-c", - "--columns", - help="The columns we want to add ", - dest="columns", - choices=column_choices, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - if ns_parser.dataset not in self.datasets: - console.print( - f"Not able to find the dataset {ns_parser.dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - return - - data = self.datasets[ns_parser.dataset] - - for option in ns_parser.columns.split(","): - if "." in option: - dataset, col = option.split(".") - columns = [col] - else: - dataset = option - columns = [x for x in self.datasets[dataset].columns if x != "date"] - - if dataset not in self.datasets: - console.print( - f"Not able to find the dataset {dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - continue - for column in columns: - data = forecast_model.combine_dfs( - data, self.datasets[dataset], column, dataset - ) - - self.datasets[ns_parser.dataset] = data - self.update_runtime_choices() - self.refresh_datasets_on_menu() - - console.print( - f"[green]Successfully added {ns_parser.columns} into {ns_parser.dataset}[/green]" - ) - - @log_start_end(log=logger) - def call_clean(self, other_args: List[str]): - """Process clean""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="clean", - description="Clean a dataset by filling and dropping NaN values.", - ) - parser.add_argument( - "-f", - "--fill", - help="The method of filling NaNs. This has options to fill rows (rfill, rbfill, rffill) or fill " - "columns (cfill, cbfill, cffill). Furthermore, it has the option to forward fill and backward fill " - "(up to --limit) which refer to how many rows/columns can be set equal to the last non-NaN value", - dest="fill", - choices=["rfill", "cfill", "rbfill", "cbfill", "rffill", "bffill"], - default="", - ) - parser.add_argument( - "--drop", - help="The method of dropping NaNs. This either has the option rdrop (drop rows that contain NaNs) " - "or cdrop (drop columns that contain NaNs)", - dest="drop", - choices=["rdrop", "cdrop"], - default="", - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args(parser, target_dataset=True) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - NO_EXPORT, - limit=5, - ) - if ns_parser: - # check proper file name is provided - if not ns_parser.target_dataset: - console.print( - "[red]Please select a valid dataset with the -d flag.\n[/red]" - ) - return - - ( - self.datasets[ns_parser.target_dataset], - clean_status, - ) = forecast_model.clean( - self.datasets[ns_parser.target_dataset], - ns_parser.fill, - ns_parser.drop, - ns_parser.limit, - ) - if not clean_status: - console.print( - f"[green]Successfully cleaned '{ns_parser.target_dataset}' dataset[/green]" - ) - else: - console.print(f"[red]{ns_parser.name} still contains NaNs.[/red]") - - @log_start_end(log=logger) - def call_ema(self, other_args: List[str]): - """Process EMA""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ema", - description="Add exponential moving average to dataset based on specific column.", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, target_dataset=True, period=10, target_column=True - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - NO_EXPORT, - ) - if ns_parser: - # check proper file name is provided - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - self.datasets[ns_parser.target_dataset] = forecast_model.add_ema( - self.datasets[ns_parser.target_dataset], - ns_parser.target_column, - ns_parser.period, - ) - console.print( - f"Successfully added 'EMA_{ns_parser.period}' to '{ns_parser.target_dataset}' dataset" - ) - - # update forecast menu with new column on modified dataset - self.refresh_datasets_on_menu() - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_sto(self, other_args: List[str]): - """Process Stochastic Oscillator""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sto", - description="Add in Stochastic Oscillator %K and %D", - ) - parser.add_argument( - "--close-col", - help="Close column name to use for Stochastic Oscillator", - dest="close_col", - type=str, - default="close", - ) - parser.add_argument( - "--high-col", - help="High column name to use for Stochastic Oscillator", - dest="high_col", - type=str, - default="high", - ) - parser.add_argument( - "--low-col", - help="Low column name to use for Stochastic Oscillator", - dest="low_col", - type=str, - default="low", - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args(parser, target_dataset=True, period=10) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, NO_EXPORT, limit=5 - ) - if ns_parser: - # check proper file name is provided - if not ns_parser.target_dataset: - console.print( - "[red]Please select a valid dataset with the -d flag.\n[/red]" - ) - return - - df = forecast_model.add_sto( - self.datasets[ns_parser.target_dataset], - close_column=ns_parser.close_col, - high_column=ns_parser.high_col, - low_column=ns_parser.low_col, - period=ns_parser.period, - ) - if not df.empty: - self.datasets[ns_parser.target_dataset] = df - console.print( - f"Successfully added 'STOCH&D_{ns_parser.period}' to '{ns_parser.target_dataset}' dataset" - ) - # update forecast menu with new column on modified dataset - self.refresh_datasets_on_menu() - self.update_runtime_choices() - - def handle_delete(self, dataset, column): - if dataset not in self.datasets: - console.print( - f"Not able to find the dataset {dataset}. Please choose one of " - f"the following: {', '.join(self.datasets)}" - ) - else: - forecast_model.delete_column(self.datasets[dataset], column) - - @log_start_end(log=logger) - def call_delete(self, other_args: List[str]): - """Process delete""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="delete", - description="The column you want to delete from a dataset.", - ) - parser.add_argument( - "--delete", - help="The columns you want to delete from a dataset. Use format: or" - " multiple with ,", - dest="delete", - choices=self.get_dataset_columns(), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--delete") - ns_parser = self.parse_known_args_and_warn(parser, other_args, NO_EXPORT) - - if ns_parser: - if "," in ns_parser.delete: - for option in ns_parser.delete: - print(option) - dataset, column = option.split(".") - self.handle_delete(dataset, column) - else: - dataset, column = ns_parser.delete.split(".") - self.handle_delete(dataset, column) - - self.update_runtime_choices() - self.refresh_datasets_on_menu() - - console.print(f"[green]Successfully deleted {ns_parser.delete}[/green]") - - @log_start_end(log=logger) - def call_rsi(self, other_args: List[str]): - """Process RSI""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rsi", - description="Add rsi to dataset based on specific column.", - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, target_dataset=True, target_column=True, period=10 - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - NO_EXPORT, - ) - if ns_parser: - # check proper file name is provided - if not ns_parser.target_dataset: - console.print( - "[red]Please select a valid dataset with the -d flag.\n[/red]" - ) - return - - self.datasets[ns_parser.target_dataset] = forecast_model.add_rsi( - self.datasets[ns_parser.target_dataset], - ns_parser.target_column, - ns_parser.period, - ) - console.print( - f"Successfully added 'RSI_{ns_parser.period}_{ns_parser.target_column}' " - f"to '{ns_parser.target_dataset}' dataset" - ) - - # update forecast menu with new column on modified dataset - self.refresh_datasets_on_menu() - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_roc(self, other_args: List[str]): - """Process ROC""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="roc", - description="Add rate of change to dataset based on specific column.", - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, target_dataset=True, target_column=True, period=10 - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - NO_EXPORT, - ) - if ns_parser: - # check proper file name is provided - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - self.datasets[ns_parser.target_dataset] = forecast_model.add_roc( - self.datasets[ns_parser.target_dataset], - ns_parser.target_column, - ns_parser.period, - ) - console.print( - f"Successfully added 'ROC_{ns_parser.period}' to '{ns_parser.target_dataset}' dataset" - ) - - # update forecast menu with new column on modified dataset - self.refresh_datasets_on_menu() - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_mom(self, other_args: List[str]): - """Process Momentum""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mom", - description="Add momentum to dataset based on specific column.", - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, target_dataset=True, target_column=True, period=10 - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - NO_EXPORT, - ) - if ns_parser: - # check proper file name is provided - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - self.datasets[ns_parser.target_dataset] = forecast_model.add_momentum( - self.datasets[ns_parser.target_dataset], - ns_parser.target_column, - ns_parser.period, - ) - console.print( - f"Successfully added 'Momentum_{ns_parser.period}' to '{ns_parser.target_dataset}' dataset" - ) - - # update forecast menu with new column on modified dataset - self.refresh_datasets_on_menu() - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_delta(self, other_args: List[str]): - """Process %Change (Delta)""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="delta", - description="Add %Change (Delta) to dataset based on specific column.", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args(parser, target_dataset=True, target_column=True) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - NO_EXPORT, - ) - if ns_parser: - # check proper file name is provided - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - self.datasets[ns_parser.target_dataset] = forecast_model.add_delta( - self.datasets[ns_parser.target_dataset], ns_parser.target_column - ) - console.print( - f"Successfully added 'Delta_{ns_parser.target_column}' to '{ns_parser.target_dataset}' dataset" - ) - - # update forecast menu with new column on modified dataset - self.refresh_datasets_on_menu() - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_atr(self, other_args: List[str]): - """Process Average True Range""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="atr", - description="Add Average True Range to dataset of specific stock ticker.", - ) - parser.add_argument( - "--close-col", - help="Close column name to use for Average True Range.", - dest="close_col", - type=str, - default="close", - ) - parser.add_argument( - "--high-col", - help="High column name to use for Average True Range.", - dest="high_col", - type=str, - default="high", - ) - parser.add_argument( - "--low-col", - help="Low column name to use for Average True Range.", - dest="low_col", - type=str, - default="low", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args(parser, target_dataset=True, target_column=True) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - NO_EXPORT, - ) - if ns_parser: - # check proper file name is provided - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - self.datasets[ns_parser.target_dataset] = forecast_model.add_atr( - self.datasets[ns_parser.target_dataset], - close_column=ns_parser.close_col, - high_column=ns_parser.high_col, - low_column=ns_parser.low_col, - ) - # check if true range was added - if "true_range" in self.datasets[ns_parser.target_dataset].columns: - console.print( - f"Successfully added 'Average True Range' to '{ns_parser.target_dataset}' dataset" - ) - else: - console.print( - "Could not add 'Average True Range' as it does not have one/all specific columns (low/close/high)" - ) - - # update forecast menu with new column on modified dataset - self.refresh_datasets_on_menu() - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_signal(self, other_args: List[str]): - """Process Price Signal""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="signal", - description="""Add price signal to dataset based on closing price. - 1 if the signal is that short term price will go up as compared to the long term. - 0 if the signal is that short term price will go down as compared to the long term. - """, - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args(parser, target_dataset=True, target_column=True) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - NO_EXPORT, - ) - if ns_parser: - # check proper file name is provided - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - self.datasets[ns_parser.target_dataset] = forecast_model.add_signal( - self.datasets[ns_parser.target_dataset], - target_column=ns_parser.target_column, - ) - console.print( - f"Successfully added 'Price Signal' to '{ns_parser.target_dataset}' dataset" - ) - - # update forecast menu with new column on modified dataset - self.refresh_datasets_on_menu() - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_export(self, other_args: List[str]): - """Process export command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="export", - description="Export dataset to Excel", - ) - parser.add_argument( - "-t", - "--type", - help="The file type you wish to export to", - dest="type", - choices=common_model.file_types, - type=str, - default="xlsx", - ) - parser.add_argument( - "--sheet-name", - help="The name of the sheet to export to when type is XLSX.", - dest="sheet_name", - type=str, - default="", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args(parser, target_dataset=True) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=NO_EXPORT, - ) - - if not helpers.check_parser_input(ns_parser, self.datasets, "ignore_column"): - return - - forecast_view.export_df( - self.datasets[ns_parser.target_dataset], - ns_parser.type, - ns_parser.target_dataset, - ns_parser.sheet_name, - ) - - # Best Statistical Model - @log_start_end(log=logger) - def call_autoselect(self, other_args: List[str]): - """Process autoselect command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="autoselect", - description=""" - Perform Automatic Statistical Forecast - (select best statistical model from AutoARIMA, AutoETS, AutoCES, MSTL, ...) - """, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - n_days=True, - seasonal="A", - periods=True, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - # TODO Convert this to multi series - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - autoselect_view.display_autoselect_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - seasonal_periods=ns_parser.seasonal_periods, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - ) - - # AutoARIMA Model - @log_start_end(log=logger) - def call_autoarima(self, other_args: List[str]): - """Process autoarima command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="autoarima", - description=""" - Perform Automatic ARIMA forecast: - https://nixtla.github.io/statsforecast/examples/getting_started_with_auto_arima_and_ets.html - """, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - n_days=True, - seasonal="A", - periods=True, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - # TODO Convert this to multi series - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - autoarima_view.display_autoarima_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - seasonal_periods=ns_parser.seasonal_periods, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - ) - - # AutoCES Model - @log_start_end(log=logger) - def call_autoces(self, other_args: List[str]): - """Process autoces command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="autoces", - description=""" - Perform Automatic Complex Exponential Smoothing forecast: - https://nixtla.github.io/statsforecast/models.html#autoces - """, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - n_days=True, - seasonal="A", - periods=True, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - # TODO Convert this to multi series - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - autoces_view.display_autoces_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - seasonal_periods=ns_parser.seasonal_periods, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - ) - - # AutoETS Model - @log_start_end(log=logger) - def call_autoets(self, other_args: List[str]): - """Process autoets command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="autoets", - description=""" - Perform Automatic ETS (Error, Trend, Seasonality) forecast: - https://nixtla.github.io/statsforecast/examples/getting_started_with_auto_arima_and_ets.html - """, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - n_days=True, - seasonal="A", - periods=True, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - # TODO Convert this to multi series - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - autoets_view.display_autoets_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - seasonal_periods=ns_parser.seasonal_periods, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - ) - - # MSTL Model - @log_start_end(log=logger) - def call_mstl(self, other_args: List[str]): - """Process mstl command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="mstl", - description=""" - Perform Multiple Seasonalities and Trend using Loess (MSTL) forecast: - https://nixtla.github.io/statsforecast/examples/multipleseasonalities.html - """, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - n_days=True, - seasonal="A", - periods=True, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - # TODO Convert this to multi series - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - mstl_view.display_mstl_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - seasonal_periods=ns_parser.seasonal_periods, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - ) - - # RWD Model - @log_start_end(log=logger) - def call_rwd(self, other_args: List[str]): - """Process rwd command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="rwd", - description=""" - Perform Random Walk with Drift forecast: - https://nixtla.github.io/statsforecast/models.html#randomwalkwithdrift - """, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - n_days=True, - seasonal="A", - periods=False, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - # TODO Convert this to multi series - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - rwd_view.display_rwd_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - ) - - # SeasonalNaive Model - @log_start_end(log=logger) - def call_seasonalnaive(self, other_args: List[str]): - """Process seasonalnaive command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="seasonalnaive", - description=""" - Perform SeasonalNaive forecasting: - https://nixtla.github.io/statsforecast/models.html#seasonalnaive - """, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - n_days=True, - seasonal="A", - periods=True, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - # TODO Convert this to multi series - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - seasonalnaive_view.display_seasonalnaive_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - seasonal_periods=ns_parser.seasonal_periods, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - ) - - # EXPO Model - @log_start_end(log=logger) - def call_expo(self, other_args: List[str]): - """Process expo command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="expo", - description=""" - Perform Probabilistic Exponential Smoothing forecast: - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.exponential_smoothing.html - """, - ) - parser.add_argument( - "--trend", - action="store", - dest="trend", - choices=expo_model.TRENDS, - default="A", - help="Trend: N: None, A: Additive, M: Multiplicative.", - ) - parser.add_argument( - "--dampen", - action="store", - dest="dampen", - default="F", - help="Dampening", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - n_days=True, - seasonal="A", - periods=True, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - # TODO Convert this to multi series - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - expo_view.display_expo_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - trend=ns_parser.trend, - seasonal=ns_parser.seasonal, - seasonal_periods=ns_parser.seasonal_periods, - dampen=ns_parser.dampen, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_theta(self, other_args: List[str]): - """Process theta command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="theta", - description=""" - Perform Theta forecast: - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.theta.html - """, - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - n_days=True, - target_column=True, - seasonal="M", - periods=True, - window=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - theta_view.display_theta_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - seasonal=ns_parser.seasonal, - seasonal_periods=ns_parser.seasonal_periods, - start_window=ns_parser.start_window, - forecast_horizon=ns_parser.n_days, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_rnn(self, other_args: List[str]): - """Process RNN command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="rnn", - description=""" - Perform RNN forecast (Vanilla RNN, LSTM, GRU): - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.rnn_model.html - """, - ) - # RNN Hyperparameters - parser.add_argument( - "--hidden-dim", - action="store", - dest="hidden_dim", - default=20, - type=check_positive, - help="Size for feature maps for each hidden RNN layer (h_n)", - ) - parser.add_argument( - "--training_length", - action="store", - dest="training_length", - default=20, - type=check_positive, - help="""The length of both input (target and covariates) and output (target) time series used during training. - Generally speaking, training_length should have a higher value than input_chunk_length because otherwise - during training the RNN is never run for as many iterations as it will during training.""", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - n_days=True, - target_column=True, - train_split=True, - input_chunk_length=True, - force_reset=True, - save_checkpoints=True, - model_save_name="rnn_model", - n_epochs=True, - model_type=True, - dropout=0, - batch_size=32, - learning_rate=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - rnn_view.display_rnn_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - model_type=ns_parser.model_type, - hidden_dim=ns_parser.hidden_dim, - dropout=ns_parser.dropout, - batch_size=ns_parser.batch_size, - n_epochs=ns_parser.n_epochs, - learning_rate=ns_parser.learning_rate, - model_save_name=ns_parser.model_save_name, - training_length=ns_parser.training_length, - input_chunk_size=ns_parser.input_chunk_length, - force_reset=ns_parser.force_reset, - save_checkpoints=ns_parser.save_checkpoints, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_nbeats(self, other_args: List[str]): - """Process NBEATS command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="nbeats", - description=""" - Perform NBEATS forecast (Neural Bayesian Estimation of Time Series): - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.nbeats.html - """, - ) - # NBEATS Hyperparameters - parser.add_argument( - "--num_stacks", - action="store", - dest="num_stacks", - default=10, - type=check_positive_float, - help="The number of stacks that make up the whole model.", - ) - parser.add_argument( - "--num_blocks", - action="store", - dest="num_blocks", - default=3, - type=check_positive, - help="The number of blocks making up every stack.", - ) - parser.add_argument( - "--num_layers", - action="store", - dest="num_layers", - default=4, - type=check_positive, - help="""The number of fully connected layers preceding the final forking layers - in each block of every stack.""", - ) - parser.add_argument( - "--layer_widths", - action="store", - dest="layer_widths", - default=512, - type=check_positive, - help="""Determines the number of neurons that make up each fully connected layer - in each block of every stack""", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - n_days=True, - target_column=True, - train_split=True, - input_chunk_length=True, - output_chunk_length=True, - save_checkpoints=True, - force_reset=True, - model_save_name="nbeats_model", - learning_rate=True, - n_epochs=True, - batch_size=800, - past_covariates=True, - all_past_covariates=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - covariates = helpers.clean_covariates( - ns_parser, self.datasets[ns_parser.target_dataset] - ) - - nbeats_view.display_nbeats_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - past_covariates=covariates, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - input_chunk_length=ns_parser.input_chunk_length, - output_chunk_length=ns_parser.output_chunk_length, - num_stacks=ns_parser.num_stacks, - num_blocks=ns_parser.num_blocks, - num_layers=ns_parser.num_layers, - layer_widths=ns_parser.layer_widths, - batch_size=ns_parser.batch_size, - n_epochs=ns_parser.n_epochs, - learning_rate=ns_parser.learning_rate, - model_save_name=ns_parser.model_save_name, - force_reset=ns_parser.force_reset, - save_checkpoints=ns_parser.save_checkpoints, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_tcn(self, other_args: List[str]): - """Process TCN command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="tcn", - description=""" - Perform TCN forecast: - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.tcn_model.html - """, - ) - # TCN Hyperparameters - parser.add_argument( - "--num-filters", - action="store", - dest="num_filters", - default=3, - type=check_positive, - help="The number of filters in a convolutional layer of the TCN", - ) - parser.add_argument( - "--weight-norm", - action="store", - dest="weight_norm", - default=True, - type=bool, - help="Boolean value indicating whether to use weight normalization.", - ) - parser.add_argument( - "--dilation-base", - action="store", - dest="dilation_base", - default=2, - type=check_positive, - help="The base of the exponent that will determine the dilation on every level.", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - target_dataset=True, - n_days=True, - target_column=True, - train_split=True, - past_covariates=True, - all_past_covariates=True, - save_checkpoints=True, - force_reset=True, - model_save_name="tcn_model", - learning_rate=True, - n_epochs=True, - dropout=0.1, - batch_size=32, - input_chunk_length=True, - output_chunk_length=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - # check proper file name is provided - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - covariates = helpers.clean_covariates( - ns_parser, self.datasets[ns_parser.target_dataset] - ) - - tcn_view.display_tcn_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - past_covariates=covariates, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - input_chunk_length=ns_parser.input_chunk_length, - output_chunk_length=ns_parser.output_chunk_length, - dropout=ns_parser.dropout, - num_filters=ns_parser.num_filters, - weight_norm=ns_parser.weight_norm, - dilation_base=ns_parser.dilation_base, - batch_size=ns_parser.batch_size, - n_epochs=ns_parser.n_epochs, - learning_rate=ns_parser.learning_rate, - model_save_name=ns_parser.model_save_name, - force_reset=ns_parser.force_reset, - save_checkpoints=ns_parser.save_checkpoints, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_regr(self, other_args: List[str]): - """Process REGR command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="regr", - description=""" - Perform a regression forecast: - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.regression_model.html - """, - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - output_chunk_length=True, - train_split=True, - past_covariates=True, - all_past_covariates=True, - n_days=True, - target_dataset=True, - target_column=True, - lags=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - explainability_raw=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - # check proper file name is provided - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - covariates = helpers.clean_covariates( - ns_parser, self.datasets[ns_parser.target_dataset] - ) - - regr_view.display_regression( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - past_covariates=covariates, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - output_chunk_length=ns_parser.output_chunk_length, - lags=ns_parser.lags, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - explainability_raw=ns_parser.explainability_raw, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_linregr(self, other_args: List[str]): - """Process LINREGR command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="linregr", - description=""" - Perform a linear regression forecast: - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.linear_regression_model.html - """, - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - lags=True, - output_chunk_length=True, - train_split=True, - past_covariates=True, - all_past_covariates=True, - target_column=True, - n_days=True, - target_dataset=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - explainability_raw=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - covariates = helpers.clean_covariates( - ns_parser, self.datasets[ns_parser.target_dataset] - ) - - linregr_view.display_linear_regression( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - past_covariates=covariates, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - output_chunk_length=ns_parser.output_chunk_length, - lags=ns_parser.lags, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - explainability_raw=ns_parser.explainability_raw, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_brnn(self, other_args: List[str]): - """Process BRNN command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="brnn", - description=""" - Perform BRNN forecast (Vanilla RNN, LSTM, GRU): - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.block_rnn_model.html - """, - ) - # BRNN Hyperparameters - parser.add_argument( - "--n-rnn-layers", - action="store", - dest="n_rnn_layers", - default=1, - type=check_positive, - help="Number of layers in the RNN module.", - ) - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - save_checkpoints=True, - force_reset=True, - model_save_name="brnn_model", - learning_rate=True, - n_epochs=True, - batch_size=32, - dropout=0, - input_chunk_length=True, - output_chunk_length=True, - model_type=True, - train_split=True, - past_covariates=True, - all_past_covariates=True, - target_dataset=True, - n_days=True, - target_column=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - covariates = helpers.clean_covariates( - ns_parser, self.datasets[ns_parser.target_dataset] - ) - - brnn_view.display_brnn_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - past_covariates=covariates, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - input_chunk_length=ns_parser.input_chunk_length, - output_chunk_length=ns_parser.output_chunk_length, - model_type=ns_parser.model_type, - n_rnn_layers=ns_parser.n_rnn_layers, - dropout=ns_parser.dropout, - batch_size=ns_parser.batch_size, - n_epochs=ns_parser.n_epochs, - learning_rate=ns_parser.learning_rate, - model_save_name=ns_parser.model_save_name, - force_reset=ns_parser.force_reset, - save_checkpoints=ns_parser.save_checkpoints, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_trans(self, other_args: List[str]): - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="trans", - description=""" - Perform Transformer Forecast: - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.transformer_model.html - """, - ) - parser.add_argument( - "--d-model", - action="store", - dest="d_model", - default=64, - type=check_positive, - help="Number of expected features in inputs.", - ) - parser.add_argument( - "--nhead", - action="store", - dest="nhead", - default=4, - type=check_positive, - help="Number of head in the attention mechanism.", - ) - parser.add_argument( - "--num_encoder_layers", - action="store", - dest="num_encoder_layers", - default=3, - type=check_positive, - help="The number of encoder layers in the encoder.", - ) - parser.add_argument( - "--num_decoder_layers", - action="store", - dest="num_decoder_layers", - default=3, - type=check_positive, - help="The number of decoder layers in the encoder.", - ) - parser.add_argument( - "--dim_feedforward", - action="store", - dest="dim_feedforward", - default=512, - type=check_positive, - help="The dimension of the feedforward model.", - ) - parser.add_argument( - "--activation", - action="store", - dest="activation", - default="relu", - choices=["relu", "gelu"], - type=str, - help="Number of LSTM layers.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - n_days=True, - target_column=True, - target_dataset=True, - past_covariates=True, - all_past_covariates=True, - train_split=True, - input_chunk_length=True, - output_chunk_length=True, - dropout=0, - batch_size=32, - n_epochs=True, - model_save_name="trans_model", - force_reset=True, - save_checkpoints=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - covariates = helpers.clean_covariates( - ns_parser, self.datasets[ns_parser.target_dataset] - ) - - trans_view.display_trans_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - past_covariates=covariates, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - input_chunk_length=ns_parser.input_chunk_length, - output_chunk_length=ns_parser.output_chunk_length, - d_model=ns_parser.d_model, - nhead=ns_parser.nhead, - num_encoder_layers=ns_parser.num_encoder_layers, - num_decoder_layers=ns_parser.num_decoder_layers, - dim_feedforward=ns_parser.dim_feedforward, - activation=ns_parser.activation, - dropout=ns_parser.dropout, - n_epochs=ns_parser.n_epochs, - batch_size=ns_parser.batch_size, - model_save_name=ns_parser.model_save_name, - force_reset=ns_parser.force_reset, - save_checkpoints=ns_parser.save_checkpoints, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_tft(self, other_args: List[str]): - """Process TFT command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="tft", - description=""" - Perform TFT forecast (Temporal Fusion Transformer): - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.tft_model.html - """, - ) - parser.add_argument( - "--lstm-layers", - action="store", - dest="lstm_layers", - default=1, - type=check_positive, - help="Number of LSTM layers.", - ) - parser.add_argument( - "--num-attention-heads", - action="store", - dest="num_attention_heads", - default=4, - type=check_positive, - help="Number of attention heads.", - ) - parser.add_argument( - "--full-attention", - action="store_true", - dest="full_attention", - default=False, - help="Whether to apply a multi-head attention query.", - ) - parser.add_argument( - "--hidden-continuous-size", - action="store", - dest="hidden_continuous_size", - default=8, - type=check_positive, - help="Default hidden size for processing continuous variables.", - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - save_checkpoints=True, - target_dataset=True, - n_days=True, - force_reset=True, - model_save_name="tft_model", - train_split=True, - hidden_size=16, - batch_size=32, - n_epochs=True, - dropout=0.1, - input_chunk_length=True, - output_chunk_length=True, - past_covariates=True, - all_past_covariates=True, - target_column=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - covariates = helpers.clean_covariates( - ns_parser, self.datasets[ns_parser.target_dataset] - ) - - tft_view.display_tft_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - past_covariates=covariates, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - input_chunk_length=ns_parser.input_chunk_length, - output_chunk_length=ns_parser.output_chunk_length, - hidden_size=ns_parser.hidden_size, - lstm_layers=ns_parser.lstm_layers, - num_attention_heads=ns_parser.num_attention_heads, - full_attention=ns_parser.full_attention, - dropout=ns_parser.dropout, - hidden_continuous_size=ns_parser.hidden_continuous_size, - n_epochs=ns_parser.n_epochs, - batch_size=ns_parser.batch_size, - model_save_name=ns_parser.model_save_name, - force_reset=ns_parser.force_reset, - save_checkpoints=ns_parser.save_checkpoints, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_nhits(self, other_args: List[str]): - """Process nhits command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="nhits", - description=""" - Perform nhits forecast: - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.tft_model.html - """, - ) - parser.add_argument( - "--num-stacks", - dest="num_stacks", - type=check_positive, - default=3, - help="The number of stacks that make up the model", - ) - parser.add_argument( - "--num-blocks", - dest="num_blocks", - type=check_positive, - default=1, - help="The number of blocks making up every stack", - ) - parser.add_argument( - "--num-layers", - dest="num_layers", - type=check_positive, - default=2, - help="The number of fully connected layers", - ) - parser.add_argument( - "--layer_widths", - dest="layer_widths", - type=check_positive, - default=512, - help="The number of neurons in each layer", - ) - parser.add_argument( - "--activation", - dest="activation", - type=str, - default="ReLU", - choices=[ - "ReLU", - "RReLU", - "PReLU", - "Softplus", - "Tanh", - "SELU", - "LeakyReLU", - "Sigmoid", - ], - help="The desired activation", - ) - parser.add_argument( - "--max_pool_1d", - action="store_true", - dest="maxpool1d", - default=True, - help="Whether to use max_pool_1d or AvgPool1d", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - save_checkpoints=True, - target_dataset=True, - n_days=True, - force_reset=True, - model_save_name="nhits_model", - train_split=True, - dropout=0.1, - input_chunk_length=True, - output_chunk_length=True, - batch_size=32, - n_epochs=True, - past_covariates=True, - all_past_covariates=True, - target_column=True, - residuals=True, - forecast_only=True, - start=True, - end=True, - naive=True, - export_pred_raw=True, - metric=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - covariates = helpers.clean_covariates( - ns_parser, self.datasets[ns_parser.target_dataset] - ) - - nhits_view.display_nhits_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - n_predict=ns_parser.n_days, - target_column=ns_parser.target_column, - past_covariates=covariates, - train_split=ns_parser.train_split, - forecast_horizon=ns_parser.n_days, - input_chunk_length=ns_parser.input_chunk_length, - output_chunk_length=ns_parser.output_chunk_length, - num_stacks=ns_parser.num_stacks, - num_blocks=ns_parser.num_blocks, - num_layers=ns_parser.num_layers, - layer_widths=ns_parser.layer_widths, - activation=ns_parser.activation, - max_pool_1d=ns_parser.maxpool1d, - batch_size=ns_parser.batch_size, - n_epochs=ns_parser.n_epochs, - dropout=ns_parser.dropout, - model_save_name=ns_parser.model_save_name, - force_reset=ns_parser.force_reset, - save_checkpoints=ns_parser.save_checkpoints, - export=ns_parser.export, - residuals=ns_parser.residuals, - forecast_only=ns_parser.forecast_only, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - naive=ns_parser.naive, - export_pred_raw=ns_parser.export_pred_raw, - metric=ns_parser.metric, - ) - - @log_start_end(log=logger) - def call_anom(self, other_args: List[str]): - """Process ANOM command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="anom", - description=""" - Perform a Quantile Anomaly detection on a given dataset: - https://unit8co.github.io/darts/generated_api/darts.ad.detectors.quantile_detector.html - """, - ) - - # if user does not put in --dataset - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - parser = self.add_standard_args( - parser, - train_split=True, - target_column=True, - target_dataset=True, - forecast_only=True, - start=True, - end=True, - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - - if ns_parser: - if not helpers.check_parser_input(ns_parser, self.datasets): - return - - anom_view.display_anomaly_detection( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - target_column=ns_parser.target_column, - train_split=ns_parser.train_split, - export=ns_parser.export, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - ) - - @log_start_end(log=logger) - def call_whisper(self, other_args: List[str]): - """Utilize Whisper Model to transcribe a video. Currently only supports Youtube URLS""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="whisper", - description=""" - Utilize Whisper Model to transcribe a video. Currently only supports Youtube URLS: - https://github.com/openai/whisper - """, - ) - parser.add_argument( - "--video", - dest="video", - type=str, - default="", - help="video URLs to transcribe", - ) - parser.add_argument( - "--model_name", - dest="model_name", - choices=whisper.available_models(), - default="base", - help="name of the Whisper model to use", - ) - parser.add_argument( - "--subtitles_format", - dest="subtitles_format", - type=str, - choices=["vtt", "srt"], - help="the subtitle format to output", - ) - parser.add_argument( - "--verbose", - dest="verbose", - type=str2bool, - default=False, - help="Whether to print out the progress and debug messages", - ) - parser.add_argument( - "--task", - dest="task", - type=str, - choices=["transcribe", "translate"], - help="whether to perform X->X speech recognition ('transcribe') or X->English translation ('translate')", - ) - parser.add_argument( - "--language", - dest="language", - type=str, - default=None, - choices=sorted(LANGUAGES.keys()) - + sorted([k.title() for k in TO_LANGUAGE_CODE.keys()]), - help="language spoken in the audio, skip to perform language detection", - ) - parser.add_argument( - "--breaklines", - dest="breaklines", - type=int, - default=0, - help="Whether to break lines into a bottom-heavy pyramid shape if line length exceeds N characters. 0 disables line breaking.", - ) - parser.add_argument( - "--save", - dest="save", - type=str, - default=get_current_user().preferences.USER_FORECAST_WHISPER_DIRECTORY, - help="Directory to save the subtitles file", - ) - - parser = self.add_standard_args( - parser, - ) - if other_args and "--video" not in other_args: - other_args.insert(0, "--video") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - ) - - if ns_parser: - if ns_parser.save is None: - ns_parser.save = ( - get_current_user().preferences.USER_FORECAST_WHISPER_DIRECTORY - ) - - whisper_model.transcribe_and_summarize( - video=ns_parser.video, - model_name=ns_parser.model_name, - subtitles_format=ns_parser.subtitles_format, - verbose=ns_parser.verbose, - task=ns_parser.task, - language=ns_parser.language, - breaklines=ns_parser.breaklines, - output_dir=ns_parser.save, - ) - - # TimeGPT Model - @log_start_end(log=logger) - def call_timegpt(self, other_args: List[str]): - """Process expo command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="timegpt", - description=""" - TODO: Update me - """, - ) - parser.add_argument( - "--horizon", - action="store", - dest="horizon", - type=check_positive, - default=12, - help="Forecasting horizon", - ) - parser.add_argument( - "--freq", - action="store", - dest="freq", - choices=["H", "D", "W", "M", "MS", "B"], - default=None, - help="Frequency of the data.", - ) - parser.add_argument( - "--finetune", - action="store", - dest="finetune", - type=check_non_negative, - default=0, - help="Number of steps used to finetune TimeGPT in the new data.", - ) - parser.add_argument( - "--ci", - action="store", - dest="confidence", - type=check_positive_float_list, - default=[80, 90], - help="Number of steps used to finetune TimeGPT in the new data.", - ) - parser.add_argument( - "--cleanex", - action="store_false", - help="Clean exogenous signal before making forecasts using TimeGPT.", - dest="cleanex", - default=True, - ) - parser.add_argument( - "--timecol", - action="store", - dest="timecol", - default="ds", - type=str, - help="Dataframe column that represents datetime", - ) - parser.add_argument( - "--targetcol", - action="store", - dest="targetcol", - default="y", - type=str, - help="Dataframe column that represents the target to forecast for", - ) - parser.add_argument( - "--sheet-name", - help="The name of the sheet to export to when type is XLSX.", - dest="sheet_name", - type=str, - default="", - ) - parser.add_argument( - "--datefeatures", - help="Specifies which date attributes have highest weight according to model.", - dest="date_features", - type=check_list_values(["auto", "year", "month", "week", "day", "weekday"]), - default=[], - ) - parser = self.add_standard_args( - parser, - target_dataset=True, - target_column=True, - start=True, - end=True, - residuals=True, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--dataset") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_FIGURES_ALLOWED, - ) - if ns_parser: - timegpt_view.display_timegpt_forecast( - data=self.datasets[ns_parser.target_dataset], - dataset_name=ns_parser.target_dataset, - time_col=ns_parser.timecol, - target_col=ns_parser.targetcol, - forecast_horizon=ns_parser.horizon, - freq=ns_parser.freq, - levels=ns_parser.confidence, - finetune_steps=ns_parser.finetune, - clean_ex_first=ns_parser.cleanex, - export=ns_parser.export, - sheet_name=ns_parser.sheet_name, - start_date=ns_parser.s_start_date, - end_date=ns_parser.s_end_date, - residuals=ns_parser.residuals, - date_features=ns_parser.date_features, - ) diff --git a/openbb_terminal/forecast/forecast_model.py b/openbb_terminal/forecast/forecast_model.py deleted file mode 100644 index 9965fe25cc5..00000000000 --- a/openbb_terminal/forecast/forecast_model.py +++ /dev/null @@ -1,553 +0,0 @@ -"""Forecast Model""" - -__docformat__ = "numpy" - -# pylint: disable=eval-used - -import logging -from itertools import chain -from pathlib import Path -from typing import Any, Dict, Optional, Tuple, Union - -import numpy as np -import pandas as pd - -from openbb_terminal.common import common_model -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def get_default_files() -> Dict[str, Path]: - """Get the default files to load. - - Returns - ------- - default_files : Dict[str, Path] - A dictionary to map the default file names to their paths. - """ - current_user = get_current_user() - default_files = { - filepath.name: filepath - for file_type in common_model.file_types - for filepath in chain( - current_user.preferences.USER_EXPORTS_DIRECTORY.rglob(f"*.{file_type}"), - current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY.rglob( - f"*.{file_type}" - ), - ) - if filepath.is_file() - } - return default_files - - -def __sdk_dt_format(x) -> str: - """Convert any Timestamp to YYYY-MM-DD when using SDK - Args: - x: Pandas Timestamp of any length - Returns: - x: formatted string - """ - x = pd.to_datetime(x) - x = x.strftime("%Y-%m-%d") - return x - - -@log_start_end(log=logger) -def get_options( - datasets: Dict[str, pd.DataFrame], dataset_name: Optional[str] = None -) -> Dict[Union[str, Any], pd.DataFrame]: - """Obtain columns-dataset combinations from loaded in datasets that can be used in other commands - - Parameters - ---------- - datasets: dict - The available datasets. - dataset_name: str - The dataset you wish to show the options for. - - Returns - ------- - option_tables: Dict[Union[str, Any], pd.DataFrame] - A dictionary with a DataFrame for each option. With dataset_name set, only shows one - options table. - """ - option_tables = {} - - if dataset_name: - columns = datasets[dataset_name].columns - option_tables[dataset_name] = pd.DataFrame( - { - "column": columns, - "option": [f"{dataset_name}.{column}" for column in columns], - } - ) - else: - for dataset, data_values in datasets.items(): - columns = data_values.columns - option_tables[dataset] = pd.DataFrame( - { - "column": columns, - "option": [f"{dataset}.{column}" for column in columns], - } - ) - - return option_tables - - -@log_start_end(log=logger) -def clean( - dataset: pd.DataFrame, - fill: Optional[str] = None, - drop: Optional[str] = None, - limit: Optional[int] = None, -) -> Tuple[pd.DataFrame, np.bool_]: - """Clean up NaNs from the dataset - - Parameters - ---------- - dataset : pd.DataFrame - The dataset you wish to clean - fill : Optional[str] - The method of filling NaNs - drop : Optional[str] - The method of dropping NaNs - limit : Optional[int] - The maximum limit you wish to apply that can be forward or backward filled - - Returns - ------- - Tuple[pd.DataFrame, np.bool_] - The cleaned dataset and a boolean indicating if there are any NaNs left - """ - kwargs = {} - if limit: - kwargs["limit"] = limit - - if fill: - if fill == "rfill": - dataset = dataset.fillna(axis="index", value=0) - if fill == "cfill": - dataset = dataset.fillna(axis="columns", value=0) - elif fill == "rbfill": - dataset = dataset.fillna(axis="index", method="bfill", **kwargs) - elif fill == "cbfill": - dataset = dataset.fillna(axis="columns", method="bfill", **kwargs) - elif fill == "rffill": - dataset = dataset.fillna(axis="index", method="ffill", **kwargs) - elif fill == "cffill": - dataset = dataset.fillna(axis="columns", method="ffill", **kwargs) - - if drop: - if drop == "rdrop": - dataset = dataset.dropna(how="any", axis="index") - elif drop == "cdrop": - dataset = dataset.dropna(how="any", axis="columns") - - # TODO - think about if we want to always interpolate and remove above options - if fill == "" and drop == "": - # drop all rows with na - dataset = dataset.dropna(axis=0) - - dataset = dataset.reset_index(drop=True) - - return dataset, dataset.isnull().values.any() - - -@log_start_end(log=logger) -def add_ema( - dataset: pd.DataFrame, target_column: str = "close", period: int = 10 -) -> pd.DataFrame: - """A moving average provides an indication of the trend of the price movement - by cut down the amount of "noise" on a price chart. - - Parameters - ---------- - dataset : pd.DataFrame - The dataset you wish to clean - target_column : str - The column you wish to add the EMA to - period : int - Time Span - - Returns - ------- - pd.DataFrame - Dataframe with added EMA column - """ - dataset[f"EMA_{period}"] = ( - dataset[target_column].ewm(span=period, adjust=False).mean() - ) - - return dataset - - -@log_start_end(log=logger) -def add_sto( - dataset: pd.DataFrame, - close_column: str = "close", - high_column: str = "high", - low_column: str = "low", - period: int = 10, -) -> pd.DataFrame: - """Stochastic Oscillator %K and %D : A stochastic oscillator is a momentum indicator comparing a particular closing - price of a security to a range of its prices over a certain period of time. %K and %D are slow and fast indicators. - - Requires Low/High/Close columns. - Note: This will drop first rows equal to period due to how this metric is calculated. - - Parameters - ---------- - dataset : pd.DataFrame - The dataset you wish to calculate for - period : int - Span of time to calculate over - close_column : str - The column name for the close price - high_column : str - The column name for the high price - low_column : str - The column name for the low price - - Returns - ------- - pd.DataFrame - Dataframe with added STO K & D columns - """ - - # check if columns exist - if ( - low_column in dataset.columns - and high_column in dataset.columns - and close_column in dataset.columns - ): - STOK = ( - (dataset[close_column] - dataset[low_column].rolling(period).min()) - / ( - dataset[high_column].rolling(period).max() - - dataset[low_column].rolling(period).min() - ) - ) * 100 - - dataset[f"SO%K_{period}"] = STOK - dataset[f"SO%D_{period}"] = STOK.rolling(3).mean() - - return dataset - - console.print("[red]Missing Low/High/Close columns[/red]\n") - return dataset - - -@log_start_end(log=logger) -def add_rsi( - dataset: pd.DataFrame, target_column: str = "close", period: int = 10 -) -> pd.DataFrame: - """A momentum indicator that measures the magnitude of recent price changes to evaluate - overbought or oversold conditions in the price of a stock or other asset. - - Parameters - ---------- - dataset : pd.DataFrame - The dataset you wish to calculate for - target_column : str - The column you wish to add the RSI to - period : int - Time Span - - Returns - ------- - pd.DataFrame - Dataframe with added RSI column - """ - - delta = dataset[target_column].diff().dropna() - u = delta * 0 - d = u.copy() - u[delta > 0] = delta[delta > 0] - d[delta < 0] = -delta[delta < 0] - u[u.index[period - 1]] = np.mean(u.iloc[:period]) # first value is sum of avg gains - u = u.drop(u.index[: (period - 1)]) - d[d.index[period - 1]] = np.mean( - d.iloc[:period] - ) # first value is sum of avg losses - d = d.drop(d.index[: (period - 1)]) - rs = ( - u.ewm(com=period - 1, adjust=False).mean() - / d.ewm(com=period - 1, adjust=False).mean() - ) - dataset[f"RSI_{period}_{target_column}"] = 100 - 100 / (1 + rs) - - return dataset - - -@log_start_end(log=logger) -def add_roc( - dataset: pd.DataFrame, target_column: str = "close", period: int = 10 -) -> pd.DataFrame: - """A momentum oscillator, which measures the percentage change between the current - value and the n period past value. - - Parameters - ---------- - dataset : pd.DataFrame - The dataset you wish to calculate with - target_column : str - The column you wish to add the ROC to - period : int - Time Span - - Returns - ------- - pd.DataFrame - Dataframe with added ROC column - """ - M = dataset[target_column].diff(period - 1) - N = dataset[target_column].shift(period - 1) - - dataset[f"ROC_{period}"] = (M / N) * 100 - - return dataset - - -@log_start_end(log=logger) -def add_momentum( - dataset: pd.DataFrame, target_column: str = "close", period: int = 10 -) -> pd.DataFrame: - """A momentum oscillator, which measures the percentage change between the current - value and the n period past value. - - Parameters - ---------- - dataset : pd.DataFrame - The dataset you wish to calculate with - target_column : str - The column you wish to add the MOM to - period : int - Time Span - - Returns - ------- - pd.DataFrame - Dataframe with added MOM column - """ - - dataset[f"Momentum_{period}"] = dataset[target_column].diff(period) - - return dataset - - -@log_start_end(log=logger) -def add_delta( - dataset: pd.DataFrame, - target_column: str = "close", -) -> pd.DataFrame: - """ - Calculate the %change of a variable based on a specific column - """ - - dataset[f"delta_{target_column}"] = dataset[target_column].pct_change().fillna(0) - - return dataset - - -@log_start_end(log=logger) -def add_atr( - dataset: pd.DataFrame, - close_column: str = "close", - high_column: str = "high", - low_column: str = "low", -) -> pd.DataFrame: - """ - Calculate the Average True Range of a variable based on a a specific stock ticker. - """ - - if close_column in dataset and high_column in dataset and low_column in dataset: - dataset["ATR1"] = abs(dataset[high_column] - dataset[low_column]) - dataset["ATR2"] = abs(dataset[high_column] - dataset[close_column].shift()) - dataset["ATR3"] = abs(dataset[low_column] - dataset[close_column].shift()) - dataset["true_range"] = dataset[["ATR1", "ATR2", "ATR3"]].max(axis=1) - - # drop ATR1, ATR2, ATR3 - dataset = dataset.drop(["ATR1", "ATR2", "ATR3"], axis=1) - - return dataset - - -@log_start_end(log=logger) -def add_signal( - dataset: pd.DataFrame, - target_column: str = "close", -) -> pd.DataFrame: - """A price signal based on short/long term price. - - 1 if the signal is that short term price will go up as compared to the long term. - 0 if the signal is that short term price will go down as compared to the long term. - - Parameters - ---------- - dataset : pd.DataFrame - The dataset you wish to calculate with - - Returns - ------- - pd.DataFrame - Dataframe with added signal column - """ - - # Create short simple moving average over the short window - # > Create long simple moving average over the long window - - # Create signals - dataset["signal"] = np.where( - dataset[target_column].rolling(window=10, min_periods=1, center=False).mean() - > dataset[target_column].rolling(window=60, min_periods=1, center=False).mean(), - 1.0, - 0.0, - ) - - return dataset - - -@log_start_end(log=logger) -def combine_dfs( - df1: pd.DataFrame, df2: pd.DataFrame, column: str, dataset: str = "" -) -> pd.DataFrame: - """Adds the given column of df2 to df1 - - Parameters - ---------- - df1: pd.DataFrame - The dataframe to add a column to - df2: pd.DataFrame - The dataframe to lose a column - column: str - The column to transfer - dataset: str - A name for df2 (shows in name of new column) - - Returns - ------- - data: pd.DataFrame - The new dataframe - """ - - # for use with SDK - # check if date is index, if true, reset index - if df1.index.name == "date": - df1 = df1.reset_index() - # remove 00:00:00 from 2019-11-19 00:00:00 - df1["date"] = df1["date"].apply(lambda x: __sdk_dt_format(x)) - if df2.index.name == "date": - df2 = df2.reset_index() - # remove 00:00:00 from 2019-11-19 00:00:00 - df2["date"] = df2["date"].apply(lambda x: __sdk_dt_format(x)) - - if column not in df2: - console.print( - f"Not able to find the column {column}. Please choose one of " - f"the following: {', '.join(df2.columns)}" - ) - return df1 - - if "date" in df1.columns and "date" in df2.columns: - selected = df2[[column, "date"]] - new_cols = [f"{dataset}_{x}" if x != "date" else "date" for x in selected] - selected.columns = new_cols - return df1.merge(selected, on="date", how="left") - - console.print( - "[red]Not all dataframes have a date column so we are combining" - " on index, this may results in data mismatching.[/red]\n" - ) - selected = df2[[column]] - new_cols = [f"{dataset}_{x}" for x in selected] - selected.columns = new_cols - return df1.merge(selected, left_index=True, right_index=True, how="left") - - -@log_start_end(log=logger) -def delete_column(data: pd.DataFrame, column: str) -> None: - """Delete a column from a dataframe - - Parameters - ---------- - data : pd.DataFrame - The dataframe to delete a column from - column : str - The column to delete - - Returns - ------- - None - """ - if column not in data: - console.print( - f"Not able to find the column {column}. Please choose one of " - f"the following: {', '.join(data.columns)}" - ) - else: - del data[column] - - -@log_start_end(log=logger) -def rename_column(data: pd.DataFrame, old_column: str, new_column: str) -> pd.DataFrame: - """Rename a column in a dataframe - - Parameters - ---------- - data: pd.DataFrame - The dataframe to have a column renamed - old_column: str - The column that will have its name changed - new_column: str - The name to update to - - Returns - ------- - new_df: pd.DataFrame - The dataframe with the renamed column - """ - if old_column not in data: - console.print( - f"Not able to find the column {old_column}. Please choose one of " - f"the following: {', '.join(data.columns)}" - ) - return data - return data.rename(columns={old_column: new_column}) - - -@log_start_end(log=logger) -def describe_df(data: pd.DataFrame) -> pd.DataFrame: - """Returns statistics for a given df - - Parameters - ---------- - data: pd.DataFrame - The df to produce statistics for - - Returns - ------- - df: pd.DataFrame - The df with the new data - """ - return data.describe() - - -@log_start_end(log=logger) -def corr_df(data: pd.DataFrame) -> pd.DataFrame: - """Returns correlation for a given df - - Parameters - ---------- - data: pd.DataFrame - The df to produce statistics for - - Returns - ------- - df: pd.DataFrame - The df with the new data - """ - corr = data.corr(numeric_only=True) - return corr diff --git a/openbb_terminal/forecast/forecast_view.py b/openbb_terminal/forecast/forecast_view.py deleted file mode 100644 index 2e615323241..00000000000 --- a/openbb_terminal/forecast/forecast_view.py +++ /dev/null @@ -1,341 +0,0 @@ -"""Forecast View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Dict, List, Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import forecast_model, helpers -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def show_options( - datasets: Dict[str, pd.DataFrame], - dataset_name: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Plot custom data - - Parameters - ---------- - datasets: dict - The loaded in datasets - dataset_name: str - The name of the dataset you wish to show options for - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export image - """ - if not datasets: - console.print( - "Please load in a dataset by using the 'load' command before using this feature." - ) - else: - option_tables = forecast_model.get_options(datasets, dataset_name) - - for dataset, data_values in option_tables.items(): - print_rich_table( - data_values, - headers=list(data_values.columns), - show_index=False, - title=f"Options for dataset: '{dataset}'", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{dataset}_options", - data_values.set_index("column"), - sheet_name, - ) - - -@log_start_end(log=logger) -def display_plot( - data: pd.DataFrame, - columns: List[str], - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot data from a dataset - Parameters - ---------- - data: pd.DataFrame - The dataframe to plot - columns: List[str] - The columns to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export image - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - # Check that there's at least a valid dataframe - if data.empty: - return console.print("No data to plot") - - # Only do if data is not plotted, otherwise an error will occur - if "date" in data.columns and "date" not in columns: - data = data.set_index("date") - - fig = OpenBBFigure() - for column in columns: - fig.add_scatter(x=data.index, y=data[column], name=column) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "plot", - sheet_name=sheet_name, - figure=fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_seasonality( - data: pd.DataFrame, - column: str = "close", - export: str = "", - sheet_name: Optional[str] = None, - m: Optional[int] = None, - max_lag: int = 24, - alpha: float = 0.05, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot seasonality from a dataset - - Parameters - ---------- - data: pd.DataFrame - The dataframe to plot - column: str - The column of the dataframe to analyze - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export image - m: Optional[int] - Optionally, a time lag to highlight on the plot. Default is none. - max_lag: int - The maximal lag order to consider. Default is 24. - alpha: float - The confidence interval to display. Default is 0.05. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if data.empty: - return console.print("No data to plot") - - _, series = helpers.get_series(data, column) - - # TODO: Add darts check_seasonality here - fig = OpenBBFigure() - fig.add_corr_plot(series.values(), m=m, max_lag=max_lag, alpha=alpha) - - fig.update_xaxes(autorange=False, range=[-1, max_lag + 1]) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "plot", - sheet_name=sheet_name, - figure=fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_corr( - dataset: pd.DataFrame, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot correlation coefficients for dataset features - - Parameters - ---------- - dataset : pd.DataFrame - The dataset fore calculating correlation coefficients - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export image - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - fig = OpenBBFigure() - - # correlation - correlation = forecast_model.corr_df(dataset) - fig.add_heatmap( - z=correlation, - x=correlation.columns, - y=correlation.index, - zmin=correlation.values.min(), - zmax=1, - showscale=True, - text=correlation, - texttemplate="%{text:.2f}", - colorscale="electric", - colorbar=dict( - thickness=10, - thicknessmode="pixels", - x=1.2, - y=1, - xanchor="right", - yanchor="top", - xpad=10, - ), - xgap=1, - ygap=1, - ) - fig.update_yaxes(autorange="reversed") - fig.update_layout(margin=dict(l=0, r=120, t=0, b=0), title="Correlation Matrix") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "plot", - sheet_name=sheet_name, - figure=fig, - ) - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def show_df( - data: pd.DataFrame, - limit: int = 15, - limit_col: int = 10, - name: str = "", - export: str = "", - sheet_name: Optional[str] = None, -): - """Show a dataframe in a table - - Parameters - ---------- - data: pd.DataFrame - The dataframe to show - limit: int - The number of rows to show - limit_col: int - The number of columns to show - name: str - The name of the dataframe - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - """ - console.print( - f"[green]{name} dataset has shape (row, column): {data.shape}\n[/green]" - ) - if len(data.columns) > limit_col: - console.print( - f"[red]Dataframe has more than {limit_col} columns." - " If you have extra screen space, consider increasing the `limit_col`," - " else export to see all of the data.[/red]\n" - ) - data = data.iloc[:, :limit_col] - print_rich_table( - data, - headers=list(data.columns), - show_index=True, - title=f"Dataset {name} | Showing {limit} of {len(data)} rows", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{name}_show", - data, - sheet_name, - ) - - -@log_start_end(log=logger) -def describe_df( - data: pd.DataFrame, - name: str = "", - export: str = "", - sheet_name: Optional[str] = None, -): - """Show descriptive statistics for a dataframe - - Parameters - ---------- - data: pd.DataFrame - The dataframe to show - name: str - The name of the dataframe - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - """ - new_df = forecast_model.describe_df(data) - print_rich_table( - new_df, - headers=list(data.describe().columns), - show_index=True, - title=f"Showing Descriptive Statistics for Dataset {name}", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{name}_show", - sheet_name, - ) - - -@log_start_end(log=logger) -def export_df( - data: pd.DataFrame, export: str, name: str = "", sheet_name: Optional[str] = None -) -> None: - """Export a dataframe to a file - - Parameters - ---------- - data: pd.DataFrame - The dataframe to export - export: str - The format to export the dataframe to - name: str - The name of the dataframe - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - """ - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{name}_show", - data, - sheet_name, - ) diff --git a/openbb_terminal/forecast/helpers.py b/openbb_terminal/forecast/helpers.py deleted file mode 100644 index 95e5e698a31..00000000000 --- a/openbb_terminal/forecast/helpers.py +++ /dev/null @@ -1,1402 +0,0 @@ -# pylint: disable=too-many-arguments,too-many-lines -import argparse -import logging -import os -from datetime import datetime, time, timedelta -from typing import Any, Dict, List, Optional, Tuple, Union - -import numpy as np -import pandas as pd -from darts import TimeSeries -from darts.dataprocessing.transformers import MissingValuesFiller, Scaler -from darts.explainability.shap_explainer import ShapExplainer -from darts.metrics import mape, mse, rmse, smape -from darts.models.forecasting.torch_forecasting_model import GlobalForecastingModel -from pytorch_lightning.callbacks.early_stopping import EarlyStopping -from shap import Explanation -from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score -from sklearn.model_selection import train_test_split -from sklearn.preprocessing import MaxAbsScaler, MinMaxScaler, Normalizer, StandardScaler - -from openbb_terminal import OpenBBFigure, rich_config, theme -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -logging.getLogger("pytorch_lightning").setLevel(logging.CRITICAL) # No needed for now - - -def mean_absolute_percentage_error(y_true: np.ndarray, y_pred: np.ndarray) -> np.number: - """Calculate mean absolute percent error""" - y_true, y_pred = np.array(y_true), np.array(y_pred) - return np.mean(np.abs((y_true - y_pred) / y_true)) * 100 - - -def print_prediction_kpis(real: np.ndarray, pred: np.ndarray): - """Print prediction statistics""" - kpis = { - "MAPE": f"{mean_absolute_percentage_error(real, pred) :.3f} %", - "R2": f"{r2_score(real, pred) :.3f}", - "MAE": f"{mean_absolute_error(real, pred):.3f}", - "MSE": f"{mean_squared_error(real, pred):.3f}", - "RMSE": f"{mean_squared_error(real, pred, squared=False):.3f}", - } - df = pd.DataFrame.from_dict(kpis, orient="index") - print_rich_table( - df, - show_index=True, - title="KPIs", - floatfmt=".2f", - ) - - -def plot_data_predictions( - data, - preds, - y_valid, - y_dates_valid, - scaler, - title, - forecast_data, - n_loops, - time_str: str = "", - external_axes: bool = False, -): - """Plots data predictions for the different ML techniques - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - fig = OpenBBFigure() - fig.add_scatter( - x=data.index, - y=data.values, - mode="lines+markers", - line=dict(color=theme.up_color), - name="Real data", - ) - for i in range(len(y_valid) - 1): - if scaler: - y_pred = scaler.inverse_transform(preds[i].reshape(-1, 1)).ravel() - y_act = scaler.inverse_transform(y_valid[i].reshape(-1, 1)).ravel() - else: - y_pred = preds[i].ravel() - y_act = y_valid[i].ravel() - - fig.add_scatter( - x=y_dates_valid[i], - y=y_pred, - mode="lines", - line=dict(color=theme.down_color), - name="Predictions", - ) - fig.add_scatter( - x=y_dates_valid[i], - y=y_act, - mode="lines", - line=dict(color=theme.up_color), - name="Actual", - ) - - # Leave this out of the loop so that the legend doesn't get overpopulated with "Predictions" - if scaler: - final_pred = scaler.inverse_transform(preds[-1].reshape(-1, 1)).ravel() - final_valid = scaler.inverse_transform(y_valid[-1].reshape(-1, 1)).ravel() - else: - final_pred = preds[-1].reshape(-1, 1).ravel() - final_valid = y_valid[-1].reshape(-1, 1).ravel() - - fig.add_scatter( - x=y_dates_valid[-1], - y=final_pred, - mode="lines", - line=dict(color=theme.down_color), - name="Predictions", - ) - fig.add_scatter( - x=y_dates_valid[-1], - y=final_valid, - mode="lines", - line=dict(color=theme.up_color), - name="Actual", - ) - - fig.add_vline( - x=forecast_data.index[0], line=dict(width=1, dash="dash", color="white") - ) - if n_loops == 1: - fig.add_scatter( - x=forecast_data.index, - y=forecast_data.values, - mode="lines", - line=dict(color=theme.up_color), - name="Forecast", - ) - - else: - fig.add_scatter( - x=forecast_data.index, - y=forecast_data.median(axis=1).values, - mode="lines", - line=dict(color=theme.up_color), - name="Forecast", - ) - fig.add_scatter( - x=forecast_data.index, - y=forecast_data.quantile(0.25, axis=1).values, - mode="lines", - line=dict(color=theme.up_color), - name="Forecast", - ) - - ymin, ymax = fig.layout.yaxis.range - # Subtracting 1 day only for daily data. For now if not daily, then start line on last point - if (not time_str or time_str == "1D") and isinstance( - forecast_data.index[0], datetime - ): - fig.add_shape( - type="rect", - x0=forecast_data.index[0] - timedelta(days=1), - y0=ymin, - x1=forecast_data.index[-1], - y1=ymax, - fillcolor=theme.up_color, - opacity=0.2, - layer="below", - line_width=0, - ) - fig.update_xaxes( - range=[data.index[0], forecast_data.index[-1] + timedelta(days=1)] - ) - - else: - fig.add_shape( - type="rect", - x0=forecast_data.index[0], - y0=ymin, - x1=forecast_data.index[-1], - y1=ymax, - fillcolor=theme.up_color, - opacity=0.2, - layer="below", - line_width=0, - ) - fig.update_xaxes(range=[data.index[0], forecast_data.index[-1]]) - - fig.update_layout( - title=title, - xaxis_title="Date", - yaxis_title="Price", - ) - return fig.show(external=external_axes) - - -def prepare_scale_train_valid_test( - data: Union[pd.DataFrame, pd.Series], - n_input_days: int, - n_predict_days: int, - test_size: float, - s_start_date: Optional[datetime] = None, - s_end_date: Optional[datetime] = None, - no_shuffle: bool = True, - Preprocess: Optional[str] = "standardization", -): - """ - Prepare and scale train, validate and test data. This is an old function for models - imported from the previous pred menu - - Parameters - ---------- - data: pd.DataFrame - Dataframe of stock prices - ns_parser: argparse.Namespace - Parsed arguments - Returns - ------- - X_train: np.ndarray - Array of training data. Shape (# samples, n_inputs, 1) - X_test: np.ndarray - Array of validation data. Shape (total sequences - #samples, n_inputs, 1) - y_train: np.ndarray - Array of training outputs. Shape (#samples, n_days) - y_test: np.ndarray - Array of validation outputs. Shape (total sequences -#samples, n_days) - X_dates_train: np.ndarray - Array of dates for X_train - X_dates_test: np.ndarray - Array of dates for X_test - y_dates_train: np.ndarray - Array of dates for y_train - y_dates_test: np.ndarray - Array of dates for y_test - test_data: np.ndarray - Array of prices after the specified end date - dates_test: np.ndarray - Array of dates after specified end date - scaler: - Fitted PREPROCESSOR - Preprocess: Optional[str] - The method to preprocess data. Choose: standardization, minmax, normalization, or None. - Default is standardization. - """ - - pre_dict = { - "standardization": StandardScaler(), - "minmax": MinMaxScaler(), - "normalization": Normalizer(), - } - scaler = None if Preprocess is None else pre_dict.get(Preprocess, None) - - if s_end_date: - data = data[data.index <= s_end_date] - if n_input_days + n_predict_days > data.shape[0]: - console.print( - "Cannot train enough input days to predict with loaded dataframe\n" - ) - return (None * 11, True) # type: ignore - if s_start_date: - data = data[data.index >= s_start_date] - if n_input_days + n_predict_days > data.shape[0]: - console.print( - "Cannot train enough input days to predict with loaded dataframe\n" - ) - return (None * 11, True) # type: ignore - - test_data = data.iloc[-n_input_days:] - train_data = data.iloc[:-n_input_days] - - dates = data.index - dates_test = test_data.index - if scaler: - train_data = scaler.fit_transform(data.values.reshape(-1, 1)) - test_data = scaler.transform(test_data.values.reshape(-1, 1)) - else: - train_data = data.values.reshape(-1, 1) - test_data = test_data.values.reshape(-1, 1) - - prices = train_data - - input_dates = [] - input_prices = [] - next_n_day_prices = [] - next_n_day_dates = [] - - for idx in range(len(prices) - n_input_days - n_predict_days): - input_prices.append(prices[idx : idx + n_input_days]) # noqa: E203 - input_dates.append(dates[idx : idx + n_input_days]) # noqa: E203 - next_n_day_prices.append( - prices[ - idx + n_input_days : idx + n_input_days + n_predict_days # noqa: E203 - ] - ) - next_n_day_dates.append( - dates[ - idx + n_input_days : idx + n_input_days + n_predict_days # noqa: E203 - ] - ) - - input_dates = np.asarray(input_dates) # type: ignore - input_prices = np.array(input_prices) # type: ignore - next_n_day_prices = np.array(next_n_day_prices) # type: ignore - next_n_day_dates = np.asarray(next_n_day_dates) # type: ignore - - ( - X_train, - X_valid, - y_train, - y_valid, - X_dates_train, - X_dates_valid, - y_dates_train, - y_dates_valid, - ) = train_test_split( - input_prices, - next_n_day_prices, - input_dates, - next_n_day_dates, - test_size=test_size, - shuffle=no_shuffle, - ) - return ( - X_train, - X_valid, - y_train, - y_valid, - X_dates_train, - X_dates_valid, - y_dates_train, - y_dates_valid, - test_data, - dates_test, - scaler, - False, - ) - - -def print_pretty_prediction(df_pred: pd.DataFrame, last_price: float): - """Print predictions""" - - if rich_config.USE_COLOR: - df_pred = pd.DataFrame(df_pred) - df_pred.columns = ["pred"] - if not get_current_user().preferences.USE_INTERACTIVE_DF: - df_pred["pred"] = df_pred["pred"].apply( - lambda x: f"[#00AAFF]{x:.2f} [/#00AAFF]" - ) - if check_dates(df_pred.index.to_series()): - df_pred.index = df_pred.index.date - print_rich_table( - df_pred, - show_index=True, - index_name="Datetime", - headers=["Prediction"], - floatfmt=".2f", - title=f"Actual price: [yellow]{last_price:.2f} [/yellow]", - ) - - -def past_covs( - past_covariates: Optional[str], - data: Union[pd.DataFrame, pd.Series], - train_split: float, - is_scaler: bool = True, -): - if past_covariates is not None: - target_covariates_names = past_covariates.split(",") - - # create first covariate to then stack - console.print(f"[green]Covariate #0: {target_covariates_names[0]}[/green]") - _, past_covariate_whole = get_series( - data, target_column=target_covariates_names[0], is_scaler=is_scaler - ) - - if len(target_covariates_names) > 1: - for i, column in enumerate(target_covariates_names[1:]): - console.print(f"[green]Covariate #{i+1}: {column}[/green]") - _, _temp_new_covariate = get_series( - data, - target_column=target_covariates_names[i + 1], - is_scaler=is_scaler, - ) - - # continually stack covariates based on column names - past_covariate_whole = past_covariate_whole.stack(_temp_new_covariate) - - # Split the full scale covariate to train and val - ( - past_covariate_train, - past_covariate_val, - ) = past_covariate_whole.split_before(train_split) - return ( - past_covariate_whole, - past_covariate_train, - past_covariate_val, - ) - return None, None, None - - -def early_stopper(patience: int, monitor: str = "val_loss"): - my_stopper = EarlyStopping( - monitor=monitor, - patience=patience, - min_delta=0.01, - mode="min", - ) - return my_stopper - - -def get_pl_kwargs( - patience: int = 20, monitor: str = "val_loss", accelerator: str = "cpu" -) -> Dict[str, Any]: - my_stopper = early_stopper(patience, monitor) - pl_trainer_kwargs = { - "callbacks": [my_stopper], - "accelerator": accelerator, - # "logger": False, - # "enable_progress_bar": False, - "enable_model_summary": False, - } - return pl_trainer_kwargs - - -@log_start_end(log=logger) -def plot_predicted( - predicted_values: type[TimeSeries], - fig: OpenBBFigure, - central_quantile: Union[float, str] = 0.5, - low_quantile: Optional[float] = 0.05, - high_quantile: Optional[float] = 0.95, - label: Optional[Union[str, List[str]]] = "", -): - """Plots the predicted_values time series on the given figure. - - Parameters - ---------- - predicted_values: TimeSeries - The predicted values TimeSeries. - fig: OpenBBFigure - The figure to plot on. - central_quantile: float or str - The quantile (between 0 and 1) to plot as a "central" value, if the series is stochastic (i.e., if - it has multiple samples). This will be applied on each component separately (i.e., to display quantiles - of the components' marginal distributions). For instance, setting `central_quantile=0.5` will plot the - median of each component. `central_quantile` can also be set to 'mean'. - low_quantile: float - The quantile to use for the lower bound of the plotted confidence interval. Similar to `central_quantile`, - this is applied to each component separately (i.e., displaying marginal distributions). No confidence - interval is shown if `confidence_low_quantile` is None (default 0.05). - high_quantile: float - The quantile to use for the upper bound of the plotted confidence interval. Similar to `central_quantile`, - this is applied to each component separately (i.e., displaying marginal distributions). No confidence - interval is shown if `high_quantile` is None (default 0.95). - label: str or list of str - A prefix that will appear in front of each component of the TimeSeries or a list of string of - length the number of components in the plotted TimeSeries (default ""). - """ - - if central_quantile != "mean" and not ( - isinstance(central_quantile, float) and 0.0 <= central_quantile <= 1.0 - ): - raise ValueError( - 'central_quantile must be either "mean", or a float between 0 and 1.' - ) - - if ( - high_quantile is not None - and low_quantile is not None - and not (0.0 <= low_quantile <= 1.0 and 0.0 <= high_quantile <= 1.0) - ): - raise ValueError( - "confidence interval low and high quantiles must be between 0 and 1." - ) - - if predicted_values.n_components > 10: - logger.warning( - "Number of components is larger than 10 (%s). Plotting only the first 10 components.", - predicted_values.n_components, - ) - - if not isinstance(label, str) and isinstance(label, list): - if not ( - len(label) == predicted_values.n_components - or (predicted_values.n_components > 10 and len(label) >= 10) - ): - console.print( - "The label argument should have the same length as the number of plotted components " - f"({min(predicted_values.n_components, 10)}), only {len(label)} labels were provided" - ) - else: - pass - - # pylint: disable=protected-access - for i, c in enumerate(predicted_values._xa.component[:10]): - comp_name = str(c.values) - comp = predicted_values._xa.sel(component=c) - - if comp.sample.size > 1: - if central_quantile == "mean": - central_series = comp.mean(dim="sample") - else: - central_series = comp.quantile(q=central_quantile, dim="sample") - else: - central_series = comp.mean(dim="sample") - - if isinstance(label, list): - label_to_use = label[i] - else: - label_to_use = ( - (f"{label}" + (f"_{i}" if len(predicted_values.components) > 1 else "")) - if label != "" - else comp_name - ) - - if central_series.shape[0] > 1: - for col in central_series.coords: - if isinstance(central_series.coords[col].values[0], np.datetime64): - central_series = central_series.rename({col: "date"}) - break - - fig.add_scatter( - x=central_series.date, - y=central_series.values, - mode="lines", - name=label_to_use, - line=dict(width=2, color=theme.up_color), - ) - - elif central_series.shape[0] == 1: - fig.add_scatter( - x=[predicted_values.start_time()], - y=[central_series.values[0]], - mode="markers", - name=label_to_use, - ) - - # Optionally show confidence intervals - if ( - comp.sample.size > 1 - and low_quantile is not None - and high_quantile is not None - ): - low_series = comp.quantile(q=low_quantile, dim="sample") - high_series = comp.quantile(q=high_quantile, dim="sample") - if low_series.shape[0] > 1: - # plotly fill - fig.add_scatter( - x=predicted_values.time_index, - y=high_series, - name=f"High Confidence Interval ({high_quantile * 100}%)", - mode="lines", - line_width=0, - opacity=0.2, - showlegend=False, - ) - fig.add_scatter( - x=predicted_values.time_index, - y=low_series, - name=f"Low Confidence Interval ({low_quantile * 100}%)", - fill="tonexty", - mode="lines", - fillcolor=theme.up_color_transparent.replace("0.50", "0.35"), - line_width=0, - opacity=0.2, - showlegend=False, - ) - - else: - fig.add_scatter( - x=[predicted_values.start_time(), predicted_values.start_time()], - y=[low_series.values[0], high_series.values[0]], - mode="lines", - name=label_to_use, - line_color=theme.up_color, - ) - - return fig - - -@log_start_end(log=logger) -def plot_forecast( - name: str, - target_col: str, - historical_fcast: type[TimeSeries], - predicted_values: type[TimeSeries], - ticker_series: type[TimeSeries], - ticker_name: str, - data: Union[pd.DataFrame, pd.Series], - n_predict: int, - forecast_horizon: int, - past_covariates: Optional[str] = None, - precision: Optional[int] = None, - probabilistic: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - low_quantile: Optional[float] = None, - high_quantile: Optional[float] = None, - forecast_only: bool = False, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - quant_kwargs = {} - if low_quantile: - quant_kwargs["low_quantile"] = low_quantile - if high_quantile: - quant_kwargs["high_quantile"] = high_quantile - - naive_fcast: type[TimeSeries] = ticker_series.shift(1) - if forecast_only: - ticker_series = ticker_series.drop_before(historical_fcast.start_time()) - - fig = OpenBBFigure(yaxis_title=target_col, xaxis_title="Date") - fig.set_title( - f"{name} for <{ticker_name}> for next [{n_predict}]" - f" days ({metric.upper()}={precision:.2f}%)" - ) - - fig.add_scatter( - y=list(ticker_series.univariate_values()), - x=list(ticker_series.time_index), - name="Actual", - line_color="gold", - mode="lines", - ) - - fig.add_scatter( - y=list(historical_fcast.univariate_values()), - x=list(historical_fcast.time_index), - name=f"Backtest {forecast_horizon}-Steps ahead forecast", - line_color="red", - mode="lines", - ) - - if naive: - # show naive forecast shift timeseries by 1 - naive_fcast = naive_fcast.drop_before(historical_fcast.start_time()) - - # calculate precision based on metric - if metric == "rsme": - naive_precision = rmse(ticker_series, naive_fcast) - elif metric == "mse": - naive_precision = mse(ticker_series, naive_fcast) - elif metric == "mape": - naive_precision = mape(ticker_series, naive_fcast) - elif metric == "smape": - naive_precision = smape(ticker_series, naive_fcast) - - fig.add_scatter( - y=list(naive_fcast.univariate_values()), - x=list(naive_fcast.time_index), - name=f"Naive+1: {naive_precision:.2f}%", - line_color="green", - mode="lines", - ) - - pred_label = f"{name} Forecast" - if past_covariates: - pred_label += " w/ past covs" - - fig = plot_predicted(predicted_values, fig, label=pred_label, **quant_kwargs) - - fig.update_layout(title=dict(x=0.5, xanchor="center", yanchor="top")) - fig.update_xaxes( - range=[ - ticker_series.start_time(), - ticker_series.end_time() + timedelta(days=30 + n_predict), - ], - autorange=False, - ) - - if probabilistic: - numeric_forecast = predicted_values.quantile_df()[f"{target_col}_0.5"].tail( - n_predict - ) - else: - numeric_forecast = predicted_values.pd_dataframe()[target_col].tail(n_predict) - - print_pretty_prediction(numeric_forecast, data[target_col].iloc[-1]) - - # user wants to export plot - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - name, - sheet_name=sheet_name, - figure=fig, - ) - - # user wants to export only raw predictions - if export_pred_raw: - # convert numeric)forecast to dataframe - numeric_forecast = numeric_forecast.to_frame() - - # if numeric_forcast has a column with *_0.5, then rename it to target_col - if f"{target_col}_0.5" in numeric_forecast.columns: - numeric_forecast.rename( - columns={f"{target_col}_0.5": target_col}, inplace=True - ) - - # convert non-date column to 2 decimal places - numeric_forecast[target_col] = numeric_forecast[target_col].apply( - lambda x: round(x, 2) - ) - - export_data( - "csv", - os.path.dirname(os.path.abspath(__file__)), - name + "_predictions", - numeric_forecast, - sheet_name, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def plotly_shap_scatter_plot( - shap_values: Explanation, - features: Optional[Union[pd.DataFrame, list, np.ndarray]] = None, - feature_names: Optional[Union[List[str], np.ndarray]] = None, - max_display: Optional[int] = None, -) -> OpenBBFigure: - """Generate a shap values summary plot where features are ranked from - highest mean absolute shap value to lowest, with point clouds shown - for each feature. - - Parameters: - ----------- - shap_exp: Explanation - The shap values for the model. - features : Optional[Union[pd.DataFrame, list, np.ndarray]] - Matrix of feature values (# samples x # features) or a feature_names list as shorthand - feature_names : Optional[List[str]] - Names of the features (length # features) - max_display: Optional[int] - The maximum number of features to display. Defaults to 20. - - Returns: - -------- - OpenBBFigure - The shap values summary plot. - """ - if max_display is None: - max_display = 20 - - shap_exp = shap_values - shap_values = shap_exp.values - if features is None: - features = shap_exp.data - if feature_names is None: - feature_names = shap_exp.feature_names - - idx2cat = None - # convert from a DataFrame or other types - if isinstance(features, pd.DataFrame): - if feature_names is None: - feature_names = features.columns - # feature index to category flag - idx2cat = features.dtypes.astype(str).isin(["object", "category"]).tolist() - features = features.values - elif isinstance(features, list): - if feature_names is None: - feature_names = features - features = None - elif (features is not None) and len(features.shape) == 1 and feature_names is None: - feature_names = features - features = None - - num_features = shap_values.shape[1] - - if feature_names is None: - feature_names = np.array([f"Feature {i}" for i in range(num_features)]) - - feature_order = np.argsort(np.sum(np.abs(shap_values), axis=0)) - feature_order = feature_order[-min(max_display, len(feature_order)) :] - - fig = OpenBBFigure.create_subplots( - 1, 2, specs=[[{}, {}]], column_widths=[0.01, 0.99], horizontal_spacing=0 - ) - fig.set_xaxis_title("SHAP value (impact on model output)") - - for pos, i in enumerate(feature_order): - new_pos = pos + 2 - shaps = shap_values[:, i] - values = None if features is None else features[:, i] - inds = np.arange(len(shaps)) - np.random.shuffle(inds) - if values is not None: - values = values[inds] - shaps = shaps[inds] - - colored_feature = True - try: - if idx2cat is not None and idx2cat[i]: # check categorical feature - colored_feature = False - else: - values = np.array( - values, dtype=np.float64 - ) # make sure this can be numeric - except Exception: - colored_feature = False - - N = len(shaps) - nbins = 100 - quant = np.round( - nbins * (shaps - np.min(shaps)) / (np.max(shaps) - np.min(shaps) + 1e-8) - ) - inds = np.argsort(quant + np.random.randn(N) * 1e-6) - layer = 0 - last_bin = -1 - - ys = np.zeros(N) - for ind in inds: - if quant[ind] != last_bin: - layer = 0 - ys[ind] = np.ceil(layer / 2) * ((layer % 2) * 2 - 1) - layer += 1 - last_bin = quant[ind] - ys *= 0.9 * (0.4 / np.max(ys + 1)) - - if features is not None and colored_feature: - # trim the color range, but prevent the color range from collapsing - vmin = np.nanpercentile(values, 5) - vmax = np.nanpercentile(values, 95) - if vmin == vmax: - vmin = np.nanpercentile(values, 1) - vmax = np.nanpercentile(values, 99) - if vmin == vmax: - vmin = np.min(values) - vmax = np.max(values) - - # fixes rare numerical precision issues - vmin = min(vmin, vmax) - - # plot the nan values in the interaction feature as grey - nan_mask = np.isnan(values) - fig.add_scattergl( - x=shaps[nan_mask], - y=new_pos + ys[nan_mask], - mode="markers", - marker=dict( - color="#777777", - cmin=vmin, - cmax=vmax, - size=10, - ), - hoverinfo="none", - showlegend=False, - row=1, - col=2, - ) - # plot the non-nan values colored by the trimmed feature value - cvals = values[np.invert(nan_mask)].astype(np.float64) - cvals_imp = cvals.copy() - cvals_imp[np.isnan(cvals)] = (vmin + vmax) / 2.0 - cvals[cvals_imp > vmax] = vmax - cvals[cvals_imp < vmin] = vmin - - fig.add_scattergl( - x=shaps[np.invert(nan_mask)], - y=new_pos + ys[np.invert(nan_mask)], - mode="markers", - marker=dict( - color=cvals, - colorscale="Bluered", - showscale=bool(new_pos == 2), - colorbar=dict( - x=-0.05, - thickness=10, - xpad=0, - thicknessmode="pixels", - title=dict( - text="Feature Value", - side="right", - font=dict(size=12), - ), - tickmode="array", - tickvals=[vmin, vmax], - ticktext=["Low", "High"], - tickfont=dict(size=12), - ticklabelposition="outside left", - borderwidth=0, - ), - cmin=vmin, - cmax=vmax, - size=10, - ), - hoverinfo="none", - name=feature_names[i], - showlegend=False, - row=1, - col=2, - ) - - display_columns = [feature_names[i] for i in feature_order] - fig.update_yaxes( - position=0, - tickmode="array", - ticktext=display_columns, - tickvals=np.arange(2, len(display_columns) + 2), - automargin=False, - range=[1.5, len(display_columns) + 1.5], - ) - fig.update_layout( - margin=dict(r=190, l=20), - height=100 + len(display_columns) * 50, - ) - - return fig - - -@log_start_end(log=logger) -def plot_explainability( - model: type[GlobalForecastingModel], - explainability_raw: bool = False, - sheet_name: Optional[str] = None, - export: str = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """ - Plot explainability of the model - - Parameters - ---------- - model: type[GlobalForecastingModel] - The model to plot explainability for - explainability_raw: bool - Whether to plot raw explainability or not - sheet_name: Optional[str] - Optionally specify the name of the sheet the data is exported to. - export: (str, optional) - Export data to csv, jpg, png, or pdf. Defaults to "". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - shap_explain = ShapExplainer(model) - - # pylint: disable=W0212 - horizons, target_components = shap_explain._check_horizons_and_targets(1, None) - - foreground_X_sampled = shap_explain.explainers.background_X - - shaps_ = shap_explain.explainers.shap_explanations( - foreground_X_sampled, horizons, target_components - ) - for t in target_components: - for h in horizons: - fig = plotly_shap_scatter_plot(shaps_[h][t], foreground_X_sampled) - fig.set_title(f"Target: `{t}` - Horizon: t+{h}") - fig.add_vline(x=0, line_width=1.5, line_color="white", opacity=0.7) - - if explainability_raw: - console.print("\n") - console.print("[green]Exporting Raw Explainability DataFrame[/green]") - raw_df = shap_explain.explain().get_explanation(horizon=1).pd_dataframe() - export_data( - "csv", - os.path.dirname(os.path.abspath(__file__)), - "explainability_raw", - raw_df, - sheet_name, - ) - - fig.cmd_xshift = -20 - if fig.is_image_export(export): - name, ext = export.split(".") if "." in export else (None, None) - if name and ext: - export = f"{name}_explainability.{ext}" - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "explainability", - figure=fig, - ) - - return fig.show(external=external_axes) - - -def dt_format(x) -> str: - """Convert any Timestamp to YYYY-MM-DD - Args: - x: Pandas Timestamp of any length - Returns: - x: formatted string - """ - # x = pd.to_datetime(x, infer_datetime_format=False) - - # x = x.strftime("%Y-%m-%d") if x.time() == datetime.time(0,0) else x.strftime("%Y-%m-%d %H:%M:%S") - return x - - -def get_series( - data: pd.DataFrame, - target_column: Optional[str] = None, - is_scaler: bool = True, - time_col: str = "date", -) -> Tuple[Optional[Scaler], TimeSeries]: - filler = MissingValuesFiller() - filler_kwargs = dict( - df=data, - time_col=time_col, - value_cols=[target_column], - freq=None, - fill_missing_dates=True, - ) - freq = "D" - try: - # for the sdk, we must check if date is a column not an index - # check if date is in the index, if true, reset the index - if time_col in data.index.names: - # # make a new column with the index - data[time_col] = data.index - # reset the index - data.reset_index(drop=True, inplace=True) - # remove 00:00:00 from 2019-11-19 00:00:00 - # data[time_col] = data[time_col].apply(lambda x: dt_format(x)) - freq = pd.infer_freq(data[time_col]) - - filler_kwargs["freq"] = freq - - ticker_series = TimeSeries.from_dataframe(**filler_kwargs) - except ValueError: - # remove business days to allow base lib to assume freq - filler_kwargs.pop("freq") - ticker_series = TimeSeries.from_dataframe(**filler_kwargs) - except AttributeError: - # Uses index if no date column - console.print( - "[red]Warning: no date found, assuming items evenly spaced.[/red]\n" - ) - filler_kwargs.pop("time_col") - ticker_series = TimeSeries.from_dataframe(**filler_kwargs) - - if is_scaler: - scaler = Scaler(scaler=MaxAbsScaler()) - scaled_ticker_series = scaler.fit_transform( - filler.transform(ticker_series) - ).astype(np.float32) - return scaler, scaled_ticker_series - ticker_series = filler.transform(ticker_series).astype(np.float32) - scaler = None - return scaler, ticker_series - - -def fit_model( - model: type[GlobalForecastingModel], - series: TimeSeries, - val_series: Optional[TimeSeries] = None, - past_covariates: Optional[TimeSeries] = None, - val_past_covariates: Optional[TimeSeries] = None, - **kwargs, -): - fit_kwargs = dict( - series=series, - ) - fit_kwargs.update(kwargs) - if val_series is not None: - fit_kwargs["val_series"] = val_series - if past_covariates is not None: - fit_kwargs["past_covariates"] = past_covariates - fit_kwargs["val_past_covariates"] = val_past_covariates - - model.fit(**fit_kwargs) - - -@log_start_end(log=logger) -def get_prediction( - model_name: str, - probabilistic: bool, - use_scalers: bool, - scaler: Optional[Scaler], - past_covariates: Optional[str], - best_model: type[GlobalForecastingModel], - ticker_series: TimeSeries, - past_covariate_whole: Optional[TimeSeries], - train_split: float, - forecast_horizon: int, - n_predict: int, - metric: str, -): - _, val = ticker_series.split_before(train_split) - - console.print(f"Predicting {model_name} for {n_predict} periods") - if model_name not in ["Regression", "Logistic Regression"]: - # need to create a new pytorch trainer for historical backtesting to remove progress bar - best_model.trainer = None - # best_model.trainer_params["enable_progress_bar"] = True - - # Historical backtest if with covariates - if past_covariates is not None: - historical_fcast = best_model.historical_forecasts( - ticker_series, - past_covariates=past_covariate_whole, - start=train_split, - forecast_horizon=forecast_horizon, - retrain=False, - verbose=True, - ) - # historical backtest without covariates - else: - historical_fcast = best_model.historical_forecasts( - ticker_series, - start=train_split, - forecast_horizon=forecast_horizon, - retrain=False, - verbose=True, - ) - - # now predict N days in the future - if past_covariates is not None: - if probabilistic: - prediction = best_model.predict( - series=ticker_series, - past_covariates=past_covariate_whole, - n=n_predict, - num_samples=500, - ) - else: - prediction = best_model.predict( - series=ticker_series, - past_covariates=past_covariate_whole, - n=n_predict, - ) - elif probabilistic: - prediction = best_model.predict( - series=ticker_series, n=n_predict, num_samples=500 - ) - else: - prediction = best_model.predict(series=ticker_series, n=n_predict) - - # calculate precision based on metric (rmse, mse, mape) - if metric == "rmse": - precision = rmse(actual_series=val, pred_series=historical_fcast) - elif metric == "mse": - precision = mse(actual_series=val, pred_series=historical_fcast) - elif metric == "mape": - precision = mape(actual_series=val, pred_series=historical_fcast) - elif metric == "smape": - precision = smape(actual_series=val, pred_series=historical_fcast) - - console.print(f"{model_name} model obtains {metric.upper()}: {precision:.2f}% \n") - - # scale back - if use_scalers and isinstance(scaler, Scaler): - ticker_series = scaler.inverse_transform(ticker_series) - historical_fcast = scaler.inverse_transform(historical_fcast) - prediction = scaler.inverse_transform(prediction) - - return ticker_series, historical_fcast, prediction, precision, best_model - - -def check_parser_input(parser: argparse.ArgumentParser, datasets, *args) -> bool: - # check proper file name is provided - if not hasattr(parser, "target_dataset"): - return False - if not parser.target_dataset: # type: ignore - console.print("[red]Please select a valid dataset with the -d flag.\n[/red]") - return False - if "ignore_column" in args: - return True - if not hasattr(parser, "target_column"): - return False - - # must check that data["date"] col is within target series - if parser.target_column not in datasets[parser.target_dataset].columns: # type: ignore - console.print( - f"[red]The column {parser.target_column} does not exist.\n[/red]" # type: ignore - ) - return False - return True - - -@log_start_end(log=logger) -def plot_residuals( - model: type[GlobalForecastingModel], - past_covariates: Optional[str], - series: Optional[Union[type[TimeSeries], List[type[TimeSeries]], List[np.ndarray]]], - forecast_horizon: int = 1, - num_bins: int = 20, - default_formatting: bool = False, -): - del default_formatting - - if past_covariates: - console.print( - "[red]Cannot calculate and plot residuals if there are past covariates.[/red]" - ) - else: - console.print( - "[green]Calculating and plotting residuals... This may take a few moments.[/green]" - ) - my_stopper = early_stopper(5, "val_loss") - pl_trainer_kwargs = {"callbacks": [my_stopper], "accelerator": "cpu"} - model.trainer_params = pl_trainer_kwargs - residuals: TimeSeries = model.residuals( - series=series, - forecast_horizon=forecast_horizon, - retrain=False, - verbose=True, - ) - - residuals._assert_univariate() # pylint: disable=protected-access - - fig = OpenBBFigure.create_subplots( - rows=2, - cols=2, - shared_xaxes=False, - subplot_titles=("Residuals Values", "ACF", "Distribution"), - specs=[ - [{"colspan": 2}, None], # type: ignore - [{"type": "scatter"}, {"type": "histogram"}], - ], - ) - fig.set_xaxis_title("date", row=1, col=1) - fig.set_yaxis_title("value", row=1, col=1) - fig.set_xaxis_title("lag", row=2, col=1) - fig.set_yaxis_title("ACF value", row=2, col=1) - fig.set_xaxis_title("value", row=2, col=2) - fig.set_yaxis_title("count", row=2, col=2) - - df_res = residuals.pd_dataframe() - fig.add_scatter(x=df_res.index, y=df_res["close"], name="close", row=1, col=1) - fig.add_corr_plot(residuals.univariate_values(), row=2, col=1) - fig.add_histplot(residuals, row=2, col=2, bins=num_bins, forecast=True) - fig.update_traces(showlegend=False) - fig.update_layout(hovermode="closest") - fig.show() - - -def check_data_length( - train, test, input_chunk_length: int, output_chunk_length: int -) -> bool: - if input_chunk_length + output_chunk_length > len(train): - base = "[red]Not enough train data. Please increase the " - console.print( - base + "train test split or decrease input or output chunk length.[/red]\n" - ) - return False - if input_chunk_length + output_chunk_length > len(test): - base = "[red]Not enough test data. Please decrease the " - console.print( - base - + "train test split or decrease the input or output chunk length.[/red]\n" - ) - return False - return True - - -def filter_dates( - data: Union[pd.DataFrame, pd.Series], - start_date: Optional[datetime], - end_date: Optional[datetime], -) -> Union[pd.DataFrame, pd.Series]: - if start_date and end_date and start_date >= end_date: - console.print("[red]The start date must be before the end date.[/red]\n") - return data - if end_date: - if isinstance(data["date"].values[0], str): - data = data[pd.to_datetime(data["date"]) <= end_date] - else: - data = data[data["date"] <= end_date] - if start_date: - if isinstance(data["date"].values[0], str): - data = data[pd.to_datetime(data["date"]) >= start_date] - else: - data = data[data["date"] >= start_date] - return data - - -def clean_data( - data: Union[pd.DataFrame, pd.Series], - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - target_column: Optional[str] = None, - past_covariates: Optional[str] = None, -) -> Union[pd.DataFrame, pd.Series]: - # check if target column is in data and if the target_column has any inf - # replace all inf with nan. This is because darts does not handle inf - # Creating a timeseries with fillna=True will replace all nan with interoplated values - if ( - target_column - and target_column in data.columns - and data[target_column].isin([np.inf, -np.inf]).any() - ): - console.print( - f"[red]The target column [{target_column}] has inf values. Cleaning...[/red]\n" - ) - data = data.replace([np.inf, -np.inf], np.nan) - - # check if past covariates are in data and if they have any inf - if past_covariates: - covariates = past_covariates.split(",") - for covariate in covariates: - if ( - covariate in data.columns - and data[covariate].isin([np.inf, -np.inf]).any() - ): - console.print( - f"[red]The covariate:{covariate} has inf values. Cleaning...[/red]\n" - ) - data = data.replace([np.inf, -np.inf], np.nan) - - if isinstance(data, pd.Series): - col = data.name - columns = ["date", col] - data = pd.DataFrame(data).reset_index() - data.columns = columns - data = filter_dates(data, start_date, end_date) - data = data.set_index("date") - data = data[col] - elif "date" in data.columns: - data = filter_dates(data, start_date, end_date) - elif end_date or start_date: - console.print( - "[red]No 'date' column specified, ignoring start end end date[/red]\n" - ) - return data - - -def clean_covariates(parser, dataset: pd.DataFrame) -> Optional[str]: - forecast_column: str = parser.target_column - if parser.all_past_covariates: - data = dataset - covs = [x for x in data.columns if x not in ["date", parser.target_column]] - return ",".join(covs) - covariates = parser.past_covariates - if not covariates: - return covariates - covs_list = covariates.split(",") - for covariate in covs_list: - if covariate == forecast_column: - covs_list.remove(covariate) - console.print( - f"[red]Forecast target '{forecast_column}' cannot be within the past" - " covariates. Removed the covariates from analysis[/red]\n" - ) - if len(covariates) == 0: - return None - covariates = ",".join(covs_list) - return covariates - - -def check_data( - data: pd.DataFrame, target_column: str, past_covariates: Optional[str] = None -) -> bool: - if target_column not in data.columns: - console.print( - f"[red]Column {target_column} is not in the dataframe." - " Change the 'target_column' parameter.[/red]\n" - ) - return False - if data.empty: - console.print("[red]The data provided is empty.[/red]\n") - return False - if past_covariates is not None: - covariates = past_covariates.split(",") - for covariate in covariates: - if covariate not in data.columns: - console.print( - f"[red]Column {covariate} is not in the dataframe." - " Change the 'past_covariates' parameter.[/red]\n" - ) - return False - return True - - -def check_output( - output_chunk_length: int, n_predict: int, past_covariates: bool -) -> int: - if past_covariates: - if output_chunk_length != n_predict: - console.print( - "[red]Warning: when using past covariates n_predict must equal" - f" output_chunk_length. We have changed your output_chunk_length to {n_predict}" - " to match your n_predict" - ) - return n_predict - return output_chunk_length - - -def check_dates(s: pd.Series) -> bool: - """Checks whether all hours, minutes, seconds and milliseconds are 0""" "" - for _, value in s.items(): - try: - if value.time() != time(0, 0, 0, 0): - return False - except AttributeError: - return False - return True - - -def print_tensorboard_logs(model_save_name: str, user_directory: str): - console.print() - tensorboard_logs_dir = os.path.join(user_directory, model_save_name, "logs") - console.print( - "This model supports Tensorboard logs. To view, open a new terminal windown and run:" - ) - console.print("tensorboard --logdir=" + tensorboard_logs_dir) - console.print() diff --git a/openbb_terminal/forecast/linregr_model.py b/openbb_terminal/forecast/linregr_model.py deleted file mode 100644 index 4ad548cc22c..00000000000 --- a/openbb_terminal/forecast/linregr_model.py +++ /dev/null @@ -1,116 +0,0 @@ -# pylint: disable=too-many-arguments -"""Linear Regression Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models import LinearRegressionModel - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_linear_regression_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - output_chunk_length: int = 5, - lags: Union[int, List[int]] = 14, - random_state: Optional[int] = None, - metric: str = "mape", -) -> Tuple[ - List[TimeSeries], - List[TimeSeries], - List[TimeSeries], - float, - LinearRegressionModel, -]: - """Perform Linear Regression Forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - output_chunk_length: int - The length of the forecast of the model. Defaults to 1. - lags: Union[int, List[int]] - lagged target values to predict the next time step - random_state: Optional[int] - The state for the model - metric: str - The metric to use for the model. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], float, LinearRegressionModel] - Adjusted Data series, - Historical forecast by best RNN model, - list of Predictions, - Mean average precision error, - Best Linear Regression Model. - """ - use_scalers = False - probabilistic = True - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - - past_covariate_whole, _, _ = helpers.past_covs( - past_covariates, data, train_split, use_scalers - ) - - lags_past_covariates = lags if past_covariates is not None else None - - lin_reg_model = LinearRegressionModel( - output_chunk_length=output_chunk_length, - lags=lags, - lags_past_covariates=lags_past_covariates, - likelihood="quantile", - quantiles=[0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95], - random_state=random_state, - ) - - with warnings.catch_warnings(): - warnings.simplefilter(action="ignore", category=FutureWarning) - if past_covariates is not None: - lin_reg_model.fit( - series=ticker_series, past_covariates=past_covariate_whole - ) - else: - lin_reg_model.fit(series=ticker_series) - - return helpers.get_prediction( - "Logistic Regression", - probabilistic, - use_scalers, - scaler, - past_covariates, - lin_reg_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/linregr_view.py b/openbb_terminal/forecast/linregr_view.py deleted file mode 100644 index d4b2585edf8..00000000000 --- a/openbb_terminal/forecast/linregr_view.py +++ /dev/null @@ -1,143 +0,0 @@ -"""Linear Regression View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import List, Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, linregr_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_linear_regression( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - output_chunk_length: int = 5, - lags: Union[int, List[int]] = 14, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - explainability_raw: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display Linear Regression Forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - dataset_name: str - The name of the ticker to be predicted - n_predict: int - Days to predict. Defaults to 5. - target_col: str - Target column to forecast. Defaults to "close". - train_split: (float, optional) - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - output_chunk_length: int - The length of the forecast of the model. Defaults to 1. - lags: Union[int, List[int]] - lagged target values to predict the next time step - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the - same as the previous day's closing price. Defaults to False. - explainability_raw: bool - Whether to show the raw explainability data. Defaults to False. - export_pred_raw: bool - Whether to export the raw prediction data. Defaults to False. - metric: str - The metric to use for the model. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data( - data, start_date, end_date, target_column, past_covariates - ) - if not helpers.check_data(data, target_column, past_covariates): - return None - output_chunk_length = helpers.check_output( - output_chunk_length, n_predict, bool(past_covariates) - ) - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = linregr_model.get_linear_regression_data( - data=data, - n_predict=n_predict, - target_column=target_column, - past_covariates=past_covariates, - train_split=train_split, - forecast_horizon=forecast_horizon, - output_chunk_length=output_chunk_length, - lags=lags, - metric=metric, - ) - - probabilistic = True - fig = helpers.plot_forecast( - name="LINREGR", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - metric=metric, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - # SHAP - helpers.plot_explainability(_model, explainability_raw, export=export) - - return fig diff --git a/openbb_terminal/forecast/mstl_model.py b/openbb_terminal/forecast/mstl_model.py deleted file mode 100644 index 8ac60a83987..00000000000 --- a/openbb_terminal/forecast/mstl_model.py +++ /dev/null @@ -1,155 +0,0 @@ -# pylint: disable=too-many-arguments,E1101 -"""Multiple Seasonalities and Trend using Loess (MSTL) Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from statsforecast.core import StatsForecast - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -warnings.simplefilter("ignore") - -logger = logging.getLogger(__name__) - -# pylint: disable=E1123 - - -@log_start_end(log=logger) -def get_mstl_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, -) -> Tuple[ - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[float], - Optional[StatsForecast], -]: - """Performs MSTL forecasting - This is a wrapper around StatsForecast MSTL; - we refer to this link for the original and more complete documentation of the parameters. - - - https://nixtla.github.io/statsforecast/models.html#mstl - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year (7 for daily data) - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - - Returns - ------- - Tuple[List[np.ndarray], List[np.ndarray], List[np.ndarray], Optional[float], StatsForecast]: - Adjusted Data series, - List of historical fcast values, - List of predicted fcast values, - Optional[float] - precision, - Fit MSTL model object. - """ - - use_scalers = False - # statsforecast preprocessing - # when including more time series - # the preprocessing is similar - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - freq = ticker_series.freq_str - ticker_series = ticker_series.pd_dataframe().reset_index() - ticker_series.columns = ["ds", "y"] - ticker_series.insert(0, "unique_id", target_column) - # check MSLT availability - try: - from statsforecast.models import MSTL # pylint: disable=import-outside-toplevel - except Exception as e: - error = str(e) - if "cannot import name" in error: - console.print( - "[red]Please update statsforecast to version 1.2.0 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None - - try: - # Model Init - model = MSTL( - season_length=int(seasonal_periods), - ) - fcst = StatsForecast(df=ticker_series, models=[model], freq=freq, verbose=True) - except Exception as e: # noqa - error = str(e) - if "got an unexpected keyword argument" in error: - console.print( - "[red]Please update statsforecast to version 1.1.3 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None - - # Historical backtesting - last_training_point = int((len(ticker_series) - 1) * start_window) - historical_fcast = fcst.cross_validation( - h=int(forecast_horizon), - test_size=len(ticker_series) - last_training_point, - n_windows=None, - input_size=min(10 * forecast_horizon, len(ticker_series)), - ) - - # train new model on entire timeseries to provide best current forecast - # we have the historical fcast, now lets predict. - forecast = fcst.forecast(int(n_predict)) - y_true = historical_fcast["y"].values - y_hat = historical_fcast["MSTL"].values - precision = helpers.mean_absolute_percentage_error(y_true, y_hat) - console.print(f"MSTL obtains MAPE: {precision:.2f}% \n") - - # transform outputs to make them compatible with - # plots - use_scalers = False - _, ticker_series = helpers.get_series( - ticker_series.rename(columns={"y": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, forecast = helpers.get_series( - forecast.rename(columns={"MSTL": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, historical_fcast = helpers.get_series( - historical_fcast.groupby("ds").head(1).rename(columns={"MSTL": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - - return ( - ticker_series, - historical_fcast, - forecast, - float(precision), - fcst, - ) diff --git a/openbb_terminal/forecast/mstl_view.py b/openbb_terminal/forecast/mstl_view.py deleted file mode 100644 index 94cc186a473..00000000000 --- a/openbb_terminal/forecast/mstl_view.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Multiple Seasonalities and Trend using Loess (MSTL) View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, mstl_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_mstl_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - external_axes: bool = False, -): - """Display MSTL Model - - Parameters - ---------- - data: Union[pd.Series, np.array] - Data to forecast - dataset_name str - The name of the ticker to be predicted - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = mstl_model.get_mstl_data( - data=data, - target_column=target_column, - seasonal_periods=seasonal_periods, - n_predict=n_predict, - start_window=start_window, - forecast_horizon=forecast_horizon, - ) - - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="MSTL", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, None, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/nbeats_model.py b/openbb_terminal/forecast/nbeats_model.py deleted file mode 100644 index 72b7017cce1..00000000000 --- a/openbb_terminal/forecast/nbeats_model.py +++ /dev/null @@ -1,182 +0,0 @@ -# pylint: disable=too-many-arguments -"""NBEATS Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models import NBEATSModel - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_NBEATS_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - num_stacks: int = 10, - num_blocks: int = 3, - num_layers: int = 4, - layer_widths: int = 512, - batch_size: int = 800, - n_epochs: int = 300, - learning_rate: float = 1e-3, - model_save_name: str = "nbeats_model", - force_reset: bool = True, - save_checkpoints: bool = True, - metric: str = "mape", -) -> Tuple[ - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[float], - Optional[type[NBEATSModel]], -]: - """Perform NBEATS Forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - num_stacks: int - The number of stacks that make up the whole model. Defaults to 10. - num_blocks: int - The number of blocks making up every stack. Defaults to 3. - num_layers: int - The number of fully connected layers preceding the final forking layers in each block - of every stack. Defaults to 4. - layer_widths: int - Determines the number of neurons that make up each fully connected layer in each block - of every stack. Defaults to 512. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - metric: str - Metric to use for model selection. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], type[NBEATSModel]] - Adjusted Data series, - Historical forecast by best RNN model, - list of Predictions, - Mean average precision error, - Best NBEATS Model. - """ - - use_scalers = True - probabilistic = False - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - train, val = ticker_series.split_before(train_split) - valid = helpers.check_data_length( - train, val, input_chunk_length, output_chunk_length - ) - if not valid: - return [], [], [], None, None - - ( - past_covariate_whole, - past_covariate_train, - past_covariate_val, - ) = helpers.past_covs(past_covariates, data, train_split, use_scalers) - - current_user = get_current_user() - - nbeats_model = NBEATSModel( - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - generic_architecture=True, - num_stacks=num_stacks, - num_blocks=num_blocks, - num_layers=num_layers, - layer_widths=layer_widths, - n_epochs=n_epochs, - nr_epochs_val_period=1, - batch_size=batch_size, - optimizer_kwargs={"lr": learning_rate}, - model_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - random_state=42, - pl_trainer_kwargs=helpers.get_pl_kwargs(accelerator="cpu"), - log_tensorboard=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - # fit model on train series for historical forecasting - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - helpers.fit_model( - nbeats_model, - train, - val, - past_covariate_train, - past_covariate_val, - ) - - best_model = NBEATSModel.load_from_checkpoint( - model_name=model_save_name, - best=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - helpers.print_tensorboard_logs( - model_save_name, str(current_user.preferences.USER_FORECAST_MODELS_DIRECTORY) - ) - - # Showing historical backtesting without retraining model (too slow) - return helpers.get_prediction( - "NBEATS", - probabilistic, - use_scalers, - scaler, - past_covariates, - best_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/nbeats_view.py b/openbb_terminal/forecast/nbeats_view.py deleted file mode 100644 index 8fd07b31806..00000000000 --- a/openbb_terminal/forecast/nbeats_view.py +++ /dev/null @@ -1,185 +0,0 @@ -"""NBEATS View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, nbeats_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_nbeats_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - num_stacks: int = 10, - num_blocks: int = 3, - num_layers: int = 4, - layer_widths: int = 512, - n_epochs: int = 300, - learning_rate: float = 1e-3, - batch_size: int = 800, - model_save_name: str = "nbeats_model", - force_reset: bool = True, - save_checkpoints: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display NBEATS forecast - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - dataset_name: str - The name of the ticker to be predicted - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - num_stacks: int - The number of stacks that make up the whole model. Defaults to 10. - num_blocks: int - The number of blocks making up every stack. Defaults to 3. - num_layers: int - The number of fully connected layers preceding the final forking layers in each block - of every stack. Defaults to 4. - layer_widths: int - Determines the number of neurons that make up each fully connected layer in each block - of every stack. Defaults to 512. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults - to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset (all - checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - export_pred_raw: bool - Whether to export the raw predicted values. Defaults to False. - metric: str - Metric to use for evaluation. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - data = helpers.clean_data( - data, start_date, end_date, target_column, past_covariates - ) - if not helpers.check_data(data, target_column, past_covariates): - return None - output_chunk_length = helpers.check_output( - output_chunk_length, n_predict, bool(past_covariates) - ) - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = nbeats_model.get_NBEATS_data( - data=data, - n_predict=n_predict, - target_column=target_column, - past_covariates=past_covariates, - train_split=train_split, - forecast_horizon=forecast_horizon, - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - num_stacks=num_stacks, - num_blocks=num_blocks, - num_layers=num_layers, - layer_widths=layer_widths, - batch_size=batch_size, - n_epochs=n_epochs, - learning_rate=learning_rate, - model_save_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - metric=metric, - ) - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="NBEATS", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - metric=metric, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/nhits_model.py b/openbb_terminal/forecast/nhits_model.py deleted file mode 100644 index bc56e4ee072..00000000000 --- a/openbb_terminal/forecast/nhits_model.py +++ /dev/null @@ -1,210 +0,0 @@ -# pylint: disable=too-many-arguments -"""Nhits Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models.forecasting.nhits import NHiTSModel -from darts.utils.likelihood_models import GaussianLikelihood - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_nhits_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - train_split: float = 0.85, - past_covariates: Optional[str] = None, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - num_stacks: int = 3, - num_blocks: int = 1, - num_layers: int = 2, - layer_widths: int = 512, - pooling_kernel_sizes: Optional[Tuple[Tuple[int]]] = None, - n_freq_downsample: Optional[Tuple[Tuple[int]]] = None, - dropout: float = 0.1, - activation: str = "ReLU", - max_pool_1d: bool = True, - batch_size: int = 32, - n_epochs: int = 100, - learning_rate: float = 1e-3, - model_save_name: str = "nhits_model", - force_reset: bool = True, - save_checkpoints: bool = True, - metric: str = "mape", -) -> Tuple[ - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[float], - Optional[type[NHiTSModel]], -]: - """Performs Nhits forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - num_stacks: int - The number of stacks that make up the whole model. - num_blocks: int - The number of blocks making up every stack. - num_layers: int - The number of fully connected layers preceding the final forking layers in each block - of every stack. - layer_widths: int - Determines the number of neurons that make up each fully connected layer in each - block of every stack. If a list is passed, it must have a length equal to num_stacks - and every entry in that list corresponds to the layer width of the corresponding stack. - If an integer is passed, every stack will have blocks with FC layers of the same width. - pooling_kernel_size: Optional[Tuple[Tuple[int]]]: - If set, this parameter must be a tuple of tuples, of size (num_stacks x num_blocks), - specifying the kernel size for each block in each stack used for the input pooling - layer. If left to None, some default values will be used based on input_chunk_length. - n_freq_downsample: Optional[Tuple[Tuple[int]]] - If set, this parameter must be a tuple of tuples, of size (num_stacks x num_blocks), - specifying the downsampling factors before interpolation, for each block in each stack. - If left to None, some default values will be used based on output_chunk_length. - dropout: float - The dropout probability to be used in fully connected layers. - activation: str - Supported activations: [‘ReLU’,’RReLU’, ‘PReLU’, ‘Softplus’, ‘Tanh’, ‘SELU’, ‘LeakyReLU’, ‘Sigmoid’] - max_pool_1d: bool - Use max_pool_1d pooling. False uses AvgPool1d. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset (all checkpoints will be - discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. Defaults to True. - metric: str - Metric to use for model selection. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], type[NHiTSModel]] - Adjusted Data series, - Historical forecast by best RNN model, - list of Predictions, - Mean average precision error, - Best BRNN Model. - """ - - # TODO Check if torch GPU AVAILABLE - - use_scalers = True - probabilistic = False - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - train, val = ticker_series.split_before(train_split) - valid = helpers.check_data_length( - train, val, input_chunk_length, output_chunk_length - ) - if not valid: - return [], [], [], None, None - - ( - past_covariate_whole, - past_covariate_train, - past_covariate_val, - ) = helpers.past_covs(past_covariates, data, train_split, use_scalers) - - # Early Stopping - - current_user = get_current_user() - - nhits_model = NHiTSModel( - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - num_stacks=num_stacks, - num_blocks=num_blocks, - num_layers=num_layers, - layer_widths=layer_widths, - pooling_kernel_sizes=pooling_kernel_sizes, - n_freq_downsample=n_freq_downsample, - dropout=dropout, - activation=activation, - MaxPool1d=max_pool_1d, - n_epochs=n_epochs, - batch_size=batch_size, - optimizer_kwargs={"lr": learning_rate}, - model_name=model_save_name, - random_state=42, - pl_trainer_kwargs=helpers.get_pl_kwargs(accelerator="cpu"), - force_reset=force_reset, - save_checkpoints=save_checkpoints, - likelihood=GaussianLikelihood(), - log_tensorboard=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - # fit model on train series for historical forecasting - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - helpers.fit_model( - nhits_model, - train, - val, - past_covariate_train, - past_covariate_val, - ) - best_model = NHiTSModel.load_from_checkpoint( - model_name=model_save_name, - best=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - helpers.print_tensorboard_logs( - model_save_name, str(current_user.preferences.USER_FORECAST_MODELS_DIRECTORY) - ) - - # Showing historical backtesting without retraining model (too slow) - return helpers.get_prediction( - "NHITS", - probabilistic, - use_scalers, - scaler, - past_covariates, - best_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/nhits_view.py b/openbb_terminal/forecast/nhits_view.py deleted file mode 100644 index 7855e5277fb..00000000000 --- a/openbb_terminal/forecast/nhits_view.py +++ /dev/null @@ -1,209 +0,0 @@ -"""Nhits View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Tuple, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, nhits_model - -logger = logging.getLogger(__name__) -# pylint: disable=R0913,R0914 - - -@log_start_end(log=logger) -def display_nhits_forecast( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - num_stacks: int = 3, - num_blocks: int = 1, - num_layers: int = 2, - layer_widths: int = 512, - pooling_kernel_sizes: Optional[Tuple[Tuple[int]]] = None, - n_freq_downsample: Optional[Tuple[Tuple[int]]] = None, - dropout: float = 0.1, - activation: str = "ReLU", - max_pool_1d: bool = True, - batch_size: int = 32, - n_epochs: int = 100, - learning_rate: float = 1e-3, - model_save_name: str = "nhits_model", - force_reset: bool = True, - save_checkpoints: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display Nhits forecast - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - dataset_name: str - The name of the ticker to be predicted - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - num_stacks: int - The number of stacks that make up the whole model. - num_blocks: int - The number of blocks making up every stack. - num_layers: int - The number of fully connected layers preceding the final forking layers in each block - of every stack. - layer_widths: int - Determines the number of neurons that make up each fully connected layer in each - block of every stack. If a list is passed, it must have a length equal to num_stacks - and every entry in that list corresponds to the layer width of the corresponding stack. - If an integer is passed, every stack will have blocks with FC layers of the same width. - pooling_kernel_size: Optional[Tuple[Tuple[int]]] - If set, this parameter must be a tuple of tuples, of size (num_stacks x num_blocks), - specifying the kernel size for each block in each stack used for the input pooling - layer. If left to None, some default values will be used based on input_chunk_length. - n_freq_downsample: Optional[Tuple[Tuple[int]]] - If set, this parameter must be a tuple of tuples, of size (num_stacks x num_blocks), - specifying the downsampling factors before interpolation, for each block in each stack. - If left to None, some default values will be used based on output_chunk_length. - dropout: float - The dropout probability to be used in fully connected layers. - activation: str - Supported activations: [[‘ReLU’,’RReLU’, ‘PReLU’, ‘Softplus’, ‘Tanh’, ‘SELU’, ‘LeakyReLU’, ‘Sigmoid’] - max_pool_1d: bool - Use max_pool_1d pooling. False uses AvgPool1d. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - export_pred_raw: bool - Whether to export the raw predicted values. Defaults to False. - metric: str - Metric to use for evaluation. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data( - data, start_date, end_date, target_column, past_covariates - ) - if not helpers.check_data(data, target_column, past_covariates): - return None - output_chunk_length = helpers.check_output( - output_chunk_length, n_predict, bool(past_covariates) - ) - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = nhits_model.get_nhits_data( - data=data, - n_predict=n_predict, - target_column=target_column, - past_covariates=past_covariates, - train_split=train_split, - forecast_horizon=forecast_horizon, - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - num_stacks=num_stacks, - num_blocks=num_blocks, - num_layers=num_layers, - layer_widths=layer_widths, - pooling_kernel_sizes=pooling_kernel_sizes, - n_freq_downsample=n_freq_downsample, - dropout=dropout, - activation=activation, - max_pool_1d=max_pool_1d, - batch_size=batch_size, - n_epochs=n_epochs, - learning_rate=learning_rate, - model_save_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - metric=metric, - ) - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="NHITS", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - metric=metric, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/regr_model.py b/openbb_terminal/forecast/regr_model.py deleted file mode 100644 index 5ebd3da6abd..00000000000 --- a/openbb_terminal/forecast/regr_model.py +++ /dev/null @@ -1,103 +0,0 @@ -# pylint: disable=too-many-arguments -"""Regression Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models import RegressionModel - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_regression_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - output_chunk_length: int = 5, - lags: Union[int, List[int]] = 14, - metric: str = "mape", -) -> Tuple[ - List[TimeSeries], List[TimeSeries], List[TimeSeries], float, type[RegressionModel] -]: - """Perform Regression Forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - n_predict: int - Days to predict. Defaults to 5. - target_column: str - Target column to forecast. Defaults to "close". - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - output_chunk_length: int - The length of the forecast of the model. Defaults to 1. - lags: Union[int, List[int]] - lagged target values to predict the next time step - metric: str - Metric to use for evaluation. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], float, type[RegressionModel]] - Adjusted Data series, - Historical forecast by best RNN model, - list of Predictions, - Mean average precision error, - Best Regression Model. - """ - - use_scalers = False - probabilistic = False - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - - past_covariate_whole, _, _ = helpers.past_covs( - past_covariates, data, train_split, use_scalers - ) - - lags_past_covariates = lags if past_covariates is not None else None - - reg_model = RegressionModel( - output_chunk_length=output_chunk_length, - lags=lags, - lags_past_covariates=lags_past_covariates, - ) - - with warnings.catch_warnings(): - warnings.simplefilter(action="ignore", category=FutureWarning) - reg_model.fit(ticker_series, past_covariate_whole) - - # Showing historical backtesting without retraining model (too slow) - return helpers.get_prediction( - "Regression", - probabilistic, - use_scalers, - scaler, - past_covariates, - reg_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/regr_view.py b/openbb_terminal/forecast/regr_view.py deleted file mode 100644 index a3b574563b5..00000000000 --- a/openbb_terminal/forecast/regr_view.py +++ /dev/null @@ -1,138 +0,0 @@ -""" Regression View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import List, Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, regr_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_regression( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - output_chunk_length: int = 5, - lags: Union[int, List[int]] = 14, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - explainability_raw: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display Regression Forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - dataset_name: str - The name of the ticker to be predicted - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - output_chunk_length: int - The length of the forecast of the model. Defaults to 1. - lags: Union[int, List[int]] - lagged target values to predict the next time step - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - metric: str - The metric to use for the forecast. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data( - data, start_date, end_date, target_column, past_covariates - ) - if not helpers.check_data(data, target_column, past_covariates): - return None - output_chunk_length = helpers.check_output( - output_chunk_length, n_predict, bool(past_covariates) - ) - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = regr_model.get_regression_data( - data=data, - n_predict=n_predict, - target_column=target_column, - past_covariates=past_covariates, - train_split=train_split, - forecast_horizon=forecast_horizon, - output_chunk_length=output_chunk_length, - lags=lags, - metric=metric, - ) - probabilistic = False - fig = helpers.plot_forecast( - name="REGR", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - metric=metric, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - # SHAP - helpers.plot_explainability(_model, explainability_raw, export=export) - - return fig diff --git a/openbb_terminal/forecast/rnn_model.py b/openbb_terminal/forecast/rnn_model.py deleted file mode 100644 index f70c0963f25..00000000000 --- a/openbb_terminal/forecast/rnn_model.py +++ /dev/null @@ -1,158 +0,0 @@ -# pylint: disable=too-many-arguments -"""RNN Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models import RNNModel -from darts.utils.likelihood_models import GaussianLikelihood - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_rnn_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - train_split: float = 0.85, - forecast_horizon: int = 5, - model_type: str = "LSTM", - hidden_dim: int = 20, - dropout: float = 0.0, - batch_size: int = 32, - n_epochs: int = 100, - learning_rate: float = 1e-3, - model_save_name: str = "rnn_model", - training_length: int = 20, - input_chunk_size: int = 14, - force_reset: bool = True, - save_checkpoints: bool = True, - metric: str = "mape", -) -> Tuple[ - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[float], - Optional[type[RNNModel]], -]: - """Perform RNN forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - n_predict: int - Days to predict. Defaults to 5. - target_column: str - Target column to forecast. Defaults to "close". - train_split: float - Train/val split. Defaults to 0.85. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - model_type: str - Either a string specifying the RNN module type ("RNN", "LSTM" or "GRU"). Defaults to "LSTM". - hidden_dim: int - Size for feature maps for each hidden RNN layer.. Defaults to 20. - dropout: float - Fraction of neurons affected by Dropout. Defaults to 0.0. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - metric: str - Metric to use for model selection. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], type[RNNModel]] - Adjusted Data series, - Historical forecast by best RNN model, - list of Predictions, - Mean average precision error, - Best RNN Model - """ - - # TODO Check if torch GPU AVAILABLE - - use_scalers = True - probabilistic = True - past_covariates = None - past_covariate_whole = None - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - train, val = ticker_series.split_before(train_split) - valid = helpers.check_data_length(train, val, input_chunk_size, 0) - if not valid: - return [], [], [], None, None - - current_user = get_current_user() - - rnn_model = RNNModel( - model=model_type, - hidden_dim=hidden_dim, - dropout=dropout, - batch_size=batch_size, - n_epochs=n_epochs, - optimizer_kwargs={"lr": learning_rate}, - model_name=model_save_name, - random_state=42, - training_length=training_length, - input_chunk_length=input_chunk_size, - pl_trainer_kwargs=helpers.get_pl_kwargs(accelerator="cpu"), - force_reset=force_reset, - save_checkpoints=save_checkpoints, - likelihood=GaussianLikelihood(), - log_tensorboard=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - # fit model on train series for historical forecasting - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - helpers.fit_model(rnn_model, train, val) - best_model = RNNModel.load_from_checkpoint( - model_name=model_save_name, - best=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - helpers.print_tensorboard_logs( - model_save_name, str(current_user.preferences.USER_FORECAST_MODELS_DIRECTORY) - ) - - # Showing historical backtesting without retraining model (too slow) - return helpers.get_prediction( - "RNN", - probabilistic, - use_scalers, - scaler, - past_covariates, - best_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/rnn_view.py b/openbb_terminal/forecast/rnn_view.py deleted file mode 100644 index 443f23bb478..00000000000 --- a/openbb_terminal/forecast/rnn_view.py +++ /dev/null @@ -1,165 +0,0 @@ -"""RNN View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, rnn_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_rnn_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - train_split: float = 0.85, - forecast_horizon: int = 5, - model_type: str = "LSTM", - hidden_dim: int = 20, - dropout: float = 0.0, - batch_size: int = 32, - n_epochs: int = 100, - learning_rate: float = 1e-3, - model_save_name: str = "rnn_model", - training_length: int = 20, - input_chunk_size: int = 14, - force_reset: bool = True, - save_checkpoints: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display RNN forecast - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - dataset_name: str - The name of the ticker to be predicted - n_predict: int - Days to predict. Defaults to 5. - target_column: str - Target column to forecast. Defaults to "close". - train_split: float - Train/val split. Defaults to 0.85. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - model_type: str - Either a string specifying the RNN module type ("RNN", "LSTM" or "GRU"). Defaults to "LSTM". - hidden_dim: int - Size for feature maps for each hidden RNN layer.. Defaults to 20. - dropout: float - Fraction of neurons affected by Dropout. Defaults to 0.0. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. Defaults to True. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - export_pred_raw: bool - Whether to export the raw predictions. Defaults to False. - metric: str - The metric to use for the forecast. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = rnn_model.get_rnn_data( - data=data, - n_predict=n_predict, - target_column=target_column, - train_split=train_split, - forecast_horizon=forecast_horizon, - model_type=model_type, - hidden_dim=hidden_dim, - dropout=dropout, - batch_size=batch_size, - n_epochs=n_epochs, - learning_rate=learning_rate, - model_save_name=model_save_name, - training_length=training_length, - input_chunk_size=input_chunk_size, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - metric=metric, - ) - if ticker_series == []: - return None - - past_covariates = None - probabilistic = True - fig = helpers.plot_forecast( - name="RNN", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - metric=metric, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/rwd_model.py b/openbb_terminal/forecast/rwd_model.py deleted file mode 100644 index 753b9e170f0..00000000000 --- a/openbb_terminal/forecast/rwd_model.py +++ /dev/null @@ -1,134 +0,0 @@ -# pylint: disable=too-many-arguments,E1101 -"""Random Walk with Drift Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import numpy as np -import pandas as pd -from statsforecast.core import StatsForecast -from statsforecast.models import RandomWalkWithDrift - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -warnings.simplefilter("ignore") - -logger = logging.getLogger(__name__) - -# pylint: disable=E1123 - - -@log_start_end(log=logger) -def get_rwd_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, -) -> Tuple[ - List[np.ndarray], List[np.ndarray], List[np.ndarray], Optional[float], StatsForecast -]: - """Performs Random Walk with Drift forecasting - This is a wrapper around StatsForecast RandomWalkWithDrift; - we refer to this link for the original and more complete documentation of the parameters. - - - https://nixtla.github.io/statsforecast/models.html#randomwalkwithdrift - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - - Returns - ------- - Tuple[List[np.ndarray], List[np.ndarray], List[np.ndarray], Optional[float], StatsForecast]: - Adjusted Data series, - List of historical fcast values, - List of predicted fcast values, - Optional[float] precision, - Fit RWD model object. - """ - - use_scalers = False - # statsforecast preprocessing - # when including more time series - # the preprocessing is similar - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - freq = ticker_series.freq_str - ticker_series = ticker_series.pd_dataframe().reset_index() - ticker_series.columns = ["ds", "y"] - ticker_series.insert(0, "unique_id", target_column) - - try: - # Model Init - model = RandomWalkWithDrift() - fcst = StatsForecast(df=ticker_series, models=[model], freq=freq, verbose=True) - except Exception as e: # noqa - error = str(e) - if "got an unexpected keyword argument" in error: - console.print( - "[red]Please update statsforecast to version 1.1.3 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None - - # Historical backtesting - last_training_point = int((len(ticker_series) - 1) * start_window) - historical_fcast = fcst.cross_validation( - h=int(forecast_horizon), - test_size=len(ticker_series) - last_training_point, - n_windows=None, - input_size=min(10 * forecast_horizon, len(ticker_series)), - ) - - # train new model on entire timeseries to provide best current forecast - # we have the historical fcast, now lets predict. - forecast = fcst.forecast(int(n_predict)) - y_true = historical_fcast["y"].values - y_hat = historical_fcast["RWD"].values - precision = helpers.mean_absolute_percentage_error(y_true, y_hat) - console.print(f"RWD obtains MAPE: {precision:.2f}% \n") - - # transform outputs to make them compatible with - # plots - use_scalers = False - _, ticker_series = helpers.get_series( - ticker_series.rename(columns={"y": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, forecast = helpers.get_series( - forecast.rename(columns={"RWD": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, historical_fcast = helpers.get_series( - historical_fcast.groupby("ds").head(1).rename(columns={"RWD": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - - return ( - ticker_series, - historical_fcast, - forecast, - float(precision), - fcst, - ) diff --git a/openbb_terminal/forecast/rwd_view.py b/openbb_terminal/forecast/rwd_view.py deleted file mode 100644 index bc77cc3aaaa..00000000000 --- a/openbb_terminal/forecast/rwd_view.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Random Walk with Drift View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, rwd_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_rwd_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - external_axes: bool = False, -): - """Display Random Walk with Drift Model - - Parameters - ---------- - data : Union[pd.Series, np.array] - Data to forecast - dataset_name: str - The name of the ticker to be predicted - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = rwd_model.get_rwd_data( - data=data, - target_column=target_column, - n_predict=n_predict, - start_window=start_window, - forecast_horizon=forecast_horizon, - ) - - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="RWD", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, None, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/seasonalnaive_model.py b/openbb_terminal/forecast/seasonalnaive_model.py deleted file mode 100644 index cece12ef714..00000000000 --- a/openbb_terminal/forecast/seasonalnaive_model.py +++ /dev/null @@ -1,146 +0,0 @@ -# pylint: disable=too-many-arguments,E1101 -"""Seasonal Naive Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from statsforecast.core import StatsForecast -from statsforecast.models import SeasonalNaive - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -warnings.simplefilter("ignore") - -logger = logging.getLogger(__name__) - -# pylint: disable=E1123 - - -@log_start_end(log=logger) -def get_seasonalnaive_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, -) -> Tuple[ - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[List[type[TimeSeries]]], - Optional[float], - Optional[type[StatsForecast]], -]: - """Performs Seasonal Naive forecasting - This is a wrapper around StatsForecast Seasonal Naive; - we refer to this link for the original and more complete documentation of the parameters. - - - https://nixtla.github.io/statsforecast/models.html#seasonalnaive - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year (7 for daily data) - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], StatsForecast] - Adjusted Data series, - List of historical fcast values, - List of predicted fcast values, - Optional[float] - precision, - Fit SeasonalNaive model object. - """ - - use_scalers = False - # statsforecast preprocessing - # when including more time series - # the preprocessing is similar - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - freq = ticker_series.freq_str - ticker_series = ticker_series.pd_dataframe().reset_index() - ticker_series.columns = ["ds", "y"] - ticker_series.insert(0, "unique_id", target_column) - - try: - # Model Init - model = SeasonalNaive( - season_length=int(seasonal_periods), - ) - fcst = StatsForecast(df=ticker_series, models=[model], freq=freq, verbose=True) - except Exception as e: # noqa - error = str(e) - if "got an unexpected keyword argument" in error: - console.print( - "[red]Please update statsforecast to version 1.1.3 or higher.[/red]" - ) - else: - console.print(f"[red]{error}[/red]") - return [], [], [], None, None - - # Historical backtesting - last_training_point = int((len(ticker_series) - 1) * start_window) - historical_fcast = fcst.cross_validation( - h=int(forecast_horizon), - test_size=len(ticker_series) - last_training_point, - n_windows=None, - input_size=min(10 * forecast_horizon, len(ticker_series)), - ) - - # train new model on entire timeseries to provide best current forecast - # we have the historical fcast, now lets predict. - forecast = fcst.forecast(int(n_predict)) - y_true = historical_fcast["y"].values - y_hat = historical_fcast["SeasonalNaive"].values - precision = helpers.mean_absolute_percentage_error(y_true, y_hat) - console.print(f"SeasonalNaive obtains MAPE: {precision:.2f}% \n") - - # transform outputs to make them compatible with - # plots - use_scalers = False - _, ticker_series = helpers.get_series( - ticker_series.rename(columns={"y": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, forecast = helpers.get_series( - forecast.rename(columns={"SeasonalNaive": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - _, historical_fcast = helpers.get_series( - historical_fcast.groupby("ds") - .head(1) - .rename(columns={"SeasonalNaive": target_column}), - target_column, - is_scaler=use_scalers, - time_col="ds", - ) - - return ( - ticker_series, - historical_fcast, - forecast, - float(precision), - fcst, - ) diff --git a/openbb_terminal/forecast/seasonalnaive_view.py b/openbb_terminal/forecast/seasonalnaive_view.py deleted file mode 100644 index 03af5dceb46..00000000000 --- a/openbb_terminal/forecast/seasonalnaive_view.py +++ /dev/null @@ -1,122 +0,0 @@ -"""SeasonalNaive View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, seasonalnaive_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_seasonalnaive_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - external_axes: bool = False, -): - """Display SeasonalNaive Model - - Parameters - ---------- - data : Union[pd.Series, np.array] - Data to forecast - dataset_name: str - The name of the ticker to be predicted - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = seasonalnaive_model.get_seasonalnaive_data( - data=data, - target_column=target_column, - seasonal_periods=seasonal_periods, - n_predict=n_predict, - start_window=start_window, - forecast_horizon=forecast_horizon, - ) - - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="SeasonalNaive", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, None, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/tcn_model.py b/openbb_terminal/forecast/tcn_model.py deleted file mode 100644 index 5f47f0d6ff9..00000000000 --- a/openbb_terminal/forecast/tcn_model.py +++ /dev/null @@ -1,179 +0,0 @@ -# pylint: disable=too-many-arguments -"""TCN Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models import TCNModel - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_tcn_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - dropout: float = 0.1, - num_filters: int = 3, - weight_norm: bool = True, - dilation_base: int = 2, - n_epochs: int = 300, - learning_rate: float = 1e-3, - batch_size: int = 32, - model_save_name: str = "tcn_model", - force_reset: bool = True, - save_checkpoints: bool = True, - metric: str = "mape", -) -> Tuple[ - List[Optional[TimeSeries]], - List[Optional[TimeSeries]], - List[Optional[TimeSeries]], - Optional[float], - Optional[type[TCNModel]], -]: - """Perform TCN forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - dropout: float - Fraction of neurons affected by Dropout. Defaults to 0.1. - num_filters: int - The number of filters in a convolutional layer of the TCN. Defaults to 6. - weight_norm: bool - Boolean value indicating whether to use weight normalization. Defaults to True. - dilation_base: int - The base of the exponent that will determine the dilation on every level. Defaults to 2. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - metric: str - Metric to use for model selection. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], type[TCNModel]] - Adjusted Data series, - Historical forecast by best RNN model, - list of Predictions, - Mean average precision error, - Best TCN Model. - """ - # TODO Check if torch GPU AVAILABLE - - use_scalers = True - probabilistic = False - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - train, val = ticker_series.split_before(train_split) - valid = helpers.check_data_length( - train, val, input_chunk_length, output_chunk_length - ) - if not valid: - return [], [], [], None, None - - ( - past_covariate_whole, - past_covariate_train, - past_covariate_val, - ) = helpers.past_covs(past_covariates, data, train_split, use_scalers) - - current_user = get_current_user() - - tcn_model = TCNModel( - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - dropout=dropout, - dilation_base=dilation_base, - weight_norm=weight_norm, - num_filters=num_filters, - n_epochs=n_epochs, - nr_epochs_val_period=1, - batch_size=batch_size, - optimizer_kwargs={"lr": learning_rate}, - model_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - random_state=42, - pl_trainer_kwargs=helpers.get_pl_kwargs(accelerator="cpu"), - log_tensorboard=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - # fit model on train series for historical forecasting - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - helpers.fit_model( - tcn_model, - train, - val, - past_covariate_train, - past_covariate_val, - ) - best_model = TCNModel.load_from_checkpoint( - model_name=model_save_name, - best=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - helpers.print_tensorboard_logs( - model_save_name, str(current_user.preferences.USER_FORECAST_MODELS_DIRECTORY) - ) - - # Showing historical backtesting without retraining model (too slow) - return helpers.get_prediction( - "TCN", - probabilistic, - use_scalers, - scaler, - past_covariates, - best_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/tcn_view.py b/openbb_terminal/forecast/tcn_view.py deleted file mode 100644 index 62e31ea8b28..00000000000 --- a/openbb_terminal/forecast/tcn_view.py +++ /dev/null @@ -1,181 +0,0 @@ -"""TCN View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, tcn_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_tcn_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - dropout: float = 0.1, - num_filters: int = 3, - weight_norm: bool = True, - dilation_base: int = 2, - n_epochs: int = 300, - learning_rate: float = 1e-3, - batch_size: int = 32, - model_save_name: str = "tcn_model", - force_reset: bool = True, - save_checkpoints: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display TCN forecast - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - target_column: str - Target column to forecast. Defaults to "close". - dataset_name: str - The name of the ticker to be predicted - n_predict: int - Days to predict. Defaults to 5. - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - dropout: float - Fraction of neurons affected by Dropout. Defaults to 0.1. - num_filters: int - The number of filters in a convolutional layer of the TCN. Defaults to 6. - weight_norm: bool - Boolean value indicating whether to use weight normalization. Defaults to True. - dilation_base: int - The base of the exponent that will determine the dilation on every level. Defaults to 2. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. Defaults to True. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - export_pred_raw: bool - Whether to export the raw predicted values. Defaults to False. - metric: str - The metric to use for the model. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - data = helpers.clean_data( - data, start_date, end_date, target_column, past_covariates - ) - if not helpers.check_data(data, target_column, past_covariates): - return None - output_chunk_length = helpers.check_output( - output_chunk_length, n_predict, bool(past_covariates) - ) - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = tcn_model.get_tcn_data( - data=data, - n_predict=n_predict, - target_column=target_column, - past_covariates=past_covariates, - train_split=train_split, - forecast_horizon=forecast_horizon, - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - dropout=dropout, - num_filters=num_filters, - weight_norm=weight_norm, - dilation_base=dilation_base, - batch_size=batch_size, - n_epochs=n_epochs, - learning_rate=learning_rate, - model_save_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - metric=metric, - ) - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="TCN", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - metric=metric, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/tft_model.py b/openbb_terminal/forecast/tft_model.py deleted file mode 100644 index ea248c99059..00000000000 --- a/openbb_terminal/forecast/tft_model.py +++ /dev/null @@ -1,217 +0,0 @@ -# pylint: disable=too-many-arguments -"""Temporal Fusion Transformer Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models import TFTModel -from darts.utils.likelihood_models import QuantileRegression -from statsmodels.tools.sm_exceptions import ConvergenceWarning - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -warnings.simplefilter("ignore", ConvergenceWarning) - - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_tft_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - hidden_size: int = 16, - lstm_layers: int = 1, - num_attention_heads: int = 4, - full_attention: bool = False, - dropout: float = 0.1, - hidden_continuous_size: int = 8, - n_epochs: int = 200, - batch_size: int = 32, - model_save_name: str = "tft_model", - force_reset: bool = True, - save_checkpoints: bool = True, - metric: str = "mape", -) -> Tuple[ - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[float], - Optional[type[TFTModel]], -]: - """Performs Temporal Fusion Transformer forecasting - The TFT applies multi-head attention queries on future inputs from mandatory future_covariates. - Specifying future encoders with add_encoders (read below) can automatically generate future - covariates and allows to use the model without having to pass any future_covariates to fit() - and predict(). - - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.tft_model.html - - Parameters - ---------- - data (Union[pd.Series, pd.DataFrame]): - Input Data - target_column: Optional[str] - Target column to forecast. Defaults to "close". - n_predict: (int, optional) - Days to predict. Defaults to 5. - train_split: (float, optional) - Train/val split. Defaults to 0.85. - past_covariates: (str, optional) - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: (int, optional) - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: (int, optional) - Number of past time steps that are fed to the forecasting module at prediction time. - Defaults to 14. - output_chunk_length: (int, optional) - The length of the forecast of the model. Defaults to 5. - hidden_size: (int, optional) - Hidden state size of the TFT. Defaults to 16. - lstm_layers: (int, optional) - Number of layers for the Long Short Term Memory Encoder and Decoder. Defaults to 16. - num_attention_headers: (int, optional) - Number of attention heads. Defaults to 4. - full_attention: (bool, optional) - Whether to apply a multi-head attention query. Defaults to False> - dropout: (float, optional) - Fraction of neurons affected by dropout. Defaults to 0.1. - hidden_continuous_size: (int, optional) - Default hidden size for processing continuous variables. Defaults to 8. - n_epochs: (int, optional) - Number of epochs to run during training. Defaults to 200. - batch_size: (int, optional) - Number of samples to pass through network during a single epoch. Defaults to 32. - model_save_name: (str, optional) - The name for the model. Defaults to tft_model - force_reset: (bool, optional) - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: (bool, optional) - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - metric: (str, optional) - Metric to use for model selection. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], type[TFTModel]]: - Adjusted Data series, - List of historical fcast values, - List of predicted fcast values, - Optional[float] - precision, - Fit Prob. TFT model object. - """ - - use_scalers = True - probabilistic = True - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - train, val = ticker_series.split_before(train_split) - valid = helpers.check_data_length( - train, val, input_chunk_length, output_chunk_length - ) - if not valid: - return [], [], [], None, None - - ( - past_covariate_whole, - past_covariate_train, - past_covariate_val, - ) = helpers.past_covs(past_covariates, data, train_split, use_scalers) - - quantiles = [ - 0.01, - 0.05, - 0.1, - 0.15, - 0.2, - 0.25, - 0.3, - 0.4, - 0.5, - 0.6, - 0.7, - 0.75, - 0.8, - 0.85, - 0.9, - 0.95, - 0.99, - ] - - current_user = get_current_user() - - tft_model = TFTModel( - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - hidden_size=hidden_size, - lstm_layers=lstm_layers, - num_attention_heads=num_attention_heads, - full_attention=full_attention, - dropout=dropout, - hidden_continuous_size=hidden_continuous_size, - model_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - random_state=42, - n_epochs=n_epochs, - batch_size=batch_size, - pl_trainer_kwargs=helpers.get_pl_kwargs(accelerator="cpu"), - likelihood=QuantileRegression( - quantiles=quantiles - ), # QuantileRegression is set per default - add_relative_index=True, # TODO There is a bug with this. Must fix. Should be false - log_tensorboard=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - # fit model on train series for historical forecasting - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - helpers.fit_model( - tft_model, - train, - val, - past_covariate_train, - past_covariate_val, - ) - best_model = TFTModel.load_from_checkpoint( - model_name=model_save_name, - best=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - helpers.print_tensorboard_logs( - model_save_name, str(current_user.preferences.USER_FORECAST_MODELS_DIRECTORY) - ) - - # Showing historical backtesting without retraining model (too slow) - return helpers.get_prediction( - "TFT", - probabilistic, - use_scalers, - scaler, - past_covariates, - best_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/tft_view.py b/openbb_terminal/forecast/tft_view.py deleted file mode 100644 index e80f69daf6b..00000000000 --- a/openbb_terminal/forecast/tft_view.py +++ /dev/null @@ -1,181 +0,0 @@ -"""Temporal Fusion Transformer View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, tft_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_tft_forecast( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - hidden_size: int = 16, - lstm_layers: int = 1, - num_attention_heads: int = 4, - full_attention: bool = False, - dropout: float = 0.1, - hidden_continuous_size: int = 8, - n_epochs: int = 200, - batch_size: int = 32, - model_save_name: str = "tft_model", - force_reset: bool = True, - save_checkpoints: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display Temporal Fusion Transformer forecast - - Parameters - ---------- - data (Union[pd.Series, pd.DataFrame]): - Input Data - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - dataset_name str - The name of the ticker to be predicted - n_predict (int, optional): - Days to predict. Defaults to 5. - train_split (float, optional): - Train/val split. Defaults to 0.85. - past_covariates (str, optional): - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon (int, optional): - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length (int, optional): - Number of past time steps that are fed to the forecasting module at prediction time. - Defaults to 14. - output_chunk_length (int, optional): - The length of the forecast of the model. Defaults to 5. - hidden_size (int, optional): - Hidden state size of the TFT. Defaults to 16. - lstm_layers (int, optional): - Number of layers for the Long Short Term Memory Encoder and Decoder. Defaults to 16. - num_attention_headers (int, optional): - Number of attention heads. Defaults to 4. - full_attention (bool, optional): - Whether to apply a multi-head attention query. Defaults to False> - dropout (float, optional): - Fraction of neurons affected by dropout. Defaults to 0.1. - hidden_continuous_size (int, optional): - Default hidden size for processing continuous variables. Defaults to 8. - n_epochs (int, optional): - Number of epochs to run during training. Defaults to 200. - batch_size (int, optional): - Number of samples to pass through network during a single epoch. Defaults to 32. - model_save_name (str, optional): - The name for the model. Defaults to tft_model - force_reset (bool, optional): - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints (bool, optional): - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - metric: str - The metric to use for the model. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - data = helpers.clean_data( - data, start_date, end_date, target_column, past_covariates - ) - if not helpers.check_data(data, target_column, past_covariates): - return None - output_chunk_length = helpers.check_output( - output_chunk_length, n_predict, bool(past_covariates) - ) - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = tft_model.get_tft_data( - data=data, - n_predict=n_predict, - target_column=target_column, - past_covariates=past_covariates, - train_split=train_split, - forecast_horizon=forecast_horizon, - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - hidden_size=hidden_size, - lstm_layers=lstm_layers, - num_attention_heads=num_attention_heads, - full_attention=full_attention, - dropout=dropout, - hidden_continuous_size=hidden_continuous_size, - n_epochs=n_epochs, - batch_size=batch_size, - model_save_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - metric=metric, - ) - if ticker_series == []: - return None - - probabilistic = True - fig = helpers.plot_forecast( - name="TFT", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - metric=metric, - external_axes=external_axes, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/theta_model.py b/openbb_terminal/forecast/theta_model.py deleted file mode 100644 index c21a84f0c1d..00000000000 --- a/openbb_terminal/forecast/theta_model.py +++ /dev/null @@ -1,167 +0,0 @@ -"""Theta Model""" - -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import numpy as np -import pandas as pd -from darts import TimeSeries -from darts.metrics import mape, mse, rmse, smape -from darts.models import Theta -from darts.utils.utils import SeasonalityMode -from statsmodels.tools.sm_exceptions import ConvergenceWarning - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers -from openbb_terminal.rich_config import console - -warnings.simplefilter("ignore", ConvergenceWarning) - -SEASONS = ["N", "A", "M"] -PERIODS = [4, 5, 7] - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_theta_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - seasonal: str = "M", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - metric: str = "mape", -) -> Tuple[ - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[float], - Optional[float], - Optional[type[Theta]], -]: - """Performs Theta forecasting - An implementation of the 4Theta method with configurable theta parameter. - - https://unit8co.github.io/darts/generated_api/darts.models.forecasting.theta.html - - Parameters - ---------- - data : Union[pd.Series, np.ndarray] - Input data. - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - seasonal: str - Seasonal component. One of [N, A, M] - Defaults to MULTIPLICATIVE. - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical data - metric: str - Metric to use when backtesting and retraining historical data. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], float, float, type[Theta]] - Adjusted Data series, - Historical forecast by best theta, - list of Predictions, - Mean average precision error, - Best Theta, - Theta Model. - """ - - use_scalers = False - _, ticker_series = helpers.get_series(data, target_column, is_scaler=use_scalers) - train, val = ticker_series.split_before(start_window) - - if seasonal == "A": - seasonal = SeasonalityMode.ADDITIVE - elif seasonal == "N": - seasonal = SeasonalityMode.NONE - else: # Default - seasonal = SeasonalityMode.MULTIPLICATIVE - - thetas = np.linspace(-10, 10, 50) - best_mape = float("inf") - best_theta = 0 - error = "" - for theta in thetas: - model = Theta( - theta=theta, - season_mode=seasonal, - seasonality_period=seasonal_periods, - ) - try: - model.fit(train) - pred_theta = model.predict(len(val)) - res = mape(val, pred_theta) - if res < best_mape: - best_mape = res - best_theta = theta - except Exception as e: # noqa - error = str(e) - - if best_theta == 0: - console.print(f"[red]{error}[/red]") - return [], [], [], 0, 0, None - - best_theta_model = Theta( - best_theta, - season_mode=seasonal, - seasonality_period=seasonal_periods, - ) - - # Training model based on historical backtesting - historical_fcast_theta = best_theta_model.historical_forecasts( - ticker_series, - start=float(start_window), - forecast_horizon=int(forecast_horizon), - verbose=True, - ) - - best_theta_model_final = Theta( - best_theta, - season_mode=seasonal, - seasonality_period=seasonal_periods, - ) - - # fit model on entire series for final prediction - best_theta_model_final.fit(ticker_series) - prediction = best_theta_model_final.predict(int(n_predict)) - - if metric == "rmse": - precision = rmse( - actual_series=ticker_series, pred_series=historical_fcast_theta - ) - elif metric == "mse": - precision = mse(actual_series=ticker_series, pred_series=historical_fcast_theta) - elif metric == "mape": - precision = mape( - actual_series=ticker_series, pred_series=historical_fcast_theta - ) - elif metric == "smape": - precision = smape( - actual_series=ticker_series, pred_series=historical_fcast_theta - ) - - console.print(f"Theta Model obtains {metric.upper()}: {precision:.2f}% \n") - - return ( - ticker_series, - historical_fcast_theta, - prediction, - precision, - best_theta, - best_theta_model, - ) diff --git a/openbb_terminal/forecast/theta_view.py b/openbb_terminal/forecast/theta_view.py deleted file mode 100644 index fd03485ffe4..00000000000 --- a/openbb_terminal/forecast/theta_view.py +++ /dev/null @@ -1,137 +0,0 @@ -"""Theta View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, theta_model -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_theta_forecast( - data: Union[pd.DataFrame, pd.Series], - target_column: str = "close", - dataset_name: str = "", - seasonal: str = "M", - seasonal_periods: int = 7, - n_predict: int = 5, - start_window: float = 0.85, - forecast_horizon: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display Theta forecast - - Parameters - ---------- - data : Union[pd.Series, np.array] - Data to forecast - target_column: Optional[str]: - Target column to forecast. Defaults to "close". - dataset_name: str - The name of the ticker to be predicted - seasonal: str - Seasonal component. One of [N, A, M] - Defaults to MULTIPLICATIVE. - seasonal_periods: int - Number of seasonal periods in a year - If not set, inferred from frequency of the series. - n_predict: int - Number of days to forecast - start_window: float - Size of sliding window from start of timeseries and onwards - forecast_horizon: int - Number of days to forecast when backtesting and retraining historical - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - export_pred_raw: bool - Whether to export the raw predicted values. Defaults to False. - metric: str - The metric to use for backtesting. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - data = helpers.clean_data(data, start_date, end_date, target_column, None) - if not helpers.check_data(data, target_column, None): - return None - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - best_theta, - _model, - ) = theta_model.get_theta_data( - data=data, - seasonal=seasonal, - seasonal_periods=seasonal_periods, - n_predict=n_predict, - target_column=target_column, - start_window=start_window, - forecast_horizon=forecast_horizon, - metric=metric, - ) - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name=f"THETA_{best_theta:.2f}", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=None, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - metric=metric, - ) - - if residuals: - # TODO: Figure out why residuals do not work with Theta - console.print( - "[red]Theta residual is currently not supported. Please stay tuned![/red]" - ) - - return fig diff --git a/openbb_terminal/forecast/timegpt_model.py b/openbb_terminal/forecast/timegpt_model.py deleted file mode 100644 index 1cc3a9724c9..00000000000 --- a/openbb_terminal/forecast/timegpt_model.py +++ /dev/null @@ -1,91 +0,0 @@ -# pylint: disable=too-many-arguments -"""Probabilistic Exponential Smoothing Model""" -__docformat__ = "numpy" - -import logging -from typing import List, Optional, Union - -import numpy -import pandas as pd -from nixtlats import TimeGPT - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_NIXTLA"]) -def get_timegpt_model( - data: Union[pd.Series, pd.DataFrame], - time_col: str = "ds", - target_col: str = "y", - forecast_horizon: int = 12, - levels: Optional[List[float]] = None, - freq: Union[str, None] = None, - finetune_steps: int = 0, - clean_ex_first: bool = True, - residuals: bool = False, - date_features: Optional[List[str]] = None, -) -> pd.DataFrame: - """TimeGPT was trained on the largest collection of data in history - - over 100 billion rows of financial, weather, energy, and web data - - and democratizes the power of time-series analysis. - - Parameters - ---------- - data : Union[pd.Series, pd.DataFrame] - Input data. - time_col: str: - Column that identifies each timestep, its values can be timestamps or integers. Defaults to "ds". - target_column: str: - Target column to forecast. Defaults to "y". - forecast_horizon: int - Number of days to forecast. Defaults to 12. - levels: List[float] - Confidence levels between 0 and 100 for prediction intervals. - freq: Optional[str, None] - Frequency of the data. By default, the freq will be inferred automatically. - finetune_steps: int - Number of steps used to finetune TimeGPT in the new data. - clean_ex_first: bool - Clean exogenous signal before making forecasts using TimeGPT. - residuals: bool - Whether to show residuals for the model. Defaults to False. - date_features: Optional[List[str]] - Specifies which date attributes have highest weight according to model. - - Returns - ------- - pd.DataFrame - Forecasted values. - """ - timegpt = TimeGPT( - token=get_current_user().credentials.API_KEY_NIXTLA, - ) - - if levels is None: - levels = [80, 95] - - if isinstance(data[time_col].values[0], pd.Timestamp): - data[time_col] = data[time_col].dt.strftime("%Y-%m-%d %H:%M:%S") - elif isinstance(data[time_col].values[0], numpy.datetime64): - data[time_col] = pd.to_datetime(data[time_col]).dt.strftime("%Y-%m-%d %H:%M:%S") - - date_features_param = True if "auto" in date_features else date_features # type: ignore - - fcst_df = timegpt.forecast( - data, - time_col=time_col, - target_col=target_col, - h=forecast_horizon, - freq=freq, - level=levels, - finetune_steps=finetune_steps, - clean_ex_first=clean_ex_first, - add_history=residuals, - date_features=date_features_param if date_features else False, - ) - - return fcst_df, timegpt.weights_x diff --git a/openbb_terminal/forecast/timegpt_view.py b/openbb_terminal/forecast/timegpt_view.py deleted file mode 100644 index b5ee6f99f4f..00000000000 --- a/openbb_terminal/forecast/timegpt_view.py +++ /dev/null @@ -1,245 +0,0 @@ -"""Probabilistic Exponential Smoothing View""" - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import List, Optional, Union - -import pandas as pd -import plotly.graph_objs as go - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.forecast import helpers, timegpt_model -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_NIXTLA"]) -def display_timegpt_forecast( - data: Union[pd.DataFrame, pd.Series], - dataset_name: str = "", - time_col: str = "ds", - target_col: str = "y", - forecast_horizon: int = 12, - levels: Optional[List[float]] = None, - freq: Union[str, None] = None, - finetune_steps: int = 0, - clean_ex_first: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - residuals: bool = False, - date_features: Optional[List[str]] = None, -) -> Union[OpenBBFigure, None]: - """TimeGPT was trained on the largest collection of data in history - - over 100 billion rows of financial, weather, energy, and web data - - and democratizes the power of time-series analysis. - - Parameters - ---------- - data : Union[pd.Series, pd.DataFrame] - Input data. - dataset_name : str - Dataset name - time_col: str: - Column that identifies each timestep, its values can be timestamps or integers. Defaults to "ds". - target_column: str: - Target column to forecast. Defaults to "y". - forecast_horizon: int - Number of days to forecast. Defaults to 12. - levels: List[float] - Confidence levels between 0 and 100 for prediction intervals. - freq: Optional[str, None] - Frequency of the data. By default, the freq will be inferred automatically. - finetune_steps: int - Number of steps used to finetune TimeGPT in the new data. - clean_ex_first: bool - Clean exogenous signal before making forecasts using TimeGPT. - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - external_axes: Optional[List[plt.axes]] - External axes to plot on - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - residuals: bool - Whether to show residuals for the model. Defaults to False. - date_features: Optional[List[str]] - Specifies which date attributes have highest weight according to model. - - Returns - ------- - pd.DataFrame - Forecasted values. - """ - if levels is None: - levels = [80, 95] - - if time_col not in data.columns: - if time_col == "ds": # means that the user has not set it yet, bc default - console.print( - f"[red]Set the time column for '{dataset_name}' between: {', '.join(data.columns)}[/red]" - ) - else: - console.print(f"[red]Time column '{time_col}' not found in data[/red]") - return None - - if target_col not in data.columns: - if target_col == "y": # means that the user has not set it yet, bc default - console.print( - f"[red]Set the target column for '{dataset_name}' between: {', '.join(data.columns)}[/red]" - ) - else: - console.print(f"[red]Target column '{target_col}' not found in data[/red]") - return None - - data = helpers.clean_data(data, start_date, end_date, target_col, None) - if not helpers.check_data(data, target_col, None): - return None - - df, datefeatures_df = timegpt_model.get_timegpt_model( - data=data, - time_col=time_col, - target_col=target_col, - forecast_horizon=forecast_horizon, - levels=levels, - freq=freq, - finetune_steps=finetune_steps, - clean_ex_first=clean_ex_first, - residuals=residuals, - date_features=date_features, - ) - - fig = OpenBBFigure(xaxis_title="Date") - - fig.set_title(f"TimeGPT-1 (Beta) on {target_col} with horizon {forecast_horizon}") - - if residuals: - xds = list(pd.to_datetime(df[time_col].values))[:-forecast_horizon] - xds_reverse = list(pd.to_datetime(df[time_col].values))[:-forecast_horizon] - xds_reverse.reverse() - xds += xds_reverse - - xds_forecast = list(pd.to_datetime(df[time_col].values))[-forecast_horizon:] - xds_forecast_reverse = list(pd.to_datetime(df[time_col].values))[-forecast_horizon:] - xds_forecast_reverse.reverse() - xds_forecast += xds_forecast_reverse - - # this is done so the confidence levels are displayed correctly - levels.sort() - for count, lvl in enumerate(levels): - lvl_name = ( - str(lvl) - if isinstance(lvl, int) - else str(int(lvl) if lvl.is_integer() else lvl) - ) - - if residuals: - ylo = list(df[f"TimeGPT-lo-{lvl_name}"].values)[:-forecast_horizon] - yhigh = list(df[f"TimeGPT-hi-{lvl_name}"].values)[:-forecast_horizon] - yhigh.reverse() - ylo += yhigh - - fig.add_traces( - [ - go.Scatter( - x=xds, - y=ylo, - mode="lines", - line_color=f"rgba(255,127,14,{.2+(len(levels)-count)*(.6/(len(levels)+1))})", - name=f"{lvl_name}% confidence interval historical", - fill="toself", - fillcolor=f"rgba(255,127,14,{.2+(len(levels)-count)*(.6/(len(levels)+1))})", - ) - ] - ) - - ylo_forecast = list(df[f"TimeGPT-lo-{lvl_name}"].values)[-forecast_horizon:] - yhigh_forecast = list(df[f"TimeGPT-hi-{lvl_name}"].values)[-forecast_horizon:] - yhigh_forecast.reverse() - ylo_forecast += yhigh_forecast - - fig.add_traces( - [ - go.Scatter( - x=xds_forecast, - y=ylo_forecast, - mode="lines", - line_color=f"rgba(0,172,255,{.2+(len(levels)-count)*(.6/(len(levels)+1))})", - name=f"{lvl_name}% confidence interval", - fill="toself", - fillcolor=f"rgba(0,172,255,{.2+(len(levels)-count)*(.6/(len(levels)+1))})", - ) - ] - ) - - if residuals: - # TimeGPT prediction - historical - fig.add_scatter( - x=list(pd.to_datetime(df[time_col].values))[:-forecast_horizon], - y=list(df["TimeGPT"].values)[:-forecast_horizon], - name="TimeGPT historical forecast", - mode="lines", - line=dict( - color="rgba(255,127,14,1)", - width=3, - ), - ) - - # TimeGPT prediction - fig.add_scatter( - x=list(pd.to_datetime(df[time_col].values))[-forecast_horizon:], - y=list(df["TimeGPT"].values)[-forecast_horizon:], - name="TimeGPT forecast", - mode="markers+lines", - line=dict( - color="rgba(0,172,255,1)", - width=3, - ), - ) - - # Current data - fig.add_scatter( - x=list(pd.to_datetime(data[time_col].values)), - y=list(data[target_col].values), - name="Actual", - line_color="gold", - mode="lines", - ) - fig.show(external=external_axes) - - if date_features: - fig2 = OpenBBFigure(xaxis_title="Weights") - - fig2.set_title("Date features weight") - - fig2.add_bar( - y=datefeatures_df["features"], - x=datefeatures_df["weights"], - marker_color=theme.up_color, - orientation="h", - ) - fig2.show(external=external_axes) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"timegpt_forecast_{target_col}", - df=df, - sheet_name=sheet_name, - figure=fig, - ) - - return fig diff --git a/openbb_terminal/forecast/trans_model.py b/openbb_terminal/forecast/trans_model.py deleted file mode 100644 index 714e90f89a1..00000000000 --- a/openbb_terminal/forecast/trans_model.py +++ /dev/null @@ -1,189 +0,0 @@ -# pylint: disable=too-many-arguments -"""Transformer Model""" -__docformat__ = "numpy" - -import logging -import warnings -from typing import List, Optional, Tuple, Union - -import pandas as pd -from darts import TimeSeries -from darts.models import TransformerModel - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_trans_data( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - n_predict: int = 5, - train_split: float = 0.85, - past_covariates: Optional[str] = None, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - d_model: int = 64, - nhead: int = 4, - num_encoder_layers: int = 3, - num_decoder_layers: int = 3, - dim_feedforward: int = 512, - activation: str = "relu", - dropout: float = 0.0, - batch_size: int = 32, - n_epochs: int = 300, - learning_rate: float = 1e-3, - model_save_name: str = "trans_model", - force_reset: bool = True, - save_checkpoints: bool = True, - metric: str = "mape", -) -> Tuple[ - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[List[TimeSeries]], - Optional[float], - Optional[type[TransformerModel]], -]: - """Performs Transformer forecasting - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - n_predict: int - Days to predict. Defaults to 5. - target_column: str - Target column to forecast. Defaults to "close". - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - d_model: int - The number of expected features in the encoder/decoder inputs. Defaults to 64. - nhead: int - The number of heads in the multi-head attention mechanism. Defaults to 4. - num_encoder_layers: int - The number of encoder layers in the encoder. Defaults to 3. - num_decoder_layers: int - The number of decoder layers in the encoder. Defaults to 3. - dim_feedforward: int - The dimension of the feedforward network model. Defaults to 512. - activation: str - The activation function of encoder/decoder intermediate layer, ‘relu’ or ‘gelu’. Defaults to 'relu'. - dropout: float - Fraction of neurons affected by Dropout. Defaults to 0.0. - batch_size: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - learning_rate: float - Defaults to 1e-3. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset (all checkpoints will be - discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. Defaults to True. - metric: str - Metric to use for model selection. Defaults to "mape". - - Returns - ------- - Tuple[List[TimeSeries], List[TimeSeries], List[TimeSeries], Optional[float], type[TransformerModel]] - Adjusted Data series, - Historical forecast by best RNN model, - list of Predictions, - Mean average precision error, - Best transformer Model. - """ - # TODO Check if torch GPU AVAILABLE - - use_scalers = True - probabilistic = False - - scaler, ticker_series = helpers.get_series( - data, target_column, is_scaler=use_scalers - ) - train, val = ticker_series.split_before(train_split) - valid = helpers.check_data_length( - train, val, input_chunk_length, output_chunk_length - ) - if not valid: - return [], [], [], None, None - - ( - past_covariate_whole, - past_covariate_train, - past_covariate_val, - ) = helpers.past_covs(past_covariates, data, train_split, use_scalers) - - current_user = get_current_user() - - trans_model = TransformerModel( - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - d_model=d_model, - nhead=nhead, - num_encoder_layers=num_encoder_layers, - num_decoder_layers=num_decoder_layers, - dim_feedforward=dim_feedforward, - activation=activation, - dropout=dropout, - batch_size=batch_size, - n_epochs=n_epochs, - optimizer_kwargs={"lr": learning_rate}, - model_name=model_save_name, - random_state=42, - pl_trainer_kwargs=helpers.get_pl_kwargs(accelerator="cpu"), - force_reset=force_reset, - save_checkpoints=save_checkpoints, - log_tensorboard=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - # fit model on train series for historical forecasting - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - helpers.fit_model( - trans_model, - train, - val, - past_covariate_train, - past_covariate_val, - ) - best_model = TransformerModel.load_from_checkpoint( - model_name=model_save_name, - best=True, - work_dir=current_user.preferences.USER_FORECAST_MODELS_DIRECTORY, - ) - - helpers.print_tensorboard_logs( - model_save_name, str(current_user.preferences.USER_FORECAST_MODELS_DIRECTORY) - ) - - # Showing historical backtesting without retraining model (too slow) - return helpers.get_prediction( - "Transformer", - probabilistic, - use_scalers, - scaler, - past_covariates, - best_model, - ticker_series, - past_covariate_whole, - train_split, - forecast_horizon, - n_predict, - metric, - ) diff --git a/openbb_terminal/forecast/trans_view.py b/openbb_terminal/forecast/trans_view.py deleted file mode 100644 index 4378e936117..00000000000 --- a/openbb_terminal/forecast/trans_view.py +++ /dev/null @@ -1,192 +0,0 @@ -"""Transformer View""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast import helpers, trans_model - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_trans_forecast( - data: Union[pd.Series, pd.DataFrame], - target_column: str = "close", - dataset_name: str = "", - n_predict: int = 5, - past_covariates: Optional[str] = None, - train_split: float = 0.85, - forecast_horizon: int = 5, - input_chunk_length: int = 14, - output_chunk_length: int = 5, - d_model: int = 64, - nhead: int = 4, - num_encoder_layers: int = 3, - num_decoder_layers: int = 3, - dim_feedforward: int = 512, - activation: str = "relu", - dropout: float = 0.0, - batch_size: int = 32, - n_epochs: int = 300, - learning_rate: float = 1e-3, - model_save_name: str = "trans_model", - force_reset: bool = True, - save_checkpoints: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - residuals: bool = False, - forecast_only: bool = False, - start_date: Optional[datetime] = None, - end_date: Optional[datetime] = None, - naive: bool = False, - export_pred_raw: bool = False, - metric: str = "mape", - external_axes: bool = False, -): - """Display Transformer forecast - - Parameters - ---------- - data: Union[pd.Series, pd.DataFrame] - Input Data - dataset_name: str - The name of the ticker to be predicted - n_predict: int - Days to predict. Defaults to 5. - target_column: str - Target column to forecast. Defaults to "close". - train_split: float - Train/val split. Defaults to 0.85. - past_covariates: str - Multiple secondary columns to factor in when forecasting. Defaults to None. - forecast_horizon: int - Forecast horizon when performing historical forecasting. Defaults to 5. - input_chunk_length: int - Number of past time steps that are fed to the forecasting module at prediction time. Defaults to 14. - output_chunk_length: int - The length of the forecast of the model. Defaults to 5. - d_model: int - The number of expected features in the encoder/decoder inputs. Defaults to 64. - nhead: int - The number of heads in the multi-head attention mechanism. Defaults to 4. - num_encoder_layers: int - The number of encoder layers in the encoder. Defaults to 3. - num_decoder_layers: int - The number of decoder layers in the encoder. Defaults to 3. - dim_feedforward: int - The dimension of the feedforward network model. Defaults to 512. - activation: str - The activation function of encoder/decoder intermediate layer, ‘relu’ or ‘gelu’. Defaults to 'relu'. - dropout: float - Fraction of neurons affected by Dropout. Defaults to 0.1. - batch_size_: int - Number of time series (input and output sequences) used in each training pass. Defaults to 32. - n_epochs: int - Number of epochs over which to train the model. Defaults to 100. - model_save_name: str - Name for model. Defaults to "brnn_model". - force_reset: bool - If set to True, any previously-existing model with the same name will be reset - (all checkpoints will be discarded). Defaults to True. - save_checkpoints: bool - Whether or not to automatically save the untrained model and checkpoints from training. - Defaults to True. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - residuals: bool - Whether to show residuals for the model. Defaults to False. - forecast_only: bool - Whether to only show dates in the forecasting range. Defaults to False. - start_date: Optional[datetime] - The starting date to perform analysis, data before this is trimmed. Defaults to None. - end_date: Optional[datetime] - The ending date to perform analysis, data after this is trimmed. Defaults to None. - naive: bool - Whether to show the naive baseline. This just assumes the closing price will be the same - as the previous day's closing price. Defaults to False. - export_pred_raw: bool - Whether to export the raw predicted values. Defaults to False. - metric: str - The metric to use for the model. Defaults to "mape". - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - data = helpers.clean_data( - data, start_date, end_date, target_column, past_covariates - ) - if not helpers.check_data(data, target_column, past_covariates): - return None - output_chunk_length = helpers.check_output( - output_chunk_length, n_predict, bool(past_covariates) - ) - ( - ticker_series, - historical_fcast, - predicted_values, - precision, - _model, - ) = trans_model.get_trans_data( - data=data, - n_predict=n_predict, - target_column=target_column, - past_covariates=past_covariates, - train_split=train_split, - forecast_horizon=forecast_horizon, - input_chunk_length=input_chunk_length, - output_chunk_length=output_chunk_length, - d_model=d_model, - nhead=nhead, - num_encoder_layers=num_encoder_layers, - num_decoder_layers=num_decoder_layers, - dim_feedforward=dim_feedforward, - activation=activation, - dropout=dropout, - batch_size=batch_size, - n_epochs=n_epochs, - learning_rate=learning_rate, - model_save_name=model_save_name, - force_reset=force_reset, - save_checkpoints=save_checkpoints, - metric=metric, - ) - if ticker_series == []: - return None - - probabilistic = False - fig = helpers.plot_forecast( - name="TRANS", - target_col=target_column, - historical_fcast=historical_fcast, - predicted_values=predicted_values, - ticker_series=ticker_series, - ticker_name=dataset_name, - data=data, - n_predict=n_predict, - forecast_horizon=forecast_horizon, - past_covariates=past_covariates, - precision=precision, - probabilistic=probabilistic, - export=export, - sheet_name=sheet_name, - forecast_only=forecast_only, - naive=naive, - export_pred_raw=export_pred_raw, - external_axes=external_axes, - metric=metric, - ) - if residuals: - helpers.plot_residuals( - _model, past_covariates, ticker_series, forecast_horizon=forecast_horizon - ) - - return fig diff --git a/openbb_terminal/forecast/whisper_model.py b/openbb_terminal/forecast/whisper_model.py deleted file mode 100644 index 349e8023828..00000000000 --- a/openbb_terminal/forecast/whisper_model.py +++ /dev/null @@ -1,342 +0,0 @@ -"""Utilize OpenAI Whisper to transcribe and summarize text""" - -__docformat__ = "numpy" -# pylint: disable=I0011,C0413,R0915,R0912,R0914 -import logging -import os -import tempfile -import warnings -from typing import Optional - -import whisper - -try: - import yt_dlp -except ModuleNotFoundError as exc: - raise ModuleNotFoundError( - "Please use poetry to install latest yt-dlp library and dependencies. \n" - "poetry install -E forecast \n" - "\n" - "If you are not using poetry, please install the yt_dlp library with the following command: \n" - "pip install yt_dlp \n" - ) from exc - -from huggingface_hub import scan_cache_dir -from tqdm import tqdm -from transformers import BartForConditionalGeneration, BartTokenizer, pipeline - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forecast.whisper_utils import slugify, write_srt, write_vtt -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -# pylint: disable=too-many-arguments - - -def get_audio(urls): - temp_dir = tempfile.gettempdir() - - ydl = yt_dlp.YoutubeDL( - { - "quiet": True, - "verbose": False, - "format": "bestaudio", - "outtmpl": os.path.join(temp_dir, "%(id)s.%(ext)s"), - "external_downloader_args": ["-loglevel", "panic"], - "postprocessors": [ - { - "preferredcodec": "mp3", - "preferredquality": "192", - "key": "FFmpegExtractAudio", - } - ], - } - ) - - paths = {} - - for url in urls: - result = ydl.extract_info(url, download=True) - console.print( - f"Downloaded video \"{result['title']}\". Generating subtitles..." - ) - paths[result["title"]] = os.path.join(temp_dir, f"{result['id']}.mp3") - - return paths - - -@log_start_end(log=logger) -def transcribe_and_summarize( - video: str = "", - model_name: str = "base", - subtitles_format: str = "vtt", - verbose: bool = False, - task: str = "transcribe", - language: Optional[str] = None, - breaklines: int = 0, - output_dir: str = "", -): - if video == "": - console.print("[red]Please provide a video URL. [/red]") - return - - # check to make sure the video is a valid URL with a .com - if not ((video.startswith("https") or "youtube" in video) and ".com" in video): - console.print("[red]Please provide a valid video URL. [/red]") - return - - os.makedirs(output_dir, exist_ok=True) - - console.print( - "[yellow][DISCLAIMER]: This is a beta feature that uses standard NLP models. More recent" - " models such as GPT will be added in future releases. [/yellow]" - ) - console.print("") - console.print("[green]Downloading and Loading NLP Pipelines from cache...[/green]") - - hf_cache_info = scan_cache_dir() - - # Check if required models are already in cache - model_cached = False - - # check for bart-large-cnn model - for cached_repo in hf_cache_info.repos: - if cached_repo.repo_id == "facebook/bart-large-cnn": - summarizer = BartForConditionalGeneration.from_pretrained( - "facebook/bart-large-cnn" - ) - summary_tokenizer = BartTokenizer.from_pretrained("facebook/bart-large-cnn") - model_cached = True - break - - # download bart-large-cnn model if not in cache - if not model_cached: - console.print( - "[yellow]Summarization Model not found in cache. Download model (1.64G)? (y/n)[/yellow]" - ) - response = input() - if response.lower() != "y": - console.print("[red]Cancelling download and exiting command.[/red]") - return - summarizer = BartForConditionalGeneration.from_pretrained( - "facebook/bart-large-cnn" - ) - summary_tokenizer = BartTokenizer.from_pretrained("facebook/bart-large-cnn") - console.print( - "[green]Summarization Model downloaded and saved to cache for future use.[/green]" - ) - - # check for distilbert model - model_cached = False - - for cached_repo in hf_cache_info.repos: - if cached_repo.repo_id == "distilbert-base-uncased-finetuned-sst-2-english": - classifier = pipeline( - "sentiment-analysis", - model="distilbert-base-uncased-finetuned-sst-2-english", - ) - model_cached = True - break - - # download distilbert model if not in cache - if not model_cached: - console.print( - "[yellow]Sentiment Analysis model not found" - " in cache. Download model (275mb)? (y/n)[/yellow]" - ) - response = input() - if response.lower() != "y": - console.print("[red]Cancelling download and exiting command.[/red]") - return - classifier = pipeline( - "sentiment-analysis", - model="distilbert-base-uncased-finetuned-sst-2-english", - ) - console.print( - "[green]Sentiment Analysis model downloaded and" - " saved to cache for future use.[/green]" - ) - - if model_name.endswith(".en"): - warnings.warn( - f"{model_name} is an English-only model, forcing English detection." - ) - - # make user accept the download of the model if it's not in cache - # cache directory is ~/.cache/whisper - whisper_cache_dir = os.path.join(os.path.expanduser("~"), ".cache", "whisper") - - os.makedirs(whisper_cache_dir, exist_ok=True) - - sizes = { - "small": "483.6 MB", - "tiny": "75.6 MB", - "base": "145.3 MB", - "medium": "1.53 GB", - "large": "2.87 GB", - } - clean_key = model_name.replace(".en", "").replace("-v2", "").replace("-v1", "") - model_size = sizes[clean_key] - - full_model_name = model_name + ".en.pt" if language == "en" else model_name + ".pt" - if full_model_name not in os.listdir(whisper_cache_dir): - console.print("") - console.print( - f"[yellow]Whisper model ({model_name}) not found in cache. Download model ({model_size})? (y/n)[/yellow]" - ) - response = input() - if response.lower() != "y": - console.print("[red]Cancelling download and exiting command.[/red]") - return - - whisper_model = whisper.load_model(model_name) - - console.print("") - console.print("[green]All NLP Pipelines loaded.[/green]") - console.print("") - console.print("[green]Transcribing and summarizing...[/green]") - - try: - audios = get_audio([video]) - except Exception as e: - console.print(f"[red]Error downloading audio: {e}[/red]") - console.print( - "This command requires the command-line tool ffmpeg to be installed on your system," - " which is available from most package managers using the following commands:\n" - ) - console.print( - "[yellow]On Ubuntu or Debian: sudo apt update && sudo apt install ffmpeg[/yellow]\n" - ) - console.print("[yellow]On Arch Linux: sudo pacman -S ffmpeg[/yellow]\n") - console.print( - "[yellow]On MacOS using Homebrew (https://brew.sh/): brew install ffmpeg[/yellow]\n" - ) - console.print( - "[yellow]On Windows using Chocolatey (https://chocolatey.org/): choco install ffmpeg[/yellow]\n" - ) - console.print( - "[yellow]On Windows using Scoop (https://scoop.sh/): scoop install ffmpeg[/yellow]\n" - ) - return - - for title, audio_path in audios.items(): - warnings.filterwarnings("ignore") - - decode_options = {"task": task, "language": language} - result = whisper.transcribe( - model=whisper_model, - audio=audio_path, - verbose=verbose, - **decode_options, - ) - warnings.filterwarnings("default") - - all_text = "" - for segment in result["segments"]: - all_text += segment["text"] - - # original text length - original_text_length = len(all_text) - - # split the text into chunks - chunk_size = 1024 - chunks = [ - all_text[i : i + chunk_size] for i in range(0, len(all_text), chunk_size) - ] - - # process each chunk and concatenate the summaries - summary_text = "" - - # tqdm is used to show a progress bar - for chunk in tqdm(chunks): - # encode the chunk using the tokenizer - inputs = summary_tokenizer( - chunk, return_tensors="pt", truncation=True, max_length=1024 - ) - - # generate a summary using the model - summary_ids = summarizer.generate( - inputs["input_ids"], num_beams=4, max_length=100, early_stopping=True - ) - summary = summary_tokenizer.decode(summary_ids[0], skip_special_tokens=True) - - # concatenate the summaries and add a new line character - summary_text += summary + "\n" - - # initialize sentiment scores - # sentiment analysis - sentiment_size = 512 - positive_score = 0.0 - negative_score = 0.0 - total_length = 0 - for i in range(0, len(summary_text), sentiment_size): - # process each chunk and compute sentiment analysis - chunk = summary_text[i : i + sentiment_size] - chunk_sentiment = classifier(chunk)[0] - chunk_sentiment_label = chunk_sentiment["label"] - chunk_sentiment_score = round(chunk_sentiment["score"], 4) - if chunk_sentiment_label == "POSITIVE": - positive_score += chunk_sentiment_score * len(chunk) - elif chunk_sentiment_label == "NEGATIVE": - negative_score += chunk_sentiment_score * len(chunk) - total_length += len(chunk) - - overall_sentiment_label, overall_sentiment_score = get_sentiment_score( - total_length, positive_score, negative_score - ) - - # Write summary and get reduction - summary_text_length = len(summary_text) - percent_reduction = round( - (1 - (summary_text_length / original_text_length)) * 100, 2 - ) - # if there is negative reduction, set to 0 - percent_reduction = max(percent_reduction, 0) - - console.print("") - console.print("-------------------------") - console.print(f"Summary: [blue]Reduction: {percent_reduction}%[/blue]") - if overall_sentiment_label == "NEUTRAL": - console.print( - f"Sentiment: {overall_sentiment_label}:{round(overall_sentiment_score, 2)}%" - ) - else: - sent_color = "green" if overall_sentiment_label == "POSITIVE" else "red" - console.print( - "Sentiment:", - f"[{sent_color}]{overall_sentiment_label}: {round(overall_sentiment_score, 4)}[/{sent_color}]", - ) - - console.print("-------------------------") - console.print(f"[green]{summary_text}[/green]") - - # Save subtitles to file - if subtitles_format == "vtt": - vtt_path = os.path.join(output_dir, f"{slugify(title)}.vtt") - with open(vtt_path, "w", encoding="utf-8") as vtt: - write_vtt(result["segments"], file=vtt, line_length=breaklines) - - console.print("Saved VTT to", os.path.abspath(vtt_path)) - else: - srt_path = os.path.join(output_dir, f"{slugify(title)}.srt") - with open(srt_path, "w", encoding="utf-8") as srt: - write_srt(result["segments"], file=srt, line_length=breaklines) - - console.print("Saved SRT to", os.path.abspath(srt_path)) - - # Save summary to file - summary_path = os.path.join(output_dir, f"{slugify(title)}_summary.txt") - with open(summary_path, "w") as f: - f.write(summary_text) - - -def get_sentiment_score(total_length, positive_score, negative_score): - if total_length > 0: - positive_percent = positive_score / total_length * 100 - negative_percent = negative_score / total_length * 100 - if positive_percent > 70: - return "POSITIVE", positive_percent - if negative_percent > 70: - return "NEGATIVE", negative_percent - return "NEUTRAL", (positive_percent + negative_percent) / 2 - return "NEUTRAL", 0.0 diff --git a/openbb_terminal/forecast/whisper_utils.py b/openbb_terminal/forecast/whisper_utils.py deleted file mode 100644 index 4f62ceeece2..00000000000 --- a/openbb_terminal/forecast/whisper_utils.py +++ /dev/null @@ -1,87 +0,0 @@ -from typing import Iterator, TextIO - - -def str2bool(string): - str2val = {"True": True, "False": False} - if string in str2val: - return str2val[string] - raise ValueError(f"Expected one of {set(str2val.keys())}, got {string}") - - -def format_timestamp( - seconds: float, always_include_hours: bool = False, decimal_marker: str = "." -): - if seconds < 0: - raise ValueError("non-negative timestamp expected") - - milliseconds = round(seconds * 1000.0) - - hours = milliseconds // 3_600_000 - milliseconds -= hours * 3_600_000 - - minutes = milliseconds // 60_000 - milliseconds -= minutes * 60_000 - - seconds = milliseconds // 1_000 - milliseconds -= seconds * 1_000 - - hours_marker = f"{hours:02d}:" if always_include_hours or hours > 0 else "" - return ( - f"{hours_marker}{minutes:02d}:{seconds:02d}{decimal_marker}{milliseconds:03d}" - ) - - -def break_line(line: str, length: int): - break_index = min(len(line) // 2, length) # split evenly or at maximum length - - # work backwards from that guess to split between words - # if break_index <= 1, we've hit the beginning of the string and can't split - while break_index > 1: - if line[break_index - 1] == " ": - break # break at space - break_index -= 1 - if break_index > 1: - # split the line, not including the space at break_index - return line[: break_index - 1] + "\n" + line[break_index:] - return line - - -def process_segment(segment: dict, line_length: int = 0): - segment["text"] = segment["text"].strip() - if line_length and len(segment["text"]) > line_length: - # break at N characters as per Netflix guidelines - segment["text"] = break_line(segment["text"], line_length) - - return segment - - -def write_vtt(transcript: Iterator[dict], file: TextIO, line_length: int = 0): - print("WEBVTT\n", file=file) - for segment in transcript: - clean_segment = process_segment(segment, line_length=line_length) - - print( - f"{format_timestamp(clean_segment['start'])} --> {format_timestamp(clean_segment['end'])}\n" - f"{clean_segment['text'].strip().replace('-->', '->')}\n", - file=file, - flush=True, - ) - - -def write_srt(transcript: Iterator[dict], file: TextIO, line_length: int = 0): - for i, segment in enumerate(transcript, start=1): - clean_segment = process_segment(segment, line_length=line_length) - - print( - f"{i}\n" - f"{format_timestamp(clean_segment['start'], always_include_hours=True, decimal_marker=',')} --> " - f"{format_timestamp(clean_segment['end'], always_include_hours=True, decimal_marker=',')}\n" - f"{clean_segment['text'].strip().replace('-->', '->')}\n", - file=file, - flush=True, - ) - - -def slugify(title): - # adding a slug (-) in each of the string where there is a space - return "".join(c if c.isalnum() else "_" for c in title).rstrip("_") diff --git a/openbb_terminal/forex/README.md b/openbb_terminal/forex/README.md deleted file mode 100644 index ab6ac1426f6..00000000000 --- a/openbb_terminal/forex/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Resources - -* Forex Trading: A Beginner’s Guide : diff --git a/openbb_terminal/forex/__init__.py b/openbb_terminal/forex/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/forex/av_model.py b/openbb_terminal/forex/av_model.py deleted file mode 100644 index bf34651f78f..00000000000 --- a/openbb_terminal/forex/av_model.py +++ /dev/null @@ -1,178 +0,0 @@ -"""AlphaVantage FX Model.""" - -import argparse -import logging -import os -from typing import Any, Dict, List - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_timezone, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def get_currency_list() -> List: - """Load AV currency codes from a local file.""" - path = os.path.join(os.path.dirname(__file__), "data/av_forex_currencies.csv") - return list(pd.read_csv(path)["currency code"]) - - -CURRENCY_LIST = get_currency_list() - - -@log_start_end(log=logger) -def check_valid_forex_currency(symbol: str) -> str: - """Check if given symbol is supported on alphavantage. - - Parameters - ---------- - symbol : str - Symbol to check - - Returns - ------- - str - Currency symbol - - Raises - ------ - argparse.ArgumentTypeError - Symbol not valid on alphavantage - """ - if symbol.upper() in CURRENCY_LIST: - return symbol.upper() - - raise argparse.ArgumentTypeError( - f"{symbol.upper()} not found in alphavantage supported currency codes. " - ) - - -@log_start_end(log=logger) -def get_quote(to_symbol: str = "USD", from_symbol: str = "EUR") -> Dict[str, Any]: - """Get current exchange rate quote from alpha vantage. - - Parameters - ---------- - to_symbol : str - To forex symbol - from_symbol : str - From forex symbol - - Returns - ------- - Dict[str, Any] - Dictionary of exchange rate - """ - url = ( - "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE" - + f"&from_currency={from_symbol}" - + f"&to_currency={to_symbol}" - + f"&apikey={get_current_user().credentials.API_KEY_ALPHAVANTAGE}" - ) - - response = request(url) - response_json = response.json() - result = {} - - # If the returned data was unsuccessful - if "Error Message" in response_json: - console.print(response_json["Error Message"]) - logger.error(response_json["Error Message"]) - # check if json is empty - elif not response_json: - console.print("No data found.\n") - else: - result = response_json - - return result - - -@log_start_end(log=logger) -def get_historical( - to_symbol: str = "USD", - from_symbol: str = "EUR", - resolution: str = "d", - interval: int = 5, - start_date: str = "", - end_date: str = "", -) -> pd.DataFrame: - """Get historical forex data. - - Parameters - ---------- - to_symbol : str - To forex symbol - from_symbol : str - From forex symbol - resolution : str, optional - Resolution of data. Can be "i", "d", "w", "m" for intraday, daily, weekly or monthly - interval : int, optional - Interval for intraday data - start_date : str, optional - Start date for data. - end_date : str, optional - End date for data. - - Returns - ------- - pd.DataFrame - Historical data for forex pair - """ - d_res = {"i": "FX_INTRADAY", "d": "FX_DAILY", "w": "FX_WEEKLY", "m": "FX_MONTHLY"} - - url = f"https://www.alphavantage.co/query?function={d_res[resolution]}&from_symbol={from_symbol}" - url += f"&to_symbol={to_symbol}&outputsize=full&apikey={get_current_user().credentials.API_KEY_ALPHAVANTAGE}" - if resolution == "i": - url += f"&interval={interval}min" - - r = request(url) - response_json = r.json() - - if r.status_code != 200: - return pd.DataFrame() - - df = pd.DataFrame() - - # If the returned data was unsuccessful - if "Error Message" in response_json: - console.print(response_json["Error Message"]) - elif "Note" in response_json: - console.print(response_json["Note"]) - elif not response_json: - console.print("No data found.\n") - else: - if "Meta Data" not in response_json and "Information" in response_json: - console.print(response_json["Information"]) - return pd.DataFrame() - - key = list(response_json.keys())[1] - - df = pd.DataFrame.from_dict(response_json[key], orient="index") - df.index = pd.to_datetime(df.index) - - if start_date and resolution != "i": - df = df[df.index > start_date] - - if end_date and resolution != "i": - df = df[df.index < end_date] - - if (df.index.hour != 0).any(): - # if intraday data, convert to local timezone - df.index = df.index.tz_localize("UTC").tz_convert(get_user_timezone()) - - df = df.rename( - columns={ - "1. open": "Open", - "2. high": "High", - "3. low": "Low", - "4. close": "Close", - } - ) - df.index = pd.DatetimeIndex(df.index) - df = df[::-1] - - return df.astype(float) diff --git a/openbb_terminal/forex/av_view.py b/openbb_terminal/forex/av_view.py deleted file mode 100644 index 9bec95528b6..00000000000 --- a/openbb_terminal/forex/av_view.py +++ /dev/null @@ -1,53 +0,0 @@ -"""AlphaVantage Forex View.""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.forex import av_model -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_quote(to_symbol: str = "USD", from_symbol: str = "EUR"): - """Display current forex pair exchange rate. - - Parameters - ---------- - to_symbol : str - To symbol - from_symbol : str - From forex symbol - """ - quote = av_model.get_quote(to_symbol, from_symbol) - - if not bool(quote) or "Note" in quote: - if "Note" in quote: - console.print(quote["Note"]) - console.print( - "\n[red]" - + "No historical data loaded.\n" - + "Make sure 'av' supports the requested pair and you aren't hitting your API call limits." - + "[/red]\n" - ) - return - - try: - df = pd.DataFrame.from_dict(quote) - except ValueError: - console.print(f"[red]{quote['Information']}[/red]\n") - return - df.index = df.index.to_series().apply(lambda x: x[3:]).values - df = df.iloc[[0, 2, 5, 4, 7, 8]] - print_rich_table( - df, - show_index=True, - title=f"[bold]{from_symbol}/{to_symbol} Quote [/bold]", - ) diff --git a/openbb_terminal/forex/data/av_forex_currencies.csv b/openbb_terminal/forex/data/av_forex_currencies.csv deleted file mode 100644 index 706c5a1ef18..00000000000 --- a/openbb_terminal/forex/data/av_forex_currencies.csv +++ /dev/null @@ -1,160 +0,0 @@ -currency code,currency name -AED,United Arab Emirates Dirham -AFN,Afghan Afghani -ALL,Albanian Lek -AMD,Armenian Dram -ANG,Netherlands Antillean Guilder -AOA,Angolan Kwanza -ARS,Argentine Peso -AUD,Australian Dollar -AWG,Aruban Florin -AZN,Azerbaijani Manat -BAM,Bosnia-Herzegovina Convertible Mark -BBD,Barbadian Dollar -BDT,Bangladeshi Taka -BGN,Bulgarian Lev -BHD,Bahraini Dinar -BIF,Burundian Franc -BMD,Bermudan Dollar -BND,Brunei Dollar -BOB,Bolivian Boliviano -BRL,Brazilian Real -BSD,Bahamian Dollar -BTN,Bhutanese Ngultrum -BWP,Botswanan Pula -BZD,Belize Dollar -CAD,Canadian Dollar -CDF,Congolese Franc -CHF,Swiss Franc -CLF,Chilean Unit of Account UF -CLP,Chilean Peso -CNH,Chinese Yuan Offshore -CNY,Chinese Yuan -COP,Colombian Peso -CUP,Cuban Peso -CVE,Cape Verdean Escudo -CZK,Czech Republic Koruna -DJF,Djiboutian Franc -DKK,Danish Krone -DOP,Dominican Peso -DZD,Algerian Dinar -EGP,Egyptian Pound -ERN,Eritrean Nakfa -ETB,Ethiopian Birr -EUR,Euro -FJD,Fijian Dollar -FKP,Falkland Islands Pound -GBP,British Pound Sterling -GEL,Georgian Lari -GHS,Ghanaian Cedi -GIP,Gibraltar Pound -GMD,Gambian Dalasi -GNF,Guinean Franc -GTQ,Guatemalan Quetzal -GYD,Guyanaese Dollar -HKD,Hong Kong Dollar -HNL,Honduran Lempira -HRK,Croatian Kuna -HTG,Haitian Gourde -HUF,Hungarian Forint -ICP,Internet Computer -IDR,Indonesian Rupiah -ILS,Israeli New Sheqel -INR,Indian Rupee -IQD,Iraqi Dinar -IRR,Iranian Rial -ISK,Icelandic Krona -JEP,Jersey Pound -JMD,Jamaican Dollar -JOD,Jordanian Dinar -JPY,Japanese Yen -KES,Kenyan Shilling -KGS,Kyrgystani Som -KHR,Cambodian Riel -KMF,Comorian Franc -KPW,North Korean Won -KRW,South Korean Won -KWD,Kuwaiti Dinar -KYD,Cayman Islands Dollar -KZT,Kazakhstani Tenge -LAK,Laotian Kip -LBP,Lebanese Pound -LKR,Sri Lankan Rupee -LRD,Liberian Dollar -LSL,Lesotho Loti -LYD,Libyan Dinar -MAD,Moroccan Dirham -MDL,Moldovan Leu -MGA,Malagasy Ariary -MKD,Macedonian Denar -MMK,Myanma Kyat -MNT,Mongolian Tugrik -MOP,Macanese Pataca -MRO,Mauritanian Ouguiya (pre-2018) -MRU,Mauritanian Ouguiya -MUR,Mauritian Rupee -MVR,Maldivian Rufiyaa -MWK,Malawian Kwacha -MXN,Mexican Peso -MYR,Malaysian Ringgit -MZN,Mozambican Metical -NAD,Namibian Dollar -NGN,Nigerian Naira -NOK,Norwegian Krone -NPR,Nepalese Rupee -NZD,New Zealand Dollar -OMR,Omani Rial -PAB,Panamanian Balboa -PEN,Peruvian Nuevo Sol -PGK,Papua New Guinean Kina -PHP,Philippine Peso -PKR,Pakistani Rupee -PLN,Polish Zloty -PYG,Paraguayan Guarani -QAR,Qatari Rial -RON,Romanian Leu -RSD,Serbian Dinar -RUB,Russian Ruble -RUR,Old Russian Ruble -RWF,Rwandan Franc -SAR,Saudi Riyal -SBDf,Solomon Islands Dollar -SCR,Seychellois Rupee -SDG,Sudanese Pound -SDR,Special Drawing Rights -SEK,Swedish Krona -SGD,Singapore Dollar -SHIB,Shiba Inu -SHP,Saint Helena Pound -SLL,Sierra Leonean Leone -SOS,Somali Shilling -SRD,Surinamese Dollar -SYP,Syrian Pound -SZL,Swazi Lilangeni -THB,Thai Baht -TJS,Tajikistani Somoni -TMT,Turkmenistani Manat -TND,Tunisian Dinar -TOP,Tongan Pa'anga -TRY,Turkish Lira -TTD,Trinidad and Tobago Dollar -TWD,New Taiwan Dollar -TZS,Tanzanian Shilling -UAH,Ukrainian Hryvnia -UGX,Ugandan Shilling -USD,United States Dollar -UYU,Uruguayan Peso -UZS,Uzbekistan Som -VND,Vietnamese Dong -VUV,Vanuatu Vatu -WBTC,Wrapped Bitcoin -WST,Samoan Tala -XAF,CFA Franc BEAC -XCD,East Caribbean Dollar -XDR,Special Drawing Rights -XOF,CFA Franc BCEAO -XPF,CFP Franc -YER,Yemeni Rial -ZAR,South African Rand -ZMW,Zambian Kwacha -ZWL,Zimbabwean Dollar diff --git a/openbb_terminal/forex/data/polygon_tickers.csv b/openbb_terminal/forex/data/polygon_tickers.csv deleted file mode 100644 index 6ca7b188aca..00000000000 --- a/openbb_terminal/forex/data/polygon_tickers.csv +++ /dev/null @@ -1,1121 +0,0 @@ -GBPMDL -AUDCHF -ZARPLN -DKKNZD -XAGUSD -USDRWF -MYREUR -ZAREUR -MWKGBP -USDBSD -ALLGBP -MXNZAR -HUFUSD -BRLARS -OMRAED -ZARUGX -GBPCZK -XAUCNY -MURGBP -EURLSL -EURBWP -AEDPKR -XAGBRL -XAUGBP -IDRKRW -BRICHF -VNDJPY -MADAUD -GBPBMD -USDTMT -HRKUSD -HUFPLN -HUFDKK -SGDGBP -KZTGBP -AUNUSD -EURCNY -HUFCAD -GBPSEK -ZARIDR -JPYTHB -MDLGBP -IDRCHF -KWDAED -SARJPY -GBPRON -ARSPEN -TWDCHF -INREUR -DKKCAD -USDCZK -AUDIDR -SARUSD -THBINR -XAUCAD -GHSGBP -EURKHR -ZMWZAR -ILSSEK -SGDTHB -EURKYD -USDMGA -ZARNOK -SGDCAD -SEKINR -DKKRUB -THBUSD -USDMYR -USDUZS -ZARSZL -JPYCLP -UAHUSD -JPYMXN -JPYNOK -AUDCNY -EURGYD -GBPBDT -PENUSD -ZARPHP -MXNARS -PLNCZK -GBPAUD -AEDAUD -RUBPLN -USDSDG -EURSCR -HKDMXN -RUBCHF -TRYJPY -OSOUSD -GBPMAD -EURTJS -PKREUR -EURCDF -AUDCAD -USDRSD -JPYCZK -BRLCLP -MWKUSD -ISKCHF -GBPBTN -BAMGBP -ILSZAR -SEKZAR -HUFZAR -QARAED -SARKWD -MYRHKD -RONZAR -JPYSAR -BHDPKR -DOPUSD -AUDGBP -INRHKD -XAUAUD -IDRAUD -XAUHKD -KRWSGD -AUDNZD -JPYGBP -KESUSD -KRWRUB -TNDGBP -USDEUR -BWPGBP -DKKMYR -GBPNGN -QARPKR -TWDCAD -BMDGBP -HUFJPY -GBPSCR -EURMVR -KHRGBP -LKRZAR -ZARBWP -EURIDR -GBPUAH -GBPGYD -ZARTND -KRWUSD -AUDMAD -ZARPKR -NZDGBP -AEDSAR -MYRIDR -XPTEUR -GHSZAR -SGDSEK -PENCAD -HKDZAR -SGDJPY -TRYUSD -GBPAMD -EURCOP -NZDZAR -DKKSGD -IDRTHB -AEDSEK -BDTGBP -AUDTHB -XAUKRW -GBPRUB -GBPYER -USDMVR -BRLMXN -TTDGBP -GBPBBD -EURPLN -SGDUSD -AUDILS -EURAED -NSOUSD -VRLCHF -SCRUSD -RWFUSD -ZARHKD -EURNZD -IDRZAR -TWDINR -KRWZAR -ZARNAD -HRKPLN -BAMUSD -EUREGP -NPRUSD -KRWTWD -XAUJPY -USDBHD -BNDGBP -EURBMD -EURNOK -MXNUSD -USDEGP -XPTGBP -ARSCOP -NZDCHF -NZDJPY -KRWCAD -EURBRL -DKKSEK -MALCHF -SOSUSD -GBPCUP -AUDCLP -EURUYU -EURUZS -UYUUSD -EURUSD -BWPZAR -NZDPLN -GBPLRD -IDRMYR -PABUSD -USDPKR -ARSCHF -MYRNZD -JODUSD -ARSHKD -EURMKD -EURLYD -XAGARS -ISKUSD -HKDSEK -USDTRY -RSDUSD -DKKAED -ZARMYR -KMFUSD -NGNZAR -IDRJPY -SARPKR -GBPMYR -ILSAUD -IDREUR -JPYAUD -QARUSD -GBPSLL -GBPEUR -PHPUSD -THBEUR -RUBEUR -USDMMK -XAGGBP -ZARARS -USDKMF -BZDUSD -SEKPLN -PLNJPY -GBPLBP -EURHNL -AUDPKR -KYDCAD -XAUZAR -MGAGBP -LRDUSD -NZDTRY -AUDHUF -USDLKR -EURNGN -BNDUSD -SGDIDR -MYRCAD -HKDPLN -EUROMR -EURSAR -GBPJPY -IDRHKD -MXNPEN -USDXPF -IQDGBP -USDPGK -INRCHF -NZDHKD -AUDHKD -BBDGBP -USDGTQ -NGNJPY -ARSSGD -GBPMOP -TWDMYR -BMDCAD -XPDUSD -USDDZD -EURBSD -SGDZAR -AUNCHF -ARSMXN -AUDFJD -UGXZAR -AUDARS -IQDUSD -MXNEUR -HKDAUD -XAGEUR -RONGBP -USDZMW -TRYSGD -INRSGD -GBPLAK -GBPMWK -EURKES -USDCUP -USDUYU -AUDJPY -XAGCAD -DKKCNY -USDJMD -MADGBP -ILSNOK -MUREUR -THBHKD -INRTHB -SAREUR -MYRGBP -AMDGBP -HKDARS -GBPARS -SGDAUD -INRTWD -BHDUSD -LRDGBP -GBPPAB -RONUSD -IDRNZD -MYRKRW -KMFGBP -ZARGHS -PLNMXN -USDHUF -ARECHF -BMDEUR -KYDGBP -AUDNOK -AUDCNH -TWDNZD -KWDEUR -EURZMW -KZTUSD -GBPBOB -GBPJMD -DKKCHF -EURCNH -SEKILS -AUDKRW -AWGGBP -HNLGBP -XAUEUR -NSOCHF -GBPLSL -USDBZD -ZARKES -SLLUSD -THBCHF -NOKUSD -EURCZK -HKDGBP -GBPTTD -MMKGBP -PGKGBP -BRLAUD -AEDDKK -EURBHD -JMDUSD -USDNGN -NOKRUB -SVCUSD -GBPQAR -LTLAUD -JPYTWD -USDRON -KRUCHF -SGDBRL -EGPPKR -EURJMD -MXNDKK -NZDMYR -USDLBP -MWKZAR -HRKAUD -MYRJPY -USDKWD -AUDBRL -EURYER -PLNGBP -IDRTWD -EGPEUR -EURPYG -ILSAED -EURKZT -DKKZAR -FJDUSD -NBLUSD -AEDEUR -PLNNOK -XPTUSD -MXNBRL -USDNAD -ZARUSD -NOKINR -ARSZAR -BOBGBP -RWFZAR -GBPLYD -USDUGX -GBPCHF -DKKAUD -PLNCHF -USDBIF -AUDSGD -MXNCZK -PENGBP -NOKAED -USDMKD -MKDGBP -PKRUSD -EURPGK -THBPKR -HTGUSD -SGDPKR -LKRGBP -EURPKR -ZARMXN -USDMXN -USDHRK -INRJPY -FRNCHF -MXNCLP -GBPNOK -SGDPLN -USDARS -OMRUSD -DKKBGN -INRSEK -USDCAD -XPDGBP -KESZAR -JPYPKR -BDTJPY -NPRGBP -EURZAR -MYRPKR -PGKAUD -KYDEUR -DKKISK -SGDEUR -MYRZAR -PLNDKK -BRLPEN -ZMWUSD -TNDUSD -GHSEUR -PGKUSD -KYDBMD -SZLGBP -DKKMXN -BGNCAD -BRLCHF -INRMYR -GBPISK -SGDNOK -HKDTHB -TWDPKR -GBPPYG -NOKDKK -GBPTHB -EURJOD -EURUAH -RUBZAR -PENMXN -SGDNZD -HKDPKR -USDDOP -NZDHUF -KYDUSD -GBPBZD -EGPZAR -NZDUSD -KWDGBP -USDBAM -EURTTD -ZARCAD -AEDINR -EURTND -USDCDF -ZARCOP -KRWEUR -ILSEUR -SVCGBP -GBPBAM -IDRINR -DOECHF -ZARNGN -DOEUSD -THBCNY -GBPTND -MGAUSD -THBZAR -TWDJPY -SZLEUR -EURLAK -EURUGX -KRWCNY -DZDEUR -EURCHF -ZARGBP -MYRCHF -HKDINR -EURHRK -USDKRW -SEKEUR -MDLEUR -AEDBHD -RUBKRW -USDBND -NIOGBP -DKKUSD -NIOUSD -BDTUSD -USDILS -GBPSZL -NOKZAR -BRLRSD -HTGGBP -RUBSEK -EURLKR -SEKGBP -DKKCZK -THBCAD -EURVND -USDGNF -GBPPHP -MXNSGD -AUDMYR -EURGNF -USDKES -ETBGBP -EURBZD -GBPUGX -USDPAB -HNLUSD -ARSAUD -SGDARS -JODILS -GBPKHR -USDFJD -DKKNOK -GBPPEN -DKKINR -JPYKRW -ZARAUD -VNDCAD -TWDEUR -JPYSEK -ZARDKK -INRCAD -EURLBP -XCDGBP -HUFCHF -XAUMXN -XAUINR -GBPMKD -THBMYR -KWDPKR -USDJPY -DKKTHB -TWDHKD -NZDIDR -AEDNOK -PKRGBP -MXNRUB -ZARCZK -INRKRW -MYRTHB -NZDPKR -EURBBD -MYRSGD -JPYDKK -USDMWK -USDTND -NOKCHF -TWDAUD -NGNUSD -AUDZAR -SGDMXN -KRWHKD -MVRUSD -THBTWD -EURFJD -GBPUSD -GBPILS -GBPXCD -INRNZD -EURNIO -USDSLL -USDAUD -USDNZD -KRWINR -JPYMYR -ZARRUB -EURSEK -JPYARS -AEDCAD -MXNCOP -AUDMXN -XPDEUR -EURMDL -TWDKRW -XAGTRY -BRLSGD -USDSEK -AUDHRK -DKKPLN -DKKGBP -ZARKRW -BGNUSD -AEDGBP -EURILS -USDLRD -AUDSEK -ZARRON -VNDEUR -SCRGBP -AUDAED -GNFUSD -INRZAR -DKKEUR -ARSBRL -MXNCHF -GBPDOP -LYDUSD -EURSOS -EURMAD -USDPLN -USDCHF -DKKHUF -DJFGBP -EURAFN -TRYCHF -GBPGNF -PHPJPY -EURCAD -USDPYG -EURDOP -PENARS -EGPJPY -GBPVND -USDBWP -DKKTWD -PENCLP -GBPBHD -SEKCHF -PLNEUR -XAGRUB -MYRINR -THBGBP -EURPAB -USDMDL -EURTRY -USDHKD -EURRUB -ZARAED -AUDPLN -SGDCHF -ILSJPY -KRWAUD -OSOCHF -GBPMMK -SZLCHF -GBPKMF -KRWNZD -ARSCAD -JMDGBP -THBAUD -USDCOP -ZARRWF -SEKNOK -HKDCHF -ZARTHB -ALLUSD -SOSGBP -EURINR -EURMMK -USDBDT -AUDPGK -USDKHR -USDBOB -EURAUD -TWDDKK -XAGMXN -SGDKRW -USDSCR -XPFGBP -XAGHKD -GBPKWD -USDAED -MMKUSD -PENBRL -GBPAED -HKDTWD -TWDSGD -NOKILS -EURPHP -USDRUB -SLLGBP -RUBMXN -GBPMXN -EURDKK -GBPPLN -IDRSGD -DZDUSD -MXNHKD -AUDPHP -SEKJPY -USDPEN -SGDCNY -ZARJPY -NZDINR -SGDINR -HKDKRW -GBPMUR -ZAREGP -OMRGBP -USDVND -GBPRWF -ETBUSD -TWDZAR -GBPGTQ -XAURUB -MKDUSD -EURCUP -GBPNPR -SEKUSD -ZARCNY -EURMYR -USDZAR -NZDEUR -EURDZD -GBPKZT -GBPPGK -ZARCYP -RONCHF -AEDCHF -MXNGBP -MXNJPY -GBPBWP -EURALL -JPYILS -INRGBP -GBPFJD -GBPHRK -JODAED -KWDUSD -USDLSL -HKDJPY -AUDUSD -ARSCLP -KRWSEK -NZDCZK -USDMUR -SHPUSD -GBPKES -AUDTWD -HKDBRL -GTQUSD -GBPETB -USDCVE -GHSUSD -ZARSEK -BGNCNY -ILSPLN -GBPAFN -GBPCNH -BRIUSD -ZARBRL -GBPBND -GBPKYD -JPYHKD -EURMZN -DKKCOP -BOBUSD -ZARHUF -EURHUF -ZARINR -MURUSD -USDSAR -EURTHB -EURDJF -EURMGA -ZARSGD -NZDAUD -XAGKRW -EURNPR -BWPCHF -YERUSD -ALLEUR -TRYPLN -MYRCNY -AEDNZD -AWGUSD -JPYCAD -EURRON -EURBOB -USDHNL -GBPCAD -THBSGD -SGDTRY -USDIDR -BBDUSD -EURNAD -EURGTQ -GBPALL -NOKEUR -NZDTWD -BRLZAR -USDBBD -BRLEUR -LKRUSD -EURBTN -JPYRUB -USDQAR -LYDGBP -BBDEUR -GBPUYU -USDTHB -GBPINR -BRLUSD -VNDUSD -UZSUSD -ILSUSD -MXNAUD -GBPTRY -JPYINR -KRUUSD -XAUUSD -EURMXN -USDPHP -GBPJOD -AFNGBP -SGDTWD -EURHKD -AEDZAR -GBPNIO -USDNOK -SGDDKK -ZARTWD -BIFUSD -GYDGBP -PKRZAR -JPYPLN -XAUBRL -USDETB -BMDKYD -ZARTRY -GBPRSD -USDAWG -PENCOP -USDTTD -RSDGBP -GBPMGA -GBPNZD -DKKPKR -ZARNZD -QARZAR -DKKJPY -GBPKRW -TZSUSD -GBPGHS -AUDDKK -NBLCHF -BMDBBD -THBIDR -NZDAED -MYRUSD -EURMOP -TWDSEK -AUDBGN -BHDEUR -GBPBGN -EURBIF -USDCLP -GTQGBP -JPYIDR -XAGAUD -EURCLP -UYUGBP -DKKPHP -USDCNH -USDKZT -EURETB -EURPEN -BGNAUD -INRUSD -IDRCAD -EURMUR -INRCNY -OMRZAR -HKDCAD -GBPHKD -HKDUSD -TJSUSD -DKKHKD -AUDCZK -GBPEGP -EURSLL -GBPCNY -EURGBP -ZARILS -GBPTWD -XAUARS -USDBRL -BWPUSD -BSDUSD -EURGHS -NZDTHB -NZDKRW -JPYEUR -USDOMR -EURBDT -HKDMYR -PYGUSD -GYDUSD -SEKDKK -EURRSD -EURBGN -GBPLKR -DKKUAH -XAGINR -ILSJOD -RUBAUD -LSLGBP -SEKCZK -ARSGBP -GBPIQD -GBPMVR -USDSZL -GBPHNL -ARSUSD -GBPIDR -USDDJF -EURQAR -JPYZAR -KRWIDR -RUBUSD -UAHDKK -JPYNZD -TWDIDR -USDALL -PLNUSD -PHPGBP -USDGBP -SGDHKD -GBPOMR -KRWTHB -ARSEUR -USDJOD -GBPSOS -SZLZAR -USDHTG -NOKPLN -GBPXPF -EURKWD -SZLUSD -MADZAR -SEKAUD -NOKGBP -ILSCHF -GBPSAR -GBPBIF -DKKTRY -EGPUSD -USDYER -MADUSD -TRYZAR -XAUTHB -XAUSAR -AEDJPY -USDISK -USDDKK -AUDTRY -USDBGN -PLNSEK -NZDCNY -THBJPY -JPYBRL -RUBJPY -AEDUSD -USDGHS -TTDUSD -USDKYD -EURRWF -GBPSGD -FJDGBP -HRKEUR -BSDGBP -INRAUD -USDINR -USDCNY -GBPCLP -XAGZAR -KRWBRL -DOPGBP -USDNIO -USDIQD -HKDEUR -AUDINR -BTNGBP -JPYCHF -USDLYD -EURSDG -USDSGD -GBPDKK -JPYRSD -EURBND -HKDNZD -TRYDKK -XAGJPY -GNFGBP -RWFGBP -GBPDZD -EURSGD -HKDSGD -IDRUSD -IDRCNY -SEKCAD -USDMAD -NOKJPY -ARSJPY -SGDAED -NZDCAD -RUBNOK -EURTZS -HKDDKK -XAUTRY -USDLAK -PLNHUF -THBKRW -THBNZD -EURSZL -MYRAUD -EURARS -SGDMYR -TWDCNY -MDLUSD -GBPDJF -HRKCAD -GBPHTG -EURTWD -ILSCAD -NGNGBP -KRWMYR -EURHTG -LBPUSD -BRLJPY -GBPCOP -NOKSEK -NZDDKK -PLNILS -EURMWK -USDUAH -KRWJPY -PHPDKK -USDTJS -EURIQD -HUFEUR -MXNPLN -GBPBRL -USDTZS -PENEUR -EURAMD -ZARMWK -TZSZAR -USDTWD -HKDCNY -LSLUSD -ZARMAD -EURKRW -TWDTHB -BRLCAD -BRLCOP -XAGCNY -JPYCNY -JPYAED -YERGBP -EURLRD -JPYUSD -BRLSEK -USDNPR -NZDNOK -MOPUSD -MYRDKK -GBPBSD -EURISK -USDGYD -AUDLTL -NZDMXN -TWDUSD -MYRTWD -GBPCDF -NZDSEK -IDRGBP -AUDEUR -USDMOP -RUBGBP -VRNCHF -BWPEUR -EURJPY -GBPHUF -KRWCHF -MALUSD -BGNDKK -BRLKRW -AUDRUB -NZDSGD -ZARCHF -HKDIDR -MXNCAD -GBPZAR -XAGSAR -BRLHKD -UYUEUR diff --git a/openbb_terminal/forex/data/yahoofinance_forex.json b/openbb_terminal/forex/data/yahoofinance_forex.json deleted file mode 100644 index 4e5535413b0..00000000000 --- a/openbb_terminal/forex/data/yahoofinance_forex.json +++ /dev/null @@ -1,20418 +0,0 @@ -[ - { - "id": "AED=X", - "currency": "AED", - "exchange": "CCY", - "name": "USD/AED", - "from_symbol": "USD", - "to_symbol": "AED" - }, - { - "id": "AEDAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "AED/AUD", - "from_symbol": "AED", - "to_symbol": "AUD" - }, - { - "id": "AEDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "AED/BRX", - "from_symbol": "AED", - "to_symbol": "BRX" - }, - { - "id": "AEDCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "AED/CAD", - "from_symbol": "AED", - "to_symbol": "CAD" - }, - { - "id": "AEDCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "AED/CHF", - "from_symbol": "AED", - "to_symbol": "CHF" - }, - { - "id": "AEDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "AED/EUR", - "from_symbol": "AED", - "to_symbol": "EUR" - }, - { - "id": "AEDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "AED/GBP", - "from_symbol": "AED", - "to_symbol": "GBP" - }, - { - "id": "AEDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "AED/HKD", - "from_symbol": "AED", - "to_symbol": "HKD" - }, - { - "id": "AEDINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "AED/INR", - "from_symbol": "AED", - "to_symbol": "INR" - }, - { - "id": "AEDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "AED/JPY", - "from_symbol": "AED", - "to_symbol": "JPY" - }, - { - "id": "AEDKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "AED/KRW", - "from_symbol": "AED", - "to_symbol": "KRW" - }, - { - "id": "AEDKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "AED/KWD", - "from_symbol": "AED", - "to_symbol": "KWD" - }, - { - "id": "AEDLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "AED/LKR", - "from_symbol": "AED", - "to_symbol": "LKR" - }, - { - "id": "AEDMAD=X", - "currency": "MAD", - "exchange": "CCY", - "name": "AED/MAD", - "from_symbol": "AED", - "to_symbol": "MAD" - }, - { - "id": "AEDMUR=X", - "currency": "MUR", - "exchange": "CCY", - "name": "AED/MUR", - "from_symbol": "AED", - "to_symbol": "MUR" - }, - { - "id": "AEDMYX=X", - "currency": null, - "exchange": "CCY", - "name": "AED/MYX", - "from_symbol": "AED", - "to_symbol": "MYX" - }, - { - "id": "AEDNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "AED/NOK", - "from_symbol": "AED", - "to_symbol": "NOK" - }, - { - "id": "AEDNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "AED/NZD", - "from_symbol": "AED", - "to_symbol": "NZD" - }, - { - "id": "AEDOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "AED/OMR", - "from_symbol": "AED", - "to_symbol": "OMR" - }, - { - "id": "AEDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "AED/PKR", - "from_symbol": "AED", - "to_symbol": "PKR" - }, - { - "id": "AEDQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "AED/QAR", - "from_symbol": "AED", - "to_symbol": "QAR" - }, - { - "id": "AEDSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "AED/SAR", - "from_symbol": "AED", - "to_symbol": "SAR" - }, - { - "id": "AEDSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "AED/SEK", - "from_symbol": "AED", - "to_symbol": "SEK" - }, - { - "id": "AEDSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "AED/SGD", - "from_symbol": "AED", - "to_symbol": "SGD" - }, - { - "id": "AEDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "AED/THX", - "from_symbol": "AED", - "to_symbol": "THX" - }, - { - "id": "AEDTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "AED/TRY", - "from_symbol": "AED", - "to_symbol": "TRY" - }, - { - "id": "AEDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "AED/USD", - "from_symbol": "AED", - "to_symbol": "USD" - }, - { - "id": "AEDXDR=X", - "currency": null, - "exchange": "CCY", - "name": "AED/XDR", - "from_symbol": "AED", - "to_symbol": "XDR" - }, - { - "id": "AEDZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "AED/ZAR", - "from_symbol": "AED", - "to_symbol": "ZAR" - }, - { - "id": "AFN=X", - "currency": "AFN", - "exchange": "CCY", - "name": "USD/AFN", - "from_symbol": "USD", - "to_symbol": "AFN" - }, - { - "id": "AFNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "AFN/BRX", - "from_symbol": "AFN", - "to_symbol": "BRX" - }, - { - "id": "AFNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "AFN/USD", - "from_symbol": "AFN", - "to_symbol": "USD" - }, - { - "id": "ALL=X", - "currency": "ALL", - "exchange": "CCY", - "name": "USD/ALL", - "from_symbol": "USD", - "to_symbol": "ALL" - }, - { - "id": "ALLBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ALL/BRX", - "from_symbol": "ALL", - "to_symbol": "BRX" - }, - { - "id": "ALLUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ALL/USD", - "from_symbol": "ALL", - "to_symbol": "USD" - }, - { - "id": "AMD=X", - "currency": "AMD", - "exchange": "CCY", - "name": "USD/AMD", - "from_symbol": "USD", - "to_symbol": "AMD" - }, - { - "id": "AMDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "AMD/BRX", - "from_symbol": "AMD", - "to_symbol": "BRX" - }, - { - "id": "AMDRUX=X", - "currency": null, - "exchange": "CCY", - "name": "AMD/RUX", - "from_symbol": "AMD", - "to_symbol": "RUX" - }, - { - "id": "ANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "USD/ANG", - "from_symbol": "USD", - "to_symbol": "ANG" - }, - { - "id": "ANGBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ANG/BRX", - "from_symbol": "ANG", - "to_symbol": "BRX" - }, - { - "id": "ANGEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "ANG/EUR", - "from_symbol": "ANG", - "to_symbol": "EUR" - }, - { - "id": "ANGSRD=X", - "currency": null, - "exchange": "CCY", - "name": "ANG/SRD", - "from_symbol": "ANG", - "to_symbol": "SRD" - }, - { - "id": "ANGUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ANG/USD", - "from_symbol": "ANG", - "to_symbol": "USD" - }, - { - "id": "ANGZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "ANG/ZAR", - "from_symbol": "ANG", - "to_symbol": "ZAR" - }, - { - "id": "AOA=X", - "currency": "AOA", - "exchange": "CCY", - "name": "USD/AOA", - "from_symbol": "USD", - "to_symbol": "AOA" - }, - { - "id": "AOABRX=X", - "currency": null, - "exchange": "CCY", - "name": "AOA/BRX", - "from_symbol": "AOA", - "to_symbol": "BRX" - }, - { - "id": "AOAUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "AOA/USD", - "from_symbol": "AOA", - "to_symbol": "USD" - }, - { - "id": "ARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "USD/ARS", - "from_symbol": "USD", - "to_symbol": "ARS" - }, - { - "id": "ARSBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "ARS/BRL", - "from_symbol": "ARS", - "to_symbol": "BRL" - }, - { - "id": "ARSBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ARS/BRX", - "from_symbol": "ARS", - "to_symbol": "BRX" - }, - { - "id": "ARSEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "ARS/EUR", - "from_symbol": "ARS", - "to_symbol": "EUR" - }, - { - "id": "ARSGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "ARS/GBP", - "from_symbol": "ARS", - "to_symbol": "GBP" - }, - { - "id": "ARSJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "ARS/JPY", - "from_symbol": "ARS", - "to_symbol": "JPY" - }, - { - "id": "ARSUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ARS/USD", - "from_symbol": "ARS", - "to_symbol": "USD" - }, - { - "id": "ARSXCU=X", - "currency": null, - "exchange": "CCY", - "name": "ARS/XCU", - "from_symbol": "ARS", - "to_symbol": "XCU" - }, - { - "id": "AUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "USD/AUD", - "from_symbol": "USD", - "to_symbol": "AUD" - }, - { - "id": "AUDAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "AUD/AED", - "from_symbol": "AUD", - "to_symbol": "AED" - }, - { - "id": "AUDANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "AUD/ANG", - "from_symbol": "AUD", - "to_symbol": "ANG" - }, - { - "id": "AUDARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "AUD/ARS", - "from_symbol": "AUD", - "to_symbol": "ARS" - }, - { - "id": "AUDAWG=X", - "currency": "AWG", - "exchange": "CCY", - "name": "AUD/AWG", - "from_symbol": "AUD", - "to_symbol": "AWG" - }, - { - "id": "AUDBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "AUD/BAM", - "from_symbol": "AUD", - "to_symbol": "BAM" - }, - { - "id": "AUDBBD=X", - "currency": "BBD", - "exchange": "CCY", - "name": "AUD/BBD", - "from_symbol": "AUD", - "to_symbol": "BBD" - }, - { - "id": "AUDBHD=X", - "currency": "BHD", - "exchange": "CCY", - "name": "AUD/BHD", - "from_symbol": "AUD", - "to_symbol": "BHD" - }, - { - "id": "AUDBMD=X", - "currency": "BMD", - "exchange": "CCY", - "name": "AUD/BMD", - "from_symbol": "AUD", - "to_symbol": "BMD" - }, - { - "id": "AUDBOB=X", - "currency": "BOB", - "exchange": "CCY", - "name": "AUD/BOB", - "from_symbol": "AUD", - "to_symbol": "BOB" - }, - { - "id": "AUDBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "AUD/BRL", - "from_symbol": "AUD", - "to_symbol": "BRL" - }, - { - "id": "AUDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/BRX", - "from_symbol": "AUD", - "to_symbol": "BRX" - }, - { - "id": "AUDBSD=X", - "currency": "BSD", - "exchange": "CCY", - "name": "AUD/BSD", - "from_symbol": "AUD", - "to_symbol": "BSD" - }, - { - "id": "AUDBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "AUD/BWP", - "from_symbol": "AUD", - "to_symbol": "BWP" - }, - { - "id": "AUDBZD=X", - "currency": "BZD", - "exchange": "CCY", - "name": "AUD/BZD", - "from_symbol": "AUD", - "to_symbol": "BZD" - }, - { - "id": "AUDCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "AUD/CAD", - "from_symbol": "AUD", - "to_symbol": "CAD" - }, - { - "id": "AUDCAX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/CAX", - "from_symbol": "AUD", - "to_symbol": "CAX" - }, - { - "id": "AUDCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "AUD/CHF", - "from_symbol": "AUD", - "to_symbol": "CHF" - }, - { - "id": "AUDCLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "AUD/CLP", - "from_symbol": "AUD", - "to_symbol": "CLP" - }, - { - "id": "AUDCNH=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/CNH", - "from_symbol": "AUD", - "to_symbol": "CNH" - }, - { - "id": "AUDCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "AUD/CNY", - "from_symbol": "AUD", - "to_symbol": "CNY" - }, - { - "id": "AUDCOP=X", - "currency": "COP", - "exchange": "CCY", - "name": "AUD/COP", - "from_symbol": "AUD", - "to_symbol": "COP" - }, - { - "id": "AUDCRC=X", - "currency": "CRC", - "exchange": "CCY", - "name": "AUD/CRC", - "from_symbol": "AUD", - "to_symbol": "CRC" - }, - { - "id": "AUDCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "AUD/CZK", - "from_symbol": "AUD", - "to_symbol": "CZK" - }, - { - "id": "AUDCZX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/CZX", - "from_symbol": "AUD", - "to_symbol": "CZX" - }, - { - "id": "AUDDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "AUD/DKK", - "from_symbol": "AUD", - "to_symbol": "DKK" - }, - { - "id": "AUDDKX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/DKX", - "from_symbol": "AUD", - "to_symbol": "DKX" - }, - { - "id": "AUDDOP=X", - "currency": "DOP", - "exchange": "CCY", - "name": "AUD/DOP", - "from_symbol": "AUD", - "to_symbol": "DOP" - }, - { - "id": "AUDEGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "AUD/EGP", - "from_symbol": "AUD", - "to_symbol": "EGP" - }, - { - "id": "AUDERN=X", - "currency": "ERN", - "exchange": "CCY", - "name": "AUD/ERN", - "from_symbol": "AUD", - "to_symbol": "ERN" - }, - { - "id": "AUDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "AUD/EUR", - "from_symbol": "AUD", - "to_symbol": "EUR" - }, - { - "id": "AUDFJD=X", - "currency": "FJD", - "exchange": "CCY", - "name": "AUD/FJD", - "from_symbol": "AUD", - "to_symbol": "FJD" - }, - { - "id": "AUDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "AUD/GBP", - "from_symbol": "AUD", - "to_symbol": "GBP" - }, - { - "id": "AUDGHS=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/GHS", - "from_symbol": "AUD", - "to_symbol": "GHS" - }, - { - "id": "AUDGIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "AUD/GIP", - "from_symbol": "AUD", - "to_symbol": "GIP" - }, - { - "id": "AUDGTQ=X", - "currency": "GTQ", - "exchange": "CCY", - "name": "AUD/GTQ", - "from_symbol": "AUD", - "to_symbol": "GTQ" - }, - { - "id": "AUDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "AUD/HKD", - "from_symbol": "AUD", - "to_symbol": "HKD" - }, - { - "id": "AUDHNL=X", - "currency": "HNL", - "exchange": "CCY", - "name": "AUD/HNL", - "from_symbol": "AUD", - "to_symbol": "HNL" - }, - { - "id": "AUDHRK=X", - "currency": "HRK", - "exchange": "CCY", - "name": "AUD/HRK", - "from_symbol": "AUD", - "to_symbol": "HRK" - }, - { - "id": "AUDHRX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/HRX", - "from_symbol": "AUD", - "to_symbol": "HRX" - }, - { - "id": "AUDHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "AUD/HUF", - "from_symbol": "AUD", - "to_symbol": "HUF" - }, - { - "id": "AUDHUX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/HUX", - "from_symbol": "AUD", - "to_symbol": "HUX" - }, - { - "id": "AUDIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "AUD/IDR", - "from_symbol": "AUD", - "to_symbol": "IDR" - }, - { - "id": "AUDILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "AUD/ILS", - "from_symbol": "AUD", - "to_symbol": "ILS" - }, - { - "id": "AUDINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "AUD/INR", - "from_symbol": "AUD", - "to_symbol": "INR" - }, - { - "id": "AUDISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "AUD/ISK", - "from_symbol": "AUD", - "to_symbol": "ISK" - }, - { - "id": "AUDJMD=X", - "currency": "JMD", - "exchange": "CCY", - "name": "AUD/JMD", - "from_symbol": "AUD", - "to_symbol": "JMD" - }, - { - "id": "AUDJOD=X", - "currency": "JOD", - "exchange": "CCY", - "name": "AUD/JOD", - "from_symbol": "AUD", - "to_symbol": "JOD" - }, - { - "id": "AUDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "AUD/JPY", - "from_symbol": "AUD", - "to_symbol": "JPY" - }, - { - "id": "AUDKES=X", - "currency": "KES", - "exchange": "CCY", - "name": "AUD/KES", - "from_symbol": "AUD", - "to_symbol": "KES" - }, - { - "id": "AUDKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "AUD/KRW", - "from_symbol": "AUD", - "to_symbol": "KRW" - }, - { - "id": "AUDKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "AUD/KWD", - "from_symbol": "AUD", - "to_symbol": "KWD" - }, - { - "id": "AUDKYD=X", - "currency": "KYD", - "exchange": "CCY", - "name": "AUD/KYD", - "from_symbol": "AUD", - "to_symbol": "KYD" - }, - { - "id": "AUDLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "AUD/LKR", - "from_symbol": "AUD", - "to_symbol": "LKR" - }, - { - "id": "AUDMAD=X", - "currency": "MAD", - "exchange": "CCY", - "name": "AUD/MAD", - "from_symbol": "AUD", - "to_symbol": "MAD" - }, - { - "id": "AUDMGA=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/MGA", - "from_symbol": "AUD", - "to_symbol": "MGA" - }, - { - "id": "AUDMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "AUD/MXN", - "from_symbol": "AUD", - "to_symbol": "MXN" - }, - { - "id": "AUDMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "AUD/MYR", - "from_symbol": "AUD", - "to_symbol": "MYR" - }, - { - "id": "AUDMYX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/MYX", - "from_symbol": "AUD", - "to_symbol": "MYX" - }, - { - "id": "AUDNGN=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/NGN", - "from_symbol": "AUD", - "to_symbol": "NGN" - }, - { - "id": "AUDNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "AUD/NOK", - "from_symbol": "AUD", - "to_symbol": "NOK" - }, - { - "id": "AUDNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "AUD/NZD", - "from_symbol": "AUD", - "to_symbol": "NZD" - }, - { - "id": "AUDOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "AUD/OMR", - "from_symbol": "AUD", - "to_symbol": "OMR" - }, - { - "id": "AUDPEN=X", - "currency": "PEN", - "exchange": "CCY", - "name": "AUD/PEN", - "from_symbol": "AUD", - "to_symbol": "PEN" - }, - { - "id": "AUDPGK=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/PGK", - "from_symbol": "AUD", - "to_symbol": "PGK" - }, - { - "id": "AUDPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "AUD/PHP", - "from_symbol": "AUD", - "to_symbol": "PHP" - }, - { - "id": "AUDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "AUD/PKR", - "from_symbol": "AUD", - "to_symbol": "PKR" - }, - { - "id": "AUDPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "AUD/PLN", - "from_symbol": "AUD", - "to_symbol": "PLN" - }, - { - "id": "AUDPLX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/PLX", - "from_symbol": "AUD", - "to_symbol": "PLX" - }, - { - "id": "AUDPYG=X", - "currency": "PYG", - "exchange": "CCY", - "name": "AUD/PYG", - "from_symbol": "AUD", - "to_symbol": "PYG" - }, - { - "id": "AUDQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "AUD/QAR", - "from_symbol": "AUD", - "to_symbol": "QAR" - }, - { - "id": "AUDRSD=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/RSD", - "from_symbol": "AUD", - "to_symbol": "RSD" - }, - { - "id": "AUDRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "AUD/RUB", - "from_symbol": "AUD", - "to_symbol": "RUB" - }, - { - "id": "AUDRUX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/RUX", - "from_symbol": "AUD", - "to_symbol": "RUX" - }, - { - "id": "AUDSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "AUD/SAR", - "from_symbol": "AUD", - "to_symbol": "SAR" - }, - { - "id": "AUDSCR=X", - "currency": "SCR", - "exchange": "CCY", - "name": "AUD/SCR", - "from_symbol": "AUD", - "to_symbol": "SCR" - }, - { - "id": "AUDSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "AUD/SEK", - "from_symbol": "AUD", - "to_symbol": "SEK" - }, - { - "id": "AUDSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "AUD/SGD", - "from_symbol": "AUD", - "to_symbol": "SGD" - }, - { - "id": "AUDSVC=X", - "currency": "SVC", - "exchange": "CCY", - "name": "AUD/SVC", - "from_symbol": "AUD", - "to_symbol": "SVC" - }, - { - "id": "AUDSYP=X", - "currency": "SYP", - "exchange": "CCY", - "name": "AUD/SYP", - "from_symbol": "AUD", - "to_symbol": "SYP" - }, - { - "id": "AUDTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "AUD/THB", - "from_symbol": "AUD", - "to_symbol": "THB" - }, - { - "id": "AUDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/THX", - "from_symbol": "AUD", - "to_symbol": "THX" - }, - { - "id": "AUDTJS=X", - "currency": "TJS", - "exchange": "CCY", - "name": "AUD/TJS", - "from_symbol": "AUD", - "to_symbol": "TJS" - }, - { - "id": "AUDTMT=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/TMT", - "from_symbol": "AUD", - "to_symbol": "TMT" - }, - { - "id": "AUDTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "AUD/TND", - "from_symbol": "AUD", - "to_symbol": "TND" - }, - { - "id": "AUDTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "AUD/TRY", - "from_symbol": "AUD", - "to_symbol": "TRY" - }, - { - "id": "AUDTTD=X", - "currency": "TTD", - "exchange": "CCY", - "name": "AUD/TTD", - "from_symbol": "AUD", - "to_symbol": "TTD" - }, - { - "id": "AUDTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "AUD/TWD", - "from_symbol": "AUD", - "to_symbol": "TWD" - }, - { - "id": "AUDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "AUD/USD", - "from_symbol": "AUD", - "to_symbol": "USD" - }, - { - "id": "AUDUYU=X", - "currency": "UYU", - "exchange": "CCY", - "name": "AUD/UYU", - "from_symbol": "AUD", - "to_symbol": "UYU" - }, - { - "id": "AUDVES=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/VES", - "from_symbol": "AUD", - "to_symbol": "VES" - }, - { - "id": "AUDXCD=X", - "currency": "XCD", - "exchange": "CCY", - "name": "AUD/XCD", - "from_symbol": "AUD", - "to_symbol": "XCD" - }, - { - "id": "AUDXCU=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/XCU", - "from_symbol": "AUD", - "to_symbol": "XCU" - }, - { - "id": "AUDXDR=X", - "currency": null, - "exchange": "CCY", - "name": "AUD/XDR", - "from_symbol": "AUD", - "to_symbol": "XDR" - }, - { - "id": "AUDXPF=X", - "currency": "XPF", - "exchange": "CCY", - "name": "AUD/XPF", - "from_symbol": "AUD", - "to_symbol": "XPF" - }, - { - "id": "AUDZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "AUD/ZAR", - "from_symbol": "AUD", - "to_symbol": "ZAR" - }, - { - "id": "AUXCAD=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/CAD", - "from_symbol": "AUX", - "to_symbol": "CAD" - }, - { - "id": "AUXCHF=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/CHF", - "from_symbol": "AUX", - "to_symbol": "CHF" - }, - { - "id": "AUXCNY=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/CNY", - "from_symbol": "AUX", - "to_symbol": "CNY" - }, - { - "id": "AUXEUR=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/EUR", - "from_symbol": "AUX", - "to_symbol": "EUR" - }, - { - "id": "AUXHKD=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/HKD", - "from_symbol": "AUX", - "to_symbol": "HKD" - }, - { - "id": "AUXIDR=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/IDR", - "from_symbol": "AUX", - "to_symbol": "IDR" - }, - { - "id": "AUXINR=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/INR", - "from_symbol": "AUX", - "to_symbol": "INR" - }, - { - "id": "AUXJPY=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/JPY", - "from_symbol": "AUX", - "to_symbol": "JPY" - }, - { - "id": "AUXKRW=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/KRW", - "from_symbol": "AUX", - "to_symbol": "KRW" - }, - { - "id": "AUXMYR=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/MYR", - "from_symbol": "AUX", - "to_symbol": "MYR" - }, - { - "id": "AUXNZD=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/NZD", - "from_symbol": "AUX", - "to_symbol": "NZD" - }, - { - "id": "AUXPGK=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/PGK", - "from_symbol": "AUX", - "to_symbol": "PGK" - }, - { - "id": "AUXSGD=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/SGD", - "from_symbol": "AUX", - "to_symbol": "SGD" - }, - { - "id": "AUXTHB=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/THB", - "from_symbol": "AUX", - "to_symbol": "THB" - }, - { - "id": "AUXTWD=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/TWD", - "from_symbol": "AUX", - "to_symbol": "TWD" - }, - { - "id": "AUXTWI=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/TWI", - "from_symbol": "AUX", - "to_symbol": "TWI" - }, - { - "id": "AUXVND=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/VND", - "from_symbol": "AUX", - "to_symbol": "VND" - }, - { - "id": "AUXXDR=X", - "currency": null, - "exchange": "CCY", - "name": "AUX/XDR", - "from_symbol": "AUX", - "to_symbol": "XDR" - }, - { - "id": "AWG=X", - "currency": "AWG", - "exchange": "CCY", - "name": "USD/AWG", - "from_symbol": "USD", - "to_symbol": "AWG" - }, - { - "id": "AWGBRX=X", - "currency": null, - "exchange": "CCY", - "name": "AWG/BRX", - "from_symbol": "AWG", - "to_symbol": "BRX" - }, - { - "id": "AWGUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "AWG/USD", - "from_symbol": "AWG", - "to_symbol": "USD" - }, - { - "id": "AZN=X", - "currency": "AZN", - "exchange": "CCY", - "name": "USD/AZN", - "from_symbol": "USD", - "to_symbol": "AZN" - }, - { - "id": "AZNRUX=X", - "currency": null, - "exchange": "CCY", - "name": "AZN/RUX", - "from_symbol": "AZN", - "to_symbol": "RUX" - }, - { - "id": "BAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "USD/BAM", - "from_symbol": "USD", - "to_symbol": "BAM" - }, - { - "id": "BAMHRX=X", - "currency": null, - "exchange": "CCY", - "name": "BAM/HRX", - "from_symbol": "BAM", - "to_symbol": "HRX" - }, - { - "id": "BAMRSD=X", - "currency": null, - "exchange": "CCY", - "name": "BAM/RSD", - "from_symbol": "BAM", - "to_symbol": "RSD" - }, - { - "id": "BBD=X", - "currency": "BBD", - "exchange": "CCY", - "name": "USD/BBD", - "from_symbol": "USD", - "to_symbol": "BBD" - }, - { - "id": "BBDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BBD/BRX", - "from_symbol": "BBD", - "to_symbol": "BRX" - }, - { - "id": "BBDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "BBD/GBP", - "from_symbol": "BBD", - "to_symbol": "GBP" - }, - { - "id": "BBDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BBD/USD", - "from_symbol": "BBD", - "to_symbol": "USD" - }, - { - "id": "BDT=X", - "currency": "BDT", - "exchange": "CCY", - "name": "USD/BDT", - "from_symbol": "USD", - "to_symbol": "BDT" - }, - { - "id": "BDTBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BDT/BRX", - "from_symbol": "BDT", - "to_symbol": "BRX" - }, - { - "id": "BDTCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "BDT/CAD", - "from_symbol": "BDT", - "to_symbol": "CAD" - }, - { - "id": "BDTCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "BDT/CHF", - "from_symbol": "BDT", - "to_symbol": "CHF" - }, - { - "id": "BDTEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "BDT/EUR", - "from_symbol": "BDT", - "to_symbol": "EUR" - }, - { - "id": "BDTGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "BDT/GBP", - "from_symbol": "BDT", - "to_symbol": "GBP" - }, - { - "id": "BDTHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "BDT/HKD", - "from_symbol": "BDT", - "to_symbol": "HKD" - }, - { - "id": "BDTJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "BDT/JPY", - "from_symbol": "BDT", - "to_symbol": "JPY" - }, - { - "id": "BDTSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "BDT/SGD", - "from_symbol": "BDT", - "to_symbol": "SGD" - }, - { - "id": "BDTTHX=X", - "currency": null, - "exchange": "CCY", - "name": "BDT/THX", - "from_symbol": "BDT", - "to_symbol": "THX" - }, - { - "id": "BDTUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BDT/USD", - "from_symbol": "BDT", - "to_symbol": "USD" - }, - { - "id": "BGN=X", - "currency": "BGN", - "exchange": "CCY", - "name": "USD/BGN", - "from_symbol": "USD", - "to_symbol": "BGN" - }, - { - "id": "BGNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BGN/BRX", - "from_symbol": "BGN", - "to_symbol": "BRX" - }, - { - "id": "BGNCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "BGN/CHF", - "from_symbol": "BGN", - "to_symbol": "CHF" - }, - { - "id": "BGNCZX=X", - "currency": null, - "exchange": "CCY", - "name": "BGN/CZX", - "from_symbol": "BGN", - "to_symbol": "CZX" - }, - { - "id": "BGNDKX=X", - "currency": null, - "exchange": "CCY", - "name": "BGN/DKX", - "from_symbol": "BGN", - "to_symbol": "DKX" - }, - { - "id": "BGNEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "BGN/EUR", - "from_symbol": "BGN", - "to_symbol": "EUR" - }, - { - "id": "BGNGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "BGN/GBP", - "from_symbol": "BGN", - "to_symbol": "GBP" - }, - { - "id": "BGNHUX=X", - "currency": null, - "exchange": "CCY", - "name": "BGN/HUX", - "from_symbol": "BGN", - "to_symbol": "HUX" - }, - { - "id": "BGNPLX=X", - "currency": null, - "exchange": "CCY", - "name": "BGN/PLX", - "from_symbol": "BGN", - "to_symbol": "PLX" - }, - { - "id": "BGNRSD=X", - "currency": null, - "exchange": "CCY", - "name": "BGN/RSD", - "from_symbol": "BGN", - "to_symbol": "RSD" - }, - { - "id": "BGNRUX=X", - "currency": null, - "exchange": "CCY", - "name": "BGN/RUX", - "from_symbol": "BGN", - "to_symbol": "RUX" - }, - { - "id": "BGNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BGN/USD", - "from_symbol": "BGN", - "to_symbol": "USD" - }, - { - "id": "BHD=X", - "currency": "BHD", - "exchange": "CCY", - "name": "USD/BHD", - "from_symbol": "USD", - "to_symbol": "BHD" - }, - { - "id": "BHDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BHD/BRX", - "from_symbol": "BHD", - "to_symbol": "BRX" - }, - { - "id": "BHDCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "BHD/CHF", - "from_symbol": "BHD", - "to_symbol": "CHF" - }, - { - "id": "BHDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "BHD/EUR", - "from_symbol": "BHD", - "to_symbol": "EUR" - }, - { - "id": "BHDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "BHD/GBP", - "from_symbol": "BHD", - "to_symbol": "GBP" - }, - { - "id": "BHDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "BHD/HKD", - "from_symbol": "BHD", - "to_symbol": "HKD" - }, - { - "id": "BHDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "BHD/JPY", - "from_symbol": "BHD", - "to_symbol": "JPY" - }, - { - "id": "BHDNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "BHD/NZD", - "from_symbol": "BHD", - "to_symbol": "NZD" - }, - { - "id": "BHDOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "BHD/OMR", - "from_symbol": "BHD", - "to_symbol": "OMR" - }, - { - "id": "BHDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "BHD/PKR", - "from_symbol": "BHD", - "to_symbol": "PKR" - }, - { - "id": "BHDQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "BHD/QAR", - "from_symbol": "BHD", - "to_symbol": "QAR" - }, - { - "id": "BHDSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "BHD/SGD", - "from_symbol": "BHD", - "to_symbol": "SGD" - }, - { - "id": "BHDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "BHD/THX", - "from_symbol": "BHD", - "to_symbol": "THX" - }, - { - "id": "BHDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BHD/USD", - "from_symbol": "BHD", - "to_symbol": "USD" - }, - { - "id": "BIF=X", - "currency": "BIF", - "exchange": "CCY", - "name": "USD/BIF", - "from_symbol": "USD", - "to_symbol": "BIF" - }, - { - "id": "BIFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BIF/BRX", - "from_symbol": "BIF", - "to_symbol": "BRX" - }, - { - "id": "BIFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BIF/USD", - "from_symbol": "BIF", - "to_symbol": "USD" - }, - { - "id": "BMD=X", - "currency": "BMD", - "exchange": "CCY", - "name": "USD/BMD", - "from_symbol": "USD", - "to_symbol": "BMD" - }, - { - "id": "BMDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BMD/BRX", - "from_symbol": "BMD", - "to_symbol": "BRX" - }, - { - "id": "BMDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "BMD/EUR", - "from_symbol": "BMD", - "to_symbol": "EUR" - }, - { - "id": "BMDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "BMD/GBP", - "from_symbol": "BMD", - "to_symbol": "GBP" - }, - { - "id": "BMDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BMD/USD", - "from_symbol": "BMD", - "to_symbol": "USD" - }, - { - "id": "BND=X", - "currency": "BND", - "exchange": "CCY", - "name": "USD/BND", - "from_symbol": "USD", - "to_symbol": "BND" - }, - { - "id": "BNDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BND/BRX", - "from_symbol": "BND", - "to_symbol": "BRX" - }, - { - "id": "BNDMYX=X", - "currency": null, - "exchange": "CCY", - "name": "BND/MYX", - "from_symbol": "BND", - "to_symbol": "MYX" - }, - { - "id": "BNDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "BND/THX", - "from_symbol": "BND", - "to_symbol": "THX" - }, - { - "id": "BNDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BND/USD", - "from_symbol": "BND", - "to_symbol": "USD" - }, - { - "id": "BNDXDR=X", - "currency": null, - "exchange": "CCY", - "name": "BND/XDR", - "from_symbol": "BND", - "to_symbol": "XDR" - }, - { - "id": "BOB=X", - "currency": "BOB", - "exchange": "CCY", - "name": "USD/BOB", - "from_symbol": "USD", - "to_symbol": "BOB" - }, - { - "id": "BOBBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "BOB/BRL", - "from_symbol": "BOB", - "to_symbol": "BRL" - }, - { - "id": "BOBBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BOB/BRX", - "from_symbol": "BOB", - "to_symbol": "BRX" - }, - { - "id": "BOBUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BOB/USD", - "from_symbol": "BOB", - "to_symbol": "USD" - }, - { - "id": "BRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "USD/BRL", - "from_symbol": "USD", - "to_symbol": "BRL" - }, - { - "id": "BRLARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "BRL/ARS", - "from_symbol": "BRL", - "to_symbol": "ARS" - }, - { - "id": "BRLAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "BRL/AUD", - "from_symbol": "BRL", - "to_symbol": "AUD" - }, - { - "id": "BRLBOB=X", - "currency": "BOB", - "exchange": "CCY", - "name": "BRL/BOB", - "from_symbol": "BRL", - "to_symbol": "BOB" - }, - { - "id": "BRLCAX=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/CAX", - "from_symbol": "BRL", - "to_symbol": "CAX" - }, - { - "id": "BRLCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "BRL/CHF", - "from_symbol": "BRL", - "to_symbol": "CHF" - }, - { - "id": "BRLCLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "BRL/CLP", - "from_symbol": "BRL", - "to_symbol": "CLP" - }, - { - "id": "BRLCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "BRL/CNY", - "from_symbol": "BRL", - "to_symbol": "CNY" - }, - { - "id": "BRLCOP=X", - "currency": "COP", - "exchange": "CCY", - "name": "BRL/COP", - "from_symbol": "BRL", - "to_symbol": "COP" - }, - { - "id": "BRLCZX=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/CZX", - "from_symbol": "BRL", - "to_symbol": "CZX" - }, - { - "id": "BRLDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "BRL/DKK", - "from_symbol": "BRL", - "to_symbol": "DKK" - }, - { - "id": "BRLDKX=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/DKX", - "from_symbol": "BRL", - "to_symbol": "DKX" - }, - { - "id": "BRLEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "BRL/EUR", - "from_symbol": "BRL", - "to_symbol": "EUR" - }, - { - "id": "BRLGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "BRL/GBP", - "from_symbol": "BRL", - "to_symbol": "GBP" - }, - { - "id": "BRLHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "BRL/HKD", - "from_symbol": "BRL", - "to_symbol": "HKD" - }, - { - "id": "BRLHUX=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/HUX", - "from_symbol": "BRL", - "to_symbol": "HUX" - }, - { - "id": "BRLIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "BRL/IDR", - "from_symbol": "BRL", - "to_symbol": "IDR" - }, - { - "id": "BRLILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "BRL/ILS", - "from_symbol": "BRL", - "to_symbol": "ILS" - }, - { - "id": "BRLINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "BRL/INR", - "from_symbol": "BRL", - "to_symbol": "INR" - }, - { - "id": "BRLJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "BRL/JPY", - "from_symbol": "BRL", - "to_symbol": "JPY" - }, - { - "id": "BRLKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "BRL/KRW", - "from_symbol": "BRL", - "to_symbol": "KRW" - }, - { - "id": "BRLMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "BRL/MXN", - "from_symbol": "BRL", - "to_symbol": "MXN" - }, - { - "id": "BRLMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "BRL/MYR", - "from_symbol": "BRL", - "to_symbol": "MYR" - }, - { - "id": "BRLNGN=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/NGN", - "from_symbol": "BRL", - "to_symbol": "NGN" - }, - { - "id": "BRLNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "BRL/NOK", - "from_symbol": "BRL", - "to_symbol": "NOK" - }, - { - "id": "BRLNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "BRL/NZD", - "from_symbol": "BRL", - "to_symbol": "NZD" - }, - { - "id": "BRLPLX=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/PLX", - "from_symbol": "BRL", - "to_symbol": "PLX" - }, - { - "id": "BRLRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "BRL/RUB", - "from_symbol": "BRL", - "to_symbol": "RUB" - }, - { - "id": "BRLRUX=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/RUX", - "from_symbol": "BRL", - "to_symbol": "RUX" - }, - { - "id": "BRLSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "BRL/SEK", - "from_symbol": "BRL", - "to_symbol": "SEK" - }, - { - "id": "BRLSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "BRL/SGD", - "from_symbol": "BRL", - "to_symbol": "SGD" - }, - { - "id": "BRLTWD=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/TWD", - "from_symbol": "BRL", - "to_symbol": "TWD" - }, - { - "id": "BRLUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BRL/USD", - "from_symbol": "BRL", - "to_symbol": "USD" - }, - { - "id": "BRLVES=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/VES", - "from_symbol": "BRL", - "to_symbol": "VES" - }, - { - "id": "BRLXCU=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/XCU", - "from_symbol": "BRL", - "to_symbol": "XCU" - }, - { - "id": "BRLXDR=X", - "currency": null, - "exchange": "CCY", - "name": "BRL/XDR", - "from_symbol": "BRL", - "to_symbol": "XDR" - }, - { - "id": "BRLZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "BRL/ZAR", - "from_symbol": "BRL", - "to_symbol": "ZAR" - }, - { - "id": "BRX=X", - "currency": "BRX", - "exchange": "CCY", - "name": "USD/BRX", - "from_symbol": "USD", - "to_symbol": "BRX" - }, - { - "id": "BSD=X", - "currency": "BSD", - "exchange": "CCY", - "name": "USD/BSD", - "from_symbol": "USD", - "to_symbol": "BSD" - }, - { - "id": "BSDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BSD/BRX", - "from_symbol": "BSD", - "to_symbol": "BRX" - }, - { - "id": "BSDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BSD/USD", - "from_symbol": "BSD", - "to_symbol": "USD" - }, - { - "id": "BTN=X", - "currency": "BTN", - "exchange": "CCY", - "name": "USD/BTN", - "from_symbol": "USD", - "to_symbol": "BTN" - }, - { - "id": "BTNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BTN/BRX", - "from_symbol": "BTN", - "to_symbol": "BRX" - }, - { - "id": "BTNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BTN/USD", - "from_symbol": "BTN", - "to_symbol": "USD" - }, - { - "id": "BWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "USD/BWP", - "from_symbol": "USD", - "to_symbol": "BWP" - }, - { - "id": "BWPAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "BWP/AUD", - "from_symbol": "BWP", - "to_symbol": "AUD" - }, - { - "id": "BWPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BWP/BRX", - "from_symbol": "BWP", - "to_symbol": "BRX" - }, - { - "id": "BWPCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "BWP/CAD", - "from_symbol": "BWP", - "to_symbol": "CAD" - }, - { - "id": "BWPCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "BWP/CNY", - "from_symbol": "BWP", - "to_symbol": "CNY" - }, - { - "id": "BWPEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "BWP/EUR", - "from_symbol": "BWP", - "to_symbol": "EUR" - }, - { - "id": "BWPGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "BWP/GBP", - "from_symbol": "BWP", - "to_symbol": "GBP" - }, - { - "id": "BWPIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "BWP/IDR", - "from_symbol": "BWP", - "to_symbol": "IDR" - }, - { - "id": "BWPINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "BWP/INR", - "from_symbol": "BWP", - "to_symbol": "INR" - }, - { - "id": "BWPJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "BWP/JPY", - "from_symbol": "BWP", - "to_symbol": "JPY" - }, - { - "id": "BWPKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "BWP/KRW", - "from_symbol": "BWP", - "to_symbol": "KRW" - }, - { - "id": "BWPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BWP/USD", - "from_symbol": "BWP", - "to_symbol": "USD" - }, - { - "id": "BWPXDR=X", - "currency": null, - "exchange": "CCY", - "name": "BWP/XDR", - "from_symbol": "BWP", - "to_symbol": "XDR" - }, - { - "id": "BWPZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "BWP/ZAR", - "from_symbol": "BWP", - "to_symbol": "ZAR" - }, - { - "id": "BYN=X", - "currency": "BYN", - "exchange": "CCY", - "name": "USD/BYN", - "from_symbol": "USD", - "to_symbol": "BYN" - }, - { - "id": "BYNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BYN/BRX", - "from_symbol": "BYN", - "to_symbol": "BRX" - }, - { - "id": "BYNRUX=X", - "currency": null, - "exchange": "CCY", - "name": "BYN/RUX", - "from_symbol": "BYN", - "to_symbol": "RUX" - }, - { - "id": "BZD=X", - "currency": "BZD", - "exchange": "CCY", - "name": "USD/BZD", - "from_symbol": "USD", - "to_symbol": "BZD" - }, - { - "id": "BZDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "BZD/BRX", - "from_symbol": "BZD", - "to_symbol": "BRX" - }, - { - "id": "BZDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "BZD/USD", - "from_symbol": "BZD", - "to_symbol": "USD" - }, - { - "id": "CAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "USD/CAD", - "from_symbol": "USD", - "to_symbol": "CAD" - }, - { - "id": "CADAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "CAD/AED", - "from_symbol": "CAD", - "to_symbol": "AED" - }, - { - "id": "CADAFN=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/AFN", - "from_symbol": "CAD", - "to_symbol": "AFN" - }, - { - "id": "CADALL=X", - "currency": "ALL", - "exchange": "CCY", - "name": "CAD/ALL", - "from_symbol": "CAD", - "to_symbol": "ALL" - }, - { - "id": "CADAMD=X", - "currency": "AMD", - "exchange": "CCY", - "name": "CAD/AMD", - "from_symbol": "CAD", - "to_symbol": "AMD" - }, - { - "id": "CADANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "CAD/ANG", - "from_symbol": "CAD", - "to_symbol": "ANG" - }, - { - "id": "CADAOA=X", - "currency": "AOA", - "exchange": "CCY", - "name": "CAD/AOA", - "from_symbol": "CAD", - "to_symbol": "AOA" - }, - { - "id": "CADARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "CAD/ARS", - "from_symbol": "CAD", - "to_symbol": "ARS" - }, - { - "id": "CADAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "CAD/AUD", - "from_symbol": "CAD", - "to_symbol": "AUD" - }, - { - "id": "CADAWG=X", - "currency": "AWG", - "exchange": "CCY", - "name": "CAD/AWG", - "from_symbol": "CAD", - "to_symbol": "AWG" - }, - { - "id": "CADAZN=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/AZN", - "from_symbol": "CAD", - "to_symbol": "AZN" - }, - { - "id": "CADBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "CAD/BAM", - "from_symbol": "CAD", - "to_symbol": "BAM" - }, - { - "id": "CADBBD=X", - "currency": "BBD", - "exchange": "CCY", - "name": "CAD/BBD", - "from_symbol": "CAD", - "to_symbol": "BBD" - }, - { - "id": "CADBDT=X", - "currency": "BDT", - "exchange": "CCY", - "name": "CAD/BDT", - "from_symbol": "CAD", - "to_symbol": "BDT" - }, - { - "id": "CADBGN=X", - "currency": "BGN", - "exchange": "CCY", - "name": "CAD/BGN", - "from_symbol": "CAD", - "to_symbol": "BGN" - }, - { - "id": "CADBHD=X", - "currency": "BHD", - "exchange": "CCY", - "name": "CAD/BHD", - "from_symbol": "CAD", - "to_symbol": "BHD" - }, - { - "id": "CADBIF=X", - "currency": "BIF", - "exchange": "CCY", - "name": "CAD/BIF", - "from_symbol": "CAD", - "to_symbol": "BIF" - }, - { - "id": "CADBMD=X", - "currency": "BMD", - "exchange": "CCY", - "name": "CAD/BMD", - "from_symbol": "CAD", - "to_symbol": "BMD" - }, - { - "id": "CADBND=X", - "currency": "BND", - "exchange": "CCY", - "name": "CAD/BND", - "from_symbol": "CAD", - "to_symbol": "BND" - }, - { - "id": "CADBOB=X", - "currency": "BOB", - "exchange": "CCY", - "name": "CAD/BOB", - "from_symbol": "CAD", - "to_symbol": "BOB" - }, - { - "id": "CADBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "CAD/BRL", - "from_symbol": "CAD", - "to_symbol": "BRL" - }, - { - "id": "CADBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/BRX", - "from_symbol": "CAD", - "to_symbol": "BRX" - }, - { - "id": "CADBSD=X", - "currency": "BSD", - "exchange": "CCY", - "name": "CAD/BSD", - "from_symbol": "CAD", - "to_symbol": "BSD" - }, - { - "id": "CADBTN=X", - "currency": "BTN", - "exchange": "CCY", - "name": "CAD/BTN", - "from_symbol": "CAD", - "to_symbol": "BTN" - }, - { - "id": "CADBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "CAD/BWP", - "from_symbol": "CAD", - "to_symbol": "BWP" - }, - { - "id": "CADBYN=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/BYN", - "from_symbol": "CAD", - "to_symbol": "BYN" - }, - { - "id": "CADBZD=X", - "currency": "BZD", - "exchange": "CCY", - "name": "CAD/BZD", - "from_symbol": "CAD", - "to_symbol": "BZD" - }, - { - "id": "CADCDF=X", - "currency": "CDF", - "exchange": "CCY", - "name": "CAD/CDF", - "from_symbol": "CAD", - "to_symbol": "CDF" - }, - { - "id": "CADCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "CAD/CHF", - "from_symbol": "CAD", - "to_symbol": "CHF" - }, - { - "id": "CADCLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "CAD/CLP", - "from_symbol": "CAD", - "to_symbol": "CLP" - }, - { - "id": "CADCNH=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/CNH", - "from_symbol": "CAD", - "to_symbol": "CNH" - }, - { - "id": "CADCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "CAD/CNY", - "from_symbol": "CAD", - "to_symbol": "CNY" - }, - { - "id": "CADCOP=X", - "currency": "COP", - "exchange": "CCY", - "name": "CAD/COP", - "from_symbol": "CAD", - "to_symbol": "COP" - }, - { - "id": "CADCRC=X", - "currency": "CRC", - "exchange": "CCY", - "name": "CAD/CRC", - "from_symbol": "CAD", - "to_symbol": "CRC" - }, - { - "id": "CADCUP=X", - "currency": "CUP", - "exchange": "CCY", - "name": "CAD/CUP", - "from_symbol": "CAD", - "to_symbol": "CUP" - }, - { - "id": "CADCVE=X", - "currency": "CVE", - "exchange": "CCY", - "name": "CAD/CVE", - "from_symbol": "CAD", - "to_symbol": "CVE" - }, - { - "id": "CADCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "CAD/CZK", - "from_symbol": "CAD", - "to_symbol": "CZK" - }, - { - "id": "CADCZX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/CZX", - "from_symbol": "CAD", - "to_symbol": "CZX" - }, - { - "id": "CADDJF=X", - "currency": "DJF", - "exchange": "CCY", - "name": "CAD/DJF", - "from_symbol": "CAD", - "to_symbol": "DJF" - }, - { - "id": "CADDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "CAD/DKK", - "from_symbol": "CAD", - "to_symbol": "DKK" - }, - { - "id": "CADDKX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/DKX", - "from_symbol": "CAD", - "to_symbol": "DKX" - }, - { - "id": "CADDOP=X", - "currency": "DOP", - "exchange": "CCY", - "name": "CAD/DOP", - "from_symbol": "CAD", - "to_symbol": "DOP" - }, - { - "id": "CADDZD=X", - "currency": "DZD", - "exchange": "CCY", - "name": "CAD/DZD", - "from_symbol": "CAD", - "to_symbol": "DZD" - }, - { - "id": "CADEGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "CAD/EGP", - "from_symbol": "CAD", - "to_symbol": "EGP" - }, - { - "id": "CADERN=X", - "currency": "ERN", - "exchange": "CCY", - "name": "CAD/ERN", - "from_symbol": "CAD", - "to_symbol": "ERN" - }, - { - "id": "CADETB=X", - "currency": "ETB", - "exchange": "CCY", - "name": "CAD/ETB", - "from_symbol": "CAD", - "to_symbol": "ETB" - }, - { - "id": "CADEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "CAD/EUR", - "from_symbol": "CAD", - "to_symbol": "EUR" - }, - { - "id": "CADFJD=X", - "currency": "FJD", - "exchange": "CCY", - "name": "CAD/FJD", - "from_symbol": "CAD", - "to_symbol": "FJD" - }, - { - "id": "CADFKP=X", - "currency": "FKP", - "exchange": "CCY", - "name": "CAD/FKP", - "from_symbol": "CAD", - "to_symbol": "FKP" - }, - { - "id": "CADGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "CAD/GBP", - "from_symbol": "CAD", - "to_symbol": "GBP" - }, - { - "id": "CADGEL=X", - "currency": "GEL", - "exchange": "CCY", - "name": "CAD/GEL", - "from_symbol": "CAD", - "to_symbol": "GEL" - }, - { - "id": "CADGHS=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/GHS", - "from_symbol": "CAD", - "to_symbol": "GHS" - }, - { - "id": "CADGIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "CAD/GIP", - "from_symbol": "CAD", - "to_symbol": "GIP" - }, - { - "id": "CADGMD=X", - "currency": "GMD", - "exchange": "CCY", - "name": "CAD/GMD", - "from_symbol": "CAD", - "to_symbol": "GMD" - }, - { - "id": "CADGNF=X", - "currency": "GNF", - "exchange": "CCY", - "name": "CAD/GNF", - "from_symbol": "CAD", - "to_symbol": "GNF" - }, - { - "id": "CADGTQ=X", - "currency": "GTQ", - "exchange": "CCY", - "name": "CAD/GTQ", - "from_symbol": "CAD", - "to_symbol": "GTQ" - }, - { - "id": "CADGYD=X", - "currency": "GYD", - "exchange": "CCY", - "name": "CAD/GYD", - "from_symbol": "CAD", - "to_symbol": "GYD" - }, - { - "id": "CADHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "CAD/HKD", - "from_symbol": "CAD", - "to_symbol": "HKD" - }, - { - "id": "CADHNL=X", - "currency": "HNL", - "exchange": "CCY", - "name": "CAD/HNL", - "from_symbol": "CAD", - "to_symbol": "HNL" - }, - { - "id": "CADHRK=X", - "currency": "HRK", - "exchange": "CCY", - "name": "CAD/HRK", - "from_symbol": "CAD", - "to_symbol": "HRK" - }, - { - "id": "CADHRX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/HRX", - "from_symbol": "CAD", - "to_symbol": "HRX" - }, - { - "id": "CADHTG=X", - "currency": "HTG", - "exchange": "CCY", - "name": "CAD/HTG", - "from_symbol": "CAD", - "to_symbol": "HTG" - }, - { - "id": "CADHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "CAD/HUF", - "from_symbol": "CAD", - "to_symbol": "HUF" - }, - { - "id": "CADHUX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/HUX", - "from_symbol": "CAD", - "to_symbol": "HUX" - }, - { - "id": "CADIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "CAD/IDR", - "from_symbol": "CAD", - "to_symbol": "IDR" - }, - { - "id": "CADILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "CAD/ILS", - "from_symbol": "CAD", - "to_symbol": "ILS" - }, - { - "id": "CADINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "CAD/INR", - "from_symbol": "CAD", - "to_symbol": "INR" - }, - { - "id": "CADIQD=X", - "currency": "IQD", - "exchange": "CCY", - "name": "CAD/IQD", - "from_symbol": "CAD", - "to_symbol": "IQD" - }, - { - "id": "CADIRR=X", - "currency": "IRR", - "exchange": "CCY", - "name": "CAD/IRR", - "from_symbol": "CAD", - "to_symbol": "IRR" - }, - { - "id": "CADISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "CAD/ISK", - "from_symbol": "CAD", - "to_symbol": "ISK" - }, - { - "id": "CADISX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/ISX", - "from_symbol": "CAD", - "to_symbol": "ISX" - }, - { - "id": "CADJMD=X", - "currency": "JMD", - "exchange": "CCY", - "name": "CAD/JMD", - "from_symbol": "CAD", - "to_symbol": "JMD" - }, - { - "id": "CADJOD=X", - "currency": "JOD", - "exchange": "CCY", - "name": "CAD/JOD", - "from_symbol": "CAD", - "to_symbol": "JOD" - }, - { - "id": "CADJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "CAD/JPY", - "from_symbol": "CAD", - "to_symbol": "JPY" - }, - { - "id": "CADKES=X", - "currency": "KES", - "exchange": "CCY", - "name": "CAD/KES", - "from_symbol": "CAD", - "to_symbol": "KES" - }, - { - "id": "CADKGS=X", - "currency": "KGS", - "exchange": "CCY", - "name": "CAD/KGS", - "from_symbol": "CAD", - "to_symbol": "KGS" - }, - { - "id": "CADKHR=X", - "currency": "KHR", - "exchange": "CCY", - "name": "CAD/KHR", - "from_symbol": "CAD", - "to_symbol": "KHR" - }, - { - "id": "CADKMF=X", - "currency": "KMF", - "exchange": "CCY", - "name": "CAD/KMF", - "from_symbol": "CAD", - "to_symbol": "KMF" - }, - { - "id": "CADKPW=X", - "currency": "KPW", - "exchange": "CCY", - "name": "CAD/KPW", - "from_symbol": "CAD", - "to_symbol": "KPW" - }, - { - "id": "CADKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "CAD/KRW", - "from_symbol": "CAD", - "to_symbol": "KRW" - }, - { - "id": "CADKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "CAD/KWD", - "from_symbol": "CAD", - "to_symbol": "KWD" - }, - { - "id": "CADKYD=X", - "currency": "KYD", - "exchange": "CCY", - "name": "CAD/KYD", - "from_symbol": "CAD", - "to_symbol": "KYD" - }, - { - "id": "CADKZT=X", - "currency": "KZT", - "exchange": "CCY", - "name": "CAD/KZT", - "from_symbol": "CAD", - "to_symbol": "KZT" - }, - { - "id": "CADLAK=X", - "currency": "LAK", - "exchange": "CCY", - "name": "CAD/LAK", - "from_symbol": "CAD", - "to_symbol": "LAK" - }, - { - "id": "CADLBP=X", - "currency": "LBP", - "exchange": "CCY", - "name": "CAD/LBP", - "from_symbol": "CAD", - "to_symbol": "LBP" - }, - { - "id": "CADLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "CAD/LKR", - "from_symbol": "CAD", - "to_symbol": "LKR" - }, - { - "id": "CADLRD=X", - "currency": "LRD", - "exchange": "CCY", - "name": "CAD/LRD", - "from_symbol": "CAD", - "to_symbol": "LRD" - }, - { - "id": "CADLSL=X", - "currency": "LSL", - "exchange": "CCY", - "name": "CAD/LSL", - "from_symbol": "CAD", - "to_symbol": "LSL" - }, - { - "id": "CADLYD=X", - "currency": "LYD", - "exchange": "CCY", - "name": "CAD/LYD", - "from_symbol": "CAD", - "to_symbol": "LYD" - }, - { - "id": "CADMAD=X", - "currency": "MAD", - "exchange": "CCY", - "name": "CAD/MAD", - "from_symbol": "CAD", - "to_symbol": "MAD" - }, - { - "id": "CADMDL=X", - "currency": "MDL", - "exchange": "CCY", - "name": "CAD/MDL", - "from_symbol": "CAD", - "to_symbol": "MDL" - }, - { - "id": "CADMGA=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/MGA", - "from_symbol": "CAD", - "to_symbol": "MGA" - }, - { - "id": "CADMKD=X", - "currency": "MKD", - "exchange": "CCY", - "name": "CAD/MKD", - "from_symbol": "CAD", - "to_symbol": "MKD" - }, - { - "id": "CADMMK=X", - "currency": "MMK", - "exchange": "CCY", - "name": "CAD/MMK", - "from_symbol": "CAD", - "to_symbol": "MMK" - }, - { - "id": "CADMNT=X", - "currency": "MNT", - "exchange": "CCY", - "name": "CAD/MNT", - "from_symbol": "CAD", - "to_symbol": "MNT" - }, - { - "id": "CADMOP=X", - "currency": "MOP", - "exchange": "CCY", - "name": "CAD/MOP", - "from_symbol": "CAD", - "to_symbol": "MOP" - }, - { - "id": "CADMRU=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/MRU", - "from_symbol": "CAD", - "to_symbol": "MRU" - }, - { - "id": "CADMUR=X", - "currency": "MUR", - "exchange": "CCY", - "name": "CAD/MUR", - "from_symbol": "CAD", - "to_symbol": "MUR" - }, - { - "id": "CADMVR=X", - "currency": "MVR", - "exchange": "CCY", - "name": "CAD/MVR", - "from_symbol": "CAD", - "to_symbol": "MVR" - }, - { - "id": "CADMWK=X", - "currency": "MWK", - "exchange": "CCY", - "name": "CAD/MWK", - "from_symbol": "CAD", - "to_symbol": "MWK" - }, - { - "id": "CADMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "CAD/MXN", - "from_symbol": "CAD", - "to_symbol": "MXN" - }, - { - "id": "CADMXX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/MXX", - "from_symbol": "CAD", - "to_symbol": "MXX" - }, - { - "id": "CADMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "CAD/MYR", - "from_symbol": "CAD", - "to_symbol": "MYR" - }, - { - "id": "CADMYX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/MYX", - "from_symbol": "CAD", - "to_symbol": "MYX" - }, - { - "id": "CADMZN=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/MZN", - "from_symbol": "CAD", - "to_symbol": "MZN" - }, - { - "id": "CADNAD=X", - "currency": "NAD", - "exchange": "CCY", - "name": "CAD/NAD", - "from_symbol": "CAD", - "to_symbol": "NAD" - }, - { - "id": "CADNGN=X", - "currency": "NGN", - "exchange": "CCY", - "name": "CAD/NGN", - "from_symbol": "CAD", - "to_symbol": "NGN" - }, - { - "id": "CADNIO=X", - "currency": "NIO", - "exchange": "CCY", - "name": "CAD/NIO", - "from_symbol": "CAD", - "to_symbol": "NIO" - }, - { - "id": "CADNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "CAD/NOK", - "from_symbol": "CAD", - "to_symbol": "NOK" - }, - { - "id": "CADNPR=X", - "currency": "NPR", - "exchange": "CCY", - "name": "CAD/NPR", - "from_symbol": "CAD", - "to_symbol": "NPR" - }, - { - "id": "CADNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "CAD/NZD", - "from_symbol": "CAD", - "to_symbol": "NZD" - }, - { - "id": "CADOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "CAD/OMR", - "from_symbol": "CAD", - "to_symbol": "OMR" - }, - { - "id": "CADPAB=X", - "currency": "PAB", - "exchange": "CCY", - "name": "CAD/PAB", - "from_symbol": "CAD", - "to_symbol": "PAB" - }, - { - "id": "CADPEN=X", - "currency": "PEN", - "exchange": "CCY", - "name": "CAD/PEN", - "from_symbol": "CAD", - "to_symbol": "PEN" - }, - { - "id": "CADPGK=X", - "currency": "PGK", - "exchange": "CCY", - "name": "CAD/PGK", - "from_symbol": "CAD", - "to_symbol": "PGK" - }, - { - "id": "CADPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "CAD/PHP", - "from_symbol": "CAD", - "to_symbol": "PHP" - }, - { - "id": "CADPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "CAD/PKR", - "from_symbol": "CAD", - "to_symbol": "PKR" - }, - { - "id": "CADPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "CAD/PLN", - "from_symbol": "CAD", - "to_symbol": "PLN" - }, - { - "id": "CADPLX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/PLX", - "from_symbol": "CAD", - "to_symbol": "PLX" - }, - { - "id": "CADPYG=X", - "currency": "PYG", - "exchange": "CCY", - "name": "CAD/PYG", - "from_symbol": "CAD", - "to_symbol": "PYG" - }, - { - "id": "CADQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "CAD/QAR", - "from_symbol": "CAD", - "to_symbol": "QAR" - }, - { - "id": "CADRON=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/RON", - "from_symbol": "CAD", - "to_symbol": "RON" - }, - { - "id": "CADRSD=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/RSD", - "from_symbol": "CAD", - "to_symbol": "RSD" - }, - { - "id": "CADRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "CAD/RUB", - "from_symbol": "CAD", - "to_symbol": "RUB" - }, - { - "id": "CADRUX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/RUX", - "from_symbol": "CAD", - "to_symbol": "RUX" - }, - { - "id": "CADRWF=X", - "currency": "RWF", - "exchange": "CCY", - "name": "CAD/RWF", - "from_symbol": "CAD", - "to_symbol": "RWF" - }, - { - "id": "CADSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "CAD/SAR", - "from_symbol": "CAD", - "to_symbol": "SAR" - }, - { - "id": "CADSBD=X", - "currency": "SBD", - "exchange": "CCY", - "name": "CAD/SBD", - "from_symbol": "CAD", - "to_symbol": "SBD" - }, - { - "id": "CADSCR=X", - "currency": "SCR", - "exchange": "CCY", - "name": "CAD/SCR", - "from_symbol": "CAD", - "to_symbol": "SCR" - }, - { - "id": "CADSDG=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/SDG", - "from_symbol": "CAD", - "to_symbol": "SDG" - }, - { - "id": "CADSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "CAD/SEK", - "from_symbol": "CAD", - "to_symbol": "SEK" - }, - { - "id": "CADSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "CAD/SGD", - "from_symbol": "CAD", - "to_symbol": "SGD" - }, - { - "id": "CADSHP=X", - "currency": "SHP", - "exchange": "CCY", - "name": "CAD/SHP", - "from_symbol": "CAD", - "to_symbol": "SHP" - }, - { - "id": "CADSLL=X", - "currency": "SLL", - "exchange": "CCY", - "name": "CAD/SLL", - "from_symbol": "CAD", - "to_symbol": "SLL" - }, - { - "id": "CADSOS=X", - "currency": "SOS", - "exchange": "CCY", - "name": "CAD/SOS", - "from_symbol": "CAD", - "to_symbol": "SOS" - }, - { - "id": "CADSRD=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/SRD", - "from_symbol": "CAD", - "to_symbol": "SRD" - }, - { - "id": "CADSTN=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/STN", - "from_symbol": "CAD", - "to_symbol": "STN" - }, - { - "id": "CADSVC=X", - "currency": "SVC", - "exchange": "CCY", - "name": "CAD/SVC", - "from_symbol": "CAD", - "to_symbol": "SVC" - }, - { - "id": "CADSYP=X", - "currency": "SYP", - "exchange": "CCY", - "name": "CAD/SYP", - "from_symbol": "CAD", - "to_symbol": "SYP" - }, - { - "id": "CADSZL=X", - "currency": "SZL", - "exchange": "CCY", - "name": "CAD/SZL", - "from_symbol": "CAD", - "to_symbol": "SZL" - }, - { - "id": "CADTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "CAD/THB", - "from_symbol": "CAD", - "to_symbol": "THB" - }, - { - "id": "CADTHX=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/THX", - "from_symbol": "CAD", - "to_symbol": "THX" - }, - { - "id": "CADTJS=X", - "currency": "TJS", - "exchange": "CCY", - "name": "CAD/TJS", - "from_symbol": "CAD", - "to_symbol": "TJS" - }, - { - "id": "CADTMT=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/TMT", - "from_symbol": "CAD", - "to_symbol": "TMT" - }, - { - "id": "CADTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "CAD/TND", - "from_symbol": "CAD", - "to_symbol": "TND" - }, - { - "id": "CADTOP=X", - "currency": "TOP", - "exchange": "CCY", - "name": "CAD/TOP", - "from_symbol": "CAD", - "to_symbol": "TOP" - }, - { - "id": "CADTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "CAD/TRY", - "from_symbol": "CAD", - "to_symbol": "TRY" - }, - { - "id": "CADTTD=X", - "currency": "TTD", - "exchange": "CCY", - "name": "CAD/TTD", - "from_symbol": "CAD", - "to_symbol": "TTD" - }, - { - "id": "CADTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "CAD/TWD", - "from_symbol": "CAD", - "to_symbol": "TWD" - }, - { - "id": "CADTZS=X", - "currency": "TZS", - "exchange": "CCY", - "name": "CAD/TZS", - "from_symbol": "CAD", - "to_symbol": "TZS" - }, - { - "id": "CADUAH=X", - "currency": "UAH", - "exchange": "CCY", - "name": "CAD/UAH", - "from_symbol": "CAD", - "to_symbol": "UAH" - }, - { - "id": "CADUGX=X", - "currency": "UGX", - "exchange": "CCY", - "name": "CAD/UGX", - "from_symbol": "CAD", - "to_symbol": "UGX" - }, - { - "id": "CADUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CAD/USD", - "from_symbol": "CAD", - "to_symbol": "USD" - }, - { - "id": "CADUYU=X", - "currency": "UYU", - "exchange": "CCY", - "name": "CAD/UYU", - "from_symbol": "CAD", - "to_symbol": "UYU" - }, - { - "id": "CADUZS=X", - "currency": "UZS", - "exchange": "CCY", - "name": "CAD/UZS", - "from_symbol": "CAD", - "to_symbol": "UZS" - }, - { - "id": "CADVES=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/VES", - "from_symbol": "CAD", - "to_symbol": "VES" - }, - { - "id": "CADVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "CAD/VND", - "from_symbol": "CAD", - "to_symbol": "VND" - }, - { - "id": "CADVUV=X", - "currency": "VUV", - "exchange": "CCY", - "name": "CAD/VUV", - "from_symbol": "CAD", - "to_symbol": "VUV" - }, - { - "id": "CADWST=X", - "currency": "WST", - "exchange": "CCY", - "name": "CAD/WST", - "from_symbol": "CAD", - "to_symbol": "WST" - }, - { - "id": "CADXAF=X", - "currency": "XAF", - "exchange": "CCY", - "name": "CAD/XAF", - "from_symbol": "CAD", - "to_symbol": "XAF" - }, - { - "id": "CADXCD=X", - "currency": "XCD", - "exchange": "CCY", - "name": "CAD/XCD", - "from_symbol": "CAD", - "to_symbol": "XCD" - }, - { - "id": "CADXCU=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/XCU", - "from_symbol": "CAD", - "to_symbol": "XCU" - }, - { - "id": "CADXDR=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/XDR", - "from_symbol": "CAD", - "to_symbol": "XDR" - }, - { - "id": "CADXOF=X", - "currency": "XOF", - "exchange": "CCY", - "name": "CAD/XOF", - "from_symbol": "CAD", - "to_symbol": "XOF" - }, - { - "id": "CADXPF=X", - "currency": "XPF", - "exchange": "CCY", - "name": "CAD/XPF", - "from_symbol": "CAD", - "to_symbol": "XPF" - }, - { - "id": "CADYER=X", - "currency": "YER", - "exchange": "CCY", - "name": "CAD/YER", - "from_symbol": "CAD", - "to_symbol": "YER" - }, - { - "id": "CADZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "CAD/ZAR", - "from_symbol": "CAD", - "to_symbol": "ZAR" - }, - { - "id": "CADZMW=X", - "currency": null, - "exchange": "CCY", - "name": "CAD/ZMW", - "from_symbol": "CAD", - "to_symbol": "ZMW" - }, - { - "id": "CAX=X", - "currency": "CAX", - "exchange": "CCY", - "name": "USD/CAX", - "from_symbol": "USD", - "to_symbol": "CAX" - }, - { - "id": "CDF=X", - "currency": "CDF", - "exchange": "CCY", - "name": "USD/CDF", - "from_symbol": "USD", - "to_symbol": "CDF" - }, - { - "id": "CDFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CDF/BRX", - "from_symbol": "CDF", - "to_symbol": "BRX" - }, - { - "id": "CHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "USD/CHF", - "from_symbol": "USD", - "to_symbol": "CHF" - }, - { - "id": "CHFAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "CHF/AED", - "from_symbol": "CHF", - "to_symbol": "AED" - }, - { - "id": "CHFAFN=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/AFN", - "from_symbol": "CHF", - "to_symbol": "AFN" - }, - { - "id": "CHFALL=X", - "currency": "ALL", - "exchange": "CCY", - "name": "CHF/ALL", - "from_symbol": "CHF", - "to_symbol": "ALL" - }, - { - "id": "CHFAMD=X", - "currency": "AMD", - "exchange": "CCY", - "name": "CHF/AMD", - "from_symbol": "CHF", - "to_symbol": "AMD" - }, - { - "id": "CHFANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "CHF/ANG", - "from_symbol": "CHF", - "to_symbol": "ANG" - }, - { - "id": "CHFAOA=X", - "currency": "AOA", - "exchange": "CCY", - "name": "CHF/AOA", - "from_symbol": "CHF", - "to_symbol": "AOA" - }, - { - "id": "CHFARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "CHF/ARS", - "from_symbol": "CHF", - "to_symbol": "ARS" - }, - { - "id": "CHFAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "CHF/AUD", - "from_symbol": "CHF", - "to_symbol": "AUD" - }, - { - "id": "CHFAWG=X", - "currency": "AWG", - "exchange": "CCY", - "name": "CHF/AWG", - "from_symbol": "CHF", - "to_symbol": "AWG" - }, - { - "id": "CHFAZN=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/AZN", - "from_symbol": "CHF", - "to_symbol": "AZN" - }, - { - "id": "CHFBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "CHF/BAM", - "from_symbol": "CHF", - "to_symbol": "BAM" - }, - { - "id": "CHFBBD=X", - "currency": "BBD", - "exchange": "CCY", - "name": "CHF/BBD", - "from_symbol": "CHF", - "to_symbol": "BBD" - }, - { - "id": "CHFBDT=X", - "currency": "BDT", - "exchange": "CCY", - "name": "CHF/BDT", - "from_symbol": "CHF", - "to_symbol": "BDT" - }, - { - "id": "CHFBGN=X", - "currency": "BGN", - "exchange": "CCY", - "name": "CHF/BGN", - "from_symbol": "CHF", - "to_symbol": "BGN" - }, - { - "id": "CHFBHD=X", - "currency": "BHD", - "exchange": "CCY", - "name": "CHF/BHD", - "from_symbol": "CHF", - "to_symbol": "BHD" - }, - { - "id": "CHFBIF=X", - "currency": "BIF", - "exchange": "CCY", - "name": "CHF/BIF", - "from_symbol": "CHF", - "to_symbol": "BIF" - }, - { - "id": "CHFBMD=X", - "currency": "BMD", - "exchange": "CCY", - "name": "CHF/BMD", - "from_symbol": "CHF", - "to_symbol": "BMD" - }, - { - "id": "CHFBND=X", - "currency": "BND", - "exchange": "CCY", - "name": "CHF/BND", - "from_symbol": "CHF", - "to_symbol": "BND" - }, - { - "id": "CHFBOB=X", - "currency": "BOB", - "exchange": "CCY", - "name": "CHF/BOB", - "from_symbol": "CHF", - "to_symbol": "BOB" - }, - { - "id": "CHFBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "CHF/BRL", - "from_symbol": "CHF", - "to_symbol": "BRL" - }, - { - "id": "CHFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/BRX", - "from_symbol": "CHF", - "to_symbol": "BRX" - }, - { - "id": "CHFBSD=X", - "currency": "BSD", - "exchange": "CCY", - "name": "CHF/BSD", - "from_symbol": "CHF", - "to_symbol": "BSD" - }, - { - "id": "CHFBTN=X", - "currency": "BTN", - "exchange": "CCY", - "name": "CHF/BTN", - "from_symbol": "CHF", - "to_symbol": "BTN" - }, - { - "id": "CHFBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "CHF/BWP", - "from_symbol": "CHF", - "to_symbol": "BWP" - }, - { - "id": "CHFBYN=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/BYN", - "from_symbol": "CHF", - "to_symbol": "BYN" - }, - { - "id": "CHFBZD=X", - "currency": "BZD", - "exchange": "CCY", - "name": "CHF/BZD", - "from_symbol": "CHF", - "to_symbol": "BZD" - }, - { - "id": "CHFCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "CHF/CAD", - "from_symbol": "CHF", - "to_symbol": "CAD" - }, - { - "id": "CHFCAX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/CAX", - "from_symbol": "CHF", - "to_symbol": "CAX" - }, - { - "id": "CHFCDF=X", - "currency": "CDF", - "exchange": "CCY", - "name": "CHF/CDF", - "from_symbol": "CHF", - "to_symbol": "CDF" - }, - { - "id": "CHFCLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "CHF/CLP", - "from_symbol": "CHF", - "to_symbol": "CLP" - }, - { - "id": "CHFCNH=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/CNH", - "from_symbol": "CHF", - "to_symbol": "CNH" - }, - { - "id": "CHFCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "CHF/CNY", - "from_symbol": "CHF", - "to_symbol": "CNY" - }, - { - "id": "CHFCOP=X", - "currency": "COP", - "exchange": "CCY", - "name": "CHF/COP", - "from_symbol": "CHF", - "to_symbol": "COP" - }, - { - "id": "CHFCRC=X", - "currency": "CRC", - "exchange": "CCY", - "name": "CHF/CRC", - "from_symbol": "CHF", - "to_symbol": "CRC" - }, - { - "id": "CHFCUP=X", - "currency": "CUP", - "exchange": "CCY", - "name": "CHF/CUP", - "from_symbol": "CHF", - "to_symbol": "CUP" - }, - { - "id": "CHFCVE=X", - "currency": "CVE", - "exchange": "CCY", - "name": "CHF/CVE", - "from_symbol": "CHF", - "to_symbol": "CVE" - }, - { - "id": "CHFCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "CHF/CZK", - "from_symbol": "CHF", - "to_symbol": "CZK" - }, - { - "id": "CHFCZX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/CZX", - "from_symbol": "CHF", - "to_symbol": "CZX" - }, - { - "id": "CHFDJF=X", - "currency": "DJF", - "exchange": "CCY", - "name": "CHF/DJF", - "from_symbol": "CHF", - "to_symbol": "DJF" - }, - { - "id": "CHFDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "CHF/DKK", - "from_symbol": "CHF", - "to_symbol": "DKK" - }, - { - "id": "CHFDKX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/DKX", - "from_symbol": "CHF", - "to_symbol": "DKX" - }, - { - "id": "CHFDOP=X", - "currency": "DOP", - "exchange": "CCY", - "name": "CHF/DOP", - "from_symbol": "CHF", - "to_symbol": "DOP" - }, - { - "id": "CHFDZD=X", - "currency": "DZD", - "exchange": "CCY", - "name": "CHF/DZD", - "from_symbol": "CHF", - "to_symbol": "DZD" - }, - { - "id": "CHFEGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "CHF/EGP", - "from_symbol": "CHF", - "to_symbol": "EGP" - }, - { - "id": "CHFERN=X", - "currency": "ERN", - "exchange": "CCY", - "name": "CHF/ERN", - "from_symbol": "CHF", - "to_symbol": "ERN" - }, - { - "id": "CHFETB=X", - "currency": "ETB", - "exchange": "CCY", - "name": "CHF/ETB", - "from_symbol": "CHF", - "to_symbol": "ETB" - }, - { - "id": "CHFEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "CHF/EUR", - "from_symbol": "CHF", - "to_symbol": "EUR" - }, - { - "id": "CHFFJD=X", - "currency": "FJD", - "exchange": "CCY", - "name": "CHF/FJD", - "from_symbol": "CHF", - "to_symbol": "FJD" - }, - { - "id": "CHFFKP=X", - "currency": "FKP", - "exchange": "CCY", - "name": "CHF/FKP", - "from_symbol": "CHF", - "to_symbol": "FKP" - }, - { - "id": "CHFGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "CHF/GBP", - "from_symbol": "CHF", - "to_symbol": "GBP" - }, - { - "id": "CHFGEL=X", - "currency": "GEL", - "exchange": "CCY", - "name": "CHF/GEL", - "from_symbol": "CHF", - "to_symbol": "GEL" - }, - { - "id": "CHFGHS=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/GHS", - "from_symbol": "CHF", - "to_symbol": "GHS" - }, - { - "id": "CHFGIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "CHF/GIP", - "from_symbol": "CHF", - "to_symbol": "GIP" - }, - { - "id": "CHFGMD=X", - "currency": "GMD", - "exchange": "CCY", - "name": "CHF/GMD", - "from_symbol": "CHF", - "to_symbol": "GMD" - }, - { - "id": "CHFGNF=X", - "currency": "GNF", - "exchange": "CCY", - "name": "CHF/GNF", - "from_symbol": "CHF", - "to_symbol": "GNF" - }, - { - "id": "CHFGTQ=X", - "currency": "GTQ", - "exchange": "CCY", - "name": "CHF/GTQ", - "from_symbol": "CHF", - "to_symbol": "GTQ" - }, - { - "id": "CHFGYD=X", - "currency": "GYD", - "exchange": "CCY", - "name": "CHF/GYD", - "from_symbol": "CHF", - "to_symbol": "GYD" - }, - { - "id": "CHFHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "CHF/HKD", - "from_symbol": "CHF", - "to_symbol": "HKD" - }, - { - "id": "CHFHNL=X", - "currency": "HNL", - "exchange": "CCY", - "name": "CHF/HNL", - "from_symbol": "CHF", - "to_symbol": "HNL" - }, - { - "id": "CHFHRK=X", - "currency": "HRK", - "exchange": "CCY", - "name": "CHF/HRK", - "from_symbol": "CHF", - "to_symbol": "HRK" - }, - { - "id": "CHFHRX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/HRX", - "from_symbol": "CHF", - "to_symbol": "HRX" - }, - { - "id": "CHFHTG=X", - "currency": "HTG", - "exchange": "CCY", - "name": "CHF/HTG", - "from_symbol": "CHF", - "to_symbol": "HTG" - }, - { - "id": "CHFHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "CHF/HUF", - "from_symbol": "CHF", - "to_symbol": "HUF" - }, - { - "id": "CHFHUX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/HUX", - "from_symbol": "CHF", - "to_symbol": "HUX" - }, - { - "id": "CHFIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "CHF/IDR", - "from_symbol": "CHF", - "to_symbol": "IDR" - }, - { - "id": "CHFILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "CHF/ILS", - "from_symbol": "CHF", - "to_symbol": "ILS" - }, - { - "id": "CHFINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "CHF/INR", - "from_symbol": "CHF", - "to_symbol": "INR" - }, - { - "id": "CHFIQD=X", - "currency": "IQD", - "exchange": "CCY", - "name": "CHF/IQD", - "from_symbol": "CHF", - "to_symbol": "IQD" - }, - { - "id": "CHFIRR=X", - "currency": "IRR", - "exchange": "CCY", - "name": "CHF/IRR", - "from_symbol": "CHF", - "to_symbol": "IRR" - }, - { - "id": "CHFISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "CHF/ISK", - "from_symbol": "CHF", - "to_symbol": "ISK" - }, - { - "id": "CHFISX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/ISX", - "from_symbol": "CHF", - "to_symbol": "ISX" - }, - { - "id": "CHFJMD=X", - "currency": "JMD", - "exchange": "CCY", - "name": "CHF/JMD", - "from_symbol": "CHF", - "to_symbol": "JMD" - }, - { - "id": "CHFJOD=X", - "currency": "JOD", - "exchange": "CCY", - "name": "CHF/JOD", - "from_symbol": "CHF", - "to_symbol": "JOD" - }, - { - "id": "CHFJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "CHF/JPY", - "from_symbol": "CHF", - "to_symbol": "JPY" - }, - { - "id": "CHFKES=X", - "currency": "KES", - "exchange": "CCY", - "name": "CHF/KES", - "from_symbol": "CHF", - "to_symbol": "KES" - }, - { - "id": "CHFKGS=X", - "currency": "KGS", - "exchange": "CCY", - "name": "CHF/KGS", - "from_symbol": "CHF", - "to_symbol": "KGS" - }, - { - "id": "CHFKHR=X", - "currency": "KHR", - "exchange": "CCY", - "name": "CHF/KHR", - "from_symbol": "CHF", - "to_symbol": "KHR" - }, - { - "id": "CHFKMF=X", - "currency": "KMF", - "exchange": "CCY", - "name": "CHF/KMF", - "from_symbol": "CHF", - "to_symbol": "KMF" - }, - { - "id": "CHFKPW=X", - "currency": "KPW", - "exchange": "CCY", - "name": "CHF/KPW", - "from_symbol": "CHF", - "to_symbol": "KPW" - }, - { - "id": "CHFKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "CHF/KRW", - "from_symbol": "CHF", - "to_symbol": "KRW" - }, - { - "id": "CHFKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "CHF/KWD", - "from_symbol": "CHF", - "to_symbol": "KWD" - }, - { - "id": "CHFKYD=X", - "currency": "KYD", - "exchange": "CCY", - "name": "CHF/KYD", - "from_symbol": "CHF", - "to_symbol": "KYD" - }, - { - "id": "CHFKZT=X", - "currency": "KZT", - "exchange": "CCY", - "name": "CHF/KZT", - "from_symbol": "CHF", - "to_symbol": "KZT" - }, - { - "id": "CHFLAK=X", - "currency": "LAK", - "exchange": "CCY", - "name": "CHF/LAK", - "from_symbol": "CHF", - "to_symbol": "LAK" - }, - { - "id": "CHFLBP=X", - "currency": "LBP", - "exchange": "CCY", - "name": "CHF/LBP", - "from_symbol": "CHF", - "to_symbol": "LBP" - }, - { - "id": "CHFLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "CHF/LKR", - "from_symbol": "CHF", - "to_symbol": "LKR" - }, - { - "id": "CHFLRD=X", - "currency": "LRD", - "exchange": "CCY", - "name": "CHF/LRD", - "from_symbol": "CHF", - "to_symbol": "LRD" - }, - { - "id": "CHFLSL=X", - "currency": "LSL", - "exchange": "CCY", - "name": "CHF/LSL", - "from_symbol": "CHF", - "to_symbol": "LSL" - }, - { - "id": "CHFLYD=X", - "currency": "LYD", - "exchange": "CCY", - "name": "CHF/LYD", - "from_symbol": "CHF", - "to_symbol": "LYD" - }, - { - "id": "CHFMAD=X", - "currency": "MAD", - "exchange": "CCY", - "name": "CHF/MAD", - "from_symbol": "CHF", - "to_symbol": "MAD" - }, - { - "id": "CHFMDL=X", - "currency": "MDL", - "exchange": "CCY", - "name": "CHF/MDL", - "from_symbol": "CHF", - "to_symbol": "MDL" - }, - { - "id": "CHFMGA=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/MGA", - "from_symbol": "CHF", - "to_symbol": "MGA" - }, - { - "id": "CHFMKD=X", - "currency": "MKD", - "exchange": "CCY", - "name": "CHF/MKD", - "from_symbol": "CHF", - "to_symbol": "MKD" - }, - { - "id": "CHFMMK=X", - "currency": "MMK", - "exchange": "CCY", - "name": "CHF/MMK", - "from_symbol": "CHF", - "to_symbol": "MMK" - }, - { - "id": "CHFMNT=X", - "currency": "MNT", - "exchange": "CCY", - "name": "CHF/MNT", - "from_symbol": "CHF", - "to_symbol": "MNT" - }, - { - "id": "CHFMOP=X", - "currency": "MOP", - "exchange": "CCY", - "name": "CHF/MOP", - "from_symbol": "CHF", - "to_symbol": "MOP" - }, - { - "id": "CHFMRU=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/MRU", - "from_symbol": "CHF", - "to_symbol": "MRU" - }, - { - "id": "CHFMUR=X", - "currency": "MUR", - "exchange": "CCY", - "name": "CHF/MUR", - "from_symbol": "CHF", - "to_symbol": "MUR" - }, - { - "id": "CHFMVR=X", - "currency": "MVR", - "exchange": "CCY", - "name": "CHF/MVR", - "from_symbol": "CHF", - "to_symbol": "MVR" - }, - { - "id": "CHFMWK=X", - "currency": "MWK", - "exchange": "CCY", - "name": "CHF/MWK", - "from_symbol": "CHF", - "to_symbol": "MWK" - }, - { - "id": "CHFMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "CHF/MXN", - "from_symbol": "CHF", - "to_symbol": "MXN" - }, - { - "id": "CHFMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "CHF/MYR", - "from_symbol": "CHF", - "to_symbol": "MYR" - }, - { - "id": "CHFMYX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/MYX", - "from_symbol": "CHF", - "to_symbol": "MYX" - }, - { - "id": "CHFMZN=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/MZN", - "from_symbol": "CHF", - "to_symbol": "MZN" - }, - { - "id": "CHFNAD=X", - "currency": "NAD", - "exchange": "CCY", - "name": "CHF/NAD", - "from_symbol": "CHF", - "to_symbol": "NAD" - }, - { - "id": "CHFNGN=X", - "currency": "NGN", - "exchange": "CCY", - "name": "CHF/NGN", - "from_symbol": "CHF", - "to_symbol": "NGN" - }, - { - "id": "CHFNIO=X", - "currency": "NIO", - "exchange": "CCY", - "name": "CHF/NIO", - "from_symbol": "CHF", - "to_symbol": "NIO" - }, - { - "id": "CHFNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "CHF/NOK", - "from_symbol": "CHF", - "to_symbol": "NOK" - }, - { - "id": "CHFNPR=X", - "currency": "NPR", - "exchange": "CCY", - "name": "CHF/NPR", - "from_symbol": "CHF", - "to_symbol": "NPR" - }, - { - "id": "CHFNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "CHF/NZD", - "from_symbol": "CHF", - "to_symbol": "NZD" - }, - { - "id": "CHFOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "CHF/OMR", - "from_symbol": "CHF", - "to_symbol": "OMR" - }, - { - "id": "CHFPAB=X", - "currency": "PAB", - "exchange": "CCY", - "name": "CHF/PAB", - "from_symbol": "CHF", - "to_symbol": "PAB" - }, - { - "id": "CHFPEN=X", - "currency": "PEN", - "exchange": "CCY", - "name": "CHF/PEN", - "from_symbol": "CHF", - "to_symbol": "PEN" - }, - { - "id": "CHFPGK=X", - "currency": "PGK", - "exchange": "CCY", - "name": "CHF/PGK", - "from_symbol": "CHF", - "to_symbol": "PGK" - }, - { - "id": "CHFPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "CHF/PHP", - "from_symbol": "CHF", - "to_symbol": "PHP" - }, - { - "id": "CHFPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "CHF/PKR", - "from_symbol": "CHF", - "to_symbol": "PKR" - }, - { - "id": "CHFPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "CHF/PLN", - "from_symbol": "CHF", - "to_symbol": "PLN" - }, - { - "id": "CHFPLX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/PLX", - "from_symbol": "CHF", - "to_symbol": "PLX" - }, - { - "id": "CHFPYG=X", - "currency": "PYG", - "exchange": "CCY", - "name": "CHF/PYG", - "from_symbol": "CHF", - "to_symbol": "PYG" - }, - { - "id": "CHFQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "CHF/QAR", - "from_symbol": "CHF", - "to_symbol": "QAR" - }, - { - "id": "CHFRON=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/RON", - "from_symbol": "CHF", - "to_symbol": "RON" - }, - { - "id": "CHFRSD=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/RSD", - "from_symbol": "CHF", - "to_symbol": "RSD" - }, - { - "id": "CHFRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "CHF/RUB", - "from_symbol": "CHF", - "to_symbol": "RUB" - }, - { - "id": "CHFRUX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/RUX", - "from_symbol": "CHF", - "to_symbol": "RUX" - }, - { - "id": "CHFRWF=X", - "currency": "RWF", - "exchange": "CCY", - "name": "CHF/RWF", - "from_symbol": "CHF", - "to_symbol": "RWF" - }, - { - "id": "CHFSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "CHF/SAR", - "from_symbol": "CHF", - "to_symbol": "SAR" - }, - { - "id": "CHFSBD=X", - "currency": "SBD", - "exchange": "CCY", - "name": "CHF/SBD", - "from_symbol": "CHF", - "to_symbol": "SBD" - }, - { - "id": "CHFSCR=X", - "currency": "SCR", - "exchange": "CCY", - "name": "CHF/SCR", - "from_symbol": "CHF", - "to_symbol": "SCR" - }, - { - "id": "CHFSDG=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/SDG", - "from_symbol": "CHF", - "to_symbol": "SDG" - }, - { - "id": "CHFSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "CHF/SEK", - "from_symbol": "CHF", - "to_symbol": "SEK" - }, - { - "id": "CHFSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "CHF/SGD", - "from_symbol": "CHF", - "to_symbol": "SGD" - }, - { - "id": "CHFSHP=X", - "currency": "SHP", - "exchange": "CCY", - "name": "CHF/SHP", - "from_symbol": "CHF", - "to_symbol": "SHP" - }, - { - "id": "CHFSLL=X", - "currency": "SLL", - "exchange": "CCY", - "name": "CHF/SLL", - "from_symbol": "CHF", - "to_symbol": "SLL" - }, - { - "id": "CHFSOS=X", - "currency": "SOS", - "exchange": "CCY", - "name": "CHF/SOS", - "from_symbol": "CHF", - "to_symbol": "SOS" - }, - { - "id": "CHFSRD=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/SRD", - "from_symbol": "CHF", - "to_symbol": "SRD" - }, - { - "id": "CHFSTN=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/STN", - "from_symbol": "CHF", - "to_symbol": "STN" - }, - { - "id": "CHFSVC=X", - "currency": "SVC", - "exchange": "CCY", - "name": "CHF/SVC", - "from_symbol": "CHF", - "to_symbol": "SVC" - }, - { - "id": "CHFSYP=X", - "currency": "SYP", - "exchange": "CCY", - "name": "CHF/SYP", - "from_symbol": "CHF", - "to_symbol": "SYP" - }, - { - "id": "CHFSZL=X", - "currency": "SZL", - "exchange": "CCY", - "name": "CHF/SZL", - "from_symbol": "CHF", - "to_symbol": "SZL" - }, - { - "id": "CHFTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "CHF/THB", - "from_symbol": "CHF", - "to_symbol": "THB" - }, - { - "id": "CHFTHX=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/THX", - "from_symbol": "CHF", - "to_symbol": "THX" - }, - { - "id": "CHFTJS=X", - "currency": "TJS", - "exchange": "CCY", - "name": "CHF/TJS", - "from_symbol": "CHF", - "to_symbol": "TJS" - }, - { - "id": "CHFTMT=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/TMT", - "from_symbol": "CHF", - "to_symbol": "TMT" - }, - { - "id": "CHFTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "CHF/TND", - "from_symbol": "CHF", - "to_symbol": "TND" - }, - { - "id": "CHFTOP=X", - "currency": "TOP", - "exchange": "CCY", - "name": "CHF/TOP", - "from_symbol": "CHF", - "to_symbol": "TOP" - }, - { - "id": "CHFTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "CHF/TRY", - "from_symbol": "CHF", - "to_symbol": "TRY" - }, - { - "id": "CHFTTD=X", - "currency": "TTD", - "exchange": "CCY", - "name": "CHF/TTD", - "from_symbol": "CHF", - "to_symbol": "TTD" - }, - { - "id": "CHFTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "CHF/TWD", - "from_symbol": "CHF", - "to_symbol": "TWD" - }, - { - "id": "CHFTZS=X", - "currency": "TZS", - "exchange": "CCY", - "name": "CHF/TZS", - "from_symbol": "CHF", - "to_symbol": "TZS" - }, - { - "id": "CHFUAH=X", - "currency": "UAH", - "exchange": "CCY", - "name": "CHF/UAH", - "from_symbol": "CHF", - "to_symbol": "UAH" - }, - { - "id": "CHFUGX=X", - "currency": "UGX", - "exchange": "CCY", - "name": "CHF/UGX", - "from_symbol": "CHF", - "to_symbol": "UGX" - }, - { - "id": "CHFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CHF/USD", - "from_symbol": "CHF", - "to_symbol": "USD" - }, - { - "id": "CHFUYU=X", - "currency": "UYU", - "exchange": "CCY", - "name": "CHF/UYU", - "from_symbol": "CHF", - "to_symbol": "UYU" - }, - { - "id": "CHFUZS=X", - "currency": "UZS", - "exchange": "CCY", - "name": "CHF/UZS", - "from_symbol": "CHF", - "to_symbol": "UZS" - }, - { - "id": "CHFVES=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/VES", - "from_symbol": "CHF", - "to_symbol": "VES" - }, - { - "id": "CHFVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "CHF/VND", - "from_symbol": "CHF", - "to_symbol": "VND" - }, - { - "id": "CHFVUV=X", - "currency": "VUV", - "exchange": "CCY", - "name": "CHF/VUV", - "from_symbol": "CHF", - "to_symbol": "VUV" - }, - { - "id": "CHFWST=X", - "currency": "WST", - "exchange": "CCY", - "name": "CHF/WST", - "from_symbol": "CHF", - "to_symbol": "WST" - }, - { - "id": "CHFXAF=X", - "currency": "XAF", - "exchange": "CCY", - "name": "CHF/XAF", - "from_symbol": "CHF", - "to_symbol": "XAF" - }, - { - "id": "CHFXCD=X", - "currency": "XCD", - "exchange": "CCY", - "name": "CHF/XCD", - "from_symbol": "CHF", - "to_symbol": "XCD" - }, - { - "id": "CHFXCU=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/XCU", - "from_symbol": "CHF", - "to_symbol": "XCU" - }, - { - "id": "CHFXDR=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/XDR", - "from_symbol": "CHF", - "to_symbol": "XDR" - }, - { - "id": "CHFXOF=X", - "currency": "XOF", - "exchange": "CCY", - "name": "CHF/XOF", - "from_symbol": "CHF", - "to_symbol": "XOF" - }, - { - "id": "CHFXPF=X", - "currency": "XPF", - "exchange": "CCY", - "name": "CHF/XPF", - "from_symbol": "CHF", - "to_symbol": "XPF" - }, - { - "id": "CHFYER=X", - "currency": "YER", - "exchange": "CCY", - "name": "CHF/YER", - "from_symbol": "CHF", - "to_symbol": "YER" - }, - { - "id": "CHFZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "CHF/ZAR", - "from_symbol": "CHF", - "to_symbol": "ZAR" - }, - { - "id": "CHFZMW=X", - "currency": null, - "exchange": "CCY", - "name": "CHF/ZMW", - "from_symbol": "CHF", - "to_symbol": "ZMW" - }, - { - "id": "CLF=X", - "currency": "CLF", - "exchange": "CCY", - "name": "USD/CLF", - "from_symbol": "USD", - "to_symbol": "CLF" - }, - { - "id": "CLFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CLF/BRX", - "from_symbol": "CLF", - "to_symbol": "BRX" - }, - { - "id": "CLFCLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "CLF/CLP", - "from_symbol": "CLF", - "to_symbol": "CLP" - }, - { - "id": "CLFEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "CLF/EUR", - "from_symbol": "CLF", - "to_symbol": "EUR" - }, - { - "id": "CLFGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "CLF/GBP", - "from_symbol": "CLF", - "to_symbol": "GBP" - }, - { - "id": "CLFJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "CLF/JPY", - "from_symbol": "CLF", - "to_symbol": "JPY" - }, - { - "id": "CLFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CLF/USD", - "from_symbol": "CLF", - "to_symbol": "USD" - }, - { - "id": "CLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "USD/CLP", - "from_symbol": "USD", - "to_symbol": "CLP" - }, - { - "id": "CLPBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "CLP/BRL", - "from_symbol": "CLP", - "to_symbol": "BRL" - }, - { - "id": "CLPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CLP/BRX", - "from_symbol": "CLP", - "to_symbol": "BRX" - }, - { - "id": "CLPEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "CLP/EUR", - "from_symbol": "CLP", - "to_symbol": "EUR" - }, - { - "id": "CLPGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "CLP/GBP", - "from_symbol": "CLP", - "to_symbol": "GBP" - }, - { - "id": "CLPJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "CLP/JPY", - "from_symbol": "CLP", - "to_symbol": "JPY" - }, - { - "id": "CLPPLX=X", - "currency": null, - "exchange": "CCY", - "name": "CLP/PLX", - "from_symbol": "CLP", - "to_symbol": "PLX" - }, - { - "id": "CLPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CLP/USD", - "from_symbol": "CLP", - "to_symbol": "USD" - }, - { - "id": "CLPXDR=X", - "currency": null, - "exchange": "CCY", - "name": "CLP/XDR", - "from_symbol": "CLP", - "to_symbol": "XDR" - }, - { - "id": "CNH=X", - "currency": "CNH", - "exchange": "CCY", - "name": "USD/CNH", - "from_symbol": "USD", - "to_symbol": "CNH" - }, - { - "id": "CNHBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CNH/BRX", - "from_symbol": "CNH", - "to_symbol": "BRX" - }, - { - "id": "CNHCNY=X", - "currency": null, - "exchange": "CCY", - "name": "CNH/CNY", - "from_symbol": "CNH", - "to_symbol": "CNY" - }, - { - "id": "CNHHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "CNH/HKD", - "from_symbol": "CNH", - "to_symbol": "HKD" - }, - { - "id": "CNHIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "CNH/IDR", - "from_symbol": "CNH", - "to_symbol": "IDR" - }, - { - "id": "CNHINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "CNH/INR", - "from_symbol": "CNH", - "to_symbol": "INR" - }, - { - "id": "CNHJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "CNH/JPY", - "from_symbol": "CNH", - "to_symbol": "JPY" - }, - { - "id": "CNHKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "CNH/KRW", - "from_symbol": "CNH", - "to_symbol": "KRW" - }, - { - "id": "CNHPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "CNH/PHP", - "from_symbol": "CNH", - "to_symbol": "PHP" - }, - { - "id": "CNHSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "CNH/SGD", - "from_symbol": "CNH", - "to_symbol": "SGD" - }, - { - "id": "CNHTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "CNH/THB", - "from_symbol": "CNH", - "to_symbol": "THB" - }, - { - "id": "CNHTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "CNH/TWD", - "from_symbol": "CNH", - "to_symbol": "TWD" - }, - { - "id": "CNHUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CNH/USD", - "from_symbol": "CNH", - "to_symbol": "USD" - }, - { - "id": "CNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "USD/CNY", - "from_symbol": "USD", - "to_symbol": "CNY" - }, - { - "id": "CNYAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "CNY/AUD", - "from_symbol": "CNY", - "to_symbol": "AUD" - }, - { - "id": "CNYBAM=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/BAM", - "from_symbol": "CNY", - "to_symbol": "BAM" - }, - { - "id": "CNYBND=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/BND", - "from_symbol": "CNY", - "to_symbol": "BND" - }, - { - "id": "CNYBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "CNY/BRL", - "from_symbol": "CNY", - "to_symbol": "BRL" - }, - { - "id": "CNYBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/BRX", - "from_symbol": "CNY", - "to_symbol": "BRX" - }, - { - "id": "CNYBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "CNY/BWP", - "from_symbol": "CNY", - "to_symbol": "BWP" - }, - { - "id": "CNYCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "CNY/CAD", - "from_symbol": "CNY", - "to_symbol": "CAD" - }, - { - "id": "CNYCAX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/CAX", - "from_symbol": "CNY", - "to_symbol": "CAX" - }, - { - "id": "CNYCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "CNY/CHF", - "from_symbol": "CNY", - "to_symbol": "CHF" - }, - { - "id": "CNYCNH=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/CNH", - "from_symbol": "CNY", - "to_symbol": "CNH" - }, - { - "id": "CNYCZX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/CZX", - "from_symbol": "CNY", - "to_symbol": "CZX" - }, - { - "id": "CNYDKX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/DKX", - "from_symbol": "CNY", - "to_symbol": "DKX" - }, - { - "id": "CNYEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "CNY/EUR", - "from_symbol": "CNY", - "to_symbol": "EUR" - }, - { - "id": "CNYGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "CNY/GBP", - "from_symbol": "CNY", - "to_symbol": "GBP" - }, - { - "id": "CNYGHS=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/GHS", - "from_symbol": "CNY", - "to_symbol": "GHS" - }, - { - "id": "CNYHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "CNY/HKD", - "from_symbol": "CNY", - "to_symbol": "HKD" - }, - { - "id": "CNYHUX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/HUX", - "from_symbol": "CNY", - "to_symbol": "HUX" - }, - { - "id": "CNYIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "CNY/IDR", - "from_symbol": "CNY", - "to_symbol": "IDR" - }, - { - "id": "CNYINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "CNY/INR", - "from_symbol": "CNY", - "to_symbol": "INR" - }, - { - "id": "CNYJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "CNY/JPY", - "from_symbol": "CNY", - "to_symbol": "JPY" - }, - { - "id": "CNYKHR=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/KHR", - "from_symbol": "CNY", - "to_symbol": "KHR" - }, - { - "id": "CNYKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "CNY/KRW", - "from_symbol": "CNY", - "to_symbol": "KRW" - }, - { - "id": "CNYLAK=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/LAK", - "from_symbol": "CNY", - "to_symbol": "LAK" - }, - { - "id": "CNYLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "CNY/LKR", - "from_symbol": "CNY", - "to_symbol": "LKR" - }, - { - "id": "CNYMMK=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/MMK", - "from_symbol": "CNY", - "to_symbol": "MMK" - }, - { - "id": "CNYMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "CNY/MYR", - "from_symbol": "CNY", - "to_symbol": "MYR" - }, - { - "id": "CNYMYX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/MYX", - "from_symbol": "CNY", - "to_symbol": "MYX" - }, - { - "id": "CNYNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "CNY/NZD", - "from_symbol": "CNY", - "to_symbol": "NZD" - }, - { - "id": "CNYPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "CNY/PHP", - "from_symbol": "CNY", - "to_symbol": "PHP" - }, - { - "id": "CNYPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "CNY/PLN", - "from_symbol": "CNY", - "to_symbol": "PLN" - }, - { - "id": "CNYPLX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/PLX", - "from_symbol": "CNY", - "to_symbol": "PLX" - }, - { - "id": "CNYRSD=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/RSD", - "from_symbol": "CNY", - "to_symbol": "RSD" - }, - { - "id": "CNYRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "CNY/RUB", - "from_symbol": "CNY", - "to_symbol": "RUB" - }, - { - "id": "CNYRUX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/RUX", - "from_symbol": "CNY", - "to_symbol": "RUX" - }, - { - "id": "CNYSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "CNY/SGD", - "from_symbol": "CNY", - "to_symbol": "SGD" - }, - { - "id": "CNYTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "CNY/THB", - "from_symbol": "CNY", - "to_symbol": "THB" - }, - { - "id": "CNYTHX=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/THX", - "from_symbol": "CNY", - "to_symbol": "THX" - }, - { - "id": "CNYTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "CNY/TWD", - "from_symbol": "CNY", - "to_symbol": "TWD" - }, - { - "id": "CNYUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CNY/USD", - "from_symbol": "CNY", - "to_symbol": "USD" - }, - { - "id": "CNYVND=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/VND", - "from_symbol": "CNY", - "to_symbol": "VND" - }, - { - "id": "CNYXCU=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/XCU", - "from_symbol": "CNY", - "to_symbol": "XCU" - }, - { - "id": "CNYXDR=X", - "currency": null, - "exchange": "CCY", - "name": "CNY/XDR", - "from_symbol": "CNY", - "to_symbol": "XDR" - }, - { - "id": "CNYZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "CNY/ZAR", - "from_symbol": "CNY", - "to_symbol": "ZAR" - }, - { - "id": "COP=X", - "currency": "COP", - "exchange": "CCY", - "name": "USD/COP", - "from_symbol": "USD", - "to_symbol": "COP" - }, - { - "id": "COPBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "COP/BRL", - "from_symbol": "COP", - "to_symbol": "BRL" - }, - { - "id": "COPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "COP/BRX", - "from_symbol": "COP", - "to_symbol": "BRX" - }, - { - "id": "COPEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "COP/EUR", - "from_symbol": "COP", - "to_symbol": "EUR" - }, - { - "id": "COPGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "COP/GBP", - "from_symbol": "COP", - "to_symbol": "GBP" - }, - { - "id": "COPJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "COP/JPY", - "from_symbol": "COP", - "to_symbol": "JPY" - }, - { - "id": "COPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "COP/USD", - "from_symbol": "COP", - "to_symbol": "USD" - }, - { - "id": "COPXDR=X", - "currency": null, - "exchange": "CCY", - "name": "COP/XDR", - "from_symbol": "COP", - "to_symbol": "XDR" - }, - { - "id": "CRC=X", - "currency": "CRC", - "exchange": "CCY", - "name": "USD/CRC", - "from_symbol": "USD", - "to_symbol": "CRC" - }, - { - "id": "CRCBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CRC/BRX", - "from_symbol": "CRC", - "to_symbol": "BRX" - }, - { - "id": "CRCGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "CRC/GBP", - "from_symbol": "CRC", - "to_symbol": "GBP" - }, - { - "id": "CRCUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CRC/USD", - "from_symbol": "CRC", - "to_symbol": "USD" - }, - { - "id": "CUC=X", - "currency": "CUC", - "exchange": "CCY", - "name": "USD/CUC", - "from_symbol": "USD", - "to_symbol": "CUC" - }, - { - "id": "CUCCUP=X", - "currency": null, - "exchange": "CCY", - "name": "CUC/CUP", - "from_symbol": "CUC", - "to_symbol": "CUP" - }, - { - "id": "CUCUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CUC/USD", - "from_symbol": "CUC", - "to_symbol": "USD" - }, - { - "id": "CUP=X", - "currency": "CUP", - "exchange": "CCY", - "name": "USD/CUP", - "from_symbol": "USD", - "to_symbol": "CUP" - }, - { - "id": "CUPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CUP/BRX", - "from_symbol": "CUP", - "to_symbol": "BRX" - }, - { - "id": "CUPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CUP/USD", - "from_symbol": "CUP", - "to_symbol": "USD" - }, - { - "id": "CVE=X", - "currency": "CVE", - "exchange": "CCY", - "name": "USD/CVE", - "from_symbol": "USD", - "to_symbol": "CVE" - }, - { - "id": "CVEBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CVE/BRX", - "from_symbol": "CVE", - "to_symbol": "BRX" - }, - { - "id": "CVEUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CVE/USD", - "from_symbol": "CVE", - "to_symbol": "USD" - }, - { - "id": "CZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "USD/CZK", - "from_symbol": "USD", - "to_symbol": "CZK" - }, - { - "id": "CZKAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "CZK/AUD", - "from_symbol": "CZK", - "to_symbol": "AUD" - }, - { - "id": "CZKBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "CZK/BAM", - "from_symbol": "CZK", - "to_symbol": "BAM" - }, - { - "id": "CZKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/BRX", - "from_symbol": "CZK", - "to_symbol": "BRX" - }, - { - "id": "CZKCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "CZK/CHF", - "from_symbol": "CZK", - "to_symbol": "CHF" - }, - { - "id": "CZKDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "CZK/DKK", - "from_symbol": "CZK", - "to_symbol": "DKK" - }, - { - "id": "CZKDKX=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/DKX", - "from_symbol": "CZK", - "to_symbol": "DKX" - }, - { - "id": "CZKEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "CZK/EUR", - "from_symbol": "CZK", - "to_symbol": "EUR" - }, - { - "id": "CZKGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "CZK/GBP", - "from_symbol": "CZK", - "to_symbol": "GBP" - }, - { - "id": "CZKHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "CZK/HKD", - "from_symbol": "CZK", - "to_symbol": "HKD" - }, - { - "id": "CZKHRX=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/HRX", - "from_symbol": "CZK", - "to_symbol": "HRX" - }, - { - "id": "CZKHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "CZK/HUF", - "from_symbol": "CZK", - "to_symbol": "HUF" - }, - { - "id": "CZKHUX=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/HUX", - "from_symbol": "CZK", - "to_symbol": "HUX" - }, - { - "id": "CZKJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "CZK/JPY", - "from_symbol": "CZK", - "to_symbol": "JPY" - }, - { - "id": "CZKPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "CZK/PLN", - "from_symbol": "CZK", - "to_symbol": "PLN" - }, - { - "id": "CZKPLX=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/PLX", - "from_symbol": "CZK", - "to_symbol": "PLX" - }, - { - "id": "CZKRSD=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/RSD", - "from_symbol": "CZK", - "to_symbol": "RSD" - }, - { - "id": "CZKRUX=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/RUX", - "from_symbol": "CZK", - "to_symbol": "RUX" - }, - { - "id": "CZKSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "CZK/SEK", - "from_symbol": "CZK", - "to_symbol": "SEK" - }, - { - "id": "CZKTHX=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/THX", - "from_symbol": "CZK", - "to_symbol": "THX" - }, - { - "id": "CZKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "CZK/USD", - "from_symbol": "CZK", - "to_symbol": "USD" - }, - { - "id": "CZKXDR=X", - "currency": null, - "exchange": "CCY", - "name": "CZK/XDR", - "from_symbol": "CZK", - "to_symbol": "XDR" - }, - { - "id": "CZX=X", - "currency": "CZX", - "exchange": "CCY", - "name": "USD/CZX", - "from_symbol": "USD", - "to_symbol": "CZX" - }, - { - "id": "DJF=X", - "currency": "DJF", - "exchange": "CCY", - "name": "USD/DJF", - "from_symbol": "USD", - "to_symbol": "DJF" - }, - { - "id": "DJFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "DJF/BRX", - "from_symbol": "DJF", - "to_symbol": "BRX" - }, - { - "id": "DJFGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "DJF/GBP", - "from_symbol": "DJF", - "to_symbol": "GBP" - }, - { - "id": "DJFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "DJF/USD", - "from_symbol": "DJF", - "to_symbol": "USD" - }, - { - "id": "DKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "USD/DKK", - "from_symbol": "USD", - "to_symbol": "DKK" - }, - { - "id": "DKKANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "DKK/ANG", - "from_symbol": "DKK", - "to_symbol": "ANG" - }, - { - "id": "DKKAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "DKK/AUD", - "from_symbol": "DKK", - "to_symbol": "AUD" - }, - { - "id": "DKKBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "DKK/BAM", - "from_symbol": "DKK", - "to_symbol": "BAM" - }, - { - "id": "DKKBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "DKK/BRL", - "from_symbol": "DKK", - "to_symbol": "BRL" - }, - { - "id": "DKKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/BRX", - "from_symbol": "DKK", - "to_symbol": "BRX" - }, - { - "id": "DKKCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "DKK/CAD", - "from_symbol": "DKK", - "to_symbol": "CAD" - }, - { - "id": "DKKCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "DKK/CHF", - "from_symbol": "DKK", - "to_symbol": "CHF" - }, - { - "id": "DKKCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "DKK/CZK", - "from_symbol": "DKK", - "to_symbol": "CZK" - }, - { - "id": "DKKCZX=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/CZX", - "from_symbol": "DKK", - "to_symbol": "CZX" - }, - { - "id": "DKKEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "DKK/EUR", - "from_symbol": "DKK", - "to_symbol": "EUR" - }, - { - "id": "DKKGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "DKK/GBP", - "from_symbol": "DKK", - "to_symbol": "GBP" - }, - { - "id": "DKKGIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "DKK/GIP", - "from_symbol": "DKK", - "to_symbol": "GIP" - }, - { - "id": "DKKHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "DKK/HKD", - "from_symbol": "DKK", - "to_symbol": "HKD" - }, - { - "id": "DKKHRX=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/HRX", - "from_symbol": "DKK", - "to_symbol": "HRX" - }, - { - "id": "DKKHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "DKK/HUF", - "from_symbol": "DKK", - "to_symbol": "HUF" - }, - { - "id": "DKKHUX=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/HUX", - "from_symbol": "DKK", - "to_symbol": "HUX" - }, - { - "id": "DKKIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "DKK/IDR", - "from_symbol": "DKK", - "to_symbol": "IDR" - }, - { - "id": "DKKINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "DKK/INR", - "from_symbol": "DKK", - "to_symbol": "INR" - }, - { - "id": "DKKISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "DKK/ISK", - "from_symbol": "DKK", - "to_symbol": "ISK" - }, - { - "id": "DKKISX=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/ISX", - "from_symbol": "DKK", - "to_symbol": "ISX" - }, - { - "id": "DKKJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "DKK/JPY", - "from_symbol": "DKK", - "to_symbol": "JPY" - }, - { - "id": "DKKKPW=X", - "currency": "KPW", - "exchange": "CCY", - "name": "DKK/KPW", - "from_symbol": "DKK", - "to_symbol": "KPW" - }, - { - "id": "DKKKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "DKK/KRW", - "from_symbol": "DKK", - "to_symbol": "KRW" - }, - { - "id": "DKKLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "DKK/LKR", - "from_symbol": "DKK", - "to_symbol": "LKR" - }, - { - "id": "DKKNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "DKK/NOK", - "from_symbol": "DKK", - "to_symbol": "NOK" - }, - { - "id": "DKKNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "DKK/NZD", - "from_symbol": "DKK", - "to_symbol": "NZD" - }, - { - "id": "DKKOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "DKK/OMR", - "from_symbol": "DKK", - "to_symbol": "OMR" - }, - { - "id": "DKKPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "DKK/PKR", - "from_symbol": "DKK", - "to_symbol": "PKR" - }, - { - "id": "DKKPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "DKK/PLN", - "from_symbol": "DKK", - "to_symbol": "PLN" - }, - { - "id": "DKKPLX=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/PLX", - "from_symbol": "DKK", - "to_symbol": "PLX" - }, - { - "id": "DKKQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "DKK/QAR", - "from_symbol": "DKK", - "to_symbol": "QAR" - }, - { - "id": "DKKRSD=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/RSD", - "from_symbol": "DKK", - "to_symbol": "RSD" - }, - { - "id": "DKKRUX=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/RUX", - "from_symbol": "DKK", - "to_symbol": "RUX" - }, - { - "id": "DKKSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "DKK/SAR", - "from_symbol": "DKK", - "to_symbol": "SAR" - }, - { - "id": "DKKSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "DKK/SEK", - "from_symbol": "DKK", - "to_symbol": "SEK" - }, - { - "id": "DKKSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "DKK/SGD", - "from_symbol": "DKK", - "to_symbol": "SGD" - }, - { - "id": "DKKTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "DKK/THB", - "from_symbol": "DKK", - "to_symbol": "THB" - }, - { - "id": "DKKTHX=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/THX", - "from_symbol": "DKK", - "to_symbol": "THX" - }, - { - "id": "DKKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "DKK/USD", - "from_symbol": "DKK", - "to_symbol": "USD" - }, - { - "id": "DKKXCU=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/XCU", - "from_symbol": "DKK", - "to_symbol": "XCU" - }, - { - "id": "DKKXDR=X", - "currency": null, - "exchange": "CCY", - "name": "DKK/XDR", - "from_symbol": "DKK", - "to_symbol": "XDR" - }, - { - "id": "DKKZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "DKK/ZAR", - "from_symbol": "DKK", - "to_symbol": "ZAR" - }, - { - "id": "DKX=X", - "currency": "DKX", - "exchange": "CCY", - "name": "USD/DKX", - "from_symbol": "USD", - "to_symbol": "DKX" - }, - { - "id": "DOP=X", - "currency": "DOP", - "exchange": "CCY", - "name": "USD/DOP", - "from_symbol": "USD", - "to_symbol": "DOP" - }, - { - "id": "DOPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "DOP/BRX", - "from_symbol": "DOP", - "to_symbol": "BRX" - }, - { - "id": "DOPGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "DOP/GBP", - "from_symbol": "DOP", - "to_symbol": "GBP" - }, - { - "id": "DOPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "DOP/USD", - "from_symbol": "DOP", - "to_symbol": "USD" - }, - { - "id": "DZD=X", - "currency": "DZD", - "exchange": "CCY", - "name": "USD/DZD", - "from_symbol": "USD", - "to_symbol": "DZD" - }, - { - "id": "DZDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "DZD/BRX", - "from_symbol": "DZD", - "to_symbol": "BRX" - }, - { - "id": "DZDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "DZD/GBP", - "from_symbol": "DZD", - "to_symbol": "GBP" - }, - { - "id": "DZDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "DZD/USD", - "from_symbol": "DZD", - "to_symbol": "USD" - }, - { - "id": "DZDXDR=X", - "currency": null, - "exchange": "CCY", - "name": "DZD/XDR", - "from_symbol": "DZD", - "to_symbol": "XDR" - }, - { - "id": "EGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "USD/EGP", - "from_symbol": "USD", - "to_symbol": "EGP" - }, - { - "id": "EGPAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "EGP/AED", - "from_symbol": "EGP", - "to_symbol": "AED" - }, - { - "id": "EGPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "EGP/BRX", - "from_symbol": "EGP", - "to_symbol": "BRX" - }, - { - "id": "EGPCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "EGP/CHF", - "from_symbol": "EGP", - "to_symbol": "CHF" - }, - { - "id": "EGPEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "EGP/EUR", - "from_symbol": "EGP", - "to_symbol": "EUR" - }, - { - "id": "EGPGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "EGP/GBP", - "from_symbol": "EGP", - "to_symbol": "GBP" - }, - { - "id": "EGPJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "EGP/JPY", - "from_symbol": "EGP", - "to_symbol": "JPY" - }, - { - "id": "EGPMYX=X", - "currency": null, - "exchange": "CCY", - "name": "EGP/MYX", - "from_symbol": "EGP", - "to_symbol": "MYX" - }, - { - "id": "EGPPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "EGP/PKR", - "from_symbol": "EGP", - "to_symbol": "PKR" - }, - { - "id": "EGPTHX=X", - "currency": null, - "exchange": "CCY", - "name": "EGP/THX", - "from_symbol": "EGP", - "to_symbol": "THX" - }, - { - "id": "EGPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "EGP/USD", - "from_symbol": "EGP", - "to_symbol": "USD" - }, - { - "id": "EGPZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "EGP/ZAR", - "from_symbol": "EGP", - "to_symbol": "ZAR" - }, - { - "id": "ERN=X", - "currency": "ERN", - "exchange": "CCY", - "name": "USD/ERN", - "from_symbol": "USD", - "to_symbol": "ERN" - }, - { - "id": "ERNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ERN/BRX", - "from_symbol": "ERN", - "to_symbol": "BRX" - }, - { - "id": "ERNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ERN/USD", - "from_symbol": "ERN", - "to_symbol": "USD" - }, - { - "id": "ETB=X", - "currency": "ETB", - "exchange": "CCY", - "name": "USD/ETB", - "from_symbol": "USD", - "to_symbol": "ETB" - }, - { - "id": "ETBBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ETB/BRX", - "from_symbol": "ETB", - "to_symbol": "BRX" - }, - { - "id": "ETBGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "ETB/GBP", - "from_symbol": "ETB", - "to_symbol": "GBP" - }, - { - "id": "ETBUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ETB/USD", - "from_symbol": "ETB", - "to_symbol": "USD" - }, - { - "id": "EUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "USD/EUR", - "from_symbol": "USD", - "to_symbol": "EUR" - }, - { - "id": "EURAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "EUR/AED", - "from_symbol": "EUR", - "to_symbol": "AED" - }, - { - "id": "EURAFN=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/AFN", - "from_symbol": "EUR", - "to_symbol": "AFN" - }, - { - "id": "EURALL=X", - "currency": "ALL", - "exchange": "CCY", - "name": "EUR/ALL", - "from_symbol": "EUR", - "to_symbol": "ALL" - }, - { - "id": "EURAMD=X", - "currency": "AMD", - "exchange": "CCY", - "name": "EUR/AMD", - "from_symbol": "EUR", - "to_symbol": "AMD" - }, - { - "id": "EURANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "EUR/ANG", - "from_symbol": "EUR", - "to_symbol": "ANG" - }, - { - "id": "EURAOA=X", - "currency": "AOA", - "exchange": "CCY", - "name": "EUR/AOA", - "from_symbol": "EUR", - "to_symbol": "AOA" - }, - { - "id": "EURARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "EUR/ARS", - "from_symbol": "EUR", - "to_symbol": "ARS" - }, - { - "id": "EURAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "EUR/AUD", - "from_symbol": "EUR", - "to_symbol": "AUD" - }, - { - "id": "EURAWG=X", - "currency": "AWG", - "exchange": "CCY", - "name": "EUR/AWG", - "from_symbol": "EUR", - "to_symbol": "AWG" - }, - { - "id": "EURAZN=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/AZN", - "from_symbol": "EUR", - "to_symbol": "AZN" - }, - { - "id": "EURBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "EUR/BAM", - "from_symbol": "EUR", - "to_symbol": "BAM" - }, - { - "id": "EURBBD=X", - "currency": "BBD", - "exchange": "CCY", - "name": "EUR/BBD", - "from_symbol": "EUR", - "to_symbol": "BBD" - }, - { - "id": "EURBDT=X", - "currency": "BDT", - "exchange": "CCY", - "name": "EUR/BDT", - "from_symbol": "EUR", - "to_symbol": "BDT" - }, - { - "id": "EURBGN=X", - "currency": "BGN", - "exchange": "CCY", - "name": "EUR/BGN", - "from_symbol": "EUR", - "to_symbol": "BGN" - }, - { - "id": "EURBHD=X", - "currency": "BHD", - "exchange": "CCY", - "name": "EUR/BHD", - "from_symbol": "EUR", - "to_symbol": "BHD" - }, - { - "id": "EURBIF=X", - "currency": "BIF", - "exchange": "CCY", - "name": "EUR/BIF", - "from_symbol": "EUR", - "to_symbol": "BIF" - }, - { - "id": "EURBMD=X", - "currency": "BMD", - "exchange": "CCY", - "name": "EUR/BMD", - "from_symbol": "EUR", - "to_symbol": "BMD" - }, - { - "id": "EURBND=X", - "currency": "BND", - "exchange": "CCY", - "name": "EUR/BND", - "from_symbol": "EUR", - "to_symbol": "BND" - }, - { - "id": "EURBOB=X", - "currency": "BOB", - "exchange": "CCY", - "name": "EUR/BOB", - "from_symbol": "EUR", - "to_symbol": "BOB" - }, - { - "id": "EURBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "EUR/BRL", - "from_symbol": "EUR", - "to_symbol": "BRL" - }, - { - "id": "EURBRX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/BRX", - "from_symbol": "EUR", - "to_symbol": "BRX" - }, - { - "id": "EURBSD=X", - "currency": "BSD", - "exchange": "CCY", - "name": "EUR/BSD", - "from_symbol": "EUR", - "to_symbol": "BSD" - }, - { - "id": "EURBTN=X", - "currency": "BTN", - "exchange": "CCY", - "name": "EUR/BTN", - "from_symbol": "EUR", - "to_symbol": "BTN" - }, - { - "id": "EURBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "EUR/BWP", - "from_symbol": "EUR", - "to_symbol": "BWP" - }, - { - "id": "EURBYN=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/BYN", - "from_symbol": "EUR", - "to_symbol": "BYN" - }, - { - "id": "EURBZD=X", - "currency": "BZD", - "exchange": "CCY", - "name": "EUR/BZD", - "from_symbol": "EUR", - "to_symbol": "BZD" - }, - { - "id": "EURCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "EUR/CAD", - "from_symbol": "EUR", - "to_symbol": "CAD" - }, - { - "id": "EURCAX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/CAX", - "from_symbol": "EUR", - "to_symbol": "CAX" - }, - { - "id": "EURCDF=X", - "currency": "CDF", - "exchange": "CCY", - "name": "EUR/CDF", - "from_symbol": "EUR", - "to_symbol": "CDF" - }, - { - "id": "EURCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "EUR/CHF", - "from_symbol": "EUR", - "to_symbol": "CHF" - }, - { - "id": "EURCLF=X", - "currency": "CLF", - "exchange": "CCY", - "name": "EUR/CLF", - "from_symbol": "EUR", - "to_symbol": "CLF" - }, - { - "id": "EURCLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "EUR/CLP", - "from_symbol": "EUR", - "to_symbol": "CLP" - }, - { - "id": "EURCNH=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/CNH", - "from_symbol": "EUR", - "to_symbol": "CNH" - }, - { - "id": "EURCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "EUR/CNY", - "from_symbol": "EUR", - "to_symbol": "CNY" - }, - { - "id": "EURCOP=X", - "currency": "COP", - "exchange": "CCY", - "name": "EUR/COP", - "from_symbol": "EUR", - "to_symbol": "COP" - }, - { - "id": "EURCRC=X", - "currency": "CRC", - "exchange": "CCY", - "name": "EUR/CRC", - "from_symbol": "EUR", - "to_symbol": "CRC" - }, - { - "id": "EURCUP=X", - "currency": "CUP", - "exchange": "CCY", - "name": "EUR/CUP", - "from_symbol": "EUR", - "to_symbol": "CUP" - }, - { - "id": "EURCVE=X", - "currency": "CVE", - "exchange": "CCY", - "name": "EUR/CVE", - "from_symbol": "EUR", - "to_symbol": "CVE" - }, - { - "id": "EURCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "EUR/CZK", - "from_symbol": "EUR", - "to_symbol": "CZK" - }, - { - "id": "EURCZX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/CZX", - "from_symbol": "EUR", - "to_symbol": "CZX" - }, - { - "id": "EURDJF=X", - "currency": "DJF", - "exchange": "CCY", - "name": "EUR/DJF", - "from_symbol": "EUR", - "to_symbol": "DJF" - }, - { - "id": "EURDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "EUR/DKK", - "from_symbol": "EUR", - "to_symbol": "DKK" - }, - { - "id": "EURDKX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/DKX", - "from_symbol": "EUR", - "to_symbol": "DKX" - }, - { - "id": "EURDOP=X", - "currency": "DOP", - "exchange": "CCY", - "name": "EUR/DOP", - "from_symbol": "EUR", - "to_symbol": "DOP" - }, - { - "id": "EURDZD=X", - "currency": "DZD", - "exchange": "CCY", - "name": "EUR/DZD", - "from_symbol": "EUR", - "to_symbol": "DZD" - }, - { - "id": "EUREGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "EUR/EGP", - "from_symbol": "EUR", - "to_symbol": "EGP" - }, - { - "id": "EURERN=X", - "currency": "ERN", - "exchange": "CCY", - "name": "EUR/ERN", - "from_symbol": "EUR", - "to_symbol": "ERN" - }, - { - "id": "EURETB=X", - "currency": "ETB", - "exchange": "CCY", - "name": "EUR/ETB", - "from_symbol": "EUR", - "to_symbol": "ETB" - }, - { - "id": "EUREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "EUR/EUR", - "from_symbol": "EUR", - "to_symbol": "EUR" - }, - { - "id": "EURFJD=X", - "currency": "FJD", - "exchange": "CCY", - "name": "EUR/FJD", - "from_symbol": "EUR", - "to_symbol": "FJD" - }, - { - "id": "EURFKP=X", - "currency": "FKP", - "exchange": "CCY", - "name": "EUR/FKP", - "from_symbol": "EUR", - "to_symbol": "FKP" - }, - { - "id": "EURGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "EUR/GBP", - "from_symbol": "EUR", - "to_symbol": "GBP" - }, - { - "id": "EURGEL=X", - "currency": "GEL", - "exchange": "CCY", - "name": "EUR/GEL", - "from_symbol": "EUR", - "to_symbol": "GEL" - }, - { - "id": "EURGHS=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/GHS", - "from_symbol": "EUR", - "to_symbol": "GHS" - }, - { - "id": "EURGIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "EUR/GIP", - "from_symbol": "EUR", - "to_symbol": "GIP" - }, - { - "id": "EURGMD=X", - "currency": "GMD", - "exchange": "CCY", - "name": "EUR/GMD", - "from_symbol": "EUR", - "to_symbol": "GMD" - }, - { - "id": "EURGNF=X", - "currency": "GNF", - "exchange": "CCY", - "name": "EUR/GNF", - "from_symbol": "EUR", - "to_symbol": "GNF" - }, - { - "id": "EURGTQ=X", - "currency": "GTQ", - "exchange": "CCY", - "name": "EUR/GTQ", - "from_symbol": "EUR", - "to_symbol": "GTQ" - }, - { - "id": "EURGYD=X", - "currency": "GYD", - "exchange": "CCY", - "name": "EUR/GYD", - "from_symbol": "EUR", - "to_symbol": "GYD" - }, - { - "id": "EURHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "EUR/HKD", - "from_symbol": "EUR", - "to_symbol": "HKD" - }, - { - "id": "EURHNL=X", - "currency": "HNL", - "exchange": "CCY", - "name": "EUR/HNL", - "from_symbol": "EUR", - "to_symbol": "HNL" - }, - { - "id": "EURHRK=X", - "currency": "HRK", - "exchange": "CCY", - "name": "EUR/HRK", - "from_symbol": "EUR", - "to_symbol": "HRK" - }, - { - "id": "EURHRX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/HRX", - "from_symbol": "EUR", - "to_symbol": "HRX" - }, - { - "id": "EURHTG=X", - "currency": "HTG", - "exchange": "CCY", - "name": "EUR/HTG", - "from_symbol": "EUR", - "to_symbol": "HTG" - }, - { - "id": "EURHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "EUR/HUF", - "from_symbol": "EUR", - "to_symbol": "HUF" - }, - { - "id": "EURHUX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/HUX", - "from_symbol": "EUR", - "to_symbol": "HUX" - }, - { - "id": "EURIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "EUR/IDR", - "from_symbol": "EUR", - "to_symbol": "IDR" - }, - { - "id": "EURILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "EUR/ILS", - "from_symbol": "EUR", - "to_symbol": "ILS" - }, - { - "id": "EURINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "EUR/INR", - "from_symbol": "EUR", - "to_symbol": "INR" - }, - { - "id": "EURIQD=X", - "currency": "IQD", - "exchange": "CCY", - "name": "EUR/IQD", - "from_symbol": "EUR", - "to_symbol": "IQD" - }, - { - "id": "EURIRR=X", - "currency": "IRR", - "exchange": "CCY", - "name": "EUR/IRR", - "from_symbol": "EUR", - "to_symbol": "IRR" - }, - { - "id": "EURISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "EUR/ISK", - "from_symbol": "EUR", - "to_symbol": "ISK" - }, - { - "id": "EURISX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/ISX", - "from_symbol": "EUR", - "to_symbol": "ISX" - }, - { - "id": "EURJMD=X", - "currency": "JMD", - "exchange": "CCY", - "name": "EUR/JMD", - "from_symbol": "EUR", - "to_symbol": "JMD" - }, - { - "id": "EURJOD=X", - "currency": "JOD", - "exchange": "CCY", - "name": "EUR/JOD", - "from_symbol": "EUR", - "to_symbol": "JOD" - }, - { - "id": "EURJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "EUR/JPY", - "from_symbol": "EUR", - "to_symbol": "JPY" - }, - { - "id": "EURKES=X", - "currency": "KES", - "exchange": "CCY", - "name": "EUR/KES", - "from_symbol": "EUR", - "to_symbol": "KES" - }, - { - "id": "EURKGS=X", - "currency": "KGS", - "exchange": "CCY", - "name": "EUR/KGS", - "from_symbol": "EUR", - "to_symbol": "KGS" - }, - { - "id": "EURKHR=X", - "currency": "KHR", - "exchange": "CCY", - "name": "EUR/KHR", - "from_symbol": "EUR", - "to_symbol": "KHR" - }, - { - "id": "EURKMF=X", - "currency": "KMF", - "exchange": "CCY", - "name": "EUR/KMF", - "from_symbol": "EUR", - "to_symbol": "KMF" - }, - { - "id": "EURKPW=X", - "currency": "KPW", - "exchange": "CCY", - "name": "EUR/KPW", - "from_symbol": "EUR", - "to_symbol": "KPW" - }, - { - "id": "EURKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "EUR/KRW", - "from_symbol": "EUR", - "to_symbol": "KRW" - }, - { - "id": "EURKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "EUR/KWD", - "from_symbol": "EUR", - "to_symbol": "KWD" - }, - { - "id": "EURKYD=X", - "currency": "KYD", - "exchange": "CCY", - "name": "EUR/KYD", - "from_symbol": "EUR", - "to_symbol": "KYD" - }, - { - "id": "EURKZT=X", - "currency": "KZT", - "exchange": "CCY", - "name": "EUR/KZT", - "from_symbol": "EUR", - "to_symbol": "KZT" - }, - { - "id": "EURLAK=X", - "currency": "LAK", - "exchange": "CCY", - "name": "EUR/LAK", - "from_symbol": "EUR", - "to_symbol": "LAK" - }, - { - "id": "EURLBP=X", - "currency": "LBP", - "exchange": "CCY", - "name": "EUR/LBP", - "from_symbol": "EUR", - "to_symbol": "LBP" - }, - { - "id": "EURLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "EUR/LKR", - "from_symbol": "EUR", - "to_symbol": "LKR" - }, - { - "id": "EURLRD=X", - "currency": "LRD", - "exchange": "CCY", - "name": "EUR/LRD", - "from_symbol": "EUR", - "to_symbol": "LRD" - }, - { - "id": "EURLSL=X", - "currency": "LSL", - "exchange": "CCY", - "name": "EUR/LSL", - "from_symbol": "EUR", - "to_symbol": "LSL" - }, - { - "id": "EURLYD=X", - "currency": "LYD", - "exchange": "CCY", - "name": "EUR/LYD", - "from_symbol": "EUR", - "to_symbol": "LYD" - }, - { - "id": "EURMAD=X", - "currency": "MAD", - "exchange": "CCY", - "name": "EUR/MAD", - "from_symbol": "EUR", - "to_symbol": "MAD" - }, - { - "id": "EURMDL=X", - "currency": "MDL", - "exchange": "CCY", - "name": "EUR/MDL", - "from_symbol": "EUR", - "to_symbol": "MDL" - }, - { - "id": "EURMGA=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/MGA", - "from_symbol": "EUR", - "to_symbol": "MGA" - }, - { - "id": "EURMKD=X", - "currency": "MKD", - "exchange": "CCY", - "name": "EUR/MKD", - "from_symbol": "EUR", - "to_symbol": "MKD" - }, - { - "id": "EURMMK=X", - "currency": "MMK", - "exchange": "CCY", - "name": "EUR/MMK", - "from_symbol": "EUR", - "to_symbol": "MMK" - }, - { - "id": "EURMNT=X", - "currency": "MNT", - "exchange": "CCY", - "name": "EUR/MNT", - "from_symbol": "EUR", - "to_symbol": "MNT" - }, - { - "id": "EURMOP=X", - "currency": "MOP", - "exchange": "CCY", - "name": "EUR/MOP", - "from_symbol": "EUR", - "to_symbol": "MOP" - }, - { - "id": "EURMRU=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/MRU", - "from_symbol": "EUR", - "to_symbol": "MRU" - }, - { - "id": "EURMUR=X", - "currency": "MUR", - "exchange": "CCY", - "name": "EUR/MUR", - "from_symbol": "EUR", - "to_symbol": "MUR" - }, - { - "id": "EURMVR=X", - "currency": "MVR", - "exchange": "CCY", - "name": "EUR/MVR", - "from_symbol": "EUR", - "to_symbol": "MVR" - }, - { - "id": "EURMWK=X", - "currency": "MWK", - "exchange": "CCY", - "name": "EUR/MWK", - "from_symbol": "EUR", - "to_symbol": "MWK" - }, - { - "id": "EURMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "EUR/MXN", - "from_symbol": "EUR", - "to_symbol": "MXN" - }, - { - "id": "EURMXX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/MXX", - "from_symbol": "EUR", - "to_symbol": "MXX" - }, - { - "id": "EURMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "EUR/MYR", - "from_symbol": "EUR", - "to_symbol": "MYR" - }, - { - "id": "EURMYX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/MYX", - "from_symbol": "EUR", - "to_symbol": "MYX" - }, - { - "id": "EURMZN=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/MZN", - "from_symbol": "EUR", - "to_symbol": "MZN" - }, - { - "id": "EURNAD=X", - "currency": "NAD", - "exchange": "CCY", - "name": "EUR/NAD", - "from_symbol": "EUR", - "to_symbol": "NAD" - }, - { - "id": "EURNGN=X", - "currency": "NGN", - "exchange": "CCY", - "name": "EUR/NGN", - "from_symbol": "EUR", - "to_symbol": "NGN" - }, - { - "id": "EURNIO=X", - "currency": "NIO", - "exchange": "CCY", - "name": "EUR/NIO", - "from_symbol": "EUR", - "to_symbol": "NIO" - }, - { - "id": "EURNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "EUR/NOK", - "from_symbol": "EUR", - "to_symbol": "NOK" - }, - { - "id": "EURNPR=X", - "currency": "NPR", - "exchange": "CCY", - "name": "EUR/NPR", - "from_symbol": "EUR", - "to_symbol": "NPR" - }, - { - "id": "EURNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "EUR/NZD", - "from_symbol": "EUR", - "to_symbol": "NZD" - }, - { - "id": "EURPAB=X", - "currency": "PAB", - "exchange": "CCY", - "name": "EUR/PAB", - "from_symbol": "EUR", - "to_symbol": "PAB" - }, - { - "id": "EURPEN=X", - "currency": "PEN", - "exchange": "CCY", - "name": "EUR/PEN", - "from_symbol": "EUR", - "to_symbol": "PEN" - }, - { - "id": "EURPGK=X", - "currency": "PGK", - "exchange": "CCY", - "name": "EUR/PGK", - "from_symbol": "EUR", - "to_symbol": "PGK" - }, - { - "id": "EURPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "EUR/PHP", - "from_symbol": "EUR", - "to_symbol": "PHP" - }, - { - "id": "EURPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "EUR/PKR", - "from_symbol": "EUR", - "to_symbol": "PKR" - }, - { - "id": "EURPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "EUR/PLN", - "from_symbol": "EUR", - "to_symbol": "PLN" - }, - { - "id": "EURPLX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/PLX", - "from_symbol": "EUR", - "to_symbol": "PLX" - }, - { - "id": "EURPYG=X", - "currency": "PYG", - "exchange": "CCY", - "name": "EUR/PYG", - "from_symbol": "EUR", - "to_symbol": "PYG" - }, - { - "id": "EURQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "EUR/QAR", - "from_symbol": "EUR", - "to_symbol": "QAR" - }, - { - "id": "EURRON=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/RON", - "from_symbol": "EUR", - "to_symbol": "RON" - }, - { - "id": "EURRSD=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/RSD", - "from_symbol": "EUR", - "to_symbol": "RSD" - }, - { - "id": "EURRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "EUR/RUB", - "from_symbol": "EUR", - "to_symbol": "RUB" - }, - { - "id": "EURRUX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/RUX", - "from_symbol": "EUR", - "to_symbol": "RUX" - }, - { - "id": "EURRWF=X", - "currency": "RWF", - "exchange": "CCY", - "name": "EUR/RWF", - "from_symbol": "EUR", - "to_symbol": "RWF" - }, - { - "id": "EURSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "EUR/SAR", - "from_symbol": "EUR", - "to_symbol": "SAR" - }, - { - "id": "EURSBD=X", - "currency": "SBD", - "exchange": "CCY", - "name": "EUR/SBD", - "from_symbol": "EUR", - "to_symbol": "SBD" - }, - { - "id": "EURSCR=X", - "currency": "SCR", - "exchange": "CCY", - "name": "EUR/SCR", - "from_symbol": "EUR", - "to_symbol": "SCR" - }, - { - "id": "EURSDG=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/SDG", - "from_symbol": "EUR", - "to_symbol": "SDG" - }, - { - "id": "EURSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "EUR/SEK", - "from_symbol": "EUR", - "to_symbol": "SEK" - }, - { - "id": "EURSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "EUR/SGD", - "from_symbol": "EUR", - "to_symbol": "SGD" - }, - { - "id": "EURSHP=X", - "currency": "SHP", - "exchange": "CCY", - "name": "EUR/SHP", - "from_symbol": "EUR", - "to_symbol": "SHP" - }, - { - "id": "EURSLL=X", - "currency": "SLL", - "exchange": "CCY", - "name": "EUR/SLL", - "from_symbol": "EUR", - "to_symbol": "SLL" - }, - { - "id": "EURSOS=X", - "currency": "SOS", - "exchange": "CCY", - "name": "EUR/SOS", - "from_symbol": "EUR", - "to_symbol": "SOS" - }, - { - "id": "EURSRD=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/SRD", - "from_symbol": "EUR", - "to_symbol": "SRD" - }, - { - "id": "EURSTN=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/STN", - "from_symbol": "EUR", - "to_symbol": "STN" - }, - { - "id": "EURSVC=X", - "currency": "SVC", - "exchange": "CCY", - "name": "EUR/SVC", - "from_symbol": "EUR", - "to_symbol": "SVC" - }, - { - "id": "EURSYP=X", - "currency": "SYP", - "exchange": "CCY", - "name": "EUR/SYP", - "from_symbol": "EUR", - "to_symbol": "SYP" - }, - { - "id": "EURSZL=X", - "currency": "SZL", - "exchange": "CCY", - "name": "EUR/SZL", - "from_symbol": "EUR", - "to_symbol": "SZL" - }, - { - "id": "EURTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "EUR/THB", - "from_symbol": "EUR", - "to_symbol": "THB" - }, - { - "id": "EURTHX=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/THX", - "from_symbol": "EUR", - "to_symbol": "THX" - }, - { - "id": "EURTJS=X", - "currency": "TJS", - "exchange": "CCY", - "name": "EUR/TJS", - "from_symbol": "EUR", - "to_symbol": "TJS" - }, - { - "id": "EURTMT=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/TMT", - "from_symbol": "EUR", - "to_symbol": "TMT" - }, - { - "id": "EURTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "EUR/TND", - "from_symbol": "EUR", - "to_symbol": "TND" - }, - { - "id": "EURTOP=X", - "currency": "TOP", - "exchange": "CCY", - "name": "EUR/TOP", - "from_symbol": "EUR", - "to_symbol": "TOP" - }, - { - "id": "EURTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "EUR/TRY", - "from_symbol": "EUR", - "to_symbol": "TRY" - }, - { - "id": "EURTTD=X", - "currency": "TTD", - "exchange": "CCY", - "name": "EUR/TTD", - "from_symbol": "EUR", - "to_symbol": "TTD" - }, - { - "id": "EURTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "EUR/TWD", - "from_symbol": "EUR", - "to_symbol": "TWD" - }, - { - "id": "EURTZS=X", - "currency": "TZS", - "exchange": "CCY", - "name": "EUR/TZS", - "from_symbol": "EUR", - "to_symbol": "TZS" - }, - { - "id": "EURUAH=X", - "currency": "UAH", - "exchange": "CCY", - "name": "EUR/UAH", - "from_symbol": "EUR", - "to_symbol": "UAH" - }, - { - "id": "EURUGX=X", - "currency": "UGX", - "exchange": "CCY", - "name": "EUR/UGX", - "from_symbol": "EUR", - "to_symbol": "UGX" - }, - { - "id": "EURUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "EUR/USD", - "from_symbol": "EUR", - "to_symbol": "USD" - }, - { - "id": "EURUYU=X", - "currency": "UYU", - "exchange": "CCY", - "name": "EUR/UYU", - "from_symbol": "EUR", - "to_symbol": "UYU" - }, - { - "id": "EURUZS=X", - "currency": "UZS", - "exchange": "CCY", - "name": "EUR/UZS", - "from_symbol": "EUR", - "to_symbol": "UZS" - }, - { - "id": "EURVES=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/VES", - "from_symbol": "EUR", - "to_symbol": "VES" - }, - { - "id": "EURVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "EUR/VND", - "from_symbol": "EUR", - "to_symbol": "VND" - }, - { - "id": "EURVUV=X", - "currency": "VUV", - "exchange": "CCY", - "name": "EUR/VUV", - "from_symbol": "EUR", - "to_symbol": "VUV" - }, - { - "id": "EURWST=X", - "currency": "WST", - "exchange": "CCY", - "name": "EUR/WST", - "from_symbol": "EUR", - "to_symbol": "WST" - }, - { - "id": "EURXAF=X", - "currency": "XAF", - "exchange": "CCY", - "name": "EUR/XAF", - "from_symbol": "EUR", - "to_symbol": "XAF" - }, - { - "id": "EURXCD=X", - "currency": "XCD", - "exchange": "CCY", - "name": "EUR/XCD", - "from_symbol": "EUR", - "to_symbol": "XCD" - }, - { - "id": "EURXCU=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/XCU", - "from_symbol": "EUR", - "to_symbol": "XCU" - }, - { - "id": "EURXDR=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/XDR", - "from_symbol": "EUR", - "to_symbol": "XDR" - }, - { - "id": "EURXOF=X", - "currency": "XOF", - "exchange": "CCY", - "name": "EUR/XOF", - "from_symbol": "EUR", - "to_symbol": "XOF" - }, - { - "id": "EURXPF=X", - "currency": "XPF", - "exchange": "CCY", - "name": "EUR/XPF", - "from_symbol": "EUR", - "to_symbol": "XPF" - }, - { - "id": "EURYER=X", - "currency": "YER", - "exchange": "CCY", - "name": "EUR/YER", - "from_symbol": "EUR", - "to_symbol": "YER" - }, - { - "id": "EURZAC=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/ZAC", - "from_symbol": "EUR", - "to_symbol": "ZAC" - }, - { - "id": "EURZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "EUR/ZAR", - "from_symbol": "EUR", - "to_symbol": "ZAR" - }, - { - "id": "EURZMW=X", - "currency": null, - "exchange": "CCY", - "name": "EUR/ZMW", - "from_symbol": "EUR", - "to_symbol": "ZMW" - }, - { - "id": "EUXAUD=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/AUD", - "from_symbol": "EUX", - "to_symbol": "AUD" - }, - { - "id": "EUXBGN=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/BGN", - "from_symbol": "EUX", - "to_symbol": "BGN" - }, - { - "id": "EUXBRL=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/BRL", - "from_symbol": "EUX", - "to_symbol": "BRL" - }, - { - "id": "EUXCAD=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/CAD", - "from_symbol": "EUX", - "to_symbol": "CAD" - }, - { - "id": "EUXCHF=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/CHF", - "from_symbol": "EUX", - "to_symbol": "CHF" - }, - { - "id": "EUXCNY=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/CNY", - "from_symbol": "EUX", - "to_symbol": "CNY" - }, - { - "id": "EUXCZK=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/CZK", - "from_symbol": "EUX", - "to_symbol": "CZK" - }, - { - "id": "EUXDKK=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/DKK", - "from_symbol": "EUX", - "to_symbol": "DKK" - }, - { - "id": "EUXGBP=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/GBP", - "from_symbol": "EUX", - "to_symbol": "GBP" - }, - { - "id": "EUXHKD=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/HKD", - "from_symbol": "EUX", - "to_symbol": "HKD" - }, - { - "id": "EUXHRK=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/HRK", - "from_symbol": "EUX", - "to_symbol": "HRK" - }, - { - "id": "EUXHUF=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/HUF", - "from_symbol": "EUX", - "to_symbol": "HUF" - }, - { - "id": "EUXIDR=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/IDR", - "from_symbol": "EUX", - "to_symbol": "IDR" - }, - { - "id": "EUXILS=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/ILS", - "from_symbol": "EUX", - "to_symbol": "ILS" - }, - { - "id": "EUXINR=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/INR", - "from_symbol": "EUX", - "to_symbol": "INR" - }, - { - "id": "EUXISK=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/ISK", - "from_symbol": "EUX", - "to_symbol": "ISK" - }, - { - "id": "EUXJPY=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/JPY", - "from_symbol": "EUX", - "to_symbol": "JPY" - }, - { - "id": "EUXKRW=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/KRW", - "from_symbol": "EUX", - "to_symbol": "KRW" - }, - { - "id": "EUXMXN=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/MXN", - "from_symbol": "EUX", - "to_symbol": "MXN" - }, - { - "id": "EUXMYR=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/MYR", - "from_symbol": "EUX", - "to_symbol": "MYR" - }, - { - "id": "EUXNOK=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/NOK", - "from_symbol": "EUX", - "to_symbol": "NOK" - }, - { - "id": "EUXNZD=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/NZD", - "from_symbol": "EUX", - "to_symbol": "NZD" - }, - { - "id": "EUXPHP=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/PHP", - "from_symbol": "EUX", - "to_symbol": "PHP" - }, - { - "id": "EUXPLN=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/PLN", - "from_symbol": "EUX", - "to_symbol": "PLN" - }, - { - "id": "EUXRON=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/RON", - "from_symbol": "EUX", - "to_symbol": "RON" - }, - { - "id": "EUXRUB=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/RUB", - "from_symbol": "EUX", - "to_symbol": "RUB" - }, - { - "id": "EUXSEK=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/SEK", - "from_symbol": "EUX", - "to_symbol": "SEK" - }, - { - "id": "EUXSGD=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/SGD", - "from_symbol": "EUX", - "to_symbol": "SGD" - }, - { - "id": "EUXTHB=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/THB", - "from_symbol": "EUX", - "to_symbol": "THB" - }, - { - "id": "EUXTRY=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/TRY", - "from_symbol": "EUX", - "to_symbol": "TRY" - }, - { - "id": "EUXZAR=X", - "currency": null, - "exchange": "CCY", - "name": "EUX/ZAR", - "from_symbol": "EUX", - "to_symbol": "ZAR" - }, - { - "id": "FJD=X", - "currency": "FJD", - "exchange": "CCY", - "name": "USD/FJD", - "from_symbol": "USD", - "to_symbol": "FJD" - }, - { - "id": "FJDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "FJD/BRX", - "from_symbol": "FJD", - "to_symbol": "BRX" - }, - { - "id": "FJDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "FJD/HKD", - "from_symbol": "FJD", - "to_symbol": "HKD" - }, - { - "id": "FKP=X", - "currency": "FKP", - "exchange": "CCY", - "name": "USD/FKP", - "from_symbol": "USD", - "to_symbol": "FKP" - }, - { - "id": "FKPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "FKP/BRX", - "from_symbol": "FKP", - "to_symbol": "BRX" - }, - { - "id": "GBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "USD/GBP", - "from_symbol": "USD", - "to_symbol": "GBP" - }, - { - "id": "GBPAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "GBP/AED", - "from_symbol": "GBP", - "to_symbol": "AED" - }, - { - "id": "GBPAFN=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/AFN", - "from_symbol": "GBP", - "to_symbol": "AFN" - }, - { - "id": "GBPALL=X", - "currency": "ALL", - "exchange": "CCY", - "name": "GBP/ALL", - "from_symbol": "GBP", - "to_symbol": "ALL" - }, - { - "id": "GBPAMD=X", - "currency": "AMD", - "exchange": "CCY", - "name": "GBP/AMD", - "from_symbol": "GBP", - "to_symbol": "AMD" - }, - { - "id": "GBPANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "GBP/ANG", - "from_symbol": "GBP", - "to_symbol": "ANG" - }, - { - "id": "GBPAOA=X", - "currency": "AOA", - "exchange": "CCY", - "name": "GBP/AOA", - "from_symbol": "GBP", - "to_symbol": "AOA" - }, - { - "id": "GBPARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "GBP/ARS", - "from_symbol": "GBP", - "to_symbol": "ARS" - }, - { - "id": "GBPAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "GBP/AUD", - "from_symbol": "GBP", - "to_symbol": "AUD" - }, - { - "id": "GBPAWG=X", - "currency": "AWG", - "exchange": "CCY", - "name": "GBP/AWG", - "from_symbol": "GBP", - "to_symbol": "AWG" - }, - { - "id": "GBPAZN=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/AZN", - "from_symbol": "GBP", - "to_symbol": "AZN" - }, - { - "id": "GBPBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "GBP/BAM", - "from_symbol": "GBP", - "to_symbol": "BAM" - }, - { - "id": "GBPBBD=X", - "currency": "BBD", - "exchange": "CCY", - "name": "GBP/BBD", - "from_symbol": "GBP", - "to_symbol": "BBD" - }, - { - "id": "GBPBDT=X", - "currency": "BDT", - "exchange": "CCY", - "name": "GBP/BDT", - "from_symbol": "GBP", - "to_symbol": "BDT" - }, - { - "id": "GBPBGN=X", - "currency": "BGN", - "exchange": "CCY", - "name": "GBP/BGN", - "from_symbol": "GBP", - "to_symbol": "BGN" - }, - { - "id": "GBPBHD=X", - "currency": "BHD", - "exchange": "CCY", - "name": "GBP/BHD", - "from_symbol": "GBP", - "to_symbol": "BHD" - }, - { - "id": "GBPBIF=X", - "currency": "BIF", - "exchange": "CCY", - "name": "GBP/BIF", - "from_symbol": "GBP", - "to_symbol": "BIF" - }, - { - "id": "GBPBMD=X", - "currency": "BMD", - "exchange": "CCY", - "name": "GBP/BMD", - "from_symbol": "GBP", - "to_symbol": "BMD" - }, - { - "id": "GBPBND=X", - "currency": "BND", - "exchange": "CCY", - "name": "GBP/BND", - "from_symbol": "GBP", - "to_symbol": "BND" - }, - { - "id": "GBPBOB=X", - "currency": "BOB", - "exchange": "CCY", - "name": "GBP/BOB", - "from_symbol": "GBP", - "to_symbol": "BOB" - }, - { - "id": "GBPBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "GBP/BRL", - "from_symbol": "GBP", - "to_symbol": "BRL" - }, - { - "id": "GBPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/BRX", - "from_symbol": "GBP", - "to_symbol": "BRX" - }, - { - "id": "GBPBSD=X", - "currency": "BSD", - "exchange": "CCY", - "name": "GBP/BSD", - "from_symbol": "GBP", - "to_symbol": "BSD" - }, - { - "id": "GBPBTN=X", - "currency": "BTN", - "exchange": "CCY", - "name": "GBP/BTN", - "from_symbol": "GBP", - "to_symbol": "BTN" - }, - { - "id": "GBPBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "GBP/BWP", - "from_symbol": "GBP", - "to_symbol": "BWP" - }, - { - "id": "GBPBYN=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/BYN", - "from_symbol": "GBP", - "to_symbol": "BYN" - }, - { - "id": "GBPBZD=X", - "currency": "BZD", - "exchange": "CCY", - "name": "GBP/BZD", - "from_symbol": "GBP", - "to_symbol": "BZD" - }, - { - "id": "GBPCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "GBP/CAD", - "from_symbol": "GBP", - "to_symbol": "CAD" - }, - { - "id": "GBPCAX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/CAX", - "from_symbol": "GBP", - "to_symbol": "CAX" - }, - { - "id": "GBPCDF=X", - "currency": "CDF", - "exchange": "CCY", - "name": "GBP/CDF", - "from_symbol": "GBP", - "to_symbol": "CDF" - }, - { - "id": "GBPCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "GBP/CHF", - "from_symbol": "GBP", - "to_symbol": "CHF" - }, - { - "id": "GBPCLF=X", - "currency": "CLF", - "exchange": "CCY", - "name": "GBP/CLF", - "from_symbol": "GBP", - "to_symbol": "CLF" - }, - { - "id": "GBPCLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "GBP/CLP", - "from_symbol": "GBP", - "to_symbol": "CLP" - }, - { - "id": "GBPCNH=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/CNH", - "from_symbol": "GBP", - "to_symbol": "CNH" - }, - { - "id": "GBPCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "GBP/CNY", - "from_symbol": "GBP", - "to_symbol": "CNY" - }, - { - "id": "GBPCOP=X", - "currency": "COP", - "exchange": "CCY", - "name": "GBP/COP", - "from_symbol": "GBP", - "to_symbol": "COP" - }, - { - "id": "GBPCRC=X", - "currency": "CRC", - "exchange": "CCY", - "name": "GBP/CRC", - "from_symbol": "GBP", - "to_symbol": "CRC" - }, - { - "id": "GBPCUP=X", - "currency": "CUP", - "exchange": "CCY", - "name": "GBP/CUP", - "from_symbol": "GBP", - "to_symbol": "CUP" - }, - { - "id": "GBPCVE=X", - "currency": "CVE", - "exchange": "CCY", - "name": "GBP/CVE", - "from_symbol": "GBP", - "to_symbol": "CVE" - }, - { - "id": "GBPCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "GBP/CZK", - "from_symbol": "GBP", - "to_symbol": "CZK" - }, - { - "id": "GBPCZX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/CZX", - "from_symbol": "GBP", - "to_symbol": "CZX" - }, - { - "id": "GBPDJF=X", - "currency": "DJF", - "exchange": "CCY", - "name": "GBP/DJF", - "from_symbol": "GBP", - "to_symbol": "DJF" - }, - { - "id": "GBPDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "GBP/DKK", - "from_symbol": "GBP", - "to_symbol": "DKK" - }, - { - "id": "GBPDKX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/DKX", - "from_symbol": "GBP", - "to_symbol": "DKX" - }, - { - "id": "GBPDOP=X", - "currency": "DOP", - "exchange": "CCY", - "name": "GBP/DOP", - "from_symbol": "GBP", - "to_symbol": "DOP" - }, - { - "id": "GBPDZD=X", - "currency": "DZD", - "exchange": "CCY", - "name": "GBP/DZD", - "from_symbol": "GBP", - "to_symbol": "DZD" - }, - { - "id": "GBPEGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "GBP/EGP", - "from_symbol": "GBP", - "to_symbol": "EGP" - }, - { - "id": "GBPERN=X", - "currency": "ERN", - "exchange": "CCY", - "name": "GBP/ERN", - "from_symbol": "GBP", - "to_symbol": "ERN" - }, - { - "id": "GBPETB=X", - "currency": "ETB", - "exchange": "CCY", - "name": "GBP/ETB", - "from_symbol": "GBP", - "to_symbol": "ETB" - }, - { - "id": "GBPEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "GBP/EUR", - "from_symbol": "GBP", - "to_symbol": "EUR" - }, - { - "id": "GBPFJD=X", - "currency": "FJD", - "exchange": "CCY", - "name": "GBP/FJD", - "from_symbol": "GBP", - "to_symbol": "FJD" - }, - { - "id": "GBPFKP=X", - "currency": "FKP", - "exchange": "CCY", - "name": "GBP/FKP", - "from_symbol": "GBP", - "to_symbol": "FKP" - }, - { - "id": "GBPGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "GBP/GBP", - "from_symbol": "GBP", - "to_symbol": "GBP" - }, - { - "id": "GBPGEL=X", - "currency": "GEL", - "exchange": "CCY", - "name": "GBP/GEL", - "from_symbol": "GBP", - "to_symbol": "GEL" - }, - { - "id": "GBPGHS=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/GHS", - "from_symbol": "GBP", - "to_symbol": "GHS" - }, - { - "id": "GBPGIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "GBP/GIP", - "from_symbol": "GBP", - "to_symbol": "GIP" - }, - { - "id": "GBPGMD=X", - "currency": "GMD", - "exchange": "CCY", - "name": "GBP/GMD", - "from_symbol": "GBP", - "to_symbol": "GMD" - }, - { - "id": "GBPGNF=X", - "currency": "GNF", - "exchange": "CCY", - "name": "GBP/GNF", - "from_symbol": "GBP", - "to_symbol": "GNF" - }, - { - "id": "GBPGTQ=X", - "currency": "GTQ", - "exchange": "CCY", - "name": "GBP/GTQ", - "from_symbol": "GBP", - "to_symbol": "GTQ" - }, - { - "id": "GBPGYD=X", - "currency": "GYD", - "exchange": "CCY", - "name": "GBP/GYD", - "from_symbol": "GBP", - "to_symbol": "GYD" - }, - { - "id": "GBPHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "GBP/HKD", - "from_symbol": "GBP", - "to_symbol": "HKD" - }, - { - "id": "GBPHNL=X", - "currency": "HNL", - "exchange": "CCY", - "name": "GBP/HNL", - "from_symbol": "GBP", - "to_symbol": "HNL" - }, - { - "id": "GBPHRK=X", - "currency": "HRK", - "exchange": "CCY", - "name": "GBP/HRK", - "from_symbol": "GBP", - "to_symbol": "HRK" - }, - { - "id": "GBPHRX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/HRX", - "from_symbol": "GBP", - "to_symbol": "HRX" - }, - { - "id": "GBPHTG=X", - "currency": "HTG", - "exchange": "CCY", - "name": "GBP/HTG", - "from_symbol": "GBP", - "to_symbol": "HTG" - }, - { - "id": "GBPHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "GBP/HUF", - "from_symbol": "GBP", - "to_symbol": "HUF" - }, - { - "id": "GBPHUX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/HUX", - "from_symbol": "GBP", - "to_symbol": "HUX" - }, - { - "id": "GBPIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "GBP/IDR", - "from_symbol": "GBP", - "to_symbol": "IDR" - }, - { - "id": "GBPILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "GBP/ILS", - "from_symbol": "GBP", - "to_symbol": "ILS" - }, - { - "id": "GBPINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "GBP/INR", - "from_symbol": "GBP", - "to_symbol": "INR" - }, - { - "id": "GBPIQD=X", - "currency": "IQD", - "exchange": "CCY", - "name": "GBP/IQD", - "from_symbol": "GBP", - "to_symbol": "IQD" - }, - { - "id": "GBPIRR=X", - "currency": "IRR", - "exchange": "CCY", - "name": "GBP/IRR", - "from_symbol": "GBP", - "to_symbol": "IRR" - }, - { - "id": "GBPISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "GBP/ISK", - "from_symbol": "GBP", - "to_symbol": "ISK" - }, - { - "id": "GBPISX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/ISX", - "from_symbol": "GBP", - "to_symbol": "ISX" - }, - { - "id": "GBPJMD=X", - "currency": "JMD", - "exchange": "CCY", - "name": "GBP/JMD", - "from_symbol": "GBP", - "to_symbol": "JMD" - }, - { - "id": "GBPJOD=X", - "currency": "JOD", - "exchange": "CCY", - "name": "GBP/JOD", - "from_symbol": "GBP", - "to_symbol": "JOD" - }, - { - "id": "GBPJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "GBP/JPY", - "from_symbol": "GBP", - "to_symbol": "JPY" - }, - { - "id": "GBPKES=X", - "currency": "KES", - "exchange": "CCY", - "name": "GBP/KES", - "from_symbol": "GBP", - "to_symbol": "KES" - }, - { - "id": "GBPKGS=X", - "currency": "KGS", - "exchange": "CCY", - "name": "GBP/KGS", - "from_symbol": "GBP", - "to_symbol": "KGS" - }, - { - "id": "GBPKHR=X", - "currency": "KHR", - "exchange": "CCY", - "name": "GBP/KHR", - "from_symbol": "GBP", - "to_symbol": "KHR" - }, - { - "id": "GBPKMF=X", - "currency": "KMF", - "exchange": "CCY", - "name": "GBP/KMF", - "from_symbol": "GBP", - "to_symbol": "KMF" - }, - { - "id": "GBPKPW=X", - "currency": "KPW", - "exchange": "CCY", - "name": "GBP/KPW", - "from_symbol": "GBP", - "to_symbol": "KPW" - }, - { - "id": "GBPKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "GBP/KRW", - "from_symbol": "GBP", - "to_symbol": "KRW" - }, - { - "id": "GBPKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "GBP/KWD", - "from_symbol": "GBP", - "to_symbol": "KWD" - }, - { - "id": "GBPKYD=X", - "currency": "KYD", - "exchange": "CCY", - "name": "GBP/KYD", - "from_symbol": "GBP", - "to_symbol": "KYD" - }, - { - "id": "GBPKZT=X", - "currency": "KZT", - "exchange": "CCY", - "name": "GBP/KZT", - "from_symbol": "GBP", - "to_symbol": "KZT" - }, - { - "id": "GBPLAK=X", - "currency": "LAK", - "exchange": "CCY", - "name": "GBP/LAK", - "from_symbol": "GBP", - "to_symbol": "LAK" - }, - { - "id": "GBPLBP=X", - "currency": "LBP", - "exchange": "CCY", - "name": "GBP/LBP", - "from_symbol": "GBP", - "to_symbol": "LBP" - }, - { - "id": "GBPLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "GBP/LKR", - "from_symbol": "GBP", - "to_symbol": "LKR" - }, - { - "id": "GBPLRD=X", - "currency": "LRD", - "exchange": "CCY", - "name": "GBP/LRD", - "from_symbol": "GBP", - "to_symbol": "LRD" - }, - { - "id": "GBPLSL=X", - "currency": "LSL", - "exchange": "CCY", - "name": "GBP/LSL", - "from_symbol": "GBP", - "to_symbol": "LSL" - }, - { - "id": "GBPLYD=X", - "currency": "LYD", - "exchange": "CCY", - "name": "GBP/LYD", - "from_symbol": "GBP", - "to_symbol": "LYD" - }, - { - "id": "GBPMAD=X", - "currency": "MAD", - "exchange": "CCY", - "name": "GBP/MAD", - "from_symbol": "GBP", - "to_symbol": "MAD" - }, - { - "id": "GBPMDL=X", - "currency": "MDL", - "exchange": "CCY", - "name": "GBP/MDL", - "from_symbol": "GBP", - "to_symbol": "MDL" - }, - { - "id": "GBPMGA=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/MGA", - "from_symbol": "GBP", - "to_symbol": "MGA" - }, - { - "id": "GBPMKD=X", - "currency": "MKD", - "exchange": "CCY", - "name": "GBP/MKD", - "from_symbol": "GBP", - "to_symbol": "MKD" - }, - { - "id": "GBPMMK=X", - "currency": "MMK", - "exchange": "CCY", - "name": "GBP/MMK", - "from_symbol": "GBP", - "to_symbol": "MMK" - }, - { - "id": "GBPMNT=X", - "currency": "MNT", - "exchange": "CCY", - "name": "GBP/MNT", - "from_symbol": "GBP", - "to_symbol": "MNT" - }, - { - "id": "GBPMOP=X", - "currency": "MOP", - "exchange": "CCY", - "name": "GBP/MOP", - "from_symbol": "GBP", - "to_symbol": "MOP" - }, - { - "id": "GBPMRU=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/MRU", - "from_symbol": "GBP", - "to_symbol": "MRU" - }, - { - "id": "GBPMUR=X", - "currency": "MUR", - "exchange": "CCY", - "name": "GBP/MUR", - "from_symbol": "GBP", - "to_symbol": "MUR" - }, - { - "id": "GBPMVR=X", - "currency": "MVR", - "exchange": "CCY", - "name": "GBP/MVR", - "from_symbol": "GBP", - "to_symbol": "MVR" - }, - { - "id": "GBPMWK=X", - "currency": "MWK", - "exchange": "CCY", - "name": "GBP/MWK", - "from_symbol": "GBP", - "to_symbol": "MWK" - }, - { - "id": "GBPMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "GBP/MXN", - "from_symbol": "GBP", - "to_symbol": "MXN" - }, - { - "id": "GBPMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "GBP/MYR", - "from_symbol": "GBP", - "to_symbol": "MYR" - }, - { - "id": "GBPMYX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/MYX", - "from_symbol": "GBP", - "to_symbol": "MYX" - }, - { - "id": "GBPMZN=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/MZN", - "from_symbol": "GBP", - "to_symbol": "MZN" - }, - { - "id": "GBPNAD=X", - "currency": "NAD", - "exchange": "CCY", - "name": "GBP/NAD", - "from_symbol": "GBP", - "to_symbol": "NAD" - }, - { - "id": "GBPNGN=X", - "currency": "NGN", - "exchange": "CCY", - "name": "GBP/NGN", - "from_symbol": "GBP", - "to_symbol": "NGN" - }, - { - "id": "GBPNIO=X", - "currency": "NIO", - "exchange": "CCY", - "name": "GBP/NIO", - "from_symbol": "GBP", - "to_symbol": "NIO" - }, - { - "id": "GBPNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "GBP/NOK", - "from_symbol": "GBP", - "to_symbol": "NOK" - }, - { - "id": "GBPNPR=X", - "currency": "NPR", - "exchange": "CCY", - "name": "GBP/NPR", - "from_symbol": "GBP", - "to_symbol": "NPR" - }, - { - "id": "GBPNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "GBP/NZD", - "from_symbol": "GBP", - "to_symbol": "NZD" - }, - { - "id": "GBPOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "GBP/OMR", - "from_symbol": "GBP", - "to_symbol": "OMR" - }, - { - "id": "GBPPAB=X", - "currency": "PAB", - "exchange": "CCY", - "name": "GBP/PAB", - "from_symbol": "GBP", - "to_symbol": "PAB" - }, - { - "id": "GBPPEN=X", - "currency": "PEN", - "exchange": "CCY", - "name": "GBP/PEN", - "from_symbol": "GBP", - "to_symbol": "PEN" - }, - { - "id": "GBPPGK=X", - "currency": "PGK", - "exchange": "CCY", - "name": "GBP/PGK", - "from_symbol": "GBP", - "to_symbol": "PGK" - }, - { - "id": "GBPPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "GBP/PHP", - "from_symbol": "GBP", - "to_symbol": "PHP" - }, - { - "id": "GBPPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "GBP/PKR", - "from_symbol": "GBP", - "to_symbol": "PKR" - }, - { - "id": "GBPPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "GBP/PLN", - "from_symbol": "GBP", - "to_symbol": "PLN" - }, - { - "id": "GBPPLX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/PLX", - "from_symbol": "GBP", - "to_symbol": "PLX" - }, - { - "id": "GBPPYG=X", - "currency": "PYG", - "exchange": "CCY", - "name": "GBP/PYG", - "from_symbol": "GBP", - "to_symbol": "PYG" - }, - { - "id": "GBPQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "GBP/QAR", - "from_symbol": "GBP", - "to_symbol": "QAR" - }, - { - "id": "GBPRON=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/RON", - "from_symbol": "GBP", - "to_symbol": "RON" - }, - { - "id": "GBPRSD=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/RSD", - "from_symbol": "GBP", - "to_symbol": "RSD" - }, - { - "id": "GBPRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "GBP/RUB", - "from_symbol": "GBP", - "to_symbol": "RUB" - }, - { - "id": "GBPRUX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/RUX", - "from_symbol": "GBP", - "to_symbol": "RUX" - }, - { - "id": "GBPRWF=X", - "currency": "RWF", - "exchange": "CCY", - "name": "GBP/RWF", - "from_symbol": "GBP", - "to_symbol": "RWF" - }, - { - "id": "GBPSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "GBP/SAR", - "from_symbol": "GBP", - "to_symbol": "SAR" - }, - { - "id": "GBPSBD=X", - "currency": "SBD", - "exchange": "CCY", - "name": "GBP/SBD", - "from_symbol": "GBP", - "to_symbol": "SBD" - }, - { - "id": "GBPSCR=X", - "currency": "SCR", - "exchange": "CCY", - "name": "GBP/SCR", - "from_symbol": "GBP", - "to_symbol": "SCR" - }, - { - "id": "GBPSDG=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/SDG", - "from_symbol": "GBP", - "to_symbol": "SDG" - }, - { - "id": "GBPSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "GBP/SEK", - "from_symbol": "GBP", - "to_symbol": "SEK" - }, - { - "id": "GBPSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "GBP/SGD", - "from_symbol": "GBP", - "to_symbol": "SGD" - }, - { - "id": "GBPSHP=X", - "currency": "SHP", - "exchange": "CCY", - "name": "GBP/SHP", - "from_symbol": "GBP", - "to_symbol": "SHP" - }, - { - "id": "GBPSLL=X", - "currency": "SLL", - "exchange": "CCY", - "name": "GBP/SLL", - "from_symbol": "GBP", - "to_symbol": "SLL" - }, - { - "id": "GBPSOS=X", - "currency": "SOS", - "exchange": "CCY", - "name": "GBP/SOS", - "from_symbol": "GBP", - "to_symbol": "SOS" - }, - { - "id": "GBPSRD=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/SRD", - "from_symbol": "GBP", - "to_symbol": "SRD" - }, - { - "id": "GBPSTN=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/STN", - "from_symbol": "GBP", - "to_symbol": "STN" - }, - { - "id": "GBPSVC=X", - "currency": "SVC", - "exchange": "CCY", - "name": "GBP/SVC", - "from_symbol": "GBP", - "to_symbol": "SVC" - }, - { - "id": "GBPSYP=X", - "currency": "SYP", - "exchange": "CCY", - "name": "GBP/SYP", - "from_symbol": "GBP", - "to_symbol": "SYP" - }, - { - "id": "GBPSZL=X", - "currency": "SZL", - "exchange": "CCY", - "name": "GBP/SZL", - "from_symbol": "GBP", - "to_symbol": "SZL" - }, - { - "id": "GBPTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "GBP/THB", - "from_symbol": "GBP", - "to_symbol": "THB" - }, - { - "id": "GBPTHX=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/THX", - "from_symbol": "GBP", - "to_symbol": "THX" - }, - { - "id": "GBPTJS=X", - "currency": "TJS", - "exchange": "CCY", - "name": "GBP/TJS", - "from_symbol": "GBP", - "to_symbol": "TJS" - }, - { - "id": "GBPTMT=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/TMT", - "from_symbol": "GBP", - "to_symbol": "TMT" - }, - { - "id": "GBPTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "GBP/TND", - "from_symbol": "GBP", - "to_symbol": "TND" - }, - { - "id": "GBPTOP=X", - "currency": "TOP", - "exchange": "CCY", - "name": "GBP/TOP", - "from_symbol": "GBP", - "to_symbol": "TOP" - }, - { - "id": "GBPTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "GBP/TRY", - "from_symbol": "GBP", - "to_symbol": "TRY" - }, - { - "id": "GBPTTD=X", - "currency": "TTD", - "exchange": "CCY", - "name": "GBP/TTD", - "from_symbol": "GBP", - "to_symbol": "TTD" - }, - { - "id": "GBPTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "GBP/TWD", - "from_symbol": "GBP", - "to_symbol": "TWD" - }, - { - "id": "GBPTZS=X", - "currency": "TZS", - "exchange": "CCY", - "name": "GBP/TZS", - "from_symbol": "GBP", - "to_symbol": "TZS" - }, - { - "id": "GBPUAH=X", - "currency": "UAH", - "exchange": "CCY", - "name": "GBP/UAH", - "from_symbol": "GBP", - "to_symbol": "UAH" - }, - { - "id": "GBPUGX=X", - "currency": "UGX", - "exchange": "CCY", - "name": "GBP/UGX", - "from_symbol": "GBP", - "to_symbol": "UGX" - }, - { - "id": "GBPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "GBP/USD", - "from_symbol": "GBP", - "to_symbol": "USD" - }, - { - "id": "GBPUSY=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/USY", - "from_symbol": "GBP", - "to_symbol": "USY" - }, - { - "id": "GBPUYU=X", - "currency": "UYU", - "exchange": "CCY", - "name": "GBP/UYU", - "from_symbol": "GBP", - "to_symbol": "UYU" - }, - { - "id": "GBPUZS=X", - "currency": "UZS", - "exchange": "CCY", - "name": "GBP/UZS", - "from_symbol": "GBP", - "to_symbol": "UZS" - }, - { - "id": "GBPVES=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/VES", - "from_symbol": "GBP", - "to_symbol": "VES" - }, - { - "id": "GBPVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "GBP/VND", - "from_symbol": "GBP", - "to_symbol": "VND" - }, - { - "id": "GBPVUV=X", - "currency": "VUV", - "exchange": "CCY", - "name": "GBP/VUV", - "from_symbol": "GBP", - "to_symbol": "VUV" - }, - { - "id": "GBPWST=X", - "currency": "WST", - "exchange": "CCY", - "name": "GBP/WST", - "from_symbol": "GBP", - "to_symbol": "WST" - }, - { - "id": "GBPXAF=X", - "currency": "XAF", - "exchange": "CCY", - "name": "GBP/XAF", - "from_symbol": "GBP", - "to_symbol": "XAF" - }, - { - "id": "GBPXCD=X", - "currency": "XCD", - "exchange": "CCY", - "name": "GBP/XCD", - "from_symbol": "GBP", - "to_symbol": "XCD" - }, - { - "id": "GBPXCU=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/XCU", - "from_symbol": "GBP", - "to_symbol": "XCU" - }, - { - "id": "GBPXDR=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/XDR", - "from_symbol": "GBP", - "to_symbol": "XDR" - }, - { - "id": "GBPXOF=X", - "currency": "XOF", - "exchange": "CCY", - "name": "GBP/XOF", - "from_symbol": "GBP", - "to_symbol": "XOF" - }, - { - "id": "GBPXPF=X", - "currency": "XPF", - "exchange": "CCY", - "name": "GBP/XPF", - "from_symbol": "GBP", - "to_symbol": "XPF" - }, - { - "id": "GBPYER=X", - "currency": "YER", - "exchange": "CCY", - "name": "GBP/YER", - "from_symbol": "GBP", - "to_symbol": "YER" - }, - { - "id": "GBPZAC=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/ZAC", - "from_symbol": "GBP", - "to_symbol": "ZAC" - }, - { - "id": "GBPZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "GBP/ZAR", - "from_symbol": "GBP", - "to_symbol": "ZAR" - }, - { - "id": "GBPZMW=X", - "currency": null, - "exchange": "CCY", - "name": "GBP/ZMW", - "from_symbol": "GBP", - "to_symbol": "ZMW" - }, - { - "id": "GEL=X", - "currency": "GEL", - "exchange": "CCY", - "name": "USD/GEL", - "from_symbol": "USD", - "to_symbol": "GEL" - }, - { - "id": "GELBRX=X", - "currency": null, - "exchange": "CCY", - "name": "GEL/BRX", - "from_symbol": "GEL", - "to_symbol": "BRX" - }, - { - "id": "GHS=X", - "currency": "GHS", - "exchange": "CCY", - "name": "USD/GHS", - "from_symbol": "USD", - "to_symbol": "GHS" - }, - { - "id": "GHSAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "GHS/AUD", - "from_symbol": "GHS", - "to_symbol": "AUD" - }, - { - "id": "GHSBRX=X", - "currency": null, - "exchange": "CCY", - "name": "GHS/BRX", - "from_symbol": "GHS", - "to_symbol": "BRX" - }, - { - "id": "GHSCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "GHS/CAD", - "from_symbol": "GHS", - "to_symbol": "CAD" - }, - { - "id": "GHSCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "GHS/CNY", - "from_symbol": "GHS", - "to_symbol": "CNY" - }, - { - "id": "GHSEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "GHS/EUR", - "from_symbol": "GHS", - "to_symbol": "EUR" - }, - { - "id": "GHSGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "GHS/GBP", - "from_symbol": "GHS", - "to_symbol": "GBP" - }, - { - "id": "GHSIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "GHS/IDR", - "from_symbol": "GHS", - "to_symbol": "IDR" - }, - { - "id": "GHSINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "GHS/INR", - "from_symbol": "GHS", - "to_symbol": "INR" - }, - { - "id": "GHSJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "GHS/JPY", - "from_symbol": "GHS", - "to_symbol": "JPY" - }, - { - "id": "GHSKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "GHS/KRW", - "from_symbol": "GHS", - "to_symbol": "KRW" - }, - { - "id": "GHSNGN=X", - "currency": "NGN", - "exchange": "CCY", - "name": "GHS/NGN", - "from_symbol": "GHS", - "to_symbol": "NGN" - }, - { - "id": "GHSUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "GHS/USD", - "from_symbol": "GHS", - "to_symbol": "USD" - }, - { - "id": "GHSZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "GHS/ZAR", - "from_symbol": "GHS", - "to_symbol": "ZAR" - }, - { - "id": "GIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "USD/GIP", - "from_symbol": "USD", - "to_symbol": "GIP" - }, - { - "id": "GIPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "GIP/BRX", - "from_symbol": "GIP", - "to_symbol": "BRX" - }, - { - "id": "GIPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "GIP/USD", - "from_symbol": "GIP", - "to_symbol": "USD" - }, - { - "id": "GMD=X", - "currency": "GMD", - "exchange": "CCY", - "name": "USD/GMD", - "from_symbol": "USD", - "to_symbol": "GMD" - }, - { - "id": "GMDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "GMD/BRX", - "from_symbol": "GMD", - "to_symbol": "BRX" - }, - { - "id": "GMDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "GMD/GBP", - "from_symbol": "GMD", - "to_symbol": "GBP" - }, - { - "id": "GMDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "GMD/USD", - "from_symbol": "GMD", - "to_symbol": "USD" - }, - { - "id": "GNF=X", - "currency": "GNF", - "exchange": "CCY", - "name": "USD/GNF", - "from_symbol": "USD", - "to_symbol": "GNF" - }, - { - "id": "GNFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "GNF/BRX", - "from_symbol": "GNF", - "to_symbol": "BRX" - }, - { - "id": "GNFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "GNF/USD", - "from_symbol": "GNF", - "to_symbol": "USD" - }, - { - "id": "GTQ=X", - "currency": "GTQ", - "exchange": "CCY", - "name": "USD/GTQ", - "from_symbol": "USD", - "to_symbol": "GTQ" - }, - { - "id": "GTQBRX=X", - "currency": null, - "exchange": "CCY", - "name": "GTQ/BRX", - "from_symbol": "GTQ", - "to_symbol": "BRX" - }, - { - "id": "GTQUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "GTQ/USD", - "from_symbol": "GTQ", - "to_symbol": "USD" - }, - { - "id": "GYD=X", - "currency": "GYD", - "exchange": "CCY", - "name": "USD/GYD", - "from_symbol": "USD", - "to_symbol": "GYD" - }, - { - "id": "GYDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "GYD/BRX", - "from_symbol": "GYD", - "to_symbol": "BRX" - }, - { - "id": "GYDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "GYD/USD", - "from_symbol": "GYD", - "to_symbol": "USD" - }, - { - "id": "HKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "USD/HKD", - "from_symbol": "USD", - "to_symbol": "HKD" - }, - { - "id": "HKDAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "HKD/AED", - "from_symbol": "HKD", - "to_symbol": "AED" - }, - { - "id": "HKDAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "HKD/AUD", - "from_symbol": "HKD", - "to_symbol": "AUD" - }, - { - "id": "HKDBDT=X", - "currency": "BDT", - "exchange": "CCY", - "name": "HKD/BDT", - "from_symbol": "HKD", - "to_symbol": "BDT" - }, - { - "id": "HKDBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "HKD/BRL", - "from_symbol": "HKD", - "to_symbol": "BRL" - }, - { - "id": "HKDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/BRX", - "from_symbol": "HKD", - "to_symbol": "BRX" - }, - { - "id": "HKDCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "HKD/CAD", - "from_symbol": "HKD", - "to_symbol": "CAD" - }, - { - "id": "HKDCAX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/CAX", - "from_symbol": "HKD", - "to_symbol": "CAX" - }, - { - "id": "HKDCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "HKD/CHF", - "from_symbol": "HKD", - "to_symbol": "CHF" - }, - { - "id": "HKDCNH=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/CNH", - "from_symbol": "HKD", - "to_symbol": "CNH" - }, - { - "id": "HKDCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "HKD/CNY", - "from_symbol": "HKD", - "to_symbol": "CNY" - }, - { - "id": "HKDCZX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/CZX", - "from_symbol": "HKD", - "to_symbol": "CZX" - }, - { - "id": "HKDDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "HKD/DKK", - "from_symbol": "HKD", - "to_symbol": "DKK" - }, - { - "id": "HKDDKX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/DKX", - "from_symbol": "HKD", - "to_symbol": "DKX" - }, - { - "id": "HKDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "HKD/EUR", - "from_symbol": "HKD", - "to_symbol": "EUR" - }, - { - "id": "HKDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "HKD/GBP", - "from_symbol": "HKD", - "to_symbol": "GBP" - }, - { - "id": "HKDHUX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/HUX", - "from_symbol": "HKD", - "to_symbol": "HUX" - }, - { - "id": "HKDIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "HKD/IDR", - "from_symbol": "HKD", - "to_symbol": "IDR" - }, - { - "id": "HKDILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "HKD/ILS", - "from_symbol": "HKD", - "to_symbol": "ILS" - }, - { - "id": "HKDINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "HKD/INR", - "from_symbol": "HKD", - "to_symbol": "INR" - }, - { - "id": "HKDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "HKD/JPY", - "from_symbol": "HKD", - "to_symbol": "JPY" - }, - { - "id": "HKDKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "HKD/KRW", - "from_symbol": "HKD", - "to_symbol": "KRW" - }, - { - "id": "HKDLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "HKD/LKR", - "from_symbol": "HKD", - "to_symbol": "LKR" - }, - { - "id": "HKDMOP=X", - "currency": "MOP", - "exchange": "CCY", - "name": "HKD/MOP", - "from_symbol": "HKD", - "to_symbol": "MOP" - }, - { - "id": "HKDMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "HKD/MXN", - "from_symbol": "HKD", - "to_symbol": "MXN" - }, - { - "id": "HKDMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "HKD/MYR", - "from_symbol": "HKD", - "to_symbol": "MYR" - }, - { - "id": "HKDMYX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/MYX", - "from_symbol": "HKD", - "to_symbol": "MYX" - }, - { - "id": "HKDNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "HKD/NOK", - "from_symbol": "HKD", - "to_symbol": "NOK" - }, - { - "id": "HKDNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "HKD/NZD", - "from_symbol": "HKD", - "to_symbol": "NZD" - }, - { - "id": "HKDOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "HKD/OMR", - "from_symbol": "HKD", - "to_symbol": "OMR" - }, - { - "id": "HKDPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "HKD/PHP", - "from_symbol": "HKD", - "to_symbol": "PHP" - }, - { - "id": "HKDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "HKD/PKR", - "from_symbol": "HKD", - "to_symbol": "PKR" - }, - { - "id": "HKDPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "HKD/PLN", - "from_symbol": "HKD", - "to_symbol": "PLN" - }, - { - "id": "HKDPLX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/PLX", - "from_symbol": "HKD", - "to_symbol": "PLX" - }, - { - "id": "HKDRUX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/RUX", - "from_symbol": "HKD", - "to_symbol": "RUX" - }, - { - "id": "HKDSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "HKD/SEK", - "from_symbol": "HKD", - "to_symbol": "SEK" - }, - { - "id": "HKDSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "HKD/SGD", - "from_symbol": "HKD", - "to_symbol": "SGD" - }, - { - "id": "HKDTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "HKD/THB", - "from_symbol": "HKD", - "to_symbol": "THB" - }, - { - "id": "HKDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "HKD/THX", - "from_symbol": "HKD", - "to_symbol": "THX" - }, - { - "id": "HKDTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "HKD/TWD", - "from_symbol": "HKD", - "to_symbol": "TWD" - }, - { - "id": "HKDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "HKD/USD", - "from_symbol": "HKD", - "to_symbol": "USD" - }, - { - "id": "HKDVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "HKD/VND", - "from_symbol": "HKD", - "to_symbol": "VND" - }, - { - "id": "HKDZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "HKD/ZAR", - "from_symbol": "HKD", - "to_symbol": "ZAR" - }, - { - "id": "HNL=X", - "currency": "HNL", - "exchange": "CCY", - "name": "USD/HNL", - "from_symbol": "USD", - "to_symbol": "HNL" - }, - { - "id": "HNLBRX=X", - "currency": null, - "exchange": "CCY", - "name": "HNL/BRX", - "from_symbol": "HNL", - "to_symbol": "BRX" - }, - { - "id": "HNLUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "HNL/USD", - "from_symbol": "HNL", - "to_symbol": "USD" - }, - { - "id": "HRK=X", - "currency": "HRK", - "exchange": "CCY", - "name": "USD/HRK", - "from_symbol": "USD", - "to_symbol": "HRK" - }, - { - "id": "HRKBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "HRK/BAM", - "from_symbol": "HRK", - "to_symbol": "BAM" - }, - { - "id": "HRKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "HRK/BRX", - "from_symbol": "HRK", - "to_symbol": "BRX" - }, - { - "id": "HRKCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "HRK/CHF", - "from_symbol": "HRK", - "to_symbol": "CHF" - }, - { - "id": "HRKCZX=X", - "currency": null, - "exchange": "CCY", - "name": "HRK/CZX", - "from_symbol": "HRK", - "to_symbol": "CZX" - }, - { - "id": "HRKDKX=X", - "currency": null, - "exchange": "CCY", - "name": "HRK/DKX", - "from_symbol": "HRK", - "to_symbol": "DKX" - }, - { - "id": "HRKEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "HRK/EUR", - "from_symbol": "HRK", - "to_symbol": "EUR" - }, - { - "id": "HRKGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "HRK/GBP", - "from_symbol": "HRK", - "to_symbol": "GBP" - }, - { - "id": "HRKHUX=X", - "currency": null, - "exchange": "CCY", - "name": "HRK/HUX", - "from_symbol": "HRK", - "to_symbol": "HUX" - }, - { - "id": "HRKPLX=X", - "currency": null, - "exchange": "CCY", - "name": "HRK/PLX", - "from_symbol": "HRK", - "to_symbol": "PLX" - }, - { - "id": "HRKRSD=X", - "currency": null, - "exchange": "CCY", - "name": "HRK/RSD", - "from_symbol": "HRK", - "to_symbol": "RSD" - }, - { - "id": "HRKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "HRK/USD", - "from_symbol": "HRK", - "to_symbol": "USD" - }, - { - "id": "HRX=X", - "currency": "HRX", - "exchange": "CCY", - "name": "USD/HRX", - "from_symbol": "USD", - "to_symbol": "HRX" - }, - { - "id": "HTG=X", - "currency": "HTG", - "exchange": "CCY", - "name": "USD/HTG", - "from_symbol": "USD", - "to_symbol": "HTG" - }, - { - "id": "HTGBRX=X", - "currency": null, - "exchange": "CCY", - "name": "HTG/BRX", - "from_symbol": "HTG", - "to_symbol": "BRX" - }, - { - "id": "HTGUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "HTG/USD", - "from_symbol": "HTG", - "to_symbol": "USD" - }, - { - "id": "HUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "USD/HUF", - "from_symbol": "USD", - "to_symbol": "HUF" - }, - { - "id": "HUFBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "HUF/BAM", - "from_symbol": "HUF", - "to_symbol": "BAM" - }, - { - "id": "HUFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "HUF/BRX", - "from_symbol": "HUF", - "to_symbol": "BRX" - }, - { - "id": "HUFCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "HUF/CHF", - "from_symbol": "HUF", - "to_symbol": "CHF" - }, - { - "id": "HUFCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "HUF/CZK", - "from_symbol": "HUF", - "to_symbol": "CZK" - }, - { - "id": "HUFCZX=X", - "currency": null, - "exchange": "CCY", - "name": "HUF/CZX", - "from_symbol": "HUF", - "to_symbol": "CZX" - }, - { - "id": "HUFDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "HUF/DKK", - "from_symbol": "HUF", - "to_symbol": "DKK" - }, - { - "id": "HUFDKX=X", - "currency": null, - "exchange": "CCY", - "name": "HUF/DKX", - "from_symbol": "HUF", - "to_symbol": "DKX" - }, - { - "id": "HUFEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "HUF/EUR", - "from_symbol": "HUF", - "to_symbol": "EUR" - }, - { - "id": "HUFGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "HUF/GBP", - "from_symbol": "HUF", - "to_symbol": "GBP" - }, - { - "id": "HUFHRX=X", - "currency": null, - "exchange": "CCY", - "name": "HUF/HRX", - "from_symbol": "HUF", - "to_symbol": "HRX" - }, - { - "id": "HUFJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "HUF/JPY", - "from_symbol": "HUF", - "to_symbol": "JPY" - }, - { - "id": "HUFPLX=X", - "currency": null, - "exchange": "CCY", - "name": "HUF/PLX", - "from_symbol": "HUF", - "to_symbol": "PLX" - }, - { - "id": "HUFRSD=X", - "currency": null, - "exchange": "CCY", - "name": "HUF/RSD", - "from_symbol": "HUF", - "to_symbol": "RSD" - }, - { - "id": "HUFRUX=X", - "currency": null, - "exchange": "CCY", - "name": "HUF/RUX", - "from_symbol": "HUF", - "to_symbol": "RUX" - }, - { - "id": "HUFSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "HUF/SEK", - "from_symbol": "HUF", - "to_symbol": "SEK" - }, - { - "id": "HUFTHX=X", - "currency": null, - "exchange": "CCY", - "name": "HUF/THX", - "from_symbol": "HUF", - "to_symbol": "THX" - }, - { - "id": "HUFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "HUF/USD", - "from_symbol": "HUF", - "to_symbol": "USD" - }, - { - "id": "HUX=X", - "currency": "HUX", - "exchange": "CCY", - "name": "USD/HUX", - "from_symbol": "USD", - "to_symbol": "HUX" - }, - { - "id": "IDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "USD/IDR", - "from_symbol": "USD", - "to_symbol": "IDR" - }, - { - "id": "IDRAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "IDR/AUD", - "from_symbol": "IDR", - "to_symbol": "AUD" - }, - { - "id": "IDRBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "IDR/BRL", - "from_symbol": "IDR", - "to_symbol": "BRL" - }, - { - "id": "IDRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/BRX", - "from_symbol": "IDR", - "to_symbol": "BRX" - }, - { - "id": "IDRBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "IDR/BWP", - "from_symbol": "IDR", - "to_symbol": "BWP" - }, - { - "id": "IDRCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "IDR/CAD", - "from_symbol": "IDR", - "to_symbol": "CAD" - }, - { - "id": "IDRCAX=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/CAX", - "from_symbol": "IDR", - "to_symbol": "CAX" - }, - { - "id": "IDRCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "IDR/CHF", - "from_symbol": "IDR", - "to_symbol": "CHF" - }, - { - "id": "IDRCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "IDR/CNY", - "from_symbol": "IDR", - "to_symbol": "CNY" - }, - { - "id": "IDRCZX=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/CZX", - "from_symbol": "IDR", - "to_symbol": "CZX" - }, - { - "id": "IDRDKX=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/DKX", - "from_symbol": "IDR", - "to_symbol": "DKX" - }, - { - "id": "IDREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "IDR/EUR", - "from_symbol": "IDR", - "to_symbol": "EUR" - }, - { - "id": "IDRGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "IDR/GBP", - "from_symbol": "IDR", - "to_symbol": "GBP" - }, - { - "id": "IDRGHS=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/GHS", - "from_symbol": "IDR", - "to_symbol": "GHS" - }, - { - "id": "IDRHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "IDR/HKD", - "from_symbol": "IDR", - "to_symbol": "HKD" - }, - { - "id": "IDRHUX=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/HUX", - "from_symbol": "IDR", - "to_symbol": "HUX" - }, - { - "id": "IDRINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "IDR/INR", - "from_symbol": "IDR", - "to_symbol": "INR" - }, - { - "id": "IDRJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "IDR/JPY", - "from_symbol": "IDR", - "to_symbol": "JPY" - }, - { - "id": "IDRKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "IDR/KRW", - "from_symbol": "IDR", - "to_symbol": "KRW" - }, - { - "id": "IDRMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "IDR/MYR", - "from_symbol": "IDR", - "to_symbol": "MYR" - }, - { - "id": "IDRMYX=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/MYX", - "from_symbol": "IDR", - "to_symbol": "MYX" - }, - { - "id": "IDRNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "IDR/NZD", - "from_symbol": "IDR", - "to_symbol": "NZD" - }, - { - "id": "IDRPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "IDR/PHP", - "from_symbol": "IDR", - "to_symbol": "PHP" - }, - { - "id": "IDRPLX=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/PLX", - "from_symbol": "IDR", - "to_symbol": "PLX" - }, - { - "id": "IDRSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "IDR/SGD", - "from_symbol": "IDR", - "to_symbol": "SGD" - }, - { - "id": "IDRTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "IDR/THB", - "from_symbol": "IDR", - "to_symbol": "THB" - }, - { - "id": "IDRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/THX", - "from_symbol": "IDR", - "to_symbol": "THX" - }, - { - "id": "IDRTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "IDR/TWD", - "from_symbol": "IDR", - "to_symbol": "TWD" - }, - { - "id": "IDRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "IDR/USD", - "from_symbol": "IDR", - "to_symbol": "USD" - }, - { - "id": "IDRXCU=X", - "currency": null, - "exchange": "CCY", - "name": "IDR/XCU", - "from_symbol": "IDR", - "to_symbol": "XCU" - }, - { - "id": "IDRZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "IDR/ZAR", - "from_symbol": "IDR", - "to_symbol": "ZAR" - }, - { - "id": "ILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "USD/ILS", - "from_symbol": "USD", - "to_symbol": "ILS" - }, - { - "id": "ILSBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "ILS/BRL", - "from_symbol": "ILS", - "to_symbol": "BRL" - }, - { - "id": "ILSBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ILS/BRX", - "from_symbol": "ILS", - "to_symbol": "BRX" - }, - { - "id": "ILSCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "ILS/CHF", - "from_symbol": "ILS", - "to_symbol": "CHF" - }, - { - "id": "ILSCZX=X", - "currency": null, - "exchange": "CCY", - "name": "ILS/CZX", - "from_symbol": "ILS", - "to_symbol": "CZX" - }, - { - "id": "ILSDKX=X", - "currency": null, - "exchange": "CCY", - "name": "ILS/DKX", - "from_symbol": "ILS", - "to_symbol": "DKX" - }, - { - "id": "ILSEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "ILS/EUR", - "from_symbol": "ILS", - "to_symbol": "EUR" - }, - { - "id": "ILSGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "ILS/GBP", - "from_symbol": "ILS", - "to_symbol": "GBP" - }, - { - "id": "ILSHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "ILS/HKD", - "from_symbol": "ILS", - "to_symbol": "HKD" - }, - { - "id": "ILSHUX=X", - "currency": null, - "exchange": "CCY", - "name": "ILS/HUX", - "from_symbol": "ILS", - "to_symbol": "HUX" - }, - { - "id": "ILSJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "ILS/JPY", - "from_symbol": "ILS", - "to_symbol": "JPY" - }, - { - "id": "ILSPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "ILS/PLN", - "from_symbol": "ILS", - "to_symbol": "PLN" - }, - { - "id": "ILSPLX=X", - "currency": null, - "exchange": "CCY", - "name": "ILS/PLX", - "from_symbol": "ILS", - "to_symbol": "PLX" - }, - { - "id": "ILSSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "ILS/SGD", - "from_symbol": "ILS", - "to_symbol": "SGD" - }, - { - "id": "ILSTHX=X", - "currency": null, - "exchange": "CCY", - "name": "ILS/THX", - "from_symbol": "ILS", - "to_symbol": "THX" - }, - { - "id": "ILSUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ILS/USD", - "from_symbol": "ILS", - "to_symbol": "USD" - }, - { - "id": "ILSXDR=X", - "currency": null, - "exchange": "CCY", - "name": "ILS/XDR", - "from_symbol": "ILS", - "to_symbol": "XDR" - }, - { - "id": "INR=X", - "currency": "INR", - "exchange": "CCY", - "name": "USD/INR", - "from_symbol": "USD", - "to_symbol": "INR" - }, - { - "id": "INRAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "INR/AED", - "from_symbol": "INR", - "to_symbol": "AED" - }, - { - "id": "INRAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "INR/AUD", - "from_symbol": "INR", - "to_symbol": "AUD" - }, - { - "id": "INRBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "INR/BRL", - "from_symbol": "INR", - "to_symbol": "BRL" - }, - { - "id": "INRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/BRX", - "from_symbol": "INR", - "to_symbol": "BRX" - }, - { - "id": "INRBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "INR/BWP", - "from_symbol": "INR", - "to_symbol": "BWP" - }, - { - "id": "INRCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "INR/CAD", - "from_symbol": "INR", - "to_symbol": "CAD" - }, - { - "id": "INRCAX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/CAX", - "from_symbol": "INR", - "to_symbol": "CAX" - }, - { - "id": "INRCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "INR/CHF", - "from_symbol": "INR", - "to_symbol": "CHF" - }, - { - "id": "INRCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "INR/CNY", - "from_symbol": "INR", - "to_symbol": "CNY" - }, - { - "id": "INRCZX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/CZX", - "from_symbol": "INR", - "to_symbol": "CZX" - }, - { - "id": "INRDKX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/DKX", - "from_symbol": "INR", - "to_symbol": "DKX" - }, - { - "id": "INREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "INR/EUR", - "from_symbol": "INR", - "to_symbol": "EUR" - }, - { - "id": "INRGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "INR/GBP", - "from_symbol": "INR", - "to_symbol": "GBP" - }, - { - "id": "INRGHS=X", - "currency": null, - "exchange": "CCY", - "name": "INR/GHS", - "from_symbol": "INR", - "to_symbol": "GHS" - }, - { - "id": "INRHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "INR/HKD", - "from_symbol": "INR", - "to_symbol": "HKD" - }, - { - "id": "INRHUX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/HUX", - "from_symbol": "INR", - "to_symbol": "HUX" - }, - { - "id": "INRIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "INR/IDR", - "from_symbol": "INR", - "to_symbol": "IDR" - }, - { - "id": "INRJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "INR/JPY", - "from_symbol": "INR", - "to_symbol": "JPY" - }, - { - "id": "INRKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "INR/KRW", - "from_symbol": "INR", - "to_symbol": "KRW" - }, - { - "id": "INRMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "INR/MYR", - "from_symbol": "INR", - "to_symbol": "MYR" - }, - { - "id": "INRMYX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/MYX", - "from_symbol": "INR", - "to_symbol": "MYX" - }, - { - "id": "INRNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "INR/NZD", - "from_symbol": "INR", - "to_symbol": "NZD" - }, - { - "id": "INROMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "INR/OMR", - "from_symbol": "INR", - "to_symbol": "OMR" - }, - { - "id": "INRPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "INR/PHP", - "from_symbol": "INR", - "to_symbol": "PHP" - }, - { - "id": "INRPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "INR/PKR", - "from_symbol": "INR", - "to_symbol": "PKR" - }, - { - "id": "INRPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "INR/PLN", - "from_symbol": "INR", - "to_symbol": "PLN" - }, - { - "id": "INRPLX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/PLX", - "from_symbol": "INR", - "to_symbol": "PLX" - }, - { - "id": "INRRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "INR/RUB", - "from_symbol": "INR", - "to_symbol": "RUB" - }, - { - "id": "INRRUX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/RUX", - "from_symbol": "INR", - "to_symbol": "RUX" - }, - { - "id": "INRSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "INR/SGD", - "from_symbol": "INR", - "to_symbol": "SGD" - }, - { - "id": "INRTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "INR/THB", - "from_symbol": "INR", - "to_symbol": "THB" - }, - { - "id": "INRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "INR/THX", - "from_symbol": "INR", - "to_symbol": "THX" - }, - { - "id": "INRTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "INR/TWD", - "from_symbol": "INR", - "to_symbol": "TWD" - }, - { - "id": "INRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "INR/USD", - "from_symbol": "INR", - "to_symbol": "USD" - }, - { - "id": "INRXCU=X", - "currency": null, - "exchange": "CCY", - "name": "INR/XCU", - "from_symbol": "INR", - "to_symbol": "XCU" - }, - { - "id": "INRXDR=X", - "currency": null, - "exchange": "CCY", - "name": "INR/XDR", - "from_symbol": "INR", - "to_symbol": "XDR" - }, - { - "id": "INRZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "INR/ZAR", - "from_symbol": "INR", - "to_symbol": "ZAR" - }, - { - "id": "IQD=X", - "currency": "IQD", - "exchange": "CCY", - "name": "USD/IQD", - "from_symbol": "USD", - "to_symbol": "IQD" - }, - { - "id": "IQDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "IQD/BRX", - "from_symbol": "IQD", - "to_symbol": "BRX" - }, - { - "id": "IQDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "IQD/THX", - "from_symbol": "IQD", - "to_symbol": "THX" - }, - { - "id": "IQDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "IQD/USD", - "from_symbol": "IQD", - "to_symbol": "USD" - }, - { - "id": "IRR=X", - "currency": "IRR", - "exchange": "CCY", - "name": "USD/IRR", - "from_symbol": "USD", - "to_symbol": "IRR" - }, - { - "id": "IRRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "IRR/BRX", - "from_symbol": "IRR", - "to_symbol": "BRX" - }, - { - "id": "IRRJPY=X", - "currency": null, - "exchange": "CCY", - "name": "IRR/JPY", - "from_symbol": "IRR", - "to_symbol": "JPY" - }, - { - "id": "IRRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "IRR/USD", - "from_symbol": "IRR", - "to_symbol": "USD" - }, - { - "id": "ISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "USD/ISK", - "from_symbol": "USD", - "to_symbol": "ISK" - }, - { - "id": "ISKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ISK/BRX", - "from_symbol": "ISK", - "to_symbol": "BRX" - }, - { - "id": "ISKCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "ISK/CHF", - "from_symbol": "ISK", - "to_symbol": "CHF" - }, - { - "id": "ISKDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "ISK/DKK", - "from_symbol": "ISK", - "to_symbol": "DKK" - }, - { - "id": "ISKDKX=X", - "currency": null, - "exchange": "CCY", - "name": "ISK/DKX", - "from_symbol": "ISK", - "to_symbol": "DKX" - }, - { - "id": "ISKEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "ISK/EUR", - "from_symbol": "ISK", - "to_symbol": "EUR" - }, - { - "id": "ISKGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "ISK/GBP", - "from_symbol": "ISK", - "to_symbol": "GBP" - }, - { - "id": "ISKHUX=X", - "currency": null, - "exchange": "CCY", - "name": "ISK/HUX", - "from_symbol": "ISK", - "to_symbol": "HUX" - }, - { - "id": "ISKPLX=X", - "currency": null, - "exchange": "CCY", - "name": "ISK/PLX", - "from_symbol": "ISK", - "to_symbol": "PLX" - }, - { - "id": "ISKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ISK/USD", - "from_symbol": "ISK", - "to_symbol": "USD" - }, - { - "id": "ISX=X", - "currency": "ISX", - "exchange": "CCY", - "name": "USD/ISX", - "from_symbol": "USD", - "to_symbol": "ISX" - }, - { - "id": "JMD=X", - "currency": "JMD", - "exchange": "CCY", - "name": "USD/JMD", - "from_symbol": "USD", - "to_symbol": "JMD" - }, - { - "id": "JMDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "JMD/BRX", - "from_symbol": "JMD", - "to_symbol": "BRX" - }, - { - "id": "JMDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "JMD/GBP", - "from_symbol": "JMD", - "to_symbol": "GBP" - }, - { - "id": "JMDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "JMD/USD", - "from_symbol": "JMD", - "to_symbol": "USD" - }, - { - "id": "JOD=X", - "currency": "JOD", - "exchange": "CCY", - "name": "USD/JOD", - "from_symbol": "USD", - "to_symbol": "JOD" - }, - { - "id": "JODBRX=X", - "currency": null, - "exchange": "CCY", - "name": "JOD/BRX", - "from_symbol": "JOD", - "to_symbol": "BRX" - }, - { - "id": "JODEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "JOD/EUR", - "from_symbol": "JOD", - "to_symbol": "EUR" - }, - { - "id": "JODGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "JOD/GBP", - "from_symbol": "JOD", - "to_symbol": "GBP" - }, - { - "id": "JODTHX=X", - "currency": null, - "exchange": "CCY", - "name": "JOD/THX", - "from_symbol": "JOD", - "to_symbol": "THX" - }, - { - "id": "JODUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "JOD/USD", - "from_symbol": "JOD", - "to_symbol": "USD" - }, - { - "id": "JPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "USD/JPY", - "from_symbol": "USD", - "to_symbol": "JPY" - }, - { - "id": "JPYAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "JPY/AED", - "from_symbol": "JPY", - "to_symbol": "AED" - }, - { - "id": "JPYAFN=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/AFN", - "from_symbol": "JPY", - "to_symbol": "AFN" - }, - { - "id": "JPYALL=X", - "currency": "ALL", - "exchange": "CCY", - "name": "JPY/ALL", - "from_symbol": "JPY", - "to_symbol": "ALL" - }, - { - "id": "JPYAMD=X", - "currency": "AMD", - "exchange": "CCY", - "name": "JPY/AMD", - "from_symbol": "JPY", - "to_symbol": "AMD" - }, - { - "id": "JPYANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "JPY/ANG", - "from_symbol": "JPY", - "to_symbol": "ANG" - }, - { - "id": "JPYAOA=X", - "currency": "AOA", - "exchange": "CCY", - "name": "JPY/AOA", - "from_symbol": "JPY", - "to_symbol": "AOA" - }, - { - "id": "JPYARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "JPY/ARS", - "from_symbol": "JPY", - "to_symbol": "ARS" - }, - { - "id": "JPYAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "JPY/AUD", - "from_symbol": "JPY", - "to_symbol": "AUD" - }, - { - "id": "JPYAWG=X", - "currency": "AWG", - "exchange": "CCY", - "name": "JPY/AWG", - "from_symbol": "JPY", - "to_symbol": "AWG" - }, - { - "id": "JPYAZN=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/AZN", - "from_symbol": "JPY", - "to_symbol": "AZN" - }, - { - "id": "JPYBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "JPY/BAM", - "from_symbol": "JPY", - "to_symbol": "BAM" - }, - { - "id": "JPYBBD=X", - "currency": "BBD", - "exchange": "CCY", - "name": "JPY/BBD", - "from_symbol": "JPY", - "to_symbol": "BBD" - }, - { - "id": "JPYBDT=X", - "currency": "BDT", - "exchange": "CCY", - "name": "JPY/BDT", - "from_symbol": "JPY", - "to_symbol": "BDT" - }, - { - "id": "JPYBGN=X", - "currency": "BGN", - "exchange": "CCY", - "name": "JPY/BGN", - "from_symbol": "JPY", - "to_symbol": "BGN" - }, - { - "id": "JPYBHD=X", - "currency": "BHD", - "exchange": "CCY", - "name": "JPY/BHD", - "from_symbol": "JPY", - "to_symbol": "BHD" - }, - { - "id": "JPYBIF=X", - "currency": "BIF", - "exchange": "CCY", - "name": "JPY/BIF", - "from_symbol": "JPY", - "to_symbol": "BIF" - }, - { - "id": "JPYBMD=X", - "currency": "BMD", - "exchange": "CCY", - "name": "JPY/BMD", - "from_symbol": "JPY", - "to_symbol": "BMD" - }, - { - "id": "JPYBND=X", - "currency": "BND", - "exchange": "CCY", - "name": "JPY/BND", - "from_symbol": "JPY", - "to_symbol": "BND" - }, - { - "id": "JPYBOB=X", - "currency": "BOB", - "exchange": "CCY", - "name": "JPY/BOB", - "from_symbol": "JPY", - "to_symbol": "BOB" - }, - { - "id": "JPYBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "JPY/BRL", - "from_symbol": "JPY", - "to_symbol": "BRL" - }, - { - "id": "JPYBRX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/BRX", - "from_symbol": "JPY", - "to_symbol": "BRX" - }, - { - "id": "JPYBSD=X", - "currency": "BSD", - "exchange": "CCY", - "name": "JPY/BSD", - "from_symbol": "JPY", - "to_symbol": "BSD" - }, - { - "id": "JPYBTN=X", - "currency": "BTN", - "exchange": "CCY", - "name": "JPY/BTN", - "from_symbol": "JPY", - "to_symbol": "BTN" - }, - { - "id": "JPYBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "JPY/BWP", - "from_symbol": "JPY", - "to_symbol": "BWP" - }, - { - "id": "JPYBYN=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/BYN", - "from_symbol": "JPY", - "to_symbol": "BYN" - }, - { - "id": "JPYBZD=X", - "currency": "BZD", - "exchange": "CCY", - "name": "JPY/BZD", - "from_symbol": "JPY", - "to_symbol": "BZD" - }, - { - "id": "JPYCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "JPY/CAD", - "from_symbol": "JPY", - "to_symbol": "CAD" - }, - { - "id": "JPYCAX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/CAX", - "from_symbol": "JPY", - "to_symbol": "CAX" - }, - { - "id": "JPYCDF=X", - "currency": "CDF", - "exchange": "CCY", - "name": "JPY/CDF", - "from_symbol": "JPY", - "to_symbol": "CDF" - }, - { - "id": "JPYCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "JPY/CHF", - "from_symbol": "JPY", - "to_symbol": "CHF" - }, - { - "id": "JPYCLF=X", - "currency": "CLF", - "exchange": "CCY", - "name": "JPY/CLF", - "from_symbol": "JPY", - "to_symbol": "CLF" - }, - { - "id": "JPYCLP=X", - "currency": "CLP", - "exchange": "CCY", - "name": "JPY/CLP", - "from_symbol": "JPY", - "to_symbol": "CLP" - }, - { - "id": "JPYCNH=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/CNH", - "from_symbol": "JPY", - "to_symbol": "CNH" - }, - { - "id": "JPYCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "JPY/CNY", - "from_symbol": "JPY", - "to_symbol": "CNY" - }, - { - "id": "JPYCOP=X", - "currency": "COP", - "exchange": "CCY", - "name": "JPY/COP", - "from_symbol": "JPY", - "to_symbol": "COP" - }, - { - "id": "JPYCRC=X", - "currency": "CRC", - "exchange": "CCY", - "name": "JPY/CRC", - "from_symbol": "JPY", - "to_symbol": "CRC" - }, - { - "id": "JPYCUP=X", - "currency": "CUP", - "exchange": "CCY", - "name": "JPY/CUP", - "from_symbol": "JPY", - "to_symbol": "CUP" - }, - { - "id": "JPYCVE=X", - "currency": "CVE", - "exchange": "CCY", - "name": "JPY/CVE", - "from_symbol": "JPY", - "to_symbol": "CVE" - }, - { - "id": "JPYCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "JPY/CZK", - "from_symbol": "JPY", - "to_symbol": "CZK" - }, - { - "id": "JPYCZX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/CZX", - "from_symbol": "JPY", - "to_symbol": "CZX" - }, - { - "id": "JPYDJF=X", - "currency": "DJF", - "exchange": "CCY", - "name": "JPY/DJF", - "from_symbol": "JPY", - "to_symbol": "DJF" - }, - { - "id": "JPYDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "JPY/DKK", - "from_symbol": "JPY", - "to_symbol": "DKK" - }, - { - "id": "JPYDKX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/DKX", - "from_symbol": "JPY", - "to_symbol": "DKX" - }, - { - "id": "JPYDOP=X", - "currency": "DOP", - "exchange": "CCY", - "name": "JPY/DOP", - "from_symbol": "JPY", - "to_symbol": "DOP" - }, - { - "id": "JPYDZD=X", - "currency": "DZD", - "exchange": "CCY", - "name": "JPY/DZD", - "from_symbol": "JPY", - "to_symbol": "DZD" - }, - { - "id": "JPYEGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "JPY/EGP", - "from_symbol": "JPY", - "to_symbol": "EGP" - }, - { - "id": "JPYERN=X", - "currency": "ERN", - "exchange": "CCY", - "name": "JPY/ERN", - "from_symbol": "JPY", - "to_symbol": "ERN" - }, - { - "id": "JPYETB=X", - "currency": "ETB", - "exchange": "CCY", - "name": "JPY/ETB", - "from_symbol": "JPY", - "to_symbol": "ETB" - }, - { - "id": "JPYEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "JPY/EUR", - "from_symbol": "JPY", - "to_symbol": "EUR" - }, - { - "id": "JPYFJD=X", - "currency": "FJD", - "exchange": "CCY", - "name": "JPY/FJD", - "from_symbol": "JPY", - "to_symbol": "FJD" - }, - { - "id": "JPYFKP=X", - "currency": "FKP", - "exchange": "CCY", - "name": "JPY/FKP", - "from_symbol": "JPY", - "to_symbol": "FKP" - }, - { - "id": "JPYGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "JPY/GBP", - "from_symbol": "JPY", - "to_symbol": "GBP" - }, - { - "id": "JPYGEL=X", - "currency": "GEL", - "exchange": "CCY", - "name": "JPY/GEL", - "from_symbol": "JPY", - "to_symbol": "GEL" - }, - { - "id": "JPYGHS=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/GHS", - "from_symbol": "JPY", - "to_symbol": "GHS" - }, - { - "id": "JPYGIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "JPY/GIP", - "from_symbol": "JPY", - "to_symbol": "GIP" - }, - { - "id": "JPYGMD=X", - "currency": "GMD", - "exchange": "CCY", - "name": "JPY/GMD", - "from_symbol": "JPY", - "to_symbol": "GMD" - }, - { - "id": "JPYGNF=X", - "currency": "GNF", - "exchange": "CCY", - "name": "JPY/GNF", - "from_symbol": "JPY", - "to_symbol": "GNF" - }, - { - "id": "JPYGTQ=X", - "currency": "GTQ", - "exchange": "CCY", - "name": "JPY/GTQ", - "from_symbol": "JPY", - "to_symbol": "GTQ" - }, - { - "id": "JPYGYD=X", - "currency": "GYD", - "exchange": "CCY", - "name": "JPY/GYD", - "from_symbol": "JPY", - "to_symbol": "GYD" - }, - { - "id": "JPYHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "JPY/HKD", - "from_symbol": "JPY", - "to_symbol": "HKD" - }, - { - "id": "JPYHNL=X", - "currency": "HNL", - "exchange": "CCY", - "name": "JPY/HNL", - "from_symbol": "JPY", - "to_symbol": "HNL" - }, - { - "id": "JPYHRK=X", - "currency": "HRK", - "exchange": "CCY", - "name": "JPY/HRK", - "from_symbol": "JPY", - "to_symbol": "HRK" - }, - { - "id": "JPYHRX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/HRX", - "from_symbol": "JPY", - "to_symbol": "HRX" - }, - { - "id": "JPYHTG=X", - "currency": "HTG", - "exchange": "CCY", - "name": "JPY/HTG", - "from_symbol": "JPY", - "to_symbol": "HTG" - }, - { - "id": "JPYHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "JPY/HUF", - "from_symbol": "JPY", - "to_symbol": "HUF" - }, - { - "id": "JPYHUX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/HUX", - "from_symbol": "JPY", - "to_symbol": "HUX" - }, - { - "id": "JPYIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "JPY/IDR", - "from_symbol": "JPY", - "to_symbol": "IDR" - }, - { - "id": "JPYILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "JPY/ILS", - "from_symbol": "JPY", - "to_symbol": "ILS" - }, - { - "id": "JPYINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "JPY/INR", - "from_symbol": "JPY", - "to_symbol": "INR" - }, - { - "id": "JPYIQD=X", - "currency": "IQD", - "exchange": "CCY", - "name": "JPY/IQD", - "from_symbol": "JPY", - "to_symbol": "IQD" - }, - { - "id": "JPYIRR=X", - "currency": "IRR", - "exchange": "CCY", - "name": "JPY/IRR", - "from_symbol": "JPY", - "to_symbol": "IRR" - }, - { - "id": "JPYISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "JPY/ISK", - "from_symbol": "JPY", - "to_symbol": "ISK" - }, - { - "id": "JPYISX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/ISX", - "from_symbol": "JPY", - "to_symbol": "ISX" - }, - { - "id": "JPYJMD=X", - "currency": "JMD", - "exchange": "CCY", - "name": "JPY/JMD", - "from_symbol": "JPY", - "to_symbol": "JMD" - }, - { - "id": "JPYJOD=X", - "currency": "JOD", - "exchange": "CCY", - "name": "JPY/JOD", - "from_symbol": "JPY", - "to_symbol": "JOD" - }, - { - "id": "JPYKES=X", - "currency": "KES", - "exchange": "CCY", - "name": "JPY/KES", - "from_symbol": "JPY", - "to_symbol": "KES" - }, - { - "id": "JPYKGS=X", - "currency": "KGS", - "exchange": "CCY", - "name": "JPY/KGS", - "from_symbol": "JPY", - "to_symbol": "KGS" - }, - { - "id": "JPYKHR=X", - "currency": "KHR", - "exchange": "CCY", - "name": "JPY/KHR", - "from_symbol": "JPY", - "to_symbol": "KHR" - }, - { - "id": "JPYKMF=X", - "currency": "KMF", - "exchange": "CCY", - "name": "JPY/KMF", - "from_symbol": "JPY", - "to_symbol": "KMF" - }, - { - "id": "JPYKPW=X", - "currency": "KPW", - "exchange": "CCY", - "name": "JPY/KPW", - "from_symbol": "JPY", - "to_symbol": "KPW" - }, - { - "id": "JPYKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "JPY/KRW", - "from_symbol": "JPY", - "to_symbol": "KRW" - }, - { - "id": "JPYKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "JPY/KWD", - "from_symbol": "JPY", - "to_symbol": "KWD" - }, - { - "id": "JPYKYD=X", - "currency": "KYD", - "exchange": "CCY", - "name": "JPY/KYD", - "from_symbol": "JPY", - "to_symbol": "KYD" - }, - { - "id": "JPYKZT=X", - "currency": "KZT", - "exchange": "CCY", - "name": "JPY/KZT", - "from_symbol": "JPY", - "to_symbol": "KZT" - }, - { - "id": "JPYLAK=X", - "currency": "LAK", - "exchange": "CCY", - "name": "JPY/LAK", - "from_symbol": "JPY", - "to_symbol": "LAK" - }, - { - "id": "JPYLBP=X", - "currency": "LBP", - "exchange": "CCY", - "name": "JPY/LBP", - "from_symbol": "JPY", - "to_symbol": "LBP" - }, - { - "id": "JPYLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "JPY/LKR", - "from_symbol": "JPY", - "to_symbol": "LKR" - }, - { - "id": "JPYLRD=X", - "currency": "LRD", - "exchange": "CCY", - "name": "JPY/LRD", - "from_symbol": "JPY", - "to_symbol": "LRD" - }, - { - "id": "JPYLSL=X", - "currency": "LSL", - "exchange": "CCY", - "name": "JPY/LSL", - "from_symbol": "JPY", - "to_symbol": "LSL" - }, - { - "id": "JPYLYD=X", - "currency": "LYD", - "exchange": "CCY", - "name": "JPY/LYD", - "from_symbol": "JPY", - "to_symbol": "LYD" - }, - { - "id": "JPYMAD=X", - "currency": "MAD", - "exchange": "CCY", - "name": "JPY/MAD", - "from_symbol": "JPY", - "to_symbol": "MAD" - }, - { - "id": "JPYMDL=X", - "currency": "MDL", - "exchange": "CCY", - "name": "JPY/MDL", - "from_symbol": "JPY", - "to_symbol": "MDL" - }, - { - "id": "JPYMGA=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/MGA", - "from_symbol": "JPY", - "to_symbol": "MGA" - }, - { - "id": "JPYMKD=X", - "currency": "MKD", - "exchange": "CCY", - "name": "JPY/MKD", - "from_symbol": "JPY", - "to_symbol": "MKD" - }, - { - "id": "JPYMMK=X", - "currency": "MMK", - "exchange": "CCY", - "name": "JPY/MMK", - "from_symbol": "JPY", - "to_symbol": "MMK" - }, - { - "id": "JPYMNT=X", - "currency": "MNT", - "exchange": "CCY", - "name": "JPY/MNT", - "from_symbol": "JPY", - "to_symbol": "MNT" - }, - { - "id": "JPYMOP=X", - "currency": "MOP", - "exchange": "CCY", - "name": "JPY/MOP", - "from_symbol": "JPY", - "to_symbol": "MOP" - }, - { - "id": "JPYMRU=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/MRU", - "from_symbol": "JPY", - "to_symbol": "MRU" - }, - { - "id": "JPYMUR=X", - "currency": "MUR", - "exchange": "CCY", - "name": "JPY/MUR", - "from_symbol": "JPY", - "to_symbol": "MUR" - }, - { - "id": "JPYMVR=X", - "currency": "MVR", - "exchange": "CCY", - "name": "JPY/MVR", - "from_symbol": "JPY", - "to_symbol": "MVR" - }, - { - "id": "JPYMWK=X", - "currency": "MWK", - "exchange": "CCY", - "name": "JPY/MWK", - "from_symbol": "JPY", - "to_symbol": "MWK" - }, - { - "id": "JPYMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "JPY/MXN", - "from_symbol": "JPY", - "to_symbol": "MXN" - }, - { - "id": "JPYMXX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/MXX", - "from_symbol": "JPY", - "to_symbol": "MXX" - }, - { - "id": "JPYMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "JPY/MYR", - "from_symbol": "JPY", - "to_symbol": "MYR" - }, - { - "id": "JPYMYX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/MYX", - "from_symbol": "JPY", - "to_symbol": "MYX" - }, - { - "id": "JPYMZN=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/MZN", - "from_symbol": "JPY", - "to_symbol": "MZN" - }, - { - "id": "JPYNAD=X", - "currency": "NAD", - "exchange": "CCY", - "name": "JPY/NAD", - "from_symbol": "JPY", - "to_symbol": "NAD" - }, - { - "id": "JPYNGN=X", - "currency": "NGN", - "exchange": "CCY", - "name": "JPY/NGN", - "from_symbol": "JPY", - "to_symbol": "NGN" - }, - { - "id": "JPYNIO=X", - "currency": "NIO", - "exchange": "CCY", - "name": "JPY/NIO", - "from_symbol": "JPY", - "to_symbol": "NIO" - }, - { - "id": "JPYNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "JPY/NOK", - "from_symbol": "JPY", - "to_symbol": "NOK" - }, - { - "id": "JPYNPR=X", - "currency": "NPR", - "exchange": "CCY", - "name": "JPY/NPR", - "from_symbol": "JPY", - "to_symbol": "NPR" - }, - { - "id": "JPYNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "JPY/NZD", - "from_symbol": "JPY", - "to_symbol": "NZD" - }, - { - "id": "JPYOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "JPY/OMR", - "from_symbol": "JPY", - "to_symbol": "OMR" - }, - { - "id": "JPYPAB=X", - "currency": "PAB", - "exchange": "CCY", - "name": "JPY/PAB", - "from_symbol": "JPY", - "to_symbol": "PAB" - }, - { - "id": "JPYPEN=X", - "currency": "PEN", - "exchange": "CCY", - "name": "JPY/PEN", - "from_symbol": "JPY", - "to_symbol": "PEN" - }, - { - "id": "JPYPGK=X", - "currency": "PGK", - "exchange": "CCY", - "name": "JPY/PGK", - "from_symbol": "JPY", - "to_symbol": "PGK" - }, - { - "id": "JPYPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "JPY/PHP", - "from_symbol": "JPY", - "to_symbol": "PHP" - }, - { - "id": "JPYPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "JPY/PKR", - "from_symbol": "JPY", - "to_symbol": "PKR" - }, - { - "id": "JPYPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "JPY/PLN", - "from_symbol": "JPY", - "to_symbol": "PLN" - }, - { - "id": "JPYPLX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/PLX", - "from_symbol": "JPY", - "to_symbol": "PLX" - }, - { - "id": "JPYPYG=X", - "currency": "PYG", - "exchange": "CCY", - "name": "JPY/PYG", - "from_symbol": "JPY", - "to_symbol": "PYG" - }, - { - "id": "JPYQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "JPY/QAR", - "from_symbol": "JPY", - "to_symbol": "QAR" - }, - { - "id": "JPYRON=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/RON", - "from_symbol": "JPY", - "to_symbol": "RON" - }, - { - "id": "JPYRSD=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/RSD", - "from_symbol": "JPY", - "to_symbol": "RSD" - }, - { - "id": "JPYRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "JPY/RUB", - "from_symbol": "JPY", - "to_symbol": "RUB" - }, - { - "id": "JPYRUX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/RUX", - "from_symbol": "JPY", - "to_symbol": "RUX" - }, - { - "id": "JPYRWF=X", - "currency": "RWF", - "exchange": "CCY", - "name": "JPY/RWF", - "from_symbol": "JPY", - "to_symbol": "RWF" - }, - { - "id": "JPYSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "JPY/SAR", - "from_symbol": "JPY", - "to_symbol": "SAR" - }, - { - "id": "JPYSBD=X", - "currency": "SBD", - "exchange": "CCY", - "name": "JPY/SBD", - "from_symbol": "JPY", - "to_symbol": "SBD" - }, - { - "id": "JPYSCR=X", - "currency": "SCR", - "exchange": "CCY", - "name": "JPY/SCR", - "from_symbol": "JPY", - "to_symbol": "SCR" - }, - { - "id": "JPYSDG=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/SDG", - "from_symbol": "JPY", - "to_symbol": "SDG" - }, - { - "id": "JPYSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "JPY/SEK", - "from_symbol": "JPY", - "to_symbol": "SEK" - }, - { - "id": "JPYSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "JPY/SGD", - "from_symbol": "JPY", - "to_symbol": "SGD" - }, - { - "id": "JPYSHP=X", - "currency": "SHP", - "exchange": "CCY", - "name": "JPY/SHP", - "from_symbol": "JPY", - "to_symbol": "SHP" - }, - { - "id": "JPYSLL=X", - "currency": "SLL", - "exchange": "CCY", - "name": "JPY/SLL", - "from_symbol": "JPY", - "to_symbol": "SLL" - }, - { - "id": "JPYSOS=X", - "currency": "SOS", - "exchange": "CCY", - "name": "JPY/SOS", - "from_symbol": "JPY", - "to_symbol": "SOS" - }, - { - "id": "JPYSRD=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/SRD", - "from_symbol": "JPY", - "to_symbol": "SRD" - }, - { - "id": "JPYSTN=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/STN", - "from_symbol": "JPY", - "to_symbol": "STN" - }, - { - "id": "JPYSVC=X", - "currency": "SVC", - "exchange": "CCY", - "name": "JPY/SVC", - "from_symbol": "JPY", - "to_symbol": "SVC" - }, - { - "id": "JPYSYP=X", - "currency": "SYP", - "exchange": "CCY", - "name": "JPY/SYP", - "from_symbol": "JPY", - "to_symbol": "SYP" - }, - { - "id": "JPYSZL=X", - "currency": "SZL", - "exchange": "CCY", - "name": "JPY/SZL", - "from_symbol": "JPY", - "to_symbol": "SZL" - }, - { - "id": "JPYTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "JPY/THB", - "from_symbol": "JPY", - "to_symbol": "THB" - }, - { - "id": "JPYTHX=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/THX", - "from_symbol": "JPY", - "to_symbol": "THX" - }, - { - "id": "JPYTJS=X", - "currency": "TJS", - "exchange": "CCY", - "name": "JPY/TJS", - "from_symbol": "JPY", - "to_symbol": "TJS" - }, - { - "id": "JPYTMT=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/TMT", - "from_symbol": "JPY", - "to_symbol": "TMT" - }, - { - "id": "JPYTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "JPY/TND", - "from_symbol": "JPY", - "to_symbol": "TND" - }, - { - "id": "JPYTOP=X", - "currency": "TOP", - "exchange": "CCY", - "name": "JPY/TOP", - "from_symbol": "JPY", - "to_symbol": "TOP" - }, - { - "id": "JPYTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "JPY/TRY", - "from_symbol": "JPY", - "to_symbol": "TRY" - }, - { - "id": "JPYTTD=X", - "currency": "TTD", - "exchange": "CCY", - "name": "JPY/TTD", - "from_symbol": "JPY", - "to_symbol": "TTD" - }, - { - "id": "JPYTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "JPY/TWD", - "from_symbol": "JPY", - "to_symbol": "TWD" - }, - { - "id": "JPYTZS=X", - "currency": "TZS", - "exchange": "CCY", - "name": "JPY/TZS", - "from_symbol": "JPY", - "to_symbol": "TZS" - }, - { - "id": "JPYUAH=X", - "currency": "UAH", - "exchange": "CCY", - "name": "JPY/UAH", - "from_symbol": "JPY", - "to_symbol": "UAH" - }, - { - "id": "JPYUGX=X", - "currency": "UGX", - "exchange": "CCY", - "name": "JPY/UGX", - "from_symbol": "JPY", - "to_symbol": "UGX" - }, - { - "id": "JPYUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "JPY/USD", - "from_symbol": "JPY", - "to_symbol": "USD" - }, - { - "id": "JPYUYU=X", - "currency": "UYU", - "exchange": "CCY", - "name": "JPY/UYU", - "from_symbol": "JPY", - "to_symbol": "UYU" - }, - { - "id": "JPYUZS=X", - "currency": "UZS", - "exchange": "CCY", - "name": "JPY/UZS", - "from_symbol": "JPY", - "to_symbol": "UZS" - }, - { - "id": "JPYVES=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/VES", - "from_symbol": "JPY", - "to_symbol": "VES" - }, - { - "id": "JPYVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "JPY/VND", - "from_symbol": "JPY", - "to_symbol": "VND" - }, - { - "id": "JPYVUV=X", - "currency": "VUV", - "exchange": "CCY", - "name": "JPY/VUV", - "from_symbol": "JPY", - "to_symbol": "VUV" - }, - { - "id": "JPYWST=X", - "currency": "WST", - "exchange": "CCY", - "name": "JPY/WST", - "from_symbol": "JPY", - "to_symbol": "WST" - }, - { - "id": "JPYXAF=X", - "currency": "XAF", - "exchange": "CCY", - "name": "JPY/XAF", - "from_symbol": "JPY", - "to_symbol": "XAF" - }, - { - "id": "JPYXCD=X", - "currency": "XCD", - "exchange": "CCY", - "name": "JPY/XCD", - "from_symbol": "JPY", - "to_symbol": "XCD" - }, - { - "id": "JPYXCU=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/XCU", - "from_symbol": "JPY", - "to_symbol": "XCU" - }, - { - "id": "JPYXDR=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/XDR", - "from_symbol": "JPY", - "to_symbol": "XDR" - }, - { - "id": "JPYXOF=X", - "currency": "XOF", - "exchange": "CCY", - "name": "JPY/XOF", - "from_symbol": "JPY", - "to_symbol": "XOF" - }, - { - "id": "JPYXPF=X", - "currency": "XPF", - "exchange": "CCY", - "name": "JPY/XPF", - "from_symbol": "JPY", - "to_symbol": "XPF" - }, - { - "id": "JPYYER=X", - "currency": "YER", - "exchange": "CCY", - "name": "JPY/YER", - "from_symbol": "JPY", - "to_symbol": "YER" - }, - { - "id": "JPYZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "JPY/ZAR", - "from_symbol": "JPY", - "to_symbol": "ZAR" - }, - { - "id": "JPYZMW=X", - "currency": null, - "exchange": "CCY", - "name": "JPY/ZMW", - "from_symbol": "JPY", - "to_symbol": "ZMW" - }, - { - "id": "KES=X", - "currency": "KES", - "exchange": "CCY", - "name": "USD/KES", - "from_symbol": "USD", - "to_symbol": "KES" - }, - { - "id": "KESBRX=X", - "currency": null, - "exchange": "CCY", - "name": "KES/BRX", - "from_symbol": "KES", - "to_symbol": "BRX" - }, - { - "id": "KESEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "KES/EUR", - "from_symbol": "KES", - "to_symbol": "EUR" - }, - { - "id": "KESGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "KES/GBP", - "from_symbol": "KES", - "to_symbol": "GBP" - }, - { - "id": "KESOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "KES/OMR", - "from_symbol": "KES", - "to_symbol": "OMR" - }, - { - "id": "KESTHX=X", - "currency": null, - "exchange": "CCY", - "name": "KES/THX", - "from_symbol": "KES", - "to_symbol": "THX" - }, - { - "id": "KESUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "KES/USD", - "from_symbol": "KES", - "to_symbol": "USD" - }, - { - "id": "KESZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "KES/ZAR", - "from_symbol": "KES", - "to_symbol": "ZAR" - }, - { - "id": "KGS=X", - "currency": "KGS", - "exchange": "CCY", - "name": "USD/KGS", - "from_symbol": "USD", - "to_symbol": "KGS" - }, - { - "id": "KGSBRX=X", - "currency": null, - "exchange": "CCY", - "name": "KGS/BRX", - "from_symbol": "KGS", - "to_symbol": "BRX" - }, - { - "id": "KGSRUX=X", - "currency": null, - "exchange": "CCY", - "name": "KGS/RUX", - "from_symbol": "KGS", - "to_symbol": "RUX" - }, - { - "id": "KHR=X", - "currency": "KHR", - "exchange": "CCY", - "name": "USD/KHR", - "from_symbol": "USD", - "to_symbol": "KHR" - }, - { - "id": "KHRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "KHR/BRX", - "from_symbol": "KHR", - "to_symbol": "BRX" - }, - { - "id": "KHRMYX=X", - "currency": null, - "exchange": "CCY", - "name": "KHR/MYX", - "from_symbol": "KHR", - "to_symbol": "MYX" - }, - { - "id": "KHRSGD=X", - "currency": null, - "exchange": "CCY", - "name": "KHR/SGD", - "from_symbol": "KHR", - "to_symbol": "SGD" - }, - { - "id": "KHRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "KHR/THX", - "from_symbol": "KHR", - "to_symbol": "THX" - }, - { - "id": "KHRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "KHR/USD", - "from_symbol": "KHR", - "to_symbol": "USD" - }, - { - "id": "KMF=X", - "currency": "KMF", - "exchange": "CCY", - "name": "USD/KMF", - "from_symbol": "USD", - "to_symbol": "KMF" - }, - { - "id": "KMFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "KMF/BRX", - "from_symbol": "KMF", - "to_symbol": "BRX" - }, - { - "id": "KMFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "KMF/USD", - "from_symbol": "KMF", - "to_symbol": "USD" - }, - { - "id": "KPW=X", - "currency": "KPW", - "exchange": "CCY", - "name": "USD/KPW", - "from_symbol": "USD", - "to_symbol": "KPW" - }, - { - "id": "KRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "USD/KRW", - "from_symbol": "USD", - "to_symbol": "KRW" - }, - { - "id": "KRWAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "KRW/AED", - "from_symbol": "KRW", - "to_symbol": "AED" - }, - { - "id": "KRWAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "KRW/AUD", - "from_symbol": "KRW", - "to_symbol": "AUD" - }, - { - "id": "KRWBRL=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/BRL", - "from_symbol": "KRW", - "to_symbol": "BRL" - }, - { - "id": "KRWBRX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/BRX", - "from_symbol": "KRW", - "to_symbol": "BRX" - }, - { - "id": "KRWBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "KRW/BWP", - "from_symbol": "KRW", - "to_symbol": "BWP" - }, - { - "id": "KRWCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "KRW/CAD", - "from_symbol": "KRW", - "to_symbol": "CAD" - }, - { - "id": "KRWCAX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/CAX", - "from_symbol": "KRW", - "to_symbol": "CAX" - }, - { - "id": "KRWCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "KRW/CHF", - "from_symbol": "KRW", - "to_symbol": "CHF" - }, - { - "id": "KRWCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "KRW/CNY", - "from_symbol": "KRW", - "to_symbol": "CNY" - }, - { - "id": "KRWCRC=X", - "currency": "CRC", - "exchange": "CCY", - "name": "KRW/CRC", - "from_symbol": "KRW", - "to_symbol": "CRC" - }, - { - "id": "KRWCZX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/CZX", - "from_symbol": "KRW", - "to_symbol": "CZX" - }, - { - "id": "KRWDKX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/DKX", - "from_symbol": "KRW", - "to_symbol": "DKX" - }, - { - "id": "KRWEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "KRW/EUR", - "from_symbol": "KRW", - "to_symbol": "EUR" - }, - { - "id": "KRWGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "KRW/GBP", - "from_symbol": "KRW", - "to_symbol": "GBP" - }, - { - "id": "KRWGHS=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/GHS", - "from_symbol": "KRW", - "to_symbol": "GHS" - }, - { - "id": "KRWHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "KRW/HKD", - "from_symbol": "KRW", - "to_symbol": "HKD" - }, - { - "id": "KRWHUX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/HUX", - "from_symbol": "KRW", - "to_symbol": "HUX" - }, - { - "id": "KRWIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "KRW/IDR", - "from_symbol": "KRW", - "to_symbol": "IDR" - }, - { - "id": "KRWINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "KRW/INR", - "from_symbol": "KRW", - "to_symbol": "INR" - }, - { - "id": "KRWJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "KRW/JPY", - "from_symbol": "KRW", - "to_symbol": "JPY" - }, - { - "id": "KRWMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "KRW/MYR", - "from_symbol": "KRW", - "to_symbol": "MYR" - }, - { - "id": "KRWMYX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/MYX", - "from_symbol": "KRW", - "to_symbol": "MYX" - }, - { - "id": "KRWNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "KRW/NZD", - "from_symbol": "KRW", - "to_symbol": "NZD" - }, - { - "id": "KRWPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "KRW/PHP", - "from_symbol": "KRW", - "to_symbol": "PHP" - }, - { - "id": "KRWPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "KRW/PKR", - "from_symbol": "KRW", - "to_symbol": "PKR" - }, - { - "id": "KRWPLX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/PLX", - "from_symbol": "KRW", - "to_symbol": "PLX" - }, - { - "id": "KRWRUX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/RUX", - "from_symbol": "KRW", - "to_symbol": "RUX" - }, - { - "id": "KRWSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "KRW/SEK", - "from_symbol": "KRW", - "to_symbol": "SEK" - }, - { - "id": "KRWSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "KRW/SGD", - "from_symbol": "KRW", - "to_symbol": "SGD" - }, - { - "id": "KRWTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "KRW/THB", - "from_symbol": "KRW", - "to_symbol": "THB" - }, - { - "id": "KRWTHX=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/THX", - "from_symbol": "KRW", - "to_symbol": "THX" - }, - { - "id": "KRWTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "KRW/TWD", - "from_symbol": "KRW", - "to_symbol": "TWD" - }, - { - "id": "KRWUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "KRW/USD", - "from_symbol": "KRW", - "to_symbol": "USD" - }, - { - "id": "KRWXCU=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/XCU", - "from_symbol": "KRW", - "to_symbol": "XCU" - }, - { - "id": "KRWXDR=X", - "currency": null, - "exchange": "CCY", - "name": "KRW/XDR", - "from_symbol": "KRW", - "to_symbol": "XDR" - }, - { - "id": "KRWZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "KRW/ZAR", - "from_symbol": "KRW", - "to_symbol": "ZAR" - }, - { - "id": "KWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "USD/KWD", - "from_symbol": "USD", - "to_symbol": "KWD" - }, - { - "id": "KWDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "KWD/BRX", - "from_symbol": "KWD", - "to_symbol": "BRX" - }, - { - "id": "KWDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "KWD/EUR", - "from_symbol": "KWD", - "to_symbol": "EUR" - }, - { - "id": "KWDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "KWD/GBP", - "from_symbol": "KWD", - "to_symbol": "GBP" - }, - { - "id": "KWDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "KWD/HKD", - "from_symbol": "KWD", - "to_symbol": "HKD" - }, - { - "id": "KWDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "KWD/JPY", - "from_symbol": "KWD", - "to_symbol": "JPY" - }, - { - "id": "KWDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "KWD/PKR", - "from_symbol": "KWD", - "to_symbol": "PKR" - }, - { - "id": "KWDRSD=X", - "currency": null, - "exchange": "CCY", - "name": "KWD/RSD", - "from_symbol": "KWD", - "to_symbol": "RSD" - }, - { - "id": "KWDSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "KWD/SGD", - "from_symbol": "KWD", - "to_symbol": "SGD" - }, - { - "id": "KWDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "KWD/THX", - "from_symbol": "KWD", - "to_symbol": "THX" - }, - { - "id": "KWDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "KWD/USD", - "from_symbol": "KWD", - "to_symbol": "USD" - }, - { - "id": "KWDXDR=X", - "currency": null, - "exchange": "CCY", - "name": "KWD/XDR", - "from_symbol": "KWD", - "to_symbol": "XDR" - }, - { - "id": "KYD=X", - "currency": "KYD", - "exchange": "CCY", - "name": "USD/KYD", - "from_symbol": "USD", - "to_symbol": "KYD" - }, - { - "id": "KYDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "KYD/BRX", - "from_symbol": "KYD", - "to_symbol": "BRX" - }, - { - "id": "KYDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "KYD/EUR", - "from_symbol": "KYD", - "to_symbol": "EUR" - }, - { - "id": "KYDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "KYD/GBP", - "from_symbol": "KYD", - "to_symbol": "GBP" - }, - { - "id": "KYDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "KYD/USD", - "from_symbol": "KYD", - "to_symbol": "USD" - }, - { - "id": "KZT=X", - "currency": "KZT", - "exchange": "CCY", - "name": "USD/KZT", - "from_symbol": "USD", - "to_symbol": "KZT" - }, - { - "id": "KZTBRX=X", - "currency": null, - "exchange": "CCY", - "name": "KZT/BRX", - "from_symbol": "KZT", - "to_symbol": "BRX" - }, - { - "id": "KZTEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "KZT/EUR", - "from_symbol": "KZT", - "to_symbol": "EUR" - }, - { - "id": "KZTGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "KZT/GBP", - "from_symbol": "KZT", - "to_symbol": "GBP" - }, - { - "id": "KZTRUX=X", - "currency": null, - "exchange": "CCY", - "name": "KZT/RUX", - "from_symbol": "KZT", - "to_symbol": "RUX" - }, - { - "id": "KZTUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "KZT/USD", - "from_symbol": "KZT", - "to_symbol": "USD" - }, - { - "id": "LAK=X", - "currency": "LAK", - "exchange": "CCY", - "name": "USD/LAK", - "from_symbol": "USD", - "to_symbol": "LAK" - }, - { - "id": "LAKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "LAK/BRX", - "from_symbol": "LAK", - "to_symbol": "BRX" - }, - { - "id": "LAKTHX=X", - "currency": null, - "exchange": "CCY", - "name": "LAK/THX", - "from_symbol": "LAK", - "to_symbol": "THX" - }, - { - "id": "LAKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "LAK/USD", - "from_symbol": "LAK", - "to_symbol": "USD" - }, - { - "id": "LBP=X", - "currency": "LBP", - "exchange": "CCY", - "name": "USD/LBP", - "from_symbol": "USD", - "to_symbol": "LBP" - }, - { - "id": "LBPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "LBP/BRX", - "from_symbol": "LBP", - "to_symbol": "BRX" - }, - { - "id": "LBPGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "LBP/GBP", - "from_symbol": "LBP", - "to_symbol": "GBP" - }, - { - "id": "LBPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "LBP/USD", - "from_symbol": "LBP", - "to_symbol": "USD" - }, - { - "id": "LKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "USD/LKR", - "from_symbol": "USD", - "to_symbol": "LKR" - }, - { - "id": "LKRAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "LKR/AED", - "from_symbol": "LKR", - "to_symbol": "AED" - }, - { - "id": "LKRAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "LKR/AUD", - "from_symbol": "LKR", - "to_symbol": "AUD" - }, - { - "id": "LKRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "LKR/BRX", - "from_symbol": "LKR", - "to_symbol": "BRX" - }, - { - "id": "LKRCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "LKR/CAD", - "from_symbol": "LKR", - "to_symbol": "CAD" - }, - { - "id": "LKRCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "LKR/CHF", - "from_symbol": "LKR", - "to_symbol": "CHF" - }, - { - "id": "LKRCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "LKR/CNY", - "from_symbol": "LKR", - "to_symbol": "CNY" - }, - { - "id": "LKRDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "LKR/DKK", - "from_symbol": "LKR", - "to_symbol": "DKK" - }, - { - "id": "LKREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "LKR/EUR", - "from_symbol": "LKR", - "to_symbol": "EUR" - }, - { - "id": "LKRGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "LKR/GBP", - "from_symbol": "LKR", - "to_symbol": "GBP" - }, - { - "id": "LKRHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "LKR/HKD", - "from_symbol": "LKR", - "to_symbol": "HKD" - }, - { - "id": "LKRJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "LKR/JPY", - "from_symbol": "LKR", - "to_symbol": "JPY" - }, - { - "id": "LKRNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "LKR/NOK", - "from_symbol": "LKR", - "to_symbol": "NOK" - }, - { - "id": "LKRNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "LKR/NZD", - "from_symbol": "LKR", - "to_symbol": "NZD" - }, - { - "id": "LKRSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "LKR/SAR", - "from_symbol": "LKR", - "to_symbol": "SAR" - }, - { - "id": "LKRSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "LKR/SEK", - "from_symbol": "LKR", - "to_symbol": "SEK" - }, - { - "id": "LKRSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "LKR/SGD", - "from_symbol": "LKR", - "to_symbol": "SGD" - }, - { - "id": "LKRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "LKR/THX", - "from_symbol": "LKR", - "to_symbol": "THX" - }, - { - "id": "LKRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "LKR/USD", - "from_symbol": "LKR", - "to_symbol": "USD" - }, - { - "id": "LKRZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "LKR/ZAR", - "from_symbol": "LKR", - "to_symbol": "ZAR" - }, - { - "id": "LRD=X", - "currency": "LRD", - "exchange": "CCY", - "name": "USD/LRD", - "from_symbol": "USD", - "to_symbol": "LRD" - }, - { - "id": "LRDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "LRD/BRX", - "from_symbol": "LRD", - "to_symbol": "BRX" - }, - { - "id": "LRDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "LRD/USD", - "from_symbol": "LRD", - "to_symbol": "USD" - }, - { - "id": "LSL=X", - "currency": "LSL", - "exchange": "CCY", - "name": "USD/LSL", - "from_symbol": "USD", - "to_symbol": "LSL" - }, - { - "id": "LSLBRX=X", - "currency": null, - "exchange": "CCY", - "name": "LSL/BRX", - "from_symbol": "LSL", - "to_symbol": "BRX" - }, - { - "id": "LSLUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "LSL/USD", - "from_symbol": "LSL", - "to_symbol": "USD" - }, - { - "id": "LSLZAR=X", - "currency": null, - "exchange": "CCY", - "name": "LSL/ZAR", - "from_symbol": "LSL", - "to_symbol": "ZAR" - }, - { - "id": "LYD=X", - "currency": "LYD", - "exchange": "CCY", - "name": "USD/LYD", - "from_symbol": "USD", - "to_symbol": "LYD" - }, - { - "id": "LYDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "LYD/BRX", - "from_symbol": "LYD", - "to_symbol": "BRX" - }, - { - "id": "MAD=X", - "currency": "MAD", - "exchange": "CCY", - "name": "USD/MAD", - "from_symbol": "USD", - "to_symbol": "MAD" - }, - { - "id": "MADBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MAD/BRX", - "from_symbol": "MAD", - "to_symbol": "BRX" - }, - { - "id": "MADEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "MAD/EUR", - "from_symbol": "MAD", - "to_symbol": "EUR" - }, - { - "id": "MADGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "MAD/GBP", - "from_symbol": "MAD", - "to_symbol": "GBP" - }, - { - "id": "MADUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MAD/USD", - "from_symbol": "MAD", - "to_symbol": "USD" - }, - { - "id": "MDL=X", - "currency": "MDL", - "exchange": "CCY", - "name": "USD/MDL", - "from_symbol": "USD", - "to_symbol": "MDL" - }, - { - "id": "MDLBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MDL/BRX", - "from_symbol": "MDL", - "to_symbol": "BRX" - }, - { - "id": "MDLRUX=X", - "currency": null, - "exchange": "CCY", - "name": "MDL/RUX", - "from_symbol": "MDL", - "to_symbol": "RUX" - }, - { - "id": "MGA=X", - "currency": "MGA", - "exchange": "CCY", - "name": "USD/MGA", - "from_symbol": "USD", - "to_symbol": "MGA" - }, - { - "id": "MGABRX=X", - "currency": null, - "exchange": "CCY", - "name": "MGA/BRX", - "from_symbol": "MGA", - "to_symbol": "BRX" - }, - { - "id": "MGAUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MGA/USD", - "from_symbol": "MGA", - "to_symbol": "USD" - }, - { - "id": "MKD=X", - "currency": "MKD", - "exchange": "CCY", - "name": "USD/MKD", - "from_symbol": "USD", - "to_symbol": "MKD" - }, - { - "id": "MKDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MKD/BRX", - "from_symbol": "MKD", - "to_symbol": "BRX" - }, - { - "id": "MKDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "MKD/GBP", - "from_symbol": "MKD", - "to_symbol": "GBP" - }, - { - "id": "MMK=X", - "currency": "MMK", - "exchange": "CCY", - "name": "USD/MMK", - "from_symbol": "USD", - "to_symbol": "MMK" - }, - { - "id": "MMKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MMK/BRX", - "from_symbol": "MMK", - "to_symbol": "BRX" - }, - { - "id": "MMKMYX=X", - "currency": null, - "exchange": "CCY", - "name": "MMK/MYX", - "from_symbol": "MMK", - "to_symbol": "MYX" - }, - { - "id": "MMKTHX=X", - "currency": null, - "exchange": "CCY", - "name": "MMK/THX", - "from_symbol": "MMK", - "to_symbol": "THX" - }, - { - "id": "MMKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MMK/USD", - "from_symbol": "MMK", - "to_symbol": "USD" - }, - { - "id": "MNT=X", - "currency": "MNT", - "exchange": "CCY", - "name": "USD/MNT", - "from_symbol": "USD", - "to_symbol": "MNT" - }, - { - "id": "MNTBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MNT/BRX", - "from_symbol": "MNT", - "to_symbol": "BRX" - }, - { - "id": "MNTUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MNT/USD", - "from_symbol": "MNT", - "to_symbol": "USD" - }, - { - "id": "MOP=X", - "currency": "MOP", - "exchange": "CCY", - "name": "USD/MOP", - "from_symbol": "USD", - "to_symbol": "MOP" - }, - { - "id": "MOPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MOP/BRX", - "from_symbol": "MOP", - "to_symbol": "BRX" - }, - { - "id": "MOPHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "MOP/HKD", - "from_symbol": "MOP", - "to_symbol": "HKD" - }, - { - "id": "MOPSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "MOP/SGD", - "from_symbol": "MOP", - "to_symbol": "SGD" - }, - { - "id": "MOPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MOP/USD", - "from_symbol": "MOP", - "to_symbol": "USD" - }, - { - "id": "MRU=X", - "currency": "MRU", - "exchange": "CCY", - "name": "USD/MRU", - "from_symbol": "USD", - "to_symbol": "MRU" - }, - { - "id": "MRUBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MRU/BRX", - "from_symbol": "MRU", - "to_symbol": "BRX" - }, - { - "id": "MRUUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MRU/USD", - "from_symbol": "MRU", - "to_symbol": "USD" - }, - { - "id": "MUR=X", - "currency": "MUR", - "exchange": "CCY", - "name": "USD/MUR", - "from_symbol": "USD", - "to_symbol": "MUR" - }, - { - "id": "MURBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MUR/BRX", - "from_symbol": "MUR", - "to_symbol": "BRX" - }, - { - "id": "MURCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "MUR/CHF", - "from_symbol": "MUR", - "to_symbol": "CHF" - }, - { - "id": "MUREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "MUR/EUR", - "from_symbol": "MUR", - "to_symbol": "EUR" - }, - { - "id": "MURGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "MUR/GBP", - "from_symbol": "MUR", - "to_symbol": "GBP" - }, - { - "id": "MURUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MUR/USD", - "from_symbol": "MUR", - "to_symbol": "USD" - }, - { - "id": "MURXDR=X", - "currency": null, - "exchange": "CCY", - "name": "MUR/XDR", - "from_symbol": "MUR", - "to_symbol": "XDR" - }, - { - "id": "MVR=X", - "currency": "MVR", - "exchange": "CCY", - "name": "USD/MVR", - "from_symbol": "USD", - "to_symbol": "MVR" - }, - { - "id": "MVRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MVR/BRX", - "from_symbol": "MVR", - "to_symbol": "BRX" - }, - { - "id": "MVRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "MVR/THX", - "from_symbol": "MVR", - "to_symbol": "THX" - }, - { - "id": "MWK=X", - "currency": "MWK", - "exchange": "CCY", - "name": "USD/MWK", - "from_symbol": "USD", - "to_symbol": "MWK" - }, - { - "id": "MWKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MWK/BRX", - "from_symbol": "MWK", - "to_symbol": "BRX" - }, - { - "id": "MWKGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "MWK/GBP", - "from_symbol": "MWK", - "to_symbol": "GBP" - }, - { - "id": "MWKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MWK/USD", - "from_symbol": "MWK", - "to_symbol": "USD" - }, - { - "id": "MXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "USD/MXN", - "from_symbol": "USD", - "to_symbol": "MXN" - }, - { - "id": "MXNAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "MXN/AUD", - "from_symbol": "MXN", - "to_symbol": "AUD" - }, - { - "id": "MXNBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "MXN/BRL", - "from_symbol": "MXN", - "to_symbol": "BRL" - }, - { - "id": "MXNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/BRX", - "from_symbol": "MXN", - "to_symbol": "BRX" - }, - { - "id": "MXNCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "MXN/CAD", - "from_symbol": "MXN", - "to_symbol": "CAD" - }, - { - "id": "MXNCAX=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/CAX", - "from_symbol": "MXN", - "to_symbol": "CAX" - }, - { - "id": "MXNCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "MXN/CHF", - "from_symbol": "MXN", - "to_symbol": "CHF" - }, - { - "id": "MXNCZX=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/CZX", - "from_symbol": "MXN", - "to_symbol": "CZX" - }, - { - "id": "MXNDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "MXN/DKK", - "from_symbol": "MXN", - "to_symbol": "DKK" - }, - { - "id": "MXNDKX=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/DKX", - "from_symbol": "MXN", - "to_symbol": "DKX" - }, - { - "id": "MXNEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "MXN/EUR", - "from_symbol": "MXN", - "to_symbol": "EUR" - }, - { - "id": "MXNGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "MXN/GBP", - "from_symbol": "MXN", - "to_symbol": "GBP" - }, - { - "id": "MXNHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "MXN/HKD", - "from_symbol": "MXN", - "to_symbol": "HKD" - }, - { - "id": "MXNHUX=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/HUX", - "from_symbol": "MXN", - "to_symbol": "HUX" - }, - { - "id": "MXNJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "MXN/JPY", - "from_symbol": "MXN", - "to_symbol": "JPY" - }, - { - "id": "MXNPLX=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/PLX", - "from_symbol": "MXN", - "to_symbol": "PLX" - }, - { - "id": "MXNSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "MXN/SGD", - "from_symbol": "MXN", - "to_symbol": "SGD" - }, - { - "id": "MXNTHX=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/THX", - "from_symbol": "MXN", - "to_symbol": "THX" - }, - { - "id": "MXNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MXN/USD", - "from_symbol": "MXN", - "to_symbol": "USD" - }, - { - "id": "MXNXCU=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/XCU", - "from_symbol": "MXN", - "to_symbol": "XCU" - }, - { - "id": "MXNXDR=X", - "currency": null, - "exchange": "CCY", - "name": "MXN/XDR", - "from_symbol": "MXN", - "to_symbol": "XDR" - }, - { - "id": "MXNZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "MXN/ZAR", - "from_symbol": "MXN", - "to_symbol": "ZAR" - }, - { - "id": "MXV=X", - "currency": "MXV", - "exchange": "CCY", - "name": "USD/MXV", - "from_symbol": "USD", - "to_symbol": "MXV" - }, - { - "id": "MXVMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "MXV/MXN", - "from_symbol": "MXV", - "to_symbol": "MXN" - }, - { - "id": "MXX=X", - "currency": "MXX", - "exchange": "CCY", - "name": "USD/MXX", - "from_symbol": "USD", - "to_symbol": "MXX" - }, - { - "id": "MYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "USD/MYR", - "from_symbol": "USD", - "to_symbol": "MYR" - }, - { - "id": "MYRAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "MYR/AUD", - "from_symbol": "MYR", - "to_symbol": "AUD" - }, - { - "id": "MYRBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "MYR/BRL", - "from_symbol": "MYR", - "to_symbol": "BRL" - }, - { - "id": "MYRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MYR/BRX", - "from_symbol": "MYR", - "to_symbol": "BRX" - }, - { - "id": "MYRCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "MYR/CAD", - "from_symbol": "MYR", - "to_symbol": "CAD" - }, - { - "id": "MYRCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "MYR/CHF", - "from_symbol": "MYR", - "to_symbol": "CHF" - }, - { - "id": "MYRCNH=X", - "currency": null, - "exchange": "CCY", - "name": "MYR/CNH", - "from_symbol": "MYR", - "to_symbol": "CNH" - }, - { - "id": "MYRCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "MYR/CNY", - "from_symbol": "MYR", - "to_symbol": "CNY" - }, - { - "id": "MYRCZX=X", - "currency": null, - "exchange": "CCY", - "name": "MYR/CZX", - "from_symbol": "MYR", - "to_symbol": "CZX" - }, - { - "id": "MYRDKX=X", - "currency": null, - "exchange": "CCY", - "name": "MYR/DKX", - "from_symbol": "MYR", - "to_symbol": "DKX" - }, - { - "id": "MYREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "MYR/EUR", - "from_symbol": "MYR", - "to_symbol": "EUR" - }, - { - "id": "MYRGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "MYR/GBP", - "from_symbol": "MYR", - "to_symbol": "GBP" - }, - { - "id": "MYRHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "MYR/HKD", - "from_symbol": "MYR", - "to_symbol": "HKD" - }, - { - "id": "MYRHUX=X", - "currency": null, - "exchange": "CCY", - "name": "MYR/HUX", - "from_symbol": "MYR", - "to_symbol": "HUX" - }, - { - "id": "MYRIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "MYR/IDR", - "from_symbol": "MYR", - "to_symbol": "IDR" - }, - { - "id": "MYRINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "MYR/INR", - "from_symbol": "MYR", - "to_symbol": "INR" - }, - { - "id": "MYRJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "MYR/JPY", - "from_symbol": "MYR", - "to_symbol": "JPY" - }, - { - "id": "MYRKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "MYR/KRW", - "from_symbol": "MYR", - "to_symbol": "KRW" - }, - { - "id": "MYRNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "MYR/NZD", - "from_symbol": "MYR", - "to_symbol": "NZD" - }, - { - "id": "MYRPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "MYR/PHP", - "from_symbol": "MYR", - "to_symbol": "PHP" - }, - { - "id": "MYRPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "MYR/PKR", - "from_symbol": "MYR", - "to_symbol": "PKR" - }, - { - "id": "MYRPLX=X", - "currency": null, - "exchange": "CCY", - "name": "MYR/PLX", - "from_symbol": "MYR", - "to_symbol": "PLX" - }, - { - "id": "MYRRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "MYR/RUB", - "from_symbol": "MYR", - "to_symbol": "RUB" - }, - { - "id": "MYRSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "MYR/SEK", - "from_symbol": "MYR", - "to_symbol": "SEK" - }, - { - "id": "MYRSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "MYR/SGD", - "from_symbol": "MYR", - "to_symbol": "SGD" - }, - { - "id": "MYRTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "MYR/THB", - "from_symbol": "MYR", - "to_symbol": "THB" - }, - { - "id": "MYRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "MYR/THX", - "from_symbol": "MYR", - "to_symbol": "THX" - }, - { - "id": "MYRTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "MYR/TND", - "from_symbol": "MYR", - "to_symbol": "TND" - }, - { - "id": "MYRTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "MYR/TWD", - "from_symbol": "MYR", - "to_symbol": "TWD" - }, - { - "id": "MYRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MYR/USD", - "from_symbol": "MYR", - "to_symbol": "USD" - }, - { - "id": "MYRXDR=X", - "currency": null, - "exchange": "CCY", - "name": "MYR/XDR", - "from_symbol": "MYR", - "to_symbol": "XDR" - }, - { - "id": "MYRZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "MYR/ZAR", - "from_symbol": "MYR", - "to_symbol": "ZAR" - }, - { - "id": "MYX=X", - "currency": "MYX", - "exchange": "CCY", - "name": "USD/MYX", - "from_symbol": "USD", - "to_symbol": "MYX" - }, - { - "id": "MZN=X", - "currency": "MZN", - "exchange": "CCY", - "name": "USD/MZN", - "from_symbol": "USD", - "to_symbol": "MZN" - }, - { - "id": "MZNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "MZN/BRX", - "from_symbol": "MZN", - "to_symbol": "BRX" - }, - { - "id": "MZNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "MZN/USD", - "from_symbol": "MZN", - "to_symbol": "USD" - }, - { - "id": "NAD=X", - "currency": "NAD", - "exchange": "CCY", - "name": "USD/NAD", - "from_symbol": "USD", - "to_symbol": "NAD" - }, - { - "id": "NADBRX=X", - "currency": null, - "exchange": "CCY", - "name": "NAD/BRX", - "from_symbol": "NAD", - "to_symbol": "BRX" - }, - { - "id": "NADEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "NAD/EUR", - "from_symbol": "NAD", - "to_symbol": "EUR" - }, - { - "id": "NADGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "NAD/GBP", - "from_symbol": "NAD", - "to_symbol": "GBP" - }, - { - "id": "NADUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "NAD/USD", - "from_symbol": "NAD", - "to_symbol": "USD" - }, - { - "id": "NADZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "NAD/ZAR", - "from_symbol": "NAD", - "to_symbol": "ZAR" - }, - { - "id": "NGN=X", - "currency": "NGN", - "exchange": "CCY", - "name": "USD/NGN", - "from_symbol": "USD", - "to_symbol": "NGN" - }, - { - "id": "NGNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "NGN/BRX", - "from_symbol": "NGN", - "to_symbol": "BRX" - }, - { - "id": "NGNEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "NGN/EUR", - "from_symbol": "NGN", - "to_symbol": "EUR" - }, - { - "id": "NGNGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "NGN/GBP", - "from_symbol": "NGN", - "to_symbol": "GBP" - }, - { - "id": "NGNJPY=X", - "currency": null, - "exchange": "CCY", - "name": "NGN/JPY", - "from_symbol": "NGN", - "to_symbol": "JPY" - }, - { - "id": "NGNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "NGN/USD", - "from_symbol": "NGN", - "to_symbol": "USD" - }, - { - "id": "NIO=X", - "currency": "NIO", - "exchange": "CCY", - "name": "USD/NIO", - "from_symbol": "USD", - "to_symbol": "NIO" - }, - { - "id": "NIOBRX=X", - "currency": null, - "exchange": "CCY", - "name": "NIO/BRX", - "from_symbol": "NIO", - "to_symbol": "BRX" - }, - { - "id": "NIOUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "NIO/USD", - "from_symbol": "NIO", - "to_symbol": "USD" - }, - { - "id": "NOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "USD/NOK", - "from_symbol": "USD", - "to_symbol": "NOK" - }, - { - "id": "NOKAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "NOK/AED", - "from_symbol": "NOK", - "to_symbol": "AED" - }, - { - "id": "NOKAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "NOK/AUD", - "from_symbol": "NOK", - "to_symbol": "AUD" - }, - { - "id": "NOKBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "NOK/BAM", - "from_symbol": "NOK", - "to_symbol": "BAM" - }, - { - "id": "NOKBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "NOK/BRL", - "from_symbol": "NOK", - "to_symbol": "BRL" - }, - { - "id": "NOKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/BRX", - "from_symbol": "NOK", - "to_symbol": "BRX" - }, - { - "id": "NOKCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "NOK/CAD", - "from_symbol": "NOK", - "to_symbol": "CAD" - }, - { - "id": "NOKCAX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/CAX", - "from_symbol": "NOK", - "to_symbol": "CAX" - }, - { - "id": "NOKCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "NOK/CHF", - "from_symbol": "NOK", - "to_symbol": "CHF" - }, - { - "id": "NOKCNH=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/CNH", - "from_symbol": "NOK", - "to_symbol": "CNH" - }, - { - "id": "NOKCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "NOK/CZK", - "from_symbol": "NOK", - "to_symbol": "CZK" - }, - { - "id": "NOKCZX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/CZX", - "from_symbol": "NOK", - "to_symbol": "CZX" - }, - { - "id": "NOKDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "NOK/DKK", - "from_symbol": "NOK", - "to_symbol": "DKK" - }, - { - "id": "NOKDKX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/DKX", - "from_symbol": "NOK", - "to_symbol": "DKX" - }, - { - "id": "NOKEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "NOK/EUR", - "from_symbol": "NOK", - "to_symbol": "EUR" - }, - { - "id": "NOKGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "NOK/GBP", - "from_symbol": "NOK", - "to_symbol": "GBP" - }, - { - "id": "NOKHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "NOK/HKD", - "from_symbol": "NOK", - "to_symbol": "HKD" - }, - { - "id": "NOKHRX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/HRX", - "from_symbol": "NOK", - "to_symbol": "HRX" - }, - { - "id": "NOKHUX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/HUX", - "from_symbol": "NOK", - "to_symbol": "HUX" - }, - { - "id": "NOKISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "NOK/ISK", - "from_symbol": "NOK", - "to_symbol": "ISK" - }, - { - "id": "NOKISX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/ISX", - "from_symbol": "NOK", - "to_symbol": "ISX" - }, - { - "id": "NOKJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "NOK/JPY", - "from_symbol": "NOK", - "to_symbol": "JPY" - }, - { - "id": "NOKLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "NOK/LKR", - "from_symbol": "NOK", - "to_symbol": "LKR" - }, - { - "id": "NOKNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "NOK/NZD", - "from_symbol": "NOK", - "to_symbol": "NZD" - }, - { - "id": "NOKPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "NOK/PKR", - "from_symbol": "NOK", - "to_symbol": "PKR" - }, - { - "id": "NOKPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "NOK/PLN", - "from_symbol": "NOK", - "to_symbol": "PLN" - }, - { - "id": "NOKPLX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/PLX", - "from_symbol": "NOK", - "to_symbol": "PLX" - }, - { - "id": "NOKRSD=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/RSD", - "from_symbol": "NOK", - "to_symbol": "RSD" - }, - { - "id": "NOKRUX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/RUX", - "from_symbol": "NOK", - "to_symbol": "RUX" - }, - { - "id": "NOKSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "NOK/SEK", - "from_symbol": "NOK", - "to_symbol": "SEK" - }, - { - "id": "NOKSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "NOK/SGD", - "from_symbol": "NOK", - "to_symbol": "SGD" - }, - { - "id": "NOKTHX=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/THX", - "from_symbol": "NOK", - "to_symbol": "THX" - }, - { - "id": "NOKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "NOK/USD", - "from_symbol": "NOK", - "to_symbol": "USD" - }, - { - "id": "NOKXCU=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/XCU", - "from_symbol": "NOK", - "to_symbol": "XCU" - }, - { - "id": "NOKXDR=X", - "currency": null, - "exchange": "CCY", - "name": "NOK/XDR", - "from_symbol": "NOK", - "to_symbol": "XDR" - }, - { - "id": "NOKZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "NOK/ZAR", - "from_symbol": "NOK", - "to_symbol": "ZAR" - }, - { - "id": "NPR=X", - "currency": "NPR", - "exchange": "CCY", - "name": "USD/NPR", - "from_symbol": "USD", - "to_symbol": "NPR" - }, - { - "id": "NPRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "NPR/BRX", - "from_symbol": "NPR", - "to_symbol": "BRX" - }, - { - "id": "NPRMYX=X", - "currency": null, - "exchange": "CCY", - "name": "NPR/MYX", - "from_symbol": "NPR", - "to_symbol": "MYX" - }, - { - "id": "NPRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "NPR/THX", - "from_symbol": "NPR", - "to_symbol": "THX" - }, - { - "id": "NPRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "NPR/USD", - "from_symbol": "NPR", - "to_symbol": "USD" - }, - { - "id": "NZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "USD/NZD", - "from_symbol": "USD", - "to_symbol": "NZD" - }, - { - "id": "NZDAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "NZD/AED", - "from_symbol": "NZD", - "to_symbol": "AED" - }, - { - "id": "NZDAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "NZD/AUD", - "from_symbol": "NZD", - "to_symbol": "AUD" - }, - { - "id": "NZDBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "NZD/BRL", - "from_symbol": "NZD", - "to_symbol": "BRL" - }, - { - "id": "NZDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/BRX", - "from_symbol": "NZD", - "to_symbol": "BRX" - }, - { - "id": "NZDCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "NZD/CAD", - "from_symbol": "NZD", - "to_symbol": "CAD" - }, - { - "id": "NZDCAX=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/CAX", - "from_symbol": "NZD", - "to_symbol": "CAX" - }, - { - "id": "NZDCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "NZD/CHF", - "from_symbol": "NZD", - "to_symbol": "CHF" - }, - { - "id": "NZDCNH=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/CNH", - "from_symbol": "NZD", - "to_symbol": "CNH" - }, - { - "id": "NZDCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "NZD/CNY", - "from_symbol": "NZD", - "to_symbol": "CNY" - }, - { - "id": "NZDCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "NZD/CZK", - "from_symbol": "NZD", - "to_symbol": "CZK" - }, - { - "id": "NZDCZX=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/CZX", - "from_symbol": "NZD", - "to_symbol": "CZX" - }, - { - "id": "NZDDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "NZD/DKK", - "from_symbol": "NZD", - "to_symbol": "DKK" - }, - { - "id": "NZDDKX=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/DKX", - "from_symbol": "NZD", - "to_symbol": "DKX" - }, - { - "id": "NZDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "NZD/EUR", - "from_symbol": "NZD", - "to_symbol": "EUR" - }, - { - "id": "NZDFJD=X", - "currency": "FJD", - "exchange": "CCY", - "name": "NZD/FJD", - "from_symbol": "NZD", - "to_symbol": "FJD" - }, - { - "id": "NZDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "NZD/GBP", - "from_symbol": "NZD", - "to_symbol": "GBP" - }, - { - "id": "NZDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "NZD/HKD", - "from_symbol": "NZD", - "to_symbol": "HKD" - }, - { - "id": "NZDHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "NZD/HUF", - "from_symbol": "NZD", - "to_symbol": "HUF" - }, - { - "id": "NZDHUX=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/HUX", - "from_symbol": "NZD", - "to_symbol": "HUX" - }, - { - "id": "NZDIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "NZD/IDR", - "from_symbol": "NZD", - "to_symbol": "IDR" - }, - { - "id": "NZDILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "NZD/ILS", - "from_symbol": "NZD", - "to_symbol": "ILS" - }, - { - "id": "NZDINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "NZD/INR", - "from_symbol": "NZD", - "to_symbol": "INR" - }, - { - "id": "NZDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "NZD/JPY", - "from_symbol": "NZD", - "to_symbol": "JPY" - }, - { - "id": "NZDKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "NZD/KRW", - "from_symbol": "NZD", - "to_symbol": "KRW" - }, - { - "id": "NZDLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "NZD/LKR", - "from_symbol": "NZD", - "to_symbol": "LKR" - }, - { - "id": "NZDMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "NZD/MXN", - "from_symbol": "NZD", - "to_symbol": "MXN" - }, - { - "id": "NZDMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "NZD/MYR", - "from_symbol": "NZD", - "to_symbol": "MYR" - }, - { - "id": "NZDMYX=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/MYX", - "from_symbol": "NZD", - "to_symbol": "MYX" - }, - { - "id": "NZDNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "NZD/NOK", - "from_symbol": "NZD", - "to_symbol": "NOK" - }, - { - "id": "NZDPGK=X", - "currency": "PGK", - "exchange": "CCY", - "name": "NZD/PGK", - "from_symbol": "NZD", - "to_symbol": "PGK" - }, - { - "id": "NZDPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "NZD/PHP", - "from_symbol": "NZD", - "to_symbol": "PHP" - }, - { - "id": "NZDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "NZD/PKR", - "from_symbol": "NZD", - "to_symbol": "PKR" - }, - { - "id": "NZDPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "NZD/PLN", - "from_symbol": "NZD", - "to_symbol": "PLN" - }, - { - "id": "NZDPLX=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/PLX", - "from_symbol": "NZD", - "to_symbol": "PLX" - }, - { - "id": "NZDSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "NZD/SAR", - "from_symbol": "NZD", - "to_symbol": "SAR" - }, - { - "id": "NZDSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "NZD/SEK", - "from_symbol": "NZD", - "to_symbol": "SEK" - }, - { - "id": "NZDSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "NZD/SGD", - "from_symbol": "NZD", - "to_symbol": "SGD" - }, - { - "id": "NZDTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "NZD/THB", - "from_symbol": "NZD", - "to_symbol": "THB" - }, - { - "id": "NZDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/THX", - "from_symbol": "NZD", - "to_symbol": "THX" - }, - { - "id": "NZDTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "NZD/TND", - "from_symbol": "NZD", - "to_symbol": "TND" - }, - { - "id": "NZDTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "NZD/TRY", - "from_symbol": "NZD", - "to_symbol": "TRY" - }, - { - "id": "NZDTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "NZD/TWD", - "from_symbol": "NZD", - "to_symbol": "TWD" - }, - { - "id": "NZDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "NZD/USD", - "from_symbol": "NZD", - "to_symbol": "USD" - }, - { - "id": "NZDXCU=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/XCU", - "from_symbol": "NZD", - "to_symbol": "XCU" - }, - { - "id": "NZDXDR=X", - "currency": null, - "exchange": "CCY", - "name": "NZD/XDR", - "from_symbol": "NZD", - "to_symbol": "XDR" - }, - { - "id": "NZDZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "NZD/ZAR", - "from_symbol": "NZD", - "to_symbol": "ZAR" - }, - { - "id": "OMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "USD/OMR", - "from_symbol": "USD", - "to_symbol": "OMR" - }, - { - "id": "OMRAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "OMR/AED", - "from_symbol": "OMR", - "to_symbol": "AED" - }, - { - "id": "OMRAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "OMR/AUD", - "from_symbol": "OMR", - "to_symbol": "AUD" - }, - { - "id": "OMRBHD=X", - "currency": "BHD", - "exchange": "CCY", - "name": "OMR/BHD", - "from_symbol": "OMR", - "to_symbol": "BHD" - }, - { - "id": "OMRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "OMR/BRX", - "from_symbol": "OMR", - "to_symbol": "BRX" - }, - { - "id": "OMRCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "OMR/CAD", - "from_symbol": "OMR", - "to_symbol": "CAD" - }, - { - "id": "OMRCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "OMR/CHF", - "from_symbol": "OMR", - "to_symbol": "CHF" - }, - { - "id": "OMRDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "OMR/DKK", - "from_symbol": "OMR", - "to_symbol": "DKK" - }, - { - "id": "OMREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "OMR/EUR", - "from_symbol": "OMR", - "to_symbol": "EUR" - }, - { - "id": "OMRGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "OMR/GBP", - "from_symbol": "OMR", - "to_symbol": "GBP" - }, - { - "id": "OMRHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "OMR/HKD", - "from_symbol": "OMR", - "to_symbol": "HKD" - }, - { - "id": "OMRINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "OMR/INR", - "from_symbol": "OMR", - "to_symbol": "INR" - }, - { - "id": "OMRJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "OMR/JPY", - "from_symbol": "OMR", - "to_symbol": "JPY" - }, - { - "id": "OMRKES=X", - "currency": "KES", - "exchange": "CCY", - "name": "OMR/KES", - "from_symbol": "OMR", - "to_symbol": "KES" - }, - { - "id": "OMRPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "OMR/PKR", - "from_symbol": "OMR", - "to_symbol": "PKR" - }, - { - "id": "OMRQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "OMR/QAR", - "from_symbol": "OMR", - "to_symbol": "QAR" - }, - { - "id": "OMRSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "OMR/SGD", - "from_symbol": "OMR", - "to_symbol": "SGD" - }, - { - "id": "OMRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "OMR/THX", - "from_symbol": "OMR", - "to_symbol": "THX" - }, - { - "id": "OMRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "OMR/USD", - "from_symbol": "OMR", - "to_symbol": "USD" - }, - { - "id": "OMRXDR=X", - "currency": null, - "exchange": "CCY", - "name": "OMR/XDR", - "from_symbol": "OMR", - "to_symbol": "XDR" - }, - { - "id": "PAB=X", - "currency": "PAB", - "exchange": "CCY", - "name": "USD/PAB", - "from_symbol": "USD", - "to_symbol": "PAB" - }, - { - "id": "PABBRX=X", - "currency": null, - "exchange": "CCY", - "name": "PAB/BRX", - "from_symbol": "PAB", - "to_symbol": "BRX" - }, - { - "id": "PABGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "PAB/GBP", - "from_symbol": "PAB", - "to_symbol": "GBP" - }, - { - "id": "PABUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "PAB/USD", - "from_symbol": "PAB", - "to_symbol": "USD" - }, - { - "id": "PEN=X", - "currency": "PEN", - "exchange": "CCY", - "name": "USD/PEN", - "from_symbol": "USD", - "to_symbol": "PEN" - }, - { - "id": "PENBRX=X", - "currency": null, - "exchange": "CCY", - "name": "PEN/BRX", - "from_symbol": "PEN", - "to_symbol": "BRX" - }, - { - "id": "PENCAX=X", - "currency": null, - "exchange": "CCY", - "name": "PEN/CAX", - "from_symbol": "PEN", - "to_symbol": "CAX" - }, - { - "id": "PENEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "PEN/EUR", - "from_symbol": "PEN", - "to_symbol": "EUR" - }, - { - "id": "PENGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "PEN/GBP", - "from_symbol": "PEN", - "to_symbol": "GBP" - }, - { - "id": "PENJPY=X", - "currency": null, - "exchange": "CCY", - "name": "PEN/JPY", - "from_symbol": "PEN", - "to_symbol": "JPY" - }, - { - "id": "PENUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "PEN/USD", - "from_symbol": "PEN", - "to_symbol": "USD" - }, - { - "id": "PENXDR=X", - "currency": null, - "exchange": "CCY", - "name": "PEN/XDR", - "from_symbol": "PEN", - "to_symbol": "XDR" - }, - { - "id": "PGK=X", - "currency": "PGK", - "exchange": "CCY", - "name": "USD/PGK", - "from_symbol": "USD", - "to_symbol": "PGK" - }, - { - "id": "PGKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "PGK/BRX", - "from_symbol": "PGK", - "to_symbol": "BRX" - }, - { - "id": "PGKGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "PGK/GBP", - "from_symbol": "PGK", - "to_symbol": "GBP" - }, - { - "id": "PGKHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "PGK/HKD", - "from_symbol": "PGK", - "to_symbol": "HKD" - }, - { - "id": "PGKTHX=X", - "currency": null, - "exchange": "CCY", - "name": "PGK/THX", - "from_symbol": "PGK", - "to_symbol": "THX" - }, - { - "id": "PGKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "PGK/USD", - "from_symbol": "PGK", - "to_symbol": "USD" - }, - { - "id": "PHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "USD/PHP", - "from_symbol": "USD", - "to_symbol": "PHP" - }, - { - "id": "PHPAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "PHP/AUD", - "from_symbol": "PHP", - "to_symbol": "AUD" - }, - { - "id": "PHPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "PHP/BRX", - "from_symbol": "PHP", - "to_symbol": "BRX" - }, - { - "id": "PHPCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "PHP/CAD", - "from_symbol": "PHP", - "to_symbol": "CAD" - }, - { - "id": "PHPCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "PHP/CHF", - "from_symbol": "PHP", - "to_symbol": "CHF" - }, - { - "id": "PHPCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "PHP/CNY", - "from_symbol": "PHP", - "to_symbol": "CNY" - }, - { - "id": "PHPCZX=X", - "currency": null, - "exchange": "CCY", - "name": "PHP/CZX", - "from_symbol": "PHP", - "to_symbol": "CZX" - }, - { - "id": "PHPDKX=X", - "currency": null, - "exchange": "CCY", - "name": "PHP/DKX", - "from_symbol": "PHP", - "to_symbol": "DKX" - }, - { - "id": "PHPEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "PHP/EUR", - "from_symbol": "PHP", - "to_symbol": "EUR" - }, - { - "id": "PHPGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "PHP/GBP", - "from_symbol": "PHP", - "to_symbol": "GBP" - }, - { - "id": "PHPHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "PHP/HKD", - "from_symbol": "PHP", - "to_symbol": "HKD" - }, - { - "id": "PHPHUX=X", - "currency": null, - "exchange": "CCY", - "name": "PHP/HUX", - "from_symbol": "PHP", - "to_symbol": "HUX" - }, - { - "id": "PHPIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "PHP/IDR", - "from_symbol": "PHP", - "to_symbol": "IDR" - }, - { - "id": "PHPINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "PHP/INR", - "from_symbol": "PHP", - "to_symbol": "INR" - }, - { - "id": "PHPJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "PHP/JPY", - "from_symbol": "PHP", - "to_symbol": "JPY" - }, - { - "id": "PHPKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "PHP/KRW", - "from_symbol": "PHP", - "to_symbol": "KRW" - }, - { - "id": "PHPMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "PHP/MYR", - "from_symbol": "PHP", - "to_symbol": "MYR" - }, - { - "id": "PHPMYX=X", - "currency": null, - "exchange": "CCY", - "name": "PHP/MYX", - "from_symbol": "PHP", - "to_symbol": "MYX" - }, - { - "id": "PHPNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "PHP/NZD", - "from_symbol": "PHP", - "to_symbol": "NZD" - }, - { - "id": "PHPPLX=X", - "currency": null, - "exchange": "CCY", - "name": "PHP/PLX", - "from_symbol": "PHP", - "to_symbol": "PLX" - }, - { - "id": "PHPSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "PHP/SGD", - "from_symbol": "PHP", - "to_symbol": "SGD" - }, - { - "id": "PHPTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "PHP/THB", - "from_symbol": "PHP", - "to_symbol": "THB" - }, - { - "id": "PHPTHX=X", - "currency": null, - "exchange": "CCY", - "name": "PHP/THX", - "from_symbol": "PHP", - "to_symbol": "THX" - }, - { - "id": "PHPTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "PHP/TWD", - "from_symbol": "PHP", - "to_symbol": "TWD" - }, - { - "id": "PHPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "PHP/USD", - "from_symbol": "PHP", - "to_symbol": "USD" - }, - { - "id": "PHPVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "PHP/VND", - "from_symbol": "PHP", - "to_symbol": "VND" - }, - { - "id": "PHPXDR=X", - "currency": null, - "exchange": "CCY", - "name": "PHP/XDR", - "from_symbol": "PHP", - "to_symbol": "XDR" - }, - { - "id": "PKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "USD/PKR", - "from_symbol": "USD", - "to_symbol": "PKR" - }, - { - "id": "PKRAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "PKR/AED", - "from_symbol": "PKR", - "to_symbol": "AED" - }, - { - "id": "PKRAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "PKR/AUD", - "from_symbol": "PKR", - "to_symbol": "AUD" - }, - { - "id": "PKRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "PKR/BRX", - "from_symbol": "PKR", - "to_symbol": "BRX" - }, - { - "id": "PKRCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "PKR/CAD", - "from_symbol": "PKR", - "to_symbol": "CAD" - }, - { - "id": "PKRCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "PKR/CHF", - "from_symbol": "PKR", - "to_symbol": "CHF" - }, - { - "id": "PKREGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "PKR/EGP", - "from_symbol": "PKR", - "to_symbol": "EGP" - }, - { - "id": "PKREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "PKR/EUR", - "from_symbol": "PKR", - "to_symbol": "EUR" - }, - { - "id": "PKRGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "PKR/GBP", - "from_symbol": "PKR", - "to_symbol": "GBP" - }, - { - "id": "PKRHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "PKR/HKD", - "from_symbol": "PKR", - "to_symbol": "HKD" - }, - { - "id": "PKRINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "PKR/INR", - "from_symbol": "PKR", - "to_symbol": "INR" - }, - { - "id": "PKRJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "PKR/JPY", - "from_symbol": "PKR", - "to_symbol": "JPY" - }, - { - "id": "PKRKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "PKR/KRW", - "from_symbol": "PKR", - "to_symbol": "KRW" - }, - { - "id": "PKRKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "PKR/KWD", - "from_symbol": "PKR", - "to_symbol": "KWD" - }, - { - "id": "PKRMYX=X", - "currency": null, - "exchange": "CCY", - "name": "PKR/MYX", - "from_symbol": "PKR", - "to_symbol": "MYX" - }, - { - "id": "PKRQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "PKR/QAR", - "from_symbol": "PKR", - "to_symbol": "QAR" - }, - { - "id": "PKRSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "PKR/SGD", - "from_symbol": "PKR", - "to_symbol": "SGD" - }, - { - "id": "PKRTHX=X", - "currency": null, - "exchange": "CCY", - "name": "PKR/THX", - "from_symbol": "PKR", - "to_symbol": "THX" - }, - { - "id": "PKRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "PKR/USD", - "from_symbol": "PKR", - "to_symbol": "USD" - }, - { - "id": "PKRVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "PKR/VND", - "from_symbol": "PKR", - "to_symbol": "VND" - }, - { - "id": "PKRZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "PKR/ZAR", - "from_symbol": "PKR", - "to_symbol": "ZAR" - }, - { - "id": "PLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "USD/PLN", - "from_symbol": "USD", - "to_symbol": "PLN" - }, - { - "id": "PLNAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "PLN/AUD", - "from_symbol": "PLN", - "to_symbol": "AUD" - }, - { - "id": "PLNBRX=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/BRX", - "from_symbol": "PLN", - "to_symbol": "BRX" - }, - { - "id": "PLNCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "PLN/CHF", - "from_symbol": "PLN", - "to_symbol": "CHF" - }, - { - "id": "PLNCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "PLN/CNY", - "from_symbol": "PLN", - "to_symbol": "CNY" - }, - { - "id": "PLNCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "PLN/CZK", - "from_symbol": "PLN", - "to_symbol": "CZK" - }, - { - "id": "PLNCZX=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/CZX", - "from_symbol": "PLN", - "to_symbol": "CZX" - }, - { - "id": "PLNDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "PLN/DKK", - "from_symbol": "PLN", - "to_symbol": "DKK" - }, - { - "id": "PLNDKX=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/DKX", - "from_symbol": "PLN", - "to_symbol": "DKX" - }, - { - "id": "PLNEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "PLN/EUR", - "from_symbol": "PLN", - "to_symbol": "EUR" - }, - { - "id": "PLNGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "PLN/GBP", - "from_symbol": "PLN", - "to_symbol": "GBP" - }, - { - "id": "PLNHRX=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/HRX", - "from_symbol": "PLN", - "to_symbol": "HRX" - }, - { - "id": "PLNHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "PLN/HUF", - "from_symbol": "PLN", - "to_symbol": "HUF" - }, - { - "id": "PLNHUX=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/HUX", - "from_symbol": "PLN", - "to_symbol": "HUX" - }, - { - "id": "PLNILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "PLN/ILS", - "from_symbol": "PLN", - "to_symbol": "ILS" - }, - { - "id": "PLNINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "PLN/INR", - "from_symbol": "PLN", - "to_symbol": "INR" - }, - { - "id": "PLNJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "PLN/JPY", - "from_symbol": "PLN", - "to_symbol": "JPY" - }, - { - "id": "PLNRSD=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/RSD", - "from_symbol": "PLN", - "to_symbol": "RSD" - }, - { - "id": "PLNRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "PLN/RUB", - "from_symbol": "PLN", - "to_symbol": "RUB" - }, - { - "id": "PLNRUX=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/RUX", - "from_symbol": "PLN", - "to_symbol": "RUX" - }, - { - "id": "PLNSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "PLN/SEK", - "from_symbol": "PLN", - "to_symbol": "SEK" - }, - { - "id": "PLNTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "PLN/THB", - "from_symbol": "PLN", - "to_symbol": "THB" - }, - { - "id": "PLNTHX=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/THX", - "from_symbol": "PLN", - "to_symbol": "THX" - }, - { - "id": "PLNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "PLN/USD", - "from_symbol": "PLN", - "to_symbol": "USD" - }, - { - "id": "PLNXCU=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/XCU", - "from_symbol": "PLN", - "to_symbol": "XCU" - }, - { - "id": "PLNXDR=X", - "currency": null, - "exchange": "CCY", - "name": "PLN/XDR", - "from_symbol": "PLN", - "to_symbol": "XDR" - }, - { - "id": "PLNZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "PLN/ZAR", - "from_symbol": "PLN", - "to_symbol": "ZAR" - }, - { - "id": "PLX=X", - "currency": "PLX", - "exchange": "CCY", - "name": "USD/PLX", - "from_symbol": "USD", - "to_symbol": "PLX" - }, - { - "id": "PYG=X", - "currency": "PYG", - "exchange": "CCY", - "name": "USD/PYG", - "from_symbol": "USD", - "to_symbol": "PYG" - }, - { - "id": "PYGBRX=X", - "currency": null, - "exchange": "CCY", - "name": "PYG/BRX", - "from_symbol": "PYG", - "to_symbol": "BRX" - }, - { - "id": "PYGUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "PYG/USD", - "from_symbol": "PYG", - "to_symbol": "USD" - }, - { - "id": "QAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "USD/QAR", - "from_symbol": "USD", - "to_symbol": "QAR" - }, - { - "id": "QARAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "QAR/AED", - "from_symbol": "QAR", - "to_symbol": "AED" - }, - { - "id": "QARBRX=X", - "currency": null, - "exchange": "CCY", - "name": "QAR/BRX", - "from_symbol": "QAR", - "to_symbol": "BRX" - }, - { - "id": "QAREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "QAR/EUR", - "from_symbol": "QAR", - "to_symbol": "EUR" - }, - { - "id": "QARGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "QAR/GBP", - "from_symbol": "QAR", - "to_symbol": "GBP" - }, - { - "id": "QARJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "QAR/JPY", - "from_symbol": "QAR", - "to_symbol": "JPY" - }, - { - "id": "QAROMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "QAR/OMR", - "from_symbol": "QAR", - "to_symbol": "OMR" - }, - { - "id": "QARPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "QAR/PKR", - "from_symbol": "QAR", - "to_symbol": "PKR" - }, - { - "id": "QARSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "QAR/SGD", - "from_symbol": "QAR", - "to_symbol": "SGD" - }, - { - "id": "QARTHX=X", - "currency": null, - "exchange": "CCY", - "name": "QAR/THX", - "from_symbol": "QAR", - "to_symbol": "THX" - }, - { - "id": "QARUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "QAR/USD", - "from_symbol": "QAR", - "to_symbol": "USD" - }, - { - "id": "QARXDR=X", - "currency": null, - "exchange": "CCY", - "name": "QAR/XDR", - "from_symbol": "QAR", - "to_symbol": "XDR" - }, - { - "id": "RON=X", - "currency": "RON", - "exchange": "CCY", - "name": "USD/RON", - "from_symbol": "USD", - "to_symbol": "RON" - }, - { - "id": "RONBRX=X", - "currency": null, - "exchange": "CCY", - "name": "RON/BRX", - "from_symbol": "RON", - "to_symbol": "BRX" - }, - { - "id": "RONCZX=X", - "currency": null, - "exchange": "CCY", - "name": "RON/CZX", - "from_symbol": "RON", - "to_symbol": "CZX" - }, - { - "id": "RONDKX=X", - "currency": null, - "exchange": "CCY", - "name": "RON/DKX", - "from_symbol": "RON", - "to_symbol": "DKX" - }, - { - "id": "RONEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "RON/EUR", - "from_symbol": "RON", - "to_symbol": "EUR" - }, - { - "id": "RONGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "RON/GBP", - "from_symbol": "RON", - "to_symbol": "GBP" - }, - { - "id": "RONHUX=X", - "currency": null, - "exchange": "CCY", - "name": "RON/HUX", - "from_symbol": "RON", - "to_symbol": "HUX" - }, - { - "id": "RONJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "RON/JPY", - "from_symbol": "RON", - "to_symbol": "JPY" - }, - { - "id": "RONPLX=X", - "currency": null, - "exchange": "CCY", - "name": "RON/PLX", - "from_symbol": "RON", - "to_symbol": "PLX" - }, - { - "id": "RONRSD=X", - "currency": null, - "exchange": "CCY", - "name": "RON/RSD", - "from_symbol": "RON", - "to_symbol": "RSD" - }, - { - "id": "RONRUX=X", - "currency": null, - "exchange": "CCY", - "name": "RON/RUX", - "from_symbol": "RON", - "to_symbol": "RUX" - }, - { - "id": "RONSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "RON/SEK", - "from_symbol": "RON", - "to_symbol": "SEK" - }, - { - "id": "RONUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "RON/USD", - "from_symbol": "RON", - "to_symbol": "USD" - }, - { - "id": "RSD=X", - "currency": "RSD", - "exchange": "CCY", - "name": "USD/RSD", - "from_symbol": "USD", - "to_symbol": "RSD" - }, - { - "id": "RSDBAM=X", - "currency": null, - "exchange": "CCY", - "name": "RSD/BAM", - "from_symbol": "RSD", - "to_symbol": "BAM" - }, - { - "id": "RSDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "RSD/BRX", - "from_symbol": "RSD", - "to_symbol": "BRX" - }, - { - "id": "RSDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "RSD/EUR", - "from_symbol": "RSD", - "to_symbol": "EUR" - }, - { - "id": "RSDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "RSD/GBP", - "from_symbol": "RSD", - "to_symbol": "GBP" - }, - { - "id": "RSDHUX=X", - "currency": null, - "exchange": "CCY", - "name": "RSD/HUX", - "from_symbol": "RSD", - "to_symbol": "HUX" - }, - { - "id": "RSDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "RSD/USD", - "from_symbol": "RSD", - "to_symbol": "USD" - }, - { - "id": "RUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "USD/RUB", - "from_symbol": "USD", - "to_symbol": "RUB" - }, - { - "id": "RUBAMD=X", - "currency": "AMD", - "exchange": "CCY", - "name": "RUB/AMD", - "from_symbol": "RUB", - "to_symbol": "AMD" - }, - { - "id": "RUBAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "RUB/AUD", - "from_symbol": "RUB", - "to_symbol": "AUD" - }, - { - "id": "RUBBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "RUB/BRL", - "from_symbol": "RUB", - "to_symbol": "BRL" - }, - { - "id": "RUBBRX=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/BRX", - "from_symbol": "RUB", - "to_symbol": "BRX" - }, - { - "id": "RUBCAX=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/CAX", - "from_symbol": "RUB", - "to_symbol": "CAX" - }, - { - "id": "RUBCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "RUB/CHF", - "from_symbol": "RUB", - "to_symbol": "CHF" - }, - { - "id": "RUBCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "RUB/CNY", - "from_symbol": "RUB", - "to_symbol": "CNY" - }, - { - "id": "RUBCZX=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/CZX", - "from_symbol": "RUB", - "to_symbol": "CZX" - }, - { - "id": "RUBDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "RUB/DKK", - "from_symbol": "RUB", - "to_symbol": "DKK" - }, - { - "id": "RUBDKX=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/DKX", - "from_symbol": "RUB", - "to_symbol": "DKX" - }, - { - "id": "RUBEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "RUB/EUR", - "from_symbol": "RUB", - "to_symbol": "EUR" - }, - { - "id": "RUBGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "RUB/GBP", - "from_symbol": "RUB", - "to_symbol": "GBP" - }, - { - "id": "RUBHUX=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/HUX", - "from_symbol": "RUB", - "to_symbol": "HUX" - }, - { - "id": "RUBJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "RUB/JPY", - "from_symbol": "RUB", - "to_symbol": "JPY" - }, - { - "id": "RUBMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "RUB/MYR", - "from_symbol": "RUB", - "to_symbol": "MYR" - }, - { - "id": "RUBNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "RUB/NOK", - "from_symbol": "RUB", - "to_symbol": "NOK" - }, - { - "id": "RUBPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "RUB/PLN", - "from_symbol": "RUB", - "to_symbol": "PLN" - }, - { - "id": "RUBPLX=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/PLX", - "from_symbol": "RUB", - "to_symbol": "PLX" - }, - { - "id": "RUBRSD=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/RSD", - "from_symbol": "RUB", - "to_symbol": "RSD" - }, - { - "id": "RUBSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "RUB/SEK", - "from_symbol": "RUB", - "to_symbol": "SEK" - }, - { - "id": "RUBSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "RUB/SGD", - "from_symbol": "RUB", - "to_symbol": "SGD" - }, - { - "id": "RUBTHX=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/THX", - "from_symbol": "RUB", - "to_symbol": "THX" - }, - { - "id": "RUBUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "RUB/USD", - "from_symbol": "RUB", - "to_symbol": "USD" - }, - { - "id": "RUBXCU=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/XCU", - "from_symbol": "RUB", - "to_symbol": "XCU" - }, - { - "id": "RUBXDR=X", - "currency": null, - "exchange": "CCY", - "name": "RUB/XDR", - "from_symbol": "RUB", - "to_symbol": "XDR" - }, - { - "id": "RUBZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "RUB/ZAR", - "from_symbol": "RUB", - "to_symbol": "ZAR" - }, - { - "id": "RUX=X", - "currency": "RUX", - "exchange": "CCY", - "name": "USD/RUX", - "from_symbol": "USD", - "to_symbol": "RUX" - }, - { - "id": "RWF=X", - "currency": "RWF", - "exchange": "CCY", - "name": "USD/RWF", - "from_symbol": "USD", - "to_symbol": "RWF" - }, - { - "id": "RWFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "RWF/BRX", - "from_symbol": "RWF", - "to_symbol": "BRX" - }, - { - "id": "RWFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "RWF/USD", - "from_symbol": "RWF", - "to_symbol": "USD" - }, - { - "id": "SAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "USD/SAR", - "from_symbol": "USD", - "to_symbol": "SAR" - }, - { - "id": "SARAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "SAR/AED", - "from_symbol": "SAR", - "to_symbol": "AED" - }, - { - "id": "SARBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SAR/BRX", - "from_symbol": "SAR", - "to_symbol": "BRX" - }, - { - "id": "SARCAX=X", - "currency": null, - "exchange": "CCY", - "name": "SAR/CAX", - "from_symbol": "SAR", - "to_symbol": "CAX" - }, - { - "id": "SARCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "SAR/CHF", - "from_symbol": "SAR", - "to_symbol": "CHF" - }, - { - "id": "SAREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "SAR/EUR", - "from_symbol": "SAR", - "to_symbol": "EUR" - }, - { - "id": "SARGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "SAR/GBP", - "from_symbol": "SAR", - "to_symbol": "GBP" - }, - { - "id": "SARHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "SAR/HKD", - "from_symbol": "SAR", - "to_symbol": "HKD" - }, - { - "id": "SARJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "SAR/JPY", - "from_symbol": "SAR", - "to_symbol": "JPY" - }, - { - "id": "SARLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "SAR/LKR", - "from_symbol": "SAR", - "to_symbol": "LKR" - }, - { - "id": "SARMYX=X", - "currency": null, - "exchange": "CCY", - "name": "SAR/MYX", - "from_symbol": "SAR", - "to_symbol": "MYX" - }, - { - "id": "SARPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "SAR/PKR", - "from_symbol": "SAR", - "to_symbol": "PKR" - }, - { - "id": "SARSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "SAR/SEK", - "from_symbol": "SAR", - "to_symbol": "SEK" - }, - { - "id": "SARSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "SAR/SGD", - "from_symbol": "SAR", - "to_symbol": "SGD" - }, - { - "id": "SARTHX=X", - "currency": null, - "exchange": "CCY", - "name": "SAR/THX", - "from_symbol": "SAR", - "to_symbol": "THX" - }, - { - "id": "SARUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SAR/USD", - "from_symbol": "SAR", - "to_symbol": "USD" - }, - { - "id": "SARXDR=X", - "currency": null, - "exchange": "CCY", - "name": "SAR/XDR", - "from_symbol": "SAR", - "to_symbol": "XDR" - }, - { - "id": "SBD=X", - "currency": "SBD", - "exchange": "CCY", - "name": "USD/SBD", - "from_symbol": "USD", - "to_symbol": "SBD" - }, - { - "id": "SBDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SBD/BRX", - "from_symbol": "SBD", - "to_symbol": "BRX" - }, - { - "id": "SBDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SBD/USD", - "from_symbol": "SBD", - "to_symbol": "USD" - }, - { - "id": "SCR=X", - "currency": "SCR", - "exchange": "CCY", - "name": "USD/SCR", - "from_symbol": "USD", - "to_symbol": "SCR" - }, - { - "id": "SCRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SCR/BRX", - "from_symbol": "SCR", - "to_symbol": "BRX" - }, - { - "id": "SCRGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "SCR/GBP", - "from_symbol": "SCR", - "to_symbol": "GBP" - }, - { - "id": "SCRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SCR/USD", - "from_symbol": "SCR", - "to_symbol": "USD" - }, - { - "id": "SDG=X", - "currency": "SDG", - "exchange": "CCY", - "name": "USD/SDG", - "from_symbol": "USD", - "to_symbol": "SDG" - }, - { - "id": "SDGBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SDG/BRX", - "from_symbol": "SDG", - "to_symbol": "BRX" - }, - { - "id": "SEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "USD/SEK", - "from_symbol": "USD", - "to_symbol": "SEK" - }, - { - "id": "SEKAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "SEK/AED", - "from_symbol": "SEK", - "to_symbol": "AED" - }, - { - "id": "SEKAMD=X", - "currency": "AMD", - "exchange": "CCY", - "name": "SEK/AMD", - "from_symbol": "SEK", - "to_symbol": "AMD" - }, - { - "id": "SEKANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "SEK/ANG", - "from_symbol": "SEK", - "to_symbol": "ANG" - }, - { - "id": "SEKAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "SEK/AUD", - "from_symbol": "SEK", - "to_symbol": "AUD" - }, - { - "id": "SEKBAM=X", - "currency": "BAM", - "exchange": "CCY", - "name": "SEK/BAM", - "from_symbol": "SEK", - "to_symbol": "BAM" - }, - { - "id": "SEKBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "SEK/BRL", - "from_symbol": "SEK", - "to_symbol": "BRL" - }, - { - "id": "SEKBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/BRX", - "from_symbol": "SEK", - "to_symbol": "BRX" - }, - { - "id": "SEKCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "SEK/CAD", - "from_symbol": "SEK", - "to_symbol": "CAD" - }, - { - "id": "SEKCAX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/CAX", - "from_symbol": "SEK", - "to_symbol": "CAX" - }, - { - "id": "SEKCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "SEK/CHF", - "from_symbol": "SEK", - "to_symbol": "CHF" - }, - { - "id": "SEKCNH=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/CNH", - "from_symbol": "SEK", - "to_symbol": "CNH" - }, - { - "id": "SEKCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "SEK/CNY", - "from_symbol": "SEK", - "to_symbol": "CNY" - }, - { - "id": "SEKCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "SEK/CZK", - "from_symbol": "SEK", - "to_symbol": "CZK" - }, - { - "id": "SEKCZX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/CZX", - "from_symbol": "SEK", - "to_symbol": "CZX" - }, - { - "id": "SEKDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "SEK/DKK", - "from_symbol": "SEK", - "to_symbol": "DKK" - }, - { - "id": "SEKDKX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/DKX", - "from_symbol": "SEK", - "to_symbol": "DKX" - }, - { - "id": "SEKEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "SEK/EUR", - "from_symbol": "SEK", - "to_symbol": "EUR" - }, - { - "id": "SEKGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "SEK/GBP", - "from_symbol": "SEK", - "to_symbol": "GBP" - }, - { - "id": "SEKGIP=X", - "currency": "GIP", - "exchange": "CCY", - "name": "SEK/GIP", - "from_symbol": "SEK", - "to_symbol": "GIP" - }, - { - "id": "SEKHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "SEK/HKD", - "from_symbol": "SEK", - "to_symbol": "HKD" - }, - { - "id": "SEKHRX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/HRX", - "from_symbol": "SEK", - "to_symbol": "HRX" - }, - { - "id": "SEKHUX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/HUX", - "from_symbol": "SEK", - "to_symbol": "HUX" - }, - { - "id": "SEKIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "SEK/IDR", - "from_symbol": "SEK", - "to_symbol": "IDR" - }, - { - "id": "SEKILS=X", - "currency": "ILS", - "exchange": "CCY", - "name": "SEK/ILS", - "from_symbol": "SEK", - "to_symbol": "ILS" - }, - { - "id": "SEKINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "SEK/INR", - "from_symbol": "SEK", - "to_symbol": "INR" - }, - { - "id": "SEKISK=X", - "currency": "ISK", - "exchange": "CCY", - "name": "SEK/ISK", - "from_symbol": "SEK", - "to_symbol": "ISK" - }, - { - "id": "SEKISX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/ISX", - "from_symbol": "SEK", - "to_symbol": "ISX" - }, - { - "id": "SEKJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "SEK/JPY", - "from_symbol": "SEK", - "to_symbol": "JPY" - }, - { - "id": "SEKKPW=X", - "currency": "KPW", - "exchange": "CCY", - "name": "SEK/KPW", - "from_symbol": "SEK", - "to_symbol": "KPW" - }, - { - "id": "SEKKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "SEK/KRW", - "from_symbol": "SEK", - "to_symbol": "KRW" - }, - { - "id": "SEKLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "SEK/LKR", - "from_symbol": "SEK", - "to_symbol": "LKR" - }, - { - "id": "SEKNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "SEK/NOK", - "from_symbol": "SEK", - "to_symbol": "NOK" - }, - { - "id": "SEKNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "SEK/NZD", - "from_symbol": "SEK", - "to_symbol": "NZD" - }, - { - "id": "SEKPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "SEK/PKR", - "from_symbol": "SEK", - "to_symbol": "PKR" - }, - { - "id": "SEKPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "SEK/PLN", - "from_symbol": "SEK", - "to_symbol": "PLN" - }, - { - "id": "SEKPLX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/PLX", - "from_symbol": "SEK", - "to_symbol": "PLX" - }, - { - "id": "SEKQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "SEK/QAR", - "from_symbol": "SEK", - "to_symbol": "QAR" - }, - { - "id": "SEKRSD=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/RSD", - "from_symbol": "SEK", - "to_symbol": "RSD" - }, - { - "id": "SEKRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "SEK/RUB", - "from_symbol": "SEK", - "to_symbol": "RUB" - }, - { - "id": "SEKRUX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/RUX", - "from_symbol": "SEK", - "to_symbol": "RUX" - }, - { - "id": "SEKSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "SEK/SAR", - "from_symbol": "SEK", - "to_symbol": "SAR" - }, - { - "id": "SEKSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "SEK/SGD", - "from_symbol": "SEK", - "to_symbol": "SGD" - }, - { - "id": "SEKTHX=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/THX", - "from_symbol": "SEK", - "to_symbol": "THX" - }, - { - "id": "SEKUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SEK/USD", - "from_symbol": "SEK", - "to_symbol": "USD" - }, - { - "id": "SEKXCU=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/XCU", - "from_symbol": "SEK", - "to_symbol": "XCU" - }, - { - "id": "SEKXDR=X", - "currency": null, - "exchange": "CCY", - "name": "SEK/XDR", - "from_symbol": "SEK", - "to_symbol": "XDR" - }, - { - "id": "SEKZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "SEK/ZAR", - "from_symbol": "SEK", - "to_symbol": "ZAR" - }, - { - "id": "SGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "USD/SGD", - "from_symbol": "USD", - "to_symbol": "SGD" - }, - { - "id": "SGDAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "SGD/AED", - "from_symbol": "SGD", - "to_symbol": "AED" - }, - { - "id": "SGDARS=X", - "currency": "ARS", - "exchange": "CCY", - "name": "SGD/ARS", - "from_symbol": "SGD", - "to_symbol": "ARS" - }, - { - "id": "SGDAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "SGD/AUD", - "from_symbol": "SGD", - "to_symbol": "AUD" - }, - { - "id": "SGDBDT=X", - "currency": "BDT", - "exchange": "CCY", - "name": "SGD/BDT", - "from_symbol": "SGD", - "to_symbol": "BDT" - }, - { - "id": "SGDBGN=X", - "currency": "BGN", - "exchange": "CCY", - "name": "SGD/BGN", - "from_symbol": "SGD", - "to_symbol": "BGN" - }, - { - "id": "SGDBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "SGD/BRL", - "from_symbol": "SGD", - "to_symbol": "BRL" - }, - { - "id": "SGDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/BRX", - "from_symbol": "SGD", - "to_symbol": "BRX" - }, - { - "id": "SGDCAX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/CAX", - "from_symbol": "SGD", - "to_symbol": "CAX" - }, - { - "id": "SGDCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "SGD/CHF", - "from_symbol": "SGD", - "to_symbol": "CHF" - }, - { - "id": "SGDCNH=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/CNH", - "from_symbol": "SGD", - "to_symbol": "CNH" - }, - { - "id": "SGDCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "SGD/CNY", - "from_symbol": "SGD", - "to_symbol": "CNY" - }, - { - "id": "SGDCZX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/CZX", - "from_symbol": "SGD", - "to_symbol": "CZX" - }, - { - "id": "SGDDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "SGD/DKK", - "from_symbol": "SGD", - "to_symbol": "DKK" - }, - { - "id": "SGDDKX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/DKX", - "from_symbol": "SGD", - "to_symbol": "DKX" - }, - { - "id": "SGDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "SGD/EUR", - "from_symbol": "SGD", - "to_symbol": "EUR" - }, - { - "id": "SGDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "SGD/GBP", - "from_symbol": "SGD", - "to_symbol": "GBP" - }, - { - "id": "SGDGHS=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/GHS", - "from_symbol": "SGD", - "to_symbol": "GHS" - }, - { - "id": "SGDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "SGD/HKD", - "from_symbol": "SGD", - "to_symbol": "HKD" - }, - { - "id": "SGDHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "SGD/HUF", - "from_symbol": "SGD", - "to_symbol": "HUF" - }, - { - "id": "SGDHUX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/HUX", - "from_symbol": "SGD", - "to_symbol": "HUX" - }, - { - "id": "SGDIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "SGD/IDR", - "from_symbol": "SGD", - "to_symbol": "IDR" - }, - { - "id": "SGDINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "SGD/INR", - "from_symbol": "SGD", - "to_symbol": "INR" - }, - { - "id": "SGDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "SGD/JPY", - "from_symbol": "SGD", - "to_symbol": "JPY" - }, - { - "id": "SGDKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "SGD/KRW", - "from_symbol": "SGD", - "to_symbol": "KRW" - }, - { - "id": "SGDKWD=X", - "currency": "KWD", - "exchange": "CCY", - "name": "SGD/KWD", - "from_symbol": "SGD", - "to_symbol": "KWD" - }, - { - "id": "SGDLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "SGD/LKR", - "from_symbol": "SGD", - "to_symbol": "LKR" - }, - { - "id": "SGDMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "SGD/MXN", - "from_symbol": "SGD", - "to_symbol": "MXN" - }, - { - "id": "SGDMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "SGD/MYR", - "from_symbol": "SGD", - "to_symbol": "MYR" - }, - { - "id": "SGDMYX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/MYX", - "from_symbol": "SGD", - "to_symbol": "MYX" - }, - { - "id": "SGDNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "SGD/NOK", - "from_symbol": "SGD", - "to_symbol": "NOK" - }, - { - "id": "SGDNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "SGD/NZD", - "from_symbol": "SGD", - "to_symbol": "NZD" - }, - { - "id": "SGDOMR=X", - "currency": "OMR", - "exchange": "CCY", - "name": "SGD/OMR", - "from_symbol": "SGD", - "to_symbol": "OMR" - }, - { - "id": "SGDPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "SGD/PHP", - "from_symbol": "SGD", - "to_symbol": "PHP" - }, - { - "id": "SGDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "SGD/PKR", - "from_symbol": "SGD", - "to_symbol": "PKR" - }, - { - "id": "SGDPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "SGD/PLN", - "from_symbol": "SGD", - "to_symbol": "PLN" - }, - { - "id": "SGDPLX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/PLX", - "from_symbol": "SGD", - "to_symbol": "PLX" - }, - { - "id": "SGDQAR=X", - "currency": "QAR", - "exchange": "CCY", - "name": "SGD/QAR", - "from_symbol": "SGD", - "to_symbol": "QAR" - }, - { - "id": "SGDRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "SGD/RUB", - "from_symbol": "SGD", - "to_symbol": "RUB" - }, - { - "id": "SGDRUX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/RUX", - "from_symbol": "SGD", - "to_symbol": "RUX" - }, - { - "id": "SGDSAR=X", - "currency": "SAR", - "exchange": "CCY", - "name": "SGD/SAR", - "from_symbol": "SGD", - "to_symbol": "SAR" - }, - { - "id": "SGDSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "SGD/SEK", - "from_symbol": "SGD", - "to_symbol": "SEK" - }, - { - "id": "SGDTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "SGD/THB", - "from_symbol": "SGD", - "to_symbol": "THB" - }, - { - "id": "SGDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/THX", - "from_symbol": "SGD", - "to_symbol": "THX" - }, - { - "id": "SGDTRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "SGD/TRY", - "from_symbol": "SGD", - "to_symbol": "TRY" - }, - { - "id": "SGDTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "SGD/TWD", - "from_symbol": "SGD", - "to_symbol": "TWD" - }, - { - "id": "SGDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SGD/USD", - "from_symbol": "SGD", - "to_symbol": "USD" - }, - { - "id": "SGDVND=X", - "currency": "VND", - "exchange": "CCY", - "name": "SGD/VND", - "from_symbol": "SGD", - "to_symbol": "VND" - }, - { - "id": "SGDXCU=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/XCU", - "from_symbol": "SGD", - "to_symbol": "XCU" - }, - { - "id": "SGDXDR=X", - "currency": null, - "exchange": "CCY", - "name": "SGD/XDR", - "from_symbol": "SGD", - "to_symbol": "XDR" - }, - { - "id": "SGDZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "SGD/ZAR", - "from_symbol": "SGD", - "to_symbol": "ZAR" - }, - { - "id": "SHP=X", - "currency": "SHP", - "exchange": "CCY", - "name": "USD/SHP", - "from_symbol": "USD", - "to_symbol": "SHP" - }, - { - "id": "SHPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SHP/BRX", - "from_symbol": "SHP", - "to_symbol": "BRX" - }, - { - "id": "SLL=X", - "currency": "SLL", - "exchange": "CCY", - "name": "USD/SLL", - "from_symbol": "USD", - "to_symbol": "SLL" - }, - { - "id": "SLLBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SLL/BRX", - "from_symbol": "SLL", - "to_symbol": "BRX" - }, - { - "id": "SLLUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SLL/USD", - "from_symbol": "SLL", - "to_symbol": "USD" - }, - { - "id": "SOS=X", - "currency": "SOS", - "exchange": "CCY", - "name": "USD/SOS", - "from_symbol": "USD", - "to_symbol": "SOS" - }, - { - "id": "SOSBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SOS/BRX", - "from_symbol": "SOS", - "to_symbol": "BRX" - }, - { - "id": "SOSUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SOS/USD", - "from_symbol": "SOS", - "to_symbol": "USD" - }, - { - "id": "SRD=X", - "currency": "SRD", - "exchange": "CCY", - "name": "USD/SRD", - "from_symbol": "USD", - "to_symbol": "SRD" - }, - { - "id": "SRDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SRD/BRX", - "from_symbol": "SRD", - "to_symbol": "BRX" - }, - { - "id": "SSPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SSP/BRX", - "from_symbol": "SSP", - "to_symbol": "BRX" - }, - { - "id": "STN=X", - "currency": "STN", - "exchange": "CCY", - "name": "USD/STN", - "from_symbol": "USD", - "to_symbol": "STN" - }, - { - "id": "STNUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "STN/USD", - "from_symbol": "STN", - "to_symbol": "USD" - }, - { - "id": "SVC=X", - "currency": "SVC", - "exchange": "CCY", - "name": "USD/SVC", - "from_symbol": "USD", - "to_symbol": "SVC" - }, - { - "id": "SVCBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SVC/BRX", - "from_symbol": "SVC", - "to_symbol": "BRX" - }, - { - "id": "SVCUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SVC/USD", - "from_symbol": "SVC", - "to_symbol": "USD" - }, - { - "id": "SYP=X", - "currency": "SYP", - "exchange": "CCY", - "name": "USD/SYP", - "from_symbol": "USD", - "to_symbol": "SYP" - }, - { - "id": "SYPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SYP/BRX", - "from_symbol": "SYP", - "to_symbol": "BRX" - }, - { - "id": "SYPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SYP/USD", - "from_symbol": "SYP", - "to_symbol": "USD" - }, - { - "id": "SZL=X", - "currency": "SZL", - "exchange": "CCY", - "name": "USD/SZL", - "from_symbol": "USD", - "to_symbol": "SZL" - }, - { - "id": "SZLBRX=X", - "currency": null, - "exchange": "CCY", - "name": "SZL/BRX", - "from_symbol": "SZL", - "to_symbol": "BRX" - }, - { - "id": "SZLGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "SZL/GBP", - "from_symbol": "SZL", - "to_symbol": "GBP" - }, - { - "id": "SZLUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "SZL/USD", - "from_symbol": "SZL", - "to_symbol": "USD" - }, - { - "id": "SZLZAR=X", - "currency": null, - "exchange": "CCY", - "name": "SZL/ZAR", - "from_symbol": "SZL", - "to_symbol": "ZAR" - }, - { - "id": "THB=X", - "currency": "THB", - "exchange": "CCY", - "name": "USD/THB", - "from_symbol": "USD", - "to_symbol": "THB" - }, - { - "id": "THBAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "THB/AUD", - "from_symbol": "THB", - "to_symbol": "AUD" - }, - { - "id": "THBBRX=X", - "currency": null, - "exchange": "CCY", - "name": "THB/BRX", - "from_symbol": "THB", - "to_symbol": "BRX" - }, - { - "id": "THBCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "THB/CAD", - "from_symbol": "THB", - "to_symbol": "CAD" - }, - { - "id": "THBCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "THB/CHF", - "from_symbol": "THB", - "to_symbol": "CHF" - }, - { - "id": "THBCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "THB/CNY", - "from_symbol": "THB", - "to_symbol": "CNY" - }, - { - "id": "THBCZX=X", - "currency": null, - "exchange": "CCY", - "name": "THB/CZX", - "from_symbol": "THB", - "to_symbol": "CZX" - }, - { - "id": "THBDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "THB/DKK", - "from_symbol": "THB", - "to_symbol": "DKK" - }, - { - "id": "THBDKX=X", - "currency": null, - "exchange": "CCY", - "name": "THB/DKX", - "from_symbol": "THB", - "to_symbol": "DKX" - }, - { - "id": "THBEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "THB/EUR", - "from_symbol": "THB", - "to_symbol": "EUR" - }, - { - "id": "THBGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "THB/GBP", - "from_symbol": "THB", - "to_symbol": "GBP" - }, - { - "id": "THBHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "THB/HKD", - "from_symbol": "THB", - "to_symbol": "HKD" - }, - { - "id": "THBHUX=X", - "currency": null, - "exchange": "CCY", - "name": "THB/HUX", - "from_symbol": "THB", - "to_symbol": "HUX" - }, - { - "id": "THBIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "THB/IDR", - "from_symbol": "THB", - "to_symbol": "IDR" - }, - { - "id": "THBINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "THB/INR", - "from_symbol": "THB", - "to_symbol": "INR" - }, - { - "id": "THBJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "THB/JPY", - "from_symbol": "THB", - "to_symbol": "JPY" - }, - { - "id": "THBKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "THB/KRW", - "from_symbol": "THB", - "to_symbol": "KRW" - }, - { - "id": "THBMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "THB/MYR", - "from_symbol": "THB", - "to_symbol": "MYR" - }, - { - "id": "THBMYX=X", - "currency": null, - "exchange": "CCY", - "name": "THB/MYX", - "from_symbol": "THB", - "to_symbol": "MYX" - }, - { - "id": "THBNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "THB/NOK", - "from_symbol": "THB", - "to_symbol": "NOK" - }, - { - "id": "THBNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "THB/NZD", - "from_symbol": "THB", - "to_symbol": "NZD" - }, - { - "id": "THBPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "THB/PHP", - "from_symbol": "THB", - "to_symbol": "PHP" - }, - { - "id": "THBPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "THB/PKR", - "from_symbol": "THB", - "to_symbol": "PKR" - }, - { - "id": "THBPLX=X", - "currency": null, - "exchange": "CCY", - "name": "THB/PLX", - "from_symbol": "THB", - "to_symbol": "PLX" - }, - { - "id": "THBSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "THB/SEK", - "from_symbol": "THB", - "to_symbol": "SEK" - }, - { - "id": "THBSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "THB/SGD", - "from_symbol": "THB", - "to_symbol": "SGD" - }, - { - "id": "THBTND=X", - "currency": "TND", - "exchange": "CCY", - "name": "THB/TND", - "from_symbol": "THB", - "to_symbol": "TND" - }, - { - "id": "THBTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "THB/TWD", - "from_symbol": "THB", - "to_symbol": "TWD" - }, - { - "id": "THBUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "THB/USD", - "from_symbol": "THB", - "to_symbol": "USD" - }, - { - "id": "THBXDR=X", - "currency": null, - "exchange": "CCY", - "name": "THB/XDR", - "from_symbol": "THB", - "to_symbol": "XDR" - }, - { - "id": "THBZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "THB/ZAR", - "from_symbol": "THB", - "to_symbol": "ZAR" - }, - { - "id": "THX=X", - "currency": "THX", - "exchange": "CCY", - "name": "USD/THX", - "from_symbol": "USD", - "to_symbol": "THX" - }, - { - "id": "TJS=X", - "currency": "TJS", - "exchange": "CCY", - "name": "USD/TJS", - "from_symbol": "USD", - "to_symbol": "TJS" - }, - { - "id": "TJSBRX=X", - "currency": null, - "exchange": "CCY", - "name": "TJS/BRX", - "from_symbol": "TJS", - "to_symbol": "BRX" - }, - { - "id": "TJSRUX=X", - "currency": null, - "exchange": "CCY", - "name": "TJS/RUX", - "from_symbol": "TJS", - "to_symbol": "RUX" - }, - { - "id": "TMT=X", - "currency": "TMT", - "exchange": "CCY", - "name": "USD/TMT", - "from_symbol": "USD", - "to_symbol": "TMT" - }, - { - "id": "TMTBRX=X", - "currency": null, - "exchange": "CCY", - "name": "TMT/BRX", - "from_symbol": "TMT", - "to_symbol": "BRX" - }, - { - "id": "TMTRUX=X", - "currency": null, - "exchange": "CCY", - "name": "TMT/RUX", - "from_symbol": "TMT", - "to_symbol": "RUX" - }, - { - "id": "TND=X", - "currency": "TND", - "exchange": "CCY", - "name": "USD/TND", - "from_symbol": "USD", - "to_symbol": "TND" - }, - { - "id": "TNDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "TND/BRX", - "from_symbol": "TND", - "to_symbol": "BRX" - }, - { - "id": "TNDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "TND/EUR", - "from_symbol": "TND", - "to_symbol": "EUR" - }, - { - "id": "TNDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "TND/GBP", - "from_symbol": "TND", - "to_symbol": "GBP" - }, - { - "id": "TNDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "TND/USD", - "from_symbol": "TND", - "to_symbol": "USD" - }, - { - "id": "TOP=X", - "currency": "TOP", - "exchange": "CCY", - "name": "USD/TOP", - "from_symbol": "USD", - "to_symbol": "TOP" - }, - { - "id": "TOPBRX=X", - "currency": null, - "exchange": "CCY", - "name": "TOP/BRX", - "from_symbol": "TOP", - "to_symbol": "BRX" - }, - { - "id": "TOPUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "TOP/USD", - "from_symbol": "TOP", - "to_symbol": "USD" - }, - { - "id": "TRY=X", - "currency": "TRY", - "exchange": "CCY", - "name": "USD/TRY", - "from_symbol": "USD", - "to_symbol": "TRY" - }, - { - "id": "TRYBAM=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/BAM", - "from_symbol": "TRY", - "to_symbol": "BAM" - }, - { - "id": "TRYBRX=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/BRX", - "from_symbol": "TRY", - "to_symbol": "BRX" - }, - { - "id": "TRYCAX=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/CAX", - "from_symbol": "TRY", - "to_symbol": "CAX" - }, - { - "id": "TRYCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "TRY/CHF", - "from_symbol": "TRY", - "to_symbol": "CHF" - }, - { - "id": "TRYCZK=X", - "currency": "CZK", - "exchange": "CCY", - "name": "TRY/CZK", - "from_symbol": "TRY", - "to_symbol": "CZK" - }, - { - "id": "TRYCZX=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/CZX", - "from_symbol": "TRY", - "to_symbol": "CZX" - }, - { - "id": "TRYDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "TRY/DKK", - "from_symbol": "TRY", - "to_symbol": "DKK" - }, - { - "id": "TRYDKX=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/DKX", - "from_symbol": "TRY", - "to_symbol": "DKX" - }, - { - "id": "TRYEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "TRY/EUR", - "from_symbol": "TRY", - "to_symbol": "EUR" - }, - { - "id": "TRYGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "TRY/GBP", - "from_symbol": "TRY", - "to_symbol": "GBP" - }, - { - "id": "TRYHUF=X", - "currency": "HUF", - "exchange": "CCY", - "name": "TRY/HUF", - "from_symbol": "TRY", - "to_symbol": "HUF" - }, - { - "id": "TRYHUX=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/HUX", - "from_symbol": "TRY", - "to_symbol": "HUX" - }, - { - "id": "TRYJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "TRY/JPY", - "from_symbol": "TRY", - "to_symbol": "JPY" - }, - { - "id": "TRYPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "TRY/PLN", - "from_symbol": "TRY", - "to_symbol": "PLN" - }, - { - "id": "TRYPLX=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/PLX", - "from_symbol": "TRY", - "to_symbol": "PLX" - }, - { - "id": "TRYRSD=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/RSD", - "from_symbol": "TRY", - "to_symbol": "RSD" - }, - { - "id": "TRYRUX=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/RUX", - "from_symbol": "TRY", - "to_symbol": "RUX" - }, - { - "id": "TRYSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "TRY/SGD", - "from_symbol": "TRY", - "to_symbol": "SGD" - }, - { - "id": "TRYUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "TRY/USD", - "from_symbol": "TRY", - "to_symbol": "USD" - }, - { - "id": "TRYXCU=X", - "currency": null, - "exchange": "CCY", - "name": "TRY/XCU", - "from_symbol": "TRY", - "to_symbol": "XCU" - }, - { - "id": "TRYZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "TRY/ZAR", - "from_symbol": "TRY", - "to_symbol": "ZAR" - }, - { - "id": "TTD=X", - "currency": "TTD", - "exchange": "CCY", - "name": "USD/TTD", - "from_symbol": "USD", - "to_symbol": "TTD" - }, - { - "id": "TTDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "TTD/BRX", - "from_symbol": "TTD", - "to_symbol": "BRX" - }, - { - "id": "TTDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "TTD/GBP", - "from_symbol": "TTD", - "to_symbol": "GBP" - }, - { - "id": "TTDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "TTD/USD", - "from_symbol": "TTD", - "to_symbol": "USD" - }, - { - "id": "TTDXDR=X", - "currency": null, - "exchange": "CCY", - "name": "TTD/XDR", - "from_symbol": "TTD", - "to_symbol": "XDR" - }, - { - "id": "TWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "USD/TWD", - "from_symbol": "USD", - "to_symbol": "TWD" - }, - { - "id": "TWDAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "TWD/AUD", - "from_symbol": "TWD", - "to_symbol": "AUD" - }, - { - "id": "TWDBRL=X", - "currency": null, - "exchange": "CCY", - "name": "TWD/BRL", - "from_symbol": "TWD", - "to_symbol": "BRL" - }, - { - "id": "TWDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "TWD/BRX", - "from_symbol": "TWD", - "to_symbol": "BRX" - }, - { - "id": "TWDCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "TWD/CAD", - "from_symbol": "TWD", - "to_symbol": "CAD" - }, - { - "id": "TWDCAX=X", - "currency": null, - "exchange": "CCY", - "name": "TWD/CAX", - "from_symbol": "TWD", - "to_symbol": "CAX" - }, - { - "id": "TWDCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "TWD/CHF", - "from_symbol": "TWD", - "to_symbol": "CHF" - }, - { - "id": "TWDCNH=X", - "currency": null, - "exchange": "CCY", - "name": "TWD/CNH", - "from_symbol": "TWD", - "to_symbol": "CNH" - }, - { - "id": "TWDCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "TWD/CNY", - "from_symbol": "TWD", - "to_symbol": "CNY" - }, - { - "id": "TWDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "TWD/EUR", - "from_symbol": "TWD", - "to_symbol": "EUR" - }, - { - "id": "TWDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "TWD/GBP", - "from_symbol": "TWD", - "to_symbol": "GBP" - }, - { - "id": "TWDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "TWD/HKD", - "from_symbol": "TWD", - "to_symbol": "HKD" - }, - { - "id": "TWDIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "TWD/IDR", - "from_symbol": "TWD", - "to_symbol": "IDR" - }, - { - "id": "TWDINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "TWD/INR", - "from_symbol": "TWD", - "to_symbol": "INR" - }, - { - "id": "TWDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "TWD/JPY", - "from_symbol": "TWD", - "to_symbol": "JPY" - }, - { - "id": "TWDKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "TWD/KRW", - "from_symbol": "TWD", - "to_symbol": "KRW" - }, - { - "id": "TWDMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "TWD/MYR", - "from_symbol": "TWD", - "to_symbol": "MYR" - }, - { - "id": "TWDMYX=X", - "currency": null, - "exchange": "CCY", - "name": "TWD/MYX", - "from_symbol": "TWD", - "to_symbol": "MYX" - }, - { - "id": "TWDNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "TWD/NZD", - "from_symbol": "TWD", - "to_symbol": "NZD" - }, - { - "id": "TWDPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "TWD/PHP", - "from_symbol": "TWD", - "to_symbol": "PHP" - }, - { - "id": "TWDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "TWD/PKR", - "from_symbol": "TWD", - "to_symbol": "PKR" - }, - { - "id": "TWDSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "TWD/SEK", - "from_symbol": "TWD", - "to_symbol": "SEK" - }, - { - "id": "TWDSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "TWD/SGD", - "from_symbol": "TWD", - "to_symbol": "SGD" - }, - { - "id": "TWDTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "TWD/THB", - "from_symbol": "TWD", - "to_symbol": "THB" - }, - { - "id": "TWDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "TWD/THX", - "from_symbol": "TWD", - "to_symbol": "THX" - }, - { - "id": "TWDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "TWD/USD", - "from_symbol": "TWD", - "to_symbol": "USD" - }, - { - "id": "TWDZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "TWD/ZAR", - "from_symbol": "TWD", - "to_symbol": "ZAR" - }, - { - "id": "TZS=X", - "currency": "TZS", - "exchange": "CCY", - "name": "USD/TZS", - "from_symbol": "USD", - "to_symbol": "TZS" - }, - { - "id": "TZSBRX=X", - "currency": null, - "exchange": "CCY", - "name": "TZS/BRX", - "from_symbol": "TZS", - "to_symbol": "BRX" - }, - { - "id": "TZSEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "TZS/EUR", - "from_symbol": "TZS", - "to_symbol": "EUR" - }, - { - "id": "TZSGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "TZS/GBP", - "from_symbol": "TZS", - "to_symbol": "GBP" - }, - { - "id": "TZSSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "TZS/SGD", - "from_symbol": "TZS", - "to_symbol": "SGD" - }, - { - "id": "TZSUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "TZS/USD", - "from_symbol": "TZS", - "to_symbol": "USD" - }, - { - "id": "UAH=X", - "currency": "UAH", - "exchange": "CCY", - "name": "USD/UAH", - "from_symbol": "USD", - "to_symbol": "UAH" - }, - { - "id": "UAHBRX=X", - "currency": null, - "exchange": "CCY", - "name": "UAH/BRX", - "from_symbol": "UAH", - "to_symbol": "BRX" - }, - { - "id": "UAHGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "UAH/GBP", - "from_symbol": "UAH", - "to_symbol": "GBP" - }, - { - "id": "UAHHUX=X", - "currency": null, - "exchange": "CCY", - "name": "UAH/HUX", - "from_symbol": "UAH", - "to_symbol": "HUX" - }, - { - "id": "UAHPLX=X", - "currency": null, - "exchange": "CCY", - "name": "UAH/PLX", - "from_symbol": "UAH", - "to_symbol": "PLX" - }, - { - "id": "UAHRUX=X", - "currency": null, - "exchange": "CCY", - "name": "UAH/RUX", - "from_symbol": "UAH", - "to_symbol": "RUX" - }, - { - "id": "UAHUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "UAH/USD", - "from_symbol": "UAH", - "to_symbol": "USD" - }, - { - "id": "UGX=X", - "currency": "UGX", - "exchange": "CCY", - "name": "USD/UGX", - "from_symbol": "USD", - "to_symbol": "UGX" - }, - { - "id": "UGXAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "UGX/AUD", - "from_symbol": "UGX", - "to_symbol": "AUD" - }, - { - "id": "UGXBRX=X", - "currency": null, - "exchange": "CCY", - "name": "UGX/BRX", - "from_symbol": "UGX", - "to_symbol": "BRX" - }, - { - "id": "UGXCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "UGX/CAD", - "from_symbol": "UGX", - "to_symbol": "CAD" - }, - { - "id": "UGXCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "UGX/CNY", - "from_symbol": "UGX", - "to_symbol": "CNY" - }, - { - "id": "UGXEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "UGX/EUR", - "from_symbol": "UGX", - "to_symbol": "EUR" - }, - { - "id": "UGXGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "UGX/GBP", - "from_symbol": "UGX", - "to_symbol": "GBP" - }, - { - "id": "UGXIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "UGX/IDR", - "from_symbol": "UGX", - "to_symbol": "IDR" - }, - { - "id": "UGXINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "UGX/INR", - "from_symbol": "UGX", - "to_symbol": "INR" - }, - { - "id": "UGXJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "UGX/JPY", - "from_symbol": "UGX", - "to_symbol": "JPY" - }, - { - "id": "UGXKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "UGX/KRW", - "from_symbol": "UGX", - "to_symbol": "KRW" - }, - { - "id": "UGXUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "UGX/USD", - "from_symbol": "UGX", - "to_symbol": "USD" - }, - { - "id": "UGXZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "UGX/ZAR", - "from_symbol": "UGX", - "to_symbol": "ZAR" - }, - { - "id": "USD=X", - "currency": "USD", - "exchange": "CCY", - "name": "USD/USD", - "from_symbol": "USD", - "to_symbol": "USD" - }, - { - "id": "USYAUD=X", - "currency": null, - "exchange": "CCY", - "name": "USY/AUD", - "from_symbol": "USY", - "to_symbol": "AUD" - }, - { - "id": "USYCAD=X", - "currency": null, - "exchange": "CCY", - "name": "USY/CAD", - "from_symbol": "USY", - "to_symbol": "CAD" - }, - { - "id": "USYCHF=X", - "currency": null, - "exchange": "CCY", - "name": "USY/CHF", - "from_symbol": "USY", - "to_symbol": "CHF" - }, - { - "id": "USYCNY=X", - "currency": null, - "exchange": "CCY", - "name": "USY/CNY", - "from_symbol": "USY", - "to_symbol": "CNY" - }, - { - "id": "USYDKK=X", - "currency": null, - "exchange": "CCY", - "name": "USY/DKK", - "from_symbol": "USY", - "to_symbol": "DKK" - }, - { - "id": "USYEUR=X", - "currency": null, - "exchange": "CCY", - "name": "USY/EUR", - "from_symbol": "USY", - "to_symbol": "EUR" - }, - { - "id": "USYHKD=X", - "currency": null, - "exchange": "CCY", - "name": "USY/HKD", - "from_symbol": "USY", - "to_symbol": "HKD" - }, - { - "id": "USYJPY=X", - "currency": null, - "exchange": "CCY", - "name": "USY/JPY", - "from_symbol": "USY", - "to_symbol": "JPY" - }, - { - "id": "USYKRW=X", - "currency": null, - "exchange": "CCY", - "name": "USY/KRW", - "from_symbol": "USY", - "to_symbol": "KRW" - }, - { - "id": "USYNOK=X", - "currency": null, - "exchange": "CCY", - "name": "USY/NOK", - "from_symbol": "USY", - "to_symbol": "NOK" - }, - { - "id": "USYSEK=X", - "currency": null, - "exchange": "CCY", - "name": "USY/SEK", - "from_symbol": "USY", - "to_symbol": "SEK" - }, - { - "id": "USYSGD=X", - "currency": null, - "exchange": "CCY", - "name": "USY/SGD", - "from_symbol": "USY", - "to_symbol": "SGD" - }, - { - "id": "USYTRY=X", - "currency": null, - "exchange": "CCY", - "name": "USY/TRY", - "from_symbol": "USY", - "to_symbol": "TRY" - }, - { - "id": "USYZAR=X", - "currency": null, - "exchange": "CCY", - "name": "USY/ZAR", - "from_symbol": "USY", - "to_symbol": "ZAR" - }, - { - "id": "UYU=X", - "currency": "UYU", - "exchange": "CCY", - "name": "USD/UYU", - "from_symbol": "USD", - "to_symbol": "UYU" - }, - { - "id": "UYUBRX=X", - "currency": null, - "exchange": "CCY", - "name": "UYU/BRX", - "from_symbol": "UYU", - "to_symbol": "BRX" - }, - { - "id": "UYUGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "UYU/GBP", - "from_symbol": "UYU", - "to_symbol": "GBP" - }, - { - "id": "UYUUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "UYU/USD", - "from_symbol": "UYU", - "to_symbol": "USD" - }, - { - "id": "UYUXDR=X", - "currency": null, - "exchange": "CCY", - "name": "UYU/XDR", - "from_symbol": "UYU", - "to_symbol": "XDR" - }, - { - "id": "UZS=X", - "currency": "UZS", - "exchange": "CCY", - "name": "USD/UZS", - "from_symbol": "USD", - "to_symbol": "UZS" - }, - { - "id": "UZSBRX=X", - "currency": null, - "exchange": "CCY", - "name": "UZS/BRX", - "from_symbol": "UZS", - "to_symbol": "BRX" - }, - { - "id": "UZSRUX=X", - "currency": null, - "exchange": "CCY", - "name": "UZS/RUX", - "from_symbol": "UZS", - "to_symbol": "RUX" - }, - { - "id": "VES=X", - "currency": "VES", - "exchange": "CCY", - "name": "USD/VES", - "from_symbol": "USD", - "to_symbol": "VES" - }, - { - "id": "VESBRX=X", - "currency": null, - "exchange": "CCY", - "name": "VES/BRX", - "from_symbol": "VES", - "to_symbol": "BRX" - }, - { - "id": "VND=X", - "currency": "VND", - "exchange": "CCY", - "name": "USD/VND", - "from_symbol": "USD", - "to_symbol": "VND" - }, - { - "id": "VNDAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "VND/AED", - "from_symbol": "VND", - "to_symbol": "AED" - }, - { - "id": "VNDAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "VND/AUD", - "from_symbol": "VND", - "to_symbol": "AUD" - }, - { - "id": "VNDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "VND/BRX", - "from_symbol": "VND", - "to_symbol": "BRX" - }, - { - "id": "VNDEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "VND/EUR", - "from_symbol": "VND", - "to_symbol": "EUR" - }, - { - "id": "VNDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "VND/GBP", - "from_symbol": "VND", - "to_symbol": "GBP" - }, - { - "id": "VNDHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "VND/HKD", - "from_symbol": "VND", - "to_symbol": "HKD" - }, - { - "id": "VNDINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "VND/INR", - "from_symbol": "VND", - "to_symbol": "INR" - }, - { - "id": "VNDJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "VND/JPY", - "from_symbol": "VND", - "to_symbol": "JPY" - }, - { - "id": "VNDMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "VND/MYR", - "from_symbol": "VND", - "to_symbol": "MYR" - }, - { - "id": "VNDMYX=X", - "currency": null, - "exchange": "CCY", - "name": "VND/MYX", - "from_symbol": "VND", - "to_symbol": "MYX" - }, - { - "id": "VNDPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "VND/PHP", - "from_symbol": "VND", - "to_symbol": "PHP" - }, - { - "id": "VNDPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "VND/PKR", - "from_symbol": "VND", - "to_symbol": "PKR" - }, - { - "id": "VNDSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "VND/SGD", - "from_symbol": "VND", - "to_symbol": "SGD" - }, - { - "id": "VNDTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "VND/THB", - "from_symbol": "VND", - "to_symbol": "THB" - }, - { - "id": "VNDTHX=X", - "currency": null, - "exchange": "CCY", - "name": "VND/THX", - "from_symbol": "VND", - "to_symbol": "THX" - }, - { - "id": "VNDTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "VND/TWD", - "from_symbol": "VND", - "to_symbol": "TWD" - }, - { - "id": "VNDUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "VND/USD", - "from_symbol": "VND", - "to_symbol": "USD" - }, - { - "id": "VUV=X", - "currency": "VUV", - "exchange": "CCY", - "name": "USD/VUV", - "from_symbol": "USD", - "to_symbol": "VUV" - }, - { - "id": "VUVBRX=X", - "currency": null, - "exchange": "CCY", - "name": "VUV/BRX", - "from_symbol": "VUV", - "to_symbol": "BRX" - }, - { - "id": "VUVUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "VUV/USD", - "from_symbol": "VUV", - "to_symbol": "USD" - }, - { - "id": "WST=X", - "currency": "WST", - "exchange": "CCY", - "name": "USD/WST", - "from_symbol": "USD", - "to_symbol": "WST" - }, - { - "id": "WSTBRX=X", - "currency": null, - "exchange": "CCY", - "name": "WST/BRX", - "from_symbol": "WST", - "to_symbol": "BRX" - }, - { - "id": "WSTUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "WST/USD", - "from_symbol": "WST", - "to_symbol": "USD" - }, - { - "id": "XAF=X", - "currency": "XAF", - "exchange": "CCY", - "name": "USD/XAF", - "from_symbol": "USD", - "to_symbol": "XAF" - }, - { - "id": "XAFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "XAF/BRX", - "from_symbol": "XAF", - "to_symbol": "BRX" - }, - { - "id": "XAFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "XAF/USD", - "from_symbol": "XAF", - "to_symbol": "USD" - }, - { - "id": "XCD=X", - "currency": "XCD", - "exchange": "CCY", - "name": "USD/XCD", - "from_symbol": "USD", - "to_symbol": "XCD" - }, - { - "id": "XCDBRX=X", - "currency": null, - "exchange": "CCY", - "name": "XCD/BRX", - "from_symbol": "XCD", - "to_symbol": "BRX" - }, - { - "id": "XCDGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "XCD/GBP", - "from_symbol": "XCD", - "to_symbol": "GBP" - }, - { - "id": "XCU=X", - "currency": "XCU", - "exchange": "CCY", - "name": "USD/XCU", - "from_symbol": "USD", - "to_symbol": "XCU" - }, - { - "id": "XCUAED=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/AED", - "from_symbol": "XCU", - "to_symbol": "AED" - }, - { - "id": "XCUARS=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/ARS", - "from_symbol": "XCU", - "to_symbol": "ARS" - }, - { - "id": "XCUAUD=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/AUD", - "from_symbol": "XCU", - "to_symbol": "AUD" - }, - { - "id": "XCUBHD=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/BHD", - "from_symbol": "XCU", - "to_symbol": "BHD" - }, - { - "id": "XCUBRL=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/BRL", - "from_symbol": "XCU", - "to_symbol": "BRL" - }, - { - "id": "XCUCAD=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/CAD", - "from_symbol": "XCU", - "to_symbol": "CAD" - }, - { - "id": "XCUCHF=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/CHF", - "from_symbol": "XCU", - "to_symbol": "CHF" - }, - { - "id": "XCUCNY=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/CNY", - "from_symbol": "XCU", - "to_symbol": "CNY" - }, - { - "id": "XCUDKK=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/DKK", - "from_symbol": "XCU", - "to_symbol": "DKK" - }, - { - "id": "XCUEUR=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/EUR", - "from_symbol": "XCU", - "to_symbol": "EUR" - }, - { - "id": "XCUGBP=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/GBP", - "from_symbol": "XCU", - "to_symbol": "GBP" - }, - { - "id": "XCUIDR=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/IDR", - "from_symbol": "XCU", - "to_symbol": "IDR" - }, - { - "id": "XCUILS=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/ILS", - "from_symbol": "XCU", - "to_symbol": "ILS" - }, - { - "id": "XCUINR=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/INR", - "from_symbol": "XCU", - "to_symbol": "INR" - }, - { - "id": "XCUJPY=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/JPY", - "from_symbol": "XCU", - "to_symbol": "JPY" - }, - { - "id": "XCUKRW=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/KRW", - "from_symbol": "XCU", - "to_symbol": "KRW" - }, - { - "id": "XCUKWD=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/KWD", - "from_symbol": "XCU", - "to_symbol": "KWD" - }, - { - "id": "XCUMXN=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/MXN", - "from_symbol": "XCU", - "to_symbol": "MXN" - }, - { - "id": "XCUMYR=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/MYR", - "from_symbol": "XCU", - "to_symbol": "MYR" - }, - { - "id": "XCUNOK=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/NOK", - "from_symbol": "XCU", - "to_symbol": "NOK" - }, - { - "id": "XCUNZD=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/NZD", - "from_symbol": "XCU", - "to_symbol": "NZD" - }, - { - "id": "XCUPLN=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/PLN", - "from_symbol": "XCU", - "to_symbol": "PLN" - }, - { - "id": "XCURUB=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/RUB", - "from_symbol": "XCU", - "to_symbol": "RUB" - }, - { - "id": "XCUSEK=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/SEK", - "from_symbol": "XCU", - "to_symbol": "SEK" - }, - { - "id": "XCUSGD=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/SGD", - "from_symbol": "XCU", - "to_symbol": "SGD" - }, - { - "id": "XCUTRY=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/TRY", - "from_symbol": "XCU", - "to_symbol": "TRY" - }, - { - "id": "XCUUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "XCU/USD", - "from_symbol": "XCU", - "to_symbol": "USD" - }, - { - "id": "XCUXDR=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/XDR", - "from_symbol": "XCU", - "to_symbol": "XDR" - }, - { - "id": "XCUZAR=X", - "currency": null, - "exchange": "CCY", - "name": "XCU/ZAR", - "from_symbol": "XCU", - "to_symbol": "ZAR" - }, - { - "id": "XDR=X", - "currency": "XDR", - "exchange": "CCY", - "name": "USD/XDR", - "from_symbol": "USD", - "to_symbol": "XDR" - }, - { - "id": "XDRBRX=X", - "currency": null, - "exchange": "CCY", - "name": "XDR/BRX", - "from_symbol": "XDR", - "to_symbol": "BRX" - }, - { - "id": "XDRCZX=X", - "currency": null, - "exchange": "CCY", - "name": "XDR/CZX", - "from_symbol": "XDR", - "to_symbol": "CZX" - }, - { - "id": "XDRDKX=X", - "currency": null, - "exchange": "CCY", - "name": "XDR/DKX", - "from_symbol": "XDR", - "to_symbol": "DKX" - }, - { - "id": "XDRISX=X", - "currency": null, - "exchange": "CCY", - "name": "XDR/ISX", - "from_symbol": "XDR", - "to_symbol": "ISX" - }, - { - "id": "XDRPLX=X", - "currency": null, - "exchange": "CCY", - "name": "XDR/PLX", - "from_symbol": "XDR", - "to_symbol": "PLX" - }, - { - "id": "XDRRUX=X", - "currency": null, - "exchange": "CCY", - "name": "XDR/RUX", - "from_symbol": "XDR", - "to_symbol": "RUX" - }, - { - "id": "XDRUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "XDR/USD", - "from_symbol": "XDR", - "to_symbol": "USD" - }, - { - "id": "XDRXCU=X", - "currency": null, - "exchange": "CCY", - "name": "XDR/XCU", - "from_symbol": "XDR", - "to_symbol": "XCU" - }, - { - "id": "XOF=X", - "currency": "XOF", - "exchange": "CCY", - "name": "USD/XOF", - "from_symbol": "USD", - "to_symbol": "XOF" - }, - { - "id": "XOFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "XOF/BRX", - "from_symbol": "XOF", - "to_symbol": "BRX" - }, - { - "id": "XOFEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "XOF/EUR", - "from_symbol": "XOF", - "to_symbol": "EUR" - }, - { - "id": "XOFGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "XOF/GBP", - "from_symbol": "XOF", - "to_symbol": "GBP" - }, - { - "id": "XOFUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "XOF/USD", - "from_symbol": "XOF", - "to_symbol": "USD" - }, - { - "id": "XPF=X", - "currency": "XPF", - "exchange": "CCY", - "name": "USD/XPF", - "from_symbol": "USD", - "to_symbol": "XPF" - }, - { - "id": "XPFBRX=X", - "currency": null, - "exchange": "CCY", - "name": "XPF/BRX", - "from_symbol": "XPF", - "to_symbol": "BRX" - }, - { - "id": "YER=X", - "currency": "YER", - "exchange": "CCY", - "name": "USD/YER", - "from_symbol": "USD", - "to_symbol": "YER" - }, - { - "id": "YERBRX=X", - "currency": null, - "exchange": "CCY", - "name": "YER/BRX", - "from_symbol": "YER", - "to_symbol": "BRX" - }, - { - "id": "ZAC=X", - "currency": "ZAC", - "exchange": "CCY", - "name": "USD/ZAC", - "from_symbol": "USD", - "to_symbol": "ZAC" - }, - { - "id": "ZAR=X", - "currency": "ZAR", - "exchange": "CCY", - "name": "USD/ZAR", - "from_symbol": "USD", - "to_symbol": "ZAR" - }, - { - "id": "ZARAED=X", - "currency": "AED", - "exchange": "CCY", - "name": "ZAR/AED", - "from_symbol": "ZAR", - "to_symbol": "AED" - }, - { - "id": "ZARANG=X", - "currency": "ANG", - "exchange": "CCY", - "name": "ZAR/ANG", - "from_symbol": "ZAR", - "to_symbol": "ANG" - }, - { - "id": "ZARAOA=X", - "currency": "AOA", - "exchange": "CCY", - "name": "ZAR/AOA", - "from_symbol": "ZAR", - "to_symbol": "AOA" - }, - { - "id": "ZARAUD=X", - "currency": "AUD", - "exchange": "CCY", - "name": "ZAR/AUD", - "from_symbol": "ZAR", - "to_symbol": "AUD" - }, - { - "id": "ZARBRL=X", - "currency": "BRL", - "exchange": "CCY", - "name": "ZAR/BRL", - "from_symbol": "ZAR", - "to_symbol": "BRL" - }, - { - "id": "ZARBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/BRX", - "from_symbol": "ZAR", - "to_symbol": "BRX" - }, - { - "id": "ZARBWP=X", - "currency": "BWP", - "exchange": "CCY", - "name": "ZAR/BWP", - "from_symbol": "ZAR", - "to_symbol": "BWP" - }, - { - "id": "ZARCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "ZAR/CAD", - "from_symbol": "ZAR", - "to_symbol": "CAD" - }, - { - "id": "ZARCAX=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/CAX", - "from_symbol": "ZAR", - "to_symbol": "CAX" - }, - { - "id": "ZARCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "ZAR/CHF", - "from_symbol": "ZAR", - "to_symbol": "CHF" - }, - { - "id": "ZARCNY=X", - "currency": "CNY", - "exchange": "CCY", - "name": "ZAR/CNY", - "from_symbol": "ZAR", - "to_symbol": "CNY" - }, - { - "id": "ZARCZX=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/CZX", - "from_symbol": "ZAR", - "to_symbol": "CZX" - }, - { - "id": "ZARDKK=X", - "currency": "DKK", - "exchange": "CCY", - "name": "ZAR/DKK", - "from_symbol": "ZAR", - "to_symbol": "DKK" - }, - { - "id": "ZARDKX=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/DKX", - "from_symbol": "ZAR", - "to_symbol": "DKX" - }, - { - "id": "ZAREGP=X", - "currency": "EGP", - "exchange": "CCY", - "name": "ZAR/EGP", - "from_symbol": "ZAR", - "to_symbol": "EGP" - }, - { - "id": "ZAREUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "ZAR/EUR", - "from_symbol": "ZAR", - "to_symbol": "EUR" - }, - { - "id": "ZARGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "ZAR/GBP", - "from_symbol": "ZAR", - "to_symbol": "GBP" - }, - { - "id": "ZARGHS=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/GHS", - "from_symbol": "ZAR", - "to_symbol": "GHS" - }, - { - "id": "ZARHKD=X", - "currency": "HKD", - "exchange": "CCY", - "name": "ZAR/HKD", - "from_symbol": "ZAR", - "to_symbol": "HKD" - }, - { - "id": "ZARHUX=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/HUX", - "from_symbol": "ZAR", - "to_symbol": "HUX" - }, - { - "id": "ZARIDR=X", - "currency": "IDR", - "exchange": "CCY", - "name": "ZAR/IDR", - "from_symbol": "ZAR", - "to_symbol": "IDR" - }, - { - "id": "ZARINR=X", - "currency": "INR", - "exchange": "CCY", - "name": "ZAR/INR", - "from_symbol": "ZAR", - "to_symbol": "INR" - }, - { - "id": "ZARJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "ZAR/JPY", - "from_symbol": "ZAR", - "to_symbol": "JPY" - }, - { - "id": "ZARKES=X", - "currency": "KES", - "exchange": "CCY", - "name": "ZAR/KES", - "from_symbol": "ZAR", - "to_symbol": "KES" - }, - { - "id": "ZARKRW=X", - "currency": "KRW", - "exchange": "CCY", - "name": "ZAR/KRW", - "from_symbol": "ZAR", - "to_symbol": "KRW" - }, - { - "id": "ZARLKR=X", - "currency": "LKR", - "exchange": "CCY", - "name": "ZAR/LKR", - "from_symbol": "ZAR", - "to_symbol": "LKR" - }, - { - "id": "ZARLSL=X", - "currency": "LSL", - "exchange": "CCY", - "name": "ZAR/LSL", - "from_symbol": "ZAR", - "to_symbol": "LSL" - }, - { - "id": "ZARMWK=X", - "currency": "MWK", - "exchange": "CCY", - "name": "ZAR/MWK", - "from_symbol": "ZAR", - "to_symbol": "MWK" - }, - { - "id": "ZARMXN=X", - "currency": "MXN", - "exchange": "CCY", - "name": "ZAR/MXN", - "from_symbol": "ZAR", - "to_symbol": "MXN" - }, - { - "id": "ZARMYR=X", - "currency": "MYR", - "exchange": "CCY", - "name": "ZAR/MYR", - "from_symbol": "ZAR", - "to_symbol": "MYR" - }, - { - "id": "ZARNOK=X", - "currency": "NOK", - "exchange": "CCY", - "name": "ZAR/NOK", - "from_symbol": "ZAR", - "to_symbol": "NOK" - }, - { - "id": "ZARNZD=X", - "currency": "NZD", - "exchange": "CCY", - "name": "ZAR/NZD", - "from_symbol": "ZAR", - "to_symbol": "NZD" - }, - { - "id": "ZARPHP=X", - "currency": "PHP", - "exchange": "CCY", - "name": "ZAR/PHP", - "from_symbol": "ZAR", - "to_symbol": "PHP" - }, - { - "id": "ZARPKR=X", - "currency": "PKR", - "exchange": "CCY", - "name": "ZAR/PKR", - "from_symbol": "ZAR", - "to_symbol": "PKR" - }, - { - "id": "ZARPLN=X", - "currency": "PLN", - "exchange": "CCY", - "name": "ZAR/PLN", - "from_symbol": "ZAR", - "to_symbol": "PLN" - }, - { - "id": "ZARPLX=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/PLX", - "from_symbol": "ZAR", - "to_symbol": "PLX" - }, - { - "id": "ZARRUB=X", - "currency": "RUB", - "exchange": "CCY", - "name": "ZAR/RUB", - "from_symbol": "ZAR", - "to_symbol": "RUB" - }, - { - "id": "ZARRUX=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/RUX", - "from_symbol": "ZAR", - "to_symbol": "RUX" - }, - { - "id": "ZARSCR=X", - "currency": "SCR", - "exchange": "CCY", - "name": "ZAR/SCR", - "from_symbol": "ZAR", - "to_symbol": "SCR" - }, - { - "id": "ZARSEK=X", - "currency": "SEK", - "exchange": "CCY", - "name": "ZAR/SEK", - "from_symbol": "ZAR", - "to_symbol": "SEK" - }, - { - "id": "ZARSGD=X", - "currency": "SGD", - "exchange": "CCY", - "name": "ZAR/SGD", - "from_symbol": "ZAR", - "to_symbol": "SGD" - }, - { - "id": "ZARSZL=X", - "currency": "SZL", - "exchange": "CCY", - "name": "ZAR/SZL", - "from_symbol": "ZAR", - "to_symbol": "SZL" - }, - { - "id": "ZARTHB=X", - "currency": "THB", - "exchange": "CCY", - "name": "ZAR/THB", - "from_symbol": "ZAR", - "to_symbol": "THB" - }, - { - "id": "ZARTHX=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/THX", - "from_symbol": "ZAR", - "to_symbol": "THX" - }, - { - "id": "ZARTWD=X", - "currency": "TWD", - "exchange": "CCY", - "name": "ZAR/TWD", - "from_symbol": "ZAR", - "to_symbol": "TWD" - }, - { - "id": "ZARUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ZAR/USD", - "from_symbol": "ZAR", - "to_symbol": "USD" - }, - { - "id": "ZARXDR=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/XDR", - "from_symbol": "ZAR", - "to_symbol": "XDR" - }, - { - "id": "ZARZMW=X", - "currency": null, - "exchange": "CCY", - "name": "ZAR/ZMW", - "from_symbol": "ZAR", - "to_symbol": "ZMW" - }, - { - "id": "ZMW=X", - "currency": "ZMW", - "exchange": "CCY", - "name": "USD/ZMW", - "from_symbol": "USD", - "to_symbol": "ZMW" - }, - { - "id": "ZMWBRX=X", - "currency": null, - "exchange": "CCY", - "name": "ZMW/BRX", - "from_symbol": "ZMW", - "to_symbol": "BRX" - }, - { - "id": "ZMWCAD=X", - "currency": "CAD", - "exchange": "CCY", - "name": "ZMW/CAD", - "from_symbol": "ZMW", - "to_symbol": "CAD" - }, - { - "id": "ZMWCHF=X", - "currency": "CHF", - "exchange": "CCY", - "name": "ZMW/CHF", - "from_symbol": "ZMW", - "to_symbol": "CHF" - }, - { - "id": "ZMWEUR=X", - "currency": "EUR", - "exchange": "CCY", - "name": "ZMW/EUR", - "from_symbol": "ZMW", - "to_symbol": "EUR" - }, - { - "id": "ZMWGBP=X", - "currency": "GBP", - "exchange": "CCY", - "name": "ZMW/GBP", - "from_symbol": "ZMW", - "to_symbol": "GBP" - }, - { - "id": "ZMWJPY=X", - "currency": "JPY", - "exchange": "CCY", - "name": "ZMW/JPY", - "from_symbol": "ZMW", - "to_symbol": "JPY" - }, - { - "id": "ZMWUSD=X", - "currency": "USD", - "exchange": "CCY", - "name": "ZMW/USD", - "from_symbol": "ZMW", - "to_symbol": "USD" - }, - { - "id": "ZMWZAR=X", - "currency": null, - "exchange": "CCY", - "name": "ZMW/ZAR", - "from_symbol": "ZMW", - "to_symbol": "ZAR" - } - ] diff --git a/openbb_terminal/forex/forex_controller.py b/openbb_terminal/forex/forex_controller.py deleted file mode 100644 index 69002b4a01c..00000000000 --- a/openbb_terminal/forex/forex_controller.py +++ /dev/null @@ -1,453 +0,0 @@ -"""Forex Controller.""" - -__docformat__ = "numpy" - -import argparse -import logging -import os -from datetime import datetime, timedelta -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.common.quantitative_analysis import qa_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forex import av_view, forex_helper, fxempire_view -from openbb_terminal.forex.forex_helper import ( - FOREX_SOURCES, - SOURCES_INTERVALS, - parse_forex_symbol, -) -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_RAW_DATA_ALLOWED, - export_data, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console, get_ordered_list_sources -from openbb_terminal.stocks import stocks_helper - -# pylint: disable=R1710,import-outside-toplevel - -logger = logging.getLogger(__name__) - - -forex_data_path = os.path.join( - os.path.dirname(__file__), os.path.join("data", "polygon_tickers.csv") -) -tickers = pd.read_csv(forex_data_path).iloc[:, 0].to_list() -FX_TICKERS = list(set(tickers + [t[-3:] + t[:3] for t in tickers if len(t) == 6])) - - -class ForexController(BaseController): - """Forex Controller class.""" - - CHOICES_COMMANDS = [ - "fwd", - "candle", - "load", - "quote", - ] - CHOICES_MENUS = [ - "forecast", - "qa", - "ta", - ] - RESOLUTION = ["i", "d", "w", "m"] - - PATH = "/forex/" - FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md") - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Construct Data.""" - super().__init__(queue) - - self.fx_pair = "" - self.from_symbol = "" - self.to_symbol = "" - self.source = get_ordered_list_sources(f"{self.PATH}load")[0] - self.data = pd.DataFrame() - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - choices["load"].update({c: {} for c in FX_TICKERS}) - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help.""" - mt = MenuText("forex/", 80) - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_ticker", self.fx_pair) - mt.add_param("_source", FOREX_SOURCES[self.source]) - mt.add_raw("\n") - mt.add_cmd("quote", self.fx_pair) - mt.add_cmd("candle", self.fx_pair) - mt.add_cmd("fwd", self.fx_pair) - mt.add_raw("\n") - mt.add_menu("ta", self.fx_pair) - mt.add_menu("qa", self.fx_pair) - mt.add_menu("forecast") - console.print(text=mt.menu_text, menu="Forex") - - def custom_reset(self): - """Class specific component of reset command""" - set_fx_pair = f"load {self.fx_pair}" if self.fx_pair else "" - if set_fx_pair: - return ["forex", set_fx_pair] - return [] - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - """Process select command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Load historical exchange rate data." - "Available data sources are Alpha Advantage and YahooFinance" - "By default main source used for analysis is YahooFinance (yf). To change it use --source av", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Currency pair to load.", - type=parse_forex_symbol, - metavar="TICKER", - choices=FX_TICKERS, - ) - parser.add_argument( - "-r", - "--resolution", - choices=self.RESOLUTION, - default="d", - help="[Alphavantage only] Resolution of data. Can be intraday, daily, weekly or monthly", - dest="resolution", - ) - parser.add_argument( - "-i", - "--interval", - choices=SOURCES_INTERVALS["YahooFinance"], - default="1day", - help="""Interval of intraday data. Options: - [YahooFinance] 1min, 2min, 5min, 15min, 30min, 60min, 90min, 1hour, 1day, 5day, 1week, 1month, 3month. - [AlphaVantage] 1min, 5min, 15min, 30min, 60min""", - dest="interval", - ) - parser.add_argument( - "-s", - "--start", - default=(datetime.now() - timedelta(days=365)), - type=valid_date, - help="The starting date (format YYYY-MM-DD) of the forex pair", - dest="start_date", - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=datetime.now().strftime("%Y-%m-%d"), - dest="end", - help="The ending date (format YYYY-MM-DD) of the forex pair", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.ticker not in FX_TICKERS: - logger.error("Invalid forex pair") - console.print(f"{ns_parser.ticker} not a valid forex pair.\n") - return - - self.fx_pair = ns_parser.ticker - self.from_symbol = ns_parser.ticker[:3] - self.to_symbol = ns_parser.ticker[3:] - - if self.to_symbol and self.from_symbol: - self.data = forex_helper.load( - to_symbol=self.to_symbol, - from_symbol=self.from_symbol, - resolution=ns_parser.resolution, - interval=ns_parser.interval, - start_date=ns_parser.start_date.strftime("%Y-%m-%d"), - end_date=ns_parser.end.strftime("%Y-%m-%d"), - source=ns_parser.source, - ) - - if self.data.empty: - console.print( - "\n[red]No historical data loaded.\n\n" - f"Make sure you have appropriate access for the '{ns_parser.source}' data source " - f"and that '{ns_parser.source}' supports the requested range.[/red]" - ) - else: - self.data.index.name = "date" - console.print(f"{self.from_symbol}-{self.to_symbol} loaded.") - - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - "load", - self.data.copy(), - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None, - ) - self.source = ns_parser.source - else: - console.print("\n[red]Make sure to load.[/red]\n") - - @log_start_end(log=logger) - def call_candle(self, other_args: List[str]): - """Process candle command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="candle", - description="Show candle for loaded fx data", - ) - parser.add_argument( - "--sort", - choices=forex_helper.CANDLE_SORT, - default="", - type=str.lower, - dest="sort", - help="Choose a column to sort by. Only works when raw data is displayed.", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "--raw", - action="store_true", - dest="raw", - default=False, - help="Shows raw data instead of chart.", - ) - parser.add_argument( - "-t", - "--trend", - action="store_true", - default=False, - help="Flag to add high and low trends to candle", - dest="trendlines", - ) - parser.add_argument( - "--ma", - dest="mov_avg", - type=str, - help=( - "Add moving average in number of days to plot and separate by a comma. " - "Value for ma (moving average) keyword needs to be greater than 1." - ), - default=None, - ) - parser.add_argument( - "--log", - help="Plot with y axis on log scale", - action="store_true", - default=False, - dest="logy", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=20, - ) - - if ns_parser: - if not self.to_symbol: - console.print("No ticker loaded. First use 'load '") - return - - data = stocks_helper.process_candle(self.data) - if ns_parser.raw: - if ( - ns_parser.trendlines - and (data.index[1] - data.index[0]).total_seconds() >= 86400 - ): - data = stocks_helper.find_trendline(data, "OC_High", "high") - data = stocks_helper.find_trendline(data, "OC_Low", "low") - - qa_view.display_raw( - data=data, - sortby=ns_parser.sort, - ascend=ns_parser.reverse, - limit=ns_parser.limit, - ) - - else: - mov_avgs = [] - - if ns_parser.mov_avg: - mov_list = (num for num in ns_parser.mov_avg.split(",")) - - for num in mov_list: - try: - clean_num = int(num) - - if clean_num <= 1: - raise ValueError - - mov_avgs.append(clean_num) - except ValueError: - console.print( - f"[red]{num} is not a valid moving average, must be an integer greater than 1." - ) - - forex_helper.display_candle( - to_symbol=self.to_symbol, - from_symbol=self.from_symbol, - data=data, - add_trend=ns_parser.trendlines, - ma=mov_avgs, - yscale="log" if ns_parser.logy else "linear", - ) - - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - f"{self.fx_pair}", - self.data, - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None, - ) - - @log_start_end(log=logger) - def call_quote(self, other_args: List[str]): - """Process quote command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="quote", - description="Get current exchange rate quote", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if ns_parser.source == "YahooFinance": - if self.to_symbol and self.from_symbol: - self.data = forex_helper.load( - to_symbol=self.to_symbol, - from_symbol=self.from_symbol, - resolution="i", - interval="1min", - start_date=(datetime.now() - timedelta(days=5)).strftime( - "%Y-%m-%d" - ), - source="YahooFinance", - ) - console.print(f"\nQuote for {self.from_symbol}/{self.to_symbol}\n") - console.print( - f"Last refreshed : {self.data.index[-1].strftime('%Y-%m-%d %H:%M:%S')}" - ) - console.print(f"Last value : {self.data['Adj Close'][-1]}\n") - else: - logger.error("No forex pair loaded.") - console.print("[red]Make sure a forex pair is loaded.[/red]\n") - - elif ns_parser.source == "AlphaVantage": - if self.to_symbol and self.from_symbol: - av_view.display_quote(self.to_symbol, self.from_symbol) - else: - logger.error("No forex pair loaded.") - console.print("[red]Make sure a forex pair is loaded.[/red]\n") - - @log_start_end(log=logger) - def call_fwd(self, other_args: List[str]): - """Process fwd command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fwd", - description="Get forward rates for loaded pair.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.fx_pair: - fxempire_view.display_forward_rates( - self.to_symbol, - self.from_symbol, - ns_parser.export, - ns_parser.sheet_name, - ) - else: - logger.error("Make sure ba currency pair is loaded.") - console.print("[red]Make sure a currency pair is loaded.[/red]\n") - - # MENUS - - @log_start_end(log=logger) - def call_ta(self, _): - """Process ta command""" - from openbb_terminal.forex.technical_analysis.ta_controller import ( - TechnicalAnalysisController, - ) - - # TODO: Play with this to get correct usage - if self.to_symbol and self.from_symbol and not self.data.empty: - self.queue = self.load_class( - TechnicalAnalysisController, - ticker=f"{self.from_symbol}/{self.to_symbol}", - source=self.source, - data=self.data, - start=self.data.index[0], - interval="", - queue=self.queue, - ) - - else: - console.print("No currency pair data is loaded. Use 'load' to load data.\n") - - @log_start_end(log=logger) - def call_qa(self, _): - """Process qa command""" - if self.from_symbol and self.to_symbol: - if self.data.empty: - console.print( - "No currency pair data is loaded. Use 'load' to load data.\n" - ) - else: - from openbb_terminal.forex.quantitative_analysis import qa_controller - - self.queue = self.load_class( - qa_controller.QaController, - self.from_symbol, - self.to_symbol, - self.data, - self.queue, - ) - else: - console.print("No pair selected.\n") - - @log_start_end(log=logger) - def call_forecast(self, _): - """Process forecast command""" - from openbb_terminal.forecast import forecast_controller - - self.queue = self.load_class( - forecast_controller.ForecastController, - self.fx_pair, - self.data, - self.queue, - ) diff --git a/openbb_terminal/forex/forex_helper.py b/openbb_terminal/forex/forex_helper.py deleted file mode 100644 index 9c96030b4eb..00000000000 --- a/openbb_terminal/forex/forex_helper.py +++ /dev/null @@ -1,302 +0,0 @@ -"""Forex helper.""" - -import argparse -import logging -import os -import re -from datetime import datetime, timedelta -from typing import Dict, Iterable, List, Optional - -import pandas as pd -import yfinance as yf - -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forex import av_model, polygon_model -from openbb_terminal.rich_config import console -from openbb_terminal.stocks import stocks_helper - -CANDLE_SORT = [ - "adjclose", - "open", - "close", - "high", - "low", - "volume", - "logret", -] - -FOREX_SOURCES: Dict = { - "YahooFinance": "YahooFinance", - "AlphaVantage": "AlphaAdvantage", - "Polygon": "Polygon", - "Oanda": "Oanda", -} - -SOURCES_INTERVALS: Dict = { - "YahooFinance": [ - "1min", - "5min", - "15min", - "30min", - "60min", - "90min", - "1hour", - "1day", - "5day", - "1week", - "1month", - "3month", - ], - "AlphaVantage": ["1min", "5min", "15min", "30min", "60min"], -} - -INTERVAL_MAPS: Dict = { - "YahooFinance": { - "1min": "1m", - "2min": "2m", - "5min": "5m", - "15min": "15m", - "30min": "30m", - "60min": "60m", - "90min": "90m", - "1hour": "60m", - "1day": "1d", - "5day": "5d", - "1week": "1wk", - "1month": "1mo", - "3month": "3mo", - }, - "AlphaVantage": { - "1min": 1, - "5min": 5, - "15min": 15, - "30min": 30, - "60min": 60, - "1day": 1, - }, -} - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def load( - to_symbol: str, - from_symbol: str, - resolution: str = "d", - interval: str = "1day", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - source: str = "YahooFinance", - verbose: bool = False, -) -> pd.DataFrame: - """Load forex for two given symbols. - - Parameters - ---------- - to_symbol : str - The from currency symbol. Ex: USD, EUR, GBP, YEN - from_symbol : str - The from currency symbol. Ex: USD, EUR, GBP, YEN - resolution : str, optional - The resolution for the data, by default "d" - interval : str, optional - What interval to get data for, by default "1day" - start_date : Optional[str], optional - When to begin loading in data, by default last_year.strftime("%Y-%m-%d") - end_date : Optional[str], optional - When to end loading in data, by default None - source : str, optional - Where to get data from, by default "YahooFinance" - verbose : bool, optional - Display verbose information on what was the pair that was loaded, by default True - - Returns - ------- - pd.DataFrame - The loaded data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.forex.load(from_symbol="EUR", to_symbol="USD", start_date="2020-11-30", end_date="2022-12-01") - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") - if end_date is None: - end_date = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d") - else: - end_date = ( - datetime.strptime(end_date, "%Y-%m-%d") + timedelta(days=1) - ).strftime("%Y-%m-%d") - - if source in ["YahooFinance", "AlphaVantage"]: - interval_map = INTERVAL_MAPS[source] - - if interval not in interval_map and resolution != "d": - if verbose: - console.print( - f"Interval not supported by {FOREX_SOURCES[source]}." - " Need to be one of the following options", - list(interval_map.keys()), - ) - return pd.DataFrame() - - # Check interval in multiple ways - if interval in interval_map: - clean_interval = interval_map[interval] - elif interval in interval_map.values(): - clean_interval = interval - else: - console.print(f"[red]'{interval}' is an invalid interval[/red]\n") - return pd.DataFrame() - - if source == "AlphaVantage": - if "min" in interval: - resolution = "i" - df = av_model.get_historical( - to_symbol=to_symbol, - from_symbol=from_symbol, - resolution=resolution, - interval=clean_interval, - start_date=start_date, - end_date=end_date, - ) - df.index.name = "date" - df.name = f"{from_symbol}/{to_symbol}" - return df - - if source == "YahooFinance": - df = yf.download( - f"{from_symbol}{to_symbol}=X", - start=datetime.strptime(start_date, "%Y-%m-%d"), - end=datetime.strptime(end_date, "%Y-%m-%d"), - interval=clean_interval, - progress=verbose, - ) - df.index = pd.to_datetime(df.index).tz_localize(None) - df.index.name = "date" - df.name = f"{from_symbol}/{to_symbol}" - return df - - if source == "Polygon": - # Interval for polygon gets broken into multiplier and timeframe - temp = re.split(r"(\d+)", interval) - multiplier = int(temp[1]) - timeframe = temp[2] - if timeframe == "min": - timeframe = "minute" - df = polygon_model.get_historical( - f"{from_symbol}{to_symbol}", - multiplier=multiplier, - timespan=timeframe, - start_date=start_date, - end_date=end_date, - ) - df.index.name = "date" - df.name = f"{from_symbol}/{to_symbol}" - return df - - console.print(f"Source {source} not supported") - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_yf_currency_list() -> List: - """Load YF list of forex pair a local file.""" - path = os.path.join(os.path.dirname(__file__), "data/yahoofinance_forex.json") - - return sorted(list(set(pd.read_json(path)["from_symbol"]))) - - -YF_CURRENCY_LIST = get_yf_currency_list() - - -@log_start_end(log=logger) -def check_valid_yf_forex_currency(fx_symbol: str) -> str: - """Check if given symbol is supported on Yahoo Finance. - - Parameters - ---------- - fx_symbol : str - Symbol to check - - Returns - ------- - str - Currency symbol - - Raises - ------ - argparse.ArgumentTypeError - Symbol not valid on YahooFinance - """ - if fx_symbol.upper() in get_yf_currency_list(): - return fx_symbol.upper() - - raise argparse.ArgumentTypeError( - f"{fx_symbol.upper()} not found in YahooFinance supported currency codes. " - ) - - -@log_start_end(log=logger) -def display_candle( - data: pd.DataFrame, - to_symbol: str = "", - from_symbol: str = "", - ma: Optional[Iterable[int]] = None, - external_axes: bool = False, - add_trend: bool = False, - yscale: str = "linear", -): - """Show candle plot for fx data. - - Parameters - ---------- - data : pd.DataFrame - Loaded fx historical data - to_symbol : str - To forex symbol - from_symbol : str - From forex symbol - ma : Optional[Iterable[int]] - Moving averages - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - # We check if there's Volume data to avoid errors and empty subplots - if not (has_volume := False) and "Volume" in data.columns: - has_volume = bool(data["Volume"].sum() > 0) - - if add_trend and (data.index[1] - data.index[0]).total_seconds() >= 86400: - if "date_id" not in data.columns: - data = stocks_helper.process_candle(data) - data = stocks_helper.find_trendline(data, "OC_High", "high") - data = stocks_helper.find_trendline(data, "OC_Low", "low") - - indicators = {} - if ma: - indicators = dict(rma=dict(length=ma)) - - data.name = f"{from_symbol}/{to_symbol}" - fig = PlotlyTA.plot(data, indicators, volume=has_volume) - if add_trend: - fig.add_trend(data, secondary_y=False) - - fig.update_yaxes(type=yscale, row=1, col=1, nticks=20, secondary_y=False) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def parse_forex_symbol(input_symbol): - """Parse potential forex symbols.""" - for potential_split in ["-", "/"]: - if potential_split in input_symbol: - symbol = input_symbol.replace(potential_split, "") - return symbol - if len(input_symbol) != 6: - raise argparse.ArgumentTypeError("Input symbol should be 6 characters.\n ") - return input_symbol.upper() diff --git a/openbb_terminal/forex/fxempire_model.py b/openbb_terminal/forex/fxempire_model.py deleted file mode 100644 index 4ace5afb0cf..00000000000 --- a/openbb_terminal/forex/fxempire_model.py +++ /dev/null @@ -1,39 +0,0 @@ -"""FXEmpire Model""" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_forward_rates(to_symbol: str = "USD", from_symbol: str = "EUR") -> pd.DataFrame: - """Gets forward rates from fxempire - - Parameters - ---------- - to_symbol: str - To currency - from_symbol: str - From currency - - Returns - ------- - df: pd.DataFrame - Dataframe containing forward rates - - """ - r = request( - f"https://www.fxempire.com/currencies/{from_symbol}-{to_symbol}/forward-rates", - headers={"User-Agent": get_user_agent()}, - ) - if r.status_code == 200: - forwards = pd.read_html(r.text)[0].set_index("Expiration") - return forwards - - logger.info("Currency not found.") - return pd.DataFrame() diff --git a/openbb_terminal/forex/fxempire_view.py b/openbb_terminal/forex/fxempire_view.py deleted file mode 100644 index 9fe556565d0..00000000000 --- a/openbb_terminal/forex/fxempire_view.py +++ /dev/null @@ -1,58 +0,0 @@ -"""FXEmpire view""" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forex import fxempire_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_forward_rates( - to_symbol: str = "USD", - from_symbol: str = "EUR", - export: str = "", - sheet_name: Optional[str] = None, -): - """Display forward rates for currency pairs - - Parameters - ---------- - to_symbol: str - To currency - from_symbol: str - From currency - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - forward_rates = fxempire_model.get_forward_rates(to_symbol, from_symbol) - if forward_rates.empty: - console.print( - f"[red]Forward rates not found for {to_symbol}/{from_symbol}.[/red]\n" - ) - return - - print_rich_table( - forward_rates, - index_name="Expirations", - show_index=True, - headers=forward_rates.columns, - title=f"Forward rates for {from_symbol}/{to_symbol}", - floatfmt=".4f", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fwd", - forward_rates, - sheet_name, - ) diff --git a/openbb_terminal/forex/polygon_model.py b/openbb_terminal/forex/polygon_model.py deleted file mode 100644 index 5732ec3d844..00000000000 --- a/openbb_terminal/forex/polygon_model.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Polygon.io model""" - -__docformat__ = "numpy" - -from datetime import datetime -from typing import Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key -from openbb_terminal.helper_funcs import get_user_agent, get_user_timezone, request -from openbb_terminal.rich_config import console - -# pylint: disable=unsupported-assignment-operation - - -@check_api_key(["API_POLYGON_KEY"]) -def get_historical( - fx_pair: str, - multiplier: int = 1, - timespan: str = "day", - start_date: str = "2000-01-01", - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Load historical fx data from polygon - - Parameters - ---------- - fx_pair: str - Forex pair to download - multiplier: int - Multiplier for timespan. 5 with minute timespan indicated 5min windows - timespan: str - Window to aggregate data. - start_date : str - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - Dataframe of historical forex prices - """ - - if end_date is None: - end_date = datetime.now().date().strftime("%Y-%m-%d") - - request_url = ( - f"https://api.polygon.io/v2/aggs/ticker/C:{fx_pair}/range" - f"/{multiplier}/{timespan}/{start_date}/{end_date}?adjusted=true&sort=desc&" - f"limit=50000&apiKey={get_current_user().credentials.API_POLYGON_KEY}" - ) - json_response = request( - request_url, headers={"User-Agent": get_user_agent()} - ).json() - - if json_response["status"] == "ERROR": - console.print(f"[red]{json_response['error']}[/red]\n") - return pd.DataFrame() - - if "results" not in json_response: - console.print("[red]Error in polygon request[/red]\n") - return pd.DataFrame() - - historical = pd.DataFrame(json_response["results"]).rename( - columns={ - "o": "Open", - "c": "Close", - "h": "High", - "l": "Low", - "t": "date", - "v": "Volume", - "n": "Transactions", - } - ) - historical["date"] = pd.to_datetime(historical.date, unit="ms") - historical = historical.sort_values(by="date", ascending=True) - historical = historical.set_index("date") - historical.index = ( - historical.index.tz_localize(tz="UTC") - .tz_convert(get_user_timezone()) - .tz_localize(None) - ) - return historical diff --git a/openbb_terminal/forex/quantitative_analysis/__init__.py b/openbb_terminal/forex/quantitative_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/forex/quantitative_analysis/qa_controller.py b/openbb_terminal/forex/quantitative_analysis/qa_controller.py deleted file mode 100644 index d2a56f852a4..00000000000 --- a/openbb_terminal/forex/quantitative_analysis/qa_controller.py +++ /dev/null @@ -1,785 +0,0 @@ -"""Quantitative Analysis Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -from typing import List, Optional - -import numpy as np -import pandas as pd - -from openbb_terminal.common.quantitative_analysis import qa_view, rolling_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_list_dates, - check_positive, - check_proportion_range, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import CryptoBaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class QaController(CryptoBaseController): - """Quantitative Analysis Controller class""" - - CHOICES_COMMANDS = [ - "pick", - "raw", - "summary", - "line", - "hist", - "cdf", - "bw", - "rolling", - "decompose", - "cusum", - "acf", - "spread", - "quantile", - "skew", - "kurtosis", - "normality", - "qqplot", - "unitroot", - "unitroot", - ] - FULLER_REG = ["c", "ct", "ctt", "nc"] - KPS_REG = ["c", "ct"] - - PATH = "/forex/qa/" - CHOICES_GENERATION = True - - def __init__( - self, - from_symbol: str, - to_symbol: str, - data: pd.DataFrame, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - data["Returns"] = data["Close"].pct_change() - data["LogRet"] = np.log(data["Close"]) - np.log(data["Close"].shift(1)) - data = data.dropna() - - self.data = data - self.from_symbol = from_symbol - self.to_symbol = to_symbol - self.ticker = f"{from_symbol}/{to_symbol}" - self.target = "Close" - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - choices["pick"].update({c: {} for c in list(data.columns)}) - choices["load"] = { - "--interval": { - c: {} - for c in [ - "1", - "5", - "15", - "30", - "60", - "240", - "1440", - "10080", - "43200", - ] - }, - "-i": "--interval", - "--exchange": {c: {} for c in self.exchanges}, - "--source": {c: {} for c in ["CCXT", "YahooFinance", "CoingGecko"]}, - "--vs": {c: {} for c in ["usd", "eur"]}, - "--start": None, - "-s": "--start", - "--end": None, - "-e": "--end", - } - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("forex/qa/") - mt.add_cmd("pick") - mt.add_raw("\n") - mt.add_param("_pair", self.ticker) - mt.add_param("_target", self.target) - mt.add_raw("\n") - mt.add_info("_statistics_") - mt.add_cmd("summary") - mt.add_cmd("normality") - mt.add_cmd("unitroot") - mt.add_info("_plots_") - mt.add_cmd("line") - mt.add_cmd("hist") - mt.add_cmd("cdf") - mt.add_cmd("bw") - mt.add_cmd("acf") - mt.add_cmd("qqplot") - mt.add_info("_rolling_metrics_") - mt.add_cmd("rolling") - mt.add_cmd("spread") - mt.add_cmd("quantile") - mt.add_cmd("skew") - mt.add_cmd("kurtosis") - mt.add_info("_other_") - mt.add_cmd("raw") - mt.add_cmd("decompose") - mt.add_cmd("cusum") - console.print(text=mt.menu_text, menu="Forex - Quantitative Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - return [ - "forex", - f"from {self.from_symbol}", - f"to {self.to_symbol}", - "load", - "qa", - ] - return [] - - @log_start_end(log=logger) - def call_pick(self, other_args: List[str]): - """Process pick command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="pick", - description=""" - Change target variable - """, - ) - parser.add_argument( - "-t", - "--target", - dest="target", - choices=list(self.data.columns), - help="Select variable to analyze", - ) - if other_args and "-t" not in other_args and "-h" not in other_args: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - self.target = ns_parser.target - - @log_start_end(log=logger) - def call_raw(self, other_args: List[str]): - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="raw", - description=""" - Print raw data to console - """, - ) - parser.add_argument( - "-l", - "--limit", - help="Number to show", - type=check_positive, - default=20, - dest="limit", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sortby", - help="The column to sort by", - type=str.lower, - choices=[x.lower().replace(" ", "") for x in self.data.columns], - dest="sortby", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_raw( - data=self.data, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_summary(self, other_args: List[str]): - """Process summary command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="summary", - description=""" - Summary statistics - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_summary( - data=self.data, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_line(self, other_args: List[str]): - """Process line command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="line", - description="Show line plot of selected data or highlight specific datetimes.", - ) - parser.add_argument( - "--log", - help="Plot with y on log scale", - dest="log", - action="store_true", - default=False, - ) - parser.add_argument( - "--ml", - help="Draw vertical line markers to highlight certain events", - dest="ml", - type=check_list_dates, - default="", - ) - parser.add_argument( - "--ms", - help="Draw scatter markers to highlight certain events", - dest="ms", - type=check_list_dates, - default="", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - qa_view.display_line( - self.data[self.target], - title=f"{self.ticker} {self.target}", - log_y=ns_parser.log, - markers_lines=ns_parser.ml, - markers_scatter=ns_parser.ms, - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_hist(self, other_args: List[str]): - """Process hist command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="hist", - description=""" - Histogram with density and rug - """, - ) - parser.add_argument( - "-b", - "--bins", - type=check_positive, - default=15, - dest="n_bins", - choices=range(10, 100), - metavar="BINS", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_hist( - symbol=self.ticker, - data=self.data, - target=self.target, - bins=ns_parser.n_bins, - ) - - @log_start_end(log=logger) - def call_cdf(self, other_args: List[str]): - """Process cdf command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="cdf", - description=""" - Cumulative distribution function - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_cdf( - symbol=self.ticker, - data=self.data, - target=self.target, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bw(self, other_args: List[str]): - """Process bwy command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="bw", - description=""" - Box and Whisker plot - """, - ) - parser.add_argument( - "-y", - "--yearly", - action="store_true", - default=False, - dest="year", - help="Flag to show yearly bw plot", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_bw( - symbol=self.ticker, - data=self.data, - target=self.target, - yearly=ns_parser.year, - ) - - @log_start_end(log=logger) - def call_decompose(self, other_args: List[str]): - """Process decompose command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="decompose", - description=""" - Decompose time series as: - - Additive Time Series = Level + CyclicTrend + Residual + Seasonality - - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality - """, - ) - parser.add_argument( - "-m", - "--multiplicative", - action="store_true", - default=False, - dest="multiplicative", - help="decompose using multiplicative model instead of additive", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - qa_view.display_seasonal( - symbol=self.ticker, - data=self.data, - target=self.target, - multiplicative=ns_parser.multiplicative, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cusum(self, other_args: List[str]): - """Process cusum command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cusum", - description=""" - Cumulative sum algorithm (CUSUM) to detect abrupt changes in data - """, - ) - parser.add_argument( - "-t", - "--threshold", - dest="threshold", - type=float, - default=( - max(self.data[self.target].values) - min(self.data[self.target].values) - ) - / 40, - help="threshold", - ) - parser.add_argument( - "-d", - "--drift", - dest="drift", - type=float, - default=( - max(self.data[self.target].values) - min(self.data[self.target].values) - ) - / 80, - help="drift", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - qa_view.display_cusum( - data=self.data, - target=self.target, - threshold=ns_parser.threshold, - drift=ns_parser.drift, - ) - - @log_start_end(log=logger) - def call_acf(self, other_args: List[str]): - """Process acf command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="acf", - description=""" - Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff forex data - """, - ) - parser.add_argument( - "-l", - "--lags", - dest="lags", - type=check_positive, - default=15, - help="maximum lags to display in plots", - choices=range(5, 100), - metavar="LAGS", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.target != "Close": - console.print( - "Target not Close. For best results, use `pick Close` first." - ) - - qa_view.display_acf( - symbol=self.ticker, - data=self.data, - target=self.target, - lags=ns_parser.lags, - ) - - @log_start_end(log=logger) - def call_rolling(self, other_args: List[str]): - """Process rolling command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rolling", - description=""" - Rolling mean and std deviation - """, - ) - parser.add_argument( - "-w", - "--window", - action="store", - dest="n_window", - type=check_positive, - default=14, - help="Window length", - choices=range(5, 100), - metavar="N_WINDOW", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - rolling_view.display_mean_std( - symbol=self.ticker, - data=self.data, - target=self.target, - window=ns_parser.n_window, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_spread(self, other_args: List[str]): - """Process spread command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="spread", - description="""Shows rolling spread measurement - """, - ) - parser.add_argument( - "-w", - "--window", - action="store", - dest="n_window", - type=check_positive, - default=14, - help="Window length", - choices=range(5, 100), - metavar="N_WINDOW", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - rolling_view.display_spread( - symbol=self.ticker, - data=self.data, - target=self.target, - window=ns_parser.n_window, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_quantile(self, other_args: List[str]): - """Process quantile command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="quantile", - description=""" - The quantiles are values which divide the distribution such that - there is a given proportion of observations below the quantile. - For example, the median is a quantile. The median is the central - value of the distribution, such that half the points are less than - or equal to it and half are greater than or equal to it. - - By default, q is set at 0.5, which effectively is median. Change q to - get the desired quantile (0 pd.DataFrame: - """Get forex quote. - - Parameters - ---------- - symbol : str - Forex symbol to get quote for. - source : str, optional - Source to get quote from, by default "YahooFinance" - - Returns - ------- - pd.DataFrame - DataFrame of quote data. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> EUR_USD_quote = openbb.forex.quote("EURUSD") - - This also supports AlphaVantage and will handle different conventions - >>> EUR_USD= openbb.forex.quote("EUR/USD", source="AlphaVantage") - """ - symbol = symbol.replace("/", "").replace("-", "") - to_symbol = symbol[:3] - from_symbol = symbol[3:] - if source == "YahooFinance": - data = yf.download(f"{symbol}=X", period="1d", interval="1m", progress=False) - return pd.DataFrame.from_dict( - { - "Last Refreshed": data.index[-1].strftime("%Y-%m-%d %H:%M:%S %Z"), - "Quote": data.Close[-1], - }, - orient="index", - ) - if source == "AlphaVantage": - to_symbol = symbol[:3] - from_symbol = symbol[3:] - data = av_model.get_quote(to_symbol, from_symbol) - return pd.DataFrame.from_dict(data) - - return pd.DataFrame() diff --git a/openbb_terminal/forex/technical_analysis/__init__.py b/openbb_terminal/forex/technical_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/forex/technical_analysis/ta_controller.py b/openbb_terminal/forex/technical_analysis/ta_controller.py deleted file mode 100644 index 751f1baf320..00000000000 --- a/openbb_terminal/forex/technical_analysis/ta_controller.py +++ /dev/null @@ -1,987 +0,0 @@ -"""Crypto Technical Analysis Controller Module""" - -__docformat__ = "numpy" -# pylint:disable=too-many-lines - -import argparse -import logging -from datetime import datetime -from typing import List, Optional - -import numpy as np -import pandas as pd - -from openbb_terminal.common.technical_analysis import ( - custom_indicators_view, - momentum_view, - overlap_view, - trend_indicators_view, - volatility_model, - volatility_view, - volume_view, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forex.forex_helper import FOREX_SOURCES -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - check_positive, - check_positive_float, - check_positive_list, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class TechnicalAnalysisController(StockBaseController): - """Technical Analysis Controller class""" - - CHOICES_COMMANDS = [ - "ema", - "sma", - "zlma", - "cci", - "macd", - "rsi", - "stoch", - "fisher", - "cg", - "adx", - "aroon", - "bbands", - "donchian", - "fib", - ] - - PATH = "/forex/ta/" - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - source: str, - start: datetime, - interval: str, - data: pd.DataFrame, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - self.source = source - self.start = start - self.interval = interval - self.data = data - self.data["Adj Close"] = data["Close"] - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("forex/ta/") - mt.add_param( - "_currency", f"{self.ticker} (from {self.start.strftime('%Y-%m-%d')})" - ) - mt.add_param("_source", FOREX_SOURCES[self.source]) - mt.add_raw("\n") - mt.add_info("_overlap_") - mt.add_cmd("ema") - mt.add_cmd("sma") - mt.add_cmd("zlma") - mt.add_info("_momentum_") - mt.add_cmd("cci") - mt.add_cmd("macd") - mt.add_cmd("rsi") - mt.add_cmd("stoch") - mt.add_cmd("fisher") - mt.add_cmd("cg") - mt.add_info("_trend_") - mt.add_cmd("adx") - mt.add_cmd("aroon") - mt.add_info("_volatility_") - mt.add_cmd("bbands") - mt.add_cmd("donchian") - mt.add_info("_custom_") - mt.add_cmd("fib") - console.print(text=mt.menu_text, menu="Forex - Technical Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - from_symbol, to_symbol = self.ticker.split("/") - return ["forex", f"from {from_symbol}", f"to {to_symbol}", "load", "ta"] - return [] - - # TODO: Go through all models and make sure all needed columns are in dfs. - # This todo is a duplicated in crypto/ta_controller - - @log_start_end(log=logger) - def call_ema(self, other_args: List[str]): - """Process ema command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ema", - description=""" - The Exponential Moving Average is a staple of technical - analysis and is used in countless technical indicators. In a Simple Moving - Average, each value in the time period carries equal weight, and values outside - of the time period are not included in the average. However, the Exponential - Moving Average is a cumulative calculation, including all data. Past values have - a diminishing contribution to the average, while more recent values have a greater - contribution. This method allows the moving average to be more responsive to changes - in the data. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=[20, 50], - help="Window lengths. Multiple values indicated as comma separated values.", - ) - - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - overlap_view.view_ma( - ma_type="EMA", - symbol=self.ticker, - data=self.data["Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_sma(self, other_args: List[str]): - """Process sma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sma", - description=""" - Moving Averages are used to smooth the data in an array to - help eliminate noise and identify trends. The Simple Moving Average is literally - the simplest form of a moving average. Each output value is the average of the - previous n values. In a Simple Moving Average, each value in the time period carries - equal weight, and values outside of the time period are not included in the average. - This makes it less responsive to recent changes in the data, which can be useful for - filtering out those changes. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=[20, 50], - help="Window lengths. Multiple values indicated as comma separated values. ", - ) - - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - overlap_view.view_ma( - ma_type="SMA", - symbol=self.ticker, - data=self.data["Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_zlma(self, other_args: List[str]): - """Process zlma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="zlma", - description=""" - The zero lag exponential moving average (ZLEMA) indicator - was created by John Ehlers and Ric Way. The idea is do a - regular exponential moving average (EMA) calculation but - on a de-lagged data instead of doing it on the regular data. - Data is de-lagged by removing the data from "lag" days ago - thus removing (or attempting to) the cumulative effect of - the moving average. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=[20], - help="Window lengths. Multiple values indicated as comma separated values.", - ) - - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - overlap_view.view_ma( - ma_type="ZLMA", - symbol=self.ticker, - data=self.data["Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cci(self, other_args: List[str]): - """Process cci command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cci", - description=""" - The CCI is designed to detect beginning and ending market trends. - The range of 100 to -100 is the normal trading range. CCI values outside of this - range indicate overbought or oversold conditions. You can also look for price - divergence in the CCI. If the price is making new highs, and the CCI is not, - then a price correction is likely. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=0.015, - help="scalar", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_cci( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_macd(self, other_args: List[str]): - """Process macd command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="macd", - description=""" - The Moving Average Convergence Divergence (MACD) is the difference - between two Exponential Moving Averages. The Signal line is an Exponential Moving - Average of the MACD. \n \n The MACD signals trend changes and indicates the start - of new trend direction. High values indicate overbought conditions, low values - indicate oversold conditions. Divergence with the price indicates an end to the - current trend, especially if the MACD is at extreme high or low values. When the MACD - line crosses above the signal line a buy signal is generated. When the MACD crosses - below the signal line a sell signal is generated. To confirm the signal, the MACD - should be above zero for a buy, and below zero for a sell. - """, - ) - parser.add_argument( - "--fast", - action="store", - dest="n_fast", - type=check_positive, - default=12, - help="The short period.", - ) - parser.add_argument( - "--slow", - action="store", - dest="n_slow", - type=check_positive, - default=26, - help="The long period.", - ) - parser.add_argument( - "--signal", - action="store", - dest="n_signal", - type=check_positive, - default=9, - help="The signal period.", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_macd( - symbol=self.ticker, - data=self.data["Adj Close"], - n_fast=ns_parser.n_fast, - n_slow=ns_parser.n_slow, - n_signal=ns_parser.n_signal, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rsi(self, other_args: List[str]): - """Process rsi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rsi", - description=""" - The Relative Strength Index (RSI) calculates a ratio of the - recent upward price movements to the absolute price movement. The RSI ranges - from 0 to 100. The RSI is interpreted as an overbought/oversold indicator when - the value is over 70/below 30. You can also look for divergence with price. If - the price is making new highs/lows, and the RSI is not, it indicates a reversal. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - parser.add_argument( - "-d", - "--drift", - action="store", - dest="n_drift", - type=check_positive, - default=1, - help="drift", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_rsi( - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - drift=ns_parser.n_drift, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_stoch(self, other_args: List[str]): - """Process stoch command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="stoch", - description=""" - The Stochastic Oscillator measures where the close is in relation - to the recent trading range. The values range from zero to 100. %D values over 75 - indicate an overbought condition; values under 25 indicate an oversold condition. - When the Fast %D crosses above the Slow %D, it is a buy signal; when it crosses - below, it is a sell signal. The Raw %K is generally considered too erratic to use - for crossover signals. - """, - ) - parser.add_argument( - "-k", - "--fastkperiod", - action="store", - dest="n_fastkperiod", - type=check_positive, - default=14, - help="The time period of the fastk moving average", - ) - parser.add_argument( - "-d", - "--slowdperiod", - action="store", - dest="n_slowdperiod", - type=check_positive, - default=3, - help="The time period of the slowd moving average", - ) - parser.add_argument( - "--slowkperiod", - action="store", - dest="n_slowkperiod", - type=check_positive, - default=3, - help="The time period of the slowk moving average", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_stoch( - symbol=self.ticker, - data=self.data, - fastkperiod=ns_parser.n_fastkperiod, - slowdperiod=ns_parser.n_slowdperiod, - slowkperiod=ns_parser.n_slowkperiod, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fisher(self, other_args: List[str]): - """Process fisher command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fisher", - description=""" - The Fisher Transform is a technical indicator created by John F. Ehlers - that converts prices into a Gaussian normal distribution.1 The indicator - highlights when prices have moved to an extreme, based on recent prices. - This may help in spotting turning points in the price of an asset. It also - helps show the trend and isolate the price waves within a trend. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_fisher( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cg(self, other_args: List[str]): - """Process cg command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cg", - description=""" - The Center of Gravity indicator, in short, is used to anticipate future price movements - and to trade on price reversals as soon as they happen. However, just like other oscillators, - the COG indicator returns the best results in range-bound markets and should be avoided when - the price is trending. Traders who use it will be able to closely speculate the upcoming - price change of the asset. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - momentum_view.display_cg( - symbol=self.ticker, - data=self.data["Adj Close"], - window=ns_parser.n_length, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_adx(self, other_args: List[str]): - """Process adx command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="adx", - description=""" - The ADX is a Welles Wilder style moving average of the Directional Movement Index (DX). - The values range from 0 to 100, but rarely get above 60. To interpret the ADX, consider - a high number to be a strong trend, and a low number, a weak trend. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - - parser.add_argument( - "-d", - "--drift", - action="store", - dest="n_drift", - type=check_positive, - default=1, - help="drift", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - trend_indicators_view.display_adx( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - drift=ns_parser.n_drift, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_aroon(self, other_args: List[str]): - """Process aroon command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="aroon", - description=""" - The word aroon is Sanskrit for "dawn's early light." The Aroon - indicator attempts to show when a new trend is dawning. The indicator consists - of two lines (Up and Down) that measure how long it has been since the highest - high/lowest low has occurred within an n period range. \n \n When the Aroon Up is - staying between 70 and 100 then it indicates an upward trend. When the Aroon Down - is staying between 70 and 100 then it indicates an downward trend. A strong upward - trend is indicated when the Aroon Up is above 70 while the Aroon Down is below 30. - Likewise, a strong downward trend is indicated when the Aroon Down is above 70 while - the Aroon Up is below 30. Also look for crossovers. When the Aroon Down crosses above - the Aroon Up, it indicates a weakening of the upward trend (and vice versa). - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=25, - help="length", - ) - - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=check_positive, - default=0, - help="offset", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - trend_indicators_view.display_aroon( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bbands(self, other_args: List[str]): - """Process bbands command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bbands", - description=""" - Bollinger Bands consist of three lines. The middle band is a simple - moving average (generally 20 periods) of the typical price (TP). The upper and lower - bands are F standard deviations (generally 2) above and below the middle band. - The bands widen and narrow when the volatility of the price is higher or lower, - respectively. \n \nBollinger Bands do not, in themselves, generate buy or sell signals; - they are an indicator of overbought or oversold conditions. When the price is near the - upper or lower band it indicates that a reversal may be imminent. The middle band - becomes a support or resistance level. The upper and lower bands can also be - interpreted as price targets. When the price bounces off of the lower band and crosses - the middle band, then the upper band becomes the price target. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=5, - help="length", - ) - - parser.add_argument( - "-s", - "--std", - action="store", - dest="n_std", - type=check_positive_float, - default=2, - help="std", - choices=np.arange(0.0, 10, 0.25).tolist(), - metavar="N_STD", - ) - - parser.add_argument( - "-m", - "--mamode", - action="store", - dest="s_mamode", - default="sma", - choices=volatility_model.MAMODES, - help="mamode", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volatility_view.display_bbands( - symbol=self.ticker, - data=self.data, - window=ns_parser.n_length, - n_std=ns_parser.n_std, - mamode=ns_parser.s_mamode, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_donchian(self, other_args: List[str]): - """Process donchian command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="donchian", - description=""" - Donchian Channels are three lines generated by moving average - calculations that comprise an indicator formed by upper and lower - bands around a midrange or median band. The upper band marks the - highest price of a security over N periods while the lower band - marks the lowest price of a security over N periods. The area - between the upper and lower bands represents the Donchian Channel. - """, - ) - - parser.add_argument( - "-u", - "--length_upper", - action="store", - dest="n_length_upper", - type=check_positive, - default=20, - help="length", - ) - - parser.add_argument( - "-l", - "--length_lower", - action="store", - dest="n_length_lower", - type=check_positive, - default=20, - help="length", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volatility_view.display_donchian( - symbol=self.ticker, - data=self.data, - upper_length=ns_parser.n_length_upper, - lower_length=ns_parser.n_length_lower, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ad(self, other_args: List[str]): - """Process ad command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ad", - description=""" - The Accumulation/Distribution Line is similar to the On Balance - Volume (OBV), which sums the volume times +1/-1 based on whether the close is - higher than the previous close. The Accumulation/Distribution indicator, however - multiplies the volume by the close location value (CLV). The CLV is based on the - movement of the issue within a single bar and can be +1, -1 or zero. \n \n - The Accumulation/Distribution Line is interpreted by looking for a divergence in - the direction of the indicator relative to price. If the Accumulation/Distribution - Line is trending upward it indicates that the price may follow. Also, if the - Accumulation/Distribution Line becomes flat while the price is still rising (or falling) - then it signals an impending flattening of the price. - """, - ) - - parser.add_argument( - "--open", - action="store_true", - default=False, - dest="b_use_open", - help="uses open value of data", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volume_view.display_ad( - symbol=self.ticker, - data=self.data, - use_open=ns_parser.b_use_open, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_obv(self, other_args: List[str]): - """Process obv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="obv", - description=""" - The On Balance Volume (OBV) is a cumulative total of the up and - down volume. When the close is higher than the previous close, the volume is added - to the running total, and when the close is lower than the previous close, the volume - is subtracted from the running total. \n \n To interpret the OBV, look for the OBV - to move with the price or precede price moves. If the price moves before the OBV, - then it is a non-confirmed move. A series of rising peaks, or falling troughs, in the - OBV indicates a strong trend. If the OBV is flat, then the market is not trending. - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - volume_view.display_obv( - symbol=self.ticker, - data=self.data, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fib(self, other_args: List[str]): - """Process fib command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fib", - description="Calculates the fibonacci retracement levels", - ) - - parser.add_argument( - "-p", - "--period", - dest="period", - type=int, - help="Days to look back for retracement", - default=120, - choices=range(1, 960), - metavar="PERIOD", - ) - - parser.add_argument( - "--start", - dest="start", - type=valid_date, - help="Starting date to select", - required="--end" in other_args, - ) - - parser.add_argument( - "--end", - dest="end", - type=valid_date, - help="Ending date to select", - required="--start" in other_args, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - custom_indicators_view.fibonacci_retracement( - symbol=self.ticker, - data=self.data, - limit=ns_parser.period, - start_date=ns_parser.start, - end_date=ns_parser.end, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/futures/__init__.py b/openbb_terminal/futures/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/futures/databento_view.py b/openbb_terminal/futures/databento_view.py deleted file mode 100644 index a9edc739fb6..00000000000 --- a/openbb_terminal/futures/databento_view.py +++ /dev/null @@ -1,82 +0,0 @@ -"""DataBento view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks import databento_model, stocks_helper - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_historical( - symbol: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Optional[OpenBBFigure]: - """Display historical futures [Source: DataBento] - - Parameters - ---------- - symbol: List[str] - Symbol to display - start_date: Optional[str] - Start date of the historical data with format YYYY-MM-DD - end_date: Optional[str] - End date of the historical data with format YYYY-MM-DD - raw: bool - Display futures timeseries in raw format - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = databento_model.get_historical_futures( - symbol.upper(), start_date=start_date, end_date=end_date - ) - if data.empty: - return console.print(f"No data found for {symbol}.") - - data = stocks_helper.process_candle(data) - figure = stocks_helper.display_candle( - symbol=symbol, - data=data, - external_axes=True, - ) - - if raw: - print_rich_table( - data, - headers=list(data.columns), - show_index=True, - title="Futures timeseries", - export=bool(export), - ) - console.print() - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "historical_db", - data, - sheet_name, - figure, - ) - - return figure.show(external=raw or external_axes) # type: ignore diff --git a/openbb_terminal/futures/futures_controller.py b/openbb_terminal/futures/futures_controller.py deleted file mode 100644 index eda14632287..00000000000 --- a/openbb_terminal/futures/futures_controller.py +++ /dev/null @@ -1,278 +0,0 @@ -""" Futures Controller """ - -__docformat__ = "numpy" - -# pylint:disable=too-many-lines - -import argparse -import logging -from datetime import datetime, timedelta -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.futures import databento_view, yfinance_model, yfinance_view -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - parse_and_split_input, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -def valid_expiry_date(s: str) -> str: - """Argparse type to check date is in valid format""" - try: - if not s: - return s - return datetime.strptime(s, "%Y-%m").strftime("%Y-%m") - except ValueError as value_error: - logging.exception(str(value_error)) - raise argparse.ArgumentTypeError(f"Not a valid date: {s}") from value_error - - -class FuturesController(BaseController): - """Futures Controller class""" - - CHOICES_COMMANDS = [ - "search", - "curve", - "historical", - ] - - curve_type = "futures" - - PATH = "/futures/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.all_tickers = yfinance_model.FUTURES_DATA["Ticker"].unique().tolist() - self.all_exchanges = yfinance_model.FUTURES_DATA["Exchange"].unique().tolist() - self.all_categories = yfinance_model.FUTURES_DATA["Category"].unique().tolist() - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - self.choices: dict = self.choices_default - - self.choices["historical"].update({c: None for c in self.all_tickers}) - self.choices["historical"]["--ticker"] = {c: None for c in self.all_tickers} - self.choices["curve"].update({c: None for c in self.all_tickers}) - - self.completer = NestedCompleter.from_nested_dict(self.choices) # type: ignore - - def parse_input(self, an_input: str) -> List: - """Parse controller input - - Overrides the parent class function to handle github org/repo path convention. - See `BaseController.parse_input()` for details. - """ - # Filtering out sorting parameters with forward slashes like P/E - sort_filter = r"((\ -s |\ --sortby ).*?(P\/E|Fwd P\/E|P\/S|P\/B|P\/C|P\/FCF)*)" - - custom_filters = [sort_filter] - - commands = parse_and_split_input( - an_input=an_input, custom_filters=custom_filters - ) - return commands - - def print_help(self): - """Print help""" - mt = MenuText("futures/") - mt.add_cmd("search") - mt.add_raw("\n") - mt.add_cmd("historical") - mt.add_cmd("curve") - console.print(text=mt.menu_text, menu="Futures") - - @log_start_end(log=logger) - def call_search(self, other_args: List[str]): - """Process search command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="search", - description="""Search futures. [Source: YahooFinance]""", - ) - parser.add_argument( - "-e", - "--exchange", - dest="exchange", - type=str, - choices=self.all_exchanges, - default="", - help="Select the exchange where the future exists", - ) - parser.add_argument( - "-c", - "--category", - dest="category", - type=str, - choices=self.all_categories, - default="", - help="Select the category where the future exists", - ) - parser.add_argument( - "-d", - "--description", - dest="description", - type=str, - nargs="+", - default="", - help="Select the description future you are interested in", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yfinance_view.display_search( - category=ns_parser.category, - exchange=ns_parser.exchange, - description=" ".join(ns_parser.description), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_historical(self, other_args: List[str]): - """Process historical command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="historical", - description="""Display futures historical. [Source: YahooFinance]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - type=str, - default="", - help="Future ticker to display timeseries separated by comma when multiple, e.g.: BLK,QI", - required="-h" not in other_args, - ) - parser.add_argument( - "-s", - "--start", - dest="start", - type=valid_date, - help="Initial date. Default: 2 years ago", - default=(datetime.now() - timedelta(days=2 * 365)), - ) - parser.add_argument( - "-e", - "--end", - dest="end", - type=valid_date, - help="Final date. Default: today", - default=datetime.now(), - ) - parser.add_argument( - "--expiry", - dest="expiry", - type=valid_expiry_date, - help="Select future expiry date with format YYYY-MM", - default="", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ) - if ns_parser: - if ns_parser.source == "YahooFinance": - yfinance_view.display_historical( - symbols=ns_parser.ticker.upper().split(","), - expiry=ns_parser.expiry, - start_date=ns_parser.start.strftime("%Y-%m-%d"), - end_date=ns_parser.end.strftime("%Y-%m-%d"), - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - if ns_parser.source == "DataBento": - databento_view.display_historical( - symbol=ns_parser.ticker, - start_date=ns_parser.start.strftime("%Y-%m-%d"), - end_date=ns_parser.end.strftime("%Y-%m-%d"), - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_curve(self, other_args: List[str]): - """Process curve command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="curve", - description="""Display futures curve. [Source: YahooFinance]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - type=lambda x: str(x).upper(), - default="", - help="Future curve to be selected", - required="-h" not in other_args, - metavar="TICKER", - choices=self.all_tickers, - ) - parser.add_argument( - "-d", - "--date", - dest="date", - type=valid_date, - help="Include the curve as of a previous date with format YYYY-MM-DD", - metavar="DATE", - default=None, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - raw=True, - ) - - if ns_parser: - if ns_parser.date: - yfinance_view.display_curve( - symbol=ns_parser.ticker.upper(), - date=ns_parser.date.strftime("%Y-%m-%d"), - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - yfinance_view.display_curve( - symbol=ns_parser.ticker.upper(), - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/futures/futures_helper.py b/openbb_terminal/futures/futures_helper.py deleted file mode 100644 index 4248f0a8753..00000000000 --- a/openbb_terminal/futures/futures_helper.py +++ /dev/null @@ -1,16 +0,0 @@ -from matplotlib.axes import Axes - - -def make_white(ax: Axes) -> None: - """Make the labels and ticks white - - Parameters - ---------- - ax: Axes - The axes to make white - - """ - ax.xaxis.label.set_color("white") - ax.tick_params(axis="x", colors="white") - ax.yaxis.label.set_color("white") - ax.tick_params(axis="y", colors="white") diff --git a/openbb_terminal/futures/sdk_helper.py b/openbb_terminal/futures/sdk_helper.py deleted file mode 100644 index 2863be93972..00000000000 --- a/openbb_terminal/futures/sdk_helper.py +++ /dev/null @@ -1,57 +0,0 @@ -"""SDK Helper Functions""" - -__docformat__ = "numpy" - -from typing import List, Optional, Union - -import pandas as pd - -from openbb_terminal.futures import yfinance_model -from openbb_terminal.rich_config import console -from openbb_terminal.stocks import databento_model - - -def get_historical( - symbols: Union[str, List[str]], - start_date: str, - end_date: str, - source: Optional[str] = "YahooFinance", - expiry: Optional[str] = "", -) -> pd.DataFrame: - """Get historical futures data - - Parameters - ---------- - symbols : Union[str, List[str]] - The futures symbols you want to retrieve historical data for. - It can be either a single symbol as a string or a list of symbols. - start_date : str - The start date of the historical data you want to retrieve. The date should be in the format "YYYY-MM-DD". - end_date : str - The end date of the historical data you want to retrieve. The date should be in the format "YYYY-MM-DD". - source : Optional[str], default "YahooFinance" - The source from which you want to retrieve the historical data. - Valid values for the source are "YahooFinance" and "DataBento". - expiry : Optional[str], default "" - The expiry date for futures contracts. This parameter is optional and defaults to an empty string. - It is applicable only when the source is "YahooFinance" and the symbols are futures contracts. - - Returns - ------- - pd.DataFrame - A pandas DataFrame containing the historical data for the given symbols and date range. - """ - if source == "YahooFinance": - if isinstance(symbols, str): - symbols = [symbols] - return yfinance_model.get_historical_futures( - symbols, expiry, start_date, end_date - ) - if source == "DataBento": - if isinstance(symbols, list): - console.print( - "DataBento only supports one symbol at a time. Using first symbol." - ) - symbols = symbols[0] - return databento_model.get_historical_futures(symbols, start_date, end_date) - return pd.DataFrame() diff --git a/openbb_terminal/futures/yfinance_model.py b/openbb_terminal/futures/yfinance_model.py deleted file mode 100644 index 692d9ffbf71..00000000000 --- a/openbb_terminal/futures/yfinance_model.py +++ /dev/null @@ -1,216 +0,0 @@ -"""Yahoo Finance model""" - -__docformat__ = "numpy" - -import logging -import os -import sys -from datetime import datetime, timedelta -from typing import List, Optional - -import pandas as pd -import yfinance as yf -from dateutil.relativedelta import relativedelta - -from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -# pylint: disable=attribute-defined-outside-init - -logger = logging.getLogger(__name__) - -FUTURES_DATA = pd.read_csv(MISCELLANEOUS_DIRECTORY / "futures" / "futures.csv") - -MONTHS = { - 1: "F", - 2: "G", - 3: "H", - 4: "J", - 5: "K", - 6: "M", - 7: "N", - 8: "Q", - 9: "U", - 10: "V", - 11: "X", - 12: "Z", -} - - -class HiddenPrints: - def __enter__(self): - self._original_stdout = sys.stdout - sys.stdout = open(os.devnull, "w") # noqa: SIM115 - - def __exit__(self, exc_type, exc_val, exc_tb): - sys.stdout.close() - sys.stdout = self._original_stdout - - -@log_start_end(log=logger) -def get_search_futures( - category: str = "", - exchange: str = "", - description: str = "", -): - """Get search futures [Source: Yahoo Finance] - - Parameters - ---------- - category: str - Select the category where the future exists - exchange: str - Select the exchange where the future exists - description: str - Select the description where the future exists - """ - df = FUTURES_DATA - if category: - df = df[df["Category"] == category] - if exchange: - df = df[df["Exchange"] == exchange] - if description: - df = df[ - [description.lower() in desc.lower() for desc in df["Description"].values] - ] - return df - - -@log_start_end(log=logger) -def get_historical_futures( - symbols: List[str], - expiry: str = "", - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get historical futures [Source: Yahoo Finance] - - Parameters - ---------- - symbols: List[str] - List of future timeseries symbols to display - expiry: str - Future expiry date with format YYYY-MM - start_date: Optional[str] - Start date of the historical data with format YYYY-MM-DD - end_date: Optional[str] - End date of the historical data with format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - Historical futures data - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=3 * 365)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - if start_date >= end_date: - console.print("[yellow]Start date must be before end date.[/yellow]") - return pd.DataFrame() - - if expiry: - symbols_with_expiry = list() - - for symbol in symbols: - expiry_date = datetime.strptime(expiry, "%Y-%m") - exchange = FUTURES_DATA[FUTURES_DATA["Ticker"] == symbol][ - "Exchange" - ].values[0] - - symbols_with_expiry.append( - f"{symbol}{MONTHS[expiry_date.month]}{str(expiry_date.year)[-2:]}.{exchange}" - ) - - return yf.download( - symbols_with_expiry, - start=start_date, - end=end_date, - progress=False, - period="max", - ignore_tz=True, - ) - - df = yf.download( - [t + "=F" for t in symbols], - start=start_date, - end=end_date, - progress=False, - period="max", - ignore_tz=True, - ) - if len(symbols) > 1: - df.columns = pd.MultiIndex.from_tuples( - [(tup[0], tup[1].replace("=F", "")) for tup in df.columns] - ) - - return df - - -@log_start_end(log=logger) -def get_curve_futures( - symbol: str = "", - date: Optional[str] = "", -) -> pd.DataFrame: - """Get curve futures [Source: Yahoo Finance] - - Parameters - ---------- - symbol: str - symbol to get forward curve - date: str - optionally include historical price for each contract - - Returns - ------- - pd.DataFrame - Dictionary with sector weightings allocation - """ - if symbol not in FUTURES_DATA["Ticker"].unique().tolist(): - return pd.DataFrame() - - exchange = FUTURES_DATA[FUTURES_DATA["Ticker"] == symbol]["Exchange"].values[0] - today = datetime.today() - - futures_index = list() - futures_curve = list() - historical_curve = list() - i = 0 - empty_count = 0 - # Loop through until we find 12 consecutive empty months - while empty_count < 12: - future = today + relativedelta(months=i) - future_symbol = ( - f"{symbol}{MONTHS[future.month]}{str(future.year)[-2:]}.{exchange}" - ) - with HiddenPrints(): - data = yf.download(future_symbol, progress=False, ignore_tz=True) - - if data.empty: - empty_count += 1 - - else: - empty_count = 0 - futures_index.append(future.strftime("%b-%Y")) - futures_curve.append(data["Adj Close"].values[-1]) - if date != "": - historical_curve.append(data["Adj Close"].get(date, None)) - - i += 1 - - if not futures_index: - return pd.DataFrame() - - if historical_curve: - if None in historical_curve: - console.print("[yellow]No pricing data for selected date.[/yellow]") - return pd.DataFrame( - index=futures_index, - data=zip(futures_curve, historical_curve), - columns=["Last Price", date], - ) - return pd.DataFrame(index=futures_index, data=futures_curve, columns=["Last Price"]) diff --git a/openbb_terminal/futures/yfinance_view.py b/openbb_terminal/futures/yfinance_view.py deleted file mode 100644 index 70ba4a94a9a..00000000000 --- a/openbb_terminal/futures/yfinance_view.py +++ /dev/null @@ -1,281 +0,0 @@ -"""Yahoo Finance view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import List, Optional - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.futures import yfinance_model -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_search( - category: str = "", - exchange: str = "", - description: str = "", - export: str = "", - sheet_name: Optional[str] = None, -): - """Display search futures [Source: Yahoo Finance] - - Parameters - ---------- - category: str - Select the category where the future exists - exchange: str - Select the exchange where the future exists - description: str - Select the description of the future - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data - """ - df = yfinance_model.get_search_futures(category, exchange, description) - if df.empty: - console.print("[red]No futures data found.\n[/red]") - return - - print_rich_table(df, export=bool(export)) - console.print() - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "search", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_historical( - symbols: List[str], - expiry: str = "", - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display historical futures [Source: Yahoo Finance] - - Parameters - ---------- - symbols: List[str] - List of future timeseries symbols to display - expiry: str - Future expiry date with format YYYY-MM - start_date: Optional[str] - Start date of the historical data with format YYYY-MM-DD - end_date: Optional[str] - End date of the historical data with format YYYY-MM-DD - raw: bool - Display futures timeseries in raw format - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - symbols_validated = list() - for symbol in symbols: - if symbol in yfinance_model.FUTURES_DATA["Ticker"].unique().tolist(): - symbols_validated.append(symbol) - else: - console.print(f"[red]{symbol} is not a valid symbol[/red]") - - symbols = symbols_validated - - if not symbols: - return console.print("No symbol was provided.\n") - - historicals = yfinance_model.get_historical_futures( - symbols, expiry, start_date, end_date - ) - - if historicals.empty: - return None - - fig = OpenBBFigure() - - if len(symbols) > 1: - for tick in historicals["Adj Close"].columns.tolist(): - if len(historicals["Adj Close"][tick].dropna()) == 1: - console.print( - f"\nA single datapoint on {tick} is not enough to depict a chart, data shown below." - ) - naming = yfinance_model.FUTURES_DATA[ - yfinance_model.FUTURES_DATA["Ticker"] == tick - ]["Description"].values[0] - print_rich_table( - historicals["Adj Close"][tick].dropna().to_frame(), - headers=[naming], - show_index=True, - title="Futures timeseries", - ) - continue - - name = yfinance_model.FUTURES_DATA[ - yfinance_model.FUTURES_DATA["Ticker"] == tick - ]["Description"].values[0] - - fig.add_scatter( - x=historicals["Adj Close"][tick].dropna().index, - y=historicals["Adj Close"][tick].dropna().values, - name=name, - ) - - else: - if len(historicals["Adj Close"]) == 1: - console.print( - f"\nA single datapoint on {symbols[0]} is not enough to depict a chart, data shown below." - ) - return print_rich_table( - historicals, - headers=list(historicals["Adj Close"].columns), - show_index=True, - title="Futures timeseries", - ) - - name = yfinance_model.FUTURES_DATA[ - yfinance_model.FUTURES_DATA["Ticker"] == symbols[0] - ]["Description"].values[0] - - fig.add_scatter( - x=historicals["Adj Close"].dropna().index, - y=historicals["Adj Close"].dropna().values, - name=name, - ) - if expiry: - name += f" with expiry {expiry}" - - fig.set_title(name) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "historical", - historicals, - sheet_name, - fig, - ) - - if raw or len(historicals) == 1: - if not raw and len(historicals) == 1: - console.print( - "\nA single datapoint is not enough to depict a chart, data is presented below." - ) - - print_rich_table( - historicals, - headers=list(historicals.columns), - show_index=True, - title="Futures timeseries", - export=bool(export), - ) - return console.print() - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_curve( - symbol: str, - date: Optional[str] = "", - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display curve futures [Source: Yahoo Finance] - - Parameters - ---------- - symbol: str - Curve future symbol to display - date: str - Optionally include historical futures prices for each contract - raw: bool - Display futures prices in raw format - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Type of format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if symbol not in yfinance_model.FUTURES_DATA["Ticker"].unique().tolist(): - return console.print(f"[red]'{symbol}' is not a valid symbol[/red]") - - df = ( - yfinance_model.get_curve_futures(symbol) - if date == "" - else yfinance_model.get_curve_futures(symbol, date) - ) - - if df.empty: - return console.print("[red]No future data found to generate curve.[/red]\n") - - fig = OpenBBFigure() - - name = yfinance_model.FUTURES_DATA[yfinance_model.FUTURES_DATA["Ticker"] == symbol][ - "Description" - ].values[0] - - df.index = pd.to_datetime(df.index, format="%b-%Y") - - if date == "": - fig.add_scatter( - x=df.index, - y=df.iloc[:, 0], - mode="lines+markers", - name=name, - line=dict(dash="dash", width=4), - marker=dict(size=10), - ) - else: - for col in df.columns.tolist(): - fig.add_scatter( - x=df.index, - y=df[col], - mode="lines+markers", - name=col, - line=dict(dash="dash", width=4), - marker=dict(size=10), - ) - fig.set_title(name) - fig.update_layout(xaxis=dict(tickformat="%b-%Y")) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "curve", - df, - sheet_name, - fig, - ) - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title="Futures curve", - export=bool(export), - ) - return console.print() - - return fig.show(external=external_axes) diff --git a/openbb_terminal/helper_classes.py b/openbb_terminal/helper_classes.py deleted file mode 100644 index d64a1337873..00000000000 --- a/openbb_terminal/helper_classes.py +++ /dev/null @@ -1,384 +0,0 @@ -"""Helper classes.""" - -__docformat__ = "numpy" -import argparse -import io -import json -import os -from importlib import machinery, util -from pathlib import Path -from typing import Dict, List, Optional, Union - -import matplotlib.pyplot as plt -import plotly.express as px -from matplotlib import font_manager, ticker -from PIL import Image - -from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY -from openbb_terminal.core.session.current_user import get_current_user - - -# pylint: disable=too-few-public-methods -class ModelsNamespace: - """A namespace placeholder for the menu models. - - This class is used in all api wrappers to create a `models` namespace and import - all the model functions. - """ - - def __init__(self, folders: Union[str, List[str]]) -> None: - """Import all menu models into the models namespace. - - Instantiation of the namespace requires either a path to the folder that - contains model files or a list of such folders. - - Parameters - ---------- - folders : Union[str, List[str]] - a folder or a list of folders to import models from - """ - if isinstance(folders, str): - folders = [folders] - for folder in folders: - menu_models = [ - ( - f.replace("_model.py", ""), - os.path.abspath(os.path.join(folder, f)), - ) - for f in os.listdir(folder) - if f.endswith("_model.py") - ] - - for model_name, model_file in menu_models: - loader = machinery.SourceFileLoader(model_name, model_file) - spec = util.spec_from_loader(model_name, loader) - if spec is not None: - setattr(self, model_name, util.module_from_spec(spec)) - loader.exec_module(getattr(self, model_name)) - else: - pass - - -# pylint: disable=R0902 -class TerminalStyle: - """The class that helps with handling of style configurations. - - It serves styles for 3 libraries. For `Matplotlib` this class serves absolute paths - to the .mplstyle files. For `Matplotlib Finance` and `Rich` this class serves custom - styles as python dictionaries. - """ - - DEFAULT_STYLES_LOCATION = MISCELLANEOUS_DIRECTORY / "styles" / "default" - USER_STYLES_LOCATION = get_current_user().preferences.USER_DATA_DIRECTORY / "styles" - - mpl_styles_available: Dict[str, str] = {} - mpl_style: str = "" - - mpl_rcparams_available: Dict[str, str] = {} - mpl_rcparams: Dict = {} - - mpf_styles_available: Dict[str, str] = {} - mpf_style: Dict = {} - - console_styles_available: Dict[str, str] = {} - console_style: Dict[str, str] = {} - - down_color: str = "" - up_color: str = "" - - xticks_rotation: str = "" - tight_layout_padding: int = 0 - pie_wedgeprops: Dict = {} - pie_startangle: int = 0 - line_width: float = 1.5 - volume_bar_width: float = 0.8 - - def __init__( - self, - mpl_style: Optional[str] = "", - mpf_style: Optional[str] = "", - console_style: Optional[str] = "", - ) -> None: - """Instantiate a terminal style class - - The stylesheet files should be placed to the `styles/default` or `styles/user` - folders. The parameters required for class instantiation are stylesheet names - without extensions (following matplotlib convention). - - Ex. `styles/default/boring.mplstyle` should be passed as `boring`. - - Parameters - ---------- - mpl_style : str, optional - Style name without extension, by default "" - mpf_style : str, optional - Style name without extension, by default "" - console_style : str, optional - Style name without extension, by default "" - """ - # To import all styles from terminal repo folder to user data - - for folder in [self.DEFAULT_STYLES_LOCATION, self.USER_STYLES_LOCATION]: - self.load_available_styles_from_folder(folder) - self.load_custom_fonts_from_folder(folder) - - if mpl_style in self.mpl_styles_available: - self.mpl_style = self.mpl_styles_available[mpl_style] - else: - self.mpl_style = self.mpl_styles_available.get("dark", "") - - if mpl_style in self.mpl_rcparams_available: - with open(self.mpl_rcparams_available[mpl_style]) as stylesheet: - self.mpl_rcparams = json.load(stylesheet) - else: - with open(self.mpl_rcparams_available["dark"]) as stylesheet: - self.mpl_rcparams = json.load(stylesheet) - - if mpf_style in self.mpf_styles_available: - with open(self.mpf_styles_available[mpf_style]) as stylesheet: - self.mpf_style = json.load(stylesheet) - self.mpf_style["base_mpl_style"] = self.mpl_style - else: - with open(self.mpf_styles_available["dark"]) as stylesheet: - self.mpf_style = json.load(stylesheet) - self.mpf_style["base_mpl_style"] = self.mpl_style - - if "openbb_config" in self.console_styles_available: - with open(self.console_styles_available["openbb_config"]) as stylesheet: - self.console_style = json.load(stylesheet) - elif console_style in self.console_styles_available: - with open(self.console_styles_available[console_style]) as stylesheet: - self.console_style = json.load(stylesheet) - else: - with open(self.console_styles_available["dark"]) as stylesheet: - self.console_style = json.load(stylesheet) - - self.applyMPLstyle() - - def load_custom_fonts_from_folder(self, folder: Path) -> None: - """Load custom fonts form folder. - - TTF and OTF fonts are loaded into the mpl font manager and are available for - selection in mpl by their name (for example "Consolas" or "Hack"). - - Parameters - ---------- - folder : str - Path to the folder containing the fonts - """ - - if not folder.exists(): - return - - for font_file in folder.iterdir(): - if not font_file.is_file(): - continue - - if font_file.name.endswith(".otf") or font_file.name.endswith(".ttf"): - font_path = os.path.abspath(os.path.join(folder, font_file)) - font_manager.fontManager.addfont(font_path) - - def load_available_styles_from_folder(self, folder: Path) -> None: - """Load custom styles from folder. - - Parses the styles/default and styles/user folders and loads style files. - To be recognized files need to follow a naming convention: - *.mplstyle - matplotlib stylesheets - *.mplrc.json - matplotlib rc stylesheets that are not handled by mplstyle - *.mpfstyle.json - matplotlib finance stylesheets - *.richstyle.json - rich stylesheets - - Parameters - ---------- - folder : str - Path to the folder containing the stylesheets - """ - - if not folder.exists(): - return - - for stf in folder.iterdir(): - if not stf.is_file(): - continue - - if stf.name.endswith(".mplstyle"): - self.mpl_styles_available[stf.name.replace(".mplstyle", "")] = ( - os.path.join(folder, stf) - ) - elif stf.name.endswith(".mplrc.json"): - self.mpl_rcparams_available[stf.name.replace(".mplrc.json", "")] = ( - os.path.join(folder, stf) - ) - elif stf.name.endswith(".mpfstyle.json"): - self.mpf_styles_available[stf.name.replace(".mpfstyle.json", "")] = ( - os.path.join(folder, stf) - ) - elif stf.name.endswith(".richstyle.json"): - self.console_styles_available[ - stf.name.replace(".richstyle.json", "") - ] = os.path.join(folder, stf) - - def applyMPLstyle(self): - """Apply style to the current matplotlib context.""" - plt.style.use(self.mpl_style) - self.xticks_rotation = self.mpl_rcparams["xticks_rotation"] - self.tight_layout_padding = self.mpl_rcparams["tight_layout_padding"] - self.pie_wedgeprops = self.mpl_rcparams["pie_wedgeprops"] - self.pie_startangle = self.mpl_rcparams["pie_startangle"] - self.mpf_style["mavcolors"] = plt.rcParams["axes.prop_cycle"].by_key()["color"] - self.down_color = self.mpf_style["marketcolors"]["volume"]["down"] - self.up_color = self.mpf_style["marketcolors"]["volume"]["up"] - self.line_width = plt.rcParams["lines.linewidth"] - try: - self.volume_bar_width = self.mpl_rcparams["volume_bar_width"] - except Exception(): - pass - - def get_colors(self, reverse: bool = False) -> List: - """Get hex color sequence from the stylesheet.""" - plt.style.use(self.mpl_style) - colors = plt.rcParams["axes.prop_cycle"].by_key()["color"] - if reverse: - colors.reverse() - return colors - - def style_primary_axis( - self, - ax: plt.Axes, - data_index: Optional[List[int]] = None, - tick_labels: Optional[List[str]] = None, - ): - """Apply styling to a primary axis. - - Parameters - ---------- - ax : plt.Axes - A matplolib axis - """ - ax.yaxis.set_label_position("right") - ax.grid(axis="both", visible=True, zorder=0) - if ( - all([data_index, tick_labels]) - and isinstance(data_index, list) - and isinstance(tick_labels, list) - ): - ax.xaxis.set_major_formatter( - ticker.FuncFormatter( - lambda value, _: ( - tick_labels[int(value)] if int(value) in data_index else "" - ) - ) - ) - ax.xaxis.set_major_locator(ticker.MaxNLocator(6, integer=True)) - ax.tick_params(axis="x", labelrotation=self.xticks_rotation) - - def style_twin_axis(self, ax: plt.Axes): - """Apply styling to a twin axis. - - Parameters - ---------- - ax : plt.Axes - A matplolib axis - """ - ax.yaxis.set_label_position("left") - - def style_twin_axes(self, ax1: plt.Axes, ax2: plt.Axes): - """Apply styling to a twin axes - - Parameters - ---------- - ax1 : plt.Axes - Primary matplolib axis - ax2 : plt.Axes - Twinx matplolib axis - - """ - - ax1.tick_params(axis="x", labelrotation=self.xticks_rotation) - ax1.grid(axis="both", visible=True, zorder=0) - - ax2.grid(visible=False) - - def add_label( - self, - fig: plt.figure, - ): - """Add a text label to a figure in a funny position. - - Parameters - ---------- - fig : plt.figure - A matplotlib figure - """ - label = "OpenBB Terminal" - fig.text( - 0.99, - 0.0420, - label, - fontsize=12, - color="gray", - alpha=0.5, - horizontalalignment="right", - ) - - # pylint: disable=import-outside-toplevel - def add_cmd_source( - self, - fig: plt.figure, - ): - """Add a text label to a figure in a funny position. - - Parameters - ---------- - fig : plt.figure - A matplotlib figure - """ - from openbb_terminal.helper_funcs import command_location - - if command_location: - fig.text( - 0.01, - 0.5, - command_location, - rotation=90, - fontsize=12, - color="gray", - alpha=0.5, - verticalalignment="center", - ) - - # pylint: disable=import-outside-toplevel - def visualize_output( - self, force_tight_layout: bool = True, external_axes: bool = False - ): - """Show chart in an interactive widget.""" - - self.add_cmd_source(plt.gcf()) - self.add_label(plt.gcf()) - - if force_tight_layout: - plt.tight_layout(pad=self.tight_layout_padding) - - if external_axes: - img_buf = io.BytesIO() - plt.savefig(img_buf, format="jpg") - im = Image.open(img_buf) - fig = px.imshow(im) - plt.close() - fig.update_layout( - xaxis=dict(visible=False, showticklabels=False), - yaxis=dict(visible=False, showticklabels=False), - margin=dict(l=0, r=0, t=0, b=0), - autosize=False, - width=im.width, - height=im.height, - ) - else: - fig = None - plt.show() - return fig - - -class AllowArgsWithWhiteSpace(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, " ".join(values)) diff --git a/openbb_terminal/helper_funcs.py b/openbb_terminal/helper_funcs.py deleted file mode 100644 index 42c7fa979d9..00000000000 --- a/openbb_terminal/helper_funcs.py +++ /dev/null @@ -1,2215 +0,0 @@ -"""Helper functions.""" - -__docformat__ = "numpy" - -# pylint: disable=too-many-lines - -# IMPORTS STANDARD LIBRARY -# IMPORTS STANDARD -import argparse -import inspect -import io -import json -import logging -import os -import random -import re -import sys -import urllib.parse -import webbrowser -from datetime import ( - date as d, - datetime, - timedelta, -) -from difflib import SequenceMatcher -from functools import lru_cache -from pathlib import Path -from typing import Dict, List, Optional, Tuple, Union - -# IMPORTS THIRDPARTY -import iso8601 -import matplotlib -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import pandas.io.formats.format -import pandas_ta as ta -import pytz -import requests -import yfinance as yf -from holidays import US as us_holidays -from pandas._config.config import get_option -from pandas.plotting import register_matplotlib_converters -from PIL import Image, ImageDraw -from rich.table import Table -from screeninfo import get_monitors - -from openbb_terminal import OpenBBFigure, plots_backend -from openbb_terminal.core.config.paths import ( - HOME_DIRECTORY, - MISCELLANEOUS_DIRECTORY, -) -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.core.session.current_system import get_current_system - -# IMPORTS INTERNAL -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -register_matplotlib_converters() -if ( - get_current_user().preferences.PLOT_BACKEND is not None - and get_current_user().preferences.PLOT_BACKEND != "None" -): - matplotlib.use(get_current_user().preferences.PLOT_BACKEND) - -NO_EXPORT = 0 -EXPORT_ONLY_RAW_DATA_ALLOWED = 1 -EXPORT_ONLY_FIGURES_ALLOWED = 2 -EXPORT_BOTH_RAW_DATA_AND_FIGURES = 3 - -MENU_GO_BACK = 0 -MENU_QUIT = 1 -MENU_RESET = 2 - -GPT_INDEX_DIRECTORY = MISCELLANEOUS_DIRECTORY / "gpt_index/" -GPT_INDEX_VER = 0.4 - - -# Command location path to be shown in the figures depending on watermark flag -command_location = "" - - -# pylint: disable=R1702,R0912 - - -# pylint: disable=global-statement -def set_command_location(cmd_loc: str): - """Set command location. - - Parameters - ---------- - cmd_loc: str - Command location called by user - """ - if cmd_loc.split("/")[-1] == "hold": - return - global command_location # noqa - command_location = cmd_loc - - -def check_path(path: str) -> str: - """Check that path file exists. - - Parameters - ---------- - path: str - path of file - - Returns - ------- - str: - Ratio of similarity between two strings - """ - # Just return empty path because this will be handled outside this function - if not path: - return "" - if path[0] == "~": - path = path.replace("~", HOME_DIRECTORY.as_posix()) - # Return string of path if such relative path exists - if os.path.isfile(path): - return path - # Return string of path if an absolute path exists - if os.path.isfile("/" + path): - return f"/{path}" - logger.error("The path file '%s' does not exist.", path) - console.print(f"[red]The path file '{path}' does not exist.\n[/red]") - return "" - - -def parse_and_split_input(an_input: str, custom_filters: List) -> List[str]: - """Filter and split the input queue. - - Uses regex to filters command arguments that have forward slashes so that it doesn't - break the execution of the command queue. - Currently handles unix paths and sorting settings for screener menus. - - Parameters - ---------- - an_input : str - User input as string - custom_filters : List - Additional regular expressions to match - - Returns - ------- - List[str] - Command queue as list - """ - # Make sure that the user can go back to the root when doing "/" - if an_input and an_input == "/": - an_input = "home" - - # everything from ` -f ` to the next known extension - file_flag = r"(\ -f |\ --file )" - up_to = r".*?" - known_extensions = r"(\.(xlsx|csv|xls|tsv|json|yaml|ini|openbb|ipynb))" - unix_path_arg_exp = f"({file_flag}{up_to}{known_extensions})" - - # Add custom expressions to handle edge cases of individual controllers - custom_filter = "" - for exp in custom_filters: - if exp is not None: - custom_filter += f"|{exp}" - del exp - - slash_filter_exp = f"({unix_path_arg_exp}){custom_filter}" - - filter_input = True - placeholders: Dict[str, str] = {} - while filter_input: - match = re.search(pattern=slash_filter_exp, string=an_input) - if match is not None: - placeholder = f"{{placeholder{len(placeholders)+1}}}" - placeholders[placeholder] = an_input[ - match.span()[0] : match.span()[1] # noqa:E203 - ] - an_input = ( - an_input[: match.span()[0]] - + placeholder - + an_input[match.span()[1] :] # noqa:E203 - ) - else: - filter_input = False - - commands = an_input.split("/") - - for command_num, command in enumerate(commands): - if command == commands[command_num] == commands[-1] == "": - return list(filter(None, commands)) - matching_placeholders = [tag for tag in placeholders if tag in command] - if len(matching_placeholders) > 0: - for tag in matching_placeholders: - commands[command_num] = command.replace(tag, placeholders[tag]) - return commands - - -def log_and_raise(error: Union[argparse.ArgumentTypeError, ValueError]) -> None: - """Log and output an error.""" - logger.error(str(error)) - raise error - - -def similar(a: str, b: str) -> float: - """Return a similarity float between string a and string b. - - Parameters - ---------- - a: str - string a - b: str - string b - - Returns - ------- - float: - Ratio of similarity between two strings - """ - return SequenceMatcher(None, a, b).ratio() - - -def return_colored_value(value: str): - """Return the string value with green, yellow, red or white color based on - whether the number is positive, negative, zero or other, respectively. - - Parameters - ---------- - value: str - string to be checked - - Returns - ------- - value: str - string with color based on value of number if it exists - """ - values = re.findall(r"[-+]?(?:\d*\.\d+|\d+)", value) - - # Finds exactly 1 number in the string - if len(values) == 1: - if float(values[0]) > 0: - return f"[green]{value}[/green]" - - if float(values[0]) < 0: - return f"[red]{value}[/red]" - - if float(values[0]) == 0: - return f"[yellow]{value}[/yellow]" - - return f"{value}" - - -# pylint: disable=too-many-arguments -def print_rich_table( # noqa: PLR0912 - df: pd.DataFrame, - show_index: bool = False, - title: str = "", - index_name: str = "", - headers: Optional[Union[List[str], pd.Index]] = None, - floatfmt: Union[str, List[str]] = ".2f", - show_header: bool = True, - automatic_coloring: bool = False, - columns_to_auto_color: Optional[List[str]] = None, - rows_to_auto_color: Optional[List[str]] = None, - export: bool = False, - print_to_console: bool = False, - limit: Optional[int] = 1000, - source: Optional[str] = None, - columns_keep_types: Optional[List[str]] = None, -): - """Prepare a table from df in rich. - - Parameters - ---------- - df: pd.DataFrame - Dataframe to turn into table - show_index: bool - Whether to include index - title: str - Title for table - index_name : str - Title for index column - headers: List[str] - Titles for columns - floatfmt: Union[str, List[str]] - Float number formatting specs as string or list of strings. Defaults to ".2f" - show_header: bool - Whether to show the header row. - automatic_coloring: bool - Automatically color a table based on positive and negative values - columns_to_auto_color: List[str] - Columns to automatically color - rows_to_auto_color: List[str] - Rows to automatically color - export: bool - Whether we are exporting the table to a file. If so, we don't want to print it. - limit: Optional[int] - Limit the number of rows to show. - print_to_console: bool - Whether to print the table to the console. If False and interactive mode is - enabled, the table will be displayed in a new window. Otherwise, it will print to the - console. - source: Optional[str] - Source of the table. If provided, it will be displayed in the header of the table. - columns_keep_types: Optional[List[str]] - Columns to keep their types, i.e. not convert to numeric - """ - if export: - return - - current_user = get_current_user() - enable_interactive = ( - current_user.preferences.USE_INTERACTIVE_DF and plots_backend().isatty - ) - - # Make a copy of the dataframe to avoid SettingWithCopyWarning - df = df.copy() - - show_index = not isinstance(df.index, pd.RangeIndex) and show_index - # convert non-str that are not timestamp or int into str - # eg) praw.models.reddit.subreddit.Subreddit - for col in df.columns: - if columns_keep_types is not None and col in columns_keep_types: - continue - try: - if not any( - isinstance(df[col].iloc[x], pd.Timestamp) - for x in range(min(10, len(df))) - ): - df[col] = pd.to_numeric(df[col], errors="ignore") - except (ValueError, TypeError): - df[col] = df[col].astype(str) - - def _get_headers(_headers: Union[List[str], pd.Index]) -> List[str]: - """Check if headers are valid and return them.""" - output = _headers - if isinstance(_headers, pd.Index): - output = list(_headers) - if len(output) != len(df.columns): - log_and_raise( - ValueError("Length of headers does not match length of DataFrame") - ) - return output - - if enable_interactive and not print_to_console: - df_outgoing = df.copy() - # If headers are provided, use them - if headers is not None: - # We check if headers are valid - df_outgoing.columns = _get_headers(headers) - - if show_index and index_name not in df_outgoing.columns: - # If index name is provided, we use it - df_outgoing.index.name = index_name or "Index" - df_outgoing = df_outgoing.reset_index() - - for col in df_outgoing.columns: - if col == "": - df_outgoing = df_outgoing.rename(columns={col: " "}) - - plots_backend().send_table( - df_table=df_outgoing, - title=title, - source=source, # type: ignore - theme=current_user.preferences.TABLE_STYLE, - ) - return - - df = df.copy() if not limit else df.copy().iloc[:limit] - if automatic_coloring: - if columns_to_auto_color: - for col in columns_to_auto_color: - # checks whether column exists - if col in df.columns: - df[col] = df[col].apply(lambda x: return_colored_value(str(x))) - if rows_to_auto_color: - for row in rows_to_auto_color: - # checks whether row exists - if row in df.index: - df.loc[row] = df.loc[row].apply( - lambda x: return_colored_value(str(x)) - ) - - if columns_to_auto_color is None and rows_to_auto_color is None: - df = df.applymap(lambda x: return_colored_value(str(x))) - - if current_user.preferences.USE_TABULATE_DF: - table = Table(title=title, show_lines=True, show_header=show_header) - - if show_index: - table.add_column(index_name) - - if headers is not None: - headers = _get_headers(headers) - for header in headers: - table.add_column(str(header)) - else: - for column in df.columns: - table.add_column(str(column)) - - if isinstance(floatfmt, list) and len(floatfmt) != len(df.columns): - log_and_raise( - ValueError( - "Length of floatfmt list does not match length of DataFrame columns." - ) - ) - if isinstance(floatfmt, str): - floatfmt = [floatfmt for _ in range(len(df.columns))] - - for idx, values in zip(df.index.tolist(), df.values.tolist()): - # remove hour/min/sec from timestamp index - Format: YYYY-MM-DD # make better - row_idx = [str(idx)] if show_index else [] - row_idx += [ - ( - str(x) - if not isinstance(x, float) and not isinstance(x, np.float64) - else ( - f"{x:{floatfmt[idx]}}" - if isinstance(floatfmt, list) - else ( - f"{x:.2e}" - if 0 < abs(float(x)) <= 0.0001 - else f"{x:floatfmt}" - ) - ) - ) - for idx, x in enumerate(values) - ] - table.add_row(*row_idx) - console.print(table) - else: - console.print(df.to_string(col_space=0)) - - -def check_int_range(mini: int, maxi: int): - """Check if argparse argument is an int between 2 values. - - Parameters - ---------- - mini: int - Min value to compare - maxi: int - Max value to compare - - Returns - ------- - int_range_checker: - Function that compares the three integers - """ - - # Define the function with default arguments - def int_range_checker(num: int) -> int: - """Check if int is between a high and low value. - - Parameters - ---------- - num: int - Input integer - - Returns - ---------- - num: int - Input number if conditions are met - - Raises - ------ - argparse.ArgumentTypeError - Input number not between min and max values - """ - num = int(num) - if num < mini or num > maxi: - log_and_raise( - argparse.ArgumentTypeError(f"Argument must be in range [{mini},{maxi}]") - ) - return num - - # Return function handle to checking function - return int_range_checker - - -def check_non_negative(value) -> int: - """Argparse type to check non negative int.""" - new_value = int(value) - if new_value < 0: - log_and_raise(argparse.ArgumentTypeError(f"{value} is negative")) - return new_value - - -def check_terra_address_format(address: str) -> str: - """Validate that terra account address has proper format. - - Example: ^terra1[a-z0-9]{38}$ - - Parameters - ---------- - address: str - terra blockchain account address - Returns - ------- - str - Terra blockchain address or raise argparse exception - """ - pattern = re.compile(r"^terra1[a-z0-9]{38}$") - if not pattern.match(address): - log_and_raise( - argparse.ArgumentTypeError( - f"Terra address: {address} has invalid format. Valid format: ^terra1[a-z0-9]{{38}}$" - ) - ) - return address - - -def check_non_negative_float(value) -> float: - """Argparse type to check non negative int.""" - new_value = float(value) - if new_value < 0: - log_and_raise(argparse.ArgumentTypeError(f"{value} is negative")) - return new_value - - -def check_positive_list(value) -> List[int]: - """Argparse type to return list of positive ints.""" - list_of_nums = value.split(",") - list_of_pos = [] - for a_value in list_of_nums: - new_value = int(a_value) - if new_value <= 0: - log_and_raise( - argparse.ArgumentTypeError(f"{value} is an invalid positive int value") - ) - list_of_pos.append(new_value) - return list_of_pos - - -def check_positive_float_list(value) -> List[float]: - """Argparse type to return list of positive floats.""" - list_of_nums = value.split(",") - list_of_pos = [] - for a_value in list_of_nums: - new_value = float(a_value) - if new_value <= 0: - log_and_raise( - argparse.ArgumentTypeError(f"{value} is an invalid positive int value") - ) - list_of_pos.append(new_value) - return list_of_pos - - -def check_positive(value) -> int: - """Argparse type to check positive int.""" - new_value = int(value) - if new_value <= 0: - log_and_raise( - argparse.ArgumentTypeError(f"{value} is an invalid positive int value") - ) - return new_value - - -def check_indicators(string: str) -> List[str]: - """Check if indicators are valid.""" - ta_cls = PlotlyTA() - choices = sorted( - [c.name.replace("plot_", "") for c in ta_cls if c.name != "plot_ma"] - + ta_cls.ma_mode - ) - choices_print = ( - f"{'`, `'.join(choices[:10])}`\n `{'`, `'.join(choices[10:20])}" - f"`\n `{'`, `'.join(choices[20:])}" - ) - - strings = string.split(",") - for s in strings: - if s not in choices: - raise argparse.ArgumentTypeError( - f"\nInvalid choice: {s}, choose from \n `{choices_print}`", - ) - return strings - - -def check_indicator_parameters(args: str, _help: bool = False) -> str: - """Check if indicators parameters are valid.""" - ta_cls = PlotlyTA() - indicators_dict: dict = {} - - regex = re.compile(r"([a-zA-Z]+)\[([0-9.,]*)\]") - no_params_regex = re.compile(r"([a-zA-Z]+)") - - matches = regex.findall(args) - no_params_matches = no_params_regex.findall(args) - - indicators = [m[0] for m in matches] - for match in no_params_matches: - if match not in indicators: - matches.append((match, "")) - - if _help: - console.print( - """[yellow]To pass custom parameters to indicators:[/] - - [green]Example: - -i macd[12,26,9],rsi[14],sma[20,50] - -i macd,rsi,sma (uses default parameters) - - [yellow]Would pass the following to the indicators:[/] - [green]macd=dict(fast=12, slow=26, signal=9) - rsi=dict(length=14) - sma=dict(length=[20,50]) - - They must be in the same order as the function parameters.[/]\n""" - ) - - pop_keys = ["close", "high", "low", "open", "open_", "volume", "talib", "return"] - if matches: - check_indicators(",".join([m[0] for m in matches])) - for match in matches: - indicator, args = match - - indicators_dict.setdefault(indicator, {}) - if indicator in ["fib", "srlines", "demark", "clenow"]: - if _help: - console.print( - f"[yellow]{indicator}:[/]\n{'':^4}[green]Parameters: None[/]" - ) - continue - - fullspec = inspect.getfullargspec(getattr(ta, indicator)) - kwargs = list(set(fullspec.args) - set(pop_keys)) - kwargs.sort(key=fullspec.args.index) - - if _help: - console.print( - f"[yellow]{indicator}:[/]\n{'':^4}[green]Parameters: {', '.join(kwargs)}[/]" - ) - - if indicator in ta_cls.ma_mode: - indicators_dict[indicator]["length"] = check_positive_list(args) - continue - - for i, arg in enumerate(args.split(",")): - if arg and len(kwargs) > i: - indicators_dict[indicator][kwargs[i]] = ( - float(arg) if "." in arg else int(arg) - ) - return json.dumps(indicators_dict) - - if not matches: - raise argparse.ArgumentTypeError( - f"Invalid indicator arguments: {args}. \n Example: -i macd[12,26,9],rsi[14]" - ) - return args - - -def check_positive_float(value) -> float: - """Argparse type to check positive float.""" - new_value = float(value) - if new_value <= 0: - log_and_raise( - argparse.ArgumentTypeError(f"{value} is not a positive float value") - ) - return new_value - - -def check_percentage_range(num) -> float: - """Check if float is between 0 and 100. If so, return it. - - Parameters - ---------- - num: float - Input float - - Returns - ------- - num: float - Input number if conditions are met - - Raises - ------ - argparse.ArgumentTypeError - Input number not between min and max values - """ - num = float(num) - maxi = 100.0 - mini = 0.0 - if num <= mini or num >= maxi: - log_and_raise(argparse.ArgumentTypeError("Value must be between 0 and 100")) - return num - - -def check_proportion_range(num) -> float: - """Check if float is between 0 and 1. If so, return it. - - Parameters - ---------- - num: float - Input float - Returns - ------- - num: float - Input number if conditions are met - Raises - ---------- - argparse.ArgumentTypeError - Input number not between min and max values - """ - num = float(num) - maxi = 1.0 - mini = 0.0 - if num < mini or num > maxi: - log_and_raise(argparse.ArgumentTypeError("Value must be between 0 and 1")) - return num - - -def valid_date_in_past(s: str) -> datetime: - """Argparse type to check date is in valid format.""" - try: - delta = datetime.now() - datetime.strptime(s, "%Y-%m-%d") - if delta.days < 1: - log_and_raise( - argparse.ArgumentTypeError( - f"Not a valid date: {s}. Must be earlier than today" - ) - ) - return datetime.strptime(s, "%Y-%m-%d") - except ValueError as value_error: - logging.exception(str(value_error)) - raise argparse.ArgumentTypeError(f"Not a valid date: {s}") from value_error - - -def check_list_dates(str_dates: str) -> List[datetime]: - """Argparse type to check list of dates provided have a valid format. - - Parameters - ---------- - str_dates: str - string with dates separated by "," - - Returns - ------- - list_dates: List[datetime] - List of valid dates - """ - list_dates = list() - if str_dates: - if "," in str_dates: - for dt_marker in str_dates.split(","): - list_dates.append(valid_date(dt_marker)) - else: - list_dates.append(valid_date(str_dates)) - - return list_dates - - -def valid_date(s: str) -> datetime: - """Argparse type to check date is in valid format.""" - try: - return datetime.strptime(s, "%Y-%m-%d") - except ValueError as value_error: - logging.exception(str(value_error)) - raise argparse.ArgumentTypeError(f"Not a valid date: {s}") from value_error - - -def is_valid_date(s: str) -> bool: - """Check if date is in valid format.""" - try: - datetime.strptime(s, "%Y-%m-%d") - return True - except ValueError: - return False - - -def valid_repo(repo: str) -> str: - """Argparse type to check github repo is in valid format.""" - result = re.search(r"^[a-zA-Z0-9-_.]+\/[a-zA-Z0-9-_.]+$", repo) # noqa: W605 - if not result: - log_and_raise( - argparse.ArgumentTypeError( - f"{repo} is not a valid repo. Valid repo: org/repo" - ) - ) - return repo - - -def valid_hour(hr: str) -> int: - """Argparse type to check hour is valid with 24-hour notation.""" - new_hr = int(hr) - - if (new_hr < 0) or (new_hr > 24): - log_and_raise( - argparse.ArgumentTypeError(f"{hr} doesn't follow 24-hour notion.") - ) - return new_hr - - -def lower_str(string: str) -> str: - """Convert string to lowercase.""" - return string.lower() - - -def us_market_holidays(years) -> list: - """Get US market holidays.""" - if isinstance(years, int): - years = [ - years, - ] - # https://www.nyse.com/markets/hours-calendars - market_holidays = [ - "Martin Luther King Jr. Day", - "Washington's Birthday", - "Memorial Day", - "Independence Day", - "Labor Day", - "Thanksgiving", - "Christmas Day", - ] - # http://www.maa.clell.de/StarDate/publ_holidays.html - good_fridays = { - 2010: "2010-04-02", - 2011: "2011-04-22", - 2012: "2012-04-06", - 2013: "2013-03-29", - 2014: "2014-04-18", - 2015: "2015-04-03", - 2016: "2016-03-25", - 2017: "2017-04-14", - 2018: "2018-03-30", - 2019: "2019-04-19", - 2020: "2020-04-10", - 2021: "2021-04-02", - 2022: "2022-04-15", - 2023: "2023-04-07", - 2024: "2024-03-29", - 2025: "2025-04-18", - 2026: "2026-04-03", - 2027: "2027-03-26", - 2028: "2028-04-14", - 2029: "2029-03-30", - 2030: "2030-04-19", - } - market_and_observed_holidays = market_holidays + [ - holiday + " (Observed)" for holiday in market_holidays - ] - all_holidays = us_holidays(years=years) - valid_holidays = [ - date - for date in list(all_holidays) - if all_holidays[date] in market_and_observed_holidays - ] - - for year in years: - new_Year = datetime.strptime(f"{year}-01-01", "%Y-%m-%d") - if new_Year.weekday() != 5: # ignore saturday - valid_holidays.append(new_Year.date()) - if new_Year.weekday() == 6: # add monday for Sunday - valid_holidays.append(new_Year.date() + timedelta(1)) - for year in years: - valid_holidays.append(datetime.strptime(good_fridays[year], "%Y-%m-%d").date()) - return valid_holidays - - -def lambda_long_number_format(num, round_decimal=3) -> Union[str, int, float]: - """Format a long number.""" - if get_current_user().preferences.USE_INTERACTIVE_DF: - return num - - if num == float("inf"): - return "inf" - - if isinstance(num, float): - magnitude = 0 - while abs(num) >= 1000: - magnitude += 1 - num /= 1000.0 - - string_fmt = f".{round_decimal}f" - - num_str = int(num) if num.is_integer() else f"{num:{string_fmt}}" - - return f"{num_str} {' KMBTP'[magnitude]}".strip() - if isinstance(num, int): - num = str(num) - if ( - isinstance(num, str) - and num.lstrip("-").isdigit() - and not num.lstrip("-").startswith("0") - and not is_valid_date(num) - ): - num = int(num) - num /= 1.0 - magnitude = 0 - while abs(num) >= 1000: - magnitude += 1 - num /= 1000.0 - - string_fmt = f".{round_decimal}f" - num_str = int(num) if num.is_integer() else f"{num:{string_fmt}}" - - return f"{num_str} {' KMBTP'[magnitude]}".strip() - return num - - -def revert_lambda_long_number_format(num_str: str) -> Union[float, str]: - """ - Revert the formatting of a long number if the input is a formatted number, otherwise return the input as is. - - Parameters - ---------- - num_str : str - The number to remove the formatting. - - Returns - ------- - Union[float, str] - The number as float (with no formatting) or the input as is. - - """ - magnitude_dict = { - "K": 1000, - "M": 1000000, - "B": 1000000000, - "T": 1000000000000, - "P": 1000000000000000, - } - - # Ensure the input is a string and not empty - if not num_str or not isinstance(num_str, str): - return num_str - - num_as_list = num_str.strip().split() - - # If the input string is a number parse it as float - if ( - len(num_as_list) == 1 - and num_as_list[0].replace(".", "").replace("-", "").isdigit() - and not is_valid_date(num_str) - ): - return float(num_str) - - # If the input string is a formatted number with magnitude - if ( - len(num_as_list) == 2 - and num_as_list[1] in magnitude_dict - and num_as_list[0].replace(".", "").replace("-", "").isdigit() - ): - num, unit = num_as_list - magnitude = magnitude_dict.get(unit) - if magnitude: - return float(num) * magnitude - - # Return the input string as is if it's not a formatted number - return num_str - - -def lambda_long_number_format_y_axis(df, y_column, ax): - """Format long number that goes onto Y axis.""" - max_values = df[y_column].values.max() - - magnitude = 0 - while abs(max_values) >= 1000: - magnitude += 1 - max_values /= 1000.0 - - magnitude_sym = " KMBTP"[magnitude] - - # Second y label axis - - if magnitude_sym == " ": - ax[2].set_ylabel(f"{y_column}") - else: - ax[2].set_ylabel(f"{y_column} [{magnitude_sym}]") - - divider_map = {" ": 1, "K": 1000, "M": 1000000, "B": 1000000000} - divider = divider_map[magnitude_sym] - - ax[2].get_yaxis().set_major_formatter( - matplotlib.ticker.FuncFormatter(lambda x, _: int(x / divider)) - ) - - -def lambda_clean_data_values_to_float(val: str) -> float: - """Clean data to float based on string ending.""" - # Remove any leading or trailing parentheses and spaces - val = val.strip("( )") - if val == "-": - val = "0" - - # Convert percentage to decimal - if val.endswith("%"): - return float(val[:-1]) / 100.0 - if val.endswith("B"): - return float(val[:-1]) * 1_000_000_000 - if val.endswith("M"): - return float(val[:-1]) * 1_000_000 - if val.endswith("K"): - return float(val[:-1]) * 1000 - return float(val) - - -def lambda_int_or_round_float(x) -> str: - """Format int or round float.""" - # If the data is inf, -inf, or NaN then simply return '~' because it is either too - # large, too small, or we do not have data to display for it - if x in (np.inf, -np.inf, np.nan): - return " " + "~" - if (x - int(x) < -sys.float_info.epsilon) or (x - int(x) > sys.float_info.epsilon): - return " " + str(round(x, 2)) - - return " " + str(int(x)) - - -def divide_chunks(data, n): - """Split into chunks.""" - # looping till length of data - for i in range(0, len(data), n): - yield data[i : i + n] # noqa: E203 - - -def get_next_stock_market_days(last_stock_day, n_next_days) -> list: - """Get the next stock market day. - - Checks against weekends and holidays. - """ - n_days = 0 - l_pred_days = [] - years: list = [] - holidays: list = [] - if isinstance(last_stock_day, datetime): - while n_days < n_next_days: - last_stock_day += timedelta(hours=24) - year = last_stock_day.date().year - if year not in years: - years.append(year) - holidays += us_market_holidays(year) - # Check if it is a weekend - if last_stock_day.date().weekday() > 4: - continue - # Check if it is a holiday - if last_stock_day.strftime("%Y-%m-%d") in holidays: - continue - # Otherwise stock market is open - n_days += 1 - l_pred_days.append(last_stock_day) - else: - while n_days < n_next_days: - l_pred_days.append(last_stock_day + 1 + n_days) - n_days += 1 - - return l_pred_days - - -def is_intraday(df: pd.DataFrame) -> bool: - """Check if the data granularity is intraday. - - Parameters - ---------- - df : pd.DataFrame - Price data - - Returns - ------- - bool - True if data is intraday - """ - granularity = df.index[1] - df.index[0] - intraday = not granularity >= timedelta(days=1) - return intraday - - -def reindex_dates(df: pd.DataFrame) -> pd.DataFrame: - """Reindex dataframe to exclude non-trading days. - - Resets the index of a df to an integer and prepares the 'date' column to become - x tick labels on a plot. - - Parameters - ---------- - df : pd.DataFrame - Source dataframe - - Returns - ------- - pd.DataFrame - Reindexed dataframe - """ - date_format = "%b %d %H:%M" if is_intraday(df) else "%Y-%m-%d" - reindexed_df = df.reset_index() - reindexed_df["date"] = reindexed_df["date"].dt.strftime(date_format) - return reindexed_df - - -def get_data(tweet): - """Get twitter data from API request.""" - if "+" in tweet["created_at"]: - s_datetime = tweet["created_at"].split(" +")[0] - else: - s_datetime = iso8601.parse_date(tweet["created_at"]).strftime( - "%Y-%m-%d %H:%M:%S" - ) - - s_text = tweet["full_text"] if "full_text" in tweet else tweet["text"] - return {"created_at": s_datetime, "text": s_text} - - -def clean_tweet(tweet: str, symbol: str) -> str: - """Clean tweets to be fed to sentiment model.""" - whitespace = re.compile(r"\s+") - web_address = re.compile(r"(?i)http(s):\/\/[a-z0-9.~_\-\/]+") - ticker = re.compile(rf"(?i)@{symbol}(?=\b)") - user = re.compile(r"(?i)@[a-z0-9_]+") - - tweet = whitespace.sub(" ", tweet) - tweet = web_address.sub("", tweet) - tweet = ticker.sub(symbol, tweet) - tweet = user.sub("", tweet) - - return tweet - - -def get_user_agent() -> str: - """Get a not very random user agent.""" - user_agent_strings = [ - "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.10; rv:86.1) Gecko/20100101 Firefox/86.1", - "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:86.1) Gecko/20100101 Firefox/86.1", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:82.1) Gecko/20100101 Firefox/82.1", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:86.0) Gecko/20100101 Firefox/86.0", - "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:86.0) Gecko/20100101 Firefox/86.0", - "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.10; rv:83.0) Gecko/20100101 Firefox/83.0", - "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:84.0) Gecko/20100101 Firefox/84.0", - ] - - return random.choice(user_agent_strings) # nosec # noqa: S311 - - -def text_adjustment_init(self): - """Adjust text monkey patch for Pandas.""" - self.ansi_regx = re.compile(r"\x1B[@-_][0-?]*[ -/]*[@-~]") - self.encoding = get_option("display.encoding") - - -def text_adjustment_len(self, text): - """Get the length of the text adjustment.""" - # return compat.strlen(self.ansi_regx.sub("", text), encoding=self.encoding) - return len(self.ansi_regx.sub("", text)) - - -def text_adjustment_justify(self, texts, max_len, mode="right"): - """Apply 'Justify' text alignment.""" - justify = ( - str.ljust - if (mode == "left") - else str.rjust if (mode == "right") else str.center - ) - out = [] - for s in texts: - escapes = self.ansi_regx.findall(s) - if len(escapes) == 2: - out.append( - escapes[0].strip() - + justify(self.ansi_regx.sub("", s), max_len) - + escapes[1].strip() - ) - else: - out.append(justify(s, max_len)) - return out - - -# pylint: disable=unused-argument -def text_adjustment_join_unicode(self, lines, sep=""): - """Join Unicode.""" - try: - return sep.join(lines) - except UnicodeDecodeError: - # sep = compat.text_type(sep) - return sep.join([x.decode("utf-8") if isinstance(x, str) else x for x in lines]) - - -# pylint: disable=unused-argument -def text_adjustment_adjoin(self, space, *lists, **kwargs): - """Join text.""" - # Add space for all but the last column: - pads = ([space] * (len(lists) - 1)) + [0] - max_col_len = max(len(col) for col in lists) - new_cols = [] - for col, pad in zip(lists, pads): - width = max(self.len(s) for s in col) + pad - c = self.justify(col, width, mode="left") - # Add blank cells to end of col if needed for different col lens: - if len(col) < max_col_len: - c.extend([" " * width] * (max_col_len - len(col))) - new_cols.append(c) - - rows = [self.join_unicode(row_tup) for row_tup in zip(*new_cols)] - return self.join_unicode(rows, sep="\n") - - -# https://github.com/pandas-dev/pandas/issues/18066#issuecomment-522192922 -def patch_pandas_text_adjustment(): - """Set pandas text adjustment settings.""" - pandas.io.formats.format.TextAdjustment.__init__ = text_adjustment_init - pandas.io.formats.format.TextAdjustment.len = text_adjustment_len - pandas.io.formats.format.TextAdjustment.justify = text_adjustment_justify - pandas.io.formats.format.TextAdjustment.join_unicode = text_adjustment_join_unicode - pandas.io.formats.format.TextAdjustment.adjoin = text_adjustment_adjoin - - -def lambda_financials_colored_values(val: str) -> str: - """Add a color to a value.""" - - # We don't want to do the color stuff in interactive mode - if get_current_user().preferences.USE_INTERACTIVE_DF: - return val - - if val == "N/A" or str(val) == "nan": - val = "[yellow]N/A[/yellow]" - elif sum(c.isalpha() for c in val) < 2: - if "%" in val and "-" in val or "%" not in val and "(" in val: - val = f"[red]{val}[/red]" - elif "%" in val: - val = f"[green]{val}[/green]" - return val - - -def check_ohlc(type_ohlc: str) -> str: - """Check that data is in ohlc.""" - if bool(re.match("^[ohlca]+$", type_ohlc)): - return type_ohlc - raise argparse.ArgumentTypeError("The type specified is not recognized") - - -def lett_to_num(word: str) -> str: - """Match ohlca to integers.""" - replacements = [("o", "1"), ("h", "2"), ("l", "3"), ("c", "4"), ("a", "5")] - for a, b in replacements: - word = word.replace(a, b) - return word - - -AVAILABLE_FLAIRS = { - ":openbb": "(🦋)", - ":bug": "(🐛)", - ":rocket": "(🚀)", - ":diamond": "(💎)", - ":stars": "(✨)", - ":baseball": "(⚾)", - ":boat": "(⛵)", - ":phone": "(☎)", - ":mercury": "(☿)", - ":hidden": "", - ":sun": "(☼)", - ":moon": "(☾)", - ":nuke": "(☢)", - ":hazard": "(☣)", - ":tunder": "(☈)", - ":king": "(♔)", - ":queen": "(♕)", - ":knight": "(♘)", - ":recycle": "(♻)", - ":scales": "(⚖)", - ":ball": "(⚽)", - ":golf": "(⛳)", - ":piece": "(☮)", - ":yy": "(☯)", -} - - -def get_flair() -> str: - """Get a flair icon.""" - - current_user = get_current_user() # pylint: disable=redefined-outer-name - current_flair = str(current_user.preferences.FLAIR) - flair = AVAILABLE_FLAIRS.get(current_flair, current_flair) - - if ( - current_user.preferences.USE_DATETIME - and get_user_timezone_or_invalid() != "INVALID" - ): - dtime = datetime.now(pytz.timezone(get_user_timezone())).strftime( - "%Y %b %d, %H:%M" - ) - - # if there is no flair, don't add an extra space after the time - if flair == "": - return f"{dtime}" - - return f"{dtime} {flair}" - - return flair - - -def is_timezone_valid(user_tz: str) -> bool: - """Check whether user timezone is valid. - - Parameters - ---------- - user_tz: str - Timezone to check for validity - - Returns - ------- - bool - True if timezone provided is valid - """ - return user_tz in pytz.all_timezones - - -def get_user_timezone() -> str: - """Get user timezone if it is a valid one. - - Returns - ------- - str - user timezone based on .env file - """ - return get_current_user().preferences.TIMEZONE - - -def get_user_timezone_or_invalid() -> str: - """Get user timezone if it is a valid one. - - Returns - ------- - str - user timezone based on timezone.openbb file or INVALID - """ - user_tz = get_user_timezone() - if is_timezone_valid(user_tz): - return f"{user_tz}" - return "INVALID" - - -def str_to_bool(value) -> bool: - """Match a string to a boolean value.""" - if isinstance(value, bool): - return value - if value.lower() in {"false", "f", "0", "no", "n"}: - return False - if value.lower() in {"true", "t", "1", "yes", "y"}: - return True - raise ValueError(f"{value} is not a valid boolean value") - - -def get_screeninfo(): - """Get screeninfo.""" - try: - screens = get_monitors() # Get all available monitors - except Exception: - return None - - if screens: - current_user = get_current_user() - if ( - len(screens) - 1 < current_user.preferences.MONITOR - ): # Check to see if chosen monitor is detected - monitor = 0 - console.print( - f"Could not locate monitor {current_user.preferences.MONITOR}, using primary monitor." - ) - else: - monitor = current_user.preferences.MONITOR - main_screen = screens[monitor] # Choose what monitor to get - - return (main_screen.width, main_screen.height) - - return None - - -def plot_autoscale(): - """Autoscale plot.""" - current_user = get_current_user() - screen_info = get_screeninfo() - if current_user.preferences.USE_PLOT_AUTOSCALING and screen_info: - x, y = screen_info # Get screen size - # account for ultrawide monitors - if x / y > 1.5: - x = x * 0.4 - - x = ((x) * current_user.preferences.PLOT_WIDTH_PERCENTAGE * 10**-2) / ( - current_user.preferences.PLOT_DPI - ) # Calculate width - if current_user.preferences.PLOT_HEIGHT_PERCENTAGE == 100: # If full height - y = y - 60 # Remove the height of window toolbar - y = ((y) * current_user.preferences.PLOT_HEIGHT_PERCENTAGE * 10**-2) / ( - current_user.preferences.PLOT_DPI - ) - else: # If not autoscale, use size defined in config_plot.py - x = current_user.preferences.PLOT_WIDTH / (current_user.preferences.PLOT_DPI) - y = current_user.preferences.PLOT_HEIGHT / (current_user.preferences.PLOT_DPI) - return x, y - - -def get_last_time_market_was_open(dt): - """Get last time the US market was open.""" - # Check if it is a weekend - if dt.date().weekday() > 4: - dt = get_last_time_market_was_open(dt - timedelta(hours=24)) - - # Check if it is a holiday - if dt.strftime("%Y-%m-%d") in us_holidays(): - dt = get_last_time_market_was_open(dt - timedelta(hours=24)) - - dt = dt.replace(hour=21, minute=0, second=0) - - return dt - - -def check_file_type_saved(valid_types: Optional[List[str]] = None): - """Provide valid types for the user to be able to select. - - Parameters - ---------- - valid_types: List[str] - List of valid types to export data - - Returns - ------- - check_filenames: Optional[List[str]] - Function that returns list of filenames to export data - """ - - def check_filenames(filenames: str = "") -> str: - """Check if filenames are valid. - - Parameters - ---------- - filenames: str - filenames to be saved separated with comma - - Returns - ---------- - str - valid filenames separated with comma - """ - if not filenames or not valid_types: - return "" - valid_filenames = list() - for filename in filenames.split(","): - if filename.endswith(tuple(valid_types)): - valid_filenames.append(filename) - else: - console.print( - f"[red]Filename '{filename}' provided is not valid!\nPlease use one of the following file types:" - f"{','.join(valid_types)}[/red]\n" - ) - return ",".join(valid_filenames) - - return check_filenames - - -def compose_export_path(func_name: str, dir_path: str) -> Path: - """Compose export path for data from the terminal. - - Creates a path to a folder and a filename based on conditions. - - Parameters - ---------- - func_name : str - Name of the command that invokes this function - dir_path : str - Path of directory from where this function is called - - Returns - ------- - Path - Path variable containing the path of the exported file - """ - now = datetime.now() - # Resolving all symlinks and also normalizing path. - resolve_path = Path(dir_path).resolve() - # Getting the directory names from the path. Instead of using split/replace (Windows doesn't like that) - # check if this is done in a main context to avoid saving with openbb_terminal - if resolve_path.parts[-2] == "openbb_terminal": - path_cmd = f"{resolve_path.parts[-1]}" - else: - path_cmd = f"{resolve_path.parts[-2]}_{resolve_path.parts[-1]}" - - default_filename = f"{now.strftime('%Y%m%d_%H%M%S')}_{path_cmd}_{func_name}" - - full_path = get_current_user().preferences.USER_EXPORTS_DIRECTORY / default_filename - - return full_path - - -def ask_file_overwrite(file_path: Path) -> Tuple[bool, bool]: - """Helper to provide a prompt for overwriting existing files. - - Returns two values, the first is a boolean indicating if the file exists and the - second is a boolean indicating if the user wants to overwrite the file. - """ - # Jeroen asked for a flag to overwrite no matter what - current_user = get_current_user() - if current_user.preferences.FILE_OVERWRITE: - return False, True - if get_current_system().TEST_MODE: - return False, True - if file_path.exists(): - overwrite = input("\nFile already exists. Overwrite? [y/n]: ").lower() - if overwrite == "y": - file_path.unlink(missing_ok=True) - # File exists and user wants to overwrite - return True, True - # File exists and user does not want to overwrite - return True, False - # File does not exist - return False, True - - -def save_to_excel(df, saved_path, sheet_name, start_row=0, index=True, header=True): - """Saves a Pandas DataFrame to an Excel file. - - Args: - df: A Pandas DataFrame. - saved_path: The path to the Excel file to save to. - sheet_name: The name of the sheet to save the DataFrame to. - start_row: The row number to start writing the DataFrame at. - index: Whether to write the DataFrame index to the Excel file. - header: Whether to write the DataFrame header to the Excel file. - """ - - overwrite_options = { - "o": "replace", - "a": "overlay", - "n": "new", - } - - if not saved_path.exists(): - with pd.ExcelWriter(saved_path, engine="openpyxl") as writer: - df.to_excel(writer, sheet_name=sheet_name, index=index, header=header) - - else: - with pd.ExcelFile(saved_path) as reader: - overwrite_option = "n" - if sheet_name in reader.sheet_names: - overwrite_option = input( - "\nSheet already exists. Overwrite/Append/New? [o/a/n]: " - ).lower() - start_row = 0 - if overwrite_option == "a": - existing_df = pd.read_excel(saved_path, sheet_name=sheet_name) - start_row = existing_df.shape[0] + 1 - - with pd.ExcelWriter( - saved_path, - mode="a", - if_sheet_exists=overwrite_options[overwrite_option], - engine="openpyxl", - ) as writer: - df.to_excel( - writer, - sheet_name=sheet_name, - startrow=start_row, - index=index, - header=False if overwrite_option == "a" else header, - ) - - -# This is a false positive on pylint and being tracked in pylint #3060 -# pylint: disable=abstract-class-instantiated -def export_data( - export_type: str, - dir_path: str, - func_name: str, - df: pd.DataFrame = pd.DataFrame(), - sheet_name: Optional[str] = None, - figure: Optional[OpenBBFigure] = None, - margin: bool = True, -) -> None: - """Export data to a file. - - Parameters - ---------- - export_type : str - Type of export between: csv,json,xlsx,xls - dir_path : str - Path of directory from where this function is called - func_name : str - Name of the command that invokes this function - df : pd.Dataframe - Dataframe of data to save - sheet_name : str - If provided. The name of the sheet to save in excel file - figure : Optional[OpenBBFigure] - Figure object to save as image file - margin : bool - Automatically adjust subplot parameters to give specified padding. - """ - - if export_type: - saved_path = compose_export_path(func_name, dir_path).resolve() - saved_path.parent.mkdir(parents=True, exist_ok=True) - for exp_type in export_type.split(","): - # In this scenario the path was provided, e.g. --export pt.csv, pt.jpg - if "." in exp_type: - saved_path = saved_path.with_name(exp_type) - # In this scenario we use the default filename - else: - if ".OpenBB_openbb_terminal" in saved_path.name: - saved_path = saved_path.with_name( - saved_path.name.replace( - ".OpenBB_openbb_terminal", "OpenBBTerminal" - ) - ) - saved_path = saved_path.with_suffix(f".{exp_type}") - - exists, overwrite = False, False - is_xlsx = exp_type.endswith("xlsx") - if sheet_name is None and is_xlsx or not is_xlsx: - exists, overwrite = ask_file_overwrite(saved_path) - - if exists and not overwrite: - existing = len(list(saved_path.parent.glob(saved_path.stem + "*"))) - saved_path = saved_path.with_stem(f"{saved_path.stem}_{existing + 1}") - - df = df.replace( - { - r"\[yellow\]": "", - r"\[/yellow\]": "", - r"\[green\]": "", - r"\[/green\]": "", - r"\[red\]": "", - r"\[/red\]": "", - r"\[magenta\]": "", - r"\[/magenta\]": "", - }, - regex=True, - ) - - df = df.applymap(revert_lambda_long_number_format) - - if exp_type.endswith("csv"): - df.to_csv(saved_path) - elif exp_type.endswith("json"): - df.reset_index(drop=True, inplace=True) - df.to_json(saved_path) - elif exp_type.endswith("xlsx"): - # since xlsx does not support datetimes with timezones we need to remove it - df = remove_timezone_from_dataframe(df) - - if sheet_name is None: # noqa: SIM223 - df.to_excel( - saved_path, - index=True, - header=True, - ) - else: - save_to_excel(df, saved_path, sheet_name) - - elif saved_path.suffix in [".jpg", ".pdf", ".png", ".svg"]: - if figure is None: - console.print("No plot to export.") - continue - figure.show(export_image=saved_path, margin=margin) - else: - console.print("Wrong export file specified.") - continue - - console.print(f"Saved file: {saved_path}") - - if figure is not None: - figure._exported = True # pylint: disable=protected-access - - -def get_rf() -> float: - """Use the fiscaldata.gov API to get most recent T-Bill rate. - - Returns - ------- - rate : float - The current US T-Bill rate - """ - try: - base = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service" - end = "/v2/accounting/od/avg_interest_rates" - filters = "?filter=security_desc:eq:Treasury Bills&sort=-record_date" - response = request(base + end + filters) - latest = response.json()["data"][0] - return round(float(latest["avg_interest_rate_amt"]) / 100, 8) - except Exception: - return 0.02 - - -def system_clear(): - """Clear screen.""" - os.system("cls||clear") # nosec # noqa: S605,S607 - - -def excel_columns() -> List[str]: - """Return potential columns for excel. - - Returns - ------- - letters : List[str] - Letters to be used as excel columns - """ - letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"] - letters += ["N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] - - opts = ( - [f"{x}" for x in letters] - + [f"{x}{y}" for x in letters for y in letters] - + [f"{x}{y}{z}" for x in letters for y in letters for z in letters] - ) - return opts - - -def handle_error_code(requests_obj, error_code_map): - """Handle error code of HTTP requests. - - Parameters - ---------- - requests_obj: Object - Request object - error_code_map: Dict - Dictionary mapping of HTTP error code and output message - - """ - for error_code, error_msg in error_code_map.items(): - if requests_obj.status_code == error_code: - console.print(error_msg) - - -def prefill_form(ticket_type, menu, path, command, message): - """Pre-fill Google Form and open it in the browser.""" - form_url = "https://my.openbb.co/app/terminal/support?" - - params = { - "type": ticket_type, - "menu": menu, - "path": path, - "command": command, - "message": message, - } - - url_params = urllib.parse.urlencode(params) - - webbrowser.open(form_url + url_params) - - -def get_closing_price(ticker, days): - """Get historical close price for n days in past for market asset. - - Parameters - ---------- - ticker : str - Ticker to get data for - days : datetime - No. of days in past - - Returns - ------- - data : pd.DataFrame - Historic close prices for ticker for given days - """ - tick = yf.Ticker(ticker) - df = tick.history( - start=d.today() - timedelta(days=days), - interval="1d", - )["Close"] - df = df.to_frame().reset_index() - df = df.rename(columns={0: "Close"}) - df.index.name = "index" - return df - - -def camel_case_split(string: str) -> str: - """Convert a camel-case string to separate words. - - Parameters - ---------- - string : str - The string to be converted - - Returns - ------- - new_string: str - The formatted string - """ - words = [[string[0]]] - - for c in string[1:]: - if words[-1][-1].islower() and c.isupper(): - words.append(list(c)) - else: - words[-1].append(c) - - results = ["".join(word) for word in words] - return " ".join(results).title() - - -def is_valid_axes_count( - axes: List[plt.Axes], - n: int, - custom_text: Optional[str] = None, - prefix_text: Optional[str] = None, - suffix_text: Optional[str] = None, -): - """Check if axes list length is equal to n and log text if check result is false. - - Parameters - ---------- - axes: List[plt.Axes] - External axes (2 axes are expected in the list) - n: int - number of expected axes - custom_text: Optional[str] = None - custom text to log - prefix_text: Optional[str] = None - prefix text to add before text to log - suffix_text: Optional[str] = None - suffix text to add after text to log - """ - if len(axes) == n: - return True - - print_text = ( - custom_text - if custom_text - else f"Expected list of {n} axis item{'s' if n > 1 else ''}." - ) - - if prefix_text: - print_text = f"{prefix_text} {print_text}" - if suffix_text: - print_text = f"{suffix_text} {print_text}" - - logger.error(print_text) - console.print(f"[red]{print_text}\n[/red]") - return False - - -def support_message(s: str) -> str: - """Argparse type to check string is in valid format for the support command.""" - return s.replace('"', "") - - -def check_list_values(valid_values: List[str]): - """Get valid values to test arguments given by user. - - Parameters - ---------- - valid_values: List[str] - List of valid values to be checked - - Returns - ------- - check_list_values_from_valid_values_list: - Function that ensures that the valid values go through and notifies user when value is not valid. - """ - - # Define the function with default arguments - def check_list_values_from_valid_values_list(given_values: str) -> List[str]: - """Check if argparse argument is an str format. - - Ensure that value1,value2,value3 and that the values value1, value2 and value3 are valid. - - Parameters - ---------- - given_values: str - values provided by the user - - Raises - ------ - argparse.ArgumentTypeError - Input number not between min and max values - """ - success_values = list() - - values_found = ( - [val.strip() for val in given_values.split(",")] - if "," in given_values - else [given_values] - ) - - for value in values_found: - # check if the value is valid - if value in valid_values: - success_values.append(value) - else: - console.print(f"[red]'{value}' is not valid.[/red]") - - if not success_values: - log_and_raise( - argparse.ArgumentTypeError("No correct arguments have been found") - ) - return success_values - - # Return function handle to checking function - return check_list_values_from_valid_values_list - - -def search_wikipedia(expression: str) -> None: - """Search wikipedia for a given expression. - - Parameters - ---------- - expression: str - Expression to search for - """ - url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{expression}" - - response = requests.request("GET", url, headers={}, data={}) - - if response.status_code == 200: - response_json = json.loads(response.text) - res = { - "title": response_json["title"], - "url": f"{response_json['content_urls']['desktop']['page']}", - "summary": response_json["extract"], - } - else: - res = { - "title": "[red]Not Found[/red]", - } - - df = pd.json_normalize(res) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"Wikipedia results for {expression}", - ) - - -def screenshot() -> None: - """Screenshot the terminal window or the plot window. - - Parameters - ---------- - terminal_window_target: bool - Target the terminal window - """ - try: - if plt.get_fignums(): - img_buf = io.BytesIO() - plt.savefig(img_buf, format="png") - shot = Image.open(img_buf) - screenshot_to_canvas(shot, plot_exists=True) - - else: - console.print("No plots found.\n") - - except Exception as err: - console.print(f"Cannot reach window - {err}\n") - - -def screenshot_to_canvas(shot, plot_exists: bool = False): - """Frame image to OpenBB canvas. - - Parameters - ---------- - shot - Image to frame with OpenBB Canvas - plot_exists: bool - Variable to say whether the image is a plot or screenshot of terminal - """ - WHITE_LINE_WIDTH = 3 - OUTSIDE_CANVAS_WIDTH = shot.width + 4 * WHITE_LINE_WIDTH + 5 - OUTSIDE_CANVAS_HEIGHT = shot.height + 4 * WHITE_LINE_WIDTH + 5 - UPPER_SPACE = 40 - BACKGROUND_WIDTH_SLACK = 150 - BACKGROUND_HEIGHT_SLACK = 150 - - background = Image.open( - Path(os.path.abspath(__file__), "../../images/background.png") - ) - logo = Image.open( - Path(os.path.abspath(__file__), "../../images/openbb_horizontal_logo.png") - ) - - try: - if plot_exists: - HEADER_HEIGHT = 0 - RADIUS = 8 - - background = background.resize( - ( - shot.width + BACKGROUND_WIDTH_SLACK, - shot.height + BACKGROUND_HEIGHT_SLACK, - ) - ) - - x = int((background.width - OUTSIDE_CANVAS_WIDTH) / 2) - y = UPPER_SPACE - - white_shape = ( - (x, y), - (x + OUTSIDE_CANVAS_WIDTH, y + OUTSIDE_CANVAS_HEIGHT), - ) - img = ImageDraw.Draw(background) - img.rounded_rectangle( - white_shape, - fill="black", - outline="white", - width=WHITE_LINE_WIDTH, - radius=RADIUS, - ) - background.paste(shot, (x + WHITE_LINE_WIDTH + 5, y + WHITE_LINE_WIDTH + 5)) - - # Logo - background.paste( - logo, - ( - int((background.width - logo.width) / 2), - UPPER_SPACE - + OUTSIDE_CANVAS_HEIGHT - + HEADER_HEIGHT - + int( - ( - background.height - - UPPER_SPACE - - OUTSIDE_CANVAS_HEIGHT - - HEADER_HEIGHT - - logo.height - ) - / 2 - ), - ), - logo, - ) - - background.show(title="screenshot") - - except Exception: - console.print("Shot failed.") - - -@lru_cache -def load_json(path: Path) -> Dict[str, str]: - """Load a dictionary from a json file path. - - Parameter - ---------- - path : Path - The path for the json file - - Returns - ------- - Dict[str, str] - The dictionary loaded from json - """ - try: - with open(path) as file: - return json.load(file) - except Exception as e: - console.print( - f"[red]Failed to load preferred source from file: " - f"{get_current_user().preferences.USER_DATA_SOURCES_FILE}[/red]" - ) - console.print(f"[red]{e}[/red]") - return {} - - -def list_from_str(value: str) -> List[str]: - """Convert a string to a list. - - Parameter - --------- - value : str - The string to convert - - Returns - ------- - new_value: List[str] - The list of strings - """ - if value: - return value.split(",") - return [] - - -def str_date_to_timestamp(date: str) -> int: - """Transform string date to timestamp - - Parameters - ---------- - start_date : str - Initial date, format YYYY-MM-DD - - Returns - ------- - date_ts : int - Initial date timestamp (e.g., 1_614_556_800) - """ - - date_ts = int( - datetime.strptime(date + " 00:00:00+0000", "%Y-%m-%d %H:%M:%S%z").timestamp() - ) - - return date_ts - - -def check_start_less_than_end(start_date: str, end_date: str) -> bool: - """Check if start_date is equal to end_date. - - Parameters - ---------- - start_date : str - Initial date, format YYYY-MM-DD - end_date : str - Final date, format YYYY-MM-DD - - Returns - ------- - bool - True if start_date is not equal to end_date, False otherwise - """ - if start_date is None or end_date is None: - return False - if start_date == end_date: - console.print("[red]Start date and end date cannot be the same.[/red]") - return True - if start_date > end_date: - console.print("[red]Start date cannot be greater than end date.[/red]") - return True - return False - - -# Write an abstract helper to make requests from a url with potential headers and params -def request( - url: str, method: str = "get", timeout: int = 0, **kwargs -) -> requests.Response: - """Abstract helper to make requests from a url with potential headers and params. - - Parameters - ---------- - url : str - Url to make the request to - method : str - HTTP method to use. Choose from: - delete, get, head, patch, post, put, by default "get" - timeout : int - How many seconds to wait for the server to send data - - Returns - ------- - requests.Response - Request response object - - Raises - ------ - ValueError - If invalid method is passed - """ - method = method.lower() - if method not in ["delete", "get", "head", "patch", "post", "put"]: - raise ValueError(f"Invalid method: {method}") - current_user = get_current_user() - # We want to add a user agent to the request, so check if there are any headers - # If there are headers, check if there is a user agent, if not add one. - # Some requests seem to work only with a specific user agent, so we want to be able to override it. - headers = kwargs.pop("headers", {}) - timeout = timeout or current_user.preferences.REQUEST_TIMEOUT - - if "User-Agent" not in headers: - headers["User-Agent"] = get_user_agent() - func = getattr(requests, method) - return func( - url, - headers=headers, - timeout=timeout, - **kwargs, - ) - - -def remove_timezone_from_dataframe(df: pd.DataFrame) -> pd.DataFrame: - """ - Remove timezone information from a dataframe. - - Parameters - ---------- - df : pd.DataFrame - The dataframe to remove timezone information from - - Returns - ------- - pd.DataFrame - The dataframe with timezone information removed - """ - - date_cols = [] - index_is_date = False - - # Find columns and index containing date data - if ( - df.index.dtype.kind == "M" - and hasattr(df.index.dtype, "tz") - and df.index.dtype.tz is not None - ): - index_is_date = True - - for col, dtype in df.dtypes.items(): - if dtype.kind == "M" and hasattr(df.index.dtype, "tz") and dtype.tz is not None: - date_cols.append(col) - - # Remove the timezone information - for col in date_cols: - df[col] = df[col].dt.date - - if index_is_date: - index_name = df.index.name - df.index = df.index.date - df.index.name = index_name - - return df - - -def check_valid_date(date_string) -> bool: - """ "Helper to see if we can parse the string to a date""" - try: - # Try to parse the string with strptime() - datetime.strptime( - date_string, "%Y-%m-%d" - ) # Use the format your dates are expected to be in - return True # If it can be parsed, then it is a valid date string - except ValueError: # strptime() throws a ValueError if the string can't be parsed - return False diff --git a/openbb_terminal/helpers_denomination.py b/openbb_terminal/helpers_denomination.py deleted file mode 100644 index 2516eb2e83d..00000000000 --- a/openbb_terminal/helpers_denomination.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Denomination Helper functions""" - -from typing import Callable, Dict, Literal, Optional, Tuple - -import pandas as pd -from pandas._typing import Axis - -DENOMINATION = Literal[ - "Trillions", "Billions", "Millions", "Tens of thousands", "Thousands", "Units", "" -] - - -def transform( - df: pd.DataFrame, - sourceDenomination: DENOMINATION = "Units", - targetDenomination: Optional[DENOMINATION] = None, - maxValue: Optional[float] = None, - axis: Axis = 0, - skipPredicate: Optional[Callable[[pd.Series], bool]] = None, -) -> Tuple[pd.DataFrame, DENOMINATION]: - """Transforms data frame by denomination. - - Args: - df (pd.DataFrame): Source data frame - sourceDenomination (DENOMINATION, optional): Current denomination. Defaults to Units. - targetDenomination (DENOMINATION, optional): Desired denomination. Defaults to None, meaning we will find it. - maxValue (float, optional): Max value of the data frame. Defaults to None, meaning df.max().max() will be used. - axis (Axis, optional): Axis to apply to skip predicate. Defaults to 0. - skipPredicate (Callable[[pd.Series], bool], optional): Predicate for skipping a transform. - Returns: - pd.DataFrame - Tuple[pd.DataFrame, DENOMINATION]: Pair of transformed data frame and applied denomination. - """ - - def apply( - df: pd.DataFrame, source: DENOMINATION, target: DENOMINATION - ) -> pd.DataFrame: - multiplier = get_denominations()[source] / get_denominations()[target] - - df[df.select_dtypes(include=["number"]).columns] = df.select_dtypes( - include=["number"] - ).apply(pd.to_numeric, downcast="float") - - return df.apply( - lambda series: ( - series - if skipPredicate is not None and skipPredicate(series) - else series * multiplier if isinstance(series, (float, int)) else series - ), - axis, - ) - - if targetDenomination is not None: - return ( - apply(df, sourceDenomination, targetDenomination), - targetDenomination, - ) - - if maxValue is None: - maxValue = df.max(numeric_only=True).max(numeric_only=True) - - foundTargetDenomination = get_denomination(maxValue) - - return ( - apply(df, sourceDenomination, foundTargetDenomination[0]), - foundTargetDenomination[0], - ) - - -def get_denominations() -> Dict[DENOMINATION, float]: - """Gets all supported denominations and their lower bound value - - Returns: - Dict[DENOMINATION, int]: All supported denominations and their lower bound value - """ - return { - "Trillions": 1_000_000_000_000, - "Billions": 1_000_000_000, - "Millions": 1_000_000, - "Tens of thousands": 10_000, - "Thousands": 1_000, - "Units": 1, - } - - -def get_denomination(value: float) -> Tuple[DENOMINATION, float]: - """Gets denomination that fits the supplied value. - If no denomination found, 'Units' is returned. - - Args: - value (int): Value - - Returns: - Tuple[DENOMINATION, int]:Denomination that fits the supplied value - """ - return next((x for x in get_denominations().items() if value >= x[1]), ("Units", 1)) diff --git a/openbb_terminal/intro.json b/openbb_terminal/intro.json deleted file mode 100644 index 8b3c70f323d..00000000000 --- a/openbb_terminal/intro.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "prompts": [ - { - "header": "Welcome to the OpenBB Terminal.", - "content": [ - "\nThe following walkthrough will guide you towards making the most out of the OpenBB Terminal.\n\n", - "Press [param]Enter[/param] to continue or [param]'q'[/param] followed by [param]Enter[/param] to exit." - ] - }, - { - "header": "#1 - Commands vs menu.", - "content": [ - "\nMenus are a collection of 'commands' and 'sub-menus'.\n", - "You can identify them through their distinct color and a '>' at the beginning of the line\n\n", - "For instance:\n", - "[menu]> stocks access historical pricing data, options, sector [/menu]", - "[menu]and industry, and overall due diligence [/menu]\n\n", - "Commands are expected to return data either as a chart or table.\n", - "You can identify them through their distinct color\n\n", - "For instance:\n", - "[cmds] news display news articles based on term and data sources [/cmds]" - ] - }, - { - "header": "#2 - Using commands", - "content": [ - "\nCommands throughout the terminal can have additional arguments.\n\n", - "Let's say that in the current menu, you want to have more information about the command [param]'news'[/param]. \n\n", - "You can either see the available arguments in the terminal, using: [param]news -h[/param]\n\n", - "or you can find out more about it with an output example on the browser, using: [param]about news[/param]" - ] - }, - { - "header": "#3 - Setting API Keys", - "content": [ - "\nThe OpenBB Terminal does not own any of the data you have access to.\n\n", - "Instead, we provide the infrastructure to access over 100 different data sources ", - "from a single location.\n\n", - "Thus, it is necessary for each user to set their own API keys for the various third party sources\n\n", - "You can find more about this on OpenBB Hub at: [param]https://my.openbb.co/app/terminal/data-sources[/param] ", - "or go into the '[param]/keys[/param]' menu.\n\n", - "For many commands, there are multiple data sources that can be selected.\n\n", - "The help menu shows the data sources supported by each command.\n\n", - "For instance:\n", - "[cmds] load load a specific stock ticker and additional info for analysis [/cmds]", - "[src][YahooFinance, AlphaVantage, Polygon, EODHD] [/src]\n\n", - "This shows that [src]YahooFinance[/src] is default data source but [src]AlphaVantage, Polygon, EODHD[/src] are also supported.\n\n", - "If you want to try a different data source you could do '[param]load MSFT --source Polygon[/param]'\n\n", - "If you are interested in changing the default permanently, you can do it through OpenBB Hub at ", - "[param]https://my.openbb.co/app/terminal/data-sources[/param] ", - "or go into the '[param]/sources[/param]' menu and select their preferred default data source" - ] - }, - { - "header": "#4 - Symbol dependent menus and commands", - "content": [ - "\nThroughout the terminal, you will see commands and menus greyed out.\n\n", - "These menus or commands cannot be accessed until an object is loaded.\n\n", - "Let's take as an example the '[param]/stocks/ta[/param]' menu.\n\n", - "You will see that the command '[param]/stocks/ta/load[/param]' is available as you can access it to load a ticker:\n", - "[cmds]load load a specific stock ticker for analysis [YahooFinance, AlphaVantage, Polygon, EODHD][/cmds]\n\n", - "On the other hand, '[param]/stocks/ta/ema[/param]' cmd to perform exponential moving average requires a ticker to be loaded.\n\n", - "And therefore, appears as:\n", - "[dim]ema exponential moving average[/dim]\n\n", - "Once a ticker is loaded with: [param]load TSLA[/param]\n\n", - "The '[param]ema[/param]' command will be available as:\n", - "[cmds]ema exponential moving average [/cmds]" - ] - }, - { - "header": "#5 - Terminal Navigation", - "content": [ - "\nThe terminal has a tree like structure, where menus branch off into new menus.\n\n", - "The users current location is displayed before the text prompt.\n\n", - "For instance, if the user is inside the menu disc which is inside stocks, the following prompt ", - "will appear: \n2022 Oct 18, 21:53 (\ud83e\udd8b) [param]/stocks/disc/[/param] $\n\n", - "If the user wants to go back to the menu above, all they need to do is type '[param]q[/param]'.\n\n", - "If the user wants to go back to the home of the terminal, they can type '[param]/[/param]' instead.\n\n", - "Note: Always type '[param]h[/param]' to know what commands are available in each menu" - ] - }, - { - "header": "#6 - Command Pipeline", - "content": [ - "\nThe terminal offers the capability of allowing users to speed up their ", - "navigation and command execution.", - "\n\nTherefore, typing the following prompt is valid:\n", - "2022 Oct 18, 21:53 (\ud83e\udd8b) / $ [param]stocks/load TSLA/dd/pt[/param]\n\n", - "In this example, the terminal - in a single action - will go into '[param]stocks[/param]' menu, ", - "run command '[param]load[/param]' with '[param]TSLA[/param]' as input, \n", - "go into sub-menu '[param]dd[/param]' (due diligence) and run the command ", - "'[param]pt[/param]' (price target)." - ] - }, - { - "header": "#7 - OpenBB Scripts", - "content": [ - "\nThe command pipeline capability is nice but the user experience wasn't great having to copy-paste large ", - "lists of commands.\n\n", - "We allow the user to create a text file of the form [param]FOLDER_PATH/my_script.openbb[/param] with following content:\n\n", - "stocks\nload TSLA\ndd\npt\n\n", - "which can be run through the '[param]exe[/param]' command in the home menu, with:\n", - "2022 Oct 18, 22:33 (\ud83e\udd8b) / $ [param]exe FOLDER_PATH/my_script.openbb[/param]\n\n" - ] - }, - { - "header": "#8 - OpenBB Scripts with Arguments", - "content": [ - "\nThe user can create a script that includes arguments for the commands.\n\n", - "For instance, [param]FOLDER_PATH/my_script_with_variable_input.openbb[/param] may contain:\n\n", - "stocks\n# this is a comment\nload $ARGV[0]\ndd\npt\nq\nload $ARGV[1]\ncandle\n\n", - "and then, if this script is run with:\n", - "2022 Oct 18, 22:33 (\ud83e\udd8b) / $ [param]exe FOLDER_PATH/my_script_with_variable_input.openbb ", - "-i AAPL,MSFT[/param]\n\n", - "This means that the [param]pt[/param] will run on [param]AAPL[/param] while ", - "[param]candle[/param] on [param]MSFT[/param]" - ] - }, - { - "header": "#9 - OpenBB Script Generation", - "content": [ - "\nTo make it easier for users to create scripts, we have created a ", - "command that 'records' user commands ", - "directly into a script.\n\n", - "From the home menu, the user can run:\n", - "2022 Oct 18, 22:33 (\ud83e\udd8b) / $ [param]record[/param]\n\n", - "and then perform your typical investment research workflow before entering\n\n", - "2022 Oct 18, 22:33 (\ud83e\udd8b) / $ [param]stop[/param]\n\n", - "After stopping, the script will be saved to the 'scripts' folder." - ] - }, - { - "header": "#10 - Terminal Customization", - "content": [ - "\nUsers should explore the [param]settings[/param] and [param]featflags[/param] menus ", - "to configure their terminal.\n\n", - "The fact that our terminal is fully open source allows users to be able to customize ", - "anything they want.\n\n", - "If you are interested in contributing to the project, please check:\n", - "[param]https://github.com/OpenBB-finance/OpenBBTerminal[/param]" - ] - }, - { - "header": "#11 - Support", - "content": [ - "\nWe are nothing without our community, hence we put a lot of effort in being here for you.\n\n", - "If you find any bug that you wish to report to improve the terminal you can do so with:\n", - "2022 Oct 18, 22:33 (\ud83e\udd8b) / $ [param]support CMD[/param]\n\n", - "which should open a form in your browser where you can report the bug in said 'CMD'.\n\n", - "If you want to know more, or have any further question. Please join us on Discord: ", - "[param]https://openbb.co/discord[/param]" - ] - } - ] -} diff --git a/openbb_terminal/keys_controller.py b/openbb_terminal/keys_controller.py deleted file mode 100644 index 9d103aa6bff..00000000000 --- a/openbb_terminal/keys_controller.py +++ /dev/null @@ -1,1131 +0,0 @@ -"""Keys Controller Module""" - -__docformat__ = "numpy" - -# pylint: disable=too-many-lines - -import argparse -import logging -from typing import Callable, Dict, List, Optional - -from openbb_terminal import keys_model, keys_view -from openbb_terminal.core.session.constants import BackendEnvironment -from openbb_terminal.core.session.current_user import get_current_user, is_local -from openbb_terminal.core.session.env_handler import get_reading_order -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import EXPORT_ONLY_RAW_DATA_ALLOWED -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import ( - MenuText, - console, - optional_rich_track, - translate, -) - -logger = logging.getLogger(__name__) - - -class KeysController(BaseController): # pylint: disable=too-many-public-methods - """Keys Controller class""" - - API_DICT = keys_model.API_DICT - API_LIST = list(API_DICT.keys()) - CHOICES_COMMANDS: List[str] = ["mykeys", "status"] + API_LIST - PATH = "/keys/" - status_dict: Dict = {} - help_status_text: str = "" - - def __init__( - self, - queue: Optional[List[str]] = None, - menu_usage: bool = True, - ): - """Constructor""" - super().__init__(queue) - if menu_usage and session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} - - choices["support"] = self.SUPPORT_CHOICES - - self.completer = NestedCompleter.from_nested_dict(choices) - - def get_check_keys_function(self, key: str) -> Callable: - """Get check `key` function from keys_model""" - return getattr(keys_model, f"check_{key}_key") - - def check_keys_status(self, reevaluate: bool = False) -> None: - """Check keys status""" - if not self.status_dict or reevaluate: - for api in optional_rich_track(self.API_LIST, desc="Checking keys status"): - try: - self.status_dict[api] = self.get_check_keys_function(api)() - except Exception: - self.status_dict[api] = str( - keys_model.KeyStatus.DEFINED_TEST_INCONCLUSIVE - ) - - def get_source_hierarchy(self) -> str: - """Hierarchy is equivalent to reverse order of reading .env files. - - Returns - ------- - str - Source priority string. - """ - reading_order = get_reading_order() - hierarchy = "\n ".join(list(map(str, reversed(reading_order)))) - return ( - hierarchy - if is_local() - else f"{BackendEnvironment.HUB_URL + 'app/terminal/api-keys'}\n {hierarchy}" - ) - - def print_help(self, update_status: bool = True, reevaluate: bool = True): - """Print help""" - self.check_keys_status(reevaluate=reevaluate) - mt = MenuText("keys/") - mt.add_param( - "_source", - self.get_source_hierarchy(), - ) - mt.add_raw("\n") - mt.add_info("_keys_") - mt.add_raw("\n") - mt.add_cmd("mykeys") - mt.add_cmd("status") - mt.add_raw("\n") - mt.add_info("_status_") - - status_color_map = { - keys_model.KeyStatus.DEFINED_TEST_PASSED.value: "green", - keys_model.KeyStatus.DEFINED_TEST_FAILED.value: "red", - keys_model.KeyStatus.DEFINED_NOT_TESTED.value: "yellow", - keys_model.KeyStatus.DEFINED_TEST_INCONCLUSIVE.value: "yellow", - keys_model.KeyStatus.NOT_DEFINED.value: "grey30", - } - - if not self.help_status_text or update_status: - for cmd_name, status_msg in self.status_dict.items(): - api_name = self.API_DICT[cmd_name] - - c = status_color_map.get(status_msg, "grey30") - clean_status_msg = status_msg or keys_model.KeyStatus.NOT_DEFINED.value - - mt.add_raw( - f" [cmds]{cmd_name}[/cmds] {(20 - len(cmd_name)) * ' '}" - f" [{c}] {api_name} {(25 - len(api_name)) * ' '} {translate(clean_status_msg)} [/{c}]\n" - ) - self.help_status_text = mt.menu_text - - console.print(text=self.help_status_text, menu="Keys") - - @log_start_end(log=logger) - def call_mykeys(self, other_args: List[str]): - """Display current keys""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mykeys", - description="Display current keys.", - ) - parser.add_argument( - "-s", - "--show", - type=bool, - dest="show", - help="See keys in raw text instead of the default protected display.", - default=False, - ) - if other_args and "-s" in other_args[0]: - other_args.insert(1, "True") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - keys_view.display_keys( - show=ns_parser.show, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_status(self, other_args: List[str]): - """Display current keys status with the option to force the check""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="status", - description="Reevaluate keys status.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="Force the status to reevaluate for a specific key.", - default=None, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - is_key = bool(ns_parser and ns_parser.key) - - if is_key: - if ns_parser.key in self.API_LIST: - try: - status = self.get_check_keys_function(ns_parser.key)(True) - self.status_dict[ns_parser.key] = status - console.print("") - except Exception: - status = keys_model.KeyStatus.DEFINED_TEST_INCONCLUSIVE - self.status_dict[ns_parser.key] = str(status) - console.print(f"{status.colorize()}\n") - else: - console.print(f"[red]Key `{ns_parser.key}` is not a valid key.[/red]") - return - - if "-h" not in other_args and "--help" not in other_args: - self.print_help(update_status=True, reevaluate=not is_key) - - @log_start_end(log=logger) - def call_av(self, other_args: List[str]): - """Process av command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="av", - description="Set Alpha Vantage API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://www.alphavantage.co/support/#api-key" - ) - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["av"] = keys_model.set_av_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_fmp(self, other_args: List[str]): - """Process fmp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fmp", - description="Set Financial Modeling Prep API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://financialmodelingprep.com") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["fmp"] = keys_model.set_fmp_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_quandl(self, other_args: List[str]): - """Process quandl command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="quandl", - description="Set Quandl API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://www.quandl.com") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["quandl"] = keys_model.set_quandl_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_polygon(self, other_args: List[str]): - """Process polygon command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="polygon", - description="Set Polygon API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://polygon.io") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["polygon"] = keys_model.set_polygon_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_intrinio(self, other_args: List[str]): - """Process polygon command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="intrinio", - description="Set Intrinio API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, sign up for a subscription: https://intrinio.com/starter-plan\n" - ) - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["intrinio"] = keys_model.set_intrinio_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_fred(self, other_args: List[str]): - """Process FRED command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fred", - description="Set FRED API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://fred.stlouisfed.org") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["fred"] = keys_model.set_fred_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_news(self, other_args: List[str]): - """Process News API command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="news", - description="Set News API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://newsapi.org") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["news"] = keys_model.set_news_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_biztoc(self, other_args: List[str]): - """Process BizToc API command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="news", - description="Set BizToc API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://api.biztoc.com") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["biztoc"] = keys_model.set_biztoc_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_tradier(self, other_args: List[str]): - """Process Tradier API command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tradier", - description="Set Tradier API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://developer.tradier.com") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["tradier"] = keys_model.set_tradier_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_cmc(self, other_args: List[str]): - """Process CoinMarketCap API command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cmc", - description="Set CMC API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://coinmarketcap.com") - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["cmc"] = keys_model.set_cmc_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_finnhub(self, other_args: List[str]): - """Process Finnhub API command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="finnhub", - description="Set Finnhub API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://finnhub.io") - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["finnhub"] = keys_model.set_finnhub_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_reddit(self, other_args: List[str]): - """Process reddit command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="reddit", - description="Set Reddit API key.", - ) - parser.add_argument( - "-i", - "--id", - type=str, - dest="client_id", - help="Client ID", - required="-h" not in other_args and "--help" not in other_args, - ) - parser.add_argument( - "-s", - "--secret", - type=str, - dest="client_secret", - help="Client Secret", - required="-h" not in other_args and "--help" not in other_args, - ) - parser.add_argument( - "-u", - "--username", - type=str, - dest="username", - help="Username", - required="-h" not in other_args and "--help" not in other_args, - ) - parser.add_argument( - "-p", - "--password", - type=str, - dest="password", - help="Password", - required="-h" not in other_args and "--help" not in other_args, - ) - parser.add_argument( - "-a", - "--agent", - type=str, - dest="user_agent", - help="User agent", - required="-h" not in other_args and "--help" not in other_args, - nargs="+", - ) - if not other_args: - console.print("For your API Key, visit: https://www.reddit.com") - return - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - slash_components = "".join([f"/{val}" for val in self.queue]) - useragent = " ".join(ns_parser.user_agent) + " " + slash_components - useragent = useragent.replace('"', "") - self.queue = [] - - self.status_dict["reddit"] = keys_model.set_reddit_key( - client_id=ns_parser.client_id, - client_secret=ns_parser.client_secret, - password=ns_parser.password, - username=ns_parser.username, - useragent=useragent, - persist=True, - show_output=True, - ) - - @log_start_end(log=logger) - def call_binance(self, other_args: List[str]): - """Process binance command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="binance", - description="Set Binance API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="Key", - required="-h" not in other_args and "--help" not in other_args, - ) - parser.add_argument( - "-s", - "--secret", - type=str, - dest="secret", - help="Secret key", - required="-h" not in other_args and "--help" not in other_args, - ) - if not other_args: - console.print("For your API Key, visit: https://binance.com") - return - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["binance"] = keys_model.set_binance_key( - key=ns_parser.key, - secret=ns_parser.secret, - persist=True, - show_output=True, - ) - - @log_start_end(log=logger) - def call_bitquery(self, other_args: List[str]): - """Process bitquery command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bitquery", - description="Set Bitquery API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://bitquery.io/") - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["bitquery"] = keys_model.set_bitquery_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_coinbase(self, other_args: List[str]): - """Process coinbase command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="coinbase", - description="Set Coinbase API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="Key", - required="-h" not in other_args and "--help" not in other_args, - ) - parser.add_argument( - "-s", - "--secret", - type=str, - dest="secret", - help="Secret key", - required="-h" not in other_args and "--help" not in other_args, - ) - parser.add_argument( - "-p", - "--passphrase", - type=str, - dest="passphrase", - help="Passphrase", - required="-h" not in other_args and "--help" not in other_args, - ) - if not other_args: - console.print("For your API Key, visit: https://docs.pro.coinbase.com/") - return - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["coinbase"] = keys_model.set_coinbase_key( - key=ns_parser.key, - secret=ns_parser.secret, - passphrase=ns_parser.passphrase, - persist=True, - show_output=True, - ) - - @log_start_end(log=logger) - def call_walert(self, other_args: List[str]): - """Process walert command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="walert", - description="Set Whale Alert API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://docs.whale-alert.io/") - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["walert"] = keys_model.set_walert_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_glassnode(self, other_args: List[str]): - """Process glassnode command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="glassnode", - description="Set Glassnode API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://docs.glassnode.com/basic-api/api-key#how-to-get-an-api-key/" - ) - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["glassnode"] = keys_model.set_glassnode_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_coinglass(self, other_args: List[str]): - """Process coinglass command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="coinglass", - description="Set Coinglass API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://coinglass.github.io/API-Reference/#api-key" - ) - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["coinglass"] = keys_model.set_coinglass_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_cpanic(self, other_args: List[str]): - """Process cpanic command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cpanic", - description="Set Crypto Panic API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://cryptopanic.com/developers/api/" - ) - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["cpanic"] = keys_model.set_cpanic_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_ethplorer(self, other_args: List[str]): - """Process ethplorer command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ethplorer", - description="Set Ethplorer API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API" - ) - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["ethplorer"] = keys_model.set_ethplorer_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_smartstake(self, other_args: List[str]): - """Process smartstake command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="smartstake", - description="Set Smartstake Key and Token.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="Key", - required="-h" not in other_args and "--help" not in other_args, - ) - parser.add_argument( - "-t", - "--token", - type=str, - dest="token", - help="Token", - required="-h" not in other_args and "--help" not in other_args, - ) - if not other_args: - console.print("For your API Key, visit: https://www.smartstake.io") - return - ns_parser = self.parse_simple_args(parser, other_args) - - if ns_parser: - self.status_dict["smartstake"] = keys_model.set_smartstake_key( - key=ns_parser.key, - access_token=ns_parser.token, - persist=True, - show_output=True, - ) - - @log_start_end(log=logger) - def call_github(self, other_args: List[str]): - """Process github command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="github", - description="Set GitHub API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://docs.github.com/en/rest/guides/getting-started-with-the-rest-api" - ) - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["github"] = keys_model.set_github_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_messari(self, other_args: List[str]): - """Process messari command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="messari", - description="Set Messari API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://messari.io/api/docs") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["messari"] = keys_model.set_messari_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_eodhd(self, other_args: List[str]): - """Process eodhd command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="eodhd", - description="Set End of Day Historical Data API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://eodhistoricaldata.com/r/?ref=869U7F4J" - ) - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["eodhd"] = keys_model.set_eodhd_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_santiment(self, other_args: List[str]): - """Process santiment command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="santiment", - description="Set Santiment API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: " - "https://academy.santiment.net/products-and-plans/create-an-api-key" - ) - return - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["santiment"] = keys_model.set_santiment_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_tokenterminal(self, other_args: List[str]): - """Process tokenterminal command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tokenterminal", - description="Set Token Terminal API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://tokenterminal.com/") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["tokenterminal"] = keys_model.set_tokenterminal_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_databento(self, other_args: List[str]): - """Process databento command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="databento", - description="Set DataBento API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, https://databento.com") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["databento"] = keys_model.set_databento_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_dappradar(self, other_args: List[str]): - """Process dappradar command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dappradar", - description="Set DappRadar API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print("For your API Key, visit: https://dappradar.com/api") - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["dappradar"] = keys_model.set_dappradar_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_companieshouse(self, other_args: List[str]): - """Process companies house command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="companieshouse", - description="Set Companies House API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://developer.company-information.service.gov.uk/overview" - ) - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["companieshouse"] = keys_model.set_companieshouse_key( - key=ns_parser.key, persist=True, show_output=True - ) - - @log_start_end(log=logger) - def call_nixtla(self, other_args: List[str]): - """Process nixtla command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="nixtla", - description="Set Nixtla API key.", - ) - parser.add_argument( - "-k", - "--key", - type=str, - dest="key", - help="key", - ) - if not other_args: - console.print( - "For your API Key, visit: https://docs.nixtla.io/docs/getting-started" - ) - return - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-k") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.status_dict["nixtla"] = keys_model.set_nixtla_key( - key=ns_parser.key, persist=True, show_output=True - ) diff --git a/openbb_terminal/keys_model.py b/openbb_terminal/keys_model.py deleted file mode 100644 index dfac547e0ae..00000000000 --- a/openbb_terminal/keys_model.py +++ /dev/null @@ -1,2489 +0,0 @@ -"""Keys model""" - -__docformat__ = "numpy" - -# pylint: disable=too-many-lines - -import logging -import sys -from datetime import date -from enum import Enum -from typing import Dict, List, Union - -import binance -import pandas as pd -import praw -import quandl -from alpha_vantage.timeseries import TimeSeries -from coinmarketcapapi import CoinMarketCapAPI -from nixtlats import TimeGPT -from prawcore.exceptions import ResponseException -from tokenterminal import TokenTerminal - -from openbb_terminal.core.session.current_user import get_current_user, set_credential -from openbb_terminal.core.session.env_handler import write_to_dotenv -from openbb_terminal.cryptocurrency.coinbase_helpers import ( - CoinbaseApiException, - CoinbaseProAuth, - make_coinbase_request, -) -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console -from openbb_terminal.terminal_helper import suppress_stdout - -logger = logging.getLogger(__name__) - -# README PLEASE: -# The API_DICT keys must match the set and check functions format. -# -# This format is used by the KeysController and get_keys_info(). -# E.g. tokenterminal -> set_tokenterminal_key & check_tokenterminal_key -# -# Don't forget to add it to the SDK. -# E.g. `keys.av,keys_model.set_av_key` -# For more info, please refer to the CONTRIBUTING.md file. - -API_DICT: Dict = { - "av": "ALPHA_VANTAGE", - "fmp": "FINANCIAL_MODELING_PREP", - "quandl": "QUANDL", - "polygon": "POLYGON", - "intrinio": "INTRINIO", - "databento": "DATABENTO", - "ultima": "ULTIMA", - "fred": "FRED", - "news": "NEWSAPI", - "biztoc": "BIZTOC", - "tradier": "TRADIER", - "cmc": "COINMARKETCAP", - "finnhub": "FINNHUB", - "reddit": "REDDIT", - "binance": "BINANCE", - "bitquery": "BITQUERY", - "coinbase": "COINBASE", - "walert": "WHALE_ALERT", - "glassnode": "GLASSNODE", - "coinglass": "COINGLASS", - "cpanic": "CRYPTO_PANIC", - "ethplorer": "ETHPLORER", - "smartstake": "SMARTSTAKE", - "github": "GITHUB", - "messari": "MESSARI", - "eodhd": "EODHD", - "santiment": "SANTIMENT", - "tokenterminal": "TOKEN_TERMINAL", - "dappradar": "DAPPRADAR", - "companieshouse": "COMPANIES_HOUSE", - "nixtla": "NIXTLA", -} - -# sorting api key section by name -API_DICT = dict(sorted(API_DICT.items())) - - -class KeyStatus(str, Enum): - """Class to handle status messages and colors""" - - DEFINED_TEST_FAILED = "Defined, test failed. Data may still be accessible." - NOT_DEFINED = "Not defined" - DEFINED_TEST_PASSED = "Defined, test passed" - DEFINED_TEST_INCONCLUSIVE = "Defined, test inconclusive" - DEFINED_NOT_TESTED = "Defined, not tested" - - def __str__(self): - return self.value - - def colorize(self): - c = "" - if self.name == self.DEFINED_TEST_FAILED.name: - c = "red" - elif self.name == self.NOT_DEFINED.name: - c = "grey30" - elif self.name == self.DEFINED_TEST_PASSED.name: - c = "green" - elif self.name in [ - self.DEFINED_TEST_INCONCLUSIVE.name, - self.DEFINED_NOT_TESTED.name, - ]: - c = "yellow" - - return f"[{c}]{self.value}[/{c}]" - - -def set_keys( - keys_dict: Dict[str, Dict[str, Union[str, bool]]], - persist: bool = False, - show_output: bool = False, -) -> Dict: - """Set API keys in bundle. - - Parameters - ---------- - keys_dict: Dict[str, Dict[str, Union[str, bool]]] - More info on the required inputs for each API can be found on `keys.get_keys_info()` - persist: bool - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - Dict - Status of each key set. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "fred": {"key": "XXXXX"}, - "binance": {"key": "YYYYY", "secret": "ZZZZZ"}, - } - >>> openbb.keys.set_keys(keys_dict=d) - """ - - status_dict = {} - - for api, kwargs in keys_dict.items(): - expected_args_dict = get_keys_info() - - if api in expected_args_dict: - received_kwargs_list = list(kwargs.keys()) - expected_kwargs_list = expected_args_dict[api] - - if received_kwargs_list == expected_kwargs_list: - kwargs["persist"] = persist - kwargs["show_output"] = show_output - status_dict[api] = str( - getattr(sys.modules[__name__], "set_" + str(api) + "_key")(**kwargs) - ) - else: - console.print( - f"[red]'{api}' kwargs: {received_kwargs_list} don't match expected: {expected_kwargs_list}.[/red]" - ) - else: - console.print( - f"[red]API '{api}' was not recognized. Please check get_keys_info().[/red]" - ) - - return status_dict - - -def get_keys_info() -> Dict[str, List[str]]: - """Get info on available APIs to use in set_keys. - - Returns - ------- - Dict[str, List[str]] - Dictionary of expected API keys and arguments - """ - args_dict = {} - - for api in API_DICT: - arg_list = list( - getattr( - sys.modules[__name__], "set_" + str(api) + "_key" - ).__code__.co_varnames - ) - arg_list.remove("persist") - arg_list.remove("show_output") - args_dict[api] = arg_list - - return args_dict - - -def get_keys(show: bool = False) -> pd.DataFrame: - """Get currently set API keys. - - Parameters - ---------- - show: bool, optional - Flag to choose whether to show actual keys or not. - By default, False. - - Returns - ------- - pd.DataFrame - Currents keys - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.mykeys() - Key - API - BITQUERY_KEY ******* - CMC_KEY ******* - COINGLASS_KEY ******* - """ - - current_user = get_current_user() - current_keys = {} - - for k, _ in current_user.credentials.get_fields().items(): - field_value = current_user.credentials.get_value(field=k) - if field_value and field_value != "REPLACE_ME": - current_keys[k] = field_value - - if current_keys: - df = pd.DataFrame.from_dict(current_keys, orient="index") - df.index.name = "API" - df = df.rename(columns={0: "Key"}) - if show: - return df - df.loc[:, "Key"] = "*******" - return df - - return pd.DataFrame() - - -def handle_credential(name: str, value: str, persist: bool = False): - """Handle credential: set it for current user and optionally write to .env file. - - Parameters - ---------- - name: str - Name of credential - value: str - Value of credential - persist: bool, optional - Write to .env file. By default, False. - """ - set_credential(name, value) - if persist: - write_to_dotenv("OPENBB_" + name, value) - - -def set_av_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Alpha Vantage key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.av(key="example_key") - """ - - handle_credential("API_KEY_ALPHAVANTAGE", key, persist) - return check_av_key(show_output) - - -def check_av_key(show_output: bool = False) -> str: - """Check Alpha Vantage key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if ( - current_user.credentials.API_KEY_ALPHAVANTAGE == "REPLACE_ME" - ): # pragma: allowlist secret - status = KeyStatus.NOT_DEFINED - else: - df = TimeSeries( - key=current_user.credentials.API_KEY_ALPHAVANTAGE, output_format="pandas" - ).get_intraday(symbol="AAPL") - if df[0].empty: # pylint: disable=no-member - logger.warning("Alpha Vantage key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - else: - status = KeyStatus.DEFINED_TEST_PASSED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_fmp_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Financial Modeling Prep key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.fmp(key="example_key") - """ - - handle_credential("API_KEY_FINANCIALMODELINGPREP", key, persist) - return check_fmp_key(show_output) - - -def check_fmp_key(show_output: bool = False) -> str: - """Check Financial Modeling Prep key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - status: str - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if ( - current_user.credentials.API_KEY_FINANCIALMODELINGPREP - == "REPLACE_ME" # pragma: allowlist secret - ): # pragma: allowlist secret - status = KeyStatus.NOT_DEFINED - else: - r = request( - f"https://financialmodelingprep.com/api/v3/profile/AAPL?apikey=" - f"{current_user.credentials.API_KEY_FINANCIALMODELINGPREP}" - ) - if r.status_code in [403, 401] or "Error Message" in str(r.content): - logger.warning("Financial Modeling Prep key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Financial Modeling Prep key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_quandl_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Quandl key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.quandl(key="example_key") - """ - - handle_credential("API_KEY_QUANDL", key, persist) - return check_quandl_key(show_output) - - -def check_quandl_key(show_output: bool = False) -> str: - """Check Quandl key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if ( - current_user.credentials.API_KEY_QUANDL == "REPLACE_ME" - ): # pragma: allowlist secret - status = KeyStatus.NOT_DEFINED - else: - try: - quandl.save_key(current_user.credentials.API_KEY_QUANDL) - quandl.get("EIA/PET_RWTC_D") - status = KeyStatus.DEFINED_TEST_PASSED - except Exception as _: # noqa: F841 - logger.warning("Quandl key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_polygon_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Polygon key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.polygon(key="example_key") - """ - - handle_credential("API_POLYGON_KEY", key, persist) - return check_polygon_key(show_output) - - -def check_polygon_key(show_output: bool = False) -> str: - """Check Polygon key - - Parameters - ---------- - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_POLYGON_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - check_date = date(date.today().year, date.today().month, 1).isoformat() - r = request( - f"https://api.polygon.io/v2/aggs/ticker/AAPL/range/1/day/{check_date}" - f"/{check_date}?apiKey={current_user.credentials.API_POLYGON_KEY}" - ) - if r.status_code in [403, 401]: - logger.warning("Polygon key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Polygon key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_fred_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set FRED key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.fred(key="example_key") - """ - - handle_credential("API_FRED_KEY", key, persist) - return check_fred_key(show_output) - - -def check_fred_key(show_output: bool = False) -> str: - """Check FRED key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_FRED_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - r = request( - f"https://api.stlouisfed.org/fred/series?series_id=GNPCA&api_key={current_user.credentials.API_FRED_KEY}" - ) - if r.status_code in [403, 401, 400]: - logger.warning("FRED key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("FRED key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_news_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set News key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.news(key="example_key") - """ - - handle_credential("API_NEWS_TOKEN", key, persist) - return check_news_key(show_output) - - -def check_news_key(show_output: bool = False) -> str: - """Check News key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_NEWS_TOKEN == "REPLACE_ME": # nosec# noqa: S105 - status = KeyStatus.NOT_DEFINED - else: - r = request( - f"https://newsapi.org/v2/everything?q=keyword&apiKey={current_user.credentials.API_NEWS_TOKEN}" - ) - if r.status_code in [401, 403]: - logger.warning("News API key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("News API key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_biztoc_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set BizToc key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.biztoc(key="example_key") - """ - - handle_credential("API_BIZTOC_TOKEN", key, persist) - return check_biztoc_key(show_output) - - -def check_biztoc_key(show_output: bool = False) -> str: - """Check BizToc key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_BIZTOC_TOKEN == "REPLACE_ME": # nosec# noqa: S105 - status = KeyStatus.NOT_DEFINED - else: - r = request( - "https://biztoc.p.rapidapi.com/pages", - headers={ - "X-RapidAPI-Key": current_user.credentials.API_BIZTOC_TOKEN, - "X-RapidAPI-Host": "biztoc.p.rapidapi.com", - }, - ) - if r.status_code in [401, 403, 404]: - logger.warning("BizToc API key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("BizToc API key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_tradier_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Tradier key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.tradier(key="example_key") - """ - - handle_credential("API_TRADIER_TOKEN", key, persist) - return check_tradier_key(show_output) - - -def check_tradier_key(show_output: bool = False) -> str: - """Check Tradier key - - Parameters - ---------- - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_TRADIER_TOKEN == "REPLACE_ME": # nosec# noqa: S105 - status = KeyStatus.NOT_DEFINED - else: - r = request( - "https://sandbox.tradier.com/v1/markets/quotes", - params={"symbols": "AAPL"}, - headers={ - "Authorization": f"Bearer {current_user.credentials.API_TRADIER_TOKEN}", - "Accept": "application/json", - }, - ) - if r.status_code in [401, 403]: - logger.warning("Tradier key not defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Tradier key not defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_cmc_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Coinmarketcap key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.cmc(key="example_key") - """ - - handle_credential("API_CMC_KEY", key, persist) - return check_cmc_key(show_output) - - -def check_cmc_key(show_output: bool = False) -> str: - """Check Coinmarketcap key - - Parameters - ---------- - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - status: str - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_CMC_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - cmc = CoinMarketCapAPI(current_user.credentials.API_CMC_KEY) - - try: - cmc.cryptocurrency_map() - status = KeyStatus.DEFINED_TEST_PASSED - except Exception: - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_finnhub_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Finnhub key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.finnhub(key="example_key") - """ - - handle_credential("API_FINNHUB_KEY", key, persist) - return check_finnhub_key(show_output) - - -def check_finnhub_key(show_output: bool = False) -> str: - """Check Finnhub key - - Parameters - ---------- - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_FINNHUB_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - r = request( - f"https://finnhub.io/api/v1/quote?symbol=AAPL&token={current_user.credentials.API_FINNHUB_KEY}" - ) - if r.status_code in [403, 401, 400]: - logger.warning("Finnhub key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Finnhub key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_reddit_key( - client_id: str, - client_secret: str, - password: str, - username: str, - useragent: str, - persist: bool = False, - show_output: bool = False, -) -> str: - """Set Reddit key - - Parameters - ---------- - client_id: str - Client ID - client_secret: str - Client secret - password: str - User password - username: str - User username - useragent: str - User useragent - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.reddit( - client_id="example_id", - client_secret="example_secret", - password="example_password", - username="example_username", - useragent="example_useragent" - ) - """ - - handle_credential("API_REDDIT_CLIENT_ID", client_id, persist) - handle_credential("API_REDDIT_CLIENT_SECRET", client_secret, persist) - handle_credential("API_REDDIT_PASSWORD", password, persist) - handle_credential("API_REDDIT_USERNAME", username, persist) - handle_credential("API_REDDIT_USER_AGENT", useragent, persist) - - return check_reddit_key(show_output) - - -def check_reddit_key(show_output: bool = False) -> str: - """Check Reddit key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - reddit_keys = [ - current_user.credentials.API_REDDIT_CLIENT_ID, - current_user.credentials.API_REDDIT_CLIENT_SECRET, - current_user.credentials.API_REDDIT_USERNAME, - current_user.credentials.API_REDDIT_PASSWORD, - current_user.credentials.API_REDDIT_USER_AGENT, - ] - if "REPLACE_ME" in reddit_keys: - status = KeyStatus.NOT_DEFINED - else: - try: - with suppress_stdout(): - praw_api = praw.Reddit( - client_id=current_user.credentials.API_REDDIT_CLIENT_ID, - client_secret=current_user.credentials.API_REDDIT_CLIENT_SECRET, - username=current_user.credentials.API_REDDIT_USERNAME, - user_agent=current_user.credentials.API_REDDIT_USER_AGENT, - password=current_user.credentials.API_REDDIT_PASSWORD, - check_for_updates=False, - comment_kind="t1", - message_kind="t4", - redditor_kind="t2", - submission_kind="t3", - subreddit_kind="t5", - trophy_kind="t6", - oauth_url="https://oauth.reddit.com", - reddit_url="https://www.reddit.com", - short_url="https://redd.it", - ratelimit_seconds=5, - timeout=16, - ) - - praw_api.user.me() - status = KeyStatus.DEFINED_TEST_PASSED - except (Exception, ResponseException): - logger.warning("Reddit key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_bitquery_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Bitquery key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.bitquery(key="example_key") - """ - - handle_credential("API_BITQUERY_KEY", key, persist) - - return check_bitquery_key(show_output) - - -def check_bitquery_key(show_output: bool = False) -> str: - """Check Bitquery key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - bitquery = current_user.credentials.API_BITQUERY_KEY - if "REPLACE_ME" in bitquery: - status = KeyStatus.NOT_DEFINED - else: - headers = {"x-api-key": current_user.credentials.API_BITQUERY_KEY} - query = """ - { - ethereum { - dexTrades(options: {limit: 10, desc: "count"}) { - count - protocol - }}} - """ - r = request( - "https://graphql.bitquery.io", - method="POST", - json={"query": query}, - headers=headers, - ) - if r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Bitquery key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_binance_key( - key: str, - secret: str, - persist: bool = False, - show_output: bool = False, -) -> str: - """Set Binance key - - Parameters - ---------- - key: str - API key - secret: str - API secret - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.binance( - key="example_key", - secret="example_secret" - ) - """ - - handle_credential("API_BINANCE_KEY", key, persist) - handle_credential("API_BINANCE_SECRET", secret, persist) - - return check_binance_key(show_output) - - -def check_binance_key(show_output: bool = False) -> str: - """Check Binance key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if "REPLACE_ME" in [ - current_user.credentials.API_BINANCE_KEY, - current_user.credentials.API_BINANCE_SECRET, - ]: - status = KeyStatus.NOT_DEFINED - - else: - try: - client = binance.Client( - current_user.credentials.API_BINANCE_KEY, - current_user.credentials.API_BINANCE_SECRET, - ) - client.get_account_api_permissions() - status = KeyStatus.DEFINED_TEST_PASSED - except Exception: - logger.warning("Binance key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_coinbase_key( - key: str, - secret: str, - passphrase: str, - persist: bool = False, - show_output: bool = False, -) -> str: - """Set Coinbase key - - Parameters - ---------- - key: str - API key - secret: str - API secret - passphrase: str - Account passphrase - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.coinbase( - key="example_key", - secret="example_secret", - passphrase="example_passphrase" - ) - """ - - handle_credential("API_COINBASE_KEY", key, persist) - handle_credential("API_COINBASE_SECRET", secret, persist) - handle_credential("API_COINBASE_PASS_PHRASE", passphrase, persist) - - return check_coinbase_key(show_output) - - -def check_coinbase_key(show_output: bool = False) -> str: - """Check Coinbase key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - status: str - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if "REPLACE_ME" in [ - current_user.credentials.API_COINBASE_KEY, - current_user.credentials.API_COINBASE_SECRET, - current_user.credentials.API_COINBASE_PASS_PHRASE, - ]: - status = KeyStatus.NOT_DEFINED - else: - auth = CoinbaseProAuth( - current_user.credentials.API_COINBASE_KEY, - current_user.credentials.API_COINBASE_SECRET, - current_user.credentials.API_COINBASE_PASS_PHRASE, - ) - try: - resp = make_coinbase_request("/accounts", auth=auth) - except CoinbaseApiException: - resp = None - if not resp: - logger.warning("Coinbase key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - else: - status = KeyStatus.DEFINED_TEST_PASSED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_walert_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Walert key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.walert(key="example_key") - """ - - handle_credential("API_WHALE_ALERT_KEY", key, persist) - return check_walert_key(show_output) - - -def check_walert_key(show_output: bool = False) -> str: - """Check Walert key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_WHALE_ALERT_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - url = ( - "https://api.whale-alert.io/v1/transactions?api_key=" - + current_user.credentials.API_WHALE_ALERT_KEY - ) - try: - response = request(url) - if not 200 <= response.status_code < 300: - logger.warning("Walert key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - else: - status = KeyStatus.DEFINED_TEST_PASSED - except Exception: - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_glassnode_key( - key: str, persist: bool = False, show_output: bool = False -) -> str: - """Set Glassnode key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.glassnode(key="example_key") - """ - - handle_credential("API_GLASSNODE_KEY", key, persist) - return check_glassnode_key(show_output) - - -def check_glassnode_key(show_output: bool = False) -> str: - """Check Glassnode key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_GLASSNODE_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - url = "https://api.glassnode.com/v1/metrics/market/price_usd_close" - - parameters = { - "api_key": current_user.credentials.API_GLASSNODE_KEY, - "a": "BTC", - "i": "24h", - "s": str(1_614_556_800), - "u": str(1_641_227_783_561), - } - - r = request(url, params=parameters) - if r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Glassnode key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_coinglass_key( - key: str, persist: bool = False, show_output: bool = False -) -> str: - """Set Coinglass key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.coinglass(key="example_key") - """ - - handle_credential("API_COINGLASS_KEY", key, persist) - return check_coinglass_key(show_output) - - -def check_coinglass_key(show_output: bool = False) -> str: - """Check Coinglass key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_COINGLASS_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - url = "https://open-api.coinglass.com/api/pro/v1/futures/openInterest/chart?&symbol=BTC&interval=0" - - headers = {"coinglassSecret": current_user.credentials.API_COINGLASS_KEY} - - response = request(url, headers=headers) - - if """success":false""" in str(response.content): - logger.warning("Coinglass key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif response.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Coinglass key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_cpanic_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Cpanic key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.cpanic(key="example_key") - """ - - handle_credential("API_CRYPTO_PANIC_KEY", key, persist) - return check_cpanic_key(show_output) - - -def check_cpanic_key(show_output: bool = False) -> str: - """Check Cpanic key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_CRYPTO_PANIC_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - crypto_panic_url = ( - "https://cryptopanic.com/api/v1/posts/?auth_token=" - f"{current_user.credentials.API_CRYPTO_PANIC_KEY}" - ) - response = request(crypto_panic_url) - - if response.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Cpanic key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_ethplorer_key( - key: str, persist: bool = False, show_output: bool = False -) -> str: - """Set Ethplorer key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.ethplorer(key="example_key") - """ - - handle_credential("API_ETHPLORER_KEY", key, persist) - return check_ethplorer_key(show_output) - - -def check_ethplorer_key(show_output: bool = False) -> str: - """Check Ethplorer key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_ETHPLORER_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - ethplorer_url = "https://api.ethplorer.io/getTokenInfo/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984?apiKey=" - ethplorer_url += current_user.credentials.API_ETHPLORER_KEY - - try: - response = request(ethplorer_url) - if response.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("ethplorer key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - except Exception as _: # noqa: F841 - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_smartstake_key( - key: str, access_token: str, persist: bool = False, show_output: bool = False -): - """Set Smartstake key. - - Parameters - ---------- - key: str - API key - access_token: str - API token - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.smartstake( - key="example_key", - access_token="example_access_token", - ) - """ - - handle_credential("API_SMARTSTAKE_KEY", key, persist) - handle_credential("API_SMARTSTAKE_TOKEN", access_token, persist) - return check_smartstake_key(show_output) - - -def check_smartstake_key(show_output: bool = False) -> str: - """Check Smartstake key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if "REPLACE_ME" in [ - current_user.credentials.API_SMARTSTAKE_TOKEN, - current_user.credentials.API_SMARTSTAKE_KEY, - ]: - status = KeyStatus.NOT_DEFINED - else: - payload = { - "type": "history", - "dayCount": 30, - "key": current_user.credentials.API_SMARTSTAKE_KEY, - "token": current_user.credentials.API_SMARTSTAKE_TOKEN, - } - - smartstake_url = "https://prod.smartstakeapi.com/listData?app=TERRA" - response = request(smartstake_url, params=payload) # type: ignore - - try: - if ( - "errors" in str(response.content) - or response.status_code < 200 - or response.status_code >= 300 - ): - logger.warning("Smartstake key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif 200 <= response.status_code < 300: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Smartstake key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - except Exception as _: # noqa: F841 - logger.warning("Smartstake key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_github_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set GitHub key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.github(key="example_key") - """ - - handle_credential("API_GITHUB_KEY", key, persist) - return check_github_key(show_output) - - -def check_github_key(show_output: bool = False) -> str: - """Check GitHub key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if ( - current_user.credentials.API_GITHUB_KEY == "REPLACE_ME" - ): # pragma: allowlist secret - status = KeyStatus.NOT_DEFINED - else: - status = KeyStatus.DEFINED_NOT_TESTED - # github api will not fail for the first requests without key - # only after certain amount of requests the user will get rate limited - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_messari_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Messari key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.messari(key="example_key") - """ - - handle_credential("API_MESSARI_KEY", key, persist) - return check_messari_key(show_output) - - -def check_messari_key(show_output: bool = False) -> str: - """Check Messari key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if ( - current_user.credentials.API_MESSARI_KEY - == "REPLACE_ME" # pragma: allowlist secret - ): # pragma: allowlist secret - status = KeyStatus.NOT_DEFINED - else: - url = "https://data.messari.io/api/v2/assets/bitcoin/profile" - headers = {"x-messari-api-key": current_user.credentials.API_MESSARI_KEY} - params = {"fields": "profile/general/overview/official_links"} - r = request(url, headers=headers, params=params) - - if r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Messari key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_eodhd_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Eodhd key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.eodhd(key="example_key") - """ - - handle_credential("API_EODHD_KEY", key, persist) - return check_eodhd_key(show_output) - - -def check_eodhd_key(show_output: bool = False) -> str: - """Check Eodhd key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_EODHD_KEY == "REPLACE_ME": # nosec - status = KeyStatus.NOT_DEFINED - else: - request_url = ( - "https://eodhistoricaldata.com/api/exchanges-list/?api_token=" - f"{current_user.credentials.API_EODHD_KEY}&fmt=json" - ) - r = request(request_url) - if r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Eodhd key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_santiment_key( - key: str, persist: bool = False, show_output: bool = False -) -> str: - """Set Santiment key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.santiment(key="example_key") - """ - - handle_credential("API_SANTIMENT_KEY", key, persist) - return check_santiment_key(show_output) - - -def check_santiment_key(show_output: bool = False) -> str: - """Check Santiment key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_SANTIMENT_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - headers = { - "Content-Type": "application/graphql", - "Authorization": f"Apikey {current_user.credentials.API_SANTIMENT_KEY}", - } - - # pylint: disable=line-too-long - data = '\n{{ getMetric(metric: "dev_activity"){{ timeseriesData( slug: "ethereum" from: ""2020-02-10T07:00:00Z"" to: "2020-03-10T07:00:00Z" interval: "1w"){{ datetime value }} }} }}' # noqa: E501 - - response = request( - "https://api.santiment.net/graphql", - method="POST", - headers=headers, - data=data, - ) - try: - if response.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("santiment key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - except Exception as _: # noqa: F841 - status = KeyStatus.DEFINED_TEST_FAILED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_tokenterminal_key( - key: str, persist: bool = False, show_output: bool = False -) -> str: - """Set Token Terminal key. - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.tokenterminal(key="example_key") - """ - handle_credential("API_TOKEN_TERMINAL_KEY", key, persist) - return check_tokenterminal_key(show_output) - - -def check_tokenterminal_key(show_output: bool = False) -> str: - """Check Token Terminal key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_TOKEN_TERMINAL_KEY == "REPLACE_ME": # noqa: S105 - status = KeyStatus.NOT_DEFINED - else: - token_terminal = TokenTerminal( - key=current_user.credentials.API_TOKEN_TERMINAL_KEY - ) - - if "message" in token_terminal.get_all_projects(): - logger.warning("Token Terminal key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - else: - status = KeyStatus.DEFINED_TEST_PASSED - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_intrinio_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Intrinio key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.intrinio(key="example_key") - """ - - handle_credential("API_INTRINIO_KEY", key, persist) - return check_intrinio_key(show_output) - - -def check_intrinio_key(show_output: bool = False) -> str: - """Check Intrinio key - - Parameters - ---------- - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_INTRINIO_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - r = request( - f"https://api-v2.intrinio.com/securities/AAPL/prices?api_key={current_user.credentials.API_INTRINIO_KEY}" - ) - if r.status_code in [403, 401, 429]: - logger.warning("Intrinio key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Intrinio key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_databento_key( - key: str, persist: bool = False, show_output: bool = False -) -> str: - """Set DataBento key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.databento(key="example_key") - """ - - handle_credential("API_DATABENTO_KEY", key, persist) - return check_databento_key(show_output) - - -def check_databento_key(show_output: bool = False) -> str: - """Check DataBento key - - Parameters - ---------- - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if current_user.credentials.API_DATABENTO_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - r = request( - "https://hist.databento.com/v0/metadata.list_datasets", - auth=(f"{current_user.credentials.API_DATABENTO_KEY}", ""), - ) - if r.status_code in [403, 401, 429]: - logger.warning("DataBento key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("DataBento key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_dappradar_key( - key: str, persist: bool = False, show_output: bool = False -) -> str: - """Set DappRadar key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.dappradar(key="example_key") - """ - - handle_credential("API_DAPPRADAR_KEY", key, persist) - return check_dappradar_key(show_output) - - -def check_dappradar_key(show_output: bool = False) -> str: - """Check DappRadar key - - Parameters - ---------- - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - current_user = get_current_user() - - if current_user.credentials.API_DAPPRADAR_KEY == "REPLACE_ME": - status = KeyStatus.NOT_DEFINED - else: - r = request( - "https://api.dappradar.com/4tsxo4vuhotaojtl/tokens/chains", - headers={"X-BLOBR-KEY": current_user.credentials.API_DAPPRADAR_KEY}, - ) - if r.status_code in [403, 401, 429]: - logger.warning("DappRadar key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("DappRadar key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_companieshouse_key( - key: str, - persist: bool = False, - show_output: bool = False, -) -> str: - """Set Companies House key - - Parameters - ---------- - key: str - API ID - - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.companieshouse( - api_id="example_id", - ) - """ - - handle_credential("API_COMPANIESHOUSE_KEY", key, persist) - return check_companieshouse_key(show_output) - - -def check_companieshouse_key(show_output: bool = False) -> str: - """Check Companies House key - - Parameters - ---------- - show_output: bool - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - """ - - current_user = get_current_user() - - if current_user.credentials.API_COMPANIESHOUSE_KEY == "REPLACE_ME": - logger.info("Companies House key not defined") - status = KeyStatus.NOT_DEFINED - else: - r = request( - "https://api.company-information.service.gov.uk/company/00000118", - auth=(f"{current_user.credentials.API_COMPANIESHOUSE_KEY}", ""), - ) - if r.status_code in [403, 401, 429]: - logger.warning("Companies House key defined, test failed") - status = KeyStatus.DEFINED_TEST_FAILED - elif r.status_code == 200: - logger.info("Companies House key defined, test passed") - status = KeyStatus.DEFINED_TEST_PASSED - else: - logger.warning("Companies House key defined, test inconclusive") - status = KeyStatus.DEFINED_TEST_INCONCLUSIVE - - if show_output: - console.print(status.colorize()) - - return str(status) - - -def set_nixtla_key(key: str, persist: bool = False, show_output: bool = False) -> str: - """Set Nixtla API key - - Parameters - ---------- - key: str - API key - persist: bool, optional - If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session. - If True, api key change will be global, i.e. it will affect terminal environment variables. - By default, False. - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - str - Status of key set - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.keys.nixtla(key="example_key") - """ - - handle_credential("API_KEY_NIXTLA", key, persist) - return check_nixtla_key(show_output) - - -def check_nixtla_key(show_output: bool = False) -> str: - """Check Nixtla key - - Parameters - ---------- - show_output: bool, optional - Display status string or not. By default, False. - - Returns - ------- - status: str - """ - - if show_output: - console.print("Checking status...") - - current_user = get_current_user() - - if ( - current_user.credentials.API_KEY_NIXTLA - == "REPLACE_ME" # pragma: allowlist secret - ): # pragma: allowlist secret - status = KeyStatus.NOT_DEFINED - else: - timegpt = TimeGPT( - token=get_current_user().credentials.API_KEY_NIXTLA, - ) - status = ( - KeyStatus.DEFINED_TEST_PASSED - if timegpt.validate_token() - else KeyStatus.DEFINED_TEST_FAILED - ) - - if show_output: - console.print(status.colorize()) - - return str(status) diff --git a/openbb_terminal/keys_view.py b/openbb_terminal/keys_view.py deleted file mode 100644 index f5af34b9ed0..00000000000 --- a/openbb_terminal/keys_view.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Keys view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal import keys_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_keys( - show: bool = False, export: str = "", sheet_name: Optional[str] = None -): - """Display currently set API keys. - - Parameters - ---------- - show: bool - Flag to choose whether to show actual keys or not. - By default, False. - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = keys_model.get_keys(show=show) - if df.empty: - console.print("No keys available\n") - return - - print_rich_table( - df, - show_index=True, - index_name="API", - title="Current keys", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mykeys", - df, - sheet_name, - ) diff --git a/openbb_terminal/loggers.py b/openbb_terminal/loggers.py deleted file mode 100644 index fd0f20c29ea..00000000000 --- a/openbb_terminal/loggers.py +++ /dev/null @@ -1,385 +0,0 @@ -"""Logging Configuration""" - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import json -import logging -import re -import sys -import time -import uuid -from pathlib import Path -from platform import platform, python_version -from typing import Any, Dict, Optional - -# IMPORTATION THIRDPARTY -try: - import git -except ImportError: - WITH_GIT = False -else: - WITH_GIT = True - -# IMPORTATION INTERNAL -from openbb_terminal.base_helpers import openbb_posthog -from openbb_terminal.core.log.generation.directories import get_log_dir -from openbb_terminal.core.log.generation.formatter_with_exceptions import ( - FormatterWithExceptions, -) -from openbb_terminal.core.log.generation.path_tracking_file_handler import ( - PathTrackingFileHandler, -) -from openbb_terminal.core.log.generation.settings import ( - AppSettings, - AWSSettings, - LogSettings, - Settings, -) -from openbb_terminal.core.log.generation.settings_logger import get_startup -from openbb_terminal.core.log.generation.user_logger import ( - NO_USER_PLACEHOLDER, - get_current_user, - get_user_uuid, -) -from openbb_terminal.core.session.current_system import ( - get_current_system, - set_system_variable, -) -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) -logging_verbosity = get_current_system().LOGGING_VERBOSITY - -logging.getLogger("requests").setLevel(logging_verbosity) -logging.getLogger("urllib3").setLevel(logging_verbosity) - - -def get_app_id() -> str: - """UUID of the current installation.""" - - try: - app_id = get_log_dir().stem - except OSError as e: - if e.errno == 30: - console.print("Please move the application into a writable location.") - console.print( - "Note for macOS users: copy `OpenBB Terminal` folder outside the DMG." - ) - else: - raise e - except Exception as e: - raise e - - return app_id - - -def get_session_id() -> str: - """UUID of the current session.""" - session_id = str(uuid.uuid4()) + "-" + str(int(time.time())) - return session_id - - -def get_commit_hash(use_env=True) -> str: - """Get Commit Short Hash""" - - logging_commit_hash = get_current_system().LOGGING_COMMIT_HASH - if use_env and logging_commit_hash != "REPLACE_ME": - return logging_commit_hash - - git_dir = Path(__file__).parent.parent.joinpath(".git") - - if WITH_GIT and git_dir.is_dir(): - repo = git.Repo(path=git_dir) - sha = repo.head.object.hexsha - short_sha = repo.git.rev_parse(sha, short=8) - commit_hash = f"sha:{short_sha}" - else: - commit_hash = "unknown-commit" - - return commit_hash - - -def get_branch() -> str: - def get_branch_commit_hash(branch: str) -> str: - response = request( - url=f"https://api.github.com/repos/openbb-finance/openbbterminal/branches/{branch}" - ) - return "sha:" + response.json()["commit"]["sha"][:8] - - current_commit_hash = get_commit_hash() - - for branch in ["main", "develop"]: - try: - if get_branch_commit_hash(branch) == current_commit_hash: - return branch - except Exception: # noqa: S110 - pass - - git_dir = Path(__file__).parent.parent.joinpath(".git") - if WITH_GIT and git_dir.is_dir(): - try: - repo = git.Repo(path=git_dir) - branch = repo.active_branch.name - return branch - except Exception: # noqa: S110 - pass - - return "unknown-branch" - - -class PosthogHandler(logging.Handler): - """Posthog Handler""" - - def __init__(self, settings: Settings): - super().__init__() - self.settings = settings - self.app_settings = settings.app_settings - self.logged_in = False - self.disabled = openbb_posthog.feature_enabled( - "disable_analytics", - self.app_settings.identifier, - send_feature_flag_events=False, - ) - - def emit(self, record: logging.LogRecord): - if self.disabled or "llama_index" in record.pathname: - return - try: - self.send(record=record) - except Exception: - self.handleError(record) - - def log_to_dict(self, log_info: str) -> dict: - """Log to dict""" - log_regex = r"(STARTUP|CMD|ASKOBB): (.*)" - log_dict: Dict[str, Any] = {} - - for log in re.findall(log_regex, log_info): - log_dict[log[0]] = json.loads(log[1]) - - sdk_regex = r"({\"INPUT\":.*})" - if sdk_dict := re.findall(sdk_regex, log_info): - log_dict["SDK"] = json.loads(sdk_dict[0]) - - return log_dict - - def send(self, record: logging.LogRecord): - """Send log record to Posthog""" - - app_settings = self.app_settings - - level_name = logging.getLevelName(record.levelno) - log_line = FormatterWithExceptions.filter_log_line(text=record.getMessage()) - - log_extra = self.extract_log_extra(record=record) - log_extra.update(dict(level=level_name, message=log_line)) - event_name = f"log_{level_name.lower()}" - - if log_dict := self.log_to_dict(log_info=log_line): - event_name = f"log_{list(log_dict.keys())[0].lower()}" - log_dict = log_dict.get("STARTUP", log_dict) - - log_extra = {**log_extra, **log_dict} - log_extra.pop("message", None) - - if re.match(r"^(QUEUE|START|END|INPUT:)", log_line) and not log_dict: - return - - if ( - not self.logged_in - and get_user_uuid() != NO_USER_PLACEHOLDER - and get_current_user().profile.remember - ): - self.logged_in = True - openbb_posthog.identify( - get_user_uuid(), - { - "email": get_current_user().profile.email, - "primaryUsage": get_current_user().profile.primary_usage, - **get_startup(), - }, - ) - openbb_posthog.alias(get_user_uuid(), app_settings.identifier) - - openbb_posthog.capture( - app_settings.identifier, - event_name, - properties=log_extra, - ) - - def extract_log_extra(self, record: logging.LogRecord) -> Dict[str, Any]: - """Extract log extra from record""" - - log_extra: Dict[str, Any] = { - "appName": self.app_settings.name, - "appId": self.app_settings.identifier, - "sessionId": self.app_settings.session_id, - "commitHash": self.app_settings.commit_hash, - "platform": platform(), - "pythonVersion": python_version(), - "terminalVersion": get_current_system().VERSION, - "branch": get_current_system().LOGGING_BRANCH, - } - - if get_user_uuid() != NO_USER_PLACEHOLDER: - log_extra["userId"] = get_user_uuid() - - if hasattr(record, "extra"): - log_extra = {**log_extra, **record.extra} - - if record.exc_info: - log_extra["exception"] = { - "type": str(record.exc_info[0]), - "value": str(record.exc_info[1]), - "traceback": self.format(record), - } - - return log_extra - - -def add_posthog_handler(settings: Settings): - app_settings = settings.app_settings - handler = PosthogHandler(settings=settings) - formatter = FormatterWithExceptions(app_settings=app_settings) - handler.setFormatter(formatter) - logging.getLogger().addHandler(handler) - - -def add_stdout_handler(settings: Settings): - app_settings = settings.app_settings - handler = logging.StreamHandler(sys.stdout) - formatter = FormatterWithExceptions(app_settings=app_settings) - handler.setFormatter(formatter) - logging.getLogger().addHandler(handler) - - -def add_stderr_handler(settings: Settings): - app_settings = settings.app_settings - handler = logging.StreamHandler(sys.stderr) - formatter = FormatterWithExceptions(app_settings=app_settings) - handler.setFormatter(formatter) - logging.getLogger().addHandler(handler) - - -def add_noop_handler(settings: Settings): - app_settings = settings.app_settings - handler = logging.NullHandler() - formatter = FormatterWithExceptions(app_settings=app_settings) - handler.setFormatter(formatter) - logging.getLogger().addHandler(handler) - - -def add_file_handler(settings: Settings): - app_settings = settings.app_settings - handler = PathTrackingFileHandler(settings=settings) - formatter = FormatterWithExceptions(app_settings=app_settings) - handler.setFormatter(formatter) - logging.getLogger().addHandler(handler) - - -def setup_handlers(settings: Settings): - logging_settings = settings.log_settings - handler_list = logging_settings.handler_list - verbosity = logging_settings.verbosity - - # Remove any existing log handlers - # Notebooks have a root logging handler by default, see https://github.com/ipython/ipython/issues/8282 - root_logger = logging.getLogger() - if root_logger.hasHandlers(): - root_logger.handlers.clear() - - logging.basicConfig( - level=verbosity, - format=FormatterWithExceptions.LOGFORMAT, - datefmt=FormatterWithExceptions.DATEFORMAT, - handlers=[], - ) - - posthog_active: bool = False - for handler_type in handler_list: - if handler_type == "stdout": - add_stdout_handler(settings=settings) - elif handler_type == "stderr": - add_stderr_handler(settings=settings) - elif handler_type == "noop": - add_noop_handler(settings=settings) - elif handler_type == "file": - add_file_handler(settings=settings) - elif handler_type == "posthog": - posthog_active = True - else: - logger.debug("Unknown log handler.") - - logger.info("Logging configuration finished") - logger.info("Logging set to %s", handler_list) - logger.info("Verbosity set to %s", verbosity) - logger.info( - "LOGFORMAT: %s%s", - FormatterWithExceptions.LOGPREFIXFORMAT.replace("|", "-"), - FormatterWithExceptions.LOGFORMAT.replace("|", "-"), - ) - if ( - posthog_active - and not any( - [get_current_system().TEST_MODE, get_current_system().LOGGING_SUPPRESS] - ) - and get_current_system().LOG_COLLECT - ): - add_posthog_handler(settings=settings) - - -def setup_logging( - app_name: Optional[str] = None, - frequency: Optional[str] = None, - verbosity: Optional[int] = None, -) -> None: - """Setup Logging""" - current_system = get_current_system() - - # AppSettings - commit_hash = get_commit_hash() - name = app_name or current_system.LOGGING_APP_NAME - identifier = get_app_id() - session_id = get_session_id() - user_id = get_user_uuid() - branch = get_branch() - - set_system_variable("LOGGING_APP_ID", identifier) - set_system_variable("LOGGING_COMMIT_HASH", commit_hash) - set_system_variable("LOGGING_BRANCH", branch) - - # AWSSettings - aws_access_key_id = current_system.LOGGING_AWS_ACCESS_KEY_ID - aws_secret_access_key = current_system.LOGGING_AWS_SECRET_ACCESS_KEY - - # LogSettings - directory = get_log_dir() - frequency = frequency or current_system.LOGGING_FREQUENCY - handler_list = current_system.LOGGING_HANDLERS - rolling_clock = current_system.LOGGING_ROLLING_CLOCK - verbosity = verbosity or current_system.LOGGING_VERBOSITY - - settings = Settings( - app_settings=AppSettings( - commit_hash=commit_hash, - name=name, - identifier=identifier, - session_id=session_id, - user_id=user_id, - ), - aws_settings=AWSSettings( - aws_access_key_id=aws_access_key_id, - aws_secret_access_key=aws_secret_access_key, - ), - log_settings=LogSettings( - directory=directory, - frequency=frequency, - handler_list=handler_list, - rolling_clock=rolling_clock, - verbosity=verbosity, - ), - ) - - setup_handlers(settings=settings) diff --git a/openbb_terminal/menu.py b/openbb_terminal/menu.py deleted file mode 100644 index bedc66a4ada..00000000000 --- a/openbb_terminal/menu.py +++ /dev/null @@ -1,67 +0,0 @@ -import logging -import sys -from typing import Optional - -from matplotlib import pyplot -from prompt_toolkit import PromptSession -from prompt_toolkit.eventloop.inputhook import set_eventloop_with_inputhook -from prompt_toolkit.history import FileHistory - -from openbb_terminal.core.config.paths import HIST_FILE_PATH -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -# pylint: disable=W0104 -def is_jupyter() -> bool: - try: - __IPYTHON__ # type: ignore - return True - except NameError: - return False - - -def is_papermill() -> bool: - """Check if session is being launched with args '-m ipykernel_launcher', - thus coming from papermill Popen. See 'ipykernel_launcher' in reports_model - for more detail. - """ - - return all( - i in sys.argv - for i in [ - "-m", - "ipykernel_launcher", - "-f", - "--HistoryManager.hist_file=:memory:", - ] - ) - - -def inputhook(inputhook_context): - while not inputhook_context.input_is_ready(): - try: - pyplot.pause(0.1) - except Exception as exp: - logger.exception("%s", type(exp).__name__) - continue - return False - - -try: - if sys.stdin.isatty(): - session: Optional[PromptSession] = PromptSession( - history=FileHistory(str(HIST_FILE_PATH)) - ) - set_eventloop_with_inputhook(inputhook) - else: - session = None -# pylint: disable=unused-variable -except Exception as e: # noqa: F841 - logger.exception("%s", type(e).__name__) - console.print( - "WARNING: Prompt toolkit is turned on but did not initialize successfully." - " Falling back to input()..." - ) - session = None # type: ignore diff --git a/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands.csv b/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands.csv deleted file mode 100644 index e8571cb2702..00000000000 --- a/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands.csv +++ /dev/null @@ -1,481 +0,0 @@ -file,command -CONTRIBUTING.md,update -CONTRIBUTING.md,rolling -CONTRIBUTING.md,raw -CONTRIBUTING.md,price -CONTRIBUTING.md,dd -CONTRIBUTING.md,top -CONTRIBUTING.md,mt -CONTRIBUTING.md,holdings -CONTRIBUTING.md,load -CONTRIBUTING.md,fwd -CONTRIBUTING.md,dcfc -CONTRIBUTING.md,oanda -CONTRIBUTING.md,from -CONTRIBUTING.md,forecast -CONTRIBUTING.md,search -CONTRIBUTING.md,to -CONTRIBUTING.md,forex -CONTRIBUTING.md,crypto -CONTRIBUTING.md,export -CONTRIBUTING.md,type -CONTRIBUTING.md,file -CONTRIBUTING.md,portfolio -CONTRIBUTING.md,quote -CONTRIBUTING.md,dcf -CONTRIBUTING.md,pt -CONTRIBUTING.md,grhist -CONTRIBUTING.md,qa -CONTRIBUTING.md,plot -CONTRIBUTING.md,view -CONTRIBUTING.md,ta -CONTRIBUTING.md,add -CONTRIBUTING.md,symbol -CONTRIBUTING.md,fa -CONTRIBUTING.md,dps -CONTRIBUTING.md,country -CONTRIBUTING.md,mktcap -CONTRIBUTING.md,period -CONTRIBUTING.md,psi -CONTRIBUTING.md,options -CONTRIBUTING.md,codes -CONTRIBUTING.md,overview -CONTRIBUTING.md,ticker -CONTRIBUTING.md,divs -CONTRIBUTING.md,rot -CONTRIBUTING.md,candle -CONTRIBUTING.md,tob -CONTRIBUTING.md,hist -CONTRIBUTING.md,stocks -CONTRIBUTING.md,info -CONTRIBUTING.md,status -CONTRIBUTING.md,set -CONTRIBUTING.md,"defined, test inconclusive" -CONTRIBUTING.md,not defined -CONTRIBUTING.md,"defined, test failed" -CONTRIBUTING.md,"defined, test passed" -CONTRIBUTING.md,source -CONTRIBUTING.md,download -CONTRIBUTING.md,list -CONTRIBUTING.md,get -CONTRIBUTING.md,sources -CONTRIBUTING.md,cls -CONTRIBUTING.md,keys -CONTRIBUTING.md,news -CONTRIBUTING.md,show -README.md,support -openbb_terminal/SDK_README.md,show -openbb_terminal/SDK_README.md,from -openbb_terminal/SDK_README.md,update -openbb_terminal/SDK_README.md,plot -openbb_terminal/SDK_README.md,forecast -openbb_terminal/SDK_README.md,portfolio -openbb_terminal/SDK_README.md,hrp -openbb_terminal/SDK_README.md,keys -openbb_terminal/SDK_README.md,dps -openbb_terminal/SDK_README.md,search -openbb_terminal/SDK_README.md,minrisk -openbb_terminal/SDK_README.md,source -openbb_terminal/SDK_README.md,load -openbb_terminal/SDK_README.md,mykeys -openbb_terminal/SDK_README.md,all -openbb_terminal/SDK_README.md,maxsharpe -openbb_terminal/SDK_README.md,economy -openbb_terminal/SDK_README.md,dpi -openbb_terminal/SDK_README.md,stocks -openbb_terminal/SDK_README.md,index -openbb_terminal/SDK_README.md,fmp -openbb_terminal/SDK_README.md,dpotc -openbb_terminal/SDK_README.md,fed -openbb_terminal/SDK_README.md,po -openbb_terminal/SDK_README.md,create -openbb_terminal/SDK_README.md,weights -openbb_terminal/SDK_README.md,categories -website/content/sdk/faqs/data_sources.md,stocks -website/content/sdk/faqs/data_sources.md,load -website/content/sdk/faqs/data_sources.md,source -website/content/sdk/faqs/data_sources.md,exchange -website/content/sdk/faqs/developer_issues.md,remove -website/content/sdk/faqs/developer_issues.md,update -website/content/sdk/faqs/developer_issues.md,rm -website/content/sdk/faqs/developer_issues.md,clean -website/content/sdk/faqs/developer_issues.md,global -website/content/sdk/faqs/general_operation.md,stocks -website/content/sdk/faqs/general_operation.md,from -website/content/sdk/faqs/installation_updates.md,all -website/content/sdk/faqs/installation_updates.md,sh -website/content/sdk/faqs/installation_updates.md,raw -website/content/sdk/usage/basics/index.md,index -website/content/sdk/usage/basics/index.md,calendar -website/content/sdk/usage/basics/index.md,valuation -website/content/sdk/usage/basics/index.md,events -website/content/sdk/usage/basics/index.md,symbol -website/content/sdk/usage/basics/index.md,stocks -website/content/sdk/usage/basics/index.md,donchian -website/content/sdk/usage/basics/index.md,keys -website/content/sdk/usage/basics/index.md,from -website/content/sdk/usage/basics/index.md,filter -website/content/sdk/usage/basics/index.md,ta -website/content/sdk/usage/basics/index.md,load -website/content/sdk/usage/basics/index.md,to -website/content/sdk/usage/basics/index.md,economy -website/content/sdk/usage/basics/index.md,candle -website/content/sdk/usage/basics/index.md,futures -website/content/sdk/usage/basics/index.md,ca -website/content/sdk/usage/basics/index.md,list -website/content/sdk/usage/basics/index.md,etf -website/content/sdk/usage/basics/index.md,holdings -website/content/sdk/usage/basics/index.md,dd -website/content/sdk/usage/basics/index.md,all -website/content/sdk/usage/api-keys.md,from -website/content/sdk/usage/api-keys.md,global -website/content/sdk/usage/api-keys.md,account -website/content/sdk/usage/api-keys.md,fred -website/content/sdk/usage/api-keys.md,degiro -website/content/sdk/usage/api-keys.md,oanda -website/content/sdk/usage/api-keys.md,change -website/content/sdk/usage/api-keys.md,fmp -website/content/sdk/usage/api-keys.md,mykeys -website/content/sdk/usage/api-keys.md,to -website/content/sdk/usage/api-keys.md,set -website/content/sdk/usage/api-keys.md,news -website/content/sdk/usage/api-keys.md,not defined -website/content/sdk/usage/api-keys.md,keys -website/content/sdk/usage/api-keys.md,stocks -website/content/sdk/usage/api-keys.md,quote -website/content/sdk/usage/api-keys.md,show -website/content/sdk/usage/guides/changing-sources.md,stocks -website/content/sdk/usage/guides/changing-sources.md,source -website/content/sdk/usage/guides/changing-sources.md,load -website/content/sdk/usage/guides/changing-sources.md,fa -website/content/sdk/usage/guides/changing-sources.md,symbol -website/content/sdk/usage/guides/chart-styling.md,from -website/content/sdk/usage/guides/data.md,portfolio -website/content/sdk/usage/guides/data.md,holdings -website/content/sdk/usage/intros/alternative.md,categories -website/content/sdk/usage/intros/alternative.md,country -website/content/sdk/usage/intros/alternative.md,summary -website/content/sdk/usage/intros/alternative.md,covid -website/content/sdk/usage/intros/alternative.md,oss -website/content/sdk/usage/intros/alternative.md,slopes -website/content/sdk/usage/intros/alternative.md,top -website/content/sdk/usage/intros/alternative.md,from -website/content/sdk/usage/intros/econometrics.md,bpag -website/content/sdk/usage/intros/econometrics.md,correlation -website/content/sdk/usage/intros/econometrics.md,bgod -website/content/sdk/usage/intros/econometrics.md,root -website/content/sdk/usage/intros/econometrics.md,load -website/content/sdk/usage/intros/econometrics.md,from -website/content/sdk/usage/intros/econometrics.md,to -website/content/sdk/usage/intros/econometrics.md,norm -website/content/sdk/usage/intros/econometrics.md,all -website/content/sdk/usage/intros/econometrics.md,type -website/content/sdk/usage/intros/econometrics.md,summary -website/content/sdk/usage/intros/econometrics.md,ols -website/content/sdk/usage/intros/econometrics.md,econometrics -website/content/sdk/usage/intros/economy.md,fred -website/content/sdk/usage/intros/economy.md,all -website/content/sdk/usage/intros/economy.md,calendar -website/content/sdk/usage/intros/economy.md,events -website/content/sdk/usage/intros/economy.md,index -website/content/sdk/usage/intros/economy.md,filter -website/content/sdk/usage/intros/economy.md,download -website/content/sdk/usage/intros/economy.md,from -website/content/sdk/usage/intros/economy.md,keys -website/content/sdk/usage/intros/economy.md,performance -website/content/sdk/usage/intros/economy.md,type -website/content/sdk/usage/intros/economy.md,macro -website/content/sdk/usage/intros/economy.md,to -website/content/sdk/usage/intros/economy.md,economy -website/content/sdk/usage/intros/economy.md,list -website/content/sdk/usage/intros/economy.md,sector -website/content/sdk/usage/intros/economy.md,view -website/content/sdk/usage/intros/economy.md,symbol -website/content/sdk/usage/intros/economy.md,country -website/content/sdk/usage/intros/etf.md,decliners -website/content/sdk/usage/intros/etf.md,active -website/content/sdk/usage/intros/etf.md,gainers -website/content/sdk/usage/intros/etf.md,screen -website/content/sdk/usage/intros/etf.md,index -website/content/sdk/usage/intros/etf.md,scr -website/content/sdk/usage/intros/etf.md,disc -website/content/sdk/usage/intros/etf.md,list -website/content/sdk/usage/intros/etf.md,performance -website/content/sdk/usage/intros/etf.md,ca -website/content/sdk/usage/intros/etf.md,stocks -website/content/sdk/usage/intros/etf.md,from -website/content/sdk/usage/intros/etf.md,categories -website/content/sdk/usage/intros/etf.md,movers -website/content/sdk/usage/intros/etf.md,etf -website/content/sdk/usage/intros/etf.md,holdings -website/content/sdk/usage/intros/fixedincome.md,ecb -website/content/sdk/usage/intros/fixedincome.md,fixedincome -website/content/sdk/usage/intros/fixedincome.md,options -website/content/sdk/usage/intros/fixedincome.md,projection -website/content/sdk/usage/intros/fixedincome.md,from -website/content/sdk/usage/intros/fixedincome.md,icebofa -website/content/sdk/usage/intros/fixedincome.md,icespread -website/content/sdk/usage/intros/fixedincome.md,all -website/content/sdk/usage/intros/fixedincome.md,ameribor -website/content/sdk/usage/intros/fixedincome.md,hqm -website/content/sdk/usage/intros/fixedincome.md,treasury -website/content/sdk/usage/intros/forecast.md,combine -website/content/sdk/usage/intros/forecast.md,plot -website/content/sdk/usage/intros/forecast.md,to -website/content/sdk/usage/intros/forecast.md,historical -website/content/sdk/usage/intros/forecast.md,ticker -website/content/sdk/usage/intros/forecast.md,price -website/content/sdk/usage/intros/forecast.md,export -website/content/sdk/usage/intros/forecast.md,stocks -website/content/sdk/usage/intros/forecast.md,load -website/content/sdk/usage/intros/forecast.md,analysis -website/content/sdk/usage/intros/forecast.md,show -website/content/sdk/usage/intros/forecast.md,add -website/content/sdk/usage/intros/forecast.md,delete -website/content/sdk/usage/intros/forecast.md,clean -website/content/sdk/usage/intros/forecast.md,rename -website/content/sdk/usage/intros/forecast.md,sto -website/content/sdk/usage/intros/forecast.md,forecast -website/content/sdk/usage/intros/forecast.md,forecasting -website/content/sdk/usage/intros/forecast.md,ema -website/content/sdk/usage/intros/forecast.md,prices -website/content/sdk/usage/intros/forecast.md,corr -website/content/sdk/usage/intros/forecast.md,from -website/content/sdk/usage/intros/forecast.md,metric -website/content/sdk/usage/intros/forecast.md,period -website/content/sdk/usage/intros/forecast.md,rsi -website/content/sdk/usage/intros/forecast.md,indicators -website/content/sdk/usage/intros/forecast.md,equal -website/content/sdk/usage/intros/forex.md,from -website/content/sdk/usage/intros/forex.md,atr -website/content/sdk/usage/intros/forex.md,fwd -website/content/sdk/usage/intros/forex.md,ta -website/content/sdk/usage/intros/forex.md,forex -website/content/sdk/usage/intros/forex.md,source -website/content/sdk/usage/intros/forex.md,oanda -website/content/sdk/usage/intros/forex.md,load -website/content/sdk/usage/intros/futures.md,symbol -website/content/sdk/usage/intros/futures.md,futures -website/content/sdk/usage/intros/futures.md,curve -website/content/sdk/usage/intros/futures.md,rename -website/content/sdk/usage/intros/futures.md,historical -website/content/sdk/usage/intros/futures.md,from -website/content/sdk/usage/intros/futures.md,search -website/content/sdk/usage/intros/quantitative-analysis.md,qa -website/content/sdk/usage/intros/quantitative-analysis.md,get -website/content/sdk/usage/intros/quantitative-analysis.md,symbol -website/content/sdk/usage/intros/quantitative-analysis.md,spread -website/content/sdk/usage/intros/quantitative-analysis.md,unitroot -website/content/sdk/usage/intros/quantitative-analysis.md,rename -website/content/sdk/usage/intros/quantitative-analysis.md,summary -website/content/sdk/usage/intros/quantitative-analysis.md,order -website/content/sdk/usage/intros/ta.md,obv -website/content/sdk/usage/intros/ta.md,atr -website/content/sdk/usage/intros/ta.md,ticker -website/content/sdk/usage/intros/ta.md,es -website/content/sdk/usage/intros/ta.md,donchian -website/content/sdk/usage/intros/ta.md,symbol -website/content/sdk/usage/intros/ta.md,ta -website/content/sdk/usage/intros/ta.md,stocks -website/content/sdk/usage/intros/ta.md,load -website/content/sdk/usage/intros/ta.md,rsi -website/content/terminal/usage/basics/index.md,source -website/content/terminal/usage/basics/index.md,show -website/content/terminal/usage/basics/index.md,analysis -website/content/terminal/usage/basics/index.md,performance -website/content/terminal/usage/basics/index.md,save -website/content/terminal/usage/basics/index.md,market -website/content/terminal/usage/basics/index.md,yahoo -website/content/terminal/usage/basics/index.md,about -website/content/terminal/usage/basics/index.md,select -website/content/terminal/usage/basics/index.md,exchange -website/content/terminal/usage/basics/index.md,candle -website/content/terminal/usage/basics/index.md,period -website/content/terminal/usage/basics/index.md,stocks -website/content/terminal/usage/basics/index.md,ticker -website/content/terminal/usage/basics/index.md,from -website/content/terminal/usage/basics/index.md,search -website/content/terminal/usage/basics/index.md,to -website/content/terminal/usage/basics/index.md,export -website/content/terminal/usage/basics/index.md,file -website/content/terminal/usage/basics/index.md,load -website/content/terminal/usage/basics/index.md,raw -website/content/terminal/usage/data/api-keys.md,options -website/content/terminal/usage/data/api-keys.md,fa -website/content/terminal/usage/data/api-keys.md,keys -website/content/terminal/usage/data/api-keys.md,stocks -website/content/terminal/usage/data/api-keys.md,not defined -website/content/terminal/usage/data/api-keys.md,show -website/content/terminal/usage/data/api-keys.md,fred -website/content/terminal/usage/data/api-keys.md,oanda -website/content/terminal/usage/data/api-keys.md,news -website/content/terminal/usage/data/api-keys.md,degiro -website/content/terminal/usage/data/api-keys.md,fmp -website/content/terminal/usage/data/api-keys.md,mykeys -website/content/terminal/usage/guides/changing-sources.md,save -website/content/terminal/usage/guides/changing-sources.md,raw -website/content/terminal/usage/guides/changing-sources.md,ticker -website/content/terminal/usage/guides/changing-sources.md,export -website/content/terminal/usage/guides/changing-sources.md,income -website/content/terminal/usage/guides/changing-sources.md,show -website/content/terminal/usage/guides/changing-sources.md,ratios -website/content/terminal/usage/guides/changing-sources.md,change -website/content/terminal/usage/guides/changing-sources.md,select -website/content/terminal/usage/guides/changing-sources.md,source -website/content/terminal/usage/guides/changing-sources.md,plot -website/content/terminal/usage/guides/changing-sources.md,from -website/content/terminal/usage/guides/changing-sources.md,sources -website/content/terminal/usage/guides/changing-sources.md,stocks -website/content/terminal/usage/guides/changing-sources.md,to -website/content/terminal/usage/guides/changing-sources.md,set -website/content/terminal/usage/guides/changing-sources.md,fa -website/content/terminal/usage/guides/changing-sources.md,get -website/content/terminal/usage/guides/changing-sources.md,about -website/content/terminal/usage/guides/data.md,load -website/content/terminal/usage/guides/data.md,source -website/content/terminal/usage/guides/data.md,portfolio -website/content/terminal/usage/guides/data.md,stocks -website/content/terminal/usage/guides/data.md,holdings -website/content/terminal/usage/guides/data.md,export -website/content/terminal/usage/guides/scripts-and-routines.md,treasury -website/content/terminal/usage/guides/scripts-and-routines.md,export -website/content/terminal/usage/guides/scripts-and-routines.md,run -website/content/terminal/usage/guides/scripts-and-routines.md,show -website/content/terminal/usage/guides/scripts-and-routines.md,to -website/content/terminal/usage/guides/scripts-and-routines.md,historical -website/content/terminal/usage/guides/scripts-and-routines.md,cpi -website/content/terminal/usage/guides/scripts-and-routines.md,exe -website/content/terminal/usage/guides/scripts-and-routines.md,ca -website/content/terminal/usage/guides/scripts-and-routines.md,record -website/content/terminal/usage/guides/scripts-and-routines.md,economy -website/content/terminal/usage/guides/scripts-and-routines.md,create -website/content/terminal/usage/guides/scripts-and-routines.md,options -website/content/terminal/usage/guides/scripts-and-routines.md,ta -website/content/terminal/usage/guides/scripts-and-routines.md,about -website/content/terminal/usage/guides/scripts-and-routines.md,stop -website/content/terminal/usage/guides/scripts-and-routines.md,file -website/content/terminal/usage/guides/scripts-and-routines.md,ticker -website/content/terminal/usage/guides/scripts-and-routines.md,analysis -website/content/terminal/usage/guides/scripts-and-routines.md,stocks -website/content/terminal/usage/guides/scripts-and-routines.md,type -website/content/terminal/usage/guides/scripts-and-routines.md,price -website/content/terminal/usage/guides/scripts-and-routines.md,candle -website/content/terminal/usage/guides/scripts-and-routines.md,index -website/content/terminal/usage/intros/econometrics.md,granger -website/content/terminal/usage/intros/econometrics.md,compare -website/content/terminal/usage/intros/econometrics.md,combine -website/content/terminal/usage/intros/econometrics.md,all -website/content/terminal/usage/intros/econometrics.md,rename -website/content/terminal/usage/intros/econometrics.md,set -website/content/terminal/usage/intros/econometrics.md,to -website/content/terminal/usage/intros/econometrics.md,add -website/content/terminal/usage/intros/econometrics.md,index -website/content/terminal/usage/intros/econometrics.md,ols -website/content/terminal/usage/intros/econometrics.md,from -website/content/terminal/usage/intros/econometrics.md,norm -website/content/terminal/usage/intros/econometrics.md,root -website/content/terminal/usage/intros/econometrics.md,coint -website/content/terminal/usage/intros/econometrics.md,panel -website/content/terminal/usage/intros/econometrics.md,dwat -website/content/terminal/usage/intros/econometrics.md,bgod -website/content/terminal/usage/intros/econometrics.md,bpag -website/content/terminal/usage/intros/econometrics.md,plot -website/content/terminal/usage/intros/econometrics.md,desc -website/content/terminal/usage/intros/econometrics.md,so -website/content/terminal/usage/intros/econometrics.md,export -website/content/terminal/usage/intros/econometrics.md,show -website/content/terminal/usage/intros/econometrics.md,type -website/content/terminal/usage/intros/econometrics.md,load -website/content/terminal/usage/intros/econometrics.md,fp -website/content/terminal/usage/intros/econometrics.md,change -website/content/terminal/usage/intros/econometrics.md,delete -website/content/terminal/usage/intros/econometrics.md,file -website/content/terminal/usage/intros/econometrics.md,econometrics -website/content/terminal/usage/intros/econometrics.md,unitroot -website/content/terminal/usage/intros/econometrics.md,stats -website/content/terminal/usage/intros/econometrics.md,interest -website/content/terminal/usage/intros/econometrics.md,remove -website/content/terminal/usage/intros/econometrics.md,normality -website/content/terminal/usage/intros/econometrics.md,multi -website/content/terminal/usage/intros/econometrics.md,clean -website/content/terminal/usage/intros/economy.md,futures -website/content/terminal/usage/intros/economy.md,curve -website/content/terminal/usage/intros/economy.md,gov -website/content/terminal/usage/intros/economy.md,sector -website/content/terminal/usage/intros/economy.md,price -website/content/terminal/usage/intros/economy.md,update -website/content/terminal/usage/intros/economy.md,hold -website/content/terminal/usage/intros/economy.md,macro -website/content/terminal/usage/intros/economy.md,overview -website/content/terminal/usage/intros/economy.md,period -website/content/terminal/usage/intros/economy.md,plot -website/content/terminal/usage/intros/economy.md,ownership -website/content/terminal/usage/intros/economy.md,rates -website/content/terminal/usage/intros/economy.md,to -website/content/terminal/usage/intros/economy.md,from -website/content/terminal/usage/intros/economy.md,interest -website/content/terminal/usage/intros/economy.md,treasury -website/content/terminal/usage/intros/economy.md,change -website/content/terminal/usage/intros/economy.md,fred -website/content/terminal/usage/intros/economy.md,show -website/content/terminal/usage/intros/economy.md,performance -website/content/terminal/usage/intros/economy.md,economy -website/content/terminal/usage/intros/economy.md,stocks -website/content/terminal/usage/intros/economy.md,all -website/content/terminal/usage/intros/economy.md,market -website/content/terminal/usage/intros/economy.md,index -website/content/terminal/usage/intros/etf.md,market -website/content/terminal/usage/intros/etf.md,search -website/content/terminal/usage/intros/etf.md,candle -website/content/terminal/usage/intros/etf.md,load -website/content/terminal/usage/intros/etf.md,weights -website/content/terminal/usage/intros/etf.md,period -website/content/terminal/usage/intros/etf.md,etf -website/content/terminal/usage/intros/etf.md,raw -website/content/terminal/usage/intros/etf.md,holdings -website/content/terminal/usage/intros/fixedincome.md,treasury -website/content/terminal/usage/intros/fixedincome.md,export -website/content/terminal/usage/intros/fixedincome.md,ecb -website/content/terminal/usage/intros/fixedincome.md,ameribor -website/content/terminal/usage/intros/fixedincome.md,spread -website/content/terminal/usage/intros/fixedincome.md,fixedincome -website/content/terminal/usage/intros/fixedincome.md,options -website/content/terminal/usage/intros/fixedincome.md,all -website/content/terminal/usage/intros/fixedincome.md,raw -website/content/terminal/usage/intros/fixedincome.md,icespread -website/content/terminal/usage/intros/fixedincome.md,type -website/content/terminal/usage/intros/forecast.md,type -website/content/terminal/usage/intros/forecast.md,brnn -website/content/terminal/usage/intros/forecast.md,show -website/content/terminal/usage/intros/forecast.md,stocks -website/content/terminal/usage/intros/forecast.md,all -website/content/terminal/usage/intros/forecast.md,set -website/content/terminal/usage/intros/forecast.md,plot -website/content/terminal/usage/intros/forecast.md,select -website/content/terminal/usage/intros/forecast.md,rolling -website/content/terminal/usage/intros/forecast.md,export -website/content/terminal/usage/intros/forecast.md,combine -website/content/terminal/usage/intros/forecast.md,save -website/content/terminal/usage/intros/forecast.md,rnn -website/content/terminal/usage/intros/forecast.md,expo -website/content/terminal/usage/intros/forecast.md,price -website/content/terminal/usage/intros/forecast.md,mom -website/content/terminal/usage/intros/forecast.md,forecasting -website/content/terminal/usage/intros/forecast.md,load -website/content/terminal/usage/intros/forecast.md,run -website/content/terminal/usage/intros/forecast.md,volume -website/content/terminal/usage/intros/forecast.md,desc -website/content/terminal/usage/intros/forecast.md,forecast -website/content/terminal/usage/intros/forecast.md,open -website/content/terminal/usage/intros/forecast.md,to -website/content/terminal/usage/intros/forex.md,quote -website/content/terminal/usage/intros/forex.md,fwd -website/content/terminal/usage/intros/forex.md,forex -website/content/terminal/usage/intros/forex.md,candle -website/content/terminal/usage/intros/forex.md,load -website/content/terminal/usage/intros/forex.md,rates -website/content/terminal/usage/intros/forex.md,qa -website/content/terminal/usage/intros/futures.md,export -website/content/terminal/usage/intros/futures.md,curve -website/content/terminal/usage/intros/futures.md,raw -website/content/terminal/usage/intros/futures.md,search diff --git a/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands.json b/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands.json deleted file mode 100644 index 35951a6867b..00000000000 --- a/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands.json +++ /dev/null @@ -1,845 +0,0 @@ -{ - "update": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/sdk/faqs/developer_issues.md", - "website/content/terminal/usage/intros/economy.md" - ], - "news": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/api-keys.md" - ], - "show": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "list": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/etf.md", - "website/content/sdk/usage/intros/economy.md" - ], - "download": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/intros/economy.md" - ], - "status": [ - "CONTRIBUTING.md" - ], - "defined, test passed": [ - "CONTRIBUTING.md" - ], - "defined, test failed": [ - "CONTRIBUTING.md" - ], - "not defined": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/api-keys.md" - ], - "defined, test inconclusive": [ - "CONTRIBUTING.md" - ], - "get": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "set": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/api-keys.md" - ], - "cls": [ - "CONTRIBUTING.md" - ], - "source": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/sdk/faqs/data_sources.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/data.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/sdk/usage/guides/changing-sources.md", - "website/content/sdk/usage/intros/forex.md" - ], - "stocks": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/sdk/faqs/data_sources.md", - "website/content/sdk/faqs/general_operation.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/data.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/guides/changing-sources.md", - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/etf.md", - "website/content/sdk/usage/intros/ta.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "crypto": [ - "CONTRIBUTING.md" - ], - "economy": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/economy.md" - ], - "forex": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/intros/forex.md", - "website/content/sdk/usage/intros/forex.md" - ], - "search": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/intros/futures.md", - "website/content/terminal/usage/intros/etf.md", - "website/content/sdk/usage/intros/futures.md" - ], - "forecast": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "portfolio": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/guides/data.md", - "website/content/sdk/usage/guides/data.md" - ], - "load": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/sdk/faqs/data_sources.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/data.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/etf.md", - "website/content/terminal/usage/intros/forex.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/guides/changing-sources.md", - "website/content/sdk/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/ta.md", - "website/content/sdk/usage/intros/forex.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "quote": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/intros/forex.md", - "website/content/sdk/usage/api-keys.md" - ], - "candle": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/intros/etf.md", - "website/content/terminal/usage/intros/forex.md", - "website/content/sdk/usage/basics/index.md" - ], - "options": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/sdk/usage/intros/fixedincome.md" - ], - "dps": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md" - ], - "scr": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/intros/etf.md" - ], - "fa": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/guides/changing-sources.md" - ], - "ta": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/ta.md", - "website/content/sdk/usage/intros/forex.md" - ], - "qa": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/intros/forex.md", - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "unu": [ - "CONTRIBUTING.md" - ], - "calc": [ - "CONTRIBUTING.md" - ], - "screen": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/intros/etf.md" - ], - "exp": [ - "CONTRIBUTING.md" - ], - "info": [ - "CONTRIBUTING.md" - ], - "hist": [ - "CONTRIBUTING.md" - ], - "grhist": [ - "CONTRIBUTING.md" - ], - "plot": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "view": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/intros/economy.md" - ], - "add": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "symbol": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/guides/changing-sources.md", - "website/content/sdk/usage/intros/futures.md", - "website/content/sdk/usage/intros/economy.md", - "website/content/sdk/usage/intros/quantitative-analysis.md", - "website/content/sdk/usage/intros/ta.md" - ], - "country": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/intros/alternative.md", - "website/content/sdk/usage/intros/economy.md" - ], - "period": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/intros/etf.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "shorted": [ - "CONTRIBUTING.md" - ], - "psi": [ - "CONTRIBUTING.md" - ], - "contracts": [ - "CONTRIBUTING.md" - ], - "ticker": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/sdk/usage/intros/ta.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "rot": [ - "CONTRIBUTING.md" - ], - "pt": [ - "CONTRIBUTING.md" - ], - "rolling": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/intros/forecast.md" - ], - "raw": [ - "CONTRIBUTING.md", - "website/content/sdk/faqs/installation_updates.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/terminal/usage/intros/futures.md", - "website/content/terminal/usage/intros/etf.md" - ], - "price": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "dd": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/basics/index.md" - ], - "top": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/intros/alternative.md" - ], - "events": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/economy.md" - ], - "mt": [ - "CONTRIBUTING.md" - ], - "holdings": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/guides/data.md", - "website/content/terminal/usage/intros/etf.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/guides/data.md", - "website/content/sdk/usage/intros/etf.md" - ], - "fwd": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/intros/forex.md", - "website/content/sdk/usage/intros/forex.md" - ], - "oanda": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/forex.md" - ], - "calendar": [ - "CONTRIBUTING.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/economy.md" - ], - "from": [ - "CONTRIBUTING.md", - "openbb_terminal/SDK_README.md", - "website/content/sdk/faqs/general_operation.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/guides/chart-styling.md", - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/fixedincome.md", - "website/content/sdk/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/futures.md", - "website/content/sdk/usage/intros/alternative.md", - "website/content/sdk/usage/intros/etf.md", - "website/content/sdk/usage/intros/economy.md", - "website/content/sdk/usage/intros/forex.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "to": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/economy.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "export": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/data.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/futures.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "type": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/economy.md" - ], - "file": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/intros/econometrics.md" - ], - "support": [ - "README.md" - ], - "keys": [ - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/economy.md" - ], - "mykeys": [ - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/api-keys.md" - ], - "dpi": [ - "openbb_terminal/SDK_README.md" - ], - "all": [ - "openbb_terminal/SDK_README.md", - "website/content/sdk/faqs/installation_updates.md", - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/fixedincome.md", - "website/content/sdk/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/economy.md" - ], - "dpotc": [ - "openbb_terminal/SDK_README.md" - ], - "fmp": [ - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/api-keys.md" - ], - "categories": [ - "openbb_terminal/SDK_README.md", - "website/content/sdk/usage/intros/alternative.md", - "website/content/sdk/usage/intros/etf.md" - ], - "weights": [ - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/intros/etf.md" - ], - "index": [ - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/etf.md", - "website/content/sdk/usage/intros/economy.md" - ], - "fed": [ - "openbb_terminal/SDK_README.md" - ], - "po": [ - "openbb_terminal/SDK_README.md" - ], - "create": [ - "openbb_terminal/SDK_README.md", - "website/content/terminal/usage/guides/scripts-and-routines.md" - ], - "maxsharpe": [ - "openbb_terminal/SDK_README.md" - ], - "minrisk": [ - "openbb_terminal/SDK_README.md" - ], - "hrp": [ - "openbb_terminal/SDK_README.md" - ], - "sh": [ - "website/content/sdk/faqs/installation_updates.md" - ], - "global": [ - "website/content/sdk/faqs/developer_issues.md", - "website/content/sdk/usage/api-keys.md" - ], - "rm": [ - "website/content/sdk/faqs/developer_issues.md" - ], - "remove": [ - "website/content/sdk/faqs/developer_issues.md", - "website/content/terminal/usage/intros/econometrics.md" - ], - "clean": [ - "website/content/sdk/faqs/developer_issues.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "exchange": [ - "website/content/sdk/faqs/data_sources.md", - "website/content/terminal/usage/basics/index.md" - ], - "about": [ - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/guides/changing-sources.md" - ], - "select": [ - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/forecast.md" - ], - "performance": [ - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/intros/etf.md", - "website/content/sdk/usage/intros/economy.md" - ], - "analysis": [ - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "yahoo": [ - "website/content/terminal/usage/basics/index.md" - ], - "market": [ - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/intros/etf.md", - "website/content/terminal/usage/intros/economy.md" - ], - "save": [ - "website/content/terminal/usage/basics/index.md", - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/forecast.md" - ], - "record": [ - "website/content/terminal/usage/guides/scripts-and-routines.md" - ], - "stop": [ - "website/content/terminal/usage/guides/scripts-and-routines.md" - ], - "exe": [ - "website/content/terminal/usage/guides/scripts-and-routines.md" - ], - "historical": [ - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/sdk/usage/intros/futures.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "ca": [ - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/etf.md" - ], - "cpi": [ - "website/content/terminal/usage/guides/scripts-and-routines.md" - ], - "treasury": [ - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/intros/fixedincome.md" - ], - "run": [ - "website/content/terminal/usage/guides/scripts-and-routines.md", - "website/content/terminal/usage/intros/forecast.md" - ], - "sources": [ - "website/content/terminal/usage/guides/changing-sources.md" - ], - "income": [ - "website/content/terminal/usage/guides/changing-sources.md" - ], - "ratios": [ - "website/content/terminal/usage/guides/changing-sources.md" - ], - "change": [ - "website/content/terminal/usage/guides/changing-sources.md", - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/api-keys.md" - ], - "fred": [ - "website/content/terminal/usage/data/api-keys.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/economy.md" - ], - "degiro": [ - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/api-keys.md" - ], - "fixedincome": [ - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/sdk/usage/intros/fixedincome.md" - ], - "spread": [ - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "ameribor": [ - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/sdk/usage/intros/fixedincome.md" - ], - "ecb": [ - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/sdk/usage/intros/fixedincome.md" - ], - "icespread": [ - "website/content/terminal/usage/intros/fixedincome.md", - "website/content/sdk/usage/intros/fixedincome.md" - ], - "delete": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "econometrics": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/econometrics.md" - ], - "stats": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "interest": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/economy.md" - ], - "multi": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "normality": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "unitroot": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "so": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "fp": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "desc": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/forecast.md" - ], - "compare": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "combine": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "rename": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/futures.md", - "website/content/sdk/usage/intros/quantitative-analysis.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "ols": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/econometrics.md" - ], - "norm": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/econometrics.md" - ], - "root": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/econometrics.md" - ], - "panel": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "dwat": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "bgod": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/econometrics.md" - ], - "bpag": [ - "website/content/terminal/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/econometrics.md" - ], - "granger": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "coint": [ - "website/content/terminal/usage/intros/econometrics.md" - ], - "curve": [ - "website/content/terminal/usage/intros/futures.md", - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/intros/futures.md" - ], - "etf": [ - "website/content/terminal/usage/intros/etf.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/etf.md" - ], - "futures": [ - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/futures.md" - ], - "gov": [ - "website/content/terminal/usage/intros/economy.md" - ], - "sector": [ - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/intros/economy.md" - ], - "overview": [ - "website/content/terminal/usage/intros/economy.md" - ], - "ownership": [ - "website/content/terminal/usage/intros/economy.md" - ], - "hold": [ - "website/content/terminal/usage/intros/economy.md" - ], - "macro": [ - "website/content/terminal/usage/intros/economy.md", - "website/content/sdk/usage/intros/economy.md" - ], - "rates": [ - "website/content/terminal/usage/intros/economy.md", - "website/content/terminal/usage/intros/forex.md" - ], - "open": [ - "website/content/terminal/usage/intros/forecast.md" - ], - "volume": [ - "website/content/terminal/usage/intros/forecast.md" - ], - "forecasting": [ - "website/content/terminal/usage/intros/forecast.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "mom": [ - "website/content/terminal/usage/intros/forecast.md" - ], - "expo": [ - "website/content/terminal/usage/intros/forecast.md" - ], - "rnn": [ - "website/content/terminal/usage/intros/forecast.md" - ], - "brnn": [ - "website/content/terminal/usage/intros/forecast.md" - ], - "valuation": [ - "website/content/sdk/usage/basics/index.md" - ], - "filter": [ - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/economy.md" - ], - "donchian": [ - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/ta.md" - ], - "account": [ - "website/content/sdk/usage/api-keys.md" - ], - "projection": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "icebofa": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "hqm": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "summary": [ - "website/content/sdk/usage/intros/econometrics.md", - "website/content/sdk/usage/intros/alternative.md", - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "correlation": [ - "website/content/sdk/usage/intros/econometrics.md" - ], - "covid": [ - "website/content/sdk/usage/intros/alternative.md" - ], - "oss": [ - "website/content/sdk/usage/intros/alternative.md" - ], - "slopes": [ - "website/content/sdk/usage/intros/alternative.md" - ], - "disc": [ - "website/content/sdk/usage/intros/etf.md" - ], - "gainers": [ - "website/content/sdk/usage/intros/etf.md" - ], - "active": [ - "website/content/sdk/usage/intros/etf.md" - ], - "decliners": [ - "website/content/sdk/usage/intros/etf.md" - ], - "movers": [ - "website/content/sdk/usage/intros/etf.md" - ], - "order": [ - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "rsi": [ - "website/content/sdk/usage/intros/ta.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "atr": [ - "website/content/sdk/usage/intros/ta.md", - "website/content/sdk/usage/intros/forex.md" - ], - "obv": [ - "website/content/sdk/usage/intros/ta.md" - ], - "es": [ - "website/content/sdk/usage/intros/ta.md" - ], - "metric": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "ema": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "prices": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "corr": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "equal": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "indicators": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "sto": [ - "website/content/sdk/usage/intros/forecast.md" - ] -} diff --git a/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk.csv b/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk.csv deleted file mode 100644 index 206bd1c007c..00000000000 --- a/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk.csv +++ /dev/null @@ -1,113 +0,0 @@ -file,command -CONTRIBUTING.md,news -CONTRIBUTING.md,stocks.fa.analyst -CONTRIBUTING.md,stocks.fa.pt -CONTRIBUTING.md,stocks.fa.rot -openbb_terminal/SDK_README.md,stocks.dps.dpotc -openbb_terminal/SDK_README.md,keys.set_keys -openbb_terminal/SDK_README.md,keys.reddit -openbb_terminal/SDK_README.md,stocks.search -openbb_terminal/SDK_README.md,keys.mykeys -openbb_terminal/SDK_README.md,keys.get_keys_info -openbb_terminal/SDK_README.md,portfolio.po.hrp -openbb_terminal/SDK_README.md,keys.fmp -openbb_terminal/SDK_README.md,portfolio.po.load -openbb_terminal/SDK_README.md,economy.index -openbb_terminal/SDK_README.md,portfolio.po.maxsharpe -openbb_terminal/SDK_README.md,economy.available_indices -openbb_terminal/SDK_README.md,portfolio.po.minrisk -openbb_terminal/SDK_README.md,portfolio.po.plot -openbb_terminal/SDK_README.md,stocks.load -website/content/sdk/faqs/data_sources.md,stocks.load -website/content/sdk/usage/basics/index.md,stocks.ca.screener -website/content/sdk/usage/basics/index.md,stocks.candle -website/content/sdk/usage/basics/index.md,economy.events -website/content/sdk/usage/basics/index.md,ta.donchian -website/content/sdk/usage/basics/index.md,stocks.load -website/content/sdk/usage/basics/index.md,etf.holdings -website/content/sdk/usage/api-keys.md,keys.github -website/content/sdk/usage/api-keys.md,keys.glassnode -website/content/sdk/usage/api-keys.md,keys.fred -website/content/sdk/usage/api-keys.md,keys.finnhub -website/content/sdk/usage/api-keys.md,keys.eodhd -website/content/sdk/usage/api-keys.md,keys.degiro -website/content/sdk/usage/api-keys.md,keys.databento -website/content/sdk/usage/api-keys.md,keys.quandl -website/content/sdk/usage/api-keys.md,keys.oanda -website/content/sdk/usage/api-keys.md,keys.polygon -website/content/sdk/usage/api-keys.md,keys.cpanic -website/content/sdk/usage/api-keys.md,keys.santiment -website/content/sdk/usage/api-keys.md,keys.stocksera -website/content/sdk/usage/api-keys.md,keys.tokenterminal -website/content/sdk/usage/api-keys.md,keys.tradier -website/content/sdk/usage/api-keys.md,keys.walert -website/content/sdk/usage/api-keys.md,stocks.quote -website/content/sdk/usage/api-keys.md,keys.news -website/content/sdk/usage/api-keys.md,keys.coinglass -website/content/sdk/usage/api-keys.md,keys.messari -website/content/sdk/usage/api-keys.md,keys.cmc -website/content/sdk/usage/api-keys.md,keys.fmp -website/content/sdk/usage/api-keys.md,keys.mykeys -website/content/sdk/usage/api-keys.md,keys.reddit -website/content/sdk/usage/api-keys.md,keys.coinbase -website/content/sdk/usage/api-keys.md,news -website/content/sdk/usage/api-keys.md,keys.av -website/content/sdk/usage/api-keys.md,keys.binance -website/content/sdk/usage/api-keys.md,keys.bitquery -website/content/sdk/usage/guides/changing-sources.md,stocks.load -website/content/sdk/usage/intros/alternative.md,alt.covid.slopes -website/content/sdk/usage/intros/alternative.md,alt.covid.global_deaths -website/content/sdk/usage/intros/alternative.md,alt.oss.top -website/content/sdk/usage/intros/alternative.md,alt.oss.summary -website/content/sdk/usage/intros/econometrics.md,econometrics.root -website/content/sdk/usage/intros/econometrics.md,econometrics.bgod -website/content/sdk/usage/intros/econometrics.md,econometrics.ols -website/content/sdk/usage/intros/econometrics.md,econometrics.load -website/content/sdk/usage/intros/econometrics.md,econometrics.bpag -website/content/sdk/usage/intros/econometrics.md,econometrics.re -website/content/sdk/usage/intros/econometrics.md,econometrics.norm -website/content/sdk/usage/intros/economy.md,economy.available_indices -website/content/sdk/usage/intros/economy.md,keys.av -website/content/sdk/usage/intros/economy.md,keys.fred -website/content/sdk/usage/intros/economy.md,economy.macro_countries -website/content/sdk/usage/intros/economy.md,economy.macro_parameters -website/content/sdk/usage/intros/economy.md,economy.performance -website/content/sdk/usage/intros/economy.md,economy.index -website/content/sdk/usage/intros/economy.md,keys.quandl -website/content/sdk/usage/intros/economy.md,economy.macro -website/content/sdk/usage/intros/economy.md,economy.events -website/content/sdk/usage/intros/etf.md,etf.holdings -website/content/sdk/usage/intros/etf.md,etf.disc.mover -website/content/sdk/usage/intros/etf.md,etf.etf_by_category -website/content/sdk/usage/intros/etf.md,stocks.ca.screener -website/content/sdk/usage/intros/fixedincome.md,fixedincome.icebofa -website/content/sdk/usage/intros/fixedincome.md,fixedincome.icespread -website/content/sdk/usage/intros/fixedincome.md,fixedincome.ameribor -website/content/sdk/usage/intros/fixedincome.md,fixedincome.projection -website/content/sdk/usage/intros/fixedincome.md,fixedincome.treasury -website/content/sdk/usage/intros/fixedincome.md,fixedincome.ecb -website/content/sdk/usage/intros/fixedincome.md,fixedincome.hqm -website/content/sdk/usage/intros/forecast.md,forecast.sto -website/content/sdk/usage/intros/forecast.md,forecast.rename -website/content/sdk/usage/intros/forecast.md,forecast.rsi -website/content/sdk/usage/intros/forecast.md,forecast.combine -website/content/sdk/usage/intros/forecast.md,forecast.corr -website/content/sdk/usage/intros/forecast.md,forecast.delete -website/content/sdk/usage/intros/forecast.md,stocks.load -website/content/sdk/usage/intros/forecast.md,forecast.plot -website/content/sdk/usage/intros/forecast.md,forecast.ema -website/content/sdk/usage/intros/forex.md,forex.load -website/content/sdk/usage/intros/forex.md,forex.fwd -website/content/sdk/usage/intros/forex.md,ta.atr -website/content/sdk/usage/intros/futures.md,futures.search -website/content/sdk/usage/intros/futures.md,futures.historical -website/content/sdk/usage/intros/futures.md,futures.curve -website/content/sdk/usage/intros/quantitative-analysis.md,qa.summary -website/content/sdk/usage/intros/quantitative-analysis.md,qa.unitroot -website/content/sdk/usage/intros/quantitative-analysis.md,qa.spread -website/content/sdk/usage/intros/ta.md,ta.obv -website/content/sdk/usage/intros/ta.md,ta.atr -website/content/sdk/usage/intros/ta.md,ta.rsi -website/content/sdk/usage/intros/ta.md,stocks.load -website/content/sdk/usage/intros/ta.md,ta.donchian -website/content/terminal/usage/data/api-keys.md,news diff --git a/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk.json b/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk.json deleted file mode 100644 index 243925ed3b1..00000000000 --- a/openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk.json +++ /dev/null @@ -1,301 +0,0 @@ -{ - "news": [ - "CONTRIBUTING.md", - "website/content/terminal/usage/data/api-keys.md", - "website/content/sdk/usage/api-keys.md" - ], - "stocks.dps.shorted": [ - "CONTRIBUTING.md" - ], - "stocks.fa.analyst": [ - "CONTRIBUTING.md" - ], - "stocks.fa.rot": [ - "CONTRIBUTING.md" - ], - "economy.available_indices": [ - "openbb_terminal/SDK_README.md", - "website/content/sdk/usage/intros/economy.md" - ], - "economy.index": [ - "openbb_terminal/SDK_README.md", - "website/content/sdk/usage/intros/economy.md" - ], - "keys.fmp": [ - "openbb_terminal/SDK_README.md", - "website/content/sdk/usage/api-keys.md" - ], - "keys.get_keys_info": [ - "openbb_terminal/SDK_README.md" - ], - "keys.mykeys": [ - "openbb_terminal/SDK_README.md", - "website/content/sdk/usage/api-keys.md" - ], - "keys.reddit": [ - "openbb_terminal/SDK_README.md", - "website/content/sdk/usage/api-keys.md" - ], - "keys.set_keys": [ - "openbb_terminal/SDK_README.md" - ], - "stocks.dps.dpotc": [ - "openbb_terminal/SDK_README.md" - ], - "stocks.load": [ - "openbb_terminal/SDK_README.md", - "website/content/sdk/faqs/data_sources.md", - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/guides/changing-sources.md", - "website/content/sdk/usage/intros/ta.md", - "website/content/sdk/usage/intros/forecast.md" - ], - "stocks.search": [ - "openbb_terminal/SDK_README.md" - ], - "portfolio.po.hrp": [ - "openbb_terminal/SDK_README.md" - ], - "portfolio.po.load": [ - "openbb_terminal/SDK_README.md" - ], - "portfolio.po.maxsharpe": [ - "openbb_terminal/SDK_README.md" - ], - "portfolio.po.minrisk": [ - "openbb_terminal/SDK_README.md" - ], - "portfolio.po.plot": [ - "openbb_terminal/SDK_README.md" - ], - "economy.events": [ - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/economy.md" - ], - "etf.holdings": [ - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/etf.md" - ], - "stocks.ca.screener": [ - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/etf.md" - ], - "stocks.candle": [ - "website/content/sdk/usage/basics/index.md" - ], - "ta.donchian": [ - "website/content/sdk/usage/basics/index.md", - "website/content/sdk/usage/intros/ta.md" - ], - "keys.av": [ - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/economy.md" - ], - "keys.binance": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.bitquery": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.cmc": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.coinbase": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.coinglass": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.cpanic": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.databento": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.degiro": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.eodhd": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.finnhub": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.fred": [ - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/economy.md" - ], - "keys.github": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.glassnode": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.messari": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.news": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.oanda": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.polygon": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.quandl": [ - "website/content/sdk/usage/api-keys.md", - "website/content/sdk/usage/intros/economy.md" - ], - "keys.santiment": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.shroom": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.stocksera": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.tokenterminal": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.tradier": [ - "website/content/sdk/usage/api-keys.md" - ], - "keys.walert": [ - "website/content/sdk/usage/api-keys.md" - ], - "stocks.quote": [ - "website/content/sdk/usage/api-keys.md" - ], - "fixedincome.ameribor": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "fixedincome.ecb": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "fixedincome.hqm": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "fixedincome.icebofa": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "fixedincome.icespread": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "fixedincome.projection": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "fixedincome.treasury": [ - "website/content/sdk/usage/intros/fixedincome.md" - ], - "econometrics.bgod": [ - "website/content/sdk/usage/intros/econometrics.md" - ], - "econometrics.bpag": [ - "website/content/sdk/usage/intros/econometrics.md" - ], - "econometrics.load": [ - "website/content/sdk/usage/intros/econometrics.md" - ], - "econometrics.norm": [ - "website/content/sdk/usage/intros/econometrics.md" - ], - "econometrics.ols": [ - "website/content/sdk/usage/intros/econometrics.md" - ], - "econometrics.re": [ - "website/content/sdk/usage/intros/econometrics.md" - ], - "econometrics.root": [ - "website/content/sdk/usage/intros/econometrics.md" - ], - "futures.curve": [ - "website/content/sdk/usage/intros/futures.md" - ], - "futures.historical": [ - "website/content/sdk/usage/intros/futures.md" - ], - "futures.search": [ - "website/content/sdk/usage/intros/futures.md" - ], - "alt.covid.global_deaths": [ - "website/content/sdk/usage/intros/alternative.md" - ], - "alt.covid.slopes": [ - "website/content/sdk/usage/intros/alternative.md" - ], - "alt.oss.summary": [ - "website/content/sdk/usage/intros/alternative.md" - ], - "alt.oss.top": [ - "website/content/sdk/usage/intros/alternative.md" - ], - "etf.disc.mover": [ - "website/content/sdk/usage/intros/etf.md" - ], - "etf.etf_by_category": [ - "website/content/sdk/usage/intros/etf.md" - ], - "economy.macro": [ - "website/content/sdk/usage/intros/economy.md" - ], - "economy.macro_countries": [ - "website/content/sdk/usage/intros/economy.md" - ], - "economy.macro_parameters": [ - "website/content/sdk/usage/intros/economy.md" - ], - "economy.performance": [ - "website/content/sdk/usage/intros/economy.md" - ], - "qa.spread": [ - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "qa.summary": [ - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "qa.unitroot": [ - "website/content/sdk/usage/intros/quantitative-analysis.md" - ], - "ta.atr": [ - "website/content/sdk/usage/intros/ta.md", - "website/content/sdk/usage/intros/forex.md" - ], - "ta.obv": [ - "website/content/sdk/usage/intros/ta.md" - ], - "ta.rsi": [ - "website/content/sdk/usage/intros/ta.md" - ], - "forex.fwd": [ - "website/content/sdk/usage/intros/forex.md" - ], - "forex.load": [ - "website/content/sdk/usage/intros/forex.md" - ], - "forecast.combine": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "forecast.corr": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "forecast.delete": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "forecast.ema": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "forecast.plot": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "forecast.rename": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "forecast.rsi": [ - "website/content/sdk/usage/intros/forecast.md" - ], - "forecast.sto": [ - "website/content/sdk/usage/intros/forecast.md" - ] -} diff --git a/openbb_terminal/miscellaneous/documentation_scripts/generate_documentation_commands.py b/openbb_terminal/miscellaneous/documentation_scripts/generate_documentation_commands.py deleted file mode 100644 index 28f8a0f6a5d..00000000000 --- a/openbb_terminal/miscellaneous/documentation_scripts/generate_documentation_commands.py +++ /dev/null @@ -1,350 +0,0 @@ -import argparse -import glob -import json -import re -from collections import defaultdict -from pathlib import Path -from typing import Literal - -import pandas as pd -import yaml - -from openbb_terminal.core.config.paths import ( - I18N_DICT_LOCATION, - MAP_FORECASTING_PATH, - MAP_OPTIMIZATION_PATH, - MAP_PATH, -) - -TRAIL_MAPS = [MAP_PATH, MAP_FORECASTING_PATH, MAP_OPTIMIZATION_PATH] - -EN_FILE = I18N_DICT_LOCATION / "en.yml" - -MD_FILES = ["CONTRIBUTING.md", "README.md", "openbb_terminal/SDK_README.md"] -MD_FILES.extend(glob.glob("website/content/sdk/faqs/*.md", recursive=True)) -MD_FILES.extend(glob.glob("website/content/terminal/usage/*.md", recursive=True)) -MD_FILES.extend(glob.glob("website/content/terminal/menus/*.md", recursive=True)) -MD_FILES.extend(glob.glob("website/content/sdk/usage/*.md", recursive=True)) -MD_FILES.extend(glob.glob("website/content/sdk/data-available/*.md", recursive=True)) - -EXPORT_FILE_PATH = ( - "openbb_terminal/miscellaneous/documentation_scripts/documentation_commands" -) -EXPORT_FILE_PATH_SDK = ( - "openbb_terminal/miscellaneous/documentation_scripts/documentation_commands_sdk" -) - - -def read_yaml_file(path: Path) -> dict: - """Read a YAML file and return its contents as a dictionary. - - Parameters - ---------- - path : Path - The path to the YAML file. - - Returns - ------- - dict - A dictionary containing the contents of the YAML file. - - Raises - ------ - yaml.YAMLError - If the YAML file could not be read or parsed, this exception is raised. - """ - with open(path) as stream: - try: - return yaml.safe_load(stream) - except yaml.YAMLError as exc: - raise exc - - -def get_command_list(en_dict: dict) -> list: - """ - Extract a list of commands from a dictionary. - The function looks for keys in the "en" dictionary of `en_dict` that do not start - with an underscore ("_"). - If a key contains a forward slash ("/"), the part of the key after the last slash - is considered to be a potential command. - If this potential command also starts with an underscore, it is ignored. - - Parameters - ---------- - en_dict : dict - A dictionary containing commands as keys. - The values of the dictionary are not used. - - Returns - ------- - list - A list of commands extracted from the dictionary. - - Examples - -------- - ``` - en_dict = { - "en": { - "run_command": "Execute a command.", - "run_command/cmd_option": "Execute a command with an option.", - "_helper": "A helper command that should be ignored." - } - } - command_list = get_command_list(en_dict) - # command_list == ["run_command", "cmd_option"] - ``` - """ - commands = [] - for key in en_dict["en"]: - if key.startswith("_"): - continue - if "/" in key: - potential_command = key.split("/")[-1] - if potential_command.startswith("_"): - continue - commands.append(potential_command) - else: - commands.append(key) - return commands - - -def get_sdk_command_list(trail_map: Path) -> list: - """ - Extract a list of commands from a CSV file. - The function reads the CSV file at the specified path into a pandas DataFrame and - extracts the "trail" column as a list of commands. - - Parameters - ---------- - trail_map : Path - A path to the CSV file containing the trail map. - - Returns - ------- - list - A list of commands extracted from the CSV file. - """ - df = pd.read_csv(trail_map) - return df["trail"].tolist() - - -def get_content_from_md_file(file_path: str, code_blocks_only: bool) -> str: - """ - Read a Markdown file and return its content, optionally including only code blocks. - - If `code_blocks_only` is True, the function finds all fenced code blocks - (using regular expression), - and all backtick code blocks in the Markdown file and concatenates them into a - single string. - The backtick code blocks can be either inline or multiline. - If `code_blocks_only` is False, the function returns the full contents of the - Markdown file as a string. - - Parameters - ---------- - file_path : str - The path to the Markdown file. - code_blocks_only : bool - If True, return only the code blocks found in the Markdown file. - If False, return the full contents of the Markdown file. By default, False. - - Returns - ------- - str - The contents of the Markdown file as a string. - - Examples - -------- - ``` - file_path = "example.md" - full_content = get_content_from_md_file(file_path, False) - code_blocks = get_content_from_md_file(file_path, True) - ``` - """ - - def get_code_blocks_from_md_file(file_path: str) -> str: - code_blocks = "" - with open(file_path) as f: - md = f.read() - - # find fenced code blocks using regular expression - fenced_blocks = re.findall(r"```(?:\w+\n)?([\s\S]*?)```", md) - - # find backtick code blocks using regular expression - backtick_blocks = re.findall(r"`{3}[\s\S]*?`{3}|`[^`\n]+`", md) - - code_blocks = fenced_blocks + backtick_blocks # type: ignore - code_blocks = "".join(code_blocks).replace("\n", "").replace("`", "") - return code_blocks - - if code_blocks_only: - return get_code_blocks_from_md_file(file_path) - - with open(file_path) as f: - return f.read() - - -def find_commands_in_files( - files: list, commands: list, code_blocks_only: bool = True -) -> dict: - """ - Search for specified commands in the content of Markdown files and return a - dictionary of the files where each command was found. - - The function reads the contents of each Markdown file in `files`, and for each - `command` in `commands`, searches for occurrences of the command within the file - contents. - The search is performed using regular expression to match whole words only. - If `code_blocks_only` is True, the function searches only within fenced code blocks - and backtick code blocks in the Markdown files. - - - Parameters - ---------- - files : list - A list of file paths to the Markdown files to search. - commands : list - A list of commands to search for. - code_blocks_only : bool, optional - If True, search only within code blocks in the Markdown files. - If False, search within the full contents of the Markdown files. - By default, True. - - Returns - ------- - dict - A dictionary where each key is a command that was found in one or more files, - and the value is a list of file paths where the command was found. - - Examples - -------- - ``` - files = ["file1.md", "file2.md", "file3.md"] - commands = ["command1", "command2", "command3"] - results = find_commands_in_files(files, commands) - ``` - """ - found_commands = defaultdict(list) # type: ignore - - for file_path in files: - file_contents = get_content_from_md_file(file_path, code_blocks_only) - - for command in commands: - # pattern to match whole words only - pattern = rf"\b{re.escape(command)}\b" - matches = re.finditer(pattern, file_contents) - - for _ in matches: - found_commands.setdefault(command, []) - if file_path not in found_commands[command]: - found_commands[command].append(file_path) - - return found_commands - - -def handle_export( - extension: Literal["json", "csv"], found_commands: dict, found_commands_sdk: dict -): - """ - Handle exporting the found commands into a file. - - Parameters - ---------- - extension : Literal["json", "csv"] - The extension type of the file to be exported. Can only be "json" or "csv". - found_commands : dict - The dictionary containing the commands found in the Markdown files and their - corresponding file paths. - found_commands_sdk : dict - The dictionary containing the SDK commands found in the Markdown files and their - corresponding file paths. - """ - - def export_to_json(path: Path, commands: dict): - with open(path, "w") as fp: - json.dump(commands, fp, indent=4) - - def export_to_csv(path: Path, commands: dict): - df = pd.DataFrame.from_dict(commands, orient="index").transpose() - df = pd.melt(df) - df.rename(columns={"variable": "command", "value": "file"}, inplace=True) - df = df[["file", "command"]] - df.sort_values(by=["file"], inplace=True) - df = df.dropna() - df.to_csv(path, index=False) - - if extension == "json": - export_to_json(path=f"{EXPORT_FILE_PATH}.{extension}", commands=found_commands) # type: ignore - export_to_json( - path=f"{EXPORT_FILE_PATH_SDK}.{extension}", commands=found_commands_sdk # type: ignore - ) - - else: - export_to_csv(path=f"{EXPORT_FILE_PATH}.{extension}", commands=found_commands) # type: ignore - export_to_csv( - path=f"{EXPORT_FILE_PATH_SDK}.{extension}", commands=found_commands_sdk # type: ignore - ) - - -def generate_documentation_commands( - export_extension: Literal["json", "csv"], code_blocks_only: bool -): - """ - Generate documentation commands based on commands in Markdown files. - - Parameters - ---------- - export_extension : Literal["json", "csv"] - The file extension of the export file. Valid values are "json" and "csv". - code_blocks_only : bool - If True, only code blocks from the Markdown files will be searched for - documentation commands. - If False, the entire contents of the Markdown files will be searched. - """ - en_file_as_dict = read_yaml_file(path=EN_FILE) - cmds = get_command_list(en_dict=en_file_as_dict) - cmds_sdk = [ - cmd for trail_map in TRAIL_MAPS for cmd in get_sdk_command_list(trail_map) - ] - - terminal_commands = find_commands_in_files( - files=MD_FILES, commands=cmds, code_blocks_only=code_blocks_only - ) - sdk_commands = find_commands_in_files( - files=MD_FILES, commands=cmds_sdk, code_blocks_only=code_blocks_only - ) - - handle_export( - extension=export_extension, - found_commands=terminal_commands, - found_commands_sdk=sdk_commands, - ) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="generate_documentation_commands", - description="Generate documentation commands based on commands in Markdown files.", - ) - parser.add_argument( - "-e", - "--export", - dest="export_extension", - default="csv", - choices=["json", "csv"], - help="Export the list of commands to a json or csv file", - ) - parser.add_argument( - "-c", - "--code-blocks-only", - dest="code_blocks_only", - default=True, - action="store_true", - help="Search only within code blocks in the Markdown files", - ) - args = parser.parse_args() - - generate_documentation_commands( - export_extension=args.export_extension, code_blocks_only=args.code_blocks_only - ) diff --git a/openbb_terminal/miscellaneous/exports/alternative/covid/.gitkeep b/openbb_terminal/miscellaneous/exports/alternative/covid/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/common/behavioural_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/common/behavioural_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/common/prediction_techniques/.gitkeep b/openbb_terminal/miscellaneous/exports/common/prediction_techniques/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/common/quantitative_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/common/quantitative_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/common/technical_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/common/technical_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/custom/.gitkeep b/openbb_terminal/miscellaneous/exports/custom/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/econometrics/.gitkeep b/openbb_terminal/miscellaneous/exports/econometrics/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/economy/.gitkeep b/openbb_terminal/miscellaneous/exports/economy/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/etf/movers/.gitkeep b/openbb_terminal/miscellaneous/exports/etf/movers/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/etf/screeners/.gitkeep b/openbb_terminal/miscellaneous/exports/etf/screeners/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/mutual_funds/.gitkeep b/openbb_terminal/miscellaneous/exports/mutual_funds/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/portfolio/.gitkeep b/openbb_terminal/miscellaneous/exports/portfolio/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/portfolio/views/.gitkeep b/openbb_terminal/miscellaneous/exports/portfolio/views/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/backtesting/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/backtesting/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/behavioural_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/behavioural_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/comparison_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/comparison_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/dark_pool_shorts/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/dark_pool_shorts/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/discovery/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/discovery/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/due_diligence/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/due_diligence/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/fundamental_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/fundamental_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/government/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/government/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/insider/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/insider/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/options/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/options/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/prediction_techniques/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/prediction_techniques/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/quantitative_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/quantitative_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/raw_data/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/raw_data/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/sector_industry_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/sector_industry_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/exports/stocks/technical_analysis/.gitkeep b/openbb_terminal/miscellaneous/exports/stocks/technical_analysis/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/futures/futures.csv b/openbb_terminal/miscellaneous/futures/futures.csv deleted file mode 100644 index 3058eeb4e2f..00000000000 --- a/openbb_terminal/miscellaneous/futures/futures.csv +++ /dev/null @@ -1,188 +0,0 @@ -Ticker,Description,Exchange,Category -YI,"1,000 oz Mini Silver Futures",NYB,metals -ALI,Aluminum Futures,CMX,metals -BLK,Block Cheese Futures,CME,agriculture -AW,Bloomberg Commodity Index Futures,CBT,index -BZ,Brent Crude Oil Last Day Finance Futures,NYM,hydrocarbon -BZT,Brent Crude Oil Last Day TAS Futures,NYM,hydrocarbon -CB,Cash-Settled Butter Futures,CME,agriculture -CSC,Cash-Settled Cheese Futures,CME,agriculture -GEO,CBL Global Emissions Offset Futures,NYM,hydrocarbon -ZW,Chicago SRW Wheat Futures,CBT,agriculture -ZWT,Chicago SRW Wheat TAS Futures,CBT,agriculture -DC,Class III Milk Futures,CME,agriculture -GDK,Class IV Milk Futures,CME,agriculture -HG0,Copper Spot TAS Futures,CMX,metals -HGT,Copper TAS Futures,CMX,metals -ZC,Corn Futures,CBT,agriculture -ZCT,Corn TAS Futures,CBT,agriculture -CLT,Crude Oil TAS Futures,NYM,hydrocarbon -DY,Dry Whey Futures,CME,agriculture -QC,E-mini Copper Futures,CMX,metals -QM,E-mini Crude Oil Futures,NYM,hydrocarbon -XAE,E-mini Energy Select Sector Futures,CME,index -QO,E-mini Gold Futures,CMX,metals -GC,CME Gold Futures,CMX,metals -SGU,Shanghai Gold (USD) Futures,CMX,metals -QG,E-mini Natural Gas Futures,NYM,hydrocarbon -QH,E-mini NY Harbor ULSD Futures,NYM,hydrocarbon -QU,E-mini RBOB Gasoline Futures,NYM,hydrocarbon -QI,E-mini Silver Futures,CMX,metals -GF,Feeder Cattle Futures,CME,agriculture -GFT,Feeder Cattle TAS Futures,CME,agriculture -GCT,Gold TAS Futures Futures,CMX,metals -KE,KC HRW Wheat Futures,CBT,agriculture -KET,KC HRW Wheat TAS Futures,CBT,agriculture -HE,Lean Hogs Futures,CME,agriculture -HET,Lean Hogs TAS Futures,CME,agriculture -LE,Live Cattle Futures,CME,agriculture -LET,Live Cattle TAS Futures,CME,agriculture -HG,Copper Futures,CMX,metals -MHG,E-mini Micro Copper Futures,CMX,metals -MGC,E-mini Micro Gold Futures,CMX,metals -SIL,E-mini Micro Silver Futures ,CMX,metals -MCL,Micro WTI Crude Oil Futures,NYM,hydrocarbon -YG,E-mini Gold Futures,NYB,metals -XK,Mini Soybean Futures,CBT,agriculture -XC,Mini Corn Futures,CBT,agriculture -XW,Mini Chicago SRW Wheat Futures,CBT,agriculture -GNF,Nonfat Dry Milk Futures,CME,agriculture -ZO,Oats Futures,CBT,agriculture -PRK,Pork Cutout Futures,CME,agriculture -RBT,RBOB Gasoline TAS Futures,NYM,hydrocarbon -ZR,Rough Rice Futures,CBT,agriculture -GD,S&P GSCI Commodity Index Futures,CME,index -SIT,Silver TAS Futures,CMX,metals -ZS,Soybean Futures,CBT,agriculture -ZM,Soybean Meal Futures,CBT,agriculture -ZMT,Soybean Meal TAS Futures,CBT,agriculture -ZL,Soybean Oil Futures,CBT,agriculture -ZLT,Soybean Oil TAS Futures,CBT,agriculture -SBT,Soybean TAS Futures,CBT,agriculture -ZN,10-Year T-Note Futures,CBT,bonds -N1U,10-Year USD MAC Swap Futures,CBT,bonds -TWE,20-Year U.S. Treasury Bond Futures,CBT,bonds -ZT,2-Year T-Note Futures,CBT,bonds -F1U,5-Year USD MAC Swap Futures,CBT,bonds -RX,Dow Jones Real Estate Index Futures,CBT,index -GE,Eurodollar Futures,CME,currency -GLB,One-Month Eurodollar Futures,CME,currency -ZF,Five-Year US Treasury Note Futures,CBT,bonds -10Y,Micro 10-Year Yield Futures,CBT,bonds -2YY,Micro 2-Year Yield Futures,CBT,bonds -30Y,Micro 30-Year Yield Futures,CBT,bonds -5YY,Micro 5-Year Yield Futures,CBT,bonds -SR1,One-Month SOFR Futures,CME,bonds -ZFT,TAS on 5-Year Treasury Note Futures,CBT,bonds -ZQ,Thirty-Day Fed Fund Futures,CBT,bonds -Z3N,Three Year Treasury Note Futures,CBT,bonds -SR3,Three-Month SOFR Futures,CME,bonds -ZB,U.S. Treasury Bond Futures,CBT,bonds -UB,Ultra U.S. Treasury Bond Futures,CBT,bonds -BTM,Bakkt BTC (USD) Monthly Futures,NYB,currency -BTC,Bitcoin Futures,CME,currency -ETH,Ether Futures,CME,currency -MBT,E-mini Micro Bitcoin Futures,CME,currency -MET,E-mini Micro Ether Futures,CME,currency -6A,Australian Dollar Futures,CME,currency -6L,Brazilian Real Futures,CME,currency -6B,British Pound Futures,CME,currency -PJY,British Pound/Japanese Yen Futures,CME,currency -PSF,British Pound/Swiss Franc Futures,CME,currency -6C,Canadian Dollar Futures,CME,currency -E7,E-mini Euro FX Futures,CME,currency -J7,E-mini Japanese Yen Futures,CME,currency -6E,Euro FX Futures,CME,currency -EAD,Euro/Australian Dollar Futures,CME,currency -RP,Euro/British Pound Futures,CME,currency -ECD,Euro/Canadian Dollar Futures,CME,currency -RY,Euro/Japanese Yen Futures,CME,currency -ENK,Euro/Norwegian Krone Futures,CME,currency -ESK,Euro/Swedish Krona Futures,CME,currency -RF,Euro/Swiss Franc Futures,CME,currency -SIR,Indian Rupee/USD Futures,CME,currency -6J,Japanese Yen Futures,CME,currency -KRW,Korean Won Futures,CME,currency -6M,Mexican Peso Futures,CME,currency -M6A,Micro AUD/USD Futures,CME,currency -MCD,Micro CAD/USD Futures,CME,currency -MSF,Micro CHF/USD Futures,CME,currency -M6E,Micro EUR/USD Futures,CME,currency -M6B,Micro GBP/USD Futures,CME,currency -MIR,Micro INR/USD Futures,CME,currency -M6C,Micro USD/CAD Futures,CME,currency -M6J,Micro USD/JPY Futures,CME,currency -6N,New Zealand Dollar Futures,CME,currency -NKD,Nikkei Index (USD) Futures,CME,index -NIY,Nikkei Index (YEN) Futures,CME,index -NOK,Norwegian Krone Futures,CME,currency -6R,Russian Ruble Futures,CME,currency -6Z,South African Rand Futures,CME,currency -SEK,Swedish Krona Futures,CME,currency -6S,Swiss Franc Futures,CME,currency -SJY,Swiss Franc/Japanese Yen Futures,CME,currency -TPY,Yen Denominated TOPIX Futures,CME,index -YMT,E-mini DJIA Index BTIC Futures,CBT,index -NQT,E-mini Nasdaq 100 BTIC Futures,CME,index -EST,E-mini S&P 500 BTIC Futures,CME,index -XAF,E-mini S&P 500 Financial Select Sector Index Futures,CME,index -RS1,E-mini Russell 1000 Index Futures,CME,index -RSG,E-mini Russell 1000 Growth Index Futures,CME,index -RSV,E-mini Russell 1000 Value Index Futures,CME,index -RTY,E-mini Russell 2000 Index Futures,CME,index -M2K,E-mini Micro Russell 2000 Index Futures,CME,index -ESG,E-mini S&P 500 ESG Index Futures,CME,index -EMT,E-mini S&P MidCap 400 Index BTIC Futures,CME,index -EMD,E-mini S&P MidCap 400 Index Futures,CME,index -MNQ,E-mini Micro Nasdaq 100 Index Futures,CME,index -MES,E-mini Micro S&P 500 Index Futures,CME,index -ES,E-Mini S&P 500 Index Futures,CME,index -FNG,ICE Micro FANG+ Index Futures,NYB,index -MMW,ICE MSCI ACWI NTR Index Futures,NYB,index -CHH,ICE MSCI China NTR Futures,NYB,index -MFS,ICE MSCI EAFE Index Futures,NYB,index -ASN,ICE MSCI EM Asia NTR Index Futures,NYB,index -LFM,ICE MSCI EM ESG Leaders NTR Index Futures,NYB,index -MML,ICE MSCI EM Latin America NTR Index Futures,NYB,index -MME,ICE MSCI Emerging Markets Index Futures,NYB,index -MIN,ICE MSCI India NTR Index Futures,NYB,index -USS,ICE MSCI USA GTR Index Futures,NYB,index -MWL,ICE MSCI World Index Futures,NYB,index -MWS,ICE MSCI World NTR Index Futures,NYB,index -VLQ,Nasdaq 100 Volatility Index Futures,CME,index -SDA,S&P 500 Annual Dividend Index Futures,CME,index -SED,SED (SOFR-Eurodollar) Spread Futures,CME,bonds -PL,Platinum Futures,NYM,metals -PA,Palladium Futures,NYM,metals -BUS,US Midwest Busheling Ferrous Futures,CMX,metals -AUP,Aluminum MW U.S. Transaction,CMX,metals -GLI,European Low Sulphur Gasoil Futures,NYM,hydrocarbon -DX,ICE U.S. Dollar Index Futures,NYB,index -FT5,E-mini FTSE China 50 Index Futures,CME,index -MMM,ICE MSCI Emerging Markets EMEA NTR Index Futures,NYB,index -MMN,ICE MSCI Emerging Markets NTR Index Futures,NYB,index -MLE,ICE MSCI Latin America Index Futures,NYB,index -YM,E-mini DJ $5 Index Futures,CBT,index -XAI,E-mini S&P 500 Industrial Select Sector Futures,CME,index -XAB,E-mini S&P 500 Materials Select Sector Futures,CME,index -XAK,E-mini S&P 500 Technology Select Sector Futures,CME,index -XAP,E-mini S&P 500 Consumer Staples Select Sector Futures,CME,index -XAR,E-mini S&P 500 Real Estate Select Sector Futures,CME,index -XAU,E-mini S&P 500 Utilities Select Sector Futures,CME,index -XAV,E-mini S&P 500 Health Care Select Sector Futures,CME,index -XAY,E-mini S&P 500 Consumer Discretionary Select Sector Futures,CME,index -XAZ,E-mini S&P 500 Communication Services Select Sector Futures,CME,index -BIO,E-mini Nasdaq Biotech Industry Futures,CME,index -NQ,CME Nasdaq 100 Index Futures,CME,index -NQQ,TACO on E-mini Nasdaq 100 Index Futures,CME,index -UFV,Urea (Granular) FOB US Gulf Futures,CBT,agriculture -CC,Cocoa Futures,NYB,agriculture -KC,Coffee Futures,NYB,agriculture -CT,Cotton Futures,NYB,agriculture -SF,Sugar No. 16 Futures,NYB,agriculture -SB,Sugar No. 11 Futures,NYB,agriculture -OJ,Orange Juice Futures,NYB,agriculture -NG,Natural Gas Futures,NYM,hydrocarbon -RB,RBOB Gasoline Futures,NYM,hydrocarbon -CL,WTI Crude Oil Futures,NYM,hydrocarbon -TRM,ICE TRY/USD Futures,NYB,currency diff --git a/openbb_terminal/miscellaneous/i18n/en.yml b/openbb_terminal/miscellaneous/i18n/en.yml deleted file mode 100644 index 21fdc06cfe9..00000000000 --- a/openbb_terminal/miscellaneous/i18n/en.yml +++ /dev/null @@ -1,1145 +0,0 @@ -en: - _home_: Information, guides and support for the OpenBB Terminal - intro: introduction on the OpenBB Terminal - about: discover the capabilities of the OpenBB Terminal (https://openbb.co/docs) - support: pre-populate a support ticket for our team to evaluate - survey: fill in our 2-minute survey so we better understand how we can improve the terminal - update: attempt to update the terminal automatically (GitHub version) - wiki: search for an expression in Wikipedia (https://www.wikipedia.org/) - news: display news articles based on term and data sources - keys: set API keys and check their validity - featflags: enable and disable feature flags - sources: select your preferred data sources - account: manage your OpenBB account - settings: tune settings (export folder, timezone, language, plot size) - _scripts_: Record and execute your own .openbb routine scripts - record: start recording current session - stop: stop session recording and convert to .openbb routine - exe: execute .openbb routine scripts (use exe --example for an example) - _configure_: Configure your own terminal - _main_menu_: Main menu - account/_authentication_: Authentication - account/login: Log in to OpenBB account - account/logout: Log out from OpenBB account - account/_personal_access_token_: OpenBB Personal Access Token - account/generate: Generate a Personal Access Token - account/show: Shows the current Personal Access Token - account/revoke: Revoke the Personal Access Token - account/_info_: Cloud storage of keys - account/clear: Clear keys from cloud - account/_routines_: Cloud storage of routines - account/list: List available routines - account/upload: Upload routine - account/download: Download routine - account/delete: Delete routine stored in cloud - keys/_source: Source - keys/_keys_: This menu allows you to set your own API keys. Type 'about' for more information - keys/mykeys: get your defined keys - keys/status: reevaluate keys status - keys/_status_: Status - defined, test passed: defined, test passed - defined, test failed: defined, test failed - not defined: not defined - defined, not tested: defined, not tested - defined, test inconclusive: defined, test inconclusive - sources/_info_: Get and set default data sources - sources/_source: Source - sources/get: get available data sources associated with command - sources/set: set default data source for a command - featflags/_info_: Feature flags through environment variables - featflags/retryload: retry misspelled commands with load first - featflags/interactive: open dataframes in interactive window - featflags/cls: clear console after each command - featflags/color: use coloring features - featflags/promptkit: enable prompt toolkit (autocomplete and history) - featflags/thoughts: thoughts of the day - featflags/reporthtml: open report as HTML otherwise notebook - featflags/exithelp: automatically print help when quitting menu - featflags/rcontext: remember contexts loaded params during session - featflags/rich: colorful rich terminal - featflags/richpanel: colorful rich terminal panel - featflags/ion: interactive matplotlib mode - featflags/watermark: watermark in figures - featflags/cmdloc: command location displayed in figures - featflags/overwrite: whether to overwrite Excel files if they already exists - featflags/version: whether to show the version in the bottom right corner - featflags/tbhint: displays usage hints in the bottom toolbar - settings/_info_: Current settings - settings/_cmds_: Change settings - settings/colors: set your own terminal colors - settings/_colors: Colors - settings/chart: set charts style - settings/_chart: Charts style - settings/table: set tables style - settings/_table: Tables style - settings/autoscaling: plot autoscaling - settings/dt: add or remove datetime from flair - settings/flair: set the flair emoji to be used - settings/_flair: Flair - settings/lang: terminal language - settings/_language: Language - settings/userdata: set folder to store user data - settings/tz: set different timezone - settings/dpi: dots per inch - settings/backend: plotting backend (None, tkAgg, MacOSX, Qt5Agg) - settings/height: select plot height - settings/_preferred_data_source_file: Preferred data source file - settings/width: select plot width - settings/pheight: select plot percentage height - settings/pwidth: select plot percentage width - settings/monitor: which monitor to display (primary 0, secondary 1) - settings/_dpi: Plot dots per inch - settings/_backend: Backend - settings/_plot_height: Plot height - settings/_plot_width: Plot width - settings/_plot_height_pct: Plot height [%] - settings/_plot_width_pct: Plot width [%] - settings/_monitor: Monitor - settings/_user_data_folder: User Data Folder - settings/_timezone: Timezone - settings/source: specify data source file - settings/_data_source: Data sources - settings/_tbnu: Time (in seconds) between tweet news updates - settings/_tatt: Twitter accounts to track - settings/_nttli: Number of tweets to look into from each account - settings/_tk: Keywords to look for in tweets - _toolkits_: OpenBB Toolkits - stocks: access historical pricing data, options, sector and industry, and overall due diligence - crypto: dive into onchain data, tokenomics, circulation supply, nfts and more - fixedincome: access central bank decisions, yield curves, government bonds and corporate bonds data - etf: exchange traded funds. Historical pricing, compare holdings and screening - economy: global macroeconomic data, e.g. futures, yield, treasury - forex: foreign exchanges, quotes, forward rates for currency pairs and oanda integration - funds: mutual funds search, overview, holdings and sector weights - futures: commodities, bonds, index, bitcoin and forex - futures/search: search for a future based on category or exchange - futures/historical: get historical timeseries - futures/curve: get futures curves - alternative: alternative datasets, such as open source metrics - econometrics: statistical and quantitative methods for relationships between datasets - forecast: timeseries forecasting with machine learning - portfolio: perform portfolio optimization and look at portfolio performance and attribution - dashboards: interactive dashboards using streamlit - stocks/filings: the most-recent form submissions to the SEC - stocks/search: search a specific stock ticker for analysis - stocks/load: load a specific stock ticker and additional info for analysis - stocks/_ticker: Stock - stocks/quote: view the current price for a specific stock ticker - stocks/tob: view top of book for loaded ticker (US exchanges only) - stocks/candle: view a candle chart for a specific stock ticker - stocks/news: latest news of the company - stocks/codes: FIGI, SIK and SIC codes codes - stocks/th: trading hours check open markets - stocks/options: options menu chains, open interest, greeks, parity - stocks/disc: discover trending stocks map, sectors, high short interest - stocks/dps: dark pool and short data darkpool, short interest, ftd - stocks/scr: screener stocks overview/performance, using preset filters - stocks/ins: insider trading latest penny stock buys, top officer purchases - stocks/gov: government menu house trading, contracts, corporate lobbying - stocks/ba: behavioural analysis from reddit, stocktwits, twitter, google - stocks/ca: comparison analysis get similar, historical, correlation, financials - stocks/fa: fundamental analysis income, balance, cash, ratios, earnings, expectations - stocks/res: research web page macroaxis, yahoo finance, fool - stocks/bt: strategy backtester simple ema, ema cross, rsi strategies - stocks/ta: technical analysis ema, macd, rsi, adx, bbands, obv - stocks/qa: quantitative analysis decompose, cusum, residuals analysis - stocks/forecast: forecast techniques rnn, nbeats, transformer, block rnn - stocks/options/unu: show unusual options activity - stocks/options/calc: basic call/put PnL calculator - stocks/options/screen: screens tickers based on preset - stocks/options/load: load new ticker - stocks/options/exp: see and set expiration dates - stocks/options/_ticker: Ticker - stocks/options/_expiry: Expiry - stocks/options/pcr: display put call ratio for ticker - stocks/options/info: display option information (volatility, IV rank etc) - stocks/options/chains: display option chains with greeks - stocks/options/oi: plot open interest - stocks/options/vol: plot volume - stocks/options/voi: plot volume and open interest - stocks/options/hist: plot option history - stocks/options/vsurf: show 3D volatility surface - stocks/options/grhist: plot option greek history - stocks/options/plot: plot variables provided by the user - stocks/options/greeks: shows the greeks for a given option - stocks/options/eodchain: gets option chain at a specific date - stocks/options/pricing: shows options pricing and risk neutral valuation - stocks/options/hedge: shows portfolio weights in order to neutralise delta - stocks/options/screen/view: view available presets (or one in particular) - stocks/options/screen/set: set one of the available presets - stocks/options/screen/_preset: Preset - stocks/options/screen/scr: screen data from this preset - stocks/options/screen/_screened_tickers: Last screened tickers - stocks/options/screen/ca: take these to comparison analysis menu - stocks/options/hedge/_ticker: Ticker - stocks/options/hedge/_expiry: Expiry - stocks/options/hedge/pick: pick the underlying asset position - stocks/options/hedge/_underlying: Underlying Asset Position - stocks/options/hedge/list: show the available strike prices for calls and puts - stocks/options/hedge/add: add an option to the list of options - stocks/options/hedge/rmv: remove an option from the list of options - stocks/options/hedge/sop: show selected options and neutral portfolio weights - stocks/options/hedge/plot: show the option payoff diagram - stocks/th/symbol: select the symbol - stocks/th/_symbol_name: Symbol name - stocks/th/_symbol: Symbol - stocks/th/_exchange: Exchange open - stocks/th/open: show open markets - stocks/th/closed: show closed markets - stocks/th/all: show all markets - stocks/th/exchange: show one exchange - stocks/th/holidays: show all dates on which there is a market holiday - stocks/disc/filings: the most-recent form submissions to the SEC - stocks/disc/pipo: past IPOs dates - stocks/disc/fipo: future IPOs dates - stocks/disc/gainers: show latest top gainers - stocks/disc/losers: show latest top losers - stocks/disc/ugs: undervalued stocks with revenue and earnings growth above 25% - stocks/disc/gtech: tech stocks with revenue and earnings growth more than 25% - stocks/disc/active: most active stocks by intraday trade volumest - stocks/disc/ulc: potentially undervalued large cap stocks - stocks/disc/asc: small cap stocks with earnings growth rates better than 25% - stocks/disc/arkord: orders by ARK Investment Management LLC - stocks/disc/upcoming: upcoming earnings release dates - stocks/disc/trending: trending news - stocks/disc/lowfloat: low float stocks under 10M shares float - stocks/disc/hotpenny: today's hot penny stocks - stocks/disc/rtat: top 10 retail traded stocks per day - stocks/disc/divcal: dividend calendar for selected date - stocks/disc/heatmap: heatmap of SP500 tickers - stocks/sia/select: select a specific ticker and all it's corresponding parameters - stocks/sia/clear: clear all or one of industry, sector, country and market cap parameters - stocks/sia/industry: see existing industries, or set industry if arg specified - stocks/sia/sector: see existing sectors, or set sector if arg specified - stocks/sia/country: see existing countries, or set country if arg specified - stocks/sia/mktcap: set mktcap between nano, micro, small, mid, large or mega - stocks/sia/exchange: revert exclude international exchanges flag - stocks/sia/period: set period between annual, quarterly or trailing - stocks/sia/_industry: Industry - stocks/sia/_sector: Sector - stocks/sia/_country: Country - stocks/sia/_mktcap: Market Cap - stocks/sia/_exclude_exchanges: Exclude Exchanges - stocks/sia/_period: Period - stocks/sia/_statistics_: Statistics - stocks/sia/cps: companies per Sector based on Country (and Market Cap) - stocks/sia/cpic: companies per Industry based on Country (and Market Cap) - stocks/sia/cpis: companies per Industry based on Sector (and Market Cap) - stocks/sia/cpcs: companies per Country based on Sector (and Market Cap) - stocks/sia/cpci: companies per Country based on Industry (and Market Cap) - stocks/sia/_financials_: Financials - stocks/sia/_financials_loaded_: Financials - loaded data (fast mode) - stocks/sia/metric: visualize financial metric across filters selected - stocks/sia/vis: visualize financial metric across filters selected - stocks/sia/_returned_tickers: Returned tickers - stocks/sia/ca: take these to comparison analysis menu - stocks/dps/load: load a specific stock ticker for analysis - stocks/dps/shorted: show most shorted stocks - stocks/dps/ctb: cost to borrow of stocks - stocks/dps/hsi: show top high short interest stocks of over 20% ratio - stocks/dps/prom: promising tickers based on dark pool shares regression - stocks/dps/pos: dark pool short position - stocks/dps/sidtc: short interest and days to cover - stocks/dps/_ticker: Ticker - stocks/dps/dpotc: dark pools (ATS) vs OTC data - stocks/dps/ftd: fails-to-deliver data - stocks/dps/spos: net short vs position - stocks/dps/psi: price vs short interest volume - stocks/scr/view: view available presets (defaults and customs) - stocks/scr/set: set one of the available presets - stocks/scr/_preset: Preset - stocks/scr/overview: overview (e.g. Sector, Industry, Market Cap, Volume) - stocks/scr/valuation: valuation (e.g. P/E, PEG, P/S, P/B, EPS this Y) - stocks/scr/financial: financial (e.g. Dividend, ROA, ROE, ROI, Earnings) - stocks/scr/ownership: ownership (e.g. Float, Insider Own, Short Ratio) - stocks/scr/performance: performance (e.g. Perf Week, Perf YTD, Volatility M) - stocks/scr/technical: technical (e.g. Beta, SMA50, 52W Low, RSI, Change) - stocks/scr/_screened_tickers: Last screened tickers - stocks/scr/ca: take these to comparison analysis menu - stocks/ins/view: view available presets - stocks/ins/set: set one of the available presets - stocks/ins/_preset: Preset - stocks/ins/filter: filter insiders based on preset - stocks/ins/load: load a specific stock ticker for analysis - stocks/ins/stats: insider stats of the company - stocks/ins/act: insider activity over time - stocks/ins/lins: last insider trading of the company - stocks/ins/lcb: latest cluster buys - stocks/ins/lpsb: latest penny stock buys - stocks/ins/lit: latest insider trading (all filings) - stocks/ins/lip: latest insider purchases - stocks/ins/blip: big latest insider purchases ($25k+) - stocks/ins/blop: big latest officer purchases ($25k+) - stocks/ins/blcp: big latest CEO/CFO purchases ($25k+) - stocks/ins/lis: latest insider sales - stocks/ins/blis: big latest insider sales ($100k+) - stocks/ins/blos: big latest officer sales ($100k+) - stocks/ins/blcs: big latest CEO/CFO sales ($100k+) - stocks/ins/topt: top officer purchases today - stocks/ins/toppw: top officer purchases past week - stocks/ins/toppm: top officer purchases past month - stocks/ins/tipt: top insider purchases today - stocks/ins/tippw: top insider purchases past week - stocks/ins/tippm: top insider purchases past month - stocks/ins/tist: top insider sales today - stocks/ins/tispw: top insider sales past week - stocks/ins/tispm: top insider sales past month - stocks/ins/_ticker: Ticker - stocks/ins/_last_insiders: Last Insiders - stocks/ins/_top_insiders: Top Insiders - stocks/gov/_explore: Explore - stocks/gov/lasttrades: last trades - stocks/gov/topbuys: show most purchased stocks - stocks/gov/topsells: show most sold stocks - stocks/gov/lastcontracts: show last government contracts given out - stocks/gov/qtrcontracts: quarterly government contracts analysis - stocks/gov/toplobbying: top corporate lobbying tickers - stocks/gov/load: load a specific ticker for analysis - stocks/gov/_ticker: Ticker - stocks/gov/gtrades: show government trades - stocks/gov/contracts: show government contracts - stocks/gov/histcont: historical quarterly government contracts - stocks/gov/lobbying: corporate lobbying details - stocks/ca/ticker: set ticker to get similar companies from - stocks/ca/_ticker: Ticker to get similar companies from - stocks/ca/tsne: run TSNE on all SP500 stocks and returns closest tickers - stocks/ca/get: get similar stocks - stocks/ca/set: reset and set similar companies - stocks/ca/add: add more similar companies - stocks/ca/rmv: remove similar companies individually or all - stocks/ca/_similar: Similar Companies - stocks/ca/historical: historical price data comparison - stocks/ca/hcorr: historical price correlation - stocks/ca/volume: historical volume data comparison - stocks/ca/income: income financials comparison - stocks/ca/balance: balance financials comparison - stocks/ca/cashflow: cashflow comparison - stocks/ca/sentiment: sentiment analysis comparison - stocks/ca/scorr: sentiment correlation - stocks/ca/overview: brief overview comparison - stocks/ca/valuation: brief valuation comparison - stocks/ca/financial: brief financial comparison - stocks/ca/ownership: brief ownership comparison - stocks/ca/performance: brief performance comparison - stocks/ca/technical: brief technical comparison - stocks/ba/load: load a specific stock ticker for analysis - stocks/ba/_ticker: Ticker - stocks/ba/headlines: sentiment from 15+ major news headlines - stocks/ba/snews: stock price displayed over sentiment of news headlines - stocks/ba/wsb: show what WSB gang is up to in subreddit wallstreetbets - stocks/ba/watchlist: show other users watchlist - stocks/ba/popular: show popular tickers - stocks/ba/spacc: show other users spacs announcements from subreddit SPACs - stocks/ba/spac: show other users spacs announcements from other subs - stocks/ba/getdd: gets due diligence from another user's post - stocks/ba/redditsent: searches reddit for ticker and finds reddit sentiment - stocks/ba/trending: trending stocks - stocks/ba/stalker: stalk stocktwits user's last messages - stocks/ba/bullbear: estimate quick sentiment from last 30 messages on board - stocks/ba/messages: output up to the 30 last messages on the board - stocks/ba/infer: infer about stock's sentiment from latest tweets - stocks/ba/sentiment: in-depth sentiment prediction from tweets over time - stocks/ba/mentions: interest over time based on stock's mentions - stocks/ba/regions: regions that show highest interest in stock - stocks/ba/interest: interest over time of sentences versus stock price - stocks/ba/queries: top related queries with this stock - stocks/ba/rise: top rising related queries with stock - stocks/ba/hist: plot historical RHI and AHI data by hour - stocks/ba/jcdr: Jim Cramer's daily recommendations - stocks/ba/jctr: Jim Cramer's recommendations by ticker - stocks/fa/load: load a specific stock ticker for analysis - stocks/fa/_ticker: Ticker - stocks/fa/_company_overview: Company Overview - stocks/fa/mktcap: obtain the market capitalization or enterprise value - stocks/fa/overview: financial overview of the company - stocks/fa/divs: show historical dividends for company - stocks/fa/splits: stock split and reverse split events since IPO - stocks/fa/rating: analyst prices and ratings over time of the company - stocks/fa/rot: number of analyst ratings over time on a monthly basis - stocks/fa/score: value investing scores for any time period - stocks/fa/warnings: company warnings according to Sean Seah book - stocks/fa/_management_shareholders: Management and Ownership - stocks/fa/mgmt: management team of the company - stocks/fa/shrs: shareholders (insiders, institutions and mutual funds) - stocks/fa/supplier: list of suppliers for the company - stocks/fa/customer: list of customers of the company - stocks/fa/_financial_statements: Financial Statements - stocks/fa/income: income statements of the company - stocks/fa/balance: balance sheet statements of the company - stocks/fa/cash: cash flow statements of the company - stocks/fa/growth: growth of financial statement items and ratios - stocks/fa/metrics: key financial metrics over time - stocks/fa/ratios: extensive set of ratios over time - stocks/fa/dupont: detailed breakdown for Return on Equity (RoE) - stocks/fa/fraud: key fraud ratios including M-score, Z-score and McKee - stocks/fa/sec: obtain recent SEC filings of the company - stocks/fa/analysis: analyse SEC filings with the help of machine learning - stocks/fa/_future_estimations: Future Expectations - stocks/fa/earnings: earnings dates and reported EPS - stocks/fa/epsfc: Earning Estimate by Analysts - EPS - stocks/fa/revfc: Earning Estimate by Analysts - Revenue - stocks/fa/est: quarter and year analysts earnings estimates - stocks/fa/pt: price targets over time - stocks/fa/dcf: advanced Excel customizable discounted cash flow - stocks/fa/dcfc: determine the (historical) discounted cash flow - stocks/res/_ticker: Ticker - stocks/res/macroaxis: www.macroaxis.com - stocks/res/yahoo: www.finance.yahoo.com - stocks/res/finviz: www.finviz.com - stocks/res/marketwatch: www.marketwatch.com - stocks/res/fool: www.fool.com - stocks/res/businessinsider: www.markets.businessinsider.com - stocks/res/bullrun: www.bullrun.com.br - stocks/res/fmp: www.financialmodelingprep.com - stocks/res/fidelity: www.eresearch.fidelity.com - stocks/res/tradingview: www.tradingview.com - stocks/res/marketchameleon: www.marketchameleon.com - stocks/res/stockrow: www.stockrow.com - stocks/res/barchart: www.barchart.com - stocks/res/grufity: www.grufity.com - stocks/res/fintel: www.fintel.com - stocks/res/zacks: www.zacks.com - stocks/res/macrotrends: www.macrotrends.net - stocks/res/newsfilter: www.newsfilter.io - stocks/res/stockanalysis: www.stockanalysis.com - stocks/bt/_ticker: Ticker - stocks/bt/whatif: what if you had bought X shares on day Y - stocks/bt/ema: buy when price exceeds EMA(l) - stocks/bt/emacross: buy when EMA(short) > EMA(long) - stocks/bt/rsi: buy when RSI < low and sell when RSI > high - stocks/bt/load: load a specific stock ticker for analysis - stocks/ta/_ticker: Ticker - stocks/ta/load: load a specific stock ticker for analysis - stocks/ta/summary: technical summary report - stocks/ta/recom: recommendation based on technical indicators - stocks/ta/_overlap_: Overlap - stocks/ta/ema: exponential moving average - stocks/ta/sma: simple moving average - stocks/ta/wma: weighted moving average - stocks/ta/hma: hull moving average - stocks/ta/zlma: zero lag moving average - stocks/ta/vwap: volume weighted average price - stocks/ta/_momentum_: Momentum - stocks/ta/cci: commodity channel index - stocks/ta/macd: moving average convergence/divergence - stocks/ta/rsi: relative strength index - stocks/ta/rsp: relative strength percentile - stocks/ta/stoch: stochastic oscillator - stocks/ta/fisher: fisher transform - stocks/ta/cg: centre of gravity - stocks/ta/clenow: clenow volatility adjusted momentum - stocks/ta/demark: Tom Demark's sequential indicator - stocks/ta/_trend_: Trend - stocks/ta/adx: average directional movement index - stocks/ta/aroon: aroon indicator - stocks/ta/_volatility_: Volatility - stocks/ta/bbands: bollinger bands - stocks/ta/donchian: donchian channels - stocks/ta/kc: keltner channels - stocks/ta/atr: average true range - stocks/ta/cones: realized volatility cones - stocks/ta/_volume_: Volume - stocks/ta/ad: accumulation/distribution line - stocks/ta/adosc: chaikin oscillator - stocks/ta/obv: on balance volume - stocks/ta/_custom_: Custom - stocks/ta/fib: fibonacci retracement - stocks/ta/multi: plot multiple indicators on the same chart - stocks/qa/load: load new ticker - stocks/qa/pick: pick target column for analysis - stocks/qa/_ticker: Ticker - stocks/qa/_target: Target Column - stocks/qa/_statistics_: Statistics - stocks/qa/summary: brief summary statistics of loaded stock - stocks/qa/normality: normality statistics and tests - stocks/qa/unitroot: unit root test for stationarity (ADF, KPSS) - stocks/qa/_plots_: Plots - stocks/qa/line: line plot of selected target - stocks/qa/hist: histogram with density plot - stocks/qa/cdf: cumulative distribution function - stocks/qa/bw: box and whisker plot - stocks/qa/acf: (partial) auto-correlation function differentials of prices - stocks/qa/qqplot: residuals against standard normal curve - stocks/qa/_rolling_metrics_: Rolling Metrics - stocks/qa/rolling: rolling mean and std deviation of prices - stocks/qa/spread: rolling variance and std deviation of prices - stocks/qa/quantile: rolling median and quantile of prices - stocks/qa/skew: rolling skewness of distribution of prices - stocks/qa/kurtosis: rolling kurtosis of distribution of prices - stocks/qa/_risk_: Risk - stocks/qa/var: display value at risk - stocks/qa/es: display expected shortfall - stocks/qa/sh: display sharpe ratio - stocks/qa/so: display sortino ratio - stocks/qa/om: display omega ratio - stocks/qa/_other_: Other - stocks/qa/raw: print raw data - stocks/qa/decompose: decomposition in cyclic-trend, season, and residuals of prices - stocks/qa/cusum: detects abrupt changes using cumulative sum algorithm of prices - stocks/qa/capm: capital asset pricing model - stocks/qa/beta: display beta w.r.t to any reference ticker - crypto/load: load a specific cryptocurrency for analysis - crypto/find: find coins - crypto/price: real-time price and interval of confidence - crypto/_symbol: Coin - crypto/_interval: Interval (min) - crypto/_source: Source - crypto/_exchange: Exchange - crypto/headlines: crypto sentiment from 15+ major news headlines - crypto/candle: view a candle chart for a specific cryptocurrency - crypto/prt: potential returns (e.g. upside if ETH reaches BTC market cap) - crypto/disc: discover trending cryptocurrencies top gainers, losers, top sentiment - crypto/ov: overview of the cryptocurrencies market cap, DeFi, latest news, top exchanges, stables - crypto/onchain: information on different blockchains eth gas fees, whale alerts, DEXes info - crypto/defi: decentralized finance information dpi, llama, tvl, lending, borrow, funding - crypto/tools: explore different tools apytoapr, il - crypto/nft: non-fungible tokens today drops - crypto/dd: due-diligence for loaded coin coin information, social media, market stats - crypto/ta: technical analysis for loaded coin ema, macd, rsi, adx, bbands, obv - crypto/forecast: forecast techniques rnn, nbeats, transformer, block rnn - crypto/qa: quantitative analysis decompose, cusum, residuals analysis - crypto/disc/top: top coins - crypto/disc/trending: trending coins - crypto/disc/gainers: top gainers - crypto/disc/losers: top losers - crypto/disc/search: search for coins - crypto/disc/nft_mktp_chains: nft marketplace chains - crypto/disc/nft_mktp: nft marketplaces - crypto/disc/dapps: available decentralized apps - crypto/disc/dapp_categories: show available dapp categories - crypto/disc/dapp_chains: show available dapp chains - crypto/disc/dapp_metrics: show dapp metrics - crypto/disc/defi_chains: show available defi chains - crypto/disc/tokens: show chains that support tokens - crypto/ov/global: global crypto market statistics - crypto/ov/defi: global DeFi market info - crypto/ov/stables: stablecoins - crypto/ov/exrates: coin exchange rates - crypto/ov/indexes: crypto indexes - crypto/ov/derivatives: crypto derivatives - crypto/ov/categories: crypto categories - crypto/ov/hold: ethereum, bitcoin holdings overview statistics - crypto/ov/hm: crypto heatmap - crypto/ov/info: basic info about all coins available - crypto/ov/markets: market related info about all coins available - crypto/ov/exmarkets: all available markets on given exchange - crypto/ov/platforms: list blockchain platforms eg. ethereum, solana, kusama, terra - crypto/ov/contracts: all smart contracts for given platform - crypto/ov/pairs: info about available trading pairs - crypto/ov/news: recent crypto news - crypto/ov/wf: overall withdrawal fees - crypto/ov/ewf: overall exchange withdrawal fees - crypto/ov/wfpe: crypto withdrawal fees per exchange - crypto/ov/altindex: altcoin season index (75% of top 50 coins perform better than BTC) - crypto/ov/btcrb: display bitcoin rainbow price chart (logarithmic regression) - crypto/ov/ch: lists major crypto-related hacks - crypto/ov/cr: crypto supply or borrow interest rates - crypto/ov/fun: explore projects based on fundamental metrics - crypto/onchain/hr: check blockchain hashrate over time (BTC or ETH) - crypto/onchain/btccp: displays BTC circulating supply - crypto/onchain/btcct: displays BTC confirmed transactions - crypto/onchain/btcblockdata: displays BTC single block data - crypto/onchain/gwei: check current eth gas fees - crypto/onchain/whales: check crypto wales transactions - crypto/onchain/lt: last trades by dex or month - crypto/onchain/dvcp: daily volume for crypto pair - crypto/onchain/tv: token volume on DEXes - crypto/onchain/ueat: unique ethereum addresses which made a transaction - crypto/onchain/ttcp: top traded crypto pairs on given decentralized exchange - crypto/onchain/baas: bid, ask prices, average spread for given crypto pair - crypto/onchain/query: flipsidecrypto query - crypto/onchain/_address: Ethereum address - crypto/onchain/_type: Address type - crypto/onchain/_ethereum_: Ethereum - crypto/onchain/address: load ethereum address of token, account or transaction - crypto/onchain/top: top ERC20 tokens - crypto/onchain/balance: check ethereum balance - crypto/onchain/hist: ethereum balance history (transactions) - crypto/onchain/info: ERC20 token info - crypto/onchain/holders: top ERC20 token holders - crypto/onchain/th: ERC20 token history - crypto/onchain/prices: ERC20 token historical prices - crypto/onchain/tx: ethereum blockchain transaction info - crypto/defi/newsletter: recent DeFi related newsletters - crypto/defi/dpi: DeFi protocols listed on DefiPulse - crypto/defi/vaults: top DeFi Vaults on different blockchains - crypto/defi/tokens: tokens trade-able on Uniswap - crypto/defi/stats: base statistics about Uniswap - crypto/defi/pairs: recently added pairs on Uniswap - crypto/defi/pools: pools by volume on Uniswap - crypto/defi/swaps: recent swaps done on Uniswap - crypto/defi/ldapps: lists dApps - crypto/defi/gdapps: top DeFi dApps grouped by chain - crypto/defi/stvl: historical values of the total sum of TVLs from all dApps - crypto/defi/dtvl: historical total value locked (TVL) by dApp - crypto/defi/aterra: 30-day history of specified asset in terra address - crypto/defi/ayr: 30-day history of anchor yield reserve - crypto/defi/sinfo: staking info for provided terra account address - crypto/defi/validators: information about terra blockchain validators - crypto/defi/govp: terra blockchain governance proposals list - crypto/defi/gacc: terra blockchain account growth history - crypto/defi/sratio: terra blockchain staking ratio history - crypto/defi/sreturn: terra blockchain staking returns history - crypto/defi/lcsc: Luna circulating supply changes - crypto/defi/anchor: anchor earnings data - crypto/tools/aprtoapy: convert apr to apy - crypto/tools/il: calculate impermanent loss - crypto/nft/fp: collection sales/floor price - crypto/nft/collections: top ethereum collections - crypto/nft/stats: collection stats - crypto/dd/_overview_: Overview - crypto/dd/_market_: Market - crypto/dd/_metrics_: Metrics - crypto/dd/_contributors_: Contributors and Investors - crypto/dd/_tokenomics_: Tokenomics - crypto/dd/_roadmap_: Roadmap and News - crypto/dd/_activity_: Activity and Community - crypto/dd/load: load a specific cryptocurrency for analysis - crypto/dd/_symbol: Coin - crypto/dd/_source: Source - crypto/dd/info: information about loaded coin - crypto/dd/market: market stats about loaded coin - crypto/dd/ath: all time high related stats for loaded coin - crypto/dd/atl: all time low related stats for loaded coin - crypto/dd/web: found websites for loaded coin e.g forum, homepage - crypto/dd/social: social portals urls for loaded coin, e.g reddit, twitter - crypto/dd/score: different kind of scores for loaded coin, e.g developer score, sentiment score - crypto/dd/dev: github, bitbucket coin development statistics - crypto/dd/bc: links to blockchain explorers for loaded coin - crypto/dd/desc: description on project - why, how, what - crypto/dd/active: active addresses - crypto/dd/nonzero: addresses with non-zero balances - crypto/dd/change: 30d change of supply held on exchange wallets - crypto/dd/eb: total balance held on exchanges (in percentage and units) - crypto/dd/oi: open interest per exchange - crypto/dd/fundrate: funding rate per exchange - crypto/dd/liquidations: daily liquidation stats for loaded coin - crypto/dd/basic: basic information about loaded coin - crypto/dd/ps: price and supply related metrics for loaded coin - crypto/dd/mkt: all markets for loaded coin - 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/balance: coin balance - 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 - crypto/dd/rm: roadmap - crypto/dd/tk: tokenomics e.g. circulating/max/total supply, emission type, etc - crypto/dd/pi: project information e.g. technology details, public repos, audits, vulns - crypto/dd/team: contributors (individuals and organizations) - crypto/dd/inv: investors (individuals and organizations) - crypto/dd/gov: governance details - crypto/dd/fr: fundraising details e.g. treasury accounts, sales rounds, allocation - crypto/dd/links: links e.g. whitepaper, github, twitter, youtube, reddit, telegram - crypto/dd/gh: github activity over time - crypto/dd/news: loaded coin's most recent news - crypto/dd/funot: fundamental metric over time - crypto/ta/_ticker: Coin loaded - crypto/ta/tv: open interactive chart on - crypto/ta/_overlap_: Overlap - crypto/ta/ema: exponential moving average - crypto/ta/sma: simple moving average - crypto/ta/wma: weighted moving average - crypto/ta/hma: hull moving average - crypto/ta/zlma: zero lag moving average - crypto/ta/vwap: volume weighted average price - crypto/ta/_momentum_: Momentum - crypto/ta/cci: commodity channel index - crypto/ta/demark: Tom Demark's sequential indicator - crypto/ta/macd: moving average convergence/divergence - crypto/ta/rsi: relative strength index - crypto/ta/stoch: stochastic oscillator - crypto/ta/fisher: fisher transform - crypto/ta/cg: centre of gravity - crypto/ta/_trend_: Trend - crypto/ta/adx: average directional movement index - crypto/ta/aroon: aroon indicator - crypto/ta/_volatility_: Volatility - crypto/ta/atr: average true range - crypto/ta/bbands: bollinger bands - crypto/ta/cones: realized volatility cones - crypto/ta/donchian: donchian channels - crypto/ta/kc: keltner channels - crypto/ta/_volume_: Volume - crypto/ta/ad: accumulation/distribution line - crypto/ta/adosc: chaikin oscillator - crypto/ta/obv: on balance volume - crypto/ta/_custom_: Custom - crypto/ta/fib: fibonacci retracement - crypto/qa/load: load new ticker - crypto/qa/pick: pick target column for analysis - crypto/qa/_ticker: Ticker - crypto/qa/_target: Target Column - crypto/qa/_statistics_: Statistics - crypto/qa/summary: brief summary statistics of loaded stock - crypto/qa/normality: normality statistics and tests - crypto/qa/unitroot: unit root test for stationarity (ADF, KPSS) - crypto/qa/_plots_: Plots - crypto/qa/line: line plot of selected target - crypto/qa/hist: histogram with density plot - crypto/qa/cdf: cumulative distribution function - crypto/qa/bw: box and whisker plot - crypto/qa/acf: (partial) auto-correlation function differentials of prices - crypto/qa/qqplot: residuals against standard normal curve - crypto/qa/_rolling_metrics_: Rolling Metrics - crypto/qa/rolling: rolling mean and std deviation of prices - crypto/qa/spread: rolling variance and std deviation of prices - crypto/qa/quantile: rolling median and quantile of prices - crypto/qa/skew: rolling skewness of distribution of prices - crypto/qa/kurtosis: rolling kurtosis of distribution of prices - crypto/qa/_other_: Other - crypto/qa/raw: print raw data - crypto/qa/decompose: decomposition in cyclic-trend, season, and residuals of prices - crypto/qa/cusum: detects abrupt changes using cumulative sum algorithm of prices - etf/search: search ETF by name or description - etf/load: load ETF data - etf/_symbol: Symbol - etf/_major_holdings: Major holdings - etf/ca: comparison analysis get similar, historical, correlation, financials - etf/disc: discover ETFs gainers/decliners/active - etf/scr: screener ETFs overview/performance, using preset filters - etf/overview: get overview - etf/holdings: top company holdings - etf/weights: sector weights allocation - etf/candle: view a candle chart for ETF - etf/news: latest news of the company - etf/compare: compare multiple different ETFs - etf/ta: technical analysis ema, macd, rsi, adx, bbands, obv - etf/disc/gainers: top gainers - etf/disc/decliners: top decliners - etf/disc/active: most active - etf/scr/view: view available presets - etf/scr/set: set one of the available presets - etf/scr/_preset: Preset - etf/scr/screen: screen ETF using preset selected - etf/scr/sbc: screen by category - etf/ta/_ticker: ETF - etf/ta/_overlap_: Overlap - etf/ta/ema: exponential moving average - etf/ta/sma: simple moving average - etf/ta/wma: weighted moving average - etf/ta/hma: hull moving average - etf/ta/zlma: zero lag moving average - etf/ta/vwap: volume weighted average price - etf/ta/_momentum_: Momentum - etf/ta/cci: commodity channel index - etf/ta/macd: moving average convergence/divergence - etf/ta/rsi: relative strength index - etf/ta/stoch: stochastic oscillator - etf/ta/fisher: fisher transform - etf/ta/cg: centre of gravity - etf/ta/_trend_: Trend - etf/ta/adx: average directional movement index - etf/ta/aroon: aroon indicator - etf/ta/_volatility_: Volatility - etf/ta/bbands: bollinger bands - etf/ta/donchian: donchian channels - etf/ta/kc: keltner channels - etf/ta/_volume_: Volume - etf/ta/ad: accumulation/distribution line - etf/ta/adosc: chaikin oscillator - etf/ta/obv: on balance volume - etf/ta/_custom_: Custom - etf/ta/fib: fibonacci retracement - economy/_country_: Country Performance - economy/gdp: Show nominal Gross Domestic Product (GDP) - economy/rgdp: Show real Gross Domestic Product (GDP) - economy/fgdp: Show forecasts of nominal and real Gross Domestic Product (GDP) - economy/debt: Show government debt-to-GDP ratio - economy/cpi: obtain the (harmonized) Consumer Price Index (CPI) - economy/ccpi: obtain components of the CPI including Food, Energy and Total - economy/balance: Show Government defict or surplus in capital income and expenditures - economy/revenue: Show Government revenue based on tax income - economy/spending: Show Government spending as indicator of size of government - economy/trust: Show Government trust based on surveys regarding confidence in the government - economy/_database_: Databases - economy/_stored: Stored datasets - economy/overview: show a market overview of either indices, bonds or currencies - economy/futures: display a futures and commodities overview - economy/map: S&P500 index stocks map - economy/bigmac: The Economist Big Mac index - economy/macro: collect macro data for a country or countries - economy/fred: collect macro data from FRED based on a series ID - economy/index: find and plot any (major) index on the market - economy/treasury: obtain U.S. treasury rates - economy/ycrv: show sovereign yield curves - economy/spread: show bond spread matrix - economy/events: show economic calendar - economy/edebt: show external debt statistics for various countries - economy/plot: plot data from the above commands together - economy/rtps: real-time performance sectors - economy/valuation: valuation of sectors, industry, country - economy/performance: performance of sectors, industry, country - economy/eval: create new series by performing operations on loaded data - economy/forecast: forecast techniques; rnn, nbeats, transformer, block rnn - economy/qa: Open quantitative analysis menu with stored data - economy/qa/pick: pick new series from stored economy data - economy/qa/_series: Selected series - economy/qa/_statistics_: Statistics - economy/qa/summary: brief summary statistics of loaded stock - economy/qa/normality: normality statistics and tests - economy/qa/unitroot: unit root test for stationarity (ADF, KPSS) - economy/qa/_plots_: Plots - economy/qa/line: line plot of selected target - economy/qa/hist: histogram with density plot - economy/qa/cdf: cumulative distribution function - economy/qa/bw: box and whisker plot - economy/qa/acf: (partial) auto-correlation function differentials of prices - economy/qa/qqplot: residuals against standard normal curve - economy/qa/_rolling_metrics_: Rolling Metrics - economy/qa/rolling: rolling mean and std deviation of prices - economy/qa/spread: rolling variance and std deviation of prices - economy/qa/quantile: rolling median and quantile of prices - economy/qa/skew: rolling skewness of distribution of prices - economy/qa/kurtosis: rolling kurtosis of distribution of prices - economy/qa/_other_: Other - economy/qa/raw: print raw data - economy/qa/decompose: decomposition in cyclic-trend, season, and residuals of prices - economy/qa/cusum: detects abrupt changes using cumulative sum algorithm of prices - fixedincome/_reference_rates_: Reference rates - fixedincome/estr: euro short-term rate (estr) - fixedincome/sofr: secured overnight financing rate (sofr) - fixedincome/sonia: sterling overnight index average (sonia) - fixedincome/ameribor: american interbank offered rate (ameribor) - fixedincome/_central_bank_rates_: Central bank rates - fixedincome/fed: effective federal funds rate - fixedincome/obfr: overnight bank funding rate (obfr) - fixedincome/iorb: interest rate on reserve balances - fixedincome/projection: the Federal Reserve's projection of the federal funds rate. - fixedincome/dwpcr: discount window primary credit rate - fixedincome/ecb: key ECB interest rates - fixedincome/_government_bonds_: Government Bonds - fixedincome/treasury: plot countries (including forecast) treasury rates - fixedincome/usrates: plot U.S. secondary market, constant maturity or inflation protected treasuries - fixedincome/ycrv: plot the yield curve based on constant maturity U.S. treasuries - fixedincome/ecbycrv: plot the yield curve based on Euro Area treasuries - fixedincome/_corporate_bonds_: Corporate Bonds - fixedincome/icebofa: display ICE BofA Corporate Indices - fixedincome/moody: display Moody's AAA and BAA Corporate indices - fixedincome/cp: display short-term, promissory notes issued by corporations - fixedincome/spot: display spot rates ranging from 1 year to a 100 years - fixedincome/hqm: display the High Quality Market (HQM) corporate yield curve - fixedincome/_spreads_: Spreads - fixedincome/icespread: display spread between ICE BofA Corporate Indices and spot treasury curve - fixedincome/tmc: 10-year treasury constant maturity minus selected treasury constant maturity - fixedincome/ffrmc: selected treasury constant maturity minus federal funds rate - fixedincome/tbffr: selected treasury bill minus federal funds rate - forex/_ticker: Ticker - forex/_from: From - forex/_to: To - forex/_source: Source - forex/quote: get last quote - forex/load: get historical data - forex/candle: show candle plot for loaded pair - forex/fwd: get forward rates for loaded pair - forex/ta: technical analysis ema, macd, rsi, adx, bbands, obv - forex/qa: quantitative analysis decompose, cusum, residuals analysis - forex/forex: Forex brokerages - forex/forecast: forecast techniques - forex/oanda: Oanda menu - forex/ta/_currency: Currency pair loaded - forex/ta/_source: Source - forex/ta/_overlap_: Overlap - forex/ta/ema: exponential moving average - forex/ta/sma: simple moving average - forex/ta/zlma: zero lag moving average - forex/ta/_momentum_: Momentum - forex/ta/cci: commodity channel index - forex/ta/macd: moving average convergence/divergence - forex/ta/rsi: relative strength index - forex/ta/stoch: stochastic oscillator - forex/ta/fisher: fisher transform - forex/ta/cg: centre of gravity - forex/ta/_trend_: Trend - forex/ta/adx: average directional movement index - forex/ta/aroon: aroon indicator - forex/ta/_volatility_: Volatility - forex/ta/bbands: bollinger bands - forex/ta/donchian: donchian channels - forex/ta/_custom_: Custom - forex/ta/fib: fibonacci retracement - forex/qa/pick: pick target column for analysis - forex/qa/_pair: Pair - forex/qa/_target: Target - forex/qa/_statistics_: Statistics - forex/qa/summary: brief summary statistics of loaded stock - forex/qa/normality: normality statistics and tests - forex/qa/unitroot: unit root test for stationarity (ADF, KPSS) - forex/qa/_plots_: Plots - forex/qa/line: line plot of selected target - forex/qa/hist: histogram with density plot - forex/qa/cdf: cumulative distribution function - forex/qa/bw: box and whisker plot - forex/qa/acf: (partial) auto-correlation function differentials of prices - forex/qa/qqplot: residuals against standard normal curve - forex/qa/_rolling_metrics_: Rolling Metrics - forex/qa/rolling: rolling mean and std deviation of prices - forex/qa/spread: rolling variance and std deviation of prices - forex/qa/quantile: rolling median and quantile of prices - forex/qa/skew: rolling skewness of distribution of prices - forex/qa/kurtosis: rolling kurtosis of distribution of prices - forex/qa/_other_: Other - forex/qa/raw: print raw data - forex/qa/decompose: decomposition in cyclic-trend, season, and residuals of prices - forex/qa/cusum: detects abrupt changes using cumulative sum algorithm of prices - forex/oanda/summary: shows account summary - forex/oanda/calendar: show calendar - forex/oanda/list: list order history - forex/oanda/pending: get information on pending orders - forex/oanda/cancel: cancel a pending order by ID -i order ID - forex/oanda/positions: get open positions - forex/oanda/trades: list open trades - forex/oanda/closetrade: close a trade by id - forex/oanda/from: select the "from" currency in a forex pair - forex/oanda/to: select the "to" currency in a forex pair - forex/oanda/_loaded: Loaded instrument - forex/oanda/_from: From - forex/oanda/_to: To - forex/oanda/_source: Source - forex/oanda/candles: show candles - forex/oanda/price: shows price for selected instrument - forex/oanda/order: place limit order -u NUMBER of units -p price - forex/oanda/orderbook: print orderbook - forex/oanda/positionbook: print positionbook - funds/country: set a country for filtering - funds/_country: Current Country - funds/search: search for Mutual Funds - funds/load: load historical fund data - funds/_fund: Current Fund - funds/info: get fund information - funds/plot: plot loaded historical fund data - funds/sector: sector weightings - funds/equity: equity holdings - funds/alswe: display fund allocation (sector, country, holdings) - funds/infoswe: get fund information - funds/forecast: forecasting techniques - funds/country_ms: set a country for filtering - funds/holdings: holdings of the loaded funds - funds/carbon: carbon metrics - funds/exclusion: exclusion policy - funds/_country_ms: Current Country MorningStar - alternative/hn: Hacker News most popular stories - alternative/covid: COVID menu cases, deaths, rates - alternative/oss: Open Source menu star history, repos information - alternative/realestate: Real Estate menu sold prices, HPIs - alternative/covid/slopes: get countries with highest slope in cases - alternative/covid/country: select country for data - alternative/covid/_country: Country - alternative/covid/ov: get overview (cases and deaths) for selected country - alternative/covid/deaths: get deaths for selected country - alternative/covid/cases: get cases for selected country - alternative/covid/rates: get death/cases rate for selected country - alternative/oss/rossidx: the fastest-growing open-source startups - alternative/oss/rs: repo summary - alternative/oss/sh: repo star history - alternative/oss/tr: top starred repos - alternative/realestate/sales: list UK house sales via postcode - alternative/realestate/_postcode: UK Postcode - alternative/realestate/townsales: list UK house sales via town - alternative/realestate/_town: Town - alternative/realestate/_startdate: Start Date - alternative/realestate/_enddate: End Date - alternative/realestate/regionstats: retrieve UK sold house price stats by region - alternative/realestate/_region: Region - econometrics/_data_loc: Looking for data in - econometrics/load: load a dataset (also works with StatsModels datasets) - econometrics/export: export a processed dataset - econometrics/remove: remove one of the loaded datasets - econometrics/options: show available column-dataset options - econometrics/_loaded: Loaded files and data columns - econometrics/_exploration_: Exploration - econometrics/show: show a portion of a loaded dataset - econometrics/plot: plot data from a dataset - econometrics/type: change types of the columns or display their types - econometrics/desc: show descriptive statistics of a dataset - econometrics/corr: Plot the correlation coefficients for dataset features - econometrics/season: Plot the seasonality for a dataset column - econometrics/index: set (multi) index based on columns - econometrics/clean: clean a dataset by filling or dropping NaNs - econometrics/modify: combine columns of datasets and delete or rename columns - econometrics/add: Add columns to dataset with option to use formulas - econometrics/eval: create new series by performing operations on loaded data - econometrics/delete: Delete columns from dataset - econometrics/combine: Combine columns from different datasets - econometrics/rename: Rename column from dataset - econometrics/lag: Add lag to a variable by shifting a column - econometrics/_panel_: Panel - econometrics/_residuals_: Residuals - econometrics/ols: fit a (multi) linear regression model - econometrics/norm: perform normality tests on a column of a dataset - econometrics/root: perform unitroot tests (ADF & KPSS) on a column of a dataset - econometrics/panel: estimate model based on various regression techniques - econometrics/compare: compare results of all estimated models - econometrics/_time_series_: Time Series - econometrics/dwat: Durbin-Watson autocorrelation test on the residuals of the regression - econometrics/bgod: Breusch-Godfrey autocorrelation tests with lags on the residuals of the regression - econometrics/bpag: Breusch-Pagan heteroscedasticity test on the residuals of the regression - econometrics/granger: Granger causality tests on two columns - econometrics/coint: co-integration test on a multitude of columns - econometrics/garch: estimate future volatility with GARCH - econometrics/ret: calculate returns for the given time series - portfolio/bro: brokers holdings supports robinhood, ally, degiro, coinbase - portfolio/po: portfolio optimization optimize your portfolio weights efficiently - portfolio/load: load transactions into the portfolio (use load --example for an example) - portfolio/_loaded: Loaded transactions - portfolio/_riskfreerate: Risk Free Rate - portfolio/show: show existing transactions - portfolio/bench: define the benchmark - portfolio/_benchmark: Benchmark - portfolio/_graphs_: Graphs - portfolio/holdv: holdings of assets (absolute value) - portfolio/holdp: portfolio holdings of assets (in percentage) - portfolio/cret: cumulative returns - portfolio/yret: yearly returns - portfolio/mret: monthly returns - portfolio/dret: daily returns - portfolio/distr: distribution of daily returns - portfolio/maxdd: maximum drawdown - portfolio/rvol: rolling volatility - portfolio/rsharpe: rolling sharpe - portfolio/rsort: rolling sortino - portfolio/rbeta: rolling beta - portfolio/_metrics_: Metrics - portfolio/alloc: allocation on an asset, sector, countries or regions basis - portfolio/attrib: display sector attribution of the portfolio compared to the S&P 500 - portfolio/summary: all portfolio vs benchmark metrics for a certain period of choice - portfolio/metric: portfolio vs benchmark metric for all different periods - portfolio/perf: performance of the portfolio versus benchmark - portfolio/_risk_: Risk Metrics - portfolio/var: display value at risk - portfolio/es: display expected shortfall - portfolio/om: display omega ratio - portfolio/bro/degiro: Degiro Menu - portfolio/bro/rh: Robinhood Menu - portfolio/bro/cb: Coinbase Pro Menu - portfolio/bro/ally/holdings: show account holdings - portfolio/bro/ally/history: show history of your account - portfolio/bro/ally/balances: show balance details of account - portfolio/bro/ally/_info_: Stock Information - portfolio/bro/ally/quote: get stock quote - portfolio/bro/ally/movers: get ranked lists of movers - portfolio/bro/degiro/login: connect to degiro's api - portfolio/bro/degiro/logout: disconnect from degiro's api - portfolio/bro/degiro/hold: view holdings - portfolio/bro/degiro/lookup: view search for a product by name - portfolio/bro/degiro/create: create an order - portfolio/bro/degiro/update: update an order - portfolio/bro/degiro/cancel: cancel an order using the id - portfolio/bro/degiro/pending: view pending orders - portfolio/bro/degiro/companynews: view news about a company with it's isin - portfolio/bro/degiro/lastnews: view latest news - portfolio/bro/degiro/topnews: view top news preview - portfolio/bro/degiro/paexport: csv export for portfolio analysis - portfolio/bro/rh/login: login to robinhood - portfolio/bro/rh/holdings: show account holdings in stocks - portfolio/bro/rh/history: show equity history of your account - portfolio/bro/cb/account: show balance of your account - portfolio/bro/cb/history: show history of your account - portfolio/bro/cb/deposits: show all your deposits or internal transfers - portfolio/bro/cb/orders: show all your orders - portfolio/po/file: file that contains portfolio risk parameters (use about for the parameter template) - portfolio/po/load: load tickers and categories from .xlsx or .csv file (use load --example for an example) - portfolio/po/_loaded: Portfolio loaded - portfolio/po/_tickers: Tickers - portfolio/po/_categories: Categories - portfolio/po/params/load: select portfolio parameter file (use about for the parameter template) - portfolio/po/params: specify and show portfolio risk parameters - portfolio/po/_parameter: Parameter file - portfolio/po/_mean_risk_optimization_: Mean Risk Optimization - portfolio/po/maxsharpe: maximal Sharpe ratio portfolio (a.k.a the tangency portfolio) - portfolio/po/minrisk: minimum risk portfolio - portfolio/po/maxutil: maximal risk averse utility function - portfolio/po/maxret: maximal return portfolio - portfolio/po/maxdiv: maximum diversification portfolio - portfolio/po/maxdecorr: maximum decorrelation portfolio - portfolio/po/blacklitterman: black litterman portfolio - portfolio/po/ef: show the efficient frontier - portfolio/po/_risk_parity_optimization_: Risk Parity Optimization - portfolio/po/riskparity: risk parity portfolio using risk budgeting approach - portfolio/po/relriskparity: relaxed risk parity using least squares approach - portfolio/po/_hierarchical_clustering_models_: Hierarchical Clustering Models - portfolio/po/hrp: hierarchical risk parity - portfolio/po/herc: hierarchical equal risk contribution - portfolio/po/nco: nested clustering optimization - portfolio/po/_other_optimization_techniques_: Other Optimization Techniques - portfolio/po/equal: equal weighted portfolio - portfolio/po/mktcap: weighted according to market cap - portfolio/po/_optimized_portfolio: Optimized portfolio - portfolio/po/rpf: remove portfolios from the list of saved portfolios - portfolio/po/show: show selected portfolios and categories from the list of saved portfolios - portfolio/po/plot: plot selected charts from the list of saved portfolios - portfolio/po/params/_loaded: Loaded file - portfolio/po/params/file: load portfolio risk parameters - portfolio/po/params/save: save portfolio risk parameters to specified file - portfolio/po/params/_model: Model of interest - portfolio/po/params/clear: clear model of interest from filtered parameters - portfolio/po/params/set: set model of interest to filter parameters - portfolio/po/params/arg: set a different value for an argument - portfolio/po/params/_parameters_: Parameters - dashboards/stocks: historic stock information - dashboards/forecast: forecasting for timeseries datasets - dashboards/forecasting: forecasting for timeseries datasets - dashboards/indicators: technical analysis indicators - dashboards/correlation: stock correlations - dashboards/chains: options chain analysis - dashboards/shortdata: finra shortdata analysis - dashboards/futures: historical futures performance - dashboards/options: cboe options analysis - reports: customizable research reports through jupyter notebooks - reports/_reports_: Build and run your custom reports or try one of our templates - reports/_OpenBB_reports_: OpenBB reports - reports/_Custom_reports_: Custom reports - reports/run: Run a Jupyter notebook from OpenBBUserData/reports/custom reports - forecast/_disclaimer_: DISCLAIMER - forecast/_data_loc: Looking for data in - forecast/load: load a dataset from csv - forecast/_loaded: Loaded files and data columns - forecast/_exploration_: Exploration - forecast/show: Show a portion of a loaded dataset - forecast/plot: Plot a specific column of a loaded dataset - forecast/clean: Clean a dataset by filling or dropping NaNs - forecast/setndays: Set the default number of days to forecast - forecast/desc: Show descriptive statistics of a dataset - forecast/corr: Plot the correlation coefficients for dataset features - forecast/season: Plot the seasonality for a dataset column - forecast/combine: Combine columns from different datasets - forecast/delete: Delete columns from dataset - forecast/rename: Rename columns from dataset - forecast/export: Export a processed dataset - forecast/_feateng_: Feature Engineering - forecast/ema: Add Exponentially Weighted Moving Average - forecast/sto: Add Stochastic Oscillator %K and %D - forecast/rsi: Add Relative Strength Index - forecast/roc: Add Rate of Change - forecast/mom: Add Momentum - forecast/delta: Add % Change - forecast/atr: Add Average True Range - forecast/signal: Add Price Signal (short vs. long term) - forecast/_tsforecasting_: TimeSeries Forecasting - forecast/autoarima: Automatic ARIMA Model - forecast/autoselect: Select best statistical model from AutoARIMA, AutoETS, AutoCES, MSTL, etc. - forecast/autoces: Automatic Complex Exponential Smoothing Model - forecast/autoets: Automatic ETS (Error, Trend, Seasonality) Model - forecast/mstl: Multiple Seasonalities and Trend using Loess (MSTL) Model - forecast/rwd: Random Walk with Drift Model - forecast/arima: Arima (Non-darts) - forecast/expo: Probabilistic Exponential Smoothing - forecast/theta: Theta Method - forecast/linregr: Probabilistic Linear Regression (feat. Past covariates and Explainability) - forecast/regr: Regression (feat. Past covariates and Explainability) - forecast/rnn: Probabilistic Recurrent Neural Network (RNN, LSTM, GRU) - forecast/brnn: Block Recurrent Neural Network (RNN, LSTM, GRU) (feat. Past covariates) - forecast/nbeats: Neural Bayesian Estimation (feat. Past covariates) - forecast/seasonalnaive: Seasonal Naive Model - forecast/tcn: Temporal Convolutional Neural Network (feat. Past covariates) - forecast/trans: Transformer Network (feat. Past covariates) - forecast/tft: Temporal Fusion Transformer Network(feat. Past covariates) - forecast/nhits: Neural Hierarchical Interpolation (feat. Past covariates) - forecast/_anomaly_: Anomaly Detection - forecast/anom: Quantile Anomaly Detector - forecast/_misc_: Miscellaneous AI Tools - forecast/whisper: Transcribe, translate and summarize videos - forecast/_comingsoon_: Coming Soon diff --git a/openbb_terminal/miscellaneous/i18n/help_translation.ipynb b/openbb_terminal/miscellaneous/i18n/help_translation.ipynb deleted file mode 100644 index 503a00562a7..00000000000 --- a/openbb_terminal/miscellaneous/i18n/help_translation.ipynb +++ /dev/null @@ -1,195 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "4233a439-35bf-49d8-bf3f-eebd26f8def5", - "metadata": {}, - "source": [ - "# Help translating terminal to other languages" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "20093add-f935-4007-b984-cf883e1c0ce8", - "metadata": {}, - "outputs": [], - "source": [ - "# Install libraries if they are not installed locally\n", - "# !pip install pyyaml\n", - "# !pip install txtai[pipeline]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "45a25f43-fcd3-4ed4-8a4c-fed3cd536cb5", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import yaml\n", - "from txtai.pipeline import Translation\n", - "\n", - "translate = Translation()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7f52cc10-293e-48d6-b405-3a4e5fff990c", - "metadata": {}, - "outputs": [], - "source": [ - "# Select language to translate\n", - "language_to_translate = \"pt\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d9978930-cc32-4fb9-b799-144d1de7b574", - "metadata": {}, - "outputs": [], - "source": [ - "# Read default english dictionary\n", - "english_dictionary = {}\n", - "with open(\"en.yml\", \"r\") as stream:\n", - " try:\n", - " english_dictionary = yaml.safe_load(stream)[\"en\"]\n", - " except yaml.YAMLError as exc:\n", - " print(exc)\n", - "\n", - "# Translated file\n", - "translated_file = f\"{language_to_translate}.yml\"" - ] - }, - { - "cell_type": "markdown", - "id": "154f36cf-b260-432f-b9c7-0997ee970795", - "metadata": { - "tags": [] - }, - "source": [ - "## Update other language dictionary or create from scratch" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b76629e7-9b85-41bf-be59-d21af4dd465c", - "metadata": {}, - "outputs": [], - "source": [ - "language_dictionary = {}\n", - "\n", - "if os.path.exists(translated_file):\n", - " # Read already translated dictionary\n", - " with open(translated_file, \"r\") as stream:\n", - " try:\n", - " language_dictionary = yaml.safe_load(stream)[language_to_translate]\n", - " except yaml.YAMLError as exc:\n", - " print(exc)\n", - "\n", - " # Iterate through english dictionary\n", - " for k in english_dictionary:\n", - " # Check if that variable doesn't existed on the already translated dictionary\n", - " if k not in language_dictionary:\n", - " # If the variable doesn't exist, let's update it to it's default\n", - " language_dictionary[k] = translate(\n", - " english_dictionary[k], language_to_translate\n", - " )\n", - "\n", - "else:\n", - " # Iterate through english dictionary\n", - " for k in english_dictionary:\n", - " try:\n", - " # Replace english value by its translation\n", - " language_dictionary[k] = translate(\n", - " english_dictionary[k], language_to_translate\n", - " )\n", - " except:\n", - " # Keep english language because there was an issue with the convertion\n", - " language_dictionary[k] = english_dictionary[k]\n", - "\n", - "# Save dictionary to another language\n", - "with open(f\"{language_to_translate}.yml\", \"w\") as file:\n", - " yaml.dump(\n", - " {language_to_translate: language_dictionary}, stream=file, allow_unicode=True\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "beb0bb1a-aca2-43c8-80b0-466919b46c79", - "metadata": {}, - "source": [ - "## Update all other languages based on english dictionary" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8dac2d8b-a429-4350-8b34-5143d02f35a2", - "metadata": {}, - "outputs": [], - "source": [ - "for language_to_translate in [\n", - " val.split(\".\")[0] for val in os.listdir() if val.endswith(\"yml\") and val != \"en.yml\"\n", - "]:\n", - " # Read already translated dictionary\n", - " with open(translated_file, \"r\") as stream:\n", - " try:\n", - " language_dictionary = yaml.safe_load(stream)[language_to_translate]\n", - " except yaml.YAMLError as exc:\n", - " print(exc)\n", - "\n", - " # Iterate through english dictionary\n", - " for k in english_dictionary:\n", - " # Check if that variable doesn't existed on the already translated dictionary\n", - " if k not in language_dictionary:\n", - " # If the variable doesn't exist, let's update it to it's default\n", - " language_dictionary[k] = translate(\n", - " english_dictionary[k], language_to_translate\n", - " )\n", - "\n", - " # Save dictionary to another language\n", - " with open(f\"{language_to_translate}.yml\", \"w\") as file:\n", - " yaml.dump(\n", - " {language_to_translate: language_dictionary},\n", - " stream=file,\n", - " allow_unicode=True,\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "116e615b-cc8f-4694-ab87-a422b3cd5c89", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/.gitkeep b/openbb_terminal/miscellaneous/integration_tests_scripts/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/README.MD b/openbb_terminal/miscellaneous/integration_tests_scripts/README.MD deleted file mode 100644 index e42a7e27949..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/README.MD +++ /dev/null @@ -1,119 +0,0 @@ -# Integration tests - -## Table of Contents - -- [Integration tests](#integration-tests) - - [Table of Contents](#table-of-contents) - - [What is it?](#what-is-it) - - [OpenBB Terminal integration tests](#openbb-terminal-integration-tests) - - [How to list the available tests](#how-to-list-the-available-tests) - - [How to run a test](#how-to-run-a-test) - - [Run all tests](#run-all-tests) - - [Run a specific test](#run-a-specific-test) - - [Custom flags](#custom-flags) - -## What is it? - -Integration tests are a type of software testing that is used to validate the interactions between different components or modules of an application. Unlike unit tests, which test individual pieces of code in isolation, integration tests verify that the system's different components work together correctly. - -The purpose of integration tests is to identify problems that arise when the various parts of an application are integrated. Integration tests help to ensure that the software works as a whole and that different modules can communicate and exchange data correctly. - -Integration tests are often used to test the following: - -- Integration between different software modules or services -- Integration between an application and external dependencies such as databases, APIs, and other third-party services -- End-to-end functionality of the entire system - -Integration tests can be automated or performed manually. They are typically more complex and take longer to run than unit tests, as they require the integration of multiple components. However, integration tests are an essential part of a comprehensive testing strategy and can help to identify issues that may be missed by unit tests. - -## OpenBB Terminal integration tests - -Those can be found and organized by menu as follows: - -- `stocks` menu: `openbb_terminal/miscellaneous/integration_tests_scripts/stocks/`, tests within the folder: - - `openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_stocks_ba.openbb` - - `openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_stocks_qa.openbb` - - ... -- `economy` menu: `openbb_terminal/miscellaneous/integration_tests_scripts/economy/`, tests within the folder: - - `openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_economy.openbb` -- ... - -The structure of each test should be a combination of the commands found in the terminal, here is an example you can find [here](openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_stocks_options_payoff.openbb): - -```bash - -stocks -options -load aapl -exp 0 -payoff -list -pick long -add 20 -add 15 -add 10 -p -sop -plot -exit - -``` - -Output: - -```bash - -* Collected 69 script(s)... -* Skipping 0 script(s)... - -Available scripts: -0. account/test_account.openbb -1. alternative/test_alt_covid.openbb -2. alternative/test_alt_oss.openbb -3. alternative/test_alt_realestate.openbb -4. cryptocurrency/test_crypto.openbb -5. cryptocurrency/test_crypto_dd.openbb -6. cryptocurrency/test_crypto_defi.openbb -7. cryptocurrency/test_crypto_disc.openbb -8. cryptocurrency/test_crypto_nft.openbb -9. cryptocurrency/test_crypto_onchain.openbb -10. cryptocurrency/test_crypto_ov.openbb -... -``` - -### How to list the available tests - -```bash -python terminal.py -t -l -``` - -### How to run a test - -#### Run all tests - -```bash -python terminal.py -t -``` - -#### Run a specific test - -```bash -python terminal.py -t 2 -``` - -> Here the number `2` is the index of the test you want to run, you can find the index by running `python terminal.py -t -l` - -#### Custom flags - -We can add the following flags to either run all tests or a specific test: - -- `subproc` to choose the number of subprocesses to run the tests in parallel. - -```bash -python terminal.py -t 2 --subproc 2 -``` - -- `verbose` to print the output of the tests inside the OpenBB Terminal. - -```bash -python terminal.py -t 2 --subproc 0 --verbose -``` \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/account/test_account.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/account/test_account.openbb deleted file mode 100644 index 2dcc55eb241..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/account/test_account.openbb +++ /dev/null @@ -1,15 +0,0 @@ -# GUEST -whoami -account -clear -list -upload -download -delete -generate -show -revoke -logout -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_alt.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_alt.openbb deleted file mode 100644 index 19b61c2b9d2..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_alt.openbb +++ /dev/null @@ -1,11 +0,0 @@ -alternative -hn -l 5 -covid -.. -oss -.. -realestate -.. -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_covid.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_covid.openbb deleted file mode 100644 index dabed0b273d..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_covid.openbb +++ /dev/null @@ -1,10 +0,0 @@ -alternative -covid -country ${country=Australia} -reset -slopes -ov -deaths -cases -rates -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_oss.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_oss.openbb deleted file mode 100644 index aac5e84386d..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_oss.openbb +++ /dev/null @@ -1,10 +0,0 @@ -alternative -oss -rs ${repo=openbb-finance/openbbterminal} -sh ${repo=openbb-finance/openbbterminal} -tr -rossidx -rossidx --sortby Raised [$M] --reverse -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_realestate.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_realestate.openbb deleted file mode 100644 index 8b6bb5d998b..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/alternative/test_realestate.openbb +++ /dev/null @@ -1,10 +0,0 @@ -alternative -realestate -sales -p CV67EB --limit 3 -? -regionstats -r york -townsales -t London -s 2022-01-01 -e 2022-01-03 -townsales -town London -startdate 2022-01-01 -enddate 2022-01-03 -limit 3 -reset -help -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_crypto.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_crypto.openbb deleted file mode 100644 index 88bd6c1304b..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_crypto.openbb +++ /dev/null @@ -1,59 +0,0 @@ -crypto -load ${crypto=eth} - -# # Find -find ${crypto=eth} -# find ${crypto=eth} --key symbol -# find ${crypto=eth} --key id -# find ${crypto_full=ethereum} --key name -# find ${crypto=eth} --limit 5 -# find ${crypto=eth} --skip 3 # Bug that only skips second similar coins but not first -# find ${crypto=eth} --source CoinGecko -# find ${crypto=eth} --source CoinPaprika -# find ${crypto=eth} --source Binance -# find ${crypto=eth} --source Coinbase -# # Bug with this source -# # find ${crypto=eth} --source Yahoofinance - -# # Price -# # price ${crypto=eth} -# price ${crypto=eth} - -# # Headlines -headlines -headlines ${crypto=ETH} - -# # Candle -candle -candle --log - -# PRT -# prt --vs ${crypto_vs=btc} -# Bug where we need --vs when we don't actually need it -# prt --top 3 -# Bug where --price doesn't seem to do anything -# prt --vs ${crypto_vs=btc} --price 1000 - -dd -.. -defi -.. -disc -.. -forecast -.. -nft -.. -onchain -.. -ov -.. -qa -.. -ta -.. -tools -.. -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_dd.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_dd.openbb deleted file mode 100644 index bd8e4041446..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_dd.openbb +++ /dev/null @@ -1,81 +0,0 @@ -crypto -load ${crypto=eth} -dd - -info - -ath --vs ${currency_vs=usd} -atl --vs ${currency_vs=usd} - -web -bc -pi -gov -basic - -stats -stats --vs ${currency_vs=GBP} - -desc -market - -mkt -mkt --urls -mkt --vs ${crypto_vs=ETH} --limit 3 --sort volume --urls -mkt --vs {crypto_vs=ETH} --limit 3 --sort volume --urls -mkt --vs {crypto_vs=BTC} -s volume -r - -ex -ex --limit 3 --sort adjusted_volume_24h_share - -balance - -trades -trades -e aax -trades --vs ${currency_vs=GBP} - -ob -ob -e aax -ob --vs ${currency_vs=GBP} - -ps -ps --vs ${currency_vs=ETH} - -mcapdom -mt -team -inv -tk -fr -rm - - -events -events --limit 3 --sort name --urls -events --sort date -r - -news -news --limit 3 -k media -r en --filter bullish -news --sort published_at --reverse - -links - -twitter -twitter --limit 3 --sort retweet_count -twitter --sort date -r - -active -s 2021-05-02 -u 2021-10-10 -active -s 2021-05-02 -u 2021-10-10 -i 1w -active -s 2021-05-02 -u 2021-10-10 -i 1month -change -s 2021-05-02 -u 2021-10-10 -eb -s 2021-05-02 -u 2021-10-10 -nonzero -s 2021-05-02 -u 2021-10-10 - -social -score -dev -gh - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_defi.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_defi.openbb deleted file mode 100644 index d8489587372..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_defi.openbb +++ /dev/null @@ -1,80 +0,0 @@ -crypto -defi - -# newsletter -newsletter -newsletter --limit 3 - -# dpi BROKEN - -# vaults -vaults -vaults --chain ${crypto_full=ethereum} --protocol aave --kind stable --top 3 --links -vaults --chain ${crypto_full=ethereum} -s apy -r - -# tokens -tokens # this is running into a timeout issue -tokens --skip 3 --limit 3 --sort totalLiquidity -tokens --sort txCount --reverse - -# stats -stats - -lcsc -d 2 -l 10 - -# pairs -pairs --limit 3 --vol 150 --tx 150 --days 15 --sort volumeUSD -pairs --sort volumeUSD -r - -# pools -pools -pools --limit 3 --sort volumeUSD -pools --sort volumeUSD --reverse - -# swaps -swaps --limit 3 --sort amountUSD -swaps --sort Datetime -r - -# ldapps -ldapps -ldapps --limit 3 --sort tvl -ldapps --sort change_7d -r - -# gdapps -gdapps -gdapps --limit 3 - -# stvl -stvl -stvl --limit 3 - -# dtvl -dtvl --dapps anchor - -# sinfo -sinfo -a terra13kc0x8kr3sq8226myf4nmanmn2mrk9s5s9wsnz -l 10 - -# validators -validators --sort validatorName --reverse - -# gacc -gacc -gacc --cumulative --limit 200 --kind active - -# sreturn -sreturn -sreturn --limit 200 - -# lcsc - -# anchor -anchor --address terra13kc0x8kr3sq8226myf4nmanmn2mrk9s5s9wsnz -anchor --address terra13kc0x8kr3sq8226myf4nmanmn2mrk9s5s9wsnz --transactions - -help -reset -exit - -# TERRA ADDRESSES FOR EXAMPLES -# terra1tmnqgvg567ypvsvk6rwsga3srp7e3lg6u0elp8 -# terra13kc0x8kr3sq8226myf4nmanmn2mrk9s5s9wsnz diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_discovery.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_discovery.openbb deleted file mode 100644 index 368abaa5229..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_discovery.openbb +++ /dev/null @@ -1,36 +0,0 @@ -crypto -disc - -# gctop -top -top -c stablecoins -s Volume [$] -l 5 -top -s Market Cap -r - -# cgtrending -trending - -# cggainers -# gainers # bugged -> takes too long -# gainers -s Price [$] -l 5 -i 14d # bugged -> takes too long - -# cglosers -#losers # bugged -> takes too long -# losers -s Price [$] -l 5 -i 14d # bugged -> takes too long - -# cpsearch CURRENTLY BUGGY -search ${crypto=bitcoin} -search -q ${crypto=bitcoin} -s name -search -q ${crypto=bitcoin} -c exchanges -l 2 - -nft_mktp_chains -nft_mktp -dapps -dapp_categories -dapp_chains -dapp_metrics --dappId 3 -defi_chains -tokens - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_nft.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_nft.openbb deleted file mode 100644 index 461818da48e..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_nft.openbb +++ /dev/null @@ -1,17 +0,0 @@ -crypto -nft - -# stats -stats --slugs cryptopunks - -# fp -fp --slug cryptopunks --limit 3 --raw - -# collections -collections --limit 3 --fp -collections -fp -collections --limit 3 --sales - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_onchain.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_onchain.openbb deleted file mode 100644 index 79af8081a33..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_onchain.openbb +++ /dev/null @@ -1,89 +0,0 @@ -crypto -onchain - -# hr - -btccp -btccp --since 2012-01-01 --until 2022-01-01 - -btcct -btcct --since 2012-01-01 --until 2022-01-01 - -gwei - -whales -whales --address # this is bugged -whales --min 5000000 --limit 3 --sort amount -whales --sort amount_usd -r - -# topledger -topledger -o tensor -q daily-transactions -topledger -o tensor -q weekly-users - -baas -c ETH -vs BTC -l 10 -s date -r - - -lt -lt --kind time --vs ${crypto=ETH} --limit 3 --days 7 --sort tradeAmount - -dvcp --coin ${crypto=ETH} -dvcp --coin ${crypto=ETH} --limit 3 --vs ${crypto=BTC} --days 20 --sort high # limit is bugged - -tv --coin ${crypto=ETH} -tv --coin ${crypto=ETH} --limit 3 --vs ${crypto=BTC} --sort tradeAmount - -ueat -ueat --limit 3 --sort transactions --interval month - -# ttcp BUGGED -ttcp -ttcp Bitox --limit 3 -ttcp Bitox --days 10 -ttcp Bitox --sort tradeAmount - -# baas BROKEN - -# dt - -# ds - -# tvl - -# address -address 0x71C7656EC7ab88b098defB751B7401B5f6d8976F - -# top -top -top --limit 3 --sort price - -# balance -balance --sort index -r - -# hist -hist --sort value -r --limit 20 - -# loads ECR20 token -address 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 -t - -# info -info --social - -# holders -holders --sort share -r - -# th -th --sort value --reverse - -# prices -prices --sort volumeConverted --reverse - -# loads transaction hash -address 0x9dc7b43ad4288c624fdd236b2ecb9f2b81c93e706b2ffd1d19b112c1df7849e6 -tx - -hr -i 1w - -tx - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_overview.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_overview.openbb deleted file mode 100644 index 6d2be099638..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_overview.openbb +++ /dev/null @@ -1,114 +0,0 @@ -crypto -ov - -# global -global -global --pie - -# defi -defi - -# stables -stables -stables -p -stables --pie -stables --sortbyby Price_[$] --reverse --limit 3 - -# exchanges -exchanges -exchanges --source CoinGecko -exchanges --source CoinPaprika -exchanges --urls -exchanges --vs ${crypto_vs=ETH} -exchanges -vs ${crypto_vs=ETH} -exchanges --source CoinGecko --limit 3 --sortby Trust_Score -r - -# exrates -exrates -exrates --sortby Value --reverse --limit 3 - -# indexes -indexes -indexes --sortby Last --limit 3 -r - -# derivatives -derivatives -derivatives --sortby Basis --limit 3 - -# categories -categories -categories --pie -categories --sortby Market_Cap --limit 3 - -# hold -hold -hold --bar -hold --limit 1 --coin ethereum --bar - -# hm -hm -hm --category stablecoins --limit 3 - -# info -info -info --vs ${crypto_vs=ETH} --sortby price --limit 3 - -# markets -markets -markets --vs ${crypto_vs=ETH} --sortby price --limit 3 - -# exmarkets -exmarkets -exmarkets --limit 3 --sortby quote_currency_name -exmarkets --urls - -# platforms -platforms - -# contracts -contracts -contracts --platform eth-ethereum --limit 3 --sortby type - -# pairs -pairs - -# news -news -news --region es -news --limit 3 --kind media --filter hot --sortby title - -# wf -wf -wf --limit 3 - -# ewf -ewf - -# wfpe -wfpe -wfpe -c ${crypto_full=ethereum} - -# altindex -altindex -altindex --period 30 --since 2020-10-25 --until 2021-12-01 - -btcrb - -# ch -ch -ch --slug polynetwork-rekt -ch -s wormhole-rekt -ch --sortbyby Amount [$] -r --limit 3 - -# cr -cr -cr -t borrow --limit 1 -cr --cryptocurrrencies ZRX -cr --platforms Compound -cr --cryptocurrrencies ZRX --platforms Compound - -fun --metric market_cap - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_qa.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_qa.openbb deleted file mode 100644 index 5d546c9c2be..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_qa.openbb +++ /dev/null @@ -1,77 +0,0 @@ -crypto -load ${crypto=eth} -qa - -# summary -summary - -# normality -normality - -# unitroot -unitroot -unitroot -r ct -k ct - -# line -line -line -l -line -log -line --log -line --ml 2022-01-01 --ms 2022-06-06 - -# hist -hist -hist -b 20 - -# cdf -cdf - -# bw -bw -bw -y - -# acf -acf -acf -l 20 - -# qqplot -qqplot - -# rolling -rolling -rolling -w 20 - -# spread -spread -spread --window 20 - -# qauntile -quantile -quantile -w 20 -q .1 - -# skew -skew -skew -w 20 - -# kurtosis -kurtosis -kurtosis -w 20 - -# raw -raw -raw -l 3 -raw --sortby adjclose --reverse - -# decompose -decompose -decompose -m - -# cusum -cusum -cusum -t 120 -d 60 - -pick -t Open - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_ta.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_ta.openbb deleted file mode 100644 index 79d4156ec3a..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_ta.openbb +++ /dev/null @@ -1,70 +0,0 @@ -crypto -load ${crypto=eth} -ta - -tv - -###### Overlap -ema -ema -l 100 -o 10 -hma -hma -l 100 -o 10 -sma -sma -l 100 -o 10 -vwap -wma -wma -l 100 -o 10 -zlma -zlma -l 100 -o 10 - -###### Momentum -cci -cci --length 10 --scalar 10 -cg -cg --length 10 -demark -demark --min 10 -fisher -fisher --length 10 -macd -macd --fast 10 --slow 10 --signal 5 -rsi -rsi -l 10 -s 10 -d 2 -stoch -stoch -k 10 -d 10 --slowkperiod 2 - -###### Trend -adx -adx -l 10 -s 10 -d 10 -aroon -aroon -l 10 -s 10 - -###### Volatility -atr -atr -l 10 -o 1 -m sma -atr -m ema -atr -m wma -atr -m hma -atr -m zlma -bbands -bbands -l 10 -s 0.5 -m ema -cones -cones --lower_q 0.3 --upper_q 0.7 --is_crypto --model Parkinson -donchian -donchian -u 10 -l 10 -kc -kc -l 10 -s 10 -m sma -o 1 - -###### Volume -ad -adosc -adosc --open --fast 10 --slow 10 -obv - -###### Custom -fib -fib --period 10 --start 2022-01-01 --end 2022-03-03 - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_tools.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_tools.openbb deleted file mode 100644 index 705fcb0bbd1..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/cryptocurrency/test_tools.openbb +++ /dev/null @@ -1,12 +0,0 @@ -crypto -tools - -aprtoapy -aprtoapy --narrative --apr 50 --compounding 6 - -il -il --narrative --priceChangeA 5 --priceChangeB 3 --proportion 2 --value 10 - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/dashboards/test_dashboards.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/dashboards/test_dashboards.openbb deleted file mode 100644 index 6d068e81361..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/dashboards/test_dashboards.openbb +++ /dev/null @@ -1,13 +0,0 @@ -dashboards -stocks -n -correlation -n -vsurf -n -chains -n -shortdata -n -crypto -n -futures -n -forecast -n -forecasting -n -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/econometrics/test_econometrics.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/econometrics/test_econometrics.openbb deleted file mode 100644 index 6f9bbd6d662..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/econometrics/test_econometrics.openbb +++ /dev/null @@ -1,66 +0,0 @@ -econometrics - -## Exploration - -load nile -ret -v nile.volume -garch -v nile.volume_returns -desc nile -load nile -a nile_2 -eval double_volume = volume * 2 -lag -v custom.double -l 5 -combine nile -c nile_2.volume -load -f 20221008_221605_dataset.xlsx -rename -d 20221008_221605_dataset -o population -n pop -load -e -load -f wage_panel -a wp -corr -d wp -season wp.hours -add -n wp.after_1990 -b wp.year -s > -c 1990 -add -n wp.after_1985 -b wp.year -s > -c 1985 -show -n wp -s year -l 20 -plot -v wp.nr,wp.exper,wp.hours -index -n wp -i nr,year -a -type -type wp.year --format category -desc -n wp.expersq -clean wp --fill cfill -clean wp -d rdrop -delete -d wp.black - -## Tests - -load fair -root fair.yrs_married - -## Load custom datasets - this can only be tested locally for now -## load -f 20221031_095631_AAPL.xlsx -a aapl -## load -f 20221031_095653_TSLA.xlsx -a tsla -## load -f 20221031_095706_MSFT.xlsx -a msft -## coint msft.adj_close,aapl.adj_close,tsla.adj_close -## norm tsla.adj_close -p -## norm msft.volume -p - -load strikes -granger strikes.duration,strikes.iprod - -## Regression - -panel -d wp.lwage -i wp.black,wp.hisp,wp.exper,wp.expersq,wp.married,wp.educ,wp.union,wp.year -panel -d wp.lwage -i wp.black,wp.hisp,wp.exper,wp.married,wp.educ,wp.union -r bols - -load longley -a ll -ols -d ll.totemp -i ll.gnpdefl,ll.gnp,ll.unemp,ll.armed,ll.pop,ll.year -vif ll.totemp,ll.unemp,ll.armed - -## Regression Tests - -dwat -p -bgod -l 2 -bpag - -remove -n fair - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_economy.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_economy.openbb deleted file mode 100644 index 16ddefd0c56..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_economy.openbb +++ /dev/null @@ -1,82 +0,0 @@ -economy -overview -overview -t indices -overview -t usbonds -overview -t glbonds -overview -t currencies -futures -futures -c energy --source Finviz -futures -c metals --source Finviz -futures -c meats --source Finviz -futures -c grains --source Finviz -futures -c softs --source Finviz -map -map -p 1w -t world -bigmac -bigmac -c USA,ESP,LVA,DEU,FRA,FIN -events -events --countries united_states -events -c united_states,united_kingdom -events -n -edebt --limit 10 -usdli -usdli -o NASDAQ100 -valuation -valuation --group sector -performance -performance -g basic_materials -performance -g energy -gdp -rgdp -fgdp -debt --countries australia -debt --raw -cpi -ccpi -balance -revenue -spending -trust -trust --export csv -trust --help -gdp --units USD_CAP -rgdp --units IDX -fgdp --quarterly -debt -cpi --units growth_same -ccpi -balance --countries austria,belgium,canada -revenue --countries united_states,germany,japan -spending --perspective ENVPROT -macro -macro -p RGDP,CPI -macro -p URATE -c Netherlands,Germany --transform TOYA -macro -p URATE -c United_States -macro -p GDP --countries Portugal,France,United_Kingdom,Spain,Italy,Poland -macro --help -treasury -treasury --maturity 20y --type nominal -treasury --raw -treasury --type inflation -treasury -m 10y -fred -p T10Y2Y,DFII10 -fred -q treasury -fred -p T10Y2Y,FII10 -fred -p T5YIEM,T10YIEM,T20YIEM,T30YIEM -fred -h -index -i nasdaq,dow_djus,sp500 -macro -p URATE -c Netherlands,Germany -index -i sp500 -index -i sp500 --raw -index -q ftse --limit 30 -plot --y1 T10Y2Y --y2 T5YIEM -plot --y1 T10Y2Y,T20YIEM -valuation -performance -fred -p DGS2,DGS5 -eval spread = DGS2 - DGS5 -help -.. -reset -quit -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_qa.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_qa.openbb deleted file mode 100644 index 860336c4f01..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/economy/test_qa.openbb +++ /dev/null @@ -1,38 +0,0 @@ -economy -macro -treasury -qa - -# Statistics -summary -normality -unitroot - -# Plots -line -line --log -hist -hist -b 10 -cdf -bw -bw -y -acf -qqplot - -# Rolling Metrics -rolling -spread -quantile -skew -kurtosis - -# Other -raw -r -decompose -decompose -m -cusum - -pick united_states_CPI -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_ca.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_ca.openbb deleted file mode 100644 index ffd89f49cf4..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_ca.openbb +++ /dev/null @@ -1,7 +0,0 @@ -etf -load spy -l 3 -ca -volume -.. -quit -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_disc.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_disc.openbb deleted file mode 100644 index 8f2e115e9a0..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_disc.openbb +++ /dev/null @@ -1,6 +0,0 @@ -etf -disc -gainers -decliners -active -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_etf.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_etf.openbb deleted file mode 100644 index f1376c391dc..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_etf.openbb +++ /dev/null @@ -1,25 +0,0 @@ -etf -load spy -overview -holdings -holdings_perf --limit 3 -weights -weights --raw -candle -candle --raw -candle --sort adjclose --reverse --raw -candle -ma -candle -t --ma 4 -candle -t -ma -news -compare spy,ivv -disc -.. -ca -quit -help -search -n vol -ta -.. -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_ta.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_ta.openbb deleted file mode 100644 index e2ea69334a9..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_ta.openbb +++ /dev/null @@ -1,54 +0,0 @@ -etf -load spy -ta -ema -ema --length 10 --offset 3 -sma -sma --length 2 --offset 1 -wma -wma --length 99 --offset 4 -hma -hma --length 1 --offset 95 -zlma -zlma --length 1 --offset 1 -vwap -# vwap --offset 2 --start 2022-01-01 --end 2023-01-01 -cci -cci --length 2 --scalar 10 -macd -macd --fast 13 --slow 99 --signal 15 -rsi -rsi --length 2 --scalar 10 --drift 7 -stoch -stoch --fastkperiod 2 --slowdperiod 1 -fisher -fisher --length 2 -cg -cg --length 95 -clenow -clenow --period 120 -demark -demark --min 5 -adx -adx --length 4 --scalar 10 --drift 2 -aroon -aroon --length 2 --scalar 11 -bbands -bbands --length 2 --std 0.25 --mamode zlma -donchian -donchian -donchian --length_upper 2 --length_lower 1 -kc -kc --length 2 --scalar 1 --mamode ema --offset 4 -atr -atr --length 10 --mamode sma --offset 33 -ad -ad --open -adosc -adosc --open --fast 2 --slow 1 -obv -fib -fib --period 30 --start 2021-01-01 --end 2023-01-01 -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/fixedincome/test_fixedincome.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/fixedincome/test_fixedincome.openbb deleted file mode 100644 index e1d9180f208..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/fixedincome/test_fixedincome.openbb +++ /dev/null @@ -1,78 +0,0 @@ -fixedincome -estr -s 2022-01-01 -e 2023-01-01 -estr --parameter share_of_volume_of_the_5_largest_active_banks -estr --help -sofr -s 2022-01-01 -e 2023-01-01 -sofr -s 2022-01-01 -e 2023-01-01 -p overnight -sofr -s 2022-01-01 -e 2023-01-01 -p 30_day_average -sofr -s 2022-01-01 -e 2023-01-01 -p 90_day_average -sofr -s 2022-01-01 -e 2023-01-01 -p 180_day_average -sofr -s 2022-01-01 -e 2023-01-01 -p index -sofr --help -sonia -s 2022-01-01 -e 2023-01-01 -sonia --parameter total_nominal_value -sonia --help -ameribor -s 2022-01-01 -e 2023-01-01 --parameter 90_day_ma -ameribor --help -fed -s 2022-01-01 -e 2023-01-01 -fed --parameter volume --target --quantiles -fed --help -iorb -s 2022-01-01 -e 2023-01-01 --export xlsx --sheet-name IORB -iorb --help -projection --long-run -projection --help -dwpcr -s 2022-01-01 -e 2023-01-01 --parameter weekly -dwpcr --help -ecb -s 2022-01-01 -e 2023-01-01 --type refinancing -ecb --help -treasury --short australia --long united_kingdom,united_states -s 2022-01-01 -e 2023-01-01 --forecast -treasury --help -usrates -s 2022-01-01 -e 2023-01-01 -usrates --options -usrates --parameter cmn --maturity 7_year -usrates --help -ycrv -d 2023-02-17 --inflation-adjusted -ycrv --raw -d 2022-03-31 -ycrv --export csv -ycrv --help -ecbycrv -d 2023-02-17 -ecbycrv -d 2023-02-17 --any-rating --raw -ecbycrv --detailed --parameter instantaneous_forward -ecbycrv --help -icebofa -s 2022-01-01 -e 2023-01-01 --type total_return -icebofa --options -icebofa --grade ccc --type yield_to_worst --category usd -icebofa --area emea --description -icebofa --help -moody -s 2022-01-01 -e 2023-01-01 --spread treasury --raw -moody --type aaa -moody --help -cp -s 2022-01-01 -e 2023-01-01 -cp -s 2022-01-01 -e 2023-01-01 -c asset_backed -cp -s 2022-01-01 -e 2023-01-01 -c non_financial -cp -s 2022-01-01 -e 2023-01-01 -c spread -o -d -cp --maturity overnight -cp --raw -cp --grade a2_p2 -cp --help -spot -s 2022-01-01 -e 2023-01-01 --maturity 3y -spot --raw -spot --help -hqm -d 2023-02-17 -p --raw -hqm --help -icespread -s 2022-01-01 -e 2023-01-01 -icespread --options -icespread --category duration -icespread --help -tmc -s 2022-01-01 -e 2023-01-01 --parameter 2_year -tmc --help -ffrmc -s 2022-01-01 -e 2023-01-01 --parameter 10_year -ffrmc --help -tbffr -s 2022-01-01 -e 2023-01-01 --parameter 3_month -tbffr --help -help -.. -reset -fixedincome -quit -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/forecast/test_forecast.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/forecast/test_forecast.openbb deleted file mode 100644 index b2c9be2d73f..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/forecast/test_forecast.openbb +++ /dev/null @@ -1,47 +0,0 @@ -stocks -load gme -forecast -show GME -plot GME.close -clean GME -desc GME -corr GME -season GME.close -delete GME.open -rename GME -o volume -n vol -ema GME -sto GME -rsi GME -roc GME -mom GME -delta GME -atr GME -signal GME -expo GME -theta GME -linregr GME -regr GME -rnn GME -brnn GME -nbeats GME -tcn GME -trans GME -tft GME -.. -# Test Forecast Advanced -stocks -load GME -forecast -q -load TSLA -forecast -show TSLA -q -load AMC -forecast -show AMC -combine GME -c TSLA.close,AMC.close -brnn GME --past-covariates=volume,TSLA_close,AMC_close -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_forex.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_forex.openbb deleted file mode 100644 index 80411f3cb62..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_forex.openbb +++ /dev/null @@ -1,24 +0,0 @@ -forex -load EURUSD --source YahooFinance -load EURUSD --source Polygon -load EURUSD --source AlphaVantage -load ${currency=EUR}${currency_vs=USD} -load -t ${currency=EUR}${currency_vs=USD} -r d -i 1day -s 2000-01-01 -load -t ${currency=EUR}${currency_vs=USD} -r i -i 30min -fwd -quote -quote --source YahooFinance -quote --source AlphaVantage -candle --sort open -r --raw --limit 10 -candle -t -candle --ma 14 --log -help -oanda -qa -.. -ta -.. -forecast -.. -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_oanda.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_oanda.openbb deleted file mode 100644 index 43f507f5a57..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_oanda.openbb +++ /dev/null @@ -1,72 +0,0 @@ -forex -oanda -h -oanda -argument -oanda -from -h -from -argument -from ${currency=usd} -to -h -to -argument -to ${currency_vs=chf} -price -h -price -argument -price -from ${currency=eur} -price -from ${currency=ils} -price -summary -h -summary -from usd -to ${currency_vs=chf} -orderbook -h -orderbook -list -h -list -order -order -h -order -argument -order -u 3 -p 0.8 -list -cancel -cancel -h -cancel -argument -cancel -i -cancel -i 5 -positions -positions -h -positions -argument -pending -pending -h -pending -argument -trades -trades -h -trades -argument -closetrade -closetrade -h -closetrade -argument -closetrade -i 5 -candles -candles -h -candles -v -candles -a -b -c -e -v -calendar -calendar -h -calendar -argument -from ${currency=eur} -to ${currency_vs=jpy} -calendar -to gbp -calendar -h -help -? -q -oanda -.. -.. -forex -oanda -quit -oanda -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_qa.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_qa.openbb deleted file mode 100644 index 09d9f47c814..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_qa.openbb +++ /dev/null @@ -1,61 +0,0 @@ -forex -load ${currency=EUR}${currency_vs=USD} --source YahooFinance -qa -summary -normality -unitroot -unitroot --fuller_reg ctt --kps_reg c -line -line --log -hist -hist --bins 16 -cdf -bw -bw --yearly -acf -acf --lags 5 -qqplot -rolling -rolling --window 10 -spread -spread -w 5 -quantile -quantile -w 5 -q 0.01 -skew -skew --window 5 -kurtosis -kurtosis --w 10 -var -es -raw -raw --limit 10 --reverse -decompose -decompose --multiplicative -cusum -cusum -t 0.01 -d 0.01 -.. -load ${currency=EUR}${currency_vsUSD} --source YahooFinance -qa -summary -normality -unitroot -line -hist -cdf -bw -acf -qqplot -rolling -spread -quantile -skew -kurtosis -var -es -raw -decompose -cusum -pick -t High -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_ta.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_ta.openbb deleted file mode 100644 index 0ff92521e81..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/forex/test_ta.openbb +++ /dev/null @@ -1,51 +0,0 @@ -forex -load ${currency=EUR}${currency_vs=USD} --source YahooFinance -load -ta -ema -ema -l 20,30 -o 1 -sma -sma -l 20,30 -o 1 -zlma -zlma -l 20,30 -o 1 -cci -cci -l 7 -s 2 -macd -macd --fast 10 --slow 30 --signal 5 -rsi -rsi -l 10 -s 50 -d 2 -stoch -stoch --fastkperiod 2 --slowdperiod 2 --slowkperiod 7 -fisher -fisher --length 2 -cg -cg -l 7 -adx -adx -l 10 -s 110 -d 2 -aroon -aroon --length 1 --scalar 10 -bbands -bbands -l 1 -s 0.25 -m ema -donchian -donchian --length_upper 10 --length_lower 1 -fib -load ${currency=USD}${currency_vs=EUR} --source Polygon -ta -ema -sma -zlma -cci -macd -rsi -stoch -fisher -cg -adx -aroon -bbands -donchian -fib -ad -l 15 -d 2 -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/futures/test_futures.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/futures/test_futures.openbb deleted file mode 100644 index 7a57faaaae7..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/futures/test_futures.openbb +++ /dev/null @@ -1,11 +0,0 @@ -futures -search -d Cheese -search -c agriculture -search -c agriculture -d orange -historical OJ -s 2022-01-01 -e 2022-01-20 -historical M6B,MSF,M6E -curve OJ -curve CL --raw -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/keys/test_keys.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/keys/test_keys.openbb deleted file mode 100644 index 249f8938396..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/keys/test_keys.openbb +++ /dev/null @@ -1,3 +0,0 @@ -keys -mykeys -exit \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/mutual_funds/test_mutual_fund.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/mutual_funds/test_mutual_fund.openbb deleted file mode 100644 index 6eea25971be..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/mutual_funds/test_mutual_fund.openbb +++ /dev/null @@ -1,21 +0,0 @@ -funds -country --name united_states -search --fund morning -load --fund Vanguard -plot -c both -sector -t equity -sector -t fixed_income -holdings -t all -carbon -exclusion -country sweden -load F00000OW3P -alswe --help -alswe --focus sector -infoswe --help -infoswe -help -.. -reset -quit -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_params.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_params.openbb deleted file mode 100644 index 6b0edc236ee..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_params.openbb +++ /dev/null @@ -1,12 +0,0 @@ -portfolio -load --example -po -params -load -set ef -arg tangency False -arg log_returns True -clear -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_po.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_po.openbb deleted file mode 100644 index 5a162d50e8b..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_po.openbb +++ /dev/null @@ -1,99 +0,0 @@ -portfolio -po -load --example -file -maxsharpe -s 2022-10-20 -minrisk -r 0.05 -maxutil -a 0.1 -ra 2 -tk 0.4 -tr 8 -maxret -e 2022-11-20 -maxdiv -mn 0.1 -maxdecorr -lr -ef -t -se 69 -n 10 -equal -th 0.1 -riskparity -p 1y -relriskparity -de 0.9 -hrp -cd spearman -herc -at 0.1 -nco -rm MAD -show RP_1 -plot -pf MAXSHARPE_0 -pi -plot -pf MAXSHARPE_0 -hi -plot -pf MAXSHARPE_0 -dd -plot -pf MAXSHARPE_0 -rc -plot -pf MAXSHARPE_0 -he -rpf NAME_6 -.. -load --example -po -equal -maxsharpe -s 1995-05-07 -minrisk -r 0.08 -maxutil -a 0.1 -ra 2 -tk 0.4 -tr 8 -maxret -e 2022-11-20 -maxdiv -mn 0.1 -maxdecorr -lr -p max -ef -t -se 420 -n 0 -equal -th 0.1 -riskparity -p 1y -relriskparity -de 0.9 -hrp -cd pearson -herc -at 0.1 -.. -po -show -rpf -show -pf a,b -show -ct a,b -show -pf a,b -ct c,d -plot -plot -pf a,b -plot -ct a,b -plot -pf a,b -ct c,d -load --example -show -pf a,b -ct c,d -show -pf a -show -ct c -plot -pf a,b -ct c,d -plot -pf a -plot -ct c -equal -show -show -pf name_0 -show -ct sector -show -pf name_0 -ct sector -plot -plot -pf name_0 -plot -ct sector -plot -pf name_0 -ct sector -plot -pf name_0 -ct sector -pi -plot -pf name_0 -ct sector -hi -plot -pf name_0 -ct sector -dd -plot -pf name_0 -ct sector -rc -plot -pf name_0 -ct sector -he -maxsharpe -show -pf name_0,maxsharpe_1 -ct sector,country -show -pf name_0,maxsharpe_1,a -ct c,sector,currency,current_invested_amount -plot -pf name_0,maxsharpe_1 -ct c,sector,country,current_invested_amount -pi -plot -pf name_0,maxsharpe_1 -ct c,sector,current_invested_amount -hi -plot -pf name_0,maxsharpe_1 -ct c,sector,current_invested_amount -dd -plot -pf name_0,maxsharpe_1 -ct c,sector,current_invested_amount -rc -plot -pf name_0,maxsharpe_1 -ct c,sector,current_invested_amount -he -.. -load --example -po -equal -show -pf name_0 -show -pf name_0 -ct a,b -plot -pf name_0 -ct sector -pi -plot -pf name_0 -ct sector -hi -plot -pf name_0 -ct sector -dd -plot -pf name_0 -ct sector -rc -plot -pf name_0 -ct sector -he -maxsharpe -rpf -pf a,b,c -rpf -pf name_0,maxsharpe_1 -params -.. -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_portfolio.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_portfolio.openbb deleted file mode 100644 index 163115b0887..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/portfolio/test_portfolio.openbb +++ /dev/null @@ -1,126 +0,0 @@ -portfolio -load --example -show -bench QQQ -bench FAKE_BENCH -bench TSLA -alloc assets -t -l 4 -alloc sectors -t -l 4 -alloc countries -t -l 4 -alloc regions -t -l 4 -bench SPY -alloc assets -t -l 4 -alloc sectors -t -l 4 -alloc countries -t -l 4 -alloc regions -t -l 4 -attrib -attrib --raw -attrib --type absolute -summary -p mtd -r 0.5 -summary -p qtd -r 0.5 -summary -p ytd -r 0.5 -summary -p 3m -r 0.5 -summary -p 6m -r 0.5 -summary -p 1y -r 0.5 -summary -p 5y -r 0.5 -summary -p 10y -r 0.5 -summary -p all -r 0.5 -metric volatility -metric sharpe -r 0.5 -metric sortino -r 0.5 -metric maxdrawdown -metric rsquare -metric skew -metric kurtosis -metric gaintopain -metric trackerr -metric information -metric tail -metric commonsense -metric jensens -metric calmar -metric kelly -metric payoff -metric profitfactor -perf -perf -t -holdv -holdv --unstack --raw -l 4 -holdp -holdp --unstack --raw -l 4 -yret -yret -p 3y -yret -p 5y -yret -p 10y -yret -p all --raw -mret -mret -p 3y -s -mret -p 5y -s -mret -p 10y -s -mret -p all -s -mret -p all -s --raw -dret -dret -p 3y -dret -p 5y -dret -p 10y -dret -p all --raw -l 5 -distr -p mtd -distr -p qtd -distr -p ytd -distr -p 3m -distr -p 6m -distr -p 1y -distr -p 5y -distr -p 10y -distr -p all -maxdd -rvol -p mtd -rvol -p qtd -rvol -p ytd -rvol -p 3m -rvol -p 6m -rvol -p 1y -rvol -p 5y -rvol -p 10y -rvol -p all -rsharpe -p mtd -r 0.5 -rsharpe -p qtd -r 0.5 -rsharpe -p ytd -r 0.5 -rsharpe -p 3m -r 0.5 -rsharpe -p 6m -r 0.5 -rsharpe -p 1y -r 0.5 -rsharpe -p 5y -r 0.5 -rsharpe -p 10y -r 0.5 -rsharpe -p all -r 0.5 -rsort -p mtd -r 0.5 -rsort -p qtd -r 0.5 -rsort -p ytd -r 0.5 -rsort -p 3m -r 0.5 -rsort -p 6m -r 0.5 -rsort -p 1y -r 0.5 -rsort -p 5y -r 0.5 -rsort -p 10y -r 0.5 -rsort -p all -r 0.5 -rbeta -p mtd -rbeta -p qtd -rbeta -p ytd -rbeta -p 3m -rbeta -p 6m -rbeta -p 1y -rbeta -p 5y -rbeta -p 10y -rbeta -p all -var -var -p 80 -es -es -p 50 -om -om --start 0.05 --end 0.2 -q -bro -.. -po -.. -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/reports/test_reports.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/reports/test_reports.openbb deleted file mode 100644 index bb51d33eb93..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/reports/test_reports.openbb +++ /dev/null @@ -1,43 +0,0 @@ -reports -h -help -? -q -reports -.. -reports - -crypto -crypto --symbol ${crypto=BTC} - -economy - -equity -equity --symbol ${ticker=AAPL} - - -etf -etf --symbol VEA -etf --symbol VWCE -etf --symbol ABCEF - -forecast -forecast --symbol AAPL -forecast --symbol NXPI -forecast --symbol PETR3.SA - -forex -forex --symbol USDEUR -forex --symbol USDVND -forex --symbol USDABCXYZ - -portfolio --transactions holdings_example.xlsx -portfolio --transactions holdings_test.xlsx - -run -run --file dummyfile.csv -run --file dummyfile.ipynb - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ba.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ba.openbb deleted file mode 100644 index df8fe96274e..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ba.openbb +++ /dev/null @@ -1,22 +0,0 @@ -stocks -ba -load ${ticker=aapl} -headlines -wsb -popular -getdd -redditsent -trending -stalker -bullbear -messages -mentions -regions -interest -w yolo -queries -rise -snews -ns -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_bt.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_bt.openbb deleted file mode 100644 index 3cd0e689763..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_bt.openbb +++ /dev/null @@ -1,105 +0,0 @@ -stocks -load ${ticker=tsla} -bt -whatif -whatif -n 2.34 -whatif 2021-01-01 -ema -ema --spy -ema --spy --no_bench -ema -l 2 -emacross -emacross -l 10 -s 3 -emacross --spy --no_bench --no_short -rsi -rsi -l 20 -u 80 -p 12 -rsi --spy -rsi --no_bench --no_short -.. -load ${ticker=tsla} --source YahooFinance -bt -whatif -whatif -n 2.34 -whatif 2021-01-01 -ema -ema --spy -ema --spy --no_bench -ema -l 2 -emacross -emacross -l 10 -s 3 -emacross --spy --no_bench --no_short -rsi -rsi -l 20 -u 80 -p 12 -rsi --spy -rsi --no_bench --no_short -.. -load ${ticker=tsla} -bt -whatif -whatif -n 2.34 -whatif 2021-01-01 -ema -ema --spy -ema --spy --no_bench -ema -l 2 -emacross -emacross -l 10 -s 3 -emacross --spy --no_bench --no_short -rsi -rsi -l 20 -u 80 -p 12 -rsi --spy -rsi --no_bench --no_short -.. -load ${ticker=tsla} --source AlphaVantage -bt -whatif -whatif -n 2.34 -whatif 2021-01-01 -ema -ema --spy -ema --spy --no_bench -ema -l 2 -emacross -emacross -l 10 -s 3 -emacross --spy --no_bench --no_short -rsi -rsi -l 20 -u 80 -p 12 -rsi --spy -rsi --no_bench --no_short -.. -load ${ticker=tsla} --source Polygon -bt -whatif -whatif -n 2.34 -whatif 2021-01-01 -ema -ema --spy -ema --spy --no_bench -ema -l 2 -emacross -emacross -l 10 -s 3 -emacross --spy --no_bench --no_short -rsi -rsi -l 20 -u 80 -p 12 -rsi --spy -rsi --no_bench --no_short -.. -load ${ticker=tsla} --source EODHD -bt -whatif -whatif -n 2.34 -whatif 2021-01-01 -ema -ema --spy -ema --spy --no_bench -ema -l 2 -emacross -emacross -l 10 -s 3 -emacross --spy --no_bench --no_short -rsi -rsi -l 20 -u 80 -p 12 -rsi --spy -rsi --no_bench --no_short -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ca.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ca.openbb deleted file mode 100644 index cec2163dde3..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ca.openbb +++ /dev/null @@ -1,45 +0,0 @@ -stocks -load ${ticker=aapl} -ca -tsne -historical -historical -t o -n -hcorr -hcorr --raw -hcorr -t h --display-full-matrix -hcorr --start 2023-01-01 --end 2023-05-01 --export -hcorr --help -volume --export -ticker ${ticker=msft} -income --export -income --help -income -t 31-Mar-2022 -q -balance --export -balance --help -balance -t 31-Mar-2022 -q -cashflow --export -cashflow --help -cashflow -t 31-Mar-2022 -q -sentiment --help -sentiment -r -scorr -scorr --raw -scorr -h -overview -valuation -financial -ownership -performance -technical -rmv -set -s MSFT,GOOG -ticker -t ${ticker=tsla} -get --source Polygon -get --source Finnhub -get -add -s 2 -help -.. -reset -quit -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_disc.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_disc.openbb deleted file mode 100644 index aa6afb51294..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_disc.openbb +++ /dev/null @@ -1,37 +0,0 @@ -stocks -disc -q -disc -.. -disc -quit -disc -help -h -? -filings -pipo -fipo -gainers -losers -ugs -l 3 -gtech -active -ulc -asc -arkord -arkord --fund ARKX -s Total -upcoming -trending -lowfloat -hotpenny -rtat -divcal -s dividend -a -divcal -d 2023-01-03 -s record_date -r -divcal --help - -heatmap -reset -ns --ticker AAPL -s 2023-01-01 -r -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_dps.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_dps.openbb deleted file mode 100644 index 59eeff2111f..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_dps.openbb +++ /dev/null @@ -1,20 +0,0 @@ -stocks -dps -shorted -ctb -ctb -source stocksera -ctb --raw -hsi -pos -sidtc -load -t ${ticker=gme} -spos -psi -ftd -load ${ticker=aapl} -i 5 -ftd -n 5 --raw -dpotc -prom -t T1 -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_fa.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_fa.openbb deleted file mode 100644 index a4953bd2647..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_fa.openbb +++ /dev/null @@ -1,74 +0,0 @@ -stocks -load aapl -fa -income --source YahooFinance -income --source Polygon --quarter -income --source AlphaVantage --ratios -income --source FinancialModelingPrep -income --ticker AMZN --source EODHD -balance --source YahooFinance --ratios -balance --source Polygon --quarter -balance --ticker DDS --source AlphaVantage -balance --source FinancialModelingPrep -balance --source EODHD -cash --source YahooFinance -cash --source Polygon --quarter -cash --source AlphaVantage --ratios -cash --source FinancialModelingPrep -cash --ticker GOOG --source EODHD -income --source YahooFinance --plot gross_profit -income --source Polygon --plot gross_profit -income --source AlphaVantage --plot gross_profit -income --source FinancialModelingPrep --plot gross_profit -balance --source YahooFinance --plot total_assets -balance --source Polygon --plot total_assets -balance --source AlphaVantage --plot total_assets -balance --source FinancialModelingPrep --plot total_assets -cash --source YahooFinance --plot operating_cash_flow -cash --source Polygon --plot operating_cash_flow -cash --source AlphaVantage --plot operating_cash_flow -cash --source FinancialModelingPrep --plot operating_cash_flow -mgmt -analysis -score -mktcap --ticker MSFT -mktcap --method enterprise_value -mktcap --quarter -mktcap --source YahooFinance --start 1980-01-01 --end 2023-05-01 -metrics -ratios -growth -warnings -warnings -d -dcfc -shrs -shrs major -shrs mutualfund -divs -divs -p -splits -overview -earnings --ticker DDS --source YahooFinance -earnings --source AlphaVantage --quarter -fraud -dupont -epsfc -revfc -analysis -rating -rot -rot --raw -pt --ticker GOOG -pt --raw -est -est -e quarter_revenue -est -e annual_earnings -est -e quarter_earnings -sec -supplier -customer -dcf -t aapl -g -a --no-filter -help -quit -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_forecast.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_forecast.openbb deleted file mode 100644 index f6f9d9995c7..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_forecast.openbb +++ /dev/null @@ -1,3 +0,0 @@ -stocks -forecast -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_gov.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_gov.openbb deleted file mode 100644 index 0c2a4334956..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_gov.openbb +++ /dev/null @@ -1,33 +0,0 @@ -stocks -gov -lasttrades -lasttrades house -lasttrades senate -lasttrades congress -lasttrades -p Josh -topbuys house -topbuys senate -topbuys congress -topsells house -topsells senate -topsells congress -topsells -l 2 --raw -lastcontracts -lastcontracts -l 2 -s -qtrcontracts -qtrcontracts -l 2 --raw -qtrcontracts -l 2 -a downmom -toplobbying -toplobbying -l 4 --raw -load ${ticker=aapl} -gtrades -gtrades senate -gtrades house -gtrades congress -contracts -histcont -histcont --raw -lobbying -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_hedge.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_hedge.openbb deleted file mode 100644 index 0e69de382b8..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_hedge.openbb +++ /dev/null @@ -1,16 +0,0 @@ -stocks -options -load AAPL -exp --index 0 -hedge -pick --pick 50_long_call -list -add -i 0 --short -add -i 1 --short --put -rmv Option A -sop -plot - -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_insider.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_insider.openbb deleted file mode 100644 index c231231104d..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_insider.openbb +++ /dev/null @@ -1,37 +0,0 @@ -stocks -load AAPL -ins -view -view whales -set Electronics -filter -l 4 -filter -l 2 -u -stats -stats -u -act --raw -act -lins -lcb -lpsb -lit -lip -blip -blop -blcp -lis -blis -blos -blcs -topt -toppw -toppm -tipt -tippw -tippm -tist -tispw -tispm -help -reset -.. -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_options.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_options.openbb deleted file mode 100644 index be866ac4fa3..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_options.openbb +++ /dev/null @@ -1,37 +0,0 @@ -stocks -options -unu -l 2 -s Strike -r -c -unu -l 2 -s Vol -calc -load aapl -exp 0 -pcr -info -chains -oi -vol -voi -hist 100 -grhist 100 -greeks -plot -x ltd -y iv -parity -p -m 150 -M 175 -binom -s 150 -load spy -vsurf -load NDX --source Nasdaq -exp 0 -chains --source Nasdaq -chains -M -1 -m -1 -e 2025-12-19 -eodchain -d 2025-12-19 -oi -vol -voi - -hedge -.. - -load --help -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_qa.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_qa.openbb deleted file mode 100644 index 3f1c7d8c9b5..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_qa.openbb +++ /dev/null @@ -1,88 +0,0 @@ -stocks -load ${ticker=aapl} -qa -summary -normality -unitroot -unitroot -r ct -unitroot -r ctt -hist -hist -b 33 -cdf -pick volume -bw -bw -y -acf -pick adjclose -acf -l 3 -qqplot -rolling -w 10 -spread -quantile -skew -kurtosis -raw -raw --sortby adjclose -r -decompose -cusum -capm -beta -beta -r MSFT -var -es -sh -so -om -load ${ticker=aapl} -i 1 -pick returns -summary -normality -unitroot -hist -cdf -bw -qqplot -rolling -spread -quantile -skew -kurtosis -raw -decompose -cusum -capm -beta -var -es -sh -so -om -load ${ticker=aapl} --source Polygon -pick returns -summary -normality -unitroot -hist -cdf -bw -qqplot -rolling -spread -quantile -skew -kurtosis -raw -decompose -cusum -capm -beta -var -es -sh -so -om -line -line --log -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_res.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_res.openbb deleted file mode 100644 index 3e9ce7a9c44..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_res.openbb +++ /dev/null @@ -1,6 +0,0 @@ -stocks -load ${ticker=aapl} -res -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_screener.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_screener.openbb deleted file mode 100644 index c5eb6a44fd1..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_screener.openbb +++ /dev/null @@ -1,17 +0,0 @@ -stocks -scr -view -view rosenwald -overview -p golden_cross -valuation -financial -ownership -technical -set --preset cheap_dividend -performance -ca -quit -help -reset -.. -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_stocks.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_stocks.openbb deleted file mode 100644 index 2163b8867be..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_stocks.openbb +++ /dev/null @@ -1,61 +0,0 @@ -stocks -search -c united_states -s consumer_staples -search --industrygroup banks -e nms --limit 100 -search -q gold --all-exchanges --exchangecountry india --export csv -search --help -load --ticker AAPL -i 5 --prepost -load --ticker AAPL -s 2020-10-01 -e 2022-10-01 --performance -load --ticker AAPL -s 1980-01-01 --monthly -load --ticker AAPL -s 1980-01-01 --weekly --export csv -load --help -quote -quote --ticker AAPL,MSFT,GOOG,META,AMZN,NFLX -tob -tob -e EDGX -candle -candle -t --ma 5,10 -candle --raw -candle --sort AdjClose -r --raw -candle --ha -codes -news -l 3 -news -h -th -.. -options -.. -disc -.. -dps -.. -scr -.. -ins -.. -gov -.. -ba -.. -ca -.. -fa -.. -bt -.. -ta -.. -qa -.. -forecast -.. -res -.. - -# below two submenus are deprecated -dd -sia - -help -reset -quit -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ta.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ta.openbb deleted file mode 100644 index 9b982e0bad3..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_ta.openbb +++ /dev/null @@ -1,82 +0,0 @@ -stocks -load ${ticker=aapl} -ta -recom -summary -ema -l 20,30,50 -sma -wma -hma -zlma -vwap -cci -macd -rsi -stoch -fisher -cg -clenow -demark -adx -aroon -bbands -donchian -kc -ad -adosc -obv -fib -load ${ticker=ionq} -i 1 -ema -l 20,30,50 -sma -l 3 -wma -l 5 -hma -l 10 -zlma -l 20 -vwap -cci -l 12 -macd --fast 10 --slow 20 --signal 5 -rsi -l 10 -s 90 -d 2 -stoch -k 10 -d 2 --slowkperiod 2 -fisher -l 10 -cg -l 10 -clenow -demark -adx -l 10 -s 90 -d 2 -aroon -l 20 -s 120 -bbands -l 10 -s 1 -donchian -u 30 -l 3- -kc -l 10 -s 3 -o 1 -ad --open -adosc --open --fast 4 --slow 10 -obv -fib -load ${ticker=aapl} --source Polygon -ema -sma -wma -hma -zlma -vwap -cci -macd -rsi -stoch -fisher -cg -clenow -demark -adx -aroon -bbands -donchian -kc -ad -adosc -obv -fib -atr -cones -multi -i macd[12,26,9],rsi,sma[20] -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_tradinghours.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_tradinghours.openbb deleted file mode 100644 index bdf9d0fc659..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/stocks/test_tradinghours.openbb +++ /dev/null @@ -1,12 +0,0 @@ -stocks -load ${ticker=aapl} -th -open -closed -all -exchange -holidays -symbol ${ticker=MSFT} -help -reset -exit diff --git a/openbb_terminal/miscellaneous/integration_tests_scripts/terminal/test_news.openbb b/openbb_terminal/miscellaneous/integration_tests_scripts/terminal/test_news.openbb deleted file mode 100644 index 95728b8db6a..00000000000 --- a/openbb_terminal/miscellaneous/integration_tests_scripts/terminal/test_news.openbb +++ /dev/null @@ -1,4 +0,0 @@ -news -news Apple -news Apple Boeing -s cnn -exit diff --git a/openbb_terminal/miscellaneous/models/all_api_keys.json b/openbb_terminal/miscellaneous/models/all_api_keys.json deleted file mode 100644 index 1b9cf1fba77..00000000000 --- a/openbb_terminal/miscellaneous/models/all_api_keys.json +++ /dev/null @@ -1,254 +0,0 @@ -[ - { - "name": "API_KEY_ALPHAVANTAGE", - "source": "Alpha Vantage", - "link": "https://www.alphavantage.co/support/#api-key", - "markdown": "Go to: https://www.alphavantage.co/support/#api-key\n\n![AlphaVantage](https://user-images.githubusercontent.com/46355364/207820936-46c2ba00-81ff-4cd3-98a4-4fa44412996f.png)\n\nFill out the form, pass Captcha, and click on, \"GET FREE API KEY\"." - }, - { - "name": "API_MESSARI_KEY", - "source": "Messari", - "link": "https://messari.io/api", - "markdown": "Go to: https://messari.io\n\n![Messari](https://user-images.githubusercontent.com/46355364/207848122-ec6a41e4-76b7-4620-adc3-1f1c19f4bca6.png)\n\nClick on, \"Sign Up\", and create an account.\n\n![Messari](https://user-images.githubusercontent.com/46355364/207848160-6a962e3c-3007-40a3-9431-cd5ddfe5bb8e.png)\n\nAfter creating the account, navigate to the [account page](https://messari.io/account/api), and click on the tab for, API Access.\n\n![Messari](https://user-images.githubusercontent.com/46355364/207848324-ade5bede-8e6b-4b87-bdec-eade3217c0d8.png)" - }, - { - "name": "API_KEY_FINANCIALMODELINGPREP", - "source": "Financial Modeling Prep", - "link": "https://financialmodelingprep.com/developer", - "markdown": "Go to: https://site.financialmodelingprep.com/developer/docs\n\n![FinancialModelingPrep](https://user-images.githubusercontent.com/46355364/207821920-64553d05-d461-4984-b0fe-be0368c71186.png)\n\nClick on, \"Get my API KEY here\", and sign up for a free account.\n\n![FinancialModelingPrep](https://user-images.githubusercontent.com/46355364/207822184-a723092e-ef42-4f87-8c55-db150f09741b.png)\n\nWith an account created, sign in and navigate to the Dashboard, which shows the assigned token. by pressing the \"Dashboard\" button which will show the API key.\n\n![FinancialModelingPrep](https://user-images.githubusercontent.com/46355364/207823170-dd8191db-e125-44e5-b4f3-2df0e115c91d.png)" - }, - { - "name": "API_KEY_QUANDL", - "source": "Quandl", - "link": "https://www.quandl.com/tools/api", - "markdown": "Go to: https://www.quandl.com\n\n![Quandl](https://user-images.githubusercontent.com/46355364/207823899-208a3952-f557-4b73-aee6-64ac00faedb7.png)\n\nClick on, \"Sign Up\", and register a new account.\n\n![Quandl](https://user-images.githubusercontent.com/46355364/207824214-4b6b2b74-e709-4ed4-adf2-14803e6f3568.png)\n\nFollow the sign-up instructions, and upon completion the API key will be assigned.\n\n![Quandl](https://user-images.githubusercontent.com/46355364/207824664-3c82befb-9c69-42df-8a82-510d85c19a97.png)" - }, - { - "name": "API_REDDIT_CLIENT_ID", - "source": "Reddit client ID", - "link": "https://www.reddit.com/prefs/apps", - "markdown": "Sign in to Reddit, and then go to: https://old.reddit.com/prefs/apps/\n\n![Reddit](https://preview.redd.it/540vrn3k0cn91.png?width=986&format=png&auto=webp&v=enabled&s=88228cd0cf4415b3487b8d35e1097f0caa804e15)\n\nScroll down and click on \"create application\", selecting \"script\".\n\n![Reddit](https://preview.redd.it/7je4ehqa1cn91.png?width=916&format=png&auto=webp&v=enabled&s=dbdf65ccc0820cfe28eff8e81cba056f4fd8263e)\n\nOnce the application is created, you must register it [here](https://old.reddit.com/wiki/api)\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834105-665180be-c2b6-43c8-b1c9-477729905010.png)\n\nClick on, \"Read the full API terms and sign up for usage\", and fill out the form.\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834850-32a0d4c8-9990-4919-94e3-abad1487a3bd.png)\n\nAfter submitting the form, check for a confirmation email. The credentials will be displayed [here](https://old.reddit.com/prefs/apps/)." - }, - { - "name": "API_REDDIT_CLIENT_SECRET", - "source": "Reddit client secret", - "link": "https://www.reddit.com/prefs/apps", - "markdown": "Sign in to Reddit, and then go to: https://old.reddit.com/prefs/apps/\n\n![Reddit](https://preview.redd.it/540vrn3k0cn91.png?width=986&format=png&auto=webp&v=enabled&s=88228cd0cf4415b3487b8d35e1097f0caa804e15)\n\nScroll down and click on \"create application\", selecting \"script\".\n\n![Reddit](https://preview.redd.it/7je4ehqa1cn91.png?width=916&format=png&auto=webp&v=enabled&s=dbdf65ccc0820cfe28eff8e81cba056f4fd8263e)\n\nOnce the application is created, you must register it [here](https://old.reddit.com/wiki/api)\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834105-665180be-c2b6-43c8-b1c9-477729905010.png)\n\nClick on, \"Read the full API terms and sign up for usage\", and fill out the form.\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834850-32a0d4c8-9990-4919-94e3-abad1487a3bd.png)\n\nAfter submitting the form, check for a confirmation email. The credentials will be displayed [here](https://old.reddit.com/prefs/apps/)." - }, - { - "name": "API_REDDIT_USERNAME", - "source": "Reddit username", - "link": "https://www.reddit.com/prefs/apps", - "markdown": "Sign in to Reddit, and then go to: https://old.reddit.com/prefs/apps/\n\n![Reddit](https://preview.redd.it/540vrn3k0cn91.png?width=986&format=png&auto=webp&v=enabled&s=88228cd0cf4415b3487b8d35e1097f0caa804e15)\n\nScroll down and click on \"create application\", selecting \"script\".\n\n![Reddit](https://preview.redd.it/7je4ehqa1cn91.png?width=916&format=png&auto=webp&v=enabled&s=dbdf65ccc0820cfe28eff8e81cba056f4fd8263e)\n\nOnce the application is created, you must register it [here](https://old.reddit.com/wiki/api)\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834105-665180be-c2b6-43c8-b1c9-477729905010.png)\n\nClick on, \"Read the full API terms and sign up for usage\", and fill out the form.\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834850-32a0d4c8-9990-4919-94e3-abad1487a3bd.png)\n\nAfter submitting the form, check for a confirmation email. The credentials will be displayed [here](https://old.reddit.com/prefs/apps/)." - }, - { - "name": "API_REDDIT_USER_AGENT", - "source": "Reddit user agent", - "link": "https://www.reddit.com/prefs/apps", - "markdown": "Sign in to Reddit, and then go to: https://old.reddit.com/prefs/apps/\n\n![Reddit](https://preview.redd.it/540vrn3k0cn91.png?width=986&format=png&auto=webp&v=enabled&s=88228cd0cf4415b3487b8d35e1097f0caa804e15)\n\nScroll down and click on \"create application\", selecting \"script\".\n\n![Reddit](https://preview.redd.it/7je4ehqa1cn91.png?width=916&format=png&auto=webp&v=enabled&s=dbdf65ccc0820cfe28eff8e81cba056f4fd8263e)\n\nOnce the application is created, you must register it [here](https://old.reddit.com/wiki/api)\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834105-665180be-c2b6-43c8-b1c9-477729905010.png)\n\nClick on, \"Read the full API terms and sign up for usage\", and fill out the form.\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834850-32a0d4c8-9990-4919-94e3-abad1487a3bd.png)\n\nAfter submitting the form, check for a confirmation email. The credentials will be displayed [here](https://old.reddit.com/prefs/apps/)." - }, - { - "name": "API_REDDIT_PASSWORD", - "source": "Reddit password", - "link": "https://www.reddit.com/prefs/apps", - "markdown": "Sign in to Reddit, and then go to: https://old.reddit.com/prefs/apps/\n\n![Reddit](https://preview.redd.it/540vrn3k0cn91.png?width=986&format=png&auto=webp&v=enabled&s=88228cd0cf4415b3487b8d35e1097f0caa804e15)\n\nScroll down and click on \"create application\", selecting \"script\".\n\n![Reddit](https://preview.redd.it/7je4ehqa1cn91.png?width=916&format=png&auto=webp&v=enabled&s=dbdf65ccc0820cfe28eff8e81cba056f4fd8263e)\n\nOnce the application is created, you must register it [here](https://old.reddit.com/wiki/api)\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834105-665180be-c2b6-43c8-b1c9-477729905010.png)\n\nClick on, \"Read the full API terms and sign up for usage\", and fill out the form.\n\n![Reddit](https://user-images.githubusercontent.com/46355364/207834850-32a0d4c8-9990-4919-94e3-abad1487a3bd.png)\n\nAfter submitting the form, check for a confirmation email. The credentials will be displayed [here](https://old.reddit.com/prefs/apps/)." - }, - { - "name": "API_POLYGON_KEY", - "source": "Polygon", - "link": "https://polygon.io", - "markdown": "Go to: https://polygon.io\n\n![Polygon](https://user-images.githubusercontent.com/46355364/207825623-fcd7f0a3-131a-4294-808c-754c13e38e2a.png)\n\nClick on, \"Get your Free API Key\".\n\n![Polygon](https://user-images.githubusercontent.com/46355364/207825952-ca5540ec-6ed2-4cef-a0ed-bb50b813932c.png)\n\nAfter signing up, the API Key is found at the bottom of the account dashboard page.\n\n![Polygon](https://user-images.githubusercontent.com/46355364/207826258-b1f318fa-fd9c-41d9-bf5c-fe16722e6601.png)" - }, - { - "name": "API_FRED_KEY", - "source": "FRED", - "link": "https://fred.stlouisfed.org/docs/api/api_key.html", - "markdown": "Go to: https://fred.stlouisfed.org\n\n![FRED](https://user-images.githubusercontent.com/46355364/207827137-d143ba4c-72cb-467d-a7f4-5cc27c597aec.png)\n\nClick on, \"My Account\", create a new account or sign in with Google:\n\n![FRED](https://user-images.githubusercontent.com/46355364/207827011-65cdd501-27e3-436f-bd9d-b0d8381d46a7.png)\n\nAfter completing the sign-up, go to \"My Account\", and select \"API Keys\". Then, click on, \"Request API Key\".\n\n![FRED](https://user-images.githubusercontent.com/46355364/207827577-c869f989-4ef4-4949-ab57-6f3931f2ae9d.png)\n\nFill in the box for information about the use-case for FRED, and by clicking, \"Request API key\", at the bottom of the page, the API key will be issued.\n\n![FRED](https://user-images.githubusercontent.com/46355364/207828032-0a32d3b8-1378-4db2-9064-aa1eb2111632.png)" - }, - { - "name": "API_NEWS_TOKEN", - "source": "NewsAPI", - "link": "https://newsapi.org", - "markdown": "Go to: https://newsapi.org\n\n![News API](https://user-images.githubusercontent.com/46355364/207828250-0c5bc38c-90b4-427d-a611-b43c98c8e7ab.png)\n\nClick on, \"Get API Key\", and fill out the form.\n\n![News API](https://user-images.githubusercontent.com/46355364/207828421-76922bc2-cde0-493f-9eed-7f90eb831779.png)\n\nRegister for an account and the next screen will provide the API Key.\n\n![News API](https://user-images.githubusercontent.com/46355364/207828736-f0fce53b-f302-4456-adf9-8d50ac41fbe2.png)" - }, - { - "name": "OPENBB_RH_USERNAME", - "source": "Robinhood username", - "link": "https://robinhood.com/us/en/", - "markdown": "Go to: https://robinhood.com/us/en\n\n![Robinhood](https://user-images.githubusercontent.com/46355364/207838058-a2311632-6459-4cfd-bc0a-639ee3931574.png)\n\nThe first login will request 2FA authorization from the device connected to the account." - }, - { - "name": "OPENBB_RH_PASSWORD", - "source": "Robinhood password", - "link": "https://robinhood.com/us/en/", - "markdown": "Go to: https://robinhood.com/us/en\n\n![Robinhood](https://user-images.githubusercontent.com/46355364/207838058-a2311632-6459-4cfd-bc0a-639ee3931574.png)\n\nThe first login will request 2FA authorization from the device connected to the account." - }, - { - "name": "OPENBB_DG_USERNAME", - "source": "Degiro username", - "link": "https://www.degiro.com/", - "markdown": "Go to: https://www.degiro.com/\n\n![Degiro](https://user-images.githubusercontent.com/46355364/207838353-001d350c-872c-4770-a586-fb21318122eb.png)\n\nClick on, \"Open an account\", and then go through the registration process.\n\nInstructions for setting up 2FA authorization are [here](https://github.com/Chavithra/degiro-connector#35-how-to-use-2fa-)." - }, - { - "name": "OPENBB_DG_PASSWORD", - "source": "Degiro password", - "link": "https://www.degiro.com/", - "markdown": "Go to: https://www.degiro.com/\n\n![Degiro](https://user-images.githubusercontent.com/46355364/207838353-001d350c-872c-4770-a586-fb21318122eb.png)\n\nInstructions for setting up 2FA authorization are [here](https://github.com/Chavithra/degiro-connector#35-how-to-use-2fa-)." - }, - { - "name": "OPENBB_DG_TOTP_SECRET", - "source": "Degiro secret", - "link": "https://www.degiro.com/", - "markdown": "Go to: https://www.degiro.com/\n\n![Degiro](https://user-images.githubusercontent.com/46355364/207838353-001d350c-872c-4770-a586-fb21318122eb.png)\n\nClick on, \"Open an account\", and then go through the registration process.\n\nInstructions for setting up 2FA authorization are [here](https://github.com/Chavithra/degiro-connector#35-how-to-use-2fa-)." - }, - { - "name": "OPENBB_OANDA_ACCOUNT_TYPE", - "source": "Oanda account type", - "link": "https://developer.oanda.com", - "markdown": "Go to: https://developer.oanda.com\n\n![Oanda](https://user-images.githubusercontent.com/46355364/207839324-d30aa2b6-be83-41ff-9b1b-146cac566789.png)\n\nAfter creating an account, follow the steps below.\n\n![Oanda](https://user-images.githubusercontent.com/46355364/207839246-eb40f093-b583-4edd-b178-99fe399bfb66.png)" - }, - { - "name": "OPENBB_OANDA_ACCOUNT", - "source": "Oanda account", - "link": "https://developer.oanda.com", - "markdown": "Go to: https://developer.oanda.com\n\n![Oanda](https://user-images.githubusercontent.com/46355364/207839324-d30aa2b6-be83-41ff-9b1b-146cac566789.png)\n\nAfter creating an account, follow the steps below.\n\n![Oanda](https://user-images.githubusercontent.com/46355364/207839246-eb40f093-b583-4edd-b178-99fe399bfb66.png)" - }, - { - "name": "OPENBB_OANDA_TOKEN", - "source": "Oanda token", - "link": "https://developer.oanda.com", - "markdown": "Go to: https://developer.oanda.com\n\n![Oanda](https://user-images.githubusercontent.com/46355364/207839324-d30aa2b6-be83-41ff-9b1b-146cac566789.png)\n\nAfter creating an account, follow the steps below.\n\n![Oanda](https://user-images.githubusercontent.com/46355364/207839246-eb40f093-b583-4edd-b178-99fe399bfb66.png)" - }, - { - "name": "API_TRADIER_TOKEN", - "source": "Tradier token", - "link": "https://tradier.com/products/market-data-api", - "markdown": "Go to: https://documentation.tradier.com\n\n![Tradier](https://user-images.githubusercontent.com/46355364/207829178-a8bba770-f2ea-4480-b28e-efd81cf30980.png)\n\nClick on, \"Open Account\", to start the sign-up process. After the account has been setup, navigate to [Tradier Broker Dash](https://dash.tradier.com/login?redirect=settings.api) and create the application. Request a sandbox access token." - }, - { - "name": "API_CMC_KEY", - "source": "Coinmarketcap key", - "link": "https://coinmarketcap.com/api/", - "markdown": "Go to: https://coinmarketcap.com/api\n\n![CoinMarketCap](https://user-images.githubusercontent.com/46355364/207831111-3f09ed75-740e-4121-a67e-6e1f36e8ab9a.png)\n\nClick on, \"Get Your Free API Key Now\", which opens to the page:\n\n![CoinMarketCap](https://user-images.githubusercontent.com/46355364/207831345-06a48efe-63b2-4804-bcf9-52fa4a73f7db.png)\n\nOnce the account has been created, copy the API key displayed within the dashboard.\n\n![CoinMarketCap](https://user-images.githubusercontent.com/46355364/207831705-e9f95018-bba7-49a9-b057-3443bc839861.png)" - }, - { - "name": "API_BINANCE_KEY", - "source": "Binance key", - "link": "https://www.binance.com/en/", - "markdown": "Go to: https://www.binance.com/en/support/faq/how-to-create-api-360002502072\n\n![Binance](https://user-images.githubusercontent.com/46355364/207839805-f71cf12a-62d2-41cb-ba19-0c35917abc40.png)\n\nThese instructions should provide clear guidance for obtaining an API Key." - }, - { - "name": "API_BINANCE_SECRET", - "source": "Binance secret", - "link": "https://www.binance.com/en/", - "markdown": "Go to: https://www.binance.com/en/support/faq/how-to-create-api-360002502072\n\n![Binance](https://user-images.githubusercontent.com/46355364/207839805-f71cf12a-62d2-41cb-ba19-0c35917abc40.png)\n\nThese instructions should provide clear guidance for obtaining an API Key." - }, - { - "name": "API_FINNHUB_KEY", - "source": "Finnhub key", - "link": "https://finnhub.io", - "markdown": "Go to: https://finnhub.io/\n\n![Finnhub](https://user-images.githubusercontent.com/46355364/207832028-283c3321-8c05-4ee8-b4d2-41cdc940f408.png)\n\nClick on, \"Get free api key\", to open the page:\n\n![Finnhub](https://user-images.githubusercontent.com/46355364/207832185-f4c8406a-3b75-4acc-b3e8-3c4b3272d4da.png)\n\nOnce the account has been created, find the API key in the account dashboard.\n\n![Finnhub](https://user-images.githubusercontent.com/46355364/207832601-62007d95-410c-4d03-a5a3-b177d1894a4c.png)" - }, - { - "name": "API_BITQUERY_KEY", - "source": "Bitquery Key", - "link": "https://graphql.bitquery.io", - "markdown": "Go to: https://bitquery.io/\n\n![Bitquery](https://user-images.githubusercontent.com/46355364/207840322-5532a3f9-739f-4e28-9839-a58db932882e.png)\n\nClick \"Try GraphQL API\", which opens the following screen:\n\n![Try GraphQL API](https://user-images.githubusercontent.com/46355364/207840576-2c51a538-dd9b-484d-b11d-40e3e424df62.png)\n\nAfter creating an account and verifying the email address, get the value for the key by clicking on the \"API Key\" tab.\n\n![Get Bitquery API Key](https://user-images.githubusercontent.com/46355364/207840833-35c1b12c-9b4b-43fe-a33e-f7b92c43a011.png)" - }, - { - "name": "API_COINBASE_KEY", - "source": "Coinbase key", - "link": "https://www.coinbase.com/", - "markdown": "Go to: https://help.coinbase.com/en/exchange/managing-my-account/how-to-create-an-api-key\n\n![Coinbase](https://user-images.githubusercontent.com/46355364/207841901-647f0aef-0c74-454d-b99e-367d784259f0.png)\n\nFollow the instructions to obtain the credentials for the specific account." - }, - { - "name": "API_COINBASE_SECRET", - "source": "Coinbase secret", - "link": "https://www.coinbase.com/", - "markdown": "Go to: https://help.coinbase.com/en/exchange/managing-my-account/how-to-create-an-api-key\n\n![Coinbase](https://user-images.githubusercontent.com/46355364/207841901-647f0aef-0c74-454d-b99e-367d784259f0.png)\n\nFollow the instructions to obtain the credentials for the specific account." - }, - { - "name": "API_COINBASE_PASS_PHRASE", - "source": "Coinbase passphrase", - "link": "https://www.coinbase.com/", - "markdown": "Go to: https://help.coinbase.com/en/exchange/managing-my-account/how-to-create-an-api-key\n\n![Coinbase](https://user-images.githubusercontent.com/46355364/207841901-647f0aef-0c74-454d-b99e-367d784259f0.png)\n\nFollow the instructions to obtain the credentials for the specific account." - }, - { - "name": "API_COINGLASS_KEY", - "source": "Coinglass key", - "link": "https://www.coinglass.com/", - "markdown": "Go to: https://www.coinglass.com/\n\n![Coinglass](https://user-images.githubusercontent.com/46355364/207844601-8510687a-e54f-49b9-961f-5ef6718f58ab.png)\n\nClick, \"Log in\", and then sign up for an account. This opens the page:\n\n![Coinglass](https://user-images.githubusercontent.com/46355364/207844637-a9321889-c4d8-4d44-95fe-a6288a17ad19.png)" - }, - { - "name": "API_CRYPTO_PANIC_KEY", - "source": "Crypto Panic", - "link": "https://cryptopanic.com/developers/", - "markdown": "Go to: https://cryptopanic.com/developers/api/\n\n![Crypto Panic](https://user-images.githubusercontent.com/46355364/207848733-27e5a804-7ae7-4ca2-88b2-848b32929b6f.png)\n\nClick on, [Sign up](https://cryptopanic.com/accounts/signup/?next=/developers/api/), and after creating, the API key will be displayed on the documentation page, \"Your free API auth token\".\n\n![Crypto Panic](https://user-images.githubusercontent.com/46355364/207848971-3e4771b7-1faa-45fe-955f-81bd736b16b7.png)" - }, - { - "name": "API_EODHD_KEY", - "source": "EODHD", - "link": "https://eodhistoricaldata.com/", - "markdown": "Go to: https://eodhistoricaldata.com/r/?ref=869U7F4J\n\n![EODHD](https://user-images.githubusercontent.com/46355364/207849214-23763c95-7314-42ae-b97d-cb5810686498.png)\n\nClicking on, \"Registration\", opens the page:\n\n![EODHD](https://user-images.githubusercontent.com/46355364/207849324-00d4a916-8260-45c0-9714-289e0a0574c0.png)\n\nOnce registered, the API Key will be next to \"API TOKEN\".\n\n![EODHD](https://user-images.githubusercontent.com/46355364/207849462-37471270-929a-45c5-a164-a84249b19231.png)" - }, - { - "name": "API_GITHUB_KEY", - "source": "GitHub", - "link": "https://docs.github.com/en/rest/guides/getting-started-with-the-rest-api", - "markdown": "Go to: https://github.com\n\n![GitHub](https://user-images.githubusercontent.com/46355364/207846953-7feae777-3c3b-4f21-9dcf-84817c732618.png)\n\nSign up for, or sign in to, GitHub. Once logged in, navigate to the [apps](https://github.com/settings/apps) page, under account settings.\n\n![GitHub](https://user-images.githubusercontent.com/46355364/207847215-3c04003f-26ea-4e62-9c13-ea35176bb5e3.png)\n\nSelect, \"New GitHub App\":\n\n![GitHub](https://user-images.githubusercontent.com/46355364/207847383-d24416c6-18be-43f2-ae7c-455e8372a6ed.png)\n\nAfter creating the app, the key will be issued." - }, - { - "name": "API_GLASSNODE_KEY", - "source": "Glassnode key", - "link": "https://docs.glassnode.com/basic-api/api-key#how-to-get-an-api-key/", - "markdown": "Go to: https://studio.glassnode.com\n\n![Glassnode](https://user-images.githubusercontent.com/46355364/207843761-799078ff-fa64-4d39-a6eb-ba01d250be69.png)\n\nClick on, \"Sign Up\", and create an account:\n\n![Glassnode](https://user-images.githubusercontent.com/46355364/207843795-dd2cdbdb-45eb-4c7d-b967-ae9857d4ea5d.png)\n\nAfter creating an account, navigate to the [account settings](https://studio.glassnode.com/settings/api) and generate an API Key.\n\n![Glassnode](https://user-images.githubusercontent.com/46355364/207843950-5f33f37d-0203-4302-a67f-198808f18e06.png)" - }, - { - "name": "API_SANTIMENT_KEY", - "source": "Santiment", - "link": "https://app.santiment.net/", - "markdown": "Go to: https://app.santiment.net\n\nClick on, \"Sign Up\", and register for an account.\n\n![Santiment](https://user-images.githubusercontent.com/46355364/207849732-4bae61de-2f62-4919-b85d-f418f1bbd0c4.png)\n\nNavigate to the [account dashboard](https://app.santiment.net/account#api-keys) and generate a key.\n\n![Santiment](https://user-images.githubusercontent.com/46355364/207849839-31d1d0a7-6936-4ebd-a7f8-1292f6317b07.png)" - }, - { - "name": "API_SHROOM_KEY", - "source": "ShroomDK", - "link": "https://sdk.flipsidecrypto.xyz/shroomdk", - "markdown": "Go to: https://sdk.flipsidecrypto.xyz/shroomdk\n\n![ShroomDK](https://user-images.githubusercontent.com/46355364/207850122-b8cd225e-0a65-4ea8-8069-0b40fff1600e.png)\n\nClick \"Mint Your ShroomDK API Key\", and sign up for an account.\n\n![ShroomDK](https://user-images.githubusercontent.com/46355364/207850176-f29cc73b-2b55-46e8-bce3-62c9342b6599.png)\n\nOnce created, connect a wallet to complete minting the NFT license. The API key will be displayed under the account.\n\n![ShroomDK](https://user-images.githubusercontent.com/46355364/207850380-b59554af-1e65-4616-921d-e02c9ecf1aad.png)" - }, - { - "name": "API_TOKEN_TERMINAL_KEY", - "source": "Token Terminal", - "link": "https://tokenterminal.com/", - "markdown": "Go to: https://tokenterminal.com\n\n![Token Terminal](https://user-images.githubusercontent.com/46355364/207850735-69368b4f-6a3e-46b8-ba69-3b79d9231f15.png)\n\nClick on, \"Log in\" and sign up for an account.\n\n![Token Terminal](https://user-images.githubusercontent.com/46355364/207850774-2071df78-3289-4c8e-9d64-156b9ec8ad81.png)\n\nVerify the email address, and then navigate go to the \"API\" tab and copy the API key to the clipboard.\n\n![Token Terminal](https://user-images.githubusercontent.com/46355364/207851035-71ea3eff-a11f-4835-8592-c07b3aa3f800.png)" - }, - { - "name": "API_WHALE_ALERT_KEY", - "source": "Whale Alert", - "link": "https://docs.whale-alert.io/", - "markdown": "Go to: https://docs.whale-alert.io\n\n![Whale Alert](https://user-images.githubusercontent.com/46355364/207842892-3f71ee7a-6cd3-48a2-82e4-fa5ec5b13807.png)\n\nClick on, \"sign up here\".\n\n![Whale Alert](https://user-images.githubusercontent.com/46355364/207842992-427f1d2c-b34e-41c9-85fd-18511805fd16.png)\n\nAfter creating the account, click on, \"Create\", to issue the API Key.\n\n![Whale Alert](https://user-images.githubusercontent.com/46355364/207843214-20232465-9a52-4b66-b01a-0b8cecbdd612.png)" - }, - { - "name": "API_DATABENTO_KEY", - "source": "Databento", - "link": "https://databento.com/", - "markdown": "Go to: https://docs.databento.com/getting-started\n\n![Databento](https://user-images.githubusercontent.com/85772166/221943074-7a5e55c1-6193-42d8-a04c-431603a69a05.png)\n\nClick on, [Sign up](https://databento.com/signup), and after creating an account, the API key is found in the [account portal](https://databento.com/portal/keys).\n\n![Databento](https://user-images.githubusercontent.com/85772166/221943270-e0ada7bf-c7fb-46f2-bd7a-49afb3bc7bd8.png)" - }, - { - "name": "API_INTRINIO_KEY", - "source": "Intrinio", - "link": "https://intrinio.com/", - "markdown": "Go to: https://intrinio.com/starter-plan\n\n![Intrinio](https://user-images.githubusercontent.com/85772166/219207556-fcfee614-59f1-46ae-bff4-c63dd2f6991d.png)\n\nAn API key will be issued with a subscription. Find the token value within the account dashboard." - }, - { - "name": "API_BIZTOC_TOKEN", - "source": "BizToc", - "link": "https://biztoc.com", - "markdown": "The BizToc API is hosted on RapidAPI. To set up, go to: https://rapidapi.com/thma/api/biztoc.\n\n![biztoc0](https://github.com/marban/OpenBBTerminal/assets/18151143/04cdd423-f65e-4ad8-ad5a-4a59b0f5ddda)\n\nIn the top right, select 'Sign Up'. After answering some questions, you will be prompted to select one of their plans.\n\n![biztoc1](https://github.com/marban/OpenBBTerminal/assets/18151143/9f3b72ea-ded7-48c5-aa33-bec5c0de8422)\n\nAfter signing up, navigate back to https://rapidapi.com/thma/api/biztoc. If you are logged in, you will see a header called X-RapidAPI-Key.\n\n![biztoc2](https://github.com/marban/OpenBBTerminal/assets/18151143/0f3b6c91-07e0-447a-90cd-a9e23522929f)" - }, - { - "name": "API_KEY_NIXTLA", - "source": "Nixtla", - "link": "https://docs.nixtla.io/docs/getting-started", - "markdown": "Follow instructions in https://docs.nixtla.io/docs/getting-started" - } -] diff --git a/openbb_terminal/miscellaneous/models/hub_credentials.json b/openbb_terminal/miscellaneous/models/hub_credentials.json deleted file mode 100644 index 8edb791283a..00000000000 --- a/openbb_terminal/miscellaneous/models/hub_credentials.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "API_DATABENTO_KEY": "", - "API_KEY_ALPHAVANTAGE": "", - "API_KEY_FINANCIALMODELINGPREP": "", - "API_KEY_QUANDL": "", - "API_POLYGON_KEY": "", - "API_FRED_KEY": "", - "API_NEWS_TOKEN": "", - "API_BIZTOC_TOKEN": "", - "API_CMC_KEY": "", - "API_FINNHUB_KEY": "", - "API_WHALE_ALERT_KEY": "", - "API_GLASSNODE_KEY": "", - "API_COINGLASS_KEY": "", - "API_ETHPLORER_KEY": "", - "API_CRYPTO_PANIC_KEY": "", - "API_BITQUERY_KEY": "", - "API_SMARTSTAKE_KEY": "", - "API_SMARTSTAKE_TOKEN": "", - "API_MESSARI_KEY": "", - "API_SHROOM_KEY": "", - "API_SANTIMENT_KEY": "", - "API_EODHD_KEY": "", - "API_TOKEN_TERMINAL_KEY": "", - "API_INTRINIO_KEY": "", - "API_GITHUB_KEY": "", - "API_REDDIT_CLIENT_ID": "", - "API_REDDIT_CLIENT_SECRET": "", - "API_REDDIT_USERNAME": "", - "API_REDDIT_USER_AGENT": "", - "API_REDDIT_PASSWORD": "", - "API_COMPANIESHOUSE_KEY": "", - "API_DAPPRADAR_KEY": "", - "API_KEY_NIXTLA": "" -} diff --git a/openbb_terminal/miscellaneous/models/local_credentials.json b/openbb_terminal/miscellaneous/models/local_credentials.json deleted file mode 100644 index 9401b5c848d..00000000000 --- a/openbb_terminal/miscellaneous/models/local_credentials.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "RH_USERNAME": "", - "RH_PASSWORD": "", - "DG_USERNAME": "", - "DG_PASSWORD": "", - "DG_TOTP_SECRET": "", - "OANDA_ACCOUNT_TYPE": "", - "OANDA_ACCOUNT": "", - "OANDA_TOKEN": "", - "API_BINANCE_KEY": "", - "API_BINANCE_SECRET": "", - "API_COINBASE_KEY": "", - "API_COINBASE_SECRET": "", - "API_COINBASE_PASS_PHRASE": "", - "API_TRADIER_TOKEN": "" -} diff --git a/openbb_terminal/miscellaneous/portfolio/allocation_example.xlsx b/openbb_terminal/miscellaneous/portfolio/allocation_example.xlsx deleted file mode 100644 index cd39d7299f00b59361d7e5e90aaea4cc52713fe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17970 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 diff --git a/openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx b/openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx deleted file mode 100644 index e25dee919da78add546e96e5c71aced7628a4b3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29757 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 a!Sd3GkXd|pUH}`$1hPfjd>3i~=6?Xugx6;P diff --git a/openbb_terminal/miscellaneous/portfolio/parameters_template.xlsx b/openbb_terminal/miscellaneous/portfolio/parameters_template.xlsx deleted file mode 100644 index 56ffb4d5e05832a9a1c53688891a779eefe2a5e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33733 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
qB;jcJ6 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 diff --git a/openbb_terminal/miscellaneous/routines/routine_example.openbb b/openbb_terminal/miscellaneous/routines/routine_example.openbb deleted file mode 100644 index 5eaa2b0b874..00000000000 --- a/openbb_terminal/miscellaneous/routines/routine_example.openbb +++ /dev/null @@ -1,23 +0,0 @@ -# Go into the stocks context -stocks - -# Load a company ticker, e.g. Apple -load AAPL - -# Show a candle chart with a 20 day Moving Average -candle --ma 20 - -# Switch over to the Fundamental Analysis menu -fa - -# Show Management -mgmt - -# Show price targets charts -pt - -# Show future estimations -est - -# Return to home -home \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/sources/openbb_default.json b/openbb_terminal/miscellaneous/sources/openbb_default.json deleted file mode 100644 index 3b25d68b221..00000000000 --- a/openbb_terminal/miscellaneous/sources/openbb_default.json +++ /dev/null @@ -1,609 +0,0 @@ -{ - "stocks": { - "search": ["FinanceDatabase"], - "quote": ["FinancialModelingPrep"], - "tob": ["CBOE"], - "candle": [], - "codes": ["Polygon"], - "news": ["Feedparser", "NewsApi"], - "load": [ - "YahooFinance", - "AlphaVantage", - "Polygon", - "EODHD", - "Intrinio", - "DataBento" - ], - "th": { - "open": ["Bursa"], - "closed": ["Bursa"], - "all": ["Bursa"], - "exchange": ["Bursa"], - "holidays": ["PandasMarketCalendars"], - "symbol": ["FinanceDatabase"] - }, - "options": { - "unu": ["FDScanner"], - "calc": [], - "load": [ - "YahooFinance", - "Tradier", - "Nasdaq", - "Intrinio" - ], - "pcr": [ - "AlphaQuery" - ], - "info": [ - "Barchart" - ], - "hist": [ - "ChartExchange", - "Tradier", - "Intrinio" - ], - "vsurf": [ - "YahooFinance" - ], - "grhist": [ - "Intrinio" - ], - "plot": [ - "YahooFinance" - ], - "parity": [ - "YahooFinance" - ], - "binom": [ - "YahooFinance" - ], - "greeks": [ - "YahooFinance" - ], - "eodchain": [ - "Intrinio" - ], - "pricing": { - "add": [], - "rmv": [], - "show": [], - "rnval": ["YahooFinance"] - }, - "hedge": { - "list": [], - "pick": [], - "add": [], - "rmv": [], - "sop": [], - "plot": ["YahooFinance"] - } - }, - "disc": { - "filings": ["FinancialModelingPrep"], - "pipo": ["Finnhub"], - "fipo": ["Finnhub"], - "gainers": ["YahooFinance"], - "losers": ["YahooFinance"], - "ugs": ["YahooFinance"], - "gtech": ["YahooFinance"], - "active": ["YahooFinance"], - "ulc": ["YahooFinance"], - "asc": ["YahooFinance"], - "arkord": ["CathiesArk"], - "upcoming": ["SeekingAlpha"], - "trending": ["SeekingAlpha"], - "lowfloat": ["Fidelity"], - "hotpenny": ["YahooFinance", "Shortinterest"], - "rtat": ["Nasdaq"], - "divcal": ["Nasdaq"] - }, - "sia": { - "select": ["YahooFinance"], - "clear": [], - "industry": ["FinanceDatabase"], - "sector": ["FinanceDatabase"], - "country": ["FinanceDatabase"], - "mktcap": ["FinanceDatabase"], - "exchange": ["FinanceDatabase"], - "period": ["StockAnalysis"], - "cps": ["FinanceDatabase"], - "cpic": ["FinanceDatabase"], - "cpis": ["FinanceDatabase"], - "cpcs": ["FinanceDatabase"], - "cpci": ["FinanceDatabase"], - "metric": ["YahooFinance"], - "vis": ["StockAnalysis"] - }, - "dps": { - "load": ["YahooFinance", "AlphaVantage", "Polygon", "EODHD"], - "shorted": ["YahooFinance"], - "hsi": ["Shortinterest"], - "prom": ["FINRA"], - "pos": ["Stockgrid"], - "sidtc": ["Stockgrid"], - "dpotc": ["FINRA"], - "ftd": ["SEC"], - "spos": ["Stockgrid"], - "psi": ["Stockgrid", "Quandl"] - }, - "scr": { - "view": [], - "set": [], - "historical": ["YahooFinance"], - "overview": ["Finviz"], - "valuation": ["Finviz"], - "financial": ["Finviz"], - "ownership": ["Finviz"], - "performance": ["Finviz"], - "technical": ["Finviz"], - "arktrades": ["Cathiesark"] - }, - "ins": { - "load": ["YahooFinance", "AlphaVantage", "Polygon", "EODHD"], - "view": [], - "set": [], - "filter": ["OpenInsider"], - "lcb": ["OpenInsider"], - "lpsb": ["OpenInsider"], - "lit": ["OpenInsider"], - "lip": ["OpenInsider"], - "blip": ["OpenInsider"], - "blop": ["OpenInsider"], - "blcp": ["OpenInsider"], - "lis": ["OpenInsider"], - "blis": ["OpenInsider"], - "blos": ["OpenInsider"], - "blcs": ["OpenInsider"], - "topt": ["OpenInsider"], - "toppw": ["OpenInsider"], - "toppm": ["OpenInsider"], - "tipt": ["OpenInsider"], - "tippw": ["OpenInsider"], - "tippm": ["OpenInsider"], - "tist": ["OpenInsider"], - "tispw": ["OpenInsider"], - "tispm": ["OpenInsider"], - "stats": ["OpenInsider"], - "act": ["BusinessInsider"], - "lins": ["Finviz"] - }, - "gov": { - "load": ["YahooFinance", "AlphaVantage", "Polygon", "EODHD"], - "lasttrades": ["QuiverQuant"], - "topbuys": ["QuiverQuant"], - "topsells": ["QuiverQuant"], - "lastcontracts": ["QuiverQuant"], - "qtrcontracts": ["QuiverQuant"], - "toplobbying": ["QuiverQuant"], - "gtrades": ["QuiverQuant"], - "contracts": ["QuiverQuant"], - "histcont": ["QuiverQuant"], - "lobbying": ["QuiverQuant"] - }, - "ba": { - "load": [ - "YahooFinance", - "AlphaVantage", - "Polygon", - "EODHD" - ], - "headlines": [ - "FinBrain" - ], - "snews": [ - "Finnhub" - ], - "wsb": [ - "Reddit" - ], - "spacc": [ - "Reddit" - ], - "redditsent": [ - "Reddit" - ], - "popular": [ - "Reddit" - ], - "getdd": [ - "Reddit" - ], - "trending": [ - "Stocktwits" - ], - "stalker": [ - "Stocktwits" - ], - "bullbear": [ - "Stocktwits" - ], - "messages": [ - "Stocktwits" - ], - "infer": [ - "Twitter" - ], - "sentiment": [ - "Twitter" - ], - "mentions": [ - "Google" - ], - "regions": [ - "Google" - ], - "interest": [ - "Google" - ], - "queries": [ - "Google" - ], - "rise": [ - "Google" - ], - "ns": [ - "Althub Platform" - ] - }, - "ca": { - "load": ["YahooFinance", "AlphaVantage", "Polygon", "EODHD"], - "tsne": ["YahooFinance"], - "get": ["Polygon", "Finnhub"], - "set": [], - "add": [], - "rmv": [], - "historical": ["YahooFinance"], - "hcorr": ["YahooFinance"], - "volume": ["YahooFinance"], - "income": ["MarketWatch"], - "balance": ["MarketWatch"], - "cashflow": ["MarketWatch"], - "sentiment": ["FinBrain"], - "scorr": ["FinBrain"], - "overview": ["Finviz"], - "valuation": ["Finviz"], - "financial": ["Finviz"], - "ownership": ["Finviz"], - "performance": ["Finviz"], - "technical": ["Finviz"] - }, - "fa": { - "load": ["YahooFinance", "AlphaVantage", "Polygon", "EODHD"], - "enterprise": ["FinancialModelingPrep", "YahooFinance"], - "divs": ["YahooFinance"], - "splits": ["YahooFinance"], - "rating": ["Finviz", "FinancialModelingPrep"], - "rot": ["Finnhub"], - "warnings": ["MarketWatch"], - "shrs": ["YahooFinance"], - "supplier": ["CSIMarket"], - "customer": ["CSIMarket"], - "income": [ - "FinancialModelingPrep", - "Polygon", - "AlphaVantage", - "EODHD", - "YahooFinance" - ], - "balance": [ - "FinancialModelingPrep", - "Polygon", - "AlphaVantage", - "EODHD", - "YahooFinance" - ], - "cash": [ - "FinancialModelingPrep", - "Polygon", - "AlphaVantage", - "EODHD", - "YahooFinance" - ], - "overview": [ - "Finviz", - "FinancialModelingPrep", - "AlphaVantage", - "YahooFinance" - ], - "mgmt": ["BusinessInsider"], - "analysis": ["Elect"], - "score": ["FinancialModelingPrep"], - "quote": ["FinancialModelingPrep"], - "mktcap": ["FinancialModelingPrep", "YahooFinance"], - "growth": ["FinancialModelingPrep"], - "metrics": ["FinancialModelingPrep", "AlphaVantage"], - "ratios": ["FinancialModelingPrep"], - "dupont": ["AlphaVantage"], - "fraud": ["AlphaVantage"], - "sec": ["Nasdaq"], - "earnings": ["AlphaVantage", "YahooFinance"], - "epsfc": ["SeekingAlpha"], - "revfc": ["SeekingAlpha"], - "est": ["BusinessInsider"], - "pt": ["BusinessInsider", "FinancialModelingPrep"], - "dcf": ["StockAnalysis"], - "dcfc": ["FinancialModelingPrep"] - }, - "bt": { - "load": ["YahooFinance", "AlphaVantage", "Polygon", "EODHD"], - "whatif": ["YahooFinance"] - }, - "ta": { - "load": ["YahooFinance", "AlphaVantage", "Polygon", "EODHD"], - "recom": ["TradingView"], - "summary": ["FinBrain"], - "tv": ["TradingView"] - }, - "qa": { - "load": ["YahooFinance", "AlphaVantage", "Polygon", "EODHD"] - } - }, - "crypto": { - "price": ["Pyth"], - "load": ["YahooFinance", "CoinGecko", "CCXT"], - "find": ["CoinGecko", "CoinPaprika", "Binance", "Coinbase", "YahooFinance"], - "headlines": ["FinBrain"], - "prt": ["CoinGecko"], - "disc": { - "top": ["CoinGecko", "CoinMarketCap"], - "trending": ["CoinGecko"], - "gainers": ["CoinGecko"], - "losers": ["CoinGecko"], - "search": ["CoinPaprika"], - "nft_mktp_chains": ["DappRadar"], - "nft_mktp": ["DappRadar"], - "dapps": ["DappRadar"], - "dapp_categories": ["DappRadar"], - "dapp_chains": ["DappRadar"], - "dapp_metrics": ["DappRadar"], - "defi_chains": ["DappRadar"], - "tokens": ["DappRadar"], - "fees": ["Cryptostats"] - }, - "ov": { - "global": ["CoinGecko", "CoinPaprika"], - "defi": ["CoinGecko"], - "stables": ["CoinGecko"], - "exchanges": ["CoinGecko", "CoinPaprika"], - "exrates": ["CoinGecko"], - "indexes": ["CoinGecko"], - "derivatives": ["CoinGecko"], - "categories": ["CoinGecko"], - "hold": ["CoinGecko"], - "hm": ["CoinGecko"], - "info": ["CoinPaprika"], - "markets": ["CoinPaprika"], - "exmarkets": ["CoinPaprika"], - "platforms": ["CoinPaprika"], - "contracts": ["CoinPaprika"], - "pairs": ["Coinbase"], - "news": ["CryptoPanic"], - "wf": ["WithdrawalFees"], - "ewf": ["WithdrawalFees"], - "wfpe": ["WithdrawalFees"], - "altindex": ["BlockchainCenter"], - "btcrb": ["BlockchainCenter"], - "ch": ["Rekt"], - "cr": ["LoanScan"], - "fun": ["TokenTerminal"] - }, - "onchain": { - "hr": ["Glassnode"], - "btccp": ["Blockchain"], - "btcct": ["Blockchain"], - "btcblockdata": ["Blockchain"], - "gwei": ["ETHGasStations"], - "whales": ["WhaleAlert"], - "lt": ["BitQuery"], - "dvcp": ["BitQuery"], - "tv": ["BitQuery"], - "ueat": ["BitQuery"], - "ttcp": ["BitQuery"], - "baas": ["BitQuery"], - "address": ["Ethplorer"], - "top": ["Ethplorer"], - "balance": ["Ethplorer"], - "hist": ["Ethplorer"], - "info": ["Ethplorer"], - "holders": ["Ethplorer"], - "th": ["Ethplorer"], - "prices": ["Ethplorer"], - "tx": ["Ethplorer"], - "topledger": ["Topledger"] - }, - "defi": { - "newsletter": ["Substack"], - "vaults": ["Coindix"], - "tokens": ["TheGraph"], - "stats": ["TheGraph"], - "pairs": ["TheGraph"], - "pools": ["TheGraph"], - "swaps": ["TheGraph"], - "ldapps": ["DeFiLlama"], - "gdapps": ["DeFiLlama"], - "stvl": ["DeFiLlama"], - "dtvl": ["DeFiLlama"], - "sinfo": ["TerraFCD"], - "validators": ["TerraFCD"], - "gacc": ["TerraFCD"], - "sreturn": ["TerraFCD"], - "lcsc": ["Smartstake"], - "anchor": ["CryptoSaurio"] - }, - "tools": { - "aprtoapy": [], - "il": [] - }, - "nft": { - "stats": ["OpenSea"], - "fp": ["NFTPriceFloor"], - "collections": ["NFTPriceFloor"] - }, - "dd": { - "load": ["YahooFinance", "CoinGecko", "CCXT"], - "info": ["CoinGecko"], - "ath": ["CoinGecko"], - "atl": ["CoinGecko"], - "web": ["CoinGecko"], - "bc": ["CoinGecko"], - "pi": ["Messari"], - "gov": ["Messari"], - "basic": ["CoinPaprika"], - "stats": ["Coinbase"], - "desc": ["TokenTerminal"], - "market": ["CoinGecko"], - "mkt": ["CoinPaprika"], - "ex": ["CoinPaprika"], - "balance": ["Binance"], - "oi": ["Coinglass"], - "fundrate": ["Coinglass"], - "liquidations": ["Coinglass"], - "eb": ["Coinglass"], - "trades": ["CCXT"], - "ob": ["CCXT"], - "active": ["Glassnode"], - "nonzero": ["Glassnode"], - "change": ["Glassnode"], - "ps": ["CoinPaprika"], - "mcapdom": ["Messari"], - "mt": ["Messari"], - "funot": ["TokenTerminal"], - "team": ["Messari"], - "inv": ["Messari"], - "tk": ["Messari"], - "fr": ["Messari"], - "rm": ["Messari"], - "events": ["CoinPaprika"], - "news": ["CryptoPanic"], - "links": ["Messari"], - "twitter": ["CoinPaprika"], - "social": ["CoinGecko"], - "score": ["CoinGecko"], - "dev": ["CoinGecko"], - "gh": ["Santiment"] - }, - "ta": { - "load": ["YahooFinance", "CoinGecko", "CCXT"], - "tv": ["TradingView"] - }, - "qa": { - "load": ["YahooFinance", "CoinGecko", "CCXT"] - } - }, - "etf": { - "search": ["FinanceDatabase", "StockAnalysis"], - "load": ["YahooFinance"], - "disc": { - "gainers": ["WallStreetJournal"], - "decliners": ["WallStreetJournal"], - "active": ["WallStreetJournal"] - }, - "overview": ["StockAnalysis"], - "holdings": ["StockAnalysis"], - "weights": ["FinancialModelingPrep"], - "news": ["NewsApi"], - "compare": ["StockAnalysis"], - "holding_perf": ["FinancialModelingPrep"] - }, - "economy": { - "overview": ["WallStreetJournal"], - "futures": ["WallStreetJournal", "Finviz"], - "map": ["Finviz"], - "bigmac": ["Nasdaq"], - "events": ["Nasdaq"], - "edebt": ["Wikipedia"], - "usdli": ["FRED"], - "valuation": ["Finviz"], - "performance": ["Finviz"], - "gdp": ["OECD"], - "rgdp": ["OECD"], - "fgdp": ["OECD"], - "debt": ["OECD"], - "cpi": ["FRED"], - "ccpi": ["OECD"], - "balance": ["OECD"], - "revenue": ["OECD"], - "spending": ["OECD"], - "trust": ["OECD"], - "macro": ["EconDB"], - "treasury": ["FED"], - "fred": ["FRED"], - "index": ["YahooFinance"] - }, - "forex": { - "load": ["YahooFinance", "AlphaVantage", "Polygon"], - "quote": ["YahooFinance", "AlphaVantage"], - "fwd": ["FXEmpire"] - }, - "funds": { - "country": ["MorningStar"], - "search": ["MorningStar"], - "load": ["MorningStar"], - "info": ["MorningStar"], - "plot": ["MorningStar"], - "sector": ["MorningStar"], - "carbon": ["MorningStar"], - "exclusion": ["MorningStar"], - "holdings": ["MorningStar"], - "alswe": ["Avanza"], - "infoswe": ["Avanza"] - }, - "futures": { - "search": ["YahooFinance"], - "historical": ["YahooFinance", "DataBento"], - "curve": ["YahooFinance"] - }, - "fixedincome": { - "estr": ["FRED", "ECB"], - "sofr": ["FRED"], - "sonia": ["FRED"], - "ameribor": ["FRED"], - "fed": ["FRED"], - "iorb": ["FRED"], - "projection": ["FRED"], - "dwpcr": ["FRED"], - "ecb": ["FRED"], - "treasury": ["OECD"], - "usrates": ["FRED"], - "ycrv": ["FRED"], - "ecbycrv": ["ECB"], - "icebofa": ["FRED"], - "moody": ["FRED"], - "spot": ["FRED"], - "cp": ["FRED"], - "hqm": ["FRED"], - "icespread": ["FRED"], - "tmc": ["FRED"], - "ffrmc": ["FRED"], - "tbffr": ["FRED"] - }, - "portfolio": { - "po": { - "maxsharpe": ["YahooFinance"], - "minrisk": ["YahooFinance"], - "maxutil": ["YahooFinance"], - "maxret": ["YahooFinance"], - "maxdiv": ["YahooFinance"], - "maxdecorr": ["YahooFinance"], - "blacklitterman": ["FinancialModelingPrep"], - "ef": ["YahooFinance"], - "riskparity": ["YahooFinance"], - "relriskparity": ["YahooFinance"], - "hrp": ["YahooFinance"], - "herc": ["YahooFinance"], - "nco": ["YahooFinance"], - "equal": ["YahooFinance"], - "mktcap": ["FinancialModelingPrep"] - } - }, - "alternative": { - "oss": { - "rossidx": ["RunaCapital"], - "rs": ["GitHub"], - "sh": ["GitHub"], - "tr": ["GitHub"] - }, - "hn": ["HackerNews"] - }, - "news" : ["Biztoc","Feedparser"] -} diff --git a/openbb_terminal/miscellaneous/stocks/insider/Agriculture.ini b/openbb_terminal/miscellaneous/stocks/insider/Agriculture.ini deleted file mode 100644 index 948471af385..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Agriculture.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Agriculture Services Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Agriculture, Forestry, Fish - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = diff --git a/openbb_terminal/miscellaneous/stocks/insider/Agro-Chemicals.ini b/openbb_terminal/miscellaneous/stocks/insider/Agro-Chemicals.ini deleted file mode 100644 index 49cd8057497..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Agro-Chemicals.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Agriculture Services Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Chemicals ->Agriculture Chemicals - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Air-Transportation.ini b/openbb_terminal/miscellaneous/stocks/insider/Air-Transportation.ini deleted file mode 100644 index c015743dddb..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Air-Transportation.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Air Transportation - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Transportation & Utilities -> Air Transportation - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Apparel.ini b/openbb_terminal/miscellaneous/stocks/insider/Apparel.ini deleted file mode 100644 index 3013519765c..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Apparel.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Apparel Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Apparel - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = diff --git a/openbb_terminal/miscellaneous/stocks/insider/Banks.ini b/openbb_terminal/miscellaneous/stocks/insider/Banks.ini deleted file mode 100644 index 287eb99d044..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Banks.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Banks - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Depository Institutions - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Brokers.ini b/openbb_terminal/miscellaneous/stocks/insider/Brokers.ini deleted file mode 100644 index 96ec553b8a0..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Brokers.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Security and Commodity Brokers - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Security & Commodity Brokers - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Building-Materials.ini b/openbb_terminal/miscellaneous/stocks/insider/Building-Materials.ini deleted file mode 100644 index 23280951185..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Building-Materials.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Building Materials Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Retail Trade -> Building Materials - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Business-Services.ini b/openbb_terminal/miscellaneous/stocks/insider/Business-Services.ini deleted file mode 100644 index fca04b7efbd..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Business-Services.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: B2B Services Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Services -> Business Services -> Business Services - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Closed-End-Funds.ini b/openbb_terminal/miscellaneous/stocks/insider/Closed-End-Funds.ini deleted file mode 100644 index ca3a54d49c8..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Closed-End-Funds.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Closed-End Funds - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Closed-End Funds - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Construction.ini b/openbb_terminal/miscellaneous/stocks/insider/Construction.ini deleted file mode 100644 index 2d339b6c4f3..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Construction.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Construction Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Construction - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = diff --git a/openbb_terminal/miscellaneous/stocks/insider/Credit-Industry.ini b/openbb_terminal/miscellaneous/stocks/insider/Credit-Industry.ini deleted file mode 100644 index a4a77aa138e..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Credit-Industry.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Credit Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Non-depository Credit Inst - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Durable-Goods.ini b/openbb_terminal/miscellaneous/stocks/insider/Durable-Goods.ini deleted file mode 100644 index 233a7b40666..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Durable-Goods.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Durable Goods Producers - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Wholesale Trade -> Durable Goods -> Durable Goods - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = diff --git a/openbb_terminal/miscellaneous/stocks/insider/Electronics.ini b/openbb_terminal/miscellaneous/stocks/insider/Electronics.ini deleted file mode 100644 index f6c4d2ac606..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Electronics.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Electronics Components Producers - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Electronics -> Electronic Components - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Furniture.ini b/openbb_terminal/miscellaneous/stocks/insider/Furniture.ini deleted file mode 100644 index bc10757370e..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Furniture.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Furniture Producers - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Furniture - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Gold-Silver.ini b/openbb_terminal/miscellaneous/stocks/insider/Gold-Silver.ini deleted file mode 100644 index 92b8e78310b..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Gold-Silver.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Gold and Silver Producers - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Mining -> Metal Mining -> Gold & Silver Ores - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Grocery.ini b/openbb_terminal/miscellaneous/stocks/insider/Grocery.ini deleted file mode 100644 index d547138674e..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Grocery.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Grocery Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Retail Trade -> Grocery - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = diff --git a/openbb_terminal/miscellaneous/stocks/insider/Ground-Freight.ini b/openbb_terminal/miscellaneous/stocks/insider/Ground-Freight.ini deleted file mode 100644 index b1cc2e4413f..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Ground-Freight.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Ground Freight - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Transportation & Utilities -> Road Freight Transportation - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Guided-Missiles.ini b/openbb_terminal/miscellaneous/stocks/insider/Guided-Missiles.ini deleted file mode 100644 index 06391ce2b28..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Guided-Missiles.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Guided Missile Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Transportation Equipment -> Guided Missiles & Space Vehicles - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Health.ini b/openbb_terminal/miscellaneous/stocks/insider/Health.ini deleted file mode 100644 index cd374d2fdae..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Health.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Health Services Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Services -> Health Services - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Hotels.ini b/openbb_terminal/miscellaneous/stocks/insider/Hotels.ini deleted file mode 100644 index 8d367f43027..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Hotels.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Hotel Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Services -> Hotels - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Insurance-Agents.ini b/openbb_terminal/miscellaneous/stocks/insider/Insurance-Agents.ini deleted file mode 100644 index 388e4279a56..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Insurance-Agents.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Insurance Agents - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Insurance Agents - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Insurance-Carriers.ini b/openbb_terminal/miscellaneous/stocks/insider/Insurance-Carriers.ini deleted file mode 100644 index b42fba3add0..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Insurance-Carriers.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Insurance Carriers - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Insurance Carriers - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Investment-Offices.ini b/openbb_terminal/miscellaneous/stocks/insider/Investment-Offices.ini deleted file mode 100644 index db238524ec6..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Investment-Offices.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Investment Offices - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Holding & Investment Offices - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Legal.ini b/openbb_terminal/miscellaneous/stocks/insider/Legal.ini deleted file mode 100644 index 7f36b9ffc4e..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Legal.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Legal Services Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Services -> Legal Services - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Lumber.ini b/openbb_terminal/miscellaneous/stocks/insider/Lumber.ini deleted file mode 100644 index 6136ecea6d4..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Lumber.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Lumber Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Lumber - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Manufacturing.ini b/openbb_terminal/miscellaneous/stocks/insider/Manufacturing.ini deleted file mode 100644 index d6e53ba18d6..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Manufacturing.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Manufacturing Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Miscellaneous Manufacturing - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Mining.ini b/openbb_terminal/miscellaneous/stocks/insider/Mining.ini deleted file mode 100644 index 112d4a6024e..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Mining.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Mining Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Mining - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Mortgages.ini b/openbb_terminal/miscellaneous/stocks/insider/Mortgages.ini deleted file mode 100644 index ca71126acd1..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Mortgages.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Mortgage Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Non-depository Credit Inst -> Mortgage Bankers & Loan Correspo - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Nondurable-Goods.ini b/openbb_terminal/miscellaneous/stocks/insider/Nondurable-Goods.ini deleted file mode 100644 index d0e23f26195..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Nondurable-Goods.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Nondurable Goods Producers - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Wholesale Trade -> Nondurable Goods - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = diff --git a/openbb_terminal/miscellaneous/stocks/insider/Personal-Credit.ini b/openbb_terminal/miscellaneous/stocks/insider/Personal-Credit.ini deleted file mode 100644 index a3682587191..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Personal-Credit.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Personal Credit - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Non-depository Credit Inst -> Personal Credit Institutions - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Pharmaceuticals.ini b/openbb_terminal/miscellaneous/stocks/insider/Pharmaceuticals.ini deleted file mode 100644 index cdce547de86..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Pharmaceuticals.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Pharmaceuticals - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Chemicals -> Medicinal Chemicals & Botanical - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Professional-Services.ini b/openbb_terminal/miscellaneous/stocks/insider/Professional-Services.ini deleted file mode 100644 index 2fb3760c1e3..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Professional-Services.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Professional Services - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Services -> Engr, Acct, Rsrch, Mgmt Svcs - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Railroads.ini b/openbb_terminal/miscellaneous/stocks/insider/Railroads.ini deleted file mode 100644 index 743a7dd685d..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Railroads.ini +++ /dev/null @@ -1,95 +0,0 @@ -# Author of preset: Danglewood -# Description: Railroads - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Transportation & Utilities -> Railroads -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Real-Estate.ini b/openbb_terminal/miscellaneous/stocks/insider/Real-Estate.ini deleted file mode 100644 index 27327a8aee1..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Real-Estate.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Real Estate - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Financial -> Real Estate - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Restaurants.ini b/openbb_terminal/miscellaneous/stocks/insider/Restaurants.ini deleted file mode 100644 index c201a8b9545..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Restaurants.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Restaurants - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Retail Trade -> Eating & Drinking Places - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Retail-Stores.ini b/openbb_terminal/miscellaneous/stocks/insider/Retail-Stores.ini deleted file mode 100644 index 5405345f66b..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Retail-Stores.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Retail Stores - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Retail Trade -> Miscellaneous Retail -> Retail Stores - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Retail-Trade.ini b/openbb_terminal/miscellaneous/stocks/insider/Retail-Trade.ini deleted file mode 100644 index 4e66ec685ef..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Retail-Trade.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Retail Trade - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Retail Trade - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Tobacco.ini b/openbb_terminal/miscellaneous/stocks/insider/Tobacco.ini deleted file mode 100644 index 27a243d3efb..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Tobacco.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Tobacco Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Manufacturing -> Tobacco - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = diff --git a/openbb_terminal/miscellaneous/stocks/insider/Utility-Communications.ini b/openbb_terminal/miscellaneous/stocks/insider/Utility-Communications.ini deleted file mode 100644 index 5c6c759f54a..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Utility-Communications.ini +++ /dev/null @@ -1,95 +0,0 @@ -# Author of preset: Danglewood -# Description: Communications Utilities - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Transportation & Utilities -> Communications -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Utility-Gas.ini b/openbb_terminal/miscellaneous/stocks/insider/Utility-Gas.ini deleted file mode 100644 index 7f1398011c3..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Utility-Gas.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Gas Utilities - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Gas & Other Services Combined - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/Wholesale-Trade.ini b/openbb_terminal/miscellaneous/stocks/insider/Wholesale-Trade.ini deleted file mode 100644 index a02e4bc6ba5..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/Wholesale-Trade.ini +++ /dev/null @@ -1,96 +0,0 @@ -# Author of preset: Danglewood -# Description: Whosesale Trade Industry - Last 3 Months - All Transactions - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 3 months -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = -S_Sale = -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = Wholesale Trade - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 1000 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/insider/template.ini b/openbb_terminal/miscellaneous/stocks/insider/template.ini deleted file mode 100644 index db8aef9c98e..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/template.ini +++ /dev/null @@ -1,617 +0,0 @@ -# Author of preset: Sexy Year -# Description: The user that adds the preset can add a description for what type of insiders this is meant. - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = All dates -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = All dates -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = true -S_Sale = true -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = All Sectors (except Funds) - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 100 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - - -# Available SectorSubsectorIndustry options below: -# All Sectors (except Funds) - -# Agriculture, Forestry, Fish -# Agriculture, Forestry, Fish -> Crops -# Agriculture, Forestry, Fish -> Livestock & Animal Specialties -# Agriculture, Forestry, Fish -> Agriculture Services -# Agriculture, Forestry, Fish -> Forestry -# Agriculture, Forestry, Fish -> Fishing, Hunting & Trapping - -# Mining -# Mining -> Metal Mining -# Mining -> Metal Mining -> Gold & Silver Ores -# Mining -> Metal Mining -> Miscellaneous Metal Ores -# Mining -> Coal Mining -# Mining -> Coal Mining -> Bituminous Coal & Lignite Mining -# Mining -> Coal Mining -> Bituminous Coal & Lignite Surfac -# Mining -> Oil & Gas Extraction -# Mining -> Oil & Gas Extraction -> Crude Petroleum & Natural Gas -# Mining -> Oil & Gas Extraction -> Drilling Oil & Gas Wells -# Mining -> Oil & Gas Extraction -> Oil & Gas Field Exploration Serv -# Mining -> Oil & Gas Extraction -> Oil & Gas Field Services -# Mining -> Mining Non-metallic Minerals - -# Construction -# Construction -> General Contractors & Builders -# Construction -> General Contractors & Builders -> Residential Bldg Contractors -# Construction -> General Contractors & Builders -> Operative Builders -# Construction -> General Contractors & Builders -> Nonresidential Bldg Contractors -# Construction -> Heavy Construction -# Construction -> Heavy Construction -> Heavy Construction Non-Bldg Cont -# Construction -> Heavy Construction -> Water, Sewer, Pipeline, Comm & P -# Construction -> Special Trade Contractors -# Construction -> Special Trade Contractors -> Special Trade Contractors -# Construction -> Special Trade Contractors -> Electrical Work - -# Manufacturing -# Manufacturing -> Food -# Manufacturing -> Food -> Food & Kindred Products -# Manufacturing -> Food -> Meat Packing Plants -# Manufacturing -> Food -> Sausage & Other Prepared Meat P -# Manufacturing -> Food -> Poultry Slaughtering & Processin -# Manufacturing -> Food -> Dairy Products -# Manufacturing -> Food -> Ice Cream & Froze Desserts -# Manufacturing -> Food -> Canned, Frozen & Preservd Fruit -# Manufacturing -> Food -> Canned, Fruits, Veg, Preserves -# Manufacturing -> Food -> Grain Mill Products -# Manufacturing -> Food -> Bakery Products -# Manufacturing -> Food -> Cookies & Crackers -# Manufacturing -> Food -> Sugar & Confectionery Products -# Manufacturing -> Food -> Fats & Oils -# Manufacturing -> Food -> Beverages -# Manufacturing -> Food -> Malt Beverages -# Manufacturing -> Food -> Bottled & Canned Soft Drinks & C -# Manufacturing -> Food -> Miscellaneous Food Preparations -# Manufacturing -> Food -> Prepared Fresh or Frozen Fish & -# Manufacturing -> Tobacco -# Manufacturing -> Tobacco -> Tobacco Products -# Manufacturing -> Tobacco -> Cigarettes -# Manufacturing -> Textile Mill Products -# Manufacturing -> Textile Mill Products -> Textile Mill Products -# Manufacturing -> Textile Mill Products -> Broadwoven Fabric Mills, Cotton -# Manufacturing -> Textile Mill Products -> Broadwoven Fabric Mills, Man Mad -# Manufacturing -> Textile Mill Products -> Knitting Mills -# Manufacturing -> Textile Mill Products -> Knit Outerwear Mills -# Manufacturing -> Textile Mill Products -> Carpets & Rugs -# Manufacturing -> Apparel -# Manufacturing -> Apparel -> Other Finishd Prods of -# Manufacturing -> Apparel -> Men’s & Boy’s Furnishgs, Work Cl -# Manufacturing -> Apparel -> Women’s, Misses’, & Juniors Oute -# Manufacturing -> Apparel -> Women’s, Misses’, Children’s & l -# Manufacturing -> Apparel -> Miscellaneous Fabric -# Manufacturing -> Lumber -# Manufacturing -> Lumber -> Lumber & Wood Products (No Furni) -# Manufacturing -> Lumber -> Sawmills & Planting Mills, Gener -# Manufacturing -> Lumber -> Millwood, Veneer, Plywood & Str -# Manufacturing -> Lumber -> Mobile Homes -# Manufacturing -> Lumber -> Prefabricated Wood Bldgs & Compo -# Manufacturing -> Furniture -# Manufacturing -> Furniture -> Household Furniture -# Manufacturing -> Furniture -> Wood Household Furniture, (no Up) -# Manufacturing -> Furniture -> Office Furniture -# Manufacturing -> Furniture -> Office Furniture (No Wood) -# Manufacturing -> Furniture -> Public Bldg & Related Furniture -# Manufacturing -> Furniture -> Partitions, Shelvg, Lockers & O -# Manufacturing -> Furniture -> Miscellaneous Furniture & Fixtur -# Manufacturing -> Paper -# Manufacturing -> Paper -> Papers & Allied Products -# Manufacturing -> Paper -> Pulp Mills -# Manufacturing -> Paper -> Paper Mills -# Manufacturing -> Paper -> Paperboard Mills -# Manufacturing -> Paper -> Paperboard Containers & Boxes -# Manufacturing -> Paper -> Converted Paper & Paperboard Pro -# Manufacturing -> Paper -> Plastics, Foils & Coated Paper Ba -# Manufacturing -> Printing & Publishing -# Manufacturing -> Printing & Publishing -> Newspapers: Publishing or Publis -# Manufacturing -> Printing & Publishing -> Periodicals: Publishing or Publi -# Manufacturing -> Printing & Publishing -> Books: Publishing or Publishing -# Manufacturing -> Printing & Publishing -> Book Printing -# Manufacturing -> Printing & Publishing -> Miscellaneous Publishing -# Manufacturing -> Printing & Publishing -> Commercial Printing -# Manufacturing -> Printing & Publishing -> Manifold Business Forms -# Manufacturing -> Printing & Publishing -> Greeting Cards -# Manufacturing -> Printing & Publishing -> Blankbooks, Looseleaf Binders & -# Manufacturing -> Printing & Publishing ->Service Industries For The Print -# Manufacturing -> Chemicals -# Manufacturing -> Chemicals -> Chemicals & Allied Products -# Manufacturing -> Chemicals -> Industrial Inorganic Chemicals -# Manufacturing -> Chemicals -> Plastic Material, Synth Resin/Ru -# Manufacturing -> Chemicals -> Plastic Material, Synth Resins -# Manufacturing -> Chemicals -> Medicinal Chemicals & Botanical -# Manufacturing -> Chemicals -> Pharmaceutical Preparations -# Manufacturing -> Chemicals -> In Vitro & In Vito Diagnostic Su -# Manufacturing -> Chemicals -> Biological Products ,(No Diagnos -# Manufacturing -> Chemicals -> Soap, Detergents, Cleaning, Perf -# Manufacturing -> Chemicals -> Specialty Cleaning, Polishing & -# Manufacturing -> Chemicals -> Perfumes, Cosmetics & Other Toil -# Manufacturing -> Chemicals -> Paints, Varnishes, Lacquers, Ena -# Manufacturing -> Chemicals -> Industrial Organic Chemicals -# Manufacturing -> Chemicals ->Agriculture Chemicals -# Manufacturing -> Chemicals -> Miscellaneous Chemical Products -# Manufacturing -> Chemicals -> Adhesives & Sealants -# Manufacturing -> Petroleum Refining -# Manufacturing -> Petroleum Refining -> Petroleum Refining -# Manufacturing -> Petroleum Refining -> Asphalt Paving & Roofing Materia -# Manufacturing -> Petroleum Refining -> Miscellaneous Products of Petrol -# Manufacturing -> Rubber & Plastic -# Manufacturing -> Rubber & Plastic -> Tires & Inner Tubes -# Manufacturing -> Rubber & Plastic -> Rubber & Plastics Footwear -# Manufacturing -> Rubber & Plastic -> Gaskets & Packg & Sealg Devices & -# Manufacturing -> Rubber & Plastic -> Fabricated Rubber Products -# Manufacturing -> Rubber & Plastic -> Miscellaneous Plastics Products -# Manufacturing -> Rubber & Plastic -> Unsupported Plastics Film & Shee -# Manufacturing -> Rubber & Plastic -> Plastics Foam Products -# Manufacturing -> Rubber & Plastic -> Plastics Products -# Manufacturing -> Leather -# Manufacturing -> Leather -> Leather Products -# Manufacturing -> Leather -> Footwear, (No Rubber) -# Manufacturing -> Stone, Clay, Glass & Concrete -# Manufacturing -> Stone, Clay, Glass & Concrete -> Flat Glass -# Manufacturing -> Stone, Clay, Glass & Concrete -> Glass & Glasswear, Pressed or Bl -# Manufacturing -> Stone, Clay, Glass & Concrete -> Glass Containers -# Manufacturing -> Stone, Clay, Glass & Concrete -> Glass Products Made of Purchase -# Manufacturing -> Stone, Clay, Glass & Concrete -> Cements, Hydrallic -# Manufacturing -> Stone, Clay, Glass & Concrete -> Structural Clay Products -# Manufacturing -> Stone, Clay, Glass & Concrete -> Pottery & Related Products -# Manufacturing -> Stone, Clay, Glass & Concrete -> Concreate, Gypsum & Plaster Produ -# Manufacturing -> Stone, Clay, Glass & Concrete -> Concrete Products, Except Block -# Manufacturing -> Stone, Clay, Glass & Concrete -> Cut Stone & Stone Products -# Manufacturing -> Stone, Clay, Glass & Concrete -> Abrasive, Asbestos & Misc Nonmet -# Manufacturing -> Primary Metal -# Manufacturing -> Primary Metal -> Steel Works, Blast Furnaces & Ro -# Manufacturing -> Primary Metal -> Steel Works, Blast Furnaces & Ro -# Manufacturing -> Primary Metal -> Steel Pipe & Tubes -# Manufacturing -> Primary Metal -> Iron & Steel Foundries -# Manufacturing -> Primary Metal -> Primary Smelting & Refining of N -# Manufacturing -> Primary Metal -> Primary Production of Aluminium -# Manufacturing -> Primary Metal -> Secondary Smelting & Refining of -# Manufacturing -> Primary Metal -> Rolling Drawing & Extruding of N -# Manufacturing -> Primary Metal -> Drawing & Insulating of Non Ferro -# Manufacturing -> Primary Metal -> Nonferrous Foundries (Castings) -# Manufacturing -> Primary Metal -> Miscellaneous Primary Metal Prod -# Manufacturing -> Fabricated Metal Products -# Manufacturing -> Fabricated Metal Products -> Metal Cans -# Manufacturing -> Fabricated Metal Products -> Metal Shipping Barrels, Drums, K -# Manufacturing -> Fabricated Metal Products -> Cutlery, Handtools & General Har -# Manufacturing -> Fabricated Metal Products -> Hearing Equip, Except Elec & War -# Manufacturing -> Fabricated Metal Products -> Heating Equipment, Except Electr -# Manufacturing -> Fabricated Metal Products -> Fabricated Structural Metal Prod -# Manufacturing -> Fabricated Metal Products -> Metal Doors, Sash, Frames, Moldi -# Manufacturing -> Fabricated Metal Products -> Fabricated Plate Work (Boiler Sh -# Manufacturing -> Fabricated Metal Products -> Sheet Metal Work -# Manufacturing -> Fabricated Metal Products -> Prefabricated Metal Buildings & -# Manufacturing -> Fabricated Metal Products -> Screw Machine Products -# Manufacturing -> Fabricated Metal Products -> Bolts, Nuts, Screws, Rivets & Wa -# Manufacturing -> Fabricated Metal Products -> Metal Forgings & Stampings -# Manufacturing -> Fabricated Metal Products -> Coating, Engraving & Allied Serv -# Manufacturing -> Fabricated Metal Products -> Ordnance & Accessories, (No Vehi -# Manufacturing -> Fabricated Metal Products -> Miscellaneous Fabricated Metal P -# Manufacturing -> Ind Machinery & Computers -# Manufacturing -> Ind Machinery & Computers -> Engines & Turbines -# Manufacturing -> Ind Machinery & Computers -> Farm Machinery & Equipment -# Manufacturing -> Ind Machinery & Computers -> Lawn & Garden Tractors & Home La -# Manufacturing -> Ind Machinery & Computers -> Construction, Mining & Materials -# Manufacturing -> Ind Machinery & Computers -> Construction Machinery & Equip -# Manufacturing -> Ind Machinery & Computers -> Mining Machinery & Equip (No Oil -# Manufacturing -> Ind Machinery & Computers -> Oil & Gas Field Machinery & Equi -# Manufacturing -> Ind Machinery & Computers -> Industrial Trucks, Tractors, Tra -# Manufacturing -> Ind Machinery & Computers -> Metalworking Machinery & Equipment -# Manufacturing -> Ind Machinery & Computers -> Machine Tools, Metal Cutting Typ -# Manufacturing -> Ind Machinery & Computers -> Special Industry Machinery (No M -# Manufacturing -> Ind Machinery & Computers -> Printing Trades Machinery & Equi -# Manufacturing -> Ind Machinery & Computers -> Special Industry Machinery -# Manufacturing -> Ind Machinery & Computers -> General Industrial Machinery & E -# Manufacturing -> Ind Machinery & Computers -> Pumps & Pumping Equipment -# Manufacturing -> Ind Machinery & Computers -> Ball & Roller Bearings -# Manufacturing -> Ind Machinery & Computers -> Industrial & Commercial Fans & B -# Manufacturing -> Ind Machinery & Computers -> Industrial Process Furnaces & Ov -# Manufacturing -> Ind Machinery & Computers -> General Industrial Machinery & E -# Manufacturing -> Ind Machinery & Computers -> Computer & Office Equipment -# Manufacturing -> Ind Machinery & Computers -> Electronic Computers -# Manufacturing -> Ind Machinery & Computers -> Computer Storage Devices -# Manufacturing -> Ind Machinery & Computers -> Computer Terminals -# Manufacturing -> Ind Machinery & Computers -> Computer Communications Equipmen -# Manufacturing -> Ind Machinery & Computers -> Computer Peripheral Equipment -# Manufacturing -> Ind Machinery & Computers -> Calculating & Accounting Machine -# Manufacturing -> Ind Machinery & Computers -> Office Machines -# Manufacturing -> Ind Machinery & Computers -> Refrigeration & Service Industry -# Manufacturing -> Ind Machinery & Computers -> Air-Cond & Warm Air Heatg Equip -# Manufacturing -> Ind Machinery & Computers -> Misc Industrial & Commercial Mac -# Manufacturing -> Electronics -# Manufacturing -> Electronics -> Electronic & Other Electrical Eq -# Manufacturing -> Electronics -> Power, Distribution & Specialty -# Manufacturing -> Electronics -> Switchgear & Switchboard Apparat -# Manufacturing -> Electronics -> Electrical Industrial Apparatus -# Manufacturing -> Electronics -> Motors & Generators -# Manufacturing -> Electronics -> Household Appliances -# Manufacturing -> Electronics -> Electric Housewares & Fans -# Manufacturing -> Electronics -> Electric Lighting & Wiring Equip -# Manufacturing -> Electronics -> Household Audio & Video Equipmen -# Manufacturing -> Electronics -> Phonograph Records & Prerecorded -# Manufacturing -> Electronics -> Telephone & Telegraph Apparatus -# Manufacturing -> Electronics -> Radio & Tv Broadcasting & Commun -# Manufacturing -> Electronics -> Communications Equipment -# Manufacturing -> Electronics -> Electronic Components & Accessor -# Manufacturing -> Electronics -> Printed Circuit Boards -# Manufacturing -> Electronics -> Semiconductors & Related Devices -# Manufacturing -> Electronics -> Electronic Coils & Transformers & -# Manufacturing -> Electronics -> Electronic Connectors -# Manufacturing -> Electronics -> Electronic Components -# Manufacturing -> Electronics -> Miscellaneous Electrical Machine -# Manufacturing -> Electronics -> Magnetic & Optical Recording Med -# Manufacturing -> Transportation Equipment -# Manufacturing -> Transportation Equipment -> Motor Vehicles & Passenger Car B -# Manufacturing -> Transportation Equipment -> Truck & Bus Bodies -# Manufacturing -> Transportation Equipment -> Motor Vehicle Parts & Accessorie -# Manufacturing -> Transportation Equipment -> Truck Trailers -# Manufacturing -> Transportation Equipment -> Motor Homes -# Manufacturing -> Transportation Equipment -> Aircraft & Parts -# Manufacturing -> Transportation Equipment -> Aircraft -# Manufacturing -> Transportation Equipment -> Aircraft Engines & Engine Parts -# Manufacturing -> Transportation Equipment -> Aircraft Parts & Auxiliary Equip -# Manufacturing -> Transportation Equipment -> Ship & Boat Building & Repairing -# Manufacturing -> Transportation Equipment -> Railroad Equipment -# Manufacturing -> Transportation Equipment -> Motorcycles, Bicycles & Parts -# Manufacturing -> Transportation Equipment -> Guided Missiles & Space Vehicles -# Manufacturing -> Transportation Equipment -> Miscellaneous Transportation Equ -# Manufacturing -> Specialty Instruments -# Manufacturing -> Specialty Instruments -> Search, Detection, Navagation, G -# Manufacturing -> Specialty Instruments -> Laboratory Apparatus & Furniture -# Manufacturing -> Specialty Instruments -> Auto Controls For Regulating Res -# Manufacturing -> Specialty Instruments -> Industrial Instruments For Measu -# Manufacturing -> Specialty Instruments -> Totalizing Fluid & Counti -# Manufacturing -> Specialty Instruments -> Instruments For Meas & Testing o -# Manufacturing -> Specialty Instruments -> Laboratory Analytical Instrument -# Manufacturing -> Specialty Instruments -> Optical Instruments & Lenses -# Manufacturing -> Specialty Instruments -> Measuring & Controlling Devices -# Manufacturing -> Specialty Instruments -> Surgical & Medical Instruments & -# Manufacturing -> Specialty Instruments -> Orthopedic, Prosthetic & Surgica -# Manufacturing -> Specialty Instruments -> Dental Equipment & Supplies -# Manufacturing -> Specialty Instruments -> X-Ray Apparatus & Tubes & Relate -# Manufacturing -> Specialty Instruments -> Electromedical & Electrotherapeu -# Manufacturing -> Specialty Instruments -> Ophthalmic Goods -# Manufacturing -> Specialty Instruments -> Photographic Equipment & Supplie -# Manufacturing -> Specialty Instruments -> Watches, Clocks, Clockwork Opera -# Manufacturing -> Miscellaneous Manufacturing -# Manufacturing -> Miscellaneous Manufacturing -> Jewelry, Silverware & Plated War -# Manufacturing -> Miscellaneous Manufacturing -> Jewelry, Precious Metal -# Manufacturing -> Miscellaneous Manufacturing -> Musical Instruments -# Manufacturing -> Miscellaneous Manufacturing -> Dolls & Stuffed Toys -# Manufacturing -> Miscellaneous Manufacturing -> Games, Toys & Children’s Vehicle -# Manufacturing -> Miscellaneous Manufacturing -> Sporting & Athletic Goods -# Manufacturing -> Miscellaneous Manufacturing -> Pens, Pencils & Other Artists’ M -# Manufacturing -> Miscellaneous Manufacturing -> Costumes Jewelry & Novelties -# Manufacturing -> Miscellaneous Manufacturing -> Miscellaneous Manufacturing Indu - -# Transportation & Utilities -# Transportation & Utilities -> Railroads -# Transportation & Utilities -> Railroads -> Railroads, Line-Haul Operating -# Transportation & Utilities -> Railroads -> Railroad Switching & Terminal Es -# Transportation & Utilities -> Passenger Road Transportation -# Transportation & Utilities -> Road Freight Transportation -# Transportation & Utilities -> Road Freight Transportation -> Trucking & Courier Services (No -# Transportation & Utilities -> Road Freight Transportation -> Trucking (No Local) -# Transportation & Utilities -> Road Freight Transportation -> Public Warehousing & Storage -# Transportation & Utilities -> Road Freight Transportation -> Terminal Maintenance Facilities -# Transportation & Utilities -> USPS -# Transportation & Utilities -> Water Transportation -# Transportation & Utilities -> Water Transportation -> Water Transportation -# Transportation & Utilities -> Water Transportation -> Deep Sea Foreign Transportation -# Transportation & Utilities -> Air Transportation -# Transportation & Utilities -> Air Transportation -> Air Transportation, Scheduled -# Transportation & Utilities -> Air Transportation -> Air Courier Services -# Transportation & Utilities -> Air Transportation -> Air Transportation, Nonscheduled -# Transportation & Utilities -> Air Transportation -> Airports, Flying Fields & Airpor -# Transportation & Utilities -> Pipelines -# Transportation & Utilities -> Transportation Services -# Transportation & Utilities -> Transportation Services -> Transportation Services -# Transportation & Utilities -> Transportation Services -> Arrangement of Transportation of -# Transportation & Utilities -> Communications -# Transportation & Utilities -> Communications -> Radiotelephone Communications -# Transportation & Utilities -> Communications -> Telephone Communications (No Rad -# Transportation & Utilities -> Communications -> Telegraph & Other Message Commun -# Transportation & Utilities -> Communications -> Radio Broadcasting Stations -# Transportation & Utilities -> Communications -> Television Broadcasting Stations -# Transportation & Utilities -> Communications -> Cable & Other Pay Television Ser -# Transportation & Utilities -> Communications -> Communications Services -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric, Gas, & Sanitary Svcs -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric Services -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Transmission -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Transmission & Distr -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Distribution -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric & Other Services Combin -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Gas & Other Services Combined -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Water Supply -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Sanitary Services -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Refuse Systems -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Hazardous Waste Management -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Steam & Air-Conditioning Supply -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Cogeneration Services & Small Po - -# Wholesale Trade -# Wholesale Trade -> Durable Goods -> Durable Goods -# Wholesale Trade -> Durable Goods -> Motor Vehicles & Motor Vehicle P -# Wholesale Trade -> Durable Goods -> Motor Vehicles Supplies & New Par -# Wholesale Trade -> Durable Goods -> Furniture & Home Furnishings -# Wholesale Trade -> Durable Goods -> Lumber & Other Construction Mate -# Wholesale Trade -> Durable Goods -> Lumber, Plywood, Millwork & Wood -# Wholesale Trade -> Durable Goods -> Professional & Commercial Equipm -# Wholesale Trade -> Durable Goods -> Computers & Peripheral Equipment -# Wholesale Trade -> Durable Goods -> Medical, Dental & Hospital Equip -# Wholesale Trade -> Durable Goods -> Metals & Minerals (No Petroleum) -# Wholesale Trade -> Durable Goods -> Metals Services Centers & Offices -# Wholesale Trade -> Durable Goods -> Electrical Apparatus & Equipment -# Wholesale Trade -> Durable Goods -> Electrical Appliances, Tv & Radi -# Wholesale Trade -> Durable Goods -> Electronic Parts & Equipment -# Wholesale Trade -> Durable Goods -> Hardware & Plumbing & Heating Eq -# Wholesale Trade -> Durable Goods -> Hardware -# Wholesale Trade -> Durable Goods -> Machinery, Equipment & Supplies -# Wholesale Trade -> Durable Goods -> Construction & Mining (No Petro) -# Wholesale Trade -> Durable Goods -> Industrial Machinery & Equipment -# Wholesale Trade -> Durable Goods -> Misc Durable Goods -# Wholesale Trade -> Durable Goods -> Jewelry, Watches, Precious Stone -# Wholesale Trade -> Durable Goods -> Durable Goods -# Wholesale Trade -> Nondurable Goods -# Wholesale Trade -> Nondurable Goods -> Paper & Paper Products -# Wholesale Trade -> Nondurable Goods -> Drugs, Proprietaries & Druggists -# Wholesale Trade -> Nondurable Goods -> Apparel, Piece Goods & Notions -# Wholesale Trade -> Nondurable Goods -> Groceries & Related Products -# Wholesale Trade -> Nondurable Goods -> Groceries, General Line -# Wholesale Trade -> Nondurable Goods -> Farm Product Raw Materials -# Wholesale Trade -> Nondurable Goods -> Chemicals & Allied Products -# Wholesale Trade -> Nondurable Goods -> Petroleum Bulk Stations & Termin -# Wholesale Trade -> Nondurable Goods -> Petroleum & Petroleum Products ( -# Wholesale Trade -> Nondurable Goods -> Beer, Wine & Distilled Alcoholic -# Wholesale Trade -> Nondurable Goods -> Miscellaneous Nondurable Goods - -# Retail Trade -# Retail Trade -> Building Materials -# Retail Trade -> Building Materials -> Building Materials, Hardware, Ga -# Retail Trade -> Building Materials -> Lumber & Other Buildings Material -# Retail Trade -> Building Materials -> Mobile Home Dealers -# Retail Trade -> General Merchandise -# Retail Trade -> General Merchandise -> Department Stores -# Retail Trade -> General Merchandise -> Variety Stores -# Retail Trade -> General Merchandise -> Misc General Merchandise Stores -# Retail Trade -> Grocery -# Retail Trade -> Grocery -> Food Stores -# Retail Trade -> Grocery -> Grocery Stores -# Retail Trade -> Grocery -> Convenience Stores -# Retail Trade -> Auto Dealers & Service Stations -# Retail Trade -> Auto Dealers & Service Stations -> Auto Dealers & Gasoline Stations -# Retail Trade -> Auto Dealers & Service Stations -> Auto & Home Supply Stores -# Retail Trade -> Apparel & Accessory Stores -# Retail Trade -> Apparel & Accessory Stores -> Apparel & Accessory Stores -# Retail Trade -> Apparel & Accessory Stores -> Women’s Clothing Stores -# Retail Trade -> Apparel & Accessory Stores -> Family Clothing Stores -# Retail Trade -> Apparel & Accessory Stores -> Shoe Stores -# Retail Trade -> Home Furniture -# Retail Trade -> Home Furniture -> Home Furniture, Furnishings & Eq -# Retail Trade -> Home Furniture -> Furniture Stores -# Retail Trade -> Home Furniture -> Ratio, Tv & Consumer Electronics -# Retail Trade -> Home Furniture -> Computer & Computer Software Sto -# Retail Trade -> Home Furniture -> Record & Prerecord Tape Stores -# Retail Trade -> Eating & Drinking Places -# Retail Trade -> Eating & Drinking Places -> Eating & Drinking Places -# Retail Trade -> Eating & Drinking Places -> Eating Places -# Retail Trade -> Miscellaneous Retail -# Retail Trade -> Miscellaneous Retail -> Miscellaneous Retail -# Retail Trade -> Miscellaneous Retail -> Drug Stores & Proprietary Stores -# Retail Trade -> Miscellaneous Retail -> Miscellaneous Shopping Goods Sto -# Retail Trade -> Miscellaneous Retail -> Jewelry Stores -# Retail Trade -> Miscellaneous Retail -> Hobby, Toy & Game Shops -# Retail Trade -> Miscellaneous Retail -> Nonstore Retailers -# Retail Trade -> Miscellaneous Retail -> Catalog & Mail-Order Houses -# Retail Trade -> Miscellaneous Retail -> Retail Stores - -# Financial -# Financial -> Depository Institutions -# Financial -> Depository Institutions -> National Commercial Banks -# Financial -> Depository Institutions -> State Commercial Banks -# Financial -> Depository Institutions -> Commercial Banks -# Financial -> Depository Institutions -> Savings Institution, Federally C -# Financial -> Depository Institutions -> Savings Institution, Not Federa -# Financial -> Depository Institutions -> Functions Related to Despository -# Financial -> Non-depository Credit Inst -# Financial -> Non-depository Credit Inst -> Federal & Federally-Sponsored Cr -# Financial -> Non-depository Credit Inst -> Personal Credit Institutions -# Financial -> Non-depository Credit Inst -> Short-Term Business Credit Insti -# Financial -> Non-depository Credit Inst -> Miscellaneous Business Credit In -# Financial -> Non-depository Credit Inst -> Mortgage Bankers & Loan Correspo -# Financial -> Non-depository Credit Inst -> Loan Brokers -# Financial -> Non-depository Credit Inst -> Finance Lessors -# Financial -> Non-depository Credit Inst -> Asset-Backed Securities -# Financial -> Non-depository Credit Inst -> Finance Services -# Financial -> Security & Commodity Brokers -# Financial -> Security & Commodity Brokers -> Security & Commodity Brokers, De -# Financial -> Security & Commodity Brokers -> Security Brokers, Dealers & Flot -# Financial -> Security & Commodity Brokers -> Commodity Contracts Brokers & De -# Financial -> Security & Commodity Brokers -> Investment Advice -# Financial -> Insurance Carriers -# Financial -> Insurance Carriers -> Life Insurance -# Financial -> Insurance Carriers -> Accident & Health Insurance -# Financial -> Insurance Carriers -> Hospital & Medical Service Plans -# Financial -> Insurance Carriers -> Fire, Marine & Casualty Insuranc -# Financial -> Insurance Carriers -> Surety Insurance -# Financial -> Insurance Carriers -> Title Insurance -# Financial -> Insurance Carriers -> Insurance Carriers -# Financial -> Insurance Agents -# Financial -> Real Estate -# Financial -> Real Estate -> Real Estate -# Financial -> Real Estate -> Real Estate Operators (No Develo -# Financial -> Real Estate -> Operators of Nonresidential Buil -# Financial -> Real Estate -> Operators of Apartment Buildings -# Financial -> Real Estate -> Lessors of Real Property -# Financial -> Real Estate -> Real Estate Agents & Managers -# Financial -> Real Estate -> Real Estate Dealers (For Their O -# Financial -> Real Estate -> Land Subdividers & Developers (N -# Financial -> Holding & Investment Offices -# Financial -> Holding & Investment Offices -> Blank Checks -# Financial -> Holding & Investment Offices -> Oil Royalty Traders -# Financial -> Holding & Investment Offices -> Patent Owners & Lessors -# Financial -> Holding & Investment Offices -> Mineral Royalty Traders -# Financial -> Holding & Investment Offices -> Real Estate Investment Trusts -# Financial -> Holding & Investment Offices -> Investors - -# Services -# Services -> Hotels -# Services -> Hotels -> Hotels, Rooming Houses, Camps & -# Services -> Hotels -> Hotels & Motels -# Services -> Personal Services -# Services -> Business Services -# Services -> Business Services -> Advertising -# Services -> Business Services -> Advertising Agencies -# Services -> Business Services -> Consumer Credit Reporting, Colle -# Services -> Business Services -> Mailing, Reproduction, Commercia -# Services -> Business Services -> Direct Mail Advertising Services -# Services -> Business Services -> To Dwellings & Other Buildings -# Services -> Business Services -> Miscellaneous Equipment Rental & -# Services -> Business Services -> Equipment Rental & Leasing -# Services -> Business Services -> Employment Agencies -# Services -> Business Services -> Help Supply Services -# Services -> Business Services -> Computer Programming, Data Proce -# Services -> Business Services -> Computer Programming Services -# Services -> Business Services -> Prepackaged Software -# Services -> Business Services -> Computer Integrated Systems Desi -# Services -> Business Services -> Computer Processing & Data Prepa -# Services -> Business Services -> Computer Rental & Leasing -# Services -> Business Services -> Miscellaneous Business Services -# Services -> Business Services -> Detective, Guard & Armored Car S -# Services -> Business Services -> Photofinishing Laboratories -# Services -> Business Services -> Telephone Interconnect Systems -# Services -> Business Services -> Business Services -# Services -> Automotive Repair -# Services -> Automotive Repair -> Automotive Repair, Services & Pa -# Services -> Automotive Repair -> Auto Rental & Leasing (No Driver -# Services -> Miscellaneous Repair -# Services -> Motion Pictures -# Services -> Motion Pictures -> Motion Picture & Video Tape Prod -# Services -> Motion Pictures -> Allied to Motion Picture Product -# Services -> Motion Pictures -> Motion Picture & Video Tape Dist -# Services -> Motion Pictures -> Allied to Motion Picture Distrib -# Services -> Motion Pictures -> Motion Picture Theaters -# Services -> Motion Pictures -> Video Tape Rental -# Services -> Amusement & Rec Svcs -# Services -> Amusement & Rec Svcs -> Amusement Recreation Services -# Services -> Amusement & Rec Svcs -> Racing, Including Track Operatio -# Services -> Amusement & Rec Svcs -> Miscellaneous Amusement & Recrea -# Services -> Amusement & Rec Svcs -> Membership Sports & Recreation C -# Services -> Health Services -# Services -> Health Services -> Health Services -# Services -> Health Services -> Offices & Clinics of Doctors of -# Services -> Health Services -> Nursing & Personal Care Faciliti -# Services -> Health Services -> Skilled Nursing Care Facilities -# Services -> Health Services -> Hospitals -# Services -> Health Services -> General Medical & Surgical Hospi -# Services -> Health Services -> Medical Laboratories -# Services -> Health Services -> Home Health Care Services -# Services -> Health Services -> Misc Health & Allied Services -# Services -> Health Services -> Specialty Outpatient Facilities -# Services -> Legal Services -# Services -> Educational Services -# Services -> Social Services -# Services -> Social Services -> Social Services -# Services -> Social Services -> Child Day Care Services -# Services -> Museums -# Services -> Membership Organizations -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Engineering, Accounting, Research -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Engineering Services -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Commercial Physical & Biological -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Testing Laboratories -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Management Services -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Management Consulting Services -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Facilities Support Management Se -# Services -> Miscellaneous Services - -# Closed-End Funds diff --git a/openbb_terminal/miscellaneous/stocks/insider/whales.ini b/openbb_terminal/miscellaneous/stocks/insider/whales.ini deleted file mode 100644 index f6ef2296a0b..00000000000 --- a/openbb_terminal/miscellaneous/stocks/insider/whales.ini +++ /dev/null @@ -1,617 +0,0 @@ -# Author of preset: Sexy Year -# Description: Tries to catch whales that have traded a minimum of 100M$ with a stock with a minimum share price of 20$ in the past 2 weeks. - -[General] -# Symbols or CIKs -Tickers = -# Insider Name or CIK -Insider = -SharePriceMin = 20 -SharePriceMax = -LiquidityMinM = -LiquidityMaxM = - -[Date] -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -FilingDate = All dates -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -FilingDateFrom = -FilingDateTo = -# All dates, Custom, Latest Day, Last 3 days, Last 1 week, Last 2 weeks, Last 1 month, Last 2 months, Last 3 months, Last 6 months, Last 1 year, Last 2 years, Last 4 years. -TradingDate = Last 2 weeks -# This is only regarded if FillingDate = Custom, format: dd/mm/yyyy -TradingDateFrom = -TradingDateTo = -# These values need to be smaller than 100 -FilingDelayMin = -FilingDelayMax = -NDaysAgo = - -[TransactionFiling] -# The following values are meant to be either: 'true' or empty for False. -P_Purchase = true -S_Sale = true -A_Grant = -D_SaleToLoss = -G_Gift = -NoDeriv = -F_Tax = -M_OptionEx = -X_OptionEx = -C_CnvDeriv = -W_Inherited = -MultipleDays = -# These values need to be multiples of 5 -TradedMinK = 100000 -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - -[Industry] -# Available industry options at bottom of the file. -# Sector -> Subsector -> Industry -SectorSubsectorIndustry = All Sectors (except Funds) - -[InsiderTitle] -# The following values are meant to be either: 'true' or empty for False. -COB = -CEO = -Pres = -COO = -CFO = -GC = -VP = -# 'Officer' includes all of the above: COB, CEO, Pres, COO, CFO, GC, VP -Officer = -Director = -10PctOwn = -Other = - -[Others] -# Filing, Company. -GroupBy = Filing -# Filing Date, Trade Date, Ticker Symbol, Trade Value. -SortBy = Filing Date -# 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000. -MaxResults = 100 -# Between 1 and 99, inclusive. -Page = 1 - -# This only exists if GroupBy = Company -[CompanyTotals] -# These values need to be smaller than 100 -FilingsMin = -FilingsMax = -# These values need to be smaller than 10 -InsidersMin = -InsidersMax = -OfficersMin = -OfficersMax = -# These values need to be multiples of 5 -TradedMinK = -TradedMaxK = -# These values need to be between 0 and 100% -OwnChangeMinPct = -OwnChangeMaxPct = - - -# Available SectorSubsectorIndustry options below: -# All Sectors (except Funds) - -# Agriculture, Forestry, Fish -# Agriculture, Forestry, Fish -> Crops -# Agriculture, Forestry, Fish -> Livestock & Animal Specialties -# Agriculture, Forestry, Fish -> Agriculture Services -# Agriculture, Forestry, Fish -> Forestry -# Agriculture, Forestry, Fish -> Fishing, Hunting & Trapping - -# Mining -# Mining -> Metal Mining -# Mining -> Metal Mining -> Gold & Silver Ores -# Mining -> Metal Mining -> Miscellaneous Metal Ores -# Mining -> Coal Mining -# Mining -> Coal Mining -> Bituminous Coal & Lignite Mining -# Mining -> Coal Mining -> Bituminous Coal & Lignite Surfac -# Mining -> Oil & Gas Extraction -# Mining -> Oil & Gas Extraction -> Crude Petroleum & Natural Gas -# Mining -> Oil & Gas Extraction -> Drilling Oil & Gas Wells -# Mining -> Oil & Gas Extraction -> Oil & Gas Field Exploration Serv -# Mining -> Oil & Gas Extraction -> Oil & Gas Field Services -# Mining -> Mining Non-metallic Minerals - -# Construction -# Construction -> General Contractors & Builders -# Construction -> General Contractors & Builders -> Residential Bldg Contractors -# Construction -> General Contractors & Builders -> Operative Builders -# Construction -> General Contractors & Builders -> Nonresidential Bldg Contractors -# Construction -> Heavy Construction -# Construction -> Heavy Construction -> Heavy Construction Non-Bldg Cont -# Construction -> Heavy Construction -> Water, Sewer, Pipeline, Comm & P -# Construction -> Special Trade Contractors -# Construction -> Special Trade Contractors -> Special Trade Contractors -# Construction -> Special Trade Contractors -> Electrical Work - -# Manufacturing -# Manufacturing -> Food -# Manufacturing -> Food -> Food & Kindred Products -# Manufacturing -> Food -> Meat Packing Plants -# Manufacturing -> Food -> Sausage & Other Prepared Meat P -# Manufacturing -> Food -> Poultry Slaughtering & Processin -# Manufacturing -> Food -> Dairy Products -# Manufacturing -> Food -> Ice Cream & Froze Desserts -# Manufacturing -> Food -> Canned, Frozen & Preservd Fruit -# Manufacturing -> Food -> Canned, Fruits, Veg, Preserves -# Manufacturing -> Food -> Grain Mill Products -# Manufacturing -> Food -> Bakery Products -# Manufacturing -> Food -> Cookies & Crackers -# Manufacturing -> Food -> Sugar & Confectionery Products -# Manufacturing -> Food -> Fats & Oils -# Manufacturing -> Food -> Beverages -# Manufacturing -> Food -> Malt Beverages -# Manufacturing -> Food -> Bottled & Canned Soft Drinks & C -# Manufacturing -> Food -> Miscellaneous Food Preparations -# Manufacturing -> Food -> Prepared Fresh or Frozen Fish & -# Manufacturing -> Tobacco -# Manufacturing -> Tobacco -> Tobacco Products -# Manufacturing -> Tobacco -> Cigarettes -# Manufacturing -> Textile Mill Products -# Manufacturing -> Textile Mill Products -> Textile Mill Products -# Manufacturing -> Textile Mill Products -> Broadwoven Fabric Mills, Cotton -# Manufacturing -> Textile Mill Products -> Broadwoven Fabric Mills, Man Mad -# Manufacturing -> Textile Mill Products -> Knitting Mills -# Manufacturing -> Textile Mill Products -> Knit Outerwear Mills -# Manufacturing -> Textile Mill Products -> Carpets & Rugs -# Manufacturing -> Apparel -# Manufacturing -> Apparel -> Other Finishd Prods of -# Manufacturing -> Apparel -> Men’s & Boy’s Furnishgs, Work Cl -# Manufacturing -> Apparel -> Women’s, Misses’, & Juniors Oute -# Manufacturing -> Apparel -> Women’s, Misses’, Children’s & l -# Manufacturing -> Apparel -> Miscellaneous Fabric -# Manufacturing -> Lumber -# Manufacturing -> Lumber -> Lumber & Wood Products (No Furni) -# Manufacturing -> Lumber -> Sawmills & Planting Mills, Gener -# Manufacturing -> Lumber -> Millwood, Veneer, Plywood & Str -# Manufacturing -> Lumber -> Mobile Homes -# Manufacturing -> Lumber -> Prefabricated Wood Bldgs & Compo -# Manufacturing -> Furniture -# Manufacturing -> Furniture -> Household Furniture -# Manufacturing -> Furniture -> Wood Household Furniture, (no Up) -# Manufacturing -> Furniture -> Office Furniture -# Manufacturing -> Furniture -> Office Furniture (No Wood) -# Manufacturing -> Furniture -> Public Bldg & Related Furniture -# Manufacturing -> Furniture -> Partitions, Shelvg, Lockers & O -# Manufacturing -> Furniture -> Miscellaneous Furniture & Fixtur -# Manufacturing -> Paper -# Manufacturing -> Paper -> Papers & Allied Products -# Manufacturing -> Paper -> Pulp Mills -# Manufacturing -> Paper -> Paper Mills -# Manufacturing -> Paper -> Paperboard Mills -# Manufacturing -> Paper -> Paperboard Containers & Boxes -# Manufacturing -> Paper -> Converted Paper & Paperboard Pro -# Manufacturing -> Paper -> Plastics, Foils & Coated Paper Ba -# Manufacturing -> Printing & Publishing -# Manufacturing -> Printing & Publishing -> Newspapers: Publishing or Publis -# Manufacturing -> Printing & Publishing -> Periodicals: Publishing or Publi -# Manufacturing -> Printing & Publishing -> Books: Publishing or Publishing -# Manufacturing -> Printing & Publishing -> Book Printing -# Manufacturing -> Printing & Publishing -> Miscellaneous Publishing -# Manufacturing -> Printing & Publishing -> Commercial Printing -# Manufacturing -> Printing & Publishing -> Manifold Business Forms -# Manufacturing -> Printing & Publishing -> Greeting Cards -# Manufacturing -> Printing & Publishing -> Blankbooks, Looseleaf Binders & -# Manufacturing -> Printing & Publishing ->Service Industries For The Print -# Manufacturing -> Chemicals -# Manufacturing -> Chemicals -> Chemicals & Allied Products -# Manufacturing -> Chemicals -> Industrial Inorganic Chemicals -# Manufacturing -> Chemicals -> Plastic Material, Synth Resin/Ru -# Manufacturing -> Chemicals -> Plastic Material, Synth Resins -# Manufacturing -> Chemicals -> Medicinal Chemicals & Botanical -# Manufacturing -> Chemicals -> Pharmaceutical Preparations -# Manufacturing -> Chemicals -> In Vitro & In Vito Diagnostic Su -# Manufacturing -> Chemicals -> Biological Products ,(No Diagnos -# Manufacturing -> Chemicals -> Soap, Detergents, Cleaning, Perf -# Manufacturing -> Chemicals -> Specialty Cleaning, Polishing & -# Manufacturing -> Chemicals -> Perfumes, Cosmetics & Other Toil -# Manufacturing -> Chemicals -> Paints, Varnishes, Lacquers, Ena -# Manufacturing -> Chemicals -> Industrial Organic Chemicals -# Manufacturing -> Chemicals ->Agriculture Chemicals -# Manufacturing -> Chemicals -> Miscellaneous Chemical Products -# Manufacturing -> Chemicals -> Adhesives & Sealants -# Manufacturing -> Petroleum Refining -# Manufacturing -> Petroleum Refining -> Petroleum Refining -# Manufacturing -> Petroleum Refining -> Asphalt Paving & Roofing Materia -# Manufacturing -> Petroleum Refining -> Miscellaneous Products of Petrol -# Manufacturing -> Rubber & Plastic -# Manufacturing -> Rubber & Plastic -> Tires & Inner Tubes -# Manufacturing -> Rubber & Plastic -> Rubber & Plastics Footwear -# Manufacturing -> Rubber & Plastic -> Gaskets & Packg & Sealg Devices & -# Manufacturing -> Rubber & Plastic -> Fabricated Rubber Products -# Manufacturing -> Rubber & Plastic -> Miscellaneous Plastics Products -# Manufacturing -> Rubber & Plastic -> Unsupported Plastics Film & Shee -# Manufacturing -> Rubber & Plastic -> Plastics Foam Products -# Manufacturing -> Rubber & Plastic -> Plastics Products -# Manufacturing -> Leather -# Manufacturing -> Leather -> Leather Products -# Manufacturing -> Leather -> Footwear, (No Rubber) -# Manufacturing -> Stone, Clay, Glass & Concrete -# Manufacturing -> Stone, Clay, Glass & Concrete -> Flat Glass -# Manufacturing -> Stone, Clay, Glass & Concrete -> Glass & Glasswear, Pressed or Bl -# Manufacturing -> Stone, Clay, Glass & Concrete -> Glass Containers -# Manufacturing -> Stone, Clay, Glass & Concrete -> Glass Products Made of Purchase -# Manufacturing -> Stone, Clay, Glass & Concrete -> Cements, Hydrallic -# Manufacturing -> Stone, Clay, Glass & Concrete -> Structural Clay Products -# Manufacturing -> Stone, Clay, Glass & Concrete -> Pottery & Related Products -# Manufacturing -> Stone, Clay, Glass & Concrete -> Concreate, Gypsum & Plaster Produ -# Manufacturing -> Stone, Clay, Glass & Concrete -> Concrete Products, Except Block -# Manufacturing -> Stone, Clay, Glass & Concrete -> Cut Stone & Stone Products -# Manufacturing -> Stone, Clay, Glass & Concrete -> Abrasive, Asbestos & Misc Nonmet -# Manufacturing -> Primary Metal -# Manufacturing -> Primary Metal -> Steel Works, Blast Furnaces & Ro -# Manufacturing -> Primary Metal -> Steel Works, Blast Furnaces & Ro -# Manufacturing -> Primary Metal -> Steel Pipe & Tubes -# Manufacturing -> Primary Metal -> Iron & Steel Foundries -# Manufacturing -> Primary Metal -> Primary Smelting & Refining of N -# Manufacturing -> Primary Metal -> Primary Production of Aluminium -# Manufacturing -> Primary Metal -> Secondary Smelting & Refining of -# Manufacturing -> Primary Metal -> Rolling Drawing & Extruding of N -# Manufacturing -> Primary Metal -> Drawing & Insulating of Non Ferro -# Manufacturing -> Primary Metal -> Nonferrous Foundries (Castings) -# Manufacturing -> Primary Metal -> Miscellaneous Primary Metal Prod -# Manufacturing -> Fabricated Metal Products -# Manufacturing -> Fabricated Metal Products -> Metal Cans -# Manufacturing -> Fabricated Metal Products -> Metal Shipping Barrels, Drums, K -# Manufacturing -> Fabricated Metal Products -> Cutlery, Handtools & General Har -# Manufacturing -> Fabricated Metal Products -> Hearing Equip, Except Elec & War -# Manufacturing -> Fabricated Metal Products -> Heating Equipment, Except Electr -# Manufacturing -> Fabricated Metal Products -> Fabricated Structural Metal Prod -# Manufacturing -> Fabricated Metal Products -> Metal Doors, Sash, Frames, Moldi -# Manufacturing -> Fabricated Metal Products -> Fabricated Plate Work (Boiler Sh -# Manufacturing -> Fabricated Metal Products -> Sheet Metal Work -# Manufacturing -> Fabricated Metal Products -> Prefabricated Metal Buildings & -# Manufacturing -> Fabricated Metal Products -> Screw Machine Products -# Manufacturing -> Fabricated Metal Products -> Bolts, Nuts, Screws, Rivets & Wa -# Manufacturing -> Fabricated Metal Products -> Metal Forgings & Stampings -# Manufacturing -> Fabricated Metal Products -> Coating, Engraving & Allied Serv -# Manufacturing -> Fabricated Metal Products -> Ordnance & Accessories, (No Vehi -# Manufacturing -> Fabricated Metal Products -> Miscellaneous Fabricated Metal P -# Manufacturing -> Ind Machinery & Computers -# Manufacturing -> Ind Machinery & Computers -> Engines & Turbines -# Manufacturing -> Ind Machinery & Computers -> Farm Machinery & Equipment -# Manufacturing -> Ind Machinery & Computers -> Lawn & Garden Tractors & Home La -# Manufacturing -> Ind Machinery & Computers -> Construction, Mining & Materials -# Manufacturing -> Ind Machinery & Computers -> Construction Machinery & Equip -# Manufacturing -> Ind Machinery & Computers -> Mining Machinery & Equip (No Oil -# Manufacturing -> Ind Machinery & Computers -> Oil & Gas Field Machinery & Equi -# Manufacturing -> Ind Machinery & Computers -> Industrial Trucks, Tractors, Tra -# Manufacturing -> Ind Machinery & Computers -> Metalworking Machinery & Equipment -# Manufacturing -> Ind Machinery & Computers -> Machine Tools, Metal Cutting Typ -# Manufacturing -> Ind Machinery & Computers -> Special Industry Machinery (No M -# Manufacturing -> Ind Machinery & Computers -> Printing Trades Machinery & Equi -# Manufacturing -> Ind Machinery & Computers -> Special Industry Machinery -# Manufacturing -> Ind Machinery & Computers -> General Industrial Machinery & E -# Manufacturing -> Ind Machinery & Computers -> Pumps & Pumping Equipment -# Manufacturing -> Ind Machinery & Computers -> Ball & Roller Bearings -# Manufacturing -> Ind Machinery & Computers -> Industrial & Commercial Fans & B -# Manufacturing -> Ind Machinery & Computers -> Industrial Process Furnaces & Ov -# Manufacturing -> Ind Machinery & Computers -> General Industrial Machinery & E -# Manufacturing -> Ind Machinery & Computers -> Computer & Office Equipment -# Manufacturing -> Ind Machinery & Computers -> Electronic Computers -# Manufacturing -> Ind Machinery & Computers -> Computer Storage Devices -# Manufacturing -> Ind Machinery & Computers -> Computer Terminals -# Manufacturing -> Ind Machinery & Computers -> Computer Communications Equipmen -# Manufacturing -> Ind Machinery & Computers -> Computer Peripheral Equipment -# Manufacturing -> Ind Machinery & Computers -> Calculating & Accounting Machine -# Manufacturing -> Ind Machinery & Computers -> Office Machines -# Manufacturing -> Ind Machinery & Computers -> Refrigeration & Service Industry -# Manufacturing -> Ind Machinery & Computers -> Air-Cond & Warm Air Heatg Equip -# Manufacturing -> Ind Machinery & Computers -> Misc Industrial & Commercial Mac -# Manufacturing -> Electronics -# Manufacturing -> Electronics -> Electronic & Other Electrical Eq -# Manufacturing -> Electronics -> Power, Distribution & Specialty -# Manufacturing -> Electronics -> Switchgear & Switchboard Apparat -# Manufacturing -> Electronics -> Electrical Industrial Apparatus -# Manufacturing -> Electronics -> Motors & Generators -# Manufacturing -> Electronics -> Household Appliances -# Manufacturing -> Electronics -> Electric Housewares & Fans -# Manufacturing -> Electronics -> Electric Lighting & Wiring Equip -# Manufacturing -> Electronics -> Household Audio & Video Equipmen -# Manufacturing -> Electronics -> Phonograph Records & Prerecorded -# Manufacturing -> Electronics -> Telephone & Telegraph Apparatus -# Manufacturing -> Electronics -> Radio & Tv Broadcasting & Commun -# Manufacturing -> Electronics -> Communications Equipment -# Manufacturing -> Electronics -> Electronic Components & Accessor -# Manufacturing -> Electronics -> Printed Circuit Boards -# Manufacturing -> Electronics -> Semiconductors & Related Devices -# Manufacturing -> Electronics -> Electronic Coils & Transformers & -# Manufacturing -> Electronics -> Electronic Connectors -# Manufacturing -> Electronics -> Electronic Components -# Manufacturing -> Electronics -> Miscellaneous Electrical Machine -# Manufacturing -> Electronics -> Magnetic & Optical Recording Med -# Manufacturing -> Transportation Equipment -# Manufacturing -> Transportation Equipment -> Motor Vehicles & Passenger Car B -# Manufacturing -> Transportation Equipment -> Truck & Bus Bodies -# Manufacturing -> Transportation Equipment -> Motor Vehicle Parts & Accessorie -# Manufacturing -> Transportation Equipment -> Truck Trailers -# Manufacturing -> Transportation Equipment -> Motor Homes -# Manufacturing -> Transportation Equipment -> Aircraft & Parts -# Manufacturing -> Transportation Equipment -> Aircraft -# Manufacturing -> Transportation Equipment -> Aircraft Engines & Engine Parts -# Manufacturing -> Transportation Equipment -> Aircraft Parts & Auxiliary Equip -# Manufacturing -> Transportation Equipment -> Ship & Boat Building & Repairing -# Manufacturing -> Transportation Equipment -> Railroad Equipment -# Manufacturing -> Transportation Equipment -> Motorcycles, Bicycles & Parts -# Manufacturing -> Transportation Equipment -> Guided Missiles & Space Vehicles -# Manufacturing -> Transportation Equipment -> Miscellaneous Transportation Equ -# Manufacturing -> Specialty Instruments -# Manufacturing -> Specialty Instruments -> Search, Detection, Navagation, G -# Manufacturing -> Specialty Instruments -> Laboratory Apparatus & Furniture -# Manufacturing -> Specialty Instruments -> Auto Controls For Regulating Res -# Manufacturing -> Specialty Instruments -> Industrial Instruments For Measu -# Manufacturing -> Specialty Instruments -> Totalizing Fluid & Counti -# Manufacturing -> Specialty Instruments -> Instruments For Meas & Testing o -# Manufacturing -> Specialty Instruments -> Laboratory Analytical Instrument -# Manufacturing -> Specialty Instruments -> Optical Instruments & Lenses -# Manufacturing -> Specialty Instruments -> Measuring & Controlling Devices -# Manufacturing -> Specialty Instruments -> Surgical & Medical Instruments & -# Manufacturing -> Specialty Instruments -> Orthopedic, Prosthetic & Surgica -# Manufacturing -> Specialty Instruments -> Dental Equipment & Supplies -# Manufacturing -> Specialty Instruments -> X-Ray Apparatus & Tubes & Relate -# Manufacturing -> Specialty Instruments -> Electromedical & Electrotherapeu -# Manufacturing -> Specialty Instruments -> Ophthalmic Goods -# Manufacturing -> Specialty Instruments -> Photographic Equipment & Supplie -# Manufacturing -> Specialty Instruments -> Watches, Clocks, Clockwork Opera -# Manufacturing -> Miscellaneous Manufacturing -# Manufacturing -> Miscellaneous Manufacturing -> Jewelry, Silverware & Plated War -# Manufacturing -> Miscellaneous Manufacturing -> Jewelry, Precious Metal -# Manufacturing -> Miscellaneous Manufacturing -> Musical Instruments -# Manufacturing -> Miscellaneous Manufacturing -> Dolls & Stuffed Toys -# Manufacturing -> Miscellaneous Manufacturing -> Games, Toys & Children’s Vehicle -# Manufacturing -> Miscellaneous Manufacturing -> Sporting & Athletic Goods -# Manufacturing -> Miscellaneous Manufacturing -> Pens, Pencils & Other Artists’ M -# Manufacturing -> Miscellaneous Manufacturing -> Costumes Jewelry & Novelties -# Manufacturing -> Miscellaneous Manufacturing -> Miscellaneous Manufacturing Indu - -# Transportation & Utilities -# Transportation & Utilities -> Railroads -# Transportation & Utilities -> Railroads -> Railroads, Line-Haul Operating -# Transportation & Utilities -> Railroads -> Railroad Switching & Terminal Es -# Transportation & Utilities -> Passenger Road Transportation -# Transportation & Utilities -> Road Freight Transportation -# Transportation & Utilities -> Road Freight Transportation -> Trucking & Courier Services (No -# Transportation & Utilities -> Road Freight Transportation -> Trucking (No Local) -# Transportation & Utilities -> Road Freight Transportation -> Public Warehousing & Storage -# Transportation & Utilities -> Road Freight Transportation -> Terminal Maintenance Facilities -# Transportation & Utilities -> USPS -# Transportation & Utilities -> Water Transportation -# Transportation & Utilities -> Water Transportation -> Water Transportation -# Transportation & Utilities -> Water Transportation -> Deep Sea Foreign Transportation -# Transportation & Utilities -> Air Transportation -# Transportation & Utilities -> Air Transportation -> Air Transportation, Scheduled -# Transportation & Utilities -> Air Transportation -> Air Courier Services -# Transportation & Utilities -> Air Transportation -> Air Transportation, Nonscheduled -# Transportation & Utilities -> Air Transportation -> Airports, Flying Fields & Airpor -# Transportation & Utilities -> Pipelines -# Transportation & Utilities -> Transportation Services -# Transportation & Utilities -> Transportation Services -> Transportation Services -# Transportation & Utilities -> Transportation Services -> Arrangement of Transportation of -# Transportation & Utilities -> Communications -# Transportation & Utilities -> Communications -> Radiotelephone Communications -# Transportation & Utilities -> Communications -> Telephone Communications (No Rad -# Transportation & Utilities -> Communications -> Telegraph & Other Message Commun -# Transportation & Utilities -> Communications -> Radio Broadcasting Stations -# Transportation & Utilities -> Communications -> Television Broadcasting Stations -# Transportation & Utilities -> Communications -> Cable & Other Pay Television Ser -# Transportation & Utilities -> Communications -> Communications Services -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric, Gas, & Sanitary Svcs -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric Services -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Transmission -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Transmission & Distr -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Distribution -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric & Other Services Combin -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Gas & Other Services Combined -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Water Supply -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Sanitary Services -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Refuse Systems -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Hazardous Waste Management -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Steam & Air-Conditioning Supply -# Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Cogeneration Services & Small Po - -# Wholesale Trade -# Wholesale Trade -> Durable Goods -> Durable Goods -# Wholesale Trade -> Durable Goods -> Motor Vehicles & Motor Vehicle P -# Wholesale Trade -> Durable Goods -> Motor Vehicles Supplies & New Par -# Wholesale Trade -> Durable Goods -> Furniture & Home Furnishings -# Wholesale Trade -> Durable Goods -> Lumber & Other Construction Mate -# Wholesale Trade -> Durable Goods -> Lumber, Plywood, Millwork & Wood -# Wholesale Trade -> Durable Goods -> Professional & Commercial Equipm -# Wholesale Trade -> Durable Goods -> Computers & Peripheral Equipment -# Wholesale Trade -> Durable Goods -> Medical, Dental & Hospital Equip -# Wholesale Trade -> Durable Goods -> Metals & Minerals (No Petroleum) -# Wholesale Trade -> Durable Goods -> Metals Services Centers & Offices -# Wholesale Trade -> Durable Goods -> Electrical Apparatus & Equipment -# Wholesale Trade -> Durable Goods -> Electrical Appliances, Tv & Radi -# Wholesale Trade -> Durable Goods -> Electronic Parts & Equipment -# Wholesale Trade -> Durable Goods -> Hardware & Plumbing & Heating Eq -# Wholesale Trade -> Durable Goods -> Hardware -# Wholesale Trade -> Durable Goods -> Machinery, Equipment & Supplies -# Wholesale Trade -> Durable Goods -> Construction & Mining (No Petro) -# Wholesale Trade -> Durable Goods -> Industrial Machinery & Equipment -# Wholesale Trade -> Durable Goods -> Misc Durable Goods -# Wholesale Trade -> Durable Goods -> Jewelry, Watches, Precious Stone -# Wholesale Trade -> Durable Goods -> Durable Goods -# Wholesale Trade -> Nondurable Goods -# Wholesale Trade -> Nondurable Goods -> Paper & Paper Products -# Wholesale Trade -> Nondurable Goods -> Drugs, Proprietaries & Druggists -# Wholesale Trade -> Nondurable Goods -> Apparel, Piece Goods & Notions -# Wholesale Trade -> Nondurable Goods -> Groceries & Related Products -# Wholesale Trade -> Nondurable Goods -> Groceries, General Line -# Wholesale Trade -> Nondurable Goods -> Farm Product Raw Materials -# Wholesale Trade -> Nondurable Goods -> Chemicals & Allied Products -# Wholesale Trade -> Nondurable Goods -> Petroleum Bulk Stations & Termin -# Wholesale Trade -> Nondurable Goods -> Petroleum & Petroleum Products ( -# Wholesale Trade -> Nondurable Goods -> Beer, Wine & Distilled Alcoholic -# Wholesale Trade -> Nondurable Goods -> Miscellaneous Nondurable Goods - -# Retail Trade -# Retail Trade -> Building Materials -# Retail Trade -> Building Materials -> Building Materials, Hardware, Ga -# Retail Trade -> Building Materials -> Lumber & Other Buildings Material -# Retail Trade -> Building Materials -> Mobile Home Dealers -# Retail Trade -> General Merchandise -# Retail Trade -> General Merchandise -> Department Stores -# Retail Trade -> General Merchandise -> Variety Stores -# Retail Trade -> General Merchandise -> Misc General Merchandise Stores -# Retail Trade -> Grocery -# Retail Trade -> Grocery -> Food Stores -# Retail Trade -> Grocery -> Grocery Stores -# Retail Trade -> Grocery -> Convenience Stores -# Retail Trade -> Auto Dealers & Service Stations -# Retail Trade -> Auto Dealers & Service Stations -> Auto Dealers & Gasoline Stations -# Retail Trade -> Auto Dealers & Service Stations -> Auto & Home Supply Stores -# Retail Trade -> Apparel & Accessory Stores -# Retail Trade -> Apparel & Accessory Stores -> Apparel & Accessory Stores -# Retail Trade -> Apparel & Accessory Stores -> Women’s Clothing Stores -# Retail Trade -> Apparel & Accessory Stores -> Family Clothing Stores -# Retail Trade -> Apparel & Accessory Stores -> Shoe Stores -# Retail Trade -> Home Furniture -# Retail Trade -> Home Furniture -> Home Furniture, Furnishings & Eq -# Retail Trade -> Home Furniture -> Furniture Stores -# Retail Trade -> Home Furniture -> Ratio, Tv & Consumer Electronics -# Retail Trade -> Home Furniture -> Computer & Computer Software Sto -# Retail Trade -> Home Furniture -> Record & Prerecord Tape Stores -# Retail Trade -> Eating & Drinking Places -# Retail Trade -> Eating & Drinking Places -> Eating & Drinking Places -# Retail Trade -> Eating & Drinking Places -> Eating Places -# Retail Trade -> Miscellaneous Retail -# Retail Trade -> Miscellaneous Retail -> Miscellaneous Retail -# Retail Trade -> Miscellaneous Retail -> Drug Stores & Proprietary Stores -# Retail Trade -> Miscellaneous Retail -> Miscellaneous Shopping Goods Sto -# Retail Trade -> Miscellaneous Retail -> Jewelry Stores -# Retail Trade -> Miscellaneous Retail -> Hobby, Toy & Game Shops -# Retail Trade -> Miscellaneous Retail -> Nonstore Retailers -# Retail Trade -> Miscellaneous Retail -> Catalog & Mail-Order Houses -# Retail Trade -> Miscellaneous Retail -> Retail Stores - -# Financial -# Financial -> Depository Institutions -# Financial -> Depository Institutions -> National Commercial Banks -# Financial -> Depository Institutions -> State Commercial Banks -# Financial -> Depository Institutions -> Commercial Banks -# Financial -> Depository Institutions -> Savings Institution, Federally C -# Financial -> Depository Institutions -> Savings Institution, Not Federa -# Financial -> Depository Institutions -> Functions Related to Despository -# Financial -> Non-depository Credit Inst -# Financial -> Non-depository Credit Inst -> Federal & Federally-Sponsored Cr -# Financial -> Non-depository Credit Inst -> Personal Credit Institutions -# Financial -> Non-depository Credit Inst -> Short-Term Business Credit Insti -# Financial -> Non-depository Credit Inst -> Miscellaneous Business Credit In -# Financial -> Non-depository Credit Inst -> Mortgage Bankers & Loan Correspo -# Financial -> Non-depository Credit Inst -> Loan Brokers -# Financial -> Non-depository Credit Inst -> Finance Lessors -# Financial -> Non-depository Credit Inst -> Asset-Backed Securities -# Financial -> Non-depository Credit Inst -> Finance Services -# Financial -> Security & Commodity Brokers -# Financial -> Security & Commodity Brokers -> Security & Commodity Brokers, De -# Financial -> Security & Commodity Brokers -> Security Brokers, Dealers & Flot -# Financial -> Security & Commodity Brokers -> Commodity Contracts Brokers & De -# Financial -> Security & Commodity Brokers -> Investment Advice -# Financial -> Insurance Carriers -# Financial -> Insurance Carriers -> Life Insurance -# Financial -> Insurance Carriers -> Accident & Health Insurance -# Financial -> Insurance Carriers -> Hospital & Medical Service Plans -# Financial -> Insurance Carriers -> Fire, Marine & Casualty Insuranc -# Financial -> Insurance Carriers -> Surety Insurance -# Financial -> Insurance Carriers -> Title Insurance -# Financial -> Insurance Carriers -> Insurance Carriers -# Financial -> Insurance Agents -# Financial -> Real Estate -# Financial -> Real Estate -> Real Estate -# Financial -> Real Estate -> Real Estate Operators (No Develo -# Financial -> Real Estate -> Operators of Nonresidential Buil -# Financial -> Real Estate -> Operators of Apartment Buildings -# Financial -> Real Estate -> Lessors of Real Property -# Financial -> Real Estate -> Real Estate Agents & Managers -# Financial -> Real Estate -> Real Estate Dealers (For Their O -# Financial -> Real Estate -> Land Subdividers & Developers (N -# Financial -> Holding & Investment Offices -# Financial -> Holding & Investment Offices -> Blank Checks -# Financial -> Holding & Investment Offices -> Oil Royalty Traders -# Financial -> Holding & Investment Offices -> Patent Owners & Lessors -# Financial -> Holding & Investment Offices -> Mineral Royalty Traders -# Financial -> Holding & Investment Offices -> Real Estate Investment Trusts -# Financial -> Holding & Investment Offices -> Investors - -# Services -# Services -> Hotels -# Services -> Hotels -> Hotels, Rooming Houses, Camps & -# Services -> Hotels -> Hotels & Motels -# Services -> Personal Services -# Services -> Business Services -# Services -> Business Services -> Advertising -# Services -> Business Services -> Advertising Agencies -# Services -> Business Services -> Consumer Credit Reporting, Colle -# Services -> Business Services -> Mailing, Reproduction, Commercia -# Services -> Business Services -> Direct Mail Advertising Services -# Services -> Business Services -> To Dwellings & Other Buildings -# Services -> Business Services -> Miscellaneous Equipment Rental & -# Services -> Business Services -> Equipment Rental & Leasing -# Services -> Business Services -> Employment Agencies -# Services -> Business Services -> Help Supply Services -# Services -> Business Services -> Computer Programming, Data Proce -# Services -> Business Services -> Computer Programming Services -# Services -> Business Services -> Prepackaged Software -# Services -> Business Services -> Computer Integrated Systems Desi -# Services -> Business Services -> Computer Processing & Data Prepa -# Services -> Business Services -> Computer Rental & Leasing -# Services -> Business Services -> Miscellaneous Business Services -# Services -> Business Services -> Detective, Guard & Armored Car S -# Services -> Business Services -> Photofinishing Laboratories -# Services -> Business Services -> Telephone Interconnect Systems -# Services -> Business Services -> Business Services -# Services -> Automotive Repair -# Services -> Automotive Repair -> Automotive Repair, Services & Pa -# Services -> Automotive Repair -> Auto Rental & Leasing (No Driver -# Services -> Miscellaneous Repair -# Services -> Motion Pictures -# Services -> Motion Pictures -> Motion Picture & Video Tape Prod -# Services -> Motion Pictures -> Allied to Motion Picture Product -# Services -> Motion Pictures -> Motion Picture & Video Tape Dist -# Services -> Motion Pictures -> Allied to Motion Picture Distrib -# Services -> Motion Pictures -> Motion Picture Theaters -# Services -> Motion Pictures -> Video Tape Rental -# Services -> Amusement & Rec Svcs -# Services -> Amusement & Rec Svcs -> Amusement Recreation Services -# Services -> Amusement & Rec Svcs -> Racing, Including Track Operatio -# Services -> Amusement & Rec Svcs -> Miscellaneous Amusement & Recrea -# Services -> Amusement & Rec Svcs -> Membership Sports & Recreation C -# Services -> Health Services -# Services -> Health Services -> Health Services -# Services -> Health Services -> Offices & Clinics of Doctors of -# Services -> Health Services -> Nursing & Personal Care Faciliti -# Services -> Health Services -> Skilled Nursing Care Facilities -# Services -> Health Services -> Hospitals -# Services -> Health Services -> General Medical & Surgical Hospi -# Services -> Health Services -> Medical Laboratories -# Services -> Health Services -> Home Health Care Services -# Services -> Health Services -> Misc Health & Allied Services -# Services -> Health Services -> Specialty Outpatient Facilities -# Services -> Legal Services -# Services -> Educational Services -# Services -> Social Services -# Services -> Social Services -> Social Services -# Services -> Social Services -> Child Day Care Services -# Services -> Museums -# Services -> Membership Organizations -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Engineering, Accounting, Research -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Engineering Services -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Commercial Physical & Biological -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Testing Laboratories -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Management Services -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Management Consulting Services -# Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Facilities Support Management Se -# Services -> Miscellaneous Services - -# Closed-End Funds diff --git a/openbb_terminal/miscellaneous/stocks/screener/5pct_above_low.ini b/openbb_terminal/miscellaneous/stocks/screener/5pct_above_low.ini deleted file mode 100755 index a48a47d4ea8..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/5pct_above_low.ini +++ /dev/null @@ -1,18 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: 5% Above Low - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Exchange = NASDAQ -Current Volume = Over 300K -Country = USA -# Price = Custom (Elite only) - -[Fundamental] - - -[Technical] -20-Day High/Low = 5% or more above Low diff --git a/openbb_terminal/miscellaneous/stocks/screener/all_parameters.txt b/openbb_terminal/miscellaneous/stocks/screener/all_parameters.txt deleted file mode 100644 index 7d60fc68e4d..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/all_parameters.txt +++ /dev/null @@ -1,212 +0,0 @@ -# Author of preset: OpenBBTerminal (https://github.com/OpenBB-finance/OpenBBTerminal) -# Description: Template with all available filters and their options menu. -# More information can be found in https://finviz.com/help/screener.ashx -# and https://finviz.com/help/technical-analysis/charts-patterns.ashx - -[General] -# Ticker, Company, Sector, Industry, Country, Market Cap., Price/Earnings, Forward Price/Earnings, PEG (Price/Earnings/Growth), Price/Sales, Price/Book, Price/Cash, Price/Free Cash Flow, Dividend Yield, Payout Ratio, EPS (ttm), EPS growth this year, EPS growth next year, EPS growth past 5 years, EPS growth next 5 years, Sales growth past 5 years, EPS growth qtr over qtr, Sales growth qtr over qtr, Shares Outstanding, Shares Float, Insider Ownership, Insider Transactions, Institutional Ownership, Institutional Transactions, Short Interest Share, Short Interest Ratio, Earnings Date, Return on Assets, Return on Equity, Return on Investment, Current Ratio, Quick Ratio, LT Debt/Equity, Total Debt/Equity, Gross Margin, Operating Margin, Net Profit Margin, Analyst Recommendation, Performance (Week), Performance (Month), Performance (Quarter), Performance (Half Year), Performance (Year), Performance (Year To Date), Beta, Average True Range, Volatility (Week), Volatility (Month), 20-Day SMA (Relative), 50-Day SMA (Relative), 200-Day SMA (Relative), 50-Day High (Relative), 50-Day Low (Relative), 52-Week High (Relative), 52-Week Low (Relative), Relative Strength Index (14), Average Volume (3 Month), Relative Volume, Change, Change from Open, Gap, Volume, Price, Target Price, IPO Date -Order = Ticker - -# None (all stocks), Top Gainers, Top Losers, New High, New Low, Most Volatile, Most Active, Unusual Volume, Overbought, Oversold, Downgrades, Upgrades, Earnings Before, Earnings After, Recent Insider Buying, Recent Insider Selling, Major News, Horizontal S/R, TL Resistance, TL Support, Wedge Up, Wedge Down, Triangle Ascending, Triangle Descending, Wedge, Channel Up, Channel Down, Channel, Double Top, Double Bottom, Multiple Top, Multiple Bottom, Head & Shoulders, Head & Shoulders Inverse -Signal = Top Gainers - - -[Descriptive] -# Any, AMEX, NASDAQ, NYSE -Exchange = Any - -# Any, Mega ($200bln and more), Large ($10bln to $200bln), Mid ($2bl to $10bln), Small ($300mln to $2bln), Micro ($50mln to $300mln), Nano (under $50mln), +Large (over $50mln), +Mid (over $2bln), +Small (over $300mln), +Micro (over $50mln), -Large (under $200bln), -Mid (under $10bln), -Small (under $2bln), -Micro (under $300mln) -Market Cap. = Any - -# Any, Today, Today Before Market Open, Today Before Market Close, Tomorrow, Tomorrow Before Market Open, Tomorrow Before Market Close, Yesterday, Yesterday Before Market Open, Yesterday Before Market Close, Next 5 Days, Previous 5 Days, This Week, Next Week, Previous Week, This Month -Earnings Date = Any - -# Any, 50% Above Price, 40% Above Price, 30% Above Price, 20% Above Price, 10% Above Price, 5% Above Price, Above Price, Below Price, 5% Below Price, 10% Below Price, 20% Below Price, 30% Below Price, 40% Below Price, 50% Below Price -Target Price = Any - -# Any, S&P 500, DJIA -Index = Any - -# Any, None (0%), Positive (>0%), High (>5%), Very High (>10%), Over 1%, Over 2%, Over 3%, Over 4%, Over 5%, Over 6%, Over 7%, Over 8%, Over 9%, Over 10% -Dividend Yield = Any - -# Any, Under 50K, Under 100K, Under 500K, Under 750K, Under 1M, Over 50K, Over 100K, Over 300K, Over 400K, Over 500K, Over 750K, Over 1M, Over 2M, 100K to 500K, 100K to 1M, 500K to 1M, 500K to 10M -Average Volume = Any - -# Any, Today, Yesterday, In the last week, In the last month, In the last quarter, In the last year, In the last 2 years, In the last 3 years, In the last 5 years, More than a year ago, More that 5 years ago, More than 10 years ago, More than 15 years ago, More than 20 years ago, More than 25 years ago -IPO Date = Any - -# Any, Basic Materials, Communication Services, Consumer Cyclical, Consumer Defensive, Energy, Financial, Healthcare, Industrials, Real Estate, Technology, Utilities -Sector = Any - -# Any, Low (<5%), High (>20%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30% -Float Short = Any - -# Any, Over 10, Over 5, Over 3, Over 2, Over 1.5, Over 1, Over 0.75, Over 0.5, Over 0.25, Under 2, Under 1, Under 1.5, Under 1, Under 0.75, Under 0.5, Under 0.25, Under 0.1 -Relative Volume = Any - -# Any, Under 1M, Under 5M, Under 10M, Under 20M, Under 50M, Under 100M, Over 1M, Over 2M, Over 5M, Over 10M, Over 20M, Over 50M, Over 100M, Over 200M, Over 500M, Over 1000M -Shares Outstanding = Any - -# Any, Agricultural Inputs, Aluminum, Building Materials, Chemicals, Coking Coal, Copper, Gold, Lumber & Wood Production, Other Industrial Metals & Mining, Other Precious Metals & Mining, Paper & Paper Products, Silver, Specialty Chemicals, Steel -Industry = Any - -# Any, Strong Buy (1), Buy or Better, Buy, Hold or better, Hold, Hold or worse, Sell, Sell or worse, Strong Sell (5) -Analyst Recom. = Any - -# Any, Under 100K, Under 500K, Under 750K, Under 1M, Over 0, Over 50K, Over 100K, Over 200K, Over 300K, Over 400K, Over 500K, Over 750K, Over 1M, Over 2M, Over 5M, Over 10M, Over 20M -Current Volume = Any - -# Any, Under 1M, Under 5M, Under 10M, Under 20M, Under 50M, Under 100M, Over 1M, Over 2M, Over 5M, Over 10M, Over 20M, Over 50M, Over 100M, Over 200M, Over 500M, Over 1000M -Float = Any - -# Any, Asia, Europe, Latin America, BRIC, Argentina, Australia, Bahamas, Belgium, BeNeLux, Bermuda, Brazil, Canada, Cayman Islands, Chile, China, China & Hong Kong, Colombia, Cyprus, Denmark, Finland, France, Germany, Greece, Hong Kong, Hungary, Iceland, India, Indonesia, Ireland, Israel, Italy, Japan, Kazakhstan, Luxembourg, Malaysia, Malta, Mexico, Monaco, Netherlands, New Zealand, Norway, Panama, Peru, Philippines, Portugal, Russia, Singapore, South Africa, South Korea, Spain, Sweden, Switzerland, Taiwan, Turkey, United Arab Emirates, United Kingdom, Uruguay -Country = Any - -# Any, Optionable, Shortable, Optionable and shortable -Option/Short = Any - -# Any, Under $1, Under $2, Under $3, Under $4, Under $5, Under $7, Under $10, Under $15, Under $20, Under $30, Under $40, Under $50, Over $1, Over $2, Over $3, Over $4, Over $5 ,Over $7, Over $10, Over $15, Over $20, Over $30, Over $40, Over $50, Over $60, Over $70, Over $80, Over $90, Over $100, $1 to $5, $1 to $10, $1 to $20, $5 to $10, $5 to $20, $5 to $50, $10 to $20, $10 to $50, $20 to $50, $50 to $100 -Price = Any - - -[Fundamental] -# Any, Low (<15), Profitable (>0), High (>50), Under 5, Under 10, Under 15, Under 20, Under 25, Under 30, Under 35, Under 40, Under 45, Under 50, Over 5, Over 10, Over 15, Over 25, Over 30, Over 35, Over 40, Over 45, Over 50 -P/E = Any - -# Any, Low (<3), High (>50), Under 1, Under 2, Under 3, Under 4, Under 5, Under 6, Under 7, Under 8, Under 9, Under 10, Over 1, Over 2, Over 3, Over 4, Over 5, Over 6, Over 7, Over 8, Over 9, Over 10, Over 20, Over 30, Over 40, Over 50 -Price/Cash = Any - -# Any, Negative (<0%), Positive (>0%), Positive Low (<10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30% -EPS growthnext 5 years = Any - -# Any, Positive (>0%), Negative (<0%), Very Positive (>30%), Very Negative (<-15%), Under -50%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +50%, Over +45%, Over +40%, Over +35%, Over +30%, Over +25%, Over +20%, Over +15%, Over +10%, Over +5% -Return on Equity = Any - -# Any, High (>0.5), Low (<0.1), Under 1, Under 0.9, Under 0.8, Under 0.7, Under 0.6, Under 0.5, Under 0.4, Under 0.3, Under 0.2, Under 0.1, Over 0.1, Over 0.2, Over 0.3, Over 0.4, Over 0.5, Over 0.6, Over 0.7, Over 0.8, Over 0.9, Over 1 -Debt/Equity = Any - -# Any, Low (<5%), High (>30%), Very High (>50%), Over 10%, Over 20%, Over 30%, Over 40%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90% -InsiderOwnership = Any - -# Any, Low (<15), Profitable (>0), High (>50), Under 5, Under 10, Under 15, Under 20, Under 25, Under 30, Under 35, Under 40, Under 45, Under 50, Over 5, Over 10, Over 15, Over 20, Over 25, Over 30, Over 35, Over 40, Over 45, Over 50 -Forward P/E = Any - -# Any, Low (<15), High (>50), Under 5, Under 10, Under 15, Under 20, Under 25, Under 30, Under 35, Under 40, Under 45, Under 50, Under 60, Under 70, Under 80, Under 90, Under 100, Over 5, Over 10, Over 15, Over 20, Over 25, Over 30, Over 35, Over 40, Over 45, Over 50, Over 60, Over 70, Over 80, Over 90, Over 100 -Price/Free Cash Flow = Any - -# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30% -Sales growthpast 5 years = Any - -# Any, Positive (>0%), Negative (<0%), Very Positive (>25%), Very Negative (<-10%), Under -50%, Under -45%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +5%, Over +10%, Over +15%, Over +20%, Over +25%, Over +30%, Over +35%, Over +40%, Over +45%, Over +50% -Return on Investment = Any - -# Any, Positive (>0%), Negative (<0%), High (>50%), Under 90%, Under 80%, Under 70%, Under 60%, Under 50%, Under 45%, Under 40%, Under 35%, Under 30%, Under 25%, Under 20%, Under 15%, Under 10%, Under 5%, Under 0%, Under -10%, Under -20%, Under -30%, Under -50%, Under -70%, Under -100%, Over 0%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%, Over 40%, Over 45%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90% -Gross Margin = Any - -# Any, Very Negative (<20%), Negative (<0%), Positive (>0%), Very Positive (>20%), Under -90%, Under -80%, Under -70%, Under -60%, Under -50%, Under -45%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +5%, Over +10%, Over +15%, Over +20%, Over +25%, Over +30%, Over +35%, Over +40%, Over +45%, Over +50%, Over +60%, Over +70%, Over +80%, Over +90% -InsiderTransactions = Any - -# Any, Low (<1), High (>2), Under 1, Under 2, Under 3, Over 1, Over 2, Over 3 -PEG = Any - -# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30% -EPS growththis year = Any - -# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30% -EPS growthqtr over qtr = Any - -# Any, High (>3), Low (<1), Under 1, Under 0.5, Over 0.5, Over 1, Over 1.5, Over 2, Over 3, Over 4, Over 5, Over 10 -Current Ratio = Any - -# Any, Negative (<0%), Positive (>0%), Very Negative (<-20%), High (>25%), Under 90%, Under 80%, Under 70%, Under 60%, Under 50%, Under 45%, Under 40%, Under 35%, Under 30%, Under 25%, Under 20%, Under 15%, Under 10%, Under 5%, Under 0%, Under -10%, Under -20%, Under -30%, Under -50%, Under -70%, Under -100%, Over 0%, Over 10%, Under 15%, Over 20%, Over 25%, Over 30%, Over 35%, Over 40%, Over 45%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90% -Operating Margin = Any - -# Any, Low (<5%), High (>90%), Under 90%, Under 80%, Under 70%, Under 60%, Under 50%, Under 40%, Under 30%, Under 20%, Under 10%, Over 10%, Over 20%, Over 30%, Over 40%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90% -InstitutionalOwnership = Any - -# Any, Low (<1), High (>10), Under 1, Under 2, Under 3, Under 4, Under 5, Under 6, Under 7, Under 8, Under 9, Under 10, Over 1, Over 2, Over 3, Over 4, Over 5, Over 6, Over 6, Over 7, Over 8, Over 9, Over 10 -P/S = Any - -# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30% -EPS growthnext year = Any - -# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30% -Sales growthqtr over qtr = Any - -# Any, High (>3), Low (<0.5), Under 1, Under 0.5, Over 0.5, Over 1, Over 1.5, Over 2, Over 3, Over 4, Over 5, Over 10 -Quick Ratio = Any - -# Any, Positive (>0%), Negative (<0%), Very Negative (<-20%), High (>20%), Under 90%, Under 80%, Under 70%, Under 60%, Under 50%, Under 45%, Under 40%, Under 35%, Under 30%, Under 25%, Under 20%, Under 15%, Under 10%, Under 5%, Under 0%, Under -10%, Under -20%, Under -30%, Under -50%, Under -70%, Under -100%, Over 0%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30%, Over 35%, Over 40%, Over 45%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90% -Net Profit Margin = Any - -# Any, Very Negative (<20%), Negative (<0%), Positive (>0%), Very Positive (>20%), Under -50%, Under -45%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +5%, Over +10%, Over +15%, Over +20%, Over +25%, Over +30%, Over +35%, Over +40%, Over +45%, Over +50% -InstitutionalTransactions = Any - -# Any, Low (<1), High (>5), Under 1, Under 2, Under 3, Under 4, Under 5, Under 6, Under 7, Under 8, Under 9, Under 10, Over 1, Over 2, Over 3, Over 4, Over 5, Over 6, Over 7, Over 8, Over 9, Over 10 -P/B = Any - -# Any, Negative (<0%), Positive (>0%), Positive Low (0-10%), High (>25%), Under 5%, Under 10%, Under 15%, Under 20%, Under 25%, Under 30%, Over 5%, Over 10%, Over 15%, Over 20%, Over 25%, Over 30% -EPS growthpast 5 years = Any - -# Any, Positive (>0%), Negative (<0%), Very Positive (>15%), Very Negative (<-15%), Under -50%, Under -45%, Under -40%, Under -35%, Under -30%, Under -25%, Under -20%, Under -15%, Under -10%, Under -5%, Over +5%, Over +10%, Over +15%, Over +20%, Over +25%, Over +30%, Over +35%, Over +40%, Over +45%, Over +50% -Return on Assets = Any - -# Any, High (>0.5), Low (<0.1), Under 1, Under 0.9, Under 0.8, Under 0.7, Under 0.6, Under 0.5, Under 0.4, Under 0.3, Under 0.2, Under 0.1, Over 0.1, Over 0.2, Over 0.3, Over 0.4, Over.5, Over 0.6, Over 0.7, Over 0.8, Over 0.9, Over 1 -LT Debt/Equity = Any - -# Any, None (0%), Positive (>0%), Low (<20%), High (>50%), Over 0%, Over 10%, Over 20%, Over 30%, Over 40%, Over 50%, Over 60%, Over 70%, Over 80%, Over 90%, Over 100%, Under 10%, Under 20%, Under 30%, Under 40%, Under 50%, Under 60%, Under 70%, Under 80%, Under 90%, Under 100% -Payout Ratio = Any - - -[Technical] -# Any, Today Up, Today Down, Today -15%, Today -10%, Today -5%, Today +5%, Today +10%, Today +15%, Week -30%, Week -20%, Week -10%, Week Down, Week Up, Week +10%, Week +20%, Week +30%, Month -50%, Month -30%, Month -20%, Month -10%, Month Down, Month Up, Month +10%, Month +20%, Month +30%, Month +50%, Quarter -50%, Quarter -30%, Quarter -20%, Quarter -10%, Quarter Down, Quarter Up, Quarter +10%, Quarter +20%, Quarter +30%, Quarter +50%, Half -75%, Half -50%, Half -30%, Half -20%, Half -10%, Half Down, Half Up, Half +10%, Half +20%, Half +30%, Half +50%, Half +100%, Year -75%, Year -50%, Year -30%, Year -20%, Year -10%, Year Down, Year Up, Year +10%, Year +20%, Year +30%, Year +50%, Year +100%, Year +200%, Year +300%, Year +500%, YTD -75%, YTD -50%, YTD -30%, YTD -20%, YTD -10%, YTD -5%, YTD Down, YTD Up, YTD +5%, YTD +10%, YTD +20%, YTD +30, YTD +50%, YTD +100% -Performance = Any - -# Any, Price below SMA20, Price 10% below SMA20, Price 20% below SMA20, Price 30% below SMA20, Price 40% below SMA20, Price 50% below SMA20, Price above SMA20, Price 10% above SMA20, Price 20% above SMA20, Price 30% above SMA20, Price 40% above SMA20, Price 50% above SMA20, Price crossed SMA20, Price crossed SMA20 above, Price crossed SMA20 below, SMA20 crossed SMA50, SMA20 crossed SMA50 above, SMA20 crossed SMA50 below, SMA20 cross SMA200, SMA20 crossed SMA200 below, SMA20 crossed SMA200 above, SMA20 above SMA50, SMA20 below SMA50, SMA20 above SMA200, SMA20 below SMA200 -20-Day Simple Moving Average = Any - -# Any, New High, New Low, 5% or more below High, 10% or more below High, 15% or more below High, 20% or more below High, 30% or more below High, 40% or more below High, 50% or more below High, 0-3% below High, 0-5% below High, 0-10% below High, 5% or more above Low, 10% or more above Low, 15% or more above Low, 20% or more above Low, 30% or more above Low, 40% or more above Low, 50% or more above Low, 0-3% above Low, 0-5% above Low, 0-10% above Low -20-Day High/Low = Any - -# Any, Under 0, Under 0.5, Under 1, Under 1.5, Under 2, Over 0, Over 0.5, Over 1, Over 1.5, Over 2, Over 2.5, Over 3, Over 4, 0 to 0.5, 0 to 1, 0.5 to 1, 0.5 to 1.5, 1 to 1.5, 1 to 2 -Beta = Any - -# Any, Today Up, Today Down, Today -15%, Today -10%, Today -5%, Today +5%, Today +10%, Today +15%, Week -30%, Week -20%, Week -10%, Week Down, Week Up, Week +10%, Week +20%, Week +30%, Month -50%, Month -30%, Month -20%, Month -10%, Month Down, Month Up, Month +10%, Month +20%, Month +30%, Month +50%, Quarter -50%, Quarter -30%, Quarter -20%, Quarter -10%, Quarter Down, Quarter Up, Quarter +10%, Quarter +20%, Quarter +30%, Quarter +50%, Half -75%, Half -50%, Half -30%, Half -20%, Half -10%, Half Down, Half Up, Half +10%, Half +20%, Half +30%, Half +50%, Half +100%, Year -75%, Year -50%, Year -30%, Year -20%, Year -10%, Year Down, Year Up, Year +10%, Year +20%, Year +30%, Year +50%, Year +100%, Year +200%, Year +300%, Year +500%, YTD -75%, YTD -50%, YTD -30%, YTD -20%, YTD -10%, YTD -5%, YTD Down, YTD Up, YTD +5%, YTD +10%, YTD +20%, YTD +30, YTD +50%, YTD +100% -Performance 2 = Any - -# Any, Price below SMA50, Price 10% below SMA50, Price 20% below SMA50, Price 30% below SMA50, Price 40% below SMA50, Price 50% below SMA50, Price above SMA50, Price 10% above SMA50, Price 20% above SMA50, Price 30% above SMA50, Price 40% above SMA50, Price 50% above SMA50, Price crossed SMA50, Price crossed SMA50 above, Price crossed SMA50 below, SMA50 crossed SMA20, SMA50 crossed SMA20 above, SMA50 crossed SMA20 below, SMA50 cross SMA200, SMA50 crossed SMA200 below, SMA50 crossed SMA200 above, SMA50 above SMA20, SMA50 below SMA20, SMA50 above SMA200, SMA50 below SMA200 -50-Day Simple Moving Average = Any - -# Any, New High, New Low, 5% or more below High, 10% or more below High, 15% or more below High, 20% or more below High, 30% or more below High, 40% or more below High, 50% or more below High, 0-3% below High, 0-5% below High, 0-10% below High, 5% or more above Low, 10% or more above Low, 15% or more above Low, 20% or more above Low, 30% or more above Low, 40% or more above Low, 50% or more above Low, 0-3% above Low, 0-5% above Low, 0-10% above Low -50-Day High/Low = Any - -# Any, Over 0.25, Over 0.5, Over 0.75, Over 1, Over 1.5, Over 2. Over 2.5, Over 3, Over 3.5, Over 4, Over 4.5, Over 5, Under 0.25, Under 0.5, Under 0.75, Under 1, Under 1.5, Under 2, Under 2.5, Under 3, Under 3.5, Under 4, Under 4.5, Under 5 -Average True Range = Any - -# Any, Week - Over 3%, Week - Over 4%, Week - Over 5%, Week - Over 6%, Week - Over 7%, Week - Over 8%, Week - Over 9%, Week - Over 10%, Week - Over 12%, Week - Over 15%, Month - Over2%, Month - Over3%, Month - Over4%, Month - Over 5%, Month - Over 5%, Month - Over 6%, Month - Over 7%, Month - Over 8%, Month - Over 9%, Month - Over 10%, Month - Over 12%, Month - Over 15% -Volatility = Any - -# Any, Price below SMA200, Price 10% below SMA200, Price 20% below SMA200, Price 30% below SMA200, Price 40% below SMA200, Price 50% below SMA200, Price above SMA200, Price 10% above SMA200, Price 20% above SMA200, Price 30% above SMA200, Price 40% above SMA200, Price 50% above SMA200, Price crossed SMA200, Price crossed SMA200 above, Price crossed SMA200 below, SMA200 crossed SMA20, SMA20 crossed SMA20 above, SMA20 crossed SMA20 below, SMA200 cross SMA50, SMA200 crossed SMA50 below, SMA200 crossed SMA50 above, SMA200 above SMA20, SMA200 below SMA20, SMA200 above SMA50, SMA200 below SMA50 -200-Day Simple Moving Average = Any - -# Any, New High, New Low, 5% or more below High, 10% or more below High, 15% or more below High, 20% or more below High, 30% or more below High, 40% or more below High, 50% or more below High, 0-3% below High, 0-5% below High, 0-10% below High, 5% or more above Low, 10% or more above Low, 15% or more above Low, 20% or more above Low, 30% or more above Low, 40% or more above Low, 50% or more above Low, 0-3% above Low, 0-5% above Low, 0-10% above Low -52-Week High/Low = Any - -# Any, Overbought (90), Overbought (80), Overbought (70), Overbought (60), Oversold (40), Oversold (30), Oversold (20), Oversold (10), Not Overbought (<60), Not Overbought (<50), Not Oversold (>50), Not Oversold (>40) -RSI (14) = Any - -# Any, Up, Up 1%, Up 2%, Up 3%, Up 4%, Up 5%, Up 6%, Up 7%, Up 8%, Up 9%, Up 10%, Up 15%, Up 20%, Down, Down 1%, Down 2%, Down 3%, Down 4%, Down 5%, Down 6%, Down 7%, Down 8%, Down 9%, Down 10%, Down 15%, Down 20% -Change = Any - -# Any, Horizontal S/R, Horizontal S/R (Strong), TL Resistance, TL Resistance (Strong), TL Support, TL Support (Strong), Wedge Up, Wedge Up (Strong), Wedge Down, Wedge Down (Strong), Triangle Ascending, Triangle Ascending (Strong), Triangle Descending, Triangle Descending (Strong), Wedge, Wedge (Strong), Channel Up, Channel Up (Strong), Channel Down, Channel Down (Strong), Channel, Channel (Strong), Double Top, Double Bottom, Multiple Top, Multiple Bottom, Head & Shoulders, Head & Shoulders Inverse -Pattern = Any - -# Any, Up, Up 1%, Up 2%, Up 3%, Up 4%, Up 5%, Up 6%, Up 7%, Up 8%, Up 9%, Up 10%, Up 15%, Up 20%, Down, Down 1%, Down 2%, Down 3%, Down 4%, Down 5%, Down 6%, Down 7%, Down 8%, Down 9%, Down 10%, Down 15%, Down 20% -Gap = Any - -# Any, Up, Up 1%, Up 2%, Up 3%, Up 4%, Up 5%, Up 6%, Up 7%, Up 8%, Up 9%, Up 10%, Up 15%, Up 20%, Down, Down 1%, Down 2%, Down 3%, Down 4%, Down 5%, Down 6%, Down 7%, Down 8%, Down 9%, Down 10%, Down 15%, Down 20% -Change from Open = Any - -# Any, Long Lower Shadow, Long Upper Shadow, Hammer, Inverted Hammer, Spinning Top White, Spinning Top Black, Doji, Dragonfly Doji, Gravestone Doji, Marubozu White, Marubozu Black -Candlestick = Any diff --git a/openbb_terminal/miscellaneous/stocks/screener/analyst_strong_buy.ini b/openbb_terminal/miscellaneous/stocks/screener/analyst_strong_buy.ini deleted file mode 100755 index 494d5909ae2..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/analyst_strong_buy.ini +++ /dev/null @@ -1,16 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Analyst Strong Buy - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Analyst Recom. = Strong Buy (1) - -[Fundamental] -Debt/Equity = Under 0.6 - -[Technical] -50-Day Simple Moving Average = Price above SMA50 -200-Day Simple Moving Average = Price above SMA200 diff --git a/openbb_terminal/miscellaneous/stocks/screener/break_out_stocks.ini b/openbb_terminal/miscellaneous/stocks/screener/break_out_stocks.ini deleted file mode 100755 index ced338a55c8..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/break_out_stocks.ini +++ /dev/null @@ -1,21 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Break out Stocks - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Exchange = NASDAQ -Average Volume = Over 100K - -[Fundamental] -Return on Equity = Over +20% -Debt/Equity = Under 1 - -[Technical] -20-Day Simple Moving Average = Price above SMA20 -50-Day Simple Moving Average = Price above SMA50 -50-Day High/Low = New High -200-Day Simple Moving Average = Price above SMA200 -52-Week High/Low = 0-5% below High diff --git a/openbb_terminal/miscellaneous/stocks/screener/buffett_like.ini b/openbb_terminal/miscellaneous/stocks/screener/buffett_like.ini deleted file mode 100644 index 6b61c5bd18b..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/buffett_like.ini +++ /dev/null @@ -1,18 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Buffet like value screener (Value invsting for long term growth) - -[General] - -[Descriptive] -Market Cap. = +Mid (over $2bln) -Dividend Yield = Positive (>0%) - -[Fundamental] -EPS growthnext 5 years = Positive (>0%) -Debt/Equity = Under 0.5 -Price/Free Cash Flow = Under 50 -Sales growthpast 5 years = Positive (>0%) -Return on Investment = Over +15% -P/B = Under 3 - -[Technical] diff --git a/openbb_terminal/miscellaneous/stocks/screener/bull_runs_over_10pct.ini b/openbb_terminal/miscellaneous/stocks/screener/bull_runs_over_10pct.ini deleted file mode 100755 index 58c854dd975..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/bull_runs_over_10pct.ini +++ /dev/null @@ -1,13 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Bull runs over 10% - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] - -[Fundamental] - -[Technical] -Performance = Today +10% diff --git a/openbb_terminal/miscellaneous/stocks/screener/channel_up_and_low_debt_and_sma_50and200.ini b/openbb_terminal/miscellaneous/stocks/screener/channel_up_and_low_debt_and_sma_50and200.ini deleted file mode 100755 index 719f50dc3fe..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/channel_up_and_low_debt_and_sma_50and200.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: (Channel Up, Low Debt, Above 50 & 200 SMA) - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 100K - -[Fundamental] -Debt/Equity = Under 0.5 - -[Technical] -50-Day Simple Moving Average = Price above SMA50 -200-Day Simple Moving Average = Price above SMA200 -Pattern = Channel Up (Strong) diff --git a/openbb_terminal/miscellaneous/stocks/screener/cheap_bottom_dividend.ini b/openbb_terminal/miscellaneous/stocks/screener/cheap_bottom_dividend.ini deleted file mode 100644 index 9bf602c4d74..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/cheap_bottom_dividend.ini +++ /dev/null @@ -1,13 +0,0 @@ -# Author of preset: Traceabl3 -# Description: High Yield Dividend stonks that are at-or-near their lowest price. Inverse Head and shoulders pattern recognized. - -[General] - -[Descriptive] -Dividend Yield = High (>5%) - -[Fundamental] - -[Technical] -20-Day Simple Moving Average = SMA20 below SMA200 -Pattern = Head & Shoulders Inverse diff --git a/openbb_terminal/miscellaneous/stocks/screener/cheap_dividend.ini b/openbb_terminal/miscellaneous/stocks/screener/cheap_dividend.ini deleted file mode 100644 index adf306a7d91..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/cheap_dividend.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Cheap dividend stocks - -[General] -Signal = Oversold - -[Descriptive] -Dividend Yield = Over 1% - -[Fundamental] -P/E = Under 15 -P/B = Under 1 - -[Technical] diff --git a/openbb_terminal/miscellaneous/stocks/screener/cheap_oversold.ini b/openbb_terminal/miscellaneous/stocks/screener/cheap_oversold.ini deleted file mode 100644 index bb1710ae820..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/cheap_oversold.ini +++ /dev/null @@ -1,12 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Cheap stonks that are oversold: under 10% above the low, and oversold on the RSI. - -[General] - -[Descriptive] - -[Fundamental] - -[Technical] -52-Week High/Low = 0-10% above Low -RSI (14) = Oversold (20) diff --git a/openbb_terminal/miscellaneous/stocks/screener/continued_momentum_scan.ini b/openbb_terminal/miscellaneous/stocks/screener/continued_momentum_scan.ini deleted file mode 100755 index 0d5f1c2dbf1..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/continued_momentum_scan.ini +++ /dev/null @@ -1,20 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Continued Momentum Scan - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Market Cap. = -Mid (under $10bln) -Average Volume = Over 50K -Relative Volume = Over 2 -Current Volume = Over 1M -Price = Under $30 - -[Fundamental] - -[Technical] - -Beta = Over 1 -Volatility = Week - Over 3% diff --git a/openbb_terminal/miscellaneous/stocks/screener/death_cross.ini b/openbb_terminal/miscellaneous/stocks/screener/death_cross.ini deleted file mode 100644 index fc7a15c2a7a..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/death_cross.ini +++ /dev/null @@ -1,11 +0,0 @@ -# Author of preset: Traceabl3 -# Description: When the 50 sma crosses below the 200 sma. More information can be found in https://www.investopedia.com/terms/d/deathcross.asp - -[General] - -[Descriptive] - -[Fundamental] - -[Technical] -200-Day Simple Moving Average = SMA200 crossed SMA50 above diff --git a/openbb_terminal/miscellaneous/stocks/screener/djia_components.ini b/openbb_terminal/miscellaneous/stocks/screener/djia_components.ini deleted file mode 100755 index a4ebb54a051..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/djia_components.ini +++ /dev/null @@ -1,13 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the Dow Jones Industrial Average components. - -[General] - -[Descriptive] - -Index = DJIA - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/golden_cross.ini b/openbb_terminal/miscellaneous/stocks/screener/golden_cross.ini deleted file mode 100644 index bcb63b3a39c..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/golden_cross.ini +++ /dev/null @@ -1,11 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Golden Cross when the 50 day moves above the200 day from below. - -[General] - -[Descriptive] - -[Fundamental] - -[Technical] -50-Day Simple Moving Average = SMA50 crossed SMA200 above diff --git a/openbb_terminal/miscellaneous/stocks/screener/golden_cross_penny.ini b/openbb_terminal/miscellaneous/stocks/screener/golden_cross_penny.ini deleted file mode 100755 index ce275c4cb32..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/golden_cross_penny.ini +++ /dev/null @@ -1,16 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Golden Cross - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 50K -Price = Under $5 - -[Fundamental] - - -[Technical] -50-Day Simple Moving Average = SMA50 crossed SMA200 above diff --git a/openbb_terminal/miscellaneous/stocks/screener/growth_stocks.ini b/openbb_terminal/miscellaneous/stocks/screener/growth_stocks.ini deleted file mode 100755 index 5b410a33fa4..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/growth_stocks.ini +++ /dev/null @@ -1,24 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Growth Stocks - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Market Cap. = +Micro (over $50mln) -Average Volume = Over 300K -Country = USA -Price = Over $10 - -[Fundamental] -EPS growthnext 5 years = Over 15% -Debt/Equity = Under 0.5 -PEG = Under 2 -EPS growththis year = Over 15% -EPS growthqtr over qtr = Over 15% -EPS growthpast 5 years = Over 15% - -[Technical] -20-Day Simple Moving Average = SMA20 above SMA200 -50-Day Simple Moving Average = SMA50 above SMA200 diff --git a/openbb_terminal/miscellaneous/stocks/screener/heavy_inst_ins.ini b/openbb_terminal/miscellaneous/stocks/screener/heavy_inst_ins.ini deleted file mode 100644 index 0078bc7c69a..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/heavy_inst_ins.ini +++ /dev/null @@ -1,15 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Heavily owned by institutions and insiders (>30% each) - -[General] - -[Descriptive] -Analyst Recom. = Hold or better - -[Fundamental] -InsiderOwnership = High (>30%) -InsiderTransactions = Positive (>0%) -PEG = Under 1 -InstitutionalOwnership = Over 30% - -[Technical] diff --git a/openbb_terminal/miscellaneous/stocks/screener/high_vol_and_low_debt.ini b/openbb_terminal/miscellaneous/stocks/screener/high_vol_and_low_debt.ini deleted file mode 100755 index 73a6a662db8..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/high_vol_and_low_debt.ini +++ /dev/null @@ -1,16 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: High Volume, NEW Volume, Low Debt - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 500K -Relative Volume = Over 1.5 -Current Volume = Over 500K - -[Fundamental] -Debt/Equity = Under 0.5 - -[Technical] diff --git a/openbb_terminal/miscellaneous/stocks/screener/modified_dreman.ini b/openbb_terminal/miscellaneous/stocks/screener/modified_dreman.ini deleted file mode 100644 index 7563c7657c7..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/modified_dreman.ini +++ /dev/null @@ -1,15 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Modified Version of the Dreman Screener. - -[General] - -[Descriptive] -Dividend Yield = Over 1% - -[Fundamental] -P/E = Under 20 -EPS growthnext 5 years = Positive Low (<10%) -Sales growthpast 5 years = Positive Low (0-10%) -EPS growththis year = Positive Low (0-10%) - -[Technical] diff --git a/openbb_terminal/miscellaneous/stocks/screener/modified_neff.ini b/openbb_terminal/miscellaneous/stocks/screener/modified_neff.ini deleted file mode 100644 index 8db7c1b09ec..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/modified_neff.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Neff Screener with modifications // operational margin <50%. More information can be found in https://marketxls.com/template/neff-screen/ - -[General] - -[Descriptive] - -[Fundamental] -Sales growthpast 5 years = Under 20% -PEG = Under 2 -EPS growththis year = Under 20% -Operating Margin = Under 50% - -[Technical] diff --git a/openbb_terminal/miscellaneous/stocks/screener/news_scanner.ini b/openbb_terminal/miscellaneous/stocks/screener/news_scanner.ini deleted file mode 100755 index 044bd4be70f..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/news_scanner.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: News Scanner - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Market Cap. = Nano (under $50mln) -Current Volume = Over 0 -Price = Under $5 - -[Fundamental] -InstitutionalOwnership = Under 30% - -[Technical] -RSI (14) = Not Overbought (<50) diff --git a/openbb_terminal/miscellaneous/stocks/screener/oversold.ini b/openbb_terminal/miscellaneous/stocks/screener/oversold.ini deleted file mode 100755 index 5b7998a28b4..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/oversold.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Oversold - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Market Cap. = +Micro (over $50mln) -Current Volume = Over 300K -Price = Under $3 - -[Fundamental] -Price/Cash = Under 5 - -[Technical] -RSI (14) = Oversold (40) diff --git a/openbb_terminal/miscellaneous/stocks/screener/oversold_under_3dol.ini b/openbb_terminal/miscellaneous/stocks/screener/oversold_under_3dol.ini deleted file mode 100755 index 8dab7ec7fb9..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/oversold_under_3dol.ini +++ /dev/null @@ -1,19 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Oversold Under $3 - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Market Cap. = Nano (under $50mln) -Average Volume = Under 1M -Relative Volume = Over 0.25 -Current Volume = Over 50K -Price = Under $3 - -[Fundamental] - -[Technical] -Beta = Over 0 -RSI (14) = Oversold (40) diff --git a/openbb_terminal/miscellaneous/stocks/screener/oversold_under_5dol.ini b/openbb_terminal/miscellaneous/stocks/screener/oversold_under_5dol.ini deleted file mode 100755 index 8dd9c82a0d1..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/oversold_under_5dol.ini +++ /dev/null @@ -1,19 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Oversold Under $5 - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Market Cap. = Nano (under $50mln) -Relative Volume = Over 0.25 -Current Volume = Over 0 -Price = Under $5 - -[Fundamental] - - -[Technical] -Beta = Over 0 -RSI (14) = Oversold (40) diff --git a/openbb_terminal/miscellaneous/stocks/screener/potential_reversals.ini b/openbb_terminal/miscellaneous/stocks/screener/potential_reversals.ini deleted file mode 100755 index 7dfa551b748..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/potential_reversals.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Potential Reversals - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] - - -[Fundamental] - - -[Technical] - -Performance = Today +5% -RSI (14) = Oversold (30) diff --git a/openbb_terminal/miscellaneous/stocks/screener/recent_growth_and_support.ini b/openbb_terminal/miscellaneous/stocks/screener/recent_growth_and_support.ini deleted file mode 100755 index c2cda688510..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/recent_growth_and_support.ini +++ /dev/null @@ -1,18 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Recent Growth, Support - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 500K -Country = USA -Price = Over $5 - -[Fundamental] - -[Technical] -Performance = Quarter +20% -Performance 2 = Month +10% -Pattern = TL Support diff --git a/openbb_terminal/miscellaneous/stocks/screener/rosenwald.ini b/openbb_terminal/miscellaneous/stocks/screener/rosenwald.ini deleted file mode 100644 index 349f7e67b9b..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/rosenwald.ini +++ /dev/null @@ -1,16 +0,0 @@ -# Author of preset: Traceabl3 -# Description: The "classic rosenwald" screen based on some dude i work with best guess. - -[General] - -[Descriptive] -Analyst Recom. = Hold or better - -[Fundamental] -P/E = Under 20 -Sales growthpast 5 years = Over 30% -InsiderTransactions = Positive (>0%) -PEG = Under 2 - -[Technical] -Pattern = Channel Up (Strong) diff --git a/openbb_terminal/miscellaneous/stocks/screener/rosenwald_gtfo.ini b/openbb_terminal/miscellaneous/stocks/screener/rosenwald_gtfo.ini deleted file mode 100644 index 3e5d17df41c..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/rosenwald_gtfo.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Too many indicators indicating an impending crash. - -[General] - -[Descriptive] - -[Fundamental] - -[Technical] -20-Day Simple Moving Average = Price below SMA20 -52-Week High/Low = 0-5% below High -RSI (14) = Oversold (40) -Pattern = Wedge Down diff --git a/openbb_terminal/miscellaneous/stocks/screener/sdk_guide_preset.ini b/openbb_terminal/miscellaneous/stocks/screener/sdk_guide_preset.ini deleted file mode 100644 index 4dc2a5dcc6d..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sdk_guide_preset.ini +++ /dev/null @@ -1,16 +0,0 @@ -# Author of preset: OpenBB -# Description: SDK Demo Screener - -[General] -Order = Market Cap. -Signal = Major News - -[Descriptive] -Average Volume = Over 500K -Price = Over $5 - -[Fundamental] -Price/Free Cash Flow = Low (<15) - -[Technical] -Beta = Under 0.5 \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/stocks/screener/sexy_year.ini b/openbb_terminal/miscellaneous/stocks/screener/sexy_year.ini deleted file mode 100644 index 3096262243c..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sexy_year.ini +++ /dev/null @@ -1,16 +0,0 @@ -# Author of preset: Sexy Year -# Description: This is just a sample. The user that adds the preset can add a description for what type of stocks these filters are aimed for - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Sector = Technology -Earnings Date = This Month - -[Fundamental] -InsiderOwnership = Over 10% - -[Technical] -Performance = Month Up diff --git a/openbb_terminal/miscellaneous/stocks/screener/short_squeeze_scan.ini b/openbb_terminal/miscellaneous/stocks/screener/short_squeeze_scan.ini deleted file mode 100755 index 791cd24dbfe..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/short_squeeze_scan.ini +++ /dev/null @@ -1,18 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Short Squeeze Scan - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] - -Float Short = Over 20% - -[Fundamental] - - -[Technical] - -Performance = Today Up -Beta = Over 3 diff --git a/openbb_terminal/miscellaneous/stocks/screener/simplistic_momentum_scanner_under_7dol.ini b/openbb_terminal/miscellaneous/stocks/screener/simplistic_momentum_scanner_under_7dol.ini deleted file mode 100755 index fee2604f104..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/simplistic_momentum_scanner_under_7dol.ini +++ /dev/null @@ -1,15 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Simplistic Momentum Scanner Under $7 - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 500K -Price = Under $7 - -[Fundamental] - -[Technical] -20-Day High/Low = 5% or more above Low diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_basic_materials_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_basic_materials_sector.ini deleted file mode 100755 index 60b9bc82eab..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_basic_materials_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Basic Materials Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Basic Materials - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_communication_services_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_communication_services_sector.ini deleted file mode 100755 index ea96088be96..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_communication_services_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Communication Services Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Communication Services - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_consumer_cyclical_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_consumer_cyclical_sector.ini deleted file mode 100755 index b8f6995c76a..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_consumer_cyclical_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Consumer Cyclical Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Consumer Cyclical - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_consumer_defensive_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_consumer_defensive_sector.ini deleted file mode 100755 index 01b48144368..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_consumer_defensive_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Consumer Defensive Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Consumer Defensive - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_energy_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_energy_sector.ini deleted file mode 100755 index 931cebf4b88..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_energy_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Energy Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Energy - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_financial_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_financial_sector.ini deleted file mode 100755 index f85060ee3d0..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_financial_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Financial Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Financial - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_healthcare_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_healthcare_sector.ini deleted file mode 100755 index 19a9a06eaac..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_healthcare_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Healthcare Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Healthcare - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_industrials_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_industrials_sector.ini deleted file mode 100755 index e80a937957f..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_industrials_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Industrials Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Industrials - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_real_estate_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_real_estate_sector.ini deleted file mode 100755 index 7ac2b3a3add..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_real_estate_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Real Estate Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Real Estate - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_technology_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_technology_sector.ini deleted file mode 100755 index 8bba1c5107d..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_technology_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Technology Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Technology - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/sp500_utilities_sector.ini b/openbb_terminal/miscellaneous/stocks/screener/sp500_utilities_sector.ini deleted file mode 100755 index 4b0e4e708ee..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/sp500_utilities_sector.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: OpenBB -# Description: Filter for the S&P 500 Utilities Sector components. - -[General] - -[Descriptive] - -Index = S&P 500 -Sector = Utilities - -[Fundamental] - -[Technical] - diff --git a/openbb_terminal/miscellaneous/stocks/screener/stocks_strong_support_levels.ini b/openbb_terminal/miscellaneous/stocks/screener/stocks_strong_support_levels.ini deleted file mode 100755 index 3c1e531121f..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/stocks_strong_support_levels.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Stocks Strong Support Levels - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] - -[Fundamental] -P/E = Over 15 -Debt/Equity = Under 0.6 -Current Ratio = Over 1.5 -P/B = Under 2 - -[Technical] -Pattern = TL Support (Strong) diff --git a/openbb_terminal/miscellaneous/stocks/screener/top_performers_all.ini b/openbb_terminal/miscellaneous/stocks/screener/top_performers_all.ini deleted file mode 100755 index 6a4d74ab465..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/top_performers_all.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Top performers (ALL) - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 500K -Country = USA -Price = Over $5 - -[Fundamental] - -[Technical] -Performance = Quarter +20% -Performance 2 = Month +10% diff --git a/openbb_terminal/miscellaneous/stocks/screener/top_performers_healthcare.ini b/openbb_terminal/miscellaneous/stocks/screener/top_performers_healthcare.ini deleted file mode 100755 index 77aa6ec7551..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/top_performers_healthcare.ini +++ /dev/null @@ -1,18 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Top performers (Healthcare) - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 500K -Sector = Healthcare -Country = USA -Price = Over $5 - -[Fundamental] - -[Technical] -Performance = Quarter +20% -Performance 2 = Month +10% diff --git a/openbb_terminal/miscellaneous/stocks/screener/top_performers_tech.ini b/openbb_terminal/miscellaneous/stocks/screener/top_performers_tech.ini deleted file mode 100755 index c6f60b602f0..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/top_performers_tech.ini +++ /dev/null @@ -1,18 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Top performers (tech) - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 500K -Sector = Technology -Country = USA -Price = Over $5 - -[Fundamental] - -[Technical] -Performance = Quarter +20% -Performance 2 = Month +10% diff --git a/openbb_terminal/miscellaneous/stocks/screener/under_15dol_stocks.ini b/openbb_terminal/miscellaneous/stocks/screener/under_15dol_stocks.ini deleted file mode 100755 index 552dcee8251..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/under_15dol_stocks.ini +++ /dev/null @@ -1,16 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Under $15 Stocks - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Market Cap. = +Micro (over $50mln) -Relative Volume = Over 1.5 -Price = Under $15 - -[Fundamental] - -[Technical] -20-Day Simple Moving Average = Price above SMA20 diff --git a/openbb_terminal/miscellaneous/stocks/screener/undervalue.ini b/openbb_terminal/miscellaneous/stocks/screener/undervalue.ini deleted file mode 100644 index a8156d795a3..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/undervalue.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Author of preset: Traceabl3 -# Description: Potential Undervalued stocks - -[General] -Signal = Upgrades - -[Descriptive] - -[Fundamental] -PEG = Low (<1) -P/B = Under 3 -EPS growthpast 5 years = Over 15% - -[Technical] diff --git a/openbb_terminal/miscellaneous/stocks/screener/unusual_volume.ini b/openbb_terminal/miscellaneous/stocks/screener/unusual_volume.ini deleted file mode 100755 index 7e83ced797c..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/unusual_volume.ini +++ /dev/null @@ -1,17 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Unusual Volume - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Exchange = NASDAQ -Average Volume = Under 750K -Current Volume = Over 750K -Country = USA -# Price = Custom (Elite only) - -[Fundamental] - -[Technical] diff --git a/openbb_terminal/miscellaneous/stocks/screener/value_stocks.ini b/openbb_terminal/miscellaneous/stocks/screener/value_stocks.ini deleted file mode 100755 index 66d0cd435df..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/value_stocks.ini +++ /dev/null @@ -1,29 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: Value Stocks - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] -Average Volume = Over 200K -Analyst Recom. = Buy or better -Current Volume = Over 200K -Country = USA - -[Fundamental] -EPS growthnext 5 years = Positive (>0%) -Return on Equity = Positive (>0%) -Debt/Equity = Under 0.5 -Sales growthpast 5 years = Positive (>0%) -PEG = Under 2 -EPS growththis year = Positive (>0%) -P/S = Under 5 -EPS growthnext year = Positive (>0%) -P/B = Under 7 -EPS growthpast 5 years = Positive (>0%) -LT Debt/Equity = Under 0.5 - -[Technical] -50-Day Simple Moving Average = Price above SMA50 -200-Day Simple Moving Average = Price above SMA200 diff --git a/openbb_terminal/miscellaneous/stocks/screener/weak_support_and_top_performers.ini b/openbb_terminal/miscellaneous/stocks/screener/weak_support_and_top_performers.ini deleted file mode 100755 index ae4dbe981bb..00000000000 --- a/openbb_terminal/miscellaneous/stocks/screener/weak_support_and_top_performers.ini +++ /dev/null @@ -1,19 +0,0 @@ -# Author of preset: JohnnyDankseed -# Description: (Weak Support Trendlines, Top Performers) - -[General] -Order = Ticker -Signal = Top Gainers - -[Descriptive] - -Average Volume = Over 500K -Country = USA -Price = Over $5 - -[Fundamental] - -[Technical] -Performance = Quarter +20% -Performance 2 = Month +10% -Pattern = TL Support diff --git a/openbb_terminal/mutual_funds/README.md b/openbb_terminal/mutual_funds/README.md deleted file mode 100644 index 630bd371a0c..00000000000 --- a/openbb_terminal/mutual_funds/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Resources - -* Trading Mutual Funds for Beginners : diff --git a/openbb_terminal/mutual_funds/__init__.py b/openbb_terminal/mutual_funds/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/mutual_funds/avanza_fund_ID.csv b/openbb_terminal/mutual_funds/avanza_fund_ID.csv deleted file mode 100644 index d3317c80a08..00000000000 --- a/openbb_terminal/mutual_funds/avanza_fund_ID.csv +++ /dev/null @@ -1,1320 +0,0 @@ -Fond,ISIN,Fond Bolag,Region,Kategori,ID -Aberdeen Glb Sus Eq A Acc USD,LU0094547139,Aberdeen,,,70196 -Aberdeen Japanese Sstnb Eq Sus S Acc JPY,LU0476876247,Aberdeen,,,232949 -Aberdeen Jpn Sm Coms Sus Eq S Acc JPY,LU0476876833,Aberdeen,,,232953 -abrdn All China Sus Equity S Acc USD,LU0476876163,Aberdeen,,,232950 -abrdn Asia Pacific Eq S Acc USD,LU0476875868,Aberdeen,,,232951 -abrdn Asian Smllr Coms A Acc USD,LU0231459107,Aberdeen,,,70031 -abrdn China A Share Sus Eq A Acc USD,LU1146622755,Aberdeen,,,1307500 -abrdn EM Corp Bd A Acc USD,LU0566480116,Aberdeen,,,290802 -abrdn Emerging Markets Eq A Acc USD,LU0132412106,Aberdeen,,,70074 -abrdn Euro Govt Bd A Acc EUR,LU1646950771,Aberdeen,,,831733 -abrdn Eurp Eq Div S Acc EUR,LU0505783562,Aberdeen,,,241953 -abrdn Global Inno Eq S Acc USD,LU0476877211,Aberdeen,,,232954 -abrdn Indian Equity A Acc USD,LU0231490524,Aberdeen,,,69959 -abrdn Latin Amer Eq A Acc USD,LU0396314238,Aberdeen,,,300236 -abrdn Mltfct Glbl Eq A Acc EUR,LU1703976222,Aberdeen,,,832735 -abrdn NA SmComs A Acc USD,LU0566484027,Aberdeen,,,1307499 -abrdn Sel EM Bd A Acc USD,LU0132414144,Aberdeen,,,71042 -abrdn SelEuro HYBd A Acc EUR,LU0119176310,Aberdeen,,,70501 -abrdn USD Crdt Sus Bd A Acc USD,LU1646952801,Aberdeen,,,832734 -abrdn Wld Res Eq S USD Acc,LU0505784297,Aberdeen,,,241951 -Adrigo Small & Midcap L/S,SE0010440735,East Capital,,,792013 -AGCM Asia Dividend RC1 SEK,LU2083136650,Asia Growth,,,1142277 -AGCM Asia Growth RC SEK,LU1091660909,Asia Growth,,,544595 -AGCM China Stars RC1 SEK,LU1608617111,Asia Growth,,,782933 -Aktie-Ansvar Avkastningsfond,SE0000735771,Aktie-Ansvar,,,2034 -Aktie-Ansvar Europa,SE0000735797,Aktie-Ansvar,,,632 -Aktie-Ansvar Räntestrategi,SE0014428165,Aktie-Ansvar,,,1106850 -Aktie-Ansvar Saxxum Aktiv A,SE0005218120,Aktie-Ansvar,,,429484 -Aktie-Ansvar Sverige A,SE0000735789,Aktie-Ansvar,,,1901 -Aktie-Ansvar Total,SE0003428143,Aktie-Ansvar,,,246208 -Aktiehuset Fond,SE0013222882,SIP Nordic Fondkommission AB,,,1161196 -Aktiespararna Direktavkastning A,SE0008321780,Cicero,,,693833 -Aktiespararna Småbolag Edge,SE0011415256,Cicero,,,1236399 -Aktiespararna Topp Sverige Hållbar A,SE0000924649,Cicero,,,1961 -Alcur,SE0001904798,Alcur Fonder,,,933615 -Alcur Grow B,SE0015196019,Alcur Fonder,,,1170516 -Alfred Berg Aktiv,NO0010089444,Alfred Berg,,,1340461 -Alfred Berg Gambak,NO0010105489,Alfred Berg,,,15147 -Alfred Berg Income A,SE0016275697,Alfred Berg,,,1340459 -Alfred Berg Nordic Gambak C,NO0010833395,Alfred Berg,,,981861 -Alfred Berg Nordic Small Cap ESG A,SE0014399549,Alfred Berg,,,1129405 -Alfred Berg Norge C,NO0010089402,Alfred Berg,,,1340460 -Allianz All China Equity AT USD,LU1720050803,Allianz,,,863473 -Allianz Asian Small Cap Equity AT15 USD,LU1055786526,Allianz,,,481107 -Allianz China A Opportunities AT USD,LU2289578879,Allianz,,,1197215 -Allianz China A Shares AT USD,LU1997245177,Allianz,,,1011686 -Allianz China Equity A USD,LU0348825331,Allianz,,,286894 -Allianz China Strategic Bond A USD,LU0665630819,Allianz,,,320490 -Allianz Cyber Security AT USD Acc,LU2286300806,Allianz,,,1237259 -Allianz Dynamic Commodities A EUR,LU0542493225,Allianz,,,1386016 -Allianz Emerging Asia Equity A USD,LU0348788117,Allianz,,,414058 -Allianz Euro High Yield Bond A EUR,LU0482909818,Allianz,,,286898 -Allianz Europe Equity Gr Sel AT EUR,LU0920839346,Allianz,,,414960 -Allianz Europe Equity Growth A EUR,LU0256839191,Allianz,,,151059 -Allianz Europe Equity SRI A EUR,LU0542502157,Allianz,,,290798 -Allianz Europe Small Cap Equity AT EUR,LU0293315296,Allianz,,,288001 -Allianz European Equity Div AT EUR,LU0414045822,Allianz,,,288003 -Allianz Global Artfcl Intlgc AT EUR,LU1548497699,Allianz,,,759593 -Allianz Global Equity Growth AT USD,LU1633808545,Allianz,,,1173921 -Allianz Global Equity Insights A EUR,LU1508476725,Allianz,,,741453 -Allianz Global High Yield A USD,LU1480270997,Allianz,,,746774 -Allianz Global Metals and Mining A EUR,LU0589944643,Allianz,,,334858 -Allianz Global Small Cap Equity AT USD,LU0963586101,Allianz,,,436817 -Allianz Global Sustainability A EUR,LU0158827195,Allianz,,,151033 -Allianz Global Water AT EUR,LU1890834598,Allianz,,,909774 -Allianz Green Bond AT3 H2 SEK,LU1522997029,Allianz,,,746773 -Allianz Income and Growth AT USD,LU0689472784,Allianz,,,334870 -Allianz Japan Equity A USD,LU0348751388,Allianz,,,393598 -Allianz Pet and Animal Wlb AT EUR,LU1931535931,Allianz,,,1133347 -Allianz Smart Energy AT USD,LU2048586759,Allianz,,,1275620 -Allianz Strategic Bond AT H2-EUR,LU2066004206,Allianz,,,1084210 -Allianz Thailand Equity A EUR,LU0348798009,Allianz,,,393595 -Allianz US Equity Fund AT USD,LU0256863902,Allianz,,,410277 -Allianz US High Yield AM USD,LU0516397667,Allianz,,,334871 -AMF Aktiefond Asien Stilla havet,SE0002572313,AMF,,,143369 -AMF Aktiefond Europa,SE0000739153,AMF,,,736 -AMF Aktiefond Global,SE0000862278,AMF,,,1887 -AMF Aktiefond Nordamerika,SE0002042283,AMF,,,96927 -AMF Aktiefond Småbolag,SE0001185000,AMF,,,2801 -AMF Aktiefond Sverige,SE0000739195,AMF,,,1949 -AMF Aktiefond Tillväxtmarknader,SE0013110798,AMF,,,1025148 -AMF Aktiefond Världen,SE0000739161,AMF,,,2300 -AMF Balansfond,SE0000739179,AMF,,,281 -AMF Företagsobligationsfond,SE0011414606,AMF,,,887413 -AMF Räntefond Kort,SE0001184961,AMF,,,2802 -AMF Räntefond Lång,SE0000739187,AMF,,,2111 -AMF Räntefond Mix,SE0004324424,AMF,,,333557 -Amundi Fds China Equity A USD C,LU1882445643,Amundi,,,972004 -Amundi Fds Emerging Wld Eq A USD C,LU0347592197,Amundi,,,173876 -Amundi Fds Equity Jpn Trgt A JPY C,LU0568583008,Amundi,,,301420 -Amundi Fds Equity MENA A USD C,LU0568613946,Amundi,,,301423 -Amundi Fds Euro HY Bd A EUR C,LU0119110723,Amundi,,,70110 -Amundi Fds Euroland Equity A EUR C,LU1883303635,Amundi,,,972003 -Amundi Fds Glbl Eq Cnsrv A USD C,LU0801842559,Amundi,,,439855 -Amundi Fds Pio US Bond A USD C,LU1880401523,Amundi,,,972534 -Amundi Fds Pio US Eq Rsrch Val A USD C,LU1894683009,Amundi,,,972005 -Amundi Fds SBI FM India Eq A USD C,LU0236501697,Amundi,,,71332 -Amundi Fds Volatil Wld A USD C,LU0319687124,Amundi,,,177748 -Amundi Fds WellsFargo US Eq MdCp A$ C,LU0568602667,Amundi,,,301426 -Arden xFund,SE0004550200,AIFM Capital AB,,,346836 -Atlant Edge,SE0001007527,Atlant,,,1100 -Atlant Future A,SE0016076327,Atlant,,,1280199 -Atlant Green Tech Metals,SE0008015077,Atlant,,,666676 -Atlant Högräntefond,SE0014262788,Atlant,,,1214801 -Atlant Multi-Strategy,SE0007870993,Atlant,,,627796 -Atlant Opportunity,SE0007871488,Atlant,,,625255 -Atlant Protect A,SE0007871694,Atlant,,,625256 -Atlant Sharp,SE0002090621,Atlant,,,86249 -Atlant Stability,SE0002623553,Atlant,,,145768 -AuAg Precious Green A,SE0014808440,FoF AuAg Asset Management,,,1130441 -AuAg Silver Bullet A,SE0013358181,FoF AuAg Asset Management,,,1015114 -Augmented Reality Fund A,SE0011527829,AIFM Capital AB,,,953014 -Avanza 100,SE0004841526,Avanza,,,377805 -Avanza 50,SE0004841567,Avanza,,,377807 -Avanza 75,SE0004841534,Avanza,,,377804 -Avanza Auto 1,SE0009779655,Avanza,,,788395 -Avanza Auto 2,SE0009779663,Avanza,,,788396 -Avanza Auto 3,SE0009779671,Avanza,,,788397 -Avanza Auto 4,SE0009779689,Avanza,,,788398 -Avanza Auto 5,SE0009779697,Avanza,,,788393 -Avanza Auto 6,SE0009779705,Avanza,,,788394 -Avanza Emerging Markets,SE0012454338,Avanza,,,944976 -Avanza Europa,SE0013718699,Avanza,,,1041067 -Avanza Fastighet by Norhammar,SE0017084296,Avanza,,,1342559 -Avanza Global,SE0011527613,Avanza,,,878733 -Avanza Småbolag by Skoglund,SE0015194618,Avanza,,,1162060 -Avanza Sverige,SE0014956850,Avanza,,,1270939 -Avanza USA,SE0012741163,Avanza,,,1025150 -Avanza World Tech by TIN,SE0014453221,Avanza,,,1102641 -Avanza Zero,SE0001718388,Avanza,,,41567 -Barings ASEAN Frontiers A USD Inc,IE0000830236,Baring,,,32 -Barings Asia Growth ClassA USD Inc,IE0000830129,Baring,,,142 -Barings Europa A USD Inc,IE0000829121,Baring,,,596 -Barings Global Emerging Mkts A Inc,IE0000838304,Baring,,,2138 -Barings Global Leaders A USD Inc,IE0030016244,Baring,,,2376 -Barings Hong Kong China A USD Inc,IE0000829238,Baring,,,68 -BGF Asia Pacific Equity Inc A2 USD,LU0414403419,BlackRock,,,316502 -BGF Asian Dragon A2,LU0072462343,BlackRock,,,58 -BGF Asian Growth Leaders A2 USD,LU0821914370,BlackRock,,,436405 -BGF China A2,LU0359201612,BlackRock,,,186308 -BGF Circular Economy A2,LU2041044095,BlackRock,,,1023645 -BGF Emerging Markets A2,LU0047713382,BlackRock,,,2145 -BGF Emerging Markets Bond A2,LU0200683885,BlackRock,,,15214 -BGF Emerging Markets Eq Inc A2,LU0651946864,BlackRock,,,316497 -BGF Euro Corporate Bond A2,LU0162658883,BlackRock,,,15213 -BGF Euro-Markets A2,LU0093502762,BlackRock,,,734 -BGF European A2,LU0011846440,BlackRock,,,717 -BGF European Equity Income A2,LU0562822386,BlackRock,,,316923 -BGF European Special Situations A2,LU0154234636,BlackRock,,,708 -BGF FinTech A2,LU1861217088,BlackRock,,,927736 -BGF Future of Transport A2,LU1861214812,BlackRock,,,927737 -BGF Global Allocation A2,LU0072462426,BlackRock,,,2439 -BGF Global Dynamic Equity A2,LU0238689110,BlackRock,,,1183854 -BGF Global Equity Income A2,LU0545039389,BlackRock,,,316508 -BGF Global High Yield Bond A2 SEK Hedged,LU0797429023,BlackRock,,,374277 -BGF Global Long-Horizon Equity A2,LU0011850046,BlackRock,,,2284 -BGF India A2,LU0248272758,BlackRock,,,56067 -BGF Japan Small & MidCap Opps A2,LU0006061252,BlackRock,,,1339 -BGF Latin American A2,LU0072463663,BlackRock,,,1376 -BGF Multi-Theme Equity A2 Acc,LU2123743341,BlackRock,,,1165640 -BGF Natural Resources Gr & Inc A2 USD,LU0612318385,BlackRock,,,316507 -BGF Next Generation Technology A2,LU1861215975,BlackRock,,,929225 -BGF Sustainable Energy A2,LU0124384867,BlackRock,,,547 -BGF Systematic Glb SmallCap A2,LU0054578231,BlackRock,,,2362 -BGF US Basic Value A2,LU0072461881,BlackRock,,,1546 -BGF US Flexible Equity A2,LU0154236417,BlackRock,,,1573 -BGF US Small & MidCap Opps A2,LU0006061336,BlackRock,,,1604 -BGF World Energy A2,LU0122376428,BlackRock,,,540 -BGF World Financials A2,LU0106831901,BlackRock,,,541 -BGF World Gold A2,LU0055631609,BlackRock,,,492 -BGF World Healthscience A2,LU0122379950,BlackRock,,,475 -BGF World Mining A2,LU0075056555,BlackRock,,,2821 -BGF World Technology A2,LU0056508442,BlackRock,,,431 -BMO Diversified Growth A Acc SEK Hdg,LU1395056200,BMO,,,713373 -BMO European Real Estate Scs A Acc EUR,IE00B5N9RL80,BMO,,,713374 -BMO Global Smaller Coms Eq A Acc USD,LU0947182795,BMO,,,713377 -BMO LGM Glbl EmMkts Gr and Inc W USD Acc,IE00BZCTLS48,BMO,,,716154 -BMO LGM Responsible Asian Eq W USD Acc,IE00BYNZSR26,BMO,,,745573 -BMO LGM Rspnb Chn A-Shrs Eq W USD Acc,IE00BLP4JR31,BMO,,,1131556 -BMO Responsible Glb Em Mkts Eq A Inc USD,LU0153359632,BMO,,,2143 -BMO Responsible Global Equity A Inc USD,LU0382360757,BMO,,,713375 -BMO SDG Engagement Glbl Eq A USD Acc,LU1917706845,BMO,,,953456 -BMO Sustainable Mul Ast Inc A Acc SEKH,LU2051394869,BMO,,,1131557 -BMO Sustainable Opps Eurp Eq A Inc EUR,LU0153358667,BMO,,,715 -BMO US Smaller Companies A Inc USD,LU0153358154,BMO,,,1602 -BNP Paribas Aqua C C,LU1165135440,BNP Paribas,,,670776 -BNP Paribas Brazil Equity C C,LU0265266980,BNP Paribas,,,281970 -BNP Paribas China Eq Cl C,LU0823426308,BNP Paribas,,,415683 -BNP Paribas Consumer Innovtr Cl C,LU0823411706,BNP Paribas,,,455060 -BNP Paribas Energy Transition C C,LU0823414635,BNP Paribas,,,415797 -BNP Paribas Env Abs Rtn Th Eq EAR Cl C,LU2066067385,BNP Paribas,,,1186857 -BNP Paribas Europe Small Cap Cl C,LU0212178916,BNP Paribas,,,281283 -BNP Paribas Fds Ecsystm Rstn Cl EUR Cap,LU2308191738,BNP Paribas,,,1243599 -BNP Paribas Fds Sust Glb Eq Cl Cap,LU0956005226,BNP Paribas,,,1012654 -BNP Paribas Germany Mlt-Fac Eq C C,LU0823427611,BNP Paribas,,,416245 -BNP Paribas Global Envir Cl C,LU0347711466,BNP Paribas,,,315589 -BNP Paribas India Eq Cl Cap,LU0823428932,BNP Paribas,,,415681 -BNP Paribas Nordic Small Cap Cl C,LU0950372838,BNP Paribas,,,459897 -BNP Paribas Smart Food Cl Cap,LU1165137149,BNP Paribas,,,679795 -BNP Paribas Sst Asia ExJpn Eq C C,LU0823397103,BNP Paribas,,,416236 -BNP Paribas Sst Glb Lw Vol Eq Cl Cap,LU0823417810,BNP Paribas,,,415680 -BNP Paribas Sust Euro Corp Bd Cl C,LU0265288877,BNP Paribas,,,455102 -BNP Paribas Turkey Equity Classic R,LU0265293521,BNP Paribas,,,68707 -BNP Paribas US Growth Classic C,LU0823434583,BNP Paribas,,,415688 -BNP Paribas US Small Cap Classic R,LU0823410997,BNP Paribas,,,415796 -BNPP Sust US Value Mlt-Fac Eq Cl R,LU1458427785,BNP Paribas,,,789993 -BSF Dynamic Diversified Growth A2 SEK H,LU0724654230,BlackRock,,,334675 -BSF Emerging Markets Abs Ret A2 USD,LU0944773521,BlackRock,,,443897 -BSF Fixed Income Strategies A2 EUR,LU0438336264,BlackRock,,,375837 -BSF Global Absolute Return Bond A2 SEK H,LU0884611699,BlackRock,,,398162 -C WorldWide Asia 1A,LU0835599696,C Worldwide,,,382860 -C Worldwide Centuria Global Equities 1A,LU2276589087,C Worldwide,,,1232079 -C WorldWide Emerging Markets 1A,LU0086737482,C Worldwide,,,16527 -C WorldWide Global Equities 1A,LU0086737722,C Worldwide,,,2285 -C WorldWide Global Equities Ethical 1A,LU0122292328,C Worldwide,,,122368 -C WorldWide Healthcare Select 1A,LU0090908194,C Worldwide,,,445 -C WorldWide India 1A,LU2068969570,C Worldwide,,,1154803 -C WorldWide Nordic 1A,LU0086738027,C Worldwide,,,1643 -C WorldWide Stable Equities 1A,LU0093943974,C Worldwide,,,620 -C WorldWide Sweden 1A,LU0424681269,C Worldwide,,,207548 -C Worldwide Sweden Small Cap 1A,LU0424682077,C Worldwide,,,221608 -Caprifol Nordiska Fonden,SE0002834432,Caprifol,,,187208 -Captor Aster Global Credit A,SE0015243878,Captor Fund Management AB,,,1214808 -Captor Dahlia Green Bond A,SE0011337179,Captor Fund Management AB,,,1089363 -Captor Iris Bond A,SE0012204758,Captor Fund Management AB,,,1089362 -Captor Scilla Global Equity B,SE0010101360,Captor Fund Management AB,,,1141907 -Captor Scilla Nordic Equity B,SE0010102277,Captor Fund Management AB,,,1141908 -Carnegie All Cap A,SE0007704804,Carnegie Fonder,,,670777 -Carnegie Asia A,SE0001172263,Carnegie Fonder,,,129 -Carnegie Corporate Bond 3 SEK Cap,LU0075898915,Carnegie Fonder,,,2087 -Carnegie Fastighetsfond Norden A,SE0004296515,Carnegie Fonder,,,324195 -Carnegie Global Quality Companies A,SE0006993390,Carnegie Fonder,,,602915 -Carnegie Indienfond A,SE0001209834,Carnegie Fonder,,,3101 -Carnegie Investment Grade A,SE0000429847,Carnegie Fonder,,,2078 -Carnegie Likviditetsfond A,SE0000429854,Carnegie Fonder,,,2032 -Carnegie Listed Infrastructure A,SE0002768150,Carnegie Fonder,,,1263759 -Carnegie Listed Private Equity A,SE0003039874,Carnegie Fonder,,,198508 -Carnegie Multi A,SE0011843705,Carnegie Fonder,,,937474 -Carnegie Obligationsfond A,SE0000709313,Carnegie Fonder,,,2098 -Carnegie Småbolagsfond A,SE0004392025,Carnegie Fonder,,,334856 -Carnegie Strategifond A,SE0000429813,Carnegie Fonder,,,229 -Carnegie Strategy 3 SEK Cap,LU0075898758,Carnegie Fonder,,,230 -Carnegie Sverigefond A,SE0000429789,Carnegie Fonder,,,1892 -Carnegie Vega A,SE0001065285,Carnegie Fonder,,,1047 -Case All Star,SE0010134288,Case,,,795773 -Case Fair Play,SE0001338674,Case,,,202868 -Case Safe Play A,SE0003723808,Case,,,269548 -CB European Quality Fund A,LU0112589485,CB,,,633 -CB Save Earth Fund RC,LU0354788688,CB,,,130487 -Cicero Avkastningsfond A,SE0000891772,Cicero,,,2131 -Cicero Hållbar Mix A,SE0004576437,Cicero,,,398157 -Cicero Nordic Corporate Bond A,SE0005757275,Cicero,,,470457 -Cicero Sverige A,SE0012853828,Cicero,,,1013074 -Cicero World 0-50,SE0003655273,Cicero,,,330741 -Cliens Företagsobligationer A,SE0010948422,Cliens,,,891013 -Cliens Global Småbolag A,SE0016830749,Cliens,,,1295759 -Cliens Micro Cap A,SE0014829545,Cliens,,,1140464 -Cliens Mixfond B,SE0004841070,Cliens,,,377797 -Cliens Räntefond Kort,SE0003910298,Cliens,,,283141 -Cliens Småbolag A,SE0008992069,Cliens,,,694253 -Cliens Sverige B,SE0004869626,Cliens,,,379956 -Cliens Sverige Fokus A,SE0003910314,Cliens,,,283139 -COELI - Global Sm Cp Sel R SEK,LU2395167690,Coeli,,,1373839 -Coeli Absolute European Equity R SEK,LU1735828037,Coeli,,,880693 -Coeli Frontier Markets R SEK,LU1028961909,Coeli,,,469956 -Coeli Global Select R SEK,LU1133292463,Coeli,,,523895 -Coeli Mix R SEK,LU1720217196,Coeli,,,827894 -Coeli Nordisk Företagsobligation R SEK,LU1028962543,Coeli,,,490477 -COELI SICAV I Frontier Mkt Fxd Inc R SEK,LU2273159629,Coeli,,,1219833 -Consensus Global Select A,SE0011750926,FCG,,,1011194 -Consensus Lighthouse Asset A,SE0008373955,FCG,,,1011195 -Consensus Småbolag A,SE0008679971,FCG,,,1011196 -Consensus Sverige Select A,SE0011750967,FCG,,,1011197 -CPR Invest Global Gold Mines A USD Acc,LU1989766289,Amundi,,,1149494 -CPR Invest Global Lifestyles A USD Acc,LU1989767923,Amundi,,,1150816 -CPR Invest Global Resources A USD Acc,LU1989770125,Amundi,,,1149493 -CS (Lux) Asia Corporate Bond B USD,LU0828907005,Credit Suisse,,,378638 -CS (Lux) Asia Local Currency Bd B USD,LU0828911023,Credit Suisse,,,378651 -CS (Lux) European Div Plus Eq B EUR,LU0439729368,Credit Suisse,,,262969 -CS (Lux) European Entrepreneur Eq B,LU2066958385,Credit Suisse,,,1047827 -CS (Lux) Global Bal Convert Bd B USD,LU0426279682,Credit Suisse,,,326928 -CS (Lux) Global Div Plus Eq B USD,LU0439730457,Credit Suisse,,,414061 -CS (Lux) Global Prpty Ttl Rt Eq BH EUR,LU1483617970,Credit Suisse,,,718413 -CS (Lux) Global Value Equity B EUR,LU2066957221,Credit Suisse,,,1047829 -CS (Lux) Global Value Equity BH USD,LU2066957148,Credit Suisse,,,1047828 -CS (Lux) High Yield USD Bond B USD,LU1684797787,Credit Suisse,,,811073 -CS (Lux) Japan Value Equity B JPY,LU0496466821,Credit Suisse,,,414068 -CS (Lux) Security Equity B USD,LU0909471251,Credit Suisse,,,414056 -CS (Lux) Small and Mid Cap Germany Eq B,LU2066958898,Credit Suisse,,,1047825 -Danske Invest Aktiv Förmögenhetsförv SA,LU1349997640,Danske Invest,,,801495 -Danske Invest China A,LU0178668348,Danske Invest,,,122 -Danske Invest Emerging & Fron Mkts A SEK,LU1204910670,Danske Invest,,,596495 -Danske Invest Eur Corp Sust Bond A SEK h,LU1399304879,Danske Invest,,,766773 -Danske Invest Euro IG Corp Bd A SEKH,LU0178670161,Danske Invest,,,180708 -Danske Invest Europe High Dividend A,LU0123484957,Danske Invest,,,2375 -Danske Invest Europe Small Cap A,LU0123485178,Danske Invest,,,952 -Danske Invest Global EM Small Cap A,LU0292126785,Danske Invest,,,86668 -Danske Invest Global Emerging Market SEK,DK0061135910,Danske Invest,,,964491 -Danske Invest Global Index Restricted SA,LU1349493418,Danske Invest,,,801497 -Danske Invest Global Index SA,LU1349492527,Danske Invest,,,801496 -Danske Invest Global Sust Future A,LU0117088970,Danske Invest,,,2370 -Danske Invest Globala Realräntor SEK h,DK0060485605,Danske Invest,,,252929 -Danske Invest GloblEmergingMarkts A,LU0085580271,Danske Invest,,,2178 -Danske Invest Horisont Aktie SA,LU1349504479,Danske Invest,,,801503 -Danske Invest Horisont Balanserad SA d,LU1349505955,Danske Invest,,,801505 -Danske Invest Horisont Försiktig SA,LU1349507498,Danske Invest,,,801504 -Danske Invest Horisont Offensiv SA,LU1349509783,Danske Invest,,,801853 -Danske Invest Horisont Ränta SA,LU1349513462,Danske Invest,,,801713 -Danske Invest India A,LU0193801577,Danske Invest,,,18067 -Danske Invest Japan A,LU0193802039,Danske Invest,,,3242 -Danske Invest Nordiska Företagsobl SEK h,DK0060500262,Danske Invest,,,436064 -Danske Invest SICAV Europe A,LU0727217050,Danske Invest,,,882553 -Danske Invest SICAV Europe SA SEK,LU1891308568,Danske Invest,,,1145782 -Danske Invest Sverige Beta SA,LU1349502002,Danske Invest,,,801494 -Danske Invest Sverige Kort Ränta SA,LU1349496940,Danske Invest,,,801502 -Danske Invest Sverige Ränta SA,LU1349498565,Danske Invest,,,801500 -Danske Invest Sverige SA,LU1349494812,Danske Invest,,,801498 -Danske Invest Sverige Småbolag SA SEK,LU1857272386,Danske Invest,,,880673 -Danske Invest Tillväxtmarknadsobl SEK h,DK0060486090,Danske Invest,,,454936 -Danske Invest USA Index KL SEK,DK0060485795,Danske Invest,,,252935 -Delphi Europe A,NO0010039712,Storebrand,,,79691 -Delphi Global A,NO0010317282,Storebrand,,,79694 -Delphi Green Trends A2,NO0010911183,Storebrand,,,1205820 -Delphi Nordic A,NO0010039670,Storebrand,,,79692 -Didner & Gerge Aktiefond,SE0000428336,Didner & Gerge,,,1919 -Didner & Gerge Global,SE0004167567,Didner & Gerge,,,316079 -Didner & Gerge Small & Microcap,SE0005962347,Didner & Gerge,,,499515 -Didner & Gerge Småbolag,SE0002699421,Didner & Gerge,,,157699 -Didner & Gerge US Small and Microcap,SE0008347447,Didner & Gerge,,,679796 -DNB Europa Indeks A,NO0008001872,DNB,,,691673 -DNB Finans A,NO0008000593,DNB,,,321692 -DNB Fund - Asian Mid Cap Retail A EUR,LU0067059799,DNB,,,150 -DNB Fund - Disruptive Opp Retail A SEK,LU2085662703,DNB,,,1051349 -DNB Fund - Emerging Mkt Retail A USD,LU0090738252,DNB,,,2161 -DNB Fund - India retail A,LU0302237721,DNB,,,112109 -DNB Fund - Nordic Small Cap Retail A SEK,LU2085661721,DNB,,,1038776 -DNB Fund - Private Equity retail B,LU0302296065,DNB,,,131490 -DNB Fund Future Waves Retail A EUR,LU0029375739,DNB,,,2252 -DNB Fund Nordic Equities retail A,LU0083425479,DNB,,,1646 -DNB Fund Renewable Energy retail A,LU0302296149,DNB,,,96447 -DNB Fund Technology retail A,LU0302296495,DNB,,,131491 -DNB Fund TMT L/S Equities retail A SEK,LU0547714872,DNB,,,257890 -DNB Global Emerging Markets Indeks A,NO0010815855,DNB,,,862034 -DNB Global Indeks A,NO0010582984,DNB,,,463161 -DNB Global Lavkarbon A,NO0010801418,DNB,,,789173 -DNB Grönt Norden,NO0010102866,DNB,,,691676 -DNB Health Care A,NO0010337579,DNB,,,321696 -DNB High Yield retail A SEK,LU2077710585,DNB,,,1038774 -DNB Klimat Indeks A2,NO0011001869,DNB,,,1263179 -DNB Norden Indeks A,NO0010815871,DNB,,,862033 -DNB SMB A,NO0010337819,DNB,,,321849 -DNB Teknologi A,NO0010337678,DNB,,,321871 -DNB Telecom A,NO0010337942,DNB,,,290054 -DNB USA Indeks A,NO0010337959,DNB,,,329335 -Dynamica 90 Global,SE0003655224,Navigera,,,324895 -East Capital Balkans A1 SEK,LU1941809938,East Capital,,,1370204 -East Capital China A-Shares A USD,LU1840853219,East Capital,,,899034 -East Capital Global EM Sustainable A SEK,LU0562934264,East Capital,,,367456 -East Capital Global Frontier Mkts A SEK,LU1125674611,East Capital,,,524110 -East Capital Multi-Strategi A SEK,SE0007278585,East Capital,,,814733 -East Capital New Europe A1 SEK,LU2437452928,East Capital,,,1370205 -Elementa,SE0006852281,Elementa,,,553535 -Enter Cross Credit A,SE0000813933,Enter,,,2066 -Enter Preserve A,SE0005624632,Enter,,,470076 -Enter Return A,SE0002729038,Enter,,,158428 -Enter Select A,SE0002096545,Enter,,,91107 -Enter Select Pro,SE0001172362,Enter,,,26 -Enter Småbolagsfond A,SE0007413398,Enter,,,589935 -Enter Sverige A,SE0000813917,Enter,,,1963 -Enter Sverige Pro,SE0000813925,Enter,,,1964 -Ericsson & Partners Edge A,SE0016845416,Ericsson & Partners Förmögenhetsförvaltning AB,,,1322460 -Espiria 30 A,LU1120868382,East Capital,,,528376 -Espiria 30 C,LU0674583116,East Capital,,,323519 -Espiria 60 A,LU1120868465,East Capital,,,528379 -Espiria 90 C,LU0674582571,East Capital,,,323517 -Espiria Global A,LU0901111830,East Capital,,,421196 -Espiria SDG Solutions A,LU1120868549,East Capital,,,528375 -eSports Fund,SE0014957858,AIFM Capital AB,,,1145780 -European Alliance RPM Evolving CTA ER,LU1193396774,RPM,,,616835 -Evli Aktieindex B,FI4000058821,Evli,,,416835 -Evli Corporate Bond B SEK,FI4000043682,Evli,,,355599 -Evli Emerging Frontier B,FI4000066915,Evli,,,437855 -Evli Emerging Markets Credit B,FI4000066899,Evli,,,476475 -Evli Equity Factor Europe B,FI4000153820,Evli,,,996614 -Evli Equity Factor USA B,FI4000210810,Evli,,,996615 -Evli Euro Liquidity B SEK,FI4000058854,Evli,,,416235 -Evli Europe B,FI0008802046,Evli,,,656 -Evli Finland Mix B,FI0008801212,Evli,,,274 -Evli Finland Select B,FI0008800107,Evli,,,1674 -Evli Finnish Small Cap B,FI0008804422,Evli,,,864975 -Evli GEM B,FI4000153697,Evli,,,864973 -Evli Global B,FI0008801188,Evli,,,2354 -Evli Global Multi Manager 30 Fund B,FI0008805650,Evli,,,841333 -Evli Global X B SEK,FI4000320973,Evli,,,864974 -Evli High Yield B,FI0008803929,Evli,,,906 -Evli High Yield B SEK,FI4000043690,Evli,,,355598 -Evli Nordic B,FI0008810908,Evli,,,86207 -Evli Nordic Corporate Bond B SEK,FI4000058862,Evli,,,416237 -Evli Short Corporate Bond B,FI0008800511,Evli,,,869 -Evli Short Corporate Bond B SEK,FI4000043716,Evli,,,380220 -Evli Statsobligation B,FI0008800495,Evli,,,847 -Evli Sverige Småbolag B,FI0008813142,Evli,,,137448 -Excalibur Fixed Income A,SE0001097080,Excalibur,,,38392 -FE Select A,SE0014958161,FCG,,,1162342 -Fidelity America A-Dis-USD,LU0048573561,Fidelity,,,1484 -Fidelity American Growth A-Dis-USD,LU0077335932,Fidelity,,,1607 -Fidelity ASEAN A-Acc-USD,LU0261945553,Fidelity,,,76979 -Fidelity ASEAN A-Dis-USD,LU0048573645,Fidelity,,,1320940 -Fidelity Asia Pacific Div A-Dis-USD,LU0205439572,Fidelity,,,351739 -Fidelity Asia Pacific Opps A-Acc-EUR,LU0345361124,Fidelity,,,133428 -Fidelity Asian Bond A-Acc-USD,LU0605512275,Fidelity,,,350439 -Fidelity Asian Smaller Coms A-Acc-USD,LU0702159699,Fidelity,,,343420 -Fidelity Asian Special Sits A-Dis-USD,LU0054237671,Fidelity,,,156 -Fidelity Australian Divers Eq A-AUD,LU0048574536,Fidelity,,,79 -Fidelity China Consumer A-Acc-USD,LU0594300179,Fidelity,,,286152 -Fidelity China Focus A-Dis-USD,LU0173614495,Fidelity,,,4761 -Fidelity China High Yield A-Acc-USD,LU1313547462,Fidelity,,,966254 -Fidelity China RMB Bond A-Acc-RMB,LU0715234463,Fidelity,,,343418 -Fidelity Em Eurp Mdl Est&Afr A-Acc-USD,LU0303823156,Fidelity,,,89347 -Fidelity Em Mkt Corp Dbt A-Acc-USD,LU0900495697,Fidelity,,,426716 -Fidelity Em Mkts A-DIST-USD,LU0048575426,Fidelity,,,2151 -Fidelity Emerging Asia A-Acc-USD,LU0329678337,Fidelity,,,123809 -Fidelity Emerging Mkt Dbt A-Acc-SEK Hdg,LU0566130729,Fidelity,,,261308 -Fidelity Emerging Mkt Dbt A-Acc-USD,LU0238205958,Fidelity,,,38393 -Fidelity Euro Cash A-Dis-EUR,LU0064964074,Fidelity,,,841 -Fidelity European Dynamic Gr A-Dis-EUR,LU0119124781,Fidelity,,,669 -Fidelity European Growth A-Acc-EUR,LU0296857971,Fidelity,,,234109 -Fidelity European Growth A-Dis-EUR,LU0048578792,Fidelity,,,586 -Fidelity European Hi Yld A-Acc-EUR,LU0251130802,Fidelity,,,73515 -Fidelity European Hi Yld A-Acc-SEK Hdg,LU0413545426,Fidelity,,,165288 -Fidelity European Mlt-Asst Inc A Dis EUR,LU0052588471,Fidelity,,,242 -Fidelity European Smlr Coms A-Dis-EUR,LU0061175625,Fidelity,,,932 -Fidelity FIRST All Country Wld A-Acc-EUR,LU0267387255,Fidelity,,,57889 -Fidelity Germany A-Dis-EUR,LU0048580004,Fidelity,,,774 -Fidelity Glb Dmgrphcs A-Acc-USD,LU0528227936,Fidelity,,,350438 -Fidelity Glb Shrt Dur Inc A-Acc-USD,LU0390710027,Fidelity,,,350435 -Fidelity Glbl thmtc Oppos A-USD,LU0048584097,Fidelity,,,2289 -Fidelity Global Bond A-Dis-USD,LU0048582984,Fidelity,,,2481 -Fidelity Global Corp Bd A-Acc-EUR,LU0532243267,Fidelity,,,350597 -Fidelity Global Div Pl A EUR,LU0099575291,Fidelity,,,380 -Fidelity Global Dividend A-Acc-USD,LU0772969993,Fidelity,,,350445 -Fidelity Global Financial Svcs A-Dis-EUR,LU0114722498,Fidelity,,,521 -Fidelity Global Focus A-Acc-USD,LU1366333091,Fidelity,,,659035 -Fidelity Global Focus A-Dis-USD,LU0157215616,Fidelity,,,2453 -Fidelity Global Health Care A-Dis-EUR,LU0114720955,Fidelity,,,457 -Fidelity Global High Yield A-Acc-USD,LU0740037022,Fidelity,,,343415 -Fidelity Global Industrials A-Dis-EUR,LU0114722902,Fidelity,,,2344 -Fidelity Global Property A-Acc-USD,LU0237698914,Fidelity,,,38389 -Fidelity Global Technology A-Dis-EUR,LU0099574567,Fidelity,,,379 -Fidelity Greater China A-Dis-USD,LU0048580855,Fidelity,,,80 -Fidelity Iberia A-Acc-EUR,LU0261948904,Fidelity,,,76993 -Fidelity India Focus A-USD,LU0197229882,Fidelity,,,4762 -Fidelity Indonesia A-Dis-USD,LU0055114457,Fidelity,,,81 -Fidelity Italy A-Dis-EUR,LU0048584766,Fidelity,,,776 -Fidelity Japan Advantage A-Acc-JPY,LU0413544379,Fidelity,,,195128 -Fidelity Japan Advantage A-Dis-JPY,LU0161332480,Fidelity,,,4765 -Fidelity Latin America A-Dis-USD,LU0050427557,Fidelity,,,1362 -Fidelity Nordic A-Dis-SEK,LU0048588080,Fidelity,,,1644 -Fidelity Pacific A-Dis-USD,LU0049112450,Fidelity,,,35 -Fidelity Sst Fut Cnt A Acc USD,LU2247934214,Fidelity,,,1334999 -Fidelity Sus Europ Slr Cmp A Acc EUR,LU2296467611,Fidelity,,,1335000 -Fidelity Sus Rcd Cbn Bd A ACC USD,LU2111945882,Fidelity,,,1335002 -Fidelity Sust China A Shs A Acc EUR,LU2385790154,Fidelity,,,1344319 -Fidelity Sust Cnsmr Brds A-Dis-EUR,LU0114721508,Fidelity,,,522 -Fidelity Sust Strat Bd A-Acc-SEK H,LU0594300765,Fidelity,,,310959 -Fidelity Sust Water & Waste A Acc USD,LU1892829315,Fidelity,,,916313 -Fidelity Sustainable Asia Eq A-Dis-USD,LU0048597586,Fidelity,,,154 -Fidelity Sustainable Clt Sol A Acc USD,LU2348335964,Fidelity,,,1335003 -Fidelity Sustainable Euroz Eq A-Acc-EUR,LU0238202427,Fidelity,,,38388 -Fidelity Sustainable Euroz Eq A-Inc-EUR,LU2219351876,Fidelity,,,1320939 -Fidelity Sustainable Glb Eq A-ACC-EUR,LU1627197004,Fidelity,,,1335001 -Fidelity Sustainable Jpn Eq A-JPY,LU0048585144,Fidelity,,,1272 -Fidelity Sustainable US Eq A Acc EUR,LU0261960354,Fidelity,,,150208 -Fidelity Switzerland A-Dis-CHF,LU0054754816,Fidelity,,,777 -Fidelity Thailand A-Dis-USD,LU0048621477,Fidelity,,,85 -Fidelity UK Special Situations A-INC-GBP,LU2219351520,Fidelity,,,1320941 -Fidelity US High Yield A-Acc-EUR,LU0261953904,Fidelity,,,73607 -Finserve Global Security Fund I SEK R,LU1822851538,Finserve,,,1096906 -Fondita 2000+ B,FI0008802897,Fondita,,,2294 -Fondita Equity Spice B,FI0008802855,Fondita,,,1647 -Fondita European Micro Cap B,FI4000242854,Fondita,,,759973 -Fondita European Small Cap B,FI0008814694,Fondita,,,192934 -Fondita Healthcare B,FI4000321096,Fondita,,,886413 -Fondita Nordic Micro Cap B,FI0008810940,Fondita,,,70685 -Fondita Nordic Micro Cap SEK R,FI4000496393,Fondita,,,1292840 -Fondita Nordic Small Cap B,FI0008802871,Fondita,,,1681 -Fondita Sustainable Europe B,FI4000024492,Fondita,,,302705 -Fondita Sustainable Europe SEK R,FI4000496351,Fondita,,,1292839 -Franklin Biotechnology Discv A(acc)USD,LU0109394709,Franklin Templeton,,,455 -Franklin European Ttl Ret A(acc)SEK-H1,LU1022658311,Franklin Templeton,,,499516 -Franklin Global Real Estate A(acc)EUR-H1,LU0523919115,Franklin Templeton,,,246028 -Franklin Gold & Prec Mtls A(acc)USD,LU0496367417,Franklin Templeton,,,236069 -Franklin Gulf Wealth Bond A(acc) USD,LU0962741061,Franklin Templeton,,,693993 -Franklin India A(acc)USD,LU0231203729,Franklin Templeton,,,70371 -Franklin Innovation A(acc)USD,LU2063271972,Franklin Templeton,,,1225079 -Franklin K2 Alt Strats A(acc)SEK-H1,LU1162222308,Franklin Templeton,,,533855 -Franklin MENA A(acc)USD,LU0352132103,Franklin Templeton,,,139416 -Franklin Mutual European A(acc)EUR,LU0140363002,Franklin Templeton,,,29854 -Franklin Mutual Glbl DiscvA(acc)USD,LU0211331839,Franklin Templeton,,,39648 -Franklin Mutual US Value A acc USD,LU0070302665,Franklin Templeton,,,328 -Franklin Natural Resources A(acc)USD,LU0300736062,Franklin Templeton,,,112622 -Franklin Technology A Acc USD,LU0109392836,Franklin Templeton,,,397 -Franklin Templeton EM Sus A (acc) USD,LU2213486215,Franklin Templeton,,,1225080 -Franklin US Opportunities A(acc)SEK-H1,LU0563142453,Franklin Templeton,,,263951 -Franklin US Opportunities A(acc)USD,LU0109391861,Franklin Templeton,,,1512 -GAM Star China Equity A USD Acc,IE00B1W3X048,GAM,,,645076 -GAM Star Credit Opps (EUR) A EUR Acc,IE00B53VBZ63,GAM,,,645075 -GAM Star Disruptive Gr A USD Acc,IE00B56F8X29,GAM,,,644315 -GAM Star European Equity A EUR Acc,IE0031142981,GAM,,,644316 -GAM Star Japan Ldrs-A EUR,IE0003008186,GAM,,,124511 -Global Sustainable Leaders,SE0015837687,Navigera,,,1234479 -GlobeCap 100 A,SE0013460110,ALFAKRAFT Fonder AB,,,1038386 -Go Blockchain Fund A,SE0015837778,AIFM Capital AB,,,1220461 -GodFond Sverige & Världen,SE0002800276,SPP,,,181868 -GP Bullhound Global Technology A,SE0006881389,Finserve,,,1161889 -Handelsbanken Amerika Småbl Tema A1 SEK,SE0011336908,Handelsbanken,,,907816 -Handelsbanken Amerika Tema (A1 SEK),SE0000355828,Handelsbanken,,,1464 -Handelsbanken Asien Tema (A1 SEK),SE0000356073,Handelsbanken,,,135 -Handelsbanken Brasilien Tema A1 SEK,SE0011336874,Handelsbanken,,,909953 -Handelsbanken EM Index (A1 SEK),SE0011338409,Handelsbanken,,,910093 -Handelsbanken EMEA Tema (A1 SEK),SE0000356271,Handelsbanken,,,2572 -Handelsbanken Euro Obligation,SE0005991452,Handelsbanken,,,508257 -Handelsbanken Euro Ränta,SE0005965704,Handelsbanken,,,508256 -Handelsbanken Europa Index Crit (A1 SEK),SE0000624421,Handelsbanken,,,637 -Handelsbanken Europa Selektiv,SE0005936903,Handelsbanken,,,513575 -Handelsbanken Europa Småbolag (A1 SEK),SE0000396574,Handelsbanken,,,562 -Handelsbanken Finland Småbolag (A1 SEK),SE0005933033,Handelsbanken,,,512555 -Handelsbanken Flexibel Ränta A1 SEK,SE0005569316,Handelsbanken,,,457575 -Handelsbanken Företagsobligation,SE0005993128,Handelsbanken,,,512558 -Handelsbanken Gl Hi Div Lo Vol Cr A1 SEK,SE0013460920,Handelsbanken,,,1076398 -Handelsbanken Gl Småbolag Ind Cri A1 SEK,SE0010323675,Handelsbanken,,,799913 -Handelsbanken Global Index Crit (A1 SEK),SE0011309707,Handelsbanken,,,910094 -Handelsbanken Global Momentum A1 SEK,SE0015382114,Handelsbanken,,,1200237 -Handelsbanken Global Selektiv (A1 SEK),SE0011336809,Handelsbanken,,,913953 -Handelsbanken Global Tema (Criteria),SE0000356263,Handelsbanken,,,2240 -Handelsbanken Hälsovård Tema (A1 SEK),SE0000735375,Handelsbanken,,,464 -Handelsbanken Hållbar Energi A1 SEK,SE0005965662,Handelsbanken,,,512559 -Handelsbanken Hållbar Gl Obl A1 SEK,SE0011751155,Handelsbanken,,,1200236 -Handelsbanken Japan Tema (A1 SEK),SE0000356099,Handelsbanken,,,1254 -Handelsbanken Kinafond,SE0005932993,Handelsbanken,,,512556 -Handelsbanken Korträn SEK (A1 SEK),SE0000356305,Handelsbanken,,,2028 -Handelsbanken Kortränta (A1 SEK),SE0011309491,Handelsbanken,,,901673 -Handelsbanken Latinamerika Tema (A1 SEK),SE0000356107,Handelsbanken,,,1358 -Handelsbanken Långränta Criteria,SE0000356297,Handelsbanken,,,2079 -Handelsbanken Multi Asset 100 (A1 SEK),SE0001192600,Handelsbanken,,,2742 -Handelsbanken Multi Asset 120 (A1 SEK),SE0009697170,Handelsbanken,,,951762 -Handelsbanken Multi Asset 25 (A1 SEK),SE0005851318,Handelsbanken,,,519995 -Handelsbanken Multi Asset 50 (A1 SEK),SE0001192618,Handelsbanken,,,2743 -Handelsbanken Multi Asset 60 (A1 SEK),SE0009697147,Handelsbanken,,,1297679 -Handelsbanken Multi Asset 75 ( A1 SEK),SE0002979245,Handelsbanken,,,198168 -Handelsbanken Norden Ind Crit (A1 SEK),SE0010127894,Handelsbanken,,,791753 -Handelsbanken Norden Selektiv,SE0005991700,Handelsbanken,,,512557 -Handelsbanken Norden Tema (A1 SEK),SE0000356230,Handelsbanken,,,1632 -Handelsbanken Nordiska Småbol (A1 SEK),SE0000522724,Handelsbanken,,,1682 -Handelsbanken Norge Tema (A1 SEK),SE0009696768,Handelsbanken,,,783993 -Handelsbanken Realränte (A1 SEK),SE0003613124,Handelsbanken,,,255288 -Handelsbanken Svenska Småbolag (A1 SEK),SE0000356065,Handelsbanken,,,2006 -Handelsbanken Sverige 100 Ind Cri A1 SEK,SE0003788587,Handelsbanken,,,315116 -Handelsbanken Sverige Index Criteria,SE0001466368,Handelsbanken,,,20993 -Handelsbanken Sverige Selektiv,SE0005965639,Handelsbanken,,,508255 -Handelsbanken Sverige Tema (A1 SEK),SE0000582033,Handelsbanken,,,1959 -Handelsbanken Tillväxtmark Tema (A1 SEK),SE0000429748,Handelsbanken,,,2155 -Handelsbanken Tillväxtmarknad Obl A1 SEK,SE0011336734,Handelsbanken,,,907818 -Handelsbanken USA Ind Crit A1 SEK,SE0004139780,Handelsbanken,,,315115 -HealthInvest Alpha Fund D,SE0013110103,Healthinvest,,,1040039 -Holberg Global A,NO0010073232,Holberg,,,74108 -Holberg Norden A,NO0010072945,Holberg,,,74111 -Holberg Norge A,NO0010073224,Holberg,,,74107 -Holberg Rurik A,NO0010073216,Holberg,,,74110 -Holberg Triton A,NO0010730179,Holberg,,,634355 -HSBC GIF Asia ex Japan Equity AC,LU0165289439,HSBC,,,61548 -HSBC GIF Asia ex Jpn Eq Smlr Coms AC,LU0164939612,HSBC,,,3022 -HSBC GIF Asian Currencies Bond AC,LU0210635099,HSBC,,,440909 -HSBC GIF Brazil Equity AC,LU0196696453,HSBC,,,3141 -HSBC GIF BRIC Markets Equity AC,LU0254981946,HSBC,,,61547 -HSBC GIF Chinese Equity AC,LU0164865239,HSBC,,,3061 -HSBC GIF Euroland Eq Smlr Coms AC,LU0165073775,HSBC,,,3066 -HSBC GIF Frontier Markets AC,LU0666199749,HSBC,,,441286 -HSBC GIF Global EM Bd AC,LU0566116140,HSBC,,,440901 -HSBC GIF Global EM Local Dbt AC,LU0234585437,HSBC,,,262968 -HSBC GIF Global Emerging Mkts Eq AC,LU0164872284,HSBC,,,3070 -HSBC GIF Hong Kong Equity AC,LU0164880469,HSBC,,,3062 -HSBC GIF Indian Equity AC,LU0164881194,HSBC,,,3063 -HSBC GIF Mexico Equity AC,LU0877824093,HSBC,,,440935 -HSBC GIF Multi-Asset Style Factors AC,LU1827011492,HSBC,,,960930 -HSBC GIF RMB Fixed Income AC,LU0692309627,HSBC,,,440975 -HSBC GIF Thai Equity AC,LU0210636733,HSBC,,,35169 -HSBC GIF Turkey Equity AC,LU0213961682,HSBC,,,32347 -Humle Småbolagsfond A,SE0002229658,ISEC Services AB,,,209089 -Humle Sverigefond,SE0002229641,ISEC Services AB,,,209088 -IKC 0-100,SE0003116748,IKC,,,214730 -IKC Avkastningsfond,SE0004544823,IKC,,,346935 -IKC Fastighetsfond A,SE0006600243,IKC,,,551035 -IKC Global Flexibel,SE0004951408,IKC,,,416997 -IKC Global Infrastructure A,SE0008434948,IKC,,,698234 -IKC Strategifond,SE0004951432,IKC,,,417020 -IKC Sverige Flexibel,SE0003116730,IKC,,,214448 -Impact Credit Opportunity Fund B,SE0016829774,ISEC Services AB,,,1310139 -Indecap Guide Q30 C,SE0010441352,Indecap,,,804453 -Invesco Active Mlt-Sect Crdt A EUR Acc,LU0102737144,Invesco,,,1703 -Invesco Asia Consumer Demand A USD Acc,LU0334857355,Invesco,,,227292 -Invesco Asia Opportunities Eq A USD Acc,LU0075112721,Invesco,,,168 -Invesco Asian Equity A USD AD,LU1775951525,Invesco,,,886613 -Invesco Asian Inv Grd Bd A $ Acc,LU0794788652,Invesco,,,965039 -Invesco Balanced-Risk Allc A SEKH Acc,LU0955867832,Invesco,,,435455 -Invesco Belt and Road Debt A SEKH Acc,LU1934327435,Invesco,,,965036 -Invesco Bond A USD SD,LU1775947762,Invesco,,,891313 -Invesco China Focus Equity A USD Acc,LU0717748643,Invesco,,,404618 -Invesco Cnttl Eur Sm Cp Eq A USD AD,LU1775961243,Invesco,,,882677 -Invesco Dev Sm and MidCap Eq A USD Acc,LU0607512778,Invesco,,,317658 -Invesco Developing Mkts Eq A USD Acc,LU2014290212,Invesco,,,1006230 -Invesco Em Mkts Select Eq A USD AD,LU1775958025,Invesco,,,882674 -Invesco Emer Mkt Lcl Dbt A USD Acc,LU2014293232,Invesco,,,1006233 -Invesco Emerging Markets Bond A SEKH Acc,LU1775954974,Invesco,,,891319 -Invesco Emerging Markets Bond A USD SD,LU1775955864,Invesco,,,891318 -Invesco Emerging Mkt Corp Bd A SEKH Acc,LU0955867592,Invesco,,,435458 -Invesco Energy Trnstn Fd A Acc,LU0123357419,Invesco,,,1252020 -Invesco Euro Corporate Bond A EUR Acc,LU0243957825,Invesco,,,71104 -Invesco Euro Equity A EUR Acc,LU1240328812,Invesco,,,616837 -Invesco Glbl Consmr Trnds A USD Acc,LU0052864419,Invesco,,,1252021 -Invesco Global Equity A USD Acc,LU2014312123,Invesco,,,1006232 -Invesco Global Equity Income A USD Acc,LU0607513230,Invesco,,,317657 -Invesco Global Focus Eq A USD Acc,LU2014315142,Invesco,,,1006231 -Invesco Global High Income A SEKH Acc,LU1775968842,Invesco,,,891317 -Invesco Global High Income A USD SD,LU1775969659,Invesco,,,891316 -Invesco Global Hlth Care Innovt A USD AD,LU1775982595,Invesco,,,1252019 -Invesco Global Small Cap Eq A USD AD,LU1775975201,Invesco,,,882676 -Invesco Global Targeted Ret A EUR Acc,LU1004132566,Invesco,,,462416 -Invesco Global Thmtc Inovt Eq A USD Acc,LU1603798981,Invesco,,,1252022 -Invesco Global Ttl Ret EUR Bd A SEKH Acc,LU0955867675,Invesco,,,435457 -Invesco Gold & Special Mirls A USD Acc,LU0505655562,Invesco,,,250171 -Invesco Greater China Equity A USD Acc,LU0048816135,Invesco,,,94 -Invesco India Bond A USD Acc,LU0996662002,Invesco,,,965038 -Invesco India Equity A USD Acc,LU1642786112,Invesco,,,965034 -Invesco India Equity A USD AD,LU0267983889,Invesco,,,70759 -Invesco Macro Allc Strat A EUR Acc,LU1233164364,Invesco,,,965037 -Invesco Nippon Small/Md Cp Eq A JPY Acc,LU0028119526,Invesco,,,965040 -Invesco Pacific Equity A USD AD,LU1775963454,Invesco,,,882675 -Invesco Pan European Equity A EUR Acc,LU0028118809,Invesco,,,621 -Invesco Pan European Focus Eq A EUR Acc,LU0642795305,Invesco,,,404610 -Invesco Pan European Sm Cp Eq A EUR Acc,LU0028119013,Invesco,,,942 -Invesco Pan Eurp Struct Rspnb Eq AEURAcc,LU0194779913,Invesco,,,70541 -Invesco PRC Equity A USD AD,LU1775965582,Invesco,,,882673 -Invesco Responsible Glb Rl Asts A $ Acc,LU1775976944,Invesco,,,1252023 -Invesco Responsible Glb Rl Asts A €H Acc,LU1775976605,Invesco,,,891315 -Invesco Rspnb EM Innv Eq A USD Acc,LU2014292853,Invesco,,,1006234 -Invesco Rspnb Jpn Eq Val Discv A JPY,LU0607515367,Invesco,,,317660 -Invesco Rspnb Jpn Eq Val Discv A USD AD,LU0607515102,Invesco,,,326531 -Invesco Sus Pan Eurp Stu Eq A € Acc,LU0119750205,Invesco,,,70356 -Invesco Sust EM Struct Eq A USD Acc,LU0505655729,Invesco,,,378573 -Invesco Sust Euro Struct Eq A EUR Acc,LU1290959623,Invesco,,,616839 -Invesco Sust Glbl Struct Eq A USD AD,LU0267984937,Invesco,,,70737 -Invesco Sust US Struct Eq A USD Acc,LU0149503202,Invesco,,,71134 -Invesco Sustainable Allc A Acc EUR,LU1701702372,Invesco,,,965035 -Invesco US Value Equity A USD Acc,LU0607513826,Invesco,,,317656 -Investerum Basic Value,SE0006028064,Investerum AB,,,1044018 -Investerum Equity Value,SE0017160898,Investerum AB,,,1358060 -Investtech Invest D,SE0014704854,Investtech Invest AS,,,1217309 -INVL Baltic Fund,LTIF00000096,INVL,,,254209 -INVL Emerging Europe Bond,LTIF00000468,INVL,,,414066 -INVL Global Emerging Markets Bond,LTIF00000666,INVL,,,901674 -Janus Henderson Hrzn Asian Growth A2 USD,LU0011890851,Janus Henderson,,,12266 -Janus Henderson Hrzn AsPac Pty IncA2USD,LU0229494975,Janus Henderson,,,70560 -Janus Henderson Hrzn CHN Opps A2 USD,LU0327786744,Janus Henderson,,,117408 -Janus Henderson Hrzn EM Corp Bd A2 USD,LU1120394736,Janus Henderson,,,616838 -Janus Henderson Hrzn Euro Corp Bd A2 EUR,LU0451950314,Janus Henderson,,,244836 -Janus Henderson Hrzn Euro Corp Bd A2HSEK,LU0642280134,Janus Henderson,,,353433 -Janus Henderson Hrzn Euro HY Bd A2 EUR,LU0828815570,Janus Henderson,,,506277 -Janus Henderson Hrzn GlblPtyEqsA2USD,LU0209137388,Janus Henderson,,,70768 -Janus Henderson Hrzn GlbTechLdrs A2USD,LU0070992663,Janus Henderson,,,12268 -Janus Henderson Hrzn Japan Opps A2 USD,LU0011889929,Janus Henderson,,,12275 -Janus Henderson Hrzn Pan Eurp Eq A2 EUR,LU0138821268,Janus Henderson,,,12270 -Janus Henderson Hrzn PanEurpAbsRetA2 EUR,LU0264597617,Janus Henderson,,,70670 -Janus Henderson Hrzn PanEurpPtyEqs A2EUR,LU0088927925,Janus Henderson,,,12267 -Janus Henderson Hrzn PanEurpSmrComs A2€,LU0046217351,Janus Henderson,,,12274 -Janus HndrsnContinen Eurp A2 EUR Acc,LU0201071890,Janus Henderson,,,15208 -Janus HndrsnEmerging Markets A2 USD,LU0201073169,Janus Henderson,,,15209 -Janus HndrsnGlbl Equity A2 USD,LU0200076999,Janus Henderson,,,15185 -Janus HndrsnPan Eurp A2 EUR,LU0201075453,Janus Henderson,,,15211 -Janus HndrsnPan Eurp S&M Cp A2 EUR,LU0201078713,Janus Henderson,,,15212 -JPM Africa Equity A perf (acc) USD,LU0355584466,JP Morgan,,,173520 -JPM ASEAN Equity A (acc) USD,LU0441851309,JP Morgan,,,244488 -JPM Asia Pacific Equity A (dist) USD,LU0613488591,JP Morgan,,,435324 -JPM Brazil Equity A (acc) USD,LU0318934451,JP Morgan,,,123990 -JPM China A (dist) USD,LU0051755006,JP Morgan,,,73 -JPM China A-Share Opps A (acc) USD,LU1255011170,JP Morgan,,,1088802 -JPM Climate Change Solu A (acc) - USD,LU2394011477,JP Morgan,,,1324839 -JPM Em Mkts Small Cap A (acc) perf USD,LU0318931358,JP Morgan,,,131309 -JPM Emerging Markets Equity A (acc) USD,LU0210529656,JP Morgan,,,114006 -JPM Emerging Middle East Eq A (dist) USD,LU0083573666,JP Morgan,,,2158 -JPM Euroland Equity A (dist) EUR,LU0089640097,JP Morgan,,,973 -JPM Europe Dynamic A (dist) EUR,LU0119062650,JP Morgan,,,118348 -JPM Europe Strategic Growth A (dist) EUR,LU0107398538,JP Morgan,,,630 -JPM Global Corp Bd A (acc) SEK H,LU0442991351,JP Morgan,,,190708 -JPM Global Focus A (acc) EUR,LU0210534227,JP Morgan,,,33233 -JPM Global Healthcare A (acc) USD,LU0432979614,JP Morgan,,,196928 -JPM Global Natural Resources A (acc) EUR,LU0208853274,JP Morgan,,,33231 -JPM Global Strat Bd A perf (acc) SEKH,LU0536148769,JP Morgan,,,252931 -JPM Greater China A (dist) USD,LU0117841782,JP Morgan,,,109 -JPM Income Opp A perf (acc) SEKH,LU0323456383,JP Morgan,,,184348 -JPM India A (dist) USD,LU0058908533,JP Morgan,,,76 -JPM Japan Equity A (acc) JPY,LU0235639324,JP Morgan,,,57027 -JPM Japan Sustainable Equity A acc JPY,LU0070214613,JP Morgan,,,1316 -JPM Korea Equity A (acc) USD,LU0301634860,JP Morgan,,,100848 -JPM Latin America Equity A (acc) USD,LU0210535034,JP Morgan,,,33236 -JPM Pacific Equity A (dist) USD,LU0052474979,JP Morgan,,,1166955 -JPM Taiwan A (acc) USD,LU0210528419,JP Morgan,,,33211 -JPM US Select Equity A (acc) USD,LU0070214290,JP Morgan,,,1556 -JPM US Smaller Companies A (acc) USD,LU0210528922,JP Morgan,,,113999 -JPM US Technology A (acc) USD,LU0210536867,JP Morgan,,,951763 -Jupiter Asia Pac Inc L USD Acc,LU0231116830,Jupiter,,,92867 -Jupiter Dynamic Bd L SEK Hsc Acc,LU1074970135,Jupiter,,,766777 -Jupiter Dynamic Bond ESG L SEK Acc HSC,LU2411723179,Jupiter,,,1340059 -Jupiter European Growth L EUR Acc,LU0260085492,Jupiter,,,92871 -Jupiter Financial Innovt L USD Acc,LU0262307720,Jupiter,,,119590 -Jupiter Glbl Em Mkt Crp Bd L SEK Acc HSC,LU1551065730,Jupiter,,,1193602 -Jupiter Global Elgy Div L EUR Acc,LU1388737576,Jupiter,,,819513 -Jupiter Global Elgy Gr L EUR Acc,LU0231118026,Jupiter,,,119488 -Jupiter Global Equity Gr Unc L USD Acc,LU2223789319,Jupiter,,,1232059 -Jupiter Global Sus Eq (LUX) L USD Acc,LU2373436620,Jupiter,,,1282299 -Jupiter Global Value L USD Acc,LU0425094421,Jupiter,,,766778 -Jupiter India Select L USD A Inc,LU0365089902,Jupiter,,,212068 -Jupiter Japan Select L EUR Acc,LU0425092995,Jupiter,,,319516 -Jupiter Pan Eurp Smlr Coms L EUR Acc,LU2091609276,Jupiter,,,1054935 -Kvartil Investmentbolag+ Calculus A,SE0013121456,Quartile Fonder,,,1271439 -Kvartil Multi Asset A,SE0015194030,Quartile Fonder,,,1271459 -Lancelot Avalon A,SE0004841195,Lancelot,,,378116 -Lancelot Avalon B,SE0014704805,Lancelot,,,1150169 -Lancelot Camelot B,SE0009241649,Lancelot,,,716053 -Lancelot Ector Master Fund SB,LU0085329745,Lancelot,,,201630 -Lannebo Corporate Bond A,SE0004750396,Lannebo,,,372353 -Lannebo Europa Småbolag A SEK,SE0008092290,Lannebo,,,685395 -Lannebo Fastighetsfond A SEK,SE0016609663,Lannebo,,,1267599 -Lannebo Fastighetsfond Select A SEK,SE0016797823,Lannebo,,,1278799 -Lannebo High Yield,SE0006421855,Lannebo,,,536695 -Lannebo Mixfond,SE0000740706,Lannebo,,,291 -Lannebo Mixfond Offensiv,SE0005034949,Lannebo,,,415690 -Lannebo Norden Hållbar SEK,SE0011311281,Lannebo,,,862035 -Lannebo Räntefond Kort,SE0000865768,Lannebo,,,2069 -Lannebo Småbolag,SE0000740698,Lannebo,,,2014 -Lannebo Sustainable Corporate Bond A SEK,SE0013646866,Lannebo,,,1131558 -Lannebo Sverige,SE0000740680,Lannebo,,,1971 -Lannebo Sverige Hållbar A SEK,SE0011973684,Lannebo,,,926113 -Lannebo Sverige Plus,SE0002686584,Lannebo,,,155953 -Lannebo Teknik,SE0000740672,Lannebo,,,2338 -Lannebo Teknik Småbolag,SE0013236049,Lannebo,,,1011177 -Legg Mason CB US Agrsv Gr A SEKH Acc,IE00B8Y41X38,Franklin Templeton,,,427235 -LMM – Kavaljer Investmentbolagsfond A,LU1777968246,Kavaljer,,,852914 -LMM – Kavaljer Quality Focus A SEK,LU1232457504,Kavaljer,,,659795 -Lucy Global Fund A,SE0016075188,AIFM Capital AB,,,1244539 -Lundmark Climate Impact Fund,SE0001919143,Lundmark,,,63727 -LUX MultiManager SICAV-Eur Eq Sel B SEK,LU1972727918,BIL Manage Invest S.A,,,1096712 -Lynx Active Balanced Fund D1 SEK,IE00BGBVCQ33,Lynx,,,937914 -Lynx Dynamic,SE0002866772,Lynx,,,192388 -Länsförsäkringar Asienfond A,SE0000837056,Länsförsäkringar,,,146 -Länsförsäkringar Europa Aktiv A,SE0000837353,Länsförsäkringar,,,573 -Länsförsäkringar Europa Indexnära,SE0002793935,Länsförsäkringar,,,181109 -Länsförsäkringar Fastighet Europa,SE0010739912,Länsförsäkringar,,,839253 -Länsförsäkringar Fastighetsfond A,SE0000837338,Länsförsäkringar,,,350 -Länsförsäkringar Global Indexnära,SE0005188836,Länsförsäkringar,,,417655 -Länsförsäkringar Global KlimatIndex,SE0010739920,Länsförsäkringar,,,827673 -Länsförsäkringar Global Vision A,SE0000837205,Länsförsäkringar,,,2253 -Länsförsäkringar Japan Indexnära,SE0000837296,Länsförsäkringar,,,1268 -Länsförsäkringar Kort räntefond,SE0000837247,Länsförsäkringar,,,2084 -Länsförsäkringar Lång Räntefond A,SE0000837254,Länsförsäkringar,,,2088 -Länsförsäkringar Mix A,SE0000837197,Länsförsäkringar,,,236 -Länsförsäkringar Multistrategi,SE0008962914,Länsförsäkringar,,,692753 -Länsförsäkringar Småbolag Sverige Vis A,SE0000837239,Länsförsäkringar,,,2011 -Länsförsäkringar Sverige Indexnära,SE0002656611,Länsförsäkringar,,,159608 -Länsförsäkringar Sverige Vision A,SE0000837221,Länsförsäkringar,,,1910 -Länsförsäkringar Tillväxtmarknad Aktiv A,SE0000837213,Länsförsäkringar,,,2156 -Länsförsäkringar Tillväxtmrkd Idxnära A,SE0005796331,Länsförsäkringar,,,487076 -Länsförsäkringar USA Aktiv A,SE0000837262,Länsförsäkringar,,,1482 -Länsförsäkringar USA Indexnära,SE0002793943,Länsförsäkringar,,,181108 -Mirae Asset Asia Great Consumer Eq A USD,LU0593848301,Mirae,,,374855 -Mirae Asset China Sector Leader Eq A USD,LU0336295752,Mirae,,,202408 -Mirae Asset GEM Sector Leader Eq A USD,LU0413497198,Mirae,,,204694 -Mirae Asset Korea Equity A USD,LU0336294433,Mirae,,,202315 -Mirae Asset Sst India Sect Ldr Eq A USD,LU0336297295,Mirae,,,340280 -Mirae Asset Sustainable AsiaPac Eq A USD,LU0336300859,Mirae,,,202311 -Mirae Asst Sst Asia Sect Ldr Eq A USD,LU0336299408,Mirae,,,431056 -MS INVF Asia Opportunity A,LU1378878430,Morgan Stanley,,,691674 -MS INVF Developing Opportunity A,LU2091680145,Morgan Stanley,,,1214803 -MS INVF Emerging EMEA Equity A,LU0118140002,Morgan Stanley,,,2191 -MS INVF Emerging Leaders Equity A,LU0815263628,Morgan Stanley,,,370857 -MS INVF Emerging Mkts Corp Debt A,LU0603408039,Morgan Stanley,,,289817 -MS INVF Emerging Mkts Domstc Dbt A USD,LU0283960077,Morgan Stanley,,,84671 -MS INVF Emerging Mkts FI Opps A,LU1258507315,Morgan Stanley,,,691677 -MS INVF Europe Opportunity A,LU1387591305,Morgan Stanley,,,691675 -MS INVF European Fixed Inc Opps A,LU1109965605,Morgan Stanley,,,590078 -MS INVF European HY Bd A,LU0073255761,Morgan Stanley,,,909 -MS INVF Global Asset Backed Secs A USD,LU0858068074,Morgan Stanley,,,396355 -MS INVF Global Balanced Income A,LU1078119317,Morgan Stanley,,,590079 -MS INVF Global Balanced Sus A EUR Acc,LU2135297146,Morgan Stanley,,,1214804 -MS INVF Global Brands A USD,LU0119620416,Morgan Stanley,,,71318 -MS INVF Global Fixed Income Opps A,LU0694238766,Morgan Stanley,,,330057 -MS INVF Global High Yield Bond A,LU0702163295,Morgan Stanley,,,326700 -MS INVF Global Infrastructure A,LU0384381660,Morgan Stanley,,,240788 -MS INVF Global Insight A,LU0868753731,Morgan Stanley,,,409832 -MS INVF Global Opportunity A,LU0552385295,Morgan Stanley,,,273630 -MS INVF Global Property A USD,LU0266114312,Morgan Stanley,,,70668 -MS INVF Global Quality A,LU0955010870,Morgan Stanley,,,431316 -MS INVF Global Sustain A USD Acc,LU1842711845,Morgan Stanley,,,1214805 -MS INVF Indian Equity A,LU0266115632,Morgan Stanley,,,84669 -MS INVF Latin American Equity A,LU0073231317,Morgan Stanley,,,70220 -MS INVF Sustainable Asia Equity A,LU0073229253,Morgan Stanley,,,70879 -MS INVF Sustainable Emerging Mkts Eq A,LU0073229840,Morgan Stanley,,,70206 -MS INVF Sustainable Euro Corp Bd A,LU2198663994,Morgan Stanley,,,1214806 -MS INVF US Advantage A,LU0225737302,Morgan Stanley,,,84666 -MS INVF US Dollar High Yield Bond A,LU1134226965,Morgan Stanley,,,590080 -MS INVF US Dollar Short Dur HY Bd A,LU1134225132,Morgan Stanley,,,590081 -MS INVF US Growth A USD,LU0073232471,Morgan Stanley,,,70331 -MS INVF US Property A,LU0073233958,Morgan Stanley,,,70211 -Naventi Balanserad Flex,SE0004357051,Naventi Fonder AB,,,331222 -Naventi Defensiv Flex,SE0004545747,Naventi Fonder AB,,,346775 -Naventi Global Företagsobligationsfond,SE0010440941,Naventi Fonder AB,,,1198824 -Naventi Offensiv Flex,SE0004357069,Naventi Fonder AB,,,331224 -Navigera Aktie 1,SE0002278770,Navigera,,,165269 -Navigera Balans 1,SE0002278754,Navigera,,,165268 -Navigera Global Change,SE0008406219,Navigera,,,661875 -Navigera Tillväxt 1,SE0002278739,Navigera,,,165270 -Nordea 1 - Alpha 10 MA Fd HB SEK,LU0445386443,Nordea,,,202870 -Nordea 1 - Asian Stars Eq Fd BP - SEK,LU2206517380,Nordea,,,1132203 -Nordea 1 - Asset All Fd Fut Sub-Fd C SEK,LU0087941117,Nordea,,,208 -Nordea 1 - Chinese Bd BP SEK,LU1224685344,Nordea,,,1038778 -Nordea 1 - Emerging Mkt Corp Bd HB SEK,LU0637305300,Nordea,,,383302 -Nordea 1 - Emerging Stars Equity BP SEK,LU0602540527,Nordea,,,302887 -Nordea 1 - European Corporate Bd HB SEK,LU0402780950,Nordea,,,156028 -Nordea 1 - European Covered Bond HB SEK,LU1548767661,Nordea,,,949324 -Nordea 1 - European High Yld Bd HB SEK,LU0390857802,Nordea,,,155010 -Nordea 1 - European Sm&Mid Cp Eq BP SEK,LU0087942271,Nordea,,,972 -Nordea 1 - European Stars Equity BP SEK,LU1706106959,Nordea,,,1148786 -Nordea 1 - Flexible Fixed Income HB SEK,LU0915366768,Nordea,,,680575 -Nordea 1 - Gl Climate and Soc Imp BP SEK,LU2355687729,Nordea,,,1295739 -Nordea 1 - Glb Gender Diversity BP SEK,LU1939215585,Nordea,,,950780 -Nordea 1 - Global Disruption BP SEK,LU1941812726,Nordea,,,1107932 -Nordea 1 - Global Listed Infrastr BP SEK,LU2057980059,Nordea,,,1047826 -Nordea 1 - Global Small Cap BP SEK,LU1029331920,Nordea,,,470488 -Nordea 1 - Global Social Empow BP SEK,LU2293905118,Nordea,,,1214802 -Nordea 1 - Global Stable Equity BP SEK,LU0236490636,Nordea,,,419901 -Nordea 1 - Latin American Equity BP SEK,LU0309470028,Nordea,,,420143 -Nordea 1 - Long Duration US Bd BP SEK,LU2017792438,Nordea,,,974382 -Nordea 1 - Low Dur Eurp Cov Bd HB SEK,LU1694213312,Nordea,,,950781 -Nordea 1 - US Corporate Bond HB SEK,LU0458980835,Nordea,,,219388 -Nordea 1 - US High Yield Bond HB SEK,LU0390858958,Nordea,,,155009 -Nordea Asian Stars A SEK,FI4000048970,Nordea,,,409833 -Nordea Global Dividend Acc,FI4000064076,Nordea,,,430355 -Nordea Global Passiv Tillväxt SEK,FI4000046685,Nordea,,,380137 -Nordea Indienfond,FI0008813241,Nordea,,,43947 -Nordea Innovation Stars Fund AG-SEK,FI4000439625,Nordea,,,1120416 -Nordea Japan,FI4000048962,Nordea,,,390917 -Nordea Kinafond,FI0008813308,Nordea,,,26767 -Nordea Klimatfond BP SEK,LU0348926527,Nordea,,,121011 -Nordea Nordamerika,FI0008813381,Nordea,,,1561 -Nordea Obligationsfond icke-utd,SE0000427825,Nordea,,,2075 -Nordea Småbolagsfond Sverige,SE0003653302,Nordea,,,276112 -Nordea Stabil,SE0001666249,Nordea,,,40567 -Nordea Stabila Aktier Tillväxtmarknader,LU0637346080,Nordea,,,420192 -Nordea Sverige Passiv icke-utd,SE0002591016,Nordea,,,140888 -Nordea Swedish Ideas Equity,FI4000088000,Nordea,,,823613 -Nordea Swedish Stars icke-utd,SE0000625238,Nordea,,,1960 -Nordic Cross Bear,SE0006426086,Nordic Cross Asset Management,,,551876 -Nordic Cross Bull,SE0006426219,Nordic Cross Asset Management,,,551875 -Nordic Cross Credit Edge A,SE0011204643,Nordic Cross Asset Management,,,873613 -Nordic Cross Small Cap Edge R SEK,LU1587858033,Nordic Cross Asset Management,,,817393 -Nordic Cross Stable Return R SEK,LU1587859866,Nordic Cross Asset Management,,,817893 -Nordic Cross Total Return R SEK,LU1587867968,Nordic Cross Asset Management,,,817894 -NorQuant ESG D,SE0016038723,NorQuant Kapitalforvaltning AS,,,1243379 -NorQuant Multi Asset D,SE0016038681,NorQuant Kapitalforvaltning AS,,,1243399 -Norron Active RC SEK,LU0619829491,Norron,,,313047 -Norron Select R,LU0580532280,Norron,,,273829 -Norron Sustainable Equity RC SEK Acc,LU1982817865,Norron,,,1142574 -Norron Sustainable Premium RC,LU0772833314,Norron,,,365049 -Norron Sustainable Preserve R SEK,LU0580530409,Norron,,,273830 -Norron Target R,LU0580531472,Norron,,,273828 -ODIN Bærekraft C SEK,NO0011151789,Odin,,,1327039 -ODIN Emerging Markets C SEK,NO0010923584,Odin,,,1276720 -ODIN Europa C SEK,NO0010923709,Odin,,,1276723 -ODIN Fastighet C SEK,NO0010923477,Odin,,,1276725 -ODIN Global C SEK,NO0010923824,Odin,,,1276726 -ODIN Norden C SEK,NO0010923931,Odin,,,1276719 -ODIN Norge C SEK,NO0010924046,Odin,,,1276721 -ODIN Small Cap C SEK,SE0013668183,Odin,,,1039468 -ODIN Sverige C SEK,NO0010924764,Odin,,,1276722 -ODIN USA C SEK,NO0010924848,Odin,,,1276724 -Origo Quest 1 C,SE0006452967,Origo,,,696433 -ORIGO SELEQT C,SE0017564628,Origo,,,1369439 -Pareto Aksje Norge B,NO0010297898,Pareto Asset Management,,,202568 -Pareto ESG Global Corporate Bd A,LU1199945780,Pareto Asset Management,,,552855 -Pareto Global B,NO0010660434,Pareto Asset Management,,,381075 -Pareto Nordic Corporate Bond C,LU0922130561,Pareto Asset Management,,,417084 -Pareto Nordic Cross Credit R SEK,LU2200513823,Pareto Asset Management,,,1333719 -Pareto Nordic Equity B SEK,LU1653072758,Pareto Asset Management,,,907820 -Penser Dynamic Allocation,SE0012990596,Erik Penser Bank AB,,,1205491 -Penser Sustainable Impact A,SE0009983729,Erik Penser Bank AB,,,806333 -Penser Yield A,SE0012990570,Erik Penser Bank AB,,,1205490 -Pictet - Global Envir Opps P USD,LU0503632282,Pictet,,,460566 -Pictet-Asian Equities Ex Japan R USD,LU0155303752,Pictet,,,187 -Pictet-Biotech R USD,LU0112497440,Pictet,,,459 -Pictet-China Equities R USD,LU0168450194,Pictet,,,118 -Pictet-Clean Energy R USD,LU0280431049,Pictet,,,86287 -Pictet-Digital R USD,LU0101692753,Pictet,,,396 -Pictet-Emerging Local Ccy Dbt R USD,LU0255798364,Pictet,,,159008 -Pictet-Emerging Markets R USD,LU0131726092,Pictet,,,2186 -Pictet-Family R EUR,LU0131725367,Pictet,,,961 -Pictet-Health R USD,LU0188501331,Pictet,,,2983 -Pictet-Indian Equities R USD,LU0177113007,Pictet,,,121 -Pictet-Japanese Equity Opps R JPY,LU0155301624,Pictet,,,1324 -Pictet-Japanese Equity Sel R JPY,LU0176901758,Pictet,,,1333 -Pictet-Nutrition P EUR,LU0366534344,Pictet,,,180868 -Pictet-Premium Brands R EUR,LU0217138725,Pictet,,,20949 -Pictet-Quest Emerg Sust Eq P USD,LU0725973621,Pictet,,,364936 -Pictet-Quest Europe Sust Eqs R EUR,LU0144510053,Pictet,,,721 -Pictet-Security R USD,LU0256846568,Pictet,,,58850 -Plain Capital BronX,SE0004550176,AIFM Capital AB,,,346835 -Plain Capital LunatiX,SE0006345096,AIFM Capital AB,,,535296 -Plain Capital StyX,SE0004550218,AIFM Capital AB,,,346837 -PLUS Allabolag Sverige Index,SE0014991535,FCG,,,1151293 -PLUS Fastigheter Sverige Index,SE0016798805,FCG,,,1277999 -PLUS Hälsovård Sverige Index,SE0016828800,FCG,,,1301399 -PLUS Mikrobolag Sverige Index,SE0010323634,FCG,,,789174 -PLUS Småbolag Sverige Index,SE0010323642,FCG,,,789175 -PLUS Teknologi Sverige Index,SE0016828792,FCG,,,1286005 -PriorNilsson Balans,SE0013486073,Prior&Nilsson,,,1038777 -PriorNilsson Evolve A,SE0014263182,Prior&Nilsson,,,1101836 -PriorNilsson Idea,SE0001599432,Prior&Nilsson,,,106729 -PriorNilsson Realinvest A,SE0005189537,Prior&Nilsson,,,459035 -PriorNilsson Smart Global,SE0009580814,Prior&Nilsson,,,732593 -PriorNilsson Sverige Aktiv A,SE0004636447,Prior&Nilsson,,,377801 -PriorNilsson Yield,SE0001008434,Prior&Nilsson,,,106728 -Proethos Fond,SE0010547778,AIFM Capital AB,,,953015 -Quesada Balanserad A,SE0004357408,Cicero,,,602916 -Quesada Bond Opportunity A,SE0007279609,Cicero,,,595175 -Quesada Global,SE0008737225,Cicero,,,716153 -Quesada Offensiv A,SE0004357416,Cicero,,,602918 -Quesada Ränta,SE0002983544,Cicero,,,655035 -Quesada Sverige,SE0000893307,Cicero,,,556795 -Quesada Vision A,SE0012854172,Cicero,,,1044019 -Quesada Vision B,SE0012854180,Cicero,,,1044020 -Quesada Yield A,SE0004357390,Cicero,,,602917 -Quesada Yield C,SE0013486222,Cicero,,,1041450 -Rhenman Healthcare Equity L/S RC1 SEK,LU0417597712,Rhenman & Partners,,,276448 -Robeco Next Digital Billion D EUR,LU2368226135,Robeco Institutional Asset Mgmt BV,,,1284739 -RobecoSAM Smart Energy Eqs D EUR,LU2145461757,Robeco Institutional Asset Mgmt BV,,,1151003 -RobecoSAM Sustainable Water Eqs D EUR,LU2146190835,Robeco Institutional Asset Mgmt BV,,,1151004 -S-Bank Asset Management 50 A Growth,FI0008803879,FIM,,,20249 -S-Bank Brands Equity A,FI0008801428,FIM,,,504 -S-Bank Emerging Markets ESG Equity A,FI0008810148,FIM,,,48768 -S-Bank Emerging Markets Yield A,FI0008811930,FIM,,,189969 -S-Bank Euro Government Bond Yield A,FI0008800321,FIM,,,390076 -S-Bank Europe Equity A,FI0008801402,FIM,,,20251 -S-Bank Fenno Equity A,FI0008800339,FIM,,,1663 -S-Bank Frontier Markets Equity B,FI4000066725,FIM,,,430715 -S-Bank Green Corporate Bond ESG Yield A,FI0008807367,FIM,,,894874 -S-Bank USA Equity A,FI0008800644,FIM,,,359 -Schroder ISF Asian Opports A Acc USD,LU0106259558,Schroder,,,1166956 -Schroder ISF BRIC A Acc USD,LU0228659784,Schroder,,,41815 -Schroder ISF China A A Acc USD,LU1713307426,Schroder,,,888654 -Schroder ISF Chngng Lfstyls A Acc USD,LU2096785519,Schroder,,,1166962 -Schroder ISF Digital Infras A Acc $,LU2388134095,Schroder,,,1319939 -Schroder ISF Em Mkts A Acc USD,LU0106252389,Schroder,,,2179 -Schroder ISF European Sus Eq A Acc EUR,LU1910162970,Schroder,,,1166960 -Schroder ISF European Value A Acc EUR,LU0161305163,Schroder,,,722 -Schroder ISF Eurp Spec Sits A Acc EUR,LU0246035637,Schroder,,,259164 -Schroder ISF Frntr Mkts Eq A Acc USD,LU0562313402,Schroder,,,300237 -Schroder ISF Glb Clmt Chg Eq A Acc USD,LU0302445910,Schroder,,,111430 -Schroder ISF Glb Convert Bd A Acc USD,LU0351442180,Schroder,,,259057 -Schroder ISF Glb Disruption A Acc USD,LU1910165726,Schroder,,,1173688 -Schroder ISF Glb Em Mkt Opps A Acc USD,LU0269904917,Schroder,,,70789 -Schroder ISF Glbl Engy Tnstn A Acc USD,LU1983299162,Schroder,,,1166958 -Schroder ISF Glbl Hi Yld A Acc SEK,LU0665709670,Schroder,,,316679 -Schroder ISF Glbl Mlt Crdt A Acc SEK Hdg,LU1644498583,Schroder,,,1166963 -Schroder ISF Glbl Sust Fd & Wtr AAccUSD,LU2380233275,Schroder,,,1319940 -Schroder ISF Glbl Sust Gr A Acc USD,LU0557290698,Schroder,,,311567 -Schroder ISF Global Cities A Acc USD,LU0224508324,Schroder,,,1087966 -Schroder ISF Global Energy A Acc USD,LU0256331488,Schroder,,,71285 -Schroder ISF Global Eq Alp A Acc USD,LU0225283273,Schroder,,,259110 -Schroder ISF Global Smlr Coms A Acc USD,LU0240877869,Schroder,,,259136 -Schroder ISF Greater China A Acc USD,LU0140636845,Schroder,,,123 -Schroder ISF Hlthcr Innovt A Acc USD,LU1983299246,Schroder,,,1173690 -Schroder ISF Indian Opports A Acc USD,LU0959626531,Schroder,,,1166959 -Schroder ISF Nordic Smlr Coms A € Acc,LU2412567823,Schroder,,,1375940 -Schroder ISF QEP Glbl Qual A Acc USD,LU0323591593,Schroder,,,259146 -Schroder ISF Smart Mnfctng A Acc USD,LU2097341411,Schroder,,,1173689 -Schroder ISF Strategic Bd A Acc USD,LU0201322137,Schroder,,,259106 -Schroder ISF US Smaller Coms A Acc USD,LU0106261612,Schroder,,,1166964 -SEB Active 20 A,SE0016075816,SEB,,,1304759 -SEB Active 30 A,SE0016075881,SEB,,,1304760 -SEB Active 55 A,SE0016076020,SEB,,,1304761 -SEB Active 80 A,SE0016076095,SEB,,,1304762 -SEB Aktiesparfond,SE0000984130,SEB,,,1886 -SEB Asienfond ex Japan C SEK - Lux,LU2209646558,SEB,,,1151002 -SEB Asienfond ex Japan C USD - Lux,LU0011900676,SEB,,,132 -SEB Asset Selection C SEK - Lux,LU0256625632,SEB,,,73884 -SEB Bioteknikfond C EUR - Lux,LU0385485148,SEB,,,143228 -SEB Bioteknikfond D EUR - Lux utd,LU0118405827,SEB,,,478 -SEB Blandfond Sverige,SE0000500407,SEB,,,209 -SEB Corporate Bond C SEK - Lux,LU0133012632,SEB,,,2545 -SEB Dynamisk Aktiefond,SE0000775348,SEB,,,2384 -SEB Dynamisk Räntefond SEK – Lux,LU0979738498,SEB,,,457577 -SEB Emerging Marketsfond,SE0000984155,SEB,,,2164 -SEB Emerging Marketsfond C USD - Lux,LU0037256269,SEB,,,2136 -SEB Europa Indexnära C EUR - Lux,LU0030164395,SEB,,,1840 -SEB Europafond,SE0000984171,SEB,,,676 -SEB Europafond Småbolag,SE0000433252,SEB,,,929 -SEB Europe Equity Fund C EUR,LU0030166507,SEB,,,728 -SEB European High Yield Fund SEK - Lux,LU0977257640,SEB,,,538155 -SEB Fastighetsfond Norden A,SE0000433096,SEB,,,348 -SEB Företagsobligationsfond A,SE0011644475,SEB,,,945454 -SEB Global Aktiefond A,SE0000434151,SEB,,,2254 -SEB Global Climate Opportunity C EUR,LU0122113094,SEB,,,2368 -SEB Global Climate Opportunity D SEK,LU0845792208,SEB,,,415687 -SEB Global Equal Opportunity C (SEK),LU2364199914,SEB,,,1356639 -SEB Global High Yield C H-SEK,LU0413134395,SEB,,,164908 -SEB Global Indexnära C USD - Lux,LU0389150375,SEB,,,149930 -SEB Globalfond C USD - Lux,LU0030158231,SEB,,,2246 -SEB Korträntefond C EUR - Lux,LU0088160774,SEB,,,838 -SEB Korträntefond C USD - Lux,LU0011924932,SEB,,,1380 -SEB Korträntefond D EUR - Lux utd,LU0397075192,SEB,,,157549 -SEB Korträntefond SEK – Lux utd,LU0961016192,SEB,,,447115 -SEB Likviditetsfond SEK,SE0000577470,SEB,,,2063 -SEB Listed Private Equity C EUR - Lux,LU0385668222,SEB,,,192309 -SEB Listed Private Equity C SEK - LUX,LU0920715884,SEB,,,1161195 -SEB Läkemedelsfond,SE0000984122,SEB,,,473 -SEB Nordamerika Små och Medelstora Bolag,SE0000434268,SEB,,,1599 -SEB Nordamerikafond Småbolag,SE0000432601,SEB,,,1600 -SEB Nordenfond,SE0000984189,SEB,,,1651 -SEB Nordenfond Småbolag C EUR - Lux,LU0385664312,SEB,,,195872 -SEB Nordic Equity Fund C EUR,LU0030165871,SEB,,,1630 -SEB Nordic Equity Fund C SEK,LU1807523151,SEB,,,887013 -SEB Norway Focus Fund C SEK - Lux,LU1612341179,SEB,,,896853 -SEB Obligation Flexibel D SEK - Lux utd,LU0053968599,SEB,,,2021 -SEB Obligationsfond Flexibel C SEK - Lux,LU0053967609,SEB,,,2100 -SEB Obligationsfond SEK,SE0000984072,SEB,,,2122 -SEB Pensionsfond D USD - Lux utd,LU0044480142,SEB,,,235 -SEB Realräntefond D SEK - Lux utd,LU0055809197,SEB,,,2103 -SEB Sverige Expanderad,SE0000984197,SEB,,,1984 -SEB Sverige Indexfond B,SE0009773716,SEB,,,1196130 -SEB Sverige Indexnära A,SE0002593673,SEB,,,150048 -SEB Sverigefond,SE0000775298,SEB,,,1983 -SEB Sverigefond Småbolag,SE0000577389,SEB,,,2013 -SEB Sverigefond Småbolag C/R,SE0000434201,SEB,,,2007 -SEB Sweden Equity C (SEK),LU0047322432,SEB,,,1907 -SEB Swedish Value Fund,SE0001838004,SEB,,,87987 -SEB Teknologifond,SE0000984114,SEB,,,364 -SEB Teknologifond D USD - Lux utd,LU0047324487,SEB,,,366 -SEB US All Cap Fund C USD - Lux,LU0030166176,SEB,,,1462 -SEB USA Indexnära D USD - Lux utd,LU0047323166,SEB,,,1474 -SEB Världenfond,SE0000984098,SEB,,,247 -SEB WWF Nordenfond,SE0000691750,SEB,,,981 -Sensor Sverige Select,SE0002801290,Sensor,,,217470 -She Invest Sweden,SE0006852232,ALFAKRAFT Fonder AB,,,1029789 -Simplicity Fastigheter A,SE0015243258,Simplicity,,,1170517 -Simplicity Företagsobligationer A,SE0004452118,Simplicity,,,342255 -Simplicity Global Corporate Bond A,SE0004926368,Simplicity,,,389820 -Simplicity High Yield A,SE0014555223,Simplicity,,,1125641 -Simplicity Likviditet A,SE0001827692,Simplicity,,,56127 -Simplicity Norden,SE0000988750,Simplicity,,,1660 -Simplicity Småbolag Global,SE0010520403,Simplicity,,,830993 -Simplicity Småbolag Sverige A,SE0009161540,Simplicity,,,702073 -Simplicity Sverige,SE0006453536,Simplicity,,,648075 -SKAGEN Focus A,NO0010735129,Skagen,,,562715 -SKAGEN Global A,NO0008004009,Skagen,,,2435 -SKAGEN Kon-Tiki A,NO0010140502,Skagen,,,2197 -SKAGEN m2 A,NO0010657356,Skagen,,,376316 -SKAGEN Tellus A,NO0010327786,Skagen,,,52229 -SKAGEN Vekst A,NO0008000445,Skagen,,,552 -Skandia Asien,SE0000810756,Skandia Fonder,,,148 -Skandia Balanserad,SE0000810905,Skandia Fonder,,,210 -Skandia Cancerfonden,SE0000432759,Skandia Fonder,,,1895 -Skandia Europa Exponering,SE0000810772,Skandia Fonder,,,581 -Skandia Försiktig,SE0000810731,Skandia Fonder,,,192 -Skandia Global Exponering A,SE0005281847,Skandia Fonder,,,429935 -Skandia Japan Exponering,SE0000810764,Skandia Fonder,,,1263 -Skandia Kapitalmarknadsfond,SE0000810863,Skandia Fonder,,,2109 -Skandia Korträntefond,SE0000810897,Skandia Fonder,,,2058 -Skandia Nordamerika Exponering,SE0005065505,Skandia Fonder,,,411236 -Skandia Norden,SE0004518744,Skandia Fonder,,,347375 -Skandia Realräntefond,SE0000810871,Skandia Fonder,,,2108 -Skandia Småbolag Sverige,SE0000810814,Skandia Fonder,,,2012 -Skandia Sverige Exponering,SE0005065521,Skandia Fonder,,,403595 -Skandia Sverige Hållbar,SE0010547943,Skandia Fonder,,,823614 -Skandia Time Global,SE0001112715,Skandia Fonder,,,373 -Skandia USA,SE0000810798,Skandia Fonder,,,1478 -Skandia Världen,SE0001112723,Skandia Fonder,,,2247 -Skandia Världen Sverige,SE0014554838,Skandia Fonder,,,1148787 -Skandia Världsnaturfonden,SE0000432742,Skandia Fonder,,,1896 -Sparinvest SICAV EM Bonds EUR R,LU1735614239,Sparinvest,,,853413 -Sparinvest SICAV Equitas EUR R,LU0362354549,Sparinvest,,,147684 -Sparinvest SICAV Ethical EM Value EUR R,LU0760183912,Sparinvest,,,383498 -Sparinvest SICAV Ethical Gl Val EUR R,LU0362355355,Sparinvest,,,147674 -Sparinvest SICAV European Value EUR R,LU0264920413,Sparinvest,,,65407 -Sparinvest SICAV Global Et HY EUR R,LU1735613934,Sparinvest,,,813855 -Sparinvest SICAV Global Et HY SEK R H,LU1739245857,Sparinvest,,,813853 -Sparinvest SICAV Global Inv Grd EUR R,LU0264925727,Sparinvest,,,65465 -Sparinvest SICAV Global Value EUR R,LU0138501191,Sparinvest,,,34460 -Sparinvest SICAV Long Danish Bds EUR R,LU0274988251,Sparinvest,,,65466 -Sparinvest SICAV Procedo EUR R,LU0139792278,Sparinvest,,,148529 -Sparinvest SICAV Securus EUR R,LU0139791205,Sparinvest,,,148528 -Spets A,SE0013409463,ISEC Services AB,,,1205492 -Spiltan Aktiefond Investmentbolag,SE0004297927,Spiltan Fonder,,,325406 -Spiltan Aktiefond Småland,SE0002566349,Spiltan Fonder,,,132510 -Spiltan Aktiefond Stabil,SE0001015348,Spiltan Fonder,,,2016 -Spiltan Enkel,SE0012740926,Spiltan Fonder,,,1034185 -Spiltan Globalfond Investmentbolag,SE0008613939,Spiltan Fonder,,,693994 -Spiltan Högräntefond,SE0005798329,Spiltan Fonder,,,471796 -Spiltan Räntefond Sverige,SE0002152140,Spiltan Fonder,,,94867 -Spiltan Småbolagsfond,SE0001015355,Spiltan Fonder,,,1996 -SPP Aktiefond Europa A SEK,SE0000531881,SPP,,,599 -SPP Aktiefond Global A SEK,SE0000671919,SPP,,,2332 -SPP Aktiefond Japan A SEK,SE0000621393,SPP,,,1288 -SPP Aktiefond Stabil A SEK,SE0005224078,SPP,,,436351 -SPP Aktiefond Sverige A SEK,SE0000529992,SPP,,,1950 -SPP Aktiefond USA A SEK,SE0000594111,SPP,,,1505 -SPP Emerging Markets Plus A SEK,SE0008129969,SPP,,,663195 -SPP Emerging Markets SRI A SEK,SE0003455658,SPP,,,266309 -SPP Europa Plus A SEK,SE0010714113,SPP,,,836873 -SPP FRN Företagsobligationsfond A SEK,SE0004807097,SPP,,,377809 -SPP Generation 40-tal,SE0001095845,SPP,,,1040 -SPP Generation 50-tal,SE0001095852,SPP,,,1023 -SPP Generation 60-tal,SE0001095860,SPP,,,1022 -SPP Generation 70-tal,SE0001095878,SPP,,,1024 -SPP Generation 80-tal,SE0000619355,SPP,,,1029 -SPP Global Företagsobligations A,SE0010831578,SPP,,,862036 -SPP Global Multifactor A SEK,SE0011642958,SPP,,,901093 -SPP Global Plus A SEK,SE0008129985,SPP,,,652895 -SPP Global Solutions A SEK,SE0004576452,SPP,,,375216 -SPP Grön Obligationsfond A SEK,SE0006763967,SPP,,,544596 -SPP High Yield Företagsobligation A SEK,SE0013877263,SPP,,,1107135 -SPP Korträntefond SEK,SE0000522500,SPP,,,92827 -SPP Obligationsfond A SEK,SE0000522518,SPP,,,2128 -SPP Sverige Plus A SEK,SE0008964407,SPP,,,694153 -SPP Sverige Småbolag Plus A SEK,SE0014808382,SPP,,,1149492 -SPP USA Plus A SEK,SE0017485360,SPP,,,1377710 -Storebrand Equal Opportunities A2,NO0011030215,Storebrand,,,1341499 -Storebrand Global Multifactor A,NO0010346422,Storebrand,,,708773 -Storebrand Global Value A,NO0008000973,Storebrand,,,2328 -Storebrand Norge A,NO0008000783,Storebrand,,,1360913 -Storebrand Renewable Energy A2,NO0011110264,Storebrand,,,1291159 -Storebrand Smart Cities A2,NO0011030355,Storebrand,,,1285099 -Strand Företagsobligationsfond A,SE0015192638,Strand,,,1162341 -Strand Förmögenhetsfond,SE0001541145,Strand,,,45489 -Strand Småbolagsfond,SE0001928730,Strand,,,64710 -Swedbank Robur Access Asien A,SE0007074117,Swedbank Robur,,,596635 -Swedbank Robur Access Edge Em Mkt A,SE0012428290,Swedbank Robur,,,1020238 -Swedbank Robur Access Edge Europe A,SE0014609103,Swedbank Robur,,,1141039 -Swedbank Robur Access Edge Global A,SE0014429353,Swedbank Robur,,,1141043 -Swedbank Robur Access Edge Japan A,SE0007074091,Swedbank Robur,,,600076 -Swedbank Robur Access Edge Sweden A,SE0013121522,Swedbank Robur,,,1141040 -Swedbank Robur Access Edge USA A,SE0014556304,Swedbank Robur,,,1141041 -Swedbank Robur Access Europa A,SE0007073937,Swedbank Robur,,,592236 -Swedbank Robur Access Global A,SE0007074059,Swedbank Robur,,,600075 -Swedbank Robur Access Mix A,SE0000434359,Swedbank Robur,,,206 -Swedbank Robur Access Sverige A,SE0007074075,Swedbank Robur,,,592235 -Swedbank Robur Access USA A,SE0007074083,Swedbank Robur,,,596636 -Swedbank Robur Asienfond A,SE0000539447,Swedbank Robur,,,134 -Swedbank Robur Bas 100,SE0006219184,Swedbank Robur,,,502796 -Swedbank Robur Bas 25,SE0002135566,Swedbank Robur,,,93828 -Swedbank Robur Bas 50,SE0001285362,Swedbank Robur,,,278 -Swedbank Robur Climate Imp A,SE0015948963,Swedbank Robur,,,1277079 -Swedbank Robur Corp Bond Eur Mix A,SE0010598367,Swedbank Robur,,,818953 -Swedbank Robur Corp Bond Europe A,SE0005506300,Swedbank Robur,,,475797 -Swedbank Robur Corp Bond Europe HY A,SE0011062025,Swedbank Robur,,,1009152 -Swedbank Robur Corp Bond Europe IG A,SE0009805252,Swedbank Robur,,,1141042 -Swedbank Robur Corp Bond Nordic A,SE0016831077,Swedbank Robur,,,1310159 -Swedbank Robur Europafond A,SE0000539454,Swedbank Robur,,,574 -Swedbank Robur Fastighet A,SE0000537763,Swedbank Robur,,,347 -Swedbank Robur Global Emerging Markets A,SE0001912924,Swedbank Robur,,,64529 -Swedbank Robur Global High Dividend A,SE0005249661,Swedbank Robur,,,447023 -Swedbank Robur Global Impact A,SE0011167899,Swedbank Robur,,,853173 -Swedbank Robur Globalfond A,SE0000542979,Swedbank Robur,,,1877 -Swedbank Robur Healthcare A,SE0000639445,Swedbank Robur,,,453 -Swedbank Robur Japanfond A,SE0000539413,Swedbank Robur,,,1264 -Swedbank Robur Kinafond A,SE0001691502,Swedbank Robur,,,41067 -Swedbank Robur Nordenfond,SE0000537722,Swedbank Robur,,,1636 -Swedbank Robur Ny Teknik A,SE0000709123,Swedbank Robur,,,1933 -Swedbank Robur Obligation A,SE0000602260,Swedbank Robur,,,1731 -Swedbank Robur Obligation B,SE0016785885,Swedbank Robur,,,1293119 -Swedbank Robur Obligation Plus A,SE0000543076,Swedbank Robur,,,2077 -Swedbank Robur Realränta A,SE0000987224,Swedbank Robur,,,2123 -Swedbank Robur Räntefond Flexibel A,SE0005731189,Swedbank Robur,,,483960 -Swedbank Robur Räntefond Kort A,SE0000543043,Swedbank Robur,,,2026 -Swedbank Robur Selection 25,SE0011643568,Swedbank Robur,,,907815 -Swedbank Robur Selection 50,SE0011643584,Swedbank Robur,,,907814 -Swedbank Robur Selection 75,SE0011643592,Swedbank Robur,,,907813 -Swedbank Robur Small Cap EM A,SE0011643618,Swedbank Robur,,,933765 -Swedbank Robur Small Cap USA A,SE0012729515,Swedbank Robur,,,1009153 -Swedbank Robur Småbolagsfond Europa A,SE0000542771,Swedbank Robur,,,936 -Swedbank Robur Småbolagsfond Glb A,SE0000539439,Swedbank Robur,,,2241 -Swedbank Robur Småbolagsfond Norden A,SE0000537706,Swedbank Robur,,,1678 -Swedbank Robur Småbolagsfond Sverige A,SE0000602302,Swedbank Robur,,,2008 -Swedbank Robur Sverigefond A,SE0000996233,Swedbank Robur,,,1870 -Swedbank Robur Technology A,SE0000538944,Swedbank Robur,,,363 -Swedbank Robur Transition Energy A,SE0000538969,Swedbank Robur,,,491 -Swedbank Robur Transition Global A,SE0000537680,Swedbank Robur,,,1871 -Swedbank Robur Transition Sweden A,SE0000709016,Swedbank Robur,,,1891 -Swedbank Robur USA A,SE0000539470,Swedbank Robur,,,1476 -Swedish Sustainable Leaders A,SE0005849833,Navigera,,,512875 -Tellus Globala Investmentbolag,SE0004646628,Tellus,,,353095 -Tellus Midas,SE0001891508,Tellus,,,75253 -Templeton Asian Growth A(acc)USD,LU0128522157,Franklin Templeton,,,51 -Templeton Asian Smlr Coms A(acc)USD,LU0390135332,Franklin Templeton,,,157697 -Templeton BRIC A(acc)USD,LU0229945570,Franklin Templeton,,,39649 -Templeton China A(acc)USD,LU0052750758,Franklin Templeton,,,72 -Templeton Em Mkts Smlr Coms A(acc)USD,LU0300738514,Franklin Templeton,,,111428 -Templeton Emerging Markets A(acc)USD,LU0128522744,Franklin Templeton,,,2187 -Templeton Emerging Mkts Bd A(acc)SEK-H1,LU0563142610,Franklin Templeton,,,263949 -Templeton Emerging Mkts Bd A(Qdis)USD,LU0029876355,Franklin Templeton,,,1689 -Templeton Euroland A (Acc) EUR,LU0093666013,Franklin Templeton,,,735 -Templeton Eurp Sm-Mid Cap A(acc)EUR,LU0138075311,Franklin Templeton,,,701 -Templeton Frontier Markets A(acc)USD,LU0390136736,Franklin Templeton,,,157891 -Templeton Glb Climate Change A (acc) EUR,LU0128520375,Franklin Templeton,,,2410 -Templeton Global A(acc)USD,LU0128525929,Franklin Templeton,,,2411 -Templeton Global Bond A(acc)EUR,LU0152980495,Franklin Templeton,,,33107 -Templeton Global Bond A(acc)SEK-H1,LU0476944003,Franklin Templeton,,,212549 -Templeton Global Bond A(Mdis)USD,LU0029871042,Franklin Templeton,,,2482 -Templeton Global Smaller Coms A(acc)USD,LU0128526141,Franklin Templeton,,,2412 -Templeton Global Ttl Ret A(acc)SEK-H1,LU0563142883,Franklin Templeton,,,263948 -Templeton Latin America A(acc)USD,LU0128526570,Franklin Templeton,,,1373 -Tenoris One,SE0014808416,AIFM Capital AB,,,1145781 -Thematica – Future Mobility Retl SEK Acc,LU1814397268,IPConcept (Luxemburg) SA,,,1158131 -Threadneedle (Lux) American Abs Alp AU,LU0515763737,Threadneedle,,,289937 -Threadneedle (Lux) American AU,LU0061475181,Threadneedle,,,289895 -Threadneedle (Lux) American Select AU,LU0112528004,Threadneedle,,,289909 -Threadneedle (Lux) Asia Equity Inc AU,LU0061477393,Threadneedle,,,289981 -Threadneedle (Lux) Enhanced Cmdts AU,LU0515768298,Threadneedle,,,289971 -Threadneedle (Lux) European Select AE,LU0713326832,Threadneedle,,,376718 -Threadneedle (Lux) European Strat Bd AE,LU0096353940,Threadneedle,,,289948 -Threadneedle (Lux) Glb Dyn RlRet AU USD,LU0061474705,Threadneedle,,,289964 -Threadneedle (Lux) Glb Smlr Coms AE,LU0570870567,Threadneedle,,,289925 -Threadneedle (Lux) Glbl Mlt Ast Inc AU,LU0640488648,Threadneedle,,,376808 -Threadneedle (Lux) Global Tech AU USD,LU0444971666,Threadneedle,,,289977 -Threadneedle (Lux) Pan Eurp SmCp Opps AE,LU0282719219,Threadneedle,,,289924 -Threadneedle (Lux) UK Eqs AG GBP Acc,LU0713318490,Threadneedle,,,376714 -Threadneedle (Lux) US Contr Cr Eqs AU,LU0640476718,Threadneedle,,,376791 -Threadneedle (Lux) US Hi Yld Bd AU,LU0180519315,Threadneedle,,,289923 -Threadneedle(Lux) Emerging Mkt Dbt AU,LU0061474614,Threadneedle,,,289918 -Threadneedle(Lux) Gl EM ST Bds AU,LU0198726373,Threadneedle,,,289940 -Threadneedle(Lux) Global Em Mkt Eqs AU,LU0143863198,Threadneedle,,,289989 -Threadneedle(Lux)Pan Eurp ESG Eqs AE EUR,LU0061476155,Threadneedle,,,289902 -TIN Ny Teknik A,SE0012193019,TIN Fonder,,,927735 -Tundra Sustainable Frontier Fund A SEK,SE0004211282,Tundra,,,404075 -UB Asia REIT Plus A,FI4000081120,United Bankers,,,600080 -UB EM Infra A,FI4000081153,United Bankers,,,600078 -UB Europa REIT A,FI4000081187,United Bankers,,,600079 -UB Global REIT A,FI4000081195,United Bankers,,,600077 -UB Infra A,FI4000081211,United Bankers,,,600081 -UB Nordamerika REIT A,FI4000122874,United Bankers,,,600082 -UBS (Lux) BF Global Flex (EUR H) P acc,LU0487186396,UBS,,,235608 -UBS (Lux) BS Asian HY $ P-acc,LU0626906662,UBS,,,345795 -UBS (Lux) BS China Fxd Inc ¥ USD P acc,LU1717043670,UBS,,,1158132 -UBS (Lux) BS Global Dyn $ P-acc,LU0891671751,UBS,,,462915 -UBS (Lux) BS € Corp Sust € P-acc,LU0162626096,UBS,,,1825 -UBS (Lux) EEF Glbl Bd $ P Acc,LU0084219863,UBS,,,82967 -UBS (Lux) EF Biotech (USD) P-acc,LU0069152568,UBS,,,471 -UBS (Lux) EF China Oppo(USD) P SEK acc,LU1620846664,UBS,,,766853 -UBS (Lux) EF China Oppo(USD) P USD acc,LU0067412154,UBS,,,106 -UBS (Lux) EF Glb Sust (USD) P-acc,LU0076532638,UBS,,,2382 -UBS (Lux) EF Greater China (USD) P,LU0072913022,UBS,,,104 -UBS (Lux) EF Mid Caps USA (USD) P,LU0049842262,UBS,,,1542 -UBS (Lux) EF Small Caps USA (USD) P,LU0038842364,UBS,,,1619 -UBS (Lux) EF Tech Opp (USD) P acc,LU0081259029,UBS,,,422 -UBS (Lux) Eq Eurp Opp Sst (EUR) P acc,LU0006391097,UBS,,,674 -UBS (Lux) Eq Fd EM Sst Ldrs (USD) P,LU0106959298,UBS,,,180 -UBS (Lux) ES All China (USD) P acc,LU1807302812,UBS,,,925393 -UBS (Lux) ES As Smlr Coms (USD) P-acc,LU0746413003,UBS,,,376651 -UBS (Lux) ES Euro Countrs Inc Sust €Pacc,LU1121265208,UBS,,,700713 -UBS (Lux) ES Glbl Imp (USD) EUR H P Acc,LU1679117579,UBS,,,934955 -UBS (Lux) ES Glbl Imp (USD) P Acc,LU1679116845,UBS,,,924973 -UBS (Lux) ES SmallCapsEurp SusEURPAcc,LU0198839143,UBS,,,4764 -UBS (Lux) ES US Opp $ P Acc,LU0070848113,UBS,,,1557 -UBS (Lux) ES USA Growth $ P-acc,LU0198837287,UBS,,,4766 -UBS (Lux) KSS Asian Eqs $ USD P Acc,LU0235996351,UBS,,,181850 -UBS (Lux) KSS Eurp Eq Val Opp EUR P-acc,LU0153925689,UBS,,,13286 -UBS (Lux) KSS Glbl Allc (EUR) P,LU0197216558,UBS,,,13291 -UBS European Opportunity Uncons EUR PAcc,LU0723564463,UBS,,,378656 -UBS Global Emerging Market Opp USD P Acc,LU0328353924,UBS,,,259101 -UBS(LUX) ES Glb HD Ss USD P-acc,LU0611173427,UBS,,,376665 -UBS(Lux) Invm CHN A Opp USD P Acc USD,LU0971614614,UBS,,,1158133 -Vinga Corporate Bond A,SE0013775335,AIFM Capital AB,,,1322459 -White Fleet GP Bullhound GblEsportsR SEK,LU2105725381,Finserve,,,1097739 -World xFund Allocation A,SE0015949904,AIFM Capital AB,,,1234480 -Ålandsbanken Euro Bond B,FI0008804992,Ålandsbanken,,,237986 -Ålandsbanken Euro Bond SEK,FI4000087887,Ålandsbanken,,,476836 -Ålandsbanken Euro High Yield B,FI0008809892,Ålandsbanken,,,237975 -Ålandsbanken Europa Aktie B,FI0008805031,Ålandsbanken,,,237981 -Ålandsbanken Global Aktie B,FI0008812607,Ålandsbanken,,,118857 -Ålandsbanken Green Bond ESG B,FI4000387386,Ålandsbanken,,,1009577 -Ålandsbanken Kapitalförvaltning B,FI0008809934,Ålandsbanken,,,237974 -Ålandsbanken Kapitalförvaltning SEK,FI4000087911,Ålandsbanken,,,476838 -Ålandsbanken Kort Företagsränta,FI0008808340,Ålandsbanken,,,237973 -Ålandsbanken Norden Aktie EUR,FI4000123179,Ålandsbanken,,,559855 -Ålandsbanken Norden Aktie SEK,FI4000123245,Ålandsbanken,,,573156 -Ålandsbanken Nordiska Småbolag S,FI4000439450,Ålandsbanken,,,1193260 -Öhman Emerging Markets A,SE0004578615,Öhman,,,364655 -Öhman FRN A,SE0004951283,Öhman,,,388595 -Öhman Företagsobligationsfond A,SE0001095506,Öhman,,,1833 -Öhman Global A,SE0000533945,Öhman,,,510 -Öhman Global Growth A,SE0000493546,Öhman,,,372 -Öhman Global Småbolag A,SE0013041654,Öhman,,,1025147 -Öhman Grön Obligationsfond A,SE0010324384,Öhman,,,795454 -Öhman Hälsa och Ny Teknik A,SE0017130958,Öhman,,,1321459 -Öhman Investment Grade A,SE0004391738,Öhman,,,335015 -Öhman Kort Ränta A,SE0000493504,Öhman,,,2052 -Öhman Marknad Europa A,SE0000577975,Öhman,,,618 -Öhman Marknad Global A,SE0010049197,Öhman,,,848153 -Öhman Marknad Japan A,SE0000577959,Öhman,,,1285 -Öhman Marknad Pacific A,SE0000577983,Öhman,,,167 -Öhman Marknad Sverige A,SE0010049213,Öhman,,,766775 -Öhman Marknad USA A,SE0000577967,Öhman,,,1506 -Öhman Navigator A,SE0011090182,Öhman,,,855014 -Öhman Navigator B,SE0011090190,Öhman,,,909193 -Öhman Obligationsfond A,SE0006338398,Öhman,,,686931 -Öhman Realräntefond A,SE0001472952,Öhman,,,26827 -Öhman Räntefond Kompass A,SE0006887196,Öhman,,,563115 -Öhman Småbolagsfond A,SE0000432775,Öhman,,,2003 -Öhman Sverige A,SE0005281953,Öhman,,,430255 -Öhman Sverige Fokus A,SE0009997091,Öhman,,,855015 -Öhman Sweden Micro Cap A,SE0000432809,Öhman,,,2010 diff --git a/openbb_terminal/mutual_funds/avanza_model.py b/openbb_terminal/mutual_funds/avanza_model.py deleted file mode 100644 index 0a567d95c78..00000000000 --- a/openbb_terminal/mutual_funds/avanza_model.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Avanza Model""" - -__docformat__ = "numpy" - -# pylint: disable=E1101 - -import logging -import os - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_data(isin: str) -> dict: - """Gets the data from Avanza - - Parameters - ---------- - isin: str - ISIN of the fund - - Returns - ------- - dict - Fund data - """ - ava_fund = pd.read_csv( - os.path.join("openbb_terminal", "mutual_funds", "avanza_fund_ID.csv"), - index_col=0, - ) - fund_id = ava_fund.loc[ava_fund["ISIN"] == isin]["ID"].tolist()[0] - url = f"https://www.avanza.se/_api/fund-guide/guide/{fund_id}" - response = request(url) - fund_data = response.json() - return fund_data diff --git a/openbb_terminal/mutual_funds/avanza_view.py b/openbb_terminal/mutual_funds/avanza_view.py deleted file mode 100644 index 134a248bf63..00000000000 --- a/openbb_terminal/mutual_funds/avanza_view.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Avanza View""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.mutual_funds import avanza_model -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -sector_dict = { - "Industri": "Industry", - "Konsument, cyklisk": "Consumer goods, cyclical", - "Finans": "Finance", - "Konsument, stabil": "Consumer goods, stable", - "Sjukvård": "Health Care", - "Teknik": "Technology", - "Fastigheter": "Real Estate", - "Råvaror": "Commodities", - "Kommunikation": "Telecommunication", - "Allmännyttigt": "Utilities", - "Energi": "Energy", -} - - -@log_start_end(log=logger) -def display_allocation(name: str, isin: str, focus: str): - """Displays the allocation of the selected swedish fund - - Parameters - ---------- - name: str - Full name of the fund - isin: str - ISIN of the fund - focus: str - The focus of the displayed allocation/exposure of the fund - """ - # Taken from: https://github.com/northern-64bit/Portfolio-Report-Generator/tree/main - fund_data = avanza_model.get_data(isin) - if focus in ["holding", "all"]: - table_row = [] - console.print("") - for data in fund_data["holdingChartData"]: - table_row_temp = [] - table_row_temp.append(data["name"]) - table_row_temp.append(str(data["y"])) - table_row_temp.append(data["countryCode"]) - table_row.append(table_row_temp) - header = ["Holding", "Allocation in %", "Country"] - holding_data = pd.DataFrame(table_row, columns=header) - print_rich_table(holding_data, title=f"{name}'s Holdings", headers=header) - if focus in ["sector", "all"]: - table_row = [] - console.print("") - for data in fund_data["sectorChartData"]: - table_row_temp = [] - table_row_temp.append(sector_dict[data["name"]]) - table_row_temp.append(str(data["y"])) - table_row.append(table_row_temp) - header = ["Sector", "Allocation in %"] - sector_data = pd.DataFrame(table_row, columns=header) - print_rich_table( - sector_data, title=f"{name}'s Sector Weighting", headers=header - ) - if focus in ["country", "all"]: - table_row = [] - console.print("") - for data in fund_data["countryChartData"]: - table_row_temp = [] - table_row_temp.append(data["countryCode"]) - table_row_temp.append(str(data["y"])) - table_row.append(table_row_temp) - header = ["Country", "Allocation in %"] - country_data = pd.DataFrame(table_row, columns=header) - print_rich_table( - country_data, title=f"{name}'s Country Weighting", headers=header - ) - - -@log_start_end(log=logger) -def display_info(isin: str): - """Displays info of swedish funds - - Parameters - ---------- - isin: str - ISIN of the fund - """ - fund_data = avanza_model.get_data(isin) - text = f"\nSwedish Description:\n\n{fund_data['description']}\n\nThe fund is managed by:\n" - for manager in fund_data["fundManagers"]: - text = text + f"\t- {manager['name']} since {manager['startDate']}\n" - text = ( - text - + f"from {fund_data['adminCompany']['name']}.\nFund currency is {fund_data['currency']}" - f" and it the fund started {fund_data['startDate']}." - ) - text = ( - text + " It is a index fund." - if fund_data["indexFund"] - else text + " It is not a index fund." - ) - text = ( - text - + f" The fund manages {str(fund_data['capital'])} {fund_data['currency']}. The " - f"standard deviation of the fund is {str(fund_data['standardDeviation'])} and the sharpe " - f"ratio is {str(fund_data['sharpeRatio'])}.\n" - ) - console.print(text) diff --git a/openbb_terminal/mutual_funds/mstarpy_model.py b/openbb_terminal/mutual_funds/mstarpy_model.py deleted file mode 100644 index 73d3a259ed2..00000000000 --- a/openbb_terminal/mutual_funds/mstarpy_model.py +++ /dev/null @@ -1,247 +0,0 @@ -import logging - -import mstarpy -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -def get_historical( - loaded_funds: mstarpy.Funds, - start_date: str, - end_date: str, - comparison: str = "", -) -> pd.DataFrame: - """Get historical fund, category, index price - - Parameters - ---------- - loaded_funds: mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - start_date: str - start date of the historical data - end_date: str - end date of the historical data - comparison: str - can be index, category, both - - Returns - ------- - pd.DataFrame - Dataframe containing historical data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> f = openbb.funds.load("Vanguard", "US") - >>> openbb.funds.historical(f, "2020-01-01", "2020-12-31") - """ - try: - start_date_dt = pd.to_datetime(start_date) - end_date_dt = pd.to_datetime(end_date) - if not comparison: - data = loaded_funds.nav(start_date_dt, end_date_dt, frequency="daily") - df = pd.DataFrame(data).set_index("date") - df.index = pd.to_datetime(df.index) - else: - comparison_list = { - "index": [ - "fund", - "index", - ], - "category": ["fund", "category"], - "both": ["fund", "index", "category"], - } - data = loaded_funds.historicalData() - df_dict = {} - for x in comparison_list[comparison]: - df_dict[x] = pd.DataFrame(data["graphData"][x]).set_index("date") - - df = pd.concat( - list(df_dict.values())[:], axis=1, keys=list(df_dict.keys())[:] - ) - df.index = pd.to_datetime(df.index) - df = df.loc[(df.index >= start_date_dt) & (df.index <= end_date_dt)] - df = (df.pct_change().fillna(0) + 1).cumprod() * 100 - df.columns = [col[0] for col in df.columns] - except Exception as e: - console.print(f"Error: {e}") - return pd.DataFrame() - return df - - -@log_start_end(log=logger) -def get_sector(loaded_funds: mstarpy.Funds, asset_type: str = "equity"): - """Get fund, category, index sector breakdown - - Parameters - ---------- - loaded_funds: mstarpy.funds - class mstarpy.Funds instantiated with selected funds - asset_type: str - can be equity or fixed income - - Returns - ------- - pd.DataFrame - Dataframe containing sector breakdown - """ - key = "EQUITY" if asset_type == "equity" else "FIXEDINCOME" - - d = loaded_funds.sector()[key] - - if d: - return pd.DataFrame(d) - return pd.DataFrame() - - -@log_start_end(log=logger) -def load_carbon_metrics(loaded_funds: mstarpy.Funds) -> pd.DataFrame: - """Search mstarpy for carbon metrics - - Parameters - ---------- - loaded_funds: mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - - Returns - ------- - pd.DataFrame - Dataframe containing carbon metrics - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> f = openbb.funds.load("Vanguard", "US") - >>> openbb.funds.carbon(f) - """ - carbonMetrics = loaded_funds.carbonMetrics() - return pd.Series(carbonMetrics, name="carbonMetrics").reset_index() - - -@log_start_end(log=logger) -def load_exclusion_policy(loaded_funds: mstarpy.Funds) -> pd.DataFrame: - """Search mstarpy exclusion policy in esgData - - Parameters - ---------- - loaded_funds: mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - - Returns - ------- - pd.DataFrame - Dataframe containing exclusion policy - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> f = openbb.funds.load("Vanguard", "US") - >>> openbb.funds.exclusion(f) - """ - esgData = loaded_funds.esgData() - if "sustainabilityIntentionality" in esgData: - return pd.Series( - esgData["sustainabilityIntentionality"], name="exclusionPolicy" - ).reset_index() - return pd.DataFrame() - - -@log_start_end(log=logger) -def load_funds( - term: str = "", - country: str = "US", -) -> mstarpy.Funds: - """Search mstarpy for matching funds - - Parameters - ---------- - term : str - String that will be searched for. Can be name or isin - country : str - country where the funds is hosted - - Returns - ------- - mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> f = openbb.funds.load("Vanguard", "US") - """ - return mstarpy.Funds(term, country) - - -@log_start_end(log=logger) -def load_holdings( - loaded_funds: mstarpy.Funds, holding_type: str = "all" -) -> pd.DataFrame: - """Search mstarpy for holdings - - Parameters - ---------- - loaded_funds: mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - - holding_type : str - type of holdings, can be all, equity, bond, other - - Returns - ------- - pd.DataFrame - Dataframe containing holdings - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> f = openbb.funds.load("Vanguard", "US") - >>> openbb.funds.holdings(f) - """ - holdings = loaded_funds.holdings(holding_type) - if holdings.empty: - return pd.DataFrame() - return holdings[["isin", "securityName", "weighting", "country"]] - - -@log_start_end(log=logger) -def search_funds( - term: str = "", - country: str = "", - limit=10, -) -> pd.DataFrame: - """Search mstarpy for matching funds - - Parameters - ---------- - term : str - String that will be searched for. Can be name or isin - field : list - list of field who will be displayed - country : str - country where the funds is hosted - limit : int - length of results to display - - Returns - ------- - pd.DataFrame - Dataframe containing matches - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.funds.search("Vanguard", "US") - """ - field = ["SecId", "TenforeId", "LegalName"] - try: - return pd.DataFrame( - mstarpy.search_funds(term, field, country=country, pageSize=limit) - ) - except RuntimeError as e: - logger.exception(str(e)) - return pd.DataFrame() diff --git a/openbb_terminal/mutual_funds/mstarpy_view.py b/openbb_terminal/mutual_funds/mstarpy_view.py deleted file mode 100644 index 9fde12827c8..00000000000 --- a/openbb_terminal/mutual_funds/mstarpy_view.py +++ /dev/null @@ -1,284 +0,0 @@ -import logging -from typing import Union - -import mstarpy -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.mutual_funds import mstarpy_model -from openbb_terminal.mutual_funds.mutual_funds_utils import mapping_country -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_carbon_metrics(loaded_funds: mstarpy.Funds): - """Display results of carbon metrics - - Parameters - ---------- - loaded_funds: mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - """ - carbonMetrics = mstarpy_model.load_carbon_metrics(loaded_funds) - - print_rich_table( - carbonMetrics, - show_index=False, - title=f"[bold]Carbon metrics of the funds {loaded_funds.name}[/bold]", - ) - - -@log_start_end(log=logger) -def display_exclusion_policy(loaded_funds: mstarpy.Funds): - """Display results of exclusion policy - - Parameters - ---------- - loaded_funds: mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - """ - exclusion_policy = mstarpy_model.load_exclusion_policy(loaded_funds) - - print_rich_table( - exclusion_policy, - show_index=False, - title=f"[bold]Exclusion policy of the funds {loaded_funds.name}[/bold]", - ) - - -@log_start_end(log=logger) -def display_historical( - loaded_funds: mstarpy.Funds, - start_date: str, - end_date: str, - comparison: str = "", - external_axes: bool = False, -): - """Display historical fund, category, index price - - Parameters - ---------- - loaded_funds: mstarpy.funds - class mstarpy.Funds instantiated with selected funds - start_date: str - start date of the period to be displayed - end_date: str - end date of the period to be displayed - comparison: str - type of comparison, can be index, category, both - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> f = openbb.funds.load("Vanguard", "US") - >>> openbb.funds.historical_chart(f) - """ - - title = f"Performance of {loaded_funds.name}" - start_date_dt = pd.to_datetime(start_date) - end_date_dt = pd.to_datetime(end_date) - df = mstarpy_model.get_historical( - loaded_funds, start_date_dt, end_date_dt, comparison - ) - - if df.empty: - return None - - if not comparison: - fig = OpenBBFigure(xaxis_title="Date", yaxis_title="Nav").set_title(title) - fig.add_scatter( - x=df.index, - y=df.nav, - name=f"{loaded_funds.name}", - mode="lines", - ) - - else: - fig = OpenBBFigure(xaxis_title="Date", yaxis_title="Performance (Base 100)") - data = loaded_funds.historicalData() - for col in df.columns: - if col == "fund": - label = f"funds : {loaded_funds.name}" - else: - key = f"{col}Name" - if key in data: - label = f"{col} : {data[key]}" - title += f" vs {label}," - else: - label = col - - fig.add_scatter( - x=df.index, - y=df[col], - name=f"{label}", - mode="lines", - ) - - fig.set_title(title.rstrip(","), wrap=True, wrap_width=70) - fig.update_layout(margin=dict(t=65)) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_holdings(loaded_funds: mstarpy.Funds, holding_type: str = "all"): - """Display results of holdings - - Parameters - ---------- - loaded_funds: mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - holding_type : str - type of holdings, can be all, equity, bond, other - """ - - holdings = mstarpy_model.load_holdings(loaded_funds, holding_type) - if isinstance(holdings, pd.DataFrame): - if holdings.empty: - if holding_type != "all": - console.print(f"The funds does not hold {holding_type} assets.") - else: - console.print("No holdings to be displayed.") - - else: - print_rich_table( - holdings, - show_index=False, - title=f"[bold]{holding_type} holdings of the funds {loaded_funds.name}[/bold]", - ) - - -@log_start_end(log=logger) -def display_load( - term: str = "", - country: str = "", -) -> Union[mstarpy.Funds, None]: - """instantiate mstarpy Funds class and display the funds selected - - Parameters - ---------- - term : str - String that will be searched for - country: str - Country to filter on - - Returns - ------- - mstarpy.Funds - class mstarpy.Funds instantiated with selected funds - """ - iso_country = mapping_country[country] if country else "" - funds = mstarpy_model.load_funds(term, country=iso_country) - if isinstance(funds, mstarpy.Funds): - return funds - - return None - - -@log_start_end(log=logger) -def display_search( - term: str = "", - country: str = "", - limit: int = 10, -): - """Display results of searching for Mutual Funds - - Parameters - ---------- - term : str - String that will be searched for - country: str - Country to filter on - limit: int - Number to show - """ - iso_country = mapping_country[country] if country else "" - searches = mstarpy_model.search_funds(term, country=iso_country, limit=limit) - if searches.empty: - console.print("No matches found.") - return - - title = ( - f"Mutual Funds from {country.title()} matching {term}" - if country - else f"Mutual Funds matching {term}" - ) - - print_rich_table( - searches, - show_index=False, - title=f"[bold]{title}[/bold]", - ) - - -@log_start_end(log=logger) -def display_sector( - loaded_funds: mstarpy.Funds, asset_type: str = "equity", external_axes: bool = False -): - """Display fund, category, index sector breakdown - - Parameters - ---------- - loaded_funds: mstarpy.funds - class mstarpy.Funds instantiated with selected funds - asset_type: str - can be equity or fixed income - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - fig : plotly.graph_objects.Figure - Plotly figure object - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> f = openbb.funds.load("Vanguard", "US") - >>> openbb.funds.sector_chart(f) - """ - - df = mstarpy_model.get_sector(loaded_funds, asset_type) - - if not df.empty: - fig = OpenBBFigure() - - width = -0.3 - - title = "Sector breakdown of" - for x in ["fund", "index", "category"]: - name = df[f"{x}Name"].iloc[0] - data = df[f"{x}Portfolio"].to_dict() - - p_date = data["portfolioDate"] - portfolio_date = p_date[:10] if p_date else "" - data.pop("portfolioDate") - - labels = list(data.keys()) - values = list(data.values()) - - # if all values are 0, skip - values = [0 if v is None else v for v in values] - if sum(values) == 0: - continue - - fig.add_bar( - x=labels, - y=values, - name=f"{x} : {name} - {portfolio_date}", - ) - width += 0.3 # the width of the bars - - title += f" {name}," - - fig.update_layout(margin=dict(t=45), xaxis=dict(tickangle=-15)) - fig.set_title(title.rstrip(","), wrap=True, wrap_width=60) - - return fig.show(external=external_axes) - return None diff --git a/openbb_terminal/mutual_funds/mutual_fund_controller.py b/openbb_terminal/mutual_funds/mutual_fund_controller.py deleted file mode 100644 index 0e928804d42..00000000000 --- a/openbb_terminal/mutual_funds/mutual_fund_controller.py +++ /dev/null @@ -1,440 +0,0 @@ -"""Mutual Fund Controller""" - -__docformat__ = "numpy" - -import argparse -import logging -import os -from datetime import datetime, timedelta -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_FIGURES_ALLOWED, - check_positive, - lower_str, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.mutual_funds import avanza_view, mstarpy_view -from openbb_terminal.mutual_funds.mutual_funds_utils import mapping_country -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class FundController(BaseController): - """Fund Controller class""" - - CHOICES_COMMANDS = [ - "resources", - "country", - "search", - "load", - "plot", - "sector", - "alswe", - "infoswe", - "holdings", - "carbon", - "exclusion", - ] - - fund_countries = list(mapping_country.keys()) - PATH = "/funds/" - FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md") - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.country = "united_states" - self.funds_loaded = None - self.fund_name = "" - self.fund_symbol = "" - self.fund_isin = "" - self.TRY_RELOAD = True - self.end_date = "" - self.start_date = "" - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - choices["country"].update({c: {} for c in self.fund_countries}) - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - if self.fund_name: - fund_string = ( - f"{self.fund_name} ({self.fund_symbol})" - if self.fund_symbol - else f"{self.fund_name}" - ) - else: - fund_string = "" - mt = MenuText("funds/") - mt.add_cmd("country") - mt.add_raw("\n") - mt.add_param("_country", self.country) - mt.add_raw("\n") - mt.add_cmd("search") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_fund", fund_string) - mt.add_raw("\n") - mt.add_cmd("plot", self.fund_symbol) - mt.add_cmd("sector", self.fund_symbol) - mt.add_cmd("holdings", self.fund_symbol) - mt.add_cmd("carbon", self.fund_symbol) - mt.add_cmd("exclusion", self.fund_symbol) - mt.add_cmd("alswe", self.fund_symbol and self.country == "sweden") - mt.add_cmd("infoswe", self.fund_symbol and self.country == "sweden") - - console.print(text=mt.menu_text, menu="Mutual Funds") - - def custom_reset(self): - """Class specific component of reset command""" - if self.fund_symbol and self.country: - return ["funds", f"country {self.country}", f"load {self.fund_symbol}"] - return [] - - @log_start_end(log=logger) - def call_alswe(self, other_args: List[str]): - """Process alswe command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="alswe", - description="Show allocation of a swedish fund. " - "To get a list of available funds, check the file `avanza_fund_ID.csv`.", - ) - parser.add_argument( - "--focus", - dest="focus", - type=str, - choices=["all", "country", "sector", "holding"], - default="all", - help="The focus of the funds exposure/allocation", - ) - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - ava_fund = pd.read_csv( - os.path.join("openbb_terminal", "mutual_funds", "avanza_fund_ID.csv"), - index_col=0, - ) - if self.country != "sweden": - console.print( - "Avanza implementation currently only supports funds from sweden." - ) - return self.queue - - if self.fund_isin not in ava_fund["ISIN"].tolist(): - console.print("No fund data. Please use another fund.") - return self.queue - - avanza_view.display_allocation( - name=self.fund_name, isin=self.fund_isin, focus=ns_parser.focus - ) - - return self.queue - - @log_start_end(log=logger) - def call_infoswe(self, other_args: List[str]): - """Process infoswe command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="infoswe", - description="Show fund info of a swedish fund. " - "To get a list of available funds, check the file `avanza_fund_ID.csv`.", - ) - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - ava_fund = pd.read_csv( - os.path.join("openbb_terminal", "mutual_funds", "avanza_fund_ID.csv"), - index_col=0, - ) - if self.country != "sweden": - console.print( - "Avanza implementation currently only supports funds from sweden." - ) - return self.queue - - if self.fund_isin not in ava_fund["ISIN"].tolist(): - console.print("No fund data. Please use another fund.") - return self.queue - - avanza_view.display_info(self.fund_isin) - - return self.queue - - @log_start_end(log=logger) - def call_country(self, other_args: List[str]): - """Process country command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="country", - description="Set a country for funds.", - ) - parser.add_argument( - "-n", - "--name", - type=lower_str, - choices=self.fund_countries, - dest="name", - help="country to select", - default="united_states", - metavar="NAME", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - country_candidate = ns_parser.name.lower() - if country_candidate in self.fund_countries: - self.country = country_candidate - console.print(f"'{country_candidate}' selected.") - - return self.queue - - @log_start_end(log=logger) - def call_search(self, other_args: List[str]): - """Process search command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="search", - description="Search mutual funds in selected country", - ) - parser.add_argument( - "--fund", - help="Fund string to search for", - dest="fund", - type=str, - required="-h" not in other_args, - nargs="+", - ) - - parser.add_argument( - "-l", - "--limit", - help="Number of search results to show", - type=check_positive, - dest="limit", - default=10, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--fund") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - mstarpy_view.display_search( - term=" ".join(ns_parser.fund), - country=self.country, - limit=ns_parser.limit, - ) - return self.queue - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Load the fund to perform analysis on.", - ) - parser.add_argument( - "--fund", - help="Fund string to search for", - dest="fund", - type=str, - required="-h" not in other_args, - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - default=(datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d"), - dest="start", - help="The starting date (format YYYY-MM-DD) of the stock", - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=datetime.now().strftime("%Y-%m-%d"), - dest="end", - help="The ending date (format YYYY-MM-DD) of the stock", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--fund") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if not self.country: - console.print( - "[yellow]Loading without a country selected " - " might result in unexpected results.[/yellow]" - ) - - funds_loaded = mstarpy_view.display_load( - term=ns_parser.fund, country=self.country - ) - - if funds_loaded: - self.funds_loaded = funds_loaded - self.fund_name = funds_loaded.name - self.fund_symbol = funds_loaded.code - self.fund_isin = funds_loaded.isin - self.end_date = ns_parser.end.strftime("%Y-%m-%d") - self.start_date = ns_parser.start.strftime("%Y-%m-%d") - - console.print( - f"The fund {self.fund_name} - {self.fund_isin} ({self.fund_symbol})" - " was successfully loaded." - ) - - else: - console.print("No funds were loaded.") - return self.queue - - @log_start_end(log=logger) - def call_plot(self, other_args: List[str]): - """Process plot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="plot", - description="Plot historical data.", - ) - parser.add_argument( - "-c", - default="", - choices=["category", "index", "both"], - dest="compare", - type=str.lower, - help="Compare funds performance with its category or index", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - if not self.fund_symbol: - console.print("No fund loaded. Please use `load` first to plot.") - return self.queue - mstarpy_view.display_historical( - loaded_funds=self.funds_loaded, - start_date=datetime.strptime(self.start_date, "%Y-%m-%d"), - end_date=datetime.strptime(self.end_date, "%Y-%m-%d"), - comparison=ns_parser.compare, - ) - return self.queue - - @log_start_end(log=logger) - def call_sector(self, other_args: List[str]): - """Process plot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sector", - description="Show funds, index, category sector breakdown.", - ) - parser.add_argument( - "-t", - "--type", - type=str, - choices=["equity", "fixed_income"], - dest="type", - help="asset type to select", - default="equity", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if not self.fund_symbol: - console.print("No fund loaded. Please use `load` first.") - return self.queue - - mstarpy_view.display_sector(self.funds_loaded, asset_type=ns_parser.type) - return self.queue - - @log_start_end(log=logger) - def call_holdings(self, other_args: List[str]): - """Process holdings command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="holdings", - description="Show funds holdings.", - ) - parser.add_argument( - "-t", - "--type", - type=str, - choices=["all", "equity", "bond", "other"], - dest="type", - help="type of holdings", - default="all", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if not self.fund_symbol: - console.print("No fund loaded. Please use `load` first.") - return self.queue - - mstarpy_view.display_holdings(self.funds_loaded, ns_parser.type) - - return self.queue - - @log_start_end(log=logger) - def call_carbon(self, other_args: List[str]): - """Process carbon_metrics command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="carbon", - description="Show funds carbon metrcis.", - ) - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if not self.fund_symbol: - console.print("No fund loaded. Please use `load` first.") - return self.queue - - mstarpy_view.display_carbon_metrics(self.funds_loaded) - - return self.queue - - @log_start_end(log=logger) - def call_exclusion(self, other_args: List[str]): - """Process exclusion_policy command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="exclusion", - description="Show funds exclsuion policy.", - ) - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if not self.fund_symbol: - console.print("No fund loaded. Please use `load` first to plot.") - return self.queue - - mstarpy_view.display_exclusion_policy(self.funds_loaded) - - return self.queue diff --git a/openbb_terminal/mutual_funds/mutual_funds_utils.py b/openbb_terminal/mutual_funds/mutual_funds_utils.py deleted file mode 100644 index e94e8df7e4a..00000000000 --- a/openbb_terminal/mutual_funds/mutual_funds_utils.py +++ /dev/null @@ -1,250 +0,0 @@ -mapping_country = { - "afghanistan": "af", - "åland_islands": "ax", - "albania": "al", - "algeria": "dz", - "american_samoa": "as", - "andorra": "ad", - "angola": "ao", - "anguilla": "ai", - "antarctica": "aq", - "antigua_and_barbuda": "ag", - "argentina": "ar", - "armenia": "am", - "aruba": "aw", - "australia": "au", - "austria": "at", - "azerbaijan": "az", - "bahamas": "bs", - "bahrain": "bh", - "bangladesh": "bd", - "barbados": "bb", - "belarus": "by", - "belgium": "be", - "belize": "bz", - "benin": "bj", - "bermuda": "bm", - "bhutan": "bt", - "bolivia": "bo", - "bonaire": "bq", - "bosnia_and_herzegovina": "ba", - "botswana": "bw", - "bouvet_island": "bv", - "brazil": "br", - "british_indian_ocean_territory": "io", - "brunei_darussalam": "bn", - "bulgaria": "bg", - "burkina_faso": "bf", - "burundi": "bi", - "cabo_verde": "cv", - "cambodia": "kh", - "cameroon": "cm", - "canada": "ca", - "cayman_islands": "ky", - "central_african_republic": "cf", - "chad": "td", - "chile": "cl", - "china": "cn", - "christmas_island": "cx", - "cocos_(keeling)_islands": "cc", - "colombia": "co", - "comoros": "km", - "congo_(the_democratic_republic_of_the)": "cd", - "congo_(the)": "cg", - "cook_islands": "ck", - "costa_rica": "cr", - "cote_divoire": "ci", - "croatia": "hr", - "cuba": "cu", - "curaçao": "cw", - "cyprus": "cy", - "czechia": "cz", - "denmark": "dk", - "djibouti": "dj", - "dominica": "dm", - "dominican_republic": "do", - "ecuador": "ec", - "egypt": "eg", - "el_salvador": "sv", - "equatorial_guinea": "gq", - "eritrea": "er", - "estonia": "ee", - "eswatini": "sz", - "ethiopia": "et", - "falkland_islands": "fk", - "fiji": "fj", - "finland": "fi", - "france": "fr", - "french_guiana": "gf", - "french_polynesia": "pf", - "french_southern_territories": "tf", - "gabon": "ga", - "gambia": "gm", - "georgia": "ge", - "germany": "de", - "ghana": "gh", - "gibraltar": "gi", - "greece": "gr", - "greenland": "gl", - "grenada": "gd", - "guadeloupe": "gp", - "guam": "gu", - "guatemala": "gt", - "guernsey": "gg", - "guinea": "gn", - "guinea-bissau": "gw", - "guyana": "gy", - "haiti": "ht", - "heard_island_and_mcdonald_islands": "hm", - "holy_see": "va", - "honduras": "hn", - "hong_kong": "hk", - "hungary": "hu", - "iceland": "is", - "india": "in", - "indonesia": "id", - "iran": "ir", - "iraq": "iq", - "ireland": "ie", - "isle_of_man": "im", - "israel": "il", - "italy": "it", - "jamaica": "jm", - "japan": "jp", - "jersey": "je", - "jordan": "jo", - "kazakhstan": "kz", - "kenya": "ke", - "kiribati": "ki", - "korea_(the_democratic_peoples_republic_of)": "kp", - "korea_(the_republic_of)": "kr", - "kuwait": "kw", - "kyrgyzstan": "kg", - "lao_peoples_democratic_republic": "la", - "latvia": "lv", - "lebanon": "lb", - "lesotho": "ls", - "liberia": "lr", - "libya": "ly", - "liechtenstein": "li", - "lithuania": "lt", - "luxembourg": "lu", - "macao": "mo", - "north_macedonia": "mk", - "madagascar": "mg", - "malawi": "mw", - "malaysia": "my", - "maldives": "mv", - "mali": "ml", - "malta": "mt", - "marshall_islands": "mh", - "martinique": "mq", - "mauritania": "mr", - "mauritius": "mu", - "mayotte": "yt", - "mexico": "mx", - "micronesia": "fm", - "moldova": "md", - "monaco": "mc", - "mongolia": "mn", - "montenegro": "me", - "montserrat": "ms", - "morocco": "ma", - "mozambique": "mz", - "myanmar": "mm", - "namibia": "na", - "nauru": "nr", - "nepal": "np", - "netherlands": "nl", - "new_caledonia": "nc", - "new_zealand": "nz", - "nicaragua": "ni", - "niger": "ne", - "nigeria": "ng", - "niue": "nu", - "norfolk_island": "nf", - "northern_mariana_islands": "mp", - "norway": "no", - "oman": "om", - "pakistan": "pk", - "palau": "pw", - "palestine": "ps", - "panama": "pa", - "papua_new_guinea": "pg", - "paraguay": "py", - "peru": "pe", - "philippines": "ph", - "pitcairn": "pn", - "poland": "pl", - "portugal": "pt", - "puerto_rico": "pr", - "qatar": "qa", - "réunion": "re", - "romania": "ro", - "russian_federation": "ru", - "rwanda": "rw", - "saint_barthélemy": "bl", - "saint_helena": "sh", - "saint_kitts_and_nevis": "kn", - "saint_lucia": "lc", - "saint_martin_(french_part)": "mf", - "saint_pierre_and_miquelon": "pm", - "saint_vincent_and_the_grenadines": "vc", - "samoa": "ws", - "san_marino": "sm", - "sao_tome_and_principe": "st", - "saudi_arabia": "sa", - "senegal": "sn", - "serbia": "rs", - "seychelles": "sc", - "sierra_leone": "sl", - "singapore": "sg", - "sint_maarten_(dutch_part)": "sx", - "slovakia": "sk", - "slovenia": "si", - "solomon_islands": "sb", - "somalia": "so", - "south_africa": "za", - "south_georgia_and_the_south_sandwich_islands": "gs", - "south_sudan": "ss", - "spain": "es", - "sri_lanka": "lk", - "sudan": "sd", - "suriname": "sr", - "svalbard": "sj", - "sweden": "se", - "switzerland": "ch", - "syrian_arab_republic": "sy", - "taiwan": "tw", - "tajikistan": "tj", - "tanzania": "tz", - "thailand": "th", - "timor-leste": "tl", - "togo": "tg", - "tokelau": "tk", - "tonga": "to", - "trinidad_and_tobago": "tt", - "tunisia": "tn", - "türkiye": "tr", - "turkmenistan": "tm", - "turks_and_caicos_islands": "tc", - "tuvalu": "tv", - "uganda": "ug", - "ukraine": "ua", - "united_arab_emirates": "ae", - "united_kingdom": "gb", - "united_states_minor_outlying_islands": "um", - "united_states": "us", - "uruguay": "uy", - "uzbekistan": "uz", - "vanuatu": "vu", - "venezuela": "ve", - "viet_nam": "vn", - "virgin_islands_(british)": "vg", - "virgin_islands_(u.s.)": "vi", - "wallis_and_futuna": "wf", - "western_sahara": "eh", - "yemen": "ye", - "zambia": "zm", - "zimbabwe": "zw", -} diff --git a/openbb_terminal/openbb_terminal/__init__.py b/openbb_terminal/openbb_terminal/__init__.py new file mode 100644 index 00000000000..542734dabf3 --- /dev/null +++ b/openbb_terminal/openbb_terminal/__init__.py @@ -0,0 +1 @@ +"""Package init""" diff --git a/openbb_terminal/account/__init__.py b/openbb_terminal/openbb_terminal/argparse_translator/__init__.py similarity index 100% rename from openbb_terminal/account/__init__.py rename to openbb_terminal/openbb_terminal/argparse_translator/__init__.py diff --git a/openbb_terminal/openbb_terminal/argparse_translator/argparse_class_processor.py b/openbb_terminal/openbb_terminal/argparse_translator/argparse_class_processor.py new file mode 100644 index 00000000000..74e706e5ebb --- /dev/null +++ b/openbb_terminal/openbb_terminal/argparse_translator/argparse_class_processor.py @@ -0,0 +1,148 @@ +import inspect +from typing import Any, Dict, Optional, Type + +# TODO: this needs to be done differently +from openbb_core.app.static.container import Container + +from openbb_terminal.argparse_translator.argparse_translator import ( + ArgparseTranslator, + ReferenceToCustomArgumentsProcessor, +) + + +class ArgparseClassProcessor: + """ + Process a target class to create ArgparseTranslators for its methods. + """ + + # reference variable used to create custom groups for the ArgpaseTranslators + _reference: Dict[str, Any] = {} + + def __init__( + self, + target_class: Type, + add_help: bool = False, + reference: Optional[Dict[str, Any]] = None, + ): + """ + Initialize the ArgparseClassProcessor. + + Parameters + ---------- + target_class : Type + The target class whose methods will be processed. + add_help : Optional[bool] + Whether to add help to the ArgparseTranslators. + """ + self._target_class: Type = target_class + self._add_help: bool = add_help + self._translators: Dict[str, ArgparseTranslator] = {} + self._paths: Dict[str, str] = {} + + ArgparseClassProcessor._reference = reference or {} + + self._translators = self._process_class( + target=self._target_class, add_help=self._add_help + ) + self._paths[self._get_class_name(self._target_class)] = "path" + self._build_paths(target=self._target_class) + + @property + def translators(self) -> Dict[str, ArgparseTranslator]: + """ + Get the ArgparseTranslators associated with the target class. + + Returns + ------- + Dict[str, ArgparseTranslator] + The ArgparseTranslators associated with the target class. + """ + return self._translators + + @property + def paths(self) -> Dict[str, str]: + """ + Get the paths associated with the target class. + + Returns + ------- + Dict[str, str] + The paths associated with the target class. + """ + return self._paths + + @classmethod + def _custom_groups_from_reference(cls, class_name: str, function_name: str) -> Dict: + route = f"/{class_name.replace('_', '/')}/{function_name}" + reference = {route: cls._reference[route]} if route in cls._reference else {} + if not reference: + return {} + rp = ReferenceToCustomArgumentsProcessor(reference) + return rp.custom_groups.get(route, {}) # type: ignore + + @classmethod + def _process_class( + cls, + target: type, + add_help: bool = False, + ) -> Dict[str, ArgparseTranslator]: + methods = {} + + for name, member in inspect.getmembers(target): + if name.startswith("__") or name.startswith("_"): + continue + if inspect.ismethod(member): + + class_name = cls._get_class_name(target) + methods[f"{class_name}_{name}"] = ArgparseTranslator( + func=member, + add_help=add_help, + custom_argument_groups=cls._custom_groups_from_reference( # type: ignore + class_name=class_name, function_name=name + ), + ) + elif isinstance(member, Container): + methods = { + **methods, + **cls._process_class( + target=getattr(target, name), add_help=add_help + ), + } + + return methods + + @staticmethod + def _get_class_name(target: type) -> str: + return ( + str(type(target)) + .rsplit(".", maxsplit=1)[-1] + .replace("'>", "") + .replace("ROUTER_", "") + .lower() + ) + + def get_translator(self, command: str) -> ArgparseTranslator: + """ + Retrieve the ArgparseTranslator object associated with a specific menu and command. + + Parameters + ---------- + command : str + The command associated with the ArgparseTranslator. + + Returns + ------- + ArgparseTranslator + The ArgparseTranslator associated with the specified menu and command. + """ + return self._translators[command] + + def _build_paths(self, target: type, depth: int = 1): + for name, member in inspect.getmembers(target): + if name.startswith("__") or name.startswith("_"): + continue + if inspect.ismethod(member): + pass + elif isinstance(member, Container): + self._build_paths(target=getattr(target, name), depth=depth + 1) + self._paths[f"{name}"] = "sub" * depth + "path" diff --git a/openbb_terminal/openbb_terminal/argparse_translator/argparse_translator.py b/openbb_terminal/openbb_terminal/argparse_translator/argparse_translator.py new file mode 100644 index 00000000000..b08ebfa2845 --- /dev/null +++ b/openbb_terminal/openbb_terminal/argparse_translator/argparse_translator.py @@ -0,0 +1,528 @@ +import argparse +import inspect +from copy import deepcopy +from enum import Enum +from typing import ( + Any, + Callable, + Dict, + List, + Literal, + Optional, + Tuple, + Type, + Union, + get_args, + get_origin, + get_type_hints, +) + +from openbb_core.app.model.field import OpenBBField +from pydantic import BaseModel, model_validator +from typing_extensions import Annotated + +SEP = "__" + + +class ArgparseActionType(Enum): + store = "store" + store_true = "store_true" + + +class CustomArgument(BaseModel): + + name: str + type: Optional[Any] + dest: str + default: Any + required: bool + action: Literal["store_true", "store"] + help: str + nargs: Optional[Literal["+"]] + choices: Optional[Any] + + @model_validator(mode="after") # type: ignore + @classmethod + def validate_action(cls, values: "CustomArgument"): + if values.type is bool and values.action != "store_true": + raise ValueError('If type is bool, action must be "store_true"') + return values + + @model_validator(mode="after") # type: ignore + @classmethod + def remove_props_on_store_true(cls, values: "CustomArgument"): + if values.action == "store_true": + values.type = None + values.nargs = None + values.choices = None + return values + + # override + def model_dump(self, **kwargs): + + res = super().model_dump(**kwargs) + + # Check if choices is present and if it's an empty tuple remove it + if "choices" in res and not res["choices"]: + del res["choices"] + + return res + + +class CustomArgumentGroup(BaseModel): + name: str + arguments: List[CustomArgument] + + +class ReferenceToCustomArgumentsProcessor: + def __init__(self, reference: Dict[str, Dict]): + """Initializes the ReferenceToCustomArgumentsProcessor.""" + self.reference = reference + self.custom_groups: Dict[str, List[CustomArgumentGroup]] = {} + + self.build_custom_groups() + + @staticmethod + def _make_type_parsable(type_: str) -> type: + """Make the type parsable by removing the annotations.""" + if "Union" in type_ and "str" in type_: + return str + if "Union" in type_ and "int" in type_: + return int + if type_ in ["date", "datetime.time", "time"]: + return str + + if any(x in type_ for x in ["gt=", "ge=", "lt=", "le="]): + if "Annotated" in type_: + type_ = type_.replace("Annotated[", "").replace("]", "") + type_ = type_.split(",")[0] + + return eval(type_) # noqa: S307, E501 pylint: disable=eval-used + + def _parse_type(self, type_: str) -> type: + """Parse the type from the string representation.""" + type_ = self._make_type_parsable(type_) # type: ignore + + if get_origin(type_) is Literal: + type_ = type(get_args(type_)[0]) # type: ignore + + return type_ # type: ignore + + def _get_nargs(self, type_: type) -> Optional[Union[int, str]]: + """Get the nargs for the given type.""" + if get_origin(type_) is list: + return "+" + return None + + def _get_choices(self, type_: str) -> Tuple: + """Get the choices for the given type.""" + type_ = self._make_type_parsable(type_) # type: ignore + type_origin = get_origin(type_) + + choices = () + + if type_origin is Literal: + choices = get_args(type_) + # param_type = type(choices[0]) + + if type_origin is list: + type_ = get_args(type_)[0] + + if get_origin(type_) is Literal: + choices = get_args(type_) + # param_type = type(choices[0]) + + if type_origin is Union and type(None) in get_args(type_): + # remove NoneType from the args + args = [arg for arg in get_args(type_) if arg != type(None)] + # if there is only one arg left, use it + if len(args) > 1: + raise ValueError("Union with NoneType should have only one type left") + type_ = args[0] + + if get_origin(type_) is Literal: + choices = get_args(type_) + # param_type = type(choices[0]) + + return choices + + def build_custom_groups(self): + """Build the custom groups from the reference.""" + for route, v in self.reference.items(): + + for provider, args in v["parameters"].items(): + if provider == "standard": + continue + + custom_arguments = [] + for arg in args: + if arg.get("standard"): + continue + + type_ = self._parse_type(arg["type"]) + + custom_arguments.append( + CustomArgument( + name=arg["name"], + type=type_, + dest=arg["name"], + default=arg["default"], + required=not (arg["optional"]), + action="store" if type_ != bool else "store_true", + help=arg["description"], + nargs=self._get_nargs(type_), # type: ignore + choices=self._get_choices(arg["type"]), + ) + ) + + group = CustomArgumentGroup(name=provider, arguments=custom_arguments) + + if route not in self.custom_groups: + self.custom_groups[route] = [] + + self.custom_groups[route].append(group) + + +class ArgparseTranslator: + def __init__( + self, + func: Callable, + custom_argument_groups: Optional[List[CustomArgumentGroup]] = None, + add_help: Optional[bool] = True, + ): + """ + Initializes the ArgparseTranslator. + + Args: + func (Callable): The function to translate into an argparse program. + add_help (Optional[bool], optional): Whether to add the help argument. Defaults to False. + """ + self.func = func + self.signature = inspect.signature(func) + self.type_hints = get_type_hints(func) + + self._parser = argparse.ArgumentParser( + prog=func.__name__, + description=self._build_description(func.__doc__), # type: ignore + formatter_class=argparse.RawTextHelpFormatter, + add_help=add_help if add_help else False, + ) + self._required = self._parser.add_argument_group("required arguments") + + if any(param in self.type_hints for param in self.signature.parameters): + self._generate_argparse_arguments(self.signature.parameters) + + if custom_argument_groups: + for group in custom_argument_groups: + argparse_group = self._parser.add_argument_group(group.name) + for argument in group.arguments: + kwargs = argument.model_dump(exclude={"name"}, exclude_none=True) + + # If the argument is already in use, we can't repeat it + if f"--{argument.name}" not in self._parser_arguments(): + argparse_group.add_argument(f"--{argument.name}", **kwargs) + + def _parser_arguments(self) -> List[str]: + """Get all the arguments from all groups currently defined on the parser.""" + arguments_in_use: List[str] = [] + + # pylint: disable=protected-access + for action_group in self._parser._action_groups: + for action in action_group._group_actions: + arguments_in_use.extend(action.option_strings) + + return arguments_in_use + + @property + def parser(self) -> argparse.ArgumentParser: + return deepcopy(self._parser) + + @staticmethod + def _build_description(func_doc: str) -> str: + """Builds the description of the argparse program from the function docstring.""" + + patterns = ["openbb\n ======", "Parameters\n ----------"] + + if func_doc: + for pattern in patterns: + if pattern in func_doc: + func_doc = func_doc[: func_doc.index(pattern)].strip() + break + + return func_doc + + @staticmethod + def _param_is_default(param: inspect.Parameter) -> bool: + """Returns True if the parameter has a default value.""" + return param.default != inspect.Parameter.empty + + def _get_action_type(self, param: inspect.Parameter) -> str: + """Returns the argparse action type for the given parameter.""" + param_type = self.type_hints[param.name] + + if param_type == bool: + return ArgparseActionType.store_true.value + return ArgparseActionType.store.value + + def _get_type_and_choices( + self, param: inspect.Parameter + ) -> Tuple[Type[Any], Tuple[Any, ...]]: + """Returns the type and choices for the given parameter.""" + param_type = self.type_hints[param.name] + type_origin = get_origin(param_type) + + choices = () + + if type_origin is Literal: + choices = get_args(param_type) + param_type = type(choices[0]) # type: ignore + + if type_origin is list: # TODO: dict should also go here + param_type = get_args(param_type)[0] + + if get_origin(param_type) is Literal: + choices = get_args(param_type) + param_type = type(choices[0]) # type: ignore + + if type_origin is Union: + union_args = get_args(param_type) + if str in union_args: + param_type = str + + # check if it's an Optional, which would be a Union with NoneType + if type(None) in get_args(param_type): + # remove NoneType from the args + args = [arg for arg in get_args(param_type) if arg != type(None)] + # if there is only one arg left, use it + if len(args) > 1: + raise ValueError( + "Union with NoneType should have only one type left" + ) + param_type = args[0] + + if get_origin(param_type) is Literal: + choices = get_args(param_type) + param_type = type(choices[0]) # type: ignore + + # if there are custom choices, override + choices = self._get_argument_custom_choices(param) or choices # type: ignore + + return param_type, choices + + @staticmethod + def _split_annotation( + base_annotation: Type[Any], custom_annotation_type: Type + ) -> Tuple[Type[Any], List[Any]]: + """Find the base annotation and the custom annotations, namely the OpenBBField.""" + if get_origin(base_annotation) is not Annotated: + return base_annotation, [] + base_annotation, *maybe_custom_annotations = get_args(base_annotation) + return base_annotation, [ + annotation + for annotation in maybe_custom_annotations + if isinstance(annotation, custom_annotation_type) + ] + + @classmethod + def _get_argument_custom_help(cls, param: inspect.Parameter) -> Optional[str]: + """Returns the help annotation for the given parameter.""" + base_annotation = param.annotation + _, custom_annotations = cls._split_annotation(base_annotation, OpenBBField) + help_annotation = ( + custom_annotations[0].description if custom_annotations else None + ) + if not help_annotation: + # try to get it from the docstring + pass + return help_annotation + + @classmethod + def _get_argument_custom_choices(cls, param: inspect.Parameter) -> Optional[str]: + """Returns the help annotation for the given parameter.""" + base_annotation = param.annotation + _, custom_annotations = cls._split_annotation(base_annotation, OpenBBField) + choices_annotation = ( + custom_annotations[0].choices if custom_annotations else None + ) + if not choices_annotation: + # try to get it from the docstring + pass + return choices_annotation + + def _get_nargs(self, param: inspect.Parameter) -> Optional[str]: + """Returns the nargs annotation for the given parameter.""" + param_type = self.type_hints[param.name] + origin = get_origin(param_type) + + if origin is list: + return "+" + + if origin is Union and any( + get_origin(arg) is list for arg in get_args(param_type) + ): + return "+" + + return None + + def _generate_argparse_arguments(self, parameters) -> None: + """Generates the argparse arguments from the function parameters.""" + for param in parameters.values(): + # TODO : how to handle kwargs? + # it's possible to add unknown arguments when parsing as follows: + # args, unknown_args = parser.parse_known_args() + if param.name == "kwargs": + continue + + param_type, choices = self._get_type_and_choices(param) + + # if the param is a custom type, we need to flatten it + if inspect.isclass(param_type) and issubclass(param_type, BaseModel): + # update type hints with the custom type fields + type_hints = get_type_hints(param_type) + # prefix the type hints keys with the param name + type_hints = { + f"{param.name}{SEP}{key}": value + for key, value in type_hints.items() + } + self.type_hints.update(type_hints) + # create a signature from the custom type + sig = inspect.signature(param_type) + + # add help to the annotation + annotated_parameters: List[inspect.Parameter] = [] + for child_param in sig.parameters.values(): + new_child_param = child_param.replace( + name=f"{param.name}{SEP}{child_param.name}", + annotation=Annotated[ + child_param.annotation, + OpenBBField( + description=param_type.model_json_schema()[ + "properties" + ][child_param.name].get("description", None) + ), + ], + kind=inspect.Parameter.KEYWORD_ONLY, + ) + annotated_parameters.append(new_child_param) + + # replacing with the annotated parameters + new_signature = inspect.Signature( + parameters=annotated_parameters, + return_annotation=sig.return_annotation, + ) + self._generate_argparse_arguments(new_signature.parameters) + + # the custom type itself should not be added as an argument + continue + + required = not self._param_is_default(param) + + kwargs = { + "type": param_type, + "dest": param.name, + "default": param.default, + "required": required, + "action": self._get_action_type(param), + "help": self._get_argument_custom_help(param), + "nargs": self._get_nargs(param), + } + + if choices: + kwargs["choices"] = choices + + if param_type == bool: + # store_true action does not accept the below kwargs + kwargs.pop("type") + kwargs.pop("nargs") + + if required: + self._required.add_argument( + f"--{param.name}", + **kwargs, + ) + else: + self._parser.add_argument( + f"--{param.name}", + **kwargs, + ) + + @staticmethod + def _unflatten_args(args: dict) -> Dict[str, Any]: + """Unflatten the args that were flattened by the custom types.""" + result: Dict[str, Any] = {} + for key, value in args.items(): + if SEP in key: + parts = key.split(SEP) + nested_dict = result + for part in parts[:-1]: + if part not in nested_dict: + nested_dict[part] = {} + nested_dict = nested_dict[part] + nested_dict[parts[-1]] = value + else: + result[key] = value + return result + + def _update_with_custom_types(self, kwargs: Dict[str, Any]) -> Dict[str, Any]: + """Update the kwargs with the custom types.""" + # for each argument in the signature that is a custom type, we need to + # update the kwargs with the custom type kwargs + for param in self.signature.parameters.values(): + # TODO : how to handle kwargs? + if param.name == "kwargs": + continue + param_type, _ = self._get_type_and_choices(param) + if inspect.isclass(param_type) and issubclass(param_type, BaseModel): + custom_type_kwargs = kwargs[param.name] + kwargs[param.name] = param_type(**custom_type_kwargs) + + return kwargs + + def execute_func( + self, + parsed_args: Optional[argparse.Namespace] = None, + ) -> Any: + """ + Executes the original function with the parsed arguments. + + Args: + parsed_args (Optional[argparse.Namespace], optional): The parsed arguments. Defaults to None. + + Returns: + Any: The return value of the original function. + + """ + kwargs = self._unflatten_args(vars(parsed_args)) + kwargs = self._update_with_custom_types(kwargs) + + # remove kwargs that doesn't match the signature + kwargs = { + key: value + for key, value in kwargs.items() + if key in self.signature.parameters + } + + return self.func(**kwargs) + + def parse_args_and_execute(self) -> Any: + """ + Parses the arguments and executes the original function. + + Returns: + Any: The return value of the original function. + """ + parsed_args = self._parser.parse_args() + return self.execute_func(parsed_args) + + def translate(self) -> Callable: + """ + Wraps the original function with an argparse program. + + Returns: + Callable: The original function wrapped with an argparse program. + """ + + def wrapper_func(): + return self.parse_args_and_execute() + + return wrapper_func diff --git a/openbb_terminal/openbb_terminal/argparse_translator/obbject_registry.py b/openbb_terminal/openbb_terminal/argparse_translator/obbject_registry.py new file mode 100644 index 00000000000..cb1643747b3 --- /dev/null +++ b/openbb_terminal/openbb_terminal/argparse_translator/obbject_registry.py @@ -0,0 +1,76 @@ +"""Registry for OBBjects.""" + +import json +from typing import Dict, List + +from openbb_core.app.model.abstract.singleton import SingletonMeta +from openbb_core.app.model.obbject import OBBject + + +class Registry(metaclass=SingletonMeta): + + obbjects: List[OBBject] = [] + + @staticmethod + def _contains_obbject(uuid: str, obbjects: List[OBBject]) -> bool: + """Check if obbject with uuid is in the registry.""" + return any(obbject.id == uuid for obbject in obbjects) + + @classmethod + def register(cls, obbject: OBBject): + """Designed to add an OBBject instance to the registry.""" + if isinstance(obbject, OBBject) and not cls._contains_obbject( + obbject.id, cls.obbjects + ): + cls.obbjects.append(obbject) + + @classmethod + def get(cls, idx: int) -> OBBject: + """Return the obbject at index idx.""" + # the list should work as a stack + # i.e., the last element needs to be accessed by idx=0 and so on + reversed_list = list(reversed(cls.obbjects)) + return reversed_list[idx] + + @property + def all(self) -> Dict[int, Dict]: + """Return all obbjects in the registry""" + + def _handle_standard_params(obbject: OBBject) -> str: + """Handle standard params for obbjects""" + standard_params_json = "" + std_params = obbject._standard_params # pylint: disable=protected-access + if hasattr(std_params, "__dict__"): + standard_params = { + k: str(v)[:30] for k, v in std_params.__dict__.items() if v + } + standard_params_json = json.dumps(standard_params) + + return standard_params_json + + def _handle_data_repr(obbject: OBBject) -> str: + """Handle data representation for obbjects""" + data_repr = "" + if hasattr(obbject, "results") and obbject.results: + data_schema = ( + obbject.results[0].model_json_schema() + if obbject.results and isinstance(obbject.results, list) + else "" + ) + if data_schema and "title" in data_schema: + data_repr = f"{data_schema['title']}" + if data_schema and "description" in data_schema: + data_repr += f" - {data_schema['description'].split('.')[0]}" + + return data_repr + + obbjects = {} + for i, obbject in enumerate(list(reversed(self.obbjects))): + obbjects[i] = { + "route": obbject._route, # pylint: disable=protected-access + "provider": obbject.provider, + "standard params": _handle_standard_params(obbject), + "data": _handle_data_repr(obbject), + } + + return obbjects diff --git a/openbb_terminal/openbb_terminal/assets/i18n/en.yml b/openbb_terminal/openbb_terminal/assets/i18n/en.yml new file mode 100644 index 00000000000..beb4013229d --- /dev/null +++ b/openbb_terminal/openbb_terminal/assets/i18n/en.yml @@ -0,0 +1,36 @@ +en: + intro: introduction on the OpenBB Platform CLI + about: discover the capabilities of the OpenBB Platform CLI (https://openbb.co/docs) + support: pre-populate a support ticket for our team to evaluate + survey: fill in our 2-minute survey so we better understand how we can improve the CLI + settings: enable and disable feature flags, preferences and settings + _scripts_: Record and execute your own .openbb routine scripts + record: start recording current session + stop: stop session recording and convert to .openbb routine + exe: execute .openbb routine scripts (use exe --example for an example) + _configure_: Configure your own CLI + _main_menu_: Main menu + settings/_info_: Feature flags + settings/_settings_: Terminal settings and preferences + settings/retryload: retry misspelled commands with load first + settings/interactive: open dataframes in interactive window + settings/cls: clear console after each command + settings/color: use coloring features + settings/promptkit: enable prompt toolkit (autocomplete and history) + settings/thoughts: thoughts of the day + settings/reporthtml: open report as HTML otherwise notebook + settings/exithelp: automatically print help when quitting menu + settings/rcontext: remember contexts loaded params during session + settings/rich: colorful rich CLI + settings/richpanel: colorful rich CLI panel + settings/watermark: watermark in figures + settings/cmdloc: command location displayed in figures + settings/overwrite: whether to overwrite Excel files if they already exists + settings/version: whether to show the version in the bottom right corner + settings/tbhint: displays usage hints in the bottom toolbar + settings/console_style: apply a custom rich style to the CLI + settings/flair: choose flair icon + settings/timezone: pick timezone + settings/language: translation language + settings/n_rows: number of rows to show on non interactive tables + settings/n_cols: number of columns to show on non interactive tables diff --git a/openbb_terminal/openbb_terminal/assets/routines/routine_example.openbb b/openbb_terminal/openbb_terminal/assets/routines/routine_example.openbb new file mode 100644 index 00000000000..72c7fa574aa --- /dev/null +++ b/openbb_terminal/openbb_terminal/assets/routines/routine_example.openbb @@ -0,0 +1,21 @@ +# Go into the equity context +equity + +# Get the company's profile +profile --symbol aapl + +# Get company's statements +fundamental +balance --symbol aapl +cash --symbol aapl +transcript --symbol aapl --year 2023 + +# Load company's historical data +../price +historical --symbol aapl + +# Get its performance +performance --symbol aapl + +# Export the candle as an image and the historical data into a csv +historical --symbol aapl --chart --export csv,jpg \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/styles/default/Consolas.ttf b/openbb_terminal/openbb_terminal/assets/styles/default/Consolas.ttf similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/Consolas.ttf rename to openbb_terminal/openbb_terminal/assets/styles/default/Consolas.ttf diff --git a/openbb_terminal/miscellaneous/styles/default/dark.mpfstyle.json b/openbb_terminal/openbb_terminal/assets/styles/default/dark.mpfstyle.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/dark.mpfstyle.json rename to openbb_terminal/openbb_terminal/assets/styles/default/dark.mpfstyle.json diff --git a/openbb_terminal/miscellaneous/styles/default/dark.mplrc.json b/openbb_terminal/openbb_terminal/assets/styles/default/dark.mplrc.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/dark.mplrc.json rename to openbb_terminal/openbb_terminal/assets/styles/default/dark.mplrc.json diff --git a/openbb_terminal/miscellaneous/styles/default/dark.mplstyle b/openbb_terminal/openbb_terminal/assets/styles/default/dark.mplstyle similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/dark.mplstyle rename to openbb_terminal/openbb_terminal/assets/styles/default/dark.mplstyle diff --git a/openbb_terminal/miscellaneous/styles/default/dark.pltstyle.json b/openbb_terminal/openbb_terminal/assets/styles/default/dark.pltstyle.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/dark.pltstyle.json rename to openbb_terminal/openbb_terminal/assets/styles/default/dark.pltstyle.json diff --git a/openbb_terminal/miscellaneous/styles/default/dark.richstyle.json b/openbb_terminal/openbb_terminal/assets/styles/default/dark.richstyle.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/dark.richstyle.json rename to openbb_terminal/openbb_terminal/assets/styles/default/dark.richstyle.json diff --git a/openbb_terminal/miscellaneous/styles/default/light.mpfstyle.json b/openbb_terminal/openbb_terminal/assets/styles/default/light.mpfstyle.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/light.mpfstyle.json rename to openbb_terminal/openbb_terminal/assets/styles/default/light.mpfstyle.json diff --git a/openbb_terminal/miscellaneous/styles/default/light.mplrc.json b/openbb_terminal/openbb_terminal/assets/styles/default/light.mplrc.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/light.mplrc.json rename to openbb_terminal/openbb_terminal/assets/styles/default/light.mplrc.json diff --git a/openbb_terminal/miscellaneous/styles/default/light.mplstyle b/openbb_terminal/openbb_terminal/assets/styles/default/light.mplstyle similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/light.mplstyle rename to openbb_terminal/openbb_terminal/assets/styles/default/light.mplstyle diff --git a/openbb_terminal/miscellaneous/styles/default/light.pltstyle.json b/openbb_terminal/openbb_terminal/assets/styles/default/light.pltstyle.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/light.pltstyle.json rename to openbb_terminal/openbb_terminal/assets/styles/default/light.pltstyle.json diff --git a/openbb_terminal/miscellaneous/styles/default/light.richstyle.json b/openbb_terminal/openbb_terminal/assets/styles/default/light.richstyle.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/light.richstyle.json rename to openbb_terminal/openbb_terminal/assets/styles/default/light.richstyle.json diff --git a/openbb_terminal/miscellaneous/styles/default/tables.pltstyle.json b/openbb_terminal/openbb_terminal/assets/styles/default/tables.pltstyle.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/default/tables.pltstyle.json rename to openbb_terminal/openbb_terminal/assets/styles/default/tables.pltstyle.json diff --git a/openbb_terminal/miscellaneous/styles/user/openbb.richstyle.json b/openbb_terminal/openbb_terminal/assets/styles/user/openbb.richstyle.json similarity index 100% rename from openbb_terminal/miscellaneous/styles/user/openbb.richstyle.json rename to openbb_terminal/openbb_terminal/assets/styles/user/openbb.richstyle.json diff --git a/openbb_terminal/openbb_terminal/cli.py b/openbb_terminal/openbb_terminal/cli.py new file mode 100644 index 00000000000..aec05931367 --- /dev/null +++ b/openbb_terminal/openbb_terminal/cli.py @@ -0,0 +1,20 @@ +"""OpenBB Platform CLI entry point.""" + +import sys + +from openbb_terminal.config.setup import setup_config_terminal +from openbb_terminal.controllers.terminal_controller import launch + + +def main(): + """Use the main entry point for the OpenBB Terminal CLI.""" + setup_config_terminal() + + dev = "--dev" in sys.argv[1:] + debug = "--debug" in sys.argv[1:] + + launch(dev, debug) + + +if __name__ == "__main__": + main() diff --git a/openbb_terminal/openbb_terminal/config/__init__.py b/openbb_terminal/openbb_terminal/config/__init__.py new file mode 100644 index 00000000000..96aa4d3b1a8 --- /dev/null +++ b/openbb_terminal/openbb_terminal/config/__init__.py @@ -0,0 +1 @@ +"""Core config init.""" diff --git a/openbb_terminal/custom_prompt_toolkit.py b/openbb_terminal/openbb_terminal/config/completer.py similarity index 96% rename from openbb_terminal/custom_prompt_toolkit.py rename to openbb_terminal/openbb_terminal/config/completer.py index c3f51614869..f084c821478 100644 --- a/openbb_terminal/custom_prompt_toolkit.py +++ b/openbb_terminal/openbb_terminal/config/completer.py @@ -1,4 +1,4 @@ -""" Nestedcompleter for completion of OpenBB hierarchical data structures. """ +"""Nested completer for completion of OpenBB hierarchical data structures.""" from typing import ( Any, @@ -23,8 +23,7 @@ NestedDict = Mapping[str, Union[Any, Set[str], None, Completer]] class WordCompleter(Completer): - """ - Simple autocompletion on a list of words. + """Simple autocompletion on a list of words. :param words: List of words or callable that returns a list of words. :param ignore_case: If True, case-insensitive completion. @@ -53,6 +52,7 @@ class WordCompleter(Completer): match_middle: bool = False, pattern: Optional[Pattern[str]] = None, ) -> None: + """Initialize the WordCompleter.""" assert not (WORD and sentence) # noqa: S101 self.words = words @@ -69,6 +69,7 @@ class WordCompleter(Completer): document: Document, _complete_event: CompleteEvent, ) -> Iterable[Completion]: + """Get completions.""" # Get list of words. words = self.words if callable(words): @@ -94,7 +95,7 @@ class WordCompleter(Completer): word_before_cursor = word_before_cursor.lower() def word_matches(word: str) -> bool: - """True when the word before the cursor matches.""" + """Set True when the word before the cursor matches.""" if self.ignore_case: word = word.lower() @@ -115,8 +116,7 @@ class WordCompleter(Completer): class NestedCompleter(Completer): - """ - Completer which wraps around several other completers, and calls any the + """Completer which wraps around several other completers, and calls any the one that corresponds with the first word of the input. By combining multiple `NestedCompleter` instances, we can achieve multiple @@ -131,6 +131,7 @@ class NestedCompleter(Completer): def __init__( self, options: Dict[str, Optional[Completer]], ignore_case: bool = True ) -> None: + """Initialize the NestedCompleter.""" self.flags_processed: List = list() self.original_options = options self.options = options @@ -138,13 +139,14 @@ class NestedCompleter(Completer): self.complementary = list() def __repr__(self) -> str: + """Return string representation of NestedCompleter.""" return f"NestedCompleter({self.options!r}, ignore_case={self.ignore_case!r})" @classmethod def from_nested_dict(cls, data: dict) -> "NestedCompleter": - """ - Create a `NestedCompleter`, starting from a nested dictionary data - structure, like this: + """Create a `NestedCompleter`. + + It starts from a nested dictionary data structure, like this: .. code:: @@ -190,6 +192,7 @@ class NestedCompleter(Completer): def get_completions( # noqa: PLR0912 self, document: Document, complete_event: CompleteEvent ) -> Iterable[Completion]: + """Get completions.""" # Split document. cmd = "" text = document.text_before_cursor.lstrip() diff --git a/openbb_terminal/openbb_terminal/config/console.py b/openbb_terminal/openbb_terminal/config/console.py new file mode 100644 index 00000000000..749e40c0a3b --- /dev/null +++ b/openbb_terminal/openbb_terminal/config/console.py @@ -0,0 +1,97 @@ +from typing import TYPE_CHECKING, Optional, Tuple + +from rich import panel +from rich.console import ( + Console as RichConsole, + Theme, +) +from rich.text import Text + +from openbb_terminal.config.menu_text import RICH_TAGS + +if TYPE_CHECKING: + from openbb_terminal.models.settings import Settings + + +class Console: + """Create a rich console to wrap the console print with a Panel.""" + + def __init__( + self, + settings: "Settings", + style: Optional[str] = None, + ): + """Initialize the ConsoleAndPanel class.""" + self._console = RichConsole( + theme=Theme(style), # type: ignore[arg-type] + highlight=False, + soft_wrap=True, + ) + self._settings = settings + self.menu_text = "" + self.menu_path = "" + + def capture(self): + """Capture the console output.""" + return self._console.capture() + + @staticmethod + def filter_rich_tags(text): + """Filter out rich tags from text.""" + for val in RICH_TAGS: + text = text.replace(val, "") + + return text + + @staticmethod + def blend_text( + message: str, color1: Tuple[int, int, int], color2: Tuple[int, int, int] + ) -> Text: + """Blend text from one color to another.""" + text = Text(message) + r1, g1, b1 = color1 + r2, g2, b2 = color2 + dr = r2 - r1 + dg = g2 - g1 + db = b2 - b1 + size = len(text) + 5 + for index in range(size): + blend = index / size + color = f"#{int(r1 + dr * blend):02X}{int(g1 + dg * blend):02X}{int(b1 + db * blend):02X}" + text.stylize(color, index, index + 1) + return text + + def print(self, *args, **kwargs): + """Print the text to the console.""" + if kwargs and "text" in list(kwargs) and "menu" in list(kwargs): + if not self._settings.TEST_MODE: + if self._settings.ENABLE_RICH_PANEL: + if self._settings.SHOW_VERSION: + version = self._settings.VERSION + version = f"[param]OpenBB Platform CLI v{version}[/param] (https://openbb.co)" + else: + version = ( + "[param]OpenBB Platform CLI[/param] (https://openbb.co)" + ) + self._console.print( + panel.Panel( + "\n" + kwargs["text"], + title=kwargs["menu"], + subtitle_align="right", + subtitle=version, + ) + ) + + else: + self._console.print(kwargs["text"]) + else: + print(self.filter_rich_tags(kwargs["text"])) # noqa: T201 + elif not self._settings.TEST_MODE: + self._console.print(*args, **kwargs) + else: + print(*args, **kwargs) # noqa: T201 + + def input(self, *args, **kwargs): + """Get input from the user.""" + self.print(*args, **kwargs, end="") + return input() diff --git a/openbb_terminal/openbb_terminal/config/constants.py b/openbb_terminal/openbb_terminal/config/constants.py new file mode 100644 index 00000000000..462dae59c0a --- /dev/null +++ b/openbb_terminal/openbb_terminal/config/constants.py @@ -0,0 +1,83 @@ +"""Constants module.""" + +from pathlib import Path + +# Paths +HOME_DIRECTORY = Path.home() +REPOSITORY_DIRECTORY = Path(__file__).parent.parent.parent.parent +SRC_DIRECTORY = Path(__file__).parent.parent +SETTINGS_DIRECTORY = HOME_DIRECTORY / ".openbb_terminal" +ASSETS_DIRECTORY = SRC_DIRECTORY / "assets" +STYLES_DIRECTORY = ASSETS_DIRECTORY / "styles" +ENV_FILE_REPOSITORY = REPOSITORY_DIRECTORY / ".env" +ENV_FILE_PROJECT = REPOSITORY_DIRECTORY / "openbb_terminal" / ".env" +ENV_FILE_SETTINGS = SETTINGS_DIRECTORY / ".env" +HIST_FILE_PROMPT = SETTINGS_DIRECTORY / ".openbb_terminal.his" +I18N_FILE = ASSETS_DIRECTORY / "i18n" + + +DEFAULT_ROUTINES_URL = "https://openbb-cms.directus.app/items/Routines" +TIMEOUT = 30 +CONNECTION_ERROR_MSG = "[red]Connection error.[/red]" +CONNECTION_TIMEOUT_MSG = "[red]Connection timeout.[/red]" +SCRIPT_TAGS = [ + "stocks", + "crypto", + "etf", + "economy", + "forex", + "fixed income", + "alternative", + "funds", + "bonds", + "macro", + "mutual funds", + "equities", + "options", + "dark pool", + "shorts", + "insider", + "behavioral analysis", + "fundamental analysis", + "technical analysis", + "quantitative analysis", + "forecasting", + "government", + "comparison", + "nft", + "on chain", + "off chain", + "screener", + "report", + "overview", + "rates", + "econometrics", + "portfolio", + "real estate", +] +AVAILABLE_FLAIRS = { + ":openbb": "(🦋)", + ":bug": "(🐛)", + ":rocket": "(🚀)", + ":diamond": "(💎)", + ":stars": "(✨)", + ":baseball": "(⚾)", + ":boat": "(⛵)", + ":phone": "(☎)", + ":mercury": "(☿)", + ":hidden": "", + ":sun": "(☼)", + ":moon": "(☾)", + ":nuke": "(☢)", + ":hazard": "(☣)", + ":tunder": "(☈)", + ":king": "(♔)", + ":queen": "(♕)", + ":knight": "(♘)", + ":recycle": "(♻)", + ":scales": "(⚖)", + ":ball": "(⚽)", + ":golf": "(⛳)", + ":piece": "(☮)", + ":yy": "(☯)", +} diff --git a/openbb_terminal/openbb_terminal/config/menu_text.py b/openbb_terminal/openbb_terminal/config/menu_text.py new file mode 100644 index 00000000000..bd6234097d6 --- /dev/null +++ b/openbb_terminal/openbb_terminal/config/menu_text.py @@ -0,0 +1,262 @@ +"""Rich Module.""" + +__docformat__ = "numpy" + +from typing import Dict, List, Optional, Union + +import i18n +from openbb import obb + +# https://rich.readthedocs.io/en/stable/appendix/colors.html#appendix-colors +# https://rich.readthedocs.io/en/latest/highlighting.html#custom-highlighters + + +RICH_TAGS = [ + "[menu]", + "[/menu]", + "[cmds]", + "[/cmds]", + "[info]", + "[/info]", + "[param]", + "[/param]", + "[src]", + "[/src]", + "[help]", + "[/help]", +] + +USE_COLOR = True + + +def get_ordered_list_sources(command_path: str) -> List: + """Return the preferred source for the given command. + + Parameters + ---------- + command_path: str + The command to find the source for. E.g. "/equity/price/historical + + Returns + ------- + List + The list of sources for the given command. + """ + command_reference = obb.reference.get("paths", {}).get(command_path, {}) # type: ignore + if command_reference: + providers = list(command_reference["parameters"].keys()) + return [provider for provider in providers if provider != "standard"] + return [] + + +class MenuText: + """Create menu text with rich colors to be displayed by terminal.""" + + def __init__(self, path: str = "", column_sources: int = 100): + """Initialize menu help. + + Parameters + ---------- + path : str + path to the menu that is being created + column_sources : int + column width from which to start displaying sources + """ + self.menu_text = "" + self.menu_path = path + self.col_src = column_sources + self.warnings: List[Dict[str, str]] = [] + + def add_raw(self, raw_text: str): + """Append raw text (no translation) to a menu. + + Parameters + ---------- + raw_text : str + raw text to be appended to the menu + """ + self.menu_text += raw_text + + def add_custom(self, key: str): + """Append custom text (after translation from key) to a menu. + + Parameters + ---------- + key : str + key to get translated text and add to the menu + """ + self.menu_text += f"{i18n.t(self.menu_path + key)}" + + def add_info(self, key_info: str): + """Append info text (after translation from key) to a menu. + + Parameters + ---------- + key_info : str + key to get translated text and add to the menu as info + """ + self.menu_text += f"[info]{i18n.t(self.menu_path + key_info)}:[/info]\n" + + def add_param(self, key_param: str, value: str, col_align: int = 0): + """Append info text (after translation from key) to a menu. + + Parameters + ---------- + key_param : str + key to get translated text and add to the menu as parameter + value : str + value to display in front of the parameter + col_align : int + column alignment for the value. This allows for a better UX experience. + """ + parameter_translated = i18n.t(self.menu_path + key_param) + space = ( + (col_align - len(parameter_translated)) * " " + if col_align > len(parameter_translated) + else "" + ) + self.menu_text += f"[param]{parameter_translated}{space}:[/param] {value}\n" + + def _adjust_command_length(self, key_command: str) -> str: + """Adjust the length of the command if it is too long. + + Parameters + ---------- + key_command : str + command to be adjusted + + Returns + ------- + str + adjusted command + """ + if len(key_command) > 18: + new_key_command = key_command[:18] # Default to trimming to 18 characters + + if "_" in key_command: + key_command_split = key_command.split("_") + + new_key_command = ( + "_".join(key_command_split[:2]) + if len(key_command_split) > 2 + else key_command_split[0] + ) + + if len(new_key_command) > 18: + new_key_command = new_key_command[:18] + + if new_key_command != key_command: + self.warnings.append( + { + "warning": "Command name too long", + "command": key_command, + "trimmed_command": new_key_command, + } + ) + key_command = new_key_command + + return key_command + + def _handle_command_description( + self, key_command: str, command_description: str + ) -> str: + """Handle the command description. + + Parameters + ---------- + key_command : str + command to be adjusted + command_description : str + description of the command + + Returns + ------- + str + adjusted command description + """ + if not command_description: + command_description = i18n.t(self.menu_path + key_command) + if command_description == self.menu_path + key_command: + command_description = "" + return ( + command_description[:88] + "..." + if len(command_description) > 91 + else command_description + ) + + def add_cmd( + self, key_command: str, condition: bool = True, command_description: str = "" + ): + """Append command text (after translation from key) to a menu. + + Parameters + ---------- + key_command : str + key command to be executed by user. It is also used as a key to get description of command. + condition : bool + condition in which command is available to user. I.e. displays command and description. + If condition is false, the command line is greyed out. + """ + key_command = self._adjust_command_length(key_command) + command_description = self._handle_command_description( + key_command, command_description + ) + spacing = (23 - (len(key_command) + 4)) * " " + + cmd = f"{key_command}{spacing}{command_description}" + cmd = f"[cmds] {cmd}[/cmds]" if condition else f"[unvl] {cmd}[/unvl]" + + sources = get_ordered_list_sources(f"{self.menu_path}{key_command}") + + if sources: + space = (self.col_src - len(cmd)) * " " if self.col_src > len(cmd) else " " + cmd += f"{space}[src][{', '.join(sources)}][/src]" + + self.menu_text += cmd + "\n" + + def add_menu( + self, + key_menu: str, + condition: Optional[Union[bool, str]] = True, + menu_description: str = "", + ): + """Append menu text (after translation from key) to a menu. + + Parameters + ---------- + key_menu : str + key menu to be executed by user. It is also used as a key to get description of menu. + condition : bool + condition in which menu is available to user. I.e. displays menu and description. + If condition is false, the menu line is greyed out. + """ + spacing = (23 - (len(key_menu) + 4)) * " " + + if menu_description: + menu = f"{key_menu}{spacing}{menu_description}" + else: + menu_description = i18n.t(self.menu_path + key_menu) + if menu_description == self.menu_path + key_menu: + menu_description = "" + menu = f"{key_menu}{spacing}{menu_description}" + + if condition: + self.menu_text += f"[menu]> {menu}[/menu]\n" + else: + self.menu_text += f"[unvl]> {menu}[/unvl]\n" + + def add_setting(self, key_setting: str, status: bool = True): + """Append menu text (after translation from key) to a menu. + + Parameters + ---------- + key_setting : str + key setting to be set by user. It is also used as a key to get description of the setting. + status : bool + status of the current setting. If true the line will be green, otherwise red. + """ + spacing = (23 - (len(key_setting) + 4)) * " " + if status: + self.menu_text += f"[green] {key_setting}{spacing}{i18n.t(self.menu_path + key_setting)}[/green]\n" + else: + self.menu_text += f"[red] {key_setting}{spacing}{i18n.t(self.menu_path + key_setting)}[/red]\n" diff --git a/openbb_terminal/openbb_terminal/config/setup.py b/openbb_terminal/openbb_terminal/config/setup.py new file mode 100644 index 00000000000..c6547a62d59 --- /dev/null +++ b/openbb_terminal/openbb_terminal/config/setup.py @@ -0,0 +1,84 @@ +"""Configuration for the terminal.""" + +import copy +from pathlib import Path +from typing import TYPE_CHECKING, List, Optional, TypeVar + +import i18n + +from openbb_terminal.config.constants import I18N_FILE + +if TYPE_CHECKING: + from openbb_charting.core.openbb_figure import OpenBBFigure + +# ruff: noqa:PLW0603 + +OpenBBFigureT = TypeVar("OpenBBFigureT", bound="OpenBBFigure") +HOLD: bool = False +COMMAND_ON_CHART: bool = True +current_figure: Optional[OpenBBFigureT] = None # type: ignore +new_axis: bool = True +legends: List = [] +last_legend = "" + + +# pylint: disable=global-statement +def set_last_legend(leg: str): + """Set the last legend.""" + global last_legend + last_legend = copy.deepcopy(leg) + + +def reset_legend() -> None: + """Reset the legend.""" + global legends + legends = [] + + +def get_legends() -> list: + """Get the legends.""" + return legends + + +def set_same_axis() -> None: + """Set the same axis.""" + global new_axis + new_axis = False + + +def set_new_axis() -> None: + """Set the new axis.""" + global new_axis + new_axis = True + + +def make_new_axis() -> bool: + """Make a new axis.""" + return new_axis + + +def get_current_figure() -> Optional["OpenBBFigure"]: + """Get the current figure.""" + return current_figure + + +def set_current_figure(fig: Optional[OpenBBFigureT] = None): + """Set the current figure.""" + # pylint: disable=global-statement + global current_figure + current_figure = fig + + +def setup_i18n(i18n_path: Path = I18N_FILE, lang: str = "en"): + """Select the terminal translation language.""" + i18n.load_path.append(i18n_path) + i18n.set("locale", lang) + i18n.set("filename_format", "{locale}.{format}") + + +def setup_config_terminal(): + """Setup pre-launch configurations for the terminal.""" + # pylint: disable=import-outside-toplevel + from openbb_terminal.session import Session + + setup_i18n(lang=Session().settings.USE_LANGUAGE) diff --git a/openbb_terminal/openbb_terminal/config/style.py b/openbb_terminal/openbb_terminal/config/style.py new file mode 100644 index 00000000000..dccdf0bda2b --- /dev/null +++ b/openbb_terminal/openbb_terminal/config/style.py @@ -0,0 +1,108 @@ +"""Chart and style helpers for Plotly.""" + +# pylint: disable=C0302,R0902,W3301 +import json +from pathlib import Path +from typing import Any, Dict, List, Optional + +from rich.console import Console + +from openbb_terminal.config.constants import STYLES_DIRECTORY + +console = Console() + + +class Style: + """The class that helps with handling of style configurations. + + It serves styles for 2 libraries. For `Plotly` this class serves absolute paths + to the .pltstyle files. For `Plotly` and `Rich` this class serves custom + styles as python dictionaries. + """ + + STYLES_REPO = STYLES_DIRECTORY + + console_styles_available: Dict[str, Path] = {} + console_style: Dict[str, Any] = {} + + line_color: str = "" + up_color: str = "" + down_color: str = "" + up_colorway: List[str] = [] + down_colorway: List[str] = [] + up_color_transparent: str = "" + down_color_transparent: str = "" + + line_width: float = 1.5 + + def __init__( + self, + style: Optional[str] = "", + directory: Optional[Path] = None, + ): + """Initialize the class.""" + self._load(directory) + self.apply(style, directory) + + def apply( + self, style: Optional[str] = None, directory: Optional[Path] = None + ) -> None: + """Apply the style to the console.""" + if style: + if style in self.console_styles_available: + json_path: Optional[Path] = self.console_styles_available[style] + else: + self._load(directory) + if style in self.console_styles_available: + json_path = self.console_styles_available[style] + else: + console.print(f"\nInvalid console style '{style}', using default.") + json_path = self.console_styles_available.get("dark", None) + + if json_path: + self.console_style = self._from_json(json_path) + else: + console.print("Error loading default.") + + def _from_directory(self, folder: Optional[Path]) -> None: + """Load custom styles from folder. + + Parses the styles/default and styles/user folders and loads style files. + To be recognized files need to follow a naming convention: + *.pltstyle - plotly stylesheets + *.richstyle.json - rich stylesheets + + Parameters + ---------- + folder : str + Path to the folder containing the stylesheets + """ + if not folder or not folder.exists(): + return + + for attr, ext in zip( + ["console_styles_available"], + [".richstyle.json"], + ): + for file in folder.rglob(f"*{ext}"): + getattr(self, attr)[file.name.replace(ext, "")] = file + + def _load(self, directory: Optional[Path] = None) -> None: + """Load custom styles from default and user folders.""" + self._from_directory(self.STYLES_REPO) + self._from_directory(directory) + + def _from_json(self, file: Path) -> Dict[str, Any]: + """Load style from json file.""" + with open(file) as f: + json_style: dict = json.load(f) + for key, value in json_style.items(): + json_style[key] = value.replace( + " ", "" + ) # remove whitespaces so Rich can parse it + return json_style + + @property + def available_styles(self) -> List[str]: + """Return available styles.""" + return list(self.console_styles_available.keys()) diff --git a/openbb_terminal/parent_classes.py b/openbb_terminal/openbb_terminal/controllers/base_controller.py similarity index 50% rename from openbb_terminal/parent_classes.py rename to openbb_terminal/openbb_terminal/controllers/base_controller.py index 9d8a45506fd..21759214b29 100644 --- a/openbb_terminal/parent_classes.py +++ b/openbb_terminal/openbb_terminal/controllers/base_controller.py @@ -1,85 +1,38 @@ -"""Parent Classes.""" +"""Base controller for the terminal.""" -__docformat__ = "numpy" - -# pylint: disable=C0301,C0302,R0902,global-statement,too-many-boolean-expressions - -# IMPORTS STANDARD import argparse import difflib -import json -import logging import os import re from abc import ABCMeta, abstractmethod -from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Union +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, List, Literal, Optional, Union -# IMPORTS THIRDPARTY -import numpy as np -import pandas as pd from prompt_toolkit.formatted_text import HTML from prompt_toolkit.styles import Style -from rich.markdown import Markdown -# IMPORTS INTERNAL -import openbb_terminal.core.session.local_model as Local -from openbb_terminal import config_terminal -from openbb_terminal.account.show_prompt import get_show_prompt -from openbb_terminal.core.completer.choices import build_controller_choice_map -from openbb_terminal.core.config.paths import HIST_FILE_PATH -from openbb_terminal.core.session import hub_model as Hub -from openbb_terminal.core.session.constants import SCRIPT_TAGS -from openbb_terminal.core.session.current_user import get_current_user, is_local -from openbb_terminal.cryptocurrency import cryptocurrency_helpers -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( +from openbb_terminal.config import setup +from openbb_terminal.config.completer import NestedCompleter +from openbb_terminal.config.constants import SCRIPT_TAGS +from openbb_terminal.controllers.choices import build_controller_choice_map +from openbb_terminal.controllers.hub_service import upload_routine +from openbb_terminal.controllers.utils import ( check_file_type_saved, check_positive, - export_data, - get_flair, + get_flair_and_username, parse_and_split_input, - prefill_form, - screenshot, - search_wikipedia, - set_command_location, - support_message, - system_clear, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.rich_config import console, get_ordered_list_sources -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.terminal_helper import ( - is_auth_enabled, - open_openbb_documentation, print_guest_block_msg, + remove_file, + system_clear, ) +from openbb_terminal.session import Session -from .helper_classes import TerminalStyle as _TerminalStyle - -logger = logging.getLogger(__name__) - +# pylint: disable=C0301,C0302,R0902,global-statement,too-many-boolean-expressions # pylint: disable=R0912 -NO_EXPORT = 0 -EXPORT_ONLY_RAW_DATA_ALLOWED = 1 -EXPORT_ONLY_FIGURES_ALLOWED = 2 -EXPORT_BOTH_RAW_DATA_AND_FIGURES = 3 - controllers: Dict[str, Any] = {} -CRYPTO_SOURCES = { - "bin": "Binance", - "CoinGecko": "CoinGecko", - "cp": "CoinPaprika", - "cb": "Coinbase", - "YahooFinance": "YahooFinance", -} - -SUPPORT_TYPE = ["bug", "suggestion", "question", "generic"] - # TODO: We should try to avoid these global variables RECORD_SESSION = False @@ -108,31 +61,24 @@ class BaseController(metaclass=ABCMeta): "exit", "r", "reset", - "support", - "wiki", "stop", - "screenshot", "hold", + "whoami", ] - if is_auth_enabled(): - CHOICES_COMMON += ["whoami"] - CHOICES_COMMANDS: List[str] = [] CHOICES_MENUS: List[str] = [] - SUPPORT_CHOICES: dict = {} - ABOUT_CHOICES: dict = {} HOLD_CHOICES: dict = {} NEWS_CHOICES: dict = {} COMMAND_SEPARATOR = "/" KEYS_MENU = "keys" + COMMAND_SEPARATOR - TRY_RELOAD = False PATH: str = "" FILE_PATH: str = "" CHOICES_GENERATION = False @property def choices_default(self): + """Return the default choices.""" choices = ( build_controller_choice_map(controller=self) if self.CHOICES_GENERATION @@ -173,49 +119,6 @@ class BaseController(metaclass=ABCMeta): self.parser.exit_on_error = False # type: ignore self.parser.add_argument("cmd", choices=self.controller_choices) - # Add in about options - self.ABOUT_CHOICES = { - c: None for c in self.CHOICES_COMMANDS + self.CHOICES_MENUS - } - - # Remove common choices from list of support commands - self.support_commands = [ - c for c in self.controller_choices if c not in self.CHOICES_COMMON - ] - - # Add in support options - support_choices: dict = {c: {} for c in self.controller_choices} - - support_choices = {c: None for c in (["generic"] + self.support_commands)} - - support_choices["--command"] = { - c: None for c in (["generic"] + self.support_commands) - } - - support_choices["-c"] = {c: None for c in (["generic"] + self.support_commands)} - - support_choices["--type"] = {c: None for c in (SUPPORT_TYPE)} - - self.SUPPORT_CHOICES = support_choices - - self.HELP_CHOICES = { - c: None for c in ["on", "off", "-s", "--sameaxis", "--title"] - } - - # Add in news options - news_choices = [ - "--term", - "-t", - "--sources", - "-s", - "--help", - "-h", - "--tag", - "--taglist", - "--sourcelist", - ] - self.NEWS_CHOICES = {c: None for c in news_choices} - def check_path(self) -> None: """Check if command path is valid.""" path = self.PATH @@ -230,7 +133,7 @@ class BaseController(metaclass=ABCMeta): def load_class(self, class_ins, *args, **kwargs): """Check for an existing instance of the controller before creating a new one.""" - current_user = get_current_user() + settings = Session().settings self.save_class() arguments = len(args) + len(kwargs) # Due to the 'arguments == 1' condition, we actually NEVER load a class @@ -249,16 +152,7 @@ class BaseController(metaclass=ABCMeta): if ( class_ins.PATH in controllers and arguments == 1 - and current_user.preferences.REMEMBER_CONTEXTS - ): - old_class = controllers[class_ins.PATH] - old_class.queue = self.queue - return old_class.menu() - # Add another case so options data is saved - if ( - class_ins.PATH == "/stocks/options/" - and current_user.preferences.REMEMBER_CONTEXTS - and "/stocks/options/" in controllers + and settings.REMEMBER_CONTEXTS ): old_class = controllers[class_ins.PATH] old_class.queue = self.queue @@ -266,6 +160,7 @@ class BaseController(metaclass=ABCMeta): return class_ins(*args, **kwargs).menu() def call_hold(self, other_args: List[str]) -> None: + """Process hold command.""" self.save_class() parser = argparse.ArgumentParser( add_help=False, @@ -306,23 +201,23 @@ class BaseController(metaclass=ABCMeta): ) if ns_parser: if ns_parser.option == "on": - config_terminal.HOLD = True - config_terminal.COMMAND_ON_CHART = False + setup.HOLD = True + setup.COMMAND_ON_CHART = False if ns_parser.axes: - config_terminal.set_same_axis() + setup.set_same_axis() else: - config_terminal.set_new_axis() + setup.set_new_axis() if ns_parser.option == "off": - config_terminal.HOLD = False - if config_terminal.get_current_figure() is not None: + setup.HOLD = False + if setup.get_current_figure() is not None: # create a subplot - fig = config_terminal.get_current_figure() + fig = setup.get_current_figure() if fig is None: return - if not fig.has_subplots and not config_terminal.make_new_axis(): + if not fig.has_subplots and not setup.make_new_axis(): fig.set_subplots(1, 1, specs=[[{"secondary_y": True}]]) - if config_terminal.make_new_axis(): + if setup.make_new_axis(): for i, trace in enumerate(fig.select_traces()): trace.yaxis = f"y{i+1}" @@ -341,7 +236,6 @@ class BaseController(metaclass=ABCMeta): ), tickfont=dict( size=18, - color=_TerminalStyle().get_colors()[i], ), title=dict( font=dict( @@ -358,23 +252,23 @@ class BaseController(metaclass=ABCMeta): else: fig.update_yaxes(title="") - if any(config_terminal.get_legends()): + if any(setup.get_legends()): for trace, new_name in zip( - fig.select_traces(), config_terminal.get_legends() + fig.select_traces(), setup.get_legends() ): if new_name: trace.name = new_name fig.update_layout(title=" ".join(ns_parser.title)) fig.show() - config_terminal.COMMAND_ON_CHART = True + setup.COMMAND_ON_CHART = True - config_terminal.set_current_figure(None) - config_terminal.reset_legend() + setup.set_current_figure(None) + setup.reset_legend() def save_class(self) -> None: """Save the current instance of the class to be loaded later.""" - if get_current_user().preferences.REMEMBER_CONTEXTS: + if Session().settings.REMEMBER_CONTEXTS: controllers[self.PATH] = self def custom_reset(self) -> List[str]: @@ -417,49 +311,6 @@ class BaseController(metaclass=ABCMeta): ) return commands - def contains_keys(self, string_to_check: str) -> bool: - """Check if string contains keys.""" - if self.KEYS_MENU in string_to_check or self.KEYS_MENU in self.PATH: - return True - return False - - def log_queue(self) -> None: - """Log command queue.""" - if self.queue: - joined_queue = self.COMMAND_SEPARATOR.join(self.queue) - if not self.contains_keys(joined_queue): - queue = {"path": self.PATH, "queue": joined_queue} - logger.info( - "QUEUE: %s", json.dumps(queue, default=str, ensure_ascii=False) - ) - - def log_cmd_and_queue( - self, known_cmd: str, other_args_str: str, the_input: str - ) -> None: - """Log command and command queue. - - Parameters - ---------- - known_cmd : str - Current command - other_args_str : str - Command arguments - the_input : str - Raw command input (command queue) - """ - if not self.contains_keys(the_input): - cmd = { - "path": self.PATH, - "known_cmd": known_cmd, - "other_args": other_args_str, - "input": the_input, - } - logger.info("CMD: %s", json.dumps(cmd)) - - if the_input not in self.KEYS_MENU: - self.log_queue() - - @log_start_end(log=logger) def switch(self, an_input: str) -> List[str]: """Process and dispatch input. @@ -471,7 +322,7 @@ class BaseController(metaclass=ABCMeta): actions = self.parse_input(an_input) if an_input and an_input != "reset": - console.print() + Session().console.print() # Empty command if len(actions) == 0: @@ -511,17 +362,12 @@ class BaseController(metaclass=ABCMeta): elif known_args.cmd == "r": known_args.cmd = "reset" - set_command_location(f"{self.PATH}{known_args.cmd}") - self.log_cmd_and_queue(known_args.cmd, ";".join(other_args), an_input) - getattr( self, "call_" + known_args.cmd, lambda _: "Command not recognized!", )(other_args) - self.log_queue() - if ( an_input and an_input != "reset" @@ -529,78 +375,31 @@ class BaseController(metaclass=ABCMeta): not self.queue or (self.queue and self.queue[0] not in ("quit", "help")) ) ): - console.print() + Session().console.print() return self.queue - @log_start_end(log=logger) def call_cls(self, _) -> None: """Process cls command.""" system_clear() - @log_start_end(log=logger) def call_home(self, _) -> None: """Process home command.""" self.save_class() - if ( - self.PATH.count("/") == 1 - and get_current_user().preferences.ENABLE_EXIT_AUTO_HELP - ): + if self.PATH.count("/") == 1 and Session().settings.ENABLE_EXIT_AUTO_HELP: self.print_help() for _ in range(self.PATH.count("/") - 1): self.queue.insert(0, "quit") - @log_start_end(log=logger) def call_help(self, _) -> None: """Process help command.""" self.print_help() - @log_start_end(log=logger) - def call_about(self, other_args: List[str]) -> None: - """Process about command.""" - description = "Display the documentation of the menu or command." - if self.CHOICES_COMMANDS and self.CHOICES_MENUS: - description += ( - f" E.g. 'about {self.CHOICES_COMMANDS[0]}' opens a guide about the command " - f"{self.CHOICES_COMMANDS[0]} and 'about {self.CHOICES_MENUS[0]}' opens a guide about the " - f"menu {self.CHOICES_MENUS[0]}." - ) - - parser = argparse.ArgumentParser( - add_help=False, prog="about", description=description - ) - parser.add_argument( - "-c", - "--command", - type=str, - dest="command", - default=None, - help="Obtain documentation on the given command or menu", - choices=self.CHOICES_COMMANDS + self.CHOICES_MENUS + self.CHOICES_COMMON, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - arg_type = "" - if ns_parser.command in self.CHOICES_COMMANDS: - arg_type = "command" - elif ns_parser.command in self.CHOICES_MENUS: - arg_type = "menu" - - open_openbb_documentation( - self.PATH, command=ns_parser.command, arg_type=arg_type - ) - - @log_start_end(log=logger) def call_quit(self, _) -> None: """Process quit menu command.""" self.save_class() self.queue.insert(0, "quit") - @log_start_end(log=logger) def call_exit(self, _) -> None: # Not sure how to handle controller loading here """Process exit terminal command.""" @@ -608,12 +407,11 @@ class BaseController(metaclass=ABCMeta): for _ in range(self.PATH.count("/")): self.queue.insert(0, "quit") - if not is_local(): - Local.remove(get_current_user().preferences.USER_ROUTINES_DIRECTORY / "hub") - if not get_current_user().profile.remember: - Local.remove(HIST_FILE_PATH) + if not Session().is_local(): + remove_file( + Path(Session().user.preferences.export_directory, "routines", "hub") + ) - @log_start_end(log=logger) def call_reset(self, _) -> None: """Process reset command. @@ -631,115 +429,6 @@ class BaseController(metaclass=ABCMeta): for _ in range(len(self.path)): self.queue.insert(0, "quit") - @log_start_end(log=logger) - def call_resources(self, other_args: List[str]) -> None: - """Process resources command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="resources", - description="Display available markdown resources.", - ) - ns_parser = self.parse_simple_args(parser, other_args) - - if ns_parser: - if os.path.isfile(self.FILE_PATH): - with open(self.FILE_PATH) as f: - console.print(Markdown(f.read())) - - else: - console.print("No resources available.\n") - - @log_start_end(log=logger) - def call_support(self, other_args: List[str]) -> None: - """Process support command.""" - self.save_class() - - path_split = [x for x in self.PATH.split("/") if x != ""] - main_menu = path_split[0] if len(path_split) else "home" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="support", - description="Submit your support request", - ) - - parser.add_argument( - "-c", - "--command", - action="store", - dest="command", - choices=["generic"] + self.support_commands, - help="Command that needs support", - ) - - parser.add_argument( - "--msg", - "-m", - action="store", - type=support_message, - nargs="+", - dest="msg", - required=False, - default="", - help="Message to send. Enclose it with double quotes", - ) - - parser.add_argument( - "--type", - "-t", - action="store", - dest="type", - required=False, - choices=SUPPORT_TYPE, - default="generic", - help="Support ticket type", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - - ns_parser = self.parse_simple_args(parser, other_args) - - if ns_parser: - prefill_form( - ticket_type=ns_parser.type, - menu=main_menu, - command=ns_parser.command, - message=" ".join(ns_parser.msg), - path=self.PATH, - ) - - @log_start_end(log=logger) - def call_wiki(self, other_args: List[str]) -> None: - """Process wiki command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="wiki", - description="Search Wikipedia", - ) - parser.add_argument( - "--expression", - "-e", - action="store", - nargs="+", - dest="expression", - required=True, - default="", - help="Expression to search for", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-e") - - ns_parser = self.parse_simple_args(parser, other_args) - - if ns_parser and ns_parser.expression: - expression = " ".join(ns_parser.expression) - search_wikipedia(expression) - - @log_start_end(log=logger) def call_record(self, other_args) -> None: """Process record command.""" parser = argparse.ArgumentParser( @@ -764,7 +453,7 @@ class BaseController(metaclass=ABCMeta): type=str, dest="description", help="The description of the routine", - default=f"Routine recorded at {datetime.now().strftime('%H:%M')} from the OpenBB Terminal", + default=f"Routine recorded at {datetime.now().strftime('%H:%M')} from the OpenBB Platform CLI", nargs="+", ) parser.add_argument( @@ -814,7 +503,7 @@ class BaseController(metaclass=ABCMeta): if ns_parser: if not ns_parser.name: - console.print( + Session().console.print( "[red]Set a routine title by using the '-n' flag. E.g. 'record -n Morning routine'[/red]" ) return @@ -825,7 +514,7 @@ class BaseController(metaclass=ABCMeta): else ns_parser.tag1 ) if tag1 and tag1 not in SCRIPT_TAGS: - console.print( + Session().console.print( f"[red]The parameter 'tag1' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]" ) return @@ -836,7 +525,7 @@ class BaseController(metaclass=ABCMeta): else ns_parser.tag2 ) if tag2 and tag2 not in SCRIPT_TAGS: - console.print( + Session().console.print( f"[red]The parameter 'tag2' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]" ) return @@ -847,19 +536,19 @@ class BaseController(metaclass=ABCMeta): else ns_parser.tag3 ) if tag3 and tag3 not in SCRIPT_TAGS: - console.print( + Session().console.print( f"[red]The parameter 'tag3' needs to be one of the following {', '.join(SCRIPT_TAGS)}[/red]" ) return - if is_local() and not ns_parser.local: - console.print( + if Session().is_local() and not ns_parser.local: + Session().console.print( "[red]Recording session to the OpenBB Hub is not supported in guest mode.[/red]" ) - console.print( + Session().console.print( "\n[yellow]Sign to OpenBB Hub to register: http://openbb.co[/yellow]" ) - console.print( + Session().console.print( "\n[yellow]Otherwise set the flag '-l' to save the file locally.[/yellow]" ) return @@ -868,7 +557,7 @@ class BaseController(metaclass=ABCMeta): title = " ".join(ns_parser.name) if ns_parser.name else "" pattern = re.compile(r"^[a-zA-Z0-9\s]+$") if not pattern.match(title): - console.print( + Session().console.print( f"[red]Title '{title}' has invalid format. Please use only digits, characters and whitespaces.[/]" ) return @@ -894,29 +583,28 @@ class BaseController(metaclass=ABCMeta): SESSION_RECORDED_PUBLIC = ns_parser.public - console.print( + Session().console.print( f"[green]The routine '{title}' is successfully being recorded.[/green]" ) - console.print( + Session().console.print( "\n[yellow]Remember to run 'stop' command when you are done!\n[/yellow]" ) - @log_start_end(log=logger) def call_stop(self, _) -> None: """Process stop command.""" global RECORD_SESSION # noqa: PLW0603 global SESSION_RECORDED # noqa: PLW0603 if not RECORD_SESSION: - console.print( + Session().console.print( "[red]There is no session being recorded. Start one using the command 'record'[/red]\n" ) elif len(SESSION_RECORDED) < 5: - console.print( + Session().console.print( "[red]Run at least 4 commands before stopping recording a session.[/red]\n" ) else: - current_user = get_current_user() + current_user = Session().user # Check if the user just wants to store routine locally # This works regardless of whether they are logged in or not @@ -927,20 +615,20 @@ class BaseController(metaclass=ABCMeta): ) routine_file = os.path.join( - current_user.preferences.USER_ROUTINES_DIRECTORY, + f"{current_user.preferences.export_directory}/routines", title_for_local_storage, ) # If file already exists, add a timestamp to the name if os.path.isfile(routine_file): - i = console.input( + i = Session().console.input( "A local routine with the same name already exists, " "do you want to override it? (y/n): " ) - console.print("") + Session().console.print("") while i.lower() not in ["y", "yes", "n", "no"]: - i = console.input("Select 'y' or 'n' to proceed: ") - console.print("") + i = Session().console.input("Select 'y' or 'n' to proceed: ") + Session().console.print("") if i.lower() in ["n", "no"]: new_name = ( @@ -948,17 +636,24 @@ class BaseController(metaclass=ABCMeta): + title_for_local_storage ) routine_file = os.path.join( - current_user.preferences.USER_ROUTINES_DIRECTORY, + current_user.preferences.export_directory, + "routines", new_name, ) - console.print( + Session().console.print( f"[yellow]The routine name has been updated to '{new_name}'[/yellow]\n" ) # Writing to file + Path(os.path.dirname(routine_file)).mkdir(parents=True, exist_ok=True) + with open(routine_file, "w") as file1: - lines = ["# OpenBB Terminal - Routine", "\n"] - username = get_current_user().profile.username + lines = ["# OpenBB Platform CLI - Routine", "\n"] + + username = getattr( + Session().user.profile.hub_session, "username", "local" + ) + lines += [f"# Author: {username}", "\n\n"] if username else ["\n"] lines += [ f"# Title: {SESSION_RECORDED_NAME}", @@ -972,60 +667,47 @@ class BaseController(metaclass=ABCMeta): # Writing data to a file file1.writelines(lines) - console.print( + Session().console.print( f"[green]Your routine has been recorded and saved here: {routine_file}[/green]\n" ) # If user doesn't specify they want to store routine locally # Confirm that the user is logged in - elif not is_local(): - # routine = read_routine(file_name=routine_file) + elif not Session().is_local(): routine = "\n".join(SESSION_RECORDED[:-1]) + hub_session = current_user.profile.hub_session if routine is not None: + auth_header = ( + f"{hub_session.token_type} {hub_session.access_token.get_secret_value()}" + if hub_session + else None + ) kwargs = { - "auth_header": current_user.profile.get_auth_header(), + "auth_header": auth_header, "name": SESSION_RECORDED_NAME, "description": SESSION_RECORDED_DESCRIPTION, "routine": routine, "tags": SESSION_RECORDED_TAGS, "public": SESSION_RECORDED_PUBLIC, - "base_url": Hub.BackendEnvironment.BASE_URL, } - response = Hub.upload_routine(**kwargs) # type: ignore + response = upload_routine(**kwargs) # type: ignore if response is not None and response.status_code == 409: - i = console.input( + i = Session().console.input( "A routine with the same name already exists, " "do you want to replace it? (y/n): " ) - console.print("") + Session().console.print("") if i.lower() in ["y", "yes"]: kwargs["override"] = True # type: ignore - response = Hub.upload_routine(**kwargs) # type: ignore + response = upload_routine(**kwargs) # type: ignore else: - console.print("[info]Aborted.[/info]") + Session().console.print("[info]Aborted.[/info]") # Clear session to be recorded again RECORD_SESSION = False SESSION_RECORDED = list() - @log_start_end(log=logger) - def call_screenshot(self, other_args: List[str]) -> None: - """Process screenshot command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="screenshot", - description="Screenshot terminal window or plot figure open into an OpenBB frame. " - "Default target is plot if there is one open, otherwise it's terminal window. " - " In case the user wants the terminal window, it can be forced with '-t` or '--terminal' flag passed.", - ) - ns_parser = self.parse_simple_args(parser, other_args) - - if ns_parser: - screenshot() - - @log_start_end(log=logger) def call_whoami(self, other_args: List[str]) -> None: """Process whoami command.""" parser = argparse.ArgumentParser( @@ -1037,11 +719,16 @@ class BaseController(metaclass=ABCMeta): ns_parser = self.parse_simple_args(parser, other_args) if ns_parser: - current_user = get_current_user() - local_user = is_local() + current_user = Session().user + local_user = Session().is_local() if not local_user: - console.print(f"[info]email:[/info] {current_user.profile.email}") - console.print(f"[info]uuid:[/info] {current_user.profile.uuid}") + hub_session = current_user.profile.hub_session + Session().console.print( + f"[info]email:[/info] {hub_session.email if hub_session else 'N/A'}" + ) + Session().console.print( + f"[info]uuid:[/info] {hub_session.user_uuid if hub_session else 'N/A'}" + ) else: print_guest_block_msg() @@ -1061,29 +748,27 @@ class BaseController(metaclass=ABCMeta): ns_parser: Namespace with parsed arguments """ - current_user = get_current_user() - parser.add_argument( "-h", "--help", action="store_true", help="show this help message" ) - if current_user.preferences.USE_CLEAR_AFTER_CMD: + if Session().settings.USE_CLEAR_AFTER_CMD: system_clear() try: (ns_parser, l_unknown_args) = parser.parse_known_args(other_args) except SystemExit: # In case the command has required argument that isn't specified - console.print("\n") + Session().console.print("\n") return None if ns_parser.help: txt_help = parser.format_help() - console.print(f"[help]{txt_help}[/help]") + Session().console.print(f"[help]{txt_help}[/help]") return None if l_unknown_args: - console.print( + Session().console.print( f"The following args couldn't be interpreted: {l_unknown_args}\n" ) @@ -1094,7 +779,9 @@ class BaseController(metaclass=ABCMeta): cls, parser: argparse.ArgumentParser, other_args: List[str], - export_allowed: int = NO_EXPORT, + export_allowed: Literal[ + "no_export", "raw_data_only", "figures_only", "raw_data_and_figures" + ] = "no_export", raw: bool = False, limit: int = 0, ): @@ -1106,9 +793,8 @@ class BaseController(metaclass=ABCMeta): Parser with predefined arguments other_args: List[str] list of arguments to parse - export_allowed: int - Choose from NO_EXPORT, EXPORT_ONLY_RAW_DATA_ALLOWED, - EXPORT_ONLY_FIGURES_ALLOWED and EXPORT_BOTH_RAW_DATA_AND_FIGURES + export_allowed: Literal["no_export", "raw_data_only", "figures_only", "raw_data_and_figures"] + Export options raw: bool Add the --raw flag limit: int @@ -1123,7 +809,7 @@ class BaseController(metaclass=ABCMeta): "-h", "--help", action="store_true", help="show this help message" ) - if config_terminal.HOLD: + if setup.HOLD: parser.add_argument( "--legend", type=str, @@ -1133,14 +819,14 @@ class BaseController(metaclass=ABCMeta): help="Label for legend when hold is on.", ) - if export_allowed > NO_EXPORT: + if export_allowed != "no_export": choices_export = [] help_export = "Does not export!" - if export_allowed == EXPORT_ONLY_RAW_DATA_ALLOWED: + if export_allowed == "raw_data_only": choices_export = ["csv", "json", "xlsx"] help_export = "Export raw data into csv, json, xlsx" - elif export_allowed == EXPORT_ONLY_FIGURES_ALLOWED: + elif export_allowed == "figures_only": choices_export = ["png", "jpg", "pdf", "svg"] help_export = "Export figure into png, jpg, pdf, svg " else: @@ -1157,8 +843,8 @@ class BaseController(metaclass=ABCMeta): # If excel is an option, add the sheet name if export_allowed in [ - EXPORT_ONLY_RAW_DATA_ALLOWED, - EXPORT_BOTH_RAW_DATA_AND_FIGURES, + "raw_data_only", + "raw_data_and_figures", ]: parser.add_argument( "--sheet-name", @@ -1185,29 +871,31 @@ class BaseController(metaclass=ABCMeta): help="Number of entries to show in data.", type=check_positive, ) - sources = get_ordered_list_sources(f"{cls.PATH}{parser.prog}") - # Allow to change source if there is more than one - if len(sources) > 1: - parser.add_argument( - "--source", - action="store", - dest="source", - choices=sources, - default=sources[0], # the first source from the list is the default - help="Data source to select from", - ) - - current_user = get_current_user() - - if current_user.preferences.USE_CLEAR_AFTER_CMD: + if Session().settings.USE_CLEAR_AFTER_CMD: system_clear() + if "--help" in other_args or "-h" in other_args: + txt_help = parser.format_help() + "\n" + if parser.prog != "about": + txt_help += ( + f"For more information and examples, use 'about {parser.prog}' " + f"to access the related guide.\n" + ) + Session().console.print(f"[help]{txt_help}[/help]") + return None + try: + # If the user uses a comma separated list of arguments, split them + for index, arg in enumerate(other_args): + if "," in arg: + parts = arg.split(",") + other_args[index : index + 1] = parts + (ns_parser, l_unknown_args) = parser.parse_known_args(other_args) if export_allowed in [ - EXPORT_ONLY_RAW_DATA_ALLOWED, - EXPORT_BOTH_RAW_DATA_AND_FIGURES, + "raw_data_only", + "raw_data_and_figures", ]: ns_parser.is_image = any( ext in ns_parser.export for ext in ["png", "svg", "jpg", "pdf"] @@ -1218,30 +906,19 @@ class BaseController(metaclass=ABCMeta): return None - if ns_parser.help: - txt_help = parser.format_help() + "\n" - if parser.prog != "about": - txt_help += ( - f"For more information and examples, use 'about {parser.prog}' " - f"to access the related guide.\n" - ) - console.print(f"[help]{txt_help}[/help]") - return None - # This protects against the hidden loads in stocks/fa - if parser.prog != "load" and config_terminal.HOLD: - config_terminal.set_last_legend(" ".join(ns_parser.hold_legend_str)) + if parser.prog != "load" and setup.HOLD: + setup.set_last_legend(" ".join(ns_parser.hold_legend_str)) if l_unknown_args: - console.print( + Session().console.print( f"The following args couldn't be interpreted: {l_unknown_args}" ) return ns_parser def menu(self, custom_path_menu_above: str = ""): """Enter controller menu.""" - - current_user = get_current_user() + settings = Session().settings an_input = "HELP_ME" while True: @@ -1257,7 +934,7 @@ class BaseController(metaclass=ABCMeta): if len(self.queue) > 1: return self.queue[1:] - if current_user.preferences.ENABLE_EXIT_AUTO_HELP: + if settings.ENABLE_EXIT_AUTO_HELP: return ["help"] return [] @@ -1272,7 +949,9 @@ class BaseController(metaclass=ABCMeta): and an_input != "help" and an_input.split(" ")[0] in self.controller_choices ): - console.print(f"{get_flair()} {self.PATH} $ {an_input}") + Session().console.print( + f"{get_flair_and_username()} {self.PATH} $ {an_input}" + ) # Get input command from user else: @@ -1281,12 +960,12 @@ class BaseController(metaclass=ABCMeta): self.print_help() try: - # Get input from user using auto-completion - if session and current_user.preferences.USE_PROMPT_TOOLKIT: + prompt_session = Session().prompt_session + if prompt_session and settings.USE_PROMPT_TOOLKIT: # Check if toolbar hint was enabled - if current_user.preferences.TOOLBAR_HINT: - an_input = session.prompt( - f"{get_flair()} {self.PATH} $ ", + if settings.TOOLBAR_HINT: + an_input = prompt_session.prompt( + f"{get_flair_and_username()} {self.PATH} $ ", completer=self.completer, search_ignore_case=True, bottom_toolbar=HTML( @@ -1303,14 +982,14 @@ class BaseController(metaclass=ABCMeta): ), ) else: - an_input = session.prompt( - f"{get_flair()} {self.PATH} $ ", + an_input = prompt_session.prompt( + f"{get_flair_and_username()} {self.PATH} $ ", completer=self.completer, search_ignore_case=True, ) # Get input from user without auto-completion else: - an_input = input(f"{get_flair()} {self.PATH} $ ") + an_input = input(f"{get_flair_and_username()} {self.PATH} $ ") except (KeyboardInterrupt, EOFError): # Exit in case of keyboard interrupt @@ -1323,17 +1002,8 @@ class BaseController(metaclass=ABCMeta): # Process the input command self.queue = self.switch(an_input) - if get_show_prompt() and an_input in ("login", "logout"): - return [an_input] - except SystemExit: - if not self.contains_keys(an_input): - logger.exception( - "The command '%s' doesn't exist on the %s menu.", - an_input, - self.PATH, - ) - console.print( + Session().console.print( f"[red]The command '{an_input}' doesn't exist on the {self.PATH} menu.[/red]\n", ) similar_cmd = difflib.get_close_matches( @@ -1350,367 +1020,14 @@ class BaseController(metaclass=ABCMeta): if candidate_input == an_input: an_input = "" self.queue = [] - console.print("\n") + Session().console.print("\n") continue an_input = candidate_input else: an_input = similar_cmd[0] - if not self.contains_keys(an_input) and an_input not in [ - "exit", - "quit", - "help", - ]: - logger.warning("Replacing by %s", an_input) - console.print(f"[green]Replacing by '{an_input}'.[/green]\n") - self.queue.insert(0, an_input) - elif self.TRY_RELOAD and get_current_user().preferences.RETRY_WITH_LOAD: - console.print(f"\nTrying `load {an_input}`\n") - self.queue.insert(0, "load " + an_input) - -class StockBaseController(BaseController, metaclass=ABCMeta): - """Base controller class for stocks related menus.""" - - def __init__(self, queue): - """Instantiate the base class for Stock Controllers that use a load function.""" - super().__init__(queue) - self.stock = pd.DataFrame() - self.interval = "1440min" - self.ticker = "" - self.start = "" - self.suffix = "" # To hold suffix for Yahoo Finance - self.add_info = stocks_helper.additional_info_about_ticker("") - self.TRY_RELOAD = True - self.USER_IMPORT_FILES = { - filepath.name: filepath - for file_type in ["csv"] - for filepath in ( - get_current_user().preferences.USER_CUSTOM_IMPORTS_DIRECTORY / "stocks" - ).rglob(f"*.{file_type}") - } - - def call_load(self, other_args: List[str]): - """Process load command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Load stock ticker to perform analysis on. When the data source" - + " is yf, an Indian ticker can be" - + " loaded by using '.NS' at the end, e.g. 'SBIN.NS'. See available market in" - + " https://help.yahoo.com/kb/exchanges-data-providers-yahoo-finance-sln2310.html.", - ) - parser.add_argument( - "-t", - "--ticker", - action="store", - dest="ticker", - required="-h" not in other_args and "--help" not in other_args, - help="Stock ticker", - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - default=(datetime.now() - timedelta(days=1100)).strftime("%Y-%m-%d"), - dest="start", - help="The starting date (format YYYY-MM-DD) of the stock", - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=datetime.now().strftime("%Y-%m-%d"), - dest="end", - help="The ending date (format YYYY-MM-DD) of the stock", - ) - parser.add_argument( - "-i", - "--interval", - action="store", - dest="interval", - type=int, - default=1440, - choices=[1, 5, 15, 30, 60], - help="Intraday stock minutes", - ) - parser.add_argument( - "-p", - "--prepost", - action="store_true", - default=False, - dest="prepost", - help="Pre/After market hours. Only reflected in 'YahooFinance' intraday data.", - ) - parser.add_argument( - "-f", - "--file", - default=None, - help="Path to load custom file.", - dest="filepath", - type=str, - ) - parser.add_argument( - "-m", - "--monthly", - action="store_true", - default=False, - help="Load monthly data", - dest="monthly", - ) - parser.add_argument( - "-w", - "--weekly", - action="store_true", - default=False, - help="Load weekly data", - dest="weekly", - ) - parser.add_argument( - "--performance", - dest="performance", - action="store_true", - default=False, - help="Show performance information.", - ) - parser.add_argument( - "--india", - dest="india", - action="store_true", - default=False, - help="Only works for yf source, when the ticker has .NS suffix as part of it.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.weekly and ns_parser.monthly: - console.print( - "[red]Only one of monthly or weekly can be selected.[/red]." - ) - return - if ns_parser.india and not ns_parser.ticker.endswith((".ns", ".NS")): - ns_parser.ticker = ns_parser.ticker + ".NS" - if ns_parser.filepath is None: - df_stock_candidate = stocks_helper.load( - ns_parser.ticker, - ns_parser.start, - ns_parser.interval, - ns_parser.end, - ns_parser.prepost, - ns_parser.source, - weekly=ns_parser.weekly, - monthly=ns_parser.monthly, - ) - else: - # This seems to block the .exe since the folder needs to be manually created - # This block makes sure that we only look for the file if the -f flag is used - # Adding files in the argparse choices, will fail for the .exe even without -f - file_location = self.USER_IMPORT_FILES.get( - ns_parser.filepath, ns_parser.filepath - ) - df_stock_candidate = stocks_helper.load_custom(str(file_location)) - if df_stock_candidate.empty: - return - is_df = isinstance(df_stock_candidate, pd.DataFrame) - if not ( - (is_df and df_stock_candidate.empty) - or (not is_df and not df_stock_candidate) - ): - self.stock = df_stock_candidate - if ( - ns_parser.interval == 1440 - and not ns_parser.weekly - and not ns_parser.monthly - and ns_parser.filepath is None - and self.PATH == "/stocks/" - and ns_parser.performance - ): - console.print() - stocks_helper.show_quick_performance(self.stock, ns_parser.ticker) - if "." in ns_parser.ticker: - self.ticker, self.suffix = ns_parser.ticker.upper().split(".") - if "." not in self.ticker: - self.ticker = ns_parser.ticker.upper() - else: - self.ticker = ns_parser.ticker.upper() - self.suffix = "" - - if ns_parser.source.lower() == "EODHD": - self.start = self.stock.index[0].to_pydatetime() - else: - self.start = ns_parser.start - self.interval = f"{ns_parser.interval}min" - - if self.PATH in ["/stocks/qa/"]: - self.stock["Returns"] = self.stock["Adj Close"].pct_change() - self.stock["LogRet"] = np.log(self.stock["Adj Close"]) - np.log( - self.stock["Adj Close"].shift(1) + Session().console.print( + f"[green]Replacing by '{an_input}'.[/green]\n" ) - self.stock["LogPrice"] = np.log(self.stock["Adj Close"]) - self.stock = self.stock.rename(columns={"Adj Close": "AdjClose"}) - self.stock = self.stock.dropna() - self.stock.columns = [x.lower() for x in self.stock.columns] - # pylint: disable=attribute-defined-outside-init - self.target = "returns" if not self.stock.empty else "" - - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - f"load_{self.ticker}", - self.stock.copy(), - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - -class CryptoBaseController(BaseController, metaclass=ABCMeta): - """Base controller class for crypto related menus.""" - - def __init__(self, queue): - """Instantiate the base class for Crypto Controllers that use a load function.""" - super().__init__(queue) - - self.symbol = "" - self.vs = "" - self.current_df = pd.DataFrame() - self.current_currency = "" - self.source = "" - self.current_interval = "" - self.exchange = "" - self.price_str = "" - self.interval = "" - self.resolution = "1D" - self.TRY_RELOAD = True - self.exchanges = cryptocurrency_helpers.get_exchanges_ohlc() - - def call_load(self, other_args): - """Process load command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="""Load crypto currency to perform analysis on. - Yahoo Finance is used as default source. - Other sources can be used such as 'ccxt' or 'cg' with --source. - If you select 'ccxt', you can then select any exchange with --exchange. - You can also select a specific interval with --interval.""", - ) - parser.add_argument( - "-c", - "--coin", - help="Coin to get. Must be coin symbol (e.g., btc, eth)", - dest="coin", - type=str, - required="-h" not in other_args and "--help" not in other_args, - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - default=(datetime.now() - timedelta(days=1100)).strftime("%Y-%m-%d"), - dest="start", - help="The starting date (format YYYY-MM-DD) of the crypto", - ) - - parser.add_argument( - "--exchange", - help="Exchange to search", - dest="exchange", - type=str, - default="binance", - choices=self.exchanges, - ) - - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=datetime.now().strftime("%Y-%m-%d"), - dest="end", - help="The ending date (format YYYY-MM-DD) of the crypto", - ) - parser.add_argument( - "-i", - "--interval", - action="store", - dest="interval", - type=str, - default="1440", - choices=["1", "5", "15", "30", "60", "240", "1440", "10080", "43200"], - help="The interval of the crypto", - ) - - parser.add_argument( - "--vs", - help="Quote currency (what to view coin vs). e.g., usdc, usdt, ... if source is ccxt, usd, eur, ... otherwise", # noqa - dest="vs", - default="usdt", - type=str, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ( - ns_parser.source in ("YahooFinance", "CoinGecko") - and ns_parser.vs == "usdt" - ): - ns_parser.vs = "usd" - if ns_parser.source == "YahooFinance" and ns_parser.interval in [ - "240", - "10080", - "43200", - ]: - console.print( - f"[red]YahooFinance does not support {ns_parser.interval}min interval[/red]" - ) - return - (self.current_df) = cryptocurrency_helpers.load( - symbol=ns_parser.coin.lower(), - to_symbol=ns_parser.vs, - end_date=ns_parser.end.strftime("%Y-%m-%d"), - start_date=ns_parser.start.strftime("%Y-%m-%d"), - interval=ns_parser.interval, - source=ns_parser.source, - exchange=ns_parser.exchange, - ) - if not self.current_df.empty: - self.vs = ns_parser.vs - self.exchange = ns_parser.exchange - self.source = ns_parser.source - self.current_interval = ns_parser.interval - self.current_currency = ns_parser.vs - self.symbol = ns_parser.coin.lower() - self.data = ( # pylint: disable=attribute-defined-outside-init - self.current_df.copy() - ) - cryptocurrency_helpers.show_quick_performance( - self.current_df, - self.symbol, - self.current_currency, - ns_parser.source, - ns_parser.exchange, - self.current_interval, - ) - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - "load", - self.current_df.copy(), - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) + self.queue.insert(0, an_input) diff --git a/openbb_terminal/openbb_terminal/controllers/base_platform_controller.py b/openbb_terminal/openbb_terminal/controllers/base_platform_controller.py new file mode 100644 index 00000000000..d75edbc447a --- /dev/null +++ b/openbb_terminal/openbb_terminal/controllers/base_platform_controller.py @@ -0,0 +1,290 @@ +"""Platform Equity Controller.""" + +import os +from functools import partial, update_wrapper +from types import MethodType +from typing import Dict, List, Optional + +import pandas as pd +from openbb import obb +from openbb_charting.core.openbb_figure import OpenBBFigure + +from openbb_terminal.argparse_translator.argparse_class_processor import ( + ArgparseClassProcessor, +) +from openbb_terminal.argparse_translator.obbject_registry import Registry +from openbb_terminal.config.completer import NestedCompleter +from openbb_terminal.config.menu_text import MenuText +from openbb_terminal.controllers.base_controller import BaseController +from openbb_terminal.controllers.utils import export_data, print_rich_table +from openbb_terminal.session import Session + + +class DummyTranslation: + """Dummy Translation for testing.""" + + def __init__(self): + """Construct a Dummy Translation Class.""" + self.paths = {} + self.translators = {} + + +class PlatformController(BaseController): + """Platform Controller Base class.""" + + CHOICES_GENERATION = True + + def __init__( + self, + name: str, + parent_path: List[str], + platform_target: Optional[type] = None, + queue: Optional[List[str]] = None, + translators: Optional[Dict] = None, + ): + """Construct a Platform based Controller.""" + self.PATH = f"/{'/'.join(parent_path)}/{name}/" if parent_path else f"/{name}/" + super().__init__(queue) + self._name = name + + if not (platform_target or translators): + raise ValueError("Either platform_target or translators must be provided.") + + self._translated_target = ( + ArgparseClassProcessor( + target_class=platform_target, reference=obb.reference["paths"] # type: ignore + ) + if platform_target + else DummyTranslation() + ) + self.translators = ( + translators + if translators is not None + else getattr(self._translated_target, "translators", {}) + ) + self.paths = getattr(self._translated_target, "paths", {}) + + if self.translators: + self._link_obbject_to_data_processing_commands() + self._generate_commands() + self._generate_sub_controllers() + + if Session().prompt_session and Session().settings.USE_PROMPT_TOOLKIT: + choices: dict = self.choices_default + self.completer = NestedCompleter.from_nested_dict(choices) + + def _link_obbject_to_data_processing_commands(self): + """Link data processing commands to OBBject registry.""" + for _, trl in self.translators.items(): + for action in trl._parser._actions: # pylint: disable=protected-access + if action.dest == "data": + action.choices = range(len(Registry.obbjects)) + action.type = int + action.nargs = None + + def _intersect_data_processing_commands(self, ns_parser): + """Intersect data processing commands and change the obbject id into an actual obbject.""" + if hasattr(ns_parser, "data") and ns_parser.data in range( + len(Registry.obbjects) + ): + obbject = Registry.get(ns_parser.data) + setattr(ns_parser, "data", obbject.results) + + return ns_parser + + def _generate_sub_controllers(self): + """Handle paths.""" + for path, value in self.paths.items(): + if value == "path": + continue + + sub_menu_translators = {} + choices_commands = [] + + for translator_name, translator in self.translators.items(): + if f"{self._name}_{path}" in translator_name: + new_name = translator_name.replace(f"{self._name}_{path}_", "") + sub_menu_translators[new_name] = translator + choices_commands.append(new_name) + + if translator_name in self.CHOICES_COMMANDS: + self.CHOICES_COMMANDS.remove(translator_name) + + # Create the sub controller as a new class + class_name = f"{self._name.capitalize()}{path.capitalize()}Controller" + SubController = type( + class_name, + (PlatformController,), + { + "CHOICES_GENERATION": True, + # "CHOICES_MENUS": [], + "CHOICES_COMMANDS": choices_commands, + }, + ) + + self._generate_controller_call( + controller=SubController, + name=path, + parent_path=self.path, + translators=sub_menu_translators, + ) + + def _generate_commands(self): + """Generate commands.""" + for name, translator in self.translators.items(): + # Prepare the translator name to create a command call in the controller + new_name = name.replace(f"{self._name}_", "") + + self._generate_command_call(name=new_name, translator=translator) + + def _generate_command_call(self, name, translator): + """Generate command call.""" + + def method(self, other_args: List[str], translator=translator): + """Call the translator.""" + parser = translator.parser + + if ns_parser := self.parse_known_args_and_warn( + parser=parser, + other_args=other_args, + export_allowed="raw_data_and_figures", + ): + try: + ns_parser = self._intersect_data_processing_commands(ns_parser) + + obbject = translator.execute_func(parsed_args=ns_parser) + df: pd.DataFrame = None + fig: OpenBBFigure = None + + if obbject: + Registry.register(obbject) + + if hasattr(ns_parser, "chart") and ns_parser.chart: + obbject.show() + fig = obbject.chart.fig + if hasattr(obbject, "to_dataframe"): + df = obbject.to_dataframe() + elif isinstance(obbject, dict): + df = pd.DataFrame.from_dict(obbject, orient="index") + else: + df = None + + elif hasattr(obbject, "to_dataframe"): + df = obbject.to_dataframe() + print_rich_table(df, show_index=True) + + elif isinstance(obbject, dict): + df = pd.DataFrame.from_dict(obbject, orient="index") + print_rich_table(df, show_index=True) + + elif obbject: + Session().console.print(obbject) + + if hasattr(ns_parser, "export") and ns_parser.export: + sheet_name = getattr(ns_parser, "sheet_name", None) + export_data( + export_type=ns_parser.export, + dir_path=os.path.dirname(os.path.abspath(__file__)), + func_name=translator.func.__name__, + df=df, + sheet_name=sheet_name, + figure=fig, + ) + + except Exception as e: + Session().console.print(f"[red]{e}[/]\n") + return + + # Bind the method to the class + bound_method = MethodType(method, self) + + # Update the wrapper and set the attribute + bound_method = update_wrapper( # type: ignore + partial(bound_method, translator=translator), method + ) + setattr(self, f"call_{name}", bound_method) + + def _generate_controller_call(self, controller, name, parent_path, translators): + """Generate controller call.""" + + def method(self, _, controller, name, parent_path, translators): + """Call the controller.""" + self.queue = self.load_class( + class_ins=controller, + name=name, + parent_path=parent_path, + translators=translators, + queue=self.queue, + ) + + # Bind the method to the class + bound_method = MethodType(method, self) + + # Update the wrapper and set the attribute + bound_method = update_wrapper( # type: ignore + partial( + bound_method, + name=name, + parent_path=parent_path, + translators=translators, + controller=controller, + ), + method, + ) + setattr(self, f"call_{name}", bound_method) + + def _get_command_description(self, command: str) -> str: + """Get command description.""" + command_description = ( + obb.reference["paths"] # type: ignore + .get(f"{self.PATH}{command}", {}) + .get("description", "") + ) + + if not command_description: + trl = self.translators.get( + f"{self._name}_{command}" + ) or self.translators.get(command) + if trl and hasattr(trl, "parser"): + command_description = trl.parser.description + + return command_description.split(".")[0].lower() + + def _get_menu_description(self, menu: str) -> str: + """Get menu description.""" + menu_description = ( + obb.reference["routers"] # type: ignore + .get(f"{self.PATH}{menu}", {}) + .get("description", "") + ) or "" + + return menu_description.split(".")[0].lower() + + def print_help(self): + """Print help.""" + mt = MenuText(self.PATH) + + if self.CHOICES_MENUS: + for menu in self.CHOICES_MENUS: + menu_description = self._get_menu_description(menu) + mt.add_menu(key_menu=menu, menu_description=menu_description) + + if self.CHOICES_COMMANDS: + mt.add_raw("\n") + for command in self.CHOICES_COMMANDS: + command_description = self._get_command_description(command) + mt.add_cmd( + key_command=command.replace(f"{self._name}_", ""), + command_description=command_description, + ) + + Session().console.print(text=mt.menu_text, menu=self._name) + + settings = Session().settings + dev_mode = settings.DEBUG_MODE or settings.TEST_MODE + if mt.warnings and dev_mode: + Session().console.print("") + for w in mt.warnings: + w_str = str(w).replace("{", "").replace("}", "").replace("'", "") + Session().console.print(f"[yellow]{w_str}[/yellow]") + Session().console.print("") diff --git a/openbb_terminal/core/completer/choices.py b/openbb_terminal/openbb_terminal/controllers/choices.py similarity index 82% rename from openbb_terminal/core/completer/choices.py rename to openbb_terminal/openbb_terminal/controllers/choices.py index 5710b5365ec..ba3def5a3c2 100644 --- a/openbb_terminal/core/completer/choices.py +++ b/openbb_terminal/openbb_terminal/controllers/choices.py @@ -1,24 +1,32 @@ +"""This module contains functions to build the choice map for the controllers.""" + from argparse import SUPPRESS, ArgumentParser from contextlib import contextmanager from inspect import isfunction, unwrap from types import MethodType -from typing import Callable, List +from typing import Callable, List, Literal from unittest.mock import patch -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.helper_funcs import check_file_type_saved, check_positive -from openbb_terminal.rich_config import get_ordered_list_sources +from openbb_terminal.controllers.utils import ( + check_file_type_saved, + check_positive, +) +from openbb_terminal.session import Session def __mock_parse_known_args_and_warn( - controller, + controller, # pylint: disable=unused-argument parser: ArgumentParser, other_args: List[str], - export_allowed: int = 0, + export_allowed: Literal[ + "no_export", "raw_data_only", "figures_only", "raw_data_and_figures" + ] = "no_export", raw: bool = False, limit: int = 0, ) -> None: - """Add the arguments that would have normally added by : + """Add arguments. + + Add the arguments that would have normally added by : - openbb_terminal.parent_classes.BaseController.parse_known_args_and_warn Parameters @@ -27,27 +35,27 @@ def __mock_parse_known_args_and_warn( Parser with predefined arguments other_args: List[str] list of arguments to parse - export_allowed: int - Choose from 0, 1, - 2 and EXPORT_BOTH_RAW_DATA_AND_FIGURES + export_allowed: Literal["no_export", "raw_data_only", "figures_only", "raw_data_and_figures"] + Export options raw: bool Add the --raw flag limit: int Add a --limit flag with this number default """ - _ = other_args + parser.add_argument( "-h", "--help", action="store_true", help="show this help message" ) - if export_allowed > 0: + + if export_allowed != "no_export": choices_export = [] help_export = "Does not export!" - if export_allowed == 1: + if export_allowed == "raw_data_only": choices_export = ["csv", "json", "xlsx"] help_export = "Export raw data into csv, json, xlsx" - elif export_allowed == 2: + elif export_allowed == "figures_only": choices_export = ["png", "jpg", "pdf", "svg"] help_export = "Export figure into png, jpg, pdf, svg " else: @@ -80,21 +88,12 @@ def __mock_parse_known_args_and_warn( help="Number of entries to show in data.", type=check_positive, ) - sources = get_ordered_list_sources(f"{controller.PATH}{parser.prog}") - # Allow to change source if there is more than one - if len(sources) > 1: - parser.add_argument( - "--source", - action="store", - dest="source", - choices=sources, - default=sources[0], # the first source from the list is the default - help="Data source to select from", - ) def __mock_parse_simple_args(parser: ArgumentParser, other_args: List[str]) -> None: - """Add the arguments that would have normally added by: + """Add arguments. + + Add the arguments that would have normally added by: - openbb_terminal.parent_classes.BaseController.parse_simple_args Parameters @@ -124,7 +123,6 @@ def __get_command_func(controller, command: str): ------- Callable: Command function. """ - if command not in controller.CHOICES_COMMANDS: raise AttributeError( f"The following command is not inside `CHOICES_COMMANDS` : '{command}'" @@ -141,7 +139,9 @@ def __get_command_func(controller, command: str): def contains_functions_to_patch(command_func: Callable) -> bool: - """Check if a `command_func` actually contains the functions we want to mock, i.e.: + """Check command function. + + Check if a `command_func` actually contains the functions we want to mock, i.e.: - parse_simple_args - parse_known_args_and_warn @@ -154,7 +154,6 @@ def contains_functions_to_patch(command_func: Callable) -> bool: ------- bool: Whether or not `command_func` contains the mocked functions. """ - co_names = command_func.__code__.co_names return bool( @@ -164,7 +163,9 @@ def contains_functions_to_patch(command_func: Callable) -> bool: @contextmanager def __patch_controller_functions(controller): - """Patch the following function from a BaseController instance: + """Patch controller functions. + + Patch the following function from a BaseController instance: - parse_simple_args - parse_known_args_and_warn @@ -180,14 +181,13 @@ def __patch_controller_functions(controller): ------- List[Callable]: List of mocked functions. """ - bound_mock_parse_known_args_and_warn = MethodType( __mock_parse_known_args_and_warn, controller, ) rich = patch( - target="openbb_terminal.rich_config.ConsoleAndPanel.print", + target="openbb_terminal.config.console.Console.print", return_value=None, ) @@ -206,7 +206,7 @@ def __patch_controller_functions(controller): ), ] - if not get_current_system().DEBUG_MODE: + if not Session().settings.DEBUG_MODE: rich.start() patched_function_list = [] for patcher in patcher_list: @@ -214,7 +214,7 @@ def __patch_controller_functions(controller): yield patched_function_list - if not get_current_system().DEBUG_MODE: + if not Session().settings.DEBUG_MODE: rich.stop() for patcher in patcher_list: patcher.stop() @@ -242,7 +242,6 @@ def _get_argument_parser( ------- ArgumentParser: ArgumentParser instance from the command function. """ - command_func: Callable = __get_command_func(controller=controller, command=command) if not contains_functions_to_patch(command_func=command_func): @@ -259,8 +258,10 @@ def _get_argument_parser( for patched_function in patched_function_list: call_count += patched_function.call_count if patched_function.call_count == 1: - args = patched_function.call_args.args - argument_parser = args[0] + args, kwargs = patched_function.call_args + argument_parser = ( + kwargs["parser"] if kwargs.get("parser", None) else args[0] + ) if call_count != 1: raise AssertionError( @@ -273,6 +274,7 @@ def _get_argument_parser( def _build_command_choice_map(argument_parser: ArgumentParser) -> dict: + """Build the choice map for a command.""" choice_map: dict = {} for action in argument_parser._actions: # pylint: disable=protected-access if action.help == SUPPRESS: @@ -298,11 +300,9 @@ def _build_command_choice_map(argument_parser: ArgumentParser) -> dict: def build_controller_choice_map(controller) -> dict: + """Build the choice map for a controller.""" command_list = controller.CHOICES_COMMANDS controller_choice_map: dict = {c: {} for c in controller.controller_choices} - controller_choice_map["support"] = controller.SUPPORT_CHOICES - controller_choice_map["about"] = controller.ABOUT_CHOICES - controller_choice_map["hold"] = controller.HELP_CHOICES for command in command_list: try: @@ -314,7 +314,7 @@ def build_controller_choice_map(controller) -> dict: argument_parser=argument_parser ) except Exception as exception: - if get_current_system().DEBUG_MODE: + if Session().settings.DEBUG_MODE: raise Exception( f"On command : `{command}`.\n{str(exception)}" ) from exception diff --git a/openbb_terminal/openbb_terminal/controllers/feature_flags_controller.py b/openbb_terminal/openbb_terminal/controllers/feature_flags_controller.py new file mode 100644 index 00000000000..86a0e3c7e37 --- /dev/null +++ b/openbb_terminal/openbb_terminal/controllers/feature_flags_controller.py @@ -0,0 +1,293 @@ +"""Feature Flags Controller Module.""" + +import argparse +from typing import List, Optional + +from openbb_terminal.config.completer import NestedCompleter +from openbb_terminal.config.constants import AVAILABLE_FLAIRS +from openbb_terminal.config.menu_text import MenuText + +# pylint: disable=too-many-lines,no-member,too-many-public-methods,C0302 +# pylint:disable=import-outside-toplevel +from openbb_terminal.controllers.base_controller import BaseController +from openbb_terminal.controllers.utils import all_timezones, is_timezone_valid +from openbb_terminal.session import Session + + +class FeatureFlagsController(BaseController): + """Feature Flags Controller class.""" + + CHOICES_COMMANDS: List[str] = [ + "retryload", + "tab", + "interactive", + "cls", + "watermark", + "promptkit", + "thoughts", + "reporthtml", + "exithelp", + "rcontext", + "richpanel", + "tbhint", + "overwrite", + "version", + "console_style", + "flair", + "timezone", + "language", + "n_rows", + "n_cols", + ] + PATH = "/settings/" + CHOICES_GENERATION = True + + def __init__(self, queue: Optional[List[str]] = None): + """Initialize the Constructor.""" + super().__init__(queue) + + if Session().prompt_session and Session().settings.USE_PROMPT_TOOLKIT: + choices: dict = self.choices_default + self.completer = NestedCompleter.from_nested_dict(choices) + + def print_help(self): + """Print help.""" + settings = Session().settings + + mt = MenuText("settings/") + mt.add_info("_info_") + mt.add_raw("\n") + mt.add_setting("interactive", settings.USE_INTERACTIVE_DF) + mt.add_setting("cls", settings.USE_CLEAR_AFTER_CMD) + mt.add_setting("promptkit", settings.USE_PROMPT_TOOLKIT) + mt.add_setting("exithelp", settings.ENABLE_EXIT_AUTO_HELP) + mt.add_setting("rcontext", settings.REMEMBER_CONTEXTS) + mt.add_setting("richpanel", settings.ENABLE_RICH_PANEL) + mt.add_setting("tbhint", settings.TOOLBAR_HINT) + mt.add_setting("overwrite", settings.FILE_OVERWRITE) + mt.add_setting("version", settings.SHOW_VERSION) + mt.add_raw("\n") + mt.add_info("_settings_") + mt.add_raw("\n") + mt.add_cmd("console_style") + mt.add_cmd("flair") + mt.add_cmd("timezone") + mt.add_cmd("language") + mt.add_cmd("n_rows") + mt.add_cmd("n_cols") + + Session().console.print(text=mt.menu_text, menu="Feature Flags") + + def call_overwrite(self, _): + """Process overwrite command.""" + Session().settings.set_item( + "FILE_OVERWRITE", not Session().settings.FILE_OVERWRITE + ) + + def call_version(self, _): + """Process version command.""" + Session().settings.SHOW_VERSION = not Session().settings.SHOW_VERSION + + def call_interactive(self, _): + """Process interactive command.""" + Session().settings.set_item( + "USE_INTERACTIVE_DF", not Session().settings.USE_INTERACTIVE_DF + ) + + def call_cls(self, _): + """Process cls command.""" + Session().settings.set_item( + "USE_CLEAR_AFTER_CMD", not Session().settings.USE_CLEAR_AFTER_CMD + ) + + def call_promptkit(self, _): + """Process promptkit command.""" + Session().settings.set_item( + "USE_PROMPT_TOOLKIT", not Session().settings.USE_PROMPT_TOOLKIT + ) + + def call_exithelp(self, _): + """Process exithelp command.""" + Session().settings.set_item( + "ENABLE_EXIT_AUTO_HELP", not Session().settings.ENABLE_EXIT_AUTO_HELP + ) + + def call_rcontext(self, _): + """Process rcontext command.""" + Session().settings.set_item( + "REMEMBER_CONTEXTS", not Session().settings.REMEMBER_CONTEXTS + ) + + def call_dt(self, _): + """Process dt command.""" + Session().settings.set_item("USE_DATETIME", not Session().settings.USE_DATETIME) + + def call_richpanel(self, _): + """Process richpanel command.""" + Session().settings.set_item( + "ENABLE_RICH_PANEL", not Session().settings.ENABLE_RICH_PANEL + ) + + def call_tbhint(self, _): + """Process tbhint command.""" + if Session().settings.TOOLBAR_HINT: + Session().console.print("Will take effect when running terminal next.") + Session().settings.set_item("TOOLBAR_HINT", not Session().settings.TOOLBAR_HINT) + + def call_console_style(self, other_args: List[str]) -> None: + """Process cosole_style command.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="console_style", + description="Change your custom console style.", + add_help=False, + ) + parser.add_argument( + "-s", + "--style", + dest="style", + action="store", + required=False, + choices=Session().style.available_styles, + ) + ns_parser = self.parse_simple_args(parser, other_args) + + if ns_parser and ns_parser.style: + Session().style.apply(ns_parser.style) + Session().settings.set_item("RICH_STYLE", ns_parser.style) + elif not other_args: + Session().console.print( + f"Current console style: {Session().settings.RICH_STYLE}" + ) + + def call_flair(self, other_args: List[str]) -> None: + """Process flair command.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="flair", + description="Change your custom flair.", + add_help=False, + ) + parser.add_argument( + "-f", + "--flair", + dest="flair", + action="store", + required=False, + choices=list(AVAILABLE_FLAIRS.keys()), + ) + ns_parser = self.parse_simple_args(parser, other_args) + + if ns_parser and ns_parser.flair: + Session().settings.set_item("FLAIR", ns_parser.flair) + elif not other_args: + Session().console.print(f"Current flair: {Session().settings.FLAIR}") + + def call_timezone(self, other_args: List[str]) -> None: + """Process timezone command.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="timezone", + description="Change your custom timezone.", + add_help=False, + ) + parser.add_argument( + "-t", + "--timezone", + dest="timezone", + action="store", + required=False, + type=str, + ) + ns_parser = self.parse_simple_args(parser, other_args) + + if ns_parser and ns_parser.timezone: + if is_timezone_valid(ns_parser.timezone): + Session().settings.set_item("TIMEZONE", ns_parser.timezone) + else: + Session().console.print( + "Invalid timezone. Please enter a valid timezone." + ) + Session().console.print( + f"Available timezones are: {', '.join(all_timezones)}" + ) + elif not other_args: + Session().console.print(f"Current timezone: {Session().settings.TIMEZONE}") + + def call_language(self, other_args: List[str]) -> None: + """Process language command.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="language", + description="Change your custom language.", + add_help=False, + ) + parser.add_argument( + "-l", + "--language", + dest="language", + action="store", + required=False, + type=str, + ) + ns_parser = self.parse_simple_args(parser, other_args) + + if ns_parser and ns_parser.language: + Session().settings.set_item("USE_LANGUAGE", ns_parser.language) + + elif not other_args: + Session().console.print( + f"Current language: {Session().settings.USE_LANGUAGE}" + ) + + def call_n_rows(self, other_args: List[str]) -> None: + """Process n_rows command.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="n_rows", + description="Number of rows to show (when not using interactive tables).", + add_help=False, + ) + parser.add_argument( + "-r", + "--rows", + dest="rows", + action="store", + required=False, + type=int, + ) + ns_parser = self.parse_simple_args(parser, other_args) + + if ns_parser and ns_parser.rows: + Session().settings.set_item("ALLOWED_NUMBER_OF_ROWS", ns_parser.rows) + + elif not other_args: + Session().console.print( + f"Current number of rows: {Session().settings.ALLOWED_NUMBER_OF_ROWS}" + ) + + def call_n_cols(self, other_args: List[str]) -> None: + """Process n_cols command.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="n_cols", + description="Number of columns to show (when not using interactive tables).", + add_help=False, + ) + parser.add_argument( + "-c", + "--columns", + dest="columns", + action="store", + required=False, + type=int, + ) + ns_parser = self.parse_simple_args(parser, other_args) + + if ns_parser and ns_parser.columns: + Session().settings.set_item("ALLOWED_NUMBER_OF_COLUMNS", ns_parser.columns) + + elif not other_args: + Session().console.print( + f"Current number of columns: {Session().settings.ALLOWED_NUMBER_OF_COLUMNS}" + ) diff --git a/openbb_terminal/openbb_terminal/controllers/hub_service.py b/openbb_terminal/openbb_terminal/controllers/hub_service.py new file mode 100644 index 00000000000..c5270c3cb36 --- /dev/null +++ b/openbb_terminal/openbb_terminal/controllers/hub_service.py @@ -0,0 +1,105 @@ +"""Routines handler module.""" + +from typing import Optional + +import requests + +from openbb_terminal.config.constants import ( + CONNECTION_ERROR_MSG, + CONNECTION_TIMEOUT_MSG, + TIMEOUT, +) +from openbb_terminal.session import Session + +# created dictionaries for personal and default routines with the structure +# {"file_name" :["script","personal/default"]} +# and stored dictionaries in list +# created new directory structure to account for personal and default routines + + +# pylint: disable=too-many-arguments +def upload_routine( + auth_header: str, + name: str = "", + description: str = "", + routine: str = "", + override: bool = False, + tags: str = "", + public: bool = False, + timeout: int = TIMEOUT, +) -> Optional[requests.Response]: + """Send a routine to the server. + + Parameters + ---------- + auth_header : str + The authorization header, e.g. "Bearer ". + name : str + The name of the routine. + routine : str + The routine. + override : bool + Whether to override the routine if it already exists. + tags : str + The tags of the routine. + public : bool + Whether to make the routine public or not. + timeout : int + The timeout, by default TIMEOUT + + Returns + ------- + Optional[requests.Response] + The response from the post request. + """ + data = { + "name": name, + "description": description, + "script": routine, + "override": override, + "tags": tags, + "version": Session().settings.VERSION, + "public": public, + } + _console = Session().console + try: + response = requests.post( + headers={"Authorization": auth_header}, + url=Session().settings.BASE_URL + "/terminal/script", + json=data, + timeout=timeout, + ) + if response.status_code == 200: + username = getattr(Session().user.profile.hub_session, "username", None) + if not username: + _console.print("[red]No username found.[/red]") + _console.print("[red]Failed to upload your routine.[/red]") + return None + _console.print("[green]Successfully uploaded your routine.[/]") + + hub_url = Session().settings.HUB_URL + + if public: + _console.print( + f"\n[yellow]Share or edit it at {hub_url}/u/{username}/routine/{name.replace(' ', '-')}[/]" + ) + else: + _console.print(f"\n[yellow]Go to {hub_url} to edit this script,[/]") + _console.print( + f"[yellow]or even make it public so you can access it at " + f"{hub_url}/u/{username}/routine/{name.replace(' ', '-')}[/]" + ) + elif response.status_code != 409: # 409: routine already exists + _console.print( + "[red]" + response.json().get("detail", "Unknown error.") + "[/red]" + ) + return response + except requests.exceptions.ConnectionError: + _console.print(f"\n{CONNECTION_ERROR_MSG}") + return None + except requests.exceptions.Timeout: + _console.print(f"\n{CONNECTION_TIMEOUT_MSG}") + return None + except Exception: + _console.print("[red]Failed to upload your routine.[/red]") + return None diff --git a/openbb_terminal/openbb_terminal/controllers/platform_controller_factory.py b/openbb_terminal/openbb_terminal/controllers/platform_controller_factory.py new file mode 100644 index 00000000000..d1b613c7806 --- /dev/null +++ b/openbb_terminal/openbb_terminal/controllers/platform_controller_factory.py @@ -0,0 +1,58 @@ +"""Platform controller factory to create a platform controller.""" + +from typing import Dict, List, Union + +from openbb_terminal.argparse_translator.argparse_class_processor import ( + ArgparseClassProcessor, +) +from openbb_terminal.controllers.base_platform_controller import PlatformController + + +class PlatformControllerFactory: + """Factory to create a platform controller.""" + + def __init__(self, platform_router: type, **kwargs): + """Create the controller name.""" + self.platform_router = platform_router + self._translated_target = ArgparseClassProcessor( + target_class=self.platform_router, reference=kwargs.get("reference", {}) + ) + self.router_name = ( + str(type(self.platform_router)) + .rsplit(".", maxsplit=1)[-1] + .replace("'>", "") + .replace("ROUTER_", "") + .lower() + ) + self.controller_name = f"{self.router_name.capitalize()}Controller" + + def create(self) -> type: + """Create the platform controller.""" + ClassName = self.controller_name + Parents = (PlatformController,) + Attributes: Dict[str, Union[bool, List[str]]] = {"CHOICES_GENERATION": True} + + # Menu and Command choices generation + choices_menus: List[str] = [] + choices_commands: List[str] = [] + translators = self._translated_target.translators + paths = self._translated_target.paths + # menus + for key, value in paths.items(): + if value == "path": + continue + choices_menus.append(key) + # commands + for name, _ in translators.items(): + if any(f"{self.router_name}_{path}" in name for path in paths): + continue + new_name = name.replace(f"{self.router_name}_", "") + choices_commands.append(new_name) + + Attributes["CHOICES_MENUS"] = choices_menus + Attributes["CHOICES_COMMANDS"] = choices_commands + + # Use type to create the class + DynamicClass = type(ClassName, Parents, Attributes) + + return DynamicClass diff --git a/openbb_terminal/routine_functions.py b/openbb_terminal/openbb_terminal/controllers/script_parser.py similarity index 98% rename from openbb_terminal/routine_functions.py rename to openbb_terminal/openbb_terminal/controllers/script_parser.py index 5b2098b0612..c182e9f43c3 100644 --- a/openbb_terminal/routine_functions.py +++ b/openbb_terminal/openbb_terminal/controllers/script_parser.py @@ -1,10 +1,12 @@ +"""Routine functions for OpenBB Platform CLI.""" + import re from datetime import datetime, timedelta from typing import Dict, List, Match, Optional, Tuple, Union from dateutil.relativedelta import relativedelta -from openbb_terminal.rich_config import console +from openbb_terminal.session import Session # pylint: disable=too-many-statements,eval-used,consider-iterating-dictionary # pylint: disable=too-many-branches,too-many-return-statements @@ -37,7 +39,7 @@ WEEKDAY_VALUE = { def is_reset(command: str) -> bool: - """Test whether a command is a reset command + """Test whether a command is a reset command. Parameters ---------- @@ -59,7 +61,7 @@ def is_reset(command: str) -> bool: def match_and_return_openbb_keyword_date(keyword: str) -> str: # noqa: PLR0911 - """Return OpenBB keyword into date + """Return OpenBB keyword into date. Parameters ---------- @@ -148,8 +150,7 @@ def parse_openbb_script( # noqa: PLR0911,PLR0912 raw_lines: List[str], script_inputs: Optional[List[str]] = None, ) -> Tuple[str, str]: - """ - Parse .openbb script + """Parse .openbb script. Parameters ---------- @@ -163,7 +164,7 @@ def parse_openbb_script( # noqa: PLR0911,PLR0912 str Error that occurred - if empty means no error str - Processed string from .openbb script that can be run by the OpenBB Terminal + Processed string from .openbb script that can be run by the OpenBB Platform CLI """ ROUTINE_VARS: Dict[str, Union[str, List[str]]] = dict() if script_inputs: @@ -194,7 +195,7 @@ def parse_openbb_script( # noqa: PLR0911,PLR0912 # Just throw a warning when user uses wrong convention numdollars = len(re.findall(r"\$", line)) if numdollars > 1: - console.print( + Session().console.print( f"The variable {VAR_NAME} should not be declared as " f"{'$' * numdollars}{VAR_NAME}. Instead it will be " f"converted into ${VAR_NAME}." @@ -445,7 +446,7 @@ def parse_openbb_script( # noqa: PLR0911,PLR0912 final_lines.append(foreach_line_loop.strip()) if not varused_inside: - console.print( + Session().console.print( f"The variable {varname} was used in foreach header " "but it wasn't used inside the loop." ) @@ -462,7 +463,7 @@ def parse_openbb_script( # noqa: PLR0911,PLR0912 final_lines.append(line) # If the list is non null, then we want to convert this into a parsed string that is - # recognized by the OpenBB Terminal + # recognized by the OpenBB Platform CLI if final_lines: parsed_script = f"{'/'.join([line.rstrip() for line in final_lines])}".replace( "//", "/home/" diff --git a/openbb_terminal/terminal_controller.py b/openbb_terminal/openbb_terminal/controllers/terminal_controller.py similarity index 55% rename from openbb_terminal/terminal_controller.py rename to openbb_terminal/openbb_terminal/controllers/terminal_controller.py index 698057e18da..1cea3c31a1a 100644 --- a/openbb_terminal/terminal_controller.py +++ b/openbb_terminal/openbb_terminal/controllers/terminal_controller.py @@ -1,11 +1,9 @@ #!/usr/bin/env python """Main Terminal Module.""" -__docformat__ = "numpy" import argparse import contextlib import difflib -import json import logging import os import re @@ -13,59 +11,62 @@ import sys import time import webbrowser from datetime import datetime +from functools import partial, update_wrapper from pathlib import Path +from types import MethodType from typing import Any, Dict, List, Optional import certifi import pandas as pd import requests -from prompt_toolkit import PromptSession +from openbb import obb from prompt_toolkit.formatted_text import HTML from prompt_toolkit.styles import Style +from pydantic import BaseModel -import openbb_terminal.config_terminal as cfg -from openbb_terminal.account.show_prompt import get_show_prompt, set_show_prompt -from openbb_terminal.common import biztoc_model, biztoc_view, feedparser_view -from openbb_terminal.core.config.paths import ( +from openbb_terminal.argparse_translator.obbject_registry import Registry +from openbb_terminal.config import constants +from openbb_terminal.config.completer import NestedCompleter +from openbb_terminal.config.constants import ( + ASSETS_DIRECTORY, + ENV_FILE_SETTINGS, HOME_DIRECTORY, - MISCELLANEOUS_DIRECTORY, REPOSITORY_DIRECTORY, - SETTINGS_ENV_FILE, ) -from openbb_terminal.core.log.generation.custom_logger import log_terminal -from openbb_terminal.core.session import constants, session_controller -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.core.session.current_user import get_current_user, set_preference -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - get_flair, - parse_and_split_input, +from openbb_terminal.config.menu_text import MenuText +from openbb_terminal.controllers.base_controller import BaseController +from openbb_terminal.controllers.platform_controller_factory import ( + PlatformControllerFactory, ) -from openbb_terminal.menu import is_papermill, session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.reports.reports_model import ipykernel_launcher -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.routine_functions import is_reset, parse_openbb_script -from openbb_terminal.terminal_helper import ( +from openbb_terminal.controllers.script_parser import is_reset, parse_openbb_script +from openbb_terminal.controllers.utils import ( bootup, - check_for_updates, first_time_user, - is_auth_enabled, + get_flair_and_username, is_installer, + parse_and_split_input, print_goodbye, + print_rich_table, reset, suppress_stdout, welcome_message, ) +from openbb_terminal.session import Session + +PLATFORM_ROUTERS = { + d: "menu" if not isinstance(getattr(obb, d), BaseModel) else "command" + for d in dir(obb) + if "_" not in d +} +NON_DATA_ROUTERS = ["coverage", "account", "reference", "system", "user"] +DATA_PROCESSING_ROUTERS = ["technical", "quantitative", "econometrics"] # pylint: disable=too-many-public-methods,import-outside-toplevel, too-many-function-args # pylint: disable=too-many-branches,no-member,C0302,too-many-return-statements, inconsistent-return-statements logger = logging.getLogger(__name__) -env_file = str(SETTINGS_ENV_FILE) +env_file = str(ENV_FILE_SETTINGS) if is_installer(): # Necessary for installer so that it can locate the correct certificates for @@ -78,43 +79,18 @@ if is_installer(): class TerminalController(BaseController): """Terminal Controller class.""" - CHOICES_COMMANDS = [ - "keys", - "settings", - "survey", - "featflags", - "exe", - "guess", - "news", - "intro", - "record", - ] + CHOICES_COMMANDS = ["record", "stop", "exe", "results"] CHOICES_MENUS = [ - "stocks", - "economy", - "crypto", - "portfolio", - "forex", - "etf", - "dashboards", - "alternative", - "econometrics", - "sources", - "forecast", - "futures", - "fixedincome", - "funds", + "settings", ] - if is_auth_enabled(): - CHOICES_MENUS.append("account") + for router, value in PLATFORM_ROUTERS.items(): + if value == "menu": + CHOICES_MENUS.append(router) + else: + CHOICES_COMMANDS.append(router) PATH = "/" - - GUESS_TOTAL_TRIES = 0 - GUESS_NUMBER_TRIES_LEFT = 0 - GUESS_SUM_SCORE = 0.0 - GUESS_CORRECTLY = 0 CHOICES_GENERATION = False def __init__(self, jobs_cmds: Optional[List[str]] = None): @@ -135,44 +111,85 @@ class TerminalController(BaseController): self.update_success = False + self._generate_platform_commands() + self.update_runtime_choices() + def _generate_platform_commands(self): + """Generate Platform based commands/menus.""" + + def method_call_class(self, _, controller, name, parent_path, target): + self.queue = self.load_class( + controller, name, parent_path, target, self.queue + ) + + # pylint: disable=unused-argument + def method_call_command(self, _, router: str): + """Call command.""" + mdl = getattr(obb, router) + df = pd.DataFrame.from_dict(mdl.model_dump(), orient="index") + return print_rich_table(df, show_index=True) + + for router, value in PLATFORM_ROUTERS.items(): + target = getattr(obb, router) + + if value == "menu": + pcf = PlatformControllerFactory( + target, reference=obb.reference["paths"] # type: ignore + ) + DynamicController = pcf.create() + + # Bind the method to the class + bound_method = MethodType(method_call_class, self) + + # Update the wrapper and set the attribute + bound_method = update_wrapper( # type: ignore + partial( + bound_method, + controller=DynamicController, + name=router, + target=target, + parent_path=self.path, + ), + method_call_class, + ) + else: + bound_method = MethodType(method_call_command, self) + bound_method = update_wrapper( # type: ignore + partial(bound_method, router=router), + method_call_command, + ) + + setattr(self, f"call_{router}", bound_method) + def update_runtime_choices(self): """Update runtime choices.""" - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: + routines_directory = Path( + Session().user.preferences.export_directory, "routines" + ) + + if Session().prompt_session and Session().settings.USE_PROMPT_TOOLKIT: # choices: dict = self.choices_default choices: dict = {c: {} for c in self.controller_choices} # type: ignore - choices["support"] = self.SUPPORT_CHOICES - choices["news"] = self.NEWS_CHOICES - choices["news"]["--source"] = {c: {} for c in ["Biztoc", "Feedparser"]} choices["hold"] = {c: None for c in ["on", "off", "-s", "--sameaxis"]} choices["hold"]["off"] = {"--title": None} - if biztoc_model.BIZTOC_TAGS: - choices["news"]["--tag"] = {c: {} for c in biztoc_model.BIZTOC_TAGS} self.ROUTINE_FILES = { - filepath.name: filepath - for filepath in get_current_user().preferences.USER_ROUTINES_DIRECTORY.rglob( + filepath.name: filepath # type: ignore + for filepath in routines_directory.rglob("*.openbb") + } + self.ROUTINE_DEFAULT_FILES = { + filepath.name: filepath # type: ignore + for filepath in Path(routines_directory / "hub" / "default").rglob( + "*.openbb" + ) + } + self.ROUTINE_PERSONAL_FILES = { + filepath.name: filepath # type: ignore + for filepath in Path(routines_directory / "hub" / "personal").rglob( "*.openbb" ) } - if get_current_user().profile.get_token(): - self.ROUTINE_DEFAULT_FILES = { - filepath.name: filepath - for filepath in Path( - get_current_user().preferences.USER_ROUTINES_DIRECTORY - / "hub" - / "default" - ).rglob("*.openbb") - } - self.ROUTINE_PERSONAL_FILES = { - filepath.name: filepath - for filepath in Path( - get_current_user().preferences.USER_ROUTINES_DIRECTORY - / "hub" - / "personal" - ).rglob("*.openbb") - } choices["exe"] = { "--file": { @@ -205,20 +222,7 @@ class TerminalController(BaseController): def print_help(self): """Print help.""" mt = MenuText("") - mt.add_info("_home_") - mt.add_cmd("intro") - mt.add_cmd("about") - mt.add_cmd("support") - mt.add_cmd("survey") - mt.add_cmd("wiki") - mt.add_cmd("news") - mt.add_raw("\n") mt.add_info("_configure_") - if is_auth_enabled(): - mt.add_menu("account") - mt.add_menu("keys") - mt.add_menu("featflags") - mt.add_menu("sources") mt.add_menu("settings") mt.add_raw("\n") mt.add_info("_scripts_") @@ -226,123 +230,69 @@ class TerminalController(BaseController): mt.add_cmd("stop") mt.add_cmd("exe") mt.add_raw("\n") - mt.add_info("_main_menu_") - mt.add_menu("stocks") - mt.add_menu("crypto") - mt.add_menu("etf") - mt.add_menu("economy") - mt.add_menu("forex") - mt.add_menu("futures") - mt.add_menu("fixedincome") - mt.add_menu("alternative") - mt.add_menu("funds") - mt.add_raw("\n") - mt.add_info("_toolkits_") - mt.add_menu("econometrics") - mt.add_menu("forecast") - mt.add_menu("portfolio") - mt.add_menu("dashboards") - console.print(text=mt.menu_text, menu="Home") - self.update_runtime_choices() - - def call_news(self, other_args: List[str]) -> None: - """Process news command.""" - parse = argparse.ArgumentParser( - add_help=False, - prog="news", - description="display news articles based on term and data sources", - ) - parse.add_argument( - "-t", - "--term", - dest="term", - default=[""], - nargs="+", - help="search for a term on the news", - ) - parse.add_argument( - "-s", - "--sources", - dest="sources", - default="bloomberg", - type=str, - help="sources from where to get news from (separated by comma)", - ) - parse.add_argument( - "--tag", - dest="tag", - default="", - type=str, - help="display news for an individual tag [Biztoc only]", - ) - parse.add_argument( - "--sourcelist", - dest="sourcelist", - action="store_true", - help="list all available sources from where to get news from [Biztoc only]", - ) - parse.add_argument( - "--taglist", - dest="taglist", - action="store_true", - help="list all trending tags [Biztoc only]", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - news_parser = self.parse_known_args_and_warn( - parse, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=25 - ) - if news_parser: - if news_parser.source == "Feedparser": - # If biztoc options passed to feedparser source, let the user know - to_return = False - if news_parser.taglist: - console.print("--taglist only available for Biztoc.\n") - to_return = True - if news_parser.sourcelist: - console.print("--sourcelist only available for Biztoc.\n") - to_return = True - if news_parser.tag: - console.print("--tag only available for Biztoc.\n") - to_return = True - - if to_return: - return - - query = " ".join(news_parser.term) - feedparser_view.display_news( - term=query, - sources=news_parser.sources, - limit=news_parser.limit, - export=news_parser.export, - sheet_name=news_parser.sheet_name, + mt.add_info("Platform CLI") + mt.add_raw(" data\n") + for router, value in PLATFORM_ROUTERS.items(): + if router in NON_DATA_ROUTERS or router in DATA_PROCESSING_ROUTERS: + continue + if value == "menu": + menu_description = ( + obb.reference["routers"] # type: ignore + .get(f"{self.PATH}{router}", {}) + .get("description") + ) or "" + mt.add_menu( + key_menu=router, + menu_description=menu_description.split(".")[0].lower(), ) - if news_parser.source == "Biztoc": - query = " ".join(news_parser.term) - if news_parser.sourcelist and news_parser.sourcelist is True: - biztoc_view.display_sources( - export=news_parser.export, - sheet_name=news_parser.sheet_name, - ) - elif news_parser.taglist and news_parser.taglist is True: - biztoc_view.display_tags( - export=news_parser.export, - sheet_name=news_parser.sheet_name, + else: + mt.add_cmd(router) + + if any(router in PLATFORM_ROUTERS for router in DATA_PROCESSING_ROUTERS): + mt.add_raw("\n data processing\n") + for router, value in PLATFORM_ROUTERS.items(): + if router not in DATA_PROCESSING_ROUTERS: + continue + if value == "menu": + menu_description = ( + obb.reference["routers"] # type: ignore + .get(f"{self.PATH}{router}", {}) + .get("description") + ) or "" + mt.add_menu( + key_menu=router, + menu_description=menu_description.split(".")[0].lower(), ) else: - biztoc_view.display_news( - term=query, - tag=news_parser.tag, - source=news_parser.sources, - limit=news_parser.limit, - export=news_parser.export, - sheet_name=news_parser.sheet_name, - ) + mt.add_cmd(router) + + mt.add_raw("\n configuration\n") + for router, value in PLATFORM_ROUTERS.items(): + if router not in NON_DATA_ROUTERS or router == "reference": + continue + if value == "menu": + menu_description = ( + obb.reference["routers"] # type: ignore + .get(f"{self.PATH}{router}", {}) + .get("description") + ) or "" + mt.add_menu( + key_menu=router, + menu_description=menu_description.split(".")[0].lower(), + ) + else: + mt.add_cmd(router) + + mt.add_raw("\n cached results (OBBjects)\n") + mt.add_cmd("results") + + Session().console.print(text=mt.menu_text, menu="Home") + self.update_runtime_choices() def parse_input(self, an_input: str) -> List: - """Overwrite the BaseController parse_input for `askobb` and 'exe' + """Overwrite the BaseController parse_input for `askobb` and 'exe'. - This will allow us to search for something like "P/E" ratio + This will allow us to search for something like "P/E" ratio. """ # Filtering out sorting parameters with forward slashes like P/E sort_filter = r"((\ -q |\ --question|\ ).*?(/))" @@ -351,248 +301,21 @@ class TerminalController(BaseController): custom_filters = [sort_filter, url] return parse_and_split_input(an_input=an_input, custom_filters=custom_filters) - def call_guess(self, other_args: List[str]) -> None: - """Process guess command.""" - import random - - current_user = get_current_user() - - if self.GUESS_NUMBER_TRIES_LEFT == 0 and self.GUESS_SUM_SCORE < 0.01: - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="guess", - description="Guess command to achieve task successfully.", - ) - parser.add_argument( - "-l", - "--limit", - type=check_positive, - help="Number of tasks to attempt.", - dest="limit", - default=1, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser_guess = self.parse_simple_args(parser, other_args) - - if self.GUESS_TOTAL_TRIES == 0: - self.GUESS_NUMBER_TRIES_LEFT = ns_parser_guess.limit - self.GUESS_SUM_SCORE = 0 - self.GUESS_TOTAL_TRIES = ns_parser_guess.limit - - try: - with open(current_user.preferences.GUESS_EASTER_EGG_FILE) as f: - # Load the file as a JSON document - json_doc = json.load(f) - - task = random.choice(list(json_doc.keys())) # nosec # noqa: S311 - solution = json_doc[task] - - start = time.time() - console.print(f"\n[yellow]{task}[/yellow]\n") - an_input = ( - session.prompt("GUESS / $ ") - if isinstance(session, PromptSession) - else "" - ) - time_dif = time.time() - start - - # When there are multiple paths to same solution - if isinstance(solution, List): - if an_input.lower() in [s.lower() for s in solution]: - self.queue = an_input.split("/") + ["home"] - console.print( - f"\n[green]You guessed correctly in {round(time_dif, 2)} seconds![green]\n" - ) - # If we are already counting successes - if self.GUESS_TOTAL_TRIES > 0: - self.GUESS_CORRECTLY += 1 - self.GUESS_SUM_SCORE += time_dif - else: - solutions_texts = "\n".join(solution) - console.print( - f"\n[red]You guessed wrong! The correct paths would have been:\n{solutions_texts}[/red]\n" - ) - - # When there is a single path to the solution - elif an_input.lower() == solution.lower(): - self.queue = an_input.split("/") + ["home"] - console.print( - f"\n[green]You guessed correctly in {round(time_dif, 2)} seconds![green]\n" - ) - # If we are already counting successes - if self.GUESS_TOTAL_TRIES > 0: - self.GUESS_CORRECTLY += 1 - self.GUESS_SUM_SCORE += time_dif - else: - console.print( - f"\n[red]You guessed wrong! The correct path would have been:\n{solution}[/red]\n" - ) - - # Compute average score and provide a result if it's the last try - if self.GUESS_TOTAL_TRIES > 0: - self.GUESS_NUMBER_TRIES_LEFT -= 1 - if self.GUESS_NUMBER_TRIES_LEFT == 0 and self.GUESS_TOTAL_TRIES > 1: - color = ( - "green" - if self.GUESS_CORRECTLY == self.GUESS_TOTAL_TRIES - else "red" - ) - console.print( - f"[{color}]OUTCOME: You got {int(self.GUESS_CORRECTLY)} out of" - f" {int(self.GUESS_TOTAL_TRIES)}.[/{color}]\n" - ) - if self.GUESS_CORRECTLY == self.GUESS_TOTAL_TRIES: - avg = self.GUESS_SUM_SCORE / self.GUESS_TOTAL_TRIES - console.print( - f"[green]Average score: {round(avg, 2)} seconds![/green]\n" - ) - self.GUESS_TOTAL_TRIES = 0 - self.GUESS_CORRECTLY = 0 - self.GUESS_SUM_SCORE = 0 - else: - self.queue += ["guess"] - - except Exception as e: - console.print( - f"[red]Failed to load guess game from file: " - f"{current_user.preferences.GUESS_EASTER_EGG_FILE}[/red]" - ) - console.print(f"[red]{e}[/red]") - - @staticmethod - def call_survey(_) -> None: - """Process survey command.""" - webbrowser.open("https://openbb.co/survey") - - def call_account(self, _): - """Process account command.""" - from openbb_terminal.account.account_controller import AccountController - - self.queue = self.load_class(AccountController, self.queue) - - def call_keys(self, _): - """Process keys command.""" - from openbb_terminal.keys_controller import KeysController - - self.queue = self.load_class(KeysController, self.queue) - def call_settings(self, _): - """Process settings command.""" - from openbb_terminal.settings_controller import SettingsController - - self.queue = self.load_class(SettingsController, self.queue) - - def call_featflags(self, _): """Process feature flags command.""" - from openbb_terminal.featflags_controller import FeatureFlagsController + from openbb_terminal.controllers.feature_flags_controller import ( + FeatureFlagsController, + ) self.queue = self.load_class(FeatureFlagsController, self.queue) - def call_stocks(self, _): - """Process stocks command.""" - from openbb_terminal.stocks.stocks_controller import StocksController - - self.queue = self.load_class(StocksController, self.queue) - - def call_crypto(self, _): - """Process crypto command.""" - from openbb_terminal.cryptocurrency.crypto_controller import CryptoController - - self.queue = self.load_class(CryptoController, self.queue) - - def call_economy(self, _): - """Process economy command.""" - from openbb_terminal.economy.economy_controller import EconomyController - - self.queue = self.load_class(EconomyController, self.queue) - - def call_etf(self, _): - """Process etf command.""" - from openbb_terminal.etf.etf_controller import ETFController - - self.queue = self.load_class(ETFController, self.queue) - - def call_forex(self, _): - """Process forex command.""" - from openbb_terminal.forex.forex_controller import ForexController - - self.queue = self.load_class(ForexController, self.queue) - - def call_dashboards(self, _): - """Process dashboards command.""" - from openbb_terminal.dashboards.dashboards_controller import ( - DashboardsController, - ) - - self.queue = self.load_class(DashboardsController, self.queue) - - def call_alternative(self, _): - """Process alternative command.""" - from openbb_terminal.alternative.alt_controller import AlternativeDataController - - self.queue = self.load_class(AlternativeDataController, self.queue) - - def call_econometrics(self, _): - """Process econometrics command.""" - from openbb_terminal.econometrics.econometrics_controller import ( - EconometricsController, - ) - - self.queue = EconometricsController(self.queue).menu() - - def call_forecast(self, _): - """Process forecast command.""" - from openbb_terminal.forecast.forecast_controller import ForecastController - - self.queue = self.load_class(ForecastController, "", pd.DataFrame(), self.queue) - - def call_portfolio(self, _): - """Process portfolio command.""" - from openbb_terminal.portfolio.portfolio_controller import PortfolioController - - self.queue = self.load_class(PortfolioController, self.queue) - - def call_sources(self, _): - """Process sources command.""" - from openbb_terminal.sources_controller import SourcesController - - self.queue = self.load_class(SourcesController, self.queue) - - def call_futures(self, _): - """Process futures command.""" - from openbb_terminal.futures.futures_controller import FuturesController - - self.queue = self.load_class(FuturesController, self.queue) - - def call_fixedincome(self, _): - """Process fixedincome command.""" - from openbb_terminal.fixedincome.fixedincome_controller import ( - FixedIncomeController, - ) - - self.queue = self.load_class(FixedIncomeController, self.queue) - - def call_funds(self, _): - """Process etf command""" - from openbb_terminal.mutual_funds.mutual_fund_controller import FundController - - self.queue = self.load_class(FundController, self.queue) - - def call_intro(self, _): - """Process intro command.""" - webbrowser.open( - "https://docs.openbb.co/terminal/usage/overview/structure-and-navigation" - ) - def call_exe(self, other_args: List[str]): """Process exe command.""" # Merge rest of string path to other_args and remove queue since it is a dir other_args += self.queue if not other_args: - console.print( + Session().console.print( "[info]Provide a path to the routine you wish to execute. For an example, please use " "`exe --example` and for documentation and to learn how create your own script " "type `about exe`.\n[/info]" @@ -646,10 +369,8 @@ class TerminalController(BaseController): ns_parser = self.parse_known_args_and_warn(parser, other_args) if ns_parser: if ns_parser.example: - routine_path = ( - MISCELLANEOUS_DIRECTORY / "routines" / "routine_example.openbb" - ) - console.print( + routine_path = ASSETS_DIRECTORY / "routines" / "routine_example.openbb" + Session().console.print( "[info]Executing an example, please type `about exe` " "to learn how to create your own script.[/info]\n" ) @@ -669,10 +390,14 @@ class TerminalController(BaseController): final_url = f"{url}?raw=true" response = requests.get(final_url, timeout=10) if response.status_code != 200: - console.print("[red]Could not find the requested script.[/red]") + Session().console.print( + "[red]Could not find the requested script.[/red]" + ) return routine_text = response.json()["script"] - file_path = Path(get_current_user().preferences.USER_ROUTINES_DIRECTORY) + file_path = Path( + Session().user.preferences.export_directory, "routines" + ) routine_path = file_path / file_name with open(routine_path, "w") as file: file.write(routine_text) @@ -717,7 +442,7 @@ class TerminalController(BaseController): # issue in parsing the routine and therefore we don't want to feed it # to the terminal if err: - console.print(err) + Session().console.print(err) return self.queue = [ @@ -742,19 +467,30 @@ class TerminalController(BaseController): # Check if the directory exists if os.path.isdir(export_path): - console.print( + Session().console.print( f"Export data to be saved in the selected folder: '{export_path}'" ) else: os.makedirs(export_path) - console.print( + Session().console.print( f"[green]Folder '{export_path}' successfully created.[/green]" ) - set_preference("USER_EXPORTS_DIRECTORY", Path(export_path)) self.queue = self.queue[1:] + def call_results(self, _): + """Process results command.""" + results = Registry().all + if results: + df = pd.DataFrame.from_dict(results, orient="index") + print_rich_table( + df, show_index=True, index_name="stack index", title="OBBject Results" + ) + else: + Session().console.print("[info]No results found.[/info]") + def handle_job_cmds(jobs_cmds: Optional[List[str]]) -> Optional[List[str]]: + """Handle job commands.""" # If the path selected does not start from the user root, # give relative location from terminal root if jobs_cmds is not None and jobs_cmds: @@ -778,23 +514,20 @@ def handle_job_cmds(jobs_cmds: Optional[List[str]]) -> Optional[List[str]]: # Check if the directory exists if os.path.isdir(export_path): - console.print( + Session().console.print( f"Export data to be saved in the selected folder: '{export_path}'" ) else: os.makedirs(export_path) - console.print(f"[green]Folder '{export_path}' successfully created.[/green]") - set_preference("USER_EXPORTS_DIRECTORY", Path(export_path)) + Session().console.print( + f"[green]Folder '{export_path}' successfully created.[/green]" + ) return jobs_cmds +# pylint: disable=unused-argument def terminal(jobs_cmds: Optional[List[str]] = None, test_mode=False): - """Terminal Menu.""" - - current_user = get_current_user() - - log_terminal(test_mode=test_mode) - + """Run the terminal menu.""" ret_code = 1 t_controller = TerminalController(jobs_cmds) an_input = "" @@ -812,12 +545,8 @@ def terminal(jobs_cmds: Optional[List[str]] = None, test_mode=False): ) t_controller.print_help() - check_for_updates() while ret_code: - if current_user.preferences.ENABLE_QUICK_EXIT: - console.print("Quick exit enabled") - break # There is a command in the queue if t_controller.queue and len(t_controller.queue) > 0: @@ -832,17 +561,17 @@ def terminal(jobs_cmds: Optional[List[str]] = None, test_mode=False): # Print the current location because this was an instruction and we want user to know what was the action if an_input and an_input.split(" ")[0] in t_controller.CHOICES_COMMANDS: - console.print(f"{get_flair()} / $ {an_input}") + Session().console.print(f"{get_flair_and_username()} / $ {an_input}") # Get input command from user else: try: # Get input from user using auto-completion - if session and current_user.preferences.USE_PROMPT_TOOLKIT: + if Session().prompt_session and Session().settings.USE_PROMPT_TOOLKIT: # Check if toolbar hint was enabled - if current_user.preferences.TOOLBAR_HINT: - an_input = session.prompt( - f"{get_flair()} / $ ", + if Session().settings.TOOLBAR_HINT: + an_input = Session().prompt_session.prompt( + f"{get_flair_and_username()} / $ ", completer=t_controller.completer, search_ignore_case=True, bottom_toolbar=HTML( @@ -860,31 +589,21 @@ def terminal(jobs_cmds: Optional[List[str]] = None, test_mode=False): ), ) else: - an_input = session.prompt( - f"{get_flair()} / $ ", + an_input = Session().prompt_session.prompt( + f"{get_flair_and_username()} / $ ", completer=t_controller.completer, search_ignore_case=True, ) - elif is_papermill(): - pass - # Get input from user without auto-completion else: - an_input = input(f"{get_flair()} / $ ") + an_input = input(f"{get_flair_and_username()} / $ ") except (KeyboardInterrupt, EOFError): print_goodbye() break try: - if ( - an_input in ("login", "logout") - and get_show_prompt() - and is_auth_enabled() - ): - break - # Process the input command t_controller.queue = t_controller.switch(an_input) @@ -902,7 +621,7 @@ def terminal(jobs_cmds: Optional[List[str]] = None, test_mode=False): "The command '%s' doesn't exist on the / menu.", an_input, ) - console.print( + Session().console.print( f"[red]The command '{an_input}' doesn't exist on the / menu.[/red]\n", ) similar_cmd = difflib.get_close_matches( @@ -920,17 +639,13 @@ def terminal(jobs_cmds: Optional[List[str]] = None, test_mode=False): if candidate_input == an_input: an_input = "" t_controller.queue = [] - console.print("\n") + Session().console.print("\n") continue an_input = candidate_input - console.print(f"[green]Replacing by '{an_input}'.[/green]") + Session().console.print(f"[green]Replacing by '{an_input}'.[/green]") t_controller.queue.insert(0, an_input) - if an_input in ("login", "logout") and get_show_prompt() and is_auth_enabled(): - set_show_prompt(False) - return session_controller.main(welcome=False) - def insert_start_slash(cmds: List[str]) -> List[str]: """Insert a slash at the beginning of a command sequence.""" @@ -968,7 +683,9 @@ def run_scripts( Whether to log tests to txt files """ if not path.exists(): - console.print(f"File '{path}' doesn't exist. Launching base terminal.\n") + Session().console.print( + f"File '{path}' doesn't exist. Launching base terminal.\n" + ) if not test_mode: terminal() @@ -1022,7 +739,7 @@ def run_scripts( terminal(file_cmds, test_mode=True) else: with suppress_stdout(): - console.print(f"To ensure: {output}") + Session().console.print(f"To ensure: {output}") if output: timestamp = datetime.now().timestamp() stamp_str = str(timestamp).replace(".", "") @@ -1038,7 +755,7 @@ def run_scripts( def replace_dynamic(match: re.Match, special_arguments: Dict[str, str]) -> str: - """Replaces ${key=default} with value in special_arguments if it exists, else with default. + """Replace ${key=default} with value in special_arguments if it exists, else with default. Parameters ---------- @@ -1063,21 +780,20 @@ def replace_dynamic(match: re.Match, special_arguments: Dict[str, str]) -> str: def run_routine(file: str, routines_args=Optional[str]): """Execute command routine from .openbb file.""" - user_routine_path = ( - get_current_user().preferences.USER_DATA_DIRECTORY / "routines" / file - ) - default_routine_path = MISCELLANEOUS_DIRECTORY / "routines" / file + user_routine_path = Path(Session().user().preferences.export_directory, "routines") + default_routine_path = ASSETS_DIRECTORY / "routines" / file if user_routine_path.exists(): run_scripts(path=user_routine_path, routines_args=routines_args) elif default_routine_path.exists(): run_scripts(path=default_routine_path, routines_args=routines_args) else: - console.print( + Session().console.print( f"Routine not found, please put your `.openbb` file into : {user_routine_path}." ) +# pylint: disable=unused-argument def main( debug: bool, dev: bool, @@ -1105,19 +821,13 @@ def main( One or multiple inputs to be replaced in the routine and separated by commas. E.g. GME,AMC,BTC-USD """ - if kwargs["module"] == "ipykernel_launcher": - bootup() - return ipykernel_launcher(kwargs["module_file"], kwargs["module_hist_file"]) - if debug: - set_system_variable("DEBUG_MODE", True) + Session().settings.DEBUG_MODE = True if dev: - set_system_variable("DEV_BACKEND", True) - constants.BackendEnvironment.BASE_URL = "https://payments.openbb.dev/" - constants.BackendEnvironment.HUB_URL = "https://my.openbb.dev/" - - cfg.start_plot_backend() + Session().settings.DEV_BACKEND = True + Session().settings.BASE_URL = "https://payments.openbb.dev/" + Session().settings.HUB_URL = "https://my.openbb.dev" if isinstance(path_list, list) and path_list[0].endswith(".openbb"): run_routine(file=path_list[0], routines_args=routines_args) @@ -1134,7 +844,7 @@ def parse_args_and_run(): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="terminal", - description="The OpenBB Terminal.", + description="The OpenBB Platform CLI.", ) parser.add_argument( "-d", @@ -1142,7 +852,7 @@ def parse_args_and_run(): dest="debug", action="store_true", default=False, - help="Runs the terminal in debug mode.", + help="Runs the CLI in debug mode.", ) parser.add_argument( "--dev", @@ -1179,12 +889,6 @@ def parse_args_and_run(): " to see testing argument options." ), ) - if is_auth_enabled(): - parser.add_argument( - "--login", - action="store_true", - help="Go to login prompt.", - ) # The args -m, -f and --HistoryManager.hist_file are used only in reports menu # by papermill and that's why they have suppress help. parser.add_argument( @@ -1216,7 +920,7 @@ def parse_args_and_run(): # Use -d flag if you want to see the unknown args. if unknown: if ns_parser.debug: - console.print(unknown) + Session().console.print(unknown) else: sys.exit(-1) @@ -1231,5 +935,16 @@ def parse_args_and_run(): ) +def launch( + debug: bool = False, dev: bool = False, queue: Optional[List[str]] = None +) -> None: + """Launch terminal.""" + + if queue: + main(debug, dev, queue, module="") + else: + parse_args_and_run() + + if __name__ == "__main__": parse_args_and_run() diff --git a/openbb_terminal/openbb_terminal/controllers/utils.py b/openbb_terminal/openbb_terminal/controllers/utils.py new file mode 100644 index 00000000000..6d886d9dab5 --- /dev/null +++ b/openbb_terminal/openbb_terminal/controllers/utils.py @@ -0,0 +1,1056 @@ +"""Terminal utils.""" + +import argparse +import os +import random +import re +import shutil +import sys +from contextlib import contextmanager +from datetime import ( + datetime, +) +from pathlib import Path +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union + +import numpy as np +import pandas as pd +import requests +from openbb import obb +from openbb_charting.core.backend import create_backend, get_backend +from openbb_core.app.model.charts.charting_settings import ChartingSettings +from packaging import version +from pytz import all_timezones, timezone +from rich.table import Table + +from openbb_terminal.config.constants import AVAILABLE_FLAIRS, ENV_FILE_SETTINGS +from openbb_terminal.session import Session + +if TYPE_CHECKING: + from openbb_charting.core.openbb_figure import OpenBBFigure + +# pylint: disable=R1702,R0912 + + +# pylint: disable=too-many-statements,no-member,too-many-branches,C0302 + + +def remove_file(path: Path) -> bool: + """Remove path. + + Parameters + ---------- + path : Path + The file path. + + Returns + ------- + bool + The status of the removal. + """ + # TODO: Check why module level import leads to circular import. + try: + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) + return True + except Exception: + Session().console.print( + f"\n[bold red]Failed to remove {path}" + "\nPlease delete this manually![/bold red]" + ) + return False + + +def print_goodbye(): + """Print a goodbye message when quitting the terminal.""" + # LEGACY GOODBYE MESSAGES - You'll live in our hearts forever. + # "An informed ape, is a strong ape." + # "Remember that stonks only go up." + # "Diamond hands." + # "Apes together strong." + # "This is our way." + # "Keep the spacesuit ape, we haven't reached the moon yet." + # "I am not a cat. I'm an ape." + # "We like the terminal." + # "...when offered a flight to the moon, nobody asks about what seat." + + text = """ +[param]Thank you for using the OpenBB Platform CLI and being part of this journey.[/param] + +We hope you'll find the new CLI as valuable as this. To stay tuned, sign up for our newsletter: [cmds]https://openbb.co/newsletter.[/] + +In the meantime, check out our other products: + +[bold]OpenBB Terminal Pro[/]: [cmds]https://openbb.co/products/pro[/cmds] +[bold]OpenBB Platform:[/] [cmds]https://openbb.co/products/platform[/cmds] +[bold]OpenBB Bot[/]: [cmds]https://openbb.co/products/bot[/cmds] + """ + Session().console.print(text) + + +def hide_splashscreen(): + """Hide the splashscreen on Windows bundles. + + `pyi_splash` is a PyInstaller "fake-package" that's used to communicate + with the splashscreen on Windows. + Sending the `close` signal to the splash screen is required. + The splash screen remains open until this function is called or the Python + program is terminated. + """ + try: + import pyi_splash # type: ignore # pylint: disable=import-outside-toplevel + + pyi_splash.update_text("Terminal Loaded!") + pyi_splash.close() + except Exception as e: + Session().console.print(f"Error: Unable to hide splashscreen: {e}") + + +def print_guest_block_msg(): + """Block guest users from using the terminal.""" + if Session().is_local(): + Session().console.print( + "[info]You are currently logged as a guest.[/info]\n" + "[info]Login to use this feature.[/info]\n\n" + "[info]If you don't have an account, you can create one here: [/info]" + f"[cmds]{Session().settings.HUB_URL + '/register'}\n[/cmds]" + ) + + +def is_installer() -> bool: + """Check whether or not it is a packaged version (Windows or Mac installer.""" + return getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS") + + +def bootup(): + """Bootup the terminal.""" + if sys.platform == "win32": + # Enable VT100 Escape Sequence for WINDOWS 10 Ver. 1607 + os.system("") # nosec # noqa: S605,S607 + # Hide splashscreen loader of the packaged app + if is_installer(): + hide_splashscreen() + + try: + if os.name == "nt": + # pylint: disable=E1101 + sys.stdin.reconfigure(encoding="utf-8") # type: ignore + # pylint: disable=E1101 + sys.stdout.reconfigure(encoding="utf-8") # type: ignore + except Exception as e: + Session().console.print(e, "\n") + + +def check_for_updates() -> None: + """Check if the latest version is running. + + Checks github for the latest release version and compares it to cfg.VERSION. + """ + # The commit has was commented out because the terminal was crashing due to git import for multiple users + # ({str(git.Repo('.').head.commit)[:7]}) + try: + r = request( + "https://api.github.com/repos/openbb-finance/openbbterminal/releases/latest" + ) + except Exception: + r = None + + if r and r.status_code == 200: + latest_tag_name = r.json()["tag_name"] + latest_version = version.parse(latest_tag_name) + current_version = version.parse(Session().settings.VERSION) + + if check_valid_versions(latest_version, current_version): + if current_version == latest_version: + Session().console.print( + "[green]You are using the latest stable version[/green]" + ) + else: + Session().console.print( + "[yellow]You are not using the latest stable version[/yellow]" + ) + if current_version < latest_version: + Session().console.print( + "[yellow]Check for updates at https://my.openbb.co/app/terminal/download[/yellow]" + ) + + else: + Session().console.print( + "[yellow]You are using an unreleased version[/yellow]" + ) + + else: + Session().console.print("[red]You are using an unrecognized version.[/red]") + else: + Session().console.print( + "[yellow]Unable to check for updates... " + + "Check your internet connection and try again...[/yellow]" + ) + Session().console.print("\n") + + +def check_valid_versions( + latest_version: version.Version, + current_version: version.Version, +) -> bool: + """Check if the versions are valid.""" + if ( + not latest_version + or not current_version + or not isinstance(latest_version, version.Version) + or not isinstance(current_version, version.Version) + ): + return False + return True + + +def welcome_message(): + """Print the welcome message. + + Prints first welcome message, help and a notification if updates are available. + """ + Session().console.print( + f"\nWelcome to OpenBB Platform CLI v{Session().settings.VERSION}" + ) + + +def reset(queue: Optional[List[str]] = None): + """Reset the CLI. + + Allows for checking code without quitting. + """ + Session().console.print("resetting...") + Session().reset() + debug = Session().settings.DEBUG_MODE + dev = Session().settings.DEV_BACKEND + + try: + # remove the hub routines + if not Session().is_local(): + remove_file( + Path(Session().user.preferences.export_directory, "routines", "hub") + ) + + # if not get_current_user().profile.remember: + # Local.remove(HIST_FILE_PROMPT) + + # we clear all openbb_terminal modules from sys.modules + for module in list(sys.modules.keys()): + parts = module.split(".") + if parts[0] == "openbb_terminal": + del sys.modules[module] + + queue_list = ["/".join(queue) if len(queue) > 0 else ""] # type: ignore + # pylint: disable=import-outside-toplevel + # we run the terminal again + if Session().is_local(): + from openbb_terminal.controllers.terminal_controller import main + + main(debug, dev, queue_list, module="") # type: ignore + else: + from openbb_terminal.controllers.terminal_controller import launch + + launch(queue=queue_list) + + except Exception as e: + Session().console.print(f"Unfortunately, resetting wasn't possible: {e}\n") + print_goodbye() + + +@contextmanager +def suppress_stdout(): + """Suppress the stdout.""" + with open(os.devnull, "w") as devnull: + old_stdout = sys.stdout + old_stderr = sys.stderr + sys.stdout = devnull + sys.stderr = devnull + try: + yield + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr + + +def first_time_user() -> bool: + """Check whether a user is a first time user. + + A first time user is someone with an empty .env file. + If this is true, it also adds an env variable to make sure this does not run again. + + Returns + ------- + bool + Whether or not the user is a first time user + """ + if ENV_FILE_SETTINGS.stat().st_size == 0: + Session().settings.set_item("PREVIOUS_USE", True) + return True + return False + + +def parse_and_split_input(an_input: str, custom_filters: List) -> List[str]: + """Filter and split the input queue. + + Uses regex to filters command arguments that have forward slashes so that it doesn't + break the execution of the command queue. + Currently handles unix paths and sorting settings for screener menus. + + Parameters + ---------- + an_input : str + User input as string + custom_filters : List + Additional regular expressions to match + + Returns + ------- + List[str] + Command queue as list + """ + # Make sure that the user can go back to the root when doing "/" + if an_input and an_input == "/": + an_input = "home" + + # everything from ` -f ` to the next known extension + file_flag = r"(\ -f |\ --file )" + up_to = r".*?" + known_extensions = r"(\.(xlsx|csv|xls|tsv|json|yaml|ini|openbb|ipynb))" + unix_path_arg_exp = f"({file_flag}{up_to}{known_extensions})" + + # Add custom expressions to handle edge cases of individual controllers + custom_filter = "" + for exp in custom_filters: + if exp is not None: + custom_filter += f"|{exp}" + del exp + + slash_filter_exp = f"({unix_path_arg_exp}){custom_filter}" + + filter_input = True + placeholders: Dict[str, str] = {} + while filter_input: + match = re.search(pattern=slash_filter_exp, string=an_input) + if match is not None: + placeholder = f"{{placeholder{len(placeholders)+1}}}" + placeholders[placeholder] = an_input[ + match.span()[0] : match.span()[1] # noqa:E203 + ] + an_input = ( + an_input[: match.span()[0]] + + placeholder + + an_input[match.span()[1] :] # noqa:E203 + ) + else: + filter_input = False + + commands = an_input.split("/") if "-t" not in an_input else [an_input] + + for command_num, command in enumerate(commands): + if command == commands[command_num] == commands[-1] == "": + return list(filter(None, commands)) + matching_placeholders = [tag for tag in placeholders if tag in command] + if len(matching_placeholders) > 0: + for tag in matching_placeholders: + commands[command_num] = command.replace(tag, placeholders[tag]) + return commands + + +def return_colored_value(value: str): + """Return the string value based on condition. + + Return it with green, yellow, red or white color based on + whether the number is positive, negative, zero or other, respectively. + + Parameters + ---------- + value: str + string to be checked + + Returns + ------- + value: str + string with color based on value of number if it exists + """ + values = re.findall(r"[-+]?(?:\d*\.\d+|\d+)", value) + + # Finds exactly 1 number in the string + if len(values) == 1: + if float(values[0]) > 0: + return f"[green]{value}[/green]" + + if float(values[0]) < 0: + return f"[red]{value}[/red]" + + if float(values[0]) == 0: + return f"[yellow]{value}[/yellow]" + + return f"{value}" + + +def _get_backend(): + """Get the Platform charting backend.""" + try: + return get_backend() + except ValueError: + # backend might not be created yet + charting_settings = ChartingSettings( + system_settings=obb.system, user_settings=obb.user # type: ignore + ) + create_backend(charting_settings) + get_backend().start(debug=charting_settings.debug_mode) + return get_backend() + + +# pylint: disable=too-many-arguments +def print_rich_table( # noqa: PLR0912 + df: pd.DataFrame, + show_index: bool = False, + title: str = "", + index_name: str = "", + headers: Optional[Union[List[str], pd.Index]] = None, + floatfmt: Union[str, List[str]] = ".2f", + show_header: bool = True, + automatic_coloring: bool = False, + columns_to_auto_color: Optional[List[str]] = None, + rows_to_auto_color: Optional[List[str]] = None, + export: bool = False, + limit: Optional[int] = 1000, + columns_keep_types: Optional[List[str]] = None, + use_tabulate_df: bool = True, +): + """Prepare a table from df in rich. + + Parameters + ---------- + df: pd.DataFrame + Dataframe to turn into table + show_index: bool + Whether to include index + title: str + Title for table + index_name : str + Title for index column + headers: List[str] + Titles for columns + floatfmt: Union[str, List[str]] + Float number formatting specs as string or list of strings. Defaults to ".2f" + show_header: bool + Whether to show the header row. + automatic_coloring: bool + Automatically color a table based on positive and negative values + columns_to_auto_color: List[str] + Columns to automatically color + rows_to_auto_color: List[str] + Rows to automatically color + export: bool + Whether we are exporting the table to a file. If so, we don't want to print it. + limit: Optional[int] + Limit the number of rows to show. + columns_keep_types: Optional[List[str]] + Columns to keep their types, i.e. not convert to numeric + """ + if export: + return + + # Make a copy of the dataframe to avoid SettingWithCopyWarning + df = df.copy() + + show_index = not isinstance(df.index, pd.RangeIndex) and show_index + # convert non-str that are not timestamp or int into str + # eg) praw.models.reddit.subreddit.Subreddit + for col in df.columns: + if columns_keep_types is not None and col in columns_keep_types: + continue + try: + if not any( + isinstance(df[col].iloc[x], pd.Timestamp) + for x in range(min(10, len(df))) + ): + df[col] = pd.to_numeric(df[col], errors="ignore") + except (ValueError, TypeError): + df[col] = df[col].astype(str) + + def _get_headers(_headers: Union[List[str], pd.Index]) -> List[str]: + """Check if headers are valid and return them.""" + output = _headers + if isinstance(_headers, pd.Index): + output = list(_headers) + if len(output) != len(df.columns): + raise ValueError("Length of headers does not match length of DataFrame.") + return output + + if Session().settings.USE_INTERACTIVE_DF: + df_outgoing = df.copy() + # If headers are provided, use them + if headers is not None: + # We check if headers are valid + df_outgoing.columns = _get_headers(headers) + + if show_index and index_name not in df_outgoing.columns: + # If index name is provided, we use it + df_outgoing.index.name = index_name or "Index" + df_outgoing = df_outgoing.reset_index() + + for col in df_outgoing.columns: + if col == "": + df_outgoing = df_outgoing.rename(columns={col: " "}) + + _get_backend().send_table( + df_table=df_outgoing, + title=title, + theme=Session().user.preferences.table_style, + ) + return + + df = df.copy() if not limit else df.copy().iloc[:limit] + if automatic_coloring: + if columns_to_auto_color: + for col in columns_to_auto_color: + # checks whether column exists + if col in df.columns: + df[col] = df[col].apply(lambda x: return_colored_value(str(x))) + if rows_to_auto_color: + for row in rows_to_auto_color: + # checks whether row exists + if row in df.index: + df.loc[row] = df.loc[row].apply( + lambda x: return_colored_value(str(x)) + ) + + if columns_to_auto_color is None and rows_to_auto_color is None: + df = df.applymap(lambda x: return_colored_value(str(x))) + + exceeds_allowed_columns = ( + len(df.columns) > Session().settings.ALLOWED_NUMBER_OF_COLUMNS + ) + exceeds_allowed_rows = len(df) > Session().settings.ALLOWED_NUMBER_OF_ROWS + + if exceeds_allowed_columns: + original_columns = df.columns.tolist() + trimmed_columns = df.columns.tolist()[ + : Session().settings.ALLOWED_NUMBER_OF_COLUMNS + ] + df = df[trimmed_columns] + trimmed_columns = [ + col for col in original_columns if col not in trimmed_columns + ] + + if exceeds_allowed_rows: + n_rows = len(df.index) + trimmed_rows = df.index.tolist()[: Session().settings.ALLOWED_NUMBER_OF_ROWS] + df = df.loc[trimmed_rows] + trimmed_rows_count = n_rows - Session().settings.ALLOWED_NUMBER_OF_ROWS + + if use_tabulate_df: + table = Table(title=title, show_lines=True, show_header=show_header) + + if show_index: + table.add_column(index_name) + + if headers is not None: + headers = _get_headers(headers) + for header in headers: + table.add_column(str(header)) + else: + for column in df.columns: + table.add_column(str(column)) + + if isinstance(floatfmt, list) and len(floatfmt) != len(df.columns): + raise ( + ValueError( + "Length of floatfmt list does not match length of DataFrame columns." + ) + ) + if isinstance(floatfmt, str): + floatfmt = [floatfmt for _ in range(len(df.columns))] + + for idx, values in zip(df.index.tolist(), df.values.tolist()): + # remove hour/min/sec from timestamp index - Format: YYYY-MM-DD # make better + row_idx = [str(idx)] if show_index else [] + row_idx += [ + ( + str(x) + if not isinstance(x, float) and not isinstance(x, np.float64) + else ( + f"{x:{floatfmt[idx]}}" + if isinstance(floatfmt, list) + else ( + f"{x:.2e}" + if 0 < abs(float(x)) <= 0.0001 + else f"{x:floatfmt}" + ) + ) + ) + for idx, x in enumerate(values) + ] + table.add_row(*row_idx) + Session().console.print(table) + else: + Session().console.print(df.to_string(col_space=0)) + + if exceeds_allowed_columns: + Session().console.print( + f"[yellow]\nAllowed number of columns exceeded ({Session().settings.ALLOWED_NUMBER_OF_COLUMNS}).\n" + f"The following columns were removed from the output: {', '.join(trimmed_columns)}.\n[/yellow]" + ) + + if exceeds_allowed_rows: + Session().console.print( + f"[yellow]\nAllowed number of rows exceeded ({Session().settings.ALLOWED_NUMBER_OF_ROWS}).\n" + f"{trimmed_rows_count} rows were removed from the output.\n[/yellow]" + ) + + if exceeds_allowed_columns or exceeds_allowed_rows: + Session().console.print( + "Use the `--export` flag to analyse the full output on a file." + ) + + +def check_non_negative(value) -> int: + """Argparse type to check non negative int.""" + new_value = int(value) + if new_value < 0: + raise argparse.ArgumentTypeError(f"{value} is negative") + return new_value + + +def check_positive(value) -> int: + """Argparse type to check positive int.""" + new_value = int(value) + if new_value <= 0: + raise argparse.ArgumentTypeError(f"{value} is an invalid positive int value") + return new_value + + +def get_user_agent() -> str: + """Get a not very random user agent.""" + user_agent_strings = [ + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.10; rv:86.1) Gecko/20100101 Firefox/86.1", + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:86.1) Gecko/20100101 Firefox/86.1", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:82.1) Gecko/20100101 Firefox/82.1", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:86.0) Gecko/20100101 Firefox/86.0", + "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:86.0) Gecko/20100101 Firefox/86.0", + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.10; rv:83.0) Gecko/20100101 Firefox/83.0", + "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:84.0) Gecko/20100101 Firefox/84.0", + ] + + return random.choice(user_agent_strings) # nosec # noqa: S311 + + +def get_flair() -> str: + """Get a flair icon.""" + current_flair = str(Session().settings.FLAIR) + flair = AVAILABLE_FLAIRS.get(current_flair, current_flair) + return flair + + +def get_dtime() -> str: + """Get a datetime string.""" + dtime = "" + if Session().settings.USE_DATETIME and get_user_timezone_or_invalid() != "INVALID": + dtime = datetime.now(timezone(get_user_timezone())).strftime("%Y %b %d, %H:%M") + return dtime + + +def get_flair_and_username() -> str: + """Get a flair icon and username.""" + flair = get_flair() + dtime = get_dtime() + + if dtime: + dtime = f"{dtime} " + + username = getattr(Session().user.profile.hub_session, "username", "") + if username: + username = f"[{username}] " + + return f"{dtime}{username}{flair}" + + +def is_timezone_valid(user_tz: str) -> bool: + """Check whether user timezone is valid. + + Parameters + ---------- + user_tz: str + Timezone to check for validity + + Returns + ------- + bool + True if timezone provided is valid + """ + return user_tz in all_timezones + + +def get_user_timezone() -> str: + """Get user timezone if it is a valid one. + + Returns + ------- + str + user timezone based on .env file + """ + return Session().settings.TIMEZONE + + +def get_user_timezone_or_invalid() -> str: + """Get user timezone if it is a valid one. + + Returns + ------- + str + user timezone based on timezone.openbb file or INVALID + """ + user_tz = get_user_timezone() + if is_timezone_valid(user_tz): + return f"{user_tz}" + return "INVALID" + + +def check_file_type_saved(valid_types: Optional[List[str]] = None): + """Provide valid types for the user to be able to select. + + Parameters + ---------- + valid_types: List[str] + List of valid types to export data + + Returns + ------- + check_filenames: Optional[List[str]] + Function that returns list of filenames to export data + """ + + def check_filenames(filenames: str = "") -> str: + """Check if filenames are valid. + + Parameters + ---------- + filenames: str + filenames to be saved separated with comma + + Returns + ---------- + str + valid filenames separated with comma + """ + if not filenames or not valid_types: + return "" + valid_filenames = list() + for filename in filenames.split(","): + if filename.endswith(tuple(valid_types)): + valid_filenames.append(filename) + else: + Session().console.print( + f"[red]Filename '{filename}' provided is not valid!\nPlease use one of the following file types:" + f"{','.join(valid_types)}[/red]\n" + ) + return ",".join(valid_filenames) + + return check_filenames + + +def remove_timezone_from_dataframe(df: pd.DataFrame) -> pd.DataFrame: + """Remove timezone information from a dataframe. + + Parameters + ---------- + df : pd.DataFrame + The dataframe to remove timezone information from + + Returns + ------- + pd.DataFrame + The dataframe with timezone information removed + """ + + date_cols = [] + index_is_date = False + + # Find columns and index containing date data + if ( + df.index.dtype.kind == "M" + and hasattr(df.index.dtype, "tz") + and df.index.dtype.tz is not None + ): + index_is_date = True + + for col, dtype in df.dtypes.items(): + if dtype.kind == "M" and hasattr(df.index.dtype, "tz") and dtype.tz is not None: + date_cols.append(col) + + # Remove the timezone information + for col in date_cols: + df[col] = df[col].dt.date + + if index_is_date: + index_name = df.index.name + df.index = df.index.date + df.index.name = index_name + + return df + + +def compose_export_path(func_name: str, dir_path: str) -> Path: + """Compose export path for data from the terminal. + + Creates a path to a folder and a filename based on conditions. + + Parameters + ---------- + func_name : str + Name of the command that invokes this function + dir_path : str + Path of directory from where this function is called + + Returns + ------- + Path + Path variable containing the path of the exported file + """ + now = datetime.now() + # Resolving all symlinks and also normalizing path. + resolve_path = Path(dir_path).resolve() + # Getting the directory names from the path. Instead of using split/replace (Windows doesn't like that) + # check if this is done in a main context to avoid saving with openbb_terminal + if resolve_path.parts[-2] == "openbb_terminal": + path_cmd = f"{resolve_path.parts[-1]}" + else: + path_cmd = f"{resolve_path.parts[-2]}_{resolve_path.parts[-1]}" + + default_filename = f"{now.strftime('%Y%m%d_%H%M%S')}_{path_cmd}_{func_name}" + + full_path = Path(Session().user.preferences.export_directory) / default_filename + + return full_path + + +def ask_file_overwrite(file_path: Path) -> Tuple[bool, bool]: + """Provide a prompt for overwriting existing files. + + Returns two values, the first is a boolean indicating if the file exists and the + second is a boolean indicating if the user wants to overwrite the file. + """ + if Session().settings.FILE_OVERWRITE: + return False, True + if Session().settings.TEST_MODE: + return False, True + if file_path.exists(): + overwrite = input("\nFile already exists. Overwrite? [y/n]: ").lower() + if overwrite == "y": + file_path.unlink(missing_ok=True) + # File exists and user wants to overwrite + return True, True + # File exists and user does not want to overwrite + return True, False + # File does not exist + return False, True + + +# This is a false positive on pylint and being tracked in pylint #3060 +# pylint: disable=abstract-class-instantiated +def save_to_excel(df, saved_path, sheet_name, start_row=0, index=True, header=True): + """Save a Pandas DataFrame to an Excel file. + + Args: + df: A Pandas DataFrame. + saved_path: The path to the Excel file to save to. + sheet_name: The name of the sheet to save the DataFrame to. + start_row: The row number to start writing the DataFrame at. + index: Whether to write the DataFrame index to the Excel file. + header: Whether to write the DataFrame header to the Excel file. + """ + overwrite_options = { + "o": "replace", + "a": "overlay", + "n": "new", + } + + if not saved_path.exists(): + with pd.ExcelWriter(saved_path, engine="openpyxl") as writer: + df.to_excel(writer, sheet_name=sheet_name, index=index, header=header) + + else: + with pd.ExcelFile(saved_path) as reader: + overwrite_option = "n" + if sheet_name in reader.sheet_names: + overwrite_option = input( + "\nSheet already exists. Overwrite/Append/New? [o/a/n]: " + ).lower() + start_row = 0 + if overwrite_option == "a": + existing_df = pd.read_excel(saved_path, sheet_name=sheet_name) + start_row = existing_df.shape[0] + 1 + + with pd.ExcelWriter( + saved_path, + mode="a", + if_sheet_exists=overwrite_options[overwrite_option], + engine="openpyxl", + ) as writer: + df.to_excel( + writer, + sheet_name=sheet_name, + startrow=start_row, + index=index, + header=False if overwrite_option == "a" else header, + ) + + +# This is a false positive on pylint and being tracked in pylint #3060 +# pylint: disable=abstract-class-instantiated +def export_data( + export_type: str, + dir_path: str, + func_name: str, + df: pd.DataFrame = pd.DataFrame(), + sheet_name: Optional[str] = None, + figure: Optional["OpenBBFigure"] = None, + margin: bool = True, +) -> None: + """Export data to a file. + + Parameters + ---------- + export_type : str + Type of export between: csv,json,xlsx,xls + dir_path : str + Path of directory from where this function is called + func_name : str + Name of the command that invokes this function + df : pd.Dataframe + Dataframe of data to save + sheet_name : str + If provided. The name of the sheet to save in excel file + figure : Optional[OpenBBFigure] + Figure object to save as image file + margin : bool + Automatically adjust subplot parameters to give specified padding. + """ + + if export_type: + saved_path = compose_export_path(func_name, dir_path).resolve() + saved_path.parent.mkdir(parents=True, exist_ok=True) + for exp_type in export_type.split(","): + # In this scenario the path was provided, e.g. --export pt.csv, pt.jpg + if "." in exp_type: + saved_path = saved_path.with_name(exp_type) + # In this scenario we use the default filename + else: + if ".OpenBB_openbb_terminal" in saved_path.name: + saved_path = saved_path.with_name( + saved_path.name.replace( + ".OpenBB_openbb_terminal", "OpenBBTerminal" + ) + ) + saved_path = saved_path.with_suffix(f".{exp_type}") + + exists, overwrite = False, False + is_xlsx = exp_type.endswith("xlsx") + if sheet_name is None and is_xlsx or not is_xlsx: + exists, overwrite = ask_file_overwrite(saved_path) + + if exists and not overwrite: + existing = len(list(saved_path.parent.glob(saved_path.stem + "*"))) + saved_path = saved_path.with_stem(f"{saved_path.stem}_{existing + 1}") + + df = df.replace( + { + r"\[yellow\]": "", + r"\[/yellow\]": "", + r"\[green\]": "", + r"\[/green\]": "", + r"\[red\]": "", + r"\[/red\]": "", + r"\[magenta\]": "", + r"\[/magenta\]": "", + }, + regex=True, + ) + + if exp_type.endswith("csv"): + df.to_csv(saved_path) + elif exp_type.endswith("json"): + df.reset_index(drop=True, inplace=True) + df.to_json(saved_path) + elif exp_type.endswith("xlsx"): + # since xlsx does not support datetimes with timezones we need to remove it + df = remove_timezone_from_dataframe(df) + + if sheet_name is None: # noqa: SIM223 + df.to_excel( + saved_path, + index=True, + header=True, + ) + else: + save_to_excel(df, saved_path, sheet_name) + + elif saved_path.suffix in [".jpg", ".pdf", ".png", ".svg"]: + if figure is None: + Session().console.print("No plot to export.") + continue + figure.show(export_image=saved_path, margin=margin) + else: + Session().console.print("Wrong export file specified.") + continue + + Session().console.print(f"Saved file: {saved_path}") + + if figure is not None: + figure._exported = True # pylint: disable=protected-access + + +def system_clear(): + """Clear screen.""" + os.system("cls||clear") # nosec # noqa: S605,S607 + + +# Write an abstract helper to make requests from a url with potential headers and params +def request( + url: str, method: str = "get", timeout: int = 0, **kwargs +) -> requests.Response: + """Make requests from a url with potential headers and params. + + Parameters + ---------- + url : str + Url to make the request to + method : str + HTTP method to use. Choose from: + delete, get, head, patch, post, put, by default "get" + timeout : int + How many seconds to wait for the server to send data + + Returns + ------- + requests.Response + Request response object + + Raises + ------ + ValueError + If invalid method is passed + """ + method = method.lower() + if method not in ["delete", "get", "head", "patch", "post", "put"]: + raise ValueError(f"Invalid method: {method}") + # We want to add a user agent to the request, so check if there are any headers + # If there are headers, check if there is a user agent, if not add one. + # Some requests seem to work only with a specific user agent, so we want to be able to override it. + headers = kwargs.pop("headers", {}) + timeout = timeout or Session().user.preferences.request_timeout + + if "User-Agent" not in headers: + headers["User-Agent"] = get_user_agent() + func = getattr(requests, method) + return func( + url, + headers=headers, + timeout=timeout, + **kwargs, + ) diff --git a/openbb_terminal/openbb_terminal/models/settings.py b/openbb_terminal/openbb_terminal/models/settings.py new file mode 100644 index 00000000000..85dbaf2ef93 --- /dev/null +++ b/openbb_terminal/openbb_terminal/models/settings.py @@ -0,0 +1,86 @@ +"""Settings model""" + +from typing import Any + +from dotenv import dotenv_values, set_key +from pydantic import BaseModel, ConfigDict, model_validator + +from openbb_terminal.config.constants import ( + ENV_FILE_PROJECT, + ENV_FILE_REPOSITORY, + ENV_FILE_SETTINGS, +) + + +class Settings(BaseModel): + """Settings model.""" + + # Platform CLI version + VERSION: str = "1.0.0" + + # DEVELOPMENT FLAGS + TEST_MODE: bool = False + DEBUG_MODE: bool = False + DEV_BACKEND: bool = False + + # FEATURE FLAGS + FILE_OVERWRITE: bool = False + SHOW_VERSION: bool = True + USE_INTERACTIVE_DF: bool = True + USE_CLEAR_AFTER_CMD: bool = False + USE_DATETIME: bool = True + USE_PROMPT_TOOLKIT: bool = True + ENABLE_EXIT_AUTO_HELP: bool = True + REMEMBER_CONTEXTS: bool = True + ENABLE_RICH_PANEL: bool = True + TOOLBAR_HINT: bool = True + + # GENERAL + TIMEZONE: str = "America/New_York" + FLAIR: str = ":openbb" + USE_LANGUAGE: str = "en" + PREVIOUS_USE: bool = False + + # STYLE + RICH_STYLE: str = "dark" + + # OUTPUT + ALLOWED_NUMBER_OF_ROWS: int = 366 + ALLOWED_NUMBER_OF_COLUMNS: int = 15 + + # OPENBB + HUB_URL: str = "https://my.openbb.co" + BASE_URL: str = "https://payments.openbb.co" + + model_config = ConfigDict(validate_assignment=True) + + def __repr__(self) -> str: + """Return a string representation of the model.""" + return f"{self.__class__.__name__}\n\n" + "\n".join( + f"{k}: {v}" for k, v in self.model_dump().items() + ) + + @model_validator(mode="before") + @classmethod + def from_env(cls, values: dict) -> dict: + """Load .env files. + + Loads the dotenv files in the following order: + 1. Repository .env file + 2. Package .env file + 3. User .env file + """ + settings = {} + settings.update(dotenv_values(ENV_FILE_REPOSITORY)) + settings.update(dotenv_values(ENV_FILE_PROJECT)) + settings.update(dotenv_values(ENV_FILE_SETTINGS)) + settings.update(values) + filtered = {k.replace("OPENBB_", ""): v for k, v in settings.items()} + return filtered + + def set_item(self, key: str, value: Any) -> None: + """Set an item in the model and save to .env.""" + # TODO: Check if this is ok, we are just saving into the settings .env + # Same behavior as before... + setattr(self, key, value) + set_key(str(ENV_FILE_SETTINGS), "OPENBB_" + key, str(value)) diff --git a/openbb_terminal/openbb_terminal/session.py b/openbb_terminal/openbb_terminal/session.py new file mode 100644 index 00000000000..a1b8be6f90f --- /dev/null +++ b/openbb_terminal/openbb_terminal/session.py @@ -0,0 +1,79 @@ +"""Settings module.""" + +import sys +from pathlib import Path +from typing import Optional + +from openbb import obb +from openbb_core.app.model.abstract.singleton import SingletonMeta +from openbb_core.app.model.user_settings import UserSettings as User +from prompt_toolkit import PromptSession +from prompt_toolkit.history import FileHistory + +from openbb_terminal.config.console import Console +from openbb_terminal.config.constants import HIST_FILE_PROMPT +from openbb_terminal.config.style import Style +from openbb_terminal.models.settings import Settings + + +class Session(metaclass=SingletonMeta): + """Session class.""" + + def __init__(self): + """Initialize session.""" + self._obb = obb + self._settings = Settings() + self._style = Style( + style=self._settings.RICH_STYLE, + directory=Path(self._obb.user.preferences.user_styles_directory), + ) + self._console = Console( + settings=self._settings, style=self._style.console_style + ) + self._prompt_session = self._get_prompt_session() + + @property + def user(self) -> User: + """Get platform user.""" + return self._obb.user + + @property + def settings(self) -> Settings: + """Get terminal settings.""" + return self._settings + + @property + def style(self) -> Style: + """Get terminal style.""" + return self._style + + @property + def console(self) -> Console: + """Get console.""" + return self._console + + @property + def prompt_session(self) -> Optional[PromptSession]: + """Get prompt session.""" + return self._prompt_session + + def _get_prompt_session(self) -> Optional[PromptSession]: + """Initialize prompt session.""" + try: + if sys.stdin.isatty(): + prompt_session: Optional[PromptSession] = PromptSession( + history=FileHistory(str(HIST_FILE_PROMPT)) + ) + else: + prompt_session = None + except Exception: + prompt_session = None + + return prompt_session + + def is_local(self) -> bool: + """Check if user is local.""" + return not bool(self.user.profile.hub_session) + + def reset(self) -> None: + pass diff --git a/openbb_terminal/poetry.lock b/openbb_terminal/poetry.lock new file mode 100644 index 00000000000..45b7fb75676 --- /dev/null +++ b/openbb_terminal/poetry.lock @@ -0,0 +1,5072 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "aiohttp" +version = "3.9.5" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "anyio" +version = "3.7.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, +] + +[package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = "*" +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "astroid" +version = "3.1.0" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, + {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +optional = false +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + +[[package]] +name = "bandit" +version = "1.7.8" +description = "Security oriented static analyser for python code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bandit-1.7.8-py3-none-any.whl", hash = "sha256:509f7af645bc0cd8fd4587abc1a038fc795636671ee8204d502b933aee44f381"}, + {file = "bandit-1.7.8.tar.gz", hash = "sha256:36de50f720856ab24a24dbaa5fee2c66050ed97c1477e0a1159deab1775eab6b"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +PyYAML = ">=5.3.1" +rich = "*" +stevedore = ">=1.20.0" + +[package.extras] +baseline = ["GitPython (>=3.1.30)"] +sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] +toml = ["tomli (>=1.1.0)"] +yaml = ["PyYAML"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "black" +version = "24.4.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"}, + {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"}, + {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"}, + {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"}, + {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"}, + {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"}, + {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"}, + {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"}, + {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"}, + {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"}, + {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"}, + {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"}, + {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"}, + {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"}, + {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"}, + {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"}, + {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"}, + {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"}, + {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"}, + {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"}, + {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"}, + {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "build" +version = "1.2.1" +description = "A simple, correct Python build frontend" +optional = false +python-versions = ">=3.8" +files = [ + {file = "build-1.2.1-py3-none-any.whl", hash = "sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4"}, + {file = "build-1.2.1.tar.gz", hash = "sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "os_name == \"nt\""} +importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} +packaging = ">=19.1" +pyproject_hooks = "*" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"] +test = ["build[uv,virtualenv]", "filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"] +typing = ["build[uv]", "importlib-metadata (>=5.1)", "mypy (>=1.9.0,<1.10.0)", "tomli", "typing-extensions (>=3.7.4.3)"] +uv = ["uv (>=0.1.18)"] +virtualenv = ["virtualenv (>=20.0.35)"] + +[[package]] +name = "cachecontrol" +version = "0.14.0" +description = "httplib2 caching for requests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachecontrol-0.14.0-py3-none-any.whl", hash = "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0"}, + {file = "cachecontrol-0.14.0.tar.gz", hash = "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938"}, +] + +[package.dependencies] +filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""} +msgpack = ">=0.5.2,<2.0.0" +requests = ">=2.16.0" + +[package.extras] +dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "furo", "mypy", "pytest", "pytest-cov", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"] +filecache = ["filelock (>=3.8.0)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "cattrs" +version = "23.2.3" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"}, + {file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"}, +] + +[package.dependencies] +attrs = ">=23.1.0" +exceptiongroup = {version = ">=1.1.1", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.1.0,<4.6.3 || >4.6.3", markers = "python_version < \"3.11\""} + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +orjson = ["orjson (>=3.9.2)"] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "cleo" +version = "2.1.0" +description = "Cleo allows you to create beautiful and testable command-line interfaces." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "cleo-2.1.0-py3-none-any.whl", hash = "sha256:4a31bd4dd45695a64ee3c4758f583f134267c2bc518d8ae9a29cf237d009b07e"}, + {file = "cleo-2.1.0.tar.gz", hash = "sha256:0b2c880b5d13660a7ea651001fb4acb527696c01f15c9ee650f377aa543fd523"}, +] + +[package.dependencies] +crashtest = ">=0.4.1,<0.5.0" +rapidfuzz = ">=3.0.0,<4.0.0" + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "codespell" +version = "2.2.6" +description = "Codespell" +optional = false +python-versions = ">=3.8" +files = [ + {file = "codespell-2.2.6-py3-none-any.whl", hash = "sha256:9ee9a3e5df0990604013ac2a9f22fa8e57669c827124a2e961fe8a1da4cacc07"}, + {file = "codespell-2.2.6.tar.gz", hash = "sha256:a8c65d8eb3faa03deabab6b3bbe798bea72e1799c7e9e955d57eca4096abcff9"}, +] + +[package.extras] +dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"] +hard-encoding-detection = ["chardet"] +toml = ["tomli"] +types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "comm" +version = "0.2.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "coverage" +version = "7.5.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, + {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, + {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, + {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, + {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, + {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, + {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, + {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, + {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, + {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, + {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, + {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, + {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, + {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "crashtest" +version = "0.4.1" +description = "Manage Python errors with ease" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "crashtest-0.4.1-py3-none-any.whl", hash = "sha256:8d23eac5fa660409f57472e3851dab7ac18aba459a8d19cbbba86d3d5aecd2a5"}, + {file = "crashtest-0.4.1.tar.gz", hash = "sha256:80d7b1f316ebfbd429f648076d6275c877ba30ba48979de4191714a75266f0ce"}, +] + +[[package]] +name = "cryptography" +version = "42.0.5" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "cssselect2" +version = "0.7.0" +description = "CSS selectors for Python ElementTree" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cssselect2-0.7.0-py3-none-any.whl", hash = "sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969"}, + {file = "cssselect2-0.7.0.tar.gz", hash = "sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a"}, +] + +[package.dependencies] +tinycss2 = "*" +webencodings = "*" + +[package.extras] +doc = ["sphinx", "sphinx_rtd_theme"] +test = ["flake8", "isort", "pytest"] + +[[package]] +name = "debugpy" +version = "1.8.1" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, + {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, + {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, + {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, + {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, + {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, + {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, + {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, + {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, + {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, + {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, + {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, + {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, + {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, + {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, + {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, + {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, + {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, + {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, + {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, + {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, + {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "defusedxml" +version = "0.8.0rc2" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=3.6" +files = [ + {file = "defusedxml-0.8.0rc2-py2.py3-none-any.whl", hash = "sha256:1c812964311154c3bf4aaf3bc1443b31ee13530b7f255eaaa062c0553c76103d"}, + {file = "defusedxml-0.8.0rc2.tar.gz", hash = "sha256:138c7d540a78775182206c7c97fe65b246a2f40b29471e1a2f1b0da76e7a3942"}, +] + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "dulwich" +version = "0.21.7" +description = "Python Git Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dulwich-0.21.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d4c0110798099bb7d36a110090f2688050703065448895c4f53ade808d889dd3"}, + {file = "dulwich-0.21.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2bc12697f0918bee324c18836053644035362bb3983dc1b210318f2fed1d7132"}, + {file = "dulwich-0.21.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:471305af74790827fcbafe330fc2e8bdcee4fb56ca1177c8c481b1c8f806c4a4"}, + {file = "dulwich-0.21.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54c9d0e845be26f65f954dff13a1cd3f2b9739820c19064257b8fd7435ab263"}, + {file = "dulwich-0.21.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12d61334a575474e707614f2e93d6ed4cdae9eb47214f9277076d9e5615171d3"}, + {file = "dulwich-0.21.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e274cebaf345f0b1e3b70197f2651de92b652386b68020cfd3bf61bc30f6eaaa"}, + {file = "dulwich-0.21.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:817822f970e196e757ae01281ecbf21369383285b9f4a83496312204cf889b8c"}, + {file = "dulwich-0.21.7-cp310-cp310-win32.whl", hash = "sha256:7836da3f4110ce684dcd53489015fb7fa94ed33c5276e3318b8b1cbcb5b71e08"}, + {file = "dulwich-0.21.7-cp310-cp310-win_amd64.whl", hash = "sha256:4a043b90958cec866b4edc6aef5fe3c2c96a664d0b357e1682a46f6c477273c4"}, + {file = "dulwich-0.21.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ce8db196e79c1f381469410d26fb1d8b89c6b87a4e7f00ff418c22a35121405c"}, + {file = "dulwich-0.21.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:62bfb26bdce869cd40be443dfd93143caea7089b165d2dcc33de40f6ac9d812a"}, + {file = "dulwich-0.21.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c01a735b9a171dcb634a97a3cec1b174cfbfa8e840156870384b633da0460f18"}, + {file = "dulwich-0.21.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa4d14767cf7a49c9231c2e52cb2a3e90d0c83f843eb6a2ca2b5d81d254cf6b9"}, + {file = "dulwich-0.21.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bca4b86e96d6ef18c5bc39828ea349efb5be2f9b1f6ac9863f90589bac1084d"}, + {file = "dulwich-0.21.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a7b5624b02ef808cdc62dabd47eb10cd4ac15e8ac6df9e2e88b6ac6b40133673"}, + {file = "dulwich-0.21.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c3a539b4696a42fbdb7412cb7b66a4d4d332761299d3613d90a642923c7560e1"}, + {file = "dulwich-0.21.7-cp311-cp311-win32.whl", hash = "sha256:675a612ce913081beb0f37b286891e795d905691dfccfb9bf73721dca6757cde"}, + {file = "dulwich-0.21.7-cp311-cp311-win_amd64.whl", hash = "sha256:460ba74bdb19f8d498786ae7776745875059b1178066208c0fd509792d7f7bfc"}, + {file = "dulwich-0.21.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4c51058ec4c0b45dc5189225b9e0c671b96ca9713c1daf71d622c13b0ab07681"}, + {file = "dulwich-0.21.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4bc4c5366eaf26dda3fdffe160a3b515666ed27c2419f1d483da285ac1411de0"}, + {file = "dulwich-0.21.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a0650ec77d89cb947e3e4bbd4841c96f74e52b4650830112c3057a8ca891dc2f"}, + {file = "dulwich-0.21.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f18f0a311fb7734b033a3101292b932158cade54b74d1c44db519e42825e5a2"}, + {file = "dulwich-0.21.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c589468e5c0cd84e97eb7ec209ab005a2cb69399e8c5861c3edfe38989ac3a8"}, + {file = "dulwich-0.21.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d62446797163317a397a10080c6397ffaaca51a7804c0120b334f8165736c56a"}, + {file = "dulwich-0.21.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e84cc606b1f581733df4350ca4070e6a8b30be3662bbb81a590b177d0c996c91"}, + {file = "dulwich-0.21.7-cp312-cp312-win32.whl", hash = "sha256:c3d1685f320907a52c40fd5890627945c51f3a5fa4bcfe10edb24fec79caadec"}, + {file = "dulwich-0.21.7-cp312-cp312-win_amd64.whl", hash = "sha256:6bd69921fdd813b7469a3c77bc75c1783cc1d8d72ab15a406598e5a3ba1a1503"}, + {file = "dulwich-0.21.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d8ab29c660125db52106775caa1f8f7f77a69ed1fe8bc4b42bdf115731a25bf"}, + {file = "dulwich-0.21.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0d2e4485b98695bf95350ce9d38b1bb0aaac2c34ad00a0df789aa33c934469b"}, + {file = "dulwich-0.21.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e138d516baa6b5bafbe8f030eccc544d0d486d6819b82387fc0e285e62ef5261"}, + {file = "dulwich-0.21.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f34bf9b9fa9308376263fd9ac43143c7c09da9bc75037bb75c6c2423a151b92c"}, + {file = "dulwich-0.21.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e2c66888207b71cd1daa2acb06d3984a6bc13787b837397a64117aa9fc5936a"}, + {file = "dulwich-0.21.7-cp37-cp37m-win32.whl", hash = "sha256:10893105c6566fc95bc2a67b61df7cc1e8f9126d02a1df6a8b2b82eb59db8ab9"}, + {file = "dulwich-0.21.7-cp37-cp37m-win_amd64.whl", hash = "sha256:460b3849d5c3d3818a80743b4f7a0094c893c559f678e56a02fff570b49a644a"}, + {file = "dulwich-0.21.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74700e4c7d532877355743336c36f51b414d01e92ba7d304c4f8d9a5946dbc81"}, + {file = "dulwich-0.21.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c92e72c43c9e9e936b01a57167e0ea77d3fd2d82416edf9489faa87278a1cdf7"}, + {file = "dulwich-0.21.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d097e963eb6b9fa53266146471531ad9c6765bf390849230311514546ed64db2"}, + {file = "dulwich-0.21.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:808e8b9cc0aa9ac74870b49db4f9f39a52fb61694573f84b9c0613c928d4caf8"}, + {file = "dulwich-0.21.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1957b65f96e36c301e419d7adaadcff47647c30eb072468901bb683b1000bc5"}, + {file = "dulwich-0.21.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4b09bc3a64fb70132ec14326ecbe6e0555381108caff3496898962c4136a48c6"}, + {file = "dulwich-0.21.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5882e70b74ac3c736a42d3fdd4f5f2e6570637f59ad5d3e684760290b58f041"}, + {file = "dulwich-0.21.7-cp38-cp38-win32.whl", hash = "sha256:29bb5c1d70eba155ded41ed8a62be2f72edbb3c77b08f65b89c03976292f6d1b"}, + {file = "dulwich-0.21.7-cp38-cp38-win_amd64.whl", hash = "sha256:25c3ab8fb2e201ad2031ddd32e4c68b7c03cb34b24a5ff477b7a7dcef86372f5"}, + {file = "dulwich-0.21.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8929c37986c83deb4eb500c766ee28b6670285b512402647ee02a857320e377c"}, + {file = "dulwich-0.21.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc1e11be527ac06316539b57a7688bcb1b6a3e53933bc2f844397bc50734e9ae"}, + {file = "dulwich-0.21.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0fc3078a1ba04c588fabb0969d3530efd5cd1ce2cf248eefb6baf7cbc15fc285"}, + {file = "dulwich-0.21.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dcbd29ba30ba2c5bfbab07a61a5f20095541d5ac66d813056c122244df4ac0"}, + {file = "dulwich-0.21.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8869fc8ec3dda743e03d06d698ad489b3705775fe62825e00fa95aa158097fc0"}, + {file = "dulwich-0.21.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d96ca5e0dde49376fbcb44f10eddb6c30284a87bd03bb577c59bb0a1f63903fa"}, + {file = "dulwich-0.21.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0064363bd5e814359657ae32517fa8001e8573d9d040bd997908d488ab886ed"}, + {file = "dulwich-0.21.7-cp39-cp39-win32.whl", hash = "sha256:869eb7be48243e695673b07905d18b73d1054a85e1f6e298fe63ba2843bb2ca1"}, + {file = "dulwich-0.21.7-cp39-cp39-win_amd64.whl", hash = "sha256:404b8edeb3c3a86c47c0a498699fc064c93fa1f8bab2ffe919e8ab03eafaaad3"}, + {file = "dulwich-0.21.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e598d743c6c0548ebcd2baf94aa9c8bfacb787ea671eeeb5828cfbd7d56b552f"}, + {file = "dulwich-0.21.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a2d76c96426e791556836ef43542b639def81be4f1d6d4322cd886c115eae1"}, + {file = "dulwich-0.21.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c88acb60a1f4d31bd6d13bfba465853b3df940ee4a0f2a3d6c7a0778c705b7"}, + {file = "dulwich-0.21.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ecd315847dea406a4decfa39d388a2521e4e31acde3bd9c2609c989e817c6d62"}, + {file = "dulwich-0.21.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d05d3c781bc74e2c2a2a8f4e4e2ed693540fbe88e6ac36df81deac574a6dad99"}, + {file = "dulwich-0.21.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6de6f8de4a453fdbae8062a6faa652255d22a3d8bce0cd6d2d6701305c75f2b3"}, + {file = "dulwich-0.21.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e25953c7acbbe4e19650d0225af1c0c0e6882f8bddd2056f75c1cc2b109b88ad"}, + {file = "dulwich-0.21.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:4637cbd8ed1012f67e1068aaed19fcc8b649bcf3e9e26649826a303298c89b9d"}, + {file = "dulwich-0.21.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:858842b30ad6486aacaa607d60bab9c9a29e7c59dc2d9cb77ae5a94053878c08"}, + {file = "dulwich-0.21.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739b191f61e1c4ce18ac7d520e7a7cbda00e182c3489552408237200ce8411ad"}, + {file = "dulwich-0.21.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:274c18ec3599a92a9b67abaf110e4f181a4f779ee1aaab9e23a72e89d71b2bd9"}, + {file = "dulwich-0.21.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2590e9b431efa94fc356ae33b38f5e64f1834ec3a94a6ac3a64283b206d07aa3"}, + {file = "dulwich-0.21.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed60d1f610ef6437586f7768254c2a93820ccbd4cfdac7d182cf2d6e615969bb"}, + {file = "dulwich-0.21.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8278835e168dd097089f9e53088c7a69c6ca0841aef580d9603eafe9aea8c358"}, + {file = "dulwich-0.21.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffc27fb063f740712e02b4d2f826aee8bbed737ed799962fef625e2ce56e2d29"}, + {file = "dulwich-0.21.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61e3451bd3d3844f2dca53f131982553be4d1b1e1ebd9db701843dd76c4dba31"}, + {file = "dulwich-0.21.7.tar.gz", hash = "sha256:a9e9c66833cea580c3ac12927e4b9711985d76afca98da971405d414de60e968"}, +] + +[package.dependencies] +urllib3 = ">=1.25" + +[package.extras] +fastimport = ["fastimport"] +https = ["urllib3 (>=1.24.1)"] +paramiko = ["paramiko"] +pgp = ["gpg"] + +[[package]] +name = "ecdsa" +version = "0.19.0" +description = "ECDSA cryptographic signature library (pure python)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.6" +files = [ + {file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"}, + {file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"}, +] + +[package.dependencies] +six = ">=1.9.0" + +[package.extras] +gmpy = ["gmpy"] +gmpy2 = ["gmpy2"] + +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + +[[package]] +name = "fastapi" +version = "0.104.1" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.104.1-py3-none-any.whl", hash = "sha256:752dc31160cdbd0436bb93bad51560b57e525cbb1d4bbf6f4904ceee75548241"}, + {file = "fastapi-0.104.1.tar.gz", hash = "sha256:e5e4540a7c5e1dcfbbcf5b903c234feddcdcd881f191977a1c5dfd917487e7ae"}, +] + +[package.dependencies] +anyio = ">=3.7.1,<4.0.0" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.27.0,<0.28.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastjsonschema" +version = "2.19.1" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, + {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.13.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, + {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "freezegun" +version = "1.5.0" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.7" +files = [ + {file = "freezegun-1.5.0-py3-none-any.whl", hash = "sha256:ec3f4ba030e34eb6cf7e1e257308aee2c60c3d038ff35996d7475760c9ff3719"}, + {file = "freezegun-1.5.0.tar.gz", hash = "sha256:200a64359b363aa3653d8aac289584078386c7c3da77339d257e46a01fb5c77c"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "frozendict" +version = "2.4.2" +description = "A simple immutable dictionary" +optional = false +python-versions = ">=3.6" +files = [ + {file = "frozendict-2.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:19743495b1e92a7e4db56fcd6a5d36ea1d1b0f550822d6fd780e44d58f0b8c18"}, + {file = "frozendict-2.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81efb4ea854a1c93d954a67389eaf78c508acb2d4768321a835cda2754ec5c01"}, + {file = "frozendict-2.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5f1a4d9662b854dce52b560b60f51349905dc871826b8c6be20141a13067a53"}, + {file = "frozendict-2.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1412aeb325e4a28cfe32106c66c046372bb7fd5a9af1748193549c5d01a9e9c1"}, + {file = "frozendict-2.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7ce0535f02eba9746e4e2cf0abef0f0f2051d20fdccf4af31bc3d1adecf5a71"}, + {file = "frozendict-2.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:07153e6d2720fa1131bb180ce388c7042affb29561d8bcd1c0d6e683a8beaea2"}, + {file = "frozendict-2.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f7a90ea6d5248617a1222daef07d22fb146ff07635a36db327e1ce114bf3e304"}, + {file = "frozendict-2.4.2-cp310-cp310-win_arm64.whl", hash = "sha256:20a6f741c92fdeb3766924cde42b8ee445cf568e3be8aa983cb83e9fe5b61e63"}, + {file = "frozendict-2.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:146129502cd9d96de64e0c8f7dc4c66422da3d4bfccf891dd80a3821b358a926"}, + {file = "frozendict-2.4.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ac1f74ccf818977abbc1868090c06436b8f06534d306f808f15cffc304ae046"}, + {file = "frozendict-2.4.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d2ea4f10505ad15f53ce3742420682d916d0c4d566edb8e1019756e7cea30"}, + {file = "frozendict-2.4.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4a5841681e70d2862ca153543f2912e0bab034bf29e2d3610e86ea42506121c2"}, + {file = "frozendict-2.4.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d4a10119f17552cbeab48d4ae830ba091c6d47616589618adc31f251184579a7"}, + {file = "frozendict-2.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:7d13ffe649e9db6f4bb5e107d9be7dfd23e13101bc69f97aa5fa6cbf6aecaadd"}, + {file = "frozendict-2.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19e64630e164a297f83e9a1c69f1cd36fa4b3d1196c1f9fc006a0385aa198ea4"}, + {file = "frozendict-2.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bedb0a6587bae53bd53727b92a87c4cf90ad7a7e0bd2db562d439beb6982712e"}, + {file = "frozendict-2.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cc9d063131fd8adbeb18a473d222b5dc8301cac9505cfe578158f9a9bf55a9"}, + {file = "frozendict-2.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:92c46b155ea9eb9ecabc66ba2d9030f2634319f55c6448688965ece094f14b51"}, + {file = "frozendict-2.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f958d40637e0440bce2453019821c94fe86cfc5f3847ae11cd4f02c3548b1d1b"}, + {file = "frozendict-2.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ac954be447a907face9b652207fbd943b9b552212890db959ba653e8f1dc3f56"}, + {file = "frozendict-2.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7e0ff5e84742604a1b42c2de4f1e67630c0868cf52a5c585b54a99e06f6b453"}, + {file = "frozendict-2.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:84c36bfa819cd8442f6e0bdb86413c7678b2822a46b1a22cfa0f0dd30d9e5c45"}, + {file = "frozendict-2.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cead3bfe70c90c634a9b76807c9d7e75e6c5666ec96fa2cea8e7412ccf22a1f8"}, + {file = "frozendict-2.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fc6e3158107b5431255978b954758b1041cc70a3b8e7657373110512eb528e3"}, + {file = "frozendict-2.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4db1d6cc412bd865cab36723995208b82166a97bc6c724753bcd2b90cf24f164"}, + {file = "frozendict-2.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff6fb5831539fffb09d71cc0cc0462b1f27c0160cb6c6fa2d1f4c1bc7fffe52a"}, + {file = "frozendict-2.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:79e1c94ad2a925ad5723d82a4134c6d851d5a7bc72b7e9da8b2087c42758a512"}, + {file = "frozendict-2.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34704f9ffb21448d4b5c0f9239f8f058c0efab4bfdbe2956c5be978fef0b929c"}, + {file = "frozendict-2.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5280d685cd1659883a3010dec843afe3065416ae92e453498997d4474a898a39"}, + {file = "frozendict-2.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ca09a376114172e4d9918e6d576f58244c45e21f5af1245085699fd3a171c47"}, + {file = "frozendict-2.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55953aa2acf5bf183c664f3d0f540f8c8ac8f5fa97170f2098d413414318eb2b"}, + {file = "frozendict-2.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:476e4857e1d87b05c9102dd5409216ce4716cb7df619e6657429bc99279303cc"}, + {file = "frozendict-2.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4a8b298f39242d25770d029588ce9d4f524e9f4edc60d2d34b6178fb07c8a93e"}, + {file = "frozendict-2.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:c157b8a92743a7905b341edb0663044fecdc7780f96c59a2843d3da68d694b90"}, + {file = "frozendict-2.4.2-cp39-cp39-win_arm64.whl", hash = "sha256:cbab325c0a98b2f3ee291b36710623781b4977a3057f9103a7b0f11bcc23b177"}, + {file = "frozendict-2.4.2.tar.gz", hash = "sha256:741779e1d1a2e6bb2c623f78423bd5d14aad35dc0c57e6ccc89e54eaab5f1b8a"}, +] + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "html5lib" +version = "1.1" +description = "HTML parser based on the WHATWG HTML specification" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, + {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, +] + +[package.dependencies] +six = ">=1.9" +webencodings = "*" + +[package.extras] +all = ["chardet (>=2.2)", "genshi", "lxml"] +chardet = ["chardet (>=2.2)"] +genshi = ["genshi"] +lxml = ["lxml"] + +[[package]] +name = "identify" +version = "2.5.36" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.11.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-6.11.0-py3-none-any.whl", hash = "sha256:f0afba6205ad8f8947c7d338b5342d5db2afbfd82f9cbef7879a9539cc12eb9b"}, + {file = "importlib_metadata-6.11.0.tar.gz", hash = "sha256:1231cf92d825c9e03cfc4da076a16de6422c863558229ea0b22b675657463443"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[package]] +name = "importlib-resources" +version = "6.4.0" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, + {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "installer" +version = "0.7.0" +description = "A library for installing Python wheels." +optional = false +python-versions = ">=3.7" +files = [ + {file = "installer-0.7.0-py3-none-any.whl", hash = "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53"}, + {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"}, +] + +[[package]] +name = "ipykernel" +version = "6.29.4" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.4-py3-none-any.whl", hash = "sha256:1181e653d95c6808039c509ef8e67c4126b3b3af7781496c7cbfb5ed938a27da"}, + {file = "ipykernel-6.29.4.tar.gz", hash = "sha256:3d44070060f9475ac2092b760123fadf105d2e2493c24848b6691a7c4f42af5c"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +tornado = ">=6.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.12.3" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, + {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" +typing-extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +description = "Utility functions for Python class constructs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, + {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jeepney" +version = "0.8.0" +description = "Low-level, pure Python DBus protocol wrapper." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] + +[package.extras] +test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["async_generator", "trio"] + +[[package]] +name = "jsonschema" +version = "4.21.1" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, + {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +jsonschema-specifications = ">=2023.03.6" +pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +referencing = ">=0.31.0" + +[[package]] +name = "jupyter-client" +version = "8.6.1" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, + {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "keyring" +version = "24.3.1" +description = "Store and access your passwords safely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "keyring-24.3.1-py3-none-any.whl", hash = "sha256:df38a4d7419a6a60fea5cef1e45a948a3e8430dd12ad88b0f423c5c143906218"}, + {file = "keyring-24.3.1.tar.gz", hash = "sha256:c3327b6ffafc0e8befbdb597cacdb4928ffe5c1212f7645f186e6d9957a898db"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +importlib-resources = {version = "*", markers = "python_version < \"3.9\""} +"jaraco.classes" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +completion = ["shtab (>=1.1.0)"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[[package]] +name = "lxml" +version = "5.2.1" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=3.6" +files = [ + {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1f7785f4f789fdb522729ae465adcaa099e2a3441519df750ebdccc481d961a1"}, + {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cc6ee342fb7fa2471bd9b6d6fdfc78925a697bf5c2bcd0a302e98b0d35bfad3"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:794f04eec78f1d0e35d9e0c36cbbb22e42d370dda1609fb03bcd7aeb458c6377"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817d420c60a5183953c783b0547d9eb43b7b344a2c46f69513d5952a78cddf3"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2213afee476546a7f37c7a9b4ad4d74b1e112a6fafffc9185d6d21f043128c81"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b070bbe8d3f0f6147689bed981d19bbb33070225373338df755a46893528104a"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e02c5175f63effbd7c5e590399c118d5db6183bbfe8e0d118bdb5c2d1b48d937"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:d7520db34088c96cc0e0a3ad51a4fd5b401f279ee112aa2b7f8f976d8582606d"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:bcbf4af004f98793a95355980764b3d80d47117678118a44a80b721c9913436a"}, + {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2b44bec7adf3e9305ce6cbfa47a4395667e744097faed97abb4728748ba7d47"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1c5bb205e9212d0ebddf946bc07e73fa245c864a5f90f341d11ce7b0b854475d"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2c9d147f754b1b0e723e6afb7ba1566ecb162fe4ea657f53d2139bbf894d050a"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3545039fa4779be2df51d6395e91a810f57122290864918b172d5dc7ca5bb433"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a91481dbcddf1736c98a80b122afa0f7296eeb80b72344d7f45dc9f781551f56"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2ddfe41ddc81f29a4c44c8ce239eda5ade4e7fc305fb7311759dd6229a080052"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7baf9ffc238e4bf401299f50e971a45bfcc10a785522541a6e3179c83eabf0a"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:31e9a882013c2f6bd2f2c974241bf4ba68c85eba943648ce88936d23209a2e01"}, + {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0a15438253b34e6362b2dc41475e7f80de76320f335e70c5528b7148cac253a1"}, + {file = "lxml-5.2.1-cp310-cp310-win32.whl", hash = "sha256:6992030d43b916407c9aa52e9673612ff39a575523c5f4cf72cdef75365709a5"}, + {file = "lxml-5.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:da052e7962ea2d5e5ef5bc0355d55007407087392cf465b7ad84ce5f3e25fe0f"}, + {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:70ac664a48aa64e5e635ae5566f5227f2ab7f66a3990d67566d9907edcbbf867"}, + {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1ae67b4e737cddc96c99461d2f75d218bdf7a0c3d3ad5604d1f5e7464a2f9ffe"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f18a5a84e16886898e51ab4b1d43acb3083c39b14c8caeb3589aabff0ee0b270"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6f2c8372b98208ce609c9e1d707f6918cc118fea4e2c754c9f0812c04ca116d"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:394ed3924d7a01b5bd9a0d9d946136e1c2f7b3dc337196d99e61740ed4bc6fe1"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d077bc40a1fe984e1a9931e801e42959a1e6598edc8a3223b061d30fbd26bbc"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:764b521b75701f60683500d8621841bec41a65eb739b8466000c6fdbc256c240"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3a6b45da02336895da82b9d472cd274b22dc27a5cea1d4b793874eead23dd14f"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:5ea7b6766ac2dfe4bcac8b8595107665a18ef01f8c8343f00710b85096d1b53a"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:e196a4ff48310ba62e53a8e0f97ca2bca83cdd2fe2934d8b5cb0df0a841b193a"}, + {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:200e63525948e325d6a13a76ba2911f927ad399ef64f57898cf7c74e69b71095"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dae0ed02f6b075426accbf6b2863c3d0a7eacc1b41fb40f2251d931e50188dad"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:ab31a88a651039a07a3ae327d68ebdd8bc589b16938c09ef3f32a4b809dc96ef"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:df2e6f546c4df14bc81f9498bbc007fbb87669f1bb707c6138878c46b06f6510"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5dd1537e7cc06efd81371f5d1a992bd5ab156b2b4f88834ca852de4a8ea523fa"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9b9ec9c9978b708d488bec36b9e4c94d88fd12ccac3e62134a9d17ddba910ea9"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8e77c69d5892cb5ba71703c4057091e31ccf534bd7f129307a4d084d90d014b8"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a8d5c70e04aac1eda5c829a26d1f75c6e5286c74743133d9f742cda8e53b9c2f"}, + {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c94e75445b00319c1fad60f3c98b09cd63fe1134a8a953dcd48989ef42318534"}, + {file = "lxml-5.2.1-cp311-cp311-win32.whl", hash = "sha256:4951e4f7a5680a2db62f7f4ab2f84617674d36d2d76a729b9a8be4b59b3659be"}, + {file = "lxml-5.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:5c670c0406bdc845b474b680b9a5456c561c65cf366f8db5a60154088c92d102"}, + {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:abc25c3cab9ec7fcd299b9bcb3b8d4a1231877e425c650fa1c7576c5107ab851"}, + {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6935bbf153f9a965f1e07c2649c0849d29832487c52bb4a5c5066031d8b44fd5"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d793bebb202a6000390a5390078e945bbb49855c29c7e4d56a85901326c3b5d9"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd5562927cdef7c4f5550374acbc117fd4ecc05b5007bdfa57cc5355864e0a4"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e7259016bc4345a31af861fdce942b77c99049d6c2107ca07dc2bba2435c1d9"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:530e7c04f72002d2f334d5257c8a51bf409db0316feee7c87e4385043be136af"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59689a75ba8d7ffca577aefd017d08d659d86ad4585ccc73e43edbfc7476781a"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f9737bf36262046213a28e789cc82d82c6ef19c85a0cf05e75c670a33342ac2c"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:3a74c4f27167cb95c1d4af1c0b59e88b7f3e0182138db2501c353555f7ec57f4"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:68a2610dbe138fa8c5826b3f6d98a7cfc29707b850ddcc3e21910a6fe51f6ca0"}, + {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f0a1bc63a465b6d72569a9bba9f2ef0334c4e03958e043da1920299100bc7c08"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c2d35a1d047efd68027817b32ab1586c1169e60ca02c65d428ae815b593e65d4"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:79bd05260359170f78b181b59ce871673ed01ba048deef4bf49a36ab3e72e80b"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:865bad62df277c04beed9478fe665b9ef63eb28fe026d5dedcb89b537d2e2ea6"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71e97313406ccf55d32cc98a533ee05c61e15d11b99215b237346171c179c0b0"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:057cdc6b86ab732cf361f8b4d8af87cf195a1f6dc5b0ff3de2dced242c2015e0"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f3bbbc998d42f8e561f347e798b85513ba4da324c2b3f9b7969e9c45b10f6169"}, + {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:491755202eb21a5e350dae00c6d9a17247769c64dcf62d8c788b5c135e179dc4"}, + {file = "lxml-5.2.1-cp312-cp312-win32.whl", hash = "sha256:8de8f9d6caa7f25b204fc861718815d41cbcf27ee8f028c89c882a0cf4ae4134"}, + {file = "lxml-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:f2a9efc53d5b714b8df2b4b3e992accf8ce5bbdfe544d74d5c6766c9e1146a3a"}, + {file = "lxml-5.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:70a9768e1b9d79edca17890175ba915654ee1725975d69ab64813dd785a2bd5c"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c38d7b9a690b090de999835f0443d8aa93ce5f2064035dfc48f27f02b4afc3d0"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5670fb70a828663cc37552a2a85bf2ac38475572b0e9b91283dc09efb52c41d1"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:958244ad566c3ffc385f47dddde4145088a0ab893504b54b52c041987a8c1863"}, + {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6241d4eee5f89453307c2f2bfa03b50362052ca0af1efecf9fef9a41a22bb4f"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2a66bf12fbd4666dd023b6f51223aed3d9f3b40fef06ce404cb75bafd3d89536"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:9123716666e25b7b71c4e1789ec829ed18663152008b58544d95b008ed9e21e9"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:0c3f67e2aeda739d1cc0b1102c9a9129f7dc83901226cc24dd72ba275ced4218"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5d5792e9b3fb8d16a19f46aa8208987cfeafe082363ee2745ea8b643d9cc5b45"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:88e22fc0a6684337d25c994381ed8a1580a6f5ebebd5ad41f89f663ff4ec2885"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:21c2e6b09565ba5b45ae161b438e033a86ad1736b8c838c766146eff8ceffff9"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_s390x.whl", hash = "sha256:afbbdb120d1e78d2ba8064a68058001b871154cc57787031b645c9142b937a62"}, + {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:627402ad8dea044dde2eccde4370560a2b750ef894c9578e1d4f8ffd54000461"}, + {file = "lxml-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:e89580a581bf478d8dcb97d9cd011d567768e8bc4095f8557b21c4d4c5fea7d0"}, + {file = "lxml-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:59565f10607c244bc4c05c0c5fa0c190c990996e0c719d05deec7030c2aa8289"}, + {file = "lxml-5.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:857500f88b17a6479202ff5fe5f580fc3404922cd02ab3716197adf1ef628029"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56c22432809085b3f3ae04e6e7bdd36883d7258fcd90e53ba7b2e463efc7a6af"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a55ee573116ba208932e2d1a037cc4b10d2c1cb264ced2184d00b18ce585b2c0"}, + {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:6cf58416653c5901e12624e4013708b6e11142956e7f35e7a83f1ab02f3fe456"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:64c2baa7774bc22dd4474248ba16fe1a7f611c13ac6123408694d4cc93d66dbd"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:74b28c6334cca4dd704e8004cba1955af0b778cf449142e581e404bd211fb619"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7221d49259aa1e5a8f00d3d28b1e0b76031655ca74bb287123ef56c3db92f213"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:04ab5415bf6c86e0518d57240a96c4d1fcfc3cb370bb2ac2a732b67f579e5a04"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:6ab833e4735a7e5533711a6ea2df26459b96f9eec36d23f74cafe03631647c41"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f443cdef978430887ed55112b491f670bba6462cea7a7742ff8f14b7abb98d75"}, + {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:9e2addd2d1866fe112bc6f80117bcc6bc25191c5ed1bfbcf9f1386a884252ae8"}, + {file = "lxml-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:f51969bac61441fd31f028d7b3b45962f3ecebf691a510495e5d2cd8c8092dbd"}, + {file = "lxml-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b0b58fbfa1bf7367dde8a557994e3b1637294be6cf2169810375caf8571a085c"}, + {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5"}, + {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:804f74efe22b6a227306dd890eecc4f8c59ff25ca35f1f14e7482bbce96ef10b"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08802f0c56ed150cc6885ae0788a321b73505d2263ee56dad84d200cab11c07a"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8c09ed18ecb4ebf23e02b8e7a22a05d6411911e6fabef3a36e4f371f4f2585"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3d30321949861404323c50aebeb1943461a67cd51d4200ab02babc58bd06a86"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:b560e3aa4b1d49e0e6c847d72665384db35b2f5d45f8e6a5c0072e0283430533"}, + {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:058a1308914f20784c9f4674036527e7c04f7be6fb60f5d61353545aa7fcb739"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:adfb84ca6b87e06bc6b146dc7da7623395db1e31621c4785ad0658c5028b37d7"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a2dfe7e2473f9b59496247aad6e23b405ddf2e12ef0765677b0081c02d6c2c0b"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bf2e2458345d9bffb0d9ec16557d8858c9c88d2d11fed53998512504cd9df49b"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:58278b29cb89f3e43ff3e0c756abbd1518f3ee6adad9e35b51fb101c1c1daaec"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:64641a6068a16201366476731301441ce93457eb8452056f570133a6ceb15fca"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:78bfa756eab503673991bdcf464917ef7845a964903d3302c5f68417ecdc948c"}, + {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11a04306fcba10cd9637e669fd73aa274c1c09ca64af79c041aa820ea992b637"}, + {file = "lxml-5.2.1-cp38-cp38-win32.whl", hash = "sha256:66bc5eb8a323ed9894f8fa0ee6cb3e3fb2403d99aee635078fd19a8bc7a5a5da"}, + {file = "lxml-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:9676bfc686fa6a3fa10cd4ae6b76cae8be26eb5ec6811d2a325636c460da1806"}, + {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cf22b41fdae514ee2f1691b6c3cdeae666d8b7fa9434de445f12bbeee0cf48dd"}, + {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec42088248c596dbd61d4ae8a5b004f97a4d91a9fd286f632e42e60b706718d7"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd53553ddad4a9c2f1f022756ae64abe16da1feb497edf4d9f87f99ec7cf86bd"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feaa45c0eae424d3e90d78823f3828e7dc42a42f21ed420db98da2c4ecf0a2cb"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddc678fb4c7e30cf830a2b5a8d869538bc55b28d6c68544d09c7d0d8f17694dc"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:853e074d4931dbcba7480d4dcab23d5c56bd9607f92825ab80ee2bd916edea53"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4691d60512798304acb9207987e7b2b7c44627ea88b9d77489bbe3e6cc3bd4"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:beb72935a941965c52990f3a32d7f07ce869fe21c6af8b34bf6a277b33a345d3"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:6588c459c5627fefa30139be4d2e28a2c2a1d0d1c265aad2ba1935a7863a4913"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:588008b8497667f1ddca7c99f2f85ce8511f8f7871b4a06ceede68ab62dff64b"}, + {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6787b643356111dfd4032b5bffe26d2f8331556ecb79e15dacb9275da02866e"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7c17b64b0a6ef4e5affae6a3724010a7a66bda48a62cfe0674dabd46642e8b54"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:27aa20d45c2e0b8cd05da6d4759649170e8dfc4f4e5ef33a34d06f2d79075d57"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d4f2cc7060dc3646632d7f15fe68e2fa98f58e35dd5666cd525f3b35d3fed7f8"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff46d772d5f6f73564979cd77a4fffe55c916a05f3cb70e7c9c0590059fb29ef"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:96323338e6c14e958d775700ec8a88346014a85e5de73ac7967db0367582049b"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:52421b41ac99e9d91934e4d0d0fe7da9f02bfa7536bb4431b4c05c906c8c6919"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7a7efd5b6d3e30d81ec68ab8a88252d7c7c6f13aaa875009fe3097eb4e30b84c"}, + {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ed777c1e8c99b63037b91f9d73a6aad20fd035d77ac84afcc205225f8f41188"}, + {file = "lxml-5.2.1-cp39-cp39-win32.whl", hash = "sha256:644df54d729ef810dcd0f7732e50e5ad1bd0a135278ed8d6bcb06f33b6b6f708"}, + {file = "lxml-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:9ca66b8e90daca431b7ca1408cae085d025326570e57749695d6a01454790e95"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b0ff53900566bc6325ecde9181d89afadc59c5ffa39bddf084aaedfe3b06a11"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd6037392f2d57793ab98d9e26798f44b8b4da2f2464388588f48ac52c489ea1"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9c07e7a45bb64e21df4b6aa623cb8ba214dfb47d2027d90eac197329bb5e94"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3249cc2989d9090eeac5467e50e9ec2d40704fea9ab72f36b034ea34ee65ca98"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f42038016852ae51b4088b2862126535cc4fc85802bfe30dea3500fdfaf1864e"}, + {file = "lxml-5.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:533658f8fbf056b70e434dff7e7aa611bcacb33e01f75de7f821810e48d1bb66"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:622020d4521e22fb371e15f580d153134bfb68d6a429d1342a25f051ec72df1c"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efa7b51824aa0ee957ccd5a741c73e6851de55f40d807f08069eb4c5a26b2baa"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c6ad0fbf105f6bcc9300c00010a2ffa44ea6f555df1a2ad95c88f5656104817"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e233db59c8f76630c512ab4a4daf5a5986da5c3d5b44b8e9fc742f2a24dbd460"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a014510830df1475176466b6087fc0c08b47a36714823e58d8b8d7709132a96"}, + {file = "lxml-5.2.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d38c8f50ecf57f0463399569aa388b232cf1a2ffb8f0a9a5412d0db57e054860"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5aea8212fb823e006b995c4dda533edcf98a893d941f173f6c9506126188860d"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff097ae562e637409b429a7ac958a20aab237a0378c42dabaa1e3abf2f896e5f"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f5d65c39f16717a47c36c756af0fb36144069c4718824b7533f803ecdf91138"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e32be23d538753a8adb6c85bd539f5fd3b15cb987404327c569dfc5fd8366e85"}, + {file = "lxml-5.2.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cc518cea79fd1e2f6c90baafa28906d4309d24f3a63e801d855e7424c5b34144"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a0af35bd8ebf84888373630f73f24e86bf016642fb8576fba49d3d6b560b7cbc"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8aca2e3a72f37bfc7b14ba96d4056244001ddcc18382bd0daa087fd2e68a354"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ca1e8188b26a819387b29c3895c47a5e618708fe6f787f3b1a471de2c4a94d9"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c8ba129e6d3b0136a0f50345b2cb3db53f6bda5dd8c7f5d83fbccba97fb5dcb5"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e998e304036198b4f6914e6a1e2b6f925208a20e2042563d9734881150c6c246"}, + {file = "lxml-5.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d3be9b2076112e51b323bdf6d5a7f8a798de55fb8d95fcb64bd179460cdc0704"}, + {file = "lxml-5.2.1.tar.gz", hash = "sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html-clean = ["lxml-html-clean"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=3.0.10)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "monotonic" +version = "1.6" +description = "An implementation of time.monotonic() for Python 2 & < 3.3" +optional = false +python-versions = "*" +files = [ + {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, + {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, +] + +[[package]] +name = "more-itertools" +version = "10.2.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.8" +files = [ + {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, + {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, +] + +[[package]] +name = "msgpack" +version = "1.0.8" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, + {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, + {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, + {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, + {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, + {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, + {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, + {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, + {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, + {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, + {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, +] + +[[package]] +name = "multidict" +version = "6.0.5" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +] + +[[package]] +name = "multitasking" +version = "0.0.11" +description = "Non-blocking Python methods using decorators" +optional = false +python-versions = "*" +files = [ + {file = "multitasking-0.0.11-py3-none-any.whl", hash = "sha256:1e5b37a5f8fc1e6cfaafd1a82b6b1cc6d2ed20037d3b89c25a84f499bd7b3dd4"}, + {file = "multitasking-0.0.11.tar.gz", hash = "sha256:4d6bc3cc65f9b2dca72fb5a787850a88dae8f620c2b36ae9b55248e51bcd6026"}, +] + +[[package]] +name = "mypy" +version = "1.9.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +description = "The Jupyter Notebook format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, + {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, +] + +[package.dependencies] +fastjsonschema = ">=2.15" +jsonschema = ">=2.6" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +traitlets = ">=5.1" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["pep440", "pre-commit", "pytest", "testpath"] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "numpy" +version = "1.24.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +] + +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "openbb" +version = "4.1.7" +description = "OpenBB" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "openbb-4.1.7-py3-none-any.whl", hash = "sha256:f1711b44587e9f04a875e4205f018e568256933a7e69ecb4a0cd39aac18a4408"}, + {file = "openbb-4.1.7.tar.gz", hash = "sha256:d878e589475057d6c8128b3570a768e58dd4c7c53ed0dbf6af9bca24d9696b98"}, +] + +[package.dependencies] +openbb-benzinga = ">=1.1.5,<2.0.0" +openbb-commodity = ">=1.0.4,<2.0.0" +openbb-core = ">=1.1.6,<2.0.0" +openbb-crypto = ">=1.1.5,<2.0.0" +openbb-currency = ">=1.1.5,<2.0.0" +openbb-derivatives = ">=1.1.5,<2.0.0" +openbb-economy = ">=1.1.5,<2.0.0" +openbb-equity = ">=1.1.5,<2.0.0" +openbb-etf = ">=1.1.5,<2.0.0" +openbb-federal-reserve = ">=1.1.5,<2.0.0" +openbb-fixedincome = ">=1.1.5,<2.0.0" +openbb-fmp = ">=1.1.5,<2.0.0" +openbb-fred = ">=1.1.5,<2.0.0" +openbb-index = ">=1.1.5,<2.0.0" +openbb-intrinio = ">=1.1.5,<2.0.0" +openbb-news = ">=1.1.5,<2.0.0" +openbb-oecd = ">=1.1.5,<2.0.0" +openbb-polygon = ">=1.1.5,<2.0.0" +openbb-regulators = ">=1.1.5,<2.0.0" +openbb-sec = ">=1.1.5,<2.0.0" +openbb-tiingo = ">=1.1.5,<2.0.0" +openbb-tradingeconomics = ">=1.1.5,<2.0.0" +openbb-yfinance = ">=1.1.5,<2.0.0" + +[package.extras] +all = ["openbb-alpha-vantage (>=1.1.5,<2.0.0)", "openbb-biztoc (>=1.1.5,<2.0.0)", "openbb-cboe (>=1.1.5,<2.0.0)", "openbb-charting (>=2.0.3,<3.0.0)", "openbb-ecb (>=1.1.5,<2.0.0)", "openbb-econometrics (>=1.1.5,<2.0.0)", "openbb-finra (>=1.1.5,<2.0.0)", "openbb-finviz (>=1.0.4,<2.0.0)", "openbb-government-us (>=1.1.5,<2.0.0)", "openbb-nasdaq (>=1.1.5,<2.0.0)", "openbb-quantitative (>=1.1.5,<2.0.0)", "openbb-seeking-alpha (>=1.1.5,<2.0.0)", "openbb-stockgrid (>=1.1.5,<2.0.0)", "openbb-technical (>=1.1.6,<2.0.0)", "openbb-tmx (>=1.0.2,<2.0.0)", "openbb-tradier (>=1.0.2,<2.0.0)", "openbb-wsj (>=1.1.5,<2.0.0)"] +alpha-vantage = ["openbb-alpha-vantage (>=1.1.5,<2.0.0)"] +biztoc = ["openbb-biztoc (>=1.1.5,<2.0.0)"] +cboe = ["openbb-cboe (>=1.1.5,<2.0.0)"] +charting = ["openbb-charting (>=2.0.3,<3.0.0)"] +ecb = ["openbb-ecb (>=1.1.5,<2.0.0)"] +econometrics = ["openbb-econometrics (>=1.1.5,<2.0.0)"] +finra = ["openbb-finra (>=1.1.5,<2.0.0)"] +finviz = ["openbb-finviz (>=1.0.4,<2.0.0)"] +government-us = ["openbb-government-us (>=1.1.5,<2.0.0)"] +nasdaq = ["openbb-nasdaq (>=1.1.5,<2.0.0)"] +quantitative = ["openbb-quantitative (>=1.1.5,<2.0.0)"] +seeking-alpha = ["openbb-seeking-alpha (>=1.1.5,<2.0.0)"] +stockgrid = ["openbb-stockgrid (>=1.1.5,<2.0.0)"] +technical = ["openbb-technical (>=1.1.6,<2.0.0)"] +tmx = ["openbb-tmx (>=1.0.2,<2.0.0)"] +tradier = ["openbb-tradier (>=1.0.2,<2.0.0)"] +wsj = ["openbb-wsj (>=1.1.5,<2.0.0)"] + +[[package]] +name = "openbb-benzinga" +version = "1.1.5" +description = "Benzinga extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_benzinga-1.1.5-py3-none-any.whl", hash = "sha256:a0d2082d45fdb834c4cfe0c55b62d7462babed999bd0deb1c419da690aaa8787"}, + {file = "openbb_benzinga-1.1.5.tar.gz", hash = "sha256:b2ce16c51aeffa27142fc8b7b41e4a6ebe92029dc8d9d2cbe885bbf2b3bfcd9b"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-charting" +version = "2.0.3" +description = "Charting extension for OpenBB" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "openbb_charting-2.0.3-py3-none-any.whl", hash = "sha256:a876bf235ce2401fde74b54212780e2eefef81274de365dc060ab9e585f1d299"}, + {file = "openbb_charting-2.0.3.tar.gz", hash = "sha256:a7bce6f8c8b59fce8bb530ae65aeaf90b5585b28e457d11dbef289af48560982"}, +] + +[package.dependencies] +nbformat = ">=5.9.2,<6.0.0" +openbb-core = ">=1.1.6,<2.0.0" +pandas-ta = ">=0.3.14b,<0.4.0" +plotly = ">=5.17.0,<6.0.0" +pywry = ">=0.6.1,<0.7.0" +reportlab = ">=4.0.4,<5.0.0" +scipy = ">=1.10.0,<2.0.0" +statsmodels = ">=0.14.0,<0.15.0" +svglib = ">=1.5.1,<2.0.0" + +[[package]] +name = "openbb-commodity" +version = "1.0.4" +description = "Commodity extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_commodity-1.0.4-py3-none-any.whl", hash = "sha256:9a238e4d1d85bf39838fb6e21b278ab4f7cef70629abd5d9a99511e794451c10"}, + {file = "openbb_commodity-1.0.4.tar.gz", hash = "sha256:aece2e34eaa66c337166b7bd4e7ce3aacf058a61e0c1b71a60cdf5650d24b4d4"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-core" +version = "1.1.6" +description = "OpenBB package with core functionality" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_core-1.1.6-py3-none-any.whl", hash = "sha256:4161d6e7654cab2185fcc39e5172385c8226e3cf6bfd5d4b1ff1076f70a6374c"}, + {file = "openbb_core-1.1.6.tar.gz", hash = "sha256:bf56a8a0dc26d72e679a2c68b1f57bf6703ed130b52348a77a2f6cfa32c9fcae"}, +] + +[package.dependencies] +aiohttp = ">=3.9.0,<4.0.0" +fastapi = ">=0.104.1,<0.105.0" +html5lib = ">=1.1,<2.0" +importlib-metadata = ">=6.8.0,<7.0.0" +pandas = ">=1.5.3" +posthog = ">=3.3.1,<4.0.0" +pydantic = ">=2.5.1,<3.0.0" +python-dotenv = ">=1.0.0,<2.0.0" +python-jose = ">=3.3.0,<4.0.0" +python-multipart = ">=0.0.6,<0.0.7" +requests = ">=2.31.0,<3.0.0" +ruff = ">=0.1.6,<0.2.0" +uuid7 = ">=0.1.0,<0.2.0" +uvicorn = ">=0.24,<0.25" +websockets = ">=12.0,<13.0" + +[[package]] +name = "openbb-crypto" +version = "1.1.5" +description = "Crypto extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_crypto-1.1.5-py3-none-any.whl", hash = "sha256:61ce16b69ebbe8d3a92b8a52ff3194567f3c4294e4613c118fa55c5b99f033cf"}, + {file = "openbb_crypto-1.1.5.tar.gz", hash = "sha256:a92dc2064eefb96d9ade3d2c9bad6db64d8eecf1ce3e68aba8e1fd5c589ddeee"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-currency" +version = "1.1.5" +description = "Currency extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_currency-1.1.5-py3-none-any.whl", hash = "sha256:a481428ac6a056c0c63f31d01ff6a615b3514a8867af9c0ba8bfcf864075e76d"}, + {file = "openbb_currency-1.1.5.tar.gz", hash = "sha256:d0754dbf9a636050d4b69d1fc7c370e9eec6db9165c0baf22ad546e45f59b47f"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-derivatives" +version = "1.1.5" +description = "Derivatives extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_derivatives-1.1.5-py3-none-any.whl", hash = "sha256:4d85985a43967d466572f53620dd83032e391f6d93f0c0433884cf801bd23bf3"}, + {file = "openbb_derivatives-1.1.5.tar.gz", hash = "sha256:db1ff010aa48aedcde7f6e781aa1dcc665c8cd9a239f70eb4f01c922a2bf721b"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-devtools" +version = "1.1.6" +description = "Tools for OpenBB Platform Developers" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "openbb_devtools-1.1.6-py3-none-any.whl", hash = "sha256:677b318aa331fcda43e5b739e9924645ff446d3dda9522a5dc3e7bc0007c7f2e"}, + {file = "openbb_devtools-1.1.6.tar.gz", hash = "sha256:5f899dbf9ebf36e38d7a87b68c983a6cf6fd80f72568ba125ea34ff2a2ac28da"}, +] + +[package.dependencies] +bandit = ">=1.7.5,<2.0.0" +black = ">=24.1.0,<25.0.0" +codespell = ">=2.2.5,<3.0.0" +ipykernel = ">=6.26.0,<7.0.0" +mypy = ">=1.6.1,<2.0.0" +poetry = ">=1.7.0,<2.0.0" +pre-commit = ">=3.5.0,<4.0.0" +pydocstyle = ">=6.3.0,<7.0.0" +pylint = ">=3.0.2,<4.0.0" +pytest = ">=7.4.3,<8.0.0" +pytest-cov = ">=4.1.0,<5.0.0" +ruff = ">=0.1.5,<0.2.0" +tox = ">=4.11.3,<5.0.0" +types-python-dateutil = ">=2.8.19.14,<3.0.0.0" +types-toml = ">=0.10.8.7,<0.11.0.0" + +[[package]] +name = "openbb-economy" +version = "1.1.5" +description = "Economy extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_economy-1.1.5-py3-none-any.whl", hash = "sha256:ee54d3c30418c3b3118c85a757ea927bba582095ba6a91751ee0e77f442a40de"}, + {file = "openbb_economy-1.1.5.tar.gz", hash = "sha256:a336a419d45353e33ec34df4f9952583a3a80daeadd17ce8a070342839e9cec4"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-equity" +version = "1.1.5" +description = "Equity extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_equity-1.1.5-py3-none-any.whl", hash = "sha256:d58109ff7860739ca20889953eca0256a7b2aa54cde3c4a81facb10d7c7d9605"}, + {file = "openbb_equity-1.1.5.tar.gz", hash = "sha256:e8e9ebf0cf660b39620b94baae43ce07d9239fb785269289fff287d4631d58a1"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-etf" +version = "1.1.5" +description = "ETF extension for OpenBB" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "openbb_etf-1.1.5-py3-none-any.whl", hash = "sha256:f11eb17f5a0a4af3b72e0d8a5b42cb6c1a2594d35841d288d0c1b75c87ea99fc"}, + {file = "openbb_etf-1.1.5.tar.gz", hash = "sha256:8195a912827d9390181b97d960d20ad6da3e245b31af881ee01c1bcbc07cd661"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-federal-reserve" +version = "1.1.5" +description = "US Federal Reserve Data Extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_federal_reserve-1.1.5-py3-none-any.whl", hash = "sha256:f55342063c7cdab5df0c96af23e8471b339cfd2cf14c92bd1a11a1fba2dd7254"}, + {file = "openbb_federal_reserve-1.1.5.tar.gz", hash = "sha256:79e3a5ceb38302b552be24108d35d14b0c8b7b529eedd206d4fb4b20d66ec809"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-fixedincome" +version = "1.1.5" +description = "Fixed income extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_fixedincome-1.1.5-py3-none-any.whl", hash = "sha256:84f1bc861891a171c17c861e54698f2d4743eb699220f7a1eaab9882849b0096"}, + {file = "openbb_fixedincome-1.1.5.tar.gz", hash = "sha256:dcb003eb928e0a8a90b5504b4f800e586684c8dfcbf4de17bc5dd82a9291f12b"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-fmp" +version = "1.1.5" +description = "FMP extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_fmp-1.1.5-py3-none-any.whl", hash = "sha256:a41ed60b4f5944810d4d9d44f6ea270a68e94051752a1fe9ebf13c4a694bed77"}, + {file = "openbb_fmp-1.1.5.tar.gz", hash = "sha256:f340d60b0d2709d9371d9765d8c3dfbbce731d6f24ea3f8039e72443c6802b6e"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-fred" +version = "1.1.5" +description = "FRED extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_fred-1.1.5-py3-none-any.whl", hash = "sha256:fa0bb9533719c5d2ed9f6c6d8eb4ce887fd601bb9ca1667c59c0cb78307777e8"}, + {file = "openbb_fred-1.1.5.tar.gz", hash = "sha256:541e913e0e1835beddfad88fffd3afb1a3067fe570dbdeb5f9f1fb731f4e7f95"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-index" +version = "1.1.5" +description = "Index extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_index-1.1.5-py3-none-any.whl", hash = "sha256:d92baa9dd50b87d86797c455db6a1fc6ec335bb44d43f69a08d83535288d1657"}, + {file = "openbb_index-1.1.5.tar.gz", hash = "sha256:022bf419993bb9aa94ac7e7e59088ad947c983222a177132e8e6dd2673a88f8e"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-intrinio" +version = "1.1.5" +description = "Intrinio extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_intrinio-1.1.5-py3-none-any.whl", hash = "sha256:536cdc3da6bf7380bd9a14c374aa675a9abdcdf2ee5801245de7cf353de94987"}, + {file = "openbb_intrinio-1.1.5.tar.gz", hash = "sha256:a8722773248179337d60555c2ac6e7d63a0e59cadf1f7bdc6f53420bef6b864a"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" +requests-cache = ">=1.1.0,<2.0.0" + +[[package]] +name = "openbb-news" +version = "1.1.5" +description = "News extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_news-1.1.5-py3-none-any.whl", hash = "sha256:b5fcc5003d4259af5e0c6e74b9f222b43d9ce4dd6d0274f8d0a89711de772090"}, + {file = "openbb_news-1.1.5.tar.gz", hash = "sha256:baee0789a1b338a86fa4050c4b1b5e8be58d40c9c51d8c268b57e0d46e78c844"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-oecd" +version = "1.1.5" +description = "OECD extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_oecd-1.1.5-py3-none-any.whl", hash = "sha256:47e19ed2591c15b96b501330be3f06c7a82a38160e8befeeb688f64cfd9c5e38"}, + {file = "openbb_oecd-1.1.5.tar.gz", hash = "sha256:4ccffeef6927401bb82f533390a6d9edb39eb18692969c271bae9fe15724ac11"}, +] + +[package.dependencies] +defusedxml = ">=0.8.0rc2,<0.9.0" +openbb-core = ">=1.1.6,<2.0.0" +urllib3 = ">1.26.16" + +[[package]] +name = "openbb-polygon" +version = "1.1.5" +description = "Polygon extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_polygon-1.1.5-py3-none-any.whl", hash = "sha256:ef9f8c719d33eb1bf5285b9ca762648bca215adf86483b55db1133602dc85186"}, + {file = "openbb_polygon-1.1.5.tar.gz", hash = "sha256:7382ca4487bfb7556d3c4ea865139b14c882339c2de723423894acf9b82547f1"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-regulators" +version = "1.1.5" +description = "Markets and Agency Regulators extension for OpenBB" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "openbb_regulators-1.1.5-py3-none-any.whl", hash = "sha256:d1defbd26143a1826182c3bd342f94141a08e2fb8bb8fa43771bacc15ab41355"}, + {file = "openbb_regulators-1.1.5.tar.gz", hash = "sha256:7ba633162feb4c1ad1a3b9f7e6ca4e734aa1ae79cda4b7ab8c75478e7e48d022"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-sec" +version = "1.1.5" +description = "SEC extension for OpenBB" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "openbb_sec-1.1.5-py3-none-any.whl", hash = "sha256:3318e92ad17ad7f661cf5e59ee527064c893f8100658a266ef4a64c48b613c96"}, + {file = "openbb_sec-1.1.5.tar.gz", hash = "sha256:e09eb1945a0ba968baf4b6014efe144d6df80cd11e545133ea32a054ac5699b2"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" +pytest-freezegun = ">=0.4.2,<0.5.0" +requests-cache = ">=1.1.0,<2.0.0" +xmltodict = ">=0.13.0,<0.14.0" + +[[package]] +name = "openbb-tiingo" +version = "1.1.5" +description = "Tiingo extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_tiingo-1.1.5-py3-none-any.whl", hash = "sha256:79bbf35aec3022c2c98c1ec49320dfddfec99dbbde6083d40382e969e96c643e"}, + {file = "openbb_tiingo-1.1.5.tar.gz", hash = "sha256:ff7ef99f80adcb7a9afa0f3368e854e0fe32780088f71d2d067f40efba87e0a7"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-tradingeconomics" +version = "1.1.5" +description = "Trading Economics extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_tradingeconomics-1.1.5-py3-none-any.whl", hash = "sha256:020eed3eb75f8aecb68180a8a53420aeaa435317912bc47ff5aa3e553b00c632"}, + {file = "openbb_tradingeconomics-1.1.5.tar.gz", hash = "sha256:2c3a14cff1f69aa602a625cab18321a5db667c75cc96af0d57f81fc0ba4d5adc"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" + +[[package]] +name = "openbb-yfinance" +version = "1.1.5" +description = "yfinance extension for OpenBB" +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "openbb_yfinance-1.1.5-py3-none-any.whl", hash = "sha256:75faf9734adaa66f761e9830837ee848af92a63f5fd8a891f7620566193f8f4f"}, + {file = "openbb_yfinance-1.1.5.tar.gz", hash = "sha256:430ac4a2ef1e704450b6682da6586d6b370f1aa469a696df314ef911887c78b2"}, +] + +[package.dependencies] +openbb-core = ">=1.1.6,<2.0.0" +yfinance = ">=0.2.27,<0.3.0" + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pandas" +version = "2.0.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, + {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, + {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, + {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, + {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, + {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, + {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, + {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, + {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, + {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.1" + +[package.extras] +all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] +aws = ["s3fs (>=2021.08.0)"] +clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] +compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] +computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] +feather = ["pyarrow (>=7.0.0)"] +fss = ["fsspec (>=2021.07.0)"] +gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] +hdf5 = ["tables (>=3.6.1)"] +html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] +mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] +parquet = ["pyarrow (>=7.0.0)"] +performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] +plot = ["matplotlib (>=3.6.1)"] +postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] +spss = ["pyreadstat (>=1.1.2)"] +sql-other = ["SQLAlchemy (>=1.4.16)"] +test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.6.3)"] + +[[package]] +name = "pandas-ta" +version = "0.3.14b" +description = "An easy to use Python 3 Pandas Extension with 130+ Technical Analysis Indicators. Can be called from a Pandas DataFrame or standalone like TA-Lib. Correlation tested with TA-Lib." +optional = false +python-versions = "*" +files = [ + {file = "pandas_ta-0.3.14b.tar.gz", hash = "sha256:0fa35aec831d2815ea30b871688a8d20a76b288a7be2d26cc00c35cd8c09a993"}, +] + +[package.dependencies] +pandas = "*" + +[package.extras] +dev = ["alphaVantage-api", "matplotlib", "mplfinance", "scipy", "sklearn", "statsmodels", "stochastic", "talib", "tqdm", "vectorbt", "yfinance"] +test = ["ta-lib"] + +[[package]] +name = "parso" +version = "0.8.4" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "patsy" +version = "0.5.6" +description = "A Python package for describing statistical models and for building design matrices." +optional = false +python-versions = "*" +files = [ + {file = "patsy-0.5.6-py2.py3-none-any.whl", hash = "sha256:19056886fd8fa71863fa32f0eb090267f21fb74be00f19f5c70b2e9d76c883c6"}, + {file = "patsy-0.5.6.tar.gz", hash = "sha256:95c6d47a7222535f84bff7f63d7303f2e297747a598db89cf5c67f0c0c7d2cdb"}, +] + +[package.dependencies] +numpy = ">=1.4" +six = "*" + +[package.extras] +test = ["pytest", "pytest-cov", "scipy"] + +[[package]] +name = "pbr" +version = "6.0.0" +description = "Python Build Reasonableness" +optional = false +python-versions = ">=2.6" +files = [ + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, +] + +[[package]] +name = "peewee" +version = "3.17.3" +description = "a little orm" +optional = false +python-versions = "*" +files = [ + {file = "peewee-3.17.3.tar.gz", hash = "sha256:ef15f90b628e41a584be8306cdc3243c51f73ce88b06154d9572f6d0284a0169"}, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +optional = false +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + +[[package]] +name = "pillow" +version = "10.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "pkginfo" +version = "1.10.0" +description = "Query metadata from sdists / bdists / installed packages." +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkginfo-1.10.0-py3-none-any.whl", hash = "sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097"}, + {file = "pkginfo-1.10.0.tar.gz", hash = "sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297"}, +] + +[package.extras] +testing = ["pytest", "pytest-cov", "wheel"] + +[[package]] +name = "pkgutil-resolve-name" +version = "1.3.10" +description = "Resolve a name to an object." +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, + {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "plotly" +version = "5.21.0" +description = "An open-source, interactive data visualization library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "plotly-5.21.0-py3-none-any.whl", hash = "sha256:a33f41fd5922e45b2b253f795b200d14452eb625790bb72d0a72cf1328a6abbf"}, + {file = "plotly-5.21.0.tar.gz", hash = "sha256:69243f8c165d4be26c0df1c6f0b7b258e2dfeefe032763404ad7e7fb7d7c2073"}, +] + +[package.dependencies] +packaging = "*" +tenacity = ">=6.2.0" + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "poetry" +version = "1.8.2" +description = "Python dependency management and packaging made easy." +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "poetry-1.8.2-py3-none-any.whl", hash = "sha256:b42b400d9a803af6e788a30a6f3e9998020b77860e28df20647eb10b6f414910"}, + {file = "poetry-1.8.2.tar.gz", hash = "sha256:49cceb3838104647c3e1021f3a4f13c6053704cc18d33f849a90fe687a29cb73"}, +] + +[package.dependencies] +build = ">=1.0.3,<2.0.0" +cachecontrol = {version = ">=0.14.0,<0.15.0", extras = ["filecache"]} +cleo = ">=2.1.0,<3.0.0" +crashtest = ">=0.4.1,<0.5.0" +dulwich = ">=0.21.2,<0.22.0" +fastjsonschema = ">=2.18.0,<3.0.0" +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} +installer = ">=0.7.0,<0.8.0" +keyring = ">=24.0.0,<25.0.0" +packaging = ">=23.1" +pexpect = ">=4.7.0,<5.0.0" +pkginfo = ">=1.9.4,<2.0.0" +platformdirs = ">=3.0.0,<5" +poetry-core = "1.9.0" +poetry-plugin-export = ">=1.6.0,<2.0.0" +pyproject-hooks = ">=1.0.0,<2.0.0" +requests = ">=2.26,<3.0" +requests-toolbelt = ">=1.0.0,<2.0.0" +shellingham = ">=1.5,<2.0" +tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.11.4,<1.0.0" +trove-classifiers = ">=2022.5.19" +virtualenv = ">=20.23.0,<21.0.0" +xattr = {version = ">=1.0.0,<2.0.0", markers = "sys_platform == \"darwin\""} + +[[package]] +name = "poetry-core" +version = "1.9.0" +description = "Poetry PEP 517 Build Backend" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "poetry_core-1.9.0-py3-none-any.whl", hash = "sha256:4e0c9c6ad8cf89956f03b308736d84ea6ddb44089d16f2adc94050108ec1f5a1"}, + {file = "poetry_core-1.9.0.tar.gz", hash = "sha256:fa7a4001eae8aa572ee84f35feb510b321bd652e5cf9293249d62853e1f935a2"}, +] + +[[package]] +name = "poetry-plugin-export" +version = "1.7.1" +description = "Poetry plugin to export the dependencies to various formats" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "poetry_plugin_export-1.7.1-py3-none-any.whl", hash = "sha256:b2258e53ae0d369a73806f957ed0e726eb95c571a0ce8b1f273da686528cc1da"}, + {file = "poetry_plugin_export-1.7.1.tar.gz", hash = "sha256:cf62cfb6218a904290ba6db3bc1a24aa076d10f81c48c6e48b2ded430131e22e"}, +] + +[package.dependencies] +poetry = ">=1.8.0,<2.0.0" +poetry-core = ">=1.7.0,<2.0.0" + +[[package]] +name = "posthog" +version = "3.5.0" +description = "Integrate PostHog into any python application." +optional = false +python-versions = "*" +files = [ + {file = "posthog-3.5.0-py2.py3-none-any.whl", hash = "sha256:3c672be7ba6f95d555ea207d4486c171d06657eb34b3ce25eb043bfe7b6b5b76"}, + {file = "posthog-3.5.0.tar.gz", hash = "sha256:8f7e3b2c6e8714d0c0c542a2109b83a7549f63b7113a133ab2763a89245ef2ef"}, +] + +[package.dependencies] +backoff = ">=1.10.0" +monotonic = ">=1.5" +python-dateutil = ">2.1" +requests = ">=2.7,<3.0" +six = ">=1.5" + +[package.extras] +dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"] +sentry = ["django", "sentry-sdk"] +test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] + +[[package]] +name = "pre-commit" +version = "3.5.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "pyasn1" +version = "0.6.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, + {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, +] + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "2.7.1" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, + {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.2" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, + {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, + {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, + {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, + {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, + {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, + {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, + {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, + {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, + {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, + {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, + {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, + {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, + {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pylint" +version = "3.1.0" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, + {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, +] + +[package.dependencies] +astroid = ">=3.1.0,<=3.2.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pyproject-api" +version = "1.6.1" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, + {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, +] + +[package.dependencies] +packaging = ">=23.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] + +[[package]] +name = "pyproject-hooks" +version = "1.0.0" +description = "Wrappers to call pyproject.toml-based build backend hooks." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyproject_hooks-1.0.0-py3-none-any.whl", hash = "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8"}, + {file = "pyproject_hooks-1.0.0.tar.gz", hash = "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5"}, +] + +[package.dependencies] +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-freezegun" +version = "0.4.2" +description = "Wrap tests with fixtures in freeze_time" +optional = false +python-versions = "*" +files = [ + {file = "pytest-freezegun-0.4.2.zip", hash = "sha256:19c82d5633751bf3ec92caa481fb5cffaac1787bd485f0df6436fd6242176949"}, + {file = "pytest_freezegun-0.4.2-py2.py3-none-any.whl", hash = "sha256:5318a6bfb8ba4b709c8471c94d0033113877b3ee02da5bfcd917c1889cde99a7"}, +] + +[package.dependencies] +freezegun = ">0.3" +pytest = ">=3.0.0" + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-i18n" +version = "0.3.9" +description = "Translation library for Python" +optional = false +python-versions = "*" +files = [ + {file = "python-i18n-0.3.9.tar.gz", hash = "sha256:df97f3d2364bf3a7ebfbd6cbefe8e45483468e52a9e30b909c6078f5f471e4e8"}, + {file = "python_i18n-0.3.9-py3-none-any.whl", hash = "sha256:bda5b8d889ebd51973e22e53746417bd32783c9bd6780fd27cadbb733915651d"}, +] + +[package.extras] +yaml = ["pyyaml (>=3.10)"] + +[[package]] +name = "python-jose" +version = "3.3.0" +description = "JOSE implementation in Python" +optional = false +python-versions = "*" +files = [ + {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, + {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, +] + +[package.dependencies] +ecdsa = "!=0.15" +pyasn1 = "*" +rsa = "*" + +[package.extras] +cryptography = ["cryptography (>=3.4.0)"] +pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.6" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python_multipart-0.0.6-py3-none-any.whl", hash = "sha256:ee698bab5ef148b0a760751c261902cd096e57e10558e11aca17646b74ee1c18"}, + {file = "python_multipart-0.0.6.tar.gz", hash = "sha256:e9925a80bb668529f1b67c7fdb0a5dacdd7cbfc6fb0bff3ea443fe22bdd62132"}, +] + +[package.extras] +dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatch", "invoke (==1.7.3)", "more-itertools (==4.3.0)", "pbr (==4.3.0)", "pluggy (==1.0.0)", "py (==1.11.0)", "pytest (==7.2.0)", "pytest-cov (==4.0.0)", "pytest-timeout (==2.1.0)", "pyyaml (==5.1)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.2" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, + {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, +] + +[[package]] +name = "pywry" +version = "0.6.2" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pywry-0.6.2-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:45d6bb827bf76b2532a9d70b539209d70f37dfb13e9862549b7bff8500ad2495"}, + {file = "pywry-0.6.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:1d9ffd826a3a08c132843340e6d896efb7b972b301d045e3239a7dc08d9cac2f"}, + {file = "pywry-0.6.2-py3-none-win_amd64.whl", hash = "sha256:4f0e5b502555ee8b8e799baeaebe63243a84b7ce51df01a1c439dbc4e8227b9e"}, + {file = "pywry-0.6.2.tar.gz", hash = "sha256:9bd88c36ab0860728d9e64360010f8abcede43645656030e4a63e69e81a98c95"}, +] + +[package.dependencies] +setproctitle = "*" + +[package.extras] +dev = ["auditwheel", "wheel"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "pyzmq" +version = "26.0.2" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyzmq-26.0.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:1a60a03b01e8c9c58932ec0cca15b1712d911c2800eb82d4281bc1ae5b6dad50"}, + {file = "pyzmq-26.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:949067079e14ea1973bd740255e0840118c163d4bce8837f539d749f145cf5c3"}, + {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37e7edfa6cf96d036a403775c96afa25058d1bb940a79786a9a2fc94a783abe3"}, + {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:903cc7a84a7d4326b43755c368780800e035aa3d711deae84a533fdffa8755b0"}, + {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cb2e41af165e5f327d06fbdd79a42a4e930267fade4e9f92d17f3ccce03f3a7"}, + {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:55353b8189adcfc4c125fc4ce59d477744118e9c0ec379dd0999c5fa120ac4f5"}, + {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f961423ff6236a752ced80057a20e623044df95924ed1009f844cde8b3a595f9"}, + {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ba77fe84fe4f5f3dc0ef681a6d366685c8ffe1c8439c1d7530997b05ac06a04b"}, + {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:52589f0a745ef61b9c75c872cf91f8c1f7c0668eb3dd99d7abd639d8c0fb9ca7"}, + {file = "pyzmq-26.0.2-cp310-cp310-win32.whl", hash = "sha256:b7b6d2a46c7afe2ad03ec8faf9967090c8ceae85c4d8934d17d7cae6f9062b64"}, + {file = "pyzmq-26.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:86531e20de249d9204cc6d8b13d5a30537748c78820215161d8a3b9ea58ca111"}, + {file = "pyzmq-26.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:f26a05029ecd2bd306b941ff8cb80f7620b7901421052bc429d238305b1cbf2f"}, + {file = "pyzmq-26.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:70770e296a9cb03d955540c99360aab861cbb3cba29516abbd106a15dbd91268"}, + {file = "pyzmq-26.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2740fd7161b39e178554ebf21aa5667a1c9ef0cd2cb74298fd4ef017dae7aec4"}, + {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3706c32dea077faa42b1c92d825b7f86c866f72532d342e0be5e64d14d858"}, + {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fa1416876194927f7723d6b7171b95e1115602967fc6bfccbc0d2d51d8ebae1"}, + {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ef9a79a48794099c57dc2df00340b5d47c5caa1792f9ddb8c7a26b1280bd575"}, + {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1c60fcdfa3229aeee4291c5d60faed3a813b18bdadb86299c4bf49e8e51e8605"}, + {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e943c39c206b04df2eb5d71305761d7c3ca75fd49452115ea92db1b5b98dbdef"}, + {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8da0ed8a598693731c76659880a668f4748b59158f26ed283a93f7f04d47447e"}, + {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7bf51970b11d67096bede97cdbad0f4333f7664f4708b9b2acb352bf4faa3140"}, + {file = "pyzmq-26.0.2-cp311-cp311-win32.whl", hash = "sha256:6f8e6bd5d066be605faa9fe5ec10aa1a46ad9f18fc8646f2b9aaefc8fb575742"}, + {file = "pyzmq-26.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:6d03da3a0ae691b361edcb39530075461202f699ce05adbb15055a0e1c9bcaa4"}, + {file = "pyzmq-26.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f84e33321b68ff00b60e9dbd1a483e31ab6022c577c8de525b8e771bd274ce68"}, + {file = "pyzmq-26.0.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:44c33ebd1c62a01db7fbc24e18bdda569d6639217d13d5929e986a2b0f69070d"}, + {file = "pyzmq-26.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ac04f904b4fce4afea9cdccbb78e24d468cb610a839d5a698853e14e2a3f9ecf"}, + {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2133de5ba9adc5f481884ccb699eac9ce789708292945c05746880f95b241c0"}, + {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7753c67c570d7fc80c2dc59b90ca1196f1224e0e2e29a548980c95fe0fe27fc1"}, + {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d4e51632e6b12e65e8d9d7612446ecda2eda637a868afa7bce16270194650dd"}, + {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d6c38806f6ecd0acf3104b8d7e76a206bcf56dadd6ce03720d2fa9d9157d5718"}, + {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:48f496bbe14686b51cec15406323ae6942851e14022efd7fc0e2ecd092c5982c"}, + {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e84a3161149c75bb7a7dc8646384186c34033e286a67fec1ad1bdedea165e7f4"}, + {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dabf796c67aa9f5a4fcc956d47f0d48b5c1ed288d628cf53aa1cf08e88654343"}, + {file = "pyzmq-26.0.2-cp312-cp312-win32.whl", hash = "sha256:3eee4c676af1b109f708d80ef0cf57ecb8aaa5900d1edaf90406aea7e0e20e37"}, + {file = "pyzmq-26.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:26721fec65846b3e4450dad050d67d31b017f97e67f7e0647b5f98aa47f828cf"}, + {file = "pyzmq-26.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:653955c6c233e90de128a1b8e882abc7216f41f44218056bd519969c8c413a15"}, + {file = "pyzmq-26.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:becd8d8fb068fbb5a52096efd83a2d8e54354383f691781f53a4c26aee944542"}, + {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7a15e5465e7083c12517209c9dd24722b25e9b63c49a563922922fc03554eb35"}, + {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e8158ac8616941f874841f9fa0f6d2f1466178c2ff91ea08353fdc19de0d40c2"}, + {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c6a53e28c7066ea7db86fcc0b71d78d01b818bb11d4a4341ec35059885295"}, + {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bdbc7dab0b0e9c62c97b732899c4242e3282ba803bad668e03650b59b165466e"}, + {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e74b6d5ef57bb65bf1b4a37453d8d86d88550dde3fb0f23b1f1a24e60c70af5b"}, + {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ed4c6ee624ecbc77b18aeeb07bf0700d26571ab95b8f723f0d02e056b5bce438"}, + {file = "pyzmq-26.0.2-cp37-cp37m-win32.whl", hash = "sha256:8a98b3cb0484b83c19d8fb5524c8a469cd9f10e743f5904ac285d92678ee761f"}, + {file = "pyzmq-26.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:aa5f95d71b6eca9cec28aa0a2f8310ea53dea313b63db74932879ff860c1fb8d"}, + {file = "pyzmq-26.0.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:5ff56c76ce77b9805378a7a73032c17cbdb1a5b84faa1df03c5d3e306e5616df"}, + {file = "pyzmq-26.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bab697fc1574fee4b81da955678708567c43c813c84c91074e452bda5346c921"}, + {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0c0fed8aa9ba0488ee1cbdaa304deea92d52fab43d373297002cfcc69c0a20c5"}, + {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:606b922699fcec472ed814dda4dc3ff7c748254e0b26762a0ba21a726eb1c107"}, + {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f0fd82bad4d199fa993fbf0ac586a7ac5879addbe436a35a389df7e0eb4c91"}, + {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:166c5e41045939a52c01e6f374e493d9a6a45dfe677360d3e7026e38c42e8906"}, + {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d566e859e8b8d5bca08467c093061774924b3d78a5ba290e82735b2569edc84b"}, + {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:264ee0e72b72ca59279dc320deab5ae0fac0d97881aed1875ce4bde2e56ffde0"}, + {file = "pyzmq-26.0.2-cp38-cp38-win32.whl", hash = "sha256:3152bbd3a4744cbdd83dfb210ed701838b8b0c9065cef14671d6d91df12197d0"}, + {file = "pyzmq-26.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:bf77601d75ca692c179154b7e5943c286a4aaffec02c491afe05e60493ce95f2"}, + {file = "pyzmq-26.0.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:c770a7545b3deca2db185b59175e710a820dd4ed43619f4c02e90b0e227c6252"}, + {file = "pyzmq-26.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d47175f0a380bfd051726bc5c0054036ae4a5d8caf922c62c8a172ccd95c1a2a"}, + {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9bce298c1ce077837e110367c321285dc4246b531cde1abfc27e4a5bbe2bed4d"}, + {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c40b09b7e184d6e3e1be1c8af2cc320c0f9f610d8a5df3dd866e6e6e4e32b235"}, + {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d420d856bf728713874cefb911398efe69e1577835851dd297a308a78c14c249"}, + {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d792d3cab987058451e55c70c5926e93e2ceb68ca5a2334863bb903eb860c9cb"}, + {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:83ec17729cf6d3464dab98a11e98294fcd50e6b17eaabd3d841515c23f6dbd3a"}, + {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47c17d5ebfa88ae90f08960c97b49917098665b8cd8be31f2c24e177bcf37a0f"}, + {file = "pyzmq-26.0.2-cp39-cp39-win32.whl", hash = "sha256:d509685d1cd1d018705a811c5f9d5bc237790936ead6d06f6558b77e16cc7235"}, + {file = "pyzmq-26.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:c7cc8cc009e8f6989a6d86c96f87dae5f5fb07d6c96916cdc7719d546152c7db"}, + {file = "pyzmq-26.0.2-cp39-cp39-win_arm64.whl", hash = "sha256:3ada31cb879cd7532f4a85b501f4255c747d4813ab76b35c49ed510ce4865b45"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0a6ceaddc830dd3ca86cb8451cf373d1f05215368e11834538c2902ed5205139"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a967681463aa7a99eb9a62bb18229b653b45c10ff0947b31cc0837a83dfb86f"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6472a73bc115bc40a2076609a90894775abe6faf19a78375675a2f889a613071"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d6aea92bcccfe5e5524d3c70a6f16ffdae548390ddad26f4207d55c55a40593"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e025f6351e49d48a5aa2f5a09293aa769b0ee7369c25bed551647234b7fa0c75"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:40bd7ebe4dbb37d27f0c56e2a844f360239343a99be422085e13e97da13f73f9"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1dd40d586ad6f53764104df6e01810fe1b4e88fd353774629a5e6fe253813f79"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f2aca15e9ad8c8657b5b3d7ae3d1724dc8c1c1059c06b4b674c3aa36305f4930"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:450ec234736732eb0ebeffdb95a352450d4592f12c3e087e2a9183386d22c8bf"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f43be2bebbd09360a2f23af83b243dc25ffe7b583ea8c722e6df03e03a55f02f"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:867f55e54aff254940bcec5eec068e7c0ac1e6bf360ab91479394a8bf356b0e6"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b4dbc033c5ad46f8c429bf238c25a889b8c1d86bfe23a74e1031a991cb3f0000"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6e8dd2961462e337e21092ec2da0c69d814dcb1b6e892955a37444a425e9cfb8"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35391e72df6c14a09b697c7b94384947c1dd326aca883ff98ff137acdf586c33"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:1c3d3c92fa54eda94ab369ca5b8d35059987c326ba5e55326eb068862f64b1fc"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e7aa61a9cc4f0523373e31fc9255bf4567185a099f85ca3598e64de484da3ab2"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee53a8191271f144cc20b12c19daa9f1546adc84a2f33839e3338039b55c373c"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac60a980f07fa988983f7bfe6404ef3f1e4303f5288a01713bc1266df6d18783"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88896b1b4817d7b2fe1ec7205c4bbe07bf5d92fb249bf2d226ddea8761996068"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:18dfffe23751edee917764ffa133d5d3fef28dfd1cf3adebef8c90bc854c74c4"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6926dd14cfe6967d3322640b6d5c3c3039db71716a5e43cca6e3b474e73e0b36"}, + {file = "pyzmq-26.0.2.tar.gz", hash = "sha256:f0f9bb370449158359bb72a3e12c658327670c0ffe6fbcd1af083152b64f9df0"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "rapidfuzz" +version = "3.8.1" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rapidfuzz-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1b176f01490b48337183da5b4223005bc0c2354a4faee5118917d2fba0bedc1c"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0798e32304b8009d215026bf7e1c448f1831da0a03987b7de30059a41bee92f3"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad4dbd06c1f579eb043b2dcfc635bc6c9fb858240a70f0abd3bed84d8ac79994"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6ec696a268e8d730b42711537e500f7397afc06125c0e8fa9c8211386d315a5"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8a007fdc5cf646e48e361a39eabe725b93af7673c5ab90294e551cae72ff58"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68b185a0397aebe78bcc5d0e1efd96509d4e2f3c4a05996e5c843732f547e9ef"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:267ff42370e031195e3020fff075420c136b69dc918ecb5542ec75c1e36af81f"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:987cd277d27d14301019fdf61c17524f6127f5d364be5482228726049d8e0d10"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bc5a1ec3bd05b55d3070d557c0cdd4412272d51b4966c79aa3e9da207bd33d65"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa223c73c59cc45c12eaa9c439318084003beced0447ff92b578a890288e19eb"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d4276c7ee061db0bac54846933b40339f60085523675f917f37de24a4b3ce0ee"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2ba0e43e9a94d256a704a674c7010e6f8ef9225edf7287cf3e7f66c9894b06cd"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c22b32a57ab47afb207e8fe4bd7bb58c90f9291a63723cafd4e704742166e368"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-win32.whl", hash = "sha256:50db3867864422bf6a6435ea65b9ac9de71ef52ed1e05d62f498cd430189eece"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:bca5acf77508d1822023a85118c2dd8d3c16abdd56d2762359a46deb14daa5e0"}, + {file = "rapidfuzz-3.8.1-cp310-cp310-win_arm64.whl", hash = "sha256:c763d99cf087e7b2c5be0cf34ae9a0e1b031f5057d2341a0a0ed782458645b7e"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:30c282612b7ebf2d7646ebebfd98dd308c582246a94d576734e4b0162f57baf4"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c6a43446f0cd8ff347b1fbb918dc0d657bebf484ddfa960ee069e422a477428"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4969fe0eb179aedacee53ca8f8f1be3c655964a6d62db30f247fee444b9c52b4"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:799f5f221d639d1c2ed8a2348d1edf5e22aa489b58b2cc99f5bf0c1917e2d0f2"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e62bde7d5df3312acc528786ee801c472cae5078b1f1e42761c853ba7fe1072a"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ea3d2e41d8fac71cb63ee72f75bee0ed1e9c50709d4c58587f15437761c1858"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f34a541895627c2bc9ef7757f16f02428a08d960d33208adfb96b33338d0945"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0643a25937fafe8d117f2907606e9940cd1cc905c66f16ece9ab93128299994"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:63044a7b6791a2e945dce9d812a6886e93159deb0464984eb403617ded257f08"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bbc15985c5658691f637a6b97651771147744edfad2a4be56b8a06755e3932fa"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:48b6e5a337a814aec7c6dda5d6460f947c9330860615301f35b519e16dde3c77"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:8c40da44ca20235cda05751d6e828b6b348e7a7c5de2922fa0f9c63f564fd675"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c21d5c7cfa6078c79897e5e482a7e84ff927143d2f3fb020dd6edd27f5469574"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-win32.whl", hash = "sha256:209bb712c448cdec4def6260b9f059bd4681ec61a01568f5e70e37bfe9efe830"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:6f7641992de44ec2ca54102422be44a8e3fb75b9690ccd74fff72b9ac7fc00ee"}, + {file = "rapidfuzz-3.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:c458085e067c766112f089f78ce39eab2b69ba027d7bbb11d067a0b085774367"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1905d9319a97bed29f21584ca641190dbc9218a556202b77876f1e37618d2e03"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f176867f438ff2a43e6a837930153ca78fddb3ca94e378603a1e7b860d7869bf"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25498650e30122f4a5ad6b27c7614b4af8628c1d32b19d406410d33f77a86c80"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16153a97efacadbd693ccc612a3285df2f072fd07c121f30c2c135a709537075"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c0264d03dcee1bb975975b77c2fe041820fb4d4a25a99e3cb74ddd083d671ca"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17d79398849c1244f646425cf31d856eab9ebd67b7d6571273e53df724ca817e"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e08b01dc9369941a24d7e512b0d81bf514e7d6add1b93d8aeec3c8fa08a824e"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97c13f156f14f10667e1cfc4257069b775440ce005e896c09ce3aff21c9ae665"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8b76abfec195bf1ee6f9ec56c33ba5e9615ff2d0a9530a54001ed87e5a6ced3b"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b0ba20be465566264fa5580d874ccf5eabba6975dba45857e2c76e2df3359c6d"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4d5cd86aca3f12e73bfc70015db7e8fc44122da03aa3761138b95112e83f66e4"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:9a16ef3702cecf16056c5fd66398b7ea8622ff4e3afeb00a8db3e74427e850af"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:392582aa784737d95255ca122ebe7dca3c774da900d100c07b53d32cd221a60e"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-win32.whl", hash = "sha256:ceb10039e7346927cec47eaa490b34abb602b537e738ee9914bb41b8de029fbc"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:cc4af7090a626c902c48db9b5d786c1faa0d8e141571e8a63a5350419ea575bd"}, + {file = "rapidfuzz-3.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:3aff3b829b0b04bdf78bd780ec9faf5f26eac3591df98c35a0ae216c925ae436"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78a0d2a11bb3936463609777c6d6d4984a27ebb2360b58339c699899d85db036"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f8af980695b866255447703bf634551e67e1a4e1c2d2d26501858d9233d886d7"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d1a15fef1938b43468002f2d81012dbc9e7b50eb8533af202b0559c2dc7865d9"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4dbb1ebc9a811f38da33f32ed2bb5f58b149289b89eb11e384519e9ba7ca881"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41219536634bd6f85419f38450ef080cfb519638125d805cf8626443e677dc61"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e3f882110f2f4894942e314451773c47e8b1b4920b5ea2b6dd2e2d4079dd3135"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c754ce1fab41b731259f100d5d46529a38aa2c9b683c92aeb7e96ef5b2898cd8"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:718ea99f84b16c4bdbf6a93e53552cdccefa18e12ff9a02c5041e621460e2e61"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9441aca94b21f7349cdb231cd0ce9ca251b2355836e8a02bf6ccbea5b442d7a9"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90167a48de3ed7f062058826608a80242b8561d0fb0cce2c610d741624811a61"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8e02425bfc7ebed617323a674974b70eaecd8f07b64a7d16e0bf3e766b93e3c9"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:d48657a404fab82b2754faa813a10c5ad6aa594cb1829dca168a49438b61b4ec"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f8b62fdccc429e6643cefffd5df9c7bca65588d06e8925b78014ad9ad983bf5"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-win32.whl", hash = "sha256:63db612bb6da1bb9f6aa7412739f0e714b1910ec07bc675943044fe683ef192c"}, + {file = "rapidfuzz-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:bb571dbd4cc93342be0ba632f0b8d7de4cbd9d959d76371d33716d2216090d41"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b27cea618601ca5032ea98ee116ca6e0fe67be7b286bcb0b9f956d64db697472"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d5592b08e3cadc9e06ef3af6a9d66b6ef1bf871ed5acd7f9b1e162d78806a65"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:58999b21d01dd353f49511a61937eac20c7a5b22eab87612063947081855d85f"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ee3909f611cc5860cc8d9f92d039fd84241ce7360b49ea88e657181d2b45f6"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00b5ee47b387fa3805f4038362a085ec58149135dc5bc640ca315a9893a16f9e"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4c647795c5b901091a68e210c76b769af70a33a8624ac496ac3e34d33366c0d"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:77ea62879932b32aba77ab23a9296390a67d024bf2f048dee99143be80a4ce26"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fee62ae76e3b8b9fff8aa2ca4061575ee358927ffbdb2919a8c84a98da59f78"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:231dc1cb63b1c8dd78c0597aa3ad3749a86a2b7e76af295dd81609522699a558"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:827ddf2d5d157ac3d1001b52e84c9e20366237a742946599ffc435af7fdd26d0"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c04ef83c9ca3162d200df36e933b3ea0327a2626cee2e01bbe55acbc004ce261"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:747265f39978bbaad356f5c6b6c808f0e8f5e8994875af0119b82b4700c55387"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:14791324f0c753f5a0918df1249b91515f5ddc16281fbaa5ec48bff8fa659229"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-win32.whl", hash = "sha256:b7b9cbc60e3eb08da6d18636c62c6eb6206cd9d0c7ad73996f7a1df3fc415b27"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:2084193fd8fd346db496a2220363437eb9370a06d1d5a7a9dba00a64390c6a28"}, + {file = "rapidfuzz-3.8.1-cp39-cp39-win_arm64.whl", hash = "sha256:c9597a05d08e8103ad59ebdf29e3fbffb0d0dbf3b641f102cfbeadc3a77bde51"}, + {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5f4174079dfe8ed1f13ece9bde7660f19f98ab17e0c0d002d90cc845c3a7e238"}, + {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07d7d4a3c49a15146d65f06e44d7545628ca0437c929684e32ef122852f44d95"}, + {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ef119fc127c982053fb9ec638dcc3277f83b034b5972eb05941984b9ec4a290"}, + {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e57f9c2367706a320b78e91f8bf9a3b03bf9069464eb7b54455fa340d03e4c"}, + {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6d4f1956fe1fc618e34ac79a6ed84fff5a6f23e41a8a476dd3e8570f0b12f02b"}, + {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:313bdcd16e9cd5e5568b4a31d18a631f0b04cc10a3fd916e4ef75b713e6f177e"}, + {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a02def2eb526cc934d2125533cf2f15aa71c72ed4397afca38427ab047901e88"}, + {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9d5d924970b07128c61c08eebee718686f4bd9838ef712a50468169520c953f"}, + {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1edafc0a2737df277d3ddf401f3a73f76e246b7502762c94a3916453ae67e9b1"}, + {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:81fd28389bedab28251f0535b3c034b0e63a618efc3ff1d338c81a3da723adb3"}, + {file = "rapidfuzz-3.8.1.tar.gz", hash = "sha256:a357aae6791118011ad3ab4f2a4aa7bd7a487e5f9981b390e9f3c2c5137ecadf"}, +] + +[package.extras] +full = ["numpy"] + +[[package]] +name = "referencing" +version = "0.35.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.0-py3-none-any.whl", hash = "sha256:8080727b30e364e5783152903672df9b6b091c926a146a759080b62ca3126cd6"}, + {file = "referencing-0.35.0.tar.gz", hash = "sha256:191e936b0c696d0af17ad7430a3dc68e88bc11be6514f4757dc890f04ab05889"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "reportlab" +version = "4.2.0" +description = "The Reportlab Toolkit" +optional = false +python-versions = "<4,>=3.7" +files = [ + {file = "reportlab-4.2.0-py3-none-any.whl", hash = "sha256:53630f9d25a7938def3e6a93d723b72a7a5921d34d23cf7a0930adeb2cb0e6c1"}, + {file = "reportlab-4.2.0.tar.gz", hash = "sha256:474fb28d63431a5d47d75c90d580393050df7d491a09c7877df3291a2e9f6d0a"}, +] + +[package.dependencies] +chardet = "*" +pillow = ">=9.0.0" + +[package.extras] +accel = ["rl-accel (>=0.9.0,<1.1)"] +pycairo = ["freetype-py (>=2.3.0,<2.4)", "rlPyCairo (>=0.2.0,<1)"] +renderpm = ["rl-renderPM (>=4.0.3,<4.1)"] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-cache" +version = "1.2.0" +description = "A persistent cache for python requests" +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests_cache-1.2.0-py3-none-any.whl", hash = "sha256:490324301bf0cb924ff4e6324bd2613453e7e1f847353928b08adb0fdfb7f722"}, + {file = "requests_cache-1.2.0.tar.gz", hash = "sha256:db1c709ca343cc1cd5b6c8b1a5387298eceed02306a6040760db538c885e3838"}, +] + +[package.dependencies] +attrs = ">=21.2" +cattrs = ">=22.2" +platformdirs = ">=2.5" +requests = ">=2.22" +url-normalize = ">=1.4" +urllib3 = ">=1.25.5" + +[package.extras] +all = ["boto3 (>=1.15)", "botocore (>=1.18)", "itsdangerous (>=2.0)", "pymongo (>=3)", "pyyaml (>=6.0.1)", "redis (>=3)", "ujson (>=5.4)"] +bson = ["bson (>=0.5)"] +docs = ["furo (>=2023.3,<2024.0)", "linkify-it-py (>=2.0,<3.0)", "myst-parser (>=1.0,<2.0)", "sphinx (>=5.0.2,<6.0.0)", "sphinx-autodoc-typehints (>=1.19)", "sphinx-automodapi (>=0.14)", "sphinx-copybutton (>=0.5)", "sphinx-design (>=0.2)", "sphinx-notfound-page (>=0.8)", "sphinxcontrib-apidoc (>=0.3)", "sphinxext-opengraph (>=0.9)"] +dynamodb = ["boto3 (>=1.15)", "botocore (>=1.18)"] +json = ["ujson (>=5.4)"] +mongodb = ["pymongo (>=3)"] +redis = ["redis (>=3)"] +security = ["itsdangerous (>=2.0)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "rpds-py" +version = "0.18.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, +] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "ruff" +version = "0.1.15" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"}, + {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"}, + {file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"}, + {file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"}, + {file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"}, + {file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"}, + {file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"}, + {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"}, +] + +[[package]] +name = "scipy" +version = "1.10.1" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = "<3.12,>=3.8" +files = [ + {file = "scipy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7354fd7527a4b0377ce55f286805b34e8c54b91be865bac273f527e1b839019"}, + {file = "scipy-1.10.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4b3f429188c66603a1a5c549fb414e4d3bdc2a24792e061ffbd607d3d75fd84e"}, + {file = "scipy-1.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1553b5dcddd64ba9a0d95355e63fe6c3fc303a8fd77c7bc91e77d61363f7433f"}, + {file = "scipy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c0ff64b06b10e35215abce517252b375e580a6125fd5fdf6421b98efbefb2d2"}, + {file = "scipy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:fae8a7b898c42dffe3f7361c40d5952b6bf32d10c4569098d276b4c547905ee1"}, + {file = "scipy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f1564ea217e82c1bbe75ddf7285ba0709ecd503f048cb1236ae9995f64217bd"}, + {file = "scipy-1.10.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d925fa1c81b772882aa55bcc10bf88324dadb66ff85d548c71515f6689c6dac5"}, + {file = "scipy-1.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaea0a6be54462ec027de54fca511540980d1e9eea68b2d5c1dbfe084797be35"}, + {file = "scipy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15a35c4242ec5f292c3dd364a7c71a61be87a3d4ddcc693372813c0b73c9af1d"}, + {file = "scipy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:43b8e0bcb877faf0abfb613d51026cd5cc78918e9530e375727bf0625c82788f"}, + {file = "scipy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5678f88c68ea866ed9ebe3a989091088553ba12c6090244fdae3e467b1139c35"}, + {file = "scipy-1.10.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:39becb03541f9e58243f4197584286e339029e8908c46f7221abeea4b749fa88"}, + {file = "scipy-1.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bce5869c8d68cf383ce240e44c1d9ae7c06078a9396df68ce88a1230f93a30c1"}, + {file = "scipy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07c3457ce0b3ad5124f98a86533106b643dd811dd61b548e78cf4c8786652f6f"}, + {file = "scipy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:049a8bbf0ad95277ffba9b3b7d23e5369cc39e66406d60422c8cfef40ccc8415"}, + {file = "scipy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd9f1027ff30d90618914a64ca9b1a77a431159df0e2a195d8a9e8a04c78abf9"}, + {file = "scipy-1.10.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:79c8e5a6c6ffaf3a2262ef1be1e108a035cf4f05c14df56057b64acc5bebffb6"}, + {file = "scipy-1.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51af417a000d2dbe1ec6c372dfe688e041a7084da4fdd350aeb139bd3fb55353"}, + {file = "scipy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b4735d6c28aad3cdcf52117e0e91d6b39acd4272f3f5cd9907c24ee931ad601"}, + {file = "scipy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ff7f37b1bf4417baca958d254e8e2875d0cc23aaadbe65b3d5b3077b0eb23ea"}, + {file = "scipy-1.10.1.tar.gz", hash = "sha256:2cf9dfb80a7b4589ba4c40ce7588986d6d5cebc5457cad2c2880f6bc2d42f3a5"}, +] + +[package.dependencies] +numpy = ">=1.19.5,<1.27.0" + +[package.extras] +dev = ["click", "doit (>=0.36.0)", "flake8", "mypy", "pycodestyle", "pydevtool", "rich-click", "typing_extensions"] +doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] +test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "secretstorage" +version = "3.3.3" +description = "Python bindings to FreeDesktop.org Secret Service API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, + {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "setproctitle" +version = "1.3.3" +description = "A Python module to customize the process title" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:897a73208da48db41e687225f355ce993167079eda1260ba5e13c4e53be7f754"}, + {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c331e91a14ba4076f88c29c777ad6b58639530ed5b24b5564b5ed2fd7a95452"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbbd6c7de0771c84b4aa30e70b409565eb1fc13627a723ca6be774ed6b9d9fa3"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c05ac48ef16ee013b8a326c63e4610e2430dbec037ec5c5b58fcced550382b74"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1342f4fdb37f89d3e3c1c0a59d6ddbedbde838fff5c51178a7982993d238fe4f"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc74e84fdfa96821580fb5e9c0b0777c1c4779434ce16d3d62a9c4d8c710df39"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9617b676b95adb412bb69645d5b077d664b6882bb0d37bfdafbbb1b999568d85"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6a249415f5bb88b5e9e8c4db47f609e0bf0e20a75e8d744ea787f3092ba1f2d0"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:38da436a0aaace9add67b999eb6abe4b84397edf4a78ec28f264e5b4c9d53cd5"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:da0d57edd4c95bf221b2ebbaa061e65b1788f1544977288bdf95831b6e44e44d"}, + {file = "setproctitle-1.3.3-cp310-cp310-win32.whl", hash = "sha256:a1fcac43918b836ace25f69b1dca8c9395253ad8152b625064415b1d2f9be4fb"}, + {file = "setproctitle-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:200620c3b15388d7f3f97e0ae26599c0c378fdf07ae9ac5a13616e933cbd2086"}, + {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334f7ed39895d692f753a443102dd5fed180c571eb6a48b2a5b7f5b3564908c8"}, + {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:950f6476d56ff7817a8fed4ab207727fc5260af83481b2a4b125f32844df513a"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:195c961f54a09eb2acabbfc90c413955cf16c6e2f8caa2adbf2237d1019c7dd8"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f05e66746bf9fe6a3397ec246fe481096664a9c97eb3fea6004735a4daf867fd"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5901a31012a40ec913265b64e48c2a4059278d9f4e6be628441482dd13fb8b5"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64286f8a995f2cd934082b398fc63fca7d5ffe31f0e27e75b3ca6b4efda4e353"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:184239903bbc6b813b1a8fc86394dc6ca7d20e2ebe6f69f716bec301e4b0199d"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:664698ae0013f986118064b6676d7dcd28fefd0d7d5a5ae9497cbc10cba48fa5"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e5119a211c2e98ff18b9908ba62a3bd0e3fabb02a29277a7232a6fb4b2560aa0"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:417de6b2e214e837827067048f61841f5d7fc27926f2e43954567094051aff18"}, + {file = "setproctitle-1.3.3-cp311-cp311-win32.whl", hash = "sha256:6a143b31d758296dc2f440175f6c8e0b5301ced3b0f477b84ca43cdcf7f2f476"}, + {file = "setproctitle-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a680d62c399fa4b44899094027ec9a1bdaf6f31c650e44183b50d4c4d0ccc085"}, + {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d4460795a8a7a391e3567b902ec5bdf6c60a47d791c3b1d27080fc203d11c9dc"}, + {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bdfd7254745bb737ca1384dee57e6523651892f0ea2a7344490e9caefcc35e64"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477d3da48e216d7fc04bddab67b0dcde633e19f484a146fd2a34bb0e9dbb4a1e"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab2900d111e93aff5df9fddc64cf51ca4ef2c9f98702ce26524f1acc5a786ae7"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088b9efc62d5aa5d6edf6cba1cf0c81f4488b5ce1c0342a8b67ae39d64001120"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6d50252377db62d6a0bb82cc898089916457f2db2041e1d03ce7fadd4a07381"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:87e668f9561fd3a457ba189edfc9e37709261287b52293c115ae3487a24b92f6"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:287490eb90e7a0ddd22e74c89a92cc922389daa95babc833c08cf80c84c4df0a"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe1c49486109f72d502f8be569972e27f385fe632bd8895f4730df3c87d5ac8"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4a6ba2494a6449b1f477bd3e67935c2b7b0274f2f6dcd0f7c6aceae10c6c6ba3"}, + {file = "setproctitle-1.3.3-cp312-cp312-win32.whl", hash = "sha256:2df2b67e4b1d7498632e18c56722851ba4db5d6a0c91aaf0fd395111e51cdcf4"}, + {file = "setproctitle-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f38d48abc121263f3b62943f84cbaede05749047e428409c2c199664feb6abc7"}, + {file = "setproctitle-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:816330675e3504ae4d9a2185c46b573105d2310c20b19ea2b4596a9460a4f674"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f960bc22d8d8e4ac886d1e2e21ccbd283adcf3c43136161c1ba0fa509088e0"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e6e7adff74796ef12753ff399491b8827f84f6c77659d71bd0b35870a17d8f"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53bc0d2358507596c22b02db079618451f3bd720755d88e3cccd840bafb4c41c"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6d20f9541f5f6ac63df553b6d7a04f313947f550eab6a61aa758b45f0d5657"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c1c84beab776b0becaa368254801e57692ed749d935469ac10e2b9b825dbdd8e"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:507e8dc2891021350eaea40a44ddd887c9f006e6b599af8d64a505c0f718f170"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b1067647ac7aba0b44b591936118a22847bda3c507b0a42d74272256a7a798e9"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e71f6365744bf53714e8bd2522b3c9c1d83f52ffa6324bd7cbb4da707312cd8"}, + {file = "setproctitle-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:7f1d36a1e15a46e8ede4e953abb104fdbc0845a266ec0e99cc0492a4364f8c44"}, + {file = "setproctitle-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9a402881ec269d0cc9c354b149fc29f9ec1a1939a777f1c858cdb09c7a261df"}, + {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ff814dea1e5c492a4980e3e7d094286077054e7ea116cbeda138819db194b2cd"}, + {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:accb66d7b3ccb00d5cd11d8c6e07055a4568a24c95cf86109894dcc0c134cc89"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554eae5a5b28f02705b83a230e9d163d645c9a08914c0ad921df363a07cf39b1"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a911b26264dbe9e8066c7531c0591cfab27b464459c74385b276fe487ca91c12"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2982efe7640c4835f7355fdb4da313ad37fb3b40f5c69069912f8048f77b28c8"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3f4274b80709d8bcab2f9a862973d453b308b97a0b423a501bcd93582852e3"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:af2c67ae4c795d1674a8d3ac1988676fa306bcfa1e23fddb5e0bd5f5635309ca"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af4061f67fd7ec01624c5e3c21f6b7af2ef0e6bab7fbb43f209e6506c9ce0092"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37a62cbe16d4c6294e84670b59cf7adcc73faafe6af07f8cb9adaf1f0e775b19"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a83ca086fbb017f0d87f240a8f9bbcf0809f3b754ee01cec928fff926542c450"}, + {file = "setproctitle-1.3.3-cp38-cp38-win32.whl", hash = "sha256:059f4ce86f8cc92e5860abfc43a1dceb21137b26a02373618d88f6b4b86ba9b2"}, + {file = "setproctitle-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ab92e51cd4a218208efee4c6d37db7368fdf182f6e7ff148fb295ecddf264287"}, + {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c7951820b77abe03d88b114b998867c0f99da03859e5ab2623d94690848d3e45"}, + {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc94cf128676e8fac6503b37763adb378e2b6be1249d207630f83fc325d9b11"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f5d9027eeda64d353cf21a3ceb74bb1760bd534526c9214e19f052424b37e42"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e4a8104db15d3462e29d9946f26bed817a5b1d7a47eabca2d9dc2b995991503"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c32c41ace41f344d317399efff4cffb133e709cec2ef09c99e7a13e9f3b9483c"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf16381c7bf7f963b58fb4daaa65684e10966ee14d26f5cc90f07049bfd8c1e"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e18b7bd0898398cc97ce2dfc83bb192a13a087ef6b2d5a8a36460311cb09e775"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69d565d20efe527bd8a9b92e7f299ae5e73b6c0470f3719bd66f3cd821e0d5bd"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ddedd300cd690a3b06e7eac90ed4452348b1348635777ce23d460d913b5b63c3"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:415bfcfd01d1fbf5cbd75004599ef167a533395955305f42220a585f64036081"}, + {file = "setproctitle-1.3.3-cp39-cp39-win32.whl", hash = "sha256:21112fcd2195d48f25760f0eafa7a76510871bbb3b750219310cf88b04456ae3"}, + {file = "setproctitle-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:5a740f05d0968a5a17da3d676ce6afefebeeeb5ce137510901bf6306ba8ee002"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6b9e62ddb3db4b5205c0321dd69a406d8af9ee1693529d144e86bd43bcb4b6c0"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3b99b338598de0bd6b2643bf8c343cf5ff70db3627af3ca427a5e1a1a90dd9"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ae9a02766dad331deb06855fb7a6ca15daea333b3967e214de12cfae8f0ef5"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:200ede6fd11233085ba9b764eb055a2a191fb4ffb950c68675ac53c874c22e20"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d3a953c50776751e80fe755a380a64cb14d61e8762bd43041ab3f8cc436092f"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e08e232b78ba3ac6bc0d23ce9e2bee8fad2be391b7e2da834fc9a45129eb87"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1da82c3e11284da4fcbf54957dafbf0655d2389cd3d54e4eaba636faf6d117a"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:aeaa71fb9568ebe9b911ddb490c644fbd2006e8c940f21cb9a1e9425bd709574"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:59335d000c6250c35989394661eb6287187854e94ac79ea22315469ee4f4c244"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3ba57029c9c50ecaf0c92bb127224cc2ea9fda057b5d99d3f348c9ec2855ad3"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d876d355c53d975c2ef9c4f2487c8f83dad6aeaaee1b6571453cb0ee992f55f6"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:224602f0939e6fb9d5dd881be1229d485f3257b540f8a900d4271a2c2aa4e5f4"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d7f27e0268af2d7503386e0e6be87fb9b6657afd96f5726b733837121146750d"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5e7266498cd31a4572378c61920af9f6b4676a73c299fce8ba93afd694f8ae7"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33c5609ad51cd99d388e55651b19148ea99727516132fb44680e1f28dd0d1de9"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:eae8988e78192fd1a3245a6f4f382390b61bce6cfcc93f3809726e4c885fa68d"}, + {file = "setproctitle-1.3.3.tar.gz", hash = "sha256:c913e151e7ea01567837ff037a23ca8740192880198b7fbb90b16d181607caae"}, +] + +[package.extras] +test = ["pytest"] + +[[package]] +name = "setuptools" +version = "69.5.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, + {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + +[[package]] +name = "starlette" +version = "0.27.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.7" +files = [ + {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, + {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] + +[[package]] +name = "statsmodels" +version = "0.14.1" +description = "Statistical computations and models for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "statsmodels-0.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43af9c0b07c9d72f275cf14ea54a481a3f20911f0b443181be4769def258fdeb"}, + {file = "statsmodels-0.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a16975ab6ad505d837ba9aee11f92a8c5b49c4fa1ff45b60fe23780b19e5705e"}, + {file = "statsmodels-0.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e278fe74da5ed5e06c11a30851eda1af08ef5af6be8507c2c45d2e08f7550dde"}, + {file = "statsmodels-0.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0564d92cb05b219b4538ed09e77d96658a924a691255e1f7dd23ee338df441b"}, + {file = "statsmodels-0.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5385e22e72159a09c099c4fb975f350a9f3afeb57c1efce273b89dcf1fe44c0f"}, + {file = "statsmodels-0.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:0a8aae75a2e08ebd990e5fa394f8e32738b55785cb70798449a3f4207085e667"}, + {file = "statsmodels-0.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b69a63ad6c979a6e4cde11870ffa727c76a318c225a7e509f031fbbdfb4e416a"}, + {file = "statsmodels-0.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7562cb18a90a114f39fab6f1c25b9c7b39d9cd5f433d0044b430ca9d44a8b52c"}, + {file = "statsmodels-0.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3abaca4b963259a2bf349c7609cfbb0ce64ad5fb3d92d6f08e21453e4890248"}, + {file = "statsmodels-0.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f727fe697f6406d5f677b67211abe5a55101896abdfacdb3f38410405f6ad8"}, + {file = "statsmodels-0.14.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6838ac6bdb286daabb5e91af90fd4258f09d0cec9aace78cc441cb2b17df428"}, + {file = "statsmodels-0.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:709bfcef2dbe66f705b17e56d1021abad02243ee1a5d1efdb90f9bad8b06a329"}, + {file = "statsmodels-0.14.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f32a7cd424cf33304a54daee39d32cccf1d0265e652c920adeaeedff6d576457"}, + {file = "statsmodels-0.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f8c30181c084173d662aaf0531867667be2ff1bee103b84feb64f149f792dbd2"}, + {file = "statsmodels-0.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de2b97413913d52ad6342dece2d653e77f78620013b7705fad291d4e4266ccb"}, + {file = "statsmodels-0.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3420f88289c593ba2bca33619023059c476674c160733bd7d858564787c83d3"}, + {file = "statsmodels-0.14.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c008e16096f24f0514e53907890ccac6589a16ad6c81c218f2ee6752fdada555"}, + {file = "statsmodels-0.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:bc0351d279c4e080f0ce638a3d886d312aa29eade96042e3ba0a73771b1abdfb"}, + {file = "statsmodels-0.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf293ada63b2859d95210165ad1dfcd97bd7b994a5266d6fbeb23659d8f0bf68"}, + {file = "statsmodels-0.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44ca8cb88fa3d3a4ffaff1fb8eb0e98bbf83fc936fcd9b9eedee258ecc76696a"}, + {file = "statsmodels-0.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d5373d176239993c095b00d06036690a50309a4e00c2da553b65b840f956ae6"}, + {file = "statsmodels-0.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a532dfe899f8b6632cd8caa0b089b403415618f51e840d1817a1e4b97e200c73"}, + {file = "statsmodels-0.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:4fe0a60695952b82139ae8750952786a700292f9e0551d572d7685070944487b"}, + {file = "statsmodels-0.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:04293890f153ffe577e60a227bd43babd5f6c1fc50ea56a3ab1862ae85247a95"}, + {file = "statsmodels-0.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e70a2e93d54d40b2cb6426072acbc04f35501b1ea2569f6786964adde6ca572"}, + {file = "statsmodels-0.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab3a73d16c0569adbba181ebb967e5baaa74935f6d2efe86ac6fc5857449b07d"}, + {file = "statsmodels-0.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eefa5bcff335440ee93e28745eab63559a20cd34eea0375c66d96b016de909b3"}, + {file = "statsmodels-0.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:bc43765710099ca6a942b5ffa1bac7668965052542ba793dd072d26c83453572"}, + {file = "statsmodels-0.14.1.tar.gz", hash = "sha256:2260efdc1ef89f39c670a0bd8151b1d0843567781bcafec6cda0534eb47a94f6"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.18,<2", markers = "python_version != \"3.10\" or platform_system != \"Windows\" or platform_python_implementation == \"PyPy\""}, + {version = ">=1.22.3,<2", markers = "python_version == \"3.10\" and platform_system == \"Windows\" and platform_python_implementation != \"PyPy\""}, +] +packaging = ">=21.3" +pandas = ">=1.0,<2.1.0 || >2.1.0" +patsy = ">=0.5.4" +scipy = ">=1.4,<1.9.2 || >1.9.2" + +[package.extras] +build = ["cython (>=0.29.33)"] +develop = ["colorama", "cython (>=0.29.33)", "cython (>=0.29.33,<4.0.0)", "flake8", "isort", "joblib", "matplotlib (>=3)", "oldest-supported-numpy (>=2022.4.18)", "pytest (>=7.3.0)", "pytest-cov", "pytest-randomly", "pytest-xdist", "pywinpty", "setuptools-scm[toml] (>=8.0,<9.0)"] +docs = ["ipykernel", "jupyter-client", "matplotlib", "nbconvert", "nbformat", "numpydoc", "pandas-datareader", "sphinx"] + +[[package]] +name = "stevedore" +version = "5.2.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, + {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, +] + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +name = "svglib" +version = "1.5.1" +description = "A pure-Python library for reading and converting SVG" +optional = false +python-versions = ">=3.7" +files = [ + {file = "svglib-1.5.1.tar.gz", hash = "sha256:3ae765d3a9409ee60c0fb4d24c2deb6a80617aa927054f5bcd7fc98f0695e587"}, +] + +[package.dependencies] +cssselect2 = ">=0.2.0" +lxml = "*" +reportlab = "*" +tinycss2 = ">=0.6.0" + +[[package]] +name = "tenacity" +version = "8.2.3" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, + {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, +] + +[package.extras] +doc = ["reno", "sphinx", "tornado (>=4.5)"] + +[[package]] +name = "tinycss2" +version = "1.3.0" +description = "A tiny CSS parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, + {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, +] + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["sphinx", "sphinx_rtd_theme"] +test = ["pytest", "ruff"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "tox" +version = "4.14.2" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tox-4.14.2-py3-none-any.whl", hash = "sha256:2900c4eb7b716af4a928a7fdc2ed248ad6575294ed7cfae2ea41203937422847"}, + {file = "tox-4.14.2.tar.gz", hash = "sha256:0defb44f6dafd911b61788325741cc6b2e12ea71f987ac025ad4d649f1f1a104"}, +] + +[package.dependencies] +cachetools = ">=5.3.2" +chardet = ">=5.2" +colorama = ">=0.4.6" +filelock = ">=3.13.1" +packaging = ">=23.2" +platformdirs = ">=4.1" +pluggy = ">=1.3" +pyproject-api = ">=1.6.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.25" + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] +testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"] + +[[package]] +name = "traitlets" +version = "5.14.3" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "trove-classifiers" +version = "2024.4.10" +description = "Canonical source for classifiers on PyPI (pypi.org)." +optional = false +python-versions = "*" +files = [ + {file = "trove-classifiers-2024.4.10.tar.gz", hash = "sha256:49f40bb6a746b72a1cba4f8d55ee8252169cda0f70802e3fd24f04b7fb25a492"}, + {file = "trove_classifiers-2024.4.10-py3-none-any.whl", hash = "sha256:678bd6fcc5218d72e3304e27a608acc9b91e17bd00c3f3d8c968497c843ad98b"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20240316" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, + {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +description = "Typing stubs for toml" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"}, + {file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"}, +] + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "url-normalize" +version = "1.4.3" +description = "URL normalization for Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "url-normalize-1.4.3.tar.gz", hash = "sha256:d23d3a070ac52a67b83a1c59a0e68f8608d1cd538783b401bc9de2c0fac999b2"}, + {file = "url_normalize-1.4.3-py2.py3-none-any.whl", hash = "sha256:ec3c301f04e5bb676d333a7fa162fa977ad2ca04b7e652bfc9fac4e405728eed"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uuid7" +version = "0.1.0" +description = "UUID version 7, generating time-sorted UUIDs with 200ns time resolution and 48 bits of randomness" +optional = false +python-versions = ">=3.7" +files = [ + {file = "uuid7-0.1.0-py2.py3-none-any.whl", hash = "sha256:5e259bb63c8cb4aded5927ff41b444a80d0c7124e8a0ced7cf44efa1f5cccf61"}, + {file = "uuid7-0.1.0.tar.gz", hash = "sha256:8c57aa32ee7456d3cc68c95c4530bc571646defac01895cfc73545449894a63c"}, +] + +[[package]] +name = "uvicorn" +version = "0.24.0.post1" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.24.0.post1-py3-none-any.whl", hash = "sha256:7c84fea70c619d4a710153482c0d230929af7bcf76c7bfa6de151f0a3a80121e"}, + {file = "uvicorn-0.24.0.post1.tar.gz", hash = "sha256:09c8e5a79dc466bdf28dead50093957db184de356fcdc48697bad3bde4c2588e"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "virtualenv" +version = "20.26.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.0-py3-none-any.whl", hash = "sha256:0846377ea76e818daaa3e00a4365c018bc3ac9760cbb3544de542885aad61fb3"}, + {file = "virtualenv-20.26.0.tar.gz", hash = "sha256:ec25a9671a5102c8d2657f62792a27b48f016664c6873f6beed3800008577210"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[[package]] +name = "xattr" +version = "1.1.0" +description = "Python wrapper for extended filesystem attributes" +optional = false +python-versions = ">=3.8" +files = [ + {file = "xattr-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef2fa0f85458736178fd3dcfeb09c3cf423f0843313e25391db2cfd1acec8888"}, + {file = "xattr-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ccab735d0632fe71f7d72e72adf886f45c18b7787430467ce0070207882cfe25"}, + {file = "xattr-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9013f290387f1ac90bccbb1926555ca9aef75651271098d99217284d9e010f7c"}, + {file = "xattr-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcd5dfbcee73c7be057676ecb900cabb46c691aff4397bf48c579ffb30bb963"}, + {file = "xattr-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6480589c1dac7785d1f851347a32c4a97305937bf7b488b857fe8b28a25de9e9"}, + {file = "xattr-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08f61cbed52dc6f7c181455826a9ff1e375ad86f67dd9d5eb7663574abb32451"}, + {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:918e1f83f2e8a072da2671eac710871ee5af337e9bf8554b5ce7f20cdb113186"}, + {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0f06e0c1e4d06b4e0e49aaa1184b6f0e81c3758c2e8365597918054890763b53"}, + {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a641ac038a9f53d2f696716147ca4dbd6a01998dc9cd4bc628801bc0df7f4d"}, + {file = "xattr-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7e4ca0956fd11679bb2e0c0d6b9cdc0f25470cc00d8da173bb7656cc9a9cf104"}, + {file = "xattr-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6881b120f9a4b36ccd8a28d933bc0f6e1de67218b6ce6e66874e0280fc006844"}, + {file = "xattr-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab29d9288aa28e68a6f355ddfc3f0a7342b40c9012798829f3e7bd765e85c2c"}, + {file = "xattr-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c80bbf55339c93770fc294b4b6586b5bf8e85ec00a4c2d585c33dbd84b5006"}, + {file = "xattr-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1418705f253b6b6a7224b69773842cac83fcbcd12870354b6e11dd1cd54630f"}, + {file = "xattr-1.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687e7d18611ef8d84a6ecd8f4d1ab6757500c1302f4c2046ce0aa3585e13da3f"}, + {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6ceb9efe0657a982ccb8b8a2efe96b690891779584c901d2f920784e5d20ae3"}, + {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b489b7916f239100956ea0b39c504f3c3a00258ba65677e4c8ba1bd0b5513446"}, + {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0a9c431b0e66516a078125e9a273251d4b8e5ba84fe644b619f2725050d688a0"}, + {file = "xattr-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1a5921ea3313cc1c57f2f53b63ea8ca9a91e48f4cc7ebec057d2447ec82c7efe"}, + {file = "xattr-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6ad2a7bd5e6cf71d4a862413234a067cf158ca0ae94a40d4b87b98b62808498"}, + {file = "xattr-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0683dae7609f7280b0c89774d00b5957e6ffcb181c6019c46632b389706b77e6"}, + {file = "xattr-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54cb15cd94e5ef8a0ef02309f1bf973ba0e13c11e87686e983f371948cfee6af"}, + {file = "xattr-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff6223a854229055e803c2ad0c0ea9a6da50c6be30d92c198cf5f9f28819a921"}, + {file = "xattr-1.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d44e8f955218638c9ab222eed21e9bd9ab430d296caf2176fb37abe69a714e5c"}, + {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:caab2c2986c30f92301f12e9c50415d324412e8e6a739a52a603c3e6a54b3610"}, + {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d6eb7d5f281014cd44e2d847a9107491af1bf3087f5afeded75ed3e37ec87239"}, + {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:47a3bdfe034b4fdb70e5941d97037405e3904accc28e10dbef6d1c9061fb6fd7"}, + {file = "xattr-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:00d2b415cf9d6a24112d019e721aa2a85652f7bbc9f3b9574b2d1cd8668eb491"}, + {file = "xattr-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:78b377832dd0ee408f9f121a354082c6346960f7b6b1480483ed0618b1912120"}, + {file = "xattr-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6461a43b585e5f2e049b39bcbfcb6391bfef3c5118231f1b15d10bdb89ef17fe"}, + {file = "xattr-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24d97f0d28f63695e3344ffdabca9fcc30c33e5c8ccc198c7524361a98d526f2"}, + {file = "xattr-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ad47d89968c9097900607457a0c89160b4771601d813e769f68263755516065"}, + {file = "xattr-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc53cab265f6e8449bd683d5ee3bc5a191e6dd940736f3de1a188e6da66b0653"}, + {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cd11e917f5b89f2a0ad639d9875943806c6c9309a3dd02da5a3e8ef92db7bed9"}, + {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9c5a78c7558989492c4cb7242e490ffb03482437bf782967dfff114e44242343"}, + {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cebcf8a303a44fbc439b68321408af7267507c0d8643229dbb107f6c132d389c"}, + {file = "xattr-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b0d73150f2f9655b4da01c2369eb33a294b7f9d56eccb089819eafdbeb99f896"}, + {file = "xattr-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:793c01deaadac50926c0e1481702133260c7cb5e62116762f6fe1543d07b826f"}, + {file = "xattr-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e189e440bcd04ccaad0474720abee6ee64890823ec0db361fb0a4fb5e843a1bf"}, + {file = "xattr-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afacebbc1fa519f41728f8746a92da891c7755e6745164bd0d5739face318e86"}, + {file = "xattr-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b1664edf003153ac8d1911e83a0fc60db1b1b374ee8ac943f215f93754a1102"}, + {file = "xattr-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda2684228798e937a7c29b0e1c7ef3d70e2b85390a69b42a1c61b2039ba81de"}, + {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b735ac2625a4fc2c9343b19f806793db6494336338537d2911c8ee4c390dda46"}, + {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fa6a7af7a4ada43f15ccc58b6f9adcdbff4c36ba040013d2681e589e07ae280a"}, + {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1059b2f726e2702c8bbf9bbf369acfc042202a4cc576c2dec6791234ad5e948"}, + {file = "xattr-1.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e2255f36ebf2cb2dbf772a7437ad870836b7396e60517211834cf66ce678b595"}, + {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba4f80b9855cc98513ddf22b7ad8551bc448c70d3147799ea4f6c0b758fb466"}, + {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb70c16e7c3ae6ba0ab6c6835c8448c61d8caf43ea63b813af1f4dbe83dd156"}, + {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83652910ef6a368b77b00825ad67815e5c92bfab551a848ca66e9981d14a7519"}, + {file = "xattr-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7a92aff66c43fa3e44cbeab7cbeee66266c91178a0f595e044bf3ce51485743b"}, + {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d4f71b673339aeaae1f6ea9ef8ea6c9643c8cd0df5003b9a0eaa75403e2e06c"}, + {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a20de1c47b5cd7b47da61799a3b34e11e5815d716299351f82a88627a43f9a96"}, + {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23705c7079b05761ff2fa778ad17396e7599c8759401abc05b312dfb3bc99f69"}, + {file = "xattr-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:27272afeba8422f2a9d27e1080a9a7b807394e88cce73db9ed8d2dde3afcfb87"}, + {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd43978966de3baf4aea367c99ffa102b289d6c2ea5f3d9ce34a203dc2f2ab73"}, + {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ded771eaf27bb4eb3c64c0d09866460ee8801d81dc21097269cf495b3cac8657"}, + {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca300c0acca4f0cddd2332bb860ef58e1465d376364f0e72a1823fdd58e90d"}, + {file = "xattr-1.1.0.tar.gz", hash = "sha256:fecbf3b05043ed3487a28190dec3e4c4d879b2fcec0e30bafd8ec5d4b6043630"}, +] + +[package.dependencies] +cffi = ">=1.16.0" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "xmltodict" +version = "0.13.0" +description = "Makes working with XML feel like you are working with JSON" +optional = false +python-versions = ">=3.4" +files = [ + {file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"}, + {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"}, +] + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "yfinance" +version = "0.2.38" +description = "Download market data from Yahoo! Finance API" +optional = false +python-versions = "*" +files = [ + {file = "yfinance-0.2.38-py2.py3-none-any.whl", hash = "sha256:07525cf84414272723a3e2b9d4c0a2898ddb60cc0828aa190de26664fac6f676"}, + {file = "yfinance-0.2.38.tar.gz", hash = "sha256:483eecae0743d829fc337f21d80da4612f5257d5c1f35570efc4a5e98e4401a7"}, +] + +[package.dependencies] +appdirs = ">=1.4.4" +beautifulsoup4 = ">=4.11.1" +frozendict = ">=2.3.4" +html5lib = ">=1.1" +lxml = ">=4.9.1" +multitasking = ">=0.0.7" +numpy = ">=1.16.5" +pandas = ">=1.3.0" +peewee = ">=3.16.2" +pytz = ">=2022.5" +requests = ">=2.31" + +[package.extras] +nospam = ["requests-cache (>=1.0)", "requests-ratelimiter (>=0.3.1)"] +repair = ["scipy (>=1.6.3)"] + +[[package]] +name = "zipp" +version = "3.18.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8.1,<3.12" +content-hash = "bf27c8f9b575f4443c195486a3d343775ae83bb1263642673a07872ac447af12" diff --git a/openbb_terminal/portfolio/__init__.py b/openbb_terminal/portfolio/__init__.py deleted file mode 100644 index f16dab8f49b..00000000000 --- a/openbb_terminal/portfolio/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# flake8: noqa -from . import portfolio_optimization diff --git a/openbb_terminal/portfolio/allocation_model.py b/openbb_terminal/portfolio/allocation_model.py deleted file mode 100644 index 4cf41bc5c7b..00000000000 --- a/openbb_terminal/portfolio/allocation_model.py +++ /dev/null @@ -1,468 +0,0 @@ -"""Allocation Model""" - -__docformat__ = "numpy" - -import logging -from typing import Tuple - -import pandas as pd -import yfinance as yf -from tqdm import tqdm - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.etf import fmp_model, stockanalysis_model -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_allocation( - category: str, benchmark_ticker: str, portfolio_trades: pd.DataFrame -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Get category allocation for benchmark and portfolio - - Parameters - ---------- - category: str - Chosen category: Asset, Sector, Country or Region - benchmark_ticker: str - The ticker of the benchmark - portfolio_trades: pd.DataFrame - Object containing trades made within the portfolio - - Returns - ------- - pd.DataFrame - DataFrame with the top 10 of the benchmark's asset allocations - pd.DataFrame - DataFrame with the portfolio's asset allocations - """ - - if category == "Asset": - return get_assets_allocation(benchmark_ticker, portfolio_trades) - if category == "Sector": - return get_sectors_allocation(benchmark_ticker, portfolio_trades) - if category == "Country": - return get_countries_allocation(benchmark_ticker, portfolio_trades) - if category == "Region": - return get_regions_allocation(benchmark_ticker, portfolio_trades) - console.print( - "Category not available. Choose from: Asset, Sector, Country or Region." - ) - return pd.DataFrame(), pd.DataFrame() - - -@log_start_end(log=logger) -def get_assets_allocation( - benchmark_ticker: str, portfolio_trades: pd.DataFrame -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Get assets allocation for benchmark and portfolio [Source: Yahoo Finance] - - Parameters - ---------- - benchmark_ticker: str - The ticker of the benchmark - portfolio_trades: pd.DataFrame - Object containing trades made within the portfolio - - Returns - ------- - pd.DataFrame - DataFrame with the top 10 of the benchmark's asset allocations - pd.DataFrame - DataFrame with the portfolio's asset allocations - """ - - benchmark_assets_allocation = stockanalysis_model.get_etf_holdings( - benchmark_ticker - ).reset_index() - if benchmark_assets_allocation.empty: # if not an etf or no data is available - benchmark_assets_allocation = pd.DataFrame( - data={ - "symbol": [benchmark_ticker], - "Name": [benchmark_ticker], - "% Of Etf": ["100%"], - "Shares": [1], - }, - index=[0], - ) - benchmark_assets_allocation.rename( - columns={"symbol": "Symbol", "% Of Etf": "Benchmark"}, inplace=True - ) - benchmark_assets_allocation.drop(columns=["Name", "Shares"], inplace=True) - - benchmark_values_formatted = [] - for benchmark_value in benchmark_assets_allocation["Benchmark"].values: - benchmark_values_formatted.append(float(benchmark_value.strip("%")) / 100) - - benchmark_assets_allocation["Benchmark"] = benchmark_values_formatted - - portfolio_assets_allocation = ( - portfolio_trades[portfolio_trades["Type"] != "CASH"] - .groupby(by="Ticker") - .agg({"Portfolio Value": "sum"}) - .div(portfolio_trades["Portfolio Value"].sum()) - ).sort_values(by="Portfolio Value", ascending=False) - portfolio_assets_allocation.reset_index(inplace=True) - portfolio_assets_allocation.rename( - columns={"Ticker": "Symbol", "Portfolio Value": "Portfolio"}, inplace=True - ) - portfolio_assets_allocation.fillna(0, inplace=True) - - return benchmark_assets_allocation, portfolio_assets_allocation - - -@log_start_end(log=logger) -def get_sectors_allocation( - benchmark_ticker: str, portfolio_trades: pd.DataFrame -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Get sector allocation for benchmark and portfolio [Source: Yahoo Finance] - - Parameters - ---------- - benchmark_ticker: str - The ticker of the benchmark - portfolio_trades: pd.DataFrame - Object containing trades made within the portfolio - - Returns - ------- - pd.DataFrame - DataFrame with benchmark allocations. - pd.DataFrame - DataFrame with portfolio allocations - """ - - sectors = fmp_model.get_etf_sector_weightings(benchmark_ticker) - - sector_weights_formatted = {} - for sector_weight in sectors: - sector_weights_formatted[sector_weight["sector"]] = ( - float(sector_weight["weightPercentage"].strip("%")) / 100 - ) - - benchmark_sectors_allocation = pd.DataFrame( - [sector_weights_formatted] - ).T.reset_index() - benchmark_sectors_allocation.rename( - columns={"index": "Sector", 0: "Benchmark"}, inplace=True - ) - - # Define portfolio sector allocation - # Aggregate sector value for stocks and crypto - portfolio_sectors_allocation = ( - portfolio_trades[portfolio_trades["Type"].isin(["STOCK", "CRYPTO"])] - .groupby(by="Sector") - .agg({"Portfolio Value": "sum"}) - ) - - # Aggregate sector value for ETFs - # Start by getting value by isin/symbol - etf_ticker_value = ( - portfolio_trades[portfolio_trades["Type"].isin(["ETF"])] - .groupby(by="Ticker") - .agg({"Portfolio Value": "sum"}) - ) - etf_global_sector_alloc = pd.DataFrame() - - if not etf_ticker_value.empty: - # Loop through each etf and multiply sector weights by current value - for item in tqdm(etf_ticker_value.index.values, desc="Loading ETF data"): - # TODO: This can be improved by caching this info similar to what is done in stocks - etf_info = yf.Ticker(item).info - - try: - etf_sector_weight = pd.DataFrame.from_dict( - data={ - sector_name: allocation - for sector in etf_info["sectorWeightings"] - for sector_name, allocation in sector.items() - }, - orient="index", - columns=["Portfolio Value"], - ) - - except Exception: - # If ETF has no sectors like VIX for example or it was not found, add to Other - etf_sector_weight = pd.DataFrame.from_dict( - data={"Other": 1}, orient="index", columns=["Portfolio Value"] - ) - - etf_ticker_sector_alloc = ( - etf_sector_weight * etf_ticker_value["Portfolio Value"][item] - ) - - # Aggregate etf sector allocation by value - etf_global_sector_alloc = pd.concat( - [etf_global_sector_alloc, etf_ticker_sector_alloc], axis=1 - ) - etf_global_sector_alloc.fillna(0, inplace=True) - etf_global_sector_alloc = etf_global_sector_alloc.sum(axis=1) - - etf_global_sector_alloc = pd.DataFrame( - etf_global_sector_alloc, columns=["Portfolio Value"] - ) - console.print("\n") - - # Rename columns to match stock and crypto classification - etf_global_sector_alloc.index.name = "Sector" - prettified = [ - sector.replace("_", " ").title() for sector in etf_global_sector_alloc.index - ] - - etf_global_sector_alloc.index = prettified - - # Aggregate sector allocation for stocks and crypto with ETFs - portfolio_sectors_allocation = pd.merge( - portfolio_sectors_allocation, - etf_global_sector_alloc, - how="outer", - left_index=True, - right_index=True, - ).sum(axis=1) - - portfolio_sectors_allocation = pd.DataFrame( - portfolio_sectors_allocation, columns=["Portfolio Value"] - ) - - portfolio_sectors_allocation = portfolio_sectors_allocation.div( - portfolio_trades["Portfolio Value"].sum() - ).sort_values(by="Portfolio Value", ascending=False) - - portfolio_sectors_allocation.fillna(0, inplace=True) - portfolio_sectors_allocation = pd.DataFrame(portfolio_sectors_allocation) - portfolio_sectors_allocation.reset_index(inplace=True) - portfolio_sectors_allocation.columns = ["Sector", "Portfolio"] - - return benchmark_sectors_allocation, portfolio_sectors_allocation - - -@log_start_end(log=logger) -def get_countries_allocation( - benchmark_ticker: str, portfolio_trades: pd.DataFrame -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Get countries allocation for benchmark and portfolio [Source: Yahoo Finance] - - Parameters - ---------- - benchmark_ticker: str - The ticker of the benchmark - portfolio_trades: pd.DataFrame - Object containing trades made within the portfolio - - Returns - ------- - pd.DataFrame - DataFrame with benchmark allocations. - pd.DataFrame - DataFrame with portfolio allocations - """ - - benchmark_allocation = get_symbol_allocation( - symbol=benchmark_ticker, category="Country", col_name="Benchmark" - ) - - portfolio_allocation = get_portfolio_allocation( - category="Country", portfolio_trades=portfolio_trades - ) - - return benchmark_allocation, portfolio_allocation - - -@log_start_end(log=logger) -def get_regions_allocation( - benchmark_ticker: str, portfolio_trades: pd.DataFrame -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Get regions allocation for benchmark and portfolio [Source: Yahoo Finance] - - Parameters - ---------- - benchmark_ticker: str - The ticker of the benchmark - portfolio_trades: pd.DataFrame - Object containing trades made within the portfolio - - Returns - ------- - pd.DataFrame - DataFrame with regional allocations. - pd.DataFrame - DataFrame with country allocations - """ - benchmark_allocation = get_symbol_allocation( - symbol=benchmark_ticker, category="Region", col_name="Benchmark" - ) - - portfolio_allocation = get_portfolio_allocation( - category="Region", portfolio_trades=portfolio_trades - ) - - return benchmark_allocation, portfolio_allocation - - -def get_symbol_allocation( - symbol: str, category: str, col_name: str = "Weight" -) -> pd.DataFrame: - """Get benchmark allocation [Source: Fidelity] - - Parameters - ---------- - symbol: str - ETF symbol to get allocation - category: str - Chosen category: Country or Region - - Returns - ------- - pd.DataFrame - Dictionary with category allocations - """ - - if category == "Region": - category_list = [ - "North America", - "Europe", - "Asia", - "Latin America", - "Africa", - "Middle East", - ] - - if category == "Country": - category_list = [ - "United States", - "United Kingdom", - "Japan", - "Switzerland", - "China", - ] - - item_list = 0 - - # Collect data from Fidelity about the portfolio composition of the benchmark - URL = f"https://screener.fidelity.com/ftgw/etf/goto/snapshot/portfolioComposition.jhtml?symbols={symbol}" - html = request(URL).content - try: - df_list = pd.read_html(html) - except ValueError: - return pd.DataFrame(columns=[category, col_name]) - - # Find the ones that contain regions and countries - for index, item in enumerate(df_list): - for category_item in category_list: - if category_item in item.values: - item_list = index - break - - if item_list: - allocation = { - row[1]: float(row[2].strip("%")) / 100 - for _, row in df_list[item_list].dropna(axis="columns").iterrows() - } - allocation_df = pd.DataFrame.from_dict(allocation, orient="index") - allocation_df.reset_index(inplace=True) - allocation_df.columns = [category, col_name] - else: - allocation_df = pd.DataFrame(columns=[category, col_name]) - - return allocation_df - - -@log_start_end(log=logger) -def get_portfolio_allocation( - category: str, portfolio_trades: pd.DataFrame -) -> pd.DataFrame: - """Get portfolio allocation - - Parameters - ---------- - category: str - Chosen category: Country or Region - portfolio_trades: pd.DataFrame - Object containing trades made within the portfolio - - Returns - ------- - pd.DataFrame - Dictionary with country allocations - """ - - # Define portfolio allocation - if not portfolio_trades[category].isnull().any(): - allocation = ( - portfolio_trades[portfolio_trades["Type"].isin(["STOCK", "CRYPTO"])] - .groupby(by=category) - .agg({"Portfolio Value": "sum"}) - ) - else: - allocation = pd.DataFrame() - - # Aggregate sector value for ETFs - # Start by getting value by symbol - etf_ticker_value = ( - portfolio_trades[portfolio_trades["Type"].isin(["ETF"])] - .groupby(by="Ticker") - .agg({"Portfolio Value": "sum"}) - ) - etf_global_alloc = pd.DataFrame(columns=[category, "Portfolio Value"]) - - if not etf_ticker_value.empty: - no_info = [] - # Loop through each etf and multiply sector weights by current value - for item in tqdm(etf_ticker_value.index.values, desc="Loading ETF data"): - etf_weight = get_symbol_allocation( - symbol=item, category=category, col_name="Portfolio Value" - ) - - if etf_weight.empty: - etf_weight = pd.DataFrame.from_dict( - data={"Other": 1}, orient="index", columns=["Portfolio Value"] - ) - etf_weight.index.name = category - no_info.append(item) - else: - etf_weight.set_index(category, inplace=True) - - # Aggregate etf allocation by value - etf_ticker_alloc = etf_weight - etf_ticker_alloc["Portfolio Value"] = ( - etf_ticker_alloc["Portfolio Value"] - * etf_ticker_value["Portfolio Value"][item] - ) - etf_global_alloc = pd.concat([etf_global_alloc, etf_ticker_alloc], axis=1) - etf_global_alloc.fillna(0, inplace=True) - etf_global_alloc = etf_global_alloc.sum(axis=1) - - etf_global_alloc = pd.DataFrame(etf_global_alloc, columns=["Portfolio Value"]) - - console.print("") - - if no_info: - console.print( - f"[red]No data found for: {', '.join(no_info)}. Included in 'Other'.[/red]\n" - ) - - # Aggregate allocation for stocks and crypto with ETFs - allocation = pd.merge( - allocation, - etf_global_alloc, - how="outer", - left_index=True, - right_index=True, - ).sum(axis=1) - - allocation = pd.DataFrame(allocation, columns=["Portfolio Value"]) - - allocation = allocation.div(portfolio_trades["Portfolio Value"].sum()).sort_values( - by="Portfolio Value", ascending=False - ) - - allocation.fillna(0, inplace=True) - - allocation.reset_index(inplace=True) - - allocation.columns = [category, "Portfolio"] - - return allocation diff --git a/openbb_terminal/portfolio/attribution_model.py b/openbb_terminal/portfolio/attribution_model.py deleted file mode 100644 index 068b9045181..00000000000 --- a/openbb_terminal/portfolio/attribution_model.py +++ /dev/null @@ -1,398 +0,0 @@ -"""Attribution Model""" - -__docformat__ = "numpy" - -import logging -from datetime import date, datetime -from typing import Dict - -import pandas as pd -import yfinance as yf - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.etf import fmp_model - -logger = logging.getLogger(__name__) - - -SPY_SECTORS_MAP = { - "S&P 500 Materials (Sector)": "Basic Materials", - "S&P 500 Industrials (Sector)": "Industrials", - "S&P 500 Consumer Discretionary (Sector)": "Consumer Cyclical", - "S&P 500 Consumer Staples (Sector)": "Consumer Defensive", - "S&P 500 Health Care (Sector)": "Healthcare", - "S&P 500 Financials (Sector)": "Financial Services", - "S&P 500 Information Technology (Sector)": "Technology", - "S&P 500 Telecommunication Services (Sector)": "Communication Services", - "S&P 500 Utilities (Sector)": "Utilities", - "S&P 500 Real Estate (Sector)": "Real Estate", - "S&P 500 Energy (Sector)": "Energy", -} - -PF_SECTORS_MAP = { - "Basic Materials": "S&P 500 Materials (Sector)", - "Industrials": "S&P 500 Industrials (Sector)", - "Consumer Cyclical": "S&P 500 Consumer Discretionary (Sector)", - "Consumer Defensive": "S&P 500 Consumer Staples (Sector)", - "Healthcare": "S&P 500 Health Care (Sector)", - "Financial Services": "S&P 500 Financials (Sector)", - "Technology": "S&P 500 Information Technology (Sector)", - "Communication Services": "S&P 500 Telecommunication Services (Sector)", - "Utilities": "S&P 500 Utilities (Sector)", - "Real Estate": "S&P 500 Real Estate (Sector)", - "Energy": "S&P 500 Energy (Sector)", -} - - -@log_start_end(log=logger) -def get_spy_sector_contributions( - start_date, end_date=date.today() -) -> pd.DataFrame: # format like 2015-01-15 (YYYY-MM-DD) - """ - Fetch sector contributions for the SPY for a fixed period - - Parameters - ---------- - start_date : str ('yyyy-mm-dd') or datetime.date - start date for fetching data - end_date : str ('yyyy-mm-dd') or datetime.date - end date for fetching data - - Returns - ------- - contributions : pd.DataFrame - DataFrame with contributions for each sector - """ - - # Sector Map - sectors_ticker = "SPY" - - # Load in info - sp500_tickers_data = get_daily_sector_prices(start_date, end_date) - weight_data = fmp_model.get_etf_sector_weightings(sectors_ticker) - - # reformat Data - weights: Dict[str, dict] = {"SPY": {}} - - for sector in weight_data: - weight_formatted = float(sector["weightPercentage"].strip("%")) / 100 - weights[sectors_ticker][sector["sector"]] = weight_formatted - - # add the sectors + dates + adj close to the dataframe - records = [] - for sector, data in sp500_tickers_data.items(): - for x in range(0, len(data["sector_data"])): - record = { - "sector": sector, - "date": data["sector_data"].index[x], - "adj_close": data["sector_data"][x], - "sector_weight": weights[sectors_ticker][SPY_SECTORS_MAP[sector]], - } - records.append(record) - - df = pd.DataFrame(records) - - df["pct_change"] = df.groupby("sector")["adj_close"].pct_change() - df["contribution"] = df["pct_change"] * df["sector_weight"] - - contributions = df.groupby("sector").agg({"contribution": "sum"}) - contributions["contribution_as_pct"] = ( - contributions["contribution"] / abs(df["contribution"].sum()) - ) * 100 - - return contributions - - -@log_start_end(log=logger) -def get_portfolio_sector_contributions( - start_date, portfolio_trades: pd.DataFrame -) -> pd.DataFrame: - """Calculate sector contributions for the loaded portfolio for a fixed period. This is done - by calculating the daily attribution of each asset (% change in adj_close * Weight in PF) - then grouping by sector and summing the contribution. - - Parameters - ---------- - start_data : str ('yyyy-mm-dd') or datetime.date - start date for calculating contributions from - portfolio_trades : dataframe - dataframe of trades in the loaded portfolio - - Returns - ------- - contributions : pd.DataFrame - dataframe with portfolio raw contributions - """ - - # Cast portfolio_trades "Date" to datetime64[ns] - portfolio_trades["Date"] = pd.to_datetime(portfolio_trades["Date"]) - - contrib_df = pd.DataFrame() - asset_tickers = list(portfolio_trades["Ticker"].unique()) - first_price = portfolio_trades["Date"].min() - - price_data = pd.DataFrame( - yf.download(asset_tickers, start=first_price, progress=False, ignore_tz=True)[ - "Adj Close" - ] - ) # returns series when one ticker, hence cast to df - - # if there is only one ticker the column name is "Adj Close" instead of the ticker, - # if so it needs to be renamed to allow the df multiplication to work - if len(asset_tickers) == 1: - price_data = price_data.rename(columns={"Adj Close": asset_tickers[0]}) - - price_change = price_data.pct_change() - - # Create a wide dataframe of shares owned on each day - cumulative_positions = portfolio_trades.copy() - cumulative_positions["Quantity"] = cumulative_positions.groupby("Ticker")[ - "Quantity" - ].cumsum() - - cumulative_positions_wide = pd.pivot( - cumulative_positions, index="Date", columns="Ticker", values="Quantity" - ) - index = pd.date_range(start=first_price, end=datetime.now(), freq="1D") - contrib_df = cumulative_positions_wide.reindex(index).ffill(axis=0) - - # Multiply shares by price to get market cap of holdings on day - contrib_df = contrib_df * price_data - - # turn daily market caps market caps into portfolio weights - contrib_df = contrib_df.div(contrib_df.sum(axis=1), axis=0) - - # multiply by pct change in price to get daily attribution - contrib_df = contrib_df * price_change - - # Wide to Long for aggregation - contrib_df = contrib_df.reset_index() - contrib_df = contrib_df.rename(columns={"index": "date"}) - - # filter passed off desired date here - contrib_df["date"] = contrib_df["date"].dt.date - contrib_df = contrib_df[~(contrib_df["date"] < start_date.date())] - - # melt on datetime field - contrib_df = pd.melt(contrib_df, id_vars="date") - - # # Get Sectors - sector_df = ( - portfolio_trades[["Ticker", "Sector"]] - .groupby("Ticker") - .agg({"Sector": "min"}) - .reset_index() - ) - - contrib_df = pd.merge(contrib_df, sector_df) - contrib_df = contrib_df.rename(columns={"value": "contribution"}) - - contrib_df = contrib_df.groupby("Sector").agg({"contribution": "sum"}) - contrib_df["contribution_as_pct"] = ( - contrib_df["contribution"] / abs(contrib_df["contribution"].sum()) - ) * 100 - - contrib_df = contrib_df.rename(index=PF_SECTORS_MAP) - contrib_df = contrib_df.reindex(PF_SECTORS_MAP.values()) - contrib_df = contrib_df.fillna(0) - - return contrib_df - - -@log_start_end(log=logger) -def percentage_attrib_categorizer( - bench_df: pd.DataFrame, pf_df: pd.DataFrame -) -> pd.DataFrame: - """ - Merge S&P500 benchmark attribution and portfolio attribution dataframes and calculates - excess attribution, attribution ratio, attribution direction and attribution sensitivity. - Returns attribution results as a proportion of the portfolio. - - for example if a PF returns 1% and the raw attribution of a sector is 0.5% the result for the - sector is 50%. - - Parameters - ---------- - bench_df : pd.DataFrame - S&P500 attribution dataframe - pf_df : pd.DataFrame - portfolio attribution dataframe - - Returns - ------- - result : pd.DataFrame - dataframe of S&P500 and PF attribution as a proportion - """ - - # using percentage contributions - bench_df = bench_df.iloc[:, [1]] - pf_df = pf_df.iloc[:, [1]] - - # rename columns - bench_df.rename(columns={"contribution_as_pct": "S&P500 [%]"}, inplace=True) - pf_df.rename(columns={"contribution_as_pct": "Portfolio [%]"}, inplace=True) - result = bench_df.join(pf_df) - - # 1. Excess Attribution - - result["Excess Attribution"] = result["Portfolio [%]"] - result["S&P500 [%]"] - - # 2. Attribution Ratio - - result["Attribution Ratio"] = result["Portfolio [%]"] / result["S&P500 [%]"] - - # 3. Attribution Direction - - direction = [] - - for ratio in result["Attribution Ratio"]: - if ratio >= 0: - direction.append("Correlated (+)") - elif ratio < 0: - direction.append("Uncorrelated (-)") - - result["Attribution Direction [+/-]"] = direction - - # 4. Attribution Sensitivity - - sensitivity = [] - - for ratio in result["Attribution Ratio"]: - if abs(ratio) > 1.25: - sensitivity.append("High") - elif 0.75 <= abs(ratio) <= 1.25: - sensitivity.append("Normal") - elif abs(ratio) < 0.75: - sensitivity.append("Low") - - result["Attribution Sensitivity"] = sensitivity - - # 5. round values before output - result["S&P500 [%]"] = result["S&P500 [%]"].astype(float).round(2) - result["Portfolio [%]"] = result["Portfolio [%]"].astype(float).round(2) - - return result - - -@log_start_end(log=logger) -def raw_attrib_categorizer(bench_df, pf_df) -> pd.DataFrame: - """Merge S&P500 benchmark attribution and portfolio attribution dataframes and calculates - excess attribution, attribution ratio, attribution direction and attribution sensitivity. - Returns attribution results as raw values - - for example if a PF returns 1% and the raw attribution of a sector is 0.5% the result for the - sector is 0.5 - - Parameters - ---------- - bench_df : pd.DataFrame - S&P500 attribution dataframe - pf_df : pd.DataFrame - portfolio attribution dataframe - - Returns - ------- - result : pd.DataFrame - dataframe of S&P500 and PF attribution as raw values. - """ - - bench_df = bench_df.iloc[:, [0]] - pf_df = pf_df.iloc[:, [0]] - - # rename columns - bench_df.rename(columns={"contribution": "S&P500"}, inplace=True) - pf_df.rename(columns={"contribution": "Portfolio"}, inplace=True) - result = bench_df.join(pf_df) - - # 1. Excess Attribution - - result["Excess Attribution"] = result["Portfolio"] - result["S&P500"] - - # 2. Attribution Ratio - - result["Attribution Ratio"] = result["Portfolio"] / result["S&P500"] - - # 3. Attribution Direction - - direction = [] - - for ratio in result["Attribution Ratio"]: - if ratio >= 0: - direction.append("Correlated (+)") - elif ratio < 0: - direction.append("Uncorrelated (-)") - - result["Attribution Direction [+/-]"] = direction - - # 4. Attribution Sensitivity - - sensitivity = [] - - for ratio in result["Attribution Ratio"]: - if abs(ratio) > 1.25: - sensitivity.append("High") - elif 0.75 <= abs(ratio) <= 1.25: - sensitivity.append("Normal") - elif abs(ratio) < 0.75: - sensitivity.append("Low") - - result["Attribution Sensitivity"] = sensitivity - - # 5. round values before output - result["S&P500"] = result["S&P500"].astype(float).round(4) - result["Portfolio"] = result["Portfolio"].astype(float).round(4) - - return result - - -@log_start_end(log=logger) -def get_daily_sector_prices(start_date, end_date) -> dict: - """Fetch daily sector prices for S&P500 for a fixed time period - - Parameters - ---------- - start_date : str ('yyyy-mm-dd') or datetime.date - start date for fetching data - end_date : str ('yyyy-mm-dd') or datetime.date - end date for fetching data - - Returns - ------- - sp500_tickers_data : dict - dictionary of dataframes with SPY daily sector prices - """ - # sector ticker information - sp500_tickers = { - "S&P 500 Materials (Sector)": "^SP500-15", - "S&P 500 Industrials (Sector)": "^SP500-20", - "S&P 500 Consumer Discretionary (Sector)": "^SP500-25", - "S&P 500 Consumer Staples (Sector)": "^SP500-30", - "S&P 500 Health Care (Sector)": "^SP500-35", - "S&P 500 Financials (Sector)": "^SP500-40", - "S&P 500 Information Technology (Sector)": "^SP500-45", - "S&P 500 Telecommunication Services (Sector)": "^SP500-50", - "S&P 500 Utilities (Sector)": "^SP500-55", - "S&P 500 Real Estate (Sector)": "^SP500-60", - "S&P 500 Energy (Sector)": "^GSPE", - } - - sp500_tickers_data = {} # to store data - - for ( - sector, - sector_ticker, - ) in sp500_tickers.items(): # iterate thru the sectors - # load the data required from yfinance - sp500_tickers_data[sector] = ( - { # builds a dictionary entry for the sector with adj close data - "sector_data": yf.download( - sector_ticker, - start=start_date, - end=end_date, - progress=False, - ignore_tz=True, - )["Adj Close"] - } - ) # stores the data here - - return sp500_tickers_data diff --git a/openbb_terminal/portfolio/metrics_model.py b/openbb_terminal/portfolio/metrics_model.py deleted file mode 100644 index eca0d36ca44..00000000000 --- a/openbb_terminal/portfolio/metrics_model.py +++ /dev/null @@ -1,985 +0,0 @@ -"""Metrics Model""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Tuple - -import numpy as np -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.portfolio.portfolio_helper import filter_df_by_period -from openbb_terminal.portfolio.statics import PERIODS -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -# pylint: disable=too-many-return-statements, too-many-lines, too-many-statements -# pylint: disable=C0302 - - -now = datetime.now() -PERIODS_DAYS = { - "mtd": (now - datetime(now.year, now.month, 1)).days, - "qtd": ( - now - - datetime( - now.year, - 1 if now.month < 4 else 4 if now.month < 7 else 7 if now.month < 10 else 10, - 1, - ) - ).days, - "ytd": (now - datetime(now.year, 1, 1)).days, - "all": 1, - "3m": 3 * 21, - "6m": 6 * 21, - "1y": 12 * 21, - "3y": 3 * 12 * 21, - "5y": 5 * 12 * 21, - "10y": 10 * 12 * 21, -} - -DEFAULT_HOLDINGS_PATH = ( - get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY / "holdings" -) - - -@log_start_end(log=logger) -def rolling_volatility( - portfolio_returns: pd.Series, window: str = "1y" -) -> pd.DataFrame: - """Get rolling volatility - - Parameters - ---------- - portfolio_returns : pd.Series - Series of portfolio returns - window : str - Rolling window size to use - - Returns - ------- - pd.DataFrame - Rolling volatility DataFrame - """ - - length = PERIODS_DAYS[window] - sample_length = len(portfolio_returns) - - if length > sample_length: - console.print( - f"[red]Window length ({window}->{length}) is larger than returns length ({sample_length}).\ - \nTry a smaller window.[/red]" - ) - return pd.DataFrame() - - # max(2, length) -> std needs at least 2 observations - return portfolio_returns.rolling(max(2, length)).std() - - -@log_start_end(log=logger) -def sharpe_ratio(portfolio_returns: pd.Series, risk_free_rate: float) -> float: - """Get sharpe ratio - - Parameters - ---------- - return_series : pd.Series - Series of portfolio returns - risk_free_rate: float - Value to use for risk free rate - - Returns - ------- - float - Sharpe ratio - """ - mean = portfolio_returns.mean() - risk_free_rate - sigma = portfolio_returns.std() - - return mean / sigma - - -@log_start_end(log=logger) -def rolling_sharpe( - portfolio_returns: pd.DataFrame, risk_free_rate: float, window: str = "1y" -) -> pd.DataFrame: - """Get rolling sharpe ratio - - Parameters - ---------- - portfolio_returns : pd.Series - Series of portfolio returns - risk_free_rate : float - Risk free rate - window : str - Rolling window to use - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y - - Returns - ------- - pd.DataFrame - Rolling sharpe ratio DataFrame - """ - - length = PERIODS_DAYS[window] - sample_length = len(portfolio_returns) - - if length > sample_length: - console.print( - f"[red]Window length ({window}->{length}) is larger than returns length ({sample_length}).\ - \nTry a smaller window.[/red]" - ) - return pd.DataFrame() - - # max(2, length) -> std needs at least 2 observations - rolling_sharpe_df = portfolio_returns.rolling(max(2, length)).apply( - lambda x: (x.mean() - risk_free_rate) / x.std() - ) - return rolling_sharpe_df - - -@log_start_end(log=logger) -def sortino_ratio(portfolio_returns: pd.Series, risk_free_rate: float) -> float: - """Get sortino ratio - - Parameters - ---------- - portfolio_returns : pd.Series - Series of portfolio returns - risk_free_rate: float - Value to use for risk free rate - - Returns - ------- - float - Sortino ratio - """ - mean = portfolio_returns.mean() - risk_free_rate - std_neg = portfolio_returns[portfolio_returns < 0].std() - - return mean / std_neg - - -@log_start_end(log=logger) -def rolling_sortino( - portfolio_returns: pd.Series, risk_free_rate: float, window: str = "1y" -) -> pd.DataFrame: - """Get rolling sortino ratio - - Parameters - ---------- - portfolio_returns : pd.Series - Series of portfolio returns - risk_free_rate : float - Risk free rate - window : str - Rolling window to use - - Returns - ------- - pd.DataFrame - Rolling sortino ratio DataFrame - """ - length = PERIODS_DAYS[window] - - sample_length = len(portfolio_returns) - - if length > sample_length: - console.print( - f"[red]Window length ({window}->{length}) is larger than returns length ({sample_length}).\ - \nTry a smaller window.[/red]" - ) - return pd.DataFrame() - - # max(2, length) -> std needs at least 2 observations - rolling_sortino_df = portfolio_returns.rolling(max(2, length)).apply( - lambda x: (x.mean() - risk_free_rate) / x[x < 0].std() - ) - - return rolling_sortino_df - - -@log_start_end(log=logger) -def rolling_beta( - portfolio_returns: pd.Series, - benchmark_returns: pd.Series, - window: str = "1y", -) -> pd.DataFrame: - """Get rolling beta using portfolio and benchmark returns - - Parameters - ---------- - returns: pd.Series - Series of portfolio returns - benchmark_returns: pd.Series - Series of benchmark returns - window: string - Interval used for rolling values. - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y. - - Returns - ------- - pd.DataFrame - DataFrame of the portfolio's rolling beta - """ - - length = PERIODS_DAYS[window] - - sample_length = len(portfolio_returns) - - if length > sample_length: - console.print( - f"[red]Window length ({window}->{length}) is larger than returns length ({sample_length}).\ - \nTry a smaller window.[/red]" - ) - return pd.DataFrame() - - covs = ( - pd.DataFrame({"Portfolio": portfolio_returns, "Benchmark": benchmark_returns}) - .dropna(axis=0) - .rolling(max(2, length)) # needs at least 2 observations. - .cov() - .unstack() - .dropna() - ) - - rolling_beta_num = covs["Portfolio"]["Benchmark"] / covs["Benchmark"]["Benchmark"] - - return rolling_beta_num - - -@log_start_end(log=logger) -def maximum_drawdown(portfolio_returns: pd.Series) -> float: - """Get maximum drawdown - - Parameters - ---------- - portfolio_returns : pd.Series - Series of portfolio returns - - Returns - ------- - float - Maximum drawdown - """ - comp_ret = (portfolio_returns + 1).cumprod() - peak = comp_ret.expanding(min_periods=1).max() - dd = (comp_ret / peak) - 1 - - return dd.min() - - -@log_start_end(log=logger) -def cumulative_returns(data: pd.Series) -> pd.Series: - """Calculate cumulative returns filtered by period - - Parameters - ---------- - data : pd.Series - Series of portfolio returns - - Returns - ---------- - pd.Series - Cumulative investment returns series - ------- - """ - return (1 + data.shift(periods=1, fill_value=0)).cumprod() - 1 - - -@log_start_end(log=logger) -def get_gaintopain_ratio( - historical_trade_data: pd.DataFrame, - benchmark_trades: pd.DataFrame, - benchmark_returns: pd.DataFrame, -) -> pd.DataFrame: - """Get Pain-to-Gain ratio - - Parameters - ---------- - historical_trade_data: pd.DataFrame - Dataframe of historical data for the portfolios trade - benchmark_trades: pd.DataFrame - Dataframe of the benchmark's trades - benchmark_returns: pd.DataFrame - Dataframe of benchmark returns - - Returns - ------- - pd.DataFrame - DataFrame of the portfolio's gain-to-pain ratio - """ - benchmark_trades = benchmark_trades.set_index("Date") - vals = list() - for period in PERIODS: - period_historical_trade_data = filter_df_by_period( - historical_trade_data, period - ) - period_bench_trades = filter_df_by_period(benchmark_trades, period) - period_bench_return = filter_df_by_period(benchmark_returns, period) - if not period_historical_trade_data.empty: - if not period_bench_trades.empty: - benchmark_values = ( - period_bench_trades["Benchmark Value"].sum() - / period_bench_trades["Benchmark Investment"].sum() - - 1 - ) / maximum_drawdown(period_bench_return) - else: - benchmark_values = ((1 + period_bench_return).cumprod() - 1).iloc[ - -1 - ] / maximum_drawdown(period_bench_return) - - vals.append( - [ - round( - ( - period_historical_trade_data["End Value"]["Total"].iloc[-1] - / ( - period_historical_trade_data["Initial Value"][ - "Total" - ].iloc[0] - + period_historical_trade_data["Investment"][ - "Total" - ].iloc[-1] - - period_historical_trade_data["Investment"][ - "Total" - ].iloc[0] - ) - - 1 - ) - / maximum_drawdown( - period_historical_trade_data["Period percentage return"][ - "Total" - ] - ), - 3, - ), - round( - benchmark_values, - 3, - ), - ] - ) - else: - vals.append(["-", "-"]) - gtr_period_df = pd.DataFrame( - vals, index=PERIODS, columns=["Portfolio", "Benchmark"] - ) - - return gtr_period_df - - -@log_start_end(log=logger) -def calculate_beta(portfolio_returns: pd.Series, benchmark_returns: pd.Series) -> float: - """Calculate the beta using portfolio and benchmark return values - - Parameters - ---------- - portfolio_returns: pd.Series - Series of portfolio returns - benchmark_returns: pd.Series - Series of benchmark returns - - Returns - ------- - float - The calculated beta value - """ - axis_diff = len(portfolio_returns) - len(benchmark_returns) - axis_diff_bench = 0 - if axis_diff < 0: - axis_diff_bench = -axis_diff - axis_diff = 0 - - covariance = np.cov( - portfolio_returns[axis_diff:], benchmark_returns[axis_diff_bench:] - )[0][1] - variance = portfolio_returns.var() - - return covariance / variance - - -@log_start_end(log=logger) -def get_tracking_error( - portfolio_returns: pd.Series, benchmark_returns: pd.Series, window: str = "252d" -) -> Tuple[pd.DataFrame, pd.Series]: - """Get tracking error, or active risk, using portfolio and benchmark returns - - Parameters - ---------- - portfolio_returns: pd.Series - Series of portfolio returns - benchmark_returns: pd.Series - Series of benchmark returns - window: string - Interval used for rolling values in days. - Examples: 1d, 5d, 10d - - Returns - ------- - pd.DataFrame - DataFrame of tracking errors during different time periods - pd.Series - Series of rolling tracking error - """ - diff_returns = portfolio_returns - benchmark_returns - - tracker_rolling = diff_returns.rolling(window).std() - - vals = list() - for periods in PERIODS: - period_return = filter_df_by_period(diff_returns, periods) - if not period_return.empty: - vals.append([round(period_return.std(), 3)]) - else: - vals.append(["-"]) - tracker_period_df = pd.DataFrame(vals, index=PERIODS, columns=["Tracking Error"]) - - return tracker_period_df, tracker_rolling - - -@log_start_end(log=logger) -def get_information_ratio( - portfolio_returns: pd.Series, - historical_trade_data: pd.DataFrame, - benchmark_trades: pd.DataFrame, - benchmark_returns: pd.Series, -) -> pd.DataFrame: - """Calculate information ratio, which measures the active return of an investment - compared to the benchmark relative to the volatility of the active return - - Parameters - ---------- - portfolio_returns: pd.Series - Series of portfolio returns - historical_trade_data: pd.DataFrame - Dataframe of historical data for the portfolio's trade - benchmark_trades: pd.DataFrame - Dataframe of the benchmark's trades - benchmark_returns: pd.Series - Series of benchmark returns - - Returns - ------- - pd.DataFrame - DataFrame of the information ratio during different time periods - """ - tracking_err_df, _ = get_tracking_error(portfolio_returns, benchmark_returns) - benchmark_trades = benchmark_trades.set_index("Date") - vals = list() - for periods in PERIODS: - period_historical_trade_data = filter_df_by_period( - historical_trade_data, periods - ) - period_bench_trades = filter_df_by_period(benchmark_trades, periods) - period_bench_return = filter_df_by_period(benchmark_returns, periods) - if not period_historical_trade_data.empty: - if not period_bench_trades.empty: - period_bench_total_return = ( - period_bench_trades["Benchmark Value"].sum() - / period_bench_trades["Benchmark Investment"].sum() - - 1 - ) - else: - period_bench_total_return = ( - (1 + period_bench_return).cumprod() - 1 - ).iloc[-1] - vals.append( - [ - round( - ( - ( - period_historical_trade_data["End Value"]["Total"].iloc[ - -1 - ] - / ( - period_historical_trade_data["Initial Value"][ - "Total" - ].iloc[0] - + period_historical_trade_data["Investment"][ - "Total" - ].iloc[-1] - - period_historical_trade_data["Investment"][ - "Total" - ].iloc[0] - ) - - 1 - ) - - period_bench_total_return - ) - / tracking_err_df.loc[periods, "Tracking Error"], - 3, - ) - ] - ) - else: - vals.append(["-"]) - - ir_period_df = pd.DataFrame(vals, index=PERIODS, columns=["Information Ratio"]) - - return ir_period_df - - -@log_start_end(log=logger) -def get_tail_ratio( - portfolio_returns: pd.Series, benchmark_returns: pd.Series, window: str = "252d" -) -> Tuple[pd.DataFrame, pd.Series, pd.Series]: - """Return the portfolios tail ratio - - Parameters - ---------- - portfolio_returns: pd.Series - Series of portfolio returns - benchmark_returns: pd.Series - Series of benchmark returns - window: string - Interval used for rolling values in days. - Examples: 1d, 5d, 10d - - Returns - ------- - pd.DataFrame - DataFrame of the portfolios and the benchmarks tail ratio during different time periods - pd.Series - Series of the portfolios rolling tail ratio - pd.Series - Series of the benchmarks rolling tail ratio - """ - returns_r = portfolio_returns.rolling(window) - benchmark_returns_r = benchmark_returns.rolling(window) - - portfolio_tr = returns_r.quantile(0.95) / abs(returns_r.quantile(0.05)) - benchmark_tr = benchmark_returns_r.quantile(0.95) / abs( - benchmark_returns_r.quantile(0.05) - ) - - vals = list() - for periods in PERIODS: - period_return = filter_df_by_period(portfolio_returns, periods) - period_bench_return = filter_df_by_period(benchmark_returns, periods) - if not period_return.empty: - vals.append( - [ - round( - period_return.quantile(0.95) - / abs(period_return.quantile(0.05)), - 3, - ), - round( - period_bench_return.quantile(0.95) - / abs(period_bench_return.quantile(0.05)), - 3, - ), - ] - ) - else: - vals.append(["-", "-"]) - - tailr_period_df = pd.DataFrame( - vals, index=PERIODS, columns=["Portfolio", "Benchmark"] - ) - - return tailr_period_df, portfolio_tr, benchmark_tr - - -@log_start_end(log=logger) -def get_common_sense_ratio( - portfolio_returns: pd.Series, - historical_trade_data: pd.DataFrame, - benchmark_trades: pd.DataFrame, - benchmark_returns: pd.Series, -) -> pd.DataFrame: - """Get common sense ratio - - Parameters - ---------- - portfolio_returns: pd.Series - Series of portfolio returns - historical_trade_data: pd.DataFrame - Dataframe of historical data for the portfolios trade - benchmark_trades: pd.DataFrame - Dataframe of the benchmarks trades - benchmark_returns: pd.Series - Series of benchmark returns - - Returns - ------- - pd.DataFrame - DataFrame of the portfolios and the benchmarks common sense ratio during different time periods - """ - tail_ratio_df, _, _ = get_tail_ratio(portfolio_returns, benchmark_returns) - gaintopain_ratio_df = get_gaintopain_ratio( - historical_trade_data, benchmark_trades, benchmark_returns - ) - - vals = list() - for period in PERIODS: - vals.append( - [ - round( - tail_ratio_df.loc[period, "Portfolio"] - * gaintopain_ratio_df.loc[period, "Portfolio"], - 3, - ), - round( - tail_ratio_df.loc[period, "Benchmark"] - * gaintopain_ratio_df.loc[period, "Benchmark"], - 3, - ), - ] - ) - - csr_period_df = pd.DataFrame( - vals, index=PERIODS, columns=["Portfolio", "Benchmark"] - ) - - return csr_period_df - - -@log_start_end(log=logger) -def jensens_alpha( - portfolio_returns: pd.Series, - historical_trade_data: pd.DataFrame, - benchmark_trades: pd.DataFrame, - benchmark_returns: pd.Series, - risk_free_rate: float = 0, - window: str = "1y", -) -> Tuple[pd.DataFrame, pd.Series]: - """Get jensen's alpha - - Parameters - ---------- - portfolio_returns: pd.Series - Series of portfolio returns - historical_trade_data: pd.DataFrame - Dataframe of historical data for the portfolios trade - benchmark_trades: pd.DataFrame - Dataframe of the benchmarks trades - benchmark_returns: pd.Series - Series of benchmark returns - risk_free_rate: float - Risk free rate - window: str - Interval used for rolling values. - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y. - - Returns - ------- - pd.DataFrame - DataFrame of jensens's alpha during different time periods - pd.Series - Series of jensens's alpha data - """ - length = PERIODS_DAYS[window] - periods_d = PERIODS_DAYS - - period_cum_returns = (1.0 + portfolio_returns).rolling(window=length).agg( - lambda x: x.prod() - ) - 1 - period_cum_bench_returns = (1.0 + benchmark_returns).rolling(window=length).agg( - lambda x: x.prod() - ) - 1 - rfr_cum_returns = risk_free_rate * length / 252 - beta = rolling_beta(portfolio_returns, benchmark_returns, window) - ja_rolling = period_cum_returns - ( - rfr_cum_returns + beta * (period_cum_bench_returns - rfr_cum_returns) - ) - - benchmark_trades = benchmark_trades.set_index("Date") - vals = list() - for periods in PERIODS: - period_return = filter_df_by_period(portfolio_returns, periods) - period_bench_return = filter_df_by_period(benchmark_returns, periods) - period_historical_trade_data = filter_df_by_period( - historical_trade_data, periods - ) - period_bench_trades = filter_df_by_period(benchmark_trades, periods) - if not period_return.empty: - beta = calculate_beta(period_return, period_bench_return) - period_cum_returns = ( - period_historical_trade_data["End Value"]["Total"].iloc[-1] - / ( - period_historical_trade_data["Initial Value"]["Total"].iloc[0] - + period_historical_trade_data["Investment"]["Total"].iloc[-1] - - period_historical_trade_data["Investment"]["Total"].iloc[0] - ) - - 1 - ) - if not period_bench_trades.empty: - period_bench_total_return = ( - period_bench_trades["Benchmark Value"].sum() - / period_bench_trades["Benchmark Investment"].sum() - - 1 - ) - else: - period_bench_total_return = ( - (1 + period_bench_return).cumprod() - 1 - ).iloc[-1] - rfr_cum_returns = risk_free_rate * periods_d[periods] / 252 - vals.append( - [ - round( - period_cum_returns - - ( - rfr_cum_returns - + beta * (period_bench_total_return - rfr_cum_returns) - ), - 3, - ) - ] - ) - else: - vals.append(["-"]) - - ja_period_df = pd.DataFrame(vals, index=PERIODS, columns=["Portfolio"]) - - return ja_period_df, ja_rolling - - -@log_start_end(log=logger) -def get_calmar_ratio( - portfolio_returns: pd.Series, - historical_trade_data: pd.DataFrame, - benchmark_trades: pd.DataFrame, - benchmark_returns: pd.Series, - window: str = "3y", -) -> Tuple[pd.DataFrame, pd.Series]: - """Get calmar ratio - - Parameters - ---------- - portfolio_returns: pd.Serires - Series of portfolio returns - historical_trade_data: pd.DataFrame - Dataframe of historical data for the portfolios trade - benchmark_trades: pd.DataFrame - Dataframe of the benchmarks trades - benchmark_returns: pd.DataFrame - Series of benchmark returns - window: str - Interval used for rolling values. - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y. - - Returns - ------- - pd.DataFrame - DataFrame of calmar ratio of the benchmark and portfolio during different time periods - pd.Series - Series of calmar ratio data - """ - periods_d = PERIODS_DAYS - period_cum_returns = (1.0 + portfolio_returns).rolling(window=window).agg( - lambda x: x.prod() - ) - 1 - - # Calculate annual return - annual_return = period_cum_returns ** (1 / (int(window) / 252)) - 1 - - cr_rolling = annual_return / maximum_drawdown(portfolio_returns) - - benchmark_trades = benchmark_trades.set_index("Date") - vals = list() - for periods in PERIODS: - period_return = filter_df_by_period(portfolio_returns, periods) - period_historical_trade_data = filter_df_by_period( - historical_trade_data, periods - ) - period_bench_trades = filter_df_by_period(benchmark_trades, periods) - period_bench_return = filter_df_by_period(benchmark_returns, periods) - if (not period_return.empty) and (periods_d[periods] != 0): - period_cum_returns = ( - period_historical_trade_data["End Value"]["Total"].iloc[-1] - / ( - period_historical_trade_data["Initial Value"]["Total"].iloc[0] - + period_historical_trade_data["Investment"]["Total"].iloc[-1] - - period_historical_trade_data["Investment"]["Total"].iloc[0] - ) - - 1 - ) - if not period_bench_trades.empty: - period_bench_total_return = ( - period_bench_trades["Benchmark Value"].sum() - / period_bench_trades["Benchmark Investment"].sum() - - 1 - ) - else: - period_bench_total_return = ( - (1 + period_bench_return).cumprod() - 1 - ).iloc[-1] - annual_return = (1 + period_cum_returns) ** ( - 1 / (len(period_return) / 252) - ) - 1 - annual_bench_return = (1 + period_bench_total_return) ** ( - 1 / (len(period_bench_return) / 252) - ) - 1 - drawdown = maximum_drawdown(period_return) - bench_drawdown = maximum_drawdown(period_bench_return) - if (drawdown != 0) and (bench_drawdown != 0): - vals.append( - [ - round(annual_return / drawdown, 3), - round(annual_bench_return / bench_drawdown, 3), - ] - ) - else: - vals.append(["-", "-"]) - else: - vals.append(["-", "-"]) - - cr_period_df = pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"]) - - return cr_period_df, cr_rolling - - -@log_start_end(log=logger) -def get_kelly_criterion( - portfolio_returns: pd.Series, portfolio_trades: pd.DataFrame -) -> pd.DataFrame: - """Get kelly criterion - - Parameters - ---------- - portfolio_returns: pd.Series - DataFrame of portfolio returns - portfolio_trades: pd.DataFrame - DataFrame of the portfolio trades with trade return in % - - Returns - ------- - pd.DataFrame - DataFrame of kelly criterion of the portfolio during different time periods - """ - portfolio_trades["Date"] = pd.to_datetime(portfolio_trades["Date"]) - portfolio_trades = portfolio_trades.set_index("Date") - - vals: list = list() - for period in PERIODS: - period_return = filter_df_by_period(portfolio_returns, period) - period_portfolio_tr = filter_df_by_period(portfolio_trades, period) - if (not period_return.empty) and (not period_portfolio_tr.empty): - w = len(period_return[period_return > 0]) / len(period_return) - r = len( - period_portfolio_tr[period_portfolio_tr["Portfolio % Return"] > 0] - ) / len( - period_portfolio_tr[period_portfolio_tr["Type"].str.upper() != "CASH"] - ) - if r != 0: - vals.append([round(w - (1 - w) / r, 3)]) - else: - vals.append(["-"]) - else: - vals.append(["-"]) - - kc_period_df = pd.DataFrame(vals, index=PERIODS, columns=["Kelly %"]) - - return kc_period_df - - -@log_start_end(log=logger) -def get_payoff_ratio(portfolio_trades: pd.DataFrame) -> pd.DataFrame: - """Get payoff ratio - - Parameters - ---------- - portfolio_trades: pd.DataFrame - DataFrame of the portfolio trades with trade return in % and abs values - - Returns - ------- - pd.DataFrame - DataFrame of payoff ratio of the portfolio during different time periods - """ - portfolio_trades["Date"] = pd.to_datetime(portfolio_trades["Date"]) - portfolio_trades = portfolio_trades.set_index("Date") - - no_losses = False - - vals = list() - for period in PERIODS: - period_portfolio_tr = filter_df_by_period(portfolio_trades, period) - if not portfolio_trades.empty: - portfolio_wins = period_portfolio_tr[ - period_portfolio_tr["Portfolio % Return"] > 0 - ] - portfolio_loses = period_portfolio_tr[ - period_portfolio_tr["Portfolio % Return"] < 0 - ] - if portfolio_loses.empty: - vals.append(["-"]) - no_losses = True - continue - avg_w = portfolio_wins["Abs Portfolio Return"].mean() - avg_l = portfolio_loses["Abs Portfolio Return"].mean() - vals.append( - [round(avg_w / abs(avg_l), 3)] if avg_w is not np.nan else ["0"] - ) - else: - vals.append(["-"]) - - if no_losses: - console.print( - "During some time periods there were no losing trades.", - "Thus some values could not be calculated.", - ) - - pr_period_ratio = pd.DataFrame( - vals, index=PERIODS, columns=["Payoff Ratio"] - ).fillna("-") - - return pr_period_ratio - - -@log_start_end(log=logger) -def get_profit_factor(portfolio_trades: pd.DataFrame) -> pd.DataFrame: - """Get profit factor - - Parameters - ---------- - portfolio_trades: pd.DataFrame - DataFrame of the portfolio trades with trade return in % and abs values - - Returns - ------- - pd.DataFrame - DataFrame of profit factor of the portfolio during different time periods - """ - portfolio_trades["Date"] = pd.to_datetime(portfolio_trades["Date"]) - portfolio_trades = portfolio_trades.set_index("Date") - - no_losses = False - - vals = list() - for period in PERIODS: - period_portfolio_tr = filter_df_by_period(portfolio_trades, period) - if not portfolio_trades.empty: - portfolio_wins = period_portfolio_tr[ - period_portfolio_tr["Portfolio % Return"] > 0 - ] - portfolio_loses = period_portfolio_tr[ - period_portfolio_tr["Portfolio % Return"] < 0 - ] - if portfolio_loses.empty: - vals.append(["-"]) - no_losses = True - continue - gross_profit = portfolio_wins["Abs Portfolio Return"].sum() - gross_loss = portfolio_loses["Abs Portfolio Return"].sum() - vals.append([round(gross_profit / abs(gross_loss), 3)]) - else: - vals.append(["-"]) - - if no_losses: - console.print( - "During some time periods there were no losing trades.", - "Thus some values could not be calculated.", - ) - - pf_period_df = pd.DataFrame(vals, index=PERIODS, columns=["Profit Factor"]).fillna( - "-" - ) - - return pf_period_df diff --git a/openbb_terminal/portfolio/portfolio_controller.py b/openbb_terminal/portfolio/portfolio_controller.py deleted file mode 100644 index b6d865e882d..00000000000 --- a/openbb_terminal/portfolio/portfolio_controller.py +++ /dev/null @@ -1,1552 +0,0 @@ -"""Portfolio Controller""" - -__docformat__ = "numpy" - -import argparse -import logging -import os -from datetime import date -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.common.quantitative_analysis import qa_view -from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.portfolio import ( - attribution_model, - portfolio_helper, - portfolio_view, - statics, -) -from openbb_terminal.portfolio.portfolio_model import generate_portfolio -from openbb_terminal.rich_config import MenuText, console - -try: - from openbb_terminal.portfolio.portfolio_optimization import po_controller - - OPTIMIZATION_TOOLKIT_ENABLED = True -except ModuleNotFoundError: - OPTIMIZATION_TOOLKIT_ENABLED = False - console.print( - "[yellow]" - "Portfolio Optimization Toolkit is disabled. " - "To use the Optimization features please install the toolkit following the " - "instructions here: https://github.com/OpenBB-finance/OpenBBTerminal/" - "blob/main/openbb_terminal/README.md#anaconda--python" - "\n" - "[/yellow]" - ) - -# pylint: disable=R1710,E1101,C0415,W0212,too-many-function-args,C0302,too-many-instance-attributes - -logger = logging.getLogger(__name__) - - -class PortfolioController(BaseController): - """Portfolio Controller class""" - - CHOICES_COMMANDS = [ - "load", - "show", - "bench", - "alloc", - "attrib", - "perf", - "yret", - "mret", - "dret", - "distr", - "holdv", - "holdp", - "maxdd", - "var", - "es", - "om", - "rvol", - "rsharpe", - "rsort", - "rbeta", - "metric", - "summary", - ] - CHOICES_MENUS = [ - "po", - ] - VALID_DISTRIBUTIONS = ["laplace", "student_t", "logistic", "normal"] - AGGREGATION_METRICS = ["assets", "sectors", "countries", "regions"] - VALID_METRICS = [ - "volatility", - "sharpe", - "sortino", - "maxdrawdown", - "rsquare", - "skew", - "kurtosis", - "gaintopain", - "trackerr", - "information", - "tail", - "commonsense", - "jensens", - "calmar", - "kelly", - "payoff", - "profitfactor", - ] - PERIODS = ["3y", "5y", "10y", "all"] - PATH = "/portfolio/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - self.file_types = ["xlsx", "csv"] - - self.DEFAULT_HOLDINGS_PATH = portfolio_helper.DEFAULT_HOLDINGS_PATH - - self.DATA_HOLDINGS_FILES = { - filepath.name: filepath - for file_type in self.file_types - for filepath in self.DEFAULT_HOLDINGS_PATH.rglob(f"*.{file_type}") - } - - self.portfolio_df = pd.DataFrame( - columns=[ - "Date", - "Name", - "Type", - "Sector", - "Industry", - "Country", - "Price", - "Quantity", - "Fees", - "Premium", - "Investment", - "Side", - "Currency", - ] - ) - - self.portfolio_name: str = "" - self.benchmark_name: str = "" - self.original_benchmark_ticker = "" - self.recalculate_alloc = False - self.risk_free_rate = 0 - self.portlist: List[str] = os.listdir(self.DEFAULT_HOLDINGS_PATH) - self.portfolio = None - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - self.update_choices() - choices: dict = self.choices_default - self.choices = choices - self.choices["bench"] = { - "--benchmark": {c: None for c in statics.BENCHMARK_CHOICES}, - "-b": "--benchmark", - } - self.completer = NestedCompleter.from_nested_dict(choices) - - def update_choices(self): - self.DEFAULT_HOLDINGS_PATH = portfolio_helper.DEFAULT_HOLDINGS_PATH - - self.DATA_HOLDINGS_FILES.update( - { - filepath.name: filepath - for file_type in self.file_types - for filepath in self.DEFAULT_HOLDINGS_PATH.rglob(f"*.{file_type}") - } - ) - - def print_help(self): - """Print help""" - mt = MenuText("portfolio/") - mt.add_menu("po") - mt.add_raw("\n") - - mt.add_cmd("load") - mt.add_cmd("show") - mt.add_cmd("bench") - mt.add_raw("\n") - mt.add_param("_loaded", self.portfolio_name) - mt.add_param("_riskfreerate", f"{self.risk_free_rate}%") - mt.add_param("_benchmark", self.benchmark_name) - mt.add_raw("\n") - - mt.add_info("_graphs_") - mt.add_cmd("holdv", self.portfolio_name and self.benchmark_name) - mt.add_cmd("holdp", self.portfolio_name and self.benchmark_name) - mt.add_cmd("yret", self.portfolio_name and self.benchmark_name) - mt.add_cmd("mret", self.portfolio_name and self.benchmark_name) - mt.add_cmd("dret", self.portfolio_name and self.benchmark_name) - mt.add_cmd("distr", self.portfolio_name and self.benchmark_name) - mt.add_cmd("maxdd", self.portfolio_name and self.benchmark_name) - mt.add_cmd("rvol", self.portfolio_name and self.benchmark_name) - mt.add_cmd("rsharpe", self.portfolio_name and self.benchmark_name) - mt.add_cmd("rsort", self.portfolio_name and self.benchmark_name) - mt.add_cmd("rbeta", self.portfolio_name and self.benchmark_name) - - mt.add_info("_metrics_") - mt.add_cmd("summary", self.portfolio_name and self.benchmark_name) - mt.add_cmd("alloc", self.portfolio_name and self.benchmark_name) - mt.add_cmd("attrib", self.portfolio_name and self.benchmark_name) - mt.add_cmd("metric", self.portfolio_name and self.benchmark_name) - mt.add_cmd("perf", self.portfolio_name and self.benchmark_name) - - mt.add_info("_risk_") - mt.add_cmd("var", self.portfolio_name and self.benchmark_name) - mt.add_cmd("es", self.portfolio_name and self.benchmark_name) - mt.add_cmd("om", self.portfolio_name and self.benchmark_name) - - console.print(text=mt.menu_text, menu="Portfolio - Portfolio Optimization") - self.update_choices() - - def custom_reset(self): - """Class specific component of reset command""" - objects_to_reload = ["portfolio"] - if self.portfolio_name: - objects_to_reload.append(f"load {self.portfolio_name}") - if self.original_benchmark_ticker: - objects_to_reload.append(f'bench "{self.original_benchmark_ticker}"') - return objects_to_reload - - @log_start_end(log=logger) - def call_po(self, _): - """Process po command""" - if OPTIMIZATION_TOOLKIT_ENABLED: - if self.portfolio is None: - tickers = [] - categories = None - else: - tickers = self.portfolio.tickers_list - transactions = self.portfolio.get_transactions() - - categories = { - "ASSET_CLASS": {}, - "SECTOR": {}, - "INDUSTRY": {}, - "COUNTRY": {}, - "CURRENT_INVESTED_AMOUNT": {}, - "CURRENCY": {}, - } - for _, transaction in transactions.iterrows(): - if transaction["Ticker"] not in categories["ASSET_CLASS"]: - categories["ASSET_CLASS"][transaction["Ticker"]] = transaction[ - "Type" - ] - categories["SECTOR"][transaction["Ticker"]] = transaction[ - "Sector" - ] - categories["INDUSTRY"][transaction["Ticker"]] = transaction[ - "Industry" - ] - categories["COUNTRY"][transaction["Ticker"]] = transaction[ - "Country" - ] - categories["CURRENT_INVESTED_AMOUNT"][transaction["Ticker"]] = ( - transactions[ - transactions["Ticker"] == transaction["Ticker"] - ]["Investment"].sum() - ) - categories["CURRENCY"][transaction["Ticker"]] = transaction[ - "Currency" - ] - - self.queue = self.load_class( - po_controller.PortfolioOptimizationController, - tickers, - None, - categories, - self.queue, - ) - else: - console.print("[yellow]Portfolio Optimization Toolkit is disabled[/yellow]") - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - """Process load command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Load your portfolio transactions.", - ) - parser.add_argument( - "-f", - "--file", - type=str, - dest="file", - help="The file to be loaded", - choices={c: {} for c in self.DATA_HOLDINGS_FILES}, - metavar="FILE", - ) - parser.add_argument( - "-n", - "--name", - type=str, - dest="name", - help="The name that you wish to give to your portfolio", - ) - parser.add_argument( - "-r", - "--rfr", - type=float, - default=0, - dest="risk_free_rate", - help="Set the risk free rate.", - ) - parser.add_argument( - "-e", - "--example", - help="Run an example holdings file to understand how the portfolio menu can be used.", - dest="example", - action="store_true", - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-f") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser and ns_parser.file or ns_parser and ns_parser.example: - if ns_parser.example: - file_location = ( - MISCELLANEOUS_DIRECTORY / "portfolio" / "holdings_example.xlsx" - ) - console.print( - "[green]Loading an example, please type `about` " - "to learn how to create your own Portfolio Excel sheet.[/green]\n" - ) - elif ns_parser.file in self.DATA_HOLDINGS_FILES: - file_location = self.DATA_HOLDINGS_FILES[ns_parser.file] - else: - file_location = ns_parser.file # type: ignore - - self.portfolio = generate_portfolio( - transactions_file_path=str(file_location), - benchmark_symbol="SPY", - risk_free_rate=ns_parser.risk_free_rate / 100, - ) - - if ns_parser.name: - self.portfolio_name = ns_parser.name - elif ns_parser.example: - self.portfolio_name = "OpenBB Example Portfolio" - else: - self.portfolio_name = ns_parser.file - console.print( - f"\n[bold][param]Portfolio:[/param][/bold] {self.portfolio_name}" - ) - - self.risk_free_rate = ns_parser.risk_free_rate - console.print( - f"[bold][param]Risk Free Rate:[/param][/bold] {self.risk_free_rate}%" - ) - - self.benchmark_name = "SPDR S&P 500 ETF Trust (SPY)" - console.print( - f"[bold][param]Benchmark:[/param][/bold] {self.benchmark_name}" - ) - - @log_start_end(log=logger) - def call_show(self, other_args: List[str]): - """Process show command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="show", - description="Show transactions table", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - limit=10, - ) - if ns_parser and self.portfolio is not None: - portfolio_view.display_transactions( - self.portfolio, - show_index=False, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bench(self, other_args: List[str]): - """Process bench command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bench", - description="Load in a benchmark from a selected list or set your own based on the ticker.", - ) - parser.add_argument( - "-b", - "--benchmark", - type=str, - default="SPY", - dest="benchmark", - required="-h" not in other_args, - help="Set the benchmark for the portfolio. By default, this is SPDR S&P 500 ETF Trust (SPY).", - metavar="BENCHMARK", - ) - parser.add_argument( - "-s", - "--full_shares", - action="store_true", - default=False, - dest="full_shares", - help="Whether to only make a trade with the benchmark when a full share can be bought (no partial shares).", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-b") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if self.portfolio is None: - console.print( - "[red]Please first load transactions file using 'load'[/red]" - ) - elif self.portfolio.set_benchmark( - ns_parser.benchmark, ns_parser.full_shares - ): - self.benchmark_name = statics.BENCHMARK_CHOICES.get( - ns_parser.benchmark, ns_parser.benchmark - ) - self.original_benchmark_ticker = ns_parser.benchmark - self.recalculate_alloc = True - - @log_start_end(log=logger) - def call_alloc(self, other_args: List[str]): - """Process alloc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="alloc", - description=""" - Show your allocation to each asset or sector compared to the benchmark. - """, - ) - parser.add_argument( - "-a", - "--agg", - default="assets", - choices=self.AGGREGATION_METRICS, - dest="agg", - help="The type of allocation aggregation you wish to do", - metavar="AGG", - ) - parser.add_argument( - "-t", - "--tables", - action="store_true", - default=False, - dest="tables", - help="Whether to also include the assets/sectors tables of both the benchmark and the portfolio.", - ) - if other_args and other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-a") - - ns_parser = self.parse_known_args_and_warn(parser, other_args, limit=10) - - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - if ns_parser.agg == "assets": - portfolio_view.display_assets_allocation( - portfolio_engine=self.portfolio, - limit=ns_parser.limit, - tables=ns_parser.tables, - recalculate=self.recalculate_alloc, - ) - elif ns_parser.agg == "sectors": - portfolio_view.display_sectors_allocation( - portfolio_engine=self.portfolio, - limit=ns_parser.limit, - tables=ns_parser.tables, - recalculate=self.recalculate_alloc, - ) - elif ns_parser.agg == "countries": - portfolio_view.display_countries_allocation( - portfolio_engine=self.portfolio, - limit=ns_parser.limit, - tables=ns_parser.tables, - recalculate=self.recalculate_alloc, - ) - elif ns_parser.agg == "regions": - portfolio_view.display_regions_allocation( - portfolio_engine=self.portfolio, - limit=ns_parser.limit, - tables=ns_parser.tables, - recalculate=self.recalculate_alloc, - ) - else: - console.print( - f"{ns_parser.agg} is not an available option. The options " - f"are: {', '.join(self.AGGREGATION_METRICS)}" - ) - - @log_start_end(log=logger) - def call_attrib(self, other_args: List[str]): - """Process attrib command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="attrib", - description=""" - Displays sector attribution of the portfolio compared to the S&P 500 - """, - ) - parser.add_argument( - "-p", - "--period", - type=str, - choices=statics.PERIODS, - dest="period", - default="all", - help="Period in which to calculate attribution", - ) - parser.add_argument( - "-t", - "--type", - type=str, - choices=["relative", "absolute"], - dest="type", - default="relative", - help="Select between relative or absolute attribution values", - ) - parser.add_argument( - "--raw", - type=bool, - dest="raw", - default=False, - const=True, - nargs="?", - help="View raw attribution values in a table", - ) - - if other_args and other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-a") - - ns_parser = self.parse_known_args_and_warn(parser, other_args, limit=10) - - if ns_parser and self.portfolio is not None: - if check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ): - if self.benchmark_name != "SPDR S&P 500 ETF Trust (SPY)": - console.print( - "This feature uses S&P 500 as benchmark and will disregard selected benchmark if different." - ) - # sector contribution - end_date = date.today() - # set correct time period - if ns_parser.period == "all": - start_date = self.portfolio.inception_date - else: - start_date = portfolio_helper.get_start_date_from_period( - ns_parser.period - ) - - # calculate benchmark and portfolio contribution values - bench_result = attribution_model.get_spy_sector_contributions( - start_date, end_date - ) - if bench_result.empty: - return - portfolio_result = attribution_model.get_portfolio_sector_contributions( - start_date, self.portfolio.portfolio_trades - ) - if portfolio_result.empty: - return - - # relative results - the proportions of return attribution - if ns_parser.type == "relative": - categorization_result = ( - attribution_model.percentage_attrib_categorizer( - bench_result, portfolio_result - ) - ) - - portfolio_view.display_attribution_categorization( - display=categorization_result, - time_period=ns_parser.period, - attrib_type="Contributions as % of PF", - plot_fields=["S&P500 [%]", "Portfolio [%]"], - show_table=ns_parser.raw, - ) - - # absolute - the raw values of return attribution - if ns_parser.type == "absolute": - categorization_result = attribution_model.raw_attrib_categorizer( - bench_result, portfolio_result - ) - - portfolio_view.display_attribution_categorization( - display=categorization_result, - time_period=ns_parser.period, - attrib_type="Raw contributions (Return x PF Weight)", - plot_fields=["S&P500", "Portfolio"], - show_table=ns_parser.raw, - ) - - console.print() - - @log_start_end(log=logger) - def call_perf(self, other_args: List[str]): - """Process performance command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="performance", - description=""" - Shows performance of each trade and total performance of the portfolio versus the benchmark. - """, - ) - parser.add_argument( - "-t", - "--show_trades", - action="store_true", - default=False, - dest="show_trades", - help="Whether to show performance on all trades in comparison to the benchmark.", - ) - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_performance_vs_benchmark( - self.portfolio, - ns_parser.show_trades, - ) - - @log_start_end(log=logger) - def call_holdv(self, other_args: List[str]): - """Process holdv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="holdv", - description="Display holdings of assets (absolute value)", - ) - parser.add_argument( - "-u", - "--unstack", - action="store_true", - default=False, - dest="unstack", - help="Sum all assets value over time", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10, - ) - if ns_parser and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ): - portfolio_view.display_holdings_value( - self.portfolio, - ns_parser.unstack, - ns_parser.raw, - ns_parser.limit, - ns_parser.export, - ns_parser.sheet_name, - ) - - @log_start_end(log=logger) - def call_holdp(self, other_args: List[str]): - """Process holdp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="holdp", - description="Display holdings of assets (in percentage)", - ) - parser.add_argument( - "-u", - "--unstack", - action="store_true", - default=False, - dest="unstack", - help="Sum all assets percentage over time", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10, - ) - if ns_parser and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ): - portfolio_view.display_holdings_percentage( - self.portfolio, - ns_parser.unstack, - ns_parser.raw, - ns_parser.limit, - ns_parser.export, - ns_parser.sheet_name, - ) - - @log_start_end(log=logger) - def call_var(self, other_args: List[str]): - """Process var command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="var", - description=""" - Provides value at risk (short: VaR) of the selected portfolio. - """, - ) - parser.add_argument( - "-m", - "--mean", - action="store_true", - default=True, - dest="use_mean", - help="If one should use the mean of the portfolio return", - ) - parser.add_argument( - "-a", - "--adjusted", - action="store_true", - default=False, - dest="adjusted", - help=""" - If the VaR should be adjusted for skew and kurtosis (Cornish-Fisher-Expansion) - """, - ) - parser.add_argument( - "-s", - "--student", - action="store_true", - default=False, - dest="student_t", - help=""" - If one should use the student-t distribution - """, - ) - parser.add_argument( - "-p", - "--percentile", - action="store", - dest="percentile", - type=float, - default=99.9, - help=""" - Percentile used for VaR calculations, for example input 99.9 equals a 99.9 Percent VaR - """, - ) - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser and self.portfolio is not None: - if self.portfolio_name: - if ns_parser.adjusted and ns_parser.student_t: - console.print( - "Select either the adjusted or the student_t parameter.\n" - ) - else: - portfolio_view.display_var( - portfolio_engine=self.portfolio, - use_mean=ns_parser.use_mean, - adjusted_var=ns_parser.adjusted, - student_t=ns_parser.student_t, - percentile=ns_parser.percentile, - ) - else: - console.print( - "[red]Please first define the portfolio using 'load'[/red]\n" - ) - - @log_start_end(log=logger) - def call_es(self, other_args: List[str]): - """Process es command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="es", - description=""" - Provides Expected Shortfall (short: ES) of the selected portfolio. - """, - ) - parser.add_argument( - "-m", - "--mean", - action="store_true", - default=True, - dest="use_mean", - help="If one should use the mean of the portfolios return", - ) - parser.add_argument( - "-d", - "--dist", - dest="distribution", - type=str, - choices=self.VALID_DISTRIBUTIONS, - default="normal", - help="Distribution used for the calculations", - metavar="DIST", - ) - parser.add_argument( - "-p", - "--percentile", - action="store", - dest="percentile", - type=float, - default=99.9, - help=""" - Percentile used for ES calculations, for example input 99.9 equals a 99.9 Percent Expected Shortfall - """, - ) - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if self.portfolio and self.portfolio_name: - portfolio_view.display_es( - portfolio_engine=self.portfolio, - use_mean=ns_parser.use_mean, - distribution=ns_parser.distribution, - percentile=ns_parser.percentile, - ) - else: - console.print( - "[red]Please first define the portfolio using 'load'[/red]\n" - ) - - @log_start_end(log=logger) - def call_om(self, other_args: List[str]): - """Process om command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="om", - description=""" - Provides omega ratio of the selected portfolio. - """, - ) - parser.add_argument( - "-s", - "--start", - action="store", - dest="start", - type=float, - default=0, - help=""" - Start of the omega ratio threshold - """, - ) - parser.add_argument( - "-e", - "--end", - action="store", - dest="end", - type=float, - default=1.5, - help=""" - End of the omega ratio threshold - """, - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser and self.portfolio is not None: - if self.portfolio_name: - data = self.portfolio.portfolio_returns[1:] - qa_view.display_omega( - data, - ns_parser.start, - ns_parser.end, - ) - else: - console.print( - "[red]Please define the portfolio first (via 'load')[/red]\n" - ) - - @log_start_end(log=logger) - def call_yret(self, other_args: List[str]): - """Process yret command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="yret", - description="End of the year returns", - ) - parser.add_argument( - "-p", - "--period", - type=str, - dest="period", - default="all", - choices=self.PERIODS, - help="Period to select start end of the year returns", - metavar="PERIOD", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - raw=True, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - ) - - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_yearly_returns( - self.portfolio, - ns_parser.period, - ns_parser.raw, - ns_parser.export, - ns_parser.sheet_name, - ) - - @log_start_end(log=logger) - def call_mret(self, other_args: List[str]): - """Process mret command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mret", - description="Monthly returns", - ) - parser.add_argument( - "-p", - "--period", - type=str, - dest="period", - default="all", - choices=self.PERIODS, - help="Period to select start end of the year returns", - metavar="PERIOD", - ) - parser.add_argument( - "-i", - "--instrument", - type=str, - dest="instrument", - default="both", - choices=["both", "portfolio", "benchmark"], - help="Whether to show portfolio or benchmark monthly returns. By default both are shown in one table.", - ) - parser.add_argument( - "-g", - "--graph", - action="store_true", - default=False, - dest="graph", - help="Plot the monthly returns on a heatmap", - ) - parser.add_argument( - "-s", - "--show", - action="store_true", - default=False, - dest="show_vals", - help="Show monthly returns on heatmap", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - raw=True, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_monthly_returns( - self.portfolio, - ns_parser.period, - ns_parser.instrument, - ns_parser.graph, - ns_parser.show_vals, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dret(self, other_args: List[str]): - """Process dret command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dret", - description="Daily returns", - ) - parser.add_argument( - "-p", - "--period", - type=str, - dest="period", - default="all", - choices=self.PERIODS, - help="Period to select start end of the year returns", - metavar="PERIOD", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - raw=True, - limit=10, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - ) - - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_daily_returns( - self.portfolio, - ns_parser.period, - ns_parser.raw, - ns_parser.limit, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_maxdd(self, other_args: List[str]): - """Process maxdd command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="maxdd", - description="Show portfolio maximum drawdown", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_maximum_drawdown( - self.portfolio, - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_rvol(self, other_args: List[str]): - """Process rolling volatility command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rvol", - description="Show rolling volatility portfolio vs benchmark", - ) - parser.add_argument( - "-p", - "--period", - type=str, - dest="period", - default="1y", - choices=statics.PERIODS, - help="Period to apply rolling window", - metavar="PERIOD", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_rolling_volatility( - self.portfolio, - window=ns_parser.period, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rsharpe(self, other_args: List[str]): - """Process rolling sharpe command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rsharpe", - description="Show rolling sharpe portfolio vs benchmark", - ) - parser.add_argument( - "-p", - "--period", - type=str, - dest="period", - default="1y", - choices=statics.PERIODS, - help="Period to apply rolling window", - metavar="PERIOD", - ) - parser.add_argument( - "-r", - "--rfr", - type=float, - dest="risk_free_rate", - default=self.risk_free_rate, - help="Set risk free rate for calculations.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_rolling_sharpe( - self.portfolio, - risk_free_rate=ns_parser.risk_free_rate / 100, - window=ns_parser.period, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rsort(self, other_args: List[str]): - """Process rolling sortino command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rsort", - description="Show rolling sortino portfolio vs benchmark", - ) - parser.add_argument( - "-p", - "--period", - type=str, - dest="period", - default="1y", - choices=statics.PERIODS, - help="Period to apply rolling window", - metavar="PERIOD", - ) - parser.add_argument( - "-r", - "--rfr", - type=float, - dest="risk_free_rate", - default=self.risk_free_rate, - help="Set risk free rate for calculations.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_rolling_sortino( - portfolio_engine=self.portfolio, - risk_free_rate=ns_parser.risk_free_rate / 100, - window=ns_parser.period, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rbeta(self, other_args: List[str]): - """Process rolling beta command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rbeta", - description="Show rolling beta portfolio vs benchmark", - ) - parser.add_argument( - "-p", - "--period", - type=str, - dest="period", - default="1y", - choices=statics.PERIODS, - help="Period to apply rolling window", - metavar="PERIOD", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_rolling_beta( - self.portfolio, - window=ns_parser.period, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_metric(self, other_args: List[str]): - """Process metric command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="metric", - description="Display metric of choice for different periods", - ) - parser.add_argument( - "-m", - "--metric", - type=str, - dest="metric", - default="-h" not in other_args, - choices=self.VALID_METRICS, - help="Set metric of choice", - metavar="METRIC", - ) - parser.add_argument( - "-r", - "--rfr", - type=float, - dest="risk_free_rate", - default=self.risk_free_rate, - help="Set risk free rate for calculations.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-m") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ): - if ns_parser.metric == "skew": - portfolio_view.display_skewness( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "kurtosis": - portfolio_view.display_kurtosis( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "volatility": - portfolio_view.display_volatility( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "sharpe": - portfolio_view.display_sharpe_ratio( - self.portfolio, - ns_parser.risk_free_rate / 100, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "sortino": - portfolio_view.display_sortino_ratio( - self.portfolio, - ns_parser.risk_free_rate / 100, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "maxdrawdown": - portfolio_view.display_maximum_drawdown_ratio( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "rsquare": - portfolio_view.display_rsquare( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "gaintopain": - portfolio_view.display_gaintopain_ratio( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "trackerr": - portfolio_view.display_tracking_error( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "information": - portfolio_view.display_information_ratio( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "tail": - portfolio_view.display_tail_ratio( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "commonsense": - portfolio_view.display_common_sense_ratio( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "jensens": - portfolio_view.display_jensens_alpha( - self.portfolio, - ns_parser.risk_free_rate / 100, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "calmar": - portfolio_view.display_calmar_ratio( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "kelly": - portfolio_view.display_kelly_criterion( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "payoff" and self.portfolio is not None: - portfolio_view.display_payoff_ratio( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.metric == "profitfactor" and self.portfolio is not None: - portfolio_view.display_profit_factor( - self.portfolio, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_distr(self, other_args: List[str]): - """Process distr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="distr", - description="Compute distribution of daily returns", - ) - parser.add_argument( - "-p", - "--period", - type=str, - choices=statics.PERIODS, - dest="period", - default="all", - help="The file to be loaded", - metavar="PERIOD", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - raw=True, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - ) - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_distribution_returns( - self.portfolio, - ns_parser.period, - ns_parser.raw, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_summary(self, other_args: List[str]): - """Process summary command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="summary", - description="Display summary of portfolio vs benchmark", - ) - parser.add_argument( - "-p", - "--period", - type=str, - choices=statics.PERIODS, - dest="period", - default="all", - help="The file to be loaded", - metavar="PERIOD", - ) - parser.add_argument( - "-r", - "--rfr", - type=float, - dest="risk_free_rate", - default=self.risk_free_rate, - help="Set risk free rate for calculations.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - if ( - ns_parser - and self.portfolio is not None - and check_portfolio_benchmark_defined( - self.portfolio_name, self.benchmark_name - ) - ): - portfolio_view.display_summary( - self.portfolio, - ns_parser.period, - ns_parser.risk_free_rate / 100, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - -def check_portfolio_benchmark_defined(portfolio_name: str, benchmark_name: str) -> bool: - """Check that portfolio and benchmark have been defined - - Parameters - ---------- - portfolio_name: str - Portfolio name, will be empty if not defined - benchmark_name: str - Benchmark name, will be empty if not defined - - Returns - ------- - bool - If both portfolio and benchmark have been defined - """ - - if not portfolio_name: - console.print("[red]Please first define the portfolio (via 'load')[/red]\n") - return False - - if not benchmark_name: - console.print("[red]Please first define the benchmark (via 'bench')[/red]\n") - return False - - return True diff --git a/openbb_terminal/portfolio/portfolio_engine.py b/openbb_terminal/portfolio/portfolio_engine.py deleted file mode 100644 index fd29c364e73..00000000000 --- a/openbb_terminal/portfolio/portfolio_engine.py +++ /dev/null @@ -1,1006 +0,0 @@ -"""Portfolio Engine""" - -__docformat__ = "numpy" - -import datetime -import logging -import warnings -from typing import Any, Dict - -import numpy as np -import pandas as pd -import yfinance as yf -from tqdm import tqdm - -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.decorators import log_start_end -from openbb_terminal.portfolio.allocation_model import get_allocation -from openbb_terminal.portfolio.portfolio_helper import ( - get_info_from_ticker, - make_equal_length, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis.yahoo_finance_model import get_splits -from openbb_terminal.terminal_helper import suppress_stdout - -# pylint: disable=E1136,W0201,R0902,C0302 -# pylint: disable=unsupported-assignment-operation,redefined-outer-name -# pylint: too-many-public-methods, consider-using-f-string,disable=raise-missing-from - -logger = logging.getLogger(__name__) - -pd.options.mode.chained_assignment = None - - -class PortfolioEngine: - """Class for portfolio analysis in OpenBB - - Implements a Portfolio and related methods. - - Attributes - ---------- - # General - empty: bool - True if no transactions have been loaded - risk_free_rate: float - Risk free rate - inception_date: datetime.date - Inception date of the portfolio - tickers_list: list - List of tickers in the portfolio - tickers: Dict[Any, Any] - Dictionary of ticker type (e.g. ETF, STOCK, CRYPTO) as keys and ticker - list as values - benchmark_ticker: str - Benchmark ticker - benchmark_info: Any - Benchmark info - historical_trade_data: pd.DataFrame - DataFrame with state of the portfolio at each day since inception, including - trade quantity, invested amount, return, etc. - - # Portfolio - portfolio_historical_prices: pd.DataFrame - DataFrame with historical prices of the ticker in the portfolio - portfolio_returns: pd.DataFrame - DataFrame with portfolio returns, i.e. the total daily return of the portfolio - portfolio_trades: pd.DataFrame - DataFrame with portfolio trades and respective performance - portfolio_assets_allocation: pd.DataFrame - DataFrame with portfolio assets allocation - portfolio_sectors_allocation: pd.DataFrame - DataFrame with portfolio sectors allocation - portfolio_regions_allocation: pd.DataFrame - DataFrame with portfolio regions allocation - portfolio_countries_allocation: pd.DataFrame - DataFrame with portfolio countries allocation - - # Benchmark - benchmark_historical_prices: pd.DataFrame - DataFrame with historical prices of the benchmark ticker - benchmark_returns: pd.DataFrame - DataFrame with benchmark returns, i.e. the total daily return of the benchmark - benchmark_trades: pd.DataFrame - DataFrame with benchmark trades and respective performance - benchmark_assets_allocation: pd.DataFrame - DataFrame with benchmark assets allocation - benchmark_sectors_allocation: pd.DataFrame - DataFrame with benchmark sectors allocation - benchmark_regions_allocation: pd.DataFrame - DataFrame with benchmark regions allocation - benchmark_countries_allocation: pd.DataFrame - DataFrame with benchmark countries allocation - - Methods - ------- - read_transactions: Static method to read transactions from file - __set_transactions: - __preprocess_transactions: Method to preprocess, format and compute auxiliary fields - __load_company_data: Load company data for stocks such as sector, industry and country - get_transactions: Outputs the formatted transactions DataFrame - set_benchmark: Adds benchmark ticker, info, prices and returns - __mimic_trades_for_benchmark: Mimic trades from the transactions based on chosen benchmark assuming partial shares - generate_portfolio_data: Generates portfolio data from transactions - __load_portfolio_historical_prices: Loads historical adj close prices for tickers in list of trades - __populate_historical_trade_data: Create a new dataframe to store historical prices by ticker - __calculate_portfolio_returns: Calculate portfolio daily returns - __calculate_portfolio_performance: Calculate portfolio trades performance - set_risk_free_rate: Sets risk free rate - calculate_reserves: Takes dividends into account for returns calculation - calculate_allocation: Determine allocation based on assets, sectors, countries and regions. - """ - - def __init__(self, transactions: pd.DataFrame = pd.DataFrame()): - """Initialize PortfolioEngine class""" - - # General - self.empty = True - self.risk_free_rate = float(0) - self.inception_date = datetime.date(1970, 1, 1) - self.tickers_list = None - self.tickers: Dict[Any, Any] = {} - self.benchmark_ticker: str = "" - self.historical_trade_data = pd.DataFrame() - - # Portfolio - self.portfolio_historical_prices = pd.DataFrame() - self.portfolio_returns = pd.DataFrame() - self.portfolio_trades = pd.DataFrame() - self.portfolio_assets_allocation = pd.DataFrame() - self.portfolio_sectors_allocation = pd.DataFrame() - self.portfolio_regions_allocation = pd.DataFrame() - self.portfolio_countries_allocation = pd.DataFrame() - - # Benchmark - self.benchmark_historical_prices = pd.DataFrame() - self.benchmark_returns = pd.DataFrame() - self.benchmark_trades = pd.DataFrame() - self.benchmark_assets_allocation = pd.DataFrame() - self.benchmark_sectors_allocation = pd.DataFrame() - self.benchmark_regions_allocation = pd.DataFrame() - self.benchmark_countries_allocation = pd.DataFrame() - - # Set and preprocess transactions - if not transactions.empty: - self.__set_transactions(transactions) - - def __set_transactions(self, transactions): - self.__transactions = transactions - self.__preprocess_transactions() - self.empty = False - - def get_transactions(self): - """Get formatted transactions - - Returns - ------- - pd.DataFrame - Formatted transactions - """ - - df = self.__transactions[ - [ - "Date", - "Type", - "Ticker", - "Side", - "Price", - "Quantity", - "Fees", - "Investment", - "Currency", - "Sector", - "Industry", - "Country", - "Region", - ] - ] - df = df.replace(np.nan, "-") - df["Date"] = df["Date"].dt.strftime("%Y-%m-%d") - df.sort_values(by="Date", ascending=False, inplace=True) - return df - - @staticmethod - def read_transactions(path: str) -> pd.DataFrame: - """Read static method to read transactions from file. - - Parameters - ---------- - path: str - path to transactions file - - Returns - ------- - pd.DataFrame - DataFrame with transactions - """ - - # Load transactions from file - if path.endswith(".xlsx"): - if not get_current_system().DEBUG_MODE: - warnings.filterwarnings( - "ignore", category=UserWarning, module="openpyxl" - ) - transactions = pd.read_excel(path) - elif path.endswith(".csv"): - transactions = pd.read_csv(path) - - return transactions - - @log_start_end(log=logger) - def __preprocess_transactions(self): - """Preprocess, format and compute auxiliary fields. - - Preprocessing steps: - 0. If optional fields not in the transactions add missing - 1. Convert Date to datetime - 2. Sort transactions by date - 3. Capitalize Ticker and Type [of instrument...] - 4. Translate side: ["deposit", "buy"] -> 1 and ["withdrawal", "sell"] -> -1 - 5. Convert quantity to signed integer - 6. Determining the investment/divestment value - 7. Reformat crypto tickers to yfinance format (e.g. BTC -> BTC-USD) - 8. Reformat STOCK/ETF tickers to yfinance format if ISIN provided - 9. Remove unsupported ISINs that came out empty - 10. Create tickers dictionary with structure {'Type': [Ticker]} - 11. Create list with tickers except cash - 12. Save transactions inception date - 13. Populate fields Sector, Industry and Country - """ - - p_bar = tqdm(range(14), desc="Preprocessing transactions", leave=False) - - try: - # 0. If optional fields not in the transactions add missing - optional_fields = [ - "Sector", - "Industry", - "Country", - "Region", - "Fees", - "Premium", - "ISIN", - ] - if not set(optional_fields).issubset(set(self.__transactions.columns)): - for field in optional_fields: - if field not in self.__transactions.columns: - self.__transactions[field] = np.nan - - p_bar.n += 1 - p_bar.refresh() - - # 1. Convert Date to datetime - self.__transactions["Date"] = pd.to_datetime(self.__transactions["Date"]) - - p_bar.n += 1 - p_bar.refresh() - - # 2. Sort transactions by date - self.__transactions = self.__transactions.sort_values(by="Date") - - p_bar.n += 1 - p_bar.refresh() - - # 3. Capitalize Ticker and Type [of instrument...] - self.__transactions["Ticker"] = self.__transactions["Ticker"].map( - lambda x: x.upper() if isinstance(x, str) else x - ) - self.__transactions["Type"] = self.__transactions["Type"].map( - lambda x: x.upper() if isinstance(x, str) else x - ) - - p_bar.n += 1 - p_bar.refresh() - - # 4. Translate side: ["deposit", "buy"] -> 1 and ["withdrawal", "sell"] -> -1 - self.__transactions["Signal"] = self.__transactions["Side"].map( - lambda x: ( - 1 - if x.lower() in ["deposit", "buy"] - else (-1 if x.lower() in ["withdrawal", "sell"] else 0) - ) - ) - - p_bar.n += 1 - p_bar.refresh() - - # 5. Convert quantity to signed integer - self.__transactions["Quantity"] = ( - abs(self.__transactions["Quantity"]) * self.__transactions["Signal"] - ) - - # Adjust quantity and price for splits - for ticker in self.__transactions["Ticker"].unique(): - try: - splits_df = get_splits(ticker) - if not splits_df.empty: - splits_df = splits_df.tz_localize(tz=None) - for split_date in splits_df.index: - self.__transactions["Quantity"] = np.where( - (self.__transactions["Ticker"] == ticker) - & (self.__transactions["Date"] < split_date), - self.__transactions["Quantity"] - * splits_df.loc[split_date].values, - self.__transactions["Quantity"], - ) - self.__transactions["Price"] = np.where( - (self.__transactions["Ticker"] == ticker) - & (self.__transactions["Date"] < split_date), - self.__transactions["Price"] - / splits_df.loc[split_date].values, - self.__transactions["Price"], - ) - - except Exception: - console.print("\nCould not get splits adjusted") - - p_bar.n += 1 - p_bar.refresh() - - # 6. Determining the investment/divestment value - self.__transactions["Investment"] = ( - self.__transactions["Quantity"] * self.__transactions["Price"] - + self.__transactions["Fees"] - ) - - p_bar.n += 1 - p_bar.refresh() - - # 7. Reformat crypto tickers to yfinance format (e.g. BTC -> BTC-USD) - crypto_trades = self.__transactions[self.__transactions.Type == "CRYPTO"] - self.__transactions.loc[ - (self.__transactions.Type == "CRYPTO"), "Ticker" - ] = [ - f"{crypto}-{currency}" - for crypto, currency in zip( - crypto_trades.Ticker, crypto_trades.Currency - ) - ] - - p_bar.n += 1 - p_bar.refresh() - - # 8. Reformat STOCK/ETF tickers to yfinance format if ISIN provided. - - # If isin not valid ticker is empty - self.__transactions["yf_Ticker"] = self.__transactions["ISIN"].apply( - lambda x: yf.utils.get_ticker_by_isin(x) if not pd.isna(x) else np.nan - ) - - empty_tickers = list( - self.__transactions[ - (self.__transactions["yf_Ticker"] == "") - | (self.__transactions["yf_Ticker"].isna()) - ]["Ticker"].unique() - ) - - # If ticker from isin is empty it is not valid in yfinance, so check if user provided ticker is supported - removed_tickers = [] - for item in empty_tickers: - with suppress_stdout(): - # Suppress yfinance failed download message if occurs - valid_ticker = not ( - yf.download( - item, - start=datetime.datetime.now() + datetime.timedelta(days=-5), - progress=False, - ).empty - ) - if valid_ticker: - # Invalid ISIN but valid ticker - self.__transactions.loc[ - self.__transactions["Ticker"] == item, "yf_Ticker" - ] = np.nan - else: - self.__transactions.loc[ - self.__transactions["Ticker"] == item, "yf_Ticker" - ] = "" - removed_tickers.append(item) - - # Merge reformatted tickers into Ticker - self.__transactions["Ticker"] = self.__transactions["yf_Ticker"].fillna( - self.__transactions["Ticker"] - ) - - p_bar.n += 1 - p_bar.refresh() - - # 9. Remove unsupported ISINs that came out empty - self.__transactions.drop( - self.__transactions[self.__transactions["Ticker"] == ""].index, - inplace=True, - ) - - p_bar.n += 1 - p_bar.refresh() - - # 10. Create tickers dictionary with structure {'Type': [Ticker]} - unsupported_type = self.__transactions[ - (~self.__transactions["Type"].isin(["STOCK", "ETF", "CRYPTO"])) - ].index - if unsupported_type.any(): - self.__transactions.drop(unsupported_type, inplace=True) - console.print( - "[red]Unsupported transaction type detected and removed. Supported types: stock, etf or crypto.[/red]" - ) - - for ticker_type in set(self.__transactions["Type"]): - self.tickers[ticker_type] = list( - set( - self.__transactions[ - self.__transactions["Type"].isin([ticker_type]) - ]["Ticker"] - ) - ) - - p_bar.n += 1 - p_bar.refresh() - - # 11. Create list with tickers except cash - self.tickers_list = list(set(self.__transactions["Ticker"])) - - p_bar.n += 1 - p_bar.refresh() - - # 12. Save transactions inception date - self.inception_date = self.__transactions["Date"].iloc[0] - - p_bar.n += 1 - p_bar.refresh() - - # 13. Populate fields Sector, Industry and Country - if ( - self.__transactions.loc[ - self.__transactions["Type"] == "STOCK", - optional_fields, - ] - .isnull() - .values.any() - ): - # If any fields is empty for stocks (overwrites any info there) - self.__load_company_data() - - p_bar.n += 1 - p_bar.refresh() - - # Warn user of removed ISINs - if removed_tickers: - p_bar.disable = True - console.print( - f"\n[red]The following tickers are not supported and were removed: {removed_tickers}." - f"\nManually edit the 'Ticker' field with the proper Yahoo Finance suffix or provide a valid ISIN." - f"\nSuffix info on 'Yahoo Finance market coverage':" - " https://help.yahoo.com/kb/exchanges-data-providers-yahoo-finance-sln2310.html" - f"\nE.g. IWDA -> IWDA.AS[/red]\n" - ) - except Exception: - console.print("\nCould not preprocess transactions.") - raise - - @log_start_end(log=logger) - def __load_company_data(self): - """Load company data for stocks such as sector, industry and country""" - - for ticker_type, ticker_list in self.tickers.items(): - # yfinance only has sector, industry and country for stocks - if ticker_type == "STOCK": - for ticker in ticker_list: - # Only gets fields for tickers with missing data - # TODO: Should only get field missing for tickers with missing data - # now it's taking the 4 of them - if ( - self.__transactions.loc[ - self.__transactions["Ticker"] == ticker, - ["Sector", "Industry", "Country", "Region"], - ] - .isnull() - .values.any() - ): - # Get ticker info in list ["Sector", "Industry", "Country", "Region"] from isin/ticker - info_list = get_info_from_ticker(ticker) - - # Replace fields in transactions - self.__transactions.loc[ - self.__transactions.Ticker == ticker, - ["Sector", "Industry", "Country", "Region"], - ] = info_list - - elif ticker_type == "CRYPTO": - for ticker in ticker_list: - if ( - self.__transactions.loc[ - self.__transactions["Ticker"] == ticker, - ["Sector", "Industry", "Country", "Region"], - ] - .isnull() - .values.any() - ): - # Get ticker info in list ["Sector", "Industry", "Country", "Region"] - info_list = ["Crypto", "Crypto", "Crypto", "Crypto"] - - # Replace fields in transactions - self.__transactions.loc[ - self.__transactions.Ticker == ticker, - ["Sector", "Industry", "Country", "Region"], - ] = info_list - - else: - for ticker in ticker_list: - if ( - self.__transactions.loc[ - self.__transactions["Ticker"] == ticker, - ["Sector", "Industry", "Country", "Region"], - ] - .isnull() - .values.any() - ): - # Get ticker info in list ["Sector", "Industry", "Country", "Region"] - info_list = ["-", "-", "-", "-"] - - # Replace fields in transactions - self.__transactions.loc[ - self.__transactions.Ticker == ticker, - ["Sector", "Industry", "Country", "Region"], - ] = info_list - - @log_start_end(log=logger) - def set_benchmark(self, symbol: str = "SPY", full_shares: bool = False) -> bool: - """Load benchmark into portfolio. - - Parameters - ---------- - symbol: str - Benchmark symbol to download data - full_shares: bool - Whether to mimic the portfolio trades exactly (partial shares) or round down the - quantity to the nearest number - - Returns - ------- - bool - True if successful, False otherwise - """ - - p_bar = tqdm(range(4), desc=" Loading benchmark", leave=False) - - self.benchmark_historical_prices = yf.download( - symbol, - start=self.inception_date - datetime.timedelta(days=1), - threads=False, - progress=False, - ignore_tz=True, - )["Adj Close"] - - if self.benchmark_historical_prices.empty: - console.print( - f"\n[red]Could not download benchmark data for {symbol}." - " Choose another symbol.\n[/red]" - ) - return False - - self.benchmark_ticker = symbol - - p_bar.n += 1 - p_bar.refresh() - - self.__mimic_trades_for_benchmark(full_shares) - - # Merge benchmark and portfolio dates to ensure same length - self.benchmark_historical_prices = pd.merge( - self.portfolio_historical_prices["Close"], - self.benchmark_historical_prices, - how="outer", - left_index=True, - right_index=True, - )["Adj Close"] - self.benchmark_historical_prices.fillna(method="ffill", inplace=True) - - p_bar.n += 1 - p_bar.refresh() - - self.benchmark_returns = self.benchmark_historical_prices.pct_change().dropna() - ( - self.portfolio_returns, - self.benchmark_returns, - ) = make_equal_length(self.portfolio_returns, self.benchmark_returns) - - p_bar.n += 1 - p_bar.refresh() - - return True - - @log_start_end(log=logger) - def __mimic_trades_for_benchmark(self, full_shares: bool = False): - """Mimic trades from the transactions based on chosen benchmark assuming partial shares - - Parameters - ---------- - full_shares: bool - whether to mimic the portfolio trades exactly (partial shares) or round down the - quantity to the nearest number. - """ - - # Create dataframe to store benchmark trades - self.benchmark_trades = self.__transactions[ - ["Date", "Type", "Investment"] - ].copy() - - # Set current price of benchmark - self.benchmark_trades["Last price"] = self.benchmark_historical_prices[-1] - - # Map historical prices into trades - self.benchmark_trades[["Benchmark Quantity"]] = float(0) - benchmark_historical_prices = pd.DataFrame(self.benchmark_historical_prices) - benchmark_historical_prices.columns.values[0] = "Trade price" - self.benchmark_trades = self.benchmark_trades.set_index("Date") - self.benchmark_trades.index = pd.to_datetime(self.benchmark_trades.index) - self.benchmark_trades = self.benchmark_trades.merge( - benchmark_historical_prices, how="left", left_index=True, right_index=True - ) - self.benchmark_trades = self.benchmark_trades.reset_index() - self.benchmark_trades["Trade price"] = self.benchmark_trades[ - "Trade price" - ].fillna(method="ffill") - - # Calculate benchmark investment quantity - if full_shares: - self.benchmark_trades["Benchmark Quantity"] = np.floor( - self.benchmark_trades["Investment"] - / self.benchmark_trades["Trade price"] - ) - else: - self.benchmark_trades["Benchmark Quantity"] = ( - self.benchmark_trades["Investment"] - / self.benchmark_trades["Trade price"] - ) - - self.benchmark_trades["Benchmark Investment"] = ( - self.benchmark_trades["Trade price"] - * self.benchmark_trades["Benchmark Quantity"] - ) - self.benchmark_trades["Benchmark Value"] = ( - self.benchmark_trades["Last price"] - * self.benchmark_trades["Benchmark Quantity"] - ) - self.benchmark_trades["Benchmark % Return"] = ( - self.benchmark_trades["Benchmark Value"] - / self.benchmark_trades["Benchmark Investment"] - - 1 - ) - self.benchmark_trades["Benchmark Abs Return"] = ( - self.benchmark_trades["Benchmark Value"] - - self.benchmark_trades["Benchmark Investment"] - ) - # TODO: To add alpha here, we must pull prices from original trades and get last price - # for each of those trades. Then just calculate returns and compare to benchmark - self.benchmark_trades.fillna(0, inplace=True) - - if full_shares: - console.print( - "Note that with full shares (-s) enabled, there will be a mismatch between how much you invested in the" - f" portfolio ({round(sum(self.portfolio_trades['Portfolio Investment']), 2)}) and how much you invested" - f" in the benchmark ({round(sum(self.benchmark_trades['Benchmark Investment']), 2)})." - ) - - @log_start_end(log=logger) - def generate_portfolio_data(self): - """Generate portfolio data from transactions - - Workflow: - 1. Load historical adj close/close prices for tickers in list of trades - 2. Record the state of the portfolio at each day since inception. - 3. Calculate portfolio daily returns from historical trade data - 4. Calculate portfolio trades performance - - """ - self.__load_portfolio_historical_prices() - self.__populate_historical_trade_data() - self.__calculate_portfolio_returns() - self.__calculate_portfolio_performance() - - @log_start_end(log=logger) - def __load_portfolio_historical_prices(self, use_close: bool = False): - """Load historical adj close/close prices for tickers in list of trades - - Parameters - ---------- - use_close: bool - whether to use close or adjusted close prices - """ - - p_bar = tqdm( - range(len(self.tickers)), desc=" Loading price data", leave=False - ) - - for ticker_type, data in self.tickers.items(): - price_data = yf.download( - data, start=self.inception_date, progress=False, ignore_tz=True - )["Close" if use_close or ticker_type == "CRYPTO" else "Adj Close"] - - # Set up column name if only 1 ticker (pd.DataFrame only does this if >1 ticker) - if len(data) == 1: - price_data = pd.DataFrame(price_data) - price_data.columns = data - - self.portfolio_historical_prices = pd.concat( - [self.portfolio_historical_prices, price_data], axis=1 - ) - - self.portfolio_historical_prices.fillna(method="ffill", inplace=True) - - p_bar.n += 1 - p_bar.refresh() - - @log_start_end(log=logger) - def __populate_historical_trade_data(self): - """Record the state of the portfolio at each day since inception - - The state includes the following information for each ticker and total: - - Quantity: Number of shares held - - Investment: Total investment in shares - - Investment delta: Change in investment since last day - - Close: Close price of shares - - End value: Total value of shares at close price - """ - - trade_data = self.__transactions.pivot_table( - index="Date", - columns=["Ticker"], - values=[ - "Quantity", - "Investment", - ], - aggfunc={"Quantity": np.sum, "Investment": np.sum}, - ) - - # Make historical prices columns a multi-index. This helps the merging. - self.portfolio_historical_prices.columns = pd.MultiIndex.from_product( - [["Close"], self.portfolio_historical_prices.columns] - ) - - trade_data = pd.merge( - trade_data, - self.portfolio_historical_prices, - how="outer", - left_index=True, - right_index=True, - ) - - trade_data["Close"] = trade_data["Close"].fillna(method="ffill") - trade_data.fillna(0, inplace=True) - - trade_data["Quantity"] = trade_data["Quantity"].cumsum() - trade_data["Investment"] = trade_data["Investment"].cumsum() - trade_data["Investment", "Total"] = trade_data["Investment"].sum(axis=1) - trade_data[ - pd.MultiIndex.from_product( - [["Investment delta"], self.tickers_list + ["Total"]] - ) - ] = (trade_data["Investment"].diff(periods=1).fillna(trade_data["Investment"])) - - # End Value = Quantity * Close - trade_data[pd.MultiIndex.from_product([["End Value"], self.tickers_list])] = ( - trade_data["Quantity"][self.tickers_list] - * trade_data["Close"][self.tickers_list] - ) - - trade_data.loc[:, ("End Value", "Total")] = trade_data["End Value"][ - self.tickers_list - ].sum(axis=1) - - # Initial Value = Previous End Value + Investment changes - trade_data[ - pd.MultiIndex.from_product( - [["Initial Value"], self.tickers_list + ["Total"]] - ) - ] = 0 - - trade_data["Initial Value"] = trade_data["End Value"].shift(1) + trade_data[ - "Investment" - ].diff(periods=1) - - # Set first day Initial Value as the Investment (NaNs break first period) - for t in self.tickers_list + ["Total"]: - trade_data.at[trade_data.index[0], ("Initial Value", t)] = trade_data.iloc[ - 0 - ]["Investment"][t] - - trade_data = trade_data.reindex( - columns=[ - "Quantity", - "Investment", - "Investment delta", - "Close", - "Initial Value", - "End Value", - ], - level=0, - ) - self.historical_trade_data = trade_data - - @log_start_end(log=logger) - def __calculate_portfolio_returns(self): - """Calculate portfolio daily returns from historical trade data - - - End value: Total value of shares at close price - - Investment delta: Change in investment since last day - - Period cash inflow: min(0, Investment delta) - Any negative change in investment, should occur after a sale - - Period cash outflow: max(0, Investment delta) - Any positive change in investment, should occur after a purchase - - Period return: Value at end / Value at start - 1 - Value at start: [End Value(t - 1) + Cash Outflow(t)] - We assume that at period 't' start, the portfolio value is equal to - the sum of the value of assets held [End Value(t - 1)] plus the value - of any purchases made during the period [Cash Outflow(t)]. Here, we - are implicitly assuming that you deposit the cash to buy the shares at - the start. - Value at end: [End Value(t) + Cash Inflow(t)] - We assume that at period 't' end, the portfolio value is equal to - the sum of the value of assets held [End Value(t)] plus the proceeds - from any sales during the period [Cash Inflow(t)]. - """ - - p_bar = tqdm(range(1), desc=" Calculating returns", leave=False) - - trade_data = self.historical_trade_data - - # Helper functions to calculate cash inflow and outflow - def f_min(x): - return x.apply(lambda x: min(x, 0)) - - def f_max(x): - return x.apply(lambda x: max(x, 0)) - - # Calculate cash inflow and outflow - trade_data[ - pd.MultiIndex.from_product( - [["Period cash inflow"], self.tickers_list + ["Total"]] - ) - ] = -1 * trade_data["Investment delta"][:].apply(lambda x: f_min(x), axis=0) - - trade_data[ - pd.MultiIndex.from_product( - [["Period cash outflow"], self.tickers_list + ["Total"]] - ) - ] = trade_data["Investment delta"][:].apply(lambda x: f_max(x), axis=1) - - # Calculate period return - - trade_data[ - pd.MultiIndex.from_product( - [["Period absolute return"], self.tickers_list + ["Total"]] - ) - ] = (trade_data["End Value"] + trade_data["Period cash inflow"]) - ( - trade_data["End Value"].shift(1).fillna(0) - + trade_data["Period cash outflow"] - ) - - trade_data[ - pd.MultiIndex.from_product( - [["Period percentage return"], self.tickers_list + ["Total"]] - ) - ] = (trade_data["End Value"] + trade_data["Period cash inflow"]) / ( - trade_data["End Value"].shift(1).fillna(0) - + trade_data["Period cash outflow"] - ) - 1 - - trade_data["Period percentage return"].fillna(0, inplace=True) - - self.historical_trade_data = trade_data - - self.portfolio_returns = self.historical_trade_data["Period percentage return"][ - "Total" - ] - - p_bar.n += 1 - p_bar.refresh() - - @log_start_end(log=logger) - def __calculate_portfolio_performance(self): - """Calculate portfolio performance""" - - # TODO: saving the trade performance in portfolio_trades is wrong as it is. - # If we have sales we end up summing negative investments and positive investments. - # That leads to wrong % return calculation. This implementation does not allow - # performance between trade dates. - # We need to fix this. - - # Determine invested amount, relative and absolute return based on last close - last_price = self.historical_trade_data["Close"].iloc[-1] - - # Save portfolio trades to compute allocations later - self.portfolio_trades = self.__transactions.copy() - self.portfolio_trades[ - [ - "Portfolio Investment", - "Close", - "Portfolio Value", - "Portfolio % Return", - "Abs Portfolio Return", - ] - ] = float(0) - - for index, trade in self.__transactions.iterrows(): - self.portfolio_trades["Close"][index] = last_price[trade["Ticker"]] - self.portfolio_trades["Portfolio Investment"][index] = trade["Investment"] - self.portfolio_trades["Portfolio Value"][index] = ( - self.portfolio_trades["Close"][index] * trade["Quantity"] - ) - self.portfolio_trades["Portfolio % Return"][index] = ( - self.portfolio_trades["Portfolio Value"][index] - / self.portfolio_trades["Portfolio Investment"][index] - ) - 1 - self.portfolio_trades["Abs Portfolio Return"].loc[index] = ( - self.portfolio_trades["Portfolio Value"][index] - - self.portfolio_trades["Portfolio Investment"][index] - ) - - @log_start_end(log=logger) - def set_risk_free_rate(self, risk_free_rate: float): - """Set risk free rate - - Parameters - ---------- - risk_free_rate : float - Risk free rate in float format - """ - - self.risk_free_rate = risk_free_rate - - @log_start_end(log=logger) - def calculate_reserves(self): - """Take dividends into account for returns calculation""" - # TODO: Add back cash dividends and deduct exchange costs - console.print("Still has to be build.") - - @log_start_end(log=logger) - def calculate_allocation(self, category: str, recalculate: bool = False): - """Determine allocation based on Asset, Sector, Country or Region - - Parameters - ---------- - category: str - Chosen allocation category from Asset, Sector, Country or Region - recalculate: bool - Flag to force recalculate allocation if already exists - """ - - if not self.benchmark_ticker or self.portfolio_trades.empty: - console.print( - "Please load in a portfolio holdings first with `load --file` or obtain an example with `load --example`" - ) - return - - if category == "Asset": - if ( - self.benchmark_assets_allocation.empty - or self.portfolio_assets_allocation.empty - or recalculate - ): - ( - self.benchmark_assets_allocation, - self.portfolio_assets_allocation, - ) = get_allocation( - category, self.benchmark_ticker, self.portfolio_trades - ) - elif category == "Sector": - if ( - self.benchmark_sectors_allocation.empty - or self.portfolio_sectors_allocation.empty - or recalculate - ): - ( - self.benchmark_sectors_allocation, - self.portfolio_sectors_allocation, - ) = get_allocation( - category, self.benchmark_ticker, self.portfolio_trades - ) - elif category == "Country": - if ( - self.benchmark_countries_allocation.empty - or self.portfolio_countries_allocation.empty - or recalculate - ): - ( - self.benchmark_countries_allocation, - self.portfolio_countries_allocation, - ) = get_allocation( - category, self.benchmark_ticker, self.portfolio_trades - ) - elif category == "Region": - if ( - self.benchmark_regions_allocation.empty - or self.portfolio_regions_allocation.empty - or recalculate - ): - ( - self.benchmark_regions_allocation, - self.portfolio_regions_allocation, - ) = get_allocation( - category, self.benchmark_ticker, self.portfolio_trades - ) - else: - console.print( - "Category not available. Choose from: Asset, Sector, Country or Region" - ) diff --git a/openbb_terminal/portfolio/portfolio_helper.py b/openbb_terminal/portfolio/portfolio_helper.py deleted file mode 100644 index 8f8b691f5e1..00000000000 --- a/openbb_terminal/portfolio/portfolio_helper.py +++ /dev/null @@ -1,339 +0,0 @@ -"""Portfolio Helper""" - -__docformat__ = "numpy" - -import csv -import logging -import os -from datetime import date, datetime -from pathlib import Path -from typing import List - -import pandas as pd -import yfinance as yf -from dateutil.relativedelta import relativedelta - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.portfolio.statics import REGIONS -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -# pylint: disable=too-many-return-statements, too-many-lines, too-many-statements, consider-iterating-dictionary -# pylint: disable=C0302 - - -now = datetime.now() -PERIODS_DAYS = { - "mtd": (now - datetime(now.year, now.month, 1)).days, - "qtd": ( - now - - datetime( - now.year, - 1 if now.month < 4 else 4 if now.month < 7 else 7 if now.month < 10 else 10, - 1, - ) - ).days, - "ytd": (now - datetime(now.year, 1, 1)).days, - "all": 1, - "3m": 3 * 21, - "6m": 6 * 21, - "1y": 12 * 21, - "3y": 3 * 12 * 21, - "5y": 5 * 12 * 21, - "10y": 10 * 12 * 21, -} - -DEFAULT_HOLDINGS_PATH = ( - get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY / "holdings" -) - - -def is_ticker(ticker: str) -> bool: - """Determine whether a string is a valid ticker - - Parameters - ---------- - ticker : str - The string to be tested - - Returns - ------- - bool - Whether the string is a ticker - """ - item = yf.Ticker(ticker) - return "previousClose" in item.fast_info - - -# TODO: Is this being used anywhere? -def beta_word(beta: float) -> str: - """Describe a beta - - Parameters - ---------- - beta : float - The beta for a portfolio - - Returns - ------- - str - The description of the beta - """ - if abs(1 - beta) > 3: - part = "extremely " - elif abs(1 - beta) > 2: - part = "very " - elif abs(1 - beta) > 1: - part = "" - else: - part = "moderately " - - return part + "high" if beta > 1 else "low" - - -def clean_name(name: str) -> str: - """Clean a name to a ticker - - Parameters - ---------- - name : str - The value to be cleaned - - Returns - ------- - str - A cleaned value - """ - return name.replace("beta_", "").upper() - - -def filter_df_by_period(df: pd.DataFrame, period: str = "all") -> pd.DataFrame: - """Filter dataframe by selected period - - Parameters - ---------- - df: pd.DataFrame - Dataframe to be filtered in terms of time - period : str - Period in which to filter dataframe. - Possible choices are: mtd, qtd, ytd, 3m, 6m, 1y, 3y, 5y, 10y, all - - Returns - ------- - pd.DataFrame - A cleaned value - """ - if period == "mtd": - return df[df.index.strftime("%Y-%m") == datetime.now().strftime("%Y-%m")] - if period == "qtd": - if datetime.now().month < 4: - return df[ - df.index.strftime("%Y-%m") < f"{datetime.now().strftime('%Y')}-04" - ] - if datetime.now().month < 7: - return df[ - (df.index.strftime("%Y-%m") >= f"{datetime.now().strftime('%Y')}-04") - & (df.index.strftime("%Y-%m") < f"{datetime.now().strftime('%Y')}-07") - ] - if datetime.now().month < 10: - return df[ - (df.index.strftime("%Y-%m") >= f"{datetime.now().strftime('%Y')}-07") - & (df.index.strftime("%Y-%m") < f"{datetime.now().strftime('%Y')}-10") - ] - return df[df.index.strftime("%Y-%m") >= f"{datetime.now().strftime('%Y')}-10"] - if period == "ytd": - return df[df.index.strftime("%Y") == datetime.now().strftime("%Y")] - period_dict = { - "3m": relativedelta(months=3), - "6m": relativedelta(months=6), - "1y": relativedelta(years=1), - "3y": relativedelta(years=3), - "5y": relativedelta(years=5), - "10y": relativedelta(years=10), - } - if period in period_dict: - return df[df.index >= (datetime.now() - period_dict[period])] - return df - - -def make_equal_length(df1: pd.DataFrame, df2: pd.DataFrame): - """Filter dataframe by selected period - - Parameters - ---------- - df1: pd.DataFrame - The first DataFrame that needs to be compared. - df2: pd.DataFrame - The second DataFrame that needs to be compared. - - Returns - ------- - df1 and df2 - Both DataFrames returned - """ - # Match the DataFrames so they share a similar length - if isinstance(df1, pd.Series): - df1 = df1.to_frame() - if isinstance(df2, pd.Series): - df2 = df2.to_frame() - df2.columns = [str(i) + "2" for i in df2.columns] - df_merged = df1.join(df2, how="outer") - df_merged = df_merged.fillna(0) - - df1 = df_merged[df1.columns] - df2 = df_merged[df2.columns] - - df2.columns = [i[:-1] for i in df2.columns] - - return df1.iloc[:, 0], df2.iloc[:, 0] - - -def get_region_from_country(country: str) -> str: - """Get region from country - - Parameters - ---------- - country: str - The country to assign region. - - Returns - ------- - str - Region to which country belongs. - """ - return REGIONS[country] - - -def get_info_update_file(ticker: str, file_path: Path, writemode: str) -> List[str]: - """Get info (Sector, Industry, Country and Region) from ticker and save information in file to access later. - - Parameters - ---------- - ticker: str - The ticker to get information. - file_path: str - The file to save information. - writemode: str - The mode to write into the file, 'w' or 'a'. - - Returns - ------- - List[str] - List with ticker information. - """ - - # Pull ticker info from yf - yf_ticker_info = yf.Ticker(ticker).info - - if "sector" in yf_ticker_info: - # Ticker has valid sector - # Replace the dash to UTF-8 readable - ticker_info_list = [ - yf_ticker_info["sector"], - yf_ticker_info["industry"].replace("—", "-"), - yf_ticker_info["country"], - get_region_from_country(yf_ticker_info["country"]), - ] - - with open(file_path, writemode, newline="") as f: - writer = csv.writer(f) - - if writemode == "a": - # file already has data, so just append - writer.writerow([ticker] + ticker_info_list) - else: - # file did not exist or as empty, so write headers first - writer.writerow(["Ticker", "Sector", "Industry", "Country", "Region"]) - writer.writerow([ticker] + ticker_info_list) - f.close() - return ticker_info_list - # Ticker does not have a valid sector - console.print(f"F:{ticker}", end="") - return ["", "", "", ""] - - -def get_info_from_ticker(ticker: str) -> list: - """Get info (Sector, Industry, Country and Region) from ticker. - - Parameters - ---------- - ticker: str - The ticker to get information. - - Returns - ------- - List[str] - List with ticker information. - """ - - filename = "tickers_info.csv" - - file_path = Path( - str(get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY), filename - ) - - if file_path.is_file() and os.stat(file_path).st_size > 0: - # file exists and is not empty, so append if necessary - ticker_info_df = pd.read_csv(file_path) - df_row = ticker_info_df.loc[ticker_info_df["Ticker"] == ticker] - - if len(df_row) > 0: - # ticker is in file, just return it - ticker_info_list = list(df_row.iloc[0].drop("Ticker")) - return ticker_info_list - # ticker is not in file, go get it - ticker_info_list = get_info_update_file(ticker, file_path, "a") - return ticker_info_list - # file does not exist or is empty, so write it - ticker_info_list = get_info_update_file(ticker, file_path, "w") - - return ticker_info_list - - -def get_start_date_from_period(period: str) -> date: - """Get start date of a time period based on the period string. - - Parameters - ---------- - period: str - Period to get start date from (e.g. 10y, 3m, etc.) - - Returns - ------- - date - Start date of the period. - """ - if period == "10y": - start_date = date.today() + relativedelta(years=-10) - elif period == "5y": - start_date = date.today() + relativedelta(years=-5) - elif period == "3y": - start_date = date.today() + relativedelta(years=-3) - elif period == "1y": - start_date = date.today() + relativedelta(years=-1) - elif period == "6m": - start_date = date.today() + relativedelta(months=-6) - elif period == "3m": - start_date = date.today() + relativedelta(months=-3) - elif period == "ytd": - start_date = date(date.today().year, 1, 1) - elif period == "qtd": - cm = date.today().month - if 3 >= cm >= 1: - start_date = date(date.today().year, 1, 1) - elif 6 >= cm >= 4: - start_date = date(date.today().year, 4, 1) - elif 9 >= cm >= 7: - start_date = date(date.today().year, 7, 1) - elif 12 >= cm >= 10: - start_date = date(date.today().year, 10, 1) - else: - console.print("Error") - elif period == "mtd": - cur_month = date.today().month - cur_year = date.today().year - start_date = date(cur_year, cur_month, 1) - - return start_date diff --git a/openbb_terminal/portfolio/portfolio_model.py b/openbb_terminal/portfolio/portfolio_model.py deleted file mode 100644 index 07b357a618d..00000000000 --- a/openbb_terminal/portfolio/portfolio_model.py +++ /dev/null @@ -1,1915 +0,0 @@ -"""Portfolio Model""" - -__docformat__ = "numpy" - -import logging -from typing import Tuple, Union - -import numpy as np -import pandas as pd -import scipy -from sklearn.metrics import r2_score - -from openbb_terminal.common.quantitative_analysis import qa_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.portfolio import metrics_model, portfolio_helper -from openbb_terminal.portfolio.portfolio_engine import PortfolioEngine -from openbb_terminal.portfolio.statics import PERIODS - -# pylint: disable=E1136,W0201,R0902,C0302, consider-using-f-string, consider-iterating-dictionary -# pylint: disable=unsupported-assignment-operation,redefined-outer-name,too-many-public-methods - -logger = logging.getLogger(__name__) - -pd.options.mode.chained_assignment = None - - -@log_start_end(log=logger) -def generate_portfolio( - transactions_file_path: str, - benchmark_symbol: str = "SPY", - full_shares: bool = False, - risk_free_rate: float = 0, -) -> PortfolioEngine: - """Get PortfolioEngine object - - Parameters - ---------- - transactions_file_path : str - Path to transactions file - benchmark_symbol : str - Benchmark ticker to download data - full_shares : bool - Whether to mimic the portfolio trades exactly (partial shares) or round down the - quantity to the nearest number - risk_free_rate : float - Risk free rate in float format - - Returns - ------- - PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - """ - - transactions = PortfolioEngine.read_transactions(transactions_file_path) - portfolio_engine = PortfolioEngine(transactions) - portfolio_engine.generate_portfolio_data() - portfolio_engine.set_risk_free_rate(risk_free_rate) - portfolio_engine.set_benchmark(symbol=benchmark_symbol, full_shares=full_shares) - - return portfolio_engine - - -@log_start_end(log=logger) -def get_transactions(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get portfolio transactions - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - Portfolio transactions - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.show(p) - """ - - return portfolio_engine.get_transactions() - - -@log_start_end(log=logger) -def set_benchmark( - portfolio_engine: PortfolioEngine, symbol: str, full_shares: bool = False -) -> bool: - """Load benchmark into portfolio - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - symbol: str - Benchmark symbol to download data - full_shares: bool - Whether to mimic the portfolio trades exactly (partial shares) or round down the - quantity to the nearest number - - Returns - ------- - bool - True if successful, False otherwise - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.bench(p, symbol="SPY") - """ - - return portfolio_engine.set_benchmark(symbol=symbol, full_shares=full_shares) - - -@log_start_end(log=logger) -def set_risk_free_rate(portfolio_engine: PortfolioEngine, risk_free_rate: float): - """Set risk-free rate - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - risk_free_rate: float - Risk free rate in float format - """ - - portfolio_engine.set_risk_free_rate(risk_free_rate=risk_free_rate) - - -def get_holdings_value(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get holdings of assets (absolute value) - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame of holdings value - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.holdv(p) - """ - - all_holdings = portfolio_engine.historical_trade_data["End Value"][ - portfolio_engine.tickers_list - ] - - all_holdings["Total Value"] = all_holdings.sum(axis=1) - # No need to account for time since this is daily data - all_holdings.index = all_holdings.index.date - - return all_holdings - - -def get_holdings_percentage( - portfolio_engine: PortfolioEngine, -) -> pd.DataFrame: - """Get holdings of assets (in percentage) - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame of holdings percentage - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.holdp(p) - """ - - all_holdings = portfolio_engine.historical_trade_data["End Value"][ - portfolio_engine.tickers_list - ] - - all_holdings = all_holdings.divide(all_holdings.sum(axis=1), axis=0) * 100 - - # order it a bit more in terms of magnitude - all_holdings = all_holdings[all_holdings.sum().sort_values(ascending=False).index] - - return all_holdings - - -@log_start_end(log=logger) -def get_yearly_returns( - portfolio_engine: PortfolioEngine, - window: str = "all", -) -> pd.DataFrame: - """Get yearly returns - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - - Returns - ------- - pd.DataFrame - DataFrame with yearly returns - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.yret(p) - """ - - portfolio_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, window - ) - benchmark_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, window - ) - - creturns_year_val = list() - breturns_year_val = list() - - for year in sorted(set(portfolio_returns.index.year)): - creturns_year = portfolio_returns[portfolio_returns.index.year == year] - cumulative_returns = 100 * metrics_model.cumulative_returns(creturns_year) - creturns_year_val.append(cumulative_returns.values[-1]) - - breturns_year = benchmark_returns[benchmark_returns.index.year == year] - benchmark_c_returns = 100 * metrics_model.cumulative_returns(breturns_year) - breturns_year_val.append(benchmark_c_returns.values[-1]) - - df = pd.DataFrame( - { - "Portfolio": pd.Series( - creturns_year_val, index=list(set(portfolio_returns.index.year)) - ), - "Benchmark": pd.Series( - breturns_year_val, index=list(set(portfolio_returns.index.year)) - ), - "Difference": pd.Series( - np.array(creturns_year_val) - np.array(breturns_year_val), - index=list(set(portfolio_returns.index.year)), - ), - } - ) - - return df - - -@log_start_end(log=logger) -def get_monthly_returns( - portfolio_engine: PortfolioEngine, - window: str = "all", -) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: - """Get monthly returns - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - - Returns - ------- - pd.DataFrame - DataFrame with monthly returns - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.mret(p) - """ - - portfolio_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, window - ) - benchmark_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, window - ) - - creturns_month_val = list() - breturns_month_val = list() - - for year in sorted(list(set(portfolio_returns.index.year))): - creturns_year = portfolio_returns[portfolio_returns.index.year == year] - creturns_val = list() - for i in range(1, 13): - creturns_year_month = creturns_year[creturns_year.index.month == i] - creturns_year_month_val = 100 * metrics_model.cumulative_returns( - creturns_year_month - ) - - if creturns_year_month.empty: - creturns_val.append(0) - else: - creturns_val.append(creturns_year_month_val.values[-1]) - creturns_month_val.append(creturns_val) - - breturns_year = benchmark_returns[benchmark_returns.index.year == year] - breturns_val = list() - for i in range(1, 13): - breturns_year_month = breturns_year[breturns_year.index.month == i] - breturns_year_month_val = 100 * metrics_model.cumulative_returns( - breturns_year_month - ) - - if breturns_year_month.empty: - breturns_val.append(0) - else: - breturns_val.append(breturns_year_month_val.values[-1]) - breturns_month_val.append(breturns_val) - - columns = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ] - monthly_returns = pd.DataFrame( - creturns_month_val, - index=sorted(list(set(portfolio_returns.index.year))), - columns=columns, - ) - bench_monthly_returns = pd.DataFrame( - breturns_month_val, - index=sorted(list(set(benchmark_returns.index.year))), - columns=columns, - ) - - years = [ - (year, instrument) - for year in monthly_returns.index - for instrument in ["Portfolio", "Benchmark", "Alpha"] - ] - total_monthly_returns = pd.DataFrame( - np.nan, - columns=monthly_returns.columns, - index=pd.MultiIndex.from_tuples(years, names=["Year", "Instrument"]), - ) - - for year in monthly_returns.index: - for instrument in ["Portfolio", "Benchmark", "Alpha"]: - if instrument == "Portfolio": - total_monthly_returns.loc[ - (year, instrument), monthly_returns.columns - ] = monthly_returns.loc[year].values - elif instrument == "Benchmark": - total_monthly_returns.loc[ - (year, instrument), monthly_returns.columns - ] = bench_monthly_returns.loc[year].values - elif instrument == "Alpha": - total_monthly_returns.loc[ - (year, instrument), monthly_returns.columns - ] = ( - monthly_returns.loc[year].values - - bench_monthly_returns.loc[year].values - ) - - return monthly_returns, bench_monthly_returns, total_monthly_returns - - -@log_start_end(log=logger) -def get_daily_returns( - portfolio_engine: PortfolioEngine, - window: str = "all", -) -> pd.DataFrame: - """Get daily returns - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - - Returns - ------- - pd.DataFrame - DataFrame with daily returns - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.dret(p) - """ - - portfolio_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, window - ) - benchmark_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, window - ) - - df = portfolio_returns.to_frame() - df = df.join(benchmark_returns) - df.index = df.index.date - df.columns = ["portfolio", "benchmark"] - - return df - - -def join_allocation( - portfolio: pd.DataFrame, benchmark: pd.DataFrame, column: str -) -> pd.DataFrame: - """Help method to join portfolio and benchmark allocation by column - - Parameters - ---------- - portfolio: pd.DataFrame - Portfolio allocation - benchmark: pd.DataFrame - Benchmark allocation - column: str - Column to join DataFrames - - Returns - ------- - pd.DataFrame - DataFrame with portfolio and benchmark allocations - """ - - if portfolio.empty: - portfolio = pd.DataFrame(columns=[column, "Portfolio"]) - - if benchmark.empty: - benchmark = pd.DataFrame(columns=[column, "Benchmark"]) - - combined = pd.merge(portfolio, benchmark, on=column, how="left") - combined["Difference"] = combined["Portfolio"] - combined["Benchmark"] - combined = combined.replace(np.nan, "-") - combined = combined.replace(0, "-") - - return combined - - -def get_distribution_returns( - portfolio_engine: PortfolioEngine, - window: str = "all", -) -> pd.DataFrame: - """Display daily returns - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - - Returns - ------- - pd.DataFrame - DataFrame of returns distribution - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.distr(p) - """ - - portfolio_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, window - ) - benchmark_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, window - ) - - df = pd.DataFrame(portfolio_returns).join(pd.DataFrame(benchmark_returns)) - df.columns.values[0] = "portfolio" - df.columns.values[1] = "benchmark" - - return df - - -@log_start_end(log=logger) -def get_maximum_drawdown( - portfolio_engine: PortfolioEngine, is_returns: bool = False -) -> Tuple[pd.DataFrame, pd.Series]: - """Calculate the drawdown (MDD) of historical series. Note that the calculation is done - on cumulative returns (or prices). The definition of drawdown is - - DD = (current value - rolling maximum) / rolling maximum - - Parameters - ---------- - data: pd.Series - Series of input values - is_returns: bool - Flag to indicate inputs are returns - - Returns - ------- - pd.Series - Holdings series - pd.Series - Drawdown series - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.maxdd(p) - """ - - holdings: pd.Series = portfolio_engine.historical_trade_data["End Value"]["Total"] - if is_returns: - holdings = (1 + holdings).cumprod() - - rolling_max = holdings.cummax() - drawdown = (holdings - rolling_max) / rolling_max - - return holdings, drawdown - - -def get_rolling_volatility( - portfolio_engine: PortfolioEngine, window: str = "1y" -) -> pd.DataFrame: - """Get rolling volatility - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - Rolling window size to use - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y - - Returns - ------- - pd.DataFrame - Rolling volatility DataFrame - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.rvol(p) - """ - - portfolio_rvol = metrics_model.rolling_volatility( - portfolio_engine.portfolio_returns, window - ) - if portfolio_rvol.empty: - return pd.DataFrame() - - benchmark_rvol = metrics_model.rolling_volatility( - portfolio_engine.benchmark_returns, window - ) - if benchmark_rvol.empty: - return pd.DataFrame() - - df = pd.DataFrame(portfolio_rvol).join(pd.DataFrame(benchmark_rvol)) - df.columns.values[0] = "portfolio" - df.columns.values[1] = "benchmark" - - return df - - -def get_rolling_sharpe( - portfolio_engine: pd.DataFrame, risk_free_rate: float = 0, window: str = "1y" -) -> pd.DataFrame: - """Get rolling sharpe ratio - - Parameters - ---------- - portfolio_returns : pd.Series - Series of portfolio returns - risk_free_rate : float - Risk free rate - window : str - Rolling window to use - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y - - Returns - ------- - pd.DataFrame - Rolling sharpe ratio DataFrame - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.rsharpe(p) - """ - - portfolio_rsharpe = metrics_model.rolling_sharpe( - portfolio_engine.portfolio_returns, risk_free_rate, window - ) - if portfolio_rsharpe.empty: - return pd.DataFrame() - - benchmark_rsharpe = metrics_model.rolling_sharpe( - portfolio_engine.benchmark_returns, risk_free_rate, window - ) - if benchmark_rsharpe.empty: - return pd.DataFrame() - - df = pd.DataFrame(portfolio_rsharpe).join(pd.DataFrame(benchmark_rsharpe)) - df.columns.values[0] = "portfolio" - df.columns.values[1] = "benchmark" - - return df - - -def get_rolling_sortino( - portfolio_engine: PortfolioEngine, - risk_free_rate: float = 0, - window: str = "1y", -) -> pd.DataFrame: - """Get rolling sortino - - Parameters - ---------- - portfolio : PortfolioEngine - PortfolioEngine object - window: str - interval for window to consider - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y - risk_free_rate: float - Value to use for risk free rate in sharpe/other calculations - - Returns - ------- - pd.DataFrame - Rolling sortino ratio DataFrame - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.rsort(p) - """ - - portfolio_rsortino = metrics_model.rolling_sortino( - portfolio_engine.portfolio_returns, risk_free_rate, window - ) - if portfolio_rsortino.empty: - return pd.DataFrame() - - benchmark_rsortino = metrics_model.rolling_sortino( - portfolio_engine.benchmark_returns, risk_free_rate, window - ) - if benchmark_rsortino.empty: - return pd.DataFrame() - - df = pd.DataFrame(portfolio_rsortino).join(pd.DataFrame(benchmark_rsortino)) - df.columns.values[0] = "portfolio" - df.columns.values[1] = "benchmark" - - return df - - -@log_start_end(log=logger) -def get_rolling_beta( - portfolio_engine: PortfolioEngine, - window: str = "1y", -) -> pd.DataFrame: - """Get rolling beta using portfolio and benchmark returns - - Parameters - ---------- - portfolio : PortfolioEngine - PortfolioEngine object - window: string - Interval used for rolling values. - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y. - - Returns - ------- - pd.DataFrame - DataFrame of the portfolio's rolling beta - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.rbeta(p) - """ - - df = metrics_model.rolling_beta( - portfolio_engine.portfolio_returns, portfolio_engine.benchmark_returns, window - ) - - return df - - -def get_summary( - portfolio_engine: PortfolioEngine, - window: str = "all", - risk_free_rate: float = 0, -) -> pd.DataFrame: - """Get portfolio and benchmark returns summary - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - risk_free_rate : float - Risk free rate for calculations - - Returns - ------- - pd.DataFrame - DataFrame with portfolio and benchmark returns summary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.summary(p) - """ - - portfolio_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, window - ) - benchmark_returns = portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, window - ) - - r2_portfolio_returns = portfolio_returns.copy() - r2_benchmark_returns = benchmark_returns.copy() - if len(portfolio_returns) > len(benchmark_returns): - r2_portfolio_returns = r2_portfolio_returns[ - r2_portfolio_returns.index.isin(r2_benchmark_returns.index) - ] - elif len(portfolio_returns) < len(benchmark_returns): - r2_benchmark_returns = r2_benchmark_returns[ - r2_benchmark_returns.index.isin(r2_portfolio_returns.index) - ] - - metrics = { - "Volatility": [portfolio_returns.std(), benchmark_returns.std()], - "Skew": [ - scipy.stats.skew(portfolio_returns), - scipy.stats.skew(benchmark_returns), - ], - "Kurtosis": [ - scipy.stats.kurtosis(portfolio_returns), - scipy.stats.kurtosis(benchmark_returns), - ], - "Maximum Drawdown": [ - metrics_model.maximum_drawdown(portfolio_returns), - metrics_model.maximum_drawdown(benchmark_returns), - ], - "Sharpe ratio": [ - metrics_model.sharpe_ratio(portfolio_returns, risk_free_rate), - metrics_model.sharpe_ratio(benchmark_returns, risk_free_rate), - ], - "Sortino ratio": [ - metrics_model.sortino_ratio(portfolio_returns, risk_free_rate), - metrics_model.sortino_ratio(benchmark_returns, risk_free_rate), - ], - "R2 Score": [ - r2_score(r2_portfolio_returns, r2_benchmark_returns), - r2_score(r2_portfolio_returns, r2_benchmark_returns), - ], - } - - summary = pd.DataFrame( - metrics.values(), index=metrics.keys(), columns=["Portfolio", "Benchmark"] - ) - summary["Difference"] = summary["Portfolio"] - summary["Benchmark"] - summary.loc["Volatility"] = summary.loc["Volatility"].apply("{:.2%}".format) - summary.loc["Maximum Drawdown"] = summary.loc["Maximum Drawdown"].apply( - "{:.2%}".format - ) - summary.loc["R2 Score"] = summary.loc["R2 Score"].apply("{:.2%}".format) - - return summary - - -@log_start_end(log=logger) -def get_assets_allocation( - portfolio_engine: PortfolioEngine, - tables: bool = False, - limit: int = 10, - recalculate: bool = False, -) -> Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]]: - """Display portfolio asset allocation compared to the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - tables: bool - Whether to include separate allocation tables - limit: int - The amount of assets you wish to show, by default this is set to 10 - recalculate: bool - Flag to force recalculate allocation if already exists - - Returns - ------- - Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]] - DataFrame with combined allocation plus individual allocation if tables is `True`. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.alloc.assets(p) - """ - - portfolio_engine.calculate_allocation(category="Asset", recalculate=recalculate) - - benchmark_allocation = portfolio_engine.benchmark_assets_allocation.iloc[:limit] - portfolio_allocation = portfolio_engine.portfolio_assets_allocation.iloc[:limit] - - combined = join_allocation(portfolio_allocation, benchmark_allocation, "Symbol") - - if tables: - return combined, portfolio_allocation, benchmark_allocation - return combined - - -def get_sectors_allocation( - portfolio_engine: PortfolioEngine, - limit: int = 10, - tables: bool = False, - recalculate: bool = False, -): - """Display portfolio sector allocation compared to the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - tables: bool - Whether to include separate allocation tables - limit: int - The amount of assets you wish to show, by default this is set to 10 - recalculate: bool - Flag to force recalculate allocation if already exists - - Returns - ------- - Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]] - DataFrame with combined allocation plus individual allocation if tables is `True`. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.alloc.sectors(p) - """ - - portfolio_engine.calculate_allocation(category="Sector", recalculate=recalculate) - - benchmark_allocation = portfolio_engine.benchmark_sectors_allocation.iloc[:limit] - portfolio_allocation = portfolio_engine.portfolio_sectors_allocation.iloc[:limit] - - combined = join_allocation(portfolio_allocation, benchmark_allocation, "Sector") - - if tables: - return combined, portfolio_allocation, benchmark_allocation - return combined - - -def get_countries_allocation( - portfolio_engine: PortfolioEngine, - limit: int = 10, - tables: bool = False, - recalculate: bool = False, -): - """Display portfolio country allocation compared to the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - tables: bool - Whether to include separate allocation tables - limit: int - The amount of assets you wish to show, by default this is set to 10 - recalculate: bool - Flag to force recalculate allocation if already exists - - Returns - ------- - Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]] - DataFrame with combined allocation plus individual allocation if tables is `True`. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.alloc.countries(p) - """ - - portfolio_engine.calculate_allocation(category="Country", recalculate=recalculate) - - benchmark_allocation = portfolio_engine.benchmark_countries_allocation.iloc[:limit] - portfolio_allocation = portfolio_engine.portfolio_countries_allocation.iloc[:limit] - - combined = join_allocation(portfolio_allocation, benchmark_allocation, "Country") - - if tables: - return combined, portfolio_allocation, benchmark_allocation - return combined - - -def get_regions_allocation( - portfolio_engine: PortfolioEngine, - limit: int = 10, - tables: bool = False, - recalculate: bool = False, -): - """Display portfolio region allocation compared to the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - tables: bool - Whether to include separate allocation tables - limit: int - The amount of assets you wish to show, by default this is set to 10 - recalculate: bool - Flag to force recalculate allocation if already exists - - Returns - ------- - Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]] - DataFrame with combined allocation plus individual allocation if tables is `True`. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.alloc.regions(p) - """ - - portfolio_engine.calculate_allocation(category="Region", recalculate=recalculate) - - benchmark_allocation = portfolio_engine.benchmark_regions_allocation.iloc[:limit] - portfolio_allocation = portfolio_engine.portfolio_regions_allocation.iloc[:limit] - - combined = join_allocation(portfolio_allocation, benchmark_allocation, "Region") - - if tables: - return combined, portfolio_allocation, benchmark_allocation - return combined - - -@log_start_end(log=logger) -def get_r2_score(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get R2 Score for portfolio and benchmark selected - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame with R2 Score between portfolio and benchmark for different periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.rsquare(p) - """ - - vals = list() - for period in PERIODS: - vals.append( - round( - r2_score( - portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, period - ), - portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, period - ), - ), - 3, - ) - ) - return pd.DataFrame(vals, index=PERIODS, columns=["R2 Score"]) - - -@log_start_end(log=logger) -def get_skewness(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get skewness for portfolio and benchmark selected - - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame with skewness for portfolio and benchmark for different periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.skew(p) - """ - - vals = list() - for period in PERIODS: - vals.append( - [ - round( - scipy.stats.skew( - portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, period - ) - ), - 3, - ), - round( - scipy.stats.skew( - portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, period - ) - ), - 3, - ), - ] - ) - return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"]) - - -@log_start_end(log=logger) -def get_kurtosis(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get kurtosis for portfolio and benchmark selected - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame with kurtosis for portfolio and benchmark for different periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.kurtosis(p) - """ - - vals = list() - for period in PERIODS: - vals.append( - [ - round( - scipy.stats.kurtosis( - portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, period - ) - ), - 3, - ), - round( - scipy.stats.skew( - portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, period - ) - ), - 3, - ), - ] - ) - return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"]) - - -@log_start_end(log=logger) -def get_stats(portfolio_engine: PortfolioEngine, window: str = "all") -> pd.DataFrame: - """Get stats for portfolio and benchmark selected based on a certain interval - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to consider. Choices are: mtd, qtd, ytd, 3m, 6m, 1y, 3y, 5y, 10y, all - - Returns - ------- - pd.DataFrame - DataFrame with overall stats for portfolio and benchmark for a certain period - """ - - df = ( - portfolio_helper.filter_df_by_period(portfolio_engine.portfolio_returns, window) - .describe() - .to_frame() - .join( - portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, window - ).describe() - ) - ) - df.columns = ["Portfolio", "Benchmark"] - return df - - -@log_start_end(log=logger) -def get_volatility(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get volatility for portfolio and benchmark selected - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame with volatility for portfolio and benchmark for different periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.volatility(p) - """ - - vals = list() - for period in PERIODS: - port_rets = portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, period - ) - bench_rets = portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, period - ) - vals.append( - [ - round( - port_rets.std() * (len(port_rets) ** 0.5), - 3, - ), - round( - bench_rets.std() * (len(bench_rets) ** 0.5), - 3, - ), - ] - ) - return pd.DataFrame( - vals, - index=PERIODS, - columns=["Portfolio [%]", "Benchmark [%]"], - ) - - -@log_start_end(log=logger) -def get_sharpe_ratio( - portfolio_engine: PortfolioEngine, risk_free_rate: float = 0 -) -> pd.DataFrame: - """Get sharpe ratio for portfolio and benchmark selected - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - risk_free_rate: float - Risk free rate value - - Returns - ------- - pd.DataFrame - DataFrame with sharpe ratio for portfolio and benchmark for different periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.sharpe(p) - """ - - vals = list() - for period in PERIODS: - vals.append( - [ - round( - metrics_model.sharpe_ratio( - portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, period - ), - risk_free_rate, - ), - 3, - ), - round( - metrics_model.sharpe_ratio( - portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, period - ), - risk_free_rate, - ), - 3, - ), - ] - ) - return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"]) - - -@log_start_end(log=logger) -def get_sortino_ratio( - portfolio_engine: PortfolioEngine, risk_free_rate: float = 0 -) -> pd.DataFrame: - """Get sortino ratio for portfolio and benchmark selected - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - risk_free_rate: float - Risk free rate value - - Returns - ------- - pd.DataFrame - DataFrame with sortino ratio for portfolio and benchmark for different periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.sortino(p) - """ - - vals = list() - for period in PERIODS: - vals.append( - [ - round( - metrics_model.sortino_ratio( - portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, period - ), - risk_free_rate, - ), - 3, - ), - round( - metrics_model.sortino_ratio( - portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, period - ), - risk_free_rate, - ), - 3, - ), - ] - ) - return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"]) - - -@log_start_end(log=logger) -def get_maximum_drawdown_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get maximum drawdown ratio for portfolio and benchmark selected - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame with maximum drawdown for portfolio and benchmark for different periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.maxdrawdown(p) - """ - - vals = list() - for period in PERIODS: - vals.append( - [ - round( - metrics_model.maximum_drawdown( - portfolio_helper.filter_df_by_period( - portfolio_engine.portfolio_returns, period - ) - ), - 3, - ), - round( - metrics_model.maximum_drawdown( - portfolio_helper.filter_df_by_period( - portfolio_engine.benchmark_returns, period - ) - ), - 3, - ), - ] - ) - return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"]) - - -@log_start_end(log=logger) -def get_gaintopain_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get Pain-to-Gain ratio based on historical data - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame of the portfolio's gain-to-pain ratio - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.gaintopain(p) - """ - - gtp_period_df = metrics_model.get_gaintopain_ratio( - portfolio_engine.historical_trade_data, - portfolio_engine.benchmark_trades, - portfolio_engine.benchmark_returns, - ) - - return gtp_period_df - - -@log_start_end(log=logger) -def get_tracking_error( - portfolio_engine: PortfolioEngine, window: int = 252 -) -> Tuple[pd.DataFrame, pd.Series]: - """Get tracking error - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window: int - Interval used for rolling values - - Returns - ------- - pd.DataFrame - DataFrame of tracking errors during different time windows - pd.Series - Series of rolling tracking error - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.trackerr(p) - """ - - trackr_period_df, trackr_rolling = metrics_model.get_tracking_error( - portfolio_engine.portfolio_returns, portfolio_engine.benchmark_returns, window - ) - - return trackr_period_df, trackr_rolling - - -@log_start_end(log=logger) -def get_information_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get information ratio - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame of the information ratio during different time periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.information(p) - """ - - ir_period_df = metrics_model.get_information_ratio( - portfolio_engine.portfolio_returns, - portfolio_engine.historical_trade_data, - portfolio_engine.benchmark_trades, - portfolio_engine.benchmark_returns, - ) - - return ir_period_df - - -@log_start_end(log=logger) -def get_tail_ratio( - portfolio_engine: PortfolioEngine, window: int = 252 -) -> Tuple[pd.DataFrame, pd.Series, pd.Series]: - """Get tail ratio - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window: int - Interval used for rolling values - - Returns - ------- - pd.DataFrame - DataFrame of the portfolios and the benchmarks tail ratio during different time windows - pd.Series - Series of the portfolios rolling tail ratio - pd.Series - Series of the benchmarks rolling tail ratio - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.tail(p) - """ - - tailr_period_df, portfolio_tr, benchmark_tr = metrics_model.get_tail_ratio( - portfolio_engine.portfolio_returns, portfolio_engine.benchmark_returns, window - ) - - return tailr_period_df, portfolio_tr, benchmark_tr - - -@log_start_end(log=logger) -def get_common_sense_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get common sense ratio - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame of the portfolios and the benchmarks common sense ratio during different time periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.commonsense(p) - """ - - csr_period_df = metrics_model.get_common_sense_ratio( - portfolio_engine.portfolio_returns, - portfolio_engine.historical_trade_data, - portfolio_engine.benchmark_trades, - portfolio_engine.benchmark_returns, - ) - - return csr_period_df - - -@log_start_end(log=logger) -def get_jensens_alpha( - portfolio_engine: PortfolioEngine, risk_free_rate: float = 0, window: str = "1y" -) -> Tuple[pd.DataFrame, pd.Series]: - """Get jensen's alpha - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window: str - Interval used for rolling values - risk_free_rate: float - Risk free rate - - Returns - ------- - pd.DataFrame - DataFrame of jensens's alpha during different time windows - pd.Series - Series of jensens's alpha data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.jensens(p) - """ - - ja_period_df, ja_rolling = metrics_model.jensens_alpha( - portfolio_engine.portfolio_returns, - portfolio_engine.historical_trade_data, - portfolio_engine.benchmark_trades, - portfolio_engine.benchmark_returns, - risk_free_rate, - window, - ) - - return ja_period_df, ja_rolling - - -@log_start_end(log=logger) -def get_calmar_ratio( - portfolio_engine: PortfolioEngine, window: int = 756 -) -> Tuple[pd.DataFrame, pd.Series]: - """Get calmar ratio - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window: int - Interval used for rolling values - - Returns - ------- - pd.DataFrame - DataFrame of calmar ratio of the benchmark and portfolio during different time periods - pd.Series - Series of calmar ratio data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.calmar(p) - """ - - cr_period_df, cr_rolling = metrics_model.get_calmar_ratio( - portfolio_engine.portfolio_returns, - portfolio_engine.historical_trade_data, - portfolio_engine.benchmark_trades, - portfolio_engine.benchmark_returns, - window, - ) - - return cr_period_df, cr_rolling - - -@log_start_end(log=logger) -def get_kelly_criterion(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get kelly criterion - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame of kelly criterion of the portfolio during different time periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.kelly(p) - """ - - kc_period_df = metrics_model.get_kelly_criterion( - portfolio_engine.portfolio_returns, portfolio_engine.portfolio_trades - ) - - return kc_period_df - - -@log_start_end(log=logger) -def get_payoff_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get payoff ratio - - Returns - ------- - pd.DataFrame - DataFrame of payoff ratio of the portfolio during different time periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.payoff(p) - During some time periods there were no losing trades. Thus some values could not be calculated. - """ - - pr_period_ratio = metrics_model.get_payoff_ratio(portfolio_engine.portfolio_trades) - - return pr_period_ratio - - -@log_start_end(log=logger) -def get_profit_factor(portfolio_engine: PortfolioEngine) -> pd.DataFrame: - """Get profit factor - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - - Returns - ------- - pd.DataFrame - DataFrame of profit factor of the portfolio during different time periods - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.metric.profitfactor(p) - During some time periods there were no losing trades. Thus some values could not be calculated. - """ - - pf_period_df = metrics_model.get_profit_factor(portfolio_engine.portfolio_trades) - - return pf_period_df - - -@log_start_end(log=logger) -def get_performance_vs_benchmark( - portfolio_engine: PortfolioEngine, - show_all_trades: bool = False, -) -> pd.DataFrame: - """Get portfolio performance vs the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - show_all_trades: bool - Whether to also show all trades made and their performance (default is False) - - Returns - ------- - pd.DataFrame - DataFrame with portfolio performance vs the benchmark - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.perf(p) - """ - - portfolio_trades = portfolio_engine.portfolio_trades - benchmark_trades = portfolio_engine.benchmark_trades - - portfolio_trades.index = pd.to_datetime(portfolio_trades["Date"].values) - benchmark_trades.index = pd.to_datetime(benchmark_trades["Date"].values) - - if show_all_trades: - # Combine DataFrames - combined = pd.concat( - [ - portfolio_trades[ - ["Date", "Ticker", "Portfolio Value", "Portfolio % Return"] - ], - benchmark_trades[["Benchmark Value", "Benchmark % Return"]], - ], - axis=1, - ) - - # Calculate alpha - combined["Alpha"] = ( - combined["Portfolio % Return"] - combined["Benchmark % Return"] - ) - - combined["Date"] = pd.to_datetime(combined["Date"]).dt.date - - return combined - - # Calculate total value and return - total_investment_difference = ( - portfolio_trades["Portfolio Investment"].sum() - - benchmark_trades["Benchmark Investment"].sum() - ) - total_value_difference = ( - portfolio_trades["Portfolio Value"].sum() - - benchmark_trades["Benchmark Value"].sum() - ) - total_portfolio_return = ( - portfolio_trades["Portfolio Value"].sum() - / portfolio_trades["Portfolio Investment"].sum() - ) - 1 - total_benchmark_return = ( - benchmark_trades["Benchmark Value"].sum() - / benchmark_trades["Benchmark Investment"].sum() - ) - 1 - total_abs_return_difference = ( - portfolio_trades["Portfolio Value"].sum() - - portfolio_trades["Portfolio Investment"].sum() - ) - ( - benchmark_trades["Benchmark Value"].sum() - - benchmark_trades["Benchmark Investment"].sum() - ) - - totals = pd.DataFrame.from_dict( - { - "Total Investment": [ - portfolio_trades["Portfolio Investment"].sum(), - benchmark_trades["Benchmark Investment"].sum(), - total_investment_difference, - ], - "Total Value": [ - portfolio_trades["Portfolio Value"].sum(), - benchmark_trades["Benchmark Value"].sum(), - total_value_difference, - ], - "Total % Return": [ - f"{total_portfolio_return:.2%}", - f"{total_benchmark_return:.2%}", - f"{total_portfolio_return - total_benchmark_return:.2%}", - ], - "Total Abs Return": [ - portfolio_trades["Portfolio Value"].sum() - - portfolio_trades["Portfolio Investment"].sum(), - benchmark_trades["Benchmark Value"].sum() - - benchmark_trades["Benchmark Investment"].sum(), - total_abs_return_difference, - ], - }, - orient="index", - columns=["Portfolio", "Benchmark", "Difference"], - ) - - return totals.replace(0, "-") - - -@log_start_end(log=logger) -def get_var( - portfolio_engine: PortfolioEngine, - use_mean: bool = False, - adjusted_var: bool = False, - student_t: bool = False, - percentile: float = 99.9, -) -> pd.DataFrame: - """Get portfolio VaR - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - use_mean: bool - if one should use the data mean return - adjusted_var: bool - if one should have VaR adjusted for skew and kurtosis (Cornish-Fisher-Expansion) - student_t: bool - If one should use the student-t distribution - percentile: float - var percentile (%) - - Returns - ------- - pd.DataFrame - DataFrame with portfolio VaR - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.var(p) - """ - - return qa_model.get_var( - data=portfolio_engine.portfolio_returns, - use_mean=use_mean, - adjusted_var=adjusted_var, - student_t=student_t, - percentile=percentile, - portfolio=True, - ) - - -@log_start_end(log=logger) -def get_es( - portfolio_engine: PortfolioEngine, - use_mean: bool = False, - distribution: str = "normal", - percentile: float = 99.9, -) -> pd.DataFrame: - """Get portfolio expected shortfall - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - use_mean: - if one should use the data mean return - distribution: str - choose distribution to use: logistic, laplace, normal - percentile: float - es percentile (%) - - Returns - ------- - pd.DataFrame - DataFrame with portfolio expected shortfall - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.es(p) - """ - - return qa_model.get_es( - data=portfolio_engine.portfolio_returns, - use_mean=use_mean, - distribution=distribution, - percentile=percentile, - portfolio=True, - ) - - -@log_start_end(log=logger) -def get_omega( - portfolio_engine: PortfolioEngine, - threshold_start: float = 0, - threshold_end: float = 1.5, -) -> pd.DataFrame: - """Get omega ratio - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - threshold_start: float - annualized target return threshold start of plotted threshold range - threshold_end: float - annualized target return threshold end of plotted threshold range - - Returns - ------- - pd.DataFrame - DataFrame with portfolio omega ratio - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio/holdings_example.xlsx") - >>> output = openbb.portfolio.om(p) - """ - - return qa_model.get_omega( - data=portfolio_engine.portfolio_returns, - threshold_start=threshold_start, - threshold_end=threshold_end, - ) diff --git a/openbb_terminal/portfolio/portfolio_optimization/__init__.py b/openbb_terminal/portfolio/portfolio_optimization/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/portfolio/portfolio_optimization/excel_model.py b/openbb_terminal/portfolio/portfolio_optimization/excel_model.py deleted file mode 100644 index 8e903cb6cff..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/excel_model.py +++ /dev/null @@ -1,180 +0,0 @@ -"""Excel Model""" - -__docformat__ = "numpy" - -# pylint: disable=abstract-class-instantiated - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def load_configuration(excel_file: str = ""): - """ - Load in the Excel file to determine the configuration that needs to be set. - - Parameters - ---------- - excel_file: str - The location of the Excel file that needs to be loaded. - - Returns - ------- - configuration: dictionary - Returns a dictionary with the configurations set. - """ - # Read in the Optimization template - df = pd.read_excel( - excel_file, - sheet_name="Optimization", - skiprows=2, - usecols="B:D", - names=["Parameter", "Value", "Description"], - index_col="Parameter", - ) - - # Remove completely empty NaN rows - cleaned_df = df.dropna(axis="rows", thresh=2) - - # Filter out any general columns - filtered_df = cleaned_df[cleaned_df["Description"] != "Description"] - - # Convert to Dictionary - configuration = filtered_df.to_dict() - - return configuration["Value"], configuration["Description"] - - -@log_start_end(log=logger) -def load_allocation(excel_file: str = ""): - """ - Load in the Excel file to determine the allocation that needs to be set. - - Parameters - ---------- - excel_file: str - The location of the Excel file that needs to be loaded. - - Returns - ------- - tickers: list - Returns a list with ticker symbols - categories: dictionary - Returns a dictionary that specifies each category - """ - if str(excel_file).endswith(".xlsx"): - categories = pd.read_excel(excel_file, sheet_name="Allocation", usecols="A:G") - categories = categories.dropna(axis="rows") - elif str(excel_file).endswith(".csv"): - categories = pd.read_excel(excel_file) - categories = categories.dropna(axis="rows") - else: - console.print("Only Excel (.xlsx and .csv) files are accepted.\n") - return [], {} - - categories.columns = [ - col.upper().strip().replace(" ", "_") for col in categories.columns - ] - categories = categories.apply(lambda x: x.astype(str).str.upper()) - categories = categories[~categories.index.duplicated(keep="first")] - - try: - categories.set_index("TICKER", inplace=True) - categories.sort_index(inplace=True) - except KeyError: - console.print("Allocation table needs a TICKER column\n") - return [], {} - - tickers = list(categories.index) - tickers.sort() - categories = categories.to_dict() - - return tickers, categories - - -@log_start_end(log=logger) -def load_bl_views(excel_file: str = ""): - """ - Load a Excel file with views for Black Litterman model. - - Parameters - ---------- - excel_file: str - The location of the Excel file that needs to be loaded. - - Returns - ------- - p_views: list - Returns a list with p_views matrix - q_views: list - Returns a list with q_views matrix - """ - if str(excel_file).endswith(".xlsx"): - try: - p_views = pd.read_excel(excel_file, sheet_name="p_views", index_col=0) - p_views = p_views.fillna(0) - p_views = p_views.dropna(axis="rows") - except KeyError: - console.print("Excel file needs a p_views sheet\n") - return {}, {} - try: - q_views = pd.read_excel(excel_file, sheet_name="q_views", index_col=0) - q_views = q_views.dropna(axis="rows") - except KeyError: - console.print("Excel file needs a p_views sheet\n") - return {}, {} - else: - console.print("Only Excel (.xlsx) files are accepted.\n") - return {}, {} - - p_views = p_views.T.sort_index() - p_views = p_views.T.to_csv(index=False, header=0).replace("\n", ";") - p_views = p_views[:-1] - p_views = [[float(item) for item in row.split(",")] for row in p_views.split(";")] - q_views = q_views.to_csv(index=False, header=0).replace("\n", ",") - q_views = q_views[:-1] - q_views = [float(item) for item in q_views.split(",")] - - return p_views, q_views - - -@log_start_end(log=logger) -def excel_bl_views(file: str, stocks: str, n: int = 3): - """ - Create an Excel file with required format to build n views for Black Litterman cmd. - - Parameters - ---------- - stocks: str - List of stocks used to build the Black Litterman model views. - n: int - The number of views that will be created. - - Returns - ------- - file: excel - Returns a list with ticker symbols - """ - if len(stocks) < 2: - console.print("Please have at least 2 loaded tickers to create views.\n") - - p_views = [[""] * len(stocks) for i in range(n)] - p_views_df = pd.DataFrame(p_views, columns=stocks) - - q_views = [[""] for i in range(n)] - q_views_df = pd.DataFrame(q_views, columns=["Returns"]) - - if file.endswith(".xlsx"): - pass - else: - file += ".xlsx" - - with pd.ExcelWriter(file) as writer: - p_views_df.to_excel(writer, sheet_name="p_views") - q_views_df.to_excel(writer, sheet_name="q_views") diff --git a/openbb_terminal/portfolio/portfolio_optimization/optimizer_helper.py b/openbb_terminal/portfolio/portfolio_optimization/optimizer_helper.py deleted file mode 100644 index bf6f819b64b..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/optimizer_helper.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Optimization helpers""" - -__docformat__ = "numpy" - -from typing import Any, Optional - -import pandas as pd - -from openbb_terminal.portfolio.portfolio_optimization.statics import ( - OPTIMIZATION_PARAMETERS, - RISK_CHOICES, - TERMINAL_TEMPLATE_MAP, -) -from openbb_terminal.rich_config import console - - -def dict_to_df(d: dict) -> pd.DataFrame: - """Convert a dictionary to a DataFrame - - Parameters - ---------- - d : dict - Dictionary to convert - - Returns - ------- - pd.DataFrame - DataFrame with dictionary - """ - - if not d: - return pd.DataFrame() - - df = pd.DataFrame.from_dict(data=d, orient="index", columns=["value"]) - - return df - - -def validate_risk_measure(risk_measure: str, warning: bool = True) -> str: - """Check that the risk measure selected is valid - - Parameters - ---------- - risk_measure : str - Risk measure to check - - Returns - ------- - str - Validated risk measure - """ - if risk_measure.lower() in RISK_CHOICES: - return RISK_CHOICES[risk_measure.lower()] - if warning: - console.print("[yellow]Risk measure not found. Using 'MV'.[/yellow]") - return "MV" - - -def get_kwarg(key: str, kwargs: dict, default: Optional[Any] = None) -> Any: - """Get a key from kwargs - - If key is in kwargs, returns it. - Otherwise, if default provided, returns it. - Otherwise, if key is in OPTIMIZATION_PARAMETERS, returns it. - - Parameters - ---------- - key : str - The key to be searched - kwargs : dict - The kwargs to be searched - default : Any - The default value to be returned if the key is not found - - Returns - ------- - Any - The value of the key if it exists, else None - """ - - if key in kwargs: - return kwargs[key] - - if default: - return default - - # TODO: Remove this line when mapping between template and terminal is not needed - template_key = TERMINAL_TEMPLATE_MAP.get(key, key) - - PARAMETER = OPTIMIZATION_PARAMETERS.get(template_key) - if PARAMETER is None: - return default - return PARAMETER.default diff --git a/openbb_terminal/portfolio/portfolio_optimization/optimizer_model.py b/openbb_terminal/portfolio/portfolio_optimization/optimizer_model.py deleted file mode 100644 index a4c66fa112a..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/optimizer_model.py +++ /dev/null @@ -1,3011 +0,0 @@ -"""Optimization Model""" - -__docformat__ = "numpy" - -# pylint: disable=R0913, C0302, E1101, line-too-long -# flake8: noqa: E501 - -import logging -from datetime import date -from typing import Any, Dict, List, Optional, Tuple, Union - -import numpy as np -import pandas as pd -import riskfolio as rp -from dateutil.relativedelta import FR, relativedelta -from numpy import floating -from numpy.typing import NDArray -from scipy.interpolate import interp1d - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.portfolio.portfolio_optimization import yahoo_finance_model -from openbb_terminal.portfolio.portfolio_optimization.optimizer_helper import ( - get_kwarg, - validate_risk_measure, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import fmp_model - -logger = logging.getLogger(__name__) - -upper_risk = { - "MV": "upperdev", - "MAD": "uppermad", - "MSV": "uppersdev", - "FLPM": "upperflpm", - "SLPM": "upperslpm", - "CVaR": "upperCVaR", - "EVaR": "upperEVaR", - "WR": "upperwr", - "MDD": "uppermdd", - "ADD": "upperadd", - "CDaR": "upperCDaR", - "EDaR": "upperEDaR", - "UCI": "upperuci", -} - -objectives_choices = { - "minrisk": "MinRisk", - "sharpe": "Sharpe", - "utility": "Utility", - "maxret": "MaxRet", - "erc": "ERC", -} - -risk_names = { - "mv": "volatility", - "mad": "mean absolute deviation", - "gmd": "gini mean difference", - "msv": "semi standard deviation", - "var": "value at risk (VaR)", - "cvar": "conditional value at risk (CVaR)", - "tg": "tail gini", - "evar": "entropic value at risk (EVaR)", - "rg": "range", - "cvrg": "CVaR range", - "tgrg": "tail gini range", - "wr": "worst realization", - "flpm": "first lower partial moment", - "slpm": "second lower partial moment", - "mdd": "maximum drawdown uncompounded", - "add": "average drawdown uncompounded", - "dar": "drawdown at risk (DaR) uncompounded", - "cdar": "conditional drawdown at risk (CDaR) uncompounded", - "edar": "entropic drawdown at risk (EDaR) uncompounded", - "uci": "ulcer index uncompounded", - "mdd_rel": "maximum drawdown compounded", - "add_rel": "average drawdown compounded", - "dar_rel": "drawdown at risk (DaR) compounded", - "cdar_rel": "conditional drawdown at risk (CDaR) compounded", - "edar_rel": "entropic drawdown at risk (EDaR) compounded", - "uci_rel": "ulcer index compounded", -} - -risk_choices = { - "mv": "MV", - "mad": "MAD", - "gmd": "GMD", - "msv": "MSV", - "var": "VaR", - "cvar": "CVaR", - "tg": "TG", - "evar": "EVaR", - "rg": "RG", - "cvrg": "CVRG", - "tgrg": "TGRG", - "wr": "WR", - "flpm": "FLPM", - "slpm": "SLPM", - "mdd": "MDD", - "add": "ADD", - "dar": "DaR", - "cdar": "CDaR", - "edar": "EDaR", - "uci": "UCI", - "mdd_rel": "MDD_Rel", - "add_rel": "ADD_Rel", - "dar_rel": "DaR_Rel", - "cdar_rel": "CDaR_Rel", - "edar_rel": "EDaR_Rel", - "uci_rel": "UCI_Rel", -} - -time_factor = { - "D": 252.0, - "W": 52.0, - "M": 12.0, -} - -dict_conversion = {"period": "historic_period", "start": "start_period"} - - -@log_start_end(log=logger) -def d_period(interval: str = "1y", start_date: str = "", end_date: str = ""): - """ - Builds a date range string - - Parameters - ---------- - interval : str - interval starting today - start_date: str - If not using interval, start date string (YYYY-MM-DD) - end_date: str - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - """ - extra_choices = { - "ytd": "[Year-to-Date]", - "max": "[All-time]", - } - - if start_date == "": - if interval in extra_choices: - p = extra_choices[interval] - elif interval[-1] == "d": - p = "[" + interval[:-1] + " Days]" - elif interval[-1] == "w": - p = "[" + interval[:-1] + " Weeks]" - elif interval[-1] == "o": - p = "[" + interval[:-2] + " Months]" - elif interval[-1] == "y": - p = "[" + interval[:-1] + " Years]" - if p[1:3] == "1 ": - p = p.replace("s", "") - else: - if end_date == "": - end_ = date.today() - if end_.weekday() >= 5: - end_ = end_ + relativedelta(weekday=FR(-1)) - end_date = end_.strftime("%Y-%m-%d") - p = "[From " + start_date + " to " + end_date + "]" - - return p - - -@log_start_end(log=logger) -def get_equal_weights( - symbols: List[str], - **kwargs, -) -> Union[Tuple[Dict[str, float], pd.DataFrame], None]: - """Equally weighted portfolio, where weight = 1/# of symbols - - Parameters - ---------- - symbols : List[str] - List of symbols to be included in the portfolio - interval : str, optional - Interval to get data, by default "3y" - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns: bool, optional - If True use log returns, else arithmetic returns, by default False - freq: str, optional - Frequency of returns, by default "D". Options: "D" for daily, "W" for weekly, "M" for monthly - maxnan: float - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float - Value used to replace outliers that are higher than threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - value : float, optional - Amount to allocate. Returns percentages if set to 1. - - Returns - ------- - Union[Tuple[Dict[str, float], pd.DataFrame], None] - Dictionary of weights where keys are the tickers, dataframe of stock returns - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - weights = { - stock: value * round(1 / len(stock_returns.columns), 5) - for stock in stock_returns.columns - } - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_property_weights( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[Dict[str, Any]], Optional[pd.DataFrame]]: - """Calculate portfolio weights based on selected property, currently this is only market cap. - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - interval : str, optional - interval to get stock data, by default "3mo" - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - maxnan: float - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - value : float, optional - Amount of money to allocate - - Returns - ------- - Tuple[Dict[str, Any], pd.DataFrame] - Dictionary of portfolio weights or allocations - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - prop = {} - no_data = [] - prop_sum = 0 - for stock in symbols: - stock_prop = get_market_cap(stock) - prop[stock] = stock_prop - prop_sum += stock_prop - - if not prop[stock]: - no_data.append(stock) - - if prop_sum == 0: - console.print( - "No market cap data has been found for all selected tickers. Not able to optimize the portfolio.", - "\n", - ) - return None, None - if no_data: - console.print( - "No market cap data has been found for the following tickers: " - + ", ".join(no_data) - + ". Therefore, these will be excluded from the optimization process.", - "\n", - ) - - weights = {k: value * v / prop_sum for k, v in prop.items()} - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_market_cap(symbol) -> float: - """Get market cap from FinancialModelingPrep - - Parameters - ---------- - symbol : str - Stock ticker - - Returns - ------- - updated_value : float - value of market cap - """ - market_cap = fmp_model.get_enterprise(symbol) - - if not market_cap.empty: - latest_year = market_cap.index[-1] - - value = market_cap.loc[latest_year]["Market capitalization"] - values_str = str(value) - - # use values_str in string operations - if values_str.endswith("M"): - updated_value = float(values_str.split(" M", maxsplit=1)[0]) * 1000000 - elif values_str.endswith("B"): - updated_value = float(values_str.split(" B", maxsplit=1)[0]) * 1000000000 - elif values_str.endswith("T"): - updated_value = float(values_str.split(" T", maxsplit=1)[0]) * 1000000000000 - else: - updated_value = float(values_str) - else: - updated_value = 0 - return updated_value - - -@log_start_end(log=logger) -def get_mean_risk_portfolio( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """Builds a mean risk optimal portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - interval : str, optional - interval to get stock data, by default "3mo" - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - objective: str - Objective function of the optimization model. - The default is 'Sharpe'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in annual frequency. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount of money to allocate. The default is 1. - value_short : float, optional - Amount to allocate to portfolio in short positions. The default is 0. - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - value_short = get_kwarg("value_short", kwargs) - - risk_measure = get_kwarg("risk_measure", kwargs) - objective = get_kwarg("objective", kwargs) - risk_free_rate = get_kwarg("risk_free_rate", kwargs) - risk_aversion = get_kwarg("risk_aversion", kwargs) - alpha = get_kwarg("alpha", kwargs) - target_return = get_kwarg("target_return", kwargs) - target_risk = get_kwarg("target_risk", kwargs) - mean = get_kwarg("mean", kwargs) - covariance = get_kwarg("covariance", kwargs) - d_ewma = get_kwarg("d_ewma", kwargs) - - risk_measure = validate_risk_measure(risk_measure) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - if stock_returns.empty: - console.print( - "[red]Not enough data points in range to run calculations.[/red]\n" - ) - return {}, pd.DataFrame() - - if stock_returns.shape[1] < 2: - console.print( - f"[red]Given the parameters could only get data for '{stock_returns.columns[0]}'.[/red]\n" - "[red]Optimization needs at least two assets.[/red]\n", - ) - return {}, pd.DataFrame() - - first_day = stock_returns.index[0].strftime("%Y-%m-%d") - console.print( - f"[yellow]First day of data respecting parameters: {first_day}[/yellow]\n" - ) - - risk_free_rate = risk_free_rate / time_factor[freq.upper()] - - try: - # Building the portfolio object - port = rp.Portfolio(returns=stock_returns, alpha=alpha) - - # Estimate input parameters: - port.assets_stats(method_mu=mean, method_cov=covariance, d=d_ewma) - - # Budget constraints - port.upperlng = value - if value_short > 0: - port.sht = True - port.uppersht = value_short - port.budget = value - value_short - else: - port.budget = value - - # Estimate optimal portfolio: - model = "Classic" - hist = True - - if target_return > -1: - port.lowerret = float(target_return) / time_factor[freq.upper()] - - if target_risk > -1: - if risk_measure not in ["ADD", "MDD", "CDaR", "EDaR", "UCI"]: - setattr( - port, - upper_risk[risk_measure], - float(target_risk) / time_factor[freq.upper()] ** 0.5, - ) - else: - setattr(port, upper_risk[risk_measure], float(target_risk)) - - weights = port.optimization( - model=model, - rm=risk_measure, - obj=objective, - rf=risk_free_rate, - l=risk_aversion, - hist=hist, - ) - - except Exception as _: - weights = None - - if weights is not None: - weights = weights.round(5) - weights = weights.squeeze().to_dict() - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_max_sharpe( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """ - Builds a maximal return/risk ratio portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - weights, stock_returns = get_mean_risk_portfolio( - symbols=symbols, - objective=objectives_choices["sharpe"], - **kwargs, - ) - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_min_risk( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """ - Builds a maximal return/risk ratio portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - weights, stock_returns = get_mean_risk_portfolio( - symbols=symbols, - objective=objectives_choices["minrisk"], - **kwargs, - ) - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_max_util( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """ - Builds a maximal return/risk ratio portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - weights, stock_returns = get_mean_risk_portfolio( - symbols=symbols, - objective=objectives_choices["utility"], - **kwargs, - ) - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_max_ret( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """ - Builds a maximal return/risk ratio portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - weights, stock_returns = get_mean_risk_portfolio( - symbols=symbols, - objective=objectives_choices["maxret"], - **kwargs, - ) - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_max_diversification_portfolio( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """Builds a maximal diversification portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - interval : str, optional - interval to get stock data, by default "3mo" - start_date: str - If not using interval, start date string (YYYY-MM-DD) - end_date: str - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount of money to allocate. The default is 1. - value_short : float, optional - Amount to allocate to portfolio in short positions. The default is 0. - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - value_short = get_kwarg("value_short", kwargs) - - covariance = get_kwarg("covariance", kwargs) - d_ewma = get_kwarg("d_ewma", kwargs) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - try: - # Building the portfolio object - port = rp.Portfolio(returns=stock_returns) - - # Estimate input parameters: - port.assets_stats(method_mu="hist", method_cov=covariance, d=d_ewma) - port.mu = stock_returns.std().to_frame().T - - # Budget constraints - port.upperlng = value - if value_short > 0: - port.sht = True - port.uppersht = value_short - port.budget = value - value_short - else: - port.budget = value - - # Estimate optimal portfolio: - weights = port.optimization( - model="Classic", rm="MV", obj="Sharpe", rf=0, hist=True - ) - except Exception as _: - weights = None - - if weights is not None: - weights = weights.round(5) - weights = weights.squeeze().to_dict() - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_max_decorrelation_portfolio( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """Builds a maximal decorrelation portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - interval : str, optional - interval to get stock data, by default "3mo" - start_date: str - If not using interval, start date string (YYYY-MM-DD) - end_date: str - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see s`interpolate `__. - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount of money to allocate. The default is 1. - value_short : float, optional - Amount to allocate to portfolio in short positions. The default is 0. - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - value_short = get_kwarg("value_short", kwargs) - - covariance = get_kwarg("covariance", kwargs) - d_ewma = get_kwarg("d_ewma", kwargs) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - try: - # Building the portfolio object - port = rp.Portfolio(returns=stock_returns) - - # Estimate input parameters: - port.assets_stats(method_mu="hist", method_cov=covariance, d=d_ewma) - port.cov = rp.cov2corr(port.cov) - - # Budget constraints - port.upperlng = value - if value_short > 0: - port.sht = True - port.uppersht = value_short - port.budget = value - value_short - else: - port.budget = value - - # Estimate optimal portfolio: - weights = port.optimization( - model="Classic", rm="MV", obj="MinRisk", rf=0, hist=True - ) - except Exception as _: - weights = None - - if weights is not None: - weights = weights.round(5) - - weights = weights.squeeze().to_dict() if len(weights) > 1 else weights.to_dict() - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_black_litterman_portfolio( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """Builds a maximal diversification portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - benchmark : Dict - Dict of portfolio weights - p_views: List - Matrix P of views that shows relationships among assets and returns. - Default value to None. - q_views: List - Matrix Q of expected returns of views. Default value is None. - interval : str, optional - interval to get stock data, by default "3mo" - start_date: str - If not using interval, start date string (YYYY-MM-DD) - end_date: str - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - objective: str - Objective function of the optimization model. - The default is 'Sharpe'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_free_rate: float, optional - Risk free rate, must be in annual frequency. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - delta: float, optional - Risk aversion factor of Black Litterman model. Default value is None. - equilibrium: bool, optional - If True excess returns are based on equilibrium market portfolio, if False - excess returns are calculated as historical returns minus risk free rate. - Default value is True. - optimize: bool, optional - If True Black Litterman estimates are used as inputs of mean variance model, - if False returns equilibrium weights from Black Litterman model - Default value is True. - value : float, optional - Amount of money to allocate. The default is 1. - value_short : float, optional - Amount to allocate to portfolio in short positions. The default is 0. - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - value_short = get_kwarg("value_short", kwargs) - - benchmark = get_kwarg("benchmark", kwargs) - p_views = get_kwarg("p_views", kwargs) - q_views = get_kwarg("q_views", kwargs) - objective = get_kwarg("objective", kwargs) - risk_free_rate = get_kwarg("risk_free_rate", kwargs) - risk_aversion = get_kwarg("risk_aversion", kwargs) - delta = get_kwarg("delta", kwargs) - equilibrium = get_kwarg("equilibrium", kwargs) - optimize = get_kwarg("optimize", kwargs) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - # By theory default benchmark is market capitalization portfolio - if benchmark is None: - benchmark, _ = get_property_weights( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - value=value, - ) - - if benchmark is None: - return None, stock_returns - - factor = time_factor[freq.upper()] - risk_free_rate = risk_free_rate / factor - - mu, cov, weights = black_litterman( - stock_returns=stock_returns, - benchmark=benchmark, - p_views=p_views, - q_views=q_views, - delta=delta, - risk_free_rate=risk_free_rate, - equilibrium=equilibrium, - factor=factor, - ) - weights = pd.DataFrame(weights) - - if optimize: - try: - # Building the portfolio object - port = rp.Portfolio(returns=stock_returns) - - # Estimate input parameters: - port.assets_stats(method_mu="hist", method_cov="hist") - port.mu_bl = pd.DataFrame(mu).T - port.cov_bl = pd.DataFrame(cov) - - # Budget constraints - port.upperlng = value - if value_short > 0: - port.sht = True - port.uppersht = value_short - port.budget = value - value_short - else: - port.budget = value - - # Estimate optimal portfolio: - weights = port.optimization( - model="BL", - rm="MV", - obj=objective, - rf=risk_free_rate, - l=risk_aversion, - hist=True, - ) - except Exception as _: - weights = None - - if weights is not None: - weights = weights.round(5) - weights = weights.squeeze().to_dict() - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_ef( - symbols: List[str], - **kwargs, -) -> Tuple[ - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - Optional[pd.DataFrame], - NDArray[floating], - NDArray[floating], - rp.Portfolio, -]: - """ - Get efficient frontier - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - The default is 0.05. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - n_portfolios: int, optional - "Number of portfolios to simulate. The default value is 100. - seed: int, optional - Seed used to generate random portfolios. The default value is 123. - - Returns - ------- - Tuple[ - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - Optional[pd.DataFrame], - NDArray[floating], - NDArray[floating], - rp.Portfolio, - ] - Parameters to create efficient frontier: - frontier, mu, cov, stock_returns, weights, X1, Y1, port - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - value_short = get_kwarg("value_short", kwargs) - - risk_measure = get_kwarg("risk_measure", kwargs) - risk_free_rate = get_kwarg("risk_free_rate", kwargs) - alpha = get_kwarg("alpha", kwargs) - n_portfolios = get_kwarg("n_portfolios", kwargs) - seed = get_kwarg("seed", kwargs) - - risk_free_rate = risk_free_rate / time_factor[freq.upper()] - risk_measure = validate_risk_measure(risk_measure) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - try: - # Building the portfolio object - port = rp.Portfolio(returns=stock_returns, alpha=alpha) - - # Estimate input parameters: - port.assets_stats(method_mu="hist", method_cov="hist") - - # Budget constraints - port.upperlng = value - if value_short > 0: - port.sht = True - port.uppersht = value_short - port.budget = value - value_short - else: - port.budget = value - - # Estimate tangency portfolio: - weights: Optional[pd.DataFrame] = port.optimization( - model="Classic", - rm=risk_choices[risk_measure.lower()], - obj="Sharpe", - rf=risk_free_rate, - hist=True, - ) - except Exception as _: - weights = None - - points = 20 # Number of points of the frontier - frontier = port.efficient_frontier( - model="Classic", - rm=risk_choices[risk_measure.lower()], - points=points, - rf=risk_free_rate, - hist=True, - ) - - random_weights = generate_random_portfolios( - symbols=symbols, - n_portfolios=n_portfolios, - seed=seed, - ) - - mu = stock_returns.mean().to_frame().T - cov = stock_returns.cov() - Y = (mu @ frontier).to_numpy() * time_factor[freq.upper()] - Y = np.ravel(Y) - X = np.zeros_like(Y) - - for i in range(frontier.shape[1]): - w = np.array(frontier.iloc[:, i], ndmin=2).T - risk = rp.Sharpe_Risk( - w, - cov=cov, - returns=stock_returns, - rm=risk_choices[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=b_sim, - ) - X[i] = risk - - if risk_choices[risk_measure.lower()] not in ["ADD", "MDD", "CDaR", "EDaR", "UCI"]: - X = X * time_factor[freq.upper()] ** 0.5 - f = interp1d(X, Y, kind="quadratic") - X1 = np.linspace(X[0], X[-1], num=100) - Y1 = f(X1) - - frontier = pd.concat([frontier, random_weights], axis=1) - # to delete stocks with corrupted data - frontier.drop( - frontier.tail(len(random_weights.index) - len(stock_returns.columns)).index, - inplace=True, - ) - - return frontier, mu, cov, stock_returns, weights, X1, Y1, port - - -@log_start_end(log=logger) -def get_risk_parity_portfolio( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """Builds a risk parity portfolio using the risk budgeting approach - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - interval : str, optional - interval to get stock data, by default "3mo" - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - risk_cont: List[str], optional - The vector of risk contribution per asset. If empty, the default is - 1/n (number of assets). - risk_free_rate: float, optional - Risk free rate, must be in annual frequency. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount of money to allocate. The default is 1. - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - - risk_measure = get_kwarg("risk_measure", kwargs) - risk_free_rate = get_kwarg("risk_free_rate", kwargs) - alpha = get_kwarg("alpha", kwargs) - target_return = get_kwarg("target_return", kwargs) - mean = get_kwarg("mean", kwargs) - covariance = get_kwarg("covariance", kwargs) - d_ewma = get_kwarg("d_ewma", kwargs) - risk_cont = get_kwarg("risk_cont", kwargs) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - risk_free_rate = risk_free_rate / time_factor[freq.upper()] - risk_measure = validate_risk_measure(risk_measure) - - try: - # Building the portfolio object - port = rp.Portfolio(returns=stock_returns, alpha=alpha) - - # Calculating optimal portfolio - port.assets_stats(method_mu=mean, method_cov=covariance, d=d_ewma) - - # Estimate optimal portfolio: - model = "Classic" - hist = True - - if risk_cont is None: - risk_cont_ = None # Risk contribution constraints vector - else: - risk_cont_ = np.array(risk_cont).reshape(1, -1) - risk_cont_ = risk_cont_ / np.sum(risk_cont_) - - if target_return > -1: - port.lowerret = float(target_return) / time_factor[freq.upper()] - - weights = port.rp_optimization( - model=model, rm=risk_measure, rf=risk_free_rate, b=risk_cont_, hist=hist - ) - except Exception as _: - weights = None - - if weights is not None: - if value > 0.0: - weights = value * weights - weights = weights.round(5) - weights = weights.squeeze().to_dict() - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_rel_risk_parity_portfolio( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """Builds a relaxed risk parity portfolio using the least squares approach - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - interval : str, optional - interval to get stock data, by default "3mo" - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - version : str, optional - Relaxed risk parity model version. The default is 'A'. - Possible values are: - - - 'A': without regularization and penalization constraints. - - 'B': with regularization constraint but without penalization constraint. - - 'C': with regularization and penalization constraints. - - risk_cont: List[str], optional - The vector of risk contribution per asset. If empty, the default is - 1/n (number of assets). - penal_factor: float, optional - The penalization factor of penalization constraints. Only used with - version 'C'. The default is 1. - target_return: float, optional - Constraint on minimum level of portfolio's return. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount of money to allocate. The default is 1. - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - - target_return = get_kwarg("target_return", kwargs) - mean = get_kwarg("mean", kwargs) - covariance = get_kwarg("covariance", kwargs) - d_ewma = get_kwarg("d_ewma", kwargs) - risk_cont = get_kwarg("risk_cont", kwargs) - version = get_kwarg("version", kwargs) - penal_factor = get_kwarg("penal_factor", kwargs) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - try: - # Building the portfolio object - port = rp.Portfolio(returns=stock_returns) - - # Calculating optimal portfolio - port.assets_stats(method_mu=mean, method_cov=covariance, d=d_ewma) - - # Estimate optimal portfolio: - model = "Classic" - hist = True - - if risk_cont is None: - risk_cont_ = None # Risk contribution constraints vector - else: - risk_cont_ = np.array(risk_cont).reshape(1, -1) - risk_cont_ = risk_cont_ / np.sum(risk_cont_) - - if target_return > -1: - port.lowerret = float(target_return) / time_factor[freq.upper()] - - weights = port.rrp_optimization( - model=model, version=version, l=penal_factor, b=risk_cont_, hist=hist - ) - except Exception as _: - weights = None - - if weights is not None: - if value > 0.0: - weights = value * weights - weights = weights.round(5) - weights = weights.squeeze().to_dict() - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_hcp_portfolio( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """Builds hierarchical clustering based portfolios - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - interval : str, optional - interval to get stock data, by default "3mo" - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - model: str, optional - The hierarchical cluster portfolio model used for optimize the - portfolio. The default is 'HRP'. Possible values are: - - - 'HRP': Hierarchical Risk Parity. - - 'HERC': Hierarchical Equal Risk Contribution. - - 'NCO': Nested Clustered Optimization. - - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})} - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})} - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)} - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)} - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})} - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}}. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `c-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `c-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `c-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `c-MLforAM`. - - objective: str, optional - Objective function used by the NCO model. - The default is 'MinRisk'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'ERC': Equally risk contribution portfolio of the selected risk measure. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in annual frequency. - Used for 'FLPM' and 'SLPM'. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic. - The default is None. - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters. The default is 10. - bins_info: str, optional - Number of bins used to calculate variation of information. The default - value is 'KN'. Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index. The default is 0.05. - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal. The default is True. - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount of money to allocate. The default is 1. - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - interval = get_kwarg("interval", kwargs) - start_date = get_kwarg("start_date", kwargs) - end_date = get_kwarg("end_date", kwargs) - log_returns = get_kwarg("log_returns", kwargs) - freq = get_kwarg("freq", kwargs) - maxnan = get_kwarg("maxnan", kwargs) - threshold = get_kwarg("threshold", kwargs) - method = get_kwarg("method", kwargs) - value = get_kwarg("value", kwargs) - - objective = get_kwarg("objective", kwargs) - risk_measure = get_kwarg("risk_measure", kwargs) - risk_free_rate = get_kwarg("risk_free_rate", kwargs) - risk_aversion = get_kwarg("risk_aversion", kwargs) - alpha = get_kwarg("alpha", kwargs) - a_sim = get_kwarg("a_sim", kwargs) - beta = get_kwarg("beta", kwargs) - b_sim = get_kwarg("b_sim", kwargs) - covariance = get_kwarg("covariance", kwargs) - d_ewma = get_kwarg("d_ewma", kwargs) - - model = get_kwarg("model", kwargs, default="HRP") - - codependence = get_kwarg("codependence", kwargs) - linkage = get_kwarg("linkage", kwargs) - k = get_kwarg("k", kwargs) - max_k = get_kwarg("max_k", kwargs) - bins_info = get_kwarg("bins_info", kwargs) - alpha_tail = get_kwarg("alpha_tail", kwargs) - leaf_order = get_kwarg("leaf_order", kwargs) - - risk_measure = validate_risk_measure(risk_measure) - - stock_prices = yahoo_finance_model.process_stocks( - symbols, interval, start_date, end_date - ) - stock_returns = yahoo_finance_model.process_returns( - stock_prices, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - ) - - if linkage == "dbht": - linkage = linkage.upper() - - risk_free_rate = risk_free_rate / time_factor[freq.upper()] - - try: - # Building the portfolio object - port = rp.HCPortfolio( - returns=stock_returns, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - ) - - weights = port.optimization( - model=model, - codependence=codependence, - covariance=covariance, - obj=objective, - rm=risk_measure, - rf=risk_free_rate, - l=risk_aversion, - linkage=linkage, - k=k, - max_k=max_k, - bins_info=bins_info, - alpha_tail=alpha_tail, - leaf_order=leaf_order, - d=d_ewma, - ) - except Exception as _: - weights = None - - if weights is not None: - if value > 0.0: - weights = value * weights - weights = weights.round(5) - weights = weights.squeeze().to_dict() - - return weights, stock_returns - - -@log_start_end(log=logger) -def get_hrp( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """ - Builds a hierarchical risk parity portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})} - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})} - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)} - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)} - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})} - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}}. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `c-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `c-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `c-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `c-MLforAM`. - - objective: str, optional - Objective function used by the NCO model. - The default is 'MinRisk'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'ERC': Equally risk contribution portfolio of the selected risk measure. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. - Used for 'FLPM' and 'SLPM'. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic. - The default is None. - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters. The default is 10. - bins_info: str, optional - Number of bins used to calculate variation of information. The default - value is 'KN'. Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index. The default is 0.05. - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal. The default is True. - d: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - - weights, stock_returns = get_hcp_portfolio( - symbols=symbols, - model="HRP", - **kwargs, - ) - return weights, stock_returns - - -@log_start_end(log=logger) -def get_herc( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """ - Builds a hierarchical risk parity portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})} - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})} - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)} - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)} - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})} - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}}. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `c-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `c-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `c-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `c-MLforAM`. - - objective: str, optional - Objective function used by the NCO model. - The default is 'MinRisk'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'ERC': Equally risk contribution portfolio of the selected risk measure. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. - Used for 'FLPM' and 'SLPM'. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic. - The default is None. - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters. The default is 10. - bins_info: str, optional - Number of bins used to calculate variation of information. The default - value is 'KN'. Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index. The default is 0.05. - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal. The default is True. - d: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - weights, stock_returns = get_hcp_portfolio( - symbols=symbols, - model="HERC", - **kwargs, - ) - return weights, stock_returns - - -@log_start_end(log=logger) -def get_nco( - symbols: List[str], - **kwargs, -) -> Tuple[Optional[dict], pd.DataFrame]: - """ - Builds a hierarchical risk parity portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})} - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})} - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)} - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)} - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})} - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}}. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `c-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `c-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `c-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `c-MLforAM`. - - objective: str, optional - Objective function used by the NCO model. - The default is 'MinRisk'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'ERC': Equally risk contribution portfolio of the selected risk measure. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. - Used for 'FLPM' and 'SLPM'. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic. - The default is None. - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters. The default is 10. - bins_info: str, optional - Number of bins used to calculate variation of information. The default - value is 'KN'. Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index. The default is 0.05. - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal. The default is True. - d: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - - Returns - ------- - Tuple[Optional[dict], pd.DataFrame] - Dictionary of portfolio weights, - DataFrame of stock returns. - """ - weights, stock_returns = get_hcp_portfolio( - symbols=symbols, - model="NCO", - **kwargs, - ) - return weights, stock_returns - - -@log_start_end(log=logger) -def black_litterman( - stock_returns: pd.DataFrame, - benchmark, - **kwargs, -) -> Tuple[dict, dict, dict]: - """ - Calculates Black-Litterman estimates following He and Litterman (1999) - - Parameters - ---------- - stock_returns: pd.DataFrame - _description_ - benchmark: Dict - Dict of portfolio weights - p_views: List - Matrix P of views that shows relationships among assets and returns. - Default value to None. - q_views: List - Matrix Q of expected returns of views in annual frequency. Default value is None. - delta: float - Risk aversion factor. Default value is None. - risk_free_rate: float, optional - Risk free rate, must be in annual frequency. Default value is 0. - equilibrium: bool, optional - If True excess returns are based on equilibrium market portfolio, if False - excess returns are calculated as historical returns minus risk free rate. - Default value is True. - factor: int - The time factor - - Returns - ------- - Tuple[dict, dict, dict] - Black-Litterman model estimates of expected returns, - Covariance matrix, - Portfolio weights. - """ - - p_views = get_kwarg("p_views", kwargs) - q_views = get_kwarg("q_views", kwargs) - delta = get_kwarg("delta", kwargs) - risk_free_rate = get_kwarg("risk_free_rate", kwargs) - equilibrium = get_kwarg("equilibrium", kwargs) - factor = get_kwarg("factor", kwargs, default=252) - - symbols = stock_returns.columns.tolist() - benchmark = pd.Series(benchmark).to_numpy().reshape(-1, 1) - - mu = stock_returns.mean().to_numpy().reshape(-1, 1) - S = stock_returns.cov().to_numpy() - - if delta is None: - a = mu.T @ benchmark - delta = (a - risk_free_rate) / (benchmark.T @ S @ benchmark) - delta = delta.item() - - PI_eq = delta * (S @ benchmark) if equilibrium else mu - risk_free_rate - - flag = False - if p_views is None or q_views is None: - p_views = np.identity(S.shape[0]) - q_views = PI_eq - flag = True - else: - p_views = np.array(p_views, dtype=float) - q_views = np.array(q_views, dtype=float).reshape(-1, 1) / factor - - tau = 1 / stock_returns.shape[0] - Omega = np.diag(np.diag(p_views @ (tau * S) @ p_views.T)) - - PI = np.linalg.inv( - np.linalg.inv(tau * S) + p_views.T @ np.linalg.inv(Omega) @ p_views - ) @ (np.linalg.inv(tau * S) @ PI_eq + p_views.T @ np.linalg.inv(Omega) @ q_views) - - if flag: - n, m = S.shape - M = np.zeros([n, m]) - else: - M = np.linalg.inv( - np.linalg.inv(tau * S) + p_views.T @ np.linalg.inv(Omega) @ p_views - ) - - mu = PI + risk_free_rate - cov = S + M - weights = np.linalg.inv(delta * cov) @ PI - - mu = pd.DataFrame(mu, index=symbols).to_dict() - cov = pd.DataFrame(cov, index=symbols, columns=symbols).to_dict() - weights = pd.DataFrame(weights, index=symbols).to_dict() - - return mu, cov, weights - - -@log_start_end(log=logger) -def generate_random_portfolios( - symbols: List[str], - n_portfolios: int = 100, - seed: int = 123, - value: float = 1.0, -) -> pd.DataFrame: - """Build random portfolios - - Parameters - ---------- - symbols : List[str] - List of portfolio stocks - n_portfolios: int, optional - "Number of portfolios to simulate. The default value is 100. - seed: int, optional - Seed used to generate random portfolios. The default value is 123. - value : float, optional - Amount of money to allocate. The default is 1. - """ - assets = symbols.copy() - - # Generate random portfolios - n_samples = int(n_portfolios / 3) - rs = np.random.RandomState(seed=seed) - - # Equal probability for each asset - w1 = rs.dirichlet(np.ones(len(assets)), n_samples) - - # More concentrated - w2 = rs.dirichlet(np.ones(len(assets)) * 0.65, n_samples) - - # More diversified - w3 = rs.dirichlet(np.ones(len(assets)) * 2, n_samples) - - # Each individual asset - w4 = np.identity(len(assets)) - w = np.concatenate((w1, w2, w3, w4), axis=0) - w = pd.DataFrame(w, columns=assets).T - - if value > 0.0: - w = value * w - - return w - - -@log_start_end(log=logger) -def get_categories( - weights: dict, categories: dict, column: str = "ASSET_CLASS" -) -> pd.DataFrame: - """Get categories from dictionary - - Parameters - ---------- - weights : dict - Dictionary with weights - categories: dict - Dictionary with categories - column : str, optional - Column name to use on categories, by default "ASSET_CLASS" - - Returns - ------- - pd.DataFrame - DataFrame with weights - """ - - if not weights: - return pd.DataFrame() - - if column == "CURRENT_INVESTED_AMOUNT": - return pd.DataFrame() - - df = pd.DataFrame.from_dict( - data=weights, orient="index", columns=["value"], dtype=float - ) - categories_df = pd.DataFrame.from_dict(data=categories, dtype=float) - - categories_df = df.join(categories_df) - categories_df.set_index(column, inplace=True) - categories_df.groupby(level=0).sum() - - df = pd.pivot_table( - categories_df, - values=["value", "CURRENT_INVESTED_AMOUNT"], - index=["CURRENCY", column], - aggfunc=np.sum, - ) - df["CURRENT_WEIGHTS"] = ( - df["CURRENT_INVESTED_AMOUNT"].groupby(level=0).transform(lambda x: x / sum(x)) - ) - df["value"] = df["value"].groupby(level=0).transform(lambda x: x / sum(x)) - df = pd.concat( - [d.append(d.sum().rename((k, "TOTAL " + k))) for k, d in df.groupby(level=0)] - ) - df = df.iloc[:, [0, 2, 1]] - - return df diff --git a/openbb_terminal/portfolio/portfolio_optimization/optimizer_view.py b/openbb_terminal/portfolio/portfolio_optimization/optimizer_view.py deleted file mode 100644 index ce8334568fe..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/optimizer_view.py +++ /dev/null @@ -1,4081 +0,0 @@ -"""Optimization View""" - -__docformat__ = "numpy" - -# pylint: disable=R0913, R0914, C0302, too-many-branches, too-many-statements, line-too-long -# flake8: noqa: E501 - -# IMPORTS STANDARD -import logging -import math -import warnings -from datetime import date -from typing import Any, Dict, List, Optional - -# IMPORTS THIRD-PARTY -import matplotlib -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import riskfolio as rp -from dateutil.relativedelta import FR, relativedelta -from matplotlib.gridspec import GridSpec -from matplotlib.lines import Line2D - -from openbb_terminal.config_terminal import theme - -# IMPORTS INTERNAL -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import plot_autoscale, print_rich_table -from openbb_terminal.portfolio.portfolio_optimization import ( - optimizer_helper, - optimizer_model, -) -from openbb_terminal.rich_config import console - -warnings.filterwarnings("ignore") - -logger = logging.getLogger(__name__) - -objectives_choices = { - "minrisk": "MinRisk", - "sharpe": "Sharpe", - "utility": "Utility", - "maxret": "MaxRet", - "erc": "ERC", -} - -risk_names = { - "mv": "volatility", - "mad": "mean absolute deviation", - "gmd": "gini mean difference", - "msv": "semi standard deviation", - "var": "value at risk (VaR)", - "cvar": "conditional value at risk (CVaR)", - "tg": "tail gini", - "evar": "entropic value at risk (EVaR)", - "rg": "range", - "cvrg": "CVaR range", - "tgrg": "tail gini range", - "wr": "worst realization", - "flpm": "first lower partial moment", - "slpm": "second lower partial moment", - "mdd": "maximum drawdown uncompounded", - "add": "average drawdown uncompounded", - "dar": "drawdown at risk (DaR) uncompounded", - "cdar": "conditional drawdown at risk (CDaR) uncompounded", - "edar": "entropic drawdown at risk (EDaR) uncompounded", - "uci": "ulcer index uncompounded", - "mdd_rel": "maximum drawdown compounded", - "add_rel": "average drawdown compounded", - "dar_rel": "drawdown at risk (DaR) compounded", - "cdar_rel": "conditional drawdown at risk (CDaR) compounded", - "edar_rel": "entropic drawdown at risk (EDaR) compounded", - "uci_rel": "ulcer index compounded", -} - -risk_choices = { - "mv": "MV", - "mad": "MAD", - "gmd": "GMD", - "msv": "MSV", - "var": "VaR", - "cvar": "CVaR", - "tg": "TG", - "evar": "EVaR", - "rg": "RG", - "cvrg": "CVRG", - "tgrg": "TGRG", - "wr": "WR", - "flpm": "FLPM", - "slpm": "SLPM", - "mdd": "MDD", - "add": "ADD", - "dar": "DaR", - "cdar": "CDaR", - "edar": "EDaR", - "uci": "UCI", - "mdd_rel": "MDD_Rel", - "add_rel": "ADD_Rel", - "dar_rel": "DaR_Rel", - "cdar_rel": "CDaR_Rel", - "edar_rel": "EDaR_Rel", - "uci_rel": "UCI_Rel", -} - -time_factor = { - "D": 252.0, - "W": 52.0, - "M": 12.0, -} - -dict_conversion = {"period": "historic_period", "start": "start_period"} - - -@log_start_end(log=logger) -def d_period(interval: str = "1y", start_date: str = "", end_date: str = ""): - """ - Builds a date range string - - Parameters - ---------- - interval : str - interval starting today - start_date: str - If not using interval, start date string (YYYY-MM-DD) - end_date: str - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - """ - extra_choices = { - "ytd": "[Year-to-Date]", - "max": "[All-time]", - } - - if start_date == "": - if interval in extra_choices: - p = extra_choices[interval] - elif interval[-1] == "d": - p = "[" + interval[:-1] + " Days]" - elif interval[-1] == "w": - p = "[" + interval[:-1] + " Weeks]" - elif interval[-1] == "o": - p = "[" + interval[:-2] + " Months]" - elif interval[-1] == "y": - p = "[" + interval[:-1] + " Years]" - if p[1:3] == "1 ": - p = p.replace("s", "") - else: - if end_date == "": - end_ = date.today() - if end_.weekday() >= 5: - end_ = end_ + relativedelta(weekday=FR(-1)) - end_date = end_.strftime("%Y-%m-%d") - p = "[From " + start_date + " to " + end_date + "]" - - return p - - -@log_start_end(log=logger) -def portfolio_performance( - weights: dict, - data: pd.DataFrame, - freq: str = "D", - risk_measure: str = "MV", - risk_free_rate: float = 0, - alpha: float = 0.05, - a_sim: float = 100, - beta: Optional[float] = None, - b_sim: Optional[float] = None, -): - """ - Prints portfolio performance indicators - - Parameters - ---------- - weights: dict - Portfolio weights - data: pd.DataFrame - Stock returns dataframe - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - risk_measure : str, optional - The risk measure used. The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate : float, optional - risk free rate. - alpha : float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of - losses. The default is 0.05. - a_sim : float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default - is 100. - beta : float, optional - Significance level of CVaR and Tail Gini of gains. If None it - duplicates alpha value. The default is None. - b_sim : float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None - it duplicates a_sim value. The default is None. - """ - - try: - freq = freq.upper() - weights = pd.Series(weights).to_frame() - returns = data @ weights - mu = returns.mean().item() * time_factor[freq] - sigma = returns.std().item() * time_factor[freq] ** 0.5 - sharpe = (mu - risk_free_rate) / sigma - - factor_1 = str(int(time_factor[freq])) + ") " - factor_2 = "√" + factor_1 - - console.print("\nAnnual (by " + factor_1 + f"expected return: {100 * mu:.2f}%") - console.print("Annual (by " + factor_2 + f"volatility: {100 * sigma:.2f}%") - console.print(f"Sharpe ratio: {sharpe:.4f}") - - if risk_measure != "MV": - risk = rp.Sharpe_Risk( - weights, - cov=data.cov(), - returns=data, - rm=risk_measure, - rf=risk_free_rate, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - ) - - drawdowns = [ - "MDD", - "ADD", - "DaR", - "CDaR", - "EDaR", - "UCI", - "MDD_Rel", - "ADD_Rel", - "DaR_Rel", - "CDaR_Rel", - "EDaR_Rel", - "UCI_Rel", - ] - - if risk_measure in drawdowns: - sharpe_2 = (mu - risk_free_rate) / risk - console.print( - risk_names[risk_measure.lower()].capitalize() - + " : " - + f"{100 * risk:.2f}%" - ) - else: - risk = risk * time_factor[freq] ** 0.5 - sharpe_2 = (mu - risk_free_rate) / risk - console.print( - "Annual (by " - + factor_2 - + risk_names[risk_measure.lower()] - + " : " - + f"{100 * risk:.2f}%" - ) - - console.print( - "Return / " - + risk_names[risk_measure.lower()] - + f" ratio: {sharpe_2:.4f}" - ) - except Exception as _: - console.print( - "[red]\nFailed to calculate portfolio performance indicators.[/red]" - ) - - -@log_start_end(log=logger) -def display_weights(weights: dict, market_neutral: bool = False): - """ - Prints weights in a nice format - - Parameters - ---------- - weights: dict - weights to display. Keys are stocks. Values are either weights or values - market_neutral : bool - Flag indicating shorting allowed (negative weights) - """ - - df = optimizer_helper.dict_to_df(weights) - - if df.empty: - return - - if math.isclose(df.sum()["value"], 1, rel_tol=0.5): - df["value"] = (df["value"] * 100).apply(lambda s: f"{s:.2f}") + " %" - df["value"] = ( - df["value"] - .astype(str) - .apply(lambda s: " " * (8 - len(s)) + s if len(s) < 8 else "" + s) - ) - else: - df["value"] = (df["value"] * 100).apply(lambda s: f"{s:.0f}") + " $" - df["value"] = ( - df["value"] - .astype(str) - .apply(lambda s: " " * (16 - len(s)) + s if len(s) < 16 else "" + s) - ) - - if market_neutral: - tot_value = df["value"].abs().mean() - header = "Value ($)" if tot_value > 1.01 else "Value (%)" - print_rich_table(df, headers=[header], show_index=True, title="Weights") - else: - print_rich_table(df, headers=["Value"], show_index=True, title="Weights") - - -@log_start_end(log=logger) -def display_weights_sa(weights: dict, weights_sa: dict): - """ - Prints weights in a nice format - - Parameters - ---------- - weights: dict - weights to display. Keys are stocks. Values are either weights or values - weights_sa: dict - weights of sensitivity analysis to display. Keys are stocks. - Values are either weights or values - """ - if not weights or not weights_sa: - return - weight_df = pd.DataFrame.from_dict( - data=weights, orient="index", columns=["value"], dtype=float - ) - weight_sa_df = pd.DataFrame.from_dict( - data=weights_sa, orient="index", columns=["value s.a."], dtype=float - ) - weight_df = weight_df.join(weight_sa_df, how="inner") - weight_df["value vs value s.a."] = weight_df["value"] - weight_df["value s.a."] - - weight_df["value"] = (weight_df["value"] * 100).apply(lambda s: f"{s:.2f}") + " %" - weight_df["value"] = ( - weight_df["value"] - .astype(str) - .apply(lambda s: " " * (8 - len(s)) + s if len(s) < 8 else "" + s) - ) - weight_df["value s.a."] = (weight_df["value s.a."] * 100).apply( - lambda s: f"{s:.2f}" - ) + " %" - weight_df["value s.a."] = ( - weight_df["value s.a."] - .astype(str) - .apply( - lambda s: ( - " " * (len("value s.a.") - len(s)) + s - if len(s) < len("value s.a.") - else "" + s - ) - ) - ) - weight_df["value vs value s.a."] = (weight_df["value vs value s.a."] * 100).apply( - lambda s: f"{s:.2f}" - ) + " %" - weight_df["value vs value s.a."] = ( - weight_df["value vs value s.a."] - .astype(str) - .apply( - lambda s: ( - " " * (len("value vs value s.a.") - len(s)) + s - if len(s) < len("value vs value s.a.") - else "" + s - ) - ) - ) - - headers = list(weight_df.columns) - headers = [s.title() for s in headers] - print_rich_table( - weight_df, headers=headers, show_index=True, title="Weights Comparison" - ) - - -@log_start_end(log=logger) -def display_categories( - weights: dict, categories: dict, column: str = "ASSET_CLASS", title: str = "" -): - """ - Prints categories in a nice format - - Parameters - ---------- - weights: dict - weights to display. Keys are stocks. Values are either weights or values - categories: dict - categories to display. Keys are stocks. Values are either weights or values - column: str - column selected to show table - - ASSET_CLASS - - SECTOR - - INDUSTRY - - COUNTRY - title: str - title to display - """ - - if column == "CURRENT_INVESTED_AMOUNT": - console.print(f"[yellow]'{column}' cannot be displayed as a category.[/yellow]") - return - - df = optimizer_model.get_categories(weights, categories, column) - - if df.empty: - return - - df["value"] = (df["value"] * 100).apply(lambda s: f"{s:.2f}") + " %" - df["value"] = ( - df["value"] - .astype(str) - .apply(lambda s: " " * (8 - len(s)) + s if len(s) < 8 else "" + s) - ) - df["CURRENT_WEIGHTS"] = (df["CURRENT_WEIGHTS"] * 100).apply( - lambda s: f"{s:.2f}" - ) + " %" - df["CURRENT_WEIGHTS"] = ( - df["CURRENT_WEIGHTS"] - .astype(str) - .apply( - lambda s: ( - " " * (len("CURRENT_WEIGHTS") - len(s)) + s - if len(s) < len("CURRENT_WEIGHTS") - else "" + s - ) - ) - ) - df["CURRENT_INVESTED_AMOUNT"] = ( - df["CURRENT_INVESTED_AMOUNT"].apply(lambda s: f"{s:,.0f}") + " $" - ) - df["CURRENT_INVESTED_AMOUNT"] = ( - df["CURRENT_INVESTED_AMOUNT"] - .astype(str) - .apply( - lambda s: ( - " " * (len("CURRENT_INVESTED_AMOUNT") - len(s)) + s - if len(s) < len("CURRENT_INVESTED_AMOUNT") - else "" + s - ) - ) - ) - - df.reset_index(level=1, inplace=True) - headers = [s.title() for s in list(df.columns)] - show_index = True - if column == "CURRENCY": - show_index = False - - print_rich_table(df, headers=headers, show_index=show_index, title=title) - - -@log_start_end(log=logger) -def display_categories_sa( - weights: dict, weights_sa: dict, categories: dict, column: str, title: str = "" -): - """ - Prints categories in a nice format - - Parameters - ---------- - weights: dict - weights to display. Keys are stocks. Values are either weights or values - weights_sa: dict - weights of sensitivity analysis to display. Keys are stocks. Values are either weights or values - categories: dict - categories to display. Keys are stocks. Values are either weights or values - column: int. - column selected to show table - - ASSET_CLASS - - SECTOR - - INDUSTRY - - COUNTRY - """ - if not weights or not weights_sa: - return - weight_df = pd.DataFrame.from_dict( - data=weights, orient="index", columns=["value"], dtype=float - ) - weight_sa_df = pd.DataFrame.from_dict( - data=weights_sa, orient="index", columns=["value s.a."], dtype=float - ) - categories_df = pd.DataFrame.from_dict(data=categories, dtype=float) - - col = list(categories_df.columns).index(column) - categories_df = weight_df.join(categories_df.iloc[:, [col, 4, 5]], how="inner") - categories_df = categories_df.join(weight_sa_df, how="inner") - categories_df.set_index(column, inplace=True) - categories_df.groupby(level=0).sum() - - table_df = pd.pivot_table( - categories_df, - values=["value", "value s.a.", "CURRENT_INVESTED_AMOUNT"], - index=["CURRENCY", column], - aggfunc=np.sum, - ) - table_df["CURRENT_WEIGHTS"] = ( - table_df["CURRENT_INVESTED_AMOUNT"] - .groupby(level=0) - .transform(lambda x: x / sum(x)) - ) - table_df["value"] = ( - table_df["value"].groupby(level=0).transform(lambda x: x / sum(x)) - ) - table_df["value s.a."] = ( - table_df["value s.a."].groupby(level=0).transform(lambda x: x / sum(x)) - ) - table_df = pd.concat( - [ - d.append(d.sum().rename((k, "TOTAL " + k))) - for k, d in table_df.groupby(level=0) - ] - ) - table_df["value vs value s.a."] = table_df["value"] - table_df["value s.a."] - - table_df = table_df.iloc[:, [0, 3, 1, 2, 4]] - - table_df["value"] = (table_df["value"] * 100).apply(lambda s: f"{s:.2f}") + " %" - table_df["value"] = ( - table_df["value"] - .astype(str) - .apply(lambda s: " " * (8 - len(s)) + s if len(s) < 8 else "" + s) - ) - table_df["value s.a."] = (table_df["value s.a."] * 100).apply( - lambda s: f"{s:.2f}" - ) + " %" - table_df["value s.a."] = ( - table_df["value s.a."] - .astype(str) - .apply( - lambda s: ( - " " * (len("value s.a.") - len(s)) + s - if len(s) < len("value s.a.") - else "" + s - ) - ) - ) - table_df["value vs value s.a."] = (table_df["value vs value s.a."] * 100).apply( - lambda s: f"{s:.2f}" - ) + " %" - table_df["value vs value s.a."] = ( - table_df["value vs value s.a."] - .astype(str) - .apply( - lambda s: ( - " " * (len("value vs value s.a.") - len(s)) + s - if len(s) < len("value vs value s.a.") - else "" + s - ) - ) - ) - table_df["CURRENT_WEIGHTS"] = (table_df["CURRENT_WEIGHTS"] * 100).apply( - lambda s: f"{s:.2f}" - ) + " %" - table_df["CURRENT_WEIGHTS"] = ( - table_df["CURRENT_WEIGHTS"] - .astype(str) - .apply( - lambda s: ( - " " * (len("CURRENT_WEIGHTS") - len(s)) + s - if len(s) < len("CURRENT_WEIGHTS") - else "" + s - ) - ) - ) - table_df["CURRENT_INVESTED_AMOUNT"] = ( - table_df["CURRENT_INVESTED_AMOUNT"].apply(lambda s: f"{s:,.0f}") + " $" - ) - table_df["CURRENT_INVESTED_AMOUNT"] = ( - table_df["CURRENT_INVESTED_AMOUNT"] - .astype(str) - .apply( - lambda s: ( - " " * (len("CURRENT_INVESTED_AMOUNT") - len(s)) + s - if len(s) < len("CURRENT_INVESTED_AMOUNT") - else "" + s - ) - ) - ) - - table_df.reset_index(inplace=True) - table_df.set_index("CURRENCY", inplace=True) - - headers = list(table_df.columns) - headers = [s.title() for s in headers] - print_rich_table(table_df, headers=headers, show_index=True, title=title) - - -@log_start_end(log=logger) -def display_equal_weight( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure="mv", - risk_free_rate: float = 0, - alpha: float = 0.05, - value: float = 1, - table: bool = False, -) -> Dict: - """ - Equally weighted portfolio, where weight = 1/# of symbols - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False. - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR. - value : float, optional - Amount to allocate to portfolio, by default 1.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Equally Weighted Portfolio\n" - - weights, stock_returns = optimizer_model.get_equal_weights( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - value=value, - ) - - if not weights: - console.print("There is no solution with these parameters.") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=beta_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_property_weighting( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure: str = "mv", - risk_free_rate: float = 0, - alpha: float = 0.05, - value: float = 1, - table: bool = False, -) -> Dict[str, float]: - """ - Builds a portfolio weighted by selected property - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - s_property : str - Property to get weighted portfolio of - risk_measure: str, optional - The risk measure used to compute indicators. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR. - value : float, optional - Amount to allocate to portfolio, by default 1.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Weighted Portfolio based on Market Cap \n" - - weights, stock_returns = optimizer_model.get_property_weights( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - value=value, - ) - - if not weights: - console.print("There is no solution with these parameters.") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=beta_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_mean_risk( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure: str = "mv", - objective: str = "sharpe", - risk_free_rate: float = 0, - risk_aversion: float = 1, - alpha: float = 0.05, - target_return: float = -1, - target_risk: float = -1, - mean: str = "hist", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - value_short: float = 0.0, - table: bool = False, -) -> Dict: - """ - Builds a mean risk optimal portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - objective: str - Objective function of the optimization model. - The default is 'Sharpe'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - if objective == "sharpe": - s_title = f"{p} Maximal return/risk ratio portfolio using " - elif objective == "minrisk": - s_title = f"{p} Minimum risk portfolio using " - elif objective == "maxret": - s_title = f"{p} Maximal return portfolio using " - elif objective == "utility": - s_title = f"{p} Maximal risk averse utility function portfolio using " - s_title += risk_names[risk_measure] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_mean_risk_portfolio( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - risk_measure=risk_choices[risk_measure], - objective=objectives_choices[objective], - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - target_return=target_return, - target_risk=target_risk, - mean=mean, - covariance=covariance, - d_ewma=d_ewma, - value=value, - value_short=value_short, - ) - - if not weights: - console.print("There is no solution with these parameters.") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=beta_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_max_sharpe( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure: str = "MV", - risk_free_rate: float = 0, - risk_aversion: float = 1, - alpha: float = 0.05, - target_return: float = -1, - target_risk: float = -1, - mean: str = "hist", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - value_short: float = 0.0, - table: bool = False, -) -> Dict: - """ - Builds a maximal return/risk ratio portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - - p = d_period(interval, start_date, end_date) - s_title = f"{p} Maximal return/risk ratio portfolio using " - s_title += risk_names[risk_measure.lower()] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_max_sharpe( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - risk_measure=risk_measure, - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - target_return=target_return, - target_risk=target_risk, - mean=mean, - covariance=covariance, - d_ewma=d_ewma, - value=value, - value_short=value_short, - ) - - if not weights: - console.print("There is no solution with these parameters.") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=beta_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_min_risk( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure: str = "MV", - risk_free_rate: float = 0, - risk_aversion: float = 1, - alpha: float = 0.05, - target_return: float = -1, - target_risk: float = -1, - mean: str = "hist", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - value_short: float = 0.0, - table: bool = False, -) -> Dict: - """ - Builds a minimum risk portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Minimum risk portfolio using " - s_title += risk_names[risk_measure.lower()] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_min_risk( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - risk_measure=risk_measure, - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - target_return=target_return, - target_risk=target_risk, - mean=mean, - covariance=covariance, - d_ewma=d_ewma, - value=value, - value_short=value_short, - ) - - if not weights: - console.print("There is no solution with these parameters.") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=beta_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_max_util( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure: str = "MV", - risk_free_rate: float = 0, - risk_aversion: float = 1, - alpha: float = 0.05, - target_return: float = -1, - target_risk: float = -1, - mean: str = "hist", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - value_short: float = 0.0, - table: bool = False, -) -> Dict: - """ - Builds a maximal risk averse utility portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Maximal risk averse utility function portfolio using " - s_title += risk_names[risk_measure.lower()] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_max_util( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - risk_measure=risk_measure, - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - target_return=target_return, - target_risk=target_risk, - mean=mean, - covariance=covariance, - d_ewma=d_ewma, - value=value, - value_short=value_short, - ) - - if not weights: - console.print("There is no solution with these parameters.") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=beta_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_max_ret( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure: str = "MV", - risk_free_rate: float = 0, - risk_aversion: float = 1, - alpha: float = 0.05, - target_return: float = -1, - target_risk: float = -1, - mean: str = "hist", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - value_short: float = 0.0, - table: bool = False, -) -> Dict: - """ - Builds a maximal return portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - target_risk: float, optional - Constraint on maximum level of portfolio's risk. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Maximal risk averse utility function portfolio using " - s_title += risk_names[risk_measure.lower()] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_max_ret( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - risk_measure=risk_measure, - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - target_return=target_return, - target_risk=target_risk, - mean=mean, - covariance=covariance, - d_ewma=d_ewma, - value=value, - value_short=value_short, - ) - - if not weights: - console.print("There is no solution with these parameters.") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=beta_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_max_div( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - value_short: float = 0.0, - table: bool = False, -) -> Dict: - """ - Builds a maximal diversification portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Maximal diversification portfolio\n" - - weights, stock_returns = optimizer_model.get_max_diversification_portfolio( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - covariance=covariance, - d_ewma=d_ewma, - value=value, - value_short=value_short, - ) - - if not weights: - console.print("There is no solution with these parameters.") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure="MV", - risk_free_rate=0, - # alpha=0.05, - # a_sim=100, - # beta=None, - # b_sim=None, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_max_decorr( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - value_short: float = 0.0, - table: bool = False, -) -> Dict: - """ - Builds a maximal decorrelation portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Maximal decorrelation portfolio\n" - - weights, stock_returns = optimizer_model.get_max_decorrelation_portfolio( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - covariance=covariance, - d_ewma=d_ewma, - value=value, - value_short=value_short, - ) - - if not weights: - console.print("There is no solution with this parameters") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure="MV", - risk_free_rate=0, - # alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_simb_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_black_litterman( - symbols: List[str], - p_views: Optional[List] = None, - q_views: Optional[List] = None, - benchmark: Optional[Dict] = None, - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - objective: str = "Sharpe", - risk_free_rate: float = 0, - risk_aversion: float = 1, - delta: Optional[float] = None, - equilibrium: bool = True, - optimize: bool = True, - value: float = 1.0, - value_short: float = 0, - table: bool = False, -) -> Dict: - """ - Builds a black litterman portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - p_views: List - Matrix P of views that shows relationships among assets and returns. - Default value to None. - q_views: List - Matrix Q of expected returns of views. Default value is None. - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - benchmark : Dict - Dict of portfolio weights - objective: str - Objective function of the optimization model. - The default is 'Sharpe'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_free_rate: float, optional - Risk free rate, must be in annual frequency. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - delta: float, optional - Risk aversion factor of Black Litterman model. Default value is None. - equilibrium: bool, optional - If True excess returns are based on equilibrium market portfolio, if False - excess returns are calculated as historical returns minus risk free rate. - Default value is True. - optimize: bool, optional - If True Black Litterman estimates are used as inputs of mean variance model, - if False returns equilibrium weights from Black Litterman model - Default value is True. - value : float, optional - Amount of money to allocate. The default is 1. - value_short : float, optional - Amount to allocate to portfolio in short positions. The default is 0. - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Black Litterman portfolio\n" - weights, stock_returns = optimizer_model.get_black_litterman_portfolio( - symbols=symbols, - benchmark=benchmark, - p_views=p_views, - q_views=q_views, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - objective=objectives_choices[objective], - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - delta=delta, - equilibrium=equilibrium, - optimize=optimize, - value=value, - value_short=value_short, - ) - - if not weights: - console.print("There is no solution with this parameters") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure="MV", - risk_free_rate=0, - # alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_simb_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_ef( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure: str = "MV", - risk_free_rate: float = 0, - alpha: float = 0.05, - value: float = 1.0, - value_short: float = 0.0, - n_portfolios: int = 100, - seed: int = 123, - tangency: bool = False, - plot_tickers: bool = True, - external_axes: bool = False, -): - """ - Display efficient frontier - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str, optional - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - The default is 0.05. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - n_portfolios: int, optional - "Number of portfolios to simulate. The default value is 100. - seed: int, optional - Seed used to generate random portfolios. The default value is 123. - tangency: bool, optional - Adds the optimal line with the risk-free asset. - external_axes: bool - Optional axes to plot data on - plot_tickers: bool - Whether to plot the tickers for the assets - """ - - frontier, mu, cov, stock_returns, weights, X1, Y1, port = optimizer_model.get_ef( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - risk_measure=risk_measure, - risk_free_rate=risk_free_rate, - alpha=alpha, - value=value, - value_short=value_short, - n_portfolios=n_portfolios, - seed=seed, - ) - - try: - risk_free_rate = risk_free_rate / time_factor[freq.upper()] - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - ax = rp.plot_frontier( - w_frontier=frontier, - mu=mu, - cov=cov, - returns=stock_returns, - rm=risk_choices[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - cmap="RdYlBu", - w=weights, - label="", - marker="*", - s=16, - c="r", - t_factor=time_factor[freq.upper()], - ax=ax, - ) - - # Add risk free line - if tangency: - ret_sharpe = (mu @ weights).to_numpy().item() * time_factor[freq.upper()] - risk_sharpe = rp.Sharpe_Risk( - weights, - cov=cov, - returns=stock_returns, - rm=risk_choices[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=b_sim, - ) - if risk_choices[risk_measure.lower()] not in [ - "ADD", - "MDD", - "CDaR", - "EDaR", - "UCI", - ]: - risk_sharpe = risk_sharpe * time_factor[freq.upper()] ** 0.5 - - y = ret_sharpe * 1.5 - b = risk_free_rate * time_factor[freq.upper()] - m = (ret_sharpe - b) / risk_sharpe - x2 = (y - b) / m - x = [0, x2] - y = [b, y] - line = Line2D(x, y, label="Capital Allocation Line") - ax.set_xlim(xmin=min(X1) * 0.8) - ax.add_line(line) - - ax.plot(X1, Y1, color="b") - - plot_tickers = True - if plot_tickers: - ticker_plot = pd.DataFrame(columns=["ticker", "var"]) - for ticker in port.cov.columns: - weight_df = pd.DataFrame({"weights": 1}, index=[ticker]) - risk = rp.Sharpe_Risk( - weight_df, - cov=port.cov[ticker][ticker], - returns=stock_returns.loc[:, [ticker]], - rm=risk_choices[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - ) - - if risk_choices[risk_measure.lower()] not in [ - "MDD", - "ADD", - "CDaR", - "EDaR", - "UCI", - ]: - risk = risk * time_factor[freq.upper()] ** 0.5 - - ticker_plot = ticker_plot.append( - {"ticker": ticker, "var": risk}, ignore_index=True - ) - ticker_plot = ticker_plot.set_index("ticker") - ticker_plot = ticker_plot.merge( - port.mu.T * time_factor[freq.upper()], right_index=True, left_index=True - ) - ticker_plot = ticker_plot.rename(columns={0: "ret"}) - ax.scatter(ticker_plot["var"], ticker_plot["ret"]) - for row in ticker_plot.iterrows(): - ax.annotate(row[0], (row[1]["var"], row[1]["ret"])) - ax.set_title(f"Efficient Frontier simulating {n_portfolios} portfolios") - ax.legend(loc="best", scatterpoints=1) - theme.style_primary_axis(ax) - L, b, w, h = ax.get_position().bounds - ax.set_position([L, b, w * 0.9, h]) - ax1 = ax.get_figure().axes - ll, bb, ww, hh = ax1[-1].get_position().bounds - ax1[-1].set_position([ll * 1.02, bb, ww, hh]) - - return theme.visualize_output( - force_tight_layout=False, external_axes=external_axes - ) - except Exception as _: - console.print("[red]Error plotting efficient frontier.[/red]") - return None - - -@log_start_end(log=logger) -def display_risk_parity( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - risk_measure: str = "mv", - risk_cont: Optional[List[str]] = None, - risk_free_rate: float = 0, - alpha: float = 0.05, - target_return: float = -1, - mean: str = "hist", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - table: bool = False, -) -> Dict: - """ - Builds a risk parity portfolio using the risk budgeting approach - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str - interval to look at returns from - start_date: str - If not using interval, start date string (YYYY-MM-DD) - end_date: str - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - X (integer days) for returns calculated every X days. - - maxnan: float - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float - Value used to replace outliers that are higher to threshold. - method: str - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - risk_measure: str - The risk measure used to optimize the portfolio. - The default is 'MV'. Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - risk_cont: List[str], optional - The vector of risk contribution per asset. If empty, the default is - 1/n (number of assets). - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. Used for - 'FLPM' and 'SLPM' and Sharpe objective function. The default is 0. - alpha: float, optional - Significance level of CVaR, EVaR, CDaR and EDaR - target_return: float, optional - Constraint on minimum level of portfolio's return. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio, by default 1.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Risk parity portfolio based on risk budgeting approach\n" - s_title += "using " + risk_names[risk_measure] + " as risk measure\n" - weights, stock_returns = optimizer_model.get_risk_parity_portfolio( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - risk_measure=risk_choices[risk_measure], - risk_cont=risk_cont, - risk_free_rate=risk_free_rate, - alpha=alpha, - target_return=target_return, - mean=mean, - covariance=covariance, - d_ewma=d_ewma, - value=value, - ) - - if not weights: - console.print("There is no solution with this parameters") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_rel_risk_parity( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - version: str = "A", - risk_cont: Optional[List[str]] = None, - penal_factor: float = 1, - target_return: float = -1, - mean: str = "hist", - covariance: str = "hist", - d_ewma: float = 0.94, - value: float = 1.0, - table: bool = False, -) -> Dict: - """ - Builds a relaxed risk parity portfolio using the least squares approach - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str, optional - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - X (integer days) for returns calculated every X days. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - version : str, optional - Relaxed risk parity model version. The default is 'A'. - Possible values are: - - - 'A': without regularization and penalization constraints. - - 'B': with regularization constraint but without penalization constraint. - - 'C': with regularization and penalization constraints. - - risk_cont: List[str], optional - The vector of risk contribution per asset. If empty, the default is - 1/n (number of assets). - penal_factor: float, optional - The penalization factor of penalization constraints. Only used with - version 'C'. The default is 1. - target_return: float, optional - Constraint on minimum level of portfolio's return. - mean: str, optional - The method used to estimate the expected returns. - The default value is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio, by default 1.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - s_title = f"{p} Relaxed risk parity portfolio based on least squares approach\n" - weights, stock_returns = optimizer_model.get_rel_risk_parity_portfolio( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - version=version.upper(), - risk_cont=risk_cont, - penal_factor=penal_factor, - target_return=target_return, - mean=mean, - covariance=covariance, - d_ewma=d_ewma, - value=value, - ) - - if not weights: - console.print("There is no solution with this parameters") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices["mv"], - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_hcp( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - model: str = "HRP", - codependence: str = "pearson", - covariance: str = "hist", - objective: str = "minrisk", - risk_measure: str = "mv", - risk_free_rate: float = 0.0, - risk_aversion: float = 1.0, - alpha: float = 0.05, - a_sim: int = 100, - beta: Optional[float] = None, - b_sim: Optional[int] = None, - linkage: str = "ward", - k: Optional[int] = None, - max_k: int = 10, - bins_info: str = "KN", - alpha_tail: float = 0.05, - leaf_order: bool = True, - d_ewma: float = 0.94, - value: float = 1.0, - table: bool = False, -) -> Dict: - """ - Builds a hierarchical clustering portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - model: str, optional - The hierarchical cluster portfolio model used for optimize the - portfolio. The default is 'HRP'. Possible values are: - - - 'HRP': Hierarchical Risk Parity. - - 'HERC': Hierarchical Equal Risk Contribution. - - 'NCO': Nested Clustered Optimization. - - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})}. - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})}. - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)}. - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)}. - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})}. - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}}. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `c-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `c-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `c-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `c-MLforAM`. - - objective: str, optional - Objective function used by the NCO model. - The default is 'MinRisk'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'ERC': Equally risk contribution portfolio of the selected risk measure. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. - Used for 'FLPM' and 'SLPM'. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic. - The default is None. - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters. The default is 10. - bins_info: str, optional - Number of bins used to calculate variation of information. The default - value is 'KN'. Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index. The default is 0.05. - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal. The default is True. - d: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio, by default 1.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - - if model == "HRP": - s_title = f"{p} Hierarchical risk parity portfolio" - s_title += " using " + codependence + " codependence,\n" + linkage - elif model == "HERC": - s_title = f"{p} Hierarchical equal risk contribution portfolio" - s_title += " using " + codependence + "\ncodependence," + linkage - elif model == "NCO": - s_title = f"{p} Nested clustered optimization" - s_title += " using " + codependence + " codependence,\n" + linkage - s_title += " linkage and " + risk_names[risk_measure] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_hcp_portfolio( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - model=model, - codependence=codependence, - covariance=covariance, - objective=objectives_choices[objective], - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - linkage=linkage, - k=k, - max_k=max_k, - bins_info=bins_info, - alpha_tail=alpha_tail, - leaf_order=leaf_order, - d_ewma=d_ewma, - value=value, - ) - - if not weights: - console.print("There is no solution with this parameters") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_hrp( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - codependence: str = "pearson", - covariance: str = "hist", - risk_measure: str = "mv", - risk_free_rate: float = 0.0, - risk_aversion: float = 1.0, - alpha: float = 0.05, - a_sim: int = 100, - beta: Optional[float] = None, - b_sim: Optional[int] = None, - linkage: str = "single", - k: int = 0, - max_k: int = 10, - bins_info: str = "KN", - alpha_tail: float = 0.05, - leaf_order: bool = True, - d_ewma: float = 0.94, - value: float = 1.0, - table: bool = False, -) -> Dict: - """ - Builds a hierarchical risk parity portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})}. - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})}. - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)}. - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)}. - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})}. - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}}. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `c-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `c-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `c-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `c-MLforAM`. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. - Used for 'FLPM' and 'SLPM'. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic. - The default is None. - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters. The default is 10. - bins_info: str, optional - Number of bins used to calculate variation of information. The default - value is 'KN'. Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index. The default is 0.05. - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal. The default is True. - d: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - - s_title = f"{p} Hierarchical risk parity portfolio" - s_title += " using " + codependence + " codependence,\n" + linkage - s_title += " linkage and " + risk_names[risk_measure] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_hrp( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - codependence=codependence, - covariance=covariance, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - linkage=linkage, - k=k, - max_k=max_k, - bins_info=bins_info, - alpha_tail=alpha_tail, - leaf_order=leaf_order, - d_ewma=d_ewma, - value=value, - ) - - if not weights: - console.print("There is no solution with this parameters") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_herc( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - codependence: str = "pearson", - covariance: str = "hist", - risk_measure: str = "mv", - risk_free_rate: float = 0.0, - risk_aversion: float = 1.0, - alpha: float = 0.05, - a_sim: int = 100, - beta: Optional[float] = None, - b_sim: Optional[int] = None, - linkage: str = "ward", - k: int = 0, - max_k: int = 10, - bins_info: str = "KN", - alpha_tail: float = 0.05, - leaf_order: bool = True, - d_ewma: float = 0.94, - value: float = 1.0, - table: bool = False, -) -> Dict: - """ - Builds a hierarchical equal risk contribution portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - model: str, optional - The hierarchical cluster portfolio model used for optimize the - portfolio. The default is 'HRP'. Possible values are: - - - 'HRP': Hierarchical Risk Parity. - - 'HERC': Hierarchical Equal Risk Contribution. - - 'NCO': Nested Clustered Optimization. - - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})}. - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})}. - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)}. - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)}. - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})}. - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}}. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `c-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `c-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `c-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `c-MLforAM`. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. - Used for 'FLPM' and 'SLPM'. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic. - The default is None. - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters. The default is 10. - bins_info: str, optional - Number of bins used to calculate variation of information. The default - value is 'KN'. Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index. The default is 0.05. - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal. The default is True. - d: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - - s_title = f"{p} Hierarchical equal risk contribution portfolio" - s_title += " using " + codependence + "\ncodependence," + linkage - s_title += " linkage and " + risk_names[risk_measure] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_herc( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - codependence=codependence, - covariance=covariance, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - linkage=linkage, - k=k, - max_k=max_k, - bins_info=bins_info, - alpha_tail=alpha_tail, - leaf_order=leaf_order, - d_ewma=d_ewma, - value=value, - ) - - if not weights: - console.print("There is no solution with this parameters") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def display_nco( - symbols: List[str], - interval: str = "3y", - start_date: str = "", - end_date: str = "", - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", - codependence: str = "pearson", - covariance: str = "hist", - objective: str = "MinRisk", - risk_measure: str = "mv", - risk_free_rate: float = 0.0, - risk_aversion: float = 1.0, - alpha: float = 0.05, - a_sim: int = 100, - beta: Optional[float] = None, - b_sim: Optional[int] = None, - linkage: str = "ward", - k: Optional[int] = None, - max_k: int = 10, - bins_info: str = "KN", - alpha_tail: float = 0.05, - leaf_order: bool = True, - d_ewma: float = 0.94, - value: float = 1.0, - table: bool = False, -) -> Dict: - """ - Builds a hierarchical equal risk contribution portfolio - - Parameters - ---------- - symbols : List[str] - List of portfolio tickers - interval : str - interval to look at returns from - start_date: str, optional - If not using interval, start date string (YYYY-MM-DD) - end_date: str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - log_returns: bool, optional - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: float, optional - Max percentage of nan values accepted per asset to be included in - returns. - threshold: float, optional - Value used to replace outliers that are higher to threshold. - method: str, optional - Method used to fill nan values. Default value is 'time'. For more information see `interpolate `__. - model: str, optional - The hierarchical cluster portfolio model used for optimize the - portfolio. The default is 'HRP'. Possible values are: - - - 'HRP': Hierarchical Risk Parity. - - 'HERC': Hierarchical Equal Risk Contribution. - - 'NCO': Nested Clustered Optimization. - - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})}. - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})}. - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)}. - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)}. - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})}. - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}}. - - covariance: str, optional - The method used to estimate the covariance matrix: - The default is 'hist'. Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `c-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `c-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `c-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `c-MLforAM`. - - objective: str, optional - Objective function used by the NCO model. - The default is 'MinRisk'. Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'ERC': Equally risk contribution portfolio of the selected risk measure. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. - Used for 'FLPM' and 'SLPM'. The default is 0. - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function. - The default is 1. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic. - The default is None. - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters. The default is 10. - bins_info: str, optional - Number of bins used to calculate variation of information. The default - value is 'KN'. Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index. The default is 0.05. - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal. The default is True. - d: float, optional - The smoothing factor of ewma methods. - The default is 0.94. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - table: bool, optional - True if plot table weights, by default False - """ - p = d_period(interval, start_date, end_date) - - s_title = f"{p} Nested clustered optimization" - s_title += " using " + codependence + " codependence,\n" + linkage - s_title += " linkage and " + risk_names[risk_measure] + " as risk measure\n" - - weights, stock_returns = optimizer_model.get_nco( - symbols=symbols, - interval=interval, - start_date=start_date, - end_date=end_date, - log_returns=log_returns, - freq=freq, - maxnan=maxnan, - threshold=threshold, - method=method, - codependence=codependence, - covariance=covariance, - objective=objectives_choices[objective.lower()], - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - risk_aversion=risk_aversion, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - linkage=linkage, - k=k, - max_k=max_k, - bins_info=bins_info, - alpha_tail=alpha_tail, - leaf_order=leaf_order, - d_ewma=d_ewma, - value=value, - ) - - if not weights: - console.print("There is no solution with this parameters") - return {} - - if table: - console.print(s_title) - display_weights(weights) - portfolio_performance( - weights=weights, - data=stock_returns, - risk_measure=risk_choices[risk_measure], - risk_free_rate=risk_free_rate, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - freq=freq, - ) - - return weights - - -@log_start_end(log=logger) -def my_autopct(x): - """Function for autopct of plt.pie. This results in values not being printed in the pie if they are 'too small'""" - if x > 4: - return f"{x:.2f} %" - - return "" - - -@log_start_end(log=logger) -def pie_chart_weights( - weights: dict, - title_opt: str, - external_axes: bool = False, -): - """Show a pie chart of holdings - - Parameters - ---------- - weights: dict - Weights to display, where keys are tickers, and values are either weights or values if -v specified - title_opt: str - Title to be used on the plot title - external_axes: bool - Optional external axes to plot data on - """ - if not weights: - return None - - init_stocks = list(weights.keys()) - init_sizes = list(weights.values()) - symbols = [] - sizes = [] - for stock, size in zip(init_stocks, init_sizes): - if size > 0: - symbols.append(stock) - sizes.append(size) - - total_size = np.sum(sizes) - colors = theme.get_colors() - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - if math.isclose(sum(sizes), 1, rel_tol=0.1): - _, _, autotexts = ax.pie( - sizes, - labels=symbols, - autopct=my_autopct, - colors=colors, - textprops=dict(color="white"), - wedgeprops={"linewidth": 0.5, "edgecolor": "white"}, - labeldistance=1.05, - startangle=45, - normalize=True, - ) - plt.setp(autotexts, color="white", fontweight="bold") - else: - _, _, autotexts = ax.pie( - sizes, - labels=symbols, - autopct="", - colors=colors, - textprops=dict(color="white"), - wedgeprops={"linewidth": 0.5, "edgecolor": "white"}, - labeldistance=1.05, - startangle=45, - normalize=True, - ) - plt.setp(autotexts, color="white", fontweight="bold") - for i, a in enumerate(autotexts): - if sizes[i] / total_size > 0.05: - a.set_text(f"{sizes[i]:.2f}") - else: - a.set_text("") - - ax.axis("equal") - - # leg1 = ax.legend( - # wedges, - # [str(s) for s in stocks], - # title=" Ticker", - # loc="upper left", - # bbox_to_anchor=(0.80, 0, 0.5, 1), - # frameon=False, - # ) - # leg2 = ax.legend( - # wedges, - # [ - # f"{' ' if ((100*s/total_size) < 10) else ''}{100*s/total_size:.2f}%" - # for s in sizes - # ], - # title=" ", - # loc="upper left", - # handlelength=0, - # bbox_to_anchor=(0.91, 0, 0.5, 1), - # frameon=False, - # ) - # ax.add_artist(leg1) - # ax.add_artist(leg2) - - plt.setp(autotexts, size=8, weight="bold") - - title = "Portfolio - " + title_opt + "\n" - title += "Portfolio Composition" - ax.set_title(title) - - return theme.visualize_output(force_tight_layout=True, external_axes=external_axes) - - -@log_start_end(log=logger) -def additional_plots( - weights: Dict, - data: pd.DataFrame, - category_dict: Optional[Dict] = None, - category: str = "", - portfolio_name: str = "", - freq: str = "D", - risk_measure: str = "MV", - risk_free_rate: float = 0, - alpha: float = 0.05, - a_sim: float = 100, - beta: Optional[float] = None, - b_sim: Optional[float] = None, - pie: bool = False, - hist: bool = False, - dd: bool = False, - rc_chart: bool = False, - heat: bool = False, - external_axes: bool = False, -): - """ - Plot additional charts - - Parameters - ---------- - weights: Dict - Dict of portfolio weights - data: pd.DataFrame - DataFrame of stock returns - category_dict: Dict - Dict of categories - category: str - Category to plot - portfolio_name: str - Portfolio name - freq: str, optional - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - risk_measure: str, optional - The risk measure used to optimize the portfolio. If model is 'NCO', - the risk measures available depends on the objective function. - The default is 'MV'. Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, must be in the same interval of assets returns. - Used for 'FLPM' and 'SLPM'. The default is 0. - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses. - The default is 0.05. - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses. The default is 100. - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value. - The default is None. - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value. - The default is None. - pie : bool, optional - Display a pie chart of values, by default False - hist : bool, optional - Display a histogram with risk measures, by default False - dd : bool, optional - Display a drawdown chart with risk measures, by default False - rc-chart : float, optional - Display a risk contribution chart for assets, by default False - heat : float, optional - Display a heatmap of correlation matrix with dendrogram, by default False - external_axes: bool - Optional axes to plot data on - """ - - title_opt = category if not portfolio_name else category + " - " + portfolio_name - - if category_dict is not None: - weights_df = pd.DataFrame.from_dict( - data=weights, orient="index", columns=["value"], dtype=float - ) - category_df = pd.DataFrame.from_dict( - data=category_dict, orient="index", columns=["category"] - ) - weights_df = weights_df.join(category_df, how="inner") - weights_df.sort_index(inplace=True) - - # Calculating classes returns - classes = list(set(weights_df["category"])) - weights_classes = weights_df.groupby(["category"]).sum() - matrix_classes = np.zeros((len(weights_df), len(classes))) - labels = weights_df["category"].tolist() - - j_value = 0 - for i in classes: - matrix_classes[:, j_value] = np.array( - [1 if x == i else 0 for x in labels], dtype=float - ) - matrix_classes[:, j_value] = ( - matrix_classes[:, j_value] - * weights_df["value"] - / weights_classes.loc[i, "value"] - ) - j_value += 1 - - matrix_classes = pd.DataFrame( - matrix_classes, columns=classes, index=weights_df.index - ) - data = data @ matrix_classes - weights_df = weights_classes["value"].copy() - weights_df.replace(0, np.nan, inplace=True) - weights_df.dropna(inplace=True) - weights_df.sort_values(ascending=True, inplace=True) - data = data[weights_df.index.tolist()] - data.columns = [i.title() for i in data.columns] - weights_df.index = [i.title() for i in weights_df.index] - weights = weights_df.to_dict() - - colors = theme.get_colors() - if pie: - pie_chart_weights(weights, title_opt, external_axes) - - if hist: - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - ax = rp.plot_hist(data, w=pd.Series(weights).to_frame(), alpha=alpha, ax=ax) - ax.legend(fontsize="x-small", loc="best") - - # Changing colors - for i in ax.get_children()[:-1]: - if isinstance(i, matplotlib.patches.Rectangle): - i.set_color(colors[0]) - i.set_alpha(0.7) - - k = 1 - for i, j in zip(ax.get_legend().get_lines()[::-1], ax.get_lines()[::-1]): - i.set_color(colors[k]) - j.set_color(colors[k]) - k += 1 - - title = "Portfolio - " + title_opt + "\n" - title += ax.get_title(loc="left") - ax.set_title(title) - - return theme.visualize_output( - force_tight_layout=False, external_axes=external_axes - ) - - if dd: - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - nav = data.cumsum() - ax = rp.plot_drawdown( - nav=nav, w=pd.Series(weights).to_frame(), alpha=alpha, ax=ax - ) - - ax[0].remove() - ax = ax[1] - fig = ax.get_figure() - gs = GridSpec(1, 1, figure=fig) - ax.set_position(gs[0].get_position(fig)) - ax.set_subplotspec(gs[0]) - - # Changing colors - ax.get_lines()[0].set_color(colors[0]) - k = 1 - for i, j in zip(ax.get_legend().get_lines()[::-1], ax.get_lines()[1:][::-1]): - i.set_color(colors[k]) - j.set_color(colors[k]) - k += 1 - - ax.get_children()[1].set_facecolor(colors[0]) - ax.get_children()[1].set_alpha(0.7) - - title = "Portfolio - " + title_opt + "\n" - title += ax.get_title(loc="left") - ax.set_title(title) - - return theme.visualize_output( - force_tight_layout=False, external_axes=external_axes - ) - - if rc_chart: - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - ax = rp.plot_risk_con( - w=pd.Series(weights).to_frame(), - cov=data.cov(), - returns=data, - rm=risk_choices[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - color=colors[1], - t_factor=time_factor[freq.upper()], - ax=ax, - ) - - # Changing colors - for i in ax.get_children()[:-1]: - if isinstance(i, matplotlib.patches.Rectangle): - i.set_width(i.get_width()) - i.set_color(colors[0]) - - title = "Portfolio - " + title_opt + "\n" - title += ax.get_title(loc="left") - ax.set_title(title) - - return theme.visualize_output( - force_tight_layout=False, external_axes=external_axes - ) - - if heat: - if len(weights) == 1: - single_key = list(weights.keys())[0].upper() - console.print( - f"[yellow]Heatmap needs at least two values for '{category}', only found '{single_key}'.[/yellow]" - ) - return None - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - number_of_clusters = len(weights) if len(weights) <= 3 else None - - ax = rp.plot_clusters( - returns=data, - codependence="pearson", - linkage="ward", - k=number_of_clusters, - max_k=10, - leaf_order=True, - dendrogram=True, - cmap="RdYlBu", - # linecolor='tab:purple', - ax=ax, - ) - - ax = ax.get_figure().axes - ax[0].grid(False) - ax[0].axis("off") - - if category_dict is None: - # Vertical dendrogram - L, b, w, h = ax[4].get_position().bounds - l1 = L * 0.5 - w1 = w * 0.2 - b1 = h * 0.05 - ax[4].set_position([L - l1, b + b1, w * 0.8, h * 0.95]) - # Heatmap - L, b, w, h = ax[1].get_position().bounds - ax[1].set_position([L - l1 - w1, b + b1, w * 0.8, h * 0.95]) - w2 = w * 0.2 - # colorbar - L, b, w, h = ax[2].get_position().bounds - ax[2].set_position([L - l1 - w1 - w2, b, w, h]) - # Horizontal dendrogram - L, b, w, h = ax[3].get_position().bounds - ax[3].set_position([L - l1 - w1, b, w * 0.8, h]) - else: - # Vertical dendrogram - L, b, w, h = ax[4].get_position().bounds - l1 = L * 0.5 - w1 = w * 0.4 - b1 = h * 0.2 - ax[4].set_position([L - l1, b + b1, w * 0.6, h * 0.8]) - # Heatmap - L, b, w, h = ax[1].get_position().bounds - ax[1].set_position([L - l1 - w1, b + b1, w * 0.6, h * 0.8]) - w2 = w * 0.05 - # colorbar - L, b, w, h = ax[2].get_position().bounds - ax[2].set_position([L - l1 - w1 - w2, b, w, h]) - # Horizontal dendrogram - L, b, w, h = ax[3].get_position().bounds - ax[3].set_position([L - l1 - w1, b, w * 0.6, h]) - - title = "Portfolio - " + title_opt + "\n" - title += ax[3].get_title(loc="left") - ax[3].set_title(title) - - return theme.visualize_output( - force_tight_layout=False, external_axes=external_axes - ) - - return None - - -def display_show(weights: Dict, tables: List[str], categories_dict: Dict[Any, Any]): - """Display the results of the optimization. - - Parameters - ---------- - weights : Dict - Dictionary of weights. - tables : List[str] - List of tables to display. - categories_dict : Dict[Any, Any] - Dictionary of categories. - """ - - display_weights(weights) - - for t in tables: - console.print("") - display_categories( - weights=weights, - categories=categories_dict, - column=t, - title="Category - " + t.title(), - ) diff --git a/openbb_terminal/portfolio/portfolio_optimization/parameters/Parameter.py b/openbb_terminal/portfolio/portfolio_optimization/parameters/Parameter.py deleted file mode 100644 index 4cdc065e48a..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/parameters/Parameter.py +++ /dev/null @@ -1,86 +0,0 @@ -from dataclasses import dataclass -from typing import Any, List, Optional - -# pylint: disable=unidiomatic-typecheck - - -@dataclass(frozen=True) -class Parameter: - """Class for storing parameters. This dataclass is frozen so no attributes can be changed after initialization. - - Parameters - ---------- - name : str - Name of the parameter. - type_ : type - Type of the parameter. - default : Any - Default value of the parameter. - choices : Optional[List[Any]] - List of possible values for the parameter. - """ - - name: str - type_: Any - default: Any - choices: Optional[List[Any]] = None - - def __post_init__(self): - """Post initialization.""" - self.validate() - - def validate(self): - """Validate the value of the parameter. - - Parameters - ---------- - value : Any - Value of the parameter. - - Raises - ------ - TypeError - If the value is not of the correct type. - ValueError - If the value is not in the list of possible values. - """ - if not self.validate_type(self.default): - raise TypeError( - f"Default for '{self.name}' must be of type '{self.type_.__name__}'." - ) - if self.choices is not None and not all( - self.validate_type(choice) for choice in self.choices - ): - raise TypeError( - f"Choices must be of the same type as the parameter.\n" - f"'{self.name}': '{self.choices}' -> '{self.type_.__name__}'" - ) - if self.choices is not None and self.default not in self.choices: - raise ValueError( - f"Parameter '{self.name}' must be one of type '{self.choices}'." - ) - - def validate_type(self, value: Any) -> bool: - """Check if the value is of the correct type. - - Parameters - ---------- - value : Any - Value of the parameter. - - Returns - ------- - bool - True if the value is of the correct type, False otherwise. - """ - # Temporary workaround until we drop Python 3.9 for: - # Error: Subscripted generics cannot be used with class and instance checks - # - # isinstance for subscripted generics (e.g. List[str]) is only available - # in Python 3.10, so this method does not check for the type of subscripted generics. - # TODO: Though we should implement it. - # See https://stackoverflow.com/questions/70825351/python-3-typeerror-subscripted-generics-cannot-be-used-with-class-and-instance # noqa: E501 - try: - return isinstance(value, self.type_) - except Exception as _: # noqa: F841 - return True diff --git a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_controller.py b/openbb_terminal/portfolio/portfolio_optimization/parameters/params_controller.py deleted file mode 100644 index 0a5a4acd406..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_controller.py +++ /dev/null @@ -1,355 +0,0 @@ -# type: ignore -""" Parameters Controller Module """ -__docformat__ = "numpy" -# pylint: disable=C0302, no-else-return - -import argparse -import contextlib -import logging -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.portfolio.portfolio_optimization.parameters import ( - params_helpers, - params_view, -) -from openbb_terminal.portfolio.portfolio_optimization.parameters.params_statics import ( - AVAILABLE_OPTIONS, - DEFAULT_BOOL, - DEFAULT_PARAMETERS, - MODEL_PARAMS, -) -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class ParametersController(BaseController): - """Portfolio Optimization Parameters Controller class""" - - CHOICES_COMMANDS = [ - "set", - "load", - "save", - "new", - "clear", - "arg", - ] - CHOICES_PARAMS = ["freq", "threshold", "method", "alpha", "maxnan"] - CHOICES_COMMANDS += CHOICES_PARAMS - - PATH = "/portfolio/po/params/" - - models = [ - "maxsharpe", - "minrisk", - "maxutil", - "maxret", - "maxdiv", - "maxdecorr", - "ef", - "riskparity", - "relriskparity", - "hrp", - "herc", - "nco", - ] - - current_model = "" - current_file = "" - - def __init__( - self, - file: str, - queue: Optional[List[str]] = None, - params: Optional[dict] = None, - current_model=None, - ): - """Constructor""" - super().__init__(queue) - - self.params: dict = params if params else {} - self.current_file = file - self.current_model = current_model - self.description: Optional[str] = None - self.DATA_FILES = params_helpers.load_data_files() - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = {c: {} for c in self.controller_choices} - choices["set"] = {c: None for c in self.models} - choices["set"]["--model"] = {c: None for c in self.models} - choices["set"]["-m"] = "--model" - choices["arg"] = {c: None for c in AVAILABLE_OPTIONS} - choices["load"] = {c: {} for c in self.DATA_FILES} - choices["load"]["--file"] = {c: {} for c in self.DATA_FILES} - choices["load"]["-f"] = "--file" - choices["save"]["--file"] = None - choices["save"]["-f"] = "--file" - choices["arg"] = { - "--argument": None, - "-a": "--argument", - "--show_arguments": {}, - "-s": "--show_arguments", - } - self.choices = choices - self.completer = NestedCompleter.from_nested_dict(choices) - - def update_runtime_choices(self): - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - self.DATA_FILES = params_helpers.load_data_files() - self.choices["load"]["--file"] = {c: {} for c in self.DATA_FILES} - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def print_help(self): - """Print help""" - mt = MenuText("portfolio/po/params/") - mt.add_param("_loaded", self.current_file) - mt.add_raw("\n") - mt.add_cmd("load") - mt.add_cmd("save") - mt.add_raw("\n") - mt.add_param("_model", self.current_model or "") - mt.add_raw("\n") - mt.add_cmd("clear", self.current_file) - mt.add_cmd("set", self.current_file) - mt.add_cmd("arg", self.current_file) - if self.current_file: - mt.add_raw("\n") - mt.add_info("_parameters_") - if self.current_model: - max_len = max(len(k) for k in self.params) - for k, v in self.params.items(): - clean_v = params_helpers.booltostr(v) - all_params = DEFAULT_PARAMETERS + MODEL_PARAMS[self.current_model] - if k in all_params: - mt.add_raw( - f" [param]{k}{' ' * (max_len - len(k))} :[/param] {clean_v}\n" - ) - else: - max_len = max(len(k) for k in self.params) - for k, v in self.params.items(): - clean_v = params_helpers.booltostr(v) - mt.add_raw( - f" [param]{k}{' ' * (max_len - len(k))} :[/param] {clean_v}\n" - ) - console.print( - text=mt.menu_text, menu="Portfolio - Portfolio Optimization - Parameters" - ) - - def custom_reset(self): - """Class specific component of reset command""" - if self.current_file: - if self.current_model: - return [ - "portfolio", - "po", - f"file {self.current_file}", - "params", - f"set {self.current_model}", - ] - else: - return ["portfolio", "po", f"file {self.current_file}", "params"] - return [] - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - """Process load command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="file", - description="Select parameter file to use (ini or xlsx). The OpenBB Parameters Template can be " - "found inside the Portfolio Optimization documentation. Please type `about` to access the documentation.", - ) - - parser.add_argument( - "-f", - "--file", - nargs="+", - dest="file", - help="Parameter file to be used", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-f") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if not ns_parser.file: - console.print( - "[green]The OpenBB Parameters Template can be found inside " - "the Portfolio Optimization documentation. Please type `about` " - "to access the documentation. [green]\n" - ) - else: - self.current_file = " ".join(ns_parser.file) - - if self.current_file in self.DATA_FILES: - file_location = str(self.DATA_FILES[self.current_file]) - else: - file_location = str(self.current_file) - - self.params, self.current_model = params_view.load_file(file_location) - - @log_start_end(log=logger) - def call_save(self, other_args: List[str]): - """Process save command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="save", - description="Save portfolio risk parameters (ini or xlsx)", - ) - parser.add_argument( - "-f", - "--file", - required=True, - type=params_helpers.check_save_file, - dest="file", - help="Filename to be saved", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-f") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - self.current_file = str(params_view.save_file(ns_parser.file, self.params)) - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_clear(self, other_args: List[str]): - """Process set command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="clear", - description="Clear selected portfolio optimization models", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - self.current_model = "" - - @log_start_end(log=logger) - def call_set(self, other_args: List[str]): - """Process set command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="set", - description="Select one of the portfolio optimization models", - ) - parser.add_argument( - "-m", - "--model", - required=True, - dest="model", - help="Frequency used to calculate returns", - choices=self.models, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-m") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if not self.current_file: - console.print( - "[red]Load portfolio risk parameters first using `file`.\n[/red]" - ) - return - self.current_model = ns_parser.model - - @log_start_end(log=logger) - def call_arg(self, other_args: List[str]): - """Process arg command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="arg", - description="Set a different value for one of the available arguments.", - ) - parser.add_argument( - "-a", - "--argument", - nargs=2, - dest="argument", - help="Set a value for an argument", - ) - parser.add_argument( - "-s", - "--show_arguments", - dest="show", - action="store_true", - default=False, - help="Show the available arguments, the options and a description.", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-a") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if not self.current_file: - console.print( - "[red]Load portfolio risk parameters first using `file`.\n[/red]" - ) - return - if self.current_file.endswith(".ini"): - console.print( - f"Please adjust the parameters directly in the {self.current_file} file." - ) - elif ns_parser.show: - params_view.show_arguments(AVAILABLE_OPTIONS, self.description) - - elif ns_parser.argument: - argument = ns_parser.argument[0] - value = ns_parser.argument[1] - - if self.current_model and argument not in self.params: - console.print( - "[red]The parameter you are trying to access is unused in this model.[/red]\n" - ) - - with contextlib.suppress(ValueError): - value = float(value) - - if argument == "historic_period": - for option in AVAILABLE_OPTIONS[argument]: - if option in str(value): - self.params[argument] = value - break - if self.params[argument] != value: - console.print( - f"[red]The value {value} is not an option for {argument}.\n" - f"Please select enter a " - f"number before the option d, w, mo and y or select ytd or max. For example: 252d, " - f"12w, 10y or max[/red]" - ) - elif ( - value in AVAILABLE_OPTIONS[argument] - or "Any" in AVAILABLE_OPTIONS[argument] - ): - if AVAILABLE_OPTIONS[argument] == DEFAULT_BOOL: - value = value == "True" - self.params[argument] = value - else: - options = ", ".join(AVAILABLE_OPTIONS[argument]) - if len(AVAILABLE_OPTIONS[argument]) > 15: - minimum = min(AVAILABLE_OPTIONS[argument]) - maximum = max(AVAILABLE_OPTIONS[argument]) - options = ( - f"between {minimum} and {maximum} in steps of " - f"{maximum / sum(x > 0 for x in AVAILABLE_OPTIONS[argument])}" - ) - - console.print( - f"[red]The value {value} is not an option for {argument}.\n" - f"The value needs to be {options}[/red]" - ) - - else: - self.params[argument] = str(AVAILABLE_OPTIONS[argument]) # type: ignore - - console.print() diff --git a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_helpers.py b/openbb_terminal/portfolio/portfolio_optimization/parameters/params_helpers.py deleted file mode 100644 index 7fce5d259fe..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_helpers.py +++ /dev/null @@ -1,197 +0,0 @@ -import argparse -from datetime import date, datetime -from pathlib import Path -from typing import Any, Dict, List - -from openbb_terminal.core.config import paths -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.helper_funcs import log_and_raise -from openbb_terminal.portfolio.portfolio_optimization.statics import ( - OPTIMIZATION_PARAMETERS, - TERMINAL_TEMPLATE_MAP, -) -from openbb_terminal.rich_config import console - - -def check_save_file(file: str) -> str: - """Argparse type to check parameter file to be saved""" - if file == "defaults.ini": - log_and_raise( - argparse.ArgumentTypeError( - "Cannot overwrite defaults.ini file, please save with a different name" - ) - ) - elif not file.endswith(".ini"): - log_and_raise(argparse.ArgumentTypeError("File to be saved needs to be .ini")) - - return file - - -def load_data_files() -> Dict[str, Path]: - """Loads files from the misc directory and from the user's custom exports - - Returns - ------- - Dict[str, Path] - The dictionary of filenames and their paths - """ - default_path = paths.MISCELLANEOUS_DIRECTORY / "portfolio" - custom_exports = ( - get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY / "optimization" - ) - data_files = {} - for directory in [default_path, custom_exports]: - for file_type in ["xlsx", "ini"]: - for filepath in Path(directory).rglob(f"*.{file_type}"): - if filepath.is_file() and "example" not in filepath.name: - data_files[filepath.name] = filepath - - return data_files - - -def check_convert_parameters(received_parameters: dict) -> dict: - """Check if the argument is in the parameters list. - If it is, try to cast it to the correct type, else use default value. - - Parameters - ---------- - received_parameters: dict - The parameters to be checked - - Returns - ------- - dict - The parameters with the correct types - """ - - converted_parameters = check_convert_dates( - received_parameters, ["start_period", "start_date", "end_period", "end_date"] - ) - - for received_name, received_value in received_parameters.items(): - # TODO: Remove this line when mapping between template and terminal is not needed - template_name = TERMINAL_TEMPLATE_MAP.get(received_name, received_name) - - if template_name in OPTIMIZATION_PARAMETERS: - PARAMETER = OPTIMIZATION_PARAMETERS[template_name] - if not PARAMETER.validate_type(received_value): - converted_parameters[received_name] = check_convert_parameter( - name=received_name, value=received_value, parameter=PARAMETER - ) - - return converted_parameters - - -def check_convert_parameter(name, value, parameter): - """Converts a parameter to the correct type - - Parameters - ---------- - name: str - The name of the received parameter - value: str - The value of the received parameter - parameter: Parameter - The parameter object - - Returns - ------- - The converted parameter - """ - - try: - # Try to cast the value to the correct type if int or float - if parameter.type_ is int: - new_value = parameter.type_(float(value)) - elif parameter.type_ is float: - new_value = parameter.type_(value) - elif parameter.type_ is bool: - new_value = strtobool(value) - else: - new_value = value - except ValueError: - new_value = parameter.default - console.print( - f"[red]'{name}' format should be '{parameter.type_.__name__}' type[/red]", - f"[red]and could not be converted. Setting default '{new_value}'.\n[/red]", - ) - - return new_value - - -def check_convert_dates(params: dict, param_name_list: List[str]) -> dict: - """Check if the argument is in the list and convert it to a str object - Parameters - ---------- - params : dict - The parameters to be converted - param_name_list : List[str] - The list of arguments to be converted - - Returns - ------- - dict - The converted parameters - """ - - for param_name in param_name_list: - if param_name in params: - param_value = params[param_name] - if isinstance(param_value, date): - params[param_name] = param_value.strftime("%Y-%m-%d") - elif isinstance(param_value, str): - try: - param_value = datetime.strptime(param_value, "%Y-%m-%d") - except ValueError: - console.print( - f"[red]'{param_name}' format is not a valid date, must be YYYY-MM-DD.\n[/red]" - ) - params.pop(param_name) - else: - console.print( - f"[red]'{param_name}' format is not a valid date, must be YYYY-MM-DD.\n[/red]" - ) - params.pop(param_name) - - return params - - -def booltostr(value: bool) -> Any: - """Converts a bool to a string or returns the value itself if not bool - - Parameters - ---------- - value: bool - The bool to be converted or the value itself if not bool - - Returns - ------- - Any - The converted value - """ - - if isinstance(value, bool): - return "True" if value else "False" - return value - - -def strtobool(value: str) -> Any: - """Converts a string to a bool or returns the value itself if not string - - Parameters - ---------- - value: str - The string to be converted or the value itself if not string - - Returns - ------- - Any - The converted value - """ - - if isinstance(value, str): - if value.lower() == "true": - return True - elif value.lower() == "false": - return False - return value diff --git a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_statics.py b/openbb_terminal/portfolio/portfolio_optimization/parameters/params_statics.py deleted file mode 100644 index 34d479c0efe..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_statics.py +++ /dev/null @@ -1,221 +0,0 @@ -from typing import Dict - -DEFAULT_RANGE = [value / 1000 for value in range(0, 1001)] -DEFAULT_BOOL = ["True", "False"] - -AVAILABLE_OPTIONS: Dict = { - "historic_period": ["d", "w", "mo", "y", "ytd", "max"], - "start_period": ["Any"], - "end_period": ["Any"], - "log_returns": DEFAULT_BOOL, - "return_frequency": ["d", "w", "m"], - "max_nan": DEFAULT_RANGE, - "threshold_value": DEFAULT_RANGE, - "nan_fill_method": [ - "linear", - "time", - "nearest", - "zero", - "slinear", - "quadratic", - "cubic", - ], - "risk_free": DEFAULT_RANGE, - "significance_level": DEFAULT_RANGE, - "risk_measure": [ - "MV", - "MAD", - "MSV", - "FLPM", - "SLPM", - "CVaR", - "EVaR", - "WR", - "ADD", - "UCI", - "CDaR", - "EDaR", - "MDD", - ], - "target_return": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE], - "target_risk": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE], - "expected_return": ["hist", "ewma1", "ewma2"], - "covariance": [ - "hist", - "ewma1", - "ewma2", - "ledoit", - "oas", - "shrunk", - "gl", - "jlogo", - "fixed", - "spectral", - "shrink", - ], - "smoothing_factor_ewma": DEFAULT_RANGE, - "long_allocation": DEFAULT_RANGE, - "short_allocation": DEFAULT_RANGE, - "risk_aversion": [value / 100 for value in range(-500, 501)], - "amount_portfolios": range(1, 10001), - "random_seed": range(1, 10001), - "tangency": DEFAULT_BOOL, - "risk_parity_model": ["A", "B", "C"], - "penal_factor": DEFAULT_RANGE + [-x for x in DEFAULT_RANGE], - "co_dependence": [ - "pearson", - "spearman", - "abs_pearson", - "abs_spearman", - "distance", - "mutual_info", - "tail", - ], - "cvar_simulations": range(1, 10001), - "cvar_significance": DEFAULT_RANGE, - "linkage": [ - "single", - "complete", - "average", - "weighted", - "centroid", - "ward", - "dbht", - ], - "max_clusters": range(1, 101), - "amount_bins": ["KN", "FD", "SC", "HGR", "Integer"], - "alpha_tail": DEFAULT_RANGE, - "leaf_order": DEFAULT_BOOL, - "objective": ["MinRisk", "Utility", "Sharpe", "MaxRet"], -} - -DEFAULT_PARAMETERS = [ - "historic_period", - "start_period", - "end_period", - "log_returns", - "return_frequency", - "max_nan", - "threshold_value", - "nan_fill_method", - "risk_free", - "significance_level", -] -MODEL_PARAMS = { - "maxsharpe": [ - "risk_measure", - "target_return", - "target_risk", - "expected_return", - "covariance", - "smoothing_factor_ewma", - "long_allocation", - "short_allocation", - ], - "minrisk": [ - "risk_measure", - "target_return", - "target_risk", - "expected_return", - "covariance", - "smoothing_factor_ewma", - "long_allocation", - "short_allocation", - ], - "maxutil": [ - "risk_measure", - "target_return", - "target_risk", - "expected_return", - "covariance", - "smoothing_factor_ewma", - "long_allocation", - "short_allocation", - "risk_aversion", - ], - "maxret": [ - "risk_measure", - "target_return", - "target_risk", - "expected_return", - "covariance", - "smoothing_factor_ewma", - "long_allocation", - ], - "maxdiv": ["covariance", "long_allocation"], - "maxdecorr": ["covariance", "long_allocation"], - "ef": [ - "risk_measure", - "long_allocation", - "short_allocation", - "amount_portfolios", - "random_seed", - "tangency", - ], - "equal": ["risk_measure", "long_allocation"], - "mktcap": ["risk_measure", "long_allocation"], - "dividend": ["risk_measure", "long_allocation"], - "riskparity": [ - "risk_measure", - "target_return", - "long_allocation", - "risk_contribution", - ], - "relriskparity": [ - "risk_measure", - "covariance", - "smoothing_factor_ewma", - "long_allocation", - "risk_contribution", - "risk_parity_model", - "penal_factor", - ], - "hrp": [ - "risk_measure", - "covariance", - "smoothing_factor_ewma", - "long_allocation", - "co_dependence", - "cvar_simulations", - "cvar_significance", - "linkage", - "amount_clusters", - "max_clusters", - "amount_bins", - "alpha_tail", - "leaf_order", - "objective", - ], - "herc": [ - "risk_measure", - "covariance", - "smoothing_factor_ewma", - "long_allocation", - "co_dependence", - "cvar_simulations", - "cvar_significance", - "linkage", - "amount_clusters", - "max_clusters", - "amount_bins", - "alpha_tail", - "leaf_order", - "objective", - ], - "nco": [ - "risk_measure", - "covariance", - "smoothing_factor_ewma", - "long_allocation", - "co_dependence", - "cvar_simulations", - "cvar_significance", - "linkage", - "amount_clusters", - "max_clusters", - "amount_bins", - "alpha_tail", - "leaf_order", - "objective", - ], -} diff --git a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_view.py b/openbb_terminal/portfolio/portfolio_optimization/parameters/params_view.py deleted file mode 100644 index aead731d933..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/parameters/params_view.py +++ /dev/null @@ -1,138 +0,0 @@ -import configparser -from pathlib import Path -from typing import Tuple - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.portfolio.portfolio_optimization import excel_model -from openbb_terminal.portfolio.portfolio_optimization.parameters import params_statics -from openbb_terminal.portfolio.portfolio_optimization.parameters.params_helpers import ( - booltostr, - check_convert_parameters, -) -from openbb_terminal.rich_config import console - - -def load_file(path: str = "") -> Tuple[dict, str]: - """ - Loads in the configuration file and return the parameters in a dictionary including the model - if available. - - Parameters - ---------- - path: str - The location of the file to be loaded in either xlsx or ini. - - Returns - ------- - Tuple[dict, str] - Return the parameters and the model, if available. - """ - if str(path).endswith(".ini"): - params_obj = configparser.RawConfigParser() - params_obj.read(path) - params_obj.optionxform = str # type: ignore - params: dict = dict(params_obj["OPENBB"].items()) - - current_model = params["technique"] if "technique" in params else "" - - elif str(path).endswith(".xlsx"): - params, _ = excel_model.load_configuration(path) - current_model = params["technique"] - else: - console.print("Cannot load in the file due to not being an .ini or .xlsx file.") - return {}, "" - - converted_parameters = check_convert_parameters(received_parameters=params) - - max_len = max(len(k) for k in converted_parameters) - help_text = "[info]Parameters:[/info]\n" - - if current_model: - for k, v in converted_parameters.items(): - all_params = ( - params_statics.DEFAULT_PARAMETERS - + params_statics.MODEL_PARAMS[current_model] - ) - if k in all_params: - clean_v = booltostr(v) - help_text += ( - f" [param]{k}{' ' * (max_len - len(k))} :[/param] {clean_v}\n" - ) - else: - for k, v in converted_parameters.items(): - clean_v = booltostr(v) - help_text += ( - f" [param]{k}{' ' * (max_len - len(k))} :[/param] {clean_v}\n" - ) - - console.print(help_text) - - return converted_parameters, current_model - - -def save_file(path: str, params: dict) -> Path: - if not path.endswith(".ini"): - console.print("[red]File to be saved needs to be a .ini file.[/red]\n") - # Create file if it does not exist - base_path = ( - get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY / "optimization" - ) - if not base_path.is_dir(): - base_path.mkdir() - filepath = base_path / path - - config_parser = configparser.RawConfigParser() - config_parser.add_section("OPENBB") - for key, value in params.items(): - config_parser.set("OPENBB", key, value) - - with open(filepath, "w") as configfile: - config_parser.write(configfile) - - return filepath - - -def show_arguments(arguments, description=None): - """ - Show the available arguments and the choices you have for each. If available, also show - the description of the argument. - - Parameters - ---------- - arguments: Dictionary - A dictionary containing the keys and the possible values. - description: Dictionary - A dictionary containing the keys equal to arguments and the descriptions. - - Returns - ------- - A table containing the parameter names, possible values and (if applicable) the description. - """ - adjusted_arguments = {} - - for variable in arguments: - if len(arguments[variable]) > 15: - minimum = min(arguments[variable]) - maximum = max(arguments[variable]) - adjusted_arguments[variable] = ( - f"Between {minimum} and {maximum} in steps of " - f"{maximum / sum(x > 0 for x in arguments[variable])}" - ) - else: - adjusted_arguments[variable] = ", ".join(arguments[variable]) - - if description: - df = pd.DataFrame([adjusted_arguments, description]).T - columns = ["Options", "Description"] - else: - df = pd.DataFrame([adjusted_arguments]).T - columns = ["Options"] - - df = df[df.index != "technique"] - - print_rich_table( - df, headers=list(columns), show_index=True, index_name="Parameters" - ) diff --git a/openbb_terminal/portfolio/portfolio_optimization/po_controller.py b/openbb_terminal/portfolio/portfolio_optimization/po_controller.py deleted file mode 100644 index f5b5c2afb6c..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/po_controller.py +++ /dev/null @@ -1,3679 +0,0 @@ -""" Portfolio Optimization Controller Module """ - -__docformat__ = "numpy" - -# pylint: disable=too-many-lines,too-many-instance-attributes - -import argparse -import logging -from typing import Dict, List, Optional, Tuple - -from openbb_terminal import ( - parent_classes, -) -from openbb_terminal.core.config.paths import ( - MISCELLANEOUS_DIRECTORY, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import check_non_negative, get_rf -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.portfolio.portfolio_optimization import ( - excel_model, - optimizer_model, - optimizer_view, - statics, -) -from openbb_terminal.portfolio.portfolio_optimization.parameters import ( - params_controller, - params_view, -) -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -def add_arguments(parser_update, parser, not_in_list): - parser_dict = vars(parser) - for i in parser_dict["_actions"]: - data_dict = vars(i) - variables = list(data_dict.keys()) - if variables[0] == "option_strings" and data_dict["dest"] not in not_in_list: - args = [data_dict["option_strings"][0] + "-sa"] - if len(data_dict["option_strings"]) == 2: - args.append(data_dict["option_strings"][1] + "-sa") - if len(data_dict["option_strings"]) in [1, 2]: - parser_update.add_argument( - *args, - type=data_dict["type"], - default=data_dict["default"], - dest=data_dict["dest"] + "_sa", - choices=data_dict["choices"], - help=data_dict["help"], - ) - - -def check_input( - input_type: str, input_list: List[str], available_list: List[str] -) -> List[str]: - """Check if input is valid - - Parameters - ---------- - input_type : str - Type of input - input_list : List[str] - List of input - available_list : List[str] - List of available input - - Returns - ------- - List[str] - Valid categories - """ - - valid: List[str] = [] - - for i in input_list: - if i in available_list: - valid.append(i) - else: - console.print(f"[red]{input_type} '{i}' not available.[/red]\n") - return valid - - -def get_valid_portfolio_categories( - input_portfolios: List[str], - available_portfolios: Dict, - input_categories: List[str], - available_categories: Dict, -) -> Tuple[List[str], List[str]]: - """Get valid portfolios and categories - - Parameters - ---------- - input_portfolios : List[str] - List of input portfolios - available_portfolios : Dict - Dict of available portfolios - input_categories : List[str] - List of input categories - available_categories : Dict - Dict of available categories - - Returns - ------- - Tuple[List[str], List[str]] - Valid portfolios and categories - """ - portfolios_list = list(set(available_portfolios.keys())) - categories_list = list(set(available_categories.keys())) - - if not portfolios_list: - portfolio_msg = "None. Perform some optimization to build a portfolio." - else: - if not input_portfolios: - console.print("[yellow]Please select at least one portfolio.[/yellow]\n") - portfolio_msg = ", ".join(portfolios_list) - - if not categories_list: - categories_msg = "None. Attribute some categories in the loaded file." - else: - categories_msg = ", ".join(categories_list) - - console.print( - f"[yellow]Current Portfolios: [/yellow]{portfolio_msg}\n", - ) - - console.print( - f"[yellow]Current Categories: [/yellow]{categories_msg}\n", - ) - - valid_portfolios = check_input( - input_type="Portfolio", - input_list=input_portfolios, - available_list=portfolios_list, - ) - - valid_categories = check_input( - input_type="Category", - input_list=input_categories, - available_list=categories_list, - ) - - return valid_portfolios, valid_categories - - -class PortfolioOptimizationController(BaseController): - """Portfolio Optimization Controller class""" - - FILE_TYPE_LIST = ["xlsx", "ini"] - - CHOICES_COMMANDS = [ - "show", - "rpf", - "load", - "plot", - "maxsharpe", - "minrisk", - "maxutil", - "maxret", - "maxdiv", - "maxdecorr", - "blacklitterman", - "riskparity", - "relriskparity", - "hrp", - "herc", - "nco", - "mktcap", - "equal", - "ef", - "file", - ] - CHOICES_MENUS = ["params"] - - PATH = "/portfolio/po/" - CHOICES_GENERATION = True - - files_available: List = list() - - @classmethod - def build_allocation_file_map(cls) -> dict: - allocation_file_map = { - filepath.name: filepath - for file_type in cls.FILE_TYPE_LIST - for filepath in ( - get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY - / "allocation" - ).rglob(f"*.{file_type}") - } - - return allocation_file_map - - @classmethod - def build_optimization_file_map(cls) -> dict: - optimization_file_map = { - filepath.name: filepath - for file_type in cls.FILE_TYPE_LIST - for filepath in ( - get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY - / "optimization" - ).rglob(f"*.{file_type}") - } - - return optimization_file_map - - def __init__( - self, - tickers: Optional[List[str]] = None, - portfolios: Optional[Dict] = None, - categories: Optional[Dict] = None, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - self.current_model = None - - if tickers: - self.tickers = list(set(tickers)) - self.tickers.sort() - else: - self.tickers = list() - - if portfolios: - self.portfolios = dict(portfolios) - else: - self.portfolios = dict() - - if categories: - self.categories = dict(categories) - self.available_categories = list(self.categories.keys()) - - if "CURRENT_INVESTED_AMOUNT" in self.available_categories: - self.available_categories.remove("CURRENT_INVESTED_AMOUNT") - else: - self.categories = dict() - self.available_categories = list() - - self.count = 0 - self.current_portfolio = "" - - self.allocation_file_map = self.build_allocation_file_map() - self.optimization_file_map = self.build_optimization_file_map() - - self.current_file = "" - - self.params: Dict = {} - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.choices = choices - self.completer = NestedCompleter.from_nested_dict(choices) - - def update_runtime_choices(self): - if ( - session - and get_current_user().preferences.USE_PROMPT_TOOLKIT - and self.portfolios - ): - self.choices["show"]["--portfolios"] = { - c: {} for c in list(self.portfolios.keys()) - } - self.choices["rpf"]["--portfolios"] = { - c: {} for c in list(self.portfolios.keys()) - } - self.choices["plot"]["--portfolios"] = { - c: {} for c in list(self.portfolios.keys()) - } - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def print_help(self): - """Print help""" - mt = MenuText("portfolio/po/") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_loaded", self.current_portfolio or "") - mt.add_raw("\n") - mt.add_param("_tickers", ", ".join(self.tickers)) - mt.add_param("_categories", ", ".join(self.available_categories)) - mt.add_raw("\n") - mt.add_cmd("file") - mt.add_menu("params") - mt.add_raw("\n") - mt.add_param("_parameter", self.current_file) - mt.add_raw("\n") - mt.add_info("_mean_risk_optimization_") - mt.add_cmd("maxsharpe", self.tickers) - mt.add_cmd("minrisk", self.tickers) - mt.add_cmd("maxutil", self.tickers) - mt.add_cmd("maxret", self.tickers) - mt.add_cmd("maxdiv", self.tickers) - mt.add_cmd("maxdecorr", self.tickers) - mt.add_cmd("blacklitterman", self.tickers) - mt.add_cmd("ef", self.tickers) - mt.add_raw("\n") - - mt.add_info("_risk_parity_optimization_") - mt.add_cmd("riskparity", self.tickers) - mt.add_cmd("relriskparity", self.tickers) - mt.add_raw("\n") - - mt.add_info("_hierarchical_clustering_models_") - mt.add_cmd("hrp", self.tickers) - mt.add_cmd("herc", self.tickers) - mt.add_cmd("nco", self.tickers) - mt.add_raw("\n") - - mt.add_info("_other_optimization_techniques_") - mt.add_cmd("equal", self.tickers) - mt.add_cmd("mktcap", self.tickers) - mt.add_raw("\n") - - mt.add_param("_optimized_portfolio", ", ".join(self.portfolios.keys())) - mt.add_raw("\n") - - mt.add_cmd("rpf", bool(self.portfolios.keys())) - mt.add_cmd("show", bool(self.portfolios.keys())) - mt.add_cmd("plot", bool(self.portfolios.keys())) - - console.print(text=mt.menu_text, menu="Portfolio - Portfolio Optimization") - - # pylint: disable=too-many-arguments - def po_parser( - self, - parser, - rm: bool = False, - mt: bool = False, - ct: bool = False, - p: bool = False, - s: bool = False, - e: bool = False, - lr: bool = False, - freq: bool = False, - mn: bool = False, - th: bool = False, - r: bool = False, - a: bool = False, - v: bool = True, - name: str = "", - ): - """Holds common parser arguments to eliminate repetition""" - if rm: - parser.add_argument( - "-rm", - "--risk-measure", - default=( - self.params["risk_measure"] - if "risk_measure" in self.params - else "MV" - ), - dest="risk_measure", - help="""Risk measure used to optimize the portfolio. Possible values are: - 'MV' : Variance - 'MAD' : Mean Absolute Deviation - 'MSV' : Semi Variance (Variance of negative returns) - 'FLPM' : First Lower Partial Moment - 'SLPM' : Second Lower Partial Moment - 'CVaR' : Conditional Value at Risk - 'EVaR' : Entropic Value at Risk - 'WR' : Worst Realization - 'ADD' : Average Drawdown of uncompounded returns - 'UCI' : Ulcer Index of uncompounded returns - 'CDaR' : Conditional Drawdown at Risk of uncompounded returns - 'EDaR' : Entropic Drawdown at Risk of uncompounded returns - 'MDD' : Maximum Drawdown of uncompounded returns - """, - choices=statics.MEAN_RISK_CHOICES, - ) - if mt: - parser.add_argument( - "-mt", - "--method", - default=( - self.params["nan_fill_method"] - if "nan_fill_method" in self.params - else "time" - ), - dest="nan_fill_method", - help="""Method used to fill nan values in time series, by default time. - Possible values are: - 'linear': linear interpolation - 'time': linear interpolation based on time index - 'nearest': use nearest value to replace nan values - 'zero': spline of zeroth order - 'slinear': spline of first order - 'quadratic': spline of second order - 'cubic': spline of third order - 'barycentric': builds a polynomial that pass for all points""", - choices=statics.METHOD_CHOICES, - metavar="METHOD", - ) - if ct: - parser.add_argument( - "-ct", - "--categories", - dest="categories", - type=lambda s: [str(item).upper() for item in s.split(",")], - default=self.available_categories, - help="Show selected categories", - ) - if p: - parser.add_argument( - "-p", - "--period", - default=( - self.params["historic_period"] - if "historic_period" in self.params - else "3y" - ), - dest="historic_period", - help="""Period to get yfinance data from. - Possible frequency strings are: - 'd': means days, for example '252d' means 252 days - 'w': means weeks, for example '52w' means 52 weeks - 'mo': means months, for example '12mo' means 12 months - 'y': means years, for example '1y' means 1 year - 'ytd': downloads data from beginning of year to today - 'max': downloads all data available for each asset""", - choices=statics.PERIOD_CHOICES, - metavar="PERIOD", - ) - if s: - parser.add_argument( - "-s", - "--start", - default=( - self.params["start_period"] if "start_period" in self.params else "" - ), - dest="start_period", - help="""Start date to get yfinance data from. Must be in - 'YYYY-MM-DD' format""", - ) - if e: - parser.add_argument( - "-e", - "--end", - default=( - self.params["end_period"] if "end_period" in self.params else "" - ), - dest="end_period", - help="""End date to get yfinance data from. Must be in - 'YYYY-MM-DD' format""", - ) - if lr: - parser.add_argument( - "-lr", - "--log-returns", - action="store_true", - default=( - self.params["log_returns"] - if "log_returns" in self.params - else False - ), - dest="log_returns", - help="If use logarithmic or arithmetic returns to calculate returns", - ) - if freq: - parser.add_argument( - "--freq", - default=( - self.params["return_frequency"] - if "return_frequency" in self.params - else "d" - ), - dest="return_frequency", - help="""Frequency used to calculate returns. Possible values are: - 'd': for daily returns - 'w': for weekly returns - 'm': for monthly returns - """, - choices=statics.FREQ_CHOICES, - ) - if mn: - parser.add_argument( - "-mn", - "--maxnan", - type=float, - default=self.params["max_nan"] if "max_nan" in self.params else 0.05, - dest="max_nan", - help="""Max percentage of nan values accepted per asset to be - considered in the optimization process""", - ) - if th: - parser.add_argument( - "-th", - "--threshold", - type=float, - default=( - self.params["threshold_value"] - if "threshold_value" in self.params - else 0.30 - ), - dest="threshold_value", - help="""Value used to replace outliers that are higher to threshold - in absolute value""", - ) - if r: - parser.add_argument( - "-r", - "--risk-free-rate", - type=float, - dest="risk_free", - default=( - self.params["risk_free"] if "risk_free" in self.params else get_rf() - ), - help="""Risk-free rate of borrowing/lending. The period of the - risk-free rate must be annual""", - ) - if a: - parser.add_argument( - "-a", - "--alpha", - type=float, - default=( - self.params["significance_level"] - if "significance_level" in self.params - else 0.05 - ), - dest="significance_level", - help="Significance level of CVaR, EVaR, CDaR and EDaR", - ) - if v: - parser.add_argument( - "-v", - "--value", - default=( - self.params["long_allocation"] - if "long_allocation" in self.params - else 1 - ), - type=float, - dest="long_allocation", - help="Amount to allocate to portfolio", - ) - if name: - parser.add_argument( - "--name", - type=str, - dest="name", - default=name + str(self.count), - help="Save portfolio with personalized or default name", - ) - return parser - - def custom_reset(self): - """Class specific component of reset command""" - objects_to_reload = ["portfolio", "po"] - if self.current_portfolio: - objects_to_reload.append(f"load {self.current_portfolio}") - if self.current_file: - objects_to_reload.append(f"file {self.current_file}") - return objects_to_reload - - @log_start_end(log=logger) - def call_file(self, other_args: List[str]): - """Process file command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="file", - description="Select parameter file to use. The OpenBB Parameters Template can be " - "found inside the Portfolio Optimization documentation. Please type `about` to access the documentation.", - ) - - parser.add_argument( - "-f", - "--file", - nargs="+", - dest="file", - help="Parameter file to be used", - choices=self.optimization_file_map.keys(), - metavar="FILE", - type=str, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--file") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if not ns_parser.file: - console.print( - "[green]The OpenBB Parameters Template can be found inside " - "the Portfolio Optimization documentation. Please type `about` " - "to access the documentation. [green]\n" - ) - else: - self.current_file = " ".join(ns_parser.file) - - file_location = self.optimization_file_map.get( - self.current_file, self.current_file - ) - self.params, self.current_model = params_view.load_file(file_location) # type: ignore - - @log_start_end(log=logger) - def call_params(self, _): - """Process params command""" - self.queue = self.load_class( - params_controller.ParametersController, - self.current_file, - self.queue, - self.params, - self.current_model, - ) - if "/portfolio/po/params/" in parent_classes.controllers: - self.current_file = parent_classes.controllers[ - "/portfolio/po/params/" - ].current_file - self.current_model = parent_classes.controllers[ - "/portfolio/po/params/" - ].current_model - self.params = parent_classes.controllers["/portfolio/po/params/"].params - - @log_start_end(log=logger) - def call_show(self, other_args: List[str]): - """Show saved portfolios""" - parser = argparse.ArgumentParser( - add_help=False, - prog="show", - description="""Show selected saved portfolios""", - ) - parser.add_argument( - "-pf", - "--portfolios", - dest="portfolios", - type=lambda s: [str(item).upper() for item in s.split(",")], - default=[], - help="Show selected saved portfolios", - ) - if other_args and "-" not in other_args[0]: - other_args.insert(0, "-pf") - - parser = self.po_parser(parser, ct=True) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - portfolios, categories = get_valid_portfolio_categories( - input_portfolios=ns_parser.portfolios, - available_portfolios=self.portfolios, - input_categories=ns_parser.categories, - available_categories=self.categories, - ) - - for p in portfolios: - console.print("[yellow]Portfolio[/yellow]: " + p + "\n") - optimizer_view.display_show( - weights=self.portfolios[p], - tables=categories, - categories_dict=self.categories, - ) - - @log_start_end(log=logger) - def call_rpf(self, other_args: List[str]): - """Remove one portfolio""" - parser = argparse.ArgumentParser( - add_help=False, - prog="rpf", - description="""Remove one of the portfolios""", - ) - parser.add_argument( - "-pf", - "--portfolios", - dest="portfolios", - type=lambda s: [str(item).upper() for item in s.split(",")], - default=[], - help="portfolios to be removed from the saved portfolios", - ) - if other_args and "-" not in other_args[0]: - other_args.insert(0, "-pf") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - portfolios = set(self.portfolios.keys()) - for portfolio in ns_parser.portfolios: - if portfolio in portfolios: - self.portfolios.pop(portfolio) - portfolios.remove(portfolio) - console.print(f"[param]Removed '{portfolio}'.[/param]") - else: - console.print(f"[red]Portfolio '{portfolio}' does not exist.[/red]") - - if self.portfolios: - console.print( - f"\n[param]Current Portfolios: [/param]{('None', ', '.join(portfolios))[bool(portfolios)]}" - ) - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - """Load file with stocks tickers and categories""" - parser = argparse.ArgumentParser( - add_help=False, - prog="load", - description="""Load file of stocks tickers with optional categories""", - ) - parser.add_argument( - "-f", - "--file", - nargs="+", - dest="file", - help="Allocation file to be used", - choices=self.allocation_file_map.keys(), - ) - parser.add_argument( - "-e", - "--example", - help="Run an example allocation file to understand how the portfolio optimization menu can be used.", - dest="example", - action="store_true", - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--file") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - filename = "" - - if ns_parser.file: - filename = " ".join(ns_parser.file) - elif ns_parser.example: - filename = "OpenBB Example Portfolio" - else: - console.print( - "[green]Please input a filename with load --file or obtain an example with load --example[/green]" - ) - - if filename: - if ns_parser.example: - file_location = ( - MISCELLANEOUS_DIRECTORY - / "portfolio" - / "allocation_example.xlsx" - ) - - console.print( - "[green]Loading an example, please type `about` " - "to learn how to create your own Portfolio Optimization Excel sheet.[/green]\n" - ) - elif filename in self.allocation_file_map: - file_location = self.allocation_file_map[filename] - else: - file_location = filename # type: ignore - - self.tickers, self.categories = excel_model.load_allocation( - file_location - ) - self.available_categories = list(self.categories.keys()) - if "CURRENT_INVESTED_AMOUNT" in self.available_categories: - self.available_categories.remove("CURRENT_INVESTED_AMOUNT") - self.portfolios = dict() - self.update_runtime_choices() - self.current_portfolio = filename - - console.print( - f"[param]Portfolio loaded:[/param] {self.current_portfolio}" - ) - console.print( - f"[param]Categories:[/param] {', '.join(self.available_categories)}\n" - ) - - @log_start_end(log=logger) - def call_plot(self, other_args: List[str]): - """Process plot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="plot", - description="Plot selected charts for portfolios", - ) - parser.add_argument( - "-pf", - "--portfolios", - type=lambda s: [str(item).upper() for item in s.split(",")], - default=[], - dest="portfolios", - help="Selected portfolios that will be plotted", - ) - parser.add_argument( - "-pi", - "--pie", - action="store_true", - dest="pie", - default=False, - help="Display a pie chart for weights", - ) - parser.add_argument( - "-hi", - "--hist", - action="store_true", - dest="hist", - default=False, - help="Display a histogram with risk measures", - ) - parser.add_argument( - "-dd", - "--drawdown", - action="store_true", - dest="dd", - default=False, - help="Display a drawdown chart with risk measures", - ) - parser.add_argument( - "-rc", - "--rc-chart", - action="store_true", - dest="rc_chart", - default=False, - help="Display a risk contribution chart for assets", - ) - parser.add_argument( - "-he", - "--heat", - action="store_true", - dest="heat", - default=False, - help="Display a heatmap of correlation matrix with dendrogram", - ) - parser = self.po_parser( - parser, - rm=True, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - ) - if other_args and "-" not in other_args[0]: - other_args.insert(0, "-pf") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - portfolios, categories = get_valid_portfolio_categories( - input_portfolios=ns_parser.portfolios, - available_portfolios=self.portfolios, - input_categories=ns_parser.categories, - available_categories=self.categories, - ) - if not portfolios: - return - - if not ( - ns_parser.pie - or ns_parser.hist - or ns_parser.dd - or ns_parser.rc_chart - or ns_parser.heat - ): - console.print( - "[red]Please select at least one chart to plot " - "from the following: -pi, -hi, -dd, -rc, -he.[/red]", - ) - return - - stocks = [] - for i in portfolios: - stocks += list(self.portfolios[i].keys()) - stocks = list(set(stocks)) - stocks.sort() - - _, stock_returns = optimizer_model.get_equal_weights( - symbols=stocks, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - value=1, - ) - - if ns_parser.hist or ns_parser.dd: - for i in portfolios: - weights = self.portfolios[i] - weights = dict( - sorted(weights.items(), key=lambda x: x[1], reverse=True) - ) - stocks = list(weights.keys()) - - # hist and dd are transversal to all categories - optimizer_view.additional_plots( - weights=weights, - data=stock_returns[stocks], - portfolio_name=i, - freq=ns_parser.return_frequency, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - a_sim=100, - beta=ns_parser.significance_level, - b_sim=100, - hist=ns_parser.hist, - dd=ns_parser.dd, - ) - - if ns_parser.pie or ns_parser.rc_chart or ns_parser.heat: - if not categories: - console.print( - "[yellow]Categories must be provided to use -pi, -rc or -he.[/yellow]" - ) - return - - for i in portfolios: - weights = self.portfolios[i] - weights = dict( - sorted(weights.items(), key=lambda x: x[1], reverse=True) - ) - stocks = list(weights.keys()) - - # pie, rc_chart and heat apply to each category - for category in categories: - filtered_categories = dict( - filter( - lambda elem: elem[0] in stocks, - self.categories[category].items(), - ) - ) - optimizer_view.additional_plots( - weights=weights, - data=stock_returns[stocks], - category_dict=filtered_categories, - category=category, - portfolio_name=i, - freq=ns_parser.return_frequency, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - a_sim=100, - beta=ns_parser.significance_level, - b_sim=100, - pie=ns_parser.pie, - rc_chart=ns_parser.rc_chart, - heat=ns_parser.heat, - ) - - @log_start_end(log=logger) - def call_equal(self, other_args: List[str]): - """Process equal command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="equal", - description="Returns an equally weighted portfolio", - ) - parser = self.po_parser( - parser, - rm=True, - mt=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="NAME_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - weights = optimizer_view.display_equal_weight( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - value=ns_parser.long_allocation, - table=True, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_mktcap(self, other_args: List[str]): - """Process mktcap command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mktcap", - description="Returns a portfolio that is weighted based on Market Cap.", - ) - parser = self.po_parser( - parser, - rm=True, - mt=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="MKTCAP_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 stocks selected to perform calculations." - ) - return - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_property_weighting( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - value=ns_parser.long_allocation, - table=True, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_maxsharpe(self, other_args: List[str]): - """Process maxsharpe command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="maxsharpe", - description="Maximizes the portfolio's return/risk ratio", - ) - parser.add_argument( - "-tr", - "--target-return", - dest="target_return", - default=( - self.params["target_return"] if "target_return" in self.params else -1 - ), - type=float, - help="Constraint on minimum level of portfolio's return", - ) - parser.add_argument( - "-tk", - "--target-risk", - dest="target_risk", - default=self.params["target_risk"] if "target_risk" in self.params else -1, - type=float, - help="Constraint on maximum level of portfolio's risk", - ) - parser.add_argument( - "-m", - "--mean", - default=( - self.params["expected_return"] - if "expected_return" in self.params - else "hist" - ), - dest="expected_return", - help="Method used to estimate the expected return vector", - choices=statics.MEAN_CHOICES, - ) - parser.add_argument( - "-cv", - "--covariance", - default=( - self.params["covariance"] if "covariance" in self.params else "hist" - ), - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - parser.add_argument( - "-vs", - "--value-short", - dest="short_allocation", - help="Amount to allocate to portfolio in short positions", - type=float, - default=( - self.params["short_allocation"] - if "short_allocation" in self.params - else 0.0 - ), - ) - - parser = self.po_parser( - parser, - rm=True, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="MAXSHARPE_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_max_sharpe( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - target_return=ns_parser.target_return, - target_risk=ns_parser.target_risk, - mean=ns_parser.expected_return.lower(), - covariance=ns_parser.covariance.lower(), - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - value_short=ns_parser.short_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_max_sharpe( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - risk_measure=ns_parser.risk_measure_sa.lower(), - risk_free_rate=ns_parser.risk_free_sa, - alpha=ns_parser.significance_level_sa, - target_return=ns_parser.target_return_sa, - target_risk=ns_parser.target_risk_sa, - mean=ns_parser.expected_return_sa.lower(), - covariance=ns_parser.covariance_sa.lower(), - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - value_short=ns_parser.short_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_minrisk(self, other_args: List[str]): - """Process minrisk command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="minrisk", - description="Minimizes portfolio's risk", - ) - parser.add_argument( - "-tr", - "--target-return", - dest="target_return", - default=( - self.params["target_return"] if "target_return" in self.params else -1 - ), - type=float, - help="Constraint on minimum level of portfolio's return", - ) - parser.add_argument( - "-tk", - "--target-risk", - dest="target_risk", - default=self.params["target_risk"] if "target_risk" in self.params else -1, - type=float, - help="Constraint on maximum level of portfolio's risk", - ) - parser.add_argument( - "-m", - "--mean", - default=( - self.params["expected_return"] - if "expected_return" in self.params - else "hist" - ), - dest="expected_return", - help="Method used to estimate expected returns vector", - choices=statics.MEAN_CHOICES, - ) - parser.add_argument( - "-cv", - "--covariance", - default=self.params["covariance"] if "mean_ewma" in self.params else "hist", - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - parser.add_argument( - "-vs", - "--value-short", - type=float, - default=( - self.params["short_allocation"] - if "short_allocation" in self.params - else 0.0 - ), - dest="short_allocation", - help="Amount to allocate to portfolio in short positions", - ) - - parser = self.po_parser( - parser, - rm=True, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="MINRISK_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_min_risk( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - target_return=ns_parser.target_return, - target_risk=ns_parser.target_risk, - mean=ns_parser.expected_return.lower(), - covariance=ns_parser.covariance.lower(), - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - value_short=ns_parser.short_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_min_risk( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - risk_measure=ns_parser.risk_measure_sa.lower(), - risk_free_rate=ns_parser.risk_free_sa, - alpha=ns_parser.significance_level_sa, - target_return=ns_parser.target_return_sa, - target_risk=ns_parser.target_risk_sa, - mean=ns_parser.expected_return_sa.lower(), - covariance=ns_parser.covariance_sa.lower(), - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - value_short=ns_parser.short_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_maxutil(self, other_args: List[str]): - """Process maxutil command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="maxutil", - description="Maximizes a risk averse utility function", - ) - parser.add_argument( - "-ra", - "--risk-aversion", - type=float, - dest="risk_aversion", - default=( - self.params["risk_aversion"] if "risk_aversion" in self.params else 1 - ), - help="Risk aversion parameter", - ) - parser.add_argument( - "-tr", - "--target-return", - dest="target_return", - default=( - self.params["target_return"] if "target_return" in self.params else -1 - ), - type=float, - help="Constraint on minimum level of portfolio's return", - ) - parser.add_argument( - "-tk", - "--target-risk", - dest="target_risk", - default=self.params["target_risk"] if "target_risk" in self.params else -1, - type=float, - help="Constraint on maximum level of portfolio's risk", - ) - parser.add_argument( - "-m", - "--mean", - default=( - self.params["expected_return"] - if "expected_return" in self.params - else "hist" - ), - dest="expected_return", - help="Method used to estimate the expected return vector", - choices=statics.MEAN_CHOICES, - ) - parser.add_argument( - "-cv", - "--covariance", - default=( - self.params["covariance"] if "covariance" in self.params else "hist" - ), - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - parser.add_argument( - "-vs", - "--value-short", - dest="short_allocation", - help="Amount to allocate to portfolio in short positions", - type=float, - default=( - self.params["short_allocation"] - if "short_allocation" in self.params - else 0.0 - ), - ) - - parser = self.po_parser( - parser, - rm=True, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="MAXUTIL_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_max_util( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - risk_aversion=ns_parser.risk_aversion, - alpha=ns_parser.significance_level, - target_return=ns_parser.target_return, - target_risk=ns_parser.target_risk, - mean=ns_parser.expected_return.lower(), - covariance=ns_parser.covariance.lower(), - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - value_short=ns_parser.short_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_max_util( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - risk_measure=ns_parser.risk_measure_sa.lower(), - risk_free_rate=ns_parser.risk_free_sa, - risk_aversion=ns_parser.risk_aversion_sa, - alpha=ns_parser.significance_level_sa, - target_return=ns_parser.target_return_sa, - target_risk=ns_parser.target_risk_sa, - mean=ns_parser.expected_return_sa.lower(), - covariance=ns_parser.covariance_sa.lower(), - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - value_short=ns_parser.short_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_maxret(self, other_args: List[str]): - """Process maxret command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="maxret", - description="Maximizes the portfolio's return ", - ) - parser.add_argument( - "-tr", - "--target-return", - dest="target_return", - default=( - self.params["target_return"] if "target_return" in self.params else -1 - ), - type=float, - help="Constraint on minimum level of portfolio's return", - ) - parser.add_argument( - "-tk", - "--target-risk", - dest="target_risk", - default=self.params["target_risk"] if "target_risk" in self.params else -1, - type=float, - help="Constraint on maximum level of portfolio's risk", - ) - parser.add_argument( - "-m", - "--mean", - default=( - self.params["expected_return"] - if "expected_return" in self.params - else "hist" - ), - dest="expected_return", - help="Method used to estimate the expected return vector", - choices=statics.MEAN_CHOICES, - ) - parser.add_argument( - "-cv", - "--covariance", - default=( - self.params["covariance"] if "covariance" in self.params else "hist" - ), - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - parser.add_argument( - "-vs", - "--value-short", - dest="short_allocation", - help="Amount to allocate to portfolio in short positions", - type=float, - default=( - self.params["short_allocation"] - if "short_allocation" in self.params - else 0.0 - ), - ) - - parser = self.po_parser( - parser, - rm=True, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="MAXRET_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_max_ret( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - target_return=ns_parser.target_return, - target_risk=ns_parser.target_risk, - mean=ns_parser.expected_return.lower(), - covariance=ns_parser.covariance.lower(), - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - value_short=ns_parser.short_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_max_ret( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - risk_measure=ns_parser.risk_measure_sa.lower(), - risk_free_rate=ns_parser.risk_free_sa, - alpha=ns_parser.significance_level_sa, - target_return=ns_parser.target_return_sa, - target_risk=ns_parser.target_risk_sa, - mean=ns_parser.expected_return_sa.lower(), - covariance=ns_parser.covariance_sa.lower(), - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - value_short=ns_parser.short_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_maxdiv(self, other_args: List[str]): - """Process maxdiv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="maxdiv", - description="Maximizes the portfolio's diversification ratio", - ) - parser.add_argument( - "-cv", - "--covariance", - default=( - self.params["covariance"] if "covariance" in self.params else "hist" - ), - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - parser.add_argument( - "-vs", - "--value-short", - dest="short_allocation", - help="Amount to allocate to portfolio in short positions", - type=float, - default=( - self.params["short_allocation"] - if "short_allocation" in self.params - else 0.0 - ), - ) - - parser = self.po_parser( - parser, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - v=True, - name="MAXDIV_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_max_div( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - covariance=ns_parser.covariance.lower(), - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - value_short=ns_parser.short_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_max_div( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - covariance=ns_parser.covariance_sa.lower(), - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - value_short=ns_parser.short_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_maxdecorr(self, other_args: List[str]): - """Process maxdecorr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="maxdecorr", - description="Maximizes the portfolio's decorrelation", - ) - parser.add_argument( - "-cv", - "--covariance", - default=( - self.params["covariance"] if "covariance" in self.params else "hist" - ), - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - parser.add_argument( - "-vs", - "--value-short", - dest="short_allocation", - help="Amount to allocate to portfolio in short positions", - type=float, - default=( - self.params["short_allocation"] - if "short_allocation" in self.params - else 0.0 - ), - ) - - parser = self.po_parser( - parser, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - v=True, - name="MAXDECORR_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_max_decorr( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - covariance=ns_parser.covariance.lower(), - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - value_short=ns_parser.short_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_max_decorr( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - covariance=ns_parser.covariance_sa.lower(), - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - value_short=ns_parser.short_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_blacklitterman(self, other_args: List[str]): - """Process blacklitterman command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="blacklitterman", - description="Optimize portfolio using Black Litterman estimates", - ) - parser.add_argument( - "-bm", - "--benchmark", - type=str, - default=None, - dest="benchmark", - help="portfolio name from current portfolio list", - ) - parser.add_argument( - "-o", - "--objective", - default=( - self.params["objective"] if "objective" in self.params else "Sharpe" - ), - dest="objective", - help="Objective function used to optimize the portfolio", - choices=statics.OBJECTIVE_CHOICES, - ) - parser.add_argument( - "-pv", - "--p-views", - type=lambda s: [ - [float(item) for item in row.split(",")] for row in s.split(";") - ], - default=self.params["p_views"] if "p_views" in self.params else None, - dest="p_views", - help="matrix P of analyst views", - ) - parser.add_argument( - "-qv", - "--q-views", - type=lambda s: [float(item) for item in s.split(",")], - default=self.params["q_views"] if "q_views" in self.params else None, - dest="q_views", - help="matrix Q of analyst views", - ) - parser.add_argument( - "-ra", - "--risk-aversion", - type=float, - dest="risk_aversion", - default=( - self.params["risk_aversion"] if "risk_aversion" in self.params else 1 - ), - help="Risk aversion parameter", - ) - parser.add_argument( - "-d", - "--delta", - default=self.params["delta"] if "delta" in self.params else None, - dest="delta", - type=float, - help="Risk aversion factor of Black Litterman model", - ) - parser.add_argument( - "-eq", - "--equilibrium", - action="store_true", - default=( - self.params["equilibrium"] if "equilibrium" in self.params else True - ), - dest="equilibrium", - help="""If True excess returns are based on equilibrium market portfolio, if False - excess returns are calculated as historical returns minus risk free rate. - """, - ) - parser.add_argument( - "-op", - "--optimize", - action="store_false", - default=self.params["optimize"] if "optimize" in self.params else True, - dest="optimize", - help="""If True Black Litterman estimates are used as inputs of mean variance model, - if False returns equilibrium weights from Black Litterman model - """, - ) - parser.add_argument( - "-vs", - "--value-short", - type=float, - default=( - self.params["short_allocation"] - if "short_allocation" in self.params - else 0.0 - ), - dest="short_allocation", - help="Amount to allocate to portfolio in short positions", - ) - parser.add_argument( - "--file", - type=lambda s: s if s.endswith(".xlsx") or len(s) == 0 else s + ".xlsx", - dest="file", - default="", - help="Upload an Excel file with views for Black Litterman model", - ) - - parser.add_argument( - "--download", - type=lambda s: s if s.endswith(".xlsx") or len(s) == 0 else s + ".xlsx", - dest="download", - default="", - help="Create a template to design Black Litterman model views", - ) - parser = self.po_parser( - parser, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - v=True, - name="BL_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - current_user = get_current_user() - if len(ns_parser.download) > 0: - file = ( - current_user.preferences.USER_EXPORTS_DIRECTORY - / "portfolio" - / "views" - / ns_parser.download - ) - - excel_model.excel_bl_views(file=file, stocks=self.tickers, n=1) - return - - if ns_parser.file: - excel_file = ( - current_user.preferences.USER_PORTFOLIO_DATA_DIRECTORY - / "views" - / ns_parser.file - ) - p_views, q_views = excel_model.load_bl_views(excel_file=excel_file) - else: - p_views = ns_parser.p_views - q_views = ns_parser.q_views - - benchmark = ( - None - if ns_parser.benchmark is None - else self.portfolios[ns_parser.benchmark.upper()] - ) - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_black_litterman( - symbols=self.tickers, - p_views=p_views, - q_views=q_views, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - benchmark=benchmark, - objective=ns_parser.objective.lower(), - risk_free_rate=ns_parser.risk_free, - risk_aversion=ns_parser.risk_aversion, - delta=ns_parser.delta, - equilibrium=ns_parser.equilibrium, - optimize=ns_parser.optimize, - value=ns_parser.long_allocation, - value_short=ns_parser.short_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - if ns_parser.file_sa: - excel_file = ( - get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY - / "views" - / ns_parser.file_sa - ) - p_views_sa, q_views_sa = excel_model.load_bl_views( - excel_file=excel_file - ) - else: - p_views_sa = ns_parser.p_views_sa - q_views_sa = ns_parser.q_views_sa - - weights_sa = optimizer_view.display_black_litterman( - symbols=self.tickers, - p_views=p_views_sa, - q_views=q_views_sa, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - benchmark=benchmark, - objective=ns_parser.objective_sa.lower(), - risk_free_rate=ns_parser.risk_free_sa, - risk_aversion=ns_parser.risk_aversion_sa, - delta=ns_parser.delta_sa, - equilibrium=ns_parser.equilibrium_sa, - optimize=ns_parser.optimize_sa, - value=ns_parser.long_allocation_sa, - value_short=ns_parser.short_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_ef(self, other_args): - """Process ef command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ef", - description="""This function plots random portfolios based on their - risk and returns and shows the efficient frontier.""", - ) - parser.add_argument( - "-vs", - "--value-short", - dest="short_allocation", - help="Amount to allocate to portfolio in short positions", - type=float, - default=( - self.params["short_allocation"] - if "short_allocation" in self.params - else 0.0 - ), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-n") - parser.add_argument( - "-n", - "--number-portfolios", - default=( - self.params["amount_portfolios"] - if "amount_portfolios" in self.params - else 100 - ), - type=check_non_negative, - dest="amount_portfolios", - help="Number of portfolios to simulate", - ) - parser.add_argument( - "-se", - "--seed", - default=self.params["random_seed"] if "random_seed" in self.params else 123, - type=check_non_negative, - dest="random_seed", - help="Seed used to generate random portfolios", - ) - parser.add_argument( - "-t", - "--tangency", - action="store_true", - dest="tangency", - default=self.params["tangency"] if "tangency" in self.params else False, - help="Adds the optimal line with the risk-free asset", - ) - parser.add_argument( - "--no_plot", - action="store_false", - dest="plot_tickers", - default=True, - help="Whether or not to plot the tickers for the assets provided", - ) - parser = self.po_parser( - parser, - rm=True, - mt=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - optimizer_view.display_ef( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - value=ns_parser.long_allocation, - value_short=ns_parser.short_allocation, - n_portfolios=ns_parser.amount_portfolios, - seed=ns_parser.random_seed, - tangency=ns_parser.tangency, - plot_tickers=ns_parser.plot_tickers, - ) - - @log_start_end(log=logger) - def call_riskparity(self, other_args: List[str]): - """Process riskparity command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="riskparity", - description="""Build a risk parity portfolio based on risk - budgeting approach""", - ) - parser.add_argument( - "-rm", - "--risk-measure", - default=( - self.params["risk_measure"] if "risk_measure" in self.params else "MV" - ), - dest="risk_measure", - help="""Risk measure used to optimize the portfolio. Possible values are: - 'MV' : Variance - 'MAD' : Mean Absolute Deviation - 'MSV' : Semi Variance (Variance of negative returns) - 'FLPM' : First Lower Partial Moment - 'SLPM' : Second Lower Partial Moment - 'CVaR' : Conditional Value at Risk - 'EVaR' : Entropic Value at Risk - 'UCI' : Ulcer Index of uncompounded returns - 'CDaR' : Conditional Drawdown at Risk of uncompounded returns - 'EDaR' : Entropic Drawdown at Risk of uncompounded returns - """, - choices=statics.RISK_PARITY_CHOICES, - metavar="RISK-MEASURE", - ) - parser.add_argument( - "-rc", - "--risk-cont", - type=lambda s: [float(item) for item in s.split(",")], - default=( - self.params["risk_contribution"] - if "risk_contribution" in self.params - else None - ), - dest="risk_contribution", - help="vector of risk contribution constraint", - ) - parser.add_argument( - "-tr", - "--target-return", - dest="target_return", - default=( - self.params["target_return"] if "target_return" in self.params else -1 - ), - type=float, - help="Constraint on minimum level of portfolio's return", - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - - parser = self.po_parser( - parser, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="RP_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_risk_parity( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - risk_measure=ns_parser.risk_measure.lower(), - risk_cont=ns_parser.risk_contribution, - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - target_return=ns_parser.target_return, - value=ns_parser.long_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_risk_parity( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - risk_measure=ns_parser.risk_measure_sa.lower(), - risk_cont=ns_parser.risk_contribution_sa, - risk_free_rate=ns_parser.risk_free_sa, - alpha=ns_parser.significance_level_sa, - target_return=ns_parser.target_return_sa, - value=ns_parser.long_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_relriskparity(self, other_args: List[str]): - """Process relriskparity command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="relriskparity", - description="""Build a relaxed risk parity portfolio based on - least squares approach""", - ) - parser.add_argument( - "-ve", - "--version", - default=( - self.params["risk_parity_model"] - if "risk_parity_model" in self.params - else "A" - ), - dest="risk_parity_model", - help="""version of relaxed risk parity model: Possible values are: - 'A': risk parity without regularization and penalization constraints - 'B': with regularization constraint but without penalization constraint - 'C': with regularization and penalization constraints""", - choices=statics.REL_RISK_PARITY_CHOICES, - metavar="VERSION", - ) - parser.add_argument( - "-rc", - "--risk-cont", - type=lambda s: [float(item) for item in s.split(",")], - default=( - self.params["risk_contribution"] - if "risk_contribution" in self.params - else None - ), - dest="risk_contribution", - help="Vector of risk contribution constraints", - ) - parser.add_argument( - "-pf", - "--penal-factor", - type=float, - dest="penal_factor", - default=self.params["penal_factor"] if "penal_factor" in self.params else 1, - help="""The penalization factor of penalization constraints. Only - used with version 'C'.""", - ) - parser.add_argument( - "-tr", - "--target-return", - dest="target_return", - default=( - self.params["target_return"] if "target_return" in self.params else -1 - ), - type=float, - help="Constraint on minimum level of portfolio's return", - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - - parser = self.po_parser( - parser, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - v=True, - name="RRP_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_rel_risk_parity( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - version=ns_parser.risk_parity_model, - risk_cont=ns_parser.risk_contribution, - penal_factor=ns_parser.penal_factor, - target_return=ns_parser.target_return, - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_rel_risk_parity( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - version=ns_parser.risk_parity_model_sa, - risk_cont=ns_parser.risk_contribution_sa, - penal_factor=ns_parser.penal_factor_sa, - target_return=ns_parser.target_return_sa, - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_hrp(self, other_args: List[str]): - """Process hierarchical risk parity command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hrp", - description="Builds a hierarchical risk parity portfolio", - ) - parser.add_argument( - "-cd", - "--codependence", - default=( - self.params["co_dependence"] - if "co_dependence" in self.params - else "pearson" - ), - dest="co_dependence", - help="""The codependence or similarity matrix used to build the - distance metric and clusters. Possible values are: - 'pearson': pearson correlation matrix - 'spearman': spearman correlation matrix - 'abs_pearson': absolute value of pearson correlation matrix - 'abs_spearman': absolute value of spearman correlation matrix - 'distance': distance correlation matrix - 'mutual_info': mutual information codependence matrix - 'tail': tail index codependence matrix""", - choices=statics.CODEPENDENCE_CHOICES, - ) - parser.add_argument( - "-cv", - "--covariance", - default=( - self.params["covariance"] if "covariance" in self.params else "hist" - ), - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-rm", - "--risk-measure", - default=( - self.params["risk_measure"] if "risk_measure" in self.params else "MV" - ), - dest="risk_measure", - help="""Risk measure used to optimize the portfolio. Possible values are: - 'MV' : Variance - 'MAD' : Mean Absolute Deviation - 'GMD' : Gini Mean Difference - 'MSV' : Semi Variance (Variance of negative returns) - 'FLPM' : First Lower Partial Moment - 'SLPM' : Second Lower Partial Moment - 'VaR' : Value at Risk - 'CVaR' : Conditional Value at Risk - 'TG' : Tail Gini - 'EVaR' : Entropic Value at Risk - 'WR' : Worst Realization - 'RG' : Range - 'CVRG' : CVaR Range - 'TGRG' : Tail Gini Range - 'ADD' : Average Drawdown of uncompounded returns - 'UCI' : Ulcer Index of uncompounded returns - 'DaR' : Drawdown at Risk of uncompounded returns - 'CDaR' : Conditional Drawdown at Risk of uncompounded returns - 'EDaR' : Entropic Drawdown at Risk of uncompounded returns - 'MDD' : Maximum Drawdown of uncompounded returns - 'ADD_Rel' : Average Drawdown of compounded returns - 'UCI_Rel' : Ulcer Index of compounded returns - 'DaR_Rel' : Drawdown at Risk of compounded returns - 'CDaR_Rel' : Conditional Drawdown at Risk of compounded returns - 'EDaR_Rel' : Entropic Drawdown at Risk of compounded returns - 'MDD_Rel' : Maximum Drawdown of compounded returns - """, - choices=statics.HCP_CHOICES, - metavar="RISK-MEASURE", - ) - parser.add_argument( - "-as", - "--a-sim", - type=int, - default=( - self.params["cvar_simulations_losses"] - if "cvar_simulations_losses" in self.params - else 100 - ), - dest="cvar_simulations_losses", - help="""Number of CVaRs used to approximate Tail Gini of losses. - The default is 100""", - ) - parser.add_argument( - "-b", - "--beta", - type=float, - default=( - self.params["cvar_significance"] - if "cvar_significance" in self.params - else None - ), - dest="cvar_significance", - help="""Significance level of CVaR and Tail Gini of gains. If - empty it duplicates alpha""", - ) - parser.add_argument( - "-bs", - "--b-sim", - type=int, - default=( - self.params["cvar_simulations_gains"] - if "cvar_simulations_gains" in self.params - else None - ), - dest="cvar_simulations_gains", - help="""Number of CVaRs used to approximate Tail Gini of gains. - If empty it duplicates a_sim value""", - ) - parser.add_argument( - "-lk", - "--linkage", - default=self.params["linkage"] if "linkage" in self.params else "single", - dest="linkage", - help="Linkage method of hierarchical clustering", - choices=statics.LINKAGE_CHOICES, - metavar="LINKAGE", - ) - parser.add_argument( - "-k", - type=int, - default=( - self.params["amount_clusters"] - if "amount_clusters" in self.params - else None - ), - dest="amount_clusters", - help="Number of clusters specified in advance", - ) - parser.add_argument( - "-mk", - "--max-k", - type=int, - default=( - self.params["max_clusters"] if "max_clusters" in self.params else 10 - ), - dest="max_clusters", - help="""Max number of clusters used by the two difference gap - statistic to find the optimal number of clusters. If k is - empty this value is used""", - ) - parser.add_argument( - "-bi", - "--bins-info", - default=( - self.params["amount_bins"] if "amount_bins" in self.params else "KN" - ), - dest="amount_bins", - help="Number of bins used to calculate the variation of information", - choices=statics.BINS_CHOICES, - ) - parser.add_argument( - "-at", - "--alpha-tail", - type=float, - default=self.params["alpha_tail"] if "alpha_tail" in self.params else 0.05, - dest="alpha_tail", - help="""Significance level for lower tail dependence index, only - used when when codependence value is 'tail' """, - ) - parser.add_argument( - "-lo", - "--leaf-order", - default=self.params["leaf_order"] if "leaf_order" in self.params else True, - dest="leaf_order", - help="""Indicates if the cluster are ordered so that the distance - between successive leaves is minimal""", - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - - parser = self.po_parser( - parser, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="_HRP", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_hrp( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - codependence=ns_parser.co_dependence.lower(), - covariance=ns_parser.covariance.lower(), - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - a_sim=ns_parser.cvar_simulations_losses, - beta=ns_parser.cvar_significance, - b_sim=ns_parser.cvar_simulations_gains, - linkage=ns_parser.linkage.lower(), - k=ns_parser.amount_clusters, - max_k=ns_parser.max_clusters, - bins_info=ns_parser.amount_bins.upper(), - alpha_tail=ns_parser.alpha_tail, - leaf_order=ns_parser.leaf_order, - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_hrp( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - codependence=ns_parser.co_dependence_sa.lower(), - covariance=ns_parser.covariance_sa.lower(), - risk_measure=ns_parser.risk_measure_sa.lower(), - risk_free_rate=ns_parser.risk_free_sa, - alpha=ns_parser.significance_level_sa, - a_sim=ns_parser.cvar_simulations_losses_sa, - beta=ns_parser.cvar_significance_sa, - b_sim=ns_parser.cvar_simulations_gains_sa, - linkage=ns_parser.linkage_sa.lower(), - k=ns_parser.amount_clusters_sa, - max_k=ns_parser.max_clusters_sa, - bins_info=ns_parser.amount_bins_sa.upper(), - alpha_tail=ns_parser.alpha_tail_sa, - leaf_order=ns_parser.leaf_order_sa, - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_herc(self, other_args: List[str]): - """Process hierarchical equal risk contribution command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="herc", - description="Builds a hierarchical equal risk contribution portfolio", - ) - parser.add_argument( - "-cd", - "--codependence", - default="pearson", - dest="co_dependence", - help="""The codependence or similarity matrix used to build the - distance metric and clusters. Possible values are: - 'pearson': pearson correlation matrix - 'spearman': spearman correlation matrix - 'abs_pearson': absolute value of pearson correlation matrix - 'abs_spearman': absolute value of spearman correlation matrix - 'distance': distance correlation matrix - 'mutual_info': mutual information codependence matrix - 'tail': tail index codependence matrix""", - choices=statics.CODEPENDENCE_CHOICES, - ) - parser.add_argument( - "-cv", - "--covariance", - default=( - self.params["covariance"] if "covariance" in self.params else "hist" - ), - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-rm", - "--risk-measure", - default=( - self.params["risk_measure"] if "risk_measure" in self.params else "MV" - ), - dest="risk_measure", - help="""Risk measure used to optimize the portfolio. Possible values are: - 'MV' : Variance - 'MAD' : Mean Absolute Deviation - 'GMD' : Gini Mean Difference - 'MSV' : Semi Variance (Variance of negative returns) - 'FLPM' : First Lower Partial Moment - 'SLPM' : Second Lower Partial Moment - 'VaR' : Value at Risk - 'CVaR' : Conditional Value at Risk - 'TG' : Tail Gini - 'EVaR' : Entropic Value at Risk - 'WR' : Worst Realization - 'RG' : Range - 'CVRG' : CVaR Range - 'TGRG' : Tail Gini Range - 'ADD' : Average Drawdown of uncompounded returns - 'UCI' : Ulcer Index of uncompounded returns - 'DaR' : Drawdown at Risk of uncompounded returns - 'CDaR' : Conditional Drawdown at Risk of uncompounded returns - 'EDaR' : Entropic Drawdown at Risk of uncompounded returns - 'MDD' : Maximum Drawdown of uncompounded returns - 'ADD_Rel' : Average Drawdown of compounded returns - 'UCI_Rel' : Ulcer Index of compounded returns - 'DaR_Rel' : Drawdown at Risk of compounded returns - 'CDaR_Rel' : Conditional Drawdown at Risk of compounded returns - 'EDaR_Rel' : Entropic Drawdown at Risk of compounded returns - 'MDD_Rel' : Maximum Drawdown of compounded returns - """, - choices=statics.HCP_CHOICES, - metavar="RISK-MEASURE", - ) - parser.add_argument( - "-as", - "--a-sim", - type=int, - default=( - self.params["cvar_simulations_losses"] - if "cvar_simulations_losses" in self.params - else 100 - ), - dest="cvar_simulations_losses", - help="""Number of CVaRs used to approximate Tail Gini of losses. - The default is 100""", - ) - parser.add_argument( - "-b", - "--beta", - type=float, - default=( - self.params["cvar_significance"] - if "cvar_significance" in self.params - else None - ), - dest="cvar_significance", - help="""Significance level of CVaR and Tail Gini of gains. If - empty it duplicates alpha""", - ) - parser.add_argument( - "-bs", - "--b-sim", - type=int, - default=( - self.params["cvar_simulations_gains"] - if "cvar_simulations_gains" in self.params - else None - ), - dest="cvar_simulations_gains", - help="""Number of CVaRs used to approximate Tail Gini of gains. - If empty it duplicates a_sim value""", - ) - parser.add_argument( - "-lk", - "--linkage", - default=self.params["linkage"] if "linkage" in self.params else "single", - dest="linkage", - help="Linkage method of hierarchical clustering", - choices=statics.LINKAGE_CHOICES, - metavar="LINKAGE", - ) - parser.add_argument( - "-k", - type=int, - default=( - self.params["amount_clusters"] - if "amount_clusters" in self.params - else None - ), - dest="amount_clusters", - help="Number of clusters specified in advance", - ) - parser.add_argument( - "-mk", - "--max-k", - type=int, - default=( - self.params["max_clusters"] if "max_clusters" in self.params else 10 - ), - dest="max_clusters", - help="""Max number of clusters used by the two difference gap - statistic to find the optimal number of clusters. If k is - empty this value is used""", - ) - parser.add_argument( - "-bi", - "--bins-info", - default=( - self.params["amount_bins"] if "amount_bins" in self.params else "KN" - ), - dest="amount_bins", - help="Number of bins used to calculate the variation of information", - choices=statics.BINS_CHOICES, - ) - parser.add_argument( - "-at", - "--alpha-tail", - type=float, - default=self.params["alpha_tail"] if "alpha_tail" in self.params else 0.05, - dest="alpha_tail", - help="""Significance level for lower tail dependence index, only - used when when codependence value is 'tail' """, - ) - parser.add_argument( - "-lo", - "--leaf-order", - default=self.params["leaf_order"] if "leaf_order" in self.params else True, - dest="leaf_order", - help="""Indicates if the cluster are ordered so that the distance - between successive leaves is minimal""", - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - - parser = self.po_parser( - parser, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="HERC_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_herc( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - codependence=ns_parser.co_dependence.lower(), - covariance=ns_parser.covariance.lower(), - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - alpha=ns_parser.significance_level, - a_sim=ns_parser.cvar_simulations_losses, - beta=ns_parser.cvar_significance, - b_sim=ns_parser.cvar_simulations_gains, - linkage=ns_parser.linkage.lower(), - k=ns_parser.amount_clusters, - max_k=ns_parser.max_clusters, - bins_info=ns_parser.amount_bins.upper(), - alpha_tail=ns_parser.alpha_tail, - leaf_order=ns_parser.leaf_order, - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_herc( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - codependence=ns_parser.co_dependence_sa.lower(), - covariance=ns_parser.covariance_sa.lower(), - risk_measure=ns_parser.risk_measure_sa.lower(), - risk_free_rate=ns_parser.risk_free_sa, - alpha=ns_parser.significance_level_sa, - a_sim=ns_parser.cvar_simulations_losses_sa, - beta=ns_parser.cvar_significance_sa, - b_sim=ns_parser.cvar_simulations_gains_sa, - linkage=ns_parser.linkage_sa.lower(), - k=ns_parser.amount_clusters_sa, - max_k=ns_parser.max_clusters_sa, - bins_info=ns_parser.amount_bins_sa.upper(), - alpha_tail=ns_parser.alpha_tail_sa, - leaf_order=ns_parser.leaf_order_sa, - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) - - @log_start_end(log=logger) - def call_nco(self, other_args: List[str]): - """Process nested clustered optimization command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="nco", - description="Builds a nested clustered optimization portfolio", - ) - parser.add_argument( - "-cd", - "--codependence", - default=( - self.params["co_dependence"] - if "co_dependence" in self.params - else "pearson" - ), - dest="co_dependence", - help="""The codependence or similarity matrix used to build the - distance metric and clusters. Possible values are: - 'pearson': pearson correlation matrix - 'spearman': spearman correlation matrix - 'abs_pearson': absolute value of pearson correlation matrix - 'abs_spearman': absolute value of spearman correlation matrix - 'distance': distance correlation matrix - 'mutual_info': mutual information codependence matrix - 'tail': tail index codependence matrix""", - choices=statics.CODEPENDENCE_CHOICES, - ) - parser.add_argument( - "-cv", - "--covariance", - default=( - self.params["covariance"] if "covariance" in self.params else "hist" - ), - dest="covariance", - help="""Method used to estimate covariance matrix. Possible values are - 'hist': historical method - 'ewma1': exponential weighted moving average with adjust=True - 'ewma2': exponential weighted moving average with adjust=False - 'ledoit': Ledoit and Wolf shrinkage method - 'oas': oracle shrinkage method - 'shrunk': scikit-learn shrunk method - 'gl': graphical lasso method - 'jlogo': j-logo covariance - 'fixed': takes average of eigenvalues above max Marchenko Pastour limit - 'spectral': makes zero eigenvalues above max Marchenko Pastour limit - 'shrink': Lopez de Prado's book shrinkage method - """, - choices=statics.COVARIANCE_CHOICES, - ) - parser.add_argument( - "-o", - "--objective", - default=( - self.params["objective"] if "objective" in self.params else "MinRisk" - ), - dest="objective", - help="Objective function used to optimize the portfolio", - choices=statics.NCO_OBJECTIVE_CHOICES, - ) - parser.add_argument( - "-ra", - "--risk-aversion", - type=float, - dest="risk_aversion", - default=( - self.params["long_allocation"] - if "long_allocation" in self.params - else 1 - ), - help="Risk aversion parameter", - ) - parser.add_argument( - "-lk", - "--linkage", - default=self.params["linkage"] if "linkage" in self.params else "single", - dest="linkage", - help="Linkage method of hierarchical clustering", - choices=statics.LINKAGE_CHOICES, - metavar="LINKAGE", - ) - parser.add_argument( - "-k", - type=int, - default=( - self.params["amount_clusters"] - if "amount_clusters" in self.params - else None - ), - dest="amount_clusters", - help="Number of clusters specified in advance", - ) - parser.add_argument( - "-mk", - "--max-k", - type=int, - default=( - self.params["max_clusters"] if "max_clusters" in self.params else 10 - ), - dest="max_clusters", - help="""Max number of clusters used by the two difference gap - statistic to find the optimal number of clusters. If k is - empty this value is used""", - ) - parser.add_argument( - "-bi", - "--bins-info", - default=( - self.params["amount_bins"] if "amount_bins" in self.params else "KN" - ), - dest="amount_bins", - help="Number of bins used to calculate the variation of information", - choices=statics.BINS_CHOICES, - ) - parser.add_argument( - "-at", - "--alpha-tail", - type=float, - default=self.params["alpha_tail"] if "alpha_tail" in self.params else 0.05, - dest="alpha_tail", - help="""Significance level for lower tail dependence index, only - used when when codependence value is 'tail' """, - ) - parser.add_argument( - "-lo", - "--leaf-order", - action="store_true", - default=self.params["leaf_order"] if "leaf_order" in self.params else True, - dest="leaf_order", - help="""indicates if the cluster are ordered so that the distance - between successive leaves is minimal""", - ) - parser.add_argument( - "-de", - "--d-ewma", - type=float, - default=( - self.params["smoothing_factor_ewma"] - if "smoothing_factor_ewma" in self.params - else 0.94 - ), - dest="smoothing_factor_ewma", - help="Smoothing factor for ewma estimators", - ) - - parser = self.po_parser( - parser, - rm=True, - mt=True, - ct=True, - p=True, - s=True, - e=True, - lr=True, - freq=True, - mn=True, - th=True, - r=True, - a=True, - v=True, - name="NCO_", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if len(self.tickers) < 2: - console.print( - "Please have at least 2 loaded tickers to calculate weights.\n" - ) - return - - table = True - if "historic_period_sa" in vars(ns_parser): - table = False - - console.print( - "[yellow]Optimization can take time. Please be patient...\n[/yellow]" - ) - - weights = optimizer_view.display_nco( - symbols=self.tickers, - interval=ns_parser.historic_period, - start_date=ns_parser.start_period, - end_date=ns_parser.end_period, - log_returns=ns_parser.log_returns, - freq=ns_parser.return_frequency, - maxnan=ns_parser.max_nan, - threshold=ns_parser.threshold_value, - method=ns_parser.nan_fill_method, - codependence=ns_parser.co_dependence.lower(), - covariance=ns_parser.covariance.lower(), - objective=ns_parser.objective.lower(), - risk_measure=ns_parser.risk_measure.lower(), - risk_free_rate=ns_parser.risk_free, - risk_aversion=ns_parser.risk_aversion, - alpha=ns_parser.significance_level, - linkage=ns_parser.linkage.lower(), - k=ns_parser.amount_clusters, - max_k=ns_parser.max_clusters, - bins_info=ns_parser.amount_bins.upper(), - alpha_tail=ns_parser.alpha_tail, - leaf_order=ns_parser.leaf_order, - d_ewma=ns_parser.smoothing_factor_ewma, - value=ns_parser.long_allocation, - table=table, - ) - - self.portfolios[ns_parser.name.upper()] = weights - self.count += 1 - self.update_runtime_choices() - - if table is False: - weights_sa = optimizer_view.display_nco( - symbols=self.tickers, - interval=ns_parser.historic_period_sa, - start_date=ns_parser.start_period_sa, - end_date=ns_parser.end_period_sa, - log_returns=ns_parser.log_returns_sa, - freq=ns_parser.return_frequency_sa, - maxnan=ns_parser.max_nan_sa, - threshold=ns_parser.threshold_value_sa, - method=ns_parser.nan_fill_method_sa, - codependence=ns_parser.co_dependence_sa.lower(), - covariance=ns_parser.covariance_sa.lower(), - objective=ns_parser.objective_sa.lower(), - risk_measure=ns_parser.risk_measure_sa.lower(), - risk_free_rate=ns_parser.risk_free_sa, - risk_aversion=ns_parser.risk_aversion_sa, - alpha=ns_parser.significance_level_sa, - linkage=ns_parser.linkage_sa.lower(), - k=ns_parser.amount_clusters_sa, - max_k=ns_parser.max_clusters_sa, - bins_info=ns_parser.amount_bins_sa.upper(), - alpha_tail=ns_parser.alpha_tail_sa, - leaf_order=ns_parser.leaf_order_sa, - d_ewma=ns_parser.smoothing_factor_ewma_sa, - value=ns_parser.long_allocation_sa, - table=table, - ) - - console.print("") - optimizer_view.display_weights_sa( - weights=weights, weights_sa=weights_sa - ) - - if not ns_parser.categories: - categories = ["ASSET_CLASS", "COUNTRY", "SECTOR", "INDUSTRY"] - else: - categories = ns_parser.categories - - for category in categories: - optimizer_view.display_categories_sa( - weights=weights, - weights_sa=weights_sa, - categories=self.categories, - column=category, - title="Category - " + category.title(), - ) diff --git a/openbb_terminal/portfolio/portfolio_optimization/po_engine.py b/openbb_terminal/portfolio/portfolio_optimization/po_engine.py deleted file mode 100644 index 5bfb2abdbb4..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/po_engine.py +++ /dev/null @@ -1,313 +0,0 @@ -from typing import Dict, List, Optional, Tuple - -import pandas as pd - -from openbb_terminal.portfolio.portfolio_optimization import ( - excel_model, - optimizer_helper, - optimizer_model, -) -from openbb_terminal.portfolio.portfolio_optimization.parameters import params_view -from openbb_terminal.rich_config import console - - -class PoEngine: - """Portfolio Optimization Engine""" - - def __init__( - self, - symbols_categories: Optional[Dict[str, Dict[str, str]]] = None, - symbols_file_path: Optional[str] = None, - parameters_file_path: Optional[str] = None, - ): - """Initialize the engine - - Parameters - ---------- - symbols_categories : Dict[str, float], optional - Categories, by default None - symbols_file_path : str, optional - Symbols file path, by default None - parameters_file_path : str, optional - Parameters file path, by default None - """ - - self._categories: Dict[str, Dict[str, str]] = {} - self._symbols: List[str] = [] - self._weights: Dict[str, float] = {} - self._returns: pd.DataFrame = None - self._params: Dict[str, float] = {} - self._current_model: str - - if symbols_categories is not None: - self._symbols, self._categories = PoEngine.__parse_dictionary( - symbols_categories - ) - - elif symbols_file_path is not None: - self._symbols, self._categories = excel_model.load_allocation( - symbols_file_path - ) - else: - raise ValueError("symbols or file_path must be provided") - - if parameters_file_path is not None: - self._params, self._current_model = params_view.load_file( - parameters_file_path - ) - - @staticmethod - def __parse_dictionary( - symbols_categories: Dict[str, Dict[str, str]] - ) -> Tuple[List[str], Dict[str, Dict[str, str]]]: - """Parse the categories dictionary - - Parameters - ---------- - symbols_categories : Dict[str, Dict[str, str]] - Categories - - Returns - ------- - Tuple[List[str], Dict[str, Dict[str, str]]] - Symbols and categories - """ - if isinstance(symbols_categories, dict): - symbols = PoEngine.__get_symbols_from_categories(symbols_categories) - else: - raise TypeError("'symbols_categories' must be a dictionary.") - - for symbol in symbols: - symbols_categories["CURRENCY"].setdefault(symbol, "USD") - symbols_categories["CURRENT_INVESTED_AMOUNT"].setdefault(symbol, "0") - - return symbols, symbols_categories - - @staticmethod - def __get_symbols_from_categories( - symbols_categories: Dict[str, Dict[str, str]] - ) -> List[str]: - """Get the symbols from the categories dictionary - - Parameters - ---------- - symbols_categories : Dict[str, Dict[str, str]], optional - Categories - - Returns - ------- - List[str] - List of symbols - """ - - try: - symbols = [] - for item in symbols_categories.items(): - _, values = item - for v in values: - symbols.append(v) - - return list(set(symbols)) - - except Exception: - console.print( - "Unsupported dictionary format. See `portfolio.po.load` examples for correct format." - ) - return [] - - def get_symbols(self): - return self._symbols - - def get_available_categories(self) -> List[str]: - """Get the available categories - - Returns - ------- - List[str] - Available categories - """ - available_categories = list(self._categories.keys()) - - if "CURRENT_INVESTED_AMOUNT" in available_categories: - available_categories.remove("CURRENT_INVESTED_AMOUNT") - - return available_categories - - def get_category(self, category: Optional[str] = None) -> Dict[str, str]: - """Get the category - - Parameters - ---------- - category : str, optional - Category, by default None - - Returns - ------- - Dict[str, str] - Category - """ - if category is None: - console.print("No category provided. Please provide a category.") - return {} - - d = self.get_categories_dict() - if category in d: - return d[category] - return {} - - def get_categories_dict(self) -> Dict[str, Dict[str, str]]: - """Get the categories - - Returns - ------- - Dict[str, Dict[str, str]] - Categories - """ - if not self._categories: - console.print("No categories found. Use 'load' to load a file.") - return {} - return self._categories - - def get_category_df(self, category: Optional[str] = None) -> pd.DataFrame: - """Get the category df - - Returns - ------- - pd.DataFrame - Category DataFrame - """ - if category is None: - console.print("No category provided. Please provide a category.") - return pd.DataFrame() - if not self._categories: - console.print("No categories found. Use 'load' to load a file.") - return pd.DataFrame() - return optimizer_model.get_categories( - weights=self._weights, categories=self._categories, column=category - ) - - def set_weights(self, weights: Dict[str, float]): - """Set the weights - - Parameters - ---------- - weights : Dict[str, float] - Weights - """ - self._weights = weights - - def get_weights(self, warning=True) -> Dict[str, float]: - """Get the weights - - Parameters - ---------- - warning : bool, optional - Display warning, by default True - - Returns - ------- - Dict[str, float] - Weights - """ - if not self._weights: - if warning: - console.print("No weights found. Please perform some optimization.") - return {} - return self._weights - - def get_weights_df(self, warning=True) -> pd.DataFrame: - """Get the weights - - Parameters - ---------- - warning : bool, optional - Display warning, by default True - - Returns - ------- - pd.DataFrame - Weights - """ - if not self._weights: - if warning: - console.print("No weights found. Please perform some optimization.") - return pd.DataFrame() - return optimizer_helper.dict_to_df(self._weights) - - def set_params(self, params: Dict[str, float], update=False): - """Set the parameters - - Parameters - ---------- - params : Dict[str, float] - Parameters - update : bool, optional - Update the current model, by default False - """ - if update: - self._params.update(params) - else: - self._params = params - - def get_params(self) -> Dict: - """Get the parameters - - Returns - ------- - Dict - Parameters - """ - return self._params - - def set_params_from_file(self, file_path: str): - """Set the parameters from a file - - Parameters - ---------- - file_path : str - File path - """ - self._params, self._current_model = params_view.load_file(file_path) - - def set_current_model(self, model: str): - """Set the current model - - Parameters - ---------- - model : str - Model - """ - self._current_model = model - - def get_current_model(self) -> str: - """Get the current model - - Returns - ------- - str - Current model - """ - return self._current_model - - def set_returns(self, returns: pd.DataFrame): - """Set the stock returns - - Parameters - ---------- - returns : pd.DataFrame - Stock returns - """ - self._returns = returns - - def get_returns(self) -> pd.DataFrame: - """Get the stock returns - - Returns - ------- - pd.DataFrame - Stock returns - """ - if self._returns.empty: - console.print("No returns found. Please perform some optimization.") - return pd.DataFrame() - return self._returns diff --git a/openbb_terminal/portfolio/portfolio_optimization/po_model.py b/openbb_terminal/portfolio/portfolio_optimization/po_model.py deleted file mode 100644 index c35f1b346fe..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/po_model.py +++ /dev/null @@ -1,2284 +0,0 @@ -"""Optimization Model""" - -__docformat__ = "numpy" - -# pylint: disable=R0913, C0302, E1101, line-too-long -# flake8: noqa: E501 - -import logging -import warnings -from typing import Dict, List, Optional, Tuple, Union - -import pandas as pd -from numpy import floating -from numpy.typing import NDArray -from riskfolio import rp - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.portfolio.portfolio_optimization import ( - optimizer_helper, - optimizer_model, -) -from openbb_terminal.portfolio.portfolio_optimization.parameters.params_helpers import ( - check_convert_parameters, -) -from openbb_terminal.portfolio.portfolio_optimization.po_engine import PoEngine -from openbb_terminal.portfolio.portfolio_optimization.statics import ( - DRAWDOWNS, - RISK_NAMES, - TERMINAL_TEMPLATE_MAP, - TIME_FACTOR, -) -from openbb_terminal.rich_config import console - -warnings.filterwarnings("ignore") - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def generate_portfolio( - symbols_categories: Optional[Dict[str, Dict[str, str]]] = None, - symbols_file_path: Optional[str] = None, - parameters_file_path: Optional[str] = None, -) -> Union[PoEngine, None]: - """Load portfolio optimization engine - - Parameters - ---------- - symbols_categories: Dict[str, Dict[str, str]] = None - Categories, by default None - symbols_file_path : str, optional - Symbols file full path, by default None - parameters_file_path : str, optional - Parameters file full path, by default None - - Returns - ------- - PoEngine - Portfolio optimization engine - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p) - """ - - if symbols_file_path: - return PoEngine( - symbols_file_path=symbols_file_path, - parameters_file_path=parameters_file_path, - ) - - if symbols_categories: - return PoEngine( - symbols_categories=symbols_categories, - parameters_file_path=parameters_file_path, - ) - - console.print("No 'symbols_file_path' or 'symbols_categories' provided.") - return None - - -@log_start_end(log=logger) -def load_parameters_file( - portfolio_engine: PoEngine, - parameters_file_path: str, -) -> Dict: - """Load portfolio optimization engine from file - - Parameters - ---------- - portfolio_engine : PoEngine - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - parameters_file_path : str - Parameters file full path, by default None - - Returns - ------- - Dict - Loaded parameters - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> p.get_params() - {} - >>> parameters = openbb.portfolio.po.file(portfolio_engine=p, parameters_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/optimization/defaults.ini") - Parameters: - interval : 3y - log_returns : 0 - freq : d - maxnan : 0.05 - threshold : 0.3 - alpha : 0.05 - >>> p.get_params() - {'interval': '3y', - 'log_returns': '0', - 'freq': 'd', - 'maxnan': '0.05', - 'threshold': '0.3', - 'alpha': '0.05'} - >>> p.set_params({"risk_free_rate": 0.05}, update=True) - >>> p.get_params() - {'interval': '3y', - 'log_returns': '0', - 'freq': 'd', - 'maxnan': '0.05', - 'threshold': '0.3', - 'alpha': '0.05', - 'risk_free_rate': 0.05} - >>> weights, performance = openbb.portfolio.po.maxsharpe(portfolio_engine=p) - """ - - portfolio_engine.set_params_from_file(parameters_file_path) - return portfolio_engine.get_params() - - -@log_start_end(log=logger) -def validate_inputs( - portfolio_engine=None, kwargs=None -) -> Tuple[List[str], PoEngine, dict]: - """Check valid inputs - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - kwargs : dict - Keyword arguments, by default None - - Returns - ------- - Tuple[List[str], PoEngine, dict] - List of symbols, Portfolio optimization engine, Keyword arguments - """ - - if portfolio_engine: - symbols = portfolio_engine.get_symbols() - parameters = portfolio_engine.get_params().copy() - parameters.update(kwargs) - else: - console.print("No 'portfolio_engine' provided.") - - converted_parameters = check_convert_parameters(received_parameters=parameters) - - terminal_parameters = {} - # TODO: Remove this conversion when mapping between template and terminal is not needed - TEMPLATE_TERMINAL_MAP = {v: k for k, v in TERMINAL_TEMPLATE_MAP.items()} - for key, value in converted_parameters.items(): - terminal_parameters[TEMPLATE_TERMINAL_MAP.get(key, key)] = value - - return symbols, portfolio_engine, terminal_parameters - - -@log_start_end(log=logger) -def get_portfolio_performance(weights: Dict, data: pd.DataFrame, **kwargs) -> Dict: - """Get portfolio performance - - Parameters - ---------- - weights : Dict - Portfolio weights - data : pd.DataFrame - Dataframe with returns - - Returns - ------- - Dict - Portfolio performance - """ - - try: - if not weights: - return {} - - freq = optimizer_helper.get_kwarg("freq", kwargs) - risk_measure = optimizer_helper.get_kwarg("risk_measure", kwargs) - risk_free_rate = optimizer_helper.get_kwarg("risk_free_rate", kwargs) - alpha = optimizer_helper.get_kwarg("alpha", kwargs) - a_sim = optimizer_helper.get_kwarg("a_sim", kwargs) - beta = optimizer_helper.get_kwarg("beta", kwargs) - b_sim = optimizer_helper.get_kwarg("b_sim", kwargs) - - freq = freq.upper() - weights = pd.Series(weights).to_frame() - returns = data @ weights - mu = returns.mean().item() * TIME_FACTOR[freq] - sigma = returns.std().item() * TIME_FACTOR[freq] ** 0.5 - sharpe = (mu - risk_free_rate) / sigma - - performance_dict = { - "Return": mu, - "Volatility": sigma, - "Sharpe ratio": sharpe, - } - - risk_measure = optimizer_helper.validate_risk_measure( - risk_measure, warning=False - ) - - if risk_measure != "MV": - risk = rp.Sharpe_Risk( - weights, - cov=data.cov(), - returns=data, - rm=risk_measure, - rf=risk_free_rate, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - ) - - if risk_measure in DRAWDOWNS: - sharpe_2 = (mu - risk_free_rate) / risk - else: - risk = risk * TIME_FACTOR[freq] ** 0.5 - sharpe_2 = (mu - risk_free_rate) / risk - - performance_dict[RISK_NAMES[risk_measure.lower()]] = risk - performance_dict.update({"Sharpe ratio (risk adjusted)": sharpe_2}) - except Exception as _: - console.print( - "[red]\nFailed to calculate portfolio performance indicators.[/red]" - ) - performance_dict = {} - return performance_dict - - -@log_start_end(log=logger) -def get_maxsharpe( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize Sharpe ratio weights - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - objective: str, optional - Objective function of the optimization model, by default 'Sharpe' - Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function, by default 1.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - target_return: float, optional - Constraint on minimum level of portfolio's return, by default -1.0 - target_risk: float, optional - Constraint on maximum level of portfolio's risk, by default -1.0 - mean: str, optional - The method used to estimate the expected returns, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.maxsharpe(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.maxsharpe(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_max_sharpe( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_minrisk( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize minimum risk weights - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - objective: str, optional - Objective function of the optimization model, by default 'Sharpe' - Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function, by default 1.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - target_return: float, optional - Constraint on minimum level of portfolio's return, by default -1.0 - target_risk: float, optional - Constraint on maximum level of portfolio's risk, by default -1.0 - mean: str, optional - The method used to estimate the expected returns, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.minrisk(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.minrisk(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_min_risk( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_maxutil( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize maximum utility weights - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - objective: str, optional - Objective function of the optimization model, by default 'Sharpe' - Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function, by default 1.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - target_return: float, optional - Constraint on minimum level of portfolio's return, by default -1.0 - target_risk: float, optional - Constraint on maximum level of portfolio's risk, by default -1.0 - mean: str, optional - The method used to estimate the expected returns, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.maxutil(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.maxutil(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_max_util( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_maxret( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize maximum return weights - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - objective: str, optional - Objective function of the optimization model, by default 'Sharpe' - Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function, by default 1.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - target_return: float, optional - Constraint on minimum level of portfolio's return, by default -1.0 - target_risk: float, optional - Constraint on maximum level of portfolio's risk, by default -1.0 - mean: str, optional - The method used to estimate the expected returns, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.maxret(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.maxret(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_max_ret( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_maxdiv( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize diversification weights - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.maxdiv(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.maxdiv(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_max_diversification_portfolio( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_maxdecorr( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize decorrelation weights - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.maxdecorr(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.maxdecorr(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_max_decorrelation_portfolio( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_blacklitterman( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize decorrelation weights - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - benchmark : Dict - Dict of portfolio weights, by default None - p_views: List - Matrix P of views that shows relationships among assets and returns, by default None - q_views: List - Matrix Q of expected returns of views, by default None - objective: str, optional - Objective function of the optimization model, by default 'Sharpe' - Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function, by default 1.0 - delta: float, optional - Risk aversion factor of Black Litterman model, by default None - equilibrium: bool, optional - If True excess returns are based on equilibrium market portfolio, if False - excess returns are calculated as historical returns minus risk free rate, by default True - optimize: bool, optional - If True Black Litterman estimates are used as inputs of mean variance model, - if False returns equilibrium weights from Black Litterman model, by default True - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.blacklitterman(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.blacklitterman(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_black_litterman_portfolio( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_ef(portfolio_engine: Optional[PoEngine] = None, **kwargs) -> Tuple[ - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - Optional[pd.DataFrame], - NDArray[floating], - NDArray[floating], - rp.Portfolio, -]: - """Get Efficient Frontier - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - n_portfolios: int, optional - Number of portfolios to simulate, by default 100 - seed: int, optional - Seed used to generate random portfolios, by default 123 - - Returns - ------- - Tuple[ - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - pd.DataFrame, - Optional[pd.DataFrame], - NDArray[floating], - NDArray[floating], - rp.Portfolio, - ] - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.ef(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> frontier = openbb.portfolio.po.ef(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - frontier, mu, cov, returns, weights, X1, Y1, port = optimizer_model.get_ef( - symbols=valid_symbols, **valid_kwargs - ) - valid_portfolio_engine.set_weights(weights=weights.to_dict()["weights"]) - valid_portfolio_engine.set_returns(returns=returns) - - return frontier, mu, cov, returns, weights, X1, Y1, port - - -@log_start_end(log=logger) -def get_riskparity( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize with Risk Parity using the risk budgeting approach - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - target_return: float, optional - Constraint on minimum level of portfolio's return, by default -1.0 - mean: str, optional - The method used to estimate the expected returns, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - risk_cont: List[str], optional - The vector of risk contribution per asset, by default 1/n (number of assets) - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.riskparity(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.riskparity(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_risk_parity_portfolio( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_relriskparity( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize with Relaxed Risk Parity using the least squares approach - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - target_return: float, optional - Constraint on minimum level of portfolio's return, by default -1.0 - mean: str, optional - The method used to estimate the expected returns, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - risk_cont: List[str], optional - The vector of risk contribution per asset, by default 1/n (number of assets) - version : str, optional - Relaxed risk parity model version, by default 'A' - Possible values are: - - - 'A': without regularization and penalization constraints. - - 'B': with regularization constraint but without penalization constraint. - - 'C': with regularization and penalization constraints. - - penal_factor: float, optional - The penalization factor of penalization constraints. Only used with - version 'C', by default 1.0 - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.relriskparity(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.relriskparity(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_rel_risk_parity_portfolio( - symbols=valid_symbols, **valid_kwargs - ) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_hrp( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize with Hierarchical Risk Parity - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - objective: str, optional - Objective function of the optimization model, by default 'MinRisk' - Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - If model is 'NCO', the risk measures available depends on the objective function. - Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function, by default 1.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses, by default 100 - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value, by default None - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value, by default None - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})} - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})} - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)} - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)} - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})} - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}} - - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic, by default None - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters, by default 10 - bins_info: str, optional - Number of bins used to calculate variation of information, by default 'KN'. - Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index, by default 0.05 - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal, by default True - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.hrp(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.hrp(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_hrp(symbols=valid_symbols, **valid_kwargs) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_herc( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize with Hierarchical Equal Risk Contribution (HERC) method. - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - objective: str, optional - Objective function of the optimization model, by default 'MinRisk' - Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - If model is 'NCO', the risk measures available depends on the objective function. - Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function, by default 1.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses, by default 100 - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value, by default None - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value, by default None - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})} - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})} - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)} - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)} - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})} - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}} - - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic, by default None - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters, by default 10 - bins_info: str, optional - Number of bins used to calculate variation of information, by default 'KN'. - Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index, by default 0.05 - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal, by default True - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.herc(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.herc(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_herc(symbols=valid_symbols, **valid_kwargs) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def get_nco( - portfolio_engine: Optional[PoEngine] = None, **kwargs -) -> Tuple[pd.DataFrame, Dict]: - """Optimize with Non-Convex Optimization (NCO) model. - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - objective: str, optional - Objective function of the optimization model, by default 'MinRisk' - Possible values are: - - - 'MinRisk': Minimize the selected risk measure. - - 'Utility': Maximize the risk averse utility function. - - 'Sharpe': Maximize the risk adjusted return ratio based on the selected risk measure. - - 'MaxRet': Maximize the expected return of the portfolio. - - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - If model is 'NCO', the risk measures available depends on the objective function. - Possible values are: - - - 'MV': Variance. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'VaR': Value at Risk. - - 'CVaR': Conditional Value at Risk. - - 'TG': Tail Gini. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization (Minimax). - - 'RG': Range of returns. - - 'CVRG': CVaR range of returns. - - 'TGRG': Tail Gini range of returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio). - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'DaR': Drawdown at Risk of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'MDD_Rel': Maximum Drawdown of compounded cumulative returns (Calmar Ratio). - - 'ADD_Rel': Average Drawdown of compounded cumulative returns. - - 'DaR_Rel': Drawdown at Risk of compounded cumulative returns. - - 'CDaR_Rel': Conditional Drawdown at Risk of compounded cumulative returns. - - 'EDaR_Rel': Entropic Drawdown at Risk of compounded cumulative returns. - - 'UCI_Rel': Ulcer Index of compounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - risk_aversion: float, optional - Risk aversion factor of the 'Utility' objective function, by default 1.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - a_sim: float, optional - Number of CVaRs used to approximate Tail Gini of losses, by default 100 - beta: float, optional - Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value, by default None - b_sim: float, optional - Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value, by default None - covariance: str, optional - The method used to estimate the covariance matrix, by default 'hist' - Possible values are: - - - 'hist': use historical estimates. - - 'ewma1': use ewma with adjust=True. For more information see `EWM `__. - - 'ewma2': use ewma with adjust=False. For more information see `EWM `__. - - 'ledoit': use the Ledoit and Wolf Shrinkage method. - - 'oas': use the Oracle Approximation Shrinkage method. - - 'shrunk': use the basic Shrunk Covariance method. - - 'gl': use the basic Graphical Lasso Covariance method. - - 'jlogo': use the j-LoGo Covariance method. For more information see: `a-jLogo`. - - 'fixed': denoise using fixed method. For more information see chapter 2 of `a-MLforAM`. - - 'spectral': denoise using spectral method. For more information see chapter 2 of `a-MLforAM`. - - 'shrink': denoise using shrink method. For more information see chapter 2 of `a-MLforAM`. - - d_ewma: float, optional - The smoothing factor of ewma methods, by default 0.94 - codependence: str, optional - The codependence or similarity matrix used to build the distance - metric and clusters. The default is 'pearson'. Possible values are: - - - 'pearson': pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{pearson}_{i,j})} - - 'spearman': spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{0.5(1-\\rho^{spearman}_{i,j})} - - 'abs_pearson': absolute value pearson correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{pearson}_{i,j}|)} - - 'abs_spearman': absolute value spearman correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-|\\rho^{spearman}_{i,j}|)} - - 'distance': distance correlation matrix. Distance formula: - .. math:: D_{i,j} = \\sqrt{(1-\\rho^{distance}_{i,j})} - - 'mutual_info': mutual information matrix. Distance used is variation information matrix. - - 'tail': lower tail dependence index matrix. Dissimilarity formula: - .. math:: D_{i,j} = -\\log{\\lambda_{i,j}} - - linkage: str, optional - Linkage method of hierarchical clustering. For more information see `linkage `__. - The default is 'single'. Possible values are: - - - 'single'. - - 'complete'. - - 'average'. - - 'weighted'. - - 'centroid'. - - 'median'. - - 'ward'. - - 'dbht': Direct Bubble Hierarchical Tree. - - k: int, optional - Number of clusters. This value is took instead of the optimal number - of clusters calculated with the two difference gap statistic, by default None - max_k: int, optional - Max number of clusters used by the two difference gap statistic - to find the optimal number of clusters, by default 10 - bins_info: str, optional - Number of bins used to calculate variation of information, by default 'KN'. - Possible values are: - - - 'KN': Knuth's choice method. For more information see `knuth_bin_width `__. - - 'FD': Freedman–Diaconis' choice method. For more information see `freedman_bin_width `__. - - 'SC': Scotts' choice method. For more information see `scott_bin_width `__. - - 'HGR': Hacine-Gharbi and Ravier' choice method. - - alpha_tail: float, optional - Significance level for lower tail dependence index, by default 0.05 - leaf_order: bool, optional - Indicates if the cluster are ordered so that the distance between - successive leaves is minimal, by default True - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Tuple with weights and performance dictionary - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.nco(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.nco(portfolio_engine=p) - """ - - valid_symbols, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine, kwargs - ) - if not valid_symbols: - return pd.DataFrame() - - weights, returns = optimizer_model.get_nco(symbols=valid_symbols, **valid_kwargs) - performance_dict = get_portfolio_performance(weights, returns, **valid_kwargs) - - valid_portfolio_engine.set_weights(weights=weights) - valid_portfolio_engine.set_returns(returns=returns) - - return valid_portfolio_engine.get_weights_df(warning=False), performance_dict - - -@log_start_end(log=logger) -def show( - portfolio_engine: PoEngine, - category: Optional[str] = None, -) -> Union[pd.DataFrame, pd.DataFrame]: - """Show portfolio optimization results - - Parameters - ---------- - portfolio_engine : PoEngine - Portfolio optimization engine - Use `portfolio.po.load` to load a portfolio engine - category : str, optional - Category to show, by default None - After loading a portfolio with `portfolio.po.load` you can use - the object method `get_available_categories()` to get a list of available categories. - You can also use the object method `set_categories_dict()` to set a custom dictionary - of categories. The dictionary must contain "CURRENT_INVESTED_AMOUNT" and "CURRENT_WEIGHTS" - as keys as shown in the example below. - - Returns - ------- - Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame]] - Portfolio weights and categories - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p) - >>> p.get_available_categories() - ['SECTOR', 'CURRENCY'] - >>> weights_df, category_df = openbb.portfolio.po.show(portfolio_engine=p, category="SECTOR") - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="~/openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p) - >>> p.get_available_categories() - ['ASSET_CLASS', - 'SECTOR', - 'INDUSTRY', - 'COUNTRY', - 'CURRENCY'] - >>> weights_df, category_df = openbb.portfolio.po.show(portfolio_engine=p, category="ASSET_CLASS") - """ - - weights = portfolio_engine.get_weights_df() - - if weights.empty: - return pd.DataFrame() - - available_categories = portfolio_engine.get_available_categories() - - if category is None: - print_categories_msg(available_categories) - return weights, pd.DataFrame() - - if category not in available_categories: - print_categories_msg(available_categories) - return weights, pd.DataFrame() - - category_df = portfolio_engine.get_category_df(category=category) - return weights, category_df - - -def print_categories_msg(available_categories: List[str]) -> None: - """Print categories message""" - - if not available_categories: - console.print("No categories found.") - else: - msg = ", ".join(available_categories) - console.print(f"Please specify a category from the following: {msg}.") diff --git a/openbb_terminal/portfolio/portfolio_optimization/po_view.py b/openbb_terminal/portfolio/portfolio_optimization/po_view.py deleted file mode 100644 index 04e2a4ee0a8..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/po_view.py +++ /dev/null @@ -1,720 +0,0 @@ -"""Optimization View""" - -__docformat__ = "numpy" - -# pylint: disable=R0913, R0914, C0302, too-many-branches, too-many-statements, line-too-long -# flake8: noqa: E501 - -# IMPORTS STANDARD -import logging -import math -import warnings -from typing import Optional - -# IMPORTATION THIRDPARTY -import matplotlib -import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import riskfolio as rp -from matplotlib.gridspec import GridSpec -from matplotlib.lines import Line2D - -from openbb_terminal.config_terminal import theme - -# IMPORTATION INTERNAL -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import plot_autoscale -from openbb_terminal.portfolio.portfolio_optimization.optimizer_helper import get_kwarg -from openbb_terminal.portfolio.portfolio_optimization.po_engine import PoEngine -from openbb_terminal.portfolio.portfolio_optimization.po_model import ( - get_ef, - validate_inputs, -) -from openbb_terminal.portfolio.portfolio_optimization.statics import ( - RISK_CHOICES, - TIME_FACTOR, -) -from openbb_terminal.rich_config import console - -warnings.filterwarnings("ignore") - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_ef(portfolio_engine: Optional[PoEngine] = None, **kwargs): - """Display efficient frontier - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - interval : str, optional - Interval to get data, by default '3y' - start_date : str, optional - If not using interval, start date string (YYYY-MM-DD), by default "" - end_date : str, optional - If not using interval, end date string (YYYY-MM-DD). If empty use last weekday, by default "" - log_returns : bool, optional - If True use log returns, else arithmetic returns, by default False - freq : str, optional - Frequency of returns, by default 'D'. Options: 'D' for daily, 'W' for weekly, 'M' for monthly - maxnan: float, optional - Maximum percentage of NaNs allowed in the data, by default 0.05 - threshold: float, optional - Value used to replace outliers that are higher than threshold, by default 0.0 - method: str, optional - Method used to fill nan values, by default 'time' - For more information see `interpolate `__. - value : float, optional - Amount to allocate to portfolio in long positions, by default 1.0 - value_short : float, optional - Amount to allocate to portfolio in short positions, by default 0.0 - risk_measure: str, optional - The risk measure used to optimize the portfolio, by default 'MV' - Possible values are: - - - 'MV': Standard Deviation. - - 'MAD': Mean Absolute Deviation. - - 'MSV': Semi Standard Deviation. - - 'FLPM': First Lower Partial Moment (Omega Ratio). - - 'SLPM': Second Lower Partial Moment (Sortino Ratio). - - 'CVaR': Conditional Value at Risk. - - 'EVaR': Entropic Value at Risk. - - 'WR': Worst Realization. - - 'ADD': Average Drawdown of uncompounded cumulative returns. - - 'UCI': Ulcer Index of uncompounded cumulative returns. - - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns. - - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns. - - 'MDD': Maximum Drawdown of uncompounded cumulative returns. - - risk_free_rate: float, optional - Risk free rate, annualized. Used for 'FLPM' and 'SLPM' and Sharpe objective function, by default 0.0 - alpha: float, optional - Significance level of VaR, CVaR, EDaR, DaR, CDaR, EDaR, Tail Gini of losses, by default 0.05 - n_portfolios: int, optional - Number of portfolios to simulate, by default 100 - seed: int, optional - Seed used to generate random portfolios, by default 123 - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> openbb.portfolio.po.ef_chart(portfolio_engine=p) - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> openbb.portfolio.po.ef_chart(portfolio_engine=p) - """ - - n_portfolios = get_kwarg("n_portfolios", kwargs) - freq = get_kwarg("freq", kwargs) - risk_measure = get_kwarg("risk_measure", kwargs) - risk_free_rate = get_kwarg("risk_free_rate", kwargs) - alpha = get_kwarg("alpha", kwargs) - - # Pop chart args - tangency = kwargs.pop("tangency", False) - plot_tickers = kwargs.pop("plot_tickers", False) - external_axes = kwargs.pop("external_axes", False) - - frontier, mu, cov, stock_returns, weights, X1, Y1, port = get_ef( - portfolio_engine, - **kwargs, - ) - - risk_free_rate = risk_free_rate / TIME_FACTOR[freq.upper()] - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - ax = rp.plot_frontier( - w_frontier=frontier, - mu=mu, - cov=cov, - returns=stock_returns, - rm=RISK_CHOICES[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - cmap="RdYlBu", - w=weights, - label="", - marker="*", - s=16, - c="r", - t_factor=TIME_FACTOR[freq.upper()], - ax=ax, - ) - - # Add risk free line - if tangency: - ret_sharpe = (mu @ weights).to_numpy().item() * TIME_FACTOR[freq.upper()] - risk_sharpe = rp.Sharpe_Risk( - weights, - cov=cov, - returns=stock_returns, - rm=RISK_CHOICES[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - # a_sim=a_sim, - # beta=beta, - # b_sim=b_sim, - ) - if RISK_CHOICES[risk_measure.lower()] not in [ - "ADD", - "MDD", - "CDaR", - "EDaR", - "UCI", - ]: - risk_sharpe = risk_sharpe * TIME_FACTOR[freq.upper()] ** 0.5 - - y = ret_sharpe * 1.5 - b = risk_free_rate * TIME_FACTOR[freq.upper()] - m = (ret_sharpe - b) / risk_sharpe - x2 = (y - b) / m - x = [0, x2] - y = [b, y] - line = Line2D(x, y, label="Capital Allocation Line") - ax.set_xlim(xmin=min(X1) * 0.8) - ax.add_line(line) - - ax.plot(X1, Y1, color="b") - - plot_tickers = True - if plot_tickers: - ticker_plot = pd.DataFrame(columns=["ticker", "var"]) - for ticker in port.cov.columns: - weight_df = pd.DataFrame({"weights": 1}, index=[ticker]) - risk = rp.Sharpe_Risk( - weight_df, - cov=port.cov[ticker][ticker], - returns=stock_returns.loc[:, [ticker]], - rm=RISK_CHOICES[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - ) - - if RISK_CHOICES[risk_measure.lower()] not in [ - "MDD", - "ADD", - "CDaR", - "EDaR", - "UCI", - ]: - risk = risk * TIME_FACTOR[freq.upper()] ** 0.5 - - ticker_plot = ticker_plot.append( - {"ticker": ticker, "var": risk}, ignore_index=True - ) - ticker_plot = ticker_plot.set_index("ticker") - ticker_plot = ticker_plot.merge( - port.mu.T * TIME_FACTOR[freq.upper()], right_index=True, left_index=True - ) - ticker_plot = ticker_plot.rename(columns={0: "ret"}) - ax.scatter(ticker_plot["var"], ticker_plot["ret"]) - for row in ticker_plot.iterrows(): - ax.annotate(row[0], (row[1]["var"], row[1]["ret"])) - ax.set_title(f"Efficient Frontier simulating {n_portfolios} portfolios") - ax.legend(loc="best", scatterpoints=1) - theme.style_primary_axis(ax) - L, b, w, h = ax.get_position().bounds - ax.set_position([L, b, w * 0.9, h]) - ax1 = ax.get_figure().axes - ll, bb, ww, hh = ax1[-1].get_position().bounds - ax1[-1].set_position([ll * 1.02, bb, ww, hh]) - - return theme.visualize_output(force_tight_layout=False, external_axes=external_axes) - - -@log_start_end(log=logger) -def display_plot( - portfolio_engine: Optional[PoEngine] = None, chart_type: str = "pie", **kwargs -): - """ - Display efficient frontier - - Parameters - ---------- - portfolio_engine : PoEngine, optional - Portfolio optimization engine, by default None - Use `portfolio.po.load` to load a portfolio engine - chart_type : str, optional - Chart type, by default "pie" - Options are "pie", "hist", "dd" or "rc" - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> d = { - "SECTOR": { - "AAPL": "INFORMATION TECHNOLOGY", - "MSFT": "INFORMATION TECHNOLOGY", - "AMZN": "CONSUMER DISCRETIONARY", - }, - "CURRENT_INVESTED_AMOUNT": { - "AAPL": "100000.0", - "MSFT": "200000.0", - "AMZN": "300000.0", - }, - "CURRENCY": { - "AAPL": "USD", - "MSFT": "USD", - "AMZN": "USD", - }, - } - >>> p = openbb.portfolio.po.load(symbols_categories=d) - >>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p) - >>> p.get_available_categories() - ['SECTOR', 'CURRENCY'] - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="pie") - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="hist") - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="dd") - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="rc") - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="heat") - - >>> from openbb_terminal.sdk import openbb - >>> p = openbb.portfolio.po.load(symbols_file_path="openbb_terminal/miscellaneous/portfolio_examples/allocation/60_40_Portfolio.xlsx") - >>> weights, performance = openbb.portfolio.po.equal(portfolio_engine=p) - >>> p.get_available_categories() - ['ASSET_CLASS', - 'SECTOR', - 'INDUSTRY', - 'COUNTRY', - 'CURRENCY'] - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="ASSET_CLASS", chart_type="pie") - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="SECTOR", chart_type="hist") - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="INDUSTRY", chart_type="dd") - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="COUNTRY", chart_type="rc") - >>> openbb.portfolio.po.plot(portfolio_engine=p, category="ASSET_CLASS", chart_type="heat") - """ - - if portfolio_engine is None: - console.print("No portfolio engine found.") - return - - available_categories = portfolio_engine.get_available_categories() - - if not available_categories: - console.print("No categories found.") - return - msg = ", ".join(available_categories) - if "category" not in kwargs: - console.print(f"Please specify a category from the following: {msg}") - return - if kwargs["category"] not in available_categories: - console.print(f"Please specify a category from the following: {msg}") - return - - category = kwargs["category"] - - _, valid_portfolio_engine, valid_kwargs = validate_inputs( - portfolio_engine=portfolio_engine, kwargs=kwargs - ) - - weights: pd.DataFrame = valid_portfolio_engine.get_weights_df() - if weights.empty: - return - - data: pd.DataFrame = valid_portfolio_engine.get_returns() - if data.empty: - return - - if category: - category_dict = valid_portfolio_engine.get_category(category) - category_df = pd.DataFrame.from_dict( - data=category_dict, orient="index", columns=["category"] - ) - weights = weights.join(category_df, how="inner") - weights.sort_index(inplace=True) - - # Calculating classes returns - classes = list(set(weights["category"])) - weights_classes = weights.groupby(["category"]).sum() - matrix_classes = np.zeros((len(weights), len(classes))) - labels = weights["category"].tolist() - - j_value = 0 - for i in classes: - matrix_classes[:, j_value] = np.array( - [1 if x == i else 0 for x in labels], dtype=float - ) - matrix_classes[:, j_value] = ( - matrix_classes[:, j_value] - * weights["value"] - / weights_classes.loc[i, "value"] - ) - j_value += 1 - - matrix_classes = pd.DataFrame( - matrix_classes, columns=classes, index=weights.index - ) - data = data @ matrix_classes - weights = weights_classes["value"].copy() - weights.replace(0, np.nan, inplace=True) - weights.dropna(inplace=True) - weights.sort_values(ascending=True, inplace=True) - data = data[weights.index.tolist()] - data.columns = [i.title() for i in data.columns] - weights.index = [i.title() for i in weights.index] - weights = weights.to_dict() - - valid_kwargs["weights"] = weights - valid_kwargs["data"] = data - valid_kwargs["colors"] = theme.get_colors() - - if chart_type == "pie": - display_pie(**valid_kwargs) - elif chart_type == "hist": - display_hist(**valid_kwargs) - elif chart_type == "dd": - display_dd(**valid_kwargs) - elif chart_type == "rc": - display_rc(**valid_kwargs) - elif chart_type == "heat": - display_heat(**valid_kwargs) - else: - console.print( - "Invalid chart type, please choose from the following: pie, hist, dd, rc, heat" - ) - - -@log_start_end(log=logger) -def display_heat(**kwargs): - weights = kwargs.get("weights", None) - data = kwargs.get("data", None) - category = kwargs.get("category", None) - title = kwargs.get("title", "") - external_axes = kwargs.get("external_axes", False) - - if len(weights) == 1: - single_key = list(weights.keys())[0].upper() - console.print( - f"[yellow]Heatmap needs at least two values for '{category}', only found '{single_key}'.[/yellow]" - ) - return None - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - number_of_clusters = len(weights) if len(weights) <= 3 else None - - ax = rp.plot_clusters( - returns=data, - codependence="pearson", - linkage="ward", - k=number_of_clusters, - max_k=10, - leaf_order=True, - dendrogram=True, - cmap="RdYlBu", - # linecolor='tab:purple', - ax=ax, - ) - - ax = ax.get_figure().axes - ax[0].grid(False) - ax[0].axis("off") - - if category is None: - # Vertical dendrogram - L, b, w, h = ax[4].get_position().bounds - l1 = L * 0.5 - w1 = w * 0.2 - b1 = h * 0.05 - ax[4].set_position([L - l1, b + b1, w * 0.8, h * 0.95]) - # Heatmap - L, b, w, h = ax[1].get_position().bounds - ax[1].set_position([L - l1 - w1, b + b1, w * 0.8, h * 0.95]) - w2 = w * 0.2 - # colorbar - L, b, w, h = ax[2].get_position().bounds - ax[2].set_position([L - l1 - w1 - w2, b, w, h]) - # Horizontal dendrogram - L, b, w, h = ax[3].get_position().bounds - ax[3].set_position([L - l1 - w1, b, w * 0.8, h]) - else: - # Vertical dendrogram - L, b, w, h = ax[4].get_position().bounds - l1 = L * 0.5 - w1 = w * 0.4 - b1 = h * 0.2 - ax[4].set_position([L - l1, b + b1, w * 0.6, h * 0.8]) - # Heatmap - L, b, w, h = ax[1].get_position().bounds - ax[1].set_position([L - l1 - w1, b + b1, w * 0.6, h * 0.8]) - w2 = w * 0.05 - # colorbar - L, b, w, h = ax[2].get_position().bounds - ax[2].set_position([L - l1 - w1 - w2, b, w, h]) - # Horizontal dendrogram - L, b, w, h = ax[3].get_position().bounds - ax[3].set_position([L - l1 - w1, b, w * 0.6, h]) - - title = "Portfolio - " + title + "\n" - title += ax[3].get_title(loc="left") - ax[3].set_title(title) - - return theme.visualize_output(force_tight_layout=True, external_axes=external_axes) - - -@log_start_end(log=logger) -def display_rc(**kwargs): - weights = kwargs.get("weights", None) - data = kwargs.get("data", None) - colors = kwargs.get("colors", None) - title = kwargs.get("title", "") - external_axes = kwargs.get("external_axes", False) - - risk_measure = get_kwarg("risk_measure", kwargs) - risk_free_rate = get_kwarg("risk_free_rate", kwargs) - alpha = get_kwarg("alpha", kwargs) - a_sim = get_kwarg("a_sim", kwargs) - beta = get_kwarg("beta", kwargs) - b_sim = get_kwarg("b_sim", kwargs) - freq = get_kwarg("freq", kwargs) - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - ax = rp.plot_risk_con( - w=pd.Series(weights).to_frame(), - cov=data.cov(), - returns=data, - rm=RISK_CHOICES[risk_measure.lower()], - rf=risk_free_rate, - alpha=alpha, - a_sim=a_sim, - beta=beta, - b_sim=b_sim, - color=colors[1], - t_factor=TIME_FACTOR[freq.upper()], - ax=ax, - ) - - # Changing colors - for i in ax.get_children()[:-1]: - if isinstance(i, matplotlib.patches.Rectangle): - i.set_width(i.get_width()) - i.set_color(colors[0]) - - title = "Portfolio - " + title + "\n" - title += ax.get_title(loc="left") - ax.set_title(title) - - return theme.visualize_output(force_tight_layout=True, external_axes=external_axes) - - -@log_start_end(log=logger) -def display_hist(**kwargs): - weights = kwargs.get("weights", None) - data = kwargs.get("data", None) - colors = kwargs.get("colors", None) - title = kwargs.get("title", "") - external_axes = kwargs.get("external_axes", False) - - alpha = kwargs.get("alpha", 0.05) - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - ax = rp.plot_hist(data, w=pd.Series(weights).to_frame(), alpha=alpha, ax=ax) - ax.legend(fontsize="x-small", loc="best") - - # Changing colors - for i in ax.get_children()[:-1]: - if isinstance(i, matplotlib.patches.Rectangle): - i.set_color(colors[0]) - i.set_alpha(0.7) - - k = 1 - for i, j in zip(ax.get_legend().get_lines()[::-1], ax.get_lines()[::-1]): - i.set_color(colors[k]) - j.set_color(colors[k]) - k += 1 - - title = "Portfolio - " + title + "\n" - title += ax.get_title(loc="left") - ax.set_title(title) - - return theme.visualize_output(force_tight_layout=True, external_axes=external_axes) - - -@log_start_end(log=logger) -def display_dd(**kwargs): - weights = kwargs.get("weights", None) - data = kwargs.get("data", None) - colors = kwargs.get("colors", None) - title = kwargs.get("title", "") - external_axes = kwargs.get("external_axes", False) - - alpha = get_kwarg("alpha", kwargs) - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - nav = data.cumsum() - ax = rp.plot_drawdown(nav=nav, w=pd.Series(weights).to_frame(), alpha=alpha, ax=ax) - - ax[0].remove() - ax = ax[1] - fig = ax.get_figure() - gs = GridSpec(1, 1, figure=fig) - ax.set_position(gs[0].get_position(fig)) - ax.set_subplotspec(gs[0]) - - # Changing colors - ax.get_lines()[0].set_color(colors[0]) - k = 1 - for i, j in zip(ax.get_legend().get_lines()[::-1], ax.get_lines()[1:][::-1]): - i.set_color(colors[k]) - j.set_color(colors[k]) - k += 1 - - ax.get_children()[1].set_facecolor(colors[0]) - ax.get_children()[1].set_alpha(0.7) - - title = "Portfolio - " + title + "\n" - title += ax.get_title(loc="left") - ax.set_title(title) - - return theme.visualize_output(force_tight_layout=True, external_axes=external_axes) - - -@log_start_end(log=logger) -def display_pie(**kwargs): - """Show a pie chart of holdings - - Parameters - ---------- - weights: dict - Weights to display, where keys are tickers, and values are either weights or values if -v specified - title: str - Title to be used on the plot title - external_axes:Optional[List[plt.Axes]] - Optional external axes to plot data on - """ - - weights = kwargs.get("weights", None) - colors = kwargs.get("colors", None) - title = kwargs.get("title", "") - external_axes = kwargs.get("external_axes", False) - - if not weights: - return None - - init_stocks = list(weights.keys()) - init_sizes = list(weights.values()) - symbols = [] - sizes = [] - for stock, size in zip(init_stocks, init_sizes): - if size > 0: - symbols.append(stock) - sizes.append(size) - - total_size = np.sum(sizes) - colors = theme.get_colors() - - _, ax = plt.subplots( - figsize=plot_autoscale(), dpi=get_current_user().preferences.PLOT_DPI - ) - - if math.isclose(sum(sizes), 1, rel_tol=0.1): - _, _, autotexts = ax.pie( - sizes, - labels=symbols, - autopct=my_autopct, - colors=colors, - textprops=dict(color="white"), - wedgeprops={"linewidth": 0.5, "edgecolor": "white"}, - labeldistance=1.05, - startangle=45, - normalize=True, - ) - plt.setp(autotexts, color="white", fontweight="bold") - else: - _, _, autotexts = ax.pie( - sizes, - labels=symbols, - autopct="", - colors=colors, - textprops=dict(color="white"), - wedgeprops={"linewidth": 0.5, "edgecolor": "white"}, - labeldistance=1.05, - startangle=45, - normalize=True, - ) - plt.setp(autotexts, color="white", fontweight="bold") - for i, a in enumerate(autotexts): - if sizes[i] / total_size > 0.05: - a.set_text(f"{sizes[i]:.2f}") - else: - a.set_text("") - - ax.axis("equal") - - # leg1 = ax.legend( - # wedges, - # [str(s) for s in stocks], - # title=" Ticker", - # loc="upper left", - # bbox_to_anchor=(0.80, 0, 0.5, 1), - # frameon=False, - # ) - # leg2 = ax.legend( - # wedges, - # [ - # f"{' ' if ((100*s/total_size) < 10) else ''}{100*s/total_size:.2f}%" - # for s in sizes - # ], - # title=" ", - # loc="upper left", - # handlelength=0, - # bbox_to_anchor=(0.91, 0, 0.5, 1), - # frameon=False, - # ) - # ax.add_artist(leg1) - # ax.add_artist(leg2) - - plt.setp(autotexts, size=8, weight="bold") - - title = "Portfolio - " + title + "\n" - title += "Portfolio Composition" - ax.set_title(title) - - return theme.visualize_output(force_tight_layout=True, external_axes=external_axes) - - -@log_start_end(log=logger) -def my_autopct(x): - """Function for autopct of plt.pie. This results in values not being printed in the pie if they are 'too small'""" - if x > 4: - return f"{x:.2f} %" - - return "" diff --git a/openbb_terminal/portfolio/portfolio_optimization/statics.py b/openbb_terminal/portfolio/portfolio_optimization/statics.py deleted file mode 100644 index fbfb212692e..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/statics.py +++ /dev/null @@ -1,550 +0,0 @@ -from typing import List, Optional - -from openbb_terminal.helper_funcs import get_rf -from openbb_terminal.portfolio.portfolio_optimization.parameters.Parameter import ( - Parameter, -) - -PERIOD_CHOICES = [ - "1d", - "5d", - "1mo", - "3mo", - "6mo", - "1y", - "2y", - "3y", - "5y", - "10y", - "ytd", - "max", -] - -MEAN_RISK_CHOICES = [ - "MV", - "MAD", - "MSV", - "FLPM", - "SLPM", - "CVaR", - "EVaR", - "WR", - "ADD", - "UCI", - "CDaR", - "EDaR", - "MDD", -] - -RISK_PARITY_CHOICES = [ - "MV", - "MAD", - "MSV", - "FLPM", - "SLPM", - "CVaR", - "EVaR", - "CDaR", - "EDaR", - "UCI", -] - -REL_RISK_PARITY_CHOICES = [ - "A", - "B", - "C", -] - -HCP_CHOICES = [ - "MV", - "MAD", - "GMD", - "MSV", - "VaR", - "CVaR", - "TG", - "EVaR", - "RG", - "CVRG", - "TGRG", - "WR", - "FLPM", - "SLPM", - "MDD", - "ADD", - "DaR", - "CDaR", - "EDaR", - "UCI", - "MDD_Rel", - "ADD_Rel", - "DaR_Rel", - "CDaR_Rel", - "EDaR_Rel", - "UCI_Rel", -] - -RISK_CHOICES = { - "mv": "MV", - "mad": "MAD", - "gmd": "GMD", - "msv": "MSV", - "var": "VaR", - "cvar": "CVaR", - "tg": "TG", - "evar": "EVaR", - "rg": "RG", - "cvrg": "CVRG", - "tgrg": "TGRG", - "wr": "WR", - "flpm": "FLPM", - "slpm": "SLPM", - "mdd": "MDD", - "add": "ADD", - "dar": "DaR", - "cdar": "CDaR", - "edar": "EDaR", - "uci": "UCI", - "mdd_rel": "MDD_Rel", - "add_rel": "ADD_Rel", - "dar_rel": "DaR_Rel", - "cdar_rel": "CDaR_Rel", - "edar_rel": "EDaR_Rel", - "uci_rel": "UCI_Rel", -} -MEAN_CHOICES = [ - "hist", - "ewma1", - "ewma2", -] - -CODEPENDENCE_CHOICES = [ - "pearson", - "spearman", - "abs_pearson", - "abs_spearman", - "distance", - "mutual_info", - "tail", -] - -COVARIANCE_CHOICES = [ - "hist", - "ewma1", - "ewma2", - "ledoit", - "oas", - "shrunk", - "gl", - "jlogo", - "fixed", - "spectral", - "shrink", -] - -OBJECTIVE_CHOICES = [ - "MinRisk", - "Utility", - "Sharpe", - "MaxRet", -] - -NCO_OBJECTIVE_CHOICES = [ - "MinRisk", - "Utility", - "Sharpe", - "ERC", -] - -LINKAGE_CHOICES = [ - "single", - "complete", - "average", - "weighted", - "centroid", - "median", - "ward", - "dbht", -] - -BINS_CHOICES = [ - "KN", - "FD", - "SC", - "HGR", -] - -FREQ_CHOICES = [ - "d", - "w", - "m", -] - -METHOD_CHOICES = [ - "linear", - "time", - "nearest", - "zero", - "slinear", - "quadratic", - "cubic", - "barycentric", -] - -TIME_FACTOR = { - "D": 252.0, - "W": 52.0, - "M": 12.0, -} - -RISK_NAMES = { - "mv": "volatility", - "mad": "mean absolute deviation", - "gmd": "gini mean difference", - "msv": "semi standard deviation", - "var": "value at risk (VaR)", - "cvar": "conditional value at risk (CVaR)", - "tg": "tail gini", - "evar": "entropic value at risk (EVaR)", - "rg": "range", - "cvrg": "CVaR range", - "tgrg": "tail gini range", - "wr": "worst realization", - "flpm": "first lower partial moment", - "slpm": "second lower partial moment", - "mdd": "maximum drawdown uncompounded", - "add": "average drawdown uncompounded", - "dar": "drawdown at risk (DaR) uncompounded", - "cdar": "conditional drawdown at risk (CDaR) uncompounded", - "edar": "entropic drawdown at risk (EDaR) uncompounded", - "uci": "ulcer index uncompounded", - "mdd_rel": "maximum drawdown compounded", - "add_rel": "average drawdown compounded", - "dar_rel": "drawdown at risk (DaR) compounded", - "cdar_rel": "conditional drawdown at risk (CDaR) compounded", - "edar_rel": "entropic drawdown at risk (EDaR) compounded", - "uci_rel": "ulcer index compounded", -} - - -DRAWDOWNS = [ - "MDD", - "ADD", - "DaR", - "CDaR", - "EDaR", - "UCI", - "MDD_Rel", - "ADD_Rel", - "DaR_Rel", - "CDaR_Rel", - "EDaR_Rel", - "UCI_Rel", -] - -NAN_FILL_METHOD_CHOICES = [ - "linear", - "time", - "nearest", - "zero", - "slinear", - "quadratic", - "cubic", - "barycentric", -] - -TECHNIQUE_CHOICES = [ - "maxsharpe", - "minrisk", - "maxutil", - "maxret", - "maxdiv", - "maxdecorr", - "ef", - "equal", - "mktcap", - "dividend", - "blacklitterman", - "riskparity", - "relriskparity", - "hrp", - "herc", - "nco", -] - - -# Parameter template files -OPTIMIZATION_PARAMETERS = { - "historic_period": Parameter( - name="historic_period", - type_=str, - default="3y", - choices=PERIOD_CHOICES, - ), - "start_period": Parameter( - name="start_period", - type_=str, - default="", - ), - "end_period": Parameter( - name="end_period", - type_=str, - default="", - ), - "log_returns": Parameter( - name="log_returns", - type_=bool, - default=False, - ), - "return_frequency": Parameter( - name="return_frequency", - type_=str, - default="d", - choices=FREQ_CHOICES, - ), - "max_nan": Parameter( - name="max_nan", - type_=float, - default=0.05, - ), - "threshold_value": Parameter( - name="threshold_value", - type_=float, - default=0.3, - ), - "nan_fill_method": Parameter( - name="nan_fill_method", - type_=str, - default="time", - choices=NAN_FILL_METHOD_CHOICES, - ), - "risk_free": Parameter( - name="risk_free", - type_=float, - default=get_rf(), - ), - "significance_level": Parameter( - name="significance_level", - type_=float, - default=0.05, - ), - "technique": Parameter( - name="technique", - type_=str, - default="maxsharpe", - choices=TECHNIQUE_CHOICES, - ), - "risk_measure": Parameter( - name="risk_measure", - type_=str, - default="MV", - choices=MEAN_RISK_CHOICES + RISK_PARITY_CHOICES + HCP_CHOICES, - ), - "target_return": Parameter( - name="target_return", - type_=float, - default=-1.0, - ), - "target_risk": Parameter( - name="target_risk", - type_=float, - default=-1.0, - ), - "expected_return": Parameter( - name="expected_return", - type_=str, - default="hist", - choices=MEAN_CHOICES, - ), - "covariance": Parameter( - name="covariance", - type_=str, - default="hist", - choices=COVARIANCE_CHOICES, - ), - "smoothing_factor_ewma": Parameter( - name="smoothing_factor_ewma", - type_=float, - default=0.94, - ), - "long_allocation": Parameter( - name="long_allocation", - type_=float, - default=1.0, - ), - "short_allocation": Parameter( - name="short_allocation", - type_=float, - default=0.0, - ), - "risk_aversion": Parameter( - name="risk_aversion", - type_=float, - default=1.0, - ), - "amount_portfolios": Parameter( - name="amount_portfolios", - type_=int, - default=100, - ), - "random_seed": Parameter( - name="random_seed", - type_=int, - default=123, - ), - "tangency": Parameter( - name="tangency", - type_=bool, - default=False, - ), - "risk_contribution": Parameter( - name="risk_contribution", - type_=Optional[List[str]], # type: ignore - default=None, - ), - "risk_parity_model": Parameter( - name="risk_parity_model", - type_=str, - default="A", - choices=REL_RISK_PARITY_CHOICES, - ), - "penal_factor": Parameter( - name="penal_factor", - type_=float, - default=1.0, - ), - "p_views": Parameter( - name="p_views", - type_=Optional[List[List[float]]], # type: ignore - default=None, - ), - "q_views": Parameter( - name="q_views", - type_=Optional[List[List[float]]], # type: ignore - default=None, - ), - "delta": Parameter( - name="delta", - type_=Optional[float], # type: ignore - default=None, - ), - "equilibrium": Parameter( - name="equilibrium", - type_=bool, - default=True, - ), - "optimize": Parameter( - name="optimize", - type_=bool, - default=True, - ), - "co_dependence": Parameter( - name="co_dependence", - type_=str, - default="pearson", - choices=CODEPENDENCE_CHOICES, - ), - "cvar_simulations_losses": Parameter( - name="cvar_simulations_losses", - type_=int, - default=100, - ), - "cvar_simulations_gains": Parameter( - name="cvar_simulations_gains", - type_=Optional[int], # type: ignore - default=None, - ), - "cvar_significance": Parameter( - name="cvar_significance", - type_=Optional[float], # type: ignore - default=None, - ), - "linkage": Parameter( - name="linkage", - type_=str, - default="single", - choices=LINKAGE_CHOICES, - ), - "amount_clusters": Parameter( - name="amount_clusters", - type_=Optional[int], # type: ignore - default=None, - ), - "max_clusters": Parameter( - name="max_clusters", - type_=int, - default=10, - ), - "amount_bins": Parameter( - name="amount_bins", - type_=str, - default="KN", - choices=BINS_CHOICES, - ), - "alpha_tail": Parameter( - name="alpha_tail", - type_=float, - default=0.05, - ), - "leaf_order": Parameter( - name="leaf_order", - type_=bool, - default=True, - ), - "objective": Parameter( - name="objective", - type_=str, - default="MinRisk", - choices=OBJECTIVE_CHOICES + NCO_OBJECTIVE_CHOICES, - ), -} - -# Model functions -# TODO: The dictionary below should not be needed, we can input defaults in the Parameter directly -# But since template variable names are different from functions argument variable names -# we need to map them before removing this dictionary. When we standardize the names, we can remove this. - -TERMINAL_TEMPLATE_MAP = { - "a_sim": "cvar_simulations_losses", - "alpha": "significance_level", - "alpha_tail": "alpha_tail", - "n_portfolios": "amount_portfolios", - "b_sim": "cvar_simulations_gains", - "beta": "cvar_significance", - "bins_info": "amount_bins", - "codependence": "co_dependence", - "covariance": "covariance", - "d_ewma": "smoothing_factor_ewma", - "delta": "delta", - "end_date": "end_period", - "equilibrium": "equilibrium", - "freq": "return_frequency", - "interval": "historic_period", - "k": "amount_clusters", - "leaf_order": "leaf_order", - "linkage": "linkage", - "log_returns": "log_returns", - "max_k": "max_clusters", - "maxnan": "max_nan", - "mean": "expected_return", - "method": "nan_fill_method", - "objective": "objective", - "optimize": "optimize", - "p_views": "p_views", - "penal_factor": "penal_factor", - "q_views": "q_views", - "seed": "random_seed", - "risk_aversion": "risk_aversion", - "risk_cont": "risk_contribution", - "risk_free_rate": "risk_free", - "risk_measure": "risk_measure", - "version": "risk_parity_model", - "start_date": "start_period", - "tangency": "tangency", - "target_return": "target_return", - "target_risk": "target_risk", - "technique": "technique", - "threshold": "threshold_value", - "value": "long_allocation", - "value_short": "short_allocation", -} diff --git a/openbb_terminal/portfolio/portfolio_optimization/yahoo_finance_model.py b/openbb_terminal/portfolio/portfolio_optimization/yahoo_finance_model.py deleted file mode 100644 index ea4a493358d..00000000000 --- a/openbb_terminal/portfolio/portfolio_optimization/yahoo_finance_model.py +++ /dev/null @@ -1,388 +0,0 @@ -"""YFinance Model""" - -__docformat__ = "numpy" - -import logging -import os -import tempfile -from calendar import monthrange -from datetime import date -from typing import List - -import numpy as np -import pandas as pd -import yfinance as yf -from dateutil.relativedelta import FR, relativedelta - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint: disable=R0912, E1101 - -fast_info_map = { - "previousClose": "previous_close", - "twoHundredDayAverage": "two_hundred_day_average", - "regularMarketDayHigh": "day_high", - "averageDailyVolume10Day": "ten_day_average_volume", - "regularMarketPreviousClose": "regular_market_previous_close", - "fiftyDayAverage": "fifty_day_average", - "open": "open", - "averageVolume10days": "ten_day_average_volume", - "regularMarketDayLow": "day_low", - "currency": "currency", - "marketCap": "market_cap", - "averageVolume": "three_month_average_volume", - "dayLow": "day_low", - "volume": "last_volume", - "fiftyTwoWeekHigh": "year_high", - "fiftyTwoWeekLow": "year_low", - "dayHigh": "day_high", - "regularMarketPrice": "last_price", -} - -yf_info_choices = [ - "previousClose", - "twoHundredDayAverage", - "trailingAnnualDividendYield", - "payoutRatio", - "volume24Hr", - "regularMarketDayHigh", - "navPrice", - "averageDailyVolume10Day", - "totalAssets", - "fiftyDayAverage", - "trailingAnnualDividendRate", - "open", - "toCurrency", - "averageVolume10days", - "expireDate", - "yield", - "algorithm", - "dividendRate", - "exDividendDate", - "beta", - "circulatingSupply", - "regularMarketDayLow", - "priceHint", - "currency", - "trailingPE", - "lastMarket", - "maxSupply", - "openInterest", - "volumeAllCurrencies", - "strikePrice", - "averageVolume", - "priceToSalesTrailing12Months", - "dayLow", - "ask", - "ytdReturn", - "askSize", - "volume", - "fiftyTwoWeekHigh", - "forwardPE", - "fromCurrency", - "fiveYearAvgDividendYield", - "fiftyTwoWeekLow", - "bid", - "dividendYield", - "bidSize", - "dayHigh", - "annualHoldingsTurnover", - "enterpriseToRevenue", - "beta3Year", - "profitMargins", - "enterpriseToEbitda", - "52WeekChange", - "morningStarRiskRating", - "forwardEps", - "revenueQuarterlyGrowth", - "sharesOutstanding", - "fundInceptionDate", - "annualReportExpenseRatio", - "bookValue", - "sharesShort", - "sharesPercentSharesOut", - "heldPercentInstitutions", - "netIncomeToCommon", - "trailingEps", - "lastDividendValue", - "SandP52WeekChange", - "priceToBook", - "heldPercentInsiders", - "shortRatio", - "sharesShortPreviousMonthDate", - "floatShares", - "enterpriseValue", - "fundFamily", - "threeYearAverageReturn", - "lastSplitFactor", - "legalType", - "lastDividendDate", - "morningStarOverallRating", - "earningsQuarterlyGrowth", - "pegRatio", - "lastCapGain", - "shortPercentOfFloat", - "sharesShortPriorMonth", - "impliedSharesOutstanding", - "fiveYearAverageReturn", - "regularMarketPrice", -] - - -@log_start_end(log=logger) -def process_stocks( - symbols: List[str], interval: str = "3mo", start_date: str = "", end_date: str = "" -) -> pd.DataFrame: - """Get adjusted closing price for each stock in the list - - Parameters - ---------- - symbols: List[str] - List of tickers to get historical data for - interval: str - interval to get data from yfinance, personalized - start_date: str - If not using interval, start date string (YYYY-MM-DD) - end_date: str - If not using interval, end date string (YYYY-MM-DD). If empty use last - weekday. - - Returns - ------- - stock_closes: DataFrame - DataFrame containing daily (adjusted) close prices for each stock in list - """ - - period_choices = [ - "1d", - "5d", - "1mo", - "3mo", - "6mo", - "1y", - "2y", - "5y", - "10y", - "ytd", - "max", - ] - - directory = "gst_temp_files" - parent_dir = tempfile.gettempdir() - path = os.path.join(parent_dir, directory) - - if os.path.isdir(path) is False: - os.mkdir(path) - - stock_closes = None - - if start_date != "": - end_ = date.today() if end_date == "" else date.fromisoformat(end_date) - - # Check if end date is on weekend - if end_.weekday() >= 5: - end_ = end_ + relativedelta(weekday=FR(-1)) - - end_date = end_.strftime("%Y-%m-%d") - - # Creating temporal file name - name = os.path.join(path, "Stocks " + start_date + " to " + end_date + ".pkl") - - # Checking if exist - if os.path.exists(name): - stock_closes_0 = pd.read_pickle(name) # noqa: S301 - list_of_stocks_0 = list(set(symbols) - set(stock_closes_0.columns)) - else: - stock_closes_0 = None - list_of_stocks_0 = symbols - - # Download assets that are not in temporal file - if list_of_stocks_0 == []: - stock_closes = stock_closes_0.copy() - else: - stock_prices = yf.download( - list_of_stocks_0, - start=start_date, - end=end_date, - progress=False, - group_by="ticker", - ) - - elif interval in period_choices: - # Setting temporal file name - name = os.path.join( - path, - "Stocks " + interval + " " + date.today().strftime("%Y-%m-%d") + ".pkl", - ) - - # Creating if exist - if os.path.exists(name): - stock_closes_0 = pd.read_pickle(name) # noqa: S301 - list_of_stocks_0 = list(set(symbols) - set(stock_closes_0.columns)) - else: - stock_closes_0 = None - list_of_stocks_0 = symbols - - # Download assets that are not in temporal file - if list_of_stocks_0 == []: - stock_closes = stock_closes_0.copy() - else: - stock_prices = yf.download( - list_of_stocks_0, period=interval, progress=False, group_by="ticker" - ) - - else: - end_ = date.today() - if end_.weekday() >= 5: - end_ = end_ + relativedelta(weekday=FR(-1)) - for item in ["d", "w", "mo", "y"]: - if interval.find(item) >= 1: - n = int(interval[: -len(item)]) - start_ = end_ - relativedelta(days=n) - break - else: - return None - - start_date = start_.strftime("%Y-%m-%d") - end_date = end_.strftime("%Y-%m-%d") - - # Creating temporal file name - name = os.path.join(path, "Stocks " + start_date + " to " + end_date + ".pkl") - - # Checking if temporal file exists - if os.path.exists(name): - stock_closes_0 = pd.read_pickle(name) # noqa: S301 - list_of_stocks_0 = list(set(symbols) - set(stock_closes_0.columns)) - else: - stock_closes_0 = None - list_of_stocks_0 = symbols - - # Download assets that are not in temporal file - if list_of_stocks_0 == []: - stock_closes = stock_closes_0.copy() - else: - stock_prices = yf.download( - list_of_stocks_0, - start=start_date, - end=end_date, - progress=False, - group_by="ticker", - ) - - if stock_closes is None: - if len(list_of_stocks_0) == 1: - stock_closes = stock_prices.loc[:, ["Adj Close"]] - stock_closes.columns = list_of_stocks_0 - else: - stock_closes = stock_prices.loc[:, (slice(None), "Adj Close")] - stock_closes.columns = stock_closes.columns.get_level_values(0) - - if list_of_stocks_0 != []: - stock_closes = pd.concat([stock_closes, stock_closes_0], axis=1) - stock_closes.to_pickle(name) - - stock_closes = stock_closes[symbols] - - return stock_closes - - -@log_start_end(log=logger) -def process_returns( - data: pd.DataFrame, - log_returns: bool = False, - freq: str = "D", - maxnan: float = 0.05, - threshold: float = 0, - method: str = "time", -) -> pd.DataFrame: - """Process stock prices to calculate returns and delete outliers - - Parameters - ---------- - data: pd.DataFrame - DataFrame of stock prices - log_returns: bool - If True calculate log returns, else arithmetic returns. Default value - is False - freq: str or int - The frequency used to calculate returns. Default value is 'D'. Possible - values are: - - 'D' for daily returns. - - 'W' for weekly returns. - - 'M' for monthly returns. - - maxnan: str or float - Max percentage of nan values accepted per asset to be included in - returns - threshold: str or float - Value used to replace outliers that are higher to threshold in daily returns. - method: str - Method used to fill nan values. Default value is 'time'. For more information see - `interpolate `_. - - Returns - ------- - stock_returns: DataFrame - DataFrame containing daily (adjusted) close prices for each stock in list - """ - - # Interpolate nan values - stock_returns = data.copy() - stock_returns = stock_returns.set_index(pd.DatetimeIndex(stock_returns.index)) - stock_returns.interpolate(method=method, axis=0, inplace=True) - - # Select stocks with low number of nans - selected_stocks = np.isnan(stock_returns).sum(axis=0) - selected_stocks = np.where(selected_stocks <= maxnan * stock_returns.shape[0])[0] - filtered_out = [ - s - for s in stock_returns.columns - if s not in stock_returns.iloc[:, selected_stocks] - ] - if filtered_out: - console.print( - "The following stocks were filtered out, due to too many NaNs: " - + ", ".join(filtered_out) - ) - stock_returns = stock_returns.iloc[:, selected_stocks] - - # Replace values above and below threshold - if threshold > 0: - stock_returns = stock_returns.pct_change() - stock_returns.mask(stock_returns > threshold, threshold, inplace=True) - stock_returns.mask(stock_returns < -threshold, -threshold, inplace=True) - - s = stock_returns.isna().idxmin().tolist() - j = 0 - for i in s: - stock_returns.iloc[stock_returns.index.get_loc(i) - 1, j] = 0 - j += 1 - - stock_returns = stock_returns + 1 - stock_returns = stock_returns.cumprod() - - # Change the frequency of the data - if freq.upper() == "D": - pass - elif freq.upper() in ["W", "M"]: - last_day = stock_returns.index[-1] - stock_returns = stock_returns.resample(freq).last() - if freq.upper() == ["W"] and last_day.weekday() < 4: - stock_returns = stock_returns.iloc[:-1, :] - if ( - freq.upper() == ["M"] - and monthrange(last_day.year, last_day.month)[1] - last_day.day <= 5 - ): - stock_returns = stock_returns.iloc[:-1, :] - - # Calculate returns - if log_returns is True: - stock_returns = np.log(stock_returns) - stock_returns = stock_returns.diff().dropna() - else: - stock_returns = stock_returns.pct_change().dropna() - - return stock_returns diff --git a/openbb_terminal/portfolio/portfolio_view.py b/openbb_terminal/portfolio/portfolio_view.py deleted file mode 100644 index 711455d6393..00000000000 --- a/openbb_terminal/portfolio/portfolio_view.py +++ /dev/null @@ -1,2035 +0,0 @@ -"""Portfolio View""" - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.common.quantitative_analysis import qa_view -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.portfolio.portfolio_model import ( - PortfolioEngine, - get_assets_allocation, - get_calmar_ratio, - get_common_sense_ratio, - get_countries_allocation, - get_daily_returns, - get_distribution_returns, - get_gaintopain_ratio, - get_holdings_percentage, - get_holdings_value, - get_information_ratio, - get_jensens_alpha, - get_kelly_criterion, - get_kurtosis, - get_maximum_drawdown, - get_maximum_drawdown_ratio, - get_monthly_returns, - get_payoff_ratio, - get_performance_vs_benchmark, - get_profit_factor, - get_r2_score, - get_regions_allocation, - get_rolling_beta, - get_rolling_sharpe, - get_rolling_sortino, - get_rolling_volatility, - get_sectors_allocation, - get_sharpe_ratio, - get_skewness, - get_sortino_ratio, - get_stats, - get_summary, - get_tail_ratio, - get_tracking_error, - get_transactions, - get_volatility, - get_yearly_returns, -) -from openbb_terminal.rich_config import console - -# pylint: disable=C0302,redefined-outer-name - -# from reportlab.lib.pagesizes import letter -# from reportlab.pdfgen import canvas -# from reportlab.lib.utils import ImageReader -# from openbb_terminal.portfolio import reportlab_helpers - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def load_info(): - """Print instructions to load a CSV - - Returns - ------- - text : str - Information on how to load a csv - """ - text = """ -In order to load a CSV do the following: - -1. Add headers to the first row, below is data for each column:\n -\t1. Identifier for the asset (such as a stock ticker) -\t2. Type of asset (stock, bond, option, crypto) -\t3. The volume of the asset transacted -\t4. The buy date in yyyy/mm/dd -\t5. The Price paid for the asset -\t6. Any fees paid during the transaction -\t7. A premium paid or received if this was an option -\t8. Whether the asset was bought (covered) or sold (shorted)\n -2. Place this file in openbb_terminal/portfolio/portfolios\n - """ - console.print(text) - - -@log_start_end(log=logger) -def display_transactions( - portfolio_engine: PortfolioEngine, - show_index=False, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display portfolio transactions - - Parameters - ---------- - portfolio_engine: PortfolioEngine - Instance of PortfolioEngine class - show_index: bool - Defaults to False. - limit: int - Number of rows to display - export : str - Export certain type of data - """ - - if portfolio_engine.empty: - logger.warning("No transactions file loaded.") - console.print("[red]No transactions file loaded.[/red]\n") - - else: - df = get_transactions(portfolio_engine) - print_rich_table( - df, - show_index=show_index, - title=f"Last {limit if limit < len(df) else len(df)} transactions", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "transactions", - df.set_index("Date"), - sheet_name, - ) - - -@log_start_end(log=logger) -def display_assets_allocation( - portfolio_engine: PortfolioEngine, - limit: int = 10, - tables: bool = False, - recalculate: bool = False, -): - """Display portfolio asset allocation compared to the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - Instance of PortfolioEngine class - limit: int - The amount of assets you wish to show, by default this is set to 10 - tables: bool - Whether to include separate asset allocation tables - recalculate: bool - Whether to recalculate the allocation - """ - - combined, portfolio_allocation, benchmark_allocation = get_assets_allocation( - portfolio_engine=portfolio_engine, - limit=limit, - tables=True, - recalculate=recalculate, - ) - - display_category( - category="assets", - df0=combined, - df1=portfolio_allocation, - df2=benchmark_allocation, - tables=tables, - limit=limit, - ) - - -@log_start_end(log=logger) -def display_sectors_allocation( - portfolio_engine: PortfolioEngine, - limit: int = 10, - tables: bool = False, - recalculate: bool = False, -): - """Display portfolio sector allocation compared to the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - Instance of PortfolioEngine class - limit: int - The amount of assets you wish to show, by default this is set to 10 - tables: bool - Whether to include separate asset allocation tables - recalculate: bool - Whether to recalculate the allocation - """ - - combined, portfolio_allocation, benchmark_allocation = get_sectors_allocation( - portfolio_engine=portfolio_engine, - limit=limit, - tables=True, - recalculate=recalculate, - ) - - display_category( - category="sectors", - df0=combined, - df1=portfolio_allocation, - df2=benchmark_allocation, - tables=tables, - limit=limit, - ) - - -@log_start_end(log=logger) -def display_countries_allocation( - portfolio_engine: PortfolioEngine, - limit: int = 10, - tables: bool = False, - recalculate: bool = False, -): - """Display portfolio country allocation compared to the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - Instance of PortfolioEngine class - limit: int - The amount of assets you wish to show, by default this is set to 10 - tables: bool - Whether to include separate asset allocation tables - recalculate: bool - Whether to recalculate the allocation - """ - - combined, portfolio_allocation, benchmark_allocation = get_countries_allocation( - portfolio_engine=portfolio_engine, - limit=limit, - tables=True, - recalculate=recalculate, - ) - - display_category( - category="countries", - df0=combined, - df1=portfolio_allocation, - df2=benchmark_allocation, - tables=tables, - limit=limit, - ) - - -@log_start_end(log=logger) -def display_regions_allocation( - portfolio_engine: PortfolioEngine, - limit: int = 10, - tables: bool = False, - recalculate: bool = False, -): - """Display portfolio region allocation compared to the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - Instance of PortfolioEngine class - limit: int - The amount of assets you wish to show, by default this is set to 10 - tables: bool - Whether to include separate asset allocation tables - recalculate: bool - Whether to recalculate the allocation - """ - - combined, portfolio_allocation, benchmark_allocation = get_regions_allocation( - portfolio_engine=portfolio_engine, - limit=limit, - tables=True, - recalculate=recalculate, - ) - - display_category( - category="regions", - df0=combined, - df1=portfolio_allocation, - df2=benchmark_allocation, - tables=tables, - limit=limit, - ) - - -def display_category(**kwargs): - """Display category tables - - Parameters - ---------- - **kwargs - """ - - category = kwargs["category"] - combined = kwargs["df0"] - portfolio_allocation = kwargs["df1"] - benchmark_allocation = kwargs["df2"] - tables = kwargs["tables"] - limit = kwargs["limit"] - - if benchmark_allocation.empty: - console.print(f"[red]Benchmark data for {category} is empty.[/red]") - return - - if portfolio_allocation.empty: - console.print(f"[red]Portfolio data for {category} is empty.[/red]") - return - - if tables: - print_rich_table( - combined.replace(0, "-"), - headers=list(combined.columns), - title=f"Portfolio vs. Benchmark - Top {len(combined) if len(combined) < limit else limit} " - f"{category.capitalize()} Allocation", - floatfmt=[".2f", ".2%", ".2%", ".2%"], - show_index=False, - ) - console.print("") - print_rich_table( - pd.DataFrame(portfolio_allocation), - headers=["Symbol", "Allocation"], - title=f"Portfolio - Top {len(portfolio_allocation) if len(portfolio_allocation) < limit else limit} " - f"{category.capitalize()} Allocation", - floatfmt=["", ".2%"], - show_index=False, - ) - console.print("") - print_rich_table( - pd.DataFrame(benchmark_allocation), - headers=["Symbol", "Allocation"], - title=f"Benchmark - Top {len(benchmark_allocation) if len(benchmark_allocation) < limit else limit} " - f"{category.capitalize()} Allocation", - floatfmt=["", ".2%"], - show_index=False, - ) - else: - print_rich_table( - combined.replace(0, "-"), - headers=list(combined.columns), - title=f"Portfolio vs. Benchmark - Top {len(combined) if len(combined) < limit else limit} " - f"{category.capitalize()} Allocation", - floatfmt=[".2f", ".2%", ".2%", ".2%"], - show_index=False, - ) - - -@log_start_end(log=logger) -def display_performance_vs_benchmark( - portfolio_engine: PortfolioEngine, - show_all_trades: bool = False, -): - """Display portfolio performance vs the benchmark - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - show_all_trades: bool - Whether to also show all trades made and their performance (default is False) - """ - - df = get_performance_vs_benchmark(portfolio_engine, show_all_trades) - - if show_all_trades: - print_rich_table( - df, - title="Portfolio vs. Benchmark - Individual Trades in period", - headers=list(df.columns), - show_index=False, - floatfmt=[".2f", ".2f", ".2f", ".2%", ".2f", ".2%", ".2%"], - ) - else: - print_rich_table( - df, - title="Portfolio vs. Benchmark", - headers=list(df.columns), - show_index=True, - ) - - -@log_start_end(log=logger) -def display_yearly_returns( - portfolio_engine: PortfolioEngine, - window: str = "all", - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display yearly returns - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - raw : False - Display raw data from cumulative return - export : str - Export certain type of data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure() - df = get_yearly_returns(portfolio_engine, window) - - if raw: - print_rich_table( - df, - title="Yearly Portfolio and Benchmark returns", - headers=["Portfolio [%]", "Benchmark [%]", "Difference [%]"], - show_index=True, - export=bool(export), - ) - - else: - fig = OpenBBFigure() - - creturns_year_idx = list() - breturns_year_idx = list() - - for year in df.index.values: - creturns_year_idx.append(datetime.strptime(f"{year}-04-15", "%Y-%m-%d")) - breturns_year_idx.append(datetime.strptime(f"{year}-08-15", "%Y-%m-%d")) - - fig.add_bar( - x=creturns_year_idx, - y=df["Portfolio"], - name="Portfolio", - ) - fig.add_bar( - x=breturns_year_idx, - y=df["Benchmark"], - name="Benchmark", - ) - - fig.update_layout( - title=f"Yearly Returns [%] in period {window}", - xaxis=dict(title="Year", type="date"), - yaxis=dict(title="Yearly Returns [%]"), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "yret", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_monthly_returns( - portfolio_engine: PortfolioEngine, - window: str = "all", - instrument: str = "both", - graph: bool = False, - show_vals: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Display monthly returns - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - instrument : str - Display raw data from cumulative return, default is showing both the portfolio and benchmark in one table - show_vals : False - Show values on heatmap - export : str - Export certain type of data - sheet_name : str - Whether to export to a specific sheet name in an Excel file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure() - - portfolio_returns, benchmark_returns, total_monthly_returns = get_monthly_returns( - portfolio_engine, window - ) - - if instrument == "portfolio": - print_rich_table( - portfolio_returns, - title="Monthly returns - portfolio [%]", - headers=portfolio_returns.columns, - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mret_portfolio", - portfolio_returns, - sheet_name, - ) - elif instrument == "benchmark": - print_rich_table( - benchmark_returns, - title="Monthly returns - benchmark [%]", - headers=benchmark_returns.columns, - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mret_benchmark", - benchmark_returns, - sheet_name, - ) - - elif instrument == "both": - # Converts multi-index into readable rich table while keeping the multi-index intact for export - total_monthly_returns_rich = total_monthly_returns.copy() - total_monthly_returns_rich.insert( - 0, "Instruments", total_monthly_returns_rich.index.get_level_values(1) - ) - total_monthly_returns_rich.insert( - 0, - "Year", - sum( - [ - [year, "", ""] - for year in total_monthly_returns_rich.index.get_level_values( - 0 - ).unique() - ], - [], - ), - ) - - print_rich_table( - total_monthly_returns_rich, - title="Total Monthly returns [%]", - headers=total_monthly_returns_rich.columns, - show_index=False, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mret_total", - total_monthly_returns, - sheet_name, - ) - - if graph or show_vals or fig.is_image_export(export): - fig = OpenBBFigure.create_subplots( - rows=2, - cols=1, - subplot_titles=("Portfolio", "Benchmark"), - specs=[[{"type": "heatmap"}], [{"type": "heatmap"}]], - vertical_spacing=0.15, - shared_xaxes=False, - row_width=[0.8, 0.8], - ) - fig.set_title(title=f"Monthly Returns [%] in period {window}") - - texttemplate = "%{z:.2f}%" if show_vals else "" - hovertemplate = "%{x} %{y:.0f}:
%{z:.2f}%" - - if isinstance(portfolio_returns, pd.Series): - portfolio_returns = portfolio_returns.to_frame() - if isinstance(benchmark_returns, pd.Series): - benchmark_returns = benchmark_returns.to_frame() - - pb_mins = (portfolio_returns.min().min(), benchmark_returns.min().min()) - pb_maxs = (portfolio_returns.max().max(), benchmark_returns.max().max()) - - try: - zero = 0 - float(min(pb_mins)) / (float(max(pb_maxs)) - float(min(pb_mins))) - except ZeroDivisionError: - zero = 2 if float(max(pb_maxs)) < 0 else -2 - - up_colorway = theme.up_colorway - down_colorway = theme.down_colorway - - colorscale = [ - [0.0, down_colorway[3]], - [zero, down_colorway[-3]], - [zero + 0.01, up_colorway[-2]], - [1.0, up_colorway[0]], - ] - if zero < 0: - colorscale = [ - [0.0, up_colorway[-1]], - [1.0, up_colorway[4]], - ] - elif zero > 1: - colorscale = [ - [0.0, down_colorway[4]], - [1.0, down_colorway[6]], - ] - - i = 0 - # mypy does not like enumerate on zip - for df, name in zip( - [portfolio_returns, benchmark_returns], ["Portfolio", "Benchmark"] - ): - fig.add_heatmap( - z=df, - x=df.columns, - y=df.index, - zmin=min(pb_mins), - zmax=max(pb_maxs), - zmid=zero, - name=name, - texttemplate=texttemplate, - hovertemplate=hovertemplate, - textfont=dict(color="white"), - text=df, - colorscale=colorscale, - colorbar=dict( - thickness=10, - thicknessmode="pixels", - x=1.1, - y=1, - xanchor="right", - yanchor="top", - xpad=10, - ), - row=i + 1, - col=1, - ) - i += 1 - - fig.update_layout( - margin=dict(l=0, r=60, t=0, b=0), - xaxis=dict(title="Month"), - yaxis=dict(title="Year", autorange="reversed"), - xaxis2=dict(title="Month"), - yaxis2=dict(title="Year", autorange="reversed"), - font=dict(size=12), - showlegend=False, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mret_heatmap", - sheet_name=sheet_name, - figure=fig, - ) - - return fig.show(external=external_axes) - - return None - - -@log_start_end(log=logger) -def display_daily_returns( - portfolio_engine: PortfolioEngine, - window: str = "all", - raw: bool = False, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display daily returns - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - raw : False - Display raw data from cumulative return - limit : int - Last daily returns to display - export : str - Export certain type of data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = get_daily_returns(portfolio_engine, window) - - clrs_portfolio = [ - theme.down_color if (x < 0) else theme.up_color for x in df["portfolio"] - ] - clrs_benchmark = [ - theme.down_color if (x < 0) else theme.up_color for x in df["benchmark"] - ] - - fig = OpenBBFigure(xaxis_title="Date", yaxis_title="Returns [%]") - fig.set_title(f"Daily Returns [%] in period {window}") - - fig.add_bar( - x=df.index, - y=df["portfolio"], - name="Portfolio", - marker_color=clrs_portfolio, - ) - - fig.add_bar( - x=df.index, - y=df["benchmark"], - name="Benchmark", - marker_color=clrs_benchmark, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dret", - df, - sheet_name, - fig, - ) - - if raw: - df = df.sort_index(ascending=False) - return print_rich_table( - df, - title="Portfolio and Benchmark daily returns", - headers=["Portfolio [%]", "Benchmark [%]"], - show_index=True, - export=bool(export), - limit=limit, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_distribution_returns( - portfolio_engine: PortfolioEngine, - window: str = "all", - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display daily returns - - Parameters - ---------- - portfolio_engine : PortfolioEngine - The engine for the portfolio - window: str - The window - raw : bool - Display raw data from cumulative return - export : str - Export certain type of data - sheet_name: Optional[str] - The name for the sheet - external_axes : bool - Whether to return the figure object or not, by default False - """ - - df = get_distribution_returns(portfolio_engine, window) - df_portfolio = df["portfolio"] - df_benchmark = df["benchmark"] - - stats = df.describe() - - fig = OpenBBFigure(xaxis_title="Returns [%]", yaxis_title="Density") - fig.set_title(f"Returns distribution in period {window}") - - fig.add_histogram( - x=df_portfolio, - name="Portfolio", - histnorm="probability density", - marker_color=theme.up_color, - ) - - fig.add_histogram( - x=df_benchmark, - name="Benchmark", - histnorm="probability density", - marker_color=theme.down_color, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "distr", - stats, - sheet_name, - fig, - ) - - if raw: - return print_rich_table( - stats, - title=f"Stats for Portfolio and Benchmark in period {window}", - show_index=True, - headers=["Portfolio", "Benchmark"], - export=bool(export), - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_holdings_value( - portfolio_engine: PortfolioEngine, - unstack: bool = False, - raw: bool = False, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display holdings of assets (absolute value) - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - unstack: bool - Individual assets over time - raw : bool - To display raw data - limit : int - Number of past market days to display holdings - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export plot - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - all_holdings = get_holdings_value(portfolio_engine) - - if not unstack and "Total Value" in all_holdings.columns: - all_holdings.drop(columns=["Total Value"], inplace=True) - - fig = OpenBBFigure(xaxis_title="Date", yaxis_title="Holdings ($)") - fig.set_title("Total Holdings (value)") - - for col in all_holdings.columns: - fig.add_scatter( - x=all_holdings.index, - y=all_holdings[col].values, - name=col, - stackgroup="one", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "holdv", - all_holdings, - sheet_name, - fig, - ) - - if raw: - all_holdings = all_holdings.sort_index(ascending=False) - return print_rich_table( - all_holdings, - title="Holdings of assets (absolute value)", - headers=all_holdings.columns, - show_index=True, - export=bool(export), - limit=limit, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_holdings_percentage( - portfolio_engine: PortfolioEngine, - unstack: bool = False, - raw: bool = False, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display holdings of assets (in percentage) - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - unstack: bool - Individual assets over time - raw : bool - To display raw data - limit : int - Number of past market days to display holdings - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export plot - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - all_holdings = get_holdings_percentage(portfolio_engine) - - if not unstack and "Total Value" in all_holdings.columns: - all_holdings.drop(columns=["Total Value"], inplace=True) - - fig = OpenBBFigure(xaxis_title="Date", yaxis_title="Holdings (%)") - fig.set_title("Total Holdings (percentage)") - - for col in all_holdings.columns: - fig.add_scatter( - x=all_holdings.index, - y=all_holdings[col].values, - name=col, - stackgroup="one", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "holdp", - all_holdings, - sheet_name, - fig, - ) - - if raw: - # No need to account for time since this is daily data - all_holdings.index = all_holdings.index.date - - all_holdings.columns = [f"{col} [%]" for col in all_holdings.columns] - - all_holdings = all_holdings.sort_index(ascending=False) - return print_rich_table( - all_holdings, - title="Holdings of assets (in percentage)", - headers=all_holdings.columns, - show_index=True, - export=bool(export), - limit=limit, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_rolling_volatility( - portfolio_engine: PortfolioEngine, - window: str = "1y", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display rolling volatility - - Parameters - ---------- - portfolio : PortfolioEngine - PortfolioEngine object - interval: str - interval for window to consider - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export to file - external_axes: bool - Whether to return the figure object or not, by default False - """ - - metric = "volatility" - df = get_rolling_volatility(portfolio_engine, window) - if df.empty: - return None - - df_portfolio = df["portfolio"] - df_benchmark = df["benchmark"] - - fig = OpenBBFigure(xaxis_title="Date", yaxis_title=f"{metric.title()}") - fig.set_title(f"Rolling {metric.title()} using {window} window") - - fig.add_scatter( - x=df_portfolio.index, - y=df_portfolio, - name="Portfolio", - marker_color=theme.up_color, - ) - - fig.add_scatter( - x=df_benchmark.index, - y=df_benchmark, - name="Benchmark", - marker_color=theme.down_color, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - metric, - df_portfolio.to_frame().join(df_benchmark), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_rolling_sharpe( - portfolio_engine: PortfolioEngine, - risk_free_rate: float = 0, - window: str = "1y", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display rolling sharpe - - Parameters - ---------- - portfolio : PortfolioEngine - PortfolioEngine object - risk_free_rate: float - Value to use for risk free rate in sharpe/other calculations - window: str - interval for window to consider - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export to file - external_axes: bool - Whether to return the figure object or not, by default False - """ - - metric = "sharpe" - df = get_rolling_sharpe(portfolio_engine, risk_free_rate, window) - if df.empty: - return None - - df_portfolio = df["portfolio"] - df_benchmark = df["benchmark"] - - fig = OpenBBFigure(xaxis_title="Date", yaxis_title=f"{metric.title()}") - fig.set_title(f"Rolling {metric.title()} using {window} window") - - fig.add_scatter(x=df_portfolio.index, y=df_portfolio, name="Portfolio") - - fig.add_scatter(x=df_benchmark.index, y=df_benchmark, name="Benchmark") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - metric, - df_portfolio.to_frame().join(df_benchmark), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_rolling_sortino( - portfolio_engine: PortfolioEngine, - risk_free_rate: float = 0, - window: str = "1y", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display rolling sortino - - Parameters - ---------- - portfolio : PortfolioEngine - PortfolioEngine object - risk_free_rate: float - Value to use for risk free rate in sharpe/other calculations - window: str - interval for window to consider - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export to file - external_axes: bool - Whether to return the figure object or not, by default False - """ - - metric = "sortino" - df = get_rolling_sortino(portfolio_engine, risk_free_rate, window) - if df.empty: - return None - - df_portfolio = df["portfolio"] - df_benchmark = df["benchmark"] - - fig = OpenBBFigure(xaxis_title="Date", yaxis_title=f"{metric.title()}") - fig.set_title(f"Rolling {metric.title()} using {window} window") - - fig.add_scatter(x=df_portfolio.index, y=df_portfolio, name="Portfolio") - fig.add_scatter(x=df_benchmark.index, y=df_benchmark, name="Benchmark") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - metric, - df_portfolio.to_frame().join(df_benchmark), - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_rolling_beta( - portfolio_engine: PortfolioEngine, - window: str = "1y", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display rolling beta - - Parameters - ---------- - portfolio : PortfolioEngine - PortfolioEngine object - window: str - interval for window to consider - Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export to file - external_axes: bool - Whether to return the figure object or not, by default False - """ - - rolling_beta = get_rolling_beta(portfolio_engine, window) - if rolling_beta.empty: - return None - - metric = "beta" - - fig = OpenBBFigure(xaxis_title="Date", yaxis_title=f"{metric.title()}") - fig.set_title(f"Rolling {metric.title()} using {window} window") - - fig.add_scatter(x=rolling_beta.index, y=rolling_beta, name="Portfolio") - fig.add_hline_legend(y=1, name="Benchmark", line=dict(dash="dash", color="red")) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - metric, - rolling_beta, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_maximum_drawdown( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display maximum drawdown curve - - Parameters - ---------- - portfolio : PortfolioEngine - PortfolioEngine object - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - holdings, drawdown = get_maximum_drawdown(portfolio_engine) - - fig = OpenBBFigure.create_subplots( - rows=2, - cols=1, - subplot_titles=["Holdings", "Portfolio Drawdown"], - vertical_spacing=0.1, - ) - fig.set_title("Maximum Drawdown") - - fig.add_scatter(x=holdings.index, y=holdings, name="Holdings", row=1, col=1) - fig.add_scatter( - x=holdings.index, - y=drawdown, - line_color=theme.get_colors()[0], - name="Portfolio Drawdown", - fill="tozeroy", - fillcolor="rgba(255, 217, 0, 0.5)", - row=2, - col=1, - ) - fig.add_hline(y=0, row=2, col=1, line_color="white", opacity=0.5) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "maxdd", - drawdown, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_rsquare( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display R-square - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - export : str - Export data format - """ - - df = get_r2_score(portfolio_engine).fillna("-") - - print_rich_table( - df, - title="R-Square Score between Portfolio and Benchmark", - headers=["R-Square Score"], - show_index=True, - floatfmt=".2%", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rsquare", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_skewness( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display skewness - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - export : str - Export data format - """ - - df = get_skewness(portfolio_engine).fillna("-") - - print_rich_table( - df, - title="Skewness for Portfolio and Benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "skew", - sheet_name, - ) - - -@log_start_end(log=logger) -def display_kurtosis( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display kurtosis - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - export : str - Export data format - """ - - df = get_kurtosis(portfolio_engine).fillna("-") - - print_rich_table( - df, - title="Kurtosis for Portfolio and Benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "kurt", - sheet_name, - ) - - -@log_start_end(log=logger) -def display_stats( - portfolio_engine: PortfolioEngine, - window: str = "all", - export: str = "", - sheet_name: Optional[str] = None, -): - """Display stats - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to consider. Choices are: mtd, qtd, ytd, 3m, 6m, 1y, 3y, 5y, 10y, all - export : str - Export data format - """ - - df = get_stats(portfolio_engine, window) - - print_rich_table( - df, - title=f"Stats for Portfolio and Benchmark in period {window}", - show_index=True, - floatfmt=".3f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "stats", - sheet_name, - ) - - -@log_start_end(log=logger) -def display_volatility( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display volatility for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - export : str - Export data format - """ - - df = get_volatility(portfolio_engine).fillna("-") - print_rich_table( - df, - title="Volatility for Portfolio and Benchmark", - show_index=True, - floatfmt=".2%", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_volatility", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_sharpe_ratio( - portfolio_engine: PortfolioEngine, - risk_free_rate: float = 0, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display sharpe ratio for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - risk_free_rate: float - Risk free rate value - export : str - Export data format - """ - - df = get_sharpe_ratio(portfolio_engine, risk_free_rate).fillna("-") - print_rich_table( - df, - title="Sharpe ratio for Portfolio and Benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_sharpe", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_sortino_ratio( - portfolio_engine: PortfolioEngine, - risk_free_rate: float = 0, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display sortino ratio for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - risk_free_rate: float - Risk free rate value - export : str - Export data format - """ - - df = get_sortino_ratio(portfolio_engine, risk_free_rate).fillna("-") - print_rich_table( - df, - title="Sortino ratio for Portfolio and Benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_sortino", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_maximum_drawdown_ratio( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display maximum drawdown for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - export : str - Export data format - """ - - df = get_maximum_drawdown_ratio(portfolio_engine).fillna("-") - print_rich_table( - df, - title="Maximum drawdown for Portfolio and Benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_maxdrawdown", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_gaintopain_ratio( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display gain-to-pain ratio for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine object with returns and benchmark loaded - export : str - Export data format - """ - - df = get_gaintopain_ratio(portfolio_engine).fillna("-") - print_rich_table( - df, - title="Gain-to-pain ratio for portfolio and benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_gaintopain_ratio", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_tracking_error( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display tracking error for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine object with returns and benchmark loaded - export : str - Export data format - """ - - df, _ = get_tracking_error(portfolio_engine) - df = df.fillna("-") - print_rich_table( - df, - title="Benchmark Tracking Error", - show_index=True, - floatfmt=".2%", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_tracking_error", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_information_ratio( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display information ratio for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine object with returns and benchmark loaded - export : str - Export data format - """ - - df = get_information_ratio(portfolio_engine).fillna("-") - print_rich_table( - df, - title="Information ratio for portfolio", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_information_ratio", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_tail_ratio( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display tail ratio for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine object with returns and benchmark loaded - window: str - interval for window to consider - export : str - Export data format - """ - - df, _, _ = get_tail_ratio(portfolio_engine) - df = df.fillna("-") - print_rich_table( - df, - title="Tail ratio for portfolio and benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_tail_ratio", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_common_sense_ratio( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display common sense ratio for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine object with returns and benchmark loaded - export : str - Export data format - """ - - df = get_common_sense_ratio(portfolio_engine).fillna("-") - print_rich_table( - df, - title="Common sense ratio for portfolio and benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_common_sense_ratio", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_jensens_alpha( - portfolio_engine: PortfolioEngine, - risk_free_rate: float = 0, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display jensens alpha for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine object with returns and benchmark loaded - risk_free_rate: float - Risk free rate - export : str - Export data format - """ - - df, _ = get_jensens_alpha(portfolio_engine, risk_free_rate) - df = df.fillna("-") - print_rich_table( - df, - title="Portfolio's jensen's alpha", - show_index=True, - floatfmt=".2%", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_jensens_alpha", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_calmar_ratio( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display calmar ratio for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine object with returns and benchmark loaded - export : str - Export data format - """ - - df, _ = get_calmar_ratio(portfolio_engine) - df = df.fillna("-") - print_rich_table( - df, - title="Calmar ratio for portfolio and benchmark", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_calmar_ratio", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_kelly_criterion( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display kelly criterion for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine object with trades and returns loaded - export : str - Export data format - """ - - df = get_kelly_criterion(portfolio_engine).fillna("-") - print_rich_table( - df, - title="Kelly criterion of the portfolio", - show_index=True, - floatfmt=".2%", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_kelly_criterion", - df, - sheet_name, - ) - - -def display_payoff_ratio( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display payoff ratio for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - export : str - Export data format - """ - - df = get_payoff_ratio(portfolio_engine).fillna("-") - print_rich_table( - df, - title="Portfolio's payoff ratio", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_payoff_ratio", - df, - sheet_name, - ) - - -def display_profit_factor( - portfolio_engine: PortfolioEngine, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display profit factor for multiple intervals - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - export : str - Export data format - """ - - df = get_profit_factor(portfolio_engine).fillna("-") - print_rich_table( - df, - title="Portfolio's profit factor", - show_index=True, - floatfmt=".2f", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metric_profit_factor", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_summary( - portfolio_engine: PortfolioEngine, - window: str = "all", - risk_free_rate: float = 0, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display summary portfolio and benchmark returns - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - window : str - interval to compare cumulative returns and benchmark - risk_free_rate : float - Risk free rate for calculations - export : str - Export certain type of data - """ - - summary = get_summary(portfolio_engine, window, risk_free_rate) - print_rich_table( - summary, - title=f"Summary of Portfolio vs Benchmark for {window} period", - show_index=True, - headers=summary.columns, - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "summary", - summary, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_var( - portfolio_engine: PortfolioEngine, - use_mean: bool = True, - adjusted_var: bool = False, - student_t: bool = False, - percentile: float = 99.9, -): - """Display portfolio VaR - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - use_mean: bool - if one should use the data mean return - adjusted_var: bool - if one should have VaR adjusted for skew and kurtosis (Cornish-Fisher-Expansion) - student_t: bool - If one should use the student-t distribution - percentile: float - var percentile (%) - """ - - qa_view.display_var( - data=portfolio_engine.portfolio_returns, - symbol="Portfolio", - use_mean=use_mean, - adjusted_var=adjusted_var, - student_t=student_t, - percentile=percentile, - portfolio=True, - ) - - -@log_start_end(log=logger) -def display_es( - portfolio_engine: PortfolioEngine, - use_mean: bool = True, - distribution: str = "normal", - percentile: float = 99.9, -): - """Display expected shortfall - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - use_mean: - if one should use the data mean return - distribution: str - choose distribution to use: logistic, laplace, normal - percentile: float - es percentile (%) - """ - - qa_view.display_es( - data=portfolio_engine.portfolio_returns, - symbol="Portfolio", - use_mean=use_mean, - distribution=distribution, - percentile=percentile, - portfolio=True, - ) - - -@log_start_end(log=logger) -def display_omega( - portfolio_engine: PortfolioEngine, - threshold_start: float = 0, - threshold_end: float = 1.5, -): - """Display omega ratio - - Parameters - ---------- - portfolio_engine: PortfolioEngine - PortfolioEngine class instance, this will hold transactions and perform calculations. - Use `portfolio.load` to create a PortfolioEngine. - threshold_start: float - annualized target return threshold start of plotted threshold range - threshold_end: float - annualized target return threshold end of plotted threshold range - """ - - qa_view.display_omega( - data=portfolio_engine.portfolio_returns, - threshold_start=threshold_start, - threshold_end=threshold_end, - ) - - -@log_start_end(log=logger) -def display_attribution_categorization( - display: pd.DataFrame, - time_period: str, - attrib_type: str, - plot_fields: list, - show_table: bool = False, - external_axes: bool = False, -): - """Display attribution for sector comparison to portfolio - - Parameters - ---------- - display: pd.DataFrame - Dataframe to display - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if show_table: - print_rich_table( - display, - title=f"{attrib_type}: Portfolio vs. Benchmark Attribution categorization {time_period}", - show_index=True, - floatfmt=".2f", - ) - - fig = OpenBBFigure().set_title(f"{attrib_type} By Sector") - colors = {plot_fields[0]: "#1f77b4", plot_fields[1]: "#ff7f0e"} - - for field in plot_fields: - # We add trailing spaces to the y-axis labels to prevent them from being - # cut off due to Fire Code font size limits - fig.add_bar( - x=display[field].values, - y=[f"{i} " for i in display.index], - name=field, - orientation="h", - marker_color=colors[field], - ) - fig.update_layout(yaxis=dict(nticks=20)) - fig.horizontal_legend(yanchor="top", y=1) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/portfolio/statics.py b/openbb_terminal/portfolio/statics.py deleted file mode 100644 index 502f11a3867..00000000000 --- a/openbb_terminal/portfolio/statics.py +++ /dev/null @@ -1,220 +0,0 @@ -"""Statics""" - -__docformat__ = "numpy" - -BENCHMARK_CHOICES = { - "SPY": "SPDR S&P 500 ETF Trust (SPY)", - "IVV": "iShares Core S&P 500 ETF (IVV)", - "VTI": "Vanguard Total Stock Market ETF (VTI)", - "VOO": "Vanguard S&P 500 ETF (VOO)", - "QQQ": "Invesco QQQ Trust (QQQ)", - "VTV": "Vanguard Value ETF (VTV)", - "VEA": "Vanguard FTSE Developed Markets ETF (VEA)", - "IEFA": "iShares Core MSCI EAFE ETF (IEFA)", - "AGG": "iShares Core U.S. Aggregate Bond ETF (AGG)", - "BND": "Vanguard Total Bond Market ETF (BND)", - "VWO": "Vanguard FTSE Emerging Markets ETF (VWO)", - "VUG": "Vanguard Growth ETF (VUG)", - "IEMG": "iShares Core MSCI Emerging Markets ETF (IEMG)", - "IJR": "iShares Core S&P Small-Cap ETF (IJR)", - "GLD": "SPDR Gold Shares (GLD)", - "IWF": "iShares Russell 1000 Growth ETF (IWF)", - "IJH": "iShares Core S&P Mid-Cap ETF (IJH)", - "VIG": "Vanguard Dividend Appreciation ETF (VIG)", - "IWM": "iShares Russell 2000 ETF (IWM)", - "IWD": "iShares Russell 1000 Value ETF (IWD)", - "VO": "Vanguard Mid-Cap ETF (VO)", - "EFA": "iShares MSCI EAFE ETF (EFA)", - "VXUS": "Vanguard Total International Stock ETF (VXUS)", - "VGT": "Vanguard Information Technology ETF (VGT)", - "VYM": "Vanguard High Dividend Yield Index ETF (VYM)", - "BNDX": "Vanguard Total International Bond ETF (BNDX)", - "VNQ": "Vanguard Real Estate ETF (VNQ)", - "VB": "Vanguard Small Cap ETF (VB)", - "XLK": "Technology Select Sector SPDR Fund (XLK)", - "ITOT": "iShares Core S&P Total U.S. Stock Market ETF (ITOT)", - "VCIT": "Vanguard Intermediate-Term Corporate Bond ETF (VCIT)", - "VCSH": "Vanguard Short-Term Corporate Bond ETF (VCSH)", - "XLE": "Energy Select Sector SPDR Fund (XLE)", - "XLV": "Health Care Select Sector SPDR Fund (XLV)", - "BSV": "Vanguard Short-Term Bond ETF (BSV)", - "XLF": "Financial Select Sector SPDR Fund (XLF)", - "SCHD": "Schwab US Dividend Equity ETF (SCHD)", - "RSP": "Invesco S&P 500® Equal Weight ETF (RSP)", - "LQD": "iShares iBoxx $ Investment Grade Corporate Bond ETF (LQD)", - "IVW": "iShares S&P 500 Growth ETF (IVW)", - "VEU": "Vanguard FTSE All-World ex-US Index Fund (VEU)", - "TIP": "iShares TIPS Bond ETF (TIP)", - "IAU": "iShares Gold Trust (IAU)", - "SCHX": "Schwab U.S. Large-Cap ETF (SCHX)", - "IXUS": "iShares Core MSCI Total International Stock ETF (IXUS)", - "IWR": "iShares Russell Midcap ETF (IWR)", - "IWB": "iShares Russell 1000 ETF (IWB)", - "DIA": "SPDR Dow Jones Industrial Average ETF Trust (DIA)", - "EEM": "iShares MSCI Emerging Markets ETF (EEM)", - "USMV": "iShares MSCI USA Min Vol Factor ETF (USMV)", - "SCHF": "Schwab International Equity ETF (SCHF)", - "IVE": "iShares S&P 500 Value ETF (IVE)", - "MUB": "iShares National Muni Bond ETF (MUB)", - "VV": "Vanguard Large Cap ETF (VV)", - "VBR": "Vanguard Small Cap Value ETF (VBR)", - "ESGU": "iShares ESG Aware MSCI USA ETF (ESGU)", - "VT": "Vanguard Total World Stock ETF (VT)", - "DGRO": "iShares Core Dividend Growth ETF (DGRO)", - "SHY": "iShares 1-3 Year Treasury Bond ETF (SHY)", - "DVY": "iShares Select Dividend ETF (DVY)", - "QUAL": "iShares MSCI USA Quality Factor ETF (QUAL)", - "SCHB": "Schwab U.S. Broad Market ETF (SCHB)", - "MBB": "iShares MBS ETF (MBB)", - "SDY": "SPDR S&P Dividend ETF (SDY)", - "IGSB": "iShares 1-5 Year Investment Grade Corporate Bond ETF (IGSB)", - "VTIP": "Vanguard Short-Term Inflation-Protected Securities ETF (VTIP)", - "JPST": "JPMorgan Ultra-Short Income ETF (JPST)", - "TLT": "iShares 20+ Year Treasury Bond ETF (TLT)", - "ACWI": "iShares MSCI ACWI ETF (ACWI)", - "MDY": "SPDR S&P Midcap 400 ETF Trust (MDY)", - "IUSB": "iShares Core Total USD Bond Market ETF (IUSB)", - "SHV": "iShares Short Treasury Bond ETF (SHV)", - "VGK": "Vanguard FTSE Europe ETF (VGK)", - "XLY": "Consumer Discretionary Select Sector SPDR Fund (XLY)", - "BIL": "SPDR Bloomberg 1-3 Month T-Bill ETF (BIL)", - "GOVT": "iShares U.S. Treasury Bond ETF (GOVT)", - "VHT": "Vanguard Health Care ETF (VHT)", - "VOE": "Vanguard Mid-Cap Value ETF (VOE)", - "XLP": "Consumer Staples Select Sector SPDR Fund (XLP)", - "SCHP": "Schwab U.S. TIPS ETF (SCHP)", - "IEF": "iShares 7-10 Year Treasury Bond ETF (IEF)", - "PFF": "iShares Preferred & Income Securities ETF (PFF)", - "XLU": "Utilities Select Sector SPDR Fund (XLU)", - "VTEB": "Vanguard Tax-Exempt Bond ETF (VTEB)", - "EFV": "iShares MSCI EAFE Value ETF (EFV)", - "SCHG": "Schwab U.S. Large-Cap Growth ETF (SCHG)", - "EMB": "iShares J.P. Morgan USD Emerging Markets Bond ETF (EMB)", - "DFAC": "Dimensional U.S. Core Equity 2 ETF (DFAC)", - "SCHA": "Schwab U.S. Small-Cap ETF (SCHA)", - "GDX": "VanEck Gold Miners ETF (GDX)", - "VMBS": "Vanguard Mortgage-Backed Securities ETF (VMBS)", - "TQQQ": "ProShares UltraPro QQQ (TQQQ)", - "VGSH": "Vanguard Short-Term Treasury ETF (VGSH)", - "HYG": "iShares iBoxx $ High Yield Corporate Bond ETF (HYG)", - "XLI": "Industrial Select Sector SPDR Fund (XLI)", - "IWS": "iShares Russell Mid-Cap Value ETF (IWS)", - "VXF": "Vanguard Extended Market ETF (VXF)", - "SPLG": "SPDR Portfolio S&P 500 ETF (SPLG)", - "SPYV": "SPDR Portfolio S&P 500 Value ETF (SPYV)", - "IWN": "iShares Russell 2000 Value ETF (IWN)", -} - -PERIODS = ["mtd", "qtd", "ytd", "3m", "6m", "1y", "3y", "5y", "10y", "all"] - -REGIONS = { - "Afghanistan": "Middle East", - "Anguilla": "North America", - "Argentina": "Latin America", - "Australia": "Asia", - "Austria": "Europe", - "Azerbaijan": "Europe", - "Bahamas": "North America", - "Bangladesh": "Asia", - "Barbados": "North America", - "Belgium": "Europe", - "Belize": "North America", - "Bermuda": "North America", - "Botswana": "Africa", - "Brazil": "Latin America", - "British Virgin Islands": "North America", - "Cambodia": "Asia", - "Canada": "North America", - "Cayman Islands": "North America", - "Chile": "Latin America", - "China": "Asia", - "Colombia": "Latin America", - "Costa Rica": "North America", - "Cyprus": "Europe", - "Czech Republic": "Europe", - "Denmark": "Europe", - "Dominican Republic": "North America", - "Egypt": "Middle East", - "Estonia": "Europe", - "Falkland Islands": "Latin America", - "Finland": "Europe", - "France": "Europe", - "French Guiana": "Europe", - "Gabon": "Africa", - "Georgia": "Europe", - "Germany": "Europe", - "Ghana": "Africa", - "Gibraltar": "Europe", - "Greece": "Europe", - "Greenland": "North America", - "Guernsey": "Europe", - "Hong Kong": "Asia", - "Hungary": "Europe", - "Iceland": "Europe", - "India": "Asia", - "Indonesia": "Asia", - "Ireland": "Europe", - "Isle of Man": "Europe", - "Israel": "Middle East", - "Italy": "Europe", - "Ivory Coast": "Africa", - "Japan": "Asia", - "Jersey": "Europe", - "Jordan": "Middle East", - "Kazakhstan": "Asia", - "Kyrgyzstan": "Asia", - "Latvia": "Europe", - "Liechtenstein": "Europe", - "Lithuania": "Europe", - "Luxembourg": "Europe", - "Macau": "Asia", - "Macedonia": "Europe", - "Malaysia": "Asia", - "Malta": "Europe", - "Mauritius": "Africa", - "Mexico": "Latin America", - "Monaco": "Europe", - "Mongolia": "Asia", - "Montenegro": "Europe", - "Morocco": "Africa", - "Mozambique": "Africa", - "Myanmar": "Asia", - "Namibia": "Africa", - "Netherlands": "Europe", - "Netherlands Antilles": "Europe", - "New Zealand": "Asia", - "Nigeria": "Africa", - "Norway": "Europe", - "Panama": "North America", - "Papua New Guinea": "Asia", - "Peru": "Latin America", - "Philippines": "Asia", - "Poland": "Europe", - "Portugal": "Europe", - "Qatar": "Middle East", - "Reunion": "Africa", - "Romania": "Europe", - "Russia": "Asia", - "Saudi Arabia": "Middle East", - "Senegal": "Africa", - "Singapore": "Asia", - "Slovakia": "Europe", - "Slovenia": "Europe", - "South Africa": "Africa", - "South Korea": "Asia", - "Spain": "Europe", - "Suriname": "Latin America", - "Sweden": "Europe", - "Switzerland": "Europe", - "Taiwan": "Asia", - "Tanzania": "Africa", - "Thailand": "Asia", - "Turkey": "Middle East", - "Ukraine": "Europe", - "United Arab Emirates": "Middle East", - "United Kingdom": "Europe", - "United States": "North America", - "Uruguay": "Latin America", - "Vietnam": "Asia", - "Zambia": "Africa", -} diff --git a/openbb_terminal/pyproject.toml b/openbb_terminal/pyproject.toml new file mode 100644 index 00000000000..9969037e81b --- /dev/null +++ b/openbb_terminal/pyproject.toml @@ -0,0 +1,102 @@ +[tool.poetry] +name = "openbb-cli" +version = "1.0.0" +description = "Investment Research for Everyone, Anywhere." +license = "MIT" +authors = ["OpenBB "] +packages = [{ include = "openbb_terminal" }] +include = ["openbb_terminal/.env"] +readme = "README.md" +homepage = "https://openbb.co" +repository = "https://github.com/OpenBB-finance/OpenBBTerminal" +documentation = "https://docs.openbb.co/sdk" + +[tool.poetry.scripts] +openbb = 'openbb_terminal.cli:main' + +[tool.poetry.dependencies] +python = "^3.8.1,<3.12" + +# OpenBB dependencies +openbb = "^4.1.6" +openbb-charting = "^2.0.2" + +# Terminal dependencies +prompt-toolkit = "^3.0.16" +rich = "^13" +python-dotenv = "^1.0.0" +python-i18n = "^0.3.9" + +[tool.poetry.group.dev.dependencies] +openbb-devtools = "^1.1.3" + +[build-system] +requires = ["setuptools<65.5.0", "poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.pydocstyle] +inherit = true +convention = "numpy" +match = '((?!test_).)*\.py' +add-ignore = "D401" + +[tool.flake8] +max-line-length = 122 +ignore = "S101,W503" + +[tool.ruff] +line-length = 122 +target-version = "py38" +# This is an introductory addition of ruff. We should look to adding: +# D: pydocstyle, PD: pandas-vet +# All options here: https://github.com/charliermarsh/ruff#supported-rules +select = [ + "E", + "W", + "F", + "Q", + "S", + "UP", + "I", + "PLC", + "PLE", + "PLR", + "PLW", + "SIM", + "T20", +] +# These ignores should be seen as temporary solutions to problems that will NEED fixed +ignore = ["PLR2004", "PLR0913", "PLR0915"] +exclude = [ + "^openbb_platform/platform/core/openbb_core/app/static/package/.*", + "^openbb_platform/openbb/package/.*", +] + +[tool.ruff.per-file-ignores] +"**/tests/*" = ["S101"] +"*init*.py" = ["F401"] +"website/*" = ["T201"] +"*integration/*" = ["S101"] + +[tool.ruff.flake8-import-conventions.aliases] +"matplotlib.pyplot" = "plt" +numpy = "np" +pandas = "pd" +seaborn = "sns" + +[tool.ruff.isort] +combine-as-imports = true +force-wrap-aliases = true + +[tool.ruff.pylint] +max-args = 8 +max-branches = 25 +max-returns = 9 +max-statements = 30 + +[tool.isort] +profile = "black" +line_length = 122 +skip_gitignore = true +combine_as_imports = true +src_paths = ["openbb_terminal"] diff --git a/openbb_terminal/reports/.ipynb_checkpoints/test-checkpoint.html b/openbb_terminal/reports/.ipynb_checkpoints/test-checkpoint.html deleted file mode 100644 index 4098468caa3..00000000000 --- a/openbb_terminal/reports/.ipynb_checkpoints/test-checkpoint.html +++ /dev/null @@ -1,36444 +0,0 @@ - - - - - - test - - - - - - - -
- OpenBB -
-

Analyst: Didier Rodrigues Lopes

-

Date : 09 May, 2022

-

Time : 00:55 BST

-
-

INVESTMENT RESEARCH REPORT: OSTK

-
-
-
- - - - - - -
- - - - -

KPIs:

✅ Average price ratings last 30 days (56.5) > Last closing price (34.95)

❌ Regression on the last 30 market days (-0.49 dollars/market day) is negative

🟡 RSI level is NORMAL. 30 < 41.47 < 70

❌ Last 10 insider trading. -0.75 M was sold in value

✅ Last FinBrain sentiment is BULLISH: 10

✅ Regression for next 20th market price (37.46) > Last closing price (34.95)

Opinion:



No comment.

- -
- - - - - - - 2022-05-09T00:56:41.851370 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -

Description

Overstock.com, Inc. operates as an online retailer in the United States. The company offers furniture, décor, area rug, bedding and bath, home improvement, outdoor, and kitchen and dining items. It provides its products and services through its internet websites comprising overstock.com, o.co, overstock.ca, and overstockgovernment.com. The company also offers businesses advertising products or services on its website; Market Partner, a service that allows its partners to sell their products through third party sites; products to international customers using third party logistics providers; and Supplier Oasis, a single integration point through its partners can manage their products, inventory, and sales channels, as well as obtain multi-channel fulfillment services through its distribution network. The company was formerly known as D2-Discounts Direct and changed its name to Overstock.com, Inc. in October 1999. Overstock.com, Inc. was founded in 1997 and is headquartered in Midvale, Utah.

- -

No comment.

- -
- - - - - - - 2022-05-09T00:56:43.469257 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-09T00:56:45.030963 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CategoryAnalystRatingPrice Target
date
2022-04-25DowngradeBofA SecuritiesBuy -> Neutral38.0
2021-11-23InitiatedGuggenheimNeutral111.0
2021-09-07ResumedBofA SecuritiesBuy89.0
2021-07-08InitiatedNeedhamBuy150.0
2021-05-19ResumedCredit SuisseOutperform105.0
- -

No comment.

- -

Estimates

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
202220232024
YEARLY ESTIMATES
Revenue2,4732,6702,978
Dividend0.080.02-
Dividend Yield (in %)0.23 %0.06 %-
EPS1.471.972.26
P/E Ratio23.7517.7315.50
- -
- -

Earnings

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Previous QuarterCurrent QuarterNext QuarterCurrent YearNext Year
QUARTER EARNINGS ESTIMATES
Dateending 03/31/22ending 06/30/22ending 09/30/22ending 12/31/22ending 12/31/23
No. of Analysts66666
Average Estimate0.233 USD0.370 USD0.398 USD1.472 USD1.972 USD
Year Ago0.560 USD0.730 USD0.540 USD2.190 USD1.472 USD
Publish Date4/28/20228/4/202211/10/2022--
- -
- -

Revenues

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Previous QuarterCurrent QuarterNext QuarterCurrent YearNext Year
QUARTER REVENUES ESTIMATES
Dateending 03/31/22ending 06/30/22ending 09/30/22ending 12/31/22ending 12/31/23
No. of Analysts66666
Average Estimate577 USD645 USD647 USD2,473 USD2,670 USD
Year Ago660 USD795 USD689 USD2,800 USD2,473 USD
Publish Date4/28/20228/4/202211/10/2022--
- -
- -

SEC filings

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeCategoryLink
Filing Date
05/04/202210-QQuarterly Reportshttps://www.marketwatch.com/investing/stock/ostk/financials/secfilings?docid=15785307
04/28/20228-KSpecial Eventshttps://www.marketwatch.com/investing/stock/ostk/financials/secfilings?docid=15760186
03/23/2022DEF 14AProxy Statementhttps://www.marketwatch.com/investing/stock/ostk/financials/secfilings?docid=15679466
03/09/20228-KSpecial Eventshttps://www.marketwatch.com/investing/stock/ostk/financials/secfilings?docid=15645760
03/04/2022PRE 14AProxy Statementhttps://www.marketwatch.com/investing/stock/ostk/financials/secfilings?docid=15635805
- -

No comment.

- -
- - - - - - - 2022-05-09T00:55:56.429440 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-09T00:56:40.772612 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-09T00:56:37.499148 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-09T00:56:39.308073 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -

No comment.

- -
- - - - - - - 2022-05-09T00:56:45.312551 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -

No comment.

- -

Last Activity

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Insider TradingRelationshipDateTransactionCost#SharesValue ($)#Shares TotalSEC Form 4
0Nickle Eric GlenChief Legal OfficerApr 11Sale40.031,00040,02612,979Apr 11 08:31 PM
1Lee AdrianneChief Financial OfficerApr 01Option Exercise0.0010,000118,854Apr 04 08:55 PM
2Dalton KristaChief Merchandising OfficerApr 01Sale43.608,036350,3707,292Apr 04 08:53 PM
3Nielsen David J.PresidentFeb 24Sale50.057,200360,32824,527Feb 25 05:01 PM
4ABRAHAM ALLISON HDirectorFeb 17Option Exercise0.003,333089,366Feb 18 06:07 PM
5Weight JoelChief Technology OfficerFeb 17Option Exercise0.006,66718,689Feb 18 06:04 PM
6TABACCO JOSEPH J JRDirectorFeb 17Option Exercise0.003,333070,866Feb 18 06:01 PM
7Tuohig MeghanChief People OfficerFeb 17Option Exercise0.006,667122,949Feb 18 06:02 PM
8JOHNSON JONATHAN E IIIChief Executive OfficerFeb 17Option Exercise0.0016,6672123,368Feb 18 05:58 PM
9Shapiro Robert JacobDirectorFeb 17Option Exercise0.003,33309,666Feb 18 05:51 PM
- -

No comment.

- -
- - - - - - - 2022-05-09T00:56:46.052925 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-09T00:56:47.682705 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -

Stocktwits

Watchlist count: 30998
-Last 17 sentiment messages:
Bullish 88.24%
Bearish 11.76%
- -

No comment.

- -
- - - - - - - 2022-05-09T00:56:48.256480 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-09T00:56:48.520908 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -

No comment.

- -
- - - - - - - 2022-05-09T00:56:49.603604 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-09T00:56:49.233337 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - \ No newline at end of file diff --git a/openbb_terminal/reports/README.md b/openbb_terminal/reports/README.md deleted file mode 100644 index 1e78654067b..00000000000 --- a/openbb_terminal/reports/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# REPORTS - -This menu has a different concept from remaining menus. It has 2 main goals: - -- Generate a personalised report. -- Allow the user to write notes on that notebook (report), as if it was his personal investment diary. - -## How to create your own personal report - -1. Copy one of the existing notebook reports as a template, e.g. [dark_pool.ipynb](/openbb_terminal/reports/dark_pool.ipynb). - -2. Rename that notebook with a name that reflects what you want this notebook to contain, even including your name. - -3. One of the notebook cells will have a `parameters` tag, that's where the papermill will parametrize the notebook. - Thus, your variables should be declared there. - -4. Remove the function calls that you don't care about to build your own report. - -5. When building your template notebook, there are 2 type of possible things you can do: - - Create a markdown cell, where you can add a title or a description. E.g. - - ```text - ## Analyst Targets - ``` - - - Create a code cell, where you should both: import a module, and call a function to display a chart or print data. E.g. - - ```python - from openbb_terminal.stocks.due_diligence import finviz_view - - finviz_view.analyst(ticker=ticker, export='') - ``` - -**Note**: In order to find the name of the function you want, you should either crawl through the codebase (and ask -help in discord) or look into our readthedocs (#TODO). - -## How to run the report - -1. In [config_terminal](/openbb_terminal/config_terminal.py) you may need to change your `PAPERMILL_NOTEBOOK_REPORT_PORT` - from `8888`. If the notebook generation fails, it is likely that the port selected is not the correct one. - -2. Start the terminal with `python terminal.py` - -3. Select `reports` context, and choose from the options selected - which are derived from the .ipynb jupyter notebook - files on the [reports](/openbb_terminal/reports/) folder. - -4. This should prompt you with a filled notebook with name: `__.ipynb`. E.g. `20210725_193517_dark_pool_AMC.ipynb` - -5. Due to the last cell of the notebook, the report is saved by default as `.html`. E.g. `20210725_193517_dark_pool_AMC.html` - - ```text - folder = "notebooks/reports/" - extension = ".ipynb" - !jupyter nbconvert {report_name} --to html --no-input - ``` - -6. You can now write your own personal notes on the notebook. By doing: - - Click on the notebook cell that you are interested in writing your notes - - On the toolbar click on "+" to add a new cell - - On the toolbar click on "Cell" -> "Cell Type" -> "Markdown" - - You're now ready to write your own notes - -7. Once you're happy with your report, you can either: - - Re-run the last cell, potentially changing output from `.html`: - - Or, on the toolbar click on "File" -> "Download as" -> Select your preferred option (e.g. HTML) diff --git a/openbb_terminal/reports/__init__.py b/openbb_terminal/reports/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/reports/data/crypto_tickers.csv b/openbb_terminal/reports/data/crypto_tickers.csv deleted file mode 100644 index 8e08688fb64..00000000000 --- a/openbb_terminal/reports/data/crypto_tickers.csv +++ /dev/null @@ -1,100 +0,0 @@ -Symbol -BTC -ETH -BNB -ADA -DOT -XRP -UNI -LTC -LINK -THETA -USDC -BCH -XLM -LUNA -WBTC -DOGE -VET -CRO -FIL -AAVE -TRX -MIOTA -ATOM -SOL -EOS -AVAX -XMR -BSV -KSM -FTT -XTZ -XEM -BTT -ALGO -NEO -CHZ -DAI -HT -HBAR -EGLD -SUSHI -SNX -DASH -TFUEL -LEO -GRT -DCR -MKR -ZIL -COMP -MATIC -NEAR -ENJ -RVN -BAT -NPXS -ZEC -CAKE -ONE -MANA -NEXO -UMA -ETC -BNT -CHSB -RUNE -YFI -HOT -RSR -ICX -CEL -ZRX -WAVES -FTM -REN -SC -REV -VGX -ONT -DGB -DENT -OKB -IOST -OMG -PAX -CELO -1INCH -NANO -NANO -QTUM -LRC -OCEAN -CRV -KNC -ZEN -MVL -BTG -LSK -XVG diff --git a/openbb_terminal/reports/data/etf_tickers.csv b/openbb_terminal/reports/data/etf_tickers.csv deleted file mode 100644 index 22de7024835..00000000000 --- a/openbb_terminal/reports/data/etf_tickers.csv +++ /dev/null @@ -1,101 +0,0 @@ -Symbol -SPY -IVV -VOO -VTI -QQQ -VTV -VEA -BND -AGG -IEFA -VUG -VWO -IJR -VIG -IJH -IEMG -IWF -GLD -IWM -IWD -VO -BNDX -VYM -VXUS -VCSH -EFA -VB -BSV -XLE -ITOT -VGT -VCIT -XLV -SCHD -XLK -LQD -VNQ -MUB -SHY -VEU -RSP -SCHX -USMV -XLF -IVW -TIP -BIL -DIA -IAU -IWB -IWR -GOVT -TLT -IXUS -SHV -SCHF -VV -JPST -IVE -VBR -IEF -DGRO -IGSB -VT -SDY -EEM -ESGU -DVY -VTEB -MBB -SCHB -VTIP -IUSB -VGSH -MDY -QUAL -VHT -XLU -XLP -ACWI -VOE -SCHP -DFAC -VMBS -XLY -PFF -EMB -SPLG -SCHG -JEPI -EFV -SCHA -VXF -HYG -BIV -VGK -STIP -IWS -SPYG -VGIT diff --git a/openbb_terminal/reports/data/stocks_tickers.csv b/openbb_terminal/reports/data/stocks_tickers.csv deleted file mode 100644 index 1d355c5a570..00000000000 --- a/openbb_terminal/reports/data/stocks_tickers.csv +++ /dev/null @@ -1,52 +0,0 @@ -Symbol -AAPL -ABBV -ABT -ACN -AMZN -ASML -AVGO -AZN -BABA -BAC -BMY -BRK-A -COP -COST -CRM -CSCO -CVX -DHR -DIS -GOOGL -HD -JNJ -JPM -KO -LLY -MA -MCD -META -MRK -MSFT -NEE -NVDA -NVO -NVS -ORCL -PEP -PFE -PG -TM -TMO -TMUS -TSLA -TSM -UNH -UPS -V -VZ -WFC -WMT -XOM -Summ diff --git a/openbb_terminal/reports/reports_controller.py b/openbb_terminal/reports/reports_controller.py deleted file mode 100644 index 1cc5f408ba4..00000000000 --- a/openbb_terminal/reports/reports_controller.py +++ /dev/null @@ -1,378 +0,0 @@ -"""Reports Controller Module.""" - -__docformat__ = "numpy" - -import argparse -import logging -import os - -# pylint: disable=R1732, R0912 -from typing import Any, Dict, List, Optional - -import requests - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import parse_and_split_input -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.reports import reports_model -from openbb_terminal.rich_config import MenuText, console - -# from openbb_terminal.terminal_helper import is_packaged_application - -logger = logging.getLogger(__name__) - - -class ReportController(BaseController): - """Report Controller class.""" - - CHOICES_COMMANDS: List[str] = [ - "crypto", - "economy", - "equity", - "etf", - "forecast", - "forex", - "portfolio", - "run", - "load", - "exe", - ] - PATH = "/reports/" - - REPORTS: List[str] = reports_model.get_reports_available() - REPORTS = sorted(list(set(CHOICES_COMMANDS).intersection(REPORTS))) - PARAMETERS_DICT: Dict[str, Any] = {} - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - - super().__init__(queue) - self.update_choices() - - def update_choices(self): - """Update controller choices with reports available under templates folder.""" - - for report_name in self.REPORTS: - # Extract report parameters do display on menu - self.PARAMETERS_DICT[report_name] = reports_model.extract_parameters( - str(reports_model.REPORTS_FOLDER / report_name) - ) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - self.choices: dict = {c: {} for c in self.controller_choices} # type: ignore - self.choices["run"] = { - "--file": {c: None for c in reports_model.USER_REPORTS}, - "-f": "--file", - "--parameters": {}, - "-p": "--parameters", - } - for report_name in self.REPORTS: - # Completer with limited user choices to avoid unforeseen problems - self.choices[report_name] = {} - for arg in self.PARAMETERS_DICT[report_name]: - if report_name in reports_model.REPORT_CHOICES: - self.choices[report_name]["--" + arg] = ( - reports_model.REPORT_CHOICES[report_name]["--" + arg] - ) - - self.choices["support"] = self.SUPPORT_CHOICES - self.choices["about"] = self.ABOUT_CHOICES - - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def parse_input(self, an_input: str) -> List: - """Overwrite the BaseController parse_input for `askobb` and 'exe' - - This will allow us to search for something like "P/E" ratio - """ - raw_url = r"(exe (--url |-u )?(https?://)?raw\.githubusercontent\.(com)/.*)" - github_url = r"(exe (--url |-u )?(https?://)?github\.(com)/.*)" - custom_filters = [raw_url, github_url] - return parse_and_split_input(an_input=an_input, custom_filters=custom_filters) - - def print_help(self): - """Print help.""" - - self.REPORTS = reports_model.get_reports_available() - self.REPORTS = sorted( - list(set(self.CHOICES_COMMANDS).intersection(self.REPORTS)) - ) - self.update_choices() - - mt = MenuText("reports/") - mt.add_raw("\n") - mt.add_info("_OpenBB_reports_") - MAX_LEN_NAME = max(len(name) for name in self.REPORTS) + 2 - templates_string = "" - for report_name in self.REPORTS: - parameters_names = list(self.PARAMETERS_DICT[report_name].keys()) - - if len(parameters_names) > 1 or not parameters_names: - args = f"<{'> <'.join(parameters_names)}>" - else: - args = f"<{parameters_names[0]}>" - - templates_string += ( - f" [cmds]{report_name}[/cmds]" - + f"{(MAX_LEN_NAME-len(report_name))*' '} " - + f"[param]{args if args != '<>' else ''}[/param]\n" - ) - mt.add_raw(f"{templates_string}") - mt.add_raw("\n") - mt.add_info("_Custom_reports_") - mt.add_cmd("run") - mt.add_cmd("exe") - console.print(text=mt.menu_text, menu="Reports") - - @log_start_end(log=logger) - def call_etf(self, other_args: List[str]): - self.run_report("etf", other_args) - - @log_start_end(log=logger) - def call_forex(self, other_args: List[str]): - self.run_report("forex", other_args) - - @log_start_end(log=logger) - def call_portfolio(self, other_args: List[str]): - self.run_report("portfolio", other_args) - - @log_start_end(log=logger) - def call_economy(self, other_args: List[str]): - self.run_report("economy", other_args) - - @log_start_end(log=logger) - def call_equity(self, other_args: List[str]): - self.run_report("equity", other_args) - - @log_start_end(log=logger) - def call_crypto(self, other_args: List[str]): - self.run_report("crypto", other_args) - - @log_start_end(log=logger) - def call_forecast(self, other_args: List[str]): - try: - # noqa: F401, E501 # pylint: disable=C0415,W0611 - import darts # noqa # pyright: reportMissingImports=false - - FORECASTING_TOOLKIT_ENABLED = True - except ImportError: - FORECASTING_TOOLKIT_ENABLED = False - console.print( - "[yellow]" - "Forecasting Toolkit is disabled. " - "To use the Forecasting features please install the toolkit following the " - "instructions here: https://my.openbb.co/app/sdk/installation" - "\n" - "[/yellow]" - ) - if FORECASTING_TOOLKIT_ENABLED: - self.run_report("forecast", other_args) - - @log_start_end(log=logger) - def run_report(self, report_name: str, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog=report_name, - description=f"Run {report_name} report.", - ) - - if report_name in self.PARAMETERS_DICT: - # Assign respective parameters as arguments - for arg_name, arg_default in self.PARAMETERS_DICT[report_name].items(): - choices = reports_model.get_arg_choices(report_name, arg_name) - - getattr(parser, "add_argument")( - "--" + arg_name, - type=str, - default=arg_default, - choices=choices, - dest=arg_name, - help=arg_name, - ) - - if ( - other_args - and "-" not in other_args[0][0] - and len(self.PARAMETERS_DICT[report_name]) == 1 - ): - other_args.insert( - 0, "--" + list(self.PARAMETERS_DICT[report_name].keys())[0] - ) - ns_parser = self.parse_simple_args(parser, other_args) - - if ns_parser: - if report_name == "equity" and "." in ns_parser.symbol: - console.print( - "[red]Cannot currently handle tickers with a '.' in them[/red]\n" - ) - return - parameters = vars(ns_parser) - parameters.pop("help") - reports_model.render_report( - input_path=str(reports_model.REPORTS_FOLDER / report_name), - args_dict=parameters, - ) - - else: - console.print("[red]Notebook not found![/red]\n") - - @log_start_end(log=logger) - def call_run(self, other_args: List[str]): - """Process run command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="run", - description="Run a notebook from this folder: '" - f"{str(get_current_user().preferences.USER_CUSTOM_REPORTS_DIRECTORY)}'.", - ) - parser.add_argument( - "-f", - "--file", - dest="file", - choices=reports_model.USER_REPORTS, - required="-h" not in other_args, - help="The file to be loaded", - ) - parser.add_argument( - "-p", - "--parameters", - nargs="+", - dest="parameters", - help="Report parameters with format 'name:value'.", - ) - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - # Validate parameter inputs - parameters_dict = {} - if ns_parser.parameters: - for p in ns_parser.parameters: - if ":" in p: - item = p.split(":") - if item[1]: - parameters_dict[item[0]] = item[1] - else: - console.print( - f"[red]Bad format '{p}': empty value.[/red]\nExecuting with defaults.\n" - ) - else: - console.print( - f"[red]Bad format '{p}': use format 'name:value'.[/red]\nExecuting with defaults.\n" - ) - - if ns_parser.file: - complete_file_path = str( - get_current_user().preferences.USER_CUSTOM_REPORTS_DIRECTORY - / ns_parser.file - ) - if os.path.exists(complete_file_path): - reports_model.render_report( - input_path=complete_file_path, args_dict=parameters_dict - ) - else: - console.print( - f"[red]Notebook '{ns_parser.file}' not found![/red]\n" - ) - - def call_exe(self, other_args: List[str]): - """Process exe command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="exe", - description="Run a notebook from a url that contains the ipynb contents", - ) - parser.add_argument( - "-u", - "--url", - dest="url", - required="-h" not in other_args, - help="The url of the file to be loaded", - ) - parser.add_argument( - "-p", - "--parameters", - nargs="+", - dest="parameters", - help="Report parameters with format 'name:value'.", - ) - - # If first argument is a url, insert the -u flag - if other_args[0].startswith("http"): - other_args.insert(0, "--url") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - # Validate parameter inputs - parameters_dict = {} - if ns_parser.parameters: - for p in ns_parser.parameters: - if ":" in p: - item = p.split(":") - if item[1]: - parameters_dict[item[0]] = item[1] - else: - console.print( - f"[red]Bad format '{p}': empty value.[/red]\nExecuting with defaults.\n" - ) - else: - console.print( - f"[red]Bad format '{p}': use format 'name:value'.[/red]\nExecuting with defaults.\n" - ) - - if "raw.githubusercontent" in ns_parser.url: - url = ns_parser.url - else: - url = ns_parser.url.replace( - "github.com", "raw.githubusercontent.com" - ).replace("/blob", "") - - if url: - try: - # Send an HTTP GET request to fetch the raw notebook - response = requests.get(url, timeout=20) - - if response.status_code == 200: - temporary_folder = os.path.join( - get_current_user().preferences.USER_REPORTS_DIRECTORY, - "temporary", - ) - - # Does the temp folder exist? if not create it - if not os.path.exists(temporary_folder): - os.makedirs(temporary_folder) - - # Local file path where you want to save the notebook - local_file_path = os.path.join( - temporary_folder, - url.split(".com/")[1].replace( - "/", "_" - ), # .replace(".ipynb", "") - ) - - # Save the template notebook locally - with open(local_file_path, "wb") as notebook_file: - notebook_file.write(response.content) - - # To confirm that the notebook is saved locally - if os.path.exists(local_file_path): - reports_model.render_report( - input_path=local_file_path, args_dict=parameters_dict - ) - else: - console.print(f"[red]Notebook '{url}' not found![/red]\n") - - else: - console.print( - f"Failed to fetch notebook from {url}. Status code: {response.status_code}" - ) - - except Exception as e: - console.print(f"An error occurred: {str(e)}") diff --git a/openbb_terminal/reports/reports_model.py b/openbb_terminal/reports/reports_model.py deleted file mode 100644 index 9a351184bb9..00000000000 --- a/openbb_terminal/reports/reports_model.py +++ /dev/null @@ -1,445 +0,0 @@ -"""Reports Model Module.""" - -__docformat__ = "numpy" - -import logging - -# pylint: disable=R1732, R0912 -import os -from datetime import datetime -from pathlib import Path -from threading import Thread -from typing import Any, Dict, List, Union - -import nbformat -import pandas as pd -import papermill as pm -from ipykernel.kernelapp import IPKernelApp - -from openbb_terminal.core.config.paths import ( - MISCELLANEOUS_DIRECTORY, -) -from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.forex.forex_controller import FX_TICKERS -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -CURRENT_LOCATION = Path(__file__) -REPORTS_FOLDER = CURRENT_LOCATION.parent / "templates" - -USER_REPORTS = { - filepath.name: filepath - for file_type in ["ipynb"] - for filepath in get_current_user().preferences.USER_CUSTOM_REPORTS_DIRECTORY.rglob( - f"*.{file_type}" - ) -} - -# TODO: Trim available choices to avoid errors in notebooks. - -etf_data_path = CURRENT_LOCATION.parent / "data" / "etf_tickers.csv" -ETF_TICKERS = pd.read_csv(etf_data_path).iloc[:, 0].to_list() - -crypto_data_path = CURRENT_LOCATION.parent / "data" / "crypto_tickers.csv" -CRYPTO_TICKERS = pd.read_csv(crypto_data_path).iloc[:, 0].to_list() - -stocks_data_path = CURRENT_LOCATION.parent / "data" / "stocks_tickers.csv" -STOCKS_TICKERS = pd.read_csv(stocks_data_path).iloc[:, 0].to_list() - -PORTFOLIO_HOLDINGS_FILES = { - filepath.name: filepath - for file_type in ["xlsx", "csv"] - for filepath in ( - get_current_user().preferences.USER_PORTFOLIO_DATA_DIRECTORY / "holdings" - ).rglob(f"*.{file_type}") -} - -PORTFOLIO_HOLDINGS_FILES.update( - { - "holdings_example.xlsx": MISCELLANEOUS_DIRECTORY - / "portfolio" - / "holdings_example.xlsx" - } -) - -REPORT_CHOICES = { - "etf": { - "--symbol": {c: None for c in ETF_TICKERS}, - }, - "forex": { - "--symbol": {c: None for c in FX_TICKERS}, - }, - "portfolio": { - "--transactions": {c: None for c in PORTFOLIO_HOLDINGS_FILES}, - }, - "economy": None, - "equity": { - "--symbol": {c: None for c in STOCKS_TICKERS}, - }, - "crypto": { - "--symbol": {c: None for c in CRYPTO_TICKERS}, - }, - "forecast": { - "--symbol": {c: None for c in STOCKS_TICKERS + ETF_TICKERS}, - }, -} -TARGET_TAG = "parameters" - - -@log_start_end(log=logger) -def get_arg_choices(report_name: str, arg_name: str) -> Union[List[str], None]: - """Get argument choices from autocompletion for crypto, forex and portfolio. - - Parameters - ---------- - report_name: str - Name of report chosen. - arg_name: str - Argument to limit choices. - - Returns: - List[str]: List with argument choices from autocompletion. - """ - - choices = None - if report_name in ("forex", "portfolio") and "--" + arg_name in REPORT_CHOICES[report_name]: # type: ignore - choices = list(REPORT_CHOICES[report_name]["--" + arg_name].keys()) # type: ignore - return choices - - -@log_start_end(log=logger) -def get_reports_available( - folder: Path = REPORTS_FOLDER, warn: bool = True -) -> List[str]: - """Get Jupyter notebook available in folder. - - Parameters - ---------- - folder: Path - Path to folder. - - Returns: - List[str]: List with names of notebooks available. - """ - - bad_format = [] - available = [] - - for notebook in os.listdir(folder): - if notebook.endswith(".ipynb"): - if " " in notebook: - bad_format.append(notebook) - else: - available.append(notebook[:-6]) - - if bad_format and warn: - s = ", ".join(bad_format) - console.print( - f"[red]Character '_' not allowed in the following names: {s}.[/red]" - ) - - return available - - -@log_start_end(log=logger) -def extract_parameters(input_path: str) -> Dict[str, str]: - """Extract required parameters from notebook content. - - Parameters - ---------- - input_path: str - Path of report to be rendered. - - Returns: - Dict[str, str]: Dictionary with parameters names and values. - """ - - input_path = add_ipynb_extension(input_path) - - with open(input_path) as f: - nb = nbformat.read(f, as_version=nbformat.NO_CONVERT) - - target_tag = TARGET_TAG - - cell_lines = [] - for cell in nb.cells: - # Filter cells by tag - if target_tag in cell.metadata.get("tags", []): - # Split cell source into separate lines - lines = cell.source.split("\n") - # Remove empty and commented lines - lines = [ - line for line in lines if line.strip() and not line.startswith("#") - ] - cell_lines.extend(lines) - - parameters_dict = {} - for line in cell_lines: - # Extract key and value from each line - key, value = line.split("=") - key = key.strip() - value = value.strip() - # Add key-value pair to dictionary and remove quotes - parameters_dict[key] = value.replace('"', "") - - if "report_name" in parameters_dict: - parameters_dict.pop("report_name") - - return parameters_dict - - -@log_start_end(log=logger) -def render_report(input_path: str, args_dict: Dict[str, str]): - """Report rendering end to end. - - Workflow: - 1. Update parameters to use in notebook with received arguments - 2. Create output path - 3. Update parameters with output_path - 4. Validate and execute notebook in a new thread. - - Parameters - ---------- - input_path: str - Path of report to be rendered. - args_dict: Dict[str, str] - Dictionary with received arguments dictionary. - """ - - try: - parameters_dict = update_parameters(input_path, args_dict) - output_path = create_output_path(input_path, parameters_dict) - parameters_dict["report_name"] = output_path - if parameters_dict: - try: - t = Thread( - target=execute_notebook, - args=(input_path, parameters_dict, output_path), - daemon=True, - ) - t.start() - t.join() - except KeyboardInterrupt: - console.print("[red]Execution interrupted by user.[/red]") - except Exception as e: - console.print(f"[red]Cannot execute notebook - {e}") - - -@log_start_end(log=logger) -def update_parameters(input_path: str, args_dict: Dict[str, str]) -> Dict[str, Any]: - """Update dictionary of parameters to be used in report with received arguments. - - Parameters - ---------- - input_path: str - Path of report to be rendered. - args_dict: Dict[str, str] - Dictionary with received arguments dictionary. - - Returns - ------- - Dict[str, Any] - Dictionary with report parameters. - """ - - parameters_dict = extract_parameters(input_path) - for key, val in args_dict.items(): - if key in parameters_dict: - parameters_dict[key] = val - else: - console.print(f"[red]'{key}' not found in notebook parameters.[/red]") - - return parameters_dict - - -@log_start_end(log=logger) -def create_output_path(input_path: str, parameters_dict: Dict[str, Any]) -> str: - """Create path to save rendered report, thus the output path. - - Parameters - ---------- - input_path: str - Path of report to be rendered. - parameters_dict: Dict[str, Any] - Dictionary with report parameters. - - Returns - ------- - str - Path of rendered report. - """ - - report_name = os.path.split(input_path)[-1] - param_values = list(parameters_dict.values()) - args_to_output = f"_{'_'.join(param_values)}" if "_".join(param_values) else "" - report_output_name = ( - f"{datetime.now().strftime('%Y%m%d_%H%M%S')}" - + "_" - + f"{report_name}{args_to_output}" - ) - - special_chars = [ - ".", - ";", - ":", - "!", - "*", - " ", - "^", - "<", - ">", - '"', - "/", - "|", - "?", - "\\", - ] - for char in special_chars: - report_output_name = report_output_name.replace(char, "_") - - output_path = str( - get_current_user().preferences.USER_REPORTS_DIRECTORY / report_output_name - ) - - return output_path - - -@log_start_end(log=logger) -def execute_notebook(input_path, parameters, output_path): - """Execute the input path's notebook with the parameters provided. - Then, save it in the output path. - - Parameters - ---------- - input_path: str - Path of report to be rendered. - parameters: Dict[str, Any] - Dictionary with report parameters. - output_path: str - Path of rendered report. - """ - - input_path = add_ipynb_extension(input_path) - - try: - result = pm.execute_notebook( - input_path=input_path, - output_path=output_path + ".ipynb", - parameters=parameters, - ) - - if not result["metadata"]["papermill"]["exception"]: - console.print(f"\n[green]Notebook:[/green] {output_path}.ipynb") - if get_current_user().preferences.OPEN_REPORT_AS_HTML: - report_output_path = os.path.join( - os.path.abspath(os.path.join(".")), output_path + ".html" - ) - report_output_path = Path(report_output_path) - - plots_backend().send_url( - url=f"/{report_output_path.as_uri()}", title="Reports" - ) - console.print(f"\n[green]Report:[/green] {report_output_path}\n") - else: - console.print("\n") - else: - console.print("[red]\nReport .html couldn't be created.\n[/red]") - - except pm.PapermillExecutionError as e: - console.print( - f"[red]\nAn error was encountered in cell [{e.exec_count}], check the notebook:[/red]\n" - f"{output_path}.ipynb\n" - ) - - -@log_start_end(log=logger) -def add_ipynb_extension(path: str) -> str: - """Add .ipynb extension to path. - Parameters - ---------- - path: str - Path to notebook file. - - Returns - ------- - str - Path to .ipynb file. - """ - - if not path.endswith(".ipynb"): - return path + ".ipynb" - return path - - -@log_start_end(log=logger) -def check_ipynb(path: str) -> str: - """Check if there is .ipynb extension in path. - This is useful to the controller type check. - - Parameters - ---------- - path: str - Path to notebook file. - - Returns - ------- - bool - Path if paths endswith .ipynb, else empty string. - """ - - if not path.endswith(".ipynb"): - console.print("[red]Please provide a .ipynb file.[/red]\n") - return "" - return path - - -def ipykernel_launcher(module_file: str, module_hist_file: str): - """This function mocks 'ipykernel_launcher.py' launching a Jupyter notebook kernel. - - It is useful when running python commands inside a frozen application like our - installer distribution, where sys.executable[0] is not the path to python - interpreter, rather it is the path to the application executable. - - Problem: - 'papermill' was trying to execute the following command on a subprocess: - $ .../bin/python -m ipykernel_launcher -f ... --HistoryManager.hist_file ... - - 'papermill' was using '.../bin/python' because it is looks for the sys.executable[0], - which most of the time leads to the python interpreter. In our frozen app, - sys.executable[0] leads to 'OpenBB Terminal/.OpenBB/OpenBBTerminal', which in turn - executes 'terminal.py. - - This means that the command was being executed in 'terminal.py'. Consequently, - one gets the following error message: - $ terminal: error: unrecognized arguments: -m ipykernel_launcher -f ... --HistoryManager.hist_file ... - - Solution: - Parse 'papermill' command in the 'terminal_controller', which is what follows - 'terminal.py' and here receive the parsed 'papermill' command arguments and - route them to IPKernelApp as if this is 'ipykernel_launcher' module - - the kernel is launched. - - Source: https://pyinstaller.org/en/stable/runtime-information.html#using-sys-executable-and-sys-argv-0 - - Parameters - ---------- - module_file: str - Specified connection file. - module_hist_file: str - History manager file. - """ - # pylint: disable=import-outside-toplevel - import matplotlib # noqa - - matplotlib.use("agg") - IPKernelApp.launch_instance( - argv=[ - "-f", - module_file, - "--HistoryManager.hist_file", - module_hist_file, - ] - ) diff --git a/openbb_terminal/reports/stored/.gitkeep b/openbb_terminal/reports/stored/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/reports/stored/20220512_040312_equity_NVDA.html b/openbb_terminal/reports/stored/20220512_040312_equity_NVDA.html deleted file mode 100644 index 33722e4bff4..00000000000 --- a/openbb_terminal/reports/stored/20220512_040312_equity_NVDA.html +++ /dev/null @@ -1,38448 +0,0 @@ - - - - - - openbb_terminal/reports/stored/20220512_040312_equity_NVDA - - - - - - - -
- OpenBB -
-

Analyst: Didier Rodrigues Lopes

-

Date : 12 May, 2022

-

Time : 04:03 BST

-
-

INVESTMENT RESEARCH REPORT: NVDA

-
-



-

- No comment. -

KPIs

✅ Average price ratings of last 30 day is above last closing price. 261.75 > 166.3

❌ Regression (dollars per market day) on last 30 market days is negative. -3.38 < 0

🟡 RSI level is normal. 30 < 33.13 < 70

❌ The sum of last 10 insider trading (in millions) was negative. -247.17 < 0

✅ Last FinBrain sentiment is bullish. 0.178 > 0.1

❌ The regression for the next 20th market price is below closing price. -50.33 < 0

-

-

- No comment. -

- -
- - - - - - - 2022-05-12T04:04:06.660540 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -

Description

NVIDIA Corporation provides graphics, and compute and networking solutions in the United States, Taiwan, China, and internationally. The company's Graphics segment offers GeForce GPUs for gaming and PCs, the GeForce NOW game streaming service and related infrastructure, and solutions for gaming platforms; Quadro/NVIDIA RTX GPUs for enterprise workstation graphics; vGPU software for cloud-based visual and virtual computing; automotive platforms for infotainment systems; and Omniverse software for building 3D designs and virtual worlds. Its Compute & Networking segment provides Data Center platforms and systems for AI, HPC, and accelerated computing; Mellanox networking and interconnect solutions; automotive AI Cockpit, autonomous driving development agreements, and autonomous vehicle solutions; cryptocurrency mining processors; Jetson for robotics and other embedded platforms; and NVIDIA AI Enterprise and other software. The company's products are used in gaming, professional visualization, datacenter, and automotive markets. NVIDIA Corporation sells its products to original equipment manufacturers, original device manufacturers, system builders, add-in board manufacturers, retailers/distributors, independent software vendors, Internet and cloud service providers, automotive manufacturers and tier-1 automotive suppliers, mapping companies, start-ups, and other ecosystem participants. It has a strategic collaboration with Kroger Co. NVIDIA Corporation was incorporated in 1993 and is headquartered in Santa Clara, California.

- -
-

-

- No comment. -

- -
- - - - - - - 2022-05-12T04:04:08.583265 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-12T04:04:09.466130 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CategoryAnalystRatingPrice Target
date
2022-05-03ResumedMorgan StanleyEqual-Weight217.0
2022-04-13UpgradeNew StreetNeutral -> Buy280.0
2022-04-11DowngradeRobert W. BairdOutperform -> Neutral225.0
2022-03-15ReiteratedCowenOutperform350.0
2022-03-07ResumedGoldmanNeutral245.0
- -
-

-

- No comment. -

- -

Estimates

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
202220232024
YEARLY ESTIMATES
Revenue26,66334,88340,596
Dividend0.340.280.29
Dividend Yield (in %)0.19 %0.16 %0.16 %
EPS4.345.666.67
P/E Ratio40.5231.0826.36
- -
- -

Earnings

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Previous QuarterCurrent QuarterNext QuarterCurrent YearNext Year
QUARTER EARNINGS ESTIMATES
Date---ending 01/31/22ending 01/31/23
No. of Analysts---3740
Average Estimate---4.342 USD5.661 USD
Year Ago---2.500 USD4.342 USD
Publish Date---6/2/2022-
- -
- -

Revenues

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Previous QuarterCurrent QuarterNext QuarterCurrent YearNext Year
QUARTER REVENUES ESTIMATES
Date---ending 01/31/22ending 01/31/23
No. of Analysts---3942
Average Estimate---26,663 USD34,883 USD
Year Ago---16,675 USD26,663 USD
Publish Date---6/2/2022-
- -
- -

SEC filings

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeCategoryLink
Filing Date
04/19/2022DEF 14AProxy Statementhttps://www.marketwatch.com/investing/stock/nvda/financials/secfilings?docid=15739033
04/19/2022DEF 14AProxy Statementhttps://www.marketwatch.com/investing/stock/nvda/financials/secfilings?docid=15739000
04/08/2022PRE 14AProxy Statementhttps://www.marketwatch.com/investing/stock/nvda/financials/secfilings?docid=15722884
03/18/202210-KAnnual Reportshttps://www.marketwatch.com/investing/stock/nvda/financials/secfilings?docid=15668939
03/09/20228-KSpecial Eventshttps://www.marketwatch.com/investing/stock/nvda/financials/secfilings?docid=15645553
- -
-

-

- No comment. -

- -
- - - - - - - 2022-05-12T04:03:29.032118 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-12T04:04:05.322811 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-12T04:04:02.005789 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-12T04:04:04.043634 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

-

- No comment. -

- -
- - - - - - - 2022-05-12T04:04:09.933946 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

-

- No comment. -

- -

Last Activity

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Insider TradingRelationshipDateTransactionCost#SharesValue ($)#Shares TotalSEC Form 4
0COXE TENCHDirectorMar 28Sale274.06100,00027,405,7203,597,136Mar 29 07:26 PM
1PERRY MARK LDirectorMar 18Sale263.8016,7164,409,69911,524Mar 21 06:16 PM
2HUANG JEN HSUNPresident and CEOMar 01Option Exercise3.62292,3401,056,8095,471,307Mar 02 06:58 PM
3HUANG JEN HSUNPresident and CEOMar 01Sale237.12292,34069,321,0005,178,967Mar 02 06:58 PM
4HUANG JEN HSUNPresident and CEOJan 03Option Exercise3.62440,0001,590,6005,618,925Jan 05 05:25 PM
5HUANG JEN HSUNPresident and CEOJan 03Sale302.17440,000132,954,0315,178,925Jan 05 05:25 PM
6Dabiri JohnDirectorDec 23Sale297.5028183,5984,912Dec 23 07:28 PM
7JONES HARVEY CDirectorDec 16Sale311.5041,48412,922,266866,396Dec 20 04:39 PM
8Dabiri JohnDirectorDec 13Sale302.0524774,6055,193Dec 15 04:42 PM
9HUANG JEN HSUNPresident and CEODec 01Option Exercise3.62440,0001,590,6005,634,956Dec 03 06:04 PM
- -
-

-

- No comment. -

- -
- - - - - - - 2022-05-12T04:04:12.919036 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-12T04:04:15.376549 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -

Stocktwits

Watchlist count: 371033
-Last 8 sentiment messages:
Bullish 75.0%
Bearish 25.0%
- -
-

-

- No comment. -

- -
- - - - - - - 2022-05-12T04:04:16.462393 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-12T04:04:16.894780 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-

-

- No comment. -

- -
- - - - - - - 2022-05-12T04:04:18.708706 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - 2022-05-12T04:04:18.099338 - image/svg+xml - - - Matplotlib v3.5.1, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- - - \ No newline at end of file diff --git a/openbb_terminal/reports/templates/OpenBB_reports_logo.png b/openbb_terminal/reports/templates/OpenBB_reports_logo.png deleted file mode 100644 index 392bc5e14d9fcdcab106155db31326b36620572a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5214 zcmeHLYgAKL7Ctw~W1ldO zwTe7bswH(nA-qDZB*aH#5EU&?!yBrAAtV?eU_zL4yZUpM|Cs(5ver%3-DjV(zx|zk z_SrjG2ZHt(V+mLQfN_Ao?;!y6lmK8DMg~xFuhj?#fByEBe@Hw4tIg2|0R=_YP>94I z+T#N%pRS#NFRLQG_j?0Sox585nLYsKssLZ_!wf{;FN?7`N@yNVPS}!_mgSKt&^x8S zFZr9p{Jn+&`jxSntNku(+lV-?i-@t)g?M+B zd9E830c~6J1K$!#3kwThjJ0vec)&M11C4=6B7&ksRuNGorVu_b^%Cm=;00C&mXQ+n zlv1g@3D{Q?sp={MPbE^gU;(8?r_*s3(pb8l**h&KezOAn8p-kJoksQ1nYDuJmVp25 z%hM^Rnao8My?f_*TZj*1#DL#M>xK{j*0I@!0I&gOWnkGUS=-^iDX&#Ar&g0KLG*L= zBDr^@Ib&G!EU`Zd;k(K`Mwp>%t<;1Val`w+x2Ju)L;)3&1P{6Nc0Fxe%3_HqPM_`q z70*v>b+nDZl9n;jR?hLeQ=8jQm!*S)OV4ce#i=vQ@XZF)?&83IT)Bc_8>>v-J#{5r zF6TwzHm=bz5%IP}YH}aGHe=ZHRCll)7L?q|*=Q9UvNZJi_3K^?JF*~%SL-|`yAm`%ZWGi&_-v_^@BolBg1U`*VwK~ho@L}ebiXj6?nTH z?2E2L!kZ+T3IIx8%}*h~=HyBWgjc}-zVb4;aBxDStC9WMilNjdvE08H%e#?rcH1?K zi-huM*DCG2wvOU7fEO3+=e392QM!F(En2Fpp6SIpm;>{$JPWn0#^5 z#Kc6@bo=%~4J`hx_aT%#G$X<=`ak>dk5KO#2vcWYb1G#)(I;YKVTPm&E`S z9Bg(Q-OO;HZQ~Ha&M}<^N+N8ddaR+S-1CE;OaQxDl;z5#N_?L_KZR%fc(I#n`)XhGalH%VCGOElK zKYMm*ddb~#aTCm~hrfr{<-Wxv5}EB_+2V``(UJr=vdT zMt7`wn>1-5c58l72fi*G2t^8+Jf*nv^^$szM#}9My=qILauqC0>P8WO-0VwF8>F5oAGFAJRQh0q^*IT zZ#J^GWtW;OftZ%b0AB|}Q|IhQ5bF;iNoy`adNR{sRkqo`oaM22`nD73@q^oIUGa&% zNEn|U2KeVt!VToN+9wJ}G3@vM4q@L7T@NE;3=I|whnH37&4O{5ZD2Tv>$E}n1XyHK^DP0 z8H%VIph)NSK#!jd0gJ<7q)!smZ)rC!6(o7joXmNg;IWukq0WpYYmTa!LOIS#Lc2I? zZ6{-CpUv4mlD%nGU6+%bA1bHgVle+D%dfeYj^OH2pPM?E!czl4fM1Yrr4KFRKi9m` A1poj5 diff --git a/openbb_terminal/reports/templates/crypto.ipynb b/openbb_terminal/reports/templates/crypto.ipynb deleted file mode 100644 index 36245820ad5..00000000000 --- a/openbb_terminal/reports/templates/crypto.ipynb +++ /dev/null @@ -1,1166 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "70ba2c8a", - "metadata": {}, - "source": [ - "## Notebook setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "driven-billion", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import io\n", - "import warnings\n", - "import datetime\n", - "import numpy as np\n", - "import base64\n", - "import pandas as pd\n", - "from typing import List\n", - "\n", - "from IPython.display import HTML\n", - "\n", - "import openbb_terminal.config_terminal as cfg\n", - "from openbb_terminal.helper_classes import TerminalStyle\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "\n", - "\n", - "warnings.filterwarnings(\"ignore\")\n", - "\n", - "pd.set_option(\"display.float_format\", lambda x: \"%.5f\" % x)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24ed42f0", - "metadata": {}, - "outputs": [], - "source": [ - "# Suppressing sdk logs\n", - "from openbb_terminal.core.session.current_system import set_system_variable\n", - "\n", - "set_system_variable(\"LOGGING_SUPPRESS\", True)\n", - "\n", - "# Import the OpenBB SDK\n", - "from openbb_terminal.sdk import openbb, widgets" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3fe7db5-ec6a-42cf-9e66-52dc1de22370", - "metadata": {}, - "outputs": [], - "source": [ - "from openbb_terminal.core.session.current_user import get_current_user\n", - "\n", - "user = get_current_user()\n", - "\n", - "cfg.theme = TerminalStyle(\n", - " user.preferences.MPL_STYLE,\n", - " user.preferences.PMF_STYLE,\n", - " user.preferences.RICH_STYLE,\n", - ")\n", - "theme.apply_style(\"light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c52f83e1", - "metadata": {}, - "outputs": [], - "source": [ - "stylesheet = widgets.html_report_stylesheet()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "807fda4e", - "metadata": {}, - "source": [ - "## Select Ticker" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "intended-sherman", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "# Parameters that will be replaced when calling this notebook\n", - "# Do not leave parameters blank as notebook will not run otherwise\n", - "symbol = \"BTC\"\n", - "report_name = f\"Crypto Report for {symbol}\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c46efb8", - "metadata": {}, - "outputs": [], - "source": [ - "ticker = symbol" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d4897334", - "metadata": {}, - "outputs": [], - "source": [ - "author = \"\"\n", - "report_title = f\"INVESTMENT RESEARCH REPORT ON {ticker.upper()}\"\n", - "report_date = datetime.datetime.now().strftime(\"%d %B, %Y\")\n", - "report_time = datetime.datetime.now().strftime(\"%H:%M\")\n", - "report_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo\n", - "report_title, report_date, report_time, report_timezone" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35d9a943", - "metadata": {}, - "outputs": [], - "source": [ - "# basic info\n", - "\n", - "basic_info = openbb.crypto.dd.basic(ticker)\n", - "if not basic_info.empty:\n", - " basic_info[\"Metric\"] = basic_info[\"Metric\"].str.title()\n", - " basic_info[\"Metric\"] = basic_info[\"Metric\"].str.replace(\"_\", \" \")\n", - " basic_info = basic_info.set_index(\"Metric\")\n", - " basic_info" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "091d305e", - "metadata": {}, - "outputs": [], - "source": [ - "news = openbb.crypto.dd.news(symbol=ticker).head(10)\n", - "\n", - "if (isinstance(news, pd.DataFrame) and not news.empty) or (\n", - " not isinstance(news, pd.DataFrame) and news\n", - "):\n", - " news = news.set_index(\"published_at\")\n", - " news.sort_index()\n", - " news[\"link\"] = news[\"link\"].apply(lambda x: f'
{x}')\n", - " news\n", - "else:\n", - " news = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bd281ab8", - "metadata": {}, - "outputs": [], - "source": [ - "links = openbb.crypto.dd.links(ticker)\n", - "\n", - "if (isinstance(links, pd.DataFrame) and not links.empty) or (\n", - " not isinstance(links, pd.DataFrame) and links\n", - "):\n", - " links[\"Link\"] = links[\"Link\"].apply(lambda x: f'{x}')\n", - " links\n", - "else:\n", - " links = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "07d09ce8", - "metadata": {}, - "outputs": [], - "source": [ - "five_year = datetime.datetime.now() - datetime.timedelta(days=1825)\n", - "three_year = datetime.datetime.now() - datetime.timedelta(days=1095)\n", - "one_year = datetime.datetime.now() - datetime.timedelta(days=365)\n", - "three_months = datetime.datetime.now() - datetime.timedelta(days=90)\n", - "\n", - "dates = {\n", - " \"three_months\": three_months,\n", - " \"one_year\": one_year,\n", - " \"three_year\": three_year,\n", - " \"five_year\": five_year,\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e6d6d43d", - "metadata": {}, - "outputs": [], - "source": [ - "from tkinter.filedialog import Open\n", - "\n", - "\n", - "def get_historical_data(ticker, dict_of_dates, is_plot=True) -> List[pd.DataFrame]:\n", - " list_of_historical_data = []\n", - "\n", - " for date in dict_of_dates:\n", - " data = openbb.crypto.load(\n", - " symbol=ticker,\n", - " to_symbol=\"usd\",\n", - " start_date=dict_of_dates[date],\n", - " source=\"YahooFinance\",\n", - " )\n", - "\n", - " if data.empty:\n", - " continue\n", - "\n", - " # we'll be using the adjusted close price as same as close price\n", - " data[\"Adj Close\"] = data[\"Close\"]\n", - "\n", - " if is_plot:\n", - " fig = OpenBBFigure(yaxis_title=\"USDT\").set_title(\n", - " f\"{date} historical data for {ticker.upper()}\"\n", - " )\n", - " fig.add_scatter(x=data.index, y=data[\"Adj Close\"], name=\"Close\")\n", - "\n", - " plot_data = fig.to_html()\n", - "\n", - " list_of_historical_data.append(plot_data)\n", - " else:\n", - " list_of_historical_data.append(data)\n", - "\n", - " return list_of_historical_data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ed19a708", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " historical_data = get_historical_data(ticker, dates)\n", - "except Exception as e:\n", - " raise KeyboardInterrupt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8f815dbc", - "metadata": {}, - "outputs": [], - "source": [ - "historical_data_one_year = get_historical_data(ticker, {\"one_year\": one_year}, False)[0]\n", - "historical_data_one_year" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "081a4bb3", - "metadata": {}, - "outputs": [], - "source": [ - "from openbb_terminal.cryptocurrency.cryptocurrency_helpers import show_quick_performance\n", - "\n", - "show_quick_performance(\n", - " crypto_df=historical_data_one_year,\n", - " symbol=ticker,\n", - " current_currency=\"USDT\",\n", - " source=\"YahooFinance\",\n", - " exchange=\"binance\",\n", - " interval=\"1440\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "88af0fbe", - "metadata": {}, - "outputs": [], - "source": [ - "headlines = openbb.crypto.dd.headlines_chart(ticker, external_axes=True)\n", - "\n", - "if headlines:\n", - " headlines = headlines.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e4509c6e", - "metadata": {}, - "outputs": [], - "source": [ - "candle_one_year = openbb.crypto.chart(\n", - " historical_data_one_year,\n", - " to_symbol=ticker,\n", - " from_symbol=\"USDT\",\n", - " source=\"YahooFinance\",\n", - " exchange=\"binance\",\n", - " interval=\"1440\",\n", - " external_axes=True,\n", - ")\n", - "\n", - "if candle_one_year:\n", - " candle_one_year = candle_one_year.to_html()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "7f83daae", - "metadata": {}, - "source": [ - "## Due Diligence" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93d66cf1", - "metadata": {}, - "outputs": [], - "source": [ - "# market cap dominance\n", - "mcapdom_df = openbb.crypto.dd.mcapdom(symbol=ticker)\n", - "\n", - "if (isinstance(mcapdom_df, pd.DataFrame) and not mcapdom_df.empty) or (\n", - " not isinstance(mcapdom_df, pd.DataFrame) and mcapdom_df\n", - "):\n", - " mcapdom = openbb.crypto.dd.mcapdom_chart(symbol=ticker, external_axes=True)\n", - "\n", - " if mcapdom:\n", - " mcapdom = mcapdom.to_html()\n", - "else:\n", - " mcapdom_df = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "284b0586", - "metadata": {}, - "outputs": [], - "source": [ - "# roadmap\n", - "roadmap_model = openbb.crypto.dd.rm(symbol=ticker)\n", - "\n", - "if (isinstance(roadmap_model, pd.DataFrame) and not roadmap_model.empty) or (\n", - " not isinstance(roadmap_model, pd.DataFrame) and roadmap_model\n", - "):\n", - " roadmap_model = roadmap_model.set_index(\"Date\")\n", - " roadmap_model.sort_index()\n", - "\n", - " roadmap_view = openbb.crypto.dd.rm_chart(symbol=ticker, external_axes=True)\n", - " if roadmap_view:\n", - " roadmap_view = roadmap_view.to_html()\n", - "\n", - "else:\n", - " roadmap_model = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9ddbc654", - "metadata": {}, - "outputs": [], - "source": [ - "tokenomics_model = openbb.crypto.dd.rm(symbol=ticker)\n", - "\n", - "if (isinstance(tokenomics_model, pd.DataFrame) and not tokenomics_model.empty) or (\n", - " not isinstance(tokenomics_model, pd.DataFrame) and tokenomics_model\n", - "):\n", - " tokenomics_model = tokenomics_model.set_index(\"Date\")\n", - " tokenomics_model.sort_index()\n", - "\n", - " tokenomics_view = openbb.crypto.dd.tk_chart(symbol=ticker, external_axes=True)\n", - " if tokenomics_view:\n", - " tokenomics_view = tokenomics_view.to_html()\n", - "\n", - "else:\n", - " tokenomics_model = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7d02fcc4", - "metadata": {}, - "outputs": [], - "source": [ - "# fundraise\n", - "fundraise = openbb.crypto.dd.fr(symbol=ticker) # this returns a tuple\n", - "\n", - "if fundraise:\n", - " fundraise_info = fundraise[0]\n", - " fundraise_df = fundraise[1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e625a2e", - "metadata": {}, - "outputs": [], - "source": [ - "trading_pair_info = openbb.crypto.dd.trading_pair_info(f\"{ticker}-USDT\")\n", - "if (isinstance(trading_pair_info, pd.DataFrame) and not trading_pair_info.empty) or (\n", - " not isinstance(trading_pair_info, pd.DataFrame) and trading_pair_info\n", - "):\n", - " trading_pair_info[\"Metric\"] = trading_pair_info[\"Metric\"].str.title()\n", - " trading_pair_info[\"Metric\"] = trading_pair_info[\"Metric\"].str.replace(\"_\", \" \")\n", - " trading_pair_info = trading_pair_info.set_index(\"Metric\")\n", - " trading_pair_info\n", - "else:\n", - " trading_pair_info = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8f33b4d8", - "metadata": {}, - "outputs": [], - "source": [ - "import openbb_terminal.cryptocurrency.due_diligence.pycoingecko_model as gecko\n", - "from openbb_terminal.cryptocurrency.cryptocurrency_helpers import get_coingecko_id\n", - "\n", - "ath = pd.DataFrame()\n", - "try:\n", - " cg_id = get_coingecko_id(symbol=ticker)\n", - " coin = gecko.Coin(ticker)\n", - " ath = coin.get_all_time_high(currency=cg_id)\n", - "\n", - "except Exception as e:\n", - " print(\"Reached coingecko API limit\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2a82e990", - "metadata": {}, - "outputs": [], - "source": [ - "import openbb_terminal.cryptocurrency.due_diligence.pycoingecko_model as gecko\n", - "from openbb_terminal.cryptocurrency.cryptocurrency_helpers import get_coingecko_id\n", - "\n", - "atl = pd.DataFrame()\n", - "try:\n", - " cg_id = get_coingecko_id(symbol=ticker)\n", - " coin = gecko.Coin(ticker)\n", - " atl = coin.get_all_time_low(currency=cg_id)\n", - "\n", - "except Exception as e:\n", - " print(\"Reached coingecko API limit\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e9e512e9", - "metadata": {}, - "outputs": [], - "source": [ - "# market stats about loaded coin\n", - "\n", - "market = openbb.crypto.dd.mkt(ticker)\n", - "\n", - "if (isinstance(market, pd.DataFrame) and not market.empty) or (\n", - " not isinstance(market, pd.DataFrame) and market\n", - "):\n", - " market = market.set_index(\"exchange\")\n", - "\n", - " market[\"market_url\"] = market[\"market_url\"].apply(\n", - " lambda x: f'{x}'\n", - " )\n", - " market\n", - "else:\n", - " market = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1d6e76cb", - "metadata": {}, - "outputs": [], - "source": [ - "# all exchanges where loaded coin is listed\n", - "ex = openbb.crypto.dd.ex(ticker)\n", - "\n", - "if (isinstance(ex, pd.DataFrame) and not ex.empty) or (\n", - " not isinstance(ex, pd.DataFrame) and ex\n", - "):\n", - " ex = ex.set_index(\"id\")\n", - " ex\n", - "else:\n", - " ex = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1fcdbc67", - "metadata": {}, - "outputs": [], - "source": [ - "# last trades\n", - "trades = openbb.crypto.dd.trades(symbol=ticker, exchange_id=\"gemini\", to_symbol=\"USD\")\n", - "if (isinstance(trades, pd.DataFrame) and not trades.empty) or (\n", - " not isinstance(trades, pd.DataFrame) and trades\n", - "):\n", - " trades = trades.set_index(\"Date\")\n", - " trades\n", - "else:\n", - " trades = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "262d190a", - "metadata": {}, - "outputs": [], - "source": [ - "# order book\n", - "order_book_raw = openbb.crypto.dd.ob(\n", - " symbol=ticker, exchange=\"gemini\", to_symbol=\"USD\"\n", - ") # this returns a dict\n", - "\n", - "if order_book_raw:\n", - " order_book = openbb.crypto.dd.ob_chart(\n", - " symbol=ticker, exchange=\"gemini\", to_symbol=\"USD\", external_axes=True\n", - " )\n", - " if order_book:\n", - " order_book = order_book.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "679d0d79", - "metadata": {}, - "outputs": [], - "source": [ - "# price and supply related metrics for loaded coin\n", - "\n", - "ps = openbb.crypto.dd.ps(ticker)\n", - "if (isinstance(ps, pd.DataFrame) and not ps.empty) or (\n", - " not isinstance(ps, pd.DataFrame) and ps\n", - "):\n", - " ps = ps.set_index(\"Metric\")\n", - " ps\n", - "else:\n", - " ps = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "92e76d79", - "metadata": {}, - "outputs": [], - "source": [ - "# events\n", - "\n", - "events = openbb.crypto.dd.events(ticker)\n", - "\n", - "if (isinstance(events, pd.DataFrame) and not events.empty) or (\n", - " not isinstance(events, pd.DataFrame) and events\n", - "):\n", - " events = events.set_index(\"date\")\n", - " events[\"link\"] = events[\"link\"].apply(lambda x: f'{x}')\n", - "else:\n", - " events = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a14130db", - "metadata": {}, - "outputs": [], - "source": [ - "# social media\n", - "\n", - "from openbb_terminal.cryptocurrency.due_diligence.pycoingecko_view import display_social\n", - "\n", - "display_social(ticker)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8f89627f", - "metadata": {}, - "outputs": [], - "source": [ - "# tweets for loaded coin\n", - "\n", - "tweets = openbb.crypto.dd.twitter(ticker)\n", - "tweets" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9bb75443", - "metadata": {}, - "outputs": [], - "source": [ - "# github activity over time\n", - "\n", - "gh_data = openbb.crypto.dd.gh(symbol=ticker)\n", - "\n", - "if (isinstance(gh_data, pd.DataFrame) and not gh_data.empty) or (\n", - " not isinstance(gh_data, pd.DataFrame) and gh_data\n", - "):\n", - " gh = openbb.crypto.dd.gh_chart(symbol=ticker, external_axes=True)\n", - " if gh:\n", - " gh = gh.to_html()\n", - "else:\n", - " gh_data = pd.DataFrame()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "fcaa973d", - "metadata": {}, - "source": [ - "## TA\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "477317b4", - "metadata": {}, - "outputs": [], - "source": [ - "ticker_data = historical_data_one_year.copy()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7af3695c", - "metadata": {}, - "outputs": [], - "source": [ - "# simple moving average\n", - "ticker_data.index.names = [\"date\"]\n", - "\n", - "sma = openbb.ta.ma_chart(\n", - " data=ticker_data,\n", - " ma_type=\"SMA\",\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if sma:\n", - " sma = sma.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5d5089fa", - "metadata": {}, - "outputs": [], - "source": [ - "# exponential moving average\n", - "ema = openbb.ta.ma_chart(\n", - " data=ticker_data,\n", - " ma_type=\"EMA\",\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if ema:\n", - " ema = ema.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5facfbf4", - "metadata": {}, - "outputs": [], - "source": [ - "# zero lag exponential moving average\n", - "\n", - "zlma = openbb.ta.ma_chart(\n", - " data=ticker_data,\n", - " ma_type=\"ZLMA\",\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if zlma:\n", - " zlma = zlma.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "df54f7ad", - "metadata": {}, - "outputs": [], - "source": [ - "# commodity channel index\n", - "\n", - "cci = openbb.ta.cci_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if cci:\n", - " cci = cci.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f53469eb", - "metadata": {}, - "outputs": [], - "source": [ - "# moving average convergence/divergence\n", - "\n", - "macd = openbb.ta.macd_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if macd:\n", - " macd = macd.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dd79e6b6", - "metadata": {}, - "outputs": [], - "source": [ - "# fisher transform\n", - "\n", - "fisher = openbb.ta.fisher_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if fisher:\n", - " fisher = fisher.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b699e61f", - "metadata": {}, - "outputs": [], - "source": [ - "# aroon indicator\n", - "\n", - "aroon = openbb.ta.aroon_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if aroon:\n", - " aroon = aroon.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "79401027", - "metadata": {}, - "outputs": [], - "source": [ - "# bollinger bands\n", - "\n", - "bbands = openbb.ta.bbands_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if bbands:\n", - " bbands = bbands.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0b5e2949", - "metadata": {}, - "outputs": [], - "source": [ - "# fibonacci retracement\n", - "\n", - "fib = openbb.ta.fib_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if fib:\n", - " fib = fib.to_html()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "48847a7f", - "metadata": {}, - "source": [ - "## QA" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e77b373b", - "metadata": {}, - "outputs": [], - "source": [ - "# summary\n", - "\n", - "summary = openbb.qa.summary(data=ticker_data)\n", - "summary" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ecfc93e", - "metadata": {}, - "outputs": [], - "source": [ - "# normality statistics and tests\n", - "\n", - "# openbb.qa.normality_chart(\n", - "# data=ticker_data,\n", - "# target=\"Close\",\n", - "# )\n", - "\n", - "normality = openbb.qa.normality(\n", - " data=ticker_data[\"Close\"],\n", - ")\n", - "normality" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8abe96b0", - "metadata": {}, - "outputs": [], - "source": [ - "# box and whisker plot\n", - "\n", - "bw = openbb.qa.bw(\n", - " symbol=ticker,\n", - " data=ticker_data,\n", - " target=\"Close\",\n", - " yearly=False,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if bw:\n", - " bw = bw.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "66a7dccc", - "metadata": {}, - "outputs": [], - "source": [ - "# rolling mean and std deviation of prices\n", - "\n", - "rolling = openbb.qa.rolling_chart(\n", - " symbol=ticker,\n", - " data=ticker_data,\n", - " target=\"Close\",\n", - " external_axes=True,\n", - ")\n", - "\n", - "if rolling:\n", - " rolling.update_layout(\n", - " legend=dict(\n", - " x=0.01,\n", - " y=0.99,\n", - " xanchor=\"left\",\n", - " yanchor=\"top\",\n", - " xref=\"paper\",\n", - " yref=\"paper\",\n", - " bgcolor=\"rgba(0, 0, 0, 0)\",\n", - " ),\n", - " legend2=dict(bgcolor=\"rgba(0, 0, 0, 0)\"),\n", - " )\n", - " rolling = rolling.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6b43dd7", - "metadata": {}, - "outputs": [], - "source": [ - "# rolling kurtosis of distribution of prices\n", - "\n", - "kurtosis = openbb.qa.kurtosis_chart(\n", - " symbol=ticker,\n", - " data=ticker_data,\n", - " target=\"Close\",\n", - " external_axes=True,\n", - ")\n", - "\n", - "if kurtosis:\n", - " kurtosis = kurtosis.to_html()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ac501a32-124d-4d21-aae1-a8d147bb1071", - "metadata": {}, - "source": [ - "## Render the report template to a file" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "abfe37e4-ea38-42c3-8fb5-7cceb233fb12", - "metadata": {}, - "outputs": [], - "source": [ - "body = \"\"\n", - "\n", - "\n", - "body += widgets.header(\n", - " author,\n", - " report_date,\n", - " report_time,\n", - " report_timezone,\n", - " f\"INVESTMENT RESEARCH REPORT: {ticker}\",\n", - " plotly_js=True,\n", - ")\n", - "\n", - "body += widgets.tablinks(\n", - " [\n", - " \"SUMMARY\",\n", - " \"Due Diligence\",\n", - " \"Technical Analysis\",\n", - " \"Quantitative Analysis\",\n", - " ]\n", - ")\n", - "\n", - "comment = \"\"\n", - "\n", - "# Summary tab\n", - "htmlcode = \"\"\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Basic information about loaded coin\") + basic_info.to_html()]\n", - ")\n", - "if not links.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"Links\") + links.to_html(escape=False)])\n", - "\n", - "if not news.empty:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, \"Published news\") + news.to_html(escape=False)]\n", - " )\n", - "\n", - "htmlcode += widgets.row([widgets.h(3, \"Historical data\")])\n", - "for data in historical_data:\n", - " htmlcode += widgets.row([data])\n", - "\n", - "htmlcode += widgets.row([widgets.h(3, \"1-year historical data candlestick chart\")])\n", - "htmlcode += widgets.row([candle_one_year])\n", - "htmlcode += widgets.row([widgets.h(3, \"Sentiment analysis\")])\n", - "htmlcode += widgets.row([headlines])\n", - "# TODO: print rich table\n", - "# htmlcode += widgets.row([widgets.h(3, \"Quick performance\")])\n", - "# htmlcode += widgets.row([quick_performance])\n", - "body += widgets.add_tab(\"SUMMARY\", htmlcode)\n", - "\n", - "# # Due diligence tab\n", - "htmlcode = \"\"\n", - "if not trading_pair_info.empty:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, f\"Trading pair info {ticker}/USDT\") + trading_pair_info.to_html()]\n", - " )\n", - "if not mcapdom_df.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"Market cap dominance\")])\n", - " htmlcode += widgets.row([mcapdom])\n", - "\n", - "if not roadmap_model.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"Roadmap\") + roadmap_model.to_html()])\n", - " htmlcode += widgets.row([roadmap_view])\n", - "\n", - "if not tokenomics_model.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"Tokenomics\") + tokenomics_model.to_html()])\n", - " htmlcode += widgets.row([tokenomics_view])\n", - "\n", - "if fundraise:\n", - " htmlcode += widgets.row([widgets.h(3, \"Fundraising Details\")])\n", - " htmlcode += widgets.row([widgets.p(fundraise_info)])\n", - " htmlcode += widgets.row([fundraise_df.to_html()])\n", - "\n", - "if not ath.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"All time high\")])\n", - " htmlcode += widgets.row([ath.to_html()])\n", - "\n", - "if not atl.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"All time low\")])\n", - " htmlcode += widgets.row([atl.to_html()])\n", - "\n", - "if not market.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"All markets\")])\n", - " htmlcode += widgets.row([market.sample(n=20).sort_index().to_html(escape=False)])\n", - "\n", - "if not ex.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"Exchanges where loaded coin is listed\")])\n", - " htmlcode += widgets.row([ex.sample(n=20).sort_index().to_html()])\n", - "\n", - "if not trades.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"Last trades\")])\n", - " htmlcode += widgets.row([trades.tail(20).to_html()])\n", - "\n", - "if order_book_raw:\n", - " htmlcode += widgets.row([widgets.h(3, \"Order book\")])\n", - " htmlcode += widgets.row([order_book])\n", - "\n", - "if not ps.empty:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, \"Price and supply related metrics for loaded coin\")]\n", - " )\n", - " htmlcode += widgets.row([ps.to_html()])\n", - "\n", - "if not events.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"Events\")])\n", - " htmlcode += widgets.row([events.to_html(escape=False)])\n", - "\n", - "# TODO: print rich table\n", - "# htmlcode += widgets.row([widgets.h(3, \"Tweets for loaded coin\")])\n", - "# htmlcode += widgets.row([tweets])\n", - "# htmlcode += widgets.row([widgets.h(3, \"Social media\")])\n", - "# htmlcode += widgets.row([social.to_html()])\n", - "\n", - "if not gh_data.empty:\n", - " htmlcode += widgets.row([widgets.h(3, \"Github activity over time\")])\n", - " htmlcode += widgets.row([gh])\n", - "else:\n", - " htmlcode += widgets.row([widgets.h(3, \"Github activity over time\")])\n", - " htmlcode += widgets.row([widgets.p(\"No data available.\")])\n", - "\n", - "body += widgets.add_tab(\"Due Diligence\", htmlcode)\n", - "\n", - "# Technical Analysis tab\n", - "htmlcode = \"\"\n", - "htmlcode = widgets.row([widgets.h(3, \"Simple moving average\")])\n", - "htmlcode += widgets.row([sma])\n", - "htmlcode += widgets.row([widgets.h(3, \"Exponential moving average\")])\n", - "htmlcode += widgets.row([ema])\n", - "htmlcode += widgets.row([widgets.h(3, \"Zero lag exponential moving average\")])\n", - "htmlcode += widgets.row([zlma])\n", - "htmlcode += widgets.row([widgets.h(3, \"Commodity channel index\")])\n", - "htmlcode += widgets.row([cci])\n", - "htmlcode += widgets.row([widgets.h(3, \"Moving average convergence/divergence\")])\n", - "htmlcode += widgets.row([macd])\n", - "htmlcode += widgets.row([widgets.h(3, \"Fisher transform\")])\n", - "htmlcode += widgets.row([fisher])\n", - "htmlcode += widgets.row([widgets.h(3, \"Aroon indicator\")])\n", - "htmlcode += widgets.row([aroon])\n", - "htmlcode += widgets.row([widgets.h(3, \"Bollinger bands\")])\n", - "htmlcode += widgets.row([bbands])\n", - "htmlcode += widgets.row([widgets.h(3, \"Fibonacci retracement\")])\n", - "htmlcode += widgets.row([fib])\n", - "body += widgets.add_tab(\"Technical Analysis\", htmlcode)\n", - "\n", - "# Quantitative Analysis tab\n", - "htmlcode = \"\"\n", - "htmlcode = widgets.row([widgets.h(3, \"Summary\") + summary.to_html()])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Normality statistics and tests\") + normality.to_html()]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"Box and whisker plot\")])\n", - "htmlcode += widgets.row([bw])\n", - "htmlcode += widgets.row([widgets.h(3, \"Rolling mean and std deviation of prices\")])\n", - "htmlcode += widgets.row([rolling])\n", - "htmlcode += widgets.row([widgets.h(3, \"Rolling kurtosis of distribution of prices\")])\n", - "htmlcode += widgets.row([kurtosis])\n", - "body += widgets.add_tab(\"Quantitative Analysis\", htmlcode)\n", - "\n", - "body += widgets.tab_clickable_and_save_evt()\n", - "report = widgets.html_report(title=report_name, stylesheet=stylesheet, body=body)\n", - "\n", - "# # to save the results\n", - "with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n", - " fh.write(report)" - ] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.6" - }, - "vscode": { - "interpreter": { - "hash": "381a209f752e9104bff95ec5e80679ea2e476866b80e4326f1ee6b7658af6049" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/reports/templates/economy.ipynb b/openbb_terminal/reports/templates/economy.ipynb deleted file mode 100644 index c1206c81687..00000000000 --- a/openbb_terminal/reports/templates/economy.ipynb +++ /dev/null @@ -1,685 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "70ba2c8a", - "metadata": {}, - "source": [ - "## Notebook setup\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "driven-billion", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import io\n", - "import warnings\n", - "import datetime\n", - "import numpy as np\n", - "import pandas as pd\n", - "import base64\n", - "import pytz\n", - "\n", - "from IPython.display import HTML\n", - "\n", - "import openbb_terminal.config_terminal as cfg\n", - "from openbb_terminal.helper_funcs import get_user_timezone\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "from openbb_terminal.helper_classes import TerminalStyle\n", - "\n", - "\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4494cd3e", - "metadata": {}, - "outputs": [], - "source": [ - "# Suppressing sdk logs\n", - "from openbb_terminal.core.session.current_system import set_system_variable\n", - "\n", - "set_system_variable(\"LOGGING_SUPPRESS\", True)\n", - "\n", - "# Import the OpenBB SDK\n", - "from openbb_terminal.sdk import openbb, widgets" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f9feebc8", - "metadata": {}, - "outputs": [], - "source": [ - "from openbb_terminal.core.session.current_user import get_current_user\n", - "\n", - "user = get_current_user()\n", - "\n", - "cfg.theme = TerminalStyle(\n", - " user.preferences.MPL_STYLE,\n", - " user.preferences.PMF_STYLE,\n", - " user.preferences.RICH_STYLE,\n", - ")\n", - "theme.apply_style(\"light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e6e39973", - "metadata": {}, - "outputs": [], - "source": [ - "stylesheet = widgets.html_report_stylesheet()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "daef64ec", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "# Parameters that will be replaced when calling this notebook\n", - "# Do not leave parameters blank as notebook will not run otherwise\n", - "report_name = \"economy_report\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1be26dae-cafe-4a22-80aa-eff296fc1a9b", - "metadata": {}, - "outputs": [], - "source": [ - "author = \"OpenBB\"\n", - "report_title = f\"Economy report\"\n", - "user_time_zone = pytz.timezone(get_user_timezone())\n", - "report_date = pd.Timestamp.now(tz=user_time_zone).strftime(\"%d %B, %Y\")\n", - "report_time = pd.Timestamp.now(tz=user_time_zone).strftime(\"%H:%M\")\n", - "report_timezone = pd.Timestamp.now(tz=user_time_zone).tzinfo\n", - "report_title, report_date, report_time, report_timezone" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "9363f19a", - "metadata": {}, - "source": [ - "## Data\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "0b9ec3f9", - "metadata": {}, - "source": [ - "Summary\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e3b15d6f-dc9e-4f97-aa07-37d949a4e4bb", - "metadata": {}, - "outputs": [], - "source": [ - "overview = openbb.economy.overview()\n", - "overview.set_index(list(overview.columns[0]), inplace=True)\n", - "overview = OpenBBFigure.to_table(overview, print_index=True, width=600)\n", - "\n", - "futures = openbb.economy.futures()\n", - "futures.set_index(list(futures.columns[0]), inplace=True)\n", - "futures[\"%Chg\"] = futures[\"%Chg\"].apply(lambda x: float(f\"{x:.2f}\"))\n", - "futures_fig = OpenBBFigure.to_table(futures, print_index=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "351d0451", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " news = openbb.news(\"economy\")\n", - " news.columns = news.columns.str.title()\n", - " news = news.rename(\n", - " columns={\"Date\": \"Published\", \"Description\": \"Title\", \"Url\": \"Link\"}\n", - " )\n", - " news[\"Title\"] = news.apply(\n", - " lambda x: f'{x[\"Title\"]}', axis=1\n", - " )\n", - " news = news[[\"Title\", \"Published\"]]\n", - " news[\"Published\"] = pd.to_datetime(news[\"Published\"]).dt.strftime(\"%Y-%m-%d %H:%M\")\n", - " news = news.set_index(\"Published\")\n", - "except:\n", - " news = pd.DataFrame()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "7315ac99", - "metadata": {}, - "source": [ - "Events\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a02c6166", - "metadata": {}, - "outputs": [], - "source": [ - "events_1 = openbb.economy.events(\n", - " countries=\"United States\",\n", - " start_date=datetime.datetime.now().strftime(\"%Y-%m-%d\"),\n", - ")\n", - "if not events_1.empty:\n", - " events_1 = OpenBBFigure.to_table(\n", - " events_1.set_index(\"Date\"), print_index=True, height=200\n", - " )\n", - "\n", - "events_2 = openbb.economy.events(\n", - " countries=[\n", - " \"Germany\",\n", - " \"Spain\",\n", - " \"Italy\",\n", - " \"United Kingdom\",\n", - " \"France\",\n", - " \"Portugal\",\n", - " \"Greece\",\n", - " ],\n", - " start_date=datetime.datetime.now().strftime(\"%Y-%m-%d\"),\n", - ")\n", - "if not events_2.empty:\n", - " events_2 = OpenBBFigure.to_table(events_2.set_index(\"Date\"), print_index=True)\n", - "\n", - "events_3 = openbb.economy.events(\n", - " countries=\"China\",\n", - " start_date=datetime.datetime.now().strftime(\"%Y-%m-%d\"),\n", - ")\n", - "if not events_3.empty:\n", - " events_3 = OpenBBFigure.to_table(events_3.set_index(\"Date\"), print_index=True)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "f1863bd0", - "metadata": {}, - "source": [ - "Yields\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8e332ca6", - "metadata": {}, - "outputs": [], - "source": [ - "ycrv = openbb.fixedincome.ycrv(return_date=True)\n", - "\n", - "if (isinstance(ycrv, pd.DataFrame) and not ycrv.empty) or (\n", - " not isinstance(ycrv, pd.DataFrame) and ycrv\n", - "):\n", - " ycrv_data_1, _ = ycrv\n", - " ycrv_country_1 = \"United states\"\n", - "\n", - " ycrv_chart_1 = openbb.fixedincome.ycrv_chart(external_axes=True)\n", - "\n", - " if ycrv_chart_1:\n", - " ycrv_chart_1 = ycrv_chart_1.to_html()\n", - "\n", - " if not ycrv_data_1.empty:\n", - " ycrv_data_1.drop([\"Maturity\"], axis=1, inplace=True)\n", - " ycrv_data_1.index = [\n", - " \"1M\",\n", - " \"3M\",\n", - " \"6M\",\n", - " \"1Y\",\n", - " \"2Y\",\n", - " \"3Y\",\n", - " \"5Y\",\n", - " \"7Y\",\n", - " \"10Y\",\n", - " \"20Y\",\n", - " \"30Y\",\n", - " ]\n", - "else:\n", - " ycrv = None" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c4f987af", - "metadata": {}, - "outputs": [], - "source": [ - "subplots = OpenBBFigure.create_subplots(\n", - " 2,\n", - " 2,\n", - " vertical_spacing=0.15,\n", - " row_heights=[0.5, 0.5],\n", - " subplot_titles=(\"UST 1m-1y\", \"UST 10y-30y\", \"UST 1y-10y\"),\n", - " specs=[[{\"type\": \"xy\"}, {\"type\": \"xy\"}], [{\"colspan\": 2}, None]],\n", - ")\n", - "openbb.economy.treasury_chart(\n", - " maturities=[\"1m\", \"3m\", \"6m\", \"1y\"],\n", - " start_date=\"2020-01-01\",\n", - " external_axes=True,\n", - ").to_subplot(subplots, 1, 1)\n", - "\n", - "openbb.economy.treasury_chart(\n", - " maturities=[\"10y\", \"20y\", \"30y\"],\n", - " start_date=\"2020-01-01\",\n", - " external_axes=True,\n", - ").to_subplot(subplots, 1, 2)\n", - "\n", - "openbb.economy.treasury_chart(\n", - " maturities=[\"1y\", \"5y\", \"7y\", \"10y\"],\n", - " start_date=\"2020-01-01\",\n", - " external_axes=True,\n", - ").to_subplot(subplots, 2, 1)\n", - "\n", - "subplots.update_traces(legend=\"legend\", row=1, col=1)\n", - "subplots.update_traces(legend=\"legend2\", row=1, col=2)\n", - "subplots.update_traces(legend=\"legend3\", row=2, col=1)\n", - "defaults = dict(\n", - " xanchor=\"left\",\n", - " yanchor=\"top\",\n", - " xref=\"paper\",\n", - " yref=\"paper\",\n", - " bgcolor=\"rgba(0, 0, 0, 0)\",\n", - ")\n", - "subplots.update_layout(\n", - " legend=dict(x=0.01, y=0.99, **defaults),\n", - " legend2=dict(x=0.562, y=0.99, **defaults),\n", - " legend3=dict(x=0.01, y=0.41, **defaults),\n", - ")\n", - "\n", - "\n", - "ust = subplots.to_html()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "7a3c421f", - "metadata": {}, - "source": [ - "Equity Market\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12c98e06", - "metadata": {}, - "outputs": [], - "source": [ - "valuation = OpenBBFigure.to_table(openbb.economy.valuation(), print_index=False)\n", - "performance = OpenBBFigure.to_table(openbb.economy.performance(), print_index=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "42f5570d", - "metadata": {}, - "outputs": [], - "source": [ - "fig = OpenBBFigure(yaxis_title=\"Cumulative returns (%)\")\n", - "data = openbb.economy.index(\n", - " indices=[\"sp500\", \"dow_djus\", \"nasdaq100\", \"russell3000\", \"cboe_vix\"],\n", - " start_date=\"2000-03-09\",\n", - ")\n", - "\n", - "data = data.pct_change().dropna()\n", - "data = (1 + data.shift(periods=1, fill_value=0)).cumprod() - 1\n", - "for col in data.columns:\n", - " fig.add_scatter(\n", - " x=data.index,\n", - " y=data[col] * 100,\n", - " name=col,\n", - " )\n", - "\n", - "eqty_0 = fig.update_layout(margin=dict(l=20, r=60), legend=dict(x=1.1)).to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "286a82d7", - "metadata": {}, - "outputs": [], - "source": [ - "fig = OpenBBFigure(yaxis_title=\"Cumulative returns (%)\")\n", - "data = openbb.economy.index(\n", - " indices=[\"ftse100\", \"de_dax40\", \"fr_cac40\", \"it_mib40\", \"es_ibex35\"],\n", - " start_date=\"2000-01-01\",\n", - ")\n", - "\n", - "data = data.pct_change().dropna()\n", - "data = (1 + data.shift(periods=1, fill_value=0)).cumprod() - 1\n", - "for col in data.columns:\n", - " fig.add_scatter(\n", - " x=data.index,\n", - " y=data[col] * 100,\n", - " name=col,\n", - " )\n", - "\n", - "eqty_1 = fig.update_layout(margin=dict(l=20, r=60), legend=dict(x=1.1)).to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e825e8ed", - "metadata": {}, - "outputs": [], - "source": [ - "fig = OpenBBFigure(yaxis_title=\"Cumulative returns (%)\")\n", - "data = openbb.economy.index(\n", - " indices=[\"jp_n225\", \"cn_csi300\", \"hk_china\", \"au_asx50\"], start_date=\"2014-01-01\"\n", - ")\n", - "\n", - "data = data.pct_change().dropna()\n", - "data = (1 + data.shift(periods=1, fill_value=0)).cumprod() - 1\n", - "for col in data.columns:\n", - " fig.add_scatter(\n", - " x=data.index,\n", - " y=data[col] * 100,\n", - " name=col,\n", - " )\n", - "\n", - "eqty_2 = fig.update_layout(margin=dict(l=0, r=60), legend=dict(x=1.1)).to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8eb1327b", - "metadata": {}, - "outputs": [], - "source": [ - "fig = OpenBBFigure(yaxis_title=\"Cumulative returns (%)\")\n", - "data = openbb.economy.index(\n", - " indices=[\"in_nse50\", \"br_bvsp\", \"za_jo\", \"mx_ipc\", \"ru_moex\"],\n", - " start_date=\"2013-01-01\",\n", - ")\n", - "\n", - "data = data.pct_change().dropna()\n", - "data = (1 + data.shift(periods=1, fill_value=0)).cumprod() - 1\n", - "for col in data.columns:\n", - " fig.add_scatter(\n", - " x=data.index,\n", - " y=data[col] * 100,\n", - " name=col,\n", - " )\n", - "\n", - "eqty_3 = fig.update_layout(margin=dict(l=20, r=60), legend=dict(x=1.1)).to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "02630bd6", - "metadata": {}, - "outputs": [], - "source": [ - "fig = OpenBBFigure(yaxis_title=\"Cumulative returns (%)\")\n", - "data = openbb.economy.index(indices=[\"arca_xoi\", \"arca_pse\"], start_date=\"2000-01-01\")\n", - "\n", - "data = data.pct_change().dropna()\n", - "data = (1 + data.shift(periods=1, fill_value=0)).cumprod() - 1\n", - "for col in data.columns:\n", - " fig.add_scatter(\n", - " x=data.index,\n", - " y=data[col] * 100,\n", - " name=col,\n", - " )\n", - "\n", - "eqty_4 = fig.update_layout(margin=dict(l=20, r=60), legend=dict(x=1.1)).to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1947a06c", - "metadata": {}, - "outputs": [], - "source": [ - "subplots = OpenBBFigure.create_subplots(1, 1, specs=[[{\"secondary_y\": True}]])\n", - "\n", - "openbb.economy.macro_chart(\n", - " parameters=[\"CONF\", \"IP\"], start_date=\"2010-01-01\", external_axes=True\n", - ").to_subplot(subplots, 1, 1, secondary_y=False)\n", - "subplots.update_yaxes(title_text=\"Confidence and IP\", secondary_y=False)\n", - "\n", - "\n", - "df, df_dict, _ = openbb.economy.macro(parameters=[\"CPI\"], start_date=\"2010-01-01\")\n", - "cpi_chg = df.pct_change(1) * 100\n", - "subplots.add_scatter(\n", - " x=cpi_chg.index,\n", - " y=cpi_chg.T.values[0],\n", - " name=\"CPI (% change)\",\n", - " mode=\"lines\",\n", - " line=dict(color=\"red\", dash=\"dash\"),\n", - " secondary_y=True,\n", - " yaxis=\"y2\",\n", - ")\n", - "subplots.update_yaxes(\n", - " title_text=\"CPI (% change)\",\n", - " secondary_y=True,\n", - " range=[-2, 2],\n", - " side=\"left\",\n", - " overlaying=\"y\",\n", - ")\n", - "subplots.horizontal_legend()\n", - "\n", - "macro_0 = subplots.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "971e4c51", - "metadata": {}, - "outputs": [], - "source": [ - "fred = openbb.economy.fred(series_ids=[\"T10Y3M\"], start_date=\"1980-01-01\")\n", - "\n", - "if (isinstance(fred, pd.DataFrame) and not fred.empty) or (\n", - " not isinstance(fred, pd.DataFrame) and fred\n", - "):\n", - " df, df_dict = fred\n", - "\n", - " fig = OpenBBFigure.create_subplots(1, 1, specs=[[{\"secondary_y\": True}]])\n", - " fig.add_scatter(\n", - " x=df.index,\n", - " y=df[\"T10Y3M\"],\n", - " name=df_dict[\"T10Y3M\"][\"title\"],\n", - " line=dict(color=\"red\", dash=\"dash\"),\n", - " secondary_y=False,\n", - " )\n", - " openbb.economy.fred_chart(\n", - " series_ids=[\"A191RP1A027NBEA\"],\n", - " start_date=\"1980-01-01\",\n", - " external_axes=True,\n", - " ).to_subplot(fig, 1, 1, secondary_y=True)\n", - " fig.update_yaxes(secondary_y=True, side=\"left\", overlaying=\"y\")\n", - " fig.set_title(\"\")\n", - " fig.horizontal_legend()\n", - "\n", - " fred_0 = fig.to_html()\n", - "else:\n", - " fred = None" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ac501a32-124d-4d21-aae1-a8d147bb1071", - "metadata": {}, - "source": [ - "## Render the report template to a file\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "abfe37e4-ea38-42c3-8fb5-7cceb233fb12", - "metadata": {}, - "outputs": [], - "source": [ - "body = \"\"\n", - "\n", - "\n", - "body += widgets.header(\n", - " author,\n", - " report_date,\n", - " report_time,\n", - " report_timezone,\n", - " f\"ECONOMY REPORT: Leverage multiple sources to create custom reports.\",\n", - " plotly_js=True,\n", - ")\n", - "\n", - "body += widgets.tablinks(\n", - " [\n", - " \"SUMMARY\",\n", - " \"Events\",\n", - " \"Yields\",\n", - " \"Equity Market\",\n", - " \"Macro Data\",\n", - " ]\n", - ")\n", - "\n", - "# Summary\n", - "htmlcode = widgets.row(\n", - " [widgets.h(3, \"Global markets (source: WSJ)\") + overview.to_html()]\n", - ")\n", - "\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Commodities (source: WSJ)\") + futures_fig.to_html()]\n", - ")\n", - "\n", - "if not news.empty:\n", - " htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, \"Top news on 'economy' keyword (source: Feedparser)\")\n", - " + news.to_html(escape=False)\n", - " ]\n", - " )\n", - "\n", - "body += widgets.add_tab(\"SUMMARY\", htmlcode)\n", - "\n", - "# Events\n", - "htmlcode = widgets.row([widgets.h(3, \"Economic calendars (source: investing.com)\")])\n", - "htmlcode += widgets.row([widgets.h(3, \"United States\") + events_1.to_html()])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Select European Countries\") + events_2.to_html()]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"China\") + events_3.to_html()])\n", - "body += widgets.add_tab(\"Events\", htmlcode)\n", - "\n", - "# Yields\n", - "htmlcode = widgets.row([widgets.h(3, \"US treasuries (source: FED)\") + ust])\n", - "\n", - "if ycrv:\n", - " htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, f\"{ycrv_country_1} yield curve (source: FRED)\")\n", - " + (ycrv_chart_1 or \"\")\n", - " ]\n", - " )\n", - " htmlcode += widgets.row([widgets.h(3, \"\") + ycrv_data_1.to_html()])\n", - "\n", - "body += widgets.add_tab(\"Yields\", htmlcode)\n", - "\n", - "# Equity market\n", - "htmlcode = widgets.row(\n", - " [widgets.h(3, \"Valuation (source: Finviz)\") + valuation.to_html()]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Performance (source: Finviz)\") + performance.to_html()]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"US markets (source: YahooFinance)\") + eqty_0])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"European markets (source: YahooFinance)\") + eqty_1]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"APAC markets (source: YahooFinance)\") + eqty_2])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Emerging markets (source: YahooFinance)\") + eqty_3]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"ARCA (source: YahooFinance)\") + eqty_4])\n", - "body += widgets.add_tab(\"Equity Market\", htmlcode)\n", - "\n", - "# Macro\n", - "htmlcode = \"\"\n", - "if not bigmac_res.empty:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, \"Bigmac index (source: Nasdaq)\") + (bigmac or \"\")]\n", - " )\n", - "htmlcode += widgets.row([widgets.h(3, \"Leading indicators (source: EconDB)\") + macro_0])\n", - "if fred:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, \"Recession indicator (source: FRED)\") + fred_0]\n", - " )\n", - "body += widgets.add_tab(\"Macro Data\", htmlcode)\n", - "\n", - "body += widgets.tab_clickable_and_save_evt()\n", - "report = widgets.html_report(title=report_name, stylesheet=stylesheet, body=body)\n", - "\n", - "# to save the results\n", - "with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n", - " fh.write(report)" - ] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - }, - "vscode": { - "interpreter": { - "hash": "381a209f752e9104bff95ec5e80679ea2e476866b80e4326f1ee6b7658af6049" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/reports/templates/equity.ipynb b/openbb_terminal/reports/templates/equity.ipynb deleted file mode 100644 index bb144bab6da..00000000000 --- a/openbb_terminal/reports/templates/equity.ipynb +++ /dev/null @@ -1,1410 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "70ba2c8a", - "metadata": {}, - "source": [ - "## Notebook setup\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "driven-billion", - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "import datetime\n", - "import numpy as np\n", - "import pandas as pd\n", - "\n", - "from IPython.display import HTML\n", - "\n", - "from openbb_terminal import config_terminal as cfg\n", - "from openbb_terminal.helper_classes import TerminalStyle\n", - "from openbb_terminal import OpenBBFigure\n", - "from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA\n", - "\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "150e5103", - "metadata": {}, - "outputs": [], - "source": [ - "# Suppressing sdk logs\n", - "from openbb_terminal.core.session.current_system import set_system_variable\n", - "\n", - "set_system_variable(\"LOGGING_SUPPRESS\", True)\n", - "\n", - "# Import the OpenBB SDK\n", - "from openbb_terminal.sdk import openbb, widgets" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cf31bcc7", - "metadata": {}, - "outputs": [], - "source": [ - "from openbb_terminal.core.session.current_user import get_current_user\n", - "\n", - "user = get_current_user()\n", - "\n", - "from openbb_terminal import theme\n", - "\n", - "cfg.theme = TerminalStyle(\n", - " user.preferences.MPL_STYLE,\n", - " user.preferences.PMF_STYLE,\n", - " user.preferences.RICH_STYLE,\n", - ")\n", - "theme.apply_style(\"light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3fe7db5-ec6a-42cf-9e66-52dc1de22370", - "metadata": {}, - "outputs": [], - "source": [ - "stylesheet = widgets.html_report_stylesheet()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "807fda4e", - "metadata": {}, - "source": [ - "## Select symbol\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "intended-sherman", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "# Parameters that will be replaced when calling this notebook\n", - "# Do not leave parameters blank as notebook will not run otherwise\n", - "symbol = \"MSFT\"\n", - "report_name = f\"Equity report for {symbol}\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a441ddbc-d75b-4a18-84d4-e01b677674d3", - "metadata": {}, - "outputs": [], - "source": [ - "if \".\" in symbol:\n", - " import sys\n", - "\n", - " sys.exit(0)\n", - "symbol = symbol.upper()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1be26dae-cafe-4a22-80aa-eff296fc1a9b", - "metadata": {}, - "outputs": [], - "source": [ - "ticker_data = openbb.stocks.load(\n", - " symbol=symbol, start_date=datetime.datetime.now() - datetime.timedelta(days=18 * 30)\n", - ")\n", - "ticker_data = openbb.stocks.process_candle(data=ticker_data)\n", - "\n", - "author = \"\"\n", - "report_title = f\"Investment Research Report on {symbol.upper()}\"\n", - "report_date = datetime.datetime.now().strftime(\"%d %B, %Y\")\n", - "report_time = datetime.datetime.now().strftime(\"%H:%M\")\n", - "report_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo\n", - "report_title, report_date, report_time, report_timezone" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e3b15d6f-dc9e-4f97-aa07-37d949a4e4bb", - "metadata": {}, - "outputs": [], - "source": [ - "info = openbb.stocks.fa.overview(symbol=symbol, source=\"Finviz\").transpose()\n", - "\n", - "info\n", - "try:\n", - " company_name = info[\"Company\"][0]\n", - "except:\n", - " company_name = \"N/A\"\n", - "try:\n", - " company_sector = info[\"Sector\"][0]\n", - "except:\n", - " company_sector = \"N/A\"\n", - "\n", - "try:\n", - " company_industry = info[\"Industry\"][0]\n", - "except:\n", - " company_industry = \"N/A\"\n", - "\n", - "\n", - "overview = [company_name, company_sector, company_industry]" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "f8a0d747", - "metadata": {}, - "source": [ - "## Data\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aca4b6ef", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " (\n", - " df_year_estimates,\n", - " df_quarter_earnings,\n", - " df_quarter_revenues,\n", - " ) = openbb.stocks.fa.est(symbol=symbol)\n", - " df_quarter_revenues.transpose()\n", - "except Exception as e:\n", - " print(e)\n", - " df_year_estimates = pd.DataFrame()\n", - " df_quarter_earnings = pd.DataFrame()\n", - " df_quarter_revenues = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c0f0a1ca", - "metadata": {}, - "outputs": [], - "source": [ - "display_year = sorted(df_year_estimates.columns.tolist())[:3]\n", - "df_year_estimates = df_year_estimates[display_year].head(5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6976c1d5", - "metadata": {}, - "outputs": [], - "source": [ - "tables = openbb.news(symbol)\n", - "\n", - "for table in tables:\n", - " if not isinstance(table, pd.DataFrame):\n", - " continue\n", - "\n", - " table[0].loc[\"URL\"] = table[0].loc[\"URL\"].apply(lambda x: f'{x}')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "38e38d7c", - "metadata": {}, - "outputs": [], - "source": [ - "quote_data = info[[\"Price\", \"Prev Close\", \"Change\", \"Volume\", \"52W Range\", \"Perf YTD\"]]\n", - "quote_data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fde46353", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " df_institutional_shareholders = openbb.stocks.fa.shrs(\n", - " symbol, holder=\"institutional\"\n", - " )\n", - " df_institutional_shareholders.index += 1\n", - " df_institutional_shareholders\n", - "except Exception as e:\n", - " print(e)\n", - " df_institutional_shareholders = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3022130e", - "metadata": {}, - "outputs": [], - "source": [ - "# this command works 1/2 the time - really need to fix this - but this will catch the errors and stop it from breaking the report\n", - "try:\n", - " df_sec_filings = openbb.stocks.fa.sec(symbol)[\n", - " [\"Company Name\", \"Reporting Owner\", \"Form Type\", \"Period\", \"View\"]\n", - " ].head(5)\n", - " df_sec_filings\n", - "except:\n", - " df_sec_filings = \"Error with Nasdaq API\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4662b1ac", - "metadata": {}, - "outputs": [], - "source": [ - "df_analyst = openbb.stocks.fa.pt(symbol=symbol).sort_index(ascending=False)\n", - "# print(df_analyst)\n", - "# if not df_analyst.empty:\n", - "# if \"target\" in df_analyst.columns:\n", - "# if \"target_to\" in df_analyst.columns:\n", - "# df_analyst[\"target\"] = df_analyst[\"target_to\"].combine_first(\n", - "# df_analyst[\"target\"]\n", - "# )\n", - "# else:\n", - "# df_analyst[\"target\"] = df_analyst[\"target\"]\n", - "# df_analyst = df_analyst[[\"category\", \"analyst\", \"rating\", \"target\"]].rename(\n", - "# columns={\n", - "# \"category\": \"Category\",\n", - "# \"analyst\": \"Analyst\",\n", - "# \"rating\": \"Rating\",\n", - "# \"target\": \"Price Target\",\n", - "# }\n", - "# )\n", - "df_analyst.head(10)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "df8bdb7d", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " df_rating = openbb.stocks.fa.rating(symbol)\n", - " df_rating\n", - "except Exception as e:\n", - " print(e)\n", - " df_rating = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0df953b", - "metadata": {}, - "outputs": [], - "source": [ - "pcr_chart = openbb.stocks.options.pcr_chart(symbol, window=30, external_axes=True)\n", - "\n", - "if pcr_chart:\n", - " pcr_chart = pcr_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bd79195c", - "metadata": {}, - "outputs": [], - "source": [ - "current_price = 0\n", - "try:\n", - " current_price = float(ticker_data[\"Adj Close\"][-1])\n", - " expiry_dates = openbb.stocks.options.expirations(symbol)\n", - "\n", - " if expiry_dates:\n", - " exp = expiry_dates[0]\n", - " option_chain = openbb.stocks.options.chains(symbol, expiration=exp)\n", - "except Exception as e:\n", - " print(e)\n", - " expiry_dates = []\n", - " option_chain = pd.DataFrame()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ad6d0c24", - "metadata": {}, - "outputs": [], - "source": [ - "if expiry_dates:\n", - " vol_chart = openbb.stocks.options.vol(\n", - " option_chain, current_price, symbol, exp, external_axes=True\n", - " )\n", - "\n", - " if vol_chart:\n", - " vol_chart = vol_chart.to_html()\n", - "\n", - " voi_chart = openbb.stocks.options.voi(\n", - " option_chain, current_price, symbol, exp, external_axes=True\n", - " )\n", - "\n", - " if voi_chart:\n", - " voi_chart = voi_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "324e2d4b-b2f6-4da3-bac4-ef785ad4e436", - "metadata": {}, - "outputs": [], - "source": [ - "net_short_position = openbb.stocks.dps.spos_chart(\n", - " symbol=symbol,\n", - " limit=84,\n", - " raw=False,\n", - " export=\"\",\n", - " external_axes=True,\n", - ")\n", - "\n", - "if net_short_position:\n", - " net_short_position = net_short_position.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ac0bfbda-e4b8-4b3b-b8e7-c02639fdb971", - "metadata": {}, - "outputs": [], - "source": [ - "dark_pools = openbb.stocks.dps.dpotc_chart(symbol=symbol, external_axes=True)\n", - "\n", - "if dark_pools:\n", - " dark_pools = dark_pools.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "22a7266f", - "metadata": {}, - "outputs": [], - "source": [ - "gtrades_chart = openbb.stocks.gov.gtrades_chart(\n", - " symbol,\n", - " past_transactions_months=12,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if gtrades_chart:\n", - " gtrades_chart = gtrades_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4c670ba3", - "metadata": {}, - "outputs": [], - "source": [ - "gov_contracts_chart = openbb.stocks.gov.contracts_chart(\n", - " symbol,\n", - " past_transaction_days=365,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if gov_contracts_chart:\n", - " gov_contracts_chart = gov_contracts_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "caa3dae2", - "metadata": {}, - "outputs": [], - "source": [ - "google_mentions_chart = openbb.stocks.ba.mentions_chart(\n", - " symbol,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if google_mentions_chart:\n", - " google_mentions_chart = google_mentions_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b77e60d8", - "metadata": {}, - "outputs": [], - "source": [ - "google_regions_chart = openbb.stocks.ba.regions_chart(\n", - " symbol,\n", - " limit=10,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if google_regions_chart:\n", - " google_regions_chart = google_regions_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78593369", - "metadata": {}, - "outputs": [], - "source": [ - "similar_companies = openbb.stocks.ca.similar(symbol, source=\"Finviz\")[0:5]\n", - "if similar_companies:\n", - " similar_companies.append(symbol)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4639665b", - "metadata": {}, - "outputs": [], - "source": [ - "historical_similar = openbb.stocks.ca.hist_chart(\n", - " similar_companies,\n", - " external_axes=True,\n", - " normalize=False,\n", - ")\n", - "\n", - "if historical_similar:\n", - " historical_similar = historical_similar.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "74565e53", - "metadata": {}, - "outputs": [], - "source": [ - "hcorr_similar = openbb.stocks.ca.hcorr_chart(\n", - " similar_companies,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if hcorr_similar:\n", - " hcorr_similar = hcorr_similar.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "936babfd", - "metadata": {}, - "outputs": [], - "source": [ - "vol_similar = openbb.stocks.ca.volume_chart(\n", - " similar_companies,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if vol_similar:\n", - " vol_similar = vol_similar.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "99388d4e", - "metadata": {}, - "outputs": [], - "source": [ - "scorr_similar = openbb.stocks.ca.scorr_chart(\n", - " similar_companies,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if scorr_similar:\n", - " scorr_similar = scorr_similar.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eaa67343", - "metadata": {}, - "outputs": [], - "source": [ - "income_comparison = openbb.stocks.ca.income(similar_companies)\n", - "income_comparison" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "13ad71cf", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " df_related_queries = openbb.stocks.ba.queries(symbol)\n", - " df_related_queries.index += 1\n", - " df_related_queries.index\n", - "except:\n", - " pass" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "58c06129", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " df_rising_queries = openbb.stocks.ba.rise(symbol)\n", - " df_rising_queries.index += 1\n", - " df_rising_queries\n", - "except:\n", - " pass" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0a467f1a", - "metadata": {}, - "outputs": [], - "source": [ - "gov_histcont_chart = openbb.stocks.gov.histcont_chart(\n", - " symbol,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if gov_histcont_chart:\n", - " gov_histcont_chart = gov_histcont_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "730cae26", - "metadata": {}, - "outputs": [], - "source": [ - "df_lobbying = openbb.stocks.gov.lobbying(symbol, limit=5)\n", - "df_lobbying" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8bb53c1-147e-4342-af61-0d0a9cc56e59", - "metadata": {}, - "outputs": [], - "source": [ - "price_vs_short_interest = openbb.stocks.dps.psi_sg_chart(\n", - " symbol=symbol, external_axes=True\n", - ")\n", - "\n", - "if price_vs_short_interest:\n", - " price_vs_short_interest = price_vs_short_interest.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "394668e1-1158-4a2b-8a38-04c72a6abdd6", - "metadata": {}, - "outputs": [], - "source": [ - "price_chart = openbb.stocks.candle(\n", - " symbol=symbol,\n", - " data=ticker_data,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if price_chart:\n", - " price_chart = price_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "900686a3", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " price_target_chart = openbb.stocks.fa.pt_chart(\n", - " symbol=symbol,\n", - " start_date=\"2022-01-01\",\n", - " data=ticker_data,\n", - " limit=10,\n", - " raw=False,\n", - " external_axes=True,\n", - " )\n", - "\n", - " if price_target_chart:\n", - " price_target_chart = price_target_chart.to_html()\n", - "except Exception:\n", - " price_target_chart = OpenBBFigure()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "61d62e1a-199e-481d-b57d-38ab486951be", - "metadata": {}, - "outputs": [], - "source": [ - "df = df_analyst.copy()\n", - "avg_ratings_last_30_days = 0\n", - "days = 0\n", - "if not df.empty:\n", - " df_ratings = df[datetime.datetime.now() - datetime.timedelta(days=days) :]\n", - " while df_ratings.empty:\n", - " days += 30\n", - " df_ratings = df[datetime.datetime.now() - datetime.timedelta(days=days) :]\n", - "\n", - " if days > 100:\n", - " break\n", - "\n", - " if not df_ratings.empty:\n", - " avg_ratings_last_30_days = round(np.mean(df_ratings[\"Price Target\"].values), 2)\n", - " else:\n", - " avg_ratings = 0\n", - "\n", - "last_price = round(ticker_data[\"Close\"][-1], 2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f3733ff2", - "metadata": {}, - "outputs": [], - "source": [ - "ratings_over_time_chart = openbb.stocks.fa.rot_chart(\n", - " symbol=symbol,\n", - " limit=10,\n", - " raw=False,\n", - " export=\"\",\n", - " external_axes=True,\n", - ")\n", - "\n", - "if ratings_over_time_chart:\n", - " ratings_over_time_chart = ratings_over_time_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "924f8042-e58a-449c-91a3-7923158ff714", - "metadata": {}, - "outputs": [], - "source": [ - "ta_rsi = openbb.ta.rsi_chart(ticker_data[\"Close\"], external_axes=True)\n", - "\n", - "if ta_rsi:\n", - " ta_rsi = ta_rsi.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "953a340e-7ea1-4aa3-ae1d-872683dced41", - "metadata": {}, - "outputs": [], - "source": [ - "df = openbb.ta.rsi(ticker_data[\"Close\"])\n", - "rsi_value = round(df.values[-1][0], 2)\n", - "rsi_value" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dce71711-20b3-4c69-af52-e0f9a69991f8", - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.linear_model import LinearRegression\n", - "\n", - "model = LinearRegression().fit(\n", - " np.array(range(len(ticker_data[\"Close\"][-30:].index))).reshape(-1, 1),\n", - " ticker_data[\"Close\"][-30:].values,\n", - ")\n", - "regression_slope = round(model.coef_[0], 2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6272e34b-9511-4b48-9eea-ea882ce9846a", - "metadata": {}, - "outputs": [], - "source": [ - "df_insider = pd.DataFrame.from_dict(openbb.stocks.ins.lins(symbol=symbol)).head(10)\n", - "if not df_insider.empty:\n", - " df_insider[\"Val ($)\"] = df_insider[\"Value ($)\"].replace({\",\": \"\"}, regex=True)\n", - " df_insider[\"Trade\"] = df_insider.apply(\n", - " lambda row: (-1 * float(row[\"Val ($)\"]))\n", - " if row[\"Transaction\"] == \"Sale\"\n", - " else (float(row[\"Val ($)\"]) if row[\"Transaction\"] == \"Buy\" else 0),\n", - " axis=1,\n", - " )\n", - " last_10_insider_trading = round(sum(df_insider[\"Trade\"]) / 1_000_000, 2)\n", - " df_insider = df_insider.drop(columns=[\"Val ($)\", \"Trade\"])\n", - " df_insider" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29254666-58c6-4686-87e6-2635ad8387e2", - "metadata": {}, - "outputs": [], - "source": [ - "finbrain_sentiment = openbb.stocks.ba.headlines_chart(symbol=symbol, external_axes=True)\n", - "\n", - "if finbrain_sentiment:\n", - " finbrain_sentiment = finbrain_sentiment.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b8251b47-8ecc-47be-83f3-d9f108596898", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " df_sentiment_finbrain = openbb.stocks.ca.sentiment(symbols=[symbol])\n", - " finbrain_sentiment_val = float(df_sentiment_finbrain.values[-1][0])\n", - "except Exception as e:\n", - " finbrain_sentiment_val = \"N/A\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6fe6e513-d568-41b4-8aa0-37cc578c9dc3", - "metadata": {}, - "outputs": [], - "source": [ - "(\n", - " watchlist_count,\n", - " n_cases,\n", - " n_bull,\n", - " n_bear,\n", - ") = openbb.stocks.ba.bullbear(symbol=symbol)\n", - "stocktwits_sentiment = f\"Watchlist count: {watchlist_count}
\"\n", - "if n_cases > 0:\n", - " stocktwits_sentiment += f\"\\nLast {n_cases} sentiment messages:
\"\n", - " stocktwits_sentiment += f\"Bullish {round(100*n_bull/n_cases, 2)}%
\"\n", - " stocktwits_sentiment += f\"Bearish {round(100*n_bear/n_cases, 2)}%\"\n", - "else:\n", - " stocktwits_sentiment += \"No messages found\"\n", - "stocktwits_sentiment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bfb75909-b877-4b3a-82a7-21ce174a7bb4", - "metadata": {}, - "outputs": [], - "source": [ - "snews = openbb.stocks.ba.snews_chart(symbol, external_axes=True)\n", - "\n", - "if snews:\n", - " snews = snews.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "329c9e1b-e1de-4836-91fd-656cc87f7111", - "metadata": {}, - "outputs": [], - "source": [ - "ticker_data_all = openbb.stocks.load(\n", - " symbol=symbol,\n", - " start_date=datetime.datetime.now() - datetime.timedelta(days=5 * 12 * 21),\n", - " verbose=False,\n", - ")\n", - "ticker_data_all[\"Returns\"] = ticker_data_all[\"Adj Close\"].pct_change()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "37743ef3-2f16-4166-bfcc-a0fe3eb959bc", - "metadata": {}, - "outputs": [], - "source": [ - "bw_month = openbb.qa.bw(\n", - " ticker_data_all,\n", - " \"Returns\",\n", - " symbol,\n", - " yearly=False,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if bw_month:\n", - " bw_month = bw_month.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "16b33868-23dc-4c77-ac00-acb452da386c", - "metadata": {}, - "outputs": [], - "source": [ - "bw_year = openbb.qa.bw(\n", - " ticker_data_all,\n", - " \"Returns\",\n", - " symbol,\n", - " yearly=True,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if bw_year:\n", - " bw_year = bw_year.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "28ce0827", - "metadata": {}, - "outputs": [], - "source": [ - "income_df = openbb.stocks.fa.income(symbol, source=\"FinancialModelingPrep\")\n", - "data_df = openbb.stocks.fa.overview(symbol, source=\"FinancialModelingPrep\")\n", - "mgmt_df = openbb.stocks.fa.mgmt(symbol)\n", - "try:\n", - " if not mgmt_df.empty:\n", - " mgmt_df[\"Info\"] = mgmt_df[\"Info\"].apply(lambda x: f'{x}')\n", - " mgmt_df[\"Insider Activity\"] = mgmt_df[\"Insider Activity\"].apply(\n", - " lambda x: f'{x}' if x != \"-\" else x\n", - " )\n", - "except:\n", - " pass\n", - "try:\n", - " hist_dcf = openbb.stocks.fa.dcfc(symbol)\n", - "except:\n", - " pass\n", - "\n", - "enterprise_df = openbb.stocks.fa.enterprise(symbol)\n", - "score = openbb.stocks.fa.score(symbol, years=10)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "42e289ab", - "metadata": {}, - "outputs": [], - "source": [ - "indicators = dict(\n", - " ema=dict(length=[50]),\n", - " sma=dict(length=[50]),\n", - " wma=dict(length=[50]),\n", - ")\n", - "\n", - "ma_chart = PlotlyTA.plot(ticker_data, indicators, symbol, candles=False, volume=False)\n", - "\n", - "if ma_chart:\n", - " ma_chart = ma_chart.show(external=True)\n", - " ma_chart = ma_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e8846a6f", - "metadata": {}, - "outputs": [], - "source": [ - "macd_chart = openbb.ta.macd_chart(\n", - " ticker_data[\"Close\"], symbol=symbol, external_axes=True\n", - ")\n", - "\n", - "if macd_chart:\n", - " macd_chart = macd_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "48a7a427", - "metadata": {}, - "outputs": [], - "source": [ - "cci_chart = openbb.ta.cci_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if cci_chart:\n", - " cci_chart = cci_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6a1bd2c6", - "metadata": {}, - "outputs": [], - "source": [ - "stoch_chart = openbb.ta.stoch_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if stoch_chart:\n", - " stoch_chart = stoch_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2611d3c6", - "metadata": {}, - "outputs": [], - "source": [ - "adx_chart = openbb.ta.adx_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if adx_chart:\n", - " adx_chart = adx_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6ecb6e17", - "metadata": {}, - "outputs": [], - "source": [ - "bbands_chart = openbb.ta.bbands_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if bbands_chart:\n", - " bbands_chart = bbands_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "07e42fdb", - "metadata": {}, - "outputs": [], - "source": [ - "ad_chart = openbb.ta.ad_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if ad_chart:\n", - " ad_chart = ad_chart.to_html()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ac501a32-124d-4d21-aae1-a8d147bb1071", - "metadata": {}, - "source": [ - "## Render the report template to a file\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "abfe37e4-ea38-42c3-8fb5-7cceb233fb12", - "metadata": {}, - "outputs": [], - "source": [ - "body = \"\"\n", - "\n", - "\n", - "body += widgets.header(\n", - " author,\n", - " report_date,\n", - " report_time,\n", - " report_timezone,\n", - " report_title,\n", - " plotly_js=True,\n", - ")\n", - "\n", - "body += widgets.tablinks(\n", - " [\n", - " \"SUMMARY\",\n", - " \"Overview\",\n", - " \"Fundamental Analysis\",\n", - " \"Technical Analysis\",\n", - " \"Behavioural Analysis\",\n", - " \"Government Menu\",\n", - " \"Comparison Menu\",\n", - " \"Options\",\n", - " \"Dark Pool and Shorts\",\n", - " \"Quantitative Analysis\",\n", - " \"Analyst Opinions\",\n", - " \"Insider Trading\",\n", - " # \"Prediction Techniques\",\n", - " ]\n", - ")\n", - "\n", - "htmlcode = widgets.h(3, \"KPIs\")\n", - "htmlcode += widgets.kpi(\n", - " [last_price],\n", - " [\n", - " \"Last closing price is above the average price ratings of last 30 days\",\n", - " \"Average price ratings of last 30 day is above last closing price\",\n", - " ],\n", - " avg_ratings_last_30_days,\n", - ")\n", - "htmlcode += widgets.kpi(\n", - " [30, 70],\n", - " [\"RSI level is oversold\", \"RSI level is normal\", \"RSI level is overbought\"],\n", - " rsi_value,\n", - ")\n", - "try:\n", - " htmlcode += widgets.kpi(\n", - " [0],\n", - " [\n", - " \"The sum of last 10 insider trading (in millions) was negative\",\n", - " \"The sum of last 10 insider trading (in millions) was positive\",\n", - " ],\n", - " last_10_insider_trading,\n", - " )\n", - "except:\n", - " pass\n", - "try:\n", - " htmlcode += widgets.kpi(\n", - " [-0.1, 0.1],\n", - " [\n", - " \"Last FinBrain sentiment is bearish\",\n", - " \" Last FinBrain sentiment is neutral\",\n", - " \"Last FinBrain sentiment is bullish\",\n", - " ],\n", - " finbrain_sentiment_val,\n", - " )\n", - "except:\n", - " pass\n", - "if score and score[\"Total Score\"]:\n", - " htmlcode += widgets.kpi(\n", - " [25, 75],\n", - " [\n", - " \"Buffet Score is not favourable\",\n", - " \"Buffet Score is neutral\",\n", - " \"Buffet Score is favourable\",\n", - " ],\n", - " float(score[\"Total Score\"]),\n", - " )\n", - "body += widgets.add_tab(\"SUMMARY\", htmlcode)\n", - "\n", - "htmlcode = \"\"\n", - "if overview:\n", - " titles = [\"Company\", \"Sector\", \"Industry\"]\n", - " m = 0\n", - " for x in overview:\n", - " print(x)\n", - " htmlcode += widgets.row([widgets.h(3, titles[m]) + widgets.p(x)])\n", - " m += 1\n", - "htmlcode += widgets.row([widgets.h(3, \"Price Chart\") + (price_chart or \"\")])\n", - "\n", - "try:\n", - " htmlcode += widgets.row([widgets.h(3, \"Quote\") + quote_data.to_html()])\n", - "except:\n", - " pass\n", - "\n", - "htmlcode += widgets.row([widgets.h(3, \"Latest News for \" + symbol)])\n", - "try:\n", - " for table in tables:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(4, table[1][\"title\"]) + table[0].to_html(escape=False)]\n", - " )\n", - "except:\n", - " pass\n", - "\n", - "if not mgmt_df.empty:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, f\"Management team of {symbol}\") + mgmt_df.to_html(escape=False)]\n", - " )\n", - "body += widgets.add_tab(\"Overview\", htmlcode)\n", - "\n", - "htmlcode = widgets.row(\n", - " [widgets.h(3, \"Price Target Chart\") + (price_target_chart or \"\")]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Analyst Ratings over time\") + (ratings_over_time_chart or \"\")]\n", - ")\n", - "try:\n", - " htmlcode += widgets.row([widgets.h(3, \"Analyst Ratings\") + df_analyst.to_html()])\n", - "except:\n", - " pass\n", - "# try:\n", - "# htmlcode += widgets.row(\n", - "# [widgets.h(3, \"Analyst Recommendations\") + df_rating.to_html()]\n", - "# )\n", - "# except:\n", - "# pass\n", - "\n", - "body += widgets.add_tab(\"Analyst Opinions\", htmlcode)\n", - "\n", - "try:\n", - " htmlcode = widgets.row(\n", - " [widgets.h(3, \"Estimates\") + df_year_estimates.head().to_html()]\n", - " )\n", - "except:\n", - " pass\n", - "\n", - "if not df_quarter_earnings.empty:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, \"Earnings\") + df_quarter_earnings.head().to_html()]\n", - " )\n", - "if not df_quarter_revenues.empty:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, \"Revenues\") + df_quarter_revenues.head().to_html()]\n", - " )\n", - "try:\n", - " htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, \"Major Institutional Shareholders\")\n", - " + df_institutional_shareholders.head().to_html()\n", - " ]\n", - " )\n", - "except:\n", - " pass\n", - "try:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, f\"Historical DCF for {symbol}\") + hist_dcf.to_html()]\n", - " )\n", - "except:\n", - " pass\n", - "try:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, f\"Enterprise data for {symbol}\") + enterprise_df.to_html()]\n", - " )\n", - "except:\n", - " pass\n", - "try:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, f\"Income Statement for {symbol}\") + income_df.to_html()]\n", - " )\n", - "except:\n", - " pass\n", - "try:\n", - " htmlcode += widgets.row([widgets.h(3, f\"Data for {symbol}\") + data_df.to_html()])\n", - "except:\n", - " pass\n", - "try:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, \"SEC filings\") + df_sec_filings.to_html(escape=False)]\n", - " )\n", - "except:\n", - " pass\n", - "\n", - "body += widgets.add_tab(\"Fundamental Analysis\", htmlcode)\n", - "\n", - "htmlcode = widgets.row([widgets.h(3, \"Put to call ratio\") + (pcr_chart or \"\")])\n", - "\n", - "if expiry_dates:\n", - " htmlcode += widgets.row(\n", - " [widgets.h(3, \"Option Volume for closest expiry date\") + (vol_chart or \"\")]\n", - " )\n", - " htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, \"Volume and Open Interest for closest expiry date\")\n", - " + (voi_chart or \"\")\n", - " ]\n", - " )\n", - "\n", - "body += widgets.add_tab(\"Options\", htmlcode)\n", - "\n", - "htmlcode = widgets.row([net_short_position or \"\"])\n", - "htmlcode += widgets.row([price_vs_short_interest or \"\"])\n", - "# htmlcode += widgets.row([dark_pools])\n", - "body += widgets.add_tab(\"Dark Pool and Shorts\", htmlcode)\n", - "\n", - "htmlcode = widgets.row(\n", - " [\n", - " widgets.h(3, \"Congress trading in the past 12 months for \" + symbol)\n", - " + (gtrades_chart or \"\")\n", - " ]\n", - ")\n", - "htmlcode += widgets.row([gov_histcont_chart or \"\"])\n", - "htmlcode += widgets.row([gov_contracts_chart or \"\"])\n", - "htmlcode += widgets.row([widgets.h(3, \"Recent Corporate Lobbying by \" + symbol)])\n", - "try:\n", - " for _, row in df_lobbying.iterrows():\n", - " amount = (\n", - " \"$\" + str(int(float(row[\"Amount\"]))) if row[\"Amount\"] is not None else \"N/A\"\n", - " )\n", - " htmlcode += widgets.row([widgets.p(f\"{row['Date']}: {row['Client']} {amount}\")])\n", - " if (row[\"Amount\"] is not None) and (row[\"Specific_Issue\"] is not None):\n", - " htmlcode += widgets.row(\n", - " [\n", - " widgets.p(\n", - " \"\\t\"\n", - " + row[\"Specific_Issue\"].replace(\"\\n\", \" \").replace(\"\\r\", \"\")\n", - " )\n", - " ]\n", - " )\n", - "except:\n", - " pass\n", - "\n", - "body += widgets.add_tab(\"Government Menu\", htmlcode)\n", - "\n", - "htmlcode = widgets.row(\n", - " [\n", - " widgets.h(3, f\"Price over the past year for companies similar to {symbol}\")\n", - " + (historical_similar or \"\")\n", - " ]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, f\"Price correlation with similar companies for {symbol}\")\n", - " + (hcorr_similar or \"\")\n", - " ]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, f\"Volume over the past year for companies similar to {symbol}\")\n", - " + (vol_similar or \"\")\n", - " ]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, f\"Sentiment correlation with similar companies for {symbol}\")\n", - " + (scorr_similar or \"\")\n", - " ]\n", - ")\n", - "if isinstance(income_comparison, pd.DataFrame):\n", - " htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, f\"Income data for similar companies to {symbol}\")\n", - " + income_comparison.to_html()\n", - " ]\n", - " )\n", - "htmlcode += widgets.row(\n", - " [\n", - " widgets.p(\n", - " \"Note that similar companies have been found using openbb.stocks.ca.similar\"\n", - " )\n", - " ]\n", - ")\n", - "body += widgets.add_tab(\"Comparison Menu\", htmlcode)\n", - "\n", - "htmlcode = widgets.row([widgets.h(3, f\"Moving Averages for {symbol}\") + ma_chart])\n", - "htmlcode += widgets.row([macd_chart or \"\"])\n", - "htmlcode += widgets.row([ta_rsi or \"\"])\n", - "htmlcode += widgets.row([stoch_chart or \"\"])\n", - "htmlcode += widgets.row([cci_chart or \"\"])\n", - "htmlcode += widgets.row([ad_chart or \"\"])\n", - "htmlcode += widgets.row([bbands_chart or \"\"])\n", - "htmlcode += widgets.row([adx_chart or \"\"])\n", - "body += widgets.add_tab(\"Technical Analysis\", htmlcode)\n", - "\n", - "if not df_insider.empty:\n", - " htmlcode = widgets.row(\n", - " [widgets.h(3, \"Last Activity\") + df_insider.head(10).to_html(col_space=\"75px\")]\n", - " )\n", - "else:\n", - " htmlcode = widgets.row(\n", - " [widgets.h(3, \"Last Activity\") + widgets.p(\"No insider trading data found.\")]\n", - " )\n", - "\n", - "body += widgets.add_tab(\"Insider Trading\", htmlcode)\n", - "\n", - "\n", - "htmlcode = widgets.row([bw_year or \"\"])\n", - "htmlcode += widgets.row([bw_month or \"\"])\n", - "body += widgets.add_tab(\"Quantitative Analysis\", htmlcode)\n", - "\n", - "\n", - "htmlcode = widgets.row([finbrain_sentiment or \"\"])\n", - "htmlcode += widgets.row([snews or \"\"])\n", - "htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, \"Interest in \" + symbol + \" based on google analytics\")\n", - " + (google_mentions_chart or \"\")\n", - " ]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, f\"Regions with highest interest in {symbol}\")\n", - " + (google_regions_chart or \"\")\n", - " ]\n", - ")\n", - "try:\n", - " htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, f\"Top queries related to {symbol}\")\n", - " + df_related_queries.to_html()\n", - " ]\n", - " )\n", - " htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, f\"Top rising queries related to {symbol}\")\n", - " + df_rising_queries.to_html()\n", - " ]\n", - " )\n", - "except:\n", - " pass\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, f\"Stocktwits sentiment for {symbol}\") + stocktwits_sentiment]\n", - ")\n", - "body += widgets.add_tab(\"Behavioural Analysis\", htmlcode)\n", - "\n", - "body += widgets.tab_clickable_and_save_evt()\n", - "\n", - "report = widgets.html_report(\n", - " title=report_name, stylesheet=widgets.html_report_stylesheet(), body=body\n", - ")\n", - "\n", - "# to save the results\n", - "with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n", - " fh.write(report)" - ] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.11" - }, - "vscode": { - "interpreter": { - "hash": "af870c0f9615f5bb08692efcc9bf540319fe60e6793056508a4dab09c9fa8dda" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/reports/templates/etf.ipynb b/openbb_terminal/reports/templates/etf.ipynb deleted file mode 100644 index a824d6d1b05..00000000000 --- a/openbb_terminal/reports/templates/etf.ipynb +++ /dev/null @@ -1,499 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "70ba2c8a", - "metadata": {}, - "source": [ - "## Notebook setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "driven-billion", - "metadata": {}, - "outputs": [], - "source": [ - "import warnings\n", - "import numpy as np\n", - "import datetime\n", - "\n", - "from IPython.display import HTML\n", - "\n", - "import openbb_terminal.config_terminal as cfg\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "from openbb_terminal.helper_classes import TerminalStyle\n", - "from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA\n", - "\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "473e83c7", - "metadata": {}, - "outputs": [], - "source": [ - "# Suppressing sdk logs\n", - "from openbb_terminal.core.session.current_system import set_system_variable\n", - "\n", - "set_system_variable(\"LOGGING_SUPPRESS\", True)\n", - "\n", - "# Import the OpenBB SDK\n", - "from openbb_terminal.sdk import openbb, widgets" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3fe7db5-ec6a-42cf-9e66-52dc1de22370", - "metadata": {}, - "outputs": [], - "source": [ - "from openbb_terminal.core.session.current_user import get_current_user\n", - "\n", - "user = get_current_user()\n", - "\n", - "cfg.theme = TerminalStyle(\n", - " user.preferences.MPL_STYLE,\n", - " user.preferences.PMF_STYLE,\n", - " user.preferences.RICH_STYLE,\n", - ")\n", - "theme.apply_style(\"light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "36091f30", - "metadata": {}, - "outputs": [], - "source": [ - "stylesheet = widgets.html_report_stylesheet()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "807fda4e", - "metadata": {}, - "source": [ - "## Select Ticker" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "intended-sherman", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "# Parameters that will be replaced when calling this notebook\n", - "# Do not leave parameters blank as notebook will not run otherwise\n", - "symbol = \"GLD\"\n", - "report_name = \"Equity Report for VTI\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e1f87190", - "metadata": {}, - "outputs": [], - "source": [ - "ticker = symbol" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a441ddbc-d75b-4a18-84d4-e01b677674d3", - "metadata": {}, - "outputs": [], - "source": [ - "if \".\" in ticker:\n", - " import sys\n", - "\n", - " sys.exit(0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1be26dae-cafe-4a22-80aa-eff296fc1a9b", - "metadata": {}, - "outputs": [], - "source": [ - "ticker_data = openbb.stocks.load(\n", - " symbol=ticker, start_date=datetime.datetime.now() - datetime.timedelta(days=4 * 30)\n", - ")\n", - "ticker_data = openbb.stocks.process_candle(data=ticker_data)\n", - "\n", - "author = \"OpenBB\"\n", - "report_title = f\"ETF RESEARCH REPORT ON {ticker.upper()}\"\n", - "report_date = datetime.datetime.now().strftime(\"%d %B, %Y\")\n", - "report_time = datetime.datetime.now().strftime(\"%H:%M\")\n", - "report_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo\n", - "report_title, report_date, report_time, report_timezone" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2ecfcf81", - "metadata": {}, - "outputs": [], - "source": [ - "# info = openbb.etf.summary(name=ticker)\n", - "# info" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "f8a0d747", - "metadata": {}, - "source": [ - "## Data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "39b8e77d", - "metadata": {}, - "outputs": [], - "source": [ - "data = openbb.stocks.load(ticker)\n", - "\n", - "candle_chart = openbb.etf.candle(\n", - " symbol=ticker,\n", - " data=data,\n", - " external_axes=True,\n", - ")\n", - "\n", - "if candle_chart:\n", - " candle_chart = candle_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f751f57c", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "\n", - "sectors = openbb.etf.weights(ticker)\n", - "sector_weights_formatted = {}\n", - "for sector_weight in sectors:\n", - " sector_weights_formatted[sector_weight[\"sector\"]] = (\n", - " float(sector_weight[\"weightPercentage\"].strip(\"%\")) / 100\n", - " )\n", - "sector_weights_formatted = dict(sorted(sector_weights_formatted.items()))\n", - "weights_df = pd.DataFrame.from_dict(sector_weights_formatted, orient=\"index\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "324e2d4b-b2f6-4da3-bac4-ef785ad4e436", - "metadata": {}, - "outputs": [], - "source": [ - "weights_chart = \"\"\n", - "\n", - "if \"Percentage\" in weights_df.columns:\n", - " weights_df.columns = [\"Percentage\"]\n", - "\n", - "if (\n", - " \"Percentage\" in weights_df.columns\n", - " and len(weights_df[weights_df[\"Percentage\"] > 10].index) > 1\n", - "):\n", - " colors = theme.get_colors()\n", - " fig = OpenBBFigure.create_subplots(\n", - " 1,\n", - " 3,\n", - " specs=[[{\"type\": \"domain\"}, {\"type\": \"pie\", \"colspan\": 2}, None]],\n", - " row_heights=[1],\n", - " column_widths=[0.1, 0.8, 0.1],\n", - " )\n", - "\n", - " fig.add_pie(\n", - " labels=weights_df[weights_df[\"Percentage\"] > 10].index,\n", - " values=weights_df[weights_df[\"Percentage\"] > 10][\"Percentage\"],\n", - " textinfo=\"label+percent\",\n", - " hoverinfo=\"label+percent\",\n", - " automargin=True,\n", - " rotation=45,\n", - " row=1,\n", - " col=2,\n", - " )\n", - " fig.update_traces(\n", - " textposition=\"outside\",\n", - " textfont_size=15,\n", - " marker=dict(\n", - " colors=colors,\n", - " line=dict(color=\"#F5EFF3\", width=0.8),\n", - " ),\n", - " )\n", - "\n", - " fig.update_layout(\n", - " margin=dict(t=40, b=20),\n", - " title=dict(\n", - " text=f\"Sector holdings of {ticker}\",\n", - " y=0.98,\n", - " x=0.5,\n", - " xanchor=\"center\",\n", - " yanchor=\"top\",\n", - " ),\n", - " colorway=colors,\n", - " showlegend=False,\n", - " )\n", - "\n", - " weights_chart = fig.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8bb53c1-147e-4342-af61-0d0a9cc56e59", - "metadata": {}, - "outputs": [], - "source": [ - "holdings = openbb.etf.holdings(ticker)\n", - "holdings" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b60ebf25", - "metadata": {}, - "outputs": [], - "source": [ - "indicators = dict(\n", - " ema=dict(length=[50]),\n", - " sma=dict(length=[50]),\n", - " wma=dict(length=[50]),\n", - ")\n", - "\n", - "ma_chart = PlotlyTA.plot(ticker_data, indicators, symbol, candles=False, volume=False)\n", - "\n", - "if ma_chart:\n", - " ma_chart = ma_chart.show(external=True)\n", - " ma_chart = ma_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fa02a874", - "metadata": {}, - "outputs": [], - "source": [ - "macd_chart = openbb.ta.macd_chart(\n", - " ticker_data[\"Close\"], symbol=symbol, external_axes=True\n", - ")\n", - "\n", - "if macd_chart:\n", - " macd_chart = macd_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "de3d59c7", - "metadata": {}, - "outputs": [], - "source": [ - "cci_chart = openbb.ta.cci_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if cci_chart:\n", - " cci_chart = cci_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "44326981", - "metadata": {}, - "outputs": [], - "source": [ - "stoch_chart = openbb.ta.stoch_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if stoch_chart:\n", - " stoch_chart = stoch_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e171fe5", - "metadata": {}, - "outputs": [], - "source": [ - "adx_chart = openbb.ta.adx_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if adx_chart:\n", - " adx_chart = adx_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fc1a5418", - "metadata": {}, - "outputs": [], - "source": [ - "bbands_chart = openbb.ta.bbands_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if bbands_chart:\n", - " bbands_chart = bbands_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d4be9f55", - "metadata": {}, - "outputs": [], - "source": [ - "ad_chart = openbb.ta.ad_chart(ticker_data, symbol=symbol, external_axes=True)\n", - "\n", - "if ad_chart:\n", - " ad_chart = ad_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4d29aafa", - "metadata": {}, - "outputs": [], - "source": [ - "ta_rsi = openbb.ta.rsi_chart(ticker_data[\"Close\"], external_axes=True)\n", - "\n", - "if ta_rsi:\n", - " ta_rsi = ta_rsi.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e539e995", - "metadata": {}, - "outputs": [], - "source": [ - "df = openbb.ta.rsi(ticker_data[\"Close\"])\n", - "rsi_value = round(df.values[-1][0], 2)\n", - "rsi_value" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ac501a32-124d-4d21-aae1-a8d147bb1071", - "metadata": {}, - "source": [ - "## Render the report template to a file" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "abfe37e4-ea38-42c3-8fb5-7cceb233fb12", - "metadata": {}, - "outputs": [], - "source": [ - "body = \"\"\n", - "\n", - "\n", - "body += widgets.header(\n", - " author,\n", - " report_date,\n", - " report_time,\n", - " report_timezone,\n", - " f\"ETF RESEARCH REPORT: {ticker.upper()}\",\n", - " plotly_js=True,\n", - ")\n", - "\n", - "body += widgets.tablinks(\n", - " [\n", - " \"SUMMARY\",\n", - " \"Technical Analysis\",\n", - " ]\n", - ")\n", - "\n", - "htmlcode = widgets.h(3, \"Summary\")\n", - "# htmlcode += widgets.p(info)\n", - "htmlcode += widgets.row([candle_chart])\n", - "\n", - "htmlcode += widgets.p(\"The ETF has the following weights distribution:\")\n", - "\n", - "htmlcode += widgets.row([weights_df.to_html()])\n", - "htmlcode += widgets.row([weights_chart])\n", - "\n", - "htmlcode += widgets.p(\"And has funds in the following assets:\")\n", - "htmlcode += widgets.row([holdings.to_html()])\n", - "\n", - "body += widgets.add_tab(\"SUMMARY\", htmlcode)\n", - "\n", - "htmlcode = widgets.row([widgets.h(3, f\"Moving Averages for {symbol}\") + ma_chart])\n", - "htmlcode += widgets.row([macd_chart or \"\"])\n", - "htmlcode += widgets.row([ta_rsi or \"\"])\n", - "htmlcode += widgets.row([stoch_chart or \"\"])\n", - "htmlcode += widgets.row([cci_chart or \"\"])\n", - "htmlcode += widgets.row([ad_chart or \"\"])\n", - "htmlcode += widgets.row([bbands_chart or \"\"])\n", - "htmlcode += widgets.row([adx_chart or \"\"])\n", - "body += widgets.add_tab(\"Technical Analysis\", htmlcode)\n", - "\n", - "\n", - "body += widgets.tab_clickable_and_save_evt()\n", - "\n", - "report = widgets.html_report(title=report_name, stylesheet=stylesheet, body=body)\n", - "\n", - "\n", - "# to save the results\n", - "with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n", - " fh.write(report)" - ] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - }, - "vscode": { - "interpreter": { - "hash": "381a209f752e9104bff95ec5e80679ea2e476866b80e4326f1ee6b7658af6049" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/reports/templates/floppy-disc.png b/openbb_terminal/reports/templates/floppy-disc.png deleted file mode 100644 index a318f414acaa1ecbd31ad171251fdffe37bacd60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2782 zcmc&$eNa?Y6hH6nA`cjW1xJx()^*YKgO5fUi3kXo$H#(UQRR~ndgXy)_-i)h5Ua8*2m&znqI1^RZ{=tLcW z;}!Cw&UiUWad2~9L_*$+IfwEJQ*zUxu&~fKGdoL{nxakj&B@KEz8B;UV4tL7!{YTd zQ@6t2l^$@5{ON-H`%%BSa8Z>$N4D!NvCGhxKF#red^#GOL4E#3-7D(Syv?b@>ppAW zx2T@pcsaZI;qAUuns4h7`!Wrhi9gbdOc_#+ovtRb zVRGy3Tne};k9ik4fF|i3BhfMRjGCYadp=TxqJUCISCVl)zk*~mcikmAG^UG!`7l#3 zjk^TXIU-cKXVRS@OsOpdq4jQOIn+322_ofr^q(iO)>eO^=$@}tBw0Xbi?A{}eU{*! zY7y$v6~zi}ZKXi`bjJol+&j@NPz;Cs>rUSll)ZgH5sG>PWhPQZR?$w9u3@}VkoMfO zC&}oKx#hyRko8nZds+|})=+L}yY5wxjXy^Pw zJsvVMy=helmn^AnVv_Qn@+$^dxU}ptmYnsGl}f(q)?)Dlu4JtQtj=C8e-Rp+Y_F&f zIYB@fc7l!Xio0rgN^V+2gWtVA57s>2CR?-5TKA*3)xQ|B$c6Ptv;0qcg$I-WIlIXg zw6zYacG{P;FF)qb!`XtWVA!aw4(9f*)!9hSaMEP9SVYj;XS1qT-(xSvuYtu7+kRaJ z4y`w2{5kcCF5Jh?`ky-;52^Vp@^@%>9KQieyXKdlb@e_qE7!0njZ_~$XgYlSZ@@FORECAST REPORT ON:
{ticker.upper()}\",\n", - " plotly_js=True,\n", - ")\n", - "\n", - "body += widgets.tablinks([\"SUMMARY\"])\n", - "\n", - "htmlcode = widgets.h(3, \"Summary\")\n", - "htmlcode += widgets.row([widgets.h(3, \"Understand the data\")])\n", - "htmlcode += widgets.row([ticker_data.head(10).to_html()])\n", - "htmlcode += widgets.row([plot_chart])\n", - "htmlcode += widgets.row([widgets.h(3, \"Analyzing the data\")])\n", - "htmlcode += widgets.row([corr_chart])\n", - "htmlcode += widgets.row([season_chart])\n", - "htmlcode += widgets.row([widgets.h(3, \"Creating predictions\")])\n", - "htmlcode += widgets.row([expo_chart])\n", - "htmlcode += widgets.row([rnn_chart])\n", - "\n", - "body += widgets.add_tab(\"SUMMARY\", htmlcode)\n", - "body += widgets.tab_clickable_and_save_evt()\n", - "report = widgets.html_report(title=report_name, stylesheet=stylesheet, body=body)\n", - "\n", - "# to save the results\n", - "with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n", - " fh.write(report)" - ] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - }, - "vscode": { - "interpreter": { - "hash": "381a209f752e9104bff95ec5e80679ea2e476866b80e4326f1ee6b7658af6049" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/reports/templates/forex.ipynb b/openbb_terminal/reports/templates/forex.ipynb deleted file mode 100644 index e418f748860..00000000000 --- a/openbb_terminal/reports/templates/forex.ipynb +++ /dev/null @@ -1,674 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "70ba2c8a", - "metadata": {}, - "source": [ - "## Notebook setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "driven-billion", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import io\n", - "import warnings\n", - "import datetime\n", - "import numpy as np\n", - "import base64\n", - "import pandas as pd\n", - "\n", - "from IPython.display import HTML\n", - "\n", - "import openbb_terminal.config_terminal as cfg\n", - "from openbb_terminal.helper_classes import TerminalStyle\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "03a286e7", - "metadata": {}, - "outputs": [], - "source": [ - "# Suppressing sdk logs\n", - "from openbb_terminal.core.session.current_system import set_system_variable\n", - "\n", - "set_system_variable(\"LOGGING_SUPPRESS\", True)\n", - "\n", - "# Import the OpenBB SDK\n", - "from openbb_terminal.sdk import openbb, widgets" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3fe7db5-ec6a-42cf-9e66-52dc1de22370", - "metadata": {}, - "outputs": [], - "source": [ - "from openbb_terminal.core.session.current_user import get_current_user\n", - "\n", - "user = get_current_user()\n", - "\n", - "cfg.theme = TerminalStyle(\n", - " user.preferences.MPL_STYLE,\n", - " user.preferences.PMF_STYLE,\n", - " user.preferences.RICH_STYLE,\n", - ")\n", - "\n", - "theme.apply_style(\"light\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4b002bfd", - "metadata": {}, - "outputs": [], - "source": [ - "stylesheet = widgets.html_report_stylesheet()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "807fda4e", - "metadata": {}, - "source": [ - "## Select Ticker" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "intended-sherman", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "# Parameters that will be replaced when calling this notebook\n", - "# Do not leave parameters blank as notebook will not run otherwise\n", - "symbol = \"EURUSD\"\n", - "report_name = \"Forex Report for EURUSD\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "613cea1d", - "metadata": {}, - "outputs": [], - "source": [ - "ticker = symbol" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d4897334", - "metadata": {}, - "outputs": [], - "source": [ - "author = \"\"\n", - "report_title = f\"INVESTMENT RESEARCH REPORT ON {ticker.upper()}\"\n", - "report_date = datetime.datetime.now().strftime(\"%d %B, %Y\")\n", - "report_time = datetime.datetime.now().strftime(\"%H:%M\")\n", - "report_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo\n", - "report_title, report_date, report_time, report_timezone" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "07d09ce8", - "metadata": {}, - "outputs": [], - "source": [ - "from_symbol = ticker[:3]\n", - "to_symbol = ticker[3:]\n", - "last_year = datetime.datetime.now() - datetime.timedelta(days=365)\n", - "\n", - "ticker_data = openbb.forex.load(\n", - " from_symbol=from_symbol,\n", - " to_symbol=to_symbol,\n", - " start_date=last_year.strftime(\"%Y-%m-%d\"),\n", - " interval=\"1day\",\n", - " resolution=\"d\",\n", - ")\n", - "\n", - "# 1 year historical data\n", - "ticker_data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "88af0fbe", - "metadata": {}, - "outputs": [], - "source": [ - "last_year = datetime.datetime.now() - datetime.timedelta(days=365)\n", - "\n", - "ticker_data_interval_1month = openbb.forex.load(\n", - " from_symbol=from_symbol,\n", - " to_symbol=to_symbol,\n", - " start_date=last_year.strftime(\"%Y-%m-%d\"),\n", - " interval=\"1month\",\n", - " resolution=\"d\",\n", - ")\n", - "ticker_data_interval_1month = ticker_data_interval_1month.drop(columns=\"Volume\")\n", - "ticker_data_interval_1month" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "f8a0d747", - "metadata": {}, - "source": [ - "## Data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "709a2a7e", - "metadata": {}, - "outputs": [], - "source": [ - "# candle\n", - "\n", - "candle = openbb.forex.candle(\n", - " data=ticker_data,\n", - " to_symbol=to_symbol,\n", - " from_symbol=from_symbol,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "05f54519", - "metadata": {}, - "outputs": [], - "source": [ - "# candle graph with moving averages 7/14 and 30/60\n", - "candle_ma_7_14 = openbb.forex.candle(\n", - " data=ticker_data,\n", - " to_symbol=to_symbol,\n", - " from_symbol=from_symbol,\n", - " ma=[7, 14],\n", - " external_axes=True,\n", - ").to_html()\n", - "\n", - "candle_ma_30_60 = openbb.forex.candle(\n", - " data=ticker_data,\n", - " to_symbol=to_symbol,\n", - " from_symbol=from_symbol,\n", - " ma=[30, 60],\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fc17a601", - "metadata": {}, - "outputs": [], - "source": [ - "fwd = openbb.forex.fwd(\n", - " to_symbol=to_symbol,\n", - " from_symbol=from_symbol,\n", - ")\n", - "\n", - "# forward rates\n", - "fwd" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "6c852082", - "metadata": {}, - "source": [ - "# Technical analysis" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4ff6345d", - "metadata": {}, - "outputs": [], - "source": [ - "# simple moving average\n", - "ticker_data.index.names = [\"date\"]\n", - "\n", - "sma = openbb.ta.ma_chart(\n", - " data=ticker_data[\"Close\"],\n", - " ma_type=\"SMA\",\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5d5089fa", - "metadata": {}, - "outputs": [], - "source": [ - "# exponential moving average\n", - "ema = openbb.ta.ma_chart(\n", - " data=ticker_data[\"Close\"],\n", - " ma_type=\"EMA\",\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5facfbf4", - "metadata": {}, - "outputs": [], - "source": [ - "# zero lag exponential moving average\n", - "\n", - "zlma = openbb.ta.ma_chart(\n", - " data=ticker_data[\"Close\"],\n", - " ma_type=\"ZLMA\",\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "df54f7ad", - "metadata": {}, - "outputs": [], - "source": [ - "# commodity channel index\n", - "\n", - "cci = openbb.ta.cci_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f53469eb", - "metadata": {}, - "outputs": [], - "source": [ - "# moving average convergence/divergence\n", - "\n", - "macd = openbb.ta.macd_chart(\n", - " data=ticker_data[\"Adj Close\"],\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dd79e6b6", - "metadata": {}, - "outputs": [], - "source": [ - "# fisher transform\n", - "\n", - "fisher = openbb.ta.fisher_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b699e61f", - "metadata": {}, - "outputs": [], - "source": [ - "# aroon indicator\n", - "\n", - "aroon = openbb.ta.aroon_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "79401027", - "metadata": {}, - "outputs": [], - "source": [ - "# bollinger bands\n", - "\n", - "bbands = openbb.ta.bbands_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0b5e2949", - "metadata": {}, - "outputs": [], - "source": [ - "# fibonacci retracement\n", - "\n", - "fib = openbb.ta.fib_chart(\n", - " data=ticker_data,\n", - " symbol=ticker,\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "e6f6ce8a", - "metadata": {}, - "source": [ - "# Quantitative Analysis" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e77b373b", - "metadata": {}, - "outputs": [], - "source": [ - "# summary\n", - "\n", - "summary = openbb.qa.summary(data=ticker_data)\n", - "summary" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ecfc93e", - "metadata": {}, - "outputs": [], - "source": [ - "# normality statistics and tests\n", - "\n", - "normality = openbb.qa.normality(\n", - " data=ticker_data[\"Close\"],\n", - ")\n", - "normality" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "66a7dccc", - "metadata": {}, - "outputs": [], - "source": [ - "# rolling mean and std deviation of prices\n", - "\n", - "rolling = (\n", - " openbb.qa.rolling_chart(\n", - " symbol=ticker,\n", - " data=ticker_data,\n", - " target=\"Close\",\n", - " external_axes=True,\n", - " )\n", - " .update_layout(\n", - " legend=dict(\n", - " x=0.01,\n", - " y=0.99,\n", - " xanchor=\"left\",\n", - " yanchor=\"top\",\n", - " xref=\"paper\",\n", - " yref=\"paper\",\n", - " bgcolor=\"rgba(0, 0, 0, 0)\",\n", - " ),\n", - " legend2=dict(bgcolor=\"rgba(0, 0, 0, 0)\"),\n", - " )\n", - " .to_html()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6b43dd7", - "metadata": {}, - "outputs": [], - "source": [ - "# rolling kurtosis of distribution of prices\n", - "\n", - "kurtosis = openbb.qa.kurtosis_chart(\n", - " symbol=ticker,\n", - " data=ticker_data,\n", - " target=\"Close\",\n", - " external_axes=True,\n", - ").to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fc31624a", - "metadata": {}, - "outputs": [], - "source": [ - "# latest news\n", - "try:\n", - " news = openbb.news(term=ticker).head(10)\n", - " news.columns = news.columns.str.title()\n", - "\n", - " news = news.rename(\n", - " columns={\"Date\": \"Published\", \"Description\": \"Title\", \"Url\": \"Link\"}\n", - " )\n", - " news[\"Title\"] = news.apply(\n", - " lambda x: f'{x[\"Title\"]}', axis=1\n", - " )\n", - " news = news[[\"Title\", \"Published\"]]\n", - " news[\"Published\"] = pd.to_datetime(news[\"Published\"]).dt.strftime(\"%Y-%m-%d %H:%M\")\n", - "\n", - " news = news.set_index(\"Published\")\n", - " news.sort_index()\n", - "except:\n", - " news = pd.DataFrame()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ac501a32-124d-4d21-aae1-a8d147bb1071", - "metadata": {}, - "source": [ - "## Render the report template to a file" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "abfe37e4-ea38-42c3-8fb5-7cceb233fb12", - "metadata": {}, - "outputs": [], - "source": [ - "body = \"\"\n", - "\n", - "\n", - "body += widgets.header(\n", - " author,\n", - " report_date,\n", - " report_time,\n", - " report_timezone,\n", - " f\"INVESTMENT RESEARCH REPORT: {ticker}\",\n", - " plotly_js=True,\n", - ")\n", - "\n", - "body += widgets.tablinks(\n", - " [\n", - " \"SUMMARY\",\n", - " \"Overview\",\n", - " \"Technical Analysis\",\n", - " \"Quantitative Analysis\",\n", - " ]\n", - ")\n", - "\n", - "comment = \"Every analysis displayed considered every day data YTD\"\n", - "\n", - "\n", - "# TODO: add KPIs\n", - "\n", - "htmlcode = \"\"\n", - "\n", - "# htmlcode += widgets.h(3, \"KPIs\")\n", - "# htmlcode += widgets.kpi(\n", - "# [30, 70],\n", - "# [\"RSI level is oversold\", \"RSI level is normal\", \"RSI level is overbought\"],\n", - "# 999,\n", - "# )\n", - "# htmlcode += widgets.kpi(\n", - "# [0],\n", - "# [\n", - "# \"The sum of last 10 insider trading (in millions) was negative\",\n", - "# \"The sum of last 10 insider trading (in millions) was positive\",\n", - "# ],\n", - "# 999,\n", - "# )\n", - "# htmlcode += widgets.kpi(\n", - "# [-0.1, 0.1],\n", - "# [\n", - "# \"Last FinBrain sentiment is bearish\",\n", - "# \"Last FinBrain sentiment is neutral\",\n", - "# \"Last FinBrain sentiment is bullish\",\n", - "# ],\n", - "# 999,\n", - "# )\n", - "\n", - "# Summary tab\n", - "htmlcode += widgets.row([widgets.h(3, \"Latest news\") + news.to_html(escape=False)])\n", - "htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, \"One year historical data for 1st day each month\")\n", - " + ticker_data_interval_1month.to_html()\n", - " ]\n", - ")\n", - "body += widgets.add_tab(\"SUMMARY\", htmlcode)\n", - "\n", - "# Overview tab\n", - "htmlcode = \"\"\n", - "htmlcode = widgets.row([widgets.h(3, \"Candlestick chart\")])\n", - "htmlcode += widgets.row([candle])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Candlestick chart with moving averages 7 and 14\")]\n", - ")\n", - "htmlcode += widgets.row([candle_ma_7_14])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Candlestick chart with moving averages 30 and 60\")]\n", - ")\n", - "htmlcode += widgets.row([candle_ma_30_60])\n", - "htmlcode += widgets.row([widgets.h(3, \"Forward rates\")])\n", - "htmlcode += widgets.row([fwd.to_html()])\n", - "body += widgets.add_tab(\"Overview\", htmlcode, True, comment)\n", - "\n", - "# Technical Analysis tab\n", - "htmlcode = widgets.row([widgets.h(3, \"Simple moving average\")])\n", - "htmlcode += widgets.row([sma])\n", - "htmlcode += widgets.row([widgets.h(3, \"Exponential moving average\")])\n", - "htmlcode += widgets.row([ema])\n", - "htmlcode += widgets.row([widgets.h(3, \"Zero lag exponential moving average\")])\n", - "htmlcode += widgets.row([zlma])\n", - "htmlcode += widgets.row([widgets.h(3, \"Commodity channel index\")])\n", - "htmlcode += widgets.row([cci])\n", - "htmlcode += widgets.row([widgets.h(3, \"Moving average convergence/divergence\")])\n", - "htmlcode += widgets.row([macd])\n", - "htmlcode += widgets.row([widgets.h(3, \"Fisher transform\")])\n", - "htmlcode += widgets.row([fisher])\n", - "htmlcode += widgets.row([widgets.h(3, \"Aroon indicator\")])\n", - "htmlcode += widgets.row([aroon])\n", - "htmlcode += widgets.row([widgets.h(3, \"Bollinger bands\")])\n", - "htmlcode += widgets.row([bbands])\n", - "htmlcode += widgets.row([widgets.h(3, \"Fibonacci retracement\")])\n", - "htmlcode += widgets.row([fib])\n", - "body += widgets.add_tab(\"Technical Analysis\", htmlcode, True, comment)\n", - "\n", - "# Quantitative Analysis tab\n", - "htmlcode = widgets.row([widgets.h(3, \"Summary\") + summary.to_html()])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Normality statistics and tests\") + normality.to_html()]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"Rolling mean and std deviation of prices\")])\n", - "htmlcode += widgets.row([rolling])\n", - "htmlcode += widgets.row([widgets.h(3, \"Rolling kurtosis of distribution of prices\")])\n", - "htmlcode += widgets.row([kurtosis])\n", - "body += widgets.add_tab(\"Quantitative Analysis\", htmlcode, True, comment)\n", - "\n", - "body += widgets.tab_clickable_and_save_evt()\n", - "report = widgets.html_report(title=report_name, stylesheet=stylesheet, body=body)\n", - "\n", - "# # to save the results\n", - "with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n", - " fh.write(report)" - ] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.13" - }, - "vscode": { - "interpreter": { - "hash": "381a209f752e9104bff95ec5e80679ea2e476866b80e4326f1ee6b7658af6049" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/reports/templates/portfolio.ipynb b/openbb_terminal/reports/templates/portfolio.ipynb deleted file mode 100644 index 075534e3ea7..00000000000 --- a/openbb_terminal/reports/templates/portfolio.ipynb +++ /dev/null @@ -1,511 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "70ba2c8a", - "metadata": {}, - "source": [ - "## Notebook setup" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "driven-billion", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import io\n", - "import warnings\n", - "import datetime\n", - "import numpy as np\n", - "import base64\n", - "import pytz\n", - "import pandas as pd\n", - "\n", - "from IPython.display import HTML\n", - "\n", - "import openbb_terminal.config_terminal as cfg\n", - "from openbb_terminal.helper_classes import TerminalStyle\n", - "from openbb_terminal.core.config.paths import REPOSITORY_DIRECTORY\n", - "from openbb_terminal import OpenBBFigure, theme\n", - "\n", - "warnings.filterwarnings(\"ignore\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "49032d55", - "metadata": {}, - "outputs": [], - "source": [ - "# Suppressing sdk logs\n", - "from openbb_terminal.core.session.current_system import set_system_variable\n", - "\n", - "set_system_variable(\"LOGGING_SUPPRESS\", True)\n", - "\n", - "# Import the OpenBB SDK\n", - "from openbb_terminal.sdk import openbb, widgets, helper" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "62d3ea0e", - "metadata": {}, - "outputs": [], - "source": [ - "from openbb_terminal.core.session.current_user import get_current_user\n", - "\n", - "user = get_current_user()\n", - "\n", - "cfg.theme = TerminalStyle(\n", - " user.preferences.MPL_STYLE,\n", - " user.preferences.PMF_STYLE,\n", - " user.preferences.RICH_STYLE,\n", - ")\n", - "theme.apply_style(\"light\")\n", - "\n", - "USER_PORTFOLIO_DATA_DIRECTORY = user.preferences.USER_PORTFOLIO_DATA_DIRECTORY" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4391e874", - "metadata": {}, - "outputs": [], - "source": [ - "stylesheet = widgets.html_report_stylesheet()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "8136bf90", - "metadata": {}, - "source": [ - "### Header" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3fe7db5-ec6a-42cf-9e66-52dc1de22370", - "metadata": {}, - "outputs": [], - "source": [ - "stylesheet = widgets.html_report_stylesheet()\n", - "user_time_zone = pytz.timezone(helper.get_user_timezone())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "daef64ec", - "metadata": { - "tags": [ - "parameters" - ] - }, - "outputs": [], - "source": [ - "# Parameters that will be replaced when calling this notebook\n", - "# Do not leave parameters blank as notebook will not run otherwise\n", - "transactions = \"holdings_example.xlsx\"\n", - "report_name = \"Portfolio Report\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dd441a31", - "metadata": {}, - "outputs": [], - "source": [ - "file_0 = (\n", - " str(REPOSITORY_DIRECTORY)\n", - " + \"/openbb_terminal/miscellaneous/portfolio/\"\n", - " + transactions\n", - ")\n", - "file_1 = str(USER_PORTFOLIO_DATA_DIRECTORY) + \"/holdings/\" + transactions\n", - "\n", - "if os.path.isfile(file_0):\n", - " transactions_path = file_0\n", - "elif os.path.isfile(file_1):\n", - " transactions_path = file_1\n", - "else:\n", - " raise FileNotFoundError(\"File not found: \" + transactions)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1be26dae-cafe-4a22-80aa-eff296fc1a9b", - "metadata": {}, - "outputs": [], - "source": [ - "_, transactions_file = os.path.split(transactions_path)\n", - "report_title = f\"Portfolio report for {transactions_file}\"\n", - "report_date = pd.Timestamp.now(tz=user_time_zone).strftime(\"%d %B, %Y\")\n", - "report_time = pd.Timestamp.now(tz=user_time_zone).strftime(\"%H:%M\")\n", - "report_timezone = pd.Timestamp.now(tz=user_time_zone).tzinfo\n", - "report_title, report_date, report_time, report_timezone" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "9363f19a", - "metadata": {}, - "source": [ - "## Data" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "eb3dbd48", - "metadata": {}, - "source": [ - "Loading" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b139556d", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " P = openbb.portfolio.load(transactions_path)\n", - " openbb.portfolio.show(P)\n", - "except Exception as e:\n", - " raise Exception(\"Failed to load the transactions.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "af24ed22", - "metadata": {}, - "outputs": [], - "source": [ - "beta_chart = openbb.portfolio.rbeta_chart(P, external_axes=True)\n", - "\n", - "if beta_chart:\n", - " beta_chart = beta_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "636c743d", - "metadata": {}, - "outputs": [], - "source": [ - "sharpe_chart = openbb.portfolio.rsharpe_chart(P, external_axes=True)\n", - "\n", - "if sharpe_chart:\n", - " sharpe_chart = sharpe_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae0d9381", - "metadata": {}, - "outputs": [], - "source": [ - "volatility_chart = openbb.portfolio.rvol_chart(P, external_axes=True)\n", - "\n", - "if volatility_chart:\n", - " volatility_chart = volatility_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e5e6fff", - "metadata": {}, - "outputs": [], - "source": [ - "sortino_chart = openbb.portfolio.rsort_chart(P, external_axes=True)\n", - "\n", - "if sortino_chart:\n", - " sortino_chart = sortino_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3567b0ca", - "metadata": {}, - "outputs": [], - "source": [ - "maxdd_chart = openbb.portfolio.maxdd_chart(P, external_axes=True)\n", - "\n", - "if maxdd_chart:\n", - " maxdd_chart = maxdd_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5dbcc1eb", - "metadata": {}, - "outputs": [], - "source": [ - "country_allocation = openbb.portfolio.alloc.countries(P)\n", - "\n", - "country_allocation[\"Portfolio\"] = (country_allocation[\"Portfolio\"] * 100).map(\n", - " \"{:.3}%\".format\n", - ")\n", - "country_allocation[\"Benchmark\"] = (country_allocation[\"Benchmark\"] * 100).map(\n", - " \"{:.3}%\".format\n", - ")\n", - "country_allocation[\"Difference\"] = (country_allocation[\"Difference\"] * 100).map(\n", - " \"{:.3}pp\".format\n", - ")\n", - "\n", - "country_allocation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8e1a67a6", - "metadata": {}, - "outputs": [], - "source": [ - "sector_allocation = openbb.portfolio.alloc.sectors(P)\n", - "\n", - "sector_allocation[\"Portfolio\"] = (sector_allocation[\"Portfolio\"] * 100).map(\n", - " \"{:.3}%\".format\n", - ")\n", - "sector_allocation[\"Benchmark\"] = (sector_allocation[\"Benchmark\"] * 100).map(\n", - " \"{:.3}%\".format\n", - ")\n", - "sector_allocation[\"Difference\"] = (sector_allocation[\"Difference\"] * 100).map(\n", - " \"{:.3}pp\".format\n", - ")\n", - "\n", - "sector_allocation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6e1260b4", - "metadata": {}, - "outputs": [], - "source": [ - "distr_chart = openbb.portfolio.distr_chart(P, external_axes=True)\n", - "\n", - "if distr_chart:\n", - " distr_chart = distr_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a668a58b", - "metadata": {}, - "outputs": [], - "source": [ - "dret_chart = openbb.portfolio.dret_chart(P, external_axes=True)\n", - "\n", - "if dret_chart:\n", - " dret_chart = dret_chart.to_html()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1ea71ca6", - "metadata": {}, - "outputs": [], - "source": [ - "yret_chart = openbb.portfolio.yret_chart(P, external_axes=True)\n", - "\n", - "if yret_chart:\n", - " yret_chart = yret_chart.to_html()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "ac795b8e", - "metadata": {}, - "source": [ - "## Render the report template to a file" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ebe0799e", - "metadata": {}, - "outputs": [], - "source": [ - "body = \"\"\n", - "\n", - "\n", - "body += widgets.header(\n", - " \"OpenBB\",\n", - " report_date,\n", - " report_time,\n", - " report_timezone,\n", - " report_title,\n", - " plotly_js=True,\n", - ")\n", - "\n", - "body += widgets.tablinks([\"SUMMARY\", \"In-Depth Analysis\", \"Statistics\"])\n", - "\n", - "# Summary\n", - "htmlcode = widgets.p(\n", - " \"The total performance of the portfolio versus the benchmark is as follows. \"\n", - " \"Please note this calculation assumes partially shares exist for the benchmark.\"\n", - ")\n", - "\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Performance\") + openbb.portfolio.perf(P).to_html()]\n", - ")\n", - "\n", - "htmlcode += widgets.p(\"In which the following yearly returns are achieved:\")\n", - "\n", - "htmlcode += widgets.row([widgets.h(3, \"Yearly Performance\") + yret_chart])\n", - "\n", - "htmlcode += widgets.p(\n", - " \"The portfolio and benchmark consists of the following allocations to countries and sectors.\"\n", - ")\n", - "\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Country Allocation\") + country_allocation.to_html()]\n", - ")\n", - "\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Sector Allocation\") + sector_allocation.to_html()]\n", - ")\n", - "\n", - "htmlcode += widgets.p(\n", - " \"The portfolio consists of the following trades including returns.\"\n", - ")\n", - "\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Portfolio Trades\") + P.portfolio_trades.to_html()]\n", - ")\n", - "\n", - "htmlcode += widgets.p(\n", - " \"The benchmark consists of the following trades including returns.\"\n", - ")\n", - "\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Benchmark Trades\") + P.benchmark_trades.to_html()]\n", - ")\n", - "\n", - "body += widgets.add_tab(\"SUMMARY\", htmlcode)\n", - "\n", - "# In Depth Analysis\n", - "htmlcode = widgets.p(\n", - " \"The following metrics, Tracking Error and Information Ratio, measure whether you are \"\n", - " \"closely aligned with the benchmark. If the Tracking Error is not close to 1, a different benchmark \"\n", - " \"might be a better fit.\"\n", - ")\n", - "\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Tracking Error\") + openbb.portfolio.metric.trackerr(P)[0].to_html()]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [\n", - " widgets.h(3, \"Information Ratio\")\n", - " + openbb.portfolio.metric.information(P).to_html()\n", - " ]\n", - ")\n", - "\n", - "htmlcode += widgets.row([widgets.h(3, \"Beta Chart\") + beta_chart])\n", - "\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Sharpe Ratio\") + openbb.portfolio.metric.sharpe(P).to_html()]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"Sharpe Ratio Chart\") + sharpe_chart])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Volatility\") + openbb.portfolio.metric.volatility(P).to_html()]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"Volatility Chart\") + volatility_chart])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Sortino Ratio\") + openbb.portfolio.metric.sortino(P).to_html()]\n", - ")\n", - "htmlcode += widgets.row([widgets.h(3, \"Sortino Chart\") + sortino_chart])\n", - "\n", - "htmlcode += widgets.row([widgets.h(3, \"Maximum Drawdown\") + maxdd_chart])\n", - "\n", - "\n", - "body += widgets.add_tab(\"In-Depth Analysis\", htmlcode)\n", - "\n", - "# Statistics\n", - "htmlcode = widgets.row([widgets.h(3, \"Distribution\") + distr_chart])\n", - "htmlcode += widgets.row([widgets.h(3, \"Daily Returns\") + dret_chart])\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Volatility\") + openbb.portfolio.metric.volatility(P).to_html()]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Kurtosis\") + openbb.portfolio.metric.kurtosis(P).to_html()]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Skew\") + openbb.portfolio.metric.skew(P).to_html()]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Value at Risk (VaR)\") + openbb.portfolio.var(P).to_html()]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Expected Shortfall (cVaR)\") + openbb.portfolio.es(P).to_html()]\n", - ")\n", - "htmlcode += widgets.row(\n", - " [widgets.h(3, \"Omega Ratio\") + openbb.portfolio.om(P).to_html()]\n", - ")\n", - "\n", - "body += widgets.add_tab(\"Statistics\", htmlcode)\n", - "\n", - "body += widgets.tab_clickable_and_save_evt()\n", - "\n", - "report = widgets.html_report(title=report_name, stylesheet=stylesheet, body=body)\n", - "\n", - "# to save the results\n", - "with open(report_name + \".html\", \"w\", encoding=\"utf-8\") as fh:\n", - " fh.write(report)" - ] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "obb", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.9" - }, - "vscode": { - "interpreter": { - "hash": "100174a9203096c0c10fb537684ff280825ee9e252451beb8786068677204f06" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/openbb_terminal/reports/widget_helpers.py b/openbb_terminal/reports/widget_helpers.py deleted file mode 100644 index dd95eddcbf4..00000000000 --- a/openbb_terminal/reports/widget_helpers.py +++ /dev/null @@ -1,410 +0,0 @@ -"""Widgets Helper Library. - -A library of `ipywidgets` wrappers for notebook based reports and voila dashboards. -The library includes both python code and html/css/js elements that can be found in the -`./widgets` folder. -""" - -import base64 -import os -from typing import List - -from jinja2 import Template - -from openbb_terminal.core.config.paths import PACKAGE_DIRECTORY -from openbb_terminal.core.plots.backend import PLOTLYJS_PATH -from openbb_terminal.rich_config import console - - -def price_card_stylesheet(): - """Load a default CSS stylesheet from file.""" - with open( - os.path.join(os.path.dirname(os.path.abspath(__file__)), "widgets", "style.css") - ) as f: - style = f.read() - return style - - -def price_card(ticker: str, price: str, price_color: str = "neutral_color") -> str: - """Prepare a styled HTML element of a 128 by 128 price card. - - Parameters - ---------- - ticker : str - Instrument ticker for the price card - price : str - Instrument price as a string - price_color : str, optional - The color of the price. Accepts "up_color", "down_color" and default "neutral_color" - - Returns - ------- - str - HTML code as string - """ - with open( - os.path.join(os.path.dirname(os.path.abspath(__file__)), "widgets", "card.j2") - ) as f: - template = Template(f.read()) - card = template.render(ticker=ticker, price=price, price_color=price_color) - return card - - -def html_report_stylesheet(): - """Load a default CSS stylesheet from file.""" - with open( - os.path.join( - os.path.dirname(os.path.abspath(__file__)), "widgets", "report.css" - ) - ) as f: - style = f.read() - return style - - -def html_report(title: str = "", stylesheet: str = "", body: str = "") -> str: - """Prepare a styled HTML page element. - - Parameters - ---------- - title : str, optional - Contents of the title tag, by default "" - stylesheet : str, optional - Contents of the stylesheet tag, by default "" - body : str, optional - Contents of the body tag, by default "" - - Returns - ------- - str - HTML code as string - """ - with open( - os.path.join(os.path.dirname(os.path.abspath(__file__)), "widgets", "report.j2") - ) as f: - template = Template(f.read()) - return template.render(title=title, stylesheet=stylesheet, body=body + "") - - -def h(level: int, text: str) -> str: - """Wrap text into an HTML `h` tag. - - Parameters - ---------- - level : int - HTML `h` level tag - text : str - Contents for `h` level tag - - Returns - ------- - str - HTML code as string - """ - return f"{text}" - - -def p(text: str, style: str = "") -> str: - """Wrap text into an HTML `p` tag. - - Parameters - ---------- - text : str - Contents for HTML `p` tag - style: str - Div style - - Returns - ------- - str - HTML code as string - """ - return f'

{text}

' - - -def row(elements: List) -> str: - """HTML code elements to add in a single row - - Parameters - ---------- - elements : List - List of HTML code elements to add in a row - - Returns - ------- - str - HTML code as string - """ - with open( - os.path.join(os.path.dirname(os.path.abspath(__file__)), "widgets", "row.j2") - ) as f: - template = Template(f.read()) - return template.render(elements=elements) - - -def kpi(thresholds: List[float], sentences: List[str], value: float) -> str: - """Add new key performance indicator to main page of report - - Parameters - ---------- - thresholds : List[float] - List of thresholds to take into account - sentences : List[str] - List of sentences to take into account. len(sentences) = len(thresholds)+1 - value : float - Current value for the KPI in question - - Returns - ------- - str - HTML code as string - """ - if len(thresholds) == 1 and len(sentences) == 2: - if value < thresholds[0]: - return f'

❌ {sentences[0]}. {value} < {thresholds[0]}

' - return f'

✅ {sentences[1]}. {value} > {thresholds[0]}

' - if len(thresholds) == 2 and len(sentences) == 3: - if value < thresholds[0]: - return f'

❌ {sentences[0]}. {value} < {thresholds[0]}

' - if value > thresholds[1]: - return f'

✅ {sentences[2]}. {value} > {thresholds[1]}

' - return f'

🟡 {sentences[1]}. {thresholds[0]} < {value} < {thresholds[1]}

' - console.print("Error. KPI condition is not correctly set") - return "" - - -def add_tab( - title: str, htmlcode: str, comment_cell: bool = True, commment_text: str = "" -) -> str: - """Add new tab section for the report. By default adds an opinion editable box at the start. - - Parameters - ---------- - title : str - Title associated with this tab / section - htmlcode : str - All HTML code contain within this section - comment_cell : bool - Comment cell - - Returns - ------- - str - HTML code as string - """ - html_text = f'

' - if comment_cell: - comment = commment_text if commment_text else "No comment." - html_text += f"""

- {comment} -

""" - html_text += f"{htmlcode}
" - return html_text - - -def tab_clickable_and_save_evt() -> str: - """Adds javascript code within HTML at the bottom that allows the interactivity with tabs. - - Parameters - ---------- - report_name : str - Report name for the file to be saved - - Returns - ------- - str - javascript code in HTML to process interactive tabs - """ - return """ - """ - - -def tablinks(tabs: List[str]) -> str: - """Adds list of tabs/sections for the reports that are able to be clicked. For every - 6 tabs we push them onto a new line. - - Parameters - ---------- - tabs : List[str] - List of tabs/sections for the reports. - - Returns - ------- - str - HTML code for interactive tabs - """ - htmlcode = '
' - for idx, tab in enumerate(tabs): - htmlcode += f"""""" - if ((idx + 1) % 5) == 0: - htmlcode += "
" - htmlcode += "
" - return htmlcode - - -def header( - author: str, - report_date: str, - report_time: str, - report_tz: str, - title: str, - plotly_js: bool = False, -) -> str: - """Creates reports header - - Parameters - ---------- - author : str - Name of author responsible by report - report_date : str - Date when report is run - report_time : str - Time when report is run - report_tz : str - Timezone associated with datetime of report being run - title : str - Title of the report - plotly_js : bool - If True, then we add plotly.js to the report - - Returns - ------- - str - HTML code for interactive tabs - """ - openbb_img_path = PACKAGE_DIRECTORY / "reports/templates/OpenBB_reports_logo.png" - floppy_disk_path = PACKAGE_DIRECTORY / "reports/templates/floppy-disc.png" - - try: - openbb_image_encoded = base64.b64encode(openbb_img_path.read_bytes()) - openbb_img = f""" - OpenBB""" - except Exception: - openbb_img = "" - - try: - floppy_disk_encoded = base64.b64encode(floppy_disk_path.read_bytes()) - flask_disk_save = f""" -
OpenBB
""" - except Exception: - flask_disk_save = "" - - plotly_script = "" - - if plotly_js: - plotly_script = """ - - """ - try: - plotly_script += ( - f"""""" - ) - except Exception: - plotly_script += ( - "" - ) - - return f""" - - - - OpenBB Terminal Report - - - {plotly_script} -
- {openbb_img} -
-

Analyst: {author}

-

Date : {report_date}

-

Time : {report_time} {report_tz}

-
-

{title}

-
- -
- """ - - -def add_external_fig(figloc: str, style: str = "") -> str: - """Add external figure to HTML - - Parameters - ---------- - figloc : str - Relative figure location - style: str - Div style - - Returns - ------- - str - HTML code for figure - """ - try: - with open(figloc, "rb") as image_file: - img = base64.b64encode(image_file.read()).decode() - return f'' - except Exception: - return "" diff --git a/openbb_terminal/reports/widgets/card.j2 b/openbb_terminal/reports/widgets/card.j2 deleted file mode 100644 index 09e630ad46b..00000000000 --- a/openbb_terminal/reports/widgets/card.j2 +++ /dev/null @@ -1,8 +0,0 @@ -
-
-
-

{{ ticker }}

-

{{ price }}

-
-
-
diff --git a/openbb_terminal/reports/widgets/report.css b/openbb_terminal/reports/widgets/report.css deleted file mode 100644 index d90f38eb92a..00000000000 --- a/openbb_terminal/reports/widgets/report.css +++ /dev/null @@ -1,225 +0,0 @@ -.body { - background-color: #ffffff; - color: #000000; - font-weight: 400; - text-decoration: none; - vertical-align: baseline; - font-size: 12pt; - font-family: "Consolas", Arial, Helvetica, sans-serif; - font-style: normal; - margin-top: 30px; - margin-bottom: 30px; - margin-left: 50px; - margin-left: 50px; - padding: 0; -} - -.title { - padding-top: 0pt; - color: #000000; - font-size: 42pt; - font-weight: 800; - padding-bottom: 0pt; - line-height: 1.15; - page-break-after: avoid; - text-align: center; -} - -.subtitle { - padding-top: 0pt; - color: #666666; - font-size: 33pt; - padding-bottom: 10pt; - line-height: 1.15; - page-break-after: avoid; - font-style: italic; - text-align: center; -} - -li { - color: #000000; - font-size: 13pt; -} - -p { - margin: 0; - color: #000000; - font-size: 13pt; -} - -h1 { - padding-top: 10pt; - color: #000000; - font-size: 28pt; - padding-bottom: 0pt; - line-height: 1.15; - page-break-after: avoid; - text-align: left; -} - -h2 { - padding-top: 10pt; - color: #000000; - font-weight: 700; - font-size: 21pt; - padding-bottom: 0pt; - line-height: 1.15; - page-break-after: avoid; - text-align: left; -} - -h3 { - padding-top: 8pt; - color: #666666; - font-weight: 700; - font-size: 14pt; - padding-bottom: 0pt; - line-height: 1.15; - page-break-after: avoid; - text-align: left; -} - -h4 { - padding-top: 8pt; - -webkit-text-decoration-skip: none; - color: #666666; - text-decoration: underline; - font-size: 13pt; - padding-bottom: 0pt; - line-height: 1.15; - page-break-after: avoid; - text-decoration-skip-ink: none; - text-align: left; -} - -h5 { - padding-top: 8pt; - color: #666666; - font-size: 13pt; - padding-bottom: 0pt; - line-height: 1.15; - page-break-after: avoid; - text-align: left; -} - -h6 { - padding-top: 8pt; - color: #666666; - font-size: 13pt; - padding-bottom: 0pt; - line-height: 1.15; - page-break-after: avoid; - font-style: italic; - text-align: left; -} - -table { - border-collapse: collapse; - border-spacing: 0; - border-width: 0; - border-top-color: #ffffff; - border-bottom-color: #ffffff; - border-left-color: #ffffff; - border-right-color: #ffffff; - color: black; - table-layout: fixed; - margin-left: 0; - margin-right: 0; - margin-top: 15pt; - margin-bottom: 30pt; - padding: 3pt; -} - -table thead { - color: black; - text-align: center; -} - -table thead tr th { - border-top: 0; - border-bottom: 0; - border-left: 1; - border-left-color: #000000; - border-right: 1; - border-right-color: #000000; - padding-right: 5pt; - padding-left: 5pt; - font-weight: 600; -} - -table thead tr th:first-child { - border-left: 0; -} - -table thead tr th:last-child { - border-right: 0; -} - -table tbody { - color: black; -} - -table tbody tr td { - border: 1; - border-color: #000000; - padding-left: 7pt; - padding-right: 7pt; -} - -table tbody tr th { - border: 1; - border-color: #000000; - font-weight: 200; - text-align: right; - padding-left: 7pt; - padding-right: 7pt; -} - -table tbody tr th:first-child { - border-left: 0; -} - -table tbody tr th:last-child { - border-right: 0; -} - -table tbody tr:last-child th { - border-bottom: 0; -} - -table tbody tr td:first-child { - border-left: 0; -} - -table tbody tr td:last-child { - border-right: 0; -} - -table tbody tr:last-child td { - border-bottom: 0; -} - -tbody tr:nth-child(even) { - background-color: rgb(216, 216, 216); -} - -@media print { - table { - -webkit-print-color-adjust: exact; - } -} - -.hcontainer { - margin: 10pt; - width: 1000px; - display: flex; -} - -.item { - display: inline-block; - zoom: 1; - padding-left: 10pt; - padding-right: 10pt; - vertical-align: top; - flex: 1; -} diff --git a/openbb_terminal/reports/widgets/report.j2 b/openbb_terminal/reports/widgets/report.j2 deleted file mode 100644 index cfd9124ac77..00000000000 --- a/openbb_terminal/reports/widgets/report.j2 +++ /dev/null @@ -1,19 +0,0 @@ - - - - - {% if title %} - {{ title }} - {% else %} - OpenBB Terminal - {% endif %} - {% if stylesheet %} - - {% endif %} - - - {{ body }} - - diff --git a/openbb_terminal/reports/widgets/row.j2 b/openbb_terminal/reports/widgets/row.j2 deleted file mode 100644 index 5e8a5a6bb4d..00000000000 --- a/openbb_terminal/reports/widgets/row.j2 +++ /dev/null @@ -1,5 +0,0 @@ -
- {% for item in elements %} -
{{ item }}
- {% endfor %} -
diff --git a/openbb_terminal/reports/widgets/style.css b/openbb_terminal/reports/widgets/style.css deleted file mode 100644 index 7584138ab99..00000000000 --- a/openbb_terminal/reports/widgets/style.css +++ /dev/null @@ -1,54 +0,0 @@ -.container { - position: relative; -} - -.container .card { - font-family: Consolas, sans-serif; - position: relative; - width: 128px; - height: 128px; - background: #000000; - border-radius: 5px; - border-color: rgb(142, 142, 142); - border-style: solid; - border-width: 0.5px; - overflow: hidden; -} - -.container .card .contentBx { - position: absolute; - top: 28; - bottom: 32; - width: 100%; - text-align: center; - z-index: 10; -} - -.container .card .contentBx h1 { - position: relative; - font-size: 24px; - font-weight: 600; - letter-spacing: 1px; - color: #fff; - margin-top: 28px; - margin-bottom: 10px; -} - -.container .card .contentBx h2 { - position: relative; - font-size: 24px; - font-weight: 600; - letter-spacing: 1px; -} - -.up_color { - color: #00ACFF; -} - -.down_color { - color: #e4003a; -} - -.neutral_color { - color: #ffed00; -} diff --git a/openbb_terminal/requirements-full.txt b/openbb_terminal/requirements-full.txt deleted file mode 100644 index 19987f21814..00000000000 --- a/openbb_terminal/requirements-full.txt +++ /dev/null @@ -1,444 +0,0 @@ -absl-py==1.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -adagio==0.2.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiodns==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiofiles==22.1.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -aiohttp==3.8.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiosignal==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiosqlite==0.19.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -alabaster==0.7.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -alpha-vantage==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -altair==5.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -altgraph==0.17.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ansi2html==1.8.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ansiwrap==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -antlr4-python3-runtime==4.11.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -anyio==3.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -appdirs==1.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -appnope==0.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" or python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and platform_system == "Darwin" -arch==5.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -argon2-cffi-bindings==21.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -argon2-cffi==21.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -arrow==1.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ascii-magic==1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -astor==0.8.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -astroid==2.15.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -astropy==5.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -asttokens==2.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -async-timeout==4.0.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -atomicwrites==1.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "win32" -attrs==21.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -babel==2.12.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -backcall==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -backoff==2.2.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -backports-zoneinfo==0.2.1 ; python_full_version >= "3.8.1" and python_version < "3.9" -bandit==1.7.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -base58==2.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -beautifulsoup4==4.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -black==23.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bleach==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -blinker==1.6.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -brotli==1.0.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and platform_python_implementation == "CPython" -brotlicffi==1.0.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and platform_python_implementation != "CPython" -bs4==0.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bt==0.2.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cachetools==5.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -catboost==1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ccxt==4.0.33 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -certifi==2023.5.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cffi==1.15.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cfgv==3.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -charset-normalizer==3.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -click==8.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cloudpickle==2.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cmdstanpy==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -codespell==2.2.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -comm==0.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -commonmark==0.9.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -contourpy==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -convertdate==2.4.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -coverage==7.2.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -coverage[toml]==7.2.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cryptography==41.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cssselect2==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cssselect==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cvxpy==1.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cycler==0.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cython==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dash-core-components==2.0.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dash-html-components==2.0.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dash-table==5.0.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dash==2.9.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dataclasses-json==0.5.12 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dateparser==1.1.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -datetime==5.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -debugpy==1.6.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -decorator==5.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -defusedxml==0.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -degiro-connector==2.0.21 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -detecta==0.0.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dill==0.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -distlib==0.3.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dnspython==2.4.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -docutils==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ecdsa==0.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ecos==2.0.12 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -entrypoints==0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ephem==4.1.4 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -et-xmlfile==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -exceptiongroup==1.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -exchange-calendars==4.2.8 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -execnet==2.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -executing==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fastjsonschema==2.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -feedparser==6.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ffmpeg-python==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ffn==0.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -filelock==3.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -financedatabase==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finnhub-python==2.4.18 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finviz==1.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finvizfinance==0.14.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -flake8==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -flask==2.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -fonttools==4.41.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -formulaic==0.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fqdn==1.5.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -fred==3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fredapi==0.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -freezegun==1.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -frozendict==2.3.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -frozenlist==1.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fs==2.4.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fsspec==2023.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fsspec[http]==2023.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fugue-sql-antlr==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fugue==0.8.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fundamentalanalysis==0.2.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -future==0.18.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -gitdb==4.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -gitpython==3.1.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -google-auth-oauthlib==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -google-auth==2.22.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -graphlib-backport==1.0.3 ; python_full_version >= "3.8.1" and python_version < "3.9" -graphviz==0.20.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -greenlet==2.0.2 ; python_full_version >= "3.8.1" and platform_machine == "aarch64" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "ppc64le" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "x86_64" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "amd64" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "AMD64" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "win32" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "WIN32" and python_full_version != "3.9.7" and python_version < "3.11" -grpcio==1.56.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -h11==0.14.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -hijri-converter==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -holidays==0.14.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -html5lib==1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -httpcore==0.17.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -huggingface-hub==0.16.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -identify==2.5.25 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -idna==3.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -imagesize==1.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -importlib-metadata==6.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -importlib-resources==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -inflection==0.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -iniconfig==2.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -interface-meta==1.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -intrinio-sdk==6.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipykernel==6.24.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipympl==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipython-genutils==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipython==8.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipywidgets==8.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -iso8601==0.1.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -isodate==0.6.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -isoduration==20.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -isort==5.12.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -itsdangerous==2.1.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -jedi==0.18.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jinja2==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -joblib==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -json5==0.9.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonpointer==2.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonschema==4.17.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonschema[format-nongpl]==4.17.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-client==7.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-core==5.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-dash==0.4.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -jupyter-events==0.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server-fileid==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server-ydoc==0.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server==1.23.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-ydoc==0.2.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-pygments==0.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-server==2.23.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-widgets==3.0.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab==3.6.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -kiwisolver==1.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -korean-lunar-calendar==0.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -langchain==0.0.237 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -langsmith==0.0.10 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -lazy-object-proxy==1.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lightgbm==3.3.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lightning-utilities==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -linearmodels==4.31 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -llama-index==0.7.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -llvmlite==0.40.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -loguru==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lunarcalendar==0.0.9 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -lxml==4.9.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -macholib==1.16.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" and sys_platform == "darwin" -markdown-it-py==1.1.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -markdown==3.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -markupsafe==2.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -marshmallow==3.19.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -matplotlib-inline==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -matplotlib==3.7.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mccabe==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mdit-py-plugins==0.2.8 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -mistune==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mock==4.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -monotonic==1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -more-itertools==9.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mplfinance==0.12.9b7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mstarpy==0.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -multidict==6.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -multitasking==0.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mutagen==1.46.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mypy-extensions==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mypy==1.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -myst-parser==0.15.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbclassic==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbclient==0.6.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbconvert==7.7.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbformat==5.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbmake==1.4.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -nest-asyncio==1.5.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -networkx==3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nfoursid==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nodeenv==1.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -notebook-shim==0.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -notebook==6.5.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -numba==0.57.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -numexpr==2.8.4 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -numpy==1.23.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -oandapyv20==0.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -oauthlib==3.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -onetimepass==1.0.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -openai-whisper==20230124 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -openai==0.27.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -openapi-schema-pydantic==1.2.4 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -openpyxl==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -orjson==3.9.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -osqp==0.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -packaging==23.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-datareader==0.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-market-calendars==3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-ta==0.3.14b ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas==1.5.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandocfilters==1.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -papermill==2.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -parso==0.8.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pathspec==0.11.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -patsy==0.5.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pbr==5.11.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pefile==2023.2.7 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" and sys_platform == "win32" -pexpect==4.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform != "win32" -pickleshare==0.7.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pillow==9.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pkgutil-resolve-name==1.3.10 ; python_full_version >= "3.8.1" and python_version < "3.9" -platformdirs==3.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -plotly-resampler==0.8.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -plotly==5.15.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pluggy==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pmaw==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pmdarima==2.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -posthog==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -praw==7.7.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -prawcore==2.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pre-commit==2.21.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -prometheus-client==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -prompt-toolkit==3.0.39 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -property-cached==1.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -prophet==1.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -protobuf==3.20.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -psutil==5.9.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ptyprocess==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform != "win32" or python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and os_name != "nt" -pure-eval==0.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -py==1.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyarrow==12.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyasn1-modules==0.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyasn1==0.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycares==4.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycodestyle==2.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycoingecko==3.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycparser==2.21 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycryptodome==3.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycryptodomex==3.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydantic==1.10.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydeck==0.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydeprecate==0.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydocstyle==6.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyerfa==2.0.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyflakes==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pygments==2.15.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyhdfe==0.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyinstaller-hooks-contrib==2023.6 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pyinstaller==4.10 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pylint==2.17.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyluach==2.2.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pymeeus==0.5.12 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pympler==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyobjc-core==9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" -pyobjc-framework-cocoa==9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" -pyod==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyotp==2.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyparsing==3.0.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyprind==2.11.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyrsistent==0.19.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-cov==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-mock==3.11.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-recorder==0.2.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pytest-recording==0.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-timeout==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-xdist==3.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest==6.2.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pythclient==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-binance==1.0.17 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-coinmarketcap==0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-dateutil==2.8.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-dotenv==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-i18n==0.3.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-jose==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-json-logger==2.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytorch-lightning==1.6.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytrends==4.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytz-deprecation-shim==0.1.0.post0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytz==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pywin32-ctypes==0.2.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" and sys_platform == "win32" -pywin32==306 ; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pywinpty==2.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and os_name == "nt" -pywry==0.5.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyzmq==25.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -qdldl==0.1.7.post0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -qpd==0.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -quandl==3.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rdflib==6.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -regex==2023.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -reportlab==3.6.13 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -requests-futures==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -requests-oauthlib==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -requests==2.31.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -retrying==1.3.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rfc3339-validator==0.1.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rfc3986-validator==0.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rich==12.6.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -riskfolio-lib==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -robin-stocks==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rsa==4.9 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ruamel-yaml-clib==0.2.7 ; platform_python_implementation == "CPython" and python_version < "3.11" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" -ruamel-yaml==0.17.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ruff==0.0.256 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scikit-learn==1.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scipy==1.10.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -screeninfo==0.6.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scs==3.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -seaborn==0.11.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -semantic-version==2.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -send2trash==1.8.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setproctitle==1.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools-rust==1.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools-scm[toml]==7.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools==65.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sgmllib3k==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -shap==0.42.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -six==1.16.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -slicer==0.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -smmap==5.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sniffio==1.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -snowballstemmer==2.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -soupsieve==2.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sparqlwrapper==2.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinx==4.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-applehelp==1.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-devhelp==1.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-htmlhelp==2.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-jsmath==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-qthelp==1.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-serializinghtml==1.1.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sqlalchemy==2.0.19 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sqlglot==17.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -squarify==0.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -stack-data==0.6.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -statsforecast==1.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -statsmodels==0.13.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -stevedore==5.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -stocksera==0.1.21 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -streamlit==1.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -svglib==1.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tabulate==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tbats==1.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tenacity==8.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tensorboard-data-server==0.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tensorboard==2.13.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -terminado==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -textwrap3==0.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -thepassiveinvestor==1.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -threadpoolctl==3.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tiktoken==0.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -time-machine==2.11.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -tinycss2==1.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tokenizers==0.13.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tokenterminal==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -toml==0.10.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tomli==2.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tomlkit==0.11.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -toolz==0.12.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -torch==1.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -torchmetrics==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tornado==6.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tqdm==4.65.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -trace-updater==0.0.9.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -tradingview-ta==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -traitlets==5.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -transformers==4.27.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -triad==0.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-python-dateutil==2.8.19.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-pytz==2021.3.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-pyyaml==6.0.12.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-requests==2.31.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-setuptools==57.4.18 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-six==1.16.21.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-urllib3==1.26.25.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -typing-extensions==4.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -typing-inspect==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tzdata==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tzlocal==4.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -u8darts[torch]==0.23.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ujson==5.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -update-checker==0.18.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -uri-template==1.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -urllib3==1.26.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -user-agent==0.1.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -vadersentiment==3.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -validators==0.20.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -valinvest==0.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -vcrpy==4.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -virtualenv==20.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -watchdog==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -wcwidth==0.2.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -webcolors==1.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -webencodings==0.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -websocket-client==1.6.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -websockets==11.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -werkzeug==2.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -wheel==0.40.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -widgetsnbextension==4.0.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -win32-setctime==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "win32" -wrapt==1.15.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -xarray==2023.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -xgboost==1.7.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -xlsxwriter==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -y-py==0.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yahooquery==2.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yarl==1.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yfinance==0.2.25 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ypy-websocket==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yt-dlp==2023.7.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -zipp==3.16.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -zope-interface==6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" diff --git a/openbb_terminal/requirements.txt b/openbb_terminal/requirements.txt deleted file mode 100644 index 115a934a9a7..00000000000 --- a/openbb_terminal/requirements.txt +++ /dev/null @@ -1,298 +0,0 @@ -aiodns==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiofiles==22.1.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -aiohttp==3.8.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiosignal==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiosqlite==0.19.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -alpha-vantage==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -altair==5.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ansiwrap==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -anyio==3.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -appdirs==1.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -appnope==0.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" or python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and platform_system == "Darwin" -arch==5.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -argon2-cffi-bindings==21.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -argon2-cffi==21.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -arrow==1.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ascii-magic==1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -astor==0.8.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -asttokens==2.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -async-timeout==4.0.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -attrs==21.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -babel==2.12.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -backcall==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -backoff==2.2.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -backports-zoneinfo==0.2.1 ; python_full_version >= "3.8.1" and python_version < "3.9" -base58==2.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -beautifulsoup4==4.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bleach==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -blinker==1.6.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bs4==0.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bt==0.2.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cachetools==5.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ccxt==4.0.33 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -certifi==2023.5.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cffi==1.15.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -charset-normalizer==3.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -click==8.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -comm==0.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -commonmark==0.9.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -contourpy==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -convertdate==2.4.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -cryptography==41.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cssselect2==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cssselect==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cycler==0.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cython==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dataclasses-json==0.5.12 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dateparser==1.1.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -datetime==5.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -debugpy==1.6.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -decorator==5.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -defusedxml==0.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -degiro-connector==2.0.21 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -detecta==0.0.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dnspython==2.4.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ecdsa==0.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -entrypoints==0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -et-xmlfile==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -exceptiongroup==1.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -exchange-calendars==4.2.8 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -executing==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fastjsonschema==2.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -feedparser==6.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ffn==0.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -financedatabase==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finnhub-python==2.4.18 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finviz==1.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finvizfinance==0.14.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -flake8==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fonttools==4.41.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -formulaic==0.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fqdn==1.5.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -fred==3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fredapi==0.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -frozendict==2.3.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -frozenlist==1.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fsspec==2023.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fundamentalanalysis==0.2.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -future==0.18.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -gitdb==4.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -gitpython==3.1.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -graphlib-backport==1.0.3 ; python_full_version >= "3.8.1" and python_version < "3.9" -greenlet==2.0.2 ; python_full_version >= "3.8.1" and platform_machine == "aarch64" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "ppc64le" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "x86_64" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "amd64" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "AMD64" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "win32" and python_full_version != "3.9.7" and python_version < "3.11" or python_full_version >= "3.8.1" and platform_machine == "WIN32" and python_full_version != "3.9.7" and python_version < "3.11" -grpcio==1.56.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -h11==0.14.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -hijri-converter==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -holidays==0.14.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -html5lib==1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -httpcore==0.17.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -idna==3.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -importlib-metadata==6.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -importlib-resources==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.10" -inflection==0.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -interface-meta==1.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -intrinio-sdk==6.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipykernel==6.24.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipympl==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipython-genutils==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipython==8.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipywidgets==8.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -iso8601==0.1.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -isodate==0.6.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -isoduration==20.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jedi==0.18.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jinja2==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -joblib==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -json5==0.9.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonpointer==2.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonschema==4.17.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonschema[format-nongpl]==4.17.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-client==7.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-core==5.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-events==0.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server-fileid==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server-ydoc==0.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server==1.23.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-ydoc==0.2.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-pygments==0.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-server==2.23.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-widgets==3.0.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab==3.6.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -kiwisolver==1.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -korean-lunar-calendar==0.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -langchain==0.0.237 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -langsmith==0.0.10 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -linearmodels==4.31 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -llama-index==0.7.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -loguru==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lxml==4.9.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -markupsafe==2.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -marshmallow==3.19.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -matplotlib-inline==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -matplotlib==3.7.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mccabe==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mistune==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -monotonic==1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -more-itertools==9.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mplfinance==0.12.9b7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mstarpy==0.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -multidict==6.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -multitasking==0.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mypy-extensions==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbclassic==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbclient==0.6.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbconvert==7.7.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbformat==5.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nest-asyncio==1.5.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -notebook-shim==0.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -notebook==6.5.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -numexpr==2.8.4 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -numpy==1.23.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -oandapyv20==0.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -onetimepass==1.0.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -openai==0.27.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -openapi-schema-pydantic==1.2.4 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -openpyxl==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -packaging==23.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-datareader==0.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-market-calendars==3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-ta==0.3.14b ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas==1.5.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandocfilters==1.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -papermill==2.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -parso==0.8.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -patsy==0.5.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pexpect==4.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform != "win32" -pickleshare==0.7.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pillow==9.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pkgutil-resolve-name==1.3.10 ; python_full_version >= "3.8.1" and python_version < "3.9" -platformdirs==3.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -plotly==5.15.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pmaw==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -posthog==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -praw==7.7.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -prawcore==2.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -prometheus-client==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -prompt-toolkit==3.0.39 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -property-cached==1.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -protobuf==3.20.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -psutil==5.9.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ptyprocess==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform != "win32" or python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and os_name != "nt" -pure-eval==0.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyarrow==12.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyasn1==0.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycares==4.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycodestyle==2.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycoingecko==3.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycparser==2.21 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycryptodome==3.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydantic==1.10.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydeck==0.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyflakes==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pygments==2.15.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyhdfe==0.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyluach==2.2.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pymeeus==0.5.12 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pympler==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyobjc-core==9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" -pyobjc-framework-cocoa==9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" -pyotp==2.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyparsing==3.0.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyprind==2.11.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyrsistent==0.19.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pythclient==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-binance==1.0.17 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-coinmarketcap==0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-dateutil==2.8.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-dotenv==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-i18n==0.3.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-jose==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-json-logger==2.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytrends==4.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytz-deprecation-shim==0.1.0.post0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytz==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pywin32==306 ; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pywinpty==2.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and os_name == "nt" -pywry==0.5.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyzmq==25.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -quandl==3.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rdflib==6.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -regex==2023.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -reportlab==3.6.13 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -requests-futures==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -requests==2.31.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -retrying==1.3.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rfc3339-validator==0.1.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rfc3986-validator==0.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rich==12.6.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -robin-stocks==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rsa==4.9 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ruamel-yaml-clib==0.2.7 ; platform_python_implementation == "CPython" and python_version < "3.11" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" -ruamel-yaml==0.17.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scikit-learn==1.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scipy==1.10.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -screeninfo==0.6.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -seaborn==0.11.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -send2trash==1.8.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setproctitle==1.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools-scm[toml]==7.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools==65.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sgmllib3k==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -six==1.16.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -smmap==5.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sniffio==1.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -soupsieve==2.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sparqlwrapper==2.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sqlalchemy==2.0.19 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -squarify==0.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -stack-data==0.6.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -statsmodels==0.13.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -stocksera==0.1.21 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -streamlit==1.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -svglib==1.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tabulate==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tenacity==8.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -terminado==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -textwrap3==0.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -thepassiveinvestor==1.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -threadpoolctl==3.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tiktoken==0.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tinycss2==1.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tokenterminal==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -toml==0.10.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tomli==2.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -toolz==0.12.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -tornado==6.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tqdm==4.65.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tradingview-ta==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -traitlets==5.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -typing-extensions==4.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -typing-inspect==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tzdata==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tzlocal==4.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ujson==5.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -update-checker==0.18.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -uri-template==1.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -urllib3==1.26.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -user-agent==0.1.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -vadersentiment==3.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -validators==0.20.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -valinvest==0.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -watchdog==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -wcwidth==0.2.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -webcolors==1.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -webencodings==0.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -websocket-client==1.6.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -websockets==11.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -widgetsnbextension==4.0.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -win32-setctime==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "win32" -wrapt==1.15.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -y-py==0.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yahooquery==2.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yarl==1.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yfinance==0.2.25 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ypy-websocket==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -zipp==3.16.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -zope-interface==6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" diff --git a/openbb_terminal/rich_config.py b/openbb_terminal/rich_config.py deleted file mode 100644 index 59a56a8d71e..00000000000 --- a/openbb_terminal/rich_config.py +++ /dev/null @@ -1,300 +0,0 @@ -"""Rich Module""" - -__docformat__ = "numpy" - -from typing import Iterable, List, Optional, Tuple, Union - -import i18n -from rich import panel -from rich.console import Console, Theme -from rich.progress import track -from rich.text import Text - -from openbb_terminal.core.plots.plotly_helper import theme -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.core.session.current_user import get_current_user - -# pylint: disable=no-member,c-extension-no-member - - -# https://rich.readthedocs.io/en/stable/appendix/colors.html#appendix-colors -# https://rich.readthedocs.io/en/latest/highlighting.html#custom-highlighters - - -RICH_TAGS = [ - "[menu]", - "[/menu]", - "[cmds]", - "[/cmds]", - "[info]", - "[/info]", - "[param]", - "[/param]", - "[src]", - "[/src]", - "[help]", - "[/help]", -] - -USE_COLOR = True - - -def translate(key: str): - return i18n.t(key) - - -def no_panel(renderable, *args, **kwargs): # pylint: disable=unused-argument - return renderable - - -def get_ordered_list_sources(command_path: str) -> List: - """ - Returns the preferred source for the given command. - - Parameters - ---------- - command_path: str - The command to find the source for. E.g. "stocks/load - - Returns - ------- - List - The list of sources for the given command. - """ - command_path = command_path[1:] if command_path.startswith("/") else command_path - return get_current_user().sources.choices.get(command_path, []) - - -class MenuText: - """Create menu text with rich colors to be displayed by terminal""" - - def __init__(self, path: str = "", column_sources: int = 100): - """Initialize menu help - - Parameters - ---------- - path : str - path to the menu that is being created - column_sources : int - column width from which to start displaying sources - """ - self.menu_text = "" - self.menu_path = path - self.col_src = column_sources - - def add_raw(self, raw_text: str): - """Append raw text (no translation) to a menu - - Parameters - ---------- - raw_text : str - raw text to be appended to the menu - """ - self.menu_text += raw_text - - def add_custom(self, key: str): - """Append custom text (after translation from key) to a menu - - Parameters - ---------- - key : str - key to get translated text and add to the menu - """ - self.menu_text += f"{i18n.t(self.menu_path + key)}" - - def add_info(self, key_info: str): - """Append info text (after translation from key) to a menu - - Parameters - ---------- - key_info : str - key to get translated text and add to the menu as info - """ - self.menu_text += f"[info]{i18n.t(self.menu_path + key_info)}:[/info]\n" - - def add_param(self, key_param: str, value: str, col_align: int = 0): - """Append info text (after translation from key) to a menu - - Parameters - ---------- - key_param : str - key to get translated text and add to the menu as parameter - value : str - value to display in front of the parameter - col_align : int - column alignment for the value. This allows for a better UX experience. - """ - parameter_translated = i18n.t(self.menu_path + key_param) - space = ( - (col_align - len(parameter_translated)) * " " - if col_align > len(parameter_translated) - else "" - ) - self.menu_text += f"[param]{parameter_translated}{space}:[/param] {value}\n" - - def add_cmd(self, key_command: str, condition: bool = True): - """Append command text (after translation from key) to a menu - - Parameters - ---------- - key_command : str - key command to be executed by user. It is also used as a key to get description of command. - condition : bool - condition in which command is available to user. I.e. displays command and description. - If condition is false, the command line is greyed out. - """ - spacing = (23 - (len(key_command) + 4)) * " " - if condition: - cmd = f"[cmds] {key_command}{spacing}{i18n.t(self.menu_path + key_command)}[/cmds]" - else: - cmd = f"[unvl] {key_command}{spacing}{i18n.t(self.menu_path + key_command)}[/unvl]" - - sources = get_ordered_list_sources(f"/{self.menu_path}{key_command}") - - if sources: - space = (self.col_src - len(cmd)) * " " if self.col_src > len(cmd) else " " - cmd += f"{space}[src][{', '.join(sources)}][/src]" - - self.menu_text += cmd + "\n" - - def add_menu(self, key_menu: str, condition: Optional[Union[bool, str]] = True): - """Append menu text (after translation from key) to a menu - - Parameters - ---------- - key_menu : str - key menu to be executed by user. It is also used as a key to get description of menu. - condition : bool - condition in which menu is available to user. I.e. displays menu and description. - If condition is false, the menu line is greyed out. - """ - spacing = (23 - (len(key_menu) + 4)) * " " - if condition: - self.menu_text += f"[menu]> {key_menu}{spacing}{i18n.t(self.menu_path + key_menu)}[/menu]\n" - else: - self.menu_text += f"[unvl]> {key_menu}{spacing}{i18n.t(self.menu_path + key_menu)}[/unvl]\n" - - def add_setting(self, key_setting: str, status: bool = True): - """Append menu text (after translation from key) to a menu - - Parameters - ---------- - key_setting : str - key setting to be set by user. It is also used as a key to get description of the setting. - status : bool - status of the current setting. If true the line will be green, otherwise red. - """ - spacing = (23 - (len(key_setting) + 4)) * " " - if status: - self.menu_text += f"[green] {key_setting}{spacing}{i18n.t(self.menu_path + key_setting)}[/green]\n" - else: - self.menu_text += f"[red] {key_setting}{spacing}{i18n.t(self.menu_path + key_setting)}[/red]\n" - - -class ConsoleAndPanel: - """Create a rich console to wrap the console print with a Panel""" - - def __init__(self): - self.preferences = get_current_user().preferences - self.__console = Console( - theme=Theme(theme.console_style), highlight=False, soft_wrap=True - ) - self.menu_text = "" - self.menu_path = "" - - def reload_console(self): - current_preferences = get_current_user().preferences - if current_preferences != self.preferences: - self.preferences = current_preferences - theme.apply_console_style(current_preferences.RICH_STYLE) - self.__console = Console( - theme=Theme(theme.console_style), highlight=False, soft_wrap=True - ) - - def capture(self): - return self.__console.capture() - - @staticmethod - def filter_rich_tags(text): - for val in RICH_TAGS: - text = text.replace(val, "") - - return text - - @staticmethod - def blend_text( - message: str, color1: Tuple[int, int, int], color2: Tuple[int, int, int] - ) -> Text: - """Blend text from one color to another.""" - text = Text(message) - r1, g1, b1 = color1 - r2, g2, b2 = color2 - dr = r2 - r1 - dg = g2 - g1 - db = b2 - b1 - size = len(text) + 5 - for index in range(size): - blend = index / size - color = f"#{int(r1 + dr * blend):02X}{int(g1 + dg * blend):02X}{int(b1 + db * blend):02X}" - text.stylize(color, index, index + 1) - return text - - def print(self, *args, **kwargs): - self.reload_console() - current_user = get_current_user() - if kwargs and "text" in list(kwargs) and "menu" in list(kwargs): - if not get_current_system().TEST_MODE: - if current_user.preferences.ENABLE_RICH_PANEL: - if current_user.preferences.SHOW_VERSION: - version = get_current_system().VERSION - version = f"[param]OpenBB Terminal v{version}[/param] (https://openbb.co)" - else: - version = "[param]OpenBB Terminal[/param] (https://openbb.co)" - self.__console.print( - panel.Panel( - "\n" + kwargs["text"], - title=kwargs["menu"], - subtitle_align="right", - subtitle=version, - ) - ) - - else: - self.__console.print(kwargs["text"]) - else: - print(self.filter_rich_tags(kwargs["text"])) # noqa: T201 - elif not get_current_system().TEST_MODE: - self.__console.print(*args, **kwargs) - else: - print(*args, **kwargs) # noqa: T201 - - def input(self, *args, **kwargs): - self.print(*args, **kwargs, end="") - return input() - - -console = ConsoleAndPanel() - - -def optional_rich_track( - inputs: Iterable, - suppress_output: bool = False, - desc: str = "", - total: Optional[int] = None, -): - """Generate a rich track progress bar if desired - - Parameters - ---------- - inputs : Iterable - The items to be looped through - suppress_output : bool, optional - Flag to suppress the output, by default False - desc : str, optional - String to describe the progress bar, by default "" - total : Optional[int], optional - Total number of items to be looped through, by default None - """ - if suppress_output: - return inputs - return track(inputs, description=desc, total=total) diff --git a/openbb_terminal/sdk.py b/openbb_terminal/sdk.py deleted file mode 100644 index 1caf410101e..00000000000 --- a/openbb_terminal/sdk.py +++ /dev/null @@ -1,643 +0,0 @@ -"""OpenBB Terminal SDK.""" - -# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### # - - -# flake8: noqa - - -# pylint: disable=unused-import,wrong-import-order - - -# pylint: disable=C0302,W0611,R0902,R0903,C0412,C0301,not-callable - - -import logging - - -import openbb_terminal.config_terminal as cfg - - -from openbb_terminal import helper_funcs as helper # noqa: F401 - - -from openbb_terminal.core.plots.plotly_helper import theme # noqa: F401 - - -from openbb_terminal.cryptocurrency.due_diligence.pycoingecko_model import Coin - - -from openbb_terminal.dashboards.dashboards_controller import DashboardsController - - -from openbb_terminal.helper_classes import TerminalStyle # noqa: F401 - - -from openbb_terminal.reports import widget_helpers as widgets # noqa: F401 - - -from openbb_terminal.reports.reports_controller import ReportController - - -import openbb_terminal.core.sdk.sdk_init as lib - - -from openbb_terminal.core.sdk import ( - controllers as ctrl, - models as model, -) - - -from openbb_terminal.core.session.current_system import get_current_system - - -from openbb_terminal.core.session.current_user import is_local - - -from openbb_terminal.terminal_helper import is_auth_enabled - - -cfg.setup_config_terminal(is_sdk=True) - - -logger = logging.getLogger(__name__) - - -cfg.theme.applyMPLstyle() - - -class OpenBBSDK: - """OpenBB SDK Class. - - Attributes: - `login`: Login and load user info.\n - `logout`: Logout and clear session.\n - `news`: Access news from either feedparser or biztoc for a given term or from specified sources\n - `whoami`: Display user info.\n - """ - - __version__ = get_current_system().VERSION - - def __init__(self): - SDKLogger() - self.login = lib.sdk_session.login - self.logout = lib.sdk_session.logout - self.news = lib.common_news_sdk_helper.news - self.whoami = lib.sdk_session.whoami - SDKLogger._try_to_login(self) - - @property - def alt(self): - """Alternative Submodule - - Submodules: - `companieshouse`: Companieshouse Module - `oss`: Oss Module - `realestate`: Realestate Module - - Attributes: - `hn`: Get top stories from HackerNews.\n - `hn_chart`: View top stories from HackerNews.\n - """ - - return ctrl.AltController() - - @property - def crypto(self): - """Cryptocurrency Submodule - - Submodules: - `dd`: Due Diligence Module - `defi`: DeFi Module - `disc`: Discovery Module - `nft`: NFT Module - `onchain`: OnChain Module - `ov`: Overview Module - `tools`: Tools Module - - Attributes: - `candle`: Plot candle chart from dataframe. [Source: Binance]\n - `chart`: Load data for Technical Analysis\n - `find`: Find similar coin by coin name,symbol or id.\n - `load`: Load crypto currency to get data for\n - `price`: Displays live price from pyth live feed [Source: https://pyth.network/]\n - """ - - return ctrl.CryptoController() - - @property - def econometrics(self): - """Econometrics Submodule - - Attributes: - `bgod`: Calculate test statistics for autocorrelation\n - `bgod_chart`: Show Breusch-Godfrey autocorrelation test\n - `bols`: The between estimator is an alternative, usually less efficient estimator, can can be used to\n - `bpag`: Calculate test statistics for heteroscedasticity\n - `bpag_chart`: Show Breusch-Pagan heteroscedasticity test\n - `clean`: Clean up NaNs from the dataset\n - `coint`: Calculate cointegration tests between variable number of input series\n - `coint_chart`: Estimates long-run and short-run cointegration relationship for series y and x and apply\n - `comparison`: Compare regression results between Panel Data regressions.\n - `dwat`: Calculate test statistics for Durbin Watson autocorrelation\n - `dwat_chart`: Show Durbin-Watson autocorrelation tests\n - `fdols`: First differencing is an alternative to using fixed effects when there might be correlation.\n - `fe`: When effects are correlated with the regressors the RE and BE estimators are not consistent.\n - `garch`: Calculates volatility forecasts based on GARCH.\n - `garch_chart`: Plots the volatility forecasts based on GARCH\n - `get_regression_data`: This function creates a DataFrame with the required regression data as\n - `granger`: Calculate granger tests\n - `granger_chart`: Show granger tests\n - `load`: Load custom file into dataframe.\n - `norm`: The distribution of returns and generate statistics on the relation to the normal curve.\n - `norm_chart`: Determine the normality of a timeseries.\n - `ols`: Performs an OLS regression on timeseries data. [Source: Statsmodels]\n - `options`: Obtain columns-dataset combinations from loaded in datasets that can be used in other commands\n - `options_chart`: Plot custom data\n - `panel`: Based on the regression type, this function decides what regression to run.\n - `panel_chart`: Based on the regression type, this function decides what regression to run.\n - `pols`: PooledOLS is just plain OLS that understands that various panel data structures.\n - `re`: The random effects model is virtually identical to the pooled OLS model except that is accounts for the\n - `root`: Calculate test statistics for unit roots\n - `root_chart`: Determine the normality of a timeseries.\n - `vif`: Calculates VIF (variance inflation factor), which tests collinearity.\n - """ - - return model.EconometricsRoot() - - @property - def economy(self): - """Economy Submodule - - Attributes: - `available_indices`: Get available indices\n - `balance`: General government deficit is defined as the balance of income and expenditure of government,\n - `balance_chart`: General government balance is defined as the balance of income and expenditure of government,\n - `ccpi`: Inflation measured by consumer price index (CPI) is defined as the change in the prices\n - `ccpi_chart`: Inflation measured by consumer price index (CPI) is defined as the change in the prices\n - `country_codes`: Get available country codes for Bigmac index\n - `cpi`: Obtain CPI data from FRED. [Source: FRED]\n - `cpi_chart`: Inflation measured by consumer price index (CPI) is defined as the change in\n - `currencies`: Scrape data for global currencies\n - `debt`: General government debt-to-GDP ratio measures the gross debt of the general\n - `debt_chart`: General government debt-to-GDP ratio measures the gross debt of the general\n - `events`: Get economic calendar for countries between specified dates\n - `fgdp`: Real gross domestic product (GDP) is GDP given in constant prices and\n - `fgdp_chart`: Real gross domestic product (GDP) is GDP given in constant prices and\n - `fred`: Get Series data. [Source: FRED]\n - `fred_chart`: Display (multiple) series from https://fred.stlouisfed.org. [Source: FRED]\n - `fred_notes`: Get series notes. [Source: FRED]\n - `future`: Get futures data. [Source: Finviz]\n - `futures`: Get futures data.\n - `gdp`: Gross domestic product (GDP) is the standard measure of the value added created\n - `gdp_chart`: Gross domestic product (GDP) is the standard measure of the value added created\n - `get_groups`: Get group available\n - `glbonds`: Scrape data for global bonds\n - `index`: Get data on selected indices over time [Source: Yahoo Finance]\n - `index_chart`: Load (and show) the selected indices over time [Source: Yahoo Finance]\n - `indices`: Get the top US indices\n - `macro`: This functions groups the data queried from the EconDB database [Source: EconDB]\n - `macro_chart`: Show the received macro data about a company [Source: EconDB]\n - `macro_countries`: This function returns the available countries and respective currencies.\n - `macro_parameters`: This function returns the available macro parameters with detail.\n - `overview`: Scrape data for market overview\n - `perfmap`: Opens Finviz map website in a browser. [Source: Finviz]\n - `performance`: Get group (sectors, industry or country) performance data. [Source: Finviz]\n - `revenue`: Governments collect revenues mainly for two purposes: to finance the goods\n - `revenue_chart`: Governments collect revenues mainly for two purposes: to finance the goods\n - `rgdp`: Gross domestic product (GDP) is the standard measure of the value added\n - `rgdp_chart`: Gross domestic product (GDP) is the standard measure of the value added\n - `search_index`: Search indices by keyword. [Source: FinanceDatabase]\n - `spending`: General government spending provides an indication of the size\n - `spending_chart`: General government spending provides an indication of the size\n - `treasury`: Get treasury rates from Federal Reserve\n - `treasury_chart`: Display U.S. Treasury rates [Source: EconDB]\n - `trust`: Trust in government refers to the share of people who report having confidence in\n - `trust_chart`: Trust in government refers to the share of people who report having confidence in\n - `usbonds`: Scrape data for us bonds\n - `usdli`: The USD Liquidity Index is defined as: [WALCL - WLRRAL - WDTGAL]. It is expressed in billions of USD.\n - `usdli_chart`: Display US Dollar Liquidity\n - `valuation`: Get group (sectors, industry or country) valuation data. [Source: Finviz]\n - """ - - return model.EconomyRoot() - - @property - def etf(self): - """Etf Submodule - - Submodules: - `disc`: Discovery Module - - Attributes: - `candle`: Show candle plot of loaded ticker.\n - `compare`: Compare selected ETFs\n - `etf_by_category`: Return a selection of ETFs based on category filtered by total assets.\n - `etf_by_name`: Get an ETF symbol and name based on ETF string to search. [Source: StockAnalysis]\n - `holdings`: Get ETF holdings\n - `ld`: Return a selection of ETFs based on description filtered by total assets.\n - `ln`: Return a selection of ETFs based on name filtered by total assets. [Source: Finance Database]\n - `load`: Load a symbol to perform analysis using the string above as a template.\n - `news`: Get news for a given term. [Source: NewsAPI]\n - `news_chart`: Prints table showing news for a given term. [Source: NewsAPI]\n - `overview`: Get overview data for selected etf\n - `symbols`: Gets all etf names and symbols\n - `weights`: Return sector weightings allocation of ETF. [Source: FinancialModelingPrep]\n - """ - - return ctrl.EtfController() - - @property - def fixedincome(self): - """Fixedincome Submodule - - Attributes: - `ameribor`: Obtain data for American Interbank Offered Rate (AMERIBOR)\n - `cp`: Obtain Commercial Paper data\n - `dwpcr`: Obtain data for the Discount Window Primary Credit Rate.\n - `ecb`: Obtain data for ECB interest rates.\n - `ecbycrv`: Gets euro area yield curve data from ECB.\n - `estr`: Obtain data for Euro Short-Term Rate (ESTR)\n - `fed`: Obtain data for Effective Federal Funds Rate.\n - `ffrmc`: Get data for Selected Treasury Constant Maturity Minus Federal Funds Rate\n - `hqm`: The HQM yield curve represents the high quality corporate bond market, i.e.,\n - `icebofa`: Get data for ICE BofA US Corporate Bond Indices.\n - `icespread`: Get data for ICE BofA US Corporate Bond Spreads\n - `iorb`: Obtain data for Interest Rate on Reserve Balances.\n - `moody`: Get data for Moody Corporate Bond Index\n - `projection`: Obtain data for the Federal Reserve's projection of the federal funds rate.\n - `sofr`: Obtain data for Secured Overnight Financing Rate (SOFR)\n - `sonia`: Obtain data for Sterling Overnight Index Average (SONIA)\n - `spot`: The spot rate for any maturity is the yield on a bond that provides\n - `tbffr`: Get data for Selected Treasury Bill Minus Federal Funds Rate.\n - `tmc`: Get data for 10-Year Treasury Constant Maturity Minus Selected Treasury Constant Maturity.\n - `treasury`: Gets interest rates data from selected countries (3 month and 10 year)\n - `usrates`: Plot various treasury rates from the United States\n - `ycrv`: Gets yield curve data from FRED.\n - `ycrv_chart`: Display yield curve based on US Treasury rates for a specified date.\n - """ - - return model.FixedincomeRoot() - - @property - def forecast(self): - """Forecasting Submodule - - Attributes: - `anom`: Get Quantile Anomaly Detection Data\n - `anom_chart`: Display Quantile Anomaly Detection\n - `atr`: Calculate the Average True Range of a variable based on a a specific stock ticker.\n - `autoarima`: Performs Automatic ARIMA forecasting\n - `autoarima_chart`: Display Automatic ARIMA model.\n - `autoces`: Performs Automatic Complex Exponential Smoothing forecasting\n - `autoces_chart`: Display Automatic Complex Exponential Smoothing Model\n - `autoets`: Performs Automatic ETS forecasting\n - `autoets_chart`: Display Automatic ETS (Error, Trend, Sesonality) Model\n - `autoselect`: Performs Automatic Statistical forecasting\n - `autoselect_chart`: Display Automatic Statistical Forecasting Model\n - `brnn`: Performs Block RNN forecasting\n - `brnn_chart`: Display BRNN forecast\n - `clean`: Clean up NaNs from the dataset\n - `combine`: Adds the given column of df2 to df1\n - `corr`: Returns correlation for a given df\n - `corr_chart`: Plot correlation coefficients for dataset features\n - `delete`: Delete a column from a dataframe\n - `delta`: Calculate the %change of a variable based on a specific column\n - `desc`: Returns statistics for a given df\n - `desc_chart`: Show descriptive statistics for a dataframe\n - `ema`: A moving average provides an indication of the trend of the price movement\n - `expo`: Performs Probabilistic Exponential Smoothing forecasting\n - `expo_chart`: Display Probabilistic Exponential Smoothing forecast\n - `export`: Export a dataframe to a file\n - `linregr`: Perform Linear Regression Forecasting\n - `linregr_chart`: Display Linear Regression Forecasting\n - `load`: Load custom file into dataframe.\n - `mom`: A momentum oscillator, which measures the percentage change between the current\n - `mstl`: Performs MSTL forecasting\n - `mstl_chart`: Display MSTL Model\n - `nbeats`: Perform NBEATS Forecasting\n - `nbeats_chart`: Display NBEATS forecast\n - `nhits`: Performs Nhits forecasting\n - `nhits_chart`: Display Nhits forecast\n - `plot`: Plot data from a dataset\n - `plot_chart`: Plot data from a dataset\n - `regr`: Perform Regression Forecasting\n - `regr_chart`: Display Regression Forecasting\n - `rename`: Rename a column in a dataframe\n - `rnn`: Perform RNN forecasting\n - `rnn_chart`: Display RNN forecast\n - `roc`: A momentum oscillator, which measures the percentage change between the current\n - `rsi`: A momentum indicator that measures the magnitude of recent price changes to evaluate\n - `rwd`: Performs Random Walk with Drift forecasting\n - `rwd_chart`: Display Random Walk with Drift Model\n - `season_chart`: Plot seasonality from a dataset\n - `seasonalnaive`: Performs Seasonal Naive forecasting\n - `seasonalnaive_chart`: Display SeasonalNaive Model\n - `show`: Show a dataframe in a table\n - `signal`: A price signal based on short/long term price.\n - `sto`: Stochastic Oscillator %K and %D : A stochastic oscillator is a momentum indicator comparing a particular closing\n - `tcn`: Perform TCN forecasting\n - `tcn_chart`: Display TCN forecast\n - `tft`: Performs Temporal Fusion Transformer forecasting\n - `tft_chart`: Display Temporal Fusion Transformer forecast\n - `theta`: Performs Theta forecasting\n - `theta_chart`: Display Theta forecast\n - `trans`: Performs Transformer forecasting\n - `trans_chart`: Display Transformer forecast\n - """ - - return model.ForecastRoot() - - @property - def forex(self): - """Forex Submodule - - Attributes: - `candle`: Show candle plot for fx data.\n - `fwd`: Gets forward rates from fxempire\n - `get_currency_list`: Load AV currency codes from a local file.\n - `load`: Load forex for two given symbols.\n - `quote`: Get forex quote.\n - """ - - return model.ForexRoot() - - @property - def funds(self): - """Mutual Funds Submodule - - Attributes: - `carbon`: Search mstarpy for carbon metrics\n - `exclusion`: Search mstarpy exclusion policy in esgData\n - `historical`: Get historical fund, category, index price\n - `historical_chart`: Display historical fund, category, index price\n - `holdings`: Search mstarpy for holdings\n - `load`: Search mstarpy for matching funds\n - `search`: Search mstarpy for matching funds\n - `sector`: Get fund, category, index sector breakdown\n - `sector_chart`: Display fund, category, index sector breakdown\n - """ - - return model.FundsRoot() - - @property - def futures(self): - """Futures Submodule - - Attributes: - `curve`: Get curve futures [Source: Yahoo Finance]\n - `curve_chart`: Display curve futures [Source: Yahoo Finance]\n - `historical`: Get historical futures data\n - `historical_chart`: Display historical futures [Source: Yahoo Finance]\n - `search`: Get search futures [Source: Yahoo Finance]\n - """ - - return model.FuturesRoot() - - @property - def keys(self): - """Keys Submodule - - Attributes: - `av`: Set Alpha Vantage key\n - `binance`: Set Binance key\n - `bitquery`: Set Bitquery key\n - `biztoc`: Set BizToc key\n - `cmc`: Set Coinmarketcap key\n - `coinbase`: Set Coinbase key\n - `cpanic`: Set Cpanic key.\n - `databento`: Set DataBento key\n - `eodhd`: Set Eodhd key.\n - `ethplorer`: Set Ethplorer key.\n - `finnhub`: Set Finnhub key\n - `fmp`: Set Financial Modeling Prep key\n - `fred`: Set FRED key\n - `get_keys_info`: Get info on available APIs to use in set_keys.\n - `github`: Set GitHub key.\n - `messari`: Set Messari key.\n - `mykeys`: Get currently set API keys.\n - `news`: Set News key\n - `polygon`: Set Polygon key\n - `quandl`: Set Quandl key\n - `reddit`: Set Reddit key\n - `santiment`: Set Santiment key.\n - `set_keys`: Set API keys in bundle.\n - `smartstake`: Set Smartstake key.\n - `tradier`: Set Tradier key\n - `walert`: Set Walert key\n - """ - - return model.KeysRoot() - - @property - def portfolio(self): - """Portfolio Submodule - - Submodules: - `alloc`: Alloc Module - `metric`: Metric Module - `po`: Portfolio Optimization Module - - Attributes: - `bench`: Load benchmark into portfolio\n - `distr`: Display daily returns\n - `distr_chart`: Display daily returns\n - `dret`: Get daily returns\n - `dret_chart`: Display daily returns\n - `es`: Get portfolio expected shortfall\n - `holdp`: Get holdings of assets (in percentage)\n - `holdp_chart`: Display holdings of assets (in percentage)\n - `holdv`: Get holdings of assets (absolute value)\n - `holdv_chart`: Display holdings of assets (absolute value)\n - `load`: Get PortfolioEngine object\n - `maxdd`: Calculate the drawdown (MDD) of historical series. Note that the calculation is done\n - `maxdd_chart`: Display maximum drawdown curve\n - `mret`: Get monthly returns\n - `mret_chart`: Display monthly returns\n - `om`: Get omega ratio\n - `om_chart`: Display omega ratio\n - `perf`: Get portfolio performance vs the benchmark\n - `rbeta`: Get rolling beta using portfolio and benchmark returns\n - `rbeta_chart`: Display rolling beta\n - `rsharpe`: Get rolling sharpe ratio\n - `rsharpe_chart`: Display rolling sharpe\n - `rsort`: Get rolling sortino\n - `rsort_chart`: Display rolling sortino\n - `rvol`: Get rolling volatility\n - `rvol_chart`: Display rolling volatility\n - `show`: Get portfolio transactions\n - `summary`: Get portfolio and benchmark returns summary\n - `var`: Get portfolio VaR\n - `yret`: Get yearly returns\n - `yret_chart`: Display yearly returns\n - """ - - return ctrl.PortfolioController() - - @property - def qa(self): - """Quantitative Analysis Submodule - - Attributes: - `acf`: Plots Auto and Partial Auto Correlation of returns and change in returns\n - `bw`: Plots box and whisker plots\n - `calculate_adjusted_var`: Calculates VaR, which is adjusted for skew and kurtosis (Cornish-Fischer-Expansion)\n - `cdf`: Plots Cumulative Distribution Function\n - `cusum`: Plots Cumulative sum algorithm (CUSUM) to detect abrupt changes in data\n - `decompose`: Perform seasonal decomposition\n - `es`: Gets Expected Shortfall for specified stock dataframe.\n - `es_chart`: Prints table showing expected shortfall.\n - `hist`: Plots histogram of data\n - `kurtosis`: Kurtosis Indicator\n - `kurtosis_chart`: Plots rolling kurtosis\n - `line`: Display line plot of data\n - `normality`: Look at the distribution of returns and generate statistics on the relation to the normal curve.\n - `normality_chart`: Prints table showing normality statistics\n - `omega`: Get the omega series\n - `omega_chart`: Plots the omega ratio\n - `qqplot`: Plots QQ plot for data against normal quantiles\n - `quantile`: Overlay Median & Quantile\n - `quantile_chart`: Plots rolling quantile\n - `rolling`: Return rolling mean and standard deviation\n - `rolling_chart`: Plots mean std deviation\n - `sharpe`: Calculates the sharpe ratio\n - `sharpe_chart`: Plots Calculated the sharpe ratio\n - `skew`: Skewness Indicator\n - `skew_chart`: Plots rolling skew\n - `sortino`: Calculates the sortino ratio\n - `sortino_chart`: Plots the sortino ratio\n - `spread`: Standard Deviation and Variance\n - `spread_chart`: Plots rolling spread\n - `summary`: Print summary statistics\n - `summary_chart`: Prints table showing summary statistics\n - `unitroot`: Calculate test statistics for unit roots\n - `unitroot_chart`: Prints table showing unit root test calculations\n - `var`: Gets value at risk for specified stock dataframe.\n - `var_chart`: Prints table showing VaR of dataframe.\n - """ - - return model.QaRoot() - - @property - def stocks(self): - """Stocks Submodule - - Submodules: - `ba`: Behavioral Analysis Module - `ca`: Comparison Analysis Module - `disc`: Discovery Module - `dps`: Darkpool Shorts Module - `fa`: Fundamental Analysis Module - `gov`: Government Module - `ins`: Insiders Module - `options`: Options Module - `qa`: Quantitative Analysis Module - `screener`: Screener Module - `ta`: Technical Analysis Module - `th`: Trading Hours Module - - Attributes: - `candle`: Show candle plot of loaded ticker.\n - `load`: Load a symbol to perform analysis using the string above as a template.\n - `process_candle`: Process DataFrame into candle style plot.\n - `quote`: Gets ticker quote from FMP\n - `quote_chart`: Financial Modeling Prep ticker(s) quote.\n - `search`: Search selected query for tickers.\n - `tob`: Get top of book bid and ask for ticker on exchange [CBOE.com]\n - """ - - return ctrl.StocksController() - - @property - def ta(self): - """Technical Analysis Submodule - - Attributes: - `ad`: Calculate AD technical indicator\n - `ad_chart`: Plots AD technical indicator\n - `adosc`: Calculate AD oscillator technical indicator\n - `adosc_chart`: Plots AD Osc Indicator\n - `adx`: ADX technical indicator\n - `adx_chart`: Plots ADX indicator\n - `aroon`: Aroon technical indicator\n - `aroon_chart`: Plots Aroon indicator\n - `atr`: Average True Range\n - `atr_chart`: Plots ATR\n - `bbands`: Calculate Bollinger Bands\n - `bbands_chart`: Plots bollinger bands\n - `cci`: Commodity channel index\n - `cci_chart`: Plots CCI Indicator\n - `cg`: Center of gravity\n - `cg_chart`: Plots center of gravity Indicator\n - `clenow`: Gets the Clenow Volatility Adjusted Momentum. this is defined as the regression coefficient on log prices\n - `clenow_chart`: Prints table and plots clenow momentum\n - `cones`: Returns a DataFrame of realized volatility quantiles.\n - `cones_chart`: Plots the realized volatility quantiles for the loaded ticker.\n - `demark`: Get the integer value for demark sequential indicator\n - `demark_chart`: Plot demark sequential indicator\n - `donchian`: Calculate Donchian Channels\n - `donchian_chart`: Plots donchian channels\n - `ema`: Gets exponential moving average (EMA) for stock\n - `fib`: Calculate Fibonacci levels\n - `fib_chart`: Plots Calculated fibonacci retracement levels\n - `fisher`: Fisher Transform\n - `fisher_chart`: Plots Fisher Indicator\n - `hma`: Gets hull moving average (HMA) for stock\n - `kc`: Keltner Channels\n - `kc_chart`: Plots Keltner Channels Indicator\n - `ma`: Plots MA technical indicator\n - `ma_chart`: Plots MA technical indicator\n - `macd`: Moving average convergence divergence\n - `macd_chart`: Plots MACD signal\n - `obv`: On Balance Volume\n - `obv_chart`: Plots OBV technical indicator\n - `rsi`: Relative strength index\n - `rsi_chart`: Plots RSI Indicator\n - `rvol_garman_klass`: Garman-Klass volatility extends Parkinson volatility by taking into account the opening and closing price.\n - `rvol_hodges_tompkins`: Hodges-Tompkins volatility is a bias correction for estimation using an overlapping data sample.\n - `rvol_parkinson`: Parkinson volatility uses the high and low price of the day rather than just close to close prices.\n - `rvol_rogers_satchell`: Rogers-Satchell is an estimator for measuring the volatility with an average return not equal to zero.\n - `rvol_std`: Standard deviation measures how widely returns are dispersed from the average return.\n - `rvol_yang_zhang`: Yang-Zhang volatility is the combination of the overnight (close-to-open volatility).\n - `sma`: Gets simple moving average (SMA) for stock\n - `standard_deviation`: Standard deviation measures how widely returns are dispersed from the average return.\n - `stoch`: Stochastic oscillator\n - `stoch_chart`: Plots stochastic oscillator signal\n - `vwap`: Gets volume weighted average price (VWAP)\n - `vwap_chart`: Plots VWMA technical indicator\n - `wma`: Gets weighted moving average (WMA) for stock\n - `zlma`: Gets zero-lagged exponential moving average (ZLEMA) for stock\n - """ - - return model.TaRoot() - - -class SDKLogger: - def __init__(self) -> None: - self.__check_initialize_logging() - - def __check_initialize_logging(self): - if not get_current_system().LOGGING_SUPPRESS: - self.__initialize_logging() - - @staticmethod - def __initialize_logging() -> None: - # pylint: disable=C0415 - from openbb_terminal.core.session.current_system import set_system_variable - from openbb_terminal.core.log.generation.settings_logger import log_all_settings - from openbb_terminal.loggers import setup_logging - - set_system_variable("LOGGING_SUB_APP", "sdk") - setup_logging() - log_all_settings() - - @staticmethod - def _try_to_login(sdk: "OpenBBSDK"): - if is_local() and is_auth_enabled(): - try: - sdk.login(silent=True) - except Exception: - pass - - -openbb = OpenBBSDK() diff --git a/openbb_terminal/settings_controller.py b/openbb_terminal/settings_controller.py deleted file mode 100644 index d7cd6a81985..00000000000 --- a/openbb_terminal/settings_controller.py +++ /dev/null @@ -1,545 +0,0 @@ -"""Settings Controller Module""" - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import argparse -import logging -import os -import os.path -from pathlib import Path -from typing import List, Optional, Union - -# IMPORTATION THIRDPARTY -import pytz - -import openbb_terminal.core.session.hub_model as Hub -from openbb_terminal import theme - -# IMPORTATION INTERNAL -from openbb_terminal.core.config.paths import ( - I18N_DICT_LOCATION, - SETTINGS_ENV_FILE, - STYLES_DIRECTORY_REPO, -) -from openbb_terminal.core.session.constants import BackendEnvironment -from openbb_terminal.core.session.current_user import ( - get_current_user, - is_local, - set_preference, -) -from openbb_terminal.core.session.env_handler import write_to_dotenv -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - AVAILABLE_FLAIRS, - get_flair, - get_user_timezone_or_invalid, - parse_and_split_input, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import RICH_TAGS, MenuText, console - -# pylint: disable=too-many-lines,no-member,too-many-public-methods,C0302 -# pylint: disable=import-outside-toplevel - -logger = logging.getLogger(__name__) - - -class SettingsController(BaseController): - """Settings Controller class""" - - CHOICES_COMMANDS: List[str] = [ - "chart", - "colors", - "dt", - "flair", - "height", - "lang", - "table", - "tz", - "userdata", - "width", - ] - if is_local(): - CHOICES_COMMANDS.append("source") - PATH = "/settings/" - CHOICES_GENERATION = True - - languages_available = [ - lang.strip(".yml") - for lang in os.listdir(I18N_DICT_LOCATION) - if lang.endswith(".yml") - ] - - def __init__( - self, queue: Optional[List[str]] = None, env_file: str = str(SETTINGS_ENV_FILE) - ): - """Constructor""" - super().__init__(queue) - self.env_file = env_file - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - choices["tz"] = {c: None for c in pytz.all_timezones} - choices["lang"] = {c: None for c in self.languages_available} - choices["flair"]["--emoji"] = {c: None for c in AVAILABLE_FLAIRS} - choices["flair"]["-e"] = "--emoji" - - self.choices = choices - self.completer = NestedCompleter.from_nested_dict(choices) - - self.sort_filter = r"((tz\ -t |tz ).*?(" - for tz in pytz.all_timezones: - clean_tz = tz.replace("/", r"\/") - self.sort_filter += f"{clean_tz}|" - self.sort_filter += ")*)" - - self.PREVIEW = ", ".join( - [ - f"[{tag}]{tag}[/{tag}]" - for tag in sorted( - set( - name.replace("[", "").replace("]", "").replace("/", "") - for name in RICH_TAGS - ) - ) - ] - ) - - def parse_input(self, an_input: str) -> List: - """Parse controller input - - Overrides the parent class function to handle github org/repo path convention. - See `BaseController.parse_input()` for details. - """ - # Filtering out - sort_filter = self.sort_filter - - custom_filters = [sort_filter] - - commands = parse_and_split_input( - an_input=an_input, custom_filters=custom_filters - ) - return commands - - def print_help(self): - """Print help""" - current_user = get_current_user() - - mt = MenuText("settings/") - mt.add_info("_info_") - mt.add_raw("\n") - mt.add_setting("dt", current_user.preferences.USE_DATETIME) - mt.add_raw("\n") - mt.add_cmd("chart") - mt.add_raw("\n") - mt.add_param("_chart", current_user.preferences.CHART_STYLE) - mt.add_raw("\n") - mt.add_cmd("table") - mt.add_raw("\n") - mt.add_param("_table", current_user.preferences.TABLE_STYLE) - mt.add_raw("\n") - mt.add_cmd("colors") - mt.add_raw("\n") - mt.add_param( - "_colors", f"{current_user.preferences.RICH_STYLE} -> {self.PREVIEW}" - ) - mt.add_raw("\n") - mt.add_cmd("flair") - mt.add_raw("\n") - mt.add_param("_flair", get_flair()) - mt.add_raw("\n") - mt.add_cmd("lang") - mt.add_raw("\n") - mt.add_param("_language", current_user.preferences.USE_LANGUAGE) - mt.add_raw("\n") - mt.add_cmd("userdata") - mt.add_raw("\n") - mt.add_param( - "_user_data_folder", - current_user.preferences.USER_DATA_DIRECTORY, - ) - mt.add_raw("\n") - mt.add_cmd("tz") - mt.add_raw("\n") - mt.add_param("_timezone", get_user_timezone_or_invalid()) - mt.add_raw("\n") - mt.add_cmd("height") - mt.add_cmd("width") - mt.add_raw("\n") - mt.add_param("_plot_height", current_user.preferences.PLOT_PYWRY_HEIGHT, 12) - mt.add_param("_plot_width", current_user.preferences.PLOT_PYWRY_WIDTH, 12) - mt.add_raw("\n") - if is_local(): - mt.add_cmd("source") - mt.add_raw("\n") - mt.add_param( - "_data_source", - get_current_user().preferences.USER_DATA_SOURCES_FILE, - ) - mt.add_raw("\n") - console.print(text=mt.menu_text, menu="Settings") - - @staticmethod - def set_and_save_preference(name: str, value: Union[bool, Path, str]): - """Set preference and write to .env - - Parameters - ---------- - name : str - Preference name - value : Union[bool, Path, str] - Preference value - """ - set_preference(name, value) - write_to_dotenv("OPENBB_" + name, str(value)) - - @log_start_end(log=logger) - def call_dt(self, other_args: List[str]): - """Process dt command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dt", - description="Set the use of datetime in the plots", - ) - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.set_and_save_preference( - "USE_DATETIME", not get_current_user().preferences.USE_DATETIME - ) - - @log_start_end(log=logger) - def call_colors(self, other_args: List[str]): - """Process colors command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="colors", - description="Console style.", - ) - theme.load_available_styles() - parser.add_argument( - "-s", - "--style", - type=str, - choices=theme.console_styles_available, - dest="style", - required="-h" not in other_args and "--help" not in other_args, - help="To use 'custom' option, go to https://openbb.co/customize and create your theme." - " Then, place the downloaded file 'openbb_config.richstyle.json'" - f" inside {get_current_user().preferences.USER_STYLES_DIRECTORY} or " - f"{STYLES_DIRECTORY_REPO}. If you have a hub account you can change colors " - f"here {BackendEnvironment.BASE_URL + 'app/terminal/theme'}.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - if is_local(): - self.set_and_save_preference("RICH_STYLE", ns_parser.style) - else: - set_preference("RICH_STYLE", ns_parser.style) - Hub.upload_config( - key="RICH_STYLE", - value=ns_parser.style, - type_="settings", - auth_header=get_current_user().profile.get_auth_header(), - ) - console.print("") - console.print("Colors updated.") - - @log_start_end(log=logger) - def call_chart(self, other_args: List[str]): - """Process chart command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="theme", - description="Choose chart style.", - ) - parser.add_argument( - "-s", - "--style", - type=str, - dest="style", - choices=["dark", "light"], - help="Choose chart style. If you have a hub account you can change theme " - f"here {BackendEnvironment.BASE_URL + 'app/terminal/theme/charts-tables'}.", - required="-h" not in other_args and "--help" not in other_args, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser and ns_parser.style: - if is_local(): - self.set_and_save_preference("CHART_STYLE", ns_parser.style) - else: - set_preference("CHART_STYLE", ns_parser.style) - Hub.upload_config( - key="chart", - value=ns_parser.style, - type_="terminal_style", - auth_header=get_current_user().profile.get_auth_header(), - ) - console.print("") - theme.apply_style(ns_parser.style) - console.print("Chart theme updated.\n") - - @log_start_end(log=logger) - def call_table(self, other_args: List[str]): - """Process theme command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="theme", - description="Choose table style.", - ) - parser.add_argument( - "-s", - "--style", - type=str, - dest="style", - choices=["dark", "light"], - help="Choose table style. If you have a hub account you can change theme " - f"here {BackendEnvironment.BASE_URL + 'app/terminal/theme/charts-tables'}.", - required="-h" not in other_args and "--help" not in other_args, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser and ns_parser.style: - if is_local(): - self.set_and_save_preference("TABLE_STYLE", ns_parser.style) - else: - set_preference("TABLE_STYLE", ns_parser.style) - Hub.upload_config( - key="table", - value=ns_parser.style, - type_="terminal_style", - auth_header=get_current_user().profile.get_auth_header(), - ) - console.print("") - console.print("Table theme updated.\n") - - @log_start_end(log=logger) - def call_source(self, other_args: List[str]): - """Process source command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="source", - description="Preferred data source file.", - ) - parser.add_argument( - "-f", - "--file", - type=str, - dest="file", - help="file", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-f") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser and ns_parser.file: - if os.path.exists(ns_parser.file): - self.set_and_save_preference( - "USER_DATA_SOURCES_FILE", str(ns_parser.file) - ) - console.print("[green]Sources file changed successfully![/green]") - else: - console.print("[red]Couldn't find the sources file![/red]") - - @log_start_end(log=logger) - def call_height(self, other_args: List[str]): - """Process height command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="height", - description="select plot height (autoscaling disabled)", - ) - parser.add_argument( - "-v", - "--value", - type=int, - dest="value", - help="value", - required="-h" not in other_args, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.set_and_save_preference("PLOT_PYWRY_HEIGHT", ns_parser.value) - - @log_start_end(log=logger) - def call_width(self, other_args: List[str]): - """Process width command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="width", - description="select plot width (autoscaling disabled)", - ) - parser.add_argument( - "-v", - "--value", - type=int, - dest="value", - help="value", - required="-h" not in other_args, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - self.set_and_save_preference("PLOT_PYWRY_WIDTH", ns_parser.value) - - @log_start_end(log=logger) - def call_lang(self, other_args: List[str]): - """Process lang command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lang", - description="Choose language for terminal", - ) - parser.add_argument( - "-v", - "--value", - type=str, - dest="value", - help="Language", - choices=self.languages_available, - default="", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-v") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - if ns_parser.value: - self.set_and_save_preference("USE_LANGUAGE", ns_parser.value) - else: - console.print( - f"Languages available: {', '.join(self.languages_available)}" - ) - - @log_start_end(log=logger) - def call_tz(self, other_args: List[str]): - """Process tz command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description=""" - Setting a different timezone - """, - ) - parser.add_argument( - "-t", - dest="timezone", - help="Choose timezone", - required="-h" not in other_args, - metavar="TIMEZONE", - choices=pytz.all_timezones, - ) - - if other_args and "-t" not in other_args[0]: - other_args.insert(0, "-t") - - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser and ns_parser.timezone: - self.set_and_save_preference("TIMEZONE", ns_parser.timezone) - - @log_start_end(log=logger) - def call_flair(self, other_args: List[str]): - """Process flair command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="flair", - description="set the flair emoji to be used", - ) - parser.add_argument( - "-e", - "--emoji", - type=str, - dest="emoji", - help="flair emoji to be used", - nargs="+", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-e") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - if not ns_parser.emoji: - ns_parser.emoji = "" - else: - ns_parser.emoji = " ".join(ns_parser.emoji) - - self.set_and_save_preference("FLAIR", ns_parser.emoji) - - @log_start_end(log=logger) - def call_userdata(self, other_args: List[str]): - """Process userdata command""" - - def save_file(file_path: Union[str, Path]): - console.print(f"User data to be saved in the default folder: '{file_path}'") - self.set_and_save_preference("USER_DATA_DIRECTORY", file_path) - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="userdata", - description="Set folder to store user data such as exports, presets, logs", - ) - parser.add_argument( - "--folder", - type=str, - dest="folder", - help="Folder where to store user data. ", - default=f"{str(Path.home() / 'OpenBBUserData')}", - ) - parser.add_argument( - "--default", - action="store", - dest="default", - default=False, - type=bool, - help="Revert to the default path.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--folder") - ns_parser = self.parse_simple_args(parser, other_args) - - if not (ns_parser and (other_args or self.queue)): - return - - if ns_parser.default: - save_file(Path.home() / "OpenBBUserData") - - userdata_path = "" if other_args else "/" - userdata_path += "/".join([ns_parser.folder] + self.queue) - userdata_path = userdata_path.replace("'", "").replace('"', "") - # If the path selected does not start from the user root, give relative location from root - if userdata_path[0] == "~": - userdata_path = userdata_path.replace("~", os.path.expanduser("~")) - - self.queue = [] - # Check if the directory exists - if os.path.isdir(userdata_path): - save_file(userdata_path) - return - - user_opt = input( - f"Path `{userdata_path}` does not exist, do you wish to create it? [Y/N]\n" - ) - - if user_opt.upper() == "Y": - os.makedirs(userdata_path) - console.print( - f"[green]Folder '{userdata_path}' successfully created.[/green]" - ) - save_file(userdata_path) diff --git a/openbb_terminal/sources_controller.py b/openbb_terminal/sources_controller.py deleted file mode 100644 index 65907e7f111..00000000000 --- a/openbb_terminal/sources_controller.py +++ /dev/null @@ -1,193 +0,0 @@ -"""Sources Controller Module""" - -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import argparse -import logging -import os -from pathlib import Path -from typing import List, Optional - -from openbb_terminal.core.session.constants import BackendEnvironment - -# IMPORTATION THIRDPARTY -# IMPORTATION INTERNAL -from openbb_terminal.core.session.current_user import ( - get_current_user, - is_local, - set_sources, -) -from openbb_terminal.core.session.hub_model import upload_user_field -from openbb_terminal.core.session.sources_handler import write_sources -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import parse_and_split_input -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class SourcesController(BaseController): - """Sources Controller class""" - - CHOICES_COMMANDS: List[str] = [ - "get", - "set", - ] - PATH = "/sources/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - self.source = "default" - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - self.choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def update_print_help(self): - """Update print help""" - sources_file = get_current_user().preferences.USER_DATA_SOURCES_FILE - if is_local(): - if Path(sources_file).exists() and os.stat(sources_file).st_size > 0: - self.source = sources_file - else: - self.source = BackendEnvironment.HUB_URL + "app/terminal/data-sources" - - def parse_input(self, an_input: str) -> List: - """Parse controller input - - Overrides the parent class function to handle github org/repo path convention. - See `BaseController.parse_input()` for details. - """ - cmd_filter = r"((set\s+--cmd\s+|set\s+-c\s+|set\s+|get\s+--cmd\s+|get\s+-c\s+|get\s+).*?(" - for cmd in get_current_user().sources.choices: - clean_cmd = cmd.replace("/", r"\/") - cmd_filter += f"{clean_cmd}|" - cmd_filter += ")*)" - - commands = parse_and_split_input(an_input=an_input, custom_filters=[cmd_filter]) - return commands - - def print_help(self): - """Print help""" - mt = MenuText("sources/") - mt.add_param("_source", self.source) - mt.add_raw("\n") - mt.add_info("_info_") - mt.add_cmd("get") - mt.add_cmd("set") - console.print(text=mt.menu_text, menu="Data Sources") - - @log_start_end(log=logger) - def call_get(self, other_args): - """Process get command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="get", - description="Get sources associated with a command and the one selected by default.", - ) - parser.add_argument( - "-c", - "--cmd", - action="store", - dest="cmd", - choices=list(get_current_user().sources.choices.keys()), - required="-h" not in other_args and "--help" not in other_args, - help="Command that we want to check the available data sources and the default one.", - metavar="COMMAND", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - choices = get_current_user().sources.choices - cmd_defaults = choices.get(ns_parser.cmd, None) - if cmd_defaults is None: - console.print(f"[red]'{ns_parser.cmd}' is not a valid command.[/red]\n") - elif len(cmd_defaults) == 0: - console.print("This command has no data sources available.\n") - else: - console.print( - f"[param]Default :[/param] {cmd_defaults[0]}\n" - f"[param]Available :[/param] {', '.join(cmd_defaults)}" - ) - self.update_print_help() - - @log_start_end(log=logger) - def call_set(self, other_args): - """Process set command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="set", - description="Set a default data sources associated with a command, using 'set '.", - ) - parser.add_argument( - "-c", - "--cmd", - action="store", - dest="cmd", - choices=list(get_current_user().sources.choices.keys()), - required="-h" not in other_args and "--help" not in other_args, - help="Command that we to select the default data source.", - metavar="COMMAND", - ) - parser.add_argument( - "-s", - "--source", - action="store", - dest="source", - type=str, - required="-h" not in other_args and "--help" not in other_args, - help="Data source to use by default on specified command.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - - if ( - other_args - and len(other_args) >= 2 - and "-s" not in other_args - and "--source" not in other_args - ): - other_args.insert(2, "-s") - - ns_parser = self.parse_simple_args(parser, other_args) - if ns_parser: - choices = get_current_user().sources.choices - if ns_parser.source in choices[ns_parser.cmd]: - choices[ns_parser.cmd].remove(ns_parser.source) - choices[ns_parser.cmd].insert(0, ns_parser.source) - set_sources(choices) - - if is_local(): - write_sources( - sources=choices, - path=Path( - get_current_user().preferences.USER_DATA_SOURCES_FILE - ), - ) - else: - upload_user_field( - key="features_sources", - value=choices, - auth_header=get_current_user().profile.get_auth_header(), - base_url=BackendEnvironment.BASE_URL, - ) - console.print("") - console.print( - f"Default data source for '{ns_parser.cmd}' set to " - f"'{ns_parser.source}'.\n" - ) - else: - console.print( - f"[red]'{ns_parser.source}' is not a valid data source for " - f"'{ns_parser.cmd}' command.[/red]\n" - f"[param]\nAvailable :[/param] {', '.join(choices[ns_parser.cmd])}\n" - ) - self.update_print_help() diff --git a/openbb_terminal/stocks/README.md b/openbb_terminal/stocks/README.md deleted file mode 100644 index b85e499a7bf..00000000000 --- a/openbb_terminal/stocks/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Resources - -* Compilation of free knowledge and educational resources : -* Hedge fund letters or reports : -* Open MIT Open Online course on equity options: diff --git a/openbb_terminal/stocks/__init__.py b/openbb_terminal/stocks/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/backtesting/__init__.py b/openbb_terminal/stocks/backtesting/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/backtesting/bt_controller.py b/openbb_terminal/stocks/backtesting/bt_controller.py deleted file mode 100644 index f9964604752..00000000000 --- a/openbb_terminal/stocks/backtesting/bt_controller.py +++ /dev/null @@ -1,321 +0,0 @@ -"""Backtesting Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -from typing import List, Optional - -import matplotlib as mpl -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_non_negative_float, - check_positive, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console - -# This code below aims to fix an issue with the fnn module, used by bt module -# which forces matplotlib backend to be 'agg' which doesn't allow to plot -# Save current matplotlib backend -default_backend = mpl.get_backend() -# Restore backend matplotlib used - -# pylint: disable=wrong-import-position -from openbb_terminal.stocks.backtesting import bt_view # noqa: E402 - -logger = logging.getLogger(__name__) - -mpl.use(default_backend) - - -def no_data_message(): - """Print message when no ticker is loaded""" - console.print("[red]No data loaded. Use 'load' command to load a symbol[/red]") - - -class BacktestingController(StockBaseController): - """Backtesting Controller class""" - - CHOICES_COMMANDS = ["load", "ema", "emacross", "rsi", "whatif"] - PATH = "/stocks/bt/" - CHOICES_GENERATION = True - - def __init__( - self, ticker: str, stock: pd.DataFrame, queue: Optional[List[str]] = None - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - self.stock = stock - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("stocks/bt/") - mt.add_raw("") - mt.add_param("_ticker", self.ticker.upper() or "No Ticker Loaded") - mt.add_raw("\n") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_cmd("whatif", self.ticker) - mt.add_raw("\n") - mt.add_cmd("ema", self.ticker) - mt.add_cmd("emacross", self.ticker) - mt.add_cmd("rsi", self.ticker) - console.print(text=mt.menu_text, menu="Stocks - Backtesting") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - return ["stocks", f"load {self.ticker}", "bt"] - return [] - - @log_start_end(log=logger) - def call_whatif(self, other_args: List[str]): - """Call whatif""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="whatif", - description="Displays what if scenario of having bought X shares at date Y", - ) - parser.add_argument( - "-d", - "--date", - default=None, - dest="date_shares_acquired", - type=valid_date, - help="Date at which the shares were acquired", - ) - parser.add_argument( - "-n", - "--number", - default=1.0, - type=check_non_negative_float, - help="Number of shares acquired", - dest="num_shares_acquired", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.stock.empty: - no_data_message() - return - bt_view.display_whatif_scenario( - symbol=self.ticker, - num_shares_acquired=ns_parser.num_shares_acquired, - date_shares_acquired=ns_parser.date_shares_acquired, - ) - - @log_start_end(log=logger) - def call_ema(self, other_args: List[str]): - """Call EMA strategy""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ema", - description="Strategy where stock is bought when Price > EMA(l)", - ) - parser.add_argument( - "-l", - default=20, - dest="length", - type=check_positive, - help="EMA period to consider", - ) - parser.add_argument( - "--spy", - action="store_true", - default=False, - help="Flag to add spy hold comparison", - dest="spy", - ) - parser.add_argument( - "--no_bench", - action="store_true", - default=False, - help="Flag to not show buy and hold comparison", - dest="no_bench", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - bt_view.display_simple_ema( - symbol=self.ticker, - data=self.stock, - ema_length=ns_parser.length, - spy_bt=ns_parser.spy, - no_bench=ns_parser.no_bench, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_emacross(self, other_args: List[str]): - """Call EMA Cross strategy""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="emacross", - description="Cross between a long and a short Exponential Moving Average.", - ) - parser.add_argument( - "-l", - "--long", - default=50, - dest="long", - type=check_positive, - help="Long EMA period", - ) - parser.add_argument( - "-s", - "--short", - default=20, - dest="short", - type=check_positive, - help="Short EMA period", - ) - parser.add_argument( - "--spy", - action="store_true", - default=False, - help="Flag to add spy hold comparison", - dest="spy", - ) - parser.add_argument( - "--no_bench", - action="store_true", - default=False, - help="Flag to not show buy and hold comparison", - dest="no_bench", - ) - parser.add_argument( - "--no_short", - action="store_false", - default=True, - dest="shortable", - help="Flag that disables the short sell", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - if ns_parser.long < ns_parser.short: - console.print("Short EMA period is longer than Long EMA period\n") - - bt_view.display_emacross( - symbol=self.ticker, - data=self.stock, - short_ema=ns_parser.short, - long_ema=ns_parser.long, - spy_bt=ns_parser.spy, - no_bench=ns_parser.no_bench, - shortable=ns_parser.shortable, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rsi(self, other_args: List[str]): - """Call RSI Strategy""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rsi", - description="""Strategy that buys when the stock is less than a threshold - and shorts when it exceeds a threshold.""", - ) - parser.add_argument( - "-p", - "--periods", - dest="periods", - help="Number of periods for RSI calculation", - type=check_positive, - default=14, - ) - parser.add_argument( - "-u", - "--high", - default=70, - dest="high", - type=check_positive, - help="High (upper) RSI Level", - ) - parser.add_argument( - "-l", - "--low", - default=30, - dest="low", - type=check_positive, - help="Low RSI Level", - ) - parser.add_argument( - "--spy", - action="store_true", - default=False, - help="Flag to add spy hold comparison", - dest="spy", - ) - parser.add_argument( - "--no_bench", - action="store_true", - default=False, - help="Flag to not show buy and hold comparison", - dest="no_bench", - ) - parser.add_argument( - "--no_short", - action="store_false", - default=True, - dest="shortable", - help="Flag that disables the short sell", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - if ns_parser.high < ns_parser.low: - console.print("Low RSI value is higher than Low RSI value\n") - - bt_view.display_rsi_strategy( - symbol=self.ticker, - data=self.stock, - periods=ns_parser.periods, - low_rsi=ns_parser.low, - high_rsi=ns_parser.high, - spy_bt=ns_parser.spy, - no_bench=ns_parser.no_bench, - shortable=ns_parser.shortable, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/stocks/backtesting/bt_model.py b/openbb_terminal/stocks/backtesting/bt_model.py deleted file mode 100644 index ebd51ff0ab8..00000000000 --- a/openbb_terminal/stocks/backtesting/bt_model.py +++ /dev/null @@ -1,292 +0,0 @@ -"""Backtesting Model""" - -__docformat__ = "numpy" - -import logging -import warnings - -import bt -import pandas as pd -import pandas_ta as ta -import yfinance as yf - -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import is_intraday - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_data(symbol: str, start_date: str = "2019-01-01") -> pd.DataFrame: - """Function to replace bt.get, gets Adjusted close of symbol using yfinance. - - Parameters - ---------- - symbol: str - Ticker to get data for - start_date: str - Start date in YYYY-MM-DD format - - Returns - ------- - prices: pd.DataFrame - Dataframe of Adj Close with columns = [ticker] - """ - data = yf.download(symbol, start=start_date, progress=False, ignore_tz=True) - close_col = ta_helpers.check_columns(data, high=False, low=False) - if close_col is None: - return pd.DataFrame() - df = pd.DataFrame(data[close_col]) - df.columns = [symbol] - - return df - - -@log_start_end(log=logger) -def buy_and_hold(symbol: str, start_date: str, name: str = "") -> bt.Backtest: - """Generates a buy and hold backtest object for the given ticker. - - Parameters - ---------- - symbol: str - Stock to test - start_date: str - Backtest start date, in YYYY-MM-DD format. Can be either string or datetime - name: str - Name of the backtest (for labeling purposes) - - Returns - ------- - bt.Backtest - Backtest object for buy and hold strategy - """ - prices = get_data(symbol, start_date) - bt_strategy = bt.Strategy( - name, - [ - bt.algos.RunOnce(), - bt.algos.SelectAll(), - bt.algos.WeighEqually(), - bt.algos.Rebalance(), - ], - ) - return bt.Backtest(bt_strategy, prices) - - -@log_start_end(log=logger) -def ema_strategy( - symbol: str, - data: pd.DataFrame, - ema_length: int = 20, - spy_bt: bool = True, - no_bench: bool = False, -) -> bt.backtest.Result: - """Perform backtest for simple EMA strategy. Buys when price>EMA(l). - - Parameters - ---------- - symbol: str - Stock ticker - data: pd.DataFrame - Dataframe of prices - ema_length: int - Length of ema window - spy_bt: bool - Boolean to add spy comparison - no_bench: bool - Boolean to not show buy and hold comparison - - Returns - ------- - bt.backtest.Result - Backtest results - """ - - # TODO: Help Wanted! - # Implement support for backtesting on intraday data - if is_intraday(data): - return None - data.index = pd.to_datetime(data.index.date) - - symbol = symbol.lower() - ema = pd.DataFrame() - start_date = data.index[0] - close_col = ta_helpers.check_columns(data, high=False, low=False) - if close_col is None: - return bt.backtest.Result() - prices = pd.DataFrame(data[close_col]) - prices.columns = [symbol] - ema[symbol] = ta.ema(prices[symbol], ema_length) - bt_strategy = bt.Strategy( - "AboveEMA", - [ - bt.algos.SelectWhere(prices >= ema), - bt.algos.WeighEqually(), - bt.algos.Rebalance(), - ], - ) - bt_backtest = bt.Backtest(bt_strategy, prices) - backtests = [bt_backtest] - if spy_bt: - spy_bt = buy_and_hold("spy", start_date, "SPY Hold") - backtests.append(spy_bt) - if not no_bench: - stock_bt = buy_and_hold(symbol, start_date, symbol.upper() + " Hold") - backtests.append(stock_bt) - - res = bt.run(*backtests) - return res - - -@log_start_end(log=logger) -def emacross_strategy( - symbol: str, - data: pd.DataFrame, - short_length: int = 20, - long_length: int = 50, - spy_bt: bool = True, - no_bench: bool = False, - shortable: bool = True, -) -> bt.backtest.Result: - """Perform backtest for simple EMA strategy. Buys when price>EMA(l). - - Parameters - ---------- - symbol : str - Stock ticker - data : pd.DataFrame - Dataframe of prices - short_length : int - Length of short ema window - long_length : int - Length of long ema window - spy_bt : bool - Boolean to add spy comparison - no_bench : bool - Boolean to not show buy and hold comparison - shortable : bool - Boolean to allow for selling of the stock at cross - - Returns - ------- - Result - Backtest results - """ - symbol = symbol.lower() - start_date = data.index[0] - close_col = ta_helpers.check_columns(data, low=False, high=False) - if close_col is None: - return bt.backtest.Result() - prices = pd.DataFrame(data[close_col]) - prices.columns = [symbol] - short_ema = pd.DataFrame(ta.ema(prices[symbol], short_length)) - short_ema.columns = [symbol] - long_ema = pd.DataFrame(ta.ema(prices[symbol], long_length)) - long_ema.columns = [symbol] - - # signals - signals = long_ema.copy() - signals[short_ema > long_ema] = 1.0 - signals[short_ema <= long_ema] = -1.0 * shortable - signals[long_ema.isnull()] = 0.0 - - combined_data = bt.merge(signals, prices, short_ema, long_ema) - combined_data.columns = ["signal", "price", "ema_short", "ema_long"] - bt_strategy = bt.Strategy( - "EMACross", - [ - bt.algos.WeighTarget(signals), - bt.algos.Rebalance(), - ], - ) - bt_backtest = bt.Backtest(bt_strategy, prices) - backtests = [bt_backtest] - if spy_bt: - spy_bt = buy_and_hold("spy", start_date, "SPY Hold") - backtests.append(spy_bt) - if not no_bench: - stock_bt = buy_and_hold(symbol, start_date, symbol.upper() + " Hold") - backtests.append(stock_bt) - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore") - res = bt.run(*backtests) - return res - - -@log_start_end(log=logger) -def rsi_strategy( - symbol: str, - data: pd.DataFrame, - periods: int = 14, - low_rsi: int = 30, - high_rsi: int = 70, - spy_bt: bool = True, - no_bench: bool = False, - shortable: bool = True, -) -> bt.backtest.Result: - """Perform backtest for simple EMA strategy. Buys when price>EMA(l). - - Parameters - ---------- - symbol : str - Stock ticker - data : pd.DataFrame - Dataframe of prices - periods : int - Number of periods for RSI calculation - low_rsi : int - Low RSI value to buy - high_rsi : int - High RSI value to sell - spy_bt : bool - Boolean to add spy comparison - no_bench : bool - Boolean to not show buy and hold comparison - shortable : bool - Flag to disable the ability to short sell - - Returns - ------- - Result - Backtest results - """ - symbol = symbol.lower() - start_date = data.index[0] - close_col = ta_helpers.check_columns(data, high=False, low=False) - if close_col is None: - return pd.DataFrame() - prices = pd.DataFrame(data[close_col]) - prices.columns = [symbol] - rsi = pd.DataFrame(ta.rsi(prices[symbol], periods)) - rsi.columns = [symbol] - - signal = 0 * rsi.copy() - signal[rsi > high_rsi] = -1 * shortable - signal[rsi < low_rsi] = 1 - signal[rsi.isnull()] = 0 - - merged_data = bt.merge(signal, prices) - merged_data.columns = ["signal", "price"] - - warnings.simplefilter(action="ignore", category=FutureWarning) - bt_strategy = bt.Strategy( - "RSI Reversion", [bt.algos.WeighTarget(signal), bt.algos.Rebalance()] - ) - bt_backtest = bt.Backtest(bt_strategy, prices) - bt_backtest = bt.Backtest(bt_strategy, prices) - backtests = [bt_backtest] - # Once the bt package replaces pd iteritems with items we can remove this - with warnings.catch_warnings(): - if spy_bt: - spy_bt = buy_and_hold("spy", start_date, "SPY Hold") - backtests.append(spy_bt) - if not no_bench: - stock_bt = buy_and_hold(symbol, start_date, symbol.upper() + " Hold") - backtests.append(stock_bt) - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore") - res = bt.run(*backtests) - return res diff --git a/openbb_terminal/stocks/backtesting/bt_view.py b/openbb_terminal/stocks/backtesting/bt_view.py deleted file mode 100644 index 42a95e8b949..00000000000 --- a/openbb_terminal/stocks/backtesting/bt_view.py +++ /dev/null @@ -1,316 +0,0 @@ -"""bt view module""" - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import Optional - -import numpy as np -import pandas as pd -import yfinance as yf - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, is_intraday -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.backtesting import bt_model - -logger = logging.getLogger(__name__) - - -np.seterr(divide="ignore") - - -@log_start_end(log=logger) -def display_whatif_scenario( - symbol: str, - date_shares_acquired: Optional[datetime] = None, - num_shares_acquired: float = 1, -): - """Display what if scenario - - Parameters - ---------- - symbol: str - Ticker to check what if scenario - date_shares_acquired: str - Date at which the shares were acquired - num_shares_acquired: float - Number of shares acquired - """ - data = yf.download(symbol, progress=False, ignore_tz=True) - - if not data.empty: - data = data["Adj Close"] - - ipo_date = data.index[0] - last_date = data.index[-1] - - if not date_shares_acquired: - date_shares_ac = ipo_date - console.print("IPO date selected by default.") - else: - date_shares_ac = date_shares_acquired - - if date_shares_ac > last_date: - console.print("The date selected is in the future. Select a valid date.", "\n") - return - - if date_shares_ac < ipo_date: - console.print( - f"{symbol} had not IPO at that date. Thus, changing the date to IPO on the {ipo_date.strftime('%Y-%m-%d')}", - "\n", - ) - date_shares_ac = ipo_date - - initial_shares_value = ( - data[data.index > date_shares_ac].values[0] * num_shares_acquired - ) - - if (num_shares_acquired - int(num_shares_acquired)) > 0: - nshares = round(num_shares_acquired, 2) - else: - nshares = round(num_shares_acquired) - - shares = "share" - these = "This" - if nshares > 1: - shares += "s" - these = "These" - - console.print( - f"If you had acquired {nshares} {shares} of {symbol} on " - f"{date_shares_ac.strftime('%Y-%m-%d')} with a cost of {initial_shares_value:.2f}." - ) - - current_shares_value = ( - data[data.index > date_shares_ac].values[-1] * num_shares_acquired - ) - if current_shares_value > initial_shares_value: - pct = 100 * ( - (current_shares_value - initial_shares_value) / initial_shares_value - ) - console.print( - f"{these} would be worth {current_shares_value:.2f}. Which represents an increase of {pct:.2f}%.", - "\n", - ) - else: - pct = 100 * ( - (initial_shares_value - current_shares_value) / initial_shares_value - ) - console.print( - f"{these} would be worth {current_shares_value:.2f}. Which represents an decrease of {pct:.2f}%.", - "\n", - ) - - -@log_start_end(log=logger) -def display_simple_ema( - symbol: str, - data: pd.DataFrame, - ema_length: int = 20, - spy_bt: bool = True, - no_bench: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Strategy where stock is bought when Price > EMA(l) - - Parameters - ---------- - symbol : str - Stock ticker - data : pd.Dataframe - Dataframe of prices - ema_length : int - Length of ema window - spy_bt : bool - Boolean to add spy comparison - no_bench : bool - Boolean to not show buy and hold comparison - export : bool - Format to export backtest results - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - # TODO: Help Wanted! - # Implement support for backtesting on intraday data - if is_intraday(data): - console.print("Backtesting on intraday data is not yet supported.") - console.print("Submit a feature request to let us know that you need it here:") - return console.print("https://openbb.co/request-a-feature") - - fig = OpenBBFigure(xaxis_title="Date").set_title(f"Equity for EMA({ema_length})") - - res = bt_model.ema_strategy(symbol, data, ema_length, spy_bt, no_bench) - df_res = res._get_series(None).rebase() # pylint: disable=protected-access - - for col in df_res.columns: - fig.add_scatter( - x=df_res.index, - y=df_res[col], - mode="lines", - name=col, - ) - - console.print(res.display(), "\n") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "simple_ema", - res.stats, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_emacross( - symbol: str, - data: pd.DataFrame, - short_ema: int = 20, - long_ema: int = 50, - spy_bt: bool = True, - no_bench: bool = False, - shortable: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): # pylint: disable=R0913 - """Strategy where we go long/short when EMA(short) is greater than/less than EMA(short) - - Parameters - ---------- - symbol : str - Stock ticker - data : pd.Dataframe - Dataframe of prices - short_ema : int - Length of short ema window - long_ema : int - Length of long ema window - spy_bt : bool - Boolean to add spy comparison - no_bench : bool - Boolean to not show buy and hold comparison - shortable : bool - Boolean to allow for selling of the stock at cross - export : str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - # TODO: Help Wanted! - # Implement support for backtesting on intraday data - if is_intraday(data): - console.print("Backtesting on intraday data is not yet supported.") - console.print("Submit a feature request to let us know that you need it here:") - return console.print("https://openbb.co/request-a-feature") - - fig = OpenBBFigure(xaxis_title="Date").set_title( - f"Equity for EMA({short_ema})/EMA({long_ema})" - ) - - res = bt_model.emacross_strategy( - symbol, data, short_ema, long_ema, spy_bt, no_bench, shortable - ) - df_res = res._get_series(None).rebase() # pylint: disable=protected-access - - for col in df_res.columns: - fig.add_scatter( - x=df_res.index, - y=df_res[col], - mode="lines", - name=col, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "emacross", - res.stats, - sheet_name, - fig, - ) - return fig.show(external=external_axes) - - -# pylint:disable=too-many-arguments -@log_start_end(log=logger) -def display_rsi_strategy( - symbol: str, - data: pd.DataFrame, - periods: int = 14, - low_rsi: int = 30, - high_rsi: int = 70, - spy_bt: bool = True, - no_bench: bool = False, - shortable: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Strategy that buys when the stock is less than a threshold and shorts when it exceeds a threshold. - - Parameters - ---------- - symbol : str - Stock ticker - data : pd.Dataframe - Dataframe of prices - periods : int - Number of periods for RSI calculation - low_rsi : int - Low RSI value to buy - high_rsi : int - High RSI value to sell - spy_bt : bool - Boolean to add spy comparison - no_bench : bool - Boolean to not show buy and hold comparison - shortable : bool - Boolean to allow for selling of the stock at cross - export : str - Format to export backtest results - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - # TODO: Help Wanted! - # Implement support for backtesting on intraday data - if is_intraday(data): - console.print("Backtesting on intraday data is not yet supported.") - console.print("Submit a feature request to let us know that you need it here:") - return console.print("https://openbb.co/request-a-feature") - - fig = OpenBBFigure(xaxis_title="Date").set_title( - f"Equity for RSI({periods}) between ({low_rsi}, {high_rsi})" - ) - - res = bt_model.rsi_strategy( - symbol, data, periods, low_rsi, high_rsi, spy_bt, no_bench, shortable - ) - df_res = res._get_series(None).rebase() # pylint: disable=protected-access - - for col in df_res.columns: - fig.add_scatter( - x=df_res.index, - y=df_res[col], - mode="lines", - name=col, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rsi_corss", - res.stats, - sheet_name, - fig, - ) - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/behavioural_analysis/__init__.py b/openbb_terminal/stocks/behavioural_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/behavioural_analysis/ba_controller.py b/openbb_terminal/stocks/behavioural_analysis/ba_controller.py deleted file mode 100644 index ff30679539a..00000000000 --- a/openbb_terminal/stocks/behavioural_analysis/ba_controller.py +++ /dev/null @@ -1,585 +0,0 @@ -"""Behavioural Analysis Controller Module.""" - -__docformat__ = "numpy" - -import argparse -import logging -from datetime import datetime, timedelta -from typing import List, Optional - -import yfinance as yf - -from openbb_terminal.common.behavioural_analysis import ( - finbrain_view, - google_view, - reddit_view, - stocktwits_view, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks.behavioural_analysis import ( - finnhub_view, -) - -# pylint:disable=R0904,C0302 - - -logger = logging.getLogger(__name__) - - -class BehaviouralAnalysisController(StockBaseController): - """Behavioural Analysis Controller class.""" - - CHOICES_COMMANDS = [ - "load", - "wsb", - "redditsent", - "bullbear", - "messages", - "trending", - "stalker", - "mentions", - "regions", - "queries", - "rise", - "headlines", - "snews", - "interest", - ] - - historical_sort = ["date", "value"] - historical_direction = ["asc", "desc"] - historical_metric = ["sentiment", "AHI", "RHI", "SGP"] - reddit_sort = ["relevance", "hot", "top", "new", "comments"] - reddit_time = ["hour", "day", "week", "month", "year", "all"] - PATH = "/stocks/ba/" - CHOICES_GENERATION = True - - def __init__(self, ticker: str, start: datetime, queue: Optional[List[str]] = None): - """Construct a new instance.""" - super().__init__(queue) - - self.ticker = ticker - self.start = start - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help.""" - mt = MenuText("stocks/ba/") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_ticker", self.ticker.upper()) - mt.add_raw("\n") - mt.add_cmd("headlines", self.ticker) - mt.add_cmd("snews", self.ticker) - mt.add_cmd("wsb") - mt.add_cmd("redditsent", self.ticker) - mt.add_cmd("trending") - mt.add_cmd("stalker") - mt.add_cmd("bullbear", self.ticker) - mt.add_cmd("messages", self.ticker) - mt.add_cmd("mentions", self.ticker) - mt.add_cmd("regions", self.ticker) - mt.add_cmd("interest", self.ticker) - mt.add_cmd("queries", self.ticker) - mt.add_cmd("rise", self.ticker) - console.print(text=mt.menu_text, menu="Stocks - Behavioural Analysis") - - def custom_reset(self): - """Class specific component of reset command.""" - if self.ticker: - return ["stocks", "ba", f"load {self.ticker}"] - return [] - - @log_start_end(log=logger) - def call_snews(self, other_args: List[str]): - """Process snews command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="snews", - description="Display stock price and headlines sentiment using VADER model over time. [Source: Finnhub]", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - finnhub_view.display_stock_price_headlines_sentiment( - symbol=self.ticker, export=ns_parser.export - ) - - @log_start_end(log=logger) - def call_wsb(self, other_args: List[str]): - """Process wsb command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="wsb", - description="""Print what WSB gang are up to in subreddit wallstreetbets. [Source: Reddit]""", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="n_limit", - type=check_positive, - default=10, - help="limit of posts to print.", - ) - parser.add_argument( - "--new", - action="store_true", - default=False, - dest="b_new", - help="new flag, if true the posts retrieved are based on being more recent rather than their score.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - reddit_view.display_wsb_community( - limit=ns_parser.n_limit, new=ns_parser.b_new - ) - - @log_start_end(log=logger) - def call_redditsent(self, other_args: List[str]): - """Process redditsent command.""" - parser = argparse.ArgumentParser( - add_help=False, - prog="redditsent", - description=""" - Determine general Reddit sentiment about a ticker. [Source: Reddit] - """, - ) - parser.add_argument( - "-s", - "--sort", - action="store", - dest="sort", - choices=self.reddit_sort, - default="relevance", - help="search sorting type", - ) - parser.add_argument( - "-c", - "--company", - action="store", - dest="company", - default=None, - help="explicit name of company to search for, will override ticker symbol", - ) - parser.add_argument( - "--subreddits", - action="store", - dest="subreddits", - default="all", - help="comma-separated list of subreddits to search", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - default=10, - type=check_positive, - help="how many posts to gather from each subreddit", - ) - parser.add_argument( - "-t", - "--time", - action="store", - dest="time", - default="week", - choices=self.reddit_time, - help="time period to get posts from -- all, year, month, week, or day; defaults to week", - ) - parser.add_argument( - "--full", - action="store_true", - dest="full_search", - default=False, - help="enable comprehensive search", - ) - parser.add_argument( - "-g", - "--graphic", - action="store_true", - dest="graphic", - default=True, - help="display graphic", - ) - parser.add_argument( - "-d", - "--display", - action="store_true", - dest="display", - default=False, - help="Print table of sentiment values", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - ticker = ns_parser.company if ns_parser.company else self.ticker - if self.ticker: - reddit_view.display_redditsent( - symbol=ticker, - sortby=ns_parser.sort, - limit=ns_parser.limit, - graphic=ns_parser.graphic, - time_frame=ns_parser.time, - full_search=ns_parser.full_search, - subreddits=ns_parser.subreddits, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - display=ns_parser.display, - ) - else: - console.print("No ticker loaded. Please load using 'load '\n") - - @log_start_end(log=logger) - def call_bullbear(self, other_args: List[str]): - """Process bullbear command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bullbear", - description=""" - Print bullbear sentiment based on last 30 messages on the board. - Also prints the watchlist_count. [Source: Stocktwits] - """, - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.ticker: - stocktwits_view.display_bullbear(symbol=self.ticker) - else: - console.print("No ticker loaded. Please load using 'load '\n") - - @log_start_end(log=logger) - def call_messages(self, other_args: List[str]): - """Process messages command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="messages", - description="""Print up to 30 of the last messages on the board. [Source: Stocktwits]""", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=30, - help="limit messages shown.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.ticker: - stocktwits_view.display_messages( - symbol=self.ticker, limit=ns_parser.limit - ) - else: - console.print("No ticker loaded. Please load using 'load '\n") - - @log_start_end(log=logger) - def call_trending(self, other_args: List[str]): - """Process trending command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="trending", - description="""Stocks trending. [Source: Stocktwits]""", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - stocktwits_view.display_trending() - - @log_start_end(log=logger) - def call_stalker(self, other_args: List[str]): - """Process stalker command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="stalker", - description="""Print up to the last 30 messages of a user. [Source: Stocktwits]""", - ) - parser.add_argument( - "-u", - "--user", - action="store", - dest="s_user", - type=str, - default="Newsfilter", - help="username.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=30, - help="limit messages shown.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - stocktwits_view.display_stalker( - user=ns_parser.s_user, limit=ns_parser.limit - ) - - @log_start_end(log=logger) - def call_mentions(self, other_args: List[str]): - """Process mentions command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mentions", - description=""" - Plot weekly bars of stock's interest over time. other users watchlist. [Source: Google] - """, - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - dest="start", - default=self.start if self.start != "" else "2000-01-01", - help="starting date (format YYYY-MM-DD) from when we are interested in stock's mentions.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.ticker: - google_view.display_mentions( - symbol=self.ticker, - start_date=ns_parser.start, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded. Please load using 'load '\n") - - @log_start_end(log=logger) - def call_regions(self, other_args: List[str]): - """Process regions command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="regions", - description="""Plot bars of regions based on stock's interest. [Source: Google]""", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="limit of regions to plot that show highest interest.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.ticker: - google_view.display_regions( - symbol=self.ticker, limit=ns_parser.limit, export=ns_parser.export - ) - else: - console.print("No ticker loaded. Please load using 'load '\n") - - @log_start_end(log=logger) - def call_interest(self, other_args: List[str]): - """Process interest command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="interest", - description=""" - Plot interest over time of words/sentences versus stock price. [Source: Google] - """, - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - dest="start", - default=(datetime.now() - timedelta(days=2 * 366)).strftime("%Y-%m-%d"), - help="starting date (format YYYY-MM-DD) of interest", - ) - parser.add_argument( - "-w", - "--words", - help="Select multiple sentences/words separated by commas. E.g. COVID,WW3,NFT", - dest="words", - nargs="+", - type=str, - default=None, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-w") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.ticker: - if ns_parser.words: - words = " ".join(ns_parser.words).split(",") - df_stock = yf.download( - self.ticker, - start=ns_parser.start.strftime("%Y-%m-%d"), - progress=False, - ) - - if not df_stock.empty: - google_view.display_correlation_interest( - symbol=self.ticker, - data=df_stock, - words=words, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - console.print( - "[red]Ticker provided doesn't exist, load another one.\n[/red]" - ) - else: - console.print("[red]Please provide a phrase for analysis.\n[/red]") - else: - console.print( - "[red]No ticker loaded. Please load using 'load '.\n[/red]" - ) - - @log_start_end(log=logger) - def call_queries(self, other_args: List[str]): - """Process queries command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="queries", - description="""Print top related queries with this stock's query. [Source: Google]""", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="limit of top related queries to print.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.ticker: - google_view.display_queries( - symbol=self.ticker, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded. Please load using 'load '\n") - - @log_start_end(log=logger) - def call_rise(self, other_args: List[str]): - """Process rise command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rise", - description="""Print top rising related queries with this stock's query. [Source: Google]""", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="limit of top rising related queries to print.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.ticker: - google_view.display_rise( - symbol=self.ticker, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded. Please load using 'load '\n") - - @log_start_end(log=logger) - def call_headlines(self, other_args: List[str]): - """Process finbrain command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="headlines", - description="""FinBrain collects the news headlines from 15+ major financial news - sources on a daily basis and analyzes them to generate sentiment scores - for more than 4500 US stocks.FinBrain Technologies develops deep learning - algorithms for financial analysis and prediction, which currently serves - traders from more than 150 countries all around the world. - [Source: https://finbrain.tech]""", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - if self.ticker: - finbrain_view.display_sentiment_analysis( - symbol=self.ticker, raw=ns_parser.raw, export=ns_parser.export - ) - else: - console.print("No ticker loaded. Please load using 'load '\n") diff --git a/openbb_terminal/stocks/behavioural_analysis/finnhub_model.py b/openbb_terminal/stocks/behavioural_analysis/finnhub_model.py deleted file mode 100644 index f6302f0ddef..00000000000 --- a/openbb_terminal/stocks/behavioural_analysis/finnhub_model.py +++ /dev/null @@ -1,128 +0,0 @@ -""" Finnhub Model """ - -__docformat__ = "numpy" - -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional - -import finnhub -import pandas as pd -from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import similar -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def get_company_news( - symbol: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> List[Dict]: - """Get news from a company. [Source: Finnhub] - - Parameters - ---------- - symbol : str - company ticker to look for news articles - start_date: Optional[str] - date to start searching articles, with format YYYY-MM-DD - end_date: Optional[str] - date to end searching articles, with format YYYY-MM-DD - - Returns - ------- - articles : List - term to search on the news articles - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - try: - finnhub_client = finnhub.Client( - api_key=get_current_user().credentials.API_FINNHUB_KEY - ) - articles = finnhub_client.company_news( - symbol.upper(), _from=start_date, to=end_date - ) - return articles - - except Exception as e: - console.print(f"[red]{e}\n[/red]") - return [{}] - - -@log_start_end(log=logger) -def process_news_headlines_sentiment( - articles: List[Dict], -) -> pd.DataFrame: - """Process news headlines sentiment of a company using a VADER model. - - Parameters - ---------- - articles : List[Dict] - list of articles with `headline` and `datetime` keys - - Returns - ------- - pd.DataFrame - Headlines sentiment using VADER model over time - """ - l_datetime = list() - l_compound = list() - - if articles and len(articles) > 1: - analyzer = SentimentIntensityAnalyzer() - - last_headline = "" - for article in articles: - # allows to discard news with similar headline - if similar(last_headline, article["headline"].upper()) < 0.7: - l_compound.append( - analyzer.polarity_scores(article["headline"])["compound"] - ) - l_datetime.append(datetime.fromtimestamp(article["datetime"])) - last_headline = article["headline"].upper() - - return pd.DataFrame(l_compound, index=l_datetime).sort_index() - - -@log_start_end(log=logger) -def get_headlines_sentiment( - symbol: str, -) -> pd.DataFrame: - """Get headlines sentiment using VADER model over time. [Source: Finnhub] - - Parameters - ---------- - symbol : str - Ticker of company - - Returns - ---------- - pd.DataFrame - The news article information - """ - start = datetime.now() - timedelta(days=30) - if not symbol: - console.print("[red]Do not run this command without setting a ticker.[/red]\n") - return pd.DataFrame() - - articles = get_company_news( - symbol.upper(), - start_date=start.strftime("%Y-%m-%d"), - end_date=datetime.now().strftime("%Y-%m-%d"), - ) - sentiment = process_news_headlines_sentiment(articles) - - return sentiment diff --git a/openbb_terminal/stocks/behavioural_analysis/finnhub_view.py b/openbb_terminal/stocks/behavioural_analysis/finnhub_view.py deleted file mode 100644 index 91d195d4fc4..00000000000 --- a/openbb_terminal/stocks/behavioural_analysis/finnhub_view.py +++ /dev/null @@ -1,125 +0,0 @@ -""" Finnhub View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd -import yfinance as yf - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.stocks.behavioural_analysis import finnhub_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def display_stock_price_headlines_sentiment( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Display stock price and headlines sentiment using VADER model over time. [Source: Finnhub] - - Parameters - ---------- - symbol : str - Ticker of company - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - sentiment = finnhub_model.get_headlines_sentiment(symbol) - - if not sentiment.empty: - sentiment_data = [item for sublist in sentiment.values for item in sublist] - - df_stock: pd.DataFrame = yf.download( - symbol, - start=min(sentiment.index).to_pydatetime().date(), - interval="15m", - prepost=True, - progress=False, - ) - - if not df_stock.empty: - fig = OpenBBFigure.create_subplots(2, 1, row_heights=[1, 0.5]) - - fig.add_scatter( - x=df_stock.index, - y=df_stock["Adj Close"].values, - name="Price", - line_color="#FCED00", - connectgaps=True, - row=1, - col=1, - showlegend=False, - ) - - fig.add_scatter( - x=sentiment.index, - y=pd.Series(sentiment_data) - .rolling(window=5, min_periods=1) - .mean() - .values, - name="Sentiment", - connectgaps=True, - row=2, - col=1, - ) - - fig.add_bar( - x=sentiment[sentiment.values >= 0].index, - y=[ - item - for sublist in sentiment[sentiment.values >= 0].values - for item in sublist - ], - name="Positive", - marker=dict(color=theme.up_color, line=dict(color=theme.up_color)), - row=2, - col=1, - ) - - fig.add_bar( - x=sentiment[sentiment.values < 0].index, - y=[ - item - for sublist in sentiment[sentiment.values < 0].values - for item in sublist - ], - name="Negative", - marker=dict(color=theme.down_color, line=dict(color=theme.down_color)), - row=2, - col=1, - ) - - fig.update_layout( - title=f"Headlines sentiment and {symbol} price", - xaxis2_title="Date", - yaxis_title="Stock Price", - yaxis2_title="Headline Sentiment", - ) - fig.bar_width = 5 - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "snews", - sentiment, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - return None diff --git a/openbb_terminal/stocks/cboe_model.py b/openbb_terminal/stocks/cboe_model.py deleted file mode 100644 index eb43cd9a8ea..00000000000 --- a/openbb_terminal/stocks/cboe_model.py +++ /dev/null @@ -1,60 +0,0 @@ -"""CBOE Model Functions""" - -__docformat__ = "numpy" - -from typing import Tuple - -import pandas as pd - -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - - -def get_top_of_book( - symbol: str, exchange: str = "BZX" -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Get top of book bid and ask for ticker on exchange [CBOE.com] - - Parameters - ---------- - symbol: str - Ticker to get - exchange: str - Exchange to look at. Can be `BZX`,`EDGX`, `BYX`, `EDGA` - - Returns - ------- - pd.DatatFrame - Dataframe of Bids - pd.DataFrame - Dataframe of asks - - """ - if exchange not in ["BZX", "EDGX", "BYX", "EDGA"]: - console.print(f"[red]Exchange not valid: {exchange}[/red]") - return pd.DataFrame(), pd.DataFrame() - # exchange need to be lower case. Not sure why - url = f"https://www.cboe.com/json/{exchange.lower()}/book/{symbol}" - - r = request( - url, - headers={ - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko)" - " Chrome/106.0.0.0 Safari/537.36", - "referer": "https://www.cboe.com/us/equities/market_statistics/book_viewer/", - }, - ) - if r.status_code != 200: - console.print(f"[red]Request failed with code {r.status_code}[/red]") - return pd.DataFrame(), pd.DataFrame() - r_json = r.json() - if r_json["data"]["company"] == "unknown symbol": - console.print(f"[red]Unknown symbol: {symbol}[/red]") - return pd.DataFrame(), pd.DataFrame() - bids = pd.DataFrame(r_json["data"]["bids"], columns=["Size", "Price"]) - asks = pd.DataFrame(r_json["data"]["asks"], columns=["Size", "Price"]) - if bids.empty or asks.empty: - console.print( - "[red]No bid/ask data. Note this is real time so there is no data after market close.[/red]" - ) - return bids, asks diff --git a/openbb_terminal/stocks/cboe_view.py b/openbb_terminal/stocks/cboe_view.py deleted file mode 100644 index 3bf6feac8ee..00000000000 --- a/openbb_terminal/stocks/cboe_view.py +++ /dev/null @@ -1,36 +0,0 @@ -"""CBOE View""" - -__docformat__ = "numpy" - -import pandas as pd - -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks import cboe_model - - -def display_top_of_book(symbol: str, exchange: str = "BZX"): - """Prints Table showing top of book data [CBOE] - - Parameters - ---------- - symbol: str - Symbol to get data for - exchange: str - Exchange to get data for - """ - bids, asks = cboe_model.get_top_of_book(symbol, exchange) - - if not bids.empty and not asks.empty: - asks = asks[["Price", "Size"]] - bids.columns = ["Bid " + col for col in bids.columns] - asks.columns = ["Ask " + col for col in asks.columns] - merged = pd.concat([bids, asks], axis=1) - print_rich_table( - merged, - title=f"{symbol} Top of Book", - show_index=False, - headers=merged.columns, - ) - - console.print() diff --git a/openbb_terminal/stocks/comparison_analysis/__init__.py b/openbb_terminal/stocks/comparison_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/comparison_analysis/ca_controller.py b/openbb_terminal/stocks/comparison_analysis/ca_controller.py deleted file mode 100644 index cc9b9c33343..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/ca_controller.py +++ /dev/null @@ -1,926 +0,0 @@ -"""Comparison Analysis Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -import random -from datetime import datetime, timedelta -from typing import List, Optional - -import yfinance as yf - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - check_start_less_than_end, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console, get_ordered_list_sources -from openbb_terminal.stocks.comparison_analysis import ( - finbrain_view, - finnhub_model, - finviz_compare_view, - marketwatch_view, - polygon_model, - yahoo_finance_view, -) - -# pylint: disable=E1121,C0302,R0904 - -# TODO: HELP WANTED! This controller still has some view functionality that should be -# refactored in order to implement an API wrapper. Use the discovery controller -# as an example. - - -logger = logging.getLogger(__name__) - - -class ComparisonAnalysisController(BaseController): - """Comparison Analysis Controller class""" - - CHOICES_COMMANDS = [ - "ticker", - "get", - "set", - "add", - "rmv", - "historical", - "hcorr", - "volume", - "income", - "balance", - "cashflow", - "sentiment", - "scorr", - "overview", - "valuation", - "financial", - "ownership", - "performance", - "technical", - ] - choices_ohlca = ["o", "h", "l", "c", "a"] - CHOICES_MENUS: List = list() - PATH = "/stocks/ca/" - CHOICES_GENERATION = True - - def __init__( - self, similar: Optional[List[str]] = None, queue: Optional[List[str]] = None - ): - """Constructor""" - super().__init__(queue) - - self.ticker = "" - self.user = "" - - if similar: - self.similar = similar - if len(similar) == 1: - self.ticker = self.similar[0].upper() - else: - self.similar = [] - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def call_exit(self, _) -> None: - """Process exit terminal command from forecast menu.""" - self.save_class() - for _ in range(self.PATH.count("/") + 1): - self.queue.insert(0, "quit") - - def print_help(self): - """Print help""" - mt = MenuText("stocks/ca/", 80) - mt.add_cmd("ticker") - mt.add_raw("\n") - mt.add_param("_ticker", self.ticker) - mt.add_raw("\n") - mt.add_cmd("get", self.ticker) - mt.add_raw("\n") - mt.add_cmd("set") - mt.add_cmd("add") - mt.add_cmd("rmv") - mt.add_raw("\n") - mt.add_param("_similar", ", ".join(self.similar)) - mt.add_raw("\n") - mt.add_cmd("historical", self.similar and len(self.similar) > 1) - mt.add_cmd("hcorr", self.similar and len(self.similar) > 1) - mt.add_cmd("volume", self.similar and len(self.similar) > 1) - mt.add_cmd("income", self.similar and len(self.similar) > 1) - mt.add_cmd("balance", self.similar and len(self.similar) > 1) - mt.add_cmd("cashflow", self.similar and len(self.similar) > 1) - mt.add_cmd("sentiment", self.similar and len(self.similar) > 1) - mt.add_cmd("scorr", self.similar and len(self.similar) > 1) - mt.add_cmd("overview", self.similar and len(self.similar) > 1) - mt.add_cmd("valuation", self.similar and len(self.similar) > 1) - mt.add_cmd("financial", self.similar and len(self.similar) > 1) - mt.add_cmd("ownership", self.similar and len(self.similar) > 1) - mt.add_cmd("performance", self.similar and len(self.similar) > 1) - mt.add_cmd("technical", self.similar and len(self.similar) > 1) - console.print(text=mt.menu_text, menu="Stocks - Comparison Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - if self.similar: - return ["stocks", "ca", f"set {','.join(self.similar)}"] - return [] - - # TODO: Figure out if this function is actually needed here - @log_start_end(log=logger) - def call_ticker(self, other_args: List[str]): - """Process ticker command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ticker", - description="""Set ticker to extract similar from""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - type=str, - required="-h" not in other_args, - help="Ticker get similar tickers from", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if "," in ns_parser.ticker: - console.print("Only one ticker must be selected!") - else: - stock_data = yf.download( - ns_parser.ticker, - progress=False, - ) - if stock_data.empty: - console.print( - f"The ticker '{ns_parser.ticker}' provided does not exist!" - ) - else: - self.ticker = ns_parser.ticker.upper() - self.similar = [] - - @log_start_end(log=logger) - def call_get(self, other_args: List[str]): - """Process get command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="get", - description="""Get similar companies from selected data source (default: Finviz) to compare with.""", - ) - parser.add_argument( - "-u", - "--us_only", - action="store_true", - default=False, - dest="us_only", - help="Show only stocks from the US stock exchanges. Works only with Polygon", - ) - parser.add_argument( - "-n", - "--nocountry", - action="store_true", - default=False, - dest="b_no_country", - help="Similar stocks from finviz using only Industry and Sector.", - ) - parser.add_argument( - "-l", - "--limit", - default=10, - dest="limit", - type=check_positive, - help="Limit of stocks to retrieve.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.ticker: - if ns_parser.source == "Polygon": - self.similar = polygon_model.get_similar_companies( - self.ticker, ns_parser.us_only - ) - if self.similar is None: - return - self.user = "Polygon" - - if self.ticker.upper() in self.similar: - self.similar.remove(self.ticker.upper()) - - if len(self.similar) > ns_parser.limit: - random.shuffle(self.similar) - self.similar = sorted(self.similar[: ns_parser.limit]) - console.print( - f"The limit of stocks to compare are {ns_parser.limit}. The subsample will occur randomly.\n", - ) - - if self.similar: - self.similar = [self.ticker] + self.similar - console.print( - f"[{self.user}] Similar Companies: {', '.join(self.similar)}", - "\n", - ) - elif ns_parser.source == "Finnhub": - self.similar = finnhub_model.get_similar_companies(self.ticker) - - if self.similar: - self.user = "Finnhub" - - if self.ticker.upper() in self.similar: - self.similar.remove(self.ticker.upper()) - - if len(self.similar) > ns_parser.limit: - random.shuffle(self.similar) - self.similar = sorted(self.similar[: ns_parser.limit]) - console.print( - f"The limit of stocks to compare are {ns_parser.limit}." - " The subsample will occur randomly.\n", - ) - - self.similar = [self.ticker] + self.similar - console.print( - f"[{self.user}] Similar Companies: {', '.join(self.similar)}", - "\n", - ) - else: - console.print( - f"Use a valid data source: {', '.join(get_ordered_list_sources(f'{self.PATH}get'))}" - ) - else: - console.print( - "You need to 'set' a ticker to get similar companies from first! This is " - "for example done by running 'ticker aapl'" - ) - - @log_start_end(log=logger) - def call_add(self, other_args: List[str]): - """Process add command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="add", - description="""Add similar tickers to compare with.""", - ) - parser.add_argument( - "-s", - "--similar", - dest="l_similar", - type=lambda s: [str(item).upper() for item in s.split(",")], - default=[], - help="Tickers to add to similar list", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.similar: - self.similar = list(set(self.similar + ns_parser.l_similar)) - else: - self.similar = ns_parser.l_similar - self.user = "Custom" - - console.print( - f"[{self.user}] Similar Companies: {', '.join(self.similar)}", "\n" - ) - - @log_start_end(log=logger) - def call_rmv(self, other_args: List[str]): - """Process rmv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rmv", - description="""Remove similar tickers to compare with.""", - ) - parser.add_argument( - "-s", - "--similar", - dest="l_similar", - type=lambda s: [str(item).upper() for item in s.split(",")], - default=[], - help="Tickers to remove from similar list", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if ns_parser.l_similar: - for symbol in ns_parser.l_similar: - if symbol in self.similar: - self.similar.remove(symbol) - else: - console.print( - f"Ticker {symbol} does not exist in similar list to be removed" - ) - - console.print( - f"[{self.user}] Similar Companies: {', '.join(self.similar)}" - ) - - else: - self.similar = [] - - console.print() - self.user = "Custom" - - @log_start_end(log=logger) - def call_set(self, other_args: List[str]): - """Process set command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="set", - description="""Select similar companies to compare with.""", - ) - parser.add_argument( - "-s", - "--similar", - dest="l_similar", - type=lambda s: [str(item).upper() for item in s.split(",")], - default=[], - help="similar companies to compare with.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.ticker: - self.similar = list(set(ns_parser.l_similar + [self.ticker.upper()])) - else: - self.similar = list(set(ns_parser.l_similar)) - self.user = "Custom" - console.print( - f"[{self.user}] Similar Companies: {', '.join(self.similar)}", "\n" - ) - - @log_start_end(log=logger) - def call_historical(self, other_args: List[str]): - """Process historical command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="historical", - description="""Historical price comparison between similar companies.""", - ) - parser.add_argument( - "-t", - "--type", - action="store", - dest="type_candle", - type=str, - choices=self.choices_ohlca, - default="a", # in case it's adjusted close - help="Candle data to use: o-open, h-high, l-low, c-close, a-adjusted close.", - ) - parser.add_argument( - "-n", - "--normalize", - action="store_true", - dest="normalize", - default=False, - help="Flag to normalize all prices on same 0-1 scale", - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - default=(datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d"), - dest="start", - help="The starting date (format YYYY-MM-DD) of the stocks", - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=(datetime.now()).strftime("%Y-%m-%d"), - dest="end", - help="The end date (format YYYY-MM-DD) of the stocks", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - if check_start_less_than_end(ns_parser.start, ns_parser.end): - return - yahoo_finance_view.display_historical( - similar=self.similar, - start_date=ns_parser.start.strftime("%Y-%m-%d"), - end_date=ns_parser.end.strftime("%Y-%m-%d"), - candle_type=ns_parser.type_candle, - normalize=ns_parser.normalize, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - else: - console.print( - "Please make sure there are more than 1 similar tickers selected. \n" - ) - - @log_start_end(log=logger) - def call_hcorr(self, other_args: List[str]): - """Process historical correlation command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hcorr", - description=""" Correlation heatmap based on historical price comparison between similar - companies. - """, - ) - parser.add_argument( - "-t", - "--type", - action="store", - dest="type_candle", - type=str, - choices=self.choices_ohlca, - default="a", # in case it's adjusted close - help="Candle data to use: o-open, h-high, l-low, c-close, a-adjusted close, r-returns.", - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - default=(datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d"), - dest="start", - help="The starting date (format YYYY-MM-DD) of the stock", - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=(datetime.now()).strftime("%Y-%m-%d"), - dest="end", - help="The end date (format YYYY-MM-DD) of the stocks", - ) - parser.add_argument( - "--display-full-matrix", - action="store_true", - help="Display all matrix values, rather than masking off half.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - if check_start_less_than_end(ns_parser.start, ns_parser.end): - return - yahoo_finance_view.display_correlation( - similar=self.similar, - start_date=ns_parser.start.strftime("%Y-%m-%d"), - end_date=ns_parser.end.strftime("%Y-%m-%d"), - candle_type=ns_parser.type_candle, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - display_full_matrix=ns_parser.display_full_matrix, - raw=ns_parser.raw, - ) - else: - console.print("Please make sure there are similar tickers selected. \n") - - @log_start_end(log=logger) - def call_income(self, other_args: List[str]): - """Process income command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="income", - description=""" - Prints either yearly or quarterly income statement the company, and compares - it against similar companies. - """, - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter financial data flag.", - ) - parser.add_argument( - "-t", - "--timeframe", - dest="s_timeframe", - type=str, - default=None, - help="Specify yearly/quarterly timeframe. Default is last.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - marketwatch_view.display_income_comparison( - symbols=self.similar, - timeframe=ns_parser.s_timeframe, - quarter=ns_parser.b_quarter, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_volume(self, other_args: List[str]): - """Process volume command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="volume", - description="""Historical volume comparison between similar companies. - """, - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - default=(datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d"), - dest="start", - help="The starting date (format YYYY-MM-DD) of the stock", - ) - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=(datetime.now()).strftime("%Y-%m-%d"), - dest="end", - help="The end date (format YYYY-MM-DD) of the stocks", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - if check_start_less_than_end(ns_parser.start, ns_parser.end): - return - yahoo_finance_view.display_volume( - similar=self.similar, - start_date=ns_parser.start.strftime("%Y-%m-%d"), - end_date=ns_parser.end.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - else: - console.print("Please make sure there are similar tickers selected. \n") - - @log_start_end(log=logger) - def call_balance(self, other_args: List[str]): - """Process balance command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="balance", - description=""" - Prints either yearly or quarterly balance statement the company, and compares - it against similar companies. - """, - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter financial data flag.", - ) - parser.add_argument( - "-t", - "--timeframe", - dest="s_timeframe", - type=str, - default=None, - help="Specify yearly/quarterly timeframe. Default is last.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - marketwatch_view.display_balance_comparison( - symbols=self.similar, - timeframe=ns_parser.s_timeframe, - quarter=ns_parser.b_quarter, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cashflow(self, other_args: List[str]): - """Process cashflow command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cashflow", - description=""" - Prints either yearly or quarterly cashflow statement the company, and compares - it against similar companies. - """, - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter financial data flag.", - ) - parser.add_argument( - "-t", - "--timeframe", - dest="s_timeframe", - type=str, - default=None, - help="Specify year/quarter of the cashflow statement to be retrieved. The format for year is YYYY and for " - "quarter is DD-MMM-YYY (for example, 30-Sep-2021). Default is last year/quarter.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - marketwatch_view.display_cashflow_comparison( - symbols=self.similar, - timeframe=ns_parser.s_timeframe, - quarter=ns_parser.b_quarter, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_sentiment(self, other_args: List[str]): - """Process sentiment command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sentiment_compare", - description=""" - FinBrain's sentiment comparison across similar tickers. - """, - ) - parser.add_argument( - "-r", - "--raw", - action="store_true", - default=False, - help="Display raw sentiment data", - dest="raw", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - finbrain_view.display_sentiment_compare( - similar=self.similar, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print( - "Please make sure there are more than 1 similar tickers selected. \n" - ) - - @log_start_end(log=logger) - def call_scorr(self, other_args: List[str]): - """Process sentiment correlation command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sentiment_compare", - description=""" - FinBrain's sentiment correlation across similar tickers. - """, - ) - parser.add_argument( - "-r", - "--raw", - action="store_true", - default=False, - help="Display raw sentiment data", - dest="raw", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - finbrain_view.display_sentiment_correlation( - similar=self.similar, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("Please make sure there are similar tickers selected. \n") - - @log_start_end(log=logger) - def call_overview(self, other_args: List[str]): - """Process overview command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="overview", - description=""" - Prints screener data of similar companies. [Source: Finviz] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - finviz_compare_view.screener( - similar=self.similar, - data_type="overview", - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print( - "Please make sure there are more than 1 similar tickers selected. \n" - ) - - @log_start_end(log=logger) - def call_valuation(self, other_args: List[str]): - """Process valuation command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="valuation", - description=""" - Prints screener data of similar companies. [Source: Finviz] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - finviz_compare_view.screener( - similar=self.similar, - data_type="valuation", - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print( - "Please make sure there are more than 1 similar tickers selected. \n" - ) - - @log_start_end(log=logger) - def call_financial(self, other_args: List[str]): - """Process financial command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="financial", - description=""" - Prints screener data of similar companies. [Source: Finviz] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - finviz_compare_view.screener( - similar=self.similar, - data_type="financial", - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print( - "Please make sure there are more than 1 similar tickers selected. \n" - ) - - @log_start_end(log=logger) - def call_ownership(self, other_args: List[str]): - """Process ownership command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ownership", - description=""" - Prints screener data of similar companies. [Source: Finviz] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - finviz_compare_view.screener( - similar=self.similar, - data_type="ownership", - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print( - "Please make sure there are more than 1 similar tickers selected. \n" - ) - - @log_start_end(log=logger) - def call_performance(self, other_args: List[str]): - """Process performance command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="performance", - description=""" - Prints screener data of similar companies. [Source: Finviz] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - finviz_compare_view.screener( - similar=self.similar, - data_type="performance", - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print( - "Please make sure there are more than 1 similar tickers selected. \n" - ) - - @log_start_end(log=logger) - def call_technical(self, other_args: List[str]): - """Process technical command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="technical", - description=""" - Prints screener data of similar companies. [Source: Finviz] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.similar and len(self.similar) > 1: - finviz_compare_view.screener( - similar=self.similar, - data_type="technical", - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print( - "Please make sure there are more than 1 similar tickers selected. \n" - ) diff --git a/openbb_terminal/stocks/comparison_analysis/finbrain_model.py b/openbb_terminal/stocks/comparison_analysis/finbrain_model.py deleted file mode 100644 index 8b8a87f76e6..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/finbrain_model.py +++ /dev/null @@ -1,146 +0,0 @@ -""" Comparison Analysis FinBrain Model """ - -__docformat__ = "numpy" - -import logging -from typing import List, Tuple - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def find_smallest_num_data_point(results_list: List[dict]) -> int: - """Helper function to find the number for the smallest total number of data points - out of all the tickers. This is necessary because if one ticker has more or less - data points than another then it will throw an indexing error. The solution is to - have each ticker have the same number of data points as to graph and view properly. - We chose to set each ticker to the minimum of number data points out of all the - tickers. - - Parameters - ---------- - results_list : List[json] - List of dicts storing ticker data - - Returns - ------- - int - Value of smallest total number of sentiment data points - """ - small_list = list() - for result_json in results_list: - if ( - "ticker" in result_json - and "sentimentAnalysis" in result_json - and len(result_json["sentimentAnalysis"].values()) > 0 - ): - small_list.append(len(result_json["sentimentAnalysis"].values())) - return min(small_list) - - -@log_start_end(log=logger) -def get_sentiments(symbols: List[str]) -> pd.DataFrame: - """Gets Sentiment analysis from several symbols provided by FinBrain's API. - - Parameters - ---------- - symbols : List[str] - List of tickers to get sentiment. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - - Returns - ------- - pd.DataFrame - Contains sentiment analysis from several tickers - """ - - df_sentiment = pd.DataFrame() - dates_sentiment = [] - symbols_to_remove = list() - results_list = list() - for ticker in symbols: - result = request(f"https://api.finbrain.tech/v0/sentiments/{ticker}") - # Check status code, if its correct then convert to dict using .json() - if result.status_code == 200: - result_json = result.json() - results_list.append(result_json) - else: - console.print( - f"Request error in retrieving {ticker} sentiment from FinBrain API" - ) - symbols_to_remove.append(ticker) - - # Finds the smallest amount of data points from any of the tickers as to not run - # into an indexing error when graphing - smallest_num_data_point = find_smallest_num_data_point(results_list) - - num = 0 - for result_json in results_list: - ticker = symbols[num] - # Checks to see if sentiment data in results_json - if ( - "ticker" in result_json - and "sentimentAnalysis" in result_json - and len(result_json["sentimentAnalysis"].values()) > 0 - ): - # Storing sentiments and dates in list - sentiments = list(result_json["sentimentAnalysis"].values()) - dates_sentiment = list(result_json["sentimentAnalysis"].keys()) - - # If there are more sentiment data points for one ticker compared to the - # smallest amount of data points, then remove that data points from that - # ticker as to be able to graph properly - if len(sentiments) > smallest_num_data_point: - sentiments = sentiments[0:smallest_num_data_point] - dates_sentiment = dates_sentiment[0:smallest_num_data_point] - - df_sentiment[ticker] = [float(val) for val in sentiments] - - # If sentiment data not in results_json remove it - else: - console.print( - f"Unexpected data format or no data from FinBrain API for {ticker}" - ) - symbols_to_remove.append(ticker) - - num = num + 1 - - for ticker in symbols_to_remove: - symbols.remove(ticker) - - if not df_sentiment.empty: - df_sentiment.index = dates_sentiment - df_sentiment.sort_index(ascending=True, inplace=True) - - return df_sentiment - - -@log_start_end(log=logger) -def get_sentiment_correlation( - similar: List[str], -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Get correlation sentiments across similar companies. [Source: FinBrain]. - - Parameters - ---------- - similar : List[str] - Similar companies to compare income with. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - - Returns - ------- - Tuple[pd.DataFrame,pd.DataFrame] - Contains sentiment analysis from several tickers - """ - df_sentiment = get_sentiments(similar) - corrs = df_sentiment.corr() - - return corrs, df_sentiment diff --git a/openbb_terminal/stocks/comparison_analysis/finbrain_view.py b/openbb_terminal/stocks/comparison_analysis/finbrain_view.py deleted file mode 100644 index e1896dd718e..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/finbrain_view.py +++ /dev/null @@ -1,200 +0,0 @@ -""" Comparison Analysis FinBrain View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import List, Optional - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.comparison_analysis import finbrain_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_sentiment_compare( - similar: List[str], - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display sentiment for all ticker. [Source: FinBrain]. - - Parameters - ---------- - similar : List[str] - Similar companies to compare income with. - Comparable companies can be accessed through - finviz_peers(), finnhub_peers() or polygon_peers(). - raw : bool, optional - Output raw values, by default False - export : str, optional - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_sentiment = finbrain_model.get_sentiments(similar) - if df_sentiment.empty: - return console.print("No sentiments found.") - - fig = OpenBBFigure(yaxis_title="Sentiment") - fig.set_title(f"FinBrain's Sentiment Analysis since {df_sentiment.index[0]}") - - # we need to remove the tickers that are not in the df_sentiment dataframe - similar = [tick for tick in similar if tick in df_sentiment.columns] - - for idx, tick in enumerate(similar): - offset = 2 * idx - fig.add_hline(y=offset + 1, line=dict(color="white", dash="dash")) - - senValues = np.array(pd.to_numeric(df_sentiment[tick].values)) - senNone = np.array(0 * len(df_sentiment)) - negative_yloc = np.where(senValues < senNone)[0] - positive_yloc = np.where(senValues > senNone)[0] - - fig.add_scatter( - x=df_sentiment.index[positive_yloc], - y=pd.to_numeric(df_sentiment[tick].values)[positive_yloc] + offset, - mode="lines", - line_width=0, - name=tick, - ) - fig.add_scatter( - x=[df_sentiment.index[0], df_sentiment.index[-1]], - y=[offset, offset], - fillcolor=theme.up_color, - line=dict(color="white", dash="dash"), - fill="tonexty", - mode="lines", - name=tick, - ) - fig.add_scatter( - x=df_sentiment.index[negative_yloc], - y=pd.to_numeric(df_sentiment[tick].values)[negative_yloc] + offset, - fill="tonexty", - fillcolor=theme.down_color, - line_width=0, - mode="lines", - name=tick, - ) - - fig.add_hline(y=-1, line=dict(color="white", dash="dash")) - fig.update_traces(showlegend=False) - fig.update_yaxes(ticktext=similar, tickvals=np.arange(len(similar)) * 2) - - if raw: - print_rich_table( - df_sentiment, - headers=list(df_sentiment.columns), - show_index=True, - index_name="Date", - title="Ticker Sentiment", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "sentiment", - df_sentiment, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_sentiment_correlation( - similar: List[str], - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Plot correlation sentiments heatmap across similar companies. [Source: FinBrain]. - - Parameters - ---------- - similar : List[str] - Similar companies to compare income with. - Comparable companies can be accessed through - finviz_peers(), finnhub_peers() or polygon_peers(). - raw : bool, optional - Output raw values, by default False - export : str, optional - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - corrs, df_sentiment = finbrain_model.get_sentiment_correlation(similar) - - if df_sentiment.empty: - return console.print("No sentiments found.") - - similar_string = ",".join(similar) - fig = OpenBBFigure().set_title( - f"Sentiment correlation heatmap across {similar_string}" - ) - - mask = np.zeros((len(similar), len(similar)), dtype=bool) - mask[np.triu_indices(len(mask))] = True - df_corr = corrs.mask(mask) - - df_corr.fillna("", inplace=True) - - fig.add_heatmap( - x=df_corr.columns, - y=df_corr.index, - z=df_corr.values.tolist(), - zmin=-1, - zmax=1, - hoverongaps=False, - showscale=True, - colorscale="RdYlGn", - text=df_corr.to_numpy(), - textfont=dict(color="black"), - texttemplate="%{text:.2f}", - colorbar=dict( - thickness=20, - thicknessmode="pixels", - x=1.15, - y=1, - xanchor="right", - yanchor="top", - xpad=10, - ), - xgap=1, - ygap=1, - ) - fig.update_layout( - margin=dict(l=0, r=20, t=20, b=0), - yaxis=dict(autorange="reversed"), - ) - - if raw: - print_rich_table( - corrs, - headers=list(corrs.columns), - show_index=True, - title="Correlation Sentiments", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "scorr", - corrs, - sheet_name, - fig, - ) - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/stocks/comparison_analysis/finnhub_model.py b/openbb_terminal/stocks/comparison_analysis/finnhub_model.py deleted file mode 100644 index 278fac87ab1..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/finnhub_model.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Finnhub model""" - -__docformat__ = "numpy" - -import logging -from typing import List - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def get_similar_companies(symbol: str) -> List[str]: - """Get similar companies from Finhub. - - Parameters - ---------- - symbol : str - Ticker to find comparisons for - - Returns - ------- - List[str] - List of similar companies - """ - - response = request( - f"https://finnhub.io/api/v1/stock/peers?symbol={symbol}&token={get_current_user().credentials.API_FINNHUB_KEY}" - ) - - similar = [] - - if response.status_code == 200: - similar = response.json() - - if not similar: - console.print("Similar companies not found.") - elif response.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - elif response.status_code == 403: - console.print("[red]API Key not authorized for Premium Feature[/red]\n") - else: - console.print(f"Error in request: {response.json()['error']}", "\n") - - return similar diff --git a/openbb_terminal/stocks/comparison_analysis/finviz_compare_model.py b/openbb_terminal/stocks/comparison_analysis/finviz_compare_model.py deleted file mode 100644 index 959437482ff..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/finviz_compare_model.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Finviz Comparison Model""" - -__docformat__ = "numpy" - -import logging -from typing import List - -import pandas as pd -from finvizfinance.screener import ( - financial, - overview, - ownership, - performance, - technical, - valuation, -) - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_comparison_data(similar: List[str], data_type: str = "overview"): - """Screener Overview. - - Parameters - ---------- - similar: - List of similar companies. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - data_type : str - Data type between: overview, valuation, financial, ownership, performance, technical - - Returns - ------- - pd.DataFrame - Dataframe with overview, valuation, financial, ownership, performance or technical - """ - if data_type == "overview": - screen = overview.Overview() - elif data_type == "valuation": - screen = valuation.Valuation() - elif data_type == "financial": - screen = financial.Financial() - elif data_type == "ownership": - screen = ownership.Ownership() - elif data_type == "performance": - screen = performance.Performance() - - elif data_type == "technical": - screen = technical.Technical() - else: - console.print("Invalid selected screener type") - return pd.DataFrame() - - screen.set_filter(ticker=",".join(similar)) - try: - screen_df = screen.screener_view(verbose=0) - screen_df.columns = screen_df.columns.str.strip() - screen_df = screen_df.rename( - columns={ - "Perf Week": "1W", - "Perf Month": "1M", - "Perf Quart": "3M", - "Perf Half": "6M", - "Perf Year": "1Y", - "Perf YTD": "YTD", - "Volatility W": "1W Volatility", - "Volatility M": "1M Volatility", - } - ) - return screen_df - except IndexError: - console.print("[red]Invalid data from website[red]\n") - return pd.DataFrame() - except AttributeError: - console.print("[red]Invalid data from website[red]\n") - return pd.DataFrame() diff --git a/openbb_terminal/stocks/comparison_analysis/finviz_compare_view.py b/openbb_terminal/stocks/comparison_analysis/finviz_compare_view.py deleted file mode 100644 index 5d31ab93f23..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/finviz_compare_view.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Finviz Comparison View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import List, Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.comparison_analysis import finviz_compare_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def screener( - similar: List[str], - data_type: str = "overview", - export: str = "", - sheet_name: Optional[str] = None, -): - """Screener. - - Parameters - ---------- - similar : List[str] - Similar companies to compare income with. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - data_type : str - Screener to use. One of {overview, valuation, financial, ownership, performance, technical} - export : str - Format to export data - """ - df_screen = finviz_compare_model.get_comparison_data( - similar=similar, data_type=data_type - ) - - if df_screen is None or df_screen.empty: - console.print("No screened data found.") - else: - print_rich_table( - df_screen, - headers=list(df_screen.columns), - show_index=False, - title="Stock Screener", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - data_type, - df_screen, - sheet_name, - ) diff --git a/openbb_terminal/stocks/comparison_analysis/marketwatch_model.py b/openbb_terminal/stocks/comparison_analysis/marketwatch_model.py deleted file mode 100644 index b649c6959c7..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/marketwatch_model.py +++ /dev/null @@ -1,398 +0,0 @@ -""" Comparison Analysis Marketwatch Model """ - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Dict, List, Tuple - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_financial_comparisons( - symbols: List[str], - data: str = "income", - timeframe: str = str(datetime.now().year - 1), - quarter: bool = False, -) -> pd.DataFrame: - """Get dataframe of income data from marketwatch. - - Parameters - ---------- - symbols : List[str] - List of tickers to compare. Enter tickers you want to see as shown below: - ["TSLA", "AAPL", "NFLX", "BBY"] - data : str - Data to get. Can be income, balance or cashflow - timeframe : str - What year/quarter to look at - quarter : bool - Flag to use quarterly data. - - Returns - ------- - pd.DataFrame - Dataframe of financial statements - - Raises - ------ - ValueError - Timeframe not valid - """ - l_timeframes, ddf_financials = prepare_comparison_financials(symbols, data, quarter) - - if timeframe and l_timeframes: - if (timeframe == str(datetime.now().year - 1)) and quarter: - timeframe = l_timeframes[-1] - elif timeframe not in l_timeframes: - raise ValueError( - f"Timeframe selected should be one of {', '.join(l_timeframes)}" - ) - s_timeframe = timeframe - else: - if len(l_timeframes) == 0: - return pd.DataFrame() - s_timeframe = l_timeframes[-1] - - console.print( - f"Other available {('yearly', 'quarterly')[quarter]} timeframes are:" - f" {', '.join(l_timeframes)}\n" - ) - - return combine_similar_financials(ddf_financials, symbols, s_timeframe, quarter) - - -@log_start_end(log=logger) -def get_income_comparison( - similar: List[str], - timeframe: str = str(datetime.today().year - 1), - quarter: bool = False, -) -> pd.DataFrame: - """Get income data. [Source: Marketwatch]. - - Parameters - ---------- - similar : List[str] - List of tickers to compare. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - timeframe : str - Column header to compare - quarter : bool, optional - Whether to use quarterly statements, by default False - export : str, optional - Format to export data - - Returns - ------- - pd.DataFrame - Dataframe of income statements - """ - df_financials_compared = get_financial_comparisons( - similar, "income", timeframe, quarter - ) - - return df_financials_compared - - -@log_start_end(log=logger) -def get_balance_comparison( - similar: List[str], - timeframe: str = str(datetime.today().year - 1), - quarter: bool = False, -) -> pd.DataFrame: - """Get balance data. [Source: Marketwatch]. - - Parameters - ---------- - similar : List[str] - List of tickers to compare. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - timeframe : str - Column header to compare - quarter : bool, optional - Whether to use quarterly statements, by default False - export : str, optional - Format to export data - - Returns - ------- - pd.DataFrame - Dataframe of balance comparisons - """ - df_financials_compared = get_financial_comparisons( - similar, "balance", timeframe, quarter - ) - - return df_financials_compared - - -@log_start_end(log=logger) -def get_cashflow_comparison( - similar: List[str], - timeframe: str = str(datetime.today().year - 1), - quarter: bool = False, -) -> pd.DataFrame: - """Get cashflow data. [Source: Marketwatch] - - Parameters - ---------- - similar : List[str] - List of tickers to compare. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - timeframe : str - Column header to compare - quarter : bool, optional - Whether to use quarterly statements, by default False - export : str, optional - Format to export data - - Returns - ------- - pd.DataFrame - Dataframe of cashflow comparisons - """ - df_financials_compared = get_financial_comparisons( - similar, "cashflow", timeframe, quarter - ) - - return df_financials_compared - - -@log_start_end(log=logger) -def prepare_df_financials( - ticker: str, statement: str, quarter: bool = False -) -> pd.DataFrame: - """Builds a DataFrame with financial statements for a given company. - - Parameters - ---------- - ticker: str - Company's stock ticker - statement: str - Financial statement to get. Can be income, balance or cashflow - quarter: bool, optional - Return quarterly financial statements instead of annual, by default False - - Returns - ------- - pd.DataFrame - A DataFrame with financial info - - Raises - ------ - ValueError - If statement is not income, balance or cashflow - """ - financial_urls = { - "income": { - "quarter": "https://www.marketwatch.com/investing/stock/{}/financials/income/quarter", - "annual": "https://www.marketwatch.com/investing/stock/{}/financials/income", - }, - "balance": { - "quarter": "https://www.marketwatch.com/investing/stock/{}/financials/balance-sheet/quarter", - "annual": "https://www.marketwatch.com/investing/stock/{}/financials/balance-sheet", - }, - "cashflow": { - "quarter": "https://www.marketwatch.com/investing/stock/{}/financials/cash-flow/quarter", - "annual": "https://www.marketwatch.com/investing/stock/{}/financials/cash-flow", - }, - } - - if statement not in financial_urls: - raise ValueError(f"type {statement} is not in {financial_urls.keys()}") - - try: - period = "quarter" if quarter else "annual" - text_soup_financials = BeautifulSoup( - request( - financial_urls[statement][period].format(ticker), - headers={"User-Agent": get_user_agent()}, - ).text, - "lxml", - ) - - # Define financials columns - a_financials_header = [ - financials_header.text.strip("\n").split("\n")[0] - for financials_header in text_soup_financials.findAll( - "th", {"class": "overflow__heading"} - ) - ] - - s_header_end_trend = ("5-year trend", "5- qtr trend")[quarter] - if s_header_end_trend in a_financials_header: - df_financials = pd.DataFrame( - columns=a_financials_header[ - 0 : a_financials_header.index(s_header_end_trend) - ] - ) - else: - # We don't have the data we need for whatever reason, so return an empty DataFrame - return pd.DataFrame() - - find_table = text_soup_financials.findAll( - "div", {"class": "element element--table table--fixed financials"} - ) - - if not find_table: - return df_financials - - financials_rows = find_table[0].findAll( - "tr", {"class": ["table__row is-highlighted", "table__row"]} - ) - - for a_row in financials_rows: - constructed_row = [] - financial_columns = a_row.findAll( - "td", {"class": ["overflow__cell", "overflow__cell fixed--column"]} - ) - - if not financial_columns: - continue - - for a_column in financial_columns: - column_to_text = a_column.text.strip() - if "\n" in column_to_text: - column_to_text = column_to_text.split("\n")[0] - - if column_to_text == "": - continue - constructed_row.append(column_to_text) - - df_financials.loc[len(df_financials)] = constructed_row - except Exception: - df_financials = pd.DataFrame() - - return df_financials - - -@log_start_end(log=logger) -def prepare_comparison_financials( - similar: List[str], statement: str, quarter: bool = False -) -> Tuple[List[str], Dict[str, pd.DataFrame]]: - """Builds a dictionary of DataFrame with financial statements for list of tickers - - Parameters - ---------- - similar : List[str] - List of similar stock tickers - statement : str - Financial statement to get. Can be income, balance or cashflow - quarter : bool - Return quarterly financial statements instead of annual, by default False - - Returns - ------- - Tuple[List[str], Dict[str, pd.DataFrame]] - List of index headers, - A dictionary of DataFrame with financial info from list of similar tickers - """ - - if not similar: - console.print("[red]No similar tickers found.") - return [], {} - - financials = {} - # We need a copy since we are modifying the original potentially - for symbol in similar.copy(): - results = prepare_df_financials(symbol, statement, quarter) - if results.empty: - # If we have an empty result set, don't do further analysis on this symbol and remove it from consideration - console.print( - "Didn't get data for ticker " - + symbol - + ". Removing from further processing." - ) - similar.remove(symbol) - continue - financials[symbol] = results.set_index("Item") - - if quarter: - items = financials[similar[0]].columns - - else: - items = [] - # Get common headers between tickers - for symbol in similar: - if len(financials[symbol].columns) > len(items): - items = financials[symbol].columns - - # Add columns with N/A when data is not available, to have similar columns - for symbol in similar: - if len(financials[symbol].columns) < len(items): - for item in items: - if item not in financials[symbol].columns: - financials[symbol][item] = "N/A" - financials[symbol] = financials[symbol].reindex(sorted(items), axis=1) - - return list(items), financials - - -@log_start_end(log=logger) -def combine_similar_financials( - datasets: Dict[str, pd.DataFrame], - similar: List[str], - timeframe: str, - quarter: bool = False, -) -> pd.DataFrame: - """Builds a DataFrame with financial statements from a certain timeframe of a list of tickers - - Parameters - ---------- - datasets: Dict[str, pd.DataFrame] - A dictionary of DataFrame with financial info from list of similar tickers - similar: List[str] - List of similar stock tickers - timeframe: str - Column label, which is a timeframe - quarter: bool - False for yearly data, True for quarterly - - Returns - ------- - pd.DataFrame - A DataFrame with financial statements from a certain timeframe of a list of tickers - """ - - # Quarter data is a bit more confusing for comparison due to the fact that - # the reports may occur at slight different moments. Hence we rely on the - # order set by the Market Watch website - - if quarter: - compare_financials = datasets[similar[0]][timeframe].to_frame() - compare_financials.rename(columns={timeframe: similar[0]}, inplace=True) - earnings_dates = [timeframe] - idx = len(datasets[similar[0]].columns) - list( - datasets[similar[0]].columns - ).index(timeframe) - - for symbol in similar: - report_quarter_date = list(datasets[symbol].columns)[-idx] - earnings_dates.append(report_quarter_date) - compare_financials[symbol + " (" + report_quarter_date + ")"] = datasets[ - symbol - ][report_quarter_date] - - compare_financials.drop( - columns=compare_financials.columns[0], axis=1, inplace=True - ) - - else: - compare_financials = datasets[similar[0]][timeframe].to_frame() - compare_financials.rename(columns={timeframe: similar[0]}, inplace=True) - - for symbol in similar[1:]: - compare_financials[symbol] = datasets[symbol][timeframe] - - return compare_financials.fillna("-").replace("-", "") diff --git a/openbb_terminal/stocks/comparison_analysis/marketwatch_view.py b/openbb_terminal/stocks/comparison_analysis/marketwatch_view.py deleted file mode 100644 index e07ebc5832d..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/marketwatch_view.py +++ /dev/null @@ -1,200 +0,0 @@ -""" Comparison Analysis Marketwatch View """ - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import List, Optional - -from openbb_terminal import rich_config -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_financials_colored_values, - patch_pandas_text_adjustment, - print_rich_table, -) -from openbb_terminal.stocks.comparison_analysis import marketwatch_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_income_comparison( - symbols: List[str], - timeframe: str = str(datetime.now().year - 1), - quarter: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display income data. [Source: Marketwatch]. - - Parameters - ---------- - symbols : List[str] - List of tickers to compare. Enter tickers you want to see as shown below: - ["TSLA", "AAPL", "NFLX", "BBY"] - You can also get a list of comparable peers with - finnhub_peers(), finviz_peers(), polygon_peers(). - timeframe : str - What year to look at - quarter : bool, optional - Whether to use quarterly statements, by default False - export : str, optional - Format to export data - """ - df_financials_compared = marketwatch_model.get_income_comparison( - symbols, timeframe, quarter - ) - - if len(df_financials_compared) == 0 or df_financials_compared.empty: - return - - # Export data before the color - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "income", - df_financials_compared, - sheet_name, - ) - - if rich_config.USE_COLOR and not get_current_user().preferences.USE_INTERACTIVE_DF: - df_financials_compared = df_financials_compared.applymap( - lambda_financials_colored_values - ) - patch_pandas_text_adjustment() - - if not quarter: - df_financials_compared.index.name = timeframe - - print_rich_table( - df_financials_compared, - headers=list(df_financials_compared.columns), - show_index=True, - title="Income Data", - export=bool(export), - ) - - -@log_start_end(log=logger) -def display_balance_comparison( - symbols: List[str], - timeframe: str = str(datetime.now().year - 1), - quarter: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Compare balance between companies. [Source: Marketwatch] - - Parameters - ---------- - symbols : List[str] - List of tickers to compare. Enter tickers you want to see as shown below: - ["TSLA", "AAPL", "NFLX", "BBY"] - You can also get a list of comparable peers with - finnhub_peers(), finviz_peers(), polygon_peers(). - timeframe : str - What year to look at - quarter : bool, optional - Whether to use quarterly statements, by default False - export : str, optional - Format to export data - """ - df_financials_compared = marketwatch_model.get_financial_comparisons( - symbols, "balance", timeframe, quarter - ) - - if len(df_financials_compared) == 0 or df_financials_compared.empty: - return - - # Export data before the color - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "balance", - df_financials_compared, - sheet_name, - ) - - if rich_config.USE_COLOR and not get_current_user().preferences.USE_INTERACTIVE_DF: - df_financials_compared = df_financials_compared.applymap( - lambda_financials_colored_values - ) - patch_pandas_text_adjustment() - - if not quarter: - df_financials_compared.index.name = timeframe - - print_rich_table( - df_financials_compared, - headers=list(df_financials_compared.columns), - show_index=True, - title="Company Comparison", - export=bool(export), - ) - - -@log_start_end(log=logger) -def display_cashflow_comparison( - symbols: List[str], - timeframe: str = str(datetime.now().year - 1), - quarter: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Compare cashflow between companies. [Source: Marketwatch] - - Parameters - ---------- - symbols : List[str] - List of tickers to compare. Enter tickers you want to see as shown below: - ["TSLA", "AAPL", "NFLX", "BBY"] - You can also get a list of comparable peers with - finnhub_peers(), finviz_peers(), polygon_peers(). - timeframe : str - What year/quarter to look at - quarter : bool, optional - Whether to use quarterly statements, by default False - export : str, optional - Format to export data - """ - df_financials_compared = marketwatch_model.get_financial_comparisons( - symbols, "cashflow", timeframe, quarter - ) - - if len(df_financials_compared) == 0 or df_financials_compared.empty: - return - - # Export data before the color - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cashflow", - df_financials_compared, - sheet_name, - ) - - if rich_config.USE_COLOR and not get_current_user().preferences.USE_INTERACTIVE_DF: - df_financials_compared = df_financials_compared.applymap( - lambda_financials_colored_values - ) - patch_pandas_text_adjustment() - - if not quarter: - df_financials_compared.index.name = timeframe - - if any(isinstance(col, tuple) for col in df_financials_compared.columns): - df_financials_compared.columns = [ - " ".join(col) for col in df_financials_compared.columns - ] - - print_rich_table( - df_financials_compared, - headers=list(df_financials_compared.columns), - show_index=True, - title="Cashflow Comparison", - export=bool(export), - ) diff --git a/openbb_terminal/stocks/comparison_analysis/polygon_model.py b/openbb_terminal/stocks/comparison_analysis/polygon_model.py deleted file mode 100644 index ab6fe4c0017..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/polygon_model.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Polygon Model""" - -__docformat__ = "numpy" - -import logging -from typing import List - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_POLYGON_KEY"]) -def get_similar_companies(symbol: str, us_only: bool = False) -> List[str]: - """Get similar companies from Polygon - - Parameters - ---------- - symbol: str - Ticker to get similar companies of - us_only: bool - Only stocks from the US stock exchanges - - Returns - ------- - List[str]: - List of similar tickers - """ - result = request( - f"https://api.polygon.io/v1/meta/symbols/{symbol.upper()}/company?&apiKey={get_current_user().credentials.API_POLYGON_KEY}" - ) - - similar = [] - - if result.status_code == 200: - similar = result.json()["similar"] - if us_only: - us_similar = [] - mkw_link = "https://www.marketwatch.com/investing/stock/" - for sym in similar: - prep_link = mkw_link + sym - sent_req = request(prep_link) - if prep_link == sent_req.request.url: - us_similar.append(sym) - similar = us_similar - elif result.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - elif result.status_code == 403: - console.print("[red]API Key not authorized for Premium Feature[/red]\n") - else: - console.print(result.json()["error"]) - - return similar diff --git a/openbb_terminal/stocks/comparison_analysis/sdk_helpers.py b/openbb_terminal/stocks/comparison_analysis/sdk_helpers.py deleted file mode 100644 index 4fb2a59ef96..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/sdk_helpers.py +++ /dev/null @@ -1,47 +0,0 @@ -"""CA SDK Helpers.""" - -__docformat__ = "numpy" - -from typing import List - -from openbb_terminal.stocks.comparison_analysis import ( - finnhub_model, - polygon_model, - yahoo_finance_model, -) - - -def get_similar(symbol: str, source="Finviz") -> List[str]: - """Find similar tickers to a given symbol. - - Parameters - ---------- - symbol : str - Symbol to find similar tickers to. - source : str, optional - Source to get data for, by default "Finviz". Can be "Finviz", "Polygon", "Finnhub", or "TSNE" - - Returns - ------- - List[str] - List of similar tickers. - - Examples - -------- - To get similar tickers to AAPL from Finviz: - >>> from openbb_terminal.sdk import openbb - >>> similar_tickers = openbb.stocks.ca.similar("AAPL") - - To use our custom TSNE model for similar tickers in the SP500: - >>> similar_tickers = openbb.stocks.ca.similar("AAPL", source="TSNE") - """ - if source.lower() == "polygon": - return polygon_model.get_similar_companies(symbol) - - if source.lower() == "finnhub": - return finnhub_model.get_similar_companies(symbol) - - if source.lower() == "tsne": - return yahoo_finance_model.get_sp500_comps_tsne(symbol).index.to_list()[:10] - - return [] diff --git a/openbb_terminal/stocks/comparison_analysis/yahoo_finance_model.py b/openbb_terminal/stocks/comparison_analysis/yahoo_finance_model.py deleted file mode 100644 index e9400ec847d..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/yahoo_finance_model.py +++ /dev/null @@ -1,264 +0,0 @@ -"""Yahoo Finance Comparison Model""" - -__docformat__ = "numpy" - -import logging -import warnings -from datetime import datetime, timedelta -from typing import List, Optional, Tuple - -import numpy as np -import pandas as pd -import yfinance as yf -from sklearn.manifold import TSNE -from sklearn.preprocessing import normalize - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -d_candle_types = { - "o": "Open", - "h": "High", - "l": "Low", - "c": "Close", - "a": "Adj Close", - "v": "Volume", -} - - -@log_start_end(log=logger) -def get_historical( - similar: List[str], - start_date: Optional[str] = None, - end_date: Optional[str] = None, - candle_type: str = "a", -) -> pd.DataFrame: - """Get historical prices for all comparison stocks - - Parameters - ---------- - similar: List[str] - List of similar tickers. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - start_date: Optional[str], optional - Initial date (e.g., 2021-10-01). Defaults to 1 year back - end_date: Optional[str], optional - End date (e.g., 2023-01-01). None defaults to today - candle_type: str, optional - Candle variable to compare, by default "a" for Adjusted Close. Possible values are: o, h, l, c, a, v, r - - Returns - ------- - pd.DataFrame - Dataframe of historical prices for all comparison stocks - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - - Start by getting similar tickers from finviz for AAPL - - >>> similar = openbb.stocks.comparison_analysis.finviz_peers("AAPL") - >>> hist_df = openbb.stocks.ca.hist(similar) - - We can specify a start date and an end date - >>> hist_df_2022 = openbb.stocks.ca.hist(similar, start_date="2022-01-01", end_date="2022-12-31") - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d") - - candle_type = candle_type.lower() - use_returns = False - if candle_type == "r": - # Calculate returns based off of adjusted close - use_returns = True - candle_type = "a" - - # To avoid having to recursively append, just do a single yfinance call. This will give dataframe - # where all tickers are columns. - similar_tickers_dataframe = yf.download( - similar, start=start_date, progress=False, threads=False, ignore_tz=True - )[d_candle_types[candle_type]] - - returnable = ( - similar_tickers_dataframe - if similar_tickers_dataframe.empty - else similar_tickers_dataframe[similar] - ) - - if use_returns: - # To calculate the period to period return, - # shift the dataframe by one row, then divide it into - # the other, then subtract 1 to get a percentage, which is the return. - shifted = returnable.shift(1)[1:] - returnable = returnable.div(shifted) - 1 - - df_similar = returnable[similar] - - if np.any(df_similar.isna()): - nan_tickers = df_similar.columns[df_similar.isna().sum() >= 1].to_list() - console.print( - f"NaN values found in: {', '.join(nan_tickers)}. Backfilling data" - ) - df_similar = df_similar.fillna(method="bfill") - - df_similar = df_similar.dropna(axis=1, how="all") - - if end_date: - df_similar = df_similar[df_similar.index <= end_date] - return df_similar - - -@log_start_end(log=logger) -def get_correlation( - similar: List[str], - start_date: Optional[str] = None, - end_date: Optional[str] = None, - candle_type: str = "a", -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """ - Get historical price correlation. [Source: Yahoo Finance] - - Parameters - ---------- - similar : List[str] - List of similar tickers. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - start_date : Optional[str], optional - Initial date (e.g., 2021-10-01). Defaults to 1 year back - end : Optional[str], optional - Initial date (e.g., 2021-10-01). Defaults to today - candle_type : str, optional - OHLCA column to use for candles or R for returns, by default "a" for Adjusted Close - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame] - Dataframe with correlation matrix, Dataframe with historical prices for all comparison stocks - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d") - - df_similar = get_historical(similar, start_date, end_date, candle_type=candle_type) - - correlations = df_similar.corr() - - return correlations, df_similar - - -@log_start_end(log=logger) -def get_volume( - similar: List[str], - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get stock volume. [Source: Yahoo Finance] - - Parameters - ---------- - similar : List[str] - List of similar tickers. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - start_date : Optional[str], optional - Initial date (e.g., 2021-10-01). Defaults to 1 year back - end_date : Optional[str], optional - End date (e.g., 2023-01-01). None defaults to today - - Returns - ------- - pd.DataFrame - Dataframe with volume for stock - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d") - - df_similar = get_historical(similar, start_date, end_date, candle_type="v") - df_similar = df_similar[df_similar.columns] - return df_similar - - -@log_start_end(log=logger) -def get_1y_sp500() -> pd.DataFrame: - """ - Gets the last year of Adj Close prices for all current SP 500 stocks. - They are scraped daily using yfinance at https://github.com/jmaslek/daily_sp_500 - - Returns - ------- - pd.DataFrame - DataFrame containing last 1 year of closes for all SP500 stocks. - """ - df = pd.read_csv( - "https://raw.githubusercontent.com/jmaslek/daily_sp_500/main/SP500_prices_1yr.csv", - index_col=0, - ) - df.reset_index(inplace=True) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d", utc=True).dt.strftime( - "%Y-%m-%d" - ) - df.set_index("Date", inplace=True) - return df - - -# pylint:disable=E1137,E1101 - - -@log_start_end(log=logger) -def get_sp500_comps_tsne( - symbol: str, - lr: int = 200, -) -> pd.DataFrame: - """ - Runs TSNE on SP500 tickers (along with ticker if not in SP500). - TSNE is a method of visualing higher dimensional data - https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html - Note that the TSNE numbers are meaningless and will be arbitrary if run again. - - Parameters - ---------- - symbol: str - Ticker to get comparisons to - lr: int - Learning rate for TSNE - - Returns - ------- - pd.DataFrame - Dataframe of tickers closest to selected ticker - """ - - # Adding the type makes pylint stop yelling - close_vals: pd.DataFrame = get_1y_sp500() - - if symbol not in close_vals.columns: - df_symbol = yf.download( - symbol, start=close_vals.index[0], progress=False, ignore_tz=True - )["Adj Close"].to_frame() - df_symbol.columns = [symbol] - df_symbol.index = df_symbol.index.astype(str) - close_vals = close_vals.join(df_symbol) - - close_vals = close_vals.fillna(method="bfill") - rets = close_vals.pct_change()[1:].T - # Future warning from sklearn. Think 1.2 will stop printing it - warnings.filterwarnings("ignore", category=FutureWarning) - model = TSNE(learning_rate=lr, init="pca") - tsne_features = model.fit_transform(normalize(rets)) - warnings.resetwarnings() - xs = tsne_features[:, 0] - ys = tsne_features[:, 1] - - data = pd.DataFrame({"X": xs, "Y": ys}, index=rets.index) - x0, y0 = data.loc[symbol] - data["dist"] = (data.X - x0) ** 2 + (data.Y - y0) ** 2 - data = data.sort_values(by="dist") - - return data diff --git a/openbb_terminal/stocks/comparison_analysis/yahoo_finance_view.py b/openbb_terminal/stocks/comparison_analysis/yahoo_finance_view.py deleted file mode 100644 index 5625afd91f4..00000000000 --- a/openbb_terminal/stocks/comparison_analysis/yahoo_finance_view.py +++ /dev/null @@ -1,341 +0,0 @@ -""" Comparison Analysis Yahoo Finance View """ - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime, timedelta -from typing import List, Optional, Tuple, Union - -import numpy as np -import pandas as pd -from sklearn.preprocessing import MinMaxScaler - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.comparison_analysis import yahoo_finance_model - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -d_candle_types = { - "o": "Open", - "h": "High", - "l": "Low", - "c": "Close", - "a": "Adj Close", - "v": "Volume", - "r": "Returns", -} - - -@log_start_end(log=logger) -def display_historical( - similar: List[str], - start_date: Optional[str] = None, - end_date: Optional[str] = None, - candle_type: str = "a", - normalize: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display historical stock prices. [Source: Yahoo Finance] - - Parameters - ---------- - similar: List[str] - List of similar tickers. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - start_date: Optional[str], optional - Initial date (e.g., 2021-10-01). Defaults to 1 year back - end_date: Optional[str], optional - End date (e.g., 2023-01-01) - candle_type: str, optional - OHLCA column to use or R to use daily returns calculated from Adjusted Close, by default "a" for Adjusted Close - normalize: bool, optional - Boolean to normalize all stock prices using MinMax defaults True - export: str, optional - Format to export historical prices, by default "" - external_axes: bool, optional - Whether to return the figure object or not, by default False - """ - df_similar = yahoo_finance_model.get_historical( - similar, start_date, end_date, candle_type - ) - - # This puts everything on 0-1 scale for visualizing - if normalize: - mm_scale = MinMaxScaler() - df_similar = pd.DataFrame( - mm_scale.fit_transform(df_similar), - columns=df_similar.columns, - index=df_similar.index, - ) - - fig = OpenBBFigure( - yaxis_title=f"{['','Normalized'][normalize]} Share Price {['($)',''][normalize]}" - ) - fig.set_title("Historical price of similar companies") - - for ticker in df_similar.columns: - fig.add_scatter( - x=df_similar.index, - y=df_similar[ticker], - name=ticker, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "historical", - df_similar, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_volume( - similar: List[str], - start_date: Optional[str] = None, - end_date: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display stock volume. [Source: Yahoo Finance] - - Parameters - ---------- - similar : List[str] - List of similar tickers. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - start_date : Optional[str], optional - Initial date (e.g., 2021-10-01). Defaults to 1 year back - end_date : Optional[str], optional - End date (e.g., 2023-01-01). Defaults to today - export : str, optional - Format to export historical prices, by default "" - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_similar = yahoo_finance_model.get_volume(similar, start_date, end_date) - - fig = OpenBBFigure(yaxis_title="Volume").set_title( - "Historical volume of similar companies" - ) - for ticker in df_similar.columns: - fig.add_scatter( - x=df_similar.index, - y=df_similar[ticker], - name=ticker, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "volume", - df_similar, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_correlation( - similar: List[str], - start_date: Optional[str] = None, - end_date: Optional[str] = None, - candle_type: str = "a", - display_full_matrix: bool = False, - raw: bool = False, - external_axes: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> Union[OpenBBFigure, None]: - """ - Correlation heatmap based on historical price comparison - between similar companies. [Source: Yahoo Finance] - - Parameters - ---------- - similar : List[str] - List of similar tickers. - Comparable companies can be accessed through - finnhub_peers(), finviz_peers(), polygon_peers(). - start_date : Optional[str], optional - Initial date (e.g., 2021-10-01). Defaults to 1 year back - end_date : Optional[str], optional - End date (e.g., 2023-01-01) - candle_type : str, optional - OHLCA column to use for candles or R for returns, by default "a" for Adjusted Close - display_full_matrix : bool, optional - Optionally display all values in the matrix, rather than masking off half, by default False - raw: bool, optional - Whether to display raw data - external_axes : bool, optional - Whether to return the figure object or not, by default False - export : str, optional - Format to export correlation prices, by default "" - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d") - - correlations, df_similar = yahoo_finance_model.get_correlation( - similar, start_date, end_date, candle_type - ) - - df_corr = correlations - if not display_full_matrix: - mask = np.zeros((df_similar.shape[1], df_similar.shape[1]), dtype=bool) - mask[np.triu_indices(len(mask))] = True - df_corr = correlations.mask(mask) - - df_corr.fillna("", inplace=True) - - fig = OpenBBFigure() - fig.set_title(f"Correlation Heatmap of similar companies from {start_date}") - fig.add_heatmap( - x=df_corr.columns, - y=df_corr.index, - z=df_corr.values.tolist(), - zmin=-1, - zmax=1, - hoverongaps=False, - showscale=True, - colorscale="RdYlGn", - text=df_corr.to_numpy(), - textfont=dict(color="black"), - texttemplate="%{text:.2f}", - colorbar=dict( - thickness=10, - thicknessmode="pixels", - x=1.2, - y=1, - xanchor="right", - yanchor="top", - xpad=10, - ), - xgap=1, - ygap=1, - ) - fig.update_layout( - margin=dict(l=0, r=120, t=0, b=0), yaxis=dict(autorange="reversed") - ) - - if raw: - print_rich_table( - correlations, - headers=[x.title().upper() for x in correlations.columns], - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hcorr", - df_similar, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_sp500_comps_tsne( - symbol: str, - lr: int = 200, - no_plot: bool = False, - limit: int = 10, - external_axes: bool = False, -) -> Union[List[str], Tuple[List[str], Optional[OpenBBFigure]]]: - """Runs TSNE on SP500 tickers (along with ticker if not in SP500). - TSNE is a method of visualing higher dimensional data - https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html - Note that the TSNE numbers are meaningless and will be arbitrary if run again. - - Parameters - ---------- - symbol: str - Ticker to get comparisons to - lr: int - Learning rate for TSNE - no_plot: bool - Flag to hold off on plotting - limit: int - Number of tickers to return - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Returns - ------- - List[str] - List of the 10 closest stocks due to TSNE - """ - data = yahoo_finance_model.get_sp500_comps_tsne(symbol=symbol, lr=lr) - - top_n = data.iloc[1 : (limit + 1)] - top_n_name = top_n.index.to_list() - - if not no_plot: - fig = OpenBBFigure(xaxis_title="Dimension 1", yaxis_title="Dimension 2") - fig.set_title( - f"Top 100 closest stocks on S&P500 to {symbol} using TSNE algorithm" - ) - - top_100 = data[(limit + 1) : 101] - symbol_df = data[data.index == symbol] - - fig.add_scatter( - x=top_n.X, - y=top_n.Y, - text=top_n.index, - textfont=dict(color=theme.up_color), - textposition="top center", - texttemplate="%{text}", - mode="markers+text", - marker=dict(size=10, color=theme.up_color), - name=f"Top {limit} closest tickers", - ) - fig.add_scatter( - x=top_100.X, - y=top_100.Y, - text=top_100.index, - textfont=dict(color="grey"), - textposition="top center", - texttemplate="%{text}", - mode="markers+text", - marker=dict(size=10, color="grey"), - name="Top 100 closest tickers", - ) - - fig.add_scatter( - x=symbol_df.X, - y=symbol_df.Y, - mode="markers+text", - name=symbol, - text=symbol_df.index, - textfont=dict(color=theme.down_color), - textposition="top center", - texttemplate="%{text}", - marker=dict(size=12, color=theme.down_color), - ) - if external_axes: - return top_n_name, fig.show(external=external_axes) - - fig.show() - - return top_n_name diff --git a/openbb_terminal/stocks/dark_pool_shorts/__init__.py b/openbb_terminal/stocks/dark_pool_shorts/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/dark_pool_shorts/dps_controller.py b/openbb_terminal/stocks/dark_pool_shorts/dps_controller.py deleted file mode 100644 index 156049117f0..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/dps_controller.py +++ /dev/null @@ -1,553 +0,0 @@ -""" Dark Pool and Shorts Controller Module """ - -__docformat__ = "numpy" - -import argparse -import logging -from datetime import datetime, timedelta -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_int_range, - check_positive, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks.dark_pool_shorts import ( - finra_view, - quandl_view, - sec_view, - shortinterest_view, - stockgrid_view, - yahoofinance_view, -) - -logger = logging.getLogger(__name__) - - -class DarkPoolShortsController(StockBaseController): - """Dark Pool Shorts Controller class""" - - CHOICES_COMMANDS = [ - "load", - "shorted", - "hsi", - "prom", - "pos", - "sidtc", - "psi", - "dpotc", - "ftd", - "spos", - # "volexch", - ] - POS_CHOICES = ["sv", "sv_pct", "nsv", "nsv_dollar", "dpp", "dpp_dollar"] - PATH = "/stocks/dps/" - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - start: str, - stock: pd.DataFrame, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - self.start = start - self.stock = stock - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - return ["stocks", f"load {self.ticker}", "dps"] - return [] - - def print_help(self): - """Print help""" - mt = MenuText("stocks/dps/") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_cmd("shorted") - mt.add_cmd("hsi") - mt.add_cmd("prom") - mt.add_cmd("pos") - mt.add_cmd("sidtc") - mt.add_raw("\n") - mt.add_param("_ticker", self.ticker or "") - mt.add_raw("\n") - mt.add_cmd("dpotc", self.ticker) - mt.add_cmd("ftd", self.ticker) - mt.add_cmd("spos", self.ticker) - mt.add_cmd("psi", self.ticker) - console.print(text=mt.menu_text, menu="Stocks - Dark Pool and Short data") - - @log_start_end(log=logger) - def call_shorted(self, other_args: List[str]): - """Process shorted command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="shorted", - description="Print up to 25 top ticker most shorted. [Source: Yahoo Finance]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=10, - help="Limit of the most shorted stocks to retrieve.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yahoofinance_view.display_most_shorted( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_hsi(self, other_args: List[str]): - """Process hsi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hsi", - description=""" - Print top stocks being more heavily shorted. HighShortInterest.com provides - a convenient sorted database of stocks which have a short interest of over - 20 percent. Additional key data such as the float, number of outstanding shares, - and company industry is displayed. Data is presented for the Nasdaq Stock Market, - the New York Stock Exchange, and the American Stock Exchange. [Source: www.highshortinterest.com] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=10, - help="Limit of the top heavily shorted stocks to retrieve.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - shortinterest_view.high_short_interest( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_prom(self, other_args: List[str]): - """Process prom command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="prom", - description="Display dark pool (ATS) data of tickers with growing trades activity using linear regression.", - ) - parser.add_argument( - "-n", - "--num", - action="store", - dest="n_num", - type=check_positive, - default=1_000, - help="Number of tickers to filter from entire ATS data based on the sum of the total weekly shares quantity.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of most promising tickers to display.", - ) - parser.add_argument( - "-t", - "--tier", - action="store", - dest="tier", - type=str, - choices=["T1", "T2", "OTCE"], - default="", - help="Tier to process data from.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - finra_view.darkpool_otc( - input_limit=ns_parser.n_num, - limit=ns_parser.limit, - tier=ns_parser.tier, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_pos(self, other_args: List[str]): - """Process pos command""" - parser = argparse.ArgumentParser( - prog="pos", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Get dark pool short positions. [Source: Stockgrid]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of tickers to display.", - ) - parser.add_argument( - "-s", - "--sort", - help="Field for which to sort by, where 'sv': Short Vol. [1M], " - "'sv_pct': Short Vol. %%, 'nsv': Net Short Vol. [1M], " - "'nsv_dollar': Net Short Vol. ($100M), 'dpp': DP Position [1M], " - "'dpp_dollar': DP Position ($1B)", - choices=self.POS_CHOICES, - default="dpp_dollar", - dest="sort_field", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - stockgrid_view.dark_pool_short_positions( - limit=ns_parser.limit, - sortby=ns_parser.sort_field, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_sidtc(self, other_args: List[str]): - """Process sidtc command""" - parser = argparse.ArgumentParser( - prog="sidtc", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Print short interest and days to cover. [Source: Stockgrid]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of tickers to display.", - ) - parser.add_argument( - "-s", - "--sort", - help="Field for which to sort by, where 'float': Float Short %%, " - "'dtc': Days to Cover, 'si': Short Interest", - choices=["float", "dtc", "si"], - default="float", - dest="sort_field", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - stockgrid_view.short_interest_days_to_cover( - limit=ns_parser.limit, - sortby=ns_parser.sort_field, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_dpotc(self, other_args: List[str]): - """Process dpotc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dpotc", - description="Display barchart of dark pool (ATS) and OTC (Non ATS) data. [Source: FINRA]", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.ticker: - finra_view.darkpool_ats_otc( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded.\n") - - @log_start_end(log=logger) - def call_ftd(self, other_args: List[str]): - """Process ftd command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="ftd", - description="""Prints latest fails-to-deliver data. [Source: SEC]""", - ) - parser.add_argument( - "-s", - "--start", - action="store", - dest="start", - type=valid_date, - default=(datetime.now() - timedelta(days=60)).strftime("%Y-%m-%d"), - help="start of datetime to see FTD", - ) - parser.add_argument( - "-e", - "--end", - action="store", - dest="end", - type=valid_date, - default=datetime.now().strftime("%Y-%m-%d"), - help="end of datetime to see FTD", - ) - parser.add_argument( - "-n", - "--num", - action="store", - dest="n_num", - type=check_positive, - default=0, - help="number of latest fails-to-deliver being printed", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.ticker: - sec_view.fails_to_deliver( - symbol=self.ticker, - data=self.stock, - start_date=ns_parser.start.strftime("%Y-%m-%d"), - end_date=ns_parser.end.strftime("%Y-%m-%d"), - limit=ns_parser.n_num, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded.\n") - - @log_start_end(log=logger) - def call_spos(self, other_args: List[str]): - """Process spos command""" - parser = argparse.ArgumentParser( - prog="spos", - add_help=False, - description="Shows Net Short Vol. vs Position. [Source: Stockgrid]", - ) - parser.add_argument( - "-n", - "--number", - help="Number of last open market days to show", - type=check_positive, - default=10 if "-r" in other_args else 120, - dest="num", - ) - parser.add_argument( - "-r", - "--raw", - action="store_true", - default=False, - help="Flag to print raw data instead", - dest="raw", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.ticker: - stockgrid_view.net_short_position( - symbol=self.ticker, - limit=ns_parser.num, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded.\n") - - @log_start_end(log=logger) - def call_psi(self, other_args: List[str]): - """Process psi command""" - parser = argparse.ArgumentParser( - prog="psi", - add_help=False, - description="Shows price vs short interest volume. [Source: Quandl/Stockgrid]", - ) - parser.add_argument( - "--nyse", - action="store_true", - default=False, - dest="b_nyse", - help="Data from NYSE flag. Otherwise comes from NASDAQ. Only works for Quandl.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10 if "-r" in other_args else 120, - ) - if ns_parser: - if self.ticker: - if ns_parser.source == "Quandl": - quandl_view.short_interest( - symbol=self.ticker, - nyse=ns_parser.b_nyse, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - stockgrid_view.short_interest_volume( - symbol=self.ticker, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - console.print("No ticker loaded.\n") - - # TODO: Load back in once data is properly stored - # @log_start_end(log=logger) - # def call_volexch(self, other_args: List[str]): - # """Process volexch command""" - # parser = argparse.ArgumentParser( - # prog="volexch", - # add_help=False, - # description="Displays short volume based on exchange.", - # ) - # parser.add_argument( - # "-r", - # "--raw", - # help="Display raw data", - # dest="raw", - # action="store_true", - # default=False, - # ) - # parser.add_argument( - # "-s", - # "--sort", - # help="Column to sort by", - # dest="sort", - # type=str, - # default="", - # choices=["", "NetShort", "Date", "TotalVolume", "PctShort"], - # ) - # parser.add_argument( - # "-a", - # "--asc", - # help="Sort in ascending order", - # dest="asc", - # action="store_true", - # default=False, - # ) - # parser.add_argument( - # "-p", - # "--plotly", - # help="Display plot using interactive plotly.", - # dest="plotly", - # action="store_false", - # default=True, - # ) - # ns_parser = self.parse_known_args_and_warn( - # parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - # ) - # if ns_parser: - # if self.ticker: - # nyse_view.display_short_by_exchange( - # ticker=self.ticker, - # raw=ns_parser.raw, - # sort=ns_parser.sort, - # asc=ns_parser.asc, - # mpl=ns_parser.plotly, - # export=ns_parser.export, - # sheet_name= " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None, - # ) - # else: - # console.print("No ticker loaded. Use `load ticker` first.") diff --git a/openbb_terminal/stocks/dark_pool_shorts/finra_model.py b/openbb_terminal/stocks/dark_pool_shorts/finra_model.py deleted file mode 100644 index 2f365bfcf0e..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/finra_model.py +++ /dev/null @@ -1,347 +0,0 @@ -""" FINRA Model """ - -__docformat__ = "numpy" - -import logging -from typing import Dict, List, Tuple - -import pandas as pd -import requests -from scipy import stats - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def getFINRAweeks(tier: str = "T1", is_ats: bool = True) -> List: - """Get FINRA weeks. [Source: FINRA] - - Parameters - ---------- - tier : str - Stock tier between T1, T2, or OTCE - is_ats : bool - ATS data if true, NON-ATS otherwise - - Returns - ------- - List - List of response data - """ - req_hdr = {"Accept": "application/json", "Content-Type": "application/json"} - - req_data = { - "compareFilters": [ - { - "compareType": "EQUAL", - "fieldName": "summaryTypeCode", - "fieldValue": "ATS_W_SMBL" if is_ats else "OTC_W_SMBL", - }, - { - "compareType": "EQUAL", - "fieldName": "tierIdentifier", - "fieldValue": tier, - }, - ], - "delimiter": "|", - "fields": ["weekStartDate"], - "limit": 27, - "quoteValues": False, - "sortFields": ["-weekStartDate"], - } - - response = request( - "https://api.finra.org/data/group/otcMarket/name/weeklyDownloadDetails", - method="POST", - headers=req_hdr, - json=req_data, - ) - - return response.json() if response.status_code == 200 else list() - - -def getFINRAdata_offset( - start_date: str, - tier: str = "T1", - symbol: str = "", - is_ats: bool = True, - offset: int = 0, -) -> requests.Response: - """Get FINRA data. [Source: FINRA] - - Parameters - ---------- - start_date: str - Weekly data to get FINRA data, in YYYY-MM-DD format - tier: str - Stock tier between T1, T2, or OTCE - symbol: str - Stock ticker to get data from - is_ats: bool - ATS data if true, NON-ATS otherwise - offset: int - Offset in getting the data - - Returns - ------- - requests.Response - Response from FINRA data - """ - req_hdr = {"Accept": "application/json", "Content-Type": "application/json"} - - l_cmp_filters = [ - { - "compareType": "EQUAL", - "fieldName": "weekStartDate", - "fieldValue": start_date, - }, - {"compareType": "EQUAL", "fieldName": "tierIdentifier", "fieldValue": tier}, - { - "compareType": "EQUAL", - "description": "", - "fieldName": "summaryTypeCode", - "fieldValue": "ATS_W_SMBL" if is_ats else "OTC_W_SMBL", - }, - ] - - if symbol: - l_cmp_filters.append( - { - "compareType": "EQUAL", - "fieldName": "issueSymbolIdentifier", - "fieldValue": symbol, - } - ) - - req_data = { - "compareFilters": l_cmp_filters, - "delimiter": "|", - "fields": [ - "issueSymbolIdentifier", - "totalWeeklyShareQuantity", - "totalWeeklyTradeCount", - "lastUpdateDate", - ], - "limit": 5000, - "offset": offset, - "quoteValues": False, - "sortFields": ["totalWeeklyShareQuantity"], - } - - return request( - "https://api.finra.org/data/group/otcMarket/name/weeklySummary", - method="POST", - headers=req_hdr, - json=req_data, - ) - - -def getFINRAdata( - start_date: str, symbol: str = "", tier: str = "T1", is_ats: bool = True -) -> Tuple[int, List]: - """Get FINRA data. [Source: FINRA] - - Parameters - ---------- - start_date : str - Weekly data to get FINRA data, in YYYY-MM-DD format - symbol : str - Stock ticker to get data from - tier : str - Stock tier between T1, T2, or OTCE - is_ats : bool - ATS data if true, NON-ATS otherwise - - Returns - ------- - Tuple[int, List] - Status code from request, List of response data - """ - req_hdr = {"Accept": "application/json", "Content-Type": "application/json"} - - l_cmp_filters = [ - { - "compareType": "EQUAL", - "fieldName": "weekStartDate", - "fieldValue": start_date, - }, - {"compareType": "EQUAL", "fieldName": "tierIdentifier", "fieldValue": tier}, - { - "compareType": "EQUAL", - "description": "", - "fieldName": "summaryTypeCode", - "fieldValue": "ATS_W_SMBL" if is_ats else "OTC_W_SMBL", - }, - ] - - if symbol: - l_cmp_filters.append( - { - "compareType": "EQUAL", - "fieldName": "issueSymbolIdentifier", - "fieldValue": symbol, - } - ) - - req_data = { - "compareFilters": l_cmp_filters, - "delimiter": "|", - "fields": [ - "issueSymbolIdentifier", - "totalWeeklyShareQuantity", - "totalWeeklyTradeCount", - "lastUpdateDate", - ], - "limit": 5000, - "quoteValues": False, - "sortFields": ["totalWeeklyShareQuantity"], - } - - response = request( - "https://api.finra.org/data/group/otcMarket/name/weeklySummary", - method="POST", - headers=req_hdr, - json=req_data, - ) - - return ( - response.status_code, - response.json() if response.status_code == 200 else list(), - ) - - -@log_start_end(log=logger) -def getATSdata(limit: int = 1000, tier_ats: str = "T1") -> Tuple[pd.DataFrame, Dict]: - """Get all FINRA ATS data, and parse most promising tickers based on linear regression - - Parameters - ---------- - limit: int - Number of tickers to filter from entire ATS data based on the sum of the total weekly shares quantity - tier_ats : int - Tier to process data from: T1, T2 or OTCE - - Returns - ------- - Tuple[pd.DataFrame, Dict] - Dark Pools (ATS) Data, Tickers from Dark Pools with better regression slope - """ - tiers = [tier_ats] if tier_ats else ["T1", "T2", "OTCE"] - df_ats = pd.DataFrame() - - for tier in tiers: - console.print(f"Processing Tier {tier} ...") - for d_week in getFINRAweeks(tier, is_ats=True): - offset = 0 - response = getFINRAdata_offset( - d_week["weekStartDate"], tier, "", True, offset - ) - l_data = response.json() - - while len(response.json()) == 5000: - offset += 5000 - response = getFINRAdata_offset( - d_week["weekStartDate"], tier, "", True, offset - ) - l_data += response.json() - - df_ats_week = pd.DataFrame(l_data) - df_ats_week["weekStartDate"] = d_week["weekStartDate"] - - if not df_ats_week.empty: - # df_ats = df_ats.append(df_ats_week, ignore_index=True) - df_ats = pd.concat([df_ats, df_ats_week], ignore_index=True) - - if not df_ats.empty: - df_ats = df_ats.sort_values("weekStartDate") - df_ats["weekStartDateInt"] = pd.to_datetime(df_ats["weekStartDate"]).apply( - lambda x: x.timestamp() - ) - - console.print(f"Processing regression on {limit} promising tickers ...") - - d_ats_reg = {} - # set(df_ats['issueSymbolIdentifier'].values) this would be iterating through all tickers - # but that is extremely time consuming for little reward. A little filtering is done to - # speed up search for best ATS tickers - for symbol in list( - df_ats.groupby("issueSymbolIdentifier")["totalWeeklyShareQuantity"] - .sum() - .sort_values()[-limit:] - .index - ): - try: - slope = stats.linregress( - df_ats[df_ats["issueSymbolIdentifier"] == symbol][ - "weekStartDateInt" - ].values, - df_ats[df_ats["issueSymbolIdentifier"] == symbol][ - "totalWeeklyShareQuantity" - ].values, - )[0] - d_ats_reg[symbol] = slope - except Exception: # nosec B110 # noqa: S110 - pass - - return df_ats, d_ats_reg - - -@log_start_end(log=logger) -def getTickerFINRAdata(symbol: str) -> Tuple[pd.DataFrame, pd.DataFrame]: - """Get all FINRA data associated with a ticker - - Parameters - ---------- - symbol : str - Stock ticker to get data from - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame] - Dark Pools (ATS) Data, OTC (Non-ATS) Data - """ - tiers = ["T1", "T2", "OTCE"] - - l_data = [] - for tier in tiers: - for d_week in getFINRAweeks(tier, is_ats=True): - status_code, response = getFINRAdata( - d_week["weekStartDate"], symbol, tier, True - ) - if status_code == 200: - if response: - d_data: dict = response[0] - d_data.update(d_week) - l_data.append(d_data) - else: - break - - df_ats = pd.DataFrame(l_data) - if not df_ats.empty: - df_ats = df_ats.sort_values("weekStartDate") - df_ats = df_ats.set_index("weekStartDate") - - l_data = [] - for tier in tiers: - for d_week in getFINRAweeks(tier, is_ats=False): - status_code, response = getFINRAdata( - d_week["weekStartDate"], symbol, tier, False - ) - if status_code == 200: - if response: - d_data = response[0] - d_data.update(d_week) - l_data.append(d_data) - else: - break - - df_otc = pd.DataFrame(l_data) - if not df_otc.empty: - df_otc = df_otc.sort_values("weekStartDate") - df_otc = df_otc.set_index("weekStartDate") - - return df_ats, df_otc diff --git a/openbb_terminal/stocks/dark_pool_shorts/finra_view.py b/openbb_terminal/stocks/dark_pool_shorts/finra_view.py deleted file mode 100644 index 7e53851f0ac..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/finra_view.py +++ /dev/null @@ -1,260 +0,0 @@ -""" FINRA View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import List, Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.dark_pool_shorts import finra_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def darkpool_ats_otc( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display barchart of dark pool (ATS) and OTC (Non ATS) data. [Source: FINRA] - - Parameters - ---------- - symbol : str - Stock ticker - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - ats, otc = finra_model.getTickerFINRAdata(symbol) - if ats.empty: - return console.print("[red]Could not get data[/red]\n") - - if ats.empty and otc.empty: - return console.print("No ticker data found!") - - fig = OpenBBFigure.create_subplots( - rows=2, - cols=1, - shared_xaxes=True, - vertical_spacing=0.05, - row_width=[0.4, 0.6], - specs=[[{"secondary_y": True}], [{"secondary_y": False}]], - ) - - if not ats.empty and not otc.empty: - fig.add_bar( - x=ats.index, - y=(ats["totalWeeklyShareQuantity"] + otc["totalWeeklyShareQuantity"]), - name="ATS", - opacity=0.8, - row=1, - col=1, - secondary_y=False, - ) - fig.add_bar( - x=otc.index, - y=otc["totalWeeklyShareQuantity"], - name="OTC", - opacity=0.8, - yaxis="y2", - offset=0.0001, - row=1, - col=1, - ) - - elif not ats.empty: - fig.add_bar( - x=ats.index, - y=(ats["totalWeeklyShareQuantity"] + otc["totalWeeklyShareQuantity"]), - name="ATS", - opacity=0.8, - row=1, - col=1, - secondary_y=False, - ) - - elif not otc.empty: - fig.add_bar( - x=otc.index, - y=otc["totalWeeklyShareQuantity"], - name="OTC", - opacity=0.8, - yaxis="y2", - secondary_y=False, - row=1, - col=1, - ) - - if not ats.empty: - fig.add_scatter( - name="ATS", - x=ats.index, - y=ats["totalWeeklyShareQuantity"] / ats["totalWeeklyTradeCount"], - line=dict(color="#fdc708", width=2), - opacity=1, - yaxis="y2", - row=2, - col=1, - ) - - if not otc.empty: - fig.add_scatter( - name="OTC", - x=otc.index, - y=otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"], - line=dict(color="#d81aea", width=2), - opacity=1, - row=2, - col=1, - ) - else: - fig.add_scatter( - name="OTC", - x=otc.index, - y=otc["totalWeeklyShareQuantity"] / otc["totalWeeklyTradeCount"], - line=dict(color="#d81aea", width=2), - opacity=1, - row=2, - col=1, - ) - - fig.update_layout( - margin=dict(t=30), - title=f"Dark Pools (ATS) vs OTC (Non-ATS) Data for {symbol}", - title_font_size=14, - yaxis3_title="Shares per Trade", - yaxis_title="Total Weekly Shares", - xaxis2_title="Weeks", - yaxis=dict(fixedrange=False, nticks=20), - yaxis2=dict( - fixedrange=False, - showgrid=False, - overlaying="y", - anchor="x", - layer="above traces", - title_standoff=0, - ), - yaxis3=dict(fixedrange=False, nticks=10), - xaxis=dict( - rangeslider=dict(visible=False), type="date", showspikes=True, nticks=20 - ), - barmode="group", - bargap=0.5, - bargroupgap=0, - hovermode="x unified", - spikedistance=1, - hoverdistance=1, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dpotc_ats", - ats, - sheet_name, - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dpotc_otc", - otc, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def plot_dark_pools_ats( - data: pd.DataFrame, symbols: List, external_axes: bool = False -) -> Union[OpenBBFigure, None]: - """Plots promising tickers based on growing ATS data - - Parameters - ---------- - data: pd.DataFrame - Dark Pools (ATS) Data - symbols: List - List of tickers from most promising with better linear regression slope - external_axes: bool, optional - Whether to return the figure object or not, by default False - - """ - - fig = OpenBBFigure(xaxis_title="Weeks", yaxis_title="Total Weekly Shares") - fig.set_title("Dark Pool (ATS) growing tickers") - - for symbol in symbols: - fig.add_scatter( - x=pd.to_datetime( - data[data["issueSymbolIdentifier"] == symbol]["weekStartDate"] - ), - y=data[data["issueSymbolIdentifier"] == symbol]["totalWeeklyShareQuantity"], - name=symbol, - mode="lines", - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def darkpool_otc( - input_limit: int = 1000, - limit: int = 10, - tier: str = "T1", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display dark pool (ATS) data of tickers with growing trades activity. [Source: FINRA] - - Parameters - ---------- - input_limit : int - Number of tickers to filter from entire ATS data based on - the sum of the total weekly shares quantity - limit : int - Number of tickers to display from most promising with - better linear regression slope - tier : str - Tier to process data from: T1, T2 or OTCE - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - # TODO: Improve command logic to be faster and more useful - df_ats, d_ats_reg = finra_model.getATSdata(input_limit, tier) - - if not df_ats.empty and d_ats_reg: - symbols = list( - dict( - sorted(d_ats_reg.items(), key=lambda item: item[1], reverse=True) - ).keys() - )[:limit] - - fig = plot_dark_pools_ats(df_ats, symbols, True) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "prom", - df_ats, - sheet_name, - fig, - ) - return fig.show(external=external_axes) - - return console.print("[red]Could not get data[/red]\n") diff --git a/openbb_terminal/stocks/dark_pool_shorts/ibkr_model.py b/openbb_terminal/stocks/dark_pool_shorts/ibkr_model.py deleted file mode 100644 index 842a459afab..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/ibkr_model.py +++ /dev/null @@ -1,40 +0,0 @@ -""" Interactive Broker Model """ - -__docformat__ = "numpy" - -import ftplib -import logging -from io import BytesIO - -import pandas as pd - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_cost_to_borrow() -> pd.DataFrame: - """Get stocks with highest cost to borrow [Source: Interactive Broker] - - Returns - ------- - pd.DataFrame - Cost to borrow - """ - ftp = ftplib.FTP("ftp3.interactivebrokers.com", "shortstock") # noqa: S321 - - flo = BytesIO() - ftp.retrbinary("RETR usa.txt", flo.write) - flo.seek(0) - - data = pd.read_csv(flo, sep="|", skiprows=1) - data = data[["#SYM", "FEERATE", "AVAILABLE"]] - data["AVAILABLE"] = data["AVAILABLE"].replace(">10000000", 10000000) - data.fillna(0, inplace=True) - data["AVAILABLE"] = data["AVAILABLE"].astype(int) - data.sort_values(by=["FEERATE"], ascending=False, inplace=True) - data["FEERATE"] = data["FEERATE"].apply(lambda x: str(x) + "%") - data.columns = ["Symbol", "Fees", "Available"] - data = data.reset_index(drop=True) - return data diff --git a/openbb_terminal/stocks/dark_pool_shorts/ibkr_view.py b/openbb_terminal/stocks/dark_pool_shorts/ibkr_view.py deleted file mode 100644 index 59ba388bdf8..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/ibkr_view.py +++ /dev/null @@ -1,49 +0,0 @@ -""" Interactive Broker View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.dark_pool_shorts import ibkr_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_cost_to_borrow( - limit: int = 20, export: str = "", sheet_name: Optional[str] = None -): - """Display stocks with highest cost to borrow. [Source: Interactive Broker] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - df = ibkr_model.get_cost_to_borrow().head(limit) - - if df.empty: - return console.print("No data found.") - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Highest Cost to Borrow", - export=bool(export), - ) - - return export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cost_to_borrow", - df, - sheet_name, - ) diff --git a/openbb_terminal/stocks/dark_pool_shorts/quandl_model.py b/openbb_terminal/stocks/dark_pool_shorts/quandl_model.py deleted file mode 100644 index 7c4e4de6093..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/quandl_model.py +++ /dev/null @@ -1,54 +0,0 @@ -""" Quandl Model """ - -__docformat__ = "numpy" - -import logging -from multiprocessing import AuthenticationError - -import pandas as pd -import quandl - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_QUANDL"]) -def get_short_interest(symbol: str, nyse: bool = False) -> pd.DataFrame: - """Plots the short interest of a stock. This corresponds to the - number of shares that have been sold short but have not yet been - covered or closed out. Either NASDAQ or NYSE [Source: Quandl] - - Parameters - ---------- - symbol : str - ticker to get short interest from - nyse : bool - data from NYSE if true, otherwise NASDAQ - - Returns - ------- - pd.DataFrame - short interest volume data - """ - quandl.ApiConfig.api_key = get_current_user().credentials.API_KEY_QUANDL - - df = pd.DataFrame() - - try: - df = ( - quandl.get(f"FINRA/FNYX_{symbol}") - if nyse - else quandl.get(f"FINRA/FNSQ_{symbol}") - ) - - except AuthenticationError: - console.print("[red]Invalid API Key[/red]\n") - # Catch invalid symbol - except Exception as e: - console.print(e) - - return df diff --git a/openbb_terminal/stocks/dark_pool_shorts/quandl_view.py b/openbb_terminal/stocks/dark_pool_shorts/quandl_view.py deleted file mode 100644 index 09cb9491d2a..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/quandl_view.py +++ /dev/null @@ -1,161 +0,0 @@ -""" Quandl View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.stocks.dark_pool_shorts import quandl_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_QUANDL"]) -def plot_short_interest( - symbol: str, - data: pd.DataFrame, - nyse: bool = False, - external_axes: bool = False, -): - """Plot the short interest of a stock. This corresponds to the - number of shares that have been sold short but have not yet been - covered or closed out. Either NASDAQ or NYSE [Source: Quandl] - - Parameters - ---------- - symbol : str - ticker to get short interest from - data: pd.DataFrame - Short interest dataframe - nyse : bool - data from NYSE if true, otherwise NASDAQ - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - fig = OpenBBFigure.create_subplots(1, 1, specs=[[{"secondary_y": True}]]) - fig.set_title(f"{('NASDAQ', 'NYSE')[nyse]} Short Interest on {symbol}") - - data.index = pd.to_datetime(data.index).strftime("%Y-%m-%d") - fig.add_bar( - x=data.index, - y=data["Short Volume"], - name="Short Volume", - marker_color=theme.down_color, - secondary_y=False, - ) - fig.add_bar( - x=data.index, - y=data["Total Volume"] - data["Short Volume"], - name="Total Volume", - marker_color=theme.up_color, - secondary_y=False, - ) - - fig.add_scatter( - x=data.index, - y=data["% of Volume Shorted"].values, - name="Fees", - marker_color=theme.get_colors()[0], - secondary_y=True, - showlegend=False, - ) - fig.update_yaxes( - title_text="Percentage of Volume Shorted", secondary_y=True, tickformat="%.0f%%" - ) - fig.update_yaxes(secondary_y=False, side="left", title_text="Shares") - fig.update_xaxes(title_text="Date", type="category", nticks=6) - fig.update_layout(barmode="stack", margin=dict(l=50)) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_QUANDL"]) -def short_interest( - symbol: str, - nyse: bool = False, - limit: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Plot the short interest of a stock. This corresponds to the - number of shares that have been sold short but have not yet been - covered or closed out. Either NASDAQ or NYSE [Source: Quandl] - - Parameters - ---------- - symbol : str - ticker to get short interest from - nyse : bool - data from NYSE if true, otherwise NASDAQ - limit: int - Number of past days to show short interest - raw : bool - Flag to print raw data instead - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_short_interest = quandl_model.get_short_interest(symbol, nyse) - - df_short_interest = df_short_interest.tail(limit) - - df_short_interest.columns = [ - "".join(" " + char if char.isupper() else char.strip() for char in idx).strip() - for idx in df_short_interest.columns.tolist() - ] - pd.options.mode.chained_assignment = None - - vol_pct = ( - 100 - * df_short_interest["Short Volume"].values - / df_short_interest["Total Volume"].values - ) - df_short_interest["% of Volume Shorted"] = [round(pct, 2) for pct in vol_pct] - - fig = plot_short_interest(symbol, df_short_interest, nyse, True) - - if raw: - if not get_current_user().preferences.USE_INTERACTIVE_DF: - df_short_interest["% of Volume Shorted"] = df_short_interest[ - "% of Volume Shorted" - ].apply(lambda x: f"{x/100:.2%}") - - df_short_interest = df_short_interest.applymap( - lambda x: lambda_long_number_format(x) - ).sort_index(ascending=False) - - print_rich_table( - df_short_interest, - headers=list(df_short_interest.columns), - show_index=True, - title="Short Interest of Stock", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "psi(quandl)", - df_short_interest, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/stocks/dark_pool_shorts/sec_model.py b/openbb_terminal/stocks/dark_pool_shorts/sec_model.py deleted file mode 100644 index 6da7d3835a1..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/sec_model.py +++ /dev/null @@ -1,209 +0,0 @@ -""" SEC Model """ - -__docformat__ = "numpy" -import io -import logging -import zipfile -from datetime import datetime, timedelta -from typing import Optional -from urllib.error import HTTPError - -import pandas as pd -import requests -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def catching_diff_url_formats(ftd_urls: list) -> list: - """Catches if URL for SEC data is one of the few URLS that are not in the - standard format. Catches are for either specific date ranges that have a different - format or singular URLs that have a different format. - - Parameters - ---------- - ftd_urls : list - list of urls of sec data - - Returns - ------- - list - list of ftd urls - """ - feb_mar_apr_catch = ["202002", "202003", "202004"] - for i, ftd_url in enumerate(ftd_urls): - # URLs with dates prior to the first half of June 2017 have different formats - if int(ftd_url[58:64]) < 201706 or "201706a" in ftd_url: - ftd_urls[i] = ftd_url.replace( - "fails-deliver-data", - "frequently-requested-foia-document-fails-deliver-data", - ) - # URLs between february, march, and april of 2020 have different formats - elif any(x in ftd_urls[i] for x in feb_mar_apr_catch): - ftd_urls[i] = ftd_url.replace( - "data/fails-deliver-data", "node/add/data_distribution" - ) - # First half of october 2019 has a different format - elif ( - ftd_url - == "https://www.sec.gov/files/data/fails-deliver-data/cnsfails201910a.zip" - ): - ftd_urls[i] = ( - "https://www.sec.gov/files/data/fails-deliver-data/cnsfails201910a_0.zip" - ) - - return ftd_urls - - -@log_start_end(log=logger) -def get_fails_to_deliver( - symbol: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - limit: int = 0, -) -> pd.DataFrame: - """Display fails-to-deliver data for a given ticker. [Source: SEC] - - Parameters - ---------- - symbol : str - Stock ticker - start_date : Optional[str] - Start of data, in YYYY-MM-DD format - end_date : Optional[str] - End of data, in YYYY-MM-DD format - limit : int - Number of latest fails-to-deliver being printed - - Returns - ------- - pd.DataFrame - Fail to deliver data - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=60)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - ftds_data = pd.DataFrame() - start = datetime.strptime(start_date, "%Y-%m-%d") - end = datetime.strptime(end_date, "%Y-%m-%d") - - # Filter by number of last FTD - if limit > 0: - url_ftds = "https://www.sec.gov/data/foiadocsfailsdatahtm" - text_soup_ftds = BeautifulSoup( - request(url_ftds, headers={"User-Agent": get_user_agent()}).text, - "lxml", - ) - - table = text_soup_ftds.find("table", {"class": "list"}) - links = table.findAll("a") - link_idx = 0 - - while len(ftds_data) < limit: - if link_idx > len(links): - break - link = links[link_idx] - url = "https://www.sec.gov" + link["href"] - response = requests.get( - url, headers={"User-Agent": get_user_agent()}, timeout=10 - ) - with zipfile.ZipFile(io.BytesIO(response.content)) as z, z.open( - z.namelist()[0] - ) as f: - all_ftds = pd.read_csv( - f, - sep="|", - usecols=[0, 2, 3, 5], - dtype={"QUANTITY (FAILS)": "int"}, - encoding="iso8859", - ) - tmp_ftds = all_ftds[all_ftds["SYMBOL"] == symbol] - del tmp_ftds["PRICE"] - del tmp_ftds["SYMBOL"] - # merge the data from this archive - ftds_data = pd.concat([ftds_data, tmp_ftds], ignore_index=True) - link_idx += 1 - - # clip away extra rows - ftds_data = ftds_data.sort_values("SETTLEMENT DATE")[-limit:] - - ftds_data["SETTLEMENT DATE"] = ftds_data["SETTLEMENT DATE"].apply( - lambda x: datetime.strptime(str(x), "%Y%m%d") - ) - - # Filter by start and end dates for FTD - else: - base_url = "https://www.sec.gov/files/data/fails-deliver-data/cnsfails" - ftd_dates = [] - - for y in range(start.year, end.year + 1): - if y < end.year: - for a_month in range(start.month, 13): - formatted_month = f"{a_month:02d}" - - if a_month == start.month and y == start.year: - if start.day < 16: - ftd_dates.append(str(y) + formatted_month + "a") - ftd_dates.append(str(y) + formatted_month + "b") - else: - ftd_dates.append(str(y) + formatted_month + "a") - ftd_dates.append(str(y) + formatted_month + "b") - - else: - for a_month in range(1, end.month): - formatted_month = f"{a_month:02d}" - - if a_month == end.month - 1: - ftd_dates.append(str(y) + formatted_month + "a") - if end.day > 15: - ftd_dates.append(str(y) + formatted_month + "b") - else: - ftd_dates.append(str(y) + formatted_month + "a") - ftd_dates.append(str(y) + formatted_month + "b") - - ftd_urls = [base_url + ftd_date + ".zip" for ftd_date in ftd_dates] - - # Calling function that catches a handful of urls that are slightly - # different than the standard format - ftd_urls = catching_diff_url_formats(ftd_urls) - - for ftd_link in ftd_urls: - try: - response = requests.get( - ftd_link, headers={"User-Agent": get_user_agent()}, timeout=10 - ) - with zipfile.ZipFile(io.BytesIO(response.content)) as z, z.open( - z.namelist()[0] - ) as f: - all_ftds = pd.read_csv( - f, - sep="|", - usecols=[0, 2, 3, 5], - dtype={"QUANTITY (FAILS)": "Int64"}, - encoding="iso8859", - ) - except HTTPError: - continue - - tmp_ftds = all_ftds[all_ftds["SYMBOL"] == symbol] - del tmp_ftds["PRICE"] - del tmp_ftds["SYMBOL"] - # merge the data from this archive - ftds_data = pd.concat([ftds_data, tmp_ftds], ignore_index=True) - - ftds_data["SETTLEMENT DATE"] = ftds_data["SETTLEMENT DATE"].apply( - lambda x: datetime.strptime(str(x), "%Y%m%d") - ) - - ftds_data = ftds_data[ftds_data["SETTLEMENT DATE"] > start] - ftds_data = ftds_data[ftds_data["SETTLEMENT DATE"] < end] - - return ftds_data diff --git a/openbb_terminal/stocks/dark_pool_shorts/sec_view.py b/openbb_terminal/stocks/dark_pool_shorts/sec_view.py deleted file mode 100644 index 1f1815ccb3c..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/sec_view.py +++ /dev/null @@ -1,123 +0,0 @@ -""" SEC View """ - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime, timedelta -from typing import Optional - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.dark_pool_shorts import sec_model - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def fails_to_deliver( - symbol: str, - data: Optional[pd.DataFrame] = None, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - limit: int = 0, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display fails-to-deliver data for a given ticker. [Source: SEC] - - Parameters - ---------- - symbol: str - Stock ticker - data: pd.DataFrame - Stock data - start_date: Optional[str] - Start of data, in YYYY-MM-DD format - end_date: Optional[str] - End of data, in YYYY-MM-DD format - limit : int - Number of latest fails-to-deliver being printed - raw: bool - Print raw data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=60)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - if data is None: - data = stocks_helper.load( - symbol=symbol, start_date=start_date, end_date=end_date - ) - - ftds_data = sec_model.get_fails_to_deliver(symbol, start_date, end_date, limit) - - fig = OpenBBFigure.create_subplots( - shared_xaxes=True, specs=[[{"secondary_y": True}]] - ) - if limit > 0: - data_ftd = data[data.index > (datetime.now() - timedelta(days=limit + 31))] - else: - data_ftd = data[data.index > start_date] - data_ftd = data_ftd[data_ftd.index < end_date] - - fig.add_bar( - name="Fail Quantity", - x=ftds_data["SETTLEMENT DATE"], - y=ftds_data["QUANTITY (FAILS)"], - secondary_y=False, - ) - fig.add_scatter( - name="Share Price", - x=data_ftd.index, - y=data_ftd["Adj Close"], - yaxis="y2", - line=dict(width=3), - opacity=1, - secondary_y=True, - ) - fig.update_layout( - margin=dict(l=30, r=0, t=30, b=10), - yaxis2=dict(title="Share Price [$]", overlaying="y", nticks=20), - yaxis=dict(side="left", title="Shares [K]", showgrid=False), - title=f"Fails-to-deliver Data for {symbol}", - xaxis_title="Date", - legend=dict(yanchor="bottom", y=0, xanchor="right", x=0.95), - ) - - if raw: - print_rich_table( - ftds_data, - headers=list(ftds_data.columns), - show_index=False, - title="Fails-To-Deliver Data", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ftd", - ftds_data.reset_index(), - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/stocks/dark_pool_shorts/shortinterest_model.py b/openbb_terminal/stocks/dark_pool_shorts/shortinterest_model.py deleted file mode 100644 index db884a43ecb..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/shortinterest_model.py +++ /dev/null @@ -1,67 +0,0 @@ -""" Short Interest View """ - -__docformat__ = "numpy" - -import logging - -import pandas as pd -from bs4 import BeautifulSoup -from pandas.core.frame import DataFrame - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_high_short_interest() -> DataFrame: - """Returns a high short interest DataFrame - - Returns - ------- - DataFrame - High short interest Dataframe with the following columns: - Ticker, Company, Exchange, ShortInt, Float, Outstd, Industry - """ - url_high_short_interested_stocks = "https://www.highshortinterest.com" - - text_soup_high_short_interested_stocks = BeautifulSoup( - request( - url_high_short_interested_stocks, headers={"User-Agent": get_user_agent()} - ).text, - "lxml", - ) - - a_high_short_interest_header = [ - high_short_interest_header.text.strip("\n").split("\n")[0] - for high_short_interest_header in text_soup_high_short_interested_stocks.findAll( - "td", {"class": "tblhdr"} - ) - ] - - if not a_high_short_interest_header: - return pd.DataFrame() - - df_high_short_interest = pd.DataFrame(columns=a_high_short_interest_header) - df_high_short_interest.loc[0] = ["", "", "", "", "", "", ""] - - stock_list_tr = text_soup_high_short_interested_stocks.find_all("tr") - - shorted_stock_data = [] - for a_stock in stock_list_tr: - a_stock_txt = a_stock.text - - if a_stock_txt == "": - continue - - shorted_stock_data = a_stock_txt.split("\n") - - if len(shorted_stock_data) == 8: - df_high_short_interest.loc[len(df_high_short_interest.index)] = ( - shorted_stock_data[:-1] - ) - - shorted_stock_data = [] - - return df_high_short_interest diff --git a/openbb_terminal/stocks/dark_pool_shorts/shortinterest_view.py b/openbb_terminal/stocks/dark_pool_shorts/shortinterest_view.py deleted file mode 100644 index 0d4440dfbeb..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/shortinterest_view.py +++ /dev/null @@ -1,52 +0,0 @@ -""" Short Interest View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.dark_pool_shorts import shortinterest_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def high_short_interest( - limit: int = 10, export: str = "", sheet_name: Optional[str] = None -): - """Prints top N high shorted interest stocks from https://www.highshortinterest.com - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - df_high_short_interest = shortinterest_model.get_high_short_interest() - - if df_high_short_interest.empty: - console.print("No data available.", "\n") - return - - df_high_short_interest = df_high_short_interest.iloc[1:].head(n=limit) - - print_rich_table( - df_high_short_interest, - headers=list(df_high_short_interest.columns), - show_index=False, - title="Top Interest Stocks", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hsi", - df_high_short_interest, - sheet_name, - ) diff --git a/openbb_terminal/stocks/dark_pool_shorts/stockgrid_model.py b/openbb_terminal/stocks/dark_pool_shorts/stockgrid_model.py deleted file mode 100644 index 64ebfeb164f..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/stockgrid_model.py +++ /dev/null @@ -1,198 +0,0 @@ -""" Stockgrid View """ - -__docformat__ = "numpy" - -import logging -from json import JSONDecodeError -from typing import List, Tuple - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) -# pylint: disable=unsupported-assignment-operation - - -@log_start_end(log=logger) -def get_dark_pool_short_positions( - sortby: str = "dpp_dollar", ascend: bool = False -) -> pd.DataFrame: - """Get dark pool short positions. [Source: Stockgrid] - - Parameters - ---------- - sortby : str - Field for which to sort by, where 'sv': Short Vol. [1M], - 'sv_pct': Short Vol. %%, 'nsv': Net Short Vol. [1M], - 'nsv_dollar': Net Short Vol. ($100M), 'dpp': DP Position [1M], - 'dpp_dollar': DP Position ($1B) - ascend : bool - Data in ascending order - - Returns - ------- - pd.DataFrame - Dark pool short position data - """ - d_fields_endpoints = { - "sv": "Short+Volume", - "sv_pct": "Short+Volume+%25", - "nsv": "Net+Short+Volume", - "nsv_dollar": "Net+Short+Volume+$", - "dpp": "Dark+Pools+Position", - "dpp_dollar": "Dark+Pools+Position+$", - } - - field = d_fields_endpoints[sortby] - - order = "asc" if ascend else "desc" - - link = f"https://stockgridapp.herokuapp.com/get_dark_pool_data?top={field}&minmax={order}" - - response = request(link) - df = pd.DataFrame(response.json()["data"]) - - df = df[ - [ - "Ticker", - "Date", - "Short Volume", - "Short Volume %", - "Net Short Volume", - "Net Short Volume $", - "Dark Pools Position", - "Dark Pools Position $", - ] - ] - - return df - - -@log_start_end(log=logger) -def get_short_interest_days_to_cover(sortby: str = "float") -> pd.DataFrame: - """Get short interest and days to cover. [Source: Stockgrid] - - Parameters - ---------- - sortby : str - Field for which to sort by, where 'float': Float Short %%, - 'dtc': Days to Cover, 'si': Short Interest - - Returns - ------- - pd.DataFrame - Short interest and days to cover data - """ - link = "https://stockgridapp.herokuapp.com/get_short_interest?top=days" - r = request(link) - df = pd.DataFrame(r.json()["data"]) - - d_fields = { - "float": "%Float Short", - "dtc": "Days To Cover", - "si": "Short Interest", - } - - df = df[ - ["Ticker", "Date", "%Float Short", "Days To Cover", "Short Interest"] - ].sort_values( - by=d_fields[sortby], - ascending=bool(sortby == "dtc"), - ) - - df["Short Interest"] = df["Short Interest"] / 1_000_000 - df.head() - df.columns = [ - "Ticker", - "Date", - "Float Short %", - "Days to Cover", - "Short Interest [1M]", - ] - - return df - - -@log_start_end(log=logger) -def get_short_interest_volume(symbol: str) -> Tuple[pd.DataFrame, List]: - """Get price vs short interest volume. [Source: Stockgrid] - - Parameters - ---------- - symbol : str - Stock to get data from - - Returns - ------- - Tuple[pd.DataFrame, List] - Short interest volume data, Price data - """ - link = f"https://stockgridapp.herokuapp.com/get_dark_pool_individual_data?ticker={symbol}" - response = request(link) - try: - response_json = response.json() - except JSONDecodeError: - return pd.DataFrame(), [None] - - df = pd.DataFrame(response_json["individual_short_volume_table"]["data"]) - df["date"] = pd.to_datetime(df["date"]) - - df = df.sort_values(by="date", ascending=False) - - df["Short Vol. [1M]"] = df["short_volume"] / 1_000_000 - df["Short Vol. %"] = df["short_volume%"] * 100 - df["Short Exempt Vol. [1k]"] = df["short_exempt_volume"] / 1_000 - df["Total Vol. [1M]"] = df["total_volume"] / 1_000_000 - - df = df[ - [ - "date", - "Short Vol. [1M]", - "Short Vol. %", - "Short Exempt Vol. [1k]", - "Total Vol. [1M]", - ] - ] - - return df, response_json["prices"]["prices"] - - -@log_start_end(log=logger) -def get_net_short_position(symbol: str) -> pd.DataFrame: - """Get net short position. [Source: Stockgrid] - - Parameters - ---------- - symbol: str - Stock to get data from - - Returns - ------- - pd.DataFrame - Net short position - """ - link = f"https://stockgridapp.herokuapp.com/get_dark_pool_individual_data?ticker={symbol}" - response = request(link) - - try: - df = pd.DataFrame(response.json()["individual_dark_pool_position_data"]) - except JSONDecodeError: - return pd.DataFrame() - df["dates"] = pd.to_datetime(df["dates"]) - - df = df.sort_values(by="dates", ascending=False) - - df["Net Short Vol. (1k $)"] = df["dollar_net_volume"] / 1_000 - df["Position (1M $)"] = df["dollar_dp_position"] - - df = df[ - [ - "dates", - "Net Short Vol. (1k $)", - "Position (1M $)", - ] - ] - - return df diff --git a/openbb_terminal/stocks/dark_pool_shorts/stockgrid_view.py b/openbb_terminal/stocks/dark_pool_shorts/stockgrid_view.py deleted file mode 100644 index 4b6f8792253..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/stockgrid_view.py +++ /dev/null @@ -1,388 +0,0 @@ -""" Stockgrid View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.dark_pool_shorts import stockgrid_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def dark_pool_short_positions( - limit: int = 10, - sortby: str = "dpp_dollar", - ascend: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Get dark pool short positions. [Source: Stockgrid] - - Parameters - ---------- - limit : int - Number of top tickers to show - sortby : str - Field for which to sort by, where 'sv': Short Vol. [1M], - 'sv_pct': Short Vol. %%, 'nsv': Net Short Vol. [1M], - 'nsv_dollar': Net Short Vol. ($100M), 'dpp': DP Position [1M], - 'dpp_dollar': DP Position ($1B) - ascend : bool - Data in ascending order - export : str - Export dataframe data to csv,json,xlsx file - """ - df = stockgrid_model.get_dark_pool_short_positions(sortby, ascend) - - dp_date = df["Date"].values[0] - df = df.drop(columns=["Date"]) - - df["Net Short Volume $"] = df["Net Short Volume $"] / 100_000_000 - df["Short Volume"] = df["Short Volume"] / 1_000_000 - df["Net Short Volume"] = df["Net Short Volume"] / 1_000_000 - df["Short Volume %"] = df["Short Volume %"] * 100 - df["Dark Pools Position $"] = df["Dark Pools Position $"] / (1_000_000_000) - df["Dark Pools Position"] = df["Dark Pools Position"] / 1_000_000 - df.columns = [ - "Ticker", - "Short Vol. [1M]", - "Short Vol. %", - "Net Short Vol. [1M]", - "Net Short Vol. ($100M)", - "DP Position [1M]", - "DP Position ($1B)", - ] - - # Assuming that the datetime is the same, which from my experiments seems to be the case - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"Data for: {dp_date}", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dppos", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def short_interest_days_to_cover( - limit: int = 10, - sortby: str = "float", - export: str = "", - sheet_name: Optional[str] = None, -): - """Print short interest and days to cover. [Source: Stockgrid] - - Parameters - ---------- - limit : int - Number of top tickers to show - sortby : str - Field for which to sort by, where 'float': Float Short %%, - 'dtc': Days to Cover, 'si': Short Interest - export : str - Export dataframe data to csv,json,xlsx file - """ - df = stockgrid_model.get_short_interest_days_to_cover(sortby) - - dp_date = df["Date"].values[0] - df = df.drop(columns=["Date"]) - - # Assuming that the datetime is the same, which from my experiments seems to be the case - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"Data for: {dp_date}", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "shortdtc", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def short_interest_volume( - symbol: str, - limit: int = 84, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot price vs short interest volume. [Source: Stockgrid] - - Parameters - ---------- - symbol : str - Stock to plot for - limit : int - Number of last open market days to show - raw : bool - Flag to print raw data instead - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - - """ - - df, prices = stockgrid_model.get_short_interest_volume(symbol) - if df.empty: - return console.print("[red]No data available[/red]\n") - - if raw: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "shortint(stockgrid)", - df, - sheet_name, - ) - - df.date = df.date.dt.date - return print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Price vs Short Volume", - export=bool(export), - limit=limit, - ) - - fig = OpenBBFigure.create_subplots( - rows=2, - cols=1, - shared_xaxes=True, - vertical_spacing=0.06, - horizontal_spacing=0, - row_width=[0.3, 0.6], - specs=[[{"secondary_y": True}], [{"secondary_y": False}]], - ) - fig.set_title(f"Price vs Short Volume Interest for {symbol}") - - # pycodestyle: disable=E501,E203 - fig.add_scatter( - name=symbol, - x=df["date"], - y=prices[len(prices) - len(df) :], # pycodestyle: disable=E501,E203 - line=dict(color="#fdc708", width=2), - connectgaps=True, - yaxis="y2", - opacity=1, - showlegend=False, - row=1, - col=1, - secondary_y=True, - ) - fig.add_bar( - x=df["date"], - y=df["Total Vol. [1M]"], - name="Total Volume", - marker_color=theme.up_color, - row=1, - col=1, - secondary_y=False, - ) - fig.add_bar( - x=df["date"], - y=df["Short Vol. [1M]"], - name="Short Volume", - marker_color=theme.down_color, - row=1, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="Short Vol. %", - x=df["date"], - y=df["Short Vol. %"], - line=dict(width=2), - connectgaps=True, - opacity=1, - showlegend=False, - row=2, - col=1, - secondary_y=False, - ) - - fig.update_traces(hovertemplate="%{y:.2f}") - fig.update_layout( - margin=dict(t=30), - yaxis2_title="Stock Price ($)", - yaxis_title="FINRA Volume [M]", - yaxis3_title="Short Vol. %", - yaxis=dict( - side="left", - fixedrange=False, - showgrid=False, - nticks=15, - layer="above traces", - ), - yaxis2=dict( - side="right", - fixedrange=False, - anchor="x", - overlaying="y", - nticks=10, - layer="below traces", - title_standoff=10, - ), - yaxis3=dict( - fixedrange=False, - nticks=10, - ), - hovermode="x unified", - spikedistance=1, - hoverdistance=1, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "shortint(stockgrid)", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def net_short_position( - symbol: str, - limit: int = 84, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot net short position. [Source: Stockgrid] - - Parameters - ---------- - symbol: str - Stock to plot for - limit : int - Number of last open market days to show - raw : bool - Flag to print raw data instead - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - - """ - - df = stockgrid_model.get_net_short_position(symbol) - if df.empty: - return console.print("[red]No data available[/red]\n") - - if raw: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "shortpos", - df, - sheet_name, - ) - - df["dates"] = df["dates"].dt.date - - return print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Net Short Positions", - export=bool(export), - limit=limit, - ) - - df = df.sort_values(by=["dates"]) - - fig = OpenBBFigure.create_subplots( - rows=1, - cols=1, - shared_xaxes=True, - specs=[[{"secondary_y": True}]], - ) - fig.set_title(f"Net Short Vol. vs Position for {symbol}") - - fig.add_bar( - x=df["dates"], - y=df["Net Short Vol. (1k $)"], - name="Net Short Vol. (1k $)", - marker_color=theme.down_color, - row=1, - col=1, - secondary_y=False, - ) - fig.add_scatter( - name="Position (1M $)", - x=df["dates"], - y=df["Position (1M $)"], - connectgaps=True, - marker_color=theme.up_color, - row=1, - col=1, - secondary_y=True, - yaxis="y2", - ) - fig.update_traces(hovertemplate="%{y:.2f}") - fig.update_layout( - margin=dict(l=40), - yaxis2_title="Net Short Vol. (1k $)", - yaxis_title="Position (1M $)", - yaxis=dict( - side="left", - fixedrange=False, - showgrid=False, - nticks=15, - layer="above traces", - ), - yaxis2=dict( - side="right", - fixedrange=False, - anchor="x", - overlaying="y", - nticks=10, - layer="below traces", - title_standoff=10, - ), - hovermode="x unified", - spikedistance=1, - hoverdistance=1, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "shortpos", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/dark_pool_shorts/yahoofinance_model.py b/openbb_terminal/stocks/dark_pool_shorts/yahoofinance_model.py deleted file mode 100644 index 9e497c9fff1..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/yahoofinance_model.py +++ /dev/null @@ -1,28 +0,0 @@ -""" Yahoo Finance Model """ - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_most_shorted() -> pd.DataFrame: - """Get most shorted stock screener [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Most Shorted Stocks - """ - url = "https://finance.yahoo.com/screener/predefined/most_shorted_stocks" - - data = pd.read_html(request(url, headers={"User-Agent": get_user_agent()}).text)[0] - data = data.iloc[:, :-1] - return data diff --git a/openbb_terminal/stocks/dark_pool_shorts/yahoofinance_view.py b/openbb_terminal/stocks/dark_pool_shorts/yahoofinance_view.py deleted file mode 100644 index 52c9ec59877..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/yahoofinance_view.py +++ /dev/null @@ -1,51 +0,0 @@ -""" Yahoo Finance View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.dark_pool_shorts import yahoofinance_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_most_shorted( - limit: int = 10, export: str = "", sheet_name: Optional[str] = None -): - """Display most shorted stocks screener. [Source: Yahoo Finance] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - df = yahoofinance_model.get_most_shorted().head(limit) - df.dropna(how="all", axis=1, inplace=True) - df = df.replace(float("NaN"), "") - - if df.empty: - return console.print("No data found.") - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Most Shorted Stocks", - export=bool(export), - ) - - return export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "shorted", - df, - sheet_name, - ) diff --git a/openbb_terminal/stocks/databento_model.py b/openbb_terminal/stocks/databento_model.py deleted file mode 100644 index ffd7659be4d..00000000000 --- a/openbb_terminal/stocks/databento_model.py +++ /dev/null @@ -1,135 +0,0 @@ -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import logging -from datetime import datetime, timedelta -from io import StringIO - -# IMPORTATION THIRDPARTY -import pandas as pd -import requests -from pydantic import BaseModel - -# IMPORTATION INTERNAL -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint:disable=too-few-public-methods - - -class DataBento(BaseModel): - symbol: str - exchange: str = "XNAS.ITCH" - stype: str = "raw_symbol" - start: str = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") - end: str = datetime.now().strftime("%Y-%m-%d") - - class Config: - arbitrary_types_allowed = True - - def get_historical_stock(self): - """Gets historical EOD stock data from DataBento. Currently, just NASDAQ is supported. - Note that only nonadjusted data is available.""" - self.exchange = "XNAS.ITCH" - self.stype = "raw_symbol" - return self.get_historical() - - def get_historical_futures(self): - """Gets historical EODfutures data from DataBento. Currently, just CME is supported. - Note this gets the highest volume contract each day""" - self.exchange = "GLBX.MDP3" - self.stype = "continuous" - return self.get_historical() - - @log_start_end(log=logger) - @check_api_key(["API_DATABENTO_KEY"]) - def get_historical(self) -> pd.DataFrame: - """Prepares the request to be made. I am using their https interface instead of their python client. - This updated to .C.0""" - - base_url = "https://hist.databento.com/v0/timeseries.get_range" - symbol = self.symbol + ".C.0" if self.exchange == "GLBX.MDP3" else self.symbol - params = { - "dataset": self.exchange, - "symbols": symbol, - "schema": "ohlcv-1d", - "start": self.start, - "end": self.end, - "stype_in": self.stype, - "encoding": "csv", - } - auth = requests.auth.HTTPBasicAuth( - get_current_user().credentials.API_DATABENTO_KEY, "" - ) - data = self.process_request(base_url, params, auth) - if data.empty: - return pd.DataFrame() - return data - - def process_request(self, base_url, params, auth) -> pd.DataFrame: - """Takes the request and returns the adjusted dataframe""" - r = request(base_url, params=params, auth=auth) - - if r.status_code == 422 and "Unprocessable" in r.text: - console.print(r.json()["detail"]) - return pd.DataFrame() - - if r.status_code != 200: - console.print(f"Error: Status Code {r.status_code}") - return pd.DataFrame() - df = pd.read_csv(StringIO(r.text)) - df["time"] = pd.to_datetime(df.ts_event, unit="ns") - df[["open", "high", "low", "close"]] /= 1_000_000_000 - df = df.drop(columns=["ts_event", "publisher_id"]).set_index("time") - df = df.rename( - columns={ - "open": "Open", - "high": "High", - "low": "Low", - "close": "Close", - "volume": "Volume", - } - ) - return df - - -@log_start_end(log=logger) -@check_api_key(["API_DATABENTO_KEY"]) -def get_historical_stock(symbol: str, start_date: str, end_date: str) -> pd.DataFrame: - """Gets historical EOD data from DataBento. Currently, just NASDAQ is supported. - - Parameters - ---------- - symbol : str - Symbol to get data for - start_date : str - Start date of data - end_date : str - End date to get data for - - """ - db = DataBento(symbol=symbol, start=start_date, end=end_date) - return db.get_historical_stock() - - -@log_start_end(log=logger) -@check_api_key(["API_DATABENTO_KEY"]) -def get_historical_futures(symbol: str, start_date: str, end_date: str) -> pd.DataFrame: - """Gets historical EODfutures data from DataBento. Currently, just CME is supported. - - Parameters - ---------- - symbol : str - Symbol to get data for - start_date : str - Start date of data - end_date : str - End date to get data for - - """ - db = DataBento(symbol=symbol, start=start_date, end=end_date) - return db.get_historical_futures() diff --git a/openbb_terminal/stocks/discovery/__init__.py b/openbb_terminal/stocks/discovery/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/discovery/ark_model.py b/openbb_terminal/stocks/discovery/ark_model.py deleted file mode 100644 index 6c7e75cc080..00000000000 --- a/openbb_terminal/stocks/discovery/ark_model.py +++ /dev/null @@ -1,149 +0,0 @@ -""" ARK Model """ - -__docformat__ = "numpy" - -import json -import logging -from datetime import timedelta - -import numpy as np -import pandas as pd -import yfinance as yf -from bs4 import BeautifulSoup -from pandas.core.frame import DataFrame - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_ark_orders( - buys_only: bool = False, - sells_only: bool = False, - fund: str = "", -) -> DataFrame: - """Returns ARK orders in a Dataframe - - Parameters - ---------- - buys_only: bool - Flag to filter on buys only - sells_only: bool - Flag to sort on sells only - fund: str - Optional filter by fund - - Returns - ------- - DataFrame - ARK orders data frame with the following columns - - (ticker, date, shares, weight, fund, direction) - """ - url_orders = "https://cathiesark.com/ark-funds-combined/trades" - - raw_page = request(url_orders, headers={"User-Agent": get_user_agent()}).text - - parsed_script = BeautifulSoup(raw_page, "lxml").find( - "script", {"id": "__NEXT_DATA__"} - ) - - parsed_json = json.loads(parsed_script.string) - - df_orders = pd.json_normalize(parsed_json["props"]["pageProps"]["arkTrades"]) - - if df_orders.empty: - return pd.DataFrame() - - df_orders.drop( - [ - "hidden", - "images.thumbnail", - "cusip", - "estimated_price", - "updated_at", - "created_at", - "region", - "country", - "isADR", - "companyName", - "clinicalTrialsSearchHandle", - "wasSPACBuy", - "currencyMultiplier", - "useRapidAPI", - "description", - "quandlTicker", - "customThumbnail", - "custom_thumbnail", - "id", - ], - axis=1, - inplace=True, - ) - - df_orders["date"] = pd.to_datetime(df_orders["date"], format="%Y-%m-%d").dt.date - - if df_orders.empty: - console.print("The ARK orders aren't available at the moment.\n") - return pd.DataFrame() - if fund: - df_orders = df_orders[df_orders.fund == fund] - if buys_only: - df_orders = df_orders[df_orders.direction == "Buy"] - if sells_only: - df_orders = df_orders[df_orders.direction == "Sell"] - - return df_orders - - -@log_start_end(log=logger) -def add_order_total(data: DataFrame) -> DataFrame: - """Takes an ARK orders dataframe and pulls data from Yahoo Finance to add - volume, open, close, high, low, and total columns - - Parameters - ---------- - data: DataFrame - ARK orders data frame with the following columns - - (ticker, date, shares, weight, fund, direction) - - Returns - ------- - DataFrame - ARK orders data frame with the following columns - - (ticker, date, shares, volume, open, close, high, low, total, weight, fund, direction) - """ - start_date = data["date"].iloc[-1] - timedelta(days=1) - - tickers = " ".join(data["ticker"].unique()) - - prices = yf.download(tickers, start=start_date, progress=False) - - if prices.empty: - return pd.DataFrame() - for i, candle in enumerate(["Volume", "Open", "Close", "High", "Low", "Total"]): - data.insert(i + 3, candle.lower(), 0) - - pd.options.mode.chained_assignment = None - for i, _ in data.iterrows(): - if np.isnan( - prices["Open"][data.loc[i, "ticker"]][ - data.loc[i, "date"].strftime("%Y-%m-%d") - ] - ): - for candle in ["Volume", "Open", "Close", "High", "Low", "Total"]: - data.loc[i, candle.lower()] = 0 - continue - - for candle in ["Volume", "Open", "Close", "High", "Low"]: - data.loc[i, candle.lower()] = prices[candle][data.loc[i, "ticker"]][ - data.loc[i, "date"].strftime("%Y-%m-%d") - ] - - data.loc[i, "total"] = data.loc[i, "close"] * data.loc[i, "shares"] - - pd.options.mode.chained_assignment = "warn" - - return data diff --git a/openbb_terminal/stocks/discovery/ark_view.py b/openbb_terminal/stocks/discovery/ark_view.py deleted file mode 100644 index 44efc98ac0f..00000000000 --- a/openbb_terminal/stocks/discovery/ark_view.py +++ /dev/null @@ -1,96 +0,0 @@ -""" ARK View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal import rich_config -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.discovery import ark_model - -logger = logging.getLogger(__name__) - - -def lambda_direction_color_red_green(val: str) -> str: - """Adds color tags to the Direction information: Buy -> Green, Sell -> Red - - Parameters - ---------- - val : str - Direction string - either Buy or Sell - - Returns - ------- - str - Direction string with color tags added - """ - color = "green" if val == "Buy" else "red" if val == "Sell" else "" - return f"[{color}]{val}[/{color}]" - - -@log_start_end(log=logger) -def ark_orders_view( - limit: int = 10, - sortby: str = "", - ascend: bool = False, - buys_only: bool = False, - sells_only: bool = False, - fund: str = "", - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints a table of the last N ARK Orders - - Parameters - ---------- - limit: int - Number of stocks to display - sortby: str - Column to sort on - ascend: bool - Flag to sort in ascending order - buys_only: bool - Flag to filter on buys only - sells_only: bool - Flag to sort on sells only - fund: str - Optional filter by fund - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx file - """ - df_orders = ark_model.get_ark_orders(buys_only, sells_only, fund) - if not df_orders.empty: - df_orders = ark_model.add_order_total(df_orders.head(limit)) - - if ( - rich_config.USE_COLOR - and not get_current_user().preferences.USE_INTERACTIVE_DF - ): - df_orders["direction"] = df_orders["direction"].apply( - lambda_direction_color_red_green - ) - - if sortby: - df_orders = df_orders.sort_values(by=sortby, ascending=ascend) - - print_rich_table( - df_orders, - headers=[x.title() for x in df_orders.columns], - show_index=False, - title="Orders by ARK Investment Management LLC", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "arkord", - df_orders, - sheet_name, - ) diff --git a/openbb_terminal/stocks/discovery/disc_controller.py b/openbb_terminal/stocks/discovery/disc_controller.py deleted file mode 100644 index 905e524e29c..00000000000 --- a/openbb_terminal/stocks/discovery/disc_controller.py +++ /dev/null @@ -1,885 +0,0 @@ -"""Discovery Controller Module.""" - -__docformat__ = "numpy" - -import argparse -import logging -from datetime import datetime -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_int_range, - check_non_negative, - check_positive, - valid_date, - valid_date_in_past, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.discovery import ( - ark_view, - finnhub_view, - finviz_view, - fmp_view, - nasdaq_view, - seeking_alpha_view, - shortinterest_view, - yahoofinance_view, -) - -# pylint:disable=C0302 - - -logger = logging.getLogger(__name__) - - -class DiscoveryController(BaseController): - """Discovery Controller class""" - - CHOICES_COMMANDS = [ - "filings", - "pipo", - "fipo", - "gainers", - "losers", - "ugs", - "gtech", - "active", - "ulc", - "asc", - "arkord", - "upcoming", - "trending", - "lowfloat", - "hotpenny", - "rtat", - "divcal", - "heatmap", - ] - - arkord_sortby_choices = [ - "date", - "volume", - "open", - "high", - "close", - "low", - "total", - "weight", - "shares", - ] - arkord_fund_choices = ["ARKK", "ARKF", "ARKW", "ARKQ", "ARKG", "ARKX", ""] - - PATH = "/stocks/disc/" - dividend_columns = [ - "Name", - "Symbol", - "Ex-Dividend Date", - "Payment Date", - "Record Date", - "Dividend", - "Annual Dividend", - "Announcement Date", - ] - heatmap_timeframes = ["day", "week", "month", "3month", "6month", "year", "ytd"] - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("stocks/disc/") - mt.add_cmd("filings", "FinancialModelingPrep") - mt.add_cmd("pipo", "Finnhub") - mt.add_cmd("fipo", "Finnhub") - mt.add_cmd("gainers", "Yahoo Finance") - mt.add_cmd("losers", "Yahoo Finance") - mt.add_cmd("ugs", "Yahoo Finance") - mt.add_cmd("gtech", "Yahoo Finance") - mt.add_cmd("active", "Yahoo Finance") - mt.add_cmd("ulc", "Yahoo Finance") - mt.add_cmd("asc", "Yahoo Finance") - mt.add_cmd("arkord", "Cathies Ark") - mt.add_cmd("upcoming", "Seeking Alpha") - mt.add_cmd("trending", "Seeking Alpha") - mt.add_cmd("lowfloat", "Fidelity") - mt.add_cmd("hotpenny", "Shortinterest") - mt.add_cmd("rtat", "NASDAQ Data Link") - mt.add_cmd("divcal", "NASDAQ Data Link") - mt.add_cmd("heatmap", "Finviz") - console.print(text=mt.menu_text, menu="Stocks - Discovery") - - # TODO Add flag for adding last price to the following table - @log_start_end(log=logger) - def call_divcal(self, other_args: List[str]): - """Process divcal command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="divcal", - description="""Get dividend calendar for selected date""", - ) - parser.add_argument( - "-d", - "--date", - default=datetime.now(), - type=valid_date, - dest="date", - help="Date to get format for", - ) - parser.add_argument( - "-s", - "--sort", - default="dividend", - type=str.lower, - choices=stocks_helper.format_parse_choices(self.dividend_columns), - help="Column to sort by", - dest="sort", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-d") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10 - ) - if ns_parser: - # Map fixes - - sort_col = stocks_helper.map_parse_choices(self.dividend_columns)[ - ns_parser.sort - ] - nasdaq_view.display_dividend_calendar( - date=ns_parser.date.strftime("%Y-%m-%d"), - sortby=sort_col, - ascend=ns_parser.reverse, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_pipo(self, other_args: List[str]): - """Process pipo command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="pipo", - description=""" - Past IPOs dates. [Source: https://finnhub.io] - """, - ) - parser.add_argument( - "-d", - "--days", - action="store", - dest="days", - type=check_non_negative, - default=5, - help="Number of past days to look for IPOs.", - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date_in_past, - default=None, - dest="start", - help="""The starting date (format YYYY-MM-DD) to look for IPOs. - When set, start date will override --days argument""", - ) - - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_non_negative, - default=20, - help="Limit number of IPOs to display.", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - finnhub_view.past_ipo( - num_days_behind=ns_parser.days, - limit=ns_parser.limit, - start_date=ns_parser.start, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fipo(self, other_args: List[str]): - """Process fipo command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fipo", - description=""" - Future IPOs dates. [Source: https://finnhub.io] - """, - ) - - parser.add_argument( - "-d", - "--days", - action="store", - dest="days", - type=check_non_negative, - default=5, - help="Number of days in the future to look for IPOs.", - ) - - parser.add_argument( - "-s", - "--end", - type=valid_date, - default=None, - dest="end", - help="""The end date (format YYYY-MM-DD) to look for IPOs, starting from today. - When set, end date will override --days argument""", - ) - - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_non_negative, - default=20, - help="Limit number of IPOs to display.", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - finnhub_view.future_ipo( - num_days_ahead=ns_parser.days, - limit=ns_parser.limit, - end_date=ns_parser.end, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_gainers(self, other_args: List[str]): - """Process gainers command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="gainers", - description="Print up to 25 top gainers. [Source: Yahoo Finance]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=5, - help="Limit of stocks to display.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yahoofinance_view.display_gainers( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_losers(self, other_args: List[str]): - """Process losers command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="losers", - description="Print up to 25 top losers. [Source: Yahoo Finance]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=5, - help="Limit of stocks to display.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yahoofinance_view.display_losers( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ugs(self, other_args: List[str]): - """Process ugs command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ugs", - description=""" - Print up to 25 undervalued stocks with revenue and earnings growth in excess of 25%. - [Source: Yahoo Finance] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=5, - help="Limit of stocks to display.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yahoofinance_view.display_ugs( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_gtech(self, other_args: List[str]): - """Process gtech command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="gtech", - description="Print up to 25 top tech stocks with revenue and earnings" - + " growth in excess of 25%. [Source: Yahoo Finance]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=5, - help="Limit of stocks to display.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yahoofinance_view.display_gtech( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_active(self, other_args: List[str]): - """Process active command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="active", - description=""" - Print up to 25 top most actively traded intraday tickers. [Source: Yahoo Finance] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=5, - help="Limit of stocks to display.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yahoofinance_view.display_active( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ulc(self, other_args: List[str]): - """Process ulc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ulc", - description=""" - Print up to 25 potentially undervalued large cap stocks. [Source: Yahoo Finance] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=5, - help="Limit of stocks to display.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yahoofinance_view.display_ulc( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_asc(self, other_args: List[str]): - """Process asc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="asc", - description=""" - Print up to 25 small cap stocks with earnings growth rates better than 25%. [Source: Yahoo Finance] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_int_range(1, 25), - default=5, - help="Limit of stocks to display.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - yahoofinance_view.display_asc( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_arkord(self, other_args: List[str]): - """Process arkord command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="arkord", - description=""" - Orders by ARK Investment Management LLC - https://ark-funds.com/. [Source: https://cathiesark.com] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of stocks to display.", - ) - parser.add_argument( - "-s", - "--sortby", - dest="sort_col", - choices=self.arkord_sortby_choices, - type=str, - help="Column to sort by", - default="", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-b", - "--buy_only", - dest="buys_only", - help="Flag to look at buys only", - action="store_true", - default=False, - ) - parser.add_argument( - "-c", - "--sell_only", - dest="sells_only", - help="Flag to look at sells only", - action="store_true", - default=False, - ) - parser.add_argument( - "--fund", - type=str, - default="", - help="Filter by fund", - dest="fund", - choices=self.arkord_fund_choices, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - ark_view.ark_orders_view( - limit=ns_parser.limit, - sortby=ns_parser.sort_col, - ascend=ns_parser.reverse, - buys_only=ns_parser.buys_only, - sells_only=ns_parser.sells_only, - fund=ns_parser.fund, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_upcoming(self, other_args: List[str]): - # TODO: switch to nasdaq - """Process upcoming command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="upcoming", - description="""Upcoming earnings release dates. [Source: Seeking Alpha]""", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Limit of upcoming earnings release dates to look ahead.", - ) - parser.add_argument( - "-s", - "--start", - type=valid_date, - help="Start date of data, in YYYY-MM-DD format. Defaults to today.", - dest="start_date", - default=datetime.today().strftime("%Y-%m-%d"), - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - seeking_alpha_view.upcoming_earning_release_dates( - limit=ns_parser.limit, - start_date=ns_parser.start_date, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_trending(self, other_args: List[str]): - """Process trending command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="trending", - description="""Trending news articles. [Source: Seeking Alpha]""", - ) - parser.add_argument( - "-i", - "--id", - action="store", - dest="n_id", - type=check_positive, - default=-1, - help="article ID", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="limit of articles being printed", - ) - parser.add_argument( - "-d", - "--date", - action="store", - dest="s_date", - type=valid_date, - default=datetime.now().strftime("%Y-%m-%d"), - help="starting date of articles", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-i") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - seeking_alpha_view.news( - article_id=ns_parser.n_id, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_lowfloat(self, other_args: List[str]): - """Process lowfloat command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lowfloat", - description=""" - Print top stocks with lowest float. LowFloat.com provides a convenient - sorted database of stocks which have a float of under 10 million shares. Additional key - data such as the number of outstanding shares, short interest, and company industry is - displayed. Data is presented for the Nasdaq Stock Market, the New York Stock Exchange, - the American Stock Exchange, and the Over the Counter Bulletin Board. [Source: www.lowfloat.com] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="limit of stocks to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - shortinterest_view.low_float( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_hotpenny(self, other_args: List[str]): - """Process hotpenny command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hotpenny", - description="Provides top penny stocks from various websites. [Source: Yfinance]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="limit of stocks to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - shortinterest_view.hot_penny_stocks( - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - source=ns_parser.source, - ) - - @log_start_end(log=logger) - def call_rtat(self, other_args: List[str]): - """Process rtat command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rtat", - description=""" - Tracking over $30B USD/day of individual investors trades, - RTAT gives a daily view into retail activity and sentiment for over 9,500 US traded stocks, - ADRs, and ETPs - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=3, - help="limit of days to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - nasdaq_view.display_top_retail( - limit=ns_parser.limit, export=ns_parser.export - ) - - @log_start_end(log=logger) - def call_filings(self, other_args: List[str]) -> None: - """Process Filings command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="filings", - description="The most-recent filings submitted to the SEC", - ) - parser.add_argument( - "-p", - "--pages", - dest="pages", - metavar="pages", - type=int, - default=1, - help="The number of pages to get data from (1000 entries/page; maximum 30 pages)", - ) - parser.add_argument( - "-t", - "--today", - dest="today", - action="store_true", - default=False, - help="Show all filings from today", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=5, - ) - if ns_parser: - fmp_view.display_filings( - ns_parser.pages, ns_parser.limit, ns_parser.today, ns_parser.export - ) - - @log_start_end(log=logger) - def call_heatmap(self, other_args: List[str]): - """Process heatmap command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="heatmap", - description=""" - Get the SP 500 heatmap from finviz and display in interactive treemap - """, - ) - parser.add_argument( - "-t", - "--timeframe", - default="day", - choices=self.heatmap_timeframes, - help="Timeframe to get heatmap data for", - dest="timeframe", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - finviz_view.display_heatmap( - ns_parser.timeframe, - ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/stocks/discovery/disc_helpers.py b/openbb_terminal/stocks/discovery/disc_helpers.py deleted file mode 100644 index 9218298e802..00000000000 --- a/openbb_terminal/stocks/discovery/disc_helpers.py +++ /dev/null @@ -1,15 +0,0 @@ -import re -from typing import Optional - -import pandas as pd - -from openbb_terminal.helper_funcs import get_user_agent, request - - -def get_df(url: str, header: Optional[int] = None) -> pd.DataFrame: - headers = {"User-Agent": get_user_agent()} - html = request(url, headers=headers).text - # use regex to replace radio button html entries - html_clean = re.sub(r"().*?()", "", html) - dfs = pd.read_html(html_clean, header=header) - return dfs diff --git a/openbb_terminal/stocks/discovery/fidelity_model.py b/openbb_terminal/stocks/discovery/fidelity_model.py deleted file mode 100644 index 63a8f3b166f..00000000000 --- a/openbb_terminal/stocks/discovery/fidelity_model.py +++ /dev/null @@ -1,100 +0,0 @@ -""" Fidelity Model """ - -__docformat__ = "numpy" - -import logging -from typing import Tuple - -import pandas as pd -from bs4 import BeautifulSoup -from pandas.core.frame import DataFrame - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_orders() -> Tuple[str, DataFrame]: - """Returns Fidelity orders in a Dataframe - - Returns - ------- - Tuple[str, DataFrame] - First value in the tuple is a Fidelity orders header - Fidelity orders Dataframe with the following columns - - Symbol, Buy / Sell Ratio, Price Change, Company, # Buy Orders, # Sell Orders - """ - url_orders = ( - "https://eresearch.fidelity.com/eresearch/gotoBL/fidelityTopOrders.jhtml" - ) - - text_soup_url_orders = BeautifulSoup( - request(url_orders, headers={"User-Agent": get_user_agent()}).text, "lxml" - ) - - l_orders = [] - l_orders_vals = [] - idx = 0 - order_list = text_soup_url_orders.findAll( - "td", - {"class": ["second", "third", "fourth", "fifth", "sixth", "seventh", "eight"]}, - ) - for an_order in order_list: - if ((idx + 1) % 3 == 0) or ((idx + 1) % 4 == 0) or ((idx + 1) % 6 == 0): - if not an_order: - l_orders_vals.append("") - else: - try: - l_orders_vals.append(an_order.contents[1]) - except IndexError: - l_orders_vals.append("0") - elif (idx + 1) % 5 == 0: - s_orders = str(an_order) - l_orders_vals.append( - s_orders[ - s_orders.find('title="') + len('title="') : s_orders.find('"/>') - ] - ) - else: - l_orders_vals.append(an_order.text.strip()) - - idx += 1 - - # Add value to dictionary - if (idx + 1) % 8 == 0: - l_orders.append(l_orders_vals) - l_orders_vals = [] - idx = 0 - - df_orders = pd.DataFrame( - l_orders, - columns=[ - "Symbol", - "Company", - "Price Change", - "# Buy Orders", - "Buy / Sell Ratio", - "# Sell Orders", - "Latest News", - ], - ) - - df_orders = df_orders[ - [ - "Symbol", - "Buy / Sell Ratio", - "Price Change", - "Company", - "# Buy Orders", - "# Sell Orders", - "Latest News", - ] - ] - - order_header = text_soup_url_orders.findAll("span", {"class": "source"})[ - 0 - ].text.capitalize() - - return order_header, df_orders diff --git a/openbb_terminal/stocks/discovery/fidelity_view.py b/openbb_terminal/stocks/discovery/fidelity_view.py deleted file mode 100644 index 8137d9c678a..00000000000 --- a/openbb_terminal/stocks/discovery/fidelity_view.py +++ /dev/null @@ -1,104 +0,0 @@ -""" Fidelity View """ - -__docformat__ = "numpy" - -import logging -import os -import re -from typing import Optional - -from openbb_terminal import rich_config -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.discovery import fidelity_model - -logger = logging.getLogger(__name__) - - -def lambda_buy_sell_ratio_color_red_green(val: str) -> str: - """Add color tags to the Buys/Sells ratio cell - - Parameters - ---------- - val : str - Buys/Sells ratio cell - - Returns - ------- - str - Buys/Sells ratio cell with color tags - """ - - buy_sell_match = re.match(r"(\d+)% Buys, (\d+)% Sells", val, re.M | re.I) - - if not buy_sell_match: - return val - - buys = int(buy_sell_match.group(1)) - sells = int(buy_sell_match.group(2)) - - if buys >= sells: - return f"[green]{buys}%[/green] Buys, {sells}% Sells" - - return f"{buys}% Buys, [red]{sells}%[/red] Sells" - - -def lambda_price_change_color_red_green(val: str) -> str: - """Add color tags to the price change cell - - Parameters - ---------- - val : str - Price change cell - - Returns - ------- - str - Price change cell with color tags - """ - - val_float = float(val.split(" ")[0]) - if val_float > 0: - return f"[green]{val}[/green]" - return f"[red]{val}[/red]" - - -@log_start_end(log=logger) -def orders_view(limit: int = 5, export: str = "", sheet_name: Optional[str] = None): - """Prints last N orders by Fidelity customers. [Source: Fidelity] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - order_header, df_orders = fidelity_model.get_orders() - - if rich_config.USE_COLOR and not get_current_user().preferences.USE_INTERACTIVE_DF: - df_orders["Buy / Sell Ratio"] = df_orders["Buy / Sell Ratio"].apply( - lambda_buy_sell_ratio_color_red_green - ) - df_orders["Price Change"] = df_orders["Price Change"].apply( - lambda_price_change_color_red_green - ) - - df_orders = df_orders.head(n=limit).iloc[:, :-1] - - print_rich_table( - df_orders, - headers=[x.title() for x in df_orders.columns], - show_index=False, - title=f"{order_header}:", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ford", - df_orders, - sheet_name, - ) diff --git a/openbb_terminal/stocks/discovery/finnhub_model.py b/openbb_terminal/stocks/discovery/finnhub_model.py deleted file mode 100644 index adba489dec7..00000000000 --- a/openbb_terminal/stocks/discovery/finnhub_model.py +++ /dev/null @@ -1,153 +0,0 @@ -import logging -from datetime import datetime, timedelta -from typing import Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def get_ipo_calendar( - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Get IPO calendar - - Parameters - ---------- - start_date : Optional[str] - Initial date, format YYYY-MM-DD - end_date : Optional[str] - Final date, format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - Get dataframe with IPO calendar events - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=5)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - response = request( - f"https://finnhub.io/api/v1/calendar/ipo?from={start_date}&to={end_date}&token={get_current_user().credentials.API_FINNHUB_KEY}" - ) - - df = pd.DataFrame() - - if response.status_code == 200: - d_data = response.json() - if "ipoCalendar" in d_data: - d_refactor_columns = { - "numberOfShares": "Number of Shares", - "totalSharesValue": "Total Shares Value", - "date": "Date", - "exchange": "Exchange", - "name": "Name", - "price": "Price", - "status": "Status", - } - df = pd.DataFrame(d_data["ipoCalendar"]).rename(columns=d_refactor_columns) - else: - console.print("Response is empty") - elif response.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - elif response.status_code == 403: - console.print("[red]API Key not authorized for Premium Feature[/red]\n") - else: - console.print(f"Error in request: {response.json()['error']}", "\n") - - return df - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def get_past_ipo( - num_days_behind: int = 5, - start_date: Optional[str] = None, -) -> pd.DataFrame: - """Past IPOs dates. [Source: Finnhub] - - Parameters - ---------- - num_days_behind: int - Number of days to look behind for IPOs dates - start_date: str - The starting date (format YYYY-MM-DD) to look for IPOs - - Returns - ------- - pd.DataFrame - Get dataframe with past IPOs - """ - today = datetime.now() - - start = ( - (today - timedelta(days=num_days_behind)).strftime("%Y-%m-%d") - if start_date is None - else start_date - ) - - df_past_ipo = ( - get_ipo_calendar(start, today.strftime("%Y-%m-%d")) - .rename(columns={"Date": "Past"}) - .fillna("") - ) - - if df_past_ipo.empty: - console.print(f"No IPOs found since the last {num_days_behind} days") - else: - df_past_ipo = df_past_ipo.sort_values("Past", ascending=False) - - return df_past_ipo - - -@log_start_end(log=logger) -def get_future_ipo( - num_days_ahead: int = 5, - end_date: Optional[str] = None, -) -> pd.DataFrame: - """Future IPOs dates. [Source: Finnhub] - - Parameters - ---------- - num_days_ahead: int - Number of days to look ahead for IPOs dates - end_date: datetime - The end date (format YYYY-MM-DD) to look for IPOs from today onwards - - Returns - ------- - pd.DataFrame - Get dataframe with future IPOs - """ - today = datetime.now() - - end = ( - (today + timedelta(days=num_days_ahead)).strftime("%Y-%m-%d") - if end_date is None - else end_date - ) - - df_future_ipo = ( - get_ipo_calendar(today.strftime("%Y-%m-%d"), end) - .rename(columns={"Date": "Future"}) - .fillna("") - ) - - if df_future_ipo.empty: - console.print(f"No IPOs found for the next {num_days_ahead} days") - else: - df_future_ipo = df_future_ipo.sort_values("Future", ascending=False) - - return df_future_ipo diff --git a/openbb_terminal/stocks/discovery/finnhub_view.py b/openbb_terminal/stocks/discovery/finnhub_view.py deleted file mode 100644 index bd5f6d83ae4..00000000000 --- a/openbb_terminal/stocks/discovery/finnhub_view.py +++ /dev/null @@ -1,97 +0,0 @@ -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.discovery import finnhub_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def past_ipo( - num_days_behind: int = 5, - start_date: Optional[str] = None, - limit: int = 20, - export: str = "", - sheet_name: Optional[str] = None, -): - """Past IPOs dates. [Source: Finnhub] - - Parameters - ---------- - num_days_behind: int - Number of days to look behind for IPOs dates - start_date: str - The starting date (format YYYY-MM-DD) to look for IPOs - limit: int - Limit number of IPOs to display. Default is 20 - export : str - Export dataframe data to csv,json,xlsx file - """ - - df_past_ipo = finnhub_model.get_past_ipo(num_days_behind, start_date) - - if not df_past_ipo.empty: - print_rich_table( - df_past_ipo, - headers=list(df_past_ipo.columns), - show_index=False, - title="IPO Dates", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "pipo", - df_past_ipo, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def future_ipo( - num_days_ahead: int = 5, - end_date: Optional[str] = None, - limit: int = 20, - export: str = "", - sheet_name: Optional[str] = None, -): - """Future IPOs dates. [Source: Finnhub] - - Parameters - ---------- - num_days_ahead: int - Number of days to look ahead for IPOs dates - end_date: datetime - The end date (format YYYY-MM-DD) to look for IPOs from today onwards - limit: int - Limit number of IPOs to display. Default is 20 - export : str - Export dataframe data to csv,json,xlsx file - """ - - df_future_ipo = finnhub_model.get_future_ipo(num_days_ahead, end_date) - - if not df_future_ipo.empty: - print_rich_table( - df_future_ipo, - headers=list(df_future_ipo.columns), - show_index=False, - title="Future IPO Dates", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fipo", - df_future_ipo, - sheet_name, - ) diff --git a/openbb_terminal/stocks/discovery/finviz_model.py b/openbb_terminal/stocks/discovery/finviz_model.py deleted file mode 100644 index cf0d44167a2..00000000000 --- a/openbb_terminal/stocks/discovery/finviz_model.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Finviz Model""" - -__docformat__ = "numpy" - -import pandas as pd - -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -timeframe_map = { - "day": "", - "week": "w1", - "month": "w4", - "3month": "w13", - "6month": "w26", - "year": "w52", - "ytd": "ytd", -} - - -def get_heatmap_data(timeframe: str) -> pd.DataFrame: - """Get heatmap data from finviz - - Parameters - ---------- - timeframe: str - Timeframe to get performance for - - Returns - ------- - pd.DataFrame - Dataframe of tickers, changes and sectors - """ - if timeframe not in timeframe_map: - console.print(f"[red]{timeframe} is an invalid timeframe[/red]") - return pd.DataFrame() - r = request( - f"https://finviz.com/api/map_perf.ashx?t=sec&st={timeframe_map[timeframe]}", - headers={"User-Agent": get_user_agent()}, - ) - r2_json = request( - "https://finviz.com/maps/sec.json?rev=316", - headers={"User-Agent": get_user_agent()}, - ).json() - df_change = pd.DataFrame.from_dict(r.json()["nodes"], orient="index") - df_change.columns = ["Change"] - df_change["Change"] = df_change.Change - changes_dict = df_change.to_dict() - - dfs = pd.DataFrame() - for sector_dict in r2_json["children"]: - for industry_dict in sector_dict["children"]: - temp = pd.DataFrame(industry_dict["children"]) - temp["Sector"] = sector_dict["name"] - temp["Industry"] = industry_dict["name"] - dfs = pd.concat([dfs, temp], axis=0).reset_index(drop=True) - - dfs["Change"] = dfs["name"].copy().map(changes_dict["Change"]) - dfs = dfs.rename(columns={"name": "Ticker"}) - return dfs diff --git a/openbb_terminal/stocks/discovery/finviz_view.py b/openbb_terminal/stocks/discovery/finviz_view.py deleted file mode 100644 index 1fb46d8419d..00000000000 --- a/openbb_terminal/stocks/discovery/finviz_view.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Finviz View""" - -__docformat__ = "numpy" - -import os -from typing import Optional, Union - -import pandas as pd -import plotly.express as px - -from openbb_terminal import OpenBBFigure -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.stocks.discovery import finviz_model - - -def display_heatmap( - timeframe: str, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Display heatmap from finviz - - Parameters - ---------- - timeframe: str - Timeframe for performance - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - dfs = finviz_model.get_heatmap_data(timeframe) - if dfs.empty: - return None - - color_bin = [-100, -2, -1, -0.001, 0.001, 1, 2, 100] - dfs["colors"] = pd.cut( - dfs["Change"], - bins=color_bin, - labels=[ - "rgb(246, 53, 56)", - "rgb(191, 64, 69)", - "rgb(139, 68, 78)", - "grey", - "rgb(53, 118, 78)", - "rgb(47, 158, 79)", - "rgb(48, 204, 90)", - ], - ) - - fig = OpenBBFigure.create_subplots( - print_grid=False, - vertical_spacing=0, - horizontal_spacing=-0, - specs=[[{"type": "domain"}]], - rows=1, - cols=1, - ) - - path_tree = [px.Constant("SP 500"), "Sector", "Ticker"] - treemap = px.treemap( - dfs, - path=path_tree, - values="value", - custom_data=["Change"], - color="colors", - color_discrete_map={ - "(?)": "#262931", - "grey": "grey", - "rgb(246, 53, 56)": "rgb(246, 53, 56)", - "rgb(191, 64, 69)": "rgb(191, 64, 69)", - "rgb(139, 68, 78)": "rgb(139, 68, 78)", - "rgb(53, 118, 78)": "rgb(53, 118, 78)", - "rgb(47, 158, 79)": "rgb(47, 158, 79)", - "rgb(48, 204, 90)": "rgb(48, 204, 90)", - }, - ) - fig.add_trace(treemap["data"][0], row=1, col=1) - - fig.data[0].texttemplate = ( - "

%{label}
%{customdata[0]:.2f}%



" - ) - fig.data[0].insidetextfont = dict(family="Arial Black", size=20, color="white") - - fig.update_traces( - textinfo="label+text+value", - textposition="middle center", - selector=dict(type="treemap"), - marker_line_width=0.3, - marker_pad_b=10, - marker_pad_l=0, - marker_pad_r=0, - marker_pad_t=25, - tiling_pad=2, - ) - fig.update_layout( - margin=dict(t=0, l=0, r=0, b=0), - hovermode=False, - font=dict(family="Arial Black", size=20, color="white"), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "heatmap", - dfs, - sheet_name, - fig, - margin=False, - ) - - return fig.show(margin=False, external=external_axes) diff --git a/openbb_terminal/stocks/discovery/fmp_view.py b/openbb_terminal/stocks/discovery/fmp_view.py deleted file mode 100644 index 5f6c86e3c3b..00000000000 --- a/openbb_terminal/stocks/discovery/fmp_view.py +++ /dev/null @@ -1,85 +0,0 @@ -""" Financial Modeling Prep View """ - -__docformat__ = "numpy" - -import datetime -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import fmp_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_filings( - pages: int = 1, - limit: int = 5, - today: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Display recent forms submitted to the SEC - Parameters - ---------- - pages: int = 1 - The range of most-rececnt pages to get entries from (1000 per page, max 30 pages) - limit: int = 5 - Limit the number of entries to display (default: 5) - today: bool = False - Show all from today - export: str = "" - Export data as csv, json, or xlsx - Examples - -------- - openbb.stocks.display_filings() - openbb.stocks.display_filings(today = True, export = "csv") - """ - filings = fmp_model.get_filings(pages) - if today is True: - now: str = datetime.datetime.now().strftime("%Y-%m-%d") - iso_today: int = datetime.datetime.today().isoweekday() - if iso_today < 6 and not filings.empty: - filings = filings.filter(like=now, axis=0) - limit = 1000 - else: - console.print( - "[red]No filings today, displaying the most recent submissions instead[/red]" - ) - - if not filings.empty: - filings.reset_index(["Date"], inplace=True) - for _, row in filings.head(limit).iterrows(): - console.print( - "Timestamp: ", - f"{row['Date']}", - " US/Eastern", - "\n", - "Ticker: ", - f"{row['Ticker']}", - "\n", - "CIK: " f"{row['CIK']}", - "\n", - "Form Type: ", - f"{row['Form Type']}", - "\n", - f"{row['Title']}", - "\n", - f"{row['URL']}\n", - sep="", - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "filings", - filings, - sheet_name, - ) - else: - logger.error("Could not get data") - console.print("[red]Could not get data[/red]\n") diff --git a/openbb_terminal/stocks/discovery/nasdaq_model.py b/openbb_terminal/stocks/discovery/nasdaq_model.py deleted file mode 100644 index bcf488c9dce..00000000000 --- a/openbb_terminal/stocks/discovery/nasdaq_model.py +++ /dev/null @@ -1,92 +0,0 @@ -"""NASDAQ DataLink Model""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Optional - -import pandas as pd -import requests - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_QUANDL"]) -def get_retail_tickers() -> pd.DataFrame: - """Gets the top 10 retail stocks per day - - Returns - ------- - pd.DataFrame - Dataframe of tickers - """ - r = request( - f"https://data.nasdaq.com/api/v3/datatables/NDAQ/RTAT10/?api_key={get_current_user().credentials.API_KEY_QUANDL}" - ) - - df = pd.DataFrame() - - if r.status_code == 200: - df = pd.DataFrame(r.json()["datatable"]["data"]) - df.columns = ["Date", "Ticker", "Activity", "Sentiment"] - else: - console.print(r.text) - - return df - - -@log_start_end(log=logger) -def get_dividend_cal(date: Optional[str] = None) -> pd.DataFrame: - """Gets dividend calendar for given date. Date represents Ex-Dividend Date - - Parameters - ---------- - date: datetime - Date to get for in format YYYY-MM-DD - - Returns - ------- - pd.DataFrame - Dataframe of dividend calendar - """ - # TODO: HELP WANTED: - # Nasdaq API doesn't like a lot of stuff. Your ISP or VPN, the specific user agent - # that you might be using, etc. More exploration is required to make this feature - # equally usable for all. In the time being we patch selection of the user agent and - # add a timeout for cases when the URL doesn't respond. - - if date is None: - date = datetime.today().strftime("%Y-%m-%d") - - ag = get_user_agent() - # Nasdaq API doesn't like this user agent, thus we always get other than this particular one - while ( - ag - == "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:82.1) Gecko/20100101 Firefox/82.1" - ): - ag = get_user_agent() - try: - r = request( - f"https://api.nasdaq.com/api/calendar/dividends?date={date}", - headers={"User-Agent": ag}, - ) - - df = pd.DataFrame() - - if r.status_code == 200: - df = pd.DataFrame(r.json()["data"]["calendar"]["rows"]) - - else: - console.print(r.text) - - except requests.exceptions.ReadTimeout: - logger.exception("Request timed out") - return pd.DataFrame() - return df diff --git a/openbb_terminal/stocks/discovery/nasdaq_view.py b/openbb_terminal/stocks/discovery/nasdaq_view.py deleted file mode 100644 index 91dd5b897ad..00000000000 --- a/openbb_terminal/stocks/discovery/nasdaq_view.py +++ /dev/null @@ -1,124 +0,0 @@ -"""NASDAQ DataLink View""" - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import Optional - -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.discovery import nasdaq_model - -# pylint: disable=E1123 - - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_QUANDL"]) -def display_top_retail( - limit: int = 3, export: str = "", sheet_name: Optional[str] = None -): - """Display the top 10 retail traded stocks for last days - - Parameters - ---------- - limit : int, optional - Number of days to show by default 3 - export : str, optional - Format to export data, by default "" - """ - retails = nasdaq_model.get_retail_tickers() - - if retails.empty: - return - - for date, df in retails.head(limit * 10).groupby("Date"): - clean_df = df.drop(columns=["Date"]) - clean_df = clean_df.reset_index(drop=True) - print_rich_table( - clean_df, - headers=[x.title() for x in df.columns], - show_index=False, - title=f"[bold]{date} Top Retail:[/bold]", - export=bool(export), - ) - console.print("") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rtat", - retails, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_dividend_calendar( - date: Optional[str] = None, - sortby: str = "Dividend", - ascend: bool = False, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display NASDAQ dividend calendar - - Parameters - ---------- - date: str - Date to get dividend calendar for, format YYYY-MM-DD - sortby: str - Column to sort data for - ascend: bool - Flag to sort in ascending order - limit: int - Number of results to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - - if date is None: - date = datetime.today().strftime("%Y-%m-%d") - - div_map = { - "symbol": "Symbol", - "companyName": "Name", - "dividend_Ex_Date": "Ex-Dividend Date", - "payment_Date": "Payment Date", - "record_Date": "Record Date", - "dividend_Rate": "Dividend", - "indicated_Annual_Dividend": "Annual Dividend", - "announcement_Date": "Announcement Date", - } - calendar = nasdaq_model.get_dividend_cal(date) - if calendar.empty: - console.print( - "No data found. Check that the date provided is a market day. If it is then try this function" - " again as the request may have not gone through.\n" - ) - return - calendar = calendar.drop(columns=["announcement_Date"]) - calendar.columns = calendar.columns.map(div_map) - calendar = calendar.sort_values(by=sortby, ascending=ascend) - print_rich_table( - calendar, - headers=[x.title() for x in calendar.columns], - title=f"[bold]Dividend Calendar for {date}[/bold]", - export=bool(export), - limit=limit, - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "divcal", - calendar, - sheet_name, - ) diff --git a/openbb_terminal/stocks/discovery/seeking_alpha_model.py b/openbb_terminal/stocks/discovery/seeking_alpha_model.py deleted file mode 100644 index a417488205c..00000000000 --- a/openbb_terminal/stocks/discovery/seeking_alpha_model.py +++ /dev/null @@ -1,215 +0,0 @@ -""" Seeking Alpha Model """ - -__docformat__ = "numpy" - -import logging -from datetime import date, timedelta - -import pandas as pd -from bs4 import BeautifulSoup -from pandas.core.frame import DataFrame - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_earnings_html(url_next_earnings: str) -> str: - """Wraps HTTP request for testibility - - Parameters - ---------- - url_next_earnings : str - Next earnings URL - - Returns - ------- - str - HTML page of next earnings - """ - earnings_html = request( - url_next_earnings, headers={"User-Agent": get_user_agent()} - ).text - - return earnings_html - - -def get_filters(date_str: str) -> str: - text = f"?filter[selected_date]={date_str}&filter[with_rating]=false&filter[currency]=USD" - return text - - -@log_start_end(log=logger) -def get_next_earnings(limit: int = 5, start_date: date = date.today()) -> DataFrame: - """Returns a DataFrame with upcoming earnings - - Parameters - ---------- - limit : int - Number of days to look ahead - start_date: date - Date to start from. Defaults to today - - Returns - ------- - DataFrame - Upcoming earnings DataFrame - """ - base_url = "https://seekingalpha.com/api/v3/earnings_calendar/tickers" - df_earnings = pd.DataFrame() - - for _ in range(0, limit): - start_date = pd.to_datetime(start_date) - date_str = str(start_date.strftime("%Y-%m-%d")) - response = request(base_url + get_filters(date_str), timeout=10) - json = response.json() - try: - data = json["data"] - cleaned_data = [x["attributes"] for x in data] - temp_df = pd.DataFrame.from_records(cleaned_data) - temp_df = temp_df.drop(columns=["sector_id"]) - temp_df["Date"] = start_date # pylint: disable=E1137 - df_earnings = pd.concat( - [df_earnings, temp_df], join="outer", ignore_index=True - ) - start_date = start_date + timedelta(days=1) - except KeyError: - pass - - df_earnings = df_earnings.rename( - columns={ - "slug": "Ticker", - "name": "Name", - "release_time": "Release Time", - "exchange": "Exchange", - } - ) - - if df_earnings.empty: - console.print("No earnings found. Try adjusting the date.\n") - return pd.DataFrame() - - df_earnings = df_earnings[ - df_earnings["Date"] <= pd.to_datetime(start_date + timedelta(days=limit)) - ] - - return df_earnings - - -@log_start_end(log=logger) -def get_articles_html(url_articles: str) -> str: - """Wraps HTTP request for testability - - Parameters - ---------- - url_articles : str - Articles URL - - Returns - ------- - str - HTML page of articles - """ - articles_html = request(url_articles, headers={"User-Agent": get_user_agent()}).text - - return articles_html - - -@log_start_end(log=logger) -def get_trending_list(limit: int = 5) -> list: - """Returns a list of trending articles - - Parameters - ---------- - limit: int - Number of articles - - Returns - ------- - list - Trending articles list - """ - - articles = [] - url_articles = "https://seekingalpha.com/news/trending_news" - response = request(url_articles, headers={"User-Agent": get_user_agent()}) - - # Check that the API response was successful - if response.status_code != 200: - console.print("Invalid response\n") - else: - for item in response.json(): - article_url = item["uri"] - if not article_url.startswith("/news/"): - continue - article_id = article_url.split("/")[2].split("-")[0] - articles.append( - { - "title": item["title"], - "publishedAt": item["publish_on"][: item["publish_on"].rfind(".")], - "url": "https://seekingalpha.com" + article_url, - "id": article_id, - } - ) - - return articles[:limit] - - -@log_start_end(log=logger) -def get_article_data(article_id: int) -> dict: - """Returns an article - - Parameters - ---------- - article_id : int - Article ID - - Returns - ------- - dict - Article data - """ - - article_url = f"https://seekingalpha.com/api/v3/news/{article_id}" - response = request(article_url, headers={"User-Agent": get_user_agent()}) - jdata = response.json() - content = jdata["data"]["attributes"]["content"].replace("", "\n") - content = BeautifulSoup(content, features="html.parser").get_text() - - article = { - "title": jdata["data"]["attributes"]["title"], - "publishedAt": jdata["data"]["attributes"]["lastModified"], - "url": "https://seekingalpha.com" + jdata["data"]["links"]["self"], - "content": content, - } - - return article - - -@log_start_end(log=logger) -def get_news_html(news_type: str = "Top-News") -> dict: - """Gets news. [Source: SeekingAlpha] - - Parameters - ---------- - news_type : str - From: Top-News, On-The-Move, Market-Pulse, Notable-Calls, Buybacks, Commodities, Crypto, Issuance, Global, - Guidance, IPOs, SPACs, Politics, M-A, Consumer, Energy, Financials, Healthcare, MLPs, REITs, Technology - - Returns - ------- - dict - HTML page of articles - """ - sa_url = ( - f"http://seekingalpha.com/api/v3/news?filter%5Bcategory%5D=market-news%3A%3A{news_type}" - "&filter%5Bsince%5D=0&filter%5Buntil%5D=0&include=author%2CprimaryTickers%2CsecondaryTickers" - "&isMounting=true&page%5Bsize%5D=25&page%5Bnumber%5D=1" - ) - - articles_html = request(sa_url, headers={"User-Agent": get_user_agent()}).json() - - return articles_html diff --git a/openbb_terminal/stocks/discovery/seeking_alpha_view.py b/openbb_terminal/stocks/discovery/seeking_alpha_view.py deleted file mode 100644 index 4eb7725d1d8..00000000000 --- a/openbb_terminal/stocks/discovery/seeking_alpha_view.py +++ /dev/null @@ -1,131 +0,0 @@ -""" Seeking Alpha View """ - -__docformat__ = "numpy" - -import logging -import os -from datetime import date -from typing import Optional - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.discovery import seeking_alpha_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def upcoming_earning_release_dates( - limit: int = 5, - start_date: date = date.today(), - export: str = "", - sheet_name: Optional[str] = None, -): - """Displays upcoming earnings release dates - - Parameters - ---------- - num_pages: int - Number of pages to scrape, each page is one day - limit: int - Number of upcoming earnings release dates - start_date: Optional[date] - The day to start looking at earnings releases from - export : str - Export dataframe data to csv,json,xlsx file - """ - - df_earnings = seeking_alpha_model.get_next_earnings(limit, start_date) - - if df_earnings.empty: - console.print("No upcoming earnings release dates found") - return - - print_rich_table( - df_earnings, - show_index=False, - headers=df_earnings.columns, - title="Upcoming Earnings Releases", - export=bool(export), - ) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "upcoming", - df_earnings, - sheet_name, - ) - - -@log_start_end(log=logger) -def news( - article_id: int = -1, - limit: int = 5, - export: str = "", - sheet_name: Optional[str] = None, -): - """Prints the latest news article list. [Source: Seeking Alpha] - - Parameters - ---------- - article_id: int - Article ID. If -1, none is selected - limit: int - Number of articles to display. Only used if article_id is -1. - export : str - Export dataframe data to csv,json,xlsx file - """ - # User wants to see all latest news - if article_id == -1: - articles = seeking_alpha_model.get_trending_list(limit) - - df_articles = pd.DataFrame(articles) - - df_articles["publishedAt"] = pd.to_datetime(df_articles["publishedAt"]) - - df_news = pd.DataFrame( - df_articles, columns=["publishedAt", "id", "title", "url"] - ) - - # We look for a date name in the column to assume its a date on frontend side for filtering etc - df_news.rename(columns={"publishedAt": "publishedAtDate"}, inplace=True) - - df_news = df_news.drop("id", axis=1) - print_rich_table( - df_news, - show_index=False, - export=bool(export), - limit=limit, - ) - - # User wants to access specific article - else: - article = seeking_alpha_model.get_article_data(article_id) - - if export: - df_articles = pd.DataFrame(article) - - console.print( - article["publishedAt"][: article["publishedAt"].rfind(":") - 3].replace( - "T", " " - ), - " ", - article["title"], - ) - console.print(article["url"]) - console.print("\n") - console.print(article["content"]) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "trending", - df_articles, - sheet_name, - ) diff --git a/openbb_terminal/stocks/discovery/shortinterest_model.py b/openbb_terminal/stocks/discovery/shortinterest_model.py deleted file mode 100644 index a3b800ddfca..00000000000 --- a/openbb_terminal/stocks/discovery/shortinterest_model.py +++ /dev/null @@ -1,49 +0,0 @@ -""" Short Interest View """ - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.stocks.discovery.disc_helpers import get_df - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_low_float() -> pd.DataFrame: - """Returns low float DataFrame - - Returns - ------- - DataFrame - Low float DataFrame with the following columns: - Ticker, Company, Exchange, ShortInt, Float, Outstd, Industry - """ - df = get_df("https://www.lowfloat.com")[2] - df.columns = [ - "Ticker", - "Company", - "Exchange", - "Float", - "Outstd", - "ShortInt", - "Industry", - ] - return df.drop([31]) - - -@log_start_end(log=logger) -def get_today_hot_penny_stocks() -> pd.DataFrame: - """Returns today hot penny stocks - - Returns - ------- - DataFrame - Today hot penny stocks DataFrame with the following columns: - Ticker, Price, Change, $ Volume, Volume, # Trades - """ - df = get_df("https://www.pennystockflow.com", 0)[1] - return df.drop([10]) diff --git a/openbb_terminal/stocks/discovery/shortinterest_view.py b/openbb_terminal/stocks/discovery/shortinterest_view.py deleted file mode 100644 index 3528381382f..00000000000 --- a/openbb_terminal/stocks/discovery/shortinterest_view.py +++ /dev/null @@ -1,93 +0,0 @@ -""" Short Interest View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.discovery import shortinterest_model, yahoofinance_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def low_float(limit: int = 5, export: str = "", sheet_name: Optional[str] = None): - """Prints top N low float stocks from https://www.lowfloat.com - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - df_low_float = shortinterest_model.get_low_float() - df_low_float = df_low_float.iloc[1:].head(n=limit) - - print_rich_table( - df_low_float, - headers=list(df_low_float.columns), - show_index=False, - title="Top Float Stocks", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "lowfloat", - df_low_float, - sheet_name, - ) - - -@log_start_end(log=logger) -def hot_penny_stocks( - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - source: str = "YahooFinance", -): - """Prints top N hot penny stocks from https://www.pennystockflow.com - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - source : str - Where to get the data from. Choose from - YahooFinance or Shortinterest - """ - if source == "YahooFinance": - df_penny_stocks = yahoofinance_model.get_hotpenny() - elif source == "Shortinterest": - console.print("[red]Data from this source is often not penny stocks[/red]\n") - df_penny_stocks = shortinterest_model.get_today_hot_penny_stocks() - else: - console.print("[red]Invalid source provided[/red]\n") - return - - if df_penny_stocks.empty: - console.print("[red]No data found.[/red]") - return - - print_rich_table( - df_penny_stocks, - headers=list(df_penny_stocks.columns) if source != "Shortinterest" else None, - show_index=False, - title="Top Penny Stocks", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hotpenny", - df_penny_stocks, - sheet_name, - ) diff --git a/openbb_terminal/stocks/discovery/yahoofinance_model.py b/openbb_terminal/stocks/discovery/yahoofinance_model.py deleted file mode 100644 index 3a5037d2012..00000000000 --- a/openbb_terminal/stocks/discovery/yahoofinance_model.py +++ /dev/null @@ -1,171 +0,0 @@ -""" Yahoo Finance Model """ - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.discovery.disc_helpers import get_df - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_gainers() -> pd.DataFrame: - """Get top gainers. [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Stock Gainers - """ - - df_gainers = get_df("https://finance.yahoo.com/screener/predefined/day_gainers")[0] - df_gainers.dropna(how="all", axis=1, inplace=True) - df_gainers = df_gainers.replace(float("NaN"), "") - if df_gainers.empty: - console.print("No gainers found.") - - return df_gainers - - -@log_start_end(log=logger) -def get_losers() -> pd.DataFrame: - """Get top losers. [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Stock Losers - """ - - df_losers = get_df("https://finance.yahoo.com/screener/predefined/day_losers")[0] - df_losers.dropna(how="all", axis=1, inplace=True) - df_losers = df_losers.replace(float("NaN"), "") - - if df_losers.empty: - console.print("No losers found.") - return df_losers - - -@log_start_end(log=logger) -def get_ugs() -> pd.DataFrame: - """Get stocks with earnings growth rates better than 25% and relatively low PE and PEG ratios. - [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Undervalued stocks - """ - - df = get_df( - "https://finance.yahoo.com/screener/predefined/undervalued_growth_stocks" - )[0] - df.dropna(how="all", axis=1, inplace=True) - df = df.replace(float("NaN"), "") - - if df.empty: - console.print("No data found.") - return df - - -@log_start_end(log=logger) -def get_gtech() -> pd.DataFrame: - """Get technology stocks with revenue and earnings growth in excess of 25%. [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Growth technology stocks - """ - - df = get_df( - "https://finance.yahoo.com/screener/predefined/growth_technology_stocks" - )[0] - df.dropna(how="all", axis=1, inplace=True) - df = df.replace(float("NaN"), "") - - if df.empty: - console.print("No data found.") - return df - - -@log_start_end(log=logger) -def get_active() -> pd.DataFrame: - """Get stocks ordered in descending order by intraday trade volume. [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Most active stocks - """ - - df = get_df("https://finance.yahoo.com/screener/predefined/most_actives")[0] - df.dropna(how="all", axis=1, inplace=True) - df = df.replace(float("NaN"), "") - - if df.empty: - console.print("No data found.") - return df - - -@log_start_end(log=logger) -def get_ulc() -> pd.DataFrame: - """Get Yahoo Finance potentially undervalued large cap stocks. - [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Most undervalued large cap stocks - """ - - df = get_df("https://finance.yahoo.com/screener/predefined/undervalued_large_caps")[ - 0 - ] - df.dropna(how="all", axis=1, inplace=True) - df = df.replace(float("NaN"), "") - - if df.empty: - console.print("No data found.") - return df - - -@log_start_end(log=logger) -def get_asc() -> pd.DataFrame: - """Get Yahoo Finance small cap stocks with earnings growth rates better than 25%. - [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Most aggressive small cap stocks - """ - - df = get_df("https://finance.yahoo.com/screener/predefined/aggressive_small_caps")[ - 0 - ] - df.dropna(how="all", axis=1, inplace=True) - df = df.replace(float("NaN"), "") - - if df.empty: - console.print("No data found.") - return df - - -@log_start_end(log=logger) -def get_hotpenny() -> pd.DataFrame: - """Get Yahoo Finance hot penny stocks. [Source: Yahoo Finance] - - Returns - ------- - pd.DataFrame - Hottest penny stocks - """ - return get_df( - "https://finance.yahoo.com/u/yahoo-finance/watchlists/most-active-penny-stocks/" - )[1] diff --git a/openbb_terminal/stocks/discovery/yahoofinance_view.py b/openbb_terminal/stocks/discovery/yahoofinance_view.py deleted file mode 100644 index 8c7424667d7..00000000000 --- a/openbb_terminal/stocks/discovery/yahoofinance_view.py +++ /dev/null @@ -1,257 +0,0 @@ -""" Yahoo Finance View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.discovery import yahoofinance_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_gainers( - limit: int = 5, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Display gainers. [Source: Yahoo Finance] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df_gainers = yahoofinance_model.get_gainers() - - if not df_gainers.empty: - print_rich_table( - df_gainers, - headers=list(df_gainers.columns), - show_index=False, - title="Gainers", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gainers", - df_gainers, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_losers( - limit: int = 5, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Display losers. [Source: Yahoo Finance] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df_losers = yahoofinance_model.get_losers() - - if not df_losers.empty: - print_rich_table( - df_losers, - headers=list(df_losers.columns), - show_index=False, - title="Display Losers", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "losers", - df_losers, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_ugs( - limit: int = 5, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Display most undervalued growth stock. [Source: Yahoo Finance] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = yahoofinance_model.get_ugs() - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Undervalued Growth Stocks", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ugs", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_gtech( - limit: int = 5, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Display growth technology stocks. [Source: Yahoo Finance] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = yahoofinance_model.get_gtech() - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Growth Tech Stocks", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gtech", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_active( - limit: int = 5, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Display most active stocks. [Source: Yahoo Finance] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = yahoofinance_model.get_active() - - if not df.empty: - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Most Active Stocks", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "active", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_ulc( - limit: int = 5, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Display potentially undervalued large cap stocks. [Source: Yahoo Finance] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = yahoofinance_model.get_ulc() - - if not df.empty: - print_rich_table( - df.dropna(), - headers=list(df.columns), - show_index=False, - title="Undervalued Large Cap Stocks", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ulc", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_asc( - limit: int = 5, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Display small cap stocks with earnings growth rates better than 25%. [Source: Yahoo Finance] - - Parameters - ---------- - limit: int - Number of stocks to display - export : str - Export dataframe data to csv,json,xlsx file - """ - - df = yahoofinance_model.get_asc() - - if not df.empty: - print_rich_table( - df.dropna(), - headers=list(df.columns), - show_index=False, - title="High Growth Small Caps", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "asc", - df, - sheet_name, - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/__init__.py b/openbb_terminal/stocks/fundamental_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/fundamental_analysis/av_model.py b/openbb_terminal/stocks/fundamental_analysis/av_model.py deleted file mode 100644 index f6716f3880e..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/av_model.py +++ /dev/null @@ -1,762 +0,0 @@ -"""Alpha Vantage Model""" - -__docformat__ = "numpy" - -import logging -from typing import Dict, List - -import numpy as np -import pandas as pd -from alpha_vantage.fundamentaldata import FundamentalData - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import lambda_long_number_format, request -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import yahoo_finance_model -from openbb_terminal.stocks.fundamental_analysis.fa_helper import clean_df_index -from openbb_terminal.stocks.stocks_helper import clean_fraction - -logger = logging.getLogger(__name__) - - -def check_premium_key(json_response: Dict) -> bool: - """Checks if the response is the premium endpoint""" - if json_response == { - "Information": "Thank you for using Alpha Vantage! This is a premium endpoint. You may subscribe to " - "any of the premium plans at https://www.alphavantage.co/premium/ to instantly unlock all premium endpoints" - }: - console.print( - "This is a premium endpoint for AlphaVantage. Please use a premium key.\n" - ) - return True - return False - - -@log_start_end(log=logger) -def get_overview(symbol: str) -> pd.DataFrame: - """Get alpha vantage company overview - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - Dataframe of fundamentals - """ - # Request OVERVIEW data from Alpha Vantage API - s_req = ( - f"https://www.alphavantage.co/query?function=OVERVIEW&symbol={symbol}&apikey" - f"={get_current_user().credentials.API_KEY_ALPHAVANTAGE}" - ) - result = request(s_req, stream=True) - result_json = result.json() - - df_fa = pd.DataFrame() - - # If the returned data was unsuccessful - if "Error Message" in result_json: - console.print(result_json["Error Message"]) - # check if json is empty - elif not result_json: - console.print("No data found from Alpha Vantage\n") - # Parse json data to dataframe - elif "Note" in result_json: - console.print(result_json["Note"], "\n") - else: - df_fa = pd.json_normalize(result_json) - - # Keep json data sorting in dataframe - df_fa = df_fa[list(result_json.keys())].T - df_fa.iloc[5:] = df_fa.iloc[5:].applymap(lambda x: lambda_long_number_format(x)) - df_fa.columns = [" "] - - return df_fa - - -@log_start_end(log=logger) -def get_key_metrics(symbol: str) -> pd.DataFrame: - """Get key metrics from overview - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - Dataframe of key metrics - """ - # Request OVERVIEW data - s_req = ( - f"https://www.alphavantage.co/query?function=OVERVIEW&symbol={symbol}" - f"&apikey={get_current_user().credentials.API_KEY_ALPHAVANTAGE}" - ) - result = request(s_req, stream=True) - result_json = result.json() - - # If the returned data was unsuccessful - if "Error Message" in result_json: - console.print(result_json["Error Message"]) - else: - # check if json is empty - if not result_json or len(result_json) < 2: - console.print("No data found from Alpha Vantage\n") - return pd.DataFrame() - - df_fa = pd.json_normalize(result_json) - df_fa = df_fa[list(result_json.keys())].T - - if not get_current_user().preferences.USE_INTERACTIVE_DF: - df_fa = df_fa.applymap(lambda x: lambda_long_number_format(x)) - clean_df_index(df_fa) - df_fa = df_fa.rename( - index={ - "E b i t d a": "EBITDA", - "P e ratio": "PE ratio", - "P e g ratio": "PEG ratio", - "E p s": "EPS", - "Return on equity t t m": "Return on equity TTM", - "Price to sales ratio t t m": "Price to sales ratio TTM", - } - ) - as_key_metrics = [ - "Market capitalization", - "EBITDA", - "EPS", - "PE ratio", - "PEG ratio", - "Price to book ratio", - "Return on equity TTM", - "Price to sales ratio TTM", - "Dividend yield", - "50 day moving average", - "Analyst target price", - "Beta", - ] - return df_fa.loc[as_key_metrics] - - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_income_statements( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: bool = False, -) -> pd.DataFrame: - """Get income statements for company - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number of past to get - quarterly : bool, optional - Flag to get quarterly instead of annual, by default False - ratios: bool - Shows percentage change, by default False - plot: bool - If the data shall be formatted ready to plot - - Returns - ------- - pd.DataFrame - DataFrame of income statements - """ - url = ( - f"https://www.alphavantage.co/query?function=INCOME_STATEMENT&symbol={symbol}" - f"&apikey={get_current_user().credentials.API_KEY_ALPHAVANTAGE}" - ) - r = request(url) - response_json = r.json() - if check_premium_key(response_json): - return pd.DataFrame() - - # If the returned data was unsuccessful - if "Error Message" in response_json: - console.print(response_json["Error Message"]) - # check if json is empty - elif not response_json: - console.print("No data found from Alpha Vantage, looking in Yahoo Finance\n") - if ( - yahoo_finance_model.get_financials(symbol, statement="financials") - is not None - ): - return yahoo_finance_model.get_financials(symbol, statement="financials") - else: - statements = response_json - df_fa = pd.DataFrame() - - if quarterly: - if "quarterlyReports" in statements: - df_fa = pd.DataFrame(statements["quarterlyReports"]) - elif "annualReports" in statements: - df_fa = pd.DataFrame(statements["annualReports"]) - - if df_fa.empty: - console.print("No data found from Alpha Vantage\n") - return pd.DataFrame() - - df_fa = df_fa.set_index("fiscalDateEnding") - df_fa = df_fa[::-1].T - - df_fa = df_fa.replace("None", "0") - df_fa.iloc[1:] = df_fa.iloc[1:].astype("float") - - df_fa_c = df_fa.iloc[:, -limit:].applymap( - lambda x: lambda_long_number_format(x) - ) - - if ratios: - df_fa_pc = df_fa.iloc[1:].pct_change(axis="columns").fillna(0) - j = 0 - for i in list(range(1, 25)): - df_fa.iloc[i] = df_fa_pc.iloc[j] - j += 1 - - df_fa = df_fa.iloc[:, 0:limit] - - return df_fa_c if not plot else df_fa - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_balance_sheet( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: bool = False, -) -> pd.DataFrame: - """Get balance sheets for company - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number of past to get - quarterly : bool, optional - Flag to get quarterly instead of annual, by default False - ratios: bool - Shows percentage change, by default False - plot: bool - If the data shall be formatted ready to plot - - Returns - ------- - pd.DataFrame - DataFrame of the balance sheet - """ - current_user = get_current_user() - url = ( - f"https://www.alphavantage.co/query?function=BALANCE_SHEET&symbol={symbol}&" - f"apikey={current_user.credentials.API_KEY_ALPHAVANTAGE}" - ) - r = request(url) - response_json = r.json() - if check_premium_key(response_json): - return pd.DataFrame() - # If the returned data was unsuccessful - if "Error Message" in response_json: - console.print(response_json["Error Message"]) - - # check if json is empty - if not response_json: - console.print("No data found from Alpha Vantage, looking in Yahoo Finance\n") - if ( - yahoo_finance_model.get_financials(symbol, statement="balance-sheet") - is not None - ): - return yahoo_finance_model.get_financials(symbol, statement="balance-sheet") - else: - statements = response_json - df_fa = pd.DataFrame() - - if quarterly: - if "quarterlyReports" in statements: - df_fa = pd.DataFrame(statements["quarterlyReports"]) - elif "annualReports" in statements: - df_fa = pd.DataFrame(statements["annualReports"]) - - if df_fa.empty: - console.print("No data found from Alpha Vantage\n") - return pd.DataFrame() - - df_fa = df_fa.set_index("fiscalDateEnding") - df_fa = df_fa[::-1].T - - df_fa = df_fa.replace("None", "0") - df_fa.iloc[1:] = df_fa.iloc[1:].astype("float") - - df_fa_c = df_fa.iloc[:, -limit:].applymap( - lambda x: lambda_long_number_format(x) - ) - - if ratios: - df_fa_pc = df_fa.iloc[1:].pct_change(axis="columns").fillna(0) - j = 0 - for i in list(range(1, 37)): - df_fa.iloc[i] = df_fa_pc.iloc[j] - j += 1 - - df_fa_c = df_fa.iloc[:, 0:limit].applymap( - lambda x: lambda_long_number_format(x) - ) - - df_fa = df_fa.iloc[:, 0:limit] - - return df_fa_c if not plot else df_fa - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_cash_flow( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: bool = False, -) -> pd.DataFrame: - """Get cash flows for company - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number of past to get - quarterly : bool, optional - Flag to get quarterly instead of annual, by default False - ratios: bool - Shows percentage change, by default False - plot: bool - If the data shall be formatted ready to plot - - Returns - ------- - pd.DataFrame - Dataframe of cash flow statements - """ - url = ( - f"https://www.alphavantage.co/query?function=CASH_FLOW&symbol={symbol}" - f"&apikey={get_current_user().credentials.API_KEY_ALPHAVANTAGE}" - ) - r = request(url) - response_json = r.json() - if check_premium_key(response_json): - return pd.DataFrame() - # If the returned data was unsuccessful - if "Error Message" in response_json: - console.print(response_json["Error Message"]) - elif not response_json: - console.print("No data found from Alpha Vantage, looking in Yahoo Finance\n") - - if ( - yahoo_finance_model.get_financials(symbol, statement="cash-flow") - is not None - ): - return yahoo_finance_model.get_financials(symbol, statement="cash-flow") - else: - statements = response_json - df_fa = pd.DataFrame() - - if quarterly: - if "quarterlyReports" in statements: - df_fa = pd.DataFrame(statements["quarterlyReports"]) - elif "annualReports" in statements: - df_fa = pd.DataFrame(statements["annualReports"]) - - if df_fa.empty: - console.print("No data found from Alpha Vantage\n") - return pd.DataFrame() - - df_fa = df_fa.set_index("fiscalDateEnding") - df_fa = df_fa[::-1].T - - df_fa = df_fa.replace("None", "0") - df_fa.iloc[1:] = df_fa.iloc[1:].astype("float") - - df_fa_c = df_fa.iloc[:, -limit:].applymap( - lambda x: lambda_long_number_format(x) - ) - - if ratios: - df_fa_pc = df_fa.iloc[1:].pct_change(axis="columns").fillna(0) - j = 0 - for i in list(range(1, 37)): - df_fa.iloc[i] = df_fa_pc.iloc[j] - j += 1 - - df_fa_c = df_fa.iloc[:, 0:limit].applymap( - lambda x: lambda_long_number_format(x) - ) - - df_fa = df_fa.iloc[:, 0:limit] - - return df_fa_c if not plot else df_fa - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_earnings(symbol: str, quarterly: bool = False) -> pd.DataFrame: - """Get earnings calendar for ticker - - Parameters - ---------- - symbol : str - Stock ticker symbol - quarterly : bool, optional - Flag to get quarterly and not annual, by default False - - Returns - ------- - pd.DataFrame - Dataframe of earnings - """ - # Request EARNINGS data from Alpha Vantage API - s_req = ( - "https://www.alphavantage.co/query?function=EARNINGS&" - f"symbol={symbol}&apikey={get_current_user().credentials.API_KEY_ALPHAVANTAGE}" - ) - result = request(s_req, stream=True) - result_json = result.json() - df_fa = pd.DataFrame() - - # If the returned data was unsuccessful - if "Error Message" in result_json: - console.print(result_json["Error Message"]) - # check if json is empty - elif not result_json or len(result_json) < 2: - console.print("No data found from Alpha Vantage\n") - else: - df_fa = pd.json_normalize(result_json) - - if quarterly: - df_fa = pd.DataFrame(df_fa["quarterlyEarnings"][0]) - df_fa = df_fa[ - [ - "fiscalDateEnding", - "reportedDate", - "reportedEPS", - "estimatedEPS", - "surprise", - "surprisePercentage", - ] - ] - df_fa = df_fa.rename( - columns={ - "fiscalDateEnding": "Fiscal Date Ending", - "reportedEPS": "Reported EPS", - "estimatedEPS": "Estimated EPS", - "reportedDate": "Reported Date", - "surprise": "Surprise", - "surprisePercentage": "Surprise Percentage", - } - ) - else: - df_fa = pd.DataFrame(df_fa["annualEarnings"][0]) - df_fa = df_fa.rename( - columns={ - "fiscalDateEnding": "Fiscal Date Ending", - "reportedEPS": "Reported EPS", - } - ) - - return df_fa - - -def df_values( - df: pd.DataFrame, item: str, index: int = 0, length: int = 2 -) -> List[int]: - """Clean the values from the df - - Parameters - ---------- - df : pd.DataFrame - The Dataframe to use - item : str - The item to select - index : int - The number of row to display - length : int - The number of rows to return - - Returns - ------- - values : List[int] - The values for the dataframe - """ - if index: - df = df.iloc[index : index + length] - selection = df[item] - values = selection.apply(lambda x: 0 if (not x or x == "None") else int(x)).values - return values.tolist() - - -def replace_df(name: str, row: pd.Series) -> pd.Series: - """Replaces text in pandas row based on color functions - - Return - ---------- - name : str - The name of the row - row : pd.Series - The original row - - Parameters - ---------- - new_row : pd.Series - The new formatted row - """ - for i, item in enumerate(row): - if name == "Mscore": - row[i] = color_mscore(item) - elif name in ["Zscore", "McKee"]: - row[i] = color_zscore_mckee(item) - else: - row[i] = str(round(float(item), 2)) if item != "nan" else "N/A" - return row - - -def color_mscore(value: str) -> str: - """Adds color to mscore dataframe values - - Parameters - ---------- - value : str - The string value - - Returns - ------- - new_value : str - The string formatted with rich color - """ - if value == "nan": - return "N/A" - value_float = float(value) - if value_float <= -2.22: - return f"[green]{value_float:.2f}[/green]" - if value_float <= -1.78: - return f"[yellow]{value_float:.2f}[/yellow]" - return f"[red]{value_float:.2f}[/red]" - - -def color_zscore_mckee(value: str) -> str: - """Adds color to mckee or zscore dataframe values - Parameters - ---------- - value : str - The string value - - Returns - ------- - new_value : str - The string formatted with rich color - """ - if value == "nan": - return "N/A" - value_float = float(value) - if value_float < 0.5: - return f"[red]{value_float:.2f}[/red]" - return f"[green]{value_float:.2f}[/green]" - - -@log_start_end(log=logger) -def get_fraud_ratios(symbol: str, detail: bool = False) -> pd.DataFrame: - """Get fraud ratios based on fundamentals - - Parameters - ---------- - symbol : str - Stock ticker symbol - detail : bool - Whether to provide extra m-score details - - Returns - ------- - metrics : pd.DataFrame - The fraud ratios - """ - - try: - fd = FundamentalData( - key=get_current_user().credentials.API_KEY_ALPHAVANTAGE, - output_format="pandas", - ) - # pylint: disable=unbalanced-tuple-unpacking - df_cf, _ = fd.get_cash_flow_annual(symbol=symbol) - df_bs, _ = fd.get_balance_sheet_annual(symbol=symbol) - df_is, _ = fd.get_income_statement_annual(symbol=symbol) - - except ValueError as e: - if "premium endpoint" in str(e): - console.print( - "This is a premium endpoint for AlphaVantage. Please use a premium key.\n" - ) - return pd.DataFrame() - - # pylint: disable=no-member - df_cf = df_cf.set_index("fiscalDateEnding") - df_bs = df_bs.set_index("fiscalDateEnding") - df_is = df_is.set_index("fiscalDateEnding") - fraud_years = pd.DataFrame() - for i in range(len(df_cf) - 1): - ar = df_values(df_bs, "currentNetReceivables", i) - sales = df_values(df_is, "totalRevenue", i) - cogs = df_values(df_is, "costofGoodsAndServicesSold", i) - ni = df_values(df_is, "netIncome", i) - ca = df_values(df_bs, "totalCurrentAssets", i) - cl = df_values(df_bs, "totalCurrentLiabilities", i) - ppe = df_values(df_bs, "propertyPlantEquipment", i) - cash = df_values(df_bs, "cashAndCashEquivalentsAtCarryingValue", i) - cash_and_sec = df_values(df_bs, "cashAndShortTermInvestments", i) - sec = [y - x for (x, y) in zip(cash, cash_and_sec)] - ta = df_values(df_bs, "totalAssets", i) - dep = df_values(df_bs, "accumulatedDepreciationAmortizationPPE", i) - sga = df_values(df_is, "sellingGeneralAndAdministrative", i) - tl = df_values(df_bs, "totalLiabilities", i) - icfo = df_values(df_is, "netIncomeFromContinuingOperations", i) - cfo = df_values(df_cf, "operatingCashflow", i) - - ratios: Dict = {} - try: - ratios["DSRI"] = (ar[0] / sales[0]) / (ar[1] / sales[1]) - ratios["GMI"] = ((sales[1] - cogs[1]) / sales[1]) / ( - (sales[0] - cogs[0]) / sales[0] - ) - ratios["AQI"] = (1 - ((ca[0] + ppe[0] + sec[0]) / ta[0])) / ( - 1 - ((ca[1] + ppe[1] + sec[1]) / ta[1]) - ) - ratios["SGI"] = sales[0] / sales[1] - ratios["DEPI"] = (dep[1] / (ppe[1] + dep[1])) / (dep[0] / (ppe[0] + dep[0])) - ratios["SGAI"] = (sga[0] / sales[0]) / (sga[1] / sales[1]) - ratios["LVGI"] = (tl[0] / ta[0]) / (tl[1] / ta[1]) - ratios["TATA"] = (icfo[0] - cfo[0]) / ta[0] - ratios["Mscore"] = ( - -4.84 - + (0.92 * ratios["DSRI"]) - + (0.58 * ratios["GMI"]) - + (0.404 * ratios["AQI"]) - + (0.892 * ratios["SGI"]) - + (0.115 * ratios["DEPI"] - (0.172 * ratios["SGAI"])) - + (4.679 * ratios["TATA"]) - - (0.327 * ratios["LVGI"]) - ) - - zscore = ( - -4.336 - - (4.513 * (ni[0] / ta[0])) - + (5.679 * (tl[0] / ta[0])) - + (0.004 * (ca[0] / cl[0])) - ) - v1 = np.log(ta[0] / 1000) - v2 = ni[0] / ta[0] - v3 = cash[0] / cl[0] - - x = ((v1 + 0.85) * v2) - 0.85 - y = 1 + v3 - - mckee = x**2 / (x**2 + y**2) - ratios["Zscore"] = zscore - ratios["McKee"] = mckee - except ZeroDivisionError: - for item in [ - "DSRI", - "GMI", - "AQI", - "SGI", - "DEPI", - "SGAI", - "LVGI", - "TATA", - "Mscore", - "Zscore", - "Mckee", - ]: - ratios[item] = np.nan - if fraud_years.empty: - fraud_years.index = ratios.keys() - fraud_years[df_cf.index[i]] = ratios.values() - fraud_years = fraud_years[sorted(fraud_years)] - if not detail: - details = ["DSRI", "GMI", "AQI", "SGI", "DEPI", "SGAI", "LVGI", "TATA"] - fraud_years = fraud_years.drop(details) - - return fraud_years - - -@log_start_end(log=logger) -def get_dupont(symbol: str) -> pd.DataFrame: - """Get dupont ratios - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - dupont : pd.DataFrame - The dupont ratio breakdown - """ - - try: - fd = FundamentalData( - key=get_current_user().credentials.API_KEY_ALPHAVANTAGE, - output_format="pandas", - ) - # pylint: disable=unbalanced-tuple-unpacking - df_bs, _ = fd.get_balance_sheet_annual(symbol=symbol) - df_is, _ = fd.get_income_statement_annual(symbol=symbol) - - except ValueError as e: - if "premium endpoint" in str(e): - console.print( - "This is a premium endpoint for AlphaVantage. Please use a premium key.\n" - ) - return pd.DataFrame() - - # pylint: disable=no-member - df_bs = df_bs.set_index("fiscalDateEnding") - df_is = df_is.set_index("fiscalDateEnding") - dupont_years = pd.DataFrame() - - if len(df_bs) != len(df_is): - console.print( - "The fiscal dates in the balance sheet do not correspond to the fiscal dates in the income statement." - ) - return pd.DataFrame() - - for i in range(len(df_bs)): - ni = df_values(df_is, "netIncome", i, 1) - pretax = df_values(df_is, "incomeBeforeTax", i, 1) - ebit = df_values(df_is, "ebit", i, 1) - sales = df_values(df_is, "totalRevenue", i, 1) - assets = df_values(df_bs, "totalAssets", i, 1) - equity = df_values(df_bs, "totalShareholderEquity", i, 1) - - ratios: Dict = {} - try: - ratios["Tax Burden"] = clean_fraction(ni[0], pretax[0]) - ratios["Interest Burden"] = clean_fraction(pretax[0], ebit[0]) - ratios["EBIT Margin"] = clean_fraction(ebit[0], sales[0]) - ratios["Asset Turnover"] = clean_fraction(sales[0], assets[0]) - ratios["Finance Leverage"] = clean_fraction(assets[0], equity[0]) - ratios["ROI"] = clean_fraction(ni[0], equity[0]) - except IndexError: - pass - - if dupont_years.empty: - dupont_years.index = ratios.keys() - dupont_years[df_bs.index[i]] = ratios.values() - dupont_years = dupont_years[sorted(dupont_years)] - return dupont_years diff --git a/openbb_terminal/stocks/fundamental_analysis/av_view.py b/openbb_terminal/stocks/fundamental_analysis/av_view.py deleted file mode 100644 index c00518af9af..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/av_view.py +++ /dev/null @@ -1,555 +0,0 @@ -""" Alpha Vantage View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.fundamental_analysis import av_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_overview(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Alpha Vantage stock ticker overview - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - """ - df_fa = av_model.get_overview(symbol) - if df_fa.empty: - console.print("No API calls left. Try me later", "\n") - return - - print_rich_table( - df_fa, - title=f"{symbol} Overview", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "overview", - df_fa, - sheet_name, - ) - - console.print(f"Company Description:\n\n{df_fa.loc['Description'][0]}") - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_key(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Alpha Vantage key metrics - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - """ - df_key = av_model.get_key_metrics(symbol) - - if df_key.empty: - return - - print_rich_table( - df_key, - headers=[symbol], - title=f"{symbol} Key Metrics", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "key", - df_key, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_income_statement( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Alpha Vantage income statement - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number of past statements, by default 5 - quarterly: bool - Flag to get quarterly instead of annual, by default False - ratios: bool - Shows percentage change, by default False - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - fig = OpenBBFigure() - - df_income = av_model.get_income_statements( - symbol, limit, quarterly, ratios, bool(plot) - ) - - if df_income.empty: - return - - df_income.index = [ - stocks_helper.INCOME_PLOT["AlphaVantage"][i] - for i in [i.replace(" ", "_") for i in df_income.index.str.lower()] - ] - - if plot: - rows_plot = len(plot) - income_plot_data = df_income.transpose() - - if rows_plot == 1: - fig = OpenBBFigure() - fig.add_scatter( - x=income_plot_data.index, - y=income_plot_data[plot[0]], - name=plot[0].replace("_", ""), - ) - fig.set_title( - f"{plot[0].replace('_', ' ').lower()} {'QoQ' if quarterly else 'YoY'} Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ')} of {symbol.upper()}" - ) - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_scatter( - x=income_plot_data.index, - y=income_plot_data[plot[i]], - name=plot[i].replace("_", ""), - row=i + 1, - col=1, - ) - fig.set_title(f"{plot[i].replace('_', ' ')}", row=i + 1, col=1) - - fig.show(external=fig.is_image_export(export)) - - else: - # Snake case to english - df_income.index = [x.replace("_", " ").title() for x in df_income.index] - - print_rich_table( - df_income, - headers=list(df_income.columns), - title=( - f"{symbol} Income Statement" - if not ratios - else f"{'QoQ' if quarterly else 'YoY'} Change of {symbol} Income Statement" - ), - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "income", - df_income.transpose(), - sheet_name, - fig, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_balance_sheet( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Alpha Vantage balance sheet statement - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number of past statements, by default 5 - quarterly: bool - Flag to get quarterly instead of annual, by default False - ratios: bool - Shows percentage change, by default False - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - fig = OpenBBFigure() - - df_balance = av_model.get_balance_sheet( - symbol, limit, quarterly, ratios, bool(plot) - ) - - if df_balance.empty: - return - - df_balance.index = [ - stocks_helper.BALANCE_PLOT["AlphaVantage"][i] - for i in [i.replace(" ", "_") for i in df_balance.index.str.lower()] - ] - - if plot: - rows_plot = len(plot) - balance_plot_data = df_balance.transpose() - - if rows_plot == 1: - fig = OpenBBFigure() - fig.set_title( - f"{plot[0].replace('_', ' ').lower()} {'QoQ' if quarterly else 'YoY'} Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ')} of {symbol.upper()}" - ) - fig.add_scatter( - x=balance_plot_data.index, - y=balance_plot_data[plot[0]], - name=plot[0].replace("_", ""), - ) - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_scatter( - x=balance_plot_data.index, - y=balance_plot_data[plot[i]], - name=plot[i].replace("_", ""), - row=i + 1, - col=1, - ) - fig.set_title(f"{plot[i].replace('_', ' ')}", row=i + 1, col=1) - - fig.show(external=fig.is_image_export(export)) - else: - # Snake case to english - df_balance.index = [x.replace("_", " ").title() for x in df_balance.index] - - print_rich_table( - df_balance, - headers=list(df_balance.columns), - title=( - f"{symbol} Balance Sheet" - if not ratios - else f"{'QoQ' if quarterly else 'YoY'} Change of {symbol} Balance Sheet" - ), - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "balance", - df_balance, - sheet_name, - fig, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_cash_flow( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Alpha Vantage income statement - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number of past statements, by default 5 - quarterly: bool - Flag to get quarterly instead of annual, by default False - ratios: bool - Shows percentage change, by default False - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - fig = OpenBBFigure() - - df_cash = av_model.get_cash_flow(symbol, limit, quarterly, ratios, bool(plot)) - - if df_cash.empty: - return - - df_cash.index = [ - stocks_helper.CASH_PLOT["AlphaVantage"][i] - for i in [i.replace(" ", "_") for i in df_cash.index.str.lower()] - ] - - if plot: - rows_plot = len(plot) - cash_plot_data = df_cash.transpose() - - if rows_plot == 1: - fig = OpenBBFigure() - fig.set_title( - f"{plot[0].replace('_', ' ').lower()} {'QoQ' if quarterly else 'YoY'} Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ')} of {symbol.upper()}" - ) - fig.add_scatter( - x=cash_plot_data.index, - y=cash_plot_data[plot[0]], - name=plot[0].replace("_", ""), - ) - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_scatter( - x=cash_plot_data.index, - y=cash_plot_data[plot[i]], - name=plot[i].replace("_", ""), - row=i + 1, - col=1, - ) - fig.set_title(f"{plot[i].replace('_', ' ')}", row=i + 1, col=1) - - fig.show(external=fig.is_image_export(export)) - else: - # Snake case to english - df_cash.index = [x.replace("_", " ").title() for x in df_cash.index] - - print_rich_table( - df_cash, - headers=list(df_cash.columns), - title=( - f"{symbol} Cash flow" - if not ratios - else f"{'QoQ' if quarterly else 'YoY'} Change of {symbol} Cash flow" - ), - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cash", - df_cash, - sheet_name, - fig, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_earnings( - symbol: str, - limit: int = 5, - quarterly: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Alpha Vantage earnings - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit:int - Number of events to show - quarterly: bool - Flag to show quarterly instead of annual - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - df_fa = av_model.get_earnings(symbol, quarterly) - - if df_fa.empty: - return - - print_rich_table( - df_fa, - headers=list(df_fa.columns), - show_index=False, - title=f"{symbol} Earnings", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "earnings", - df_fa, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_fraud( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - help_text: bool = False, - color: bool = True, - detail: bool = False, -): - """Fraud indicators for given ticker - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - export : str - Whether to export the dupont breakdown - help_text : bool - Whether to show help text - color : bool - Whether to show color in the dataframe - detail : bool - Whether to show the details for the mscore - """ - current_user = get_current_user() - enable_interactive = ( - current_user.preferences.USE_INTERACTIVE_DF and plots_backend().isatty - ) - df = av_model.get_fraud_ratios(symbol, detail=detail) - - if df.empty: - console.print("[red]No data found[/red]") - return - - df_color = df.copy() - if color and not enable_interactive: - for column in df_color: - df_color[column] = df_color[column].astype(str) - df_color = df_color.apply(lambda x: av_model.replace_df(x.name, x), axis=1) - df_color = df_color.fillna("N/A") - - print_rich_table( - df_color, - headers=list(df_color.columns), - show_index=True, - title="Fraud Risk Statistics", - export=bool(export), - ) - - help_message = """ -MSCORE: -An mscore above -1.78 indicates a high risk of fraud, and one above -2.22 indicates a medium risk of fraud. - -ZSCORE: -A zscore less than 0.5 indicates a high risk of fraud. - -Mckee: -A mckee less than 0.5 indicates a high risk of fraud. - """ - - if help_text: - console.print(help_message) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "fraud", - df, - sheet_name, - ) - return - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_ALPHAVANTAGE"]) -def display_dupont( - symbol: str, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Shows the extended dupont ratio - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - raw : str - Show raw data instead of a graph - export : bool - Whether to export the dupont breakdown - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df = av_model.get_dupont(symbol) - if df.empty: - return None - if raw: - return print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title="Extended Dupont", - export=bool(export), - ) - - fig = OpenBBFigure().set_title("Extended Dupont by Year") - - df = df.transpose() - for column in df: - fig.add_scatter( - x=df.index, - y=df[column], - name=column, - mode="lines", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dupont", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/fundamental_analysis/business_insider_model.py b/openbb_terminal/stocks/fundamental_analysis/business_insider_model.py deleted file mode 100644 index f83e57c89e6..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/business_insider_model.py +++ /dev/null @@ -1,253 +0,0 @@ -"""Business Insider Model""" - -__docformat__ = "numpy" - -import json -import logging -from typing import Tuple - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_management(symbol: str) -> pd.DataFrame: - """Get company managers from Business Insider - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - Dataframe of managers - """ - url_market_business_insider = ( - f"https://markets.businessinsider.com/stocks/{symbol.lower()}-stock" - ) - text_soup_market_business_insider = BeautifulSoup( - request( - url_market_business_insider, headers={"User-Agent": get_user_agent()} - ).text, - "lxml", - ) - - found_h2s = {} - - for next_h2 in text_soup_market_business_insider.findAll( - "h2", {"class": "header-underline"} - ): - next_table = next_h2.find_next_sibling("table", {"class": "table"}) - - if next_table: - found_h2s[next_h2.text] = next_table - - # Business Insider changed management display convention from 'Management' to - # 'Ticker Management'. These next few lines simply find 'Ticker Management' - # header key and copy it to a 'Management' key as to not alter the rest of - # the function - ticker_management_to_be_deleted = "" - management_data_available = False - for key in found_h2s: - if "Management" in key: - ticker_management_to_be_deleted = key - management_data_available = True - if management_data_available: - found_h2s["Management"] = found_h2s[ticker_management_to_be_deleted] - del found_h2s[ticker_management_to_be_deleted] - - if found_h2s.get("Management") is None: - console.print( - f"[red]No management information in Business Insider for {symbol}[/red]" - ) - return pd.DataFrame() - - l_titles = [ - s_title.text.strip() - for s_title in found_h2s["Management"].findAll( - "td", {"class": "table__td text-right"} - ) - if any(c.isalpha() for c in s_title.text.strip()) - and ("USD" not in s_title.text.strip()) - ] - - l_names = [ - s_name.text.strip() - for s_name in found_h2s["Management"].findAll( - "td", {"class": "table__td table--allow-wrap"} - ) - ] - - df_management = pd.DataFrame( - {"Name": l_names[-len(l_titles) :], "Title": l_titles}, # noqa: E203 - columns=["Name", "Title"], - ) - - df_management["Info"] = "-" - df_management = df_management.set_index("Name") - - for s_name in df_management.index: - df_management.loc[s_name]["Info"] = ( - f"http://www.google.com/search?q={s_name} {symbol.upper()}".replace( - " ", "%20" - ) - ) - - return df_management - - -@log_start_end(log=logger) -def get_price_target_from_analysts(symbol: str) -> pd.DataFrame: - """Get analysts' price targets for a given stock. [Source: Business Insider] - - Parameters - ---------- - symbol : str - Ticker symbol - - Returns - ------- - pd.DataFrame - Analysts data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> df = openbb.stocks.fa.pt(symbol="AAPL") - """ - url_market_business_insider = ( - f"https://markets.businessinsider.com/stocks/{symbol.lower()}-stock" - ) - text_soup_market_business_insider = BeautifulSoup( - request( - url_market_business_insider, headers={"User-Agent": get_user_agent()} - ).text, - "lxml", - ) - - d_analyst_data = None - for script in text_soup_market_business_insider.find_all("script"): - # Get Analyst data - if "window.analyseChartConfigs.push" in str(script): - # Extract config data: - s_analyst_data = str(script).split("config: ", 1)[1].split(",\r\n", 1)[0] - d_analyst_data = json.loads(s_analyst_data.split(",\n")[0]) - break - - try: - df_analyst_data = pd.DataFrame.from_dict(d_analyst_data["Markers"]) # type: ignore - except TypeError: - return pd.DataFrame() - df_analyst_data = df_analyst_data[ - ["DateLabel", "Company", "InternalRating", "PriceTarget"] - ] - df_analyst_data.columns = ["Date", "Company", "Rating", "Price Target"] - # df_analyst_data - df_analyst_data["Rating"].replace( - {"gut": "BUY", "neutral": "HOLD", "schlecht": "SELL"}, inplace=True - ) - df_analyst_data["Date"] = pd.to_datetime(df_analyst_data["Date"]) - df_analyst_data = df_analyst_data.set_index("Date") - - return df_analyst_data - - -@log_start_end(log=logger) -def get_estimates(symbol: str) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: - """Get analysts' estimates for a given ticker. [Source: Business Insider] - - Parameters - ---------- - symbol : str - Ticker to get analysts' estimates - - Returns - ------- - df_year_estimates : pd.DataFrame - Year estimates - df_quarter_earnings : pd.DataFrame - Quarter earnings estimates - df_quarter_revenues : pd.DataFrame - Quarter revenues estimates - - Returns - ------- - Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame] - Year estimates, quarter earnings estimates, quarter revenues estimates - """ - url_market_business_insider = ( - f"https://markets.businessinsider.com/stocks/{symbol.lower()}-stock" - ) - text_soup_market_business_insider = BeautifulSoup( - request( - url_market_business_insider, headers={"User-Agent": get_user_agent()} - ).text, - "lxml", - ) - - # Get all tables and convert them to list of pandas dataframes - tables = text_soup_market_business_insider.find_all("table") - list_df = pd.read_html(str(tables)) - - # Get year estimates - df_year_estimates = list_df[3] - l_year_estimates_columns = df_year_estimates.columns.tolist() - l_year_estimates_columns[0] = "YEARLY ESTIMATES" - df_year_estimates.columns = l_year_estimates_columns - df_year_estimates.set_index("YEARLY ESTIMATES", inplace=True) - - df_quarter = list_df[4] - date_row = dict() - - # Get quarter earnings estimates - df_quarter_earnings = df_quarter.iloc[0:5, :].reset_index(drop=True).copy() - df_quarter_earnings.drop(index=0, inplace=True) - l_quarter_earnings_columns = df_quarter_earnings.columns.tolist() - l_quarter_earnings_columns[0] = "QUARTER EARNINGS ESTIMATES" - date_row["QUARTER EARNINGS ESTIMATES"] = "Date" - - # Adding Date info to add to dataframe - for col in l_quarter_earnings_columns[1:]: - key = col.split("ending")[0].strip() - value = col[col.find("ending") :].strip() - date_row[key] = value - - df_quarter_earnings.columns = date_row.keys() - date_row = pd.DataFrame(date_row, index=[0]) - df_quarter_earnings = pd.concat([date_row, df_quarter_earnings]).reset_index( - drop=True - ) - df_quarter_earnings.set_index("QUARTER EARNINGS ESTIMATES", inplace=True) - - # Setting date_row to empty dict object - date_row = dict() - - # Get quarter revenues estimates - df_quarter_revenues = df_quarter.iloc[5:, :].reset_index(drop=True).copy() - df_quarter_revenues.drop(index=0, inplace=True) - l_quarter_revenues_columns = df_quarter_revenues.columns.tolist() - l_quarter_revenues_columns[0] = "QUARTER REVENUES ESTIMATES" - date_row["QUARTER REVENUES ESTIMATES"] = "Date" - - # Adding Date info to add to dataframe - for col in l_quarter_revenues_columns[1:]: - key = col.split("ending")[0].strip() - value = col[col.find("ending") :].strip() - date_row[key] = value - - df_quarter_revenues.columns = date_row.keys() - date_row = pd.DataFrame(date_row, index=[0]) - df_quarter_revenues = pd.concat([date_row, df_quarter_revenues]).reset_index( - drop=True - ) - df_quarter_revenues.set_index("QUARTER REVENUES ESTIMATES", inplace=True) - - return df_year_estimates, df_quarter_earnings, df_quarter_revenues diff --git a/openbb_terminal/stocks/fundamental_analysis/business_insider_view.py b/openbb_terminal/stocks/fundamental_analysis/business_insider_view.py deleted file mode 100644 index 6c9620878d3..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/business_insider_view.py +++ /dev/null @@ -1,290 +0,0 @@ -""" Business Insider View """ - -__docformat__ = "numpy" - -import logging -import os -import textwrap -from datetime import datetime, timedelta -from functools import partial -from typing import Optional, Union - -import pandas as pd -from pandas.core.frame import DataFrame - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.common.technical_analysis import ta_helpers -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import ( - business_insider_model, - yahoo_finance_model, -) -from openbb_terminal.stocks.stocks_helper import load - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_management(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Display company's managers - - Parameters - ---------- - symbol : str - Stock ticker symbol - export : str - Format to export data - """ - df_management = business_insider_model.get_management(symbol) - - df_new = df_management.applymap( - lambda x: "\n".join(textwrap.wrap(x, width=30)) if isinstance(x, str) else x - ) - - if not df_new.empty: - print_rich_table( - df_new, - title="Company Managers", - headers=list(df_new.columns), - show_index=True, - index_name="Name", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mgmt", - df_management, - sheet_name, - ) - else: - logger.error("Data not available") - - -# pylint: disable=R0913 -@log_start_end(log=logger) -def display_price_target_from_analysts( - symbol: str, - data: Optional[DataFrame] = None, - start_date: Optional[str] = None, - limit: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, - adjust_for_splits: bool = True, -) -> Union[OpenBBFigure, None]: - """Display analysts' price targets for a given stock. [Source: Business Insider] - - Parameters - ---------- - symbol: str - Due diligence ticker symbol - data: Optional[DataFrame] - Price target DataFrame - start_date : Optional[str] - Start date of the stock data, format YYYY-MM-DD - limit : int - Number of latest price targets from analysts to print - raw: bool - Display raw data only - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx file - external_axes: bool, optional - Whether to return the figure object or not, by default False - adjust_for_splits: bool - Whether to adjust analyst price targets for stock splits, by default True - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.fa.pt_chart(symbol="AAPL") - """ - - def adjust_splits(row, splits: pd.DataFrame): - original_value = row["Price Target"] - for index, sub_row in splits.iterrows(): - if row.name < index: - original_value = original_value / sub_row["Stock Splits"] - return round(original_value, 2) - - if start_date is None: - start_date = (datetime.now() - timedelta(days=1100)).strftime("%Y-%m-%d") - - if data is None: - data = load(symbol=symbol, start_date=start_date) - - df_analyst_data = business_insider_model.get_price_target_from_analysts(symbol) - if df_analyst_data.empty: - return console.print("[red]Could not get data for ticker.[/red]\n") - if adjust_for_splits: - df_splits = yahoo_finance_model.get_splits(symbol) - if not df_splits.empty: - df_splits.index = df_splits.index.tz_convert(None) - adjusted_splits = partial(adjust_splits, splits=df_splits) - df_analyst_data["Price Target"] = df_analyst_data.apply( - adjusted_splits, axis=1 - ) - - fig = OpenBBFigure(yaxis_title="Share Price").set_title( - f"{symbol} (Time Series) and Price Target" - ) - close_col = ta_helpers.check_columns(data, False, False) - df_analyst_plot = df_analyst_data.copy() - - # Slice start of ratings - if start_date: - df_analyst_plot = df_analyst_plot[start_date:] # type: ignore - - fig.add_scatter( - x=data.index, - y=data[close_col].values, - name="Close", - line_width=2, - ) - - df_grouped = df_analyst_plot.groupby(by=["Date"]).mean(numeric_only=True) - - fig.add_scatter( - x=df_grouped.index, - y=df_grouped["Price Target"].values, - name="Average Price Target", - ) - - colors = df_analyst_plot["Rating"].apply( - lambda x: ( - theme.up_color - if x == "BUY" - else theme.down_color if x == "SELL" else "#b3a8a8" - ) - ) - - fig.add_scatter( - x=df_analyst_plot.index, - y=df_analyst_plot["Price Target"].values, - name="Price Target", - mode="markers", - customdata=df_analyst_plot.apply( - lambda row: "
".join( - [ - f"${x['Price Target']} - {x['Company']} ({x['Rating']})" - for _, x in df_analyst_plot[df_analyst_plot.index == row.name] - .sort_values(by="Price Target", ascending=False) - .iterrows() - ] - ), - axis=1, - ), - hovertemplate="
%{customdata}", - marker=dict( - color=colors, - line=dict(width=1, color="DarkSlateGrey"), - size=10, - ), - line=dict(color=theme.get_colors()[1]), - ) - - fig.update_layout(hovermode="x unified") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "pt", - df_analyst_data, - sheet_name, - fig, - ) - - if raw: - df_analyst_data.index = df_analyst_data.index.strftime("%Y-%m-%d") - return print_rich_table( - df_analyst_data.sort_index(ascending=False), - headers=list(df_analyst_data.columns), - show_index=True, - title="Analyst Price Targets", - export=bool(export), - limit=limit, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_estimates( - symbol: str, estimate: str, export: str = "", sheet_name: Optional[str] = None -): - """Display analysts' estimates for a given ticker. [Source: Business Insider] - - Parameters - ---------- - symbol : str - Ticker to get analysts' estimates - estimate: str - Type of estimate to get - export : str - Export dataframe data to csv,json,xlsx file - """ - ( - df_year_estimates, - df_quarter_earnings, - df_quarter_revenues, - ) = business_insider_model.get_estimates(symbol) - - if estimate == "annual_earnings": - print_rich_table( - df_year_estimates, - headers=list(df_year_estimates.columns), - show_index=True, - title="Annual Earnings Estimates", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "pt_year", - df_year_estimates, - sheet_name, - ) - - elif estimate == "quarter_earnings": - print_rich_table( - df_quarter_earnings, - headers=list(df_quarter_earnings.columns), - show_index=True, - title="Quarterly Earnings Estimates", - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "pt_qtr_earnings", - df_quarter_earnings, - sheet_name, - ) - - elif estimate == "quarter_revenues": - print_rich_table( - df_quarter_revenues, - headers=list(df_quarter_revenues.columns), - show_index=True, - title="Quarterly Revenue Estimates", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "pt_qtr_revenues", - df_quarter_revenues, - sheet_name, - ) - else: - console.print("[red]Invalid estimate type[/red]") diff --git a/openbb_terminal/stocks/fundamental_analysis/csimarket_model.py b/openbb_terminal/stocks/fundamental_analysis/csimarket_model.py deleted file mode 100644 index ea4be6bf5d2..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/csimarket_model.py +++ /dev/null @@ -1,82 +0,0 @@ -"""CSIMarket Model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -def clean_table(df: pd.DataFrame) -> pd.DataFrame: - """Clean up the table from CSIMarket - - Parameters - ---------- - df: pd.DataFrame - Dataframe to clean - - Returns - ------- - df: pd.DataFrame - Cleaned dataframe - """ - if df.empty: - return df - if "TICKER" in df.columns: - df = df.set_index("TICKER") - df.columns = [x.title() for x in df.columns] - if "Company Name.1" in df.columns: - df = df.drop("Company Name.1", axis=1) - if "SUBTOTAL" in df.index: - df = df.drop("SUBTOTAL", axis=0) - if "Revenue" in df.columns: - df = df.sort_values("Revenue", ascending=False) - df = df[df.index.notnull()] - return df - - -@log_start_end(log=logger) -def get_suppliers(symbol: str) -> pd.DataFrame: - """Get suppliers from ticker provided. [Source: CSIMarket] - - Parameters - ---------- - symbol: str - Ticker to select suppliers from - - Returns - ------- - pd.DataFrame - A dataframe of suppliers - """ - url = f"https://csimarket.com/stocks/competition2.php?supply&code={symbol.upper()}" - dfs = pd.read_html(url, header=0) - df = dfs[10] - df = clean_table(df) - return df - - -@log_start_end(log=logger) -def get_customers(symbol: str) -> pd.DataFrame: - """Print customers from ticker provided - - Parameters - ---------- - symbol: str - Ticker to select customers from - - Returns - ------- - pd.DataFrame - A dataframe of suppliers - """ - url = f"https://csimarket.com/stocks/custexNO.php?markets&code={symbol.upper()}" - dfs = pd.read_html(url, header=0) - df = dfs[9] - df = clean_table(df) - - return df diff --git a/openbb_terminal/stocks/fundamental_analysis/csimarket_view.py b/openbb_terminal/stocks/fundamental_analysis/csimarket_view.py deleted file mode 100644 index c975fe29a61..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/csimarket_view.py +++ /dev/null @@ -1,89 +0,0 @@ -"""CSIMarket View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import csimarket_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def suppliers( - symbol: str, export: str = "", sheet_name: Optional[str] = None, limit: int = 10 -) -> None: - """Display suppliers from ticker provided. [Source: CSIMarket] - - Parameters - ---------- - symbol: str - Ticker to select suppliers from - export : str - Export dataframe data to csv,json,xlsx file - limit: int - The maximum number of rows to show - """ - tickers = csimarket_model.get_suppliers(symbol) - - if tickers.empty: - console.print("No suppliers found.\n") - else: - print_rich_table( - tickers, - headers=list(tickers.columns), - show_index=True, - title=f"Suppliers for {symbol.upper()}", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "supplier", - tickers, - sheet_name, - ) - - -@log_start_end(log=logger) -def customers(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Display customers from ticker provided. [Source: CSIMarket] - - Parameters - ---------- - symbol: str - Ticker to select customers from - export : str - Export dataframe data to csv,json,xlsx file - """ - tickers = csimarket_model.get_customers(symbol) - if tickers.empty: - console.print("No customers found.\n") - else: - # Table is a bit weird so need to change the columns header - tickers.columns = tickers.iloc[1] - tickers = tickers.iloc[2:] - print_rich_table( - tickers, - headers=list(tickers.columns), - show_index=True, - title=f"Customers for {symbol.upper()}", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "customer", - pd.DataFrame(tickers), - sheet_name, - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/dcf_model.py b/openbb_terminal/stocks/fundamental_analysis/dcf_model.py deleted file mode 100644 index 043844372f8..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/dcf_model.py +++ /dev/null @@ -1,611 +0,0 @@ -""" DCF Model """ - -__docformat__ = "numpy" - -import logging -import os -from io import BytesIO -from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple, Union -from urllib.request import urlopen -from zipfile import ZipFile - -import financedatabase as fd -import pandas as pd -import yfinance as yf -from bs4 import BeautifulSoup -from openpyxl import worksheet -from sklearn.linear_model import LinearRegression - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import compose_export_path, request -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import dcf_static - -logger = logging.getLogger(__name__) - -CURRENCIES = [ - "ALL", - "AFN", - "ARS", - "AWG", - "AUD", - "AZN", - "BSD", - "BBD", - "BYN", - "BZD", - "BMD", - "BOB", - "BAM", - "BWP", - "BGN", - "BRL", - "BND", - "KHR", - "CAD", - "KYD", - "CLP", - "CNY", - "COP", - "CRC", - "HRK", - "CUP", - "CZK", - "DKK", - "DOP", - "XCD", - "EGP", - "SVC", - "EUR", - "FKP", - "FJD", - "GHS", - "GIP", - "GTQ", - "GGP", - "GYD", - "HNL", - "HKD", - "HUF", - "ISK", - "INR", - "IDR", - "IRR", - "IMP", - "ILS", - "JMD", - "JPY", - "JEP", - "KZT", - "KPW", - "KRW", - "KGS", - "LAK", - "LBP", - "LRD", - "MKD", - "MYR", - "MUR", - "MXN", - "MNT", - "MNT", - "MZN", - "NAD", - "NPR", - "ANG", - "NZD", - "NIO", - "NGN", - "NOK", - "OMR", - "PKR", - "PAB", - "PYG", - "PEN", - "PHP", - "PLN", - "QAR", - "RON", - "RUB", - "SHP", - "SAR", - "RSD", - "SCR", - "SGD", - "SBD", - "SOS", - "KRW", - "ZAR", - "LKR", - "SEK", - "CHF", - "SRD", - "SYP", - "TWD", - "THB", - "TTD", - "TRY", - "TVD", - "UAH", - "AED", - "GBP", - "USD", - "UYU", - "UZS", - "VEF", - "VND", - "YER", - "ZWD", -] - - -def string_float(string: str) -> float: - """Convert a string to a float - - Parameters - ---------- - string : str - String to be converted - - Returns - ------- - number : float - Analysis of filings text - """ - if string.strip().replace(",", "").replace("-", "") == "": - return 0 - return float(string.strip().replace(",", "").replace("-", "")) - - -def insert_row( - name: str, index: str, df: pd.DataFrame, row_v: List[str] -) -> pd.DataFrame: - """Allows a row to be added given an index and name - - Parameters - ---------- - name : str - Name to be added to df - index : str - The row the new item will go after - df : pd.DataFrame - The dataframe to be modified - row_v : List[str] - The items to be added to the row - - Returns - ------- - new_df : pd.DataFrame - The new dataframe - """ - pd.options.mode.chained_assignment = None - if name not in df.index: - row_number = df.index.get_loc(index) + 1 - df1 = df[0:row_number] - df2 = df[row_number:] - df1.loc[name] = row_v - df_result = pd.concat([df1, df2]) - return df_result - return df - - -def set_cell( - ws: worksheet, - cell: str, - text: Optional[Union[int, str, float]] = None, - font: Optional[str] = None, - border: Optional[str] = None, - fill: Optional[str] = None, - alignment: Optional[str] = None, - num_form: Optional[str] = None, -): - """Set the value for a cell - - Parameters - ---------- - ws : worksheet - The worksheet to be modified - cell : str - The cell that will be modified - text : Union[int, str, float] - The new value of the cell - font : str - The type of font - border : str - The type of border - fill : str - The type of fill - alignment : str - The type of alignment - num_form : str - The format for numbers - """ - if text: - ws[cell] = text - if font: - ws[cell].font = font - if border: - ws[cell].border = border - if fill: - ws[cell].fill = fill - if alignment: - ws[cell].alignment = alignment - if num_form: - ws[cell].number_format = num_form - - -@log_start_end(log=logger) -def get_fama_raw() -> pd.DataFrame: - """Get Fama French data - - Returns - ------- - df : pd.DataFrame - Fama French data - """ - with urlopen( # noqa: SIM117,S310 - "https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_CSV.zip" - ) as url: - # Download Zipfile and create pandas DataFrame - with ZipFile(BytesIO(url.read())) as zipfile: - with zipfile.open("F-F_Research_Data_Factors.CSV") as zip_open: - df = pd.read_csv( - zip_open, - header=0, - names=["Date", "MKT-RF", "SMB", "HML", "RF"], - skiprows=3, - ) - - df = df[df["Date"].apply(lambda x: len(str(x).strip()) == 6)] - df["Date"] = df["Date"].astype(str) + "01" - df["Date"] = pd.to_datetime(df["Date"], format="%Y%m%d") - df["MKT-RF"] = pd.to_numeric(df["MKT-RF"], downcast="float") - df["SMB"] = pd.to_numeric(df["SMB"], downcast="float") - df["HML"] = pd.to_numeric(df["HML"], downcast="float") - df["RF"] = pd.to_numeric(df["RF"], downcast="float") - df["MKT-RF"] = df["MKT-RF"] / 100 - df["SMB"] = df["SMB"] / 100 - df["HML"] = df["HML"] / 100 - df["RF"] = df["RF"] / 100 - df = df.set_index("Date") - return df - - -@log_start_end(log=logger) -def get_historical_5(symbol: str) -> pd.DataFrame: - """Get 5 year monthly historical performance for a ticker with dividends filtered - - Parameters - ---------- - symbol: str - The ticker symbol to be analyzed - - Returns - ------- - df: pd.DataFrame - Historical data - """ - tick = yf.Ticker(symbol) - df = tick.history(period="5y", interval="1mo") - df = df[df.index.to_series().apply(lambda x: x.day == 1)] - df = df.drop(["Dividends", "Stock Splits"], axis=1) - df = df.dropna() - df.index = [d.replace(tzinfo=None) for d in df.index] - - return df - - -@log_start_end(log=logger) -def get_fama_coe(symbol: str) -> float: - """Use Fama and French to get the cost of equity for a company - - Parameters - ---------- - symbol : str - The ticker symbol to be analyzed - - Returns - ------- - coef : float - The stock's Fama French coefficient - """ - df_f = get_fama_raw() - df_h = get_historical_5(symbol) - df = df_h.join(df_f) - df = df.dropna() - df["Monthly Return"] = df["Close"].pct_change() - df["Excess Monthly Return"] = df["Monthly Return"] - df["RF"] - df = df.dropna() - x = df[["MKT-RF", "SMB", "HML"]] - y = df["Excess Monthly Return"] - - model = LinearRegression().fit(x, y) - coefs = model.coef_ - return ( - df["RF"].mean() - + coefs[0] * df["MKT-RF"].mean() - + coefs[1] * df["SMB"].mean() - + coefs[2] * df["HML"].mean() - ) * 12 - - -@log_start_end(log=logger) -def others_in_sector( - symbol: str, - sector: str, - industry_group: str, - industry: str, - no_filter: bool = False, -) -> List[str]: - """Get other stocks in a ticker's sector - - Parameters - ---------- - symbol: str - The ticker symbol to be excluded - sector: str - The sector to pull from - industry: str - The industry to pull from - no_filter: bool - True means that we do not filter based on market cap - - Returns - ------- - List[str] - List of symbols in the same sector - """ - industry = industry.replace("—", " - ") - industry = industry.replace("/", " ") - - equities = fd.Equities() - similars = equities.select( - sector=sector, industry_group=industry_group, industry=industry - ) - - # This filters similars to match market cap and removes ticker analyzed - if symbol in similars.index: - market_cap = similars.loc[symbol]["market_cap"] - similars.drop([symbol], inplace=True) - if not no_filter: - similars = { - k: v for k, v in similars.iterrows() if v["market_cap"] == market_cap - } - similars = list(similars) - return similars - - -def create_dataframe(symbol: str, statement: str, period: str = "annual"): - """ - Creates a df financial statement for a given ticker - - Parameters - ---------- - symbol : str - The ticker symbol to create a dataframe for - statement : str - The financial statement dataframe to create - period : str - Whether to look at annual, quarterly, or trailing - - Returns - ------- - statement : pd.DataFrame - The financial statement requested - rounding : int - The amount of rounding to use - statement_currency: str - The currency the financial statements are reported in - """ - if statement not in ["BS", "CF", "IS"]: - raise ValueError("statement variable must be 'BS', 'CF' or 'IS'") - if period not in ["annual", "quarterly", "trailing"]: - raise ValueError( - "statement variable must be 'annual','quarterly', or 'trailing'" - ) - per_url = f"{period}/" if period != "annual" else "" - - URL = f"https://stockanalysis.com/stocks/{symbol}/financials/" - URL += dcf_static.statement_url[statement] + per_url - ignores = dcf_static.statement_ignore[statement] - - r = request(URL, headers=dcf_static.headers) - - if r.status_code == 429: - console.print("Too many requests, please try again later") - return pd.DataFrame(), None, None - - if "404 - Page Not Found" in r.text: - return pd.DataFrame(), None, None - - try: - df = pd.read_html(r.text)[0] - except ValueError: - return pd.DataFrame(), None, None - - soup = BeautifulSoup(r.content, "html.parser") - phrase = soup.find( - "div", attrs={"class": "hidden pb-1 text-sm text-faded lg:block"} - ) - phrase = phrase.get_text().lower() if phrase else "" - - if "thousand" in phrase: - rounding = 1_000 - elif "millions" in phrase: - rounding = 1_000_000 - elif "billions" in phrase: - rounding = 1_000_000_000 - else: - return pd.DataFrame(), None, None - - statement_currency = "" - for currency in CURRENCIES: - if currency.lower() in phrase: - statement_currency = currency - break - - if "Quarter Ended" in df.columns: - df = df.set_index("Quarter Ended") - elif "Quarter Ending" in df.columns: - df = df.set_index("Quarter Ending") - else: - df = df.set_index("Year") - df = df.loc[:, ~(df == "Upgrade").any()] - - for ignore in ignores: - if ignore in df.index: - df = df.drop([ignore]) - df = df[df.columns[::-1]] - - if statement == "IS": - vals = ["Revenue", dcf_static.gaap_is] - elif statement == "BS": - vals = ["Cash & Equivalents", dcf_static.gaap_bs] - elif statement == "CF": - vals = ["Net Income", dcf_static.gaap_cf] - - if vals[0] in df.index: - blank_list = ["0" for _ in df.loc[vals[0]].to_list()] - else: - return pd.DataFrame(), None, None - for i, _ in enumerate(vals[1][1:]): - df = insert_row(vals[1][i + 1], vals[1][i], df, blank_list) - - return df, rounding, statement_currency - - -@log_start_end(log=logger) -def get_similar_dfs(symbol: str, info: Dict[str, Any], n: int, no_filter: bool = False): - """ - Get dataframes for similar companies - - Parameters - ---------- - symbol : str - The ticker symbol to create a dataframe for - into : Dict[str,Any] - The dictionary based on info collected from fd.Equities() - n : int - The number of similar companies to produce - no_filter : bool - True means that we do not filter based on market cap - - Returns - ------- - new_list : List[str, pd.DataFrame] - A list of similar companies - """ - similars = others_in_sector( - symbol, info["sector"], info["industry_group"], info["industry"], no_filter - ) - i = 0 - new_list = [] - while i < n and similars: - similar_ret = [create_dataframe(similars[0], x)[0] for x in ["BS", "IS", "CF"]] - blank = [x.empty for x in similar_ret] - if True not in blank: - vals = [similars[0], similar_ret] - new_list.append(vals) - i += 1 - similars.pop(0) - return new_list - - -@log_start_end(log=logger) -def clean_dataframes(*args) -> List[pd.DataFrame]: - """ - All dataframes in the list take on the length of the shortest dataframe - - Parameters - ---------- - *args : List[pd.DataFrame] - List of dataframes to clean - - Returns - ------- - dfs : List[pd.DataFrame] - Cleaned list of dataframes - """ - min_cols = min(x.shape[1] for x in args) - dfs = [x.iloc[:, -min_cols:] for x in args] - - return dfs - - -@log_start_end(log=logger) -def get_value(df: pd.DataFrame, row: str, column: int) -> Tuple[float, float]: - """ - Gets a specific value from the dataframe - - Parameters - ---------- - df : pd.DataFrame - The dataframe to get the information from - row : str - The row to get the information from - column : int - The column to get the information from - - Returns - ------- - value : List[float] - The information in float format - """ - val1 = df.at[row, df.columns[column]] - if isinstance(val1, str): - fin_val1: float = float(val1.replace(",", "").replace("-", "-0")) - else: - fin_val1 = float(val1) - val2 = df.at[row, df.columns[column + 1]] - if isinstance(val2, str): - fin_val2: float = float(val2.replace(",", "").replace("-", "-0")) - else: - fin_val2 = float(val2) - return fin_val1, fin_val2 - - -def frac(num: float, denom: float) -> Union[str, float]: - """ - Converts a numerator and a denominator in a fraction, checking for invalid denominators - - Parameters - ---------- - num : float - The numerator - denom : float - The denominator - - Returns - ------- - value : Union[str, float] - The fraction - """ - return "N/A" if denom == 0 else num / denom - - -@log_start_end(log=logger) -def generate_path(n: int, file_name: str) -> Path: - """ - Create the path to save an excel file to - - Parameters - ---------- - n: int - The try number - file_name: str - The name of the file to save - - Returns - ------- - path: Path - The path to save a file to - """ - val = "" if n == 0 else f"({n})" - export_folder = compose_export_path( - func_name="dcf", dir_path=os.path.abspath(os.path.dirname(__file__)) - ).parent - trypath = export_folder / file_name / val - trypath = str(trypath) + ".xlsx" # type: ignore - - return Path(trypath) diff --git a/openbb_terminal/stocks/fundamental_analysis/dcf_static.py b/openbb_terminal/stocks/fundamental_analysis/dcf_static.py deleted file mode 100644 index 14b3d827230..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/dcf_static.py +++ /dev/null @@ -1,181 +0,0 @@ -""" DCF Static """ - -__docformat__ = "numpy" -from typing import Dict, List - -from openpyxl.styles import Alignment, Border, Font, PatternFill, Side - -from openbb_terminal.helper_funcs import excel_columns, get_user_agent - -letters = excel_columns() - -non_gaap_is = [ - "Revenue Growth", - "Net Income Common", - "Net Income Growth", - "Shares Outstanding (Basic)", - "Shares Outstanding (Diluted)", - "Shares Change", - "EPS (Basic)", - "EPS (Diluted)", - "EPS Growth", - "Free Cash Flow Per Share", - "Dividend Per Share", - "Dividend Growth", - "Gross Margin", - "Operating Margin", - "Profit Margin", - "Free Cash Flow Margin", - "Effective Tax Rate", - "EBITDA", - "EBITDA Margin", - "EBIT", - "EBIT Margin", - "Operating Expenses", - "Pretax Income", - "Revenue Growth (YoY)", - "Depreciation & Amortization", -] -gaap_is = [ - "Revenue", - "Cost of Revenue", - "Gross Profit", - "Selling, General & Admin", - "Research & Development", - "Other Operating Expenses", - "Operating Income", - "Interest Expense / Income", - "Other Expense / Income", - "Income Tax", - "Net Income", - "Preferred Dividends", -] -non_gaap_bs = [ - "Cash Growth", - "Debt Growth", - "Net Cash / Debt", - "Net Cash / Debt Growth", - "Net Cash Per Share", - "Working Capital", - "Book Value Per Share", - "Total Debt", -] -gaap_bs = [ - "Cash & Equivalents", - "Short-Term Investments", - "Cash & Cash Equivalents", - "Receivables", - "Inventory", - "Other Current Assets", - "Total Current Assets", - "Property, Plant & Equipment", - "Long-Term Investments", - "Goodwill and Intangibles", - "Other Long-Term Assets", - "Total Long-Term Assets", - "Total Assets", - "Accounts Payable", - "Deferred Revenue", - "Current Debt", - "Other Current Liabilities", - "Total Current Liabilities", - "Long-Term Debt", - "Other Long-Term Liabilities", - "Total Long-Term Liabilities", - "Total Liabilities", - "Common Stock", - "Retained Earnings", - "Comprehensive Income", - "Shareholders' Equity", - "Total Liabilities and Equity", -] -non_gaap_cf = [ - "Operating Cash Flow Growth", - "Free Cash Flow Growth", - "Free Cash Flow Margin", - "Free Cash Flow Per Share", - "Free Cash Flow", -] -gaap_cf = [ - "Net Income", - "Depreciation & Amortization", - "Share-Based Compensation", - "Other Operating Activities", - "Operating Cash Flow", - "Capital Expenditures", - "Acquisitions", - "Change in Investments", - "Other Investing Activities", - "Investing Cash Flow", - "Dividends Paid", - "Share Issuance / Repurchase", - "Debt Issued / Paid", - "Other Financing Activities", - "Financing Cash Flow", - "Net Cash Flow", -] -sum_rows = [ - "Gross Profit", - "Operating Income", - "Net Income", - "Cash & Cash Equivalents", - "Total Current Assets", - "Total Long-Term Assets", - "Total Assets", - "Total Current Liabilities", - "Total Long-Term Liabilities", - "Total Liabilities", - "Shareholders' Equity", - "Total Liabilities and Equity", - "Operating Cash Flow", - "Investing Cash Flow", - "Financing Cash Flow", - "Net Cash Flow", -] -bold_font = Font(bold=True) -thin_border_top = Border(top=Side(style="thin")) -thin_border_bottom = Border(bottom=Side(style="thin")) - -thin_border_nl = Border( - right=Side(style="thin"), - top=Side(style="thin"), - bottom=Side(style="thin"), -) - -thin_border_nr = Border( - left=Side(style="thin"), - top=Side(style="thin"), - bottom=Side(style="thin"), -) - -thin_border = Border( - left=Side(style="thin"), - right=Side(style="thin"), - top=Side(style="thin"), - bottom=Side(style="thin"), -) - -green_bg = PatternFill(fgColor="7fe5cd", fill_type="solid") - -center = Alignment(horizontal="center") - -red = Font(color="FF0000") - -fmt_acct = "_($* #,##0.00_);[Red]_($* (#,##0.00);_($* -_0_0_);_(@" - -statement_titles = {"BS": "Balance Sheet", "CF": "Cash Flows", "IS": "Income Statement"} - -statement_url: Dict[str, str] = { - "BS": "balance-sheet/", - "CF": "cash-flow-statement/", - "IS": "", -} - -statement_ignore: Dict[str, List[str]] = { - "BS": non_gaap_bs, - "CF": non_gaap_cf, - "IS": non_gaap_is, -} - - -headers = {"User-Agent": get_user_agent()} diff --git a/openbb_terminal/stocks/fundamental_analysis/dcf_view.py b/openbb_terminal/stocks/fundamental_analysis/dcf_view.py deleted file mode 100644 index 53cdf5fa9b1..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/dcf_view.py +++ /dev/null @@ -1,1188 +0,0 @@ -""" DCF View """ - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Any, Dict, List, Optional, Union - -import financedatabase as fd -import numpy as np -import pandas as pd -import yfinance as yf -from openpyxl import Workbook -from openpyxl.styles import Font -from openpyxl.styles.numbers import FORMAT_PERCENTAGE_00 -from sklearn.linear_model import LinearRegression - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_rf -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import dcf_model, dcf_static - -# pylint: disable=C0302,too-many-arguments,too-many-boolean-expressions - - -logger = logging.getLogger(__name__) - - -class CreateExcelFA: - @log_start_end(log=logger) - def __init__( - self, - symbol: str, - beta: float, - audit: bool = False, - ratios: bool = True, - len_pred: int = 10, - max_similars: int = 3, - no_filter: bool = False, - growth: bool = False, - ): - """ - Creates a detailed DCF for a given company - - Parameters - ---------- - symbol : str - The ticker symbol to create a DCF for - audit : bool - Whether or not to show that the balance sheet and income statement tie-out - ratios : bool - Whether to show ratios for the company and for similar companies - len_pred : int - The number of years to make predictions for before assuming a terminal value - max_similars : int - The maximum number of similar companies to show, will be less if there are not enough - no_filter : bool - Disable filtering of similar companies to being in the same market cap category - growth : bool - When true this turns the revenue assumption from linear regression to percentage growth - """ - self.info: Dict[str, Any] = { - "len_data": 0, - "len_pred": len_pred, - "max_similars": max_similars, - "rounding": 0, - "symbol": symbol, - "audit": audit, - "ratios": ratios, - "no_filter": no_filter, - } - self.letter: int = 0 - self.growth = growth - self.starts: Dict[str, int] = {"IS": 4, "BS": 18, "CF": 47} - self.wb: Workbook = Workbook() - self.ws: Dict[int, Any] = { - 1: self.wb.active, - 2: self.wb.create_sheet("Free Cash Flows"), - 3: self.wb.create_sheet("Explanations"), - } - self.df: Dict[str, pd.DataFrame] = { - "BS": self.get_data("BS", self.starts["BS"], False), - "IS": self.get_data("IS", self.starts["IS"], True), - "CF": self.get_data("CF", self.starts["CF"], False), - } - - df_not_created = ( - (not isinstance(self.df["BS"], pd.DataFrame) and not self.df["BS"]) - and (not isinstance(self.df["IS"], pd.DataFrame) and not self.df["IS"]) - and (not isinstance(self.df["CF"], pd.DataFrame) and not self.df["CF"]) - ) - equities_database = fd.Equities() - self.data: Dict[str, Any] = ( - { - "now": datetime.now().strftime("%Y-%m-%d"), - "info": { - "country": equities_database.data.loc[self.info["symbol"]][ - "country" - ], - "sector": equities_database.data.loc[self.info["symbol"]]["sector"], - "industry_group": equities_database.data.loc[self.info["symbol"]][ - "industry_group" - ], - "industry": equities_database.data.loc[self.info["symbol"]][ - "industry" - ], - "beta": beta, - }, - "t_bill": get_rf(), - "r_ff": dcf_model.get_fama_coe(self.info["symbol"]), - "f_info": yf.Ticker(symbol).fast_info, - } - if not df_not_created - else {} - ) - - if df_not_created: - console.print( - "It was not possible to create a DCF for this company.\n" - "Please consider that non US-listed companies are not supported." - ) - - @log_start_end(log=logger) - def create_workbook(self): - self.ws[1].title = "Financials" - self.ws[1].column_dimensions["A"].width = 25 - self.ws[2].column_dimensions["A"].width = 22 - for column in dcf_static.letters[1:21]: - self.ws[1].column_dimensions[column].width = 14 - for column in dcf_static.letters[1:21]: - self.ws[2].column_dimensions[column].width = 14 - self.ws[3].column_dimensions["A"].width = 3 - - for _, value in self.ws.items(): - self.create_header(value) - - self.df["BS"], self.df["IS"], self.df["CF"] = dcf_model.clean_dataframes( - self.df["BS"], self.df["IS"], self.df["CF"] - ) - self.add_estimates() - self.create_dcf() - if self.info["ratios"]: - self.add_ratios() - if self.info["audit"]: - self.run_audit() - - i = 0 - while True: - file_name = f"{self.data['now'].replace('-', '')}_dcf_{self.info['symbol']}" - path = dcf_model.generate_path(i, file_name) - path.parent.mkdir(parents=True, exist_ok=True) - - if not path.is_file(): - self.wb.save(path) - console.print( - f"Find the Discounted Cash Flow (DCF) Analysis of {self.info['symbol']} here: {path}" - ) - break - i += 1 - - @log_start_end(log=logger) - def get_data(self, statement: str, row: int, header: bool) -> pd.DataFrame: - symbol = self.info["symbol"] - df, rounding, _ = dcf_model.create_dataframe(symbol, statement) - if df.empty: - raise ValueError( - f"Could not generate a dataframe for the ticker `{symbol}` and statement `{statement}`." - ) - self.info["rounding"] = rounding - if not self.info["len_data"]: - self.info["len_data"] = len(df.columns) - - self.ws[1][f"A{row}"] = dcf_static.statement_titles[statement] - self.ws[1][f"A{row}"].font = dcf_static.bold_font - - rowI = row + 1 - names = df.index.values.tolist() - - for name in names: - self.ws[1][f"A{rowI}"] = name - if name in dcf_static.sum_rows: - length = self.info["len_data"] + ( - self.info["len_pred"] if statement != "CF" else 0 - ) - for i in range(length): - if statement == "CF" and name == "Net Income": - pass - else: - self.ws[1][ - f"{dcf_static.letters[i+1]}{rowI}" - ].font = dcf_static.bold_font - self.ws[1][ - f"{dcf_static.letters[i+1]}{rowI}" - ].border = dcf_static.thin_border_top - rowI += 1 - - column = 1 - - for key, value in df.items(): - rowI = row - if header: - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[column]}{rowI}", - float(key), - font=dcf_static.bold_font, - ) - for item in value: - rowI += 1 - try: - m = 0 if item is None else float(item.replace(",", "")) - except ValueError: - m = 0 - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[column]}{rowI}", - m, - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - column += 1 - - return df - - @log_start_end(log=logger) - def add_estimates(self): - last_year = self.df["BS"].columns[-1] # Replace with columns in DF - col = self.info["len_data"] + 1 - for i in range(self.info["len_pred"]): - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+i]}4", - int(last_year) + 1 + i, - font=dcf_static.bold_font, - ) - - for i in range(41): - col = self.info["len_pred"] + self.info["len_data"] + 3 - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col]}{3+i}", - fill=dcf_static.green_bg, - border=dcf_static.thin_border_nr, - ) - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+1]}{3+i}", - fill=dcf_static.green_bg, - border=dcf_static.thin_border_nl, - ) - - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col]}3", - "Linear model", - alignment=dcf_static.center, - ) - self.ws[1].merge_cells( - f"{dcf_static.letters[col]}3:{dcf_static.letters[col+1]}3" - ) - dcf_model.set_cell(self.ws[1], f"{dcf_static.letters[col]}4", "m") - dcf_model.set_cell(self.ws[1], f"{dcf_static.letters[col+1]}4", "b") - rev_pred_type = "growth" if self.growth else "linear" - self.get_growth("Date", "Revenue", pred_type=rev_pred_type) - self.get_growth("Revenue", "Cost of Revenue") - self.get_sum("Gross Profit", "Revenue", [], ["Cost of Revenue"]) - self.get_growth("Revenue", "Selling, General & Admin", True) - self.get_growth("Revenue", "Research & Development", True) - self.get_growth("Revenue", "Other Operating Expenses") - self.get_sum( - "Operating Income", - "Gross Profit", - [], - [ - "Selling, General & Admin", - "Research & Development", - "Other Operating Expenses", - ], - ) - self.get_growth("Revenue", "Preferred Dividends") - self.get_growth("Revenue", "Interest Expense / Income") - self.get_growth("Revenue", "Other Expense / Income") - self.get_growth("Operating Income", "Income Tax") - self.get_sum( - "Net Income", - "Operating Income", - [], - ["Interest Expense / Income", "Other Expense / Income", "Income Tax"], - ) - self.custom_exp( - "Preferred Dividends", - "Preferred Dividends are not important in a DCF so we do not attempt to predict them.", - ) - self.get_growth("Revenue", "Cash & Equivalents", True) - self.get_growth("Revenue", "Short-Term Investments", True) - self.get_sum( - "Cash & Cash Equivalents", - "Cash & Equivalents", - ["Short-Term Investments"], - [], - ) - self.get_growth("Revenue", "Receivables", True) - self.get_growth("Revenue", "Inventory", True) - self.get_growth("Revenue", "Other Current Assets") - self.get_sum( - "Total Current Assets", - "Cash & Cash Equivalents", - ["Receivables", "Inventory", "Other Current Assets"], - [], - ) - self.get_growth("Revenue", "Property, Plant & Equipment", True) - self.get_growth("Revenue", "Long-Term Investments", True) - self.get_growth("Revenue", "Goodwill and Intangibles", True) - self.get_growth("Revenue", "Other Long-Term Assets") - self.get_sum( - "Total Long-Term Assets", - "Property, Plant & Equipment", - [ - "Long-Term Investments", - "Goodwill and Intangibles", - "Other Long-Term Assets", - ], - [], - ) - self.get_sum( - "Total Assets", "Total Current Assets", ["Total Long-Term Assets"], [] - ) - self.get_growth("Revenue", "Accounts Payable") - self.get_growth("Revenue", "Deferred Revenue") - self.get_growth("Revenue", "Current Debt") - self.get_growth("Revenue", "Other Current Liabilities") - self.get_sum( - "Total Current Liabilities", - "Accounts Payable", - ["Deferred Revenue", "Current Debt", "Other Current Liabilities"], - [], - ) - self.get_sum( - "Long-Term Debt", - "Total Assets", - [], - [ - "Total Current Liabilities", - "Other Long-Term Liabilities", - "Shareholders' Equity", - ], - text=( - "This is the plug. For more information on plugs visit https://corporatefina" - "nceinstitute.com/resources/questions/model-questions/financial-modeling-plug/" - ), - ) # This is the plug - self.get_growth("Revenue", "Other Long-Term Liabilities") - self.get_sum( - "Total Long-Term Liabilities", - "Long-Term Debt", - ["Other Long-Term Liabilities"], - [], - ) - self.get_sum( - "Total Liabilities", - "Total Current Liabilities", - ["Total Long-Term Liabilities"], - [], - ) - self.get_growth("Revenue", "Common Stock") - col = self.info["len_data"] + 1 - rer = self.title_to_row("Retained Earnings") - nir = self.title_to_row("Net Income") - for i in range(self.info["len_pred"]): - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+i]}{rer}", - f"={dcf_static.letters[col+i]}{nir}+{dcf_static.letters[col+i-1]}{rer}", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - - self.get_growth("Revenue", "Comprehensive Income") - self.get_sum( - "Shareholders' Equity", - "Common Stock", - ["Retained Earnings", "Comprehensive Income"], - [], - ) - self.get_sum( - "Total Liabilities and Equity", - "Total Liabilities", - ["Shareholders' Equity"], - [], - ) - - dcf_model.set_cell( - self.ws[1], - "A65", - ( - "Warning: Stock Analysis does not have all of the cash flow items included. Operating" - ", Financing, and Investing Cash Flows may not add up to total cash flows." - ), - font=dcf_static.red, - ) - - @log_start_end(log=logger) - def create_dcf(self): - self.ws[2]["A5"] = "Net Income" - self.ws[2]["A6"] = "Change in NWC" - self.ws[2]["A7"] = "Change in Capex" - self.ws[2]["A8"] = "Preferred Dividends" - self.ws[2]["A9"] = "Free Cash Flows" - r = 4 - - c1 = dcf_static.letters[self.info["len_pred"] + 3] - c2 = dcf_static.letters[self.info["len_pred"] + 4] - c3 = dcf_static.letters[self.info["len_pred"] + 5] - for i in range(self.info["len_pred"]): - j = 1 + i + self.info["len_data"] - cols = dcf_static.letters - dcf_model.set_cell( - self.ws[2], - f"{cols[1+i]}4", - f"=Financials!{cols[j]}4", - font=dcf_static.bold_font, - ) - dcf_model.set_cell( - self.ws[2], - f"{cols[1+i]}5", - f"=Financials!{cols[j]}{self.title_to_row('Net Income')}", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - - dcf_model.set_cell( - self.ws[2], - f"{dcf_static.letters[1+i]}6", - ( - f"=Financials!{cols[j]}{self.title_to_row('Total Current Assets')}" - f"-Financials!{cols[j-1]}{self.title_to_row('Total Current Assets')}" - f"-Financials!{cols[j]}{self.title_to_row('Total Current Liabilities')}" - f"+Financials!{cols[j-1]}{self.title_to_row('Total Current Liabilities')}" - ), - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell( - self.ws[2], - f"{dcf_static.letters[1+i]}7", - ( - f"=Financials!{cols[j]}{self.title_to_row('Total Long-Term Assets')}" - f"-Financials!{cols[j-1]}{self.title_to_row('Total Long-Term Assets')}" - ), - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell( - self.ws[2], - f"{dcf_static.letters[1+i]}8", - f"=Financials!{cols[j]}{self.title_to_row('Preferred Dividends')}", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell( - self.ws[2], - f"{cols[1+i]}9", - f"={cols[1+i]}5-{cols[1+i]}6-{cols[1+i]}7-{cols[1+i]}8", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - font=dcf_static.bold_font, - border=dcf_static.thin_border_top, - ) - dcf_model.set_cell( - self.ws[2], - f"{cols[1+self.info['len_pred']]}9", - f"=({cols[self.info['len_pred']]}9*(1+{c2}" - f"{r+15}))/({c2}{r+11}-{c2}{r+15})", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - - dcf_model.set_cell( - self.ws[2], - f"{c1}{r-2}", - "Note: We do not allow r values to go below 0.5%.", - font=dcf_static.red, - ) - self.ws[2].merge_cells(f"{c1}{r}:{c2}{r}") - for x in [c1, c2]: - dcf_model.set_cell( - self.ws[2], f"{x}{r}", border=dcf_static.thin_border_bottom - ) - dcf_model.set_cell( - self.ws[2], - f"{c1}{r}", - "Discount Rate", - alignment=dcf_static.center, - border=dcf_static.thin_border_bottom, - ) - - # CAPM - dcf_model.set_cell(self.ws[2], f"{c1}{r+1}", "Risk Free Rate") - dcf_model.set_cell( - self.ws[2], - f"{c2}{r+1}", - float(self.data["t_bill"]) / 100, - num_form=FORMAT_PERCENTAGE_00, - ) - self.custom_exp(r + 1, "Pulled from US Treasurey.", 2, f"{c3}") - dcf_model.set_cell(self.ws[2], f"{c1}{r+2}", "Market Rate") - dcf_model.set_cell( - self.ws[2], f"{c2}{r+2}", 0.08, num_form=FORMAT_PERCENTAGE_00 - ) - self.custom_exp( - r + 2, "Average return of the S&P 500 is 8% [Investopedia]", 2, f"{c3}" - ) - dcf_model.set_cell(self.ws[2], f"{c1}{r+3}", "Beta") - if self.data["info"]["beta"] is None: - dcf_model.set_cell(self.ws[2], f"{c2}{r+3}", float(1)) - self.custom_exp( - r + 3, "Warning: Beta not found. Assumed a beta of one.", 2, f"{c3}" - ) - self.data["info"]["beta"] = 1 - else: - dcf_model.set_cell( - self.ws[2], f"{c2}{r+3}", float(self.data["info"]["beta"]) - ) - self.custom_exp(r + 3, "Beta from yahoo finance", 2, f"{c3}") - dcf_model.set_cell(self.ws[2], f"{c1}{r+4}", "r (CAPM)") - dcf_model.set_cell( - self.ws[2], - f"{c2}{r+4}", - f"=max((({c2}{r+2}-{c2}{r+1})*{c2}{r+3})+{c2}{r+1},0.005)", - num_form=FORMAT_PERCENTAGE_00, - border=dcf_static.thin_border_top, - font=dcf_static.bold_font, - ) - - # Fama French - dcf_model.set_cell(self.ws[2], f"{c1}{r+7}", "Fama French") - dcf_model.set_cell( - self.ws[2], - f"{c2}{r+7}", - f"=max({self.data['r_ff']},0.005)", - num_form=FORMAT_PERCENTAGE_00, - ) - self.custom_exp( - r + 7, - ( - "Calculated using the Fama and French Three-Factor model. For more" - "information visit https://www.investopedia.com/terms/f/famaandfrenchthreefactormodel.asp." - ), - 2, - f"{c3}", - ) - - # Decide - for x in [c1, c2]: - dcf_model.set_cell( - self.ws[2], f"{x}{r+9}", border=dcf_static.thin_border_bottom - ) - self.ws[2].merge_cells(f"{c1}{r+9}:{c2}{r+9}") - dcf_model.set_cell( - self.ws[2], - f"{c1}{r+9}", - "Choose model", - border=dcf_static.thin_border_bottom, - alignment=dcf_static.center, - num_form=FORMAT_PERCENTAGE_00, - ) - dcf_model.set_cell(self.ws[2], f"{c1}{r+10}", "Model") - dcf_model.set_cell(self.ws[2], f"{c2}{r+10}", "Fama French") - dcf_model.set_cell(self.ws[2], f"{c3}{r+10}", "Type 'Fama French' or 'CAPM'") - dcf_model.set_cell(self.ws[2], f"{c1}{r+11}", "r") - dcf_model.set_cell( - self.ws[2], - f"{c2}{r+11}", - f'=if({c2}{r+10}="Fama French",{c2}{r+7},if({c2}{r+10}="CAPM",{c2}{r+4},"Invalid Selection"))', - num_form=FORMAT_PERCENTAGE_00, - ) - - dcf_model.set_cell(self.ws[2], f"{c1}{r+15}", "Long Term Growth") - dcf_model.set_cell( - self.ws[2], - f"{c2}{r+15}", - f"=min(0.04,{c2}{r+11}*0.9)", - num_form=FORMAT_PERCENTAGE_00, - ) - dcf_model.set_cell(self.ws[2], "A11", "Value from Operations") - dcf_model.set_cell( - self.ws[2], - "B11", - f"=NPV({c2}{r+11},B9:{dcf_static.letters[self.info['len_pred']+1]}9)", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell(self.ws[2], "A12", "Cash and Cash Equivalents") - dcf_model.set_cell( - self.ws[2], - "B12", - f"=financials!{dcf_static.letters[self.info['len_data']]}{self.title_to_row('Cash & Cash Equivalents')}", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell(self.ws[2], "A13", "Intrinsic Value (sum)") - dcf_model.set_cell( - self.ws[2], - "B13", - "=B11+B12", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell(self.ws[2], "A14", "Debt Obligations") - dcf_model.set_cell( - self.ws[2], - "B14", - f"=financials!{dcf_static.letters[self.info['len_data']]}" - + f"{self.title_to_row('Total Long-Term Liabilities')}", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell(self.ws[2], "A15", "Firm value without debt") - dcf_model.set_cell( - self.ws[2], - "B15", - ( - f"=max(B13-B14," - f"Financials!{dcf_static.letters[self.info['len_data']]}" - + f"{self.title_to_row('Total Assets')}" - f"-Financials!{dcf_static.letters[self.info['len_data']]}" - + f"{self.title_to_row('Total Liabilities')})" - ), - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell( - self.ws[2], - "C15", - *( - f"=if((B13-B14)>" - f"(Financials!{dcf_static.letters[self.info['len_data']]}" - + f"{self.title_to_row('Total Assets')}" - f"-Financials!{dcf_static.letters[self.info['len_data']]}" - + f"{self.title_to_row('Total Liabilities')})," - '"","Note: Total assets minus total liabilities exceeds projected firm value without debt.' - + ' Value shown is total assets minus total liabilities.")', - ), - font=dcf_static.red, - ) - dcf_model.set_cell(self.ws[2], "A16", "Shares Outstanding") - dcf_model.set_cell(self.ws[2], "B16", int(self.data["f_info"]["shares"])) - dcf_model.set_cell(self.ws[2], "A17", "Shares Price") - dcf_model.set_cell( - self.ws[2], - "B17", - f"=(B15*{self.info['rounding']})/B16", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - dcf_model.set_cell(self.ws[2], "A18", "Actual Price") - dcf_model.set_cell(self.ws[2], "B18", float(self.data["f_info"]["last_price"])) - - @log_start_end(log=logger) - def create_header(self, ws: Workbook): - for i in range(10): - dcf_model.set_cell( - ws, f"{dcf_static.letters[i]}1", border=dcf_static.thin_border - ) - - ws.merge_cells("A1:J1") - dcf_model.set_cell( - ws, - "A1", - f"OpenBB Terminal Analysis: {self.info['symbol'].upper()}", - font=Font(color="04cca8", size=20), - border=dcf_static.thin_border, - alignment=dcf_static.center, - ) - dcf_model.set_cell( - ws, "A2", f"DCF for {self.info['symbol']} generated on {self.data['now']}" - ) - - @log_start_end(log=logger) - def run_audit(self): - start = 67 - for i, value in enumerate(dcf_static.sum_rows): - dcf_model.set_cell(self.ws[1], f"A{start + i}", value) - - self.ws[1].merge_cells(f"A{start-2}:K{start-2}") - dcf_model.set_cell( - self.ws[1], - f"A{start - 2}", - "Financial Statement Audit", - font=Font(color="FF0000"), - alignment=dcf_static.center, - ) - dcf_model.set_cell( - self.ws[1], - f"A{start - 1}", - "Audit ensures data integrity. Numbers should be 0 (with slight rounding difference).", - ) - - self.get_sum(start, "Revenue", [], ["Cost of Revenue", "Gross Profit"], True) - self.get_sum( - start + 1, - "Gross Profit", - [], - [ - "Selling, General & Admin", - "Research & Development", - "Other Operating Expenses", - "Operating Income", - ], - True, - ) - self.get_sum( - start + 2, - "Operating Income", - [], - [ - "Interest Expense / Income", - "Other Expense / Income", - "Income Tax", - "Net Income", - ], - True, - ) - self.get_sum( - start + 3, - "Cash & Equivalents", - ["Short-Term Investments"], - ["Cash & Cash Equivalents"], - True, - ) - self.get_sum( - start + 4, - "Cash & Cash Equivalents", - ["Receivables", "Inventory", "Other Current Assets"], - ["Total Current Assets"], - True, - ) - self.get_sum( - start + 5, - "Property, Plant & Equipment", - [ - "Long-Term Investments", - "Goodwill and Intangibles", - "Other Long-Term Assets", - ], - ["Total Long-Term Assets"], - True, - ) - self.get_sum( - start + 6, - "Total Current Assets", - ["Total Long-Term Assets"], - ["Total Assets"], - True, - ) - self.get_sum( - start + 7, - "Accounts Payable", - ["Deferred Revenue", "Current Debt", "Other Current Liabilities"], - ["Total Current Liabilities"], - True, - ) - self.get_sum( - start + 8, - "Long-Term Debt", - ["Other Long-Term Liabilities"], - ["Total Long-Term Liabilities"], - True, - ) - self.get_sum( - start + 9, - "Total Current Liabilities", - ["Total Long-Term Liabilities"], - ["Total Liabilities"], - True, - ) - self.get_sum( - start + 10, - "Common Stock", - ["Retained Earnings", "Comprehensive Income"], - ["Shareholders' Equity"], - True, - ) - self.get_sum( - start + 11, - "Total Liabilities", - ["Shareholders' Equity"], - ["Total Liabilities and Equity"], - True, - ) - self.get_sum( - start + 12, - "Net Income", - [ - "Depreciation & Amortization", - "Share-Based Compensation", - "Other Operating Activities", - ], - ["Operating Cash Flow"], - True, - ) - self.get_sum( - start + 13, - "Capital Expenditures", - ["Acquisitions", "Change in Investments", "Other Investing Activities"], - ["Investing Cash Flow"], - True, - ) - self.get_sum( - start + 14, - "Dividends Paid", - [ - "Share Issuance / Repurchase", - "Debt Issued / Paid", - "Other Financing Activities", - ], - ["Financing Cash Flow"], - True, - ) - self.get_sum( - start + 15, - "Operating Cash Flow", - ["Investing Cash Flow", "Financing Cash Flow"], - ["Net Cash Flow"], - True, - ) - - @log_start_end(log=logger) - def get_growth( - self, x_ind: str, y_ind: str, no_neg: bool = False, pred_type: str = "linear" - ): - """Add growth to a column. Usually this is linear but other options are allowed - - x_ind: str - The x variable to use. This is unused if growth is the pred_type - y_ind: str - The y variable to use - no_neg: bool - Whether or not the value can have negative numbers (profit can be revenue cannot) - pred_type: str - This is assumed to be linear but growth is allowed as well - """ - - x_type = "IS" if x_ind in self.df["IS"].index else "BS" - y_type = "IS" if y_ind in self.df["IS"].index else "BS" - x_df = self.df["IS"] if x_type == "IS" else self.df["BS"] - y_df = self.df["IS"] if y_type == "IS" else self.df["BS"] - - analy_message = "" - row1 = ( - y_df.index.get_loc(y_ind) - + 1 - + (self.starts["IS"] if y_type == "IS" else self.starts["BS"]) - ) - - col = self.info["len_pred"] + self.info["len_data"] + 3 - if pred_type == "linear": - pre_x = ( - x_df.columns.to_numpy() - if x_ind == "Date" - else x_df.loc[x_ind].to_numpy() - ) - - vfunc = np.vectorize(dcf_model.string_float) - pre_x = vfunc(pre_x) - - if x_ind == "Date": - pre_x = pre_x - np.min(pre_x) - x = pre_x.reshape((-1, 1)) - pre_y = y_df.loc[y_ind].to_numpy() - y = vfunc(pre_y) - model = LinearRegression().fit(x, y) - r_sq = model.score(x, y) - r = abs(r_sq**0.5) - - if r > 0.9: - strength = "very strong" - elif r > 0.7: - strength = "strong" - elif r > 0.5: - strength = "moderate" - elif r > 0.3: - strength = "weak" - else: - strength = "very weak" - analy_message = ( - f"The correlation between {x_ind.lower()} and {y_ind.lower()}" - f" is {strength} with a correlation coefficient of {r:.4f}." - ) - dcf_model.set_cell( - self.ws[1], f"{dcf_static.letters[col]}{row1}", float(model.coef_) - ) - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+1]}{row1}", - float(model.intercept_), - ) - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+2]}{row1}", - dcf_static.letters[self.letter], - font=dcf_static.red, - ) - dcf_model.set_cell( - self.ws[3], - f"A{self.letter+4}", - dcf_static.letters[self.letter], - font=dcf_static.red, - ) - elif pred_type == "growth": - pre_y = y_df.loc[y_ind].to_numpy() - fin_y = [] - for item in pre_y: - try: - fin_y.append(float(item)) - except TypeError: - fin_y.append(0) - overall_growth = (fin_y[-1] - fin_y[0]) / fin_y[0] - yearly_growth = overall_growth / len(fin_y) - analy_message = ( - f"Assuming growth percentage of {(yearly_growth * 100):.2f}%" - ) - dcf_model.set_cell( - self.ws[1], f"{dcf_static.letters[col]}{row1}", yearly_growth - ) - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+2]}{row1}", - dcf_static.letters[self.letter], - font=dcf_static.red, - ) - - dcf_model.set_cell(self.ws[3], f"B{self.letter+4}", analy_message) - - col = self.info["len_data"] + 1 - for i in range(self.info["len_pred"]): - if pred_type == "growth": - row_n = ( - y_df.index.get_loc(y_ind) + 1 + self.starts["IS"] - if y_type == "IS" - else self.starts["BS"] - ) - base = ( - f"({dcf_static.letters[col+i-1]}{row1}* (1+" - f"{dcf_static.letters[col+self.info['len_pred']+2]}{row1}))" - ) - - elif x_ind == "Date": - base = ( - f"(({dcf_static.letters[col+i]}4-B4)*" - f"{dcf_static.letters[col+self.info['len_pred']+2]}" - f"{row1})+{dcf_static.letters[col+self.info['len_pred']+3]}{row1}" - ) - else: - row_n = ( - x_df.index.get_loc(x_ind) + 1 + self.starts["IS"] - if x_type == "IS" - else self.starts["BS"] - ) - base = ( - f"({dcf_static.letters[col+i]}{row_n}*" - f"{dcf_static.letters[col+self.info['len_pred']+2]}{row1})" - f"+{dcf_static.letters[col+self.info['len_pred']+3]}{row1}" - ) - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+i]}{row1}", - f"=max({base},0)" if no_neg else f"={base}", - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - - self.letter += 1 - - @log_start_end(log=logger) - def get_sum( - self, - row: Union[int, str], - first: str, - adds: List[str], - subtracts: List[str], - audit: bool = False, - text: Optional[str] = None, - ): - col = 1 if audit else self.info["len_data"] + 1 - for i in range(self.info["len_data"] if audit else self.info["len_pred"]): - sum_formula = f"={dcf_static.letters[col+i]}{self.title_to_row(first)}" - for item in adds: - sum_formula += f"+{dcf_static.letters[col+i]}{self.title_to_row(item)}" - for item in subtracts: - sum_formula += f"-{dcf_static.letters[col+i]}{self.title_to_row(item)}" - rowI = row if isinstance(row, int) else self.title_to_row(row) - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+i]}{rowI}", - sum_formula, - num_form="[$$-409]#,##0.00;[RED]-[$$-409]#,##0.00", - ) - if text: - self.custom_exp(row, text) - - def title_to_row(self, title: str) -> int: - df = ( - self.df["IS"] - if title in self.df["IS"].index - else self.df["BS"] if title in self.df["BS"].index else self.df["CF"] - ) - ind = ( - df.index.get_loc(title) - + 1 - + ( - self.starts["IS"] - if title in self.df["IS"].index - else ( - self.starts["BS"] - if title in self.df["BS"].index - else self.starts["CF"] - ) - ) - ) - return ind - - @log_start_end(log=logger) - def custom_exp( - self, row: Union[int, str], text: str, ws: int = 1, column: Optional[str] = None - ): - if ws == 1: - rowT = row if isinstance(row, int) else self.title_to_row(row) - col = self.info["len_pred"] + self.info["len_data"] + 3 - dcf_model.set_cell( - self.ws[1], - f"{dcf_static.letters[col+2]}{rowT}", - dcf_static.letters[self.letter], - font=dcf_static.red, - ) - if ws == 2: - dcf_model.set_cell( - self.ws[2], - f"{column}{row}", - dcf_static.letters[self.letter], - font=dcf_static.red, - ) - - dcf_model.set_cell( - self.ws[3], - f"A{self.letter+4}", - dcf_static.letters[self.letter], - font=dcf_static.red, - ) - dcf_model.set_cell(self.ws[3], f"B{self.letter+4}", text) - self.letter += 1 - - @log_start_end(log=logger) - def add_ratios(self): - self.ws[4] = self.wb.create_sheet("Ratios") - self.create_header(self.ws[4]) - self.ws[4].column_dimensions["A"].width = 27 - dcf_model.set_cell(self.ws[4], "B4", "Sector:") - dcf_model.set_cell(self.ws[4], "C4", self.data["info"]["sector"]) - similar_data = dcf_model.get_similar_dfs( - self.info["symbol"], - self.data["info"], - self.info["max_similars"], - self.info["no_filter"], - ) - similar_data.insert( - 0, [self.info["symbol"], [self.df["BS"], self.df["IS"], self.df["CF"]]] - ) - row = 6 - for val in similar_data: - self.ws[4].merge_cells(f"A{row}:J{row}") - - # Header columns formatted as row index, text, format type - titles = [ - [0, val[0], 1], - [1, "Liquidity Ratios", 2], - [2, "Current Ratio", 0], - [3, "Quick Ratio", 0], - [5, "Activity Ratios", 2], - [6, "AR Turnover", 0], - [7, "Days Sales in AR", 0], - [8, "Inventory Turnover", 0], - [9, "Days in Inventory", 0], - [10, "Average Payable Turnover", 0], - [11, "Days of Payables Outstanding", 0], - [12, "Cash Conversion Cycle", 0], - [13, "Asset Turnover", 0], - [15, "Profitability Ratios", 2], - [16, "Profit Margin", 0], - [17, "Return on Assets", 0], - [18, "Return on Equity", 0], - [19, "Return on Sales", 0], - [20, "Gross Margin", 0], - [21, "Operating Cash Flow Ratio", 0], - [23, "Coverage Ratios", 2], - [24, "Debt-to-Equity", 0], - [25, "Total Debt Ratio", 0], - [26, "Equity Multiplier", 0], - [27, "Times Interest Earned", 0], - [29, "Investor Ratios", 2], - [30, "Earnings Per Share", 0], - [31, "Price Earnings Ratio", 0], - ] - - for j in titles: - if j[2] == 1: - align = dcf_static.center - dcf_model.set_cell( - self.ws[4], f"A{row+j[0]}", j[1], alignment=align - ) - elif j[2] == 2: - border = dcf_static.thin_border - font = dcf_static.bold_font - dcf_model.set_cell( - self.ws[4], f"A{row+j[0]}", j[1], border=border, font=font - ) - else: - dcf_model.set_cell(self.ws[4], f"A{row+j[0]}", j[1]) - - for j in range(val[1][0].shape[1] - 1): - lt = dcf_static.letters[j + 1] - - cace1 = dcf_model.get_value(val[1][0], "Cash & Cash Equivalents", j)[1] - ar0, ar1 = dcf_model.get_value(val[1][0], "Receivables", j) - inv0, inv1 = dcf_model.get_value(val[1][0], "Inventory", j) - ca1 = dcf_model.get_value(val[1][0], "Total Current Assets", j)[1] - ta0, ta1 = dcf_model.get_value(val[1][0], "Total Assets", j) - ap0, ap1 = dcf_model.get_value(val[1][0], "Accounts Payable", j) - cl1 = dcf_model.get_value(val[1][0], "Total Current Liabilities", j)[1] - tl1 = dcf_model.get_value(val[1][0], "Total Liabilities", j)[1] - te0, te1 = dcf_model.get_value(val[1][0], "Shareholders' Equity", j) - sls1 = dcf_model.get_value(val[1][1], "Revenue", j)[1] - cogs1 = dcf_model.get_value(val[1][1], "Cost of Revenue", j)[1] - inte1 = dcf_model.get_value(val[1][1], "Interest Expense / Income", j)[ - 1 - ] - tax1 = dcf_model.get_value(val[1][1], "Income Tax", j)[1] - ni1 = dcf_model.get_value(val[1][1], "Net Income", j)[1] - pdiv1 = dcf_model.get_value(val[1][1], "Preferred Dividends", j)[1] - opcf1 = dcf_model.get_value(val[1][2], "Operating Cash Flow", j)[1] - - info, outstand = self.data["f_info"], float( - self.data["f_info"]["shares"] - ) - - # Enter row offset, number to display, and format number - rows = [ - [1, int(val[1][0].columns[j + 1]), 1], - [2, dcf_model.frac(ca1, cl1), 0], - [3, dcf_model.frac(cace1 + ar1, cl1), 0], - [6, dcf_model.frac(sls1, (ar0 + ar1) / 2), 0], - [7, dcf_model.frac(ar1, sls1 / 365), 0], - [8, dcf_model.frac(cogs1, (inv0 + inv1) / 2), 0], - [9, dcf_model.frac(inv1, cogs1 / 365), 0], - [10, dcf_model.frac(cogs1, (ap0 + ap1) / 2), 0], - [11, dcf_model.frac(ap1, cogs1 / 365), 0], - [ - 12, - ( - "N/A" - if sls1 == 0 or cogs1 == 0 - else dcf_model.frac(ar1, sls1 / 365) - + dcf_model.frac(inv1, cogs1 / 365) - - dcf_model.frac(ap1, cogs1 / 365) - ), - 0, - ], - [13, dcf_model.frac(sls1, (ta0 + ta1) / 2), 0], - [16, dcf_model.frac(ni1, sls1), 0], - [17, dcf_model.frac(ni1, (ar0 + ar1) / 2), 0], - [18, dcf_model.frac(ni1, (te0 + te1) / 2), 0], - [19, dcf_model.frac(ni1 + inte1 + tax1, sls1), 0], - [20, dcf_model.frac(sls1 - cogs1, sls1), 0], - [21, dcf_model.frac(opcf1, cl1), 0], - [24, dcf_model.frac(tl1, te1), 0], - [25, dcf_model.frac(tl1, ta1), 0], - [26, dcf_model.frac(ta1, te1), 0], - [27, dcf_model.frac(ni1 + inte1 + tax1, inte1), 0], - [ - 30, - dcf_model.frac((ni1 - pdiv1) * self.info["rounding"], outstand), - 0, - ], - [ - 31, - dcf_model.frac( - float(info["regular_market_previous_close"]) * outstand, - (ni1 - pdiv1) * self.info["rounding"], - ), - 0, - ], - ] - - for k in rows: - if k[2] == 1: - font = dcf_static.bold_font - dcf_model.set_cell( - self.ws[4], f"{lt}{row+k[0]}", k[1], font=font - ) - else: - dcf_model.set_cell(self.ws[4], f"{lt}{row+k[0]}", k[1]) - - row += 35 diff --git a/openbb_terminal/stocks/fundamental_analysis/eclect_us_model.py b/openbb_terminal/stocks/fundamental_analysis/eclect_us_model.py deleted file mode 100644 index bfe5837414c..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/eclect_us_model.py +++ /dev/null @@ -1,77 +0,0 @@ -"""Eclect.us model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -# pylint: disable=R1718 - - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_filings_analysis(symbol: str) -> pd.DataFrame: - """Save time reading SEC filings with the help of machine learning. [Source: https://eclect.us] - - Parameters - ---------- - symbol: str - Ticker symbol to see analysis of filings - - Returns - ------- - str - Analysis of filings text - """ - - response = request(f"https://api.eclect.us/symbol/{symbol.lower()}?page=1") - - if response.status_code != 200: - return pd.DataFrame() - - response_dict = response.json() - - def resultGroupMapper(g): - def resultMapper(a): - return { - "Good": a["good_or_bad"] == "good", - "Sentence": a["sentence"], - "Group": g, - } - - return resultMapper - - if len(response_dict) == 0: - return pd.DataFrame() - - risk = pd.DataFrame( - map(resultGroupMapper("Risk factors"), response_dict[0]["rf_highlights"]), - columns=["Group", "Good", "Sentence"], - ) - - if risk.empty: - response_dict_rf = [x for x in response_dict if x["rf_highlights"] != []] - if response_dict_rf != []: - risk = pd.DataFrame( - map( - resultGroupMapper("Risk factors"), - response_dict_rf[0]["rf_highlights"], - ), - columns=["Group", "Good", "Sentence"], - ) - - analysis = pd.DataFrame( - map( - resultGroupMapper("Discussion and Analysis"), - response_dict[0]["daa_highlights"], - ), - columns=["Group", "Good", "Sentence"], - ) - - return pd.concat([risk, analysis], ignore_index=True) diff --git a/openbb_terminal/stocks/fundamental_analysis/eclect_us_view.py b/openbb_terminal/stocks/fundamental_analysis/eclect_us_view.py deleted file mode 100644 index bcb704f4c38..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/eclect_us_view.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Eclect.us view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import eclect_us_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_analysis( - symbol: str, export: str = "", sheet_name: Optional[str] = None -) -> None: - """Display analysis of SEC filings based on NLP model. [Source: https://eclect.us] - - Parameters - ---------- - symbol: str - Ticker symbol to do SEC filings analysis from - """ - - analysis = eclect_us_model.get_filings_analysis(symbol) - - if not analysis.empty: - print_rich_table( - analysis, - title="SEC filings analysis", - show_index=False, - export=bool(export), - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "analysis", - analysis, - sheet_name, - ) - else: - console.print("Filings not found from eclect.us") diff --git a/openbb_terminal/stocks/fundamental_analysis/eodhd_model.py b/openbb_terminal/stocks/fundamental_analysis/eodhd_model.py deleted file mode 100644 index 84b11bc3696..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/eodhd_model.py +++ /dev/null @@ -1,75 +0,0 @@ -"""eodhd Model""" - -__docformat__ = "numpy" - -import logging - -import numpy as np -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_EODHD_KEY"]) -def get_financials( - symbol: str, statement: str, quarterly: bool = False, ratios: bool = False -) -> pd.DataFrame: - """Get ticker financial statements from eodhd - - Parameters - ---------- - symbol: str - Stock ticker symbol - statement: str - Financial statement data to retrieve, can be balance, income or cash - quarterly:bool - Flag to get quarterly reports, by default False - ratios: bool - Shows percentage change, by default False - - Returns - ------- - pd.DataFrame - Balance Sheets or Income Statements or cashflow - """ - - # Note the filing date is over 30 years so will always get as many as allowed - request_url = ( - "https://eodhistoricaldata.com/api/fundamentals/" - f"{symbol}?" - f"api_token={get_current_user().credentials.API_EODHD_KEY}" - f"&filter=Financials::{statement}" - f"::{['yearly', 'quarterly'][quarterly]}" - ) - - r = request(request_url) - if r.status_code != 200: - console.print( - "[red]Invalid API Key for EODHD. Please note that for Fundamental Data, a paid plan is required.\n[/red]" - "Get your API key here: https://eodhistoricaldata.com/r/?ref=869U7F4J and select either the " - "'Fundamentals Data Feed' or 'ALL-IN-ONE Package'.\n" - ) - return pd.DataFrame() - - r_json = r.json() - - df_financials = pd.DataFrame(r_json) - df_financials.drop("date", inplace=True) - df_financials.drop("filing_date", inplace=True) - df_financials.drop("currency_symbol", inplace=True) - - df_financials = df_financials.fillna(value=np.nan) - df_financials = df_financials.dropna(how="all") - - if ratios: - df_financials = df_financials.iloc[:, :5] - df_financials = df_financials.replace("-", "0") - df_financials = df_financials.astype(float) - df_financials = df_financials.pct_change(axis="columns", periods=-1).fillna(0) - return df_financials diff --git a/openbb_terminal/stocks/fundamental_analysis/eodhd_view.py b/openbb_terminal/stocks/fundamental_analysis/eodhd_view.py deleted file mode 100644 index c8b1632168e..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/eodhd_view.py +++ /dev/null @@ -1,148 +0,0 @@ -"""eodhd view""" - -__docformat__ = "numpy" -import logging -import os -from typing import Optional - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.stocks.fundamental_analysis import eodhd_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_EODHD_KEY"]) -def display_fundamentals( - symbol: str, - statement: str, - limit: int = 10, - quarterly: bool = False, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display tickers balance sheet; income statement; cash flow statement - - Parameters - ---------- - symbol: str - Stock ticker symbol - statement:str - Either balance or income or cashflow - limit: int - Number of results to show, by default 10 - quarterly: bool - Flag to get quarterly reports, by default False - ratios: bool - Shows percentage change, by default False - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - fig = OpenBBFigure() - - fundamentals = eodhd_model.get_financials(symbol, statement, quarterly, ratios) - title_str = { - "Balance_Sheet": "Balance Sheet", - "Income_Statement": "Income Statement", - "Cash_Flow": "Cash Flows", - }[statement] - - if fundamentals.empty: - return - - if ratios or plot: - fundamentals = fundamentals.iloc[:, :limit] - - if plot: - rows_plot = len(plot) - fundamentals_plot_data = fundamentals.transpose().fillna(-1) - fundamentals_plot_data.columns = fundamentals_plot_data.columns.str.lower() - fundamentals_plot_data = fundamentals_plot_data.replace("-", "-1") - fundamentals_plot_data = fundamentals_plot_data.astype(float) - if "ttm" in list(fundamentals_plot_data.index): - fundamentals_plot_data = fundamentals_plot_data.drop(["ttm"]) - fundamentals_plot_data = fundamentals_plot_data.sort_index() - - if not ratios: - maximum_value = fundamentals_plot_data.max().max() - if maximum_value > 1_000_000_000_000: - df_rounded = fundamentals_plot_data / 1_000_000_000_000 - denomination = "in Trillions" - elif maximum_value > 1_000_000_000: - df_rounded = fundamentals_plot_data / 1_000_000_000 - denomination = "in Billions" - elif maximum_value > 1_000_000: - df_rounded = fundamentals_plot_data / 1_000_000 - denomination = "in Millions" - elif maximum_value > 1_000: - df_rounded = fundamentals_plot_data / 1_000 - denomination = "in Thousands" - else: - df_rounded = fundamentals_plot_data - denomination = "" - else: - df_rounded = fundamentals_plot_data - denomination = "" - - if rows_plot == 1: - fig.add_bar( - x=df_rounded.index, - y=df_rounded[plot[0].replace("_", " ")], - name=plot[0].replace("_", " "), - ) - fig.set_title( - f"{plot[0].replace('_', ' ').capitalize()} QoQ Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ').capitalize()} of {symbol.upper()} {denomination}" - ) - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_bar( - x=df_rounded.index, - y=df_rounded[plot[i].replace("_", " ")], - name=plot[i].replace("_", " "), - row=i + 1, - col=1, - ) - fig.set_title( - f"{plot[i].replace('_', ' ')} {denomination}", row=i + 1, col=1 - ) - - fig.show(external=fig.is_image_export(export)) - else: - # Snake case to english - fundamentals.index = fundamentals.index.to_series().apply( - lambda x: x.replace("_", " ").title() - ) - - # Readable numbers - fundamentals = fundamentals.applymap(lambda_long_number_format).fillna("-") - print_rich_table( - fundamentals.applymap(lambda x: "-" if x == "nan" else x), - show_index=True, - title=f"{symbol} {title_str}", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - statement, - fundamentals, - sheet_name, - fig, - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/fa_controller.py b/openbb_terminal/stocks/fundamental_analysis/fa_controller.py deleted file mode 100644 index 2dc7de1633c..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/fa_controller.py +++ /dev/null @@ -1,2145 +0,0 @@ -"""Fundamental Analysis Controller.""" - -__docformat__ = "numpy" -import argparse -import logging -from datetime import datetime -from typing import List, Optional - -from pandas.core.frame import DataFrame - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, - list_from_str, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console, get_ordered_list_sources -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.fundamental_analysis import ( - av_view, - business_insider_view, - csimarket_view, - dcf_view, - eclect_us_view, - eodhd_view, - finnhub_view, - finviz_view, - fmp_view, - marketwatch_view, - nasdaq_model, - nasdaq_view, - polygon_view, - seeking_alpha_view, - yahoo_finance_view, -) -from openbb_terminal.terminal_helper import suppress_stdout - -# pylint: disable=inconsistent-return-statements,C0302,R0904 - - -logger = logging.getLogger(__name__) - -no_ticker_message = ( - "No ticker loaded. Use 'load' command to load a ticker or use the -t flag." -) - - -class FundamentalAnalysisController(StockBaseController): - """Fundamental Analysis Controller class""" - - CHOICES_COMMANDS = [ - "load", - "analysis", - "score", - "mktcap", - "metrics", - "ratios", - "growth", - "revfc", - "epsfc", - "warnings", - "income", - "balance", - "cash", - "mgmt", - "splits", - "shrs", - "overview", - "income", - "balance", - "cash", - "divs", - "earnings", - "fraud", - "dcf", - "dcfc", - "dupont", - "sec", - "rating", - "pt", - "rot", - "est", - "supplier", - "customer", - ] - - PATH = "/stocks/fa/" - - SHRS_CHOICES = ["major", "institutional", "mutualfund"] - ESTIMATE_CHOICES = ["annual_earnings", "quarter_earnings", "quarter_revenues"] - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - start: str, - interval: str, - stock: DataFrame, - suffix: str = "", - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = f"{ticker}.{suffix}" if suffix else ticker - self.start = start - self.interval = interval - self.suffix = suffix - self.stock = stock - - self.default_income = get_ordered_list_sources(f"{self.PATH}income")[0] - self.default_balance = get_ordered_list_sources(f"{self.PATH}balance")[0] - self.default_cash = get_ordered_list_sources(f"{self.PATH}cash")[0] - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help.""" - mt = MenuText("stocks/fa/") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_ticker", self.ticker.upper()) - mt.add_raw("\n") - mt.add_info("_company_overview") - mt.add_cmd("mktcap", not self.stock.empty) - mt.add_cmd("overview", not self.stock.empty) - mt.add_cmd("divs", not self.suffix and not self.stock.empty) - mt.add_cmd("splits", not self.suffix and not self.stock.empty) - mt.add_cmd("rating", not self.stock.empty) - mt.add_cmd("rot", not self.stock.empty) - mt.add_cmd("score", not self.stock.empty) - mt.add_cmd("warnings", not self.stock.empty) - mt.add_raw("\n") - mt.add_info("_management_shareholders") - mt.add_cmd("mgmt", not self.stock.empty) - mt.add_cmd("shrs", not self.suffix and not self.stock.empty) - mt.add_cmd("supplier", not self.stock.empty) - mt.add_cmd("customer", not self.stock.empty) - mt.add_raw("\n") - mt.add_info("_financial_statements") - mt.add_cmd("income", not self.stock.empty) - mt.add_cmd("balance", not self.stock.empty) - mt.add_cmd("cash", not self.stock.empty) - mt.add_cmd("growth", not self.stock.empty) - mt.add_cmd("metrics", not self.stock.empty) - mt.add_cmd("ratios", not self.stock.empty) - mt.add_cmd("dupont", not self.stock.empty) - mt.add_cmd("fraud", not self.stock.empty) - mt.add_cmd("sec", not self.stock.empty) - mt.add_cmd("analysis", not self.stock.empty) - mt.add_raw("\n") - mt.add_info("_future_estimations") - mt.add_cmd("earnings", not self.stock.empty) - mt.add_cmd("epsfc", not self.stock.empty) - mt.add_cmd("revfc", not self.stock.empty) - mt.add_cmd("est", not self.stock.empty) - mt.add_cmd("pt", not self.stock.empty) - mt.add_cmd("dcf", not self.stock.empty) - mt.add_cmd("dcfc", not self.stock.empty) - console.print(text=mt.menu_text, menu="Stocks - Fundamental Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - if self.suffix: - return ["stocks", f"load {self.ticker}.{self.suffix}", "fa"] - return ["stocks", f"load {self.ticker}", "fa"] - return [] - - def custom_load_wrapper(self, other_args: List[str]): - """Class specific component of load command""" - with suppress_stdout(): - self.call_load(other_args) - - @log_start_end(log=logger) - def call_analysis(self, other_args: List[str]): - """Process analysis command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="analysis", - description="""Display analysis of SEC filings based on NLP model. [Source: https://eclect.us]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - eclect_us_view.display_analysis( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_mgmt(self, other_args: List[str]): - """Process mgmt command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mgmt", - description=""" - Print management team. Namely: Name, Title, and Information from google - [Source: Business Insider] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - business_insider_view.display_management( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_overview(self, other_args: List[str]): - """Process overview command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="overview", - description=""" - Prints information about, among other things, the industry, sector exchange and company description. - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - if ns_parser.source == "Finviz": - finviz_view.display_screen_data( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "YahooFinance": - yahoo_finance_view.display_info( - self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "AlphaVantage": - av_view.display_overview( - self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "FinancialModelingPrep": - fmp_view.display_profile( - self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_score(self, other_args: List[str]): - """Process score command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="score", - description=""" - Value investing based on Warren Buffett, Joseph Piotroski and Benjamin Graham thoughts [Source: FMP]. - Data is gathered from fmp and the scores are calculated using the valinvest library. The repository - For this library can be found here: https://github.com/astro30/valinvest - """, - ) - parser.add_argument( - "-y", - "--years", - type=int, - default=10, - dest="years", - help="Define the amount of years required to calculate the score.", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - fmp_view.valinvest_score( - self.ticker, - ns_parser.years, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_mktcap(self, other_args: List[str]): - """Process enterprise command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="mktcap", - description=""" - Prints stock price, number of shares, market capitalization and - enterprise value over time. The following fields are expected: Add total debt, - Enterprise value, Market capitalization, Minus cash and cash equivalents, Number - of shares, Stock price, and Symbol. [Source: Financial Modeling Prep] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - - parser.add_argument( - "-s", - "--start", - type=valid_date, - default="1900-01-01", - dest="start", - help="The starting date (format YYYY-MM-DD) of the enterprise value to display. ", - ) - - parser.add_argument( - "-e", - "--end", - type=valid_date, - default=datetime.now(), - dest="end", - help="The ending date (format YYYY-MM-DD) of the enterprise value to display. ", - ) - - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - - parser.add_argument( - "-m", - "--method", - default="market_cap", - type=str, - dest="method", - choices=["enterprise_value", "market_cap"], - help="Define the data to display.", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, raw=True - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - if ns_parser.source == "FinancialModelingPrep": - fmp_view.display_enterprise( - symbol=self.ticker, - start_date=ns_parser.start, - end_date=ns_parser.end, - quarterly=ns_parser.b_quarter, - method=ns_parser.method, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "YahooFinance": - if ns_parser.method == "enterprise_value": - console.print("YahooFinance only has market cap data.") - - yahoo_finance_view.display_mktcap( - self.ticker, - start_date=ns_parser.start, - end_date=ns_parser.end, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_metrics(self, other_args: List[str]): - """Process metrics command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="metrics", - description=""" - Prints a list of the key metrics of a company over time. This can be either - quarterly or annually. This includes, among other things, Return on Equity (ROE), - Working Capital, Current Ratio and Debt to Assets. The following fields are expected: - Average inventory, Average payables, Average receivables, Book value per share, Capex - per share, Capex to depreciation, Capex to operating cash flow, Capex to revenue, Cash - per share, Current ratio, Days of inventory on hand, Days payables outstanding, Days - sales outstanding, Debt to assets, Debt to equity, Dividend yield, Earnings yield, - Enterprise value, Enterprise value over EBITDA, Ev to free cash flow, Ev to operating - cash flow, Ev to sales, Free cash flow per share, Free cash flow yield, Graham net net, - Graham number, Income quality, Intangibles to total assets, Interest debt per share, - Inventory turnover, Market cap, Net current asset value, Net debt to EBITDA, Net income - per share, Operating cash flow per share, Payables turnover, Payout ratio, Pb ratio, Pe - ratio, Pfcf ratio, Pocf ratio, Price to sales ratio, Ptb ratio, Receivables turnover, - Research and development to revenue, Return on tangible assets, Revenue per share, - Roe, Roic, Sales general and administrative to revenue, Shareholders equity per - share, Stock based compensation to revenue, Tangible book value per share, and Working - capital. [Source: Financial Modeling Prep] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Limit of latest years/quarters.", - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - if ns_parser.source == "FinancialModelingPrep": - fmp_view.display_key_metrics( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - if ns_parser.source == "AlphaVantage": - av_view.display_key( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ratios(self, other_args: List[str]): - """Process ratios command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ratios", - description=""" - Prints in-depth ratios of a company over time. This can be either quarterly or - annually. This contains, among other things, Price-to-Book Ratio, Payout Ratio and - Operating Cycle. The following fields are expected: Asset turnover, Capital expenditure - coverage ratio, Cash conversion cycle, Cash flow coverage ratios, Cash flow to debt - ratio, Cash per share, Cash ratio, Company equity multiplier, Current ratio, Days of - inventory outstanding, Days of payables outstanding, Days of sales outstanding, Debt - equity ratio, Debt ratio, Dividend paid and capex coverage ratio, Dividend payout ratio, - Dividend yield, Ebit per revenue, Ebt per ebit, Effective tax rate, Enterprise value - multiple, Fixed asset turnover, Free cash flow operating cash flow ratio, Free cash - flow per share, Gross profit margin, Inventory turnover, Long term debt to - capitalization, Net income per EBT, Net profit margin, Operating cash flow per share, - Operating cash flow sales ratio, Operating cycle, Operating profit margin, Payables - turnover, Payout ratio, Pretax profit margin, Price book value ratio, Price cash flow - ratio, Price earnings ratio, Price earnings to growth ratio, Price fair value, - Price sales ratio, Price to book ratio, Price to free cash flows ratio, Price to - operating cash flows ratio, Price to sales ratio, Quick ratio, Receivables turnover, - Return on assets, Return on capital employed, Return on equity, Short term coverage - ratios, and Total debt to capitalization. [Source: Financial Modeling Prep] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Limit of latest years/quarters.", - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - fmp_view.display_financial_ratios( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_growth(self, other_args: List[str]): - """Process growth command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="growth", - description=""" Prints the growth of several financial statement items and ratios over - time. This can be either annually and quarterly. These are, among other things, Revenue - Growth (3, 5 and 10 years), inventory growth and operating cash flow growth (3, 5 and 10 - years). The following fields are expected: Asset growth, Book valueper share growth, Debt - growth, Dividendsper share growth, Ebit growth, Eps diluted growth, Eps growth, Five y - dividendper share growth per share, Five y net income growth per share, Five y operating c - f growth per share, Five y revenue growth per share, Five y shareholders equity growth per - share, Free cash flow growth, Gross profit growth, Inventory growth, Net income growth, - Operating cash flow growth, Operating income growth, Rd expense growth, Receivables growth, - Revenue growth, Sga expenses growth, Ten y dividendper share growth per share, Ten y net - income growth per share, Ten y operating c f growth per share, Ten y revenue growth per - share, Ten y shareholders equity growth per share, Three y dividendper share growth per - share, Three y net income growth per share, Three y operating c f growth per share, Three y - revenue growth per share, Three y shareholders equity growth per share, Weighted average - shares diluted growth, and Weighted average shares growth [Source: Financial Modeling Prep] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Limit of latest years/quarters.", - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - fmp_view.display_financial_statement_growth( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_epsfc(self, other_args: List[str]): - """Process eps forecast command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="epsfc", - description="""Estimated EPS [Source: Seeking Alpha]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - seeking_alpha_view.display_eps_estimates( - self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_revfc(self, other_args: List[str]): - """Process revenue forecast command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="revfc", - description="""Estimated revenue [Source: Seeking Alpha]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - seeking_alpha_view.display_rev_estimates( - self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_splits(self, other_args: List[str]): - """Process splits command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="splits", - description="""Stock splits and reverse split events since IPO [Source: Yahoo Finance]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - yahoo_finance_view.display_splits( - self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_shrs(self, other_args: List[str]): - """Process shrs command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="shrs", - description="""Print Major, institutional and mutualfunds shareholders. - [Source: Yahoo Finance]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "--holder", - choices=self.SHRS_CHOICES, - default="institutional", - help="Table of holders to get", - dest="holder", - ) - - if other_args and "--holder" not in other_args[0] and "-h" not in other_args: - other_args.insert(0, "--holder") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - if not self.suffix: - yahoo_finance_view.display_shareholders( - self.ticker, - holder=ns_parser.holder, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("Only US tickers are recognized.", "\n") - - @log_start_end(log=logger) - def call_divs(self, other_args: List[str]): - """Process divs command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="divs", - description="Historical dividends for a company", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - default=16, - help="Number of previous dividends to show", - ) - parser.add_argument( - "-p", - "--plot", - dest="plot", - default=False, - action="store_true", - help="Plots changes in dividend over time", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - if not self.suffix: - yahoo_finance_view.display_dividends( - symbol=self.ticker, - limit=ns_parser.limit, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("Only US tickers are recognized.", "\n") - - @log_start_end(log=logger) - def call_income(self, other_args: List[str]): - """Process income command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="income", - description=""" - Prints a complete income statement over time. This can be either quarterly or annually. - The following fields are expected: Accepted date, Cost and expenses, Cost of revenue, - Depreciation and amortization, Ebitda, Ebitda Ratio, Eps, EPS Diluted, Filling date, - Final link, General and administrative expenses, Gross profit, Gross profit ratio, - Income before tax, Income before tax ratio, Income tax expense, Interest expense, Link, - Net income, Net income ratio, Operating expenses, Operating income, Operating income - ratio, Other expenses, Period, Research and development expenses, Revenue, Selling and - marketing expenses, Total other income expenses net, Weighted average shs out, Weighted - average shs out dil [Source: Alpha Vantage]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - parser.add_argument( - "-r", - "--ratios", - action="store_true", - default=False, - dest="ratios", - help="Shows percentage change of values.", - ) - parser.add_argument( - "-p", - "--plot", - action="store", - metavar="column", - choices=stocks_helper.INCOME_PLOT_CHOICES, - type=str, - default=None, - dest="plot", - help="Rows to plot, comma separated. (-1 represents invalid data)", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - limit=5, - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - ns_parser.plot = list_from_str(ns_parser.plot) - # TODO: Switch to actually getting data - if ns_parser.source == "YahooFinance" and ns_parser.b_quarter: - console.print( - "[red]Quarterly data currently unavailable for yfinance" - ", showing yearly.[/red]\n" - ) - if stocks_helper.verify_plot_options( - "income", ns_parser.source, ns_parser.plot - ): - return - if ns_parser.source == "AlphaVantage": - av_view.display_income_statement( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "Polygon": - polygon_view.display_fundamentals( - symbol=self.ticker, - statement="income", - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "FinancialModelingPrep": - fmp_view.display_income_statement( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "YahooFinance": - yahoo_finance_view.display_fundamentals( - symbol=self.ticker, - statement="financials", - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - limit=ns_parser.limit, - ) - elif ns_parser.source == "EODHD": - eodhd_view.display_fundamentals( - symbol=self.ticker, - statement="Income_Statement", - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_balance(self, other_args: List[str]): - """Process balance command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="balance", - description=""" - Prints a complete balance sheet statement over time. This can be either quarterly or - annually. The following fields are expected: Accepted date, Account payables, - Accumulated other comprehensive income loss, Cash and cash equivalents, Cash and short - term investments, Common stock, Deferred revenue, Deferred revenue non current, - Deferred tax liabilities non current, Filling date, Final link, Goodwill, - Goodwill and intangible assets, Intangible assets, Inventory, Link, Long term debt, - Long term investments, Net debt, Net receivables, Other assets, Other current assets, - Other current liabilities, Other liabilities, Other non current assets, Other non - current liabilities, Othertotal stockholders equity, Period, Property plant equipment - net, Retained earnings, Short term debt, Short term investments, Tax assets, Tax - payables, Total assets, Total current assets, Total current liabilities, Total debt, - Total investments, Total liabilities, Total liabilities and stockholders equity, Total - non current assets, Total non current liabilities, and Total stockholders equity. - [Source: Alpha Vantage] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - parser.add_argument( - "-r", - "--ratios", - action="store_true", - default=False, - dest="ratios", - help="Shows percentage change of values.", - ) - parser.add_argument( - "-p", - "--plot", - action="store", - choices=stocks_helper.BALANCE_PLOT_CHOICES, - type=str, - metavar="column", - default=None, - dest="plot", - help="Rows to plot, comma separated. (-1 represents invalid data)", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=5, - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - ns_parser.plot = list_from_str(ns_parser.plot) - # TODO: Switch to actually getting data - if ns_parser.source == "YahooFinance" and ns_parser.b_quarter: - text = "Quarterly data currently unavailable for yfinance" - console.print(f"[red]{text}, showing yearly.[/red]\n") - if stocks_helper.verify_plot_options( - "balance", ns_parser.source, ns_parser.plot - ): - return - if ns_parser.source == "AlphaVantage": - av_view.display_balance_sheet( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "Polygon": - polygon_view.display_fundamentals( - symbol=self.ticker, - statement="balance", - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "FinancialModelingPrep": - fmp_view.display_balance_sheet( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "YahooFinance": - yahoo_finance_view.display_fundamentals( - symbol=self.ticker, - statement="balance-sheet", - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - limit=ns_parser.limit, - ) - elif ns_parser.source == "EODHD": - eodhd_view.display_fundamentals( - symbol=self.ticker, - statement="Balance_Sheet", - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cash(self, other_args: List[str]): - """Process cash command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cash", - description=""" - Prints a complete cash flow statement over time. This can be either quarterly or - annually. The following fields are expected: Accepted date, Accounts payables, Accounts - receivables, Acquisitions net, Capital expenditure, Cash at beginning of period, Cash - at end of period, Change in working capital, Common stock issued, Common stock - repurchased, Debt repayment, Deferred income tax, Depreciation and amortization, - Dividends paid, Effect of forex changes on cash, Filling date, Final link, Free cash - flow, Inventory, Investments in property plant and equipment, Link, Net cash provided - by operating activities, Net cash used for investing activities, Net cash used provided - by financing activities, Net change in cash, Net income, Operating cash flow, Other - financing activities, Other investing activities, Other non cash items, Other working - capital, Period, Purchases of investments, Sales maturities of investments, Stock based - compensation. [Source: Alpha Vantage] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Number of latest years/quarters.", - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - parser.add_argument( - "-r", - "--ratios", - action="store_true", - default=False, - dest="ratios", - help="Shows percentage change of values.", - ) - parser.add_argument( - "-p", - "--plot", - action="store", - type=str, - choices=stocks_helper.CASH_PLOT_CHOICES, - metavar="column", - default=None, - dest="plot", - help="Rows to plot. (-1 represents invalid data)", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - ns_parser.plot = list_from_str(ns_parser.plot) - # TODO: Switch to actually getting data - if ns_parser.source == "YahooFinance" and ns_parser.b_quarter: - text = "Quarterly data currently unavailable for yfinance" - console.print(f"[red]{text}, showing yearly.[/red]\n") - if stocks_helper.verify_plot_options( - "cash", ns_parser.source, ns_parser.plot - ): - return - if ns_parser.source == "AlphaVantage": - av_view.display_cash_flow( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "FinancialModelingPrep": - fmp_view.display_cash_flow( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "Polygon": - polygon_view.display_fundamentals( - symbol=self.ticker, - statement="cash", - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "YahooFinance": - yahoo_finance_view.display_fundamentals( - symbol=self.ticker, - statement="cash-flow", - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - limit=ns_parser.limit, - ) - elif ns_parser.source == "EODHD": - eodhd_view.display_fundamentals( - symbol=self.ticker, - statement="Cash_Flow", - quarterly=ns_parser.b_quarter, - ratios=ns_parser.ratios, - plot=ns_parser.plot, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_earnings(self, other_args: List[str]): - """Process earnings command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="earnings", - description=""" - Print earnings dates and reported EPS of the company. The following fields are - expected: Fiscal Date Ending and Reported EPS. [Source: Alpha Vantage] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Number of latest info", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - if ns_parser.source == "AlphaVantage": - av_view.display_earnings( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "YahooFinance": - yahoo_finance_view.display_earnings( - symbol=self.ticker, limit=ns_parser.limit, export=ns_parser.export - ) - - @log_start_end(log=logger) - def call_fraud(self, other_args: List[str]): - """Process fraud command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.RawTextHelpFormatter, - prog="fraud", - description=( - "M-score:\n------------------------------------------------\n" - "The Beneish model is a statistical model that uses financial ratios calculated with" - " accounting data of a specific company in order to check if it is likely (high" - " probability) that the reported earnings of the company have been manipulated." - " A score of -5 to -2.22 indicated a low chance of fraud, a score of -2.22 to -1.78" - " indicates a moderate change of fraud, and a score above -1.78 indicated a high" - " chance of fraud.[Source: Wikipedia]\n\nDSRI:\nDays Sales in Receivables Index" - " gauges whether receivables and revenue are out of balance, a large number is" - " expected to be associated with a higher likelihood that revenues and earnings are" - " overstated.\n\nGMI:\nGross Margin Index shows if gross margins are deteriorating." - " Research suggests that firms with worsening gross margin are more likely to engage" - " in earnings management, therefore there should be a positive correlation between" - " GMI and probability of earnings management.\n\nAQI:\nAsset Quality Index measures" - " the proportion of assets where potential benefit is less certain. A positive" - " relation between AQI and earnings manipulation is expected.\n\nSGI:\nSales Growth" - " Index shows the amount of growth companies are having. Higher growth companies are" - " more likely to commit fraud so there should be a positive relation between SGI and" - " earnings management.\n\nDEPI:\nDepreciation Index is the ratio for the rate of" - " depreciation. A DEPI greater than 1 shows that the depreciation rate has slowed and" - " is positively correlated with earnings management.\n\nSGAI:\nSales General and" - " Administrative Expenses Index measures the change in SG&A over sales. There should" - " be a positive relationship between SGAI and earnings management.\n\nLVGI:\nLeverage" - " Index represents change in leverage. A LVGI greater than one indicates a lower" - " change of fraud.\n\nTATA: \nTotal Accruals to Total Assets is a proxy for the" - " extent that cash underlies earnings. A higher number is associated with a higher" - " likelihood of manipulation.\n\n\n" - "Z-score:\n------------------------------------------------\n" - "The Zmijewski Score is a bankruptcy model used to predict a firm's bankruptcy in two" - " years. The ratio uses in the Zmijewski score were determined by probit analysis (" - "think of probit as probability unit). In this case, scores less than .5 represent a" - " higher probability of default. One of the criticisms that Zmijewski made was that" - " other bankruptcy scoring models oversampled distressed firms and favored situations" - " with more complete data.[Source: YCharts]" - "\n\nMcKee-score:\n------------------------------------------------\n" - "The McKee Score is a bankruptcy model used to predict a firm's bankruptcy in one year" - "It looks at a company's size, profitability, and liquidity to determine the probability." - "This model is 80% accurate in predicting bankruptcy." - ), - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-e", - "--explanation", - action="store_true", - dest="exp", - default=False, - help="Shows an explanation for the metrics", - ) - parser.add_argument( - "-d", - "--detail", - action="store_true", - dest="detail", - default=False, - help="Shows the details for calculating the mscore", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - av_view.display_fraud( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - detail=ns_parser.detail, - ) - - @log_start_end(log=logger) - def call_dupont(self, other_args: List[str]): - """Process dupont command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.RawTextHelpFormatter, - prog="dupont", - description="The extended dupont deconstructs return on equity to allow investors to understand it better", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - av_view.display_dupont( - self.ticker, raw=ns_parser.raw, export=ns_parser.export - ) - - @log_start_end(log=logger) - def call_dcf(self, other_args: List[str]): - """Process dcf command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dcf", - description=""" - A discounted cash flow statement looks to analyze the value of a company. To do - this we need to predict the future cash flows and then determine how much those - cash flows are worth to us today.\n\n - - We predict the future expected cash flows by predicting what the financial - statements will look like in the future, and then using this to determine the - cash the company will have in the future. This cash is paid to share holders. - We use linear regression to predict the future financial statements.\n\n - - Once we have our predicted financial statements we need to determine how much the - cash flows are worth today. This is done with a discount factor. Our DCF allows - users to choose between Fama French and CAPM for the factor. This allows us - to calculate the present value of the future cash flows.\n\n - - The present value of all of these cash payments is the companies' value. Dividing - this value by the number of shares outstanding allows us to calculate the value of - each share in a company.\n\n - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-a", - "--audit", - action="store_true", - dest="audit", - default=False, - help="Generates a tie-out for financial statement information pulled from online.", - ) - parser.add_argument( - "--no-ratios", - action="store_false", - dest="ratios", - default=True, - help="Removes ratios from DCF.", - ) - parser.add_argument( - "--no-filter", - action="store_true", - dest="ratios", - default=False, - help="Allow similar companies of any market cap to be shown.", - ) - parser.add_argument( - "-p", - "--prediction", - type=int, - dest="prediction", - default=10, - help="Number of years to predict before using terminal value.", - ) - parser.add_argument( - "-s", - "--similar", - type=int, - dest="similar", - default=0, - help="Number of similar companies to generate ratios for.", - ) - parser.add_argument( - "-b", - "--beta", - type=float, - dest="beta", - default=1, - help="The beta you'd like to use for the calculation.", - ) - parser.add_argument( - "-g", - "--growth", - action="store_true", - dest="growth", - default=False, - help="Whether to replace a linear regression estimate with a growth estimate.", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - if self.ticker: - try: - dcf = dcf_view.CreateExcelFA( - symbol=self.ticker, - beta=ns_parser.beta, - audit=ns_parser.audit, - ratios=ns_parser.ratios, - len_pred=ns_parser.prediction, - max_similars=ns_parser.similar, - growth=ns_parser.growth, - ) - except Exception as e: - logger.exception(e) - console.print( - "[red]Could not properly create the DCF, please make sure you are" - " using a valid, US listed ticker.[/red]" - ) - return - if dcf and dcf.data: - dcf.create_workbook() - else: - console.print("Please use --ticker or load a ticker first.") - - @log_start_end(log=logger) - def call_dcfc(self, other_args: List[str]): - """Process dcfc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="dcfc", - description=""" - Prints the discounted cash flow of a company over time including the DCF of today. The - following fields are expected: DCF, Stock price, and Date. [Source: Financial Modeling - Prep] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Limit of latest years/quarters.", - ) - parser.add_argument( - "-q", - "--quarter", - action="store_true", - default=False, - dest="b_quarter", - help="Quarter fundamental data flag.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - fmp_view.display_discounted_cash_flow( - symbol=self.ticker, - limit=ns_parser.limit, - quarterly=ns_parser.b_quarter, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_warnings(self, other_args: List[str]): - """Process warnings command.""" - parser = argparse.ArgumentParser( - add_help=False, - prog="warnings", - description=""" - Sean Seah warnings. Check: Consistent historical earnings per share; - Consistently high return on equity; Consistently high return on assets; 5x Net - Income > Long-Term Debt; and Interest coverage ratio more than 3. See - https://www.drwealth.com/gone-fishing-with-buffett-by-sean-seah/comment-page-1/ - [Source: Market Watch] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-d", - "--debug", - action="store_true", - default=False, - dest="b_debug", - help="print insights into warnings calculation.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - - marketwatch_view.display_sean_seah_warnings( - symbol=self.ticker, debug=ns_parser.b_debug - ) - - @log_start_end(log=logger) - def key_metrics_explained(self, other_args: List[str]): - """Key metrics explained.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="info", - description=""" - Provides information about main key metrics. Namely: EBITDA, - EPS, P/E, PEG, FCF, P/B, ROE, DPR, P/S, Dividend Yield Ratio, D/E, and Beta. - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - filepath = "fundamental_analysis/key_metrics_explained.txt" - with open(filepath) as fp: - line = fp.readline() - while line: - console.print(f"{line.strip()}") - line = fp.readline() - - @log_start_end(log=logger) - def call_pt(self, other_args: List[str]): - """Process pt command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="pt", - description="""Prints price target from analysts. [Source: Business Insider and Financial Modeling Prep]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True, limit=10 - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - if ns_parser.source == "BusinessInsider": - business_insider_view.display_price_target_from_analysts( - symbol=self.ticker, - data=self.stock, - start_date=self.start, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "FinancialModelingPrep": - fmp_view.display_price_targets( - symbol=self.ticker, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_est(self, other_args: List[str]): - """Process est command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="est", - description="""Yearly estimates and quarter earnings/revenues. - [Source: Business Insider]""", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-e", - "--estimate", - help="Estimates to get", - dest="estimate", - choices=self.ESTIMATE_CHOICES, - default="annual_earnings", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - business_insider_view.display_estimates( - symbol=self.ticker, - estimate=ns_parser.estimate, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rot(self, other_args: List[str]): - """Process rot command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="rot", - description=""" - Rating over time (monthly). [Source: Finnhub] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of last months", - ) - parser.add_argument( - "--raw", - action="store_true", - dest="raw", - help="Only output raw data", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - finnhub_view.rating_over_time( - symbol=self.ticker, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rating(self, other_args: List[str]): - """Process rating command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="rating", - description=""" - Based on specific ratios, prints information whether the company - is a (strong) buy, neutral or a (strong) sell. The following fields are expected: - P/B, ROA, DCF, P/E, ROE, and D/E. [Source: Financial Modeling Prep] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="limit of last days to display ratings", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - if ns_parser.source == "Finviz": - finviz_view.analyst( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "FinancialModelingPrep": - fmp_view.rating( - symbol=self.ticker, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_sec(self, other_args: List[str]): - """Process sec command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="sec", - description=""" - Prints SEC filings of the company. The following fields are expected: Filing Date, - Document Date, Type, Category, Amended, and Link. [Source: Market Watch and FinancialModelingPrep] - """, - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - default=self.ticker, - help="The ticker to be used to get SEC filings.", - ) - - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=20, - help="number of latest SEC filings.", - ) - parser.add_argument( - "-y", - "--year", - action="store", - dest="year", - type=check_positive, - default=None, - help="year of SEC filings.", - ) - parser.add_argument( - "-f", - "--form", - action="store", - dest="form", - type=str, - help="form group of SEC filings.", - choices=nasdaq_model.FORM_GROUP.keys(), - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - nasdaq_view.sec_filings( - symbol=ns_parser.ticker, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - year=ns_parser.year, - form_group=ns_parser.form, - ) - - @log_start_end(log=logger) - def call_supplier(self, other_args: List[str]): - """Process supplier command""" - parser = argparse.ArgumentParser( - prog="supplier", - add_help=False, - description="List of suppliers from ticker provided. [Source: CSIMarket]", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - csimarket_view.suppliers( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_customer(self, other_args: List[str]): - """Process customer command""" - parser = argparse.ArgumentParser( - prog="customer", - add_help=False, - description="List of customers from ticker provided. [Source: CSIMarket]", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if not self.ticker: - console.print(no_ticker_message) - return - - csimarket_view.customers( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/fa_helper.py b/openbb_terminal/stocks/fundamental_analysis/fa_helper.py deleted file mode 100644 index ac54d59c25f..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/fa_helper.py +++ /dev/null @@ -1,9 +0,0 @@ -import pandas as pd - - -def clean_df_index(df: pd.DataFrame): - df.index = [ - "".join(" " + char if char.isupper() else char.strip() for char in idx).strip() - for idx in df.index.tolist() - ] - df.index = [s_val.capitalize() for s_val in df.index] diff --git a/openbb_terminal/stocks/fundamental_analysis/finnhub_model.py b/openbb_terminal/stocks/fundamental_analysis/finnhub_model.py deleted file mode 100644 index 57aac8fbb02..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/finnhub_model.py +++ /dev/null @@ -1,49 +0,0 @@ -""" Finnhub Model """ - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def get_rating_over_time(symbol: str) -> pd.DataFrame: - """Get rating over time data. [Source: Finnhub] - - Parameters - ---------- - symbol : str - Ticker symbol to get ratings from - - Returns - ------- - pd.DataFrame - Get dataframe with ratings - """ - response = request( - f"https://finnhub.io/api/v1/stock/recommendation?symbol={symbol}&token={get_current_user().credentials.API_FINNHUB_KEY}" - ) - df = pd.DataFrame() - - if response.status_code == 200: - if response.json(): - df = pd.DataFrame(response.json()) - else: - console.print("No ratings over time found", "\n") - elif response.status_code == 401: - console.print("[red]Invalid API Key[/red]\n") - elif response.status_code == 403: - console.print("[red]API Key not authorized for Premium Feature[/red]\n") - else: - console.print(f"Error in request: {response.json()['error']}", "\n") - - return df diff --git a/openbb_terminal/stocks/fundamental_analysis/finnhub_view.py b/openbb_terminal/stocks/fundamental_analysis/finnhub_view.py deleted file mode 100644 index 7b153fc99fe..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/finnhub_view.py +++ /dev/null @@ -1,127 +0,0 @@ -""" Finnhub View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.fundamental_analysis import finnhub_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def plot_rating_over_time( - data: pd.DataFrame, - symbol: str = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plot rating over time - - Parameters - ---------- - data: pd.DataFrame - Rating over time - symbol: str - Ticker symbol associated with ratings - external_axes: bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure(yaxis_title="Rating") - fig.set_title(f"{symbol}'s ratings over time") - - rot = data.sort_values("period") - fig.add_scatter( - x=rot["period"], y=rot["strongBuy"], name="Strong Buy", line_color="green" - ) - fig.add_scatter(x=rot["period"], y=rot["buy"], name="Buy", line_color="lightgreen") - fig.add_scatter(x=rot["period"], y=rot["hold"], name="Hold", line_color="grey") - fig.add_scatter(x=rot["period"], y=rot["sell"], name="Sell", line_color="pink") - fig.add_scatter( - x=rot["period"], y=rot["strongSell"], name="Strong Sell", line_color="red" - ) - - fig.update_traces(selector=dict(type="scatter"), line_width=3, mode="lines") - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_FINNHUB_KEY"]) -def rating_over_time( - symbol: str, - limit: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Rating over time (monthly). [Source: Finnhub] - - Parameters - ---------- - ticker : str - Ticker to get ratings from - limit : int - Number of last months ratings to show - raw: bool - Display raw data only - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_rot = finnhub_model.get_rating_over_time(symbol) - - if df_rot.empty: - return None - - if raw: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rot", - df_rot, - sheet_name, - ) - - d_cols = { - "strongSell": "Strong Sell", - "sell": "Sell", - "hold": "Hold", - "buy": "Buy", - "strongBuy": "Strong Buy", - } - df_rot_raw = ( - df_rot[["period", "strongSell", "sell", "hold", "buy", "strongBuy"]] - .rename(columns=d_cols) - .head(limit) - ) - return print_rich_table( - df_rot_raw, - headers=list(df_rot_raw.columns), - show_index=False, - title="Monthly Rating", - export=bool(export), - ) - - fig = plot_rating_over_time(df_rot.head(limit), symbol, True) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rot", - df_rot, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/fundamental_analysis/finviz_model.py b/openbb_terminal/stocks/fundamental_analysis/finviz_model.py deleted file mode 100644 index 079c9995807..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/finviz_model.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Finviz Model""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Any, Dict, List - -import finviz -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_data(symbol: str) -> pd.DataFrame: - """Get fundamental data from finviz - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - DataFrame of fundamental data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.fa.data("IWV") - """ - try: - d_finviz_stock = finviz.get_stock(symbol) - except Exception: - return pd.DataFrame() - df_fa = pd.DataFrame.from_dict(d_finviz_stock, orient="index", columns=["Values"]) - return df_fa[df_fa.Values != "-"] - - -@log_start_end(log=logger) -def get_analyst_data(symbol: str) -> pd.DataFrame: - """Get analyst data. [Source: Finviz] - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - df_fa: DataFrame - Analyst price targets - """ - d_finviz_analyst_price = get_analyst_price_targets_workaround(symbol) - df_fa = pd.DataFrame.from_dict(d_finviz_analyst_price) - if not df_fa.empty: - df_fa.set_index("date", inplace=True) - - return df_fa - - -# Patches finviz function while finviz is not updated -def get_analyst_price_targets_workaround( - ticker: str, last_ratings: int = 5 -) -> List[Dict]: - """Patch the analyst price targets function from finviz - - Parameters - ---------- - ticker: str - Ticker symbol - last_ratings: int - Number to get - - """ - - analyst_price_targets = [] - - try: - finviz.main_func.get_page(ticker) - page_parsed = finviz.main_func.STOCK_PAGE[ticker] - table = page_parsed.cssselect( - 'table[class="js-table-ratings fullview-ratings-outer"]' - )[0] - - # skip first row of table since its the header - for row in table[1:]: - rating = row.xpath("td//text()") - rating = [ - val.replace("→", "->").replace("$", "") for val in rating if val != "\n" - ] - rating[0] = datetime.strptime(rating[0], "%b-%d-%y").strftime("%Y-%m-%d") - - data = { - "date": rating[0], - "category": rating[1], - "analyst": rating[2], - "rating": rating[3], - } - if len(rating) == 5: - if "->" in rating[4]: - rating.extend(rating[4].replace(" ", "").split("->")) - del rating[4] - data["target_from"] = float(rating[4]) - data["target_to"] = float(rating[5]) - else: - data["target"] = float(rating[4]) - - analyst_price_targets.append(data) - except Exception: # noqa: S110 - pass # noqa - - return analyst_price_targets[:last_ratings] - - -@log_start_end(log=logger) -def get_news(symbol: str) -> List[Any]: - """Get news from Finviz - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - List[Any] - News - """ - try: - result = finviz.get_news(symbol) - except ValueError: - console.print(f"[red]Error getting news for {symbol}[/red]") - result = [] - return result diff --git a/openbb_terminal/stocks/fundamental_analysis/finviz_view.py b/openbb_terminal/stocks/fundamental_analysis/finviz_view.py deleted file mode 100644 index e6a27999124..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/finviz_view.py +++ /dev/null @@ -1,104 +0,0 @@ -""" FinViz View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal import rich_config -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import finviz_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_screen_data( - symbol: str, export: str = "", sheet_name: Optional[str] = None -): - """FinViz ticker screener - - Parameters - ---------- - symbol : str - Stock ticker symbol - export : str - Format to export data - """ - fund_data = finviz_model.get_data(symbol) - - if fund_data.empty: - console.print(f"No data found for {symbol}", style="bold red") - return - - print_rich_table( - fund_data, title="Ticker Screener", show_index=True, export=bool(export) - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "data", - fund_data, - sheet_name, - ) - - -def lambda_category_color_red_green(val: str) -> str: - """Add color to analyst rating - - Parameters - ---------- - val : str - Analyst rating - Upgrade/Downgrade - - Returns - ------- - str - Analyst rating with color - """ - - if val == "Upgrade": - return f"[green]{val}[/green]" - if val == "Downgrade": - return f"[red]{val}[/red]" - if val == "Reiterated": - return f"[yellow]{val}[/yellow]" - return val - - -@log_start_end(log=logger) -def analyst(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Display analyst ratings. [Source: Finviz] - - Parameters - ---------- - symbol : str - Stock ticker - export : str - Export dataframe data to csv,json,xlsx file - """ - df = finviz_model.get_analyst_data(symbol) - - if rich_config.USE_COLOR and not get_current_user().preferences.USE_INTERACTIVE_DF: - df["category"] = df["category"].apply(lambda_category_color_red_green) - - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title="Display Analyst Ratings", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "analyst", - df, - sheet_name, - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/fmp_model.py b/openbb_terminal/stocks/fundamental_analysis/fmp_model.py deleted file mode 100644 index a3edd5c0783..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/fmp_model.py +++ /dev/null @@ -1,818 +0,0 @@ -""" Financial Modeling Prep Model""" - -__docformat__ = "numpy" -import logging -import warnings -from datetime import datetime -from typing import Any, Dict, Optional - -import fundamentalanalysis as fa # Financial Modeling Prep -import pandas as pd -import valinvest -from requests.exceptions import HTTPError - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import lambda_long_number_format, request -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis.fa_helper import clean_df_index - -logger = logging.getLogger(__name__) - -# pylint: disable=protected-access - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_score(symbol: str, years: int) -> Dict[str, Any]: - """Gets value score from fmp - - Parameters - ---------- - symbol : str - Stock ticker symbol - years : int - The amount of years to use to calculate the score - - Returns - ------- - np.number - Value score - """ - - current_user = get_current_user() - - scores = {} - - try: - valstock = valinvest.Fundamental( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - warnings.filterwarnings("ignore", category=FutureWarning) - scores = { - "Beta Score": 100 * (valstock.beta_score() / 9), - "CROIC Score": 100 - * (valstock._score(valstock.croic_growth, years=years) / 9), - "Debt Cost Score": 100 - * (valstock._score(valstock.debt_cost_growth, years=years) / 9), - "EBITDA Cover Score": 100 - * (valstock._score(valstock.ebitda_cover_growth, years=years) / 9), - "EBITDA Score": 100 - * (valstock._score(valstock.ebitda_growth, years=years) / 9), - "EPS Score": 100 * (valstock._score(valstock.eps_growth, years=years) / 9), - "Equity Buyback Score": 100 - * (valstock._score(valstock.eq_buyback_growth, years=years) / 9), - "Revenue Score": 100 - * (valstock._score(valstock.revenue_growth, years=years) / 9), - "ROIC Score": 100 - * (valstock._score(valstock.roic_growth, years=years) / 9), - } - - # This resembles the same methodology as valstock.fscore - scores["Total Score"] = sum(scores.values()) - warnings.filterwarnings("ignore", category=FutureWarning) - except KeyError: - console.print("[red]Invalid API Key[/red]\n") - # Invalid ticker (Ticker should be a NASDAQ 100 ticker or SP 500 ticker) - except ValueError as e: - console.print(e, "\n") - return scores - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_profile(symbol: str) -> pd.DataFrame: - """Get ticker profile from FMP - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - Dataframe of ticker profile - """ - - current_user = get_current_user() - - df = pd.DataFrame() - - try: - df = fa.profile(symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP) - # Invalid API Keys - except ValueError: - console.print("[red]Invalid API Key[/red]\n") - # Premium feature, API plan is not authorized - except HTTPError: - console.print("[red]API Key not authorized for Premium feature[/red]\n") - return df - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_enterprise( - symbol: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - quarterly: bool = False, -) -> pd.DataFrame: - """Financial Modeling Prep ticker enterprise - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - start_date : str - Start date of data - end_date : str - End date of data - quarterly: bool - Flag to get quarterly data - - Returns - ------- - pd.DataFrame - Dataframe of enterprise information - """ - if start_date is None: - # Set data far in the past to ensure all data is returned - start_date_year = 1900 - elif isinstance(start_date, str): - start_date_year = datetime.strptime(start_date, "%Y-%m-%d").year - else: - start_date_year = start_date.year - - if end_date is None: - end_date_year = datetime.now().year - elif isinstance(end_date, str): - end_date_year = datetime.strptime(end_date, "%Y-%m-%d").year - else: - end_date_year = end_date.year # type: ignore - - # There is a margin of 3 added to ensure that the start date is included - limit = datetime.now().year - start_date_year + 3 - - current_user = get_current_user() - - df_fa = pd.DataFrame() - - try: - if quarterly: - df_fa = fa.enterprise( - symbol, - current_user.credentials.API_KEY_FINANCIALMODELINGPREP, - period="quarter", - ) - else: - df_fa = fa.enterprise( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - - start_date_position = ( - df_fa.columns.get_loc(str(start_date_year)) - if str(start_date_year) in df_fa.columns - else 0 - ) - end_date_position = ( - df_fa.columns.get_loc(str(end_date_year)) - if str(end_date_year) in df_fa.columns - else 0 - ) - - # Select the right portion of the data - if start_date_position: - df_fa = df_fa.iloc[:, end_date_position : start_date_position + 1] - elif end_date_position: - df_fa = df_fa.iloc[:, end_date_position:] - - # Invalid API Keys - except ValueError as e: - console.print(e) - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - - if not df_fa.empty: - df_fa = clean_metrics_df(df_fa, num=limit, mask=False) - - # Transpose the dataframe to make it easier to read - df_fa = df_fa.T - df_fa = df_fa.sort_index(ascending=True) - - return df_fa - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_dcf(symbol: str, limit: int = 5, quarterly: bool = False) -> pd.DataFrame: - """Get stocks dcf from FMP - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number to get - quarterly : bool, optional - Flag to get quarterly data, by default False - - Returns - ------- - pd.DataFrame - Dataframe of dcf data - """ - - current_user = get_current_user() - - df_fa = pd.DataFrame() - - try: - if quarterly: - df_fa = fa.discounted_cash_flow( - symbol, - current_user.credentials.API_KEY_FINANCIALMODELINGPREP, - period="quarter", - ) - else: - df_fa = fa.discounted_cash_flow( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - df_fa = clean_metrics_df(df_fa, num=limit, mask=False) - # Invalid API Keys - except ValueError as e: - console.print(e) - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - - return df_fa - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_income( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: bool = False, -) -> pd.DataFrame: - """Get income statements - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number to get - quarterly : bool, optional - Flag to get quarterly data, by default False - ratios: bool - Shows percentage change, by default False - plot: bool - If the data shall be formatted ready to plot - - Returns - ------- - pd.DataFrame - Dataframe of the income statements - """ - - current_user = get_current_user() - - df_fa = pd.DataFrame() - - try: - if quarterly: - df_fa = fa.income_statement( - symbol, - current_user.credentials.API_KEY_FINANCIALMODELINGPREP, - period="quarter", - ) - else: - df_fa = fa.income_statement( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - - # Invalid API Keys - except ValueError as e: - console.print(e) - return pd.DataFrame() - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - return pd.DataFrame() - - if ratios: - types = df_fa.copy().applymap(lambda x: isinstance(x, (float, int))).all(axis=1) - valid = [] - i = 0 - for row in types: - if row: - valid.append(i) - i += 1 - df_fa_pc = df_fa.iloc[valid].pct_change(axis="columns", periods=-1).fillna(0) - j = 0 - for i in valid: - df_fa.iloc[i] = df_fa_pc.iloc[j] - j += 1 - - df_fa = df_fa.iloc[:, 0:limit] - df_fa_c = clean_metrics_df(df_fa, num=limit) - df_fa.index = df_fa_c.index - - return df_fa_c if not plot else df_fa - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_balance( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: bool = False, -) -> pd.DataFrame: - """Get balance sheets - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number to get - quarterly : bool, optional - Flag to get quarterly data, by default False - ratios: bool - Shows percentage change, by default False - plot: bool - If the data shall be formatted ready to plot - - Returns - ------- - pd.DataFrame - Dataframe of balance sheet - """ - - current_user = get_current_user() - - df_fa = pd.DataFrame() - - try: - if quarterly: - df_fa = fa.balance_sheet_statement( - symbol, - current_user.credentials.API_KEY_FINANCIALMODELINGPREP, - period="quarter", - ) - else: - df_fa = fa.balance_sheet_statement( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - - # Invalid API Keys - except ValueError as e: - console.print(e) - return pd.DataFrame() - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - return pd.DataFrame() - - if ratios: - types = df_fa.copy().applymap(lambda x: isinstance(x, (float, int))).all(axis=1) - valid = [] - i = 0 - for row in types: - if row: - valid.append(i) - i += 1 - df_fa_pc = df_fa.iloc[valid].pct_change(axis="columns", periods=-1).fillna(0) - j = 0 - for i in valid: - df_fa.iloc[i] = df_fa_pc.iloc[j] - j += 1 - - df_fa = df_fa.iloc[:, 0:limit] - df_fa_c = clean_metrics_df(df_fa, num=limit) - df_fa.index = df_fa_c.index - - return df_fa_c if not plot else df_fa - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_cash( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: bool = False, -) -> pd.DataFrame: - """Get cash flow - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number to get - quarterly : bool, optional - Flag to get quarterly data, by default False - ratios: bool - Shows percentage change, by default False - plot: bool - If the data shall be formatted ready to plot - - Returns - ------- - pd.DataFrame - Dataframe of company cash flow - """ - - current_user = get_current_user() - - df_fa = pd.DataFrame() - - try: - if quarterly: - df_fa = fa.cash_flow_statement( - symbol, - current_user.credentials.API_KEY_FINANCIALMODELINGPREP, - period="quarter", - ) - else: - df_fa = fa.cash_flow_statement( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - - # Invalid API Keys - except ValueError as e: - console.print(e) - return pd.DataFrame() - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - return pd.DataFrame() - - if ratios: - types = df_fa.copy().applymap(lambda x: isinstance(x, (float, int))).all(axis=1) - valid = [] - i = 0 - for row in types: - if row: - valid.append(i) - i += 1 - df_fa_pc = df_fa.iloc[valid].pct_change(axis="columns", periods=-1).fillna(0) - j = 0 - for i in valid: - df_fa.iloc[i] = df_fa_pc.iloc[j] - j += 1 - - df_fa = df_fa.iloc[:, 0:limit] - df_fa_c = clean_metrics_df(df_fa, num=limit) - df_fa.index = df_fa_c.index - - return df_fa_c if not plot else df_fa - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_key_metrics( - symbol: str, limit: int = 5, quarterly: bool = False -) -> pd.DataFrame: - """Get key metrics - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number to get - quarterly : bool, optional - Flag to get quarterly data, by default False - - Returns - ------- - pd.DataFrame - Dataframe of key metrics - """ - - current_user = get_current_user() - - df_fa = pd.DataFrame() - - try: - if quarterly: - df_fa = fa.key_metrics( - symbol, - current_user.credentials.API_KEY_FINANCIALMODELINGPREP, - period="quarter", - ) - else: - df_fa = fa.key_metrics( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - - df_fa = clean_metrics_df(df_fa, num=limit) - # Invalid API Keys - except ValueError as e: - console.print(e) - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - - return df_fa - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_key_ratios( - symbol: str, limit: int = 5, quarterly: bool = False -) -> pd.DataFrame: - """Get key ratios - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number to get - quarterly : bool, optional - Flag to get quarterly data, by default False - - Returns - ------- - pd.DataFrame - Dataframe of key ratios - """ - - current_user = get_current_user() - - df_fa = pd.DataFrame() - - try: - if quarterly: - df_fa = fa.financial_ratios( - symbol, - current_user.credentials.API_KEY_FINANCIALMODELINGPREP, - period="quarter", - ) - else: - df_fa = fa.financial_ratios( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - - df_fa = clean_metrics_df(df_fa, num=limit) - # Invalid API Keys - except ValueError as e: - console.print(e) - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - - return df_fa - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_financial_growth( - symbol: str, limit: int = 5, quarterly: bool = False -) -> pd.DataFrame: - """Get financial statement growth - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number to get - quarterly : bool, optional - Flag to get quarterly data, by default False - - Returns - ------- - pd.DataFrame - Dataframe of financial statement growth - """ - - current_user = get_current_user() - - df_fa = pd.DataFrame() - - try: - if quarterly: - df_fa = fa.financial_statement_growth( - symbol, - current_user.credentials.API_KEY_FINANCIALMODELINGPREP, - period="quarter", - ) - else: - df_fa = fa.financial_statement_growth( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - - df_fa = clean_metrics_df(df_fa, num=limit) - - df_fa = df_fa[df_fa.columns[::-1]] - # Invalid API Keys - except ValueError as e: - console.print(e) - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - - return df_fa - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def clean_metrics_df(data: pd.DataFrame, num: int, mask: bool = False) -> pd.DataFrame: - """Clean metrics data frame - - Parameters - ---------- - data : pd.DataFrame - Metrics data frame - num : int - Number of columns to clean - mask : bool, optional - Apply mask, by default True - - Returns - ------- - pd.DataFrame - Cleaned metrics data frame - """ - # iloc will fail if number is greater than number of columns - num = min(num, data.shape[1]) - data = data.iloc[:, 0:num].copy() - - if mask: - data = data.mask(data.astype(object).eq(num * ["None"])).dropna() - data = data.mask(data.astype(object).eq(num * ["0"])).dropna() - - date_rows = { - "calendarYear": "%Y", - "fillingDate": "%Y-%m-%d", - "acceptedDate": "%Y-%m-%d %H:%M:%S", - } - for row, dt_type in date_rows.items(): - if row in data.index: - data.loc[row] = pd.to_datetime(data.loc[row], format=dt_type) - # we dont want to format this going to pywry window - if not get_current_user().preferences.USE_INTERACTIVE_DF: - data = data.applymap(lambda x: lambda_long_number_format(x)) - clean_df_index(data) - data.columns.name = "Fiscal Date Ending" - data = data.rename( - index={ - "Enterprise value over e b i t d a": "Enterprise value over EBITDA", - "Net debt to e b i t d a": "Net debt to EBITDA", - "D c f": "DCF", - "Net income per e b t": "Net income per EBT", - } - ) - - return data - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_rating(symbol: str) -> pd.DataFrame: - """Get ratings for a given ticker. [Source: Financial Modeling Prep] - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - Rating data - """ - current_user = get_current_user() - if current_user.credentials.API_KEY_FINANCIALMODELINGPREP: - try: - df = fa.rating( - symbol, current_user.credentials.API_KEY_FINANCIALMODELINGPREP - ) - l_recoms = [col for col in df.columns if "Recommendation" in col] - l_recoms_show = [ - recom.replace("rating", "") - .replace("Details", "") - .replace("Recommendation", "") - for recom in l_recoms - ] - l_recoms_show[0] = "Rating" - df = df[l_recoms] - df.columns = l_recoms_show - except ValueError as e: - console.print(f"[red]{e}[/red]\n") - logger.exception(str(e)) - df = pd.DataFrame() - else: - df = pd.DataFrame() - return df - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_price_targets(symbol: str) -> pd.DataFrame: - """Get price targets for a company [Source: Financial Modeling Prep] - - Parameters - ---------- - symbol : str - Symbol to get data for - - Returns - ------- - pd.DataFrame - DataFrame of price targets - """ - current_user = get_current_user() - - url = ( - "https://financialmodelingprep.com/api/v4/price-target?" - f"symbol={symbol}&apikey={current_user.credentials.API_KEY_FINANCIALMODELINGPREP}" - ) - response = request(url) - - # Check if response is valid - if response.status_code != 200 or "Error Message" in response.json(): - message = f"Error, Status Code: {response.status_code}." - message = ( - message - if "Error Message" not in response.json() - else message + "\n" + response.json()["Error Message"] + ".\n" - ) - console.print(message) - return pd.DataFrame() - - return pd.DataFrame(response.json()) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_filings( - pages: int = 1, -) -> pd.DataFrame: - """Get SEC Filings RSS feed, disseminated by FMP - Parameters - ---------- - pages: range = 1 - The range of most-rececnt pages to get entries from (1000 per page; maximum of 30 pages) - Returns - ------- - df: pd.DataFrame - Dataframe of results - Examples - -------- - df = openbb.stocks.filings() - df = openbb.stocks.filings(pages=30) - """ - current_user = get_current_user() - temp = [] - try: - for i in range(pages): - temp.append( - pd.read_json( - "https://financialmodelingprep.com/api/v3/rss_feed?&page=" - f"{i}" - "&apikey=" - f"{current_user.credentials.API_KEY_FINANCIALMODELINGPREP}" - ) - ) - df = pd.concat(temp) - df = df.rename( - columns={ - "title": "Title", - "date": "Date", - "link": "URL", - "cik": "CIK", - "form_type": "Form Type", - "ticker": "Ticker", - }, - ) - df_columns = ["Date", "Ticker", "CIK", "Form Type", "Title", "URL"] - df = ( - pd.DataFrame(df, columns=df_columns) - .set_index(keys=["Date"]) - .copy() - .sort_index(ascending=False) - ) - - # Invalid API Keys - except ValueError as e: - console.print(e) - df = pd.DataFrame() - # Premium feature, API plan is not authorized - except HTTPError as e: - console.print(e) - df = pd.DataFrame() - - return df diff --git a/openbb_terminal/stocks/fundamental_analysis/fmp_view.py b/openbb_terminal/stocks/fundamental_analysis/fmp_view.py deleted file mode 100644 index 1ea83a88747..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/fmp_view.py +++ /dev/null @@ -1,810 +0,0 @@ -""" Financial Modeling Prep View """ - -__docformat__ = "numpy" - -import logging -import os -from datetime import datetime -from typing import Optional - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, - revert_lambda_long_number_format, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.fundamental_analysis import fmp_model - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments, R1710 - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def valinvest_score( - symbol: str, years: int, export: str = "", sheet_name: Optional[str] = None -): - """ - Value investing tool based on Warren Buffett, Joseph Piotroski and Benjamin Graham thoughts [Source: FMP] - The data is gathered from fmp and the scores are calculated using the valinvest library. The repository - For this library can be found here: https://github.com/astro30/valinvest - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - scores = pd.DataFrame.from_dict( - fmp_model.get_score(symbol, years), orient="index", columns=["Score"] - ) - - if not scores.empty: - updated_scores = [] - for score in scores["Score"]: - updated_scores.append(f"{score:.2f}".rstrip("0").rstrip(".") + " %") - - scores["Score"] = updated_scores - - print_rich_table( - scores, - title=f"Value Investing Scores [{years} Years]", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "scores", - scores, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_profile(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Financial Modeling Prep ticker profile - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - profile = fmp_model.get_profile(symbol) - - if not profile.empty: - console.print(f"\nImage: {profile.loc['image'][0]}") - console.print(f"\nDescription: {profile.loc['description'][0]}") - - profile.drop(index=["description", "image"], inplace=True) - profile.columns = [" "] - - print_rich_table( - profile, - title=f"{symbol.upper()} Profile", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "scores", - profile, - sheet_name, - ) - else: - logger.error("Could not get data") - console.print("[red]Unable to get data[/red]\n") - - console.print() - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_enterprise( - symbol: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - quarterly: bool = False, - method: str = "market_cap", - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Financial Modeling Prep ticker enterprise - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - start_date: str - Start date of the data - end_date: str - End date of the data - quarterly: bool - Flag to get quarterly data - plot: bool - Flag to plot the data - method: str - Type of data to plot, market_cap or enterprise_value - raw: bool - Flag to print raw data - export: str - Format to export data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - df_fa = fmp_model.get_enterprise(symbol, start_date, end_date, quarterly) - - # Re-order the returned columns so they are in a more logical ordering - df_fa = df_fa.reindex( - columns=[ - "Symbol", - "Stock price", - "Number of shares", - "Market capitalization", - "Add total debt", - "Minus cash and cash equivalents", - "Enterprise value", - ] - ) - - if df_fa.empty: - console.print("[red]No data available[/red]\n") - else: - df_fa_plot = df_fa.applymap(revert_lambda_long_number_format) - - type_str = ( - "Market capitalization" if method == "market_cap" else "Enterprise value" - ) - - fig = OpenBBFigure(yaxis_title=f"{type_str} in Billions") - fig.set_title(f"{type_str} of {symbol}") - fig.add_scatter( - x=df_fa_plot.index, - y=df_fa_plot[type_str].values / 1e9, - mode="lines", - name=type_str, - line_color=theme.up_color, - stackgroup="one", - ) - - if raw: - print_rich_table( - df_fa, - headers=list(df_fa.columns), - title=f"{symbol} Enterprise Value", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - method, - df_fa, - sheet_name, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_discounted_cash_flow( - symbol: str, - limit: int = 5, - quarterly: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Financial Modeling Prep ticker discounted cash flow - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number to get - quarterly: bool - Flag to get quarterly data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - dcf = fmp_model.get_dcf(symbol, limit, quarterly) - dcf = dcf[dcf.columns[::-1]] - dcf.columns = dcf.iloc[0].values - dcf = dcf.drop("Date") - if dcf.empty: - console.print("[red]No data available[/red]\n") - else: - print_rich_table( - dcf, title="Discounted Cash Flow", show_index=True, export=bool(export) - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "dcf", - dcf.transpose(), - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_income_statement( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Financial Modeling Prep ticker income statement - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number to get - quarterly: bool - Flag to get quarterly data - ratios: bool - Shows percentage change, by default False - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - income = fmp_model.get_income(symbol, limit, quarterly, ratios, bool(plot)) - - if not income.empty: - fig = OpenBBFigure() - - income.index = [ - stocks_helper.INCOME_PLOT["FinancialModelingPrep"][i] - for i in [i.replace(" ", "_") for i in income.index.str.lower()] - ] - - if plot: - income_plot_data = income[income.columns[::-1]] - rows_plot = len(plot) - income_plot_data = income_plot_data.transpose() - - if rows_plot == 1: - title = ( - f"{plot[0].replace('_', ' ').title()} {'QoQ' if quarterly else 'YoY'} Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ').title()} of {symbol.upper()}" - ) - fig.add_scatter( - x=income_plot_data.index, - y=income_plot_data[plot[0]], - mode="lines", - name=f'{symbol.upper()} {plot[0].replace("_", "")}', - ) - fig.set_title(title) - - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_scatter( - x=income_plot_data.index, - y=income_plot_data[plot[i]], - mode="lines", - name=f'{symbol.upper()} {plot[i].replace("_", "")}', - row=i + 1, - col=1, - ) - fig.set_title(f"{plot[i].replace('_', ' ')}", row=i + 1, col=1) - - fig.show(external=fig.is_image_export(export)) - else: - income = income[income.columns[::-1]] - # Snake case to english - income.index = income.index.to_series().apply( - lambda x: x.replace("_", " ").title() - ) - print_rich_table( - income.drop(index=["Final Link", "Link"]), - headers=list(income.columns), - title=( - f"{symbol.upper()} Income Statement" - if not ratios - else f"{'QoQ' if quarterly else 'YoY'} Change of {symbol.upper()} Income Statement" - ), - show_index=True, - export=bool(export), - ) - - console.print(income.loc["Final Link"].to_frame().to_string()) - console.print() - console.print(income.loc["Link"].to_frame().to_string()) - console.print() - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "income", - income, - sheet_name, - fig, - ) - else: - logger.error("Could not get data") - console.print("[red]Could not get data[/red]\n") - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_balance_sheet( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Financial Modeling Prep ticker balance sheet - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number to get - quarterly: bool - Flag to get quarterly data - ratios: bool - Shows percentage change, by default False - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - balance = fmp_model.get_balance(symbol, limit, quarterly, ratios, bool(plot)) - - if not balance.empty: - fig = OpenBBFigure() - - balance.index = [ - stocks_helper.BALANCE_PLOT["FinancialModelingPrep"][i] - for i in [i.replace(" ", "_") for i in balance.index.str.lower()] - ] - - if plot: - balance_plot_data = balance[balance.columns[::-1]] - rows_plot = len(plot) - balance_plot_data = balance_plot_data.transpose() - - if rows_plot == 1: - fig.add_scatter( - x=balance_plot_data.index, - y=balance_plot_data[plot[0]], - mode="lines", - name=f'{symbol.upper()} {plot[0].replace("_", " ")}', - ) - fig.set_title( - f"{plot[0].replace('_', ' ').lower()} {'QoQ' if quarterly else 'YoY'} Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ').title()} of {symbol.upper()}" - ) - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_scatter( - x=balance_plot_data.index, - y=balance_plot_data[plot[i]], - mode="lines", - name=f'{symbol.upper()} {plot[i].replace("_", " ")}', - row=i + 1, - col=1, - ) - fig.set_title(f"{plot[i].replace('_', ' ')}", row=i + 1, col=1) - - fig.show(external=fig.is_image_export(export)) - else: - balance = balance[balance.columns[::-1]] - # Snake case to english - balance.index = balance.index.to_series().apply( - lambda x: x.replace("_", " ").title() - ) - print_rich_table( - balance.drop(index=["Final Link", "Link"]), - headers=list(balance.columns), - title=f"{symbol.upper()} Balance Sheet", - show_index=True, - export=bool(export), - ) - - console.print(balance.loc["Final Link"].to_frame().to_string()) - console.print() - console.print(balance.loc["Link"].to_frame().to_string()) - console.print() - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "balance", - balance, - sheet_name, - fig, - ) - else: - logger.error("Could not get data") - console.print("[red]Could not get data[/red]\n") - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_cash_flow( - symbol: str, - limit: int = 5, - quarterly: bool = False, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Financial Modeling Prep ticker cash flow - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number to get - quarterly: bool - Flag to get quarterly data - ratios: bool - Shows percentage change, by default False - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - cash = fmp_model.get_cash(symbol, limit, quarterly, ratios, bool(plot)) - - if not cash.empty: - fig = OpenBBFigure() - - cash.index = [ - stocks_helper.CASH_PLOT["FinancialModelingPrep"][i] - for i in [i.replace(" ", "_") for i in cash.index.str.lower()] - ] - - if plot: - cash_plot_data = cash[cash.columns[::-1]] - rows_plot = len(plot) - cash_plot_data = cash_plot_data.transpose() - - if rows_plot == 1: - fig.add_scatter( - x=cash_plot_data.index, - y=cash_plot_data[plot[0]], - mode="lines", - name=f'{symbol.upper()} {plot[0].replace("_", " ")}', - ) - fig.set_title( - f"{plot[0].replace('_', ' ').lower()} {'QoQ' if quarterly else 'YoY'} Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ').title()} of {symbol.upper()}" - ) - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_scatter( - x=cash_plot_data.index, - y=cash_plot_data[plot[i]], - mode="lines", - name=f'{symbol.upper()} {plot[i].replace("_", " ")}', - row=i + 1, - col=1, - ) - fig.set_title(f"{plot[i].replace('_', ' ')}", row=i + 1, col=1) - fig.show(external=fig.is_image_export(export)) - else: - cash = cash[cash.columns[::-1]] - # Snake case to english - cash.index = cash.index.to_series().apply( - lambda x: x.replace("_", " ").title() - ) - - print_rich_table( - cash.drop(index=["Final Link", "Link"]), - headers=list(cash.columns), - title=f"{symbol.upper()} Cash Flow", - show_index=True, - export=bool(export), - ) - - console.print(cash.loc["Final Link"].to_frame().to_string()) - console.print() - console.print(cash.loc["Link"].to_frame().to_string()) - console.print() - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "cash", - cash, - sheet_name, - fig, - ) - else: - logger.error("Could not get data") - console.print("[red]Could not get data[/red]\n") - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_key_metrics( - symbol: str, - limit: int = 5, - quarterly: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Financial Modeling Prep ticker key metrics - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number to get - quarterly: bool - Flag to get quarterly data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - key_metrics = fmp_model.get_key_metrics(symbol, limit, quarterly) - - if not key_metrics.empty: - key_metrics = key_metrics[key_metrics.columns[::-1]] - print_rich_table( - key_metrics, - headers=list(key_metrics.columns), - title=f"{symbol.upper()} Key Metrics", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "metrics", - key_metrics, - sheet_name, - ) - else: - logger.error("Could not get data") - console.print("[red]Could not get data[/red]\n") - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_financial_ratios( - symbol: str, - limit: int = 5, - quarterly: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Financial Modeling Prep ticker ratios - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number to get - quarterly: bool - Flag to get quarterly data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - ratios = fmp_model.get_key_ratios(symbol, limit, quarterly) - - if not ratios.empty: - ratios = ratios[ratios.columns[::-1]] - print_rich_table( - ratios, - headers=list(ratios.columns), - title=f"{symbol.upper()} Ratios", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "grratiosowth", - ratios, - sheet_name, - ) - else: - logger.error("Could not get data") - console.print("[red]Could not get data[/red]\n") - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_financial_statement_growth( - symbol: str, - limit: int = 5, - quarterly: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Financial Modeling Prep ticker growth - - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - limit: int - Number to get - quarterly: bool - Flag to get quarterly data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - growth = fmp_model.get_financial_growth(symbol, limit, quarterly) - - if not growth.empty: - print_rich_table( - growth, - headers=list(growth.columns), - title=f"{symbol.upper()} Growth", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "growth", - growth, - sheet_name, - ) - else: - logger.error("Could not get data") - console.print("[red]Could not get data[/red]\n") - - -def add_color(value: str) -> str: - if "buy" in value.lower(): - value = f"[green]{value}[/green]" - elif "sell" in value.lower(): - value = f"[red]{value}[/red]" - return value - - -@log_start_end(log=logger) -def rating( - symbol: str, limit: int = 10, export: str = "", sheet_name: Optional[str] = None -): - """Display ratings for a given ticker. [Source: Financial Modeling Prep] - - Parameters - ---------- - symbol: str - Stock ticker symbol - limit: int - Number of last days ratings to display - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx file - """ - df = fmp_model.get_rating(symbol) - - if (isinstance(df, pd.DataFrame) and df.empty) or ( - not isinstance(df, pd.DataFrame) and not df - ): - return - - # TODO: This could be displayed in a nice rating plot over time - - df = df.astype(str).applymap(lambda x: add_color(x)) - - print_rich_table( - df, - headers=df.columns, - show_index=True, - title="Rating", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rot", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_price_targets( - symbol: str, limit: int = 10, export: str = "", sheet_name: Optional[str] = None -): - """Display price targets for a given ticker. [Source: Financial Modeling Prep] - - Parameters - ---------- - symbol : str - Symbol - limit: int - Number of last days ratings to display - export: str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - """ - columns_to_show = [ - "publishedDate", - "analystCompany", - "adjPriceTarget", - "priceWhenPosted", - ] - price_targets = fmp_model.get_price_targets(symbol) - if price_targets.empty: - console.print(f"[red]No price targets found for {symbol}[/red]\n") - return - price_targets["publishedDate"] = price_targets["publishedDate"].apply( - lambda x: datetime.strptime(x, "%Y-%m-%dT%H:%M:%S.%fZ").strftime( - "%Y-%m-%d %H:%M" - ) - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "pt", - price_targets, - sheet_name, - ) - - print_rich_table( - price_targets[columns_to_show].head(limit), - headers=["Date", "Company", "Target", "Posted Price"], - show_index=False, - title=f"{symbol.upper()} Price Targets", - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/key_metrics_explained.txt b/openbb_terminal/stocks/fundamental_analysis/key_metrics_explained.txt deleted file mode 100644 index 7e5388d7c1d..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/key_metrics_explained.txt +++ /dev/null @@ -1,35 +0,0 @@ -EBITDA -Earnings (net income) before interest, taxes, depreciation, and amortisation. EBITDA can be used to analyse and compare profitability among companies and industries, as it eliminates the effects of financing and capital expenditures. - -EARNINGS PER SHARE (EPS) -This is the portion of a company’s profit that is assigned to each share of its stock. It’s essentially the bottom line net income, just on a per-share basis. A growing EPS is a good sign to investors because it means that their shares are likely to be worth more. - -PRICE TO EARNINGS RATIO (P/E) -The price-to-earnings ratio measures the relationship between the stock price of a company and its per-share earnings. It helps investors determine if a stock is undervalued or overvalued relative to others in the same sector. Since the P/E ratio shows what the market is willing to pay today for a stock based on its past or future earnings, investors simply compare the P/E ratio of a stock to those of its competitors and industry standards. A lower P/E ratio means the current stock price is low relative to earnings, which is favourable to investors. - -PROJECTED EARNINGS GROWTH (PEG) -The P/E ratio is a good fundamental analysis indicator but is somewhat limited by the fact that it doesn’t include future earnings growth. The PEG compensates for this by anticipating the one-year earnings growth rate of the stock. Analysts can estimate the future growth rate of a company by looking at its historical growth rate. This provides a more complete picture of a stock’s valuation. To calculate Projected Earnings Growth, divide the P/E ratio by the company’s 12-month growth rate. The percentage in growth rate is removed in the calculation. - -FREE CASH FLOW (FCF) -In simplest terms, Free Cash Flow is the cash left over after a company has paid for its operating expenses and capital expenditures. Cash is critical to the sustenance and amelioration of a business. Companies with high free cash flow can improve shareholder value, fund innovation, and survive downturns better than their less-liquid counterparts. Many investors cherish FCF as a fundamental indicator because it shows whether a company still has enough cash to reward its shareholders through dividends after funding operations and capital expenditures. Here's where this can be useful, if a company's P/E ratio makes it look excessively cheap or expensive, calculating the company's free cash flow can help you understand why. - -PRICE TO BOOK RATIO (P/B) -Also known as the price to equity ratio, the price-to-book ratio is a fundamental analysis indicator that compares the book value of a stock to its market value. By showing the difference between the stock’s market value and the value the company has stated in its financial books, P/B helps investors determine whether the stock is under or overvalued relative to its book value. It is the theoretical value of a company if it will be liquidated. Value investors often look for low P/B multiples when searching for bargain-priced stocks. - -RETURN ON EQUITY (ROE) -ROE is a profitability ratio that signifies the rate of return a shareholder receives for the portion of their investment in that company. It measures how well a company generates positive returns to its shareholders’ investment. Since profit is an actual driver of stock prices, separating out the profits earned with shareholder equity is actually a pretty good indicator of the financial health of a company and fair value of its stock. - -DIVIDEND PAYOUT RATIO (DPR) -As you know, companies pay a part of their profits to their shareholders in the form of dividends. But it’s also important to know how well the company’s earnings support those dividend payments. This is what the Dividend Payout Ratio is all about. It tells you what portion of net income a company returns to its shareholders, as well as how much it sets aside for growth, cash reserve, and debt repayments. - -PRICE TO SALES RATIO (P/S) -The price-to-sales ratio is a fundamental analysis indicator that can help determine the fair value of a stock by utilising a company’s market capitalisation and revenue. It shows how much the market values the company’s sales, which can be effective in valuing growth stocks that have yet to turn a profit or aren’t performing as expected due to a temporary setback. The P/S formula is calculated by dividing sales per share by the market value per share. A lower P/S ratio is seen as a good sign by investors. This is another metric that’s also useful when comparing companies in the same sector or industry. - -DIVIDEND YIELD RATIO -Dividend yield ratio looks at the amount paid by a company in dividends every year relative to its share price. It is an estimate of the dividend-only return of a stock investment. Assuming there are no changes to the dividend, the yield features an inverse relationship with the stock price — the yield rises when the stock price falls and vice versa. This is important to investors because it tells them how much they are getting back from every dollar they’ve invested in the company’s stock. - -DEBT-TO-EQUITY RATIO (D/E) -The debt to equity ratio is the last fundamental analysis indicator on this list. It measures the relationship between a company’s borrowed capital and the capital provided by its shareholders. Investors can use it to determine how a company finances its assets. The debt-to-equity ratio (D/E) helps investors evaluate the financial leverage of a company, signalling just how much shareholder’s equity can fulfil obligations to creditors should the business encounter financial hardship. - -Beta -Beta is a measurement of how reactive a stock is, compared to the overall market, generally defined as the S&P 500. A beta of 1 indicates that a stock tends to move in line with the S&P 500 -- that is, a 5% upward move in the index should produce roughly the same move in the stock. A beta of less than 1 indicates that a stock is less reactive to market swings, while a beta of more than 1 indicates a more volatile stock. For example, if a stock's beta is 0.4, a 10% move in the S&P 500 should theoretically produce a 4% swing in the stock. And a negative beta indicates that a stock tends to move in the opposite direction of the market. Beta can be found in most stock quotes, and it will give you an idea of how much volatility you're taking on before you buy a stock. diff --git a/openbb_terminal/stocks/fundamental_analysis/marketwatch_model.py b/openbb_terminal/stocks/fundamental_analysis/marketwatch_model.py deleted file mode 100644 index d74d21619a3..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/marketwatch_model.py +++ /dev/null @@ -1,351 +0,0 @@ -""" Fundamental Analysis Market Watch Model """ - -__docformat__ = "numpy" - -import logging -import re -from typing import List, Tuple - -import numpy as np -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - get_user_agent, - lambda_clean_data_values_to_float, - lambda_int_or_round_float, - request, -) - -# pylint: disable=too-many-branches - - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def prepare_df_financials( - symbol: str, statement: str, quarter: bool = False -) -> pd.DataFrame: - """Builds a DataFrame with financial statements for a given company - - Parameters - ---------- - symbol : str - Company's stock symbol - statement : str - Either income, balance or cashflow - quarter : bool, optional - Return quarterly financial statements instead of annual, by default False - - Returns - ------- - pd.DataFrame - A DataFrame with financial info - - Raises - ------ - ValueError - If statement is not income, balance or cashflow - """ - financial_urls = { - "income": { - "quarter": "https://www.marketwatch.com/investing/stock/{}/financials/income/quarter", - "annual": "https://www.marketwatch.com/investing/stock/{}/financials/income", - }, - "balance": { - "quarter": "https://www.marketwatch.com/investing/stock/{}/financials/balance-sheet/quarter", - "annual": "https://www.marketwatch.com/investing/stock/{}/financials/balance-sheet", - }, - "cashflow": { - "quarter": "https://www.marketwatch.com/investing/stock/{}/financials/cash-flow/quarter", - "annual": "https://www.marketwatch.com/investing/stock/{}/financials/cash-flow", - }, - } - - if statement not in financial_urls: - raise ValueError(f"type {statement} is not in {financial_urls.keys()}") - - period = "quarter" if quarter else "annual" - - text_soup_financials = BeautifulSoup( - request( - financial_urls[statement][period].format(symbol), - headers={"User-Agent": get_user_agent()}, - ).text, - "lxml", - ) - - # Define financials columns - a_financials_header = [ - financials_header.text.strip("\n").split("\n")[0] - for financials_header in text_soup_financials.findAll( - "th", {"class": "overflow__heading"} - ) - ] - - s_header_end_trend = ("5-year trend", "5- qtr trend")[quarter] - - if s_header_end_trend not in a_financials_header: - return pd.DataFrame() - - if s_header_end_trend in a_financials_header: - df_financials = pd.DataFrame( - columns=a_financials_header[ - 0 : a_financials_header.index(s_header_end_trend) - ] - ) - else: - return pd.DataFrame() - - find_table = text_soup_financials.findAll( - "div", {"class": "element element--table table--fixed financials"} - ) - - if not find_table: - return df_financials - - financials_rows = find_table[0].findAll( - "tr", {"class": ["table__row is-highlighted", "table__row"]} - ) - - for a_row in financials_rows: - constructed_row = [] - financial_columns = a_row.findAll( - "td", {"class": ["overflow__cell", "overflow__cell fixed--column"]} - ) - - if not financial_columns: - continue - - for a_column in financial_columns: - column_to_text = a_column.text.strip() - if "\n" in column_to_text: - column_to_text = column_to_text.split("\n")[0] - - if column_to_text == "": - continue - constructed_row.append(column_to_text) - - df_financials.loc[len(df_financials)] = constructed_row - - return df_financials - - -@log_start_end(log=logger) -def get_sean_seah_warnings( - symbol: str, debug: bool = False -) -> Tuple[pd.DataFrame, List[str], List[str]]: - """Get financial statements and prepare Sean Seah warnings - - Parameters - ---------- - symbol : str - Ticker to look at - debug : bool, optional - Whether or not to return debug messages. - Defaults to False. - - Returns - ------- - pd.DataFrame - Dataframe of financials - List[str] - List of warnings - List[str] - List of debug messages - """ - - # From INCOME STATEMENT, get: 'EPS (Basic)', 'Net Income', 'Interest Expense', 'EBITDA' - url_financials = ( - f"https://www.marketwatch.com/investing/stock/{symbol}/financials/income" - ) - text_soup_financials = BeautifulSoup( - request(url_financials, headers={"User-Agent": get_user_agent()}).text, - "lxml", - ) - - # Define financials columns - a_financials_header = [ - financials_header.text.strip("\n").split("\n")[0] - for financials_header in text_soup_financials.findAll( - "th", {"class": "overflow__heading"} - ) - ] - - df_financials = pd.DataFrame(columns=a_financials_header[0:-1]) - - # Add financials values - soup_financials = text_soup_financials.findAll( - lambda tag: tag.name == "tr" and tag.get("class") == ["table__row"] - ) - soup_financials += text_soup_financials.findAll( - "tr", {"class": "table__row is-highlighted"} - ) - for financials_info in soup_financials: - financials_row = financials_info.text.split("\n") - if len(financials_row) > 5: - for item in financials_row: - if bool(re.search(r"\d", item)): - a_financials_info = financials_info.text.split("\n") - l_financials = [a_financials_info[2]] - l_financials.extend(a_financials_info[5:-2]) - # Append data values to financials - df_financials.loc[len(df_financials.index)] = l_financials - break - - l_fin = ["EPS (Basic)", "Net Income", "Interest Expense", "EBITDA"] - - if not all(elem in df_financials["Item"].values for elem in l_fin): - return pd.DataFrame(), [], [] - - # Set item name as index - df_financials = df_financials.set_index("Item") - - df_sean_seah = df_financials.loc[l_fin] - - # From BALANCE SHEET, get: 'Liabilities & Shareholders\' Equity', 'Long-Term Debt' - url_financials = ( - f"https://www.marketwatch.com/investing/stock/{symbol}/financials/balance-sheet" - ) - text_soup_financials = BeautifulSoup( - request(url_financials, headers={"User-Agent": get_user_agent()}).text, - "lxml", - ) - - # Define financials columns - a_financials_header = [] - for financials_header in text_soup_financials.findAll( - "th", {"class": "overflow__heading"} - ): - a_financials_header.append(financials_header.text.strip("\n").split("\n")[0]) - - s_header_end_trend = "5-year trend" - df_financials = pd.DataFrame( - columns=a_financials_header[0 : a_financials_header.index(s_header_end_trend)] - ) - - # Add financials values - soup_financials = text_soup_financials.findAll( - lambda tag: tag.name == "tr" and tag.get("class") == ["table__row"] - ) - soup_financials += text_soup_financials.findAll( - "tr", {"class": "table__row is-highlighted"} - ) - for financials_info in soup_financials: - financials_row = financials_info.text.split("\n") - if len(financials_row) > 5: - for item in financials_row: - if bool(re.search(r"\d", item)): - a_financials_info = financials_info.text.split("\n") - l_financials = [a_financials_info[2]] - l_financials.extend(a_financials_info[5:-2]) - # Append data values to financials - df_financials.loc[len(df_financials.index)] = l_financials - break - - # Set item name as index - df_financials = df_financials.set_index("Item") - - # Create dataframe to compute meaningful metrics from sean seah book - transfer_cols = [ - "Total Shareholders' Equity", - "Liabilities & Shareholders' Equity", - "Long-Term Debt", - ] - df_sean_seah = pd.concat([df_sean_seah, df_financials.loc[transfer_cols]]) - - # Clean these metrics by parsing their values to float - df_sean_seah = df_sean_seah.applymap(lambda x: lambda_clean_data_values_to_float(x)) - df_sean_seah = df_sean_seah.T - - # Add additional necessary metrics - df_sean_seah["ROE"] = ( - df_sean_seah["Net Income"] / df_sean_seah["Total Shareholders' Equity"] - ) - - df_sean_seah["Interest Coverage Ratio"] = ( - df_sean_seah["EBITDA"] / df_sean_seah["Interest Expense"] - ) - - df_sean_seah["ROA"] = ( - df_sean_seah["Net Income"] / df_sean_seah["Liabilities & Shareholders' Equity"] - ) - df_sean_seah = df_sean_seah.sort_index() - df_sean_seah = df_sean_seah.T - - n_warnings = 0 - warnings = [] - debugged_warnings = [] - - if np.any(df_sean_seah.loc["EPS (Basic)"].diff().dropna().values < 0): - warnings.append("No consistent historical earnings per share") - n_warnings += 1 - if debug: - sa_eps = np.array2string( - df_sean_seah.loc["EPS (Basic)"].values, - formatter={"float_kind": lambda x: lambda_int_or_round_float(x)}, - ) - sa_growth = np.array2string( - df_sean_seah.loc["EPS (Basic)"].diff().dropna().values, - formatter={"float_kind": lambda x: lambda_int_or_round_float(x)}, - ) - debugged_warnings.append(f"\tEPS: {sa_eps}\n\tGrowth: {sa_growth} < 0") - - if np.any(df_sean_seah.loc["ROE"].values < 0.15): - warnings.append("NOT consistently high return on equity") - n_warnings += 1 - if debug: - sa_roe = np.array2string( - df_sean_seah.loc["ROE"].values, - formatter={"float_kind": lambda x: lambda_int_or_round_float(x)}, - ) - debugged_warnings.append(f"\tROE: {sa_roe} < 0.15") - - if np.any(df_sean_seah.loc["ROA"].values < 0.07): - warnings.append("NOT consistently high return on assets") - n_warnings += 1 - if debug: - sa_roa = np.array2string( - df_sean_seah.loc["ROA"].values, - formatter={"float_kind": lambda x: lambda_int_or_round_float(x)}, - ) - debugged_warnings.append(f"\tROA: {sa_roa} < 0.07") - - if np.any( - df_sean_seah.loc["Long-Term Debt"].values - > 5 * df_sean_seah.loc["Net Income"].values - ): - warnings.append("5x Net Income < Long-Term Debt") - n_warnings += 1 - if debug: - sa_5_net_income = np.array2string( - 5 * df_sean_seah.loc["Net Income"].values, - formatter={"float_kind": lambda x: lambda_int_or_round_float(x)}, - ) - sa_long_term_debt = np.array2string( - df_sean_seah.loc["Long-Term Debt"].values, - formatter={"float_kind": lambda x: lambda_int_or_round_float(x)}, - ) - debugged_warnings.append( - f"\t5x NET Income: {sa_5_net_income}\n\tlower than Long-Term Debt: {sa_long_term_debt}" - ) - - if np.any(df_sean_seah.loc["Interest Coverage Ratio"].values < 3): - warnings.append("Interest coverage ratio less than 3") - n_warnings += 1 - if debug: - sa_interest_coverage_ratio = np.array2string( - 100 * df_sean_seah.loc["Interest Coverage Ratio"].values, - formatter={"float_kind": lambda x: lambda_int_or_round_float(x)}, - ) - debugged_warnings.append( - f"\tInterest Coverage Ratio: {sa_interest_coverage_ratio} < 3" - ) - - return ( - df_sean_seah.applymap(lambda x: lambda_int_or_round_float(x)), - warnings, - debugged_warnings, - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/marketwatch_view.py b/openbb_terminal/stocks/fundamental_analysis/marketwatch_view.py deleted file mode 100644 index 0e8dde7385b..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/marketwatch_view.py +++ /dev/null @@ -1,55 +0,0 @@ -""" Market Watch View """ - -__docformat__ = "numpy" - -import logging - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import marketwatch_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@log_start_end(log=logger) -def display_sean_seah_warnings(symbol: str, debug: bool = False): - """Display Sean Seah warnings - - Parameters - ---------- - symbol : str - Stock ticker - debug : bool, optional - Whether or not to return debug messages. - Defaults to False. - """ - - financials, warnings, debugged_warnings = marketwatch_model.get_sean_seah_warnings( - symbol, debug - ) - - if financials.empty: - console.print(f"No financials found for {symbol}\n") - return - - print_rich_table( - financials, - headers=list(financials.columns), - title="Sean Seah Warnings", - show_index=True, - ) - - if not warnings: - console.print("No warnings found. Good stonk") - return - - messages = ( - [item for pair in zip(warnings, debugged_warnings) for item in pair] - if debug - else warnings - ) - - console.print("Warnings:\n") - console.print("\n".join(messages)) diff --git a/openbb_terminal/stocks/fundamental_analysis/nasdaq_model.py b/openbb_terminal/stocks/fundamental_analysis/nasdaq_model.py deleted file mode 100644 index 928a9ef93d1..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/nasdaq_model.py +++ /dev/null @@ -1,93 +0,0 @@ -""" Nasdaq Model """ - -__docformat__ = "numpy" - -import logging -from typing import Optional - -import pandas as pd -from requests.exceptions import ReadTimeout - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - request, -) -from openbb_terminal.rich_config import console - -# pylint: disable=too-many-branches - - -logger = logging.getLogger(__name__) - -FORM_GROUP = { - "annual": "Annual%20Reports", - "quarterly": "Quarterly%20Reports", - "proxies": "Proxies%20and%20Info%20Statements", - "insiders": "Insider%20Transactions", - "8-K": "8-K%20Related", - "registrations": "Registration%20Statements", - "comments": "Comment%20Letters", -} - - -@log_start_end(log=logger) -def get_sec_filings( - symbol: str, - limit: int = 20, - year: Optional[int] = None, - form_group: Optional[str] = None, -) -> pd.DataFrame: - """Get SEC filings for a given stock ticker. [Source: Nasdaq] - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - The number of filings to return - year: Optional[int] - The year to grab from. The year will be ignored if form_group is not specified - form_group: Optional[str] - The form type to filter for: - Choose from: annual, quarterly, proxies, insiders, 8-K, registrations, comments - - Returns - ------- - df_financials : pd.DataFrame - SEC filings data - """ - base_url = f"https://api.nasdaq.com/api/company/{symbol}/sec-filings" - arguments = f"?limit={limit}&sortColumn=filed&sortOrder=desc&IsQuoteMedia=true" - valid_form_group = False - if form_group in FORM_GROUP: - arguments = f"{arguments}&formGroup={FORM_GROUP[form_group]}" - valid_form_group = True - if year and valid_form_group: - arguments = f"{arguments}&Year={year}" - elif year: - console.print("Year will be ignored if form_group is not specified") - try: - response = request(base_url + arguments) - except ReadTimeout: - return pd.DataFrame() - try: - data = response.json()["data"]["rows"] - except KeyError: - return pd.DataFrame() - final_df = pd.DataFrame(data) - try: - final_df["view"] = final_df["view"].apply(lambda x: x["htmlLink"]) - except KeyError: - return pd.DataFrame() - final_df = final_df.rename( - columns={ - "companyName": "Company Name", - "reportingOwner": "Reporting Owner", - "formType": "Form Type", - "filed": "Filed", - "view": "View", - "period": "Period", - } - ) - final_df = final_df.set_index("Filed") - return final_df diff --git a/openbb_terminal/stocks/fundamental_analysis/nasdaq_view.py b/openbb_terminal/stocks/fundamental_analysis/nasdaq_view.py deleted file mode 100644 index b00852afbcc..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/nasdaq_view.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Nasdaq View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import nasdaq_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def sec_filings( - symbol: str, - limit: int = 5, - export: str = "", - sheet_name: Optional[str] = None, - year: Optional[int] = None, - form_group: Optional[str] = None, -): - """Display SEC filings for a given stock ticker. [Source: Nasdaq] - - Parameters - ---------- - symbol: str - Stock ticker symbol - limit: int - Number of ratings to display - export: str - Export dataframe data to csv,json,xlsx file - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - year: Optional[int] - The year to grab from. The year will be ignored if form_group is not specified - form_group: Optional[str] - The form type to filter for: - Choose from: annual, quarterly, proxies, insiders, 8-K, registrations, comments - """ - df_financials = nasdaq_model.get_sec_filings( - symbol.upper().replace("-", "."), limit, year, form_group - ) - if not isinstance(df_financials, pd.DataFrame) or df_financials.empty: - console.print(f"No data found for {symbol}") - return - print_rich_table( - df_financials, - headers=list(df_financials.columns), - show_index=True, - title="SEC Filings", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "sec", - df_financials, - sheet_name, - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/polygon_model.py b/openbb_terminal/stocks/fundamental_analysis/polygon_model.py deleted file mode 100644 index 6da5442233d..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/polygon_model.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Polygon Model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_POLYGON_KEY"]) -def get_financials( - symbol: str, statement: str, quarterly: bool = False, ratios: bool = False -) -> pd.DataFrame: - """Get ticker financial statements from polygon - - Parameters - ---------- - symbol: str - Stock ticker symbol - statement: str - Financial statement data to retrieve, can be balance, income or cash - quarterly:bool - Flag to get quarterly reports, by default False - ratios: bool - Shows percentage change, by default False - - Returns - ------- - pd.DataFrame - Balance Sheets or Income Statements - """ - # Note the filing date is over 30 years so will always get as many as allowed - json_request = request( - "https://api.polygon.io/vX/reference/financials?" - f"ticker={symbol}" - f"&timeframe={['annual','quarterly'][quarterly]}" - "&limit=100" - "&filing_date.gte=1980-01-01" - f"&apiKey={get_current_user().credentials.API_POLYGON_KEY}" - ).json() - - if statement not in ["balance", "income", "cash"]: - console.print("financial must be 'balance' or 'income'.\n") - return pd.DataFrame() - - if json_request["status"] == "ERROR": - console.print(json_request["status"]) - return pd.DataFrame() - - all_results = json_request["results"] - - if len(all_results) == 0: - console.print("No financials found from Polygon.\n") - return pd.DataFrame() - - balance_sheets = pd.DataFrame() - income_statements = pd.DataFrame() - cash_flows = pd.DataFrame() - first = True - for single_thing in all_results: - if first: - balance_sheets = pd.concat( - [ - pd.DataFrame(), - pd.DataFrame.from_dict( - single_thing["financials"]["balance_sheet"], orient="index" - ), - ], - axis=1, - ) - balance_sheets = balance_sheets[["value"]] - balance_sheets.columns = [single_thing["filing_date"]] - - income_statements = pd.concat( - [ - pd.DataFrame(), - pd.DataFrame.from_dict( - single_thing["financials"]["income_statement"], orient="index" - ), - ], - axis=1, - ) - income_statements = income_statements[["value"]] - income_statements.columns = [single_thing["filing_date"]] - - cash_flows = pd.concat( - [ - pd.DataFrame(), - pd.DataFrame.from_dict( - single_thing["financials"]["cash_flow_statement"], - orient="index", - ), - ], - axis=1, - ) - cash_flows = cash_flows[["value"]] - cash_flows.columns = [single_thing["filing_date"]] - - first = False - else: - values = pd.DataFrame( - pd.DataFrame.from_dict( - single_thing["financials"]["balance_sheet"], orient="index" - ).value - ) - values.columns = [single_thing["filing_date"]] - balance_sheets = pd.concat([balance_sheets, values], axis=1) - - values = pd.DataFrame( - pd.DataFrame.from_dict( - single_thing["financials"]["income_statement"], orient="index" - ).value - ) - values.columns = [single_thing["filing_date"]] - income_statements = pd.concat([income_statements, values], axis=1) - if "cash_flow_statement" in single_thing["financials"]: - values = pd.DataFrame( - pd.DataFrame.from_dict( - single_thing["financials"]["cash_flow_statement"], - orient="index", - ).value - ) - values.columns = [single_thing["filing_date"]] - cash_flows = pd.concat([cash_flows, values], axis=1) - if statement == "balance": - df_fa = balance_sheets - elif statement == "income": - df_fa = income_statements - elif statement == "cash": - df_fa = cash_flows - else: - return pd.DataFrame() - - if ratios: - types = df_fa.copy().applymap(lambda x: isinstance(x, (float, int))) - types = types.all(axis=1) - - # For rows with complete data - valid = [] - i = 0 - for row in types: - if row: - valid.append(i) - i += 1 - df_fa_pc = df_fa.iloc[valid].pct_change(axis="columns", periods=-1).fillna(0) - j = 0 - for i in valid: - df_fa.iloc[i] = df_fa_pc.iloc[j] - j += 1 - - return df_fa diff --git a/openbb_terminal/stocks/fundamental_analysis/polygon_view.py b/openbb_terminal/stocks/fundamental_analysis/polygon_view.py deleted file mode 100644 index 5426b137398..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/polygon_view.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Polygon view""" - -__docformat__ = "numpy" -import logging -import os -from typing import Optional - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.fundamental_analysis import polygon_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_POLYGON_KEY"]) -def display_fundamentals( - symbol: str, - statement: str, - limit: int = 10, - quarterly: bool = False, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display tickers balance sheet or income statement - - Parameters - ---------- - symbol: str - Stock ticker symbol - statement:str - Either balance or income - limit: int - Number of results to show, by default 10 - quarterly: bool - Flag to get quarterly reports, by default False - ratios: bool - Shows percentage change, by default False - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - fig = OpenBBFigure() - - fundamentals = polygon_model.get_financials(symbol, statement, quarterly, ratios) - title_str = { - "balance": "Balance Sheet", - "income": "Income Statement", - "cash": "Cash Flows", - }[statement] - - if fundamentals.empty: - return - - fundamentals = fundamentals.iloc[:, :limit] - fundamentals = fundamentals[fundamentals.columns[::-1]] - if statement == "income": - fundamentals.index = [ - stocks_helper.INCOME_PLOT["Polygon"][i] - for i in [i.replace(" ", "_") for i in fundamentals.index.str.lower()] - ] - elif statement == "balance": - fundamentals.index = [ - stocks_helper.BALANCE_PLOT["Polygon"][i] - for i in [i.replace(" ", "_") for i in fundamentals.index.str.lower()] - ] - elif statement == "cash": - fundamentals.index = [ - stocks_helper.CASH_PLOT["Polygon"][i] - for i in [i.replace(" ", "_") for i in fundamentals.index.str.lower()] - ] - - if plot: - fundamentals_plot_data = fundamentals.copy().fillna(-1) - rows_plot = len(plot) - fundamentals_plot_data = fundamentals_plot_data.transpose() - - if rows_plot == 1: - fig.add_scatter( - x=fundamentals_plot_data.index, - y=fundamentals_plot_data[plot[0]], - name=plot[0].replace("_", " "), - ) - title = ( - f"{plot[0].replace('_', ' ').lower()} {'QoQ' if quarterly else 'YoY'} Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ')} of {symbol.upper()}" - ) - fig.set_title(title) - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_scatter( - x=fundamentals_plot_data.index, - y=fundamentals_plot_data[plot[i]], - name=plot[i].replace("_", " "), - row=i + 1, - col=1, - ) - fig.set_title(f"{plot[i].replace('_', ' ')}", row=i + 1, col=1) - - fig.show(external=fig.is_image_export(export)) - else: - # Snake case to english - fundamentals.index = fundamentals.index.to_series().apply( - lambda x: x.replace("_", " ").title() - ) - - # Readable numbers - fundamentals = fundamentals.applymap(lambda_long_number_format).fillna("-") - print_rich_table( - fundamentals.applymap(lambda x: "-" if x == "nan" else x), - show_index=True, - title=( - f"{symbol} {title_str}" - if not ratios - else f"{'QoQ' if quarterly else 'YoY'} Change of {symbol} {title_str}" - ), - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - statement, - fundamentals, - sheet_name, - fig, - ) diff --git a/openbb_terminal/stocks/fundamental_analysis/sdk_helpers.py b/openbb_terminal/stocks/fundamental_analysis/sdk_helpers.py deleted file mode 100644 index bce74151301..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/sdk_helpers.py +++ /dev/null @@ -1,276 +0,0 @@ -"""SDK Helper Functions""" - -__docformat__ = "numpy" - -import pandas as pd - -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import ( - av_model, - eodhd_model, - finviz_model, - fmp_model, - polygon_model, - yahoo_finance_model, -) - - -def get_overview(symbol: str, source: str = "YahooFinance"): - """Get overview. - - Parameters - ---------- - symbol : str - Symbol to get overview for - source : str, optional - Data source for overview, by default "YahooFinance" - Sources: YahooFinance, AlphaVantage, FinancialModelingPrep, Finviz - - Returns - ------- - pd.DataFrame - Dataframe of overview - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> overview = openbb.stocks.fa.overview("AAPL", source="AlphaVantage") - """ - if source == "YahooFinance": - return yahoo_finance_model.get_info(symbol=symbol) - if source == "AlphaVantage": - return av_model.get_overview(symbol=symbol) - if source == "FinancialModelingPrep": - return fmp_model.get_profile(symbol=symbol) - if source == "Finviz": - return finviz_model.get_data(symbol=symbol) - return pd.DataFrame() - - -def get_income_statement( - symbol: str, - quarterly: bool = False, - ratios: bool = False, - source: str = "YahooFinance", - limit: int = 10, -) -> pd.DataFrame: - """Get income statement. - - Parameters - ---------- - symbol : str - Symbol to get income statement for - source : str, optional - Data source for income statement, by default "YahooFinance" - Sources: YahooFinance, AlphaVantage, FinancialModelingPrep, Polygon, EODHD - quarterly : bool, optional - Flag to get quarterly data - ratios : bool, optional - Flag to return data as a percent change. - limit : int - Number of statements to return (free tiers may be limited to 5 years) - - Returns - ------- - pd.DataFrame - Dataframe of income statement - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> income_statement = openbb.stocks.fa.income("AAPL", source="YahooFinance") - - If you have a premium AlphaVantage key, you can use the quarterly flag to get quarterly statements - >>> quarterly_income_statement = openbb.stocks.fa.income("AAPL", source="AlphaVantage", quarterly=True) - """ - if source == "YahooFinance": - if quarterly: - console.print( - "Quarterly income statement not available from Yahoo Finance. Returning annual" - ) - df = yahoo_finance_model.get_financials( - symbol=symbol, statement="financials", ratios=ratios - ) - return df - if source == "AlphaVantage": - df = av_model.get_income_statements( - symbol=symbol, quarterly=quarterly, ratios=ratios, limit=limit - ) - return df - if source == "FinancialModelingPrep": - df = fmp_model.get_income( - symbol=symbol, limit=limit, quarterly=quarterly, ratios=ratios - ) - return df - if source == "Polygon": - df = polygon_model.get_financials(symbol, "income", quarterly, ratios) - return df - if source == "EODHD": - df = eodhd_model.get_financials(symbol, "income", quarterly, ratios) - return df - return pd.DataFrame() - - -def get_balance_sheet( - symbol: str, - quarterly: bool = False, - ratios: bool = False, - source: str = "YahooFinance", - limit: int = 10, -) -> pd.DataFrame: - """Get balance sheet. - - Parameters - ---------- - symbol : str - Symbol to get balance sheet for - source : str, optional - Data source for balance sheet, by default "YahooFinance" - Sources: YahooFinance, AlphaVantage, FinancialModelingPrep, Polygon, EODHD - quarterly : bool, optional - Flag to get quarterly data - ratios : bool, optional - Flag to return data as a percent change. - limit : int - Number of statements to return (free tiers may be limited to 5 years) - - Returns - ------- - pd.DataFrame - Dataframe of balance sheet - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> balance_sheet = openbb.stocks.fa.balance("AAPL", source="YahooFinance") - - If you have a premium AlphaVantage key, you can use the quarterly flag to get quarterly statements - >>> quarterly_income_statement = openbb.stocks.fa.balance("AAPL", source="AlphaVantage", quarterly=True) - """ - if source == "YahooFinance": - if quarterly: - console.print( - "Quarterly statements not available from Yahoo Finance. Returning annual" - ) - df = yahoo_finance_model.get_financials( - symbol=symbol, statement="balance-sheet", ratios=ratios - ) - return df - if source == "AlphaVantage": - df = av_model.get_balance_sheet( - symbol=symbol, quarterly=quarterly, ratios=ratios, limit=limit - ) - return df - if source == "FinancialModelingPrep": - df = fmp_model.get_balance( - symbol=symbol, limit=limit, quarterly=quarterly, ratios=ratios - ) - return df - if source == "Polygon": - df = polygon_model.get_financials(symbol, "balance", quarterly, ratios) - return df - if source == "EODHD": - df = eodhd_model.get_financials(symbol, "balance", quarterly, ratios) - return df - return pd.DataFrame() - - -def get_cash_flow( - symbol: str, - quarterly: bool = False, - ratios: bool = False, - source: str = "YahooFinance", - limit: int = 10, -) -> pd.DataFrame: - """Get Cash Flow. - - Parameters - ---------- - symbol : str - Symbol to get cash flow for - source : str, optional - Data source for cash flow, by default "YahooFinance" - Sources: YahooFinance, AlphaVantage, FinancialModelingPrep, Polygon, EODHD - quarterly : bool, optional - Flag to get quarterly data - ratios : bool, optional - Flag to return data as a percent change. - limit : int - Number of statements to return (free tiers may be limited to 5 years) - - Returns - ------- - pd.DataFrame - Dataframe of cash flow - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> cash_flow = openbb.stocks.fa.cash("AAPL", source="YahooFinance") - - If you have a premium AlphaVantage key, you can use the quarterly flag to get quarterly statements - >>> quarterly_income_statement = openbb.stocks.fa.cash("AAPL", source="AlphaVantage", quarterly=True) - """ - if source == "YahooFinance": - if quarterly: - console.print( - "Quarterly statements not available from Yahoo Finance. Returning annual" - ) - df = yahoo_finance_model.get_financials( - symbol=symbol, statement="cash-flow", ratios=ratios - ) - return df - if source == "AlphaVantage": - df = av_model.get_cash_flow( - symbol=symbol, quarterly=quarterly, ratios=ratios, limit=limit - ) - return df - if source == "FinancialModelingPrep": - df = fmp_model.get_cash( - symbol=symbol, limit=limit, quarterly=quarterly, ratios=ratios - ) - return df - if source == "Polygon": - df = polygon_model.get_financials(symbol, "cash", quarterly, ratios) - return df - if source == "EODHD": - df = eodhd_model.get_financials(symbol, "cash", quarterly, ratios) - return df - return pd.DataFrame() - - -def earnings( - symbol: str, source: str = "YahooFinance", quarterly: bool = False -) -> pd.DataFrame: - """Get earnings data. - - Parameters - ---------- - symbol : str - Stock ticker - source : str, optional - Source to use, by default "AlphaVantage" - Sources: YahooFinance, AlphaVantage - quarterly : bool, optional - Flag to get quarterly data (AlphaVantage only), by default False. - - Returns - ------- - pd.DataFrame - Dataframe of earnings - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> aapl_earnings = openbb.stocks.fa.earnings("AAPL", source ="YahooFinance") - - To obtain quarterly earnings, use the quarterly flag with AlphaVantage - >>> aapl_earnings = openbb.stocks.fa.metrics("earnings", source ="AlphaVantage", quarterly=True) - """ - if source == "YahooFinance": - df = yahoo_finance_model.get_earnings_history(symbol) - return df - if source == "AlphaVantage": - df = av_model.get_earnings(symbol, quarterly) - return df - return pd.DataFrame() diff --git a/openbb_terminal/stocks/fundamental_analysis/seeking_alpha_model.py b/openbb_terminal/stocks/fundamental_analysis/seeking_alpha_model.py deleted file mode 100644 index e4856e5a2d2..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/seeking_alpha_model.py +++ /dev/null @@ -1,344 +0,0 @@ -""" Seeking Alpha Model """ - -__docformat__ = "numpy" - -import logging - -import pandas as pd -import requests - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import lambda_long_number_format - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_estimates_eps(ticker: str) -> pd.DataFrame: - """Takes the ticker, asks for seekingalphaID and gets eps estimates - - Parameters - ---------- - ticker: str - ticker of company - Returns - ------- - pd.DataFrame - eps estimates for the next 10yrs - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.fa.epsfc("AAPL") - """ - - url = "https://seekingalpha.com/api/v3/symbol_data/estimates" - - querystring = { - "estimates_data_items": "eps_normalized_actual,eps_normalized_consensus_low,eps_normalized_consensus_mean," - "eps_normalized_consensus_high,eps_normalized_num_of_estimates", - "period_type": "annual", - "relative_periods": "-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11", - } - - # add ticker_ids for the ticker - seek_id = get_seekingalpha_id(ticker) - querystring["ticker_ids"] = seek_id - - payload = "" - headers = { - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0", - "Accept": "*/*", - "Accept-Language": "de,en-US;q=0.7,en;q=0.3", - "Accept-Encoding": "gzip, deflate, br", - "Sec-Fetch-Dest": "empty", - "Sec-Fetch-Mode": "cors", - "Sec-Fetch-Site": "same-origin", - "Connection": "keep-alive", - } - - # semi random user agent -- disabled and static user agent because it might be the reason for 403 - # headers["User-Agent"] = get_user_agent() - - response = requests.request( - "GET", url, data=payload, headers=headers, params=querystring - ) - - # init - output = pd.DataFrame( - columns=[ - "fiscalyear", - "consensus_mean", - "change %", - "analysts", - "actual", - "consensus_low", - "consensus_high", - ] - ) - - # if no estimations exist, response is empty "reviews" and "reviews" - # {"revisions":{},"estimates":{}} - try: - seek_object = response.json()["estimates"][str(seek_id)] - - items = len(seek_object["eps_normalized_num_of_estimates"].keys()) - - for i in range(0, items - 3): - # python_dict - eps_estimates = {} - eps_estimates["fiscalyear"] = seek_object[ - "eps_normalized_num_of_estimates" - ][str(i)][0]["period"]["fiscalyear"] - eps_estimates["analysts"] = seek_object["eps_normalized_num_of_estimates"][ - str(i) - ][0]["dataitemvalue"] - try: - eps_estimates["actual"] = seek_object["eps_normalized_actual"][str(i)][ - 0 - ]["dataitemvalue"] - except Exception: - eps_estimates["actual"] = 0 - eps_estimates["consensus_low"] = seek_object[ - "eps_normalized_consensus_low" - ][str(i)][0]["dataitemvalue"] - eps_estimates["consensus_high"] = seek_object[ - "eps_normalized_consensus_high" - ][str(i)][0]["dataitemvalue"] - eps_estimates["consensus_mean"] = seek_object[ - "eps_normalized_consensus_mean" - ][str(i)][0]["dataitemvalue"] - - try: - this = float(eps_estimates["consensus_mean"]) - try: - prev = float( - seek_object["eps_normalized_actual"][str(i - 1)][0][ - "dataitemvalue" - ] - ) - except Exception: - prev = float( - seek_object["eps_normalized_consensus_mean"][str(i - 1)][0][ - "dataitemvalue" - ] - ) - - percent = ((this / prev) - 1) * 100 - except Exception: - percent = 0 - - eps_estimates["change %"] = percent - - # format correction (before return, so calculation still works) - eps_estimates["consensus_mean"] = lambda_long_number_format( - float(eps_estimates["consensus_mean"]) - ) - eps_estimates["consensus_low"] = lambda_long_number_format( - float(eps_estimates["consensus_low"]) - ) - eps_estimates["consensus_high"] = lambda_long_number_format( - float(eps_estimates["consensus_high"]) - ) - eps_estimates["actual"] = lambda_long_number_format( - float(eps_estimates["actual"]) - ) - - # df append replacement - new_row = pd.DataFrame(eps_estimates, index=[0]) - output = pd.concat([output, new_row]) - except Exception: - return pd.DataFrame() - - return output - - -@log_start_end(log=logger) -def get_estimates_rev(ticker: str) -> pd.DataFrame: - """Takes the ticker, asks for seekingalphaID and gets rev estimates - - Parameters - ---------- - ticker: str - ticker of company - Returns - ------- - pd.DataFrame - rev estimates for the next 10yrs - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.fa.revfc("AAPL") - """ - - url = "https://seekingalpha.com/api/v3/symbol_data/estimates" - - querystring = { - "estimates_data_items": "revenue_actual,revenue_consensus_low,revenue_consensus_mean," - "revenue_consensus_high,revenue_num_of_estimates", - "period_type": "annual", - "relative_periods": "-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11", - } - - # add ticker_ids for the ticker - seek_id = get_seekingalpha_id(ticker) - querystring["ticker_ids"] = seek_id - - payload = "" - headers = { - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0", - "Accept": "*/*", - "Accept-Language": "de,en-US;q=0.7,en;q=0.3", - "Accept-Encoding": "gzip, deflate, br", - "Sec-Fetch-Dest": "empty", - "Sec-Fetch-Mode": "cors", - "Sec-Fetch-Site": "same-origin", - "Connection": "keep-alive", - "TE": "trailers", - } - - # semi random user agent -- disabled and static user agent because it might be the reason for 403 - # headers["User-Agent"] = get_user_agent() - - response = requests.request( - "GET", url, data=payload, headers=headers, params=querystring - ) - - # init - # pd.empty should deliver true if no data-rows are added - output = pd.DataFrame( - columns=[ - "fiscalyear", - "consensus_mean", - "change %", - "analysts", - "actual", - "consensus_low", - "consensus_high", - ] - ) - - # if no estimations exist, response is empty "reviews" and "reviews" - # {"revisions":{},"estimates":{}} - try: - seek_object = response.json()["estimates"][seek_id] - - items = len(seek_object["revenue_num_of_estimates"].keys()) - - for i in range(0, items - 3): - # python_dict - revenue_estimates = {} - revenue_estimates["fiscalyear"] = seek_object["revenue_num_of_estimates"][ - str(i) - ][0]["period"]["fiscalyear"] - revenue_estimates["consensus_mean"] = seek_object["revenue_consensus_mean"][ - str(i) - ][0]["dataitemvalue"] - - revenue_estimates["analysts"] = seek_object["revenue_num_of_estimates"][ - str(i) - ][0]["dataitemvalue"] - if i < 1: - revenue_estimates["actual"] = seek_object["revenue_actual"][str(i)][0][ - "dataitemvalue" - ] - else: - revenue_estimates["actual"] = 0 - revenue_estimates["consensus_low"] = seek_object["revenue_consensus_low"][ - str(i) - ][0]["dataitemvalue"] - revenue_estimates["consensus_high"] = seek_object["revenue_consensus_high"][ - str(i) - ][0]["dataitemvalue"] - - try: - this = float(revenue_estimates["consensus_mean"]) - # if actual revenue is available, take it for the calc - try: - prev = float( - seek_object["revenue_actual"][str(i - 1)][0]["dataitemvalue"] - ) - except Exception: - prev = float( - seek_object["revenue_consensus_mean"][str(i - 1)][0][ - "dataitemvalue" - ] - ) - - percent = ((this / prev) - 1) * 100 - except Exception: - percent = float(0) - - revenue_estimates["change %"] = percent - - # format correction (before return, so calculation still works) - revenue_estimates["consensus_mean"] = lambda_long_number_format( - float(revenue_estimates["consensus_mean"]) - ) - revenue_estimates["consensus_low"] = lambda_long_number_format( - float(revenue_estimates["consensus_low"]) - ) - revenue_estimates["consensus_high"] = lambda_long_number_format( - float(revenue_estimates["consensus_high"]) - ) - revenue_estimates["actual"] = lambda_long_number_format( - float(revenue_estimates["actual"]) - ) - - # df append replacement - new_row = pd.DataFrame(revenue_estimates, index=[0]) - output = pd.concat([output, new_row]) - except Exception: - return pd.DataFrame() - - return output - - -@log_start_end(log=logger) -def get_seekingalpha_id(ticker: str) -> str: - """Takes the ticker, asks for seekingalphaID and returns it - - Parameters - ---------- - ticker: str - ticker of company - Returns - ------- - str: - seekingalphaID - to be used for further API calls - """ - - url = "https://seekingalpha.com/api/v3/searches" - - querystring = { - "filter[type]": "symbols", - "filter[list]": "all", - "page[size]": "1", - } - - querystring["filter[query]"] = ticker - payload = "" - - headers = { - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0", - "Accept": "*/*", - "Accept-Language": "de,en-US;q=0.7,en;q=0.3", - "Accept-Encoding": "gzip, deflate, br", - "Sec-Fetch-Dest": "empty", - "Sec-Fetch-Mode": "cors", - "Sec-Fetch-Site": "same-origin", - "Referer": "https://seekingalpha.com/", - "Connection": "keep-alive", - # "TE": "trailers", - } - - response = requests.request( - "GET", url, data=payload, headers=headers, params=querystring - ) - - try: - seekingalphaID = str(response.json()["symbols"][0]["id"]) - except Exception: - # for some reason no mapping possible - seekingalphaID = "0" - - return seekingalphaID diff --git a/openbb_terminal/stocks/fundamental_analysis/seeking_alpha_view.py b/openbb_terminal/stocks/fundamental_analysis/seeking_alpha_view.py deleted file mode 100644 index 051f7d1a3bd..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/seeking_alpha_view.py +++ /dev/null @@ -1,83 +0,0 @@ -""" Seeking Alpha View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis import seeking_alpha_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_eps_estimates( - symbol: str, export: str = "", sheet_name: Optional[str] = None -): - """Display eps Estimates - - Parameters - ---------- - symbol: str - ticker of company - """ - eps_estimates = seeking_alpha_model.get_estimates_eps(symbol) - - if not eps_estimates.empty: - print_rich_table( - eps_estimates, - headers=list(eps_estimates.columns), - show_index=False, - title=f"{symbol.upper()} EPS History and Estimations", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "eps_estimates", - eps_estimates, - sheet_name, - ) - else: - console.print("No data found.") - return - - -@log_start_end(log=logger) -def display_rev_estimates( - symbol: str, export: str = "", sheet_name: Optional[str] = None -): - """Display rev Estimates - - Parameters - ---------- - symbol: str - ticker of company - """ - - rev_estimates = seeking_alpha_model.get_estimates_rev(symbol) - - if not rev_estimates.empty: - print_rich_table( - rev_estimates, - headers=list(rev_estimates.columns), - show_index=False, - title=f"{symbol.upper()} Revenue History and Estimations", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "rev_estimates", - rev_estimates, - sheet_name, - ) - else: - console.print("No data found.") - return diff --git a/openbb_terminal/stocks/fundamental_analysis/yahoo_finance_model.py b/openbb_terminal/stocks/fundamental_analysis/yahoo_finance_model.py deleted file mode 100644 index c44a5199b24..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/yahoo_finance_model.py +++ /dev/null @@ -1,412 +0,0 @@ -"""Yahoo Finance Model""" - -__docformat__ = "numpy" - -import logging -import ssl -from datetime import datetime -from typing import Optional, Tuple -from urllib.request import Request, urlopen - -import numpy as np -import pandas as pd -import yfinance as yf -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import lambda_long_number_format -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis.fa_helper import clean_df_index - -logger = logging.getLogger(__name__) -# pylint: disable=W0212 -ssl._create_default_https_context = ssl._create_unverified_context - - -@log_start_end(log=logger) -def get_info(symbol: str) -> pd.DataFrame: - """Gets ticker symbol info - - Parameters - ---------- - symbol: str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - DataFrame of yfinance information - """ - stock = yf.Ticker(symbol) - df_info = pd.DataFrame(stock.fast_info.items(), columns=["Metric", "Value"]) - df_info = df_info.set_index("Metric") - - clean_df_index(df_info) - - if "Last split date" in df_info.index and df_info.loc["Last split date"].values[0]: - df_info.loc["Last split date"].values[0] = datetime.fromtimestamp( - df_info.loc["Last split date"].values[0] - ).strftime("%Y-%m-%d") - - df_info = df_info.mask(df_info["Value"].astype(str).eq("[]")).dropna() - df_info[df_info.index != "Zip"] = df_info[df_info.index != "Zip"].applymap( - lambda x: lambda_long_number_format(x) - ) - - df_info = df_info.rename( - index={ - "Address1": "Address", - "Average daily volume10 day": "Average daily volume 10 day", - "Average volume10days": "Average volume 10 days", - "Price to sales trailing12 months": "Price to sales trailing 12 months", - } - ) - df_info.index = df_info.index.str.replace("eps", "EPS") - df_info.index = df_info.index.str.replace("p e", "PE") - df_info.index = df_info.index.str.replace("Peg", "PEG") - return df_info - - -@log_start_end(log=logger) -def get_shareholders(symbol: str, holder: str = "institutional") -> pd.DataFrame: - """Get shareholders from yahoo - - Parameters - ---------- - symbol : str - Stock ticker symbol - holder : str - Which holder to get table for - - Returns - ------- - pd.DataFrame - Major holders - """ - stock = yf.Ticker(symbol) - - # Major holders - df_major_holders = stock.major_holders - df_major_holders[1] = df_major_holders[1].apply( - lambda x: x.replace("%", "Percentage") - ) - - # Institutional holders - df_institutional_shareholders = stock.institutional_holders - df_institutional_shareholders.columns = ( - df_institutional_shareholders.columns.str.replace("% Out", "Stake") - ) - df_institutional_shareholders["Shares"] = df_institutional_shareholders[ - "Shares" - ].apply(lambda x: lambda_long_number_format(x)) - df_institutional_shareholders["Value"] = df_institutional_shareholders[ - "Value" - ].apply(lambda x: lambda_long_number_format(x)) - df_institutional_shareholders["Stake"] = df_institutional_shareholders[ - "Stake" - ].apply(lambda x: str(f"{100 * x:.2f}") + " %") - - # Mutualfunds holders - df_mutualfund_shareholders = stock.mutualfund_holders - df_mutualfund_shareholders.columns = df_mutualfund_shareholders.columns.str.replace( - "% Out", "Stake" - ) - df_mutualfund_shareholders["Shares"] = df_mutualfund_shareholders["Shares"].apply( - lambda x: lambda_long_number_format(x) - ) - df_mutualfund_shareholders["Value"] = df_mutualfund_shareholders["Value"].apply( - lambda x: lambda_long_number_format(x) - ) - df_mutualfund_shareholders["Stake"] = df_mutualfund_shareholders["Stake"].apply( - lambda x: str(f"{100 * x:.2f}") + " %" - ) - - if holder == "major": - return df_major_holders - if holder == "institutional": - return df_institutional_shareholders - if holder == "mutualfund": - return df_mutualfund_shareholders - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_calendar_earnings(symbol: str) -> pd.DataFrame: - """Get calendar earnings for ticker symbol - - Parameters - ---------- - symbol: str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - Dataframe of calendar earnings - """ - stock = yf.Ticker(symbol) - df_calendar = stock.calendar - - if df_calendar.empty: - return pd.DataFrame() - - df_calendar.iloc[0, :] = df_calendar.iloc[0, :].apply( - lambda x: x.date().strftime("%m/%d/%Y") - ) - - df_calendar.iloc[1:, :] = df_calendar.iloc[1:, :].applymap( - lambda x: lambda_long_number_format(x) - ) - - return df_calendar.T - - -@log_start_end(log=logger) -def get_dividends(symbol: str) -> pd.DataFrame: - """Get historical dividend for ticker - - Parameters - ---------- - symbol: str - Ticker symbol to get dividend for - - Returns - ------- - pd.DataFrame - Dataframe of dividends and dates - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.fa.divs("AAPL") - """ - df = pd.DataFrame(yf.Ticker(symbol).dividends) - - if df.empty: - console.print("No dividends found.\n") - return pd.DataFrame() - - df["Change"] = df.diff() - df = df[::-1] - - return df - - -@log_start_end(log=logger) -def get_mktcap( - symbol: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, -) -> Tuple[pd.DataFrame, str]: - """Get market cap over time for ticker. [Source: Yahoo Finance] - - Parameters - ---------- - symbol: str - Ticker to get market cap over time - start_date: Optional[str] - Initial date (e.g., 2021-10-01). Defaults to 3 years back - - Returns - ------- - pd.DataFrame - Dataframe of estimated market cap over time - str: - Currency of ticker - """ - if start_date is None: - # Set data far in the past to ensure all data is returned - start_date = "1900-01-01" - if end_date is None: - end_date = datetime.now() # type: ignore - - currency = "" - df_data = yf.download( - symbol, - start=start_date, - end=end_date, - progress=False, - threads=False, - ignore_tz=True, - ) - if not df_data.empty: - data = yf.Ticker(symbol).fast_info - if data: - df_data["Adj Close"] = df_data["Adj Close"] * data["shares"] - df_data = df_data["Adj Close"] - - currency = data["currency"] if data["currency"] else "" - - return df_data, currency - - -@log_start_end(log=logger) -def get_splits(symbol: str) -> pd.DataFrame: - """Get splits and reverse splits events. [Source: Yahoo Finance] - - Parameters - ---------- - symbol: str - Ticker to get forward and reverse splits - - Returns - ------- - pd.DataFrame - Dataframe of forward and reverse splits - """ - data = yf.Ticker(symbol).splits - if not data.empty: - return data.to_frame() - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_financials(symbol: str, statement: str, ratios: bool = False) -> pd.DataFrame: - """Get cashflow statement for company - - Parameters - ---------- - symbol : str - Stock ticker symbol - statement: str - can be: - - - cash-flow - - financials for Income - - balance-sheet - - ratios: bool - Shows percentage change - - Returns - ------- - pd.DataFrame - Dataframe of Financial statement - """ - url = ( - "https://uk.finance.yahoo.com/quote/" - + symbol - + "/" - + statement - + "?p=" - + symbol - ) - - # Making the website believe that you are accessing it using a Mozilla browser - req = Request(url, headers={"User-Agent": "Mozilla/5.0"}) # noqa: S310 - - with urlopen(req) as response: # noqa: S310 - webpage = response.read() - soup = BeautifulSoup(webpage, "html.parser") - - features = soup.find_all("div", class_="D(tbr)") - headers = [] - temp_list = [] - final = [] - if len(features) == 0: - return console.print("No data found in Yahoo Finance\n") - - index = 0 # create headers - for item in features[0].find_all("div", class_="D(ib)"): - headers.append(item.text) # statement contents - while index <= len(features) - 1: - # filter for each line of the statement - temp = features[index].find_all("div", class_="D(tbc)") - for line in temp: - # each item adding to a temporary list - temp_list.append(line.text) - # temp_list added to final list - final.append(temp_list) - # clear temp_list - temp_list = [] - index += 1 - - df = pd.DataFrame(final[1:]) - if df.empty: - return pd.DataFrame() - new_headers = [] - - if statement == "balance-sheet": - for dates in headers[1:]: - read = datetime.strptime(dates, "%d/%m/%Y") - write = read.strftime("%Y-%m-%d") - new_headers.append(write) - new_headers[:0] = ["Breakdown"] - df.columns = new_headers - df.set_index("Breakdown", inplace=True) - elif statement in ("financials", "cash-flow"): - for dates in headers[2:]: - read = datetime.strptime(dates, "%d/%m/%Y") - write = read.strftime("%Y-%m-%d") - new_headers.append(write) - new_headers[:0] = ["Breakdown", "ttm"] - df.columns = new_headers - df.set_index("Breakdown", inplace=True) - - df.replace("", np.nan, inplace=True) - df.replace("-", np.nan, inplace=True) - df = df.dropna(how="all") - df = df.replace(",", "", regex=True) - df = df.replace("k", "", regex=True) - df = df.astype("float") - df.index = df.index.str.replace(",", "") - - not_skipped = ~df.reset_index()["Breakdown"].str.contains("EPS", case=False) - skipped = df.reset_index()["Breakdown"].str.contains("EPS", case=False) - skipped_df = df.reset_index()[skipped].set_index("Breakdown") - transformed = df.reset_index()[not_skipped].set_index("Breakdown") * 1000 - df = pd.concat([transformed, skipped_df]) - - if ratios: - types = df.copy().applymap(lambda x: isinstance(x, (float, int))) - types = types.all(axis=1) - - # For rows with complete data - valid = [] - i = 0 - for row in types: - if row: - valid.append(i) - i += 1 - df_fa_pc = df.iloc[valid].pct_change(axis="columns", periods=-1).fillna(0) - j = 0 - for i in valid: - df.iloc[i] = df_fa_pc.iloc[j] - j += 1 - - return df - - -@log_start_end(log=logger) -def get_earnings_history(symbol: str) -> pd.DataFrame: - """Get earning reports - - Parameters - ---------- - symbol: str - Symbol to get earnings for - - Returns - ------- - pd.DataFrame - Dataframe of historical earnings if present - """ - df = yf.Ticker(symbol).earnings_dates - df.reset_index(inplace=True) - df["Earnings Date"] = df["Earnings Date"].dt.strftime("%Y-%m-%d") - df.drop_duplicates(inplace=True) - df = df.fillna("-") - return df - - -@log_start_end(log=logger) -def get_currency(symbol) -> str: - """Quick helper to get currency for financial statements""" - try: - ticker_info = yf.Ticker(symbol).info - except Exception: - return "Not Specified" - - if "financialCurrency" in ticker_info: - return ticker_info["financialCurrency"] - return "Not Specified" diff --git a/openbb_terminal/stocks/fundamental_analysis/yahoo_finance_view.py b/openbb_terminal/stocks/fundamental_analysis/yahoo_finance_view.py deleted file mode 100644 index 18a32a36bef..00000000000 --- a/openbb_terminal/stocks/fundamental_analysis/yahoo_finance_view.py +++ /dev/null @@ -1,492 +0,0 @@ -""" Yahoo Finance View """ - -__docformat__ = "numpy" - -import logging -import os -from fractions import Fraction -from typing import Optional, Union - -import pandas as pd -import yfinance as yf - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.fundamental_analysis import yahoo_finance_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_info(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Yahoo Finance ticker info - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - summary = "" - df_info = yahoo_finance_model.get_info(symbol) - if "Long business summary" in df_info.index: - summary = df_info.loc["Long business summary"].values[0] - df_info = df_info.drop(index=["Long business summary"]) - - if not df_info.empty: - print_rich_table( - df_info, - headers=list(df_info.columns), - show_index=True, - title=f"{symbol.upper()} Info", - export=bool(export), - ) - else: - logger.error("Invalid data") - console.print("[red]Invalid data[/red]\n") - return - - if summary: - console.print("Business Summary:") - console.print(summary) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "info", - df_info, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_shareholders( - symbol: str, - holder: str = "institutional", - export: str = "", - sheet_name: Optional[str] = None, -): - """Yahoo Finance ticker shareholders - Parameters - ---------- - symbol : str - Fundamental analysis ticker symbol - holder: str - Shareholder table to get. Can be major/institutional/mutualfund - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - df = yahoo_finance_model.get_shareholders(symbol, holder) - if holder == "major": - df.columns = ["Value", "Description"] - if "Date Reported" in df.columns: - df["Date Reported"] = df["Date Reported"].apply( - lambda x: x.strftime("%Y-%m-%d") - ) - title = f"{holder.title()} Holders" - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"{symbol.upper()} {title}", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"{holder}_holders", - df, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_dividends( - symbol: str, - limit: int = 12, - plot: bool = True, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display historical dividends - - Parameters - ---------- - symbol: str - Stock ticker symbol - limit: int - Number to show - plot: bool - Plots historical data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.fa.divs_chart("AAPL") - """ - div_history = yahoo_finance_model.get_dividends(symbol) - if div_history.empty: - return None - - fig = OpenBBFigure(yaxis_title="Amount Paid ($)") - fig.set_title(f"Dividend History for {symbol}") - fig.add_scatter( - x=div_history.index, - y=div_history["Dividends"], - mode="markers+lines", - name="Dividends Payout", - marker_color=theme.down_color, - line_color=theme.get_colors()[0], - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "divs", - div_history, - sheet_name, - fig, - ) - - if not plot: - div_history.index = pd.to_datetime(div_history.index, format="%Y%m%d").strftime( - "%Y-%m-%d" - ) - return print_rich_table( - div_history, - headers=["Amount Paid ($)", "Change"], - title=f"{symbol.upper()} Historical Dividends", - show_index=True, - export=bool(export), - limit=limit, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_splits( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display splits and reverse splits events. [Source: Yahoo Finance] - - Parameters - ---------- - symbol: str - Stock ticker symbol - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_splits = yahoo_finance_model.get_splits(symbol) - if df_splits.empty: - return console.print("No splits or reverse splits events found.\n") - - # Get all stock data since IPO - df_data = yf.download(symbol, progress=False, threads=False) - if df_data.empty: - return console.print("No stock price data available.\n") - - fig = OpenBBFigure(yaxis_title="Price") - fig.set_title(f"{symbol} splits and reverse splits events") - - fig.add_scatter( - x=df_data.index, - y=df_data["Adj Close"], - mode="lines", - name="Price", - line_color="#FCED00", - ) - - for index, row in df_splits.iterrows(): - val = row.values[0] - frac = Fraction(val).limit_denominator(1000000) - if val > 1: - fig.add_annotation( - x=index, - y=df_data["Adj Close"].max(), - text=f"{frac.numerator}:{frac.denominator}", - xshift=20, - font=dict(color=theme.up_color), - ) - fig.add_vline(x=index, line_width=2, line_color=theme.up_color) - else: - fig.add_annotation( - x=index, - y=df_data["Adj Close"].max(), - text=f"{frac.numerator}:{frac.denominator}", - xshift=20, - font_color=theme.down_color, - ) - fig.add_vline(x=index, line_width=2, line_color=theme.down_color) - - df_splits.index = pd.to_datetime(df_splits.index, format="%Y%m%d").strftime( - "%Y-%m-%d" - ) - print_rich_table( - df_splits, - title=f"{symbol.upper()} splits and reverse splits", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "splits", - df_splits, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_mktcap( - symbol: str, - start_date: Optional[str] = None, - end_date: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display market cap over time. [Source: Yahoo Finance] - - Parameters - ---------- - symbol: str - Stock ticker symbol - start_date: Optional[str] - Initial date (e.g., 2021-10-01). If not provided, the earliest date available is used. - end_date: Optional[str] - End date (e.g., 2021-10-01). If not provided, the latest date available is used. - raw: bool - Whether to return the raw data or not - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_mktcap, currency = yahoo_finance_model.get_mktcap(symbol, start_date, end_date) - if df_mktcap.empty: - return console.print("No Market Cap data available.\n") - - fig = OpenBBFigure(yaxis_title=f"Market Cap in Billion ({currency})") - fig.set_title(f"{symbol} Market Cap") - fig.add_scatter( - x=df_mktcap.index, - y=df_mktcap.values / 1e9, - mode="lines", - name="Market Cap", - line_color=theme.up_color, - stackgroup="one", - ) - - if raw: - print_rich_table( - pd.DataFrame(df_mktcap).tail(10), - headers=["Market Cap"], - title=f"{symbol} Market Cap", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "mktcap", - df_mktcap, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_fundamentals( - symbol: str, - statement: str, - limit: int = 12, - ratios: bool = False, - plot: Optional[list] = None, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display tickers balance sheet, income statement or cash-flow - - Parameters - ---------- - symbol: str - Stock ticker symbol - statement: str - Possible values are: - - - cash-flow - - financials for Income - - balance-sheet - - limit: int - Number of periods to show - ratios: bool - Shows percentage change - plot: list - List of row labels to plot - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - fig = OpenBBFigure() - fundamentals = yahoo_finance_model.get_financials(symbol, statement, ratios) - - if fundamentals is None or fundamentals.empty: - # The empty data frame error handling done in model - return None - - if statement == "balance-sheet": - title_str = "Balance Sheet" - fundamentals.index = [ - stocks_helper.BALANCE_PLOT["YahooFinance"][i] - for i in [i.replace(" ", "_") for i in fundamentals.index.str.lower()] - ] - elif statement == "financials": - title_str = "Income Statement" - fundamentals.index = [ - stocks_helper.INCOME_PLOT["YahooFinance"][i] - for i in [i.replace(" ", "_") for i in fundamentals.index.str.lower()] - ] - elif statement == "cash-flow": - title_str = "Cash Flow Statement" - fundamentals.index = [ - stocks_helper.CASH_PLOT["YahooFinance"][i] - for i in [i.replace(" ", "_") for i in fundamentals.index.str.lower()] - ] - - if plot: - plot = [x.lower() for x in plot] - rows_plot = len(plot) - fundamentals_plot_data = fundamentals.transpose().fillna(-1) - fundamentals_plot_data.columns = fundamentals_plot_data.columns.str.lower() - if "ttm" in list(fundamentals_plot_data.index): - fundamentals_plot_data = fundamentals_plot_data.drop(["ttm"]) - fundamentals_plot_data = fundamentals_plot_data.sort_index() - - if rows_plot == 1: - fig.add_scatter( - x=fundamentals_plot_data.index, - y=fundamentals_plot_data[plot[0]], - name=plot[0].replace("_", " "), - ) - title = ( - f"{plot[0].replace('_', ' ').capitalize()} QoQ Growth of {symbol.upper()}" - if ratios - else f"{plot[0].replace('_', ' ').capitalize()} of {symbol.upper()}" - ) - fig.set_title(title) - else: - fig = OpenBBFigure.create_subplots(rows=rows_plot, cols=1) - for i in range(rows_plot): - fig.add_scatter( - x=fundamentals_plot_data.index, - y=fundamentals_plot_data[plot[i]], - name=plot[i].replace("_", " "), - row=i + 1, - col=1, - ) - title = f"{plot[i].replace('_', ' ')}" - fig.add_annotation(x=0.5, y=1, row=i + 1, col=1, text=title) - - fig.show(external=fig.is_image_export(export)) - - else: - # Snake case to english - fundamentals.index = fundamentals.index.to_series().apply( - lambda x: x.replace("_", " ").title() - ) - # Readable numbers - formatted_df = fundamentals.applymap(lambda_long_number_format).fillna("-") - print_rich_table( - formatted_df.applymap(lambda x: "-" if x == "nan" else x), - show_index=True, - index_name="Item", - title=f"{symbol} {title_str}", - export=bool(export), - limit=limit, - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - statement, - fundamentals, - sheet_name, - fig, - ) - - return None - - -@log_start_end(log=logger) -def display_earnings( - symbol: str, limit: int, export: str = "", sheet_name: Optional[str] = None -): - """ - - Parameters - ---------- - symbol: str - Stock ticker symbol - limit: int - Number of periods to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - - """ - earnings = yahoo_finance_model.get_earnings_history(symbol) - if earnings.empty: - return - print_rich_table( - earnings, - headers=earnings.columns, - title=f"Historical Earnings for {symbol}", - export=bool(export), - limit=limit, - ) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "earnings_yf", - earnings, - sheet_name, - ) diff --git a/openbb_terminal/stocks/government/__init__.py b/openbb_terminal/stocks/government/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/government/gov_controller.py b/openbb_terminal/stocks/government/gov_controller.py deleted file mode 100644 index 5170cec84bc..00000000000 --- a/openbb_terminal/stocks/government/gov_controller.py +++ /dev/null @@ -1,546 +0,0 @@ -""" Government Controller Module """ - -__docformat__ = "numpy" - -import argparse -import logging -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_positive, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks.government import quiverquant_view - -logger = logging.getLogger(__name__) - - -class GovController(StockBaseController): - """Gov Controller class""" - - CHOICES_COMMANDS = [ - "load", - "lasttrades", - "topbuys", - "topsells", - "lastcontracts", - "qtrcontracts", - "toplobbying", - "gtrades", - "contracts", - "histcont", - "lobbying", - ] - - gov_type_choices = ["congress", "senate", "house"] - analysis_choices = ["total", "upmom", "downmom"] - PATH = "/stocks/gov/" - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("stocks/gov/", 80) - mt.add_info("_explore") - mt.add_cmd("lasttrades") - mt.add_cmd("topbuys") - mt.add_cmd("topsells") - mt.add_cmd("lastcontracts") - mt.add_cmd("qtrcontracts") - mt.add_cmd("toplobbying") - mt.add_raw("\n") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_ticker", self.ticker or "") - mt.add_raw("\n") - mt.add_cmd("gtrades", self.ticker) - mt.add_cmd("contracts", self.ticker) - mt.add_cmd("histcont", self.ticker) - mt.add_cmd("lobbying", self.ticker) - console.print(text=mt.menu_text, menu="Stocks - Government") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - return ["stocks", f"load {self.ticker}", "gov"] - return [] - - @log_start_end(log=logger) - def call_lasttrades(self, other_args: List[str]): - """Process lasttrades command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lasttrades", - description="Last government trades. [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-g", - "--govtype", - dest="gov", - choices=self.gov_type_choices, - type=str, - default="congress", - ) - parser.add_argument( - "-p", - "--past_transactions_days", - action="store", - dest="past_transactions_days", - type=check_positive, - default=5, - help="Past transaction days", - ) - parser.add_argument( - "-r", - "--representative", - action="store", - dest="representative", - type=str, - default="", - help="Representative", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-g") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - quiverquant_view.display_last_government( - gov_type=ns_parser.gov, - limit=ns_parser.past_transactions_days, - representative=ns_parser.representative, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_topbuys(self, other_args: List[str]): - """Process topbuys command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="topbuys", - description="Top buys for government trading. [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-g", - "--govtype", - dest="gov", - choices=self.gov_type_choices, - type=str, - default="congress", - ) - parser.add_argument( - "-p", - "--past_transactions_months", - action="store", - dest="past_transactions_months", - type=check_positive, - default=6, - help="Past transaction months", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of top tickers to display", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-g") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - quiverquant_view.display_government_buys( - gov_type=ns_parser.gov, - past_transactions_months=ns_parser.past_transactions_months, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_topsells(self, other_args: List[str]): - """Process topsells command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="topsells", - description="Top sells for government trading. [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-g", - "--govtype", - dest="gov", - choices=self.gov_type_choices, - type=str, - default="congress", - ) - parser.add_argument( - "-p", - "--past_transactions_months", - action="store", - dest="past_transactions_months", - type=check_positive, - default=6, - help="Past transaction months", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of top tickers to display", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-g") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - quiverquant_view.display_government_sells( - gov_type=ns_parser.gov, - past_transactions_months=ns_parser.past_transactions_months, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_lastcontracts(self, other_args: List[str]): - """Process lastcontracts command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lastcontracts", - description="Last government contracts. [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-p", - "--past_transaction_days", - action="store", - dest="past_transaction_days", - type=check_positive, - default=2, - help="Past transaction days", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=20, - help="Limit of contracts to display", - ) - parser.add_argument( - "-s", - "--sum", - action="store_true", - dest="sum", - default=False, - help="Flag to show total amount of contracts.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - quiverquant_view.display_last_contracts( - past_transaction_days=ns_parser.past_transaction_days, - limit=ns_parser.limit, - sum_contracts=ns_parser.sum, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_qtrcontracts(self, other_args: List[str]): - """Process qtrcontracts command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="qtrcontracts", - description="Look at government contracts [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="Limit of tickers to get", - ) - parser.add_argument( - "-a", - "--analysis", - action="store", - dest="analysis", - choices=self.analysis_choices, - type=str, - default="total", - help="""Analysis to look at contracts. 'Total' shows summed contracts. - 'Upmom' shows highest sloped contacts while 'downmom' shows highest decreasing slopes.""", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - quiverquant_view.display_qtr_contracts( - analysis=ns_parser.analysis, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_toplobbying(self, other_args: List[str]): - """Process toplobbying command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="toplobbying", - description="Top lobbying. [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of stocks to display", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - quiverquant_view.display_top_lobbying( - limit=ns_parser.limit, raw=ns_parser.raw, export=ns_parser.export - ) - - @log_start_end(log=logger) - def call_gtrades(self, other_args: List[str]): - """Process gtrades command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="gtrades", - description="Government trading. [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-p", - "--past_transactions_months", - action="store", - dest="past_transactions_months", - type=check_positive, - default=6, - help="Past transaction months", - ) - parser.add_argument( - "-g", - "--govtype", - dest="gov", - choices=self.gov_type_choices, - type=str, - default="congress", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-g") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.ticker: - quiverquant_view.display_government_trading( - symbol=self.ticker, - gov_type=ns_parser.gov, - past_transactions_months=ns_parser.past_transactions_months, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded. Use `load ` first.\n") - - @log_start_end(log=logger) - def call_contracts(self, other_args: List[str]): - """Process contracts command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="contracts", - description="Contracts associated with ticker. [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-p", - "--past_transaction_days", - action="store", - dest="past_transaction_days", - type=check_positive, - default=10, - help="Past transaction days", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.ticker: - quiverquant_view.display_contracts( - symbol=self.ticker, - past_transaction_days=ns_parser.past_transaction_days, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded. Use `load ` first.\n") - - @log_start_end(log=logger) - def call_histcont(self, other_args: List[str]): - """Process histcont command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="histcont", - description="Quarterly-contracts historical [Source: www.quiverquant.com]", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if self.ticker: - quiverquant_view.display_hist_contracts( - symbol=self.ticker, raw=ns_parser.raw, export=ns_parser.export - ) - else: - console.print("No ticker loaded. Use `load ` first.\n") - - @log_start_end(log=logger) - def call_lobbying(self, other_args: List[str]): - """Process lobbying command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lobbying", - description="Lobbying details [Source: www.quiverquant.com]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of events to show", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.ticker: - quiverquant_view.display_lobbying( - symbol=self.ticker, - limit=ns_parser.limit, - ) - else: - console.print("No ticker loaded. Use `load ` first.\n") diff --git a/openbb_terminal/stocks/government/quiverquant_model.py b/openbb_terminal/stocks/government/quiverquant_model.py deleted file mode 100644 index 6eb84f2358b..00000000000 --- a/openbb_terminal/stocks/government/quiverquant_model.py +++ /dev/null @@ -1,566 +0,0 @@ -"""Quiverquant Model""" - -__docformat__ = "numpy" - -# Provided by Quiverquant guys to GST users -import logging -import textwrap -from datetime import datetime, timedelta - -import numpy as np -import pandas as pd -from sklearn.linear_model import LinearRegression - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -API_QUIVERQUANT_KEY = ( - "5cd2a65e96d0486efbe926a7cdbc1e8d8ab6c7b3" # pragma: allowlist secret -) - - -@log_start_end(log=logger) -def get_government_trading( - gov_type: str = "congress", symbol: str = "" -) -> pd.DataFrame: - """Returns the most recent transactions by members of government - - Parameters - ---------- - gov_type: str - Type of government data between: - 'congress', 'senate', 'house', 'contracts', 'quarter-contracts' and 'corporate-lobbying' - symbol : str - Ticker symbol to get congress trading data from - - Returns - ------- - pd.DataFrame - Most recent transactions by members of U.S. Congress - """ - if gov_type == "congress": - if symbol: - url = ( - f"https://api.quiverquant.com/beta/historical/congresstrading/{symbol}" - ) - else: - url = "https://api.quiverquant.com/beta/live/congresstrading" - - elif gov_type.lower() == "senate": - if symbol: - url = f"https://api.quiverquant.com/beta/historical/senatetrading/{symbol}" - else: - url = "https://api.quiverquant.com/beta/live/senatetrading" - - elif gov_type.lower() == "house": - if symbol: - url = f"https://api.quiverquant.com/beta/historical/housetrading/{symbol}" - else: - url = "https://api.quiverquant.com/beta/live/housetrading" - - elif gov_type.lower() == "contracts": - if symbol: - url = ( - f"https://api.quiverquant.com/beta/historical/govcontractsall/{symbol}" - ) - else: - url = "https://api.quiverquant.com/beta/live/govcontractsall" - - elif gov_type.lower() == "quarter-contracts": - if symbol: - url = f"https://api.quiverquant.com/beta/historical/govcontracts/{symbol}" - else: - url = "https://api.quiverquant.com/beta/live/govcontracts" - - elif gov_type.lower() == "corporate-lobbying": - if symbol: - url = f"https://api.quiverquant.com/beta/historical/lobbying/{symbol}" - else: - url = "https://api.quiverquant.com/beta/live/lobbying" - - else: - return pd.DataFrame() - headers = { - "accept": "application/json", - "X-CSRFToken": "TyTJwjuEC7VV7mOqZ622haRaaUr0x0Ng4nrwSRFKQs7vdoBcJlK9qjAS69ghzhFu", # pragma: allowlist secret - "Authorization": f"Token {API_QUIVERQUANT_KEY}", - } - response = request(url, headers=headers, timeout=10) # Default timeout causes error - if response.status_code == 200: - if gov_type in ["congress", "senate", "house"]: - return pd.DataFrame(response.json()).rename( - columns={"Date": "TransactionDate", "Senator": "Representative"} - ) - return pd.DataFrame(response.json()) - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_contracts( - symbol: str, - past_transaction_days: int = 10, -) -> pd.DataFrame: - """Get government contracts for ticker [Source: quiverquant.com] - - Parameters - ---------- - symbol: str - Ticker to get congress trading data from - past_transaction_days: int - Number of days to get transactions for - - Returns - ------- - pd.DataFrame - Most recent transactions by members of U.S. Congress - """ - df_contracts = get_government_trading("contracts", symbol) - - if df_contracts.empty: - console.print("No government contracts found\n") - return pd.DataFrame() - - df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]).dt.date - - df_contracts = df_contracts[ - df_contracts["Date"].isin(df_contracts["Date"].unique()[:past_transaction_days]) - ] - - df_contracts.drop_duplicates(inplace=True) - - return df_contracts - - -@log_start_end(log=logger) -def get_hist_contracts( - symbol: str, -) -> pd.DataFrame: - """Get historical quarterly government contracts [Source: quiverquant.com] - - Parameters - ---------- - symbol: str - Ticker symbol to get congress trading data from - - Returns - ------- - pd.DataFrame - Historical quarterly government contracts - """ - df_contracts = get_government_trading("quarter-contracts", symbol=symbol) - - if df_contracts.empty: - console.print("No quarterly government contracts found\n") - return pd.DataFrame() - - return df_contracts - - -@log_start_end(log=logger) -def get_last_government( - gov_type: str = "congress", limit: int = -1, representative: str = "" -) -> pd.DataFrame: - """Get last government trading [Source: quiverquant.com] - - Parameters - ---------- - gov_type: str - Type of government data between: congress, senate and house - limit: int - Number of days to look back - representative: str - Specific representative to look at - - Returns - ------- - pd.DataFrame - Last government trading - """ - df_gov = get_government_trading(gov_type) - - if df_gov.empty: - return pd.DataFrame() - - df_gov = df_gov[ - df_gov["TransactionDate"].isin(df_gov["TransactionDate"].unique()[:limit]) - ] - - if gov_type == "congress": - df_gov = df_gov[ - [ - "TransactionDate", - "Ticker", - "Representative", - "Transaction", - "Range", - "House", - "ReportDate", - ] - ].rename( - columns={ - "TransactionDate": "Transaction Date", - "ReportDate": "Report Date", - } - ) - else: - df_gov = df_gov[ - [ - "TransactionDate", - "Ticker", - "Representative", - "Transaction", - "Range", - ] - ].rename(columns={"TransactionDate": "Transaction Date"}) - - if representative: - df_gov = df_gov[df_gov["Representative"].str.split().str[0] == representative] - - return df_gov - - -@log_start_end(log=logger) -def get_government_buys( - gov_type: str = "congress", - past_transactions_months: int = 6, -) -> pd.DataFrame: - """Get top buy government trading [Source: quiverquant.com] - - Parameters - ---------- - gov_type: str - Type of government data between: congress, senate and house - past_transactions_months: int - Number of months to get trading for - - Returns - ------- - pd.DataFrame - DataFrame of top government buy trading - """ - df_gov = get_government_trading(gov_type) - if df_gov.empty: - return pd.DataFrame() - - df_gov = df_gov.sort_values("TransactionDate", ascending=False) - start_date = datetime.now() - timedelta(days=past_transactions_months * 30) - - df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"]) - - df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna(axis=1) - - # Catch bug where error shown for purchase of >5,000,000 - df_gov["Range"] = df_gov["Range"].apply( - lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x - ) - df_gov["min"] = df_gov["Range"].apply( - lambda x: x.split("-")[0].strip("$").replace(",", "").strip() - ) - df_gov["max"] = df_gov["Range"].apply( - lambda x: ( - x.split("-")[1].replace(",", "").strip().strip("$") - if "-" in x - else x.strip("$").replace(",", "") - ) - ) - df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( - lambda x: ( - float(x["min"]) if x["Transaction"] == "Purchase" else -float(x["max"]) - ), - axis=1, - ) - df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( - lambda x: ( - float(x["max"]) if x["Transaction"] == "Purchase" else -float(x["min"]) - ), - axis=1, - ) - df_gov = df_gov[df_gov.Transaction == "Purchase"] - df_gov = df_gov.sort_values(by="Amount", ascending=False).reset_index(drop=True) - - return df_gov - - -@log_start_end(log=logger) -def get_government_sells( - gov_type: str = "congress", - past_transactions_months: int = 6, -) -> pd.DataFrame: - """Get top sell government trading [Source: quiverquant.com] - - Parameters - ---------- - gov_type: str - Type of government data between: congress, senate and house - past_transactions_months: int - Number of months to get trading for - - Returns - ------- - pd.DataFrame - DataFrame of top government sell trading - """ - df_gov = get_government_trading(gov_type) - - if df_gov.empty: - return pd.DataFrame() - - df_gov = df_gov.sort_values("TransactionDate", ascending=False) - - start_date = datetime.now() - timedelta(days=past_transactions_months * 30) - - df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"]) - - df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna() - df_gov["Range"] = df_gov["Range"].apply( - lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x - ) - df_gov["min"] = df_gov["Range"].apply( - lambda x: x.split("-")[0] - .strip("$") - .replace(",", "") - .strip() - .replace(">$", "") - .strip() - ) - df_gov["max"] = df_gov["Range"].apply( - lambda x: ( - x.split("-")[1] - .replace(",", "") - .strip() - .strip("$") - .replace(">$", "") - .strip() - if "-" in x - else x.strip("$").replace(",", "").replace(">$", "").strip() - ) - ) - - df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( - lambda x: ( - float(x["min"]) if x["Transaction"] == "Purchase" else -float(x["max"]) - ), - axis=1, - ) - df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( - lambda x: ( - float(x["max"]) if x["Transaction"] == "Purchase" else -float(x["min"]) - ), - axis=1, - ) - - df_gov = df_gov.sort_values("TransactionDate", ascending=True) - df_gov = ( - df_gov[df_gov.Transaction == "Sale"] - .sort_values(by="Amount", key=lambda x: abs(x), ascending=False) - .reset_index(drop=True) - ) - return df_gov - - -@log_start_end(log=logger) -def get_top_lobbying() -> pd.DataFrame: - """Corporate lobbying details - - Returns - ------- - pd.DataFrame - DataFrame of top corporate lobbying - - """ - df_lobbying = get_government_trading("corporate-lobbying") - - if df_lobbying.empty: - console.print("No corporate lobbying found\n") - return pd.DataFrame() - - return df_lobbying - - -@log_start_end(log=logger) -def get_last_contracts( - past_transaction_days: int = 2, -) -> pd.DataFrame: - """Get last government contracts [Source: quiverquant.com] - - Parameters - ---------- - past_transaction_days: int - Number of days to look back - - Returns - ------- - pd.DataFrame - DataFrame of government contracts - """ - df_contracts = get_government_trading("contracts") - - if df_contracts.empty: - console.print("No government contracts found\n") - return pd.DataFrame() - - df_contracts.sort_values("Date", ascending=False) - - df_contracts["Date"] = pd.to_datetime(df_contracts["Date"]) - - df_contracts.drop_duplicates(inplace=True) - - df_contracts = df_contracts[ - df_contracts["Date"].isin(df_contracts["Date"].unique()[:past_transaction_days]) - ] - - df_contracts = df_contracts[["Date", "Ticker", "Amount", "Description", "Agency"]] - df_contracts["Description"] = df_contracts["Description"].apply( - lambda x: "\n".join(textwrap.wrap(x, 50)) if x is not None else None - ) - - return df_contracts - - -def get_cleaned_government_trading( - symbol: str, - gov_type: str = "congress", - past_transactions_months: int = 6, -) -> pd.DataFrame: - """Government trading for specific ticker [Source: quiverquant.com] - - Parameters - ---------- - symbol: str - Ticker symbol to get congress trading data from - gov_type: str - Type of government data between: congress, senate and house - past_transactions_months: int - Number of months to get transactions for - - Returns - ------- - pd.DataFrame - DataFrame of tickers government trading - """ - df_gov = get_government_trading(gov_type, symbol) - - if df_gov.empty: - return pd.DataFrame() - - df_gov = df_gov.sort_values("TransactionDate", ascending=False) - - start_date = datetime.now() - timedelta(days=past_transactions_months * 30) - - df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"]) - - df_gov = df_gov[df_gov["TransactionDate"] > start_date] - - if df_gov.empty: - console.print(f"No recent {gov_type} trading data found\n") - return pd.DataFrame() - - df_gov["min"] = df_gov["Range"].apply( - lambda x: x.split("-")[0].strip("$").replace(",", "").strip() - ) - df_gov["max"] = df_gov["Range"].apply( - lambda x: ( - x.split("-")[1].replace(",", "").strip().strip("$") - if "-" in x - else x.strip("$").replace(",", "").split("\n")[0] - ) - ) - - df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply( - lambda x: ( - int(float(x["min"])) - if x["Transaction"] == "Purchase" - else -int(float(x["max"])) - ), - axis=1, - ) - df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply( - lambda x: ( - int(float(x["max"])) - if x["Transaction"] == "Purchase" - else -1 * int(float(x["min"])) - ), - axis=1, - ) - - df_gov = df_gov.sort_values("TransactionDate", ascending=True) - - return df_gov - - -@log_start_end(log=logger) -def get_qtr_contracts(analysis: str = "total", limit: int = 5) -> pd.DataFrame: - """Analyzes quarterly contracts by ticker - - Parameters - ---------- - analysis : str - How to analyze. Either gives total amount or sorts by high/low momentum. - limit : int, optional - Number to return, by default 5 - - Returns - ------- - pd.DataFrame - Dataframe with tickers and total amount if total selected. - """ - df_contracts = get_government_trading("quarter-contracts") - df_contracts["Amount"] = pd.to_numeric(df_contracts["Amount"]) - if df_contracts.empty: - console.print("No quarterly government contracts found\n") - return pd.DataFrame() - - if analysis == "total": - df_groups = ( - df_contracts.groupby("Ticker")["Amount"].sum().sort_values(ascending=False) - ) - return pd.DataFrame(df_groups[:limit]) - - if analysis in {"upmom", "downmom"}: - coef = [] - df_groups = df_contracts.groupby("Ticker") - for tick, data in df_groups: - regr = LinearRegression() - - amounts = data.sort_values(by=["Year", "Qtr"])["Amount"].values - - # Train the model using the training sets - regr.fit(np.arange(0, len(amounts)).reshape(-1, 1), amounts) - - coef.append({"Ticker": tick, "Coef": regr.coef_[0]}) - - return pd.DataFrame(coef).sort_values( - by=["Coef"], ascending=analysis == "downmom" - )["Ticker"][:limit] - - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_lobbying(symbol: str, limit: int = 10) -> pd.DataFrame: - """Corporate lobbying details - - Parameters - ---------- - symbol: str - Ticker symbol to get corporate lobbying data from - limit: int - Number of events to show - - Returns - ------- - pd.DataFrame - Dataframe with corporate lobbying data - """ - df_lobbying = get_government_trading("corporate-lobbying", symbol=symbol) - - if df_lobbying.empty: - console.print("No corporate lobbying found\n") - return pd.DataFrame() - - df_lobbying.sort_values(by=["Date"], ascending=False) - - return df_lobbying.head(limit) diff --git a/openbb_terminal/stocks/government/quiverquant_view.py b/openbb_terminal/stocks/government/quiverquant_view.py deleted file mode 100644 index e39664284ed..00000000000 --- a/openbb_terminal/stocks/government/quiverquant_view.py +++ /dev/null @@ -1,779 +0,0 @@ -"""Quiverquant View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.government import quiverquant_model - -# pylint: disable=C0302,inconsistent-return-statements - - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_last_government( - gov_type: str = "congress", - limit: int = 5, - representative: str = "", - export: str = "", - sheet_name: Optional[str] = None, -): - """Display last government trading [Source: quiverquant.com] - - Parameters - ---------- - gov_type: str - Type of government data between: congress, senate and house - limit: int - Number of days to look back - representative: str - Specific representative to look at - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - """ - df_gov = quiverquant_model.get_last_government(gov_type, limit, representative) - - if df_gov.empty: - if representative: - console.print( - f"No representative {representative} found in the past {limit}" - f" days. The following are available: " - f"{', '.join(df_gov['Representative'].str.split().str[0].unique())}" - ) - else: - console.print(f"No {gov_type} trading data found\n") - return - - console.print(f"\nLast transactions for {gov_type.upper()}\n") - - print_rich_table( - df_gov, - headers=list(df_gov.columns), - show_index=False, - title="Representative Trading", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "lasttrades", - df_gov, - sheet_name, - ) - - -@log_start_end(log=logger) -def display_government_buys( - gov_type: str = "congress", - past_transactions_months: int = 6, - limit: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Top buy government trading [Source: quiverquant.com] - - Parameters - ---------- - gov_type: str - Type of government data between: congress, senate and house - past_transactions_months: int - Number of months to get trading for - limit: int - Number of tickers to show - raw: bool - Display raw data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - """ - df_gov = quiverquant_model.get_government_buys(gov_type, past_transactions_months) - - if df_gov.empty: - console.print(f"No {gov_type} trading data found\n") - return - - if raw: - df = pd.DataFrame( - df_gov.groupby("Ticker")["upper"] - .sum() - .div(1000) - .sort_values(ascending=False) - .head(n=limit) - ) - print_rich_table( - df, - headers=["Amount ($1k)"], - show_index=True, - title="Top Government Buys", - export=bool(export), - ) - - df_gov_sorted = ( - df_gov.groupby("Ticker")["upper"] - .sum() - .div(1000) - .sort_values(ascending=False) - .head(n=limit) - ) - - fig = OpenBBFigure(xaxis_title="Ticker", yaxis_title="Amount [1k $]") - fig.set_title( - f"{gov_type.upper()}'s top {limit} purchased stocks (upper) in last {past_transactions_months} months" - ) - - fig.add_bar( - x=df_gov_sorted.index, y=df_gov_sorted.values, marker_color=theme.get_colors() - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "topbuys", - df_gov, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_government_sells( - gov_type: str = "congress", - past_transactions_months: int = 6, - limit: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Top sell government trading [Source: quiverquant.com] - - Parameters - ---------- - gov_type: str - Type of government data between: congress, senate and house - past_transactions_months: int - Number of months to get trading for - limit: int - Number of tickers to show - raw: bool - Display raw data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_gov = quiverquant_model.get_government_sells(gov_type, past_transactions_months) - - if df_gov.empty: - console.print(f"No {gov_type} trading data found\n") - return - - if raw: - df = pd.DataFrame( - df_gov.groupby("Ticker")["upper"] - .sum() - .div(1000) - .sort_values(ascending=True) - .abs() - .head(n=limit) - ) - print_rich_table( - df, - headers=["Amount ($1k)"], - show_index=True, - title="Top Government Trades", - export=bool(export), - ) - - df_gov_sorted = ( - df_gov.groupby("Ticker")["upper"] - .sum() - .div(1000) - .sort_values() - .abs() - .head(n=limit) - ) - - fig = OpenBBFigure(xaxis_title="Ticker", yaxis_title="Amount ($1k)") - fig.set_title( - f"{limit} most sold stocks over last {past_transactions_months} months" - f" (upper bound) for {gov_type}" - ) - - fig.add_bar( - x=df_gov_sorted.index, y=df_gov_sorted.values, marker_color=theme.get_colors() - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "topsells", - df_gov, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_last_contracts( - past_transaction_days: int = 2, - limit: int = 20, - sum_contracts: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Last government contracts [Source: quiverquant.com] - - Parameters - ---------- - past_transaction_days: int - Number of days to look back - limit: int - Number of contracts to show - sum_contracts: bool - Flag to show total amount of contracts given out. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = quiverquant_model.get_last_contracts(past_transaction_days) - - if df.empty: - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title="Last Government Contracts", - export=bool(export), - limit=limit, - ) - - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d").dt.date - df = df.groupby("Date").sum(True).div(1000) - - fig = OpenBBFigure(yaxis_title="Amount ($1k)", xaxis_title="Date") - fig.set_title("Total amount of government contracts given") - - fig.add_bar(x=df.index, y=df["Amount"].values, marker_color=theme.get_colors()) - fig.update_layout(xaxis=dict(nticks=min(len(df.index) + 1, 10))) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "lastcontracts", - df, - sheet_name, - fig, - ) - - if not sum_contracts: - return - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def plot_government( - government: pd.DataFrame, - symbol: str, - gov_type: str, - external_axes: bool = False, -) -> Optional[OpenBBFigure]: - """Helper for plotting government trading - - Parameters - ---------- - government: pd.DataFrame - Data to plot - symbol: str - Ticker symbol to plot government trading - gov_type: str - Type of government data between: congress, senate and house - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - fig = OpenBBFigure(yaxis_title="Amount ($1k)", xaxis_title="Date") - fig.set_title(f"{gov_type.capitalize()} trading on {symbol}") - - fig.add_scatter( - name="lower", - x=government["TransactionDate"].unique(), - y=government.groupby("TransactionDate")["lower"].sum().values / 1000, - ) - fig.add_scatter( - name="upper", - x=government["TransactionDate"].unique(), - y=government.groupby("TransactionDate")["upper"].sum().values / 1000, - ) - - color = theme.get_colors()[0] - fig.update_traces(mode="lines", line_color=color, fillcolor=color, fill="tonexty") - fig.update_layout(showlegend=False) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_government_trading( - symbol: str, - gov_type: str = "congress", - past_transactions_months: int = 6, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Government trading for specific ticker [Source: quiverquant.com] - - Parameters - ---------- - symbol: str - Ticker symbol to get congress trading data from - gov_type: str - Type of government data between: congress, senate and house - past_transactions_months: int - Number of months to get transactions for - raw: bool - Show raw output of trades - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_gov = quiverquant_model.get_cleaned_government_trading( - symbol=symbol, - gov_type=gov_type, - past_transactions_months=past_transactions_months, - ) - - if df_gov is None or isinstance(df_gov, pd.DataFrame) and df_gov.empty: - return console.print(f"No {gov_type} trading data found\n") - - fig = plot_government(df_gov, symbol, gov_type, True) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "gtrades", - df_gov, - sheet_name, - fig, - ) - - if raw: - return print_rich_table( - df_gov, - headers=list(df_gov.columns), - show_index=False, - title=f"Government Trading for {symbol.upper()}", - export=bool(export), - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_contracts( - symbol: str, - past_transaction_days: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Show government contracts for ticker [Source: quiverquant.com] - - Parameters - ---------- - symbol: str - Ticker to get congress trading data from - past_transaction_days: int - Number of days to get transactions for - raw: bool - Flag to display raw data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_contracts = quiverquant_model.get_contracts(symbol, past_transaction_days) - - if df_contracts.empty: - return - if raw: - print_rich_table( - df_contracts, - headers=list(df_contracts.columns), - show_index=False, - title=f"Government Contracts for {symbol.upper()}", - export=bool(export), - ) - - if df_contracts.Amount.abs().sum() == 0: - return console.print("Contracts found, but they are all equal to $0.00.\n") - - fig = OpenBBFigure(yaxis_title="Amount ($1k)", xaxis_title="Date") - fig.set_title(f"Sum of latest government contracts to {symbol}") - - df_contracts_grouped = df_contracts.groupby("Date").sum(numeric_only=True) - - fig.add_bar( - name="Amount", - x=sorted(df_contracts["Date"].unique()), - y=df_contracts_grouped["Amount"] / 1000, - ) - fig.update_layout(xaxis=dict(type="category")) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "contracts", - df_contracts, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_qtr_contracts( - analysis: str = "total", - limit: int = 5, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Quarterly contracts [Source: quiverquant.com] - - Parameters - ---------- - analysis: str - Analysis to perform. Either 'total', 'upmom' 'downmom' - limit: int - Number to show - raw: bool - Flag to display raw data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - symbols = quiverquant_model.get_qtr_contracts(analysis, limit) - - if symbols.empty: - return None - - if analysis in ("upmom", "downmom"): - if raw: - return print_rich_table( - pd.DataFrame(symbols.values), - headers=["symbols"], - show_index=True, - title="Quarterly Contracts", - ) - - titles = { - "upmom": "Highest increasing quarterly Government Contracts", - "downmom": "Highest decreasing quarterly Government Contracts", - } - - fig = OpenBBFigure(xaxis_title="Quarter", yaxis_title="Amount ($1M)") - - fig.set_title(title=titles[analysis]) - - max_amount = 0 - quarter_ticks = [] - df_contracts = quiverquant_model.get_government_trading("quarter-contracts") - df_contracts["Amount"] = pd.to_numeric(df_contracts["Amount"]) - for symbol in symbols: - amounts = ( - df_contracts[df_contracts["Ticker"] == symbol] - .sort_values(by=["Year", "Qtr"])["Amount"] - .values - ) - - qtr = ( - df_contracts[df_contracts["Ticker"] == symbol] - .sort_values(by=["Year", "Qtr"])["Qtr"] - .values - ) - year = ( - df_contracts[df_contracts["Ticker"] == symbol] - .sort_values(by=["Year", "Qtr"])["Year"] - .values - ) - - fig.add_scatter( - x=np.arange(0, len(amounts)), - y=amounts / 1_000_000, - mode="lines+markers", - name=symbol, - marker=dict(size=16, line=dict(width=0), symbol="star"), - ) - - if len(amounts) > max_amount: - max_amount = len(amounts) - quarter_ticks = [ - f"{quarter[0]} - Q{quarter[1]} " for quarter in zip(year, qtr) - ] - fig.update_layout( - xaxis=dict( - tickmode="array", - tickvals=np.arange(0, len(amounts)), - ticktext=quarter_ticks, - ) - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "qtrcontracts", - symbols, - sheet_name, - fig, - ) - return fig.show(external=external_axes) - - if analysis == "total": - print_rich_table( - symbols, - headers=["Total"], - title="Quarterly Contracts", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "qtrcontracts", - symbols, - sheet_name, - ) - return None - - -@log_start_end(log=logger) -def display_hist_contracts( - symbol: str, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Show historical quarterly government contracts [Source: quiverquant.com] - - Parameters - ---------- - symbol: str - Ticker symbol to get congress trading data from - raw: bool - Flag to display raw data - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - df_contracts = quiverquant_model.get_hist_contracts(symbol) - - if df_contracts.empty: - return None - - amounts = df_contracts.sort_values(by=["Year", "Qtr"])["Amount"].astype(float) - - date_dict = { - 1: "03-31", - 2: "06-30", - 3: "09-30", - 4: "12-31", - } - df_contracts["dates"] = ( - df_contracts["Year"].astype(str) + "-" + df_contracts["Qtr"].map(date_dict) - ) - df_contracts["dates"] = pd.to_datetime(df_contracts["dates"]).dt.strftime("%Y-%m") - df_contracts.sort_values(by="dates", ascending=True, inplace=True) - - fig = OpenBBFigure( - xaxis=dict( - tickmode="array", - tickvals=np.arange(0, len(amounts)), - ticktext=df_contracts["dates"], - ), - yaxis_title="Amount ($1k)", - ) - - fig.set_title(f"Historical Quarterly Government Contracts for {symbol.upper()}") - - fig.add_scatter( - x=np.arange(0, len(amounts)), - y=amounts / 1000, - mode="lines+markers", - name=symbol, - marker=dict( - size=15, - line=dict(width=2, color=theme.get_colors()[0]), - color=theme.down_color, - ), - line=dict(color=theme.get_colors()[0]), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "histcont", - df_contracts.drop(columns=["dates"]), - sheet_name=sheet_name, - figure=fig, - ) - - if raw: - df = df_contracts.drop(columns=["dates"]).astype(str) - return print_rich_table( - df, - headers=list(df.columns), - title="Historical Quarterly Government Contracts", - export=bool(export), - columns_keep_types=["Year"], - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_top_lobbying( - limit: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Top lobbying tickers based on total spent - - Parameters - ---------- - limit: int - Number of tickers to show - raw: bool - Show raw data - export: - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - - """ - df_lobbying = quiverquant_model.get_top_lobbying() - - if df_lobbying.empty: - return - - df_lobbying["Amount"] = df_lobbying.Amount.astype(float).fillna(0) / 100_000 - - lobbying_by_ticker = pd.DataFrame( - df_lobbying.groupby("Ticker")["Amount"].agg("sum") - ).sort_values(by="Amount", ascending=False) - - df = lobbying_by_ticker.head(limit) - - fig = OpenBBFigure(xaxis_title="Ticker", yaxis_title="Total Amount ($100k)") - fig.set_title(f"Corporate Lobbying Spent since {df_lobbying['Date'].min()}") - - fig.add_bar( - x=df.index, - y=df.Amount, - name="Amount ($100k)", - marker_color=theme.get_colors(), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "lobbying", - df_lobbying, - sheet_name, - fig, - ) - - if raw: - return print_rich_table( - lobbying_by_ticker, - headers=["Amount ($100k)"], - show_index=True, - title="Top Lobbying Tickers", - export=bool(export), - limit=limit, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_lobbying(symbol: str, limit: int = 10): - """Corporate lobbying details - - Parameters - ---------- - symbol: str - Ticker symbol to get corporate lobbying data from - limit: int - Number of events to show - """ - df_lobbying = quiverquant_model.get_lobbying(symbol, limit) - - if df_lobbying.empty: - return - - for _, row in df_lobbying.iterrows(): - amount = ( - "$" + str(int(float(row["Amount"]))) if row["Amount"] is not None else "N/A" - ) - console.print(f"{row['Date']}: {row['Client']} {amount}") - if (row["Amount"] is not None) and (row["Specific_Issue"] is not None): - console.print( - "\t" + row["Specific_Issue"].replace("\n", " ").replace("\r", "") - ) - console.print("") - console.print("") diff --git a/openbb_terminal/stocks/insider/__init__.py b/openbb_terminal/stocks/insider/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/insider/businessinsider_model.py b/openbb_terminal/stocks/insider/businessinsider_model.py deleted file mode 100644 index 6e7c1a976a6..00000000000 --- a/openbb_terminal/stocks/insider/businessinsider_model.py +++ /dev/null @@ -1,73 +0,0 @@ -""" Business Insider Model """ - -__docformat__ = "numpy" - -import logging - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_insider_activity(symbol: str) -> pd.DataFrame: - """Get insider activity. [Source: Business Insider] - - Parameters - ---------- - symbol : str - Ticker symbol to get insider activity data from - - Returns - ------- - df_insider : pd.DataFrame - Insider activity data - """ - url_market_business_insider = ( - f"https://markets.businessinsider.com/stocks/{symbol.lower()}-stock" - ) - text_soup_market_business_insider = BeautifulSoup( - request( - url_market_business_insider, headers={"User-Agent": get_user_agent()} - ).text, - "lxml", - ) - - d_insider = dict() - l_insider_vals = list() - for idx, insider_val in enumerate( - text_soup_market_business_insider.findAll( - "td", {"class": "table__td text-center"} - ) - ): - l_insider_vals.append(insider_val.text.strip()) - - # Add value to dictionary - if (idx + 1) % 6 == 0: - # Check if we are still parsing insider trading activity - if "/" not in l_insider_vals[0]: - break - d_insider[(idx + 1) // 6] = l_insider_vals - l_insider_vals = list() - - df_insider = pd.DataFrame.from_dict( - d_insider, - orient="index", - columns=["Date", "Shares Traded", "Shares Held", "Price", "Type", "Option"], - ) - - df_insider["Date"] = pd.to_datetime(df_insider["Date"]) - - l_names = list() - for s_name in text_soup_market_business_insider.findAll( - "a", {"onclick": "silentTrackPI()"} - ): - l_names.append(s_name.text.strip()) - df_insider["Insider"] = l_names - df_insider = df_insider.set_index("Date") - df_insider = df_insider.sort_index(ascending=True) - return df_insider diff --git a/openbb_terminal/stocks/insider/businessinsider_view.py b/openbb_terminal/stocks/insider/businessinsider_view.py deleted file mode 100644 index f2f11b50e54..00000000000 --- a/openbb_terminal/stocks/insider/businessinsider_view.py +++ /dev/null @@ -1,173 +0,0 @@ -""" Business Insider View """ - -__docformat__ = "numpy" - -import logging -import math -import os -from datetime import datetime, timedelta -from typing import Optional - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - get_next_stock_market_days, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.insider import businessinsider_model - -logger = logging.getLogger(__name__) - - -# pylint: disable=R0912,too-many-arguments - - -@log_start_end(log=logger) -def insider_activity( - data: pd.DataFrame, - symbol: str, - start_date: Optional[str] = None, - interval: str = "1440min", - limit: int = 10, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Display insider activity. [Source: Business Insider] - - Parameters - ---------- - data: pd.DataFrame - Stock dataframe - symbol: str - Due diligence ticker symbol - start_date: Optional[str] - Initial date (e.g., 2021-10-01). Defaults to 3 years back - interval: str - Stock data interval - limit: int - Number of latest days of inside activity - raw: bool - Print to console - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export dataframe data to csv,json,xlsx file - external_axes: bool, optional - Whether to return the figure object or not, by default False - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=1100)).strftime("%Y-%m-%d") - - df_ins = businessinsider_model.get_insider_activity(symbol) - - if df_ins.empty: - logger.warning("The insider activity on the ticker does not exist") - return console.print( - "[red]The insider activity on the ticker does not exist.\n[/red]" - ) - - df_insider = df_ins[start_date:].copy() if start_date else df_ins.copy() # type: ignore - - close_col = "Adj Close" if interval == "1440min" else "Close" - - fig = OpenBBFigure(yaxis_title="Share Price") - fig.set_title(f"{symbol.upper()}'s Insider Trading Activity & Share Price") - - fig.add_scatter(x=data.index, y=data[close_col].values, name=symbol) - - df_insider_plot = df_insider.copy() - df_insider_plot["Trade"] = df_insider_plot.apply( - lambda row: (1, -1)[row.Type == "Sell"] - * float(row["Shares Traded"].replace(",", "")), - axis=1, - ) - - min_price, max_price = data[close_col].min(), data[close_col].max() - price_range = max_price - min_price - - ins_buy = ( - df_insider_plot[df_insider_plot["Type"] == "Buy"] - .groupby(by=["Date"]) - .sum(numeric_only=True) - ) - ins_sell = ( - df_insider_plot[df_insider_plot["Type"] == "Sell"] - .groupby(by=["Date"]) - .sum(numeric_only=True) - ) - - maxshares = ins_buy["Trade"].max() - minshares = ins_sell["Trade"].min() - - if math.isnan(maxshares): - shares_range = minshares - elif math.isnan(minshares): - shares_range = maxshares - else: - shares_range = maxshares - minshares - - n_proportion = price_range / shares_range - - for ind in ins_sell.index: - ind_dt = ind if ind in data.index else get_next_stock_market_days(ind, 1)[0] - - n_stock_price = data[close_col][ind_dt] - ins_loc = ins_sell.index.get_indexer([ind_dt], method="nearest") - - ymin = n_stock_price + n_proportion * float(ins_sell["Trade"][ins_loc]) - fig.add_scatter( - x=[ind_dt, ind_dt], - y=[ymin, n_stock_price], - mode="lines", - line=dict(color=theme.down_color, width=5), - name="Insider Selling", - showlegend=ind == ins_sell.index[0], - ) - - for ind in ins_buy.index: - ind_dt = ind if ind in data.index else get_next_stock_market_days(ind, 1)[0] - - n_stock_price = data[close_col][ind_dt] - ins_loc = ins_buy.index.get_indexer([ind_dt], method="nearest") - - ymax = n_stock_price + n_proportion * float(ins_buy["Trade"][ins_loc]) - fig.add_scatter( - x=[ind_dt, ind_dt], - y=[n_stock_price, ymax], - mode="lines", - line=dict(color=theme.up_color, width=5), - name="Insider Buying", - showlegend=ind == ins_buy.index[0], - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "act", - df_insider, - sheet_name, - fig, - ) - - if raw: - df_insider.index = pd.to_datetime(df_insider.index) - - return print_rich_table( - df_insider.sort_index(ascending=False).applymap( - lambda x: x.replace(".00", "").replace(",", "") - ), - headers=list(df_insider.columns), - show_index=True, - title="Insider Activity", - export=bool(export), - limit=limit, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/insider/finviz_model.py b/openbb_terminal/stocks/insider/finviz_model.py deleted file mode 100644 index 02e691b4262..00000000000 --- a/openbb_terminal/stocks/insider/finviz_model.py +++ /dev/null @@ -1,50 +0,0 @@ -""" Finviz Model """ - -__docformat__ = "numpy" - -import logging - -import finviz -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_last_insider_activity(symbol: str) -> pd.DataFrame: - """Get last insider activity for a given stock ticker. [Source: Finviz] - - Parameters - ---------- - symbol : str - Stock ticker symbol - - Returns - ------- - pd.DataFrame - Latest insider trading activity - """ - activity_dict = finviz.get_insider(symbol) - df = pd.DataFrame.from_dict(activity_dict) - - if df.empty: - console.print(f"[red]No insider information found for {symbol}.\n[/red]") - else: - df.set_index("Date", inplace=True) - df = df[ - [ - "Relationship", - "Transaction", - "#Shares", - "Cost", - "Value ($)", - "#Shares Total", - "Insider Trading", - "SEC Form 4", - ] - ] - - return df diff --git a/openbb_terminal/stocks/insider/finviz_view.py b/openbb_terminal/stocks/insider/finviz_view.py deleted file mode 100644 index 20a93498254..00000000000 --- a/openbb_terminal/stocks/insider/finviz_view.py +++ /dev/null @@ -1,51 +0,0 @@ -""" Finviz View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.insider import finviz_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def last_insider_activity( - symbol: str, limit: int = 10, export: str = "", sheet_name: Optional[str] = None -): - """Display insider activity for a given stock ticker. [Source: Finviz] - - Parameters - ---------- - symbol : str - Stock ticker symbol - limit : int - Number of latest insider activity to display - export : str - Export dataframe data to csv,json,xlsx file - """ - df = finviz_model.get_last_insider_activity(symbol) - - if df.empty: - return - - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - title="Insider Activity", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "lins", - df, - sheet_name, - ) diff --git a/openbb_terminal/stocks/insider/insider_controller.py b/openbb_terminal/stocks/insider/insider_controller.py deleted file mode 100644 index f5c89ae742f..00000000000 --- a/openbb_terminal/stocks/insider/insider_controller.py +++ /dev/null @@ -1,957 +0,0 @@ -"""Insider Controller Module""" - -__docformat__ = "numpy" - -import argparse -import configparser -import logging -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import EXPORT_ONLY_RAW_DATA_ALLOWED, check_positive -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks.insider import ( - businessinsider_view, - finviz_view, - openinsider_model, - openinsider_view, -) - -logger = logging.getLogger(__name__) - -# pylint: disable=,inconsistent-return-statements - -# TODO: HELP WANTED! This menu required some refactoring. Things that can be addressed: -# - better preset management (MVC style) -# - unification of model return types (now some return dataframes other records list) - - -class InsiderController(StockBaseController): - """Screener Controller class""" - - CHOICES_COMMANDS = [ - "load", - "view", - "set", - "filter", - "lcb", - "lpsb", - "lit", - "lip", - "blip", - "blop", - "blcp", - "lis", - "blis", - "blos", - "blcs", - "topt", - "toppw", - "toppm", - "tipt", - "tippw", - "tippm", - "tist", - "tispw", - "tispm", - "act", - "lins", - "stats", - ] - - preset_choices = openinsider_model.get_preset_choices() - - PATH = "/stocks/ins/" - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - start: str, - interval: str, - stock: pd.DataFrame, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - self.start = start - self.interval = interval - self.stock = stock - self.preset = "whales" - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("stocks/ins/", 80) - mt.add_cmd("view") - mt.add_cmd("set") - mt.add_raw("\n") - mt.add_param("_preset", self.preset) - mt.add_raw("\n") - mt.add_cmd("filter") - mt.add_raw("\n") - mt.add_info("_last_insiders") - mt.add_cmd("lcb") - mt.add_cmd("lpsb") - mt.add_cmd("lit") - mt.add_cmd("lip") - mt.add_cmd("blip") - mt.add_cmd("blop") - mt.add_cmd("blcp") - mt.add_cmd("lis") - mt.add_cmd("blis") - mt.add_cmd("blos") - mt.add_cmd("blcs") - mt.add_raw("\n") - mt.add_info("_top_insiders") - mt.add_cmd("topt") - mt.add_cmd("toppw") - mt.add_cmd("toppm") - mt.add_cmd("tipt") - mt.add_cmd("tippw") - mt.add_cmd("tippm") - mt.add_cmd("tist") - mt.add_cmd("tispw") - mt.add_cmd("tispm") - mt.add_raw("\n") - mt.add_param("_ticker", self.ticker) - mt.add_raw("\n") - mt.add_cmd("stats", self.ticker) - mt.add_cmd("act", self.ticker) - mt.add_cmd("lins", self.ticker) - console.print(text=mt.menu_text, menu="Stocks - Insider Trading") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - return ["stocks", f"load {self.ticker}", "ins"] - return [] - - @log_start_end(log=logger) - def call_view(self, other_args: List[str]): - """Process view command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="view", - description="""View available presets under presets folder.""", - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - help="View specific preset", - default="", - choices=self.preset_choices, - metavar="Desired preset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if ns_parser.preset: - preset_filter = configparser.RawConfigParser() - preset_filter.optionxform = str # type: ignore - preset_filter.read(self.preset_choices[ns_parser.preset]) - - filters_headers = [ - "General", - "Date", - "TransactionFiling", - "Industry", - "InsiderTitle", - "Others", - "CompanyTotals", - ] - - console.print("") - for filter_header in filters_headers: - console.print(f" - {filter_header} -") - d_filters = {**preset_filter[filter_header]} - d_filters = {k: v for k, v in d_filters.items() if v} - if d_filters: - max_len = len(max(d_filters, key=len)) - for key, value in d_filters.items(): - console.print(f"{key}{(max_len-len(key))*' '}: {value}") - console.print("") - - else: - for preset, path in self.preset_choices.items(): - with open( - path, - encoding="utf8", - ) as f: - description = "" - for line in f: - if line.strip() == "[General]": - break - description += line.strip() - console.print(f"\nPRESET: {preset.strip('.ini')}") - console.print( - description.split("Description: ")[1].replace("#", "") - ) - console.print("") - - @log_start_end(log=logger) - def call_set(self, other_args: List[str]): - """Process set command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="set", - description="""Set preset from under presets folder.""", - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - default="template", - help="Filter presets", - choices=self.preset_choices, - metavar="Desired preset", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - self.preset = ns_parser.preset - console.print("") - - @log_start_end(log=logger) - def call_filter(self, other_args: List[str]): - """Process filter command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="filter", - description="Print open insider filtered data using loaded preset. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - parser.add_argument( - "-u", - "--urls", - action="store_true", - default=False, - help="Flag to show hyperlinks", - dest="urls", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_filter( - preset=self.preset, - symbol="", - limit=ns_parser.limit, - links=ns_parser.urls, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_stats(self, other_args: List[str]): - """Process stats command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="stats", - description="Open insider filtered data using selected ticker. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - parser.add_argument( - "-u", - "--urls", - action="store_true", - default=False, - help="Flag to show hyperlinks", - dest="urls", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.ticker: - openinsider_view.print_insider_filter( - preset="", - symbol=self.ticker, - limit=ns_parser.limit, - links=ns_parser.urls, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("Please use `load ` before.\n") - - @log_start_end(log=logger) - def call_lcb(self, other_args: List[str]): - """Process latest-cluster-buys""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lcb", - description="Print latest cluster buys. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "lcb", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_lpsb(self, other_args: List[str]): - """Process latest-penny-stock-buys""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lpsb", - description="Print latest penny stock buys. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "lpsb", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_lit(self, other_args: List[str]): - """Process latest-insider-trading""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lit", - description="Print latest insider trading. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "lit", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_lip(self, other_args: List[str]): - """Process insider-purchases""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lip", - description="Print latest insider purchases. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "lip", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_blip(self, other_args: List[str]): - """Process latest-insider-purchases-25k""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="blip", - description="Print latest insider purchases 25k. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "blip", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_blop(self, other_args: List[str]): - """Process latest-officer-purchases-25k""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="blop", - description="Print latest officer purchases 25k. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "blop", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_blcp(self, other_args: List[str]): - """Process latest-ceo-cfo-purchases-25k""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="blcp", - description="Print latest CEO/CFO purchases 25k. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "blcp", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_lis(self, other_args: List[str]): - """Process insider-sales""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lis", - description="Print latest insider sales. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "lis", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_blis(self, other_args: List[str]): - """Process latest-insider-sales-100k""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="blis", - description="Print latest insider sales 100k. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "blis", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_blos(self, other_args: List[str]): - """Process latest-officer-sales-100k""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="blos", - description="Print latest officer sales 100k. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "blos", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_blcs(self, other_args: List[str]): - """Process latest-ceo-cfo-sales-100k""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="blcs", - description="Print latest CEO/CFO sales 100k. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "blcs", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_topt(self, other_args: List[str]): - """Process top-officer-purchases-of-the-day""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="topt", - description="Print top officer purchases of the day. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "topt", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_toppw(self, other_args: List[str]): - """Process top-officer-purchases-of-the-week""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="toppw", - description="Print top officer purchases of the week. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "toppw", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_toppm(self, other_args: List[str]): - """Process top-officer-purchases-of-the-month""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="toppm", - description="Print top officer purchases of the month. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "toppm", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_tipt(self, other_args: List[str]): - """Process top-insider-purchases-of-the-day""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tipt", - description="Print top insider purchases of the day. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "tipt", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_tippw(self, other_args: List[str]): - """Process top-insider-purchases-of-the-week""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tippw", - description="Print top insider purchases of the week. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "tippw", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_tippm(self, other_args: List[str]): - """Process top-insider-purchases-of-the-month""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tippm", - description="Print top insider purchases of the month. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "tippm", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_tist(self, other_args: List[str]): - """Process top-insider-sales-of-the-day""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tist", - description="Print top insider sales of the day. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "tist", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_tispw(self, other_args: List[str]): - """Process top-insider-sales-of-the-week""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tispw", - description="Print top insider sales of the week. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "tispw", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_tispm(self, other_args: List[str]): - """Process top-insider-sales-of-the-month""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tispm", - description="Print top insider sales of the month. [Source: OpenInsider]", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of datarows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - openinsider_view.print_insider_data( - "tispm", ns_parser.limit, ns_parser.export - ) - - @log_start_end(log=logger) - def call_act(self, other_args: List[str]): - """Process act command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="act", - description="""Prints insider activity over time [Source: Business Insider]""", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10 - ) - if ns_parser: - if self.ticker: - businessinsider_view.insider_activity( - data=self.stock, - symbol=self.ticker, - start_date=self.start, - interval=self.interval, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded. First use `load {ticker}`\n") - - @log_start_end(log=logger) - def call_lins(self, other_args: List[str]): - """Process lins command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="lins", - description=""" - Prints information about inside traders. The following fields are expected: Date, Relationship, - Transaction, #Shares, Cost, Value ($), #Shares Total, Insider Trading, SEC Form 4. [Source: Finviz] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="Limit of latest inside traders.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.ticker: - finviz_view.last_insider_activity( - symbol=self.ticker, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded. First use `load {ticker}`\n") diff --git a/openbb_terminal/stocks/insider/openinsider_model.py b/openbb_terminal/stocks/insider/openinsider_model.py deleted file mode 100644 index 4610380efe0..00000000000 --- a/openbb_terminal/stocks/insider/openinsider_model.py +++ /dev/null @@ -1,1515 +0,0 @@ -import configparser -import logging -import textwrap -from datetime import datetime -from typing import Dict, List - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -# pylint: disable=too-many-branches,line-too-long,C0302 -# flake8: noqa - - -d_SectorSubsectorIndustry = { - "All Sectors (except Funds)": "sic1=-1&sicl=100&sich=9999", - "Agriculture, Forestry, Fish": "sic1=1&sic2=1&sicl=100&sich=199", - "Agriculture, Forestry, Fish -> Crops": "sic1=1&sic2=1&sicl=100&sich=199", - "Agriculture, Forestry, Fish -> Livestock & Animal Specialties": "sic1=1&sic2=2&sicl=200&sich=699", - "Agriculture, Forestry, Fish -> Agriculture Services": "sic1=1&sic2=7&sicl=700&sich=799", - "Agriculture, Forestry, Fish -> Forestry": "sic1=1&sic2=8&sicl=800&sich=899", - "Agriculture, Forestry, Fish -> Fishing, Hunting & Trapping": "sic1=1&sic2=9&sicl=900&sich=999", - "Mining": "sic1=10&sic2=0&sicl=1000&sich=1499", - "Mining -> Metal Mining": "sic1=10&sic2=10&sic3=1000&sicl=1000&sich=1039", - "Mining -> Metal Mining -> Gold & Silver Ores": "sic1=10&sic2=10&sic3=1040&sicl=1040&sich=1089", - "Mining -> Metal Mining -> Miscellaneous Metal Ores": "sic1=10&sic2=10&sic3=1090&sicl=1090&sich=1219", - "Mining -> Coal Mining": "sic1=10&sic2=12&sic3=0&sicl=1200&sich=1299", - "Mining -> Coal Mining -> Bituminous Coal & Lignite Mining": "sic1=10&sic2=12&sic3=1220&sicl=1220&sich=1220", - "Mining -> Coal Mining -> Bituminous Coal & Lignite Surfac": "sic1=10&sic2=12&sic3=1221&sicl=1221&sich=1310", - "Mining -> Oil & Gas Extraction": "sic1=10&sic2=13&sic3=0&sicl=1300&sich=1399", - "Mining -> Oil & Gas Extraction -> Crude Petroleum & Natural Gas": "sic1=10&sic2=13&sic3=1311&sicl=1311&sich=1380", - "Mining -> Oil & Gas Extraction -> Drilling Oil & Gas Wells": "sic1=10&sic2=13&sic3=1381&sicl=1381&sich=1381", - "Mining -> Oil & Gas Extraction -> Oil & Gas Field Exploration Serv": "sic1=10&sic2=13&sic3=1382&sicl=1382&sich=1388", - "Mining -> Oil & Gas Extraction -> Oil & Gas Field Services": "sic1=10&sic2=13&sic3=1389&sicl=1389&sich=1399", - "Mining -> Mining Non-metallic Minerals": "sic1=10&sic2=14&sicl=1400&sich=1519", - "Construction": "sic1=15&sic2=0&sicl=1500&sich=1799", - "Construction -> General Contractors & Builders": "sic1=15&sic2=15&sicl=1500&sich=1599", - "Construction -> General Contractors & Builders -> Residential Bldg Contractors": "sic1=15&sic2=15&sic3=1520&sicl=1520&sich=1530", - "Construction -> General Contractors & Builders -> Operative Builders": "sic1=15&sic2=15&sic3=1531&sicl=1531&sich=1539", - "Construction -> General Contractors & Builders -> Nonresidential Bldg Contractors": "sic1=15&sic2=15&sic3=1540&sicl=1540&sich=1599", - "Construction -> Heavy Construction": "sic1=15&sic2=16&sic3=0&sicl=1600&sich=1699", - "Construction -> Heavy Construction -> Heavy Construction Non-Bldg Cont": "sic1=15&sic2=16&sic3=1600&sicl=1600&sich=1622", - "Construction -> Heavy Construction -> Water, Sewer, Pipeline, Comm & P": "sic1=15&sic2=16&sic3=1623&sicl=1623&sich=1699", - "Construction -> Special Trade Contractors": "sic1=15&sic2=17&sic3=0&sicl=1700&sich=1999", - "Construction -> Special Trade Contractors -> Special Trade Contractors": "sic1=15&sic2=17&sic3=1700&sicl=1700&sich=1730", - "Construction -> Special Trade Contractors -> Electrical Work": "sic1=15&sic2=17&sic3=1731&sicl=1731&sich=1999", - "Manufacturing": "sic1=20&sic2=0&sicl=2000&sich=3999", - "Manufacturing -> Food": "sic1=20&sic2=20&sic3=0&sicl=2000&sich=2099", - "Manufacturing -> Food -> Food & Kindred Products": "sic1=20&sic2=20&sic3=2000&sicl=2000&sich=2010", - "Manufacturing -> Food -> Meat Packing Plants": "sic1=20&sic2=20&sic3=2011&sicl=2011&sich=2012", - "Manufacturing -> Food -> Sausage & Other Prepared Meat P": "sic1=20&sic2=20&sic3=2013&sicl=2013&sich=2014", - "Manufacturing -> Food -> Poultry Slaughtering & Processing": "sic1=20&sic2=20&sic3=2015&sicl=2015&sich=2019", - "Manufacturing -> Food -> Dairy Products": "sic1=20&sic2=20&sic3=2020&sicl=2020&sich=2023", - "Manufacturing -> Food -> Ice Cream & Froze Desserts": "sic1=20&sic2=20&sic3=2024&sicl=2024&sich=2029", - "Manufacturing -> Food -> Canned, Frozen & Preserved Fruit": "sic1=20&sic2=20&sic3=2030&sicl=2030&sich=2032", - "Manufacturing -> Food -> Canned, Fruits, Veg, Preserves": "sic1=20&sic2=20&sic3=2033&sicl=2033&sich=2039", - "Manufacturing -> Food -> Grain Mill Products": "sic1=20&sic2=20&sic3=2040&sicl=2040&sich=2049", - "Manufacturing -> Food -> Bakery Products": "sic1=20&sic2=20&sic3=2050&sicl=2050&sich=2051", - "Manufacturing -> Food -> Cookies & Crackers": "sic1=20&sic2=20&sic3=2052&sicl=2052&sich=2059", - "Manufacturing -> Food -> Sugar & Confectionery Products": "sic1=20&sic2=20&sic3=2060&sicl=2060&sich=2069", - "Manufacturing -> Food -> Fats & Oils": "sic1=20&sic2=20&sic3=2070&sicl=2070&sich=2079", - "Manufacturing -> Food -> Beverages": "sic1=20&sic2=20&sic3=2080&sicl=2080&sich=2081", - "Manufacturing -> Food -> Malt Beverages": "sic1=20&sic2=20&sic3=2082&sicl=2082&sich=2085", - "Manufacturing -> Food -> Bottled & Canned Soft Drinks & C": "sic1=20&sic2=20&sic3=2086&sicl=2086&sich=2089", - "Manufacturing -> Food -> Miscellaneous Food Preparations": "sic1=20&sic2=20&sic3=2090&sicl=2090&sich=2091", - "Manufacturing -> Food -> Prepared Fresh or Frozen Fish &": "sic1=20&sic2=20&sic3=2092&sicl=2092&sich=2099", - "Manufacturing -> Tobacco": "sic1=20&sic2=21&sic3=0&sicl=2100&sich=2199", - "Manufacturing -> Tobacco -> Tobacco Products": "sic1=20&sic2=21&sic3=2100&sicl=2100&sich=2110", - "Manufacturing -> Tobacco -> Cigarettes": "sic1=20&sic2=21&sic3=2111&sicl=2111&sich=2199", - "Manufacturing -> Textile Mill Products": "sic1=20&sic2=22&sic3=0&sicl=2200&sich=2299", - "Manufacturing -> Textile Mill Products -> Textile Mill Products": "sic1=20&sic2=22&sic3=2200&sicl=2200&sich=2210", - "Manufacturing -> Textile Mill Products -> Broadwoven Fabric Mills, Cotton": "sic1=20&sic2=22&sic3=2211&sicl=2211&sich=2220", - "Manufacturing -> Textile Mill Products -> Broadwoven Fabric Mills, Man Mad": "sic1=20&sic2=22&sic3=2221&sicl=2221&sich=2249", - "Manufacturing -> Textile Mill Products -> Knitting Mills": "sic1=20&sic2=22&sic3=2250&sicl=2250&sich=2252", - "Manufacturing -> Textile Mill Products -> Knit Outerwear Mills": "sic1=20&sic2=22&sic3=2253&sicl=2253&sich=2272", - "Manufacturing -> Textile Mill Products -> Carpets & Rugs": "sic1=20&sic2=22&sic3=2273&sicl=2273&sich=2299", - "Manufacturing -> Apparel": "sic1=20&sic2=23&sic3=0&sicl=2300&sich=2399", - "Manufacturing -> Apparel -> Other Finished Prods of": "sic1=20&sic2=23&sic3=2300&sicl=2300&sich=2319", - "Manufacturing -> Apparel -> Men’s & Boy’s Furnishings, Work Cl": "sic1=20&sic2=23&sic3=2320&sicl=2320&sich=2329", - "Manufacturing -> Apparel -> Women’s, Misses’, & Juniors Oute": "sic1=20&sic2=23&sic3=2330&sicl=2330&sich=2339", - "Manufacturing -> Apparel -> Women’s, Misses’, Children’s & l": "sic1=20&sic2=23&sic3=2340&sicl=2340&sich=2389", - "Manufacturing -> Apparel -> Miscellaneous Fabric": "sic1=20&sic2=23&sic3=2390&sicl=2390&sich=2399", - "Manufacturing -> Lumber": "sic1=20&sic2=24&sic3=0&sicl=2400&sich=2499", - "Manufacturing -> Lumber -> Lumber & Wood Products (No Furni)": "sic1=20&sic2=24&sic3=2400&sicl=2400&sich=2420", - "Manufacturing -> Lumber -> Sawmills & Planting Mills, Gener": "sic1=20&sic2=24&sic3=2421&sicl=2421&sich=2429", - "Manufacturing -> Lumber -> Millwood, Veneer, Plywood & Str": "sic1=20&sic2=24&sic3=2430&sicl=2430&sich=2450", - "Manufacturing -> Lumber -> Mobile Homes": "sic1=20&sic2=24&sic3=2451&sicl=2451&sich=2451", - "Manufacturing -> Lumber -> Prefabricated Wood Buildings & Compo": "sic1=20&sic2=24&sic3=2452&sicl=2452&sich=2509", - "Manufacturing -> Furniture": "sic1=20&sic2=25&sic3=0&sicl=2500&sich=2599", - "Manufacturing -> Furniture -> Household Furniture": "sic1=20&sic2=25&sic3=2510&sicl=2510&sich=2510", - "Manufacturing -> Furniture -> Wood Household Furniture, (no Up)": "sic1=20&sic2=25&sic3=2511&sicl=2511&sich=2519", - "Manufacturing -> Furniture -> Office Furniture": "sic1=20&sic2=25&sic3=2520&sicl=2520&sich=2521", - "Manufacturing -> Furniture -> Office Furniture (No Wood)": "sic1=20&sic2=25&sic3=2522&sicl=2522&sich=2530", - "Manufacturing -> Furniture -> Public Bldg & Related Furniture": "sic1=20&sic2=25&sic3=2531&sicl=2531&sich=2539", - "Manufacturing -> Furniture -> Partitions, Shelving, Lockers & O": "sic1=20&sic2=25&sic3=2540&sicl=2540&sich=2589", - "Manufacturing -> Furniture -> Miscellaneous Furniture & Fixtures": "sic1=20&sic2=25&sic3=2590&sicl=2590&sich=2599", - "Manufacturing -> Paper": "sic1=20&sic2=26&sic3=0&sicl=2600&sich=2699", - "Manufacturing -> Paper -> Papers & Allied Products": "sic1=20&sic2=26&sic3=2600&sicl=2600&sich=2610", - "Manufacturing -> Paper -> Pulp Mills": "sic1=20&sic2=26&sic3=2611&sicl=2611&sich=2620", - "Manufacturing -> Paper -> Paper Mills": "sic1=20&sic2=26&sic3=2621&sicl=2621&sich=2630", - "Manufacturing -> Paper -> Paperboard Mills": "sic1=20&sic2=26&sic3=2631&sicl=2631&sich=2649", - "Manufacturing -> Paper -> Paperboard Containers & Boxes": "sic1=20&sic2=26&sic3=2650&sicl=2650&sich=2669", - "Manufacturing -> Paper -> Converted Paper & Paperboard Pro": "sic1=20&sic2=26&sic3=2670&sicl=2670&sich=2672", - "Manufacturing -> Paper -> Plastics, Foils & Coated Paper Ba": "sic1=20&sic2=26&sic3=2673&sicl=2673&sich=2710", - "Manufacturing -> Printing & Publishing": "sic1=20&sic2=27&sic3=0&sicl=2700&sich=2799", - "Manufacturing -> Printing & Publishing -> Newspapers: Publishing or Publishing and Printing": "sic1=20&sic2=27&sic3=2711&sicl=2711&sich=2720", - "Manufacturing -> Printing & Publishing -> Periodicals: Publishing or Publishing and Printing": "sic1=20&sic2=27&sic3=2721&sicl=2721&sich=2730", - "Manufacturing -> Printing & Publishing -> Books: Publishing or Publishing and Printing": "sic1=20&sic2=27&sic3=2731&sicl=2731&sich=2731", - "Manufacturing -> Printing & Publishing -> Book Printing": "sic1=20&sic2=27&sic3=2732&sicl=2732&sich=2740", - "Manufacturing -> Printing & Publishing -> Miscellaneous Publishing": "sic1=20&sic2=27&sic3=2741&sicl=2741&sich=2749", - "Manufacturing -> Printing & Publishing -> Commercial Printing": "sic1=20&sic2=27&sic3=2750&sicl=2750&sich=2760", - "Manufacturing -> Printing & Publishing -> Manifold Business Forms": "sic1=20&sic2=27&sic3=2761&sicl=2761&sich=2770", - "Manufacturing -> Printing & Publishing -> Greeting Cards": "sic1=20&sic2=27&sic3=2771&sicl=2771&sich=2779", - "Manufacturing -> Printing & Publishing -> Blankbooks, Looseleaf Binders &": "sic1=20&sic2=27&sic3=2780&sicl=2780&sich=2789", - "Manufacturing -> Printing & Publishing ->Service Industries For The Print": "sic1=20&sic2=27&sic3=2790&sicl=2790&sich=2799", - "Manufacturing -> Chemicals": "sic1=20&sic2=28&sic3=0&sicl=2800&sich=2899", - "Manufacturing -> Chemicals -> Chemicals & Allied Products": "sic1=20&sic2=28&sic3=2800&sicl=2800&sich=2809", - "Manufacturing -> Chemicals -> Industrial Inorganic Chemicals": "sic1=20&sic2=28&sic3=2810&sicl=2810&sich=2819", - "Manufacturing -> Chemicals -> Plastic Material, Synth Resin/Ru": "sic1=20&sic2=28&sic3=2820&sicl=2820&sich=2820", - "Manufacturing -> Chemicals -> Plastic Material, Synth Resins": "sic1=20&sic2=28&sic3=2821&sicl=2821&sich=2832", - "Manufacturing -> Chemicals -> Medicinal Chemicals & Botanical": "sic1=20&sic2=28&sic3=2833&sicl=2833&sich=2833", - "Manufacturing -> Chemicals -> Pharmaceutical Preparations": "sic1=20&sic2=28&sic3=2834&sicl=2834&sich=2834", - "Manufacturing -> Chemicals -> In Vitro & In Vito Diagnostic Su": "sic1=20&sic2=28&sic3=2835&sicl=2835&sich=2835", - "Manufacturing -> Chemicals -> Biological Products ,(No Diagnos": "sic1=20&sic2=28&sic3=2836&sicl=2836&sich=2839", - "Manufacturing -> Chemicals -> Soap, Detergents, Cleaning, Perf": "sic1=20&sic2=28&sic3=2840&sicl=2840&sich=2841", - "Manufacturing -> Chemicals -> Specialty Cleaning, Polishing &": "sic1=20&sic2=28&sic3=2842&sicl=2842&sich=2843", - "Manufacturing -> Chemicals -> Perfumes, Cosmetics & Other Toil": "sic1=20&sic2=28&sic3=2844&sicl=2844&sich=2850", - "Manufacturing -> Chemicals -> Paints, Varnishes, Lacquers, Ena": "sic1=20&sic2=28&sic3=2851&sicl=2851&sich=2859", - "Manufacturing -> Chemicals -> Industrial Organic Chemicals": "sic1=20&sic2=28&sic3=2860&sicl=2860&sich=2869", - "Manufacturing -> Chemicals ->Agriculture Chemicals": "sic1=20&sic2=28&sic3=2870&sicl=2870&sich=2889", - "Manufacturing -> Chemicals -> Miscellaneous Chemical Products": "sic1=20&sic2=28&sic3=2890&sicl=2890&sich=2890", - "Manufacturing -> Chemicals -> Adhesives & Sealants": "sic1=20&sic2=28&sic3=2891&sicl=2891&sich=2910", - "Manufacturing -> Petroleum Refining": "sic1=20&sic2=29&sic3=0&sicl=2900&sich=2999", - "Manufacturing -> Petroleum Refining -> Petroleum Refining": "sic1=20&sic2=29&sic3=2911&sicl=2911&sich=2949", - "Manufacturing -> Petroleum Refining -> Asphalt Paving & Roofing Materia": "sic1=20&sic2=29&sic3=2950&sicl=2950&sich=2989", - "Manufacturing -> Petroleum Refining -> Miscellaneous Products of Petrol": "sic1=20&sic2=29&sic3=2990&sicl=2990&sich=3010", - "Manufacturing -> Rubber & Plastic": "sic1=20&sic2=30&sic3=0&sicl=3000&sich=3099", - "Manufacturing -> Rubber & Plastic -> Tires & Inner Tubes": "sic1=20&sic2=30&sic3=3011&sicl=3011&sich=3020", - "Manufacturing -> Rubber & Plastic -> Rubber & Plastics Footwear": "sic1=20&sic2=30&sic3=3021&sicl=3021&sich=3049", - "Manufacturing -> Rubber & Plastic -> Gaskets & Packg & Sealg Devices &": "sic1=20&sic2=30&sic3=3050&sicl=3050&sich=3059", - "Manufacturing -> Rubber & Plastic -> Fabricated Rubber Products": "sic1=20&sic2=30&sic3=3060&sicl=3060&sich=3079", - "Manufacturing -> Rubber & Plastic -> Miscellaneous Plastics Products": "sic1=20&sic2=30&sic3=3080&sicl=3080&sich=3080", - "Manufacturing -> Rubber & Plastic -> Unsupported Plastics Film & Shee": "sic1=20&sic2=30&sic3=3081&sicl=3081&sich=3085", - "Manufacturing -> Rubber & Plastic -> Plastics Foam Products": "sic1=20&sic2=30&sic3=3086&sicl=3086&sich=3088", - "Manufacturing -> Rubber & Plastic -> Plastics Products": "sic1=20&sic2=30&sic3=3089&sicl=3089&sich=3099", - "Manufacturing -> Leather": "sic1=20&sic2=31&sic3=0&sicl=3100&sich=3199", - "Manufacturing -> Leather -> Leather Products": "sic1=20&sic2=31&sic3=3100&sicl=3100&sich=3139", - "Manufacturing -> Leather -> Footwear, (No Rubber)": "sic1=20&sic2=31&sic3=3140&sicl=3140&sich=3210", - "Manufacturing -> Stone, Clay, Glass & Concrete": "sic1=20&sic2=32&sic3=0&sicl=3200&sich=3299", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Flat Glass": "sic1=20&sic2=32&sic3=3211&sicl=3211&sich=3219", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Glass & Glasswear, Pressed or Bl": "sic1=20&sic2=32&sic3=3220&sicl=3220&sich=3220", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Glass Containers": "sic1=20&sic2=32&sic3=3221&sicl=3221&sich=3230", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Glass Products Made of Purchase": "sic1=20&sic2=32&sic3=3231&sicl=3231&sich=3240", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Cements, Hydrallic": "sic1=20&sic2=32&sic3=3241&sicl=3241&sich=3249", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Structural Clay Products": "sic1=20&sic2=32&sic3=3250&sicl=3250&sich=3259", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Pottery & Related Products": "sic1=20&sic2=32&sic3=3260&sicl=3260&sich=3269", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Concreate, Gypsum & Plaster Production": "sic1=20&sic2=32&sic3=3270&sicl=3270&sich=3271", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Concrete Products, Except Block": "sic1=20&sic2=32&sic3=3272&sicl=3272&sich=3280", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Cut Stone & Stone Products": "sic1=20&sic2=32&sic3=3281&sicl=3281&sich=3289", - "Manufacturing -> Stone, Clay, Glass & Concrete -> Abrasive, Asbestos & Misc Nonmet": "sic1=20&sic2=32&sic3=3290&sicl=3290&sich=3309", - "Manufacturing -> Primary Metal": "sic1=20&sic2=33&sic3=0&sicl=3300&sich=3399", - "Manufacturing -> Primary Metal -> Steel Works, Blast Furnaces & Ro": "sic1=20&sic2=33&sic3=3310&sicl=3310&sich=3311", - "Manufacturing -> Primary Metal -> Steel Works, Blast Furnaces & R": "sic1=20&sic2=33&sic3=3312&sicl=3312&sich=3316", - "Manufacturing -> Primary Metal -> Steel Pipe & Tubes": "sic1=20&sic2=33&sic3=3317&sicl=3317&sich=3319", - "Manufacturing -> Primary Metal -> Iron & Steel Foundries": "sic1=20&sic2=33&sic3=3320&sicl=3320&sich=3329", - "Manufacturing -> Primary Metal -> Primary Smelting & Refining of N": "sic1=20&sic2=33&sic3=3330&sicl=3330&sich=3333", - "Manufacturing -> Primary Metal -> Primary Production of Aluminium": "sic1=20&sic2=33&sic3=3334&sicl=3334&sich=3340", - "Manufacturing -> Primary Metal -> Secondary Smelting & Refining of": "sic1=20&sic2=33&sic3=3341&sicl=3341&sich=3349", - "Manufacturing -> Primary Metal -> Rolling Drawing & Extruding of N": "sic1=20&sic2=33&sic3=3350&sicl=3350&sich=3356", - "Manufacturing -> Primary Metal -> Drawing & Insulating of Non Ferro": "sic1=20&sic2=33&sic3=3357&sicl=3357&sich=3359", - "Manufacturing -> Primary Metal -> Nonferrous Foundries (Castings)": "sic1=20&sic2=33&sic3=3360&sicl=3360&sich=3389", - "Manufacturing -> Primary Metal -> Miscellaneous Primary Metal Prod": "sic1=20&sic2=33&sic3=3390&sicl=3390&sich=3410", - "Manufacturing -> Fabricated Metal Products": "sic1=20&sic2=34&sic3=0&sicl=3400&sich=3499", - "Manufacturing -> Fabricated Metal Products -> Metal Cans": "sic1=20&sic2=34&sic3=3411&sicl=3411&sich=3411", - "Manufacturing -> Fabricated Metal Products -> Metal Shipping Barrels, Drums, K": "sic1=20&sic2=34&sic3=3412&sicl=3412&sich=3419", - "Manufacturing -> Fabricated Metal Products -> Cutlery, Handtools & General Har": "sic1=20&sic2=34&sic3=3420&sicl=3420&sich=3429", - "Manufacturing -> Fabricated Metal Products -> Hearing Equip, Except Elec & War": "sic1=20&sic2=34&sic3=3430&sicl=3430&sich=3432", - "Manufacturing -> Fabricated Metal Products -> Heating Equipment, Except Electr": "sic1=20&sic2=34&sic3=3433&sicl=3433&sich=3439", - "Manufacturing -> Fabricated Metal Products -> Fabricated Structural Metal Prod": "sic1=20&sic2=34&sic3=3440&sicl=3440&sich=3441", - "Manufacturing -> Fabricated Metal Products -> Metal Doors, Sash, Frames, Moldi": "sic1=20&sic2=34&sic3=3442&sicl=3442&sich=3442", - "Manufacturing -> Fabricated Metal Products -> Fabricated Plate Work (Boiler Sh": "sic1=20&sic2=34&sic3=3443&sicl=3443&sich=3443", - "Manufacturing -> Fabricated Metal Products -> Sheet Metal Work": "sic1=20&sic2=34&sic3=3444&sicl=3444&sich=3447", - "Manufacturing -> Fabricated Metal Products -> Prefabricated Metal Buildings &": "sic1=20&sic2=34&sic3=3448&sicl=3448&sich=3450", - "Manufacturing -> Fabricated Metal Products -> Screw Machine Products": "sic1=20&sic2=34&sic3=3451&sicl=3451&sich=3451", - "Manufacturing -> Fabricated Metal Products -> Bolts, Nuts, Screws, Rivets & Wa": "sic1=20&sic2=34&sic3=3452&sicl=3452&sich=3459", - "Manufacturing -> Fabricated Metal Products -> Metal Forgings & Stampings": "sic1=20&sic2=34&sic3=3460&sicl=3460&sich=3469", - "Manufacturing -> Fabricated Metal Products -> Coating, Engraving & Allied Serv": "sic1=20&sic2=34&sic3=3470&sicl=3470&sich=3479", - "Manufacturing -> Fabricated Metal Products -> Ordnance & Accessories, (No Vehi": "sic1=20&sic2=34&sic3=3480&sicl=3480&sich=3489", - "Manufacturing -> Fabricated Metal Products -> Miscellaneous Fabricated Metal P": "sic1=20&sic2=34&sic3=3490&sicl=3490&sich=3509", - "Manufacturing -> Ind Machinery & Computers": "sic1=20&sic2=35&sicl=3500&sich=3599", - "Manufacturing -> Ind Machinery & Computers -> Engines & Turbines": "sic1=20&sic2=35&sic3=3510&sicl=3510&sich=3522", - "Manufacturing -> Ind Machinery & Computers -> Farm Machinery & Equipment": "sic1=20&sic2=35&sic3=3523&sicl=3523&sich=3523", - "Manufacturing -> Ind Machinery & Computers -> Lawn & Garden Tractors & Home La": "sic1=20&sic2=35&sic3=3524&sicl=3524&sich=3529", - "Manufacturing -> Ind Machinery & Computers -> Construction, Mining & Materials": "sic1=20&sic2=35&sic3=3530&sicl=3530&sich=3530", - "Manufacturing -> Ind Machinery & Computers -> Construction Machinery & Equip": "sic1=20&sic2=35&sic3=3531&sicl=3531&sich=3531", - "Manufacturing -> Ind Machinery & Computers -> Mining Machinery & Equip (No Oil": "sic1=20&sic2=35&sic3=3532&sicl=3532&sich=3532", - "Manufacturing -> Ind Machinery & Computers -> Oil & Gas Field Machinery & Equi": "sic1=20&sic2=35&sic3=3533&sicl=3533&sich=3536", - "Manufacturing -> Ind Machinery & Computers -> Industrial Trucks, Tractors, Tra": "sic1=20&sic2=35&sic3=3537&sicl=3537&sich=3539", - "Manufacturing -> Ind Machinery & Computers -> Metalworking Machinery & Equipment": "sic1=20&sic2=35&sic3=3540&sicl=3540&sich=3540", - "Manufacturing -> Ind Machinery & Computers -> Machine Tools, Metal Cutting Typ": "sic1=20&sic2=35&sic3=3541&sicl=3541&sich=3549", - "Manufacturing -> Ind Machinery & Computers -> Special Industry Machinery (No M": "sic1=20&sic2=35&sic3=3550&sicl=3550&sich=3554", - "Manufacturing -> Ind Machinery & Computers -> Printing Trades Machinery & Equi": "sic1=20&sic2=35&sic3=3555&sicl=3555&sich=3558", - "Manufacturing -> Ind Machinery & Computers -> Special Industry Machinery": "sic1=20&sic2=35&sic3=3559&sicl=3559&sich=3559", - "Manufacturing -> Ind Machinery & Computers -> General Industrial Machinery & E": "sic1=20&sic2=35&sic3=3560&sicl=3560&sich=3560", - "Manufacturing -> Ind Machinery & Computers -> Pumps & Pumping Equipment": "sic1=20&sic2=35&sic3=3561&sicl=3561&sich=3561", - "Manufacturing -> Ind Machinery & Computers -> Ball & Roller Bearings": "sic1=20&sic2=35&sic3=3562&sicl=3562&sich=3563", - "Manufacturing -> Ind Machinery & Computers -> Industrial & Commercial Fans & B": "sic1=20&sic2=35&sic3=3564&sicl=3564&sich=3566", - "Manufacturing -> Ind Machinery & Computers -> Industrial Process Furnaces & Ov": "sic1=20&sic2=35&sic3=3567&sicl=3567&sich=3568", - # "Manufacturing -> Ind Machinery & Computers -> General Industrial Machinery & E": "sic1=20&sic2=35&sic3=3569&sicl=3569&sich=3569", - "Manufacturing -> Ind Machinery & Computers -> Computer & Office Equipment": "sic1=20&sic2=35&sic3=3570&sicl=3570&sich=3570", - "Manufacturing -> Ind Machinery & Computers -> Electronic Computers": "sic1=20&sic2=35&sic3=3571&sicl=3571&sich=3571", - "Manufacturing -> Ind Machinery & Computers -> Computer Storage Devices": "sic1=20&sic2=35&sic3=3572&sicl=3572&sich=3574", - "Manufacturing -> Ind Machinery & Computers -> Computer Terminals": "sic1=20&sic2=35&sic3=3575&sicl=3575&sich=3575", - "Manufacturing -> Ind Machinery & Computers -> Computer Communications Equipmen": "sic1=20&sic2=35&sic3=3576&sicl=3576&sich=3576", - "Manufacturing -> Ind Machinery & Computers -> Computer Peripheral Equipment": "sic1=20&sic2=35&sic3=3577&sicl=3577&sich=3577", - "Manufacturing -> Ind Machinery & Computers -> Calculating & Accounting Machine": "sic1=20&sic2=35&sic3=3578&sicl=3578&sich=3578", - "Manufacturing -> Ind Machinery & Computers -> Office Machines": "sic1=20&sic2=35&sic3=3579&sicl=3579&sich=3579", - "Manufacturing -> Ind Machinery & Computers -> Refrigeration & Service Industry": "sic1=20&sic2=35&sic3=3580&sicl=3580&sich=3584", - "Manufacturing -> Ind Machinery & Computers -> Air-Cond & Warm Air Heating Equip": "sic1=20&sic2=35&sic3=3585&sicl=3585&sich=3589", - "Manufacturing -> Ind Machinery & Computers -> Misc Industrial & Commercial Mac": "sic1=20&sic2=35&sic3=3590&sicl=3590&sich=3599", - "Manufacturing -> Electronics": "sic1=20&sic2=36&sic3=0&sicl=3600&sich=3699", - "Manufacturing -> Electronics -> Electronic & Other Electrical Eq": "sic1=20&sic2=36&sic3=3600&sicl=3600&sich=3611", - "Manufacturing -> Electronics -> Power, Distribution & Specialty": "sic1=20&sic2=36&sic3=3612&sicl=3612&sich=3612", - "Manufacturing -> Electronics -> Switchgear & Switchboard Apparat": "sic1=20&sic2=36&sic3=3613&sicl=3613&sich=3619", - "Manufacturing -> Electronics -> Electrical Industrial Apparatus": "sic1=20&sic2=36&sic3=3620&sicl=3620&sich=3620", - "Manufacturing -> Electronics -> Motors & Generators": "sic1=20&sic2=36&sic3=3621&sicl=3621&sich=3629", - "Manufacturing -> Electronics -> Household Appliances": "sic1=20&sic2=36&sic3=3630&sicl=3630&sich=3633", - "Manufacturing -> Electronics -> Electric Housewares & Fans": "sic1=20&sic2=36&sic3=3634&sicl=3634&sich=3639", - "Manufacturing -> Electronics -> Electric Lighting & Wiring Equip": "sic1=20&sic2=36&sic3=3640&sicl=3640&sich=3650", - "Manufacturing -> Electronics -> Household Audio & Video Equipmen": "sic1=20&sic2=36&sic3=3651&sicl=3651&sich=3651", - "Manufacturing -> Electronics -> Phonograph Records & Prerecorded": "sic1=20&sic2=36&sic3=3652&sicl=3652&sich=3660", - "Manufacturing -> Electronics -> Telephone & Telegraph Apparatus": "sic1=20&sic2=36&sic3=3661&sicl=3661&sich=3662", - "Manufacturing -> Electronics -> Radio & Tv Broadcasting & Commun": "sic1=20&sic2=36&sic3=3663&sicl=3663&sich=3668", - "Manufacturing -> Electronics -> Communications Equipment": "sic1=20&sic2=36&sic3=3669&sicl=3669&sich=3669", - "Manufacturing -> Electronics -> Electronic Components & Accessor": "sic1=20&sic2=36&sic3=3670&sicl=3670&sich=3671", - "Manufacturing -> Electronics -> Printed Circuit Boards": "sic1=20&sic2=36&sic3=3672&sicl=3672&sich=3673", - "Manufacturing -> Electronics -> Semiconductors & Related Devices": "sic1=20&sic2=36&sic3=3674&sicl=3674&sich=3676", - "Manufacturing -> Electronics -> Electronic Coils & Transformers &": "sic1=20&sic2=36&sic3=3677&sicl=3677&sich=3677", - "Manufacturing -> Electronics -> Electronic Connectors": "sic1=20&sic2=36&sic3=3678&sicl=3678&sich=3678", - "Manufacturing -> Electronics -> Electronic Components": "sic1=20&sic2=36&sic3=3679&sicl=3679&sich=3689", - "Manufacturing -> Electronics -> Miscellaneous Electrical Machine": "sic1=20&sic2=36&sic3=3690&sicl=3690&sich=3694", - "Manufacturing -> Electronics -> Magnetic & Optical Recording Med": "sic1=20&sic2=36&sic3=3695&sicl=3695&sich=3710", - "Manufacturing -> Transportation Equipment": "sic1=20&sic2=37&sic3=0&sicl=3700&sich=3799", - "Manufacturing -> Transportation Equipment -> Motor Vehicles & Passenger Car B": "sic1=20&sic2=37&sic3=3711&sicl=3711&sich=3712", - "Manufacturing -> Transportation Equipment -> Truck & Bus Bodies": "sic1=20&sic2=37&sic3=3713&sicl=3713&sich=3713", - "Manufacturing -> Transportation Equipment -> Motor Vehicle Parts & Accessories": "sic1=20&sic2=37&sic3=3714&sicl=3714&sich=3714", - "Manufacturing -> Transportation Equipment -> Truck Trailers": "sic1=20&sic2=37&sic3=3715&sicl=3715&sich=3715", - "Manufacturing -> Transportation Equipment -> Motor Homes": "sic1=20&sic2=37&sic3=3716&sicl=3716&sich=3719", - "Manufacturing -> Transportation Equipment -> Aircraft & Parts": "sic1=20&sic2=37&sic3=3720&sicl=3720&sich=3720", - "Manufacturing -> Transportation Equipment -> Aircraft": "sic1=20&sic2=37&sic3=3721&sicl=3721&sich=3723", - "Manufacturing -> Transportation Equipment -> Aircraft Engines & Engine Parts": "sic1=20&sic2=37&sic3=3724&sicl=3724&sich=3727", - "Manufacturing -> Transportation Equipment -> Aircraft Parts & Auxiliary Equip": "sic1=20&sic2=37&sic3=3728&sicl=3728&sich=3729", - "Manufacturing -> Transportation Equipment -> Ship & Boat Building & Repairing": "sic1=20&sic2=37&sic3=3730&sicl=3730&sich=3742", - "Manufacturing -> Transportation Equipment -> Railroad Equipment": "sic1=20&sic2=37&sic3=3743&sicl=3743&sich=3750", - "Manufacturing -> Transportation Equipment -> Motorcycles, Bicycles & Parts": "sic1=20&sic2=37&sic3=3751&sicl=3751&sich=3759", - "Manufacturing -> Transportation Equipment -> Guided Missiles & Space Vehicles": "sic1=20&sic2=37&sic3=3760&sicl=3760&sich=3789", - "Manufacturing -> Transportation Equipment -> Miscellaneous Transportation Equ": "sic1=20&sic2=37&sic3=3790&sicl=3790&sich=3811", - "Manufacturing -> Specialty Instruments": "sic1=20&sic2=38&sicl=3800&sich=3899", - "Manufacturing -> Specialty Instruments -> Search, Detection, Navigation": "sic1=20&sic2=38&sic3=3812&sicl=3812&sich=3820", - "Manufacturing -> Specialty Instruments -> Laboratory Apparatus & Furniture": "sic1=20&sic2=38&sic3=3821&sicl=3821&sich=3821", - "Manufacturing -> Specialty Instruments -> Auto Controls For Regulating Res": "sic1=20&sic2=38&sic3=3822&sicl=3822&sich=3822", - "Manufacturing -> Specialty Instruments -> Industrial Instruments For Measurement": "sic1=20&sic2=38&sic3=3823&sicl=3823&sich=3823", - "Manufacturing -> Specialty Instruments -> Totalizing Fluid Meter and Counting Device Manufacturing": "sic1=20&sic2=38&sic3=3824&sicl=3824&sich=3824", - "Manufacturing -> Specialty Instruments -> Instruments For Meas & Testing o": "sic1=20&sic2=38&sic3=3825&sicl=3825&sich=3825", - "Manufacturing -> Specialty Instruments -> Laboratory Analytical Instrument": "sic1=20&sic2=38&sic3=3826&sicl=3826&sich=3826", - "Manufacturing -> Specialty Instruments -> Optical Instruments & Lenses": "sic1=20&sic2=38&sic3=3827&sicl=3827&sich=3828", - "Manufacturing -> Specialty Instruments -> Measuring & Controlling Devices": "sic1=20&sic2=38&sic3=3829&sicl=3829&sich=3840", - "Manufacturing -> Specialty Instruments -> Surgical & Medical Instruments &": "sic1=20&sic2=38&sic3=3841&sicl=3841&sich=3841", - "Manufacturing -> Specialty Instruments -> Orthopedic, Prosthetic & Surgical": "sic1=20&sic2=38&sic3=3842&sicl=3842&sich=3842", - "Manufacturing -> Specialty Instruments -> Dental Equipment & Supplies": "sic1=20&sic2=38&sic3=3843&sicl=3843&sich=3843", - "Manufacturing -> Specialty Instruments -> X-Ray Apparatus & Tubes & Relate": "sic1=20&sic2=38&sic3=3844&sicl=3844&sich=3844", - "Manufacturing -> Specialty Instruments -> Electromedical and Electrotherapeutic Apparatus": "sic1=20&sic2=38&sic3=3845&sicl=3845&sich=3850", - "Manufacturing -> Specialty Instruments -> Ophthalmic Goods": "sic1=20&sic2=38&sic3=3851&sicl=3851&sich=3860", - "Manufacturing -> Specialty Instruments -> Photographic Equipment & Supplie": "sic1=20&sic2=38&sic3=3861&sicl=3861&sich=3872", - "Manufacturing -> Specialty Instruments -> Watches, Clocks, Clockwork Opera": "sic1=20&sic2=38&sic3=3873&sicl=3873&sich=3909", - "Manufacturing -> Miscellaneous Manufacturing": "sic1=20&sic2=39&sic3=0&sicl=3900&sich=3999", - "Manufacturing -> Miscellaneous Manufacturing -> Jewelry, Silverware & Plated War": "sic1=20&sic2=39&sic3=3910&sicl=3910&sich=3910", - "Manufacturing -> Miscellaneous Manufacturing -> Jewelry, Precious Metal": "sic1=20&sic2=39&sic3=3911&sicl=3911&sich=3930", - "Manufacturing -> Miscellaneous Manufacturing -> Musical Instruments": "sic1=20&sic2=39&sic3=3931&sicl=3931&sich=3941", - "Manufacturing -> Miscellaneous Manufacturing -> Dolls & Stuffed Toys": "sic1=20&sic2=39&sic3=3942&sicl=3942&sich=3943", - "Manufacturing -> Miscellaneous Manufacturing -> Games, Toys & Children’s Vehicle": "sic1=20&sic2=39&sic3=3944&sicl=3944&sich=3948", - "Manufacturing -> Miscellaneous Manufacturing -> Sporting & Athletic Goods": "sic1=20&sic2=39&sic3=3949&sicl=3949&sich=3949", - "Manufacturing -> Miscellaneous Manufacturing -> Pens, Pencils & Other Artists’ M": "sic1=20&sic2=39&sic3=3950&sicl=3950&sich=3959", - "Manufacturing -> Miscellaneous Manufacturing -> Costumes Jewelry & Novelties": "sic1=20&sic2=39&sic3=3960&sicl=3960&sich=3989", - "Manufacturing -> Miscellaneous Manufacturing -> Miscellaneous Manufacturing Indu": "sic1=20&sic2=39&sic3=3990&sicl=3990&sich=4010", - "Transportation & Utilities": "sic1=40&sic2=0&sicl=4000&sich=4999", - "Transportation & Utilities -> Railroads": "sic1=40&sic2=40&sicl=4000&sich=4099", - "Transportation & Utilities -> Railroads -> Railroads, Line-Haul Operating": "sic1=40&sic2=40&sic3=4011&sicl=4011&sich=4012", - "Transportation & Utilities -> Railroads -> Railroad Switching & Terminal Es": "sic1=40&sic2=40&sic3=4013&sicl=4013&sich=4099", - "Transportation & Utilities -> Passenger Road Transportation": "sic1=40&sic2=41&sicl=4100&sich=4209", - "Transportation & Utilities -> Road Freight Transportation": "sic1=40&sic2=42&sic3=0&sicl=4200&sich=4299", - "Transportation & Utilities -> Road Freight Transportation -> Trucking & Courier Services (No": "sic1=40&sic2=42&sic3=4210&sicl=4210&sich=4212", - "Transportation & Utilities -> Road Freight Transportation -> Trucking (No Local)": "sic1=40&sic2=42&sic3=4213&sicl=4213&sich=4219", - "Transportation & Utilities -> Road Freight Transportation -> Public Warehousing & Storage": "sic1=40&sic2=42&sic3=4220&sicl=4220&sich=4230", - "Transportation & Utilities -> Road Freight Transportation -> Terminal Maintenance Facilities": "sic1=40&sic2=42&sic3=4231&sicl=4231&sich=4399", - "Transportation & Utilities -> USPS": "sic1=40&sic2=43&sicl=4300&sich=4399", - "Transportation & Utilities -> Water Transportation": "sic1=40&sic2=44&sic3=0&sicl=4400&sich=4499", - "Transportation & Utilities -> Water Transportation -> Water Transportation": "sic1=40&sic2=44&sic3=4400&sicl=4400&sich=4411", - "Transportation & Utilities -> Water Transportation -> Deep Sea Foreign Transportation": "sic1=40&sic2=44&sic3=4412&sicl=4412&sich=4511", - "Transportation & Utilities -> Air Transportation": "sic1=40&sic2=45&sic3=0&sicl=4500&sich=4599", - "Transportation & Utilities -> Air Transportation -> Air Transportation, Scheduled": "sic1=40&sic2=45&sic3=4512&sicl=4512&sich=4512", - "Transportation & Utilities -> Air Transportation -> Air Courier Services": "sic1=40&sic2=45&sic3=4513&sicl=4513&sich=4521", - "Transportation & Utilities -> Air Transportation -> Air Transportation, Nonscheduled": "sic1=40&sic2=45&sic3=4522&sicl=4522&sich=4580", - "Transportation & Utilities -> Air Transportation -> Airports, Flying Fields & Airpor": "sic1=40&sic2=45&sic3=4581&sicl=4581&sich=4609", - "Transportation & Utilities -> Pipelines": "sic1=40&sic2=46&sicl=4610&sich=4699", - "Transportation & Utilities -> Transportation Services": "sic1=40&sic2=47&sic3=0&sicl=4700&sich=4799", - "Transportation & Utilities -> Transportation Services -> Transportation Services": "sic1=40&sic2=47&sic3=4700&sicl=4700&sich=4730", - "Transportation & Utilities -> Transportation Services -> Arrangement of Transportation of": "sic1=40&sic2=47&sic3=4731&sicl=4731&sich=4811", - "Transportation & Utilities -> Communications": "sic1=40&sic2=48&sic3=0&sicl=4800&sich=4899", - "Transportation & Utilities -> Communications -> Radiotelephone Communications": "sic1=40&sic2=48&sic3=4812&sicl=4812&sich=4812", - "Transportation & Utilities -> Communications -> Telephone Communications (No Rad": "sic1=40&sic2=48&sic3=4813&sicl=4813&sich=4821", - "Transportation & Utilities -> Communications -> Telegraph & Other Message Commun": "sic1=40&sic2=48&sic3=4822&sicl=4822&sich=4831", - "Transportation & Utilities -> Communications -> Radio Broadcasting Stations": "sic1=40&sic2=48&sic3=4832&sicl=4832&sich=4832", - "Transportation & Utilities -> Communications -> Television Broadcasting Stations": "sic1=40&sic2=48&sic3=4833&sicl=4833&sich=4840", - "Transportation & Utilities -> Communications -> Cable & Other Pay Television Ser": "sic1=40&sic2=48&sic3=4841&sicl=4841&sich=4898", - "Transportation & Utilities -> Communications -> Communications Services": "sic1=40&sic2=48&sic3=4899&sicl=4899&sich=4899", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs": "sic1=40&sic2=49&sic3=0&sicl=4900&sich=4999", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric, Gas, & Sanitary Svcs": "sic1=40&sic2=49&sic3=4900&sicl=4900&sich=4910", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric Services": "sic1=40&sic2=49&sic3=4911&sicl=4911&sich=4921", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Transmission": "sic1=40&sic2=49&sic3=4922&sicl=4922&sich=4922", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Transmission & Distr": "sic1=40&sic2=49&sic3=4923&sicl=4923&sich=4923", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Natural Gas Distribution": "sic1=40&sic2=49&sic3=4924&sicl=4924&sich=4930", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Electric & Other Services Combin": "sic1=40&sic2=49&sic3=4931&sicl=4931&sich=4931", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Gas & Other Services Combined": "sic1=40&sic2=49&sic3=4932&sicl=4932&sich=4940", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Water Supply": "sic1=40&sic2=49&sic3=4941&sicl=4941&sich=4949", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Sanitary Services": "sic1=40&sic2=49&sic3=4950&sicl=4950&sich=4952", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Refuse Systems": "sic1=40&sic2=49&sic3=4953&sicl=4953&sich=4954", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Hazardous Waste Management": "sic1=40&sic2=49&sic3=4955&sicl=4955&sich=4960", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Steam & Air-Conditioning Supply": "sic1=40&sic2=49&sic3=4961&sicl=4961&sich=4990", - "Transportation & Utilities -> Electric, Gas, & Sanitary Svcs -> Cogeneration Services & Small Po": "sic1=40&sic2=49&sic3=4991&sicl=4991&sich=4999", - "Wholesale Trade": "sic1=50&sic2=0&sicl=5000&sich=5199", - "Wholesale Trade -> Durable Goods -> Durable Goods": "sic1=50&sic2=50&sic3=5000&sicl=5000&sich=5009", - "Wholesale Trade -> Durable Goods -> Motor Vehicles & Motor Vehicle P": "sic1=50&sic2=50&sic3=5010&sicl=5010&sich=5012", - "Wholesale Trade -> Durable Goods -> Motor Vehicles Supplies & New Par": "sic1=50&sic2=50&sic3=5013&sicl=5013&sich=5019", - "Wholesale Trade -> Durable Goods -> Furniture & Home Furnishings": "sic1=50&sic2=50&sic3=5020&sicl=5020&sich=5029", - "Wholesale Trade -> Durable Goods -> Lumber & Other Construction Mate": "sic1=50&sic2=50&sic3=5030&sicl=5030&sich=5030", - "Wholesale Trade -> Durable Goods -> Lumber, Plywood, Millwork & Wood": "sic1=50&sic2=50&sic3=5031&sicl=5031&sich=5039", - "Wholesale Trade -> Durable Goods -> Professional & Commercial Equipment": "sic1=50&sic2=50&sic3=5040&sicl=5040&sich=5044", - "Wholesale Trade -> Durable Goods -> Computers & Peripheral Equipment": "sic1=50&sic2=50&sic3=5045&sicl=5045&sich=5046", - "Wholesale Trade -> Durable Goods -> Medical, Dental & Hospital Equip": "sic1=50&sic2=50&sic3=5047&sicl=5047&sich=5049", - "Wholesale Trade -> Durable Goods -> Metals & Minerals (No Petroleum)": "sic1=50&sic2=50&sic3=5050&sicl=5050&sich=5050", - "Wholesale Trade -> Durable Goods -> Metals Services Centers & Offices": "sic1=50&sic2=50&sic3=5051&sicl=5051&sich=5062", - "Wholesale Trade -> Durable Goods -> Electrical Apparatus & Equipment": "sic1=50&sic2=50&sic3=5063&sicl=5063&sich=5063", - "Wholesale Trade -> Durable Goods -> Electrical Appliances, Tv & Radi": "sic1=50&sic2=50&sic3=5064&sicl=5064&sich=5064", - "Wholesale Trade -> Durable Goods -> Electronic Parts & Equipment": "sic1=50&sic2=50&sic3=5065&sicl=5065&sich=5069", - "Wholesale Trade -> Durable Goods -> Hardware & Plumbing & Heating Eq": "sic1=50&sic2=50&sic3=5070&sicl=5070&sich=5071", - "Wholesale Trade -> Durable Goods -> Hardware": "sic1=50&sic2=50&sic3=5072&sicl=5072&sich=5079", - "Wholesale Trade -> Durable Goods -> Machinery, Equipment & Supplies": "sic1=50&sic2=50&sic3=5080&sicl=5080&sich=5081", - "Wholesale Trade -> Durable Goods -> Construction & Mining (No Petro)": "sic1=50&sic2=50&sic3=5082&sicl=5082&sich=5083", - "Wholesale Trade -> Durable Goods -> Industrial Machinery & Equipment": "sic1=50&sic2=50&sic3=5084&sicl=5084&sich=5089", - "Wholesale Trade -> Durable Goods -> Misc Durable Goods": "sic1=50&sic2=50&sic3=5090&sicl=5090&sich=5093", - "Wholesale Trade -> Durable Goods -> Jewelry, Watches, Precious Stone": "sic1=50&sic2=50&sic3=5094&sicl=5094&sich=5098", - # "Wholesale Trade -> Durable Goods -> Durable Goods": "sic1=50&sic2=50&sic3=5099&sicl=5099&sich=5109", - "Wholesale Trade -> Nondurable Goods": "sic1=50&sic2=51&sic3=0&sicl=5100&sich=5199", - "Wholesale Trade -> Nondurable Goods -> Paper & Paper Products": "sic1=50&sic2=51&sic3=5110&sicl=5110&sich=5121", - "Wholesale Trade -> Nondurable Goods -> Drugs, Proprietaries & Druggists": "sic1=50&sic2=51&sic3=5122&sicl=5122&sich=5129", - "Wholesale Trade -> Nondurable Goods -> Apparel, Piece Goods & Notions": "sic1=50&sic2=51&sic3=5130&sicl=5130&sich=5139", - "Wholesale Trade -> Nondurable Goods -> Groceries & Related Products": "sic1=50&sic2=51&sic3=5140&sicl=5140&sich=5140", - "Wholesale Trade -> Nondurable Goods -> Groceries, General Line": "sic1=50&sic2=51&sic3=5141&sicl=5141&sich=5149", - "Wholesale Trade -> Nondurable Goods -> Farm Product Raw Materials": "sic1=50&sic2=51&sic3=5150&sicl=5150&sich=5159", - "Wholesale Trade -> Nondurable Goods -> Chemicals & Allied Products": "sic1=50&sic2=51&sic3=5160&sicl=5160&sich=5170", - "Wholesale Trade -> Nondurable Goods -> Petroleum Bulk Stations & Termin": "sic1=50&sic2=51&sic3=5171&sicl=5171&sich=5171", - "Wholesale Trade -> Nondurable Goods -> Petroleum & Petroleum Products (": "sic1=50&sic2=51&sic3=5172&sicl=5172&sich=5179", - "Wholesale Trade -> Nondurable Goods -> Beer, Wine & Distilled Alcoholic": "sic1=50&sic2=51&sic3=5180&sicl=5180&sich=5189", - "Wholesale Trade -> Nondurable Goods -> Miscellaneous Nondurable Goods": "sic1=50&sic2=51&sic3=5190&sicl=5190&sich=5199", - "Retail Trade": "sic1=52&sic2=0&sicl=5200&sich=5999", - "Retail Trade -> Building Materials": "sic1=52&sic2=52&sic3=0&sicl=5200&sich=5299", - "Retail Trade -> Building Materials -> Building Materials, Hardware, Ga": "sic1=52&sic2=52&sic3=5200&sicl=5200&sich=5210", - "Retail Trade -> Building Materials -> Lumber & Other Buildings Material": "sic1=52&sic2=52&sic3=5211&sicl=5211&sich=5270", - "Retail Trade -> Building Materials -> Mobile Home Dealers": "sic1=52&sic2=52&sic3=5271&sicl=5271&sich=5310", - "Retail Trade -> General Merchandise": "sic1=52&sic2=53&sicl=5300&sich=5399", - "Retail Trade -> General Merchandise -> Department Stores": "sic1=52&sic2=53&sic3=5311&sicl=5311&sich=5330", - "Retail Trade -> General Merchandise -> Variety Stores": "sic1=52&sic2=53&sic3=5331&sicl=5331&sich=5398", - "Retail Trade -> General Merchandise -> Misc General Merchandise Stores": "sic1=52&sic2=53&sic3=5399&sicl=5399&sich=5399", - "Retail Trade -> Grocery": "sic1=52&sic2=54&sic3=0&sicl=5400&sich=5499", - "Retail Trade -> Grocery -> Food Stores": "sic1=52&sic2=54&sic3=5400&sicl=5400&sich=5410", - "Retail Trade -> Grocery -> Grocery Stores": "sic1=52&sic2=54&sic3=5411&sicl=5411&sich=5411", - "Retail Trade -> Grocery -> Convenience Stores": "sic1=52&sic2=54&sic3=5412&sicl=5412&sich=5499", - "Retail Trade -> Auto Dealers & Service Stations": "sic1=52&sic2=55&sic3=0&sicl=5500&sich=5599", - "Retail Trade -> Auto Dealers & Service Stations -> Auto Dealers & Gasoline Stations": "sic1=52&sic2=55&sic3=5500&sicl=5500&sich=5530", - "Retail Trade -> Auto Dealers & Service Stations -> Auto & Home Supply Stores": "sic1=52&sic2=55&sic3=5531&sicl=5531&sich=5599", - "Retail Trade -> Apparel & Accessory Stores": "sic1=52&sic2=56&sic3=0&sicl=5600&sich=5699", - "Retail Trade -> Apparel & Accessory Stores -> Apparel & Accessory Stores": "sic1=52&sic2=56&sic3=5600&sicl=5600&sich=5620", - "Retail Trade -> Apparel & Accessory Stores -> Women’s Clothing Stores": "sic1=52&sic2=56&sic3=5621&sicl=5621&sich=5650", - "Retail Trade -> Apparel & Accessory Stores -> Family Clothing Stores": "sic1=52&sic2=56&sic3=5651&sicl=5651&sich=5660", - "Retail Trade -> Apparel & Accessory Stores -> Shoe Stores": "sic1=52&sic2=56&sic3=5661&sicl=5661&sich=5699", - "Retail Trade -> Home Furniture": "sic1=52&sic2=57&sic3=0&sicl=5700&sich=5799", - "Retail Trade -> Home Furniture -> Home Furniture, Furnishings & Eq": "sic1=52&sic2=57&sic3=5700&sicl=5700&sich=5711", - "Retail Trade -> Home Furniture -> Furniture Stores": "sic1=52&sic2=57&sic3=5712&sicl=5712&sich=5730", - "Retail Trade -> Home Furniture -> Ratio, Tv & Consumer Electronics": "sic1=52&sic2=57&sic3=5731&sicl=5731&sich=5733", - "Retail Trade -> Home Furniture -> Computer & Computer Software Sto": "sic1=52&sic2=57&sic3=5734&sicl=5734&sich=5734", - "Retail Trade -> Home Furniture -> Record & Prerecord Tape Stores": "sic1=52&sic2=57&sic3=5735&sicl=5735&sich=5809", - "Retail Trade -> Eating & Drinking Places": "sic1=52&sic2=58&sic3=0&sicl=5800&sich=5899", - "Retail Trade -> Eating & Drinking Places -> Eating & Drinking Places": "sic1=52&sic2=58&sic3=5810&sicl=5810&sich=5811", - "Retail Trade -> Eating & Drinking Places -> Eating Places": "sic1=52&sic2=58&sic3=5812&sicl=5812&sich=5899", - "Retail Trade -> Miscellaneous Retail": "sic1=52&sic2=59&sic3=0&sicl=5900&sich=5999", - "Retail Trade -> Miscellaneous Retail -> Miscellaneous Retail": "sic1=52&sic2=59&sic3=5900&sicl=5900&sich=5911", - "Retail Trade -> Miscellaneous Retail -> Drug Stores & Proprietary Stores": "sic1=52&sic2=59&sic3=5912&sicl=5912&sich=5939", - "Retail Trade -> Miscellaneous Retail -> Miscellaneous Shopping Goods Sto": "sic1=52&sic2=59&sic3=5940&sicl=5940&sich=5943", - "Retail Trade -> Miscellaneous Retail -> Jewelry Stores": "sic1=52&sic2=59&sic3=5944&sicl=5944&sich=5944", - "Retail Trade -> Miscellaneous Retail -> Hobby, Toy & Game Shops": "sic1=52&sic2=59&sic3=5945&sicl=5945&sich=5959", - "Retail Trade -> Miscellaneous Retail -> Nonstore Retailers": "sic1=52&sic2=59&sic3=5960&sicl=5960&sich=5960", - "Retail Trade -> Miscellaneous Retail -> Catalog & Mail-Order Houses": "sic1=52&sic2=59&sic3=5961&sicl=5961&sich=5989", - "Retail Trade -> Miscellaneous Retail -> Retail Stores": "sic1=52&sic2=59&sic3=5990&sicl=5990&sich=6020", - "Financial": "sic1=60&sic2=0&sicl=6000&sich=6799", - "Financial -> Depository Institutions": "sic1=60&sic2=60&sicl=6000&sich=6099", - "Financial -> Depository Institutions -> National Commercial Banks": "sic1=60&sic2=60&sic3=6021&sicl=6021&sich=6021", - "Financial -> Depository Institutions -> State Commercial Banks": "sic1=60&sic2=60&sic3=6022&sicl=6022&sich=6028", - "Financial -> Depository Institutions -> Commercial Banks": "sic1=60&sic2=60&sic3=6029&sicl=6029&sich=6034", - "Financial -> Depository Institutions -> Savings Institution, Federally C": "sic1=60&sic2=60&sic3=6035&sicl=6035&sich=6035", - "Financial -> Depository Institutions -> Savings Institution, Not Federa": "sic1=60&sic2=60&sic3=6036&sicl=6036&sich=6098", - "Financial -> Depository Institutions -> Functions Related to Despository": "sic1=60&sic2=60&sic3=6099&sicl=6099&sich=6110", - "Financial -> Non-depository Credit Inst": "sic1=60&sic2=61&sic3=0&sicl=6100&sich=6199", - "Financial -> Non-depository Credit Inst -> Federal & Federally-Sponsored Cr": "sic1=60&sic2=61&sic3=6111&sicl=6111&sich=6140", - "Financial -> Non-depository Credit Inst -> Personal Credit Institutions": "sic1=60&sic2=61&sic3=6141&sicl=6141&sich=6152", - "Financial -> Non-depository Credit Inst -> Short-Term Business Credit Insti": "sic1=60&sic2=61&sic3=6153&sicl=6153&sich=6158", - "Financial -> Non-depository Credit Inst -> Miscellaneous Business Credit In": "sic1=60&sic2=61&sic3=6159&sicl=6159&sich=6161", - "Financial -> Non-depository Credit Inst -> Mortgage Bankers & Loan Correspo": "sic1=60&sic2=61&sic3=6162&sicl=6162&sich=6162", - "Financial -> Non-depository Credit Inst -> Loan Brokers": "sic1=60&sic2=61&sic3=6163&sicl=6163&sich=6171", - "Financial -> Non-depository Credit Inst -> Finance Lessors": "sic1=60&sic2=61&sic3=6172&sicl=6172&sich=6188", - "Financial -> Non-depository Credit Inst -> Asset-Backed Securities": "sic1=60&sic2=61&sic3=6189&sicl=6189&sich=6198", - "Financial -> Non-depository Credit Inst -> Finance Services": "sic1=60&sic2=61&sic3=6199&sicl=6199&sich=6199", - "Financial -> Security & Commodity Brokers": "sic1=60&sic2=62&sic3=0&sicl=6200&sich=6299", - "Financial -> Security & Commodity Brokers -> Security & Commodity Brokers, De": "sic1=60&sic2=62&sic3=6200&sicl=6200&sich=6210", - "Financial -> Security & Commodity Brokers -> Security Brokers, Dealers & Flot": "sic1=60&sic2=62&sic3=6211&sicl=6211&sich=6220", - "Financial -> Security & Commodity Brokers -> Commodity Contracts Brokers & De": "sic1=60&sic2=62&sic3=6221&sicl=6221&sich=6281", - "Financial -> Security & Commodity Brokers -> Investment Advice": "sic1=60&sic2=62&sic3=6282&sicl=6282&sich=6310", - "Financial -> Insurance Carriers": "sic1=60&sic2=63&sic3=0&sicl=6300&sich=6399", - "Financial -> Insurance Carriers -> Life Insurance": "sic1=60&sic2=63&sic3=6311&sicl=6311&sich=6320", - "Financial -> Insurance Carriers -> Accident & Health Insurance": "sic1=60&sic2=63&sic3=6321&sicl=6321&sich=6323", - "Financial -> Insurance Carriers -> Hospital & Medical Service Plans": "sic1=60&sic2=63&sic3=6324&sicl=6324&sich=6330", - "Financial -> Insurance Carriers -> Fire, Marine & Casualty Insurance": "sic1=60&sic2=63&sic3=6331&sicl=6331&sich=6350", - "Financial -> Insurance Carriers -> Surety Insurance": "sic1=60&sic2=63&sic3=6351&sicl=6351&sich=6360", - "Financial -> Insurance Carriers -> Title Insurance": "sic1=60&sic2=63&sic3=6361&sicl=6361&sich=6398", - "Financial -> Insurance Carriers -> Insurance Carriers": "sic1=60&sic2=63&sic3=6399&sicl=6399&sich=6410", - "Financial -> Insurance Agents": "sic1=60&sic2=64&sicl=6411&sich=6499", - "Financial -> Real Estate": "sic1=60&sic2=65&sic3=0&sicl=6500&sich=6699", - "Financial -> Real Estate -> Real Estate": "sic1=60&sic2=65&sic3=6500&sicl=6500&sich=6509", - "Financial -> Real Estate -> Real Estate Operators (No Developers)": "sic1=60&sic2=65&sic3=6510&sicl=6510&sich=6511", - "Financial -> Real Estate -> Operators of Nonresidential Buildings": "sic1=60&sic2=65&sic3=6512&sicl=6512&sich=6512", - "Financial -> Real Estate -> Operators of Apartment Buildings": "sic1=60&sic2=65&sic3=6513&sicl=6513&sich=6518", - "Financial -> Real Estate -> Lessors of Real Property": "sic1=60&sic2=65&sic3=6519&sicl=6519&sich=6530", - "Financial -> Real Estate -> Real Estate Agents & Managers": "sic1=60&sic2=65&sic3=6531&sicl=6531&sich=6531", - "Financial -> Real Estate -> Real Estate Dealers (For Their O": "sic1=60&sic2=65&sic3=6532&sicl=6532&sich=6551", - "Financial -> Real Estate -> Land Subdividers & Developers (N": "sic1=60&sic2=65&sic3=6552&sicl=6552&sich=6769", - "Financial -> Holding & Investment Offices": "sic1=60&sic2=67&sic3=0&sicl=6700&sich=6999", - "Financial -> Holding & Investment Offices -> Blank Checks": "sic1=60&sic2=67&sic3=6770&sicl=6770&sich=6791", - "Financial -> Holding & Investment Offices -> Oil Royalty Traders": "sic1=60&sic2=67&sic3=6792&sicl=6792&sich=6793", - "Financial -> Holding & Investment Offices -> Patent Owners & Lessors": "sic1=60&sic2=67&sic3=6794&sicl=6794&sich=6794", - "Financial -> Holding & Investment Offices -> Mineral Royalty Traders": "sic1=60&sic2=67&sic3=6795&sicl=6795&sich=6797", - "Financial -> Holding & Investment Offices -> Real Estate Investment Trusts": "sic1=60&sic2=67&sic3=6798&sicl=6798&sich=6798", - "Financial -> Holding & Investment Offices -> Investors": "sic1=60&sic2=67&sic3=6799&sicl=6799&sich=6999", - "Services": "sic1=70&sic2=0&sicl=7000&sich=8999", - "Services -> Hotels": "sic1=70&sic2=70&sic3=0&sicl=7000&sich=7199", - "Services -> Hotels -> Hotels, Rooming Houses, Camps &": "sic1=70&sic2=70&sic3=7000&sicl=7000&sich=7010", - "Services -> Hotels -> Hotels & Motels": "sic1=70&sic2=70&sic3=7011&sicl=7011&sich=7199", - "Services -> Personal Services": "sic1=70&sic2=72&sicl=7200&sich=7309", - "Services -> Business Services": "sic1=70&sic2=73&sic3=0&sicl=7300&sich=7499", - "Services -> Business Services -> Advertising": "sic1=70&sic2=73&sic3=7310&sicl=7310&sich=7310", - "Services -> Business Services -> Advertising Agencies": "sic1=70&sic2=73&sic3=7311&sicl=7311&sich=7319", - "Services -> Business Services -> Consumer Credit Reporting, Colle": "sic1=70&sic2=73&sic3=7320&sicl=7320&sich=7329", - "Services -> Business Services -> Mailing, Reproduction, Commercia": "sic1=70&sic2=73&sic3=7330&sicl=7330&sich=7330", - "Services -> Business Services -> Direct Mail Advertising Services": "sic1=70&sic2=73&sic3=7331&sicl=7331&sich=7339", - "Services -> Business Services -> To Dwellings & Other Buildings": "sic1=70&sic2=73&sic3=7340&sicl=7340&sich=7349", - "Services -> Business Services -> Miscellaneous Equipment Rental &": "sic1=70&sic2=73&sic3=7350&sicl=7350&sich=7358", - "Services -> Business Services -> Equipment Rental & Leasing": "sic1=70&sic2=73&sic3=7359&sicl=7359&sich=7360", - "Services -> Business Services -> Employment Agencies": "sic1=70&sic2=73&sic3=7361&sicl=7361&sich=7362", - "Services -> Business Services -> Help Supply Services": "sic1=70&sic2=73&sic3=7363&sicl=7363&sich=7369", - "Services -> Business Services -> Computer Programming, Data Proce": "sic1=70&sic2=73&sic3=7370&sicl=7370&sich=7370", - "Services -> Business Services -> Computer Programming Services": "sic1=70&sic2=73&sic3=7371&sicl=7371&sich=7371", - "Services -> Business Services -> Prepackaged Software": "sic1=70&sic2=73&sic3=7372&sicl=7372&sich=7372", - "Services -> Business Services -> Computer Integrated Systems Desi": "sic1=70&sic2=73&sic3=7373&sicl=7373&sich=7373", - "Services -> Business Services -> Computer Processing & Data Prepa": "sic1=70&sic2=73&sic3=7374&sicl=7374&sich=7376", - "Services -> Business Services -> Computer Rental & Leasing": "sic1=70&sic2=73&sic3=7377&sicl=7377&sich=7379", - "Services -> Business Services -> Miscellaneous Business Services": "sic1=70&sic2=73&sic3=7380&sicl=7380&sich=7380", - "Services -> Business Services -> Detective, Guard & Armored Car S": "sic1=70&sic2=73&sic3=7381&sicl=7381&sich=7383", - "Services -> Business Services -> Photofinishing Laboratories": "sic1=70&sic2=73&sic3=7384&sicl=7384&sich=7384", - "Services -> Business Services -> Telephone Interconnect Systems": "sic1=70&sic2=73&sic3=7385&sicl=7385&sich=7388", - "Services -> Business Services -> Business Services": "sic1=70&sic2=73&sic3=7389&sicl=7389&sich=7499", - "Services -> Automotive Repair": "sic1=70&sic2=75&sic3=0&sicl=7500&sich=7599", - "Services -> Automotive Repair -> Automotive Repair, Services & Pa": "sic1=70&sic2=75&sic3=7500&sicl=7500&sich=7509", - "Services -> Automotive Repair -> Auto Rental & Leasing (No Driver": "sic1=70&sic2=75&sic3=7510&sicl=7510&sich=7599", - "Services -> Miscellaneous Repair": "sic1=70&sic2=76&sicl=7600&sich=7811", - "Services -> Motion Pictures": "sic1=70&sic2=78&sic3=0&sicl=7800&sich=7899", - "Services -> Motion Pictures -> Motion Picture & Video Tape Prod": "sic1=70&sic2=78&sic3=7812&sicl=7812&sich=7818", - "Services -> Motion Pictures -> Allied to Motion Picture Product": "sic1=70&sic2=78&sic3=7819&sicl=7819&sich=7821", - "Services -> Motion Pictures -> Motion Picture & Video Tape Dist": "sic1=70&sic2=78&sic3=7822&sicl=7822&sich=7828", - "Services -> Motion Pictures -> Allied to Motion Picture Distrib": "sic1=70&sic2=78&sic3=7829&sicl=7829&sich=7829", - "Services -> Motion Pictures -> Motion Picture Theaters": "sic1=70&sic2=78&sic3=7830&sicl=7830&sich=7840", - "Services -> Motion Pictures -> Video Tape Rental": "sic1=70&sic2=78&sic3=7841&sicl=7841&sich=7899", - "Services -> Amusement & Rec Svcs": "sic1=70&sic2=79&sic3=0&sicl=7900&sich=7999", - "Services -> Amusement & Rec Svcs -> Amusement Recreation Services": "sic1=70&sic2=79&sic3=7900&sicl=7900&sich=7947", - "Services -> Amusement & Rec Svcs -> Racing, Including Track Operation": "sic1=70&sic2=79&sic3=7948&sicl=7948&sich=7989", - "Services -> Amusement & Rec Svcs -> Miscellaneous Amusement & Recrea": "sic1=70&sic2=79&sic3=7990&sicl=7990&sich=7996", - "Services -> Amusement & Rec Svcs -> Membership Sports & Recreation C": "sic1=70&sic2=79&sic3=7997&sicl=7997&sich=7999", - "Services -> Health Services": "sic1=70&sic2=80&sic3=0&sicl=8000&sich=8099", - "Services -> Health Services -> Health Services": "sic1=70&sic2=80&sic3=8000&sicl=8000&sich=8010", - "Services -> Health Services -> Offices & Clinics of Doctors of": "sic1=70&sic2=80&sic3=8011&sicl=8011&sich=8049", - "Services -> Health Services -> Nursing & Personal Care Facilities": "sic1=70&sic2=80&sic3=8050&sicl=8050&sich=8050", - "Services -> Health Services -> Skilled Nursing Care Facilities": "sic1=70&sic2=80&sic3=8051&sicl=8051&sich=8059", - "Services -> Health Services -> Hospitals": "sic1=70&sic2=80&sic3=8060&sicl=8060&sich=8061", - "Services -> Health Services -> General Medical & Surgical Hospitals": "sic1=70&sic2=80&sic3=8062&sicl=8062&sich=8070", - "Services -> Health Services -> Medical Laboratories": "sic1=70&sic2=80&sic3=8071&sicl=8071&sich=8081", - "Services -> Health Services -> Home Health Care Services": "sic1=70&sic2=80&sic3=8082&sicl=8082&sich=8089", - "Services -> Health Services -> Misc Health & Allied Services": "sic1=70&sic2=80&sic3=8090&sicl=8090&sich=8092", - "Services -> Health Services -> Specialty Outpatient Facilities": "sic1=70&sic2=80&sic3=8093&sicl=8093&sich=8110", - "Services -> Legal Services": "sic1=70&sic2=81&sicl=8111&sich=8199", - "Services -> Educational Services": "sic1=70&sic2=82&sicl=8200&sich=8299", - "Services -> Social Services": "sic1=70&sic2=83&sic3=0&sicl=8300&sich=8399", - "Services -> Social Services -> Social Services": "sic1=70&sic2=83&sic3=8300&sicl=8300&sich=8350", - "Services -> Social Services -> Child Day Care Services": "sic1=70&sic2=83&sic3=8351&sicl=8351&sich=8599", - "Services -> Museums": "sic1=70&sic2=84&sicl=8400&sich=8599", - "Services -> Membership Organizations": "sic1=70&sic2=86&sicl=8600&sich=8699", - "Services -> Engr, Acct, Rsrch, Mgmt Svcs": "sic1=70&sic2=87&sic3=0&sicl=8700&sich=8899", - "Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Engineering, Accounting, Research": "sic1=70&sic2=87&sic3=8700&sicl=8700&sich=8710", - "Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Engineering Services": "sic1=70&sic2=87&sic3=8711&sicl=8711&sich=8730", - "Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Commercial Physical & Biological": "sic1=70&sic2=87&sic3=8731&sicl=8731&sich=8733", - "Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Testing Laboratories": "sic1=70&sic2=87&sic3=8734&sicl=8734&sich=8740", - "Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Management Services": "sic1=70&sic2=87&sic3=8741&sicl=8741&sich=8741", - "Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Management Consulting Services": "sic1=70&sic2=87&sic3=8742&sicl=8742&sich=8743", - "Services -> Engr, Acct, Rsrch, Mgmt Svcs -> Facilities Support Management Se": "sic1=70&sic2=87&sic3=8744&sicl=8744&sich=8879", - "Services -> Miscellaneous Services": "sic1=70&sic2=89&sicl=8900&sich=9720", - "Closed-End Funds": "sic1=0&sicl=0&sich=99", -} -d_open_insider = { - "lcb": "latest-cluster-buys", - "lpsb": "latest-penny-stock-buys", - "lit": "latest-insider-trading", - "lip": "insider-purchases", - "blip": "latest-insider-purchases-25k", - "blop": "latest-officer-purchases-25k", - "blcp": "latest-ceo-cfo-purchases-25k", - "lis": "insider-sales", - "blis": "latest-insider-sales-100k", - "blos": "latest-officer-sales-100k", - "blcs": "latest-ceo-cfo-sales-100k", - "topt": "top-officer-purchases-of-the-day", - "toppw": "top-officer-purchases-of-the-week", - "toppm": "top-officer-purchases-of-the-month", - "tipt": "top-insider-purchases-of-the-day", - "tippw": "top-insider-purchases-of-the-week", - "tippm": "top-insider-purchases-of-the-month", - "tist": "top-insider-sales-of-the-day", - "tispw": "top-insider-sales-of-the-week", - "tispm": "top-insider-sales-of-the-month", -} - - -@log_start_end(log=logger) -def check_valid_range( - category: str, field: str, val: str, min_range: int, max_range: int -) -> str: - """Check valid range of data being used - - Parameters - ---------- - category : str - category of open insider screener - field : str - field from category of open insider screener - val : str - value's field of category from open insider screener - min_range : int - min value to allow - max_range : int - max value to allow - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = "" - if val: - try: - ival = int(val) - - if ival < min_range or ival > max_range: - error = ( - f"Invalid {category}.{field} '{str(ival)}'. " - f"Choose value between {min_range} and {max_range}, inclusive." - ) - logging.exception(error) - except ValueError: - error = f"Invalid {category}.{field} '{val}'. Not a valid integer.\n" - logging.exception(error) - - return error - - -@log_start_end(log=logger) -def check_dates(d_date: Dict) -> str: - """Check valid dates - - Parameters - ---------- - d_date : Dict - dictionary with dates from open insider - - Returns - ------- - error : str - error message. If empty, no error. - """ - possible_dates = [ - "All dates", - "Custom", - "Latest Day", - "Last 3 days", - "Last 1 week", - "Last 2 weeks", - "Last 1 month", - "Last 2 months", - "Last 3 months", - "Last 6 months", - "Last 1 year", - "Last 2 years", - "Last 4 years", - ] - - error = "" - - if d_date["FilingDate"] not in possible_dates: - error += ( - f"Invalid FilingDate '{d_date['FilingDate']}'. " - f"Choose one of the following options: {', '.join(possible_dates)}.\n" - ) - else: - if d_date["FilingDate"] == "Custom": - try: - datetime.strptime(d_date["FilingDateFrom"], "%Y-%m-%d") - except ValueError: - error += f"Invalid FilingDateFrom '{d_date['FilingDateFrom']}' (format: dd/mm/yyyy).\n" - try: - datetime.strptime(d_date["FilingDateTo"], "%Y-%m-%d") - except ValueError: - error += f"Invalid FilingDateTo '{d_date['FilingDateTo']}' (format: dd/mm/yyyy).\n" - - if d_date["TradingDate"] not in possible_dates: - error += ( - f"Invalid TradingDate '{d_date['TradingDate']}'. " - f"Choose one of the following options: {', '.join(possible_dates)}.\n" - ) - else: - if d_date["TradingDate"] == "Custom": - try: - datetime.strptime(d_date["TradingDateFrom"], "%Y-%m-%d") - except ValueError: - error += f"Invalid TradingDateFrom '{d_date['TradingDateFrom']}' (format: dd/mm/yyyy).\n" - - try: - datetime.strptime(d_date["TradingDateTo"], "%Y-%m-%d") - except ValueError: - error += f"Invalid TradingDateTo '{d_date['TradingDateTo']}' (format: dd/mm/yyyy).\n" - if error: - logging.exception(error) - return error - - -@log_start_end(log=logger) -def check_valid_multiple(category: str, field: str, val: str, multiple: int) -> str: - """Check valid value being a multiple - - Parameters - ---------- - category : str - category of open insider screener - field : str - field from category of open insider screener - val : str - value's field of category from open insider screener - multiple : int - value must be multiple of this number - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = "" - if val: - try: - ival = int(val) - - if ival % multiple != 0: - error = f"Invalid {category}.{field} '{str(ival)}'. Choose value multiple of {str(multiple)}.\n" - logging.exception(error) - except ValueError: - error = f"Invalid {category}.{field} '{val}'. Not a valid integer.\n" - logging.exception(error) - return error - - -@log_start_end(log=logger) -def check_boolean_list(category: str, d_data: Dict, l_fields_to_check: List) -> str: - """Check list of fields being bools - - Parameters - ---------- - category : str - category of open insider screener - d_data : Dict - data dictionary - l_fields_to_check : List[str] - list of fields from data dictionary to check if they are bool - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = "" - - for field in l_fields_to_check: - if d_data[field] not in ["true", ""]: - error += f"Invalid {category}.{field} '{d_data[field]}'. Needs to be either 'true' or '' (empty).\n" - - return error - - -@log_start_end(log=logger) -def check_in_list( - category: str, field: str, val: int, l_possible_vals: List[str] -) -> str: - """Check value being in possible list - - Parameters - ---------- - category : str - category of open insider screener - field : str - field from category of open insider screener - val : str - value's field of category from open insider screener - l_possible_vals : List[str] - list of possible values that should be allowed - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = "" - - if val: - if val not in l_possible_vals: - error += ( - f"Invalid {category}.{field} '{val}'. " - f"Choose one of the following options: {', '.join(l_possible_vals)}.\n" - ) - - return error - - -@log_start_end(log=logger) -def check_int_in_list( - category: str, field: str, val: str, l_possible_vals: List[int] -) -> str: - """Check int value being in possible list - - Parameters - ---------- - category : str - category of open insider screener - field : str - field from category of open insider screener - val : str - value's field of category from open insider screener - l_possible_vals : List[str] - list of possible values that should be allowed - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = "" - - if val: - try: - ival = int(val) - - if ival not in l_possible_vals: - error += ( - f"Invalid {category}.{field} '{val}'. " - f"Choose one of the following options: {', '.join([str(x) for x in l_possible_vals])}.\n" - ) - logging.exception(error) - except ValueError: - error += f"Invalid {category}.{field} '{val}'. Not a valid integer.\n" - logging.exception(error) - return error - - -@log_start_end(log=logger) -def check_open_insider_general(d_general) -> str: - """Check valid open insider general - - Parameters - ---------- - d_date : Dict - dictionary of general - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = check_valid_range( - "General", "SharePriceMin", d_general["SharePriceMin"], 0, 9999 - ) - - error += check_valid_range( - "General", "SharePriceMax", d_general["SharePriceMax"], 0, 9999 - ) - - error += check_valid_range( - "General", "LiquidityMinM", d_general["LiquidityMinM"], 0, 9999 - ) - - error += check_valid_range( - "General", "LiquidityMaxM", d_general["LiquidityMaxM"], 0, 9999 - ) - - return error - - -@log_start_end(log=logger) -def check_open_insider_date(d_date: Dict) -> str: - """Check valid open insider date - - Parameters - ---------- - d_date : Dict - dictionary of date - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = check_dates(d_date) - error += check_valid_range( - "Date", "FilingDelayMin", d_date["FilingDelayMin"], 0, 99 - ) - error += check_valid_range( - "Date", "FilingDelayMax", d_date["FilingDelayMax"], 0, 99 - ) - error += check_valid_range("Date", "NDaysAgo", d_date["NDaysAgo"], 0, 99) - - return error - - -@log_start_end(log=logger) -def check_open_insider_transaction_filing(d_transaction_filing: Dict) -> str: - """Check valid open insider transaction filing - - Parameters - ---------- - d_transaction_filing : Dict - dictionary of transaction filing - - Returns - ------- - error : str - error message. If empty, no error. - """ - filings_to_check = [ - "P_Purchase", - "S_Sale", - "A_Grant", - "D_SaleToLoss", - "G_Gift", - "NoDeriv", - "F_Tax", - "M_OptionEx", - "X_OptionEx", - "C_CnvDeriv", - "W_Inherited", - "MultipleDays", - ] - - error = check_boolean_list( - "TransactionFiling", d_transaction_filing, filings_to_check - ) - error += check_valid_multiple( - "TransactionFiling", "TradedMinK", d_transaction_filing["TradedMinK"], 5 - ) - error += check_valid_multiple( - "TransactionFiling", "TradedMaxK", d_transaction_filing["TradedMaxK"], 5 - ) - error += check_valid_range( - "TransactionFiling", - "OwnChangeMinPct", - d_transaction_filing["OwnChangeMinPct"], - 0, - 99, - ) - error += check_valid_range( - "TransactionFiling", - "OwnChangeMaxPct", - d_transaction_filing["OwnChangeMaxPct"], - 0, - 99, - ) - - return error - - -@log_start_end(log=logger) -def check_open_insider_industry(d_industry: Dict) -> str: - """Check valid open insider industry - - Parameters - ---------- - d_industry : Dict - dictionary of industry - - Returns - ------- - error : str - error message. If empty, no error. - """ - industry = d_industry["SectorSubsectorIndustry"] - if industry not in d_SectorSubsectorIndustry: - return f"Invalid Industry.SectorSubsectorIndustry '{industry}'. See comments at the end of template.ini file.\n" - - return "" - - -@log_start_end(log=logger) -def check_open_insider_insider_title(d_insider_title: Dict) -> str: - """Check valid open insider title - - Parameters - ---------- - d_insider_title : Dict - dictionary of title - - Returns - ------- - error : str - error message. If empty, no error. - """ - titles_to_check = [ - "COB", - "CEO", - "Pres", - "COO", - "CFO", - "GC", - "VP", - "Officer", - "Director", - "10PctOwn", - "Other", - ] - - error = check_boolean_list("InsiderTitle", d_insider_title, titles_to_check) - - return error - - -@log_start_end(log=logger) -def check_open_insider_others(d_others: Dict) -> str: - """Check valid open insider others - - Parameters - ---------- - d_others : Dict - dictionary of others - - Returns - ------- - error : str - error message. If empty, no error. - """ - possible_groupby = ["Filing", "Company"] - error = check_in_list("Others", "GroupBy", d_others["GroupBy"], possible_groupby) - - possible_sortby = ["Filing Date", "Trade Date", "Ticker Symbol", "Trade Value"] - error = check_in_list("Others", "SortBy", d_others["SortBy"], possible_sortby) - - possible_maxresults = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] - error += check_int_in_list( - "Others", "MaxResults", d_others["MaxResults"], possible_maxresults - ) - - error += check_valid_range("Others", "Page", d_others["Page"], 0, 99) - - return error - - -@log_start_end(log=logger) -def check_open_insider_company_totals(d_company_totals: Dict) -> str: - """Check valid open insider company totals - - Parameters - ---------- - d_company_totals : Dict - dictionary of company totals - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = check_valid_range( - "CompanyTotals", "FilingsMin", d_company_totals["FilingsMin"], 0, 99 - ) - - error += check_valid_range( - "CompanyTotals", "FilingsMax", d_company_totals["FilingsMax"], 0, 99 - ) - - error += check_valid_range( - "CompanyTotals", "InsidersMin", d_company_totals["InsidersMin"], 0, 9 - ) - - error += check_valid_range( - "CompanyTotals", "InsidersMax", d_company_totals["InsidersMax"], 0, 9 - ) - - error += check_valid_range( - "CompanyTotals", "OfficersMin", d_company_totals["OfficersMin"], 0, 9 - ) - - error += check_valid_range( - "CompanyTotals", "OfficersMax", d_company_totals["OfficersMax"], 0, 9 - ) - - error += check_valid_multiple( - "CompanyTotals", "TradedMinK", d_company_totals["TradedMinK"], 5 - ) - - error += check_valid_multiple( - "CompanyTotals", "TradedMaxK", d_company_totals["TradedMaxK"], 5 - ) - - error += check_valid_range( - "CompanyTotals", "OwnChangeMinPct", d_company_totals["OwnChangeMinPct"], 0, 99 - ) - - error += check_valid_range( - "CompanyTotals", "OwnChangeMaxPct", d_company_totals["OwnChangeMaxPct"], 0, 99 - ) - - return error - - -@log_start_end(log=logger) -def check_open_insider_screener( - d_general: Dict, - d_date: Dict, - d_transaction_filing: Dict, - d_industry: Dict, - d_insider_title: Dict, - d_others: Dict, - d_company_totals: Dict, -) -> str: - """Check valid open insider screener - - Parameters - ---------- - d_general : Dict - dictionary of general - d_date : Dict - dictionary of date - d_transaction_filing : Dict - dictionary of transaction filing - d_industry : Dict - dictionary of industry - d_insider_title : Dict - dictionary of insider title - d_others : Dict - dictionary of others - d_company_totals : Dict - dictionary of company totals - - Returns - ------- - error : str - error message. If empty, no error. - """ - error = check_open_insider_general(d_general) - error += check_open_insider_date(d_date) - error += check_open_insider_transaction_filing(d_transaction_filing) - error += check_open_insider_industry(d_industry) - error += check_open_insider_insider_title(d_insider_title) - error += check_open_insider_others(d_others) - if d_others["GroupBy"] == "Company": - error += check_open_insider_company_totals(d_company_totals) - - return error - - -@log_start_end(log=logger) -def get_preset_choices() -> Dict: - """ - Return a dict containing keys as name of preset and - filepath as value - """ - - PRESETS_PATH = ( - get_current_user().preferences.USER_PRESETS_DIRECTORY / "stocks" / "insider" - ) - PRESETS_PATH_DEFAULT = MISCELLANEOUS_DIRECTORY / "stocks" / "insider" - - preset_choices = {} - - if PRESETS_PATH.exists(): - preset_choices.update( - { - filepath.name.strip(".ini"): filepath - for filepath in PRESETS_PATH.iterdir() - if filepath.suffix == ".ini" - } - ) - - if PRESETS_PATH_DEFAULT.exists(): - preset_choices.update( - { - filepath.name.strip(".ini"): filepath - for filepath in PRESETS_PATH_DEFAULT.iterdir() - if filepath.suffix == ".ini" - } - ) - - return preset_choices - - -@log_start_end(log=logger) -def get_open_insider_link(preset_loaded: str) -> str: - """Get open insider link - - Parameters - ---------- - preset_loaded: str - Loaded preset filter - - Returns - ------- - link : str - open insider filtered link - """ - preset = configparser.RawConfigParser() - preset.optionxform = str # type: ignore - choices = get_preset_choices() - if preset_loaded not in choices: - console.print("[red]Could not find the link[/red]\n") - return "" - preset.read(choices[preset_loaded]) - - d_general = dict(preset["General"]) - d_date = dict(preset["Date"]) - d_transaction_filing = dict(preset["TransactionFiling"]) - d_industry = dict(preset["Industry"]) - d_insider_title = dict(preset["InsiderTitle"]) - d_others = dict(preset["Others"]) - d_company_totals = dict(preset["CompanyTotals"]) - - result = check_open_insider_screener( - d_general, - d_date, - d_transaction_filing, - d_industry, - d_insider_title, - d_others, - d_company_totals, - ) - - if result: - console.print(result) - return "" - - d_FilingTradingDate = { - "All dates": "0", - "Custom": "-1", - "Latest Day": "1", - "Last 3 days": "3", - "Last 1 week": "7", - "Last 2 weeks": "14", - "Last 1 month": "30", - "Last 2 months": "60", - "Last 3 months": "90", - "Last 6 months": "180", - "Last 1 year": "365", - "Last 2 years": "730", - "Last 4 years": "1461", - } - d_GroupBy = { - "Filing": "0", - "Company": "2", - } - d_SortBy = { - "Filing Date": 0, - "Trade Date": 1, - "Ticker Symbol": 2, - "Trade Value": 8, - } - - link = "http://openinsider.com/screener?" - - # General - link += f"s={d_general['Tickers'].replace(' ', '+')}&" - link += f"o={d_general['Insider'].replace(' ', '+')}&" - link += f"pl={d_general['SharePriceMin']}&" - link += f"ph={d_general['SharePriceMax']}&" - link += f"ll={d_general['LiquidityMinM']}&" - link += f"lh={d_general['LiquidityMaxM']}&" - - # Date - link += f"fd={d_FilingTradingDate[d_date['FilingDate']]}&" - if d_date["FilingDate"] == "Custom": - link += f"fdr={d_date['FilingDateFrom'].replace('/','%2F')}-{d_date['FilingDateTo'].replace('/','%2F')}&" - else: - link += "fdr=&" - link += f"td={d_FilingTradingDate[d_date['TradingDate']]}&" - if d_date["TradingDate"] == "Custom": - link += f"tdr={d_date['TradingDateFrom'].replace('/','%2F')}-{d_date['TradingDateTo'].replace('/','%2F')}&" - else: - link += "tdr=&" - link += f"fdlyl={d_date['FilingDelayMin']}&" - link += f"fdlyh={d_date['FilingDelayMax']}&" - link += f"daysago={d_date['NDaysAgo']}&" - - # Transaction Filing - link += f"xp={'1' if d_transaction_filing['P_Purchase'] == 'true' else ''}&" - link += f"xs={'1' if d_transaction_filing['S_Sale'] == 'true' else ''}&" - if d_transaction_filing["A_Grant"] == "true": - link += "xa=1&" - if d_transaction_filing["D_SaleToLoss"] == "true": - link += "xd=1&" - if d_transaction_filing["G_Gift"] == "true": - link += "xg=1&" - if d_transaction_filing["NoDeriv"] == "true": - link += "excludeDerivRelated=1&" - if d_transaction_filing["F_Tax"] == "true": - link += "xf=1&" - if d_transaction_filing["M_OptionEx"] == "true": - link += "xm=1&" - if d_transaction_filing["X_OptionEx"] == "true": - link += "xx=1&" - if d_transaction_filing["C_CnvDeriv"] == "true": - link += "xc=1&" - if d_transaction_filing["W_Inherited"] == "true": - link += "xw=1&" - if d_transaction_filing["MultipleDays"] == "true": - link += "tmult=1&" - link += f"vl={d_transaction_filing['TradedMinK']}&" - link += f"vh={d_transaction_filing['TradedMaxK']}&" - link += f"ocl={d_transaction_filing['OwnChangeMinPct']}&" - link += f"och={d_transaction_filing['OwnChangeMaxPct']}&" - - # Industry - link += f"{d_SectorSubsectorIndustry[d_industry['SectorSubsectorIndustry']]}&" - - # Insider Title - if d_insider_title["Officer"] == "true": - link += "isofficer=1&" - if d_insider_title["COB"] == "true": - link += "iscob=1&" - if d_insider_title["CEO"] == "true": - link += "isceo=1&" - if d_insider_title["Pres"] == "true": - link += "ispres=1&" - if d_insider_title["COO"] == "true": - link += "iscoo=1&" - if d_insider_title["CFO"] == "true": - link += "iscfo=1&" - if d_insider_title["GC"] == "true": - link += "isgc=1&" - if d_insider_title["VP"] == "true": - link += "isvp=1&" - if d_insider_title["Director"] == "true": - link += "isdirector=1&" - if d_insider_title["10PctOwn"] == "true": - link += "istenpercent=1&" - if d_insider_title["Other"] == "true": - link += "isother=1&" - - # Others - link += f"grp={d_GroupBy[d_others['GroupBy']]}&" - - # Company Totals - link += f"nfl={d_company_totals['FilingsMin']}&" - link += f"nfh={d_company_totals['FilingsMax']}&" - link += f"nil={d_company_totals['InsidersMin']}&" - link += f"nih={d_company_totals['InsidersMax']}&" - link += f"nol={d_company_totals['OfficersMin']}&" - link += f"noh={d_company_totals['OfficersMax']}&" - link += f"v2l={d_company_totals['TradedMinK']}&" - link += f"v2h={d_company_totals['TradedMaxK']}&" - link += f"oc2l={d_company_totals['OwnChangeMinPct']}&" - link += f"oc2h={d_company_totals['OwnChangeMaxPct']}&" - - # Others - link += f"sortcol={d_SortBy[d_others['SortBy']]}&" - link += f"cnt={d_others['MaxResults']}&" - link += f"page={d_others['Page']}" - - return link - - -@log_start_end(log=logger) -def get_open_insider_data(url: str, has_company_name: bool) -> pd.DataFrame: - """Get open insider link - - Parameters - ---------- - url: str - open insider link with filters to retrieve data from - has_company_name: bool - contains company name columns - - Returns - ------- - data : pd.DataFrame - open insider filtered data - """ - text_soup_open_insider = BeautifulSoup(request(url).text, "lxml") - - if len(text_soup_open_insider.find_all("tbody")) == 0: - console.print("No insider trading found.") - return pd.DataFrame() - - l_filing_link = [] - l_ticker_link = [] - l_insider_link = [] - - idx = 0 - for val in text_soup_open_insider.find_all("tbody")[1].find_all("a"): - if idx == 0: - l_filing_link.append(val["href"]) - idx += 1 - elif idx == 1: - l_ticker_link.append("http://openinsider.com" + val["href"]) - idx += 1 - elif idx == 2 and has_company_name: - idx += 1 - else: - l_insider_link.append("http://openinsider.com" + val["href"]) - idx = 0 - - l_X = [] - l_filing_date = [] - l_trading_date = [] - l_ticker = [] - l_company = [] - l_insider = [] - l_title = [] - l_trade_type = [] - l_price = [] - l_quantity = [] - l_owned = [] - l_delta_own = [] - l_value = [] - - idx = 0 - for val in text_soup_open_insider.find_all("tbody")[1].find_all("td"): - if idx == 0: - l_X.append(val.text) - idx += 1 - elif idx == 1: - l_filing_date.append(val.text) - idx += 1 - elif idx == 2: - l_trading_date.append(val.text) - idx += 1 - elif idx == 3: - l_ticker.append(val.text.strip()) - idx += 1 - elif idx == 4 and has_company_name: - l_company.append(val.text) - idx += 1 - elif idx == (5 - int(not has_company_name)): - l_insider.append(val.text) - idx += 1 - elif idx == (6 - int(not has_company_name)): - l_title.append(val.text) - idx += 1 - elif idx == (7 - int(not has_company_name)): - l_trade_type.append(val.text) - idx += 1 - elif idx == (8 - int(not has_company_name)): - l_price.append(val.text) - idx += 1 - elif idx == (9 - int(not has_company_name)): - l_quantity.append(val.text) - idx += 1 - elif idx == (10 - int(not has_company_name)): - l_owned.append(val.text) - idx += 1 - elif idx == (11 - int(not has_company_name)): - l_delta_own.append(val.text) - idx += 1 - elif idx == (12 - int(not has_company_name)): - l_value.append(val.text) - idx += 1 - elif idx < (16 - int(not has_company_name)): - idx += 1 - else: - idx = 0 - - d_open_insider_filtered = { - "X": l_X, - "Filing Date": l_filing_date, - "Trading Date": l_trading_date, - "Ticker": l_ticker, - "Insider": l_insider, - "Title": l_title, - "Trade Type": l_trade_type, - "Price": l_price, - "Quantity": l_quantity, - "Owned": l_owned, - "Delta Own": l_delta_own, - "Value": l_value, - "Filing Link": l_filing_link, - "Ticker Link": l_ticker_link, - "Insider Link": l_insider_link, - } - if has_company_name: - d_open_insider_filtered["Company"] = l_company - - return pd.DataFrame(d_open_insider_filtered) - - -@log_start_end(log=logger) -def get_insider_types() -> Dict: - """Get insider types available for insider data - - Returns: - Dict: Dictionary with insider types and respective description - """ - return d_open_insider - - -@log_start_end(log=logger) -def get_print_insider_data(type_insider: str = "lcb"): - """Print insider data - - Parameters - ---------- - type_insider: str - Insider type of data. Available types can be accessed through get_insider_types(). - - Returns - ------- - data : pd.DataFrame - Open insider filtered data - """ - response = request( - f"http://openinsider.com/{d_open_insider[type_insider]}", - headers={"User-Agent": "Mozilla/5.0"}, - ) - - df = pd.read_html(response.text)[-3] - remove_cols = ["1d", "1w", "1m", "6m"] - - if set(remove_cols).issubset(set(df.columns)): - df = df.drop(columns=remove_cols).fillna("-") - else: - console.print("No data found for the given insider type.", style="red") - df = pd.DataFrame() - - if df.empty: - return pd.DataFrame() - - columns = [ - "X", - "Filing Date", - "Trade Date", - "Ticker", - "Company Name", - "Industry" if type_insider == "lcb" else "Insider Name", - "Title", - "Trade Type", - "Price", - "Qty", - "Owned", - "Diff Own", - "Value", - ] - - if df.shape[1] == 13: - df.columns = columns - else: - df.columns = columns[1:] - df["Filing Date"] = df["Filing Date"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=10)) if isinstance(x, str) else x - ) - df["Company Name"] = df["Company Name"].apply( - lambda x: " ".join(textwrap.wrap(x, width=20)) if isinstance(x, str) else x - ) - df["Title"] = df["Title"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=10)) if isinstance(x, str) else x - ) - if type_insider == "lcb": - df["Industry"] = df["Industry"].apply( - lambda x: " ".join(textwrap.wrap(x, width=20)) if isinstance(x, str) else x - ) - else: - df["Insider Name"] = df["Insider Name"].apply( - lambda x: "\n".join(textwrap.wrap(x, width=20)) if isinstance(x, str) else x - ) - - return df diff --git a/openbb_terminal/stocks/insider/openinsider_view.py b/openbb_terminal/stocks/insider/openinsider_view.py deleted file mode 100644 index e86cf2f7b09..00000000000 --- a/openbb_terminal/stocks/insider/openinsider_view.py +++ /dev/null @@ -1,272 +0,0 @@ -import itertools -import logging -import os -from typing import List, Optional - -import numpy as np -import pandas as pd - -from openbb_terminal import rich_config -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - patch_pandas_text_adjustment, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.insider.openinsider_model import ( - get_open_insider_data, - get_open_insider_link, - get_print_insider_data, -) - -logger = logging.getLogger(__name__) - -d_notes = { - "A": "A: Amended filing", - "D": "D: Derivative transaction in filing (usually option exercise)", - "E": "E: Error detected in filing", - "M": "M: Multiple transactions in filing; earliest reported transaction date & weighted average transaction price", -} - -d_trade_types = { - "S - Sale": "[red]S - Sale: Sale of securities on an exchange or to another person[/red]", - "S - Sale+OE": "[yellow]S - Sale+OE: Sale of securities " - "on an exchange or to another person (after option exercise)[/yellow]", - "F - Tax": "[magenta]F - Tax: Payment of exercise price or " - "tax liability using portion of securities received from the company[/magenta]", - "P - Purchase": "[green]P - Purchase: Purchase of securities on " - "an exchange or from another person[/green]", -} - - -def lambda_red_highlight(values) -> List[str]: - """Red highlight - - Parameters - ---------- - values : List[str] - dataframe values to color - - Returns - ------- - List[str] - colored dataframes values - """ - return [f"[red]{val}[/red]" for val in values] - - -def lambda_yellow_highlight(values) -> List[str]: - """Yellow highlight - - Parameters - ---------- - values : List[str] - dataframe values to color - - Returns - ------- - List[str] - colored dataframes values - """ - return [f"[yellow]{val}[/yellow]" for val in values] - - -def lambda_magenta_highlight(values): - """Magenta highlight - - Parameters - ---------- - values : List[str] - dataframe values to color - - Returns - ------- - List[str] - colored dataframes values - """ - return [f"[magenta]{val}[/magenta]" for val in values] - - -def lambda_green_highlight(values): - """Green highlight - - Parameters - ---------- - values : List[str] - dataframe values to color - - Returns - ------- - List[str] - colored dataframes values - """ - return [f"[green]{val}[/green]" for val in values] - - -@log_start_end(log=logger) -def print_insider_data( - type_insider: str = "lcb", - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, -): - """Print insider data - - Parameters - ---------- - type_insider: str - Insider type of data. Available types can be accessed through get_insider_types(). - limit: int - Limit of data rows to display - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Export data format - """ - df = get_print_insider_data(type_insider) - - if not df.empty: - print_rich_table( - df, - headers=[x.title() for x in df.columns], - show_index=False, - title="Insider Data", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - type_insider, - df, - sheet_name, - ) - - if df.shape[1] == 13: - l_chars = [list(chars) for chars in df["X"].values if chars != "-"] - l_uchars = np.unique(list(itertools.chain(*l_chars))) - - for char in l_uchars: - console.print(d_notes[char]) - - -@log_start_end(log=logger) -def print_insider_filter( - preset: str, - symbol: str, - limit: int = 10, - links: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Print insider filter based on loaded preset. [Source: OpenInsider] - - Parameters - ---------- - preset : str - Loaded preset filter - symbol : str - Stock ticker symbol - limit : int - Limit of rows of data to display - links : bool - Flag to show hyperlinks - export : str - Format to export data - """ - link = ( - f"http://openinsider.com/screener?s={symbol}" - if symbol - else get_open_insider_link(preset) - ) - - if not link: - return - - df_insider = get_open_insider_data(link, has_company_name=bool(not symbol)) - df_insider_orig = df_insider.copy() - - if df_insider.empty: - console.print("No insider data found\n") - return - - if links: - df_insider = df_insider[ - ["Ticker Link", "Insider Link", "Filing Link", "Filing Date"] - ].head(limit) - else: - df_insider = df_insider.drop( - columns=["Filing Link", "Ticker Link", "Insider Link"] - ).head(limit) - - if ( - rich_config.USE_COLOR - and not links - and not get_current_user().preferences.USE_INTERACTIVE_DF - ): - new_df_insider = df_insider.copy() - if not new_df_insider[new_df_insider["Trade Type"] == "S - Sale"].empty: - new_df_insider[new_df_insider["Trade Type"] == "S - Sale"] = new_df_insider[ - new_df_insider["Trade Type"] == "S - Sale" - ].apply(lambda_red_highlight) - if not new_df_insider[new_df_insider["Trade Type"] == "S - Sale+OE"].empty: - new_df_insider[new_df_insider["Trade Type"] == "S - Sale+OE"] = ( - new_df_insider[new_df_insider["Trade Type"] == "S - Sale+OE"].apply( - lambda_yellow_highlight - ) - ) - if not new_df_insider[new_df_insider["Trade Type"] == "F - Tax"].empty: - new_df_insider[new_df_insider["Trade Type"] == "F - Tax"] = new_df_insider[ - new_df_insider["Trade Type"] == "F - Tax" - ].apply(lambda_magenta_highlight) - if not new_df_insider[new_df_insider["Trade Type"] == "P - Purchase"].empty: - new_df_insider[new_df_insider["Trade Type"] == "P - Purchase"] = ( - new_df_insider[new_df_insider["Trade Type"] == "P - Purchase"].apply( - lambda_green_highlight - ) - ) - - patch_pandas_text_adjustment() - pd.set_option("display.max_colwidth", 0) - pd.set_option("display.max_rows", None) - - # needs to be done because table is too large :( - new_df_insider = new_df_insider.drop(columns=["Filing Date", "Trade Type"]) - - else: - # needs to be done because table is too large :( - new_df_insider = df_insider.drop(columns=["Filing Date"], axis=1) - - print_rich_table( - new_df_insider, - headers=[x.title() for x in new_df_insider.columns], - title="Insider filtered", - export=bool(export), - ) - - if not links: - l_chars = [list(chars) for chars in df_insider_orig["X"].values] - l_uchars = np.unique(list(itertools.chain(*l_chars))) - - for char in l_uchars: - console.print(d_notes[char]) - - l_tradetype = df_insider_orig["Trade Type"].values - l_utradetype = np.unique(l_tradetype) - - for tradetype in l_utradetype: - console.print(d_trade_types[tradetype]) - - if export: - cmd = "stats" if symbol else "filter" - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - cmd, - df_insider, - sheet_name, - ) diff --git a/openbb_terminal/stocks/insider/sdk_helper.py b/openbb_terminal/stocks/insider/sdk_helper.py deleted file mode 100644 index 65ad7598dfd..00000000000 --- a/openbb_terminal/stocks/insider/sdk_helper.py +++ /dev/null @@ -1,230 +0,0 @@ -"""Insider SDK Helpers""" - -__docformat__ = "numpy" - -import pandas as pd - -from openbb_terminal.stocks.insider import openinsider_model - - -def stats(symbol: str) -> pd.DataFrame: - """Get OpenInsider stats for ticker - - Parameters - ---------- - symbol : str - Ticker to get insider stats for - - Returns - ------- - pd.DataFrame - DataFrame of insider stats - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.stats("AAPL") - """ - link = f"http://openinsider.com/screener?s={symbol}" - return openinsider_model.get_open_insider_data(link, has_company_name=False) - - -def insider_filter(preset: str) -> pd.DataFrame: - """GEt insider trades based on preset filter - - Parameters - ---------- - preset : str - Name of preset filter - - Returns - ------- - pd.DataFrame - DataFrame of filtered insider data - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - - In order to filter, we pass one of the predefined .ini filters from OpenBBUserData/presets/stocks/insider - >>> filter = "Gold-Silver" - >>> insider_trades = openbb.stocks.ins.filter(filter) - """ - url = openinsider_model.get_open_insider_link(preset) - return openinsider_model.get_open_insider_data(url, has_company_name=True) - - -def lcb() -> pd.DataFrame: - """Get latest cluster buys - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.lcb() - """ - return openinsider_model.get_print_insider_data("lcb") - - -def lpsb() -> pd.DataFrame: - """Get latest penny stock buys - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.lpsb() - """ - return openinsider_model.get_print_insider_data("lpsb") - - -def lit() -> pd.DataFrame: - """Get latest insider trades - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.lit() - """ - return openinsider_model.get_print_insider_data("lit") - - -def lip() -> pd.DataFrame: - """Get latest insider purchases - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.lip() - """ - return openinsider_model.get_print_insider_data("lip") - - -def blip() -> pd.DataFrame: - """Get latest insider purchases > 25k - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.blip() - """ - return openinsider_model.get_print_insider_data("blip") - - -def blop() -> pd.DataFrame: - """Get latest officer purchases > 25k - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.blop() - """ - return openinsider_model.get_print_insider_data("blop") - - -def blcp() -> pd.DataFrame: - """Get latest CEO/CFO purchases > 25k - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.blcp() - """ - return openinsider_model.get_print_insider_data("blcp") - - -def lis() -> pd.DataFrame: - """Get latest insider sales - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.lis() - """ - return openinsider_model.get_print_insider_data("lis") - - -def blis() -> pd.DataFrame: - """Get latest insider sales > 100k - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.blis() - """ - return openinsider_model.get_print_insider_data("blis") - - -def blos() -> pd.DataFrame: - """Get latest officer sales > 100k - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.blos() - """ - return openinsider_model.get_print_insider_data("blos") - - -def blcs() -> pd.DataFrame: - """Get latest CEO/CFO sales > 100k - - Returns - ------- - pd.DataFrame - DataFrame of latest insider trades - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.ins.blcs() - """ - return openinsider_model.get_print_insider_data("blcs") diff --git a/openbb_terminal/stocks/mappings/Mic_Codes.csv b/openbb_terminal/stocks/mappings/Mic_Codes.csv deleted file mode 100644 index ccfc2ac1979..00000000000 --- a/openbb_terminal/stocks/mappings/Mic_Codes.csv +++ /dev/null @@ -1,2125 +0,0 @@ -MIC,Name -XALS,ALBANIA SECURITIES EXCHANGE -XTIR,TIRANA STOCK EXCHANGE -XALG,ALGIERS STOCK EXCHANGE -XBDV,BOLSA DE DIVIDA E VALORES DE ANGOLA (BODIVA) - ANGOLA SECURITIES EXCHANGE -XFTX,FTX -BACE,BOLSA DE CEREALES DE BUENOS AIRES -BCFS,BOLSA DE COMERCIO DE SANTA FE -MVCX,MERCADO DE VALORES DE CORDOBA S.A. -ROFX,ROSARIO FUTURE EXCHANGE -XBCC,BOLSA DE COMERCIO DE CORDOBA -XBCM,BOLSA DE COMERCIO DE MENDOZA S.A. -XBCX,MERCADO DE VALORES DE MENDOZA S.A. -XBUE,BOLSA DE COMERCIO DE BUENOS AIRES -XCNF,BOLSA DE COMERCIO CONFEDERADA S.A. -XMAB,MERCADO ABIERTO ELECTRONICO S.A. -XMEV,MERCADO DE VALORES DE BUENOS AIRES S.A. -XMTB,MERCADO A TERMINO DE BUENOS AIRES S.A. -XMVL,MERCADO DE VALORES DEL LITORAL S.A. -XROS,BOLSA DE COMERCIO ROSARIO -XROX,MERCADO DE VALORES DE ROSARIO S.A. -XTUC,NUEVA BOLSA DE COMERCIO DE TUCUMAN S.A. -XARM,ARMENIA SECURITIES EXCHANGE -APXL,SYDNEY STOCK EXCHANGE LIMITED -ASXB,ASX BOOKBUILD -ASXC,ASX - CENTRE POINT -ASXP,ASX - PUREMATCH -ASXT,ASX TRADEMATCH -ASXV,ASX - VOLUMEMATCH -AWBX,AUSTRALIAN WHEAT BOARD -AWEX,AUSTRALIAN WOOL EXCHANGE -BGCA,BGC PARTNERS (AUSTRALIA) PTY LTD -BLEV,BLOCK EVENT -BLXA,INSTINET BLX (AU) -CFAU,CROSSFINDER AUSTRALIA -CGMA,CITI MATCH AUSTRALIA -CHIA,CBOE AUSTRALIA -CLAU,CLSA AUSTRALIA - DARK -CSAU,CREDIT SUISSE EQUITIES (AUSTRALIA) LIMITED -CXAC,CBOE AUSTRALIA - LIMIT VENUE -CXAF,CBOE AUSTRALIA MARKET PEG (FARPOINT) VENUE -CXAM,CBOE AUSTRALIA MOC -CXAN,CBOE AUSTRALIA PRIMARY PEG (NEARPOINT) VENUE -CXAP,CBOE AUSTRALIA MID-POINT VENUE -CXAQ,CBOE AUSTRALIA - QUOTED MANAGED FUNDS -CXAR,CBOE AUSTRALIA - TRANSFERABLE CUSTODY RECEIPT MARKET -CXAV,CBOE AUSTRALIA VWAP -CXAW,CBOE AUSTRALIA - WARRANTS -GFAU,GFI AUSTRALIA PTY LTD -MACB,MACQUARIE AUSTRALIA BLOCK CROSSING -MAQX,MACQUARIE INTERNAL MARKETS (AUSTRALIA) -MSAL,MORGAN STANLEY AUSTRALIA SECURITIES LIMITED -NSXB,BENDIGO STOCK EXCHANGE LIMITED -NZFX,ASX - NEW ZEALAND FUTURES & OPTIONS -SIGA,SIGMA X AUSTRALIA -SIMV,SIM VENTURE SECURITIES EXCHANGE -XASX,ASX - ALL MARKETS -XFEX,FEX GLOBAL -XLQC,LIQUIDITY CUBE PTY LTD -XNEC,NATIONAL STOCK EXCHANGE OF AUSTRALIA LIMITED -XSFE,ASX - TRADE24 -XYIE,YIELDBROKER PTY LTD -APAW,WIENER BOERSE AG - APA -BKSK,BKS BANK AG - SYSTEMATIC INTERNALISER -EGSI,ERSTE GROUP BANK AG -EXAA,"WIENER BOERSE AG, AUSTRIAN ENERGY EXCHANGE" -FPWB,FIRSTPLACE WIENER BOERSE -LLAT,LIECHTENSTEINISCHE LANDESBANK (OSTERREICH) AG - SYSTEMATIC INTERNALISER -OBKL,OBERBANK AG - SYSTEMATIC INTERNALISER -RBIV,RAIFFEISEN BANK INTERNATIONAL AG - SYSTEMATIC INTERNALISER -RLBO,RAIFFEISENLANDESBANK OBERÖSTERREICH - SYSTEMATIC INTERNALISER -SLHB,SALZBURGER LANDES-HYPOTHEKENBANK -UCBA,UNICREDIT BANK AUSTRIA AG -WBAH,WIENER BOERSE AG AMTLICHER HANDEL (OFFICIAL MARKET) -WBDM,WIENER BOERSE AG VIENNA MTF (VIENNA MTF) -XRCB,RAIFFEISEN CENTROBANK AG - SYSTEMATIC INTERNALISER -XVIE,"WIENER BOERSE AG, WERTPAPIERBOERSE (SECURITIES EXCHANGE)" -XWBO,WIENER BOERSE AG -BSEX,BAKU STOCK EXCHANGE -XIBE,BAKU INTERBANK CURRENCY EXCHANGE -XBAA,BAHAMAS INTERNATIONAL SECURITIES EXCHANGE -BFEX,BAHRAIN FINANCIAL EXCHANGE -XBAH,BAHRAIN BOURSE -XCHG,CHITTAGONG STOCK EXCHANGE LTD. -XDHA,DHAKA STOCK EXCHANGE LTD -BAJM,BARBADOS STOCK EXCHANGE - JUNIOR MARKET -XBAB,BARBADOS STOCK EXCHANGE -XBIS,BARBADOS STOCK EXCHANGE - INTERNATIONAL SECURITIES MARKET -BCSE,BELARUS CURRENCY AND STOCK EXCHANGE -ALXB,EURONEXT GROWTH BRUSSELS -BEAM,MTS ASSOCIATED MARKETS -BELB,BELFIUS BANK NV/SA -BELF,BELFIUS BANK NV/SA FIXED INCOME - SYSTEMATIC INTERNALISER -BKBF,BNY MELLON S.A./N.V. - BRUSSELS BRANCH - SYSTEMATIC INTERNALISER -BKBR,BNY MELLON S.A./N.V. - BRUSSELS BRANCH -BLPX,BELGIAN POWER EXCHANGE -BMTS,MTS BELGIUM -BNPF,BNP PARIBAS FORTIS SA/NV- SYSTEMATIC INTERNALISER -KBCB,KBC BANK NV GROUP MARKETS - SYSTEMATIC INTERNALISER -MLXB,EURONEXT ACCESS BRUSSELS -MTSD,MTS DENMARK -MTSF,MTS FINLAND -SMBB,SUMITOMO MITSUI BANKING CORPORATION - BRUSSELS BRANCH - SYSTEMATIC INTERNALISER -TNLB,EURONEXT - TRADING FACILITY BRUSSELS -TNLK,EURONEXT BLOCKS -VPXB,EURONEXT - VENTES PUBLIQUES BRUSSELS -XBRD,EURONEXT - EURONEXT BRUSSELS - DERIVATIVES -XBRU,EURONEXT - EURONEXT BRUSSELS -24EX,24 EXCHANGE -GTXE,GTX ECN -XBDA,BERMUDA STOCK EXCHANGE LTD -XBOL,BOLSA BOLIVIANA DE VALORES S.A. -BLBF,BANJA LUKA STOCK EXCHANGE - FREE MARKET -XBLB,BANJA LUKA STOCK EXCHANGE -XSSE,SARAJEVO STOCK EXCHANGE -BOTE,BOTSWANA STOCK EXCHANGE - EXCHANGE TRADED FUNDS (ETF) -BOTV,BOTSWANA STOCK EXCHANGE - VENTURE CAPITAL -XBOT,BOTSWANA STOCK EXCHANGE -BCMM,BOLSA DE CEREAIS E MERCADORIAS DE MARINGÁ -BOVM,BOLSA DE VALORES MINAS-ESPÍRITO SANTO-BRASÍLIA -BRIX,BRAZILIAN ENERGY EXCHANGE -BVMF,B3 S.A. - BRASIL BOLSA BALCAO -CETI,CETIP S.A. - MERCADOS ORGANIZADOS -SELC,SISTEMA ESPECIAL DE LIQUIDACAO E CUSTODIA DE TITULOS PUBLICOS -ABUL,BULGARIAN STOCK EXCHANGE - ALTERNATIVE MARKET -BDSK,DSK BANK EAD -BEBG,BULGARIAN ENERGY TRADING PLATFORM -BGHX,BALKAN GAS HUB EAD -EUFN,EURO-FINANCE AD -GBUL,BULGARIAN STOCK EXCHANGE - SME GROWTH MARKET BEAM -GMBG,MTF SOFIA - GROWTH MARKET -IBEX,INDEPENDENT BULGARIAN ENERGY EXCHANGE -IBUL,BULGARIAN STOCK EXCHANGE - INTERNATIONAL INSTRUMENTS -JBUL,BULGARIAN STOCK EXCHANGE - INTERNATIONAL MTF -MBUL,MTF SOFIA -PBUL,BULGARIAN STOCK EXCHANGE - APA -T212,TRADING 212 LIMITED - SYSTEMATIC INTERNALISER -UCBG,UNICREDIT BULBANK AD - SYSTEMATIC INTERNALISER -XBUL,BULGARIAN STOCK EXCHANGE -ZBUL,BULGARIAN STOCK EXCHANGE - MAIN MARKET -XCSX,CAMBODIA SECURITIES EXCHANGE -XDSX,DOUALA STOCK EXCHANGE -BNSX,THE BANK OF NOVA SCOTIA - SYSTEMATIC INTERNALISER -CAND,CANDEAL.CA INC -CANX,CANNEX FINANCIAL EXCHANGE LTS -CHIC,CHI-X CANADA ATS -COTC,BMO CAPITAL MARKETS - CAD OTC TRADES -CSE2,CANADIAN SECURITIES EXCHANGE - CSE2 -EQCA,EQUILEND CANADA CORP. -IVZX,INVESCO CANADA PTF TRADES -LICA,LIQUIDNET CANADA ATS -LYNX,LYNX ATS -MATN,MATCH NOW -NEOC,NEO CONNECT -NEOD,NEO EXCHANGE - NEO-D (DARK) -NEOE,NEO EXCHANGE - NEO-L (MARKET BY ORDER) -NEON,NEO EXCHANGE - NEO-N (MARKET BY PRICE) -NGXC,NATURAL GAS EXCHANGE -OMGA,OMEGA ATS -PURE,CANADIAN SECURITIES EXCHANGE - PURE -VDRK,TSX VENTURE EXCHANGE - DRK -XATS,ALPHA EXCHANGE -XBBK,PERIMETER FINANCIAL CORP. - BLOCKBOOK ATS -XCNQ,CANADIAN SECURITIES EXCHANGE -XCX2,CX2 -XCXD,NASDAQ CXD -XDRK,TORONTO STOCK EXCHANGE - DRK -XICX,INSTINET CANADA CROSS -XMOC,MONTREAL CLIMATE EXCHANGE -XMOD,THE MONTREAL EXCHANGE / BOURSE DE MONTREAL -XTNX,TSX VENTURE EXCHANGE - NEX -XTSE,TORONTO STOCK EXCHANGE -XTSX,TSX VENTURE EXCHANGE -XBVC,CAPE VERDE STOCK EXCHANGE -XCAY,CAYMAN ISLANDS STOCK EXCHANGE -BOVA,BOLSA DE CORREDORES - BOLSA DE VALORES -XBCL,LA BOLSA ELECTRONICA DE CHILE -XSGO,SANTIAGO STOCK EXCHANGE -BJSE,BEIJING STOCK EXCHANGE -CCFX,CHINA FINANCIAL FUTURES EXCHANGE -CFBC,CHINA FOREIGN EXCHANGE TRADE SYSTEM - SHANGHAI - HONG KONG BOND CONNECT -CSSX,CHINA STAINLESS STEEL EXCHANGE -GSXC,GSX CHINA -NEEQ,NATIONAL EQUITIES EXCHANGE AND QUOTATIONS -SGEX,SHANGHAI GOLD EXCHANGE -XCFE,CHINA FOREIGN EXCHANGE TRADE SYSTEM -XDCE,DALIAN COMMODITY EXCHANGE -XINE,SHANGHAI INTERNATIONAL ENERGY EXCHANGE -XSEC,SHENZHEN STOCK EXCHANGE - SHENZHEN - HONG KONG STOCK CONNECT -XSGE,SHANGHAI FUTURES EXCHANGE -XSHE,SHENZHEN STOCK EXCHANGE -XSHG,SHANGHAI STOCK EXCHANGE -XSSC,SHANGHAI STOCK EXCHANGE - SHANGHAI - HONG KONG STOCK CONNECT -XZCE,ZHENGZHOU COMMODITY EXCHANGE -XBOG,BOLSA DE VALORES DE COLOMBIA -XBNV,"BOLSA NACIONAL DE VALORES, S.A." -XCRO,CROATIAN POWER EXCHANGE -XOTP,OTP BANKA D.D. -XTRZ,ZAGREB MONEY AND SHORT TERM SECURITIES MARKET INC -XZAG,ZAGREB STOCK EXCHANGE -XZAP,PROGRESS MARKET -ZAPA,ZAGREB STOCK EXCHANGE - APA -DCSX,DUTCH CARIBBEAN SECURITIES EXCHANGE -ATHL,ATHLOS CAPITAL INVESTMENT SERVICES LTD - SYSTEMATIC INTERNALISER -ATLN,ATONLINE LIMITED - SYSTEMATIC INTERNALISER -BCSC,BROKERCREDITSERVICE (CYPRUS) LIMITED - SYSTEMATIC INTERNALISER -CFIF,CREDIT FINANCIER INVEST (CFI) LTD -GPBC,GPB-FINANCIAL SERVICES LTD - SYSTEMATIC INTERNALISER -MKAP,MERITKAPITAL - SYSTEMATIC INTERNALISER -RENC,RENAISSANCE SECURITIES (CYPRUS) LIMITED -SIBC,SIB CYPRUS LTD - SYSTEMATIC INTERNALISER -TMCY,TRADING 212 MARKETS LIMITED - SYSTEMATIC INTERNALISER -XCYO,CYPRUS STOCK EXCHANGE - OTC -XCYS,CYPRUS STOCK EXCHANGE -XECM,MTF - CYPRUS EXCHANGE -CELP,"ČESKOSLOVENSKÁ OBCHODNÍ BANKA, A.S. - OTHER LIQUIDITY PROVIDER (OLP)" -CESI,"ČESKOSLOVENSKÁ OBCHODNÍ BANKA, A.S. - SYSTEMATIC INTERNALISER" -CSAS,"CESKA SPORITELNA, A.S. - SYSTEMATIC INTERNALISER" -CSOB,"ČESKOSLOVENSKÁ OBCHODNÍ BANKA, A.S." -FTFM,42 FINANCIAL SERVICES - MTF -FTFS,42 FINANCIAL SERVICES -KOME,"KOMERCNI BANKA, A.S." -PATF,PATRIA FINANCE A.S. -RFBK,"RAIFFEISENBANK, AS. - SYSTEMATIC INTERNALISER" -UBCZ,"UNICREDIT BANK CZECH REPUBLIC AND SLOVAKIA, A.S." -WOOD,"WOOD & COMPANY FINANCIAL SERVICES, A.S. - SYSTEMATIC INTERNALISER" -XPRA,PRAGUE STOCK EXCHANGE -XPRM,PRAGUE STOCK EXCHANGE - MTF -XPXE,POWER EXCHANGE CENTRAL EUROPE -XRMO,RM-SYSTEM CZECH STOCK EXCHANGE - MTF -XRMZ,RM-SYSTEM CZECH STOCK EXCHANGE -ABSI,ALM. BRAND BANK - SYSTEMATIC INTERNALISER -ALSI,AKTIESELSKABET ARBEJDERNES LANDSBANK - SYSTEMATIC INTERNALISER -DASI,DANSKE BANK A/S - SYSTEMATIC INTERNALISER -DCSE,NASDAQ COPENHAGEN A/S - NORDIC@MID -DKTC,DANSK OTC -DNDK,FIRST NORTH DENMARK - NORDIC@MID -DSME,FIRST NORTH DENMARK -SME GROWTH MARKET -FNDK,FIRST NORTH DENMARK -JBSI,JYSKE BANK - SYSTEMATIC INTERNALISER -JYSI,DEN JYSKE SPAREKASSE - SYSTEMATIC INTERNALISER -LASP,LAN AND SPAR BANK A/S -MCSE,NASDAQ COPENHAGEN A/S - AUCTION ON DEMAND -MNDK,FIRST NORTH DENMARK - AUCTION ON DEMAND -NPGA,GASPOINT NORDIC A/S -NYSI,NYKREDIT BANK - SYSTEMATIC INTERNALISER -SBSI,SYDBANK A/S -SKSI,SPAREKASSEN KRONJYLLAND - SYSTEMATIC INTERNALISER -SNSI,SPAR NORD BANK - SYSTEMATIC INTERNALISER -SXSI,SAXO BANK A/S -XCSE,NASDAQ COPENHAGEN A/S -XBVR,BOLSA DE VALORES DE LA REPUBLICA DOMINICANA SA. -XGUA,BOLSA DE VALORES DE GUAYAQUIL -XQUI,BOLSA DE VALORES DE QUITO -NILX,NILE STOCK EXCHANGE -XCAI,EGYPTIAN EXCHANGE -XSVA,EL SALVADOR STOCK EXCHANGE -FNEE,FIRST NORTH ESTONIA -LMNR,LUMINOR BANK AS -SWEE,SWEDBANK ESTONIA - SYSTEMATIC INTERNALISER -XTAL,NASDAQ TALLINN AS -VMFX,THE FAROESE SECURITIES MARKET -XSPS,SOUTH PACIFIC STOCK EXCHANGE -DHEL,NASDAQ HELSINKI LTD - NORDIC@MID -DNFI,FIRST NORTH FINLAND - NORDIC@MID -FGEX,KAASUPORSSI - FINNISH GAS EXCHANGE -FNFI,FIRST NORTH FINLAND -FSME,FIRST NORTH FINLAND - SME GROWTH MARKET -MHEL,NASDAQ HELSINKI LTD - AUCTION ON DEMAND -MNFI,FIRST NORTH FINLAND - AUCTION ON DEMAND -OPCO,OP CORPORATE BANK PLC - SYSTEMATIC INTERNALISER -XHEL,NASDAQ HELSINKI LTD -XNOR,NORDEA - SYSTEMATIC INTERNALISER -AACA,CREDIT AGRICOLE CIB - SYSTEMATIC INTERNALISER -ALXP,EURONEXT GROWTH PARIS -AQEA,AQUIS EXCHANGE EUROPE AUCTION ON DEMAND (AOD) -AQED,AQUIS EXCHANGE EUROPE NON DISPLAY ORDER BOOK (NDOB) -AQEU,AQUIS EXCHANGE EUROPE -AURB,AUREL -AURO,AUREL - OTF -BNPA,BNP PARIBAS ARBITRAGE SNC - SYSTEMATIC INTERNALISER -BNPS,BNP PARIBAS SA - SYSTEMATIC INTERNALISER -BPSX,BNP PARIBAS SECURITIES SERVICES - SYSTEMATIC INTERNALISER -BRED,BRED BANQUE POPULAIRE - SYSTEMATIC INTERNALISER -CMCI,CREDIT INDUSTRIEL ET COMMERCIAL - SYSTEMATIC INTERNALISER -COAL,LA COTE ALPHA -EDRF,EDMOND DE ROTHSCHILD (FRANCE) - SYSTEMATIC INTERNALISER -ELXE,ELIXIUM S.A. -ENSL,ENGNSOL - OMP -EPEX,EPEX SPOT SE -ESLO,ENGNSOL -EXSB,EXANE DERIVATIVES - SYSTEMATIC INTERNALISER CORPORATE BONDS -EXSD,EXANE DERIVATIVES - SYSTEMATIC INTERNALISER OTC DERIVATIVES -EXSE,EXANE BNP PARIBAS - SYSTEMATIC INTERNALISER -EXSF,EXANE DERIVATIVES - SYSTEMATIC INTERNALISER OTHER BONDS -EXSH,EXANE DERIVATIVES - SYSTEMATIC INTERNALISER SHARES -EXSP,EXANE DERIVATIVES - SYSTEMATIC INTERNALISER STRUCTURED PRODUCTS -EXSY,EXANE DERIVATIVES - SYSTEMATIC INTERNALISER -EXYY,EXANE DERIVATIVES - SYSTEMATIC INTERNALISER CONVERTIBLES -FMTS,MTS FRANCE SAS -GFPO,GFI PARIS - OTF -GIPB,GOLDMAN SACHS INTERNATIONAL PARIS BRANCH -GMEO,GRIFFIN MARKETS EUROPE - OTF -GMES,GRIFFIN MARKETS EUROPE -GMTF,GALAXY -GSPX,GOLDMAN SACHS INTERNATIONAL PARIS BRANCH - SYSTEMATIC INTERNALISER -HBFR,HSBC CONTINENTAL EUROPE - SYSTEMATIC INTERNALISER -HPCO,HPC ETRADING -HPCS,HPC ENERGY TRADING -HPCV,HPC SA - VOICE OTF -HPCX,HPC SA -ICOR,ICAP EU - OTF - REGISTRATION -ICOT,ICAP EU - OTF -KOTF,OTFLINK -LCHC,LCH.CLEARNET -LNEQ,TP ICAP EU - MTF - LIQUIDNET EU EQUITY -LNFI,TP ICAP EU - MTF - LIQUIDNET EU FIXED INCOME -MAQE,MACQUARIE BANK EUROPE DESIGNATED ACTIVITY COMPANY - SYSTEMATIC INTERNALISER -MHBP,MIZUHO BANK LTD. - PARIS BRANCH - SYSTEMATIC INTERNALISER -MKTF,MARKET SECURITIES (FRANCE) SA -MLER,BOFA SECURITIES EUROPE - RFQ - SYSTEMATIC INTERNALISER -MLES,BOFA SECURITIES EUROPE - SYSTEMATIC INTERNALISER -MLEX,BOFA SECURITIES EUROPE -MTCH,BONDMATCH -MUBP,"MUFG BANK, LTD. - PARIS BRANCH - SYSTEMATIC INTERNALISER" -NABP,NATIONAL AUSTRALIA BANK EUROPE S.A. -NATX,NATIXIS - SYSTEMATIC INTERNALISER -NOWX,NOW CP - NEU CP -ODDO,ODDO BHF -ODOC,ODDO CONTREPARTIE -RBCC,RBC - PARIS BRANCH - SYSTEMATIC INTERNALISER -SGMU,SIGMA X EUROPE NON-DISPLAYED BOOK -SGMV,SIGMA X EUROPE AUCTION BOOK -SGMW,SIGMA X EUROPE NEGOTIATED TRADE -SGOE,SG OPTION EUROPE - SYSTEMATIC INTERNALISER -SMBP,SUMITOMO MITSUI BANKING CORPORATION EUROPE LIMITED - PARIS BRANCH -TDON,TRADITION PARIS - TSAF -TLCM,LOUIS CAPITAL MARKETS EU -TPEE,TP ICAP EU - MTF - EQUITY AND ETF -TPER,TULLETT PREBON EU OTF - REGISTRATION -TPEU,TULLETT PREBON EU OTF -TPIC,TP ICAP EU - MTF -TPIO,TP ICAP EU - MTF - ORDERBOOK -TPIR,TP ICAP EU - MTF - REGISTRATION -TRXE,TRAD-X EUROPE -TSAF,TSAF OTC - OTF -WFSE,"WELLS FARGO SECURITIES EUROPE, S.A." -XAFR,ALTERNATIVA FRANCE -XAPA,EURONEXT - APA -XBLK,EURONEXT BLOCK 2 -XBLN,BLUENEXT -XETF,EURONEXT ETF ACCESS -XMAT,EURONEXT PARIS MATIF -XMLI,EURONEXT ACCESS PARIS -XMON,EURONEXT PARIS MONEP -XPAR,EURONEXT - EURONEXT PARIS -XPOW,POWERNEXT -XSGA,SOCIETE GENERALE - SYSTEMATIC INTERNALISER -XSPM,EURONEXT STRUCTURED PRODUCTS MTF -XTXE,XTX MARKETS SAS -XGSE,GEORGIA STOCK EXCHANGE -360T,360T -AUTO,AUTOBAHN FX -BAAD,BAADER BANK - SYSTEMATIC INTERNALISER -BDEA,BAYERNLB - IDEAL FX - SYSTEMATIC INTERNALISER -BERA,BOERSE BERLIN - REGULIERTER MARKT -BERB,BOERSE BERLIN - FREIVERKEHR -BERC,BOERSE BERLIN - BERLIN SECOND REGULATED MARKET -BGFI,BERENBERG FIXED INCOME - SYSTEMATIC INTERNALISER -BGFX,BERENBERG FX - SYSTEMATIC INTERNALISER -BGSI,BERENBERG - SYSTEMATIC - INTERNALISER -BINV,BOND INVEST - SYSTEMATIC INTERNALISER -BLBB,BAYERNLB -BLBS,BAYERNLB - BONDS MARKET - SYSTEMATIC INTERNALISER -BLEQ,BAYERNLB - EQUITIES MARKET -BLFX,BAYERNLB - FX DERIVATIVES MARKET -BLIQ,BAYERNLB - LIQP BONDS MARKET -BOCF,BANK OF CHINA LIMITED - FRANKFURT BRANCH - SYSTEMATIC INTERNALISER -BSFX,BAYERNLB - FX HANDEL - SYSTEMATIC INTERNALISER -CATS,CATS -CBKA,COMMERZBANK AG -CBKC,COMMERZBANK COMMODITY DERIVATIVES - SYSTEMATIC INTERNALISER -CBKD,COMMERZBANK EQUITY DERIVATIVES - SYSTEMATIC INTERNALISER -CBKE,COMMERZBANK EQUITY - SYSTEMATIC INTERNALISER -CBKF,COMMERZBANK COMMANDER - SYSTEMATIC INTERNALISER -CBKG,COMMERZBANK FIXED INCOME - SYSTEMATIC INTERNALISER -CBKS,COMMERZBANK INTEREST RATE SWAPS - SYSTEMATIC INTERNALISER -CDSI,CREDIT SUISSE (DEUTSCHLAND) AG - SYSTEMATIC INTERNALISER -CGEC,CITIGROUP GLOBAL MARKETS EUROPE AG - EMEA LIQUIDITY HUB -CGEE,CITI MATCH DE CONTRA LIQUIDITY -CGET,CITIGROUP GLOBAL MARKETS EUROPE AG - EMEA TRADING -CGMD,CITIGROUP GLOBAL MARKETS EUROPE AG - SYSTEMATIC INTERNALISER -CSDA,CREDIT SUISSE AKTIENGESELLSCHAFT -DAPA,DEUTSCHE BOERSE AG - APA SERVICE -DBAG,DEUTSCHE BANK AG -DBDX,DEUTSCHE BOERSE DIGITAL EXCHANGE -DBES,DEUTSCHE BANK AG - SYSTEMATIC INTERNALISER -DBLN,DEUTSCHE BANK AG - LONDON - SYSTEMATIC INTERNALISER -DBMO,DEUTSCHE BANK - MANUAL OTC -DBOX,DEUTSCHE BANK OFF EXCHANGE TRADING -DEKA,DEKABANK DEUTSCHE GIROZENTRALE -DUSA,BOERSE DUESSELDORF - REGULIERTER MARKT -DUSB,BOERSE DUESSELDORF - FREIVERKEHR -DUSC,BOERSE DUESSELDORF - QUOTRIX - REGULIERTER MARKT -DUSD,BOERSE DUESSELDORF - QUOTRIX MTF -DZBK,DZ BANK - SYSTEMATIC INTERNALISER -EBLX,BLOCKMATCH EUROPE DARK -ECAG,EUREX CLEARING AG -ECGS,EUREX CLEARING AG - SME -ENTW,BLOCKMATCH EUROPE NT -EQTA,BOERSE BERLIN EQUIDUCT TRADING - REGULIERTER MARKT -EQTB,BOERSE BERLIN EQUIDUCT TRADING - BERLIN SECOND REGULATED MARKET -EQTC,BOERSE BERLIN EQUIDUCT TRADING - FREIVERKEHR -EQTD,BOERSE BERLIN EQUIDUCT TRADING - OTC -ERFQ,BLOCKMATCH EUROPE RFQ -EUWA,EUWAX AG - SYSTEMATIC INTERNALISER -EUWX,EUWAX -FICX,FINANCIAL INFORMATION CONTRIBUTORS EXCHANGE -FRAA,BOERSE FRANKFURT - REGULIERTER MARKT -FRAB,BOERSE FRANKFURT - FREIVERKEHR -FRAS,BOERSE FRANKFURT - SCALE -FRAU,BOERSE FRANKFURT - REGULIERTERMARKT - OFF-BOOK -FRAV,BOERSE FRANKFURT - FREIVERKEHR - OFF-BOOK -FRAW,BOERSE FRANKFURT - SCALE - OFF-BOOK -GSBE,GOLDMAN SACHS BANK EUROPE SE -GSEI,GOLDMAN SACHS BANK EUROPE SE - SYSTEMATIC INTERNALISER -HAMA,BOERSE HAMBURG - REGULIERTER MARKT -HAMB,BOERSE HAMBURG - FREIVERKEHR -HAML,BOERSE HAMBURG - LANG AND SCHWARZ EXCHANGE -HAMM,BOERSE HAMBURG - LANG AND SCHWARZ EXCHANGE - REGULIERTER MARKT -HAMN,BOERSE HAMBURG - LANG AND SCHWARZ EXCHANGE - FREIVERKEHR -HANA,BOERSE HANNOVER - REGULIERTER MARKT -HANB,BOERSE HANNOVER - FREIVERKEHR -HELA,HELABA - SYSTEMATIC INTERNALISER -HSBT,HSBC TRINKAUS AND BURKHARDT AG - SYSTEMATIC INTERNALISER -IKBS,IKB DEUTSCHE INDUSTRIEBANK AG - SYSTEMATIC INTERNALISER -JEFE,JEFFERIES EUROPE -JESI,JEFFERIES EUROPE - SYSTEMATIC INTERNALISER -JPEU,J.P. MORGAN SE - SYSTEMATIC INTERNALISER -LBBW,LBBW - LANDESBANK BADEN-WUERTTEMBERG -LBCW,LLOYDS BANK CORPORATE MARKETS WERTPAPIERHANDELSBANK GMBH - SYSTEMATIC INTERNALISER -LBWL,LBBW - LANDESBANK BADEN-WUERTTEMBERG - LIQUIDITY PROVIDER -LBWS,LBBW - LANDESBANK BADEN-WUERTTEMBERG - SYSTEMATIC INTERNALISER -LIGA,LIGA BANK EG - SYSTEMATIC INTERNALISER -LSSI,LANG AND SCHWARZ TRADE CENTER - SYSTEMATIC INTERNALISER -MESI,MORGAN STANLEY EUROPE S.E. - SYSTEMATIC INTERNALISER -MHBD,"MIZUHO BANK, LTD. DUESSELDORF BRANCH - SYSTEMATIC INTERNALISER" -MHEU,MIZUHO SECURITIES EUROPE - SYSTEMATIC INTERNALISER -MSEU,MORGAN STANLEY EUROPE S.E. -MUNA,BOERSE MUENCHEN - REGULIERTER MARKT -MUNB,BOERSE MUENCHEN - FREIVERKEHR -MUNC,BOERSE MUENCHEN - GETTEX - REGULIERTER MARKT -MUND,BOERSE MUENCHEN - GETTEX - FREIVERKEHR -NCME,SMBC NIKKO CAPITAL MARKETS EUROPE GMBH -NESI,NOMURA EUROPE - SYSTEMATIC INTERNALISER -NLBX,NORDDEUTSCHE LANDESBANK - GIROZENTRALE - SYSTEMATIC INTERNALISER -NORD,HSH NORDBANK - SYSTEMATIC INTERNALISER -OLBB,OLDENBURGISCHE LANDESBANK AG FX HANDEL - SYSTEMATIC INTERNALISER -RBCG,RBC CAPITAL MARKETS (EUROPE) GMBH -SCAG,STANDARD CHARTERED BANK AG - SYSTEMATIC INTERNALISER -SEBA,STIFEL EUROPE BANK AG - SYSTEMATIC INTERNALISER -SMBD,SUMITOMO MITSUI BANKING CORPORATION - DUESSELDORF BRANCH - SYSTEMATIC INTERNALISER -SMFF,SMBC BANK EU AG - SYSTEMATIC INTERNALISER -SPEX,SPECTRUM MARKETS -SSBI,STATE STREET BANK INTERNATIONAL GMBH -SSBM,STATE STREET BANK INTERNATIONAL FX - SYSTEMATIC INTERNALISER -STUA,BOERSE STUTTGART - REGULIERTER MARKT -STUB,BOERSE STUTTGART - FREIVERKEHR -STUC,BOERSE STUTTGART - REGULIERTER MARKT - TECHNICAL PLATFORM 2 -STUD,BOERSE STUTTGART - FREIVERKEHR - TECHNICAL PLATFORM 2 -STUE,BOERSE STUTTGART - REGULIERTER MARKT - TECHNICAL PLATFORM 3 -STUF,BOERSE STUTTGART - FREIVERKEHR - TECHNICAL PLATFORM 3 -TGAT,TRADEGATE EXCHANGE -TGSI,TRADEGATE EXCHANGE - SYSTEMATIC INTERNALISER -UBSI,UBS EUROPE SE - SYSTEMATIC INTERNALISER -UBSL,UBS EUROPE SE - TRADING -UCDE,UNICREDIT BANK AG - SYSTEMATIC INTERNALISER -VONT,BANK VONTOBEL EUROPE AG - SYSTEMATIC INTERNALISER -VTLS,TRADELINK -VTPS,TRADEPLUS -VWDA,VWD - APA SERVICE -VWDX,VWD TRANSACTIONSOLUTIONS AG -XBER,BOERSE BERLIN -XDEX,BOERSE STUTTGART - DIGITAL EXCHANGE -XDUS,BOERSE DUESSELDORF -XECB,ECB EXCHANGE RATES -XECC,EUROPEAN COMMODITY CLEARING AG -XEEE,EUROPEAN ENERGY EXCHANGE -XEEO,EUROPEAN ENERGY EXCHANGE - NON-MTF MARKET -XEER,EUROPEAN ENERGY EXCHANGE - REGULATED MARKET -XEHQ,EUREX REPO - HQLA MARKET -XEQT,BOERSE BERLIN EQUIDUCT TRADING -XERE,EUREX REPO - FUNDING AND FINANCING PRODUCTS -XERT,EUREX REPO - TRIPARTY -XETA,XETRA - REGULIERTER MARKT -XETB,XETRA - FREIVERKEHR -XETE,XETRA - NON-FINANCIAL INSTRUMENTS -XETR,XETRA -XETS,XETRA - SCALE -XETU,XETRA - REGULIERTERMARKT - OFF-BOOK -XETV,XETRA - FREIVERKEHR - OFF-BOOK -XETW,XETRA - SCALE - OFF-BOOK -XETX,XETRA - NON-FINANCIAL INSTRUMENTS - OFF-BOOK -XEUM,EUREX REPO SECLEND MARKET -XEUP,EUREX REPO GMBH -XEUR,EUREX DEUTSCHLAND -XFRA,DEUTSCHE BOERSE AG -XGAT,TRADEGATE EXCHANGE - FREIVERKEHR -XGRM,TRADEGATE EXCHANGE - REGULIERTER MARKT -XHAM,HANSEATISCHE WERTPAPIERBOERSE HAMBURG -XHAN,NIEDERSAECHSISCHE BOERSE ZU HANNOVER -XIGG,BLOCKMATCH EUROPE -XINV,INVESTRO -XMUN,BOERSE MUENCHEN -XPOT,EUROPEAN ENERGY EXCHANGE - OTF GAS MARKET -XPSF,EUROPEAN ENERGY EXCHANGE - REGULATED GAS MARKET -XQTX,BOERSE DUESSELDORF - QUOTRIX -XSC1,BOERSE FRANKFURT WARRANTS TECHNICAL 1 -XSC2,BOERSE FRANKFURT WARRANTS TECHNICAL 2 -XSC3,BOERSE FRANKFURT WARRANTS TECHNICAL 3 -XSCO,BOERSE FRANKFURT WARRANTS TECHNICAL -XSTF,BOERSE STUTTGART - TECHNICAL PLATFORM 2 -XSTP,BOERSE STUTTGART - TECHNICAL PLATFORM 3 -XSTU,BOERSE STUTTGART -XXSC,FRANKFURT CEF SC -ZOBX,ZOBEX -XGHA,GHANA STOCK EXCHANGE -GSXL,THE GIBRALTAR STOCK EXCHANGE -AAPA,ATHENS EXCHANGE - APA -ABFI,ALPHA BANK - SYSTEMATIC INTERNALISER -ASEX,ATHENS STOCK EXCHANGE -ENAX,ATHENS EXCHANGE ALTERNATIVE MARKET -ERBX,EUROBANK - SYSTEMATIC INTERNALISER -HDAT,ELECTRONIC SECONDARY SECURITIES MARKET (HDAT) -HEDE,HENEX FINANCIAL ENERGY MARKET - DERIVATIVES MARKET -HEMO,HENEX S.A. -HESP,HENEX ELECTRICITY SPOT MARKET -HGSP,HENEX GAS SPOT MARKET -HOTC,HELLENIC EXCHANGE OTC MARKET -PBGR,PIRAEUS BANK - SYSTEMATIC INTERNALISER -XADE,ATHENS EXCHANGE S.A. DERIVATIVES MARKET -XATH,ATHENS EXCHANGE S.A. CASH MARKET -XIPO,HELEX ELECTRONIC BOOK BUILDING -XGTG,BOLSA DE VALORES NACIONAL SA -XCIE,THE INTERNATIONAL STOCK EXCHANGE -GSCI,THE GUYANA ASSOCIATION OF SECURITIES COMPANIES AND INTERMEDIARIES INC. -XBCV,BOLSA CENTROAMERICANA DE VALORES S.A. -BACR,BANK OF AMERICA SECURITIES - CENTRAL RISK -BAEP,BANK OF AMERICA MERRILL LYNCH EQUITY SWAP - EXTERNAL PRICE SOURCE -BAIP,BANK OF AMERICA MERRILL LYNCH EQUITY SWAP - INTERNAL PRICE SOURCE -BASE,BANK OF AMERICA MERRILL LYNCH - SWAP EXECUTIONS -BASP,BANK OF AMERICA SECURITIES EQUITY SWAP - SYNTHETIC PRICE SOURCE -CFHK,CROSSFINDER HONG KONG -CGMH,CITI MATCH - HK -CLHK,CLSA HONG KONG - DARK -CSHK,CREDIT SUISSE SECURITIES (HONG KONG) LIMITED -DBHK,DEUTSCHE BANK HONG KONG ATS -EOTC,E-OTC -GSAL,GOLDMAN SACHS (ASIA) LLC -GSPL,GS PRINCIPAL LIQUIDITY ASIA -GSXH,GSX HONG KONG -HSXA,HSBC-X HONG KONG -JPMI,JP MORGAN - JPMI MARKET -MAQH,MACQUARIE INTERNAL MARKETS (HONG KONG) -OSDS,OSL DIGITAL SECURITIES EXCHANGE -SHSC,STOCK EXCHANGE OF HONG KONG LIMITED - SHANGHAI - HONG KONG STOCK CONNECT -SIGH,SIGMA X HONG KONG -SZSC,STOCK EXCHANGE OF HONG KONG LIMITED - SHENZHEN - HONG KONG STOCK CONNECT -TFSD,T.F.S. DERIVATIVES HK LIMITED -TOCP,TORA CROSSPOINT -TRAS,TRADITION ASIA LIMITED -TWHK,TRADEWEB EUROPE LIMITED - HONG KONG -UBSX,UBS CROSS -XCGS,CHINESE GOLD & SILVER EXCHANGE SOCIETY -XGEM,HONG KONG GROWTH ENTERPRISES MARKET -XHKF,HONG KONG FUTURES EXCHANGE LTD. -XHKG,HONG KONG EXCHANGES AND CLEARING LTD -XIHK,INSTINET PACIFIC LTD -XPST,POSIT - ASIA PACIFIC -BETA,BETA MARKET -CIBH,CIB BANK - SYSTEMATIC INTERNALISER -CONC,CONCORDE SECURITIES LTD. - SYSTEMATIC INTERNALISER -EBHU,ERSTE BANK HUNGARY ZRT. - SYSTEMATIC INTERNALISER -ERST,ERSTE BEFEKTETESI ZRT - SYSTEMATIC INTERNALISER -HUDX,HUNGARIAN DERIVATIVE ENERGY EXCHANGE -HUPX,HUNGARIAN POWER EXCHANGE -KCCP,KELER CCP -KELR,KELER -KHHU,K AND H BANK ZRT - SYSTEMATIC INTERNALISER -OTPB,OTP BANK NYRT - SYSTEMATIC INTERNALISER -RBHU,RAIFFEISEN BANK (HUNGARY) - SYSTEMATIC INTERNALISER -UCHU,UNICREDIT BANK HUNGARY ZRT. - SYSTEMATIC INTERNALISER -XBND,BUDAPEST STOCK EXCHANGE - XBOND -XBUD,BUDAPEST STOCK EXCHANGE -XGAS,CENTRAL EASTERN EUROPEAN GAS EXCHANGE LTD -XTND,XTEND -DICE,NASDAQ ICELAND HF. - NORDIC@MID -DNIS,FIRST NORTH ICELAND - NORDIC@MID -FNIS,FIRST NORTH ICELAND -MICE,NASDAQ ICELAND HF. - AUCTION ON DEMAND -MNIS,FIRST NORTH ICELAND - AUCTION ON DEMAND -XICE,NASDAQ ICELAND HF. -ACEX,ACE DERIVATIVES & COMMODITY EXCHANGE LTD -ASTR,CLEARCORP DEALING SYSTEMS INDIA LIMITED - ASTROID -BSME,BSE SME -CDSL,CLEARCORP DEALING SYSTEMS (INDIA) LTD. -FXCL,CLEARCORP DEALING SYSTEMS INDIA LIMITED - FX-CLEAR -FXSW,CLEARCORP DEALING SYSTEMS INDIA LIMITED - FX-SWAP -ICXL,INDIAN COMMODITY EXCHANGE LTD. -IINX,INDIA INTERNATIONAL EXCHANGE (IFSC) LIMITED -INSE,NSE IFSC LIMITED -ISEX,INTER-CONNECTED STOCK EXCHANGE OF INDIA LTD -MCXX,METROPOLITAN STOCK EXCHANGE OF INDIA LIMITED -NBOT,NATIONAL BOARD OF TRADE LIMITED -NMCE,NATIONAL MULTI-COMMODITY EXCHANGE OF INDIA -OTCX,OTC EXCHANGE OF INDIA -PXIL,POWER EXCHANGE INDIA LTD. -RITS,REFINITIV - FORWARDS MATCHING -XBAN,BANGALORE STOCK EXCHANGE LTD -XBOM,BSE LTD -XCAL,CALCUTTA STOCK EXCHANGE -XDES,DELHI STOCK EXCHANGE -XIMC,MULTI COMMODITY EXCHANGE OF INDIA LTD. -XMDS,MADRAS STOCK EXCHANGE -XNCD,NATIONAL COMMODITY & DERIVATIVES EXCHANGE LTD -XNSE,NATIONAL STOCK EXCHANGE OF INDIA -XUSE,UNITED STOCK EXCHANGE -ICDX,INDONESIA COMMODITY AND DERIVATIVES EXCHANGE -XBBJ,JAKARTA FUTURES EXCHANGE (BURSA BERJANGKA JAKARTA) -XIDX,INDONESIA STOCK EXCHANGE -XJNB,JAKARTA NEGOTIATED BOARD -IFBX,IRAN FARA BOURSE -IMEX,IRAN MERCANTILE EXCHANGE -XTEH,TEHRAN STOCK EXCHANGE -XIQS,IRAQ STOCK EXCHANGE -AILT,ARRACO IRELAND LTD -AREX,AREX - AUTOMATED RECEIVABLES EXCHANGE -BBIE,BARCLAYS BANK IRELAND PLC -BBIS,BARCLAYS BANK IRELAND PLC - SYSTEMATIC INTERNALISER -BKDM,BNY MELLON MARKETS EUROPE LIMITED -BMLI,BANK OF AMERICA MERRILL LYNCH INTERNATIONAL BANK DESIGNATED ACTIVITY COMPANY -BMLS,BANK OF AMERICA MERRILL LYNCH INTERNATIONAL BANK DESIGNATED ACTIVITY COMPANY - SYSTEMATIC INTERNALISER -CEPL,CITIBANK EUROPE PLC - SYSTEMATIC INTERNALISER -CFIL,CANTOR FITZGERALD IRELAND LIMITED -CSGI,CITADEL SECURITIES FI - EUROPE - SYSTEMATIC INTERNALISER -DAVY,J AND E DAVY - IRISH GOVERNMENT BOND - SYSTEMATIC INTERNALISER -EDBT,EURONEXT DUBLIN - TRADED BONDS -EDGL,GLOBAL EXCHANGE MARKET - TRADED BONDS -EQIE,EQUILEND LIMITED -EQSE,EQUILEND LIMITED - SWAPS -EUCC,CITADEL CONNECT EUROPE - EU - SYSTEMATIC INTERNALISER -FXFM,FINANCIAL AND RISK TRANSACTIONS SERVICES IRELAND LIMITED - FORWARDS MATCHING -FXRQ,FINANCIAL AND RISK TRANSACTIONS SERVICES IRELAND LIMITED - FXALL RFQ MTF -FXRS,FINANCIAL AND RISK TRANSACTIONS SERVICES IRELAND LIMITED - FXALL RFS MTF -GBSI,GOODBODY STOCKBROKERS UC -HREU,HRTEU LIMITED - SYSTEMATIC INTERNALISER -IBSI,INTERACTIVE BROKERS - SYSTEMATIC INTERNALISER -ICUR,CURRENEX IRELAND MTF -ICXR,CURRENEX IRELAND MTF - RFQ -IFXA,FX CONNECT IRELAND MTF - ALLOCATIONS -IFXC,FX CONNECT IRELAND MTF -IFXR,FX CONNECT IRELAND MTF - RFQ -ITGL,POSIT MTF -LEUE,LIQUIDNET EU LIMITED EQUITY MTF -LEUF,LIQUIDNET EU LIMITED FIXED INCOME MTF -MCID,MACQUARIE CAPITAL (IRELAND) -MSEL,MAREX SPECTRON EUROPE LIMITED - OTF -RMTF,REFINITIV MTF -SEMX,SEMOPX -SIDX,SCOTIABANK (IRELAND) DESIGNATED ACTIVITY COMPANY - SYSTEMATIC INTERNALISER -SISI,SUSQUEHANNA INTERNATIONAL SECURITIES LIMITED - SYSTEMATIC INTERNALISER -TDGF,TD SECURITIES -VFIL,VIRTU FINANCIAL IRELAND LIMITED -VFSI,VIRTU FINANCIAL IRELAND LIMITED - SYSTEMATIC INTERNALISER -VFXO,VIRTU FINANCIAL IRELAND LIMITED - OTC -XATL,ATLANTIC SECURITIES MARKET -XCDE,BAXTER FINANCIAL SERVICES -XDUB,IRISH STOCK EXCHANGE - ALL MARKET -XEBI,ENERGY BROKING IRELAND GAS TRADING PLATFORM -XESM,EURONEXT GROWTH DUBLIN -XEYE,IRISH STOCK EXCHANGE - GLOBAL EXCHANGE MARKET -XMSM,EURONEXT DUBLIN -XPAC,POSIT AUCTION -XPOS,POSIT DARK -XRFQ,POSIT RFQ -XTAE,TEL AVIV STOCK EXCHANGE -AKIS,AKIS - BANCA AKROS - SYSTEMATIC INTERNALISER -ATFX,ATFUND MTF -BEIS,BANCA ETICA - SYSTEMATIC INTERNALISER -BNLD,BANCA NAZIONALE DEL LAVORO SPA - SYSTEMATIC INTERNALISER -BREA,BANCA REALE - SYSTEMATIC INTERNALISER -CGCM,CASSA DI COMPENSAZIONE E GARANZIA SPA - COLLATERALIZED MONEY MARKET GUARANTEE SERVICE -CGDB,CASSA DI COMPENSAZIONE E GARANZIA SPA - BONDS CCP SERVICE -CGEB,CASSA DI COMPENSAZIONE E GARANZIA SPA - EURO BONDS CCP SERVICE -CGGD,CASSA DI COMPENSAZIONE E GARANZIA SPA - CCP AGRICULTURAL COMMODITY DERIVATIVES -CGIT,CASSA DI COMPENSAZIONE E GARANZIA SPA -CGND,CASSA DI COMPENSAZIONE E GARANZIA SPA - CCP ENERGY DERIVATIVES -CGQD,CASSA DI COMPENSAZIONE E GARANZIA SPA - CCP EQUITY DERIVATIVES -CGQT,CASSA DI COMPENSAZIONE E GARANZIA SPA - EQUITY CCP SERVICE -CGTR,CASSA DI COMPENSAZIONE E GARANZIA SPA - TRIPARTY REPO CCP SERVICE -CREM,CREDEM BANCA - SYSTEMATIC INTERNALISER -DDTX,DEAL DONE TRADING - SYSTEMATIC INTERNALISER -EBMX,EBM - MTF -EMDR,E-MID - E-MIDER MARKET -EMIB,E-MID - BANCA D'ITALIA SHARES TRADING MARKET -EMID,E-MID -EMIR,E-MID REPO -ETFP,"ELECTRONIC ETF, ETC/ETN AND OPEN-END FUNDS MARKET" -ETLX,EUROTLX -EXGM,EURONEXT GROWTH MILAN -FBSI,FINECO BANK - SYSTEMATIC INTERNALISER -HCER,HI-CERT - MULTILATERAL TRADING FACILITY -HMOD,HI-MTF ORDER DRIVEN -HMTF,HI-MTF -HRFQ,HI-MTF RFQ -IBEQ,INVEST BANCA - IBIS EQUITY - SYSTEMATIC INTERNALISER -IBIS,INVEST BANCA - SYSTEMATIC INTERNALISER -IMMH,IMMH - INTESA SANPAOLO - SYSTEMATIC INTERNALISER -ISBV,BIVER BANCA - SYSTEMATIC INTERNALISER -MCAD,MTS CASH DOMESTIC - MTF -MIVX,MARKET FOR INVESTMENT VEHICLES -MOTX,ELECTRONIC BOND MARKET -MSWP,MTS INTERDEALER SWAPS MARKET -MTAA,ELECTRONIC SHARE MARKET -MTAH,BORSA ITALIANA EQUITY MTF -MTSC,MTS ITALIA -MTSO,MTS S.P.A. -MUBM,"MUFG BANK, LTD. - MILANO BRANCH - SYSTEMATIC INTERNALISER" -SEDX,SECURITISED DERIVATIVES MARKET -SSOB,BONDVISION EUROPE MTF -UBIS,UNIPOL BANCA S.P.A. -UCIT,UNICREDIT SPA - SYSTEMATIC INTERNALISER -XDMI,ITALIAN DERIVATIVES MARKET -XGME,GESTORE MERCATO ELETTRICO - ITALIAN POWER EXCHANGE -XMIL,BORSA ITALIANA S.P.A. -XMOT,EXTRAMOT -XBRV,BOURSE REGIONALE DES VALEURS MOBILIERES -XJAM,JAMAICA STOCK EXCHANGE -BAJD,BANK OF AMERICA MERRILL LYNCH - JAPAN INSTINCT X -BASX,BANK OF AMERICA MERRILL LYNCH - SIMPLEX BLAST X -BETP,BLOOMBERG TRADEBOOK JAPAN LIMITED -BNPX,BNP PARIBAS ALTERNATIVE TRADING SYSTEM -CFJP,CROSSFINDER JAPAN -CHIJ,CHI-X JAPAN -CHIS,CHI-X JAPAN SELECT -CHIV,CHI-X JAPAN VWAP CROSSING -CITD,CITI DARK -CITX,CITI MATCH - JP -CLJP,CLSA JAPAN - DARK -CMET,"CLEAR MARKETS JAPAN, INC." -CSJP,CREDIT SUISSE EQUITIES (JAPAN) LIMITED -DRCT,DAIWA DRECT -ICHK,NOMURA ICE - HK -ICKR,NOMURA ICE - KR -ICSH,NOMURA ICE - SH -ICSZ,NOMURA ICE - SZ -ICTW,NOMURA ICE - TW -KAIX,KAI-X -LXJP,BARCLAYS LX JAPAN -MAQJ,MACQUARIE INTERNAL MARKETS (JAPAN) -MIZX,MIZUHO INTERNAL CROSSING -MSMS,"MORGAN STANLEY MUFG SECURITIES CO., LTD" -MUDX,MITSUBISHI DIAMOND CROSSING -NMRJ,NOMURA SECURITIES CO LTD -NMSX,NOMURA - EXTERNAL CROSSING PLATFORM -NXJP,NX JAPAN -NXSE,NX SELECT JAPAN -NXVW,NX VWAP -ODXE,ODX - OSAKA DIGITAL EXCHANGE -SBIJ,JAPANNEXT - J - MARKET -SBIU,JAPANNEXT - U - MARKET -SBIV,JAPANNEXT - VWAP CROSSING -SIGJ,SIGMA X JAPAN -SMBC,SMBC NIKKO SNET DARKPOOL - SYSTEMATIC INTERNALISER -TWJP,TRADEWEB JAPAN KK - PTS -TWJT,TRADEWEB JAPAN KK - ETP -UTSL,UEDA TRADITION SECURITIES LTD. -XFKA,FUKUOKA STOCK EXCHANGE -XJPX,JAPAN EXCHANGE GROUP -XKAC,OSAKA DOJIMA COMMODITY EXCHANGE -XNGO,NAGOYA STOCK EXCHANGE -XNKS,CENTRAL JAPAN COMMODITIES EXCHANGE -XOSE,OSAKA EXCHANGE -XOSJ,OSAKA EXCHANGE J-NET -XSAP,SAPPORO SECURITIES EXCHANGE -XSBI,JAPANNEXT - X - MARKET -XTAM,TOKYO STOCK EXCHANGE-TOKYO PRO MARKET -XTFF,TOKYO FINANCIAL EXCHANGE -XTK1,TOKYO STOCK EXCHANGE - TOSTNET-1 -XTK2,TOKYO STOCK EXCHANGE - TOSTNET-2 -XTK3,TOKYO STOCK EXCHANGE - TOSTNET-3 -XTKS,TOKYO STOCK EXCHANGE -XTKT,TOKYO COMMODITY EXCHANGE -AMNL,AMMAN STOCK EXCHANGE - NON-LISTED SECURITIES MARKET -XAMM,AMMAN STOCK EXCHANGE -AIXK,ASTANA INTERNATIONAL EXCHANGE LTD -CCEX,CASPY COMMODITY EXCHANGE -ETSC,ETS EURASIAN TRADING SYSTEM COMMODITY EXCHANGE -XKAZ,KAZAKHSTAN STOCK EXCHANGE -XNAI,NAIROBI STOCK EXCHANGE -GSXK,GSX KOREA -XKCM,KOREA EXCHANGE COMMODITY MARKET -XKEM,KOREA EXCHANGE EMISSIONS MARKET -XKFB,KOREA FREEBOARD MARKET -XKFE,KOREA EXCHANGE (FUTURES MARKET) -XKON,KOREA NEW EXCHANGE -XKOS,KOREA EXCHANGE (KOSDAQ) -XKRX,KOREA EXCHANGE (STOCK MARKET) -XKUW,KUWAIT STOCK EXCHANGE -XKSE,KYRGYZ STOCK EXCHANGE -XLAO,LAO SECURITIES EXCHANGE -FNLV,FIRST NORTH LATVIA -SWLV,SWEDBANK LATVIA - SYSTEMATIC INTERNALISER -XRIS,NASDAQ RIGA AS -XBEY,BOURSE DE BEYROUTH - BEIRUT STOCK EXCHANGE -XLSM,LIBYAN STOCK MARKET -ARTX,ARTEX EUROPE -XLGT,LGT BANK AG - SYSTEMATIC INTERNALISER -XLLB,LIECHTENSTEINISCHE LANDESBANK AG - SYSTEMATIC INTERNALISER -XNOM,NOMISMA (LIECHTENSTEIN) AG -XVPB,VP BANK AG - SYSTEMATIC INTERNALISER -BAPX,BALTPOOL -FNLT,FIRST NORTH LITHUANIA -GETB,LITHUANIAN NATURAL GAS EXCHANGE -SEBL,SEB LITHUANIA - SYSTEMATIC INTERNALISER -SWLT,SWEDBANK LITHUANIA - SYSTEMATIC INTERNALISER -XLIT,AB NASDAQ VILNIUS -BCEE,"BANQUE ET CAISSE D'EPARGNE DE L'ETAT, LUXEMBOURG - BCEE - SYSTEMATIC INTERNALISER" -BGLU,BGL BNP PARIBAS - SYSTEMATIC INTERNALISER -BILU,BANQUE INTERNATIONALE A LUXEMBOURG S.A. - SYSTEMATIC INTERNALISER -BLUX,BANQUE DE LUXEMBOURG - SYSTEMATIC INTERNALISER -CCLX,FINESTI S.A. -DBLX,DEUTSCHE BANK LUXEMBOURG S.A. -DHLX,DEUTSCHE HOLDINGS (LUXEMBOURG) S.A R.L. -EMTF,EURO MTF -KBLC,KBL EURO COMMERCIAL PAPERS -KBLL,KBL EUROPEAN PRIVATE BANKERS S.A. -KBLS,KBL STRUCTURED PRODUCTS -KBLT,KBL FX TREASURY DERIVATIVES -RBCB,RBC INVESTOR SERVICES BANK S.A. -RBSI,RBC INVESTOR SERVICES BANK S.A. - SYSTEMATIC INTERNALISER -XLUX,LUXEMBOURG STOCK EXCHANGE -XVES,VESTIMA -XMAE,MACEDONIAN STOCK EXCHANGE -XMDG,MARCHE INTERBANCAIRE DES DEVISES M.I.D. -XMSW,MALAWI STOCK EXCHANGE -MESQ,ACE MARKET -XKLS,BURSA MALAYSIA -XLFX,LABUAN INTERNATIONAL FINANCIAL EXCHANGE -XRBM,RINGGIT BOND MARKET -MALX,MALDIVES STOCK EXCHANGE -COMG,COMMERG LTD. -IFSM,INSTITUTIONAL FINANCIAL SECURITIES MARKET -PROS,PROSPECTS -XMAL,MALTA STOCK EXCHANGE -AFEX,AFRINEX LIMITED -GBOT,BOURSE AFRICA LIMITED -XAFX,AFRICAN STOCK EXCHANGE -XMAU,STOCK EXCHANGE OF MAURITIUS LTD -BIVA,BOLSA INSTITUCIONAL DE VALORES -CGMX,CITI MEXICO RPI (RETAIL PRICE IMPROVEMENT) -XEMD,MERCADO MEXICANO DE DERIVADOS -XMEX,BOLSA MEXICANA DE VALORES (MEXICAN STOCK EXCHANGE) -XMOL,MOLDOVA STOCK EXCHANGE -XULA,MONGOLIAN STOCK EXCHANGE -XMNX,MONTENEGRO STOCK EXCHANGE -XCAS,CASABLANCA STOCK EXCHANGE -XBVM,MOZAMBIQUE STOCK EXCHANGE -XNAM,NAMIBIAN STOCK EXCHANGE -XNEP,NEPAL STOCK EXCHANGE -XNZE,NEW ZEALAND EXCHANGE LTD -XMAN,BOLSA DE VALORES DE NICARAGUA -NASX,NASD OTC MARKET -XNSA,THE NIGERIAN STOCK EXCHANGE -CNSI,CARNEGIE AS - SYSTEMATIC INTERNALISER -ELEU,NASDAQ OSLO ASA - EUROPEAN POWER AND GAS DERIVATIVES -ELNO,NASDAQ OSLO ASA - NORDIC POWER DERIVATIVES AND EUROPEAN UNION ALLOWANCES -ELSE,NASDAQ OSLO ASA - SWEDISH ELECTRICITY CERTIFICATE -ELUK,NASDAQ COMMODITIES - GBP POWER/ENERGY -FISH,FISH POOL ASA -FREI,NASDAQ COMMODITIES - FREIGHT COMMODITY -FSHX,FISHEX -MERD,MERKUR MARKET - DARK POOL -MERK,EURONEXT GROWTH - OSLO -NEXO,NOREXECO ASA -NIBR,NORWEGIAN INTER BANK OFFERED RATE -NOPS,NORD POOL SPOT AS -NORX,NASDAQ OSLO ASA -NOSC,NOS CLEARING ASA -NOTC,NORWEGIAN OVER THE COUNTER MARKET -OAPA,OSLO BORS - APA -OSLC,SIX X-CLEAR AG -SB1M,SPAREBANK 1 MARKETS - SYSTEMATIC INTERNALISER -SPTR,SPAREBANK 1 SMN - SYSTEMATIC INTERNALISER -XABG,ABG SUNDAL COLLIER ASA - SYSTEMATIC INTERNALISER -XDNB,DNB BANK ASA - SYSTEMATIC INTERNALISER -XIMA,INTERNATIONAL MARTIME EXCHANGE -XOAA,OSLO BORS ASA - OSLO AXESS LIT X AUCTIONS -XOAD,OSLO AXESS NORTH SEA - DARK POOL -XOAM,NORDIC ALTERNATIVE BOND MARKET -XOAS,EURONEXT EXPAND OSLO -XOBD,OSLO BORS - DERIVATIVES MARKET -XOSA,OSLO BORS - LIT X AUCTIONS -XOSC,OSLO CONNECT -XOSD,OSLO BORS NORTH SEA - DARK POOL -XOSL,OSLO BORS -XMUS,MUSCAT SECURITIES MARKET -NCEL,PAKISTAN MERCANTILE EXCHANGE -XKAR,THE PAKISTAN STOCK EXCHANGE LIMITED -XPAE,PALESTINE SECURITIES EXCHANGE -XPTY,"BOLSA DE VALORES DE PANAMA, S.A." -XPOM,PNGX MARKETS LIMITED -XVPA,BOLSA DE VALORES Y PRODUCTOS DE ASUNCION SA -XLIM,BOLSA DE VALORES DE LIMA -CLPH,CLSA PHILIPPINES - DARK -PDEX,PHILIPPINE DEALING AND EXCHANGE CORP -XPHS,"PHILIPPINE STOCK EXCHANGE, INC." -BHWA,BANK HANDLOWY W WARSZAWIE S.A. - SYSTEMATIC INTERNALISER -BNPP,BANK BGZ BNP PARIBAS S.A. - SYSTEMATIC INTERNALISER -BOSP,WARSAW STOCK EXCHANGE/BONDS/CATALYST/BONDSPOT/MTF -BPKO,PKO BANK POLSKI S.A. - SYSTEMATIC INTERNALISER -CAPL,CREDIT AGRICOLE BANK POLSKA S.A. - SYSTEMATIC INTERNALISER -HBPL,HSBC BANK POLSKA S.A. - SYSTEMATIC INTERNALISER -INGW,ING BANK SLASKI SA - SYSTEMATIC INTERNALISER -MBPL,MBANK S.A. - SYSTEMATIC INTERNALISER -MSDM,MICHAEL/STROM DOM MAKLERSKI SPOLKA AKCYJNA - SYSTEMATIC INTERNALISER -PARK,PARKER POLAND SP. ZO.O. -PKOP,BANK POLSKA KASA OPIEKI S.A. - SYSTEMATIC INTERNALISER -PLPD,WARSAW STOCK EXCHANGE/POLISH POWER EXCHANGE/REGULATED MARKET -PLPO,WARSAW STOCK EXCHANGE/COMMODITIES/POLISH POWER - EXCHANGE/ORGANIZED TRADING FACILITIES -PLPS,WARSAW STOCK EXCHANGE/COMMODITIES/POLISH POWER EXCHANGE/SPOT -PTPG,POLISH TRADING POINT -RPWC,WARSAW STOCK EXCHANGE/BONDS/CATALYST/BONDSPOT/REGULATED MARKET -SIAB,ALIOR BANK - SYSTEMATIC INTERNALISER -TBSA,WARSAW STOCK EXCHANGE/BONDS/TREASURY BOND/BONDSPOT/B2C MARKET/MTF -TBSP,WARSAW STOCK EXCHANGE/BONDS/BONDSPOT/TREASURY BOND MARKET -WBCL,WARSAW STOCK EXCHANGE/BONDS/CATALYST/LISTING -WBKP,BANK ZACHODNI WBK S.A. - SYSTEMATIC INTERNALISER -WBON,WARSAW STOCK EXCHANGE/ BONDS/CATALYST/MAIN MARKET -WDER,WARSAW STOCK EXCHANGE/FINANCIAL DERIVATIVES -WETP,WARSAW STOCK EXCHANGE/ ETPS -WIND,WARSAW STOCK EXCHANGE/INDICES -WIPO,WARSAW STOCK EXCHANGE - OTHER THAN XOFF OR XXXX -WMTF,WARSAW STOCK EXCHANGE/BONDS/CATALYST/MTF -XGLO,WARSAW STOCK EXCHANGE/ EQUITIES/GLOBAL CONNECT - MTF -XNCO,WARSAW STOCK EXCHANGE/ EQUITIES/NEW CONNECT - MTF -XWAR,WARSAW STOCK EXCHANGE/EQUITIES/MAIN MARKET -ALXL,EURONEXT GROWTH LISBON -BFPT,BANCO FINANTIA S.A. - SYSTEMATIC INTERNALISER -ENXL,EURONEXT ACCESS LISBON -MBCP,MILLENNIUM BCP-SYSTEMATIC INTERNALISER -MFOX,EURONEXT - MERCADO DE FUTUROS E OPÇÕES -OMIC,THE IBERIAN ENERGY CLEARING HOUSE -OMIP,"OMIP - POLO PORTUGUES, S.G.M.R., S.A. / OMIP DERIVATIVES MARKET" -OPEX,PEX-PRIVATE EXCHANGE -WQXL,EURONEXT - MARKET WITHOUT QUOTATIONS LISBON -XLIS,EURONEXT - EURONEXT LISBON -DSMD,QATAR EXCHANGE -TRPX,MERJ EXCHANGE LIMITED -BRDE,BRD - GROUPE SOCIETE GENERALE S.A. -BRDL,BRD - GROUPE SOCIETE GENERALE S.A. - LIQUIDITY PROVIDER -BRDS,BRD - GROUPE SOCIETE GENERALE S.A. - SYSTEMATIC INTERNALISER -BTRL,BANCA TRANSILVANIA S.A. -EMCE,ROMANIAN COMMODITIES EXCHANGE - OTF -OTPR,OTP BANK ROMANIA SA -XBRM,ROMANIAN COMMODITIES EXCHANGE -XBSD,DERIVATIVES REGULATED MARKET - BVB -XBSE,SPOT REGULATED MARKET - BVB -XCAN,CAN - ATS -XRAS,RASDAQ -XRPM,ROMANIAN POWER MARKET -MISX,MOSCOW EXCHANGE - ALL MARKETS -NAMX,NATIONAL MERCANTILE EXCHANGE -RPDX,MOSCOW ENERGY EXCHANGE -RTSX,MOSCOW EXCHANGE - DERIVATIVES MARKET -RUSX,NON-PROFIT PARTNERSHIP FOR THE DEVELOPMENT OF FINANCIAL MARKET RTS -SPBE,SPB EXCHANGE - ALL MARKETS -SPIM,ST. PETERSBURG INTERNATIONAL MERCANTILE EXCHANGE -XMOS,CENTRAL COUNTERPARTY CLEARING CENTER MFB - JOINT-STOCK COMPANY -XPET,STOCK EXCHANGE SAINT PETERSBURG -XPIC,SAINT-PETERSBURG CURRENCY EXCHANGE -XRUS,INTERNET DIRECT-ACCESS EXCHANGE -XSAM,SAMARA CURRENCY INTERBANK EXCHANGE -XSIB,SIBERIAN EXCHANGE -ROTC,RWANDA OTC MARKET -RSEX,RWANDA STOCK EXCHANGE -XECS,EASTERN CARIBBEAN SECURITIES EXCHANGE -SVXI,SAINT VINCENT AND THE GRENADINES SECURITIES EXCHANGE -XSAU,SAUDI STOCK EXCHANGE -XBEL,BELGRADE STOCK EXCHANGE -SECC,SECDEX CLEARING LIMITED -SECD,SECDEX DEPOSITORY LIMITED -SECE,SECDEX EXCHANGE LIMITED -SEDC,SECDEX DIGITAL CUSTODIAN LIMITED -APCL,ASIA PACIFIC CLEAR -APEX,ASIA PACIFIC EXCHANGE -BBLX,BONDBLOX EXCHANGE -BTBS,BLOOMBERG TRADEBOOK SINGAPORE PTE LTD -CLTD,CLEARTRADE EXCHANGE -CNOD,CNODE -ECAL,EUREX CLEARING ASIA PTE. LTD. -EEAL,EUREX EXCHANGE ASIA PTE. LTD. -ENMS,EURONEXT MARKETS SINGAPORE PTE. LTD. -EQOC,EQUOS SPOT -EQOD,EQUOS DERIVATIVES -EQOS,EQUOS -FXSM,REFINITIV MATCHING NDFS SINGAPORE -IFSG,ICE FUTURES SINGAPORE -JADX,JOINT ASIAN DERIVATIVES EXCHANGE -MALM,MARKETAXESS SINGAPORE PTE LIMITED - LIVE MARKETS -MASG,MARKETAXESS SINGAPORE PTE LIMITED -OSSG,OSL SG EXCHANGES -RTSP,REFINITIV TRANSACTION SERVICES PTE. LTD -SMEX,SINGAPORE MERCANTILE EXCHANGE PTE LTD -SYFX,SYNOPTION -TERM,TP ICAP FX HUB -TFSA,TFS GREEN AUSTRALIAN GREEN MARKETS -TSIG,TRADITION SINGAPORE PTE. LTD. -TWSG,TRADEWEB EUROPE LIMITED - SINGAPORE -XABX,ABAXX EXCHANGE -XSBT,SINGAPORE EXCHANGE BOND TRADING PTE. LTD -XSCA,SINGAPORE CATALIST MARKET -XSCE,SINGAPORE COMMODITY EXCHANGE -XSES,SINGAPORE EXCHANGE -XSIM,SINGAPORE EXCHANGE DERIVATIVES CLEARING LIMITED -CBSK,"CESKOSLOVENSKA OBCHODNA BANKA, A.S. - SYSTEMATIC INTERNALISER" -EBRA,BRATISLAVA STOCK EXCHANGE - MTF -SPXE,SPX -XBRA,BRATISLAVA STOCK EXCHANGE -SKBB,SKB BANKA D.D. LJUBLJANA -XLJM,SI ENTER -XLJU,LJUBLJANA STOCK EXCHANGE (OFFICIAL MARKET) -XSOP,BSP REGIONAL ENERGY EXCHANGE - SOUTH POOL -4AXE,4 AFRICA EXCHANGE (PTY) LTD -A2XX,A2X -ALTX,JSE ALTERNATE EXCHANGE -JSEB,JSE BOND ELECTRONIC TRADING PLATFORM -JSER,JSE REPO MARKET -RMMS,RMB MORGAN STANLEY -RMMX,RMB MORGAN STANLEY - INVENTORY MANAGER -XBES,JSE CASH BOND MARKET -XJSE,JOHANNESBURG STOCK EXCHANGE -XSAF,JSE EQUITY DERIVATIVES MARKET -XSFA,JSE COMMODITY DERIVATIVES MARKET -YLDX,JSE INTEREST RATE DERIVATIVES MARKET -ZARX,ZAR X -ZFXM,JSE CURRENCY DERIVATIVES MARKET -ABAN,ABANCA -AGBP,A AND G BANCA PRIVADA SAU -ALLT,ALLT - OTF -BBVA,BANCO BILBAO VIZCAYA ARGENTARIA S.A - SYSTEMATIC INTERNALISER -BCMA,BANCA MARCH S.A. - SYSTEMATIC INTERNALISER -BMCL,BME CLEARING S.A. -BMEA,BME - APA -BMEX,BME - BOLSAS Y MERCADOS ESPANOLES -BSAB,"BANCO DE SABADELL, S.A. - SYSTEMATIC INTERNALISER" -CABK,CAIXABANK - SYSTEMATIC INTERNALISER -CAPI,CAPI - OTF -CECA,CECABANK - SYSTEMATIC INTERNALISER -CIMA,CIMD S.V. S.A. - APPROVED PUBLICATION ARRANGEMENT -CIMB,CIMD S.V. S.A. - OTF FOR BONDS -CIMD,CIMD S.V. S.A. - OTF -CIME,CIMD S.V. S.A. - OTF FOR ENERGY DERIVATIVES -CIMV,CIMD S.V. S.A. - OTF FOR DERIVATIVES -CMAP,CAPI - APPROVED PUBLICATION ARRANGEMENT -CSMD,"CREDIT SUISSE BANK (EUROPE), S.A." -DOWE,DOWGATE EURO GOVERNMENT BONDS -DOWM,DOWGATE - MTF (MADRID) -GROW,BME GROWTH MARKET -IBER,IBERCAJA BANCO SA -IBGH,IBERIAN GAS HUB -MABX,BME MTF EQUITY (IIC AND ECR SEGMENTS) -MARF,MERCADO ALTERNATIVO DE RENTA FIJA -MDRV,MIBGAS - DERIVATIVES -MERF,MERCADO ELECTRONICO DE RENTA FIJA -MIBG,MERCADO ORGANIZADO DEL GAS -OMEL,OMI POLO ESPANOL S.A. (OMIE) -PAVE,ALTERNATIVE PLATFORM FOR SPANISH SECURITIES -POSE,PORTFOLIO STOCK EXCHANGE -SANT,BANCO SANTANDER S.A. - SYSTEMATIC INTERNALISER -SBAR,BOLSA DE BARCELONA RENTA FIJA -SBIL,BOLSA DE BILBAO RENTA FIJA -SEND,SEND - SISTEMA ELECTRONICO DE NEGOCIACION DE DEUDA -SIMD,"CREDIT SUISSE BANK (EUROPE), S.A. - SYSTEMATIC INTERNALISER" -TEUR,TRADITION ESPANA OTF -XBAR,BOLSA DE BARCELONA -XBIL,BOLSA DE VALORES DE BILBAO -XDPA,CADE - MERCADO DE DEUDA PUBLICA ANOTADA -XDRF,AIAF - MERCADO DE RENTA FIJA -XLAT,LATIBEX -XMAD,BOLSA DE MADRID -XMFX,MEFF FX FINANCIAL DERIVATIVES -XMPW,MEFF POWER DERIVATIVES -XMRV,MEFF FINANCIAL DERIVATIVES -XNAF,SISTEMA ESPANOL DE NEGOCIACION DE ACTIVOS FINANCIEROS -XVAL,BOLSA DE VALENCIA -XCOL,COLOMBO STOCK EXCHANGE -XKHA,KHARTOUM STOCK EXCHANGE -XSWA,SWAZILAND STOCK EXCHANGE -CASI,CARNEGIE INVESTMENT BANK AB - SYSTEMATIC INTERNALISER -CRYD,CRYEX - FX AND DIGITAL CURRENCIES -CRYX,CRYEX -CSTO,NASDAQ CLEARING AB -DKED,NASDAQ STOCKHOLM AB - DANISH EQ DERIVATIVES -DKFI,NASDAQ STOCKHOLM AB - DANISH FI DERIVATIVES -DKOB,NASDAQ STOCKHOLM AB - OTC DKK WB EQ DERIVATIVES -DKWB,NASDAQ STOCKHOLM AB - DKK WB EQ DERIVATIVES -DNSE,FIRST NORTH SWEDEN - NORDIC@MID -DOSE,FIRST NORTH SWEDEN - NORWAY NORDIC@MID -DSTO,NASDAQ STOCKHOLM AB - NORDIC@MID -EBON,NASDAQ STOCKHOLM AB - EUR FI DERIVATIVES -ESTO,NASDAQ STOCKHOLM AB - NORWAY ETF -EUOB,NASDAQ STOCKHOLM AB - OTC EUR WB EQ DERIVATIVES -EUWB,NASDAQ STOCKHOLM AB - EUR WB EQ DERIVATIVES -FIED,NASDAQ STOCKHOLM AB - FINNISH EQ DERIVATIVES -FNSE,FIRST NORTH SWEDEN -GBOB,NASDAQ STOCKHOLM AB - OTC GBP WB EQ DERIVATIVES -GBWB,NASDAQ STOCKHOLM AB - GBP WB EQ DERIVATIVES -GFKS,GARANTUM FONDKOMMISSION AB - SYSTEMATIC INTERNALISER -MNSE,FIRST NORTH SWEDEN - AUCTION ON DEMAND -MOSE,FIRST NORTH SWEDEN - NORWAY AUCTION ON DEMAND -MSTO,NASDAQ STOCKHOLM AB - AUCTION ON DEMAND -NAPA,NASDAQ STOCKHOLM AB - APA SERVICE -NMTF,NORDIC MTF -NOED,NASDAQ STOCKHOLM AB - NORWEGIAN EQ DERIVATIVES -NOFI,NASDAQ STOCKHOLM AB - NORWEGIAN FI DERIVATIVES -NOOB,NASDAQ STOCKHOLM AB - OTC NOK WB EQ DERIVATIVES -NOWB,NASDAQ STOCKHOLM AB - NOK WB EQ DERIVATIVES -NSME,NORDIC SME -NSPO,NASDAQ SPOT AB -ONSE,FIRST NORTH SWEDEN - NORWAY -PEPH,PEPINS - MTF - HALF-YEAR -PEPM,PEPINS - MTF - MONTH -PEPQ,PEPINS - MTF - QUARTER -PEPW,PEPINS - MTF -PEPY,PEPINS - MTF - YEAR -PNED,NASDAQ STOCKHOLM AB - PAN-NORDIC EQ DERIVATIVES -SEBS,SEB - SYSTEMATIC INTERNALISER -SEED,NASDAQ STOCKHOLM AB - SWEDISH EQ DERIVATIVES -SEOB,NASDAQ STOCKHOLM AB - OTC SEK WB EQ DERIVATIVES -SEWB,NASDAQ STOCKHOLM AB - SEK WB EQ DERIVATIVES -SSME,FIRST NORTH SWEDEN - SME GROWTH MARKET -SVES,SVENSKA HANDELSBANKEN AB - SYSTEMATIC INTERNALISER -SVEX,SVENSKA HANDELSBANKEN AB - SVEX -SWBI,SWEDBANK - SYSTEMATIC INTERNALISER -USOB,NASDAQ STOCKHOLM AB - OTC USD WB EQ DERIVATIVES -USWB,NASDAQ STOCKHOLM AB - USD WB EQ DERIVATIVES -XABC,ABG SUNDAL COLLIER AB - SYSTEMATIC INTERNALISER -XNGM,NORDIC GROWTH MARKET -XOPV,OTC PUBLICATION VENUE -XSAT,SPOTLIGHT STOCK MARKET AB -XSTO,NASDAQ STOCKHOLM AB -CBOE,CBOE FX NDFS -CCMS,CONTINENTAL CAPITAL MARKETS S.A. -CSAG,CREDIT SUISSE AG -CSOT,CREDIT SUISSE AG - OHS -CSZH,CREDIT SUISSE (CH) -DOTS,SWISS DOTS BY CATS -EBSC,"EBS MARKET- CLOB - FOR THE TRADING OF SPOT FX, PRECIOUS METALS AND OTHER FX PRODUCTS" -EBSS,EBS SERVICE COMPANY LIMITED - ALL MARKETS -KMUX,ZURCHER KANTONALBANK - EKMU-X -OTXB,BERNER KANTONALBANK OTC-X -ROSR,SIX REPO AG -S3FM,SOCIETY3 FUNDERSMART -STOX,STOXX LIMITED -UBSC,UBS PIN-FX -UBSF,UBS FX -UBSG,UBS TRADING -VLEX,VONTOBEL LIQUIDITY EXTENDER -XBRN,BX SWISS AG -XBTR,SIX SWISS BILATERAL TRADING PLATFORM FOR STRUCTURED OTC PRODUCTS -XDLP,SIX SWISS EXCHANGE - DEAL POOL - BOOK BUILDING AND ANNOUNCEMENT PLATFORM -XQMH,SIX SWISS EXCHANGE - STRUCTURED PRODUCTS -XQOD,SIX SWISS EXCHANGE - ETF QOD -XREP,SIX REPO AG - CH REPO MARKET -XROT,SIX REPO AG - OTC SPOT MARKET -XSCU,STOXX LIMITED - CUSTOMIZED INDICES -XSDX,SIX DIGITAL EXCHANGE -XSEB,SIX SWISS EXCHANGE - EBBO BOOK -XSTV,STOXX LIMITED - VOLATILITY INDICES -XSTX,STOXX LIMITED - INDICES -XSWM,SIX SWISS EXCHANGE - SIX SWISS EXCHANGE AT MIDPOINT -XSWX,SIX SWISS EXCHANGE -ZKBX,ZURCHER KANTONALBANK SECURITIES EXCHANGE -XDSE,DAMASCUS SECURITIES EXCHANGE -CFTW,CROSSFINDER TAIWAN -GSXT,GSX TAIWAN -ROCO,TAIPEI EXCHANGE -XTAF,TAIWAN FUTURES EXCHANGE -XTAI,TAIWAN STOCK EXCHANGE -XDAR,DAR ES SALAAM STOCK EXCHANGE -AFET,AGRICULTURAL FUTURES EXCHANGE OF THAILAND -BEEX,BOND ELECTRONIC EXCHANGE -TFEX,THAILAND FUTURES EXCHANGE -XBKF,STOCK EXCHANGE OF THAILAND - FOREIGN BOARD -XBKK,STOCK EXCHANGE OF THAILAND -XMAI,MARKET FOR ALTERNATIVE INVESTMENT -ABNA,ABN AMRO BANK NV - SYSTEMATIC INTERNALISER -ABNC,ABN AMRO CLEARING BANK - SYSTEMATIC INTERNALISER -AFSA,AFS - OTF -AFSE,AFS E-VENUES B.V. -AFSI,AFS - OTF - INTEREST RATE DERIVATIVES -AFSL,AFS - OTF - STRUCTURED PRODUCTS -AFSO,AFS - OTF - BONDS -AFSX,AFS - OTF - FX FORWARDS -BAPE,BLOOMBERG DATA REPORTING SERVICES B.V. - APA -BARU,CBOE EUROPE - REGULATED MARKET DARK BOOK (NL) -BEUO,CBOE EUROPE - REGULATED MARKET OFF BOOK (NL) -BEUP,CBOE EUROPE - DXE PERIODIC (NL) -BEUT,CBOE EUROPE - REGULATED MARKET INTEGRATED BOOK (NL) -BTAM,CME AMSTERDAM B.V. -BTFE,BLOOMBERG TRADING FACILITY B.V. -BTQE,CME AMSTERDAM B.V. - RFQ TRADING PROTOCOL -CABV,CME AMSTERDAM B.V. - APA -CAPA,CBOE EUROPE - APA (NL) -CAVD,CAVENDISH MARKETS B.V. -CCRM,CBOE EUROPE REGULATED MARKETS (NL) -CCXE,CBOE EUROPE EQUITIES - EUROPEAN EQUITIES (NL) -CEDX,CBOE EUROPE DERIVATIVES -CEUD,CBOE EUROPE - DXE DARK ORDER BOOK (NL) -CEUO,CBOE EUROPE - DXE OFF-BOOK (NL) -CEUX,CBOE EUROPE - DXE ORDER BOOKS (NL) -CLMX,CLIMEX -COMM,COMMERG -CPTX,CAPTIN -EBSF,EBS MTF - FX PRODUCTS - RFQ SEGMENT -EBSN,EBS MTF -ECEU,TRADECHO EU APA -ETPA,ENERGY TRADING PLATFORM AMSTERDAM -FLTB,FLOW TRADERS B.V. - SYSTEMATIC INTERNALISER BONDS -FLTR,FLOW TRADERS - SYSTEMATIC INTERNALISER -FLWX,FLOW TRADERS -GMGE,GMG EUROPE B.V. -HCHC,ICE CLEAR NETHERLANDS B.V. -IEBS,EBS MTF - RFQ - FX PRODUCTS FOR ASSET MANAGERS -IMCO,ICE ENDEX OTF FUTURES -IMCT,IMC - SYSTEMATIC INTERNALISER -IMEQ,ICE MARKETS EQUITY -INGB,ING BANK NV - SYSTEMATIC INTERNALISER -INGE,ING BANK NV - SPRINTERS AND EQUITY - SYSTEMATIC INTERNALISER -INGF,ING BANK NV - FOREIGN EXCHANGE - SYSTEMATIC INTERNALISER -ISWN,ISWAP EURO MTF - TRADE REGISTRATION -ISWO,ISWAP EURO MTF - ORDERBOOK -ISWP,ISWAP EURO MTF -ISWT,ISWAP EURO MTF - TARGETED STREAMING / RFQ -JLEU,JUMP LIQUIDITY EU EQUITY PLATFORM -JNSI,JANE STREET NETHERLANDS B.V. - SYSTEMATIC INTERNALISER -LEBV,LEDGEREDGE B.V. -LISZ,CBOE EUROPE - LIS SERVICE (NL) -MANL,MARKETAXESS NL B.V. -MHBE,MIZUHO BANK EUROPE N.V. - SYSTEMATIC INTERNALISER -MUBE,MUFG BANK (EUROPE) N.V. - SYSTEMATIC INTERNALISER -MUSN,MUFG SECURITIES (EUROPE) N.V - -MXNL,MARKETAXESS NL B.V. - LIVE MARKETS -NDEX,ICE ENDEX FUTURES -NDXS,ICE ENDEX EUROPEAN GAS SPOT -NIBC,NIBC - SYSTEMIC INTERNALISER -NLPX,APX POWER NL -NPEX,NPEX -NWNV,NATWEST MARKETS NV -OHVO,OHV OTF -RABO,RABOBANK - SYSTEMATIC INTERNALISER -RESF,EBS MTF - RESET SEGMENT -RR4G,ROUTE4GAS -STXS,STX FIXED INCOME -TNLA,EURONEXT - TRADED BUT NOT LISTED AMSTERDAM -TOWR,TOWER RESEARCH CAPITAL EUROPE - SYSTEMATIC INTERNALISER -TQEA,TURQUOISE EUROPE - LIT AUCTIONS -TQEB,TURQUOISE EUROPE - NYLON CASH ORDER BOOK -TQEM,TURQUOISE EUROPE - DARK -TQEX,TURQUOISE EUROPE -TRNL,MARKETAXESS POST-TRADE B.V. - APA -TWEA,TRADEWEB EU BV - APA -TWEM,TRADEWEB EU BV - MTF -TWEO,TRADEWEB EU BV - OTF -TWEU,TRADEWEB EU BV -XAMS,EURONEXT - EURONEXT AMSTERDAM -XEMS,EMS EXCHANGE -XEUC,"EURONEXT COM, COMMODITIES FUTURES AND OPTIONS" -XEUE,"EURONEXT EQF, EQUITIES AND INDICES DERIVATIVES" -XEUI,"EURONEXT IRF, INTEREST RATE FUTURE AND OPTIONS" -XNXC,NXCHANGE -XNXD,NXCHANGE B.V. MTF -XTRN,TRINIDAD AND TOBAGO STOCK EXCHANGE -BTUN,TUNIS STOCK EXCHANGE (BOURSE DE TUNIS) - BONDS MARKET -XTUN,TUNIS STOCK EXCHANGE (BOURSE DE TUNIS) -EWRM,TURKISH MERCANTILE EXCHANGE - ELECTRONIC WAREHOUSE RECEIPT MARKET -EXTR,ENERGY EXCHANGE ISTANBUL -TMEX,TURKISH MERCANTILE EXCHANGE -XDSM,BORSA ISTANBUL - DEBT SECURITIES MARKET -XEDA,ELECTRICITY DAY-AHEAD MARKET -XEID,ELECTRICITY INTRA-DAY MARKET -XEQY,BORSA ISTANBUL - EQUITY MARKET -XFNO,BORSA ISTANBUL - FUTURES AND OPTIONS MARKET -XIST,BORSA ISTANBUL -XPMS,BORSA ISTANBUL - PRECIOUS METALS AND DIAMONDS MARKETS -ULTX,ALT XCHANGE (U) -XUGA,UGANDA SECURITIES EXCHANGE -EESE,EAST EUROPEAN STOCK EXCHANGE -PFTQ,PFTS QUOTE DRIVEN -PFTS,PFTS STOCK EXCHANGE -SEPE,STOCK EXCHANGE PERSPECTIVA -UICE,UKRAINIAN INTERBANK CURRENCY EXCHANGE -UKEX,UKRAINIAN EXCHANGE -XDFB,JOINT-STOCK COMPANY “STOCK EXCHANGE INNEX” -XKHR,KHARKOV COMMODITY EXCHANGE -XKIE,KIEV UNIVERSAL EXCHANGE -XKIS,KIEV INTERNATIONAL STOCK EXCHANGE -XNDU,PJSC NATIONAL DEPOSITORY OF UKRAINE -XODE,ODESSA COMMODITY EXCHANGE -XPRI,PRIDNEPROVSK COMMODITY EXCHANGE -XUAX,UKRAINIAN STOCK EXCHANGE -XUKR,UKRAINIAN UNIVERSAL COMMODITY EXCHANGE -ABXX,YOSHI MARKETS LIMITED -DEXE,DEX LIMITED -DGCX,DUBAI GOLD & COMMODITIES EXCHANGE DMCC -DIFX,NASDAQ DUBAI -DUMX,DUBAI MERCANTILE EXCHANGE -GLOM,GLOMAX EXCHANGE LTD -GMGD,GMG DUBAI LIMITED -IFAD,ICE FUTURES ABU DHABI -KRME,PAYWARD MENA HOLDINGS LTD. -MATX,MATRIX LIMITED -MIDC,MIDCHAINS -XADS,ABU DHABI SECURITIES EXCHANGE -XDFM,DUBAI FINANCIAL MARKET -3579,SSY FUTURES LTD - FREIGHT SCREEN -AFDL,ABIDE FINANCIAL DRSP LIMITED APA -AIMX,LONDON STOCK EXCHANGE - AIM MTF -AMPX,ASSET MATCH PRIVATE EXCHANGE -ANLP,AUSTRALIA AND NEW ZEALAND BANKING GROUP LIMITED -ANTS,ABBEY NATIONAL TREASURY SERVICES PLC - SYSTEMATIC INTERNALISER -ANZL,AUSTRALIA AND NEW ZEALAND BANKING GROUP LIMITED SYSTEMATIC INTERNALISER -AQXA,AQUIS EXCHANGE PLC AUCTION ON DEMAND (AOD) -AQXE,AQUIS EXCHANGE PLC -ARAX,ARRACO GLOBAL MARKETS LTD -ARCH,ARCHAX -ARIA,ARIAN TRADING FACILITY -ATLB,BRAEMAR ATLANTIC SECURITIES LTD -AUTB,AUTILLA - BASE METALS -AUTP,AUTILLA - PRECIOUS METALS -AUTX,AUTILLA -BALT,THE BALTIC EXCHANGE -BANA,"BANK OF AMERICA, NATIONAL ASSOCIATION" -BAPA,BLOOMBERG - APA -BARK,CBOE EUROPE - REGULATED MARKET DARK BOOK -BARO,CBOE EUROPE - REGULATED MARKET OFF BOOK -BART,CBOE EUROPE - REGULATED MARKET INTEGRATED BOOK -BASI,"BANK OF AMERICA, NATIONAL ASSOCIATION - LONDON BRANCH OFFICE - SYSTEMATIC INTERNALISER" -BATD,CBOE EUROPE - BXE DARK ORDER BOOK -BATE,CBOE EUROPE - BXE ORDER BOOKS -BATF,CBOE EUROPE - BXE OFF-BOOK -BATP,CBOE EUROPE - BXE PERIODIC -BBSI,BARCLAYS BANK PLC - SYSTEMATIC INTERNALISER -BBSX,BB SECURITIES LTD -BBVX,BANCO BILBAO VIZCAYA ARGENTARIA S.A. - UK - SYSTEMATIC INTERNALISER -BCRM,CBOE EUROPE REGULATED MARKETS -BCSI,BARCLAYS CAPITAL SECURITIES LIMITED - SYSTEMATIC INTERNALISER -BCSL,BARCLAYS CAPITAL SECURITIES LIMITED -BCXE,CBOE EUROPE EQUITIES -BETX,BET OTC BILAT COMMODITY DERIVATIVES -BGCB,BGC BROKERS LP - TRAYPORT -BGCI,BGC BROKERS LP -BGCO,BGC BROKERS LP - OTF -BGFU,BERENBERG FIXED INCOME UK - SYSTEMATIC INTERNALISER -BGUK,BERENBERG - SYSTEMATIC INTERNALISER UK -BISI,GOLDMAN SACHS INTERNATIONAL BANK - SYSTEMATIC INTERNALISER -BKLF,BNY MELLON - SYSTEMATIC INTERNALISER -BKLN,BNY MELLON INTERNATIONAL - LONDON BRANCH -BLNK,BLINK MTF -BLOX,BLOCKMATCH DARK -BLTX,BALTEX - FREIGHT DERIVATIVES MARKET -BMCM,BMO CAPITAL MARKETS LIMITED -BMLB,BANK OF MONTREAL - LONDON BRANCH - SYSTEMATIC INTERNALISER -BMTF,BLOOMBERG TRADING FACILITY LIMITED -BNPL,BNP PARIBAS SA LONDON BRANCH - SYSTEMATIC INTERNALISER -BNTW,BLOCKMATCH MTF NT -BOAL,"BANK OF AMERICA, NATIONAL ASSOCIATION - LONDON BRANCH OFFICE" -BOAT,CINNOBER BOAT -BOFS,BANK OF SCOTLAND PLC - SYSTEMATIC INTERNALISER -BOSC,BONDSCAPE -BOTC,OFF EXCHANGE IDENTIFIER FOR OTC TRADES REPORTED TO CBOE EUROPE -BPLC,BARCLAYS BANK PLC -BRFQ,BLOCKMATCH MTF RFQ -BRGA,BRYAN GARNIER AND CO LIMITED -BRNX,BERNSTEIN CROSS (BERN-X) -BSLB,"BANCO SANTANDER, LONDON BRANCH - SYSTEMATIC INTERNALISER" -BSPL,BNP PARIBAS SECURITIES SERVICES LONDON BRANCH - SYSTEMATIC INTERNALISER -BTEE,BROKERTEC EUROPE LIMITED - ALL MARKETS -BTLX,BTL OTC BILAT COMMODITY DERIVATIVES -BTQG,BROKERTEC EUROPE LIMITED - RFQ TRADING PROTOCOL -BUYN,LCH LTD - BUY IN -BVUK,BONDVISION UK -CBAL,COMMONWEALTH BANK OF AUSTRALIA - LONDON BRANCH - SYSTEMATIC INTERNALISER -CBNL,CITIBANK N.A. LONDON -CCEU,CITADEL CONNECT EUROPE - SYSTEMATIC INTERNALISER -CCML,CONTINENTAL CAPITAL MARKETS LIMITED - OTF -CCO2,CANTORCO2E.COM LIMITED -CEPU,CITIBANK EUROPE PLC - LONDON BRANCH -CFIC,CHINAFICC -CGMC,CITIGROUP GLOBAL MARKETS LIMITED - EMEA LIQUIDITY HUB -CGMG,CITIGROUP GLOBAL MARKETS EUROPE AG - LONDON BRANCH - SYSTEMATIC INTERNALISER -CGML,CITIGROUP GLOBAL MARKETS LIMITED -CGMT,CITIGROUP GLOBAL MARKETS LIMITED - EMEA TRADING -CGMU,CITI MATCH GB CONTRA LIQUIDITY -CHEV,CA CHEUVREUX -CHID,CBOE EUROPE - CXE DARK ORDER BOOK -CHIO,CBOE EUROPE - CXE OFF-BOOK -CHIX,CBOE EUROPE - CXE ORDER BOOKS -CIBC,CANADIAN IMPERIAL BANK OF COMMERCE - SYSTEMATIC INTERNALISER -CIBP,CIBC WORLD MARKETS PLC - SYSTEMATIC INTERNALISER -CLCH,LCH LTD - CROSS NETTING -CLVE,VERTO MTF -CMCM,CMC MARKETS UK PLC - SYSTEMATIC INTERNALISER -CMEC,CME CLEARING EUROPE -CMEE,CME EUROPE -CMMT,CLEAR MARKETS EUROPE LIMITED -CRYP,CRYPTO FACILITIES -CSBX,CREDIT SUISSE AES EUROPE BENCHMARK CROSS -CSCF,CREDIT SUISSE AES CROSSFINDER EUROPE -CSEC,CITADEL SECURITIES FI - SYSTEMATIC INTERNALISER -CSEU,CREDIT SUISSE (EUROPE) -CSIN,CREDIT SUISSE INTERNATIONAL -CSLB,CREDIT SUISSE AG - LONDON BRANCH -CSSI,CREDIT SUISSE INTERNATIONAL - SYSTEMATIC INTERNALISER -CXOT,CREDITEX BROKERAGE LLP - OTF -CXRT,CREDITEX BROKERAGE LLP - MTF -DAIW,DAIWA CAPITAL MARKETS EUROPE LIMITED -DAUK,DANSKE BANK A/S - LONDON BRANCH - SYSTEMATIC INTERNALISER -DOWG,DOWGATE -DRSP,EURONEXT UK - REPORTING SERVICES -DVFX,DIGITAL VEGA -EBSO,NEX SEF - RFQ - FX PRODUCTS FOR ASSET MANAGERS -ECHO,LONDON STOCK EXCHANGE - APA -ECNL,REFINITIV TRANSACTION SERVICES LIMITED - FX SPOT ECN -ECSL,ENTERPRISE COMMODITY SERVICES LIMITED -EIXE,AQUIS EXCHANGE PLC AQUIS - EIX INFRASTRUCTURE BOND MARKET -ELIX,ELIXIUM -EMBX,EMERGING MARKETS BOND EXCHANGE LIMITED -ENCL,ENCLEAR -EQLD,EQUILEND EUROPE LIMITED -EQSL,EQUILEND EUROPE LIMITED - SWAPS -EVOL,EVOLUTION MARKETS -EXBO,EXANE BNP PARIBAS - BID-OFFER CROSSING -EXCP,EXANE BNP PARIBAS - CLOSING PRICE -EXDC,EXANE BNP PARIBAS - DIRECT CAPITAL ACCESS -EXEU,EXANE BNP PARIBAS -EXLP,EXANE BNP PARIBAS - LIQUIDITY PROVISION -EXMP,EXANE BNP PARIBAS - MID POINT -EXOR,EXANE BNP PARIBAS - CHILD ORDER CROSSING -EXOT,EXOTIX CAPITAL - OTF -EXSI,EXANE BNP PARIBAS - GB - SYSTEMATIC INTERNALISER -EXVP,EXANE BNP PARIBAS - VOLUME PROFILE CROSSING -FAIR,CANTOR SPREADFAIR -FISU,FREIGHT INVESTOR SERVICES LIMITED -FNUK,FINANTIA UK LIMITED - SYSTEMATIC INTERNALISER -FRTE,FORTE - OTF -FXGB,FXCM - MTF -FXOP,TRADITION-NEX OTF -GEMX,GEMMA (GILT EDGED MARKET MAKERS’ASSOCIATION) -GFBM,GFI BROKERS - MTF -GFBO,GFI BROKERS - OTF -GFIB,GFI BROKERS -GFIC,GFI SECURITIES LTD -GFIF,GFI FOREXMATCH -GFIN,GFI ENERGYMATCH -GFIR,GFI RATESMATCH -GFOX,GLOBAL FUTURES AND OPTIONS LTD -GFSM,GFI SECURITIES LTD - MTF -GFSO,GFI SECURITIES LTD - OTF -GMEG,GMEX EXCHANGE -GMGL,GMG BROKERS LIMITED -GRIF,GRIFFIN MARKETS LIMITED -GRIO,GRIFFIN MARKETS LIMITED - OTF -GRSE,THE GREEN STOCK EXCHANGE - ACB IMPACT MARKETS -GSIB,GOLDMAN SACHS INTERNATIONAL BANK -GSIL,GOLDMAN SACHS INTERNATIONAL -GSSI,GOLDMAN SACHS INTERNATIONAL - SYSTEMATIC INTERNALISER -HPSO,HPC OTF - UK ETRADING PLATFORM -HPSX,HPC OTF -HRSI,HUDSON RIVER TRADING - SYSTEMATIC INTERNALISER -HSBC,HSBC - SYSTEMATIC INTERNALISER -IBAL,ICE BENCHMARK ADMINISTRATION -ICEO,ICAP ENERGY - OTF -ICPM,TP ICAP UK MTF -IECL,ICAP ENERGY LTD OTF - COMMODITIES AND ENERGY DERIVATIVES -IFEN,ICE FUTURES EUROPE - OIL AND REFINED PRODUCTS DIVISION -IFEU,ICE FUTURES EUROPE -IFLL,ICE FUTURES EUROPE - FINANCIAL PRODUCTS DIVISION -IFLO,ICE FUTURES EUROPE - EQUITY PRODUCTS DIVISION -IFLS,SWAPXECUTE -IFLX,ICE FUTURES EUROPE - AGRICULTURAL PRODUCTS DIVISION -IFUT,ICE FUTURES EUROPE - EUROPEAN UTILITIES DIVISION -IGDL,ICAP GLOBAL DERIVATIVES LIMITED -ILCM,LOUIS CAPITAL MARKETS UK -IMCE,TP ICAP UK MTF - CASH EQUITY -IMED,TP ICAP UK MTF - EQUITY DERIVATIVES -IMET,TP ICAP UK MTF - EXCHANGE TRADED PRODUCTS -IMFD,TP ICAP UK MTF - FX DERIVATIVES -IMGB,TP ICAP UK MTF - GOVERNMENT BONDS EXCLUDING GILTS -IMGI,TP ICAP UK MTF - GILTS -IMMM,TP ICAP UK MTF - MONEY MARKET INSTRUMENTS -IMRD,TP ICAP UK MTF - INTEREST RATE DERIVATIVES -IMSB,TP ICAP UK MTF - CORPORATE BONDS AND SECURITIES DEBT -IMTF,INTEGRAL MTF -INGU,ING BANK NV - SYSTEMATIC INTERNALISER - LONDON BRANCH -INVE,INVESTEC BANK PLC - SYSTEMATIC INTERNALISER -IOCD,ICAP SECURITIES OTF - CREDIT DERIVATIVES -IOED,ICAP SECURITIES OTF - EQUITY DERIVATIVES -IOFI,ICAP SECURITIES OTF - CORPORATE BONDS AND SECURITISED DEBT -IOFX,ICAP SECURITIES OTF - FX DERIVATIVES -IOGB,ICAP SECURITIES OTF - GOVERNMENT BONDS EXCLUDING UK GILTS -IOGI,ICAP SECURITIES OTF - GOVERNMENT BONDS - UK GILTS -IOIR,ICAP SECURITIES OTF - INTEREST RATE DERIVATIVES -IOMM,ICAP SECURITIES OTF - MONEY MARKET INSTRUMENTS -IOTC,INSTINET EUROPE LIMITED OTC -IOTF,ICAP SECURITIES - OTF -IPSX,IPSX -IPXP,IPSX PRIME -IPXW,IPSX WHOLESALE -ISSI,ICBC STANDARD BANK - SYSTEMATIC INTERNALISER -ISWA,ISWAP UK MTF -ISWB,ISWAP UK MTF - TRADE REGISTRATION -ISWC,ISWAP UK MTF ORDER BOOK -ISWE,ICAP GLOBAL DERIVATIVES LIMITED - ELECTRONIC -ISWR,ISWAP UK MTF TARGETED STREAMING/RFQ -ISWV,ICAP GLOBAL DERIVATIVES LIMITED - VOICE -IUOB,ICAP UK OTF - ORDERBOOK -JEFS,JEFFERIES INTERNATIONAL - SYSTEMATIC INTERNALISER -JISI,JEFFERIES INTERNATIONAL -JPCB,JPMORGAN CHASE BANK N.A. LONDON BRANCH -JPSI,J.P. MORGAN SECURITIES PLC -JSSI,JANE STREET FINANCIAL LTD - SYSTEMATIC INTERNALISER -KBLM,KYTE BROKING LIMITED -KLEU,KNIGHT LINK EUROPE -LBCM,LLOYDS BANK CORPORATE MARKETS - SYSTEMATIC INTERNALISER -LCUR,CURRENEX LDFX -LELE,LEDGEREDGE -LIQF,LIQUIDNET EUROPE LIMITED -LIQH,LIQUIDNET H20 -LIQU,LIQUIDNET SYSTEMS -LISX,CBOE EUROPE - LIS SERVICE -LMAD,LMAX - DERIVATIVES -LMAE,LMAX - EQUITIES -LMAF,LMAX - FX -LMAO,LMAX - INDICES/RATES/COMMODITIES -LMAX,LMAX -LMEC,LME CLEAR -LOTC,OTC MARKET -LOUI,LOUIS CAPITAL MARKETS UK LLP -LOYD,LLOYDS BANK - SYSTEMATIC INTERNALISER -LPPM,LONDON PLATINUM AND PALLADIUM MARKET -MAEL,MARKETAXESS EUROPE LIMITED -MAKX,MAKOR SECURITIES LONDON LTD -MAQI,MACQUARIE BANK INTERNATIONAL LIMITED - SYSTEMATIC INTERNALISER -MAQL,MACQUARIE CAPITAL EUROPE LIMITED -MAQU,"MACQUARIE BANK LIMITED, LONDON BRANCH - SYSTEMATIC INTERNALISER" -MAXD,MAX MARKETS LIMITED -MBSI,MORGAN STANLEY BANK INTERNATIONAL LIMITED - SYSTEMATIC INTERNALISER -MCUR,CURRENEX MTF -MCXR,CURRENEX MTF - RFQ -MCXS,CURRENEX MTF - STREAMING -METZ,MET ZURICH -MFGL,MF GLOBAL ENERGY MTF -MFXA,FX CONNECT - MTF - ALLOCATIONS -MFXC,FX CONNECT - MTF -MFXR,FX CONNECT - MTF - RFQ -MHBL,"MIZUHO BANK, LTD. LONDON BRANCH - SYSTEMATIC INTERNALISER" -MHIP,MIZUHO INTERNATIONAL - SYSTEMATIC INTERNALISER -MLIX,MERRILL LYNCH INTERNATIONAL -MLRQ,MERRILL LYNCH INTERNATIONAL - RFQ - SYSTEMATIC INTERNALISER -MLSI,MERRILL LYNCH INTERNATIONAL - SYSTEMATIC INTERNALISER -MSBI,MORGAN STANLEY BANK INTERNATIONAL LIMITED -MSIP,MORGAN STANLEY AND CO. INTERNATIONAL PLC -MSSI,MORGAN STANLEY AND CO. INTERNATIONAL PLC - SYSTEMATIC INTERNALISER -MUBL,"MUFG BANK, LTD. - LONDON BRANCH -SYSTEMATIC INTERNALISER" -MUFP,MARIANA UFP LLP -MUSE,MUFG SECURITIES EMEA PLC - SYSTEMATIC INTERNALISER -MUTI,MITSUBISHI UFJ TRUST INTERNATIONAL LIMITED -MXLM,MARKETAXESS EUROPE LIMITED - LIVE MARKETS -MYTR,MYTREASURY -N2EX,N2EX -NABA,NATIONAL AUSTRALIA BANK - FX DERIVATIVES AND FIXED INCOME SECURITIES - SYSTEMATIC INTERNALISER -NABL,NATIONAL AUSTRALIA BANK - SYSTEMATIC INTERNALISER -NBFL,NATIONAL BANK FINANCIAL INC. - SYSTEMATIC INTERNALISER -NCML,SMBC NIKKO CAPITAL MARKETS LIMITED - SYSTEMATIC INTERNALISER -NDCM,ICE ENDEX UK OCM GAS SPOT -NEXD,AQSE MAIN MARKET (NON-EQUITY) -NEXF,AQSE GROWTH MARKET (NON-EQUITY) -NEXG,AQSE GROWTH MARKET (EQUITY) -NEXL,AQSE MAIN MARKET (EQUITY) -NEXN,AQSE TRADING (NON-EQUITY) -NEXS,NEX SEF -NEXT,AQSE TRADING (EQUITY) -NEXX,AQUIS STOCK EXCHANGE -NOFF,NOMURA OTC TRADES -NOSI,NOMURA - SYSTEMATIC INTERNALISER -NTUK,NATIXIS LONDON BRANCH -NWMS,ROYAL BANK OF SCOTLAND - SYSTEMATIC INTERNALISER -NXEU,NX -OCSI,SOVA CAPITAL - SYSTEMATIC INTERNALISER -OTCE,OTCEX -OTXT,OTCX TRADING LIMITED UK MTF -PARX,PARFX -PEEL,PEEL HUNT LLP UK -PFXD,PARNDF -PIEU,ARITAS FINANCIAL LTD -PIRM,PIRUM -PLDX,PLUS DERIVATIVES EXCHANGE -PPEX,PROPERTY PARTNER EXCHANGE -PVMF,PVM OTF -QWIX,Q-WIXX PLATFORM -R5FX,R5FX LIMITED -RABL,RABOBANK INTERNATIONAL UK - SYSTEMATIC INTERNALISER -RBCE,RBC EUROPE LIMITED -RBCM,RBC - ROYAL BANK OF CANADA -RBCT,RBC INVESTOR SERVICES TRUST -RBSX,RBS CROSS -REST,NEX SEF MTF - RESET - RISK MITIGATION SERVICES -RTSI,RBC INVESTOR SERVICES TRUST - SYSTEMATIC INTERNALISER -RTSL,REFINITIV TRANSACTIONS SERVICES LIMITED -SBEX,SCOTIABANK - SYSTEMATIC INTERNALISER -SCOT,SCOTTISH STOCK EXCHANGE -SECF,SECFINEX -SFCL,SWISSCANTO FUNDS CENTRE LIMITED -SGMX,SIGMA X MTF -SGMY,SIGMA X MTF - AUCTION BOOK -SGMZ,SIGMA X MTF - NEGOTIATED TRADE -SHAR,ASSET MATCH -SICS,CREDIT SUISSE SECURITIES (EUROPE) LIMITED - SYSTEMATIC INTERNALISER -SIFX,SIEGE FX LIMITED -SISU,SUSQUEHANNA INTERNATIONAL SECURITIES LIMITED - LONDON BRANCH - SYSTEMATIC INTERNALISER -SKYX,SKYTRA -SMBE,SUMITOMO MITSUI BANKING CORPORATION EUROPE LIMITED -SMBG,SUMITOMO MITSUI BANKING CORPORATION - LONDON BRANCH - SYSTEMATIC INTERNALISER -SNUK,SANTANDER UK - SYSTEMATIC INTERNALISER -SPDX,SPREADEX -SPEC,MAREX SPECTRON INTERNATIONAL LIMITED OTF -SPRZ,SPREADZERO -SQUA,SQUARE GLOBAL - OTF -SSBT,STATE STREET BANK AND TRUST COMPANY -SSEX,SOCIAL STOCK EXCHANGE -SSFX,STATE STREET BANK AND TRUST FX - SYSTEMATIC INTERNALISER -SSIL,STATE STREET BANK INTERNATIONAL FX - LONDON BRANCH - SYSTEMATIC INTERNALISER -STAN,STANDARD CHARTERED - SYSTEMATIC INTERNALISER -STFL,STIFEL NICOLAUS EUROPE LIMITED - SYSTEMATIC INTERNALISER -SWAP,SWAPSTREAM -TCDS,TRADITION OTF -TCML,TIDE CM -TDBL,TORONTO DOMINION BANK - LONDON BRANCH -TECO,TULLETT PREBON EUROPE - OTF - COMMODITY AND ENERGY DERIVATIVES -TEFD,TULLETT PREBON EUROPE - MTF - FX DERIVATIVES -TEFX,TULLETT PREBON EUROPE - OTF - FX DERIVATIVES -TEGB,TULLETT PREBON EUROPE - OTF - GOVERNMENT BONDS EXCLUDING UK GILTS -TEGI,TULLETT PREBON EUROPE - OTF - GOVERNMENT BONDS - UK GILTS -TEIR,TULLETT PREBON EUROPE - OTF - INTEREST RATE DERIVATIVES -TEMB,TULLETT PREBON EUROPE - MTF - GOVERNMENT BONDS EXCLUDING UK GILTS -TEMF,TULLETT PREBON EUROPE - MTF - CORPORATE BONDS AND SECURITISED DEBT -TEMI,TULLETT PREBON EUROPE - MTF - MONEY MARKETS -TEMM,TULLETT PREBON EUROPE - OTF - MONEY MARKET INSTRUMENTS -TEMR,TULLETT PREBON EUROPE - MTF - REPOS -TERE,TULLETT PREBON EUROPE - OTF - REPOS -TIRD,TULLETT PREBON EUROPE - MTF - INTEREST RATE DERIVATIVES -TPEL,TULLETT PREBON (EUROPE) LIMITED -TPEO,TULLETT PREBON EUROPE - OTF -TPID,TP ICAP - UK - DIGITAL ASSETS -TPIE,THE PROPERTY INVESTMENT EXCHANGE -TPSL,TULLETT PREBON SECURITIES - MTF -TPSO,TP UK OTF -TRAL,REFINITIV TRANSACTIONS SERVICES LIMITED - FXALL RFQ -TRAX,MARKETAXESS POST-TRADE LIMITED - APA -TRDE,TRADITION -TRDX,TRAD-X -TREA,TRADEWEB EUROPE LIMITED - APA -TREO,TRADEWEB EUROPE LIMITED - OTF -TREU,TRADEWEB EUROPE LIMITED -TRFW,REFINITIV TRANSACTION SERVICES LIMITED - FORWARDS MATCHING -TRQA,TURQUOISE LIT AUCTIONS -TRQB,TURQUOISE NYLON CASH ORDER BOOK -TRQC,TURQUOISE NYLON CLEARED CONTRACT -TRQM,TURQUOISE PLATO -TRQX,TURQUOISE -TRSI,TOWER RESEARCH CAPITAL EUROPE LTD -TRUK,TRADING 212 UK LIMITED -TSCB,TP UK OTF - CORPORATE BONDS -TSCD,TP UK OTF - CREDIT DERIVATIVES -TSED,TP UK OTF - EQUITY DERIVATIVES -TSFI,TP UK OTF - CORPORATE BONDS AND SECURITISED DEBT -TSFX,TP UK OTF - FX DERIVATIVES -TSGB,TP UK OTF - GOVERNMENT BONDS EXCLUDING UK GILTS -TSGI,TP UK OTF - GOVERNMENT BONDS - UK GILTS -TSIR,TP UK OTF - INTEREST RATE DERIVATIVES -TSMM,TP UK OTF - MONEY MARKET INSTRUMENTS -TSMR,TULLETT PREBON SECURITIES - MTF - REPOS -TSRE,TP UK OTF - REPOS -TUOB,TP UK OTF - ORDERBOOK -TWGP,TRADEWEB EUROPE LIMITED - GLOBAL TRADING PLATFORM -UBIN,UNION BANK OF INDIA UK LTD -UBSB,UBS AG LONDON BRANCH - TRADING -UBSY,UBS AG LONDON BRANCH - SYSTEMATIC INTERNALISER -UGEN,UNITEDBLOCKTRADE -UKCA,CREDIT AGRICOLE CIB UK BRANCH - SYSTEMATIC INTERNALISER -UKGD,MTS UK -UKOR,TP ICAP UK MTF - ORDERBOOK -UKPX,APX POWER UK -UKRE,TP ICAP UK MTF - REGISTRATION -UNGB,UNICREDIT BANK AG - LONDON BRANCH - UK SYSTEMATIC INTERNALISER -VAGL,VIRTUAL AUCTION GLOBAL LIMITED -VAGM,VIRTUAL AUCTION GLOBAL MARKETS - MTF -VCMO,VANTAGE CAPITAL MARKETS LLP - OTF -VEGA,VEGA-CHI -VFGB,VIRTU FINANCIAL IRELAND LIMITED (LONDON BRANCH) - SYSTEMATIC INTERNALISER -VIUK,POSIT UK -VTBC,VTB CAPITAL PLC - SYSTEMATIC INTERNALISER -WELN,WEL - SYSTEMATIC INTERNALISER -WFLB,WELLS FARGO BANK NA - LONDON BRANCH - SYSTEMATIC INTERNALISER -WINS,WINTERFLOOD SECURITIES LIMITED - ELECTRONIC PLATFORM -WINX,WINTERFLOOD SECURITIES LIMITED - MANUAL TRADING -WSIL,WELLS FARGO SECURITIES INTERNATIONAL -WSIN,WESTPAC BANKING CORPORATION - SYSTEMATIC INTERNALISER -XALT,ALTEX - ATS -XCOR,ICMA -XEBS,EBS UK MULTILATERAL TRADING FACILITY -XGCL,GLOBAL COAL LIMITED -XGCX,GLOBAL COMMODITIES EXCHANGE -XGDX,GLOBAL DERIVATIVES EXCHANGE -XGSX,GLOBAL SECURITIES EXCHANGE -XIEL,INSTINET EUROPE LIMITED -XLBM,LONDON BULLION MARKET -XLCH,LCH LTD -XLDX,LONDON DERIVATIVES EXCHANGE -XLME,LONDON METAL EXCHANGE -XLOD,LONDON STOCK EXCHANGE - CURVEGLOBAL MARKETS -XLOM,LONDON STOCK EXCHANGE - MTF -XLON,LONDON STOCK EXCHANGE -XMTS,EUROMTS LTD -XPAL,POSIT AUCTION UK -XPHX,PEEL HUNT CROSSING -XPOL,POSIT DARK UK -XPUK,XP INVESTMENTS UK LLP -XRSP,PEEL HUNT RETAIL -XSGB,SOCIETE GENERALE (LONDON BRANCH) - SYSTEMATIC INTERNALISER -XSWB,SWX SWISS BLOCK -XTXM,XTX MARKETS -XUBS,AQUIS EXCHANGE PLC - AMP - DARK ORDER BOOK -ZODM,ZODIA MARKETS -AATS,ASSENT ATS -ACKF,KCG ACKNOWLEDGE FI -ADVT,ADVISE TECHNOLOGIES - APA TRANSPARENCY REPORTING -ALDP,NYSE ALTERNEXT DARK -AMLG,AMERICAN LEDGER ATS -AMXO,NYSE AMEX OPTIONS -AQUA,AQUA EQUITIES L.P. -ARCD,ARCA DARK -ARCO,NYSE ARCA OPTIONS -ARCX,NYSE ARCA -ARKX,ARKONIS -ASEF,AEGIS SWAP EXECUTION FACILITY -ASMT,INTELLIGENTCROSS ASPEN MAKER/TAKER -ASPI,INTELLIGENTCROSS ASPEN INVERTED -ASPN,INTELLIGENTCROSS ASPEN INTELLIGENT BID/OFFER -BAML,BANK OF AMERICA - MERRILL LYNCH INSTINCT X ATS -BAMX,BANK OF AMERICA - MERRILL LYNCH GCX -BARD,BARCLAYS FX - TRADING -BARL,BARCLAYS LIQUID MARKETS -BARX,BARCLAYS ATS -BATO,CBOE BZX OPTIONS EXCHANGE -BATS,CBOE BZX U.S. EQUITIES EXCHANGE -BATY,CBOE BYX U.S. EQUITIES EXCHANGE -BBOK,BARCLAYS SINGLE DEALER PLATFORM -BBSF,BLOOMBERG SEF LLC -BCDX,BARCLAYS DIRECT EX ATS -BGCD,BGC DERIVATIVE MARKETS L.P. -BGCF,BGC FINANCIAL INC -BHSF,BATS HOTSPOT SEF LLC -BIDS,BIDS TRADING L.P. -BKKT,BAKKT -BLKX,INSTINET BLOCKCROSS ATS -BLTD,BLOOMBERG TRADEBOOK LLC -BLUE,PRO SECURITIES ATS -BNDD,TRADEWEB DIRECT LLC -BNPC,BNPP CORTEX ATS -BNYC,CONVERGEX -BOSD,NASDAQ OMX BX DARK -BPOL,BLOOMBERG BPOOL -BSTX,BOSTON SECURITY TOKEN EXCHANGE LLC -BTEC,ICAP ELECTRONIC BROKING (US) -BTEQ,BROKERTEC AMERICAS LLC - RFQ PLATFORM -BTNL,BITNOMIAL -BVUS,BONDVISION US -BYXD,CBOE BYX U.S. EQUITIES EXCHANGE DARK -BZXD,CBOE BZX U.S. EQUITIES EXCHANGE DARK -C2OX,CBOE C2 OPTIONS EXCHANGE -CAES,CREDIT SUISSE AES CROSSFINDER -CALH,CLSA AMERICAS - LIQUIDITY HUB -CAVE,CAVEAT EMPTOR -CBSX,CBOE STOCK EXCHANGE -CBTS,CME SWAPS MARKETS (CBOT) -CCMX,CARTAX -CDED,CITADEL SECURITIES -CECS,CME SWAPS MARKETS (COMEX) -CGMI,CITIGROUP GLOBAL MARKETS -CGXS,ONECHRONOS -CIOI,CIOI -CISD,"NYSE NATIONAL, INC. - DARK" -CLST,CLEAR STREET -CMES,CME SWAPS MARKETS (CME) -CMSF,"CLEAR MARKETS NORTH AMERICA, INC." -CODA,CODA MARKETS - MICRO AND BLOCK -COHR,"COMHAR CAPITAL MARKETS, LLC" -CPGX,"CLEARPOOL EXECUTION SERVICES, LLC" -CRED,CREDIT SUISSE (US) -CSCL,CREDIT SUISSE CLOSEX -CSVW,CREDIT SUISSE VWAPX -CURX,CUREX FX -DASH,DASH ATS -DBAB,DEUTSCHE BANK SECURITIES INC. -DEAL,DCX (DERIVATIVES CURRENCY EXCHANGE) -DWFI,DEALERWEB FIXED INCOME -DWSF,DW SEF LLC -EBXV,LEVEL ATS - VWAP CROSS -EDDP,CBOE EDGX U.S. EQUITIES EXCHANGE DARK -EDGA,CBOE EDGA U.S. EQUITIES EXCHANGE -EDGD,CBOE EDGA U.S. EQUITIES EXCHANGE DARK -EDGO,CBOE EDGX OPTIONS EXCHANGE -EDGX,CBOE EDGX U.S. EQUITIES EXCHANGE -EGMT,EG MARKET TECHNOLOGIES -EMLD,"MIAX EMERALD, LLC" -EPRD,MIAX PEARL EQUITIES EXCHANGE DARK -EPRL,MIAX PEARL EQUITIES -EQUS,EQUILEND LLC -ERIS,ERSX -EXPM,EXPERT MARKET -FAST,EURONEXT FX -FCBT,CHICAGO BOARD OF TRADE (FLOOR) -FCME,CHICAGO MERCANTILE EXCHANGE (FLOOR) -FICO,FINANCIALCONTENT -FINC,FINRA/NASDAQ TRF CHICAGO (TRADE REPORTING FACILITY) -FINN,FINRA/NASDAQ TRF CARTERET (TRADE REPORTING FACILITY) -FINO,FINRA ORF (TRADE REPORTING FACILITY) -FINR,FINRA -FINY,FINRA/NYSE TRF (TRADE REPORTING FACILITY) -FNCS,FENICS - US TREASURIES -FNFT,FENICS FUTURES -FNFX,FENICS FX ECN -FNIX,GFI SECURITIES LLC - FENICS DELTA X -FNXB,FENICS INVITATIONS -FREX,FAIRX -FSEF,FTSEF LLC -FTUS,FLOW TRADERS U.S LLC -FUSD,FTX US DERIVATIVES -FXAL,FXALL -FXCM,FXCM -FXNM,REFINITIV MATCHING NDFS SEF -FXPS,REFINITIV SEF REQUEST FOR STREAM -G1XX,G1 EXECUTION SERVICES -GFAM,GFI SECURITIES LLC - ALL MARKETS -GLBX,CME GLOBEX -GLLC,GATE US LLC -GLMX,GLMX -GLPS,"ESSEX RADEZ, LLC" -GLPX,"ACS EXECUTION SERVICES, LLC" -GMNI,ISE GEMINI EXCHANGE -GOTC,GLOBAL OTC -GOVX,GOVEX -GREE,THE GREEN EXCHANGE -GSCO,GOLDMAN SACHS AND CO. -GSEF,"GFI SWAPS EXCHANGE, LLC" -GTCO,KCG AMERICAS LLC -GTSX,GTSX -GTXS,"GTX SEF, LLC" -HEGX,NADEX -HPPO,POTAMUS TRADING LLC -HRTF,HUDSON RIVER TRADING (HRT) -HRTX,HUDSON RIVER TRADING -HSFX,HOTSPOT FX -IATS,INTERACTIVE BROKERS LLC - ATS -ICBX,INSTINET CBX (US) -ICEL,"ISLAND ECN LTD, THE" -ICES,ICE SWAP TRADE LLC -ICRO,INSTINET VWAP CROSS -ICSU,ICAP SEF (US) LLC. -IDXM,"IDX MARKETS, LLC" -IEPA,INTERCONTINENTAL EXCHANGE -IEXD,INVESTORS EXCHANGE - DARK -IEXG,INVESTORS EXCHANGE -IFED,ICE FUTURES U.S. ENERGY DIVISION -IFUS,ICE FUTURES U.S. -IMAG,ICE MARKETS AGRICULTURE -IMBD,ICE MARKETS BONDS -IMCC,ICE CREDIT TRADE -IMCG,CREDITEX LLC -IMCR,ICE MARKETS CREDIT -IMCS,IMC FINANCIAL MARKETS -IMEN,ICE MARKETS ENERGY -IMFX,ICE MARKETS FOREIGN EXCHANGE -IMIR,ICE MARKETS RATES -INCA,INSTINET DESK CROSS -INCR,INTELLIGENTCROSS -ISDA,ISDAFIX -ITGI,ITG - POSIT -JLEQ,JUMP LIQUIDITY - US EQUITY PLATFORM -JLQD,JUMP LIQUIDITY - MULTI-ASSET PLATFORM -JNST,"JANE STREET CAPITAL, LLC" -JPBX,JPBX -JPMS,"JPMS, LLC" -JPMX,JPMX -JSES,JANE STREET EXECUTION SERVICES LLC -JSJX,JANE STREET JX -KLSH,KALSHIEX LLC -KNCM,KNIGHT CAPITAL MARKETS LLC -KNEM,KNIGHT EQUITY MARKETS LP -KNIG,KNIGHT -KNLI,KNIGHT LINK -LAMP,LAMPOST CAPITAL -LASF,LATAM SEF -LATG,GFI SECURITIES LLC - CREDITMATCH (LATG) -LEDG,LEDGERX -LESI,LEDGEREDGE SECURITIES INC. -LEVL,LEVEL ATS -LIFI,"LIQUIDNET, INC. FIXED INCOME ATS" -LIUH,"LIQUIDNET, INC. H2O ATS" -LIUS,"LIQUIDNET, INC." -LMNX,LUMINEX TRADING & ANALYTICS LLC - ATS -LPSF,LPSFX LLC -LQED,LIQUIDITYEDGE -LQFI,CITI LIQUIFI -LTAA,LUMINEX TRADING & ANALYTICS LLC -LTSE,"LONG-TERM STOCK EXCHANGE, INC." -MAGM,MAGMA ATS -MCRY,"ISE MERCURY, LLC" -MELO,NASDAQ MIDPOINT-ELO (M-ELO) -MEMD,MEMX LLC DARK -MEMM,MEMX LLC RETAIL MIDPOINT -MEMX,MEMX LLC EQUITIES -MIHI,"MIAMI INTERNATIONAL HOLDINGS, INC." -MKAA,MARKETAXESS ATS -MLCO,BANK OF AMERICA - MERRILL LYNCH OTC -MLVX,BANK OF AMERICA - MERRILL LYNCH VWAP CROSS -MOCX,MOC CROSS -MPRL,"MIAX PEARL, LLC" -MSCO,MORGAN STANLEY AND CO. LLC -MSPL,MS POOL -MSRP,MS RPOOL -MSTX,MS TRAJECTORY CROSS -MTSB,MTS BONDS.COM -MTUS,MTS MARKETS INTERNATIONAL INC. -MTXA,MARKETAXESS CANADA COMPANY -MTXC,MARKETAXESS CORPORATION SINGLE-NAME CDS CENTRAL LIMIT ORDER -MTXM,MARKETAXESS CORPORATION MID-X TRADING SYSTEM -MTXS,MARKETAXESS SEF CORPORATION -MTXX,MARKETAXESS CORPORATION -MXOP,MEMX LLC OPTIONS -NASD,NSDQ DARK -NBLX,NOBLE EXCHANGE -NFSA,FIDELITY CROSSSTREAM -NFSC,"NATIONAL FINANCIAL SERVICES, LLC" -NFSD,FIDELITY DARK -NLAX,"CLEARPOOL EXECUTION SERVICES, LLC - NATURAL LIQUIDITY ALLIANCE" -NMRA,NOMURA SECURITIES INTERNATIONAL -NODX,NODAL EXCHANGE -NXUS,NX ATS - CROSSING PLATFORM -NYFX,MILLENNIUM -NYMS,CME SWAPS MARKETS (NYMEX) -NYPC,NEW YORK PORTFOLIO CLEARING -NYSD,NYSE DARK -OCEA,BLUE OCEAN ALTERNATIVE TRADING SYSTEM -OLLC,OTCEX LLC -ONEC,CITI-ONE -OOTC,OTHER OTC -OPRA,OPTIONS PRICE REPORTING AUTHORITY -OTCB,OTCQB MARKETPLACE -OTCI,OTC LINK NQB IDQS (INTER-DEALER QUOTATION SYSTEM) -OTCM,OTC LINK ATS - OTC MARKETS -OTCN,OTC LINK ECN -OTCQ,OTCQX MARKETPLACE -PINC,OTC PINK CURRENT -PINI,OTC PINK NO INFORMATION -PINL,OTC PINK LIMITED -PINX,OTC PINK MARKETPLACE -PIPE,ARITAS SECURITIES LLC -PIPR,PIPER SANDLER AND CO. -PJCX,PIPER SANDLER AND CO. - ATS -PRSE,PRAGMA ATS -PSGM,OTC GREY MARKET -PSXD,NASDAQ PSX -PUMA,"PUMA CAPITAL, LLC" -PUMX,"PUMA CAPITAL, LLC - OPTIONS" -PUND,PUNDION LLC -RAJA,RAYMOND JAMES -RCMA,RBC CAPITAL MARKETS ARBITRAGE S.A. -RICD,RIVERCROSS DARK -RICX,RIVERCROSS -SAGE,SAGETRADER -SCXA,SEED DIGITAL SECURITIES MARKET -SCXF,SEED FUTURES -SCXM,SEED DIGITAL COMMODITIES MARKET -SCXO,SEED CX -SCXS,SEED SEF -SFOX,SFOX -SGA2,"SG AMERICAS SECURITIES, LLC - SECOND VENUE" -SGAS,"SG AMERICAS SECURITIES, LLC" -SGMA,GOLDMAN SACH MTF -SGMT,SIGMA X2 -SHAD,D.E. SHAW DARK -SHAW,D.E. SHAW -SMFE,"SMALL EXCHANGE, INC - DESIGNATED CONTRACT MARKET" -SOHO,"TWO SIGMA SECURITIES, LLC" -SPAX,SPECTRAXE -SPTX,SPOT FX -SSTX,E-EXCHANGE -STFX,"STIFEL, NICOLAUS AND COMPANY, INCORPORATED" -STRM,PURESTREAM -SUNT,SUN TRADING LLC -TERA,TERAEXCHANGE -TFSU,TFS GREEN UNITED STATES GREEN MARKETS -THEM,THEMIS TRADING LLC -THRE,REFINITIV US SEF LLC -TMCC,ICE TMC -TMID,TRUMID ATS -TPSE,"TP SEF, INC." -TPSV,"TPSEF, INC - VOICE" -TRAI,TRAIANA INC -TRCK,TRACK ECN -TRCX,TOWER RESEARCH CAPITAL TRCX -TRU1,TRUEEX LLC - DESIGNATED CONTRACT MARKET (DMC) -TRU2,TRUEEX LLC - SEF (SWAP EXECUTION FACILITY) -TRUX,TRUEEX LLC -TRWB,TRADEWEB LLC -TSAD,TRADITION SECURITIES AND DERIVATIVES INC. -TSBX,"DBOT ATS, LLC" -TSEF,TRADITION SEF -TWSF,TW SEF LLC -UBEC,UNIVERSAL BARTER EXCHANGE CREDIT UNION -UBSA,UBS ATS -UBSP,UBS PIN (UBS PRICE IMPROVEMENT NETWORK) -UBSS,UBS SECURITIES LLC -VABD,ICE BONDPOINT -VALX,VIRTU CLIENT MARKET MAKING -VCRS,VIRTU MATCHIT - CONDITIONAL ROOM -VERT,VERTICAL -VFCM,VIRTU FINANCIAL CAPITAL MARKETS LLC -VFMI,VIRTU MATCHIT - MAIN CROSSING SESSION -VIRT,VIRTU FINANCIAL BD -VNDM,WALL STREET ACCESS NYC - VNDM -VTEX,VORTEX -VUSA,VIRTU AMERICAS LLC -WABR,WALL STREET ACCESS NYC -WELS,"WELLS FARGO SECURITIES, LLC - CASH EQUITIES" -WSAG,WALL STREET ACCESS -XADF,FINRA ALTERNATIVE DISPLAY FACILITY (ADF) -XAQS,AUTOMATED EQUITY FINANCE MARKETS -XASE,NYSE MKT LLC -XBOS,NASDAQ OMX BX -XBOX,BOX OPTIONS EXCHANGE -XBRT,BRUT ECN -XBXO,NASDAQ OMX BX OPTIONS -XCBF,CBOE FUTURES EXCHANGE -XCBO,CBOE GLOBAL MARKETS INC. -XCBT,CHICAGO BOARD OF TRADE -XCEC,COMMODITIES EXCHANGE CENTER -XCFF,CANTOR FINANCIAL FUTURES EXCHANGE -XCHI,"NYSE CHICAGO, INC." -XCIS,"NYSE NATIONAL, INC." -XCME,CHICAGO MERCANTILE EXCHANGE -XCUR,CURRENEX -XELX,ELX -XFCI,FINANCIALCONTENT - INDEXES -XFDA,FINANCIALCONTENT - DIGITAL ASSET TRADE REPORTING FACILITY -XGMX,GLOBALCLEAR MERCANTILE EXCHANGE -XIMM,INTERNATIONAL MONETARY MARKET -XINS,INSTINET -XIOM,INDEX AND OPTIONS MARKET -XISA,"INTERNATIONAL SECURITIES EXCHANGE, LLC - ALTERNATIVE MARKETS" -XISE,"INTERNATIONAL SECURITIES EXCHANGE, LLC - EQUITIES" -XISX,"INTERNATIONAL SECURITIES EXCHANGE, LLC" -XKBT,KANSAS CITY BOARD OF TRADE -XMER,MERCHANTS' EXCHANGE -XMGE,MINNEAPOLIS GRAIN EXCHANGE -XMIO,MIAMI INTERNATIONAL SECURITIES EXCHANGE -XNAS,NASDAQ - ALL MARKETS -XNCM,NASDAQ CAPITAL MARKET -XNDQ,NASDAQ OPTIONS MARKET -XNFI,NASDAQ FIXED INCOME TRADING -XNGS,NASDAQ/NGS (GLOBAL SELECT MARKET) -XNIM,NASDAQ INTERMARKET -XNLI,NYSE LIFFE -XNMS,NASDAQ/NMS (GLOBAL MARKET) -XNYE,NEW YORK MERCANTILE EXCHANGE - OTC MARKETS -XNYL,NEW YORK MERCANTILE EXCHANGE - ENERGY MARKETS -XNYM,NEW YORK MERCANTILE EXCHANGE -XNYS,"NEW YORK STOCK EXCHANGE, INC." -XPBT,NASDAQ OMX FUTURES EXCHANGE -XPHL,NASDAQ OMX PHLX -XPHO,PHILADELPHIA OPTIONS EXCHANGE -XPOR,PORTAL -XPSX,NASDAQ OMX PSX -XPUS,"XP INVESTMENTS US, LLC" -XSEF,"SWAPEX, LLC" -XSTM,FIDELITY CROSSSTREAM ATS -XTRD,XTRD -XTXD,XTX DIRECT -YKNA,"COMHAR CAPITAL MARKETS, LLC - US EQUITIES" -ZERO,ZERO HASH -BVUR,BOLSA ELECTRONICA DE VALORES DEL URUGUAY -UFEX,UFEX -XMNT,BOLSA DE VALORES DE MONTEVIDEO -XCET,UZBEK COMMODITY EXCHANGE -XCUE,UZBEKISTAN REPUBLICAN CURRENCY EXCHANGE -XKCE,KHOREZM INTERREGION COMMODITY EXCHANGE -XSTE,REPUBLICAN STOCK EXCHANGE -XUNI,UNIVERSAL BROKER'S EXCHANGE 'TASHKENT' -GXMA,GX MARKETCENTER -POTC,PACIFIC OTC -BVCA,CARACAS STOCK EXCHANGE -HSTC,HANOI STOCK EXCHANGE -XHNF,HANOI STOCK EXCHANGE - DERIVATIVES -XHNX,HANOI STOCK EXCHANGE (UNLISTED PUBLIC COMPANY TRADING PLATFORM) -XSTC,HOCHIMINH STOCK EXCHANGE -XLUS,LUSAKA STOCK EXCHANGE -VFEX,VICTORIA FALLS STOCK EXCHANGE -XZIM,ZIMBABWE STOCK EXCHANGE -BILT,OFF-EXCHANGE TRANSACTIONS - LISTED AND UNLISTED INSTRUMENTS -XOFF,OFF-EXCHANGE TRANSACTIONS - LISTED INSTRUMENTS -XXXX,NO MARKET (E.G. UNLISTED) \ No newline at end of file diff --git a/openbb_terminal/stocks/options/__init__.py b/openbb_terminal/stocks/options/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/options/alphaquery_model.py b/openbb_terminal/stocks/options/alphaquery_model.py deleted file mode 100644 index acd9f93851b..00000000000 --- a/openbb_terminal/stocks/options/alphaquery_model.py +++ /dev/null @@ -1,64 +0,0 @@ -"""AlphaQuery Model""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime, timedelta -from typing import Optional - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_put_call_ratio( - symbol: str, - window: int = 30, - start_date: Optional[str] = None, -) -> pd.DataFrame: - """Gets put call ratio over last time window [Source: AlphaQuery.com] - - Parameters - ---------- - symbol: str - Ticker symbol to look for - window: int, optional - Window to consider, by default 30 - start_date: Optional[str], optional - Start date to plot (e.g., 2021-10-01), by default last 366 days - - Returns - ------- - pd.DataFrame - Put call ratio - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> pcr_df = openbb.stocks.options.pcr("B") - """ - - if start_date is None: - start_date = (datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d") - - url = f"https://www.alphaquery.com/data/option-statistic-chart?ticker={symbol}\ - &perType={window}-Day&identifier=put-call-ratio-volume" - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) \ - Chrome/70.0.3538.77 Safari/537.36" - } - - r = request(url, headers=headers) - if r.status_code != 200: - return pd.DataFrame() - - pcr = pd.DataFrame.from_dict(r.json()) - pcr.rename(columns={"x": "Date", "value": "PCR"}, inplace=True) - pcr.set_index("Date", inplace=True) - pcr.index = pd.to_datetime(pcr.index).tz_localize(None) - - return pcr[pcr.index > start_date] diff --git a/openbb_terminal/stocks/options/alphaquery_view.py b/openbb_terminal/stocks/options/alphaquery_view.py deleted file mode 100644 index 47afb7fbe42..00000000000 --- a/openbb_terminal/stocks/options/alphaquery_view.py +++ /dev/null @@ -1,54 +0,0 @@ -"""AlphaQuery View""" - -__docforma__ = "numpy" - -import logging -import os -from datetime import datetime, timedelta -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options import alphaquery_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_put_call_ratio( - symbol: str, - window: int = 30, - start_date: str = (datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d"), - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display put call ratio [Source: AlphaQuery.com] - - Parameters - ---------- - symbol : str - Stock ticker symbol - window : int, optional - Window length to look at, by default 30 - start_date : str, optional - Starting date for data, by default (datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d") - export : str, optional - Format to export data, by default "" - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - pcr = alphaquery_model.get_put_call_ratio(symbol, window, start_date) - if pcr.empty: - return console.print("No data found.\n") - - fig = OpenBBFigure().set_title(f"Put Call Ratio for {symbol.upper()}") - fig.add_scatter(x=pcr.index, y=pcr["PCR"], name="Put Call Ratio") - - export_data( - export, os.path.dirname(os.path.abspath(__file__)), "pcr", pcr, sheet_name, fig - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/options/barchart_model.py b/openbb_terminal/stocks/options/barchart_model.py deleted file mode 100644 index d601a081aea..00000000000 --- a/openbb_terminal/stocks/options/barchart_model.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Barchart Model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_options_info(symbol: str) -> pd.DataFrame: - """Scrape barchart for options info - - Parameters - ---------- - symbol: str - Stock ticker symbol - - Returns - ------- - df: pd.DataFrame - Dataframe of options information - """ - page = f"https://www.barchart.com/stocks/quotes/{symbol}/overview" - - r = request(page, headers={"User-Agent": get_user_agent()}) - soup = BeautifulSoup(r.text, "html.parser") - tags = soup.find( - "div", - attrs={ - "class": "barchart-content-block symbol-fundamentals bc-cot-table-wrapper" - }, - ) - data = tags.find_all("li") - labels = [] - values = [] - for row in data: - labels.append(row.find_all("span")[0].getText()) - values.append(row.find_all("span")[1].getText()) - - df = pd.DataFrame(data=[labels, values]).T - - return df diff --git a/openbb_terminal/stocks/options/barchart_view.py b/openbb_terminal/stocks/options/barchart_view.py deleted file mode 100644 index a8563bf229e..00000000000 --- a/openbb_terminal/stocks/options/barchart_view.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Helper functions for scraping options data""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.options import barchart_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def print_options_data(symbol: str, export: str = "", sheet_name: Optional[str] = None): - """Scrapes Barchart.com for the options information - - Parameters - ---------- - symbol: str - Ticker symbol to get options info for - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format of export file - """ - - data = barchart_model.get_options_info(symbol) - - print_rich_table( - data, - show_index=False, - headers=["Info", "Value"], - title=f"{symbol} Options Information", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "info", - data, - sheet_name, - ) diff --git a/openbb_terminal/stocks/options/calculator_model.py b/openbb_terminal/stocks/options/calculator_model.py deleted file mode 100644 index d37ea8e82a9..00000000000 --- a/openbb_terminal/stocks/options/calculator_model.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Calculator Model""" - -___docformat__ = "numpy" - -import logging -from typing import Dict, Tuple - -import numpy as np - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def pnl_calculator( - strike: float = 10, - premium: float = 1, - put: bool = False, - sell: bool = False, - **kwargs: Dict[str, int] -) -> Tuple[np.ndarray, np.ndarray, float]: - """Calculate profit/loss for different option variables - - Parameters - ---------- - strike: float - Strike price - premium: float - Premium - put: bool - Is this a put option - sell: bool - Are you selling the option - kwargs - - Returns - ------- - Tuple[np.ndarray, np.ndarray, float] - Array of prices, array of profits/losses, breakeven price - - """ - - if "x_min" in kwargs and "x_max" in kwargs: - price_at_expiry = np.linspace(kwargs["x_min"], kwargs["x_max"], 301) # type: ignore - else: - price_at_expiry = np.linspace(strike / 2, 1.5 * strike, 301) - - sell_factor = [1, -1][sell] - - if put: - break_even = strike - sell_factor * premium - pnl = strike - premium - price_at_expiry - pnl = sell_factor * 100 * np.where(price_at_expiry < strike, pnl, -premium) - - else: - break_even = strike + sell_factor * premium - pnl = price_at_expiry - strike - premium - pnl = sell_factor * 100 * np.where(price_at_expiry > strike, pnl, -premium) - - return price_at_expiry, pnl, break_even diff --git a/openbb_terminal/stocks/options/calculator_view.py b/openbb_terminal/stocks/options/calculator_view.py deleted file mode 100644 index ce98c3af6b6..00000000000 --- a/openbb_terminal/stocks/options/calculator_view.py +++ /dev/null @@ -1,95 +0,0 @@ -import logging -from typing import Dict - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options import calculator_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def view_calculator( - strike: float = 10, - premium: float = 1, - put: bool = False, - sell: bool = False, - external_axes: bool = False, - **kwargs: Dict[str, int], -): - """ - - Parameters - ---------- - strike: float - Strike price - premium: float - Premium - put: bool - Whether option is put - sell: - Whether selling option - external_axes : bool, optional - Whether to return the figure object or not, by default False - kwargs: Dict[str,int] - """ - - price_at_expiry, pnl, break_even = calculator_model.pnl_calculator( - strike, premium, put, sell, **kwargs - ) - - fig = OpenBBFigure(xaxis_title="Price at Expiry", yaxis_title="Profit") - - fig.set_title( - f"Profit for {['Buying', 'Selling'][sell]} {['Call', 'Put'][put]} option" - ) - - fig.add_scatter( - x=price_at_expiry[pnl > 0], - y=pnl[pnl > 0], - fill="tozeroy", - fillcolor="rgba(0, 255, 0, 0.5)", - line_color="green", - name="Profit", - ) - fig.add_scatter( - x=price_at_expiry[pnl < 0], - y=pnl[pnl < 0], - fill="tozeroy", - fillcolor="rgba(255, 0, 0, 0.5)", - line_color="red", - name="Loss", - ) - - fig.add_vline(x=strike, line_width=6, line_color="white", opacity=0.5) - fig.add_vline(x=break_even, line_width=6, line_color="white", opacity=0.5) - - if sell: - fig.add_hline_legend( - y=100 * premium, - line=dict(width=5, color="green"), - name=f"Max Profit: ${100 * premium}", - ) - else: - fig.add_hline_legend( - y=-100 * premium, - line=dict(width=5, color="red"), - name=f"Max Loss: ${-100 * premium}", - ) - - print_string = f"""Strike: ${strike} -Premium: ${premium} -Breakeven price: ${break_even}\n""" - - if sell: - print_string += f"""Max profit: ${100 * premium} -Max loss: Unlimited\n""" - - else: - print_string += f"""Max profit: Unlimited -Max loss: ${-100 * premium}\n""" - - console.print(print_string) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/options/cboe_model.py b/openbb_terminal/stocks/options/cboe_model.py deleted file mode 100644 index 6ea887109a6..00000000000 --- a/openbb_terminal/stocks/options/cboe_model.py +++ /dev/null @@ -1,582 +0,0 @@ -# mypy: disable-error-code=attr-defined - -"""Model for retrieving public options data from the CBOE.""" - -from datetime import datetime -from typing import Tuple - -import pandas as pd -from requests.exceptions import HTTPError - -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options.op_helpers import Options, PydanticOptions - -__docformat__ = "numpy" - - -TICKER_EXCEPTIONS: list[str] = ["NDX", "RUT"] - - -def get_cboe_directory() -> pd.DataFrame: - """Gets the US Listings Directory for the CBOE. - - Returns - ------- - pd.DataFrame: CBOE_DIRECTORY - DataFrame of the CBOE listings directory - - Examples - ------- - >>> from openbb_terminal.stocks.options import cboe_model - >>> CBOE_DIRECTORY = cboe_model.get_cboe_directory() - """ - try: - CBOE_DIRECTORY: pd.DataFrame = pd.read_csv( - "https://www.cboe.com/us/options/symboldir/equity_index_options/?download=csv" - ) - CBOE_DIRECTORY = CBOE_DIRECTORY.rename( - columns={ - " Stock Symbol": "Symbol", - " DPM Name": "DPM Name", - " Post/Station": "Post/Station", - } - ).set_index("Symbol") - - return CBOE_DIRECTORY - - except HTTPError: - return pd.DataFrame() - - -def get_cboe_index_directory() -> pd.DataFrame: - """Gets the US Listings Directory for the CBOE - - Returns - ------- - pd.DataFrame: CBOE_INDEXES - - Examples - ------- - >>> from openb_terminal.stocks.options import cboe_model - >>> CBOE_INDEXES = cboe_model.get_cboe_index_directory() - """ - - try: - CBOE_INDEXES: pd.DataFrame = pd.DataFrame( - pd.read_json( - "https://cdn.cboe.com/api/global/us_indices/definitions/all_indices.json" - ) - ) - - CBOE_INDEXES = CBOE_INDEXES.rename( - columns={ - "calc_end_time": "Close Time", - "calc_start_time": "Open Time", - "currency": "Currency", - "description": "Description", - "display": "Display", - "featured": "Featured", - "featured_order": "Featured Order", - "index_symbol": "Ticker", - "mkt_data_delay": "Data Delay", - "name": "Name", - "tick_days": "Tick Days", - "tick_frequency": "Frequency", - "tick_period": "Period", - "time_zone": "Time Zone", - }, - ) - - indices_order: list[str] = [ - "Ticker", - "Name", - "Description", - "Currency", - "Tick Days", - "Frequency", - "Period", - "Time Zone", - ] - - CBOE_INDEXES = pd.DataFrame(CBOE_INDEXES, columns=indices_order).set_index( - "Ticker" - ) - - return CBOE_INDEXES - - except HTTPError: - return pd.DataFrame() - - -# Gets the list of indexes for parsing the ticker symbol properly. -INDEXES = get_cboe_index_directory().index.tolist() -SYMBOLS = get_cboe_directory() - - -def get_ticker_info(symbol: str) -> Tuple[pd.DataFrame, list[str]]: - """Gets basic info for the symbol and expiration dates - - Parameters - ---------- - symbol: str - The ticker to lookup - - Returns - ------- - Tuple: [pd.DataFrame, pd.Series] - ticker_details - ticker_expirations - - Examples - -------- - >>> from openbb_terminal.stocks.options import cboe_model - >>> ticker_details,ticker_expirations = cboe_model.get_ticker_info('AAPL') - >>> vix_details,vix_expirations = cboe_model.get_ticker_info('VIX') - """ - - stock = "stock" - index = "index" - symbol = symbol.upper() - new_ticker: str = "" - ticker_details = pd.DataFrame() - ticker_expirations: list = [] - try: - if symbol in TICKER_EXCEPTIONS: - new_ticker = "^" + symbol - elif symbol not in INDEXES: - new_ticker = symbol - - elif symbol in INDEXES: - new_ticker = "^" + symbol - - # Gets the data to return, and if none returns empty Tuple # - - symbol_info_url = ( - "https://www.cboe.com/education/tools/trade-optimizer/symbol-info/?symbol=" - f"{new_ticker}" - ) - - symbol_info = request(symbol_info_url) - symbol_info_json = symbol_info.json() - symbol_info_json = pd.Series(symbol_info.json()) - - if symbol_info_json.success is False: - ticker_details = pd.DataFrame() - ticker_expirations = [] - console.print("No data found for the symbol: " f"{symbol}" "") - else: - symbol_details = pd.Series(symbol_info_json["details"]) - symbol_details = pd.DataFrame(symbol_details).transpose() - symbol_details = symbol_details.reset_index() - ticker_expirations = symbol_info_json["expirations"] - - # Cleans columns depending on if the security type is a stock or an index - - type_ = symbol_details.security_type - - if stock[0] in type_[0]: - stock_details = symbol_details - ticker_details = pd.DataFrame(stock_details).rename( - columns={ - "current_price": "price", - "bid_size": "bidSize", - "ask_size": "askSize", - "iv30": "ivThirty", - "prev_day_close": "previousClose", - "price_change": "change", - "price_change_percent": "changePercent", - "iv30_change": "ivThirtyChange", - "iv30_percent_change": "ivThirtyChangePercent", - "last_trade_time": "lastTradeTimestamp", - "exchange_id": "exchangeID", - "tick": "tick", - "security_type": "type", - } - ) - details_columns = [ - "symbol", - "type", - "tick", - "bid", - "bidSize", - "askSize", - "ask", - "price", - "open", - "high", - "low", - "close", - "volume", - "previousClose", - "change", - "changePercent", - "ivThirty", - "ivThirtyChange", - "ivThirtyChangePercent", - "lastTradeTimestamp", - ] - ticker_details = ( - pd.DataFrame(ticker_details, columns=details_columns) - .set_index(keys="symbol") - .dropna(axis=1) - .transpose() - ) - - if index[0] in type_[0]: - index_details = symbol_details - ticker_details = pd.DataFrame(index_details).rename( - columns={ - "symbol": "symbol", - "security_type": "type", - "current_price": "price", - "price_change": "change", - "price_change_percent": "changePercent", - "prev_day_close": "previousClose", - "iv30": "ivThirty", - "iv30_change": "ivThirtyChange", - "iv30_change_percent": "ivThirtyChangePercent", - "last_trade_time": "lastTradeTimestamp", - } - ) - - index_columns = [ - "symbol", - "type", - "tick", - "price", - "open", - "high", - "low", - "close", - "previousClose", - "change", - "changePercent", - "ivThirty", - "ivThirtyChange", - "ivThirtyChangePercent", - "lastTradeTimestamp", - ] - - ticker_details = ( - pd.DataFrame(ticker_details, columns=index_columns) - .set_index(keys="symbol") - .dropna(axis=1) - .transpose() - ).rename(columns={f"{new_ticker}": f"{symbol}"}) - - except HTTPError: - console.print("There was an error with the request'\n") - ticker_details = pd.DataFrame() - ticker_expirations = list() - return ticker_details, ticker_expirations - - return ticker_details, ticker_expirations - - -def get_ticker_iv(symbol: str) -> pd.DataFrame: - """Gets annualized high/low historical and implied volatility over 30/60/90 day windows. - - Parameters - ---------- - symbol: str - The loaded ticker - - Returns - ------- - pd.DataFrame: ticker_iv - - Examples - -------- - >>> from openbb_terminal.stocks.options import cboe_model - >>> ticker_iv = cboe_model.get_ticker_iv('AAPL') - >>> ndx_iv = cboe_model.get_ticker_iv('NDX') - """ - - # Checks ticker to determine if ticker is an index or an exception that requires modifying the request's URLs - try: - if symbol in TICKER_EXCEPTIONS: - quotes_iv_url = ( - "https://cdn.cboe.com/api/global/delayed_quotes/historical_data/_" - f"{symbol}.json" - ) - elif symbol not in INDEXES: - quotes_iv_url = ( - "https://cdn.cboe.com/api/global/delayed_quotes/historical_data/" - f"{symbol}.json" - ) - - elif symbol in INDEXES: - quotes_iv_url = ( - "https://cdn.cboe.com/api/global/delayed_quotes/historical_data/_" - f"{symbol}.json" - ) - h_iv = request(quotes_iv_url) - - if h_iv.status_code != 200: - console.print("No data found for the symbol: " f"{symbol}" "") - return pd.DataFrame() - - data = h_iv.json() - h_data = pd.DataFrame(data)[2:-1]["data"].rename(f"{symbol}") - h_data.rename( - { - "hv30_annual_high": "hvThirtyOneYearHigh", - "hv30_annual_low": "hvThirtyOneYearLow", - "hv60_annual_high": "hvSixtyOneYearHigh", - "hv60_annual_low": "hvsixtyOneYearLow", - "hv90_annual_high": "hvNinetyOneYearHigh", - "hv90_annual_low": "hvNinetyOneYearLow", - "iv30_annual_high": "ivThirtyOneYearHigh", - "iv30_annual_low": "ivThirtyOneYearLow", - "iv60_annual_high": "ivSixtyOneYearHigh", - "iv60_annual_low": "ivSixtyOneYearLow", - "iv90_annual_high": "ivNinetyOneYearHigh", - "iv90_annual_low": "ivNinetyOneYearLow", - }, - inplace=True, - ) - - iv_order = [ - "ivThirtyOneYearHigh", - "hvThirtyOneYearHigh", - "ivThirtyOneYearLow", - "hvThirtyOneYearLow", - "ivSixtyOneYearHigh", - "hvSixtyOneYearHigh", - "ivSixtyOneYearLow", - "hvsixtyOneYearLow", - "ivNinetyOneYearHigh", - "hvNinetyOneYearHigh", - "ivNinetyOneYearLow", - "hvNinetyOneYearLow", - ] - - ticker_iv = pd.DataFrame(h_data).transpose() - except HTTPError: - console.print("There was an error with the request'\n") - - return pd.DataFrame(ticker_iv, columns=iv_order).transpose() - - -def get_quotes(symbol: str) -> pd.DataFrame: - """Gets the complete options chains for a ticker. - - Parameters - ---------- - symbol: str - The ticker get options data for - - Returns - ------- - pd.DataFrame - DataFrame with all active options contracts for the underlying symbol. - - Examples - -------- - >>> from openbb_terminal.stocks.options import cboe_model - >>> xsp = cboe_model.OptionsChains().get_chains('XSP') - >>> xsp_chains = xsp.chains - """ - # Checks ticker to determine if ticker is an index or an exception that requires modifying the request's URLs. - - try: - if symbol in TICKER_EXCEPTIONS: - quotes_url = ( - "https://cdn.cboe.com/api/global/delayed_quotes/options/_" - f"{symbol}" - ".json" - ) - else: - if symbol not in INDEXES: - quotes_url = ( - "https://cdn.cboe.com/api/global/delayed_quotes/options/" - f"{symbol}" - ".json" - ) - if symbol in INDEXES: - quotes_url = ( - "https://cdn.cboe.com/api/global/delayed_quotes/options/_" - f"{symbol}" - ".json" - ) - - r = request(quotes_url) - if r.status_code != 200: - console.print("No data found for the symbol: " f"{symbol}" "") - return pd.DataFrame() - - r_json = r.json() - data = pd.DataFrame(r_json["data"]) - options = pd.Series(data.options, index=data.index) - options_columns = list(options[0]) - options_data = list(options[:]) - options_df = pd.DataFrame(options_data, columns=options_columns) - - options_df = options_df.rename( - columns={ - "option": "contractSymbol", - "bid_size": "bidSize", - "ask_size": "askSize", - "iv": "impliedVolatility", - "open_interest": "openInterest", - "theo": "theoretical", - "last_trade_price": "lastTradePrice", - "last_trade_time": "lastTradeTimestamp", - "percent_change": "changePercent", - "prev_day_close": "previousClose", - } - ) - - # Pareses the option symbols into columns for expiration, strike, and optionType - - option_df_index = options_df["contractSymbol"].str.extractall( - r"^(?P\D*)(?P\d*)(?P\D*)(?P\d*)" - ) - option_df_index = option_df_index.reset_index().drop( - columns=["match", "level_0"] - ) - option_df_index.optionType = option_df_index.optionType.str.replace( - "C", "call" - ).str.replace("P", "put") - option_df_index.strike = [ele.lstrip("0") for ele in option_df_index.strike] - option_df_index.strike = pd.Series(option_df_index.strike).astype(float) - option_df_index.strike = option_df_index.strike * (1 / 1000) - option_df_index.strike = option_df_index.strike.to_list() - option_df_index.expiration = [ - ele.lstrip("1") for ele in option_df_index.expiration - ] - option_df_index.expiration = pd.DatetimeIndex( - option_df_index.expiration, yearfirst=True - ).astype(str) - option_df_index = option_df_index.drop(columns=["Ticker"]) - - # Joins the parsed symbol into the dataframe. - - quotes = option_df_index.join(options_df) - - now = datetime.now() - temp = pd.DatetimeIndex(quotes.expiration) - temp_ = (temp - now).days + 1 - quotes["dte"] = temp_ - - quotes = quotes.set_index( - keys=["expiration", "strike", "optionType"] - ).sort_index() - quotes["openInterest"] = quotes["openInterest"].astype(int) - quotes["volume"] = quotes["volume"].astype(int) - quotes["bidSize"] = quotes["bidSize"].astype(int) - quotes["askSize"] = quotes["askSize"].astype(int) - quotes["previousClose"] = round(quotes["previousClose"], 2) - quotes["changePercent"] = round(quotes["changePercent"], 2) - - except HTTPError: - console.print("There was an error with the request'\n") - return pd.DataFrame() - - return quotes.reset_index() - - -def load_options(symbol: str, pydantic: bool = False) -> Options: - """OptionsChains data object for CBOE. - - Parameters - ---------- - symbol: str - The ticker symbol to load. - pydantic: bool - Whether to return the object as a Pydantic Model or a subscriptable Pandas Object. Default is False. - - Returns - ------- - object: OptionsChains - chains: dict - The complete options chain for the ticker. Returns as a Pandas DataFrame if pydantic is False. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: dict - The price and recent performance of the underlying asset. Returns as a Pandas Series if pydantic is False. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "CBOE". - SYMBOLS: dict - The CBOE symbol directory. Returns as a Pandas DataFrame if pydantic is False. - - Examples - -------- - Get current options chains for AAPL. - >>> from openbb_terminal.stocks.options.cboe_model import load_options - >>> data = load_options("AAPL") - >>> chains = data.chains - - Return the object as a Pydantic Model. - >>> from openbb_terminal.stocks.options.cboe_model import load_options - >>> data = load_options("AAPL", pydantic=True) - """ - OptionsChains = Options() - symbol = symbol.upper() - OptionsChains.SYMBOLS = SYMBOLS - OptionsChains.source = "CBOE" - OptionsChains.symbol = symbol - - if OptionsChains.symbol not in OptionsChains.SYMBOLS.index: - console.print( - "The symbol, " f"{symbol}" ", was not found in the CBOE directory." - ) - return OptionsChains - info, _ = get_ticker_info(OptionsChains.symbol) - if not info.empty: - iv = get_ticker_iv(OptionsChains.symbol) - info = pd.concat([info, iv]) - info = pd.Series(info[f"{OptionsChains.symbol}"]) - underlying_name = ( - OptionsChains.SYMBOLS.reset_index() - .query("`Symbol` == @symbol")["Company Name"] - .iloc[0] - ) - OptionsChains.underlying_name = underlying_name - OptionsChains.underlying_price = info - OptionsChains.last_price = round(info.loc["price"], 2) - OptionsChains.chains = get_quotes(OptionsChains.symbol) - if not OptionsChains.chains.empty: - OptionsChains.expirations = ( - OptionsChains.chains["expiration"].astype(str).unique().tolist() - ) - OptionsChains.strikes = ( - OptionsChains.chains["strike"].sort_values().unique().tolist() - ) - OptionsChains.hasIV = "impliedVolatility" in OptionsChains.chains.columns - OptionsChains.hasGreeks = "gamma" in OptionsChains.chains.columns - - if not pydantic: - return OptionsChains - - OptionsChainsPydantic = PydanticOptions( - chains=OptionsChains.chains.to_dict(), - expirations=OptionsChains.expirations, - strikes=OptionsChains.strikes, - last_price=OptionsChains.last_price, - underlying_name=OptionsChains.underlying_name, - underlying_price=OptionsChains.underlying_price.to_dict(), - hasIV=OptionsChains.hasIV, - hasGreeks=OptionsChains.hasGreeks, - symbol=OptionsChains.symbol, - source=OptionsChains.source, - SYMBOLS=OptionsChains.SYMBOLS.to_dict(), - ) - return OptionsChainsPydantic - - return OptionsChains - - return Options() diff --git a/openbb_terminal/stocks/options/chartexchange_model.py b/openbb_terminal/stocks/options/chartexchange_model.py deleted file mode 100644 index f1e4234ec18..00000000000 --- a/openbb_terminal/stocks/options/chartexchange_model.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Chartexchange model""" - -import logging -from typing import Optional, Union - -import pandas as pd -from bs4 import BeautifulSoup - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options.op_helpers import convert - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_option_history( - symbol: str = "GME", - date: str = "2021-02-05", - call: bool = True, - price: Union[str, Union[int, float]] = "90", - chain_id: Optional[str] = None, -) -> pd.DataFrame: - """Historic prices for a specific option [chartexchange] - - Parameters - ---------- - symbol : str - Ticker symbol to get historical data from - date : str - Date as a string YYYYMMDD - call : bool - Whether to show a call or a put - price : Union[str, Union[int, float]] - Strike price for a specific option - chain_id: str - Option symbol. Overwrites other inputs. ChartExchange uses the format: - `{TICKER}{YYYYMMDD}{C/P}{PRICE}`, where the price has no leading and trailing zeros. - - Returns - ------- - historical : pd.Dataframe - Historic information for an option - """ - if chain_id is None: - url = f"https://chartexchange.com/symbol/opra-{symbol.lower()}{date.replace('-', '')}" - url += f"{'c' if call else 'p'}{float(price):g}/historical/" - else: - url = f"https://chartexchange.com/symbol/opra-{chain_id}/historical/" - data = request(url, headers={"User-Agent": get_user_agent()}).content - soup = BeautifulSoup(data, "html.parser") - table = soup.find("div", attrs={"style": "display: table; font-size: 0.9em; "}) - if table: - rows = table.find_all("div", attrs={"style": "display: table-row;"}) - else: - return pd.DataFrame() - clean_rows = [] - - if rows: - for row in rows[1:]: - item = row.find_all("div") - clean_rows.append([x.text for x in item]) - else: - if chain_id is not None: - console.print( - f"No data for {chain_id}. Check that you are using OPRA notation.\n" - ) - return pd.DataFrame() - console.print("No data for this option\n") - return pd.DataFrame() - - df = pd.DataFrame() - df["Date"] = [x[0] for x in clean_rows] - df["Open"] = [convert(x[2], ",") for x in clean_rows] - df["High"] = [convert(x[3], ",") for x in clean_rows] - df["Low"] = [convert(x[4], ",") for x in clean_rows] - df["Close"] = [convert(x[5], ",") for x in clean_rows] - df["Change"] = [convert(x[6], "%") for x in clean_rows] - df["Volume"] = [convert(x[7], ",") for x in clean_rows] - df["Open Interest"] = [convert(x[8], ",") for x in clean_rows] - df["Change Since"] = [convert(x[9], "%") for x in clean_rows] - - return df diff --git a/openbb_terminal/stocks/options/chartexchange_view.py b/openbb_terminal/stocks/options/chartexchange_view.py deleted file mode 100644 index cd79a808566..00000000000 --- a/openbb_terminal/stocks/options/chartexchange_view.py +++ /dev/null @@ -1,137 +0,0 @@ -"""Chartexchange view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options import chartexchange_model - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def plot_chart( - df: pd.DataFrame, option_type: str, symbol: str, price: float -) -> OpenBBFigure: - """Plot Candlestick chart - - Parameters - ---------- - df : pd.DataFrame - Dataframe with OHLC data - option_type : str - Type of option (call or put) - symbol : str - Ticker symbol - - Returns - ------- - OpenBBFigure - Plotly figure object - """ - titles_list = ["Historical", symbol, price, option_type.title()] - - fig = OpenBBFigure.create_subplots( - rows=1, - cols=1, - vertical_spacing=0.06, - specs=[[{"secondary_y": True}]], - ) - fig.set_title(" ".join(str(x) for x in titles_list if x)) - - fig.add_candlestick( - open=df.Open, - high=df.High, - low=df.Low, - close=df.Close, - x=df.index, - name=f"{price} {option_type.title()} OHLC", - row=1, - col=1, - secondary_y=False, - ) - fig.add_inchart_volume(df) - - return fig - - -@log_start_end(log=logger) -def display_raw( - symbol: str = "GME", - expiry: str = "2021-02-05", - call: bool = True, - price: float = 90, - limit: int = 10, - chain_id: Optional[str] = None, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Return raw stock data[chartexchange] - - Parameters - ---------- - symbol : str - Ticker symbol for the given option - expiry : str - The expiry of expiration, format "YYYY-MM-DD", i.e. 2010-12-31. - call : bool - Whether the underlying asset should be a call or a put - price : float - The strike of the expiration - limit : int - Number of rows to show - chain_id: str - Optional chain id instead of ticker and expiry and strike - export : str - Export data as CSV, JSON, XLSX - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df = chartexchange_model.get_option_history(symbol, expiry, call, price, chain_id)[ - ::-1 - ] - if df.empty: - return console.print("[red]No data found[/red]\n") - df["Date"] = pd.to_datetime(df["Date"]) - df = df.set_index("Date") - - option_type = "call" if call else "put" - fig = plot_chart(df, option_type, symbol, price) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hist", - df, - sheet_name, - fig, - ) - return None - - if raw: - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - index_name="Date", - title="Historical Option Prices", - export=bool(export), - limit=limit, - ) - return None - - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/options/fdscanner_model.py b/openbb_terminal/stocks/options/fdscanner_model.py deleted file mode 100644 index 713b6769187..00000000000 --- a/openbb_terminal/stocks/options/fdscanner_model.py +++ /dev/null @@ -1,93 +0,0 @@ -"""FDScanner model""" - -__docformat__ = "numpy" - -import logging -from typing import Tuple - -import numpy as np -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_agent, request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def unusual_options(limit: int = 100) -> Tuple[pd.DataFrame, pd.Timestamp]: - """Get unusual option activity from fdscanner.com - - Parameters - ---------- - limit: int - Number to show - - Returns - ------- - Tuple[pd.DataFrame, pd.Timestamp] - Dataframe containing options information, Timestamp indicated when data was updated from website - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> unu_df = openbb.stocks.options.unu() - """ - pages = np.arange(0, limit // 20 + 1) - data_list = [] - for page_num in pages: - r = request( - f"https://app.fdscanner.com/api2/unusualvolume?p=0&page_size=20&page={int(page_num)}", - headers={"User-Agent": get_user_agent()}, - ) - - if r.status_code != 200: - console.print("Error in fdscanner request") - return pd.DataFrame(), "request error" - - data_list.append(r.json()) - - ticker, expiry, option_strike, option_type, ask, bid, oi, vol, voi = ( - [], - [], - [], - [], - [], - [], - [], - [], - [], - ) - for data in data_list: - for entry in data["data"]: - ticker.append(entry["tk"]) - expiry.append(entry["expiry"]) - option_strike.append(float(entry["s"])) - option_type.append("Put" if entry["t"] == "P" else "Call") - ask.append(entry["a"]) - bid.append(entry["b"]) - oi.append(entry["oi"]) - vol.append(entry["v"]) - voi.append(entry["vol/oi"]) - - # Subtract an hour to align with NYSE timezone - last_updated = pd.to_datetime( - data_list[-1]["last_updated"], unit="s" - ) - pd.Timedelta(hours=5) - - df = pd.DataFrame( - { - "Ticker": ticker, - "Exp": expiry, - "Strike": option_strike, - "Type": option_type, - "Vol/OI": voi, - "Vol": vol, - "OI": oi, - "Bid": bid, - "Ask": ask, - } - ) - - return df, last_updated diff --git a/openbb_terminal/stocks/options/fdscanner_view.py b/openbb_terminal/stocks/options/fdscanner_view.py deleted file mode 100644 index 2ae916ce81b..00000000000 --- a/openbb_terminal/stocks/options/fdscanner_view.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Fdscanner view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.stocks.options import fdscanner_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_options( - limit: int = 20, - sortby: str = "Vol/OI", - ascend: bool = False, - calls_only: bool = False, - puts_only: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -): - """Displays the unusual options table - - Parameters - ---------- - limit: int - Number of rows to show - sortby: str - Data column to sort on - ascend: bool - Whether to sort in ascend order - calls_only : bool - Flag to only show calls - puts_only : bool - Flag to show puts only - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - File type to export - """ - data, last_update = fdscanner_model.unusual_options(limit) - data = data.sort_values(by=sortby, ascending=ascend) - if puts_only: - data = data[data.Type == "Put"] - if calls_only: - data = data[data.Type == "Call"] - print_rich_table( - data, - headers=list(data.columns), - show_index=False, - title=f"Last Updated: {last_update} (EST)", - export=bool(export), - limit=limit, - ) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "unu_", - data, - sheet_name, - ) diff --git a/openbb_terminal/stocks/options/intrinio_model.py b/openbb_terminal/stocks/options/intrinio_model.py deleted file mode 100644 index 9d352e993c8..00000000000 --- a/openbb_terminal/stocks/options/intrinio_model.py +++ /dev/null @@ -1,754 +0,0 @@ -# mypy: disable-error-code=attr-defined - -"""Intrinio model""" -__docformat__ = "numpy" -import logging -from datetime import datetime, timedelta -from typing import List - -import intrinio_sdk as intrinio -import numpy as np -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.rich_config import console, optional_rich_track -from openbb_terminal.stocks.options.op_helpers import Options, PydanticOptions - -logger = logging.getLogger(__name__) -intrinio.ApiClient().set_api_key(key=get_current_user().credentials.API_INTRINIO_KEY) # type: ignore[attr-defined] -api = intrinio.OptionsApi() - -eod_columns_to_drop = [ - "open_ask", - "open_bid", - "ask_high", - "ask_low", - "bid_high", - "bid_low", - "mark", -] -columns_to_drop = [ - "id", - "ticker", - "date", - "close_bid", - "close_ask", - "volume_bid", - "volume_ask", - "trades", - "open_interest_change", - "next_day_open_interest", - "implied_volatility_change", -] - -# These tickers are equity indexes that trade options and return data, but require modifying the API call. -TICKER_EXCEPTIONS = [ - "SPX", - "XSP", - "XEO", - "NDX", - "XND", - "VIX", - "RUT", - "MRUT", - "DJX", - "XAU", - "OEX", -] - - -def calculate_dte(chain_df: pd.DataFrame) -> pd.DataFrame: - """Adds a column calculating the difference between expiration and the date data is from - - Parameters - ---------- - chain_df : pd.DataFrame - Dataframe of intrinio options. Must have date and expiration columns - - Returns - ------- - pd.DataFrame - Dataframe with dte column added - """ - if "date" not in chain_df.columns: - console.print("date column not in dataframe") - return pd.DataFrame() - if "expiration" not in chain_df.columns: - console.print("expiration column not in dataframe") - return pd.DataFrame() - chain_df["dte"] = chain_df.apply( - lambda row: ( - datetime.strptime(row["expiration"], "%Y-%m-%d") - - datetime.strptime(row["date"], "%Y-%m-%d") - ).days, - axis=1, - ) - return chain_df - - -@log_start_end(log=logger) -@check_api_key(["API_INTRINIO_KEY"]) -def get_expiration_dates( - symbol: str, - start: str = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d"), - end: str = "2100-12-30", -) -> List[str]: - """Get all expirations from a start date until a specified end date. Defaults to 100 years out to get all possible. - - Parameters - ---------- - symbol : str - Intrinio symbol to get expirations for - start : str - Start date for expirations, by default T-1 so that current day is included - end : str, optional - End date for intrinio endpoint, by default "2100-12-30" - - Returns - ------- - List[str] - List of expiration dates - """ - return api.get_options_expirations(symbol, after=start, before=end).to_dict()[ - "expirations" - ] - - -@log_start_end(log=logger) -@check_api_key(["API_INTRINIO_KEY"]) -def get_option_chain( - symbol: str, - expiration: str, -) -> pd.DataFrame: - """Get option chain at a given expiration - - Parameters - ---------- - symbol : str - Ticker to get option chain for - expiration : str - Expiration to get chain for - - Returns - ------- - pd.DataFrame - Dataframe of option chain - """ - response = api.get_options_chain( - symbol, - expiration, - ).chain_dict - chain = pd.DataFrame() - if not response: - return pd.DataFrame() - for opt in response: - chain = pd.concat([chain, pd.json_normalize(opt)]) - chain.columns = [ - col.replace("option.", "").replace("price.", "") for col in chain.columns - ] - chain = chain.drop(columns=columns_to_drop).reset_index(drop=True) - # Fill nan values with 0 - chain = chain.fillna(0).rename( - columns={ - "type": "optionType", - "open_interest": "openInterest", - "implied_volatility": "impliedVolatility", - } - ) - return chain - - -@log_start_end(log=logger) -@check_api_key(["API_INTRINIO_KEY"]) -def get_full_option_chain(symbol: str, quiet: bool = False) -> pd.DataFrame: - """Get option chain across all expirations - - Parameters - ---------- - symbol : str - Symbol to get option chain for - quiet: bool - Flag to suppress progress bar - - Returns - ------- - pd.DataFrame - DataFrame of option chain - """ - expirations = get_expiration_dates(symbol) - chain = pd.DataFrame() - for exp in optional_rich_track( - expirations, suppress_output=quiet, desc="Getting option chain" - ): - chain = pd.concat([chain, get_option_chain(symbol, exp)]) - return chain.sort_values(by=["expiration", "strike"]).reset_index(drop=True) - - -@log_start_end(log=logger) -@check_api_key(["API_INTRINIO_KEY"]) -def get_eod_chain_at_expiry_given_date( - symbol: str, expiration: str, date: str, fillnans: bool = True -): - """Get the eod chain for a given expiration at a given close day - - Parameters - ---------- - symbol : str - Symbol to get option chain for - expiration : str - Expiration day of option chain - date : str - Date to get option chain for - fillnans : bool, optional - Flag to fill nan values with 0, by default True - - Returns - ------- - pd.DataFrame - Dataframe of option chain - """ - chain = pd.DataFrame() - response = api.get_options_chain_eod(symbol, expiration, date=date).chain_dict - if not response: - return pd.DataFrame() - for opt in response: - chain = pd.concat([chain, pd.json_normalize(opt)]) - chain.columns = [ - col.replace("option.", "").replace("prices.", "") for col in chain.columns - ] - chain = chain.drop(columns=eod_columns_to_drop).reset_index(drop=True) - if fillnans: - # Replace None with 0 - chain = chain.fillna(0) - return chain - - -@log_start_end(log=logger) -@check_api_key(["API_INTRINIO_KEY"]) -def get_full_chain_eod(symbol: str, date: str, quiet: bool = False) -> pd.DataFrame: - """Get full EOD option date across all expirations - - Parameters - ---------- - symbol : str - Symbol to get option chain for - date : str - Date to get EOD chain for - quiet:bool - Flag to suppress progress bar - - Returns - ------- - pd.DataFrame - Dataframe of options across all expirations at a given close - - Example - ------- - To get the EOD chain for AAPL on Dec 23, 2022, we do the following - - >>> from openbb_terminal.sdk import openbb - >>> eod_chain = openbb.stocks.options.eodchain("AAPL", date="2022-12-23") - - """ - expirations = get_expiration_dates(symbol, start=date) - # Since we can't have expirations in the past, lets do something fun: - # Note that there is an issue with using >= in that when the date = expiration, the dte will be 0, so iv*dte = 0*0 - expirations = list(filter(lambda x: x > date, expirations)) - - full_chain = pd.DataFrame() - - for exp in optional_rich_track( - expirations, suppress_output=quiet, desc="Getting Option Chain" - ): - temp = get_eod_chain_at_expiry_given_date(symbol, exp, date) - if not temp.empty: - full_chain = pd.concat([full_chain, temp]) - if full_chain.empty: - return pd.DataFrame() - full_chain = ( - full_chain.sort_values(by=["expiration", "strike"]) - .reset_index(drop=True) - .rename( - columns={ - "type": "optionType", - "open_interest": "openInterest", - "implied_volatility": "impliedVolatility", - } - ) - ) - if full_chain.empty: - logger.info("No options found for %s on %s", symbol, date) - return pd.DataFrame() - return calculate_dte(full_chain) - - -@log_start_end(log=logger) -@check_api_key(["API_INTRINIO_KEY"]) -def get_close_at_date(symbol: str, date: str) -> float: - """Get adjusted close price at a given date - - Parameters - ---------- - symbol : str - Symbol to get price for - date : str - Date of close price - - Returns - ------- - float - close price - """ - # Catch error if the price or ticker does not exist - try: - return ( - intrinio.SecurityApi() - .get_security_historical_data( - symbol, - "adj_close_price", - start_date=date, - end_date=date, - frequency="daily", - ) - .to_dict()["historical_data"][0]["value"] - ) - except Exception as e: - logger.error( - "Error getting close price for %s on %s, error: %s", symbol, date, e - ) - return np.nan - - -@log_start_end(log=logger) -@check_api_key(["API_INTRINIO_KEY"]) -def get_last_price(symbol: str) -> float: - """Get the last price of a ticker - - Parameters - ---------- - symbol : str - Ticker to get last price for - - Returns - ------- - float - Last price of ticker - """ - return ( - intrinio.SecurityApi().get_security_realtime_price(symbol, source="").last_price - ) - - -@log_start_end(log=logger) -@check_api_key(["API_INTRINIO_KEY"]) -def get_historical_options(symbol: str) -> pd.DataFrame: - """Get historical EOD option prices, with Greeks, for a given OCC chain label. - - Parameters - ---------- - symbol : str - Symbol to get historical option chain for. Should be an OCC chain label. - - Returns - ------- - pd.DataFrame - Dataframe of historical option chain. - """ - try: - historical = pd.DataFrame( - api.get_options_prices_eod(symbol).to_dict()["prices"] - ) - except Exception: - return pd.DataFrame() - historical = historical[ - [ - "date", - "close", - "volume", - "open", - "open_interest", - "high", - "low", - "implied_volatility", - "delta", - "gamma", - "theta", - "vega", - ] - ] - historical["date"] = pd.DatetimeIndex(historical.date) - # Just in case - historical = ( - historical.set_index("date") - .rename( - columns={ - "open_interest": "openInterest", - "implied_volatility": "impliedVolatility", - } - ) - .fillna(0) - ) - return historical - - -@check_api_key(["API_INTRINIO_KEY"]) -def get_all_ticker_symbols() -> list[str]: - """Gets a list of all options tickers supported by Intrinio. - - Returns - ------- - list: str - List of all ticker symbols supported. - """ - - return api.get_all_options_tickers().tickers - - -@check_api_key(["API_INTRINIO_KEY"]) -def get_ticker_info(symbol: str) -> pd.Series: - """Gets basic meta data for the ticker. - - Parameters - ---------- - symbol: str - The ticker symbol to lookup. - - Returns - ------- - pd.Series - Pandas Series object with meta data for the symbol. - """ - - symbol = symbol.upper() - - try: - info = intrinio.SecurityApi().get_security_by_id(symbol).to_dict() - except Exception: - console.print("Security not found") - return {} - - info = pd.Series(info).rename( - { - "company_id": "companyID", - "share_class": "shareClass", - "round_lot_size": "roundLotSize", - "exchange_ticker": "exchangeTicker", - "composite_ticker": "compositeTicker", - "alternate_tickers": "alternateTickers", - "composite_figi": "compositeFIGI", - "share_class_figi": "shareClassFIGI", - "figi_uniqueid": "figiUniqueID", - "primary_listing": "primaryListing", - "first_stock_price": "firstStockPrice", - "last_stock_price": "lastStockPrice", - "last_stock_price_adjustment": "lastStockPriceAdjustment", - "last_corporate_action": "lastCorporateAction", - "previous_tickers": "previousTickers", - "listing_exchange_mic": "listingExchangeMIC", - } - ) - - return info.rename(info["ticker"]) - - -@check_api_key(["API_INTRINIO_KEY"]) -def get_underlying_price(symbol: str) -> pd.Series: - """Gets the real time bid/ask and last price for the symbol. - - Parameters - ---------- - symbol: str - The ticker symbol to lookup. - - Returns - ------- - pd.Series - Pandas Series object with real time bid/ask and last price for the symbol. - - Example - ------- - >>> from openbb_terminal.stocks.options.intrinio_model import get_underlying_price - >>> underlying = get_underlying_price("AAPL") - - """ - data = intrinio.SecurityApi().get_security_realtime_price(symbol).to_dict() - - underlying_price = pd.Series(data).rename( - { - "last_price": "lastPrice", - "last_time": "timestamp", - "last_size": "lastSize", - "bid_price": "bidPrice", - "bid_size": "bidSize", - "ask_price": "askPrice", - "ask_size": "askSize", - "open_price": "open", - "close_price": "close", - "high_price": "high", - "low_price": "low", - "exchange_volume": "volume", - "market_volume": "marketVolume", - "updated_on": "lastUpdated", - } - ) - - return underlying_price.rename(underlying_price["security"]["ticker"]) - - -def get_eod_chains(symbol: str, date: str) -> Options: - """Internal function for loading historical EOD option prices. This function is called from `load_options()`. - - Parameters - ---------- - symbol : str - The ticker symbol to lookup. - date: str - The date for the EOD chains data. Format: YYYY-MM-DD - - object: OptionsChains - chains: pd.DataFrame - The complete options chain for the ticker. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "Intrinio". - SYMBOLS: list - The list of valid Intrinio symbols. - """ - OptionsChains = Options() - OptionsChains.source = "Intrinio" - OptionsChains.date = date - symbol = symbol.upper() - OptionsChains.SYMBOLS = get_all_ticker_symbols() - if symbol not in OptionsChains.SYMBOLS: - console.print(f"{symbol}", "is not supported by Intrinio.") - return OptionsChains - OptionsChains.symbol = symbol - if symbol not in TICKER_EXCEPTIONS: - underlying = ( - intrinio.SecurityApi() - .get_security_stock_prices( - symbol, start_date=date, end_date=date, frequency="daily" - ) - .to_dict() - ) - - OptionsChains.underlying_name = underlying["security"]["name"] - underlying = pd.Series(underlying["stock_prices"][0]) - underlying = underlying.rename( - { - "adj_close": "adjClose", - "adj_high": "adjHigh", - "adj_low": "adjLow", - "adj_open": "adjOpen", - "adj_volume": "adjVolume", - "split_ratio": "splitRatio", - "percent_change": "changePercent", - "fifty_two_week_high": "fiftyTwoWeekHigh", - "fifty_two_week_low": "fiftyTwoWeekLow", - } - ) - OptionsChains.underlying_price = underlying.rename(f"{OptionsChains.symbol}") - OptionsChains.last_price = underlying["adjClose"] - # If the ticker is an index, it will not return underlying data. - else: - console.print("Warning: Index options do not currently return underlying data.") - OptionsChains.underlying_name = OptionsChains.symbol - OptionsChains.underlying_price = pd.Series(dtype=object) - OptionsChains.last_price = 0 - - # If the symbol is an index, it needs to be preceded with, $. - if OptionsChains.symbol in TICKER_EXCEPTIONS: - OptionsChains.symbol = "$" + OptionsChains.symbol - - chains = get_full_chain_eod(OptionsChains.symbol, date) - chains.rename( - columns={"code": "optionSymbol", "close_bid": "bid", "close_ask": "ask"}, - inplace=True, - ) - chains = chains.set_index(["expiration", "strike", "optionType"]).sort_index() - chains.reset_index(inplace=True) - OptionsChains.chains = chains.drop(columns=["ticker"]) - if not OptionsChains.chains.empty: - OptionsChains.expirations = OptionsChains.chains["expiration"].unique().tolist() - OptionsChains.strikes = ( - OptionsChains.chains["strike"].sort_values().unique().tolist() - ) - OptionsChains.hasIV = ( - "impliedVolatility" in OptionsChains.chains.columns - and OptionsChains.chains.impliedVolatility.sum() > 0 - ) - OptionsChains.hasGreeks = ( - "gamma" in OptionsChains.chains.columns and OptionsChains.chains.gamma.sum() > 0 - ) - - return OptionsChains - - -@check_api_key(["API_INTRINIO_KEY"]) -def load_options(symbol: str, date: str = "", pydantic=False) -> Options: - """OptionsChains data object for Intrinio. - - Parameters - ---------- - symbol : str - The ticker symbol to load. - date: Optional[str] - The date for EOD chains data. - pydantic: bool - Whether to return the object as a Pydantic Model or a subscriptable Pandas Object. Default is False. - - Returns - ------- - object: OptionsChains - chains: dict - The complete options chain for the ticker. Returns as a Pandas DataFrame if pydantic is False. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: dict - The price and recent performance of the underlying asset. Returns as a Pandas Series if pydantic is False. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "Intrinio". - date: str - The date, if applicable, for the EOD chains data. (YYYY-MM-DD) - SYMBOLS: list - The list of valid Intrinio symbols. - - Examples - -------- - Get current options chains for AAPL. - >>> from openbb_terminal.stocks.options.intrinio_model import load_options - >>> data = load_options("AAPL") - >>> chains = data.chains - - Get options chains for AAPL for a specific date. - >>> from openbb_terminal.stocks.options.intrinio_model import load_options - >>> data = load_options("AAPL", "2022-01-03") - >>> chains = data.chains - - Return the object as a Pydantic Model. - >>> from openbb_terminal.stocks.options.intrinio_model import load_options - >>> data = load_options("AAPL", pydantic=True) - """ - OptionsChains = Options() - OptionsChains.source = "Intrinio" - symbol = symbol.upper() - OptionsChains.SYMBOLS = get_all_ticker_symbols() - - if symbol not in OptionsChains.SYMBOLS: - console.print(f"{symbol}", "is not supported by Intrinio.") - return OptionsChains - OptionsChains.symbol = symbol - - if date and date != "": - options = get_eod_chains(OptionsChains.symbol, date) - - if not pydantic: - return options - - if not options.chains.empty: - options.SYMBOLS = None - OptionsChainsPydantic = PydanticOptions( - chains=options.chains.to_dict(), - expirations=options.expirations, - strikes=options.strikes, - last_price=options.last_price, - underlying_name=options.underlying_name, - underlying_price=options.underlying_price.to_dict(), - hasIV=options.hasIV, - hasGreeks=options.hasGreeks, - symbol=options.symbol, - source=options.source, - date=options.date, - SYMBOLS=options.SYMBOLS, - ) - return OptionsChainsPydantic - - return OptionsChains - - if symbol not in TICKER_EXCEPTIONS: - OptionsChains.underlying_price = get_underlying_price(OptionsChains.symbol) - OptionsChains.last_price = OptionsChains.underlying_price["lastPrice"] - OptionsChains.underlying_name = get_ticker_info(OptionsChains.symbol)["name"] - - # If the ticker is an index, it will not return underlying data. - else: - console.print("Warning: Index options do not currently return underlying data.") - OptionsChains.underlying_name = OptionsChains.symbol - OptionsChains.underlying_price = pd.Series(dtype=object) - OptionsChains.last_price = 0 - - # If the symbol is an index, it needs to be preceded with, $. - if OptionsChains.symbol in TICKER_EXCEPTIONS: - OptionsChains.symbol = "$" + OptionsChains.symbol - - chains = get_full_option_chain(OptionsChains.symbol) - chains.rename(columns={"code": "optionSymbol"}, inplace=True) - - now = datetime.now() - temp = pd.DatetimeIndex(chains.expiration) - temp_ = (temp - now).days + 1 - chains["dte"] = temp_ - - chains["close"] = round(chains["close"], 2) - - chains = chains.set_index(["expiration", "strike", "optionType"]).sort_index() - - OptionsChains.chains = chains.reset_index() - - if not OptionsChains.chains.empty: - OptionsChains.expirations = OptionsChains.chains["expiration"].unique().tolist() - OptionsChains.strikes = ( - OptionsChains.chains["strike"].sort_values().unique().tolist() - ) - - OptionsChains.hasIV = "impliedVolatility" in OptionsChains.chains.columns - OptionsChains.hasGreeks = "gamma" in OptionsChains.chains.columns - - if pydantic is True: - OptionsChains.SYMBOLS = None - OptionsChainsPydantic = PydanticOptions( - chains=OptionsChains.chains.to_dict(), - expirations=OptionsChains.expirations, - strikes=OptionsChains.strikes, - last_price=OptionsChains.last_price, - underlying_name=OptionsChains.underlying_name, - underlying_price=OptionsChains.underlying_price.to_dict(), - hasIV=OptionsChains.hasIV, - hasGreeks=OptionsChains.hasGreeks, - symbol=OptionsChains.symbol, - source=OptionsChains.source, - SYMBOLS=OptionsChains.SYMBOLS, - ) - return OptionsChainsPydantic - - return OptionsChains - - return Options() diff --git a/openbb_terminal/stocks/options/intrinio_view.py b/openbb_terminal/stocks/options/intrinio_view.py deleted file mode 100644 index e9d7770d82b..00000000000 --- a/openbb_terminal/stocks/options/intrinio_view.py +++ /dev/null @@ -1,240 +0,0 @@ -"""Intrinio View Functions""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options import intrinio_model - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -@log_start_end(log=logger) -def display_historical( - symbol: str, - expiry: str, - strike: float = 0, - put: bool = False, - raw: bool = False, - chain_id: Optional[str] = None, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plot historical option prices - - Parameters - ---------- - symbol: str - Stock ticker symbol - expiry: str - Expiry date of option - strike: float - Option strike price - put: bool - Is this a put option? - raw: bool - Print raw data - chain_id: str - OCC option symbol - export: str - Format of export file - sheet_name: str - Optionally specify the name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - op_type = ["Call", "Put"][put] - if chain_id is not None: - df_hist = intrinio_model.get_historical_options(chain_id) - else: - chain_id = f"{symbol}{''.join(expiry[2:].split('-'))}{'P' if put else 'C'}{str(int(1000*strike)).zfill(8)}" - df_hist = intrinio_model.get_historical_options(chain_id) - - if df_hist.empty: - console.print(f"[red]No data found for {chain_id}[/red]") - return None - - if raw: - print_rich_table( - df_hist, - headers=[x.title() for x in df_hist.columns], - title="Historical Option Prices", - export=bool(export), - show_index=True, - index_name="Date", - ) - - df_hist.columns = [x.title() for x in df_hist.columns] - - titles_list = [symbol, strike, op_type] - fig = OpenBBFigure.create_subplots( - rows=1, - cols=1, - specs=[[{"secondary_y": True}]], - vertical_spacing=0.03, - ).set_title(" ".join([str(x) for x in titles_list if x])) - - fig.add_candlestick( - open=df_hist["Open"], - high=df_hist["High"], - low=df_hist["Low"], - close=df_hist["Close"], - x=df_hist.index, - name=f"{symbol} OHLC", - row=1, - col=1, - secondary_y=False, - ) - fig.add_inchart_volume(df_hist) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hist", - df_hist, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def view_historical_greeks( - symbol: str = "", - expiry: str = "", - strike: Union[float, str] = 0, - greek: str = "Delta", - chain_id: str = "", - put: bool = False, - raw: bool = False, - limit: Union[int, str] = 20, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots historical greeks for a given option. - - Parameters - ---------- - symbol: str - Stock ticker - expiry: str - Expiration date - strike: Union[str, float] - Strike price to consider - greek: str - Greek variable to plot - chain_id: str - OCC option chain. Overwrites other variables - put: bool - Is this a put option? - raw: bool - Print to console - limit: int - Number of rows to show in raw - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - if chain_id: - df = intrinio_model.get_historical_options(chain_id) - title = f"{(greek).capitalize()} historical for {chain_id}" - else: - chain_id = f"{symbol}{''.join(expiry[2:].split('-'))}{'P' if put else 'C'}{str(int(1000*strike)).zfill(8)}" - df = intrinio_model.get_historical_options(chain_id) - title = ( - f"Historical {(greek).capitalize()} for {symbol.upper()}" - f"{strike} {['Call','Put'][put]} and {expiry} Expiration" - ) - if df.empty: - console.print(f"No data found for {chain_id}") - return None - - df = df.rename(columns={"impliedVolatility": "iv", "close": "price"}) - - if isinstance(limit, str): - try: - limit = int(limit) - except ValueError: - return console.print( - f"[red]Could not convert limit of {limit} to a number.[/red]\n" - ) - - if raw: - df = df.sort_index(ascending=False) - print_rich_table( - df, - headers=list(df.columns), - title="Historical Greeks", - show_index=True, - export=bool(export), - limit=limit, - ) - - if greek.lower() not in df.columns: - return console.print(f"[red]Could not find greek {greek} in data.[/red]\n") - - fig = OpenBBFigure.create_subplots( - shared_xaxes=True, - specs=[[{"secondary_y": True}]], - vertical_spacing=0.03, - horizontal_spacing=0.1, - ) - fig.set_title(title) - - fig.add_scatter( - x=df.index, - y=df.price.values, - name="Option Premium", - line=dict(color=theme.down_color), - secondary_y=False, - ) - fig.add_scatter( - x=df.index, - y=df[greek.lower()].values, - name=greek.title(), - line=dict(color=theme.up_color), - yaxis="y2", - secondary_y=True, - ) - fig.update_layout( - yaxis2=dict( - side="left", - title=greek.title(), - anchor="x", - overlaying="y", - ), - yaxis=dict( - title=f"{symbol} Option Premium", - side="right", - ), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "grhist", - df, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/stocks/options/nasdaq_model.py b/openbb_terminal/stocks/options/nasdaq_model.py deleted file mode 100644 index 399c3d38eeb..00000000000 --- a/openbb_terminal/stocks/options/nasdaq_model.py +++ /dev/null @@ -1,499 +0,0 @@ -# mypy: disable-error-code=attr-defined - -"""Nasdaq Model""" -__docformat__ = "numpy" - -import logging -from datetime import datetime, timedelta -from typing import List - -import numpy as np -import pandas as pd -import pandas_datareader - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options.op_helpers import Options, PydanticOptions - -logger = logging.getLogger(__name__) -# pylint: disable=unsupported-assignment-operation - - -call_cols = [ - "c_Last", - "c_Bid", - "c_Ask", - "c_Volume", - "c_Openinterest", - "strike", - "expiration", -] -put_cols = [ - "p_Last", - "p_Bid", - "p_Ask", - "p_Volume", - "p_Openinterest", - "strike", - "expiration", -] -cols = ["lastPrice", "bid", "ask", "volume", "openInterest", "strike", "expiration"] - -sorted_chain_columns = [ - "optionType", - "expiration", - "strike", - "lastPrice", - "bid", - "ask", - "openInterest", - "volume", -] - - -def get_dte_from_expiration(date: str) -> float: - """ - Converts a date to total days until the option would expire. - This assumes that the date is in the form %B %d, %Y such as January 11, 2023 - This calculates time from 'now' to 4 PM the date of expiration - This is particularly a helper for nasdaq results. - - Parameters - ---------- - date: str - Date in format %B %d, %Y - - Returns - ------- - float - Days to expiration as a decimal - """ - # Get the date as a datetime and add 16 hours (4PM) - expiration_time = datetime.strptime(date, "%B %d, %Y") + timedelta(hours=16) - # Find total seconds from now - time_to_now = (expiration_time - datetime.now()).total_seconds() - # Convert to days - time_to_now /= 60 * 60 * 24 - return time_to_now - - -@log_start_end(log=logger) -def get_full_option_chain(symbol: str) -> pd.DataFrame: - """Get the full option chain for symbol over all expirations - - Parameters - ---------- - symbol: str - Symbol to get options for. Can be a stock, etf or index. - - Returns - ------- - pd.DataFrame - Dataframe of option chain - """ - # Nasdaq requires an asset code, so instead of making user supply one, just loop through all - for asset in ["stocks", "index", "etf"]: - url = ( - f"https://api.nasdaq.com/api/quote/{symbol}/option-chain?assetclass={asset}&" - "fromdate=2010-09-09&todate=2030-09-09&excode=oprac&callput=callput&money=all&type=all" - ) - # I have had issues with nasdaq requests, and this user agent seems to work in US and EU - response_json = request( - url, - headers={ - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" - " AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15" - }, - ).json() - - if response_json["status"]["rCode"] == 200: - return process_response(response_json) - - console.print(f"[red]{symbol} Option Chain not found.[/red]\n") - return pd.DataFrame() - - -def process_response(response_json): - df = pd.DataFrame(response_json["data"]["table"]["rows"]).drop( - columns=["c_colour", "p_colour", "drillDownURL"] - ) - df["expirygroup"] = df["expirygroup"].replace("", np.nan).fillna(method="ffill") - df["expiration"] = pd.to_datetime( - df["expirygroup"], format="%B %d, %Y" - ).dt.strftime("%Y-%m-%d") - # Make numeric - columns_w_types = { - "c_Last": float, - "c_Change": float, - "c_Bid": float, - "c_Ask": float, - "c_Volume": int, - "c_Openinterest": int, - "strike": float, - "p_Last": float, - "p_Change": float, - "p_Bid": float, - "p_Ask": float, - "p_Volume": int, - "p_Openinterest": int, - } - - for key, _ in columns_w_types.items(): - df[key] = df[key].replace(",", "", regex=True) - - df = df.fillna(np.nan).dropna(axis=0).replace("--", 0).astype(columns_w_types) - df["DTE"] = df["expirygroup"].apply(lambda t: get_dte_from_expiration(t)) - df = df[df.DTE > 0] - df = df.drop(columns=["DTE"]) - - # Process into consistent format - calls = df[call_cols].copy() - puts = df[put_cols].copy() - - calls.columns = cols - puts.columns = cols - calls["optionType"] = "call" - puts["optionType"] = "put" - - chain = ( - pd.concat([calls, puts], axis=0) - .sort_values(by=["expiration", "strike"]) - .reset_index(drop=True) - ) - - return chain[sorted_chain_columns] - - -@log_start_end(log=logger) -def option_expirations(symbol: str) -> List[str]: - """Get available expirations - - Parameters - ---------- - symbol : str - Ticker symbol to get expirations for - - Returns - ------- - List[str] - List of expiration dates - """ - df = get_full_option_chain(symbol) - - if df.empty: - return [] - - # get everything that is not an empty string - return [exp for exp in list(df.expiration.unique()) if exp] - - -@log_start_end(log=logger) -def get_last_price(symbol: str) -> float: - """Get the last price from nasdaq - - Parameters - ---------- - symbol: str - Symbol to get quote for - - Returns - ------- - float - Last price - """ - for asset in ["stocks", "index", "etf"]: - url = f"https://api.nasdaq.com/api/quote/{symbol}/info?assetclass={asset}" - response_json = request( - url, - headers={ - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" - " AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15" - }, - ).json() - if response_json["status"]["rCode"] == 200: - return float( - response_json["data"]["primaryData"]["lastSalePrice"] - .strip("$") - .replace(",", "") - ) - console.print(f"[red]Last price for {symbol} not found[/red]\n") - return np.nan - - -@log_start_end(log=logger) -def get_underlying_price(symbol: str) -> pd.Series: - """Get the last price from nasdaq - - Parameters - ---------- - symbol: str - Symbol to get quote for - - Returns - ------- - float - Last price - """ - df = pd.Series(dtype=object) - for asset in ["stocks", "index", "etf"]: - url = f"https://api.nasdaq.com/api/quote/{symbol}/info?assetclass={asset}" - response_json = request( - url, - headers={ - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" - " AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15" - }, - ).json() - if response_json["status"]["rCode"] == 200: - data = response_json["data"] - data = pd.Series(data) - df["companyName"] = data["companyName"] - df["price"] = float( - data["primaryData"]["lastSalePrice"].strip("$").replace(",", "") - ) - df["change"] = float(data["primaryData"]["netChange"]) - df["changePercent"] = float( - data["primaryData"]["percentageChange"].replace("%", "") - ) - df["volume"] = data["primaryData"]["volume"].replace(",", "") - df["date"] = pd.to_datetime( - data["primaryData"]["lastTradeTimestamp"] - .replace("ET - PRE-MARKET", "") - .replace(" - AFTER HOURS", "") - .replace("ET", ""), - yearfirst=True, - ).strftime("%Y-%m-%d") - return df.rename(symbol) - - console.print(f"[red]Last price for {symbol} not found[/red]\n") - return pd.Series() - - -# Ugh this doesn't get the full chain -# TODO: apply CRR binomial tree to backtrack IV for greeks -@log_start_end(log=logger) -def get_option_greeks(symbol: str, expiration: str) -> pd.DataFrame: - """Get option greeks from nasdaq - - Parameters - ---------- - symbol: str - Symbol to get - expiration: str - Option expiration - - Returns - ------- - pd.DataFrame - Dataframe with option greeks - """ - for asset in ["stocks", "index", "etf"]: - url_greeks = f"https://api.nasdaq.com/api/quote/{symbol}/option-chain/greeks?assetclass={asset}&date={expiration}" - response_json = request( - url_greeks, - headers={ - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" - " AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15" - }, - ).json() - if response_json["status"]["rCode"] == 200: - greeks = pd.DataFrame(response_json["data"]["table"]["rows"]) - greeks = greeks.drop(columns="url") - return greeks - - console.print(f"[red]Greeks not found for {symbol} on {expiration}[/red].") - return pd.DataFrame() - - -def get_available_greeks(OptionsChains, expiration: str = "") -> pd.DataFrame: - """Get available greeks for a specific expiration. - This function will return data for strike prices near the money only. - - Parameters - ---------- - expiration: str - The expiration date to return the data. Default is the first available date. (YYYY-MM-DD) - - Returns - ------- - pd.DataFrame - Dataframe with option greeks and strike prices. - - Examples - -------- - Near-the-Money Greeks for the closest expiration date. - - >>> greeks = self.get_available_greeks() - - Get the last expiration date. - - >>> greeks = self.get_available_greeks(self.expirations[-1]) - """ - - if expiration == "": - expiration = OptionsChains.expirations[0] - - if expiration not in OptionsChains.expirations: - console.print( - f"{expiration}", - " is not a valid expiration. Choose from, ", - OptionsChains.expirations, - sep="", - ) - return pd.DataFrame() - - greeks = get_option_greeks(OptionsChains.symbol, expiration) - - return greeks - - -@log_start_end(log=logger) -def load_options(symbol: str, pydantic: bool = False) -> Options: - """OptionsChains data object for Nasdaq. - - Parameters - ---------- - symbol: str - The ticker symbol to load. - pydantic: bool - Whether to return the object as a Pydantic Model or a subscriptable Pandas Object. Default is False. - - Returns - ------- - object: Options - chains: dict - The complete options chain for the ticker. Returns as a Pandas DataFrame if pydantic is False. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: dict - The price and recent performance of the underlying asset. Returns as a Pandas Series if pydantic is False. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "Nasdaq". - SYMBOLS: dict - The Nasdaq symbol directory. Returns as a Pandas DataFrame if pydantic is False. - - Examples - -------- - Get current options chains for AAPL. - >>> from openbb_terminal.stocks.options.nasdaq_model import load_options - >>> data = load_options("AAPL") - >>> chains = data.chains - - Return the object as a Pydantic Model. - >>> from openbb_terminal.stocks.options.nasdaq_model import load_options - >>> data = load_options("AAPL", pydantic=True) - """ - - symbol = symbol.upper() - OptionsChains = Options() - OptionsChains.source = "Nasdaq" - OptionsChains.SYMBOLS = pandas_datareader.nasdaq_trader.get_nasdaq_symbols() - OptionsChains.symbol = symbol - if ( - OptionsChains.symbol not in OptionsChains.SYMBOLS.index - and OptionsChains.symbol != "NDX" - ): - console.print(OptionsChains.symbol, "was not found in the Nasdaq directory") - return OptionsChains - - OptionsChains.symbol = symbol - - try: - OptionsChains.chains = get_full_option_chain(OptionsChains.symbol) - now = datetime.now() - temp = pd.DatetimeIndex(OptionsChains.chains.expiration) - temp_ = (temp - now).days + 1 - OptionsChains.chains["dte"] = temp_ - - except Exception: - return OptionsChains - - if not OptionsChains.chains.empty: - OptionsChains.expirations = option_expirations(OptionsChains.symbol) - OptionsChains.strikes = ( - pd.Series(OptionsChains.chains["strike"]).sort_values().unique().tolist() - ) - OptionsChains.underlying_price = get_underlying_price(OptionsChains.symbol) - OptionsChains.last_price = OptionsChains.underlying_price["price"] - OptionsChains.underlying_name = OptionsChains.underlying_price["companyName"] - - OptionsChains.hasIV = "impliedVolatility" in OptionsChains.chains.columns - OptionsChains.hasGreeks = "gamma" in OptionsChains.chains.columns - - def get_greeks(expiration: str = ""): - """Get available greeks for a specific expiration. - This function will return data for strike prices near the money only. - - Parameters - ---------- - expiration: str - The expiration date to return the data. Default is the first available date. (YYYY-MM-DD) - - Returns - ------- - pd.DataFrame - Dataframe with option greeks and strike prices. - - Examples - -------- - Near-the-Money Greeks for the closest expiration date. - - >>> greeks = self.get_available_greeks() - - Get the last expiration date. - - >>> greeks = self.get_available_greeks(self.expirations[-1]) - """ - - if expiration == "": - expiration = OptionsChains.expirations[0] - if expiration not in OptionsChains.expirations: - console.print( - f"{expiration}", - " is not a valid expiration. Choose from, ", - OptionsChains.expirations, - sep="", - ) - return pd.DataFrame() - - return get_option_greeks(OptionsChains.symbol, expiration) - - if not OptionsChains.chains.empty: - if OptionsChains.last_price is None: - OptionsChains.last_price = 0 - console.print("No last price for " + OptionsChains.symbol) - - if not pydantic: - setattr(OptionsChains, "get_available_greeks", get_greeks) - return OptionsChains - - OptionsChainsPydantic = PydanticOptions( - chains=OptionsChains.chains.to_dict(), - expirations=OptionsChains.expirations, - strikes=OptionsChains.strikes, - last_price=OptionsChains.last_price, - underlying_name=OptionsChains.underlying_name, - underlying_price=OptionsChains.underlying_price.to_dict(), - hasIV=OptionsChains.hasIV, - hasGreeks=OptionsChains.hasGreeks, - symbol=OptionsChains.symbol, - source=OptionsChains.source, - SYMBOLS=OptionsChains.SYMBOLS.to_dict(), - ) - setattr(OptionsChainsPydantic, "get_available_greeks", get_greeks) - return OptionsChainsPydantic - - return Options() diff --git a/openbb_terminal/stocks/options/op_helpers.py b/openbb_terminal/stocks/options/op_helpers.py deleted file mode 100644 index 02e50367c11..00000000000 --- a/openbb_terminal/stocks/options/op_helpers.py +++ /dev/null @@ -1,706 +0,0 @@ -"""Option helper functions""" - -__docformat__ = "numpy" - -import logging -from datetime import datetime, timedelta -from math import e, log -from typing import Optional, Tuple, Union - -import numpy as np -import pandas as pd -from pydantic import BaseModel, Extra, Field -from scipy.stats import norm - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_rf -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -# pylint: disable=too-many-arguments - - -def get_strikes( - min_sp: float, max_sp: float, chain: pd.DataFrame -) -> Tuple[float, float]: - """Function to get the min and max strikes for a given expiry""" - - min_strike = chain["strike"].min() if min_sp == -1 else min_sp - max_strike = chain["strike"].max() if max_sp == -1 else max_sp - - return min_strike, max_strike - - -def get_loss_at_strike(strike: float, chain: pd.DataFrame) -> float: - """Function to get the loss at the given expiry - - Parameters - ---------- - strike : Union[int,float] - Value to calculate total loss at - chain : Dataframe: - Dataframe containing at least strike and openInterest - - Returns - ------- - loss : Union[float,int] - Total loss - """ - - itm_calls = chain[chain.index < strike][["OI_call"]] - itm_calls["loss"] = (strike - itm_calls.index) * itm_calls["OI_call"] - call_loss = itm_calls["loss"].sum() - - itm_puts = chain[chain.index > strike][["OI_put"]] - itm_puts["loss"] = (itm_puts.index - strike) * itm_puts["OI_put"] - put_loss = itm_puts.loss.sum() - loss = call_loss + put_loss - - return loss - - -def calculate_max_pain(chain: pd.DataFrame) -> Union[int, float]: - """Returns the max pain for a given call/put dataframe - - Parameters - ---------- - chain : DataFrame - Dataframe to calculate value from - - Returns - ------- - max_pain : int - Max pain value - """ - - strikes = np.array(chain.index) - if ("OI_call" not in chain.columns) or ("OI_put" not in chain.columns): - console.print("Incorrect columns. Unable to parse max pain") - return np.nan - - loss = [get_loss_at_strike(price_at_exp, chain) for price_at_exp in strikes] - chain["loss"] = loss - max_pain = chain["loss"].idxmin() - - return max_pain - - -def convert(orig: str, to: str) -> float: - """Convert a string to a specific type of number - Parameters - ---------- - orig : str - String to convert - Returns - ------- - number : float - Decimal value of string - """ - clean = orig.replace("%", "").replace("+", "").replace(",", "") - if to == "%": - return float(clean) / 100 - if to == ",": - return float(clean) - raise ValueError("Invalid to format, please use '%' or ','.") - - -def rn_payoff(x: str, df: pd.DataFrame, put: bool, delta: int, rf: float) -> float: - """The risk neutral payoff for a stock - Parameters - ---------- - x : str - Strike price - df : pd.DataFrame - Dataframe of stocks prices and probabilities - put : bool - Whether the asset is a put or a call - delta : int - Difference between today's date and expirations date in days - rf : float - The current risk-free rate - - Returns - ------- - number : float - Risk neutral value of option - """ - if put: - df["Gain"] = np.where(x > df["Price"], x - df["Price"], 0) - else: - df["Gain"] = np.where(x < df["Price"], df["Price"] - x, 0) - df["Vals"] = df["Chance"] * df["Gain"] - risk_free = (1 + rf) ** (delta / 365) - return sum(df["Vals"]) / risk_free - - -@log_start_end(log=logger) -def process_option_chain(data: pd.DataFrame, source: str) -> pd.DataFrame: - """ - Create an option chain DataFrame from the given symbol. - Does additional processing in order to get some homogeneous between the sources. - - Parameters - ---------- - data : pd.DataFrame - The option chain data - source: str, optional - The source of the data. Valid values are "Tradier", "Nasdaq", and - "YahooFinance". The default value is "Tradier". - - Returns - ------- - pd.DataFrame - A DataFrame containing the option chain data, with columns as specified - in the `option_chain_column_mapping` mapping, and an additional column - "optionType" that indicates whether the option is a call or a put. - """ - if source == "Tradier": - df = data.rename(columns=option_chain_column_mapping["Tradier"]) - - elif source == "Nasdaq": - call_columns = ["expiration", "strike"] + [ - col for col in data.columns if col.startswith("c_") - ] - calls = data[call_columns].rename(columns=option_chain_column_mapping["Nasdaq"]) - calls["optionType"] = "call" - - put_columns = ["expiration", "strike"] + [ - col for col in data.columns if col.startswith("p_") - ] - puts = data[put_columns].rename(columns=option_chain_column_mapping["Nasdaq"]) - puts["optionType"] = "put" - - df = pd.concat([calls, puts]).drop_duplicates() - - elif source == "Intrinio": - df = data.copy() - - elif source == "YahooFinance": - call_columns = ["expiration", "strike"] + [ - col for col in data.columns if col.endswith("_c") - ] - calls = data[call_columns].rename( - columns=option_chain_column_mapping["YahooFinance"] - ) - calls["optionType"] = "call" - - put_columns = ["expiration", "strike"] + [ - col for col in data.columns if col.endswith("_p") - ] - puts = data[put_columns].rename( - columns=option_chain_column_mapping["YahooFinance"] - ) - puts["optionType"] = "put" - - df = pd.concat([calls, puts]).drop_duplicates() - - else: - df = pd.DataFrame() - - return df - - -@log_start_end(log=logger) -def get_greeks( - current_price: float, - calls: pd.DataFrame, - puts: pd.DataFrame, - expire: str, - div_cont: float = 0, - rf: Optional[float] = None, - opt_type: int = 0, - show_all: bool = False, - show_extra_greeks: bool = False, -) -> pd.DataFrame: - """ - Gets the greeks for a given option - - Parameters - ---------- - current_price: float - The current price of the underlying - div_cont: float - The dividend continuous rate - expire: str - The date of expiration - rf: float - The risk-free rate - opt_type: Union[-1, 0, 1] - The option type 1 is for call and -1 is for put - mini: float - The minimum strike price to include in the table - maxi: float - The maximum strike price to include in the table - show_all: bool - Whether to show all columns from puts and calls - show_extra_greeks: bool - Whether to show all greeks - """ - - chain = pd.DataFrame() - - if opt_type not in [-1, 0, 1]: - console.print("[red]Invalid option type[/red]") - elif opt_type == 1: - chain = calls - elif opt_type == -1: - chain = puts - else: - chain = pd.concat([calls, puts]) - - chain_columns = chain.columns.tolist() - if not all( - col in chain_columns for col in ["strike", "impliedVolatility", "optionType"] - ): - if "delta" not in chain_columns: - console.print( - "[red]It's not possible to calculate the greeks without the following " - "columns: `strike`, `impliedVolatility`, `optionType`.\n[/red]" - ) - return pd.DataFrame() - - risk_free = rf if rf is not None else get_rf() - expire_dt = datetime.strptime(expire, "%Y-%m-%d") - dif = (expire_dt - datetime.now() + timedelta(hours=16)).total_seconds() / ( - 60 * 60 * 24 - ) - strikes = [] - for _, row in chain.iterrows(): - vol = row["impliedVolatility"] - is_call = row["optionType"] == "call" - result = ( - [row[col] for col in row.index.tolist()] - if show_all - else [row[col] for col in ["strike", "impliedVolatility"]] - ) - try: - opt = Option( - current_price, row["strike"], risk_free, div_cont, dif, vol, is_call - ) - tmp = [ - opt.Delta(), - opt.Gamma(), - opt.Vega(), - opt.Theta(), - ] - result += tmp - - if show_extra_greeks: - result += [ - opt.Rho(), - opt.Phi(), - opt.Charm(), - opt.Vanna(0.01), - opt.Vomma(0.01), - ] - except ValueError: - result += [np.nan] * 4 - - if show_extra_greeks: - result += [np.nan] * 5 - strikes.append(result) - - greek_columns = [ - "Delta", - "Gamma", - "Vega", - "Theta", - ] - columns = ( - chain_columns + greek_columns - if show_all - else ["Strike", "Implied Vol"] + greek_columns - ) - - if show_extra_greeks: - additional_columns = ["Rho", "Phi", "Charm", "Vanna", "Vomma"] - columns += additional_columns - - df = pd.DataFrame(strikes, columns=columns) - - return df - - -opt_chain_cols = { - "lastTradeDate": {"format": "date", "label": "Last Trade Date"}, - "strike": {"format": "${x:.2f}", "label": "Strike"}, - "lastPrice": {"format": "${x:.2f}", "label": "Last Price"}, - "bid": {"format": "${x:.2f}", "label": "Bid"}, - "ask": {"format": "${x:.2f}", "label": "Ask"}, - "change": {"format": "${x:.2f}", "label": "Change"}, - "percentChange": {"format": "{x:.2f}%", "label": "Percent Change"}, - "volume": {"format": "{x:.2f}", "label": "Volume"}, - "openInterest": {"format": "", "label": "Open Interest"}, - "impliedVolatility": {"format": "{x:.2f}", "label": "Implied Volatility"}, -} - -option_chain_column_mapping = { - "Nasdaq": { - "strike": "strike", - "c_Last": "last", - "c_Change": "change", - "c_Bid": "bid", - "c_Ask": "ask", - "c_Volume": "volume", - "c_Openinterest": "openInterest", - "p_Last": "last", - "p_Change": "change", - "p_Bid": "bid", - "p_Ask": "ask", - "p_Volume": "volume", - "p_Openinterest": "openInterest", - }, - "Tradier": { - "open_interest": "openInterest", - "option_type": "optionType", - }, - "YahooFinance": { - "contractSymbol_c": "contractSymbol", - "lastTradeDate_c": "lastTradeDate", - "strike": "strike", - "lastPrice_c": "lastPrice", - "bid_c": "bid", - "ask_c": "ask", - "change_c": "change", - "percentChange_c": "percentChange", - "volume_c": "volume", - "openInterest_c": "openInterest", - "impliedVolatility_c": "impliedVolatility", - "inTheMoney_c": "inTheMoney", - "contractSize_c": "contractSize", - "currency_c": "currency", - "contractSymbol_p": "contractSymbol", - "lastTradeDate_p": "lastTradeDate", - "lastPrice_p": "lastPrice", - "bid_p": "bid", - "ask_p": "ask", - "change_p": "change", - "percentChange_p": "percentChange", - "volume_p": "volume", - "openInterest_p": "openInterest", - "impliedVolatility_p": "impliedVolatility", - "inTheMoney_p": "inTheMoney", - "contractSize_p": "contractSize", - "currency_p": "currency", - "expiration": "expiration", - }, -} - - -class Option: - def __init__( - self, - s: float, - k: float, - rf: float, - div_cont: float, - expiry: float, - vol: float, - is_call: bool = True, - ): - """ - Class for getting the greeks of options. Inspiration from: - http://www.smileofthales.com/computation/option-pricing-python-inheritance/ - - Parameters - ---------- - s : float - The underlying asset price - k : float - The option strike price - rf : float - The risk-free rate - div_cont : float - The dividend continuous rate - expiry : float - The number of days until expiration - vol : float - The underlying volatility for an option - is_call : bool - True if call, False if put - """ - if expiry <= 0: - raise ValueError("Expiry must be greater than 0") - if vol <= 0: - raise ValueError("Volatility must be greater than 0") - if s <= 0: - raise ValueError("Price must be greater than 0") - if k <= 0: - raise ValueError("Strike must be greater than 0") - self.Type = 1 if is_call else -1 - self.price = float(s) - self.strike = float(k) - self.risk_free = float(rf) - self.div_cont = float(div_cont) - self.exp_time = float(expiry) / 365.0 - self._sigma = float(vol) - self.sigmaT = self._sigma * self.exp_time**0.5 - - @property - def d1(self): - return ( - log(self.price / self.strike) - + (self.risk_free - self.div_cont + 0.5 * (self.sigma**2)) * self.exp_time - ) / self.sigmaT - - @property - def d2(self): - return self.d1 - self.sigmaT - - @property - def sigma(self): - return self._sigma - - @sigma.setter - def sigma(self, val): - self._sigma = val - self.sigmaT = val * self.exp_time**0.5 - - def Premium(self): - tmpprem = self.Type * ( - self.price - * e ** (-self.div_cont * self.exp_time) - * norm.cdf(self.Type * self.d1) - - self.strike - * e ** (-self.risk_free * self.exp_time) - * norm.cdf(self.Type * self.d2) - ) - return tmpprem - - # 1st order greeks - - def Delta(self): - dfq = np.exp(-self.div_cont * self.exp_time) - if self.Type == 1: - return dfq * norm.cdf(self.d1) - return dfq * (norm.cdf(self.d1) - 1) - - def Vega(self): - """Vega for 1% change in vol""" - dfq = np.exp(-self.div_cont * self.exp_time) - return 0.01 * self.price * dfq * norm.pdf(self.d1) * self.exp_time**0.5 - - def Theta(self, time_factor=1.0 / 365.0): - """Theta, by default for 1 calendar day change""" - df = np.exp(-self.risk_free * self.exp_time) - dfq = np.exp(-self.div_cont * self.exp_time) - tmptheta = time_factor * ( - -0.5 - * self.price - * dfq - * norm.pdf(self.d1) - * self.sigma - / (self.exp_time**0.5) - + self.Type - * ( - self.div_cont * self.price * dfq * norm.cdf(self.Type * self.d1) - - self.risk_free * self.strike * df * norm.cdf(self.Type * self.d2) - ) - ) - return tmptheta - - def Rho(self): - df = np.exp(-self.risk_free * self.exp_time) - return ( - self.Type - * self.strike - * self.exp_time - * df - * 0.01 - * norm.cdf(self.Type * self.d2) - ) - - def Phi(self): - dfq = np.exp(-self.div_cont * self.exp_time) - return ( - 0.01 - * -self.Type - * self.exp_time - * self.price - * dfq - * norm.cdf(self.Type * self.d1) - ) - - # 2nd order greeks - - def Gamma(self): - dfq = np.exp(-self.div_cont * self.exp_time) - return dfq * norm.pdf(self.d1) / (self.price * self.sigmaT) - - def Charm(self, time_factor=1.0 / 365.0): - """Calculates Charm, by default for 1 calendar day change""" - dfq = np.exp(-self.div_cont * self.exp_time) - cdf = norm.cdf(self.Type * self.d1) - return ( - time_factor - * -dfq - * ( - norm.pdf(self.d1) - * ( - (self.risk_free - self.div_cont) / (self.sigmaT) - - self.d2 / (2 * self.exp_time) - ) - + (self.Type * -self.div_cont) * cdf - ) - ) - - def Vanna(self, change: float): - """ - Vanna for a given percent change in volatility - - Parameters - ---------- - change : float - The change in volatility - - Returns - ---------- - num : float - The Vanna - - """ - - return ( - change - * -(e ** (-self.div_cont * self.exp_time)) - * self.d2 - / self.sigma - * norm.pdf(self.d1) - ) - - def Vomma(self, change): - """ - Vomma for a given percent change in volatility - - Parameters - ---------- - change : float - The change in volatility - - Returns - ---------- - num : float - The Vomma - - """ - return ( - change - * np.exp(-self.div_cont * self.exp_time) - * self.d1 - * self.d2 - * np.sqrt(self.exp_time) - * self.price - * norm.pdf(self.d1) - / self._sigma - ) - - -def get_dte(chain: pd.DataFrame) -> pd.DataFrame: - """ - Returns a new column containing the DTE as an integer, including 0. - Requires the chain to have the column labeled as, expiration. - """ - if "expiration" not in chain.columns: - raise ValueError("No column labeled 'expiration' was found.") - - now = datetime.now() - temp = pd.DatetimeIndex(chain.expiration) - temp_ = (temp - now).days + 1 - chain["dte"] = temp_ - - return chain - - -class Options: # pylint: disable=too-few-public-methods,too-many-instance-attributes - """The Options data object. - - Returns - ------- - object: Options - chains: pd.DataFrame - The complete options chain for the ticker. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data. - date: str - The date, when the chains data is historical EOD. - SYMBOLS: pd.DataFrame - The symbol directory for the source, when available. - """ - - chains = pd.DataFrame - expirations: list - strikes: list - last_price: float - underlying_name: str - underlying_price: pd.Series - hasIV: bool - hasGreeks: bool - symbol: str - source: str - date: str - SYMBOLS: pd.DataFrame - - -class PydanticOptions( # type: ignore [call-arg] - BaseModel, extra=Extra.allow -): # pylint: disable=too-few-public-methods - """Pydantic model for the Options data object. - - Returns - ------- - Pydantic: Options - chains: dict - The complete options chain for the ticker. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: dict - The price and recent performance of the underlying asset. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data. - date: str - The date, when the chains data is historical EOD. - SYMBOLS: dict - The symbol directory for the source, when available. - """ - - chains: dict = Field(default=None) - expirations: list = Field(default=None) - strikes: list = Field(default=None) - last_price: float = Field(default=None) - underlying_name: str = Field(default=None) - underlying_price: dict = Field(default=None) - hasIV: bool = Field(default=False) - hasGreeks: bool = Field(default=False) - symbol: str = Field(default=None) - source: str = Field(default=None) - date: str = Field(default=None) - SYMBOLS: dict = Field(default=None) diff --git a/openbb_terminal/stocks/options/options_chains_model.py b/openbb_terminal/stocks/options/options_chains_model.py deleted file mode 100644 index 3f98054fccd..00000000000 --- a/openbb_terminal/stocks/options/options_chains_model.py +++ /dev/null @@ -1,1801 +0,0 @@ -# pylint: disable=C0302, R0911, W0612, R0912 -""" Options Chains Module """ -__docformat__ = "numpy" - -# IMPORTATION STANDARD -import logging -from copy import deepcopy - -# IMPORTATION THIRDPARTY -from typing import Any, Optional - -import numpy as np -import pandas as pd - -# IMPORTATION INTERNAL -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options.cboe_model import load_options as load_cboe -from openbb_terminal.stocks.options.intrinio_model import load_options as load_intrinio -from openbb_terminal.stocks.options.nasdaq_model import load_options as load_nasdaq -from openbb_terminal.stocks.options.op_helpers import Options -from openbb_terminal.stocks.options.tmx_model import load_options as load_tmx -from openbb_terminal.stocks.options.tradier_model import load_options as load_tradier -from openbb_terminal.stocks.options.yfinance_model import load_options as load_yfinance - -logger = logging.getLogger(__name__) - -SOURCES = ["CBOE", "YahooFinance", "Tradier", "Intrinio", "Nasdaq", "TMX"] - -# mypy: disable-error-code="attr-defined, index" - - -@log_start_end(log=logger) -def load_options_chains( - symbol: str, - source: str = "CBOE", - date: str = "", - pydantic: bool = False, -) -> Options: - """Loads all options chains from a specific source, fields returned to each attribute will vary. - - Parameters - ---------- - symbol : str - The underlying asset's symbol. - source: str - The source of the data. Choices are "CBOE", "YahooFinance", "Tradier", "Intrinio", "Nasdaq", or "TMX". - date: Optional[str] - The date for the EOD option chain. Format: YYYY-MM-DD. - This parameter is only available for "TMX" or "Intrinio". - pydantic: bool - Whether to return the object as a Pydantic Model or a subscriptable Pandas object. Default is False. - - Returns - ------- - Options: Options data object - - chains: dict - All options chains data from a specific source. Returns as a Pandas DataFrame if pydantic is False. - expirations: list[str] - List of all unique expiration dates. - hasGreeks: bool - True if the source returns greeks with the chains data. - hasIV: bool - True if the source returns implied volatility with the chains data. - last_price: float - The last price (or the price at the EOD for the date.of the EOD option chain). - source: str - The source that was entered in the input. - strikes: list[float] - List of all unique strike prices. - symbol: str - The symbol that was entered in the input. - SYMBOLS: pd.DataFrame - The symbol directory to the selected source, when available. Only returned when pydantic is False. - underlying_name: str - The name of the underlying asset. - underlying_price: dict - The underlying asset's price and performance. Returns as a Pandas Series if pydantic is False. - - Examples - -------- - Loads SPY data from CBOE, returns as a Pydantic Model, and displays the longest-dated expiration chain. - - >>> from openbb_terminal.sdk import openbb - >>> import pandas as pd - >>> data = openbb.stocks.options.load_options_chains("SPY", pydantic = True) - >>> chains = pd.DataFrame(data.chains) - >>> chains[chains["expiration"] == data.expirations[-1]] - - Loads QQQ data from Tradier as a Pydantic Model. - - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("QQQ", source = "Tradier", pydantic = True) - - Loads VIX data from YahooFinance as a Pandas object. - - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("^VIX", source = "YahooFinance") - - Loads XIU data from TMX and displays the 25 highest open interest options. - - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("XIU", "TMX") - >>> data.chains.sort_values("openInterest", ascending=False).head(25) - - Loads the EOD chains data for XIU.TO from March 15, 2020, sorted by number of transactions. - - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("XIU.TO", "TMX", "2020-03-15") - >>> data.chains.sort_values("transactions", ascending=False).head(25) - """ - - if source not in SOURCES: - console.print("Invalid choice. Choose from: ", list(SOURCES), sep=None) - return Options() - - if source == "Nasdaq": - return load_nasdaq(symbol, pydantic) - if source == "YahooFinance": - return load_yfinance(symbol, pydantic) - if source == "Tradier": - return load_tradier(symbol, pydantic) - if source == "TMX": - if date != "": - return load_tmx(symbol, date, pydantic) - return load_tmx(symbol, pydantic=pydantic) - if source == "Intrinio": - if date != "": - return load_intrinio(symbol, date, pydantic) - return load_intrinio(symbol, pydantic=pydantic) - - return load_cboe(symbol, pydantic) - - -def validate_object( # noqa: PLR0911 - options: Options, scope: Optional[str] = "object", days: Optional[int] = None -) -> Any: - """This is an internal helper function for validating the Options data object passed - through the input of functions defined in the OptionsChains class. The purpose is to handle - multi-type inputs with backwards compatibility and provide robust error handling. The return - is the portion of the object, or a true/false validation, required to perform the operation. - - Parameters - ---------- - options : object - The Options data object. - Accepts both Pydantic and Pandas object types, as defined by `load_options_chains()`. - A Pandas DataFrame, or dictionary, with the options chains data is also accepted. - scope: str - The scope of the data needing to be validated. Choices are: ["chains", "object", "strategies", "nonZeroPrices"] - days: int - The number of target number of days until the expiration. - - Returns - ------- - Any: - if scope == "chains": - pd.DataFrame - Pandas DataFrame with the validated data. - if scope == "object" or scope == "strategies": - bool - True if the object is a valid Options data object. - - Examples - -------- - Load some data first: - >>> from openbb_terminal.stocks.options import options_chains_model - >>> data = options_chains_model.load_options_chains("SPY") - To extract just the chains data, use: - >>> chains = options_chains_model.validate_object(data, scope="chains") - To pass as a true/false validation, use: - >>> if options_chains_model.validate_object(data, scope="object") is False: - >>> return - To pass and return the entire object for non-zero prices: - >>> if options_chains_model.validate_object(data, scope="object") is True: - >>> data = options_chains_model.validate_object(data, scope="nonZeroPrices") - """ - - scopes = ["chains", "object", "strategies", "nonZeroPrices"] - - scope = scope or "chains" - - if scope not in scopes: - console.print("Invalid choice. The supported methods are:", scopes) - return pd.DataFrame() - - if scope == "object": - try: - if ( - isinstance(options.strikes, list) - and isinstance(options.expirations, list) - and hasattr(options, "last_price") - ): - return True - - except AttributeError: - console.print( - "Error: Invalid data type supplied. The Options data object is required. " - "Use load_options_chains() first." - ) - return False - - if scope == "strategies": - try: - if isinstance(options.last_price, float): - return True - except AttributeError: - console.print( - "`last_price` was not found in the OptionsChainsData object and is required for this operation." - ) - return False - - if scope == "chains": - try: - if isinstance(options, pd.DataFrame): - chains = options.copy() - - if isinstance(options, dict): - chains = pd.DataFrame(options) - - elif isinstance(options, object) and not isinstance(options, pd.DataFrame): - chains = ( - pd.DataFrame(options.chains) # type: ignore[attr-defined] - if isinstance(options.chains, dict) # type: ignore[attr-defined] - else options.chains.copy() # type: ignore[attr-defined] - ) - - if options is None or chains.empty: - console.print( - "No options chains data found in the supplied object. Use load_options_chains()." - ) - return pd.DataFrame() - - if "openInterest" not in chains.columns: - console.print("Expected column, openInterest, not found.") - return pd.DataFrame() - - if "volume" not in chains.columns: - console.print("Expected column, volume, not found.") - return pd.DataFrame() - except AttributeError: - console.print("Error: Invalid data type supplied.") - return pd.DataFrame() - - return chains - - if scope == "nonZeroPrices": - dte_estimate = get_nearest_dte( # noqa:F841 pylint: disable=unused-variable - options, days - ) - # When Intrinio data is not EOD, there is no "ask" column, renaming "close". - if ( - options.source == "Intrinio" - and options.date is None - or options.source == "Intrinio" - and options.date == "" - ): - options.chains["ask"] = options.chains["close"] - options.chains["bid"] = options.chains["close"] - - # Error handling for TMX EOD data when the date is an expiration date. EOD, 0-day options are excluded. - if options.source == "TMX" and options.date != "": - options.chains = options.chains[options.chains["dte"] > 0] - - if ( - options.source == "TMX" - and options.date is None - or options.source == "TMX" - and options.date == "" - ): - options.chains["ask"] = options.chains["lastPrice"] - options.chains["bid"] = options.chains["lastPrice"] - - if ( - options.source == "TMX" - or options.source == "YahooFinance" - or options.source == "Nasdaq" - and options.chains.query("`dte` == @dte_estimate")["ask"].sum() == 0 - ): - options.chains["ask"] = options.chains["lastPrice"] - if ( - options.source == "TMX" - or options.source == "YahooFinance" - or options.source == "Nasdaq" - and options.chains.query("`dte` == @dte_estimate")["bid"].sum() == 0 - ): - options.chains["bid"] = options.chains["lastPrice"] - - return options - - console.print( - "Error: No valid data supplied. Check the input to ensure it is not empty or None." - ) - - return False - - -def get_nearest_expiration(options: Options, expiration: Optional[str] = "") -> str: - """Gets the closest expiration date to the target date.""" - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - _expirations = pd.Series(pd.to_datetime(options.expirations)) - _expiration = pd.to_datetime(expiration) - _nearest = pd.DataFrame(_expirations - _expiration) - _nearest_exp = abs(_nearest[0].astype("int64")).idxmin() - - return options.expirations[_nearest_exp] - - -def get_nearest_dte(options: Options, days: Optional[int] = 30) -> int: - """Gets the closest expiration date to the target number of days until expiry. - - Parameters - ---------- - options : object - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. Default is 30 days. - - Returns - ------- - int - The closest expiration date to the target number of days until expiry, expressed as DTE. - - Example - ------- - >>> from openbb_terminal.stocks.options import options_chains_model - >>> data = options_chains_model.load_options_chains("QQQ") - >>> options_chains_model.get_nearest_dte(data, 42) - >>> options_chains_model.get_nearest_dte(data, 90) - """ - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - days = -1 if days == 0 else days - - nearest = (options.chains["dte"] - days).abs().idxmin() - - return options.chains.loc[nearest]["dte"] - - -def get_nearest_call_strike( - options: Options, days: Optional[int] = 30, strike_price: Optional[float] = 0 -) -> float: - """Gets the closest call strike to the target price and number of days until expiry. - - Parameters - ---------- - options : object - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. Default is 30 days. - strike_price: float - The target strike price. Default is the last price of the underlying stock. - - Returns - ------- - float - The closest strike price to the target price and number of days until expiry. - - Example - ------- - >>> from openbb_terminal.stocks.options import options_chains_model - >>> data = options_chains_model.load_options_chains("SPY") - >>> options_chains_model.get_nearest_call_strike(data) - >>> options_chains_model.get_nearest_call_strike(data, 180, 427) - >>> days = data.chains.dte.unique().tolist() - >>> for day in days: - >>> print(get_nearest_call_strike(data, day)) - """ - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - if strike_price == 0: - strike_price = options.last_price - - dte_estimate = get_nearest_dte(options, days) - - if ( - len( - options.chains[options.chains["dte"] == dte_estimate].query( - "`optionType` == 'call'" - ) - ) - == 0 - ): - return pd.DataFrame() - - nearest = ( - ( - options.chains[options.chains["dte"] == dte_estimate] - .query("`optionType` == 'call'") - .convert_dtypes()["strike"] - - strike_price - ) - .abs() - .idxmin() - ) - - return options.chains.loc[nearest]["strike"] - - -def get_nearest_put_strike( - options: Options, days: Optional[int] = 30, strike_price: Optional[float] = 0 -) -> float: - """Gets the closest put strike to the target price and number of days until expiry. - - Parameters - ---------- - options : object - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. Default is 30 days. - strike_price: float - The target strike price. Default is the last price of the underlying stock. - - Returns - ------- - float - The closest strike price to the target price and number of days until expiry. - - Example - ------- - >>> from openbb_terminal.stocks.options import options_chains_model - >>> data = options_chains_model.load_options_chains('SPY') - >>> options_chains_model.get_nearest_put_strike(data, 90, 402) - >>> options_chains_model.get_nearest_put_strike(data, 90) - >>> days = data.chains.dte.unique().tolist() - >>> for day in days: - >>> print(get_nearest_put_strike(data, day)) - """ - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - if strike_price == 0: - strike_price = options.last_price - - dte_estimate = get_nearest_dte(options, days) - - if ( - len( - options.chains[options.chains["dte"] == dte_estimate].query( - "`optionType` == 'put'" - ) - ) - == 0 - ): - return pd.DataFrame() - - nearest = ( - ( - options.chains[options.chains["dte"] == dte_estimate] - .query("`optionType` == 'put'") - .convert_dtypes()["strike"] - - strike_price - ) - .abs() - .idxmin() - ) - - return options.chains.loc[nearest]["strike"] - - -def get_nearest_otm_strike( - options: Options, moneyness: Optional[float] = 5 -) -> dict[str, float]: - """Gets the nearest put and call strikes at a given percent OTM from the underlying price. - - Parameters - ---------- - options : OptionsChains - The Options data object. Use load_options_chains() to load the data. - moneyness: float - The target percent OTM, expressed as a percent between 0 and 100. Default is 5. - - Returns - ------- - dict[str, float] - Dictionary of the upper (call) and lower (put) strike prices. - - Example - ------- - >>> from openbb_terminal.stocks.options import options_chains_model - >>> data = options_chains_model.load_options_chains('SPY') - >>> strikes = options_chains_model.get_nearest_otm_strike(data, 5) - """ - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - if not moneyness: - moneyness = 5 - - if 0 < moneyness < 100: - moneyness = moneyness / 100 - - if moneyness > 100 or moneyness < 0: - console.print( - "Error: Moneyness must be expressed as a percentage between 0 and 100" - ) - return {} - - upper = options.last_price * (1 + moneyness) - lower = options.last_price * (1 - moneyness) - strikes = pd.Series(options.strikes) - nearest_call = (upper - strikes).abs().idxmin() - call = strikes[nearest_call] - nearest_putt = (lower - strikes).abs().idxmin() - put = strikes[nearest_putt] - - otmStrikes = {"call": call, "put": put} - - return otmStrikes - - -def calculate_straddle( - options: Options, - days: Optional[int] = 30, - strike: float = 0, -) -> pd.DataFrame: - """Calculates the cost of a straddle and its payoff profile. Use a negative strike price for short options. - Requires the Options data object. - - Parameters - ---------- - options : object - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. Default is 30 days. - strike: float - The target strike price. Enter a negative value for short options. - Default is the last price of the underlying stock. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike1 is the nearest call strike, strike2 is the nearest put strike. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains('SPY') - >>> openbb.stocks.options.calculate_straddle(data) - """ - options = deepcopy(options) - if not days: - days = 30 - if days and days == 0: - days = -1 - - short: bool = False - if strike is not None and strike < 0: - short = True - strike_price = abs(strike) - - bidAsk = "bid" if short else "ask" # noqa:F841 - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - dte_estimate = get_nearest_dte(options, days) # noqa:F841 - - options = validate_object(options, "nonZeroPrices", dte_estimate) - - if not strike_price: - strike_price = options.last_price - - call_strike_estimate = get_nearest_call_strike( - options, dte_estimate, strike_price - ) # noqa:F841 - put_strike_estimate = get_nearest_put_strike( - options, dte_estimate, strike_price - ) # noqa:F841 - - call_premium = options.chains.query( - "`strike` == @call_strike_estimate and `dte` == @dte_estimate and `optionType` == 'call'" - )[bidAsk].values - - put_premium = options.chains.query( - "`strike` == @put_strike_estimate and `dte` == @dte_estimate and `optionType` == 'put'" - )[bidAsk].values - - straddle_cost = call_premium + put_premium - - straddle: dict[str, Any] = {} - - # Include the as-of date if the data is historical EOD. - if ( - options.source == "Intrinio" - and options.date != "" - or options.source == "TMX" - and options.date != "" - ): - straddle.update({"Date": options.date}) - - straddle.update( - { - "Symbol": options.symbol, - "Underlying Price": options.last_price, - "Expiration": options.chains.query("`dte` == @dte_estimate")[ - "expiration" - ].unique()[0], - "DTE": dte_estimate, - "Strike 1": call_strike_estimate, - "Strike 2": put_strike_estimate, - "Strike 1 Premium": call_premium[0], - "Strike 2 Premium": put_premium[0], - "Cost": straddle_cost[0] * -1 if short else straddle_cost[0], - "Cost Percent": round( - straddle_cost[0] / options.last_price * 100, ndigits=4 - ), - "Breakeven Upper": call_strike_estimate + straddle_cost[0], - "Breakeven Upper Percent": round( - (call_strike_estimate + straddle_cost[0]) / options.last_price * 100, - ndigits=4, - ) - - 100, - "Breakeven Lower": put_strike_estimate - straddle_cost[0], - "Breakeven Lower Percent": -100 - + round( - (put_strike_estimate - straddle_cost[0]) / options.last_price * 100, - ndigits=4, - ), - "Max Profit": abs(straddle_cost[0]) if short else np.inf, - "Max Loss": np.inf if short else straddle_cost[0] * -1, - } - ) - - straddle = pd.DataFrame(data=straddle.values(), index=straddle.keys()).rename( - columns={0: "Short Straddle" if short else "Long Straddle"} - ) - - straddle.loc["Payoff Ratio"] = round( - abs(straddle.loc["Max Profit"][0] / straddle.loc["Max Loss"][0]), ndigits=4 - ) - - return straddle - - -def calculate_strangle( - options: Options, - days: Optional[int] = 30, - moneyness: Optional[float] = 5, -) -> pd.DataFrame: - """Calculates the cost of a strangle and its payoff profile. Use a negative value for moneyness for short options. - - Requires the Options data object. - - Parameters - ---------- - options : object - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. Default is 30 days. - moneyness: float - The percentage of OTM moneyness, expressed as a percent between -100 < 0 < 100. - Enter a negative number for short options. - Default is 5. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike 1 is the nearest call strike, and strike 2 is the nearest put strike. - - Examples - -------- - >>> data = openbb.stocks.options.load_options_chains("SPY") - >>> data.calculate_strangle() - """ - - options = deepcopy(options) - - if not days: - days = 30 - - if not moneyness: - moneyness = 5 - - short: bool = False - - if moneyness < 0: - short = True - moneyness = abs(moneyness) - bidAsk = "bid" if short else "ask" # noqa:F841 - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - dte_estimate = get_nearest_dte(options, days) # noqa:F841 - - options = validate_object(options, "nonZeroPrices", dte_estimate) - - if moneyness > 100 or moneyness < 0: - console.print("Error: Moneyness must be between 0 and 100.") - return pd.DataFrame() - - strikes = get_nearest_otm_strike(options, moneyness) - - call_strike_estimate = get_nearest_call_strike( - options, dte_estimate, strikes["call"] - ) # noqa:F841 - put_strike_estimate = get_nearest_put_strike( - options, dte_estimate, strikes["put"] - ) # noqa:F841 - - call_premium = options.chains.query( - "`strike` == @call_strike_estimate and `dte` == @dte_estimate and `optionType` == 'call'" - )[bidAsk].values - - put_premium = options.chains.query( - "`strike` == @put_strike_estimate and `dte` == @dte_estimate and `optionType` == 'put'" - )[bidAsk].values - - strangle_cost = call_premium + put_premium - - strangle: dict[str, Any] = {} - - # Includes the as-of date if it is historical EOD data. - if ( - options.source == "Intrinio" - and options.date != "" - or options.source == "TMX" - and options.date != "" - ): - strangle.update({"Date": options.date}) - - strangle.update( - { - "Symbol": options.symbol, - "Underlying Price": options.last_price, - "Expiration": options.chains.query("`dte` == @dte_estimate")[ - "expiration" - ].unique()[0], - "DTE": dte_estimate, - "Strike 1": call_strike_estimate, - "Strike 2": put_strike_estimate, - "Strike 1 Premium": call_premium[0], - "Strike 2 Premium": put_premium[0], - "Cost": strangle_cost[0] * -1 if short else strangle_cost[0], - "Cost Percent": round( - strangle_cost[0] / options.last_price * 100, ndigits=4 - ), - "Breakeven Upper": call_strike_estimate + strangle_cost[0], - "Breakeven Upper Percent": round( - (call_strike_estimate + strangle_cost[0]) / options.last_price * 100, - ndigits=4, - ) - - 100, - "Breakeven Lower": put_strike_estimate - strangle_cost[0], - "Breakeven Lower Percent": -100 - + round( - (put_strike_estimate - strangle_cost[0]) / options.last_price * 100, - ndigits=4, - ), - "Max Profit": abs(strangle_cost[0]) if short else np.inf, - "Max Loss": np.inf if short else strangle_cost[0] * -1, - } - ) - - strangle = pd.DataFrame(data=strangle.values(), index=strangle.keys()).rename( - columns={0: "Short Strangle" if short else "Long Strangle"} - ) - - strangle.loc["Payoff Ratio"] = round( - abs(strangle.loc["Max Profit"][0] / strangle.loc["Max Loss"][0]), ndigits=4 - ) - - return strangle - - -def calculate_vertical_call_spread( - options: Options, - days: Optional[int] = 30, - sold_strike: Optional[float] = 0, - bought_strike: Optional[float] = 0, -) -> pd.DataFrame: - """Calculates the vertical call spread for the target DTE. - A bull call spread is when the sold strike is above the bought strike. - - Parameters - ---------- - options : object - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. This value will be used to get the nearest valid DTE. - Default is 30 days. - sold_strike: float - The target strike price for the short leg of the vertical call spread. - Default is the 5% OTM above the last price of the underlying. - bought_strike: float - The target strike price for the long leg of the vertical call spread. Default is the last price of the underlying. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike 1 is the sold strike, and strike 2 is the bought strike. - - Examples - -------- - Load the data: - >>> from openbb_terminal.stocks.options.options_chains_model import OptionsChains() - >>> data = op.load_options_chains("QQQ") - - For a bull call spread: - >>> data.calculate_vertical_call_spread(days=10, sold_strike=355, bought_strike=350) - - For a bear call spread: - >>> data.calculate_vertical_call_spread(days=10, sold_strike=350, bought_strike=355) - """ - - options = deepcopy(options) - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - if not days: - days = 30 - - if not bought_strike: - bought_strike = options.last_price * 1.0250 - - if not sold_strike: - sold_strike = options.last_price * 1.0750 - - dte_estimate = get_nearest_dte(options, days) # noqa:F841 - - options = validate_object(options, "nonZeroPrices", dte_estimate) - - sold = get_nearest_call_strike(options, days, sold_strike) - bought = get_nearest_call_strike(options, days, bought_strike) - - sold_premium = options.chains.query( - "`strike` == @sold and `dte` == @dte_estimate and `optionType` == 'call'" - )["bid"].values * (-1) - bought_premium = options.chains.query( - "`strike` == @bought and `dte` == @dte_estimate and `optionType` == 'call'" - )["ask"].values - - spread_cost = bought_premium + sold_premium - breakeven_price = bought + spread_cost[0] - max_profit = sold - bought - spread_cost[0] - call_spread_: dict[str, Any] = {} - if sold != bought and spread_cost[0] != 0: - # Includes the as-of date if it is historical EOD data. - if ( - options.source == "Intrinio" - and options.date != "" - or options.source == "TMX" - and options.date != "" - ): - call_spread_.update({"Date": options.date}) - - call_spread_.update( - { - "Symbol": options.symbol, - "Underlying Price": options.last_price, - "Expiration": options.chains.query("`dte` == @dte_estimate")[ - "expiration" - ].unique()[0], - "DTE": dte_estimate, - "Strike 1": sold, - "Strike 2": bought, - "Strike 1 Premium": sold_premium[0], - "Strike 2 Premium": bought_premium[0], - "Cost": spread_cost[0], - "Cost Percent": round( - spread_cost[0] / options.last_price * 100, ndigits=4 - ), - "Breakeven Lower": breakeven_price, - "Breakeven Lower Percent": round( - (breakeven_price / options.last_price * 100) - 100, ndigits=4 - ), - "Breakeven Upper": np.nan, - "Breakeven Upper Percent": np.nan, - "Max Profit": max_profit, - "Max Loss": spread_cost[0] * -1, - } - ) - - call_spread = pd.DataFrame( - data=call_spread_.values(), index=call_spread_.keys() - ).rename(columns={0: "Bull Call Spread"}) - if call_spread.loc["Cost"][0] < 0: - call_spread.loc["Max Profit"][0] = call_spread.loc["Cost"][0] * -1 - call_spread.loc["Max Loss"][0] = -1 * ( - bought - sold + call_spread.loc["Cost"][0] - ) - lower = bought if sold > bought else sold - call_spread.loc["Breakeven Upper"][0] = ( - lower + call_spread.loc["Max Profit"][0] - ) - call_spread.loc["Breakeven Upper Percent"][0] = round( - (breakeven_price / options.last_price * 100) - 100, ndigits=4 - ) - call_spread.loc["Breakeven Lower"][0] = np.nan - call_spread.loc["Breakeven Lower Percent"][0] = np.nan - call_spread.rename( - columns={"Bull Call Spread": "Bear Call Spread"}, inplace=True - ) - - call_spread.loc["Payoff Ratio"] = round( - abs(call_spread.loc["Max Profit"][0] / call_spread.loc["Max Loss"][0]), - ndigits=4, - ) - - return call_spread - return pd.DataFrame() - - -def calculate_vertical_put_spread( - options: Options, - days: Optional[int] = 30, - sold_strike: Optional[float] = 0, - bought_strike: Optional[float] = 0, -) -> pd.DataFrame: - """Calculates the vertical put spread for the target DTE. - A bear put spread is when the bought strike is above the sold strike. - - Parameters - ---------- - options : object - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. This value will be used to get the nearest valid DTE. - Default is 30 days. - sold_strike: float - The target strike price for the short leg of the vertical put spread. Default is the last price of the underlying. - bought_strike: float - The target strike price for the long leg of the vertical put spread. - Default is the 5% OTM above the last price of the underlying. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike 1 is the sold strike, strike 2 is the bought strike. - - Examples - -------- - Load the data: - >>> from openbb_terminal.stocks.options.options_chains_model import OptionsChains() - >>> data = OptionsChains("QQQ") - - For a bull put spread: - >>> data.get_vertical_put_spread(data, days=10, sold_strike=355, bought_strike=350) - - For a bear put spread: - >>> data.get_vertical_put_spread(data, days=10, sold_strike=355, bought_strike=350) - """ - options = deepcopy(options) - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - if not days: - days = 30 - - if not bought_strike: - bought_strike = options.last_price * 0.9750 - - if not sold_strike: - sold_strike = options.last_price * 0.9250 - - dte_estimate = get_nearest_dte(options, days) # noqa:F841 - options = validate_object(options, "nonZeroPrices", dte_estimate) - sold = get_nearest_put_strike(options, days, sold_strike) - bought = get_nearest_put_strike(options, days, bought_strike) - - sold_premium = options.chains.query( - "`strike` == @sold and `dte` == @dte_estimate and `optionType` == 'put'" - )["bid"].values * (-1) - bought_premium = options.chains.query( - "`strike` == @bought and `dte` == @dte_estimate and `optionType` == 'put'" - )["ask"].values - - spread_cost = bought_premium + sold_premium - max_profit = abs(spread_cost[0]) - breakeven_price = sold - max_profit - max_loss = (sold - bought - max_profit) * -1 - put_spread_: dict[str, Any] = {} - if sold != bought and max_loss != 0: - # Includes the as-of date if it is historical EOD data. - if ( - options.source == "Intrinio" - and options.date != "" - or options.source == "TMX" - and options.date != "" - ): - put_spread_.update({"Date": options.date}) - - put_spread_.update( - { - "Symbol": options.symbol, - "Underlying Price": options.last_price, - "Expiration": options.chains.query("`dte` == @dte_estimate")[ - "expiration" - ].unique()[0], - "DTE": dte_estimate, - "Strike 1": sold, - "Strike 2": bought, - "Strike 1 Premium": sold_premium[0], - "Strike 2 Premium": bought_premium[0], - "Cost": spread_cost[0], - "Cost Percent": round(max_profit / options.last_price * 100, ndigits=4), - "Breakeven Lower": np.nan, - "Breakeven Lower Percent": np.nan, - "Breakeven Upper": breakeven_price, - "Breakeven Upper Percent": ( - 100 - round((breakeven_price / options.last_price) * 100, ndigits=4) - ), - "Max Profit": max_profit, - "Max Loss": max_loss, - } - ) - - put_spread = pd.DataFrame( - data=put_spread_.values(), index=put_spread_.keys() - ).rename(columns={0: "Bull Put Spread"}) - if put_spread.loc["Cost"][0] > 0: - put_spread.loc["Max Profit"][0] = bought - sold - spread_cost[0] - put_spread.loc["Max Loss"][0] = spread_cost[0] * (-1) - put_spread.loc["Breakeven Lower"][0] = bought - spread_cost[0] - put_spread.loc["Breakeven Lower Percent"][0] = 100 - round( - (breakeven_price / options.last_price) * 100, ndigits=4 - ) - put_spread.loc["Breakeven Upper"][0] = np.nan - put_spread.loc["Breakeven Upper Percent"][0] = np.nan - put_spread.rename( - columns={"Bull Put Spread": "Bear Put Spread"}, inplace=True - ) - - put_spread.loc["Payoff Ratio"] = round( - abs(put_spread.loc["Max Profit"][0] / put_spread.loc["Max Loss"][0]), - ndigits=4, - ) - - return put_spread - return pd.DataFrame() - - -def calculate_synthetic_long( - options: Options, - days: Optional[int] = 30, - strike: float = 0, -) -> pd.DataFrame: - """Calculates the cost of a synthetic long position at a given strike. - It is expressed as the difference between a bought call and a sold put. - Requires the Options data object. - - Parameters - ----------- - options : Options - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. Default is 30 days. - strike: float - The target strike price. Default is the last price of the underlying stock. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike1 is the purchased call strike, strike2 is the sold put strike. - - Example - -------- - >>> from openbb_terminal.stocks.options import options_chains_model - >>> data = options_chains_model.load_options_chains('SPY') - >>> options_chains_model.calculate_synthetic_long(data) - """ - - options = deepcopy(options) - - if not days: - days = 30 - if days and days == 0: - days = -1 - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - dte_estimate = get_nearest_dte(options, days) - - options = validate_object(options, "nonZeroPrices", dte_estimate) - - strike_price = options.last_price if strike == 0 else strike - - strike_estimate = get_nearest_call_strike(options, dte_estimate, strike_price) - - call_premium = options.chains.query( - "`strike` == @strike_estimate and `dte` == @dte_estimate and `optionType` == 'call'" - )["ask"].values - - put_premium = options.chains.query( - "`strike` == @strike_estimate and `dte` == @dte_estimate and `optionType` == 'put'" - )["bid"].values * (-1) - - position_cost = call_premium[0] + put_premium[0] - breakeven = strike_estimate + position_cost - synthetic_long: dict[str, Any] = {} - - synthetic_long.update( - { - "Symbol": options.symbol, - "Underlying Price": options.last_price, - "Expiration": options.chains.query("`dte` == @dte_estimate")[ - "expiration" - ].unique()[0], - "DTE": dte_estimate, - "Strike 1": strike_estimate, - "Strike 2": strike_estimate, - "Strike 1 Premium": call_premium[0], - "Strike 2 Premium": put_premium[0], - "Cost": position_cost, - "Cost Percent": round(position_cost / options.last_price * 100, ndigits=4), - "Breakeven Lower": np.nan, - "Breakeven Lower Percent": np.nan, - "Breakeven Upper": breakeven, - "Breakeven Upper Percent": round( - ((breakeven - options.last_price) / options.last_price) * 100, ndigits=4 - ), - "Max Profit": np.inf, - "Max Loss": breakeven * (-1), - } - ) - - strategy = pd.DataFrame( - data=synthetic_long.values(), index=synthetic_long.keys() - ).rename(columns={0: "Synthetic Long"}) - - return strategy - - -def calculate_synthetic_short( - options: Options, - days: Optional[int] = 30, - strike: float = 0, -) -> pd.DataFrame: - """Calculates the cost of a synthetic short position at a given strike. - It is expressed as the difference between a sold call and a purchased put. - Requires the Options data object. - - Parameters - ----------- - options : Options - The Options data object. Use load_options_chains() to load the data. - days: int - The target number of days until expiry. Default is 30 days. - strike: float - The target strike price. Default is the last price of the underlying stock. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike1 is the sold call strike, strike2 is the purchased put strike. - - Example - -------- - >>> from openbb_terminal.stocks.options import options_chains_model - >>> data = options_chains_model.load_options_chains('SPY') - >>> options_chains_model.calculate_synthetic_short(data) - """ - - options = deepcopy(options) - - if not days: - days = 30 - if days and days == 0: - days = -1 - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the OptionsChain data object.") - return pd.DataFrame() - - if isinstance(options.chains, dict): - options.chains = pd.DataFrame(options.chains) - - dte_estimate = get_nearest_dte(options, days) - - options = validate_object(options, "nonZeroPrices", dte_estimate) - - strike_price = options.last_price if strike == 0 else strike - - strike_estimate = get_nearest_call_strike(options, dte_estimate, strike_price) - - call_premium = options.chains.query( - "`strike` == @strike_estimate and `dte` == @dte_estimate and `optionType` == 'call'" - )["bid"].values * (-1) - - put_premium = options.chains.query( - "`strike` == @strike_estimate and `dte` == @dte_estimate and `optionType` == 'put'" - )["ask"].values - - position_cost = put_premium[0] + call_premium[0] - breakeven = strike_estimate - position_cost - synthetic_short: dict[str, Any] = {} - - synthetic_short.update( - { - "Symbol": options.symbol, - "Underlying Price": options.last_price, - "Expiration": options.chains.query("`dte` == @dte_estimate")[ - "expiration" - ].unique()[0], - "DTE": dte_estimate, - "Strike 1": strike_estimate, - "Strike 2": strike_estimate, - "Strike 1 Premium": call_premium[0], - "Strike 2 Premium": put_premium[0], - "Cost": position_cost, - "Cost Percent": round(position_cost / options.last_price * 100, ndigits=4), - "Breakeven Lower": breakeven, - "Breakeven Lower Percent": round( - ((breakeven - options.last_price) / options.last_price) * 100, ndigits=4 - ), - "Breakeven Upper": np.nan, - "Breakeven Upper Percent": np.nan, - "Max Profit": breakeven, - "Max Loss": np.inf, - } - ) - - strategy = pd.DataFrame( - data=synthetic_short.values(), index=synthetic_short.keys() - ).rename(columns={0: "Synthetic Short"}) - - return strategy - - -@log_start_end(log=logger) -def calculate_stats(options: Options, by: Optional[str] = "expiration") -> pd.DataFrame: - """Calculates basic statistics for the options chains, like OI and Vol/OI ratios. - - Parameters - ---------- - options : object - The Options data object. - Accepts both Pydantic and Pandas object types, as defined by `load_options_chains()`. - A Pandas DataFrame, or dictionary, with the options chains data is also accepted. - by: str - Whether to calculate by strike or expiration. Default is expiration. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the calculated statistics. - - Examples - -------- - >>> from openbb_terminal.stocks.options.options_chains_model import OptionsChains - >>> data = OptionsChains("SPY") - - By expiration date: - >>> data.calculate_stats() - - By strike: - >>> data.calculate_stats(data, "strike") - """ - - if by not in ["expiration", "strike"]: - console.print( - "Invalid choice. The supported methods are: [expiration, strike]" - ) - return pd.DataFrame() - - chains = deepcopy(options) - last_price = chains.last_price if hasattr(chains, "last_price") else None - chains = validate_object(chains, scope="chains") - - if chains.empty or chains is None: - return chains == pd.DataFrame() - - stats = pd.DataFrame() - - stats["Puts OI"] = ( - chains[chains["optionType"] == "put"] - .groupby(f"{by}")[["openInterest"]] - .sum(numeric_only=True) - .astype("int64") - ) - stats["Calls OI"] = ( - chains[chains["optionType"] == "call"] - .groupby(f"{by}")[["openInterest"]] - .sum(numeric_only=True) - .astype("int64") - ) - stats["Total OI"] = stats["Calls OI"] + stats["Puts OI"] - stats["OI Ratio"] = round(stats["Puts OI"] / stats["Calls OI"], 2) - - if by == "expiration" and last_price: - stats["Puts OTM"] = ( - chains.query("`optionType` == 'put' & `strike` < @last_price") - .groupby("expiration")[["openInterest"]] - .sum(numeric_only=True) - ) - stats["Calls OTM"] = ( - chains.query("`optionType` == 'call' & `strike` > @last_price") - .groupby("expiration")[["openInterest"]] - .sum(numeric_only=True) - ) - stats["Puts ITM"] = ( - chains.query("`optionType` == 'put' & `strike` > @last_price") - .groupby("expiration")[["openInterest"]] - .sum(numeric_only=True) - ) - stats["Calls ITM"] = ( - chains.query("`optionType` == 'call' & `strike` < @last_price") - .groupby("expiration")[["openInterest"]] - .sum(numeric_only=True) - ) - stats["OTM Ratio"] = round(stats["Puts OTM"] / stats["Calls OTM"], 2) - stats["ITM Percent"] = round( - (stats["Puts ITM"] + stats["Calls ITM"]) / stats["Total OI"] * 100, 2 - ) - - stats["Puts Volume"] = ( - chains[chains["optionType"] == "put"] - .groupby(f"{by}")[["volume"]] - .sum(numeric_only=True) - .astype("int64") - ) - stats["Calls Volume"] = ( - chains[chains["optionType"] == "call"] - .groupby(f"{by}") - .sum(numeric_only=True)[["volume"]] - .astype("int64") - ) - stats["Total Volume"] = stats["Calls Volume"] + stats["Puts Volume"] - stats["Volume Ratio"] = round(stats["Puts Volume"] / stats["Calls Volume"], 2) - stats["Vol-OI Ratio"] = round(stats["Total Volume"] / stats["Total OI"], 2) - stats.rename_axis("Expiration", inplace=True) - if by == "strike": - stats.rename_axis(index={"Expiration": "Strike"}, inplace=True) - return stats.replace([np.nan, np.inf], None) - - -@log_start_end(log=logger) -def get_strategies( - options: Options, - days: Optional[list[int]] = None, - straddle_strike: Optional[float] = 0, - strangle_moneyness: Optional[list[float]] = None, - synthetic_longs: Optional[list[float]] = None, - synthetic_shorts: Optional[list[float]] = None, - vertical_calls: Optional[list[float]] = None, - vertical_puts: Optional[list[float]] = None, -) -> pd.DataFrame: - """Gets options strategies for all, or a list of, DTE(s). - Currently supports straddles, strangles, and vertical spreads. - Multiple strategies, expirations, and % moneyness can be returned. - A negative value for `straddle_strike` or `strangle_moneyness` returns short options. - A synthetic long/short position is a bought/sold call and sold/bought put at the same strike. - A sold call strike that is lower than the bought strike, or a sold put strike that is higher than the bought strike, - is a bearish vertical spread. - - Parameters - ---------- - options: object - The Options data object. Use `load_options_chains()` to load the data. - days: list[int] - List of DTE(s) to get strategies for. Enter a single value, or multiple as a list. Defaults to all. - strike_price: float - The target strike price. Defaults to the last price of the underlying stock. - strangle_moneyness: list[float] - List of OTM moneyness to target, expressed as a percent value between 0 and 100. - Enter a single value, or multiple as a list. Defaults to 5. - synthetic_long: list[float] - List of strikes for a synthetic long position. - synthetic_short: list[float] - List of strikes for a synthetic short position. - vertical_calls: list[float] - Call strikes for vertical spreads, listed as [sold strike, bought strike]. - vertical_puts: list[float] - Put strikes for vertical spreads, listed as [sold strike, bought strike]. - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. - - Examples - -------- - Load data - >>> from openbb_terminal.stocks.options import options_chains_model - >>> data = options_chains_model.load_options_chains("SPY") - - Return just straddles - >>> options_chains_model.calculate_strategies(data) - - Return strangles - >>> options_chains_model.calculate_strategies(data, strangle_moneyness = 10) - - Return multiple values for both moneness and days: - >>> options_chains_model.calculate_strategies(data, days = [10,30,60,90], moneyness = [2.5,-5,10,-20]) - - Return vertical spreads for all expirations. - >>> options_chains_model.calculate_strategies(data, vertical_calls=[430,427], vertical_puts=[420,426]) - - Return synthetic short and long positions: - >>> options_chains_model.calculate_strategies( - data, days = [30,60], synthetic_short = [450,445], synthetic_long = [450,455] - ) - """ - - def to_clean_list(x): - if x is None: - return None - return [x] if not isinstance(x, list) else x - - options = deepcopy(options) - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if validate_object(options, scope="strategies") is False: - console.print("`last_price` was not found in the Options data object.") - return pd.DataFrame() - - options.chains = ( - pd.DataFrame(options.chains) - if isinstance(options.chains, dict) - else options.chains - ) - - strangle_moneyness = strangle_moneyness or [0.0] - days = days or options.chains["dte"].unique().tolist() - - # Allows a single input to be passed instead of a list. - days = [days] if isinstance(days, int) else days # type: ignore[list-item] - - if not isinstance(vertical_calls, (list, float)) and vertical_calls is not None: - console.print( - "Two strike prices are required. Enter the sold price first, then the bought price." - ) - return pd.DataFrame() - if not isinstance(vertical_puts, (list, float)) and vertical_puts is not None: - console.print( - "Two strike prices are required. Enter the sold price first, then the bought price." - ) - return pd.DataFrame() - - strangle_moneyness = to_clean_list(strangle_moneyness) - synthetic_longs = to_clean_list(synthetic_longs) - synthetic_shorts = to_clean_list(synthetic_shorts) - - days_list = [] - strategies = pd.DataFrame() - straddles = pd.DataFrame() - strangles = pd.DataFrame() - strangles_ = pd.DataFrame() - synthetic_longs_df = pd.DataFrame() - _synthetic_longs = pd.DataFrame() - synthetic_shorts_df = pd.DataFrame() - _synthetic_shorts = pd.DataFrame() - call_spreads = pd.DataFrame() - put_spreads = pd.DataFrame() - - for day in days: - day = day or -1 # noqa: PLW2901 - days_list.append(get_nearest_dte(options, day)) - days = list(set(days_list)) - days.sort() - - if vertical_calls is not None: - cStrike1 = vertical_calls[0] - cStrike2 = vertical_calls[1] - for day in days: - call_spread = calculate_vertical_call_spread( - options, day, cStrike1, cStrike2 - ).transpose() - call_spreads = pd.concat([call_spreads, call_spread]) - - if vertical_puts: - pStrike1 = vertical_puts[0] - pStrike2 = vertical_puts[1] - for day in days: - put_spread = calculate_vertical_put_spread( - options, day, pStrike1, pStrike2 - ).transpose() - put_spreads = pd.concat([put_spreads, put_spread]) - - if straddle_strike: - straddle_strike = ( - options.last_price if straddle_strike == 0 else straddle_strike - ) - for day in days: - straddle = calculate_straddle(options, day, straddle_strike).transpose() - if straddle.iloc[0]["Cost"] != 0: - straddles = pd.concat([straddles, straddle]) - - if strangle_moneyness and strangle_moneyness[0] != 0: - for day in days: - for moneyness in strangle_moneyness: - strangle = calculate_strangle(options, day, moneyness).transpose() - if strangle.iloc[0]["Cost"] != 0: - strangles_ = pd.concat([strangles_, strangle]) - - strangles = pd.concat([strangles, strangles_]) - strangles = strangles.query("`Strike 1` != `Strike 2`").drop_duplicates() - - if synthetic_longs: - strikes = synthetic_longs - for day in days: - for strike in strikes: - _synthetic_long = calculate_synthetic_long( - options, day, strike - ).transpose() - if _synthetic_long.iloc[0]["Strike 1 Premium"] != 0: - _synthetic_longs = pd.concat([_synthetic_longs, _synthetic_long]) - - synthetic_longs_df = pd.concat([synthetic_longs_df, _synthetic_longs]) - - if synthetic_shorts: - strikes = synthetic_shorts - for day in days: - for strike in strikes: - _synthetic_short = calculate_synthetic_short( - options, day, strike - ).transpose() - if _synthetic_short.iloc[0]["Strike 1 Premium"] != 0: - _synthetic_shorts = pd.concat([_synthetic_shorts, _synthetic_short]) - - synthetic_shorts_df = pd.concat([synthetic_shorts_df, _synthetic_shorts]) - - strategies = pd.concat( - [ - straddles, - strangles, - synthetic_longs_df, - synthetic_shorts_df, - call_spreads, - put_spreads, - ] - ) - - if strategies.empty: - return get_strategies(options, straddle_strike=options.last_price) - - strategies = strategies.reset_index().rename(columns={"index": "Strategy"}) - strategies = ( - strategies.set_index(["Expiration", "DTE"]) - .sort_index() - .drop(columns=["Symbol"]) - ) - return strategies.reset_index() - - -@log_start_end(log=logger) -def calculate_skew( - options, expiration: Optional[str] = "", moneyness: Optional[float] = None -) -> pd.DataFrame: - """ - Returns the skewness of the options, either vertical or horizontal. - - The vertical skew for each expiry and option is calculated by subtracting the IV of the ATM call or put. - Returns only where the IV is greater than 0. - - Horizontal skew is returned if a value for moneyness is supplied. - It is expressed as the difference between skews of two equidistant OTM strikes (the closest call and put). - - Parameters - ----------- - options: object - The Options data object. Use `load_options_chains()` to load the data. - expiration: str - The expiration date to target. Defaults to all. - moneyness: float - The moneyness to target for calculating horizontal skew. This parameter overrides a defined expiration date. - - Returns - -------- - pd.DataFrame - Pandas DataFrame with the results. - - Examples - ---------- - >>> from openbb_terminal.stocks.options import options_chains_model as op - >>> data = op.load_options_chains("SPY") - - Vertical skew at a given expiry: - >>> skew = op.calculate_skew(data, "2025-12-19") - - Vertical skew at all expirations: - >>> skew = op.calculate_skew(data) - - Horizontal skew at a given % OTM: - >>> skew = op.calculate_skew(data, moneyness = 10) - """ - - options = deepcopy(options) - - if validate_object(options, scope="object") is False: - return pd.DataFrame() - - if options.hasIV is False: - console.print( - "Options data object does not have Implied Volatility and is required for this function." - ) - return pd.DataFrame() - - options.chains = validate_object(options.chains, scope="chains") - options.chains = options.chains[options.chains["impliedVolatility"] > 0] - days = options.chains["dte"].unique().tolist() - # expiration = get_nearest_expiration(options, expiration) - if moneyness is not None: - calls = pd.DataFrame() - atm_call_iv = pd.DataFrame() - puts = pd.DataFrame() - atm_put_iv = pd.DataFrame() - skew_df = pd.DataFrame() - strikes = get_nearest_otm_strike(options, moneyness) - for day in days: - atm_call_strike = get_nearest_call_strike( # noqa:F841 - options, day, options.last_price - ) - call_strike = get_nearest_call_strike( # noqa:F841 - options, day, strikes["call"] - ) - if ( - len( - options.chains[options.chains["dte"] == day].query( - "`optionType` == 'call'" - ) - ) - > 0 - ): - call_iv = options.chains[options.chains["dte"] == day].query( - "`optionType` == 'call' & `strike` == @call_strike" - )[["expiration", "strike", "impliedVolatility"]] - calls = pd.concat([calls, call_iv]) - atm_call = options.chains[options.chains["dte"] == day].query( - "`optionType` == 'call' & `strike` == @atm_call_strike" - )[["expiration", "strike", "impliedVolatility"]] - atm_call_iv = pd.concat([atm_call_iv, atm_call]) - atm_put_strike = get_nearest_put_strike( # noqa:F841 - options, day, options.last_price - ) - put_strike = get_nearest_put_strike( # noqa:F841 - options, day, strikes["put"] - ) - if ( - len( - options.chains[options.chains["dte"] == day].query( - "`optionType` == 'put'" - ) - ) - > 0 - ): - put_iv = options.chains[options.chains["dte"] == day].query( - "`optionType` == 'put' & `strike` == @put_strike" - )[["expiration", "strike", "impliedVolatility"]] - puts = pd.concat([puts, put_iv]) - atm_put = options.chains[options.chains["dte"] == day].query( - "`optionType` == 'put' & `strike` == @atm_put_strike" - )[["expiration", "strike", "impliedVolatility"]] - atm_put_iv = pd.concat([atm_put_iv, atm_put]) - - calls = calls.drop_duplicates(subset=["expiration"]).set_index("expiration") - atm_call_iv = atm_call_iv.drop_duplicates(subset=["expiration"]).set_index( - "expiration" - ) - puts = puts.drop_duplicates(subset=["expiration"]).set_index("expiration") - atm_put_iv = atm_put_iv.drop_duplicates(subset=["expiration"]).set_index( - "expiration" - ) - skew_df["Call Strike"] = calls["strike"] - skew_df["Call IV"] = calls["impliedVolatility"] - skew_df["Call ATM IV"] = atm_call_iv["impliedVolatility"] - skew_df["Call Skew"] = skew_df["Call IV"] - skew_df["Call ATM IV"] - skew_df["Put Strike"] = puts["strike"] - skew_df["Put IV"] = puts["impliedVolatility"] - skew_df["Put ATM IV"] = atm_put_iv["impliedVolatility"] - skew_df["Put Skew"] = skew_df["Put IV"] - skew_df["Put ATM IV"] - skew_df["ATM Skew"] = skew_df["Call ATM IV"] - skew_df["Put ATM IV"] - skew_df["IV Skew"] = skew_df["Call Skew"] - skew_df["Put Skew"] - - return skew_df - - calls = options.chains[options.chains["optionType"] == "call"] - puts = options.chains[options.chains["optionType"] == "put"] - call_skew = pd.DataFrame() - put_skew = pd.DataFrame() - skew_df = pd.DataFrame() - - for day in days: - atm_call_strike = get_nearest_call_strike(options, day) # noqa:F841 - if ( - len( - options.chains[options.chains["dte"] == day].query( - "`optionType` == 'call'" - ) - ) - > 0 - ): - call = calls[calls["dte"] == day][ - ["expiration", "optionType", "strike", "impliedVolatility"] - ] - call = call.set_index("expiration") - call_atm_iv = call.query("`strike` == @atm_call_strike")[ - "impliedVolatility" - ].iloc[0] - call["ATM IV"] = call_atm_iv - call["Skew"] = call["impliedVolatility"] - call["ATM IV"] - call_skew = pd.concat([call_skew, call]) - atm_put_strike = get_nearest_put_strike(options, day) # noqa:F841 - if ( - len( - options.chains[options.chains["dte"] == day].query( - "`optionType` == 'put'" - ) - ) - > 0 - ): - put = puts[puts["dte"] == day][ - ["expiration", "optionType", "strike", "impliedVolatility"] - ] - put = put.set_index("expiration") - put_atm_iv = put.query("`strike` == @atm_put_strike")[ - "impliedVolatility" - ].iloc[0] - put["ATM IV"] = put_atm_iv - put["Skew"] = put["impliedVolatility"] - put["ATM IV"] - put_skew = pd.concat([put_skew, put]) - - call_skew = call_skew.set_index(["strike", "optionType"], append=True) - put_skew = put_skew.set_index(["strike", "optionType"], append=True) - skew_df = pd.concat([call_skew, put_skew]).sort_index().reset_index() - cols = ["Expiration", "Strike", "Option Type", "IV", "ATM IV", "Skew"] - skew_df.columns = cols - if expiration != "": - if expiration not in options.expirations: - expiration = get_nearest_expiration(options, expiration) - - return skew_df.query("`Expiration` == @expiration") - - return skew_df diff --git a/openbb_terminal/stocks/options/options_chains_view.py b/openbb_terminal/stocks/options/options_chains_view.py deleted file mode 100644 index a40f7d3df97..00000000000 --- a/openbb_terminal/stocks/options/options_chains_view.py +++ /dev/null @@ -1,1014 +0,0 @@ -# mypy: disable-error-code=arg-type - -# IMPORTATION STANDARD -import logging -import os - -# IMPORTATION THIRDPARTY -from copy import deepcopy -from typing import Optional, Union - -import numpy as np -import pandas as pd -from scipy.spatial import Delaunay - -# IMPORTATION INTERNAL -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options import options_chains_model -from openbb_terminal.stocks.options.op_helpers import Options - -logger = logging.getLogger(__name__) - -# pylint: disable=C0302,R0913 - - -@log_start_end(log=logger) -def display_surface( - options: Options, - option_type: str = "otm", - dte_range: Optional[list[int]] = None, - moneyness: Optional[float] = None, - strike_range: Optional[list[float]] = None, - oi: bool = False, - volume: bool = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Chart the volatility as a 3-D surface. - - Parameters - ----------- - options: Options - The options data object. - option_type: str - The type of data to display. Default is "otm". - Choices are: ["otm", "itm", "puts", "calls"] - dte_range: list[int] - Specify a min/max range of DTE to display. - moneyness: float - Specify a % moneyness to target for display. - strike_range: list[float] - Specify a min/max range of strike prices to display. - oi: bool - Filter for only options that have open interest. Default is False. - volume: bool - Filter for only options that have trading volume. Default is False. - raw: bool - Display the raw data instead of the chart. - export: str - Export dataframe data to csv,json,xlsx file. - external_axes: bool - Return the OpenBB Figure Object to a variable. - - Examples - ---------- - >>> from openbb_terminal.stocks.options import options_chains_model, options_chains_view - >>> spy = options_chains_model.load_options_chains("SPY") - >>> options_chains_view.display_surface(spy) - - Display only calls: - >>> options_chains_view.display_surface(spy, "calls") - - Display only puts: - >>> options_chains_view.display_surface(spy, "puts") - - Display a range of expirations: - >>> options_chains_view.display_surface(spy, dte_range=[7, 60]) - - Filter for a range of strike prices and include only those with open interest and trading volume: - >>> options_chains_view.display_surface(spy, strike_range=[400, 500], oi=True, volume=True) - """ - - if options_chains_model.validate_object(options, "object") is False: - return None - - if options.hasIV is False: - return console.print( - "Options data object does not have Implied Volatility and is required for this function." - ) - - options = deepcopy(options) - options.chains = options_chains_model.validate_object(options.chains, "chains") - - last_price = options.last_price # noqa:F841 - - calls = options.chains.query( - "`optionType` == 'call' & `dte` >= 0 & `impliedVolatility` > 0" - ) - puts = options.chains.query( - "`optionType` == 'put' & `dte` >= 0 & `impliedVolatility` > 0" - ) - - if option_type not in ["otm", "itm", "puts", "calls"]: - console.print("Invalid option type, defaulting to 'otm'.") - option_type = "otm" - - if oi: - calls = calls[calls["openInterest"] > 0] - puts = puts[puts["openInterest"] > 0] - - if volume: - calls = calls[calls["volume"] > 0] - puts = puts[puts["volume"] > 0] - - if dte_range is not None: - dte_range = [dte_range] if isinstance(dte_range, int) else dte_range # type: ignore [list-item] - if len(dte_range) > 1: - far = ( # noqa:F841 pylint: disable=unused-variable - dte_range[1] if dte_range[1] > dte_range[0] else dte_range[0] - ) - near = ( # noqa:F841 pylint: disable=unused-variable - dte_range[0] if dte_range[1] > dte_range[0] else dte_range[1] - ) - calls = calls.query("@near <= `dte` <= @far") - puts = puts.query("@near <= `dte` <= @far") - - if moneyness is not None and moneyness > 0: - high = ( # noqa:F841 pylint: disable=unused-variable - 1 + (moneyness / 100) - ) * last_price - low = ( # noqa:F841 pylint: disable=unused-variable - 1 - (moneyness / 100) - ) * last_price - calls = calls.query("@low <= `strike` <= @high") - puts = puts.query("@low <= `strike` <= @high") - - if strike_range is not None and len(strike_range) > 1: - high = ( # noqa:F841 pylint: disable=unused-variable - strike_range[1] if strike_range[1] > strike_range[0] else strike_range[0] - ) - low = ( # noqa:F841 pylint: disable=unused-variable - strike_range[0] if strike_range[1] > strike_range[0] else strike_range[1] - ) - calls = calls.query("@low <= `strike` <= @high") - puts = puts.query("@low <= `strike` <= @high") - - if option_type == "otm": - otm_calls = calls.query("`strike` > @last_price").set_index( - ["expiration", "strike", "optionType"] - ) - otm_puts = puts.query("`strike` < @last_price").set_index( - ["expiration", "strike", "optionType"] - ) - data = pd.concat([otm_calls, otm_puts]).sort_index().reset_index() - - if option_type == "itm": - itm_calls = calls.query("`strike` < @last_price").set_index( - ["expiration", "strike", "optionType"] - ) - itm_puts = puts.query("`strike` > @last_price").set_index( - ["expiration", "strike", "optionType"] - ) - data = pd.concat([itm_calls, itm_puts]).sort_index().reset_index() - - if option_type == "calls": - data = calls - if option_type == "puts": - data = puts - - data = data[ - [ - "expiration", - "strike", - "optionType", - "dte", - "impliedVolatility", - "openInterest", - "volume", - ] - ] - cols = ["Expiration", "Strike", "Type", "DTE", "IV", "OI", "Volume"] - data.columns = cols - - label_dict = {"calls": "Call", "puts": "Put", "otm": "OTM", "itm": "ITM"} - label = ( - label_dict[option_type] + " Options IV Surface" - if not oi - else label_dict[option_type] + " Options IV Surface With Open Interest" - ) - label = label + " Excluding Untraded Contracts" if volume else label - - X = data["DTE"] - Y = data["Strike"] - Z = data["IV"] - - points3D = np.vstack((X, Y, Z)).T - points2D = points3D[:, :2] - tri = Delaunay(points2D) - II, J, K = tri.simplices.T - - fig = OpenBBFigure() - fig.set_title(f"{options.symbol} {label}") - fig_kwargs = dict(z=Z, x=X, y=Y, i=II, j=J, k=K, intensity=Z) - fig.add_mesh3d( - **fig_kwargs, - alphahull=0, - opacity=1, - contour=dict(color="black", show=True, width=15), - colorscale=[ - [0, "darkred"], - [0.001, "crimson"], - [0.005, "red"], - [0.0075, "orangered"], - [0.015, "darkorange"], - [0.025, "orange"], - [0.04, "goldenrod"], - [0.055, "gold"], - [0.11, "magenta"], - [0.15, "plum"], - [0.4, "lightblue"], - [0.7, "royalblue"], - [0.9, "blue"], - [1, "darkblue"], - ], - hovertemplate="DTE: %{x}
Strike: %{y}
" - + "IV" - + ": %{z}", - showscale=True, - flatshading=True, - lighting=dict( - ambient=0.95, - diffuse=0.9, - roughness=0.8, - specular=0.9, - fresnel=0.001, - vertexnormalsepsilon=0.0001, - facenormalsepsilon=0.0001, - ), - ) - tick_kwargs = dict(tickfont=dict(size=12), titlefont=dict(size=14)) - fig.update_layout( - scene=dict( - xaxis=dict(title="DTE", autorange="reversed", **tick_kwargs), - yaxis=dict(title="Strike", **tick_kwargs), - zaxis=dict(title="IV", **tick_kwargs), - ), - title_x=0.5, - scene_camera=dict( - up=dict(x=0, y=0, z=0.75), - center=dict(x=-0.01, y=0, z=-0.3), - eye=dict(x=1.75, y=1.75, z=0.69), - ), - ) - fig.update_scenes(aspectmode="manual", aspectratio=dict(x=1.5, y=2.0, z=0.75)) - - if raw: - print_rich_table( - data, - title=label, - show_index=False, - export=bool(export), - ) - return None - - if export and export != "": - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "surface", - data, - sheet_name, - fig, - ) - return None - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_stats( - options: Options, - by: str = "expiration", - expiry: str = "", - oi: Optional[bool] = False, - percent: Optional[bool] = True, - ratios: Optional[bool] = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Chart a variety of volume and open interest statistics. - - Parameters - ----------- - options: Options - The options data object. - by: str - Statistics can be displayed by either "expiration" or "strike". Default is "expiration". - expiry: str - The target expiration date to display. Only valid when `percent` is False. - oi: bool - Display open interest if True, else volume. Default is False. - percent: bool - Displays volume or open interest as a percentage of the total across all expirations. Default is False. - ratios: bool - Displays Put/Call ratios. This parameter overrides the others when True. Default is False. - raw: bool - Displays the raw data table instead of a chart. - export: str - Export the data to a csv,json,xlsx file. - sheet_name: str - Name of the sheet to save the data to. Only valid when `export` is a `xlsx` file. - external_axes: bool - Return the OpenBB Figure Object to a variable. - - Examples - ---------- - >>> from openbb_terminal.sdk import openbb - >>> from openbb_terminal.stocks.options import options_chains_model, options_chains_view - >>> spy = options_chains_model.load_options_chains("SPY") - - Display volume by expiration: - >>> options_chains_view.display_stats(spy) - - Display volume by strike: - >>> options_chains_view.display_stats(spy, "strike") - - Display open interest by expiration: - >>> options_chains_view.display_stats(spy, oi=True) - - Display open interest, by expiration, as a percentage of the total: - >>> options_chains_view.display_stats(spy, oi=True, percent=True) - - Display volume and open interest put/call ratios: - >>> options_chains_view.display_stats(spy, ratios=True) - """ - - by_type = ["expiration", "strike"] - if by not in by_type: - by = "expiration" - - if options_chains_model.validate_object(options, "object") is False: - return None - options = deepcopy(options) - stats = options_chains_model.calculate_stats(options, by=by) - - if by == "strike" and expiry != "" and percent is False: - expiry = options_chains_model.get_nearest_expiration(options, expiry) - stats = options_chains_model.calculate_stats( - options.chains[options.chains["expiration"] == expiry], "strike" - ) - - net_volume = stats.sum()["Total Volume"] - net_oi = stats.sum()["Total OI"] - - stat_type = "Volume" if oi is False else "Open Interest" - index_name = "Expiration" if by == "expiration" else "Strike" - - stats["Puts OI"] = stats["Puts OI"] * (-1) - stats["Puts Volume"] = stats["Puts Volume"] * (-1) - expiry = "" if oi is True and by == "expiration" else expiry - title = ( - f"Percentage of Total {options.symbol} {stat_type}" - if percent is True - else f"{options.symbol} {stat_type}" - ) - xtitle = ( - f"{index_name}s With {stat_type} > 0.5% of Total {stat_type}" - if percent is True - else expiry - ) - ytitle = f"% of Total {stat_type}" if percent is True else stat_type - - fig = OpenBBFigure() - if ratios is False: - if percent is True: - stats["% of Total Volume"] = round( - stats["Total Volume"] / net_volume * 100, 4 - ) - stats["% of Total OI"] = round(stats["Total OI"] / net_oi * 100, 4) - stats_df = ( - stats.query("`% of Total Volume` > 0.5") - if oi is False - else stats.query("`% of Total OI` > 0.5") - ) - fig.add_bar( - x=stats_df.index, - y=( - stats_df["% of Total Volume"] - if oi is False - else stats_df["% of Total OI"] - ), - name=f"% of Total {stat_type}", - orientation="v", - ) - if percent is False: - fig.add_bar( - x=stats.index, - y=stats["Puts Volume"] if oi is False else stats["Puts OI"], - name="Puts", - orientation="v", - marker=dict(color="red"), - ) - fig.add_bar( - x=stats.index, - y=stats["Calls Volume"] if oi is False else stats["Calls OI"], - name="Calls", - orientation="v", - marker=dict(color="blue"), - ) - fig.update_layout( - legend=dict( - orientation="h", - yanchor="top", - y=1.125, - xanchor="left", - x=0.01, - ) - ) - if ratios is True: - title = f"{options.symbol} Ratios" - ytitle = "" - xtitle = "" - if by != "expiration": - by = "expiration" - stats = options_chains_model.calculate_stats(options, by) - fig.add_scatter( - x=stats.index, - y=stats["OI Ratio"].dropna(), - mode="lines+markers", - name="Put/Call OI Ratio", - marker=dict(color="blue"), - ) - fig.add_scatter( - x=stats.index, - y=stats["Vol-OI Ratio"].dropna(), - mode="lines+markers", - name="Volume/OI Ratio", - marker=dict(color="red"), - ) - fig.add_scatter( - x=stats.index, - y=stats["Volume Ratio"].dropna(), - mode="lines+markers", - name="Put/Call Volume Ratio", - marker=dict(color="orange"), - ) - - fig.update_xaxes(type="category") - fig.update_traces(width=0.98, selector=dict(type="bar")) - fig.update_layout( - title=dict(text=title, x=0.5, y=0.97), - barmode="overlay", - bargap=0, - bargroupgap=0, - yaxis=dict( - title=dict(text=ytitle, font=dict(size=16)), - ticklen=0, - showgrid=True, - tickfont=dict(size=14), - ), - xaxis=dict( - title=dict(text=xtitle, font=dict(size=16)), - showgrid=False, - autorange=True, - tickangle=90, - tickfont=dict(size=11), - ), - ) - if raw: - print_rich_table( - stats, - title=( - f"{options.symbol} Stats" - if expiry == "" or percent is True - else f"{options.symbol} for {expiry} Expiration" - ), - show_index=True, - index_name=index_name, - floatfmt=".4f", - export=bool(export), - ) - return None - - if export and export != "": - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "stats", - stats, - sheet_name, - fig, - ) - return None - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_skew( - options: Options, - expirations: Optional[list[str]] = None, - moneyness: Optional[float] = None, - strike: Optional[float] = None, - atm: Optional[bool] = False, - otm_only: Optional[bool] = False, - raw: Optional[bool] = False, - export: Optional[str] = "", - sheet_name: Optional[str] = "", - external_axes: Optional[bool] = False, -) -> Union[None, OpenBBFigure]: - """Chart the vertical skew of an option expiration, or the horizontal skew of equidistant % moneyness options. - - The vertical skew for each expiry and option is calculated by subtracting the IV of the ATM call or put. - Returns only where the IV is greater than 0. - - Horizontal skew is returned if a value for moneyness, or a strike price, is supplied. - With a strike price specified, both call and put IV skew are displayed. For moneyness, - it is expressed as the difference between skews of two equidistant OTM strikes (the closest call and put). - - Parameters - ----------- - options: Options - The options data object. - expirations: list[str] - The expiration date, or a list of dates. The closest date will be returned for each entry. - Format as YYYY-MM-DD. - moneyness: float - The % moneyess. When specified, this returns the forward skew curve at the target moneyness. - strike: float - A target strike price to observe the skew vs. contract. This argument overrides other parameters. - atm: bool - When true, returns the ATM skew curve. This will override other parameters, but is overridden by strike. - otm_only: bool - When true, returns only OTM portions of the put/call skew curves. - raw: bool - Returns a table instead of a plot. - export: str - Export the data to csv, json, xlsx file. - sheet_name: str - The name of the sheet to save the data to. Only valid when `export` is a `xlsx` file. - external_axes: bool - Returns the OpenBB Figure Object to a variable. - """ - - if options.hasIV is False: - return console.print( - "Options data object does not have Implied Volatility and is required for this function." - ) - options = deepcopy(options) - options.chains = options_chains_model.validate_object(options.chains, "chains") - - put_skew = pd.DataFrame() - call_skew = pd.DataFrame() - skew_df = pd.DataFrame() - export_df = pd.DataFrame() - colors = [ - "blue", - "red", - "burlywood", - "orange", - "green", - "grey", - "magenta", - "cyan", - "indigo", - "yellowgreen", - ] - - expirations = options.expirations[1] if expirations is None else expirations - expirations = [expirations] if isinstance(expirations, str) else expirations # type: ignore [list-item] - if len(expirations) > 10 and otm_only is True: - expirations = expirations[:10] # type: ignore [index] - elif len(expirations) > 5 and otm_only is False: - expirations = expirations[:5] # type: ignore [index] - - if moneyness is None: - fig = OpenBBFigure() - color = -1 - for expiration in expirations: # type: ignore [union-attr] - if expiration == "": - expiration = options.expirations[1] # noqa: PLW2901 - if expiration not in options.expirations: - expiration = ( # noqa: PLW2901 - options_chains_model.get_nearest_expiration(options, expiration) - ) - color = color + 1 - skew = options_chains_model.calculate_skew(options, expiration, moneyness)[ - ["Expiration", "Strike", "Option Type", "IV", "ATM IV", "Skew"] - ] - if skew["IV"].sum() == 0 and len(expirations) == 1: - return console.print("No IV data available for this expiration.") - - index_name = "Strike" if len(expirations) > 1 else expiration - call_skew = skew.query("`Option Type` == 'call'").set_index("Strike") - call_idx = call_skew.index - call_skew = call_skew[~call_idx.duplicated(keep="first")] - call_skew = call_skew["Skew"] - put_skew = skew.query("`Option Type` == 'put'").set_index("Strike") - put_idx = put_skew.index - put_skew = put_skew[~put_idx.duplicated(keep="first")] - put_skew = put_skew["Skew"] - skew_df["Call Skew"] = call_skew - skew_df["Put Skew"] = put_skew - export_df = pd.concat([export_df, skew]) - if otm_only is True: - calls = skew.query("`Option Type` == 'call'").reset_index() - puts = skew.query("`Option Type` == 'put'").reset_index() - put_idx = puts[puts["Skew"] == 0].index.values[0] - call_idx = calls[calls["Skew"] == 0].index.values[0] - call_skew = calls.iloc[call_idx - 1 : -1] - call_skew = call_skew.set_index("Strike")["Skew"] - call_idx = call_skew.index - call_skew = call_skew[~call_idx.duplicated(keep="first")] - put_skew = puts.iloc[0 : put_idx + 2] - put_skew = put_skew.set_index("Strike")["Skew"] - put_idx = put_skew.index - put_skew = put_skew[~put_idx.duplicated(keep="first")] - export_df = pd.concat([export_df, skew]) - fig.add_scatter( - x=call_skew.index, - y=call_skew.values, - mode="lines+markers", - name=f"{expiration} Calls" if len(expirations) > 1 else "Call IV Skew", - marker_color=colors[color] if len(expirations) > 1 else "blue", - ) - color = color + 1 if otm_only is False else color - fig.add_scatter( - x=put_skew.index, - y=put_skew.values, - mode="lines+markers", - name=f"{expiration} Puts" if len(expirations) > 1 else "Put IV Skew", - marker_color=colors[color] if len(expirations) > 1 else "red", - ) - - title = f"IV Skew for {options.symbol}" - if otm_only is True: - title = "OTM " + title - fig.update_layout(title=title) - - if atm or moneyness or strike: - if moneyness is None: - moneyness = 0 - skew_df = options_chains_model.calculate_skew(options, moneyness=moneyness) - skew = skew_df.copy() - call_skew = skew_df["Call Skew"] - put_skew = skew_df["Put Skew"] - export_df = pd.concat([export_df, skew_df]) - title = ( - f"IV Skew for {options.symbol} at {moneyness}% OTM" - if moneyness != 0 and atm is False - else f"ATM IV Skew for {options.symbol}" - ) - index_name = "Expiration" - fig = OpenBBFigure() - fig.add_scatter( - x=skew_df.index, - y=skew_df["IV Skew"], - mode="lines+markers", - name="OTM IV Skew", - marker=dict(color="green"), - ) - fig.update_xaxes(type="category") - if atm: - fig = OpenBBFigure() - fig.add_scatter( - x=skew_df.index, - y=skew_df["ATM Skew"], - mode="lines+markers", - name="ATM IV Skew", - marker=dict(color="green"), - ) - fig.update_xaxes(type="category") - if strike and strike > 0.25: - fig = OpenBBFigure() - strike = options_chains_model.get_nearest_call_strike( - options, strike_price=strike - ) - skew_df = options_chains_model.calculate_skew(options).query( - "`Strike` == @strike" - ) - call_df = skew_df.query("`Option Type` == 'call'").set_index("Expiration") - call_idx = call_df.index - call_df = call_df[~call_idx.duplicated(keep="first")] - call_df = call_df.reset_index() - put_df = skew_df.query("`Option Type` == 'put'").set_index("Expiration") - put_idx = put_df.index - put_df = put_df[~put_idx.duplicated(keep="first")] - put_df = put_df.reset_index() - title = f"IV Skew for {options.symbol} at ${strike}" - expirations = skew_df.Expiration.unique().tolist() - fig.add_scatter( - x=expirations, - y=call_df.Skew, - mode="lines+markers", - name="Call IV Skew", - marker_color="blue", - ) - fig.add_scatter( - x=expirations, - y=put_df.Skew, - mode="lines+markers", - name="Put IV Skew", - marker_color="red", - ) - fig.update_xaxes(type="category") - - fig.update_layout( - title=dict(text=title, x=0.5, y=0.97), - yaxis=dict( - title=dict( - text="IV Skew", - ), - ticklen=0, - ), - xaxis=dict( - showgrid=False, - autorange=True, - title=dict(text=index_name), - ), - legend=dict( - font=dict(size=12), - orientation="v", - yanchor="top", - y=0.97, - xanchor="center", - x=0.525, - ), - ) - - if raw: - print_rich_table( - export_df, - title=title, - show_index=True, - index_name=index_name, - floatfmt=".4f", - export=bool(export), - ) - return None - - if export and export != "": - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "skew", - export_df, - sheet_name, - fig, - ) - return None - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_volatility( - options: Options, - expirations: Optional[list[str]] = None, - moneyness: Optional[float] = None, - strike: Optional[float] = None, - oi: bool = False, - volume: bool = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Chart the implied volatility smile. - - Parameters - ----------- - options: Options - The options data object. - expirations: list[str] - Select up to five expiration(s) to display. Overridden by moneyness or strike. Format as YYYY-MM-DD. - moneyness: float - Specify a target % moneyness to display vs. contract dates. This argument overrides expirations. - strike: float - Specify a target strike price to display vs. contract dates. - Returned strike price is estimated as the closest one listed at approximately one year forward. - This argument overrides moneyness and expirations. - oi: bool - Return only contracts with open interest. Only valid for IV vs. Strike charts. Default is False. - volume: bool - Return only contracts with trading volume. Only valid for IV vs. Strike charts. Default is False. - raw: bool - Display the raw data instead of the chart. - export: str - Export dataframe data to csv,json,xlsx file. - external_axes: bool - Return the OpenBB Figure Object to a variable. - - Examples - ---------- - >>> from openbb_terminal.stocks.options import options_chains_model, options_chains_view - >>> spy = options_chains_model.load_options_chains("SPY") - >>> options_chains_view(spy) - - Plot IV @ Strike vs. contract dates: - >>> options_chains_view(spy, strike=450) - - Plot IV @ % OTM vs. contract dates: - >>> options_chains_view(spy, moneyness=20) - - Plot multiple expirations at once, and filter for only contracts with open interest: - >>> options_chains_view(spy, expirations=["2024-12-30", "2025-12-30"], oi=True) - """ - - if options.hasIV is False: - return console.print( - "Options data object does not have Implied Volatility and is required for this function." - ) - options = deepcopy(options) - options.chains = options_chains_model.validate_object(options.chains, "chains") - colors = [ - "blue", - "red", - "burlywood", - "orange", - "green", - "grey", - "magenta", - "cyan", - "indigo", - "yellowgreen", - ] - - index_name = "" - title = f"{options.symbol} Volatility" - export_df = pd.DataFrame() - - iv_df = options.chains[options.chains["impliedVolatility"] > 0][ - [ - "expiration", - "strike", - "optionType", - "dte", - "openInterest", - "volume", - "impliedVolatility", - ] - ] - - if moneyness is not None and strike is None: - fig = OpenBBFigure() - calls = pd.DataFrame() - puts = pd.DataFrame() - title = title + f" at {moneyness}% OTM" if strike is None else title - strikes = options_chains_model.get_nearest_otm_strike(options, moneyness) - if expirations is None: - days = options.chains.dte.unique().tolist() - for day in days: - call_strike = options_chains_model.get_nearest_call_strike( - options, day, strikes["call"] - ) - call = ( - iv_df[iv_df["strike"] == call_strike] - .query("`optionType` == 'call' & `dte` == @day") - .set_index("expiration") - ) - put_strike = options_chains_model.get_nearest_put_strike( - options, day, strikes["put"] - ) - put = ( - iv_df[iv_df["strike"] == put_strike] - .query("`optionType` == 'put' & `dte` == @day") - .set_index("expiration") - ) - calls = pd.concat([calls, call]) - puts = pd.concat([puts, put]) - expirations = iv_df["expiration"].unique().tolist() - export_df = pd.concat([export_df, calls, puts]) - fig.add_scatter( - x=expirations, - y=calls["impliedVolatility"], - mode="lines+markers", - name="Calls", - marker_color="blue", - ) - fig.add_scatter( - x=expirations, - y=puts["impliedVolatility"], - mode="lines+markers", - name="Puts", - marker_color="red", - ) - fig.update_xaxes(type="category") - - if moneyness is None and strike is None: - expirations = options.expirations[1] if expirations is None else expirations - - if expirations is not None and moneyness is None: - fig = OpenBBFigure() - color = -1 - expirations = [expirations] if isinstance(expirations, str) else expirations # type: ignore [list-item] - if len(expirations) > 5: - expirations = expirations[:5] - if oi is True: - iv_df = iv_df[iv_df["openInterest"] > 0] - if volume is True: - iv_df = iv_df[iv_df["volume"] > 0] - for expiration in expirations: - expiration = options_chains_model.get_nearest_expiration( # noqa: PLW2901 - options, expiration - ) - data = iv_df[iv_df["expiration"] == expiration] - calls = data[data["optionType"] == "call"] - puts = data[data["optionType"] == "put"] - color = color + 1 - fig.add_scatter( - x=calls["strike"].unique().tolist(), - y=calls["impliedVolatility"], - mode="lines+markers", - name="Calls" if len(expirations) <= 1 else f"Calls at {expiration}", - marker_color=colors[color] if len(expirations) > 1 else "blue", - ) - color = color + 1 - fig.add_scatter( - x=puts["strike"].unique().tolist(), - y=puts["impliedVolatility"], - mode="lines+markers", - name="Puts" if len(expirations) <= 1 else f"Puts at {expiration}", - marker_color=colors[color] if len(expirations) > 1 else "red", - ) - export_df = pd.concat([export_df, data]) - index_name = expiration if len(expirations) <= 1 else "" - title = title + " With Open Interest" if oi and not strike else title - title = ( - title + " Excluding Untraded Contracts" if volume and not strike else title - ) - - if strike is not None: - fig = OpenBBFigure() - strike = ( - options_chains_model.get_nearest_call_strike( - options, 300, strike_price=strike - ) - if strike > options.last_price - else options_chains_model.get_nearest_put_strike( - options, 300, strike_price=strike - ) - ) - title = title + f" at ${strike}" - calls = iv_df[iv_df["strike"] == strike].query("`optionType` == 'call'") - puts = iv_df[iv_df["strike"] == strike].query("`optionType` == 'put'") - expirations = iv_df["expiration"].unique().tolist() - export_df = pd.concat([export_df, calls, puts]) - fig.add_scatter( - x=expirations, - y=calls["impliedVolatility"], - mode="lines+markers", - name="Calls", - marker_color="blue", - ) - fig.add_scatter( - x=expirations, - y=puts["impliedVolatility"], - mode="lines+markers", - name="Puts", - marker_color="red", - ) - fig.update_xaxes(type="category") - - fig.update_layout( - title=dict(text=title, x=0.5, y=0.97), - yaxis=dict( - title=dict( - text="IV", - ), - ticklen=0, - ), - xaxis=dict( - showgrid=False, - autorange=True, - title=dict(text=index_name), - ), - legend=dict( - font=dict(size=12), - orientation="v", - yanchor="top", - y=0.97, - xanchor="center", - x=0.525, - ), - ) - - if raw: - print_rich_table( - export_df, - title=title, - show_index=False, - floatfmt=".4f", - export=bool(export), - ) - return None - - if export and export != "": - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "volatility", - export_df, - sheet_name, - fig, - ) - return None - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/stocks/options/options_controller.py b/openbb_terminal/stocks/options/options_controller.py deleted file mode 100644 index d0e136a96ce..00000000000 --- a/openbb_terminal/stocks/options/options_controller.py +++ /dev/null @@ -1,1430 +0,0 @@ -""" Options Controller Module """ - -__docformat__ = "numpy" - -import argparse -import logging -from datetime import datetime, timedelta -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - parse_and_split_input, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console, get_ordered_list_sources -from openbb_terminal.stocks.options import ( - alphaquery_view, - barchart_view, - calculator_view, - chartexchange_view, - fdscanner_view, - intrinio_model, - intrinio_view, - nasdaq_model, - tradier_model, - tradier_view, - yfinance_model, - yfinance_view, -) -from openbb_terminal.stocks.options.options_view import ( - display_chains, - display_expiry_dates, - plot_oi, - plot_voi, - plot_vol, -) - -# pylint: disable=R1710,C0302,R0916,R0902 - -# TODO: HELP WANTED! This controller requires some MVC style refactoring -# - At the moment there's too much logic in the controller to implement an -# API wrapper. Please refactor functions like 'call_exp' - -# TODO: Additional refactoring -- load should bring in a df from the sdk_helpers functions and we can get expirations -# from there. Additionally each view function should be made a function that takes the df and plots it instead of -# getting the new chain - -logger = logging.getLogger(__name__) - - -class OptionsController(BaseController): - """Options Controller class""" - - CHOICES_COMMANDS = [ - "calc", - "info", - "pcr", - "load", - "exp", - "vol", - "voi", - "oi", - "hist", - "chains", - "grhist", - "unu", - "plot", - "vsurf", - "greeks", - "eodchain", - ] - CHOICES_MENUS = [ - "pricing", - ] - - grhist_greeks_choices = [ - "iv", - "gamma", - "theta", - "vega", - "delta", - "rho", - "premium", - ] - - unu_sortby_choices = [ - "Strike", - "Vol/OI", - "Vol", - "OI", - "Bid", - "Ask", - "Exp", - "Ticker", - ] - pcr_length_choices = ["10", "20", "30", "60", "90", "120", "150", "180"] - - plot_vars_choices = ["ltd", "s", "lp", "b", "a", "c", "pc", "v", "oi", "iv"] - plot_custom_choices = ["smile"] - PATH = "/stocks/options/" - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - queue: Optional[List[str]] = None, - expiration: Optional[str] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - self.prices = pd.DataFrame(columns=["Price", "Chance"]) - self.selected_date = expiration or "" - self.chain: pd.DataFrame = pd.DataFrame() - self.full_chain: pd.DataFrame = pd.DataFrame() - self.expiry_dates: List[str] = [] - self.current_price = 0.0 - self.is_eod = False - # Keeps track of initial source of load so we can use correct commands later - self.source = get_ordered_list_sources(f"{self.PATH}load")[0] - if ticker: - self.set_option_chain() - self.set_current_price() - self.set_expiry_dates() - self.selected_date = "" - - if not self.expiry_dates and not self.chain.empty: - console.print( - f"""[red]Issue loading options for "{self.ticker}" from source "{self.source}".[/red]""" - ) - console.print("Defaulting to YahooFinance now.") - self.expiry_dates = nasdaq_model.option_expirations(self.ticker) - - else: - self.expiry_dates = [] - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - # This menu contains dynamic choices that may change during runtime - self.choices = choices - self.completer = NestedCompleter.from_nested_dict(choices) - - self.update_runtime_choices() - - def parse_input(self, an_input: str) -> List: - """Parse controller input - - Overrides the parent class function to handle github org/repo path convention. - See `BaseController.parse_input()` for details. - """ - # Filtering out sorting parameters with forward slashes like P/E - sort_filter = r"((\ -s |\ --sortby ).*?(Vol\/OI)*)" - - custom_filters = [sort_filter] - - return parse_and_split_input(an_input=an_input, custom_filters=custom_filters) - - def set_option_chain(self): - df = pd.DataFrame() - - if self.source == "Tradier": - df = tradier_model.get_full_option_chain(self.ticker) - - elif self.source == "Nasdaq": - df = nasdaq_model.get_full_option_chain(self.ticker) - - elif self.source == "Intrinio": - df = intrinio_model.get_full_option_chain(self.ticker) - - else: - self.source = "YahooFinance" - df = yfinance_model.get_full_option_chain(self.ticker) - - if (isinstance(df, pd.DataFrame) and df.empty) or ( - not isinstance(df, pd.DataFrame) and not df - ): - console.print("[red]Error loading option chain.[/red]") - return - self.full_chain = df.copy(deep=True) - self.chain = self.full_chain.copy(deep=True) - - console.print(f"Loaded option chain from {self.source}\n") - - def set_current_price(self): - if not self.chain.empty: - if self.source == "Tradier": - last_price = tradier_model.get_last_price(self.ticker) - self.current_price = last_price or 0.0 - elif self.source == "Nasdaq": - self.current_price = nasdaq_model.get_underlying_price(self.ticker)[ - "price" - ] - elif self.source == "Intrinio": - self.current_price = intrinio_model.get_last_price(self.ticker) - else: - self.current_price = yfinance_model.get_last_price(self.ticker) - - def set_expiry_dates(self): - if self.full_chain.empty: - self.expiry_dates = [] - console.print("[red]The ticker provided could not be found[/red]\n") - else: - self.expiry_dates = self.full_chain.expiration.unique().tolist() or [] - - def update_runtime_choices(self): - """Update runtime choices""" - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - if not self.chain.empty: - strike = set(self.chain["strike"]) - - self.choices["hist"]["--strike"] = {str(c): {} for c in strike} - self.choices["grhist"]["-s"] = "--strike" - self.choices["grhist"]["--strike"] = {str(c): {} for c in strike} - self.choices["grhist"]["-s"] = "--strike" - - self.choices["chains"]["--display"] = { - str(c): {} for c in self.chain.columns - } - self.choices["chains"]["-d"] = "--display" - - if self.expiry_dates: - self.choices["vol"]["--expiration"] = { - str(c): {} for c in self.expiry_dates - } - self.choices["vol"]["-e"] = "--expiration" - self.choices["voi"]["--expiration"] = { - str(c): {} for c in self.expiry_dates - } - self.choices["voi"]["-e"] = "--expiration" - self.choices["oi"]["--expiration"] = { - str(c): {} for c in self.expiry_dates - } - self.choices["oi"]["-e"] = "--expiration" - self.choices["chains"]["--expiration"] = { - str(c): {} for c in self.expiry_dates - } - self.choices["chains"]["-e"] = "--expiration" - - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def print_help(self): - """Print help.""" - mt = MenuText("stocks/options/") - mt.add_cmd("unu") - mt.add_cmd("calc") - mt.add_raw("\n") - mt.add_cmd("load") - mt.add_cmd("exp", self.ticker) - mt.add_raw("\n") - mt.add_param("_ticker", self.ticker or "") - mt.add_param("_expiry", self.selected_date or "") - mt.add_raw("\n") - mt.add_cmd("pcr", self.ticker and self.selected_date) - mt.add_cmd("info", self.ticker and self.selected_date) - mt.add_cmd("chains", self.ticker and self.selected_date) - mt.add_cmd("oi", self.ticker and self.selected_date) - mt.add_cmd("vol", self.ticker and self.selected_date) - mt.add_cmd("voi", self.ticker and self.selected_date) - mt.add_cmd("hist", self.ticker and self.selected_date) - mt.add_cmd("vsurf", self.ticker and self.selected_date) - mt.add_cmd("grhist", self.ticker and self.selected_date) - mt.add_cmd("plot", self.ticker and self.selected_date) - mt.add_cmd("greeks", self.ticker and self.selected_date) - mt.add_cmd("eodchain", self.ticker and self.selected_date) - mt.add_raw("\n") - console.print(text=mt.menu_text, menu="Stocks - Options") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - if self.selected_date: - return [ - "stocks", - f"load {self.ticker}", - "options", - f"exp -d {self.selected_date}", - ] - return ["stocks", f"load {self.ticker}", "options"] - return [] - - @log_start_end(log=logger) - def call_calc(self, other_args: List[str]): - """Process calc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="calc", - description="Calculate profit or loss for given option settings.", - ) - parser.add_argument( - "--put", - action="store_true", - default=False, - dest="put", - help="Flag to calculate put option", - ) - parser.add_argument( - "--sell", - action="store_true", - default=False, - dest="sell", - help="Flag to get profit chart of selling contract", - ) - parser.add_argument( - "-s", - "--strike", - type=float, - dest="strike", - help="Option strike price", - default=10, - ) - parser.add_argument( - "-p", - "--premium", - type=float, - dest="premium", - help="Premium price", - default=1, - ) - parser.add_argument( - "-m", - "--min", - type=float, - dest="min", - help="Min price to look at", - default=-1, - required="-M" in other_args, - ) - parser.add_argument( - "-M", - "--max", - type=float, - dest="max", - help="Max price to look at", - default=-1, - required="-m" in other_args, - ) - if ns_parser := self.parse_known_args_and_warn(parser, other_args): - pars = ( - {"x_min": ns_parser.min, "x_max": ns_parser.max} - if ns_parser.min > 0 and ns_parser.max > 0 - else {} - ) - - calculator_view.view_calculator( - strike=ns_parser.strike, - premium=ns_parser.premium, - put=ns_parser.put, - sell=ns_parser.sell, - **pars, - ) - - @log_start_end(log=logger) - def call_unu(self, other_args: List[str]): - """Process unu command""" - parser = argparse.ArgumentParser( - prog="unu", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="This command gets unusual options from fdscanner.com", - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=int, - default=20, - help="Limit of options to show. Each scraped page gives 20 results.", - ) - parser.add_argument( - "-s", - "--sortby", - dest="sortby", - nargs="+", - default="Vol/OI", - choices=self.unu_sortby_choices, - help="Column to sort by. Vol/OI is the default and typical variable to be considered unusual.", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-p", - "--puts_only", - dest="puts_only", - help="Flag to show puts only", - default=False, - action="store_true", - ) - parser.add_argument( - "-c", - "--calls_only", - dest="calls_only", - help="Flag to show calls only", - default=False, - action="store_true", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ): - if ns_parser.calls_only and ns_parser.puts_only: - console.print( - "Cannot return puts only and calls only. Either use one or neither." - ) - else: - fdscanner_view.display_options( - limit=ns_parser.limit, - sortby=ns_parser.sortby, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ascend=ns_parser.reverse, - calls_only=ns_parser.calls_only, - puts_only=ns_parser.puts_only, - ) - - @log_start_end(log=logger) - def call_pcr(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="pcr", - description="Display put to call ratio for ticker [AlphaQuery.com]", - ) - parser.add_argument( - "-l", - "--length", - help="Window length to get", - dest="length", - choices=self.pcr_length_choices, - default=30, - ) - parser.add_argument( - "-s", - "--start", - help="Start date for plot", - type=valid_date, - default=datetime.now() - timedelta(days=366), - dest="start", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES - ): - if self.ticker: - alphaquery_view.display_put_call_ratio( - symbol=self.ticker, - window=ns_parser.length, - start_date=ns_parser.start.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded. First use `load `") - - @log_start_end(log=logger) - def call_info(self, other_args: List[str]): - """Process info command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="info", - description="Display option data [Source: Barchart.com]", - ) - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ): - if self.ticker: - barchart_view.print_options_data( - symbol=self.ticker, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - else: - console.print("No ticker loaded.\n") - - @log_start_end(log=logger) - def call_grhist(self, other_args: List[str]): - """Process grhist command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="grhist", - description="Plot historical option greeks.", - ) - parser.add_argument( - "-s", - "--strike", - dest="strike", - type=float, - help="Strike price to look at", - ) - parser.add_argument( - "-e", - "--expiration", - dest="expiration", - type=str, - default=self.selected_date, - help="The expiration date of the option. Format: YYYY-MM-DD", - ) - parser.add_argument( - "-p", - "--put", - dest="put", - action="store_true", - default=False, - help="Flag for showing put option", - ) - parser.add_argument( - "-g", - "--greek", - dest="greek", - type=str, - choices=self.grhist_greeks_choices, - default="delta", - help="Greek column to select", - ) - parser.add_argument( - "-c", - "--chain", - dest="chain_id", - default="", - type=str, - required="--strike" not in other_args and "-h" not in other_args, - help="OCC option symbol", - ) - parser.add_argument( - "-r", - "--raw", - dest="raw", - action="store_true", - default=False, - help="Display raw data", - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - default=20, - type=int, - help="Limit of raw data rows to display", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ): - if self.ticker or ns_parser.chain_id in other_args: - if ( - self.selected_date - or ns_parser.expiration in other_args - or ns_parser.chain_id - ): - if ( - not self.chain.empty - and ( - ( - ns_parser.put - and ns_parser.strike - in [float(strike) for strike in self.chain["strike"]] - ) - or ( - not ns_parser.put - and ns_parser.strike - in [float(strike) for strike in self.chain["strike"]] - ) - ) - or ns_parser.chain_id in other_args - ): - intrinio_view.view_historical_greeks( - symbol=self.ticker, - expiry=( - ns_parser.expiration - if ns_parser.expiration - else self.selected_date - ), - strike=ns_parser.strike, - greek=ns_parser.greek, - chain_id=ns_parser.chain_id, - put=ns_parser.put, - raw=ns_parser.raw, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - console.print("No correct strike input") - else: - console.print("No expiry loaded. First use `exp `") - else: - console.print("No ticker loaded. First use `load `") - - @log_start_end(log=logger) - def call_load(self, other_args: List[str]): - """Process load command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="load", - description="Load a ticker into option menu", - ) - parser.add_argument( - "-t", - "--ticker", - action="store", - dest="ticker", - required="-h" not in other_args and "--help" not in other_args, - help="Stock ticker", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - if ns_parser := self.parse_known_args_and_warn( - parser, - other_args, - ): - self.ticker = ns_parser.ticker.upper() - self.source = ns_parser.source - self.is_eod = False - - self.set_option_chain() - self.set_current_price() - self.set_expiry_dates() - self.selected_date = "" - - if not self.expiry_dates and not self.chain.empty: - console.print( - f"""[red]No expiration dates found for ticker "{self.ticker}" and source "{ns_parser.source}".""" - ) - console.print("Please try loading from a different source.") - console.print("Loading from YahooFinance now.") - self.expiry_dates = nasdaq_model.option_expirations(self.ticker) - - self.update_runtime_choices() - - @log_start_end(log=logger) - def call_exp(self, other_args: List[str]): - """Process exp command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="exp", - description="See and set expiration date", - ) - parser.add_argument( - "-i", - "--index", - dest="index", - action="store", - type=int, - default=-1, - choices=range(len(self.expiry_dates)), - help="Select index for expiry date.", - ) - parser.add_argument( - "-d", - "--date", - dest="date", - type=str, - choices=self.expiry_dates + [""], - help="Select date (YYYY-MM-DD)", - default="", - ) - - if other_args and "-" not in other_args[0][0]: - first_int = int(other_args[0].split("-")[0]) - if first_int > 2000: - other_args.insert(0, "-d") - else: - other_args.insert(0, "-i") - - if ns_parser := self.parse_known_args_and_warn(parser, other_args): - if self.ticker: - # Print possible expiry dates - if ns_parser.index == -1 and not ns_parser.date: - display_expiry_dates(self.expiry_dates) - # Set expiry date with date argument - elif ns_parser.date: - if ns_parser.date in self.expiry_dates: - console.print(f"Expiration set to {ns_parser.date}") - self.selected_date = ns_parser.date - else: - console.print("Expiration not an option") - # Set expiry date with index argument - else: - expiry_date = self.expiry_dates[ns_parser.index] - console.print(f"Expiration set to {expiry_date}") - self.selected_date = expiry_date - - if self.selected_date: - self.chain = self.full_chain[ - self.full_chain["expiration"] == self.selected_date - ] - self.update_runtime_choices() - - else: - console.print("Please load a ticker using `load `.") - - @log_start_end(log=logger) - def call_hist(self, other_args: List[str]): - """Process hist command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hist", - description="Gets historical quotes for given option chain", - ) - parser.add_argument( - "-s", - "--strike", - dest="strike", - type=float, - required="--chain" not in other_args - and "-c" not in other_args - and "-h" not in other_args, - help="Strike price to look at", - ) - parser.add_argument( - "-p", - "--put", - dest="put", - action="store_true", - default=False, - help="Flag for showing put option", - ) - parser.add_argument( - "-c", - "--chain", - dest="chain_id", - type=str, - help="Option symbol", - default=None, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-s") - if ns_parser := self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10, - ): - if not self.ticker and ns_parser.chain_id is None: - console.print("No ticker loaded. First use `load `") - return - if not self.selected_date and ns_parser.chain_id is None: - console.print("No expiry loaded. First use `exp `") - return - if ( - not self.chain.empty - and ( - ( - ns_parser.put - and ns_parser.strike - not in [float(strike) for strike in self.chain["strike"]] - ) - or ( - not ns_parser.put - and ns_parser.strike - not in [float(strike) for strike in self.chain["strike"]] - ) - ) - and ns_parser.chain_id is None - ): - console.print("No correct strike input") - return - if ns_parser.source == "ChartExchange": - chartexchange_view.display_raw( - symbol=self.ticker, - expiry=self.selected_date, - call=not ns_parser.put, - price=ns_parser.strike, - limit=ns_parser.limit, - raw=ns_parser.raw, - export=ns_parser.export, - chain_id=ns_parser.chain_id, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - if ns_parser.source == "Intrinio": - intrinio_view.display_historical( - symbol=self.ticker, - expiry=self.selected_date, - strike=ns_parser.strike, - put=ns_parser.put, - raw=ns_parser.raw, - chain_id=ns_parser.chain_id, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - if ns_parser.source == "Tradier": # nosec - tradier_view.display_historical( - symbol=self.ticker, - expiry=self.selected_date, - strike=ns_parser.strike, - put=ns_parser.put, - raw=ns_parser.raw, - chain_id=ns_parser.chain_id, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_chains(self, other_args: List[str]): - """Process chains command""" - parser = argparse.ArgumentParser( - prog="chains", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="Display option chains", - ) - parser.add_argument( - "-c", - "--calls", - action="store_true", - default=False, - dest="calls", - help="Flag to show calls only", - ) - parser.add_argument( - "-p", - "--puts", - action="store_true", - default=False, - dest="puts", - help="Flag to show puts only", - ) - parser.add_argument( - "-m", - "--min", - dest="min_sp", - type=float, - default=-1, - help="minimum strike price to consider.", - ) - parser.add_argument( - "-M", - "--max", - dest="max_sp", - type=float, - default=-1, - help="maximum strike price to consider.", - ) - parser.add_argument( - "-d", - "--display", - dest="to_display", - default=[] if self.chain.empty else ",".join(list(self.chain)), - type=str, - help="Columns to display", - ) - parser.add_argument( - "-e", - "--expiration", - dest="exp", - type=str, - choices=self.expiry_dates + [""], - help="Select expiration date (YYYY-MM-DD)", - default="", - ) - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ): - if self.ticker: - if ns_parser.exp: - if ns_parser.exp in self.expiry_dates: - self.selected_date = ns_parser.exp - self.chain = self.full_chain[ - self.full_chain["expiration"] == self.selected_date - ] - self.update_runtime_choices() - console.print(f"Expiration set to {ns_parser.exp}") - else: - self.selected_date = self.expiry_dates[0] - console.print( - f"Expiration not an option. Expiration set to {self.selected_date}" - ) - if self.selected_date: - df_chain = self.chain.copy() - needed = ["symbol", "code", "optionType", "expiration", "strike"] - needed = [col for col in needed if col in df_chain.columns] - if ns_parser.to_display: - to_display = ns_parser.to_display.split(",") - if self.is_eod: - for col in ["contractSymbol", "lastPrice", "bid", "ask"]: - to_display.remove(col) - display = [col for col in to_display if col not in needed] - df_chain = df_chain[needed + display] - - display_chains( - chain=df_chain, - expire=self.selected_date, - calls_only=ns_parser.calls, - puts_only=ns_parser.puts, - min_sp=ns_parser.min_sp, - max_sp=ns_parser.max_sp, - current_price=self.current_price, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - console.print( - "No expiry loaded. First use `exp {expiry date}` or specify an expiration with the `-e` flag" - ) - else: - console.print("No ticker loaded. First use `load `") - - @log_start_end(log=logger) - def call_vol(self, other_args: List[str]): - """Process vol command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="vol", - description="Plot volume. Volume refers to the number of contracts traded today.", - ) - parser.add_argument( - "-m", - "--min", - default=-1, - type=float, - help="Min strike to plot", - dest="min", - ) - parser.add_argument( - "-M", - "--max", - default=-1, - type=float, - help="Max strike to plot", - dest="max", - ) - parser.add_argument( - "-c", - "--calls", - action="store_true", - default=False, - dest="calls", - help="Flag to plot call options only", - ) - parser.add_argument( - "-p", - "--puts", - action="store_true", - default=False, - dest="puts", - help="Flag to plot put options only", - ) - parser.add_argument( - "-e", - "--expiration", - dest="exp", - type=str, - choices=self.expiry_dates + [""], - help="Select expiration date (YYYY-MM-DD)", - default="", - ) - if ns_parser := self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - ): - if self.ticker: - if ns_parser.exp: - if ns_parser.exp in self.expiry_dates: - self.selected_date = ns_parser.exp - self.chain = self.full_chain[ - self.full_chain["expiration"] == self.selected_date - ] - self.update_runtime_choices() - console.print(f"Expiration set to {ns_parser.exp}") - else: - self.selected_date = self.expiry_dates[0] - console.print( - f"Expiration not an option. Expiration set to {self.selected_date}" - ) - if self.selected_date: - plot_vol( - chain=self.chain, - current_price=self.current_price, - symbol=self.ticker, - expiry=self.selected_date, - min_sp=ns_parser.min, - max_sp=ns_parser.max, - calls_only=ns_parser.calls, - puts_only=ns_parser.puts, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - raw=ns_parser.raw, - ) - else: - console.print( - "No expiry loaded. First use `exp {expiry date}` or specify an expiration with the `-e` flag" - ) - else: - console.print("No ticker loaded. First use `load `") - - @log_start_end(log=logger) - def call_voi(self, other_args: List[str]): - """Process voi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="voi", - description="""Plots Volume + Open Interest of calls vs puts.""", - ) - parser.add_argument( - "-v", - "--minv", - dest="min_vol", - type=float, - default=-1, - help="minimum volume (considering open interest) threshold of the plot.", - ) - parser.add_argument( - "-m", - "--min", - dest="min_sp", - type=float, - default=-1, - help="minimum strike price to consider in the plot.", - ) - parser.add_argument( - "-M", - "--max", - dest="max_sp", - type=float, - default=-1, - help="maximum strike price to consider in the plot.", - ) - parser.add_argument( - "-e", - "--expiration", - dest="exp", - type=str, - choices=self.expiry_dates + [""], - help="Select expiration date (YYYY-MM-DD)", - default="", - ) - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True - ): - if self.ticker: - if ns_parser.exp: - if ns_parser.exp in self.expiry_dates: - self.selected_date = ns_parser.exp - self.chain = self.full_chain[ - self.full_chain["expiration"] == self.selected_date - ] - self.update_runtime_choices() - console.print(f"Expiration set to {ns_parser.exp}") - else: - self.selected_date = self.expiry_dates[0] - console.print( - f"Expiration not an option. Expiration set to {self.selected_date}" - ) - if self.selected_date: - plot_voi( - chain=self.chain, - current_price=self.current_price, - symbol=self.ticker, - expiry=self.selected_date, - min_sp=ns_parser.min_sp, - max_sp=ns_parser.max_sp, - raw=ns_parser.raw, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - console.print( - "No expiry loaded. First use `exp {expiry date}` or specify an expiration with the `-e` flag" - ) - else: - console.print("No ticker loaded. First use `load `") - - @log_start_end(log=logger) - def call_oi(self, other_args: List[str]): - """Process oi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="oi", - description="""Plot open interest. Open interest represents the number of - contracts that exist.""", - ) - parser.add_argument( - "-m", - "--min", - default=-1, - type=float, - help="Min strike to plot", - dest="min", - ) - parser.add_argument( - "-M", - "--max", - default=-1, - type=float, - help="Max strike to plot", - dest="max", - ) - parser.add_argument( - "-c", - "--calls", - action="store_true", - default=False, - dest="calls", - help="Flag to plot call options only", - ) - parser.add_argument( - "-p", - "--puts", - action="store_true", - default=False, - dest="puts", - help="Flag to plot put options only", - ) - parser.add_argument( - "-e", - "--expiration", - dest="exp", - type=str, - choices=self.expiry_dates + [""], - help="Select expiration date (YYYY-MM-DD)", - default="", - ) - if ns_parser := self.parse_known_args_and_warn( - parser, - other_args, - raw=True, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - ): - if self.ticker: - if ns_parser.exp: - if ns_parser.exp in self.expiry_dates: - self.selected_date = ns_parser.exp - self.chain = self.full_chain[ - self.full_chain["expiration"] == self.selected_date - ] - self.update_runtime_choices() - console.print(f"Expiration set to {ns_parser.exp}") - else: - self.selected_date = self.expiry_dates[0] - console.print( - f"Expiration not an option. Expiration set to {self.selected_date}" - ) - if self.selected_date: - plot_oi( - chain=self.chain, - current_price=self.current_price, - symbol=self.ticker, - expiry=self.selected_date, - min_sp=ns_parser.min, - max_sp=ns_parser.max, - calls_only=ns_parser.calls, - puts_only=ns_parser.puts, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - raw=ns_parser.raw, - ) - else: - console.print( - "No expiry loaded. First use `exp {expiry date}` or specify an expiration with the `-e` flag" - ) - else: - console.print("No ticker loaded. First use `load `") - - @log_start_end(log=logger) - def call_plot(self, other_args: List[str]): - """Process plot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="plot", - description="Shows a plot for the given x and y variables", - ) - parser.add_argument( - "-p", - "--put", - action="store_true", - default=False, - dest="put", - help="Shows puts instead of calls", - ) - parser.add_argument( - "-x", - "--x_axis", - type=str, - dest="x", - default="s", - choices=self.plot_vars_choices, - help=( - "ltd- last trade date, s- strike, lp- last price, b- bid, a- ask," - "c- change, pc- percent change, v- volume, oi- open interest, iv- implied volatility" - ), - ) - parser.add_argument( - "-y", - "--y_axis", - type=str, - dest="y", - default="iv", - choices=self.plot_vars_choices, - help=( - "ltd- last trade date, s- strike, lp- last price, b- bid, a- ask," - "c- change, pc- percent change, v- volume, oi- open interest, iv- implied volatility" - ), - ) - parser.add_argument( - "-c", - "--custom", - type=str, - choices=self.plot_custom_choices, - dest="custom", - default=None, - help="Choose from already created graphs", - ) - - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_FIGURES_ALLOWED - ): - if self.ticker: - if self.selected_date: - if ( - ns_parser.x is None or ns_parser.y is None - ) and ns_parser.custom is None: - console.print( - "Please submit an X and Y value, or select a preset." - ) - else: - yfinance_view.plot_plot( - symbol=self.ticker, - expiry=self.selected_date, - put=ns_parser.put, - x=ns_parser.x, - y=ns_parser.y, - custom=ns_parser.custom, - export=ns_parser.export, - ) - else: - console.print("No expiry loaded. First use `exp {expiry date}`") - else: - console.print("No ticker loaded. First use `load `") - - @log_start_end(log=logger) - def call_vsurf(self, other_args: List[str]): - """Process vsurf command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="vsurf", - description="Plot 3D volatility surface.", - ) - parser.add_argument( - "-z", - "--z-axis", - default="IV", - dest="z", - choices=["IV", "OI", "LP"], - type=str, - help="The data for the Z axis", - ) - - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ): - yfinance_view.display_vol_surface( - self.ticker, - export=ns_parser.export, - z=ns_parser.z, - ) - - @log_start_end(log=logger) - def call_greeks(self, other_args: List[str]): - """Process greeks command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="greeks", - description="The greeks for a given option.", - ) - parser.add_argument( - "-d", - "--div", - dest="dividend", - type=float, - default=0, - help="The dividend continuous rate", - ) - parser.add_argument( - "-r", - "--risk-free", - dest="risk_free", - default=None, - type=float, - help="The risk free rate", - ) - parser.add_argument( - "-p", - "--put", - dest="put", - action="store_true", - default=False, - help="Whether the option is a put.", - ) - parser.add_argument( - "-m", - "--min", - dest="min", - type=float, - default=None, - help="Minimum strike price to show.", - ) - parser.add_argument( - "-M", - "--max", - dest="max", - type=float, - default=None, - help="Maximum strike price to show.", - ) - parser.add_argument( - "-a", - "--all", - dest="all", - action="store_true", - default=False, - help="Whether to show all greeks.", - ) - - if ns_parser := self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ): - if not self.ticker: - console.print("No ticker loaded. First use `load `") - elif not self.selected_date: - console.print("No expiry loaded. First use `exp {expiry date}`") - else: - opt_type = -1 if ns_parser.put else 1 - yfinance_view.show_greeks( - symbol=self.ticker, - div_cont=ns_parser.dividend, - expiry=self.selected_date, - rf=ns_parser.risk_free, - opt_type=opt_type, - mini=ns_parser.min, - maxi=ns_parser.max, - show_all=ns_parser.all, - ) - - @log_start_end(log=logger) - def call_eodchain(self, other_args: List[str]): - """Process greeks command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="eodchain", - description="Get full option chain at a given end of day.", - ) - parser.add_argument( - "-d", - "--date", - type=valid_date, - dest="date", - help="Date to get option chain for. Format YYYY-MM-DD", - required="-h" not in other_args and "--help" not in other_args, - ) - - if ns_parser := self.parse_known_args_and_warn(parser, other_args): - if not self.ticker: - console.print("No ticker loaded. First use `load `") - return - self.full_chain = intrinio_model.get_full_chain_eod( - self.ticker, datetime.strftime(ns_parser.date, "%Y-%m-%d") - ) - self.is_eod = True diff --git a/openbb_terminal/stocks/options/options_sdk_helper.py b/openbb_terminal/stocks/options/options_sdk_helper.py deleted file mode 100644 index b91b5ec03c9..00000000000 --- a/openbb_terminal/stocks/options/options_sdk_helper.py +++ /dev/null @@ -1,1140 +0,0 @@ -"""Options Functions For OpenBB SDK""" - -import logging -import re -from datetime import datetime, timedelta -from typing import Optional, Union - -import numpy as np -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_rf -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options import ( - chartexchange_model, - intrinio_model, - nasdaq_model, - options_chains_model, - options_chains_view, - tradier_model, - yfinance_model, -) -from openbb_terminal.stocks.options.op_helpers import Option, Options - -logger = logging.getLogger(__name__) - -# pylint:disable=C0302,R0913 - - -@log_start_end(log=logger) -def get_full_option_chain( - symbol: str, source: str = "Nasdaq", expiration: Union[str, None] = None -) -> pd.DataFrame: - """Get Option Chain For A Stock. No greek data is returned - - Parameters - ---------- - symbol : str - Symbol to get chain for - source : str, optional - Source to get data from, by default "Nasdaq". Can be YahooFinance, Tradier, Nasdaq, or Intrinio. - expiration : Union[str, None], optional - Date to get chain for. By default returns all dates - - Returns - ------- - pd.DataFrame - Dataframe of full option chain. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> aapl_option_chain = openbb.stocks.options.chains("AAPL", source = "Nasdaq") - - To get a specific expiration date, use the expiration parameter - - >>> aapl_chain_date = openbb.stocks.options.chains("AAPL", expiration="2023-07-21", source="Nasdaq") - """ - - source = re.sub(r"\s+", "", source.lower()) - df = pd.DataFrame() - if source == "tradier": - df = tradier_model.get_full_option_chain(symbol) - - elif source == "nasdaq": - df = nasdaq_model.get_full_option_chain(symbol) - - elif source == "intrinio": - df = intrinio_model.get_full_option_chain(symbol) - - else: - df = yfinance_model.get_full_option_chain(symbol) - - if not isinstance(df, pd.DataFrame) or df.empty: - logger.info("Invalid Source or Symbol") - console.print("Invalid Source or Symbol") - return pd.DataFrame() - - if expiration: - df = df[df.expiration == expiration] - - return df - - -def get_option_current_price( - symbol: str, - source: str = "Nasdaq", -): - """Get Option current price for a stock. - - Parameters - ---------- - symbol : str - Symbol to get chain for - source : str, optional - Source to get data, by default "Nasdaq". Can be Nasdaq, Tradier, or YahooFinance - - Returns - ------- - float - float of current price - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> aapl_price = openbb.stocks.options.price("AAPL", source="Nasdaq") - """ - - source = re.sub(r"\s+", "", source.lower()) - output = None - if source == "tradier": - output = tradier_model.get_last_price(symbol) - if source == "nasdaq": - output = nasdaq_model.get_last_price(symbol) - if source == "yahoofinance": - output = yfinance_model.get_last_price(symbol) - - if not output: - logger.info("Invalid Source or Symbol") - console.print("Invalid Source or Symbol") - return 0.0 - - return output - - -@log_start_end(log=logger) -def get_option_expirations(symbol: str, source: str = "Nasdaq") -> list: - """Get Option Chain Expirations - - Parameters - ---------- - symbol : str - Symbol to get chain for - source : str, optional - Source to get data from, by default "Nasdaq". Can be Intrinio, Tradier, Nasdaq, or YahooFinance - - Returns - ------- - pd.DataFrame - Dataframe of full option chain. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> SPX_expirations = openbb.stocks.options.expirations("SPX", source = "Tradier") - """ - source = re.sub(r"\s+", "", source.lower()) - output = [] - if source == "tradier": - output = tradier_model.option_expirations(symbol) - if source == "yahoofinance": - output = yfinance_model.option_expirations(symbol) - if source == "nasdaq": - output = nasdaq_model.option_expirations(symbol) - if source == "intrinio": - output = intrinio_model.get_expiration_dates(symbol) - - if not output: - logger.info("Invalid Source or Symbol") - console.print("Invalid Source or Symbol") - return [] - - return output - - -@log_start_end(log=logger) -def hist( - symbol: str, - exp: str, - strike: Union[int, Union[float, str]], - call: bool = True, - source="ChartExchange", -) -> pd.DataFrame: - """Get historical option pricing. - - Parameters - ---------- - symbol : str - Symbol to get data for - exp : str - Expiration date - strike : Union[int ,Union[float,str]] - Strike price - call : bool, optional - Flag to indicate a call, by default True - source : str, optional - Source to get data from, by default "ChartExchange". Can be ChartExchange, Intrinio, or Tradier - - Returns - ------- - pd.DataFrame - DataFrame of historical option pricing - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> aapl_150_call = openbb.stocks.options.hist("AAPL", "2022-11-18", 150, call=True, source="ChartExchange") - - Because this generates a dataframe, we can easily plot the close price for a SPY put: - (Note that Tradier requires an API key) - >>> openbb.stocks.options.hist("SPY", "2022-11-18", 400, call=False, source="Tradier").plot(y="close") - """ - - source = re.sub(r"\s+", "", source.lower()) - output = pd.DataFrame() - if source == "chartexchange": - output = chartexchange_model.get_option_history(symbol, exp, call, strike) - if source == "tradier": - output = tradier_model.get_historical_options(symbol, exp, strike, not call) - if source == "intrinio": - occ_symbol = f"{symbol}{''.join(exp[2:].split('-'))}{'C' if call else 'P'}{str(int(1000*strike)).zfill(8)}" - output = intrinio_model.get_historical_options(occ_symbol) - - if not isinstance(output, pd.DataFrame) or output.empty: - logger.info("No data found for symbol, check symbol and expiration date") - console.print("No data found for symbol, check symbol and expiration date") - return pd.DataFrame() - - return output - - -def get_greeks( - current_price: float, - chain: pd.DataFrame, - expire: str, - div_cont: float = 0, - rf: Optional[float] = None, -) -> pd.DataFrame: - """ - Gets the greeks for a given option - - Parameters - ---------- - current_price: float - The current price of the underlying - chain: pd.DataFrame - The dataframe with option chains - div_cont: float - The dividend continuous rate - expire: str - The date of expiration - rf: float - The risk-free rate - - Returns - ------- - pd.DataFrame - Dataframe with calculated option greeks - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> aapl_chain = openbb.stocks.options.chains("AAPL", source="Tradier") - >>> aapl_last_price = openbb.stocks.options.last_price("AAPL") - >>> greeks = openbb.stocks.options.greeks(aapl_last_price, aapl_chain, aapl_chain.iloc[0, 2]) - """ - - chain = chain.rename(columns={"iv": "impliedVolatility"}) - chain_columns = chain.columns.tolist() - if not all( - col in chain_columns for col in ["strike", "impliedVolatility", "optionType"] - ): - if "delta" not in chain_columns: - console.print( - "[red]It's not possible to calculate the greeks without the following " - "columns: `strike`, `impliedVolatility`, `optionType`.\n[/red]" - ) - return pd.DataFrame() - - risk_free = rf if rf is not None else get_rf() - expire_dt = datetime.strptime(expire, "%Y-%m-%d") - dif = (expire_dt - datetime.now() + timedelta(hours=16)).total_seconds() / ( - 60 * 60 * 24 - ) - strikes = [] - for _, row in chain.iterrows(): - vol = row["impliedVolatility"] - is_call = row["optionType"] == "call" - try: - opt = Option( - current_price, row["strike"], risk_free, div_cont, dif, vol, is_call - ) - tmp = [ - opt.Delta(), - opt.Gamma(), - opt.Vega(), - opt.Theta(), - opt.Rho(), - opt.Phi(), - opt.Charm(), - opt.Vanna(0.01), - opt.Vomma(0.01), - ] - except ValueError: - tmp = [np.nan] * 9 - result = [row[col] for col in row.index.tolist()] - result += tmp - - strikes.append(result) - - greek_columns = [ - "Delta", - "Gamma", - "Vega", - "Theta", - "Rho", - "Phi", - "Charm", - "Vanna", - "Vomma", - ] - columns = chain_columns + greek_columns - - df = pd.DataFrame(strikes, columns=columns) - - return df - - -class OptionsChains(Options): # pylint: disable=too-few-public-methods - """OptionsChains class for loading and interacting with the Options data object. - - Parameters - ---------- - symbol: str - The ticker symbol to load the data for. - source: str - The source for the data. Defaults to "CBOE". ["CBOE", "Intrinio", "Nasdaq", "TMX", "Tradier", "YahooFinance"] - date: str - The date for EOD chains data. Only available for "Intrinio" and "TMX". - pydantic: bool - Whether to return as a Pydantic Model or as a Pandas object. Defaults to False. - - Returns - ------- - OptionsChains - chains: pd.DataFrame - The complete options chain for the ticker. Returns as a dictionary if pydantic is True. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. Returns as a dictionary if pydantic is True. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data. - date: str - The date, when the chains data is historical EOD. - SYMBOLS: pd.DataFrame - The symbol directory for the source, when available. Returns as a dictionary if pydantic is True. - - Methods - --------- - chart_skew: Callable - Function to chart the implied volatility skew. - chart_stats: Callable - Function to chart a variety of volume and open interest statistics. - chart_surface: Callable - Function to chart the volatility as a 3-D surface. - chart_volatility: Callable - Function to chart the implied volatility smile. - get_skew: Callable - Function to calculate horizontal and vertical skewness. - get_stats: Callable - Function to return a table of summary statistics, by strike or by expiration. - get_straddle: Callable - Function to calculate straddles and the payoff profile. - get_strangle: Callable - Function to calculate strangles and the payoff profile. - get_synthetic_long: Callable - Function to calculate a synthetic long position. - get_synthetic_short: Callable - Function to calculate a synthetic short position. - get_vertical_call_spread: Callable - Function to calculate vertical call spreads. - get_vertical_put_spreads: Callable - Function to calculate vertical put spreads. - get_strategies: Callable - Function for calculating multiple straddles and strangles at different expirations and moneyness. - - Examples - ---------- - >>> from openbb_terminal.stocks.options.options_sdk_helper import OptionsChains - >>> spy = OptionsChains("SPY") - >>> spy.__dict__ - - >>> spy.chains.query("`expiration` == spy.expirations[1]") - - >>> xiu = OptionsChains("xiu.to", "TMX") - >>> xiu.get_straddle() - """ - - def __init__(self, symbol, source="CBOE", date="", pydantic=False): - try: - options = options_chains_model.load_options_chains( - symbol, source, date, pydantic - ) - items = list(options.__dict__.keys()) - for item in items: - setattr(self, item, options.__dict__[item]) - if hasattr(self, "date") is False: - setattr(self, "date", "") - except Exception: - self.chains = pd.DataFrame() - - def __repr__(self) -> str: - return f"OptionsChains(symbol={self.symbol}, source={self.source})" - - def get_stats(self, by="expiration", query=None): - """Calculates basic statistics for the options chains, like OI and Vol/OI ratios. - - Parameters - ---------- - by: str - Whether to calculate by strike or expiration. Default is expiration. - query: DataFrame - Entry point to perform DataFrame operations on self.chains at the input stage. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the calculated statistics. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("SPY") - - By expiration date: - >>> data.calculate_stats() - - By strike: - >>> data.calculate_stats(data, "strike") - - Query: - >>> data = OptionsChains("XIU", "TMX") - >>> data.get_stats(query = data.chains.query("`openInterest` > 0"), by = "strike") - """ - - if query is not None: - if isinstance(query, pd.DataFrame): - return options_chains_model.calculate_stats(query, by) - console.print("Query must be passed a Pandas DataFrame with chains data.") - - return options_chains_model.calculate_stats(self, by) - - def get_straddle(self, days=0, strike=0): - """Calculates the cost of a straddle and its payoff profile. Use a negative strike price for short options. - - Parameters - ----------- - days: int - The target number of days until expiry. Default is 30 days. - strike: float - The target strike price. Enter a negative value for short options. - Default is the last price of the underlying stock. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike1 is the nearest call strike, strike2 is the nearest put strike. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains('SPY') - >>> data.get_straddle() - """ - - return options_chains_model.calculate_straddle(self, days, strike) - - def get_strangle(self, days=0, moneyness=0): - """Calculates the cost of a strangle and its payoff profile. - Use a negative value for moneyness for short options. - - Parameters - ----------- - days: int - The target number of days until expiry. Default is 30 days. - moneyness: float - The percentage of OTM moneyness, expressed as a percent between -100 < 0 < 100. - Enter a negative number for short options. - Default is 5. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. - Strike 1 is the nearest call strike, and strike 2 is the nearest put strike. - - Examples - -------- - >>> data = openbb.stocks.options.load_options_chains("SPY") - >>> data.get_strangle() - """ - - return options_chains_model.calculate_strangle(self, days, moneyness) - - def get_synthetic_long( - self, - days: Optional[int] = 30, - strike: float = 0, - ) -> pd.DataFrame: - """Calculates the cost of a synthetic long position at a given strike. - It is expressed as the difference between a bought call and a sold put. - Requires the Options data object. - - Parameters - ----------- - days: int - The target number of days until expiry. Default is 30 days. - strike: float - The target strike price. Default is the last price of the underlying stock. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike1 is the purchased call strike, strike2 is the sold put strike. - - Example - -------- - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains('SPY') - >>> data.get_synthetic_long() - """ - return options_chains_model.calculate_synthetic_long(self, days, strike) - - def get_synthetic_short( - self, - days: Optional[int] = 30, - strike: float = 0, - ) -> pd.DataFrame: - """Calculates the cost of a synthetic short position at a given strike. - It is expressed as the difference between a sold call and a purchased put. - Requires the Options data object. - - Parameters - ----------- - days: int - The target number of days until expiry. Default is 30 days. - strike: float - The target strike price. Default is the last price of the underlying stock. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike1 is the sold call strike, strike2 is the purchased put strike. - - Example - -------- - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains('SPY') - >>> data.get_synthetic_short() - """ - return options_chains_model.calculate_synthetic_short(self, days, strike) - - def get_vertical_call_spread(self, days=0, sold_strike=0, bought_strike=0): - """Calculates the vertical call spread for the target DTE. - A bull call spread is when the sold strike is above the bought strike. - - Parameters - ---------- - days: int - The target number of days until expiry. This value will be used to get the nearest valid DTE. - Default is 30 days. - sold_strike: float - The target strike price for the short leg of the vertical call spread. - Default is the 5% OTM above the last price of the underlying. - bought_strike: float - The target strike price for the long leg of the vertical call spread. - Default is the last price of the underlying. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike 1 is the sold strike, and strike 2 is the bought strike. - - Examples - -------- - Load the data: - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("SPY") - - For a bull call spread: - >>> data.get_vertical_call_spread(days=10, sold_strike=355, bought_strike=350) - - For a bear call spread: - >>> data.get_vertical_call_spread(days=10, sold_strike=350, bought_strike=355) - """ - - return options_chains_model.calculate_vertical_call_spread( - self, days, sold_strike, bought_strike - ) - - def get_vertical_put_spread(self, days=0, sold_strike=0, bought_strike=0): - """Calculates the vertical put spread for the target DTE. - A bear put spread is when the bought strike is above the sold strike. - - Parameters - ---------- - days: int - The target number of days until expiry. This value will be used to get the nearest valid DTE. - Default is 30 days. - sold_strike: float - The target strike price for the short leg of the vertical put spread. - Default is the last price of the underlying. - bought_strike: float - The target strike price for the long leg of the vertical put spread. - Default is the 5% OTM above the last price of the underlying. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. Strike 1 is the sold strike, strike 2 is the bought strike. - - Examples - -------- - Load the data: - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("QQQ") - - For a bull put spread: - >>> data.get_vertical_put_spread(days=10, sold_strike=355, bought_strike=350) - - For a bear put spread: - >>> data.get_vertical_put_spread(days=10, sold_strike=355, bought_strike=350) - """ - - return options_chains_model.calculate_vertical_put_spread( - self, days, sold_strike, bought_strike - ) - - def get_strategies( - self, - days: Optional[list[int]] = None, - straddle_strike: Optional[float] = None, - strangle_moneyness: Optional[float] = None, - synthetic_longs: Optional[list[float]] = None, - synthetic_shorts: Optional[list[float]] = None, - vertical_calls: Optional[list[float]] = None, - vertical_puts: Optional[list[float]] = None, - ): - """Gets options strategies for all, or a list of, DTE(s). - Currently supports straddles, strangles, and vertical spreads. - Multiple strategies, expirations, and % moneyness can be returned. - A negative value for `straddle_strike` or `strangle_moneyness` returns short options. - A synthetic long/short position is a bought/sold call and sold/bought put at the same strike. - A sold call strike that is lower than the bought strike, - or a sold put strike that is higher than the bought strike, - is a bearish vertical spread. - - Parameters - ---------- - days: list[int] - List of DTE(s) to get strategies for. Enter a single value, or multiple as a list. Defaults to all. - strike_price: float - The target strike price. Defaults to the last price of the underlying stock. - strangle_moneyness: list[float] - List of OTM moneyness to target, expressed as a percent value between 0 and 100. - Enter a single value, or multiple as a list. Defaults to 5. - synthetic_long: list[float] - List of strikes for a synthetic long position. - synthetic_short: list[float] - List of strikes for a synthetic short position. - vertical_calls: list[float] - Call strikes for vertical spreads, listed as [sold strike, bought strike]. - vertical_puts: list[float] - Put strikes for vertical spreads, listed as [sold strike, bought strike]. - - Returns - ------- - pd.DataFrame - Pandas DataFrame with the results. - - Examples - -------- - Load data - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("SPY") - - Return just long straddles for every expiry. - >>> data.get_strategies() - - Return strangles for every expiry. - >>> data.get_strategies(strangle_moneyness = [2.5,5,10]) - - Return multiple values for both moneness and days: - >>> data.get_strategies(days = [10,30,60,90], moneyness = [2.5,-5,10,-20]) - - Return vertical spreads for all expirations. - >>> data.get_strategies(vertical_calls=[430,427], vertical_puts=[420,426]) - - Return synthetic short and long positions: - >>> data.get_strategies(days = [30,60], synthetic_short = [450,445], synthetic_long = [450,455]) - """ - - if strangle_moneyness is None: - strangle_moneyness = 0 - - return options_chains_model.get_strategies( - self, - days, - straddle_strike, - strangle_moneyness, - synthetic_longs, - synthetic_shorts, - vertical_calls, - vertical_puts, - ) - - def get_skew( - self, expiration: Optional[str] = "", moneyness: Optional[float] = None - ): - """ - Returns the skewness of the options, either vertical or horizontal. - - The vertical skew for each expiry and option is calculated by subtracting the IV of the ATM call or put. - Returns only where the IV is greater than 0. - - Horizontal skew is returned if a value for moneyness is supplied. - It is expressed as the difference between skews of two equidistant OTM strikes (the closest call and put). - - Parameters - ----------- - expiration: str - The expiration date to target. Defaults to all. - moneyness: float - The moneyness to target for calculating horizontal skew. This parameter overrides a defined expiration date. - - Returns - -------- - pd.DataFrame - Pandas DataFrame with the results. - - Examples - ---------- - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("SPY") - - Vertical skew at a given expiry: - >>> skew = data.get_skew("2025-12-19") - - Vertical skew at all expirations: - >>> skew = data.get_skew() - - Horizontal skew at a given % OTM: - >>> skew = data.get_skew(moneyness = 10) - """ - return options_chains_model.calculate_skew(self, expiration, moneyness) - - def chart_stats( - self, - by: str = "expiration", - expiry: str = "", - oi: Optional[bool] = False, - percent: Optional[bool] = True, - ratios: Optional[bool] = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, - ) -> Union[None, OpenBBFigure]: - """Chart a variety of volume and open interest statistics. - - Parameters - ----------- - by: str - Statistics can be displayed by either "expiration" or "strike". Default is "expiration". - expiry: str - The target expiration date to display. Only valid when `percent` is False. - oi: bool - Display open interest if True, else volume. Default is False. - percent: bool - Displays volume or open interest as a percentage of the total across all expirations. Default is False. - ratios: bool - Displays Put/Call ratios. This parameter overrides the others when True. Default is False. - raw: bool - Displays the raw data table instead of a chart. - export: str - Export the data to a csv,json,xlsx file. - sheet_name: str - Name of the sheet to save the data to. Only valid when `export` is a `xlsx` file. - external_axes: bool - Return the OpenBB Figure Object to a variable. - - Examples - ---------- - >>> from openbb_terminal.sdk import openbb - >>> spy = options_chains_model.load_options_chains("SPY") - - Display volume by expiration: - >>> spy.chart_stats() - - Display volume by strike: - >>> spy.chart_stats("strike") - - Display open interest by expiration: - >>> spy.chart_stats(oi=True) - - Display open interest, by expiration, as a percentage of the total: - >>> spy.chart_stats(oi=True, percent=True) - - Display volume and open interest put/call ratios: - >>> spy.chart_stats(ratios=True) - """ - return options_chains_view.display_stats( - self, - by, - expiry, - oi, - percent, - ratios, - raw, - export, - sheet_name, - external_axes, - ) - - def chart_surface( - self, - option_type: str = "otm", - dte_range: Optional[list[int]] = None, - strike_range: Optional[list[float]] = None, - moneyness: Optional[float] = None, - oi: bool = False, - volume: bool = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, - ) -> Union[None, OpenBBFigure]: - """Chart the volatility as a 3-D surface. - - Parameters - ----------- - option_type: str - The type of data to display. Default is "otm". - Choices are: ["otm", "itm", "puts", "calls"] - dte_range: list[int] - Specify a min/max range of DTE to display. - moneyness: float - Specify a % moneyness to target for display. - strike_range: list[float] - Specify a min/max range of strike prices to display. - oi: bool - Filter for only options that have open interest. Default is False. - volume: bool - Filter for only options that have trading volume. Default is False. - raw: bool - Display the raw data instead of the chart. - export: str - Export dataframe data to csv,json,xlsx file. - external_axes: bool - Return the OpenBB Figure Object to a variable. - - Examples - ---------- - >>> from openbb_terminal.sdk import openbb - >>> spy = openbb.stocks.options.load_options_chains("SPY") - >>> spy.chart_surface() - - Display only calls: - >>> spy.chart_surface("calls") - - Display only puts: - >>> spy.chart_surface("puts") - - Display a range of expirations: - >>> spy.chart_surface(dte_range=[7, 60]) - - Filter for a range of strike prices and include only those with open interest and trading volume: - >>> spy.chart_surface(strike_range=[400, 500], oi=True, volume=True) - """ - return options_chains_view.display_surface( - self, - option_type, - dte_range, - moneyness, - strike_range, - oi, - volume, - raw, - export, - sheet_name, - external_axes, - ) - - def chart_skew( - self, - expirations: Optional[list[str]] = None, - moneyness: Optional[float] = None, - strike: Optional[float] = None, - atm: Optional[bool] = False, - otm_only: Optional[bool] = False, - raw: Optional[bool] = False, - export: Optional[str] = "", - sheet_name: Optional[str] = "", - external_axes: Optional[bool] = False, - ) -> Union[None, OpenBBFigure]: - """Chart the vertical skew of an option expiration, or the horizontal skew of equidistant % moneyness options. - - The vertical skew for each expiry and option is calculated by subtracting the IV of the ATM call or put. - Returns only where the IV is greater than 0. - - Horizontal skew is returned if a value for moneyness, or a strike price, is supplied. - With a strike price specified, both call and put IV skew are displayed. For moneyness, - it is expressed as the difference between skews of two equidistant OTM strikes (the closest call and put). - - Parameters - ----------- - expirations: list[str] - The expiration date, or a list of dates. The closest date will be returned for each entry. - Format as YYYY-MM-DD. - moneyness: float - The % moneyess. When specified, this returns the forward skew curve at the target moneyness. - strike: float - A target strike price to observe the skew vs. contract. This argument overrides other parameters. - atm: bool - When true, returns the ATM skew curve. This will override other parameters, but is overridden by strike. - otm_only: bool - When true, returns only OTM portions of the put/call skew curves. - raw: bool - Returns a table instead of a plot. - export: str - Export the data to csv, json, xlsx file. - sheet_name: str - The name of the sheet to save the data to. Only valid when `export` is a `xlsx` file. - external_axes: bool - Returns the OpenBB Figure Object to a variable. - - Examples - ---------- - >>> from openbb_terminal.sdk import openbb - >>> spy = openbb.stocks.options.load_options_chains("SPY") - - >>> spy.chart_skew() - - Display only OTM IV: - >>> spy.chart_skew(otm_only=True) - - Display more than one chain: - >>> spy.chart_skew(expirations=spy.expirations[1:5]) - - When otm_only is True, and there is more than one expiration, calls and puts share a colour. - >>> spy.chart_skew(expirations=spy.expirations[1:11], otm_only=True) - """ - return options_chains_view.display_skew( - self, - expirations, - moneyness, - strike, - atm, - otm_only, - raw, - export, - sheet_name, - external_axes, - ) - - def chart_volatility( - self, - expirations: Optional[list[str]] = None, - moneyness: Optional[float] = None, - strike: Optional[float] = None, - oi: bool = False, - volume: bool = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = "", - external_axes: bool = False, - ) -> Union[None, OpenBBFigure]: - """Chart the implied volatility smile. - - Parameters - ----------- - expirations: list[str] - Select up to five expiration(s) to display. Overridden by moneyness or strike. Format as YYYY-MM-DD. - moneyness: float - Specify a target % moneyness to display vs. contract dates. This argument overrides expirations. - strike: float - Specify a target strike price to display vs. contract dates. - Returned strike price is estimated as the closest one listed at approximately one year forward. - This argument overrides moneyness and expirations. - oi: bool - Return only contracts with open interest. Only valid for IV vs. Strike charts. Default is False. - volume: bool - Return only contracts with trading volume. Only valid for IV vs. Strike charts. Default is False. - raw: bool - Display the raw data instead of the chart. - export: str - Export dataframe data to csv,json,xlsx file. - external_axes: bool - Return the OpenBB Figure Object to a variable. - - Examples - ---------- - >>> from openbb_terminal.sdk import openbb - >>> spy = openbb.stocks.options.load_options_chains("SPY") - >>> spy.chart_volatility() - - Plot IV @ Strike vs. contract dates: - >>> spy.chart_volatility(strike=450) - - Plot IV @ % OTM vs. contract dates: - >>> spy.chart_volatility(moneyness=20) - - Plot multiple expirations at once, and filter for only contracts with open interest: - >>> spy.chart_volatility(expirations=["2024-12-30", "2025-12-30"], oi=True) - """ - return options_chains_view.display_volatility( - self, - expirations, - moneyness, - strike, - oi, - volume, - raw, - export, - sheet_name, - external_axes, - ) - - -@log_start_end(log=logger) -def load_options_chains( - symbol: str, source: str = "CBOE", date: str = "", pydantic: bool = False -) -> OptionsChains: - """Loads all options chains from a specific source, fields returned to each attribute will vary. - - Parameters - ----------- - symbol: str - The ticker symbol to load the data for. - source: str - The source for the data. Defaults to "CBOE". ["CBOE", "Intrinio", "Nasdaq", "TMX", "Tradier", "YahooFinance"] - date: str - The date for EOD chains data. Only available for "Intrinio" and "TMX". - pydantic: bool - Whether to return as a Pydantic Model or as a Pandas object. Defaults to False. - - Returns - ------ - Class: OptionsChains - chains: pd.DataFrame - The complete options chain for the ticker. Returns as a dictionary if pydantic is True. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. Returns as a dictionary if pydantic is True. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data. - date: str - The date, when the chains data is historical EOD. - SYMBOLS: pd.DataFrame - The symbol directory for the source, when available. Returns as a dictionary if pydantic is True. - - Methods - ------ - chart_skew: Callable - Function to chart the implied volatility skew. - chart_stats: Callable - Function to chart a variety of volume and open interest statistics. - chart_surface: Callable - Function to chart the volatility as a 3-D surface. - chart_volatility: Callable - Function to chart the implied volatility smile. - get_skew: Callable - Function to calculate horizontal and vertical skewness. - get_stats: Callable - Function to return a table of summary statistics, by strike or by expiration. - get_straddle: Callable - Function to calculate straddles and the payoff profile. - get_strangle: Callable - Function to calculate strangles and the payoff profile. - get_synthetic_long: Callable - Function to calculate a synthetic long position. - get_synthetic_short: Callable - Function to calculate a synthetic short position. - get_vertical_call_spread: Callable - Function to calculate vertical call spreads. - get_vertical_put_spreads: Callable - Function to calculate vertical put spreads. - get_strategies: Callable - Function for calculating multiple straddles and strangles at different expirations and moneyness. - - Examples - ------ - Loads SPY data from CBOE, returns as a Pydantic Model, and displays the longest-dated expiration chain. - - >>> from openbb_terminal.sdk import openbb - >>> import pandas as pd - >>> data = openbb.stocks.options.load_options_chains("SPY", pydantic = True) - >>> chains = pd.DataFrame(data.chains) - >>> chains[chains["expiration"] == data.expirations[-1]] - - Loads QQQ data from Tradier as a Pydantic Model. - - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("QQQ", source = "Tradier", pydantic = True) - - Loads VIX data from YahooFinance as a Pandas object. - - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("^VIX", source = "YahooFinance") - - Loads XIU data from TMX and displays the 25 highest open interest options. - - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("XIU", "TMX") - >>> data.chains.sort_values("openInterest", ascending=False).head(25) - - Loads the EOD chains data for XIU.TO from March 15, 2020, sorted by number of transactions. - - >>> from openbb_terminal.sdk import openbb - >>> data = openbb.stocks.options.load_options_chains("XIU.TO", "TMX", "2020-03-15") - >>> data.chains.sort_values("transactions", ascending=False).head(25) - """ - return OptionsChains(symbol, source, date, pydantic) diff --git a/openbb_terminal/stocks/options/options_view.py b/openbb_terminal/stocks/options/options_view.py deleted file mode 100644 index a69149203c3..00000000000 --- a/openbb_terminal/stocks/options/options_view.py +++ /dev/null @@ -1,507 +0,0 @@ -# IMPORTATION STANDARD -import logging -import os - -# IMPORTATION THIRDPARTY -from typing import Optional, Tuple - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.core.plots.backend import plots_backend - -# IMPORTATION INTERNAL -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options.op_helpers import ( - calculate_max_pain, - get_greeks, - get_strikes, -) - -logger = logging.getLogger(__name__) - -# pylint: disable=C0302,R0913 - - -def get_calls_and_puts(chain: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame]: - calls = chain[chain["optionType"] == "call"] - puts = chain[chain["optionType"] == "put"] - return calls, puts - - -def get_max_pain(calls: pd.DataFrame, puts: pd.DataFrame) -> float: - call_oi = calls.set_index("strike")["openInterest"] - put_oi = puts.set_index("strike")["openInterest"] - df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True) - df_opt = df_opt.rename( - columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"} - ) - return calculate_max_pain(df_opt) - - -def print_raw( - calls: pd.DataFrame, - puts: pd.DataFrame, - title: str, - calls_only: bool = False, - puts_only: bool = False, - export: str = "", -): - current_user = get_current_user() - enable_interactive = ( - current_user.preferences.USE_INTERACTIVE_DF and plots_backend().isatty - ) - - if enable_interactive: - table_title = f"{title} - Calls & Puts" - data = pd.concat([calls, puts]) - - if calls_only or puts_only: - options_type = "call" if calls_only else "put" - data = data[data["optionType"] == options_type].drop(columns=["optionType"]) - table_title = f"{title} - {options_type.title()}s" - - return print_rich_table( - data, - headers=list(data.columns), - show_index=False, - title=table_title, - export=bool(export), - floatfmt=".6f", - ) - - for opt_type, only in zip(["call", "put"], [puts_only, calls_only]): - if not only: - data = (calls if opt_type == "call" else puts).drop(columns=["optionType"]) - print_rich_table( - data, - headers=list(data.columns), - show_index=False, - title=f"{title} - {opt_type.title()}s", - export=bool(export), - floatfmt=".4f", - ) - - return None - - -@log_start_end(log=logger) -def plot_vol( - chain: pd.DataFrame, - current_price: float, - symbol: str, - expiry: str, - min_sp: float = -1, - max_sp: float = -1, - calls_only: bool = False, - puts_only: bool = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Plot volume - - Parameters - ---------- - chain: pd.Dataframe - Dataframe with options chain - current_price: float - Current price of selected symbol - symbol: str - Ticker symbol - expiry: str - expiration date for options - min_sp: float - Min strike to consider - max_sp: float - Max strike to consider - calls_only: bool - Show calls only - puts_only: bool - Show puts only - export: str - Format to export file - sheet_name: str - Optionally specify the name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> aapl_chain_data = openbb.stocks.options.chains("AAPL", expiration="2023-07-21", source="Nasdaq") - >>> aapl_price = openbb.stocks.options.price("AAPL", source="Nasdaq") - >>> openbb.stocks.options.vol( - chain=aapl_chain_data, - symbol="AAPL", - current_price=aapl_price, - expiry="2023-07-21", - ) - """ - calls, puts = get_calls_and_puts(chain) - - min_strike, max_strike = get_strikes(min_sp, max_sp, chain) - title = f"Volume for {symbol.upper()} expiring {expiry}" - - if raw: - print_raw(calls, puts, title, calls_only, puts_only, export=export) - - fig = OpenBBFigure( - xaxis=dict(title="Strike Price", range=[min_strike, max_strike]), - yaxis_title="Volume", - ) - fig.set_title(title) - - if not puts_only: - call_v = calls.set_index("strike")["volume"] - fig.add_scatter( - x=call_v.index, y=call_v.values, mode="lines+markers", name="Calls" - ) - if not calls_only: - put_v = puts.set_index("strike")["volume"] - fig.add_scatter( - x=put_v.index, y=put_v.values, mode="lines+markers", name="Puts" - ) - - fig.add_vline(x=current_price, line_dash="dash", line_width=2, name="Current Price") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"vol_{symbol}_{expiry}", - chain, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_oi( - chain: pd.DataFrame, - current_price: float, - symbol: str, - expiry: str, - min_sp: float = -1, - max_sp: float = -1, - calls_only: bool = False, - puts_only: bool = False, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Plot open interest - - Parameters - ---------- - chain: pd.Dataframe - Dataframe with options chain - current_price: float - Current price of selected symbol - symbol: str - Ticker symbol - expiry: str - expiration date for options - min_sp: float - Min strike to consider - max_sp: float - Max strike to consider - calls_only: bool - Show calls only - puts_only: bool - Show puts only - export: str - Format to export file - sheet_name: str - Optionally specify the name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> aapl_chain_data = openbb.stocks.options.chains("AAPL", expiration="2023-07-21", source="Nasdaq") - >>> aapl_price = openbb.stocks.options.price("AAPL", source="Nasdaq") - >>> openbb.stocks.options.oi( - chain=aapl_chain_data, - symbol="AAPL", - current_price=aapl_price, - expiry="2023-07-21", - ) - """ - calls, puts = get_calls_and_puts(chain) - - min_strike, max_strike = get_strikes(min_sp, max_sp, chain) - max_pain = get_max_pain(calls, puts) - title = f"Open Interest for {symbol.upper()} expiring {expiry}" - - if raw: - print_raw(calls, puts, title, calls_only, puts_only, export=export) - - fig = OpenBBFigure( - xaxis=dict(title="Strike Price", range=[min_strike, max_strike]), - yaxis_title="Open Interest", - ) - fig.set_title(title) - - if not puts_only: - call_oi = calls.set_index("strike")["openInterest"] - fig.add_scatter( - x=call_oi.index, y=call_oi.values, mode="lines+markers", name="Calls" - ) - if not calls_only: - put_oi = puts.set_index("strike")["openInterest"] - fig.add_scatter( - x=put_oi.index, y=put_oi.values, mode="lines+markers", name="Puts" - ) - - fig.add_vline_legend( - x=current_price, - name="Current Price", - line=dict(dash="dash", width=2, color="grey"), - ) - fig.add_vline_legend( - x=max_pain, - name=f"Max Pain: {max_pain}", - line=dict(dash="dash", width=2, color="white"), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"oi_{symbol}_{expiry}", - chain, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def plot_voi( - chain: pd.DataFrame, - current_price: float, - symbol: str, - expiry: str, - min_sp: float = -1, - max_sp: float = -1, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -): - """Plot volume and open interest - - Parameters - ---------- - chain: pd.Dataframe - Dataframe with options chain - current_price: float - Current price of selected symbol - symbol: str - Stock ticker symbol - expiry: str - Option expiration - min_sp: float - Min strike price - max_sp: float - Max strike price - export: str - Format for exporting data - sheet_name: str - Optionally specify the name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> aapl_chain_data = openbb.stocks.options.chains("AAPL", expiration="2023-07-21", source="Nasdaq") - >>> aapl_price = openbb.stocks.options.price("AAPL", source="Nasdaq") - >>> openbb.stocks.options.voi( - chain=aapl_chain_data, - symbol="AAPL", - current_price=aapl_price, - expiry="2023-07-21", - ) - """ - get_current_user() - calls, puts = get_calls_and_puts(chain) - - min_strike, max_strike = get_strikes(min_sp, max_sp, chain) - max_pain = get_max_pain(calls, puts) - title = f"Volume and Open Interest for {symbol.upper()} expiring {expiry}" - - option_chain = pd.merge( - calls[["volume", "strike", "openInterest"]], - puts[["volume", "strike", "openInterest"]], - on="strike", - ) - - option_chain = option_chain.rename( - columns={ - "volume_x": "volume_call", - "volume_y": "volume_put", - "openInterest_x": "openInterest_call", - "openInterest_y": "openInterest_put", - } - ) - - option_chain[["openInterest_put", "volume_put"]] = ( - option_chain[["openInterest_put", "volume_put"]] * -1 - ) - option_chain[["openInterest_call", "volume_call"]] = option_chain[ - ["openInterest_call", "volume_call"] - ] - - fig = OpenBBFigure(yaxis_title="Volume", xaxis_title="Strike Price") - fig.set_title(title) - - option_chain = option_chain[ - (option_chain.strike >= min_strike) & (option_chain.strike <= max_strike) - ] - fig.add_bar( - x=option_chain.strike, - y=option_chain.openInterest_call, - name="Calls: Open Interest", - marker_color="lightgreen", - hovertemplate="Calls Open Interest: %{y}", - ) - fig.add_bar( - x=option_chain.strike, - y=option_chain.volume_call, - name="Calls: Volume", - marker_color="green", - hovertemplate="Calls Volume: %{y}", - ) - fig.add_bar( - x=option_chain.strike, - y=option_chain.openInterest_put, - name="Puts: Open Interest", - marker_color="pink", - hovertemplate="Puts Open Interest: %{y}", - ) - fig.add_bar( - x=option_chain.strike, - y=option_chain.volume_put, - name="Puts: Volume", - marker_color="red", - hovertemplate="Puts Volume: %{y}", - ) - fig.add_vline_legend( - x=current_price, - name="Current stock price", - line=dict(dash="dash", width=2, color="white"), - ) - fig.add_vline_legend( - x=max_pain, - name=f"Max pain = {max_pain}", - line=dict(dash="dash", width=2, color="red"), - ) - - fig.update_layout(barmode="relative", hovermode="x unified") - - if raw: - print_raw(calls, puts, title, export=export) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"voi_{symbol}_{expiry}", - chain, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_expiry_dates(expiry_dates: list): - """Display expiry dates - - Parameters - ---------- - expiry_dates: list - The expiry dates of the chosen ticker. - """ - expiry_dates_df = pd.DataFrame(expiry_dates, columns=["Date"]) - - print_rich_table( - expiry_dates_df, - headers=list(expiry_dates_df.columns), - title="Available expiry dates", - show_index=True, - index_name="Identifier", - ) - - -@log_start_end(log=logger) -def display_chains( - chain: pd.DataFrame, - expire: str, - current_price: float = 0, - calls_only: bool = False, - puts_only: bool = False, - min_sp: float = -1, - max_sp: float = -1, - export: str = "", - sheet_name: Optional[str] = None, -): - """Display chains - - chain: pd.Dataframe - Dataframe with options chain - current_price: float - Current price of selected symbol - expire: str - The date of expiration - min_sp: float - Min strike price - max_sp: float - Max strike price - calls_only: bool - Show calls only - puts_only: bool - Show puts only - export: str - Format for exporting data - sheet_name: str - Optionally specify the name of the sheet to export to - """ - min_strike, max_strike = get_strikes(min_sp, max_sp, chain) - - chain = chain[chain["strike"] >= min_strike] - chain = chain[chain["strike"] <= max_strike] - - calls, puts = get_calls_and_puts(chain) - # Tradier provides the greeks, so calculate them if they are not present - if "delta" not in chain.columns: - if "impliedVolatility" in chain.columns: - chain = get_greeks( - current_price=current_price, - calls=calls, - expire=expire, - puts=puts, - show_all=True, - ) - # if the greeks calculation went with no problems, otherwise keep the previous - if not chain.empty: - calls, puts = get_calls_and_puts(chain) - console.print("Greeks calculated by OpenBB.") - else: - console.print("Greeks currently not supported without IV.") - - print_raw(calls, puts, "Option chain", calls_only, puts_only, export=export) - - export_data( - export, os.path.dirname(os.path.abspath(__file__)), "chain", chain, sheet_name - ) diff --git a/openbb_terminal/stocks/options/tmx_model.py b/openbb_terminal/stocks/options/tmx_model.py deleted file mode 100644 index d28a2a12778..00000000000 --- a/openbb_terminal/stocks/options/tmx_model.py +++ /dev/null @@ -1,595 +0,0 @@ -# mypy: disable-error-code=attr-defined - -"""Model for retrieving public options data from the Montreal OptionsChains Exchange.""" - -from datetime import datetime, timedelta - -import pandas as pd -import pandas_market_calendars as mcal -from pandas.tseries.holiday import next_workday - -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options.op_helpers import Options, PydanticOptions - -__docformat__ = "numpy" - -cal = mcal.get_calendar(name="TSX") -holidays = list(cal.regular_holidays.holidays().strftime("%Y-%m-%d")) - - -def check_weekday(date) -> str: - """Helper function to check if the input date is a weekday, and if not, returns the next weekday. - - Parameters - ---------- - date: str - The date to check in YYYY-MM-DD format. - - Returns - ------- - str - Date in YYYY-MM-DD format. If the date is a weekend, returns the date of the next weekday. - - Examples - -------- - >>> from openbb_terminal.stocks.options import tmx_model - >>> tmx_model.check_weekday("2023-05-20") - >>> tmx_model.check_weekday("2023-05-22") - """ - - if pd.to_datetime(date).weekday() not in range(0, 5): - date_ = next_workday(pd.to_datetime(date)).strftime("%Y-%m-%d") - date = date_ - return date - - -def get_all_ticker_symbols() -> pd.DataFrame: - """Returns a DataFrame with all valid ticker symbols.""" - - r = request("https://www.m-x.ca/en/trading/data/options-list") - if r.status_code == 200: - options_listings = pd.read_html(r.text) - listings = pd.concat(options_listings) - listings = listings.set_index("Option Symbol").drop_duplicates().sort_index() - symbols = listings[:-1] - symbols = symbols.fillna(value="") - symbols["Underlying Symbol"] = ( - symbols["Underlying Symbol"].str.replace(" u", ".UN").str.replace("––", "") - ) - return symbols - return pd.DataFrame() - - -SYMBOLS = get_all_ticker_symbols() - - -def get_underlying_price(symbol: str) -> pd.Series: - """Gets the current price and performance of the underlying asset from the Canadian Securities Exchange. - - Note: Volume is CSE only and is not a consolidated value. - - Parameters - ---------- - symbol: str - The ticker symbol of the underlying asset. - - Returns - ------- - pd.Series - Series of the current price and performance of the underlying asset. - """ - - data = pd.Series(dtype=object) - symbol = symbol.upper() - URL = f"https://webapi.thecse.com/trading/other/securities/{symbol}.json" - - response = request(URL) - - if response.status_code != 200: - console.print("No price data found for the underlying security, " f"{symbol}") - return data - - data = response.json()["ticker"] - data = ( - pd.Series(data) - .rename(f"{symbol}") - .drop(["Composite Index Contribution", "CSE25 Index Contribution"]) - ) - data.rename( - { - "Previous Closing Price": "previousClose", - "Trade Count": "transactions", - "Trading Volume": "volume", - "Trading Value": "value", - "Trading Value (CAD)": "valueCAD", - "Opening Price": "open", - "Last Price": "price", - "Net Change": "change", - "Net Change Percentage": "changePercent", - "Days Low Price": "low", - "Days High Price": "high", - "Days VWAP": "vwap", - "Last Tick": "tick", - "52 Week High": "fiftyTwoWeekHigh", - "52 Week Low": "fiftyTwoWeekLow", - "Time": "time", - }, - inplace=True, - ) - return data - - -def check_symbol(symbol: str) -> bool: - """Checks if the symbol is valid. This function is used as an internal helper. - - Parameters - ---------- - symbol: str - The ticker symbol to check. - - Returns - ------- - bool: True if the symbol is valid, False otherwise. - - Examples - -------- - >>> from openbb_terminal.stocks.options import tmx_model - >>> tmx_model.check_symbol("AC") - >>> tmx_model.check_symbol("SPX") - """ - symbol = symbol.upper() - return len(SYMBOLS.query("`Underlying Symbol` == @symbol")) == 1 - - -def get_chains(symbol: str = "") -> Options: - """Gets the current quotes for the complete options chain. - No implied volatility is returned from this method. - Use `get_eodchains()` to get the implied volatility. - - Parameters - ---------- - symbol: str - The ticker symbol of the underlying asset. - - Returns - ------- - object: OptionsChains - chains: pd.DataFrame - The complete options chain for the ticker. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "TMX". - SYMBOLS: pd.DataFrame - TMX symbol directory. - - Example - ------- - >>> from openbb_terminal.stocks.options import tmx_model - >>> ticker = tmx_model.get_quotes("AC") - >>> chains = ticker.chains - """ - OptionsChains = Options() - OptionsChains.SYMBOLS = SYMBOLS - OptionsChains.source = "TMX" - - if symbol == "": - console.print("Please enter a symbol.") - return OptionsChains - - symbol = symbol.upper() - - if ".TO" in symbol: - symbol = symbol.replace(".TO", "") - - if check_symbol(symbol): - symbol = list(SYMBOLS.query("`Underlying Symbol` == @symbol").index.values)[0] - - if symbol not in OptionsChains.SYMBOLS.index and not OptionsChains.SYMBOLS.empty: - console.print( - "The symbol, " f"{symbol}" ", is not a valid TMX listing.", - sep=None, - ) - return OptionsChains - - OptionsChains.symbol = symbol - - QUOTES_URL = ( - "https://www.m-x.ca/en/trading/data/quotes?symbol=" - f"{OptionsChains.symbol}" - "*" - ) - - cols = [ - "expiration", - "strike", - "bid", - "ask", - "lastPrice", - "change", - "openInterest", - "volume", - "optionType", - ] - data = pd.DataFrame() - data = pd.read_html(QUOTES_URL)[0] - data = data.iloc[:-1] - - expirations = ( - data["Unnamed: 0_level_0"]["Expiry date"].astype(str).rename("expiration") - ) - - expirations = expirations.str.strip("(Weekly)") - - OptionsChains.expirations = list( - pd.DatetimeIndex(expirations.unique()).astype(str).sort_values() - ) - - strikes = (data["Unnamed: 7_level_0"].dropna().sort_values("Strike")).rename( - columns={"Strike": "strike"} - ) - - OptionsChains.strikes = list(strikes["strike"].unique()) - - calls = pd.concat([expirations, strikes, data["Calls"]], axis=1) - calls["expiration"] = pd.DatetimeIndex(calls["expiration"]).astype(str) - calls["optionType"] = "call" - calls.columns = cols - calls = calls.set_index(["expiration", "strike", "optionType"]) - - puts = pd.concat([expirations, strikes, data["Puts"]], axis=1) - puts["expiration"] = pd.DatetimeIndex(puts["expiration"]).astype(str) - puts["optionType"] = "put" - puts.columns = cols - puts = puts.set_index(["expiration", "strike", "optionType"]) - - chains = pd.concat([calls, puts]) - chains["openInterest"] = chains["openInterest"].astype(int) - chains["volume"] = chains["volume"].astype(int) - chains["change"] = chains["change"].astype(float) - chains["lastPrice"] = chains["lastPrice"].astype(float) - chains["bid"] = chains["bid"].astype(float) - chains["ask"] = chains["ask"].astype(float) - chains = chains.sort_index() - chains = chains.reset_index() - now = datetime.now() - temp = pd.DatetimeIndex(chains.expiration) - temp_ = (temp - now).days + 1 - chains["dte"] = temp_ - - OptionsChains.chains = chains - - OptionsChains.underlying_name = OptionsChains.SYMBOLS.loc[OptionsChains.symbol][ - "Name of Underlying Instrument" - ] - - try: - OptionsChains.underlying_price = get_underlying_price(OptionsChains.symbol) - OptionsChains.last_price = OptionsChains.underlying_price["price"] - except TypeError: - OptionsChains.last_price = 0 - OptionsChains.underlying_price = 0 - - OptionsChains.hasIV = "impliedVolatility" in OptionsChains.chains.columns - OptionsChains.hasGreeks = "gamma" in OptionsChains.chains.columns - - return OptionsChains - - -def get_eodchains(symbol: str = "", date: str = "") -> Options: - """Gets the complete options chain for the EOD on a specific date. - Open Interest values are from the previous day. - - Parameters - ---------- - symbol: str - The ticker symbol of the underlying asset. - date: str - The date to get the EOD chain for. Formatted as YYYY-MM-DD. - - Returns - ------- - object: Options - chains: pd.DataFrame - The complete options chain for the ticker. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "TMX". - date: str - The date, if applicable, for the EOD chains data. (YYYY-MM-DD) - SYMBOLS: pd.DataFrame - TMX symbol directory. - - Examples - -------- - >>> from openbb_terminal.stocks.options import tmx_model - >>> xiu = tmx_model.get_eodchains("XIU", "2009-01-01") - >>> chains = xiu.chains - """ - OptionsChains = Options() - - OptionsChains.SYMBOLS = SYMBOLS - OptionsChains.source = "TMX" - - symbol = symbol.upper() - BASE_URL = "https://www.m-x.ca/en/trading/data/historical?symbol=" - - if symbol == "": - console.print("Please enter a symbol.") - return OptionsChains - - if ".TO" in symbol: - symbol = symbol.replace(".TO", "") - - if symbol not in OptionsChains.SYMBOLS.index: - console.print( - "The symbol, " f"{symbol}" ", was not found in the TMX listings.", - sep=None, - ) - return OptionsChains - - OptionsChains.symbol = symbol.upper() - - if date == "": - EOD_URL = BASE_URL + f"{OptionsChains.symbol}" "&dnld=1#quotes" - if date != "": - if pd.to_datetime(date) < pd.to_datetime("2009-01-01"): - console.print("Historical options data begins on 2009-01-02.") - date = check_weekday(date) - if date in holidays: - date = (pd.to_datetime(date) + timedelta(days=1)).strftime("%Y-%m-%d") - date = check_weekday(date) - if date in holidays: - date = (pd.to_datetime(date) + timedelta(days=1)).strftime("%Y-%m-%d") - - EOD_URL = ( - BASE_URL + f"{OptionsChains.symbol}" - "&from=" - f"{date}" - "&to=" - f"{date}" - "&dnld=1#quotes" - ) - data = pd.read_csv(EOD_URL) - if data.empty: - console.print( - "No data found. Check the date to ensure the ticker was listed before that date." - ) - return OptionsChains - - OptionsChains.date = str(date) - - contractSymbol = [] - - for i in data.index: - contractSymbol.append(data["Symbol"].iloc[i].replace(" ", "")) - data["contractSymbol"] = contractSymbol - - data["optionType"] = data["Call/Put"].replace(0, "call").replace(1, "put") - - data = data.drop( - columns=[ - "Symbol", - "Class Symbol", - "Root Symbol", - "Underlying Symbol", - "Ins. Type", - "Call/Put", - ] - ) - - cols = [ - "date", - "strike", - "expiration", - "bid", - "ask", - "bidSize", - "askSize", - "lastPrice", - "volume", - "previousClose", - "change", - "open", - "high", - "low", - "totalValue", - "transactions", - "settlementPrice", - "openInterest", - "impliedVolatility", - "contractSymbol", - "optionType", - ] - - data.columns = cols - - OptionsChains.expirations = list( - pd.DatetimeIndex(data["expiration"].iloc[1:].unique()).astype(str) - ) - - OptionsChains.strikes = list(data["strike"].iloc[1:].unique()) - - data["expiration"] = pd.to_datetime(data["expiration"], format="%Y-%m-%d") - data["date"] = pd.to_datetime(data["date"], format="%Y-%m-%d") - data["impliedVolatility"] = 0.01 * data["impliedVolatility"] - - date_ = pd.to_datetime(date, format="%Y-%m-%d") - temp = pd.DatetimeIndex(data.expiration) - temp_ = pd.Series((temp - date_).days) - data["dte"] = pd.to_numeric(temp_) - data = data.set_index(["expiration", "strike", "optionType"]) - data = data.sort_index() - data["date"] = data["date"].astype(str) - - OptionsChains.underlying_name = OptionsChains.SYMBOLS.loc[OptionsChains.symbol][ - "Name of Underlying Instrument" - ] - - OptionsChains.chains = data.reset_index() - - underlying_price = ( - OptionsChains.chains.iloc[-1].drop( - index=[ - "impliedVolatility", - "strike", - "optionType", - "settlementPrice", - "openInterest", - "expiration", - "dte", - ] - ) - ).rename(f"{OptionsChains.underlying_name}") - - underlying_price.rename( - { - "lastPrice": "price", - "totalValue": "valueCAD", - "transactions": "transactions", - "contractSymbol": "symbol", - }, - inplace=True, - ) - OptionsChains.underlying_price = underlying_price - OptionsChains.last_price = underlying_price["price"] - OptionsChains.chains = OptionsChains.chains.iloc[:-1] - OptionsChains.chains.dte = OptionsChains.chains.dte.astype(int) - OptionsChains.hasIV = "impliedVolatility" in OptionsChains.chains.columns - OptionsChains.hasGreeks = "gamma" in OptionsChains.chains.columns - - return OptionsChains - - -def load_options(symbol: str, date: str = "", pydantic: bool = False) -> Options: - """Options data object for TMX. - - Parameters - ---------- - symbol : str - The ticker symbol to load. - date: Optional[str] - The date for EOD chains data. - pydantic: bool - Whether to return the object as a Pydantic Model or a subscriptable Pandas Object. Default is False. - - Returns - ------- - object: Options - chains: pd.DataFrame - The complete options chain for the ticker. Returns a dictionary if pydantic is True. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. Returns a dictionary if pydantic is True. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "TMX". - date: str - The date, if applicable, for the EOD chains data. (YYYY-MM-DD) - SYMBOLS: pd.DataFrame - TMX symbol directory. Returns a dictionary if pydantic is True. - - Examples - -------- - Get current options chains for RY. - >>> from openbb_terminal.stocks.options.tmx_model import load_options - >>> data = load_options("RY") - >>> chains = data.chains - - Get options chains for RY for a specific date. - >>> from openbb_terminal.stocks.options.tmx_model import load_options - >>> data = load_options("RY", "2022-01-03") - >>> chains = data.chains - - Return the object as a Pydantic Model. - >>> from openbb_terminal.stocks.options.tmx_model import load_options - >>> data = load_options("RY", pydantic=True) - """ - - if date != "": - OptionsChainsEOD = get_eodchains(symbol, date) - if not pydantic: - return OptionsChainsEOD - - if not OptionsChainsEOD.chains.empty: - OptionsChainsPydantic = PydanticOptions( - chains=OptionsChainsEOD.chains.to_dict(), - expirations=OptionsChainsEOD.expirations, - strikes=OptionsChainsEOD.strikes, - last_price=OptionsChainsEOD.last_price, - underlying_name=OptionsChainsEOD.underlying_name, - underlying_price=OptionsChainsEOD.underlying_price.to_dict(), - hasIV=OptionsChainsEOD.hasIV, - hasGreeks=OptionsChainsEOD.hasGreeks, - symbol=OptionsChainsEOD.symbol, - source=OptionsChainsEOD.source, - date=OptionsChainsEOD.date, - SYMBOLS=OptionsChainsEOD.SYMBOLS.to_dict(), - ) - return OptionsChainsPydantic - - return Options() - - OptionsChainsChains = get_chains(symbol) - if not pydantic: - return OptionsChainsChains - - if not OptionsChainsChains.chains.empty: - OptionsChainsPydantic = PydanticOptions( - chains=OptionsChainsChains.chains.to_dict(), - expirations=OptionsChainsChains.expirations, - strikes=OptionsChainsChains.strikes, - last_price=OptionsChainsChains.last_price, - underlying_name=OptionsChainsChains.underlying_name, - underlying_price=OptionsChainsChains.underlying_price.to_dict(), - hasIV=OptionsChainsChains.hasIV, - hasGreeks=OptionsChainsChains.hasGreeks, - symbol=OptionsChainsChains.symbol, - source=OptionsChainsChains.source, - SYMBOLS=OptionsChainsChains.SYMBOLS.to_dict(), - ) - return OptionsChainsPydantic - - return Options() diff --git a/openbb_terminal/stocks/options/tradier_model.py b/openbb_terminal/stocks/options/tradier_model.py deleted file mode 100644 index 6d88eb78eb0..00000000000 --- a/openbb_terminal/stocks/options/tradier_model.py +++ /dev/null @@ -1,568 +0,0 @@ -# mypy: disable-error-code=attr-defined - -"""Tradier options model""" -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import List, Optional - -import pandas as pd -import requests - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console, optional_rich_track -from openbb_terminal.stocks.options.op_helpers import Options, PydanticOptions - -logger = logging.getLogger(__name__) - -option_columns = [ - "symbol", - "bid", - "ask", - "strike", - "bidsize", - "asksize", - "volume", - "open_interest", - "option_type", -] -greek_columns = ["delta", "gamma", "theta", "vega", "ask_iv", "bid_iv", "mid_iv"] -df_columns = option_columns + greek_columns - -default_columns = [ - "mid_iv", - "vega", - "delta", - "gamma", - "theta", - "volume", - "open_interest", - "bid", - "ask", -] - -sorted_chain_columns = [ - "symbol", - "option_type", - "expiration", - "strike", - "bid", - "ask", - "open_interest", - "volume", - "mid_iv", - "delta", - "gamma", - "theta", - "vega", -] - - -@check_api_key(["API_TRADIER_TOKEN"]) -def lookup_company(symbol: str): - response = request( - "https://sandbox.tradier.com/v1/markets/lookup", - params={"q": f"{symbol}"}, - headers={ - "Authorization": f"Bearer {get_current_user().credentials.API_TRADIER_TOKEN }", # type: ignore[attr-defined] - "Accept": "application/json", - }, - ) - return response.json() - - -@log_start_end(log=logger) -@check_api_key(["API_TRADIER_TOKEN"]) -def get_historical_options( - symbol: str, - expiry: str, - strike: float = 0, - put: bool = False, - chain_id: Optional[str] = None, -) -> pd.DataFrame: - """ - Gets historical option pricing. This inputs either ticker, expiration, strike or the OCC chain ID and processes - the request to tradier for historical premiums. - - Parameters - ---------- - symbol: str - Stock ticker symbol - expiry: str - Option expiration date - strike: int - Option strike price - put: bool - Is this a put option? - chain_id: Optional[str] - OCC chain ID - - Returns - ------- - df_hist: pd.DataFrame - Dataframe of historical option prices - """ - if not chain_id: - op_type = ["call", "put"][put] - chain = get_option_chain(symbol, expiry) - chain = chain[chain["option_type"] == op_type] - - try: - symbol = chain[(chain.strike == strike)]["symbol"].values[0] - except IndexError: - error = f"Strike: {strike}, Option type: {op_type} not found" - logging.exception(error) - console.print(f"{error}\n") - return pd.DataFrame() - else: - symbol = chain_id - - try: - response = request( - "https://sandbox.tradier.com/v1/markets/history", - params={"symbol": {symbol}, "interval": "daily"}, - headers={ - "Authorization": f"Bearer {get_current_user().credentials.API_TRADIER_TOKEN}", - "Accept": "application/json", - }, - ) - except requests.exceptions.ReadTimeout: - return pd.DataFrame() - - if response.status_code != 200: - console.print("Error with request") - return pd.DataFrame() - - data = response.json()["history"] - if not data: - console.print("No historical data available") - return pd.DataFrame() - - df_hist = pd.DataFrame(data["day"]) - df_hist = df_hist.set_index("date") - df_hist.index = pd.DatetimeIndex(df_hist.index) - return df_hist - - -# pylint: disable=no-else-return - -option_cols = [ - "strike", - "bid", - "ask", - "volume", - "open_interest", - "mid_iv", -] - -option_col_map = {"open_interest": "openinterest", "mid_iv": "iv"} - - -@log_start_end(log=logger) -@check_api_key(["API_TRADIER_TOKEN"]) -def get_full_option_chain(symbol: str, quiet: bool = False) -> pd.DataFrame: - """Get available expiration dates for given ticker - - Parameters - ---------- - symbol: str - Ticker symbol to get expirations for - quiet: bool - Suppress output of progress bar - - Returns - ------- - pd.DataFrame - Dataframe of all option chains - """ - - expirations = option_expirations(symbol) - options_dfs: pd.DataFrame = [] - - for expiry in optional_rich_track( - expirations, suppress_output=quiet, desc="Getting Option Chain" - ): - chain = get_option_chain(symbol, expiry) - options_dfs.append(chain) - chain = pd.concat(options_dfs) - chain = chain[sorted_chain_columns].rename( - columns={ - "mid_iv": "impliedVolatility", - "open_interest": "openInterest", - "option_type": "optionType", - "symbol": "optionSymbol", - } - ) - chain["openInterest"] = chain["openInterest"].astype(int) - chain["volume"] = chain["volume"].astype(int) - chain = chain.set_index(["expiration", "strike", "optionType"]).sort_index() - - return chain.reset_index() - - -@log_start_end(log=logger) -@check_api_key(["API_TRADIER_TOKEN"]) -def option_expirations(symbol: str) -> List[str]: - """Get available expiration dates for given ticker - - Parameters - ---------- - symbol: str - Ticker symbol to get expirations for - - Returns - ------- - dates: List[str] - List of of available expirations - """ - r = request( - "https://sandbox.tradier.com/v1/markets/options/expirations", - params={"symbol": symbol, "includeAllRoots": "true", "strikes": "false"}, - headers={ - "Authorization": f"Bearer {get_current_user().credentials.API_TRADIER_TOKEN}", # type: ignore[attr-defined] - "Accept": "application/json", - }, - ) - if r.status_code == 200: - try: - dates = r.json()["expirations"]["date"] - return dates - except TypeError: - logging.exception("Error in tradier JSON response. Check loaded ticker.") - console.print("Error in tradier JSON response. Check loaded ticker.\n") - return [] - else: - console.print("Tradier request failed. Check token. \n") - return [] - - -@log_start_end(log=logger) -@check_api_key(["API_TRADIER_TOKEN"]) -def get_option_chain(symbol: str, expiry: str) -> pd.DataFrame: - """Display option chains [Source: Tradier]" - - Parameters - ---------- - symbol : str - Ticker to get options for - expiry : str - Expiration date in the form of "YYYY-MM-DD" - - Returns - ------- - chains: pd.DataFrame - Dataframe with options for the given Symbol and Expiration date - """ - params = {"symbol": symbol, "expiration": expiry, "greeks": "true"} - - headers = { - "Authorization": f"Bearer {get_current_user().credentials.API_TRADIER_TOKEN}", # type: ignore[attr-defined] - "Accept": "application/json", - } - - response = request( - "https://sandbox.tradier.com/v1/markets/options/chains", - params=params, - headers=headers, - ) - if response.status_code != 200: - console.print("Error in request. Check API_TRADIER_TOKEN\n") - return pd.DataFrame() - - chains = process_chains(response) - chains["expiration"] = expiry - return chains - - -@log_start_end(log=logger) -def process_chains(response: requests.models.Response) -> pd.DataFrame: - """Function to take in the request and return a DataFrame - - Parameters - ---------- - response: requests.models.Response - This is the response from tradier api. - - Returns - ------- - opt_chain: pd.DataFrame - Dataframe with all available options - """ - json_response = response.json() - options = json_response["options"]["option"] - - opt_chain = pd.DataFrame(columns=df_columns) - for idx, option in enumerate(options): - # initialize empty dictionary - d = {col: "" for col in df_columns} - # populate main dictionary values - for col in option_columns: - if col in option: - d[col] = option[col] - - # populate greek dictionary values - if option["greeks"]: - for col in greek_columns: - if col in option["greeks"]: - d[col] = option["greeks"][col] - - opt_chain.loc[idx, :] = d - - return opt_chain - - -@log_start_end(log=logger) -@check_api_key(["API_TRADIER_TOKEN"]) -def get_last_price(symbol: str): - """Makes api request for last price - - Parameters - ---------- - symbol: str - Ticker symbol - - Returns - ------- - float: - Last price - """ - r = request( - "https://sandbox.tradier.com/v1/markets/quotes", - params={"symbols": symbol, "includeAllRoots": "true", "strikes": "false"}, - headers={ - "Authorization": f"Bearer {get_current_user().credentials.API_TRADIER_TOKEN}", # type: ignore[attr-defined] - "Accept": "application/json", - }, - ) - if r.status_code == 200: - last = r.json()["quotes"]["quote"]["last"] - if last is None: - return 0 - return float(last) - else: - console.print("Error getting last price") - return None - - -@check_api_key(["API_TRADIER_TOKEN"]) -def get_underlying_price(symbol: str) -> pd.Series: - """Gets the current price and performance of the underlying asset. - - Parameters - ---------- - symbol: str - Ticker symbol - - Returns - ------- - pd.Series: - Series of current price and performance of the underlying asset. - """ - r = request( - "https://sandbox.tradier.com/v1/markets/quotes", - params={"symbols": symbol, "includeAllRoots": "true", "strikes": "false"}, - headers={ - "Authorization": f"Bearer {get_current_user().credentials.API_TRADIER_TOKEN}", # type: ignore[attr-defined] - "Accept": "application/json", - }, - ) - if r.status_code != 200: - console.print("Error getting last price") - return pd.DataFrame() - underlying_price = pd.Series(dtype=object) - underlying_price = pd.Series(r.json()["quotes"]["quote"]) - underlying_price = underlying_price.rename( - index={ - "description": "name", - "change_percentage": "changePercent", - "average_volume": "avgVolume", - "last_volume": "volume", - "trade_date": "lastTradeTimestamp", - "prevclose": "previousClose", - "week_52_high": "fiftyTwoWeekHigh", - "week_52_low": "fiftyTwoWeekLow", - "bidsize": "bidSize", - "bidexch": "bidExchange", - "bid_date": "bidDate", - "asksize": "askSize", - "askexch": "askExchange", - "ask_date": "askDate", - "root_symbols": "rootSymbols", - } - ) - underlying_price[ # pylint: disable=unsupported-assignment-operation - "lastTradeTimestamp" - ] = ( - pd.to_datetime(underlying_price["lastTradeTimestamp"], unit="ms").tz_localize( - "EST" - ) - ).strftime( - "%Y-%m-%d" - ) - underlying_price["bidDate"] = ( # pylint: disable=unsupported-assignment-operation - pd.to_datetime(underlying_price["bidDate"], unit="ms").tz_localize("EST") - ).strftime("%Y-%m-%d") - underlying_price["askDate"] = ( # pylint: disable=unsupported-assignment-operation - pd.to_datetime(underlying_price["askDate"], unit="ms").tz_localize("EST") - ).strftime("%Y-%m-%d") - - return underlying_price - - -def get_chains(symbol: str) -> Options: - """OptionsChains data object for Tradier. - - Parameters - ---------- - symbol : str - The ticker symbol to load. - - Returns - ------- - object: OptionsChains - chains: pd.DataFrame - The complete options chain for the ticker. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "Tradier". - SYMBOLS: pd.DataFrame - Tradier symbol directory. - - Examples - -------- - >>> from openbb_terminal.stocks.options import tradier_model - >>> data = tradier_model.load_options("AAPL") - >>> chains = tradier_model.chains - """ - - OptionsChains = Options() - OptionsChains.source = "Tradier" - - try: - OptionsChains.SYMBOLS = pd.DataFrame( - lookup_company("")["securities"]["security"] - ) - except requests.exceptions.JSONDecodeError: - return OptionsChains - - OptionsChains.symbol = symbol.upper() - - if OptionsChains.symbol not in list(OptionsChains.SYMBOLS["symbol"]): - console.print(f"{OptionsChains.symbol} is not support by Tradier.") - return OptionsChains - - OptionsChains.underlying_price = get_underlying_price(OptionsChains.symbol) - OptionsChains.underlying_name = OptionsChains.underlying_price["name"] - OptionsChains.last_price = OptionsChains.underlying_price["last"] - - chains = get_full_option_chain(OptionsChains.symbol) - - if not chains.empty: - OptionsChains.expirations = chains["expiration"].unique().tolist() - OptionsChains.strikes = chains["strike"].sort_values().unique().tolist() - - now = datetime.now() - temp = pd.DatetimeIndex(chains.expiration) - temp_ = (temp - now).days + 1 - chains["dte"] = temp_ - - OptionsChains.chains = chains - - OptionsChains.hasIV = "impliedVolatility" in OptionsChains.chains.columns - OptionsChains.hasGreeks = "gamma" in OptionsChains.chains.columns - - return OptionsChains - - -def load_options(symbol: str, pydantic: bool = False) -> Options: - """OptionsChains data object for Tradier. - - Parameters - ---------- - symbol: str - The ticker symbol to load. - pydantic: bool - Whether to return the object as a Pydantic Model or a subscriptable Pandas Object. Default is False. - - Returns - ------- - object: Options - chains: pd.DataFrame - The complete options chain for the ticker. Returns as a dictionary if pydantic is True. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. Returns as a dictionary if pydantic is True. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Returns greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "Tradier". - SYMBOLS: pd.DataFrame - Tradier symbol directory. Returns as a dictionary if pydantic is True. - - Examples - -------- - Get current options chains for AAPL. - >>> from openbb_terminal.stocks.options.tradier_model import load_options - >>> data = load_options("AAPL") - >>> chains = data.chains - - Return the object as a Pydantic Model. - >>> from openbb_terminal.stocks.options.tradier_model import load_options - >>> data = load_options("AAPL", pydantic=True) - """ - - options = get_chains(symbol) - - if options.last_price is None: - options.last_price = 0 - console.print("No last price for " + options.symbol) - - if not pydantic: - return options - - if not options.chains.empty: - OptionsChainsPydantic = PydanticOptions( - chains=options.chains.to_dict(), - expirations=options.expirations, - strikes=options.strikes, - last_price=options.last_price, - underlying_name=options.underlying_name, - underlying_price=options.underlying_price.to_dict(), - hasIV=options.hasIV, - hasGreeks=options.hasGreeks, - symbol=options.symbol, - source=options.source, - SYMBOLS=options.SYMBOLS.to_dict(), - ) - return OptionsChainsPydantic - - return Options() diff --git a/openbb_terminal/stocks/options/tradier_view.py b/openbb_terminal/stocks/options/tradier_view.py deleted file mode 100644 index fde0f088ac9..00000000000 --- a/openbb_terminal/stocks/options/tradier_view.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Tradier options view""" - -__docformat__ = "numpy" - -import logging -import os -import warnings -from typing import Optional - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options import tradier_model - -logger = logging.getLogger(__name__) - -warnings.filterwarnings("ignore") - - -# pylint: disable=too-many-arguments -@log_start_end(log=logger) -@check_api_key(["API_TRADIER_TOKEN"]) -def display_historical( - symbol: str, - expiry: str, - strike: float = 0, - put: bool = False, - raw: bool = False, - chain_id: Optional[str] = None, - export: str = "", - sheet_name: str = "", - external_axes: bool = False, -): - """Plot historical option prices - - Parameters - ---------- - symbol: str - Stock ticker symbol - expiry: str - Expiry date of option - strike: float - Option strike price - put: bool - Is this a put option? - raw: bool - Print raw data - chain_id: str - OCC option symbol - export: str - Format of export file - sheet_name: str - Optionally specify the name of the sheet to export to - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - df_hist = tradier_model.get_historical_options( - symbol, expiry, strike, put, chain_id - ) - - if df_hist.empty: - if chain_id: - console.print(f"No historical data found for {chain_id} ") - return None - console.print(f"No historical data found for {symbol} {expiry} ") - return None - - if raw: - print_rich_table( - df_hist, - headers=[x.title() for x in df_hist.columns], - title="Historical Option Prices", - export=bool(export), - show_index=True, - index_name="Date", - ) - - op_type = ["call", "put"][put] - - df_hist.columns = [x.title() for x in df_hist.columns] - - fig = OpenBBFigure.create_subplots( - rows=1, - cols=1, - specs=[[{"secondary_y": True}]], - vertical_spacing=0.06, - ) - fig.set_title(f"Historical {symbol} {strike} {op_type.title()}") - - df_hist.index = df_hist.index.astype("datetime64[ns]") - - fig.add_candlestick( - open=df_hist["Open"], - high=df_hist["High"], - low=df_hist["Low"], - close=df_hist["Close"], - x=df_hist.index, - name=f"{symbol} OHLC", - row=1, - col=1, - secondary_y=False, - ) - fig.add_inchart_volume(df_hist) - - if export: - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hist", - df_hist, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/stocks/options/yfinance_model.py b/openbb_terminal/stocks/options/yfinance_model.py deleted file mode 100644 index d0ffb10c028..00000000000 --- a/openbb_terminal/stocks/options/yfinance_model.py +++ /dev/null @@ -1,439 +0,0 @@ -# mypy: disable-error-code=attr-defined - -"""Yfinance options model""" -__docformat__ = "numpy" - -import logging -from datetime import datetime -from typing import Any, Dict, List, Tuple - -import pandas as pd -import yfinance as yf - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console, optional_rich_track -from openbb_terminal.stocks.options.op_helpers import Options, PydanticOptions - -logger = logging.getLogger(__name__) - -sorted_chain_columns = [ - "contractSymbol", - "optionType", - "expiration", - "strike", - "lastPrice", - "bid", - "ask", - "openInterest", - "volume", - "impliedVolatility", -] - - -def get_full_option_chain(symbol: str, quiet: bool = False) -> pd.DataFrame: - """Get all options for given ticker [Source: Yahoo Finance] - - Parameters - ---------- - symbol: str - Stock ticker symbol - quiet: bool - Flag to suppress progress bar - - Returns - ------- - pd.Dataframe - Option chain - """ - ticker = yf.Ticker(symbol) - dates = ticker.options - - options = pd.DataFrame() - - for _date in optional_rich_track( - dates, suppress_output=quiet, desc="Getting Option Chain" - ): - calls = ticker.option_chain(_date).calls - calls["optionType"] = "call" - calls["expiration"] = _date - calls = calls[sorted_chain_columns] - puts = ticker.option_chain(_date).puts - puts["optionType"] = "put" - puts["expiration"] = _date - puts = puts[sorted_chain_columns] - - temp = pd.merge(calls, puts, how="outer", on="strike") - temp["expiration"] = _date - options = ( - pd.concat([options, pd.concat([calls, puts])], axis=0) - .fillna(0) - .reset_index(drop=True) - ) - return options - - -@log_start_end(log=logger) -def get_option_chain(symbol: str, expiry: str): - """Gets option chain from yf for given ticker and expiration - - Parameters - ---------- - symbol: str - Ticker symbol to get options for - expiry: str - Date to get options for. YYYY-MM-DD - - Returns - ------- - chains: yf.ticker.OptionsChains - OptionsChains chain - """ - - yf_ticker = yf.Ticker(symbol) - try: - chain = yf_ticker.option_chain(expiry)[0] - except Exception: - console.print(f"[red]Error: Expiration {expiry} cannot be found.[/red]") - chain = pd.DataFrame() - - return chain - - -@log_start_end(log=logger) -def option_expirations(symbol: str): - """Get available expiration dates for given ticker - - Parameters - ---------- - symbol: str - Ticker symbol to get expirations for - - Returns - ------- - dates: List[str] - List of of available expirations - """ - yf_ticker = yf.Ticker(symbol) - dates = list(yf_ticker.options) - if not dates: - console.print("No expiration dates found for ticker.") - return dates - - -@log_start_end(log=logger) -def get_dividend(symbol: str) -> pd.Series: - """Gets option chain from yf for given ticker and expiration - - Parameters - ---------- - symbol: str - Ticker symbol to get options for - - Returns - ------- - chains: yf.ticker.Dividends - Dividends - """ - yf_ticker = yf.Ticker(symbol) - dividend = yf_ticker.dividends - return dividend - - -@log_start_end(log=logger) -def get_x_values(current_price: float, options: List[Dict[str, int]]) -> List[float]: - """Generates different price values that need to be tested""" - x_list = list(range(101)) - mini = current_price - maxi = current_price - if len(options) == 0: - mini *= 0.5 - maxi *= 1.5 - elif len(options) > 0: - biggest = max(options, key=lambda x: x["strike"]) - smallest = min(options, key=lambda x: x["strike"]) - maxi = max(maxi, biggest["strike"]) * 1.2 - mini = min(mini, smallest["strike"]) * 0.8 - num_range = maxi - mini - return [(x / 100) * num_range + mini for x in x_list] - - -def get_y_values( - base: float, - price: float, - options: List[Dict[Any, Any]], - underlying: int, -) -> float: - """Generates y values for corresponding x values""" - option_change = 0 - change = price - base - for option in options: - if option["type"] == "Call": - abs_change = price - option["strike"] if price > option["strike"] else 0 - option_change += option["sign"] * abs_change - elif option["type"] == "Put": - abs_change = option["strike"] - price if price < option["strike"] else 0 - option_change += option["sign"] * abs_change - return (change * underlying) + option_change - - -@log_start_end(log=logger) -def generate_data( - current_price: float, options: List[Dict[str, int]], underlying: int -) -> Tuple[List[float], List[float], List[float]]: - """Gets x values, and y values before and after premiums""" - - # Remove empty elements from options - options = [o for o in options if o] - - x_vals = get_x_values(current_price, options) - base = current_price - total_cost = sum(x["cost"] for x in options) - before = [get_y_values(base, x, options, underlying) for x in x_vals] - if total_cost != 0: - after = [ - get_y_values(base, x, options, underlying) - total_cost for x in x_vals - ] - return x_vals, before, after - return x_vals, before, [] - - -@log_start_end(log=logger) -def get_price(symbol: str) -> float: - """Get current price for a given ticker - - Parameters - ---------- - symbol : str - The ticker symbol to get the price for - - Returns - ------- - price : float - The price of the ticker - """ - ticker_yahoo = yf.Ticker(symbol) - data = ticker_yahoo.history() - last_quote = data.tail(1)["Close"].iloc[0] - - return last_quote - - -@log_start_end(log=logger) -def get_info(symbol: str): - """Get info for a given ticker - - Parameters - ---------- - symbol : str - The ticker symbol to get the price for - - Returns - ------- - price : float - The info for a given ticker - """ - tick = yf.Ticker(symbol) - return tick.info - - -@log_start_end(log=logger) -def get_closing(symbol: str) -> pd.Series: - """Get closing prices for a given ticker - - Parameters - ---------- - symbol : str - The ticker symbol to get the price for - - Returns - ------- - price : List[float] - A list of closing prices for a ticker - """ - tick = yf.Ticker(symbol) - return tick.history(period="1y")["Close"] - - -def get_dte(date_value: str) -> int: - """Gets days to expiration from yfinance option date""" - return (datetime.strptime(date_value, "%Y-%m-%d") - datetime.now()).days - - -@log_start_end(log=logger) -def get_iv_surface(symbol: str) -> pd.DataFrame: - """Gets IV surface for calls and puts for ticker - - Parameters - ---------- - symbol: str - Stock ticker symbol to get - - Returns - ------- - pd.DataFrame - Dataframe of DTE, Strike and IV - """ - - stock = yf.Ticker(symbol) - dates = stock.options - vol_df = pd.DataFrame() - columns = ["strike", "impliedVolatility", "openInterest", "lastPrice"] - for date_value in dates: - df = stock.option_chain(date_value).calls[columns] - df["dte"] = get_dte(date_value) - vol_df = pd.concat([vol_df, df], axis=0) - df = stock.option_chain(date_value).puts[columns] - df["dte"] = get_dte(date_value) - vol_df = pd.concat([vol_df, df], axis=0) - return vol_df - - -@log_start_end(log=logger) -def get_last_price(symbol: str) -> pd.Series: - """Get the price and performance of the underlying asset. - - Parameters - ---------- - symbol: str - Symbol to get quote for - - Returns - ------- - pd.Series - Pandas Series with the price and performance of the underlying asset. - """ - - ticker = yf.Ticker(symbol).fast_info - df = pd.Series(dtype=object) - df["lastPrice"] = round(ticker["lastPrice"], 2) - df["previousClose"] = round(ticker["previousClose"], 2) - df["open"] = round(ticker["open"], 2) - df["high"] = round(ticker["dayHigh"], 2) - df["low"] = round(ticker["dayLow"], 2) - df["yearHigh"] = round(ticker["yearHigh"], 2) - df["yearLow"] = round(ticker["yearLow"], 2) - df["fiftyDayMA"] = round(ticker["fiftyDayAverage"], 2) - df["twoHundredDayMA"] = round(ticker["twoHundredDayAverage"], 2) - - return df.loc["lastPrice"] - - -@log_start_end(log=logger) -def get_underlying_price(symbol: str) -> pd.Series: - """Get the price and performance of the underlying asset. - - Parameters - ---------- - symbol: str - Symbol to get quote for - - Returns - ------- - pd.Series - Pandas Series with the price and performance of the underlying asset. - """ - - ticker = yf.Ticker(symbol).fast_info - df = pd.Series(dtype=object) - df["lastPrice"] = round(ticker["lastPrice"], 2) - df["previousClose"] = round(ticker["previousClose"], 2) - df["open"] = round(ticker["open"], 2) - df["high"] = round(ticker["dayHigh"], 2) - df["low"] = round(ticker["dayLow"], 2) - df["yearHigh"] = round(ticker["yearHigh"], 2) - df["yearLow"] = round(ticker["yearLow"], 2) - df["fiftyDayMA"] = round(ticker["fiftyDayAverage"], 2) - df["twoHundredDayMA"] = round(ticker["twoHundredDayAverage"], 2) - - return df.rename(f"{symbol}") - - -def load_options(symbol: str, pydantic: bool = False) -> Options: - """OptionsChains data object for YahooFinance. - - Parameters - ---------- - symbol: str - The ticker symbol to load. - pydantic: bool - Whether to return the object as a Pydantic Model or a subscriptable Pandas Object. Default is False. - - Returns - ------- - object: OptionsChains - chains: pd.DataFrame - The complete options chain for the ticker. Returns as a dictionary if pydantic is True. - expirations: list[str] - List of unique expiration dates. (YYYY-MM-DD) - strikes: list[float] - List of unique strike prices. - last_price: float - The last price of the underlying asset. - underlying_name: str - The name of the underlying asset. - underlying_price: pd.Series - The price and recent performance of the underlying asset. Returns as a dictionary if pydantic is True. - hasIV: bool - Returns implied volatility. - hasGreeks: bool - Does not return greeks data. - symbol: str - The symbol entered by the user. - source: str - The source of the data, "YahooFinance". - - Examples - -------- - Get current options chains for AAPL. - >>> from openbb_terminal.stocks.options.yfinance_model import load_options - >>> data = load_options("AAPL") - >>> chains = data.chains - - Return the object as a Pydantic Model. - >>> from openbb_terminal.stocks.options.yfinance_model import load_options - >>> data = load_options("AAPL", pydantic=True) - """ - OptionsChains = Options() - - OptionsChains.source = "YahooFinance" - OptionsChains.symbol = symbol.upper() - - chains = get_full_option_chain(OptionsChains.symbol) - - if not chains.empty: - OptionsChains.expirations = option_expirations(OptionsChains.symbol) - OptionsChains.strikes = ( - pd.Series(chains["strike"]).sort_values().unique().tolist() - ) - OptionsChains.underlying_price = get_underlying_price(OptionsChains.symbol) - OptionsChains.underlying_name = OptionsChains.symbol - OptionsChains.last_price = OptionsChains.underlying_price["lastPrice"] - now = datetime.now() - temp = pd.DatetimeIndex(chains.expiration) - temp_ = (temp - now).days + 1 - chains["dte"] = temp_ - - OptionsChains.chains = chains - OptionsChains.hasIV = "impliedVolatility" in OptionsChains.chains.columns - OptionsChains.hasGreeks = "gamma" in OptionsChains.chains.columns - - if not chains.empty and OptionsChains.last_price is None: - OptionsChains.last_price = 0 - console.print("No last price for " + OptionsChains.symbol) - - if not pydantic: - return OptionsChains - - OptionsChainsPydantic = PydanticOptions( - chains=OptionsChains.chains.to_dict(), - expirations=OptionsChains.expirations, - strikes=OptionsChains.strikes, - last_price=OptionsChains.last_price, - underlying_name=OptionsChains.underlying_name, - underlying_price=OptionsChains.underlying_price.to_dict(), - hasIV=OptionsChains.hasIV, - hasGreeks=OptionsChains.hasGreeks, - symbol=OptionsChains.symbol, - source=OptionsChains.source, - ) - return OptionsChainsPydantic diff --git a/openbb_terminal/stocks/options/yfinance_view.py b/openbb_terminal/stocks/options/yfinance_view.py deleted file mode 100644 index 926de4704f5..00000000000 --- a/openbb_terminal/stocks/options/yfinance_view.py +++ /dev/null @@ -1,425 +0,0 @@ -"""Yfinance options view""" - -__docformat__ = "numpy" - -import logging -import os -import re -from typing import Any, Dict, List, Optional, Union - -import numpy as np -from scipy.spatial import Delaunay -from scipy.stats import binom - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.core.plots.config.openbb_styles import ( - PLT_3DMESH_COLORSCALE, - PLT_3DMESH_SCENE, -) -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.options import op_helpers, yfinance_model -from openbb_terminal.stocks.options.yfinance_model import ( - generate_data, - get_option_chain, - get_price, -) - -# pylint: disable=C0302, R0913, too-many-arguments - - -logger = logging.getLogger(__name__) - - -def header_fmt(header: str) -> str: - """ - Formats strings to appear as titles - - Parameters - ---------- - header: str - The string to be formatted - - Returns - ------- - new_header: str - The clean string to use as a header - """ - - words = re.findall("[A-Z][^A-Z]*", header) - if words == []: - words = [header] - new_header = " ".join(words) - new_header = new_header.replace("_", " ") - return new_header.title() - - -@log_start_end(log=logger) -def plot_plot( - symbol: str, - expiry: str, - put: bool = False, - x: str = "s", - y: str = "iv", - custom: str = "", - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Generate a graph custom graph based on user input - - Parameters - ---------- - symbol: str - Stock ticker symbol - expiry: str - Option expiration - x: str - variable to display in x axis, choose from: - ltd, s, lp, b, a, c, pc, v, oi, iv - y: str - variable to display in y axis, choose from: - ltd, s, lp, b, a, c, pc, v, oi, iv - custom: str - type of plot - put: bool - put option instead of call - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - type of data to export - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - is_smile = custom == "smile" - convert = { - "ltd": "lastTradeDate", - "s": "strike", - "lp": "lastPrice", - "b": "bid", - "a": "ask", - "c": "change", - "pc": "percentChange", - "v": "volume", - "oi": "openInterest", - "iv": "impliedVolatility", - } - - if is_smile: - x = "strike" - y = "impliedVolatility" - else: - if x is None: - return console.print("[red]Invalid option sent for x-axis[/red]\n") - if y is None: - return console.print("[red]Invalid option sent for y-axis[/red]\n") - if x in convert: - x = convert[x] - else: - x = "strike" - console.print( - f"[red]'{x}' is not a valid option. Defaulting to `strike`.[/red]\n" - ) - if y in convert: - y = convert[y] - else: - y = "impliedVolatility" - console.print( - f"[red]'{y}' is not a valid option. Defaulting to `impliedVolatility`.[/red]\n" - ) - - chain = yfinance_model.get_option_chain(symbol, expiry) - - fig = OpenBBFigure() - if not is_smile: - varis = op_helpers.opt_chain_cols - values = chain.puts if put else chain.calls - - x_data = values[x] - y_data = values[y] - - option = "puts" if put else "calls" - title = f"{varis[y]['label']} vs. {varis[x]['label']} for {symbol} {option} on {expiry}" - xaxis_title = varis[x]["label"] - yaxis_title = varis[y]["label"] - - fig.add_scatter(x=x_data, y=y_data, mode="lines+markers") - else: - calls = chain.calls[chain.calls["impliedVolatility"] > 0.00002].dropna() - puts = chain.puts[chain.puts["impliedVolatility"] > 0.00002].dropna() - - for data, name in zip([calls, puts], ["Calls", "Puts"]): - fig.add_scatter( - x=data["strike"], - y=data["impliedVolatility"], - mode="lines+markers", - name=name, - line_color=theme.up_color if name == "Calls" else theme.down_color, - ) - - title = f"Implied Volatility Smile for {symbol} on {expiry}" - xaxis_title = "Strike" - yaxis_title = "Implied Volatility" - - fig.set_title(title) - fig.set_xaxis_title(xaxis_title) - fig.set_yaxis_title(yaxis_title) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "plot", - sheet_name=sheet_name, - figure=fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def plot_payoff( - current_price: float, - options: List[Dict[Any, Any]], - underlying: float, - symbol: str, - expiry: str, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Generate a graph showing the option payoff diagram""" - x, yb, ya = generate_data(current_price, options, underlying) - - plot = OpenBBFigure( - title=f"Option Payoff Diagram for {symbol} on {expiry}", - xaxis=dict(tickformat="${x:.2f}", title="Underlying Asset Price at Expiration"), - yaxis=dict(tickformat="${x:.2f}", title="Profit"), - ) - plot.add_scatter(x=x, y=yb, name="Payoff Before Premium") - if ya: - plot.add_scatter(x=x, y=ya, name="Payoff After Premium") - else: - plot.data[0].name = "Payoff" - - return plot.show(external=external_axes) - - -@log_start_end(log=logger) -def plot_expected_prices( - und_vals: List[List[float]], - p: float, - symbol: str, - expiry: str, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plot expected prices of the underlying asset at expiration - - Parameters - ---------- - und_vals : List[List[float]] - The expected underlying values at the expiration date - p : float - The probability of the stock price moving upward each round - symbol : str - The ticker symbol of the option's underlying asset - expiry : str - The expiration for the option - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - up_moves = list(range(len(und_vals[-1]))) - up_moves.reverse() - probs = [100 * binom.pmf(r, len(up_moves), p) for r in up_moves] - - fig = OpenBBFigure() - - fig.add_scatter( - x=und_vals[-1], - y=probs, - mode="lines", - name="Probabilities", - ) - - fig.update_layout( - title=f"Probabilities for ending prices of {symbol} on {expiry}", - xaxis_title="Price", - yaxis_title="Probability", - xaxis_tickformat="$,.2f", - yaxis_tickformat=".0%", - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_vol_surface( - symbol: str, - export: str = "", - sheet_name: Optional[str] = None, - z: str = "IV", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Display vol surface - - Parameters - ---------- - symbol : str - Ticker symbol to get surface for - export : str - Format to export data - z : str - The variable for the Z axis - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - data = yfinance_model.get_iv_surface(symbol) - z = z.upper() - - if data.empty: - return console.print(f"No options data found for {symbol}.\n") - - z_dict = { - "IV": (data.impliedVolatility, "Volatility"), - "OI": (data.openInterest, "Open Interest"), - "LP": (data.lastPrice, "Last Price"), - } - label = z_dict[z][1] - - if z not in z_dict: - return console.print( - f"Invalid z value: {z}.\n Valid values are: IV, OI, LP. (case insensitive)\n" - ) - - X = data.dte - Y = data.strike - Z = z_dict[z][0] - - points3D = np.vstack((X, Y, Z)).T - points2D = points3D[:, :2] - tri = Delaunay(points2D) - II, J, K = tri.simplices.T - - fig = OpenBBFigure() - fig.set_title(f"{label} Surface for {symbol.upper()}") - - fig_kwargs = dict(z=Z, x=X, y=Y, i=II, j=J, k=K, intensity=Z) - fig.add_mesh3d( - **fig_kwargs, - colorscale=PLT_3DMESH_COLORSCALE, - hovertemplate="DTE: %{y}
Strike: %{x}
" - + z_dict[z][1] - + ": %{z}", - showscale=False, - flatshading=True, - lighting=dict( - ambient=0.5, diffuse=0.5, roughness=0.5, specular=0.4, fresnel=0.4 - ), - ) - - tick_kwargs = dict(tickfont=dict(size=13), titlefont=dict(size=16)) - fig.update_layout( - scene=dict( - xaxis=dict(title="DTE", **tick_kwargs), - yaxis=dict(title="Strike", **tick_kwargs), - zaxis=dict(title=z, **tick_kwargs), - ) - ) - fig.update_layout( - margin=dict(l=5, r=10, t=40, b=20), - title_x=0.5, - scene_camera=dict( - up=dict(x=0, y=0, z=2), - center=dict(x=0, y=0, z=-0.3), - eye=dict(x=1.25, y=1.25, z=0.69), - ), - scene=PLT_3DMESH_SCENE, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "vsurf", - data, - sheet_name, - fig, - margin=False, - ) - - return fig.show(external=external_axes, margin=False) - - -@log_start_end(log=logger) -def show_greeks( - symbol: str, - expiry: str, - div_cont: float = 0, - rf: Optional[float] = None, - opt_type: int = 1, - mini: float = -1, - maxi: float = -1, - show_all: bool = False, -) -> None: - """ - Shows the greeks for a given option - - Parameters - ---------- - symbol: str - The ticker symbol value of the option - div_cont: float - The dividend continuous rate - expiry: str - The date of expiration, format "YYYY-MM-DD", i.e. 2010-12-31. - rf: float - The risk-free rate - opt_type: Union[1, -1] - The option type 1 is for call and -1 is for put - mini: float - The minimum strike price to include in the table - maxi: float - The maximum strike price to include in the table - show_all: bool - Whether to show all greeks - """ - - current_price = get_price(symbol) - chain = get_option_chain(symbol, expiry) - - min_strike, max_strike = op_helpers.get_strikes( - min_sp=mini, max_sp=maxi, chain=chain.puts - ) - - for option in ["calls", "puts"]: - attr = getattr(chain, option) - attr = attr[attr["strike"] >= min_strike] - attr = attr[attr["strike"] <= max_strike] - - chain.puts["optionType"] = "put" - chain.calls["optionType"] = "call" - - df = op_helpers.get_greeks( - current_price=current_price, - expire=expiry, - calls=chain.calls, - puts=chain.puts, - div_cont=div_cont, - rf=rf, - opt_type=opt_type, - show_extra_greeks=show_all, - ) - - column_formatting = [".1f", ".4f"] + [".6f"] * 4 - - if show_all: - additional_columns = ["Rho", "Phi", "Charm", "Vanna", "Vomma"] - column_formatting += [".6f"] * len(additional_columns) - - print_rich_table( - df, - headers=list(df.columns), - show_index=False, - title=f"{symbol} Greeks", - floatfmt=column_formatting, - ) diff --git a/openbb_terminal/stocks/quantitative_analysis/__init__.py b/openbb_terminal/stocks/quantitative_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/quantitative_analysis/beta_model.py b/openbb_terminal/stocks/quantitative_analysis/beta_model.py deleted file mode 100644 index 2fd682dca58..00000000000 --- a/openbb_terminal/stocks/quantitative_analysis/beta_model.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Beta model""" - -__docformat__ = "numpy" - -from typing import Optional, Tuple - -import pandas as pd -from scipy import stats - -from openbb_terminal.stocks import stocks_helper - - -def beta_model( - symbol: str, - ref_symbol: str, - data: Optional[pd.DataFrame] = None, - ref_data: Optional[pd.DataFrame] = None, - interval: int = 1440, -) -> Tuple[pd.Series, pd.Series, float, float]: - """Calculate beta for a ticker and a reference ticker. - - Parameters - ---------- - symbol: str - A ticker to calculate beta for - ref_symbol: str - A reference ticker symbol for the beta calculation (default in terminal is SPY) - data: pd.DataFrame - The selected ticker symbols price data - ref_data: pd.DataFrame - The reference ticker symbols price data - interval: int - The interval of the ref_data. This will ONLY be used if ref_data is None - - Returns - ------- - Tuple[pd.Series, pd.Series, float, float] - Stock ticker symbols close-to-close returns, Reference ticker symbols close-to-close returns, beta, alpha - """ - data = ( - stocks_helper.load(symbol=symbol) - if data is None - else data.rename({"close": "Close"}, axis=1) - ) - if ref_data is None: - ref_data = stocks_helper.load( - symbol=ref_symbol, - interval=interval, - start_date=data.index[0], - end_date=data.index[-1], - verbose=False, - ) - if ref_data.empty: - raise Exception("Invalid ref_symbol ticker") - else: - ref_data = ref_data.rename({"close": "Close"}, axis=1) - - # join returns - sr = 100 * data["Close"].pct_change().to_frame() - sr = sr.rename({"Close": "Stock Pct Ret"}, axis=1) - rr = 100 * ref_data["Close"].pct_change().to_frame() - rr = rr.rename({"Close": "Ref Pct Ret"}, axis=1) - df = sr.merge(rr, how="outer", left_index=True, right_index=True) - df = df.dropna() - sr = df["Stock Pct Ret"].tolist() - rr = df["Ref Pct Ret"].tolist() - - # compute lin reg - if not rr or not sr: - return pd.Series(dtype="object"), pd.Series(dtype="object"), 0.0, 0.0 - model = stats.linregress(rr, sr) - beta = model.slope - alpha = model.intercept - - return sr, rr, beta, alpha diff --git a/openbb_terminal/stocks/quantitative_analysis/beta_view.py b/openbb_terminal/stocks/quantitative_analysis/beta_view.py deleted file mode 100644 index 34dcff98614..00000000000 --- a/openbb_terminal/stocks/quantitative_analysis/beta_view.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Beta view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.quantitative_analysis.beta_model import beta_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def beta_view( - symbol: str, - ref_symbol: str, - data: Optional[pd.DataFrame] = None, - ref_data: Optional[pd.DataFrame] = None, - interval: int = 1440, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Display the beta scatterplot + linear regression. - - Parameters - ---------- - symbol : str - A ticker to calculate beta for - ref_symbol : str - A reference ticker symbol for the beta calculation (default in terminal is SPY) - data : pd.DataFrame - The selected ticker symbols price data - ref_data : pd.DataFrame - The reference ticker symbols price data - interval: int - The interval of the ref_data. This will ONLY be used if ref_data is None - export : str - Export dataframe data or plot to csv,json,xlsx,jpeg,pdf,png,svg file - sheet_name : str - Optionally specify the name of the sheet the data is exported to. - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - try: - sr, rr, beta, alpha = beta_model( - symbol, ref_symbol, data, ref_data, interval=interval - ) - except Exception as e: - if str(e) == "Invalid ref ticker": - return console.print(str(e) + "\n") - raise e - - beta_text = f"Raw Beta={round(beta, 2)}
Alpha={round(alpha, 2)}" - - fig = OpenBBFigure( - xaxis_title=f"{ref_symbol} Returns (%)", yaxis_title=f"{symbol} Returns (%)" - ) - fig.set_title(f"Beta of {symbol} with respect to {ref_symbol}") - fig.add_scatter(x=rr, y=sr, mode="markers", name="Returns") - - fig.add_scatter( - x=[min(rr), max(rr)], - y=[x * beta + alpha for x in [min(rr), max(rr)]], - mode="lines", - name="Linear Regression", - ) - fig.add_annotation( - x=0.9, - y=0.1, - text=beta_text, - xref="paper", - yref="paper", - xanchor="right", - yanchor="bottom", - ) - - fig.update_layout(showlegend=False) - - df = pd.DataFrame({"sr": sr, "rr": rr}) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"beta_alpha={alpha}_beta={beta}", - df, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/quantitative_analysis/factors_model.py b/openbb_terminal/stocks/quantitative_analysis/factors_model.py deleted file mode 100644 index ad7f6356e38..00000000000 --- a/openbb_terminal/stocks/quantitative_analysis/factors_model.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Factors model""" - -__docformat__ = "numpy" - -import logging -from io import BytesIO -from typing import Tuple -from urllib.request import urlopen -from zipfile import ZipFile - -import pandas as pd -import statsmodels.api as sm -import yfinance as yf - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_fama_raw() -> pd.DataFrame: - """Gets base Fama French data to calculate risk - - Returns - ------- - fama : pd.DataFrame - A data with fama french model information - """ - url = "http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/ftp/F-F_Research_Data_Factors_CSV.zip" - file = "F-F_Research_Data_Factors.CSV" - with urlopen(url) as data, ZipFile( # noqa: S310 - BytesIO(data.read()) - ) as zipfile, zipfile.open(file) as zip_open: - df = pd.read_csv( - zip_open, - header=0, - names=["Date", "MKT-RF", "SMB", "HML", "RF"], - skiprows=3, - ) - - df = df[df["Date"].apply(lambda x: len(str(x).strip()) == 6)] - df["Date"] = df["Date"].astype(str) + "01" - df["Date"] = pd.to_datetime(df["Date"], format="%Y%m%d") - df["MKT-RF"] = pd.to_numeric(df["MKT-RF"], downcast="float") - df["SMB"] = pd.to_numeric(df["SMB"], downcast="float") - df["HML"] = pd.to_numeric(df["HML"], downcast="float") - df["RF"] = pd.to_numeric(df["RF"], downcast="float") - df["MKT-RF"] = df["MKT-RF"] / 100 - df["SMB"] = df["SMB"] / 100 - df["HML"] = df["HML"] / 100 - df["RF"] = df["RF"] / 100 - df = df.set_index("Date") - return df - - -@log_start_end(log=logger) -def get_historical_5(symbol: str) -> pd.DataFrame: - """Get 5 year monthly historical performance for a ticker with dividends filtered - - Parameters - ---------- - symbol : str - A ticker symbol in string form - - Returns - ------- - data : pd.DataFrame - A dataframe with historical information - """ - tick = yf.Ticker(symbol) - df = tick.history(period="5y", interval="1mo") - df = df[df.index.to_series().apply(lambda x: x.day == 1)] - df = df.drop(["Dividends", "Stock Splits"], axis=1) - df = df.dropna() - df.index = [d.replace(tzinfo=None) for d in df.index] - return df - - -@log_start_end(log=logger) -def capm_information(symbol: str) -> Tuple[float, float]: - """Provides information that relates to the CAPM model - - Parameters - ---------- - symbol : str - A ticker symbol in string form - - Returns - ------- - Tuple[float, float] - The beta for a stock, The systematic risk for a stock - """ - df_f = get_fama_raw() - df_h = get_historical_5(symbol) - df = df_h.join(df_f) - df = df.dropna() - df["Monthly Return"] = df["Close"].pct_change() - df["Excess Monthly Return"] = df["Monthly Return"] - df["RF"] - df["Excess MKT-RF"] = df["MKT-RF"] - df["RF"] - df = df.dropna() - y = df[["Excess Monthly Return"]] - x = df["Excess MKT-RF"] - x = sm.add_constant(x) - model = sm.OLS(y, x).fit() - beta = model.params["Excess MKT-RF"] - sy = model.rsquared - return beta, sy diff --git a/openbb_terminal/stocks/quantitative_analysis/factors_view.py b/openbb_terminal/stocks/quantitative_analysis/factors_view.py deleted file mode 100644 index 701140baa3e..00000000000 --- a/openbb_terminal/stocks/quantitative_analysis/factors_view.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Factors view""" - -__docformat__ = "numpy" - -import logging - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.quantitative_analysis.factors_model import capm_information - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def capm_view(symbol: str) -> None: - """Displays information for the CAPM model. - - Parameters - ---------- - symbol : str - Selected ticker symbol - """ - beta, sy = capm_information(symbol) - console.print(f"Beta:\t\t\t{beta:.2f}") - console.print(f"Systematic Risk:\t{sy*100:.2f}%") - console.print(f"Unsystematic Risk:\t{(1-sy)*100:.2f}%\n") diff --git a/openbb_terminal/stocks/quantitative_analysis/qa_controller.py b/openbb_terminal/stocks/quantitative_analysis/qa_controller.py deleted file mode 100644 index e4be79d0daa..00000000000 --- a/openbb_terminal/stocks/quantitative_analysis/qa_controller.py +++ /dev/null @@ -1,1167 +0,0 @@ -"""Quantitative Analysis Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -from datetime import datetime -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.common.quantitative_analysis import qa_view, rolling_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_list_dates, - check_positive, - check_proportion_range, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks import stocks_helper -from openbb_terminal.stocks.quantitative_analysis.beta_view import beta_view -from openbb_terminal.stocks.quantitative_analysis.factors_view import capm_view -from openbb_terminal.stocks.quantitative_analysis.qa_model import full_stock_df - -# pylint: disable=C0302 - -logger = logging.getLogger(__name__) - - -def no_data_message(): - """Print message when no ticker is loaded""" - console.print("[red]No data loaded. Use 'load' command to load a symbol[/red]") - - -class QaController(StockBaseController): - """Quantitative Analysis Controller class""" - - CHOICES_COMMANDS = [ - "load", - "pick", - "raw", - "summary", - "line", - "hist", - "cdf", - "bw", - "rolling", - "decompose", - "cusum", - "acf", - "spread", - "quantile", - "skew", - "kurtosis", - "normality", - "qqplot", - "unitroot", - "capm", - "beta", - "var", - "es", - "sh", - "so", - "om", - ] - - stock_interval = [1, 5, 15, 30, 60] - stock_sources = ["YahooFinance", "AlphaVantage"] - distributions = ["laplace", "student_t", "logistic", "normal"] - FULLER_REG = ["c", "ct", "ctt", "nc"] - KPS_REG = ["c", "ct"] - VALID_DISTRIBUTIONS = ["laplace", "student_t", "logistic", "normal"] - PATH = "/stocks/qa/" - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - start: datetime, - interval: str, - stock: pd.DataFrame, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - if not stock.empty: - self.stock = full_stock_df(stock) - else: - self.stock = stock - self.ticker = ticker - self.start = start - self.interval = interval - self.target = "returns" if not stock.empty else "" - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - s_intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"] - if self.start: - stock_str = ( - f"{s_intraday} {self.ticker} (from {self.start.strftime('%Y-%m-%d')})" - ) - else: - stock_str = f"{s_intraday} {self.ticker}" - - mt = MenuText("stocks/qa/") - mt.add_cmd("load") - mt.add_cmd("pick", not self.stock.empty) - mt.add_raw("\n") - mt.add_param( - "_ticker", stock_str if not self.stock.empty else "No ticker loaded" - ) - mt.add_param("_target", self.target) - mt.add_raw("\n") - mt.add_info("_statistics_") - mt.add_cmd("summary", not self.stock.empty) - mt.add_cmd("normality", not self.stock.empty) - mt.add_cmd("unitroot", not self.stock.empty) - mt.add_info("_plots_") - mt.add_cmd("line", not self.stock.empty) - mt.add_cmd("hist", not self.stock.empty) - mt.add_cmd("cdf", not self.stock.empty) - mt.add_cmd("bw", not self.stock.empty) - mt.add_cmd("acf", not self.stock.empty) - mt.add_cmd("qqplot", not self.stock.empty) - mt.add_info("_rolling_metrics_") - mt.add_cmd("rolling", not self.stock.empty) - mt.add_cmd("spread", not self.stock.empty) - mt.add_cmd("quantile", not self.stock.empty) - mt.add_cmd("skew", not self.stock.empty) - mt.add_cmd("kurtosis", not self.stock.empty) - mt.add_info("_risk_") - mt.add_cmd("var", not self.stock.empty) - mt.add_cmd("es", not self.stock.empty) - mt.add_cmd("sh", not self.stock.empty) - mt.add_cmd("so", not self.stock.empty) - mt.add_cmd("om", not self.stock.empty) - mt.add_info("_other_") - mt.add_cmd("raw", not self.stock.empty) - mt.add_cmd("decompose", not self.stock.empty) - mt.add_cmd("cusum", not self.stock.empty) - mt.add_cmd("capm", not self.stock.empty) - mt.add_cmd("beta", not self.stock.empty) - console.print(text=mt.menu_text, menu="Stocks - Quantitative Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - if self.target: - return ["stocks", f"load {self.ticker}", "qa", f"pick {self.target}"] - return ["stocks", f"load {self.ticker}", "qa"] - return [] - - @log_start_end(log=logger) - def call_pick(self, other_args: List[str]): - """Process pick command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="pick", - description=""" - Change target variable - """, - ) - parser.add_argument( - "-t", - "--target", - dest="target", - type=str.lower, - choices=list(self.stock.columns), - help="Select variable to analyze", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.stock.empty: - no_data_message() - return - self.target = ns_parser.target - - @log_start_end(log=logger) - def call_raw(self, other_args: List[str]): - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="raw", - description=""" - Print raw data to console - """, - ) - parser.add_argument( - "-l", - "--limit", - help="Number to show", - type=check_positive, - default=20, - dest="limit", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sortby", - help="The column to sort by", - choices=stocks_helper.format_parse_choices(self.stock.columns), - type=str.lower, - dest="sortby", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - qa_view.display_raw( - data=self.stock, - limit=ns_parser.limit, - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_summary(self, other_args: List[str]): - """Process summary command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="summary", - description=""" - Summary statistics - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - qa_view.display_summary( - data=self.stock, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_line(self, other_args: List[str]): - """Process line command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="line", - description="Show line plot of selected data or highlight specific datetimes.", - ) - parser.add_argument( - "--log", - help="Plot with y on log scale", - dest="log", - action="store_true", - default=False, - ) - parser.add_argument( - "--ml", - help="Draw vertical line markers to highlight certain events", - dest="ml", - type=check_list_dates, - default="", - ) - parser.add_argument( - "--ms", - help="Draw scatter markers to highlight certain events", - dest="ms", - type=check_list_dates, - default="", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - qa_view.display_line( - self.stock[self.target], - title=f"{self.ticker} {self.target}", - log_y=ns_parser.log, - markers_lines=ns_parser.ml, - markers_scatter=ns_parser.ms, - export=ns_parser.export, - ) - - @log_start_end(log=logger) - def call_hist(self, other_args: List[str]): - """Process hist command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="hist", - description=""" - Histogram with density and rug - """, - ) - parser.add_argument( - "-b", "--bins", type=check_positive, default=15, dest="n_bins" - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.stock.empty: - no_data_message() - return - qa_view.display_hist( - symbol=self.ticker, - data=self.stock, - target=self.target, - bins=ns_parser.n_bins, - ) - - @log_start_end(log=logger) - def call_cdf(self, other_args: List[str]): - """Process cdf command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="cdf", - description=""" - Cumulative distribution function - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - qa_view.display_cdf( - data=self.stock, - symbol=self.ticker, - target=self.target, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bw(self, other_args: List[str]): - """Process bwy command""" - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - add_help=False, - prog="bw", - description=""" - Box and Whisker plot - """, - ) - parser.add_argument( - "-y", - "--yearly", - action="store_true", - default=False, - dest="year", - help="Flag to show yearly bw plot", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.stock.empty: - no_data_message() - return - qa_view.display_bw( - data=self.stock, - symbol=self.ticker, - target=self.target, - yearly=ns_parser.year, - ) - - @log_start_end(log=logger) - def call_decompose(self, other_args: List[str]): - """Process decompose command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="decompose", - description=""" - Decompose time series as: - - Additive Time Series = Level + CyclicTrend + Residual + Seasonality - - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality - """, - ) - parser.add_argument( - "-m", - "--multiplicative", - action="store_true", - default=False, - dest="multiplicative", - help="decompose using multiplicative model instead of additive", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - qa_view.display_seasonal( - symbol=self.ticker, - data=self.stock, - target=self.target, - multiplicative=ns_parser.multiplicative, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cusum(self, other_args: List[str]): - """Process cusum command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cusum", - description=""" - Cumulative sum algorithm (CUSUM) to detect abrupt changes in data - """, - ) - parser.add_argument( - "-t", - "--threshold", - dest="threshold", - type=float, - default=( - ( - max(self.stock[self.target].values) - - min(self.stock[self.target].values) - ) - / 40 - if not self.stock.empty - else 0 - ), - help="threshold", - ) - parser.add_argument( - "-d", - "--drift", - dest="drift", - type=float, - default=( - ( - max(self.stock[self.target].values) - - min(self.stock[self.target].values) - ) - / 80 - if not self.stock.empty - else 0 - ), - help="drift", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.stock.empty: - no_data_message() - return - qa_view.display_cusum( - data=self.stock, - target=self.target, - threshold=ns_parser.threshold, - drift=ns_parser.drift, - ) - - @log_start_end(log=logger) - def call_acf(self, other_args: List[str]): - """Process acf command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="acf", - description=""" - Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff stock data - """, - ) - parser.add_argument( - "-l", - "--lags", - dest="lags", - type=check_positive, - default=15, - help="maximum lags to display in plots", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if self.stock.empty: - no_data_message() - return - if self.target != "adjclose": - console.print( - "Target not adjclose. For best results, use `pick adjclose` first." - ) - - qa_view.display_acf( - data=self.stock, - symbol=self.ticker, - target=self.target, - lags=ns_parser.lags, - ) - - @log_start_end(log=logger) - def call_rolling(self, other_args: List[str]): - """Process rolling command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rolling", - description=""" - Rolling mean and std deviation - """, - ) - parser.add_argument( - "-w", - "--window", - action="store", - dest="n_window", - type=check_positive, - default=14, - help="Window length", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - rolling_view.display_mean_std( - symbol=self.ticker, - data=self.stock, - target=self.target, - window=ns_parser.n_window, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_spread(self, other_args: List[str]): - """Process spread command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="spread", - description="""Shows rolling spread measurement - """, - ) - parser.add_argument( - "-w", - "--window", - action="store", - dest="n_window", - type=check_positive, - default=14, - help="Window length", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if self.stock.empty: - no_data_message() - return - rolling_view.display_spread( - data=self.stock, - symbol=self.ticker, - target=self.target, - window=ns_parser.n_window, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_quantile(self, other_args: List[str]): - """Process quantile command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="quantile", - description=""" - The quantiles are values which divide the distribution such that - there is a given proportion of observations below the quantile. - For example, the median is a quantile. The median is the central - value of the distribution, such that half the points are less than - or equal to it and half are greater than or equal to it. - - By default, q is set at 0.5, which effectively is median. Change q to - get the desired quantile (0 pd.DataFrame: - df["Returns"] = df["Adj Close"].pct_change() - df["LogRet"] = np.log(df["Adj Close"]) - np.log(df["Adj Close"].shift(1)) - df["LogPrice"] = np.log(df["Adj Close"]) - df = df.rename(columns={"Adj Close": "AdjClose"}) - df = df.dropna() - df.columns = [x.lower() for x in df.columns] - return df diff --git a/openbb_terminal/stocks/research/__init__.py b/openbb_terminal/stocks/research/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/research/res_controller.py b/openbb_terminal/stocks/research/res_controller.py deleted file mode 100644 index 7fd1492823a..00000000000 --- a/openbb_terminal/stocks/research/res_controller.py +++ /dev/null @@ -1,199 +0,0 @@ -"""Research Controller Module""" - -__docformat__ = "numpy" - -import logging -import webbrowser -from datetime import datetime -from typing import List, Optional - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console - -logger = logging.getLogger(__name__) - - -class ResearchController(BaseController): - """Research Controller class""" - - CHOICES_COMMANDS = [ - "barchart", - "businessinsider", - "bullrun", - "fidelity", - "fintel", - "finviz", - "fmp", - "fool", - "grufity", - "macroaxis", - "macrotrends", - "marketchameleon", - "marketwatch", - "newsfilter", - "stockanalysis", - "stockrow", - "tradingview", - "yahoo", - "zacks", - ] - PATH = "/stocks/res/" - - def __init__( - self, - ticker: str, - start: datetime, - interval: str, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - self.start = start - self.interval = interval - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("stocks/res/", 50) - mt.add_param("_ticker", self.ticker.upper()) - mt.add_raw("\n") - mt.add_cmd("barchart") - mt.add_cmd("businessinsider") - mt.add_cmd("bullrun") - mt.add_cmd("fidelity") - mt.add_cmd("fintel") - mt.add_cmd("finviz") - mt.add_cmd("fmp") - mt.add_cmd("fool") - mt.add_cmd("grufity") - mt.add_cmd("macroaxis") - mt.add_cmd("macrotrends") - mt.add_cmd("marketchameleon") - mt.add_cmd("marketwatch") - mt.add_cmd("newsfilter") - mt.add_cmd("stockanalysis") - mt.add_cmd("stockrow") - mt.add_cmd("tradingview") - mt.add_cmd("yahoo") - mt.add_cmd("zacks") - console.print(text=mt.menu_text, menu="Stocks - Research") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - return ["stocks", f"load {self.ticker}", "res"] - return [] - - @log_start_end(log=logger) - def call_macroaxis(self, _): - """Process macroaxis command""" - webbrowser.open(f"https://www.macroaxis.com/invest/market/{self.ticker}") - - @log_start_end(log=logger) - def call_yahoo(self, _): - """Process yahoo command""" - webbrowser.open(f"https://finance.yahoo.com/quote/{self.ticker}") - - @log_start_end(log=logger) - def call_finviz(self, _): - """Process finviz command""" - webbrowser.open(f"https://finviz.com/quote.ashx?t={self.ticker}") - - @log_start_end(log=logger) - def call_bullrun(self, _): - """Process bullrun command""" - webbrowser.open(f"https://bullrun.com.br/acoes/{self.ticker}") - - @log_start_end(log=logger) - def call_marketwatch(self, _): - """Process marketwatch command""" - webbrowser.open(f"https://www.marketwatch.com/investing/stock/{self.ticker}") - - @log_start_end(log=logger) - def call_fool(self, _): - """Process fool command""" - webbrowser.open(f"https://www.fool.com/quote/{self.ticker}") - - @log_start_end(log=logger) - def call_businessinsider(self, _): - """Process businessinsider command""" - webbrowser.open( - f"https://markets.businessinsider.com/stocks/{self.ticker}-stock/" - ) - - @log_start_end(log=logger) - def call_fmp(self, _): - """Process fmp command""" - webbrowser.open( - f"https://financialmodelingprep.com/financial-summary/{self.ticker}" - ) - - @log_start_end(log=logger) - def call_fidelity(self, _): - """Process fidelity command""" - webbrowser.open( - f"https://eresearch.fidelity.com/eresearch/goto/evaluate/snapshot.jhtml?symbols={self.ticker}" - ) - - @log_start_end(log=logger) - def call_tradingview(self, _): - """Process tradingview command""" - webbrowser.open(f"https://www.tradingview.com/symbols/{self.ticker}") - - @log_start_end(log=logger) - def call_marketchameleon(self, _): - """Process marketchameleon command""" - webbrowser.open(f"https://marketchameleon.com/Overview/{self.ticker}") - - @log_start_end(log=logger) - def call_stockrow(self, _): - """Process stockrow command""" - webbrowser.open(f"https://stockrow.com/{self.ticker}") - - @log_start_end(log=logger) - def call_barchart(self, _): - """Process barchart command""" - webbrowser.open( - f"https://www.barchart.com/stocks/quotes/{self.ticker}/overview" - ) - - @log_start_end(log=logger) - def call_grufity(self, _): - """Process grufity command""" - webbrowser.open(f"https://grufity.com/stock/{self.ticker}") - - @log_start_end(log=logger) - def call_fintel(self, _): - """Process fintel command""" - webbrowser.open(f"https://fintel.io/s/us/{self.ticker}") - - @log_start_end(log=logger) - def call_zacks(self, _): - """Process zacks command""" - webbrowser.open(f"https://www.zacks.com/stock/quote/{self.ticker}") - - @log_start_end(log=logger) - def call_macrotrends(self, _): - """Process macrotrends command""" - webbrowser.open( - f"https://www.macrotrends.net/stocks/charts/{self.ticker}/{self.ticker}/market-cap" - ) - - @log_start_end(log=logger) - def call_newsfilter(self, _): - """Process newsfilter command""" - webbrowser.open(f"https://newsfilter.io/search?query={self.ticker}") - - @log_start_end(log=logger) - def call_stockanalysis(self, _): - """Process stockanalysis command""" - webbrowser.open(f"https://stockanalysis.com/stocks/{self.ticker}/") diff --git a/openbb_terminal/stocks/screener/__init__.py b/openbb_terminal/stocks/screener/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/screener/finviz_model.py b/openbb_terminal/stocks/screener/finviz_model.py deleted file mode 100644 index ff9eb0060a8..00000000000 --- a/openbb_terminal/stocks/screener/finviz_model.py +++ /dev/null @@ -1,1974 +0,0 @@ -import configparser -import logging - -import pandas as pd -from finvizfinance.screener import ( - financial, - overview, - ownership, - performance, - technical, - valuation, -) - -from openbb_terminal.core.config.paths import ( - MISCELLANEOUS_DIRECTORY, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -PRESETS_PATH = ( - get_current_user().preferences.USER_PRESETS_DIRECTORY / "stocks" / "screener" -) -PRESETS_PATH_DEFAULT = MISCELLANEOUS_DIRECTORY / "stocks" / "screener" -preset_choices = {} - -if PRESETS_PATH.exists(): - preset_choices.update( - { - filepath.name.strip(".ini"): filepath - for filepath in PRESETS_PATH.iterdir() - if filepath.suffix == ".ini" - } - ) - -if PRESETS_PATH_DEFAULT.exists(): - preset_choices.update( - { - filepath.name.strip(".ini"): filepath - for filepath in PRESETS_PATH_DEFAULT.iterdir() - if filepath.suffix == ".ini" - } - ) - -# pylint: disable=C0302 - -d_signals = { - "top_gainers": "Top Gainers", - "top_losers": "Top Losers", - "new_high": "New High", - "new_low": "New Low", - "most_volatile": "Most Volatile", - "most_active": "Most Active", - "unusual_volume": "Unusual Volume", - "overbought": "Overbought", - "oversold": "Oversold", - "downgrades": "Downgrades", - "upgrades": "Upgrades", - "earnings_before": "Earnings Before", - "earnings_after": "Earnings After", - "recent_insider_buying": "Recent Insider Buying", - "recent_insider_selling": "Recent Insider Selling", - "major_news": "Major News", - "horizontal_sr": "Horizontal S/R", - "tl_resistance": "TL Resistance", - "tl_support": "TL Support", - "wedge_up": "Wedge Up", - "wedge_down": "Wedge Down", - "wedge": "Wedge", - "triangle_ascending": "Triangle Ascending", - "triangle_descending": "Triangle Descending", - "channel_up": "Channel Up", - "channel_down": "Channel Down", - "channel": "Channel", - "double_top": "Double Top", - "double_bottom": "Double Bottom", - "multiple_top": "Multiple Top", - "multiple_bottom": "Multiple Bottom", - "head_shoulders": "Head & Shoulders", - "head_shoulders_inverse": "Head & Shoulders Inverse", -} - - -@log_start_end(log=logger) -def get_screener_data( - preset_loaded: str = "top_gainers", - data_type: str = "overview", - limit: int = -1, - ascend: bool = False, -): - """Screener Overview - - Parameters - ---------- - preset_loaded : str - Loaded preset filter - data_type : str - Data type between: overview, valuation, financial, ownership, performance, technical - limit : int - Limit of stocks filtered with presets to print - ascend : bool - Ascended order of stocks filtered to print - - Returns - ------- - pd.DataFrame - Dataframe with loaded filtered stocks - """ - df_screen = pd.DataFrame() - screen_type = { - "overview": overview.Overview, - "valuation": valuation.Valuation, - "financial": financial.Financial, - "ownership": ownership.Ownership, - "performance": performance.Performance, - "technical": technical.Technical, - } - if data_type in screen_type: - screen = screen_type[data_type]() - else: - console.print("Invalid selected screener type") - return pd.DataFrame() - - if preset_loaded in d_signals: - screen.set_filter(signal=d_signals[preset_loaded]) - - try: - if limit > 0: - df_screen = screen.screener_view(limit=limit, ascend=ascend) - else: - df_screen = screen.screener_view(ascend=ascend) - except IndexError: - console.print("[red]Invalid data provided by the website[/red]\n") - return pd.DataFrame() - except AttributeError: - console.print("[red]Invalid data provided by the website[/red]\n") - return pd.DataFrame() - - else: - preset_filter = configparser.RawConfigParser() - preset_filter.optionxform = str # type: ignore - preset_filter.read(preset_choices[preset_loaded]) - - d_general = preset_filter["General"] - d_filters = { - **preset_filter["Descriptive"], - **preset_filter["Fundamental"], - **preset_filter["Technical"], - } - - for section in ["General", "Descriptive", "Fundamental", "Technical"]: - for key, val in {**preset_filter[section]}.items(): - if key not in d_check_screener: - console.print( - f"The screener variable {section}.{key} shouldn't exist!\n" - ) - return pd.DataFrame() - - if val not in d_check_screener[key]: - console.print( - f"Invalid [{section}] {key}={val}. " - f"Choose one of the following options:\n{', '.join(d_check_screener[key])}.\n" - ) - return pd.DataFrame() - - d_filters = {k: v for k, v in d_filters.items() if v} - - screen.set_filter(filters_dict=d_filters) - - if "Order" in d_general: - if limit > 0: - df_screen = screen.screener_view( - order=d_general["Order"], - limit=limit, - ascend=ascend, - ) - else: - df_screen = screen.screener_view( - order=d_general["Order"], ascend=ascend - ) - - elif limit > 0: - df_screen = screen.screener_view(limit=limit, ascend=ascend) - else: - df_screen = screen.screener_view(ascend=ascend) - - df_screen.columns = [val.strip("\n") for val in df_screen.columns] - if "Company" in df_screen.columns: - df_screen["Company"] = df_screen["Company"].str.replace(",", "") - if data_type == "performance": - df_screen = df_screen.rename( - columns={ - "Perf Week": "1W", - "Perf Month": "1M", - "Perf Quart": "3M", - "Perf Half": "6M", - "Perf Year": "1Y", - "Perf YTD": "YTD", - "Volatility W": "1W Volatility", - "Volatility M": "1M Volatility", - } - ) - - return df_screen - - -d_signals_desc = { - "top_gainers": "stocks with the highest %% price gain today", - "top_losers": "stocks with the highest %% price loss today", - "new_high": "stocks making 52-week high today", - "new_low": "stocks making 52-week low today", - "most_volatile": "stocks with the highest widest high/low trading range today", - "most_active": "stocks with the highest trading volume today", - "unusual_volume": "stocks with unusually high volume today - the highest relative volume ratio", - "overbought": "stock is becoming overvalued and may experience a pullback.", - "oversold": "oversold stocks may represent a buying opportunity for investors", - "downgrades": "stocks downgraded by analysts today", - "upgrades": "stocks upgraded by analysts today", - "earnings_before": "companies reporting earnings today, before market open", - "earnings_after": "companies reporting earnings today, after market close", - "recent_insider_buying": "stocks with recent insider buying activity", - "recent_insider_selling": "stocks with recent insider selling activity", - "major_news": "stocks with the highest news coverage today", - "horizontal_sr": "horizontal channel of price range between support and resistance trendlines", - "tl_resistance": "once a rising trendline is broken", - "tl_support": "once a falling trendline is broken", - "wedge_up": "upward trendline support and upward trendline resistance (reversal)", - "wedge_down": "downward trendline support and downward trendline resistance (reversal)", - "wedge": "upward trendline support, downward trendline resistance (contiunation)", - "triangle_ascending": "upward trendline support and horizontal trendline resistance", - "triangle_descending": "horizontal trendline support and downward trendline resistance", - "channel_up": "both support and resistance trendlines slope upward", - "channel_down": "both support and resistance trendlines slope downward", - "channel": "both support and resistance trendlines are horizontal", - "double_top": "stock with 'M' shape that indicates a bearish reversal in trend", - "double_bottom": "stock with 'W' shape that indicates a bullish reversal in trend", - "multiple_top": "same as double_top hitting more highs", - "multiple_bottom": "same as double_bottom hitting more lows", - "head_shoulders": "chart formation that predicts a bullish-to-bearish trend reversal", - "head_shoulders_inverse": "chart formation that predicts a bearish-to-bullish trend reversal", -} - - -d_check_screener = { - "Order": [ - "Any", - "Signal", - "Ticker", - "Company", - "Sector", - "Industry", - "Country", - "Market Cap.", - "Price/Earnings", - "Forward Price/Earnings", - "PEG (Price/Earnings/Growth)", - "Price/Sales", - "Price/Book", - "Price/Cash", - "Price/Free Cash Flow", - "Dividend Yield", - "Payout Ratio", - "EPS(ttm)", - "EPS growth this year", - "EPS growth next year", - "EPS growth past 5 years", - "EPS growth next 5 years", - "Sales growth past 5 years", - "EPS growth qtr over qtr", - "Sales growth qtr over qtr", - "Shares Outstanding", - "Shares Float", - "Insider Ownership", - "Insider Transactions", - "Institutional Ownership", - "Institutional Transactions", - "Short Interest Share", - "Short Interest Ratio", - "Earnings Date", - "Return on Assets", - "Return on Equity", - "Return on Investment", - "Current Ratio", - "Quick Ratio", - "LT Debt/Equity", - "Total Debt/Equity", - "Gross Margin", - "Operating Margin", - "Net Profit Margin", - "Analyst Recommendation", - "Performance (Week)", - "Performance (Month)", - "Performance (Quarter)", - "Performance (Half Year)", - "Performance (Year)", - "Performance (Year To Date)", - "Beta", - "Average True Range", - "Volatility (Week)", - "Volatility (Month)", - "20-Day SMA (Relative)", - "50-Day SMA (Relative)", - "200-Day SMA (Relative)", - "50-Day High (Relative)", - "50-Day Low (Relative)", - "52-Week High (Relative)", - "52-Week Low (Relative)", - "Relative Strength Index (14)", - "Average Volume (3 Month)", - "Relative Volume", - "Change", - "Change from Open", - "Gap", - "Volume", - "Price", - "Target Price", - "IPO Date", - ], - "Signal": [ - "Any", - "Top Gainers", - "Top Losers", - "New High", - "New Low", - "Most Volatile", - "Most Active", - "Unusual Volume", - "Overbought", - "Oversold", - "Downgrades", - "Upgrades", - "Earnings Before", - "Earnings After", - "Recent Insider Buying", - "Recent Insider Selling", - "Major News", - "Horizontal S/R", - "TL Resistance", - "TL Support", - "Wedge Up", - "Wedge Down", - "Triangle Ascending", - "Triangle Descending", - "Wedge", - "Channel Up", - "Channel Down", - "Channel", - "Double Top", - "Double Bottom", - "Multiple Top", - "Multiple Bottom", - "Head & Shoulders", - "Head & Shoulders Inverse", - ], - "Exchange": ["Any", "AMEX", "NASDAQ", "NYSE"], - "Market Cap.": [ - "Any", - "Mega ($200bln and more)", - "Large ($10bln to $200bln)", - "Mid ($2bl to $10bln)", - "Small ($300mln to $2bln)", - "Micro ($50mln to $300mln)", - "Nano (under $50mln)", - "+Large (over $50mln)", - "+Mid (over $2bln)", - "+Small (over $300mln)", - "+Micro (over $50mln)", - "-Large (under $200bln)", - "-Mid (under $10bln)", - "-Small (under $2bln)", - "-Micro (under $300mln)", - ], - "Earnings Date": [ - "Any", - "Today", - "Today Before Market Open", - "Today Before Market Close", - "Tomorrow", - "Tomorrow Before Market Open", - "Tomorrow Before Market Close", - "Yesterday", - "Yesterday Before Market Open", - "Yesterday Before Market Close", - "Next 5 Days", - "Previous 5 Days", - "This Week", - "Next Week", - "Previous Week", - "This Month", - ], - "Target Price": [ - "Any", - "50% Above Price", - "40% Above Price", - "30% Above Price", - "20% Above Price", - "10% Above Price", - "5% Above Price", - "Above Price", - "Below Price", - "5% Below Price", - "10% Below Price", - "20% Below Price", - "30% Below Price", - "40% Below Price", - "50% Below Price", - ], - "Index": ["Any", "S&P 500", "DJIA"], - "Dividend Yield": [ - "Any", - "None (0%)", - "Positive (>0%)", - "High (>5%)", - "Very High (>10%)", - "Over 1%", - "Over 2%", - "Over 3%", - "Over 4%", - "Over 5%", - "Over 6%", - "Over 7%", - "Over 8%", - "Over 9%", - "Over 10%", - ], - "Average Volume": [ - "Any", - "Under 50K", - "Under 100K", - "Under 500K", - "Under 750K", - "Under 1M", - "Over 50K", - "Over 100K", - "Over 200K", - "Over 300K", - "Over 400K", - "Over 500K", - "Over 750K", - "Over 1M", - "Over 2M", - "100K to 500K", - "100K to 1M", - "500K to 1M", - "500K to 10M", - ], - "IPO Date": [ - "Any", - "Today", - "Yesterday", - "In the last week", - "In the last month", - "In the last quarter", - "In the last year", - "In the last 2 years", - "In the last 3 years", - "In the last 5 years", - "More than a year ago", - "More that 5 years ago", - "More than 10 years ago", - "More than 15 years ago", - "More than 20 years ago", - "More than 25 years ago", - ], - "Sector": [ - "Any", - "Basic Materials", - "Communication Services", - "Consumer Cyclical", - "Consumer Defensive", - "Energy", - "Financial", - "Healthcare", - "Industrials", - "Real Estate", - "Technology", - "Utilities", - ], - "Float Short": [ - "Any", - "Low (<5%)", - "High(>20%)", - "Under 5%", - "Under 10%", - "Under 15%", - "Under 20%", - "Under 25%", - "Under 30%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - ], - "Relative Volume": [ - "Any", - "Over 10", - "Over 5", - "Over 3", - "Over 2", - "Over 1.5", - "Over 1", - "Over 0.75", - "Over 0.5", - "Over 0.25", - "Under 2", - "Under 1", - "Under 1.5", - "Under 1", - "Under 0.75", - "Under 0.5", - "Under 0.25", - "Under 0.1", - ], - "Shares Outstanding": [ - "Any", - "Under 1M", - "Under 5M", - "Under 10M", - "Under 20M", - "Under 50M", - "Under 100M", - "Over 1M", - "Over 2M", - "Over 5M", - "Over 10M", - "Over 20M", - "Over 50M", - "Over 100M", - "Over 200M", - "Over 500M", - "Over 1000M", - ], - "Industry": [ - "Any", - "Agricultural Inputs", - "Aluminium", - "Building Materials", - "Chemicals", - "Coking Coal", - "Copper", - "Gold", - "Lumber & Wood Production", - "Other Industrial Metals & Mining", - "Other Precious Metals & Mining", - "Paper & Paper Products", - "Silver", - "Specialty Chemicals", - "Steel", - ], - "Analyst Recom.": [ - "Any", - "Strong Buy (1)", - "Buy or better", - "Buy", - "Hold or better", - "Hold", - "Hold or worse", - "Sell", - "Sell or worse", - "Strong Sell (5)", - ], - "Current Volume": [ - "Any", - "Under 100K", - "Under 500K", - "Under 750K", - "Under 1M", - "Over 0", - "Over 50K", - "Over 100K", - "Over 200K", - "Over 300K", - "Over 400K", - "Over 500K", - "Over 750K", - "Over 1M", - "Over 2M", - "Over 5M", - "Over 10M", - "Over 20M", - ], - "Float": [ - "Any", - "Under 1M", - "Under 5M", - "Under 10M", - "Under 20M", - "Under 50M", - "Under 100M", - "Over 1M", - "Over 2M", - "Over 5M", - "Over 10M", - "Over 20M", - "Over 50M", - "Over 100M", - "Over 200M", - "Over 500M", - "Over 1000M", - ], - "Country": [ - "Any", - "Asia", - "Europe", - "Latin America", - "BRIC", - "Argentina", - "Australia", - "Bahamas", - "Belgium", - "BeNeLux", - "Bermuda", - "Brazil", - "Canada", - "Cayman Islands", - "Chile", - "China", - "China & Hong Kong", - "Colombia", - "Cyprus", - "Denmark", - "Finland", - "France", - "Germany", - "Greece", - "Hong Kong", - "Hungary", - "Iceland", - "Iceland", - "India", - "Indonesia", - "Ireland", - "Israel", - "Italy", - "Japan", - "Kazakhstan", - "Luxembourg", - "Malaysia", - "Malta", - "Mexico", - "Monaco", - "Netherlands", - "New Zealand", - "Norway", - "Panama", - "Peru", - "Philippines", - "Portugal", - "Russia", - "Singapore", - "South Africa", - "South Korea", - "Spain", - "Sweden", - "Switzerland", - "Taiwan", - "Turkey", - "United Arab Emirates", - "United Kingdom", - "Uruguay", - "USA", - "Foreign (ex-USA)", - ], - "Option/Short": ["Any", "Optionable", "Shortable", "Optionable and shortable"], - "Price": [ - "Any", - "Under $1", - "Under $2", - "Under $3", - "Under $4", - "Under $5", - "Under $7", - "Under $10", - "Under $15", - "Under $20", - "Under $30", - "Under $40", - "Under $50", - "Over $1", - "Over $2", - "Over $3", - "Over $4", - "Over $5", - "Over $7", - "Over $10", - "Over $15", - "Over $20", - "Over $30", - "Over $40", - "Over $50", - "Over $60", - "Over $70", - "Over $80", - "Over $90", - "Over $100", - "$1 to $5", - "$1 to $10", - "$1 to $20", - "$5 to %10", - "$5 to $20", - "$5 to $50", - "$10 to $20", - "$10 to $50", - "$20 to $50", - "$50 to $100", - ], - "P/E": [ - "Any", - "Low (<15)", - "Profitable (>0)", - "High (>50)", - "Under 5", - "Under 10", - "Under 15", - "Under 20", - "Under 25", - "Under 30", - "Under 35", - "Under 40", - "Under 45", - "Under 50", - "Over 5", - "Over 10", - "Over 15", - "Over 25", - "Over 30", - "Over 35", - "Over 40", - "Over 45", - "Over 50", - ], - "Price/Cash": [ - "Any", - "Low (<3)", - "High (>50)", - "Under 1", - "Under 2", - "Under 3", - "Under 4", - "Under 5", - "Under 6", - "Under 7", - "Under 8", - "Under 9", - "Under 10", - "Over 1", - "Over 2", - "Over 3", - "Over 4", - "Over 5", - "Over 6", - "Over 7", - "Over 8", - "Over 9", - "Over 10", - "Over 20", - "Over 30", - "Over 40", - "Over 50", - ], - "EPS growthnext 5 years": [ - "Any", - "Negative (<0%)", - "Positive (>0%)", - "Positive Low (<10%)", - "High (>25%)", - "Under 5%", - "Under 10%", - "Under 15%", - "Under 20%", - "Under 25%", - "Under 30%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - ], - "Return on Equity": [ - "Any", - "Positive (>0%)", - "Negative (<0%)", - "Very Positive (>30%)", - "Very Negative (<-15%)", - "Under -50%", - "Under -40%", - "Under -35%", - "Under -30%", - "Under -25%", - "Under -20%", - "Under -15%", - "Under -10%", - "Under -5%", - "Over +50%", - "Over +45%", - "Over +40%", - "Over +35%", - "Over +30%", - "Over +25%", - "Over +20%", - "Over +15%", - "Over +10%", - "Over +5%", - ], - "Debt/Equity": [ - "Any", - "High (>0.5)", - "Low (<0.1)", - "Under 1", - "Under 0.9", - "Under 0.8", - "Under 0.7", - "Under 0.6", - "Under 0.5", - "Under 0.4", - "Under 0.3", - "Under 0.2", - "Under 0.1", - "Over 0.1", - "Over 0.2", - "Over 0.3", - "Over 0.4", - "Over 0.5", - "Over 0.6", - "Over 0.7", - "Over 0.8", - "Over 0.9", - "Over 1", - ], - "InsiderOwnership": [ - "Any", - "Low (<5%)", - "High (>30%)", - "Very High (>50%)", - "Over 10%", - "Over 20%", - "Over 30%", - "Over 40%", - "Over 50%", - "Over 60%", - "Over 70%", - "Over 80%", - "Over 90%", - ], - "Forward P/E": [ - "Any", - "Low (<15)", - "Profitable (>0)", - "High (>50)", - "Under 5", - "Under 10", - "Under 15", - "Under 20", - "Under 25", - "Under 30", - "Under 35", - "Under 40", - "Under 45", - "Under 50", - "Over 5", - "Over 10", - "Over 15", - "Over 20", - "Over 25", - "Over 30", - "Over 35", - "Over 40", - "Over 45", - "Over 50", - ], - "Price/Free Cash Flow": [ - "Any", - "Low (<15)", - "High (>50)", - "Under 5", - "Under 10", - "Under 15", - "Under 20", - "Under 25", - "Under 30", - "Under 35", - "Under 40", - "Under 45", - "Under 50", - "Under 60", - "Under 70", - "Under 80", - "Under 90", - "Under 100", - "Over 5", - "Over 10", - "Over 15", - "Over 20", - "Over 25", - "Over 30", - "Over 35", - "Over 40", - "Over 45", - "Over 50", - "Over 60", - "Over 70", - "Over 80", - "Over 90", - "Over 100", - ], - "Sales growthpast 5 years": [ - "Any", - "Negative (<0%)", - "Positive (>0%)", - "Positive Low (0-10%)", - "High (>25%)", - "Under 5%", - "Under 10%", - "Under 15%", - "Under 20%", - "Under 25%", - "Under 30%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - ], - "Return on Investment": [ - "Any", - "Positive (>0%)", - "Negative (<0%)", - "Very Positive (>25%)", - "Very Negative (<-10%)", - "Under -50%", - "Under -45%", - "Under -40%", - "Under -35%", - "Under -30%", - "Under -25%", - "Under -20%", - "Under -15%", - "Under -10%", - "Under -5%", - "Over +5%", - "Over +10%", - "Over +15%", - "Over +20%", - "Over +25%", - "Over +30%", - "Over +35%", - "Over +40%", - "Over +45%", - "Over +50%", - ], - "Gross Margin": [ - "Any", - "Positive (>0%)", - "Negative (<0%)", - "High (>50%)", - "Under 90%", - "Under 80%", - "Under 70%", - "Under 60%", - "Under 50%", - "Under 45%", - "Under 40%", - "Under 35%", - "Under 30%", - "Under 25%", - "Under 20%", - "Under 15%", - "Under 10%", - "Under 5%", - "Under 0%", - "Under -10%", - "Under -20%", - "Under -30%", - "Under -50%", - "Under -70%", - "Under -100%", - "Over 0%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - "Over 40%", - "Over 45%", - "Over 50%", - "Over 60%", - "Over 70%", - "Over 80%", - "Over 90%", - ], - "InsiderTransactions": [ - "Any", - "Very Negative (<20%)", - "Negative (<0%)", - "Positive (>0%)", - "Very Positive (>20%)", - "Under -90%", - "Under 80%", - "Under 70%", - "Under -60%", - "Under -50%", - "Under -45%", - "Under 40%", - "Under -35%", - "Under -30%", - "Under -25%", - "Under -20%", - "Under -15%", - "Under -10%", - "Under -5%", - "Over +5%", - "Over +10%", - "Over +15%", - "Over +20%", - "Over +25%", - "Over +30%", - "Over +35%", - "Over +40%", - "Over +45%", - "Over +50%", - "Over +60%", - "Over +70%", - "Over +80%", - "Over +90%", - ], - "PEG": [ - "Any", - "Low (<1)", - "High (>2)", - "Under 1", - "Under 2", - "Under 3", - "Over 1", - "Over 2", - "Over 3", - ], - "EPS growththis year": [ - "Any", - "Negative (<0%)", - "Positive (>0%)", - "Positive Low (0-10%)", - "High (>25%)", - "Under 5%", - "Under 10%", - "Under 15%", - "Under 20%", - "Under 25%", - "Under 30%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - ], - "EPS growthqtr over qtr": [ - "Any", - "Negative (<0%)", - "Positive (>0%)", - "Positive Low (0-10%)", - "High (>25%)", - "Under 5%", - "Under 10%", - "Under 15%", - "Under 20%", - "Under 25%", - "Under 30%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - ], - "Current Ratio": [ - "Any", - "High (>3)", - "Low (<1)", - "Under 1", - "Under 0.5", - "Over 0.5", - "Over 1", - "Over 1.5", - "Over 2", - "Over 3", - "Over 4", - "Over 5", - "Over 10", - ], - "Operating Margin": [ - "Any", - "Negative (<0%)", - "Positive (>0%)", - "Very Negative (<-20%)", - "High (>25%)", - "Under 90%", - "Under 80%", - "Under 70%", - "Under 60%", - "Under 50%", - "Under 45%", - "Under 40%", - "Under 35%", - "Under 30%", - "Under 25%", - "Under 20%", - "Under 15%", - "Under 10%", - "Under 5%", - "Under 0%", - "Under -10%", - "Under -20%", - "Under -30%", - "Under -50%", - "Under -70%", - "Under -100%", - "Over 0%", - "Over 10%", - "Under 15%", - "Over 20%", - "Over 25%", - "Over 30%", - "Over 35%", - "Over 40%", - "Over 45%", - "Over 50%", - "Over 60%", - "Over 70%", - "Over 80%", - "Over 90%", - ], - "InstitutionalOwnership": [ - "Any", - "Low (<5%)", - "High (>90%)", - "Under 90%", - "Under 80%", - "Under 70%", - "Under 60%", - "Under 50%", - "Under 40%", - "Under 30%", - "Under 20%", - "Under 10%", - "Over 10%", - "Over 20%", - "Over 30%", - "Over 40%", - "Over 50%", - "Over 60%", - "Over 70%", - "Over 80%", - "Over 90%", - ], - "P/S": [ - "Any", - "Low (<1)", - "High (>10)", - "Under 1", - "Under 2", - "Under 3", - "Under 4", - "Under 5", - "Under 6", - "Under 7", - "Under 8", - "Under 9", - "Under 10", - "Over 1", - "Over 2", - "Over 3", - "Over 4", - "Over 5", - "Over 6", - "Over 6", - "Over 7", - "Over 8", - "Over 9", - "Over 10", - ], - "EPS growthnext year": [ - "Any", - "Negative (<0%)", - "Positive (>0%)", - "Positive Low (0-10%)", - "High (>25%)", - "Under 5%", - "Under 10%", - "Under 15%", - "Under 20%", - "Under 25%", - "Under 30%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - ], - "Sales growthqtr over qtr": [ - "Any", - "Negative (<0%)", - "Positive (>0%)", - "Positive Low (0-10%)", - "High (>25%)", - "Under 5%", - "Under 10%", - "Under 15%", - "Under 20%", - "Under 25%", - "Under 30%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - ], - "Quick Ratio": [ - "Any", - "High (>3)", - "Low (<0.5)", - "Under 1", - "Under 0.5", - "Over 0.5", - "Over 1", - "Over 1.5", - "Over 2", - "Over 3", - "Over 4", - "Over 5", - "Over 10", - ], - "Net Profit Margin": [ - "Any", - "Positive (>0%)", - "Negative (<0%)", - "Very Negative (<-20%)", - "High (>20%)", - "Under 90%", - "Under 80%", - "Under 70%", - "Under 60%", - "Under 50%", - "Under 45%", - "Under 40%", - "Under 35%", - "Under 30%", - "Under 25%", - "Under 20%", - "Under 15%", - "Under 10%", - "Under 5%", - "Under 0%", - "Under -10%", - "Under -20%", - "Under -30%", - "Under -50%", - "Under -70%", - "Under -100%", - "Over 0%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - "Over 35%", - "Over 40%", - "Over 45%", - "Over 50%", - "Over 60%", - "Over 70%", - "Over 80%", - "Over 90%", - ], - "InstitutionalTransactions": [ - "Any", - "Very Negative (<20%)", - "Negative (<0%)", - "Positive (>0%)", - "Very Positive (>20%)", - "Under -50%", - "Under -45%", - "Under -40%", - "Under -35%", - "Under -30%", - "Under -25%", - "Under -20%", - "Under -15%", - "Under -10%", - "Under -5%", - "Over +5%", - "Over +10%", - "Over +15%", - "Over +20%", - "Over +25%", - "Over +30%", - "Over +35%", - "Over +40%", - "Over +45%", - "Over +50%", - ], - "P/B": [ - "Any", - "Low (<1)", - "High (>5)", - "Under 1", - "Under 2", - "Under 3", - "Under 4", - "Under 5", - "Under 6", - "Under 7", - "Under 8", - "Under 9", - "Under 10", - "Over 1", - "Over 2", - "Over 3", - "Over 4", - "Over 5", - "Over 6", - "Over 7", - "Over 8", - "Over 9", - "Over 10", - ], - "EPS growthpast 5 years": [ - "Any", - "Negative (<0%)", - "Positive (>0%)", - "Positive Low (0-10%)", - "High (>25%)", - "Under 5%", - "Under 10%", - "Under 15%", - "Under 20%", - "Under 25%", - "Under 30%", - "Over 5%", - "Over 10%", - "Over 15%", - "Over 20%", - "Over 25%", - "Over 30%", - ], - "Return on Assets": [ - "Any", - "Positive (>0%)", - "Negative (<0%)", - "Very Positive (>15%)", - "Very Negative (<-15%)", - "Under -50%", - "Under -45%", - "Under -40%", - "Under -35%", - "Under -30%", - "Under -25%", - "Under -20%", - "Under -15%", - "Under -10%", - "Under -5%", - "Over +5%", - "Over +10%", - "Over +15%", - "Over +20%", - "Over +25%", - "Over +30%", - "Over +35%", - "Over +40%", - "Over +45%", - "Over +50%", - ], - "LT Debt/Equity": [ - "Any", - "High (>0.5)", - "Low (<0.1)", - "Under 1", - "Under 0.9", - "Under 0.8", - "Under 0.7", - "Under 0.6", - "Under 0.5", - "Under 0.4", - "Under 0.3", - "Under 0.2", - "Under 0.1", - "Over 0.1", - "Over 0.2", - "Over 0.3", - "Over 0.4", - "Over.5", - "Over 0.6", - "Over 0.7", - "Over 0.8", - "Over 0.9", - "Over 1", - ], - "Payout Ratio": [ - "Any", - "None (0%)", - "Positive (>0%)", - "Low (<20%)", - "High (>50%)", - "Over 0%", - "Over 10%", - "Over 20%", - "Over 30%", - "Over 40%", - "Over 50%", - "Over 60%", - "Over 70%", - "Over 80%", - "Over 90%", - "Over 100%", - "Under 10%", - "Under 20%", - "Under 30%", - "Under 40%", - "Under 50%", - "Under 60%", - "Under 70%", - "Under 80%", - "Under 90%", - "Under 100%", - ], - "Performance": [ - "Any", - "Today Up", - "Today Down", - "Today -15%", - "Today -10%", - "Today -5%", - "Today +5%", - "Today +10%", - "Today +15%", - "Week -30%", - "Week -20%", - "Week -10%", - "Week Down", - "Week Up", - "Week +10%", - "Week +20%", - "Week +30%", - "Month -50%", - "Month -30%", - "Month -20%", - "Month -10%", - "Month Down", - "Month Up", - "Month +10%", - "Month +20%", - "Month +30%", - "Month +50%", - "Quarter -50%", - "Quarter -30%", - "Quarter -20%", - "Quarter -10%", - "Quarter Down", - "Quarter Up", - "Quarter +10%", - "Quarter +20%", - "Quarter +30%", - "Quarter +50%", - "Half -75%", - "Half -50%", - "Half -30%", - "Half -20%", - "Half -10%", - "Half Down", - "Half Up", - "Half +10%", - "Half +20%", - "Half +30%", - "Half +50%", - "Half +100%", - "Year -75%", - "Year -50%", - "Year -30%", - "Year -20%", - "Year -10%", - "Year Down", - "Year Up", - "Year +10%", - "Year +20%", - "Year +30%", - "Year +50%", - "Year +100%", - "Year +200%", - "Year +300%", - "Year +500%", - "YTD -75%", - "YTD -50%", - "YTD -30%", - "YTD -20%", - "YTD -10%", - "YTD -5%", - "YTD Down", - "YTD Up", - "YTD +5%", - "YTD +10%", - "YTD +20%", - "YTD +30", - "YTD +50%", - "YTD +100%", - ], - "20-Day Simple Moving Average": [ - "Any", - "Price below SMA20", - "Price 10% below SMA20", - "Price 20% below SMA20", - "Price 30% below SMA20", - "Price 40% below SMA20", - "Price 50% below SMA20", - "Price above SMA20", - "Price 10% above SMA20", - "Price 20% above SMA20", - "Price 30% above SMA20", - "Price 40% above SMA20", - "Price 50% above SMA20", - "Price crossed SMA20", - "Price crossed SMA20 above", - "Price crossed SMA20 below", - "SMA20 crossed SMA50", - "SMA20 crossed SMA50 above", - "SMA20 crossed SMA50 below", - "SMA20 cross SMA200", - "SMA20 crossed SMA200 below", - "SMA20 crossed SMA200 above", - "SMA20 above SMA50", - "SMA20 below SMA50", - "SMA20 above SMA200", - "SMA20 below SMA200", - ], - "20-Day High/Low": [ - "Any", - "New High", - "New Low", - "5% or more below High", - "10% or more below High", - "15% or more below High", - "20% or more below High", - "30% or more below High", - "40% or more below High", - "50% or more below High", - "0-3% below High", - "0-5% below High", - "0-10% below High", - "5% or more above Low", - "10% or more above Low", - "15% or more above Low", - "20% or more above Low", - "30% or more above Low", - "40% or more above Low", - "50% or more above Low", - "0-3% above Low", - "0-5% above Low", - "0-10% above Low", - ], - "Beta": [ - "Any", - "Under 0", - "Under 0.5", - "Under 1", - "Under 1.5", - "Under 2", - "Over 0", - "Over 0.5", - "Over 1", - "Over 1.5", - "Over 2", - "Over 2.5", - "Over 3", - "Over 4", - "0 to 0.5", - "0 to 1", - "0.5 to 1", - "0.5 to 1.5", - "1 to 1.5", - "1 to 2", - ], - "Performance 2": [ - "Any", - "Today Up", - "Today Down", - "Today -15%", - "Today -10%", - "Today -5%", - "Today +5%", - "Today +10%", - "Today +15%", - "Week -30%", - "Week -20%", - "Week -10%", - "Week Down", - "Week Up", - "Week +10%", - "Week +20%", - "Week +30%", - "Month -50%", - "Month -30%", - "Month -20%", - "Month -10%", - "Month Down", - "Month Up", - "Month +10%", - "Month +20%", - "Month +30%", - "Month +50%", - "Quarter -50%", - "Quarter -30%", - "Quarter -20%", - "Quarter -10%", - "Quarter Down", - "Quarter Up", - "Quarter +10%", - "Quarter +20%", - "Quarter +30%", - "Quarter +50%", - "Half -75%", - "Half -50%", - "Half -30%", - "Half -20%", - "Half -10%", - "Half Down", - "Half Up", - "Half +10%", - "Half +20%", - "Half +30%", - "Half +50%", - "Half +100%", - "Year -75%", - "Year -50%", - "Year -30%", - "Year -20%", - "Year -10%", - "Year Down", - "Year Up", - "Year +10%", - "Year +20%", - "Year +30%", - "Year +50%", - "Year +100%", - "Year +200%", - "Year +300%", - "Year +500%", - "YTD -75%", - "YTD -50%", - "YTD -30%", - "YTD -20%", - "YTD -10%", - "YTD -5%", - "YTD Down", - "YTD Up", - "YTD +5%", - "YTD +10%", - "YTD +20%", - "YTD +30", - "YTD +50%", - "YTD +100%", - ], - "50-Day Simple Moving Average": [ - "Any", - "Price below SMA50", - "Price 10% below SMA50", - "Price 20% below SMA50", - "Price 30% below SMA50", - "Price 40% below SMA50", - "Price 50% below SMA50", - "Price above SMA50", - "Price 10% above SMA50", - "Price 20% above SMA50", - "Price 30% above SMA50", - "Price 40% above SMA50", - "Price 50% above SMA50", - "Price crossed SMA50", - "Price crossed SMA50 above", - "Price crossed SMA50 below", - "SMA50 crossed SMA20", - "SMA50 crossed SMA20 above", - "SMA50 crossed SMA20 below", - "SMA50 cross SMA200", - "SMA50 crossed SMA200 below", - "SMA50 crossed SMA200 above", - "SMA50 above SMA20", - "SMA50 below SMA20", - "SMA50 above SMA200", - "SMA50 below SMA200", - ], - "50-Day High/Low": [ - "Any", - "New High", - "New Low", - "5% or more below High", - "10% or more below High", - "15% or more below High", - "20% or more below High", - "30% or more below High", - "40% or more below High", - "50% or more below High", - "0-3% below High", - "0-5% below High", - "0-10% below High", - "5% or more above Low", - "10% or more above Low", - "15% or more above Low", - "20% or more above Low", - "30% or more above Low", - "40% or more above Low", - "50% or more above Low", - "0-3% above Low", - "0-5% above Low", - "0-10% above Low", - ], - "Average True Range": [ - "Any", - "Over 0.25", - "Over 0.5", - "Over 0.75", - "Over 1", - "Over 1.5", - "Over 2. Over 2.5", - "Over 3", - "Over 3.5", - "Over 4", - "Over 4.5", - "Over 5", - "Under 0.25", - "Under 0.5", - "Under 0.75", - "Under 1", - "Under 1.5", - "Under 2", - "Under 2.5", - "Under 3", - "Under 3.5", - "Under 4", - "Under 4.5", - "Under 5", - ], - "Volatility": [ - "Any", - "Week - Over 3%", - "Week - Over 4%", - "Week - Over 5%", - "Week - 6%", - "Week - 7%", - "Week - 8%", - "Week - 9%", - "Week - 10%", - "Week - 12%", - "Week - 15%", - "Month - 2%", - "Month - 3%", - "Month - 4%", - "Month 5%", - "Month 5%", - "Month 6%", - "Month 7%", - "Month 8%", - "Month 9%", - "Month 10%", - "Month 12%", - "Month 15%", - ], - "200-Day Simple Moving Average": [ - "Any", - "Price below SMA200", - "Price 10% below SMA200", - "Price 20% below SMA200", - "Price 30% below SMA200", - "Price 40% below SMA200", - "Price 50% below SMA200", - "Price above SMA200", - "Price 10% above SMA200", - "Price 20% above SMA200", - "Price 30% above SMA200", - "Price 40% above SMA200", - "Price 50% above SMA200", - "Price crossed SMA200", - "Price crossed SMA200 above", - "Price crossed SMA200 below", - "SMA200 crossed SMA20", - "SMA20 crossed SMA20 above", - "SMA20 crossed SMA20 below", - "SMA200 cross SMA50", - "SMA200 crossed SMA50 below", - "SMA200 crossed SMA50 above", - "SMA200 above SMA20", - "SMA200 below SMA20", - "SMA200 above SMA50", - "SMA200 below SMA50", - ], - "52-Week High/Low": [ - "Any", - "New High", - "New Low", - "5% or more below High", - "10% or more below High", - "15% or more below High", - "20% or more below High", - "30% or more below High", - "40% or more below High", - "50% or more below High", - "0-3% below High", - "0-5% below High", - "0-10% below High", - "5% or more above Low", - "10% or more above Low", - "15% or more above Low", - "20% or more above Low", - "30% or more above Low", - "40% or more above Low", - "50% or more above Low", - "0-3% above Low", - "0-5% above Low", - "0-10% above Low", - ], - "RSI (14)": [ - "Any", - "Overbought (90)", - "Overbought (80)", - "Overbought (70)", - "Overbought (6)", - "Oversold (40)", - "Oversold (30)", - "Oversold (20)", - "Oversold (10)", - "Not Overbought (<60)", - "Not Overbought (<50)", - "Not Oversold (>50)", - "Not Oversold (>40)", - ], - "Change": [ - "Any", - "Up", - "Up 1%", - "Up 2%", - "Up 3%", - "Up 4%", - "Up 5%", - "Up 6%", - "Up 7%", - "Up 8%", - "Up 9%", - "Up 10%", - "Up 15%", - "Up 20%", - "Down", - "Down 1%", - "Down 2%", - "Down 3%", - "Down 4%", - "Down 5%", - "Down 6%", - "Down 7%", - "Down 8%", - "Down 9%", - "Down 10%", - "Down 15%", - "Down 20%", - ], - "Pattern": [ - "Any", - "Horizontal S/R", - "Horizontal S/R (Strong)", - "TL Resistance", - "TL Resistance (Strong)", - "TL Support", - "TL Support (Strong)", - "Wedge Up", - "Wedge Up (Strong)", - "Wedge Down", - "Wedge Down (Strong)", - "Triangle Ascending", - "Triangle Ascending (Strong)", - "Triangle Descending", - "Triangle Descending (Strong)", - "Wedge", - "Wedge (Strong)", - "Channel Up", - "Channel Up (Strong)", - "Channel Down", - "Channel Down (Strong)", - "Channel", - "Channel (Strong)", - "Double Top", - "Double Bottom", - "Multiple Top", - "Multiple Bottom", - "Head & Shoulders", - "Head & Shoulders Inverse", - ], - "Gap": [ - "Any", - "Up", - "Up 1%", - "Up 2%", - "Up 3%", - "Up 4%", - "Up 5%", - "Up 6%", - "Up 7%", - "Up 8%", - "Up 9%", - "Up 10%", - "Up 15%", - "Up 20%", - "Down", - "Down 1%", - "Down 2%", - "Down 3%", - "Down 4%", - "Down 5%", - "Down 6%", - "Down 7%", - "Down 8%", - "Down 9%", - "Down 10%", - "Down 15%", - "Down 20%", - ], - "Change from Open": [ - "Any", - "Up", - "Up 1%", - "Up 2%", - "Up 3%", - "Up 4%", - "Up 5%", - "Up 6%", - "Up 7%", - "Up 8%", - "Up 9%", - "Up 10%", - "Up 15%", - "Up 20%", - "Down", - "Down 1%", - "Down 2%", - "Down 3%", - "Down 4%", - "Down 5%", - "Down 6%", - "Down 7%", - "Down 8%", - "Down 9%", - "Down 10%", - "Down 15%", - "Down 20%", - ], - "Candlestick": [ - "Any", - "Long Lower Shadow", - "Long Upper Shadow", - "Hammer", - "Inverted Hammer", - "Spinning Top White", - "Spinning Top Black", - "Doji", - "Dragonfly Doji", - "Gravestone Doji", - "Marubozu White", - "Marubozu Black", - ], -} diff --git a/openbb_terminal/stocks/screener/finviz_view.py b/openbb_terminal/stocks/screener/finviz_view.py deleted file mode 100644 index 14fbff15a03..00000000000 --- a/openbb_terminal/stocks/screener/finviz_view.py +++ /dev/null @@ -1,247 +0,0 @@ -""" Finviz View """ - -__docformat__ = "numpy" - -import difflib -import logging -import os -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.screener.finviz_model import get_screener_data -from openbb_terminal.terminal_helper import suppress_stdout - -logger = logging.getLogger(__name__) - -d_cols_to_sort = { - "overview": [ - "Ticker", - "Company", - "Sector", - "Industry", - "Country", - "Market Cap", - "P/E", - "Price", - "Change", - "Volume", - ], - "valuation": [ - "Ticker", - "Market Cap", - "P/E", - "Fwd P/E", - "PEG", - "P/S", - "P/B", - "P/C", - "P/FCF", - "EPS this Y", - "EPS next Y", - "EPS past 5Y", - "EPS next 5Y", - "Sales past 5Y", - "Price", - "Change", - "Volume", - ], - "financial": [ - "Ticker", - "Market Cap", - "Dividend", - "ROA", - "ROE", - "ROI", - "Curr R", - "Quick R", - "LTDebt/Eq", - "Debt/Eq", - "Gross M", - "Oper M", - "Profit M", - "Earnings", - "Price", - "Change", - "Volume", - ], - "ownership": [ - "Ticker", - "Market Cap", - "Outstanding", - "Float", - "Insider Own", - "Insider Trans", - "Inst Own", - "Inst Trans", - "Float Short", - "Short Ratio", - "Avg Volume", - "Price", - "Change", - "Volume", - ], - "performance": [ - "Ticker", - "1W", - "1M", - "3M", - "6M", - "1Y", - "YTD", - "1W Volatility", - "1M Volatility", - "Recom", - "Avg Volume", - "Rel Volume", - "Price", - "Change", - "Volume", - ], - "technical": [ - "Ticker", - "Beta", - "ATR", - "SMA20", - "SMA50", - "SMA200", - "52W High", - "52W Low", - "RSI", - "Price", - "Change", - "from Open", - "Gap", - "Volume", - ], -} - - -@log_start_end(log=logger) -def screener( - loaded_preset: str = "top_gainers", - data_type: str = "overview", - limit: int = -1, - ascend: bool = False, - sortby: str = "", - export: str = "", - sheet_name: Optional[str] = None, -) -> List[str]: - """Screener one of the following: overview, valuation, financial, ownership, performance, technical. - - Parameters - ---------- - loaded_preset: str - Preset loaded to filter for tickers - data_type : str - Data type string between: overview, valuation, financial, ownership, performance, technical - limit : int - Limit of stocks to display - ascend : bool - Order of table to ascend or descend - sortby: str - Column to sort table by - export : str - Export dataframe data to csv,json,xlsx file - - Returns - ------- - List[str] - List of stocks that meet preset criteria - """ - with suppress_stdout(): - df_screen = get_screener_data( - preset_loaded=loaded_preset, - data_type=data_type, - limit=limit, - ascend=ascend, - ) - - if isinstance(df_screen, pd.DataFrame): - if df_screen.empty: - return [] - - df_screen = df_screen.dropna(axis="columns", how="all") - - if sortby: - if sortby in d_cols_to_sort[data_type]: - df_screen = df_screen.sort_values( - by=[sortby], - ascending=ascend, - na_position="last", - ) - else: - similar_cmd = difflib.get_close_matches( - sortby, - d_cols_to_sort[data_type], - n=1, - cutoff=0.7, - ) - if similar_cmd: - console.print( - f"Replacing '{' '.join(sortby)}' by '{similar_cmd[0]}' so table can be sorted." - ) - df_screen = df_screen.sort_values( - by=[similar_cmd[0]], - ascending=ascend, - na_position="last", - ) - else: - console.print( - f"Wrong sort column provided! Provide one of these: {', '.join(d_cols_to_sort[data_type])}" - ) - df_original = df_screen.copy() - df_screen = df_screen.fillna("") - - cols: List[str] = [] - data_type_cols = { - "ownership": ["Market Cap", "Outstanding", "Float", "Avg Volume", "Volume"], - "overview": ["Market Cap", "Volume"], - "technical": ["Volume"], - "valuation": ["Market Cap", "Volume"], - "financial": ["Market Cap", "Volume"], - "performance": ["Avg Volume", "Volume"], - } - cols = data_type_cols.get(data_type, []) - - if cols: - df_screen[cols] = df_screen[cols].applymap( - lambda x: lambda_long_number_format(x, 1) - ) - - if not get_current_user().preferences.USE_INTERACTIVE_DF: - df_original = df_screen - - print_rich_table( - df_original, - headers=list(df_original.columns), - show_index=False, - title="Finviz Screener", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - data_type, - df_original, - sheet_name, - ) - - return df_screen.Ticker.tolist() - - console.print( - "Error: The preset selected did not return results." - "This might be a temporary error that is resolved by running the command again." - "If no results continue to be returned, check the preset and expand the parameters." - ) - return [] diff --git a/openbb_terminal/stocks/screener/screener_controller.py b/openbb_terminal/stocks/screener/screener_controller.py deleted file mode 100644 index 027c95f9188..00000000000 --- a/openbb_terminal/stocks/screener/screener_controller.py +++ /dev/null @@ -1,662 +0,0 @@ -""" Screener Controller Module """ - -__docformat__ = "numpy" - -import argparse -import logging -from pathlib import Path -from typing import Dict, List, Optional, Union - -from openbb_terminal.core.config.paths import ( - MISCELLANEOUS_DIRECTORY, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_ONLY_RAW_DATA_ALLOWED, - parse_and_split_input, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks.comparison_analysis import ca_controller -from openbb_terminal.stocks.screener import ( - finviz_model, - finviz_view, - screener_helper, - screener_view, -) - -logger = logging.getLogger(__name__) - -# pylint: disable=E1121 - - -class ScreenerController(BaseController): - """Screener Controller class""" - - CHOICES_MENUS = ["ca"] - - CHOICES_COMMANDS = [ - "view", - "set", - "overview", - "valuation", - "financial", - "ownership", - "performance", - "technical", - ] - PRESETS_PATH = ( - get_current_user().preferences.USER_PRESETS_DIRECTORY / "stocks" / "screener" - ) - PRESETS_PATH_DEFAULT = MISCELLANEOUS_DIRECTORY / "stocks" / "screener" - - preset_choices: Dict[str, Union[str, Path]] = {} - - if PRESETS_PATH.exists(): - preset_choices.update( - { - filepath.name.replace(".ini", ""): filepath - for filepath in PRESETS_PATH.iterdir() - if filepath.suffix == ".ini" - } - ) - - if PRESETS_PATH_DEFAULT.exists(): - preset_choices.update( - { - filepath.name.replace(".ini", ""): filepath - for filepath in PRESETS_PATH_DEFAULT.iterdir() - if filepath.suffix == ".ini" - } - ) - - preset_choices.update(finviz_model.d_signals) - - historical_candle_choices = ["o", "h", "l", "c", "a"] - PATH = "/stocks/scr/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.preset = "top_gainers" - self.screen_tickers: List = list() - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(choices) - - def parse_input(self, an_input: str) -> List: - """Parse controller input - - Overrides the parent class function to handle github org/repo path convention. - See `BaseController.parse_input()` for details. - """ - # Filtering out sorting parameters with forward slashes like P/E - f0 = r"(p\/e|fwdp\/e|p\/s|p\/b|p\/c|p\/fcf)" - f1 = r"(P\/E|FwdP\/E|P\/S|P\/B|P\/C|P\/FCF)" - - sort_filter = r"((\ -s |\ --sort ).*?" + r"(" + f0 + r"|" + f1 + r")" + r"*)" - - custom_filters = [sort_filter] - - commands = parse_and_split_input( - an_input=an_input, custom_filters=custom_filters - ) - return commands - - def print_help(self): - """Print help""" - mt = MenuText("stocks/scr/") - mt.add_cmd("view") - mt.add_cmd("set") - mt.add_raw("\n") - mt.add_param("_preset", self.preset) - mt.add_raw("\n") - mt.add_cmd("overview") - mt.add_cmd("valuation") - mt.add_cmd("financial") - mt.add_cmd("ownership") - mt.add_cmd("performance") - mt.add_cmd("technical") - mt.add_raw("\n") - mt.add_param("_screened_tickers", ", ".join(self.screen_tickers)) - mt.add_raw("\n") - mt.add_menu("ca", self.screen_tickers) - console.print(text=mt.menu_text, menu="Stocks - Screener") - - @log_start_end(log=logger) - def call_view(self, other_args: List[str]): - """Process view command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="view", - description="""View available presets under presets folder.""", - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - help="View specific custom preset", - default="", - choices=self.preset_choices, - metavar="Desired preset.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if ns_parser.preset and ns_parser.preset in finviz_model.d_signals: - console.print( - "This preset contains no parameters other than the signal.\n" - ) - return - screener_view.display_presets(ns_parser.preset) - - @log_start_end(log=logger) - def call_set(self, other_args: List[str]): - """Process set command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="set", - description="""Set preset from custom and default ones.""", - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - default="template", - help="Filter presets", - choices=self.preset_choices, - metavar="Desired preset.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - self.preset = ns_parser.preset - - @log_start_end(log=logger) - def call_overview(self, other_args: List[str]): - """Process overview command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="overview", - description=""" - Prints overview data of the companies that meet the pre-set filtering. - """, - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - default=self.preset, - help="Filter presets", - choices=self.preset_choices, - metavar="Desired preset.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=int, - default=0, - help="Limit of stocks to print", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sort", - choices=screener_helper.finviz_choices("overview"), - type=str.lower, - dest="sort", - metavar="SORT", - default="marketcap", - help="Sort elements of the table.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - preset = ( - self.preset.replace(".ini", "") - if self.preset.replace(".ini", "") in finviz_model.d_signals - else self.preset - ) - - sort_map = screener_helper.finviz_map("overview") - self.screen_tickers = finviz_view.screener( - loaded_preset=preset, - data_type="overview", - limit=ns_parser.limit, - ascend=ns_parser.reverse, - sortby=sort_map[ns_parser.sort], - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_valuation(self, other_args: List[str]): - """Process valuation command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="valuation", - description=""" - Prints valuation data of the companies that meet the pre-set filtering. - """, - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - default=self.preset, - help="Filter presets", - choices=self.preset_choices, - metavar="Desired preset.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=int, - default=0, - help="Limit of stocks to print", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sort", - dest="sort", - default="marketcap", - choices=screener_helper.finviz_choices("valuation"), - type=str.lower, - metavar="SORT", - help="Sort elements of the table.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - preset = ( - self.preset.replace(".ini", "") - if self.preset.replace(".ini", "") in finviz_model.d_signals - else self.preset - ) - sort_map = screener_helper.finviz_map("valuation") - self.screen_tickers = finviz_view.screener( - loaded_preset=preset, - data_type="valuation", - limit=ns_parser.limit, - ascend=ns_parser.reverse, - sortby=sort_map[ns_parser.sort], - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_financial(self, other_args: List[str]): - """Process financial command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="financial", - description=""" - Prints financial data of the companies that meet the pre-set filtering. - """, - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - default=self.preset, - help="Filter presets", - choices=self.preset_choices, - metavar="Desired preset.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=int, - default=0, - help="Limit of stocks to print", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sort", - choices=screener_helper.finviz_choices("financial"), - type=str.lower, - dest="sort", - metavar="SORT", - default="marketcap", - help="Sort elements of the table.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - preset = ( - self.preset.replace(".ini", "") - if self.preset.replace(".ini", "") in finviz_model.d_signals - else self.preset - ) - sort_map = screener_helper.finviz_map("financial") - self.screen_tickers = finviz_view.screener( - loaded_preset=preset, - data_type="financial", - limit=ns_parser.limit, - ascend=ns_parser.reverse, - sortby=sort_map[ns_parser.sort], - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ownership(self, other_args: List[str]): - """Process ownership command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="ownership", - description=""" - Prints ownership data of the companies that meet the pre-set filtering. - """, - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - default=self.preset, - help="Filter presets", - choices=self.preset_choices, - metavar="Desired preset.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=int, - default=0, - help="Limit of stocks to print", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sort", - dest="sort", - metavar="SORT", - default="marketcap", - choices=screener_helper.finviz_choices("ownership"), - type=str.lower, - help="Sort elements of the table.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - preset = ( - self.preset.replace(".ini", "") - if self.preset.replace(".ini", "") in finviz_model.d_signals - else self.preset - ) - sort_map = screener_helper.finviz_map("ownership") - self.screen_tickers = finviz_view.screener( - loaded_preset=preset, - data_type="ownership", - limit=ns_parser.limit, - ascend=ns_parser.reverse, - sortby=sort_map[ns_parser.sort], - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_performance(self, other_args: List[str]): - """Process performance command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="performance", - description=""" - Prints performance data of the companies that meet the pre-set filtering. - """, - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - default=self.preset, - help="Filter presets", - choices=self.preset_choices, - metavar="Desired preset.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=int, - default=0, - help="Limit of stocks to print", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sort", - choices=screener_helper.finviz_choices("performance"), - type=str.lower, - dest="sort", - default="ytd", - metavar="SORTBY", - help="Sort elements of the table.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - sort_map = screener_helper.finviz_map("performance") - if ns_parser: - preset = ( - self.preset.replace(".ini", "") - if self.preset.replace(".ini", "") in finviz_model.d_signals - else self.preset - ) - self.screen_tickers = finviz_view.screener( - loaded_preset=preset, - data_type="performance", - limit=ns_parser.limit, - ascend=ns_parser.reverse, - sortby=sort_map[ns_parser.sort], - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_technical(self, other_args: List[str]): - """Process technical command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="technical", - description=""" - Prints technical data of the companies that meet the pre-set filtering. - """, - ) - parser.add_argument( - "-p", - "--preset", - action="store", - dest="preset", - type=str, - default=self.preset, - help="Filter presets", - choices=self.preset_choices, - metavar="Desired preset.", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=int, - default=0, - help="Limit of stocks to print", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-s", - "--sort", - choices=screener_helper.finviz_choices("technical"), - type=str.lower, - dest="sort", - default="rsi", - help="Sort elements of the table.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - preset = ( - self.preset.replace(".ini", "") - if self.preset.replace(".ini", "") in finviz_model.d_signals - else self.preset - ) - sort_map = screener_helper.finviz_map("technical") - self.screen_tickers = finviz_view.screener( - loaded_preset=preset, - data_type="technical", - limit=ns_parser.limit, - ascend=ns_parser.reverse, - sortby=sort_map[ns_parser.sort], - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ca(self, _): - """Call the comparison analysis menu with selected tickers""" - if self.screen_tickers: - self.queue = self.load_class( - ca_controller.ComparisonAnalysisController, - self.screen_tickers, - self.queue, - ) - else: - console.print( - "Please select a screener using 'set' and then run 'historical' " - "before going to the CA menu.\n" - ) diff --git a/openbb_terminal/stocks/screener/screener_helper.py b/openbb_terminal/stocks/screener/screener_helper.py deleted file mode 100644 index 9f87a7150c8..00000000000 --- a/openbb_terminal/stocks/screener/screener_helper.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Dict, List - -from openbb_terminal.stocks.screener import finviz_view - - -def finviz_choices(section: str) -> List[str]: - """Generates the available choices for the given section. Formatted in an argparse friendly way - - Parameters - ---------- - section: str - The section to generate the choices for - - Returns - ------- - List[str] - The argparse friendly strings - """ - return [x.replace(" ", "").lower() for x in finviz_view.d_cols_to_sort[section]] - - -def finviz_map(section: str) -> Dict[str, str]: - """Generates a mapping of our custom formatted choices to the actual choices - - Parameters - ---------- - section: str - The section to generate the choices for - - Returns - ------- - Dict[str] - Keys are the arpgarse friendly version, values are the original version - """ - return {x.replace(" ", "").lower(): x for x in finviz_view.d_cols_to_sort[section]} diff --git a/openbb_terminal/stocks/screener/screener_view.py b/openbb_terminal/stocks/screener/screener_view.py deleted file mode 100644 index 5fc0446b83d..00000000000 --- a/openbb_terminal/stocks/screener/screener_view.py +++ /dev/null @@ -1,79 +0,0 @@ -""" Screener View Module """ - -__docformat__ = "numpy" - -import configparser - -from openbb_terminal.core.config.paths import ( - MISCELLANEOUS_DIRECTORY, -) -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.screener import finviz_model - -PRESETS_PATH = ( - get_current_user().preferences.USER_PRESETS_DIRECTORY / "stocks" / "screener" -) -PRESETS_PATH_DEFAULT = MISCELLANEOUS_DIRECTORY / "stocks" / "screener" -preset_choices = {} - -if PRESETS_PATH.exists(): - preset_choices.update( - { - filepath.name.replace(".ini", ""): filepath - for filepath in PRESETS_PATH.iterdir() - if filepath.suffix == ".ini" - } - ) - -if PRESETS_PATH_DEFAULT.exists(): - preset_choices.update( - { - filepath.name.replace(".ini", ""): filepath - for filepath in PRESETS_PATH_DEFAULT.iterdir() - if filepath.suffix == ".ini" - } - ) - - -def display_presets(preset: str): - if preset: - preset_filter = configparser.RawConfigParser() - preset_filter.optionxform = str # type: ignore - preset_filter.read(preset_choices[preset]) - - filters_headers = ["General", "Descriptive", "Fundamental", "Technical"] - - for i, filter_header in enumerate(filters_headers): - console.print(f" - {filter_header} -") - d_filters = {**preset_filter[filter_header]} - d_filters = {k: v for k, v in d_filters.items() if v} - - if d_filters: - max_len = len(max(d_filters, key=len)) - for key, value in d_filters.items(): - console.print(f"{key}{(max_len-len(key))*' '}: {value}") - - if i < len(filters_headers) - 1: - console.print("\n") - - else: - console.print("\nCustom Presets:") - for item, path in preset_choices.items(): - with open( - path, - encoding="utf8", - ) as f: - description = "" - for line in f: - if line.strip() == "[General]": - break - description += line.strip() - console.print( - f" {item}{(50-len(item)) * ' '}" - f"{description.split('Description: ')[1].replace('#', '')}" - ) - - console.print("\nDefault Presets:") - for signame, sigdesc in finviz_model.d_signals_desc.items(): - console.print(f" {signame}{(50-len(signame)) * ' '}{sigdesc}") diff --git a/openbb_terminal/stocks/stock_statics.py b/openbb_terminal/stocks/stock_statics.py deleted file mode 100644 index 4ed66ac34ae..00000000000 --- a/openbb_terminal/stocks/stock_statics.py +++ /dev/null @@ -1,614 +0,0 @@ -INTERVALS = [1, 5, 15, 30, 60] -SOURCES = ["YahooFinance", "AlphaVantage", "EODHD"] -CANDLE_SORT = [ - "open", - "high", - "low", - "close", - "adjclose", - "volume", - "dividends", - "stock_splits", -] - -market_coverage_suffix = { - "united_states": ["CBT", "CME", "NYB", "CMX", "NYM", "US", ""], - "argentina": ["BA"], - "austria": ["VI"], - "australia": ["AX"], - "belgium": ["BR"], - "brazil": ["SA"], - "canada": ["CN", "NE", "TO", "V"], - "chile": ["SN"], - "china": ["SS", "SZ"], - "czech_republic": ["PR"], - "denmark": ["CO"], - "egypt": ["CA"], - "estonia": ["TL"], - "europe": ["NX"], - "finland": ["HE"], - "france": ["PA"], - "germany": ["BE", "BM", "DU", "F", "HM", "HA", "MU", "SG", "DE"], - "greece": ["AT"], - "hong_kong": ["HK"], - "hungary": ["BD"], - "iceland": ["IC"], - "india": ["BO", "NS"], - "indonesia": ["JK"], - "ireland": ["IR"], - "israel": ["TA"], - "italy": ["MI"], - "japan": ["T", "S"], - "latvia": ["RG"], - "lithuania": ["VS"], - "malaysia": ["KL"], - "mexico": ["MX"], - "netherlands": ["AS"], - "new_zealand": ["NZ"], - "norway": ["OL"], - "portugal": ["LS"], - "qatar": ["QA"], - "russia": ["ME"], - "singapore": ["SI"], - "south_africa": ["JO"], - "south_korea": ["KS", "KQ"], - "spain": ["MC"], - "saudi_arabia": ["SAU"], - "sweden": ["ST"], - "switzerland": ["SW"], - "taiwan": ["TWO", "TW"], - "thailand": ["BK"], - "turkey": ["IS"], - "united_kingdom": ["L", "IL"], - "venezuela": ["CR"], -} - -INCOME_PLOT = { - "AlphaVantage": { - "reportedcurrency": "reported_currency", - "grossprofit": "gross_profit", - "totalrevenue": "total_revenue", - "costofrevenue": "cost_of_revenue", - "costofgoodsandservicessold": "cost_of_goods_and_services_sold", - "operatingincome": "operating_income", - "sellinggeneralandadministrative": "selling_general_and_administrative", - "researchanddevelopment": "research_and_development_expenses", - "operatingexpenses": "operating_expenses", - "investmentincomenet": "investment_income_net", - "netinterestincome": "net_interest_income", - "interestincome": "interest_income", - "interestexpense": "interest_expense", - "noninterestincome": "revenue", - "othernonoperatingincome": "other_non_operating_income", - "depreciation": "depreciation", - "depreciationandamortization": "depreciation_and_amortization", - "incomebeforetax": "income_before_tax", - "incometaxexpense": "income_tax_expense", - "interestanddebtexpense": "interest_and_debt_expense", - "netincomefromcontinuingoperations": "net_income_from_continuing_operations", - "comprehensiveincomenetoftax": "comprehensive_income_net_of_tax", - "ebit": "ebit", - "ebitda": "ebitda", - "netincome": "net_income", - }, - "Polygon": { - "cost_of_revenue": "cost_of_revenue", - "diluted_earnings_per_share": "diluted_earnings_per_share", - "costs_and_expenses": "costs_and_expenses", - "gross_profit": "gross_profit", - "nonoperating_income_loss": "non_operating_income_loss", - "operating_income_loss": "operating_income", - "participating_securities_distributed_and_undistributed_earnings_loss_basic": "participating_securities_" - "distributed_and_undistributed_ea" - "rnings_loss_basic", - "income_tax_expense_benefit": "income_tax_expense", - "income_tax_expense_benefit_current": "current_income_tax_expense", - "net_income_loss_attributable_to_parent": "net_income_loss_attributable_to_parent", - "net_income_loss": "net_income", - "income_tax_expense_benefit_deferred": "income_tax_expense_benefit_deferred", - "preferred_stock_dividends_and_other_adjustments": "preferred_stock_dividends_and_other_adjustments", - "operating_expenses": "operating_expenses", - "income_loss_from_continuing_operations_before_tax": "continuing_operations_net_income", - "net_income_loss_attributable_to_noncontrolling_interest": "net_income_loss_attributable_to_" - "non_controlling_interest", - "income_loss_from_continuing_operations_after_tax": "income_before_tax", - "revenues": "revenue", - "net_income_loss_available_to_common_stockholders_basic": "net_income_available_to_common_shareholders", - "benefits_costs_expenses": "benefits_costs_expenses", - "basic_earnings_per_share": "basic_earnings_per_share", - "interest_expense_operating": "income_tax_expense", - "income_loss_before_equity_method_investments": "income_loss_before_equity_method_investments", - "cost_of_revenue_goods": "cost_of_revenue_goods", - "noninterest_income": "noninterest_income", - "interest_income_expense_after_provision_for_losses": "interest_income_expense_after_provision_for_losses", - "interest_income_expense_operating_net": "interest_income_expense_operating_net", - "noninterest_expense": "noninterest_expense", - "interest_and_dividend_income_operating": "interest_and_dividend_income_operating", - "provision_for_loan_lease_and_other_losses": "provision_for_loan_lease_and_other_losses", - "income_loss_from_discontinued_operations_net_of_tax": "income_loss_from_discontinued_operations_net_of_tax", - "cost_of_revenue_services": "cost_of_revenue_services", - "income_loss_from_discontinued_operations_net_of_tax_gain_loss_on_disposal": "income" - "_loss_from_discontinued_operations_net_of_tax_gain_loss_on_disposal", - "income_loss_from_equity_method_investments": "income_loss_from_equity_method_investments", - }, - "YahooFinance": { - "total_revenue": "total_revenue", - "cost_of_revenue": "cost_of_revenue", - "gross_profit": "gross_profit", - "research_development": "research_and_development_expenses", - "selling_general_and_administrative": "selling_general_and_administrative", - "total_operating_expenses": "operating_expenses", - "operating_income_or_loss": "operating_income", - "interest_expense": "interest_expense", - "total_other_income/expenses_net": "other_non_operating_income", - "income_before_tax": "income_before_tax", - "income_tax_expense": "income_tax_expense", - "income_from_continuing_operations": "net_income_from_continuing_operations", - "net_income": "net_income", - "net_income_available_to_common_shareholders": "net_income_available_to_common_shareholders", - "basic_eps": "basic_earnings_per_share", - "diluted_eps": "diluted_earnings_per_share", - "basic_average_shares": "basic_average_shares", - "diluted_average_shares": "diluted_average_shares", - "ebitda": "ebitda", - }, - "FinancialModelingPrep": { - "reported_currency": "reported_currency", - "cik": "cik", - "filling_date": "filling_date", - "accepted_date": "accepted_date", - "calendar_year": "calendar_year", - "period": "period", - "revenue": "revenue", - "cost_of_revenue": "cost_of_revenue", - "gross_profit": "gross_profit", - "gross_profit_ratio": "gross_profit_ratio", - "research_and_development_expenses": "research_and_development_expenses", - "general_and_administrative_expenses": "general_and_administrative_expenses", - "selling_and_marketing_expenses": "selling_and_marketing_expenses", - "selling_general_and_administrative_expenses": "selling_general_and_administrative", - "other_expenses": "other_expenses", - "operating_expenses": "operating_expenses", - "cost_and_expenses": "costs_and_expenses", - "interest_income": "interest_income", - "interest_expense": "interest_expense", - "depreciation_and_amortization": "depreciation_and_amortization", - "ebitda": "ebitda", - "ebitdaratio": "ebitda_ratio", - "operating_income": "operating_income", - "operating_income_ratio": "operating_income_ratio", - "total_other_income_expenses_net": "non_operating_income_loss", - "income_before_tax": "income_before_tax", - "income_before_tax_ratio": "income_before_tax_ratio", - "income_tax_expense": "income_tax_expense", - "net_income": "net_income", - "net_income_ratio": "net_income_ratio", - "eps": "basic_earnings_per_share", - "epsdiluted": "diluted_earnings_per_share", - "weighted_average_shs_out": "basic_average_shares", - "weighted_average_shs_out_dil": "diluted_average_shares", - "link": "link", - "final_link": "final_link", - }, -} -INCOME_PLOT_CHOICES = [ - "gross_profit", - "total_revenue", - "cost_of_revenue", - "cost_of_goods_and_services_sold", - "operating_income", - "selling_general_and_administrative", - "research_and_development_expenses", - "operating_expenses", - "investment_income_net", - "net_interest_income", - "interest_income", - "interest_expense", - "revenue", - "other_non_operating_income", - "depreciation", - "depreciation_and_amortization", - "income_before_tax", - "income_tax_expense", - "interest_and_debt_expense", - "net_income_from_continuing_operations", - "comprehensive_income_net_of_tax", - "ebit", - "ebitda", - "net_income", - "diluted_earnings_per_share", - "costs_and_expenses", - "non_operating_income_loss", - "participating_securities_distributed_and_undistributed_earnings_loss_basic", - "net_income_loss_attributable_to_parent", - "income_tax_expense_benefit_deferred", - "preferred_stock_dividends_and_other_adjustments", - "net_income_loss_attributable_to_non_controlling_interest", - "continuing_operations_net_income", - "income_before_tax", - "net_income_available_to_common_shareholders", - "benefits_costs_expenses", - "basic_earnings_per_share", - "income_loss_before_equity_method_investments", - "basic_average_shares", - "diluted_average_shares", - "gross_profit_ratio", - "general_and_administrative_expenses", - "selling_and_marketing_expenses", - "other_expenses", - "ebitda_ratio", - "operating_income_ratio", - "income_before_tax_ratio", - "net_income_ratio", - "current_income_tax_expense", -] -BALANCE_PLOT = { - "AlphaVantage": { - "reportedcurrency": "reported_currency", - "totalassets": "total_assets", - "totalcurrentassets": "total_current_assets", - "cashandcashequivalentsatcarryingvalue": "cash_and_cash_equivalents", - "cashandshortterminvestments": "cash_and_short_term_investments", - "inventory": "inventory", - "currentnetreceivables": "net_receivables", - "totalnoncurrentassets": "total_non_current_assets", - "propertyplantequipment": "property_plant_equipment", - "accumulateddepreciationamortizationppe": "accumulated_depreciation_amortization_ppe", - "intangibleassets": "intangible_assets", - "intangibleassetsexcludinggoodwill": "intangible_assets_excluding_goodwill", - "goodwill": "goodwill", - "investments": "investments", - "longterminvestments": "long_term_investments", - "shortterminvestments": "short_term_investments", - "othercurrentassets": "other_current_assets", - "othernoncurrentassets": "other_non_current_assets", - "totalliabilities": "total_liabilities", - "totalcurrentliabilities": "total_current_liabilities", - "currentaccountspayable": "current_accounts_payable", - "deferredrevenue": "deferred_revenue", - "currentdebt": "current_debt", - "shorttermdebt": "short_term_debt", - "totalnoncurrentliabilities": "total_non_current_liabilities", - "capitalleaseobligations": "capital_lease_obligations", - "longtermdebt": "long_term_debt", - "currentlongtermdebt": "current_long_term_debt", - "longtermdebtnoncurrent": "long_term_debt_non_current", - "shortlongtermdebttotal": "short_long_term_debt_total", - "othercurrentliabilities": "other_current_liabilities", - "othernoncurrentliabilities": "other_non_current_liabilities", - "totalshareholderequity": "total_shareholder_equity", - "treasurystock": "treasury_stock", - "retainedearnings": "retained_earnings", - "commonstock": "common_stock", - "commonstocksharesoutstanding": "common_stock_shares_outstanding", - }, - "Polygon": { - "temporary_equity": "temporary_equity", - "temporary_equity_attributable_to_parent": "temporary_equity_attributable_to_parent", - "equity_attributable_to_noncontrolling_interest": "equity_attributable_to_non_controlling_interest", - "liabilities": "total_liabilities", - "noncurrent_assets": "total_non_current_assets", - "equity": "equity", - "assets": "total_assets", - "current_assets": "total_current_assets", - "equity_attributable_to_parent": "equity_attributable_to_parent", - "current_liabilities": "total_current_liabilities", - "noncurrent_liabilities": "total_non_current_liabilities", - "fixed_assets": "fixed_assets", - "other_than_fixed_noncurrent_assets": "other_than_fixed_non_current_assets", - "liabilities_and_equity": "liabilities_and_equity", - "commitments_and_contingencies": "commitments_and_contingencies", - }, - "YahooFinance": { - "cash_and_cash_equivalents": "cash_and_cash_equivalents", - "other_short-term_investments": "short_term_investments", - "total_cash": "cash_and_short_term_investments", - "net_receivables": "net_receivables", - "inventory": "inventory", - "other_current_assets": "other_current_assets", - "total_current_assets": "total_current_assets", - "gross_property_plant_and_equipment": "gross_property_plant_and_equipment", - "accumulated_depreciation": "accumulated_depreciation_amortization_ppe", - "net_property_plant_and_equipment": "net_property_plant_and_equipment", - "equity_and_other_investments": "long_term_investments", - "other_long-term_assets": "other_non_currrent_assets", - "total_non-current_assets": "total_non_current_assets", - "total_assets": "total_assets", - "current_debt": "current_debt", - "accounts_payable": "current_accounts_payable", - "deferred_revenues": "deferred_revenue", - "other_current_liabilities": "other_current_liabilities", - "total_current_liabilities": "total_current_liabilities", - "long-term_debt": "long_term_debt", - "deferred_tax_liabilities": "deferred_tax_liabilities", - "other_long-term_liabilities": "other_non_current_liabilities", - "total_non-current_liabilities": "total_non_current_liabilities", - "total_liabilities": "total_liabilities", - "common_stock": "common_stock", - "retained_earnings": "retained_earnings", - "accumulated_other_comprehensive_income": "accumulated_other_comprehensive_income", - "total_stockholders'_equity": "total_shareholder_equity", - "total_liabilities_and_stockholders'_equity": "total_liabilities_and_stockholders_equity", - "goodwill": "goodwill", - "intangible_assets": "intangible_assets_excluding_goodwill", - "taxes_payable": "taxes_payable", - "accrued_liabilities": "accrued_liabilities", - }, - "FinancialModelingPrep": { - "reported_currency": "reported_currency", - "cik": "cik", - "filling_date": "filing_date", - "accepted_date": "accepted_date", - "calendar_year": "calendar_year", - "period": "period", - "cash_and_cash_equivalents": "cash_and_cash_equivalents", - "short_term_investments": "short_term_investments", - "cash_and_short_term_investments": "cash_and_short_term_investments", - "net_receivables": "net_receivables", - "inventory": "inventory", - "other_current_assets": "other_current_assets", - "total_current_assets": "total_current_assets", - "property_plant_equipment_net": "property_plant_equipment", - "goodwill": "goodwill", - "intangible_assets": "intangible_assets_excluding_goodwill", - "goodwill_and_intangible_assets": "intangible_assets", - "long_term_investments": "long_term_investments", - "tax_assets": "tax_assets", - "other_non_current_assets": "other_non_currrent_assets", - "total_non_current_assets": "total_non_current_assets", - "other_assets": "other_assets", - "total_assets": "total_assets", - "account_payables": "current_accounts_payable", - "short_term_debt": "current_debt", - "tax_payables": "tax_payables", - "deferred_revenue": "deferred_revenue", - "other_current_liabilities": "other_non_current_liabilities", - "total_current_liabilities": "total_current_liabilities", - "long_term_debt": "long_term_debt_non_current", - "deferred_revenue_non_current": "deferred_revenue_non_current", - "deferred_tax_liabilities_non_current": "deferred_tax_liabilities", - "other_non_current_liabilities": "other_non_current_liabilities", - "total_non_current_liabilities": "total_non_current_liabilities", - "other_liabilities": "other_liabilities", - "capital_lease_obligations": "capital_lease_obligations", - "total_liabilities": "total_liabilities", - "preferred_stock": "preferred_stock", - "common_stock": "common_stock", - "retained_earnings": "retained_earnings", - "accumulated_other_comprehensive_income_loss": "accumulated_other_comprehensive_income", - "othertotal_stockholders_equity": "other_total_stockholders_equity", - "total_stockholders_equity": "total_shareholder_equity", - "total_liabilities_and_stockholders_equity": "total_liabilities_and_stockholders_equity", - "minority_interest": "equity_attributable_to_non_controlling_interest", - "total_equity": "equity", - "total_liabilities_and_total_equity": "liabilities_and_equity", - "total_investments": "total_investments", - "total_debt": "total_debt", - "net_debt": "net_debt", - "link": "link", - "final_link": "final_link", - }, -} -BALANCE_PLOT_CHOICES = [ - "total_assets", - "total_current_assets", - "cash_and_cash_equivalents", - "cash_and_short_term_investments", - "inventory", - "net_receivables", - "total_non_current_assets", - "property_plant_equipment", - "accumulated_depreciation_amortization_ppe", - "intangible_assets", - "intangible_assets_excluding_goodwill", - "goodwill", - "investments", - "long_term_investments", - "short_term_investments", - "other_current_assets", - "other_non_currrent_assets", - "total_liabilities", - "total_current_liabilities", - "current_accounts_payable", - "deferred_revenue", - "current_debt", - "short_term_debt", - "total_non_current_liabilities", - "capital_lease_obligations", - "long_term_debt", - "current_long_term_debt", - "long_term_debt_non_current", - "short_long_term_debt_total", - "other_current_liabilities", - "other_non_current_liabilities", - "total_shareholder_equity", - "treasury_stock", - "retained_earnings", - "common_stock", - "common_stock_shares_outstanding", - "equity_attributable_to_non_controlling_interest", - "equity_attributable_to_parent", - "fixed_assets", - "other_than_fixed_non_current_assets", - "liabilities_and_equity", - "equity", - "gross_property_plant_and_equipment", - "plant_and_equipment", - "tax_payables", - "other_assets", - "tax_assets", - "deferred_revenue_non_current", - "deferred_tax_liabilities", - "other_liabilities", - "preferred_stock", - "accumulated_other_comprehensive_income", - "other_total_stockholders_equity", - "total_liabilities_and_stockholders_equity", - "total_investments", - "total_debt", - "net_debt", -] -CASH_PLOT = { - "AlphaVantage": { - "reportedcurrency": "reported_currency", - "operatingcashflow": "operating_cash_flow", - "paymentsforoperatingactivities": "payments_for_operating_activities", - "proceedsfromoperatingactivities": "proceeds_from_operating_activities", - "changeinoperatingliabilities": "change_in_operating_liabilities", - "changeinoperatingassets": "change_in_operating_assets", - "depreciationdepletionandamortization": "depreciation_depletion_and_amortization", - "capitalexpenditures": "capital_expenditure", - "changeinreceivables": "change_in_receivables", - "changeininventory": "inventory", - "profitloss": "profit_loss", - "cashflowfrominvestment": "cash_flow_from_investment", - "cashflowfromfinancing": "cash_flow_from_financing", - "proceedsfromrepaymentsofshorttermdebt": "proceeds_from_repayments_of_short_term_debt", - "paymentsforrepurchaseofcommonstock": "common_stock_repurchased", - "paymentsforrepurchaseofequity": "payments_for_repurchase_of_equity", - "paymentsforrepurchaseofpreferredstock": "payments_for_repurchase_of_preferred_stock", - "dividendpayout": "dividends_paid", - "dividendpayoutcommonstock": "dividend_payout_common_stock", - "dividendpayoutpreferredstock": "dividend_payout_preferred_stock", - "proceedsfromissuanceofcommonstock": "common_stock_issued", - "proceedsfromissuanceoflongtermdebtandcapitalsecuritiesnet": "proceeds_from_issuance_of_long_term_", - "proceedsfromissuanceofpreferredstock": "proceeds_from_issuance_of_preferred_stock", - "proceedsfromrepurchaseofequity": "proceeds_from_repurchase_of_equity", - "proceedsfromsaleoftreasurystock": "proceeds_from_sale_of_treasury_stock", - "changeincashandcashequivalents": "net_change_in_cash", - "changeinexchangerate": "change_in_exchange_rate", - "netincome": "net_income", - }, - "Polygon": { - "net_cash_flow_from_financing_activities_continuing": "net_cash_flow_from_financing_activities_continuing", - "net_cash_flow_continuing": "net_cash_flow_continuing", - "net_cash_flow_from_investing_activities": "cash_flow_from_investment", - "net_cash_flow": "net_change_in_cash", - "net_cash_flow_from_operating_activities": "operating_cash_flow", - "net_cash_flow_from_financing_activities": "cash_flow_from_financing", - "net_cash_flow_from_operating_activities_continuing": "net_cash_provided_by_operating_activities", - "net_cash_flow_from_investing_activities_continuing": "net_cash_flow_from_investing_activities_continuing", - "exchange_gains_losses": "change_in_exchange_rate", - }, - "YahooFinance": { - "net_income": "net_income", - "depreciation_&_amortisation": "depreciation_and_amortization", - "deferred_income_taxes": "deferred_income_taxes", - "stock-based_compensation": "stock_based_compensation", - "change_in_working_capital": "change_in_working_capital", - "accounts_receivable": "change_in_receivables", - "inventory": "inventory", - "accounts_payable": "accounts_payable", - "other_working_capital": "other_working_capital", - "other_non-cash_items": "other_non_cash_items", - "net_cash_provided_by_operating_activities": "net_cash_provided_by_operating_activities", - "investments_in_property_plant_and_equipment": "investments_in_property_plant_and_equipment", - "acquisitions_net": "acquisitions_net", - "purchases_of_investments": "purchases_of_investments", - "sales/maturities_of_investments": "sales_maturities_of_investments", - "other_investing_activities": "other_investing_activities", - "net_cash_used_for_investing_activities": "cash_flow_from_investment", - "debt_repayment": "debt_repayment", - "common_stock_issued": "common_stock_issued", - "common_stock_repurchased": "common_stock_repurchased", - "dividends_paid": "dividends_paid", - "other_financing_activities": "other_financing_activities", - "net_cash_used_provided_by_(used_for)_financing_activities": "cash_flow_from_financing", - "net_change_in_cash": "net_change_in_cash", - "cash_at_beginning_of_period": "cash_at_beginning_of_period", - "cash_at_end_of_period": "cash_at_end_of_period", - "operating_cash_flow": "operating_cash_flow", - "capital_expenditure": "capital_expenditure", - "free_cash_flow": "free_cash_flow", - }, - "FinancialModelingPrep": { - "reported_currency": "reported_currency", - "cik": "cik", - "filling_date": "filling_date", - "accepted_date": "accepted_date", - "calendar_year": "calendar_year", - "period": "period", - "net_income": "net_income", - "depreciation_and_amortization": "depreciation_and_amortization", - "deferred_income_tax": "deferred_income_taxes", - "stock_based_compensation": "stock_based_compensation", - "change_in_working_capital": "change_in_working_capital", - "accounts_receivables": "change_in_receivables", - "inventory": "inventory", - "accounts_payables": "accounts_payable", - "other_working_capital": "other_working_capital", - "other_non_cash_items": "other_non_cash_items", - "net_cash_provided_by_operating_activities": "net_cash_provided_by_operating_activities", - "investments_in_property_plant_and_equipment": "investments_in_property_plant_and_equipment", - "acquisitions_net": "acquisitions_net", - "purchases_of_investments": "purchases_of_investments", - "sales_maturities_of_investments": "sales_maturities_of_investments", - "other_investing_activites": "other_investing_activities", - "net_cash_used_for_investing_activites": "cash_flow_from_investment", - "debt_repayment": "debt_repayment", - "common_stock_issued": "common_stock_issued", - "common_stock_repurchased": "common_stock_repurchased", - "dividends_paid": "dividends_paid", - "other_financing_activites": "other_financing_activities", - "net_cash_used_provided_by_financing_activities": "cash_flow_from_financing", - "effect_of_forex_changes_on_cash": "change_in_exchange_rate", - "net_change_in_cash": "net_change_in_cash", - "cash_at_end_of_period": "cash_at_end_of_period", - "cash_at_beginning_of_period": "cash_at_beginning_of_period", - "operating_cash_flow": "operating_cash_flow", - "capital_expenditure": "capital_expenditure", - "free_cash_flow": "free_cash_flow", - "link": "link", - "final_link": "final_link", - }, -} -CASH_PLOT_CHOICES = [ - "operating_cash_flow", - "payments_for_operating_activities", - "proceeds_from_operating_activities", - "change_in_operating_liabilities", - "change_in_operating_assets", - "depreciation_depletion_and_amortization", - "capital_expenditure", - "change_in_receivables", - "inventory", - "profit_loss", - "cash_flow_from_investment", - "cash_flow_from_financing", - "proceeds_from_repayments_of_short_term_debt", - "common_stock_repurchased", - "payments_for_repurchase_of_equity", - "payments_for_repurchase_of_preferred_stock", - "dividends_paid", - "dividend_payout_common_stock", - "dividend_payout_preferred_stock", - "common_stock_issued", - "proceeds_from_issuance_of_long_term_debt_and_capital_securities_net", - "proceeds_from_issuance_of_preferred_stock", - "proceeds_from_repurchase_of_equity", - "proceeds_from_sale_of_treasury_stock", - "net_change_in_cash", - "change_in_exchange_rate", - "net_income", - "depreciation_&_amortisation", - "net_cash_flow_from_financing_activities_continuing", - "net_cash_flow_from_investing_activities_continuing", - "net_cash_flow_continuing", - "net_cash_provided_by_operating_activities", - "deferred_income_taxes", - "stock_based_compensation", - "change_in_working_capital", - "accounts_payable", - "other_working_capital", - "investments_in_property,_plant_and_equipment", - "acquisitions_net", - "purchases_of_investments", - "sales_maturities_of_investments", - "other_investing_activities", - "debt_repayment", - "other_financing_activities", - "cash_at_beginning_of_period", - "cash_at_end_of_period", - "free_cash_flow", - "other_non_cash_items", -] diff --git a/openbb_terminal/stocks/stocks_controller.py b/openbb_terminal/stocks/stocks_controller.py deleted file mode 100644 index c814f613e1a..00000000000 --- a/openbb_terminal/stocks/stocks_controller.py +++ /dev/null @@ -1,820 +0,0 @@ -"""Stock Context Controller.""" - -__docformat__ = "numpy" - -import argparse -import logging -import os -from datetime import datetime, timedelta -from typing import List, Optional - -import financedatabase as fd - -from openbb_terminal import config_terminal -from openbb_terminal.common import ( - feedparser_view, - newsapi_view, -) -from openbb_terminal.common.quantitative_analysis import qa_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_RAW_DATA_ALLOWED, - export_data, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console, get_ordered_list_sources -from openbb_terminal.stocks import cboe_view, stocks_helper, stocks_view -from openbb_terminal.terminal_helper import suppress_stdout - -# pylint: disable=R1710,import-outside-toplevel,R0913,R1702,no-member - -logger = logging.getLogger(__name__) - - -class StocksController(StockBaseController): - """Stocks Controller class.""" - - CHOICES_COMMANDS = [ - "search", - "load", - "quote", - "tob", - "candle", - "news", - "resources", - "codes", - ] - CHOICES_MENUS = [ - "ta", - "ba", - "qa", - "disc", - "dps", - "scr", - "sia", - "ins", - "gov", - "res", - "dd", - "fa", - "bt", - "ca", - "options", - "th", - "forecast", - ] - - PATH = "/stocks/" - FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md") - - try: - stocks_options = fd.obtain_options("equities") - country = stocks_options["country"].tolist() - sector = stocks_options["sector"].tolist() - industry_group = stocks_options["industry_group"].tolist() - industry = stocks_options["industry"].tolist() - exchange = stocks_options["exchange"].tolist() - except Exception: - country, sector, industry_group, industry, exchange = {}, {}, {}, {}, {} - console.print( - "[red]Note: Some datasets from GitHub failed to load. This means that the `search` command will not work. " - "If other commands are failing please check your internet connection or communicate with your " - "IT department that certain websites are blocked.[/red] \n" - ) - - TOB_EXCHANGES = ["BZX", "EDGX", "BYX", "EDGA"] - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Construct stocks controller.""" - super().__init__(queue) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help.""" - stock_text = "" - if self.ticker: - s_intraday = (f"Intraday {self.interval}", "Daily")[ - self.interval == "1440min" - ] - stock_text += f"{s_intraday} {self.ticker}" - if self.start: - stock_text += f" (from {self.start.strftime('%Y-%m-%d')})" - - mt = MenuText("stocks/", 100) - mt.add_cmd("search") - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param("_ticker", stock_text) - mt.add_raw("\n") - mt.add_cmd("quote", self.ticker) - mt.add_cmd("tob", self.ticker) - mt.add_cmd("candle", self.ticker) - mt.add_cmd("codes", self.ticker) - mt.add_cmd("news", self.ticker) - mt.add_raw("\n") - mt.add_menu("options") - mt.add_menu("disc") - mt.add_menu("dps") - mt.add_menu("scr") - mt.add_menu("ins") - mt.add_menu("gov") - mt.add_menu("ba") - mt.add_menu("ca") - mt.add_menu("fa") - mt.add_menu("th") - mt.add_menu("bt") - mt.add_menu("ta") - mt.add_menu("qa") - mt.add_menu("forecast") - mt.add_menu("res", self.ticker) - - console.print(text=mt.menu_text, menu="Stocks") - - def custom_reset(self): - """Class specific component of reset command.""" - if self.ticker: - return ["stocks", f"load {self.ticker}"] - return [] - - def custom_load_wrapper(self, other_args: List[str]): - """Class specific component of load command""" - with suppress_stdout(): - self.call_load(other_args) - - @log_start_end(log=logger) - def call_search(self, other_args: List[str]): - """Process search command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="search", - description="Show companies matching the search query, country, sector, industry and/or exchange. " - "Note that by default only the United States exchanges are searched which tend to contain the most " - "extensive data for each company. To search all exchanges use the --all-exchanges flag.", - ) - parser.add_argument( - "-q", - "--query", - action="store", - dest="query", - type=str.lower, - default="", - nargs="+", - help="The search term used to find company tickers", - ) - clean_countries = [x.lower().replace(" ", "_") for x in self.country] - parser.add_argument( - "-c", - "--country", - default="", - choices=clean_countries, - dest="country", - metavar="country", - type=str.lower, - help="Search by country to find stocks matching the criteria", - ) - parser.add_argument( - "-s", - "--sector", - default="", - choices=stocks_helper.format_parse_choices(self.sector), - type=str.lower, - metavar="sector", - dest="sector", - help="Search by sector to find stocks matching the criteria", - ) - parser.add_argument( - "--industrygroup", - default="", - choices=stocks_helper.format_parse_choices(self.industry_group), - type=str.lower, - metavar="industry_group", - dest="industry_group", - help="Search by industry group to find stocks matching the criteria", - ) - parser.add_argument( - "-i", - "--industry", - default="", - choices=stocks_helper.format_parse_choices(self.industry), - type=str.lower, - metavar="industry", - dest="industry", - help="Search by industry to find stocks matching the criteria", - ) - parser.add_argument( - "-e", - "--exchange", - default="", - choices=stocks_helper.format_parse_choices(self.exchange), - type=str.lower, - metavar="exchange", - dest="exchange", - help="Search by a specific exchange to find stocks matching the criteria", - ) - parser.add_argument( - "--exchangecountry", - default="", - choices=stocks_helper.format_parse_choices( - list(stocks_helper.market_coverage_suffix.keys()) - ), - type=str.lower, - metavar="exchange_country", - dest="exchange_country", - help="Search by a specific country and all its exchanges to find stocks matching the criteria", - ) - parser.add_argument( - "-a", - "--all-exchanges", - default=False, - action="store_true", - dest="all_exchanges", - help="Whether to search all exchanges, without this option only the United States market is searched.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-q") - if ns_parser := self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_ONLY_RAW_DATA_ALLOWED, - limit=0, - ): - # Mapping - sector = stocks_helper.map_parse_choices(self.sector)[ns_parser.sector] - industry = stocks_helper.map_parse_choices(self.industry)[ - ns_parser.industry - ] - industry_group = stocks_helper.map_parse_choices(self.industry_group)[ - ns_parser.industry_group - ] - exchange = stocks_helper.map_parse_choices(self.exchange)[ - ns_parser.exchange - ] - exchange_country = stocks_helper.map_parse_choices( - list(stocks_helper.market_coverage_suffix.keys()) - )[ns_parser.exchange_country] - - df = stocks_helper.search( - query=" ".join(ns_parser.query), - country=ns_parser.country, - sector=sector, - industry_group=industry_group, - industry=industry, - exchange=exchange, - exchange_country=exchange_country, - all_exchanges=ns_parser.all_exchanges, - ) - if ns_parser.export: - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - "search", - df, - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None, - ) - if not ns_parser.export: - stocks_helper.print_rich_table( - df, - show_index=False, - headers=df.columns, - title="Stock Search Results", - ) - - @log_start_end(log=logger) - def call_tob(self, other_args: List[str]): - """Process tob command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="tob", - description="Get top of book for loaded ticker from selected exchange", - ) - parser.add_argument( - "-t", - "--ticker", - action="store", - dest="s_ticker", - required=all(x not in other_args for x in ["-h", "--help"]) - and not self.ticker, - help="Ticker to get data for", - ) - parser.add_argument( - "-e", - "--exchange", - default="BZX", - choices=self.TOB_EXCHANGES, - type=str, - dest="exchange", - ) - - if not self.ticker and other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - if ns_parser := self.parse_known_args_and_warn(parser, other_args): - ticker = ns_parser.s_ticker or self.ticker - cboe_view.display_top_of_book(ticker, ns_parser.exchange) - - @log_start_end(log=logger) - def call_quote(self, other_args: List[str]): - """Process quote command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="quote", - description="Current quote for the loaded stock ticker.", - ) - parser.add_argument( - "-t", - "--ticker", - action="store", - dest="s_ticker", - required=False, - default=self.ticker, - help="Get a quote for a specific ticker, or comma-separated list of tickers.", - ) - parser.add_argument( - "--load_source", - action="store", - dest="load_source", - required=False, - default=None, - choices=get_ordered_list_sources(f"{self.PATH}load"), - help=argparse.SUPPRESS, - ) - - # For the case where a user uses: 'quote BB' - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - tickers = ns_parser.s_ticker.split(",") - if ns_parser.s_ticker and len(tickers) == 1: - self.ticker = ns_parser.s_ticker - load_other_args = [self.ticker, "--source", ns_parser.load_source] - self.custom_load_wrapper(load_other_args) - - stocks_view.display_quote( - tickers, - ns_parser.export, - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None, - ) - - @log_start_end(log=logger) - def call_codes(self, _): - """Process codes command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="codes", - description="Show CIK, FIGI and SCI code from polygon for loaded ticker.", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze", - type=str, - default=None, - ) - ns_parser = self.parse_known_args_and_warn(parser, _) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - if self.ticker: - stocks_helper.show_codes_polygon(self.ticker) - self.custom_load_wrapper([self.ticker]) - else: - console.print("No ticker loaded. First use `load {ticker}`\n") - - @log_start_end(log=logger) - def call_candle(self, other_args: List[str]): - """Process candle command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="candle", - description="Shows historic price and volume for the asset.", - ) - parser.add_argument( - "-t", - "--ticker", - dest="ticker", - help="Ticker to analyze.", - type=str, - default=None, - required=not any(x in other_args for x in ["-h", "--help"]) - and not self.ticker, - ) - parser.add_argument( - "-p", - "--prepost", - action="store_true", - default=False, - dest="prepost", - help="Pre/After market hours. Only works for intraday data.", - ) - parser.add_argument( - "--sort", - choices=stocks_helper.CANDLE_SORT, - default="", - type=str.lower, - dest="sort", - help="Choose a column to sort by. Only works when raw data is displayed.", - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "--raw", - action="store_true", - dest="raw", - default=False, - help="Shows raw data instead of a chart.", - ) - parser.add_argument( - "--trend", - action="store_true", - default=False, - help="Flag to add high and low trends to candle.", - dest="trendlines", - ) - parser.add_argument( - "--ma", - dest="mov_avg", - type=str, - help=( - "Add moving average in number of days to plot and separate by a comma. " - "Value for ma (moving average) keyword needs to be greater than 1." - ), - default=None, - ) - parser.add_argument( - "--ha", - dest="ha", - action="store_true", - default=False, - help="Flag to show Heikin Ashi candles.", - ) - parser.add_argument( - "--log", - help="Plot with y axis on log scale", - action="store_true", - default=False, - dest="logy", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - limit=20, - ) - if config_terminal.HOLD: - console.print("[red]Hold functionality not supported with candle.[/]") - return - - if ns_parser: - figure_export = None - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if self.ticker: - if ns_parser.raw: - qa_view.display_raw( - data=self.stock, - sortby=ns_parser.sort, - ascend=ns_parser.reverse, - limit=ns_parser.limit, - ) - else: - data = stocks_helper.process_candle(self.stock) - mov_avgs = [] - - if ns_parser.mov_avg: - mov_list = (num for num in ns_parser.mov_avg.split(",")) - - for num in mov_list: - try: - clean_num = int(num) - - if clean_num <= 1: - raise ValueError - - mov_avgs.append(clean_num) - except ValueError: - console.print( - f"[red]{num} is not a valid moving average, must be an integer greater than 1." - ) - - figure_export = stocks_helper.display_candle( - symbol=self.ticker, - data=data, - add_trend=ns_parser.trendlines, - ma=mov_avgs, - ha=ns_parser.ha, - prepost=ns_parser.prepost, - asset_type="", - yscale="log" if ns_parser.logy else "linear", - external_axes=ns_parser.is_image, - ) - - export_data( - ns_parser.export, - os.path.dirname(os.path.abspath(__file__)), - f"{self.ticker}", - self.stock, - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None, - figure=figure_export, - ) - else: - console.print("No ticker loaded. First use 'load '") - - @log_start_end(log=logger) - def call_news(self, other_args: List[str]): - """Process news command.""" - parser = argparse.ArgumentParser( - add_help=False, - prog="news", - description="latest news of the company", - ) - parser.add_argument( - "-t", - "--ticker", - action="store", - dest="ticker", - required=not any(x in other_args for x in ["-h", "--help"]) - and not self.ticker, - help="Ticker to get data for", - ) - parser.add_argument( - "-d", - "--date", - action="store", - dest="n_start_date", - type=valid_date, - default=datetime.now() - timedelta(days=7), - help="The starting date (format YYYY-MM-DD) to search articles from", - ) - parser.add_argument( - "-o", - "--oldest", - action="store_false", - dest="n_oldest", - default=True, - help="Show oldest articles first", - ) - parser.add_argument( - "-s", - "--sources", - dest="sources", - type=str, - default="", - help="Show news only from the sources specified (e.g bloomberg,reuters)", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-t") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10 - ) - if ns_parser: - if ns_parser.ticker: - self.ticker = ns_parser.ticker - self.custom_load_wrapper([self.ticker]) - if self.ticker: - if ns_parser.source == "NewsApi": - newsapi_view.display_news( - query=self.ticker, - limit=ns_parser.limit, - start_date=ns_parser.n_start_date.strftime("%Y-%m-%d"), - show_newest=ns_parser.n_oldest, - sources=ns_parser.sources, - ) - elif ns_parser.source == "Feedparser": - feedparser_view.display_news( - term=self.ticker, - sources=ns_parser.sources, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) - if ns_parser.sheet_name - else None - ), - ) - else: - console.print("Use 'load ' prior to this command!") - - @log_start_end(log=logger) - def call_disc(self, _): - """Process disc command.""" - from openbb_terminal.stocks.discovery.disc_controller import DiscoveryController - - self.queue = self.load_class(DiscoveryController, self.queue) - - @log_start_end(log=logger) - def call_dps(self, _): - """Process dps command.""" - from openbb_terminal.stocks.dark_pool_shorts.dps_controller import ( - DarkPoolShortsController, - ) - - self.queue = self.load_class( - DarkPoolShortsController, self.ticker, self.start, self.stock, self.queue - ) - - @log_start_end(log=logger) - def call_scr(self, _): - """Process scr command.""" - from openbb_terminal.stocks.screener.screener_controller import ( - ScreenerController, - ) - - self.queue = self.load_class(ScreenerController, self.queue) - - @log_start_end(log=logger) - def call_sia(self, _): - """Process sia command.""" - # from openbb_terminal.stocks.sector_industry_analysis.sia_controller import ( - # SectorIndustryAnalysisController, - # ) - - # self.queue = self.load_class( - # SectorIndustryAnalysisController, self.ticker, self.queue - # ) - - # TODO: Make the call_sia command available again after improving the functionality - # TODO: Update test_stocks_controller.py to reflect the changes - - console.print( - "The sia (Sector & Industry Analysis) menu is currently inactive as the functionality is " - "better represented through the stocks/ca, stocks/fa and routines functionalities. " - "Improvements to this menu is on the projects list." - ) - - @log_start_end(log=logger) - def call_ins(self, _): - """Process ins command.""" - from openbb_terminal.stocks.insider.insider_controller import InsiderController - - self.queue = self.load_class( - InsiderController, - self.ticker, - self.start, - self.interval, - self.stock, - self.queue, - ) - - @log_start_end(log=logger) - def call_gov(self, _): - """Process gov command.""" - from openbb_terminal.stocks.government.gov_controller import GovController - - self.queue = self.load_class(GovController, self.ticker, self.queue) - - @log_start_end(log=logger) - def call_options(self, _): - """Process options command.""" - from openbb_terminal.stocks.options.options_controller import OptionsController - - self.queue = self.load_class(OptionsController, self.ticker, self.queue) - - @log_start_end(log=logger) - def call_th(self, _): - """Process th command.""" - from openbb_terminal.stocks.tradinghours import tradinghours_controller - - self.queue = self.load_class( - tradinghours_controller.TradingHoursController, - self.ticker, - self.queue, - ) - - @log_start_end(log=logger) - def call_res(self, _): - """Process res command.""" - if self.ticker: - from openbb_terminal.stocks.research.res_controller import ( - ResearchController, - ) - - self.queue = self.load_class( - ResearchController, self.ticker, self.start, self.interval, self.queue - ) - else: - console.print("Use 'load ' prior to this command!") - - @log_start_end(log=logger) - def call_dd(self, _): - """Process dd command.""" - - # TODO: Get rid of the call_dd on the next release since it has been deprecated. - - console.print( - "The dd (Due Diligence) menu has been integrated into the fa (Fundamental Analysis) menu. " - "Please use this menu instead.\n" - ) - - @log_start_end(log=logger) - def call_ca(self, _): - """Process ca command.""" - from openbb_terminal.stocks.comparison_analysis import ca_controller - - self.queue = self.load_class( - ca_controller.ComparisonAnalysisController, - [f"{self.ticker}"] if self.ticker else "", - self.queue, - ) - - @log_start_end(log=logger) - def call_fa(self, _): - """Process fa command.""" - from openbb_terminal.stocks.fundamental_analysis import fa_controller - - self.queue = self.load_class( - fa_controller.FundamentalAnalysisController, - self.ticker, - self.start, - self.interval, - self.stock, - self.suffix, - self.queue, - ) - - @log_start_end(log=logger) - def call_bt(self, _): - """Process bt command.""" - from openbb_terminal.stocks.backtesting import bt_controller - - self.queue = self.load_class( - bt_controller.BacktestingController, self.ticker, self.stock, self.queue - ) - - @log_start_end(log=logger) - def call_ta(self, _): - """Process ta command.""" - from openbb_terminal.stocks.technical_analysis import ta_controller - - self.queue = self.load_class( - ta_controller.TechnicalAnalysisController, - self.ticker, - self.start, - self.interval, - self.stock, - self.queue, - ) - - @log_start_end(log=logger) - def call_ba(self, _): - """Process ba command.""" - from openbb_terminal.stocks.behavioural_analysis import ba_controller - - self.queue = self.load_class( - ba_controller.BehaviouralAnalysisController, - self.ticker, - self.start, - self.queue, - ) - - @log_start_end(log=logger) - def call_qa(self, _): - """Process qa command.""" - from openbb_terminal.stocks.quantitative_analysis import qa_controller - - self.queue = self.load_class( - qa_controller.QaController, - self.ticker, - self.start, - self.interval, - self.stock, - self.queue, - ) - - @log_start_end(log=logger) - def call_forecast(self, _): - """Process forecast command.""" - from openbb_terminal.forecast import forecast_controller - - self.queue = self.load_class( - forecast_controller.ForecastController, - ticker=self.ticker, - queue=self.queue, - ) diff --git a/openbb_terminal/stocks/stocks_helper.py b/openbb_terminal/stocks/stocks_helper.py deleted file mode 100644 index 933078187ac..00000000000 --- a/openbb_terminal/stocks/stocks_helper.py +++ /dev/null @@ -1,1131 +0,0 @@ -"""Main helper.""" - -__docformat__ = "numpy" -# pylint: disable=too-many-lines, unsupported-assignment-operation -# pylint: disable=no-member, too-many-branches, too-many-arguments -# pylint: disable=inconsistent-return-statements -# pylint: disable=consider-using-dict-items -# pylint: disable=too-many-lines -import logging -import os -from copy import deepcopy -from datetime import datetime, timedelta -from typing import Any, Dict, Iterable, List, Optional, Union - -import financedatabase as fd -import numpy as np -import pandas as pd -import pytz -import yfinance as yf -from pandas.tseries.holiday import USFederalHolidayCalendar -from pandas_ta import candles -from requests.exceptions import ReadTimeout -from scipy import stats - -from openbb_terminal.core.plots.plotly_helper import OpenBBFigure -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.helper_funcs import get_user_timezone, print_rich_table, request -from openbb_terminal.rich_config import console - -# pylint: disable=unused-import -from openbb_terminal.stocks.stock_statics import ( - BALANCE_PLOT, # noqa: F401 - BALANCE_PLOT_CHOICES, # noqa: F401 - CANDLE_SORT, # noqa: F401 - CASH_PLOT, # noqa: F401 - CASH_PLOT_CHOICES, # noqa: F401 - INCOME_PLOT, # noqa: F401 - INCOME_PLOT_CHOICES, # noqa: F401 - INTERVALS, # noqa: F401 - SOURCES, # noqa: F401 - market_coverage_suffix, -) -from openbb_terminal.stocks.stocks_model import ( - load_stock_av, - load_stock_eodhd, - load_stock_intrinio, - load_stock_polygon, - load_stock_yf, -) - -from . import databento_model - -logger = logging.getLogger(__name__) - -exch_file_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "mappings", "Mic_Codes.csv" -) -exchange_df = pd.read_csv(exch_file_path, index_col=0, header=None) -exchange_mappings = exchange_df.squeeze("columns").to_dict() - - -def check_datetime( - ck_date: Optional[Union[datetime, str]] = None, start: bool = True -) -> datetime: - """Check if given argument is string and attempts to convert to datetime. - - Parameters - ---------- - ck_date : Optional[Union[datetime, str]], optional - Date to check, by default None - start : bool, optional - If True and string is invalid, will return 1100 days ago - If False and string is invalid, will return today, by default True - - Returns - ------- - datetime - Datetime object - """ - error_catch = (datetime.now() - timedelta(days=1100)) if start else datetime.now() - try: - if ck_date is None: - return error_catch - if isinstance(ck_date, datetime): - return ck_date - if isinstance(ck_date, str): - return datetime.strptime(ck_date, "%Y-%m-%d") - except Exception: - console.print( - f"Invalid date format (YYYY-MM-DD), " - f"Using {error_catch.strftime('%Y-%m-%d')} for {ck_date}" - ) - return error_catch - - -def get_holidays( - start: Optional[Union[datetime, str]] = None, - end: Optional[Union[datetime, str]] = None, -) -> List[datetime]: - """Get holidays between start and end dates. - - Parameters - ---------- - start : Optional[Union[datetime, str]], optional - Start date, by default None - end : Optional[Union[datetime, str]], optional - End date, by default None - """ - start = check_datetime(start) - end = check_datetime(end, start=False) - return USFederalHolidayCalendar().holidays(start=start, end=end) - - -def search( - query: str = "", - country: str = "", - sector: str = "", - industry_group: str = "", - industry: str = "", - exchange: str = "", - exchange_country: str = "", - all_exchanges: bool = False, -) -> pd.DataFrame: - """Search selected query for tickers. - - Parameters - ---------- - query : str - The search term used to find company tickers - country: str - Search by country to find stocks matching the criteria - sector : str - Search by sector to find stocks matching the criteria - industry_group : str - Search by industry group to find stocks matching the criteria - industry : str - Search by industry to find stocks matching the criteria - exchange: str - Search by exchange to find stock matching the criteria - exchange_country: str - Search by exchange country to find stock matching the criteria - all_exchanges: bool - Whether to search all exchanges, without this option only the United States market is searched - - Returns - ------- - df: pd.DataFrame - Dataframe of search results. - Empty Dataframe if none are found. - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.search(country="United States", exchange_country="Germany") - """ - kwargs: Dict[str, Any] = {"exclude_exchanges": False} - if country: - kwargs["country"] = country.replace("_", " ").title() - if sector: - kwargs["sector"] = sector - if industry: - kwargs["industry"] = industry - if industry_group: - kwargs["industry_group"] = industry_group - if exchange: - kwargs["exchange"] = exchange - kwargs["exclude_exchanges"] = ( - False if (exchange_country or exchange) else not all_exchanges - ) - - try: - equities_database = fd.Equities() - - if query: - data = equities_database.search(**kwargs, name=query) - data = pd.concat([data, equities_database.search(**kwargs, name=query)]) - data = pd.concat( - [data, equities_database.search(**kwargs, index=query.upper())] - ) - - data = data.drop_duplicates() - else: - data = equities_database.search(**kwargs) - except ReadTimeout: - console.print( - "[red]Unable to retrieve company data from GitHub which limits the search" - " capabilities. This tends to be due to access restrictions for GitHub.com," - " please check if you can access this website without a VPN.[/red]\n" - ) - data = pd.DataFrame() - except ValueError: - console.print( - "[red]No companies were found that match the given criteria.[/red]\n" - ) - return pd.DataFrame() - - if data.empty: - console.print("No companies found.\n") - return pd.DataFrame() - - df = data[ - [ - "name", - "country", - "sector", - "industry_group", - "industry", - "exchange", - "isin", - "cusip", - "figi", - "composite_figi", - "shareclass_figi", - ] - ] - - if exchange_country and exchange_country in market_coverage_suffix: - suffix_tickers = [ - ticker.split(".")[1] if "." in str(ticker) else "" - for ticker in list(df.index) - ] - df = df[ - [val in market_coverage_suffix[exchange_country] for val in suffix_tickers] - ] - - exchange_suffix = {} - for k, v in market_coverage_suffix.items(): - for x in v: - exchange_suffix[x] = k - - df = df[ - [ - "name", - "country", - "sector", - "industry_group", - "industry", - "exchange", - "isin", - "cusip", - "figi", - "composite_figi", - "shareclass_figi", - ] - ] - # To automate renaming columns - df.columns = [col.replace("_", " ") for col in df.columns.tolist()] - df = df.fillna(value=np.nan) - df = df.iloc[df.isnull().sum(axis=1).mul(1).argsort()] - - return df.reset_index() - - -def get_daily_stock_candidate( - source: str, - symbol: str, - int_string: str, - start_date, - end_date, - monthly: bool, - weekly: bool, -) -> pd.DataFrame: - if source == "AlphaVantage": - return load_stock_av(symbol, int_string, start_date, end_date) - - if source == "YahooFinance": - return load_stock_yf(symbol, start_date, end_date, weekly, monthly) - - if source == "EODHD": - try: - return load_stock_eodhd(symbol, start_date, end_date, weekly, monthly) - except KeyError: - console.print( - "[red]Invalid symbol for EODHD. Please check your subscription.[/red]\n" - ) - return pd.DataFrame() - - if source == "Polygon": - return load_stock_polygon(symbol, start_date, end_date, weekly, monthly) - - if source == "Intrinio": - return load_stock_intrinio(symbol, start_date, end_date, weekly, monthly) - - if source == "DataBento": - return databento_model.get_historical_stock( - symbol, start_date.strftime("%Y-%m-%d"), end_date.strftime("%Y-%m-%d") - ) - - console.print("[red]Invalid source for stock[/red]\n") - return pd.DataFrame() - - -def load( - symbol: str, - start_date: Optional[Union[datetime, str]] = None, - interval: int = 1440, - end_date: Optional[Union[datetime, str]] = None, - prepost: bool = False, - source: str = "YahooFinance", - weekly: bool = False, - monthly: bool = False, - verbose: bool = True, -): - """Load a symbol to perform analysis using the string above as a template. - - Optional arguments and their descriptions are listed above. - - The default source is, yFinance (https://pypi.org/project/yfinance/). - Other sources: - - AlphaVantage (https://www.alphavantage.co/documentation/) - - Eod Historical Data (https://eodhistoricaldata.com/financial-apis/) - - Please note that certain analytical features are exclusive to the specific source. - - To load a symbol from an exchange outside of the NYSE/NASDAQ default, use yFinance as the source and - add the corresponding exchange to the end of the symbol. i.e. `BNS.TO`. Note this may be possible with - other paid sources check their docs. - - BNS is a dual-listed stock, there are separate options chains and order books for each listing. - Opportunities for arbitrage may arise from momentary pricing discrepancies between listings - with a dynamic exchange rate as a second order opportunity in ForEx spreads. - - Find the full list of supported exchanges here: - https://help.yahoo.com/kb/exchanges-data-providers-yahoo-finance-sln2310.html - - Certain analytical features, such as VWAP, require the ticker to be loaded as intraday - using the `-i x` argument. When encountering this error, simply reload the symbol using - the interval argument. i.e. `load -t BNS -s YYYY-MM-DD -i 1 -p` loads one-minute intervals, - including Pre/After Market data, using the default source, yFinance. - - Certain features, such as the Prediction menu, require the symbol to be loaded as daily and not intraday. - - Parameters - ---------- - symbol: str - Ticker to get data - start_date: str or datetime, optional - Start date to get data from with. - datetime or string format (YYYY-MM-DD) - interval: int - Interval (in minutes) to get data 1, 5, 15, 30, 60 or 1440 - end_date: str or datetime, optional - End date to get data from with. - datetime or string format (YYYY-MM-DD) - prepost: bool - Pre and After hours data - source: str - Source of data extracted - weekly: bool - Flag to get weekly data - monthly: bool - Flag to get monthly data - verbose: bool - Display verbose information on what was the symbol that was loaded - - Returns - ------- - df_stock_candidate: pd.DataFrame - Dataframe of data - """ - start_date = start_date or (datetime.now() - timedelta(days=1100)).strftime( - "%Y-%m-%d" - ) - end_date = end_date or datetime.now().strftime("%Y-%m-%d") - start_date = check_datetime(start_date) - end_date = check_datetime(end_date, start=False) - - int_string = "Monthly" if monthly else "Weekly" if weekly else "Daily" - - # Daily - if int(interval) == 1440: - df_stock_candidate = get_daily_stock_candidate( - source, symbol, int_string, start_date, end_date, monthly, weekly - ) - is_df = isinstance(df_stock_candidate, pd.DataFrame) - if (is_df and df_stock_candidate.empty) or ( - not is_df and not df_stock_candidate - ): - return pd.DataFrame() - - df_stock_candidate.index.name = "date" - s_start = df_stock_candidate.index[0] - s_interval = f"{interval}min" - - else: - if source == "AlphaVantage": - s_start = start_date - int_string = "Minute" - s_interval = f"{interval}min" - if end_date: - end_date = (end_date + timedelta(days=1)).strftime("%Y-%m-%d") - end_date = datetime.strptime(end_date, "%Y-%m-%d") - df_stock_candidate = load_stock_av( - symbol, int_string, start_date, end_date, s_interval - ) - s_start = df_stock_candidate.index[0] - - elif source == "YahooFinance": - s_int = str(interval) + "m" - s_interval = s_int + "in" - - # add 1 day to end_date to include the last day - if end_date: - end_date = (end_date + timedelta(days=1)).strftime("%Y-%m-%d") - else: - end_date = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d") - - df_stock_candidate = yf.download( - symbol, - start=start_date.strftime("%Y-%m-%d"), - end=end_date, - progress=False, - interval=s_int, - prepost=prepost, - show_errors=False, - ) - # Handle the case when start and end dates aren't explicitly set - # TODO: This is a temporary fix, need to find a better way to handle this - if df_stock_candidate.empty: - d_granularity = {"1m": 6, "5m": 59, "15m": 59, "30m": 59, "60m": 729} - s_start_dt = datetime.utcnow() - timedelta(days=d_granularity[s_int]) - s_date_start = s_start_dt.strftime("%Y-%m-%d") - df_stock_candidate = yf.download( - symbol, - start=( - s_date_start - if s_start_dt > start_date - else start_date.strftime("%Y-%m-%d") - ), - progress=False, - interval=s_int, - prepost=prepost, - ) - - # Check that loading a stock was not successful - if df_stock_candidate.empty: - console.print("[red]No results found in yahoo finance reply.[/red]") - return pd.DataFrame() - - df_stock_candidate.index = pd.to_datetime( - df_stock_candidate.index - ).tz_localize(None) - - s_start_dt = df_stock_candidate.index[0] - - s_start = ( - pytz.utc.localize(s_start_dt) if s_start_dt > start_date else start_date - ) - - df_stock_candidate.index.name = "date" - - elif source == "Intrinio": - console.print( - "[red]We currently do not support intraday data with Intrinio.[/red]\n" - ) - return pd.DataFrame() - - elif source == "Polygon": - request_url = ( - f"https://api.polygon.io/v2/aggs/ticker/" - f"{symbol.upper()}/range/{interval}/minute/{start_date.strftime('%Y-%m-%d')}" - f"/{end_date.strftime('%Y-%m-%d')}" - f"?adjusted=true&sort=desc&limit=49999&apiKey={get_current_user().credentials.API_POLYGON_KEY}" - ) - r = request(request_url) - if r.status_code != 200: - console.print("[red]Error in polygon request[/red]") - return pd.DataFrame() - - r_json = r.json() - if "results" not in r_json: - console.print("[red]No results found in polygon reply.[/red]") - return pd.DataFrame() - - df_stock_candidate = pd.DataFrame(r_json["results"]) - - df_stock_candidate = df_stock_candidate.rename( - columns={ - "o": "Open", - "c": "Close", - "h": "High", - "l": "Low", - "t": "date", - "v": "Volume", - "n": "Transactions", - } - ) - # pylint: disable=unsupported-assignment-operation - df_stock_candidate["date"] = pd.to_datetime( - df_stock_candidate.date, unit="ms" - ) - df_stock_candidate["Adj Close"] = df_stock_candidate.Close - df_stock_candidate = df_stock_candidate.sort_values(by="date") - - df_stock_candidate = df_stock_candidate.set_index("date") - # Check that loading a stock was not successful - if df_stock_candidate.empty: - return pd.DataFrame() - - df_stock_candidate.index = ( - df_stock_candidate.index.tz_localize(tz="UTC") - .tz_convert(get_user_timezone()) - .tz_localize(None) - ) - s_start_dt = df_stock_candidate.index[0] - - s_start = ( - pytz.utc.localize(s_start_dt) if s_start_dt > start_date else start_date - ) - s_interval = f"{interval}min" - - elif source == "EODHD": - df_stock_candidate = load_stock_eodhd( - symbol, start_date, end_date, weekly, monthly, intraday=True - ) - - if df_stock_candidate.empty: - return pd.DataFrame() - - df_stock_candidate.index = df_stock_candidate.index.tz_convert( - get_user_timezone() - ).tz_localize(None) - - s_start_dt = df_stock_candidate.index[0] - - s_start = ( - pytz.utc.localize(s_start_dt) if s_start_dt > start_date else start_date - ) - - s_interval = f"{interval}min" - - else: - console.print("[red]Invalid intraday data source[/red]") - return pd.DataFrame() - - if not prepost: - df_stock_candidate = df_stock_candidate.between_time("9:30", "16:00") - - int_string = "Intraday" - - s_intraday = (f"Intraday {interval}min", int_string)[interval == 1440] - - if verbose: - console.print( - f"Loading {s_intraday} data for {symbol.upper()} " - f"with starting period {s_start.strftime('%Y-%m-%d')}." - ) - - df_stock_candidate.name = symbol.upper() - - return df_stock_candidate - - -def display_candle( - symbol: str, - data: Optional[pd.DataFrame] = None, - add_trend: bool = False, - ma: Optional[Iterable[int]] = None, - asset_type: str = "", - start_date: Optional[Union[datetime, str]] = None, - interval: int = 1440, - end_date: Optional[Union[datetime, str]] = None, - prepost: bool = False, - source: str = "YahooFinance", - weekly: bool = False, - monthly: bool = False, - ha: Optional[bool] = False, - external_axes: bool = False, - raw: bool = False, - yscale: str = "linear", -) -> Union[None, OpenBBFigure]: - """Show candle plot of loaded ticker. - - [Source: Yahoo Finance, IEX Cloud or Alpha Vantage] - - Parameters - ---------- - symbol: str - Ticker name - data: pd.DataFrame - Stock dataframe - add_trend: bool - Flag to add high and low trends to chart - ma: Tuple[int] - Moving averages to add to the candle - asset_type: str - String to include in title - start_date: str or datetime, optional - Start date to get data from with. - datetime or string format (YYYY-MM-DD) - interval: int - Interval (in minutes) to get data 1, 5, 15, 30, 60 or 1440 - end_date: str or datetime, optional - End date to get data from with. - datetime or string format (YYYY-MM-DD) - prepost: bool - Pre and After hours data - source: str - Source of data extracted - weekly: bool - Flag to get weekly data - monthly: bool - Flag to get monthly data - ha: bool - Flag to show Heikin Ashi candles. - external_axes : bool, optional - Whether to return the figure object or not, by default False - raw : bool, optional - Flag to display raw data, by default False - yscale: str - Linear or log for yscale - - Examples - -------- - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.candle("AAPL") - """ - # We are not actually showing adj close in candle. This hasn't been an issue so far, but adding - # in intrinio returns all adjusted columns,so some care here is needed or else we end up with - # mixing up close and adj close - if data is None: - # For mypy - data = pd.DataFrame() - data = deepcopy(data) - - if "Adj Close" in data.columns: - data["Close"] = data["Adj Close"].copy() - - if start_date is None: - start_date = (datetime.now() - timedelta(days=1100)).strftime("%Y-%m-%d") - - if end_date is None: - end_date = datetime.now().strftime("%Y-%m-%d") - - start_date = check_datetime(start_date) - end_date = check_datetime(end_date, start=False) - - if data is None or data.empty: - data = load( - symbol, - start_date, - interval, - end_date, - prepost, - source, - weekly, - monthly, - ) - data = process_candle(data) - - if add_trend and (data.index[1] - data.index[0]).total_seconds() >= 86400: - data = find_trendline(data, "OC_High", "high") - data = find_trendline(data, "OC_Low", "low") - - if raw: - return data - - kwargs = {} - if ma: - kwargs["rma"] = dict(length=ma) - - if data.index[-2].date() == data.index[-1].date(): - interval = int((data.index[1] - data.index[0]).seconds / 60) - - data.name = f"{asset_type} {symbol}" - - if ha: - data_ = heikin_ashi(data) - data["Open"] = data_["HA Open"] - data["High"] = data_["HA High"] - data["Low"] = data_["HA Low"] - data["Close"] = data_["HA Close"] - data.name = f"{symbol} - Heikin Ashi Candles" - - fig = PlotlyTA.plot(data, dict(**kwargs)) - - if add_trend: - fig.add_trend(data, secondary_y=False) - - fig.update_layout(yaxis=dict(type=yscale)) - - return fig.show(external=external_axes) - - -def process_candle(data: pd.DataFrame) -> pd.DataFrame: - """Process DataFrame into candle style plot. - - Parameters - ---------- - data : DataFrame - Stock dataframe. - - Returns - ------- - DataFrame - A Panda's data frame with columns Open, High, Low, Close, Adj Close, Volume, - date_id, OC-High, OC-Low. - """ - df_data = data.copy() - df_data["date_id"] = (df_data.index.date - df_data.index.date.min()).astype( - "timedelta64[D]" - ) - df_data["date_id"] = df_data["date_id"].dt.days + 1 - - df_data["OC_High"] = df_data[["Open", "Close"]].max(axis=1) - df_data["OC_Low"] = df_data[["Open", "Close"]].min(axis=1) - - return df_data - - -def find_trendline( - df_data: pd.DataFrame, y_key: str, high_low: str = "high" -) -> pd.DataFrame: - """Attempt to find a trend line based on y_key column from a given stock ticker data frame. - - Parameters - ---------- - df_data : DataFrame - The stock ticker data frame with at least date_id, y_key columns. - y_key : str - Column name to base the trend line on. - high_low: str, optional - Either "high" or "low". High is the default. - - Returns - ------- - DataFrame - If a trend is successfully found, - An updated Panda's data frame with a trend data {y_key}_trend column. - If no trend was found, - An original Panda's data frame - """ - for iteration in [3, 4, 5, 6, 7]: - df_temp = df_data.copy() - while len(df_temp) > iteration: - reg = stats.linregress(x=df_temp["date_id"], y=df_temp[y_key]) - - if high_low == "high": - df_temp = df_temp.loc[ - df_temp[y_key] > reg[0] * df_temp["date_id"] + reg[1] - ] - else: - df_temp = df_temp.loc[ - df_temp[y_key] < reg[0] * df_temp["date_id"] + reg[1] - ] - - if len(df_temp) > 1: - break - - if len(df_temp) == 1: - return df_data - - reg = stats.linregress(x=df_temp["date_id"], y=df_temp[y_key]) - - df_data[f"{y_key}_trend"] = reg[0] * df_data["date_id"] + reg[1] - - return df_data - - -def additional_info_about_ticker(ticker: str) -> str: - """Get information about trading the ticker. - - Includes exchange, currency, timezone and market status. - - Parameters - ---------- - ticker : str - The stock ticker to extract if stock market is open or not - Returns - ------- - str - Additional information about trading the ticker - """ - extra_info = "" - - if ticker: - if ".US" in ticker.upper(): - ticker = ticker.rstrip(".US") - ticker = ticker.rstrip(".us") - ticker_stats = yf.Ticker(ticker).stats() - extra_info += "\n[param]Company: [/param]" - extra_info += ticker_stats.get("quoteType", {}).get("shortName") - extra_info += "\n[param]Exchange: [/param]" - exchange_name = ticker_stats.get("quoteType", {}).get("exchange") - extra_info += ( - exchange_mappings["X" + exchange_name] - if "X" + exchange_name in exchange_mappings - else exchange_name - ) - - extra_info += "\n[param]Currency: [/param]" - extra_info += ticker_stats.get("summaryDetail", {}).get("currency") - - else: - extra_info += "\n[param]Company: [/param]" - extra_info += "\n[param]Exchange: [/param]" - extra_info += "\n[param]Currency: [/param]" - - return extra_info + "\n" - - -def clean_fraction(num, denom): - """Return the decimal value or NA if the operation cannot be performed. - - Parameters - ---------- - num : Any - The numerator for the fraction - denom : Any - The denominator for the fraction - - Returns - ------- - val : Any - The value of the fraction - """ - try: - return num / denom - except TypeError: - return "N/A" - - -def load_custom(file_path: str) -> pd.DataFrame: - """Load in a custom csv file. - - Parameters - ---------- - file_path: str - Path to file - - Returns - ------- - pd.DataFrame - Dataframe of stock data - """ - # Double check that the file exists - if not os.path.exists(file_path): - console.print("[red]File path does not exist.[/red]\n") - return pd.DataFrame() - - if file_path.endswith(".csv"): - df = pd.read_csv(file_path) - elif file_path.endswith(".xlsx"): - df = pd.read_excel(file_path) - else: - console.print("[red]File type not supported.[/red]\n") - return pd.DataFrame() - - console.print(f"Loaded data has columns: {', '.join(df.columns.to_list())}\n") - - # Nasdaq specific - if "Close/Last" in df.columns: - df = df.rename(columns={"Close/Last": "Close"}) - if "Last" in df.columns: - df = df.rename(columns={"Last": "Close"}) - - df.columns = [col.lower().rstrip().lstrip() for col in df.columns] - - for col in df.columns: - if col in ["date", "time", "timestamp", "datetime"]: - df[col] = pd.to_datetime(df[col]) - df = df.set_index(col) - console.print(f"Column [blue]{col.title()}[/blue] set as index.") - - df.columns = [col.title() for col in df.columns] - df.index.name = df.index.name.title() - - df = df.applymap( - lambda x: clean_function(x) if not isinstance(x, (int, float)) else x - ) - if "Adj Close" not in df.columns: - df["Adj Close"] = df.Close.copy() - - return df - - -def clean_function(entry: str) -> Union[str, float]: - """Clean stock data from csv. - - This can be customized for csvs. - """ - # If there is a digit, get rid of common characters and return float - if any(char.isdigit() for char in entry): - return float(entry.replace("$", "").replace(",", "")) - return entry - - -def show_quick_performance( - stock_df: pd.DataFrame, - ticker: str, -) -> None: - """Show quick performance stats of stock prices. - - Daily prices expected. - """ - closes = stock_df["Adj Close"] - volumes = stock_df["Volume"] - - perfs = { - "1 Day": 100 * closes.pct_change(2)[-1], - "1 Week": 100 * closes.pct_change(5)[-1], - "1 Month": 100 * closes.pct_change(21)[-1], - "1 Year": 100 * closes.pct_change(252)[-1], - } - - closes_ytd = closes[closes.index.year == pd.to_datetime("today").year] - if not closes_ytd.empty: - perfs["YTD"] = 100 * (closes_ytd[-1] - closes_ytd[0]) / closes_ytd[0] - else: - perfs["Period"] = 100 * (closes[-1] - closes[0]) / closes[0] - - perf_df = pd.DataFrame.from_dict(perfs, orient="index").dropna().T - perf_df = perf_df.applymap(lambda x: str(round(x, 2)) + " %") - if len(closes) > 252: - perf_df["Volatility (1Y)"] = ( - str(round(100 * np.sqrt(252) * closes[-252:].pct_change().std(), 2)) + " %" - ) - else: - perf_df["Volatility (Ann)"] = ( - str(round(100 * np.sqrt(252) * closes.pct_change().std(), 2)) + " %" - ) - if len(volumes) > 10: - perf_df["Volume (10D avg)"] = ( - str(round(np.mean(volumes[-12:-2]) / 1_000_000, 2)) + " M" - ) - - perf_df["Previous Close"] = str(round(closes[-1], 2)) - - print_rich_table( - perf_df, - show_index=False, - headers=perf_df.columns, - title=f"{ticker.upper()} Performance", - ) - - -def show_codes_polygon(ticker: str): - """Show FIGI, SIC and SIK codes for ticker. - - Parameters - ---------- - ticker: str - Stock ticker - """ - - current_user = get_current_user() - - link = ( - f"https://api.polygon.io/v3/reference/tickers/{ticker.upper()}?apiKey=" - f"{current_user.credentials.API_POLYGON_KEY}" - ) - if current_user.credentials.API_POLYGON_KEY == "REPLACE_ME": - console.print("[red]Polygon API key missing[/red]\n") - return - r = request(link) - if r.status_code != 200: - console.print("[red]Error in polygon request[/red]\n") - return - r_json = r.json() - if "results" not in r_json: - console.print("[red]Results not found in polygon request[/red]") - return - r_json = r_json["results"] - cols = ["cik", "composite_figi", "share_class_figi", "sic_code"] - vals = [r_json[col] for col in cols] - polygon_df = pd.DataFrame( - {"codes": [c.upper() for c in cols], "values": vals}, - columns=["codes", "values"], - ) - polygon_df.codes = polygon_df.codes.apply(lambda x: x.replace("_", " ")) - print_rich_table( - polygon_df, - show_index=False, - headers=["code", "value"], - title=f"{ticker.upper()} Codes", - ) - - -def format_parse_choices(choices: List[str]) -> List[str]: - """Format a list of strings to be lowercase and replace spaces with underscores. - - Parameters - ---------- - choices: List[str] - The options to be formatted - - Returns - ------- - clean_choices: List[str] - The cleaned options - - """ - return [x.lower().replace(" ", "_") for x in choices] - - -def map_parse_choices(choices: List[str]) -> Dict[str, str]: - """Create a mapping of clean arguments (keys) to original arguments (values). - - Parameters - ---------- - choices: List[str] - The options to be formatted - - Returns - ------- - clean_choices: Dict[str, str] - The mapping - - """ - the_dict = {x.lower().replace(" ", "_"): x for x in choices} - the_dict[""] = "" - return the_dict - - -def verify_plot_options(command: str, source: str, plot: list) -> bool: - """Verify that the plot options are valid for the chosen source.""" - if command == "cash": - command_options = CASH_PLOT - elif command == "balance": - command_options = BALANCE_PLOT - else: - command_options = INCOME_PLOT - options = list(command_options.get(source, {}).values()) - - incorrect_columns = [] - for column in plot: - if column not in options: - incorrect_columns.append(column) - if incorrect_columns: - console.print( - f"[red]The chosen columns to plot is not available for {source}.[/red]\n" - ) - for column in incorrect_columns: - possible_sources = [] - for i in command_options: - if column in list(command_options[i].values()): - possible_sources.append(i) - if possible_sources: - console.print( - f"[red]{column} can be plotted with the following sources: {', '.join(possible_sources)}[/red]" - ) - else: - console.print( - f"[red]{column} does not exist in a existing data source.[/red]" - ) - return True - return False - - -def heikin_ashi(data: pd.DataFrame) -> pd.DataFrame: - """Return OHLC data as Heikin Ashi Candles. - - Parameters - ---------- - data: pd.DataFrame - DataFrame containing OHLC data. - - Returns - ------- - pd.DataFrame - Appended DataFrame with Heikin Ashi candle calculations. - """ - - check_columns = ["Open", "High", "Low", "Close"] - - data.rename( - columns={"open": "Open", "high": "High", "low": "Low", "close": "Close"}, - inplace=True, - ) - - for item in check_columns: - if item not in data.columns: - raise ValueError( - "The expected column labels, " - f"{check_columns}" - ", were not found in DataFrame." - ) - - ha = candles.ha( - data["Open"], - data["High"], - data["Low"], - data["Close"], - ) - ha.columns = [ - "HA Open", - "HA High", - "HA Low", - "HA Close", - ] - - return pd.concat([data, ha], axis=1) - - -def calculate_adjusted_prices(df: pd.DataFrame, column: str, dividends: bool = False): - """Calculates the split-adjusted prices, or split and dividend adjusted prices. - - Parameters - ------------ - df: pd.DataFrame - DataFrame with unadjusted OHLCV values + Split Factor + Dividend - column: str - The column name to adjust. - dividends: bool - Whether to adjust for both splits and dividends. Default is split-adjusted only. - - Returns - -------- - pd.DataFrame - DataFrame with adjusted prices. - """ - - df = df.copy() - adj_column = "Adj " + column - - # Reverse the DataFrame order, sorting by date in descending order - df.sort_index(ascending=False, inplace=True) - - price_col = df[column].values - split_col = df["Volume Factor"] if column == "Volume" else df["Split Factor"].values - dividend_col = df["Dividend"].values if dividends else np.zeros(len(price_col)) - adj_price_col = np.zeros(len(df.index)) - adj_price_col[0] = price_col[0] - - for i in range(1, len(price_col)): - adj_price_col[i] = adj_price_col[i - 1] + adj_price_col[i - 1] * ( - ((price_col[i] * split_col[i - 1]) - price_col[i - 1] - dividend_col[i - 1]) - / price_col[i - 1] - ) - df[adj_column] = adj_price_col - - # Change the DataFrame order back to dates ascending - df.sort_index(ascending=True, inplace=True) - return df diff --git a/openbb_terminal/stocks/stocks_model.py b/openbb_terminal/stocks/stocks_model.py deleted file mode 100644 index f84a84034e7..00000000000 --- a/openbb_terminal/stocks/stocks_model.py +++ /dev/null @@ -1,393 +0,0 @@ -import logging -import os -from datetime import datetime -from typing import List -from urllib.error import HTTPError - -import fundamentalanalysis as fa # Financial Modeling Prep -import intrinio_sdk as intrinio -import pandas as pd -import yfinance as yf -from alpha_vantage.timeseries import TimeSeries - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import lambda_long_number_format, request -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.fundamental_analysis.fa_helper import clean_df_index - -# pylint: disable=unsupported-assignment-operation,no-member - -logger = logging.getLogger(__name__) - - -def load_stock_intrinio( - symbol: str, - start_date: datetime, - end_date: datetime, - weekly: bool = False, - monthly: bool = False, -) -> pd.DataFrame: - intrinio.ApiClient().set_api_key(get_current_user().credentials.API_INTRINIO_KEY) - api = intrinio.SecurityApi() - frequency: str = "daily" - if weekly is True: - frequency = "weekly" - if monthly is True: - frequency = "monthly" - stock = api.get_security_stock_prices( - symbol.upper(), - start_date=start_date, - end_date=end_date, - frequency=frequency, - page_size=10000, - ) - data = stock - while stock.next_page: - stock = api.get_security_stock_prices( - symbol.upper(), - next_page=stock.next_page, - page_size=10000, - ) - data.stock_prices.extend(stock.stock_prices) - data = data.to_dict() - data = pd.DataFrame(data["stock_prices"]) - df = pd.DataFrame(data)[ - [ - "adj_open", - "adj_high", - "adj_low", - "close", - "adj_close", - "date", - "adj_volume", - "dividend", - "split_ratio", - "change", - "percent_change", - "fifty_two_week_high", - "fifty_two_week_low", - ] - ] - df["date"] = pd.DatetimeIndex(df["date"]) - df["close"] = df["adj_close"] - df = df.set_index("date").rename( - columns={ - "adj_close": "Adj Close", - "adj_open": "Open", - "close": "Close", - "adj_high": "High", - "adj_low": "Low", - "adj_volume": "Volume", - "dividend": "Dividend", - "split_ratio": "Split Ratio", - "change": "Change", - "percent_change": "Percent Change", - "fifty_two_week_high": "52 Week High", - "fifty_two_week_low": "52 Week Low", - } - )[::-1] - - return df - - -def load_stock_av( - symbol: str, - interval: str, - start_date: datetime, - end_date: datetime, - interval_min: str = "1min", -) -> pd.DataFrame: - try: - ts = TimeSeries( - key=get_current_user().credentials.API_KEY_ALPHAVANTAGE, - output_format="pandas", - ) - if interval == "Minute": - df_stock_candidate: pd.DataFrame = ts.get_intraday( - symbol=symbol, interval=interval_min, outputsize="full" - )[0] - elif interval == "Daily": - df_stock_candidate = ts.get_daily_adjusted( - symbol=symbol, outputsize="full" - )[0] - elif interval == "Weekly": - df_stock_candidate = ts.get_weekly_adjusted(symbol=symbol)[0] - elif interval == "Monthly": - df_stock_candidate = ts.get_monthly_adjusted(symbol=symbol)[0] - else: - console.print("Invalid interval specified") - return pd.DataFrame() - except Exception as e: - console.print(e) - return pd.DataFrame() - df_stock_candidate.columns = [ - val.split(". ")[1].capitalize() for val in df_stock_candidate.columns - ] - - df_stock_candidate = df_stock_candidate.rename( - columns={"Adjusted close": "Adj Close"} - ) - - # Check that loading a stock was not successful - if df_stock_candidate.empty: - console.print("No data found.") - return pd.DataFrame() - - df_stock_candidate.sort_index(ascending=True, inplace=True) - - df_stock_candidate.index = df_stock_candidate.index.tz_localize(None) - - df_stock_candidate = df_stock_candidate[ - (df_stock_candidate.index >= start_date) - & (df_stock_candidate.index <= end_date) - ] - - return df_stock_candidate - - -def load_stock_yf( - symbol: str, start_date: datetime, end_date: datetime, weekly: bool, monthly: bool -) -> pd.DataFrame: - # TODO: Better handling of interval with week/month - int_ = "1d" - int_string = "Daily" - if weekly: - int_ = "1wk" - int_string = "Weekly" - if monthly: - int_ = "1mo" - int_string = "Monthly" - - # Win10 version of mktime cannot cope with dates before 1970 - if os.name == "nt" and start_date < datetime(1970, 1, 1): - start_date = datetime( - 1970, 1, 2 - ) # 1 day buffer in case of timezone adjustments - - # add 1 day to end_date to include the last day - end_date = end_date + pd.Timedelta(days=1) - - # Adding a dropna for weekly and monthly because these include weird NaN columns. - df_stock_candidate = yf.download( - symbol, - start=start_date, - end=end_date, - progress=False, - auto_adjust=True, - actions=True, - interval=int_, - ignore_tz=True, - ).dropna(axis=0) - - # Check that loading a stock was not successful - if df_stock_candidate.empty: - console.print("[red]No results found in yahoo finance reply.[/red]") - return pd.DataFrame() - df_stock_candidate_cols = [ - "Open", - "High", - "Low", - "Close", - "Adj Close", - "Volume", - "Dividends", - "Stock Splits", - ] - df_stock_candidate.index.name = "date", int_string - df_stock_candidate["Adj Close"] = df_stock_candidate["Close"].copy() - df_stock_candidate = pd.DataFrame( - data=df_stock_candidate, columns=df_stock_candidate_cols - ) - return df_stock_candidate - - -def load_stock_eodhd( - symbol: str, - start_date: datetime, - end_date: datetime, - weekly: bool, - monthly: bool, - intraday: bool = False, -) -> pd.DataFrame: - request_url = "https://eodhistoricaldata.com/api/eod/" - - int_ = "d" - if weekly: - int_ = "w" - elif monthly: - int_ = "m" - elif intraday: - int_ = "1m" - request_url = "https://eodhistoricaldata.com/api/intraday/" - - request_url = ( - f"{request_url}" - f"{symbol.upper()}?" - f"from={start_date.strftime('%Y-%m-%d')}&" - f"to={end_date.strftime('%Y-%m-%d')}&" - f"period={int_}&" - f"api_token={get_current_user().credentials.API_EODHD_KEY}&" - f"fmt=json&" - f"order=d" - ) - - r = request(request_url) - if r.status_code != 200: - console.print("[red]Invalid API Key for eodhistoricaldata [/red]") - console.print( - "Get your Key here: https://eodhistoricaldata.com/r/?ref=869U7F4J" - ) - return pd.DataFrame() - - r_json = r.json() - - df_stock_candidate = pd.DataFrame(r_json).dropna(axis=0) - - # Check that loading a stock was not successful - if df_stock_candidate.empty: - console.print("No data found from End Of Day Historical Data.") - return df_stock_candidate - - df_stock_candidate = df_stock_candidate[ - ["date", "open", "high", "low", "close", "adjusted_close", "volume"] - ] - - df_stock_candidate = df_stock_candidate.rename( - columns={ - "date": "Date", - "close": "Close", - "high": "High", - "low": "Low", - "open": "Open", - "adjusted_close": "Adj Close", - "volume": "Volume", - } - ) - df_stock_candidate["Date"] = pd.to_datetime(df_stock_candidate.Date) - df_stock_candidate.set_index("Date", inplace=True) - df_stock_candidate.sort_index(ascending=True, inplace=True) - return df_stock_candidate - - -@check_api_key(["API_POLYGON_KEY"]) -def load_stock_polygon( - symbol: str, start_date: datetime, end_date: datetime, weekly: bool, monthly: bool -) -> pd.DataFrame: - # Polygon allows: day, minute, hour, day, week, month, quarter, year - timespan = "day" - if weekly or monthly: - timespan = "week" if weekly else "month" - - request_url = ( - f"https://api.polygon.io/v2/aggs/ticker/" - f"{symbol.upper()}/range/1/{timespan}/" - f"{start_date.strftime('%Y-%m-%d')}/{end_date.strftime('%Y-%m-%d')}?adjusted=true" - f"&sort=desc&limit=49999&apiKey={get_current_user().credentials.API_POLYGON_KEY}" - ) - r = request(request_url) - if r.status_code != 200: - console.print("[red]Error in polygon request[/red]") - return pd.DataFrame() - - r_json = r.json() - if "results" not in r_json: - console.print("[red]No results found in polygon reply.[/red]") - return pd.DataFrame() - - df_stock_candidate = pd.DataFrame(r_json["results"]) - - df_stock_candidate = df_stock_candidate.rename( - columns={ - "o": "Open", - "c": "Adj Close", - "h": "High", - "l": "Low", - "t": "date", - "v": "Volume", - "n": "Transactions", - } - ) - df_stock_candidate["date"] = pd.to_datetime(df_stock_candidate.date, unit="ms") - # TODO: Clean up Close vs Adj Close throughout - df_stock_candidate["Close"] = df_stock_candidate["Adj Close"] - df_stock_candidate = df_stock_candidate.sort_values(by="date") - df_stock_candidate = df_stock_candidate.set_index("date") - df_stock_candidate.index = df_stock_candidate.index.normalize() - return df_stock_candidate - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def get_quote(symbols: List[str]) -> pd.DataFrame: - """Gets ticker quote from FMP - - Parameters - ---------- - symbols : List[str] - A list of Stock ticker symbols - - Returns - ------- - pd.DataFrame - Dataframe of ticker quote - - Examples - -------- - - A single ticker must be entered as a list. - - >>> df = openbb.stocks.quote(["AAPL"]) - - Multiple tickers can be retrieved. - - >>> df = openbb.stocks.quote(["AAPL","MSFT","GOOG","NFLX","META","AMZN","NVDA"]) - """ - - symbol = symbols if isinstance(symbols, list) is False else ",".join(symbols) - - df_fa = pd.DataFrame() - - try: - df_fa = fa.quote( - symbol, get_current_user().credentials.API_KEY_FINANCIALMODELINGPREP - ).rename({"yearLow": "52 Week Low", "yearHigh": "52 Week High"}) - # Invalid API Keys - except ValueError: - console.print("[red]Invalid API Key[/red]\n") - # Premium feature, API plan is not authorized - except HTTPError: - console.print("[red]API Key not authorized for Premium feature[/red]\n") - - if not df_fa.empty: - clean_df_index(df_fa) - for c in df_fa.columns: - if not get_current_user().preferences.USE_INTERACTIVE_DF: - df_fa.loc["Market cap"][c] = lambda_long_number_format( - df_fa.loc["Market cap"][c] - ) - df_fa.loc["Shares outstanding"][c] = lambda_long_number_format( - df_fa.loc["Shares outstanding"][c] - ) - df_fa.loc["Volume"][c] = lambda_long_number_format( - df_fa.loc["Volume"][c] - ) - # Check if there is a valid earnings announcement - if df_fa.loc["Earnings announcement"][c]: - earning_announcement = datetime.strptime( - df_fa.loc["Earnings announcement"][c][0:19], "%Y-%m-%dT%H:%M:%S" - ) - df_fa.loc["Earnings announcement"][ - c - ] = f"{earning_announcement.date()} {earning_announcement.time()}" - # Check if there is a valid timestamp and convert it to a readable format - if "Timestamp" in df_fa.index and df_fa.loc["Timestamp"][c]: - df_fa.loc["Timestamp"][c] = datetime.fromtimestamp( - df_fa.loc["Timestamp"][c] - ).strftime("%Y-%m-%d %H:%M:%S") - - df_fa.columns = df_fa.loc["Symbol"][:] - df_fa = df_fa.drop("Symbol", axis=0) - df_fa.index = df_fa.index.str.title() - - return df_fa diff --git a/openbb_terminal/stocks/stocks_view.py b/openbb_terminal/stocks/stocks_view.py deleted file mode 100644 index cd54ada2b4f..00000000000 --- a/openbb_terminal/stocks/stocks_view.py +++ /dev/null @@ -1,60 +0,0 @@ -import logging -import os -from typing import List, Optional - -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks import stocks_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_KEY_FINANCIALMODELINGPREP"]) -def display_quote( - symbols: List[str], export: str = "", sheet_name: Optional[str] = None -): - """Financial Modeling Prep ticker(s) quote. - - Parameters - ---------- - symbols : List[str] - A list of ticker symbols. - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - - Examples - -------- - This end point displays the results as an interactive table. - - >>> from openbb_terminal.sdk import openbb - >>> openbb.stocks.quote_chart(["MSFT"]) - - Multiple tickers are retrieved at once using a comma-separated list. - - >>> openbb.stocks.quote_chart(["MSFT","AAPL"]) - """ - - quote = stocks_model.get_quote(symbols) - if quote.empty: - console.print("[red]Data not found[/red]\n") - return - print_rich_table( - quote, - headers=quote.columns, - title="Quote", - index_name="Info", - show_index=True, - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "quote", - quote, - sheet_name, - ) diff --git a/openbb_terminal/stocks/technical_analysis/__init__.py b/openbb_terminal/stocks/technical_analysis/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/technical_analysis/finbrain_model.py b/openbb_terminal/stocks/technical_analysis/finbrain_model.py deleted file mode 100644 index 3ea08258518..00000000000 --- a/openbb_terminal/stocks/technical_analysis/finbrain_model.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Finbrain model""" - -__docformat__ = "numpy" - -import logging - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_technical_summary_report(symbol: str) -> str: - """Get technical summary report provided by FinBrain's API - - Parameters - ---------- - symbol : str - Ticker symbol to get the technical summary - - Returns - ------- - report: str - technical summary report - """ - result = request(f"https://api.finbrain.tech/v0/technicalSummary/{symbol}") - report = "" - if result.status_code == 200: - if "technicalSummary" in result.json(): - report = result.json()["technicalSummary"] - else: - console.print("Unexpected data format from FinBrain API") - else: - console.print("Request error in retrieving sentiment from FinBrain API") - - return report diff --git a/openbb_terminal/stocks/technical_analysis/finbrain_view.py b/openbb_terminal/stocks/technical_analysis/finbrain_view.py deleted file mode 100644 index 98540eb8027..00000000000 --- a/openbb_terminal/stocks/technical_analysis/finbrain_view.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Finbrain view""" - -__docformat__ = "numpy" - -import logging - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.technical_analysis import finbrain_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def technical_summary_report(symbol: str): - """Print technical summary report provided by FinBrain's API - - Parameters - ---------- - symbol: str - Ticker symbol to get the technical summary - """ - - report = finbrain_model.get_technical_summary_report(symbol) - if report: - console.print(report.replace(". ", ".\n")) diff --git a/openbb_terminal/stocks/technical_analysis/ta_controller.py b/openbb_terminal/stocks/technical_analysis/ta_controller.py deleted file mode 100644 index cae1180849b..00000000000 --- a/openbb_terminal/stocks/technical_analysis/ta_controller.py +++ /dev/null @@ -1,1798 +0,0 @@ -"""Technical Analysis Controller Module""" - -__docformat__ = "numpy" -# pylint:disable=too-many-lines,R0904,C0201,C0302 - -import argparse -import json -import logging -from datetime import datetime -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.common.technical_analysis import ( - custom_indicators_view, - momentum_view, - overlap_model, - overlap_view, - trend_indicators_view, - volatility_model, - volatility_view, - volume_view, -) -from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_FIGURES_ALLOWED, - EXPORT_ONLY_RAW_DATA_ALLOWED, - check_indicator_parameters, - check_positive, - check_positive_float, - check_positive_list, - valid_date, -) -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import StockBaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks.technical_analysis import ( - finbrain_view, - tradingview_model, - tradingview_view, -) - -logger = logging.getLogger(__name__) - - -def no_ticker_message(): - """Print message when no ticker is loaded""" - console.print("[red]No data loaded. Use 'load' command to load a symbol[/red]") - - -class TechnicalAnalysisController(StockBaseController): - """Technical Analysis Controller class""" - - CHOICES_COMMANDS = [ - "load", - "summary", - "recom", - "ema", - "sma", - "wma", - "hma", - "vwap", - "zlma", - "cci", - "macd", - "ichimoku", - "rsi", - "stoch", - "fisher", - "cg", - "adx", - "aroon", - "bbands", - "donchian", - "kc", - "ad", - "adosc", - "obv", - "fib", - "clenow", - "demark", - "atr", - "multi", - "cones", - ] - PATH = "/stocks/ta/" - CHOICES_GENERATION = True - - def __init__( - self, - ticker: str, - start: datetime, - interval: str, - stock: pd.DataFrame, - queue: Optional[List[str]] = None, - ): - """Constructor""" - super().__init__(queue) - - self.ticker = ticker - self.start = start - self.interval = interval - self.stock = stock - ta_cls = PlotlyTA() - indicators: dict = { - c.name.replace("plot_", ""): {} for c in ta_cls if c.name != "plot_ma" - } - indicators.update({c: {} for c in ta_cls.ma_mode}) - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - choices["multi"]["--indicators"] = dict(sorted(indicators.items())) - choices["multi"]["-i"] = "--indicators" - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - s_intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"] - if self.start: - stock_str = ( - f"{s_intraday} {self.ticker} (from {self.start.strftime('%Y-%m-%d')})" - ) - else: - stock_str = f"{s_intraday} {self.ticker}" - - mt = MenuText("stocks/ta/", 90) - mt.add_cmd("load") - mt.add_raw("\n") - mt.add_param( - "_ticker", stock_str if not self.stock.empty else "No ticker loaded" - ) - mt.add_raw("\n") - mt.add_cmd("recom", not self.stock.empty) - mt.add_cmd("summary", not self.stock.empty) - mt.add_raw("\n") - mt.add_info("_overlap_") - mt.add_cmd("ema", not self.stock.empty) - mt.add_cmd("hma", not self.stock.empty) - mt.add_cmd("sma", not self.stock.empty) - mt.add_cmd("wma", not self.stock.empty) - mt.add_cmd("vwap", not self.stock.empty) - mt.add_cmd("zlma", not self.stock.empty) - mt.add_info("_momentum_") - mt.add_cmd("cci", not self.stock.empty) - mt.add_cmd("cg", not self.stock.empty) - mt.add_cmd("clenow", not self.stock.empty) - mt.add_cmd("demark", not self.stock.empty) - mt.add_cmd("macd", not self.stock.empty) - mt.add_cmd("fisher", not self.stock.empty) - mt.add_cmd("ichimoku", not self.stock.empty) - mt.add_cmd("rsi", not self.stock.empty) - mt.add_cmd("stoch", not self.stock.empty) - mt.add_info("_trend_") - mt.add_cmd("adx", not self.stock.empty) - mt.add_cmd("aroon", not self.stock.empty) - mt.add_info("_volatility_") - mt.add_cmd("atr", not self.stock.empty) - mt.add_cmd("bbands", not self.stock.empty) - mt.add_cmd("cones", not self.stock.empty) - mt.add_cmd("donchian", not self.stock.empty) - mt.add_cmd("kc", not self.stock.empty) - mt.add_info("_volume_") - mt.add_cmd("ad", not self.stock.empty) - mt.add_cmd("adosc", not self.stock.empty) - mt.add_cmd("obv", not self.stock.empty) - mt.add_info("_custom_") - mt.add_cmd("fib", not self.stock.empty) - mt.add_cmd("multi", not self.stock.empty) - console.print(text=mt.menu_text, menu="Stocks - Technical Analysis") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - return ["stocks", f"load {self.ticker}", "ta"] - return [] - - @log_start_end(log=logger) - def call_summary(self, other_args: List[str]): - """Process summary command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="summary", - description=""" - Technical summary report provided by FinBrain's API. - FinBrain Technologies develops deep learning algorithms for financial analysis - and prediction, which currently serves traders from more than 150 countries - all around the world. [Source: Finbrain] - """, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - finbrain_view.technical_summary_report(self.ticker) - - @log_start_end(log=logger) - def call_recom(self, other_args: List[str]): - """Process recom command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="recom", - description=""" - Print tradingview recommendation based on technical indicators. - [Source: Tradingview] - """, - ) - parser.add_argument( - "-s", - "--screener", - action="store", - dest="screener", - type=str, - default="america", - choices=tradingview_model.SCREENERS, - help="Screener. See https://python-tradingview-ta.readthedocs.io/en/latest/usage.html", - ) - parser.add_argument( - "-e", - "--exchange", - action="store", - dest="exchange", - type=str, - default="", - help="""Set exchange. For Forex use: 'FX_IDC', and for crypto use 'TVC'. - See https://python-tradingview-ta.readthedocs.io/en/latest/usage.html. - By default Alpha Vantage tries to get this data from the ticker. """, - ) - parser.add_argument( - "-i", - "--interval", - action="store", - dest="interval", - type=str, - default="", - choices=tradingview_model.INTERVALS.keys(), - help="""Interval, that corresponds to the recommendation given by tradingview based on technical indicators. - See https://python-tradingview-ta.readthedocs.io/en/latest/usage.html""", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - tradingview_view.print_recommendation( - symbol=self.ticker, - screener=ns_parser.screener, - exchange=ns_parser.exchange, - interval=ns_parser.interval, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - # COMMON - # TODO: Go through all models and make sure all needed columns are in dfs - - @log_start_end(log=logger) - def call_ema(self, other_args: List[str]): - """Process ema command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ema", - description=""" - The Exponential Moving Average is a staple of technical - analysis and is used in countless technical indicators. In a Simple Moving - Average, each value in the time period carries equal weight, and values outside - of the time period are not included in the average. However, the Exponential - Moving Average is a cumulative calculation, including all data. Past values have - a diminishing contribution to the average, while more recent values have a greater - contribution. This method allows the moving average to be more responsive to changes - in the data. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=overlap_model.WINDOW_LENGTHS, - help="Window lengths. Multiple values indicated as comma separated values.", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - overlap_view.view_ma( - ma_type="EMA", - symbol=self.ticker, - data=self.stock["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_sma(self, other_args: List[str]): - """Process sma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sma", - description=""" - Moving Averages are used to smooth the data in an array to - help eliminate noise and identify trends. The Simple Moving Average is literally - the simplest form of a moving average. Each output value is the average of the - previous n values. In a Simple Moving Average, each value in the time period carries - equal weight, and values outside of the time period are not included in the average. - This makes it less responsive to recent changes in the data, which can be useful for - filtering out those changes. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=overlap_model.WINDOW_LENGTHS, - help="Window lengths. Multiple values indicated as comma separated values. ", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - overlap_view.view_ma( - ma_type="SMA", - symbol=self.ticker, - data=self.stock["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_wma(self, other_args: List[str]): - """Process wma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="wma", - description=""" - A Weighted Moving Average puts more weight on recent data and less on past data. - This is done by multiplying each bar’s price by a weighting factor. Because of its - unique calculation, WMA will follow prices more closely than a corresponding Simple - Moving Average. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=overlap_model.WINDOW_LENGTHS, - help="Window lengths. Multiple values indicated as comma separated values. ", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - overlap_view.view_ma( - ma_type="WMA", - symbol=self.ticker, - data=self.stock["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_hma(self, other_args: List[str]): - """Process hma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hma", - description=""" - The Hull Moving Average solves the age old dilemma of making a moving average - more responsive to current price activity whilst maintaining curve smoothness. - In fact the HMA almost eliminates lag altogether and manages to improve smoothing - at the same time. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=overlap_model.WINDOW_LENGTHS2, - help="Window lengths. Multiple values indicated as comma separated values. ", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - overlap_view.view_ma( - ma_type="HMA", - symbol=self.ticker, - data=self.stock["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_zlma(self, other_args: List[str]): - """Process zlma command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="zlma", - description=""" - The zero lag exponential moving average (ZLEMA) indicator - was created by John Ehlers and Ric Way. The idea is do a - regular exponential moving average (EMA) calculation but - on a de-lagged data instead of doing it on the regular data. - Data is de-lagged by removing the data from "lag" days ago - thus removing (or attempting to) the cumulative effect of - the moving average. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive_list, - default=[20], - help="Window lengths. Multiple values indicated as comma separated values.", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - overlap_view.view_ma( - ma_type="ZLMA", - symbol=self.ticker, - data=self.stock["Adj Close"], - window=ns_parser.n_length, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_vwap(self, other_args: List[str]): - """Process vwap command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="vwap", - description=""" - The Volume Weighted Average Price that measures the average typical price - by volume. It is typically used with intraday charts to identify general direction. - """, - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - parser.add_argument( - "--start", - dest="start", - type=valid_date, - help="Starting date to select", - required="--end" in other_args, - ) - parser.add_argument( - "--end", - dest="end", - type=valid_date, - help="Ending date to select", - required="--start" in other_args, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - # Daily - if self.interval == "1440min": - if ns_parser.start: - interval_text = "Daily" - else: - console.print( - "If no date conditions, VWAP should be used with intraday data. \n" - ) - return - else: - interval_text = self.interval - - overlap_view.view_vwap( - data=self.stock, - symbol=self.ticker, - start_date=ns_parser.start, - end_date=ns_parser.end, - offset=ns_parser.n_offset, - interval=interval_text, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cci(self, other_args: List[str]): - """Process cci command""" - - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cci", - description=""" - The CCI is designed to detect beginning and ending market trends. - The range of 100 to -100 is the normal trading range. CCI values outside of this - range indicate overbought or oversold conditions. You can also look for price - divergence in the CCI. If the price is making new highs, and the CCI is not, - then a price correction is likely. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=0.015, - help="scalar", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - momentum_view.display_cci( - symbol=self.ticker, - data=self.stock, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_macd(self, other_args: List[str]): - """Process macd command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="macd", - description=""" - The Moving Average Convergence Divergence (MACD) is the difference - between two Exponential Moving Averages. The Signal line is an Exponential Moving - Average of the MACD. \n \n The MACD signals trend changes and indicates the start - of new trend direction. High values indicate overbought conditions, low values - indicate oversold conditions. Divergence with the price indicates an end to the - current trend, especially if the MACD is at extreme high or low values. When the MACD - line crosses above the signal line a buy signal is generated. When the MACD crosses - below the signal line a sell signal is generated. To confirm the signal, the MACD - should be above zero for a buy, and below zero for a sell. - """, - ) - parser.add_argument( - "--fast", - action="store", - dest="n_fast", - type=check_positive, - default=12, - help="The short period.", - ) - parser.add_argument( - "--slow", - action="store", - dest="n_slow", - type=check_positive, - default=26, - help="The long period.", - ) - parser.add_argument( - "--signal", - action="store", - dest="n_signal", - type=check_positive, - default=9, - help="The signal period.", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - momentum_view.display_macd( - symbol=self.ticker, - data=self.stock["Adj Close"], - n_fast=ns_parser.n_fast, - n_slow=ns_parser.n_slow, - n_signal=ns_parser.n_signal, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rsi(self, other_args: List[str]): - """Process rsi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rsi", - description=""" - The Relative Strength Index (RSI) calculates a ratio of the - recent upward price movements to the absolute price movement. The RSI ranges - from 0 to 100. The RSI is interpreted as an overbought/oversold indicator when - the value is over 70/below 30. You can also look for divergence with price. If - the price is making new highs/lows, and the RSI is not, it indicates a reversal. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - parser.add_argument( - "-d", - "--drift", - action="store", - dest="n_drift", - type=check_positive, - default=1, - help="drift", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - momentum_view.display_rsi( - symbol=self.ticker, - data=self.stock["Adj Close"], - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - drift=ns_parser.n_drift, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_stoch(self, other_args: List[str]): - """Process stoch command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="stoch", - description=""" - The Stochastic Oscillator measures where the close is in relation - to the recent trading range. The values range from zero to 100. %D values over 75 - indicate an overbought condition; values under 25 indicate an oversold condition. - When the Fast %D crosses above the Slow %D, it is a buy signal; when it crosses - below, it is a sell signal. The Raw %K is generally considered too erratic to use - for crossover signals. - """, - ) - parser.add_argument( - "-k", - "--fastkperiod", - action="store", - dest="n_fastkperiod", - type=check_positive, - default=14, - help="The time period of the fastk moving average", - ) - parser.add_argument( - "-d", - "--slowdperiod", - action="store", - dest="n_slowdperiod", - type=check_positive, - default=3, - help="The time period of the slowd moving average", - ) - parser.add_argument( - "--slowkperiod", - action="store", - dest="n_slowkperiod", - type=check_positive, - default=3, - help="The time period of the slowk moving average", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - momentum_view.display_stoch( - symbol=self.ticker, - data=self.stock, - fastkperiod=ns_parser.n_fastkperiod, - slowdperiod=ns_parser.n_slowdperiod, - slowkperiod=ns_parser.n_slowkperiod, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fisher(self, other_args: List[str]): - """Process fisher command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fisher", - description=""" - The Fisher Transform is a technical indicator created by John F. Ehlers - that converts prices into a Gaussian normal distribution.1 The indicator - highlights when prices have moved to an extreme, based on recent prices. - This may help in spotting turning points in the price of an asset. It also - helps show the trend and isolate the price waves within a trend. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - momentum_view.display_fisher( - symbol=self.ticker, - data=self.stock, - window=ns_parser.n_length, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cg(self, other_args: List[str]): - """Process cg command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cg", - description=""" - The Center of Gravity indicator, in short, is used to anticipate future price movements - and to trade on price reversals as soon as they happen. However, just like other oscillators, - the COG indicator returns the best results in range-bound markets and should be avoided when - the price is trending. Traders who use it will be able to closely speculate the upcoming - price change of the asset. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - momentum_view.display_cg( - symbol=self.ticker, - data=self.stock["Adj Close"], - window=ns_parser.n_length, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_adx(self, other_args: List[str]): - """Process adx command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="adx", - description=""" - The ADX is a Welles Wilder style moving average of the Directional Movement Index (DX). - The values range from 0 to 100, but rarely get above 60. To interpret the ADX, consider - a high number to be a strong trend, and a low number, a weak trend. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="length", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - parser.add_argument( - "-d", - "--drift", - action="store", - dest="n_drift", - type=check_positive, - default=1, - help="drift", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - trend_indicators_view.display_adx( - symbol=self.ticker, - data=self.stock, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - drift=ns_parser.n_drift, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_aroon(self, other_args: List[str]): - """Process aroon command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="aroon", - description=""" - The word aroon is Sanskrit for "dawn's early light." The Aroon - indicator attempts to show when a new trend is dawning. The indicator consists - of two lines (Up and Down) that measure how long it has been since the highest - high/lowest low has occurred within an n period range. \n \n When the Aroon Up is - staying between 70 and 100 then it indicates an upward trend. When the Aroon Down - is staying between 70 and 100 then it indicates an downward trend. A strong upward - trend is indicated when the Aroon Up is above 70 while the Aroon Down is below 30. - Likewise, a strong downward trend is indicated when the Aroon Down is above 70 while - the Aroon Up is below 30. Also look for crossovers. When the Aroon Down crosses above - the Aroon Up, it indicates a weakening of the upward trend (and vice versa). - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=25, - help="length", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=100, - help="scalar", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - trend_indicators_view.display_aroon( - symbol=self.ticker, - data=self.stock, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_bbands(self, other_args: List[str]): - """Process bbands command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="bbands", - description=""" - Bollinger Bands consist of three lines. The middle band is a simple - moving average (generally 20 periods) of the typical price (TP). The upper and lower - bands are F standard deviations (generally 2) above and below the middle band. - The bands widen and narrow when the volatility of the price is higher or lower, - respectively. \n \nBollinger Bands do not, in themselves, generate buy or sell signals; - they are an indicator of overbought or oversold conditions. When the price is near the - upper or lower band it indicates that a reversal may be imminent. The middle band - becomes a support or resistance level. The upper and lower bands can also be - interpreted as price targets. When the price bounces off of the lower band and crosses - the middle band, then the upper band becomes the price target. - """, - ) - - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=15, - help="length", - ) - parser.add_argument( - "-s", - "--std", - action="store", - dest="n_std", - type=check_positive_float, - default=2, - help="std", - ) - parser.add_argument( - "-m", - "--mamode", - action="store", - dest="s_mamode", - choices=volatility_model.MAMODES, - type=str.lower, - default="sma", - help="mamode", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - volatility_view.display_bbands( - symbol=self.ticker, - data=self.stock, - window=ns_parser.n_length, - n_std=ns_parser.n_std, - mamode=ns_parser.s_mamode, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_donchian(self, other_args: List[str]): - """Process donchian command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="donchian", - description=""" - Donchian Channels are three lines generated by moving average - calculations that comprise an indicator formed by upper and lower - bands around a midrange or median band. The upper band marks the - highest price of a security over N periods while the lower band - marks the lowest price of a security over N periods. The area - between the upper and lower bands represents the Donchian Channel. - """, - ) - parser.add_argument( - "-u", - "--length_upper", - action="store", - dest="n_length_upper", - type=check_positive, - default=20, - help="length", - ) - parser.add_argument( - "-l", - "--length_lower", - action="store", - dest="n_length_lower", - type=check_positive, - default=20, - help="length", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - volatility_view.display_donchian( - symbol=self.ticker, - data=self.stock, - upper_length=ns_parser.n_length_upper, - lower_length=ns_parser.n_length_lower, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_kc(self, other_args: List[str]): - """Process kc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="kc", - description=""" - Keltner Channels are volatility-based bands that are placed - on either side of an asset's price and can aid in determining - the direction of a trend.The Keltner channel uses the average - true range (ATR) or volatility, with breaks above or below the top - and bottom barriers signaling a continuation. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=20, - help="Window length", - ) - parser.add_argument( - "-s", - "--scalar", - action="store", - dest="n_scalar", - type=check_positive, - default=2, - help="scalar", - ) - parser.add_argument( - "-m", - "--mamode", - action="store", - dest="s_mamode", - default="ema", - choices=volatility_model.MAMODES, - type=str.lower, - help="mamode", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - volatility_view.view_kc( - symbol=self.ticker, - data=self.stock, - window=ns_parser.n_length, - scalar=ns_parser.n_scalar, - mamode=ns_parser.s_mamode, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ad(self, other_args: List[str]): - """Process ad command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ad", - description=""" - The Accumulation/Distribution Line is similar to the On Balance - Volume (OBV), which sums the volume times +1/-1 based on whether the close is - higher than the previous close. The Accumulation/Distribution indicator, however - multiplies the volume by the close location value (CLV). The CLV is based on the - movement of the issue within a single bar and can be +1, -1 or zero. \n \n - The Accumulation/Distribution Line is interpreted by looking for a divergence in - the direction of the indicator relative to price. If the Accumulation/Distribution - Line is trending upward it indicates that the price may follow. Also, if the - Accumulation/Distribution Line becomes flat while the price is still rising (or falling) - then it signals an impending flattening of the price. - """, - ) - parser.add_argument( - "--open", - action="store_true", - default=False, - dest="b_use_open", - help="uses open value of stock", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - volume_view.display_ad( - symbol=self.ticker, - data=self.stock, - use_open=ns_parser.b_use_open, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_adosc(self, other_args: List[str]): - """Process adosc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="adosc", - description=""" - Accumulation/Distribution Oscillator, also known as the Chaikin Oscillator - is essentially a momentum indicator, but of the Accumulation-Distribution line - rather than merely price. It looks at both the strength of price moves and the - underlying buying and selling pressure during a given time period. The oscillator - reading above zero indicates net buying pressure, while one below zero registers - net selling pressure. Divergence between the indicator and pure price moves are - the most common signals from the indicator, and often flag market turning points. - """, - ) - parser.add_argument( - "--open", - action="store_true", - default=False, - dest="b_use_open", - help="uses open value of stock", - ) - parser.add_argument( - "--fast", - action="store", - dest="n_length_fast", - type=check_positive, - default=3, - help="fast length", - ) - parser.add_argument( - "--slow", - action="store", - dest="n_length_slow", - type=check_positive, - default=10, - help="slow length", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - volume_view.display_adosc( - symbol=self.ticker, - data=self.stock, - use_open=ns_parser.b_use_open, - fast=ns_parser.n_length_fast, - slow=ns_parser.n_length_slow, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_obv(self, other_args: List[str]): - """Process obv command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="obv", - description=""" - The On Balance Volume (OBV) is a cumulative total of the up and - down volume. When the close is higher than the previous close, the volume is added - to the running total, and when the close is lower than the previous close, the volume - is subtracted from the running total. \n \n To interpret the OBV, look for the OBV - to move with the price or precede price moves. If the price moves before the OBV, - then it is a non-confirmed move. A series of rising peaks, or falling troughs, in the - OBV indicates a strong trend. If the OBV is flat, then the market is not trending. - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - volume_view.display_obv( - symbol=self.ticker, - data=self.stock, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fib(self, other_args: List[str]): - """Process fib command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fib", - description="Calculates the fibonacci retracement levels", - ) - parser.add_argument( - "-p", - "--period", - dest="period", - type=int, - help="Days to look back for retracement", - default=120, - ) - parser.add_argument( - "--start", - dest="start", - type=valid_date, - help="Starting date to select", - required="--end" in other_args, - ) - parser.add_argument( - "--end", - dest="end", - type=valid_date, - help="Ending date to select", - required="--start" in other_args, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - custom_indicators_view.fibonacci_retracement( - symbol=self.ticker, - data=self.stock, - limit=ns_parser.period, - start_date=ns_parser.start, - end_date=ns_parser.end, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_clenow(self, other_args: List[str]): - """Process clenow command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="clenow", - description="Calculates the Clenow Volatility Adjusted Momentum.", - ) - parser.add_argument( - "-p", - "--period", - dest="period", - help="Lookback period for regression", - default=90, - type=check_positive, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_FIGURES_ALLOWED - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - if self.interval == "1440min": - momentum_view.display_clenow_momentum( - self.stock["Adj Close"], - self.ticker.upper(), - ns_parser.period, - ns_parser.export, - ) - else: - console.print( - "[red]This regression should be performed with daily data and at least 90 days.[/red]" - ) - - @log_start_end(log=logger) - def call_demark(self, other_args: List[str]): - """Process demark command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="demark", - description="Calculates the Demark sequential indicator.", - ) - parser.add_argument( - "-m", - "--min", - help="Minimum value of indicator to show (declutters plot).", - dest="min_to_show", - type=check_positive, - default=5, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - momentum_view.display_demark( - self.stock, - self.ticker.upper(), - min_to_show=ns_parser.min_to_show, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_atr(self, other_args: List[str]): - """Process atr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="atr", - description=""" - Averge True Range is used to measure volatility, especially volatility caused by - gaps or limit moves. - """, - ) - parser.add_argument( - "-l", - "--length", - action="store", - dest="n_length", - type=check_positive, - default=14, - help="Window length", - ) - parser.add_argument( - "-m", - "--mamode", - action="store", - dest="s_mamode", - default="ema", - choices=volatility_model.MAMODES, - help="mamode", - ) - parser.add_argument( - "-o", - "--offset", - action="store", - dest="n_offset", - type=int, - default=0, - help="offset", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - if not self.ticker: - no_ticker_message() - return - volatility_view.display_atr( - data=self.stock, - symbol=self.ticker, - window=ns_parser.n_length, - mamode=ns_parser.s_mamode, - offset=ns_parser.n_offset, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_multi(self, other_args: List[str]): - """Process multi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="multi", - description=""" - Plot multiple indicators on the same chart separated by a comma. - """, - ) - parser.add_argument( - "-i", - "--indicators", - dest="indicators", - type=check_indicator_parameters, - help='Indicators with optional arguments in the form of "macd[12,26,9],rsi,sma[20]"', - required="-h" not in other_args, - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-i") - - if all(x in other_args for x in ["-h", "-i"]): - check_indicator_parameters(other_args[other_args.index("-i") + 1], True) - console.print() - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - - parameters = json.loads(ns_parser.indicators) - ns_parser.indicators = list(parameters.keys()) - - custom_indicators_view.plot_multiple_indicators( - self.stock, - ns_parser.indicators, - self.ticker, - parameters, - ns_parser.export, - ) - - @log_start_end(log=logger) - def call_cones(self, other_args: List[str]): - """Process cones command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cones", - description=""" - Calculates the realized volatility quantiles over rolling windows of time. - The model for calculating volatility is selectable. - """, - ) - parser.add_argument( - "-l", - "--lower_q", - action="store", - dest="lower_q", - type=float, - default=0.25, - help="The lower quantile value for calculations.", - ) - parser.add_argument( - "-u", - "--upper_q", - action="store", - dest="upper_q", - type=float, - default=0.75, - help="The upper quantile value for calculations.", - ) - parser.add_argument( - "-m", - "--model", - action="store", - dest="model", - default="STD", - choices=volatility_model.VOLATILITY_MODELS, - type=str, - help="The model used to calculate realized volatility.", - ) - parser.add_argument( - "--is_crypto", - dest="is_crypto", - action="store_true", - default=False, - help="If True, volatility is calculated for 365 days instead of 252.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - if ns_parser: - if not self.ticker: - no_ticker_message() - return - - volatility_view.display_cones( - data=self.stock, - symbol=self.ticker, - lower_q=ns_parser.lower_q, - upper_q=ns_parser.upper_q, - model=ns_parser.model, - is_crypto=ns_parser.is_crypto, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_ichimoku(self, other_args: List[str]): - """Process ichimoku command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ichimoku", - description=""" - The Ichimoku Cloud, also known as Ichimoku Kinko Hyo, is a versatile indicator that - defines support and resistance, identifies trend direction, gauges momentum and provides - trading signals. Ichimoku Kinko Hyo translates into "one look equilibrium chart". With - one look, chartists can identify the trend and look for potential signals within that trend. - """, - ) - - parser.add_argument( - "-c", - "--conversion", - action="store", - dest="n_conversion", - type=check_positive, - default=9, - help="conversion line period", - ) - parser.add_argument( - "-b", - "--base", - action="store", - dest="n_base", - type=check_positive, - default=26, - help="base line period", - ) - parser.add_argument( - "-l", - "--lagging", - action="store", - dest="n_lagging", - type=check_positive, - default=52, - help="lagging span period", - ) - parser.add_argument( - "-f", - "--forward", - action="store", - dest="n_forward", - type=check_positive, - default=26, - help="forward span period", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - if not self.ticker: - no_ticker_message() - return - - momentum_view.display_ichimoku( - data=self.stock, - symbol=self.ticker, - conversion_period=ns_parser.n_conversion, - base_period=ns_parser.n_base, - lagging_line_period=ns_parser.n_lagging, - displacement=ns_parser.n_forward, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/stocks/technical_analysis/tradingview_model.py b/openbb_terminal/stocks/technical_analysis/tradingview_model.py deleted file mode 100644 index 75266a4a45a..00000000000 --- a/openbb_terminal/stocks/technical_analysis/tradingview_model.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Tradingview model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd -from tradingview_ta import TA_Handler - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import request - -logger = logging.getLogger(__name__) - -INTERVALS = { - "1m": "1 min", - "5m": "5 min", - "15m": "15 min", - "1h": "1 hour", - "4h": "4 hours", - "1d": "1 day", - "1W": "1 week", - "1M": "1 month", -} - -SCREENERS = [ - "australia", - "brazil", - "cfd", - "crypto", - "euronext", - "forex", - "france", - "germany", - "hongkong", - "india", - "indonesia", - "malaysia", - "philippines", - "russia", - "ksa", - "rsa", - "korea", - "spain", - "sweden", - "taiwan", - "thailand", - "turkey", - "uk", - "america", - "vietnam", -] - - -@log_start_end(log=logger) -def get_tradingview_recommendation( - symbol: str, screener: str = "america", exchange: str = "", interval: str = "" -) -> pd.DataFrame: - """Get tradingview recommendation based on technical indicators - - Parameters - ---------- - symbol : str - Ticker symbol to get the recommendation from tradingview based on technical indicators - screener : str - Screener based on tradingview docs https://python-tradingview-ta.readthedocs.io/en/latest/usage.html - exchange: str - Exchange based on tradingview docs https://python-tradingview-ta.readthedocs.io/en/latest/usage.html - interval: str - Interval time to check technical indicators and correspondent recommendation - - Returns - ------- - df_recommendation: pd.DataFrame - Dataframe of tradingview recommendations based on technical indicators - """ - - if not exchange: - current_user = get_current_user() - s_req = ( - f"https://www.alphavantage.co/query?function=OVERVIEW&symbol={symbol}&" - f"apikey={current_user.credentials.API_KEY_ALPHAVANTAGE}" - ) - result = request(s_req, stream=True) - data = result.json() - if not data: - return pd.DataFrame() - exchange = data["Exchange"] - - intervals = ( - [interval] if interval else ["1M", "1W", "1d", "4h", "1h", "15m", "5m", "1m"] - ) - - df_recommendation = pd.DataFrame() - index_recommendation = [] - for an_interval in intervals: - if exchange: - stock_recommendation = TA_Handler( - symbol=symbol, - screener=screener, - exchange=exchange, - interval=an_interval, - ) - d_recommendation = stock_recommendation.get_analysis().summary - df_recommendation = pd.concat( - [ - pd.DataFrame.from_dict(d_recommendation, orient="index").T, - df_recommendation, - ], - axis=0, - ) - index_recommendation.append(INTERVALS[an_interval]) - - df_recommendation.index = index_recommendation - df_recommendation[["BUY", "NEUTRAL", "SELL"]] = df_recommendation[ - ["BUY", "NEUTRAL", "SELL"] - ].astype(int) - - df_recommendation.index.name = "Interval" - - return df_recommendation diff --git a/openbb_terminal/stocks/technical_analysis/tradingview_view.py b/openbb_terminal/stocks/technical_analysis/tradingview_view.py deleted file mode 100644 index 930f494981e..00000000000 --- a/openbb_terminal/stocks/technical_analysis/tradingview_view.py +++ /dev/null @@ -1,71 +0,0 @@ -"""Tradingview view""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.technical_analysis import tradingview_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def print_recommendation( - symbol: str, - screener: str = "america", - exchange: str = "", - interval: str = "", - export: str = "", - sheet_name: Optional[str] = None, -): - """Print tradingview recommendation based on technical indicators - - Parameters - ---------- - symbol : str - Ticker symbol to get tradingview recommendation based on technical indicators - screener : str - Screener based on tradingview docs https://python-tradingview-ta.readthedocs.io/en/latest/usage.html - exchange: str - Exchange based on tradingview docs https://python-tradingview-ta.readthedocs.io/en/latest/usage.html - interval: str - Interval time to check technical indicators and correspondent recommendation - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format of export file - """ - - recom = tradingview_model.get_tradingview_recommendation( - symbol, screener, exchange, interval - ) - - if (isinstance(recom, pd.DataFrame) and recom.empty) or ( - not isinstance(recom, pd.DataFrame) and not recom - ): - console.print( - "[red]The API did not return recommendations. Please make sure to include the desired parameters.[/red]\n" - ) - return - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "recom", - recom, - sheet_name, - ) - print_rich_table( - recom, - headers=list(recom.columns), - title="Ticker Recommendation", - show_index=True, - export=bool(export), - ) diff --git a/openbb_terminal/stocks/tradinghours/README.md b/openbb_terminal/stocks/tradinghours/README.md deleted file mode 100644 index a4984eec282..00000000000 --- a/openbb_terminal/stocks/tradinghours/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Trading Hours - -initial code by **Neosb** Szymon Błaszczyński @museyoucoulduse [github.com/buahaha](https://github.com/buahaha) - -## Usage - -1. `tradinghours` -2. `symbol MSFT` - -or - -1. `tradinghours` - - `open` - - `closed` - - `all` - -___ - -## Reference data - -[Exchanges in Yahoo Finance](https://help.yahoo.com/kb/SLN2310.html) diff --git a/openbb_terminal/stocks/tradinghours/__init__.py b/openbb_terminal/stocks/tradinghours/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/tradinghours/bursa_model.py b/openbb_terminal/stocks/tradinghours/bursa_model.py deleted file mode 100644 index 9e172d17a14..00000000000 --- a/openbb_terminal/stocks/tradinghours/bursa_model.py +++ /dev/null @@ -1,204 +0,0 @@ -"""Stocks Trading Hours Model.""" - -import logging -import os -from datetime import datetime - -import pandas as pd -import pytz - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) -# pylint: disable=no-member - -# pylint: disable=no-member - - -@log_start_end(log=logger) -def get_bursa(symbol: str) -> pd.DataFrame: - """Get current exchange open hours. - - Parameters - ---------- - symbol : str - Exchange symbol - - Returns - ------- - pd.DataFrame - Exchange info - """ - bursa = all_bursa() - - symbol = symbol.upper() - if symbol in bursa["short_name"].values: - df = pd.DataFrame(bursa.loc[bursa["short_name"] == symbol]).transpose() - is_open = check_if_open(bursa, symbol) - df_is_open = pd.DataFrame([is_open], index=["open"], columns=df.columns.values) - df = pd.concat([df, df_is_open], axis=0) - return df - if symbol in bursa.index: - df = pd.DataFrame(bursa.loc[symbol]) - is_open = check_if_open(bursa, symbol) - df_is_open = pd.DataFrame([is_open], index=["open"], columns=df.columns.values) - df = pd.concat([df, df_is_open], axis=0) - return df - return pd.DataFrame() - - -@log_start_end(log=logger) -def get_open() -> pd.DataFrame: - """Get open exchanges. - - Parameters - ---------- - - Returns - ------- - pd.DataFrame - Currently open exchanges - """ - bursa = all_bursa() - is_open_list = [] - for exchange in bursa.index: - is_open = check_if_open(bursa, exchange) - is_open_list.append(is_open) - bursa["open"] = is_open_list - bursa = bursa.loc[bursa["open"]] - return bursa[["name", "short_name"]] - - -@log_start_end(log=logger) -def get_closed() -> pd.DataFrame: - """Get closed exchanges. - - Parameters - ---------- - - Returns - ------- - pd.DataFrame - Currently closed exchanges - """ - bursa = all_bursa() - is_open_list = [] - for exchange in bursa.index: - is_open = check_if_open(bursa, exchange) - is_open_list.append(is_open) - bursa["open"] = is_open_list - bursa = bursa.loc[~bursa["open"]] - return bursa[["name", "short_name"]] - - -@log_start_end(log=logger) -def get_all() -> pd.DataFrame: - """Get all exchanges. - - Parameters - ---------- - - Returns - ------- - pd.DataFrame - All available exchanges - """ - bursa = all_bursa() - is_open_list = [] - for exchange in bursa.index: - is_open = check_if_open(bursa, exchange) - is_open_list.append(is_open) - bursa["open"] = is_open_list - return bursa[["name", "short_name", "open"]] - - -@log_start_end(log=logger) -def get_all_exchange_short_names() -> pd.DataFrame: - """Get all exchanges short names. - - Parameters - ---------- - - Returns - ------- - pd.DataFrame - All available exchanges short names - """ - bursa = all_bursa() - is_open_list = [] - for exchange in bursa.index: - is_open = check_if_open(bursa, exchange) - is_open_list.append(is_open) - bursa["open"] = is_open_list - return bursa[["short_name"]] - - -@log_start_end(log=logger) -def all_bursa(): - """Get all exchanges from dictionary - - Parameters - __________ - - Returns - _______ - pd.DataFrame - All exchanges - """ - path = os.path.join(os.path.dirname(__file__), "data/bursa_open_hours.json") - bursa = pd.read_json(path) # , orient="index") - return bursa - - -def check_if_open(bursa: pd.DataFrame, exchange: str) -> bool: - """Check if market open helper function - - Parameters - __________ - bursa : pd.DataFrame - pd.DataFrame of all exchanges - exchange : str - bursa pd.DataFrame index value for exchange - - Returns - _______ - bool - If market is open - """ - exchange = exchange.upper() - if exchange in bursa.index.values: - tz = bursa.loc[exchange]["timezone"] - exchange_df = bursa.loc[exchange] - elif exchange in bursa["short_name"].values: - tz = bursa.loc[bursa["short_name"] == exchange]["timezone"].values[0] - exchange_df = bursa.loc[bursa["short_name"] == exchange] - exchange_df = exchange_df.iloc[0].transpose() - utcmoment_naive = datetime.utcnow() - utcmoment = utcmoment_naive.replace(tzinfo=pytz.utc) - local_datetime = utcmoment.astimezone(pytz.timezone(tz)) - market_open = datetime.strptime(exchange_df["market_open"], "%H:%M:%S") - market_close = datetime.strptime(exchange_df["market_close"], "%H:%M:%S") - after_market_open = local_datetime.time() >= market_open.time() - before_market_close = local_datetime.time() <= market_close.time() - try: - lunchbreak_start = datetime.strptime( - exchange_df["lunchbreak_start"], "%H:%M:%S" - ) - lunchbreak_end = datetime.strptime(exchange_df["lunchbreak_end"], "%H:%M:%S") - - after_lunch_start = local_datetime.time() >= lunchbreak_start.time() - before_lunch_end = local_datetime.time() <= lunchbreak_end.time() - except Exception: - after_lunch_start = False - before_lunch_end = False - - if local_datetime.weekday() >= 5: - result = False - else: - result = ( - after_market_open - and before_market_close - and not (after_lunch_start and before_lunch_end) - ) - - return result diff --git a/openbb_terminal/stocks/tradinghours/bursa_view.py b/openbb_terminal/stocks/tradinghours/bursa_view.py deleted file mode 100644 index 9a96cbe8bfe..00000000000 --- a/openbb_terminal/stocks/tradinghours/bursa_view.py +++ /dev/null @@ -1,104 +0,0 @@ -"""Stocks Trading Hours View.""" - -__docformat__ = "numpy" - -import logging - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.tradinghours import bursa_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def display_exchange(symbol: str): - """Display current exchange trading hours. - - Parameters - ---------- - symbol : str - Exchange symbol - """ - exchange = bursa_model.get_bursa(symbol) - - if len(exchange) == 0 or exchange.empty: - console.print( - "[red]" - + "No exchange data loaded.\n" - + "Make sure you picked a valid exchange symbol." - + "[/red]\n" - ) - return - - exchange_name = exchange.loc["name"] - - print_rich_table( - exchange, - show_index=True, - title=f"[bold]{exchange_name}[/bold]", - ) - - -@log_start_end(log=logger) -def display_open(): - """Display open exchanges. - - Parameters - ---------- - """ - exchanges = bursa_model.get_open() - - if exchanges.empty: - console.print("No exchange open.\n") - return - - print_rich_table( - exchanges, - show_index=True, - index_name="Symbol", - title="[bold]Open markets[/bold]", - ) - - -@log_start_end(log=logger) -def display_closed(): - """Display closed exchanges. - - Parameters - ---------- - """ - exchanges = bursa_model.get_closed() - - if exchanges.empty: - console.print("[red]" + "No exchange data loaded.\n" + "[/red]\n") - return - - print_rich_table( - exchanges, - show_index=True, - index_name="Symbol", - title="[bold]Closed markets[/bold]", - ) - - -@log_start_end(log=logger) -def display_all(): - """Display all exchanges. - - Parameters - ---------- - """ - exchanges = bursa_model.get_all() - - if exchanges.empty: - console.print("[red]" + "No exchange data loaded.\n" + "[/red]\n") - return - - print_rich_table( - exchanges, - show_index=True, - index_name="Symbol", - title="[bold]World markets[/bold]", - ) diff --git a/openbb_terminal/stocks/tradinghours/data/__init__.py b/openbb_terminal/stocks/tradinghours/data/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/stocks/tradinghours/data/bursa_open_hours.json b/openbb_terminal/stocks/tradinghours/data/bursa_open_hours.json deleted file mode 100644 index 04e1ec9e17d..00000000000 --- a/openbb_terminal/stocks/tradinghours/data/bursa_open_hours.json +++ /dev/null @@ -1,1029 +0,0 @@ -{ - "name": { - "SHZ": "Shenzen Stock Exchange", - "KSC": "Korea Exchange", - "KOE": "Korea Exchange", - "HKG": "Hong Kong Stock Exchange", - "KLS": "Bursa Malaysia", - "FRA": "Frankfurt Stock Exchange", - "STU": "B\u00f6rse Stuttgart", - "ENX": "Euronext", - "TWO": "Taipei Exchange", - "HAM": "Hamburger B\u00f6rse", - "MUN": "B\u00f6rse M\u00fcnchen", - "DUS": "B\u00f6rse D\u00fcsseldorf", - "TW": "Taiwan Stock Exchange", - "PAR": "Euronext Paris", - "GER": "Deutsche B\u00f6rse Xetra", - "IOB": "London Stock Exchange", - "LSE": "London Stock Exchange", - "MEX": "Bolsa Mexicana de Valores", - "HAN": "B\u00f6rsen Hamburg-Hannover", - "SAU": "Tadwul", - "JPX": "Japan Exchange Group", - "ASX": "Australian Securities Exchange", - "SAP": "Sapporo Securities Exchange", - "FKA": "Frankfurt Stock Exchange", - "SES": "Singapore Exchange", - "TLO": "Borsa Italiana", - "VIE": "Wiener B\u00f6rse", - "BSE": "BSE", - "OSL": "Oslo B\u00f8rs", - "NSI": "National Stock Exchange of India", - "STO": "Nasdaq Nordic Stockholm", - "MCE": "Bolsa de Madrid", - "MIL": "Borsa Italiana", - "BUD": "Budapest Stock Exchange", - "SHH": "Shanghai Stock Exchange", - "SET": "Stock Exchange of Thailand", - "NYQ": "New York Stock Exchange", - "VAN": "TSX Venture Exchange", - "SAO": "A Bolsa do Brasil", - "ISE": "Euronext Dublin", - "AMS": "Euronext Amsterdam", - "ATH": "Athens Stock Exchange", - "PNK": "OTC Markets Group", - "AQS": "Aquis Exchange", - "CPH": "Nasdaq Nordic Copenhagen", - "TOR": "Toronto Stock Exchange", - "NGM": "NasdaqGM", - "NCM": "Nasdaq", - "NMS": "NasdaqGS", - "CSE": "Colombo Stock Exchange", - "JKT": "Indonesia Stock Exchange", - "HEL": "Nasdaq Nordic Helsinki", - "EBS": "SIX Swiss Exchange", - "ASE": "NYSE American", - "BUE": "Bolsas y Mercados Argentinos", - "SGO": "Bolsa de Santiago", - "CNQ": "Canadian Securities Exchange: CSE", - "NZE": "NZX", - "CCS": "Bolsa de Valores de Caracas", - "JNB": "JSE", - "BRU": "Euronext Brussels", - "TLV": "Tel Aviv Stock Exchange", - "DOH": "Qatar Stock Exchange", - "MCX": "MOEX", - "NEO": "NEO Exchange", - "NYS": "NYS", - "IST": "Borsa Istanbul", - "NAS": "NASDAQ", - "LIS": "Euronext Lisbon", - "LIT": "Nasdaq OMX Vilnius", - "PRA": "Prague Stock Exchange", - "TAL": "Nasdaq OMX Tallinn", - "ICE": "Nasdaq Nordic Iceland", - "RIS": "Nasdaq OMX Riga", - "CAI": "The Egyptian Exchange", - "PCX": "NYSE Arca", - "SAT": "Nasdaq OMX Stockholm" - }, - "short_name": { - "SHZ": "SHZE", - "KSC": "KRX", - "KOE": "KRX", - "HKG": "HKEX", - "KLS": "KLSE", - "FRA": "F", - "STU": "SG", - "ENX": "NX", - "TWO": "TWO", - "HAM": "HM", - "MUN": "MU", - "DUS": "DU", - "TW": "TWSE", - "PAR": "PA", - "GER": "DE", - "IOB": "IL", - "LSE": "L", - "MEX": "MX", - "HAN": "HA", - "SAU": "SR", - "JPX": "T", - "ASX": "AX", - "SAP": "S", - "FKA": "F", - "SES": "SI", - "TLO": "TI", - "VIE": "VI", - "BSE": "BO", - "OSL": "OL", - "NSI": "NS", - "STO": "ST", - "MCE": "MC", - "MIL": "MI", - "BUD": "BD", - "SHH": "SS", - "SET": "BK", - "NYQ": "NYSE", - "VAN": "V", - "SAO": "SA", - "ISE": "IR", - "AMS": "AS", - "ATH": "AT", - "PNK": "OTC", - "AQS": "AQ", - "CPH": "CO", - "TOR": "TO", - "NGM": "NASGM", - "NCM": "NASDQ", - "NMS": "NASGS", - "CSE": "CM", - "JKT": "JK", - "HEL": "HE", - "EBS": "SW", - "ASE": "AMEX", - "BUE": "BA", - "SGO": "SN", - "CNQ": "CN", - "NZE": "NZ", - "CCS": "CR", - "JNB": "JO", - "BRU": "BR", - "TLV": "TA", - "DOH": "QA", - "MCX": "ME", - "NEO": "NE", - "NYS": "NYS", - "IST": "IS", - "NAS": "NAS", - "LIS": "LS", - "LIT": "VS", - "PRA": "PR", - "TAL": "TL", - "ICE": "IC", - "RIS": "RG", - "CAI": "CA", - "PCX": "ARCA", - "SAT": "ST" - }, - "website": { - "SHZ": "http:\/\/www.szse.cn\/English\/services\/trading\/tradOverview\/", - "KSC": "http:\/\/global.krx.co.kr\/contents\/GLB\/06\/0603\/0603010300\/GLB0603010300.jsp", - "KOE": "http:\/\/global.krx.co.kr\/contents\/GLB\/06\/0603\/0603010300\/GLB0603010300.jsp", - "HKG": "https:\/\/www.hkex.com.hk\/Services\/Trading-hours-and-Severe-Weather-Arrangements\/Trading-Hours\/Securities-Market?sc_lang=en", - "KLS": "https:\/\/www.bursamalaysia.com\/trade\/trading_resources\/equities\/trading_sessions", - "FRA": "https:\/\/www.xetra.com\/xetra-en\/trading\/trading-calendar-and-trading-hours", - "STU": "https:\/\/www.boerse-stuttgart.de\/-\/media\/files\/geschaeftspartner\/regelwerke\/en\/exchange-rules\/20220101-exchange-rules-clean.ashx?la=en&hash=0F73CA59A4AC13AF6C734CDB808098D1", - "ENX": "https:\/\/www.euronext.com\/en\/trade\/trading-hours-holidays", - "TWO": "https:\/\/www.tpex.org.tw\/web\/service\/sotck_info\/feature\/feature.php?l=en-us", - "HAM": "http:\/\/www.hamburger-boerse.de\/en\/the-stock-exchange\/markets.html", - "MUN": "https:\/\/www.boerse-muenchen.de\/boersenplatz", - "DUS": "", - "TW": "https:\/\/www.twse.com.tw\/en\/page\/products\/trading\/information13.html", - "PAR": "https:\/\/www.euronext.com\/en\/trade\/trading-hours-holidays", - "GER": "https:\/\/www.xetra.com\/xetra-en\/trading\/trading-calendar-and-trading-hours", - "IOB": "https:\/\/www.londonstockexchange.com\/trade\/equity-trading", - "LSE": "https:\/\/www.londonstockexchange.com\/trade\/equity-trading", - "MEX": null, - "HAN": "https:\/\/www.boersenag.de\/Aktienhandel", - "SAU": "https:\/\/www.saudiexchange.sa\/wps\/portal\/tadawul\/knowledge-center\/about\/trading-times?locale=en", - "JPX": "https:\/\/www.jpx.co.jp\/english\/derivatives\/rules\/trading-hours\/index.html", - "ASX": "https:\/\/www2.asx.com.au\/markets\/market-resources\/trading-hours-calendar\/cash-market-trading-hours", - "SAP": "https:\/\/www.sse.or.jp\/torihiki\/kabushiki", - "FKA": "https:\/\/www.xetra.com\/xetra-en\/trading\/trading-calendar-and-trading-hours", - "SES": "http:\/\/rulebook.sgx.com\/rulebook\/regulatory-notice-821-trading-hours-market-phases-application-market-phases-and-principles", - "TLO": "https:\/\/www.borsaitaliana.it\/borsaitaliana\/calendario-e-orari-di-negoziazione\/tradinghours-2021.en_pdf.htm", - "VIE": "https:\/\/www.wienerborse.at\/en\/trading\/trading-information\/trading-hours\/", - "BSE": "https:\/\/www.bseindia.com\/static\/markets\/equity\/EQReports\/tra_trading.aspx", - "OSL": "https:\/\/www.euronext.com\/en\/trade\/trading-hours-holidays", - "NSI": "https:\/\/www.nseindia.com\/resources\/exchange-communication-holidays", - "STO": "http:\/\/www.nasdaqomxnordic.com\/tradinghours", - "MCE": "https:\/\/www.bolsamadrid.es\/ing\/Inversores\/Agenda\/HorarioMercado.aspx", - "MIL": "https:\/\/www.borsaitaliana.it\/borsaitaliana\/calendario-e-orari-di-negoziazione\/tradinghours-2021.en_pdf.htm", - "BUD": "https:\/\/www.bse.hu\/Products-and-Services\/Trading-information\/Trading-hours", - "SHH": "http:\/\/english.sse.com.cn\/start\/trading\/schedule\/", - "SET": "https:\/\/www.set.or.th\/en\/products\/trading\/equity\/tradingsystem_p2.html", - "NYQ": "https:\/\/www.nyse.com\/markets\/hours-calendars", - "VAN": "https:\/\/www.tsx.com\/trading\/calendars-and-trading-hours\/trading-hours", - "SAO": "https:\/\/www.b3.com.br\/en_us\/solutions\/platforms\/puma-trading-system\/for-members-and-traders\/trading-hours\/equities\/", - "ISE": "https:\/\/www.euronext.com\/en\/trade\/trading-hours-holidays", - "AMS": "https:\/\/www.euronext.com\/en\/trade\/trading-hours-holidays", - "ATH": "https:\/\/www.athexgroup.gr\/documents\/10180\/4974340\/OASIS+Markets+Schedule_+Oct+2021+ver+1.pdf\/f8613e1f-5b6e-4eb0-a891-05a5b9ad1492", - "PNK": "https:\/\/www.otcmarkets.com\/learn\/faqs", - "AQS": "https:\/\/www.aquis.eu\/trading\/trading-calendar-hours", - "CPH": "http:\/\/www.nasdaqomxnordic.com\/tradinghours", - "TOR": "https:\/\/www.tsx.com\/trading\/calendars-and-trading-hours\/trading-hours", - "NGM": "https:\/\/www.nasdaq.com\/stock-market-trading-hours-for-nasdaq", - "NCM": "https:\/\/www.nasdaq.com\/stock-market-trading-hours-for-nasdaq", - "NMS": "https:\/\/www.nasdaq.com\/stock-market-trading-hours-for-nasdaq", - "CSE": "https:\/\/www.cse.lk\/pages\/trading-sessions\/trading-sessions.component.html", - "JKT": "https:\/\/www.idx.co.id\/en-us\/investhub\/trading-hours\/", - "HEL": "http:\/\/www.nasdaqomxnordic.com\/tradinghours", - "EBS": "https:\/\/www.six-group.com\/en\/products-services\/the-swiss-stock-exchange\/trading\/trading-provisions\/trading-hours.html", - "ASE": "https:\/\/www.nyse.com\/markets\/nyse-arca\/market-info", - "BUE": "https:\/\/www.byma.com.ar\/servicios\/horarios\/", - "SGO": "https:\/\/www.bolsadesantiago.com\/mercado_horarios_feriados", - "CNQ": "https:\/\/www.thecse.com\/en\/trading\/trading-rules-and-links\/trading-rules-and-regulations", - "NZE": "https:\/\/www.nzx.com\/services\/nzx-trading\/hours-boards", - "CCS": "https:\/\/www.bolsadecaracas.com\/nosotros\/#horario", - "JNB": "https:\/\/www.jse.co.za\/sites\/default\/files\/media\/documents\/market-regulation\/Equities%20Directives.pdf", - "BRU": "https:\/\/www.euronext.com\/en\/trade\/trading-hours-holidays", - "TLV": "https:\/\/info.tase.co.il\/eng\/trading\/trading_schedule\/pages\/trading_schedule.aspx", - "DOH": "https:\/\/www.qe.com.qa\/web\/venture-market\/trading-hours", - "MCX": "https:\/\/www.moex.com\/en\/", - "NEO": "https:\/\/www.neo.inc\/en\/resources?item=trading-hours", - "NYS": "", - "IST": "https:\/\/www.borsaistanbul.com\/en\/sayfa\/3637\/trading-hours", - "NAS": "https:\/\/www.nasdaq.com\/stock-market-trading-hours-for-nasdaq", - "LIS": "https:\/\/www.euronext.com\/en\/trade\/trading-hours-holidays", - "LIT": "https:\/\/nasdaqbaltic.com\/market-information\/trading-day\/", - "PRA": "https:\/\/www.pse.cz\/en\/trading\/trading-information\/trading-hours", - "TAL": "https:\/\/nasdaqbaltic.com\/market-information\/trading-day\/", - "ICE": "http:\/\/www.nasdaqomxnordic.com\/tradinghours", - "RIS": "https:\/\/nasdaqbaltic.com\/market-information\/trading-day\/", - "CAI": "https:\/\/www.egx.com.eg\/en\/Trading_Hours.aspx", - "PCX": "https:\/\/www.nyse.com\/markets\/hours-calendars", - "SAT": "http:\/\/www.nasdaqomxnordic.com\/tradinghours" - }, - "market_open": { - "SHZ": "09:30:00", - "KSC": "09:00:00", - "KOE": "09:00:00", - "HKG": "09:30:00", - "KLS": "09:00:00", - "FRA": "08:00:00", - "STU": "08:00:00", - "ENX": "09:00:00", - "TWO": "09:00:00", - "HAM": "08:00:00", - "MUN": "08:00:00", - "DUS": "08:00:00", - "TW": "09:00:00", - "PAR": "09:00:00", - "GER": "09:00:00", - "IOB": "08:00:00", - "LSE": "08:00:00", - "MEX": "08:30:00", - "HAN": "09:00:00", - "SAU": "10:00:00", - "JPX": "09:00:00", - "ASX": "10:00:00", - "SAP": "09:00:00", - "FKA": "08:00:00", - "SES": "09:00:00", - "TLO": "09:00:00", - "VIE": "09:00:00", - "BSE": "09:15:00", - "OSL": "09:00:00", - "NSI": "09:15:00", - "STO": "09:00:00", - "MCE": "09:00:00", - "MIL": "09:00:00", - "BUD": "09:00:00", - "SHH": "09:30:00", - "SET": "10:00:00", - "NYQ": "09:30:00", - "VAN": "09:30:00", - "SAO": "10:00:00", - "ISE": "09:00:00", - "AMS": "09:00:00", - "ATH": "10:30:00", - "PNK": "06:00:00", - "AQS": "08:00:00", - "CPH": "09:00:00", - "TOR": "09:30:00", - "NGM": "09:30:00", - "NCM": "09:30:00", - "NMS": "09:30:00", - "CSE": "09:30:00", - "JKT": "09:00:00", - "HEL": "09:00:00", - "EBS": "09:00:00", - "ASE": "09:30:00", - "BUE": "11:00:00", - "SGO": "09:30:00", - "CNQ": "08:00:00", - "NZE": "10:00:00", - "CCS": "09:00:00", - "JNB": "09:00:00", - "BRU": "09:00:00", - "TLV": "09:59:00", - "DOH": "09:30:00", - "MCX": "01:01:00", - "NEO": "09:30:00", - "NYS": "09:30:00", - "IST": "10:00:00", - "NAS": "09:30:00", - "LIS": "09:00:00", - "LIT": "10:00:00", - "PRA": "09:00:00", - "TAL": "10:00:00", - "ICE": "07:30:00", - "RIS": "10:00:00", - "CAI": "10:00:00", - "PCX": "09:30:00", - "SAT": "09:00:00" - }, - "market_close": { - "SHZ": "14:57:00", - "KSC": "15:35:00", - "KOE": "15:35:00", - "HKG": "16:00:00", - "KLS": "16:45:00", - "FRA": "22:00:00", - "STU": "22:00:00", - "ENX": "17:30:00", - "TWO": "13:30:00", - "HAM": "20:00:00", - "MUN": "22:00:00", - "DUS": "22:00:00", - "TW": "13:25:00", - "PAR": "17:30:00", - "GER": "17:30:00", - "IOB": "16:30:00", - "LSE": "16:30:00", - "MEX": "15:00:00", - "HAN": "17:30:00", - "SAU": "15:20:00", - "JPX": "15:00:00", - "ASX": "16:00:00", - "SAP": "15:30:00", - "FKA": "22:00:00", - "SES": "17:00:00", - "TLO": "17:30:00", - "VIE": "17:30:00", - "BSE": "15:30:00", - "OSL": "17:30:00", - "NSI": "15:30:00", - "STO": "17:30:00", - "MCE": "17:30:00", - "MIL": "17:30:00", - "BUD": "17:00:00", - "SHH": "14:57:00", - "SET": "16:30:00", - "NYQ": "16:00:00", - "VAN": "16:00:00", - "SAO": "16:55:00", - "ISE": "17:30:00", - "AMS": "17:30:00", - "ATH": "17:00:00", - "PNK": "17:00:00", - "AQS": "16:30:00", - "CPH": "17:00:00", - "TOR": "16:00:00", - "NGM": "16:00:00", - "NCM": "16:00:00", - "NMS": "16:00:00", - "CSE": "14:30:00", - "JKT": "15:00:00", - "HEL": "17:30:00", - "EBS": "17:30:00", - "ASE": "16:00:00", - "BUE": "16:57:00", - "SGO": "16:00:00", - "CNQ": "18:00:00", - "NZE": "16:45:00", - "CCS": "13:00:00", - "JNB": "16:50:00", - "BRU": "17:30:00", - "TLV": "17:14:00", - "DOH": "13:15:00", - "MCX": "02:02:00", - "NEO": "16:00:00", - "NYS": "16:00:00", - "IST": "18:00:00", - "NAS": "16:00:00", - "LIS": "17:30:00", - "LIT": "15:55:00", - "PRA": "16:10:00", - "TAL": "15:55:00", - "ICE": "13:30:00", - "RIS": "15:55:00", - "CAI": "14:15:00", - "PCX": "16:00:00", - "SAT": "17:30:00" - }, - "lunchbreak_start": { - "SHZ": "11:30:00", - "KSC": null, - "KOE": null, - "HKG": null, - "KLS": "12:30:00", - "FRA": null, - "STU": null, - "ENX": null, - "TWO": null, - "HAM": null, - "MUN": null, - "DUS": null, - "TW": null, - "PAR": null, - "GER": null, - "IOB": null, - "LSE": null, - "MEX": null, - "HAN": null, - "SAU": null, - "JPX": "11:30:00", - "ASX": null, - "SAP": "11:30:00", - "FKA": null, - "SES": "12:00:00", - "TLO": null, - "VIE": null, - "BSE": null, - "OSL": null, - "NSI": null, - "STO": null, - "MCE": null, - "MIL": null, - "BUD": null, - "SHH": null, - "SET": "12:30:00", - "NYQ": null, - "VAN": null, - "SAO": null, - "ISE": null, - "AMS": null, - "ATH": null, - "PNK": null, - "AQS": null, - "CPH": null, - "TOR": null, - "NGM": null, - "NCM": null, - "NMS": null, - "CSE": null, - "JKT": "11:30:00", - "HEL": null, - "EBS": null, - "ASE": null, - "BUE": null, - "SGO": null, - "CNQ": null, - "NZE": null, - "CCS": null, - "JNB": null, - "BRU": null, - "TLV": null, - "DOH": null, - "MCX": null, - "NEO": null, - "NYS": null, - "IST": null, - "NAS": null, - "LIS": null, - "LIT": null, - "PRA": null, - "TAL": null, - "ICE": null, - "RIS": null, - "CAI": null, - "PCX": null, - "SAT": null - }, - "lunchbreak_end": { - "SHZ": "13:00:00", - "KSC": null, - "KOE": null, - "HKG": null, - "KLS": "14:00:00", - "FRA": null, - "STU": null, - "ENX": null, - "TWO": null, - "HAM": null, - "MUN": null, - "DUS": null, - "TW": null, - "PAR": null, - "GER": null, - "IOB": null, - "LSE": null, - "MEX": null, - "HAN": null, - "SAU": null, - "JPX": "12:30:00", - "ASX": null, - "SAP": "12:30:00", - "FKA": null, - "SES": "12:58:00", - "TLO": null, - "VIE": null, - "BSE": null, - "OSL": null, - "NSI": null, - "STO": null, - "MCE": null, - "MIL": null, - "BUD": null, - "SHH": null, - "SET": "14:00:00", - "NYQ": null, - "VAN": null, - "SAO": null, - "ISE": null, - "AMS": null, - "ATH": null, - "PNK": null, - "AQS": null, - "CPH": null, - "TOR": null, - "NGM": null, - "NCM": null, - "NMS": null, - "CSE": null, - "JKT": "13:30:00", - "HEL": null, - "EBS": null, - "ASE": null, - "BUE": null, - "SGO": null, - "CNQ": null, - "NZE": null, - "CCS": null, - "JNB": null, - "BRU": null, - "TLV": null, - "DOH": null, - "MCX": null, - "NEO": null, - "NYS": null, - "IST": null, - "NAS": null, - "LIS": null, - "LIT": null, - "PRA": null, - "TAL": null, - "ICE": null, - "RIS": null, - "CAI": null, - "PCX": null, - "SAT": null - }, - "opening_auction_start": { - "SHZ": "09:15:00", - "KSC": "08:30:00", - "KOE": "08:30:00", - "HKG": "09:00:00", - "KLS": "08:30:00", - "FRA": null, - "STU": null, - "ENX": null, - "TWO": null, - "HAM": null, - "MUN": null, - "DUS": null, - "TW": "08:30:00", - "PAR": null, - "GER": null, - "IOB": "07:50:00", - "LSE": "07:50:00", - "MEX": null, - "HAN": null, - "SAU": null, - "JPX": null, - "ASX": "07:00:00", - "SAP": null, - "FKA": null, - "SES": "08:30:00", - "TLO": "08:00:00", - "VIE": "08:00:00", - "BSE": null, - "OSL": null, - "NSI": "09:00:00", - "STO": null, - "MCE": "08:30:00", - "MIL": "08:00:00", - "BUD": "08:15:00", - "SHH": "09:15:00", - "SET": "09:30:00", - "NYQ": null, - "VAN": "07:00:00", - "SAO": "09:45:00", - "ISE": null, - "AMS": null, - "ATH": null, - "PNK": null, - "AQS": null, - "CPH": null, - "TOR": "07:00:00", - "NGM": null, - "NCM": null, - "NMS": null, - "CSE": "09:00:00", - "JKT": "08:45:00", - "HEL": null, - "EBS": null, - "ASE": null, - "BUE": null, - "SGO": null, - "CNQ": null, - "NZE": "08:30:00", - "CCS": "08:30:00", - "JNB": "08:30:00", - "BRU": null, - "TLV": null, - "DOH": "09:00:00", - "MCX": null, - "NEO": "07:00:00", - "NYS": null, - "IST": "09:40:00", - "NAS": null, - "LIS": null, - "LIT": null, - "PRA": "08:50:00", - "TAL": null, - "ICE": null, - "RIS": null, - "CAI": null, - "PCX": "04:00:00", - "SAT": null - }, - "opening_auction_end": { - "SHZ": "09:25:00", - "KSC": "09:00:00", - "KOE": "09:00:00", - "HKG": "09:30:00", - "KLS": "09:00:00", - "FRA": null, - "STU": null, - "ENX": null, - "TWO": null, - "HAM": null, - "MUN": null, - "DUS": null, - "TW": "09:00:00", - "PAR": null, - "GER": null, - "IOB": "08:00:00", - "LSE": "08:00:00", - "MEX": null, - "HAN": null, - "SAU": null, - "JPX": null, - "ASX": "10:00:00", - "SAP": null, - "FKA": null, - "SES": "09:00:00", - "TLO": "09:00:00", - "VIE": "09:00:00", - "BSE": null, - "OSL": null, - "NSI": "09:15:00", - "STO": null, - "MCE": "09:00:00", - "MIL": "09:00:00", - "BUD": "09:00:00", - "SHH": "09:25:00", - "SET": "10:00:00", - "NYQ": null, - "VAN": "09:30:00", - "SAO": "10:00:00", - "ISE": null, - "AMS": null, - "ATH": null, - "PNK": null, - "AQS": null, - "CPH": null, - "TOR": "09:30:00", - "NGM": null, - "NCM": null, - "NMS": null, - "CSE": "09:30:00", - "JKT": "09:00:00", - "HEL": null, - "EBS": null, - "ASE": null, - "BUE": null, - "SGO": null, - "CNQ": null, - "NZE": "10:00:00", - "CCS": "09:00:00", - "JNB": "09:00:00", - "BRU": null, - "TLV": null, - "DOH": "09:30:00", - "MCX": null, - "NEO": "09:30:00", - "NYS": null, - "IST": "10:00:00", - "NAS": null, - "LIS": null, - "LIT": null, - "PRA": "09:00:00", - "TAL": null, - "ICE": null, - "RIS": null, - "CAI": "14:15:00", - "PCX": "09:30:00", - "SAT": null - }, - "closing_auction_start": { - "SHZ": "14:57:00", - "KSC": "15:35:00", - "KOE": "15:35:00", - "HKG": "16:00:00", - "KLS": "16:45:00", - "FRA": null, - "STU": null, - "ENX": null, - "TWO": null, - "HAM": null, - "MUN": null, - "DUS": null, - "TW": "13:25:00", - "PAR": null, - "GER": null, - "IOB": "16:30:00", - "LSE": "16:30:00", - "MEX": null, - "HAN": null, - "SAU": null, - "JPX": null, - "ASX": "16:10:00", - "SAP": null, - "FKA": null, - "SES": "17:00:00", - "TLO": "17:30:00", - "VIE": "17:30:00", - "BSE": null, - "OSL": null, - "NSI": "15:40:00", - "STO": null, - "MCE": "17:30:00", - "MIL": "17:30:00", - "BUD": "17:00:00", - "SHH": "14:57:00", - "SET": "16:30:00", - "NYQ": null, - "VAN": "16:10:00", - "SAO": "16:55:00", - "ISE": null, - "AMS": null, - "ATH": null, - "PNK": null, - "AQS": null, - "CPH": null, - "TOR": "16:10:00", - "NGM": null, - "NCM": null, - "NMS": null, - "CSE": null, - "JKT": "15:01:00", - "HEL": null, - "EBS": null, - "ASE": null, - "BUE": null, - "SGO": null, - "CNQ": null, - "NZE": "16:45:00", - "CCS": "13:00:00", - "JNB": "16:50:00", - "BRU": null, - "TLV": null, - "DOH": null, - "MCX": null, - "NEO": "16:05:00", - "NYS": null, - "IST": "18:00:00", - "NAS": null, - "LIS": null, - "LIT": null, - "PRA": "16:10:00", - "TAL": null, - "ICE": null, - "RIS": null, - "CAI": "14:25:00", - "PCX": "16:00:00", - "SAT": null - }, - "closing_auction_end": { - "SHZ": "15:00:00", - "KSC": "15:45:00", - "KOE": "15:45:00", - "HKG": "16:08:00", - "KLS": "17:00:00", - "FRA": null, - "STU": null, - "ENX": null, - "TWO": null, - "HAM": null, - "MUN": null, - "DUS": null, - "TW": "13:30:00", - "PAR": null, - "GER": null, - "IOB": "16:35:00", - "LSE": "16:35:00", - "MEX": null, - "HAN": null, - "SAU": null, - "JPX": null, - "ASX": "16:12:00", - "SAP": null, - "FKA": null, - "SES": "17:16:00", - "TLO": "17:42:00", - "VIE": "17:50:00", - "BSE": null, - "OSL": null, - "NSI": "16:00:00", - "STO": null, - "MCE": "17:35:00", - "MIL": "17:42:00", - "BUD": "17:20:00", - "SHH": "15:00:00", - "SET": "17:00:00", - "NYQ": null, - "VAN": "17:00:00", - "SAO": "18:00:00", - "ISE": null, - "AMS": null, - "ATH": null, - "PNK": null, - "AQS": null, - "CPH": null, - "TOR": "17:00:00", - "NGM": null, - "NCM": null, - "NMS": null, - "CSE": null, - "JKT": "15:15:00", - "HEL": null, - "EBS": null, - "ASE": null, - "BUE": null, - "SGO": null, - "CNQ": null, - "NZE": "17:00:00", - "CCS": "13:30:00", - "JNB": "17:00:00", - "BRU": null, - "TLV": null, - "DOH": null, - "MCX": null, - "NEO": "17:00:00", - "NYS": null, - "IST": "18:05:00", - "NAS": null, - "LIS": null, - "LIT": null, - "PRA": "16:15:00", - "TAL": null, - "ICE": null, - "RIS": null, - "CAI": null, - "PCX": "20:00:00", - "SAT": null - }, - "timezone": { - "SHZ": "Asia\/Shanghai", - "KSC": "Asia\/Seoul", - "KOE": "Asia\/Seoul", - "HKG": "Asia\/Hong_Kong", - "KLS": "Asia\/Kuala_Lumpur", - "FRA": "Europe\/Berlin", - "STU": "Europe\/Berlin", - "ENX": "Europe\/Amsterdam", - "TWO": "Asia\/Taipei", - "HAM": "Europe\/Berlin", - "MUN": "Europe\/Berlin", - "DUS": "Europe\/Berlin", - "TW": "Asia\/Taipei", - "PAR": "Europe\/Paris", - "GER": "Europe\/Berlin", - "IOB": "Europe\/London", - "LSE": "Europe\/London", - "MEX": "Mexico\/General", - "HAN": "Europe\/Berlin", - "SAU": "Asia\/Riyadh", - "JPX": "Asia\/Tokyo", - "ASX": "Australia\/Sydney", - "SAP": "Asia\/Tokyo", - "FKA": "Europe\/Berlin", - "SES": "Asia\/Singapore", - "TLO": "Europe\/Rome", - "VIE": "Europe\/Vienna", - "BSE": "Asia\/Kolkata", - "OSL": "Europe\/Oslo", - "NSI": "Asia\/Kolkata", - "STO": "Europe\/Stockholm", - "MCE": "Europe\/Madrid", - "MIL": "Europe\/Rome", - "BUD": "Europe\/Budapest", - "SHH": "Asia\/Shanghai", - "SET": "Asia\/Bangkok", - "NYQ": "US\/Eastern", - "VAN": "Canada\/Eastern", - "SAO": "Brazil\/East", - "ISE": "Europe\/Dublin", - "AMS": "Europe\/Amsterdam", - "ATH": "Europe\/Athens", - "PNK": "US\/Eastern", - "AQS": "Europe\/London", - "CPH": "Europe\/Copenhagen", - "TOR": "Canada\/Eastern", - "NGM": "US\/Eastern", - "NCM": "US\/Eastern", - "NMS": "US\/Eastern", - "CSE": "Asia\/Colombo", - "JKT": "Asia\/Jakarta", - "HEL": "Europe\/Helsinki", - "EBS": "Europe\/Zurich", - "ASE": "US\/Eastern", - "BUE": "America\/Argentina\/Buenos_Aires", - "SGO": "Chile\/Continental", - "CNQ": "Canada\/Eastern", - "NZE": "Pacific\/Auckland", - "CCS": "America\/Caracas", - "JNB": "Africa\/Johannesburg", - "BRU": "Europe\/Brussels", - "TLV": "Asia\/Tel_Aviv", - "DOH": "Asia\/Qatar", - "MCX": "Europe\/Moscow", - "NEO": "Canada\/Eastern", - "NYS": "US\/Eastern", - "IST": "Europe\/Istanbul", - "NAS": "US\/Eastern", - "LIS": "Europe\/Lisbon", - "LIT": "Europe\/Vilnius", - "PRA": "Europe\/Prague", - "TAL": "Europe\/Tallinn", - "ICE": "Iceland", - "RIS": "Europe\/Riga", - "CAI": "Egypt", - "PCX": "US\/Eastern", - "SAT": "Europe\/Stockholm" - }, - "flag": { - "SHZ": "\ud83c\udde8\ud83c\uddf3", - "KSC": "\ud83c\uddf0\ud83c\uddf7", - "KOE": "\ud83c\uddf0\ud83c\uddf7", - "HKG": "\ud83c\udded\ud83c\uddf0", - "KLS": "\ud83c\uddf2\ud83c\uddfe", - "FRA": "\ud83c\udde9\ud83c\uddea", - "STU": "\ud83c\udde9\ud83c\uddea", - "ENX": "\ud83c\uddf3\ud83c\uddf1", - "TWO": "\ud83c\uddf9\ud83c\uddfc", - "HAM": "\ud83c\udde9\ud83c\uddea", - "MUN": "\ud83c\udde9\ud83c\uddea", - "DUS": "\ud83c\udde9\ud83c\uddea", - "TW": "\ud83c\uddf9\ud83c\uddfc", - "PAR": "\ud83c\uddeb\ud83c\uddf7", - "GER": "\ud83c\udde9\ud83c\uddea", - "IOB": "\ud83c\uddec\ud83c\udde7", - "LSE": "\ud83c\uddec\ud83c\udde7", - "MEX": "\ud83c\uddf2\ud83c\uddfd", - "HAN": "\ud83c\udde9\ud83c\uddea", - "SAU": "\ud83c\uddf8\ud83c\udde6", - "JPX": "\ud83c\uddef\ud83c\uddf5", - "ASX": "\ud83c\udde6\ud83c\uddfa", - "SAP": "\ud83c\uddef\ud83c\uddf5", - "FKA": "\ud83c\udde9\ud83c\uddea", - "SES": "\ud83c\uddf8\ud83c\uddec", - "TLO": "\ud83c\uddee\ud83c\uddf9", - "VIE": "\ud83c\udde6\ud83c\uddf9", - "BSE": "\ud83c\uddee\ud83c\uddf3", - "OSL": "\ud83c\uddf3\ud83c\uddf4", - "NSI": "\ud83c\uddee\ud83c\uddf3", - "STO": "\ud83c\uddf8\ud83c\uddea", - "MCE": "\ud83c\uddea\ud83c\uddf8", - "MIL": "\ud83c\uddee\ud83c\uddf9", - "BUD": "\ud83c\udded\ud83c\uddfa", - "SHH": "\ud83c\udde8\ud83c\uddf3", - "SET": "\ud83c\uddf9\ud83c\udded", - "NYQ": "\ud83c\uddfa\ud83c\uddf8", - "VAN": "\ud83c\udde8\ud83c\udde6", - "SAO": "\ud83c\udde7\ud83c\uddf7", - "ISE": "\ud83c\uddee\ud83c\uddea", - "AMS": "\ud83c\uddf3\ud83c\uddf1", - "ATH": "\ud83c\uddec\ud83c\uddf7", - "PNK": "\ud83c\uddfa\ud83c\uddf8", - "AQS": "\ud83c\uddec\ud83c\udde7", - "CPH": "\ud83c\udde9\ud83c\uddf0", - "TOR": "\ud83c\udde8\ud83c\udde6", - "NGM": "\ud83c\uddfa\ud83c\uddf8", - "NCM": "\ud83c\uddfa\ud83c\uddf8", - "NMS": "\ud83c\uddfa\ud83c\uddf8", - "CSE": "\ud83c\uddf1\ud83c\uddf0", - "JKT": "\ud83c\uddee\ud83c\udde9", - "HEL": "\ud83c\uddeb\ud83c\uddee", - "EBS": "\ud83c\udde8\ud83c\udded", - "ASE": "\ud83c\uddfa\ud83c\uddf8", - "BUE": "\ud83c\udde6\ud83c\uddf7", - "SGO": "\ud83c\udde8\ud83c\uddf1", - "CNQ": "\ud83c\udde8\ud83c\udde6", - "NZE": "\ud83c\uddf3\ud83c\uddff", - "CCS": "\ud83c\uddfb\ud83c\uddea", - "JNB": "\ud83c\uddff\ud83c\udde6", - "BRU": "\ud83c\udde7\ud83c\uddea", - "TLV": "\ud83c\uddee\ud83c\uddf1", - "DOH": "\ud83c\uddf6\ud83c\udde6", - "MCX": "\ud83c\uddf7\ud83c\uddfa", - "NEO": "\ud83c\udde8\ud83c\udde6", - "NYS": "\ud83c\uddfa\ud83c\uddf8", - "IST": "\ud83c\uddf9\ud83c\uddf7", - "NAS": "\ud83c\uddfa\ud83c\uddf8", - "LIS": "\ud83c\uddf5\ud83c\uddf9", - "LIT": "\ud83c\uddf1\ud83c\uddf9", - "PRA": "\ud83c\udde8\ud83c\uddff", - "TAL": "\ud83c\uddea\ud83c\uddea", - "ICE": "\ud83c\uddee\ud83c\uddf8", - "RIS": "\ud83c\uddf1\ud83c\uddfb", - "CAI": "\ud83c\uddea\ud83c\uddec", - "PCX": "\ud83c\uddfa\ud83c\uddf8", - "SAT": "\ud83c\uddf8\ud83c\uddea" - } -} \ No newline at end of file diff --git a/openbb_terminal/stocks/tradinghours/pandas_market_cal_model.py b/openbb_terminal/stocks/tradinghours/pandas_market_cal_model.py deleted file mode 100644 index 12ef119cabb..00000000000 --- a/openbb_terminal/stocks/tradinghours/pandas_market_cal_model.py +++ /dev/null @@ -1,58 +0,0 @@ -import logging - -import pandas as pd -import pandas_market_calendars as mcal - -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_all_holiday_exchange_short_names() -> pd.DataFrame: - """Get all holiday exchanges short names. - - Returns - ------- - pd.DataFrame - All available exchanges with holiday data (short names) - """ - exchange_short_names = mcal.calendar_registry.get_calendar_names() - - df = pd.DataFrame(exchange_short_names, columns=["short_name"]) - - return df - - -@log_start_end(log=logger) -def get_exchange_holidays(exchange_symbol: str, year: int) -> pd.DataFrame: - """Get all short name of each exchange that we hold holiday calendar for. - - Parameters - ---------- - symbol : str - Exchange symbol - year : int - Calendar year - - Returns - ------- - pd.DataFrame - All available exchanges with holiday data (short names) - """ - - cal = mcal.get_calendar(exchange_symbol) - holidays = pd.DataFrame(cal.holidays().holidays) - - start_date = str(year) + "-01-01" - end_date = str(year) + "-12-31" - holidays.columns = ["Holiday Date"] - - mask = (holidays["Holiday Date"] >= start_date) & ( - holidays["Holiday Date"] <= end_date - ) - exchange_holidays = holidays.loc[mask].copy() - - exchange_holidays["Holiday Date"] = exchange_holidays["Holiday Date"].astype(str) - - return exchange_holidays diff --git a/openbb_terminal/stocks/tradinghours/pandas_market_cal_view.py b/openbb_terminal/stocks/tradinghours/pandas_market_cal_view.py deleted file mode 100644 index a973773e9a3..00000000000 --- a/openbb_terminal/stocks/tradinghours/pandas_market_cal_view.py +++ /dev/null @@ -1,56 +0,0 @@ -import logging - -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import print_rich_table -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.tradinghours import pandas_market_cal_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_all_holiday_exchange_short_names() -> pd.DataFrame: - """Get all holiday exchanges short names. - - Parameters - ---------- - - Returns - ------- - pd.DataFrame - All available exchange with holiday data short names - """ - - return pandas_market_cal_model.get_all_holiday_exchange_short_names() - - -@log_start_end(log=logger) -def display_exchange_holidays(exchange_symbol: str, year: int): - """Display current exchange holiday calendar. - - Parameters - ---------- - symbol : str - Exchange symbol - year : int - Calendar year - """ - - exchange = pandas_market_cal_model.get_exchange_holidays(exchange_symbol, year) - - if len(exchange) == 0 or exchange.empty: - console.print( - "[red]" - + "No exchange data loaded.\n" - + "Make sure you picked a valid exchange symbol and year." - + "[/red]\n" - ) - return - - print_rich_table( - exchange, - show_index=False, - title=f"[bold]{exchange_symbol}[/bold]", - ) diff --git a/openbb_terminal/stocks/tradinghours/tradinghours_controller.py b/openbb_terminal/stocks/tradinghours/tradinghours_controller.py deleted file mode 100644 index bde4735f9fd..00000000000 --- a/openbb_terminal/stocks/tradinghours/tradinghours_controller.py +++ /dev/null @@ -1,315 +0,0 @@ -"""Trading Hours Controller.""" - -__docformat__ = "numpy" - -import argparse -import logging -import os -from datetime import date -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import get_user_timezone_or_invalid -from openbb_terminal.menu import session -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.rich_config import MenuText, console -from openbb_terminal.stocks.tradinghours import bursa_view -from openbb_terminal.stocks.tradinghours.bursa_model import get_open -from openbb_terminal.stocks.tradinghours.pandas_market_cal_view import ( - display_exchange_holidays, - get_all_holiday_exchange_short_names, -) -from openbb_terminal.stocks.tradinghours.tradinghours_helper import ( - get_exchanges_short_names, - get_fd_equities_list, - get_fd_etfs_list, -) - -logger = logging.getLogger(__name__) - -# pylint: disable=R0902 - - -class TradingHoursController(BaseController): - """Trading Hours Controller class.""" - - CHOICES_COMMANDS = ["symbol", "open", "closed", "all", "exchange", "holidays"] - PATH = "/stocks/th/" - FILE_PATH = os.path.join(os.path.dirname(__file__), "README.md") - CHOICES_GENERATION = True - - def __init__(self, ticker: str = "", queue: Optional[List[str]] = None): - """Construct Data.""" - super().__init__(queue) - - self.equities = get_fd_equities_list() - self.etfs = get_fd_etfs_list() - self.equity_tickers = sorted(list(self.equities.keys())) - short_names_df = get_exchanges_short_names() - short_names_index = short_names_df.index.values - short_names = short_names_df["short_name"].values - all_short_names = list(short_names) + list(short_names_index) - self.all_exchange_short_names = sorted(list(all_short_names)) - self.all_holiday_exchange_short_names = sorted( - list(get_all_holiday_exchange_short_names()["short_name"].values) - ) - - self.symbol = None - self.symbol_name = None - self.symbol_market_open = False - self.exchange = None - - if ticker: - if ticker in self.equities.index: - self.symbol = ticker - self.symbol_name = self.equities.loc[ticker]["name"] - self.exchange = self.equities.loc[ticker]["exchange"] - open_ex = get_open() - if self.exchange in open_ex.index: - self.symbol_market_open = True - else: - self.symbol_market_open = False - elif ticker in self.etfs.index: - self.symbol = ticker - self.symbol_name = self.etfs.loc[ticker]["name"] - self.exchange = self.etfs.loc[ticker]["exchange"] - open_ex = get_open() - if self.exchange in open_ex.index: - self.symbol_market_open = True - else: - self.symbol_market_open = False - else: - console.print(f"The ticker {ticker} was not found in the database.") - - self.source = "YahooFinance" - self.data = pd.DataFrame() - self.timezone = get_user_timezone_or_invalid() - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - exchange_opened = ( - ("OPENED" if self.symbol_market_open else "CLOSED") - if self.symbol is not None - else "" - ) - - mt = MenuText("stocks/th/") - mt.add_cmd("open") - mt.add_cmd("closed") - mt.add_cmd("all") - mt.add_cmd("exchange") - mt.add_cmd("holidays") - mt.add_raw("\n") - mt.add_cmd("symbol") - mt.add_raw("\n") - mt.add_param("_symbol_name", self.symbol_name or "") - mt.add_param("_symbol", self.symbol_name or "") - mt.add_param("_exchange", exchange_opened) - - console.print(text=mt.menu_text, menu="Stocks - Trading Hours") - - @log_start_end(log=logger) - def call_symbol(self, other_args: List[str]): - """Process 'symbol' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="symbol", - description="Select the symbol you wish to trade", - ) - parser.add_argument( - "-n", - "--name", - help="Symbol", - type=str.upper, - dest="symbol", - ) - - if ( - other_args - and "-n" not in other_args[0] - and "--name" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-n") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - self.symbol = ns_parser.symbol - - if ns_parser.symbol in self.equities.index: - self.symbol_name = self.equities.loc[self.symbol]["name"] - self.exchange = self.equities.loc[self.symbol]["exchange"] - open_ex = get_open() - if self.exchange in open_ex.index: - self.symbol_market_open = True - else: - self.symbol_market_open = False - # add currency - console.print( - f"\nSelected symbol\nSymbol: {self.symbol}\n" - f"Name: {self.symbol_name}\n" - f"Market open: {self.symbol_market_open}\n" - ) - elif ns_parser.symbol in self.etfs.index: - self.symbol_name = self.etfs.loc[self.symbol]["name"] - self.exchange = self.etfs.loc[self.symbol]["exchange"] - open_ex = get_open() - if self.exchange in open_ex.index: - self.symbol_market_open = True - else: - self.symbol_market_open = False - - console.print( - f"\nSelected symbol\nSymbol: {self.symbol}\n" - f"Name: {self.symbol_name}\n" - f"Market open: {self.symbol_market_open}\n" - ) - else: - console.print("[red]Symbol not found on database.[/red]\n") - - @log_start_end(log=logger) - def call_exchange(self, other_args: List[str]): - """Process 'exchange' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="exchange", - description="Select the exchange you want see open hours for", - ) - parser.add_argument( - "-n", - "--name", - help="Exchange short name", - type=str.upper, - choices=self.all_exchange_short_names, - dest="exchange", - ) - - if ( - other_args - and "-n" not in other_args[0] - and "--name" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-n") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if ns_parser.exchange: - bursa_view.display_exchange(ns_parser.exchange) - else: - logger.error("Select the exchange you want to know about.") - console.print( - "[red]Select the exchange you want to know about.[/red]\n" - ) - - @log_start_end(log=logger) - def call_open(self, other_args: List[str]): - """Process 'symbol' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="exchange", - description="Show currently open exchanges", - ) - if other_args and "-h" not in other_args: - other_args.insert(0, "-n") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - bursa_view.display_open() - - @log_start_end(log=logger) - def call_closed(self, other_args: List[str]): - """Process 'symbol' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="exchange", - description="Show currently closed exchanges", - ) - - if other_args and "-h" not in other_args: - other_args.insert(0, "-n") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - bursa_view.display_closed() - - @log_start_end(log=logger) - def call_all(self, other_args: List[str]): - """Process 'symbol' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="exchange", - description="Show all exchanges", - ) - - if other_args and "-h" not in other_args: - other_args.insert(0, "-n") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - bursa_view.display_all() - - @log_start_end(log=logger) - def call_holidays(self, other_args: List[str]): - """Process 'holidays' command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="exchange", - description="Select the exchange and year you want see holidays for", - ) - parser.add_argument( - "-n", - "--name", - help="Exchange short name", - dest="exchange", - required=True, - type=str.upper, - choices=self.all_holiday_exchange_short_names, - metavar="LSE", - ) - - parser.add_argument( - "-y", - "--year", - help="Year", - nargs="?", - type=int, - const=date.today().year, - dest="year", - metavar="year", - ) - - if ( - other_args - and "-n" not in other_args[0] - and "--name" not in other_args[0] - and "-h" not in other_args - ): - other_args.insert(0, "-n") - other_args.insert(2, "-y") - - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if ns_parser.exchange: - if ns_parser.year: - display_exchange_holidays(ns_parser.exchange, ns_parser.year) - else: - display_exchange_holidays(ns_parser.exchange, date.today().year) - else: - logger.error("Select the exchange and year you want holiday calendar for.") - console.print( - "[red]Select the exchange and year you want holiday calendar for.[/red]\n" - ) diff --git a/openbb_terminal/stocks/tradinghours/tradinghours_helper.py b/openbb_terminal/stocks/tradinghours/tradinghours_helper.py deleted file mode 100644 index e141b02c8eb..00000000000 --- a/openbb_terminal/stocks/tradinghours/tradinghours_helper.py +++ /dev/null @@ -1,33 +0,0 @@ -import logging -from typing import List - -import financedatabase as fd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.stocks.tradinghours.bursa_model import get_all_exchange_short_names - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def get_fd_equities_list() -> List: - """Load FD list of equity symbols.""" - equities = fd.Equities().select(exclude_exchanges=False) - - return equities - - -@log_start_end(log=logger) -def get_fd_etfs_list() -> List: - """Load FD list of etf symbols""" - etfs = fd.ETFs().select(exclude_exchanges=False) - - return etfs - - -@log_start_end(log=logger) -def get_exchanges_short_names() -> List: - """Load FD list of equity symbols.""" - shorts = get_all_exchange_short_names() - - return shorts diff --git a/openbb_terminal/terminal_helper.py b/openbb_terminal/terminal_helper.py deleted file mode 100644 index 9385475ecd7..00000000000 --- a/openbb_terminal/terminal_helper.py +++ /dev/null @@ -1,418 +0,0 @@ -"""Terminal helper""" - -__docformat__ = "numpy" - -import hashlib -import logging -import os -import sys -import webbrowser - -# IMPORTATION STANDARD -from contextlib import contextmanager -from typing import Any, Dict, List, Optional - -import matplotlib.pyplot as plt - -# IMPORTATION THIRDPARTY -from packaging import version - -# IMPORTATION INTERNAL -import openbb_terminal.core.session.local_model as Local -from openbb_terminal import thought_of_the_day as thought -from openbb_terminal.base_helpers import load_env_files -from openbb_terminal.core.config.paths import HIST_FILE_PATH, SETTINGS_ENV_FILE -from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.session.constants import BackendEnvironment -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.core.session.current_user import ( - get_current_user, - is_local, - set_preference, -) -from openbb_terminal.core.session.env_handler import write_to_dotenv -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -# pylint: disable=too-many-statements,no-member,too-many-branches,C0302 - -try: - __import__("git") -except ImportError: - WITH_GIT = False -else: - WITH_GIT = True -logger = logging.getLogger(__name__) - - -def print_goodbye(): - """Prints a goodbye message when quitting the terminal""" - # LEGACY GOODBYE MESSAGES - You'll live in our hearts forever. - # "An informed ape, is a strong ape." - # "Remember that stonks only go up." - # "Diamond hands." - # "Apes together strong." - # "This is our way." - # "Keep the spacesuit ape, we haven't reached the moon yet." - # "I am not a cat. I'm an ape." - # "We like the terminal." - # "...when offered a flight to the moon, nobody asks about what seat." - - text = """ -[param]Thank you for using the OpenBB Terminal and being part of this journey.[/param] - -We hope you'll find the new CLI as valuable as this. To stay tuned, sign up for our newsletter: [cmds]https://openbb.co/newsletter.[/] - -In the meantime, check out our other products: - -[bold]OpenBB Terminal Pro[/]: [cmds]https://openbb.co/products/pro[/cmds] -[bold]OpenBB Platform:[/] [cmds]https://openbb.co/products/platform[/cmds] -[bold]OpenBB Bot[/]: [cmds]https://openbb.co/products/bot[/cmds] - """ - console.print(text) - logger.info("END") - - -def sha256sum(filename): - h = hashlib.sha256() - b = bytearray(128 * 1024) - mv = memoryview(b) - with open(filename, "rb", buffering=0) as f: - for n in iter(lambda: f.readinto(mv), 0): - h.update(mv[:n]) - return h.hexdigest() - - -def open_openbb_documentation( # noqa: PLR0912 - path, - url="https://docs.openbb.co/terminal", - command=None, - arg_type="", -): - """Opens the documentation page based on your current location within the terminal. Make exceptions for menus - that are considered 'common' by adjusting the path accordingly.""" - if path == "/" and command is None: - path = "/usage/overview/structure-and-navigation" - command = "" - elif "keys" in path: - path = "/usage/data/api-keys" - command = "" - elif "settings" in path: - path = "/usage/overview/customizing-the-terminal" - command = "" - elif "featflags" in path: - path = "/usage/overview/customizing-the-terminal#feature-flags-menu" - command = "" - elif "sources" in path: - path = "/usage/usage/data/data-sources" - command = "" - elif "account" in path: - path = "/usage/hub" - command = "" - elif arg_type == "command": # user passed a command name - if command in ["settings", "featflags"]: - path = "/usage/overview/customizing-the-terminal" - command = "" - else: - path = f"/reference/{path}" - elif "askobb" in path: - path = "/usage/askobb-feature" - command = "" - elif arg_type == "menu": # user passed a menu name - if command in ["ta", "qa"]: - menu = path.split("/")[-2] - path = f"/menus/common/{menu}" - elif command == "stocks": - path = "/menus/stocks/introduction" - command = "" - elif command == "forecast": - command = "" - path = "/menus/forecast" - elif command == "crypto": - path = "/menus/crypto/introduction" - command = "" - - else: - path = f"/menus/{path}" - else: # user didn't pass argument and is in a menu - menu = path.split("/")[-2] - if menu == "crypto" and not command: - path = "/crypto/introduction" - if menu == "stocks": - path = "/stocks/introduction" - path = f"/menus/common/{menu}" if menu in ["ta", "qa"] else f"/menus/{path}" - - if command: - if command == "keys": - path = "/usage/data/api-keys" - command = "" - elif "settings" in path or "featflags" in path: - path = "/usage/overview/customizing-the-terminal" - command = "" - elif "sources" in path: - path = "/usage/data/data-sources" - command = "" - elif command in ["record", "stop", "exe"]: - path = "/usage/routines/introduction-to-routines" - command = "" - elif command == "sources": - path = "/usage/data/data-sources" - command = "" - elif command == "askobb": - path = "/usage/askobb-feature" - command = "" - elif command == "news": - path = "/usage/overview/commands-and-arguments#help-arguments" - command = "" - elif command in [ - "intro", - "about", - "support", - "survey", - "update", - "wiki", - ]: - path = "/usage" - command = "" - elif command in ["ta", "qa"]: - path = f"/menus/common/{command}" - command = "" - elif command == "stocks": - path = "/menus/stocks/introduction" - command = "" - elif command == "account": - path = "/usage/hub" - command = "" - elif command == "news": - path = "/usage/overview/commands-and-arguments#help-arguments" - command = "" - path += command - - full_url = f"{url}{path.replace('//', '/')}" - - if full_url[-1] == "/": - full_url = full_url[:-1] - - webbrowser.open(full_url) - - -def hide_splashscreen(): - """Hide the splashscreen on Windows bundles. - - `pyi_splash` is a PyInstaller "fake-package" that's used to communicate - with the splashscreen on Windows. - Sending the `close` signal to the splash screen is required. - The splash screen remains open until this function is called or the Python - program is terminated. - """ - try: - import pyi_splash # type: ignore # pylint: disable=import-outside-toplevel - - pyi_splash.update_text("Terminal Loaded!") - pyi_splash.close() - except Exception as e: - logger.info(e) - - -def is_auth_enabled() -> bool: - """Tell whether or not authentication is enabled. - - Returns - ------- - bool - If authentication is enabled - """ - # TODO: This function is a temporary way to block authentication - return get_current_system().ENABLE_AUTHENTICATION - - -def print_guest_block_msg(): - """Block guest users from using the terminal.""" - if is_local(): - console.print( - "[info]You are currently logged as a guest.[/info]\n" - "[info]Login to use this feature.[/info]\n\n" - "[info]If you don't have an account, you can create one here: [/info]" - f"[cmds]{BackendEnvironment.HUB_URL + 'register'}\n[/cmds]" - ) - - -def is_installer() -> bool: - """Tell whether or not it is a packaged version (Windows or Mac installer""" - return getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS") - - -def bootup(): - if sys.platform == "win32": - # Enable VT100 Escape Sequence for WINDOWS 10 Ver. 1607 - os.system("") # nosec # noqa: S605,S607 - # Hide splashscreen loader of the packaged app - if is_installer(): - hide_splashscreen() - - try: - if os.name == "nt": - # pylint: disable=E1101 - sys.stdin.reconfigure(encoding="utf-8") - # pylint: disable=E1101 - sys.stdout.reconfigure(encoding="utf-8") - except Exception as e: - logger.exception("Exception: %s", str(e)) - console.print(e, "\n") - - -def check_for_updates() -> None: - """Check if the latest version is running. - - Checks github for the latest release version and compares it to cfg.VERSION. - """ - # The commit has was commented out because the terminal was crashing due to git import for multiple users - # ({str(git.Repo('.').head.commit)[:7]}) - try: - r = request( - "https://api.github.com/repos/openbb-finance/openbbterminal/releases/latest" - ) - except Exception: - r = None - - if r is not None and r.status_code == 200: - latest_tag_name = r.json()["tag_name"] - latest_version = version.parse(latest_tag_name) - current_version = version.parse(get_current_system().VERSION) - - if check_valid_versions(latest_version, current_version): - if current_version == latest_version: - console.print("[green]You are using the latest stable version[/green]") - else: - console.print( - "[yellow]You are not using the latest stable version[/yellow]" - ) - if current_version < latest_version: - console.print( - "[yellow]Check for updates at https://my.openbb.co/app/terminal/download[/yellow]" - ) - - else: - console.print( - "[yellow]You are using an unreleased version[/yellow]" - ) - - else: - console.print("[red]You are using an unrecognized version.[/red]") - else: - console.print( - "[yellow]Unable to check for updates... " - + "Check your internet connection and try again...[/yellow]" - ) - console.print("\n") - - -def check_valid_versions( - latest_version: version.Version, - current_version: version.Version, -) -> bool: - if ( - not latest_version - or not current_version - or not isinstance(latest_version, version.Version) - or not isinstance(current_version, version.Version) - ): - return False - return True - - -def welcome_message(): - """Print the welcome message - - Prints first welcome message, help and a notification if updates are available. - """ - console.print(f"\nWelcome to OpenBB Terminal v{get_current_system().VERSION}") - - if get_current_user().preferences.ENABLE_THOUGHTS_DAY: - console.print("---------------------------------") - try: - thought.get_thought_of_the_day() - except Exception as e: - logger.exception("Exception: %s", str(e)) - console.print(e) - - -def reset(queue: Optional[List[str]] = None): - """Resets the terminal. Allows for checking code without quitting""" - console.print("resetting...") - logger.info("resetting") - plt.close("all") - plots_backend().close(reset=True) - load_env_files() - debug = get_current_system().DEBUG_MODE - dev = get_current_system().DEV_BACKEND - - try: - # save the current user - user_profile = get_current_user().profile - session: Dict[str, Any] = { - "access_token": user_profile.token, - "token_type": user_profile.token_type, - "uuid": user_profile.uuid, - "username": user_profile.username, - "remember": user_profile.remember, - } - - # remove the hub routines - if not is_local(): - Local.remove(get_current_user().preferences.USER_ROUTINES_DIRECTORY / "hub") - if not get_current_user().profile.remember: - Local.remove(HIST_FILE_PATH) - - # we clear all openbb_terminal modules from sys.modules - for module in list(sys.modules.keys()): - parts = module.split(".") - if parts[0] == "openbb_terminal": - del sys.modules[module] - - queue_list = ["/".join(queue) if len(queue) > 0 else ""] # type: ignore - # pylint: disable=import-outside-toplevel - # we run the terminal again - if is_local(): - from openbb_terminal.terminal_controller import main - - main(debug, dev, queue_list, module="") # type: ignore - else: - from openbb_terminal.core.session import session_controller - - session_controller.main(session, queue=queue_list) - - except Exception as e: - logger.exception("Exception: %s", str(e)) - console.print("Unfortunately, resetting wasn't possible!\n") - print_goodbye() - - -@contextmanager -def suppress_stdout(): - with open(os.devnull, "w") as devnull: - old_stdout = sys.stdout - old_stderr = sys.stderr - sys.stdout = devnull - sys.stderr = devnull - try: - yield - finally: - sys.stdout = old_stdout - sys.stderr = old_stderr - - -def first_time_user() -> bool: - """Whether a user is a first time user. A first time user is someone with an empty .env file. - If this is true, it also adds an env variable to make sure this does not run again. - - Returns - ------- - bool - Whether or not the user is a first time user - """ - if SETTINGS_ENV_FILE.stat().st_size == 0: - set_preference("PREVIOUS_USE", True) - write_to_dotenv("OPENBB_PREVIOUS_USE", "True") - return True - return False diff --git a/openbb_terminal/alternative/__init__.py b/openbb_terminal/tests/__init__.py similarity index 100% rename from openbb_terminal/alternative/__init__.py rename to openbb_terminal/tests/__init__.py diff --git a/openbb_terminal/thought_of_the_day.py b/openbb_terminal/thought_of_the_day.py deleted file mode 100644 index 54b81fd0cee..00000000000 --- a/openbb_terminal/thought_of_the_day.py +++ /dev/null @@ -1,142 +0,0 @@ -""" Thought of The Day """ - -from __future__ import annotations - -import random -import re - -from bs4 import BeautifulSoup - -from openbb_terminal.helper_funcs import request -from openbb_terminal.rich_config import console - -__docformat__ = "numpy" - - -class ThoughtOfTheDay: - """ThoughtOfTheDay class""" - - def __init__(self, urls: dict[str, str] | None = None): - """Constructor""" - - self.metadata: dict = {} - - if urls is None: - self.urls = { - "Marcus_Aurelius": "https://www.goodreads.com/author/quotes/17212.Marcus_Aurelius", - "Epictetus": "https://www.goodreads.com/author/quotes/13852.Epictetus", - "Seneca": "https://www.goodreads.com/author/quotes/4918776.Seneca", - "Marcus_Tullius_Cicero": "https://www.goodreads.com/author/quotes/13755.Marcus_Tullius_Cicero", - "Aristotle": "https://www.goodreads.com/author/quotes/2192.Aristotle", - "Plato": "https://www.goodreads.com/author/quotes/879.Plato", - "Pythagoras": "https://www.goodreads.com/author/quotes/203707.Pythagoras", - } - - return - - self.urls = urls - - def get_urls(self) -> dict: - """Getter method for URLs""" - return self.urls - - def get_metadata(self, author: str) -> dict: - """Loads metadata for a given author - - Parameters - ---------- - author : str - Author key - Marcus_Aurelius, Epictetus, Seneca, Marcus_Tullius_Cicero, Aristotle, Plato, Pythagoras - - Returns - ---------- - dict - Metadata dictionary that includes number of quotes, number of pages and first 30 quotes - """ - quotes_page = BeautifulSoup( - request(self.urls[author]).text, - "lxml", - ) - - find_navigation = quotes_page.find("em", {"class": "current"}).find_parent( - "div" - ) - - page_count = [ - a_page_ref.text.strip("\n") - for a_page_ref in find_navigation.find_all("a", href=True) - ] - - ret = {} - ret["pages"] = page_count[-2] - - find_count = quotes_page.find(string=re.compile("Showing 1-30 of")) - - quote_count = re.search(r"Showing 1-30 of (?P[\d,]+)", find_count) - - if quote_count: - ret["quoutes"] = quote_count.group("number") - - all_quotes = quotes_page.find_all("div", {"class": "quote"}) - - ret["quotes"] = [] - - for a_quote in all_quotes: - parsed_quote = {} - - parsed_quote = a_quote.find("div", {"class": "quoteText"}).text - - ret["quotes"].append(parsed_quote) - - return ret - - def quote_to_str(self, a_quote: str) -> str: - """Format a quote parsed from Goodreads into a string - - Parameters - ---------- - a_quote : str - A quote formatted by Goodreads - - Returns - ---------- - str - A string version of the quote - """ - ready = [] - prev = None - for a_line in a_quote: - if not prev: - ready.append(a_line) - prev = a_line - continue - - if a_line != "\n": - ready.append(a_line) - prev = a_line - continue - - if prev == " \n": - ready.pop() - prev = None - - return "".join(map(str, ready)) - - -def get_thought_of_the_day(): - """Pick a thought of the day""" - totd = ThoughtOfTheDay() - - quotes = [] - for an_author in totd.get_urls(): - metadata = totd.get_metadata(an_author) - quotes = quotes + metadata["quotes"] - - console.print("Thought of the day:") - console.print( - totd.quote_to_str( - quotes[random.randint(0, len(quotes) - 1)] # nosec # noqa: S311 - ) - ) - - console.print("\n") diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 5d6d1804b87..00000000000 --- a/poetry.lock +++ /dev/null @@ -1,10198 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. - -[[package]] -name = "absl-py" -version = "2.1.0" -description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py." -optional = true -python-versions = ">=3.7" -files = [ - {file = "absl-py-2.1.0.tar.gz", hash = "sha256:7820790efbb316739cde8b4e19357243fc3608a152024288513dd968d7d959ff"}, - {file = "absl_py-2.1.0-py3-none-any.whl", hash = "sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308"}, -] - -[[package]] -name = "adagio" -version = "0.2.4" -description = "The Dag IO Framework for Fugue projects" -optional = true -python-versions = ">=3.6" -files = [ - {file = "adagio-0.2.4-py3-none-any.whl", hash = "sha256:c6c4d812f629fc3141284a0b3cfe483731b28da3a1b18f3d5498695ff87dcc12"}, - {file = "adagio-0.2.4.tar.gz", hash = "sha256:e58abc4539184a65faf9956957d3787616bedeb1303ac5c9b1a201d8af6b87d7"}, -] - -[package.dependencies] -triad = ">=0.6.1" - -[[package]] -name = "aiodns" -version = "3.2.0" -description = "Simple DNS resolver for asyncio" -optional = false -python-versions = "*" -files = [ - {file = "aiodns-3.2.0-py3-none-any.whl", hash = "sha256:e443c0c27b07da3174a109fd9e736d69058d808f144d3c9d56dbd1776964c5f5"}, - {file = "aiodns-3.2.0.tar.gz", hash = "sha256:62869b23409349c21b072883ec8998316b234c9a9e36675756e8e317e8768f72"}, -] - -[package.dependencies] -pycares = ">=4.0.0" - -[[package]] -name = "aiofiles" -version = "22.1.0" -description = "File support for asyncio." -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "aiofiles-22.1.0-py3-none-any.whl", hash = "sha256:1142fa8e80dbae46bb6339573ad4c8c0841358f79c6eb50a493dceca14621bad"}, - {file = "aiofiles-22.1.0.tar.gz", hash = "sha256:9107f1ca0b2a5553987a94a3c9959fe5b491fdf731389aa5b7b1bd0733e32de6"}, -] - -[[package]] -name = "aiohttp" -version = "3.9.5" -description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, - {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, - {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, - {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, - {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, - {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, - {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, - {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, - {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, - {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, - {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, - {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, - {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, - {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, -] - -[package.dependencies] -aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} -attrs = ">=17.3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" - -[package.extras] -speedups = ["Brotli", "aiodns", "brotlicffi"] - -[[package]] -name = "aiosignal" -version = "1.3.1" -description = "aiosignal: a list of registered asynchronous callbacks" -optional = false -python-versions = ">=3.7" -files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, -] - -[package.dependencies] -frozenlist = ">=1.1.0" - -[[package]] -name = "aiosqlite" -version = "0.20.0" -description = "asyncio bridge to the standard sqlite3 module" -optional = false -python-versions = ">=3.8" -files = [ - {file = "aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6"}, - {file = "aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7"}, -] - -[package.dependencies] -typing_extensions = ">=4.0" - -[package.extras] -dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] -docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] - -[[package]] -name = "alabaster" -version = "0.7.13" -description = "A configurable sidebar-enabled Sphinx theme" -optional = false -python-versions = ">=3.6" -files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, -] - -[[package]] -name = "alpha-vantage" -version = "2.3.1" -description = "Python module to get stock data from the Alpha Vantage Api" -optional = false -python-versions = "*" -files = [ - {file = "alpha_vantage-2.3.1-py3-none-any.whl", hash = "sha256:5d05355febe6f0fafc4bd11dc6ce3504a02d9d3c105d60202539855c0f608661"}, - {file = "alpha_vantage-2.3.1.tar.gz", hash = "sha256:0ce76908c3e2a22f9bbdacead90195ec3a4fa41ef8ae7c69a4a2fc99459bfbec"}, -] - -[package.dependencies] -aiohttp = "*" -requests = "*" - -[[package]] -name = "altair" -version = "5.3.0" -description = "Vega-Altair: A declarative statistical visualization library for Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "altair-5.3.0-py3-none-any.whl", hash = "sha256:7084a1dab4d83c5e7e5246b92dc1b4451a6c68fd057f3716ee9d315c8980e59a"}, - {file = "altair-5.3.0.tar.gz", hash = "sha256:5a268b1a0983b23d8f9129f819f956174aa7aea2719ed55a52eba9979b9f6675"}, -] - -[package.dependencies] -jinja2 = "*" -jsonschema = ">=3.0" -numpy = "*" -packaging = "*" -pandas = ">=0.25" -toolz = "*" -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -all = ["altair-tiles (>=0.3.0)", "anywidget (>=0.9.0)", "pyarrow (>=11)", "vega-datasets (>=0.9.0)", "vegafusion[embed] (>=1.6.6)", "vl-convert-python (>=1.3.0)"] -dev = ["geopandas", "hatch", "ipython", "m2r", "mypy", "pandas-stubs", "pytest", "pytest-cov", "ruff (>=0.3.0)", "types-jsonschema", "types-setuptools"] -doc = ["docutils", "jinja2", "myst-parser", "numpydoc", "pillow (>=9,<10)", "pydata-sphinx-theme (>=0.14.1)", "scipy", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinxext-altair"] - -[[package]] -name = "altgraph" -version = "0.17.4" -description = "Python graph (network) package" -optional = true -python-versions = "*" -files = [ - {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, - {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, -] - -[[package]] -name = "ansiwrap" -version = "0.8.4" -description = "textwrap, but savvy to ANSI colors and styles" -optional = false -python-versions = "*" -files = [ - {file = "ansiwrap-0.8.4-py2.py3-none-any.whl", hash = "sha256:7b053567c88e1ad9eed030d3ac41b722125e4c1271c8a99ade797faff1f49fb1"}, - {file = "ansiwrap-0.8.4.zip", hash = "sha256:ca0c740734cde59bf919f8ff2c386f74f9a369818cdc60efe94893d01ea8d9b7"}, -] - -[package.dependencies] -textwrap3 = ">=0.9.2" - -[[package]] -name = "antlr4-python3-runtime" -version = "4.11.1" -description = "ANTLR 4.11.1 runtime for Python 3" -optional = true -python-versions = "*" -files = [ - {file = "antlr4-python3-runtime-4.11.1.tar.gz", hash = "sha256:a53de701312f9bdacc5258a6872cd6c62b90d3a90ae25e494026f76267333b60"}, - {file = "antlr4_python3_runtime-4.11.1-py3-none-any.whl", hash = "sha256:ff1954eda1ca9072c02bf500387d0c86cb549bef4dbb3b64f39468b547ec5f6b"}, -] - -[[package]] -name = "anyio" -version = "3.7.1" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.7" -files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, -] - -[package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] - -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = "*" -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - -[[package]] -name = "appnope" -version = "0.1.4" -description = "Disable App Nap on macOS >= 10.9" -optional = false -python-versions = ">=3.6" -files = [ - {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, - {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, -] - -[[package]] -name = "arch" -version = "5.6.0" -description = "ARCH for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "arch-5.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:478d049fb18e022670952792ebaa6b66acff77580c0f691497b706ce9192e941"}, - {file = "arch-5.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0da8a70e56759b469eeef52153e0a6eaf2e384c4f48427189433ea12fc8886a"}, - {file = "arch-5.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd3035fc54b9718f889455d4f8aa58fda3077d223cc722f95a9a3ecdb4c2fce"}, - {file = "arch-5.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c838e92dd60367c0e5063bc98521aa341d20d97af7adf911526d1c819eef7ee"}, - {file = "arch-5.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:7b1f78ad63b288014c3d453b29c9a1886e10a27eb53c5e58d55d8dd49b1a3d9a"}, - {file = "arch-5.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:05b232a45acc74930b8c700320b4a602431e1ae511bb2c081982ef6bde2fe118"}, - {file = "arch-5.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9ff873de28efd9af9e5a635c41745f783c40c5f771c6c322ace83164fefb0f9f"}, - {file = "arch-5.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b3aea5fb7b4c4828bd2c855a91f8cc53d547b2a106dde2deaf331d17a485b7"}, - {file = "arch-5.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee81437074d0aed1522dd09166fccb1275eb17f45ca9dce0c03529eecaa59c0d"}, - {file = "arch-5.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:c686e23b28e9bbea7db601686c5f9e1f34135a4ef909b64ef821c1323f532fbc"}, - {file = "arch-5.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:68758d5c0afcb38a7c284dd3ff1f563ed4e3d5307b41ce403471514f5e579285"}, - {file = "arch-5.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb6e6fb14f36ddcd02a95e1629a7d28fd0eb291c5af99c679c7893663ae7914e"}, - {file = "arch-5.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ae9aff6b8b3fc8759de6e439b2e8b17e5345c939f082f89582e33a60293cef4"}, - {file = "arch-5.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39e896c4ccc79aae6799d98ccac1186d951eedbf8d7c4cf836ac2a3883da7cbb"}, - {file = "arch-5.6.0-cp38-cp38-win32.whl", hash = "sha256:66a634dfbd03defcbe9696dc74bce77f02cafdfbb4e62910d484e5f07d2a1d61"}, - {file = "arch-5.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:e6253a88a6106ae381f220e2d55a99bb66893bb60b998bf1839c556ae5c10f69"}, - {file = "arch-5.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db9b12bdce62a4bd0431c2f13889b4e02ff89335061d0abac4d6589acdedabe2"}, - {file = "arch-5.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd7e3d0ec56a405ae3bf9645768fa02549b70c3c5a4589f5bd489eae21d91599"}, - {file = "arch-5.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0384c8287681bd10cef2064bb9017dbdc10ded382f2ab5effa788f0d6b43600"}, - {file = "arch-5.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6a7028c8d25f64c64366410a7164317e748435280e074e427ab7efac65a0d4b"}, - {file = "arch-5.6.0-cp39-cp39-win32.whl", hash = "sha256:6c604f082bc66144d51af6c31f7076ed768bc2f9e7eef0934c6689153ca8428c"}, - {file = "arch-5.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:84f56d139b42e8bfe6cc712b960a0cfd690b5bbf8bcb69e17713050ae6cba9d0"}, - {file = "arch-5.6.0.tar.gz", hash = "sha256:556b046010642e1ab4f837e26edd4f832ff33330a5c5a899ea1b26149ad7dc14"}, -] - -[package.dependencies] -numpy = ">=1.17" -pandas = ">=1.0" -property-cached = ">=1.6.4" -scipy = ">=1.3" -statsmodels = ">=0.11" - -[[package]] -name = "argon2-cffi" -version = "23.1.0" -description = "Argon2 for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, - {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, -] - -[package.dependencies] -argon2-cffi-bindings = "*" - -[package.extras] -dev = ["argon2-cffi[tests,typing]", "tox (>4)"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] -tests = ["hypothesis", "pytest"] -typing = ["mypy"] - -[[package]] -name = "argon2-cffi-bindings" -version = "21.2.0" -description = "Low-level CFFI bindings for Argon2" -optional = false -python-versions = ">=3.6" -files = [ - {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, - {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, -] - -[package.dependencies] -cffi = ">=1.0.1" - -[package.extras] -dev = ["cogapp", "pre-commit", "pytest", "wheel"] -tests = ["pytest"] - -[[package]] -name = "arrow" -version = "1.3.0" -description = "Better dates & times for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, - {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, -] - -[package.dependencies] -python-dateutil = ">=2.7.0" -types-python-dateutil = ">=2.8.10" - -[package.extras] -doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] -test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] - -[[package]] -name = "ascii-magic" -version = "1.6" -description = "Converts pictures into ASCII art" -optional = false -python-versions = ">=3.5" -files = [ - {file = "ascii_magic-1.6-py3-none-any.whl", hash = "sha256:937447d8677b7428856729c298c0264afd62fc2b8e7ff90c82000492cdc5f8d4"}, - {file = "ascii_magic-1.6.tar.gz", hash = "sha256:7da5518f7368e73f11e2151a0c060804aa149e267b369b7ee7653fbd7b046a51"}, -] - -[package.dependencies] -colorama = "*" -Pillow = "*" - -[[package]] -name = "astor" -version = "0.8.1" -description = "Read/rewrite/write Python ASTs" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -files = [ - {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, - {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, -] - -[[package]] -name = "astroid" -version = "2.15.8" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.7.2" -files = [ - {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, - {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, -] - -[package.dependencies] -lazy-object-proxy = ">=1.4.0" -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} -wrapt = {version = ">=1.11,<2", markers = "python_version < \"3.11\""} - -[[package]] -name = "astropy" -version = "5.2.2" -description = "Astronomy and astrophysics core library" -optional = true -python-versions = ">=3.8" -files = [ - {file = "astropy-5.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66522e897daf3766775c00ef5c63b69beb0eb359e1f45d18745d0f0ca7f29cc1"}, - {file = "astropy-5.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0ccf6f16cf7e520247ecc9d1a66dd4c3927fd60622203bdd1d06655ad81fa18f"}, - {file = "astropy-5.2.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3d0c37da922cdcb81e74437118fabd64171cbfefa06c7ea697a270e82a8164f2"}, - {file = "astropy-5.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04464e664a22382626ce9750ebe943b80a718dc8347134b9d138b63a2029f67a"}, - {file = "astropy-5.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f60cea0fa7cb6ebbd90373e48c07f5d459e95dfd6363f50e316e2db7755bead"}, - {file = "astropy-5.2.2-cp310-cp310-win32.whl", hash = "sha256:6c3abb2fa8ebaaad77875a02e664c1011f35bd0c0ef7d35a39b03c859de1129a"}, - {file = "astropy-5.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:185ade8c33cea34ba791b282e937686d98b4e205d4f343e686a4666efab2f6e7"}, - {file = "astropy-5.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f61c612e90e3dd3c075e99a61dedd53331c4577016c1d571aab00b95ca1731ab"}, - {file = "astropy-5.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3881e933ea870a27e5d6896443401fbf51e3b7e57c6356f333553f5ff0070c72"}, - {file = "astropy-5.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f210b5b4062030388437b9aca4bbf68f9063b2b27184006814a09fab41ac270e"}, - {file = "astropy-5.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e14b5a22f24ae5cf0404f21a4de135e26ca3c9cf55aefc5b0264a9ce24b53b0b"}, - {file = "astropy-5.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6768b3a670cdfff6c2416b3d7d1e4231839608299b32367e8b095959fc6733a6"}, - {file = "astropy-5.2.2-cp311-cp311-win32.whl", hash = "sha256:0aad85604cad40189b13d66bb46fb2a95df1a9095992071b31c3fa35b476fdbc"}, - {file = "astropy-5.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:ac944158794a88789a007892ad91db35da14f689da1ab37c33c8de770a27f717"}, - {file = "astropy-5.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6703860deecd384bba2d2e338f77a0e7b46672812d27ed15f95e8faaa89fcd35"}, - {file = "astropy-5.2.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:124ef2a9f9b1cdbc1a5d514f7e57538253bb67ad031215f5f5405fc4cd31a4cd"}, - {file = "astropy-5.2.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:800501cc626aef0780dfb66156619699e98cb48854ed710f1ae3708aaab79f6e"}, - {file = "astropy-5.2.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:22396592aa9b1653d37d552d3c52a8bb27ef072d077fad43b64faf841b1dcbf3"}, - {file = "astropy-5.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:093782b1f0177c3dd2c04181ec016d8e569bd9e862b48236e40b14e2a7399170"}, - {file = "astropy-5.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0c664f9194a4a3cece6215f651a9bc22c3cbd1f52dd450bd4d94eaf36f13c06c"}, - {file = "astropy-5.2.2-cp38-cp38-win32.whl", hash = "sha256:35ce00bb3dbc8bf7c842a0635354a5023cb64ae9c1925aa9b54629cf7fed2abe"}, - {file = "astropy-5.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:8304b590b20f9c161db85d5eb65d4c6323b3370a17c96ae163b18a0071cbd68a"}, - {file = "astropy-5.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:855748c2f1aedee5d770dfec8334109f1bcd1c1cee97f5915d3e888f43c04acf"}, - {file = "astropy-5.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ef9acc55c5fd70c7c78370389e79fb044321e531ac1facb7bddeef89d3132e3"}, - {file = "astropy-5.2.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f30b5d153b9d119783b96b948a3e0c4eb668820c06d2e8ba72f6ea989e4af5c1"}, - {file = "astropy-5.2.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:530e6911a54a42e9f15b1a75dc3c699be3946c0b6ffdcfdcf4e14ae5fcfcd236"}, - {file = "astropy-5.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae3b383ac84fe6765e275f897f4010cc6afe6933607b7468561414dffdc4d915"}, - {file = "astropy-5.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b00a4cd49f8264a338b0020717bff104fbcca800bd50bf0a415d952078258a39"}, - {file = "astropy-5.2.2-cp39-cp39-win32.whl", hash = "sha256:b7167b9965ebd78b7c9da7e98a943381b25e23d041bd304ec2e35e8ec811cefc"}, - {file = "astropy-5.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:df81b8f23c5e906d799b47d2d8462707c745df38cafae0cd6674ef09e9a41789"}, - {file = "astropy-5.2.2.tar.gz", hash = "sha256:e6a9e34716bda5945788353c63f0644721ee7e5447d16b1cdcb58c48a96b0d9c"}, -] - -[package.dependencies] -numpy = ">=1.20" -packaging = ">=19.0" -pyerfa = ">=2.0" -PyYAML = ">=3.13" - -[package.extras] -all = ["asdf (>=2.10.0)", "beautifulsoup4", "bleach", "bottleneck", "certifi", "dask[array]", "fsspec[http] (>=2022.8.2)", "h5py", "html5lib", "ipython (>=4.2)", "jplephem", "matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "mpmath", "pandas", "pyarrow (>=5.0.0)", "pytest (>=7.0)", "pytz", "s3fs (>=2022.8.2)", "scipy (>=1.5)", "sortedcontainers", "typing-extensions (>=3.10.0.1)"] -docs = ["Jinja2 (>=3.0)", "matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "pytest (>=7.0)", "scipy (>=1.3)", "sphinx", "sphinx-astropy (>=1.6)", "sphinx-changelog (>=1.2.0)"] -recommended = ["matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "scipy (>=1.5)"] -test = ["pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist"] -test-all = ["coverage[toml]", "ipython (>=4.2)", "objgraph", "pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist", "sgp4 (>=2.3)", "skyfield (>=1.20)"] - -[[package]] -name = "asttokens" -version = "2.4.1" -description = "Annotate AST trees with source code positions" -optional = false -python-versions = "*" -files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, -] - -[package.dependencies] -six = ">=1.12.0" - -[package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] - -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - -[[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, -] - -[[package]] -name = "attrs" -version = "21.4.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] - -[package.extras] -dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] -docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] -tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] -tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] - -[[package]] -name = "babel" -version = "2.14.0" -description = "Internationalization utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, -] - -[package.dependencies] -pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} - -[package.extras] -dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] - -[[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -optional = false -python-versions = "*" -files = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] - -[[package]] -name = "backoff" -version = "2.2.1" -description = "Function decoration for backoff and retry" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, - {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, -] - -[[package]] -name = "backports-zoneinfo" -version = "0.2.1" -description = "Backport of the standard library zoneinfo module" -optional = false -python-versions = ">=3.6" -files = [ - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, - {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, - {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, -] - -[package.extras] -tzdata = ["tzdata"] - -[[package]] -name = "bandit" -version = "1.7.8" -description = "Security oriented static analyser for python code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "bandit-1.7.8-py3-none-any.whl", hash = "sha256:509f7af645bc0cd8fd4587abc1a038fc795636671ee8204d502b933aee44f381"}, - {file = "bandit-1.7.8.tar.gz", hash = "sha256:36de50f720856ab24a24dbaa5fee2c66050ed97c1477e0a1159deab1775eab6b"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - -[package.extras] -baseline = ["GitPython (>=3.1.30)"] -sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] -toml = ["tomli (>=1.1.0)"] -yaml = ["PyYAML"] - -[[package]] -name = "base58" -version = "2.1.1" -description = "Base58 and Base58Check implementation." -optional = false -python-versions = ">=3.5" -files = [ - {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, - {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, -] - -[package.extras] -tests = ["PyHamcrest (>=2.0.2)", "mypy", "pytest (>=4.6)", "pytest-benchmark", "pytest-cov", "pytest-flake8"] - -[[package]] -name = "beautifulsoup4" -version = "4.12.3" -description = "Screen-scraping library" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, -] - -[package.dependencies] -soupsieve = ">1.2" - -[package.extras] -cchardet = ["cchardet"] -chardet = ["chardet"] -charset-normalizer = ["charset-normalizer"] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -name = "black" -version = "24.4.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"}, - {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"}, - {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"}, - {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"}, - {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"}, - {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"}, - {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"}, - {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"}, - {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"}, - {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"}, - {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"}, - {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"}, - {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"}, - {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"}, - {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"}, - {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"}, - {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"}, - {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"}, - {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"}, - {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"}, - {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"}, - {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "bleach" -version = "6.1.0" -description = "An easy safelist-based HTML-sanitizing tool." -optional = false -python-versions = ">=3.8" -files = [ - {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, - {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, -] - -[package.dependencies] -six = ">=1.9.0" -webencodings = "*" - -[package.extras] -css = ["tinycss2 (>=1.1.0,<1.3)"] - -[[package]] -name = "blinker" -version = "1.7.0" -description = "Fast, simple object-to-object and broadcast signaling" -optional = false -python-versions = ">=3.8" -files = [ - {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, - {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, -] - -[[package]] -name = "brotli" -version = "1.1.0" -description = "Python bindings for the Brotli compression library" -optional = true -python-versions = "*" -files = [ - {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, - {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, - {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, - {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, - {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, - {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, - {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, - {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, - {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, - {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, - {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, - {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, - {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, - {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, - {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, - {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, - {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, -] - -[[package]] -name = "brotlicffi" -version = "1.1.0.0" -description = "Python CFFI bindings to the Brotli library" -optional = true -python-versions = ">=3.7" -files = [ - {file = "brotlicffi-1.1.0.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9b7ae6bd1a3f0df532b6d67ff674099a96d22bc0948955cb338488c31bfb8851"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19ffc919fa4fc6ace69286e0a23b3789b4219058313cf9b45625016bf7ff996b"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9feb210d932ffe7798ee62e6145d3a757eb6233aa9a4e7db78dd3690d7755814"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84763dbdef5dd5c24b75597a77e1b30c66604725707565188ba54bab4f114820"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-win32.whl", hash = "sha256:1b12b50e07c3911e1efa3a8971543e7648100713d4e0971b13631cce22c587eb"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:994a4f0681bb6c6c3b0925530a1926b7a189d878e6e5e38fae8efa47c5d9c613"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2e4aeb0bd2540cb91b069dbdd54d458da8c4334ceaf2d25df2f4af576d6766ca"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b7b0033b0d37bb33009fb2fef73310e432e76f688af76c156b3594389d81391"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54a07bb2374a1eba8ebb52b6fafffa2afd3c4df85ddd38fcc0511f2bb387c2a8"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7901a7dc4b88f1c1475de59ae9be59799db1007b7d059817948d8e4f12e24e35"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce01c7316aebc7fce59da734286148b1d1b9455f89cf2c8a4dfce7d41db55c2d"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:246f1d1a90279bb6069de3de8d75a8856e073b8ff0b09dcca18ccc14cec85979"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc4bc5d82bc56ebd8b514fb8350cfac4627d6b0743382e46d033976a5f80fab6"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c26ecb14386a44b118ce36e546ce307f4810bc9598a6e6cb4f7fca725ae7e6"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca72968ae4eaf6470498d5c2887073f7efe3b1e7d7ec8be11a06a79cc810e990"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:add0de5b9ad9e9aa293c3aa4e9deb2b61e99ad6c1634e01d01d98c03e6a354cc"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b6068e0f3769992d6b622a1cd2e7835eae3cf8d9da123d7f51ca9c1e9c333e5"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8557a8559509b61e65083f8782329188a250102372576093c88930c875a69838"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a7ae37e5d79c5bdfb5b4b99f2715a6035e6c5bf538c3746abc8e26694f92f33"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391151ec86bb1c683835980f4816272a87eaddc46bb91cbf44f62228b84d8cca"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2f3711be9290f0453de8eed5275d93d286abe26b08ab4a35d7452caa1fef532f"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a807d760763e398bbf2c6394ae9da5815901aa93ee0a37bca5efe78d4ee3171"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa8ca0623b26c94fccc3a1fdd895be1743b838f3917300506d04aa3346fd2a14"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3de0cf28a53a3238b252aca9fed1593e9d36c1d116748013339f0949bfc84112"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6be5ec0e88a4925c91f3dea2bb0013b3a2accda6f77238f76a34a1ea532a1cb0"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d9eb71bb1085d996244439154387266fd23d6ad37161f6f52f1cd41dd95a3808"}, - {file = "brotlicffi-1.1.0.0.tar.gz", hash = "sha256:b77827a689905143f87915310b93b273ab17888fd43ef350d4832c4a71083c13"}, -] - -[package.dependencies] -cffi = ">=1.0.0" - -[[package]] -name = "bs4" -version = "0.0.1" -description = "Dummy package for Beautiful Soup" -optional = false -python-versions = "*" -files = [ - {file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"}, -] - -[package.dependencies] -beautifulsoup4 = "*" - -[[package]] -name = "bt" -version = "0.2.9" -description = "A flexible backtesting framework for Python" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "bt-0.2.9-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:5654e5639ffd0778714d567ae14fb06d485e43becb720a0dc5a38b405251485e"}, - {file = "bt-0.2.9-cp36-cp36m-win_amd64.whl", hash = "sha256:5d6dd92fd6aa1efbade3efee980add709fc591a8b007643016f2e0cbf7372daf"}, - {file = "bt-0.2.9-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:637fd917f57d0302b17968025650be7d8b2b6da32c2c7daea9ae48f6a66cbb8c"}, - {file = "bt-0.2.9-cp37-cp37m-win_amd64.whl", hash = "sha256:55a9cf1ca3aa2c425a48fc1e8d324cd5959d4d189e221f7744c1c6e30149c61b"}, - {file = "bt-0.2.9-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5dbf1ced108a6f2086b927dcf17ecd1deff7d98266d7f52ab3ff43be4a349ec3"}, - {file = "bt-0.2.9-cp38-cp38-win_amd64.whl", hash = "sha256:e77a56adc7bcac285df2c70e714b8724fc92fa1beb8de4b1fd991ab3862f84b0"}, - {file = "bt-0.2.9-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a1aed6e0ee3fa3b6f5bcef1f26d29e2a96c8329ea54894362014e20012de053b"}, - {file = "bt-0.2.9-cp39-cp39-win_amd64.whl", hash = "sha256:399bedbf48026899e85536ea8cbd21459461346638ae20f439a09bc75fa131e9"}, - {file = "bt-0.2.9.tar.gz", hash = "sha256:d162d71aaaaf7265a848d1fc0040f503add32dac2f9f3127bece0d74c22efb9b"}, -] - -[package.dependencies] -ffn = ">=0.3.5" -pyprind = ">=2.11" - -[package.extras] -dev = ["black (>=20.8b1)", "codecov", "coverage", "cython (>=0.25)", "ffn (>=0.3.5)", "flake8", "flake8-black", "future", "matplotlib (>=2)", "mock", "nose", "numpy (>=1)", "pandas (>=0.19)", "pyprind (>=2.11)"] - -[[package]] -name = "cachetools" -version = "5.3.3" -description = "Extensible memoizing collections and decorators" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, - {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, -] - -[[package]] -name = "catboost" -version = "1.2.5" -description = "CatBoost Python Package" -optional = true -python-versions = "*" -files = [ - {file = "catboost-1.2.5-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d266bcd87e7afa8c8c4cae1dbdb66f9487368ee2c1d2bbcefda0cbff7cb48ac4"}, - {file = "catboost-1.2.5-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4143449dae1efc2752cbde2537b90611f36716e4e1e6827fc6856d07de16b431"}, - {file = "catboost-1.2.5-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:a92da61e95919b03d611045f0f3373799deec6f8192d7d1211714a21ff0da65e"}, - {file = "catboost-1.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:ad6ae9387759ca121042db60f4c73014027cb160a9dde1f52d353ca18b471e7c"}, - {file = "catboost-1.2.5-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:68dc17e6850f1ad8256fbbfbfac2c26b7be291c78b0961b135adf3aa6ed503bf"}, - {file = "catboost-1.2.5-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b6e1617b57deca52c8880ec2439d0fb514d7e9f78479bae8dbfbd2f95bb3b28e"}, - {file = "catboost-1.2.5-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:9e0aac17d1a25e0f67770ba7362c6275db611ebb5dc6179daed84f55c3db976c"}, - {file = "catboost-1.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:901bf56e43334ce42f45b5c62fab1d8a14d54160b6da0645c798b7b7ad415da1"}, - {file = "catboost-1.2.5-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:82a321ba006abdeae9beb6e53c2d407b0eaaeadb4c1b56de1344f693e7512d08"}, - {file = "catboost-1.2.5-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:4180fe03d110acf44d09df7e3fd8e1ef62bfd3ac22ce90185332f52e24e96ac9"}, - {file = "catboost-1.2.5-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:e1925714d40a2a0d9fd086c33f2a668610c07630201527018f3f17a3d530c464"}, - {file = "catboost-1.2.5-cp312-cp312-win_amd64.whl", hash = "sha256:f13acbcf2f3639d3d06808428db3207c7d776314041619cc32578927ec752221"}, - {file = "catboost-1.2.5-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:1d8b06333495a90192618ddc4b0ea15186a35a53f55b7743e4c9758de1bb785d"}, - {file = "catboost-1.2.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:87a556dfdad702d958b2f7bb1b6fc8bee4b1bf9bf3e7736aac3f70cb9522d53a"}, - {file = "catboost-1.2.5-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:d0db06d11dd9446bcb1cd1417bced0c4f956abe3f50f72df79f1b2b6789a6bde"}, - {file = "catboost-1.2.5-cp37-cp37m-win_amd64.whl", hash = "sha256:a1149bedba20fff940ece5ddd8e56420ebe26850b23533dfc6e5e4994049d220"}, - {file = "catboost-1.2.5-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:6e3a9bd84c3a92f7cab0a9c3451df21d930f4ebb4716f85513b4feac2b0ef209"}, - {file = "catboost-1.2.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:51acfea3bf141d25e3bc43e8dfc505920a8e701cb06c8d4164661a118e3e1178"}, - {file = "catboost-1.2.5-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:6acfc79e2be02b34caa26a339ace52c5991b1d9862dcb94f5ced1bd9ca9a8c18"}, - {file = "catboost-1.2.5-cp38-cp38-win_amd64.whl", hash = "sha256:dabd1ea1bda91f72792b7f348d0301e637f98251fc4d50b62ffc872721997f47"}, - {file = "catboost-1.2.5-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:ed9521ce617f370dd7f8214f6b5ea2cdb2bfb47a2be0fb19a03b7c9d4399b4d1"}, - {file = "catboost-1.2.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:6cd81cb508031cb5feccdecb6f309fc9562b7114bc82fa06c17ffdbdb079198c"}, - {file = "catboost-1.2.5-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:86e05596a5330de7a8376f344079c3333fd3254e4300549654e343703f7315b0"}, - {file = "catboost-1.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:c75f57b1df9893d67ed8f04c32d5e8fe3d591ee873a94a2ff8279e32549397b0"}, - {file = "catboost-1.2.5.tar.gz", hash = "sha256:12c31650fa55944e9ab5863d4eef194533914920c358b52a7a7d7b0b855cba9f"}, -] - -[package.dependencies] -graphviz = "*" -matplotlib = "*" -numpy = ">=1.16.0" -pandas = ">=0.24" -plotly = "*" -scipy = "*" -six = "*" - -[package.extras] -widget = ["ipython", "ipywidgets (>=7.0,<9.0)", "traitlets"] - -[[package]] -name = "cattrs" -version = "23.1.2" -description = "Composable complex class support for attrs and dataclasses." -optional = true -python-versions = ">=3.7" -files = [ - {file = "cattrs-23.1.2-py3-none-any.whl", hash = "sha256:b2bb14311ac17bed0d58785e5a60f022e5431aca3932e3fc5cc8ed8639de50a4"}, - {file = "cattrs-23.1.2.tar.gz", hash = "sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657"}, -] - -[package.dependencies] -attrs = ">=20" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -typing_extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} - -[package.extras] -bson = ["pymongo (>=4.2.0,<5.0.0)"] -cbor2 = ["cbor2 (>=5.4.6,<6.0.0)"] -msgpack = ["msgpack (>=1.0.2,<2.0.0)"] -orjson = ["orjson (>=3.5.2,<4.0.0)"] -pyyaml = ["PyYAML (>=6.0,<7.0)"] -tomlkit = ["tomlkit (>=0.11.4,<0.12.0)"] -ujson = ["ujson (>=5.4.0,<6.0.0)"] - -[[package]] -name = "ccxt" -version = "4.3.2" -description = "A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges" -optional = false -python-versions = "*" -files = [ - {file = "ccxt-4.3.2-py2.py3-none-any.whl", hash = "sha256:63f99e173be9f8f95042932db5c008a6c83845978542f3015f83b7fdc939a1e2"}, - {file = "ccxt-4.3.2.tar.gz", hash = "sha256:af3de8ed05c9dba192b0db9b1f1387a1c5ae5c5fb602630d48ef90a9d7949b1d"}, -] - -[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" -setuptools = ">=60.9.0" -typing-extensions = ">=4.4.0" -yarl = {version = ">=1.7.2", markers = "python_version >= \"3.5.2\""} - -[package.extras] -qa = ["ruff (==0.0.292)", "tox (>=4.8.0)"] -type = ["mypy (==1.6.1)"] - -[[package]] -name = "certifi" -version = "2024.2.2" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, -] - -[[package]] -name = "cffi" -version = "1.16.0" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "clarabel" -version = "0.7.1" -description = "Clarabel Conic Interior Point Solver for Rust / Python" -optional = true -python-versions = ">=3.7" -files = [ - {file = "clarabel-0.7.1-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:32980b578842d17abe3d277df2d31a6b8bff3facf33b169c74d59b2ada10b61b"}, - {file = "clarabel-0.7.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b4d414b9db040effa186f9e048de2c7d3e38b6edcadaaa2432a52b6451ba9999"}, - {file = "clarabel-0.7.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec0fde90a467e07010176b0bd159ab8cb11c59c8ff0a09580af1ac77d7861971"}, - {file = "clarabel-0.7.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:754e2681eb74e7f6fc35612ae58219d76819a0bc1b67771752b6f5425f72c1f8"}, - {file = "clarabel-0.7.1-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33283d190f1e2a3b8559c928f0eb0d1fefc45aad334fd0566cdf5eaa94606a13"}, - {file = "clarabel-0.7.1-cp37-abi3-win32.whl", hash = "sha256:519f9b1daba08f909c106da8b5fd52937cfbc4ba27086c4cbd9728d168cfbc5c"}, - {file = "clarabel-0.7.1-cp37-abi3-win_amd64.whl", hash = "sha256:33faf5a02465eb5f7f7380d892ce3cf1d293a3efefcea18d637c618f012f5f36"}, - {file = "clarabel-0.7.1.tar.gz", hash = "sha256:a30ab135f475c5bc78329fd7df575291155e6055bcaf02124eb610b8ad176396"}, -] - -[package.dependencies] -numpy = "*" -scipy = "*" - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "cloudpickle" -version = "3.0.0" -description = "Pickler class to extend the standard pickle.Pickler functionality" -optional = true -python-versions = ">=3.8" -files = [ - {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, - {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, -] - -[[package]] -name = "cmdstanpy" -version = "1.2.2" -description = "Python interface to CmdStan" -optional = true -python-versions = ">=3.8" -files = [ - {file = "cmdstanpy-1.2.2-py3-none-any.whl", hash = "sha256:b18cf61f9cae6babf4b26ab06a2c3572a64fc7781017ededdfa073430f8e29eb"}, - {file = "cmdstanpy-1.2.2.tar.gz", hash = "sha256:09bcadd54a582090d722ba3e76d516db696275ab861ef71805195d0df2dd34a9"}, -] - -[package.dependencies] -numpy = ">=1.21" -pandas = "*" -stanio = ">=0.4.0,<2.0.0" -tqdm = "*" - -[package.extras] -all = ["xarray"] -docs = ["ipykernel", "ipython", "ipywidgets", "matplotlib", "nbsphinx", "pydata-sphinx-theme (<0.9)", "sphinx (>5,<6)", "sphinx-copybutton", "xarray"] -test = ["flake8", "mypy", "pylint", "pytest", "pytest-cov", "pytest-order", "xarray"] - -[[package]] -name = "codespell" -version = "2.2.6" -description = "Codespell" -optional = false -python-versions = ">=3.8" -files = [ - {file = "codespell-2.2.6-py3-none-any.whl", hash = "sha256:9ee9a3e5df0990604013ac2a9f22fa8e57669c827124a2e961fe8a1da4cacc07"}, - {file = "codespell-2.2.6.tar.gz", hash = "sha256:a8c65d8eb3faa03deabab6b3bbe798bea72e1799c7e9e955d57eca4096abcff9"}, -] - -[package.extras] -dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"] -hard-encoding-detection = ["chardet"] -toml = ["tomli"] -types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "comm" -version = "0.2.2" -description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." -optional = false -python-versions = ">=3.8" -files = [ - {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, - {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, -] - -[package.dependencies] -traitlets = ">=4" - -[package.extras] -test = ["pytest"] - -[[package]] -name = "commonmark" -version = "0.9.1" -description = "Python parser for the CommonMark Markdown spec" -optional = false -python-versions = "*" -files = [ - {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, - {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, -] - -[package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] - -[[package]] -name = "contourpy" -version = "1.1.1" -description = "Python library for calculating contours of 2D quadrilateral grids" -optional = false -python-versions = ">=3.8" -files = [ - {file = "contourpy-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b"}, - {file = "contourpy-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d"}, - {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae"}, - {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916"}, - {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6aec19457617ef468ff091669cca01fa7ea557b12b59a7908b9474bb9674cf0"}, - {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1"}, - {file = "contourpy-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d"}, - {file = "contourpy-1.1.1-cp310-cp310-win32.whl", hash = "sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431"}, - {file = "contourpy-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb"}, - {file = "contourpy-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2"}, - {file = "contourpy-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b"}, - {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b"}, - {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532"}, - {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e"}, - {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5"}, - {file = "contourpy-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62"}, - {file = "contourpy-1.1.1-cp311-cp311-win32.whl", hash = "sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33"}, - {file = "contourpy-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45"}, - {file = "contourpy-1.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a"}, - {file = "contourpy-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e"}, - {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442"}, - {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8"}, - {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1eaac5257a8f8a047248d60e8f9315c6cff58f7803971170d952555ef6344a7"}, - {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf"}, - {file = "contourpy-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d"}, - {file = "contourpy-1.1.1-cp312-cp312-win32.whl", hash = "sha256:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6"}, - {file = "contourpy-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f44d78b61740e4e8c71db1cf1fd56d9050a4747681c59ec1094750a658ceb970"}, - {file = "contourpy-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d"}, - {file = "contourpy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9"}, - {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217"}, - {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684"}, - {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce"}, - {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8"}, - {file = "contourpy-1.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f29fb0b3f1217dfe9362ec55440d0743fe868497359f2cf93293f4b2701b8251"}, - {file = "contourpy-1.1.1-cp38-cp38-win32.whl", hash = "sha256:f9dc7f933975367251c1b34da882c4f0e0b2e24bb35dc906d2f598a40b72bfc7"}, - {file = "contourpy-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9"}, - {file = "contourpy-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba"}, - {file = "contourpy-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34"}, - {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887"}, - {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718"}, - {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f"}, - {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efe0fab26d598e1ec07d72cf03eaeeba8e42b4ecf6b9ccb5a356fde60ff08b85"}, - {file = "contourpy-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f08e469821a5e4751c97fcd34bcb586bc243c39c2e39321822060ba902eac49e"}, - {file = "contourpy-1.1.1-cp39-cp39-win32.whl", hash = "sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0"}, - {file = "contourpy-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887"}, - {file = "contourpy-1.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e"}, - {file = "contourpy-1.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3"}, - {file = "contourpy-1.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23"}, - {file = "contourpy-1.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb"}, - {file = "contourpy-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163"}, - {file = "contourpy-1.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c"}, - {file = "contourpy-1.1.1.tar.gz", hash = "sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab"}, -] - -[package.dependencies] -numpy = {version = ">=1.16,<2.0", markers = "python_version <= \"3.11\""} - -[package.extras] -bokeh = ["bokeh", "selenium"] -docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.4.1)", "types-Pillow"] -test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "wurlitzer"] - -[[package]] -name = "convertdate" -version = "2.4.0" -description = "Converts between Gregorian dates and other calendar systems" -optional = false -python-versions = "<4,>=3.7" -files = [ - {file = "convertdate-2.4.0-py3-none-any.whl", hash = "sha256:fcffe3a67522172648cf03b0c3757cfd079726fe5ae04ce29989ad3958039e4e"}, - {file = "convertdate-2.4.0.tar.gz", hash = "sha256:770c6b2195544d3e451e230b3f1c9b121ed02680b877f896306a04cf6f26b48f"}, -] - -[package.dependencies] -pymeeus = ">=0.3.13,<=1" - -[package.extras] -dev = ["black", "build", "isort", "pylint"] -docs = ["myst-parser", "sphinx", "sphinx-rtd-theme"] -tests = ["coverage"] - -[[package]] -name = "coreforecast" -version = "0.0.8" -description = "Fast implementations of common forecasting routines" -optional = true -python-versions = ">=3.8" -files = [ - {file = "coreforecast-0.0.8-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:599ca1dd0d8f41396c47107ecc9ce777769d6572f8c2a4494d9688a2e4424895"}, - {file = "coreforecast-0.0.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e5c2a1983465563ca2d3078f6ba449214e20eef5056013a62893cd6958cd7497"}, - {file = "coreforecast-0.0.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f6ca35496eaada7cbb4b3f07dde2a9612b9c8ae849f2e02dc54e1c528c42248"}, - {file = "coreforecast-0.0.8-py3-none-win_amd64.whl", hash = "sha256:155104b5077fa6b44fc23c3761cca24343cfda0203574a2b7104cc7ee609e5a7"}, - {file = "coreforecast-0.0.8.tar.gz", hash = "sha256:c352306fadf1faa52c68170abbd9ef6b5cb95fec8a0667206e4efe0f397b7f9b"}, -] - -[package.dependencies] -importlib_resources = {version = "*", markers = "python_version < \"3.10\""} -numpy = ">=1.20.0" - -[[package]] -name = "coverage" -version = "7.4.4" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, - {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, - {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, - {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, - {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, - {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, - {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, - {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, - {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, - {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, - {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, - {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, - {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, - {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, - {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, - {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, - {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, - {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, - {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, - {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, - {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, - {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, - {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, - {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, - {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, - {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, - {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, - {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "cryptography" -version = "42.0.5" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = ">=3.7" -files = [ - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, - {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, - {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, - {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, - {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, - {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, - {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, -] - -[package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] -nox = ["nox"] -pep8test = ["check-sdist", "click", "mypy", "ruff"] -sdist = ["build"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] -test-randomorder = ["pytest-randomly"] - -[[package]] -name = "cssselect" -version = "1.2.0" -description = "cssselect parses CSS3 Selectors and translates them to XPath 1.0" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cssselect-1.2.0-py2.py3-none-any.whl", hash = "sha256:da1885f0c10b60c03ed5eccbb6b68d6eff248d91976fcde348f395d54c9fd35e"}, - {file = "cssselect-1.2.0.tar.gz", hash = "sha256:666b19839cfaddb9ce9d36bfe4c969132c647b92fc9088c4e23f786b30f1b3dc"}, -] - -[[package]] -name = "cssselect2" -version = "0.7.0" -description = "CSS selectors for Python ElementTree" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cssselect2-0.7.0-py3-none-any.whl", hash = "sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969"}, - {file = "cssselect2-0.7.0.tar.gz", hash = "sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a"}, -] - -[package.dependencies] -tinycss2 = "*" -webencodings = "*" - -[package.extras] -doc = ["sphinx", "sphinx_rtd_theme"] -test = ["flake8", "isort", "pytest"] - -[[package]] -name = "cvxpy" -version = "1.4.3" -description = "A domain-specific language for modeling convex optimization problems in Python." -optional = true -python-versions = ">=3.8" -files = [ - {file = "cvxpy-1.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f4dc744a6f38328b0511cd57cfd81a517c0568d3b6e994d6dda3f309a1ce47cf"}, - {file = "cvxpy-1.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c32199a26d889e74d70bc39f0369680cb7d2625e7c47c42483ca166d37122c2"}, - {file = "cvxpy-1.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a4fed75c1714409fd5125269935a68bc3f57c522623a5d7eab68623a19f3a6f"}, - {file = "cvxpy-1.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8198e390490a0543caad428fc0d8a5c0914052c22630277d00bfdc7a770c4b11"}, - {file = "cvxpy-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:bea83a4a829d7197c307badbbe2f05d6e7aed85ed63badd800b89534b33b38de"}, - {file = "cvxpy-1.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:443313eca702750a7a2878e841de23cc5111085f4069e842d027ebc5c98c10ac"}, - {file = "cvxpy-1.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ae3d4ec202dc446206d8c6eae83353827842dc0e232f4e4f0d588fb2d59705a"}, - {file = "cvxpy-1.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8ee4d0d1ecb2e09e41a31192ea103c7c1f626204925fb7f6590d11471cb357"}, - {file = "cvxpy-1.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeb6f60608d4716ced43105cbeb0df2a787583f51bfbf9465fea5c435f8456ca"}, - {file = "cvxpy-1.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:243d0315140a7572cd4ec2e9bf13c1e407627d78dc5f9491abfa9adc64569268"}, - {file = "cvxpy-1.4.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:69ee039b43e425ffc0cb5581c2befee571bee2b007d5a119fa17695f908d861e"}, - {file = "cvxpy-1.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:502c590099a4faeee28247a3d139d618a94ec6d2986dc7cc0db2ac614660ddc5"}, - {file = "cvxpy-1.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57a803bb786ab43da0c0a4fbbb0b2438fd13a4b38b68f8f16474a214a78b0823"}, - {file = "cvxpy-1.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:331cc160e66b7271b734b8ad0b65456d32445e00c397c35b7f2b105091ecfa84"}, - {file = "cvxpy-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:4b4d46fd521b755a6abd57384b2355db28d7165a6d90118278ebd7553e4ba70b"}, - {file = "cvxpy-1.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:80dde991c27d4e3b91597a00b85d460a9db03b484ba6a3b940647e324ad30110"}, - {file = "cvxpy-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06091d3278c65ef996306b5a85a7848055331ef390262f2cf354811f43ddea68"}, - {file = "cvxpy-1.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e16bf04f75266c61ee469a0a3b1364c1e1e87d47dc47fedcdc43435c74dd97f3"}, - {file = "cvxpy-1.4.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14882db05fa36cab94262b7e9574c43f87301ab025daefdabaa7659f2623e731"}, - {file = "cvxpy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:f094da24c46e6b0b936369463d483755eafbb13fd6a8d8d1ca962987d02eb014"}, - {file = "cvxpy-1.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b8d652877989ddc28099a367fb7a4bb771d54895e3e1167c835ee3f91606bd13"}, - {file = "cvxpy-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25d3ac5953effaa5c72ab8d261dbe94d8a1947de0324a7e30053ef3b0cee853e"}, - {file = "cvxpy-1.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d612c7bdff73a730c11d6094540705ca9c8312d9ebb6af75c09337123dd66b2"}, - {file = "cvxpy-1.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1475fd4c4778067d5ec03b35fe714d6a4074e1e213262a8bddbd9b638e004419"}, - {file = "cvxpy-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:8d30081ef5e906cc6352fc6760f462a6dfcb5150a5a26e21da942b6ae0759293"}, - {file = "cvxpy-1.4.3.tar.gz", hash = "sha256:b1b078c8c05923ad128e7d814b0be1c337ac05262a78b757a8e6f957648ad953"}, -] - -[package.dependencies] -clarabel = ">=0.5.0" -ecos = ">=2" -numpy = ">=1.15" -osqp = ">=0.6.2" -pybind11 = "*" -scipy = ">=1.1.0" -scs = ">=3.0" - -[package.extras] -cbc = ["cylp (>=0.91.5)"] -clarabel = ["clarabel"] -cvxopt = ["cvxopt"] -diffcp = ["diffcp"] -glop = ["ortools (>=9.5,<9.8)"] -glpk = ["cvxopt"] -glpk-mi = ["cvxopt"] -gurobi = ["gurobipy"] -highs = ["scipy (>=1.6.1)"] -mosek = ["Mosek"] -pdlp = ["ortools (>=9.5,<9.8)"] -piqp = ["piqp"] -proxqp = ["proxsuite"] -scip = ["PySCIPOpt"] -scipy = ["scipy"] -scs = ["setuptools (>65.5.1)"] -xpress = ["xpress"] - -[[package]] -name = "cycler" -version = "0.12.1" -description = "Composable style cycles" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, - {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, -] - -[package.extras] -docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] -tests = ["pytest", "pytest-cov", "pytest-xdist"] - -[[package]] -name = "cython" -version = "3.0.10" -description = "The Cython compiler for writing C extensions in the Python language." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -files = [ - {file = "Cython-3.0.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e876272548d73583e90babda94c1299537006cad7a34e515a06c51b41f8657aa"}, - {file = "Cython-3.0.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adc377aa33c3309191e617bf675fdbb51ca727acb9dc1aa23fc698d8121f7e23"}, - {file = "Cython-3.0.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:401aba1869a57aba2922ccb656a6320447e55ace42709b504c2f8e8b166f46e1"}, - {file = "Cython-3.0.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:541fbe725d6534a90b93f8c577eb70924d664b227a4631b90a6e0506d1469591"}, - {file = "Cython-3.0.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:86998b01f6a6d48398df8467292c7637e57f7e3a2ca68655367f13f66fed7734"}, - {file = "Cython-3.0.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d092c0ddba7e9e530a5c5be4ac06db8360258acc27675d1fc86294a5dc8994c5"}, - {file = "Cython-3.0.10-cp310-cp310-win32.whl", hash = "sha256:3cffb666e649dba23810732497442fb339ee67ba4e0be1f0579991e83fcc2436"}, - {file = "Cython-3.0.10-cp310-cp310-win_amd64.whl", hash = "sha256:9ea31184c7b3a728ef1f81fccb161d8948c05aa86c79f63b74fb6f3ddec860ec"}, - {file = "Cython-3.0.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:051069638abfb076900b0c2bcb6facf545655b3f429e80dd14365192074af5a4"}, - {file = "Cython-3.0.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:712760879600907189c7d0d346851525545484e13cd8b787e94bfd293da8ccf0"}, - {file = "Cython-3.0.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38d40fa1324ac47c04483d151f5e092406a147eac88a18aec789cf01c089c3f2"}, - {file = "Cython-3.0.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bd49a3a9fdff65446a3e1c2bfc0ec85c6ce4c3cad27cd4ad7ba150a62b7fb59"}, - {file = "Cython-3.0.10-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e8df79b596633b8295eaa48b1157d796775c2bb078f32267d32f3001b687f2fd"}, - {file = "Cython-3.0.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bcc9795990e525c192bc5c0775e441d7d56d7a7d02210451e9e13c0448dba51b"}, - {file = "Cython-3.0.10-cp311-cp311-win32.whl", hash = "sha256:09f2000041db482cad3bfce94e1fa3a4c82b0e57390a164c02566cbbda8c4f12"}, - {file = "Cython-3.0.10-cp311-cp311-win_amd64.whl", hash = "sha256:3919a55ec9b6c7db6f68a004c21c05ed540c40dbe459ced5d801d5a1f326a053"}, - {file = "Cython-3.0.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8f2864ab5fcd27a346f0b50f901ebeb8f60b25a60a575ccfd982e7f3e9674914"}, - {file = "Cython-3.0.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:407840c56385b9c085826fe300213e0e76ba15d1d47daf4b58569078ecb94446"}, - {file = "Cython-3.0.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a036d00caa73550a3a976432ef21c1e3fa12637e1616aab32caded35331ae96"}, - {file = "Cython-3.0.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc6a0e7e23a96dec3f3c9d39690d4281beabd5297855140d0d30855f950275e"}, - {file = "Cython-3.0.10-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a5e14a8c6a8157d2b0cdc2e8e3444905d20a0e78e19d2a097e89fb8b04b51f6b"}, - {file = "Cython-3.0.10-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f8a2b8fa0fd8358bccb5f3304be563c4750aae175100463d212d5ea0ec74cbe0"}, - {file = "Cython-3.0.10-cp312-cp312-win32.whl", hash = "sha256:2d29e617fd23cf4b83afe8f93f2966566c9f565918ad1e86a4502fe825cc0a79"}, - {file = "Cython-3.0.10-cp312-cp312-win_amd64.whl", hash = "sha256:6c5af936940a38c300977b81598d9c0901158f220a58c177820e17e1774f1cf1"}, - {file = "Cython-3.0.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:5f465443917d5c0f69825fca3b52b64c74ac3de0143b1fff6db8ba5b48c9fb4a"}, - {file = "Cython-3.0.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fadb84193c25641973666e583df8df4e27c52cdc05ddce7c6f6510d690ba34a"}, - {file = "Cython-3.0.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fa9e7786083b6aa61594c16979d621b62e61fcd9c2edd4761641b95c7fb34b2"}, - {file = "Cython-3.0.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4780d0f98ce28191c4d841c4358b5d5e79d96520650910cd59904123821c52d"}, - {file = "Cython-3.0.10-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:32fbad02d1189be75eb96456d9c73f5548078e5338d8fa153ecb0115b6ee279f"}, - {file = "Cython-3.0.10-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:90e2f514fc753b55245351305a399463103ec18666150bb1c36779b9862388e9"}, - {file = "Cython-3.0.10-cp36-cp36m-win32.whl", hash = "sha256:a9c976e9ec429539a4367cb4b24d15a1e46b925976f4341143f49f5f161171f5"}, - {file = "Cython-3.0.10-cp36-cp36m-win_amd64.whl", hash = "sha256:a9bb402674788a7f4061aeef8057632ec440123e74ed0fb425308a59afdfa10e"}, - {file = "Cython-3.0.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:206e803598010ecc3813db8748ed685f7beeca6c413f982df9f8a505fce56563"}, - {file = "Cython-3.0.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15b6d397f4ee5ad54e373589522af37935a32863f1b23fa8c6922adf833e28e2"}, - {file = "Cython-3.0.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a181144c2f893ed8e6a994d43d0b96300bc99873f21e3b7334ca26c61c37b680"}, - {file = "Cython-3.0.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74b700d6a793113d03fb54b63bdbadba6365379424bac7c0470605672769260"}, - {file = "Cython-3.0.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:076e9fd4e0ca33c5fa00a7479180dbfb62f17fe928e2909f82da814536e96d2b"}, - {file = "Cython-3.0.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:269f06e6961e8591d56e30b46e1a51b6ccb42cab04c29fa3b30d3e8723485fb4"}, - {file = "Cython-3.0.10-cp37-cp37m-win32.whl", hash = "sha256:d4e83a8ceff7af60064da4ccfce0ac82372544dd5392f1b350c34f1b04d0fae6"}, - {file = "Cython-3.0.10-cp37-cp37m-win_amd64.whl", hash = "sha256:40fac59c3a7fbcd9c25aea64c342c890a5e2270ce64a1525e840807800167799"}, - {file = "Cython-3.0.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f43a58bf2434870d2fc42ac2e9ff8138c9e00c6251468de279d93fa279e9ba3b"}, - {file = "Cython-3.0.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e9a885ec63d3955a08cefc4eec39fefa9fe14989c6e5e2382bd4aeb6bdb9bc3"}, - {file = "Cython-3.0.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acfbe0fff364d54906058fc61f2393f38cd7fa07d344d80923937b87e339adcf"}, - {file = "Cython-3.0.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8adcde00a8a88fab27509b558cd8c2959ab0c70c65d3814cfea8c68b83fa6dcd"}, - {file = "Cython-3.0.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2c9c1e3e78909488f3b16fabae02308423fa6369ed96ab1e250807d344cfffd7"}, - {file = "Cython-3.0.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fc6e0faf5b57523b073f0cdefadcaef3a51235d519a0594865925cadb3aeadf0"}, - {file = "Cython-3.0.10-cp38-cp38-win32.whl", hash = "sha256:35f6ede7c74024ed1982832ae61c9fad7cf60cc3f5b8c6a63bb34e38bc291936"}, - {file = "Cython-3.0.10-cp38-cp38-win_amd64.whl", hash = "sha256:950c0c7b770d2a7cec74fb6f5ccc321d0b51d151f48c075c0d0db635a60ba1b5"}, - {file = "Cython-3.0.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:077b61ee789e48700e25d4a16daa4258b8e65167136e457174df400cf9b4feab"}, - {file = "Cython-3.0.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f1f8bba9d8f37c0cffc934792b4ac7c42d0891077127c11deebe9fa0a0f7e4"}, - {file = "Cython-3.0.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:651a15a8534ebfb9b58cb0b87c269c70984b6f9c88bfe65e4f635f0e3f07dfcd"}, - {file = "Cython-3.0.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d10fc9aa82e5e53a0b7fd118f9771199cddac8feb4a6d8350b7d4109085aa775"}, - {file = "Cython-3.0.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4f610964ab252a83e573a427e28b103e2f1dd3c23bee54f32319f9e73c3c5499"}, - {file = "Cython-3.0.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c9c4c4f3ab8f8c02817b0e16e8fa7b8cc880f76e9b63fe9c010e60c1a6c2b13"}, - {file = "Cython-3.0.10-cp39-cp39-win32.whl", hash = "sha256:0bac3ccdd4e03924028220c62ae3529e17efa8ca7e9df9330de95de02f582b26"}, - {file = "Cython-3.0.10-cp39-cp39-win_amd64.whl", hash = "sha256:81f356c1c8c0885b8435bfc468025f545c5d764aa9c75ab662616dd1193c331e"}, - {file = "Cython-3.0.10-py2.py3-none-any.whl", hash = "sha256:fcbb679c0b43514d591577fd0d20021c55c240ca9ccafbdb82d3fb95e5edfee2"}, - {file = "Cython-3.0.10.tar.gz", hash = "sha256:dcc96739331fb854dcf503f94607576cfe8488066c61ca50dfd55836f132de99"}, -] - -[[package]] -name = "dateparser" -version = "1.2.0" -description = "Date parsing library designed to parse dates from HTML pages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "dateparser-1.2.0-py2.py3-none-any.whl", hash = "sha256:0b21ad96534e562920a0083e97fd45fa959882d4162acc358705144520a35830"}, - {file = "dateparser-1.2.0.tar.gz", hash = "sha256:7975b43a4222283e0ae15be7b4999d08c9a70e2d378ac87385b1ccf2cffbbb30"}, -] - -[package.dependencies] -python-dateutil = "*" -pytz = "*" -regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27" -tzlocal = "*" - -[package.extras] -calendars = ["convertdate", "hijri-converter"] -fasttext = ["fasttext"] -langdetect = ["langdetect"] - -[[package]] -name = "datetime" -version = "5.5" -description = "This package provides a DateTime data type, as known from Zope. Unless you need to communicate with Zope APIs, you're probably better off using Python's built-in datetime module." -optional = false -python-versions = ">=3.7" -files = [ - {file = "DateTime-5.5-py3-none-any.whl", hash = "sha256:0abf6c51cb4ba7cee775ca46ccc727f3afdde463be28dbbe8803631fefd4a120"}, - {file = "DateTime-5.5.tar.gz", hash = "sha256:21ec6331f87a7fcb57bd7c59e8a68bfffe6fcbf5acdbbc7b356d6a9a020191d3"}, -] - -[package.dependencies] -pytz = "*" -"zope.interface" = "*" - -[[package]] -name = "debugpy" -version = "1.8.1" -description = "An implementation of the Debug Adapter Protocol for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, - {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, - {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, - {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, - {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, - {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, - {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, - {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, - {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, - {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, - {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, - {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, - {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, - {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, - {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, - {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, - {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, - {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, - {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, - {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, - {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, - {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, -] - -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -optional = false -python-versions = ">=3.5" -files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] - -[[package]] -name = "defusedxml" -version = "0.7.1" -description = "XML bomb protection for Python stdlib modules" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, - {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, -] - -[[package]] -name = "detecta" -version = "0.0.5" -description = "Detect events in data" -optional = false -python-versions = ">=3.6" -files = [ - {file = "detecta-0.0.5-py3-none-any.whl", hash = "sha256:dbaf1938e5d785386c904034e2553824e2aa31793e967984ca65983aca06d23e"}, - {file = "detecta-0.0.5.tar.gz", hash = "sha256:d2ea7d13dfbbc994d6ce385a7f8dc0a85fe675a8a8e712a64ec56e54c40603ed"}, -] - -[[package]] -name = "dill" -version = "0.3.8" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - -[[package]] -name = "distlib" -version = "0.3.8" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, -] - -[[package]] -name = "dnspython" -version = "2.6.1" -description = "DNS toolkit" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, - {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, -] - -[package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=41)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=0.9.25)"] -idna = ["idna (>=3.6)"] -trio = ["trio (>=0.23)"] -wmi = ["wmi (>=1.5.1)"] - -[[package]] -name = "docstring-parser" -version = "0.15" -description = "Parse Python docstrings in reST, Google and Numpydoc format" -optional = true -python-versions = ">=3.6,<4.0" -files = [ - {file = "docstring_parser-0.15-py3-none-any.whl", hash = "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9"}, - {file = "docstring_parser-0.15.tar.gz", hash = "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682"}, -] - -[[package]] -name = "docstring-to-markdown" -version = "0.15" -description = "On the fly conversion of Python docstrings to markdown" -optional = true -python-versions = ">=3.6" -files = [ - {file = "docstring-to-markdown-0.15.tar.gz", hash = "sha256:e146114d9c50c181b1d25505054a8d0f7a476837f0da2c19f07e06eaed52b73d"}, - {file = "docstring_to_markdown-0.15-py3-none-any.whl", hash = "sha256:27afb3faedba81e34c33521c32bbd258d7fbb79eedf7d29bc4e81080e854aec0"}, -] - -[[package]] -name = "docutils" -version = "0.17.1" -description = "Docutils -- Python Documentation Utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] - -[[package]] -name = "ecdsa" -version = "0.19.0" -description = "ECDSA cryptographic signature library (pure python)" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.6" -files = [ - {file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"}, - {file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"}, -] - -[package.dependencies] -six = ">=1.9.0" - -[package.extras] -gmpy = ["gmpy"] -gmpy2 = ["gmpy2"] - -[[package]] -name = "ecos" -version = "2.0.13" -description = "This is the Python package for ECOS: Embedded Cone Solver. See Github page for more information." -optional = true -python-versions = "*" -files = [ - {file = "ecos-2.0.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a1c1acf33b70f8657c25f07ec8d7b59bb01dbad39f072fa61fc956c2166ed979"}, - {file = "ecos-2.0.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ea88ee2c94192004d6be9c55a3c79f184eaba3bbf31474229045a1b0a8a1536"}, - {file = "ecos-2.0.13-cp310-cp310-win_amd64.whl", hash = "sha256:df8ae7fce79be9e5f79f0511c51a4824795de5154847fabe1a0288bc2ea349d3"}, - {file = "ecos-2.0.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88dd628bc6e77a069165fa5f50340e2856795c28e00e3fce213a04d7c41c584a"}, - {file = "ecos-2.0.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b2c969c7e22fd8a1d1cd0a90f4325d90572da23e2e923b0da6138ce62503d0"}, - {file = "ecos-2.0.13-cp311-cp311-win_amd64.whl", hash = "sha256:936890fb85a186360a5c8f228dd19acb760e234b38c598d0b46ab29644e31dfc"}, - {file = "ecos-2.0.13-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62ed497ab56017f1d7264eb56223826a984462b1d84fb850d10f0bec3490877d"}, - {file = "ecos-2.0.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf2b1384012bee9e58e5a2373905d3644f74a0ea000b307a239366fe7850c29c"}, - {file = "ecos-2.0.13-cp312-cp312-win_amd64.whl", hash = "sha256:2c1ea09069e32185912506f946bb6d1f144841ba1d1cd0217c67f72cbdf7a8fd"}, - {file = "ecos-2.0.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9f4a76a3e1165359e1704ec6b1b89d487858ec0d838d62a7268133d88221914"}, - {file = "ecos-2.0.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3c2d4e0d3ada1a619ddd62fbf48ccbe9b738fdbef119945fe2a05566d03b85a"}, - {file = "ecos-2.0.13-cp37-cp37m-win_amd64.whl", hash = "sha256:84c72e1e5ffa41cd38352dcf0a8c25418f5bf04ed76a576db0daaf9a69f5568f"}, - {file = "ecos-2.0.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1979f1f17ec7f1a0fc45964d02d762393f9f427d965fe8a893e7b1476a9023c3"}, - {file = "ecos-2.0.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:059e6c29c89f47a490353e4f9336e96350a5102a97e1d8a2aaff796bcbe50058"}, - {file = "ecos-2.0.13-cp38-cp38-win_amd64.whl", hash = "sha256:30c7d0cce6c830da5b9ea25af0d47b203255639524eb4d03d1331c600958c834"}, - {file = "ecos-2.0.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ba42c15f1d79eb2ada532e9781b4aeb3ed84b1c7e38239ba4d6502c6a092d5b1"}, - {file = "ecos-2.0.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32fb33185f6dd94a1c798bc481eb86c9f4e832efec91f6ab0584e2fc26fd375e"}, - {file = "ecos-2.0.13-cp39-cp39-win_amd64.whl", hash = "sha256:68995ab12d363576dddb2d1f91ead3b9c8a8ca61f29000f0b1daef1b4e7b5b64"}, - {file = "ecos-2.0.13.tar.gz", hash = "sha256:f2a9dc108ade7faf6f6f4fad245f4714b7293c8767d2a351ead59428a94a98b9"}, -] - -[package.dependencies] -numpy = ">=1.6" -scipy = ">=0.9" - -[[package]] -name = "entrypoints" -version = "0.4" -description = "Discover and load entry points from installed packages." -optional = false -python-versions = ">=3.6" -files = [ - {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, - {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, -] - -[[package]] -name = "ephem" -version = "4.1.5" -description = "Compute positions of the planets and stars" -optional = true -python-versions = "*" -files = [ - {file = "ephem-4.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0796dbcd24f76af0e81c22e1f709b42873ef81d2c4dfa962f8d346f11489785d"}, - {file = "ephem-4.1.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0266cf69d594bd94034bd13c18dbcef13b301acd7357d7cf7d1bb8acaf7f00b4"}, - {file = "ephem-4.1.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3db8d53a37c772e1b132f5ed3535027d73e8dca4ead99a7563f09c4308996b63"}, - {file = "ephem-4.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c6e7523d8856caa4ac53db1988693ce7a516fb25c1cd3c74a3472f951691c0e"}, - {file = "ephem-4.1.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:203bd3f00c49012bd07eb2627bcf9538f608b3431cb8053a7d3115e1df396312"}, - {file = "ephem-4.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7ddf15c319a83fb5dadff58bb6e5fbcaff09338f5c2a1b2c7b1445b87002b5"}, - {file = "ephem-4.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:13f800e03aad215ae6a4217122e64d0ce90e914574f8b5afa381d81dad0eecaa"}, - {file = "ephem-4.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83921b341b4f4725e59c1463b9613f3fd3991392e17904170b146f5d6945afcb"}, - {file = "ephem-4.1.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6219412800138f489733cb3a8b4fab0a8b3ef2dd5ec143aff35950dca9a71c36"}, - {file = "ephem-4.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b95bf8c92a5eae665d63371440b93778b2508ad448907d12f72ed6ed08ea476c"}, - {file = "ephem-4.1.5-cp310-cp310-win32.whl", hash = "sha256:afefe022448f09c3e15472bd3ad30fa4824f8c4a9de65216b9952b9dfcec8750"}, - {file = "ephem-4.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:53df72aa3e2f1359ed56587b1930d0030fec030db71744564a83c10ea54316a7"}, - {file = "ephem-4.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b3e385d8447d908be6dd8c53c4452fee7d61040c9b6bb5a60195b57c85545ca6"}, - {file = "ephem-4.1.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:be6c4ced150d79ab6b5ff3bb7545fee26cf7f4f64ccf476a14850a63bbf125e7"}, - {file = "ephem-4.1.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c8a70e36632e88227c4ba8184fd91dbd32d0140f799202b5fb8510508f4c8e4"}, - {file = "ephem-4.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ee2a4966167a0300db2c9155e4b64fc1a6bb67ca869659d4c0342869c44d4f3"}, - {file = "ephem-4.1.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b45b9b15e65c7f26c1342b70e06e49bd83a9c527affe9de0fb5953a3a25a0cda"}, - {file = "ephem-4.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e21a8120569f315ce181f2e654528cd10c18cb92382248de90574effed57d227"}, - {file = "ephem-4.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:864d6aba1699f26e7768ed424d5218396300dce6d8deccb1c97164a424343675"}, - {file = "ephem-4.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8f8f5dd5b1343dc77ea891ab423677a631fd7b820bbbeac7ec53ee332e8a4ca5"}, - {file = "ephem-4.1.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:8a2347ad88aad026dff6f00b931bd2b0d6b4f184bf65ffd4b62f557526bf335d"}, - {file = "ephem-4.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5496ebc21b2873e986836ad0756818bee80ed6deee58e1167a9876e4a64f0bbf"}, - {file = "ephem-4.1.5-cp311-cp311-win32.whl", hash = "sha256:f319ca58ee2ee27c18e42657ad8d3d1b0251dd369c9fd60af45b2bc2c42ded0a"}, - {file = "ephem-4.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:cc6a49fd3250cf67305230da962d779632b13da2a8fae5c383e973fa113baa97"}, - {file = "ephem-4.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:51c17c62b031ec246546aa482d98e9a5573405385eb7adf7154f5a1af309ac8a"}, - {file = "ephem-4.1.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ff1436219d2dad1433c5f0a61f3a947ab6d4c622cc6a90d7cb5c2e28a8669fba"}, - {file = "ephem-4.1.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9495d515a6f49bd7a90f58320af1a95e4c3ab89307fbf31045cf05570f779eaf"}, - {file = "ephem-4.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7952e8b54c771be6a58ce9aca91d729bcdace2293c68fe825df3433995aedafc"}, - {file = "ephem-4.1.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e99ba9fbd5c136fc18b94fe5e709a7c4a19f13ed1c6a16bdec448cbd7d1bcb1"}, - {file = "ephem-4.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d2922e6cb66434e9edd3764b103b6bdd394a701bd39395c46f0a7751b9829ee"}, - {file = "ephem-4.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:692f8dea9e91447c6406e9ee7001075d5794948677f5f8ee4601c146509b15b1"}, - {file = "ephem-4.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7122f46a0ce2c2551360bf43b693a3f1f543c0aa6e736b2513bc3203df27df7c"}, - {file = "ephem-4.1.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a68dd823d823ace3ecd94b3cf926d51ae8aa88ce248159850a8db45547720843"}, - {file = "ephem-4.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cf745e4f02455d6c0f1d85a61206c420ee2c909b392266109e2da2d3a8c023db"}, - {file = "ephem-4.1.5-cp312-cp312-win32.whl", hash = "sha256:e1772852072727e848b5fe035ce7d9dc4662c1baffbd9e4cd7467ecf384db1d9"}, - {file = "ephem-4.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:fd984e38c5078be8cb881ed75c69b2204a1d7fd2230571fc92112edfc5362bf6"}, - {file = "ephem-4.1.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:740a5950b31ac9a7ca40a95b7cfb1678e747e860f77ac0030984c10dc5464e7f"}, - {file = "ephem-4.1.5-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf5b786ed28e7d00704d2cf3862dec75223790ed800c285a91acfa538b1755c"}, - {file = "ephem-4.1.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87e5be99ebc3bfa1dc70f31ba737dcdf9f6219570fa9a5729d0ff60134c7dc43"}, - {file = "ephem-4.1.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:261f82bd821cfbb8a056bb834da06d19d3afd3eb735c8cd549f775e45caef502"}, - {file = "ephem-4.1.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc612e5d1829f64a8db55d8b2566bef4ce479c1bbee110fedf3197170635f70c"}, - {file = "ephem-4.1.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9fa8ee5d77a8a27a21d39142ee7c6332915b9e79a416a941cbc6f3db64c32cfe"}, - {file = "ephem-4.1.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c109219a4728281dd4e8b1e9f449900d7a63e9400ef43c85151750d4725b0c9e"}, - {file = "ephem-4.1.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:cf81a3bfa15d84871c4f87ecbf92280184ce96619cacdfe55f761d510a7f81db"}, - {file = "ephem-4.1.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:39dcc1b00882a2782771b12731a943706da8990c89be75caa294f0574cca661f"}, - {file = "ephem-4.1.5-cp36-cp36m-win32.whl", hash = "sha256:7b6384a032897d3e47da92ccc3348b74517c0dcf478a63b8cdedf69c177bccd2"}, - {file = "ephem-4.1.5-cp36-cp36m-win_amd64.whl", hash = "sha256:3bea9cb5a4a98d9c51430a395c4ad4f6307402fa7d3ffbe1bb28f7508e094dd3"}, - {file = "ephem-4.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cac6ed1664e7db22f3aadd8bc6b7afed191b8d5ce3aab9a00d719890a7c344d2"}, - {file = "ephem-4.1.5-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02316f8ebb3f82273bc5fbb2970fd98bfb56ed8349197aff0f56c9b5fa04e47a"}, - {file = "ephem-4.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b044d4e3b11953b0543cbd10468bf84afaa840d165c6effbffff3c15d721234"}, - {file = "ephem-4.1.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4a69b27eb82d7c0dccdd54e11ad27f3cadcddb02169a570ac7864dbaa9a10486"}, - {file = "ephem-4.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b01734c27ce38705418191322fd7bd0372d9709d326263991b1c7e1bc75a59f"}, - {file = "ephem-4.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bcae4b30c4a7474cfc102610f23a4a114196c8d15165cbc0551fb0d542a768eb"}, - {file = "ephem-4.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6fde92b6cc7837c141506a09f721f983e31b32785a9680f4d075be9763dc931b"}, - {file = "ephem-4.1.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:0849f522d1ad4daac5407307be786a54283599cfe87f3fcfc677153db45d7e22"}, - {file = "ephem-4.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:82025d4a6d5f6d76a1f45b4eb940030be93e38a3ac3c589990172beac53c854e"}, - {file = "ephem-4.1.5-cp37-cp37m-win32.whl", hash = "sha256:d7c446228a8756fa39876701abf34d516cd9b49b867d52c2f661f1cf1d927335"}, - {file = "ephem-4.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:b1f509901dcf2b95cc175be39c90eb457e081f8ed9762f4aae74f45e092c7f8b"}, - {file = "ephem-4.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8d1ca9cec961115a6197faee6ae2231bc76f9a7090bfdf759c810718c7715db9"}, - {file = "ephem-4.1.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3f95e89ab21e5214dbadfd41f9c94ce538bcd4411c18400cc638a00df728d67c"}, - {file = "ephem-4.1.5-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21e8239ec63c9c6a1ab4d8c578bf6b34c114a3754b80c2e02ce07de827925353"}, - {file = "ephem-4.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:972ff0942ddf0f4ac5124bd974608b8371b22e84ab5ed49b4634455331f41987"}, - {file = "ephem-4.1.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd85a8b5b20020e791273f9cfd80510e4f647c362e5a3b7aec2abcb604d8d02a"}, - {file = "ephem-4.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:543ddd9e6bf573c24c1ccf30257ec655d8be00e28279fbdadbc774927d949663"}, - {file = "ephem-4.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:beea8cccaf184cd2a16c091b0ce92293f1128e49fd43e3c81e79c9f8d051cea6"}, - {file = "ephem-4.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4f53899fbcdd14790b7a61aa70218337e1c6aabc26af99d2485ed6d98fec8cee"}, - {file = "ephem-4.1.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3733d751c015d28bc043d543c47cee8d0a699a8330d3d34c9afdbd7e96310842"}, - {file = "ephem-4.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a7c904e6df8511bfad2a3119d2ba2333d94953d0d212b31725f4f749267f21cc"}, - {file = "ephem-4.1.5-cp38-cp38-win32.whl", hash = "sha256:3b46aa3f576d528a9dd4357b18c3e59308d0a1e537e45fe91df3753fa63100bf"}, - {file = "ephem-4.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:37751e9bdb10ae109a2e7d7353911470ee5752f5caee4bbe0d764a39cdd57a5a"}, - {file = "ephem-4.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fd76c85313b44e4a9b2e8a14f8e5ffbf43541b3cd023d623664f1dca8c8bf407"}, - {file = "ephem-4.1.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b56f805aa26818e7efa46c2bf98417f677e4dc9ae1b6ef278747d823c7d4df6"}, - {file = "ephem-4.1.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e9c15b5e5386cbee128737465ad7b042f57603c3e75bdbab84cd71b416127cf"}, - {file = "ephem-4.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0f7f4a9de84a4ef8ce77ee980a963bb7725791caa3cb3389490ed35be1d06e8"}, - {file = "ephem-4.1.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b38d0033f7e4ba6e2f5c9ecfc0827d619796ff182d98cc0adb62c6bce701fe0"}, - {file = "ephem-4.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb55fc94512d0c46e610fa67af5fcad51e28aa2b30f7e88ef4cb38c7a443e81a"}, - {file = "ephem-4.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c4183bc20f5933c9fad3d754f9a9ac75dc9ea0d8c6a7faff63de5d28d6bf26d9"}, - {file = "ephem-4.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e067f98b6cd9da55a5de9ce7b2e1222574006b4c3083363eda507ce97ce9867b"}, - {file = "ephem-4.1.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b5cf0e29657001482f3ed8eeaf1757c67b8d8fdc9c90e57b8d544fd1e7e4d6b3"}, - {file = "ephem-4.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5058cfcf36643b11198d54266137d7db8476d66b2c9ff5e4e35638fc3254a2d"}, - {file = "ephem-4.1.5-cp39-cp39-win32.whl", hash = "sha256:c1eed128e5f6e551bc50cfaa87395cef7f03d65537fe4e8502704d200279e3b2"}, - {file = "ephem-4.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:f4ff7153ac577cb336e08fdc7a76ac91185b2af5dae9ba3ae9d0e87af3ac35c3"}, - {file = "ephem-4.1.5.tar.gz", hash = "sha256:0c64a8aa401574c75942045b9af70d1656e14c5366151c0cbb400cbeedc2362a"}, -] - -[[package]] -name = "et-xmlfile" -version = "1.1.0" -description = "An implementation of lxml.xmlfile for the standard library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, - {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "exchange-calendars" -version = "4.2.8" -description = "Calendars for securities exchanges" -optional = false -python-versions = "~=3.8" -files = [ - {file = "exchange_calendars-4.2.8-py3-none-any.whl", hash = "sha256:3695afd0608c6507ce3016dfcb68a1698220016a049b45d42b4dfa9ecf85a15c"}, - {file = "exchange_calendars-4.2.8.tar.gz", hash = "sha256:1598b6219a58e7be218c640f389375e39c9c12513c7db82d7591ae56f64467f9"}, -] - -[package.dependencies] -korean-lunar-calendar = "*" -numpy = "*" -pandas = ">=1.1" -pyluach = "*" -python-dateutil = "*" -pytz = "*" -toolz = "*" - -[package.extras] -dev = ["flake8", "hypothesis", "pip-tools", "pytest", "pytest-benchmark", "pytest-xdist"] - -[[package]] -name = "execnet" -version = "2.1.1" -description = "execnet: rapid multi-Python deployment" -optional = false -python-versions = ">=3.8" -files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, -] - -[package.extras] -testing = ["hatch", "pre-commit", "pytest", "tox"] - -[[package]] -name = "executing" -version = "2.0.1" -description = "Get the currently executing AST node of a frame, and other information" -optional = false -python-versions = ">=3.5" -files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, -] - -[package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] - -[[package]] -name = "fastjsonschema" -version = "2.19.1" -description = "Fastest Python implementation of JSON schema" -optional = false -python-versions = "*" -files = [ - {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, - {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, -] - -[package.extras] -devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] - -[[package]] -name = "feedparser" -version = "6.0.11" -description = "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds" -optional = false -python-versions = ">=3.6" -files = [ - {file = "feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45"}, - {file = "feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5"}, -] - -[package.dependencies] -sgmllib3k = "*" - -[[package]] -name = "ffmpeg-python" -version = "0.2.0" -description = "Python bindings for FFmpeg - with complex filtering support" -optional = true -python-versions = "*" -files = [ - {file = "ffmpeg-python-0.2.0.tar.gz", hash = "sha256:65225db34627c578ef0e11c8b1eb528bb35e024752f6f10b78c011f6f64c4127"}, - {file = "ffmpeg_python-0.2.0-py3-none-any.whl", hash = "sha256:ac441a0404e053f8b6a1113a77c0f452f1cfc62f6344a769475ffdc0f56c23c5"}, -] - -[package.dependencies] -future = "*" - -[package.extras] -dev = ["Sphinx (==2.1.0)", "future (==0.17.1)", "numpy (==1.16.4)", "pytest (==4.6.1)", "pytest-mock (==1.10.4)", "tox (==3.12.1)"] - -[[package]] -name = "ffn" -version = "0.3.6" -description = "Financial functions for Python" -optional = false -python-versions = "*" -files = [ - {file = "ffn-0.3.6-py2.py3-none-any.whl", hash = "sha256:1e55e8caf6b63dce2f164cc4d3b8a02b678be7ed12990cafb2006b818a9a09e7"}, - {file = "ffn-0.3.6.tar.gz", hash = "sha256:4a79e72e06ff328e333ffe97010b1ce110bcd694fcd03be7351bf5065cd273e8"}, -] - -[package.dependencies] -decorator = ">=4" -future = ">=0.15" -matplotlib = ">=1" -numpy = ">=1.5" -pandas = ">=0.19" -pandas-datareader = ">=0.2" -scikit-learn = ">=0.15" -scipy = ">=0.15" -tabulate = ">=0.7.5" - -[package.extras] -dev = ["black (>=20.8b1)", "codecov", "coverage", "flake8", "flake8-black", "future", "mock", "nose"] - -[[package]] -name = "filelock" -version = "3.13.4" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, - {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] - -[[package]] -name = "financedatabase" -version = "2.1.0" -description = "This is a database of 300.000+ symbols containing Equities, ETFs, Funds, Indices, Currencies, Cryptocurrencies and Money Markets." -optional = false -python-versions = "*" -files = [ - {file = "financedatabase-2.1.0-py3-none-any.whl", hash = "sha256:03f70813d1388a7b7989d788fc6404af91617e90f41ebc2ea2584babc89e997e"}, - {file = "financedatabase-2.1.0.tar.gz", hash = "sha256:e0fb783c9c717d279aa626c421b69d818ec2b01e7d3d119d745e7fcebcf24d79"}, -] - -[package.dependencies] -pandas = "*" - -[[package]] -name = "finnhub-python" -version = "2.4.19" -description = "Finnhub API" -optional = false -python-versions = "*" -files = [ - {file = "finnhub-python-2.4.19.tar.gz", hash = "sha256:1c216df125fe4c38ff6033613739f07efaf4bce1b7502dde54d31631bbeb5cca"}, - {file = "finnhub_python-2.4.19-py3-none-any.whl", hash = "sha256:e9bb56cf41d88a903e9f083a6fc2076187c0a0cbcc55d9f78941c4f455134229"}, -] - -[package.dependencies] -requests = ">=2.22.0" - -[[package]] -name = "finviz" -version = "1.4.6" -description = "Unofficial API for FinViz.com" -optional = false -python-versions = "*" -files = [ - {file = "finviz-1.4.6.tar.gz", hash = "sha256:2ce364e6fa8b5f7cb588207a5409cda6c3555b1904de9fa650e2241a65bd490b"}, -] - -[package.dependencies] -aiohttp = "*" -beautifulsoup4 = "*" -cssselect = "*" -lxml = "*" -requests = "*" -tenacity = "*" -tqdm = "*" -urllib3 = "*" -user_agent = "*" - -[[package]] -name = "finvizfinance" -version = "0.14.7rc1" -description = "Finviz Finance. Information downloader." -optional = false -python-versions = ">=3.5" -files = [ - {file = "finvizfinance-0.14.7rc1-py3-none-any.whl", hash = "sha256:37ea590156a4f51556c6b18a88903f748543703760b5478f30b3ded2d9c556ed"}, - {file = "finvizfinance-0.14.7rc1.tar.gz", hash = "sha256:abb9fc14057ab68db8a433e3a71a62755d69e6462c51463ecff1dd51c9b84b69"}, -] - -[package.dependencies] -bs4 = "*" -datetime = "*" -lxml = "*" -pandas = "*" -requests = "*" - -[[package]] -name = "flake8" -version = "7.0.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -files = [ - {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, - {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.2.0,<3.3.0" - -[[package]] -name = "fonttools" -version = "4.51.0" -description = "Tools to manipulate font files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:84d7751f4468dd8cdd03ddada18b8b0857a5beec80bce9f435742abc9a851a74"}, - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b4850fa2ef2cfbc1d1f689bc159ef0f45d8d83298c1425838095bf53ef46308"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5b48a1121117047d82695d276c2af2ee3a24ffe0f502ed581acc2673ecf1037"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:180194c7fe60c989bb627d7ed5011f2bef1c4d36ecf3ec64daec8302f1ae0716"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:96a48e137c36be55e68845fc4284533bda2980f8d6f835e26bca79d7e2006438"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:806e7912c32a657fa39d2d6eb1d3012d35f841387c8fc6cf349ed70b7c340039"}, - {file = "fonttools-4.51.0-cp310-cp310-win32.whl", hash = "sha256:32b17504696f605e9e960647c5f64b35704782a502cc26a37b800b4d69ff3c77"}, - {file = "fonttools-4.51.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7e91abdfae1b5c9e3a543f48ce96013f9a08c6c9668f1e6be0beabf0a569c1b"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a8feca65bab31479d795b0d16c9a9852902e3a3c0630678efb0b2b7941ea9c74"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ac27f436e8af7779f0bb4d5425aa3535270494d3bc5459ed27de3f03151e4c2"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e19bd9e9964a09cd2433a4b100ca7f34e34731e0758e13ba9a1ed6e5468cc0f"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2b92381f37b39ba2fc98c3a45a9d6383bfc9916a87d66ccb6553f7bdd129097"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5f6bc991d1610f5c3bbe997b0233cbc234b8e82fa99fc0b2932dc1ca5e5afec0"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9696fe9f3f0c32e9a321d5268208a7cc9205a52f99b89479d1b035ed54c923f1"}, - {file = "fonttools-4.51.0-cp311-cp311-win32.whl", hash = "sha256:3bee3f3bd9fa1d5ee616ccfd13b27ca605c2b4270e45715bd2883e9504735034"}, - {file = "fonttools-4.51.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f08c901d3866a8905363619e3741c33f0a83a680d92a9f0e575985c2634fcc1"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4060acc2bfa2d8e98117828a238889f13b6f69d59f4f2d5857eece5277b829ba"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1250e818b5f8a679ad79660855528120a8f0288f8f30ec88b83db51515411fcc"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76f1777d8b3386479ffb4a282e74318e730014d86ce60f016908d9801af9ca2a"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b5ad456813d93b9c4b7ee55302208db2b45324315129d85275c01f5cb7e61a2"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:68b3fb7775a923be73e739f92f7e8a72725fd333eab24834041365d2278c3671"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8e2f1a4499e3b5ee82c19b5ee57f0294673125c65b0a1ff3764ea1f9db2f9ef5"}, - {file = "fonttools-4.51.0-cp312-cp312-win32.whl", hash = "sha256:278e50f6b003c6aed19bae2242b364e575bcb16304b53f2b64f6551b9c000e15"}, - {file = "fonttools-4.51.0-cp312-cp312-win_amd64.whl", hash = "sha256:b3c61423f22165541b9403ee39874dcae84cd57a9078b82e1dce8cb06b07fa2e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1621ee57da887c17312acc4b0e7ac30d3a4fb0fec6174b2e3754a74c26bbed1e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d9298be7a05bb4801f558522adbe2feea1b0b103d5294ebf24a92dd49b78e5"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee1af4be1c5afe4c96ca23badd368d8dc75f611887fb0c0dac9f71ee5d6f110e"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b49adc721a7d0b8dfe7c3130c89b8704baf599fb396396d07d4aa69b824a1"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de7c29bdbdd35811f14493ffd2534b88f0ce1b9065316433b22d63ca1cd21f14"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cadf4e12a608ef1d13e039864f484c8a968840afa0258b0b843a0556497ea9ed"}, - {file = "fonttools-4.51.0-cp38-cp38-win32.whl", hash = "sha256:aefa011207ed36cd280babfaa8510b8176f1a77261833e895a9d96e57e44802f"}, - {file = "fonttools-4.51.0-cp38-cp38-win_amd64.whl", hash = "sha256:865a58b6e60b0938874af0968cd0553bcd88e0b2cb6e588727117bd099eef836"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:60a3409c9112aec02d5fb546f557bca6efa773dcb32ac147c6baf5f742e6258b"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7e89853d8bea103c8e3514b9f9dc86b5b4120afb4583b57eb10dfa5afbe0936"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fc244f2585d6c00b9bcc59e6593e646cf095a96fe68d62cd4da53dd1287b55"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d145976194a5242fdd22df18a1b451481a88071feadf251221af110ca8f00ce"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5b8cab0c137ca229433570151b5c1fc6af212680b58b15abd797dcdd9dd5051"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:54dcf21a2f2d06ded676e3c3f9f74b2bafded3a8ff12f0983160b13e9f2fb4a7"}, - {file = "fonttools-4.51.0-cp39-cp39-win32.whl", hash = "sha256:0118ef998a0699a96c7b28457f15546815015a2710a1b23a7bf6c1be60c01636"}, - {file = "fonttools-4.51.0-cp39-cp39-win_amd64.whl", hash = "sha256:599bdb75e220241cedc6faebfafedd7670335d2e29620d207dd0378a4e9ccc5a"}, - {file = "fonttools-4.51.0-py3-none-any.whl", hash = "sha256:15c94eeef6b095831067f72c825eb0e2d48bb4cea0647c1b05c981ecba2bf39f"}, - {file = "fonttools-4.51.0.tar.gz", hash = "sha256:dc0673361331566d7a663d7ce0f6fdcbfbdc1f59c6e3ed1165ad7202ca183c68"}, -] - -[package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] -graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres", "pycairo", "scipy"] -lxml = ["lxml (>=4.0)"] -pathops = ["skia-pathops (>=0.5.0)"] -plot = ["matplotlib"] -repacker = ["uharfbuzz (>=0.23.0)"] -symfont = ["sympy"] -type1 = ["xattr"] -ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.1.0)"] -woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] - -[[package]] -name = "formulaic" -version = "1.0.1" -description = "An implementation of Wilkinson formulas." -optional = false -python-versions = ">=3.7.2" -files = [ - {file = "formulaic-1.0.1-py3-none-any.whl", hash = "sha256:be9e6127b98ba0293654be528fd070ede264f64d0247477c4d5af799b0b12cf2"}, - {file = "formulaic-1.0.1.tar.gz", hash = "sha256:64dd7992a7aa5bbceb1e40679d0f01fc6f0ba12b7d23d78094a88c2edc68fba1"}, -] - -[package.dependencies] -astor = {version = ">=0.8", markers = "python_version < \"3.9\""} -graphlib-backport = {version = ">=1.0.0", markers = "python_version < \"3.9\""} -interface-meta = ">=1.2.0" -numpy = ">=1.16.5" -pandas = ">=1.0" -scipy = ">=1.6" -typing-extensions = ">=4.2.0" -wrapt = ">=1.0" - -[package.extras] -arrow = ["pyarrow (>=1)"] -calculus = ["sympy (>=1.3,!=1.10)"] - -[[package]] -name = "fqdn" -version = "1.5.1" -description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" -optional = false -python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" -files = [ - {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, - {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, -] - -[[package]] -name = "fred" -version = "3.1" -description = "St. Louis Federal Reserve FRED API" -optional = false -python-versions = "*" -files = [ - {file = "fred-3.1.tar.gz", hash = "sha256:f31327d648917694b8d15d66ca4e82a082dbb88ca027bdcc9d56738cbc836a6a"}, -] - -[package.dependencies] -requests = "*" - -[[package]] -name = "fredapi" -version = "0.4.3" -description = "Python API for Federal Reserve Economic Data (FRED) from St. Louis Fed" -optional = false -python-versions = "*" -files = [ - {file = "fredapi-0.4.3-py3-none-any.whl", hash = "sha256:e06075592eabddadfe0635f3c59e9072c9f48647430328b4582171ea10f7df2f"}, - {file = "fredapi-0.4.3.tar.gz", hash = "sha256:d9b3194fb60541991bd33f019c710d4a9580ecfb5e47efbf2d2571888a2aac02"}, -] - -[package.dependencies] -pandas = "*" - -[[package]] -name = "freezegun" -version = "1.4.0" -description = "Let your Python tests travel through time" -optional = false -python-versions = ">=3.7" -files = [ - {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, - {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, -] - -[package.dependencies] -python-dateutil = ">=2.7" - -[[package]] -name = "frozendict" -version = "2.4.2" -description = "A simple immutable dictionary" -optional = false -python-versions = ">=3.6" -files = [ - {file = "frozendict-2.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:19743495b1e92a7e4db56fcd6a5d36ea1d1b0f550822d6fd780e44d58f0b8c18"}, - {file = "frozendict-2.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81efb4ea854a1c93d954a67389eaf78c508acb2d4768321a835cda2754ec5c01"}, - {file = "frozendict-2.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5f1a4d9662b854dce52b560b60f51349905dc871826b8c6be20141a13067a53"}, - {file = "frozendict-2.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1412aeb325e4a28cfe32106c66c046372bb7fd5a9af1748193549c5d01a9e9c1"}, - {file = "frozendict-2.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7ce0535f02eba9746e4e2cf0abef0f0f2051d20fdccf4af31bc3d1adecf5a71"}, - {file = "frozendict-2.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:07153e6d2720fa1131bb180ce388c7042affb29561d8bcd1c0d6e683a8beaea2"}, - {file = "frozendict-2.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f7a90ea6d5248617a1222daef07d22fb146ff07635a36db327e1ce114bf3e304"}, - {file = "frozendict-2.4.2-cp310-cp310-win_arm64.whl", hash = "sha256:20a6f741c92fdeb3766924cde42b8ee445cf568e3be8aa983cb83e9fe5b61e63"}, - {file = "frozendict-2.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:146129502cd9d96de64e0c8f7dc4c66422da3d4bfccf891dd80a3821b358a926"}, - {file = "frozendict-2.4.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ac1f74ccf818977abbc1868090c06436b8f06534d306f808f15cffc304ae046"}, - {file = "frozendict-2.4.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d2ea4f10505ad15f53ce3742420682d916d0c4d566edb8e1019756e7cea30"}, - {file = "frozendict-2.4.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4a5841681e70d2862ca153543f2912e0bab034bf29e2d3610e86ea42506121c2"}, - {file = "frozendict-2.4.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d4a10119f17552cbeab48d4ae830ba091c6d47616589618adc31f251184579a7"}, - {file = "frozendict-2.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:7d13ffe649e9db6f4bb5e107d9be7dfd23e13101bc69f97aa5fa6cbf6aecaadd"}, - {file = "frozendict-2.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19e64630e164a297f83e9a1c69f1cd36fa4b3d1196c1f9fc006a0385aa198ea4"}, - {file = "frozendict-2.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bedb0a6587bae53bd53727b92a87c4cf90ad7a7e0bd2db562d439beb6982712e"}, - {file = "frozendict-2.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cc9d063131fd8adbeb18a473d222b5dc8301cac9505cfe578158f9a9bf55a9"}, - {file = "frozendict-2.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:92c46b155ea9eb9ecabc66ba2d9030f2634319f55c6448688965ece094f14b51"}, - {file = "frozendict-2.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f958d40637e0440bce2453019821c94fe86cfc5f3847ae11cd4f02c3548b1d1b"}, - {file = "frozendict-2.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ac954be447a907face9b652207fbd943b9b552212890db959ba653e8f1dc3f56"}, - {file = "frozendict-2.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7e0ff5e84742604a1b42c2de4f1e67630c0868cf52a5c585b54a99e06f6b453"}, - {file = "frozendict-2.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:84c36bfa819cd8442f6e0bdb86413c7678b2822a46b1a22cfa0f0dd30d9e5c45"}, - {file = "frozendict-2.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cead3bfe70c90c634a9b76807c9d7e75e6c5666ec96fa2cea8e7412ccf22a1f8"}, - {file = "frozendict-2.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fc6e3158107b5431255978b954758b1041cc70a3b8e7657373110512eb528e3"}, - {file = "frozendict-2.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4db1d6cc412bd865cab36723995208b82166a97bc6c724753bcd2b90cf24f164"}, - {file = "frozendict-2.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff6fb5831539fffb09d71cc0cc0462b1f27c0160cb6c6fa2d1f4c1bc7fffe52a"}, - {file = "frozendict-2.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:79e1c94ad2a925ad5723d82a4134c6d851d5a7bc72b7e9da8b2087c42758a512"}, - {file = "frozendict-2.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34704f9ffb21448d4b5c0f9239f8f058c0efab4bfdbe2956c5be978fef0b929c"}, - {file = "frozendict-2.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5280d685cd1659883a3010dec843afe3065416ae92e453498997d4474a898a39"}, - {file = "frozendict-2.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ca09a376114172e4d9918e6d576f58244c45e21f5af1245085699fd3a171c47"}, - {file = "frozendict-2.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55953aa2acf5bf183c664f3d0f540f8c8ac8f5fa97170f2098d413414318eb2b"}, - {file = "frozendict-2.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:476e4857e1d87b05c9102dd5409216ce4716cb7df619e6657429bc99279303cc"}, - {file = "frozendict-2.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4a8b298f39242d25770d029588ce9d4f524e9f4edc60d2d34b6178fb07c8a93e"}, - {file = "frozendict-2.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:c157b8a92743a7905b341edb0663044fecdc7780f96c59a2843d3da68d694b90"}, - {file = "frozendict-2.4.2-cp39-cp39-win_arm64.whl", hash = "sha256:cbab325c0a98b2f3ee291b36710623781b4977a3057f9103a7b0f11bcc23b177"}, - {file = "frozendict-2.4.2.tar.gz", hash = "sha256:741779e1d1a2e6bb2c623f78423bd5d14aad35dc0c57e6ccc89e54eaab5f1b8a"}, -] - -[[package]] -name = "frozenlist" -version = "1.4.1" -description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false -python-versions = ">=3.8" -files = [ - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, - {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, - {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, - {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, - {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, - {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, - {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, -] - -[[package]] -name = "fs" -version = "2.4.16" -description = "Python's filesystem abstraction layer" -optional = true -python-versions = "*" -files = [ - {file = "fs-2.4.16-py2.py3-none-any.whl", hash = "sha256:660064febbccda264ae0b6bace80a8d1be9e089e0a5eb2427b7d517f9a91545c"}, - {file = "fs-2.4.16.tar.gz", hash = "sha256:ae97c7d51213f4b70b6a958292530289090de3a7e15841e108fbe144f069d313"}, -] - -[package.dependencies] -appdirs = ">=1.4.3,<1.5.0" -setuptools = "*" -six = ">=1.10,<2.0" - -[package.extras] -scandir = ["scandir (>=1.5,<2.0)"] - -[[package]] -name = "fsspec" -version = "2024.3.1" -description = "File-system specification" -optional = true -python-versions = ">=3.8" -files = [ - {file = "fsspec-2024.3.1-py3-none-any.whl", hash = "sha256:918d18d41bf73f0e2b261824baeb1b124bcf771767e3a26425cd7dec3332f512"}, - {file = "fsspec-2024.3.1.tar.gz", hash = "sha256:f39780e282d7d117ffb42bb96992f8a90795e4d0fb0f661a70ca39fe9c43ded9"}, -] - -[package.dependencies] -aiohttp = {version = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1", optional = true, markers = "extra == \"http\""} - -[package.extras] -abfs = ["adlfs"] -adl = ["adlfs"] -arrow = ["pyarrow (>=1)"] -dask = ["dask", "distributed"] -devel = ["pytest", "pytest-cov"] -dropbox = ["dropbox", "dropboxdrivefs", "requests"] -full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] -fuse = ["fusepy"] -gcs = ["gcsfs"] -git = ["pygit2"] -github = ["requests"] -gs = ["gcsfs"] -gui = ["panel"] -hdfs = ["pyarrow (>=1)"] -http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] -libarchive = ["libarchive-c"] -oci = ["ocifs"] -s3 = ["s3fs"] -sftp = ["paramiko"] -smb = ["smbprotocol"] -ssh = ["paramiko"] -tqdm = ["tqdm"] - -[[package]] -name = "fugue" -version = "0.8.7" -description = "An abstraction layer for distributed computation" -optional = true -python-versions = ">=3.8" -files = [ - {file = "fugue-0.8.7-py3-none-any.whl", hash = "sha256:d4dc16bac9850024109b999cd163a6ca4976bd0bf190a85730d91ff74737c3f2"}, - {file = "fugue-0.8.7.tar.gz", hash = "sha256:4c56946de46083778cdd6ec5b91ac5d37a847164c80790771edc6832bb9a260d"}, -] - -[package.dependencies] -adagio = ">=0.2.4" -fugue-sql-antlr = ">=0.1.6" -jinja2 = "*" -qpd = ">=0.4.4" -sqlglot = "*" -triad = ">=0.9.3" - -[package.extras] -all = ["dask-sql", "dask[dataframe,distributed] (>=2023.5.0)", "duckdb (>=0.5.0)", "fugue-sql-antlr[cpp] (>=0.1.6)", "ibis-framework (>=3.2.0,<6)", "ipython (>=7.10.0)", "jinja2", "jupyterlab", "notebook", "pandas (>=2.0.2)", "polars", "pyarrow (>=6.0.1)", "pyspark (>=3.1.1)", "ray[data] (>=2.4.0)", "sqlglot"] -cpp-sql-parser = ["fugue-sql-antlr[cpp] (>=0.1.6)"] -dask = ["dask[dataframe,distributed] (>=2023.5.0)", "pandas (>=2.0.2)", "pyarrow (>=7.0.0)"] -duckdb = ["duckdb (>=0.5.0)", "numpy"] -ibis = ["ibis-framework (>=3.2.0,<6)"] -notebook = ["ipython (>=7.10.0)", "jupyterlab", "notebook"] -polars = ["polars"] -ray = ["duckdb (>=0.5.0)", "pyarrow (>=6.0.1)", "ray[data] (>=2.4.0)"] -spark = ["pyspark (>=3.1.1)"] -sql = ["fugue-sql-antlr (>=0.1.6)", "jinja2", "qpd (>=0.4.4)", "sqlglot"] - -[[package]] -name = "fugue-sql-antlr" -version = "0.2.0" -description = "Fugue SQL Antlr Parser" -optional = true -python-versions = ">=3.8" -files = [ - {file = "fugue-sql-antlr-0.2.0.tar.gz", hash = "sha256:e15433aaf09502c5b0423019d9fa93e161172ceb08e7bd27af0175dadf3cf552"}, -] - -[package.dependencies] -antlr4-python3-runtime = "<4.12" -jinja2 = "*" -packaging = "*" -triad = ">=0.6.8" - -[package.extras] -cpp = ["fugue-sql-antlr-cpp (==0.2.0)"] -test = ["speedy_antlr_tool"] - -[[package]] -name = "fundamentalanalysis" -version = "0.2.14" -description = "Fully-fledged Fundamental Analysis package capable of collecting 20 years of Company Profiles, Financial Statements, Ratios and Stock Data of 20.000+ companies." -optional = false -python-versions = "*" -files = [ - {file = "fundamentalanalysis-0.2.14-py3-none-any.whl", hash = "sha256:eda7920cb3b2f76b197cfe0a47e3936e6b4e65273a3852039cd3e5edd1bb06cc"}, - {file = "fundamentalanalysis-0.2.14.tar.gz", hash = "sha256:bc3ee7948f7de817e195b2ac6d34dc6ba9c5f4780c9d29b7768c2790e67ab4a4"}, -] - -[[package]] -name = "future" -version = "1.0.0" -description = "Clean single-source support for Python 3 and 2" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216"}, - {file = "future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05"}, -] - -[[package]] -name = "gitdb" -version = "4.0.11" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.43" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, - {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[package.extras] -doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"] -test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] - -[[package]] -name = "google-auth" -version = "2.29.0" -description = "Google Authentication Library" -optional = true -python-versions = ">=3.7" -files = [ - {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, - {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, -] - -[package.dependencies] -cachetools = ">=2.0.0,<6.0" -pyasn1-modules = ">=0.2.1" -rsa = ">=3.1.4,<5" - -[package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] -enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] -pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] -reauth = ["pyu2f (>=0.1.5)"] -requests = ["requests (>=2.20.0,<3.0.0.dev0)"] - -[[package]] -name = "google-auth-oauthlib" -version = "1.0.0" -description = "Google Authentication Library" -optional = true -python-versions = ">=3.6" -files = [ - {file = "google-auth-oauthlib-1.0.0.tar.gz", hash = "sha256:e375064964820b47221a7e1b7ee1fd77051b6323c3f9e3e19785f78ab67ecfc5"}, - {file = "google_auth_oauthlib-1.0.0-py2.py3-none-any.whl", hash = "sha256:95880ca704928c300f48194d1770cf5b1462835b6e49db61445a520f793fd5fb"}, -] - -[package.dependencies] -google-auth = ">=2.15.0" -requests-oauthlib = ">=0.7.0" - -[package.extras] -tool = ["click (>=6.0.0)"] - -[[package]] -name = "graphlib-backport" -version = "1.1.0" -description = "Backport of the Python 3.9 graphlib module for Python 3.6+" -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "graphlib_backport-1.1.0-py3-none-any.whl", hash = "sha256:eccacf9f2126cdf89ce32a6018c88e1ecd3e4898a07568add6e1907a439055ba"}, - {file = "graphlib_backport-1.1.0.tar.gz", hash = "sha256:00a7888b21e5393064a133209cb5d3b3ef0a2096cf023914c9d778dff5644125"}, -] - -[[package]] -name = "graphviz" -version = "0.20.3" -description = "Simple Python interface for Graphviz" -optional = true -python-versions = ">=3.8" -files = [ - {file = "graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5"}, - {file = "graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d"}, -] - -[package.extras] -dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] -docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] -test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] - -[[package]] -name = "grpcio" -version = "1.62.2" -description = "HTTP/2-based RPC framework" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpcio-1.62.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:66344ea741124c38588a664237ac2fa16dfd226964cca23ddc96bd4accccbde5"}, - {file = "grpcio-1.62.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:5dab7ac2c1e7cb6179c6bfad6b63174851102cbe0682294e6b1d6f0981ad7138"}, - {file = "grpcio-1.62.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:3ad00f3f0718894749d5a8bb0fa125a7980a2f49523731a9b1fabf2b3522aa43"}, - {file = "grpcio-1.62.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e72ddfee62430ea80133d2cbe788e0d06b12f865765cb24a40009668bd8ea05"}, - {file = "grpcio-1.62.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53d3a59a10af4c2558a8e563aed9f256259d2992ae0d3037817b2155f0341de1"}, - {file = "grpcio-1.62.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1511a303f8074f67af4119275b4f954189e8313541da7b88b1b3a71425cdb10"}, - {file = "grpcio-1.62.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b94d41b7412ef149743fbc3178e59d95228a7064c5ab4760ae82b562bdffb199"}, - {file = "grpcio-1.62.2-cp310-cp310-win32.whl", hash = "sha256:a75af2fc7cb1fe25785be7bed1ab18cef959a376cdae7c6870184307614caa3f"}, - {file = "grpcio-1.62.2-cp310-cp310-win_amd64.whl", hash = "sha256:80407bc007754f108dc2061e37480238b0dc1952c855e86a4fc283501ee6bb5d"}, - {file = "grpcio-1.62.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:c1624aa686d4b36790ed1c2e2306cc3498778dffaf7b8dd47066cf819028c3ad"}, - {file = "grpcio-1.62.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:1c1bb80299bdef33309dff03932264636450c8fdb142ea39f47e06a7153d3063"}, - {file = "grpcio-1.62.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:db068bbc9b1fa16479a82e1ecf172a93874540cb84be69f0b9cb9b7ac3c82670"}, - {file = "grpcio-1.62.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2cc8a308780edbe2c4913d6a49dbdb5befacdf72d489a368566be44cadaef1a"}, - {file = "grpcio-1.62.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0695ae31a89f1a8fc8256050329a91a9995b549a88619263a594ca31b76d756"}, - {file = "grpcio-1.62.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88b4f9ee77191dcdd8810241e89340a12cbe050be3e0d5f2f091c15571cd3930"}, - {file = "grpcio-1.62.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a0204532aa2f1afd467024b02b4069246320405bc18abec7babab03e2644e75"}, - {file = "grpcio-1.62.2-cp311-cp311-win32.whl", hash = "sha256:6e784f60e575a0de554ef9251cbc2ceb8790914fe324f11e28450047f264ee6f"}, - {file = "grpcio-1.62.2-cp311-cp311-win_amd64.whl", hash = "sha256:112eaa7865dd9e6d7c0556c8b04ae3c3a2dc35d62ad3373ab7f6a562d8199200"}, - {file = "grpcio-1.62.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:65034473fc09628a02fb85f26e73885cf1ed39ebd9cf270247b38689ff5942c5"}, - {file = "grpcio-1.62.2-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d2c1771d0ee3cf72d69bb5e82c6a82f27fbd504c8c782575eddb7839729fbaad"}, - {file = "grpcio-1.62.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:3abe6838196da518863b5d549938ce3159d809218936851b395b09cad9b5d64a"}, - {file = "grpcio-1.62.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5ffeb269f10cedb4f33142b89a061acda9f672fd1357331dbfd043422c94e9e"}, - {file = "grpcio-1.62.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404d3b4b6b142b99ba1cff0b2177d26b623101ea2ce51c25ef6e53d9d0d87bcc"}, - {file = "grpcio-1.62.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:262cda97efdabb20853d3b5a4c546a535347c14b64c017f628ca0cc7fa780cc6"}, - {file = "grpcio-1.62.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17708db5b11b966373e21519c4c73e5a750555f02fde82276ea2a267077c68ad"}, - {file = "grpcio-1.62.2-cp312-cp312-win32.whl", hash = "sha256:b7ec9e2f8ffc8436f6b642a10019fc513722858f295f7efc28de135d336ac189"}, - {file = "grpcio-1.62.2-cp312-cp312-win_amd64.whl", hash = "sha256:aa787b83a3cd5e482e5c79be030e2b4a122ecc6c5c6c4c42a023a2b581fdf17b"}, - {file = "grpcio-1.62.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:cfd23ad29bfa13fd4188433b0e250f84ec2c8ba66b14a9877e8bce05b524cf54"}, - {file = "grpcio-1.62.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:af15e9efa4d776dfcecd1d083f3ccfb04f876d613e90ef8432432efbeeac689d"}, - {file = "grpcio-1.62.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:f4aa94361bb5141a45ca9187464ae81a92a2a135ce2800b2203134f7a1a1d479"}, - {file = "grpcio-1.62.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82af3613a219512a28ee5c95578eb38d44dd03bca02fd918aa05603c41018051"}, - {file = "grpcio-1.62.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55ddaf53474e8caeb29eb03e3202f9d827ad3110475a21245f3c7712022882a9"}, - {file = "grpcio-1.62.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c79b518c56dddeec79e5500a53d8a4db90da995dfe1738c3ac57fe46348be049"}, - {file = "grpcio-1.62.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a5eb4844e5e60bf2c446ef38c5b40d7752c6effdee882f716eb57ae87255d20a"}, - {file = "grpcio-1.62.2-cp37-cp37m-win_amd64.whl", hash = "sha256:aaae70364a2d1fb238afd6cc9fcb10442b66e397fd559d3f0968d28cc3ac929c"}, - {file = "grpcio-1.62.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:1bcfe5070e4406f489e39325b76caeadab28c32bf9252d3ae960c79935a4cc36"}, - {file = "grpcio-1.62.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:da6a7b6b938c15fa0f0568e482efaae9c3af31963eec2da4ff13a6d8ec2888e4"}, - {file = "grpcio-1.62.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:41955b641c34db7d84db8d306937b72bc4968eef1c401bea73081a8d6c3d8033"}, - {file = "grpcio-1.62.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c772f225483905f675cb36a025969eef9712f4698364ecd3a63093760deea1bc"}, - {file = "grpcio-1.62.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07ce1f775d37ca18c7a141300e5b71539690efa1f51fe17f812ca85b5e73262f"}, - {file = "grpcio-1.62.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:26f415f40f4a93579fd648f48dca1c13dfacdfd0290f4a30f9b9aeb745026811"}, - {file = "grpcio-1.62.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:db707e3685ff16fc1eccad68527d072ac8bdd2e390f6daa97bc394ea7de4acea"}, - {file = "grpcio-1.62.2-cp38-cp38-win32.whl", hash = "sha256:589ea8e75de5fd6df387de53af6c9189c5231e212b9aa306b6b0d4f07520fbb9"}, - {file = "grpcio-1.62.2-cp38-cp38-win_amd64.whl", hash = "sha256:3c3ed41f4d7a3aabf0f01ecc70d6b5d00ce1800d4af652a549de3f7cf35c4abd"}, - {file = "grpcio-1.62.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:162ccf61499c893831b8437120600290a99c0bc1ce7b51f2c8d21ec87ff6af8b"}, - {file = "grpcio-1.62.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:f27246d7da7d7e3bd8612f63785a7b0c39a244cf14b8dd9dd2f2fab939f2d7f1"}, - {file = "grpcio-1.62.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:2507006c8a478f19e99b6fe36a2464696b89d40d88f34e4b709abe57e1337467"}, - {file = "grpcio-1.62.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a90ac47a8ce934e2c8d71e317d2f9e7e6aaceb2d199de940ce2c2eb611b8c0f4"}, - {file = "grpcio-1.62.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99701979bcaaa7de8d5f60476487c5df8f27483624f1f7e300ff4669ee44d1f2"}, - {file = "grpcio-1.62.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:af7dc3f7a44f10863b1b0ecab4078f0a00f561aae1edbd01fd03ad4dcf61c9e9"}, - {file = "grpcio-1.62.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fa63245271920786f4cb44dcada4983a3516be8f470924528cf658731864c14b"}, - {file = "grpcio-1.62.2-cp39-cp39-win32.whl", hash = "sha256:c6ad9c39704256ed91a1cffc1379d63f7d0278d6a0bad06b0330f5d30291e3a3"}, - {file = "grpcio-1.62.2-cp39-cp39-win_amd64.whl", hash = "sha256:16da954692fd61aa4941fbeda405a756cd96b97b5d95ca58a92547bba2c1624f"}, - {file = "grpcio-1.62.2.tar.gz", hash = "sha256:c77618071d96b7a8be2c10701a98537823b9c65ba256c0b9067e0594cdbd954d"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.62.2)"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "hijri-converter" -version = "2.3.1" -description = "Accurate Hijri-Gregorian dates converter based on the Umm al-Qura calendar" -optional = false -python-versions = ">=3.7" -files = [ - {file = "hijri-converter-2.3.1.tar.gz", hash = "sha256:069b6789291e0830f4860a79dcd34fb3ceea3b95516ed4010202b9656ba1ab61"}, - {file = "hijri_converter-2.3.1-py3-none-any.whl", hash = "sha256:c04f072c78e1854a1f474ada253a3377009457221dbc1b14999761728e663662"}, -] - -[[package]] -name = "holidays" -version = "0.14.2" -description = "Generate and work with holidays in Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "holidays-0.14.2-py3-none-any.whl", hash = "sha256:441156949b1c41f08bb08ef8e79fcadfd76d2dd7837f49d7bfc6a572fc442c93"}, - {file = "holidays-0.14.2.tar.gz", hash = "sha256:0e70fd174804aea1c870b151319faebcd5cdb0d955b78230434e1afd1778498e"}, -] - -[package.dependencies] -convertdate = ">=2.3.0" -hijri-converter = "*" -korean-lunar-calendar = "*" -python-dateutil = "*" - -[[package]] -name = "html5lib" -version = "1.1" -description = "HTML parser based on the WHATWG HTML specification" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, - {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, -] - -[package.dependencies] -six = ">=1.9" -webencodings = "*" - -[package.extras] -all = ["chardet (>=2.2)", "genshi", "lxml"] -chardet = ["chardet (>=2.2)"] -genshi = ["genshi"] -lxml = ["lxml"] - -[[package]] -name = "httpcore" -version = "1.0.5" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.13,<0.15" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.26.0)"] - -[[package]] -name = "httpx" -version = "0.27.0" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] - -[[package]] -name = "huggingface-hub" -version = "0.22.2" -description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" -optional = true -python-versions = ">=3.8.0" -files = [ - {file = "huggingface_hub-0.22.2-py3-none-any.whl", hash = "sha256:3429e25f38ccb834d310804a3b711e7e4953db5a9e420cc147a5e194ca90fd17"}, - {file = "huggingface_hub-0.22.2.tar.gz", hash = "sha256:32e9a9a6843c92f253ff9ca16b9985def4d80a93fb357af5353f770ef74a81be"}, -] - -[package.dependencies] -filelock = "*" -fsspec = ">=2023.5.0" -packaging = ">=20.9" -pyyaml = ">=5.1" -requests = "*" -tqdm = ">=4.42.1" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] -hf-transfer = ["hf-transfer (>=0.1.4)"] -inference = ["aiohttp", "minijinja (>=1.0)"] -quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] -tensorflow = ["graphviz", "pydot", "tensorflow"] -tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] -torch = ["safetensors", "torch"] -typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] - -[[package]] -name = "identify" -version = "2.5.35" -description = "File identification library for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, - {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.7" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, -] - -[[package]] -name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] - -[[package]] -name = "importlib-metadata" -version = "7.1.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, - {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] - -[[package]] -name = "importlib-resources" -version = "6.4.0" -description = "Read resources from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, - {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] - -[[package]] -name = "inflection" -version = "0.5.1" -description = "A port of Ruby on Rails inflector to Python" -optional = false -python-versions = ">=3.5" -files = [ - {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, - {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "interface-meta" -version = "1.3.0" -description = "`interface_meta` provides a convenient way to expose an extensible API with enforced method signatures and consistent documentation." -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "interface_meta-1.3.0-py3-none-any.whl", hash = "sha256:de35dc5241431886e709e20a14d6597ed07c9f1e8b4bfcffde2190ca5b700ee8"}, - {file = "interface_meta-1.3.0.tar.gz", hash = "sha256:8a4493f8bdb73fb9655dcd5115bc897e207319e36c8835f39c516a2d7e9d79a1"}, -] - -[[package]] -name = "intrinio-sdk" -version = "6.27.2" -description = "Intrinio API" -optional = false -python-versions = "*" -files = [ - {file = "intrinio-sdk-6.27.2.tar.gz", hash = "sha256:fed1a704f5278753efee1143589a7b48d6ffd28b2288012dffbfcaf4834db1be"}, - {file = "intrinio_sdk-6.27.2-py3-none-any.whl", hash = "sha256:6f3f905180073eaf1d2c9cb3441a181ed2c72ff1802af98bcc32a3db2ef06c24"}, -] - -[package.dependencies] -certifi = "*" -python-dateutil = "*" -retrying = ">=1.3.3" -six = ">=1.10" -urllib3 = ">=1.15" - -[[package]] -name = "ipykernel" -version = "6.29.4" -description = "IPython Kernel for Jupyter" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ipykernel-6.29.4-py3-none-any.whl", hash = "sha256:1181e653d95c6808039c509ef8e67c4126b3b3af7781496c7cbfb5ed938a27da"}, - {file = "ipykernel-6.29.4.tar.gz", hash = "sha256:3d44070060f9475ac2092b760123fadf105d2e2493c24848b6691a7c4f42af5c"}, -] - -[package.dependencies] -appnope = {version = "*", markers = "platform_system == \"Darwin\""} -comm = ">=0.1.1" -debugpy = ">=1.6.5" -ipython = ">=7.23.1" -jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -matplotlib-inline = ">=0.1" -nest-asyncio = "*" -packaging = "*" -psutil = "*" -pyzmq = ">=24" -tornado = ">=6.1" -traitlets = ">=5.4.0" - -[package.extras] -cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] -pyqt5 = ["pyqt5"] -pyside6 = ["pyside6"] -test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "ipympl" -version = "0.8.4" -description = "Matplotlib Jupyter Extension" -optional = false -python-versions = "*" -files = [ - {file = "ipympl-0.8.4-py2.py3-none-any.whl", hash = "sha256:2f955c1c04d8e6df883d57866450657040bfc568edeabcace801cbdbaf4d0295"}, - {file = "ipympl-0.8.4.tar.gz", hash = "sha256:fc799bc9d3482443d0cb54abc7c8d407e2773497de8ac1e079a0366d2073cbc0"}, -] - -[package.dependencies] -ipykernel = ">=4.7" -ipywidgets = ">=7.6.0" -matplotlib = ">=2.0.0" - -[[package]] -name = "ipython" -version = "8.11.0" -description = "IPython: Productive Interactive Computing" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ipython-8.11.0-py3-none-any.whl", hash = "sha256:5b54478e459155a326bf5f42ee4f29df76258c0279c36f21d71ddb560f88b156"}, - {file = "ipython-8.11.0.tar.gz", hash = "sha256:735cede4099dbc903ee540307b9171fbfef4aa75cfcacc5a273b2cda2f02be04"}, -] - -[package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" - -[package.extras] -all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] -black = ["black"] -doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] - -[[package]] -name = "ipython-genutils" -version = "0.2.0" -description = "Vestigial utilities from IPython" -optional = false -python-versions = "*" -files = [ - {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, - {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, -] - -[[package]] -name = "ipywidgets" -version = "8.1.2" -description = "Jupyter interactive widgets" -optional = false -python-versions = ">=3.7" -files = [ - {file = "ipywidgets-8.1.2-py3-none-any.whl", hash = "sha256:bbe43850d79fb5e906b14801d6c01402857996864d1e5b6fa62dd2ee35559f60"}, - {file = "ipywidgets-8.1.2.tar.gz", hash = "sha256:d0b9b41e49bae926a866e613a39b0f0097745d2b9f1f3dd406641b4a57ec42c9"}, -] - -[package.dependencies] -comm = ">=0.1.3" -ipython = ">=6.1.0" -jupyterlab-widgets = ">=3.0.10,<3.1.0" -traitlets = ">=4.3.1" -widgetsnbextension = ">=4.0.10,<4.1.0" - -[package.extras] -test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] - -[[package]] -name = "iso8601" -version = "0.1.16" -description = "Simple module to parse ISO 8601 dates" -optional = false -python-versions = "*" -files = [ - {file = "iso8601-0.1.16-py2.py3-none-any.whl", hash = "sha256:906714829fedbc89955d52806c903f2332e3948ed94e31e85037f9e0226b8376"}, - {file = "iso8601-0.1.16.tar.gz", hash = "sha256:36532f77cc800594e8f16641edae7f1baf7932f05d8e508545b95fc53c6dc85b"}, -] - -[[package]] -name = "isodate" -version = "0.6.1" -description = "An ISO 8601 date/time/duration parser and formatter" -optional = false -python-versions = "*" -files = [ - {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, - {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, -] - -[package.dependencies] -six = "*" - -[[package]] -name = "isoduration" -version = "20.11.0" -description = "Operations with ISO 8601 durations" -optional = false -python-versions = ">=3.7" -files = [ - {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, - {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, -] - -[package.dependencies] -arrow = ">=0.15.0" - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "jedi" -version = "0.18.2" -description = "An autocompletion tool for Python that can be used for text editors." -optional = false -python-versions = ">=3.6" -files = [ - {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, - {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, -] - -[package.dependencies] -parso = ">=0.8.0,<0.9.0" - -[package.extras] -docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "jedi-language-server" -version = "0.40.0" -description = "A language server for Jedi!" -optional = true -python-versions = ">=3.7,<3.12" -files = [ - {file = "jedi_language_server-0.40.0-py3-none-any.whl", hash = "sha256:53e590400b5cd2f6e363e77a4d824b1883798994b731cb0b4370d103748d30e2"}, - {file = "jedi_language_server-0.40.0.tar.gz", hash = "sha256:bacbae2930b6a8a0f1f284c211672fceec94b4808b0415d1c3352fa4b1ac5ad6"}, -] - -[package.dependencies] -docstring-to-markdown = "<1.0.0" -jedi = ">=0.18.1,<0.19.0" -lsprotocol = ">=2022.0.0a9" -pydantic = ">=1.9.1,<2.0.0" -pygls = ">=1.0.0,<2.0.0" - -[[package]] -name = "jinja2" -version = "3.1.3" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "joblib" -version = "1.4.0" -description = "Lightweight pipelining with Python functions" -optional = false -python-versions = ">=3.8" -files = [ - {file = "joblib-1.4.0-py3-none-any.whl", hash = "sha256:42942470d4062537be4d54c83511186da1fc14ba354961a2114da91efa9a4ed7"}, - {file = "joblib-1.4.0.tar.gz", hash = "sha256:1eb0dc091919cd384490de890cb5dfd538410a6d4b3b54eef09fb8c50b409b1c"}, -] - -[[package]] -name = "json5" -version = "0.9.25" -description = "A Python implementation of the JSON5 data format." -optional = false -python-versions = ">=3.8" -files = [ - {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, - {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, -] - -[[package]] -name = "jsonpointer" -version = "2.4" -description = "Identify specific nodes in a JSON document (RFC 6901)" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" -files = [ - {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, - {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, -] - -[[package]] -name = "jsonschema" -version = "4.17.3" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, - {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, -] - -[package.dependencies] -attrs = ">=17.4.0" -fqdn = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -idna = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} -isoduration = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format-nongpl\""} -pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" -rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -rfc3986-validator = {version = ">0.1.0", optional = true, markers = "extra == \"format-nongpl\""} -uri-template = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -webcolors = {version = ">=1.11", optional = true, markers = "extra == \"format-nongpl\""} - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "jupyter-client" -version = "7.4.1" -description = "Jupyter protocol implementation and client libraries" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyter_client-7.4.1-py3-none-any.whl", hash = "sha256:bbf6404ef64afff3d4f583c90c6335545959657b7dec3a86c440d7b1368407ea"}, - {file = "jupyter_client-7.4.1.tar.gz", hash = "sha256:3f30b1f27dc61adf5d0f5d466ef7185f6a19140c54395600b6df9adbe7d5ebcf"}, -] - -[package.dependencies] -entrypoints = "*" -jupyter-core = ">=4.9.2" -nest-asyncio = ">=1.5.4" -python-dateutil = ">=2.8.2" -pyzmq = ">=23.0" -tornado = ">=6.2" -traitlets = "*" - -[package.extras] -doc = ["ipykernel", "myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] -test = ["codecov", "coverage", "ipykernel (>=6.5)", "ipython", "mypy", "pre-commit", "pytest", "pytest-asyncio (>=0.18)", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "jupyter-core" -version = "5.7.2" -description = "Jupyter core package. A base package on which Jupyter projects rely." -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, - {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, -] - -[package.dependencies] -platformdirs = ">=2.5" -pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} -traitlets = ">=5.3" - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] -test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "jupyter-events" -version = "0.6.3" -description = "Jupyter Event System library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyter_events-0.6.3-py3-none-any.whl", hash = "sha256:57a2749f87ba387cd1bfd9b22a0875b889237dbf2edc2121ebb22bde47036c17"}, - {file = "jupyter_events-0.6.3.tar.gz", hash = "sha256:9a6e9995f75d1b7146b436ea24d696ce3a35bfa8bfe45e0c33c334c79464d0b3"}, -] - -[package.dependencies] -jsonschema = {version = ">=3.2.0", extras = ["format-nongpl"]} -python-json-logger = ">=2.0.4" -pyyaml = ">=5.3" -rfc3339-validator = "*" -rfc3986-validator = ">=0.1.1" -traitlets = ">=5.3" - -[package.extras] -cli = ["click", "rich"] -docs = ["jupyterlite-sphinx", "myst-parser", "pydata-sphinx-theme", "sphinxcontrib-spelling"] -test = ["click", "coverage", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "pytest-cov", "rich"] - -[[package]] -name = "jupyter-lsp" -version = "2.2.5" -description = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server" -optional = true -python-versions = ">=3.8" -files = [ - {file = "jupyter-lsp-2.2.5.tar.gz", hash = "sha256:793147a05ad446f809fd53ef1cd19a9f5256fd0a2d6b7ce943a982cb4f545001"}, - {file = "jupyter_lsp-2.2.5-py3-none-any.whl", hash = "sha256:45fbddbd505f3fbfb0b6cb2f1bc5e15e83ab7c79cd6e89416b248cb3c00c11da"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jupyter-server = ">=1.1.2" - -[[package]] -name = "jupyter-server" -version = "1.23.6" -description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyter_server-1.23.6-py3-none-any.whl", hash = "sha256:ede3a5c09b075541d960bb02854b617c0ffa58706c37de92e2d1c5acdc359c20"}, - {file = "jupyter_server-1.23.6.tar.gz", hash = "sha256:fde15df6d11a053b17cf2450eea9984ec9a6f28ad3cb2caa73c31e53ea184fc1"}, -] - -[package.dependencies] -anyio = ">=3.1.0,<4" -argon2-cffi = "*" -jinja2 = "*" -jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -nbconvert = ">=6.4.4" -nbformat = ">=5.2.0" -packaging = "*" -prometheus-client = "*" -pywinpty = {version = "*", markers = "os_name == \"nt\""} -pyzmq = ">=17" -Send2Trash = "*" -terminado = ">=0.8.3" -tornado = ">=6.1.0" -traitlets = ">=5.1" -websocket-client = "*" - -[package.extras] -test = ["coverage", "ipykernel", "pre-commit", "pytest (>=7.0)", "pytest-console-scripts", "pytest-cov", "pytest-mock", "pytest-timeout", "pytest-tornasync", "requests"] - -[[package]] -name = "jupyter-server-fileid" -version = "0.9.2" -description = "Jupyter Server extension providing an implementation of the File ID service." -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyter_server_fileid-0.9.2-py3-none-any.whl", hash = "sha256:76a2fbcea6950968485dcd509c2d6ac417ca11e61ab1ad447a475f0878ca808f"}, - {file = "jupyter_server_fileid-0.9.2.tar.gz", hash = "sha256:ffb11460ca5f8567644f6120b25613fca8e3f3048b38d14c6e3fe1902f314a9b"}, -] - -[package.dependencies] -jupyter-events = ">=0.5.0" -jupyter-server = ">=1.15,<3" - -[package.extras] -cli = ["click"] -test = ["jupyter-server[test] (>=1.15,<3)", "pytest", "pytest-cov", "pytest-jupyter"] - -[[package]] -name = "jupyter-server-ydoc" -version = "0.8.0" -description = "A Jupyter Server Extension Providing Y Documents." -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyter_server_ydoc-0.8.0-py3-none-any.whl", hash = "sha256:969a3a1a77ed4e99487d60a74048dc9fa7d3b0dcd32e60885d835bbf7ba7be11"}, - {file = "jupyter_server_ydoc-0.8.0.tar.gz", hash = "sha256:a6fe125091792d16c962cc3720c950c2b87fcc8c3ecf0c54c84e9a20b814526c"}, -] - -[package.dependencies] -jupyter-server-fileid = ">=0.6.0,<1" -jupyter-ydoc = ">=0.2.0,<0.4.0" -ypy-websocket = ">=0.8.2,<0.9.0" - -[package.extras] -test = ["coverage", "jupyter-server[test] (>=2.0.0a0)", "pytest (>=7.0)", "pytest-cov", "pytest-timeout", "pytest-tornasync"] - -[[package]] -name = "jupyter-ydoc" -version = "0.2.5" -description = "Document structures for collaborative editing using Ypy" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyter_ydoc-0.2.5-py3-none-any.whl", hash = "sha256:5759170f112c70320a84217dd98d287699076ae65a7f88d458d57940a9f2b882"}, - {file = "jupyter_ydoc-0.2.5.tar.gz", hash = "sha256:5a02ca7449f0d875f73e8cb8efdf695dddef15a8e71378b1f4eda6b7c90f5382"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} -y-py = ">=0.6.0,<0.7.0" - -[package.extras] -dev = ["click", "jupyter-releaser"] -test = ["pre-commit", "pytest", "pytest-asyncio", "websockets (>=10.0)", "ypy-websocket (>=0.8.4,<0.9.0)"] - -[[package]] -name = "jupyterlab" -version = "3.6.7" -description = "JupyterLab computational environment" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyterlab-3.6.7-py3-none-any.whl", hash = "sha256:d92d57d402f53922bca5090654843aa08e511290dff29fdb0809eafbbeb6df98"}, - {file = "jupyterlab-3.6.7.tar.gz", hash = "sha256:2fadeaec161b0d1aec19f17721d8b803aef1d267f89c8b636b703be14f435c8f"}, -] - -[package.dependencies] -ipython = "*" -jinja2 = ">=2.1" -jupyter-core = "*" -jupyter-server = ">=1.16.0,<3" -jupyter-server-ydoc = ">=0.8.0,<0.9.0" -jupyter-ydoc = ">=0.2.4,<0.3.0" -jupyterlab-server = ">=2.19,<3.0" -nbclassic = "*" -notebook = "<7" -packaging = "*" -tomli = {version = "*", markers = "python_version < \"3.11\""} -tornado = ">=6.1.0" - -[package.extras] -docs = ["jsx-lexer", "myst-parser", "pytest", "pytest-check-links", "pytest-tornasync", "sphinx (>=1.8)", "sphinx-copybutton", "sphinx-rtd-theme"] -test = ["check-manifest", "coverage", "jupyterlab-server[test]", "pre-commit", "pytest (>=6.0)", "pytest-check-links (>=0.5)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "requests", "requests-cache", "virtualenv"] - -[[package]] -name = "jupyterlab-code-formatter" -version = "1.6.1" -description = "Code formatter for JupyterLab" -optional = true -python-versions = ">=3.6" -files = [ - {file = "jupyterlab_code_formatter-1.6.1-py3-none-any.whl", hash = "sha256:a759768cc86b7680c7867fad93390dce15baacd316fab6bb446c8bbb3487ad22"}, - {file = "jupyterlab_code_formatter-1.6.1.tar.gz", hash = "sha256:ddf084383292ceceddcaae043f4c62b93d27a85850578a280f8fded9a1b93a32"}, -] - -[package.dependencies] -jupyterlab = ">=3.0,<4.0" - -[[package]] -name = "jupyterlab-lsp" -version = "3.10.2" -description = "Coding assistance for JupyterLab with Language Server Protocol" -optional = true -python-versions = ">=3.7" -files = [ - {file = "jupyterlab-lsp-3.10.2.tar.gz", hash = "sha256:559ad4692f97f42dd6b9f0b330ab92703f02b8e45bbaf6e9cf59898a897222a0"}, - {file = "jupyterlab_lsp-3.10.2-py3-none-any.whl", hash = "sha256:4468e095c865e2dcd65db717ae280d3631a1a3d41157b97a61a7a765fd783cbd"}, -] - -[package.dependencies] -jupyter-lsp = ">=1.4.0" -jupyterlab = ">=3.1.0,<4.0.0a0" - -[[package]] -name = "jupyterlab-pygments" -version = "0.3.0" -description = "Pygments theme using JupyterLab CSS variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, - {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, -] - -[[package]] -name = "jupyterlab-server" -version = "2.24.0" -description = "A set of server components for JupyterLab and JupyterLab like applications." -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyterlab_server-2.24.0-py3-none-any.whl", hash = "sha256:5f077e142bb8dc9b843d960f940c513581bceca3793a0d80f9c67d9522c4e876"}, - {file = "jupyterlab_server-2.24.0.tar.gz", hash = "sha256:4e6f99e0a5579bbbc32e449c4dbb039561d4f1a7827d5733273ed56738f21f07"}, -] - -[package.dependencies] -babel = ">=2.10" -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jinja2 = ">=3.0.3" -json5 = ">=0.9.0" -jsonschema = ">=4.17.3" -jupyter-server = ">=1.21,<3" -packaging = ">=21.3" -requests = ">=2.28" - -[package.extras] -docs = ["autodoc-traits", "jinja2 (<3.2.0)", "mistune (<4)", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-copybutton", "sphinxcontrib-openapi (>0.8)"] -openapi = ["openapi-core (>=0.16.1,<0.17.0)", "ruamel-yaml"] -test = ["hatch", "ipykernel", "jupyterlab-server[openapi]", "openapi-spec-validator (>=0.5.1,<0.7.0)", "pytest (>=7.0)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] - -[[package]] -name = "jupyterlab-widgets" -version = "3.0.10" -description = "Jupyter interactive widgets for JupyterLab" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyterlab_widgets-3.0.10-py3-none-any.whl", hash = "sha256:dd61f3ae7a5a7f80299e14585ce6cf3d6925a96c9103c978eda293197730cb64"}, - {file = "jupyterlab_widgets-3.0.10.tar.gz", hash = "sha256:04f2ac04976727e4f9d0fa91cdc2f1ab860f965e504c29dbd6a65c882c9d04c0"}, -] - -[[package]] -name = "kiwisolver" -version = "1.4.5" -description = "A fast implementation of the Cassowary constraint solver" -optional = false -python-versions = ">=3.7" -files = [ - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, - {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, -] - -[[package]] -name = "korean-lunar-calendar" -version = "0.3.1" -description = "Korean Lunar Calendar" -optional = false -python-versions = "*" -files = [ - {file = "korean_lunar_calendar-0.3.1-py3-none-any.whl", hash = "sha256:392757135c492c4f42a604e6038042953c35c6f449dda5f27e3f86a7f9c943e5"}, - {file = "korean_lunar_calendar-0.3.1.tar.gz", hash = "sha256:eb2c485124a061016926bdea6d89efdf9b9fdbf16db55895b6cf1e5bec17b857"}, -] - -[[package]] -name = "lazy-object-proxy" -version = "1.10.0" -description = "A fast and thorough lazy object proxy." -optional = false -python-versions = ">=3.8" -files = [ - {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, - {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, -] - -[[package]] -name = "lightgbm" -version = "3.3.5" -description = "LightGBM Python Package" -optional = true -python-versions = "*" -files = [ - {file = "lightgbm-3.3.5-py3-none-macosx_10_15_x86_64.macosx_11_6_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:843dd37c00a1096a8898b80e368b3eef7f5a5d327d1fd9eb770b6f3c2e1edc49"}, - {file = "lightgbm-3.3.5-py3-none-manylinux1_x86_64.whl", hash = "sha256:044f65664c1a32c98cb619bafa97d8cd9d93c2c2d5053376aadfe509a3a3e7fa"}, - {file = "lightgbm-3.3.5-py3-none-manylinux2014_aarch64.whl", hash = "sha256:ef01e46939293a0a36729350a7384dc17a36880176d0f17d82b738bbe77e4528"}, - {file = "lightgbm-3.3.5-py3-none-win_amd64.whl", hash = "sha256:02a40745c1972cf4a2cde764c7739228f45178c2237af2df40fde7063a58ac6a"}, - {file = "lightgbm-3.3.5.tar.gz", hash = "sha256:10b8fbdcf851e4f68a1f02f38d99bdc44c7c7fb9b1a62dcf924a0d29ff73395c"}, -] - -[package.dependencies] -numpy = "*" -scikit-learn = "!=0.22.0" -scipy = "*" -wheel = "*" - -[package.extras] -dask = ["dask[array] (>=2.0.0)", "dask[dataframe] (>=2.0.0)", "dask[distributed] (>=2.0.0)", "pandas"] - -[[package]] -name = "lightning-utilities" -version = "0.11.2" -description = "Lightning toolbox for across the our ecosystem." -optional = true -python-versions = ">=3.8" -files = [ - {file = "lightning-utilities-0.11.2.tar.gz", hash = "sha256:adf4cf9c5d912fe505db4729e51d1369c6927f3a8ac55a9dff895ce5c0da08d9"}, - {file = "lightning_utilities-0.11.2-py3-none-any.whl", hash = "sha256:541f471ed94e18a28d72879338c8c52e873bb46f4c47644d89228faeb6751159"}, -] - -[package.dependencies] -packaging = ">=17.1" -setuptools = "*" -typing-extensions = "*" - -[package.extras] -cli = ["fire"] -docs = ["requests (>=2.0.0)"] -typing = ["mypy (>=1.0.0)", "types-setuptools"] - -[[package]] -name = "linearmodels" -version = "4.31" -description = "Linear Panel, Instrumental Variable, Asset Pricing, and System Regression models for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "linearmodels-4.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b8099d476526bba1f8709ffbfaf868aac1db70e7c1607b6f1aa36913ab930d9"}, - {file = "linearmodels-4.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:248b6c325997c9e322d7c270bc89d7f3c8c6bc394053bb3ccd0d061e57a9b398"}, - {file = "linearmodels-4.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cac9aa59859834fea6d0db3ad3fdcf828aa40d6226ca0043e39bec108a171ce5"}, - {file = "linearmodels-4.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3df2f7398d94ce25683c0a71c5b8c85fe588e8eae88303c4404b1391f7387195"}, - {file = "linearmodels-4.31-cp310-cp310-win_amd64.whl", hash = "sha256:6b6f23d22b373f2be628bb5e3751c08ebb99cb7be6eb7698420596efd95bdd91"}, - {file = "linearmodels-4.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b48da80ec81176745f125f1a10cb9cc549ea9dd7275da7406ad49092d925bd3"}, - {file = "linearmodels-4.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b4452934fbbf969596028f4e3cfb68fc5b31feb39f2e8dcf90c60480b7dd80b"}, - {file = "linearmodels-4.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968720a990759afd7c37f8eb54c368f94e5b40b1491339b8a649f06677f67a61"}, - {file = "linearmodels-4.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc2dca305579129bb801aa3b1fa7471304c6b94400b80bf97345be5f38fa6566"}, - {file = "linearmodels-4.31-cp311-cp311-win_amd64.whl", hash = "sha256:4cc382c922de7278e10e08f69aec18ecaf212fc86b4718eb36faf811509037b8"}, - {file = "linearmodels-4.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cb05a791250d6366099b14e9ad4ba9bb4fab5c2865dbedc4195feb599e17f699"}, - {file = "linearmodels-4.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:18b4be70fffa7291c4058bf98ed1bd3309f8be8131c382811dd83a5f3122a4ff"}, - {file = "linearmodels-4.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a3762ff2ec4d413bf5ae97169d090ab8dc96becdf54d2b465711aa2b0e47e21"}, - {file = "linearmodels-4.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5bf38f464ba420780734ce1ed7b6ec396974458afbce698d63624727f0858e2"}, - {file = "linearmodels-4.31-cp38-cp38-win32.whl", hash = "sha256:657647d22e4d05dbf444bd154fb40c9b01b516c3af7e9fe0b8624c2fcb5f9e6a"}, - {file = "linearmodels-4.31-cp38-cp38-win_amd64.whl", hash = "sha256:663084a878b9e801f0aae261dfb999af37d385f5c7d8b67930cefab490d7b166"}, - {file = "linearmodels-4.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de2579b18a277d41b215292c7514a8c325b511c1f5a35aefbfe89fdd666a8c7f"}, - {file = "linearmodels-4.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15737df58e9a13f847f6f3e2e296255082bcf2cd8183b64e629d7252a50fbb91"}, - {file = "linearmodels-4.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7c9b245f33c573cdf983dc7eecc5d593ea51b9f17799fbd89902856797359e8"}, - {file = "linearmodels-4.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f0a033e66cdf336e682f8a48de44512362ae96fad6e0b3def7f83c01f1d346e"}, - {file = "linearmodels-4.31-cp39-cp39-win32.whl", hash = "sha256:4222bd5748fa662173fc44214b7dcf04f68775550a7014045c6bb07741ae2153"}, - {file = "linearmodels-4.31-cp39-cp39-win_amd64.whl", hash = "sha256:24cda8759583a7c3d179efee9e1e502092c8794774c6334f87b2b131869fd9dd"}, - {file = "linearmodels-4.31.tar.gz", hash = "sha256:78a00ebd0360c2886357e8197faca174dc4521256a01e9f24114054bca676be9"}, -] - -[package.dependencies] -Cython = ">=0.29.21" -formulaic = ">=0.3.2" -mypy-extensions = ">=0.4" -numpy = ">=1.16" -pandas = ">=0.24" -property-cached = ">=1.6.3" -pyhdfe = ">=0.1" -scipy = ">=1.2" -setuptools-scm = {version = ">=7.0.0", extras = ["toml"]} -statsmodels = ">=0.11" - -[[package]] -name = "llvmlite" -version = "0.41.1" -description = "lightweight wrapper around basic LLVM functionality" -optional = true -python-versions = ">=3.8" -files = [ - {file = "llvmlite-0.41.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1e1029d47ee66d3a0c4d6088641882f75b93db82bd0e6178f7bd744ebce42b9"}, - {file = "llvmlite-0.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:150d0bc275a8ac664a705135e639178883293cf08c1a38de3bbaa2f693a0a867"}, - {file = "llvmlite-0.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eee5cf17ec2b4198b509272cf300ee6577229d237c98cc6e63861b08463ddc6"}, - {file = "llvmlite-0.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd0338da625346538f1173a17cabf21d1e315cf387ca21b294ff209d176e244"}, - {file = "llvmlite-0.41.1-cp310-cp310-win32.whl", hash = "sha256:fa1469901a2e100c17eb8fe2678e34bd4255a3576d1a543421356e9c14d6e2ae"}, - {file = "llvmlite-0.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:2b76acee82ea0e9304be6be9d4b3840208d050ea0dcad75b1635fa06e949a0ae"}, - {file = "llvmlite-0.41.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:210e458723436b2469d61b54b453474e09e12a94453c97ea3fbb0742ba5a83d8"}, - {file = "llvmlite-0.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:855f280e781d49e0640aef4c4af586831ade8f1a6c4df483fb901cbe1a48d127"}, - {file = "llvmlite-0.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b67340c62c93a11fae482910dc29163a50dff3dfa88bc874872d28ee604a83be"}, - {file = "llvmlite-0.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2181bb63ef3c607e6403813421b46982c3ac6bfc1f11fa16a13eaafb46f578e6"}, - {file = "llvmlite-0.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:9564c19b31a0434f01d2025b06b44c7ed422f51e719ab5d24ff03b7560066c9a"}, - {file = "llvmlite-0.41.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5940bc901fb0325970415dbede82c0b7f3e35c2d5fd1d5e0047134c2c46b3281"}, - {file = "llvmlite-0.41.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8b0a9a47c28f67a269bb62f6256e63cef28d3c5f13cbae4fab587c3ad506778b"}, - {file = "llvmlite-0.41.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8afdfa6da33f0b4226af8e64cfc2b28986e005528fbf944d0a24a72acfc9432"}, - {file = "llvmlite-0.41.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8454c1133ef701e8c050a59edd85d238ee18bb9a0eb95faf2fca8b909ee3c89a"}, - {file = "llvmlite-0.41.1-cp38-cp38-win32.whl", hash = "sha256:2d92c51e6e9394d503033ffe3292f5bef1566ab73029ec853861f60ad5c925d0"}, - {file = "llvmlite-0.41.1-cp38-cp38-win_amd64.whl", hash = "sha256:df75594e5a4702b032684d5481db3af990b69c249ccb1d32687b8501f0689432"}, - {file = "llvmlite-0.41.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:04725975e5b2af416d685ea0769f4ecc33f97be541e301054c9f741003085802"}, - {file = "llvmlite-0.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bf14aa0eb22b58c231243dccf7e7f42f7beec48970f2549b3a6acc737d1a4ba4"}, - {file = "llvmlite-0.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92c32356f669e036eb01016e883b22add883c60739bc1ebee3a1cc0249a50828"}, - {file = "llvmlite-0.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24091a6b31242bcdd56ae2dbea40007f462260bc9bdf947953acc39dffd54f8f"}, - {file = "llvmlite-0.41.1-cp39-cp39-win32.whl", hash = "sha256:880cb57ca49e862e1cd077104375b9d1dfdc0622596dfa22105f470d7bacb309"}, - {file = "llvmlite-0.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:92f093986ab92e71c9ffe334c002f96defc7986efda18397d0f08534f3ebdc4d"}, - {file = "llvmlite-0.41.1.tar.gz", hash = "sha256:f19f767a018e6ec89608e1f6b13348fa2fcde657151137cb64e56d48598a92db"}, -] - -[[package]] -name = "loguru" -version = "0.7.2" -description = "Python logging made (stupidly) simple" -optional = false -python-versions = ">=3.5" -files = [ - {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, - {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} -win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} - -[package.extras] -dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] - -[[package]] -name = "lsprotocol" -version = "2023.0.1" -description = "Python implementation of the Language Server Protocol." -optional = true -python-versions = ">=3.7" -files = [ - {file = "lsprotocol-2023.0.1-py3-none-any.whl", hash = "sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2"}, - {file = "lsprotocol-2023.0.1.tar.gz", hash = "sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d"}, -] - -[package.dependencies] -attrs = ">=21.3.0" -cattrs = "!=23.2.1" - -[[package]] -name = "lunarcalendar" -version = "0.0.9" -description = "A lunar calendar converter, including a number of lunar and solar holidays, mainly from China." -optional = true -python-versions = ">=2.7, <4" -files = [ - {file = "LunarCalendar-0.0.9-py2.py3-none-any.whl", hash = "sha256:5ef25883d73898b37edb54da9e0f04215aaa68b897fd12e9d4b79756ff91c8cb"}, - {file = "LunarCalendar-0.0.9.tar.gz", hash = "sha256:681142f22fc353c3abca4b25699e3d1aa7083ad1c268dce36ba297eca04bed5a"}, -] - -[package.dependencies] -ephem = ">=3.7.5.3" -python-dateutil = ">=2.6.1" -pytz = "*" - -[[package]] -name = "lxml" -version = "5.2.1" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -optional = false -python-versions = ">=3.6" -files = [ - {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1f7785f4f789fdb522729ae465adcaa099e2a3441519df750ebdccc481d961a1"}, - {file = "lxml-5.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cc6ee342fb7fa2471bd9b6d6fdfc78925a697bf5c2bcd0a302e98b0d35bfad3"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:794f04eec78f1d0e35d9e0c36cbbb22e42d370dda1609fb03bcd7aeb458c6377"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817d420c60a5183953c783b0547d9eb43b7b344a2c46f69513d5952a78cddf3"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2213afee476546a7f37c7a9b4ad4d74b1e112a6fafffc9185d6d21f043128c81"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b070bbe8d3f0f6147689bed981d19bbb33070225373338df755a46893528104a"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e02c5175f63effbd7c5e590399c118d5db6183bbfe8e0d118bdb5c2d1b48d937"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:d7520db34088c96cc0e0a3ad51a4fd5b401f279ee112aa2b7f8f976d8582606d"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:bcbf4af004f98793a95355980764b3d80d47117678118a44a80b721c9913436a"}, - {file = "lxml-5.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2b44bec7adf3e9305ce6cbfa47a4395667e744097faed97abb4728748ba7d47"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1c5bb205e9212d0ebddf946bc07e73fa245c864a5f90f341d11ce7b0b854475d"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2c9d147f754b1b0e723e6afb7ba1566ecb162fe4ea657f53d2139bbf894d050a"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3545039fa4779be2df51d6395e91a810f57122290864918b172d5dc7ca5bb433"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a91481dbcddf1736c98a80b122afa0f7296eeb80b72344d7f45dc9f781551f56"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2ddfe41ddc81f29a4c44c8ce239eda5ade4e7fc305fb7311759dd6229a080052"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7baf9ffc238e4bf401299f50e971a45bfcc10a785522541a6e3179c83eabf0a"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:31e9a882013c2f6bd2f2c974241bf4ba68c85eba943648ce88936d23209a2e01"}, - {file = "lxml-5.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0a15438253b34e6362b2dc41475e7f80de76320f335e70c5528b7148cac253a1"}, - {file = "lxml-5.2.1-cp310-cp310-win32.whl", hash = "sha256:6992030d43b916407c9aa52e9673612ff39a575523c5f4cf72cdef75365709a5"}, - {file = "lxml-5.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:da052e7962ea2d5e5ef5bc0355d55007407087392cf465b7ad84ce5f3e25fe0f"}, - {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:70ac664a48aa64e5e635ae5566f5227f2ab7f66a3990d67566d9907edcbbf867"}, - {file = "lxml-5.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1ae67b4e737cddc96c99461d2f75d218bdf7a0c3d3ad5604d1f5e7464a2f9ffe"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f18a5a84e16886898e51ab4b1d43acb3083c39b14c8caeb3589aabff0ee0b270"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6f2c8372b98208ce609c9e1d707f6918cc118fea4e2c754c9f0812c04ca116d"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:394ed3924d7a01b5bd9a0d9d946136e1c2f7b3dc337196d99e61740ed4bc6fe1"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d077bc40a1fe984e1a9931e801e42959a1e6598edc8a3223b061d30fbd26bbc"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:764b521b75701f60683500d8621841bec41a65eb739b8466000c6fdbc256c240"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3a6b45da02336895da82b9d472cd274b22dc27a5cea1d4b793874eead23dd14f"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:5ea7b6766ac2dfe4bcac8b8595107665a18ef01f8c8343f00710b85096d1b53a"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:e196a4ff48310ba62e53a8e0f97ca2bca83cdd2fe2934d8b5cb0df0a841b193a"}, - {file = "lxml-5.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:200e63525948e325d6a13a76ba2911f927ad399ef64f57898cf7c74e69b71095"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dae0ed02f6b075426accbf6b2863c3d0a7eacc1b41fb40f2251d931e50188dad"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:ab31a88a651039a07a3ae327d68ebdd8bc589b16938c09ef3f32a4b809dc96ef"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:df2e6f546c4df14bc81f9498bbc007fbb87669f1bb707c6138878c46b06f6510"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5dd1537e7cc06efd81371f5d1a992bd5ab156b2b4f88834ca852de4a8ea523fa"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9b9ec9c9978b708d488bec36b9e4c94d88fd12ccac3e62134a9d17ddba910ea9"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8e77c69d5892cb5ba71703c4057091e31ccf534bd7f129307a4d084d90d014b8"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a8d5c70e04aac1eda5c829a26d1f75c6e5286c74743133d9f742cda8e53b9c2f"}, - {file = "lxml-5.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c94e75445b00319c1fad60f3c98b09cd63fe1134a8a953dcd48989ef42318534"}, - {file = "lxml-5.2.1-cp311-cp311-win32.whl", hash = "sha256:4951e4f7a5680a2db62f7f4ab2f84617674d36d2d76a729b9a8be4b59b3659be"}, - {file = "lxml-5.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:5c670c0406bdc845b474b680b9a5456c561c65cf366f8db5a60154088c92d102"}, - {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:abc25c3cab9ec7fcd299b9bcb3b8d4a1231877e425c650fa1c7576c5107ab851"}, - {file = "lxml-5.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6935bbf153f9a965f1e07c2649c0849d29832487c52bb4a5c5066031d8b44fd5"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d793bebb202a6000390a5390078e945bbb49855c29c7e4d56a85901326c3b5d9"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd5562927cdef7c4f5550374acbc117fd4ecc05b5007bdfa57cc5355864e0a4"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e7259016bc4345a31af861fdce942b77c99049d6c2107ca07dc2bba2435c1d9"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:530e7c04f72002d2f334d5257c8a51bf409db0316feee7c87e4385043be136af"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59689a75ba8d7ffca577aefd017d08d659d86ad4585ccc73e43edbfc7476781a"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f9737bf36262046213a28e789cc82d82c6ef19c85a0cf05e75c670a33342ac2c"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:3a74c4f27167cb95c1d4af1c0b59e88b7f3e0182138db2501c353555f7ec57f4"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:68a2610dbe138fa8c5826b3f6d98a7cfc29707b850ddcc3e21910a6fe51f6ca0"}, - {file = "lxml-5.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f0a1bc63a465b6d72569a9bba9f2ef0334c4e03958e043da1920299100bc7c08"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c2d35a1d047efd68027817b32ab1586c1169e60ca02c65d428ae815b593e65d4"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:79bd05260359170f78b181b59ce871673ed01ba048deef4bf49a36ab3e72e80b"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:865bad62df277c04beed9478fe665b9ef63eb28fe026d5dedcb89b537d2e2ea6"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71e97313406ccf55d32cc98a533ee05c61e15d11b99215b237346171c179c0b0"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:057cdc6b86ab732cf361f8b4d8af87cf195a1f6dc5b0ff3de2dced242c2015e0"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f3bbbc998d42f8e561f347e798b85513ba4da324c2b3f9b7969e9c45b10f6169"}, - {file = "lxml-5.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:491755202eb21a5e350dae00c6d9a17247769c64dcf62d8c788b5c135e179dc4"}, - {file = "lxml-5.2.1-cp312-cp312-win32.whl", hash = "sha256:8de8f9d6caa7f25b204fc861718815d41cbcf27ee8f028c89c882a0cf4ae4134"}, - {file = "lxml-5.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:f2a9efc53d5b714b8df2b4b3e992accf8ce5bbdfe544d74d5c6766c9e1146a3a"}, - {file = "lxml-5.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:70a9768e1b9d79edca17890175ba915654ee1725975d69ab64813dd785a2bd5c"}, - {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c38d7b9a690b090de999835f0443d8aa93ce5f2064035dfc48f27f02b4afc3d0"}, - {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5670fb70a828663cc37552a2a85bf2ac38475572b0e9b91283dc09efb52c41d1"}, - {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:958244ad566c3ffc385f47dddde4145088a0ab893504b54b52c041987a8c1863"}, - {file = "lxml-5.2.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6241d4eee5f89453307c2f2bfa03b50362052ca0af1efecf9fef9a41a22bb4f"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2a66bf12fbd4666dd023b6f51223aed3d9f3b40fef06ce404cb75bafd3d89536"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:9123716666e25b7b71c4e1789ec829ed18663152008b58544d95b008ed9e21e9"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:0c3f67e2aeda739d1cc0b1102c9a9129f7dc83901226cc24dd72ba275ced4218"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5d5792e9b3fb8d16a19f46aa8208987cfeafe082363ee2745ea8b643d9cc5b45"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:88e22fc0a6684337d25c994381ed8a1580a6f5ebebd5ad41f89f663ff4ec2885"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:21c2e6b09565ba5b45ae161b438e033a86ad1736b8c838c766146eff8ceffff9"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_s390x.whl", hash = "sha256:afbbdb120d1e78d2ba8064a68058001b871154cc57787031b645c9142b937a62"}, - {file = "lxml-5.2.1-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:627402ad8dea044dde2eccde4370560a2b750ef894c9578e1d4f8ffd54000461"}, - {file = "lxml-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:e89580a581bf478d8dcb97d9cd011d567768e8bc4095f8557b21c4d4c5fea7d0"}, - {file = "lxml-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:59565f10607c244bc4c05c0c5fa0c190c990996e0c719d05deec7030c2aa8289"}, - {file = "lxml-5.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:857500f88b17a6479202ff5fe5f580fc3404922cd02ab3716197adf1ef628029"}, - {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56c22432809085b3f3ae04e6e7bdd36883d7258fcd90e53ba7b2e463efc7a6af"}, - {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a55ee573116ba208932e2d1a037cc4b10d2c1cb264ced2184d00b18ce585b2c0"}, - {file = "lxml-5.2.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:6cf58416653c5901e12624e4013708b6e11142956e7f35e7a83f1ab02f3fe456"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:64c2baa7774bc22dd4474248ba16fe1a7f611c13ac6123408694d4cc93d66dbd"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:74b28c6334cca4dd704e8004cba1955af0b778cf449142e581e404bd211fb619"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7221d49259aa1e5a8f00d3d28b1e0b76031655ca74bb287123ef56c3db92f213"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:04ab5415bf6c86e0518d57240a96c4d1fcfc3cb370bb2ac2a732b67f579e5a04"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:6ab833e4735a7e5533711a6ea2df26459b96f9eec36d23f74cafe03631647c41"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f443cdef978430887ed55112b491f670bba6462cea7a7742ff8f14b7abb98d75"}, - {file = "lxml-5.2.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:9e2addd2d1866fe112bc6f80117bcc6bc25191c5ed1bfbcf9f1386a884252ae8"}, - {file = "lxml-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:f51969bac61441fd31f028d7b3b45962f3ecebf691a510495e5d2cd8c8092dbd"}, - {file = "lxml-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b0b58fbfa1bf7367dde8a557994e3b1637294be6cf2169810375caf8571a085c"}, - {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5"}, - {file = "lxml-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:804f74efe22b6a227306dd890eecc4f8c59ff25ca35f1f14e7482bbce96ef10b"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08802f0c56ed150cc6885ae0788a321b73505d2263ee56dad84d200cab11c07a"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8c09ed18ecb4ebf23e02b8e7a22a05d6411911e6fabef3a36e4f371f4f2585"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3d30321949861404323c50aebeb1943461a67cd51d4200ab02babc58bd06a86"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:b560e3aa4b1d49e0e6c847d72665384db35b2f5d45f8e6a5c0072e0283430533"}, - {file = "lxml-5.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:058a1308914f20784c9f4674036527e7c04f7be6fb60f5d61353545aa7fcb739"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:adfb84ca6b87e06bc6b146dc7da7623395db1e31621c4785ad0658c5028b37d7"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a2dfe7e2473f9b59496247aad6e23b405ddf2e12ef0765677b0081c02d6c2c0b"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bf2e2458345d9bffb0d9ec16557d8858c9c88d2d11fed53998512504cd9df49b"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:58278b29cb89f3e43ff3e0c756abbd1518f3ee6adad9e35b51fb101c1c1daaec"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:64641a6068a16201366476731301441ce93457eb8452056f570133a6ceb15fca"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:78bfa756eab503673991bdcf464917ef7845a964903d3302c5f68417ecdc948c"}, - {file = "lxml-5.2.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:11a04306fcba10cd9637e669fd73aa274c1c09ca64af79c041aa820ea992b637"}, - {file = "lxml-5.2.1-cp38-cp38-win32.whl", hash = "sha256:66bc5eb8a323ed9894f8fa0ee6cb3e3fb2403d99aee635078fd19a8bc7a5a5da"}, - {file = "lxml-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:9676bfc686fa6a3fa10cd4ae6b76cae8be26eb5ec6811d2a325636c460da1806"}, - {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cf22b41fdae514ee2f1691b6c3cdeae666d8b7fa9434de445f12bbeee0cf48dd"}, - {file = "lxml-5.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec42088248c596dbd61d4ae8a5b004f97a4d91a9fd286f632e42e60b706718d7"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd53553ddad4a9c2f1f022756ae64abe16da1feb497edf4d9f87f99ec7cf86bd"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feaa45c0eae424d3e90d78823f3828e7dc42a42f21ed420db98da2c4ecf0a2cb"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddc678fb4c7e30cf830a2b5a8d869538bc55b28d6c68544d09c7d0d8f17694dc"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:853e074d4931dbcba7480d4dcab23d5c56bd9607f92825ab80ee2bd916edea53"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4691d60512798304acb9207987e7b2b7c44627ea88b9d77489bbe3e6cc3bd4"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:beb72935a941965c52990f3a32d7f07ce869fe21c6af8b34bf6a277b33a345d3"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:6588c459c5627fefa30139be4d2e28a2c2a1d0d1c265aad2ba1935a7863a4913"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:588008b8497667f1ddca7c99f2f85ce8511f8f7871b4a06ceede68ab62dff64b"}, - {file = "lxml-5.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6787b643356111dfd4032b5bffe26d2f8331556ecb79e15dacb9275da02866e"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7c17b64b0a6ef4e5affae6a3724010a7a66bda48a62cfe0674dabd46642e8b54"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:27aa20d45c2e0b8cd05da6d4759649170e8dfc4f4e5ef33a34d06f2d79075d57"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d4f2cc7060dc3646632d7f15fe68e2fa98f58e35dd5666cd525f3b35d3fed7f8"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff46d772d5f6f73564979cd77a4fffe55c916a05f3cb70e7c9c0590059fb29ef"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:96323338e6c14e958d775700ec8a88346014a85e5de73ac7967db0367582049b"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:52421b41ac99e9d91934e4d0d0fe7da9f02bfa7536bb4431b4c05c906c8c6919"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7a7efd5b6d3e30d81ec68ab8a88252d7c7c6f13aaa875009fe3097eb4e30b84c"}, - {file = "lxml-5.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ed777c1e8c99b63037b91f9d73a6aad20fd035d77ac84afcc205225f8f41188"}, - {file = "lxml-5.2.1-cp39-cp39-win32.whl", hash = "sha256:644df54d729ef810dcd0f7732e50e5ad1bd0a135278ed8d6bcb06f33b6b6f708"}, - {file = "lxml-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:9ca66b8e90daca431b7ca1408cae085d025326570e57749695d6a01454790e95"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b0ff53900566bc6325ecde9181d89afadc59c5ffa39bddf084aaedfe3b06a11"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd6037392f2d57793ab98d9e26798f44b8b4da2f2464388588f48ac52c489ea1"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9c07e7a45bb64e21df4b6aa623cb8ba214dfb47d2027d90eac197329bb5e94"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3249cc2989d9090eeac5467e50e9ec2d40704fea9ab72f36b034ea34ee65ca98"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f42038016852ae51b4088b2862126535cc4fc85802bfe30dea3500fdfaf1864e"}, - {file = "lxml-5.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:533658f8fbf056b70e434dff7e7aa611bcacb33e01f75de7f821810e48d1bb66"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:622020d4521e22fb371e15f580d153134bfb68d6a429d1342a25f051ec72df1c"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efa7b51824aa0ee957ccd5a741c73e6851de55f40d807f08069eb4c5a26b2baa"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c6ad0fbf105f6bcc9300c00010a2ffa44ea6f555df1a2ad95c88f5656104817"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e233db59c8f76630c512ab4a4daf5a5986da5c3d5b44b8e9fc742f2a24dbd460"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a014510830df1475176466b6087fc0c08b47a36714823e58d8b8d7709132a96"}, - {file = "lxml-5.2.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d38c8f50ecf57f0463399569aa388b232cf1a2ffb8f0a9a5412d0db57e054860"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5aea8212fb823e006b995c4dda533edcf98a893d941f173f6c9506126188860d"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff097ae562e637409b429a7ac958a20aab237a0378c42dabaa1e3abf2f896e5f"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f5d65c39f16717a47c36c756af0fb36144069c4718824b7533f803ecdf91138"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e32be23d538753a8adb6c85bd539f5fd3b15cb987404327c569dfc5fd8366e85"}, - {file = "lxml-5.2.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cc518cea79fd1e2f6c90baafa28906d4309d24f3a63e801d855e7424c5b34144"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a0af35bd8ebf84888373630f73f24e86bf016642fb8576fba49d3d6b560b7cbc"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8aca2e3a72f37bfc7b14ba96d4056244001ddcc18382bd0daa087fd2e68a354"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ca1e8188b26a819387b29c3895c47a5e618708fe6f787f3b1a471de2c4a94d9"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c8ba129e6d3b0136a0f50345b2cb3db53f6bda5dd8c7f5d83fbccba97fb5dcb5"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e998e304036198b4f6914e6a1e2b6f925208a20e2042563d9734881150c6c246"}, - {file = "lxml-5.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d3be9b2076112e51b323bdf6d5a7f8a798de55fb8d95fcb64bd179460cdc0704"}, - {file = "lxml-5.2.1.tar.gz", hash = "sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306"}, -] - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html-clean = ["lxml-html-clean"] -html5 = ["html5lib"] -htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=3.0.10)"] - -[[package]] -name = "macholib" -version = "1.16.3" -description = "Mach-O header analysis and editing" -optional = true -python-versions = "*" -files = [ - {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, - {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, -] - -[package.dependencies] -altgraph = ">=0.17" - -[[package]] -name = "markdown" -version = "3.6" -description = "Python implementation of John Gruber's Markdown." -optional = true -python-versions = ">=3.8" -files = [ - {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, - {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "markdown-it-py" -version = "1.1.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = "~=3.6" -files = [ - {file = "markdown-it-py-1.1.0.tar.gz", hash = "sha256:36be6bb3ad987bfdb839f5ba78ddf094552ca38ccbd784ae4f74a4e1419fc6e3"}, - {file = "markdown_it_py-1.1.0-py3-none-any.whl", hash = "sha256:98080fc0bc34c4f2bcf0846a096a9429acbd9d5d8e67ed34026c03c61c464389"}, -] - -[package.dependencies] -attrs = ">=19,<22" - -[package.extras] -code-style = ["pre-commit (==2.6)"] -compare = ["commonmark (>=0.9.1,<0.10.0)", "markdown (>=3.2.2,<3.3.0)", "mistletoe-ebp (>=0.10.0,<0.11.0)", "mistune (>=0.8.4,<0.9.0)", "panflute (>=1.12,<2.0)"] -linkify = ["linkify-it-py (>=1.0,<2.0)"] -plugins = ["mdit-py-plugins"] -rtd = ["myst-nb (==0.13.0a1)", "pyyaml", "sphinx (>=2,<4)", "sphinx-book-theme", "sphinx-copybutton", "sphinx-panels (>=0.4.0,<0.5.0)"] -testing = ["coverage", "psutil", "pytest (>=3.6,<4)", "pytest-benchmark (>=3.2,<4.0)", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "markupsafe" -version = "2.1.5" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, -] - -[[package]] -name = "matplotlib" -version = "3.7.5" -description = "Python plotting package" -optional = false -python-versions = ">=3.8" -files = [ - {file = "matplotlib-3.7.5-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:4a87b69cb1cb20943010f63feb0b2901c17a3b435f75349fd9865713bfa63925"}, - {file = "matplotlib-3.7.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d3ce45010fefb028359accebb852ca0c21bd77ec0f281952831d235228f15810"}, - {file = "matplotlib-3.7.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fbea1e762b28400393d71be1a02144aa16692a3c4c676ba0178ce83fc2928fdd"}, - {file = "matplotlib-3.7.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec0e1adc0ad70ba8227e957551e25a9d2995e319c29f94a97575bb90fa1d4469"}, - {file = "matplotlib-3.7.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6738c89a635ced486c8a20e20111d33f6398a9cbebce1ced59c211e12cd61455"}, - {file = "matplotlib-3.7.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1210b7919b4ed94b5573870f316bca26de3e3b07ffdb563e79327dc0e6bba515"}, - {file = "matplotlib-3.7.5-cp310-cp310-win32.whl", hash = "sha256:068ebcc59c072781d9dcdb82f0d3f1458271c2de7ca9c78f5bd672141091e9e1"}, - {file = "matplotlib-3.7.5-cp310-cp310-win_amd64.whl", hash = "sha256:f098ffbaab9df1e3ef04e5a5586a1e6b1791380698e84938d8640961c79b1fc0"}, - {file = "matplotlib-3.7.5-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:f65342c147572673f02a4abec2d5a23ad9c3898167df9b47c149f32ce61ca078"}, - {file = "matplotlib-3.7.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4ddf7fc0e0dc553891a117aa083039088d8a07686d4c93fb8a810adca68810af"}, - {file = "matplotlib-3.7.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ccb830fc29442360d91be48527809f23a5dcaee8da5f4d9b2d5b867c1b087b8"}, - {file = "matplotlib-3.7.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efc6bb28178e844d1f408dd4d6341ee8a2e906fc9e0fa3dae497da4e0cab775d"}, - {file = "matplotlib-3.7.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b15c4c2d374f249f324f46e883340d494c01768dd5287f8bc00b65b625ab56c"}, - {file = "matplotlib-3.7.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d028555421912307845e59e3de328260b26d055c5dac9b182cc9783854e98fb"}, - {file = "matplotlib-3.7.5-cp311-cp311-win32.whl", hash = "sha256:fe184b4625b4052fa88ef350b815559dd90cc6cc8e97b62f966e1ca84074aafa"}, - {file = "matplotlib-3.7.5-cp311-cp311-win_amd64.whl", hash = "sha256:084f1f0f2f1010868c6f1f50b4e1c6f2fb201c58475494f1e5b66fed66093647"}, - {file = "matplotlib-3.7.5-cp312-cp312-macosx_10_12_universal2.whl", hash = "sha256:34bceb9d8ddb142055ff27cd7135f539f2f01be2ce0bafbace4117abe58f8fe4"}, - {file = "matplotlib-3.7.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c5a2134162273eb8cdfd320ae907bf84d171de948e62180fa372a3ca7cf0f433"}, - {file = "matplotlib-3.7.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:039ad54683a814002ff37bf7981aa1faa40b91f4ff84149beb53d1eb64617980"}, - {file = "matplotlib-3.7.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d742ccd1b09e863b4ca58291728db645b51dab343eebb08d5d4b31b308296ce"}, - {file = "matplotlib-3.7.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:743b1c488ca6a2bc7f56079d282e44d236bf375968bfd1b7ba701fd4d0fa32d6"}, - {file = "matplotlib-3.7.5-cp312-cp312-win_amd64.whl", hash = "sha256:fbf730fca3e1f23713bc1fae0a57db386e39dc81ea57dc305c67f628c1d7a342"}, - {file = "matplotlib-3.7.5-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:cfff9b838531698ee40e40ea1a8a9dc2c01edb400b27d38de6ba44c1f9a8e3d2"}, - {file = "matplotlib-3.7.5-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:1dbcca4508bca7847fe2d64a05b237a3dcaec1f959aedb756d5b1c67b770c5ee"}, - {file = "matplotlib-3.7.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4cdf4ef46c2a1609a50411b66940b31778db1e4b73d4ecc2eaa40bd588979b13"}, - {file = "matplotlib-3.7.5-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:167200ccfefd1674b60e957186dfd9baf58b324562ad1a28e5d0a6b3bea77905"}, - {file = "matplotlib-3.7.5-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:53e64522934df6e1818b25fd48cf3b645b11740d78e6ef765fbb5fa5ce080d02"}, - {file = "matplotlib-3.7.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3e3bc79b2d7d615067bd010caff9243ead1fc95cf735c16e4b2583173f717eb"}, - {file = "matplotlib-3.7.5-cp38-cp38-win32.whl", hash = "sha256:6b641b48c6819726ed47c55835cdd330e53747d4efff574109fd79b2d8a13748"}, - {file = "matplotlib-3.7.5-cp38-cp38-win_amd64.whl", hash = "sha256:f0b60993ed3488b4532ec6b697059897891927cbfc2b8d458a891b60ec03d9d7"}, - {file = "matplotlib-3.7.5-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:090964d0afaff9c90e4d8de7836757e72ecfb252fb02884016d809239f715651"}, - {file = "matplotlib-3.7.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:9fc6fcfbc55cd719bc0bfa60bde248eb68cf43876d4c22864603bdd23962ba25"}, - {file = "matplotlib-3.7.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7cc3078b019bb863752b8b60e8b269423000f1603cb2299608231996bd9d54"}, - {file = "matplotlib-3.7.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e4e9a868e8163abaaa8259842d85f949a919e1ead17644fb77a60427c90473c"}, - {file = "matplotlib-3.7.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa7ebc995a7d747dacf0a717d0eb3aa0f0c6a0e9ea88b0194d3a3cd241a1500f"}, - {file = "matplotlib-3.7.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3785bfd83b05fc0e0c2ae4c4a90034fe693ef96c679634756c50fe6efcc09856"}, - {file = "matplotlib-3.7.5-cp39-cp39-win32.whl", hash = "sha256:29b058738c104d0ca8806395f1c9089dfe4d4f0f78ea765c6c704469f3fffc81"}, - {file = "matplotlib-3.7.5-cp39-cp39-win_amd64.whl", hash = "sha256:fd4028d570fa4b31b7b165d4a685942ae9cdc669f33741e388c01857d9723eab"}, - {file = "matplotlib-3.7.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2a9a3f4d6a7f88a62a6a18c7e6a84aedcaf4faf0708b4ca46d87b19f1b526f88"}, - {file = "matplotlib-3.7.5-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9b3fd853d4a7f008a938df909b96db0b454225f935d3917520305b90680579c"}, - {file = "matplotlib-3.7.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0ad550da9f160737d7890217c5eeed4337d07e83ca1b2ca6535078f354e7675"}, - {file = "matplotlib-3.7.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:20da7924a08306a861b3f2d1da0d1aa9a6678e480cf8eacffe18b565af2813e7"}, - {file = "matplotlib-3.7.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b45c9798ea6bb920cb77eb7306409756a7fab9db9b463e462618e0559aecb30e"}, - {file = "matplotlib-3.7.5-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a99866267da1e561c7776fe12bf4442174b79aac1a47bd7e627c7e4d077ebd83"}, - {file = "matplotlib-3.7.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b6aa62adb6c268fc87d80f963aca39c64615c31830b02697743c95590ce3fbb"}, - {file = "matplotlib-3.7.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e530ab6a0afd082d2e9c17eb1eb064a63c5b09bb607b2b74fa41adbe3e162286"}, - {file = "matplotlib-3.7.5.tar.gz", hash = "sha256:1e5c971558ebc811aa07f54c7b7c677d78aa518ef4c390e14673a09e0860184a"}, -] - -[package.dependencies] -contourpy = ">=1.0.1" -cycler = ">=0.10" -fonttools = ">=4.22.0" -importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} -kiwisolver = ">=1.0.1" -numpy = ">=1.20,<2" -packaging = ">=20.0" -pillow = ">=6.2.0" -pyparsing = ">=2.3.1" -python-dateutil = ">=2.7" - -[[package]] -name = "matplotlib-inline" -version = "0.1.7" -description = "Inline Matplotlib backend for Jupyter" -optional = false -python-versions = ">=3.8" -files = [ - {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, - {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, -] - -[package.dependencies] -traitlets = "*" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mdit-py-plugins" -version = "0.2.8" -description = "Collection of plugins for markdown-it-py" -optional = false -python-versions = "~=3.6" -files = [ - {file = "mdit-py-plugins-0.2.8.tar.gz", hash = "sha256:5991cef645502e80a5388ec4fc20885d2313d4871e8b8e320ca2de14ac0c015f"}, - {file = "mdit_py_plugins-0.2.8-py3-none-any.whl", hash = "sha256:1833bf738e038e35d89cb3a07eb0d227ed647ce7dd357579b65343740c6d249c"}, -] - -[package.dependencies] -markdown-it-py = ">=1.0,<2.0" - -[package.extras] -code-style = ["pre-commit (==2.6)"] -rtd = ["myst-parser (==0.14.0a3)", "sphinx-book-theme (>=0.1.0,<0.2.0)"] -testing = ["coverage", "pytest (>=3.6,<4)", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "mistune" -version = "3.0.2" -description = "A sane and fast Markdown parser with useful plugins and renderers" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, - {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, -] - -[[package]] -name = "mock" -version = "4.0.3" -description = "Rolling backport of unittest.mock for all Pythons" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, - {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, -] - -[package.extras] -build = ["blurb", "twine", "wheel"] -docs = ["sphinx"] -test = ["pytest (<5.4)", "pytest-cov"] - -[[package]] -name = "monotonic" -version = "1.6" -description = "An implementation of time.monotonic() for Python 2 & < 3.3" -optional = false -python-versions = "*" -files = [ - {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, - {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, -] - -[[package]] -name = "more-itertools" -version = "10.2.0" -description = "More routines for operating on iterables, beyond itertools" -optional = false -python-versions = ">=3.8" -files = [ - {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, - {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, -] - -[[package]] -name = "mplfinance" -version = "0.12.10b0" -description = "Utilities for the visualization, and visual analysis, of financial data" -optional = false -python-versions = "*" -files = [ - {file = "mplfinance-0.12.10b0-py3-none-any.whl", hash = "sha256:76d3b095f05ff35de730751649de063bea4064d0c49b21b6182c82997a7f52bb"}, - {file = "mplfinance-0.12.10b0.tar.gz", hash = "sha256:7da150b5851aa5119ad6e06b55e48338b619bb6773f1b85df5de67a5ffd917bf"}, -] - -[package.dependencies] -matplotlib = "*" -pandas = "*" - -[[package]] -name = "mstarpy" -version = "0.0.4" -description = "Mutual funds data extraction from MorningStar with Python" -optional = false -python-versions = "*" -files = [ - {file = "mstarpy-0.0.4-py3-none-any.whl", hash = "sha256:afc97588deb25170851f5290e70129930a2373f156509e83c327ada4dc3c16e9"}, - {file = "mstarpy-0.0.4.tar.gz", hash = "sha256:32a75beeb039ebdaf5e40071000e31d78a18e0f54575227dfbabc46084e15f2a"}, -] - -[package.dependencies] -beautifulsoup4 = ">=4.11.1" -pandas = ">=1.4.3" -requests = ">=2.28.1" - -[[package]] -name = "multidict" -version = "6.0.5" -description = "multidict implementation" -optional = false -python-versions = ">=3.7" -files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, -] - -[[package]] -name = "multitasking" -version = "0.0.11" -description = "Non-blocking Python methods using decorators" -optional = false -python-versions = "*" -files = [ - {file = "multitasking-0.0.11-py3-none-any.whl", hash = "sha256:1e5b37a5f8fc1e6cfaafd1a82b6b1cc6d2ed20037d3b89c25a84f499bd7b3dd4"}, - {file = "multitasking-0.0.11.tar.gz", hash = "sha256:4d6bc3cc65f9b2dca72fb5a787850a88dae8f620c2b36ae9b55248e51bcd6026"}, -] - -[[package]] -name = "mutagen" -version = "1.47.0" -description = "read and write audio tags for many formats" -optional = true -python-versions = ">=3.7" -files = [ - {file = "mutagen-1.47.0-py3-none-any.whl", hash = "sha256:edd96f50c5907a9539d8e5bba7245f62c9f520aef333d13392a79a4f70aca719"}, - {file = "mutagen-1.47.0.tar.gz", hash = "sha256:719fadef0a978c31b4cf3c956261b3c58b6948b32023078a2117b1de09f0fc99"}, -] - -[[package]] -name = "mypy" -version = "1.9.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, - {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, - {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, - {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, - {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, - {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, - {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, - {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, - {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, - {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, - {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, - {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, - {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, - {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, - {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, - {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, - {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, - {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, - {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, - {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, - {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, - {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, - {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, -] - -[package.dependencies] -mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "myst-parser" -version = "0.15.2" -description = "An extended commonmark compliant parser, with bridges to docutils & sphinx." -optional = false -python-versions = ">=3.6" -files = [ - {file = "myst-parser-0.15.2.tar.gz", hash = "sha256:f7f3b2d62db7655cde658eb5d62b2ec2a4631308137bd8d10f296a40d57bbbeb"}, - {file = "myst_parser-0.15.2-py3-none-any.whl", hash = "sha256:40124b6f27a4c42ac7f06b385e23a9dcd03d84801e9c7130b59b3729a554b1f9"}, -] - -[package.dependencies] -docutils = ">=0.15,<0.18" -jinja2 = "*" -markdown-it-py = ">=1.0.0,<2.0.0" -mdit-py-plugins = ">=0.2.8,<0.3.0" -pyyaml = "*" -sphinx = ">=3.1,<5" - -[package.extras] -code-style = ["pre-commit (>=2.12,<3.0)"] -linkify = ["linkify-it-py (>=1.0,<2.0)"] -rtd = ["ipython", "sphinx-book-theme (>=0.1.0,<0.2.0)", "sphinx-panels (>=0.5.2,<0.6.0)", "sphinxcontrib-bibtex (>=2.1,<3.0)", "sphinxcontrib.mermaid (>=0.6.3,<0.7.0)", "sphinxext-opengraph (>=0.4.2,<0.5.0)", "sphinxext-rediraffe (>=0.2,<1.0)"] -testing = ["beautifulsoup4", "coverage", "docutils (>=0.17.0,<0.18.0)", "pytest (>=3.6,<4)", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "nbclassic" -version = "1.0.0" -description = "Jupyter Notebook as a Jupyter Server extension." -optional = false -python-versions = ">=3.7" -files = [ - {file = "nbclassic-1.0.0-py3-none-any.whl", hash = "sha256:f99e4769b4750076cd4235c044b61232110733322384a94a63791d2e7beacc66"}, - {file = "nbclassic-1.0.0.tar.gz", hash = "sha256:0ae11eb2319455d805596bf320336cda9554b41d99ab9a3c31bf8180bffa30e3"}, -] - -[package.dependencies] -argon2-cffi = "*" -ipykernel = "*" -ipython-genutils = "*" -jinja2 = "*" -jupyter-client = ">=6.1.1" -jupyter-core = ">=4.6.1" -jupyter-server = ">=1.8" -nbconvert = ">=5" -nbformat = "*" -nest-asyncio = ">=1.5" -notebook-shim = ">=0.2.3" -prometheus-client = "*" -pyzmq = ">=17" -Send2Trash = ">=1.8.0" -terminado = ">=0.8.3" -tornado = ">=6.1" -traitlets = ">=4.2.1" - -[package.extras] -docs = ["myst-parser", "nbsphinx", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] -json-logging = ["json-logging"] -test = ["coverage", "nbval", "pytest", "pytest-cov", "pytest-jupyter", "pytest-playwright", "pytest-tornasync", "requests", "requests-unixsocket", "testpath"] - -[[package]] -name = "nbclient" -version = "0.10.0" -description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, -] - -[package.dependencies] -jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -nbformat = ">=5.1" -traitlets = ">=5.4" - -[package.extras] -dev = ["pre-commit"] -docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] -test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] - -[[package]] -name = "nbconvert" -version = "7.16.3" -description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." -optional = false -python-versions = ">=3.8" -files = [ - {file = "nbconvert-7.16.3-py3-none-any.whl", hash = "sha256:ddeff14beeeedf3dd0bc506623e41e4507e551736de59df69a91f86700292b3b"}, - {file = "nbconvert-7.16.3.tar.gz", hash = "sha256:a6733b78ce3d47c3f85e504998495b07e6ea9cf9bf6ec1c98dda63ec6ad19142"}, -] - -[package.dependencies] -beautifulsoup4 = "*" -bleach = "!=5.0.0" -defusedxml = "*" -importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} -jinja2 = ">=3.0" -jupyter-core = ">=4.7" -jupyterlab-pygments = "*" -markupsafe = ">=2.0" -mistune = ">=2.0.3,<4" -nbclient = ">=0.5.0" -nbformat = ">=5.7" -packaging = "*" -pandocfilters = ">=1.4.1" -pygments = ">=2.4.1" -tinycss2 = "*" -traitlets = ">=5.1" - -[package.extras] -all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"] -docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] -qtpdf = ["nbconvert[qtpng]"] -qtpng = ["pyqtwebengine (>=5.15)"] -serve = ["tornado (>=6.1)"] -test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"] -webpdf = ["playwright"] - -[[package]] -name = "nbformat" -version = "5.10.4" -description = "The Jupyter Notebook format" -optional = false -python-versions = ">=3.8" -files = [ - {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, - {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, -] - -[package.dependencies] -fastjsonschema = ">=2.15" -jsonschema = ">=2.6" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -traitlets = ">=5.1" - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["pep440", "pre-commit", "pytest", "testpath"] - -[[package]] -name = "nbmake" -version = "1.1" -description = "Pytest plugin for testing notebooks" -optional = false -python-versions = ">=3.6.1,<4.0.0" -files = [ - {file = "nbmake-1.1-py3-none-any.whl", hash = "sha256:04603dd8b7894830f7079b317bc81df29b15ddd0db014279c58145b6fce80d7a"}, - {file = "nbmake-1.1.tar.gz", hash = "sha256:d59158797cecb7d7b248a061854fbd7fa0bdd9ff2c19b0d0bae8ee2ca90ffd13"}, -] - -[package.dependencies] -ipykernel = ">=5.4.0" -nbclient = ">=0.3,<1.0" -nbformat = ">=5.0.8,<6.0.0" -pydantic = ">=1.7.2,<2.0.0" -Pygments = ">=2.7.3,<3.0.0" -pytest = ">=6.1.0,<7.0.0" - -[[package]] -name = "nest-asyncio" -version = "1.6.0" -description = "Patch asyncio to allow nested event loops" -optional = false -python-versions = ">=3.5" -files = [ - {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, - {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, -] - -[[package]] -name = "networkx" -version = "3.1" -description = "Python package for creating and manipulating graphs and networks" -optional = true -python-versions = ">=3.8" -files = [ - {file = "networkx-3.1-py3-none-any.whl", hash = "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36"}, - {file = "networkx-3.1.tar.gz", hash = "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61"}, -] - -[package.extras] -default = ["matplotlib (>=3.4)", "numpy (>=1.20)", "pandas (>=1.3)", "scipy (>=1.8)"] -developer = ["mypy (>=1.1)", "pre-commit (>=3.2)"] -doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.13)", "sphinx (>=6.1)", "sphinx-gallery (>=0.12)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.10)", "sympy (>=1.10)"] -test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] - -[[package]] -name = "nfoursid" -version = "1.0.1" -description = "Implementation of N4SID, Kalman filtering and state-space models" -optional = true -python-versions = ">=3.7" -files = [ - {file = "nfoursid-1.0.1-py3-none-any.whl", hash = "sha256:cd780c40a30ddf81c1d67014e6abd6626360334a65646e16dccd6e6831afc795"}, - {file = "nfoursid-1.0.1.tar.gz", hash = "sha256:d481e8ad58f19eba4292498ea4fd1324572a31c776fe6cf2ca774ea42448c04b"}, -] - -[package.dependencies] -matplotlib = ">=3.3" -numpy = ">=1.19" -pandas = ">=1.1" - -[[package]] -name = "nixtlats" -version = "0.1.21" -description = "TimeGPT SDK" -optional = false -python-versions = ">=3.7" -files = [ - {file = "nixtlats-0.1.21-py3-none-any.whl", hash = "sha256:9706dad5b1d9b2706df0f2f0fcd6144235bdcb96826691c6e91ecf3ac182a13a"}, - {file = "nixtlats-0.1.21.tar.gz", hash = "sha256:f15a657bc5314bb794e816bf4ba76fd7b09ced4d0a95ecf9ea7041d4b48cba58"}, -] - -[package.dependencies] -httpx = "*" -pandas = "*" -pydantic = "<2" -requests = "*" -tenacity = "*" -utilsforecast = ">=0.0.13" - -[package.extras] -date-extras = ["holidays"] -dev = ["black", "dask", "fugue[ray] (>=0.8.7)", "holidays", "nbdev", "plotly", "pyspark", "python-dotenv", "ray[serve-grpc]", "statsforecast", "utilsforecast[plotting] (>=0.0.5)"] -distributed = ["dask", "fugue[ray] (>=0.8.7)", "pyspark", "ray[serve-grpc]"] -plotting = ["utilsforecast[plotting] (>=0.0.5)"] - -[[package]] -name = "nltk" -version = "3.8.1" -description = "Natural Language Toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "nltk-3.8.1-py3-none-any.whl", hash = "sha256:fd5c9109f976fa86bcadba8f91e47f5e9293bd034474752e92a520f81c93dda5"}, - {file = "nltk-3.8.1.zip", hash = "sha256:1834da3d0682cba4f2cede2f9aad6b0fafb6461ba451db0efb6f9c39798d64d3"}, -] - -[package.dependencies] -click = "*" -joblib = "*" -regex = ">=2021.8.3" -tqdm = "*" - -[package.extras] -all = ["matplotlib", "numpy", "pyparsing", "python-crfsuite", "requests", "scikit-learn", "scipy", "twython"] -corenlp = ["requests"] -machine-learning = ["numpy", "python-crfsuite", "scikit-learn", "scipy"] -plot = ["matplotlib"] -tgrep = ["pyparsing"] -twitter = ["twython"] - -[[package]] -name = "nodeenv" -version = "1.8.0" -description = "Node.js virtual environment builder" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" -files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, -] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "notebook" -version = "6.5.6" -description = "A web-based notebook environment for interactive computing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "notebook-6.5.6-py3-none-any.whl", hash = "sha256:c1e2eb2e3b6079a0552a04974883a48d04c3c05792170d64a4b23d707d453181"}, - {file = "notebook-6.5.6.tar.gz", hash = "sha256:b4625a4b7a597839dd3156b140d5ba2c7123761f98245a3290f67a8b8ee048d9"}, -] - -[package.dependencies] -argon2-cffi = "*" -ipykernel = "*" -ipython-genutils = "*" -jinja2 = "*" -jupyter-client = ">=5.3.4,<8" -jupyter-core = ">=4.6.1" -nbclassic = ">=0.4.7" -nbconvert = ">=5" -nbformat = "*" -nest-asyncio = ">=1.5" -prometheus-client = "*" -pyzmq = ">=17,<25" -Send2Trash = ">=1.8.0" -terminado = ">=0.8.3" -tornado = ">=6.1" -traitlets = ">=4.2.1" - -[package.extras] -docs = ["myst-parser", "nbsphinx", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] -json-logging = ["json-logging"] -test = ["coverage", "nbval", "pytest", "pytest-cov", "requests", "requests-unixsocket", "selenium (==4.1.5)", "testpath"] - -[[package]] -name = "notebook-shim" -version = "0.2.4" -description = "A shim layer for notebook traits and config" -optional = false -python-versions = ">=3.7" -files = [ - {file = "notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef"}, - {file = "notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb"}, -] - -[package.dependencies] -jupyter-server = ">=1.8,<3" - -[package.extras] -test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"] - -[[package]] -name = "numba" -version = "0.58.1" -description = "compiling Python code using LLVM" -optional = true -python-versions = ">=3.8" -files = [ - {file = "numba-0.58.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07f2fa7e7144aa6f275f27260e73ce0d808d3c62b30cff8906ad1dec12d87bbe"}, - {file = "numba-0.58.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7bf1ddd4f7b9c2306de0384bf3854cac3edd7b4d8dffae2ec1b925e4c436233f"}, - {file = "numba-0.58.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bc2d904d0319d7a5857bd65062340bed627f5bfe9ae4a495aef342f072880d50"}, - {file = "numba-0.58.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4e79b6cc0d2bf064a955934a2e02bf676bc7995ab2db929dbbc62e4c16551be6"}, - {file = "numba-0.58.1-cp310-cp310-win_amd64.whl", hash = "sha256:81fe5b51532478149b5081311b0fd4206959174e660c372b94ed5364cfb37c82"}, - {file = "numba-0.58.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bcecd3fb9df36554b342140a4d77d938a549be635d64caf8bd9ef6c47a47f8aa"}, - {file = "numba-0.58.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1eaa744f518bbd60e1f7ccddfb8002b3d06bd865b94a5d7eac25028efe0e0ff"}, - {file = "numba-0.58.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bf68df9c307fb0aa81cacd33faccd6e419496fdc621e83f1efce35cdc5e79cac"}, - {file = "numba-0.58.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:55a01e1881120e86d54efdff1be08381886fe9f04fc3006af309c602a72bc44d"}, - {file = "numba-0.58.1-cp311-cp311-win_amd64.whl", hash = "sha256:811305d5dc40ae43c3ace5b192c670c358a89a4d2ae4f86d1665003798ea7a1a"}, - {file = "numba-0.58.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ea5bfcf7d641d351c6a80e8e1826eb4a145d619870016eeaf20bbd71ef5caa22"}, - {file = "numba-0.58.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e63d6aacaae1ba4ef3695f1c2122b30fa3d8ba039c8f517784668075856d79e2"}, - {file = "numba-0.58.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6fe7a9d8e3bd996fbe5eac0683227ccef26cba98dae6e5cee2c1894d4b9f16c1"}, - {file = "numba-0.58.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:898af055b03f09d33a587e9425500e5be84fc90cd2f80b3fb71c6a4a17a7e354"}, - {file = "numba-0.58.1-cp38-cp38-win_amd64.whl", hash = "sha256:d3e2fe81fe9a59fcd99cc572002101119059d64d31eb6324995ee8b0f144a306"}, - {file = "numba-0.58.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c765aef472a9406a97ea9782116335ad4f9ef5c9f93fc05fd44aab0db486954"}, - {file = "numba-0.58.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e9356e943617f5e35a74bf56ff6e7cc83e6b1865d5e13cee535d79bf2cae954"}, - {file = "numba-0.58.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:240e7a1ae80eb6b14061dc91263b99dc8d6af9ea45d310751b780888097c1aaa"}, - {file = "numba-0.58.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:45698b995914003f890ad839cfc909eeb9c74921849c712a05405d1a79c50f68"}, - {file = "numba-0.58.1-cp39-cp39-win_amd64.whl", hash = "sha256:bd3dda77955be03ff366eebbfdb39919ce7c2620d86c906203bed92124989032"}, - {file = "numba-0.58.1.tar.gz", hash = "sha256:487ded0633efccd9ca3a46364b40006dbdaca0f95e99b8b83e778d1195ebcbaa"}, -] - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.9\""} -llvmlite = "==0.41.*" -numpy = ">=1.22,<1.27" - -[[package]] -name = "numpy" -version = "1.23.4" -description = "NumPy is the fundamental package for array computing with Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "numpy-1.23.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:95d79ada05005f6f4f337d3bb9de8a7774f259341c70bc88047a1f7b96a4bcb2"}, - {file = "numpy-1.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:926db372bc4ac1edf81cfb6c59e2a881606b409ddc0d0920b988174b2e2a767f"}, - {file = "numpy-1.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c237129f0e732885c9a6076a537e974160482eab8f10db6292e92154d4c67d71"}, - {file = "numpy-1.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8365b942f9c1a7d0f0dc974747d99dd0a0cdfc5949a33119caf05cb314682d3"}, - {file = "numpy-1.23.4-cp310-cp310-win32.whl", hash = "sha256:2341f4ab6dba0834b685cce16dad5f9b6606ea8a00e6da154f5dbded70fdc4dd"}, - {file = "numpy-1.23.4-cp310-cp310-win_amd64.whl", hash = "sha256:d331afac87c92373826af83d2b2b435f57b17a5c74e6268b79355b970626e329"}, - {file = "numpy-1.23.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:488a66cb667359534bc70028d653ba1cf307bae88eab5929cd707c761ff037db"}, - {file = "numpy-1.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce03305dd694c4873b9429274fd41fc7eb4e0e4dea07e0af97a933b079a5814f"}, - {file = "numpy-1.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8981d9b5619569899666170c7c9748920f4a5005bf79c72c07d08c8a035757b0"}, - {file = "numpy-1.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a70a7d3ce4c0e9284e92285cba91a4a3f5214d87ee0e95928f3614a256a1488"}, - {file = "numpy-1.23.4-cp311-cp311-win32.whl", hash = "sha256:5e13030f8793e9ee42f9c7d5777465a560eb78fa7e11b1c053427f2ccab90c79"}, - {file = "numpy-1.23.4-cp311-cp311-win_amd64.whl", hash = "sha256:7607b598217745cc40f751da38ffd03512d33ec06f3523fb0b5f82e09f6f676d"}, - {file = "numpy-1.23.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7ab46e4e7ec63c8a5e6dbf5c1b9e1c92ba23a7ebecc86c336cb7bf3bd2fb10e5"}, - {file = "numpy-1.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8aae2fb3180940011b4862b2dd3756616841c53db9734b27bb93813cd79fce6"}, - {file = "numpy-1.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c053d7557a8f022ec823196d242464b6955a7e7e5015b719e76003f63f82d0f"}, - {file = "numpy-1.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0882323e0ca4245eb0a3d0a74f88ce581cc33aedcfa396e415e5bba7bf05f68"}, - {file = "numpy-1.23.4-cp38-cp38-win32.whl", hash = "sha256:dada341ebb79619fe00a291185bba370c9803b1e1d7051610e01ed809ef3a4ba"}, - {file = "numpy-1.23.4-cp38-cp38-win_amd64.whl", hash = "sha256:0fe563fc8ed9dc4474cbf70742673fc4391d70f4363f917599a7fa99f042d5a8"}, - {file = "numpy-1.23.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c67b833dbccefe97cdd3f52798d430b9d3430396af7cdb2a0c32954c3ef73894"}, - {file = "numpy-1.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f76025acc8e2114bb664294a07ede0727aa75d63a06d2fae96bf29a81747e4a7"}, - {file = "numpy-1.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12ac457b63ec8ded85d85c1e17d85efd3c2b0967ca39560b307a35a6703a4735"}, - {file = "numpy-1.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95de7dc7dc47a312f6feddd3da2500826defdccbc41608d0031276a24181a2c0"}, - {file = "numpy-1.23.4-cp39-cp39-win32.whl", hash = "sha256:f2f390aa4da44454db40a1f0201401f9036e8d578a25f01a6e237cea238337ef"}, - {file = "numpy-1.23.4-cp39-cp39-win_amd64.whl", hash = "sha256:f260da502d7441a45695199b4e7fd8ca87db659ba1c78f2bbf31f934fe76ae0e"}, - {file = "numpy-1.23.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61be02e3bf810b60ab74e81d6d0d36246dbfb644a462458bb53b595791251911"}, - {file = "numpy-1.23.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296d17aed51161dbad3c67ed6d164e51fcd18dbcd5dd4f9d0a9c6055dce30810"}, - {file = "numpy-1.23.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4d52914c88b4930dafb6c48ba5115a96cbab40f45740239d9f4159c4ba779962"}, - {file = "numpy-1.23.4.tar.gz", hash = "sha256:ed2cc92af0efad20198638c69bb0fc2870a58dabfba6eb722c933b48556c686c"}, -] - -[[package]] -name = "oauthlib" -version = "3.2.2" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -optional = true -python-versions = ">=3.6" -files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, -] - -[package.extras] -rsa = ["cryptography (>=3.0.0)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "openai-whisper" -version = "20230124" -description = "Robust Speech Recognition via Large-Scale Weak Supervision" -optional = true -python-versions = ">=3.7" -files = [ - {file = "openai-whisper-20230124.tar.gz", hash = "sha256:31adf9353bf0e3f891b6618896f22c65cf78cd6f845a4d5b7125aa5102187f79"}, -] - -[package.dependencies] -ffmpeg-python = "0.2.0" -more-itertools = "*" -numpy = "*" -torch = "*" -tqdm = "*" -transformers = ">=4.19.0" - -[package.extras] -dev = ["pytest"] - -[[package]] -name = "openpyxl" -version = "3.1.2" -description = "A Python library to read/write Excel 2010 xlsx/xlsm files" -optional = false -python-versions = ">=3.6" -files = [ - {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"}, - {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"}, -] - -[package.dependencies] -et-xmlfile = "*" - -[[package]] -name = "osqp" -version = "0.6.5" -description = "OSQP: The Operator Splitting QP Solver" -optional = true -python-versions = "*" -files = [ - {file = "osqp-0.6.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e8024dba07281111af39e71bff6449fb22a37bf3358aa0c7fd1daa6bca692c99"}, - {file = "osqp-0.6.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a68e247f2bbb53e87f1c1ca80ff3fc86b781f771d6da2a2ecd2f6e7492c802f3"}, - {file = "osqp-0.6.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e81e299637eb2342e30eb2df0ec45dc243683af0a71676c9b45b9337bb05da97"}, - {file = "osqp-0.6.5-cp310-cp310-win_amd64.whl", hash = "sha256:42425632927d983cbe935067783b944ebd4959e9eb6611da8401007b66a0c841"}, - {file = "osqp-0.6.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7b180db09be1c3e3cb4109396b894f481ca9c6e160a530acd71f1769610f96c"}, - {file = "osqp-0.6.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:648f4beff10c16620f3b95e86dee702052d587b847ddbd5d8f71ad39ac36db3a"}, - {file = "osqp-0.6.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7649d56d775662e0a5d1665ed220d585f904d14a49cc6931bf27725bb9c4b2e0"}, - {file = "osqp-0.6.5-cp311-cp311-win_amd64.whl", hash = "sha256:b033b7aec973a655cfec4558e0c4fc92ee9f914bcb0a669e0156398d8ddbef8f"}, - {file = "osqp-0.6.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5c344619465e625aac6d13812d442dd31d4a9ab243e39abb5938c3f6116409b0"}, - {file = "osqp-0.6.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:000ad48aa071ecc4c75ebc39d1291752fe3a9937a30d00fff5dc61663ec67eeb"}, - {file = "osqp-0.6.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a36a40df69db5195fba613341663db2c7dcf977eb75b9578a8fd7682bbe02324"}, - {file = "osqp-0.6.5-cp312-cp312-win_amd64.whl", hash = "sha256:3d8212db7c55af1961ccce4a32fd382bfe34e2198664ea3f81cc47eef8d0f288"}, - {file = "osqp-0.6.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ca7d80c0767b1350cd74e4f1446ec51661152690d38b1382ceccdfccd757afce"}, - {file = "osqp-0.6.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b15e2b96d4d9b2eff37a05405372c69cf17ada3d1e42c5e28cbdbd053189ab5"}, - {file = "osqp-0.6.5-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a41600e34ece7156606fd3620987fdf224b0a35c857540cb5bf45072f5c022b"}, - {file = "osqp-0.6.5-cp36-cp36m-win_amd64.whl", hash = "sha256:8c38574b35a3ddfb794aafee9bc5a74635160b9fc52bbc89ae6164fe207556de"}, - {file = "osqp-0.6.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d06f614e3be1b1f3cd68569b2dc3628c2fdef1e7c4b992672fe05efb1add9801"}, - {file = "osqp-0.6.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a6b995e0a022bd1c33d20d8846d9a068df89cec288b905b5cdfdb98a2ffae8"}, - {file = "osqp-0.6.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09de9b53e7513ee4ade3024ce9f36ef993d916118d0927cce740d086882ea92c"}, - {file = "osqp-0.6.5-cp37-cp37m-win_amd64.whl", hash = "sha256:1f80f85d515ef29b90fb34f137857e75d4fcf21a715d644f54d2cf9494567fab"}, - {file = "osqp-0.6.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de9b9e96001e8f0b2e474106ac75e220fd9279e1635b107b836a6035795e8d07"}, - {file = "osqp-0.6.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fe545d7a87a46cfc57dfb9f0aa2788d2f29e0c71dc1ac57e92f9c9d93064753"}, - {file = "osqp-0.6.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49ab020b5fd7abb5da99e01e47bf81f817ba1df6895e3d3ba4893722cc24d9b6"}, - {file = "osqp-0.6.5-cp38-cp38-win_amd64.whl", hash = "sha256:5d1b5ed6fc4faea94117a0abe140fefe980449b29d3907bd2e6ec1c18eca3d43"}, - {file = "osqp-0.6.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dca127b7a333ce53fb430fc441b2e0aee2df619693d967277a8f8fd095e95007"}, - {file = "osqp-0.6.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ec902844defedf7c5a5ed482b93286d1735a65b71bb27c93e18c929f313c93d"}, - {file = "osqp-0.6.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25a9e1e8f1db38094dc7ee544e603e31fe7bf1b2a3fc75c78c1d39a727e2540"}, - {file = "osqp-0.6.5-cp39-cp39-win_amd64.whl", hash = "sha256:6dce90d8c4ad551489a452573ea819e089e1e1c3b23bbd8f155bb6059ce8ef36"}, - {file = "osqp-0.6.5.tar.gz", hash = "sha256:b2810aee7be2373add8b6c0be5ad99b810288774abca421751cb032d6a5aedef"}, -] - -[package.dependencies] -numpy = ">=1.7" -qdldl = "*" -scipy = ">=0.13.2,<1.12.0" - -[[package]] -name = "packaging" -version = "24.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, -] - -[[package]] -name = "pandas" -version = "1.5.3" -description = "Powerful data structures for data analysis, time series, and statistics" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406"}, - {file = "pandas-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572"}, - {file = "pandas-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996"}, - {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354"}, - {file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23"}, - {file = "pandas-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328"}, - {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc"}, - {file = "pandas-1.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d"}, - {file = "pandas-1.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc"}, - {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae"}, - {file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6"}, - {file = "pandas-1.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003"}, - {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813"}, - {file = "pandas-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31"}, - {file = "pandas-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792"}, - {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7"}, - {file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf"}, - {file = "pandas-1.5.3-cp38-cp38-win32.whl", hash = "sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51"}, - {file = "pandas-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373"}, - {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa"}, - {file = "pandas-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee"}, - {file = "pandas-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a"}, - {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0"}, - {file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5"}, - {file = "pandas-1.5.3-cp39-cp39-win32.whl", hash = "sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a"}, - {file = "pandas-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9"}, - {file = "pandas-1.5.3.tar.gz", hash = "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1"}, -] - -[package.dependencies] -numpy = [ - {version = ">=1.20.3", markers = "python_version < \"3.10\""}, - {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, -] -python-dateutil = ">=2.8.1" -pytz = ">=2020.1" - -[package.extras] -test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] - -[[package]] -name = "pandas-datareader" -version = "0.10.0" -description = "Data readers extracted from the pandas codebase,should be compatible with recent pandas versions" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pandas-datareader-0.10.0.tar.gz", hash = "sha256:9fc3c63d39bc0c10c2683f1c6d503ff625020383e38f6cbe14134826b454d5a6"}, - {file = "pandas_datareader-0.10.0-py3-none-any.whl", hash = "sha256:0b95ff3635bc3ee1a6073521b557ab0e3c39d219f4a3b720b6b0bc6e8cdb4bb7"}, -] - -[package.dependencies] -lxml = "*" -pandas = ">=0.23" -requests = ">=2.19.0" - -[[package]] -name = "pandas-market-calendars" -version = "3.2" -description = "Market and exchange trading calendars for pandas" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "pandas_market_calendars-3.2-py3-none-any.whl", hash = "sha256:bf7509d1d40c918b6b91d261adde1e8ac7bf640f4403f45a8ac9f4d4fe47154b"}, - {file = "pandas_market_calendars-3.2.tar.gz", hash = "sha256:5c67ec7158c298aa3efc6913d0c53b82239de779611d5eec23333ff5a2927cf2"}, -] - -[package.dependencies] -exchange-calendars = ">=3.3" -pandas = ">=0.18" -python-dateutil = "*" -pytz = "*" - -[[package]] -name = "pandas-ta" -version = "0.3.14b" -description = "An easy to use Python 3 Pandas Extension with 130+ Technical Analysis Indicators. Can be called from a Pandas DataFrame or standalone like TA-Lib. Correlation tested with TA-Lib." -optional = false -python-versions = "*" -files = [ - {file = "pandas_ta-0.3.14b.tar.gz", hash = "sha256:0fa35aec831d2815ea30b871688a8d20a76b288a7be2d26cc00c35cd8c09a993"}, -] - -[package.dependencies] -pandas = "*" - -[package.extras] -dev = ["alphaVantage-api", "matplotlib", "mplfinance", "scipy", "sklearn", "statsmodels", "stochastic", "talib", "tqdm", "vectorbt", "yfinance"] -test = ["ta-lib"] - -[[package]] -name = "pandocfilters" -version = "1.5.1" -description = "Utilities for writing pandoc filters in python" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, - {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, -] - -[[package]] -name = "papermill" -version = "2.4.0" -description = "Parametrize and run Jupyter and nteract Notebooks" -optional = false -python-versions = ">=3.7" -files = [ - {file = "papermill-2.4.0-py3-none-any.whl", hash = "sha256:baa76f0441257d9a25b3ad7c895e761341b94f9a70ca98cf419247fc728932d9"}, - {file = "papermill-2.4.0.tar.gz", hash = "sha256:6f8f8a9b06b39677f207c09100c8d386bcf592f0cbbdda9f0f50e81445697627"}, -] - -[package.dependencies] -ansiwrap = "*" -click = "*" -entrypoints = "*" -nbclient = ">=0.2.0" -nbformat = ">=5.1.2" -pyyaml = "*" -requests = "*" -tenacity = "*" -tqdm = ">=4.32.2" - -[package.extras] -all = ["azure-datalake-store (>=0.0.30)", "azure-storage-blob (>=12.1.0)", "black (>=19.3b0)", "boto3", "gcsfs (>=0.2.0)", "pyarrow (>=2.0)", "requests (>=2.21.0)"] -azure = ["azure-datalake-store (>=0.0.30)", "azure-storage-blob (>=12.1.0)", "requests (>=2.21.0)"] -black = ["black (>=19.3b0)"] -dev = ["attrs (>=17.4.0)", "azure-datalake-store (>=0.0.30)", "azure-storage-blob (>=12.1.0)", "black (>=19.3b0)", "boto3", "botocore", "bumpversion", "check-manifest", "codecov", "coverage", "flake8", "gcsfs (>=0.2.0)", "google-compute-engine", "ipython (>=5.0)", "ipywidgets", "moto", "notebook", "pip (>=18.1)", "pre-commit", "pyarrow (>=2.0)", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "pytest-env (>=0.6.2)", "pytest-mock (>=1.10)", "recommonmark", "requests (>=2.21.0)", "setuptools (>=38.6.0)", "tox", "twine (>=1.11.0)", "wheel (>=0.31.0)"] -gcs = ["gcsfs (>=0.2.0)"] -github = ["PyGithub (>=1.55)"] -hdfs = ["pyarrow (>=2.0)"] -s3 = ["boto3"] -test = ["attrs (>=17.4.0)", "azure-datalake-store (>=0.0.30)", "azure-storage-blob (>=12.1.0)", "black (>=19.3b0)", "boto3", "botocore", "bumpversion", "check-manifest", "codecov", "coverage", "flake8", "gcsfs (>=0.2.0)", "google-compute-engine", "ipython (>=5.0)", "ipywidgets", "moto", "notebook", "pip (>=18.1)", "pre-commit", "pyarrow (>=2.0)", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "pytest-env (>=0.6.2)", "pytest-mock (>=1.10)", "recommonmark", "requests (>=2.21.0)", "setuptools (>=38.6.0)", "tox", "twine (>=1.11.0)", "wheel (>=0.31.0)"] - -[[package]] -name = "parso" -version = "0.8.4" -description = "A Python Parser" -optional = false -python-versions = ">=3.6" -files = [ - {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, - {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, -] - -[package.extras] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["docopt", "pytest"] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "patsy" -version = "0.5.6" -description = "A Python package for describing statistical models and for building design matrices." -optional = false -python-versions = "*" -files = [ - {file = "patsy-0.5.6-py2.py3-none-any.whl", hash = "sha256:19056886fd8fa71863fa32f0eb090267f21fb74be00f19f5c70b2e9d76c883c6"}, - {file = "patsy-0.5.6.tar.gz", hash = "sha256:95c6d47a7222535f84bff7f63d7303f2e297747a598db89cf5c67f0c0c7d2cdb"}, -] - -[package.dependencies] -numpy = ">=1.4" -six = "*" - -[package.extras] -test = ["pytest", "pytest-cov", "scipy"] - -[[package]] -name = "pbr" -version = "6.0.0" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -files = [ - {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, - {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, -] - -[[package]] -name = "peewee" -version = "3.17.3" -description = "a little orm" -optional = false -python-versions = "*" -files = [ - {file = "peewee-3.17.3.tar.gz", hash = "sha256:ef15f90b628e41a584be8306cdc3243c51f73ce88b06154d9572f6d0284a0169"}, -] - -[[package]] -name = "pefile" -version = "2023.2.7" -description = "Python PE parsing module" -optional = true -python-versions = ">=3.6.0" -files = [ - {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, - {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, -] - -[[package]] -name = "pexpect" -version = "4.9.0" -description = "Pexpect allows easy control of interactive console applications." -optional = false -python-versions = "*" -files = [ - {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, - {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, -] - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -optional = false -python-versions = "*" -files = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] - -[[package]] -name = "pillow" -version = "10.3.0" -description = "Python Imaging Library (Fork)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, - {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, - {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, - {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, - {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, - {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, - {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, - {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, - {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, - {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, - {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, - {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, - {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, - {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, - {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, - {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, - {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] -fpx = ["olefile"] -mic = ["olefile"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] -typing = ["typing-extensions"] -xmp = ["defusedxml"] - -[[package]] -name = "pkgutil-resolve-name" -version = "1.3.10" -description = "Resolve a name to an object." -optional = false -python-versions = ">=3.6" -files = [ - {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, - {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, -] - -[[package]] -name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] - -[[package]] -name = "plotly" -version = "5.21.0" -description = "An open-source, interactive data visualization library for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "plotly-5.21.0-py3-none-any.whl", hash = "sha256:a33f41fd5922e45b2b253f795b200d14452eb625790bb72d0a72cf1328a6abbf"}, - {file = "plotly-5.21.0.tar.gz", hash = "sha256:69243f8c165d4be26c0df1c6f0b7b258e2dfeefe032763404ad7e7fb7d7c2073"}, -] - -[package.dependencies] -packaging = "*" -tenacity = ">=6.2.0" - -[[package]] -name = "pluggy" -version = "1.4.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pmaw" -version = "3.0.0" -description = "A multithread Pushshift.io API Wrapper for reddit.com comment and submission searches." -optional = false -python-versions = ">=3.5" -files = [ - {file = "pmaw-3.0.0-py3-none-any.whl", hash = "sha256:78b381cc2aec81ddb2615387b483bafa9729ee926a3edf0f2a55453ba17a048d"}, - {file = "pmaw-3.0.0.tar.gz", hash = "sha256:5b9d0fe7b44ea03e675240abc2d62b76e12c07f09c2e2d61bb9d307bc8375442"}, -] - -[package.dependencies] -praw = "*" -requests = "*" - -[[package]] -name = "pmdarima" -version = "2.0.4" -description = "Python's forecast::auto.arima equivalent" -optional = true -python-versions = ">=3.7" -files = [ - {file = "pmdarima-2.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8e6c16672e0f122e63ab1d7282a362c762783264a07636bbc9d4ae3d58ac7605"}, - {file = "pmdarima-2.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c9a839a477100331c47aa8a841c198ecb0b3fab4aff958622d31fec86d2aea76"}, - {file = "pmdarima-2.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccd9e2186ba1ef45006f6c88dc9ecd6121ddb8914114bebcfa0d42899b40ced7"}, - {file = "pmdarima-2.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:165bdf787f5dafd5faab543d20524413b765d9cb6f020f0e1846551e7678414a"}, - {file = "pmdarima-2.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:cbde33812c37f441ba70d9e7b0479c758d56ad77a8dcbdead1fb8baad9aee806"}, - {file = "pmdarima-2.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d961c99b445e53eadf6627c61bf800f403bd247b7ec2f62c6dfe8a0b1bcbf0b"}, - {file = "pmdarima-2.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:78e815c51074411bbe8433a912af4cc8dac394d9330cfedf57dd5ce08efe4a65"}, - {file = "pmdarima-2.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8022484256492bc425f0053b3a0346ab0a877f0f668664be738fe07f6b423c08"}, - {file = "pmdarima-2.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46677b68efffde66aa1291799b39b91420961967fa2b6e041e26bb263782d38d"}, - {file = "pmdarima-2.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:bc594dd981bca5217b4448c96e82dbd60553b07263517a5cb0510b4bfe66ded1"}, - {file = "pmdarima-2.0.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62a4ff308fbb5074a66c0877ba6b472d0fc406cbc0b5a2dba7e8fa800c9dd8ca"}, - {file = "pmdarima-2.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e215ec6130b917843d26e0698637f976e4a6cc9dbd521bf44547668d08f058a"}, - {file = "pmdarima-2.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d837bc00266a234d9292914f19a67e04ca8361d9c309b70fc8f16c1ed863551"}, - {file = "pmdarima-2.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8790bff665a5ebaa36ddbbfd0e8ea51ad2e5809270dc74d462d5c2498b26220f"}, - {file = "pmdarima-2.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:a8bf7913bdbd0e286489b2111080a0f51f5d9d3ee5e05b7011a691207047ddcf"}, - {file = "pmdarima-2.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5bce63dca165948a2311eff0c200e57370829855fce6e7d3fe930497f2f9fc04"}, - {file = "pmdarima-2.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c1ed01cbb399d9cdbe3f12f2ef505a144826769ec3ad75f6075cadbe8447a13"}, - {file = "pmdarima-2.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73060d28ffabeae7374ab48ba7882b29ae6998de3e3e34f5484c64e0baefda0d"}, - {file = "pmdarima-2.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:776e21e076393c6fe86895ffe71e6769c9b3fe0dffb92d6f6558657580cf0a42"}, - {file = "pmdarima-2.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec865e8fb07378c470f3e41a77d6705875a674cefbe6c0185e9bc070e642da5c"}, - {file = "pmdarima-2.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:670ca1f93ed4f8239e7fbd7d1dd156108899e15e8fb0717b2b3fa605fa6ace35"}, - {file = "pmdarima-2.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab1ee166f511d2497d6b357bf0cac84326efff25349eb777aea5b030ed6bf8bb"}, - {file = "pmdarima-2.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9ead43510adfe3c0d4000e37427bfcf11ba6cc3b26091368a847c5da010e052"}, - {file = "pmdarima-2.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c1213f3fa1e3ced9796f4092f9bd4be1881205f77bc2f5a1494695134a92000e"}, - {file = "pmdarima-2.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e0e3c90e7a91b44599f08cd9c62880466860e76bd1b0ca2d2ff8e72834a1a7f"}, - {file = "pmdarima-2.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fe612384e4989f010dacdefbff874231f5b7351dfb84bcbfe9ed8d718fc4115"}, - {file = "pmdarima-2.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db842d97905b171c867aae1a492b8c958ec1bae987c3ee41c561a06d99a19efd"}, - {file = "pmdarima-2.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a03936d681c720b0e44565d099de2619b762cb9d443f3043de9a78888aff6bb2"}, - {file = "pmdarima-2.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:13ba7061d4e9d48f21e1c393bfaa3d6b31f60a8c97ddbe8d455fa675c594f9e4"}, - {file = "pmdarima-2.0.4.tar.gz", hash = "sha256:b87f9d9f5b7dc2ddbd053687c2264e26ac98fd4118e843c7e9bc3dd7343e5c1a"}, -] - -[package.dependencies] -Cython = ">=0.29,<0.29.18 || >0.29.18,<0.29.31 || >0.29.31" -joblib = ">=0.11" -numpy = ">=1.21.2" -packaging = ">=17.1" -pandas = ">=0.19" -scikit-learn = ">=0.22" -scipy = ">=1.3.2" -setuptools = ">=38.6.0,<50.0.0 || >50.0.0" -statsmodels = ">=0.13.2" -urllib3 = "*" - -[[package]] -name = "posthog" -version = "3.5.0" -description = "Integrate PostHog into any python application." -optional = false -python-versions = "*" -files = [ - {file = "posthog-3.5.0-py2.py3-none-any.whl", hash = "sha256:3c672be7ba6f95d555ea207d4486c171d06657eb34b3ce25eb043bfe7b6b5b76"}, - {file = "posthog-3.5.0.tar.gz", hash = "sha256:8f7e3b2c6e8714d0c0c542a2109b83a7549f63b7113a133ab2763a89245ef2ef"}, -] - -[package.dependencies] -backoff = ">=1.10.0" -monotonic = ">=1.5" -python-dateutil = ">2.1" -requests = ">=2.7,<3.0" -six = ">=1.5" - -[package.extras] -dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"] -sentry = ["django", "sentry-sdk"] -test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] - -[[package]] -name = "praw" -version = "7.7.1" -description = "PRAW, an acronym for \"Python Reddit API Wrapper\", is a Python package that allows for simple access to Reddit's API." -optional = false -python-versions = "~=3.7" -files = [ - {file = "praw-7.7.1-py3-none-any.whl", hash = "sha256:9ec5dc943db00c175bc6a53f4e089ce625f3fdfb27305564b616747b767d38ef"}, - {file = "praw-7.7.1.tar.gz", hash = "sha256:f1d7eef414cafe28080dda12ed09253a095a69933d5c8132eca11d4dc8a070bf"}, -] - -[package.dependencies] -prawcore = ">=2.1,<3" -update-checker = ">=0.18" -websocket-client = ">=0.54.0" - -[package.extras] -ci = ["coveralls"] -dev = ["betamax (>=0.8,<0.9)", "betamax-matchers (>=0.3.0,<0.5)", "furo", "packaging", "pre-commit", "pytest (>=2.7.3)", "requests (>=2.20.1,<3)", "sphinx", "urllib3 (==1.26.*)"] -lint = ["furo", "pre-commit", "sphinx"] -readthedocs = ["furo", "sphinx"] -test = ["betamax (>=0.8,<0.9)", "betamax-matchers (>=0.3.0,<0.5)", "pytest (>=2.7.3)", "requests (>=2.20.1,<3)", "urllib3 (==1.26.*)"] - -[[package]] -name = "prawcore" -version = "2.4.0" -description = "\"Low-level communication layer for PRAW 4+." -optional = false -python-versions = "~=3.8" -files = [ - {file = "prawcore-2.4.0-py3-none-any.whl", hash = "sha256:29af5da58d85704b439ad3c820873ad541f4535e00bb98c66f0fbcc8c603065a"}, - {file = "prawcore-2.4.0.tar.gz", hash = "sha256:b7b2b5a1d04406e086ab4e79988dc794df16059862f329f4c6a43ed09986c335"}, -] - -[package.dependencies] -requests = ">=2.6.0,<3.0" - -[package.extras] -ci = ["coveralls"] -dev = ["packaging", "prawcore[lint]", "prawcore[test]"] -lint = ["pre-commit", "ruff (>=0.0.291)"] -test = ["betamax (>=0.8,<0.9)", "pytest (>=2.7.3)", "urllib3 (==1.26.*)"] - -[[package]] -name = "pre-commit" -version = "2.21.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, - {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "prometheus-client" -version = "0.20.0" -description = "Python client for the Prometheus monitoring system." -optional = false -python-versions = ">=3.8" -files = [ - {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, - {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, -] - -[package.extras] -twisted = ["twisted"] - -[[package]] -name = "prompt-toolkit" -version = "3.0.43" -description = "Library for building powerful interactive command lines in Python" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, - {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "property-cached" -version = "1.6.4" -description = "A decorator for caching properties in classes (forked from cached-property)." -optional = false -python-versions = ">= 3.5" -files = [ - {file = "property-cached-1.6.4.zip", hash = "sha256:3e9c4ef1ed3653909147510481d7df62a3cfb483461a6986a6f1dcd09b2ebb73"}, - {file = "property_cached-1.6.4-py2.py3-none-any.whl", hash = "sha256:135fc059ec969c1646424a0db15e7fbe1b5f8c36c0006d0b3c91ba568c11e7d8"}, -] - -[[package]] -name = "prophet" -version = "1.1.3" -description = "Automatic Forecasting Procedure" -optional = true -python-versions = ">=3.7" -files = [ - {file = "prophet-1.1.3-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:e64d66ca78ee1366d9c4b28397ec45357f825a7b048b384bbf899f6c6b0acd4c"}, - {file = "prophet-1.1.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd3c76f7188d8a42936c93f8b6c9a299b443cf5342d15ff6eb1a2eb777ce3315"}, - {file = "prophet-1.1.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:718291f275bd59865274866f54380ad1ee2ef23ca1dec2950a45682bec261363"}, - {file = "prophet-1.1.3-py3-none-win_amd64.whl", hash = "sha256:e248e837d37740df23ae616c7b09d2ffa9e22eadfa51984e08edeccc542b5562"}, - {file = "prophet-1.1.3.tar.gz", hash = "sha256:f8d6d45a4391fdf740c7b74f79a72313f95a5f6e3417eaf1c68892b47b920646"}, -] - -[package.dependencies] -cmdstanpy = ">=1.0.4" -convertdate = ">=2.1.2" -holidays = ">=0.14.2" -importlib-resources = "*" -LunarCalendar = ">=0.0.9" -matplotlib = ">=2.0.0" -numpy = ">=1.15.4" -pandas = ">=1.0.4" -python-dateutil = ">=2.8.0" -tqdm = ">=4.36.1" - -[package.extras] -dev = ["jupyterlab", "nbconvert", "plotly", "pytest", "setuptools (>=64)", "wheel"] -parallel = ["dask[dataframe]", "distributed"] - -[[package]] -name = "protobuf" -version = "3.20.1" -description = "Protocol Buffers" -optional = false -python-versions = ">=3.7" -files = [ - {file = "protobuf-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996"}, - {file = "protobuf-3.20.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3"}, - {file = "protobuf-3.20.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde"}, - {file = "protobuf-3.20.1-cp310-cp310-win32.whl", hash = "sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c"}, - {file = "protobuf-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7"}, - {file = "protobuf-3.20.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153"}, - {file = "protobuf-3.20.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f"}, - {file = "protobuf-3.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20"}, - {file = "protobuf-3.20.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531"}, - {file = "protobuf-3.20.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e"}, - {file = "protobuf-3.20.1-cp37-cp37m-win32.whl", hash = "sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c"}, - {file = "protobuf-3.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067"}, - {file = "protobuf-3.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf"}, - {file = "protobuf-3.20.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab"}, - {file = "protobuf-3.20.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c"}, - {file = "protobuf-3.20.1-cp38-cp38-win32.whl", hash = "sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7"}, - {file = "protobuf-3.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739"}, - {file = "protobuf-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7"}, - {file = "protobuf-3.20.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f"}, - {file = "protobuf-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9"}, - {file = "protobuf-3.20.1-cp39-cp39-win32.whl", hash = "sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8"}, - {file = "protobuf-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91"}, - {file = "protobuf-3.20.1-py2.py3-none-any.whl", hash = "sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388"}, - {file = "protobuf-3.20.1.tar.gz", hash = "sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9"}, -] - -[[package]] -name = "psutil" -version = "5.9.8" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, - {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, - {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, - {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, - {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, - {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, - {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, - {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, - {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, - {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -optional = false -python-versions = "*" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pure-eval" -version = "0.2.2" -description = "Safely evaluate AST nodes without side effects" -optional = false -python-versions = "*" -files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, -] - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] - -[[package]] -name = "pyarrow" -version = "15.0.2" -description = "Python library for Apache Arrow" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyarrow-15.0.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:88b340f0a1d05b5ccc3d2d986279045655b1fe8e41aba6ca44ea28da0d1455d8"}, - {file = "pyarrow-15.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eaa8f96cecf32da508e6c7f69bb8401f03745c050c1dd42ec2596f2e98deecac"}, - {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23c6753ed4f6adb8461e7c383e418391b8d8453c5d67e17f416c3a5d5709afbd"}, - {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f639c059035011db8c0497e541a8a45d98a58dbe34dc8fadd0ef128f2cee46e5"}, - {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:290e36a59a0993e9a5224ed2fb3e53375770f07379a0ea03ee2fce2e6d30b423"}, - {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:06c2bb2a98bc792f040bef31ad3e9be6a63d0cb39189227c08a7d955db96816e"}, - {file = "pyarrow-15.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:f7a197f3670606a960ddc12adbe8075cea5f707ad7bf0dffa09637fdbb89f76c"}, - {file = "pyarrow-15.0.2-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5f8bc839ea36b1f99984c78e06e7a06054693dc2af8920f6fb416b5bca9944e4"}, - {file = "pyarrow-15.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5e81dfb4e519baa6b4c80410421528c214427e77ca0ea9461eb4097c328fa33"}, - {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a4f240852b302a7af4646c8bfe9950c4691a419847001178662a98915fd7ee7"}, - {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e7d9cfb5a1e648e172428c7a42b744610956f3b70f524aa3a6c02a448ba853e"}, - {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2d4f905209de70c0eb5b2de6763104d5a9a37430f137678edfb9a675bac9cd98"}, - {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90adb99e8ce5f36fbecbbc422e7dcbcbed07d985eed6062e459e23f9e71fd197"}, - {file = "pyarrow-15.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:b116e7fd7889294cbd24eb90cd9bdd3850be3738d61297855a71ac3b8124ee38"}, - {file = "pyarrow-15.0.2-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:25335e6f1f07fdaa026a61c758ee7d19ce824a866b27bba744348fa73bb5a440"}, - {file = "pyarrow-15.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:90f19e976d9c3d8e73c80be84ddbe2f830b6304e4c576349d9360e335cd627fc"}, - {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a22366249bf5fd40ddacc4f03cd3160f2d7c247692945afb1899bab8a140ddfb"}, - {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2a335198f886b07e4b5ea16d08ee06557e07db54a8400cc0d03c7f6a22f785f"}, - {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e6d459c0c22f0b9c810a3917a1de3ee704b021a5fb8b3bacf968eece6df098f"}, - {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:033b7cad32198754d93465dcfb71d0ba7cb7cd5c9afd7052cab7214676eec38b"}, - {file = "pyarrow-15.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:29850d050379d6e8b5a693098f4de7fd6a2bea4365bfd073d7c57c57b95041ee"}, - {file = "pyarrow-15.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:7167107d7fb6dcadb375b4b691b7e316f4368f39f6f45405a05535d7ad5e5058"}, - {file = "pyarrow-15.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e85241b44cc3d365ef950432a1b3bd44ac54626f37b2e3a0cc89c20e45dfd8bf"}, - {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:248723e4ed3255fcd73edcecc209744d58a9ca852e4cf3d2577811b6d4b59818"}, - {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ff3bdfe6f1b81ca5b73b70a8d482d37a766433823e0c21e22d1d7dde76ca33f"}, - {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:f3d77463dee7e9f284ef42d341689b459a63ff2e75cee2b9302058d0d98fe142"}, - {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:8c1faf2482fb89766e79745670cbca04e7018497d85be9242d5350cba21357e1"}, - {file = "pyarrow-15.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:28f3016958a8e45a1069303a4a4f6a7d4910643fc08adb1e2e4a7ff056272ad3"}, - {file = "pyarrow-15.0.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:89722cb64286ab3d4daf168386f6968c126057b8c7ec3ef96302e81d8cdb8ae4"}, - {file = "pyarrow-15.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd0ba387705044b3ac77b1b317165c0498299b08261d8122c96051024f953cd5"}, - {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad2459bf1f22b6a5cdcc27ebfd99307d5526b62d217b984b9f5c974651398832"}, - {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58922e4bfece8b02abf7159f1f53a8f4d9f8e08f2d988109126c17c3bb261f22"}, - {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:adccc81d3dc0478ea0b498807b39a8d41628fa9210729b2f718b78cb997c7c91"}, - {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:8bd2baa5fe531571847983f36a30ddbf65261ef23e496862ece83bdceb70420d"}, - {file = "pyarrow-15.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6669799a1d4ca9da9c7e06ef48368320f5856f36f9a4dd31a11839dda3f6cc8c"}, - {file = "pyarrow-15.0.2.tar.gz", hash = "sha256:9c9bc803cb3b7bfacc1e96ffbfd923601065d9d3f911179d81e72d99fd74a3d9"}, -] - -[package.dependencies] -numpy = ">=1.16.6,<2" - -[[package]] -name = "pyasn1" -version = "0.6.0" -description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, - {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, -] - -[[package]] -name = "pyasn1-modules" -version = "0.4.0" -description = "A collection of ASN.1-based protocols modules" -optional = true -python-versions = ">=3.8" -files = [ - {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, - {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, -] - -[package.dependencies] -pyasn1 = ">=0.4.6,<0.7.0" - -[[package]] -name = "pybind11" -version = "2.12.0" -description = "Seamless operability between C++11 and Python" -optional = true -python-versions = ">=3.6" -files = [ - {file = "pybind11-2.12.0-py3-none-any.whl", hash = "sha256:df8d60b94f9e714d81013db233393d430ebf9f3551642b82291cf1b14d1afdbd"}, - {file = "pybind11-2.12.0.tar.gz", hash = "sha256:5e3c557a84b06b969247630407fc4d985bed157b4253b13153b8e8e165e0c3dc"}, -] - -[package.extras] -global = ["pybind11-global (==2.12.0)"] - -[[package]] -name = "pycares" -version = "4.4.0" -description = "Python interface for c-ares" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycares-4.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:24da119850841d16996713d9c3374ca28a21deee056d609fbbed29065d17e1f6"}, - {file = "pycares-4.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8f64cb58729689d4d0e78f0bfb4c25ce2f851d0274c0273ac751795c04b8798a"}, - {file = "pycares-4.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d33e2a1120887e89075f7f814ec144f66a6ce06a54f5722ccefc62fbeda83cff"}, - {file = "pycares-4.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c680fef1b502ee680f8f0b95a41af4ec2c234e50e16c0af5bbda31999d3584bd"}, - {file = "pycares-4.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fff16b09042ba077f7b8aa5868d1d22456f0002574d0ba43462b10a009331677"}, - {file = "pycares-4.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:229a1675eb33bc9afb1fc463e73ee334950ccc485bc83a43f6ae5839fb4d5fa3"}, - {file = "pycares-4.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3aebc73e5ad70464f998f77f2da2063aa617cbd8d3e8174dd7c5b4518f967153"}, - {file = "pycares-4.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef64649eba56448f65e26546d85c860709844d2fc22ef14d324fe0b27f761a9"}, - {file = "pycares-4.4.0-cp310-cp310-win32.whl", hash = "sha256:4afc2644423f4eef97857a9fd61be9758ce5e336b4b0bd3d591238bb4b8b03e0"}, - {file = "pycares-4.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5ed4e04af4012f875b78219d34434a6d08a67175150ac1b79eb70ab585d4ba8c"}, - {file = "pycares-4.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bce8db2fc6f3174bd39b81405210b9b88d7b607d33e56a970c34a0c190da0490"}, - {file = "pycares-4.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a0303428d013ccf5c51de59c83f9127aba6200adb7fd4be57eddb432a1edd2a"}, - {file = "pycares-4.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afb91792f1556f97be7f7acb57dc7756d89c5a87bd8b90363a77dbf9ea653817"}, - {file = "pycares-4.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b61579cecf1f4d616e5ea31a6e423a16680ab0d3a24a2ffe7bb1d4ee162477ff"}, - {file = "pycares-4.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7af06968cbf6851566e806bf3e72825b0e6671832a2cbe840be1d2d65350710"}, - {file = "pycares-4.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ceb12974367b0a68a05d52f4162b29f575d241bd53de155efe632bf2c943c7f6"}, - {file = "pycares-4.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2eeec144bcf6a7b6f2d74d6e70cbba7886a84dd373c886f06cb137a07de4954c"}, - {file = "pycares-4.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e3a6f7cfdfd11eb5493d6d632e582408c8f3b429f295f8799c584c108b28db6f"}, - {file = "pycares-4.4.0-cp311-cp311-win32.whl", hash = "sha256:34736a2ffaa9c08ca9c707011a2d7b69074bbf82d645d8138bba771479b2362f"}, - {file = "pycares-4.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:eb66c30eb11e877976b7ead13632082a8621df648c408b8e15cdb91a452dd502"}, - {file = "pycares-4.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fd644505a8cfd7f6584d33a9066d4e3d47700f050ef1490230c962de5dfb28c6"}, - {file = "pycares-4.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52084961262232ec04bd75f5043aed7e5d8d9695e542ff691dfef0110209f2d4"}, - {file = "pycares-4.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0c5368206057884cde18602580083aeaad9b860e2eac14fd253543158ce1e93"}, - {file = "pycares-4.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:112a4979c695b1c86f6782163d7dec58d57a3b9510536dcf4826550f9053dd9a"}, - {file = "pycares-4.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d186dafccdaa3409194c0f94db93c1a5d191145a275f19da6591f9499b8e7b8"}, - {file = "pycares-4.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:64965dc19c578a683ea73487a215a8897276224e004d50eeb21f0bc7a0b63c88"}, - {file = "pycares-4.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ed2a38e34bec6f2586435f6ff0bc5fe11d14bebd7ed492cf739a424e81681540"}, - {file = "pycares-4.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:94d6962db81541eb0396d2f0dfcbb18cdb8c8b251d165efc2d974ae652c547d4"}, - {file = "pycares-4.4.0-cp312-cp312-win32.whl", hash = "sha256:1168a48a834813aa80f412be2df4abaf630528a58d15c704857448b20b1675c0"}, - {file = "pycares-4.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:db24c4e7fea4a052c6e869cbf387dd85d53b9736cfe1ef5d8d568d1ca925e977"}, - {file = "pycares-4.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:21a5a0468861ec7df7befa69050f952da13db5427ae41ffe4713bc96291d1d95"}, - {file = "pycares-4.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:22c00bf659a9fa44d7b405cf1cd69b68b9d37537899898d8cbe5dffa4016b273"}, - {file = "pycares-4.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23aa3993a352491a47fcf17867f61472f32f874df4adcbb486294bd9fbe8abee"}, - {file = "pycares-4.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:813d661cbe2e37d87da2d16b7110a6860e93ddb11735c6919c8a3545c7b9c8d8"}, - {file = "pycares-4.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:77cf5a2fd5583c670de41a7f4a7b46e5cbabe7180d8029f728571f4d2e864084"}, - {file = "pycares-4.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3eaa6681c0a3e3f3868c77aca14b7760fed35fdfda2fe587e15c701950e7bc69"}, - {file = "pycares-4.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ad58e284a658a8a6a84af2e0b62f2f961f303cedfe551854d7bd40c3cbb61912"}, - {file = "pycares-4.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bfb89ca9e3d0a9b5332deeb666b2ede9d3469107742158f4aeda5ce032d003f4"}, - {file = "pycares-4.4.0-cp38-cp38-win32.whl", hash = "sha256:f36bdc1562142e3695555d2f4ac0cb69af165eddcefa98efc1c79495b533481f"}, - {file = "pycares-4.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:902461a92b6a80fd5041a2ec5235680c7cc35e43615639ec2a40e63fca2dfb51"}, - {file = "pycares-4.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7bddc6adba8f699728f7fc1c9ce8cef359817ad78e2ed52b9502cb5f8dc7f741"}, - {file = "pycares-4.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cb49d5805cd347c404f928c5ae7c35e86ba0c58ffa701dbe905365e77ce7d641"}, - {file = "pycares-4.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56cf3349fa3a2e67ed387a7974c11d233734636fe19facfcda261b411af14d80"}, - {file = "pycares-4.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bf2eaa83a5987e48fa63302f0fe7ce3275cfda87b34d40fef9ce703fb3ac002"}, - {file = "pycares-4.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82bba2ab77eb5addbf9758d514d9bdef3c1bfe7d1649a47bd9a0d55a23ef478b"}, - {file = "pycares-4.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c6a8bde63106f162fca736e842a916853cad3c8d9d137e11c9ffa37efa818b02"}, - {file = "pycares-4.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5f646eec041db6ffdbcaf3e0756fb92018f7af3266138c756bb09d2b5baadec"}, - {file = "pycares-4.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9dc04c54c6ea615210c1b9e803d0e2d2255f87a3d5d119b6482c8f0dfa15b26b"}, - {file = "pycares-4.4.0-cp39-cp39-win32.whl", hash = "sha256:97892cced5794d721fb4ff8765764aa4ea48fe8b2c3820677505b96b83d4ef47"}, - {file = "pycares-4.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:917f08f0b5d9324e9a34211e68d27447c552b50ab967044776bbab7e42a553a2"}, - {file = "pycares-4.4.0.tar.gz", hash = "sha256:f47579d508f2f56eddd16ce72045782ad3b1b3b678098699e2b6a1b30733e1c2"}, -] - -[package.dependencies] -cffi = ">=1.5.0" - -[package.extras] -idna = ["idna (>=2.1)"] - -[[package]] -name = "pycodestyle" -version = "2.11.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, -] - -[[package]] -name = "pycoingecko" -version = "3.1.0" -description = "Python wrapper around the CoinGecko API" -optional = false -python-versions = "*" -files = [ - {file = "pycoingecko-3.1.0-py3-none-any.whl", hash = "sha256:5798240c0ffccfea14635b7ce42f90e49c8ba54a3cfa3e233fcc99df087485f6"}, - {file = "pycoingecko-3.1.0.tar.gz", hash = "sha256:dcc08522c160e88bd1deb50bbab390be33dce87abb10a91ce6013d15bf859c12"}, -] - -[package.dependencies] -requests = "*" - -[[package]] -name = "pycparser" -version = "2.22" -description = "C parser in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - -[[package]] -name = "pycryptodome" -version = "3.20.0" -description = "Cryptographic library for Python" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, - {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, - {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, - {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, - {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, - {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, - {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, - {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, - {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, - {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, - {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, - {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, - {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, - {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, - {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, - {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, - {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, - {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, - {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, - {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, - {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, - {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, - {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, - {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, - {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, - {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, -] - -[[package]] -name = "pycryptodomex" -version = "3.20.0" -description = "Cryptographic library for Python" -optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "pycryptodomex-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:645bd4ca6f543685d643dadf6a856cc382b654cc923460e3a10a49c1b3832aeb"}, - {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ff5c9a67f8a4fba4aed887216e32cbc48f2a6fb2673bb10a99e43be463e15913"}, - {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8ee606964553c1a0bc74057dd8782a37d1c2bc0f01b83193b6f8bb14523b877b"}, - {file = "pycryptodomex-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7805830e0c56d88f4d491fa5ac640dfc894c5ec570d1ece6ed1546e9df2e98d6"}, - {file = "pycryptodomex-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:bc3ee1b4d97081260d92ae813a83de4d2653206967c4a0a017580f8b9548ddbc"}, - {file = "pycryptodomex-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:8af1a451ff9e123d0d8bd5d5e60f8e3315c3a64f3cdd6bc853e26090e195cdc8"}, - {file = "pycryptodomex-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:cbe71b6712429650e3883dc81286edb94c328ffcd24849accac0a4dbcc76958a"}, - {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:76bd15bb65c14900d98835fcd10f59e5e0435077431d3a394b60b15864fddd64"}, - {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:653b29b0819605fe0898829c8ad6400a6ccde096146730c2da54eede9b7b8baa"}, - {file = "pycryptodomex-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a5ec91388984909bb5398ea49ee61b68ecb579123694bffa172c3b0a107079"}, - {file = "pycryptodomex-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:108e5f1c1cd70ffce0b68739c75734437c919d2eaec8e85bffc2c8b4d2794305"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e"}, - {file = "pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc"}, - {file = "pycryptodomex-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458"}, - {file = "pycryptodomex-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c"}, - {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b"}, - {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea"}, - {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781"}, - {file = "pycryptodomex-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499"}, - {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3584623e68a5064a04748fb6d76117a21a7cb5eaba20608a41c7d0c61721794"}, - {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1"}, - {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dcac11031a71348faaed1f403a0debd56bf5404232284cf8c761ff918886ebc"}, - {file = "pycryptodomex-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:69138068268127cd605e03438312d8f271135a33140e2742b417d027a0539427"}, - {file = "pycryptodomex-3.20.0.tar.gz", hash = "sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e"}, -] - -[[package]] -name = "pydantic" -version = "1.10.15" -description = "Data validation and settings management using python type hints" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"}, - {file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"}, - {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"}, - {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"}, - {file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"}, - {file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"}, - {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"}, - {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"}, - {file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"}, - {file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"}, - {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"}, - {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"}, - {file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"}, - {file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"}, - {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"}, - {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"}, - {file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"}, - {file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"}, - {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"}, - {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"}, - {file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"}, - {file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"}, - {file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pydeck" -version = "0.8.0" -description = "Widget for deck.gl maps" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydeck-0.8.0-py2.py3-none-any.whl", hash = "sha256:a8fa7757c6f24bba033af39db3147cb020eef44012ba7e60d954de187f9ed4d5"}, - {file = "pydeck-0.8.0.tar.gz", hash = "sha256:07edde833f7cfcef6749124351195aa7dcd24663d4909fd7898dbd0b6fbc01ec"}, -] - -[package.dependencies] -jinja2 = ">=2.10.1" -numpy = ">=1.16.4" - -[package.extras] -carto = ["pydeck-carto"] -jupyter = ["ipykernel (>=5.1.2)", "ipython (>=5.8.0)", "ipywidgets (>=7,<8)", "traitlets (>=4.3.2)"] - -[[package]] -name = "pydeprecate" -version = "0.3.2" -description = "Deprecation tooling" -optional = true -python-versions = ">=3.6" -files = [ - {file = "pyDeprecate-0.3.2-py3-none-any.whl", hash = "sha256:ed86b68ed837e6465245904a3de2f59bf9eef78ac7a2502ee280533d04802457"}, - {file = "pyDeprecate-0.3.2.tar.gz", hash = "sha256:d481116cc5d7f6c473e7c4be820efdd9b90a16b594b350276e9e66a6cb5bdd29"}, -] - -[[package]] -name = "pydocstyle" -version = "6.3.0" -description = "Python docstring style checker" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, - {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, -] - -[package.dependencies] -snowballstemmer = ">=2.2.0" - -[package.extras] -toml = ["tomli (>=1.2.3)"] - -[[package]] -name = "pyerfa" -version = "2.0.0.3" -description = "Python bindings for ERFA" -optional = true -python-versions = ">=3.7" -files = [ - {file = "pyerfa-2.0.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:676515861ca3f0cb9d7e693389233e7126413a5ba93a0cc4d36b8ca933951e8d"}, - {file = "pyerfa-2.0.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a438865894d226247dcfcb60d683ae075a52716504537052371b2b73458fe4fc"}, - {file = "pyerfa-2.0.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73bf7d23f069d47632a2feeb1e73454b10392c4f3c16116017a6983f1f0e9b2b"}, - {file = "pyerfa-2.0.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:780b0f90adf500b8ba24e9d509a690576a7e8287e354cfb90227c5963690d3fc"}, - {file = "pyerfa-2.0.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5447bb45ddedde3052693c86b941a4908f5dbeb4a697bda45b5b89de92cfb74a"}, - {file = "pyerfa-2.0.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c24e7960c6cdd3fa3f4dba5f3444a106ad48c94ff0b19eebaee06a142c18c52"}, - {file = "pyerfa-2.0.0.3-cp310-cp310-win32.whl", hash = "sha256:170a83bd0243da518119b846f296cf33fa03f1f884a88578c1a38560182cf64e"}, - {file = "pyerfa-2.0.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:51aa6e0faa4aa9ad8f0eef1c47fec76c5bebc0da7023a436089bdd6e5cfd625f"}, - {file = "pyerfa-2.0.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fa9fceeb78057bfff7ae3aa6cdad3f1b193722de22bdbb75319256f4a9e2f76"}, - {file = "pyerfa-2.0.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a8a2029fc62ff2369d01219f66a5ce6aed35ef33eddb06118b6c27e8573a9ed8"}, - {file = "pyerfa-2.0.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da888da2c8db5a78273fbf0af4e74f04e2d312d371c3c021cf6c3b14fa60fe3b"}, - {file = "pyerfa-2.0.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7354753addba5261ec1cbf1ba45784ed3a5c42da565ecc6e0aa36b7a17fa4689"}, - {file = "pyerfa-2.0.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b55f7278c1dd362648d7956e1a5365ade5fed2fe5541b721b3ceb5271128892"}, - {file = "pyerfa-2.0.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:23e5efcf96ed7161d74f79ca261d255e1f36988843d22cd97d8f60fe9c868d44"}, - {file = "pyerfa-2.0.0.3-cp311-cp311-win32.whl", hash = "sha256:f0e9d0b122c454bcad5dbd0c3283b200783031d3f99ca9c550f49a7a7d4c41ea"}, - {file = "pyerfa-2.0.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:09af83540e23a7d61a8368b0514b3daa4ed967e1e52d0add4f501f58c500dd7f"}, - {file = "pyerfa-2.0.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a07444fd53a5dd18d7955f86f8d9b1be9a68ceb143e1145c0019a310c913c04"}, - {file = "pyerfa-2.0.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf7364e475cff1f973e2fcf6962de9df9642c8802b010e29b2c592ae337e3c5"}, - {file = "pyerfa-2.0.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8458421166f6ffe2e259aaf4aaa6e802d6539649a40e3194a81d30dccdc167a"}, - {file = "pyerfa-2.0.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96ea688341176ae6220cc4743cda655549d71e3e3b60c5a99d02d5912d0ddf55"}, - {file = "pyerfa-2.0.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d56f6b5a0a3ed7b80d630041829463a872946df277259b5453298842d42a54a4"}, - {file = "pyerfa-2.0.0.3-cp37-cp37m-win32.whl", hash = "sha256:3ecb598924ddb4ea2b06efc6f1e55ca70897ed178a690e2eaa1e290448466c7c"}, - {file = "pyerfa-2.0.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1033fdb890ec70d3a511e20a464afc8abbea2180108f27b14d8f1d1addc38cbe"}, - {file = "pyerfa-2.0.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d8c0dbb17119e52def33f9d6dbf2deaf2113ed3e657b6ff692df9b6a3598397"}, - {file = "pyerfa-2.0.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8a1edd2cbe4ead3bf9a51e578d5d83bdd7ab3b3ccb69e09b89a4c42aa5b35ffb"}, - {file = "pyerfa-2.0.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04c3b715c924b6f972dd440a94a701a16a07700bc8ba9e88b1df765bdc36ad0"}, - {file = "pyerfa-2.0.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d01c341c45b860ee5c7585ef003118c8015e9d65c30668d2f5bf657e1dcdd68"}, - {file = "pyerfa-2.0.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24d89ead30edc6038408336ad9b696683e74c4eef550708fca6afef3ecd5b010"}, - {file = "pyerfa-2.0.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b8c5e74d48a505a014e855cd4c7be11604901d94fd6f34b685f6720b7b20ed8"}, - {file = "pyerfa-2.0.0.3-cp38-cp38-win32.whl", hash = "sha256:2ccba04de166d81bdd3adcf10428d908ce2f3a56ed1c2767d740fec12680edbd"}, - {file = "pyerfa-2.0.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3df87743e27588c5bd5e1f3a886629b3277fdd418059ca048420d33169376775"}, - {file = "pyerfa-2.0.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:88aa1acedf298d255cc4b0740ee11a3b303b71763dba2f039d48abf0a95cf9df"}, - {file = "pyerfa-2.0.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06d4f08e96867b1fc3ae9a9e4b38693ed0806463288efc41473ad16e14774504"}, - {file = "pyerfa-2.0.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1819e0d95ff8dead80614f8063919d82b2dbb55437b6c0109d3393c1ab55954"}, - {file = "pyerfa-2.0.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61f1097ac2ee8c15a2a636cdfb99340d708574d66f4610456bd457d1e6b852f4"}, - {file = "pyerfa-2.0.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36f42ee01a62c6cbba58103e6f8e600b21ad3a71262dccf03d476efb4a20ea71"}, - {file = "pyerfa-2.0.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3ecd6167b48bb8f1922fae7b49554616f2e7382748a4320ad46ebd7e2cc62f3d"}, - {file = "pyerfa-2.0.0.3-cp39-cp39-win32.whl", hash = "sha256:7f9eabfefa5317ce58fe22480102902f10f270fc64a5636c010f7c0b7e0fb032"}, - {file = "pyerfa-2.0.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:4ea7ca03ecc440224c2bed8fb136fadf6cf8aea8ba67d717f635116f30c8cc8c"}, - {file = "pyerfa-2.0.0.3.tar.gz", hash = "sha256:d77fbbfa58350c194ccb99e5d93aa05d3c2b14d5aad8b662d93c6ad9fff41f39"}, -] - -[package.dependencies] -numpy = ">=1.17" - -[package.extras] -docs = ["sphinx-astropy (>=1.3)"] -test = ["pytest", "pytest-doctestplus (>=0.7)"] - -[[package]] -name = "pyflakes" -version = "3.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, - {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, -] - -[[package]] -name = "pygls" -version = "1.3.1" -description = "A pythonic generic language server (pronounced like 'pie glass')" -optional = true -python-versions = ">=3.8" -files = [ - {file = "pygls-1.3.1-py3-none-any.whl", hash = "sha256:6e00f11efc56321bdeb6eac04f6d86131f654c7d49124344a9ebb968da3dd91e"}, - {file = "pygls-1.3.1.tar.gz", hash = "sha256:140edceefa0da0e9b3c533547c892a42a7d2fd9217ae848c330c53d266a55018"}, -] - -[package.dependencies] -cattrs = ">=23.1.2" -lsprotocol = "2023.0.1" - -[package.extras] -ws = ["websockets (>=11.0.3)"] - -[[package]] -name = "pygments" -version = "2.17.2" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, -] - -[package.extras] -plugins = ["importlib-metadata"] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pyhdfe" -version = "0.2.0" -description = "High dimensional fixed effect absorption with Python 3" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyhdfe-0.2.0-py3-none-any.whl", hash = "sha256:5be73689101b97ff9e6e563874747257cdf86cb683159de8e16a5457130fb532"}, - {file = "pyhdfe-0.2.0.tar.gz", hash = "sha256:8cddc5f5a09148d3281fca3c787146a85ecc5a7517be3ae5762bfe507907b7fb"}, -] - -[package.dependencies] -numpy = ">=1.12.0" -scipy = ">=1.0.0" - -[package.extras] -docs = ["astunparse", "docutils (==0.17)", "ipython", "jinja2 (>=2.11,<3.0)", "nbsphinx (==0.5.0)", "sphinx (==2.0.0)", "sphinx-rtd-theme (==0.4.3)"] -tests = ["pytest", "pytest-xdist"] - -[[package]] -name = "pyinstaller" -version = "4.10" -description = "PyInstaller bundles a Python application and all its dependencies into a single package." -optional = true -python-versions = "<3.11,>=3.6" -files = [ - {file = "pyinstaller-4.10-py3-none-macosx_10_13_universal2.whl", hash = "sha256:15557cd1a79d182967f0a5040750e6902e13ebd6cab41e3ed84d7b28a306357b"}, - {file = "pyinstaller-4.10-py3-none-manylinux2014_aarch64.whl", hash = "sha256:f2166ff2cd95eefb0d377ae8d1071f186fa25edd410ede65b376162d5ec41909"}, - {file = "pyinstaller-4.10-py3-none-manylinux2014_i686.whl", hash = "sha256:7d94518ba1f8e9a8577345312276891ad7d6cd9785e453e9951b35647e2c7078"}, - {file = "pyinstaller-4.10-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:70c71e827f4b34602cbc7a0947a067b662c1cbdc4db51832e13b97cca3c54dd7"}, - {file = "pyinstaller-4.10-py3-none-manylinux2014_s390x.whl", hash = "sha256:05c21117b84199272ebd355b556af4714f6e79245e1c435d6f16653786d7d17e"}, - {file = "pyinstaller-4.10-py3-none-manylinux2014_x86_64.whl", hash = "sha256:714c4dcc319a41416744d1e30c6317405dfaed80d2adc45f8bfa70dc7367e664"}, - {file = "pyinstaller-4.10-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:581620bdcd32f01e89b13231256b807bb090e7eadf40c81c864ec402afa4758a"}, - {file = "pyinstaller-4.10-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d4f79c0a774451f12baca4e476376418f011fa3039dde8fd172ea2aa8ff67bad"}, - {file = "pyinstaller-4.10-py3-none-win32.whl", hash = "sha256:cfed0b3a43e73550a43a094610328109564710b9514afa093ef7199d072cae87"}, - {file = "pyinstaller-4.10-py3-none-win_amd64.whl", hash = "sha256:0dcaf6557cdb2da763c46e06e95a94a7634ab03fb09d91bc77988b01ee05c907"}, - {file = "pyinstaller-4.10.tar.gz", hash = "sha256:7749c868d2e2dc84df7d6f65437226183c8a366f3a99bb2737785625c3a3cca1"}, -] - -[package.dependencies] -altgraph = "*" -macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} -pefile = {version = ">=2017.8.1", markers = "sys_platform == \"win32\""} -pyinstaller-hooks-contrib = ">=2020.6" -pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} -setuptools = "*" - -[package.extras] -encryption = ["tinyaes (>=1.0.0)"] -hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] - -[[package]] -name = "pyinstaller-hooks-contrib" -version = "2024.4" -description = "Community maintained hooks for PyInstaller" -optional = true -python-versions = ">=3.7" -files = [ - {file = "pyinstaller_hooks_contrib-2024.4-py2.py3-none-any.whl", hash = "sha256:3c521e3c710de74a4d6eeebf5b7b76c3e5b332fea8fa0baafa42c94b73908c0e"}, - {file = "pyinstaller_hooks_contrib-2024.4.tar.gz", hash = "sha256:3aad23ec9298ce675432397cda539e11a8bf77341a314e48b4da4c787ed67e8a"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} -packaging = ">=22.0" -setuptools = ">=42.0.0" - -[[package]] -name = "pylint" -version = "2.17.0" -description = "python code static checker" -optional = false -python-versions = ">=3.7.2" -files = [ - {file = "pylint-2.17.0-py3-none-any.whl", hash = "sha256:e097d8325f8c88e14ad12844e3fe2d963d3de871ea9a8f8ad25ab1c109889ddc"}, - {file = "pylint-2.17.0.tar.gz", hash = "sha256:1460829b6397cb5eb0cdb0b4fc4b556348e515cdca32115f74a1eb7c20b896b4"}, -] - -[package.dependencies] -astroid = ">=2.15.0,<=2.17.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = {version = ">=0.2", markers = "python_version < \"3.11\""} -isort = ">=4.2.5,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -name = "pyluach" -version = "2.2.0" -description = "A Python package for dealing with Hebrew (Jewish) calendar dates." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyluach-2.2.0-py3-none-any.whl", hash = "sha256:d1eb49d6292087e9290f4661ae01b60c8c933704ec8c9cef82673b349ff96adf"}, - {file = "pyluach-2.2.0.tar.gz", hash = "sha256:9063a25387cd7624276fd0656508bada08aa8a6f22e8db352844cd858e69012b"}, -] - -[package.extras] -doc = ["sphinx (>=6.1.3,<6.2.0)", "sphinx_rtd_theme (>=1.2.0,<1.3.0)"] -test = ["beautifulsoup4", "flake8", "pytest", "pytest-cov"] - -[[package]] -name = "pymeeus" -version = "0.5.12" -description = "Python implementation of Jean Meeus astronomical routines" -optional = false -python-versions = "*" -files = [ - {file = "PyMeeus-0.5.12.tar.gz", hash = "sha256:548f7186bd8b96cbc069cf649a8e8e377dce49ac74486709849fe63a99cad684"}, -] - -[[package]] -name = "pyobjc-core" -version = "10.2" -description = "Python<->ObjC Interoperability Module" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyobjc-core-10.2.tar.gz", hash = "sha256:0153206e15d0e0d7abd53ee8a7fbaf5606602a032e177a028fc8589516a8771c"}, - {file = "pyobjc_core-10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8eab50ce7f17017a0f1d68c3b7e88bb1bb033415fdff62b8e0a9ee4ab72f242"}, - {file = "pyobjc_core-10.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f2115971463073426ab926416e17e5c16de5b90d1a1f2a2d8724637eb1c21308"}, - {file = "pyobjc_core-10.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a70546246177c23acb323c9324330e37638f1a0a3d13664abcba3bb75e43012c"}, - {file = "pyobjc_core-10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a9b5a215080d13bd7526031d21d5eb27a410780878d863f486053a0eba7ca9a5"}, - {file = "pyobjc_core-10.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:eb1ab700a44bcc4ceb125091dfaae0b998b767b49990df5fdc83eb58158d8e3f"}, - {file = "pyobjc_core-10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9a7163aff9c47d654f835f80361c1b112886ec754800d34e75d1e02ff52c3d7"}, -] - -[[package]] -name = "pyobjc-framework-cocoa" -version = "10.2" -description = "Wrappers for the Cocoa frameworks on macOS" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyobjc-framework-Cocoa-10.2.tar.gz", hash = "sha256:6383141379636b13855dca1b39c032752862b829f93a49d7ddb35046abfdc035"}, - {file = "pyobjc_framework_Cocoa-10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9227b4f271fda2250f5a88cbc686ff30ae02c0f923bb7854bb47972397496b2"}, - {file = "pyobjc_framework_Cocoa-10.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6a6042b7703bdc33b7491959c715c1e810a3f8c7a560c94b36e00ef321480797"}, - {file = "pyobjc_framework_Cocoa-10.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:18886d5013cd7dc7ecd6e0df5134c767569b5247fc10a5e293c72ee3937b217b"}, - {file = "pyobjc_framework_Cocoa-10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ecf01400ee698d2e0ff4c907bcf9608d9d710e97203fbb97b37d208507a9362"}, - {file = "pyobjc_framework_Cocoa-10.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:0def036a7b24e3ae37a244c77bec96b7c9c8384bf6bb4d33369f0a0c8807a70d"}, - {file = "pyobjc_framework_Cocoa-10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f47ecc393bc1019c4b47e8653207188df784ac006ad54d8c2eb528906ff7013"}, -] - -[package.dependencies] -pyobjc-core = ">=10.2" - -[[package]] -name = "pyod" -version = "1.1.3" -description = "A Comprehensive and Scalable Python Library for Outlier Detection (Anomaly Detection)" -optional = true -python-versions = "*" -files = [ - {file = "pyod-1.1.3.tar.gz", hash = "sha256:ae39808c8da04ce47eee3757ef641feb67e610bddb134b3362fd6f5cb2c6c7cd"}, -] - -[package.dependencies] -joblib = "*" -matplotlib = "*" -numba = ">=0.51" -numpy = ">=1.19" -scikit_learn = ">=0.22.0" -scipy = ">=1.5.1" -six = "*" - -[[package]] -name = "pyparsing" -version = "3.1.2" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, - {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pyprind" -version = "2.11.3" -description = "Python Progress Bar and Percent Indicator Utility" -optional = false -python-versions = "*" -files = [ - {file = "PyPrind-2.11.3-py2.py3-none-any.whl", hash = "sha256:cc8edb66aabb18f25f7a3cce65312b3bc64b49992ddc93ba4cf511e5e25c1be3"}, - {file = "PyPrind-2.11.3.tar.gz", hash = "sha256:e37dcab6e1a9c8e0a7f0fce65fde7a79e2deda1c75aa015910a49e2137b54cbf"}, -] - -[[package]] -name = "pyrsistent" -version = "0.19.3" -description = "Persistent/Functional/Immutable data structures" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, - {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, - {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, - {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, - {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, - {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, -] - -[[package]] -name = "pytest" -version = "6.2.5" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, - {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, -] - -[package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -toml = "*" - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] - -[[package]] -name = "pytest-cov" -version = "3.0.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.6" -files = [ - {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, - {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, -] - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] - -[[package]] -name = "pytest-mock" -version = "3.14.0" -description = "Thin-wrapper around the mock package for easier use with pytest" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, -] - -[package.dependencies] -pytest = ">=6.2.5" - -[package.extras] -dev = ["pre-commit", "pytest-asyncio", "tox"] - -[[package]] -name = "pytest-recorder" -version = "0.2.3" -description = "Pytest plugin, meant to facilitate unit tests writing for tools consumming Web APIs." -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "pytest_recorder-0.2.3-py3-none-any.whl", hash = "sha256:a2aa1f58a72e78585151840503c88b2a4b7aa85651ce3a08232c86e2680d0c90"}, - {file = "pytest_recorder-0.2.3.tar.gz", hash = "sha256:ecba7069c87d6e30229db82a091c61e6abb18662c69eeb78a3866ea8b37f04b7"}, -] - -[package.dependencies] -time-machine = ">=2.9.0,<3.0.0" -vcrpy = ">=4.2.1,<5.0.0" - -[[package]] -name = "pytest-recording" -version = "0.12.2" -description = "A pytest plugin that allows you recording of network interactions via VCR.py" -optional = false -python-versions = ">=3.5" -files = [ - {file = "pytest-recording-0.12.2.tar.gz", hash = "sha256:7c8949c24e5546a699f8fbbff0c5d6896cd09463378ac3d3f1ebb110d2186847"}, - {file = "pytest_recording-0.12.2-py3-none-any.whl", hash = "sha256:f055f97eb98bbefd0453a7796aa3a6833502c173421928b9d878cf1420b36406"}, -] - -[package.dependencies] -attrs = "*" -pytest = ">=3.5.0" -vcrpy = ">=2.0.1" - -[[package]] -name = "pytest-timeout" -version = "2.2.0" -description = "pytest plugin to abort hanging tests" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-timeout-2.2.0.tar.gz", hash = "sha256:3b0b95dabf3cb50bac9ef5ca912fa0cfc286526af17afc806824df20c2f72c90"}, - {file = "pytest_timeout-2.2.0-py3-none-any.whl", hash = "sha256:bde531e096466f49398a59f2dde76fa78429a09a12411466f88a07213e220de2"}, -] - -[package.dependencies] -pytest = ">=5.0.0" - -[[package]] -name = "pytest-xdist" -version = "3.5.0" -description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"}, - {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"}, -] - -[package.dependencies] -execnet = ">=1.1" -pytest = ">=6.2.0" - -[package.extras] -psutil = ["psutil (>=3.0)"] -setproctitle = ["setproctitle"] -testing = ["filelock"] - -[[package]] -name = "pythclient" -version = "0.1.6" -description = "A library to retrieve Pyth account structures off the Solana blockchain." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "pythclient-0.1.6-py3-none-any.whl", hash = "sha256:9758f56831ba3a53f4d178b4cf3f55a3df58476d6e06dcb24aa12f2425638721"}, - {file = "pythclient-0.1.6.tar.gz", hash = "sha256:f66e00ab7ab940d29e84609fefd2d7c06152e81dc88e93665b6ef48047d530f7"}, -] - -[package.dependencies] -aiodns = "*" -aiohttp = ">=3.7.4" -backoff = "*" -base58 = "*" -dnspython = "*" -flake8 = "*" -loguru = "*" -pytz = "*" -typing-extensions = "*" - -[package.extras] -testing = ["aiodns", "aiohttp (>=3.7.4)", "backoff", "base58", "dnspython", "flake8", "loguru", "mock", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "pytest-socket", "pytz", "typing-extensions"] - -[[package]] -name = "python-binance" -version = "1.0.19" -description = "Binance REST API python implementation" -optional = false -python-versions = "*" -files = [ - {file = "python-binance-1.0.19.tar.gz", hash = "sha256:b58cb5313d4fafbcfab3afe6082fff8ba4ebf7261211e7a322c2137637825e82"}, - {file = "python_binance-1.0.19-py2.py3-none-any.whl", hash = "sha256:aabc86955e3f99d5e8b1d49796eb584127af4c44f3eaab64ef92b8ee8fa88bff"}, -] - -[package.dependencies] -aiohttp = "*" -dateparser = "*" -pycryptodome = "*" -requests = "*" -six = "*" -ujson = "*" -websockets = "*" - -[[package]] -name = "python-coinmarketcap" -version = "0.2" -description = "CoinMarketCap Python API Wrapper" -optional = false -python-versions = "*" -files = [ - {file = "python-coinmarketcap-0.2.tar.gz", hash = "sha256:b1e0e8b098fc910a5ec943a06774d5ee3b190e915123366e2f16238ef0a8a433"}, - {file = "python_coinmarketcap-0.2-py2-none-any.whl", hash = "sha256:54c278154ee11ca78837fb73eae8c322a60af93dc0ce75b515fecf895d181cf5"}, - {file = "python_coinmarketcap-0.2-py3-none-any.whl", hash = "sha256:3b69a7a92cd4f2b4726a2982d166c0b587fa4911cf366286f798e1e6f9d94933"}, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-dotenv" -version = "1.0.1" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, - {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "python-i18n" -version = "0.3.9" -description = "Translation library for Python" -optional = false -python-versions = "*" -files = [ - {file = "python-i18n-0.3.9.tar.gz", hash = "sha256:df97f3d2364bf3a7ebfbd6cbefe8e45483468e52a9e30b909c6078f5f471e4e8"}, - {file = "python_i18n-0.3.9-py3-none-any.whl", hash = "sha256:bda5b8d889ebd51973e22e53746417bd32783c9bd6780fd27cadbb733915651d"}, -] - -[package.extras] -yaml = ["pyyaml (>=3.10)"] - -[[package]] -name = "python-jose" -version = "3.3.0" -description = "JOSE implementation in Python" -optional = false -python-versions = "*" -files = [ - {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, - {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, -] - -[package.dependencies] -ecdsa = "!=0.15" -pyasn1 = "*" -rsa = "*" - -[package.extras] -cryptography = ["cryptography (>=3.4.0)"] -pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] -pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] - -[[package]] -name = "python-json-logger" -version = "2.0.7" -description = "A python library adding a json log formatter" -optional = false -python-versions = ">=3.6" -files = [ - {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, - {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, -] - -[[package]] -name = "pytorch-lightning" -version = "1.6.5" -description = "PyTorch Lightning is the lightweight PyTorch wrapper for ML researchers. Scale your models. Write less boilerplate." -optional = true -python-versions = ">=3.7" -files = [ - {file = "pytorch-lightning-1.6.5.tar.gz", hash = "sha256:8d521f2619b9db2ada5bbaf9713330d01460e75a11e4bc0bc2ca25fd37c47c57"}, - {file = "pytorch_lightning-1.6.5-py3-none-any.whl", hash = "sha256:00c9205d26aa354defdd4dd592b2dded33916d6e0c180ccffbb06cf34dc67ccf"}, -] - -[package.dependencies] -fsspec = {version = ">=2021.05.0,<2021.06.0 || >2021.06.0", extras = ["http"]} -numpy = ">=1.17.2" -packaging = ">=17.0" -protobuf = "<=3.20.1" -pyDeprecate = ">=0.3.1" -PyYAML = ">=5.4" -tensorboard = ">=2.2.0" -torch = ">=1.8" -torchmetrics = ">=0.4.1" -tqdm = ">=4.57.0" -typing-extensions = ">=4.0.0" - -[package.extras] -all = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "comet-ml (>=3.1.12)", "coverage (>=6.4)", "deepspeed", "fairscale (>=0.4.5)", "flake8 (>=3.9.2)", "gcsfs (>=2021.5.0)", "gym[classic-control] (>=0.17.0)", "hivemind (>=1.0.1)", "horovod (>=0.21.2,!=0.24.0)", "hydra-core (>=1.0.5)", "ipython[all]", "jsonargparse[signatures] (>=4.7.1)", "matplotlib (>3.1)", "mlflow (>=1.0.0)", "mypy (>=0.920)", "neptune-client (>=0.10.0)", "omegaconf (>=2.0.5)", "onnxruntime", "pandas", "pre-commit (>=1.0)", "pytest (>=6.0)", "pytest-forked", "pytest-rerunfailures (>=10.2)", "rich (>=10.2.2,!=10.15.0.a)", "scikit-learn (>0.22.1)", "test-tube (>=0.7.5)", "torchtext (>=0.9)", "torchvision (>=0.9)", "wandb (>=0.8.21)"] -deepspeed = ["deepspeed"] -dev = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "comet-ml (>=3.1.12)", "coverage (>=6.4)", "flake8 (>=3.9.2)", "gcsfs (>=2021.5.0)", "hydra-core (>=1.0.5)", "jsonargparse[signatures] (>=4.7.1)", "matplotlib (>3.1)", "mlflow (>=1.0.0)", "mypy (>=0.920)", "neptune-client (>=0.10.0)", "omegaconf (>=2.0.5)", "onnxruntime", "pandas", "pre-commit (>=1.0)", "pytest (>=6.0)", "pytest-forked", "pytest-rerunfailures (>=10.2)", "rich (>=10.2.2,!=10.15.0.a)", "scikit-learn (>0.22.1)", "test-tube (>=0.7.5)", "torchtext (>=0.9)", "wandb (>=0.8.21)"] -examples = ["gym[classic-control] (>=0.17.0)", "ipython[all]", "torchvision (>=0.9)"] -extra = ["gcsfs (>=2021.5.0)", "hydra-core (>=1.0.5)", "jsonargparse[signatures] (>=4.7.1)", "matplotlib (>3.1)", "omegaconf (>=2.0.5)", "rich (>=10.2.2,!=10.15.0.a)", "torchtext (>=0.9)"] -fairscale = ["fairscale (>=0.4.5)"] -hivemind = ["hivemind (>=1.0.1)"] -horovod = ["horovod (>=0.21.2,!=0.24.0)"] -loggers = ["comet-ml (>=3.1.12)", "mlflow (>=1.0.0)", "neptune-client (>=0.10.0)", "test-tube (>=0.7.5)", "wandb (>=0.8.21)"] -strategies = ["deepspeed", "fairscale (>=0.4.5)", "hivemind (>=1.0.1)", "horovod (>=0.21.2,!=0.24.0)"] -test = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "coverage (>=6.4)", "flake8 (>=3.9.2)", "mypy (>=0.920)", "onnxruntime", "pandas", "pre-commit (>=1.0)", "pytest (>=6.0)", "pytest-forked", "pytest-rerunfailures (>=10.2)", "scikit-learn (>0.22.1)"] - -[[package]] -name = "pytrends" -version = "4.9.2" -description = "Pseudo API for Google Trends" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytrends-4.9.2-py3-none-any.whl", hash = "sha256:d7d0ee956be2f6e3e9fc09376c4615efb9347039d6d1f46e6f4326a5b7a14f67"}, - {file = "pytrends-4.9.2.tar.gz", hash = "sha256:691c6e36b1aeaa4754f3692bdbad0dff446e528ffb052eee2e7f139aaa2c6989"}, -] - -[package.dependencies] -lxml = "*" -pandas = ">=0.25" -requests = ">=2.0" - -[[package]] -name = "pytz" -version = "2024.1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, -] - -[[package]] -name = "pywin32" -version = "306" -description = "Python for Window Extensions" -optional = false -python-versions = "*" -files = [ - {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, - {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, - {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, - {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, - {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, - {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, - {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, - {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, - {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, - {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, - {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, - {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, - {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, - {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, -] - -[[package]] -name = "pywin32-ctypes" -version = "0.2.2" -description = "A (partial) reimplementation of pywin32 using ctypes/cffi" -optional = true -python-versions = ">=3.6" -files = [ - {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, - {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, -] - -[[package]] -name = "pywinpty" -version = "2.0.13" -description = "Pseudo terminal support for Windows from Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pywinpty-2.0.13-cp310-none-win_amd64.whl", hash = "sha256:697bff211fb5a6508fee2dc6ff174ce03f34a9a233df9d8b5fe9c8ce4d5eaf56"}, - {file = "pywinpty-2.0.13-cp311-none-win_amd64.whl", hash = "sha256:b96fb14698db1284db84ca38c79f15b4cfdc3172065b5137383910567591fa99"}, - {file = "pywinpty-2.0.13-cp312-none-win_amd64.whl", hash = "sha256:2fd876b82ca750bb1333236ce98488c1be96b08f4f7647cfdf4129dfad83c2d4"}, - {file = "pywinpty-2.0.13-cp38-none-win_amd64.whl", hash = "sha256:61d420c2116c0212808d31625611b51caf621fe67f8a6377e2e8b617ea1c1f7d"}, - {file = "pywinpty-2.0.13-cp39-none-win_amd64.whl", hash = "sha256:71cb613a9ee24174730ac7ae439fd179ca34ccb8c5349e8d7b72ab5dea2c6f4b"}, - {file = "pywinpty-2.0.13.tar.gz", hash = "sha256:c34e32351a3313ddd0d7da23d27f835c860d32fe4ac814d372a3ea9594f41dde"}, -] - -[[package]] -name = "pywry" -version = "0.6.2" -description = "" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pywry-0.6.2-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:45d6bb827bf76b2532a9d70b539209d70f37dfb13e9862549b7bff8500ad2495"}, - {file = "pywry-0.6.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:1d9ffd826a3a08c132843340e6d896efb7b972b301d045e3239a7dc08d9cac2f"}, - {file = "pywry-0.6.2-py3-none-win_amd64.whl", hash = "sha256:4f0e5b502555ee8b8e799baeaebe63243a84b7ce51df01a1c439dbc4e8227b9e"}, - {file = "pywry-0.6.2.tar.gz", hash = "sha256:9bd88c36ab0860728d9e64360010f8abcede43645656030e4a63e69e81a98c95"}, -] - -[package.dependencies] -setproctitle = "*" - -[package.extras] -dev = ["auditwheel", "wheel"] - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "pyzmq" -version = "24.0.1" -description = "Python bindings for 0MQ" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:28b119ba97129d3001673a697b7cce47fe6de1f7255d104c2f01108a5179a066"}, - {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bcbebd369493d68162cddb74a9c1fcebd139dfbb7ddb23d8f8e43e6c87bac3a6"}, - {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae61446166983c663cee42c852ed63899e43e484abf080089f771df4b9d272ef"}, - {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f7ac99b15270db8d53f28c3c7b968612993a90a5cf359da354efe96f5372b4"}, - {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca7c3956b03b7663fac4d150f5e6d4f6f38b2462c1e9afd83bcf7019f17913"}, - {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8c78bfe20d4c890cb5580a3b9290f700c570e167d4cdcc55feec07030297a5e3"}, - {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:48f721f070726cd2a6e44f3c33f8ee4b24188e4b816e6dd8ba542c8c3bb5b246"}, - {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afe1f3bc486d0ce40abb0a0c9adb39aed3bbac36ebdc596487b0cceba55c21c1"}, - {file = "pyzmq-24.0.1-cp310-cp310-win32.whl", hash = "sha256:3e6192dbcefaaa52ed81be88525a54a445f4b4fe2fffcae7fe40ebb58bd06bfd"}, - {file = "pyzmq-24.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:86de64468cad9c6d269f32a6390e210ca5ada568c7a55de8e681ca3b897bb340"}, - {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:838812c65ed5f7c2bd11f7b098d2e5d01685a3f6d1f82849423b570bae698c00"}, - {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dfb992dbcd88d8254471760879d48fb20836d91baa90f181c957122f9592b3dc"}, - {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7abddb2bd5489d30ffeb4b93a428130886c171b4d355ccd226e83254fcb6b9ef"}, - {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94010bd61bc168c103a5b3b0f56ed3b616688192db7cd5b1d626e49f28ff51b3"}, - {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8242543c522d84d033fe79be04cb559b80d7eb98ad81b137ff7e0a9020f00ace"}, - {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ccb94342d13e3bf3ffa6e62f95b5e3f0bc6bfa94558cb37f4b3d09d6feb536ff"}, - {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6640f83df0ae4ae1104d4c62b77e9ef39be85ebe53f636388707d532bee2b7b8"}, - {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a180dbd5ea5d47c2d3b716d5c19cc3fb162d1c8db93b21a1295d69585bfddac1"}, - {file = "pyzmq-24.0.1-cp311-cp311-win32.whl", hash = "sha256:624321120f7e60336be8ec74a172ae7fba5c3ed5bf787cc85f7e9986c9e0ebc2"}, - {file = "pyzmq-24.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:1724117bae69e091309ffb8255412c4651d3f6355560d9af312d547f6c5bc8b8"}, - {file = "pyzmq-24.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:15975747462ec49fdc863af906bab87c43b2491403ab37a6d88410635786b0f4"}, - {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b947e264f0e77d30dcbccbb00f49f900b204b922eb0c3a9f0afd61aaa1cedc3d"}, - {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ec91f1bad66f3ee8c6deb65fa1fe418e8ad803efedd69c35f3b5502f43bd1dc"}, - {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:db03704b3506455d86ec72c3358a779e9b1d07b61220dfb43702b7b668edcd0d"}, - {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e7e66b4e403c2836ac74f26c4b65d8ac0ca1eef41dfcac2d013b7482befaad83"}, - {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7a23ccc1083c260fa9685c93e3b170baba45aeed4b524deb3f426b0c40c11639"}, - {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fa0ae3275ef706c0309556061185dd0e4c4cd3b7d6f67ae617e4e677c7a41e2e"}, - {file = "pyzmq-24.0.1-cp36-cp36m-win32.whl", hash = "sha256:f01de4ec083daebf210531e2cca3bdb1608dbbbe00a9723e261d92087a1f6ebc"}, - {file = "pyzmq-24.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:de4217b9eb8b541cf2b7fde4401ce9d9a411cc0af85d410f9d6f4333f43640be"}, - {file = "pyzmq-24.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:78068e8678ca023594e4a0ab558905c1033b2d3e806a0ad9e3094e231e115a33"}, - {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77c2713faf25a953c69cf0f723d1b7dd83827b0834e6c41e3fb3bbc6765914a1"}, - {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bb4af15f305056e95ca1bd086239b9ebc6ad55e9f49076d27d80027f72752f6"}, - {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0f14cffd32e9c4c73da66db97853a6aeceaac34acdc0fae9e5bbc9370281864c"}, - {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0108358dab8c6b27ff6b985c2af4b12665c1bc659648284153ee501000f5c107"}, - {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d66689e840e75221b0b290b0befa86f059fb35e1ee6443bce51516d4d61b6b99"}, - {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae08ac90aa8fa14caafc7a6251bd218bf6dac518b7bff09caaa5e781119ba3f2"}, - {file = "pyzmq-24.0.1-cp37-cp37m-win32.whl", hash = "sha256:8421aa8c9b45ea608c205db9e1c0c855c7e54d0e9c2c2f337ce024f6843cab3b"}, - {file = "pyzmq-24.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54d8b9c5e288362ec8595c1d98666d36f2070fd0c2f76e2b3c60fbad9bd76227"}, - {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:acbd0a6d61cc954b9f535daaa9ec26b0a60a0d4353c5f7c1438ebc88a359a47e"}, - {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:47b11a729d61a47df56346283a4a800fa379ae6a85870d5a2e1e4956c828eedc"}, - {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abe6eb10122f0d746a0d510c2039ae8edb27bc9af29f6d1b05a66cc2401353ff"}, - {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:07bec1a1b22dacf718f2c0e71b49600bb6a31a88f06527dfd0b5aababe3fa3f7"}, - {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d945a85b70da97ae86113faf9f1b9294efe66bd4a5d6f82f2676d567338b66"}, - {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1b7928bb7580736ffac5baf814097be342ba08d3cfdfb48e52773ec959572287"}, - {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b946da90dc2799bcafa682692c1d2139b2a96ec3c24fa9fc6f5b0da782675330"}, - {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c8840f064b1fb377cffd3efeaad2b190c14d4c8da02316dae07571252d20b31f"}, - {file = "pyzmq-24.0.1-cp38-cp38-win32.whl", hash = "sha256:4854f9edc5208f63f0841c0c667260ae8d6846cfa233c479e29fdc85d42ebd58"}, - {file = "pyzmq-24.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:42d4f97b9795a7aafa152a36fe2ad44549b83a743fd3e77011136def512e6c2a"}, - {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:52afb0ac962963fff30cf1be775bc51ae083ef4c1e354266ab20e5382057dd62"}, - {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bad8210ad4df68c44ff3685cca3cda448ee46e20d13edcff8909eba6ec01ca4"}, - {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dabf1a05318d95b1537fd61d9330ef4313ea1216eea128a17615038859da3b3b"}, - {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5bd3d7dfd9cd058eb68d9a905dec854f86649f64d4ddf21f3ec289341386c44b"}, - {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8012bce6836d3f20a6c9599f81dfa945f433dab4dbd0c4917a6fb1f998ab33d"}, - {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c31805d2c8ade9b11feca4674eee2b9cce1fec3e8ddb7bbdd961a09dc76a80ea"}, - {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3104f4b084ad5d9c0cb87445cc8cfd96bba710bef4a66c2674910127044df209"}, - {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:df0841f94928f8af9c7a1f0aaaffba1fb74607af023a152f59379c01c53aee58"}, - {file = "pyzmq-24.0.1-cp39-cp39-win32.whl", hash = "sha256:a435ef8a3bd95c8a2d316d6e0ff70d0db524f6037411652803e118871d703333"}, - {file = "pyzmq-24.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:2032d9cb994ce3b4cba2b8dfae08c7e25bc14ba484c770d4d3be33c27de8c45b"}, - {file = "pyzmq-24.0.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bb5635c851eef3a7a54becde6da99485eecf7d068bd885ac8e6d173c4ecd68b0"}, - {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:83ea1a398f192957cb986d9206ce229efe0ee75e3c6635baff53ddf39bd718d5"}, - {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:941fab0073f0a54dc33d1a0460cb04e0d85893cb0c5e1476c785000f8b359409"}, - {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8f482c44ccb5884bf3f638f29bea0f8dc68c97e38b2061769c4cb697f6140d"}, - {file = "pyzmq-24.0.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:613010b5d17906c4367609e6f52e9a2595e35d5cc27d36ff3f1b6fa6e954d944"}, - {file = "pyzmq-24.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:65c94410b5a8355cfcf12fd600a313efee46ce96a09e911ea92cf2acf6708804"}, - {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:20e7eeb1166087db636c06cae04a1ef59298627f56fb17da10528ab52a14c87f"}, - {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2712aee7b3834ace51738c15d9ee152cc5a98dc7d57dd93300461b792ab7b43"}, - {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7c280185c4da99e0cc06c63bdf91f5b0b71deb70d8717f0ab870a43e376db8"}, - {file = "pyzmq-24.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:858375573c9225cc8e5b49bfac846a77b696b8d5e815711b8d4ba3141e6e8879"}, - {file = "pyzmq-24.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:80093b595921eed1a2cead546a683b9e2ae7f4a4592bb2ab22f70d30174f003a"}, - {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f3f3154fde2b1ff3aa7b4f9326347ebc89c8ef425ca1db8f665175e6d3bd42f"}, - {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb756147314430bee5d10919b8493c0ccb109ddb7f5dfd2fcd7441266a25b75"}, - {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e706bac34e9f50779cb8c39f10b53a4d15aebb97235643d3112ac20bd577b4"}, - {file = "pyzmq-24.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:687700f8371643916a1d2c61f3fdaa630407dd205c38afff936545d7b7466066"}, - {file = "pyzmq-24.0.1.tar.gz", hash = "sha256:216f5d7dbb67166759e59b0479bca82b8acf9bed6015b526b8eb10143fb08e77"}, -] - -[package.dependencies] -cffi = {version = "*", markers = "implementation_name == \"pypy\""} -py = {version = "*", markers = "implementation_name == \"pypy\""} - -[[package]] -name = "qdldl" -version = "0.1.7.post2" -description = "QDLDL, a free LDL factorization routine." -optional = true -python-versions = "*" -files = [ - {file = "qdldl-0.1.7.post2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c8d39035a64fbe4dc8d73501a444374787c087b202e875f6f0cd7e7ca166e25"}, - {file = "qdldl-0.1.7.post2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f93cd0b3338ef53efa512168d32991bff00b292d6eaf5e35427ca84622158132"}, - {file = "qdldl-0.1.7.post2-cp310-cp310-win_amd64.whl", hash = "sha256:02d6b531fe669f8894f97f697da88fe3f1c3af0c7ef12f260bf1d20e8e70f0dc"}, - {file = "qdldl-0.1.7.post2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:25180228613c66a5af7d6a648920fb71122e1ed58daf09621ed6b939b7f9aa04"}, - {file = "qdldl-0.1.7.post2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa5cf2125dea734f8977df22859b3826fae8960b35caf75cf5b16306ff1d1dc3"}, - {file = "qdldl-0.1.7.post2-cp311-cp311-win_amd64.whl", hash = "sha256:f14cda7c484dbe78e333bb52849031081d37e7e3bf961ed7bb77ed11489f16f6"}, - {file = "qdldl-0.1.7.post2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:90982ca5069077cfb8ba9ea7422c6e2a80a3af89266252d1e7a51ea5e87c8188"}, - {file = "qdldl-0.1.7.post2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5572de51c7c86b6f30cd1b5a88dd5459d48e1c3432b1a04c2f4ad669f6e827"}, - {file = "qdldl-0.1.7.post2-cp312-cp312-win_amd64.whl", hash = "sha256:a612a4dfd94f977c5c9a4389363d4c661f54a6df59c75d462adb93922d92b45f"}, - {file = "qdldl-0.1.7.post2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:97929fd925833306e3c625c92813ac0dea9a53e6b9f680e2c5da9f0c1c641b19"}, - {file = "qdldl-0.1.7.post2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b833a64ee32d1e14a3acd59c3e62ea1dae551e6bf3265e42cb4197c4353ea02"}, - {file = "qdldl-0.1.7.post2-cp36-cp36m-win_amd64.whl", hash = "sha256:f686923c983f62fc7ccc7ebda6fd6ed5f623d299c211933896c7d396c88bb012"}, - {file = "qdldl-0.1.7.post2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8206013ec1fb4c6a396aeb0ef0c68f07d85eb5c22fe7e31cb8307f05787878b9"}, - {file = "qdldl-0.1.7.post2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24e01593cd46c86f5812f1362987bc8ab1080c493f4c7e436e144d2c10481b81"}, - {file = "qdldl-0.1.7.post2-cp37-cp37m-win_amd64.whl", hash = "sha256:06c2157ea1c2f691c956d92895722777eaf8c9ebf9c2c1af4a0b947eccc8b31e"}, - {file = "qdldl-0.1.7.post2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:77547f9e58522b2445b846ab6f0fd09a689f9fb2c0e72d44d14f631054d7dd55"}, - {file = "qdldl-0.1.7.post2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19825b998202dcd2579182a0a46350bdd95421ec65fd7579326083dc9f1c4d37"}, - {file = "qdldl-0.1.7.post2-cp38-cp38-win_amd64.whl", hash = "sha256:4c08e68cf7c051c0ce9c3846cda60a9c10aa53eb6b240998362dd6fd9410779b"}, - {file = "qdldl-0.1.7.post2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:edf202648cb0377fd78dd1e59816ccd92fabdee676eb3a7b618c66b393634d10"}, - {file = "qdldl-0.1.7.post2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22bc364e665ea8a2f8f661dbd22321ff365663b33b9ceaf429b94c2661cd11ac"}, - {file = "qdldl-0.1.7.post2-cp39-cp39-win_amd64.whl", hash = "sha256:b7dec0fba6204e83aa4b5e71e84ea2e89cfebeef5b47b70443f8cf9fa12a9752"}, - {file = "qdldl-0.1.7.post2.tar.gz", hash = "sha256:4b1539a5ec10cc757afd7156d7deb4006007cad86d774c9f0fdc3e34415557d3"}, -] - -[package.dependencies] -numpy = ">=1.7" -scipy = ">=0.13.2" - -[[package]] -name = "qpd" -version = "0.4.4" -description = "Query Pandas Using SQL" -optional = true -python-versions = ">=3.7" -files = [ - {file = "qpd-0.4.4-py3-none-any.whl", hash = "sha256:fc02b53d990f505353ec495682fbc107dfc06c59e66d2206b5d2db2b5700b629"}, - {file = "qpd-0.4.4.tar.gz", hash = "sha256:e0ed05b88e321ea9935874377bda11339c90f1469f34344e9b41d16b8088e136"}, -] - -[package.dependencies] -adagio = "*" -antlr4-python3-runtime = ">=4.11.1,<4.12" -pandas = ">=1.2.0" -triad = ">=0.9.0" - -[package.extras] -all = ["cloudpickle (>=1.4.0)", "dask[dataframe,distributed]"] -dask = ["cloudpickle (>=1.4.0)", "dask[dataframe,distributed]"] - -[[package]] -name = "quandl" -version = "3.7.0" -description = "Package for quandl API access" -optional = false -python-versions = ">= 3.6" -files = [ - {file = "Quandl-3.7.0-py2.py3-none-any.whl", hash = "sha256:0e3e5dc60fd057c73c67380b1b0f2e3dc0e4c500fb5e6e146ac3a3c0d992cd1d"}, - {file = "Quandl-3.7.0.tar.gz", hash = "sha256:6e0b82fbc7861610b3577c5397277c4220e065eee0fed4e46cd6b6021655b64c"}, -] - -[package.dependencies] -inflection = ">=0.3.1" -more-itertools = "*" -numpy = ">=1.8" -pandas = ">=0.14" -python-dateutil = "*" -requests = ">=2.7.0" -six = "*" - -[[package]] -name = "rdflib" -version = "7.0.0" -description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information." -optional = false -python-versions = ">=3.8.1,<4.0.0" -files = [ - {file = "rdflib-7.0.0-py3-none-any.whl", hash = "sha256:0438920912a642c866a513de6fe8a0001bd86ef975057d6962c79ce4771687cd"}, - {file = "rdflib-7.0.0.tar.gz", hash = "sha256:9995eb8569428059b8c1affd26b25eac510d64f5043d9ce8c84e0d0036e995ae"}, -] - -[package.dependencies] -isodate = ">=0.6.0,<0.7.0" -pyparsing = ">=2.1.0,<4" - -[package.extras] -berkeleydb = ["berkeleydb (>=18.1.0,<19.0.0)"] -html = ["html5lib (>=1.0,<2.0)"] -lxml = ["lxml (>=4.3.0,<5.0.0)"] -networkx = ["networkx (>=2.0.0,<3.0.0)"] - -[[package]] -name = "regex" -version = "2024.4.16" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.7" -files = [ - {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08"}, - {file = "regex-2024.4.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18"}, - {file = "regex-2024.4.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47"}, - {file = "regex-2024.4.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af"}, - {file = "regex-2024.4.16-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a"}, - {file = "regex-2024.4.16-cp310-cp310-win32.whl", hash = "sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0"}, - {file = "regex-2024.4.16-cp310-cp310-win_amd64.whl", hash = "sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd"}, - {file = "regex-2024.4.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89"}, - {file = "regex-2024.4.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d"}, - {file = "regex-2024.4.16-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6"}, - {file = "regex-2024.4.16-cp311-cp311-win32.whl", hash = "sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d"}, - {file = "regex-2024.4.16-cp311-cp311-win_amd64.whl", hash = "sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec"}, - {file = "regex-2024.4.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70"}, - {file = "regex-2024.4.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81"}, - {file = "regex-2024.4.16-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc"}, - {file = "regex-2024.4.16-cp312-cp312-win32.whl", hash = "sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b"}, - {file = "regex-2024.4.16-cp312-cp312-win_amd64.whl", hash = "sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9"}, - {file = "regex-2024.4.16-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682"}, - {file = "regex-2024.4.16-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5"}, - {file = "regex-2024.4.16-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a"}, - {file = "regex-2024.4.16-cp37-cp37m-win32.whl", hash = "sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46"}, - {file = "regex-2024.4.16-cp37-cp37m-win_amd64.whl", hash = "sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4"}, - {file = "regex-2024.4.16-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d"}, - {file = "regex-2024.4.16-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76"}, - {file = "regex-2024.4.16-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9"}, - {file = "regex-2024.4.16-cp38-cp38-win32.whl", hash = "sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e"}, - {file = "regex-2024.4.16-cp38-cp38-win_amd64.whl", hash = "sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455"}, - {file = "regex-2024.4.16-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317"}, - {file = "regex-2024.4.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c"}, - {file = "regex-2024.4.16-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50"}, - {file = "regex-2024.4.16-cp39-cp39-win32.whl", hash = "sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335"}, - {file = "regex-2024.4.16-cp39-cp39-win_amd64.whl", hash = "sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483"}, - {file = "regex-2024.4.16.tar.gz", hash = "sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0"}, -] - -[[package]] -name = "reportlab" -version = "3.6.13" -description = "The Reportlab Toolkit" -optional = false -python-versions = ">=3.7,<4" -files = [ - {file = "reportlab-3.6.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a0330322c6c8123745ac7667fcc6ae3e0de3b73c15bdfaa28c788a9eaa0f50da"}, - {file = "reportlab-3.6.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:753485bb2b18cbd11340e227e4aaf9bde3bb64f83406dfa011e92ad0231a42c9"}, - {file = "reportlab-3.6.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58ea3471b9b4b8e7952bd357e8487789da11213470be328ffb3e5b7d7690c2c7"}, - {file = "reportlab-3.6.13-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1993a68c0edc45895d3df350d01b0456efe79aaf309cef777762742be501f2a"}, - {file = "reportlab-3.6.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca8eb7a6607f8a664187a330bab9f8d11c9f81ed885e063dfbb29a130944a72a"}, - {file = "reportlab-3.6.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57add04824bca89a130f9d428ace1b003cce4061386e0ec2a1b45b554ffe7aa3"}, - {file = "reportlab-3.6.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e98965c6e60d76ff63989d9400ae8e65efd67c665d785b377f438f166a57c053"}, - {file = "reportlab-3.6.13-cp310-cp310-win32.whl", hash = "sha256:3cb0da4975dbade6cc2ea6b0b0b17578af266dc3f669e959648f3306af993369"}, - {file = "reportlab-3.6.13-cp310-cp310-win_amd64.whl", hash = "sha256:65b441e22d8fe93154567a30662d8539e639b78142815afcaf92b388846eb3c1"}, - {file = "reportlab-3.6.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d59e62faa03003be81aa14d37ac34ea110e5ac59c8678fd4c0daa7d8b8f42096"}, - {file = "reportlab-3.6.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afb418409e0d323c6cb5e3be7ea4d14dfbf8a07eb03ab0b0062904cacf819878"}, - {file = "reportlab-3.6.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a477f652e6c417ad40387a8498d9ad827421006f156aab16f67adc9b81699a72"}, - {file = "reportlab-3.6.13-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b94e4f65a5f77a631cc010c9a7892d69e33f3251b760639dcc76420e138ce95"}, - {file = "reportlab-3.6.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7efdf68e97e8fea8683bfc17f25747fefbda729b9018bc2e3221658ac41ee0bd"}, - {file = "reportlab-3.6.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28a8d9cf462e2b4c9e71abd0630f9ec245d88b976b283b0dbb4602c9ddb3938"}, - {file = "reportlab-3.6.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d95fc8bc177a009053548c6d851a513b2147c465a5e8fea82287ea22d6825c4e"}, - {file = "reportlab-3.6.13-cp311-cp311-win32.whl", hash = "sha256:48eadd93237c7e2739525c74cf6615dd6c1a767c839f4b0d7c12167dc0b09911"}, - {file = "reportlab-3.6.13-cp311-cp311-win_amd64.whl", hash = "sha256:cca2d4c783f985b91b98e80d09ac79b6ed3f317a729cba5ba86edfe5eb9a2d9c"}, - {file = "reportlab-3.6.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:149718c3eaee937f28094325f0dd9ae1add3172c2dacbb93ff5403f37c9d3c57"}, - {file = "reportlab-3.6.13-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8260c002e4845a5af65908d5ee2099bcc25a16c7646c5c417fa27f1e4b844bc1"}, - {file = "reportlab-3.6.13-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e4983486d419daa45cade40874bb869976e27ba11f77fb4b9ae32417284ade7"}, - {file = "reportlab-3.6.13-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ea46fef07c588fef84d1164d4788fef322b39feb2bfb2df0a0706181dff79b8"}, - {file = "reportlab-3.6.13-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5949f3b4e207fa7901c0cc3b49470b2a3372617a47dfbc892db31c2b56af296"}, - {file = "reportlab-3.6.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0d91663d450c11404ec189ebc5a4abdf20f7c4eca5954a920427cdbf5601525"}, - {file = "reportlab-3.6.13-cp37-cp37m-win32.whl", hash = "sha256:269c59e508df08be498ab9e5278addb2cc16989677a03f800b17f8a31f8c5cc7"}, - {file = "reportlab-3.6.13-cp37-cp37m-win_amd64.whl", hash = "sha256:21d6b6bcdecee9c7ce047156d0553a30d736b8172629e4c0fcacab35ba261f3b"}, - {file = "reportlab-3.6.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36568d3cb4101a210c4d821d9101635c2ef6e06bd649335938c01eb197f50c5d"}, - {file = "reportlab-3.6.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a043cff1781ddb2a0ba0e8e760a79fc5be2430957c4f2a1f51bd4528cc53178f"}, - {file = "reportlab-3.6.13-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbddadca6f08212732e83a60e30a42cfc7d2695892cedea208b3c3e7131c9993"}, - {file = "reportlab-3.6.13-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faeebde62f0f6ad86985bec5685411260393d2eb7ba907972da56af586b644e8"}, - {file = "reportlab-3.6.13-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff09a0a1e5cef05309ac09dfc5185e8151d927bcf45470d2f540c96260f8a355"}, - {file = "reportlab-3.6.13-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbdbba1ec3498b17eefca14d424ee90bb95b53e1423ecb22f1c17733c3406559"}, - {file = "reportlab-3.6.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba6f533b262f4ee1636b754992bb2fb349df0500d765ac9be014a375c047f4db"}, - {file = "reportlab-3.6.13-cp38-cp38-win32.whl", hash = "sha256:7ff89011b5ee30209b3106641e3b7b4959f10aa6e9d6f3030205123c178f605d"}, - {file = "reportlab-3.6.13-cp38-cp38-win_amd64.whl", hash = "sha256:8f00175f8e12e6f7d3a01309de6d7008fac94a2cdce6837ad066f0961472c9e5"}, - {file = "reportlab-3.6.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a4dbc28fede7f504b9ac65ce9cbea35585e999d63f9fa68bc73f5a75b4929302"}, - {file = "reportlab-3.6.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9f869286fcefa7f8e89e38448309891ff110ad74f58a7317ec204f3d4b8ad5f5"}, - {file = "reportlab-3.6.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e13a4e81761636591f5b60104f6e1eec70832ffd9aa781db68d7ebb576970d4b"}, - {file = "reportlab-3.6.13-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fdac930dfdc6227720545ec44fdb396e92d53ec227a6f5ae58cc8cb9a6cbe89"}, - {file = "reportlab-3.6.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:701290747662d2b3be49fc0de33898ecc9ce3fafe0e2887d406e24693465e5ae"}, - {file = "reportlab-3.6.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b690bc30f58931b0abd47635d93a43a82d67972e83a6511cc8adbcd7da25310"}, - {file = "reportlab-3.6.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6172481e8acffcf72042653e977281fbd807a41705a39456d92d2606d8b8c5e2"}, - {file = "reportlab-3.6.13-cp39-cp39-win32.whl", hash = "sha256:5a460f4c0c30bdf9d7bef46a816671a4386a9253670a53d35c694c666544261f"}, - {file = "reportlab-3.6.13-cp39-cp39-win_amd64.whl", hash = "sha256:11a71c314183532d889ad4b3941f61c3fe4bfdda769c768a7f02d93cb69dd1bb"}, - {file = "reportlab-3.6.13.tar.gz", hash = "sha256:6f75d33f7a3720cf47371ab63ced0f0ebd1aeb6db19386ae92f8977a09be9611"}, -] - -[package.dependencies] -pillow = ">=9.0.0" - -[package.extras] -fttextpath = ["freetype-py (>=2.3.0,<2.4)"] -rlpycairo = ["rlPyCairo (>=0.1.0)"] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "requests-futures" -version = "1.0.1" -description = "Asynchronous Python HTTP for Humans." -optional = false -python-versions = "*" -files = [ - {file = "requests-futures-1.0.1.tar.gz", hash = "sha256:f55a4ef80070e2858e7d1e73123d2bfaeaf25b93fd34384d8ddf148e2b676373"}, - {file = "requests_futures-1.0.1-py2.py3-none-any.whl", hash = "sha256:4a2f5472e9911a79532137d156aa937cd9cd90fec55677f71b2976d1f7a66d38"}, -] - -[package.dependencies] -requests = ">=1.2.0" - -[package.extras] -dev = ["black (>=22.3.0)", "build (>=0.7.0)", "isort (>=5.11.4)", "pyflakes (>=2.2.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "pytest-network (>=0.0.1)", "readme-renderer[rst] (>=26.0)", "twine (>=3.4.2)"] - -[[package]] -name = "requests-oauthlib" -version = "2.0.0" -description = "OAuthlib authentication support for Requests." -optional = true -python-versions = ">=3.4" -files = [ - {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, - {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, -] - -[package.dependencies] -oauthlib = ">=3.0.0" -requests = ">=2.0.0" - -[package.extras] -rsa = ["oauthlib[signedtoken] (>=3.0.0)"] - -[[package]] -name = "retrying" -version = "1.3.4" -description = "Retrying" -optional = false -python-versions = "*" -files = [ - {file = "retrying-1.3.4-py3-none-any.whl", hash = "sha256:8cc4d43cb8e1125e0ff3344e9de678fefd85db3b750b81b2240dc0183af37b35"}, - {file = "retrying-1.3.4.tar.gz", hash = "sha256:345da8c5765bd982b1d1915deb9102fd3d1f7ad16bd84a9700b85f64d24e8f3e"}, -] - -[package.dependencies] -six = ">=1.7.0" - -[[package]] -name = "rfc3339-validator" -version = "0.1.4" -description = "A pure python RFC3339 validator" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, - {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, -] - -[package.dependencies] -six = "*" - -[[package]] -name = "rfc3986-validator" -version = "0.1.1" -description = "Pure python rfc3986 validator" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"}, - {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"}, -] - -[[package]] -name = "rich" -version = "12.6.0" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.6.3,<4.0.0" -files = [ - {file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"}, - {file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"}, -] - -[package.dependencies] -commonmark = ">=0.9.0,<0.10.0" -pygments = ">=2.6.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] - -[[package]] -name = "riskfolio-lib" -version = "3.3.0" -description = "Portfolio Optimization and Quantitative Strategic Asset Allocation in Python" -optional = true -python-versions = ">=3.7" -files = [ - {file = "Riskfolio-Lib-3.3.0.tar.gz", hash = "sha256:29d0ff046952304ee8362d914551d82cdb2aa663728a6b1df80365c589a626b4"}, - {file = "Riskfolio_Lib-3.3.0-py3-none-any.whl", hash = "sha256:17f835ba47e946f55eae5041bdc45dcdb6a0f72136361cd6cb16fc608968fce2"}, -] - -[package.dependencies] -arch = ">=4.15" -astropy = ">=4.3.1" -cvxpy = ">=1.0.25" -matplotlib = ">=3.3.0" -networkx = ">=2.5.1" -numpy = ">=1.17.0" -pandas = ">=1.0.0" -scikit-learn = ">=0.22.0" -scipy = ">=1.0.1" -statsmodels = ">=0.10.1" -xlsxwriter = ">=1.3.7" - -[[package]] -name = "rsa" -version = "4.9" -description = "Pure-Python RSA implementation" -optional = false -python-versions = ">=3.6,<4" -files = [ - {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, - {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, -] - -[package.dependencies] -pyasn1 = ">=0.1.3" - -[[package]] -name = "ruamel-yaml" -version = "0.17.40" -description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -optional = false -python-versions = ">=3" -files = [ - {file = "ruamel.yaml-0.17.40-py3-none-any.whl", hash = "sha256:b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c"}, - {file = "ruamel.yaml-0.17.40.tar.gz", hash = "sha256:6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d"}, -] - -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} - -[package.extras] -docs = ["mercurial (>5.7)", "ryd"] -jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] - -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.8" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -optional = false -python-versions = ">=3.6" -files = [ - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, - {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, - {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, -] - -[[package]] -name = "ruff" -version = "0.0.285" -description = "An extremely fast Python linter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.0.285-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:72a3a0936369b986b0e959f9090206ed3c18f9e5e439ea5b8e6867c6707aded5"}, - {file = "ruff-0.0.285-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:0d9ab6ad16742eb78919e0fba09f914f042409df40ad63423c34bb20d350162a"}, - {file = "ruff-0.0.285-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c48926156288b8ac005eb1db5e77c15e8a37309ae49d9fb6771d5cf5f777590"}, - {file = "ruff-0.0.285-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d2a60c102e7a5e147b58fc2cbea12a563c565383effc527c987ea2086a05742"}, - {file = "ruff-0.0.285-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b02aae62f922d088bb01943e1dbd861688ada13d735b78b8348a7d90121fd292"}, - {file = "ruff-0.0.285-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f572c4296d8c7ddd22c3204de4031965be524fdd1fdaaef273945932912b28c5"}, - {file = "ruff-0.0.285-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80effdf4fe69763d69eb4ab9443e186fd09e668b59fe70ba4b49f4c077d15a1b"}, - {file = "ruff-0.0.285-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5977ce304da35c263f5e082901bd7ac0bd2be845a8fcfd1a29e4d6680cddb307"}, - {file = "ruff-0.0.285-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a087712d474fa17b915d7cb9ef807e1256182b12ddfafb105eb00aeee48d1a"}, - {file = "ruff-0.0.285-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7ce67736cd8dfe97162d1e7adfc2d9a1bac0efb9aaaff32e4042c7cde079f54b"}, - {file = "ruff-0.0.285-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5473a4c6cac34f583bff08c5f63b8def5599a0ea4dc96c0302fbd2cc0b3ecbad"}, - {file = "ruff-0.0.285-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e6b1c961d608d373a032f047a20bf3c55ad05f56c32e7b96dcca0830a2a72348"}, - {file = "ruff-0.0.285-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2933cc9631f453305399c7b8fb72b113ad76b49ae1d7103cc4afd3a423bed164"}, - {file = "ruff-0.0.285-py3-none-win32.whl", hash = "sha256:770c5eb6376de024111443022cda534fb28980a9dd3b4abc83992a8770167ba6"}, - {file = "ruff-0.0.285-py3-none-win_amd64.whl", hash = "sha256:a8c6ad6b9cd77489bf6d1510950cbbe47a843aa234adff0960bae64bd06c3b6d"}, - {file = "ruff-0.0.285-py3-none-win_arm64.whl", hash = "sha256:de44fbc6c3b25fccee473ddf851416fd4e246fc6027b2197c395b1b3b3897921"}, - {file = "ruff-0.0.285.tar.gz", hash = "sha256:45866048d1dcdcc80855998cb26c4b2b05881f9e043d2e3bfe1aa36d9a2e8f28"}, -] - -[[package]] -name = "scikit-learn" -version = "1.3.2" -description = "A set of python modules for machine learning and data mining" -optional = false -python-versions = ">=3.8" -files = [ - {file = "scikit-learn-1.3.2.tar.gz", hash = "sha256:a2f54c76accc15a34bfb9066e6c7a56c1e7235dda5762b990792330b52ccfb05"}, - {file = "scikit_learn-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e326c0eb5cf4d6ba40f93776a20e9a7a69524c4db0757e7ce24ba222471ee8a1"}, - {file = "scikit_learn-1.3.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:535805c2a01ccb40ca4ab7d081d771aea67e535153e35a1fd99418fcedd1648a"}, - {file = "scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1215e5e58e9880b554b01187b8c9390bf4dc4692eedeaf542d3273f4785e342c"}, - {file = "scikit_learn-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ee107923a623b9f517754ea2f69ea3b62fc898a3641766cb7deb2f2ce450161"}, - {file = "scikit_learn-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:35a22e8015048c628ad099da9df5ab3004cdbf81edc75b396fd0cff8699ac58c"}, - {file = "scikit_learn-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6fb6bc98f234fda43163ddbe36df8bcde1d13ee176c6dc9b92bb7d3fc842eb66"}, - {file = "scikit_learn-1.3.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:18424efee518a1cde7b0b53a422cde2f6625197de6af36da0b57ec502f126157"}, - {file = "scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3271552a5eb16f208a6f7f617b8cc6d1f137b52c8a1ef8edf547db0259b2c9fb"}, - {file = "scikit_learn-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4144a5004a676d5022b798d9e573b05139e77f271253a4703eed295bde0433"}, - {file = "scikit_learn-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:67f37d708f042a9b8d59551cf94d30431e01374e00dc2645fa186059c6c5d78b"}, - {file = "scikit_learn-1.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8db94cd8a2e038b37a80a04df8783e09caac77cbe052146432e67800e430c028"}, - {file = "scikit_learn-1.3.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:61a6efd384258789aa89415a410dcdb39a50e19d3d8410bd29be365bcdd512d5"}, - {file = "scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb06f8dce3f5ddc5dee1715a9b9f19f20d295bed8e3cd4fa51e1d050347de525"}, - {file = "scikit_learn-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b2de18d86f630d68fe1f87af690d451388bb186480afc719e5f770590c2ef6c"}, - {file = "scikit_learn-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:0402638c9a7c219ee52c94cbebc8fcb5eb9fe9c773717965c1f4185588ad3107"}, - {file = "scikit_learn-1.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a19f90f95ba93c1a7f7924906d0576a84da7f3b2282ac3bfb7a08a32801add93"}, - {file = "scikit_learn-1.3.2-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:b8692e395a03a60cd927125eef3a8e3424d86dde9b2370d544f0ea35f78a8073"}, - {file = "scikit_learn-1.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15e1e94cc23d04d39da797ee34236ce2375ddea158b10bee3c343647d615581d"}, - {file = "scikit_learn-1.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:785a2213086b7b1abf037aeadbbd6d67159feb3e30263434139c98425e3dcfcf"}, - {file = "scikit_learn-1.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:64381066f8aa63c2710e6b56edc9f0894cc7bf59bd71b8ce5613a4559b6145e0"}, - {file = "scikit_learn-1.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6c43290337f7a4b969d207e620658372ba3c1ffb611f8bc2b6f031dc5c6d1d03"}, - {file = "scikit_learn-1.3.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:dc9002fc200bed597d5d34e90c752b74df516d592db162f756cc52836b38fe0e"}, - {file = "scikit_learn-1.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d08ada33e955c54355d909b9c06a4789a729977f165b8bae6f225ff0a60ec4a"}, - {file = "scikit_learn-1.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763f0ae4b79b0ff9cca0bf3716bcc9915bdacff3cebea15ec79652d1cc4fa5c9"}, - {file = "scikit_learn-1.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:ed932ea780517b00dae7431e031faae6b49b20eb6950918eb83bd043237950e0"}, -] - -[package.dependencies] -joblib = ">=1.1.1" -numpy = ">=1.17.3,<2.0" -scipy = ">=1.5.0" -threadpoolctl = ">=2.0.0" - -[package.extras] -benchmark = ["matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "pandas (>=1.0.5)"] -docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)", "sphinx (>=6.0.0)", "sphinx-copybutton (>=0.5.2)", "sphinx-gallery (>=0.10.1)", "sphinx-prompt (>=1.3.0)", "sphinxext-opengraph (>=0.4.2)"] -examples = ["matplotlib (>=3.1.3)", "pandas (>=1.0.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)"] -tests = ["black (>=23.3.0)", "matplotlib (>=3.1.3)", "mypy (>=1.3)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.0.272)", "scikit-image (>=0.16.2)"] - -[[package]] -name = "scipy" -version = "1.10.1" -description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = "<3.12,>=3.8" -files = [ - {file = "scipy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7354fd7527a4b0377ce55f286805b34e8c54b91be865bac273f527e1b839019"}, - {file = "scipy-1.10.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4b3f429188c66603a1a5c549fb414e4d3bdc2a24792e061ffbd607d3d75fd84e"}, - {file = "scipy-1.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1553b5dcddd64ba9a0d95355e63fe6c3fc303a8fd77c7bc91e77d61363f7433f"}, - {file = "scipy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c0ff64b06b10e35215abce517252b375e580a6125fd5fdf6421b98efbefb2d2"}, - {file = "scipy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:fae8a7b898c42dffe3f7361c40d5952b6bf32d10c4569098d276b4c547905ee1"}, - {file = "scipy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f1564ea217e82c1bbe75ddf7285ba0709ecd503f048cb1236ae9995f64217bd"}, - {file = "scipy-1.10.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d925fa1c81b772882aa55bcc10bf88324dadb66ff85d548c71515f6689c6dac5"}, - {file = "scipy-1.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaea0a6be54462ec027de54fca511540980d1e9eea68b2d5c1dbfe084797be35"}, - {file = "scipy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15a35c4242ec5f292c3dd364a7c71a61be87a3d4ddcc693372813c0b73c9af1d"}, - {file = "scipy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:43b8e0bcb877faf0abfb613d51026cd5cc78918e9530e375727bf0625c82788f"}, - {file = "scipy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5678f88c68ea866ed9ebe3a989091088553ba12c6090244fdae3e467b1139c35"}, - {file = "scipy-1.10.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:39becb03541f9e58243f4197584286e339029e8908c46f7221abeea4b749fa88"}, - {file = "scipy-1.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bce5869c8d68cf383ce240e44c1d9ae7c06078a9396df68ce88a1230f93a30c1"}, - {file = "scipy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07c3457ce0b3ad5124f98a86533106b643dd811dd61b548e78cf4c8786652f6f"}, - {file = "scipy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:049a8bbf0ad95277ffba9b3b7d23e5369cc39e66406d60422c8cfef40ccc8415"}, - {file = "scipy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd9f1027ff30d90618914a64ca9b1a77a431159df0e2a195d8a9e8a04c78abf9"}, - {file = "scipy-1.10.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:79c8e5a6c6ffaf3a2262ef1be1e108a035cf4f05c14df56057b64acc5bebffb6"}, - {file = "scipy-1.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51af417a000d2dbe1ec6c372dfe688e041a7084da4fdd350aeb139bd3fb55353"}, - {file = "scipy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b4735d6c28aad3cdcf52117e0e91d6b39acd4272f3f5cd9907c24ee931ad601"}, - {file = "scipy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ff7f37b1bf4417baca958d254e8e2875d0cc23aaadbe65b3d5b3077b0eb23ea"}, - {file = "scipy-1.10.1.tar.gz", hash = "sha256:2cf9dfb80a7b4589ba4c40ce7588986d6d5cebc5457cad2c2880f6bc2d42f3a5"}, -] - -[package.dependencies] -numpy = ">=1.19.5,<1.27.0" - -[package.extras] -dev = ["click", "doit (>=0.36.0)", "flake8", "mypy", "pycodestyle", "pydevtool", "rich-click", "typing_extensions"] -doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] -test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] - -[[package]] -name = "screeninfo" -version = "0.6.7" -description = "Fetch location and size of physical screens." -optional = false -python-versions = "*" -files = [ - {file = "screeninfo-0.6.7.tar.gz", hash = "sha256:1c4bac1ca329da3f68cbc4d2fbc92256aa9bb8ff8583ee3e14f91f0a7baa69cb"}, -] - -[package.dependencies] -Cython = {version = "*", markers = "sys_platform == \"darwin\""} -pyobjc-framework-Cocoa = {version = "*", markers = "sys_platform == \"darwin\""} - -[[package]] -name = "scs" -version = "3.2.4.post1" -description = "Splitting conic solver" -optional = true -python-versions = ">=3.7" -files = [ - {file = "scs-3.2.4.post1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51fed30d2a4a1e6fbfc1e52b4cb3adeecbe89d7c47f3539b49afbb852415fe19"}, - {file = "scs-3.2.4.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb0524c0b9c3ed0d65dae161475accf3efa8e170938eb93251a60e9709b156ee"}, - {file = "scs-3.2.4.post1-cp310-cp310-win_amd64.whl", hash = "sha256:534519819eea96f18902a9fce15c4ec562b99d23b38dc843a48cb137b5641613"}, - {file = "scs-3.2.4.post1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d04ee4d19ac6d0f5053663bc48fcd5c5faed534272f13b10a4e173c814eea69"}, - {file = "scs-3.2.4.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c23b4299ab77ff5f654573d5667dc982292a8ef2b979053b38c40663919f13"}, - {file = "scs-3.2.4.post1-cp311-cp311-win_amd64.whl", hash = "sha256:ae4624938d3e3a8b7e508029275c6ad7a978fd48c158d0818f69f4ae764bf945"}, - {file = "scs-3.2.4.post1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:40294e22bfe509bdf7fd65a6b77c38cec22dcb3567ff5a75f3c41a1faf2ef1d5"}, - {file = "scs-3.2.4.post1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a2337acb0604770b6df1473254065a51c210ff9c82fc7c4490595510287a337"}, - {file = "scs-3.2.4.post1-cp312-cp312-win_amd64.whl", hash = "sha256:8689e75a57e59846e65d1c4b9d57e9964b00fcbb8e67fc77f98cf6e0a0530abd"}, - {file = "scs-3.2.4.post1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ad991b00d0a87c85db57bf2f1863c21bdc4e2f13837f6c35e809f5936bc6f165"}, - {file = "scs-3.2.4.post1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a28af160a44268e726a59d6cf340629b82940c1a643c4c87fe777e9cbe550d75"}, - {file = "scs-3.2.4.post1-cp37-cp37m-win_amd64.whl", hash = "sha256:f6283f725f3fee63d4631c2532d01a5b2ea65883b04d3da3be06084b1c60171b"}, - {file = "scs-3.2.4.post1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b3a622cf2120ae765f0f3ad5c6f4f86796d317e29132bab2ad4af3c14d9bf4d"}, - {file = "scs-3.2.4.post1-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:4b5259137c263304effa2b28d0125437ac23569e6e7753c115ae1206ec5033fd"}, - {file = "scs-3.2.4.post1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424710bc19b0506feee7e05e6d2b7af98acf09af5bd5353126164cbd46ac923f"}, - {file = "scs-3.2.4.post1-cp38-cp38-win_amd64.whl", hash = "sha256:e21bdc8046648846e2c204a6c5cf24eaaedd2b8f5e0a2ab41a647b0247b8d592"}, - {file = "scs-3.2.4.post1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cea0f7e9473f43f7edf1641d020ead7e39653a81c540fbdba8f3b7b8480038c9"}, - {file = "scs-3.2.4.post1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6126f1d7ed5ff368cb8c1836715b17a50074314579eefc6d511995a3ab93d70"}, - {file = "scs-3.2.4.post1-cp39-cp39-win_amd64.whl", hash = "sha256:18788befa5284bb1f49149bac7f813703de60ef5b6bf7698a9f1c3a5a49b78e4"}, - {file = "scs-3.2.4.post1.tar.gz", hash = "sha256:7015d7a56d1d5b53264fd277289ea169949309e26101677ff88cd0e5030d032f"}, -] - -[package.dependencies] -numpy = "*" -scipy = "*" - -[[package]] -name = "seaborn" -version = "0.11.2" -description = "seaborn: statistical data visualization" -optional = false -python-versions = ">=3.6" -files = [ - {file = "seaborn-0.11.2-py3-none-any.whl", hash = "sha256:85a6baa9b55f81a0623abddc4a26b334653ff4c6b18c418361de19dbba0ef283"}, - {file = "seaborn-0.11.2.tar.gz", hash = "sha256:cf45e9286d40826864be0e3c066f98536982baf701a7caa386511792d61ff4f6"}, -] - -[package.dependencies] -matplotlib = ">=2.2" -numpy = ">=1.15" -pandas = ">=0.23" -scipy = ">=1.0" - -[[package]] -name = "semantic-version" -version = "2.10.0" -description = "A library implementing the 'SemVer' scheme." -optional = true -python-versions = ">=2.7" -files = [ - {file = "semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177"}, - {file = "semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c"}, -] - -[package.extras] -dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"] -doc = ["Sphinx", "sphinx-rtd-theme"] - -[[package]] -name = "send2trash" -version = "1.8.3" -description = "Send file to trash natively under Mac OS X, Windows and Linux" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9"}, - {file = "Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf"}, -] - -[package.extras] -nativelib = ["pyobjc-framework-Cocoa", "pywin32"] -objc = ["pyobjc-framework-Cocoa"] -win32 = ["pywin32"] - -[[package]] -name = "setproctitle" -version = "1.3.3" -description = "A Python module to customize the process title" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:897a73208da48db41e687225f355ce993167079eda1260ba5e13c4e53be7f754"}, - {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c331e91a14ba4076f88c29c777ad6b58639530ed5b24b5564b5ed2fd7a95452"}, - {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbbd6c7de0771c84b4aa30e70b409565eb1fc13627a723ca6be774ed6b9d9fa3"}, - {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c05ac48ef16ee013b8a326c63e4610e2430dbec037ec5c5b58fcced550382b74"}, - {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1342f4fdb37f89d3e3c1c0a59d6ddbedbde838fff5c51178a7982993d238fe4f"}, - {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc74e84fdfa96821580fb5e9c0b0777c1c4779434ce16d3d62a9c4d8c710df39"}, - {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9617b676b95adb412bb69645d5b077d664b6882bb0d37bfdafbbb1b999568d85"}, - {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6a249415f5bb88b5e9e8c4db47f609e0bf0e20a75e8d744ea787f3092ba1f2d0"}, - {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:38da436a0aaace9add67b999eb6abe4b84397edf4a78ec28f264e5b4c9d53cd5"}, - {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:da0d57edd4c95bf221b2ebbaa061e65b1788f1544977288bdf95831b6e44e44d"}, - {file = "setproctitle-1.3.3-cp310-cp310-win32.whl", hash = "sha256:a1fcac43918b836ace25f69b1dca8c9395253ad8152b625064415b1d2f9be4fb"}, - {file = "setproctitle-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:200620c3b15388d7f3f97e0ae26599c0c378fdf07ae9ac5a13616e933cbd2086"}, - {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334f7ed39895d692f753a443102dd5fed180c571eb6a48b2a5b7f5b3564908c8"}, - {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:950f6476d56ff7817a8fed4ab207727fc5260af83481b2a4b125f32844df513a"}, - {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:195c961f54a09eb2acabbfc90c413955cf16c6e2f8caa2adbf2237d1019c7dd8"}, - {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f05e66746bf9fe6a3397ec246fe481096664a9c97eb3fea6004735a4daf867fd"}, - {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5901a31012a40ec913265b64e48c2a4059278d9f4e6be628441482dd13fb8b5"}, - {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64286f8a995f2cd934082b398fc63fca7d5ffe31f0e27e75b3ca6b4efda4e353"}, - {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:184239903bbc6b813b1a8fc86394dc6ca7d20e2ebe6f69f716bec301e4b0199d"}, - {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:664698ae0013f986118064b6676d7dcd28fefd0d7d5a5ae9497cbc10cba48fa5"}, - {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e5119a211c2e98ff18b9908ba62a3bd0e3fabb02a29277a7232a6fb4b2560aa0"}, - {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:417de6b2e214e837827067048f61841f5d7fc27926f2e43954567094051aff18"}, - {file = "setproctitle-1.3.3-cp311-cp311-win32.whl", hash = "sha256:6a143b31d758296dc2f440175f6c8e0b5301ced3b0f477b84ca43cdcf7f2f476"}, - {file = "setproctitle-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a680d62c399fa4b44899094027ec9a1bdaf6f31c650e44183b50d4c4d0ccc085"}, - {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d4460795a8a7a391e3567b902ec5bdf6c60a47d791c3b1d27080fc203d11c9dc"}, - {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bdfd7254745bb737ca1384dee57e6523651892f0ea2a7344490e9caefcc35e64"}, - {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477d3da48e216d7fc04bddab67b0dcde633e19f484a146fd2a34bb0e9dbb4a1e"}, - {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab2900d111e93aff5df9fddc64cf51ca4ef2c9f98702ce26524f1acc5a786ae7"}, - {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088b9efc62d5aa5d6edf6cba1cf0c81f4488b5ce1c0342a8b67ae39d64001120"}, - {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6d50252377db62d6a0bb82cc898089916457f2db2041e1d03ce7fadd4a07381"}, - {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:87e668f9561fd3a457ba189edfc9e37709261287b52293c115ae3487a24b92f6"}, - {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:287490eb90e7a0ddd22e74c89a92cc922389daa95babc833c08cf80c84c4df0a"}, - {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe1c49486109f72d502f8be569972e27f385fe632bd8895f4730df3c87d5ac8"}, - {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4a6ba2494a6449b1f477bd3e67935c2b7b0274f2f6dcd0f7c6aceae10c6c6ba3"}, - {file = "setproctitle-1.3.3-cp312-cp312-win32.whl", hash = "sha256:2df2b67e4b1d7498632e18c56722851ba4db5d6a0c91aaf0fd395111e51cdcf4"}, - {file = "setproctitle-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f38d48abc121263f3b62943f84cbaede05749047e428409c2c199664feb6abc7"}, - {file = "setproctitle-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:816330675e3504ae4d9a2185c46b573105d2310c20b19ea2b4596a9460a4f674"}, - {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f960bc22d8d8e4ac886d1e2e21ccbd283adcf3c43136161c1ba0fa509088e0"}, - {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e6e7adff74796ef12753ff399491b8827f84f6c77659d71bd0b35870a17d8f"}, - {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53bc0d2358507596c22b02db079618451f3bd720755d88e3cccd840bafb4c41c"}, - {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6d20f9541f5f6ac63df553b6d7a04f313947f550eab6a61aa758b45f0d5657"}, - {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c1c84beab776b0becaa368254801e57692ed749d935469ac10e2b9b825dbdd8e"}, - {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:507e8dc2891021350eaea40a44ddd887c9f006e6b599af8d64a505c0f718f170"}, - {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b1067647ac7aba0b44b591936118a22847bda3c507b0a42d74272256a7a798e9"}, - {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e71f6365744bf53714e8bd2522b3c9c1d83f52ffa6324bd7cbb4da707312cd8"}, - {file = "setproctitle-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:7f1d36a1e15a46e8ede4e953abb104fdbc0845a266ec0e99cc0492a4364f8c44"}, - {file = "setproctitle-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9a402881ec269d0cc9c354b149fc29f9ec1a1939a777f1c858cdb09c7a261df"}, - {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ff814dea1e5c492a4980e3e7d094286077054e7ea116cbeda138819db194b2cd"}, - {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:accb66d7b3ccb00d5cd11d8c6e07055a4568a24c95cf86109894dcc0c134cc89"}, - {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554eae5a5b28f02705b83a230e9d163d645c9a08914c0ad921df363a07cf39b1"}, - {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a911b26264dbe9e8066c7531c0591cfab27b464459c74385b276fe487ca91c12"}, - {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2982efe7640c4835f7355fdb4da313ad37fb3b40f5c69069912f8048f77b28c8"}, - {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3f4274b80709d8bcab2f9a862973d453b308b97a0b423a501bcd93582852e3"}, - {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:af2c67ae4c795d1674a8d3ac1988676fa306bcfa1e23fddb5e0bd5f5635309ca"}, - {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af4061f67fd7ec01624c5e3c21f6b7af2ef0e6bab7fbb43f209e6506c9ce0092"}, - {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37a62cbe16d4c6294e84670b59cf7adcc73faafe6af07f8cb9adaf1f0e775b19"}, - {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a83ca086fbb017f0d87f240a8f9bbcf0809f3b754ee01cec928fff926542c450"}, - {file = "setproctitle-1.3.3-cp38-cp38-win32.whl", hash = "sha256:059f4ce86f8cc92e5860abfc43a1dceb21137b26a02373618d88f6b4b86ba9b2"}, - {file = "setproctitle-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ab92e51cd4a218208efee4c6d37db7368fdf182f6e7ff148fb295ecddf264287"}, - {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c7951820b77abe03d88b114b998867c0f99da03859e5ab2623d94690848d3e45"}, - {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc94cf128676e8fac6503b37763adb378e2b6be1249d207630f83fc325d9b11"}, - {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f5d9027eeda64d353cf21a3ceb74bb1760bd534526c9214e19f052424b37e42"}, - {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e4a8104db15d3462e29d9946f26bed817a5b1d7a47eabca2d9dc2b995991503"}, - {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c32c41ace41f344d317399efff4cffb133e709cec2ef09c99e7a13e9f3b9483c"}, - {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf16381c7bf7f963b58fb4daaa65684e10966ee14d26f5cc90f07049bfd8c1e"}, - {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e18b7bd0898398cc97ce2dfc83bb192a13a087ef6b2d5a8a36460311cb09e775"}, - {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69d565d20efe527bd8a9b92e7f299ae5e73b6c0470f3719bd66f3cd821e0d5bd"}, - {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ddedd300cd690a3b06e7eac90ed4452348b1348635777ce23d460d913b5b63c3"}, - {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:415bfcfd01d1fbf5cbd75004599ef167a533395955305f42220a585f64036081"}, - {file = "setproctitle-1.3.3-cp39-cp39-win32.whl", hash = "sha256:21112fcd2195d48f25760f0eafa7a76510871bbb3b750219310cf88b04456ae3"}, - {file = "setproctitle-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:5a740f05d0968a5a17da3d676ce6afefebeeeb5ce137510901bf6306ba8ee002"}, - {file = "setproctitle-1.3.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6b9e62ddb3db4b5205c0321dd69a406d8af9ee1693529d144e86bd43bcb4b6c0"}, - {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3b99b338598de0bd6b2643bf8c343cf5ff70db3627af3ca427a5e1a1a90dd9"}, - {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ae9a02766dad331deb06855fb7a6ca15daea333b3967e214de12cfae8f0ef5"}, - {file = "setproctitle-1.3.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:200ede6fd11233085ba9b764eb055a2a191fb4ffb950c68675ac53c874c22e20"}, - {file = "setproctitle-1.3.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d3a953c50776751e80fe755a380a64cb14d61e8762bd43041ab3f8cc436092f"}, - {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e08e232b78ba3ac6bc0d23ce9e2bee8fad2be391b7e2da834fc9a45129eb87"}, - {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1da82c3e11284da4fcbf54957dafbf0655d2389cd3d54e4eaba636faf6d117a"}, - {file = "setproctitle-1.3.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:aeaa71fb9568ebe9b911ddb490c644fbd2006e8c940f21cb9a1e9425bd709574"}, - {file = "setproctitle-1.3.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:59335d000c6250c35989394661eb6287187854e94ac79ea22315469ee4f4c244"}, - {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3ba57029c9c50ecaf0c92bb127224cc2ea9fda057b5d99d3f348c9ec2855ad3"}, - {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d876d355c53d975c2ef9c4f2487c8f83dad6aeaaee1b6571453cb0ee992f55f6"}, - {file = "setproctitle-1.3.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:224602f0939e6fb9d5dd881be1229d485f3257b540f8a900d4271a2c2aa4e5f4"}, - {file = "setproctitle-1.3.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d7f27e0268af2d7503386e0e6be87fb9b6657afd96f5726b733837121146750d"}, - {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5e7266498cd31a4572378c61920af9f6b4676a73c299fce8ba93afd694f8ae7"}, - {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33c5609ad51cd99d388e55651b19148ea99727516132fb44680e1f28dd0d1de9"}, - {file = "setproctitle-1.3.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:eae8988e78192fd1a3245a6f4f382390b61bce6cfcc93f3809726e4c885fa68d"}, - {file = "setproctitle-1.3.3.tar.gz", hash = "sha256:c913e151e7ea01567837ff037a23ca8740192880198b7fbb90b16d181607caae"}, -] - -[package.extras] -test = ["pytest"] - -[[package]] -name = "setuptools" -version = "65.4.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"}, - {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "setuptools-rust" -version = "1.9.0" -description = "Setuptools Rust extension plugin" -optional = true -python-versions = ">=3.8" -files = [ - {file = "setuptools-rust-1.9.0.tar.gz", hash = "sha256:704df0948f2e4cc60c2596ad6e840ea679f4f43e58ed4ad0c1857807240eab96"}, - {file = "setuptools_rust-1.9.0-py3-none-any.whl", hash = "sha256:409caf49dcf7ad9bd510b4bf4011fbad504e745fae98f57fe1c06f3a97719638"}, -] - -[package.dependencies] -semantic-version = ">=2.8.2,<3" -setuptools = ">=62.4" -tomli = {version = ">=1.2.1", markers = "python_version < \"3.11\""} - -[[package]] -name = "setuptools-scm" -version = "8.0.4" -description = "the blessed package to manage your versions by scm tags" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-scm-8.0.4.tar.gz", hash = "sha256:b5f43ff6800669595193fd09891564ee9d1d7dcb196cab4b2506d53a2e1c95c7"}, - {file = "setuptools_scm-8.0.4-py3-none-any.whl", hash = "sha256:b47844cd2a84b83b3187a5782c71128c28b4c94cad8bfb871da2784a5cb54c4f"}, -] - -[package.dependencies] -packaging = ">=20" -setuptools = "*" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} -typing-extensions = "*" - -[package.extras] -docs = ["entangled-cli[rich]", "mkdocs", "mkdocs-entangled-plugin", "mkdocs-material", "mkdocstrings[python]", "pygments"] -rich = ["rich"] -test = ["build", "pytest", "rich", "wheel"] - -[[package]] -name = "sgmllib3k" -version = "1.0.0" -description = "Py3k port of sgmllib." -optional = false -python-versions = "*" -files = [ - {file = "sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"}, -] - -[[package]] -name = "shap" -version = "0.44.1" -description = "A unified approach to explain the output of any machine learning model." -optional = true -python-versions = ">=3.8" -files = [ - {file = "shap-0.44.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:93a94961a355249855f13f1ed564466afa1c5fae84f868dd56e50e936f4f9b57"}, - {file = "shap-0.44.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2919f2b255e31363182afb1627b374eb6c4724c90b0318719cbe90a316682f5"}, - {file = "shap-0.44.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad3192026a0e4c3e399ef499da4f726ab01d62f388e03c804d8e10c4e61c8d1"}, - {file = "shap-0.44.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4ba4f3e84c3adf2c5a55e6d2becb7a4bb83e5784f7aa373316099a105ad593f"}, - {file = "shap-0.44.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1f19a3c531c6ceb0d7adea8262385580b52ef187981606342cd03288fc48cbfa"}, - {file = "shap-0.44.1-cp310-cp310-win_amd64.whl", hash = "sha256:d65a21a64f3c1e76e1fb5277a0cfbcce447c08a26eaef311b1cbae9a6efe0ea0"}, - {file = "shap-0.44.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:44ed7a80ae30ed3927f1bc7911f6f04bb32bb2d938a9e8a5794b2bb99e3b99cc"}, - {file = "shap-0.44.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e98bb13dc351317ad2420dfe2dbca5361237ab9bef511fbe28ce6c1dfb40ff8c"}, - {file = "shap-0.44.1-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fe63f1f4acf6bdfa3c85db85067d0fda2a29fe38be2d14a2811844f35290f43"}, - {file = "shap-0.44.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fa67a8810a042aa2366224afa612ded8a2a77b5a5a0d89ea60eba5eff3a0c1b"}, - {file = "shap-0.44.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dda26c39ac0ef17233deba09b0f313ceb4d5fa663dced2a1b77a494d48db96b8"}, - {file = "shap-0.44.1-cp311-cp311-win_amd64.whl", hash = "sha256:e1c3de8747fb451e59768dca11b054e395a2a1601c7482738897616a70679419"}, - {file = "shap-0.44.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c82efde41d2b2c65d707b678a0d057d77436faf72331a623545aa742ffe5b9f5"}, - {file = "shap-0.44.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:89f4492b406ec9908750560281c4616aff2a28c61f3ad2f6ac511270f44a0c5a"}, - {file = "shap-0.44.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:497bde2083b2a27e0a4b8ded09927c11c8803e80e76cb2e87e2136c49f1c93b2"}, - {file = "shap-0.44.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a466d8a9bb12e7e07f13e68ea2859e4deb1dc6d3953c3a88310bd77484943cd6"}, - {file = "shap-0.44.1-cp38-cp38-win_amd64.whl", hash = "sha256:6aede800f0ec9efa8acd35913d791304d46376d444e8c6e1be905606e626c5e2"}, - {file = "shap-0.44.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ab36e2aecd0c1ba3df58f2452aa4aa24832ec766cbb2a794e2411897eb728b30"}, - {file = "shap-0.44.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:668ebe97c60a12bdffe254dfb2c8598cf24416e202b4b662076846b12d10b4b7"}, - {file = "shap-0.44.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:947c7e25b96d37763948ef2edd1711c6b3098a161de8e97fa0d1fdad6e49848c"}, - {file = "shap-0.44.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62bb07b4748db004c78802494c29e5e70a2efff967a5c4d50209d2ee2ace16db"}, - {file = "shap-0.44.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:febd7f0ad6ac27f999ed857a897f0f9963d4a4d9eeb003ee2505cf6164590d9c"}, - {file = "shap-0.44.1-cp39-cp39-win_amd64.whl", hash = "sha256:3c873d26e4fdcdb9e6a83a1e05c1e9c8c8b48ac4365b0259d6e138afb3729c32"}, - {file = "shap-0.44.1.tar.gz", hash = "sha256:a21d5a622e12e7c3b4a58d6e93b70133b7e09b6342b19746071a0dc2d190b432"}, -] - -[package.dependencies] -cloudpickle = "*" -numba = "*" -numpy = "*" -packaging = ">20.9" -pandas = "*" -scikit-learn = "*" -scipy = "*" -slicer = "0.0.7" -tqdm = ">=4.27.0" - -[package.extras] -docs = ["ipython", "matplotlib", "myst-parser (==2.0.0)", "nbsphinx (==0.9.3)", "numpydoc", "requests", "sphinx (==7.2.6)", "sphinx-github-changelog (==1.2.1)", "sphinx-rtd-theme (==2.0.0)"] -others = ["lime"] -plots = ["ipython", "matplotlib"] -test = ["catboost", "gpboost", "lightgbm", "ngboost", "opencv-python", "protobuf (==3.20.3)", "pyod", "pyspark", "pytest", "pytest-cov", "pytest-mpl", "sentencepiece", "tensorflow", "torch", "torchvision", "transformers", "xgboost"] -test-core = ["pytest", "pytest-cov", "pytest-mpl"] -test-notebooks = ["datasets", "jupyter", "keras", "nbconvert", "nbformat", "nlp", "transformers"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "slicer" -version = "0.0.7" -description = "A small package for big slicing." -optional = true -python-versions = ">=3.6" -files = [ - {file = "slicer-0.0.7-py3-none-any.whl", hash = "sha256:0b94faa5251c0f23782c03f7b7eedda91d80144059645f452c4bc80fab875976"}, - {file = "slicer-0.0.7.tar.gz", hash = "sha256:f5d5f7b45f98d155b9c0ba6554fa9770c6b26d5793a3e77a1030fb56910ebeec"}, -] - -[[package]] -name = "smmap" -version = "5.0.1" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -optional = false -python-versions = "*" -files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] - -[[package]] -name = "soupsieve" -version = "2.5" -description = "A modern CSS selector implementation for Beautiful Soup." -optional = false -python-versions = ">=3.8" -files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, -] - -[[package]] -name = "sparqlwrapper" -version = "2.0.0" -description = "SPARQL Endpoint interface to Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "SPARQLWrapper-2.0.0-py3-none-any.whl", hash = "sha256:c99a7204fff676ee28e6acef327dc1ff8451c6f7217dcd8d49e8872f324a8a20"}, - {file = "SPARQLWrapper-2.0.0.tar.gz", hash = "sha256:3fed3ebcc77617a4a74d2644b86fd88e0f32e7f7003ac7b2b334c026201731f1"}, -] - -[package.dependencies] -rdflib = ">=6.1.1" - -[package.extras] -dev = ["mypy (>=0.931)", "pandas (>=1.3.5)", "pandas-stubs (>=1.2.0.48)", "setuptools (>=3.7.1)"] -docs = ["sphinx (<5)", "sphinx-rtd-theme"] -keepalive = ["keepalive (>=0.5)"] -pandas = ["pandas (>=1.3.5)"] - -[[package]] -name = "sphinx" -version = "4.5.0" -description = "Python documentation generator" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"}, - {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"}, -] - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=1.3" -colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.18" -imagesize = "*" -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} -Jinja2 = ">=2.3" -packaging = "*" -Pygments = ">=2.0" -requests = ">=2.5.0" -snowballstemmer = ">=1.1" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.4" -description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, - {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.1" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, - {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["html5lib", "pytest"] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] - -[package.extras] -test = ["flake8", "mypy", "pytest"] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -optional = false -python-versions = ">=3.5" -files = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sqlglot" -version = "23.10.0" -description = "An easily customizable SQL parser and transpiler" -optional = true -python-versions = ">=3.7" -files = [ - {file = "sqlglot-23.10.0-py3-none-any.whl", hash = "sha256:519ea26007688bbc831939c0c6125762ad5c71339df1ac8184a64d762f75684c"}, - {file = "sqlglot-23.10.0.tar.gz", hash = "sha256:b80a35de7716464de89cc77678d0a7f972e4a8acaaa7cefcdc6db05ffeb6c951"}, -] - -[package.extras] -dev = ["duckdb (>=0.6)", "maturin (>=1.4,<2.0)", "mypy", "pandas", "pandas-stubs", "pdoc", "pre-commit", "pyspark", "python-dateutil", "ruff (==0.3.2)", "types-python-dateutil", "typing-extensions"] -rs = ["sqlglotrs (==0.2.0)"] - -[[package]] -name = "squarify" -version = "0.4.3" -description = "Pure Python implementation of the squarify treemap layout algorithm" -optional = false -python-versions = "*" -files = [ - {file = "squarify-0.4.3-py3-none-any.whl", hash = "sha256:bec7011e0c7f4103fe57a1c16a7c091d9dc1be0f23d774e1c568b748a6f818f6"}, - {file = "squarify-0.4.3.tar.gz", hash = "sha256:54091f6ad175f7f201f8934574e647ce1b50dedc478c5fd968688eb7d7469f95"}, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -description = "Extract data from python stack frames and tracebacks for informative displays" -optional = false -python-versions = "*" -files = [ - {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, - {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, -] - -[package.dependencies] -asttokens = ">=2.1.0" -executing = ">=1.2.0" -pure-eval = "*" - -[package.extras] -tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] - -[[package]] -name = "stanio" -version = "0.5.0" -description = "Utilities for preparing Stan inputs and processing Stan outputs" -optional = true -python-versions = ">=3.8" -files = [ - {file = "stanio-0.5.0-py3-none-any.whl", hash = "sha256:412f8ede127968be51e074be6a6ca1e66d5a8ed50805e2fed89b76868e7332ae"}, - {file = "stanio-0.5.0.tar.gz", hash = "sha256:dee429314d965f390181afa8ff7ff8144446eeb58cc2547ccc108ba73e6744e2"}, -] - -[package.dependencies] -numpy = "*" - -[package.extras] -test = ["pandas", "pytest", "pytest-cov"] -ujson = ["ujson (>=5.5.0)"] - -[[package]] -name = "statsforecast" -version = "1.7.4" -description = "Time series forecasting suite using statistical models" -optional = true -python-versions = ">=3.8" -files = [ - {file = "statsforecast-1.7.4-py3-none-any.whl", hash = "sha256:0863d371e87fdfaf0e157bfee5d242e9899365a50c558a30331ec0d965c0cc64"}, - {file = "statsforecast-1.7.4.tar.gz", hash = "sha256:eaea757f0a8835bea5a710df02c3248c0bc0eff940ae8d83a46fc339abb52280"}, -] - -[package.dependencies] -cloudpickle = "*" -coreforecast = ">=0.0.7" -fugue = ">=0.8.1" -numba = ">=0.55.0" -numpy = ">=1.21.6" -pandas = ">=1.3.5" -scipy = ">=1.7.3" -statsmodels = ">=0.13.2" -threadpoolctl = "*" -tqdm = "*" -utilsforecast = ">=0.0.24" - -[package.extras] -dask = ["fugue[dask] (>=0.8.1)"] -dev = ["black", "datasetsforecast", "flake8", "fugue[dask] (>=0.8.1)", "fugue[ray] (>=0.8.1)", "fugue[spark] (>=0.8.1)", "mypy", "nbdev", "nbdev-plotly", "pandas[plot]", "plotly", "plotly-resampler", "pmdarima", "polars", "prophet", "protobuf (>=3.15.3,<4.0.0)", "pyarrow", "ray (<2.8)", "scikit-learn", "supersmoother"] -plotly = ["plotly", "plotly-resampler"] -polars = ["polars"] -ray = ["fugue[ray] (>=0.8.1)", "protobuf (>=3.15.3,<4.0.0)", "ray (<2.8)"] -spark = ["fugue[spark] (>=0.8.1)"] - -[[package]] -name = "statsmodels" -version = "0.13.2" -description = "Statistical computations and models for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "statsmodels-0.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3e7ca5b7e678c0bb7a24f5c735d58ac104a50eb61b17c484cce0e221a095560f"}, - {file = "statsmodels-0.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:066a75d5585378b2df972f81a90b9a3da5e567b7d4833300c1597438c1a35e29"}, - {file = "statsmodels-0.13.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f15f38dfc9c5c091662cb619e12322047368c67aef449c7554d9b324a15f7a94"}, - {file = "statsmodels-0.13.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c4ccc6b4744613367e8a233bd952c8a838db8f528f9fe033bda25aa13fc7d08"}, - {file = "statsmodels-0.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:855b1cc2a91ab140b9bcf304b1731705805ce73223bf500b988804968554c0ed"}, - {file = "statsmodels-0.13.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69c9af7606325095f7c40c581957bad9f28775653d41537c1ec4cd1b185ff5b"}, - {file = "statsmodels-0.13.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab31bac0f72b83bca1f217a12ec6f309a56485a50c4a705fbdd63112213d4da4"}, - {file = "statsmodels-0.13.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d680b910b57fc0aa87472662cdfe09aae0e21db4bdf19ccd6420fd4dffda892"}, - {file = "statsmodels-0.13.2-cp37-cp37m-win32.whl", hash = "sha256:9e9a3f661d372431850d55157d049e079493c97fc06f550d23d8c8c70805cc48"}, - {file = "statsmodels-0.13.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c9f6326870c095ef688f072cd476b932aff0906d60193eaa08e93ec23b29ca83"}, - {file = "statsmodels-0.13.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bc050f25f1ba1221efef9ea01b751c60935ad787fcd4259f4ece986f2da9141"}, - {file = "statsmodels-0.13.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:426b1c8ea3918d3d27dbfa38f2bee36cabf41d32163e2cbb3adfb0178b24626a"}, - {file = "statsmodels-0.13.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45b80fac4a63308b1e93fa9dc27a8598930fd5dfd77c850ca077bb850254c6d7"}, - {file = "statsmodels-0.13.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78ee69ec0e0f79f627245c65f8a495b8581c2ea19084aac63941815feb15dcf3"}, - {file = "statsmodels-0.13.2-cp38-cp38-win32.whl", hash = "sha256:20483cc30e11aa072b30d307bb80470f86a23ae8fffa51439ca54509d7aa9b05"}, - {file = "statsmodels-0.13.2-cp38-cp38-win_amd64.whl", hash = "sha256:bf43051a92231ccb9de95e4b6d22d3b15e499ee5ee9bff0a20e6b6ad293e34cb"}, - {file = "statsmodels-0.13.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6bf0dfed5f5edb59b5922b295392cd276463b10a5e730f7e57ee4ff2d8e9a87e"}, - {file = "statsmodels-0.13.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a403b559c5586dab7ac0fc9e754c737b017c96cce0ddd66ff9094764cdaf293d"}, - {file = "statsmodels-0.13.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f23554dd025ea354ce072ba32bfaa840d2b856372e5734290e181d27a1f9e0c"}, - {file = "statsmodels-0.13.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815f4df713e3eb6f40ae175c71f2a70d32f9219b5b4d23d4e0faab1171ba93ba"}, - {file = "statsmodels-0.13.2-cp39-cp39-win32.whl", hash = "sha256:461c82ab2265fa8457b96afc23ef3ca19f42eb070436e0241b57e58a38863901"}, - {file = "statsmodels-0.13.2-cp39-cp39-win_amd64.whl", hash = "sha256:39daab5a8a9332c8ea83d6464d065080c9ba65f236daf6a64aa18f64ef776fad"}, - {file = "statsmodels-0.13.2.tar.gz", hash = "sha256:77dc292c9939c036a476f1770f9d08976b05437daa229928da73231147cde7d4"}, -] - -[package.dependencies] -numpy = ">=1.17" -packaging = ">=21.3" -pandas = ">=0.25" -patsy = ">=0.5.2" -scipy = ">=1.3" - -[package.extras] -build = ["cython (>=0.29.26)"] -develop = ["cython (>=0.29.26)"] -docs = ["ipykernel", "jupyter-client", "matplotlib", "nbconvert", "nbformat", "numpydoc", "pandas-datareader", "sphinx"] - -[[package]] -name = "statsmodels" -version = "0.13.5" -description = "Statistical computations and models for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "statsmodels-0.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75319fddded9507cc310fc3980e4ae4d64e3ff37b322ad5e203a84f89d85203"}, - {file = "statsmodels-0.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f148920ef27c7ba69a5735724f65de9422c0c8bcef71b50c846b823ceab8840"}, - {file = "statsmodels-0.13.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cc4d3e866bfe0c4f804bca362d0e7e29d24b840aaba8d35a754387e16d2a119"}, - {file = "statsmodels-0.13.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072950d6f7820a6b0bd6a27b2d792a6d6f952a1d2f62f0dcf8dd808799475855"}, - {file = "statsmodels-0.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:159ae9962c61b31dcffe6356d72ae3d074bc597ad9273ec93ae653fe607b8516"}, - {file = "statsmodels-0.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9061c0d5ee4f3038b590afedd527a925e5de27195dc342381bac7675b2c5efe4"}, - {file = "statsmodels-0.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e1d89cba5fafc1bf8e75296fdfad0b619de2bfb5e6c132913991d207f3ead675"}, - {file = "statsmodels-0.13.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01bc16e7c66acb30cd3dda6004c43212c758223d1966131226024a5c99ec5a7e"}, - {file = "statsmodels-0.13.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d5cd9ab5de2c7489b890213cba2aec3d6468eaaec547041c2dfcb1e03411f7e"}, - {file = "statsmodels-0.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:857d5c0564a68a7ef77dc2252bb43c994c0699919b4e1f06a9852c2fbb588765"}, - {file = "statsmodels-0.13.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a5348b2757ab31c5c31b498f25eff2ea3c42086bef3d3b88847c25a30bdab9c"}, - {file = "statsmodels-0.13.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b21648e3a8e7514839ba000a48e495cdd8bb55f1b71c608cf314b05541e283b"}, - {file = "statsmodels-0.13.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b829eada6cec07990f5e6820a152af4871c601fd458f76a896fb79ae2114985"}, - {file = "statsmodels-0.13.5-cp37-cp37m-win_amd64.whl", hash = "sha256:872b3a8186ef20f647c7ab5ace512a8fc050148f3c2f366460ab359eec3d9695"}, - {file = "statsmodels-0.13.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc1abb81d24f56425febd5a22bb852a1b98e53b80c4a67f50938f9512f154141"}, - {file = "statsmodels-0.13.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2c46f1b0811a9736db37badeb102c0903f33bec80145ced3aa54df61aee5c2b"}, - {file = "statsmodels-0.13.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:947f79ba9662359f1cfa6e943851f17f72b06e55f4a7c7a2928ed3bc57ed6cb8"}, - {file = "statsmodels-0.13.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:046251c939c51e7632bcc8c6d6f31b8ca0eaffdf726d2498463f8de3735c9a82"}, - {file = "statsmodels-0.13.5-cp38-cp38-win_amd64.whl", hash = "sha256:84f720e8d611ef8f297e6d2ffa7248764e223ef7221a3fc136e47ae089609611"}, - {file = "statsmodels-0.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b0d1d24e4adf96ec3c64d9a027dcee2c5d5096bb0dad33b4d91034c0a3c40371"}, - {file = "statsmodels-0.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f0e5c9c58fb6cba41db01504ec8dd018c96a95152266b7d5d67e0de98840474"}, - {file = "statsmodels-0.13.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b034aa4b9ad4f4d21abc4dd4841be0809a446db14c7aa5c8a65090aea9f1143"}, - {file = "statsmodels-0.13.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73f97565c29241e839ffcef74fa995afdfe781910ccc27c189e5890193085958"}, - {file = "statsmodels-0.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:2ff331e508f2d1a53d3a188305477f4cf05cd8c52beb6483885eb3d51c8be3ad"}, - {file = "statsmodels-0.13.5.tar.gz", hash = "sha256:593526acae1c0fda0ea6c48439f67c3943094c542fe769f8b90fe9e6c6cc4871"}, -] - -[package.dependencies] -numpy = [ - {version = ">=1.17", markers = "python_version != \"3.10\" or platform_system != \"Windows\" or platform_python_implementation == \"PyPy\""}, - {version = ">=1.22.3", markers = "python_version == \"3.10\" and platform_system == \"Windows\" and platform_python_implementation != \"PyPy\""}, -] -packaging = ">=21.3" -pandas = ">=0.25" -patsy = ">=0.5.2" -scipy = {version = ">=1.3", markers = "python_version > \"3.9\" and python_version < \"3.12\" or platform_system != \"Windows\" and python_version < \"3.12\" or platform_machine != \"x86\" and python_version < \"3.12\""} - -[package.extras] -build = ["cython (>=0.29.32)"] -develop = ["Jinja2", "colorama", "cython (>=0.29.32)", "cython (>=0.29.32,<3.0.0)", "flake8", "isort", "joblib", "matplotlib (>=3)", "oldest-supported-numpy (>=2022.4.18)", "pytest (>=7.0.1,<7.1.0)", "pytest-randomly", "pytest-xdist", "pywinpty", "setuptools-scm[toml] (>=7.0.0,<7.1.0)"] -docs = ["ipykernel", "jupyter-client", "matplotlib", "nbconvert", "nbformat", "numpydoc", "pandas-datareader", "sphinx"] - -[[package]] -name = "stevedore" -version = "5.2.0" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.8" -files = [ - {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, - {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, -] - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - -[[package]] -name = "streamlit" -version = "1.33.0" -description = "A faster way to build and share data apps" -optional = false -python-versions = "!=3.9.7,>=3.8" -files = [ - {file = "streamlit-1.33.0-py2.py3-none-any.whl", hash = "sha256:bfacb5d1edefcf803c2040b051a21b4c81317a9865448e6767d0a0c6aae7edae"}, - {file = "streamlit-1.33.0.tar.gz", hash = "sha256:a8da8ff46f5b948c56d2dc7aca7a61cf8d995f4f21744cf82258ae75e63004ba"}, -] - -[package.dependencies] -altair = ">=4.0,<6" -blinker = ">=1.0.0,<2" -cachetools = ">=4.0,<6" -click = ">=7.0,<9" -gitpython = ">=3.0.7,<3.1.19 || >3.1.19,<4" -numpy = ">=1.19.3,<2" -packaging = ">=16.8,<25" -pandas = ">=1.3.0,<3" -pillow = ">=7.1.0,<11" -protobuf = ">=3.20,<5" -pyarrow = ">=7.0" -pydeck = ">=0.8.0b4,<1" -requests = ">=2.27,<3" -rich = ">=10.14.0,<14" -tenacity = ">=8.1.0,<9" -toml = ">=0.10.1,<2" -tornado = ">=6.0.3,<7" -typing-extensions = ">=4.3.0,<5" -watchdog = {version = ">=2.1.5", markers = "platform_system != \"Darwin\""} - -[package.extras] -snowflake = ["snowflake-connector-python (>=2.8.0)", "snowflake-snowpark-python (>=0.9.0)"] - -[[package]] -name = "svglib" -version = "1.5.1" -description = "A pure-Python library for reading and converting SVG" -optional = false -python-versions = ">=3.7" -files = [ - {file = "svglib-1.5.1.tar.gz", hash = "sha256:3ae765d3a9409ee60c0fb4d24c2deb6a80617aa927054f5bcd7fc98f0695e587"}, -] - -[package.dependencies] -cssselect2 = ">=0.2.0" -lxml = "*" -reportlab = "*" -tinycss2 = ">=0.6.0" - -[[package]] -name = "tabulate" -version = "0.9.0" -description = "Pretty-print tabular data" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, - {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, -] - -[package.extras] -widechars = ["wcwidth"] - -[[package]] -name = "tbats" -version = "1.1.3" -description = "BATS and TBATS for time series forecasting" -optional = true -python-versions = "*" -files = [ - {file = "tbats-1.1.3-py3-none-any.whl", hash = "sha256:aff37f39115583028dacbd87227bc2ac65e696ea25543ddcd78e334e1fbe8e3a"}, - {file = "tbats-1.1.3.tar.gz", hash = "sha256:dccd0d4cd9e2fde30bf19a944ee957d2f865975846284f48ce673992c4f5d89e"}, -] - -[package.dependencies] -numpy = "*" -pmdarima = "*" -scikit-learn = "*" -scipy = "*" - -[package.extras] -dev = ["pip-tools", "pytest", "rpy2"] - -[[package]] -name = "tenacity" -version = "8.2.3" -description = "Retry code until it succeeds" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, - {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, -] - -[package.extras] -doc = ["reno", "sphinx", "tornado (>=4.5)"] - -[[package]] -name = "tensorboard" -version = "2.14.0" -description = "TensorBoard lets you watch Tensors Flow" -optional = true -python-versions = ">=3.8" -files = [ - {file = "tensorboard-2.14.0-py3-none-any.whl", hash = "sha256:3667f9745d99280836ad673022362c840f60ed8fefd5a3e30bf071f5a8fd0017"}, -] - -[package.dependencies] -absl-py = ">=0.4" -google-auth = ">=1.6.3,<3" -google-auth-oauthlib = ">=0.5,<1.1" -grpcio = ">=1.48.2" -markdown = ">=2.6.8" -numpy = ">=1.12.0" -protobuf = ">=3.19.6" -requests = ">=2.21.0,<3" -setuptools = ">=41.0.0" -tensorboard-data-server = ">=0.7.0,<0.8.0" -werkzeug = ">=1.0.1" -wheel = ">=0.26" - -[[package]] -name = "tensorboard-data-server" -version = "0.7.2" -description = "Fast data loading for TensorBoard" -optional = true -python-versions = ">=3.7" -files = [ - {file = "tensorboard_data_server-0.7.2-py3-none-any.whl", hash = "sha256:7e0610d205889588983836ec05dc098e80f97b7e7bbff7e994ebb78f578d0ddb"}, - {file = "tensorboard_data_server-0.7.2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:9fe5d24221b29625dbc7328b0436ca7fc1c23de4acf4d272f1180856e32f9f60"}, - {file = "tensorboard_data_server-0.7.2-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:ef687163c24185ae9754ed5650eb5bc4d84ff257aabdc33f0cc6f74d8ba54530"}, -] - -[[package]] -name = "terminado" -version = "0.18.1" -description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." -optional = false -python-versions = ">=3.8" -files = [ - {file = "terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0"}, - {file = "terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e"}, -] - -[package.dependencies] -ptyprocess = {version = "*", markers = "os_name != \"nt\""} -pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""} -tornado = ">=6.1.0" - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] -typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] - -[[package]] -name = "textwrap3" -version = "0.9.2" -description = "textwrap from Python 3.6 backport (plus a few tweaks)" -optional = false -python-versions = "*" -files = [ - {file = "textwrap3-0.9.2-py2.py3-none-any.whl", hash = "sha256:bf5f4c40faf2a9ff00a9e0791fed5da7415481054cef45bb4a3cfb1f69044ae0"}, - {file = "textwrap3-0.9.2.zip", hash = "sha256:5008eeebdb236f6303dcd68f18b856d355f6197511d952ba74bc75e40e0c3414"}, -] - -[[package]] -name = "threadpoolctl" -version = "3.4.0" -description = "threadpoolctl" -optional = false -python-versions = ">=3.8" -files = [ - {file = "threadpoolctl-3.4.0-py3-none-any.whl", hash = "sha256:8f4c689a65b23e5ed825c8436a92b818aac005e0f3715f6a1664d7c7ee29d262"}, - {file = "threadpoolctl-3.4.0.tar.gz", hash = "sha256:f11b491a03661d6dd7ef692dd422ab34185d982466c49c8f98c8f716b5c93196"}, -] - -[[package]] -name = "time-machine" -version = "2.14.1" -description = "Travel through time in your tests." -optional = false -python-versions = ">=3.8" -files = [ - {file = "time-machine-2.14.1.tar.gz", hash = "sha256:57dc7efc1dde4331902d1bdefd34e8ee890a5c28533157e3b14a429c86b39533"}, - {file = "time_machine-2.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:528d588d1e8ba83e45319a74acab4be0569eb141113fdf50368045d0a7d79cee"}, - {file = "time_machine-2.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06e913d570d7ee3e199e3316f10f10c8046287049141b0a101197712b4eac106"}, - {file = "time_machine-2.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddbbba954e9a409e7d66d60df2b6b8daeb897f8338f909a92d9d20e431ec70d1"}, - {file = "time_machine-2.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72a153b085b4aee652d6b3bf9019ca897f1597ba9869b640b06f28736b267182"}, - {file = "time_machine-2.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b94274abe24b6a90d8a5c042167a9a7af2d3438b42ac8eb5ede50fbc73c08db"}, - {file = "time_machine-2.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:364353858708628655bf9fa4c2825febd679c729d9e1dd424ff86845828bac05"}, - {file = "time_machine-2.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b951b6f4b8a752ab8c441df422e21954a721a0a5276aa3814ce8cf7205aeb6da"}, - {file = "time_machine-2.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:be215eb63d74a3d580f7924bb4209c783fabcfb3253073f4dcb3424d57d0f518"}, - {file = "time_machine-2.14.1-cp310-cp310-win32.whl", hash = "sha256:0e120f95c17bf8e0c097fd8863a8eb24054f9b17d9b17c465694be50f8348a3a"}, - {file = "time_machine-2.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:fb467d6c9e9ab615c8cf22d751d34296dacf801be323a57adeb4ff345cf72473"}, - {file = "time_machine-2.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:19db257117739b2dda1d57e149bb715a593313899b3902a7e6d752c5f1d22542"}, - {file = "time_machine-2.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:442d42f1b0ef006f03a5a34905829a1d3ac569a5bcda64d29706e6dc60832f94"}, - {file = "time_machine-2.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0312b47f220e46f1bbfaded7fc1469882d9c2a27c6daf44e119aea7006b595cc"}, - {file = "time_machine-2.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a39dba3033d9c28347d2db16bcb16041bbf4e9032e2b70023686b6f95deac9d"}, - {file = "time_machine-2.14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e030d2051bb515251d7f6edd9bbcf79b2b47811e2c402aba9c126af713843d26"}, - {file = "time_machine-2.14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:993ab140eb5678d1ee7f1197f08e4499dc8ea883ad6b8858737de70d509ec5b5"}, - {file = "time_machine-2.14.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:90725f936ad8b123149bc82a46394dd7057e63157ee11ba878164053fa5bd8ad"}, - {file = "time_machine-2.14.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:59a02c3d3b3b29e2dc3a708e775c5d6b951b0024c4013fed883f0d2205305c9e"}, - {file = "time_machine-2.14.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f00f67d532da82538c4dfbbddc587e70c82664f168c11e1c2915d0c85ec2fc8"}, - {file = "time_machine-2.14.1-cp311-cp311-win32.whl", hash = "sha256:27f735cba4c6352ad7bc53ce2d86b715379261a634e690b79fac329081e26fb6"}, - {file = "time_machine-2.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee68597bd3fa5ab94633c8a9d3ebd4032091559610e078381818a732910002bc"}, - {file = "time_machine-2.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:6ced9de5eff1fb37efb12984ab7b63f31f0aeadeedec4be6d0404ec4fa91f2e7"}, - {file = "time_machine-2.14.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:30a4a18357fa6cf089eeefcb37e9549b42523aebb5933894770a8919e6c398e1"}, - {file = "time_machine-2.14.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d45bd60bea85869615b117667f10a821e3b0d3603c47bfd105b45d1f67156fc8"}, - {file = "time_machine-2.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:39de6d37a14ff8882d4f1cbd50c53268b54e1cf4ef9be2bfe590d10a51ccd314"}, - {file = "time_machine-2.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fd7d188b4f9d358c6bd477daf93b460d9b244a4c296ddd065945f2b6193c2bd"}, - {file = "time_machine-2.14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99e6f013e67c4f74a9d8f57e34173b2047f2ad48f764e44c38f3ee5344a38c01"}, - {file = "time_machine-2.14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a927d87501da8b053a27e80f5d0e1e58fbde4b50d70df2d3853ed67e89a731cf"}, - {file = "time_machine-2.14.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77a616561dd4c7c442e9eee8cbb915750496e9a5a7fca6bcb11a9860226d2d0"}, - {file = "time_machine-2.14.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e7fa70a6bdca40cc4a8386fd85bc1bae0a23ab11e49604ef853ab3ce92be127f"}, - {file = "time_machine-2.14.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d63ef00d389fa6d2c76c863af580b3e4a8f0ccc6a9aea8e64590588e37f13c00"}, - {file = "time_machine-2.14.1-cp312-cp312-win32.whl", hash = "sha256:6706eb06487354a5e219cacea709fb3ec44dec3842c6218237d5069fa5f1ad64"}, - {file = "time_machine-2.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:36aa4f17adcd73a6064bf4991a29126cac93521f0690805edb91db837c4e1453"}, - {file = "time_machine-2.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:edea570f3835a036e8860bb8d6eb8d08473c59313db86e36e3b207f796fd7b14"}, - {file = "time_machine-2.14.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87e80408e6b6670e9ce33f94b1cc6b72b1a9b646f5e19f586908129871f74b40"}, - {file = "time_machine-2.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c69c0cb498c86ef843cd15964714e76465cc25d64464da57d5d1318f499de099"}, - {file = "time_machine-2.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc48d3934109b0bdbbdc5e9ce577213f7148a92fed378420ee13453503fe4db9"}, - {file = "time_machine-2.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7161cea2ff3244cc6075e365fab89000df70ead63a3da9d473983d580558d2de"}, - {file = "time_machine-2.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39fceeb131e6c07b386de042ce1016be771576e9516124b78e75cbab94ae5041"}, - {file = "time_machine-2.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fe508a6c43fb72fa4f66b50b14684cf58d3db95fed617177ec197a7a90427bae"}, - {file = "time_machine-2.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5f3d5c21884aee10e13b00ef45fab893a43db9d59ec27271573528bd359b0ef5"}, - {file = "time_machine-2.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a75e24e59f58059bbbc50e7f97aa6d126bbc2f603a8a5cd1e884beffcf130d8f"}, - {file = "time_machine-2.14.1-cp38-cp38-win32.whl", hash = "sha256:b0f8ba70fbb71d7fbc6d6adb90bed72a83db15b3318c7af0060467539b2f1b63"}, - {file = "time_machine-2.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:15cf3623a4ba2bb4fce4529295570acd5f6c6b44bcbfd1b8d0756ce56c38fe82"}, - {file = "time_machine-2.14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bb3a2518c52aa944989b541e5297b833388eb3fe72d91eb875b21fe771597b04"}, - {file = "time_machine-2.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:416d94eab7723c7d8a37fe6b3b1882046fdbf3c31b9abec3cac87cf35dbb8230"}, - {file = "time_machine-2.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adfbfa796dd96383400b44681eacc5ab06d3cbfad39c30878e5ead0bfdca808a"}, - {file = "time_machine-2.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31e6e9bff89b7c6e4cbc169ba1d00d6c107b3abc43173b2799352b6995cf7cb2"}, - {file = "time_machine-2.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107caed387438d689180b692e8d84aa1ebe8918790df83dc5e2146e60e5e0859"}, - {file = "time_machine-2.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cab4abf4d1490a7da35db5a321ff8a4d4a2195f4832a792c75b626ffc4a5584c"}, - {file = "time_machine-2.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fd8645b820f7895fdafbc4412d1ce376956e36ad4fd05a43269aa06c3132afc3"}, - {file = "time_machine-2.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:dd26039a9ffea2d5ee1309f2ec9b656d4925371c65563822d52e4037a4186eca"}, - {file = "time_machine-2.14.1-cp39-cp39-win32.whl", hash = "sha256:5e19b19d20bfbff8c97949e06e150998cf9d0a676e1641fb90597e59a9d7d5e2"}, - {file = "time_machine-2.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:f5d371a5218318121a6b44c21438258b6408b8bfe7ccccb754cf8eb880505576"}, - {file = "time_machine-2.14.1-cp39-cp39-win_arm64.whl", hash = "sha256:2c774f4b603a36ca2611327c57aa8ce0d5042298da008238ee5234b31ce7b22c"}, -] - -[package.dependencies] -python-dateutil = "*" - -[[package]] -name = "tinycss2" -version = "1.2.1" -description = "A tiny CSS parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"}, - {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"}, -] - -[package.dependencies] -webencodings = ">=0.4" - -[package.extras] -doc = ["sphinx", "sphinx_rtd_theme"] -test = ["flake8", "isort", "pytest"] - -[[package]] -name = "tokenizers" -version = "0.13.3" -description = "Fast and Customizable Tokenizers" -optional = true -python-versions = "*" -files = [ - {file = "tokenizers-0.13.3-cp310-cp310-macosx_10_11_x86_64.whl", hash = "sha256:f3835c5be51de8c0a092058a4d4380cb9244fb34681fd0a295fbf0a52a5fdf33"}, - {file = "tokenizers-0.13.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4ef4c3e821730f2692489e926b184321e887f34fb8a6b80b8096b966ba663d07"}, - {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5fd1a6a25353e9aa762e2aae5a1e63883cad9f4e997c447ec39d071020459bc"}, - {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee0b1b311d65beab83d7a41c56a1e46ab732a9eed4460648e8eb0bd69fc2d059"}, - {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ef4215284df1277dadbcc5e17d4882bda19f770d02348e73523f7e7d8b8d396"}, - {file = "tokenizers-0.13.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4d53976079cff8a033f778fb9adca2d9d69d009c02fa2d71a878b5f3963ed30"}, - {file = "tokenizers-0.13.3-cp310-cp310-win32.whl", hash = "sha256:1f0e3b4c2ea2cd13238ce43548959c118069db7579e5d40ec270ad77da5833ce"}, - {file = "tokenizers-0.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:89649c00d0d7211e8186f7a75dfa1db6996f65edce4b84821817eadcc2d3c79e"}, - {file = "tokenizers-0.13.3-cp311-cp311-macosx_10_11_universal2.whl", hash = "sha256:56b726e0d2bbc9243872b0144515ba684af5b8d8cd112fb83ee1365e26ec74c8"}, - {file = "tokenizers-0.13.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:cc5c022ce692e1f499d745af293ab9ee6f5d92538ed2faf73f9708c89ee59ce6"}, - {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55c981ac44ba87c93e847c333e58c12abcbb377a0c2f2ef96e1a266e4184ff2"}, - {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f247eae99800ef821a91f47c5280e9e9afaeed9980fc444208d5aa6ba69ff148"}, - {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b3e3215d048e94f40f1c95802e45dcc37c5b05eb46280fc2ccc8cd351bff839"}, - {file = "tokenizers-0.13.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ba2b0bf01777c9b9bc94b53764d6684554ce98551fec496f71bc5be3a03e98b"}, - {file = "tokenizers-0.13.3-cp311-cp311-win32.whl", hash = "sha256:cc78d77f597d1c458bf0ea7c2a64b6aa06941c7a99cb135b5969b0278824d808"}, - {file = "tokenizers-0.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:ecf182bf59bd541a8876deccf0360f5ae60496fd50b58510048020751cf1724c"}, - {file = "tokenizers-0.13.3-cp37-cp37m-macosx_10_11_x86_64.whl", hash = "sha256:0527dc5436a1f6bf2c0327da3145687d3bcfbeab91fed8458920093de3901b44"}, - {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cbb2c307627dc99b44b22ef05ff4473aa7c7cc1fec8f0a8b37d8a64b1a16d2"}, - {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4560dbdeaae5b7ee0d4e493027e3de6d53c991b5002d7ff95083c99e11dd5ac0"}, - {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64064bd0322405c9374305ab9b4c07152a1474370327499911937fd4a76d004b"}, - {file = "tokenizers-0.13.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8c6e2ab0f2e3d939ca66aa1d596602105fe33b505cd2854a4c1717f704c51de"}, - {file = "tokenizers-0.13.3-cp37-cp37m-win32.whl", hash = "sha256:6cc29d410768f960db8677221e497226e545eaaea01aa3613fa0fdf2cc96cff4"}, - {file = "tokenizers-0.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fc2a7fdf864554a0dacf09d32e17c0caa9afe72baf9dd7ddedc61973bae352d8"}, - {file = "tokenizers-0.13.3-cp38-cp38-macosx_10_11_x86_64.whl", hash = "sha256:8791dedba834c1fc55e5f1521be325ea3dafb381964be20684b92fdac95d79b7"}, - {file = "tokenizers-0.13.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:d607a6a13718aeb20507bdf2b96162ead5145bbbfa26788d6b833f98b31b26e1"}, - {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3791338f809cd1bf8e4fee6b540b36822434d0c6c6bc47162448deee3f77d425"}, - {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2f35f30e39e6aab8716f07790f646bdc6e4a853816cc49a95ef2a9016bf9ce6"}, - {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310204dfed5aa797128b65d63538a9837cbdd15da2a29a77d67eefa489edda26"}, - {file = "tokenizers-0.13.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0f9b92ea052305166559f38498b3b0cae159caea712646648aaa272f7160963"}, - {file = "tokenizers-0.13.3-cp38-cp38-win32.whl", hash = "sha256:9a3fa134896c3c1f0da6e762d15141fbff30d094067c8f1157b9fdca593b5806"}, - {file = "tokenizers-0.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:8e7b0cdeace87fa9e760e6a605e0ae8fc14b7d72e9fc19c578116f7287bb873d"}, - {file = "tokenizers-0.13.3-cp39-cp39-macosx_10_11_x86_64.whl", hash = "sha256:00cee1e0859d55507e693a48fa4aef07060c4bb6bd93d80120e18fea9371c66d"}, - {file = "tokenizers-0.13.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a23ff602d0797cea1d0506ce69b27523b07e70f6dda982ab8cf82402de839088"}, - {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ce07445050b537d2696022dafb115307abdffd2a5c106f029490f84501ef97"}, - {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:280ffe95f50eaaf655b3a1dc7ff1d9cf4777029dbbc3e63a74e65a056594abc3"}, - {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97acfcec592f7e9de8cadcdcda50a7134423ac8455c0166b28c9ff04d227b371"}, - {file = "tokenizers-0.13.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd7730c98a3010cd4f523465867ff95cd9d6430db46676ce79358f65ae39797b"}, - {file = "tokenizers-0.13.3-cp39-cp39-win32.whl", hash = "sha256:48625a108029cb1ddf42e17a81b5a3230ba6888a70c9dc14e81bc319e812652d"}, - {file = "tokenizers-0.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:bc0a6f1ba036e482db6453571c9e3e60ecd5489980ffd95d11dc9f960483d783"}, - {file = "tokenizers-0.13.3.tar.gz", hash = "sha256:2e546dbb68b623008a5442353137fbb0123d311a6d7ba52f2667c8862a75af2e"}, -] - -[package.extras] -dev = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] -docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] -testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] - -[[package]] -name = "tokenterminal" -version = "1.0.1" -description = "Unofficial Token Terminal API client." -optional = false -python-versions = "*" -files = [ - {file = "tokenterminal-1.0.1-py3-none-any.whl", hash = "sha256:6c7f1f44476835c64534c0f26c7170687c1a740cda6bba4063a44b775009a68c"}, - {file = "tokenterminal-1.0.1.tar.gz", hash = "sha256:b632755ed1125d8aef9d8c8c23271917040a45fc775d4559dc99ebab89da912b"}, -] - -[package.dependencies] -requests = ">=2.18.4" - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tomlkit" -version = "0.12.4" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, - {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, -] - -[[package]] -name = "toolz" -version = "0.12.1" -description = "List processing tools and functional utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "toolz-0.12.1-py3-none-any.whl", hash = "sha256:d22731364c07d72eea0a0ad45bafb2c2937ab6fd38a3507bf55eae8744aa7d85"}, - {file = "toolz-0.12.1.tar.gz", hash = "sha256:ecca342664893f177a13dac0e6b41cbd8ac25a358e5f215316d43e2100224f4d"}, -] - -[[package]] -name = "torch" -version = "1.11.0" -description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" -optional = true -python-versions = ">=3.7.0" -files = [ - {file = "torch-1.11.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:62052b50fffc29ca7afc0c04ef8206b6f1ca9d10629cb543077e12967e8d0398"}, - {file = "torch-1.11.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:866bfba29ac98dec35d893d8e17eaec149d0ac7a53be7baae5c98069897db667"}, - {file = "torch-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:951640fb8db308a59d9b510e7d1ad910aff92913323bbe4bc75435347ddd346d"}, - {file = "torch-1.11.0-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:5d77b5ece78fdafa5c7f42995ff9474399d22571cd6b2de21a5d666306a2ff8c"}, - {file = "torch-1.11.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:b5a38682769b544c875ecc34bcb81fbad5c922139b61319aacffcfd8a32f528c"}, - {file = "torch-1.11.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f82d77695a60626f2b7382d85bc566de8a6b3e50d32080755abc040db802e419"}, - {file = "torch-1.11.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b96654d42566080a134e784705f33f8536b3b95b5dcde357ed7879b1692a5f78"}, - {file = "torch-1.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8ee7c2e8d7f7020d5bfbc1bb91b9591044c26bbd0cee5e4f694cfd7ed8649260"}, - {file = "torch-1.11.0-cp37-none-macosx_10_9_x86_64.whl", hash = "sha256:6860b1d1bf0bb0b67a6bd47f85a0e4c825b518eea13b5d6101999dbbcbd5bc0c"}, - {file = "torch-1.11.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4322aa29f50da7f404db06cdf30896ea67b09f673af4a985afc7162bc897864d"}, - {file = "torch-1.11.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e4d2e0ddd652f30e94cff750220324ec45705d4ecc69658f773b3cb1c7a28dd0"}, - {file = "torch-1.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:34ce5ea4d8d85da32cdbadb50d4585106901e9f8a3527991daa70c13a09de1f7"}, - {file = "torch-1.11.0-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:0ccc85cd06227a3edf809e2c795fd5762c3d4e8a38b5c9f744c6e7cf841361bb"}, - {file = "torch-1.11.0-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:c1554e49d74f1b2c3e7202d77056ba2dd7465437585bac64062b580f714a44e9"}, - {file = "torch-1.11.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:58c7814502b1c129a650d7092033bbb0bbd64faf1a7941631aaa1aeaddc37570"}, - {file = "torch-1.11.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:831cf588f01dda9409e75576741d2823453990dee2983d670f2584b37a01adf7"}, - {file = "torch-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:44a1d02fd20f827f0f36dc26fdcfc45e793806a6ad52769a22260655a77a4369"}, - {file = "torch-1.11.0-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:50fd9bf85c578c871c28f1cb0ace9dfc6024401c7f399b174fb0f370899f4454"}, - {file = "torch-1.11.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:0e48af66ad755f0f9c5f2664028a414f57c49d6adc37e77e06fe0004da4edb61"}, -] - -[package.dependencies] -typing-extensions = "*" - -[[package]] -name = "torchmetrics" -version = "1.3.2" -description = "PyTorch native Metrics" -optional = true -python-versions = ">=3.8" -files = [ - {file = "torchmetrics-1.3.2-py3-none-any.whl", hash = "sha256:44ca3a9f86dc050cb3f554836ef291698ea797778457195b4f685fce8e2e64a3"}, - {file = "torchmetrics-1.3.2.tar.gz", hash = "sha256:0a67694a4c4265eeb54cda741eaf5cb1f3a71da74b7e7e6215ad156c9f2379f6"}, -] - -[package.dependencies] -lightning-utilities = ">=0.8.0" -numpy = ">1.20.0" -packaging = ">17.1" -torch = ">=1.10.0" -typing-extensions = {version = "*", markers = "python_version < \"3.9\""} - -[package.extras] -all = ["SciencePlots (>=2.0.0)", "ipadic (>=1.0.0)", "matplotlib (>=3.3.0)", "mecab-ko (>=1.0.0)", "mecab-ko-dic (>=1.0.0)", "mecab-python3 (>=1.0.6)", "mypy (==1.8.0)", "nltk (>=3.6)", "piq (<=0.8.0)", "pycocotools (>2.0.0)", "pystoi (>=0.3.0)", "regex (>=2021.9.24)", "scipy (>1.0.0)", "sentencepiece (>=0.2.0)", "torch (==2.2.1)", "torch-fidelity (<=0.4.0)", "torchaudio (>=0.10.0)", "torchvision (>=0.8)", "tqdm (>=4.41.0)", "transformers (>4.4.0)", "transformers (>=4.10.0)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] -audio = ["pystoi (>=0.3.0)", "torchaudio (>=0.10.0)"] -detection = ["pycocotools (>2.0.0)", "torchvision (>=0.8)"] -dev = ["SciencePlots (>=2.0.0)", "bert-score (==0.3.13)", "dython (<=0.7.5)", "fairlearn", "fast-bss-eval (>=0.1.0)", "faster-coco-eval (>=1.3.3)", "huggingface-hub (<0.22)", "ipadic (>=1.0.0)", "jiwer (>=2.3.0)", "kornia (>=0.6.7)", "lpips (<=0.1.4)", "matplotlib (>=3.3.0)", "mecab-ko (>=1.0.0)", "mecab-ko-dic (>=1.0.0)", "mecab-python3 (>=1.0.6)", "mir-eval (>=0.6)", "monai (==1.3.0)", "mypy (==1.8.0)", "netcal (>1.0.0)", "nltk (>=3.6)", "numpy (<1.25.0)", "pandas (>1.0.0)", "pandas (>=1.4.0)", "piq (<=0.8.0)", "pycocotools (>2.0.0)", "pystoi (>=0.3.0)", "pytorch-msssim (==1.0.0)", "regex (>=2021.9.24)", "rouge-score (>0.1.0)", "sacrebleu (>=2.3.0)", "scikit-image (>=0.19.0)", "scipy (>1.0.0)", "sentencepiece (>=0.2.0)", "sewar (>=0.4.4)", "statsmodels (>0.13.5)", "torch (==2.2.1)", "torch-complex (<=0.4.3)", "torch-fidelity (<=0.4.0)", "torchaudio (>=0.10.0)", "torchvision (>=0.8)", "tqdm (>=4.41.0)", "transformers (>4.4.0)", "transformers (>=4.10.0)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] -image = ["scipy (>1.0.0)", "torch-fidelity (<=0.4.0)", "torchvision (>=0.8)"] -multimodal = ["piq (<=0.8.0)", "transformers (>=4.10.0)"] -text = ["ipadic (>=1.0.0)", "mecab-ko (>=1.0.0)", "mecab-ko-dic (>=1.0.0)", "mecab-python3 (>=1.0.6)", "nltk (>=3.6)", "regex (>=2021.9.24)", "sentencepiece (>=0.2.0)", "tqdm (>=4.41.0)", "transformers (>4.4.0)"] -typing = ["mypy (==1.8.0)", "torch (==2.2.1)", "types-PyYAML", "types-emoji", "types-protobuf", "types-requests", "types-setuptools", "types-six", "types-tabulate"] -visual = ["SciencePlots (>=2.0.0)", "matplotlib (>=3.3.0)"] - -[[package]] -name = "tornado" -version = "6.4" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -optional = false -python-versions = ">= 3.8" -files = [ - {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, - {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, - {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, - {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, - {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, -] - -[[package]] -name = "tqdm" -version = "4.66.2" -description = "Fast, Extensible Progress Meter" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, - {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - -[[package]] -name = "tradingview-ta" -version = "3.3.0" -description = "Unofficial TradingView technical analysis API wrapper." -optional = false -python-versions = ">=3.6" -files = [ - {file = "tradingview_ta-3.3.0-py3-none-any.whl", hash = "sha256:1250068a44e253caeb2066bc1cd6e588f71796612d41d5d55d25083cf86b5531"}, - {file = "tradingview_ta-3.3.0.tar.gz", hash = "sha256:f1a55351ed28554234106a28772f167e818d57f384d47f85b763bd143955c34a"}, -] - -[package.dependencies] -requests = "*" - -[[package]] -name = "traitlets" -version = "5.14.3" -description = "Traitlets Python configuration system" -optional = false -python-versions = ">=3.8" -files = [ - {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, - {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, -] - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] - -[[package]] -name = "transformers" -version = "4.27.1" -description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" -optional = true -python-versions = ">=3.7.0" -files = [ - {file = "transformers-4.27.1-py3-none-any.whl", hash = "sha256:b8cf844cbeaefd24b07f7ab1550af11b5d9cf0a7d09e5f4fedede9b15a3da2c0"}, - {file = "transformers-4.27.1.tar.gz", hash = "sha256:05120d91e407c0d3db364f5d0710cdacccd15f67114b075f161a71535c133695"}, -] - -[package.dependencies] -filelock = "*" -huggingface-hub = ">=0.11.0,<1.0" -numpy = ">=1.17" -packaging = ">=20.0" -pyyaml = ">=5.1" -regex = "!=2019.12.17" -requests = "*" -tokenizers = ">=0.11.1,<0.11.3 || >0.11.3,<0.14" -tqdm = ">=4.27" - -[package.extras] -accelerate = ["accelerate (>=0.10.0)"] -all = ["Pillow", "accelerate (>=0.10.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1)", "jax (>=0.2.8,!=0.3.2,<=0.3.6)", "jaxlib (>=0.1.65,<=0.3.6)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8)", "optuna", "phonemizer", "protobuf (<=3.20.2)", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.4,<2.12)", "tensorflow-text", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.7,!=1.12.0)", "torchaudio", "torchvision"] -audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -codecarbon = ["codecarbon (==1.2.0)"] -deepspeed = ["accelerate (>=0.10.0)", "deepspeed (>=0.6.5)"] -deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.10.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.6.5)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "optuna", "parameterized", "protobuf (<=3.20.2)", "psutil", "pytest", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "sentencepiece (>=0.1.91,!=0.1.92)", "timeout-decorator"] -dev = ["GitPython (<3.1.19)", "Pillow", "accelerate (>=0.10.0)", "av (==9.2.0)", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "decord (==0.6.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1)", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.2.8,!=0.3.2,<=0.3.6)", "jaxlib (>=0.1.65,<=0.3.6)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "optax (>=0.0.8)", "optuna", "parameterized", "phonemizer", "protobuf (<=3.20.2)", "psutil", "pyctcdecode (>=0.4.0)", "pytest", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorflow (>=2.4,<2.12)", "tensorflow-text", "tf2onnx", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.7,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] -dev-tensorflow = ["GitPython (<3.1.19)", "Pillow", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf (<=3.20.2)", "psutil", "pyctcdecode (>=0.4.0)", "pytest", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorflow (>=2.4,<2.12)", "tensorflow-text", "tf2onnx", "timeout-decorator", "tokenizers (>=0.11.1,!=0.11.3,<0.14)"] -dev-torch = ["GitPython (<3.1.19)", "Pillow", "beautifulsoup4", "black (>=23.1,<24.0)", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "librosa", "nltk", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf (<=3.20.2)", "psutil", "pyctcdecode (>=0.4.0)", "pytest", "pytest-timeout", "pytest-xdist", "ray[tune]", "rhoknp (>=1.1.0)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (>=0.0.241)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "timeout-decorator", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.7,!=1.12.0)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] -docs = ["Pillow", "accelerate (>=0.10.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1)", "hf-doc-builder", "jax (>=0.2.8,!=0.3.2,<=0.3.6)", "jaxlib (>=0.1.65,<=0.3.6)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8)", "optuna", "phonemizer", "protobuf (<=3.20.2)", "pyctcdecode (>=0.4.0)", "ray[tune]", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.4,<2.12)", "tensorflow-text", "tf2onnx", "timm", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.7,!=1.12.0)", "torchaudio", "torchvision"] -docs-specific = ["hf-doc-builder"] -fairscale = ["fairscale (>0.3)"] -flax = ["flax (>=0.4.1)", "jax (>=0.2.8,!=0.3.2,<=0.3.6)", "jaxlib (>=0.1.65,<=0.3.6)", "optax (>=0.0.8)"] -flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -ftfy = ["ftfy"] -integrations = ["optuna", "ray[tune]", "sigopt"] -ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] -modelcreation = ["cookiecutter (==1.7.3)"] -natten = ["natten (>=0.14.4)"] -onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] -onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] -optuna = ["optuna"] -quality = ["GitPython (<3.1.19)", "black (>=23.1,<24.0)", "datasets (!=2.5.0)", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "ruff (>=0.0.241)"] -ray = ["ray[tune]"] -retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] -sagemaker = ["sagemaker (>=2.31.0)"] -sentencepiece = ["protobuf (<=3.20.2)", "sentencepiece (>=0.1.91,!=0.1.92)"] -serving = ["fastapi", "pydantic", "starlette", "uvicorn"] -sigopt = ["sigopt"] -sklearn = ["scikit-learn"] -speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -testing = ["GitPython (<3.1.19)", "beautifulsoup4", "black (>=23.1,<24.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "parameterized", "protobuf (<=3.20.2)", "psutil", "pytest", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "safetensors (>=0.2.1)", "timeout-decorator"] -tf = ["keras-nlp (>=0.3.1)", "onnxconverter-common", "tensorflow (>=2.4,<2.12)", "tensorflow-text", "tf2onnx"] -tf-cpu = ["keras-nlp (>=0.3.1)", "onnxconverter-common", "tensorflow-cpu (>=2.4,<2.12)", "tensorflow-text", "tf2onnx"] -tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -timm = ["timm"] -tokenizers = ["tokenizers (>=0.11.1,!=0.11.3,<0.14)"] -torch = ["torch (>=1.7,!=1.12.0)"] -torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -torch-vision = ["Pillow", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.11.0,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf (<=3.20.2)", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.11.1,!=0.11.3,<0.14)", "torch (>=1.7,!=1.12.0)", "tqdm (>=4.27)"] -video = ["av (==9.2.0)", "decord (==0.6.0)"] -vision = ["Pillow"] - -[[package]] -name = "triad" -version = "0.9.6" -description = "A collection of python utils for Fugue projects" -optional = true -python-versions = ">=3.8" -files = [ - {file = "triad-0.9.6-py3-none-any.whl", hash = "sha256:5c7391f2e5e14a705d75fe3794bf13640f44522f381b542c2c553f99f012da03"}, - {file = "triad-0.9.6.tar.gz", hash = "sha256:4a54f3ccb23b47eb7df940ab1aac209c0e056417a086029f4d2adb38502ac451"}, -] - -[package.dependencies] -fs = "*" -fsspec = ">=2022.5.0" -numpy = "*" -pandas = ">=1.3.5" -pyarrow = ">=6.0.1" -six = "*" - -[package.extras] -ciso8601 = ["ciso8601"] - -[[package]] -name = "types-python-dateutil" -version = "2.9.0.20240316" -description = "Typing stubs for python-dateutil" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, -] - -[[package]] -name = "types-pytz" -version = "2021.3.8" -description = "Typing stubs for pytz" -optional = false -python-versions = "*" -files = [ - {file = "types-pytz-2021.3.8.tar.gz", hash = "sha256:41253a3a2bf028b6a3f17b58749a692d955af0f74e975de94f6f4d2d3cd01dbd"}, - {file = "types_pytz-2021.3.8-py3-none-any.whl", hash = "sha256:aef4a917ab28c585d3f474bfce4f4b44b91e95d9d47d4de29dd845e0db8e3910"}, -] - -[[package]] -name = "types-pyyaml" -version = "6.0.12.20240311" -description = "Typing stubs for PyYAML" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-PyYAML-6.0.12.20240311.tar.gz", hash = "sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342"}, - {file = "types_PyYAML-6.0.12.20240311-py3-none-any.whl", hash = "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6"}, -] - -[[package]] -name = "types-requests" -version = "2.31.0.20240406" -description = "Typing stubs for requests" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, - {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, -] - -[package.dependencies] -urllib3 = ">=2" - -[[package]] -name = "types-setuptools" -version = "57.4.18" -description = "Typing stubs for setuptools" -optional = false -python-versions = "*" -files = [ - {file = "types-setuptools-57.4.18.tar.gz", hash = "sha256:8ee03d823fe7fda0bd35faeae33d35cb5c25b497263e6a58b34c4cfd05f40bcf"}, - {file = "types_setuptools-57.4.18-py3-none-any.whl", hash = "sha256:9660b8774b12cd61b448e2fd87a667c02e7ec13ce9f15171f1d49a4654c4df6a"}, -] - -[[package]] -name = "types-six" -version = "1.16.21.20240311" -description = "Typing stubs for six" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-six-1.16.21.20240311.tar.gz", hash = "sha256:b5a117193ba0dc7a66507925e95e140b2af52731402cdd71ef9f2a4348e01f60"}, - {file = "types_six-1.16.21.20240311-py3-none-any.whl", hash = "sha256:abc0377990d38e9b37b3333dd115ec960ca9788d78f3d9c7eb3f778cfc6c925c"}, -] - -[[package]] -name = "typing-extensions" -version = "4.11.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, -] - -[[package]] -name = "tzdata" -version = "2024.1" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, -] - -[[package]] -name = "tzlocal" -version = "5.2" -description = "tzinfo object for the local timezone" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, - {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, -] - -[package.dependencies] -"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} -tzdata = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] - -[[package]] -name = "u8darts" -version = "0.23.0" -description = "A python library for easy manipulation and forecasting of time series." -optional = true -python-versions = ">=3.7" -files = [ - {file = "u8darts-0.23.0-py3-none-any.whl", hash = "sha256:a35fff128ca1469a8d41267f67e0e680d225221f0d45319a74bcd7ac8df692eb"}, - {file = "u8darts-0.23.0.tar.gz", hash = "sha256:4556f3a82e920ccb8bb70e6f05afa0514c8f0353d297e1b6a6a022cb3c7ef82b"}, -] - -[package.dependencies] -catboost = ">=1.0.6" -holidays = ">=0.11.1" -joblib = ">=0.16.0" -lightgbm = ">=3.2.0" -matplotlib = ">=3.3.0" -nfoursid = ">=1.0.0" -numpy = ">=1.19.0" -pandas = ">=1.0.5" -pmdarima = ">=1.8.0" -prophet = ">=1.1.1" -pyod = ">=0.9.5" -pytorch-lightning = {version = ">=1.5.0", optional = true, markers = "extra == \"torch\""} -requests = ">=2.22.0" -scikit-learn = ">=1.0.1" -scipy = ">=1.3.2" -shap = ">=0.40.0" -statsforecast = ">=1.0.0" -statsmodels = ">=0.13.0" -tbats = ">=1.1.0" -torch = {version = ">=1.8.0", optional = true, markers = "extra == \"torch\""} -tqdm = ">=4.60.0" -xarray = ">=0.17.0" -xgboost = ">=1.6.0" - -[package.extras] -all = ["catboost (>=1.0.6)", "holidays (>=0.11.1)", "joblib (>=0.16.0)", "lightgbm (>=3.2.0)", "matplotlib (>=3.3.0)", "nfoursid (>=1.0.0)", "numpy (>=1.19.0)", "pandas (>=1.0.5)", "pmdarima (>=1.8.0)", "prophet (>=1.1.1)", "pyod (>=0.9.5)", "pytorch-lightning (>=1.5.0)", "requests (>=2.22.0)", "scikit-learn (>=1.0.1)", "scipy (>=1.3.2)", "shap (>=0.40.0)", "statsforecast (>=1.0.0)", "statsmodels (>=0.13.0)", "tbats (>=1.1.0)", "torch (>=1.8.0)", "tqdm (>=4.60.0)", "xarray (>=0.17.0)", "xgboost (>=1.6.0)"] -torch = ["pytorch-lightning (>=1.5.0)", "torch (>=1.8.0)"] - -[[package]] -name = "ujson" -version = "5.9.0" -description = "Ultra fast JSON encoder and decoder for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, - {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, - {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, - {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, - {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, - {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, - {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, - {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, - {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, - {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, - {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, - {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, - {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, - {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, - {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, - {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, - {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, - {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, - {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, - {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, - {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, - {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, - {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, - {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, - {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, - {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, - {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, - {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, - {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, - {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, - {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, - {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, - {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, - {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, -] - -[[package]] -name = "update-checker" -version = "0.18.0" -description = "A python module that will check for package updates." -optional = false -python-versions = "*" -files = [ - {file = "update_checker-0.18.0-py3-none-any.whl", hash = "sha256:cbba64760a36fe2640d80d85306e8fe82b6816659190993b7bdabadee4d4bbfd"}, - {file = "update_checker-0.18.0.tar.gz", hash = "sha256:6a2d45bb4ac585884a6b03f9eade9161cedd9e8111545141e9aa9058932acb13"}, -] - -[package.dependencies] -requests = ">=2.3.0" - -[package.extras] -dev = ["black", "flake8", "pytest (>=2.7.3)"] -lint = ["black", "flake8"] -test = ["pytest (>=2.7.3)"] - -[[package]] -name = "uri-template" -version = "1.3.0" -description = "RFC 6570 URI Template Processor" -optional = false -python-versions = ">=3.7" -files = [ - {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"}, - {file = "uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"}, -] - -[package.extras] -dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"] - -[[package]] -name = "urllib3" -version = "2.2.1" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.8" -files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "user-agent" -version = "0.1.10" -description = "User-Agent generator" -optional = false -python-versions = "*" -files = [ - {file = "user_agent-0.1.10.tar.gz", hash = "sha256:b86537cb2a9d3bda0e2afcc654ec15b383502836877a67520654acadf73f1723"}, -] - -[package.dependencies] -six = "*" - -[[package]] -name = "utilsforecast" -version = "0.1.4" -description = "Forecasting utilities" -optional = false -python-versions = ">=3.8" -files = [ - {file = "utilsforecast-0.1.4-py3-none-any.whl", hash = "sha256:0b7daf492f2e01020b4e65065bea18ee6fda4297ae3251266227d57696b865ec"}, - {file = "utilsforecast-0.1.4.tar.gz", hash = "sha256:692d838cc8937415134dd886169d5399fecd794b7bb154c7bf98b1104368958c"}, -] - -[package.dependencies] -numpy = "*" -packaging = "*" -pandas = ">=1.1.1" - -[package.extras] -dev = ["datasetsforecast (==0.0.8)", "nbdev", "numba", "pandas[plot]", "plotly", "plotly-resampler", "polars", "pyarrow", "scipy"] -plotting = ["pandas[plot]", "plotly", "plotly-resampler"] -polars = ["polars"] -scalers = ["numba", "scipy"] - -[[package]] -name = "vadersentiment" -version = "3.3.2" -description = "VADER Sentiment Analysis. VADER (Valence Aware Dictionary and sEntiment Reasoner) is a lexicon and rule-based sentiment analysis tool that is specifically attuned to sentiments expressed in social media, and works well on texts from other domains." -optional = false -python-versions = "*" -files = [ - {file = "vaderSentiment-3.3.2-py2.py3-none-any.whl", hash = "sha256:3bf1d243b98b1afad575b9f22bc2cb1e212b94ff89ca74f8a23a588d024ea311"}, - {file = "vaderSentiment-3.3.2.tar.gz", hash = "sha256:5d7c06e027fc8b99238edb0d53d970cf97066ef97654009890b83703849632f9"}, -] - -[package.dependencies] -requests = "*" - -[[package]] -name = "valinvest" -version = "0.0.2" -description = "A value investing tool based on Warren Buffett, Joseph Piotroski and Benjamin Graham thoughts" -optional = false -python-versions = ">=3.6" -files = [ - {file = "valinvest-0.0.2-py3-none-any.whl", hash = "sha256:37fadaf30c69e0487fed8d9cc93bb792ca8cf36fed0839e0e755a84738166ab9"}, - {file = "valinvest-0.0.2.tar.gz", hash = "sha256:9614aaf8019e015c20ea48867ede8a6ea10e1c6410e787314066d7b2e5aeb7dc"}, -] - -[[package]] -name = "vcrpy" -version = "4.3.0" -description = "Automatically mock your HTTP interactions to simplify and speed up testing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "vcrpy-4.3.0-py2.py3-none-any.whl", hash = "sha256:8fbd4be412e8a7f35f623dd61034e6380a1c8dbd0edf6e87277a3289f6e98093"}, - {file = "vcrpy-4.3.0.tar.gz", hash = "sha256:49c270ce67e826dba027d83e20d25b67a5885487697e97bca6dbdf53d750a0ac"}, -] - -[package.dependencies] -PyYAML = "*" -six = ">=1.5" -wrapt = "*" -yarl = "*" - -[[package]] -name = "virtualenv" -version = "20.25.3" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.25.3-py3-none-any.whl", hash = "sha256:8aac4332f2ea6ef519c648d0bc48a5b1d324994753519919bddbb1aff25a104e"}, - {file = "virtualenv-20.25.3.tar.gz", hash = "sha256:7bb554bbdfeaacc3349fa614ea5bff6ac300fc7c335e9facf3a3bcfc703f45be"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[[package]] -name = "watchdog" -version = "2.3.1" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.6" -files = [ - {file = "watchdog-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1f1200d4ec53b88bf04ab636f9133cb703eb19768a39351cee649de21a33697"}, - {file = "watchdog-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:564e7739abd4bd348aeafbf71cc006b6c0ccda3160c7053c4a53b67d14091d42"}, - {file = "watchdog-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:95ad708a9454050a46f741ba5e2f3468655ea22da1114e4c40b8cbdaca572565"}, - {file = "watchdog-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a073c91a6ef0dda488087669586768195c3080c66866144880f03445ca23ef16"}, - {file = "watchdog-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa8b028750b43e80eea9946d01925168eeadb488dfdef1d82be4b1e28067f375"}, - {file = "watchdog-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:964fd236cd443933268ae49b59706569c8b741073dbfd7ca705492bae9d39aab"}, - {file = "watchdog-2.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:91fd146d723392b3e6eb1ac21f122fcce149a194a2ba0a82c5e4d0ee29cd954c"}, - {file = "watchdog-2.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:efe3252137392a471a2174d721e1037a0e6a5da7beb72a021e662b7000a9903f"}, - {file = "watchdog-2.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:85bf2263290591b7c5fa01140601b64c831be88084de41efbcba6ea289874f44"}, - {file = "watchdog-2.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f2df370cd8e4e18499dd0bfdef476431bcc396108b97195d9448d90924e3131"}, - {file = "watchdog-2.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ea5d86d1bcf4a9d24610aa2f6f25492f441960cf04aed2bd9a97db439b643a7b"}, - {file = "watchdog-2.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6f5d0f7eac86807275eba40b577c671b306f6f335ba63a5c5a348da151aba0fc"}, - {file = "watchdog-2.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b848c71ef2b15d0ef02f69da8cc120d335cec0ed82a3fa7779e27a5a8527225"}, - {file = "watchdog-2.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0d9878be36d2b9271e3abaa6f4f051b363ff54dbbe7e7df1af3c920e4311ee43"}, - {file = "watchdog-2.3.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4cd61f98cb37143206818cb1786d2438626aa78d682a8f2ecee239055a9771d5"}, - {file = "watchdog-2.3.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3d2dbcf1acd96e7a9c9aefed201c47c8e311075105d94ce5e899f118155709fd"}, - {file = "watchdog-2.3.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03f342a9432fe08107defbe8e405a2cb922c5d00c4c6c168c68b633c64ce6190"}, - {file = "watchdog-2.3.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7a596f9415a378d0339681efc08d2249e48975daae391d58f2e22a3673b977cf"}, - {file = "watchdog-2.3.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:0e1dd6d449267cc7d6935d7fe27ee0426af6ee16578eed93bacb1be9ff824d2d"}, - {file = "watchdog-2.3.1-py3-none-manylinux2014_i686.whl", hash = "sha256:7a1876f660e32027a1a46f8a0fa5747ad4fcf86cb451860eae61a26e102c8c79"}, - {file = "watchdog-2.3.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:2caf77ae137935c1466f8cefd4a3aec7017b6969f425d086e6a528241cba7256"}, - {file = "watchdog-2.3.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:53f3e95081280898d9e4fc51c5c69017715929e4eea1ab45801d5e903dd518ad"}, - {file = "watchdog-2.3.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:9da7acb9af7e4a272089bd2af0171d23e0d6271385c51d4d9bde91fe918c53ed"}, - {file = "watchdog-2.3.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8a4d484e846dcd75e96b96d80d80445302621be40e293bfdf34a631cab3b33dc"}, - {file = "watchdog-2.3.1-py3-none-win32.whl", hash = "sha256:a74155398434937ac2780fd257c045954de5b11b5c52fc844e2199ce3eecf4cf"}, - {file = "watchdog-2.3.1-py3-none-win_amd64.whl", hash = "sha256:5defe4f0918a2a1a4afbe4dbb967f743ac3a93d546ea4674567806375b024adb"}, - {file = "watchdog-2.3.1-py3-none-win_ia64.whl", hash = "sha256:4109cccf214b7e3462e8403ab1e5b17b302ecce6c103eb2fc3afa534a7f27b96"}, - {file = "watchdog-2.3.1.tar.gz", hash = "sha256:d9f9ed26ed22a9d331820a8432c3680707ea8b54121ddcc9dc7d9f2ceeb36906"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "wcwidth" -version = "0.2.13" -description = "Measures the displayed width of unicode strings in a terminal" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, -] - -[[package]] -name = "webcolors" -version = "1.13" -description = "A library for working with the color formats defined by HTML and CSS." -optional = false -python-versions = ">=3.7" -files = [ - {file = "webcolors-1.13-py3-none-any.whl", hash = "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf"}, - {file = "webcolors-1.13.tar.gz", hash = "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a"}, -] - -[package.extras] -docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] -tests = ["pytest", "pytest-cov"] - -[[package]] -name = "webencodings" -version = "0.5.1" -description = "Character encoding aliases for legacy web content" -optional = false -python-versions = "*" -files = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] - -[[package]] -name = "websocket-client" -version = "1.7.0" -description = "WebSocket client for Python with low level API options" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websocket-client-1.7.0.tar.gz", hash = "sha256:10e511ea3a8c744631d3bd77e61eb17ed09304c413ad42cf6ddfa4c7787e8fe6"}, - {file = "websocket_client-1.7.0-py3-none-any.whl", hash = "sha256:f4c3d22fec12a2461427a29957ff07d35098ee2d976d3ba244e688b8b4057588"}, -] - -[package.extras] -docs = ["Sphinx (>=6.0)", "sphinx-rtd-theme (>=1.1.0)"] -optional = ["python-socks", "wsaccel"] -test = ["websockets"] - -[[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, -] - -[[package]] -name = "werkzeug" -version = "3.0.2" -description = "The comprehensive WSGI web application library." -optional = true -python-versions = ">=3.8" -files = [ - {file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"}, - {file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"}, -] - -[package.dependencies] -MarkupSafe = ">=2.1.1" - -[package.extras] -watchdog = ["watchdog (>=2.3)"] - -[[package]] -name = "wheel" -version = "0.43.0" -description = "A built-package format for Python" -optional = true -python-versions = ">=3.8" -files = [ - {file = "wheel-0.43.0-py3-none-any.whl", hash = "sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81"}, - {file = "wheel-0.43.0.tar.gz", hash = "sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85"}, -] - -[package.extras] -test = ["pytest (>=6.0.0)", "setuptools (>=65)"] - -[[package]] -name = "widgetsnbextension" -version = "4.0.10" -description = "Jupyter interactive widgets for Jupyter Notebook" -optional = false -python-versions = ">=3.7" -files = [ - {file = "widgetsnbextension-4.0.10-py3-none-any.whl", hash = "sha256:d37c3724ec32d8c48400a435ecfa7d3e259995201fbefa37163124a9fcb393cc"}, - {file = "widgetsnbextension-4.0.10.tar.gz", hash = "sha256:64196c5ff3b9a9183a8e699a4227fb0b7002f252c814098e66c4d1cd0644688f"}, -] - -[[package]] -name = "win32-setctime" -version = "1.1.0" -description = "A small Python utility to set file creation time on Windows" -optional = false -python-versions = ">=3.5" -files = [ - {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, - {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, -] - -[package.extras] -dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] - -[[package]] -name = "wrapt" -version = "1.16.0" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.6" -files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, -] - -[[package]] -name = "xarray" -version = "2023.1.0" -description = "N-D labeled arrays and datasets in Python" -optional = true -python-versions = ">=3.8" -files = [ - {file = "xarray-2023.1.0-py3-none-any.whl", hash = "sha256:7e530b1deafdd43e5c2b577d0944e6b528fbe88045fd849e49a8d11871ecd522"}, - {file = "xarray-2023.1.0.tar.gz", hash = "sha256:7bee552751ff1b29dab8b7715726e5ecb56691ac54593cf4881dff41978ce0cd"}, -] - -[package.dependencies] -numpy = ">=1.20" -packaging = ">=21.3" -pandas = ">=1.3" - -[package.extras] -accel = ["bottleneck", "flox", "numbagg", "scipy"] -complete = ["bottleneck", "cfgrib", "cftime", "dask[complete]", "flox", "fsspec", "h5netcdf", "matplotlib", "nc-time-axis", "netCDF4", "numbagg", "pooch", "pydap", "rasterio", "scipy", "seaborn", "zarr"] -docs = ["bottleneck", "cfgrib", "cftime", "dask[complete]", "flox", "fsspec", "h5netcdf", "ipykernel", "ipython", "jupyter-client", "matplotlib", "nbsphinx", "nc-time-axis", "netCDF4", "numbagg", "pooch", "pydap", "rasterio", "scanpydoc", "scipy", "seaborn", "sphinx-autosummary-accessors", "sphinx-rtd-theme", "zarr"] -io = ["cfgrib", "cftime", "fsspec", "h5netcdf", "netCDF4", "pooch", "pydap", "rasterio", "scipy", "zarr"] -parallel = ["dask[complete]"] -viz = ["matplotlib", "nc-time-axis", "seaborn"] - -[[package]] -name = "xgboost" -version = "2.0.3" -description = "XGBoost Python Package" -optional = true -python-versions = ">=3.8" -files = [ - {file = "xgboost-2.0.3-py3-none-macosx_10_15_x86_64.macosx_11_0_x86_64.macosx_12_0_x86_64.whl", hash = "sha256:b21b2bb188b162c615fce468db93e3f995f3690e6184aadc7743b58466dc7f13"}, - {file = "xgboost-2.0.3-py3-none-macosx_12_0_arm64.whl", hash = "sha256:722d5b9351dfdf61973490dfd28abd42844db1cc469d07ed9b0cde9d1ffcdb32"}, - {file = "xgboost-2.0.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:2315a57b1883221e2f78dd514559aa9797e6c272d995d22e45495a04adac93cc"}, - {file = "xgboost-2.0.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:30bd5f789fad467fd49e04e5d19e04238b931682c3951a514da5c2410b3bf59c"}, - {file = "xgboost-2.0.3-py3-none-win_amd64.whl", hash = "sha256:462f131d7bfb1bc42f67c57fa5aa3e57d2b5755b1573a6e0d2c7e8895164e0fc"}, - {file = "xgboost-2.0.3.tar.gz", hash = "sha256:505955b5d770f8217a049beecce79e04a93787371c06dfb4b2414fec9d496bf3"}, -] - -[package.dependencies] -numpy = "*" -scipy = "*" - -[package.extras] -dask = ["dask", "distributed", "pandas"] -datatable = ["datatable"] -pandas = ["pandas"] -plotting = ["graphviz", "matplotlib"] -pyspark = ["cloudpickle", "pyspark", "scikit-learn"] -scikit-learn = ["scikit-learn"] - -[[package]] -name = "xlsxwriter" -version = "3.2.0" -description = "A Python module for creating Excel XLSX files." -optional = true -python-versions = ">=3.6" -files = [ - {file = "XlsxWriter-3.2.0-py3-none-any.whl", hash = "sha256:ecfd5405b3e0e228219bcaf24c2ca0915e012ca9464a14048021d21a995d490e"}, - {file = "XlsxWriter-3.2.0.tar.gz", hash = "sha256:9977d0c661a72866a61f9f7a809e25ebbb0fb7036baa3b9fe74afcfca6b3cb8c"}, -] - -[[package]] -name = "y-py" -version = "0.6.2" -description = "Python bindings for the Y-CRDT built from yrs (Rust)" -optional = false -python-versions = "*" -files = [ - {file = "y_py-0.6.2-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c26bada6cd109095139237a46f50fc4308f861f0d304bc9e70acbc6c4503d158"}, - {file = "y_py-0.6.2-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:bae1b1ad8d2b8cf938a60313f8f7461de609621c5dcae491b6e54975f76f83c5"}, - {file = "y_py-0.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e794e44fa260300b8850246c6371d94014753c73528f97f6ccb42f5e7ce698ae"}, - {file = "y_py-0.6.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b2686d7d8ca31531458a48e08b0344a8eec6c402405446ce7d838e2a7e43355a"}, - {file = "y_py-0.6.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d917f5bc27b85611ceee4eb85f0e4088b0a03b4eed22c472409933a94ee953cf"}, - {file = "y_py-0.6.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f6071328aad06fdcc0a4acc2dc4839396d645f5916de07584af807eb7c08407"}, - {file = "y_py-0.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:266ec46ab9f9cb40fbb5e649f55c329fc4620fa0b1a8117bdeefe91595e182dc"}, - {file = "y_py-0.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce15a842c2a0bf46180ae136743b561fa276300dd7fa61fe76daf00ec7dc0c2d"}, - {file = "y_py-0.6.2-cp310-none-win32.whl", hash = "sha256:1d5b544e79ace93fdbd0b36ed329c86e346898153ac7ba2ec62bc9b4c6b745c9"}, - {file = "y_py-0.6.2-cp310-none-win_amd64.whl", hash = "sha256:80a827e173372682959a57e6b8cc4f6468b1a4495b4bc7a775ef6ca05ae3e8e8"}, - {file = "y_py-0.6.2-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:a21148b8ea09a631b752d975f9410ee2a31c0e16796fdc113422a6d244be10e5"}, - {file = "y_py-0.6.2-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:898fede446ca1926b8406bdd711617c2aebba8227ee8ec1f0c2f8568047116f7"}, - {file = "y_py-0.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce7c20b9395696d3b5425dccf2706d374e61ccf8f3656bff9423093a6df488f5"}, - {file = "y_py-0.6.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a3932f53418b408fa03bd002e6dc573a74075c2c092926dde80657c39aa2e054"}, - {file = "y_py-0.6.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:df35ea436592eb7e30e59c5403ec08ec3a5e7759e270cf226df73c47b3e739f5"}, - {file = "y_py-0.6.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26cb1307c3ca9e21a3e307ab2c2099677e071ae9c26ec10ddffb3faceddd76b3"}, - {file = "y_py-0.6.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:863e175ce5585f9ff3eba2aa16626928387e2a576157f02c8eb247a218ecdeae"}, - {file = "y_py-0.6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:35fcb9def6ce137540fdc0e91b08729677548b9c393c0151a6359fd199da3bd7"}, - {file = "y_py-0.6.2-cp311-none-win32.whl", hash = "sha256:86422c6090f34906c062fd3e4fdfdccf3934f2922021e979573ae315050b4288"}, - {file = "y_py-0.6.2-cp311-none-win_amd64.whl", hash = "sha256:6c2f2831c5733b404d2f2da4bfd02bb4612ae18d0822e14ae79b0b92436b816d"}, - {file = "y_py-0.6.2-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:7cbefd4f1060f05768227ddf83be126397b1d430b026c64e0eb25d3cf50c5734"}, - {file = "y_py-0.6.2-cp312-cp312-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:032365dfe932bfab8e80937ad6093b4c22e67d63ad880096b5fa8768f8d829ba"}, - {file = "y_py-0.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a70aee572da3994238c974694767365f237fc5949a550bee78a650fe16f83184"}, - {file = "y_py-0.6.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae80d505aee7b3172cdcc2620ca6e2f85586337371138bb2b71aa377d2c31e9a"}, - {file = "y_py-0.6.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a497ebe617bec6a420fc47378856caae40ab0652e756f3ed40c5f1fe2a12220"}, - {file = "y_py-0.6.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8638355ae2f996356f7f281e03a3e3ce31f1259510f9d551465356532e0302c"}, - {file = "y_py-0.6.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8448da4092265142662bbd3fc46cb8b0796b1e259189c020bc8f738899abd0b5"}, - {file = "y_py-0.6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:69cfbcbe0a05f43e780e6a198080ba28034bf2bb4804d7d28f71a0379bfd1b19"}, - {file = "y_py-0.6.2-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:1f798165158b76365a463a4f8aa2e3c2a12eb89b1fc092e7020e93713f2ad4dc"}, - {file = "y_py-0.6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92878cc05e844c8da937204bc34c2e6caf66709ce5936802fbfb35f04132892"}, - {file = "y_py-0.6.2-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b8822a5c0fd9a8cffcabfcc0cd7326bad537ee614fc3654e413a03137b6da1a"}, - {file = "y_py-0.6.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e13cba03c7af8c8a846c4495875a09d64362cc4caeed495ada5390644411bbe7"}, - {file = "y_py-0.6.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82f2e5b31678065e7a7fa089ed974af5a4f076673cf4f414219bdadfc3246a21"}, - {file = "y_py-0.6.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1935d12e503780b859d343161a80df65205d23cad7b4f6c3df6e50321e188a3"}, - {file = "y_py-0.6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd302c6d46a3be57664571a5f0d4224646804be9890a01d73a0b294f2d3bbff1"}, - {file = "y_py-0.6.2-cp37-none-win32.whl", hash = "sha256:5415083f7f10eac25e1c434c87f07cb9bfa58909a6cad6649166fdad21119fc5"}, - {file = "y_py-0.6.2-cp37-none-win_amd64.whl", hash = "sha256:376c5cc0c177f03267340f36aec23e5eaf19520d41428d87605ca2ca3235d845"}, - {file = "y_py-0.6.2-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:3c011303eb2b360695d2bd4bd7ca85f42373ae89fcea48e7fa5b8dc6fc254a98"}, - {file = "y_py-0.6.2-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:c08311db17647a47d4898fc6f8d9c1f0e58b927752c894877ff0c38b3db0d6e1"}, - {file = "y_py-0.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b7cafbe946b4cafc1e5709957e6dd5c6259d241d48ed75713ded42a5e8a4663"}, - {file = "y_py-0.6.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ba99d0bdbd9cabd65f914cd07b4fb2e939ce199b54ae5ace1639ce1edf8e0a2"}, - {file = "y_py-0.6.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dab84c52f64e10adc79011a08673eb80286c159b14e8fb455524bf2994f0cb38"}, - {file = "y_py-0.6.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72875641a907523d37f4619eb4b303611d17e0a76f2ffc423b62dd1ca67eef41"}, - {file = "y_py-0.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c31240e30d5636ded02a54b7280aa129344fe8e964fd63885e85d9a8a83db206"}, - {file = "y_py-0.6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c28d977f516d4928f6bc0cd44561f6d0fdd661d76bac7cdc4b73e3c209441d9"}, - {file = "y_py-0.6.2-cp38-none-win32.whl", hash = "sha256:c011997f62d0c3b40a617e61b7faaaf6078e4eeff2e95ce4c45838db537816eb"}, - {file = "y_py-0.6.2-cp38-none-win_amd64.whl", hash = "sha256:ce0ae49879d10610cf3c40f4f376bb3cc425b18d939966ac63a2a9c73eb6f32a"}, - {file = "y_py-0.6.2-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:47fcc19158150dc4a6ae9a970c5bc12f40b0298a2b7d0c573a510a7b6bead3f3"}, - {file = "y_py-0.6.2-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:2d2b054a1a5f4004967532a4b82c6d1a45421ef2a5b41d35b6a8d41c7142aabe"}, - {file = "y_py-0.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0787e85645bb4986c27e271715bc5ce21bba428a17964e5ec527368ed64669bc"}, - {file = "y_py-0.6.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:17bce637a89f6e75f0013be68becac3e38dc082e7aefaf38935e89215f0aa64a"}, - {file = "y_py-0.6.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:beea5ad9bd9e56aa77a6583b6f4e347d66f1fe7b1a2cb196fff53b7634f9dc84"}, - {file = "y_py-0.6.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1dca48687f41efd862355e58b0aa31150586219324901dbea2989a506e291d4"}, - {file = "y_py-0.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17edd21eef863d230ea00004ebc6d582cc91d325e7132deb93f0a90eb368c855"}, - {file = "y_py-0.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:de9cfafe97c75cd3ea052a24cd4aabf9fb0cfc3c0f9f810f00121cdf123db9e4"}, - {file = "y_py-0.6.2-cp39-none-win32.whl", hash = "sha256:82f5ca62bedbf35aaf5a75d1f53b4457a1d9b6ff033497ca346e2a0cedf13d14"}, - {file = "y_py-0.6.2-cp39-none-win_amd64.whl", hash = "sha256:7227f232f2daf130ba786f6834548f2cfcfa45b7ec4f0d449e72560ac298186c"}, - {file = "y_py-0.6.2-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0649a41cd3c98e290c16592c082dbe42c7ffec747b596172eebcafb7fd8767b0"}, - {file = "y_py-0.6.2-pp38-pypy38_pp73-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:bf6020560584671e76375b7a0539e0d5388fc70fa183c99dc769895f7ef90233"}, - {file = "y_py-0.6.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf817a72ffec4295def5c5be615dd8f1e954cdf449d72ebac579ff427951328"}, - {file = "y_py-0.6.2-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c7302619fc962e53093ba4a94559281491c045c925e5c4defec5dac358e0568"}, - {file = "y_py-0.6.2-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cd6213c3cf2b9eee6f2c9867f198c39124c557f4b3b77d04a73f30fd1277a59"}, - {file = "y_py-0.6.2-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b4fac4ea2ce27b86d173ae45765ced7f159120687d4410bb6d0846cbdb170a3"}, - {file = "y_py-0.6.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:932abb560fe739416b50716a72ba6c6c20b219edded4389d1fc93266f3505d4b"}, - {file = "y_py-0.6.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e42258f66ad9f16d9b62e9c9642742982acb1f30b90f5061522048c1cb99814f"}, - {file = "y_py-0.6.2-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cfc8381df1f0f873da8969729974f90111cfb61a725ef0a2e0e6215408fe1217"}, - {file = "y_py-0.6.2-pp39-pypy39_pp73-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:613f83713714972886e81d71685403098a83ffdacf616f12344b52bc73705107"}, - {file = "y_py-0.6.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:316e5e1c40259d482883d1926fd33fa558dc87b2bd2ca53ce237a6fe8a34e473"}, - {file = "y_py-0.6.2-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:015f7f6c1ce8a83d57955d1dc7ddd57cb633ae00576741a4fc9a0f72ed70007d"}, - {file = "y_py-0.6.2-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff32548e45e45bf3280ac1d28b3148337a5c6714c28db23aeb0693e33eba257e"}, - {file = "y_py-0.6.2-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0f2d881f0f8bf5674f8fe4774a438c545501e40fa27320c73be4f22463af4b05"}, - {file = "y_py-0.6.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3bbe2f925cc587545c8d01587b4523177408edd252a32ce6d61b97113fe234d"}, - {file = "y_py-0.6.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f5c14d25611b263b876e9ada1701415a13c3e9f02ea397224fbe4ca9703992b"}, - {file = "y_py-0.6.2.tar.gz", hash = "sha256:4757a82a50406a0b3a333aa0122019a331bd6f16e49fed67dca423f928b3fd4d"}, -] - -[[package]] -name = "yahooquery" -version = "2.3.2" -description = "Python interface to unofficial Yahoo Finance API endpoints" -optional = false -python-versions = "*" -files = [ - {file = "yahooquery-2.3.2-py2.py3-none-any.whl", hash = "sha256:70365ddf5ad4ed1dd45cb79b623391728f8a5ebf25ee077ab9f25c2c2f4f5d07"}, - {file = "yahooquery-2.3.2.tar.gz", hash = "sha256:9a6ab53fa78611fa38927ee477c869f085aaedd21b5205f1e8a88f59869dc407"}, -] - -[package.dependencies] -lxml = ">=4.9.1" -pandas = ">=0.24.0" -requests-futures = ">=1.0.0" -tqdm = ">=4.54.1" - -[package.extras] -doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-jupyter (>=0.12.0,<0.13.0)", "mkdocs-material (>=6.1.7,<7.0.0)"] -premium = ["selenium (>=3.141.0)"] -test = ["coverage (==5.2.1)", "pytest (==6.0.2)", "pytest-cov (==2.10.1)", "selenium (>=3.141.0)"] - -[[package]] -name = "yarl" -version = "1.9.4" -description = "Yet another URL library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, - {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, - {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, - {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, - {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, - {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, - {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, - {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, - {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, - {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, - {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, - {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, - {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, - {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, - {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, - {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" - -[[package]] -name = "yfinance" -version = "0.2.38" -description = "Download market data from Yahoo! Finance API" -optional = false -python-versions = "*" -files = [ - {file = "yfinance-0.2.38-py2.py3-none-any.whl", hash = "sha256:07525cf84414272723a3e2b9d4c0a2898ddb60cc0828aa190de26664fac6f676"}, - {file = "yfinance-0.2.38.tar.gz", hash = "sha256:483eecae0743d829fc337f21d80da4612f5257d5c1f35570efc4a5e98e4401a7"}, -] - -[package.dependencies] -appdirs = ">=1.4.4" -beautifulsoup4 = ">=4.11.1" -frozendict = ">=2.3.4" -html5lib = ">=1.1" -lxml = ">=4.9.1" -multitasking = ">=0.0.7" -numpy = ">=1.16.5" -pandas = ">=1.3.0" -peewee = ">=3.16.2" -pytz = ">=2022.5" -requests = ">=2.31" - -[package.extras] -nospam = ["requests-cache (>=1.0)", "requests-ratelimiter (>=0.3.1)"] -repair = ["scipy (>=1.6.3)"] - -[[package]] -name = "ypy-websocket" -version = "0.8.4" -description = "WebSocket connector for Ypy" -optional = false -python-versions = ">=3.7" -files = [ - {file = "ypy_websocket-0.8.4-py3-none-any.whl", hash = "sha256:b1ba0dfcc9762f0ca168d2378062d3ca1299d39076b0f145d961359121042be5"}, - {file = "ypy_websocket-0.8.4.tar.gz", hash = "sha256:43a001473f5c8abcf182f603049cf305cbc855ad8deaa9dfa0f3b5a7cea9d0ff"}, -] - -[package.dependencies] -aiofiles = ">=22.1.0,<23" -aiosqlite = ">=0.17.0,<1" -y-py = ">=0.6.0,<0.7.0" - -[package.extras] -test = ["mypy", "pre-commit", "pytest", "pytest-asyncio", "websockets (>=10.0)"] - -[[package]] -name = "yt-dlp" -version = "2023.12.30" -description = "A youtube-dl fork with additional features and patches" -optional = true -python-versions = ">=3.8" -files = [ - {file = "yt-dlp-2023.12.30.tar.gz", hash = "sha256:a11862e57721b0a0f0883dfeb5a4d79ba213a2d4c45e1880e9fd70f8e6570c38"}, - {file = "yt_dlp-2023.12.30-py2.py3-none-any.whl", hash = "sha256:c00d9a71d64472ad441bcaa1ec0c3797d6e60c9f934f270096a96fe51657e7b3"}, -] - -[package.dependencies] -brotli = {version = "*", markers = "implementation_name == \"cpython\""} -brotlicffi = {version = "*", markers = "implementation_name != \"cpython\""} -certifi = "*" -mutagen = "*" -pycryptodomex = "*" -requests = ">=2.31.0,<3" -urllib3 = ">=1.26.17,<3" -websockets = ">=12.0" - -[[package]] -name = "zipp" -version = "3.18.1" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, - {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] - -[[package]] -name = "zope-interface" -version = "6.3" -description = "Interfaces for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zope.interface-6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f32010ffb87759c6a3ad1c65ed4d2e38e51f6b430a1ca11cee901ec2b42e021"}, - {file = "zope.interface-6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e78a183a3c2f555c2ad6aaa1ab572d1c435ba42f1dc3a7e8c82982306a19b785"}, - {file = "zope.interface-6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa0491a9f154cf8519a02026dc85a416192f4cb1efbbf32db4a173ba28b289a"}, - {file = "zope.interface-6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e32f02b3f26204d9c02c3539c802afc3eefb19d601a0987836ed126efb1f21"}, - {file = "zope.interface-6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c40df4aea777be321b7e68facb901bc67317e94b65d9ab20fb96e0eb3c0b60a1"}, - {file = "zope.interface-6.3-cp310-cp310-win_amd64.whl", hash = "sha256:46034be614d1f75f06e7dcfefba21d609b16b38c21fc912b01a99cb29e58febb"}, - {file = "zope.interface-6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39"}, - {file = "zope.interface-6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299"}, - {file = "zope.interface-6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130"}, - {file = "zope.interface-6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10"}, - {file = "zope.interface-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e"}, - {file = "zope.interface-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061"}, - {file = "zope.interface-6.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8d407e0fd8015f6d5dfad481309638e1968d70e6644e0753f229154667dd6cd5"}, - {file = "zope.interface-6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:72d5efecad16c619a97744a4f0b67ce1bcc88115aa82fcf1dc5be9bb403bcc0b"}, - {file = "zope.interface-6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:567d54c06306f9c5b6826190628d66753b9f2b0422f4c02d7c6d2b97ebf0a24e"}, - {file = "zope.interface-6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483e118b1e075f1819b3c6ace082b9d7d3a6a5eb14b2b375f1b80a0868117920"}, - {file = "zope.interface-6.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb78c12c1ad3a20c0d981a043d133299117b6854f2e14893b156979ed4e1d2c"}, - {file = "zope.interface-6.3-cp312-cp312-win_amd64.whl", hash = "sha256:ad4524289d8dbd6fb5aa17aedb18f5643e7d48358f42c007a5ee51a2afc2a7c5"}, - {file = "zope.interface-6.3-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:a56fe1261230093bfeedc1c1a6cd6f3ec568f9b07f031c9a09f46b201f793a85"}, - {file = "zope.interface-6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:014bb94fe6bf1786da1aa044eadf65bc6437bcb81c451592987e5be91e70a91e"}, - {file = "zope.interface-6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22e8a218e8e2d87d4d9342aa973b7915297a08efbebea5b25900c73e78ed468e"}, - {file = "zope.interface-6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f95bebd0afe86b2adc074df29edb6848fc4d474ff24075e2c263d698774e108d"}, - {file = "zope.interface-6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:d0e7321557c702bd92dac3c66a2f22b963155fdb4600133b6b29597f62b71b12"}, - {file = "zope.interface-6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:187f7900b63845dcdef1be320a523dbbdba94d89cae570edc2781eb55f8c2f86"}, - {file = "zope.interface-6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a058e6cf8d68a5a19cb5449f42a404f0d6c2778b897e6ce8fadda9cea308b1b0"}, - {file = "zope.interface-6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8fa0fb05083a1a4216b4b881fdefa71c5d9a106e9b094cd4399af6b52873e91"}, - {file = "zope.interface-6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a37fb395a703e39b11b00b9e921c48f82b6e32cc5851ad5d0618cd8876b5"}, - {file = "zope.interface-6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b0c4c90e5eefca2c3e045d9f9ed9f1e2cdbe70eb906bff6b247e17119ad89a1"}, - {file = "zope.interface-6.3-cp38-cp38-win_amd64.whl", hash = "sha256:5683aa8f2639016fd2b421df44301f10820e28a9b96382a6e438e5c6427253af"}, - {file = "zope.interface-6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2c3cfb272bcb83650e6695d49ae0d14dd06dc694789a3d929f23758557a23d92"}, - {file = "zope.interface-6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:01a0b3dd012f584afcf03ed814bce0fc40ed10e47396578621509ac031be98bf"}, - {file = "zope.interface-6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4137025731e824eee8d263b20682b28a0bdc0508de9c11d6c6be54163e5b7c83"}, - {file = "zope.interface-6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c8731596198198746f7ce2a4487a0edcbc9ea5e5918f0ab23c4859bce56055c"}, - {file = "zope.interface-6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf34840e102d1d0b2d39b1465918d90b312b1119552cebb61a242c42079817b9"}, - {file = "zope.interface-6.3-cp39-cp39-win_amd64.whl", hash = "sha256:a1adc14a2a9d5e95f76df625a9b39f4709267a483962a572e3f3001ef90ea6e6"}, - {file = "zope.interface-6.3.tar.gz", hash = "sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a"}, -] - -[package.dependencies] -setuptools = "*" - -[package.extras] -docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx-rtd-theme"] -test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] -testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] - -[extras] -all = ["Riskfolio-Lib", "lightgbm", "openai-whisper", "pytorch-lightning", "setuptools-rust", "torch", "transformers", "u8darts", "yt-dlp"] -doc = ["docstring-parser"] -forecast = ["lightgbm", "openai-whisper", "pytorch-lightning", "setuptools-rust", "torch", "transformers", "u8darts", "yt-dlp"] -installer = ["pyinstaller"] -jupyterlab = ["jedi-language-server", "jupyterlab-code-formatter", "jupyterlab-lsp"] -optimization = ["Riskfolio-Lib"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.8.1,<3.11, !=3.9.7" -content-hash = "ca00fd6172178039e6d7a3502e8e9b873f897a8ebf7bffde9b8adfdcad6f7234" diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 165dad84e58..00000000000 --- a/pyproject.toml +++ /dev/null @@ -1,252 +0,0 @@ -[tool.poetry] -name = "openbb-terminal" -version = "3.2.5" -description = "Investment Research for Everyone, Anywhere." -license = "MIT" -authors = ["OpenBB "] -packages = [{ include = "openbb_terminal" }] -include = ["terminal.py", "openbb_terminal/.env"] -readme = "website/pypi.md" -homepage = "https://openbb.co" -repository = "https://github.com/OpenBB-finance/OpenBBTerminal" -documentation = "https://docs.openbb.co/sdk" - -[tool.poetry.scripts] -openbb = 'terminal:main' - -[tool.poetry.dependencies] -python = "^3.8.1,<3.11, !=3.9.7" -scipy = "1.10.1" #Pinning on July 6 for pip issues -iso8601 = "^0.1.14" -FundamentalAnalysis = "^0.2.6" -requests = "^2.31.0" -alpha-vantage = "^2.3.1" -finviz = "^1.4.6" -bs4 = "^0.0.1" -yfinance = "^0.2.28" -pmaw = "^3.0.0" -praw = "^7.1.4" -Quandl = "^3.6.0" -pytrends = "4.9.2" -matplotlib = "^3.3.3" -plotly = "^5.15.0" -prompt-toolkit = "^3.0.16" -jupyterlab = "^3.2.4" -mplfinance = "^0.12.7-alpha.7" -seaborn = "^0.11.0" -fredapi = "^0.4.3" -screeninfo = "^0.6.7" -pycoingecko = "^3.1.0" -detecta = "^0.0.5" -tradingview-ta = "^3.2.3" -finvizfinance = "0.14.7rc1" -statsmodels = "^0.13" -python-coinmarketcap = "^0.2" -valinvest = "^0.0.2" -bt = "^0.2.9" -GitPython = "^3.1.17" -fred = "^3.1" -financedatabase = "2.1.0" -holidays = "^0.14.2" -vaderSentiment = "3.3.2" -python-binance = "^1.0.15" -python-i18n = "^0.3.9" -pandas-ta = "^0.3.14-beta.0" -pyrsistent = "^0.19.3" -openpyxl = "^3.0.9" -pandas = "^1.5.0" -pandas-market-calendars = "~3.2" -ipympl = "^0.8.2" -rich = "^12.6.0" -dnspython = "^2.1.0" -python-dotenv = "^1.0.0" -Pygments = "^2.11.2" -ascii-magic = "^1.6" -squarify = "^0.4.3" -finnhub-python = "^2.4.10" -linearmodels = "^4.25" -ipywidgets = "^8.0.2" -Jinja2 = "^3.0.3" -Riskfolio-Lib = { version = "^3.1.1", optional = true } -ccxt = "^4.0.33" -html5lib = "^1.1" -feedparser = "^6.0.10" -pyinstaller = { version = "^4.10", optional = true } -jupyterlab-code-formatter = { version = "^1.4.10", optional = true } -jupyterlab-lsp = { version = "^3.10.1", optional = true } -jedi-language-server = { version = "^0.40.0", optional = true } -tokenterminal = "^1.0.1" -torch = { version = "~1.11.0", optional = true } -watchdog = "^2.1.9" -pythclient = "^0.1.2" -"ruamel.yaml" = "^0.17.21" -setuptools = "<65.5.0" -numpy = "1.23.4" -papermill = "2.4" -ipython = "8.11.0" -protobuf = "3.20.1" -pytorch-lightning = { version = "1.6.5", optional = true } -u8darts = { extras = ["torch"], version = "0.23.0", optional = true } -lightgbm = { version = "3.3.5", optional = true } # locked because this is the latest version available on conda intel mac -docstring-parser = { version = "^0.15", optional = true } -grpcio = "^1.51.1" -jupyterlab-widgets = "^3.0.3" -mstarpy = "^0.0.4" -packaging = ">=22.0" -streamlit = "^1.24.1" -pywry = "^0.6.1" -svglib = "^1.5.0" -sparqlwrapper = "^2.0.0" -y-py = "!=0.5.5" # Untraceable third level dependency that requires cargo to be built -psutil = "!=5.9.4" # Forecasting menu and IPyKernel dependency broken on MacOs -intrinio-sdk = "^6.22.2" -yahooquery = "^2.3.0" -openai-whisper = { version = "^20230124", optional = true } -setuptools-rust = { version = "^1.5.2", optional = true } -transformers = { version = "4.27.1", optional = true } -yt-dlp = { version = "^2023.2.17", optional = true } -pydantic = "<2" -posthog = "^3.0.1" -reportlab = "<4" #Pinning due to build error on MacOs -arch = "^5.5.0" -ffn = "0.3.6" -jupyter-client = "7.4.1" -jupyter-server = "1.23.6" -python-jose = "^3.3.0" -nixtlats = "^0.1.11" # Pinning for stability -nltk = "^3.8.1" # Is a dependence for llama index - - -[tool.poetry.extras] -forecast = [ - "torch", - "pytorch-lightning", - "u8darts", - "lightgbm", - "openai-whisper", - "setuptools-rust", - "transformers", - "yt-dlp", -] -optimization = ["Riskfolio-Lib"] -all = [ - "torch", - "pytorch-lightning", - "u8darts", - "Riskfolio-Lib", - "lightgbm", - "openai-whisper", - "setuptools-rust", - "transformers", - "yt-dlp", -] -jupyterlab = [ - "jupyterlab-code-formatter", - "jupyterlab-lsp", - "jedi-language-server", -] -installer = ["pyinstaller"] -doc = ["docstring-parser"] - -[tool.poetry.group.dev.dependencies] -pytest-xdist = "^3.2.0" -pytest = "^6.2.2" -pylint = "2.17.0" -mock = "^4.0.3" -codespell = "^2.2.4" -pydocstyle = "^6.3.0" -sphinx = "^4.5.0" -myst-parser = "^0.15.2" -black = "^24.1.0" -bandit = "^1.7.0" -coverage = ">=5.5" -vcrpy = "^4.1.1" -pytest-mock = "^3.6.1" -pytest-recording = "^0.12.0" -types-pytz = "^2021.3.1" -types-requests = "^2.26.0" -types-PyYAML = "^6.0.1" -types-python-dateutil = "^2.8.3" -types-setuptools = "^57.4.7" -pre-commit = "^2.16.0" -pytest-cov = "^3.0.0" -types-six = "^1.16.12" -nbmake = ">0.5.0" -pytest-timeout = "^2.1.0" -pytest-recorder = "^0.2.1" -freezegun = "^1.2.2" -ruff = "^0.0.285" -mypy = "^1.5.1" -toml = "^0.10.2" -tomli = "^2.0.1" - -[build-system] -requires = ["setuptools<65.5.0", "poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - -[tool.pydocstyle] -inherit = true -convention = "numpy" -match = '((?!test_).)*\.py' -add-ignore = "D401" - -[tool.flake8] -max-line-length = 122 -ignore = "S101,W503" - -[tool.ruff] -line-length = 122 -target-version = "py38" -# This is an introductory addition of ruff. We should look to adding: -# D: pydocstyle, PD: pandas-vet -# All options here: https://github.com/charliermarsh/ruff#supported-rules -select = [ - "E", - "W", - "F", - "Q", - "S", - "UP", - "I", - "PLC", - "PLE", - "PLR", - "PLW", - "SIM", - "T20", -] -# These ignores should be seen as temporary solutions to problems that will NEED fixed -ignore = ["PLR2004", "PLR0913", "PLR0915"] -exclude = [ - "^openbb_platform/platform/core/openbb_core/app/static/package/.*", - "^openbb_platform/openbb/package/.*", -] - -[tool.ruff.per-file-ignores] -"**/tests/*" = ["S101"] -"*init*.py" = ["F401"] -"website/*" = ["T201"] -"*integration/*" = ["S101"] - -[tool.ruff.flake8-import-conventions.aliases] -"matplotlib.pyplot" = "plt" -numpy = "np" -pandas = "pd" -seaborn = "sns" - -[tool.ruff.isort] -combine-as-imports = true -force-wrap-aliases = true - -[tool.ruff.pylint] -max-args = 8 -max-branches = 25 -max-returns = 9 -max-statements = 30 - -[tool.isort] -profile = "black" -line_length = 122 -skip_gitignore = true -combine_as_imports = true -src_paths = ["openbb_terminal"] diff --git a/requirements-full.txt b/requirements-full.txt deleted file mode 100644 index 5a1d69fdad6..00000000000 --- a/requirements-full.txt +++ /dev/null @@ -1,429 +0,0 @@ -absl-py==1.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -adagio==0.2.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiodns==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiofiles==22.1.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -aiohttp==3.8.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiosignal==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiosqlite==0.19.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -alabaster==0.7.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -alpha-vantage==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -altair==5.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -altgraph==0.17.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ansi2html==1.8.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ansiwrap==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -antlr4-python3-runtime==4.11.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -anyio==3.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -appdirs==1.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -appnope==0.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and (sys_platform == "darwin" or platform_system == "Darwin") -arch==5.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -argon2-cffi-bindings==21.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -argon2-cffi==21.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -arrow==1.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ascii-magic==1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -astor==0.8.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -astroid==2.15.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -astropy==5.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -asttokens==2.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -async-timeout==4.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -atomicwrites==1.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "win32" -attrs==21.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -babel==2.12.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -backcall==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -backoff==2.2.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -backports-zoneinfo==0.2.1 ; python_full_version >= "3.8.1" and python_version < "3.9" -bandit==1.7.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -base58==2.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -beautifulsoup4==4.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -black==23.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bleach==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -blinker==1.6.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -brotli==1.0.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and platform_python_implementation == "CPython" -brotlicffi==1.0.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and platform_python_implementation != "CPython" -bs4==0.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bt==0.2.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cachetools==5.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -catboost==1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ccxt==4.0.33 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -certifi==2023.5.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cffi==1.15.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cfgv==3.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -charset-normalizer==3.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -click==8.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cloudpickle==2.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cmdstanpy==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -codespell==2.2.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -comm==0.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -commonmark==0.9.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -contourpy==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -convertdate==2.4.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -coverage==7.2.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -coverage[toml]==7.2.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cryptography==41.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cssselect2==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cssselect==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cvxpy==1.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cycler==0.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cython==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dash-core-components==2.0.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dash-html-components==2.0.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dash-table==5.0.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dash==2.9.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -dateparser==1.1.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -datetime==5.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -debugpy==1.6.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -decorator==5.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -defusedxml==0.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -detecta==0.0.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dill==0.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -distlib==0.3.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dnspython==2.4.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -docutils==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ecdsa==0.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ecos==2.0.12 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -entrypoints==0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ephem==4.1.4 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -et-xmlfile==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -exceptiongroup==1.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -exchange-calendars==4.2.8 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -execnet==2.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -executing==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fastjsonschema==2.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -feedparser==6.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ffmpeg-python==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ffn==0.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -filelock==3.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -financedatabase==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finnhub-python==2.4.18 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finviz==1.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finvizfinance==0.14.7rc1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -flake8==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -flask==2.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -fonttools==4.41.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -formulaic==0.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fqdn==1.5.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -fred==3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fredapi==0.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -freezegun==1.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -frozendict==2.3.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -frozenlist==1.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fs==2.4.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fsspec==2023.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fsspec[http]==2023.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fugue-sql-antlr==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fugue==0.8.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fundamentalanalysis==0.2.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -future==0.18.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -gitdb==4.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -gitpython==3.1.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -google-auth-oauthlib==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -google-auth==2.22.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -graphlib-backport==1.0.3 ; python_full_version >= "3.8.1" and python_version < "3.9" -graphviz==0.20.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -grpcio==1.56.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -h11==0.14.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -hijri-converter==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -holidays==0.14.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -html5lib==1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -httpcore==0.17.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -httpx==0.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -huggingface-hub==0.16.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -identify==2.5.25 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -idna==3.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -imagesize==1.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -importlib-metadata==6.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -importlib-resources==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -inflection==0.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -iniconfig==2.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -interface-meta==1.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -intrinio-sdk==6.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipykernel==6.24.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipympl==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipython-genutils==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipython==8.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipywidgets==8.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -iso8601==0.1.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -isodate==0.6.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -isoduration==20.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -isort==5.12.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -itsdangerous==2.1.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -jedi==0.18.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jinja2==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -joblib==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -json5==0.9.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonpointer==2.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonschema==4.17.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonschema[format-nongpl]==4.17.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-client==7.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-core==5.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-dash==0.4.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -jupyter-events==0.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server-fileid==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server-ydoc==0.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server==1.23.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-ydoc==0.2.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-pygments==0.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-server==2.23.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-widgets==3.0.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab==3.6.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -kiwisolver==1.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -korean-lunar-calendar==0.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lazy-object-proxy==1.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lightgbm==3.3.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lightning-utilities==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -linearmodels==4.31 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -llvmlite==0.40.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -loguru==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lunarcalendar==0.0.9 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -lxml==4.9.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -macholib==1.16.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" and sys_platform == "darwin" -markdown-it-py==1.1.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -markdown==3.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -markupsafe==2.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -matplotlib-inline==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -matplotlib==3.7.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mccabe==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mdit-py-plugins==0.2.8 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -mistune==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mock==4.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -monotonic==1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -more-itertools==9.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mplfinance==0.12.9b7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mstarpy==0.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -multidict==6.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -multitasking==0.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mutagen==1.46.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mypy-extensions==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mypy==1.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -myst-parser==0.15.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbclassic==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbclient==0.6.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbconvert==7.7.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbformat==5.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbmake==1.4.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -nest-asyncio==1.5.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -networkx==3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nfoursid==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nixtlats==0.1.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nltk==3.8.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nodeenv==1.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -notebook-shim==0.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -notebook==6.5.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -numba==0.57.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -numpy==1.23.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -oauthlib==3.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -openai-whisper==20230124 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -openpyxl==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -orjson==3.9.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -osqp==0.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -packaging==23.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-datareader==0.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-market-calendars==3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-ta==0.3.14b ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas==1.5.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandocfilters==1.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -papermill==2.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -parso==0.8.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pathspec==0.11.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -patsy==0.5.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pbr==5.11.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -peewee==3.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pefile==2023.2.7 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" and sys_platform == "win32" -pexpect==4.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform != "win32" -pickleshare==0.7.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pillow==9.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pkgutil-resolve-name==1.3.10 ; python_full_version >= "3.8.1" and python_version < "3.9" -platformdirs==3.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -plotly-resampler==0.8.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -plotly==5.15.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pluggy==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pmaw==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pmdarima==2.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -posthog==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -praw==7.7.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -prawcore==2.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pre-commit==2.21.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -prometheus-client==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -prompt-toolkit==3.0.39 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -property-cached==1.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -prophet==1.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -protobuf==3.20.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -psutil==5.9.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ptyprocess==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and (sys_platform != "win32" or os_name != "nt") -pure-eval==0.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -py==1.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyarrow==12.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyasn1-modules==0.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyasn1==0.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycares==4.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycodestyle==2.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycoingecko==3.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycparser==2.21 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycryptodome==3.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycryptodomex==3.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydantic==1.10.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydeck==0.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydeprecate==0.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydocstyle==6.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyerfa==2.0.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyflakes==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pygments==2.15.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyhdfe==0.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyinstaller-hooks-contrib==2023.6 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pyinstaller==4.10 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pylint==2.17.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyluach==2.2.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pymeeus==0.5.12 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pympler==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyobjc-core==9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" -pyobjc-framework-cocoa==9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" -pyod==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyparsing==3.0.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyprind==2.11.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyrsistent==0.19.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-cov==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-mock==3.11.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-recorder==0.2.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pytest-recording==0.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-timeout==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest-xdist==3.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytest==6.2.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pythclient==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-binance==1.0.17 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-coinmarketcap==0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-dateutil==2.8.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-dotenv==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-i18n==0.3.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-jose==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-json-logger==2.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytorch-lightning==1.6.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytrends==4.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytz-deprecation-shim==0.1.0.post0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytz==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pywin32-ctypes==0.2.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" and sys_platform == "win32" -pywin32==306 ; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pywinpty==2.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and os_name == "nt" -pywry==0.6.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyzmq==25.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -qdldl==0.1.7.post0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -qpd==0.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -quandl==3.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rdflib==6.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -regex==2023.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -reportlab==3.6.13 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -requests-futures==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -requests-oauthlib==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -requests==2.31.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -retrying==1.3.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rfc3339-validator==0.1.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rfc3986-validator==0.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rich==12.6.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -riskfolio-lib==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rsa==4.9 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ruamel-yaml-clib==0.2.7 ; platform_python_implementation == "CPython" and python_version < "3.11" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" -ruamel-yaml==0.17.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ruff==0.0.285 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scikit-learn==1.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scipy==1.10.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -screeninfo==0.6.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scs==3.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -seaborn==0.11.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -semantic-version==2.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -send2trash==1.8.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setproctitle==1.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools-rust==1.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools-scm[toml]==7.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools==65.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sgmllib3k==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -shap==0.42.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -six==1.16.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -slicer==0.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -smmap==5.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sniffio==1.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -snowballstemmer==2.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -soupsieve==2.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sparqlwrapper==2.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinx==4.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-applehelp==1.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-devhelp==1.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-htmlhelp==2.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-jsmath==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-qthelp==1.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sphinxcontrib-serializinghtml==1.1.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sqlglot==17.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -squarify==0.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -stack-data==0.6.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -statsforecast==1.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -statsmodels==0.13.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -stevedore==5.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -streamlit==1.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -svglib==1.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tabulate==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tbats==1.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tenacity==8.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tensorboard-data-server==0.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tensorboard==2.13.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -terminado==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -textwrap3==0.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -threadpoolctl==3.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -time-machine==2.11.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -tinycss2==1.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tokenizers==0.13.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tokenterminal==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -toml==0.10.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tomli==2.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tomlkit==0.11.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -toolz==0.12.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -torch==1.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -torchmetrics==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tornado==6.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tqdm==4.65.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -trace-updater==0.0.9.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -tradingview-ta==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -traitlets==5.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -transformers==4.27.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -triad==0.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-python-dateutil==2.8.19.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-pytz==2021.3.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-pyyaml==6.0.12.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-requests==2.31.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-setuptools==57.4.18 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-six==1.16.21.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -types-urllib3==1.26.25.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -typing-extensions==4.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tzdata==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tzlocal==4.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -u8darts[torch]==0.23.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ujson==5.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -update-checker==0.18.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -uri-template==1.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -urllib3==1.26.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -user-agent==0.1.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -vadersentiment==3.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -validators==0.20.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -valinvest==0.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -vcrpy==4.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -virtualenv==20.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -watchdog==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -wcwidth==0.2.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -webcolors==1.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -webencodings==0.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -websocket-client==1.6.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -websockets==11.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -werkzeug==2.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -wheel==0.40.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -widgetsnbextension==4.0.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -win32-setctime==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "win32" -wrapt==1.15.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -xarray==2023.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -xgboost==1.7.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -xlsxwriter==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -y-py==0.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yahooquery==2.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yarl==1.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yfinance==0.2.36 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ypy-websocket==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yt-dlp==2023.7.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -zipp==3.16.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -zope-interface==6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 4573b208fe5..00000000000 --- a/requirements.txt +++ /dev/null @@ -1,282 +0,0 @@ -aiodns==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiofiles==22.1.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -aiohttp==3.8.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiosignal==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -aiosqlite==0.19.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -alpha-vantage==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -altair==5.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ansiwrap==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -anyio==3.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -appdirs==1.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -appnope==0.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and (sys_platform == "darwin" or platform_system == "Darwin") -arch==5.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -argon2-cffi-bindings==21.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -argon2-cffi==21.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -arrow==1.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ascii-magic==1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -astor==0.8.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -asttokens==2.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -async-timeout==4.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -attrs==21.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -babel==2.12.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -backcall==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -backoff==2.2.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -backports-zoneinfo==0.2.1 ; python_full_version >= "3.8.1" and python_version < "3.9" -base58==2.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -beautifulsoup4==4.12.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bleach==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -blinker==1.6.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bs4==0.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -bt==0.2.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cachetools==5.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ccxt==4.0.33 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -certifi==2023.5.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cffi==1.15.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -charset-normalizer==3.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -click==8.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -comm==0.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -commonmark==0.9.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -contourpy==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -convertdate==2.4.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -cryptography==41.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cssselect2==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cssselect==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cycler==0.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -cython==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dateparser==1.1.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -datetime==5.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -debugpy==1.6.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -decorator==5.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -defusedxml==0.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -detecta==0.0.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -dnspython==2.4.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ecdsa==0.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -entrypoints==0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -et-xmlfile==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -exceptiongroup==1.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -exchange-calendars==4.2.8 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -executing==1.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fastjsonschema==2.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -feedparser==6.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ffn==0.3.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -financedatabase==2.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finnhub-python==2.4.18 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finviz==1.4.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -finvizfinance==0.14.7rc1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -flake8==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fonttools==4.41.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -formulaic==0.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fqdn==1.5.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -fred==3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fredapi==0.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -frozendict==2.3.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -frozenlist==1.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -fundamentalanalysis==0.2.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -future==0.18.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -gitdb==4.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -gitpython==3.1.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -graphlib-backport==1.0.3 ; python_full_version >= "3.8.1" and python_version < "3.9" -grpcio==1.56.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -h11==0.14.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -hijri-converter==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -holidays==0.14.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -html5lib==1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -httpcore==0.17.3 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -httpx==0.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -idna==3.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -importlib-metadata==6.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -importlib-resources==6.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.10" -inflection==0.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -interface-meta==1.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -intrinio-sdk==6.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipykernel==6.24.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipympl==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipython-genutils==0.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipython==8.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ipywidgets==8.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -iso8601==0.1.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -isodate==0.6.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -isoduration==20.11.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jedi==0.18.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jinja2==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -joblib==1.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -json5==0.9.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonpointer==2.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonschema==4.17.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jsonschema[format-nongpl]==4.17.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-client==7.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-core==5.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-events==0.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server-fileid==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server-ydoc==0.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-server==1.23.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyter-ydoc==0.2.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-pygments==0.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-server==2.23.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab-widgets==3.0.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -jupyterlab==3.6.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -kiwisolver==1.4.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -korean-lunar-calendar==0.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -linearmodels==4.31 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -loguru==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -lxml==4.9.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -markupsafe==2.1.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -matplotlib-inline==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -matplotlib==3.7.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mccabe==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mistune==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -monotonic==1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -more-itertools==9.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mplfinance==0.12.9b7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mstarpy==0.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -multidict==6.0.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -multitasking==0.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -mypy-extensions==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbclassic==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbclient==0.6.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbconvert==7.7.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nbformat==5.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nest-asyncio==1.5.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nixtlats==0.1.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -nltk==3.8.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -notebook-shim==0.2.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -notebook==6.5.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -numpy==1.23.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -openpyxl==3.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -packaging==23.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-datareader==0.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-market-calendars==3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas-ta==0.3.14b ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandas==1.5.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pandocfilters==1.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -papermill==2.4.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -parso==0.8.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -patsy==0.5.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -peewee==3.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pexpect==4.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform != "win32" -pickleshare==0.7.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pillow==9.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pkgutil-resolve-name==1.3.10 ; python_full_version >= "3.8.1" and python_version < "3.9" -platformdirs==3.9.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -plotly==5.15.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pmaw==3.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -posthog==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -praw==7.7.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -prawcore==2.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -prometheus-client==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -prompt-toolkit==3.0.39 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -property-cached==1.6.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -protobuf==3.20.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -psutil==5.9.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ptyprocess==0.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and (sys_platform != "win32" or os_name != "nt") -pure-eval==0.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyarrow==12.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyasn1==0.5.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycares==4.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycodestyle==2.10.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycoingecko==3.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycparser==2.21 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pycryptodome==3.18.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydantic==1.10.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pydeck==0.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyflakes==3.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pygments==2.15.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyhdfe==0.1.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyluach==2.2.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pymeeus==0.5.12 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -pympler==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyobjc-core==9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" -pyobjc-framework-cocoa==9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "darwin" -pyparsing==3.0.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyprind==2.11.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyrsistent==0.19.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pythclient==0.1.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-binance==1.0.17 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-coinmarketcap==0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-dateutil==2.8.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-dotenv==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-i18n==0.3.9 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-jose==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -python-json-logger==2.0.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytrends==4.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytz-deprecation-shim==0.1.0.post0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pytz==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pywin32==306 ; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pywinpty==2.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and os_name == "nt" -pywry==0.6.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -pyzmq==25.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -quandl==3.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rdflib==6.3.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -regex==2023.6.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -reportlab==3.6.13 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -requests-futures==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -requests==2.31.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -retrying==1.3.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rfc3339-validator==0.1.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rfc3986-validator==0.1.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -rich==12.6.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -rsa==4.9 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -ruamel-yaml-clib==0.2.7 ; platform_python_implementation == "CPython" and python_version < "3.11" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" -ruamel-yaml==0.17.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scikit-learn==1.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -scipy==1.10.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -screeninfo==0.6.7 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -seaborn==0.11.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -send2trash==1.8.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setproctitle==1.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools-scm[toml]==7.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -setuptools==65.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sgmllib3k==1.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -six==1.16.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -smmap==5.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sniffio==1.3.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -soupsieve==2.4.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -sparqlwrapper==2.0.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -squarify==0.4.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -stack-data==0.6.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -statsmodels==0.13.5 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -streamlit==1.24.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -svglib==1.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tabulate==0.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tenacity==8.2.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -terminado==0.17.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -textwrap3==0.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -threadpoolctl==3.2.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tinycss2==1.2.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tokenterminal==1.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -toml==0.10.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tomli==2.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -toolz==0.12.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -tornado==6.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tqdm==4.65.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tradingview-ta==3.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -traitlets==5.9.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -typing-extensions==4.7.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tzdata==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -tzlocal==4.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ujson==5.8.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -update-checker==0.18.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -uri-template==1.3.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -urllib3==1.26.16 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -user-agent==0.1.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -vadersentiment==3.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -validators==0.20.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -valinvest==0.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -watchdog==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -wcwidth==0.2.6 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -webcolors==1.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -webencodings==0.5.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -websocket-client==1.6.1 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" -websockets==11.0.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -widgetsnbextension==4.0.8 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -win32-setctime==1.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and sys_platform == "win32" -wrapt==1.15.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -y-py==0.6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yahooquery==2.3.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yarl==1.9.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -yfinance==0.2.36 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -ypy-websocket==0.8.4 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -zipp==3.16.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" -zope-interface==6.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" diff --git a/terminal.py b/terminal.py deleted file mode 100644 index 070052e594e..00000000000 --- a/terminal.py +++ /dev/null @@ -1,48 +0,0 @@ -import sys -import warnings -from multiprocessing import freeze_support - -# pylint:disable=wrong-import-position -warnings.filterwarnings("ignore", category=FutureWarning, module="yfinance.*") -warnings.filterwarnings("ignore", category=FutureWarning, module="pandas.*") -import openbb_terminal.config_terminal as cfg # noqa: E402 - -# pylint:disable=unused-import,import-outside-toplevel -import openbb_terminal.core.session.current_system as syst # noqa: F401,E402 -from openbb_terminal.terminal_helper import ( # noqa: E402 - hide_splashscreen, - is_auth_enabled, - is_installer, -) - - -def main(): - sent_args = sys.argv[1:] - cfg.setup_config_terminal() - - if "--streamlit" in sent_args: - from openbb_terminal.dashboards import streamlit_run - - hide_splashscreen() - sys.exit(streamlit_run.main()) - - if "-t" in sent_args or "--test" in sent_args: - from openbb_terminal.core.integration_tests import integration_controller - - integration_controller.main() - else: - from openbb_terminal.core.session import session_controller - - prompt_login = ( - is_auth_enabled() - and ("--login" in sys.argv[1:] or is_installer()) - and sys.stdin.isatty() - ) - dev = "--dev" in sys.argv[1:] - - session_controller.main(prompt=prompt_login, dev=dev) - - -if __name__ == "__main__": - freeze_support() - main() diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 645a18e1537..00000000000 --- a/tests/README.md +++ /dev/null @@ -1,690 +0,0 @@ -# 1. OpenBB Terminal Unit Testing - -This document is an annex of the OpenBB Terminal Contributing Guidelines. - -It provide the necessary information to build, run and maintain Unit Tests for OpenBB Terminal. - -## 1.1. Unit Tests - -Unit Tests are early filters, having them working doesn't mean the function works. - -But it means the function will work with the most common use cases : the tested ones. - -## 1.2. Rules - -Here are few rules to follow when you write Unit Tests for OpenBB Terminal. - -These rules are these to avoiding having one Contributor work negatively impact another Contributor. - -Thus keeping a good Contributor experience. - -**TIMEBOX** - -Unit Tests mustn't be an impediment to feature building. - -The following elements should be timeboxed: -- Building time -- Execution time - -**BUILDING** - -Try to cover: -- the most common cases function parameters. -- check if the return type is correct - -Only if times allow and it's convenient enough, we can: -- go deeper on function parameters : testing more cases -- have a thorough check of the returned variable - -**EXECUTION** - -We must be able to execute Unit Tests often, so they need to be fast to run. - -Slow Unit Tests will discourage Developers to run tests, thus negatively impact all the Unit Tests. - -If an Unit Test if not fast enough, either: -- Refactor it -- Disable it by default and mark them as `slow` -- Remote it - -## 1.3. Table of contents - -- [1. OpenBB Terminal Unit Testing](#1-openbb-terminal-unit-testing) - - [1.1. Unit Tests](#11-unit-tests) - - [1.2. Rules](#12-rules) - - [1.3. Table of contents](#13-table-of-contents) -- [2. Run `unit tests`](#2-run-unit-tests) - - [2.1. How to install tests dependencies?](#21-how-to-install-tests-dependencies) - - [2.2. How to run `tests`:](#22-how-to-run-tests) - - [By `module`](#by-module) - - [By test `name`](#by-test-name) - - [By `markers`](#by-markers) - - [2.3. How to skip tests](#23-how-to-skip-tests) - - [Skipping a specific test](#skipping-a-specific-test) - - [Skipping the entire test module](#skipping-the-entire-test-module) -- [3. How to build `unit tests`](#3-how-to-build-unit-tests) - - [3.1. Model Tests](#31-model-tests) - - [3.2. View Tests](#32-view-tests) - - [3.3. Controller Tests](#33-controller-tests) - - [Test print help](#test-print-help) - - [Test call function](#test-call-function) - - [3.4. Fixing existing tests](#34-fixing-existing-tests) - - [Scenario 1: Update Fixtures](#scenario-1-update-fixtures) - - [Scenario 2: Update both Fixtures and Cassettes](#scenario-2-update-both-fixtures-and-cassettes) - - [3.5. How to `mock`?](#35-how-to-mock) - - [PYTEST-MOCK](#pytest-mock) - - [MONCKEYPATCH](#monckeypatch) - - [3.6. Which `helpers` are available?](#36-which-helpers-are-available) - - [3.7. Which `fixtures` are available ?](#37-which-fixtures-are-available-) - - [3.8. Which `markers` are available ?](#38-which-markers-are-available-) - - [3.9. Known `issue` / `solution`](#39-known-issue--solution) - - [ION Usage](#ion-usage) - - [YFINANCE - example 1](#yfinance---example-1) - - [YFINANCE - example 2](#yfinance---example-2) - - [USER-AGENT](#user-agent) - - [BROTLI](#brotli) - - [BEFORE\_RECORD\_RESPONSE](#before_record_response) - - [3.10. List of useful `vscode` tools for `unit tests`](#310-list-of-useful-vscode-tools-for-unit-tests) - - [Testing on VSCODE](#testing-on-vscode) - - [3.11. How to handle `dev-dependencies` ?](#311-how-to-handle-dev-dependencies-) - - [UPDATE PYPROJECT](#update-pyproject) - - [EXPORT REQUIREMENTS](#export-requirements) -- [4. Maintain `unit tests`](#4-maintain-unit-tests) - - [4.1. What's the PR process?](#41-whats-the-pr-process) - - [4.1.1 Find right place](#411-find-right-place) - - [4.1.2 Verify coverage is above 90%](#412-verify-coverage-is-above-90) - - [4.1.3 Set the right markers](#413-set-the-right-markers) - - [4.2. Which automations are available ?](#42-which-automations-are-available-) - - [PULL REQUEST](#pull-request) - - [4.3. How to check code coverage ?](#43-how-to-check-code-coverage-) - - [PULL REQUEST AUTOMATION](#pull-request-automation) - - [MANUALLY](#manually) - - [PRE-COMMIT](#pre-commit) - - - -## 2. Run `unit tests` - -In this section we will explain everything you need to run the `unit tests` on the `OpenBB Terminal`. - -## 2.1. How to install tests dependencies? - -To run the tests you will need first to install the `dev-dependencies`. By default poetry installs the `dev-dependencies` when you run this command: - -```bash -poetry install -``` - -If you do not want to install the `dev-dependencies` you will have to add the `--no-dev` option: - -```bash -poetry install --no-dev -``` - -### 2.2. How to run `tests`: - -#### By `module` - -You can run tests on a specific package/module by specifying the path of this package/module, as follows: - -```bash -pytest tests/openbb_terminal/some_package -pytest tests/openbb_terminal/some_package/some_module.py -``` - -#### By test `name` - -You can run tests by their name with the argument `-k` - -```bash -pytest tests/openbb_terminal/some_package/some_module.py -k test_function1 -``` - -### By `markers` - -You can run tests only on specific markers, like this : - -```bash -pytest -m slow -pytest -m "not slow" -pytest -m "not slow and not network" -``` - -You can list the available markers using this command : - -```bash -pytest --markers -``` - -## 2.3. How to skip tests - -### Skipping a specific test - -You can use the decorator `@pytest.mark.skip` as below: - -```python -import pytest - -@pytest.mark.skip -def test_some_function(mocker): - pass - -@pytest.mark.skip(reason="This time with a comment") -def test_another_function(mocker): - pass -``` - -### Skipping the entire test module - -```python -import pytest - -pytest.skip(msg="Some optional comment.", allow_module_level=True) - -def test_some_function(mocker): - pass -``` - -## 3. How to build `unit tests` - -When you contribute a new feature to the OpenBB Terminal, it's important that tests are added for this particular feature since it is a part of the Checklist for the PR to be approved. - -All the `unit tests` should be insides the `tests` folder. There should be at most one `test module` for each `module` of the `OpenBB Terminal`. - -Each `test module` should follow the same path of the `module` that it is `testing`. For instance, - -- in order to test the following module `openbb_terminal/stocks/due_diligence/dd_controller.py` -- a `test module` should be added here: `tests/openbb_terminal/stocks/due_diligence/test_dd_controller.py` - -Now that you know where to add tests, let's go through the different types of tests that you can add. - -### 3.1. Model Tests - -As mentioned in the [main documentation](README.md), we are following the [MVC pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). The `_model.py`'s job to get the required data, e.g. from an external APIs or scraping. - -Therefore, in unit tests for `_model.py`, the function output is captured and saved when the test is first run. Then, the next time the test is run, the function's returned value is compared with the captured value in the first run, to ensure parity. - -Depending on the data type of function output, we can generate the following files: - -- csv : for pandas.DataFrame -- json : for python native data type (list, dict, tuple) -- txt : store str or printed output - -Examples: - -```python -@pytest.fixture(scope="module") -def vcr_config(): - return { - "filter_query_parameters": [ - ("token", "MOCK_TOKEN"), - ], - } - - -@pytest.mark.vcr -@pytest.mark.parametrize( - "ticker", - [ - ("BTC"), - ("BTC-USD"), - ], -) -def test_check_supported_ticker(ticker, recorder): - df = sentimentinvestor_model.check_supported_ticker(ticker) - recorder.capture(df) -``` - -Several things worth noting here: - -1. If the model utilizes an API key or token, make sure to filter it insider the `vcr_config` fixture inside your test module, similar to the example. This will hide your actual API key/token for security purposes. -2. Record HTTP requests using the decorator `@pytest.mark.vcr` - 1. When run the first time, the VCR will record the requests to `cassettes/test_*.yml` - 2. Run it again, and VCR will replay the recorded response when the HTTP request is made. This test is now fast (no real HTTP requests are made anymore), deterministic (the test will continue to pass, even if you have no internet connection or that external APIs go down) and accurate (the response will contain the same headers and body you get from a real request). -3. Test out multiple input variables to ensure tests cover multiple edge cases - 1. Use the decorator `@pytest.mark.parametrize` to specify the inputs for your tests. -4. Capture the returned df using `recorder.capture()` - 1. Next time the test runs, the return output will be compared against the captured file -5. Each fixture / cassette file for a test module should be stored within the same folder. An example with `tests/openbb_terminal/some_package/some_module.py` : - - `tests/openbb_terminal/some_package`/cassettes/`some_module`/test_function1.yaml - - `tests/openbb_terminal/some_package`/txt/`some_module`/test_function1.csv - - `tests/openbb_terminal/some_package`/csv/`some_module`/test_function1.csv - - `tests/openbb_terminal/some_package`/json/`some_module`/test_function1.json -6. You can choose the `cassette` name using the `@pytest.mark.default_cassette` marker. Here is an example : - -```python -@pytest.mark.default_cassette("example.yaml") -@pytest.mark.vcr -def test_default(): - assert request("http://httpbin.org/get").text == '{"get": true}' -``` - -### 3.2. View Tests - -Different from the `_model.py`, `_view.py` modules handles how data is displayed, e.g.as a chart or a table. - -The `_view.py` has no return statement, and simply prints to stdout. Hence, our unit tests will capture stdout values, and compare against the recorded fixtures in subsequent runs. - -Example: - -```python -@pytest.mark.vcr -@pytest.mark.record_stdout -def test_display_historical(mocker): - # MOCK VISUALIZE_OUTPUT - mocker.patch(target="openbb_terminal.helper_classes.TerminalStyle.visualize_output") - sentimentinvestor_view.display_historical( - ticker="AAPL", start="2021-12-12", end="2021-12-15", export="", raw=True - ) -``` - -Important notes to go through: - -1. Use `@pytest.mark.record_stdout` to capture `sdtout`. - 1. Recorded stdout is saved as fixtures in a txt file - 2. If your view function only plots a chart, and does not output anything in `sdtout`, you can skip unit testing your `view.py` - 3. Make sure to mock `visualize_output` whenever possible. - 1. Inside `visualize_output`, we use `ion` to enable interactive mode. However, under Windows, having `ion` enabled would make tests fail to run. Hence, you should always mock `visualize_output`, and hence `ion` - - -### 3.3. Controller Tests - -Unit test for `_controller.py` is the last component you would need to add. Most of the time you won't need to create a `_controller.py` from scratch. Hence, we will only cover how to add tests for a new command to an existing `test_*_controller.py` file - - -#### Test print help - -This test in the `test_*_controller.py` captured the help text printed to stdout. - -Example: - -```python -@pytest.mark.vcr(record_mode="none") -@pytest.mark.record_stdout -def test_print_help(): - controller = alt_controller.AlternativeDataController(queue=None) - controller.print_help() -``` - -Adding an additional feature to the `controller.py` will alter the help text. Make sure to run the tests with the argument `--rewrite-expected` to re-generate the text file. - -Example: `pytest openbb_terminal.cryptocurrency.defi.defi_controller --rewrite-expected` - -#### Test call function - -This test is to check when `controller.py` calls a command, the corresponding function from a `view.py` will be called with the correct arguments. - -Example: - -```python -@pytest.mark.vcr(record_mode="none") -@pytest.mark.parametrize( - "tested_func, other_args, mocked_func, called_args, called_kwargs", - [ - ( - "call_aterra", - [ - "ust", - "--address=terra1wg2mlrxdmnnkkykgqg4znky86nyrtc45q336yv", # pragma: allowlist secret - ], - "terraengineer_view.display_terra_asset_history", - [], - dict(), - ), - ( - "call_newsletter", - [], - "substack_view.display_newsletters", - [], - dict(), - ), - ], -) -def test_call_func( - tested_func, mocked_func, other_args, called_args, called_kwargs, mocker -): - path_controller = "openbb_terminal.cryptocurrency.defi.defi_controller" - - if mocked_func: - mock = mocker.Mock() - mocker.patch( - target=f"{path_controller}.{mocked_func}", - new=mock, - ) - - controller = defi_controller.DefiController(queue=None) - getattr(controller, tested_func)(other_args) - - if called_args or called_kwargs: - mock.assert_called_once_with(*called_args, **called_kwargs) - else: - mock.assert_called_once() - else: - controller = defi_controller.DefiController(queue=None) - getattr(controller, tested_func)(other_args) -``` - -When adding a new feature to an existing menu, there should already be a `test_*_controller.py` file with the `test_call_func()` function. You can simply add another tuple under `@pytest.mark.parametrize()`, which contains: - -1. `call_` from the controller.py file -2. Arguments you want to pass in the `call_` -3. The corresponding view function, which will be mocked (Here we only want check if the view function is called at least once. We mock it such that the function won't execute.) -4. Arguments you want to pass to the view function. Note that the arguments here need to be the same as arguments in (2). This is to ensure that arguments passed from the controller.py file are passed correctly to function in `view.py` - - -### 3.4. Fixing existing tests - -It is not uncommon that your new feature or bug fixes break the existing code. Understanding a few concepts can help you navigate and fix most common cases. - -Before continuing this section, please have a look at our [pytest-recorder-documentation](https://github.com/OpenBB-finance/pytest_recorder/blob/main/README.md). - -### 3.5. How to `mock`? - -#### PYTEST-MOCK - -A `mocker` fixture is available through the package `pytest-mock`. - -Here is an example on how to `mocker` fixture can be used : - -```python -def test_something(mocker): - mocker.patch("some_package.some_module") -``` - -More information about `pytest-mock` are available here : - -- [Pytest Mock](https://pypi.org/project/pytest-mock/) - -#### MONCKEYPATCH - -There is also a `monkeypatch` fixture available by default inside `pytest`. - -```python -def test_double(monkeypatch): - monkeypatch.setattr("some_package.some_module", "Some value") -``` - -More information about `monkeypatch` are available here : - -- [Monkeypatch](https://docs.pytest.org/en/6.2.x/monkeypatch.html) - -### 3.6. Which `helpers` are available? - -You can find the available helpers inside the following package/module : - -- `tests/helpers/` -- `tests/conftest.py` - -See also the `pytest fixtures` which are autoloaded helpers. - -### 3.7. Which `fixtures` are available ? - -You can list all the available `pytest fixtures` using the following command : - -```bash -pytest --fixtures -``` - -More on custom fixtures here : - -- [FIXTURES](FIXTURES.md) - -### 3.8. Which `markers` are available ? - -You can list the available markers using this command : - -```bash -pytest --markers -``` - -More information on markers location are available in pytest documentation : - -- https://docs.pytest.org/en/6.2.x/mark.html#mark - -### 3.9. Known `issue` / `solution` - -#### ION Usage - -As mentioned earlier, if you are testing for a `_view.py` module that uses `ION`, you would need to mock it. With Windows, the charts / graphs are unable to close the graph applications. This would make the tests failed when running with Windows. - -You can use the following example below. Once you have mocked the graphs / charts, simply call the display function, which is now a mocked object. - -```python -@pytest.mark.vcr -@pytest.mark.record_stdout -def test_display_defi_tvl(mocker): - # MOCK EXPORT_DATA - mocker.patch(target="openbb_terminal.cryptocurrency.defi.llama_view.export_data") - - # MOCK VISUALIZE_OUTPUT - mocker.patch(target="openbb_terminal.helper_classes.TerminalStyle.visualize_output") - - llama_view.display_defi_tvl(20) -``` - -#### YFINANCE - example 1 - -If a method is using the `yfinance` library but do not let you pick the `start/end` dates it will pick the current date each time. - -So your test might fail the next days. - -To solve that you can filter the `start/end` date like this : - -```python -@pytest.fixture(scope="module") -def vcr_config(): - return { - "filter_headers": [("User-Agent", None)], - "filter_query_parameters": [ - ("period1", "MOCK_PERIOD_1"), - ("period2", "MOCK_PERIOD_2"), - ("date", "MOCK_DATE"), - ], - } -``` - -You can also refactor this method to let access to `stard/end` dates. - -#### YFINANCE - example 2 - -If you do something like this with `yfinance` library. - -```python -import yfinance as yf - -yf.download(tickers="VCYT QSI") -``` - -Chances are you requests will be multi-threaded. - -Issues : as for now `vcrpy` seems to be incompatible with multi-threading. - -The library `vcrpy` is used to record `cassettes` (`network` calls into `yaml` files). - -Here is a solution to still combine `yfinance` and `vcrpy` : - -```python -import pytest -import yfinance - -yf_download = yf.download -def mock_yf_download(*args, **kwargs): - kwargs["threads"] = False - return yf_download(*args, **kwargs) - -@pytest.mark.vcr -def test_ark_orders_view(kwargs_dict, mocker, use_color): - mocker.patch("yfinance.download", side_effect=mock_yf_download) - - yf.download(tickers="VCYT QSI") -``` - -#### USER-AGENT - -Some function uses a random `User-Agent` on the `HTTP HEADER` when fetching data from an `API`. - -Here is how to filter this random `User-Agent`. - -```python -@pytest.fixture(scope="module") -def vcr_config(): - return { - "filter_headers": [("User-Agent", None)], - } -``` - -#### BROTLI - -The library `requests` doesn't support: `brotli` comnpression. - -Unless `brotli` library is installed in the environment. - -Plus `brotli` is not in `OpenBBTerminal` requirements. - -So if both of these conditions are fulfilled : - -- one generates a cassette with `brotli` installed in his/her environment -- the server choose to send `brotli` compressed data - -Then the `test` might work in local but crash during `PullRequest`. - -#### BEFORE_RECORD_RESPONSE - -The library `vcrpy` has a `before_record_response` which accept a filtering function that can be used to filter your cassette content : - -```python -import pytest - -def my_custom_filter(response): - return response - -@pytest.mark.vcr(before_record_response=my_custom_filter) -def test_simple(recorder): - pass -``` - -For instance this can be used to reduce the size of a `cassette` if it's too heavy. - -Or to filter sensitive data in the response. - -The issue with this `before_record_response` : it isn't launched at the first run of the test. - -More on this [here](https://github.com/kevin1024/vcrpy/pull/594). - -A solution for now is to run this command while initializing the `cassettes` : - -```bash -# THE SAME COMMAND NEEDS TO BE RUN TWICE -pytest tests/.../test_some_test_module.py --record-mode=once --rewrite-expected -pytest tests/.../test_some_test_module.py --record-mode=once --rewrite-expected -``` - -### 3.10. List of useful `vscode` tools for `unit tests` - -#### Testing on VSCODE - -The default testing tool from `vscode` should let you add breakpoints and run debug on a specific `test`. - -It's a convenient way to see what's inside your `test` while running. - -More information on this tool are available [here](https://code.visualstudio.com/docs/python/testing). - -### 3.11. How to handle `dev-dependencies` ? - -#### UPDATE PYPROJECT - -If a library need to be added or removed from `dev-dependencies` this can be done directly in the `pyproject.toml` file. - -All the `dev-dependencies` should be under the `tool.poetry.dev-dependencies` section, like in this example: - -```toml -[tool.poetry.dev-dependencies] -pytest = "^6.2.2" -pylint = "^2.7.2" -flake8 = "^3.9.0" -... -``` - -#### EXPORT REQUIREMENTS - -After updating the `pyproject.toml` you will have to export the `requirements` files using the following commands: - -```bash -poetry export -f requirements.txt -o requirements.txt --without-hashes --with dev -poetry export -f requirements.txt -o requirements-full.txt --extras forecast --extras optimization --without-hashes --with dev -``` - -## 4. Maintain `unit tests` - -### 4.1. What's the PR process? - -Here are the steps to write proper tests for the OpenBBTerminal: - -1. Find right place -2. Verify coverage is above 90% -3. Set the right markers - -#### 4.1.1 Find right place - -Put the code following the same module and package structure than `openbb_terminal` package. - -#### 4.1.2 Verify coverage is above 90% - -Once you made your `PullRequest` an automation will let you know whether or not you have the proper amount of tests coverage. - -You can also run the following command to check your coverage manually : - -- `pytest --cov --cov-fail-under=90` - -#### 4.1.3 Set the right markers - -If parts of your have specificities like : - -- being `slow` -- requiring `network` connectivity -- requiring `authentication` to `APIs` - -Then the proper markers should be sent on each test. - -The rests of this document is there to provide a deeper comprehension of this steps. - -A tests update might be asked whenever the answer to one of the following question is `yes` : - -- Is this PR fixing bug which was not detected by the current tests ? -- Is this PR reducing code coverage ? -- Is this PR adding features ? -- Is this PR updating features ? - -### 4.2. Which automations are available ? - -#### PULL REQUEST - -A `github action` should comment every `Pull Request` with the code `coverage`. - -This automation will not enforce any rules regarding `tests`. - -### 4.3. How to check code coverage ? - -#### PULL REQUEST AUTOMATION - -You should be able to see the code coverage on comment of your `Pull Requests`. - -#### MANUALLY - -This is how to manually check code coverage. - -```bash -pytest --cov=openbb_terminal --cov=terminal --cov-report term-missing -``` - -You can also select a specific package/module with the option `--cov`. - -Here is an example where we select only the package `stocks\due_diligence` : - -```bash -pytest --cov=openbb_terminal\stocks\due_diligence --cov-report term-missing -``` - -#### PRE-COMMIT - -Code `coverage` and `unit tests` are not run on `pre-commit` hooks since it will slow down each commit. diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index 2ecc998dc59..00000000000 --- a/tests/conftest.py +++ /dev/null @@ -1,522 +0,0 @@ -# IMPORTS STANDARD - -import json -import os -import pathlib -from typing import Any, Dict, List, Optional, Type - -import importlib_metadata - -# IMPORTS THIRD-PARTY -import matplotlib -import pandas as pd -import pytest -from _pytest.capture import MultiCapture, SysCapture -from _pytest.config import Config -from _pytest.config.argparsing import Parser -from _pytest.fixtures import SubRequest -from _pytest.mark.structures import Mark - -# IMPORTS INTERNAL -from openbb_terminal import ( - config_terminal, - decorators, - helper_funcs, -) -from openbb_terminal.core.session.current_system import set_system_variable - -# pylint: disable=redefined-outer-name - -config_terminal.setup_i18n() - -DISPLAY_LIMIT: int = 500 -EXTENSIONS_ALLOWED: List[str] = ["csv", "json", "txt"] -EXTENSIONS_MATCHING: Dict[str, List[Type]] = { - "csv": [pd.DataFrame, pd.Series], - "json": [bool, dict, float, int, list, tuple], - "txt": [str], -} - -set_system_variable("TEST_MODE", True) -set_system_variable("LOG_COLLECT", False) - - -class Record: - @staticmethod - def extract_string(data: Any, **kwargs) -> str: - if isinstance(data, tuple(EXTENSIONS_MATCHING["txt"])): - string_value = data - elif isinstance(data, tuple(EXTENSIONS_MATCHING["csv"])): - string_value = data.to_csv( - encoding="utf-8", - lineterminator="\n", - # date_format="%Y-%m-%d %H:%M:%S", - **kwargs, - ) - elif isinstance(data, tuple(EXTENSIONS_MATCHING["json"])): - string_value = json.dumps(data, **kwargs) - else: - raise AttributeError(f"Unsupported type : {type(data)}") - - return string_value.replace("\r\n", "\n") - - @staticmethod - def load_string(path: str) -> Optional[str]: - if os.path.exists(path): - with open( - file=path, - encoding="utf-8", - newline="\n", # Windows: newline="\r\n" Which is BAD - ) as f: - return f.read() - else: - return None - - @property - def captured(self) -> str: - return self.__captured - - @property - def strip(self) -> bool: - return self.__strip - - @property - def record_changed(self) -> bool: - return ( - self.__recorded is None - or (self.__strip and self.__recorded.strip() != self.__captured.strip()) - or (not self.__strip and self.__recorded != self.__captured) - ) - - @property - def record_exists(self) -> bool: - return self.__recorded is not None - - @property - def record_path(self) -> str: - return self.__record_path - - @property - def recorded(self) -> Optional[str]: - return self.__recorded - - def recorded_reload(self): - record_path = self.__record_path - self.__recorded = self.load_string(path=record_path) - - def __init__( - self, captured: Any, record_path: str, strip: bool = False, **kwargs - ) -> None: - self.__captured = self.extract_string(data=captured, **kwargs) - self.__record_path = record_path - self.__strip = strip - - self.__recorded = self.load_string(path=record_path) - - def persist(self): - record_path = self.__record_path - captured = self.__captured - record_dir_name = os.path.dirname(record_path) - - # CREATE FOLDER - if not os.path.exists(record_dir_name): - pathlib.Path(record_dir_name).mkdir(parents=True, exist_ok=True) - - # SAVE FILE - with open( - file=record_path, - mode="w", - encoding="utf-8", - newline="\n", # Windows: newline="\r\n" Which is BAD - ) as f: - f.write(captured) - - # RELOAD RECORDED CONTENT - self.recorded_reload() - - -class PathTemplate: - @staticmethod - def find_extension(data: Any): - for extension, type_list in EXTENSIONS_MATCHING.items(): - if isinstance(data, tuple(type_list)): - return extension - raise Exception(f"No extension found for this type : {type(data)}") - - def __init__(self, module_dir: str, module_name: str, test_name: str) -> None: - self.__module_dir = module_dir - self.__module_name = module_name - self.__test_name = test_name - - def build_path_by_extension(self, extension: str, index: int = 0): - if extension not in EXTENSIONS_ALLOWED: - raise Exception(f"Unsupported extension : {extension}") - - path = os.path.join( - self.__module_dir, extension, self.__module_name, self.__test_name - ) - if index: - path += "_" + str(index) - path += "." - path += extension - - return path - - def build_path_by_data(self, data: Any, index: int = 0): - extension = self.find_extension(data=data) - return self.build_path_by_extension(extension=extension, index=index) - - -class Recorder: - @property - def display_limit(self) -> int: - return self.__display_limit - - @display_limit.setter - def display_limit(self, display_limit: int): - self.__display_limit = display_limit - - @property - def rewrite_expected(self) -> bool: - return self.__rewrite_expected - - @rewrite_expected.setter - def rewrite_expected(self, rewrite_expected: bool): - self.__rewrite_expected = rewrite_expected - - @property - def path_template(self) -> PathTemplate: - return self.__path_template - - @property - def record_mode(self) -> str: - return self.__record_mode - - @record_mode.setter - def record_mode(self, record_mode: str): - self.__record_mode = record_mode - - def __init__( - self, - path_template: PathTemplate, - record_mode: str, - display_limit: int = DISPLAY_LIMIT, - rewrite_expected: bool = False, - ) -> None: - self.__path_template = path_template - self.__record_mode = record_mode - self.__display_limit = display_limit - self.__rewrite_expected = rewrite_expected - - self.__record_list: List[Record] = list() - - def capture(self, captured: Any, strip: bool = False, **kwargs): - record_list = self.__record_list - record_path = self.__path_template.build_path_by_data( - data=captured, index=len(record_list) - ) - record = Record( - captured=captured, record_path=record_path, strip=strip, **kwargs - ) - self.__record_list.append(record) - - def capture_list(self, captured_list: List[Any], strip: bool = False): - for captured in captured_list: - self.capture(captured=captured, strip=strip) - - def assert_equal(self): - record_list = self.__record_list - - for record in record_list: - if record.record_changed: - raise AssertionError( - "Change detected\n" - f"Record : {record.record_path}\n" - f"Expected : {record.recorded[:self.display_limit]}\n" - f"Actual : {record.captured[:self.display_limit]}\n" - ) - - def assert_in_list(self, in_list: List[str]): - record_list = self.__record_list - - for record in record_list: - for string_value in in_list: - assert string_value in record.captured - - def persist(self): - record_list = self.__record_list - record_mode = self.__record_mode - rewrite_expected = self.__rewrite_expected - - for record in record_list: - if record_mode == "all": - save = True - elif record_mode == "new_episodes": - save = record.record_changed - elif record_mode == "none": - if not record.record_exists: - raise Exception("You are using `record-mode=none`.") - - save = False - elif record_mode == "once": - save = not record.record_exists - elif record_mode == "rewrite": - save = True - else: - raise Exception(f"Unknown `record-mode` : {record_mode}") - - if save or rewrite_expected: - record.persist() - - -def build_path_by_extension( - request: SubRequest, extension: str, create_folder: bool = False -) -> str: - # SETUP PATH TEMPLATE - module_dir = request.node.fspath.dirname - module_name = request.node.fspath.purebasename - test_name = request.node.name - path_template = PathTemplate( - module_dir=module_dir, module_name=module_name, test_name=test_name - ) - - # BUILD PATH - path = path_template.build_path_by_extension(extension) - - # CREATE FOLDER - if create_folder: - dir_name = os.path.dirname(path) - if not os.path.exists(dir_name): - dir_name = os.path.dirname(path) - pathlib.Path(dir_name).mkdir(parents=True, exist_ok=True) - - return path - - -def merge_markers_kwargs(markers: List[Mark]) -> Dict[str, Any]: - """Merge all kwargs into a single dictionary.""" - kwargs: Dict[str, Any] = dict() - for marker in reversed(markers): - kwargs.update(marker.kwargs) - return kwargs - - -def record_stdout_format_kwargs( - test_name: str, record_mode: str, record_stdout_markers: List[Mark] -) -> Dict[str, Any]: - kwargs = merge_markers_kwargs(record_stdout_markers) - - formatted_fields = dict() - formatted_fields["assert_in_list"] = kwargs.get("assert_in_list", list()) - formatted_fields["display_limit"] = kwargs.get("display_limit", DISPLAY_LIMIT) - formatted_fields["record_mode"] = kwargs.get("record_mode", record_mode) - formatted_fields["record_name"] = kwargs.get("record_name", test_name) - formatted_fields["save_record"] = kwargs.get("save_record", True) - formatted_fields["strip"] = kwargs.get("strip", True) - - return formatted_fields - - -def pytest_addoption(parser: Parser): - parser.addoption( - "--forecast", - action="store_true", - help="To run tests with the marker : @pytest.mark.forecast", - ) - parser.addoption( - "--optimization", - action="store_true", - help="To run tests with the marker : @pytest.mark.optimization", - ) - parser.addoption( - "--session", - action="store_true", - help="To run tests with the marker : @pytest.mark.session", - ) - parser.addoption( - "--rewrite-expected", - action="store_true", - help="To force `record_stdout` and `recorder` to rewrite all files.", - ) - parser.addoption( - "--autodoc", - action="store_true", - default=False, - help="run auto documentation tests", - ) - - -def brotli_check(): - for item in importlib_metadata.packages_distributions(): - if "brotli" in str(item).lower(): - pytest.exit("Uninstall brotli and brotlipy before running tests") - - -def disable_rich(): - def effect(df, *xargs, **kwargs): # pylint: disable=unused-argument - print(df.to_string()) # noqa: T201 - - helper_funcs.print_rich_table = effect - - -def disable_matplotlib(): - # We add this to avoid multiple figures being opened - matplotlib.use("Agg") - - -def disable_check_api(): - decorators.disable_check_api() - - -def enable_debug(): - set_system_variable("DEBUG_MODE", True) - - -def pytest_configure(config: Config) -> None: - config.addinivalue_line("markers", "record_stdout: Mark the test as text record.") - - brotli_check() - enable_debug() - disable_rich() - disable_check_api() - disable_matplotlib() - - -@pytest.fixture(scope="session") # type: ignore -def rewrite_expected(request: SubRequest) -> bool: - """Force rewriting of all expected data by : `record_stdout` and `recorder`.""" - return request.config.getoption("--rewrite-expected") - - -@pytest.fixture(autouse=True) -def mock_matplotlib(mocker): - mocker.patch("matplotlib.pyplot.show") - - -@pytest.fixture(autouse=True) -def mock_plotly(mocker): - mocker.patch("plotly.io.show") - - -# pylint: disable=protected-access - - -@pytest.fixture -def default_csv_path(request: SubRequest) -> str: - return build_path_by_extension(request=request, extension="csv", create_folder=True) - - -@pytest.fixture -def default_txt_path(request: SubRequest) -> str: - return build_path_by_extension(request=request, extension="txt", create_folder=True) - - -@pytest.fixture -def default_json_path(request: SubRequest) -> str: - return build_path_by_extension( - request=request, extension="json", create_folder=True - ) - - -@pytest.fixture -def record_stdout_markers(request: SubRequest) -> List[Mark]: - """All markers applied to the certain test together with cassette names associated with each marker.""" - return list(request.node.iter_markers(name="record_stdout")) - - -@pytest.fixture(autouse=True) -def record_stdout( - disable_recording: bool, - rewrite_expected: bool, - record_stdout_markers: List[Mark], - record_mode: str, - request: SubRequest, -): - marker = request.node.get_closest_marker("record_stdout") - - if disable_recording: - yield None - elif marker: - # SETUP TEST DETAILS - module_dir = request.node.fspath.dirname - module_name = request.node.fspath.purebasename - test_name = request.node.name - - # FORMAT MARKER'S KEYWORD ARGUMENTS - formatted_kwargs = record_stdout_format_kwargs( - test_name=test_name, - record_mode=record_mode, - record_stdout_markers=record_stdout_markers, - ) - - # SETUP RECORDER - path_template = PathTemplate( - module_dir=module_dir, - module_name=module_name, - test_name=formatted_kwargs["record_name"], - ) - recorder = Recorder( - path_template=path_template, - record_mode=formatted_kwargs["record_mode"], - display_limit=formatted_kwargs["display_limit"], - rewrite_expected=rewrite_expected, - ) - - # CAPTURE STDOUT - capture = request.config.getoption("--capture") - if capture == "no": - global_capturing = MultiCapture( - in_=SysCapture(0), out=SysCapture(1), err=SysCapture(2) - ) - global_capturing.start_capturing() - yield - recorder.capture( - captured=global_capturing.readouterr().out, - strip=formatted_kwargs["strip"], - ) - global_capturing.stop_capturing() - else: - capsys = request.getfixturevalue("capsys") - yield - recorder.capture( - captured=capsys.readouterr().out, strip=formatted_kwargs["strip"] - ) - - # SAVE/CHECK RECORD - if formatted_kwargs["save_record"]: - recorder.persist() - recorder.assert_equal() - recorder.assert_in_list(in_list=formatted_kwargs["assert_in_list"]) - else: - recorder.assert_in_list(in_list=formatted_kwargs["assert_in_list"]) - else: - yield None - - -@pytest.fixture -def recorder( - disable_recording: bool, - rewrite_expected: bool, - record_mode: str, - request: SubRequest, -): - marker_record_stdout = request.node.get_closest_marker("record_stdout") - module_dir = request.node.fspath.dirname - module_name = request.node.fspath.purebasename - test_name = request.node.name - path_template = PathTemplate( - module_dir=module_dir, module_name=module_name, test_name=test_name - ) - if disable_recording: - yield None - elif marker_record_stdout: - raise Exception( - "You can't combine both of these fixtures : `record_stdout marker`, `recorder`." - ) - else: - recorder = Recorder( - path_template, record_mode, rewrite_expected=rewrite_expected - ) - yield recorder - recorder.persist() - recorder.assert_equal() diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/helpers/helpers.py b/tests/helpers/helpers.py deleted file mode 100644 index 4a85258cd6f..00000000000 --- a/tests/helpers/helpers.py +++ /dev/null @@ -1,41 +0,0 @@ -import functools -import io -import logging -import sys -from typing import Union - -logger = logging.getLogger(__name__) - - -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: - logging.exception("zero division") - return float("inf") - - -def check_print(assert_in: str = "", length: int = -1): - """Captures output of print function and checks if the function contains a given string""" - - def checker(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - capturedOutput = io.StringIO() - sys.stdout = capturedOutput - func(*args, **kwargs) - sys.stdout = sys.__stdout__ - capt = capturedOutput.getvalue() - if assert_in: - assert assert_in in capt - return None - if length >= 0: - assert len(capt) > length - return capt - - return wrapper - - return checker diff --git a/tests/helpers/tools.py b/tests/helpers/tools.py deleted file mode 100644 index 10ac1244fe2..00000000000 --- a/tests/helpers/tools.py +++ /dev/null @@ -1,65 +0,0 @@ -""" Test helper """ - -__docformat__ = "numpy" - -import pathlib -from typing import Callable - -import yaml - - -def parameterize_from_file(test_namespace: str, parameter_file: str) -> Callable: - """A test helper function returns a wrapper function that loads sets of parameters from a YAML and - attaches test scenarios as metadata - - Parameters - ---------- - test_namespace : str - Test namespace to use within the YAML - parameter_file : str - Test scenario YAML to load - - Returns - ------- - Callable - Wrapper function - """ - - def wrapper(function): - test_file_base_path = pathlib.Path(__file__).parent.absolute() - data_file_full_path = pathlib.Path(test_file_base_path, parameter_file) - - with open(data_file_full_path, encoding="utf-8", newline="\n") as file: - parameter_data = yaml.full_load(file) - - idlist = [] - argnames = [] - argvalues = [] - - for scenario in parameter_data[test_namespace]: - idlist.append(scenario) - argnames = [] - tempvalues = [] - for argname in parameter_data[test_namespace][scenario]: - argnames.append(argname) - tempvalues.append(parameter_data[test_namespace][scenario][argname]) - argvalues.append(tempvalues) - - function.test_params_names = argnames - function.test_param_values = argvalues - function.test_ids = idlist - - return function - - return wrapper - - -def pytest_generate_tests(metafunc): - """https://docs.pytest.org/en/stable/parametrize.html#pytest-generate-tests""" - if getattr(metafunc.function, "test_params_names", None): - metafunc.parametrize( - metafunc.function.test_params_names, - metafunc.function.test_param_values, - ids=metafunc.function.test_ids, - scope="class", - ) diff --git a/tests/test_helpers.py b/tests/test_helpers.py deleted file mode 100644 index ce982432747..00000000000 --- a/tests/test_helpers.py +++ /dev/null @@ -1,9 +0,0 @@ -import pandas as pd - - -def no_dfs(args: list, kwargs: dict) -> bool: - """Returns false if there are in dataframe objects given""" - for item in args: - if isinstance(item, pd.DataFrame): - return False - return all(not isinstance(item, pd.DataFrame) for item in kwargs.values()) diff --git a/tests/website/__init__.py b/tests/website/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/website/conftest.py b/tests/website/conftest.py deleted file mode 100644 index b18ed7e2f33..00000000000 --- a/tests/website/conftest.py +++ /dev/null @@ -1,7 +0,0 @@ -import pytest -from _pytest.nodes import Node - - -def pytest_runtest_setup(item: Node): - if not item.config.getoption("--autodoc"): - pytest.skip(msg="Runs only with option : --autodoc") diff --git a/tests/website/test_generate_docs.py b/tests/website/test_generate_docs.py deleted file mode 100644 index 04f1f7d63b9..00000000000 --- a/tests/website/test_generate_docs.py +++ /dev/null @@ -1,109 +0,0 @@ -from typing import Optional - -import pytest - -try: - from openbb_terminal.core.sdk.trailmap import get_signature_parameters - from website import ( - generate_sdk_v3_markdown as gen_sdk, - generate_terminal_v3_markdown as gen_term, - ) -except ImportError: - pytest.skip(allow_module_level=True) - - -def test_generate_terminal_markdown(): - """Test the terminal markdown generator""" - assert gen_term.main() is True - - -def test_generate_sdk_markdown(): - """Test the sdk markdown generator""" - assert gen_sdk.main() is True - - -def mock_func(arg1: Optional[str] = "Test", arg2: Optional[bool] = True) -> bool: - """Stuff here or stuff there, it doesn't matter, it's everywhere. - - - Parameters - ---------- - arg1 : Optional[str] - The first argument, by default "Test" - arg2 : Optional[bool] - The second argument, by default True - - Returns - ------- - bool - The return value. - """ - del arg1, arg2 - return True - - -# pylint:disable=too-few-public-methods -class MockFuncAttrs: - """Mock function attributes""" - - def __init__(self): - self.lineon = 69 - self.full_path = "test/mock_func.py" - self.long_doc = mock_func.__doc__ - self.func_unwrapped = mock_func - self.func_def = ( - 'openbb.mock(arg1: Optional[str] = "Test", arg2: Optional[bool] = True)' - ) - self.params = {} - for k, p in get_signature_parameters(mock_func, mock_func.__globals__).items(): - self.params[k] = p - - -class MockTrailMap: - """Mock trail map""" - - def __init__(self): - self.func_attrs = {} - self.func_attrs["model"] = MockFuncAttrs() - self.model = mock_func - self.class_attr = "mock" - self.location_path = [] - - -EXPECTED_OUTPUT = """Stuff here or stuff there, it doesn't matter, it's everywhere. - -Source Code: [[link](https://github.com/OpenBB-finance/OpenBBTerminal/tree/main/test/mock_func.py#L69)] - -```python wordwrap -openbb.mock(arg1: Optional[str] = "Test", arg2: Optional[bool] = True) -``` - ---- - -## Parameters - -| Name | Type | Description | Default | Optional | -| ---- | ---- | ----------- | ------- | -------- | -| arg1 | Optional[str] | The first argument, by default "Test" | Test | True | -| arg2 | Optional[bool] | The second argument, by default True | True | True | - - ---- - -## Returns - -| Type | Description | -| ---- | ----------- | -| bool | The return value. | ---- - -""" - - -def test_sdk_docstring_to_markdown(): - """Test the docstring to markdown converter""" - mock = MockTrailMap() - func_meta = gen_sdk.get_function_meta(mock, "model") - result = gen_sdk.generate_markdown_section(func_meta) - - assert result == EXPECTED_OUTPUT diff --git a/website/controller_doc_classes.py b/website/controller_doc_classes.py deleted file mode 100644 index d2d762f2603..00000000000 --- a/website/controller_doc_classes.py +++ /dev/null @@ -1,526 +0,0 @@ -# pylint: disable=unused-argument -import argparse -import contextlib -import inspect -import sys -from datetime import datetime, timedelta -from importlib.util import module_from_spec, spec_from_file_location -from inspect import FullArgSpec, getmembers, isclass -from pathlib import Path -from types import FunctionType, ModuleType -from typing import Any, Dict, List, Optional -from unittest.mock import patch - -import pandas as pd -from pandas.tseries.holiday import USFederalHolidayCalendar -from rich.console import Console, Theme - -import openbb_terminal -import openbb_terminal.config_terminal as cfg -from openbb_terminal.core.plots.backend import plots_backend -from openbb_terminal.core.plots.plotly_helper import theme -from openbb_terminal.core.session.current_system import set_system_variable -from openbb_terminal.core.session.current_user import get_current_user # noqa: F401 -from openbb_terminal.decorators import disable_check_api -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - EXPORT_ONLY_FIGURES_ALLOWED, - NO_EXPORT, - set_command_location, -) -from openbb_terminal.parent_classes import BaseController -from openbb_terminal.sdk import openbb - -set_system_variable("TEST_MODE", True) -set_system_variable("LOG_COLLECT", False) -disable_check_api() -console = Console(theme=Theme(theme.console_style), highlight=False, soft_wrap=True) - - -CRYPTO_DATA = openbb.crypto.load("BTC", to_symbol="usd", source="YahooFinance") -ETF_DATA = openbb.etf.load("SPY") -ETF_DATA.index.name = "date" -FOREX_DATA = openbb.forex.load(to_symbol="USD", from_symbol="EUR") -FOREX_DATA.index.name = "date" -STOCK_DATA = openbb.stocks.load("AAPL", start_date="2022-01-01") -STOCK_DATA.index.name = "date" - -data_dict = { - "stocks": { - "data": STOCK_DATA, - "symbol": "AAPL", - }, - "crypto": { - "data": CRYPTO_DATA, - "symbol": "BTC", - }, - "forex": { - "data": FOREX_DATA, - "symbol": "EURUSD", - }, - "futures": { - "data": STOCK_DATA, - "symbol": "ES", - }, - "etf": { - "data": ETF_DATA, - "symbol": "SPY", - }, -} - -sub_folders_abbr = { - "discovery": "disc", - "due_diligence": "dd", - "overview": "ov", - "alternative": "alt", - "cryptocurrency": "crypto", - "behavioural_analysis": "ba", - "comparison_analysis": "ca", - "dark_pool_shorts": "dps", - "portfolio_optimization": "po", - "companieshouse": "companieshouse", - "quantitative_analysis": "qa", - "technical_analysis": "ta", - "tradinghours": "th", - "fundamental_analysis": "fa", - "mutual_funds": "funds", - "government": "gov", - "insider": "ins", -} - -sub_names_full = { - "alt": "Alternative", - "ba": "Behavioural Analysis", - "ca": "Comparison Analysis", - "companieshouse": "Companies House", - "crypto": "Cryptocurrency", - "dd": "Due Diligence", - "defi": "DeFi", - "disc": "Discovery", - "dps": "Darkpool Shorts", - "etf": "ETFs", - "fa": "Fundamental Analysis", - "forecast": "Forecasting", - "funds": "Mutual Funds", - "gov": "Government", - "ins": "Insiders", - "keys": "Keys", - "nft": "NFTs", - "onchain": "OnChain", - "ov": "Overview", - "po": "Portfolio Optimization", - "qa": "Quantitative Analysis", - "screener": "Screener", - "ta": "Technical Analysis", - "th": "Trading Hours", -} - -required_flags = { - "crypto": { - "funot": ["-p", "ethereum"], - }, - "econometrics": { - "coint": ["-t"], # TODO: add choice - "norm": ["-v"], # TODO: add choice - }, -} - - -def get_expiration_date(): - """Gets the next expiration date for controller init""" - dt = datetime.now() - bdays_indx = pd.bdate_range( - dt.strftime("%Y-%m-%d"), - (dt + timedelta(days=20)).strftime("%Y-%m-%d"), - freq=pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar()), - ).tolist() - expiration = [x.strftime("%Y-%m-%d") for x in bdays_indx if x.weekday() == 4][0] - return expiration - - -start_date = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d") -end_date = datetime.now().strftime("%Y-%m-%d") - -param_name_to_value = { - "expiration": get_expiration_date(), - "start_date": start_date, - "start": start_date, - "end_date": end_date, - "end": end_date, - "from_symbol": "USD", - "to_symbol": "EUR", - "vs": "USDT", -} -param_type_to_value = { - (dict, Dict): {}, - list: [], - str: "", - int: 0, - float: 0.0, - bool: False, -} - - -def get_parameters( - controller_cls: BaseController, name: str, df_loaded: pd.DataFrame, symbol: str -) -> Dict[str, Any]: - """Gets the parameters of a controller's `__init__` signature. If required parameters are missing, - we get the type and use a default value for it. - - Parameters - ---------- - controller_class: Type[BaseController] - The controller class - name: str - The name of the controller - df_loaded: pd.DataFrame - The dataframe loaded from the `load` function - symbol: str - The symbol to use on controller init - - Returns - ------- - dict[str, Any] - The parameters with their values for controller init - """ - signature = inspect.signature(controller_cls) # type: ignore - kwargs: Dict[str, Any] = {} - - for param in signature.parameters.values(): - if param.name in ("ticker", "symbol", "coin"): - kwargs[param.name] = symbol - elif param.name == "data" and name in ("forecast", "qa"): - kwargs["data"] = df_loaded - elif ( - param.default is inspect.Parameter.empty - and param.kind is not inspect.Parameter.VAR_KEYWORD - ): - for param_name, value in param_name_to_value.items(): - if param.name == param_name: - kwargs[param.name] = value - break - if param.name not in kwargs: - for param_type, value in param_type_to_value.items(): - if isinstance(param_type, tuple): - if param.annotation in param_type: - kwargs[param.name] = {symbol: df_loaded} - break - elif param.annotation is pd.DataFrame: - kwargs[param.name] = df_loaded - break - elif param.annotation is param_type: - kwargs[param.name] = value - break - - return kwargs - - -class ControllerDoc: - """Class that retrieves the ArgumentParser for each command of the Controller and stores it in a dictionary - for use in auto-generating the documentation. - - Parameters - ---------- - controller: BaseController - The controller to get the commands from - - Attributes - ---------- - controller: BaseController - The controller to get the commands from - cmd_parsers: Dict[str, argparse.ArgumentParser] - A dictionary of the command name and the ArgumentParser for that command - cmd_funcs: Dict[str, FunctionType] - A dictionary of the command name and the function for that command - cmd_fullspec: Dict[str, FullArgSpec] - A dictionary of the command name and the full argument spec for that command - ignore: List[str] - A list of commands to ignore - commands: List[str] - A list of commands to document - - Methods - ------- - get_commands() - Get commands - get_command_parser(command: str) - Get the parser for a command - get_all_command_parsers() - Get all command parsers - has_commands() - Checks if controller has commands to document - """ - - def __init__(self, controller: BaseController, trailmap: str): - self.trailmap = trailmap - self.name = trailmap.split(".")[-1] - - params = data_dict.get(trailmap.split(".")[0], data_dict["stocks"]) - self.symbol = params.get("symbol", "AAPL") - self.current_df = params.get("data", STOCK_DATA) - - self.controller: BaseController = controller( # type: ignore - **get_parameters(controller, self.name, self.current_df, self.symbol) - ) - - root_menu = self.controller.path[0] - sub_menu = self.controller.path[1] if len(self.controller.path) > 1 else None - if sub_menu and sub_menu: - console.print( - f"[bold green]Loading {root_menu}[/] [bold yellow]{sub_menu}[/]" - ) - else: - console.print(f"[bold green]Loading {root_menu}[/]") - - self.cmd_parsers: Dict[str, argparse.ArgumentParser] = {} - self.cmd_funcs: Dict[str, FunctionType] = {} - self.cmd_fullspec: Dict[str, FullArgSpec] = {} - self.image_exportable: Dict[str, bool] = {} - self.ignore = [ - "call_help", - "call_new", - "call_exit", - "call_clear", - "call_cls", - "call_quit", - "call_reset", - "call_support", - "call_wiki", - "call_record", - "call_stop", - "call_screenshot", - ] - self.commands: List[str] = self.get_commands() - - if self.name == "options" and hasattr(self.controller, "selected_date"): - self.controller.selected_date = get_expiration_date() - if hasattr(self.controller, "current_currency"): - self.controller.current_currency = "usdt" - if hasattr(self.controller, "source") and trailmap.split(".")[0] == "crypto": - self.controller.source = "YahooFinance" - - for attr in ["ticker", "symbol", "coin", "etf_name"]: - if hasattr(self.controller, attr): - setattr(self.controller, attr, self.symbol) - for attr in ["current_df", "etf_data"]: - if hasattr(self.controller, attr): - setattr(self.controller, attr, self.current_df) - - self.get_all_command_parsers() - - def get_commands(self) -> List[str]: - """Get commands""" - commands = [] - for name, _ in getmembers(self.controller, predicate=inspect.ismethod): - if name.startswith("call_") and name not in self.ignore: - func = getattr(self.controller, name) - - if hasattr(func, "__wrapped__"): - while hasattr(func, "__wrapped__"): - func = func.__wrapped__ - - self.cmd_funcs[name] = func - self.cmd_fullspec[name] = inspect.getfullargspec(func) - - if "_" not in self.cmd_fullspec[ - name - ].args and "from openbb_terminal." not in inspect.getsource(func): - commands.append(name) - - return commands - - def get_command_parser(self, command: str) -> Optional[argparse.ArgumentParser]: - """Get command parser""" - if command not in self.cmd_parsers: - self._get_parser(command) - - if command in self.cmd_parsers: - return self.cmd_parsers[command] - - return None - - def _get_parser(self, command: str) -> None: - """Get parser information from source""" - self.image_exportable[command] = False - - def mock_func(fparser: argparse.ArgumentParser, *args, **kwargs): - """Mock function to get the parser""" - allowed = [EXPORT_BOTH_RAW_DATA_AND_FIGURES, EXPORT_ONLY_FIGURES_ALLOWED] - export = kwargs.get("export_allowed", NO_EXPORT) - - if export in allowed: - self.image_exportable[command] = True - else: - for arg in args: - if arg in allowed: - self.image_exportable[command] = True - break - - self.cmd_parsers[command] = fparser - - try: - with patch.object( - self.controller, "parse_known_args_and_warn", new=mock_func - ) as _: - args = {} - - fullspec = self.cmd_fullspec[command] - if "_" in fullspec.args: - return - - if len(fullspec.args) > 2: - args.update({arg: ["1234"] for arg in fullspec.args[2:]}) - with patch( - "openbb_terminal.rich_config.console.print" - ), contextlib.suppress(SystemExit, AttributeError): - _ = getattr(self.controller, command)(["--help"], **args) - - except Exception as e: - print(e) - - def run_image_exports(self) -> None: - """Run image exports""" - if not self.image_exportable: - return - - for command in self.commands: - self.get_image_export(command) - - def get_image_export(self, command: str) -> None: - """Get image export""" - if not self.image_exportable[command]: - return - - console.print( - f"[yellow]Exporting {command} image for {self.name} controller[/]" - ) - try: - cmd_name = command.replace("call_", "") - - other_args = [ - "--export", - f"{'_'.join(self.controller.path)}_{cmd_name}.png", - ] - - for action in self.cmd_parsers[command]._actions: # pylint: disable=W0212 - if action.dest == "strike": - other_args.extend(["--strike", "100"]) - if action.dest == "target_dataset": - other_args.extend(["--dataset", "AAPL"]) - if action.dest == "ticker": - other_args.extend(["--ticker", self.symbol]) - if self.name == "forecast" and action.dest == "values": - other_args.extend(["--values", "AAPL.close"]) - - set_command_location(f"/{'/'.join(self.controller.path)}/{cmd_name}") - - _ = getattr(self.controller, command)(other_args) - - except (SystemExit, KeyError): - pass - - def get_all_command_parsers(self) -> None: - """Get all command parsers""" - for command in self.commands: - self.get_command_parser(command) - - def has_commands(self) -> bool: - """Checks if controller has commands""" - return len(self.commands) > 0 - - -class LoadControllersDoc: - """Class that loads all controllers and creates a ControllerDoc class instance for each one - - - Attributes - ---------- - controller_docs: Dict[str, ControllerDoc] - A dictionary of the controller name and the ControllerDoc class instance for that controller - - Methods - ------- - get_controllers() - Gets all controllers to create a ControllerDoc class instance for - get_controller_doc(controller: str) - Gets the ControllerDoc class instance for a controller - available_controllers() - Gets a list of available controllers names - """ - - def __init__(self) -> None: - self.controller_docs: Dict[str, ControllerDoc] = {} - self.get_controllers() - - def get_controllers(self) -> None: - """Gets all controllers""" - for trailmap, module in self._get_modules().items(): - for name, obj in getmembers(module): - if ( # noqa: SIM102 - name != "TerminalController" and "BaseController" not in name - ): # noqa: SIM102 - if ( - isclass(obj) - and issubclass(obj, BaseController) - and trailmap not in self.controller_docs - ): - ctrl = ControllerDoc(obj, trailmap) # type: ignore - if ctrl.has_commands(): - self.controller_docs[trailmap] = ctrl - - def _get_modules(self) -> Dict[str, ModuleType]: - """Gets all controllers modules""" - modules = {} - terminal_path = Path(openbb_terminal.__file__).parent - - for file in terminal_path.glob("**/*controller.py"): - spec = spec_from_file_location(file.stem, file) - if spec is not None and spec.loader is not None: - module = module_from_spec(spec) - spec.loader.exec_module(module) - - ctrl_path = ( - str(file) - .replace(str(terminal_path), "") - .replace("\\", "/") - .split("/")[1:] - ) - for sub_name, abbr in sub_folders_abbr.items(): - ctrl_path = [ - path.lower().replace(sub_name, abbr) for path in ctrl_path - ] - - trailmap = ".".join(ctrl_path[:-1]) - if trailmap not in modules: - modules[trailmap] = module - - return modules - - def get_controller_doc(self, controller_name: str) -> ControllerDoc: - """Get the ControllerDoc instance for a controller""" - if controller_name not in self.controller_docs: - raise KeyError(f"Controller {controller_name} not found") - - return self.controller_docs[controller_name] - - def available_controllers(self) -> List[str]: - """Get available controllers""" - return [ctrl for ctrl in self.controller_docs if ctrl != ""] - - -if __name__ == "__main__": - try: - run_image_exports = "--images" in sys.argv - load_controllers = LoadControllersDoc() - - for loaded in load_controllers.available_controllers(): - controller_doc = load_controllers.get_controller_doc(loaded) - controller_doc.get_all_command_parsers() - if run_image_exports: - cfg.setup_config_terminal() - plots_backend().start() - plots_backend().isatty = True - get_current_user().preferences.USE_INTERACTIVE_DF = False - controller_doc.run_image_exports() - except KeyboardInterrupt: - sys.exit(0)

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

D920qxc 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

G;=X;!b% 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 version.parse("0.4.8"): - init_kwargs.update(dict(proc_name=proc_name)) - - super().__init__(**init_kwargs) - - self.plotly_html: Path = (PLOTS_CORE_PATH / "plotly.html").resolve() - self.table_html: Path = (PLOTS_CORE_PATH / "table.html").resolve() - self.isatty = ( - not JUPYTER_NOTEBOOK - and sys.stdin.isatty() - and not get_current_system().TEST_MODE - and not get_current_user().preferences.ENABLE_QUICK_EXIT - and current_process().name == "MainProcess" - ) - if has_version and PyWry.__version__ == "0.0.0": - self.isatty = False - - self.WIDTH, self.HEIGHT = 1400, 762 - self.logged_in: bool = False - - atexit.register(self.close) - - def set_window_dimensions(self): - """Set the window dimensions.""" - current_user = get_current_user() - width = current_user.preferences.PLOT_PYWRY_WIDTH or 1400 - height = current_user.preferences.PLOT_PYWRY_HEIGHT or 762 - - self.WIDTH, self.HEIGHT = int(width), int(height) - - def get_pending(self) -> list: - """Get the pending data that has not been sent to the backend.""" - # pylint: disable=W0201,E0203 - pending = self.outgoing + self.init_engine - self.outgoing: list = [] - self.init_engine: list = [] - return pending - - def get_plotly_html(self) -> Path: - """Get the plotly html file.""" - self.set_window_dimensions() - if self.plotly_html.exists(): - return self.plotly_html - - console.print( - "[bold red]plotly.html file not found, check the path:[/]" - f"[green]{PLOTS_CORE_PATH / 'plotly.html'}[/]" - ) - self.max_retries = 0 # pylint: disable=W0201 - raise FileNotFoundError - - def get_table_html(self) -> Path: - """Get the table html file.""" - self.set_window_dimensions() - if self.table_html.exists(): - return self.table_html - console.print( - "[bold red]table.html file not found, check the path:[/]" - f"[green]{PLOTS_CORE_PATH / 'table.html'}[/]" - ) - self.max_retries = 0 # pylint: disable=W0201 - raise FileNotFoundError - - def get_window_icon(self) -> Optional[Path]: - """Get the window icon.""" - icon_path = PLOTS_CORE_PATH / "assets" / "Terminal_icon.png" - if icon_path.exists(): - return icon_path - return None - - def get_json_update(self, cmd_loc: str, theme: Optional[str] = None) -> dict: - """Get the json update for the backend.""" - current_user = get_current_user() - current_system = get_current_system() - - posthog: Dict[str, Any] = dict(collect_logs=current_system.LOG_COLLECT) - if ( - current_system.LOG_COLLECT - and current_user.profile.email - and not self.logged_in - ): - self.logged_in = True - posthog.update( - dict( - user_id=current_user.profile.uuid, - email=current_user.profile.email, - ) - ) - - return dict( - theme=theme or current_user.preferences.CHART_STYLE, - log_id=current_system.LOGGING_APP_ID, - pywry_version=self.__version__, - terminal_version=current_system.VERSION, - python_version=current_system.PYTHON_VERSION, - posthog=posthog, - command_location=cmd_loc, - ) - - def send_figure( - self, fig: go.Figure, export_image: Optional[Union[Path, str]] = "" - ): - """Send a Plotly figure to the backend. - - Parameters - ---------- - fig : go.Figure - Plotly figure to send to backend. - export_image : str, optional - Path to export image to, by default "" - """ - self.check_backend() - # pylint: disable=C0415 - from openbb_terminal.helper_funcs import command_location - - title = "Interactive Chart" - - fig.layout.title.text = re.sub( - r"<[^>]*>", "", fig.layout.title.text if fig.layout.title.text else title - ) - - fig.layout.height += 69 - - if export_image and isinstance(export_image, str): - export_image = Path(export_image).resolve() - - json_data = json.loads(fig.to_json()) - - if config_terminal.COMMAND_ON_CHART: - json_data.update(self.get_json_update(command_location)) - else: - json_data.update(self.get_json_update(" ")) - outgoing = dict( - html=self.get_plotly_html(), - json_data=json_data, - export_image=export_image, - **self.get_kwargs(command_location), - ) - self.send_outgoing(outgoing) - - if export_image and isinstance(export_image, Path): - self.loop.run_until_complete(self.process_image(export_image)) - - async def process_image(self, export_image: Path): - """Check if the image has been exported to the path.""" - pdf = export_image.suffix == ".pdf" - img_path = export_image.resolve() - - checks = 0 - while not img_path.exists(): - await asyncio.sleep(0.2) - checks += 1 - if checks > 50: - break - - if pdf: - img_path = img_path.rename(img_path.with_suffix(".svg")) - - if img_path.exists(): - if pdf: - drawing = svg2rlg(img_path) - img_path.unlink(missing_ok=True) - renderPDF.drawToFile(drawing, str(export_image)) - - if get_current_user().preferences.PLOT_OPEN_EXPORT: - if sys.platform == "win32": - os.startfile(export_image) # noqa: S606 # nosec: B606 - else: - opener = "open" if sys.platform == "darwin" else "xdg-open" - subprocess.check_call( - [opener, export_image] # nosec: B603 # noqa: S603 - ) # nosec: B603 # noqa: S603 - - def send_table( - self, - df_table: pd.DataFrame, - title: str = "", - source: str = "", - theme: str = "dark", - ): - """Send table data to the backend to be displayed in a table. - - Parameters - ---------- - df_table : pd.DataFrame - Dataframe to send to backend. - title : str, optional - Title to display in the window, by default "" - source : str, optional - Source of the data, by default "" - theme : light or dark, optional - Theme of the table, by default "light" - """ - self.check_backend() - - if title: - # We remove any html tags and markdown from the title - title = re.sub(r"<[^>]*>", "", title) - title = re.sub(r"\[\/?[a-z]+\]", "", title) - - # we get the length of each column using the max length of the column - # name and the max length of the column values as the column width - columnwidth = [ - max( - len(str(df_table[col].name)), - df_table[col].astype(str).str.len().max(), - ) - for col in df_table.columns - if hasattr(df_table[col], "name") and hasattr(df_table[col], "dtype") - ] - - # we add a percentage of max to the min column width - columnwidth = [ - int(x + (max(columnwidth) - min(columnwidth)) * 0.2) for x in columnwidth - ] - - # in case of a very small table we set a min width - width = max(int(min(sum(columnwidth) * 9.7, self.WIDTH + 100)), 800) - - # pylint: disable=C0415 - from openbb_terminal.helper_funcs import command_location - - json_data = json.loads(df_table.to_json(orient="split", date_format="iso")) - json_data.update( - dict( - title=title, - source=source or "", - **self.get_json_update(command_location, theme or "dark"), - ) - ) - - outgoing = dict( - html=self.get_table_html(), - json_data=json.dumps(json_data), - width=width, - height=self.HEIGHT - 100, - **self.get_kwargs(command_location), - ) - self.send_outgoing(outgoing) - - def send_url( - self, - url: str, - title: str = "", - width: Optional[int] = None, - height: Optional[int] = None, - ): - """Send a URL to the backend to be displayed in a window. - - Parameters - ---------- - url : str - URL to display in the window. - title : str, optional - Title to display in the window, by default "" - width : int, optional - Width of the window, by default 1200 - height : int, optional - Height of the window, by default 800 - """ - self.check_backend() - script = f""" - - """ - outgoing = dict( - html=script, - **self.get_kwargs(title), - width=width or self.WIDTH, - height=height or self.HEIGHT, - ) - self.send_outgoing(outgoing) - - def get_kwargs(self, title: str = "") -> dict: - """Get the kwargs for the backend.""" - return { - "title": f"OpenBB - {title}", - "icon": self.get_window_icon(), - "download_path": str(get_current_user().preferences.USER_EXPORTS_DIRECTORY), - } - - def start(self, debug: bool = False): # pylint: disable=W0221 - """Start the backend WindowManager process.""" - if self.isatty: - super().start(debug) - - def check_backend(self): - """Override to check if isatty.""" - if not self.isatty: - return None - - message = ( - "[bold red]PyWry version 0.5.12 or higher is required to use the " - "OpenBB Plots backend.[/]\n" - "[yellow]Please update pywry with 'pip install pywry --upgrade'[/]" - ) - if not hasattr(PyWry, "__version__"): - try: - # pylint: disable=C0415 - from pywry import __version__ as pywry_version - except ImportError: - self.max_retries = 0 - return console.print(message) - - PyWry.__version__ = pywry_version # pylint: disable=W0201 - - if version.parse(PyWry.__version__) < version.parse("0.5.12"): - self.max_retries = 0 # pylint: disable=W0201 - return console.print(message) - - if version.parse(PyWry.__version__) > version.parse("0.5.12"): - return super().check_backend() - - try: - return self.loop.run_until_complete(super().check_backend()) - except Exception: - return None - - def close(self, reset: bool = False): - """Close the backend.""" - if reset: - self.max_retries = 50 # pylint: disable=W0201 - - super().close() - - async def get_results(self, description: str) -> dict: - """Wait for completion of interactive task and return the data. - - Parameters - ---------- - description : str - Description of the task to console print while waiting. - - Returns - ------- - dict - The data returned from pywry backend. - """ - console.print( - f"[green]{description}[/]\n\n" - "[yellow]If the window is closed you can continue by pressing Ctrl+C.[/]" - ) - while True: - try: - data: dict = self.recv.get(block=False) or {} - if data.get("result", False): - return json.loads(data["result"]) - except Exception: # pylint: disable=W0703 # noqa: S110 - pass - - await asyncio.sleep(1) - - def call_hub(self, login: bool = True) -> Optional[dict]: - """Call the hub to login or logout. - - Parameters - ---------- - login : bool, optional - Whether to login or logout, by default True - - Returns - ------- - Optional[dict] - The user data if login was successful, None otherwise. - """ - self.check_backend() - endpoint = {True: "login", False: "logout"}[login] - - json_url = f"{BackendEnvironment.HUB_URL}{endpoint}?pywry=true" - - outgoing = dict( - json_data=dict(url=json_url), - **self.get_kwargs(endpoint.title()), - width=900, - height=800, - ) - self.send_outgoing(outgoing) - - messages_dict = dict( - login=dict( - message="Welcome to OpenBB Terminal! Please login to continue.", - interrupt="Window closed without authentication. Please proceed below.", - ), - logout=dict( - message="Sending logout request", interrupt="Please login to continue." - ), - ) - - try: - return self.loop.run_until_complete( - self.get_results(messages_dict[endpoint]["message"]) - ) - except KeyboardInterrupt: - console.print(f"\n[red]{messages_dict[endpoint]['interrupt']}[/red]") - return None - - -async def download_plotly_js(): - """Download or updates plotly.js to the assets folder.""" - js_filename = PLOTLYJS_PATH.name - try: - # we use aiohttp to download plotly.js - # this is so we don't have to block the main thread - async with aiohttp.ClientSession( - connector=aiohttp.TCPConnector(verify_ssl=False) - ) as session, session.get(f"https://cdn.plot.ly/{js_filename}") as resp: - with open(str(PLOTLYJS_PATH), "wb") as f: - while True: - chunk = await resp.content.read(1024) - if not chunk: - break - f.write(chunk) - - # We delete the old version of plotly.js - for file in (PLOTS_CORE_PATH / "assets").glob("plotly*.js"): - if file.name != js_filename: - file.unlink(missing_ok=True) - - except Exception as err: # pylint: disable=W0703 - console.print(f"Error downloading plotly.js: {err}") - - -def plots_backend() -> Backend: - """Get the backend.""" - global BACKEND # pylint: disable=W0603 # noqa - if BACKEND is None: - BACKEND = Backend() - return BACKEND - - -# To avoid having plotly.js in the repo, we download it if it's not present -if not PLOTLYJS_PATH.exists() and not JUPYTER_NOTEBOOK: - # We run this in a thread so we don't block the main thread - Thread(target=asyncio.run, args=(download_plotly_js(),)).start() diff --git a/openbb_terminal/core/plots/config/__init__.py b/openbb_terminal/core/plots/config/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/core/plots/config/openbb_styles.py b/openbb_terminal/core/plots/config/openbb_styles.py deleted file mode 100644 index c3e60e66528..00000000000 --- a/openbb_terminal/core/plots/config/openbb_styles.py +++ /dev/null @@ -1,271 +0,0 @@ -from typing import Any, List, Optional - -import pandas as pd - -# Vsurf Plot Settings -PLT_3DMESH_COLORSCALE = "Jet" -PLT_3DMESH_SCENE = dict( - xaxis=dict( - backgroundcolor="rgb(94, 94, 94)", - gridcolor="white", - showbackground=True, - zerolinecolor="white", - ), - yaxis=dict( - backgroundcolor="rgb(94, 94, 94)", - gridcolor="white", - showbackground=True, - zerolinecolor="white", - ), - zaxis=dict( - backgroundcolor="rgb(94, 94, 94)", - gridcolor="white", - showbackground=True, - zerolinecolor="white", - ), - aspectratio=dict(x=1.2, y=1.2, z=0.8), -) -PLT_3DMESH_HOVERLABEL = dict(bgcolor="gold") - -# Chart Plots Settings -PLT_STYLE_TEMPLATE = "plotly_dark" -PLT_STYLE_INCREASING = "#00ACFF" -PLT_STYLE_DECREASING = "#e4003a" -PLT_CANDLESTICKS = dict( - increasing=dict(line_color=PLT_STYLE_INCREASING, fillcolor=PLT_STYLE_INCREASING), - decreasing=dict(line_color=PLT_STYLE_DECREASING, fillcolor=PLT_STYLE_DECREASING), -) -PLT_STYLE_INCREASING_GREEN = "#009600" -PLT_STYLE_DECREASING_RED = "#c80000" -PLT_FONT = dict(family="Fira Code", size=16) -PLOTLY_FONT = dict(family="Fira Code", size=18) - -PLT_COLORWAY = [ - "#ffed00", - "#ef7d00", - "#e4003a", - "#c13246", - "#822661", - "#48277c", - "#005ca9", - "#00aaff", - "#9b30d9", - "#af005f", - "#5f00af", - "#af87ff", -] - -PLT_FIB_COLORWAY: List[Any] = [ - "rgb(195, 50, 69)", # 0 - "rgb(130, 38, 96)", # 0.235 - "rgb(120, 70, 200)", # 0.382 - "rgb(0, 93, 168)", # 0.5 - "rgb(173, 0, 95)", # 0.618 - "rgb(235, 184, 0)", # 0.65 Golden Pocket - "rgb(162, 115, 206)", # 1 - dict(family="Arial Black", size=10), # Fib's Text - dict(color="rgb(0, 230, 195)", width=0.9, dash="dash"), # Fib Trendline -] - -PLT_INCREASING_COLORWAY = [ - "rgba(0, 150, 255, 1)", - "rgba(0, 170, 255, 0.92)", - "rgba(0, 170, 255, 0.90)", - "rgba(0, 170, 255, 0.80)", - "rgba(0, 170, 255, 0.70)", - "rgba(0, 170, 255, 0.60)", - "rgba(0, 170, 255, 0.50)", - "rgba(0, 170, 255, 0.40)", - "rgba(0, 170, 255, 0.34)", - "rgba(0, 170, 255, 0.22)", - "rgba(0, 170, 255, 0.10)", - "rgba(0, 170, 255, 0.05)", -] - -PLT_DECREASING_COLORWAY = [ - "rgba(230, 0, 57, 1)", - "rgba(230, 0, 57, 0.92)", - "rgba(230, 0, 57, 0.90)", - "rgba(230, 0, 57, 0.80)", - "rgba(230, 0, 57, 0.70)", - "rgba(230, 0, 57, 0.60)", - "rgba(230, 0, 57, 0.50)", - "rgba(230, 0, 57, 0.40)", - "rgba(230, 0, 57, 0.34)", - "rgba(230, 0, 57, 0.22)", - "rgba(230, 0, 57, 0.10)", - "rgba(230, 0, 57, 0.05)", -] - -PLT_INCREASING_COLORWAY_GREEN = [ - "rgba(0, 150, 0, 1)", - "rgba(0, 150, 0, 0.92)", - "rgba(0, 150, 0, 0.90)", - "rgba(0, 150, 0, 0.80)", - "rgba(0, 150, 0, 0.70)", - "rgba(0, 150, 0, 0.60)", - "rgba(0, 150, 0, 0.50)", - "rgba(0, 150, 0, 0.40)", - "rgba(0, 150, 0, 0.34)", - "rgba(0, 150, 0, 0.22)", - "rgba(0, 150, 0, 0.10)", - "rgba(0, 150, 0, 0.05)", -] - -PLT_DECREASING_COLORWAY_RED = [ - "rgba(200, 0, 0, 1)", - "rgba(200, 0, 0, 0.92)", - "rgba(200, 0, 0, 0.90)", - "rgba(200, 0, 0, 0.80)", - "rgba(200, 0, 0, 0.70)", - "rgba(200, 0, 0, 0.60)", - "rgba(200, 0, 0, 0.50)", - "rgba(200, 0, 0, 0.40)", - "rgba(200, 0, 0, 0.34)", - "rgba(200, 0, 0, 0.22)", - "rgba(200, 0, 0, 0.10)", - "rgba(200, 0, 0, 0.05)", -] - - -# Table Plots Settings -PLT_TBL_HEADER = dict( - fill_color="rgb(30, 30, 30)", - font_color="white", - line_color="#6e6e6e", - line_width=1, -) -PLT_TBL_CELLS = dict( - font_color="white", - line_color="#6e6e6e", - line_width=0, -) -PLT_TBL_ROW_COLORS = ( - "#333333", - "#242424", -) - - -def de_increasing_color_list( - df_column: Optional[pd.DataFrame] = None, - text: Optional[str] = None, - contains_str: str = "-", - increasing_color: str = PLT_STYLE_INCREASING, - decreasing_color: str = PLT_STYLE_DECREASING, -) -> List[str]: - """Makes a colorlist for decrease/increase if value in df_column - contains "{contains_str}" default is "-" - - Parameters - ---------- - df_column : pd.DataFrame, optional - Dataframe column to create colorlist. by default None - text : str, optional - Search in a string, by default None - contains_str : str, optional - Decreasing String to search for in df_column. The default is "-". - increasing_color : str, optional - Color to use for increasing values. The default is PLT_STYLE_INCREASING. - decreasing_color : str, optional - Color to use for decreasing values. The default is PLT_STYLE_DECREASING. - - Returns - ------- - List[str] - List of colors for df_column - """ - if df_column is None: - colorlist = [decreasing_color if contains_str in text else increasing_color] # type: ignore - else: - colorlist = [ - decreasing_color if boolv else increasing_color - for boolv in df_column.astype(str).str.contains(contains_str) - ] - return colorlist - - -PLOTLY_THEME = dict( - # Layout - layout=dict( - colorway=PLT_COLORWAY, - font=PLOTLY_FONT, - yaxis=dict( - side="right", - zeroline=True, - fixedrange=False, - title_standoff=20, - nticks=15, - showline=True, - showgrid=True, - ), - yaxis2=dict( - zeroline=False, - fixedrange=False, - anchor="x", - layer="above traces", - overlaying="y2", - nticks=6, - tick0=0.5, - title_standoff=10, - tickfont=dict(size=15), - showline=True, - ), - yaxis3=dict( - zeroline=False, - fixedrange=False, - anchor="x", - layer="above traces", - overlaying="y3", - nticks=6, - tick0=0.5, - title_standoff=10, - tickfont=dict(size=15), - showline=True, - ), - yaxis4=dict( - zeroline=False, - fixedrange=False, - anchor="x", - layer="above traces", - overlaying="y4", - nticks=6, - tick0=0.5, - title_standoff=10, - tickfont=dict(size=15), - showline=True, - ), - xaxis=dict( - showgrid=True, - zeroline=True, - showline=True, - rangeslider=dict(visible=False), - tickfont=dict(size=16), - title_standoff=20, - ), - legend=dict( - yanchor="top", - y=0.99, - xanchor="left", - font_size=15, - bgcolor="rgba(0, 0, 0, 0)", - x=0.01, - ), - dragmode="pan", - hovermode="x", - hoverlabel=dict(align="left"), - ), - data=dict( - candlestick=[ - dict( - increasing=dict( - line=dict(color=PLT_STYLE_INCREASING), - fillcolor=PLT_STYLE_INCREASING, - ), - decreasing=dict( - line=dict(color=PLT_STYLE_DECREASING), - fillcolor=PLT_STYLE_DECREASING, - ), - ) - ] - ), -) diff --git a/openbb_terminal/core/plots/no_import.py b/openbb_terminal/core/plots/no_import.py deleted file mode 100644 index 3d9a22d084c..00000000000 --- a/openbb_terminal/core/plots/no_import.py +++ /dev/null @@ -1,95 +0,0 @@ -import asyncio -from queue import Queue -from typing import List - -import dotenv - -from openbb_terminal.base_helpers import console -from openbb_terminal.core.config.paths import SETTINGS_ENV_FILE -from openbb_terminal.core.session.current_system import get_current_system -from openbb_terminal.core.session.current_user import get_current_user, set_current_user - -pywry_missing = """ -[red]PyWry is not installed or missing required linux dependencies.[/] - -[yellow]Install PyWry[/] -[green]pip install pywry --upgrade[/] - -[yellow]Platform-specific notes[/] -Here is the underlying web engine each platform uses you might need to install. - -[green]Linux[/] -Pywry uses gtk-rs and its related libraries for window creation and Wry also needs WebKitGTK for WebView. -To activate interactive plots/tables in pywry window, please make sure the following packages are installed: - -[yellow]Arch Linux / Manjaro:[/] -[green]sudo pacman -S webkit2gtk[/]\n -[yellow]Debian / Ubuntu:[/] -[green]sudo apt install libwebkit2gtk-4.0-dev[/]\n -[yellow]Fedora / CentOS / AlmaLinux:[/] -[green]sudo dnf install gtk3-devel webkit2gtk3-devel[/]\r -""" - - -class DummyBackend: - """Dummy class to avoid import errors.""" - - __version__ = "0.0.0" - - max_retries = 0 - outgoing: List[str] = [] - init_engine: List[str] = [] - daemon = True - debug = False - shell = False - base = None - recv: Queue = Queue() - - def __new__(cls, *args, **kwargs): # pylint: disable=W0613 - """Create a singleton instance of the backend.""" - if not hasattr(cls, "instance"): - cls.instance = super().__new__(cls) # pylint: disable=E1120 - return cls.instance - - def __init__(self, daemon: bool = True, max_retries: int = 30): - """Dummy init to avoid import errors.""" - self.daemon = daemon - self.max_retries = max_retries - try: - self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() - except RuntimeError: - self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(self.loop) - - current_user = get_current_user() - - # If pywry is not installed or missing required linux dependencies - # we inform the user the required packages to install and revert - # plotly default behaviour to open in browser. - # We do this only once. - if ( - current_user.preferences.PLOT_ENABLE_PYWRY - and get_current_system().LOGGING_SUB_APP != "sdk" - ): - console.print(pywry_missing) - console.print( - "\n\nContinuing with default plotly behaviour (open in browser).", - style="yellow", - ) - dotenv.set_key(SETTINGS_ENV_FILE, "PLOT_ENABLE_PYWRY", "0") - - current_user.preferences.USE_INTERACTIVE_DF = False - set_current_user(current_user) - - def close(self, reset: bool = False): # pylint: disable=W0613 - pass - - def start(self, debug: bool = False): # pylint: disable=W0613 - pass - - def send_outgoing(self, outgoing: dict): - pass - - async def check_backend(self): - """Dummy check backend method to avoid errors and revert to browser.""" - raise Exception diff --git a/openbb_terminal/core/plots/plotly.html b/openbb_terminal/core/plots/plotly.html deleted file mode 100644 index 3adeeadf47d..00000000000 --- a/openbb_terminal/core/plots/plotly.html +++ /dev/null @@ -1,3748 +0,0 @@ - - - - - - - OpenBB Interactive Charts - - - - - -